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