1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.mediav2.cts;
18 
19 import android.media.MediaCodec;
20 import android.media.MediaCodecInfo;
21 import android.media.MediaCodecList;
22 import android.media.MediaExtractor;
23 import android.media.MediaFormat;
24 import android.media.MediaMuxer;
25 import android.util.Log;
26 import android.util.Pair;
27 import android.view.Surface;
28 
29 import androidx.test.filters.LargeTest;
30 import androidx.test.platform.app.InstrumentationRegistry;
31 
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.Parameterized;
36 
37 import java.io.File;
38 import java.io.IOException;
39 import java.nio.ByteBuffer;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.List;
43 
44 import static org.junit.Assert.assertTrue;
45 import static org.junit.Assert.fail;
46 
47 @RunWith(Parameterized.class)
48 public class CodecEncoderSurfaceTest {
49     private static final String LOG_TAG = CodecEncoderSurfaceTest.class.getSimpleName();
50     private static final String mInpPrefix = WorkDir.getMediaDirString();
51     private static final boolean ENABLE_LOGS = false;
52 
53     private final String mCompName;
54     private final String mMime;
55     private final String mTestFile;
56     private final int mBitrate;
57     private final int mFrameRate;
58     private final int mMaxBFrames;
59     private int mLatency;
60     private boolean mReviseLatency;
61 
62     private MediaExtractor mExtractor;
63     private MediaCodec mEncoder;
64     private CodecAsyncHandler mAsyncHandleEncoder;
65     private MediaCodec mDecoder;
66     private CodecAsyncHandler mAsyncHandleDecoder;
67     private boolean mIsCodecInAsyncMode;
68     private boolean mSignalEOSWithLastFrame;
69     private boolean mSawDecInputEOS;
70     private boolean mSawDecOutputEOS;
71     private boolean mSawEncOutputEOS;
72     private int mDecInputCount;
73     private int mDecOutputCount;
74     private int mEncOutputCount;
75 
76     private boolean mSaveToMem;
77     private OutputManager mOutputBuff;
78 
79     private Surface mSurface;
80 
81     private MediaMuxer mMuxer;
82     private int mTrackID = -1;
83 
84     static {
85         android.os.Bundle args = InstrumentationRegistry.getArguments();
86         CodecTestBase.mimeSelKeys = args.getString(CodecTestBase.MIME_SEL_KEY);
87     }
88 
CodecEncoderSurfaceTest(String encoder, String mime, String testFile, int bitrate, int frameRate)89     public CodecEncoderSurfaceTest(String encoder, String mime, String testFile, int bitrate,
90             int frameRate) {
91         mCompName = encoder;
92         mMime = mime;
93         mTestFile = testFile;
94         mBitrate = bitrate;
95         mFrameRate = frameRate;
96         mMaxBFrames = 0;
97         mLatency = mMaxBFrames;
98         mReviseLatency = false;
99         mAsyncHandleDecoder = new CodecAsyncHandler();
100         mAsyncHandleEncoder = new CodecAsyncHandler();
101     }
102 
103     @Before
isCodecNameValid()104     public void isCodecNameValid() {
105         if (mCompName.startsWith(CodecTestBase.INVALID_CODEC)) {
106             fail("no valid component available for current test ");
107         }
108     }
109 
110     @Parameterized.Parameters(name = "{index}({0}_{1})")
input()111     public static Collection<Object[]> input() {
112         final boolean isEncoder = true;
113         final boolean needAudio = false;
114         final boolean needVideo = true;
115         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
116                 // Video - CodecMime, test file, bit rate, frame rate
117                 {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp", 128000, 15},
118                 {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4", 64000, 12},
119                 {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
120                 {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
121                 {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
122                 {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
123                 {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_cif_768kbps_30fps_avc.mp4", 512000, 30},
124         });
125         return CodecTestBase.prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo,
126                 true);
127     }
128 
hasSeenError()129     private boolean hasSeenError() {
130         return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError();
131     }
132 
setUpSource(String srcFile)133     private MediaFormat setUpSource(String srcFile) throws IOException {
134         mExtractor = new MediaExtractor();
135         mExtractor.setDataSource(mInpPrefix + srcFile);
136         for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
137             MediaFormat format = mExtractor.getTrackFormat(trackID);
138             String mime = format.getString(MediaFormat.KEY_MIME);
139             if (mime.startsWith("video/")) {
140                 mExtractor.selectTrack(trackID);
141                 // COLOR_FormatYUV420Flexible by default should be supported by all components
142                 // This call shouldn't effect configure() call for any codec
143                 format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
144                         MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
145                 return format;
146             }
147         }
148         mExtractor.release();
149         fail("No video track found in file: " + srcFile);
150         return null;
151     }
152 
resetContext(boolean isAsync, boolean signalEOSWithLastFrame)153     private void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
154         mAsyncHandleDecoder.resetContext();
155         mAsyncHandleEncoder.resetContext();
156         mIsCodecInAsyncMode = isAsync;
157         mSignalEOSWithLastFrame = signalEOSWithLastFrame;
158         mSawDecInputEOS = false;
159         mSawDecOutputEOS = false;
160         mSawEncOutputEOS = false;
161         mDecInputCount = 0;
162         mDecOutputCount = 0;
163         mEncOutputCount = 0;
164     }
165 
configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync, boolean signalEOSWithLastFrame)166     private void configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync,
167             boolean signalEOSWithLastFrame) {
168         resetContext(isAsync, signalEOSWithLastFrame);
169         mAsyncHandleEncoder.setCallBack(mEncoder, isAsync);
170         mEncoder.configure(encFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
171         if (mEncoder.getInputFormat().containsKey(MediaFormat.KEY_LATENCY)) {
172             mReviseLatency = true;
173             mLatency = mEncoder.getInputFormat().getInteger(MediaFormat.KEY_LATENCY);
174         }
175         mSurface = mEncoder.createInputSurface();
176         assertTrue("Surface is not valid", mSurface.isValid());
177         mAsyncHandleDecoder.setCallBack(mDecoder, isAsync);
178         mDecoder.configure(decFormat, mSurface, null, 0);
179         if (ENABLE_LOGS) {
180             Log.v(LOG_TAG, "codec configured");
181         }
182     }
183 
enqueueDecoderEOS(int bufferIndex)184     private void enqueueDecoderEOS(int bufferIndex) {
185         if (!mSawDecInputEOS) {
186             mDecoder.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
187             mSawDecInputEOS = true;
188             if (ENABLE_LOGS) {
189                 Log.v(LOG_TAG, "Queued End of Stream");
190             }
191         }
192     }
193 
enqueueDecoderInput(int bufferIndex)194     private void enqueueDecoderInput(int bufferIndex) {
195         if (mExtractor.getSampleSize() < 0) {
196             enqueueDecoderEOS(bufferIndex);
197         } else {
198             ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex);
199             mExtractor.readSampleData(inputBuffer, 0);
200             int size = (int) mExtractor.getSampleSize();
201             long pts = mExtractor.getSampleTime();
202             int extractorFlags = mExtractor.getSampleFlags();
203             int codecFlags = 0;
204             if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
205                 codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
206             }
207             if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
208                 codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
209             }
210             if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
211                 codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
212                 mSawDecInputEOS = true;
213             }
214             if (ENABLE_LOGS) {
215                 Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
216                         " flags: " + codecFlags);
217             }
218             mDecoder.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
219             if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
220                     MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
221                 mOutputBuff.saveInPTS(pts);
222                 mDecInputCount++;
223             }
224         }
225     }
226 
dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info)227     private void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
228         if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
229             mSawDecOutputEOS = true;
230         }
231         if (ENABLE_LOGS) {
232             Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
233                     info.size + " timestamp: " + info.presentationTimeUs);
234         }
235         if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
236             mDecOutputCount++;
237         }
238         mDecoder.releaseOutputBuffer(bufferIndex, mSurface != null);
239     }
240 
dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info)241     private void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
242         if (ENABLE_LOGS) {
243             Log.v(LOG_TAG, "encoder output: id: " + bufferIndex + " flags: " + info.flags +
244                     " size: " + info.size + " timestamp: " + info.presentationTimeUs);
245         }
246         if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
247             mSawEncOutputEOS = true;
248         }
249         if (info.size > 0) {
250             ByteBuffer buf = mEncoder.getOutputBuffer(bufferIndex);
251             if (mSaveToMem) {
252                 mOutputBuff.saveToMemory(buf, info);
253             }
254             if (mMuxer != null) {
255                 if (mTrackID == -1) {
256                     mTrackID = mMuxer.addTrack(mEncoder.getOutputFormat());
257                     mMuxer.start();
258                 }
259                 mMuxer.writeSampleData(mTrackID, buf, info);
260             }
261             if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
262                 mOutputBuff.saveOutPTS(info.presentationTimeUs);
263                 mEncOutputCount++;
264             }
265         }
266         mEncoder.releaseOutputBuffer(bufferIndex, false);
267     }
268 
tryEncoderOutput(long timeOutUs)269     private void tryEncoderOutput(long timeOutUs) throws InterruptedException {
270         if (mIsCodecInAsyncMode) {
271             if (!hasSeenError() && !mSawEncOutputEOS) {
272                 int retry = 0;
273                 while (mReviseLatency) {
274                     if (mAsyncHandleEncoder.hasOutputFormatChanged()) {
275                         mReviseLatency = false;
276                         int actualLatency = mAsyncHandleEncoder.getOutputFormat()
277                                 .getInteger(MediaFormat.KEY_LATENCY, mLatency);
278                         if (mLatency < actualLatency) {
279                             mLatency = actualLatency;
280                             return;
281                         }
282                     } else {
283                         if (retry > CodecTestBase.RETRY_LIMIT) throw new InterruptedException(
284                                 "did not receive output format changed for encoder after " +
285                                         CodecTestBase.Q_DEQ_TIMEOUT_US * CodecTestBase.RETRY_LIMIT +
286                                         " us");
287                         Thread.sleep(CodecTestBase.Q_DEQ_TIMEOUT_US / 1000);
288                         retry ++;
289                     }
290                 }
291                 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleEncoder.getOutput();
292                 if (element != null) {
293                     dequeueEncoderOutput(element.first, element.second);
294                 }
295             }
296         } else {
297             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
298             if (!mSawEncOutputEOS) {
299                 int outputBufferId = mEncoder.dequeueOutputBuffer(outInfo, timeOutUs);
300                 if (outputBufferId >= 0) {
301                     dequeueEncoderOutput(outputBufferId, outInfo);
302                 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
303                     mLatency = mEncoder.getOutputFormat()
304                             .getInteger(MediaFormat.KEY_LATENCY, mLatency);
305                 }
306             }
307         }
308     }
309 
waitForAllEncoderOutputs()310     private void waitForAllEncoderOutputs() throws InterruptedException {
311         if (mIsCodecInAsyncMode) {
312             while (!hasSeenError() && !mSawEncOutputEOS) {
313                 tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
314             }
315         } else {
316             while (!mSawEncOutputEOS) {
317                 tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
318             }
319         }
320     }
321 
queueEOS()322     private void queueEOS() throws InterruptedException {
323         if (mIsCodecInAsyncMode) {
324             while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
325                 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
326                 if (element != null) {
327                     int bufferID = element.first;
328                     MediaCodec.BufferInfo info = element.second;
329                     if (info != null) {
330                         dequeueDecoderOutput(bufferID, info);
331                     } else {
332                         enqueueDecoderEOS(element.first);
333                     }
334                 }
335             }
336         } else {
337             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
338             while (!mSawDecInputEOS) {
339                 int outputBufferId =
340                         mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
341                 if (outputBufferId >= 0) {
342                     dequeueDecoderOutput(outputBufferId, outInfo);
343                 }
344                 int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US);
345                 if (inputBufferId != -1) {
346                     enqueueDecoderEOS(inputBufferId);
347                 }
348             }
349         }
350         if (mIsCodecInAsyncMode) {
351             while (!hasSeenError() && !mSawDecOutputEOS) {
352                 Pair<Integer, MediaCodec.BufferInfo> decOp = mAsyncHandleDecoder.getOutput();
353                 if (decOp != null) dequeueDecoderOutput(decOp.first, decOp.second);
354                 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
355                 if (mDecOutputCount - mEncOutputCount > mLatency) {
356                     tryEncoderOutput(-1);
357                 }
358             }
359         } else {
360             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
361             while (!mSawDecOutputEOS) {
362                 int outputBufferId =
363                         mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
364                 if (outputBufferId >= 0) {
365                     dequeueDecoderOutput(outputBufferId, outInfo);
366                 }
367                 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
368                 if (mDecOutputCount - mEncOutputCount > mLatency) {
369                     tryEncoderOutput(-1);
370                 }
371             }
372         }
373     }
374 
doWork(int frameLimit)375     private void doWork(int frameLimit) throws InterruptedException {
376         int frameCnt = 0;
377         if (mIsCodecInAsyncMode) {
378             // dequeue output after inputEOS is expected to be done in waitForAllOutputs()
379             while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
380                 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
381                 if (element != null) {
382                     int bufferID = element.first;
383                     MediaCodec.BufferInfo info = element.second;
384                     if (info != null) {
385                         // <id, info> corresponds to output callback. Handle it accordingly
386                         dequeueDecoderOutput(bufferID, info);
387                     } else {
388                         // <id, null> corresponds to input callback. Handle it accordingly
389                         enqueueDecoderInput(bufferID);
390                         frameCnt++;
391                     }
392                 }
393                 // check decoder EOS
394                 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
395                 // encoder output
396                 if (mDecOutputCount - mEncOutputCount > mLatency) {
397                     tryEncoderOutput(-1);
398                 }
399             }
400         } else {
401             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
402             while (!mSawDecInputEOS && frameCnt < frameLimit) {
403                 // decoder input
404                 int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US);
405                 if (inputBufferId != -1) {
406                     enqueueDecoderInput(inputBufferId);
407                     frameCnt++;
408                 }
409                 // decoder output
410                 int outputBufferId =
411                         mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
412                 if (outputBufferId >= 0) {
413                     dequeueDecoderOutput(outputBufferId, outInfo);
414                 }
415                 // check decoder EOS
416                 if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
417                 // encoder output
418                 if (mDecOutputCount - mEncOutputCount > mLatency) {
419                     tryEncoderOutput(-1);
420                 }
421             }
422         }
423     }
424 
setUpEncoderFormat(MediaFormat decoderFormat)425     private MediaFormat setUpEncoderFormat(MediaFormat decoderFormat) {
426         MediaFormat encoderFormat = new MediaFormat();
427         encoderFormat.setString(MediaFormat.KEY_MIME, mMime);
428         encoderFormat.setInteger(MediaFormat.KEY_WIDTH,
429                 decoderFormat.getInteger(MediaFormat.KEY_WIDTH));
430         encoderFormat.setInteger(MediaFormat.KEY_HEIGHT,
431                 decoderFormat.getInteger(MediaFormat.KEY_HEIGHT));
432         encoderFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
433         encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate);
434         encoderFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
435         encoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
436                 MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
437         encoderFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames);
438         return encoderFormat;
439     }
440 
441     /**
442      * Tests listed encoder components for sync and async mode in surface mode.The output has to
443      * be consistent (not flaky) in all runs.
444      */
445     @LargeTest
446     @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleEncodeFromSurface()447     public void testSimpleEncodeFromSurface() throws IOException, InterruptedException {
448         MediaFormat decoderFormat = setUpSource(mTestFile);
449         MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
450         String decoder = codecList.findDecoderForFormat(decoderFormat);
451         if (decoder == null) {
452             mExtractor.release();
453             fail("no suitable decoder found for format: " + decoderFormat.toString());
454         }
455         mDecoder = MediaCodec.createByCodecName(decoder);
456         MediaFormat encoderFormat = setUpEncoderFormat(decoderFormat);
457         boolean muxOutput = true;
458         {
459             mEncoder = MediaCodec.createByCodecName(mCompName);
460             /* TODO(b/149027258) */
461             mSaveToMem = false;
462             OutputManager ref = new OutputManager();
463             OutputManager test = new OutputManager();
464             int loopCounter = 0;
465             boolean[] boolStates = {true, false};
466             for (boolean isAsync : boolStates) {
467                 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
468                 mOutputBuff = loopCounter == 0 ? ref : test;
469                 mOutputBuff.reset();
470                 if (muxOutput && loopCounter == 0) {
471                     String tmpPath;
472                     int muxerFormat;
473                     if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
474                             mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
475                         muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM;
476                         tmpPath = File.createTempFile("tmp", ".webm").getAbsolutePath();
477                     } else {
478                         muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
479                         tmpPath = File.createTempFile("tmp", ".mp4").getAbsolutePath();
480                     }
481                     mMuxer = new MediaMuxer(tmpPath, muxerFormat);
482                 }
483                 configureCodec(decoderFormat, encoderFormat, isAsync, false);
484                 mEncoder.start();
485                 mDecoder.start();
486                 doWork(Integer.MAX_VALUE);
487                 queueEOS();
488                 waitForAllEncoderOutputs();
489                 if (muxOutput) {
490                     if (mTrackID != -1) {
491                         mMuxer.stop();
492                         mTrackID = -1;
493                     }
494                     if (mMuxer != null) {
495                         mMuxer.release();
496                         mMuxer = null;
497                     }
498                 }
499                 /* TODO(b/147348711) */
500                 if (false) mDecoder.stop();
501                 else mDecoder.reset();
502                 /* TODO(b/147348711) */
503                 if (false) mEncoder.stop();
504                 else mEncoder.reset();
505                 String log = String.format(
506                         "format: %s \n codec: %s, file: %s, mode: %s:: ",
507                         encoderFormat, mCompName, mTestFile, (isAsync ? "async" : "sync"));
508                 assertTrue(log + " unexpected error", !hasSeenError());
509                 assertTrue(log + "no input sent", 0 != mDecInputCount);
510                 assertTrue(log + "no decoder output received", 0 != mDecOutputCount);
511                 assertTrue(log + "no encoder output received", 0 != mEncOutputCount);
512                 assertTrue(log + "decoder input count != output count, act/exp: " +
513                         mDecOutputCount +
514                         " / " + mDecInputCount, mDecInputCount == mDecOutputCount);
515                 /* TODO(b/153127506)
516                  *  Currently disabling all encoder output checks. Added checks only for encoder
517                  *  timeStamp is in increasing order or not.
518                  *  Once issue is fixed remove increasing timestamp check and enable encoder checks.
519                  */
520                 /*assertTrue(log + "encoder output count != decoder output count, act/exp: " +
521                                 mEncOutputCount + " / " + mDecOutputCount,
522                         mEncOutputCount == mDecOutputCount);
523                 if (loopCounter != 0) {
524                     assertTrue(log + "encoder output is flaky", ref.equals(test));
525                 } else {
526                     assertTrue(log + " input pts list and output pts list are not identical",
527                             ref.isOutPtsListIdenticalToInpPtsList((false)));
528                 }*/
529                 if (loopCounter != 0) {
530                     assertTrue("test output pts is not strictly increasing",
531                             test.isPtsStrictlyIncreasing(Long.MIN_VALUE));
532                 } else {
533                     assertTrue("ref output pts is not strictly increasing",
534                             ref.isPtsStrictlyIncreasing(Long.MIN_VALUE));
535                 }
536                 loopCounter++;
537                 mSurface.release();
538                 mSurface = null;
539             }
540             mEncoder.release();
541         }
542         mDecoder.release();
543         mExtractor.release();
544     }
545 
nativeTestSimpleEncode(String encoder, String decoder, String mime, String testFile, String muxFile, int bitrate, int framerate)546     private native boolean nativeTestSimpleEncode(String encoder, String decoder, String mime,
547             String testFile, String muxFile, int bitrate, int framerate);
548 
549     @LargeTest
550     @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
testSimpleEncodeFromSurfaceNative()551     public void testSimpleEncodeFromSurfaceNative() throws IOException {
552         MediaFormat decoderFormat = setUpSource(mTestFile);
553         MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
554         String decoder = codecList.findDecoderForFormat(decoderFormat);
555         if (decoder == null) {
556             mExtractor.release();
557             fail("no suitable decoder found for format: " + decoderFormat.toString());
558         }
559         {
560             String tmpPath;
561             if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
562                     mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
563                 tmpPath = File.createTempFile("tmp", ".webm").getAbsolutePath();
564             } else {
565                 tmpPath = File.createTempFile("tmp", ".mp4").getAbsolutePath();
566             }
567             assertTrue(nativeTestSimpleEncode(mCompName, decoder, mMime, mInpPrefix + mTestFile,
568                     tmpPath, mBitrate, mFrameRate));
569         }
570     }
571 }
572