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