• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             mOutputBuff->saveToMemory(buf, info);
279         }
280         if ((info->flags & TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME) != 0) {
281             mNumSyncFramesReceived += 1;
282             mSyncFramesPos.push_back(mOutputCount);
283         }
284         if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
285             mOutputBuff->saveOutPTS(info->presentationTimeUs);
286             mOutputCount++;
287         }
288     }
289     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, info->size,
290           info->presentationTimeUs, info->flags);
291     RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false),
292                    "AMediaCodec_releaseOutputBuffer failed")
293     return !hasSeenError();
294 }
295 
doWork(int frameLimit)296 bool CodecEncoderTest::doWork(int frameLimit) {
297     mLoopBackFrameLimit = frameLimit;
298     return CodecTestBase::doWork(frameLimit);
299 }
300 
isTestStateValid()301 bool CodecEncoderTest::isTestStateValid() {
302     if (!CodecTestBase::isTestStateValid()) return false;
303     RETURN_IF_TRUE((mIsAudio || (mIsVideo && mMaxBFrames == 0)) &&
304                            !mOutputBuff->isPtsStrictlyIncreasing(mPrevOutputPts),
305                    std::string{"Output timestamps are not strictly increasing \n"}.append(
306                            mOutputBuff->getErrorMsg()))
307     RETURN_IF_TRUE(mIsVideo && !mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0),
308                    std::string{"Input pts list and Output pts list are not identical \n"}.append(
309                            mOutputBuff->getErrorMsg()))
310     return true;
311 }
312 
initFormat(AMediaFormat * format)313 bool CodecEncoderTest::initFormat(AMediaFormat* format) {
314     if (mIsAudio) {
315         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate),
316                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_SAMPLE_RATE))
317         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mChannels),
318                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_CHANNEL_COUNT))
319     } else {
320         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth),
321                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_WIDTH))
322         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight),
323                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_HEIGHT))
324         RETURN_IF_FALSE(AMediaFormat_getInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES,
325                                               &mMaxBFrames),
326                         StringFormat("format does not have key %s",
327                                      TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES))
328         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mDefFrameRate),
329                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_FRAME_RATE))
330         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, &mColorFormat),
331                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_COLOR_FORMAT))
332     }
333     return true;
334 }
335 
encodeToMemory(const char * file,const char * encoder,int32_t frameLimit,AMediaFormat * format,OutputManager * ref)336 bool CodecEncoderTest::encodeToMemory(const char* file, const char* encoder, int32_t frameLimit,
337                                       AMediaFormat* format, OutputManager* ref) {
338     /* TODO(b/149027258) */
339     if (true) mSaveToMem = false;
340     else mSaveToMem = true;
341     mOutputBuff = ref;
342     mCodec = AMediaCodec_createCodecByName(encoder);
343     RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name %s \n", encoder))
344     setUpSource(file);
345     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", file))
346     if (!configureCodec(format, false, true, true)) return false;
347     RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
348     if (!doWork(frameLimit)) return false;
349     if (!queueEOS()) return false;
350     if (!waitForAllOutputs()) return false;
351     RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
352     RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
353     mCodec = nullptr;
354     mSaveToMem = false;
355     return !hasSeenError();
356 }
357 
forceSyncFrame(AMediaFormat * format)358 void CodecEncoderTest::forceSyncFrame(AMediaFormat* format) {
359     AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
360     ALOGV("requesting key frame");
361     AMediaCodec_setParameters(mCodec, format);
362 }
363 
updateBitrate(AMediaFormat * format,int bitrate)364 void CodecEncoderTest::updateBitrate(AMediaFormat* format, int bitrate) {
365     AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate);
366     ALOGV("requesting bitrate to be changed to %d", bitrate);
367     AMediaCodec_setParameters(mCodec, format);
368 }
369 
testSimpleEncode(const char * encoder,const char * srcPath)370 bool CodecEncoderTest::testSimpleEncode(const char* encoder, const char* srcPath) {
371     setUpSource(srcPath);
372     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
373     /* TODO(b/149027258) */
374     if (true) mSaveToMem = false;
375     else mSaveToMem = true;
376     auto ref = mRefBuff;
377     auto test = mTestBuff;
378     const bool boolStates[]{true, false};
379     for (auto format : mFormats) {
380         RETURN_IF_NULL(format,
381                        std::string{"encountered error during deserialization of media format"})
382         int loopCounter = 0;
383         for (auto eosType : boolStates) {
384             for (auto isAsync : boolStates) {
385                 mOutputBuff = loopCounter == 0 ? ref : test;
386                 mOutputBuff->reset();
387                 /* TODO(b/147348711) */
388                 /* Instead of create and delete codec at every iteration, we would like to create
389                  * once and use it for all iterations and delete before exiting */
390                 mCodec = AMediaCodec_createCodecByName(encoder);
391                 RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", encoder))
392                 char* name = nullptr;
393                 RETURN_IF_FAIL(AMediaCodec_getName(mCodec, &name), "AMediaCodec_getName failed")
394                 RETURN_IF_NULL(name, std::string{"AMediaCodec_getName returned null"})
395                 auto res = strcmp(name, encoder) != 0;
396                 AMediaCodec_releaseName(mCodec, name);
397                 RETURN_IF_TRUE(res,
398                                StringFormat("Codec name mismatch act/got: %s/%s", encoder, name))
399                 if (!configureCodec(format, isAsync, eosType, true)) return false;
400                 RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
401                 if (!doWork(INT32_MAX)) return false;
402                 if (!queueEOS()) return false;
403                 if (!waitForAllOutputs()) return false;
404                 RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
405                 RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
406                 mCodec = nullptr;
407                 RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
408                                std::string{"Encoder output is not consistent across runs \n"}
409                                        .append(test->getErrorMsg()))
410                 loopCounter++;
411             }
412         }
413     }
414     return true;
415 }
416 
testReconfigure(const char * encoder,const char * srcPath)417 bool CodecEncoderTest::testReconfigure(const char* encoder, const char* srcPath) {
418     setUpSource(srcPath);
419     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
420     auto configRef = mReconfBuff;
421     if (mFormats.size() > 1) {
422         auto format = mFormats[1];
423         RETURN_IF_NULL(format,
424                        std::string{"encountered error during deserialization of media format"})
425         RETURN_IF_FALSE(encodeToMemory(srcPath, encoder, INT32_MAX, format, configRef),
426                         StringFormat("encodeToMemory failed for file: %s codec: %s \n format: %s",
427                                      srcPath, encoder, AMediaFormat_toString(format)))
428     }
429     auto format = mFormats[0];
430     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
431     auto ref = mRefBuff;
432     RETURN_IF_FALSE(encodeToMemory(srcPath, encoder, INT32_MAX, format, ref),
433                     StringFormat("encodeToMemory failed for file: %s codec: %s \n format: %s",
434                                  srcPath, encoder, AMediaFormat_toString(format)))
435 
436     auto test = mTestBuff;
437     mOutputBuff = test;
438     const bool boolStates[]{true, false};
439     for (auto isAsync : boolStates) {
440         /* TODO(b/147348711) */
441         /* Instead of create and delete codec at every iteration, we would like to create
442          * once and use it for all iterations and delete before exiting */
443         mCodec = AMediaCodec_createCodecByName(encoder);
444         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", encoder))
445         if (!configureCodec(format, isAsync, true, true)) return false;
446         /* test reconfigure in init state */
447         if (!reConfigureCodec(format, !isAsync, false, true)) return false;
448         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
449 
450         /* test reconfigure in running state before queuing input */
451         if (!reConfigureCodec(format, !isAsync, false, true)) return false;
452         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
453         if (!doWork(23)) return false;
454 
455         /* test reconfigure codec in running state */
456         if (!reConfigureCodec(format, isAsync, true, true)) return false;
457         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
458 
459         /* TODO(b/149027258) */
460         if (true) mSaveToMem = false;
461         else mSaveToMem = true;
462         test->reset();
463         if (!doWork(INT32_MAX)) return false;
464         if (!queueEOS()) return false;
465         if (!waitForAllOutputs()) return false;
466         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
467         RETURN_IF_TRUE(!ref->equals(test),
468                        std::string{"Encoder output is not consistent across runs \n"}.append(
469                                test->getErrorMsg()))
470 
471         /* test reconfigure codec at eos state */
472         if (!reConfigureCodec(format, !isAsync, false, true)) return false;
473         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
474         test->reset();
475         if (!doWork(INT32_MAX)) return false;
476         if (!queueEOS()) return false;
477         if (!waitForAllOutputs()) return false;
478         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
479         RETURN_IF_TRUE(!ref->equals(test),
480                        std::string{"Encoder output is not consistent across runs \n"}.append(
481                                test->getErrorMsg()))
482 
483         /* test reconfigure codec for new format */
484         if (mFormats.size() > 1) {
485             if (!reConfigureCodec(mFormats[1], isAsync, false, true)) return false;
486             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
487             test->reset();
488             if (!doWork(INT32_MAX)) return false;
489             if (!queueEOS()) return false;
490             if (!waitForAllOutputs()) return false;
491             RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
492             RETURN_IF_TRUE(!configRef->equals(test),
493                            std::string{"Encoder output is not consistent across runs \n"}.append(
494                                    test->getErrorMsg()))
495         }
496         mSaveToMem = false;
497         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
498         mCodec = nullptr;
499     }
500     return true;
501 }
502 
testOnlyEos(const char * encoder)503 bool CodecEncoderTest::testOnlyEos(const char* encoder) {
504     /* TODO(b/149027258) */
505     if (true) mSaveToMem = false;
506     else mSaveToMem = true;
507     auto ref = mRefBuff;
508     auto test = mTestBuff;
509     const bool boolStates[]{true, false};
510     AMediaFormat* format = mFormats[0];
511     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
512     int loopCounter = 0;
513     for (auto isAsync : boolStates) {
514         mOutputBuff = loopCounter == 0 ? ref : test;
515         mOutputBuff->reset();
516         /* TODO(b/147348711) */
517         /* Instead of create and delete codec at every iteration, we would like to create
518          * once and use it for all iterations and delete before exiting */
519         mCodec = AMediaCodec_createCodecByName(encoder);
520         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name %s", encoder))
521         if (!configureCodec(format, isAsync, false, true)) return false;
522         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
523         if (!queueEOS()) return false;
524         if (!waitForAllOutputs()) return false;
525         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
526         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
527         mCodec = nullptr;
528         RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
529                        std::string{"Encoder output is not consistent across runs \n"}.append(
530                                test->getErrorMsg()))
531         loopCounter++;
532     }
533     return true;
534 }
535 
testSetForceSyncFrame(const char * encoder,const char * srcPath)536 bool CodecEncoderTest::testSetForceSyncFrame(const char* encoder, const char* srcPath) {
537     setUpSource(srcPath);
538     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
539     AMediaFormat* format = mFormats[0];
540     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
541     RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mDefFrameRate),
542                     StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_FRAME_RATE))
543     // Maximum allowed key frame interval variation from the target value.
544     int kMaxKeyFrameIntervalVariation = 3;
545     int kKeyFrameInterval = 2; // force key frame every 2 seconds.
546     int kKeyFramePos = mDefFrameRate * kKeyFrameInterval;
547     int kNumKeyFrameRequests = 7;
548     AMediaFormat* params = AMediaFormat_new();
549     mFormats.push_back(params);
550     mOutputBuff = mTestBuff;
551     const bool boolStates[]{true, false};
552     for (auto isAsync : boolStates) {
553         mOutputBuff->reset();
554         /* TODO(b/147348711) */
555         /* Instead of create and delete codec at every iteration, we would like to create
556          * once and use it for all iterations and delete before exiting */
557         mCodec = AMediaCodec_createCodecByName(encoder);
558         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name%s", encoder))
559         if (!configureCodec(format, isAsync, false, true)) return false;
560         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
561         for (int i = 0; i < kNumKeyFrameRequests; i++) {
562             if (!doWork(kKeyFramePos)) return false;
563             RETURN_IF_TRUE(mSawInputEOS,
564                            StringFormat("Unable to encode %d frames as the input resource contains "
565                                         "only %d frames \n",
566                                         kKeyFramePos, mInputCount))
567             forceSyncFrame(params);
568             mInputBufferReadOffset = 0;
569         }
570         if (!queueEOS()) return false;
571         if (!waitForAllOutputs()) return false;
572         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
573         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
574         mCodec = nullptr;
575         RETURN_IF_TRUE((mNumSyncFramesReceived < kNumKeyFrameRequests),
576                        StringFormat("Received only %d key frames for %d key frame requests \n",
577                                     mNumSyncFramesReceived, kNumKeyFrameRequests))
578         ALOGD("mNumSyncFramesReceived %d", mNumSyncFramesReceived);
579         for (int i = 0, expPos = 0, index = 0; i < kNumKeyFrameRequests; i++) {
580             int j = index;
581             for (; j < mSyncFramesPos.size(); j++) {
582                 // Check key frame intervals:
583                 // key frame position should not be greater than target value + 3
584                 // key frame position should not be less than target value - 3
585                 if (abs(expPos - mSyncFramesPos.at(j)) <= kMaxKeyFrameIntervalVariation) {
586                     index = j;
587                     break;
588                 }
589             }
590             if (j == mSyncFramesPos.size()) {
591                 ALOGW("requested key frame at frame index %d none found near by", expPos);
592             }
593             expPos += kKeyFramePos;
594         }
595     }
596     return true;
597 }
598 
testAdaptiveBitRate(const char * encoder,const char * srcPath)599 bool CodecEncoderTest::testAdaptiveBitRate(const char* encoder, const char* srcPath) {
600     setUpSource(srcPath);
601     RETURN_IF_NULL(mInputData, StringFormat("unable to open input file %s", srcPath))
602     AMediaFormat* format = mFormats[0];
603     RETURN_IF_NULL(format, std::string{"encountered error during deserialization of media format"})
604     RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mDefFrameRate),
605                     StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_FRAME_RATE))
606     int kAdaptiveBitrateInterval = 3; // change bitrate every 3 seconds.
607     int kAdaptiveBitrateDurationFrame = mDefFrameRate * kAdaptiveBitrateInterval;
608     int kBitrateChangeRequests = 7;
609     // TODO(b/251265293) Reduce the allowed deviation after improving the test conditions
610     float kMaxBitrateDeviation = 60.0; // allowed bitrate deviation in %
611     AMediaFormat* params = AMediaFormat_new();
612     mFormats.push_back(params);
613     mOutputBuff = mTestBuff;
614     mSaveToMem = true;
615     const bool boolStates[]{true, false};
616     for (auto isAsync : boolStates) {
617         mOutputBuff->reset();
618         /* TODO(b/147348711) */
619         /* Instead of create and delete codec at every iteration, we would like to create
620          * once and use it for all iterations and delete before exiting */
621         mCodec = AMediaCodec_createCodecByName(encoder);
622         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec by name %s", encoder))
623         if (!configureCodec(format, isAsync, false, true)) return false;
624         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
625         int expOutSize = 0;
626         int bitrate;
627         RETURN_IF_FALSE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate),
628                         StringFormat("format does not have key %s", AMEDIAFORMAT_KEY_BIT_RATE))
629         for (int i = 0; i < kBitrateChangeRequests; i++) {
630             if (!doWork(kAdaptiveBitrateDurationFrame)) return false;
631             RETURN_IF_TRUE(mSawInputEOS,
632                            StringFormat("Unable to encode %d frames as the input resource contains "
633                                         "only %d frames \n",
634                                         kAdaptiveBitrateDurationFrame, mInputCount))
635             expOutSize += kAdaptiveBitrateInterval * bitrate;
636             if ((i & 1) == 1) bitrate *= 2;
637             else bitrate /= 2;
638             updateBitrate(params, bitrate);
639             mInputBufferReadOffset = 0;
640         }
641         if (!queueEOS()) return false;
642         if (!waitForAllOutputs()) return false;
643         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
644         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
645         mCodec = nullptr;
646         /* TODO: validate output br with sliding window constraints Sec 5.2 cdd */
647         int outSize = mOutputBuff->getOutStreamSize() * 8;
648         float brDev = abs(expOutSize - outSize) * 100.0f / expOutSize;
649         RETURN_IF_TRUE(brDev > kMaxBitrateDeviation,
650                        StringFormat("Relative Bitrate error is too large : %f %%\n", brDev))
651     }
652     return true;
653 }
654 
nativeTestSimpleEncode(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)655 static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath,
656                                        jstring jMediaType, jstring jCfgParams, jstring jSeparator,
657                                        jobject jRetMsg) {
658     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
659     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
660     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
661     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
662     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
663     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
664     bool isPass = codecEncoderTest->testSimpleEncode(cEncoder, csrcPath);
665     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
666     delete codecEncoderTest;
667     jclass clazz = env->GetObjectClass(jRetMsg);
668     jmethodID mId =
669             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
670     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
671     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
672     env->ReleaseStringUTFChars(jSeparator, cSeparator);
673     env->ReleaseStringUTFChars(jEncoder, cEncoder);
674     env->ReleaseStringUTFChars(jMediaType, cMediaType);
675     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
676     return static_cast<jboolean>(isPass);
677 }
678 
nativeTestReconfigure(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jReconfigCfgParams,jstring jSeparator,jobject jRetMsg)679 static jboolean nativeTestReconfigure(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath,
680                                       jstring jMediaType, jstring jCfgParams,
681                                       jstring jReconfigCfgParams, jstring jSeparator,
682                                       jobject jRetMsg) {
683     bool isPass;
684     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
685     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
686     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
687     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
688     const char* cReconfigCfgParams = jReconfigCfgParams != nullptr
689             ? env->GetStringUTFChars(jReconfigCfgParams, nullptr)
690             : nullptr;
691     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
692     auto codecEncoderTest =
693             new CodecEncoderTest(cMediaType, cCfgParams, cReconfigCfgParams, cSeparator);
694     isPass = codecEncoderTest->testReconfigure(cEncoder, csrcPath);
695     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
696     delete codecEncoderTest;
697     jclass clazz = env->GetObjectClass(jRetMsg);
698     jmethodID mId =
699             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
700     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
701     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
702     if (cReconfigCfgParams != nullptr) {
703         env->ReleaseStringUTFChars(jReconfigCfgParams, cReconfigCfgParams);
704     }
705     env->ReleaseStringUTFChars(jSeparator, cSeparator);
706     env->ReleaseStringUTFChars(jEncoder, cEncoder);
707     env->ReleaseStringUTFChars(jMediaType, cMediaType);
708     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
709     return static_cast<jboolean>(isPass);
710 }
711 
nativeTestSetForceSyncFrame(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)712 static jboolean nativeTestSetForceSyncFrame(JNIEnv* env, jobject, jstring jEncoder,
713                                             jstring jsrcPath, jstring jMediaType,
714                                             jstring jCfgParams, jstring jSeparator,
715                                             jobject jRetMsg) {
716     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
717     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
718     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
719     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
720     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
721     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
722     bool isPass = codecEncoderTest->testSetForceSyncFrame(cEncoder, csrcPath);
723     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
724     delete codecEncoderTest;
725     jclass clazz = env->GetObjectClass(jRetMsg);
726     jmethodID mId =
727             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
728     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
729     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
730     env->ReleaseStringUTFChars(jSeparator, cSeparator);
731     env->ReleaseStringUTFChars(jEncoder, cEncoder);
732     env->ReleaseStringUTFChars(jMediaType, cMediaType);
733     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
734     return static_cast<jboolean>(isPass);
735 }
736 
nativeTestAdaptiveBitRate(JNIEnv * env,jobject,jstring jEncoder,jstring jsrcPath,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)737 static jboolean nativeTestAdaptiveBitRate(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath,
738                                           jstring jMediaType, jstring jCfgParams,
739                                           jstring jSeparator, jobject jRetMsg) {
740     const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr);
741     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
742     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
743     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
744     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
745     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
746     bool isPass = codecEncoderTest->testAdaptiveBitRate(cEncoder, csrcPath);
747     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
748     delete codecEncoderTest;
749     jclass clazz = env->GetObjectClass(jRetMsg);
750     jmethodID mId =
751             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
752     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
753     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
754     env->ReleaseStringUTFChars(jSeparator, cSeparator);
755     env->ReleaseStringUTFChars(jEncoder, cEncoder);
756     env->ReleaseStringUTFChars(jMediaType, cMediaType);
757     env->ReleaseStringUTFChars(jsrcPath, csrcPath);
758     return static_cast<jboolean>(isPass);
759 }
760 
nativeTestOnlyEos(JNIEnv * env,jobject,jstring jEncoder,jstring jMediaType,jstring jCfgParams,jstring jSeparator,jobject jRetMsg)761 static jboolean nativeTestOnlyEos(JNIEnv* env, jobject, jstring jEncoder, jstring jMediaType,
762                                   jstring jCfgParams, jstring jSeparator, jobject jRetMsg) {
763     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
764     const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
765     const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
766     const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
767     auto codecEncoderTest = new CodecEncoderTest(cMediaType, cCfgParams, nullptr, cSeparator);
768     bool isPass = codecEncoderTest->testOnlyEos(cEncoder);
769     std::string msg = isPass ? std::string{} : codecEncoderTest->getErrorMsg();
770     delete codecEncoderTest;
771     jclass clazz = env->GetObjectClass(jRetMsg);
772     jmethodID mId =
773             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
774     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
775     env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
776     env->ReleaseStringUTFChars(jSeparator, cSeparator);
777     env->ReleaseStringUTFChars(jEncoder, cEncoder);
778     env->ReleaseStringUTFChars(jMediaType, cMediaType);
779     return static_cast<jboolean>(isPass);
780 }
781 
registerAndroidMediaV2CtsEncoderTest(JNIEnv * env)782 int registerAndroidMediaV2CtsEncoderTest(JNIEnv* env) {
783     const JNINativeMethod methodTable[] = {
784             {"nativeTestSimpleEncode",
785              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
786              "String;Ljava/lang/StringBuilder;)Z",
787              (void*)nativeTestSimpleEncode},
788             {"nativeTestReconfigure",
789              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
790              "String;Ljava/lang/String;Ljava/lang/StringBuilder;)Z",
791              (void*)nativeTestReconfigure},
792             {"nativeTestSetForceSyncFrame",
793              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
794              "String;Ljava/lang/StringBuilder;)Z",
795              (void*)nativeTestSetForceSyncFrame},
796             {"nativeTestAdaptiveBitRate",
797              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
798              "String;Ljava/lang/StringBuilder;)Z",
799              (void*)nativeTestAdaptiveBitRate},
800             {"nativeTestOnlyEos",
801              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
802              "StringBuilder;)Z",
803              (void*)nativeTestOnlyEos},
804     };
805     jclass c = env->FindClass("android/mediav2/cts/CodecEncoderTest");
806     return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
807 }
808 
JNI_OnLoad(JavaVM * vm,void *)809 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
810     JNIEnv* env;
811     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
812     if (registerAndroidMediaV2CtsEncoderTest(env) != JNI_OK) return JNI_ERR;
813     return JNI_VERSION_1_6;
814 }
815