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 #include <android/native_window_jni.h>
21 #include <NdkMediaExtractor.h>
22 #include <NdkMediaMuxer.h>
23 #include <jni.h>
24 #include <sys/stat.h>
25 
26 #include "NativeCodecTestBase.h"
27 #include "NativeMediaCommon.h"
28 
29 class CodecEncoderSurfaceTest {
30   private:
31     const char* mMime;
32     ANativeWindow* mWindow;
33     AMediaExtractor* mExtractor;
34     AMediaFormat* mDecFormat;
35     AMediaFormat* mEncFormat;
36     AMediaMuxer* mMuxer;
37     AMediaCodec* mDecoder;
38     AMediaCodec* mEncoder;
39     CodecAsyncHandler mAsyncHandleDecoder;
40     CodecAsyncHandler mAsyncHandleEncoder;
41     bool mIsCodecInAsyncMode;
42     bool mSawDecInputEOS;
43     bool mSawDecOutputEOS;
44     bool mSawEncOutputEOS;
45     bool mSignalEOSWithLastFrame;
46     int mDecInputCount;
47     int mDecOutputCount;
48     int mEncOutputCount;
49     int mEncBitrate;
50     int mEncFramerate;
51     int mMaxBFrames;
52     int mLatency;
53     bool mReviseLatency;
54     int mMuxTrackID;
55 
56     OutputManager* mOutputBuff;
57     OutputManager mRefBuff;
58     OutputManager mTestBuff;
59     bool mSaveToMem;
60 
61     bool setUpExtractor(const char* srcPath);
62     void deleteExtractor();
63     bool configureCodec(bool isAsync, bool signalEOSWithLastFrame);
64     void resetContext(bool isAsync, bool signalEOSWithLastFrame);
65     void setUpEncoderFormat();
66     bool enqueueDecoderInput(size_t bufferIndex);
67     bool dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo);
68     bool dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info);
69     bool tryEncoderOutput(long timeOutUs);
70     bool waitForAllEncoderOutputs();
71     bool queueEOS();
72     bool enqueueDecoderEOS(size_t bufferIndex);
73     bool doWork(int frameLimit);
hasSeenError()74     bool hasSeenError() { return mAsyncHandleDecoder.getError() || mAsyncHandleEncoder.getError(); }
75 
76   public:
77     CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate);
78     ~CodecEncoderSurfaceTest();
79 
80     bool testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath,
81                           const char* muxOutPath);
82 };
83 
CodecEncoderSurfaceTest(const char * mime,int bitrate,int framerate)84 CodecEncoderSurfaceTest::CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate)
85     : mMime{mime}, mEncBitrate{bitrate}, mEncFramerate{framerate} {
86     mWindow = nullptr;
87     mExtractor = nullptr;
88     mDecFormat = nullptr;
89     mEncFormat = nullptr;
90     mMuxer = nullptr;
91     mDecoder = nullptr;
92     mEncoder = nullptr;
93     resetContext(false, false);
94     mMaxBFrames = 0;
95     mLatency = mMaxBFrames;
96     mReviseLatency = false;
97     mMuxTrackID = -1;
98 }
99 
~CodecEncoderSurfaceTest()100 CodecEncoderSurfaceTest::~CodecEncoderSurfaceTest() {
101     deleteExtractor();
102     if (mWindow) {
103         ANativeWindow_release(mWindow);
104         mWindow = nullptr;
105     }
106     if (mEncFormat) {
107         AMediaFormat_delete(mEncFormat);
108         mEncFormat = nullptr;
109     }
110     if (mMuxer) {
111         AMediaMuxer_delete(mMuxer);
112         mMuxer = nullptr;
113     }
114     if (mDecoder) {
115         AMediaCodec_delete(mDecoder);
116         mDecoder = nullptr;
117     }
118     if (mEncoder) {
119         AMediaCodec_delete(mEncoder);
120         mEncoder = nullptr;
121     }
122 }
123 
setUpExtractor(const char * srcFile)124 bool CodecEncoderSurfaceTest::setUpExtractor(const char* srcFile) {
125     FILE* fp = fopen(srcFile, "rbe");
126     struct stat buf {};
127     if (fp && !fstat(fileno(fp), &buf)) {
128         deleteExtractor();
129         mExtractor = AMediaExtractor_new();
130         media_status_t res =
131                 AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size);
132         if (res != AMEDIA_OK) {
133             deleteExtractor();
134         } else {
135             for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor);
136                  trackID++) {
137                 AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID);
138                 const char* mime = nullptr;
139                 AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mime);
140                 if (mime && strncmp(mime, "video/", strlen("video/")) == 0) {
141                     AMediaExtractor_selectTrack(mExtractor, trackID);
142                     AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT,
143                                           COLOR_FormatYUV420Flexible);
144                     mDecFormat = currFormat;
145                     break;
146                 }
147                 AMediaFormat_delete(currFormat);
148             }
149         }
150     }
151     if (fp) fclose(fp);
152     return mDecFormat != nullptr;
153 }
154 
deleteExtractor()155 void CodecEncoderSurfaceTest::deleteExtractor() {
156     if (mExtractor) {
157         AMediaExtractor_delete(mExtractor);
158         mExtractor = nullptr;
159     }
160     if (mDecFormat) {
161         AMediaFormat_delete(mDecFormat);
162         mDecFormat = nullptr;
163     }
164 }
165 
configureCodec(bool isAsync,bool signalEOSWithLastFrame)166 bool CodecEncoderSurfaceTest::configureCodec(bool isAsync, bool signalEOSWithLastFrame) {
167     resetContext(isAsync, signalEOSWithLastFrame);
168     CHECK_STATUS(mAsyncHandleEncoder.setCallBack(mEncoder, isAsync),
169                  "AMediaCodec_setAsyncNotifyCallback failed");
170     CHECK_STATUS(AMediaCodec_configure(mEncoder, mEncFormat, nullptr, nullptr,
171                                        AMEDIACODEC_CONFIGURE_FLAG_ENCODE),
172                  "AMediaCodec_configure failed");
173     AMediaFormat* inpFormat = AMediaCodec_getInputFormat(mEncoder);
174     mReviseLatency = AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency);
175     AMediaFormat_delete(inpFormat);
176     CHECK_STATUS(AMediaCodec_createInputSurface(mEncoder, &mWindow),
177                  "AMediaCodec_createInputSurface failed");
178     CHECK_STATUS(mAsyncHandleDecoder.setCallBack(mDecoder, isAsync),
179                  "AMediaCodec_setAsyncNotifyCallback failed");
180     CHECK_STATUS(AMediaCodec_configure(mDecoder, mDecFormat, mWindow, nullptr, 0),
181                  "AMediaCodec_configure failed");
182     return !hasSeenError();
183 }
184 
resetContext(bool isAsync,bool signalEOSWithLastFrame)185 void CodecEncoderSurfaceTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
186     mAsyncHandleDecoder.resetContext();
187     mAsyncHandleEncoder.resetContext();
188     mIsCodecInAsyncMode = isAsync;
189     mSawDecInputEOS = false;
190     mSawDecOutputEOS = false;
191     mSawEncOutputEOS = false;
192     mSignalEOSWithLastFrame = signalEOSWithLastFrame;
193     mDecInputCount = 0;
194     mDecOutputCount = 0;
195     mEncOutputCount = 0;
196 }
197 
setUpEncoderFormat()198 void CodecEncoderSurfaceTest::setUpEncoderFormat() {
199     if (mEncFormat) AMediaFormat_delete(mEncFormat);
200     mEncFormat = AMediaFormat_new();
201     int width, height;
202     AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
203     AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
204     AMediaFormat_setString(mEncFormat, AMEDIAFORMAT_KEY_MIME, mMime);
205     AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_WIDTH, width);
206     AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_HEIGHT, height);
207     AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_BIT_RATE, mEncBitrate);
208     AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_FRAME_RATE, mEncFramerate);
209     AMediaFormat_setInt32(mEncFormat, TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES, mMaxBFrames);
210     AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatSurface);
211     AMediaFormat_setFloat(mEncFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1.0F);
212 }
213 
enqueueDecoderEOS(size_t bufferIndex)214 bool CodecEncoderSurfaceTest::enqueueDecoderEOS(size_t bufferIndex) {
215     if (!hasSeenError() && !mSawDecInputEOS) {
216         CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, 0, 0,
217                                                   AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
218                      "Queued Decoder End of Stream Failed");
219         mSawDecInputEOS = true;
220         ALOGV("Queued Decoder End of Stream");
221     }
222     return !hasSeenError();
223 }
224 
enqueueDecoderInput(size_t bufferIndex)225 bool CodecEncoderSurfaceTest::enqueueDecoderInput(size_t bufferIndex) {
226     if (AMediaExtractor_getSampleSize(mExtractor) < 0) {
227         return enqueueDecoderEOS(bufferIndex);
228     } else {
229         uint32_t flags = 0;
230         size_t bufSize = 0;
231         uint8_t* buf = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufSize);
232         if (buf == nullptr) {
233             ALOGE("AMediaCodec_getInputBuffer failed");
234             return false;
235         }
236         ssize_t size = AMediaExtractor_getSampleSize(mExtractor);
237         int64_t pts = AMediaExtractor_getSampleTime(mExtractor);
238         if (size > bufSize) {
239             ALOGE("extractor sample size exceeds codec input buffer size %zu %zu", size, bufSize);
240             return false;
241         }
242         if (size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize)) {
243             ALOGE("AMediaExtractor_readSampleData failed");
244             return false;
245         }
246         if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) {
247             flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
248             mSawDecInputEOS = true;
249         }
250         CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, size, pts, flags),
251                      "AMediaCodec_queueInputBuffer failed");
252         ALOGV("input: id: %zu  size: %zu  pts: %" PRId64 "  flags: %d", bufferIndex, size, pts,
253               flags);
254         if (size > 0) {
255             mOutputBuff->saveInPTS(pts);
256             mDecInputCount++;
257         }
258     }
259     return !hasSeenError();
260 }
261 
dequeueDecoderOutput(size_t bufferIndex,AMediaCodecBufferInfo * bufferInfo)262 bool CodecEncoderSurfaceTest::dequeueDecoderOutput(size_t bufferIndex,
263                                                    AMediaCodecBufferInfo* bufferInfo) {
264     if ((bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
265         mSawDecOutputEOS = true;
266     }
267     if (bufferInfo->size > 0 && (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
268         mDecOutputCount++;
269     }
270     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, bufferInfo->size,
271           bufferInfo->presentationTimeUs, bufferInfo->flags);
272     CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, mWindow != nullptr),
273                  "AMediaCodec_releaseOutputBuffer failed");
274     return !hasSeenError();
275 }
276 
dequeueEncoderOutput(size_t bufferIndex,AMediaCodecBufferInfo * info)277 bool CodecEncoderSurfaceTest::dequeueEncoderOutput(size_t bufferIndex,
278                                                    AMediaCodecBufferInfo* info) {
279     if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
280         mSawEncOutputEOS = true;
281     }
282     if (info->size > 0) {
283         size_t buffSize;
284         uint8_t* buf = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &buffSize);
285         if (mSaveToMem) {
286             mOutputBuff->saveToMemory(buf, info);
287         }
288         if (mMuxer != nullptr) {
289             if (mMuxTrackID == -1) {
290                 mMuxTrackID = AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder));
291                 CHECK_STATUS(AMediaMuxer_start(mMuxer), "AMediaMuxer_start failed");
292             }
293             CHECK_STATUS(AMediaMuxer_writeSampleData(mMuxer, mMuxTrackID, buf, info),
294                          "AMediaMuxer_writeSampleData failed");
295         }
296         if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
297             mOutputBuff->saveOutPTS(info->presentationTimeUs);
298             mEncOutputCount++;
299         }
300     }
301     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, info->size,
302           info->presentationTimeUs, info->flags);
303     CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mEncoder, bufferIndex, false),
304                  "AMediaCodec_releaseOutputBuffer failed");
305     return !hasSeenError();
306 }
307 
tryEncoderOutput(long timeOutUs)308 bool CodecEncoderSurfaceTest::tryEncoderOutput(long timeOutUs) {
309     if (mIsCodecInAsyncMode) {
310         if (!hasSeenError() && !mSawEncOutputEOS) {
311             int retry = 0;
312             while (mReviseLatency) {
313                 if (mAsyncHandleEncoder.hasOutputFormatChanged()) {
314                     int actualLatency;
315                     mReviseLatency = false;
316                     if (AMediaFormat_getInt32(mAsyncHandleEncoder.getOutputFormat(),
317                                               AMEDIAFORMAT_KEY_LATENCY, &actualLatency)) {
318                         if (mLatency < actualLatency) {
319                             mLatency = actualLatency;
320                             return !hasSeenError();
321                         }
322                     }
323                 } else {
324                     if (retry > kRetryLimit) return false;
325                     usleep(kQDeQTimeOutUs);
326                     retry ++;
327                 }
328             }
329             callbackObject element = mAsyncHandleEncoder.getOutput();
330             if (element.bufferIndex >= 0) {
331                 if (!dequeueEncoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
332             }
333         }
334     } else {
335         AMediaCodecBufferInfo outInfo;
336         if (!mSawEncOutputEOS) {
337             int bufferID = AMediaCodec_dequeueOutputBuffer(mEncoder, &outInfo, timeOutUs);
338             if (bufferID >= 0) {
339                 if (!dequeueEncoderOutput(bufferID, &outInfo)) return false;
340             } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
341                 AMediaFormat* outFormat = AMediaCodec_getOutputFormat(mEncoder);
342                 AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency);
343                 AMediaFormat_delete(outFormat);
344             } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
345             } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
346             } else {
347                 ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", bufferID);
348                 return false;
349             }
350         }
351     }
352     return !hasSeenError();
353 }
354 
waitForAllEncoderOutputs()355 bool CodecEncoderSurfaceTest::waitForAllEncoderOutputs() {
356     if (mIsCodecInAsyncMode) {
357         while (!hasSeenError() && !mSawEncOutputEOS) {
358             if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
359         }
360     } else {
361         while (!mSawEncOutputEOS) {
362             if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
363         }
364     }
365     return !hasSeenError();
366 }
367 
queueEOS()368 bool CodecEncoderSurfaceTest::queueEOS() {
369     if (mIsCodecInAsyncMode) {
370         while (!hasSeenError() && !mSawDecInputEOS) {
371             callbackObject element = mAsyncHandleDecoder.getWork();
372             if (element.bufferIndex >= 0) {
373                 if (element.isInput) {
374                     if (!enqueueDecoderEOS(element.bufferIndex)) return false;
375                 } else {
376                     if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
377                         return false;
378                     }
379                 }
380             }
381         }
382     } else {
383         AMediaCodecBufferInfo outInfo;
384         while (!mSawDecInputEOS) {
385             ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
386             if (oBufferID >= 0) {
387                 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
388             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
389             } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
390             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
391             } else {
392                 ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID);
393                 return false;
394             }
395             ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
396             if (iBufferId >= 0) {
397                 if (!enqueueDecoderEOS(iBufferId)) return false;
398             } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
399             } else {
400                 ALOGE("unexpected return value from *_dequeueInputBuffer: %zd", iBufferId);
401                 return false;
402             }
403         }
404     }
405 
406     if (mIsCodecInAsyncMode) {
407         // output processing after queuing EOS is done in waitForAllOutputs()
408         while (!hasSeenError() && !mSawDecOutputEOS) {
409             callbackObject element = mAsyncHandleDecoder.getOutput();
410             if (element.bufferIndex >= 0) {
411                 if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
412             }
413             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
414             if (mDecOutputCount - mEncOutputCount > mLatency) {
415                 if (!tryEncoderOutput(-1)) return false;
416             }
417         }
418     } else {
419         AMediaCodecBufferInfo outInfo;
420         // output processing after queuing EOS is done in waitForAllOutputs()
421         while (!mSawDecOutputEOS) {
422             if (!mSawDecOutputEOS) {
423                 ssize_t oBufferID =
424                         AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
425                 if (oBufferID >= 0) {
426                     if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
427                 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
428                 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
429                 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
430                 } else {
431                     ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID);
432                     return false;
433                 }
434             }
435             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
436             if (mDecOutputCount - mEncOutputCount > mLatency) {
437                 if (!tryEncoderOutput(-1)) return false;
438             }
439         }
440     }
441     return !hasSeenError();
442 }
443 
doWork(int frameLimit)444 bool CodecEncoderSurfaceTest::doWork(int frameLimit) {
445     int frameCnt = 0;
446     if (mIsCodecInAsyncMode) {
447         // output processing after queuing EOS is done in waitForAllOutputs()
448         while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
449             callbackObject element = mAsyncHandleDecoder.getWork();
450             if (element.bufferIndex >= 0) {
451                 if (element.isInput) {
452                     if (!enqueueDecoderInput(element.bufferIndex)) return false;
453                     frameCnt++;
454                 } else {
455                     if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
456                         return false;
457                     }
458                 }
459             }
460             // check decoder EOS
461             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
462             // encoder output
463             if (mDecOutputCount - mEncOutputCount > mLatency) {
464                 if (!tryEncoderOutput(-1)) return false;
465             }
466         }
467     } else {
468         AMediaCodecBufferInfo outInfo;
469         // output processing after queuing EOS is done in waitForAllOutputs()
470         while (!mSawDecInputEOS && frameCnt < frameLimit) {
471             ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
472             if (oBufferID >= 0) {
473                 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
474             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
475             } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
476             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
477             } else {
478                 ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID);
479                 return false;
480             }
481             ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
482             if (iBufferId >= 0) {
483                 if (!enqueueDecoderInput(iBufferId)) return false;
484                 frameCnt++;
485             } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
486             } else {
487                 ALOGE("unexpected return value from *_dequeueInputBuffer: %zd", iBufferId);
488                 return false;
489             }
490             if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
491             if (mDecOutputCount - mEncOutputCount > mLatency) {
492                 if (!tryEncoderOutput(-1)) return false;
493             }
494         }
495     }
496     return !hasSeenError();
497 }
498 
testSimpleEncode(const char * encoder,const char * decoder,const char * srcPath,const char * muxOutPath)499 bool CodecEncoderSurfaceTest::testSimpleEncode(const char* encoder, const char* decoder,
500                                                const char* srcPath, const char* muxOutPath) {
501     bool isPass = true;
502     if (!setUpExtractor(srcPath)) {
503         ALOGE("setUpExtractor failed");
504         return false;
505     }
506     setUpEncoderFormat();
507     bool muxOutput = true;
508 
509     /* TODO(b/149027258) */
510     if (true) mSaveToMem = false;
511     else mSaveToMem = true;
512     auto ref = &mRefBuff;
513     auto test = &mTestBuff;
514     int loopCounter = 0;
515     const bool boolStates[]{true, false};
516     for (bool isAsync : boolStates) {
517         if (!isPass) break;
518         AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
519         mOutputBuff = loopCounter == 0 ? ref : test;
520         mOutputBuff->reset();
521 
522         /* TODO(b/147348711) */
523         /* Instead of create and delete codec at every iteration, we would like to create
524          * once and use it for all iterations and delete before exiting */
525         mEncoder = AMediaCodec_createCodecByName(encoder);
526         mDecoder = AMediaCodec_createCodecByName(decoder);
527         if (!mDecoder || !mEncoder) {
528             ALOGE("unable to create media codec by name %s or %s", encoder, decoder);
529             isPass = false;
530             continue;
531         }
532 
533         FILE* ofp = nullptr;
534         if (muxOutput && loopCounter == 0) {
535             int muxerFormat = 0;
536             if (!strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP8) ||
537                 !strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP9)) {
538                 muxerFormat = OUTPUT_FORMAT_WEBM;
539             } else {
540                 muxerFormat = OUTPUT_FORMAT_MPEG_4;
541             }
542             ofp = fopen(muxOutPath, "wbe+");
543             if (ofp) {
544                 mMuxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)muxerFormat);
545             }
546         }
547         if (!configureCodec(mIsCodecInAsyncMode, mSignalEOSWithLastFrame)) return false;
548         CHECK_STATUS(AMediaCodec_start(mEncoder), "AMediaCodec_start failed");
549         CHECK_STATUS(AMediaCodec_start(mDecoder), "AMediaCodec_start failed");
550         if (!doWork(INT32_MAX)) return false;
551         if (!queueEOS()) return false;
552         if (!waitForAllEncoderOutputs()) return false;
553         if (muxOutput) {
554             if (mMuxer != nullptr) {
555                 CHECK_STATUS(AMediaMuxer_stop(mMuxer), "AMediaMuxer_stop failed");
556                 mMuxTrackID = -1;
557                 CHECK_STATUS(AMediaMuxer_delete(mMuxer), "AMediaMuxer_delete failed");
558                 mMuxer = nullptr;
559             }
560             if (ofp) fclose(ofp);
561         }
562         CHECK_STATUS(AMediaCodec_stop(mDecoder), "AMediaCodec_stop failed");
563         CHECK_STATUS(AMediaCodec_stop(mEncoder), "AMediaCodec_stop failed");
564         char log[1000];
565         snprintf(log, sizeof(log), "format: %s \n codec: %s, file: %s, mode: %s:: ",
566                  AMediaFormat_toString(mEncFormat), encoder, srcPath, (isAsync ? "async" : "sync"));
567         CHECK_ERR((hasSeenError()), log, "has seen error", isPass);
568         CHECK_ERR((0 == mDecInputCount), log, "no input sent", isPass);
569         CHECK_ERR((0 == mDecOutputCount), log, "no decoder output received", isPass);
570         CHECK_ERR((0 == mEncOutputCount), log, "no encoder output received", isPass);
571         CHECK_ERR((mDecInputCount != mDecOutputCount), log, "decoder input count != output count",
572                   isPass);
573         /* TODO(b/153127506)
574          *  Currently disabling all encoder output checks. Added checks only for encoder timeStamp
575          *  is in increasing order or not.
576          *  Once issue is fixed remove increasing timestamp check and enable encoder checks.
577          */
578         /*CHECK_ERR((mEncOutputCount != mDecOutputCount), log,
579                   "encoder output count != decoder output count", isPass);
580         CHECK_ERR((loopCounter != 0 && !ref->equals(test)), log, "output is flaky", isPass);
581         CHECK_ERR((loopCounter == 0 && !ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)),
582                   log, "input pts list and output pts list are not identical", isPass);*/
583         CHECK_ERR(loopCounter == 0 && (!ref->isPtsStrictlyIncreasing(INT32_MIN)), log,
584                   "Ref pts is not strictly increasing", isPass);
585         CHECK_ERR(loopCounter != 0 && (!test->isPtsStrictlyIncreasing(INT32_MIN)), log,
586                   "Test pts is not strictly increasing", isPass);
587 
588         loopCounter++;
589         ANativeWindow_release(mWindow);
590         mWindow = nullptr;
591         CHECK_STATUS(AMediaCodec_delete(mEncoder), "AMediaCodec_delete failed");
592         mEncoder = nullptr;
593         CHECK_STATUS(AMediaCodec_delete(mDecoder), "AMediaCodec_delete failed");
594         mDecoder = nullptr;
595     }
596     return isPass;
597 }
598 
nativeTestSimpleEncode(JNIEnv * env,jobject,jstring jEncoder,jstring jDecoder,jstring jMime,jstring jtestFile,jstring jmuxFile,jint jBitrate,jint jFramerate)599 static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jDecoder,
600                                        jstring jMime, jstring jtestFile, jstring jmuxFile,
601                                        jint jBitrate, jint jFramerate) {
602     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
603     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
604     const char* cMime = env->GetStringUTFChars(jMime, nullptr);
605     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
606     const char* cMuxFile = env->GetStringUTFChars(jmuxFile, nullptr);
607     auto codecEncoderSurfaceTest =
608             new CodecEncoderSurfaceTest(cMime, (int)jBitrate, (int)jFramerate);
609     bool isPass =
610             codecEncoderSurfaceTest->testSimpleEncode(cEncoder, cDecoder, cTestFile, cMuxFile);
611     delete codecEncoderSurfaceTest;
612     env->ReleaseStringUTFChars(jEncoder, cEncoder);
613     env->ReleaseStringUTFChars(jDecoder, cDecoder);
614     env->ReleaseStringUTFChars(jMime, cMime);
615     env->ReleaseStringUTFChars(jtestFile, cTestFile);
616     env->ReleaseStringUTFChars(jmuxFile, cMuxFile);
617     return static_cast<jboolean>(isPass);
618 }
619 
registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv * env)620 int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env) {
621     const JNINativeMethod methodTable[] = {
622             {"nativeTestSimpleEncode",
623              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
624              "String;II)Z",
625              (void*)nativeTestSimpleEncode},
626     };
627     jclass c = env->FindClass("android/mediav2/cts/CodecEncoderSurfaceTest");
628     return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
629 }
630