@@ -46,6 +46,8 @@ public class ExternalTextureFlutterActivity extends TestActivity {
46
46
private static final int SURFACE_HEIGHT = 256 ;
47
47
48
48
private SurfaceRenderer surfaceViewRenderer , flutterRenderer ;
49
+
50
+ // Latch used to ensure both SurfaceRenderers produce a frame before taking a screenshot.
49
51
private final CountDownLatch firstFrameLatch = new CountDownLatch (2 );
50
52
51
53
private long textureId = 0 ;
@@ -172,6 +174,9 @@ default boolean canProduceFrames() {
172
174
void destroy ();
173
175
}
174
176
177
+ /**
178
+ * Paints a simple gradient onto the attached Surface.
179
+ */
175
180
private static class CanvasSurfaceRenderer implements SurfaceRenderer {
176
181
private Surface surface ;
177
182
private CountDownLatch onFirstFrame ;
@@ -218,6 +223,9 @@ public void repaint() {
218
223
public void destroy () {}
219
224
}
220
225
226
+ /**
227
+ * Decodes a sample video into the attached Surface.
228
+ */
221
229
@ RequiresApi (VERSION_CODES .LOLLIPOP )
222
230
private static class MediaSurfaceRenderer implements SurfaceRenderer {
223
231
private final Supplier <MediaExtractor > extractorSupplier ;
@@ -262,13 +270,16 @@ private void decodeThreadMain() {
262
270
codec .configure (format , surface , null , 0 );
263
271
codec .start ();
264
272
273
+ // Track 0 is always the video track, since the sample video doesn't contain audio.
265
274
extractor .selectTrack (0 );
266
275
267
276
MediaCodec .BufferInfo bufferInfo = new MediaCodec .BufferInfo ();
268
277
boolean seenEOS = false ;
269
278
long startTimeNs = System .nanoTime ();
270
279
271
280
while (true ) {
281
+ // Move samples (video frames) from the extractor into the decoder, as long as we haven't
282
+ // consumed all samples.
272
283
if (!seenEOS ) {
273
284
int inputBufferIndex = codec .dequeueInputBuffer (-1 );
274
285
ByteBuffer inputBuffer = codec .getInputBuffer (inputBufferIndex );
@@ -284,6 +295,8 @@ private void decodeThreadMain() {
284
295
}
285
296
}
286
297
298
+ // Then consume decoded video frames from the decoder. These frames are automatically
299
+ // pushed to the attached Surface, so this schedules them for present.
287
300
int outputBufferIndex = codec .dequeueOutputBuffer (bufferInfo , 10000 );
288
301
boolean lastBuffer = (bufferInfo .flags & MediaCodec .BUFFER_FLAG_END_OF_STREAM ) != 0 ;
289
302
if (outputBufferIndex >= 0 ) {
@@ -298,6 +311,8 @@ private void decodeThreadMain() {
298
311
}
299
312
}
300
313
314
+ // Once we present the last frame, mark this renderer as no longer producing frames and
315
+ // exit the loop.
301
316
if (lastBuffer ) {
302
317
atomicCanProduceFrames .set (false );
303
318
break ;
@@ -327,6 +342,10 @@ public void destroy() {
327
342
}
328
343
}
329
344
345
+ /**
346
+ * Takes frames from the inner SurfaceRenderer and feeds it through an ImageReader and
347
+ * ImageWriter pair.
348
+ */
330
349
@ RequiresApi (VERSION_CODES .M )
331
350
private static class ImageSurfaceRenderer implements SurfaceRenderer {
332
351
private final SurfaceRenderer inner ;
0 commit comments