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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "NativeCodecEncoderSurfaceTest"
19 #include <log/log.h>
20 
21 #include <android/native_window_jni.h>
22 #include <jni.h>
23 #include <media/NdkMediaExtractor.h>
24 #include <media/NdkMediaMuxer.h>
25 #include <sys/stat.h>
26 
27 #include "NativeCodecTestBase.h"
28 #include "NativeMediaCommon.h"
29 
30 class CodecEncoderSurfaceTest {
31   private:
32     const char* mMediaType;
33     ANativeWindow* mWindow;
34     AMediaExtractor* mExtractor;
35     AMediaFormat* mDecFormat;
36     AMediaFormat* mEncFormat;
37     AMediaMuxer* mMuxer;
38     AMediaCodec* mDecoder;
39     AMediaCodec* mEncoder;
40     CodecAsyncHandler mAsyncHandleDecoder;
41     CodecAsyncHandler mAsyncHandleEncoder;
42     bool mIsCodecInAsyncMode;
43     bool mSawDecInputEOS;
44     bool mSawDecOutputEOS;
45     bool mSawEncOutputEOS;
46     bool mSignalEOSWithLastFrame;
47     int mDecInputCount;
48     int mDecOutputCount;
49     int mEncOutputCount;
50     int mMaxBFrames;
51     int mLatency;
52     bool mReviseLatency;
53     int mMuxTrackID;
54 
55     OutputManager* mOutputBuff;
56     OutputManager* mRefBuff;
57     OutputManager* mTestBuff;
58     bool mSaveToMem;
59 
60     std::string mErrorLogs;
61     std::string mTestEnv;
62 
63     bool setUpExtractor(const char* srcFile, const char* srcMediaType, int colorFormat);
64     void deleteExtractor();
65     bool configureCodec(bool isAsync, bool signalEOSWithLastFrame, bool usePersistentSurface);
66     void resetContext(bool isAsync, bool signalEOSWithLastFrame);
67     bool enqueueDecoderInput(size_t bufferIndex);
68     bool dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo);
69     bool dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info);
70     bool tryEncoderOutput(long timeOutUs);
71     bool waitForAllEncoderOutputs();
72     bool queueEOS();
73     bool enqueueDecoderEOS(size_t bufferIndex);
74     bool doWork(int frameLimit);
hasSeenError()75     bool hasSeenError() { return mAsyncHandleDecoder.getError() || mAsyncHandleEncoder.getError(); }
76 
77   public:
getErrorMsg()78     std::string getErrorMsg() {
79         return mTestEnv +
80                 "###################       Error Details         #####################\n" +
81                 mErrorLogs;
82     }
83     CodecEncoderSurfaceTest(const char* mediaType, const char* cfgParams, const char* separator);
84     ~CodecEncoderSurfaceTest();
85 
86     bool testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath,
87                           const char* srcMediaType, const char* muxOutPath, int colorFormat,
88                           bool usePersistentSurface, int frameLimit);
89 };
90 
CodecEncoderSurfaceTest(const char * mediaType,const char * cfgParams,const char * separator)91 CodecEncoderSurfaceTest::CodecEncoderSurfaceTest(const char* mediaType, const char* cfgParams,
92                                                  const char* separator)
93     : mMediaType{mediaType} {
94     mWindow = nullptr;
95     mExtractor = nullptr;
96     mDecFormat = nullptr;
97     mEncFormat = deSerializeMediaFormat(cfgParams, separator);
98     mMuxer = nullptr;
99     mDecoder = nullptr;
100     mEncoder = nullptr;
101     resetContext(false, false);
102     mMaxBFrames = 0;
103     if (mEncFormat != nullptr) {
104         // key formalized in Android U (sdk==34).
105         // Use internally-defined when running on earlier releases, such as happens with MTS
106         if (__builtin_available(android __ANDROID_API_U__, *)) {
107             AMediaFormat_getInt32(mEncFormat, AMEDIAFORMAT_KEY_MAX_B_FRAMES, &mMaxBFrames);
108         } else {
109             AMediaFormat_getInt32(mEncFormat, COMPATIBLE_AMEDIAFORMAT_KEY_MAX_B_FRAMES,
110                                   &mMaxBFrames);
111         }
112     }
113     mLatency = mMaxBFrames;
114     mReviseLatency = false;
115     mMuxTrackID = -1;
116     mRefBuff = new OutputManager();
117     mTestBuff = new OutputManager(mRefBuff->getSharedErrorLogs());
118 }
119 
~CodecEncoderSurfaceTest()120 CodecEncoderSurfaceTest::~CodecEncoderSurfaceTest() {
121     deleteExtractor();
122     if (mWindow) {
123         ANativeWindow_release(mWindow);
124         mWindow = nullptr;
125     }
126     if (mEncFormat) {
127         AMediaFormat_delete(mEncFormat);
128         mEncFormat = nullptr;
129     }
130     if (mMuxer) {
131         AMediaMuxer_delete(mMuxer);
132         mMuxer = nullptr;
133     }
134     if (mDecoder) {
135         AMediaCodec_delete(mDecoder);
136         mDecoder = nullptr;
137     }
138     if (mEncoder) {
139         AMediaCodec_delete(mEncoder);
140         mEncoder = nullptr;
141     }
142     delete mRefBuff;
143     delete mTestBuff;
144 }
145 
setUpExtractor(const char * srcFile,const char * srcMediaType,int colorFormat)146 bool CodecEncoderSurfaceTest::setUpExtractor(const char* srcFile, const char* srcMediaType,
147                                              int colorFormat) {
148     FILE* fp = fopen(srcFile, "rbe");
149     struct stat buf {};
150     if (fp && !fstat(fileno(fp), &buf)) {
151         deleteExtractor();
152         mExtractor = AMediaExtractor_new();
153         media_status_t res =
154                 AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size);
155         if (res != AMEDIA_OK) {
156             deleteExtractor();
157         } else {
158             for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor);
159                  trackID++) {
160                 AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID);
161                 const char* mediaType = nullptr;
162                 AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
163                 if (mediaType && strcmp(mediaType, srcMediaType) == 0) {
164                     AMediaExtractor_selectTrack(mExtractor, trackID);
165                     AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, colorFormat);
166                     mDecFormat = currFormat;
167                     break;
168                 }
169                 AMediaFormat_delete(currFormat);
170             }
171         }
172     }
173     if (fp) fclose(fp);
174     return mDecFormat != nullptr;
175 }
176 
deleteExtractor()177 void CodecEncoderSurfaceTest::deleteExtractor() {
178     if (mExtractor) {
179         AMediaExtractor_delete(mExtractor);
180         mExtractor = nullptr;
181     }
182     if (mDecFormat) {
183         AMediaFormat_delete(mDecFormat);
184         mDecFormat = nullptr;
185     }
186 }
187 
configureCodec(bool isAsync,bool signalEOSWithLastFrame,bool usePersistentSurface)188 bool CodecEncoderSurfaceTest::configureCodec(bool isAsync, bool signalEOSWithLastFrame,
189                                              bool usePersistentSurface) {
190     RETURN_IF_NULL(mEncFormat,
191                    std::string{"encountered error during deserialization of media format"})
192     resetContext(isAsync, signalEOSWithLastFrame);
193     mTestEnv = "###################      Test Environment       #####################\n";
194     {
195         char* name = nullptr;
196         media_status_t val = AMediaCodec_getName(mEncoder, &name);
197         if (AMEDIA_OK != val) {
198             mErrorLogs = StringFormat("%s with error %d \n", "AMediaCodec_getName failed", val);
199             return false;
200         }
201         if (!name) {
202             mErrorLogs = std::string{"AMediaCodec_getName returned null"};
203             return false;
204         }
205         mTestEnv.append(StringFormat("Component name %s \n", name));
206         AMediaCodec_releaseName(mEncoder, name);
207     }
208     {
209         char* name = nullptr;
210         media_status_t val = AMediaCodec_getName(mDecoder, &name);
211         if (AMEDIA_OK != val) {
212             mErrorLogs = StringFormat("%s with error %d \n", "AMediaCodec_getName failed", val);
213             return false;
214         }
215         if (!name) {
216             mErrorLogs = std::string{"AMediaCodec_getName returned null"};
217             return false;
218         }
219         mTestEnv.append(StringFormat("Decoder Component name %s \n", name));
220         AMediaCodec_releaseName(mDecoder, name);
221     }
222     mTestEnv += StringFormat("Format under test :- %s \n", AMediaFormat_toString(mEncFormat));
223     mTestEnv += StringFormat("Format of Decoder input :- %s \n", AMediaFormat_toString(mDecFormat));
224     mTestEnv += StringFormat("Encoder and Decoder are operating in :- %s mode \n",
225                              (isAsync ? "asynchronous" : "synchronous"));
226     mTestEnv += StringFormat("Components received input eos :- %s \n",
227                              (signalEOSWithLastFrame ? "with full buffer" : "with empty buffer"));
228     RETURN_IF_FAIL(mAsyncHandleEncoder.setCallBack(mEncoder, isAsync),
229                    "AMediaCodec_setAsyncNotifyCallback failed")
230     RETURN_IF_FAIL(AMediaCodec_configure(mEncoder, mEncFormat, nullptr, nullptr,
231                                          AMEDIACODEC_CONFIGURE_FLAG_ENCODE),
232                    "AMediaCodec_configure failed")
233     AMediaFormat* inpFormat = AMediaCodec_getInputFormat(mEncoder);
234     mReviseLatency = AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency);
235     AMediaFormat_delete(inpFormat);
236 
237     if (usePersistentSurface) {
238         RETURN_IF_FAIL(AMediaCodec_createPersistentInputSurface(&mWindow),
239                        "AMediaCodec_createPersistentInputSurface failed")
240         RETURN_IF_FAIL(AMediaCodec_setInputSurface(mEncoder,
241                                                    reinterpret_cast<ANativeWindow*>(mWindow)),
242                        "AMediaCodec_setInputSurface failed")
243     } else {
244         RETURN_IF_FAIL(AMediaCodec_createInputSurface(mEncoder, &mWindow),
245                        "AMediaCodec_createInputSurface failed")
246     }
247     RETURN_IF_FAIL(mAsyncHandleDecoder.setCallBack(mDecoder, isAsync),
248                    "AMediaCodec_setAsyncNotifyCallback failed")
249     RETURN_IF_FAIL(AMediaCodec_configure(mDecoder, mDecFormat, mWindow, nullptr, 0),
250                    "AMediaCodec_configure failed")
251     return !hasSeenError();
252 }
253 
resetContext(bool isAsync,bool signalEOSWithLastFrame)254 void CodecEncoderSurfaceTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
255     mAsyncHandleDecoder.resetContext();
256     mAsyncHandleEncoder.resetContext();
257     mIsCodecInAsyncMode = isAsync;
258     mSawDecInputEOS = false;
259     mSawDecOutputEOS = false;
260     mSawEncOutputEOS = false;
261     mSignalEOSWithLastFrame = signalEOSWithLastFrame;
262     mDecInputCount = 0;
263     mDecOutputCount = 0;
264     mEncOutputCount = 0;
265 }
266 
enqueueDecoderEOS(size_t bufferIndex)267 bool CodecEncoderSurfaceTest::enqueueDecoderEOS(size_t bufferIndex) {
268     if (!hasSeenError() && !mSawDecInputEOS) {
269         RETURN_IF_FAIL(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, 0, 0,
270                                                     AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
271                        "Queued Decoder End of Stream Failed")
272         mSawDecInputEOS = true;
273         ALOGV("Queued Decoder End of Stream");
274     }
275     return !hasSeenError();
276 }
277 
enqueueDecoderInput(size_t bufferIndex)278 bool CodecEncoderSurfaceTest::enqueueDecoderInput(size_t bufferIndex) {
279     if (AMediaExtractor_getSampleSize(mExtractor) < 0) {
280         return enqueueDecoderEOS(bufferIndex);
281     } else {
282         uint32_t flags = 0;
283         size_t bufSize = 0;
284         uint8_t* buf = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufSize);
285         RETURN_IF_NULL(buf, std::string{"AMediaCodec_getInputBuffer failed"})
286         ssize_t size = AMediaExtractor_getSampleSize(mExtractor);
287         int64_t pts = AMediaExtractor_getSampleTime(mExtractor);
288         RETURN_IF_TRUE(size > bufSize,
289                        StringFormat("extractor sample size exceeds codec input buffer size %zu %zu",
290                                     size, bufSize))
291         RETURN_IF_TRUE(size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize),
292                        std::string{"AMediaExtractor_readSampleData failed"})
293         if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) {
294             flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
295             mSawDecInputEOS = true;
296         }
297         RETURN_IF_FAIL(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, size, pts, flags),
298                        "AMediaCodec_queueInputBuffer failed")
299         ALOGV("input: id: %zu  size: %zu  pts: %" PRId64 "  flags: %d", bufferIndex, size, pts,
300               flags);
301         if (size > 0) {
302             mOutputBuff->saveInPTS(pts);
303             mDecInputCount++;
304         }
305     }
306     return !hasSeenError();
307 }
308 
dequeueDecoderOutput(size_t bufferIndex,AMediaCodecBufferInfo * bufferInfo)309 bool CodecEncoderSurfaceTest::dequeueDecoderOutput(size_t bufferIndex,
310                                                    AMediaCodecBufferInfo* bufferInfo) {
311     if ((bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
312         mSawDecOutputEOS = true;
313     }
314     if (bufferInfo->size > 0 && (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
315         mDecOutputCount++;
316     }
317     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, bufferInfo->size,
318           bufferInfo->presentationTimeUs, bufferInfo->flags);
319     RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, mWindow != nullptr),
320                    "AMediaCodec_releaseOutputBuffer failed")
321     return !hasSeenError();
322 }
323 
dequeueEncoderOutput(size_t bufferIndex,AMediaCodecBufferInfo * info)324 bool CodecEncoderSurfaceTest::dequeueEncoderOutput(size_t bufferIndex,
325                                                    AMediaCodecBufferInfo* info) {
326     if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
327         mSawEncOutputEOS = true;
328     }
329     if (info->size > 0) {
330         size_t buffSize;
331         uint8_t* buf = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &buffSize);
332         // NdkMediaCodec calls ABuffer::data, which already adds offset
333         info->offset = 0;
334         if (mSaveToMem) {
335             mOutputBuff->saveToMemory(buf, info);
336         }
337         if (mMuxer != nullptr) {
338             if (mMuxTrackID == -1) {
339                 mMuxTrackID = AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder));
340                 RETURN_IF_FAIL(AMediaMuxer_start(mMuxer), "AMediaMuxer_start failed")
341             }
342             if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
343                 RETURN_IF_FAIL(AMediaMuxer_writeSampleData(mMuxer, mMuxTrackID, buf, info),
344                                 "AMediaMuxer_writeSampleData failed")
345             }
346         }
347         if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
348             mOutputBuff->saveOutPTS(info->presentationTimeUs);
349             mEncOutputCount++;
350         }
351     }
352     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, info->size,
353           info->presentationTimeUs, info->flags);
354     RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mEncoder, bufferIndex, false),
355                    "AMediaCodec_releaseOutputBuffer failed")
356     return !hasSeenError();
357 }
358 
tryEncoderOutput(long timeOutUs)359 bool CodecEncoderSurfaceTest::tryEncoderOutput(long timeOutUs) {
360     if (mIsCodecInAsyncMode) {
361         if (!hasSeenError() && !mSawEncOutputEOS) {
362             while (mReviseLatency) {
363                 if (!mAsyncHandleEncoder.waitOnFormatChange()) {
364                     mErrorLogs.append("taking too long to receive onOutputFormatChanged callback");
365                     return false;
366                 }
367                 int actualLatency;
368                 mReviseLatency = false;
369                 if (AMediaFormat_getInt32(mAsyncHandleEncoder.getOutputFormat(),
370                                           AMEDIAFORMAT_KEY_LATENCY, &actualLatency)) {
371                     if (mLatency < actualLatency) {
372                         mLatency = actualLatency;
373                         return !hasSeenError();
374                     }
375                 }
376             }
377             callbackObject element = mAsyncHandleEncoder.getOutput();
378             if (element.bufferIndex >= 0) {
379                 if (!dequeueEncoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
380             }
381         }
382     } else {
383         AMediaCodecBufferInfo outInfo;
384         if (!mSawEncOutputEOS) {
385             int bufferID = AMediaCodec_dequeueOutputBuffer(mEncoder, &outInfo, timeOutUs);
386             if (bufferID >= 0) {
387                 if (!dequeueEncoderOutput(bufferID, &outInfo)) return false;
388             } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
389                 AMediaFormat* outFormat = AMediaCodec_getOutputFormat(mEncoder);
390                 AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency);
391                 AMediaFormat_delete(outFormat);
392             } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
393             } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
394             } else {
395                 mErrorLogs.append(
396                         StringFormat("unexpected return value from *_dequeueOutputBuffer: %d",
397                                      bufferID));
398                 return false;
399             }
400         }
401     }
402     return !hasSeenError();
403 }
404 
waitForAllEncoderOutputs()405 bool CodecEncoderSurfaceTest::waitForAllEncoderOutputs() {
406     if (mIsCodecInAsyncMode) {
407         while (!hasSeenError() && !mSawEncOutputEOS) {
408             if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
409         }
410     } else {
411         while (!mSawEncOutputEOS) {
412             if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
413         }
414     }
415     return !hasSeenError();
416 }
417 
queueEOS()418 bool CodecEncoderSurfaceTest::queueEOS() {
419     if (mIsCodecInAsyncMode) {
420         while (!hasSeenError() && !mSawDecInputEOS) {
421             callbackObject element = mAsyncHandleDecoder.getWork();
422             if (element.bufferIndex >= 0) {
423                 if (element.isInput) {
424                     if (!enqueueDecoderEOS(element.bufferIndex)) return false;
425                 } else {
426                     if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
427                         return false;
428                     }
429                 }
430             }
431         }
432     } else {
433         AMediaCodecBufferInfo outInfo;
434         while (!mSawDecInputEOS) {
435             ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
436             if (oBufferID >= 0) {
437                 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
438             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
439             } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
440             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
441             } else {
442                 mErrorLogs.append(
443                         StringFormat("unexpected return value from *_dequeueOutputBuffer: %d",
444                                      oBufferID));
445                 return false;
446             }
447             ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
448             if (iBufferId >= 0) {
449                 if (!enqueueDecoderEOS(iBufferId)) return false;
450             } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
451             } else {
452                 mErrorLogs.append(
453                         StringFormat("unexpected return value from *_dequeueInputBuffer: %zd",
454                                      iBufferId));
455                 return false;
456             }
457         }
458     }
459 
460     if (mIsCodecInAsyncMode) {
461         // output processing after queuing EOS is done in waitForAllOutputs()
462         while (!hasSeenError() && !mSawDecOutputEOS) {
463             callbackObject element = mAsyncHandleDecoder.getOutput();
464             if (element.bufferIndex >= 0) {
465                 if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
466             }
467             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
468             if (mDecOutputCount - mEncOutputCount > mLatency) {
469                 if (!tryEncoderOutput(-1)) return false;
470             }
471         }
472     } else {
473         AMediaCodecBufferInfo outInfo;
474         // output processing after queuing EOS is done in waitForAllOutputs()
475         while (!mSawDecOutputEOS) {
476             if (!mSawDecOutputEOS) {
477                 ssize_t oBufferID =
478                         AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
479                 if (oBufferID >= 0) {
480                     if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
481                 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
482                 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
483                 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
484                 } else {
485                     mErrorLogs.append(
486                             StringFormat("unexpected return value from *_dequeueOutputBuffer: %d",
487                                          oBufferID));
488                     return false;
489                 }
490             }
491             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
492             if (mDecOutputCount - mEncOutputCount > mLatency) {
493                 if (!tryEncoderOutput(-1)) return false;
494             }
495         }
496     }
497     return !hasSeenError();
498 }
499 
doWork(int frameLimit)500 bool CodecEncoderSurfaceTest::doWork(int frameLimit) {
501     int frameCnt = 0;
502     if (mIsCodecInAsyncMode) {
503         // output processing after queuing EOS is done in waitForAllOutputs()
504         while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
505             callbackObject element = mAsyncHandleDecoder.getWork();
506             if (element.bufferIndex >= 0) {
507                 if (element.isInput) {
508                     if (!enqueueDecoderInput(element.bufferIndex)) return false;
509                     frameCnt++;
510                 } else {
511                     if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
512                         return false;
513                     }
514                 }
515             }
516             // check decoder EOS
517             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
518             // encoder output
519             if (mDecOutputCount - mEncOutputCount > mLatency) {
520                 if (!tryEncoderOutput(-1)) return false;
521             }
522         }
523     } else {
524         AMediaCodecBufferInfo outInfo;
525         // output processing after queuing EOS is done in waitForAllOutputs()
526         while (!mSawDecInputEOS && frameCnt < frameLimit) {
527             ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
528             if (oBufferID >= 0) {
529                 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
530             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
531             } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
532             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
533             } else {
534                 mErrorLogs.append(
535                         StringFormat("unexpected return value from *_dequeueOutputBuffer: %zd",
536                                      oBufferID));
537                 return false;
538             }
539             ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
540             if (iBufferId >= 0) {
541                 if (!enqueueDecoderInput(iBufferId)) return false;
542                 frameCnt++;
543             } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
544             } else {
545                 mErrorLogs.append(
546                         StringFormat("unexpected return value from *_dequeueInputBuffer: %zd",
547                                      iBufferId));
548                 return false;
549             }
550             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
551             if (mDecOutputCount - mEncOutputCount > mLatency) {
552                 if (!tryEncoderOutput(-1)) return false;
553             }
554         }
555     }
556     return !hasSeenError();
557 }
558 
testSimpleEncode(const char * encoder,const char * decoder,const char * srcPath,const char * srcMediaType,const char * muxOutPath,int colorFormat,bool usePersistentSurface,int frameLimit)559 bool CodecEncoderSurfaceTest::testSimpleEncode(const char* encoder, const char* decoder,
560                                                const char* srcPath, const char *srcMediaType,
561                                                const char* muxOutPath, int colorFormat,
562                                                bool usePersistentSurface, int frameLimit) {
563     RETURN_IF_FALSE(setUpExtractor(srcPath, srcMediaType, colorFormat),
564                     std::string{"setUpExtractor failed"})
565     bool muxOutput = muxOutPath != nullptr;
566 
567     /* TODO(b/149027258) */
568     if (true) mSaveToMem = false;
569     else mSaveToMem = true;
570     auto ref = mRefBuff;
571     auto test = mTestBuff;
572     int loopCounter = 0;
573     const bool boolStates[]{true, false};
574     for (bool isAsync : boolStates) {
575         AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
576         mOutputBuff = loopCounter == 0 ? ref : test;
577         mOutputBuff->reset();
578 
579         /* TODO(b/147348711) */
580         /* Instead of create and delete codec at every iteration, we would like to create
581          * once and use it for all iterations and delete before exiting */
582         mEncoder = AMediaCodec_createCodecByName(encoder);
583         mDecoder = AMediaCodec_createCodecByName(decoder);
584         RETURN_IF_NULL(mDecoder, StringFormat("unable to create media codec by name %s", decoder))
585         RETURN_IF_NULL(mEncoder, StringFormat("unable to create media codec by name %s", encoder))
586         FILE* ofp = nullptr;
587         if (muxOutput && loopCounter == 0) {
588             OutputFormat muxerFormat = AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4;
589             if (!strcmp(mMediaType, AMEDIA_MIMETYPE_VIDEO_VP8) ||
590                 !strcmp(mMediaType, AMEDIA_MIMETYPE_VIDEO_VP9)) {
591                 muxerFormat = AMEDIAMUXER_OUTPUT_FORMAT_WEBM;
592             }
593             ofp = fopen(muxOutPath, "wbe+");
594             if (ofp) {
595                 mMuxer = AMediaMuxer_new(fileno(ofp), muxerFormat);
596             }
597         }
598         if (!configureCodec(isAsync, false, usePersistentSurface)) return false;
599         RETURN_IF_FAIL(AMediaCodec_start(mEncoder), "Encoder AMediaCodec_start failed")
600         RETURN_IF_FAIL(AMediaCodec_start(mDecoder), "Decoder AMediaCodec_start failed")
601         if (!doWork(frameLimit)) return false;
602         if (!queueEOS()) return false;
603         if (!waitForAllEncoderOutputs()) return false;
604         if (muxOutput) {
605             if (mMuxer != nullptr) {
606                 RETURN_IF_FAIL(AMediaMuxer_stop(mMuxer), "AMediaMuxer_stop failed")
607                 mMuxTrackID = -1;
608                 RETURN_IF_FAIL(AMediaMuxer_delete(mMuxer), "AMediaMuxer_delete failed")
609                 mMuxer = nullptr;
610             }
611             if (ofp) fclose(ofp);
612         }
613         RETURN_IF_FAIL(AMediaCodec_stop(mDecoder), "AMediaCodec_stop failed for Decoder")
614         RETURN_IF_FAIL(AMediaCodec_stop(mEncoder), "AMediaCodec_stop failed for Encoder")
615         RETURN_IF_TRUE(mAsyncHandleDecoder.getError(),
616                        std::string{"Decoder has encountered error in async mode. \n"}.append(
617                                mAsyncHandleDecoder.getErrorMsg()))
618         RETURN_IF_TRUE(mAsyncHandleEncoder.getError(),
619                        std::string{"Encoder has encountered error in async mode. \n"}.append(
620                                mAsyncHandleEncoder.getErrorMsg()))
621         RETURN_IF_TRUE((0 == mDecInputCount), std::string{"Decoder has not received any input \n"})
622         RETURN_IF_TRUE((0 == mDecOutputCount), std::string{"Decoder has not sent any output \n"})
623         RETURN_IF_TRUE((0 == mEncOutputCount), std::string{"Encoder has not sent any output \n"})
624         RETURN_IF_TRUE((mDecInputCount != mDecOutputCount),
625                        StringFormat("Decoder output count is not equal to decoder input count\n "
626                                     "Input count : %s, Output count : %s\n",
627                                     mDecInputCount, mDecOutputCount))
628         RETURN_IF_TRUE((mMaxBFrames == 0 && !mOutputBuff->isPtsStrictlyIncreasing(INT32_MIN)),
629                        std::string{"Output timestamps are not strictly increasing \n"}.append(
630                                ref->getErrorMsg()))
631         RETURN_IF_TRUE((mEncOutputCount != mDecOutputCount),
632                        StringFormat("Encoder output count is not equal to decoder input "
633                                     "count\n Input count : %s, Output count : %s\n",
634                                     mDecInputCount, mEncOutputCount))
635         RETURN_IF_TRUE(!mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0),
636                        std::string{"Input pts list and Output pts list are not identical \n"}
637                                .append(ref->getErrorMsg()))
638         // TODO:(b/149027258) Remove false once output is validated across runs
639         if (false) {
640             RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
641                            std::string{"Encoder output is not consistent across runs \n"}.append(
642                                    test->getErrorMsg()))
643         }
644         loopCounter++;
645         ANativeWindow_release(mWindow);
646         mWindow = nullptr;
647         RETURN_IF_FAIL(AMediaCodec_delete(mEncoder), "AMediaCodec_delete failed for encoder")
648         mEncoder = nullptr;
649         RETURN_IF_FAIL(AMediaCodec_delete(mDecoder), "AMediaCodec_delete failed for decoder")
650         mDecoder = nullptr;
651     }
652     return true;
653 }
654 
nativeTestSimpleEncode(JNIEnv * env,jobject,jstring jEncoder,jstring jDecoder,jstring jMediaType,jstring jtestFile,jstring jTestFileMediaType,jstring jmuxFile,jint jColorFormat,jboolean jUsePersistentSurface,jstring jCfgParams,jstring jSeparator,jobject jRetMsg,jint jFrameLimit)655 static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jDecoder,
656                                        jstring jMediaType, jstring jtestFile,
657                                        jstring jTestFileMediaType, jstring jmuxFile,
658                                        jint jColorFormat, jboolean jUsePersistentSurface,
659                                        jstring jCfgParams, jstring jSeparator, jobject jRetMsg,
660                                        jint jFrameLimit) {
661     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
662     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
663     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
664     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
665     const char* cTestFileMediaType = env->GetStringUTFChars(jTestFileMediaType, nullptr);
666     const char* cMuxFile = jmuxFile ? env->GetStringUTFChars(jmuxFile, nullptr) : nullptr;
667     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
668     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
669     auto codecEncoderSurfaceTest = new CodecEncoderSurfaceTest(cMediaType, cCfgParams, cSeparator);
670     bool isPass = codecEncoderSurfaceTest->testSimpleEncode(cEncoder, cDecoder, cTestFile,
671                                                             cTestFileMediaType, cMuxFile,
672                                                             jColorFormat, jUsePersistentSurface,
673                                                             jFrameLimit);
674     std::string msg = isPass ? std::string{} : codecEncoderSurfaceTest->getErrorMsg();
675     delete codecEncoderSurfaceTest;
676     jclass clazz = env->GetObjectClass(jRetMsg);
677     jmethodID mId =
678             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
679     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
680     env->ReleaseStringUTFChars(jEncoder, cEncoder);
681     env->ReleaseStringUTFChars(jDecoder, cDecoder);
682     env->ReleaseStringUTFChars(jMediaType, cMediaType);
683     env->ReleaseStringUTFChars(jtestFile, cTestFile);
684     env->ReleaseStringUTFChars(jTestFileMediaType, cTestFileMediaType);
685     if (cMuxFile) env->ReleaseStringUTFChars(jmuxFile, cMuxFile);
686     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
687     env->ReleaseStringUTFChars(jSeparator, cSeparator);
688 
689     return isPass;
690 }
691 
registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv * env)692 int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env) {
693     const JNINativeMethod methodTable[] = {
694             {"nativeTestSimpleEncode",
695              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
696              "String;Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/"
697              "StringBuilder;I)Z",
698              (void*)nativeTestSimpleEncode},
699     };
700     jclass c = env->FindClass("android/mediav2/cts/CodecEncoderSurfaceTest");
701     return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
702 }
703 
JNI_OnLoad(JavaVM * vm,void *)704 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
705     JNIEnv* env;
706     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
707     if (registerAndroidMediaV2CtsEncoderSurfaceTest(env) != JNI_OK) return JNI_ERR;
708     return JNI_VERSION_1_6;
709 }
710