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