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 "NativeCodecEncoderTest"
19 #include <log/log.h>
20 
21 #include <jni.h>
22 #include <sys/stat.h>
23 
24 #include "NativeCodecTestBase.h"
25 #include "NativeMediaCommon.h"
26 
27 class CodecEncoderTest final : public CodecTestBase {
28   private:
29     uint8_t* mInputData;
30     size_t mInputLength;
31     int mInputBufferReadOffset;
32     int mNumBytesSubmitted;
33     int mLoopBackFrameLimit;
34     bool mIsLoopBack;
35     int64_t mInputOffsetPts;
36     std::vector<AMediaFormat*> mFormats;
37     int mNumSyncFramesReceived;
38     std::vector<int> mSyncFramesPos;
39 
40     int mWidth, mHeight;
41     int mChannels;
42     int mSampleRate;
43     int mColorFormat;
44     int mMaxBFrames;
45     int mDefFrameRate;
46     const int kInpFrmWidth = 352;
47     const int kInpFrmHeight = 288;
48 
49     void convertyuv420ptoyuv420sp();
50     void setUpSource(const char* srcPath);
51     void deleteSource();
52     void deleteParams();
53     bool configureCodec(AMediaFormat* format, bool isAsync, bool signalEOSWithLastFrame,
54                         bool isEncoder) override;
55     void resetContext(bool isAsync, bool signalEOSWithLastFrame) override;
56     bool enqueueInput(size_t bufferIndex) override;
57     bool dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo) override;
58     bool doWork(int frameLimit) override;
59     bool isTestStateValid() override;
60     bool initFormat(AMediaFormat* format);
61     bool encodeToMemory(const char* file, const char* encoder, int frameLimit, AMediaFormat* format,
62                         OutputManager* ref);
63     void fillByteBuffer(uint8_t* inputBuffer);
64     void forceSyncFrame(AMediaFormat* format);
65     void updateBitrate(AMediaFormat* format, int bitrate);
66 
67   public:
68     CodecEncoderTest(const char* mediaType, const char* cfgParams, const char* cfgReconfigParams,
69                      const char* separator);
70     ~CodecEncoderTest();
71 
72     bool testSimpleEncode(const char* encoder, const char* srcPath);
73     bool testReconfigure(const char* encoder, const char* srcPath);
74     bool testSetForceSyncFrame(const char* encoder, const char* srcPath);
75     bool testAdaptiveBitRate(const char* encoder, const char* srcPath);
76     bool testOnlyEos(const char* encoder);
77 };
78 
CodecEncoderTest(const char * mediaType,const char * cfgParams,const char * cfgReconfigParams,const char * separator)79 CodecEncoderTest::CodecEncoderTest(const char* mediaType, const char* cfgParams,
80                                    const char* cfgReconfigParams, const char* separator)
81     : CodecTestBase(mediaType) {
82     mFormats.push_back(deSerializeMediaFormat(cfgParams, separator));
83     if (cfgReconfigParams) {
84         mFormats.push_back(deSerializeMediaFormat(cfgReconfigParams, separator));
85     }
86     if (mIsVideo && mFormats[0] != nullptr) {
87         AMediaFormat_getInt32(mFormats[0], AMEDIAFORMAT_KEY_COLOR_FORMAT, &mColorFormat);
88     }
89     mInputData = nullptr;
90     mInputLength = 0;
91     mInputBufferReadOffset = 0;
92     mNumBytesSubmitted = 0;
93     mLoopBackFrameLimit = 0;
94     mIsLoopBack = false;
95     mInputOffsetPts = 0;
96 }
97 
~CodecEncoderTest()98 CodecEncoderTest::~CodecEncoderTest() {
99     deleteSource();
100     deleteParams();
101 }
102 
convertyuv420ptoyuv420sp()103 void CodecEncoderTest::convertyuv420ptoyuv420sp() {
104     int ySize = kInpFrmWidth * kInpFrmHeight;
105     int uSize = kInpFrmWidth * kInpFrmHeight / 4;
106     int frameSize = ySize + uSize * 2;
107     int totalFrames = mInputLength / frameSize;
108     uint8_t* u = new uint8_t[uSize];
109     uint8_t* v = new uint8_t[uSize];
110     uint8_t* frameBase = mInputData;
111     for (int i = 0; i < totalFrames; i++) {
112         uint8_t* uvBase = frameBase + ySize;
113         memcpy(u, uvBase, uSize);
114         memcpy(v, uvBase + uSize, uSize);
115         for (int j = 0, idx = 0; j < uSize; j++, idx += 2) {
116             uvBase[idx] = u[j];
117             uvBase[idx + 1] = v[j];
118         }
119         frameBase += frameSize;
120     }
121     delete[] u;
122     delete[] v;
123 }
124 
setUpSource(const char * srcPath)125 void CodecEncoderTest::setUpSource(const char* srcPath) {
126     FILE* fp = fopen(srcPath, "rbe");
127     struct stat buf {};
128     if (fp && !fstat(fileno(fp), &buf)) {
129         deleteSource();
130         mInputLength = buf.st_size;
131         mInputData = new uint8_t[mInputLength];
132         fread(mInputData, sizeof(uint8_t), mInputLength, fp);
133         if (mColorFormat == COLOR_FormatYUV420SemiPlanar) {
134             convertyuv420ptoyuv420sp();
135         }
136     } else {
137         ALOGE("unable to open input file %s", srcPath);
138     }
139     if (fp) fclose(fp);
140 }
141 
deleteSource()142 void CodecEncoderTest::deleteSource() {
143     if (mInputData) {
144         delete[] mInputData;
145         mInputData = nullptr;
146     }
147     mInputLength = 0;
148 }
149 
deleteParams()150 void CodecEncoderTest::deleteParams() {
151     for (auto format : mFormats) AMediaFormat_delete(format);
152     mFormats.clear();
153 }
154 
configureCodec(AMediaFormat * format,bool isAsync,bool signalEOSWithLastFrame,bool isEncoder)155 bool CodecEncoderTest::configureCodec(AMediaFormat* format, bool isAsync,
156                                       bool signalEOSWithLastFrame, bool isEncoder) {
157     if (!initFormat(format)) return false;
158     return CodecTestBase::configureCodec(format, isAsync, signalEOSWithLastFrame, isEncoder);
159 }
160 
resetContext(bool isAsync,bool signalEOSWithLastFrame)161 void CodecEncoderTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
162     CodecTestBase::resetContext(isAsync, signalEOSWithLastFrame);
163     mInputBufferReadOffset = 0;
164     mNumBytesSubmitted = 0;
165     mInputOffsetPts = 0;
166     mNumSyncFramesReceived = 0;
167     mSyncFramesPos.clear();
168 }
169 
fillByteBuffer(uint8_t * inputBuffer)170 void CodecEncoderTest::fillByteBuffer(uint8_t* inputBuffer) {
171     int width, height, tileWidth, tileHeight;
172     int offset = 0, frmOffset = mInputBufferReadOffset;
173     int numOfPlanes;
174     if (mColorFormat == COLOR_FormatYUV420SemiPlanar) {
175         numOfPlanes = 2;
176     } else {
177         numOfPlanes = 3;
178     }
179     for (int plane = 0; plane < numOfPlanes; plane++) {
180         if (plane == 0) {
181             width = mWidth;
182             height = mHeight;
183             tileWidth = kInpFrmWidth;
184             tileHeight = kInpFrmHeight;
185         } else {
186             if (mColorFormat == COLOR_FormatYUV420SemiPlanar) {
187                 width = mWidth;
188                 tileWidth = kInpFrmWidth;
189             } else {
190                 width = mWidth / 2;
191                 tileWidth = kInpFrmWidth / 2;
192             }
193             height = mHeight / 2;
194             tileHeight = kInpFrmHeight / 2;
195         }
196         for (int k = 0; k < height; k += tileHeight) {
197             int rowsToCopy = std::min(height - k, tileHeight);
198             for (int j = 0; j < rowsToCopy; j++) {
199                 for (int i = 0; i < width; i += tileWidth) {
200                     int colsToCopy = std::min(width - i, tileWidth);
201                     memcpy(inputBuffer + (offset + (k + j) * width + i),
202                            mInputData + (frmOffset + j * tileWidth), colsToCopy);
203                 }
204             }
205         }
206         offset += width * height;
207         frmOffset += tileWidth * tileHeight;
208     }
209 }
210 
enqueueInput(size_t bufferIndex)211 bool CodecEncoderTest::enqueueInput(size_t bufferIndex) {
212     if (mInputBufferReadOffset >= mInputLength) {
213         if (!mIsLoopBack) return enqueueEOS(bufferIndex);
214         mInputBufferReadOffset = 0; // loop back to beginning
215     }
216     {
217         int size = 0;
218         int flags = 0;
219         int64_t pts = mInputOffsetPts;
220         size_t buffSize;
221         uint8_t* inputBuffer = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &buffSize);
222         RETURN_IF_NULL(inputBuffer, std::string{"AMediaCodec_getInputBuffer returned nullptr"})
223         if (mIsAudio) {
224             pts += mNumBytesSubmitted * 1000000LL / (2 * mChannels * mSampleRate);
225             size = std::min(buffSize, mInputLength - mInputBufferReadOffset);
226             memcpy(inputBuffer, mInputData + mInputBufferReadOffset, size);
227             if (mSignalEOSWithLastFrame) {
228                 if (mIsLoopBack ? (mInputCount + 1 >= mLoopBackFrameLimit)
229                                 : (mInputBufferReadOffset + size >= mInputLength)) {
230                     flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
231                     mSawInputEOS = true;
232                 }
233             }
234             mInputBufferReadOffset += size;
235         } else {
236             pts += mInputCount * 1000000LL / mDefFrameRate;
237             size = mWidth * mHeight * 3 / 2;
238             int frmSize = kInpFrmWidth * kInpFrmHeight * 3 / 2;
239             RETURN_IF_TRUE(mInputBufferReadOffset + frmSize > mInputLength,
240                            std::string{"received partial frame to encode"})
241             RETURN_IF_TRUE(size > buffSize,
242                            StringFormat("frame size exceeds buffer capacity of input buffer %d %zu",
243                                         size, buffSize))
244             if (mWidth == kInpFrmWidth && mHeight == kInpFrmHeight) {
245                 memcpy(inputBuffer, mInputData + mInputBufferReadOffset, size);
246             } else {
247                 fillByteBuffer(inputBuffer);
248             }
249             if (mSignalEOSWithLastFrame) {
250                 if (mIsLoopBack ? (mInputCount + 1 >= mLoopBackFrameLimit)
251                                 : (mInputBufferReadOffset + frmSize >= mInputLength)) {
252                     flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
253                     mSawInputEOS = true;
254                 }
255             }
256             mInputBufferReadOffset += frmSize;
257         }
258         mNumBytesSubmitted += size;
259         RETURN_IF_FAIL(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, size, pts, flags),
260                        "AMediaCodec_queueInputBuffer failed")
261         ALOGV("input: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, size, pts,
262               flags);
263         mOutputBuff->saveInPTS(pts);
264         mInputCount++;
265     }
266     return !hasSeenError();
267 }
268 
dequeueOutput(size_t bufferIndex,AMediaCodecBufferInfo * info)269 bool CodecEncoderTest::dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* info) {
270     if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
271         mSawOutputEOS = true;
272     }
273     if (info->size > 0) {
274         if (mSaveToMem) {
275             size_t buffSize;
276             uint8_t* buf = AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize);
277             RETURN_IF_NULL(buf, std::string{"AMediaCodec_getOutputBuffer returned nullptr"})
278             // NdkMediaCodec calls ABuffer::data, which already adds offset
279             info->offset = 0;
280             mOutputBuff->saveToMemory(buf, info);
281             mOutputBuff->updateChecksum(buf, info);
282         }
283         if ((info->flags & AMEDIACODEC_BUFFER_FLAG_KEY_FRAME) != 0) {
284             mNumSyncFramesReceived += 1;
285             mSyncFramesPos.push_back(mOutputCount);
286         }
287         if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
288             mOutputBuff->saveOutPTS(info->presentationTimeUs);
289             mOutputCount++;
290         }
291     }
292     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, info->size,
293           info->presentationTimeUs, info->flags);
294     RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false),
295                    "AMediaCodec_releaseOutputBuffer failed")
296     return !hasSeenError();
297 }
298 
doWork(int frameLimit)299 bool CodecEncoderTest::doWork(int frameLimit) {
300     mLoopBackFrameLimit = frameLimit;
301     return CodecTestBase::doWork(frameLimit);
302 }
303 
isTestStateValid()304 bool CodecEncoderTest::isTestStateValid() {
305     if (!CodecTestBase::isTestStateValid()) return false;
306     RETURN_IF_TRUE((mIsAudio || (mIsVideo && mMaxBFrames == 0)) &&
307                            !mOutputBuff->isPtsStrictlyIncreasing(mPrevOutputPts),
308                    std::string{"Output timestamps are not strictly increasing \n"}.append(
309                            mOutputBuff->getErrorMsg()))
310     RETURN_IF_TRUE(mIsVideo && !mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0),
311                    std::string{"Input pts list and Output pts list are not identical \n"}.append(
312                            mOutputBuff->getErrorMsg()))
313     return true;
314 }
315 
316 // @ApiTest = AMEDIAFORMAT_KEY_CHANNEL_COUNT
317 // @ApiTest = AMEDIAFORMAT_KEY_COLOR_FORMAT
318 // @ApiTest = AMEDIAFORMAT_KEY_FRAME_RATE
319 // @ApiTest = AMEDIAFORMAT_KEY_HEIGHT
320 // @ApiTest = AMEDIAFORMAT_KEY_MAX_B_FRAMES
321 // @ApiTest = AMEDIAFORMAT_KEY_SAMPLE_RATE
322 // @ApiTest = AMEDIAFORMAT_KEY_WIDTH
323 //
initFormat(AMediaFormat * format)324 bool CodecEncoderTest::initFormat(AMediaFormat* format) {
325     if (mIsAudio) {
326         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate),
327                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_SAMPLE_RATE))
328         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mChannels),
329                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_CHANNEL_COUNT))
330     } else {
331         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth),
332                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_WIDTH))
333         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight),
334                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_HEIGHT))
335         // key formalized in Android U (sdk==34).
336         // Use internally-defined when running on earlier releases, such as happens with MTS
337         if (__builtin_available(android __ANDROID_API_U__, *)) {
338             RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_MAX_B_FRAMES,
339                                               &mMaxBFrames),
340                         StringFormat("format does not have key %s",
341                                      AMEDIAFORMAT_KEY_MAX_B_FRAMES))
342         } else {
343             RETURN_IF_FALSE(AMediaFormat_getInt32(format, COMPATIBLE_AMEDIAFORMAT_KEY_MAX_B_FRAMES,
344                                               &mMaxBFrames),
345                         StringFormat("format does not have key %s",
346                                      COMPATIBLE_AMEDIAFORMAT_KEY_MAX_B_FRAMES))
347         }
348         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mDefFrameRate),
349                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_FRAME_RATE))
350         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, &mColorFormat),
351                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_COLOR_FORMAT))
352     }
353     return true;
354 }
355 
encodeToMemory(const char * file,const char * encoder,int32_t frameLimit,AMediaFormat * format,OutputManager * ref)356 bool CodecEncoderTest::encodeToMemory(const char* file, const char* encoder, int32_t frameLimit,
357                                       AMediaFormat* format, OutputManager* ref) {
358     /* TODO(b/149027258) */
359     if (true) mSaveToMem = false;
360     else mSaveToMem = true;
361     mOutputBuff = ref;
362     mCodec = AMediaCodec_createCodecByName(encoder);
363     RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name %s \n", encoder))
364     setUpSource(file);
365     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", file))
366     if (!configureCodec(format, false, true, true)) return false;
367     RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
368     if (!doWork(frameLimit)) return false;
369     if (!queueEOS()) return false;
370     if (!waitForAllOutputs()) return false;
371     RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
372     RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
373     mCodec = nullptr;
374     mSaveToMem = false;
375     return !hasSeenError();
376 }
377 
forceSyncFrame(AMediaFormat * format)378 void CodecEncoderTest::forceSyncFrame(AMediaFormat* format) {
379     AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
380     ALOGV("requesting key frame");
381     AMediaCodec_setParameters(mCodec, format);
382 }
383 
updateBitrate(AMediaFormat * format,int bitrate)384 void CodecEncoderTest::updateBitrate(AMediaFormat* format, int bitrate) {
385     AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate);
386     ALOGV("requesting bitrate to be changed to %d", bitrate);
387     AMediaCodec_setParameters(mCodec, format);
388 }
389 
testSimpleEncode(const char * encoder,const char * srcPath)390 bool CodecEncoderTest::testSimpleEncode(const char* encoder, const char* srcPath) {
391     setUpSource(srcPath);
392     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
393     /* TODO(b/149027258) */
394     if (true) mSaveToMem = false;
395     else mSaveToMem = true;
396     auto ref = mRefBuff;
397     auto test = mTestBuff;
398     const bool boolStates[]{true, false};
399     for (auto format : mFormats) {
400         RETURN_IF_NULL(format,
401                        std::string{"encountered error during deserialization of media format"})
402         int loopCounter = 0;
403         for (auto eosType : boolStates) {
404             for (auto isAsync : boolStates) {
405                 mOutputBuff = loopCounter == 0 ? ref : test;
406                 mOutputBuff->reset();
407                 /* TODO(b/147348711) */
408                 /* Instead of create and delete codec at every iteration, we would like to create
409                  * once and use it for all iterations and delete before exiting */
410                 mCodec = AMediaCodec_createCodecByName(encoder);
411                 RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", encoder))
412                 char* name = nullptr;
413                 RETURN_IF_FAIL(AMediaCodec_getName(mCodec, &name), "AMediaCodec_getName failed")
414                 RETURN_IF_NULL(name, std::string{"AMediaCodec_getName returned null"})
415                 auto res = strcmp(name, encoder) != 0;
416                 AMediaCodec_releaseName(mCodec, name);
417                 RETURN_IF_TRUE(res,
418                                StringFormat("Codec name mismatch act/got: %s/%s", encoder, name))
419                 if (!configureCodec(format, isAsync, eosType, true)) return false;
420                 RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
421                 if (!doWork(INT32_MAX)) return false;
422                 if (!queueEOS()) return false;
423                 if (!waitForAllOutputs()) return false;
424                 RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
425                 RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
426                 mCodec = nullptr;
427                 RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
428                                std::string{"Encoder output is not consistent across runs \n"}
429                                        .append(test->getErrorMsg()))
430                 loopCounter++;
431             }
432         }
433     }
434     return true;
435 }
436 
testReconfigure(const char * encoder,const char * srcPath)437 bool CodecEncoderTest::testReconfigure(const char* encoder, const char* srcPath) {
438     setUpSource(srcPath);
439     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
440     auto configRef = mReconfBuff;
441     if (mFormats.size() > 1) {
442         auto format = mFormats[1];
443         RETURN_IF_NULL(format,
444                        std::string{"encountered error during deserialization of media format"})
445         RETURN_IF_FALSE(encodeToMemory(srcPath, encoder, INT32_MAX, format, configRef),
446                         StringFormat("encodeToMemory failed for file: %s codec: %s \n format: %s",
447                                      srcPath, encoder, AMediaFormat_toString(format)))
448     }
449     auto format = mFormats[0];
450     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
451     auto ref = mRefBuff;
452     RETURN_IF_FALSE(encodeToMemory(srcPath, encoder, INT32_MAX, format, ref),
453                     StringFormat("encodeToMemory failed for file: %s codec: %s \n format: %s",
454                                  srcPath, encoder, AMediaFormat_toString(format)))
455 
456     auto test = mTestBuff;
457     mOutputBuff = test;
458     const bool boolStates[]{true, false};
459     for (auto isAsync : boolStates) {
460         /* TODO(b/147348711) */
461         /* Instead of create and delete codec at every iteration, we would like to create
462          * once and use it for all iterations and delete before exiting */
463         mCodec = AMediaCodec_createCodecByName(encoder);
464         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", encoder))
465         if (!configureCodec(format, isAsync, true, true)) return false;
466         /* test reconfigure in init state */
467         if (!reConfigureCodec(format, !isAsync, false, true)) return false;
468         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
469 
470         /* test reconfigure in running state before queuing input */
471         if (!reConfigureCodec(format, !isAsync, false, true)) return false;
472         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
473         if (!doWork(23)) return false;
474 
475         /* test reconfigure codec in running state */
476         if (!reConfigureCodec(format, isAsync, true, true)) return false;
477         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
478 
479         /* TODO(b/149027258) */
480         if (true) mSaveToMem = false;
481         else mSaveToMem = true;
482         test->reset();
483         if (!doWork(INT32_MAX)) return false;
484         if (!queueEOS()) return false;
485         if (!waitForAllOutputs()) return false;
486         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
487         RETURN_IF_TRUE(!ref->equals(test),
488                        std::string{"Encoder output is not consistent across runs \n"}.append(
489                                test->getErrorMsg()))
490 
491         /* test reconfigure codec at eos state */
492         if (!reConfigureCodec(format, !isAsync, false, true)) return false;
493         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
494         test->reset();
495         if (!doWork(INT32_MAX)) return false;
496         if (!queueEOS()) return false;
497         if (!waitForAllOutputs()) return false;
498         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
499         RETURN_IF_TRUE(!ref->equals(test),
500                        std::string{"Encoder output is not consistent across runs \n"}.append(
501                                test->getErrorMsg()))
502 
503         /* test reconfigure codec for new format */
504         if (mFormats.size() > 1) {
505             if (!reConfigureCodec(mFormats[1], isAsync, false, true)) return false;
506             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
507             test->reset();
508             if (!doWork(INT32_MAX)) return false;
509             if (!queueEOS()) return false;
510             if (!waitForAllOutputs()) return false;
511             RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
512             RETURN_IF_TRUE(!configRef->equals(test),
513                            std::string{"Encoder output is not consistent across runs \n"}.append(
514                                    test->getErrorMsg()))
515         }
516         mSaveToMem = false;
517         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
518         mCodec = nullptr;
519     }
520     return true;
521 }
522 
testOnlyEos(const char * encoder)523 bool CodecEncoderTest::testOnlyEos(const char* encoder) {
524     /* TODO(b/149027258) */
525     if (true) mSaveToMem = false;
526     else mSaveToMem = true;
527     auto ref = mRefBuff;
528     auto test = mTestBuff;
529     const bool boolStates[]{true, false};
530     AMediaFormat* format = mFormats[0];
531     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
532     int loopCounter = 0;
533     for (auto isAsync : boolStates) {
534         mOutputBuff = loopCounter == 0 ? ref : test;
535         mOutputBuff->reset();
536         /* TODO(b/147348711) */
537         /* Instead of create and delete codec at every iteration, we would like to create
538          * once and use it for all iterations and delete before exiting */
539         mCodec = AMediaCodec_createCodecByName(encoder);
540         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name %s", encoder))
541         if (!configureCodec(format, isAsync, false, true)) return false;
542         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
543         if (!queueEOS()) return false;
544         if (!waitForAllOutputs()) return false;
545         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
546         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
547         mCodec = nullptr;
548         RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
549                        std::string{"Encoder output is not consistent across runs \n"}.append(
550                                test->getErrorMsg()))
551         loopCounter++;
552     }
553     return true;
554 }
555 
testSetForceSyncFrame(const char * encoder,const char * srcPath)556 bool CodecEncoderTest::testSetForceSyncFrame(const char* encoder, const char* srcPath) {
557     setUpSource(srcPath);
558     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
559     AMediaFormat* format = mFormats[0];
560     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
561     RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mDefFrameRate),
562                     StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_FRAME_RATE))
563     // Maximum allowed key frame interval variation from the target value.
564     int kMaxKeyFrameIntervalVariation = 3;
565     int kKeyFrameInterval = 2; // force key frame every 2 seconds.
566     int kKeyFramePos = mDefFrameRate * kKeyFrameInterval;
567     int kNumKeyFrameRequests = 7;
568     AMediaFormat* params = AMediaFormat_new();
569     mFormats.push_back(params);
570     mOutputBuff = mTestBuff;
571     const bool boolStates[]{true, false};
572     for (auto isAsync : boolStates) {
573         mOutputBuff->reset();
574         /* TODO(b/147348711) */
575         /* Instead of create and delete codec at every iteration, we would like to create
576          * once and use it for all iterations and delete before exiting */
577         mCodec = AMediaCodec_createCodecByName(encoder);
578         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name%s", encoder))
579         if (!configureCodec(format, isAsync, false, true)) return false;
580         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
581         for (int i = 0; i < kNumKeyFrameRequests; i++) {
582             if (!doWork(kKeyFramePos)) return false;
583             RETURN_IF_TRUE(mSawInputEOS,
584                            StringFormat("Unable to encode %d frames as the input resource contains "
585                                         "only %d frames \n",
586                                         kKeyFramePos, mInputCount))
587             forceSyncFrame(params);
588             mInputBufferReadOffset = 0;
589         }
590         if (!queueEOS()) return false;
591         if (!waitForAllOutputs()) return false;
592         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
593         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
594         mCodec = nullptr;
595         RETURN_IF_TRUE((mNumSyncFramesReceived < kNumKeyFrameRequests),
596                        StringFormat("Received only %d key frames for %d key frame requests \n",
597                                     mNumSyncFramesReceived, kNumKeyFrameRequests))
598         ALOGD("mNumSyncFramesReceived %d", mNumSyncFramesReceived);
599         for (int i = 0, expPos = 0, index = 0; i < kNumKeyFrameRequests; i++) {
600             int j = index;
601             for (; j < mSyncFramesPos.size(); j++) {
602                 // Check key frame intervals:
603                 // key frame position should not be greater than target value + 3
604                 // key frame position should not be less than target value - 3
605                 if (abs(expPos - mSyncFramesPos.at(j)) <= kMaxKeyFrameIntervalVariation) {
606                     index = j;
607                     break;
608                 }
609             }
610             if (j == mSyncFramesPos.size()) {
611                 ALOGW("requested key frame at frame index %d none found near by", expPos);
612             }
613             expPos += kKeyFramePos;
614         }
615     }
616     return true;
617 }
618 
testAdaptiveBitRate(const char * encoder,const char * srcPath)619 bool CodecEncoderTest::testAdaptiveBitRate(const char* encoder, const char* srcPath) {
620     setUpSource(srcPath);
621     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
622     AMediaFormat* format = mFormats[0];
623     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
624     RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mDefFrameRate),
625                     StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_FRAME_RATE))
626     int kAdaptiveBitrateInterval = 3; // change bitrate every 3 seconds.
627     int kAdaptiveBitrateDurationFrame = mDefFrameRate * kAdaptiveBitrateInterval;
628     int kBitrateChangeRequests = 7;
629     // TODO(b/251265293) Reduce the allowed deviation after improving the test conditions
630     float kMaxBitrateDeviation = 60.0; // allowed bitrate deviation in %
631     AMediaFormat* params = AMediaFormat_new();
632     mFormats.push_back(params);
633     mOutputBuff = mTestBuff;
634     mSaveToMem = true;
635     const bool boolStates[]{true, false};
636     for (auto isAsync : boolStates) {
637         mOutputBuff->reset();
638         /* TODO(b/147348711) */
639         /* Instead of create and delete codec at every iteration, we would like to create
640          * once and use it for all iterations and delete before exiting */
641         mCodec = AMediaCodec_createCodecByName(encoder);
642         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name %s", encoder))
643         if (!configureCodec(format, isAsync, false, true)) return false;
644         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
645         int expOutSize = 0;
646         int bitrate;
647         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate),
648                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_BIT_RATE))
649         for (int i = 0; i < kBitrateChangeRequests; i++) {
650             if (!doWork(kAdaptiveBitrateDurationFrame)) return false;
651             RETURN_IF_TRUE(mSawInputEOS,
652                            StringFormat("Unable to encode %d frames as the input resource contains "
653                                         "only %d frames \n",
654                                         kAdaptiveBitrateDurationFrame, mInputCount))
655             expOutSize += kAdaptiveBitrateInterval * bitrate;
656             if ((i & 1) == 1) bitrate *= 2;
657             else bitrate /= 2;
658             updateBitrate(params, bitrate);
659             mInputBufferReadOffset = 0;
660         }
661         if (!queueEOS()) return false;
662         if (!waitForAllOutputs()) return false;
663         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
664         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
665         mCodec = nullptr;
666         /* TODO: validate output br with sliding window constraints Sec 5.2 cdd */
667         int outSize = mOutputBuff->getOutStreamSize() * 8;
668         float brDev = abs(expOutSize - outSize) * 100.0f / expOutSize;
669         RETURN_IF_TRUE(brDev > kMaxBitrateDeviation,
670                        StringFormat("Relative Bitrate error is too large : %f %%\n", brDev))
671     }
672     return true;
673 }
674 
nativeTestSimpleEncode(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)675 static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath,
676                                        jstring jMediaType, jstring jCfgParams, jstring jSeparator,
677                                        jobject jRetMsg) {
678     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
679     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
680     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
681     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
682     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
683     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
684     bool isPass = codecEncoderTest->testSimpleEncode(cEncoder, csrcPath);
685     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
686     delete codecEncoderTest;
687     jclass clazz = env->GetObjectClass(jRetMsg);
688     jmethodID mId =
689             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
690     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
691     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
692     env->ReleaseStringUTFChars(jSeparator, cSeparator);
693     env->ReleaseStringUTFChars(jEncoder, cEncoder);
694     env->ReleaseStringUTFChars(jMediaType, cMediaType);
695     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
696     return static_cast<jboolean>(isPass);
697 }
698 
nativeTestReconfigure(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jReconfigCfgParams,jstring jSeparator,jobject jRetMsg)699 static jboolean nativeTestReconfigure(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath,
700                                       jstring jMediaType, jstring jCfgParams,
701                                       jstring jReconfigCfgParams, jstring jSeparator,
702                                       jobject jRetMsg) {
703     bool isPass;
704     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
705     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
706     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
707     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
708     const char* cReconfigCfgParams = jReconfigCfgParams != nullptr
709             ? env->GetStringUTFChars(jReconfigCfgParams, nullptr)
710             : nullptr;
711     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
712     auto codecEncoderTest =
713             new CodecEncoderTest(cMediaType, cCfgParams, cReconfigCfgParams, cSeparator);
714     isPass = codecEncoderTest->testReconfigure(cEncoder, csrcPath);
715     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
716     delete codecEncoderTest;
717     jclass clazz = env->GetObjectClass(jRetMsg);
718     jmethodID mId =
719             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
720     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
721     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
722     if (cReconfigCfgParams != nullptr) {
723         env->ReleaseStringUTFChars(jReconfigCfgParams, cReconfigCfgParams);
724     }
725     env->ReleaseStringUTFChars(jSeparator, cSeparator);
726     env->ReleaseStringUTFChars(jEncoder, cEncoder);
727     env->ReleaseStringUTFChars(jMediaType, cMediaType);
728     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
729     return static_cast<jboolean>(isPass);
730 }
731 
nativeTestSetForceSyncFrame(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)732 static jboolean nativeTestSetForceSyncFrame(JNIEnv* env, jobject, jstring jEncoder,
733                                             jstring jsrcPath, jstring jMediaType,
734                                             jstring jCfgParams, jstring jSeparator,
735                                             jobject jRetMsg) {
736     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
737     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
738     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
739     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
740     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
741     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
742     bool isPass = codecEncoderTest->testSetForceSyncFrame(cEncoder, csrcPath);
743     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
744     delete codecEncoderTest;
745     jclass clazz = env->GetObjectClass(jRetMsg);
746     jmethodID mId =
747             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
748     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
749     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
750     env->ReleaseStringUTFChars(jSeparator, cSeparator);
751     env->ReleaseStringUTFChars(jEncoder, cEncoder);
752     env->ReleaseStringUTFChars(jMediaType, cMediaType);
753     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
754     return static_cast<jboolean>(isPass);
755 }
756 
nativeTestAdaptiveBitRate(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)757 static jboolean nativeTestAdaptiveBitRate(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath,
758                                           jstring jMediaType, jstring jCfgParams,
759                                           jstring jSeparator, jobject jRetMsg) {
760     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
761     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
762     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
763     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
764     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
765     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
766     bool isPass = codecEncoderTest->testAdaptiveBitRate(cEncoder, csrcPath);
767     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
768     delete codecEncoderTest;
769     jclass clazz = env->GetObjectClass(jRetMsg);
770     jmethodID mId =
771             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
772     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
773     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
774     env->ReleaseStringUTFChars(jSeparator, cSeparator);
775     env->ReleaseStringUTFChars(jEncoder, cEncoder);
776     env->ReleaseStringUTFChars(jMediaType, cMediaType);
777     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
778     return static_cast<jboolean>(isPass);
779 }
780 
nativeTestOnlyEos(JNIEnv * env,jobject,jstring jEncoder,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)781 static jboolean nativeTestOnlyEos(JNIEnv* env, jobject, jstring jEncoder, jstring jMediaType,
782                                   jstring jCfgParams, jstring jSeparator, jobject jRetMsg) {
783     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
784     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
785     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
786     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
787     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
788     bool isPass = codecEncoderTest->testOnlyEos(cEncoder);
789     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
790     delete codecEncoderTest;
791     jclass clazz = env->GetObjectClass(jRetMsg);
792     jmethodID mId =
793             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
794     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
795     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
796     env->ReleaseStringUTFChars(jSeparator, cSeparator);
797     env->ReleaseStringUTFChars(jEncoder, cEncoder);
798     env->ReleaseStringUTFChars(jMediaType, cMediaType);
799     return static_cast<jboolean>(isPass);
800 }
801 
registerAndroidMediaV2CtsEncoderTest(JNIEnv * env)802 int registerAndroidMediaV2CtsEncoderTest(JNIEnv* env) {
803     const JNINativeMethod methodTable[] = {
804             {"nativeTestSimpleEncode",
805              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
806              "String;Ljava/lang/StringBuilder;)Z",
807              (void*)nativeTestSimpleEncode},
808             {"nativeTestReconfigure",
809              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
810              "String;Ljava/lang/String;Ljava/lang/StringBuilder;)Z",
811              (void*)nativeTestReconfigure},
812             {"nativeTestSetForceSyncFrame",
813              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
814              "String;Ljava/lang/StringBuilder;)Z",
815              (void*)nativeTestSetForceSyncFrame},
816             {"nativeTestAdaptiveBitRate",
817              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
818              "String;Ljava/lang/StringBuilder;)Z",
819              (void*)nativeTestAdaptiveBitRate},
820             {"nativeTestOnlyEos",
821              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
822              "StringBuilder;)Z",
823              (void*)nativeTestOnlyEos},
824     };
825     jclass c = env->FindClass("android/mediav2/cts/CodecEncoderTest");
826     return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
827 }
828 
JNI_OnLoad(JavaVM * vm,void *)829 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
830     JNIEnv* env;
831     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
832     if (registerAndroidMediaV2CtsEncoderTest(env) != JNI_OK) return JNI_ERR;
833     return JNI_VERSION_1_6;
834 }
835