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 "NativeCodecTestBase"
19 #include <log/log.h>
20
21 #include "NativeCodecTestBase.h"
22
onAsyncInputAvailable(AMediaCodec * codec,void * userdata,int32_t index)23 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
24 (void)codec;
25 assert(index >= 0);
26 auto* aSyncHandle = static_cast<CodecAsyncHandler*>(userdata);
27 callbackObject element{index};
28 aSyncHandle->pushToInputList(element);
29 }
30
onAsyncOutputAvailable(AMediaCodec * codec,void * userdata,int32_t index,AMediaCodecBufferInfo * bufferInfo)31 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
32 AMediaCodecBufferInfo* bufferInfo) {
33 (void)codec;
34 assert(index >= 0);
35 auto* aSyncHandle = static_cast<CodecAsyncHandler*>(userdata);
36 callbackObject element{index, bufferInfo};
37 aSyncHandle->pushToOutputList(element);
38 }
39
onAsyncFormatChanged(AMediaCodec * codec,void * userdata,AMediaFormat * format)40 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
41 (void)codec;
42 auto* aSyncHandle = static_cast<CodecAsyncHandler*>(userdata);
43 aSyncHandle->setOutputFormat(format);
44 ALOGI("Output format changed: %s", AMediaFormat_toString(format));
45 }
46
onAsyncError(AMediaCodec * codec,void * userdata,media_status_t error,int32_t actionCode,const char * detail)47 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t error,
48 int32_t actionCode, const char* detail) {
49 (void)codec;
50 auto* aSyncHandle = static_cast<CodecAsyncHandler*>(userdata);
51 auto msg = StringFormat("################### Async Error Details #####################\n "
52 "received media codec error: %s , code : %d , action code: %d \n",
53 detail, error, actionCode);
54 aSyncHandle->setError(true, msg);
55 ALOGE("received media codec error: %s , code : %d , action code: %d ", detail, error,
56 actionCode);
57 }
58
arePtsListsIdentical(const std::vector<int64_t> & refArray,const std::vector<int64_t> & testArray,const std::shared_ptr<std::string> & logs)59 static bool arePtsListsIdentical(const std::vector<int64_t>& refArray,
60 const std::vector<int64_t>& testArray,
61 const std::shared_ptr<std::string>& logs) {
62 bool isEqual = true;
63 if (refArray.size() != testArray.size()) {
64 logs->append("Reference and test timestamps list sizes are not identical \n");
65 logs->append(StringFormat("reference pts list size is %zu \n", refArray.size()));
66 logs->append(StringFormat("test pts list size is %zu \n", testArray.size()));
67 isEqual = false;
68 }
69 if (!isEqual || refArray != testArray) {
70 isEqual = false;
71 std::vector<int64_t> refArrayDiff;
72 std::vector<int64_t> testArrayDiff;
73 std::set_difference(refArray.begin(), refArray.end(), testArray.begin(), testArray.end(),
74 std::inserter(refArrayDiff, refArrayDiff.begin()));
75 std::set_difference(testArray.begin(), testArray.end(), refArray.begin(), refArray.end(),
76 std::inserter(testArrayDiff, testArrayDiff.begin()));
77 if (!refArrayDiff.empty()) {
78 logs->append("Some of the frame/access-units present in ref list are not present in "
79 "test list. Possibly due to frame drops \n");
80 logs->append("List of timestamps that are dropped by the component :- \n");
81 logs->append("pts :- [[ ");
82 for (auto pts : refArrayDiff) {
83 logs->append(StringFormat("{ %" PRId64 " us }, ", pts));
84 }
85 logs->append(" ]]\n");
86 }
87 if (!testArrayDiff.empty()) {
88 logs->append("Test list contains frame/access-units that are not present in ref list, "
89 "Possible due to duplicate transmissions \n");
90 logs->append("List of timestamps that are additionally present in test list are :- \n");
91 logs->append("pts :- [[ ");
92 for (auto pts : testArrayDiff) {
93 logs->append(StringFormat("{ %" PRId64 " us }, ", pts));
94 }
95 logs->append(" ]]\n");
96 }
97 }
98 return isEqual;
99 }
100
CodecAsyncHandler()101 CodecAsyncHandler::CodecAsyncHandler() {
102 mOutFormat = nullptr;
103 mSignalledOutFormatChanged = false;
104 mSignalledError = false;
105 }
106
~CodecAsyncHandler()107 CodecAsyncHandler::~CodecAsyncHandler() {
108 if (mOutFormat) {
109 AMediaFormat_delete(mOutFormat);
110 mOutFormat = nullptr;
111 }
112 }
113
pushToInputList(callbackObject element)114 void CodecAsyncHandler::pushToInputList(callbackObject element) {
115 std::unique_lock<std::mutex> lock{mMutex};
116 mCbInputQueue.push_back(element);
117 mCondition.notify_all();
118 }
119
pushToOutputList(callbackObject element)120 void CodecAsyncHandler::pushToOutputList(callbackObject element) {
121 std::unique_lock<std::mutex> lock{mMutex};
122 mCbOutputQueue.push_back(element);
123 mCondition.notify_all();
124 }
125
getInput()126 callbackObject CodecAsyncHandler::getInput() {
127 callbackObject element{-1};
128 std::unique_lock<std::mutex> lock{mMutex};
129 while (!mSignalledError) {
130 if (mCbInputQueue.empty()) {
131 mCondition.wait(lock);
132 } else {
133 element = mCbInputQueue.front();
134 mCbInputQueue.pop_front();
135 break;
136 }
137 }
138 return element;
139 }
140
getOutput()141 callbackObject CodecAsyncHandler::getOutput() {
142 callbackObject element;
143 std::unique_lock<std::mutex> lock{mMutex};
144 while (!mSignalledError) {
145 if (mCbOutputQueue.empty()) {
146 mCondition.wait(lock);
147 } else {
148 element = mCbOutputQueue.front();
149 mCbOutputQueue.pop_front();
150 break;
151 }
152 }
153 return element;
154 }
155
getWork()156 callbackObject CodecAsyncHandler::getWork() {
157 callbackObject element;
158 std::unique_lock<std::mutex> lock{mMutex};
159 while (!mSignalledError) {
160 if (mCbInputQueue.empty() && mCbOutputQueue.empty()) {
161 mCondition.wait(lock);
162 } else {
163 if (!mCbOutputQueue.empty()) {
164 element = mCbOutputQueue.front();
165 mCbOutputQueue.pop_front();
166 break;
167 } else {
168 element = mCbInputQueue.front();
169 mCbInputQueue.pop_front();
170 break;
171 }
172 }
173 }
174 return element;
175 }
176
isInputQueueEmpty()177 bool CodecAsyncHandler::isInputQueueEmpty() {
178 std::unique_lock<std::mutex> lock{mMutex};
179 return mCbInputQueue.empty();
180 }
181
clearQueues()182 void CodecAsyncHandler::clearQueues() {
183 std::unique_lock<std::mutex> lock{mMutex};
184 mCbInputQueue.clear();
185 mCbOutputQueue.clear();
186 }
187
setOutputFormat(AMediaFormat * format)188 void CodecAsyncHandler::setOutputFormat(AMediaFormat* format) {
189 std::unique_lock<std::mutex> lock{mMutex};
190 assert(format != nullptr);
191 if (mOutFormat) {
192 AMediaFormat_delete(mOutFormat);
193 mOutFormat = nullptr;
194 }
195 mOutFormat = format;
196 mSignalledOutFormatChanged = true;
197 }
198
getOutputFormat()199 AMediaFormat* CodecAsyncHandler::getOutputFormat() {
200 std::unique_lock<std::mutex> lock{mMutex};
201 return mOutFormat;
202 }
203
hasOutputFormatChanged()204 bool CodecAsyncHandler::hasOutputFormatChanged() {
205 std::unique_lock<std::mutex> lock{mMutex};
206 return mSignalledOutFormatChanged;
207 }
208
setError(bool status,std::string & msg)209 void CodecAsyncHandler::setError(bool status, std::string& msg) {
210 std::unique_lock<std::mutex> lock{mMutex};
211 mSignalledError = status;
212 mErrorMsg.append(msg);
213 mCondition.notify_all();
214 }
215
getError() const216 bool CodecAsyncHandler::getError() const {
217 return mSignalledError;
218 }
219
resetContext()220 void CodecAsyncHandler::resetContext() {
221 clearQueues();
222 if (mOutFormat) {
223 AMediaFormat_delete(mOutFormat);
224 mOutFormat = nullptr;
225 }
226 mSignalledOutFormatChanged = false;
227 mSignalledError = false;
228 mErrorMsg.clear();
229 }
230
getErrorMsg()231 std::string CodecAsyncHandler::getErrorMsg() {
232 return mErrorMsg;
233 }
234
setCallBack(AMediaCodec * codec,bool isCodecInAsyncMode)235 media_status_t CodecAsyncHandler::setCallBack(AMediaCodec* codec, bool isCodecInAsyncMode) {
236 media_status_t status = AMEDIA_OK;
237 if (isCodecInAsyncMode) {
238 AMediaCodecOnAsyncNotifyCallback callBack = {onAsyncInputAvailable, onAsyncOutputAvailable,
239 onAsyncFormatChanged, onAsyncError};
240 status = AMediaCodec_setAsyncNotifyCallback(codec, callBack, this);
241 }
242 return status;
243 }
244
isPtsStrictlyIncreasing(int64_t lastPts)245 bool OutputManager::isPtsStrictlyIncreasing(int64_t lastPts) {
246 bool result = true;
247 for (auto i = 0; i < outPtsArray.size(); i++) {
248 if (lastPts < outPtsArray[i]) {
249 lastPts = outPtsArray[i];
250 } else {
251 mErrorLogs.append("Timestamp values are not strictly increasing. \n");
252 mErrorLogs.append("Frame indices around which timestamp values decreased :- \n");
253 for (auto j = std::max(0, i - 3); j < std::min((int)outPtsArray.size(), i + 3); j++) {
254 if (j == 0) {
255 mErrorLogs.append(
256 StringFormat("pts of frame idx -1 is %" PRId64 "\n", lastPts));
257 }
258 mErrorLogs.append(
259 StringFormat("pts of frame idx %d is %" PRId64 "\n", j, outPtsArray[j]));
260 }
261 result = false;
262 break;
263 }
264 }
265 return result;
266 }
267
updateChecksum(uint8_t * buf,AMediaCodecBufferInfo * info,int width,int height,int stride,int bytesPerSample)268 void OutputManager::updateChecksum(uint8_t* buf, AMediaCodecBufferInfo* info, int width, int height,
269 int stride, int bytesPerSample) {
270 uint8_t flattenInfo[16];
271 int pos = 0;
272 if (width <= 0 || height <= 0 || stride <= 0) {
273 flattenField<int32_t>(flattenInfo, &pos, info->size);
274 }
275 flattenField<int32_t>(flattenInfo, &pos, info->flags & ~AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
276 flattenField<int64_t>(flattenInfo, &pos, info->presentationTimeUs);
277 crc32value = crc32(crc32value, flattenInfo, pos);
278 if (width > 0 && height > 0 && stride > 0 && bytesPerSample > 0) {
279 // Only checksum Y plane
280 std::vector<uint8_t> tmp(width * height * bytesPerSample, 0u);
281 size_t offset = 0;
282 for (int i = 0; i < height; ++i) {
283 memcpy(tmp.data() + (i * width * bytesPerSample), buf + offset, width * bytesPerSample);
284 offset += stride;
285 }
286 crc32value = crc32(crc32value, tmp.data(), width * height * bytesPerSample);
287 } else {
288 crc32value = crc32(crc32value, buf, info->size);
289 }
290 }
291
isOutPtsListIdenticalToInpPtsList(bool requireSorting)292 bool OutputManager::isOutPtsListIdenticalToInpPtsList(bool requireSorting) {
293 std::sort(inpPtsArray.begin(), inpPtsArray.end());
294 if (requireSorting) {
295 std::sort(outPtsArray.begin(), outPtsArray.end());
296 }
297 return arePtsListsIdentical(inpPtsArray, outPtsArray, mSharedErrorLogs);
298 }
299
equals(OutputManager * that)300 bool OutputManager::equals(OutputManager* that) {
301 if (this == that) return true;
302 if (that == nullptr) return false;
303 if (!equalsInterlaced(that)) return false;
304 if (!arePtsListsIdentical(outPtsArray, that->outPtsArray, mSharedErrorLogs)) return false;
305 return true;
306 }
307
equalsInterlaced(OutputManager * that)308 bool OutputManager::equalsInterlaced(OutputManager* that) {
309 if (this == that) return true;
310 if (that == nullptr) return false;
311 if (crc32value != that->crc32value) {
312 mSharedErrorLogs->append("CRC32 checksums computed for byte buffers received from "
313 "getOutputBuffer() do not match between ref and test runs. \n");
314 mSharedErrorLogs->append(StringFormat("Ref CRC32 checksum value is %lu \n", crc32value));
315 mSharedErrorLogs->append(
316 StringFormat("Test CRC32 checksum value is %lu \n", that->crc32value));
317 if (memory.size() == that->memory.size()) {
318 int count = 0;
319 for (int i = 0; i < memory.size(); i++) {
320 if (memory[i] != that->memory[i]) {
321 count++;
322 mSharedErrorLogs->append(StringFormat("At offset %d, ref buffer val is %x and "
323 "test buffer val is %x \n",
324 i, memory[i], that->memory[i]));
325 if (count == 20) {
326 mSharedErrorLogs->append("stopping after 20 mismatches, ...\n");
327 break;
328 }
329 }
330 }
331 if (count != 0) {
332 mSharedErrorLogs->append("Ref and Test outputs are not identical \n");
333 }
334 } else {
335 mSharedErrorLogs->append("CRC32 byte buffer checksums are different because ref and "
336 "test output sizes are not identical \n");
337 mSharedErrorLogs->append(StringFormat("Ref output buffer size %d \n", memory.size()));
338 mSharedErrorLogs->append(
339 StringFormat("Test output buffer size %d \n", that->memory.size()));
340 }
341 return false;
342 }
343 return true;
344 }
345
getRmsError(uint8_t * refData,int length)346 float OutputManager::getRmsError(uint8_t* refData, int length) {
347 long totalErrorSquared = 0;
348 if (length != memory.size()) return MAXFLOAT;
349 if ((length % 2) != 0) return MAXFLOAT;
350 auto* testData = new uint8_t[length];
351 std::copy(memory.begin(), memory.end(), testData);
352 auto* testDataReinterpret = reinterpret_cast<int16_t*>(testData);
353 auto* refDataReinterpret = reinterpret_cast<int16_t*>(refData);
354 for (int i = 0; i < length / 2; i++) {
355 int d = testDataReinterpret[i] - refDataReinterpret[i];
356 totalErrorSquared += d * d;
357 }
358 delete[] testData;
359 long avgErrorSquared = (totalErrorSquared / (length / 2));
360 return (float)sqrt(avgErrorSquared);
361 }
362
CodecTestBase(const char * mediaType)363 CodecTestBase::CodecTestBase(const char* mediaType) {
364 mMediaType = mediaType;
365 mIsAudio = strncmp(mediaType, "audio/", strlen("audio/")) == 0;
366 mIsVideo = strncmp(mediaType, "video/", strlen("video/")) == 0;
367 mIsCodecInAsyncMode = false;
368 mSawInputEOS = false;
369 mSawOutputEOS = false;
370 mSignalEOSWithLastFrame = false;
371 mInputCount = 0;
372 mOutputCount = 0;
373 mPrevOutputPts = INT32_MIN;
374 mSignalledOutFormatChanged = false;
375 mOutFormat = nullptr;
376 mSaveToMem = false;
377 mOutputBuff = nullptr;
378 mCodec = nullptr;
379 mBytesPerSample = mIsAudio ? 2 : 1;
380 mRefBuff = new OutputManager();
381 mTestBuff = new OutputManager(mRefBuff->getSharedErrorLogs());
382 mReconfBuff = new OutputManager(mRefBuff->getSharedErrorLogs());
383 }
384
~CodecTestBase()385 CodecTestBase::~CodecTestBase() {
386 if (mOutFormat) {
387 AMediaFormat_delete(mOutFormat);
388 mOutFormat = nullptr;
389 }
390 if (mCodec) {
391 AMediaCodec_delete(mCodec);
392 mCodec = nullptr;
393 }
394 delete mRefBuff;
395 delete mTestBuff;
396 delete mReconfBuff;
397 }
398
configureCodec(AMediaFormat * format,bool isAsync,bool signalEOSWithLastFrame,bool isEncoder)399 bool CodecTestBase::configureCodec(AMediaFormat* format, bool isAsync, bool signalEOSWithLastFrame,
400 bool isEncoder) {
401 resetContext(isAsync, signalEOSWithLastFrame);
402 mTestEnv = "################### Test Environment #####################\n";
403 {
404 char* name = nullptr;
405 media_status_t val = AMediaCodec_getName(mCodec, &name);
406 if (AMEDIA_OK != val) {
407 mErrorLogs = StringFormat("%s with error %d \n", "AMediaCodec_getName failed", val);
408 return false;
409 }
410 if (!name) {
411 mErrorLogs = std::string{"AMediaCodec_getName returned null"};
412 return false;
413 }
414 mTestEnv.append(StringFormat("Component name %s \n", name));
415 AMediaCodec_releaseName(mCodec, name);
416 }
417 mTestEnv.append(StringFormat("Format under test :- %s \n", AMediaFormat_toString(format)));
418 mTestEnv.append(StringFormat("Component operating in :- %s mode \n",
419 (isAsync ? "asynchronous" : "synchronous")));
420 mTestEnv.append(
421 StringFormat("Component received input eos :- %s \n",
422 (signalEOSWithLastFrame ? "with full buffer" : "with empty buffer")));
423 RETURN_IF_FAIL(mAsyncHandle.setCallBack(mCodec, isAsync),
424 "AMediaCodec_setAsyncNotifyCallback failed")
425 RETURN_IF_FAIL(AMediaCodec_configure(mCodec, format, nullptr, nullptr,
426 isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0),
427 "AMediaCodec_configure failed")
428 return true;
429 }
430
flushCodec()431 bool CodecTestBase::flushCodec() {
432 RETURN_IF_FAIL(AMediaCodec_flush(mCodec), "AMediaCodec_flush failed")
433 // TODO(b/147576107): is it ok to clearQueues right away or wait for some signal
434 mAsyncHandle.clearQueues();
435 mSawInputEOS = false;
436 mSawOutputEOS = false;
437 mInputCount = 0;
438 mOutputCount = 0;
439 mPrevOutputPts = INT32_MIN;
440 return true;
441 }
442
reConfigureCodec(AMediaFormat * format,bool isAsync,bool signalEOSWithLastFrame,bool isEncoder)443 bool CodecTestBase::reConfigureCodec(AMediaFormat* format, bool isAsync,
444 bool signalEOSWithLastFrame, bool isEncoder) {
445 RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
446 return configureCodec(format, isAsync, signalEOSWithLastFrame, isEncoder);
447 }
448
resetContext(bool isAsync,bool signalEOSWithLastFrame)449 void CodecTestBase::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
450 mAsyncHandle.resetContext();
451 mIsCodecInAsyncMode = isAsync;
452 mSawInputEOS = false;
453 mSawOutputEOS = false;
454 mSignalEOSWithLastFrame = signalEOSWithLastFrame;
455 mInputCount = 0;
456 mOutputCount = 0;
457 mPrevOutputPts = INT32_MIN;
458 mSignalledOutFormatChanged = false;
459 if (mOutFormat) {
460 AMediaFormat_delete(mOutFormat);
461 mOutFormat = nullptr;
462 }
463 }
464
isTestStateValid()465 bool CodecTestBase::isTestStateValid() {
466 RETURN_IF_TRUE(hasSeenError(),
467 std::string{"Encountered error in async mode. \n"}.append(
468 mAsyncHandle.getErrorMsg()))
469 RETURN_IF_TRUE(mInputCount > 0 && mOutputCount <= 0,
470 StringFormat("fed %d input frames, received no output frames \n", mInputCount))
471 /*if (mInputCount == 0 && mInputCount != mOutputCount) {
472 (void)mOutputBuff->isOutPtsListIdenticalToInpPtsList(true);
473 RETURN_IF_TRUE(true,
474 StringFormat("The number of output frames received is not same as number of "
475 "input frames queued. Output count is %d, Input count is %d \n",
476 mOutputCount, mInputCount)
477 .append(mOutputBuff->getErrorMsg()))
478 }*/
479 return true;
480 }
481
enqueueEOS(size_t bufferIndex)482 bool CodecTestBase::enqueueEOS(size_t bufferIndex) {
483 if (!hasSeenError() && !mSawInputEOS) {
484 RETURN_IF_FAIL(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, 0, 0,
485 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
486 "AMediaCodec_queueInputBuffer failed")
487 mSawInputEOS = true;
488 ALOGV("Queued End of Stream");
489 }
490 return !hasSeenError();
491 }
492
doWork(int frameLimit)493 bool CodecTestBase::doWork(int frameLimit) {
494 bool isOk = true;
495 int frameCnt = 0;
496 if (mIsCodecInAsyncMode) {
497 // output processing after queuing EOS is done in waitForAllOutputs()
498 while (!hasSeenError() && isOk && !mSawInputEOS && frameCnt < frameLimit) {
499 callbackObject element = mAsyncHandle.getWork();
500 if (element.bufferIndex >= 0) {
501 if (element.isInput) {
502 isOk = enqueueInput(element.bufferIndex);
503 frameCnt++;
504 } else {
505 isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
506 }
507 }
508 }
509 } else {
510 AMediaCodecBufferInfo outInfo;
511 // output processing after queuing EOS is done in waitForAllOutputs()
512 while (isOk && !mSawInputEOS && frameCnt < frameLimit) {
513 ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
514 if (oBufferID >= 0) {
515 isOk = dequeueOutput(oBufferID, &outInfo);
516 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
517 if (mOutFormat) {
518 AMediaFormat_delete(mOutFormat);
519 mOutFormat = nullptr;
520 }
521 mOutFormat = AMediaCodec_getOutputFormat(mCodec);
522 mSignalledOutFormatChanged = true;
523 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
524 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
525 } else {
526 auto msg = StringFormat("unexpected return value from "
527 "AMediaCodec_dequeueOutputBuffer: %zd \n",
528 oBufferID);
529 mErrorLogs.append(msg);
530 ALOGE("%s", msg.c_str());
531 return false;
532 }
533 ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs);
534 if (iBufferId >= 0) {
535 isOk = enqueueInput(iBufferId);
536 frameCnt++;
537 } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
538 } else {
539 auto msg = StringFormat("unexpected return value from "
540 "AMediaCodec_dequeueInputBuffer: %zd \n",
541 iBufferId);
542 mErrorLogs.append(msg);
543 ALOGE("%s", msg.c_str());
544 return false;
545 }
546 }
547 }
548 return !hasSeenError() && isOk;
549 }
550
queueEOS()551 bool CodecTestBase::queueEOS() {
552 bool isOk = true;
553 if (mIsCodecInAsyncMode) {
554 while (!hasSeenError() && isOk && !mSawInputEOS) {
555 callbackObject element = mAsyncHandle.getWork();
556 if (element.bufferIndex >= 0) {
557 if (element.isInput) {
558 isOk = enqueueEOS(element.bufferIndex);
559 } else {
560 isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
561 }
562 }
563 }
564 } else {
565 AMediaCodecBufferInfo outInfo;
566 while (isOk && !mSawInputEOS) {
567 ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
568 if (oBufferID >= 0) {
569 isOk = dequeueOutput(oBufferID, &outInfo);
570 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
571 if (mOutFormat) {
572 AMediaFormat_delete(mOutFormat);
573 mOutFormat = nullptr;
574 }
575 mOutFormat = AMediaCodec_getOutputFormat(mCodec);
576 mSignalledOutFormatChanged = true;
577 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
578 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
579 } else {
580 auto msg = StringFormat("unexpected return value from "
581 "AMediaCodec_dequeueOutputBuffer: %zd \n",
582 oBufferID);
583 mErrorLogs.append(msg);
584 ALOGE("%s", msg.c_str());
585 return false;
586 }
587 ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs);
588 if (iBufferId >= 0) {
589 isOk = enqueueEOS(iBufferId);
590 } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
591 } else {
592 auto msg = StringFormat("unexpected return value from "
593 "AMediaCodec_dequeueInputBuffer: %zd \n",
594 iBufferId);
595 mErrorLogs.append(msg);
596 ALOGE("%s", msg.c_str());
597 return false;
598 }
599 }
600 }
601 return !hasSeenError() && isOk;
602 }
603
waitForAllOutputs()604 bool CodecTestBase::waitForAllOutputs() {
605 bool isOk = true;
606 if (mIsCodecInAsyncMode) {
607 while (!hasSeenError() && isOk && !mSawOutputEOS) {
608 callbackObject element = mAsyncHandle.getOutput();
609 if (element.bufferIndex >= 0) {
610 isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
611 }
612 }
613 } else {
614 AMediaCodecBufferInfo outInfo;
615 while (!mSawOutputEOS) {
616 int bufferID = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
617 if (bufferID >= 0) {
618 isOk = dequeueOutput(bufferID, &outInfo);
619 } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
620 if (mOutFormat) {
621 AMediaFormat_delete(mOutFormat);
622 mOutFormat = nullptr;
623 }
624 mOutFormat = AMediaCodec_getOutputFormat(mCodec);
625 mSignalledOutFormatChanged = true;
626 } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
627 } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
628 } else {
629 auto msg = StringFormat("unexpected return value from "
630 "AMediaCodec_dequeueOutputBuffer: %d \n",
631 bufferID);
632 mErrorLogs.append(msg);
633 ALOGE("%s", msg.c_str());
634 return false;
635 }
636 }
637 }
638 return isOk && isTestStateValid();
639 }
640
getWidth(AMediaFormat * format)641 int CodecTestBase::getWidth(AMediaFormat* format) {
642 int width = -1;
643 int cropLeft, cropRight, cropTop, cropBottom;
644 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width);
645 if (AMediaFormat_getRect(format, "crop", &cropLeft, &cropTop, &cropRight, &cropBottom) ||
646 (AMediaFormat_getInt32(format, "crop-left", &cropLeft) &&
647 AMediaFormat_getInt32(format, "crop-right", &cropRight))) {
648 width = cropRight + 1 - cropLeft;
649 }
650 return width;
651 }
652
getHeight(AMediaFormat * format)653 int CodecTestBase::getHeight(AMediaFormat* format) {
654 int height = -1;
655 int cropLeft, cropRight, cropTop, cropBottom;
656 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height);
657 if (AMediaFormat_getRect(format, "crop", &cropLeft, &cropTop, &cropRight, &cropBottom) ||
658 (AMediaFormat_getInt32(format, "crop-top", &cropTop) &&
659 AMediaFormat_getInt32(format, "crop-bottom", &cropBottom))) {
660 height = cropBottom + 1 - cropTop;
661 }
662 return height;
663 }
664
isFormatSimilar(AMediaFormat * inpFormat,AMediaFormat * outFormat)665 bool CodecTestBase::isFormatSimilar(AMediaFormat* inpFormat, AMediaFormat* outFormat) {
666 const char *refMediaType = nullptr, *testMediaType = nullptr;
667 bool hasRefMediaType = AMediaFormat_getString(inpFormat, AMEDIAFORMAT_KEY_MIME, &refMediaType);
668 bool hasTestMediaType =
669 AMediaFormat_getString(outFormat, AMEDIAFORMAT_KEY_MIME, &testMediaType);
670
671 if (!hasRefMediaType || !hasTestMediaType) return false;
672 if (!strncmp(refMediaType, "audio/", strlen("audio/"))) {
673 int32_t refSampleRate = -1;
674 int32_t testSampleRate = -2;
675 int32_t refNumChannels = -1;
676 int32_t testNumChannels = -2;
677 AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &refSampleRate);
678 AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &testSampleRate);
679 AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &refNumChannels);
680 AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &testNumChannels);
681 return refNumChannels == testNumChannels && refSampleRate == testSampleRate &&
682 (strncmp(testMediaType, "audio/", strlen("audio/")) == 0);
683 } else if (!strncmp(refMediaType, "video/", strlen("video/"))) {
684 int32_t refWidth = getWidth(inpFormat);
685 int32_t testWidth = getWidth(outFormat);
686 int32_t refHeight = getHeight(inpFormat);
687 int32_t testHeight = getHeight(outFormat);
688 return refWidth != -1 && refHeight != -1 && refWidth == testWidth &&
689 refHeight == testHeight &&
690 (strncmp(testMediaType, "video/", strlen("video/")) == 0);
691 }
692 return true;
693 }
694