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