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