121 | * Throws a RuntimeException if the location is invalid.
122 | */
123 | public static void checkLocation(int location, String label) {
124 | if (location < 0) {
125 | throw new RuntimeException("Unable to locate '" + label + "' in program");
126 | }
127 | }
128 |
129 | /**
130 | * Creates a texture from raw data.
131 | *
132 | * @param data Image data, in a "direct" ByteBuffer.
133 | * @param width Texture width, in pixels (not bytes).
134 | * @param height Texture height, in pixels.
135 | * @param format Image data format (use constant appropriate for glTexImage2D(), e.g. GL_RGBA).
136 | * @return Handle to texture.
137 | */
138 | public static int createImageTexture(ByteBuffer data, int width, int height, int format) {
139 | int[] textureHandles = new int[1];
140 | int textureHandle;
141 |
142 | GLES20.glGenTextures(1, textureHandles, 0);
143 | textureHandle = textureHandles[0];
144 | GlUtil.checkGlError("glGenTextures");
145 |
146 | // Bind the texture handle to the 2D texture target.
147 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle);
148 |
149 | // Configure min/mag filtering, i.e. what scaling method do we use if what we're rendering
150 | // is smaller or larger than the source image.
151 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
152 | GLES20.GL_LINEAR);
153 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
154 | GLES20.GL_LINEAR);
155 | GlUtil.checkGlError("loadImageTexture");
156 |
157 | // Load the data from the buffer into the texture handle.
158 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, /*level*/ 0, format,
159 | width, height, /*border*/ 0, format, GLES20.GL_UNSIGNED_BYTE, data);
160 | GlUtil.checkGlError("loadImageTexture");
161 |
162 | return textureHandle;
163 | }
164 |
165 | /**
166 | * Allocates a direct float buffer, and populates it with the float array data.
167 | */
168 | public static FloatBuffer createFloatBuffer(float[] coords) {
169 | // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it.
170 | ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT);
171 | bb.order(ByteOrder.nativeOrder());
172 | FloatBuffer fb = bb.asFloatBuffer();
173 | fb.put(coords);
174 | fb.position(0);
175 | return fb;
176 | }
177 |
178 | /**
179 | * Writes GL version info to the log.
180 | */
181 | public static void logVersionInfo() {
182 | Log.i(TAG, "vendor : " + GLES20.glGetString(GLES20.GL_VENDOR));
183 | Log.i(TAG, "renderer: " + GLES20.glGetString(GLES20.GL_RENDERER));
184 | Log.i(TAG, "version : " + GLES20.glGetString(GLES20.GL_VERSION));
185 |
186 | if (false) {
187 | int[] values = new int[1];
188 | GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, values, 0);
189 | int majorVersion = values[0];
190 | GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, values, 0);
191 | int minorVersion = values[0];
192 | if (GLES30.glGetError() == GLES30.GL_NO_ERROR) {
193 | Log.i(TAG, "iversion: " + majorVersion + "." + minorVersion);
194 | }
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/guoheng/decodeencodemp4/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.android.guoheng.decodeencodemp4;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends AppCompatActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 |
13 | EncodeDecodeSurface test=new EncodeDecodeSurface();
14 | try {
15 | test.testEncodeDecodeSurface();
16 | }catch (Throwable a){
17 | a.printStackTrace();
18 | }
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/guoheng/decodeencodemp4/STextureRender.java:
--------------------------------------------------------------------------------
1 | package com.android.guoheng.decodeencodemp4;
2 |
3 | import android.graphics.SurfaceTexture;
4 | import android.opengl.GLES11Ext;
5 | import android.opengl.GLES20;
6 | import android.opengl.Matrix;
7 | import android.util.Log;
8 |
9 | import java.nio.FloatBuffer;
10 |
11 | /**
12 | * Created by guoheng on 2016/8/31.
13 | */
14 | public class STextureRender {
15 | private static final int FLOAT_SIZE_BYTES = 4;
16 | private static final String TAG = "STextureRendering";
17 |
18 | private static final float TRANSFORM_RECTANGLE_COORDS[] = {
19 | -0.914337f, -0.949318f,1.0f,
20 | 0.494437f, -0.683502f,1.0f,
21 | -0.895833f, 0.62963f,1.0f,
22 | 0.76524f, 0.689287f,1.0f
23 |
24 | };
25 |
26 |
27 | private static final float TRANSFORM_RECTANGLE_TEX_COORDS[] = {
28 | 0f, 0.822368f, 0.822368f,1.0f,
29 | 0.710227f, 0.710227f, 0.710227f,1.0f,
30 | 0f, 0f, 1f,1.0f,
31 | 0.838926f, 0f, 0.838926f,1.0f
32 |
33 | };
34 |
35 | private static final float FULL_RECTANGLE_COORDS[] = {
36 | -1.0f, -1.0f,1.0f, // 0 bottom left
37 | 1.0f, -1.0f,1.0f, // 1 bottom right
38 | -1.0f, 1.0f,1.0f, // 2 top left
39 | 1.0f, 1.0f,1.0f // 3 top right
40 | };
41 |
42 | private static final float FULL_RECTANGLE_TEX_COORDS[] = {
43 | 0.0f, 1.0f, 1f,1.0f, // 0 bottom left
44 | 1.0f, 1.0f,1f,1.0f, // 1 bottom right
45 | 0.0f, 0.0f, 1f,1.0f, // 2 top left
46 | 1.0f, 0.0f ,1f,1.0f // 3 top right
47 | };
48 |
49 | private static final FloatBuffer FULL_RECTANGLE_BUF =
50 | GlUtil.createFloatBuffer(FULL_RECTANGLE_COORDS);
51 | private static final FloatBuffer FULL_RECTANGLE_TEX_BUF =
52 | GlUtil.createFloatBuffer(FULL_RECTANGLE_TEX_COORDS);
53 |
54 |
55 |
56 | private static final FloatBuffer TRANSFORM_RECTANGLE_BUF =
57 | GlUtil.createFloatBuffer(TRANSFORM_RECTANGLE_COORDS);
58 | private static final FloatBuffer TRANSFORM_RECTANGLE_TEX_BUF =
59 | GlUtil.createFloatBuffer(TRANSFORM_RECTANGLE_TEX_COORDS);
60 |
61 |
62 |
63 | private FloatBuffer mTriangleVertices;
64 |
65 | private static final String VERTEX_SHADER =
66 | "uniform mat4 uMVPMatrix;\n" +
67 | "uniform mat4 uSTMatrix;\n" +
68 | "attribute vec4 aPosition;\n" +
69 | "attribute vec4 aTextureCoord;\n" +
70 | "varying vec4 vTextureCoord;\n" +
71 | "void main() {\n" +
72 | " gl_Position = uMVPMatrix * aPosition;\n" +
73 | " vTextureCoord = uSTMatrix * aTextureCoord;\n" +
74 | "}\n";
75 |
76 | private static final String FRAGMENT_SHADER =
77 | "#extension GL_OES_EGL_image_external : require\n" +
78 | "precision mediump float;\n" + // highp here doesn't seem to matter
79 | "varying vec4 vTextureCoord;\n" +
80 | "uniform samplerExternalOES sTexture;\n" +
81 | "void main() {\n" +
82 | " gl_FragColor = texture2D(sTexture, vTextureCoord.xy/vTextureCoord.z);" +
83 | "}\n";
84 |
85 |
86 |
87 |
88 | private float[] mMVPMatrix = new float[16];
89 | private float[] mSTMatrix = new float[16];
90 |
91 | private int mProgram;
92 | private int mTextureID = -12345;
93 | private int muMVPMatrixHandle;
94 | private int muSTMatrixHandle;
95 | private int maPositionHandle;
96 | private int maTextureHandle;
97 |
98 | public STextureRender() {
99 | Matrix.setIdentityM(mSTMatrix, 0);
100 | }
101 |
102 | public int getTextureId() {
103 | return mTextureID;
104 | }
105 |
106 | /**
107 | * Draws the external texture in SurfaceTexture onto the current EGL surface.
108 | */
109 | public void drawFrame(SurfaceTexture st, boolean invert) {
110 | checkGlError("onDrawFrame start");
111 | st.getTransformMatrix(mSTMatrix);
112 | if (invert) {
113 | mSTMatrix[5] = -mSTMatrix[5];
114 | mSTMatrix[13] = 1.0f - mSTMatrix[13];
115 | }
116 |
117 | // (optional) clear to green so we can see if we're failing to set pixels
118 | GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
119 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
120 |
121 | GLES20.glUseProgram(mProgram);
122 | checkGlError("glUseProgram");
123 |
124 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
125 | GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
126 |
127 |
128 |
129 | // Enable the "aPosition" vertex attribute.
130 | GLES20.glEnableVertexAttribArray(maPositionHandle);
131 | GlUtil.checkGlError("glEnableVertexAttribArray");
132 |
133 | // Connect vertexBuffer to "aPosition".
134 | GLES20.glVertexAttribPointer(maPositionHandle, 3,
135 | GLES20.GL_FLOAT, false, 3*FLOAT_SIZE_BYTES, TRANSFORM_RECTANGLE_BUF);
136 | GlUtil.checkGlError("glVertexAttribPointer");
137 |
138 | // Enable the "aTextureCoord" vertex attribute.
139 | GLES20.glEnableVertexAttribArray(maTextureHandle);
140 | GlUtil.checkGlError("glEnableVertexAttribArray");
141 |
142 | // Connect texBuffer to "aTextureCoord".
143 | GLES20.glVertexAttribPointer(maTextureHandle, 4,
144 | GLES20.GL_FLOAT, false, 4*FLOAT_SIZE_BYTES, TRANSFORM_RECTANGLE_TEX_BUF);
145 | GlUtil.checkGlError("glVertexAttribPointer");
146 |
147 | Matrix.setIdentityM(mMVPMatrix, 0);
148 | GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
149 | GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
150 |
151 |
152 | // Draw the rect.
153 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
154 | GlUtil.checkGlError("glDrawArrays");
155 |
156 |
157 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
158 | checkGlError("glDrawArrays");
159 |
160 |
161 | // Done -- disable vertex array, texture, and program.
162 | GLES20.glDisableVertexAttribArray(maPositionHandle);
163 | GLES20.glDisableVertexAttribArray(maTextureHandle);
164 | GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
165 | GLES20.glUseProgram(0);
166 |
167 | }
168 |
169 |
170 | /**
171 | * Initializes GL state. Call this after the EGL surface has been created and made current.
172 | */
173 | public void surfaceCreated() {
174 | mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
175 | if (mProgram == 0) {
176 | throw new RuntimeException("failed creating program");
177 | }
178 |
179 | maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
180 | checkLocation(maPositionHandle, "aPosition");
181 | maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
182 | checkLocation(maTextureHandle, "aTextureCoord");
183 |
184 | muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
185 | checkLocation(muMVPMatrixHandle, "uMVPMatrix");
186 | muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
187 | checkLocation(muSTMatrixHandle, "uSTMatrix");
188 |
189 | int[] textures = new int[1];
190 | GLES20.glGenTextures(1, textures, 0);
191 |
192 | mTextureID = textures[0];
193 | GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
194 | checkGlError("glBindTexture mTextureID");
195 |
196 | GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
197 | GLES20.GL_NEAREST);
198 | GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
199 | GLES20.GL_LINEAR);
200 | GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
201 | GLES20.GL_CLAMP_TO_EDGE);
202 | GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
203 | GLES20.GL_CLAMP_TO_EDGE);
204 | checkGlError("glTexParameter");
205 | }
206 |
207 | /**
208 | * Replaces the fragment shader. Pass in null to reset to default.
209 | */
210 | public void changeFragmentShader(String fragmentShader) {
211 | if (fragmentShader == null) {
212 | fragmentShader = FRAGMENT_SHADER;
213 | }
214 | GLES20.glDeleteProgram(mProgram);
215 | mProgram = createProgram(VERTEX_SHADER, fragmentShader);
216 | if (mProgram == 0) {
217 | throw new RuntimeException("failed creating program");
218 | }
219 | }
220 |
221 | private int loadShader(int shaderType, String source) {
222 | int shader = GLES20.glCreateShader(shaderType);
223 | checkGlError("glCreateShader type=" + shaderType);
224 | GLES20.glShaderSource(shader, source);
225 | GLES20.glCompileShader(shader);
226 | int[] compiled = new int[1];
227 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
228 | if (compiled[0] == 0) {
229 | Log.e(TAG, "Could not compile shader " + shaderType + ":");
230 | Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
231 | GLES20.glDeleteShader(shader);
232 | shader = 0;
233 | }
234 | return shader;
235 | }
236 |
237 | private int createProgram(String vertexSource, String fragmentSource) {
238 | int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
239 | if (vertexShader == 0) {
240 | return 0;
241 | }
242 | int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
243 | if (pixelShader == 0) {
244 | return 0;
245 | }
246 |
247 | int program = GLES20.glCreateProgram();
248 | if (program == 0) {
249 | Log.e(TAG, "Could not create program");
250 | }
251 | GLES20.glAttachShader(program, vertexShader);
252 | checkGlError("glAttachShader");
253 | GLES20.glAttachShader(program, pixelShader);
254 | checkGlError("glAttachShader");
255 | GLES20.glLinkProgram(program);
256 | int[] linkStatus = new int[1];
257 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
258 | if (linkStatus[0] != GLES20.GL_TRUE) {
259 | Log.e(TAG, "Could not link program: ");
260 | Log.e(TAG, GLES20.glGetProgramInfoLog(program));
261 | GLES20.glDeleteProgram(program);
262 | program = 0;
263 | }
264 | return program;
265 | }
266 |
267 | public void checkGlError(String op) {
268 | int error;
269 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
270 | Log.e(TAG, op + ": glError " + error);
271 | throw new RuntimeException(op + ": glError " + error);
272 | }
273 | }
274 |
275 | public static void checkLocation(int location, String label) {
276 | if (location < 0) {
277 | throw new RuntimeException("Unable to locate '" + label + "' in program");
278 | }
279 | }
280 | }
281 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/guoheng/decodeencodemp4/SurfaceDecoder.java:
--------------------------------------------------------------------------------
1 | package com.android.guoheng.decodeencodemp4;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaExtractor;
5 | import android.media.MediaFormat;
6 | import android.os.Environment;
7 | import android.util.Log;
8 | import android.view.Surface;
9 |
10 | import java.io.File;
11 | import java.io.FileNotFoundException;
12 | import java.io.IOException;
13 |
14 | /**
15 | * Created by guoheng on 2016/9/1.
16 | */
17 | public class SurfaceDecoder {
18 |
19 | private static final String TAG = "EncodeDecodeSurface";
20 | private static final boolean VERBOSE = false; // lots of logging
21 |
22 | int saveWidth = 1920;
23 | int saveHeight = 1080;
24 |
25 | MediaCodec decoder = null;
26 |
27 | CodecOutputSurface outputSurface = null;
28 |
29 | MediaExtractor extractor = null;
30 |
31 | public int DecodetrackIndex;
32 |
33 | // where to find files (note: requires WRITE_EXTERNAL_STORAGE permission)
34 | private static final File FILES_DIR = Environment.getExternalStorageDirectory();
35 | private static final String INPUT_FILE = "1.mp4";
36 |
37 |
38 | void SurfaceDecoderPrePare(Surface encodersurface)
39 | {
40 | try {
41 | File inputFile = new File(FILES_DIR, INPUT_FILE); // must be an absolute path
42 |
43 | if (!inputFile.canRead()) {
44 | throw new FileNotFoundException("Unable to read " + inputFile);
45 | }
46 | extractor = new MediaExtractor();
47 | extractor.setDataSource(inputFile.toString());
48 | DecodetrackIndex = selectTrack(extractor);
49 | if (DecodetrackIndex < 0) {
50 | throw new RuntimeException("No video track found in " + inputFile);
51 | }
52 | extractor.selectTrack(DecodetrackIndex);
53 |
54 | MediaFormat format = extractor.getTrackFormat(DecodetrackIndex);
55 | if (VERBOSE) {
56 | Log.d(TAG, "Video size is " + format.getInteger(MediaFormat.KEY_WIDTH) + "x" +
57 | format.getInteger(MediaFormat.KEY_HEIGHT));
58 | }
59 |
60 | outputSurface = new CodecOutputSurface(saveWidth, saveHeight,encodersurface);
61 |
62 | String mime = format.getString(MediaFormat.KEY_MIME);
63 | decoder = MediaCodec.createDecoderByType(mime);
64 | decoder.configure(format, outputSurface.getSurface(), null, 0);
65 | decoder.start();
66 | }catch (IOException e)
67 | {
68 | e.printStackTrace();
69 | }
70 |
71 |
72 | }
73 |
74 |
75 | private int selectTrack(MediaExtractor extractor) {
76 | // Select the first video track we find, ignore the rest.
77 | int numTracks = extractor.getTrackCount();
78 | for (int i = 0; i < numTracks; i++) {
79 | MediaFormat format = extractor.getTrackFormat(i);
80 | String mime = format.getString(MediaFormat.KEY_MIME);
81 | if (mime.startsWith("video/")) {
82 | if (VERBOSE) {
83 | Log.d(TAG, "Extractor selected track " + i + " (" + mime + "): " + format);
84 | }
85 | return i;
86 | }
87 | }
88 |
89 | return -1;
90 | }
91 |
92 |
93 | void release()
94 | {
95 | if (decoder != null) {
96 | decoder.stop();
97 | decoder.release();
98 | decoder = null;
99 | }
100 | if (extractor != null) {
101 | extractor.release();
102 | extractor = null;
103 | }
104 | if (outputSurface != null) {
105 | outputSurface.release();
106 | outputSurface = null;
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/app/src/main/java/com/android/guoheng/decodeencodemp4/SurfaceEncoder.java:
--------------------------------------------------------------------------------
1 | package com.android.guoheng.decodeencodemp4;
2 |
3 | import android.media.MediaCodec;
4 | import android.media.MediaCodecInfo;
5 | import android.media.MediaFormat;
6 | import android.media.MediaMuxer;
7 | import android.os.Environment;
8 | import android.util.Log;
9 | import android.view.Surface;
10 |
11 | import java.io.File;
12 | import java.io.IOException;
13 | import java.nio.ByteBuffer;
14 |
15 | /**
16 | * Created by guoheng on 2016/9/1.
17 | */
18 | public class SurfaceEncoder {
19 |
20 | private static final String TAG = "EncodeDecodeSurface";
21 | private static final boolean VERBOSE = false; // lots of logging
22 | private static final String MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding
23 | private static final int WIDTH = 1920;
24 | private static final int HEIGHT = 1080;
25 | private static final int BIT_RATE = 1920*1080*10; // 2Mbps
26 | public static final int FRAME_RATE = 30; // 30fps
27 | private static final int IFRAME_INTERVAL = 30; // 10 seconds between I-frames
28 |
29 | MediaCodec encoder = null;
30 | Surface encodesurface;
31 | private MediaCodec.BufferInfo mBufferInfo;
32 | public MediaMuxer mMuxer;
33 |
34 | public int mTrackIndex;
35 | public boolean mMuxerStarted;
36 |
37 |
38 | public void VideoEncodePrepare()
39 | {
40 | String outputPath = new File(Environment.getExternalStorageDirectory(),
41 | "mytest." + WIDTH + "x" + HEIGHT + ".mp4").toString();
42 |
43 | mBufferInfo = new MediaCodec.BufferInfo();
44 |
45 | MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
46 |
47 | // Set some properties. Failing to specify some of these can cause the MediaCodec
48 | // configure() call to throw an unhelpful exception.
49 | format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
50 | MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
51 | format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
52 | format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
53 | format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
54 |
55 |
56 | encoder = null;
57 |
58 | try {
59 | encoder = MediaCodec.createEncoderByType(MIME_TYPE);
60 |
61 | encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
62 | encodesurface=encoder.createInputSurface();
63 | encoder.start();
64 | mMuxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
65 |
66 | }catch (IOException ioe) {
67 | throw new RuntimeException("failed init encoder", ioe);
68 | }
69 |
70 | mTrackIndex = -1;
71 | mMuxerStarted = false;
72 |
73 | }
74 |
75 |
76 |
77 | public void drainEncoder(boolean endOfStream) {
78 | final int TIMEOUT_USEC = 10000;
79 | if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")");
80 |
81 | if (endOfStream) {
82 | if (VERBOSE) Log.d(TAG, "sending EOS to encoder");
83 | encoder.signalEndOfInputStream();
84 | }
85 |
86 | ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
87 | while (true) {
88 | int encoderStatus = encoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
89 | if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
90 | // no output available yet
91 | if (!endOfStream) {
92 | break; // out of while
93 | } else {
94 | if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS");
95 | }
96 | } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
97 | // not expected for an encoder
98 | encoderOutputBuffers = encoder.getOutputBuffers();
99 | } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
100 | // should happen before receiving buffers, and should only happen once
101 | if (mMuxerStarted) {
102 | throw new RuntimeException("format changed twice");
103 | }
104 | MediaFormat newFormat = encoder.getOutputFormat();
105 | Log.d(TAG, "encoder output format changed: " + newFormat);
106 |
107 | // now that we have the Magic Goodies, start the muxer
108 | mTrackIndex = mMuxer.addTrack(newFormat);
109 | mMuxer.start();
110 | mMuxerStarted = true;
111 | } else if (encoderStatus < 0) {
112 | Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " +
113 | encoderStatus);
114 | // let's ignore it
115 | } else {
116 | ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
117 | if (encodedData == null) {
118 | throw new RuntimeException("encoderOutputBuffer " + encoderStatus +
119 | " was null");
120 | }
121 |
122 | if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
123 | // The codec config data was pulled out and fed to the muxer when we got
124 | // the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
125 | if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
126 |
127 | MediaFormat format =
128 | MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
129 | format.setByteBuffer("csd-0", encodedData);
130 |
131 | mBufferInfo.size = 0;
132 | }
133 |
134 | if (mBufferInfo.size != 0) {
135 | if (!mMuxerStarted) {
136 | throw new RuntimeException("muxer hasn't started");
137 | }
138 |
139 | // adjust the ByteBuffer values to match BufferInfo (not needed?)
140 | encodedData.position(mBufferInfo.offset);
141 | encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
142 |
143 | mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);
144 | if (VERBOSE) Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer");
145 | }
146 |
147 | encoder.releaseOutputBuffer(encoderStatus, false);
148 |
149 | if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
150 | if (!endOfStream) {
151 | Log.w(TAG, "reached end of stream unexpectedly");
152 | } else {
153 | if (VERBOSE) Log.d(TAG, "end of stream reached");
154 | }
155 | break; // out of while
156 | }
157 | }
158 | }
159 | }
160 |
161 |
162 | void release()
163 | {
164 | if (encoder!=null)
165 | {
166 | encoder.stop();
167 | encoder.release();
168 | }
169 | if (mMuxer != null) {
170 | mMuxer.stop();
171 | mMuxer.release();
172 | mMuxer = null;
173 | }
174 | }
175 |
176 |
177 |
178 |
179 | Surface getEncoderSurface()
180 | {
181 | return encodesurface;
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |