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     aSyncHandle->setError(true);
52     ALOGE("received media codec error: %s , code : %d , action code: %d ", detail, error,
53           actionCode);
54 }
55 
CodecAsyncHandler()56 CodecAsyncHandler::CodecAsyncHandler() {
57     mOutFormat = nullptr;
58     mSignalledOutFormatChanged = false;
59     mSignalledError = false;
60 }
61 
~CodecAsyncHandler()62 CodecAsyncHandler::~CodecAsyncHandler() {
63     if (mOutFormat) {
64         AMediaFormat_delete(mOutFormat);
65         mOutFormat = nullptr;
66     }
67 }
68 
pushToInputList(callbackObject element)69 void CodecAsyncHandler::pushToInputList(callbackObject element) {
70     std::unique_lock<std::mutex> lock{mMutex};
71     mCbInputQueue.push_back(element);
72     mCondition.notify_all();
73 }
74 
pushToOutputList(callbackObject element)75 void CodecAsyncHandler::pushToOutputList(callbackObject element) {
76     std::unique_lock<std::mutex> lock{mMutex};
77     mCbOutputQueue.push_back(element);
78     mCondition.notify_all();
79 }
80 
getInput()81 callbackObject CodecAsyncHandler::getInput() {
82     callbackObject element{-1};
83     std::unique_lock<std::mutex> lock{mMutex};
84     while (!mSignalledError) {
85         if (mCbInputQueue.empty()) {
86             mCondition.wait(lock);
87         } else {
88             element = mCbInputQueue.front();
89             mCbInputQueue.pop_front();
90             break;
91         }
92     }
93     return element;
94 }
95 
getOutput()96 callbackObject CodecAsyncHandler::getOutput() {
97     callbackObject element;
98     std::unique_lock<std::mutex> lock{mMutex};
99     while (!mSignalledError) {
100         if (mCbOutputQueue.empty()) {
101             mCondition.wait(lock);
102         } else {
103             element = mCbOutputQueue.front();
104             mCbOutputQueue.pop_front();
105             break;
106         }
107     }
108     return element;
109 }
110 
getWork()111 callbackObject CodecAsyncHandler::getWork() {
112     callbackObject element;
113     std::unique_lock<std::mutex> lock{mMutex};
114     while (!mSignalledError) {
115         if (mCbInputQueue.empty() && mCbOutputQueue.empty()) {
116             mCondition.wait(lock);
117         } else {
118             if (!mCbOutputQueue.empty()) {
119                 element = mCbOutputQueue.front();
120                 mCbOutputQueue.pop_front();
121                 break;
122             } else {
123                 element = mCbInputQueue.front();
124                 mCbInputQueue.pop_front();
125                 break;
126             }
127         }
128     }
129     return element;
130 }
131 
isInputQueueEmpty()132 bool CodecAsyncHandler::isInputQueueEmpty() {
133     std::unique_lock<std::mutex> lock{mMutex};
134     return mCbInputQueue.empty();
135 }
136 
clearQueues()137 void CodecAsyncHandler::clearQueues() {
138     std::unique_lock<std::mutex> lock{mMutex};
139     mCbInputQueue.clear();
140     mCbOutputQueue.clear();
141 }
142 
setOutputFormat(AMediaFormat * format)143 void CodecAsyncHandler::setOutputFormat(AMediaFormat* format) {
144     std::unique_lock<std::mutex> lock{mMutex};
145     assert(format != nullptr);
146     if (mOutFormat) {
147         AMediaFormat_delete(mOutFormat);
148         mOutFormat = nullptr;
149     }
150     mOutFormat = format;
151     mSignalledOutFormatChanged = true;
152 }
153 
getOutputFormat()154 AMediaFormat* CodecAsyncHandler::getOutputFormat() {
155     std::unique_lock<std::mutex> lock{mMutex};
156     return mOutFormat;
157 }
158 
hasOutputFormatChanged()159 bool CodecAsyncHandler::hasOutputFormatChanged() {
160     std::unique_lock<std::mutex> lock{mMutex};
161     return mSignalledOutFormatChanged;
162 }
163 
setError(bool status)164 void CodecAsyncHandler::setError(bool status) {
165     std::unique_lock<std::mutex> lock{mMutex};
166     mSignalledError = status;
167     mCondition.notify_all();
168 }
169 
getError()170 bool CodecAsyncHandler::getError() {
171     return mSignalledError;
172 }
173 
resetContext()174 void CodecAsyncHandler::resetContext() {
175     clearQueues();
176     if (mOutFormat) {
177         AMediaFormat_delete(mOutFormat);
178         mOutFormat = nullptr;
179     }
180     mSignalledOutFormatChanged = false;
181     mSignalledError = false;
182 }
183 
setCallBack(AMediaCodec * codec,bool isCodecInAsyncMode)184 media_status_t CodecAsyncHandler::setCallBack(AMediaCodec* codec, bool isCodecInAsyncMode) {
185     media_status_t status = AMEDIA_OK;
186     if (isCodecInAsyncMode) {
187         AMediaCodecOnAsyncNotifyCallback callBack = {onAsyncInputAvailable, onAsyncOutputAvailable,
188                                                      onAsyncFormatChanged, onAsyncError};
189         status = AMediaCodec_setAsyncNotifyCallback(codec, callBack, this);
190     }
191     return status;
192 }
193 
isPtsStrictlyIncreasing(int64_t lastPts)194 bool OutputManager::isPtsStrictlyIncreasing(int64_t lastPts) {
195     bool result = true;
196     for (auto it1 = outPtsArray.cbegin(); it1 < outPtsArray.cend(); it1++) {
197         if (lastPts < *it1) {
198             lastPts = *it1;
199         } else {
200             ALOGE("Timestamp ordering check failed: last timestamp: %" PRId64
201                   " / current timestamp: %" PRId64 "",
202                   lastPts, *it1);
203             result = false;
204             break;
205         }
206     }
207     return result;
208 }
209 
updateChecksum(uint8_t * buf,AMediaCodecBufferInfo * info,int width,int height,int stride)210 void OutputManager::updateChecksum(
211         uint8_t* buf, AMediaCodecBufferInfo* info, int width, int height, int stride) {
212     uint8_t flattenInfo[16];
213     int pos = 0;
214     if (width <= 0 || height <= 0 || stride <= 0) {
215         flattenField<int32_t>(flattenInfo, &pos, info->size);
216     }
217     flattenField<int32_t>(flattenInfo, &pos,
218                           info->flags & ~AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
219     flattenField<int64_t>(flattenInfo, &pos, info->presentationTimeUs);
220     crc32value = crc32(crc32value, flattenInfo, pos);
221     if (width > 0 && height > 0 && stride > 0) {
222         // Only checksum Y plane
223         std::vector<uint8_t> tmp(width * height, 0u);
224         size_t offset = 0;
225         for (int i = 0; i < height; ++i) {
226             memcpy(tmp.data() + (i * width), buf + offset, width);
227             offset += stride;
228         }
229         crc32value = crc32(crc32value, tmp.data(), width * height);
230     } else {
231         crc32value = crc32(crc32value, buf, info->size);
232     }
233 }
234 
isOutPtsListIdenticalToInpPtsList(bool requireSorting)235 bool OutputManager::isOutPtsListIdenticalToInpPtsList(bool requireSorting) {
236     bool isEqual = true;
237     std::sort(inpPtsArray.begin(), inpPtsArray.end());
238     if (requireSorting) {
239         std::sort(outPtsArray.begin(), outPtsArray.end());
240     }
241     if (outPtsArray != inpPtsArray) {
242         if (outPtsArray.size() != inpPtsArray.size()) {
243             ALOGE("input and output presentation timestamp list sizes are not identical sizes "
244                   "exp/rec %zu/%zu", inpPtsArray.size(), outPtsArray.size());
245             isEqual = false;
246         } else {
247             int count = 0;
248             for (auto it1 = outPtsArray.cbegin(), it2 = inpPtsArray.cbegin();
249                  it1 < outPtsArray.cend(); it1++, it2++) {
250                 if (*it1 != *it2) {
251                     ALOGE("input output pts mismatch, exp/rec %" PRId64 "/%" PRId64 "",
252                           *it2, *it1);
253                     count++;
254                 }
255                 if (count == 20) {
256                     ALOGE("stopping after 20 mismatches ... ");
257                     break;
258                 }
259             }
260             if (count != 0) isEqual = false;
261         }
262     }
263     return isEqual;
264 }
265 
equals(const OutputManager * that)266 bool OutputManager::equals(const OutputManager* that) {
267     if (this == that) return true;
268     if (outPtsArray != that->outPtsArray) {
269         if (outPtsArray.size() != that->outPtsArray.size()) {
270             ALOGE("ref and test outputs presentation timestamp arrays are of unequal sizes "
271                   "%zu, %zu", outPtsArray.size(), that->outPtsArray.size());
272             return false;
273         } else {
274             int count = 0;
275             for (auto it1 = outPtsArray.cbegin(), it2 = that->outPtsArray.cbegin();
276                  it1 < outPtsArray.cend(); it1++, it2++) {
277                 if (*it1 != *it2) {
278                     ALOGE("presentation timestamp exp/rec %" PRId64 "/%" PRId64 "", *it1, *it2);
279                     count++;
280                 }
281                 if (count == 20) {
282                     ALOGE("stopping after 20 mismatches ... ");
283                     break;
284                 }
285             }
286             if (count != 0) return false;
287         }
288     }
289     if (crc32value != that->crc32value) {
290         ALOGE("ref and test outputs checksum do not match %lu, %lu", crc32value, that->crc32value);
291         if (memory.size() != that->memory.size()) {
292             ALOGE("ref and test outputs decoded buffer are of unequal sizes %zu, %zu",
293                   memory.size(), that->memory.size());
294         } else {
295             int count = 0;
296             for (auto it1 = memory.cbegin(), it2 = that->memory.cbegin(); it1 < memory.cend();
297                  it1++, it2++) {
298                 if (*it1 != *it2) {
299                     ALOGE("decoded sample exp/rec %d/%d", *it1, *it2);
300                     count++;
301                 }
302                 if (count == 20) {
303                     ALOGE("stopping after 20 mismatches ... ");
304                     break;
305                 }
306             }
307         }
308         return false;
309     }
310     return true;
311 }
312 
getRmsError(uint8_t * refData,int length)313 float OutputManager::getRmsError(uint8_t* refData, int length) {
314     long totalErrorSquared = 0;
315     if (length != memory.size()) return MAXFLOAT;
316     if ((length % 2) != 0) return MAXFLOAT;
317     auto* testData = new uint8_t[length];
318     std::copy(memory.begin(), memory.end(), testData);
319     auto* testDataReinterpret = reinterpret_cast<int16_t*>(testData);
320     auto* refDataReinterpret = reinterpret_cast<int16_t*>(refData);
321     for (int i = 0; i < length / 2; i++) {
322         int d = testDataReinterpret[i] - refDataReinterpret[i];
323         totalErrorSquared += d * d;
324     }
325     delete[] testData;
326     long avgErrorSquared = (totalErrorSquared / (length / 2));
327     return (float)sqrt(avgErrorSquared);
328 }
329 
CodecTestBase(const char * mime)330 CodecTestBase::CodecTestBase(const char* mime) {
331     mMime = mime;
332     mIsAudio = strncmp(mime, "audio/", strlen("audio/")) == 0;
333     mIsCodecInAsyncMode = false;
334     mSawInputEOS = false;
335     mSawOutputEOS = false;
336     mSignalEOSWithLastFrame = false;
337     mInputCount = 0;
338     mOutputCount = 0;
339     mPrevOutputPts = INT32_MIN;
340     mSignalledOutFormatChanged = false;
341     mOutFormat = nullptr;
342     mSaveToMem = false;
343     mOutputBuff = nullptr;
344     mCodec = nullptr;
345 }
346 
~CodecTestBase()347 CodecTestBase::~CodecTestBase() {
348     if (mOutFormat) {
349         AMediaFormat_delete(mOutFormat);
350         mOutFormat = nullptr;
351     }
352     if (mCodec) {
353         AMediaCodec_delete(mCodec);
354         mCodec = nullptr;
355     }
356 }
357 
configureCodec(AMediaFormat * format,bool isAsync,bool signalEOSWithLastFrame,bool isEncoder)358 bool CodecTestBase::configureCodec(AMediaFormat* format, bool isAsync, bool signalEOSWithLastFrame,
359                                    bool isEncoder) {
360     resetContext(isAsync, signalEOSWithLastFrame);
361     CHECK_STATUS(mAsyncHandle.setCallBack(mCodec, isAsync),
362                  "AMediaCodec_setAsyncNotifyCallback failed");
363     CHECK_STATUS(AMediaCodec_configure(mCodec, format, nullptr, nullptr,
364                                        isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0),
365                  "AMediaCodec_configure failed");
366     return true;
367 }
368 
flushCodec()369 bool CodecTestBase::flushCodec() {
370     CHECK_STATUS(AMediaCodec_flush(mCodec), "AMediaCodec_flush failed");
371     // TODO(b/147576107): is it ok to clearQueues right away or wait for some signal
372     mAsyncHandle.clearQueues();
373     mSawInputEOS = false;
374     mSawOutputEOS = false;
375     mInputCount = 0;
376     mOutputCount = 0;
377     mPrevOutputPts = INT32_MIN;
378     return true;
379 }
380 
reConfigureCodec(AMediaFormat * format,bool isAsync,bool signalEOSWithLastFrame,bool isEncoder)381 bool CodecTestBase::reConfigureCodec(AMediaFormat* format, bool isAsync,
382                                      bool signalEOSWithLastFrame, bool isEncoder) {
383     CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
384     return configureCodec(format, isAsync, signalEOSWithLastFrame, isEncoder);
385 }
386 
resetContext(bool isAsync,bool signalEOSWithLastFrame)387 void CodecTestBase::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
388     mAsyncHandle.resetContext();
389     mIsCodecInAsyncMode = isAsync;
390     mSawInputEOS = false;
391     mSawOutputEOS = false;
392     mSignalEOSWithLastFrame = signalEOSWithLastFrame;
393     mInputCount = 0;
394     mOutputCount = 0;
395     mPrevOutputPts = INT32_MIN;
396     mSignalledOutFormatChanged = false;
397     if (mOutFormat) {
398         AMediaFormat_delete(mOutFormat);
399         mOutFormat = nullptr;
400     }
401 }
402 
enqueueEOS(size_t bufferIndex)403 bool CodecTestBase::enqueueEOS(size_t bufferIndex) {
404     if (!hasSeenError() && !mSawInputEOS) {
405         CHECK_STATUS(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, 0, 0,
406                                                   AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
407                      "AMediaCodec_queueInputBuffer failed");
408         mSawInputEOS = true;
409         ALOGV("Queued End of Stream");
410     }
411     return !hasSeenError();
412 }
413 
doWork(int frameLimit)414 bool CodecTestBase::doWork(int frameLimit) {
415     bool isOk = true;
416     int frameCnt = 0;
417     if (mIsCodecInAsyncMode) {
418         // output processing after queuing EOS is done in waitForAllOutputs()
419         while (!hasSeenError() && isOk && !mSawInputEOS && frameCnt < frameLimit) {
420             callbackObject element = mAsyncHandle.getWork();
421             if (element.bufferIndex >= 0) {
422                 if (element.isInput) {
423                     isOk = enqueueInput(element.bufferIndex);
424                     frameCnt++;
425                 } else {
426                     isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
427                 }
428             }
429         }
430     } else {
431         AMediaCodecBufferInfo outInfo;
432         // output processing after queuing EOS is done in waitForAllOutputs()
433         while (isOk && !mSawInputEOS && frameCnt < frameLimit) {
434             ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
435             if (oBufferID >= 0) {
436                 isOk = dequeueOutput(oBufferID, &outInfo);
437             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
438                 if (mOutFormat) {
439                     AMediaFormat_delete(mOutFormat);
440                     mOutFormat = nullptr;
441                 }
442                 mOutFormat = AMediaCodec_getOutputFormat(mCodec);
443                 mSignalledOutFormatChanged = true;
444             } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
445             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
446             } else {
447                 ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID);
448                 return false;
449             }
450             ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs);
451             if (iBufferId >= 0) {
452                 isOk = enqueueInput(iBufferId);
453                 frameCnt++;
454             } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
455             } else {
456                 ALOGE("unexpected return value from *_dequeueInputBuffer: %zd", iBufferId);
457                 return false;
458             }
459         }
460     }
461     return !hasSeenError() && isOk;
462 }
463 
queueEOS()464 bool CodecTestBase::queueEOS() {
465     bool isOk = true;
466     if (mIsCodecInAsyncMode) {
467         while (!hasSeenError() && isOk && !mSawInputEOS) {
468             callbackObject element = mAsyncHandle.getWork();
469             if (element.bufferIndex >= 0) {
470                 if (element.isInput) {
471                     isOk = enqueueEOS(element.bufferIndex);
472                 } else {
473                     isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
474                 }
475             }
476         }
477     } else {
478         AMediaCodecBufferInfo outInfo;
479         while (isOk && !mSawInputEOS) {
480             ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
481             if (oBufferID >= 0) {
482                 isOk = dequeueOutput(oBufferID, &outInfo);
483             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
484                 if (mOutFormat) {
485                     AMediaFormat_delete(mOutFormat);
486                     mOutFormat = nullptr;
487                 }
488                 mOutFormat = AMediaCodec_getOutputFormat(mCodec);
489                 mSignalledOutFormatChanged = true;
490             } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
491             } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
492             } else {
493                 ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID);
494                 return false;
495             }
496             ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mCodec, kQDeQTimeOutUs);
497             if (iBufferId >= 0) {
498                 isOk = enqueueEOS(iBufferId);
499             } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
500             } else {
501                 ALOGE("unexpected return value from *_dequeueInputBuffer: %zd", iBufferId);
502                 return false;
503             }
504         }
505     }
506     return !hasSeenError() && isOk;
507 }
508 
waitForAllOutputs()509 bool CodecTestBase::waitForAllOutputs() {
510     bool isOk = true;
511     if (mIsCodecInAsyncMode) {
512         while (!hasSeenError() && isOk && !mSawOutputEOS) {
513             callbackObject element = mAsyncHandle.getOutput();
514             if (element.bufferIndex >= 0) {
515                 isOk = dequeueOutput(element.bufferIndex, &element.bufferInfo);
516             }
517         }
518     } else {
519         AMediaCodecBufferInfo outInfo;
520         while (!mSawOutputEOS) {
521             int bufferID = AMediaCodec_dequeueOutputBuffer(mCodec, &outInfo, kQDeQTimeOutUs);
522             if (bufferID >= 0) {
523                 isOk = dequeueOutput(bufferID, &outInfo);
524             } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
525                 if (mOutFormat) {
526                     AMediaFormat_delete(mOutFormat);
527                     mOutFormat = nullptr;
528                 }
529                 mOutFormat = AMediaCodec_getOutputFormat(mCodec);
530                 mSignalledOutFormatChanged = true;
531             } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
532             } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
533             } else {
534                 ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", bufferID);
535                 return false;
536             }
537         }
538     }
539     return !hasSeenError() && isOk;
540 }
541 
getWidth(AMediaFormat * format)542 int CodecTestBase::getWidth(AMediaFormat* format) {
543     int width = -1;
544     int cropLeft, cropRight, cropTop, cropBottom;
545     AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width);
546     if (AMediaFormat_getRect(format, "crop", &cropLeft, &cropTop, &cropRight, &cropBottom) ||
547         (AMediaFormat_getInt32(format, "crop-left", &cropLeft) &&
548          AMediaFormat_getInt32(format, "crop-right", &cropRight))) {
549         width = cropRight + 1 - cropLeft;
550     }
551     return width;
552 }
553 
getHeight(AMediaFormat * format)554 int CodecTestBase::getHeight(AMediaFormat* format) {
555     int height = -1;
556     int cropLeft, cropRight, cropTop, cropBottom;
557     AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height);
558     if (AMediaFormat_getRect(format, "crop", &cropLeft, &cropTop, &cropRight, &cropBottom) ||
559         (AMediaFormat_getInt32(format, "crop-top", &cropTop) &&
560          AMediaFormat_getInt32(format, "crop-bottom", &cropBottom))) {
561         height = cropBottom + 1 - cropTop;
562     }
563     return height;
564 }
565 
isFormatSimilar(AMediaFormat * inpFormat,AMediaFormat * outFormat)566 bool CodecTestBase::isFormatSimilar(AMediaFormat* inpFormat, AMediaFormat* outFormat) {
567     const char *refMime = nullptr, *testMime = nullptr;
568     bool hasRefMime = AMediaFormat_getString(inpFormat, AMEDIAFORMAT_KEY_MIME, &refMime);
569     bool hasTestMime = AMediaFormat_getString(outFormat, AMEDIAFORMAT_KEY_MIME, &testMime);
570 
571     if (!hasRefMime || !hasTestMime) return false;
572     if (!strncmp(refMime, "audio/", strlen("audio/"))) {
573         int32_t refSampleRate = -1;
574         int32_t testSampleRate = -2;
575         int32_t refNumChannels = -1;
576         int32_t testNumChannels = -2;
577         AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &refSampleRate);
578         AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &testSampleRate);
579         AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &refNumChannels);
580         AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &testNumChannels);
581         return refNumChannels == testNumChannels && refSampleRate == testSampleRate &&
582                (strncmp(testMime, "audio/", strlen("audio/")) == 0);
583     } else if (!strncmp(refMime, "video/", strlen("video/"))) {
584         int32_t refWidth = getWidth(inpFormat);
585         int32_t testWidth = getWidth(outFormat);
586         int32_t refHeight = getHeight(inpFormat);
587         int32_t testHeight = getHeight(outFormat);
588         return refWidth != -1 && refHeight != -1 && refWidth == testWidth &&
589                refHeight == testHeight && (strncmp(testMime, "video/", strlen("video/")) == 0);
590     }
591     return true;
592 }
593