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