• 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 "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