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 "NativeCodecEncoderSurfaceTest"
19 #include <log/log.h>
20
21 #include <android/native_window_jni.h>
22 #include <jni.h>
23 #include <media/NdkMediaExtractor.h>
24 #include <media/NdkMediaMuxer.h>
25 #include <sys/stat.h>
26
27 #include "NativeCodecTestBase.h"
28 #include "NativeMediaCommon.h"
29
30 class CodecEncoderSurfaceTest {
31 private:
32 const char* mMediaType;
33 ANativeWindow* mWindow;
34 AMediaExtractor* mExtractor;
35 AMediaFormat* mDecFormat;
36 AMediaFormat* mEncFormat;
37 AMediaMuxer* mMuxer;
38 AMediaCodec* mDecoder;
39 AMediaCodec* mEncoder;
40 CodecAsyncHandler mAsyncHandleDecoder;
41 CodecAsyncHandler mAsyncHandleEncoder;
42 bool mIsCodecInAsyncMode;
43 bool mSawDecInputEOS;
44 bool mSawDecOutputEOS;
45 bool mSawEncOutputEOS;
46 bool mSignalEOSWithLastFrame;
47 int mDecInputCount;
48 int mDecOutputCount;
49 int mEncOutputCount;
50 int mMaxBFrames;
51 int mLatency;
52 bool mReviseLatency;
53 int mMuxTrackID;
54
55 OutputManager* mOutputBuff;
56 OutputManager* mRefBuff;
57 OutputManager* mTestBuff;
58 bool mSaveToMem;
59
60 std::string mErrorLogs;
61 std::string mTestEnv;
62
63 bool setUpExtractor(const char* srcFile, const char* srcMediaType, int colorFormat);
64 void deleteExtractor();
65 bool configureCodec(bool isAsync, bool signalEOSWithLastFrame, bool usePersistentSurface);
66 void resetContext(bool isAsync, bool signalEOSWithLastFrame);
67 bool enqueueDecoderInput(size_t bufferIndex);
68 bool dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo);
69 bool dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info);
70 bool tryEncoderOutput(long timeOutUs);
71 bool waitForAllEncoderOutputs();
72 bool queueEOS();
73 bool enqueueDecoderEOS(size_t bufferIndex);
74 bool doWork(int frameLimit);
hasSeenError()75 bool hasSeenError() { return mAsyncHandleDecoder.getError() || mAsyncHandleEncoder.getError(); }
76
77 public:
getErrorMsg()78 std::string getErrorMsg() {
79 return mTestEnv +
80 "################### Error Details #####################\n" +
81 mErrorLogs;
82 }
83 CodecEncoderSurfaceTest(const char* mediaType, const char* cfgParams, const char* separator);
84 ~CodecEncoderSurfaceTest();
85
86 bool testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath,
87 const char* srcMediaType, const char* muxOutPath, int colorFormat,
88 bool usePersistentSurface, int frameLimit);
89 };
90
CodecEncoderSurfaceTest(const char * mediaType,const char * cfgParams,const char * separator)91 CodecEncoderSurfaceTest::CodecEncoderSurfaceTest(const char* mediaType, const char* cfgParams,
92 const char* separator)
93 : mMediaType{mediaType} {
94 mWindow = nullptr;
95 mExtractor = nullptr;
96 mDecFormat = nullptr;
97 mEncFormat = deSerializeMediaFormat(cfgParams, separator);
98 mMuxer = nullptr;
99 mDecoder = nullptr;
100 mEncoder = nullptr;
101 resetContext(false, false);
102 mMaxBFrames = 0;
103 if (mEncFormat != nullptr) {
104 // key formalized in Android U (sdk==34).
105 // Use internally-defined when running on earlier releases, such as happens with MTS
106 if (__builtin_available(android __ANDROID_API_U__, *)) {
107 AMediaFormat_getInt32(mEncFormat, AMEDIAFORMAT_KEY_MAX_B_FRAMES, &mMaxBFrames);
108 } else {
109 AMediaFormat_getInt32(mEncFormat, COMPATIBLE_AMEDIAFORMAT_KEY_MAX_B_FRAMES,
110 &mMaxBFrames);
111 }
112 }
113 mLatency = mMaxBFrames;
114 mReviseLatency = false;
115 mMuxTrackID = -1;
116 mRefBuff = new OutputManager();
117 mTestBuff = new OutputManager(mRefBuff->getSharedErrorLogs());
118 }
119
~CodecEncoderSurfaceTest()120 CodecEncoderSurfaceTest::~CodecEncoderSurfaceTest() {
121 deleteExtractor();
122 if (mWindow) {
123 ANativeWindow_release(mWindow);
124 mWindow = nullptr;
125 }
126 if (mEncFormat) {
127 AMediaFormat_delete(mEncFormat);
128 mEncFormat = nullptr;
129 }
130 if (mMuxer) {
131 AMediaMuxer_delete(mMuxer);
132 mMuxer = nullptr;
133 }
134 if (mDecoder) {
135 AMediaCodec_delete(mDecoder);
136 mDecoder = nullptr;
137 }
138 if (mEncoder) {
139 AMediaCodec_delete(mEncoder);
140 mEncoder = nullptr;
141 }
142 delete mRefBuff;
143 delete mTestBuff;
144 }
145
setUpExtractor(const char * srcFile,const char * srcMediaType,int colorFormat)146 bool CodecEncoderSurfaceTest::setUpExtractor(const char* srcFile, const char* srcMediaType,
147 int colorFormat) {
148 FILE* fp = fopen(srcFile, "rbe");
149 struct stat buf {};
150 if (fp && !fstat(fileno(fp), &buf)) {
151 deleteExtractor();
152 mExtractor = AMediaExtractor_new();
153 media_status_t res =
154 AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size);
155 if (res != AMEDIA_OK) {
156 deleteExtractor();
157 } else {
158 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor);
159 trackID++) {
160 AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID);
161 const char* mediaType = nullptr;
162 AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mediaType);
163 if (mediaType && strcmp(mediaType, srcMediaType) == 0) {
164 AMediaExtractor_selectTrack(mExtractor, trackID);
165 AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, colorFormat);
166 mDecFormat = currFormat;
167 break;
168 }
169 AMediaFormat_delete(currFormat);
170 }
171 }
172 }
173 if (fp) fclose(fp);
174 return mDecFormat != nullptr;
175 }
176
deleteExtractor()177 void CodecEncoderSurfaceTest::deleteExtractor() {
178 if (mExtractor) {
179 AMediaExtractor_delete(mExtractor);
180 mExtractor = nullptr;
181 }
182 if (mDecFormat) {
183 AMediaFormat_delete(mDecFormat);
184 mDecFormat = nullptr;
185 }
186 }
187
configureCodec(bool isAsync,bool signalEOSWithLastFrame,bool usePersistentSurface)188 bool CodecEncoderSurfaceTest::configureCodec(bool isAsync, bool signalEOSWithLastFrame,
189 bool usePersistentSurface) {
190 RETURN_IF_NULL(mEncFormat,
191 std::string{"encountered error during deserialization of media format"})
192 resetContext(isAsync, signalEOSWithLastFrame);
193 mTestEnv = "################### Test Environment #####################\n";
194 {
195 char* name = nullptr;
196 media_status_t val = AMediaCodec_getName(mEncoder, &name);
197 if (AMEDIA_OK != val) {
198 mErrorLogs = StringFormat("%s with error %d \n", "AMediaCodec_getName failed", val);
199 return false;
200 }
201 if (!name) {
202 mErrorLogs = std::string{"AMediaCodec_getName returned null"};
203 return false;
204 }
205 mTestEnv.append(StringFormat("Component name %s \n", name));
206 AMediaCodec_releaseName(mEncoder, name);
207 }
208 {
209 char* name = nullptr;
210 media_status_t val = AMediaCodec_getName(mDecoder, &name);
211 if (AMEDIA_OK != val) {
212 mErrorLogs = StringFormat("%s with error %d \n", "AMediaCodec_getName failed", val);
213 return false;
214 }
215 if (!name) {
216 mErrorLogs = std::string{"AMediaCodec_getName returned null"};
217 return false;
218 }
219 mTestEnv.append(StringFormat("Decoder Component name %s \n", name));
220 AMediaCodec_releaseName(mDecoder, name);
221 }
222 mTestEnv += StringFormat("Format under test :- %s \n", AMediaFormat_toString(mEncFormat));
223 mTestEnv += StringFormat("Format of Decoder input :- %s \n", AMediaFormat_toString(mDecFormat));
224 mTestEnv += StringFormat("Encoder and Decoder are operating in :- %s mode \n",
225 (isAsync ? "asynchronous" : "synchronous"));
226 mTestEnv += StringFormat("Components received input eos :- %s \n",
227 (signalEOSWithLastFrame ? "with full buffer" : "with empty buffer"));
228 RETURN_IF_FAIL(mAsyncHandleEncoder.setCallBack(mEncoder, isAsync),
229 "AMediaCodec_setAsyncNotifyCallback failed")
230 RETURN_IF_FAIL(AMediaCodec_configure(mEncoder, mEncFormat, nullptr, nullptr,
231 AMEDIACODEC_CONFIGURE_FLAG_ENCODE),
232 "AMediaCodec_configure failed")
233 AMediaFormat* inpFormat = AMediaCodec_getInputFormat(mEncoder);
234 mReviseLatency = AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency);
235 AMediaFormat_delete(inpFormat);
236
237 if (usePersistentSurface) {
238 RETURN_IF_FAIL(AMediaCodec_createPersistentInputSurface(&mWindow),
239 "AMediaCodec_createPersistentInputSurface failed")
240 RETURN_IF_FAIL(AMediaCodec_setInputSurface(mEncoder,
241 reinterpret_cast<ANativeWindow*>(mWindow)),
242 "AMediaCodec_setInputSurface failed")
243 } else {
244 RETURN_IF_FAIL(AMediaCodec_createInputSurface(mEncoder, &mWindow),
245 "AMediaCodec_createInputSurface failed")
246 }
247 RETURN_IF_FAIL(mAsyncHandleDecoder.setCallBack(mDecoder, isAsync),
248 "AMediaCodec_setAsyncNotifyCallback failed")
249 RETURN_IF_FAIL(AMediaCodec_configure(mDecoder, mDecFormat, mWindow, nullptr, 0),
250 "AMediaCodec_configure failed")
251 return !hasSeenError();
252 }
253
resetContext(bool isAsync,bool signalEOSWithLastFrame)254 void CodecEncoderSurfaceTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
255 mAsyncHandleDecoder.resetContext();
256 mAsyncHandleEncoder.resetContext();
257 mIsCodecInAsyncMode = isAsync;
258 mSawDecInputEOS = false;
259 mSawDecOutputEOS = false;
260 mSawEncOutputEOS = false;
261 mSignalEOSWithLastFrame = signalEOSWithLastFrame;
262 mDecInputCount = 0;
263 mDecOutputCount = 0;
264 mEncOutputCount = 0;
265 }
266
enqueueDecoderEOS(size_t bufferIndex)267 bool CodecEncoderSurfaceTest::enqueueDecoderEOS(size_t bufferIndex) {
268 if (!hasSeenError() && !mSawDecInputEOS) {
269 RETURN_IF_FAIL(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, 0, 0,
270 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
271 "Queued Decoder End of Stream Failed")
272 mSawDecInputEOS = true;
273 ALOGV("Queued Decoder End of Stream");
274 }
275 return !hasSeenError();
276 }
277
enqueueDecoderInput(size_t bufferIndex)278 bool CodecEncoderSurfaceTest::enqueueDecoderInput(size_t bufferIndex) {
279 if (AMediaExtractor_getSampleSize(mExtractor) < 0) {
280 return enqueueDecoderEOS(bufferIndex);
281 } else {
282 uint32_t flags = 0;
283 size_t bufSize = 0;
284 uint8_t* buf = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufSize);
285 RETURN_IF_NULL(buf, std::string{"AMediaCodec_getInputBuffer failed"})
286 ssize_t size = AMediaExtractor_getSampleSize(mExtractor);
287 int64_t pts = AMediaExtractor_getSampleTime(mExtractor);
288 RETURN_IF_TRUE(size > bufSize,
289 StringFormat("extractor sample size exceeds codec input buffer size %zu %zu",
290 size, bufSize))
291 RETURN_IF_TRUE(size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize),
292 std::string{"AMediaExtractor_readSampleData failed"})
293 if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) {
294 flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
295 mSawDecInputEOS = true;
296 }
297 RETURN_IF_FAIL(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, size, pts, flags),
298 "AMediaCodec_queueInputBuffer failed")
299 ALOGV("input: id: %zu size: %zu pts: %" PRId64 " flags: %d", bufferIndex, size, pts,
300 flags);
301 if (size > 0) {
302 mOutputBuff->saveInPTS(pts);
303 mDecInputCount++;
304 }
305 }
306 return !hasSeenError();
307 }
308
dequeueDecoderOutput(size_t bufferIndex,AMediaCodecBufferInfo * bufferInfo)309 bool CodecEncoderSurfaceTest::dequeueDecoderOutput(size_t bufferIndex,
310 AMediaCodecBufferInfo* bufferInfo) {
311 if ((bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
312 mSawDecOutputEOS = true;
313 }
314 if (bufferInfo->size > 0 && (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
315 mDecOutputCount++;
316 }
317 ALOGV("output: id: %zu size: %d pts: %" PRId64 " flags: %d", bufferIndex, bufferInfo->size,
318 bufferInfo->presentationTimeUs, bufferInfo->flags);
319 RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, mWindow != nullptr),
320 "AMediaCodec_releaseOutputBuffer failed")
321 return !hasSeenError();
322 }
323
dequeueEncoderOutput(size_t bufferIndex,AMediaCodecBufferInfo * info)324 bool CodecEncoderSurfaceTest::dequeueEncoderOutput(size_t bufferIndex,
325 AMediaCodecBufferInfo* info) {
326 if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
327 mSawEncOutputEOS = true;
328 }
329 if (info->size > 0) {
330 size_t buffSize;
331 uint8_t* buf = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &buffSize);
332 // NdkMediaCodec calls ABuffer::data, which already adds offset
333 info->offset = 0;
334 if (mSaveToMem) {
335 mOutputBuff->saveToMemory(buf, info);
336 }
337 if (mMuxer != nullptr) {
338 if (mMuxTrackID == -1) {
339 mMuxTrackID = AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder));
340 RETURN_IF_FAIL(AMediaMuxer_start(mMuxer), "AMediaMuxer_start failed")
341 }
342 if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
343 RETURN_IF_FAIL(AMediaMuxer_writeSampleData(mMuxer, mMuxTrackID, buf, info),
344 "AMediaMuxer_writeSampleData failed")
345 }
346 }
347 if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
348 mOutputBuff->saveOutPTS(info->presentationTimeUs);
349 mEncOutputCount++;
350 }
351 }
352 ALOGV("output: id: %zu size: %d pts: %" PRId64 " flags: %d", bufferIndex, info->size,
353 info->presentationTimeUs, info->flags);
354 RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mEncoder, bufferIndex, false),
355 "AMediaCodec_releaseOutputBuffer failed")
356 return !hasSeenError();
357 }
358
tryEncoderOutput(long timeOutUs)359 bool CodecEncoderSurfaceTest::tryEncoderOutput(long timeOutUs) {
360 if (mIsCodecInAsyncMode) {
361 if (!hasSeenError() && !mSawEncOutputEOS) {
362 while (mReviseLatency) {
363 if (!mAsyncHandleEncoder.waitOnFormatChange()) {
364 mErrorLogs.append("taking too long to receive onOutputFormatChanged callback");
365 return false;
366 }
367 int actualLatency;
368 mReviseLatency = false;
369 if (AMediaFormat_getInt32(mAsyncHandleEncoder.getOutputFormat(),
370 AMEDIAFORMAT_KEY_LATENCY, &actualLatency)) {
371 if (mLatency < actualLatency) {
372 mLatency = actualLatency;
373 return !hasSeenError();
374 }
375 }
376 }
377 callbackObject element = mAsyncHandleEncoder.getOutput();
378 if (element.bufferIndex >= 0) {
379 if (!dequeueEncoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
380 }
381 }
382 } else {
383 AMediaCodecBufferInfo outInfo;
384 if (!mSawEncOutputEOS) {
385 int bufferID = AMediaCodec_dequeueOutputBuffer(mEncoder, &outInfo, timeOutUs);
386 if (bufferID >= 0) {
387 if (!dequeueEncoderOutput(bufferID, &outInfo)) return false;
388 } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
389 AMediaFormat* outFormat = AMediaCodec_getOutputFormat(mEncoder);
390 AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency);
391 AMediaFormat_delete(outFormat);
392 } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
393 } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
394 } else {
395 mErrorLogs.append(
396 StringFormat("unexpected return value from *_dequeueOutputBuffer: %d",
397 bufferID));
398 return false;
399 }
400 }
401 }
402 return !hasSeenError();
403 }
404
waitForAllEncoderOutputs()405 bool CodecEncoderSurfaceTest::waitForAllEncoderOutputs() {
406 if (mIsCodecInAsyncMode) {
407 while (!hasSeenError() && !mSawEncOutputEOS) {
408 if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
409 }
410 } else {
411 while (!mSawEncOutputEOS) {
412 if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
413 }
414 }
415 return !hasSeenError();
416 }
417
queueEOS()418 bool CodecEncoderSurfaceTest::queueEOS() {
419 if (mIsCodecInAsyncMode) {
420 while (!hasSeenError() && !mSawDecInputEOS) {
421 callbackObject element = mAsyncHandleDecoder.getWork();
422 if (element.bufferIndex >= 0) {
423 if (element.isInput) {
424 if (!enqueueDecoderEOS(element.bufferIndex)) return false;
425 } else {
426 if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
427 return false;
428 }
429 }
430 }
431 }
432 } else {
433 AMediaCodecBufferInfo outInfo;
434 while (!mSawDecInputEOS) {
435 ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
436 if (oBufferID >= 0) {
437 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
438 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
439 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
440 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
441 } else {
442 mErrorLogs.append(
443 StringFormat("unexpected return value from *_dequeueOutputBuffer: %d",
444 oBufferID));
445 return false;
446 }
447 ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
448 if (iBufferId >= 0) {
449 if (!enqueueDecoderEOS(iBufferId)) return false;
450 } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
451 } else {
452 mErrorLogs.append(
453 StringFormat("unexpected return value from *_dequeueInputBuffer: %zd",
454 iBufferId));
455 return false;
456 }
457 }
458 }
459
460 if (mIsCodecInAsyncMode) {
461 // output processing after queuing EOS is done in waitForAllOutputs()
462 while (!hasSeenError() && !mSawDecOutputEOS) {
463 callbackObject element = mAsyncHandleDecoder.getOutput();
464 if (element.bufferIndex >= 0) {
465 if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
466 }
467 if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
468 if (mDecOutputCount - mEncOutputCount > mLatency) {
469 if (!tryEncoderOutput(-1)) return false;
470 }
471 }
472 } else {
473 AMediaCodecBufferInfo outInfo;
474 // output processing after queuing EOS is done in waitForAllOutputs()
475 while (!mSawDecOutputEOS) {
476 if (!mSawDecOutputEOS) {
477 ssize_t oBufferID =
478 AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
479 if (oBufferID >= 0) {
480 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
481 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
482 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
483 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
484 } else {
485 mErrorLogs.append(
486 StringFormat("unexpected return value from *_dequeueOutputBuffer: %d",
487 oBufferID));
488 return false;
489 }
490 }
491 if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
492 if (mDecOutputCount - mEncOutputCount > mLatency) {
493 if (!tryEncoderOutput(-1)) return false;
494 }
495 }
496 }
497 return !hasSeenError();
498 }
499
doWork(int frameLimit)500 bool CodecEncoderSurfaceTest::doWork(int frameLimit) {
501 int frameCnt = 0;
502 if (mIsCodecInAsyncMode) {
503 // output processing after queuing EOS is done in waitForAllOutputs()
504 while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
505 callbackObject element = mAsyncHandleDecoder.getWork();
506 if (element.bufferIndex >= 0) {
507 if (element.isInput) {
508 if (!enqueueDecoderInput(element.bufferIndex)) return false;
509 frameCnt++;
510 } else {
511 if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
512 return false;
513 }
514 }
515 }
516 // check decoder EOS
517 if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
518 // encoder output
519 if (mDecOutputCount - mEncOutputCount > mLatency) {
520 if (!tryEncoderOutput(-1)) return false;
521 }
522 }
523 } else {
524 AMediaCodecBufferInfo outInfo;
525 // output processing after queuing EOS is done in waitForAllOutputs()
526 while (!mSawDecInputEOS && frameCnt < frameLimit) {
527 ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
528 if (oBufferID >= 0) {
529 if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
530 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
531 } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
532 } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
533 } else {
534 mErrorLogs.append(
535 StringFormat("unexpected return value from *_dequeueOutputBuffer: %zd",
536 oBufferID));
537 return false;
538 }
539 ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
540 if (iBufferId >= 0) {
541 if (!enqueueDecoderInput(iBufferId)) return false;
542 frameCnt++;
543 } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
544 } else {
545 mErrorLogs.append(
546 StringFormat("unexpected return value from *_dequeueInputBuffer: %zd",
547 iBufferId));
548 return false;
549 }
550 if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
551 if (mDecOutputCount - mEncOutputCount > mLatency) {
552 if (!tryEncoderOutput(-1)) return false;
553 }
554 }
555 }
556 return !hasSeenError();
557 }
558
testSimpleEncode(const char * encoder,const char * decoder,const char * srcPath,const char * srcMediaType,const char * muxOutPath,int colorFormat,bool usePersistentSurface,int frameLimit)559 bool CodecEncoderSurfaceTest::testSimpleEncode(const char* encoder, const char* decoder,
560 const char* srcPath, const char *srcMediaType,
561 const char* muxOutPath, int colorFormat,
562 bool usePersistentSurface, int frameLimit) {
563 RETURN_IF_FALSE(setUpExtractor(srcPath, srcMediaType, colorFormat),
564 std::string{"setUpExtractor failed"})
565 bool muxOutput = muxOutPath != nullptr;
566
567 /* TODO(b/149027258) */
568 if (true) mSaveToMem = false;
569 else mSaveToMem = true;
570 auto ref = mRefBuff;
571 auto test = mTestBuff;
572 int loopCounter = 0;
573 const bool boolStates[]{true, false};
574 for (bool isAsync : boolStates) {
575 AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
576 mOutputBuff = loopCounter == 0 ? ref : test;
577 mOutputBuff->reset();
578
579 /* TODO(b/147348711) */
580 /* Instead of create and delete codec at every iteration, we would like to create
581 * once and use it for all iterations and delete before exiting */
582 mEncoder = AMediaCodec_createCodecByName(encoder);
583 mDecoder = AMediaCodec_createCodecByName(decoder);
584 RETURN_IF_NULL(mDecoder, StringFormat("unable to create media codec by name %s", decoder))
585 RETURN_IF_NULL(mEncoder, StringFormat("unable to create media codec by name %s", encoder))
586 FILE* ofp = nullptr;
587 if (muxOutput && loopCounter == 0) {
588 OutputFormat muxerFormat = AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4;
589 if (!strcmp(mMediaType, AMEDIA_MIMETYPE_VIDEO_VP8) ||
590 !strcmp(mMediaType, AMEDIA_MIMETYPE_VIDEO_VP9)) {
591 muxerFormat = AMEDIAMUXER_OUTPUT_FORMAT_WEBM;
592 }
593 ofp = fopen(muxOutPath, "wbe+");
594 if (ofp) {
595 mMuxer = AMediaMuxer_new(fileno(ofp), muxerFormat);
596 }
597 }
598 if (!configureCodec(isAsync, false, usePersistentSurface)) return false;
599 RETURN_IF_FAIL(AMediaCodec_start(mEncoder), "Encoder AMediaCodec_start failed")
600 RETURN_IF_FAIL(AMediaCodec_start(mDecoder), "Decoder AMediaCodec_start failed")
601 if (!doWork(frameLimit)) return false;
602 if (!queueEOS()) return false;
603 if (!waitForAllEncoderOutputs()) return false;
604 if (muxOutput) {
605 if (mMuxer != nullptr) {
606 RETURN_IF_FAIL(AMediaMuxer_stop(mMuxer), "AMediaMuxer_stop failed")
607 mMuxTrackID = -1;
608 RETURN_IF_FAIL(AMediaMuxer_delete(mMuxer), "AMediaMuxer_delete failed")
609 mMuxer = nullptr;
610 }
611 if (ofp) fclose(ofp);
612 }
613 RETURN_IF_FAIL(AMediaCodec_stop(mDecoder), "AMediaCodec_stop failed for Decoder")
614 RETURN_IF_FAIL(AMediaCodec_stop(mEncoder), "AMediaCodec_stop failed for Encoder")
615 RETURN_IF_TRUE(mAsyncHandleDecoder.getError(),
616 std::string{"Decoder has encountered error in async mode. \n"}.append(
617 mAsyncHandleDecoder.getErrorMsg()))
618 RETURN_IF_TRUE(mAsyncHandleEncoder.getError(),
619 std::string{"Encoder has encountered error in async mode. \n"}.append(
620 mAsyncHandleEncoder.getErrorMsg()))
621 RETURN_IF_TRUE((0 == mDecInputCount), std::string{"Decoder has not received any input \n"})
622 RETURN_IF_TRUE((0 == mDecOutputCount), std::string{"Decoder has not sent any output \n"})
623 RETURN_IF_TRUE((0 == mEncOutputCount), std::string{"Encoder has not sent any output \n"})
624 RETURN_IF_TRUE((mDecInputCount != mDecOutputCount),
625 StringFormat("Decoder output count is not equal to decoder input count\n "
626 "Input count : %s, Output count : %s\n",
627 mDecInputCount, mDecOutputCount))
628 RETURN_IF_TRUE((mMaxBFrames == 0 && !mOutputBuff->isPtsStrictlyIncreasing(INT32_MIN)),
629 std::string{"Output timestamps are not strictly increasing \n"}.append(
630 ref->getErrorMsg()))
631 RETURN_IF_TRUE((mEncOutputCount != mDecOutputCount),
632 StringFormat("Encoder output count is not equal to decoder input "
633 "count\n Input count : %s, Output count : %s\n",
634 mDecInputCount, mEncOutputCount))
635 RETURN_IF_TRUE(!mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0),
636 std::string{"Input pts list and Output pts list are not identical \n"}
637 .append(ref->getErrorMsg()))
638 // TODO:(b/149027258) Remove false once output is validated across runs
639 if (false) {
640 RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
641 std::string{"Encoder output is not consistent across runs \n"}.append(
642 test->getErrorMsg()))
643 }
644 loopCounter++;
645 ANativeWindow_release(mWindow);
646 mWindow = nullptr;
647 RETURN_IF_FAIL(AMediaCodec_delete(mEncoder), "AMediaCodec_delete failed for encoder")
648 mEncoder = nullptr;
649 RETURN_IF_FAIL(AMediaCodec_delete(mDecoder), "AMediaCodec_delete failed for decoder")
650 mDecoder = nullptr;
651 }
652 return true;
653 }
654
nativeTestSimpleEncode(JNIEnv * env,jobject,jstring jEncoder,jstring jDecoder,jstring jMediaType,jstring jtestFile,jstring jTestFileMediaType,jstring jmuxFile,jint jColorFormat,jboolean jUsePersistentSurface,jstring jCfgParams,jstring jSeparator,jobject jRetMsg,jint jFrameLimit)655 static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jDecoder,
656 jstring jMediaType, jstring jtestFile,
657 jstring jTestFileMediaType, jstring jmuxFile,
658 jint jColorFormat, jboolean jUsePersistentSurface,
659 jstring jCfgParams, jstring jSeparator, jobject jRetMsg,
660 jint jFrameLimit) {
661 const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
662 const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
663 const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
664 const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
665 const char* cTestFileMediaType = env->GetStringUTFChars(jTestFileMediaType, nullptr);
666 const char* cMuxFile = jmuxFile ? env->GetStringUTFChars(jmuxFile, nullptr) : nullptr;
667 const char* cCfgParams = env->GetStringUTFChars(jCfgParams, nullptr);
668 const char* cSeparator = env->GetStringUTFChars(jSeparator, nullptr);
669 auto codecEncoderSurfaceTest = new CodecEncoderSurfaceTest(cMediaType, cCfgParams, cSeparator);
670 bool isPass = codecEncoderSurfaceTest->testSimpleEncode(cEncoder, cDecoder, cTestFile,
671 cTestFileMediaType, cMuxFile,
672 jColorFormat, jUsePersistentSurface,
673 jFrameLimit);
674 std::string msg = isPass ? std::string{} : codecEncoderSurfaceTest->getErrorMsg();
675 delete codecEncoderSurfaceTest;
676 jclass clazz = env->GetObjectClass(jRetMsg);
677 jmethodID mId =
678 env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
679 env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
680 env->ReleaseStringUTFChars(jEncoder, cEncoder);
681 env->ReleaseStringUTFChars(jDecoder, cDecoder);
682 env->ReleaseStringUTFChars(jMediaType, cMediaType);
683 env->ReleaseStringUTFChars(jtestFile, cTestFile);
684 env->ReleaseStringUTFChars(jTestFileMediaType, cTestFileMediaType);
685 if (cMuxFile) env->ReleaseStringUTFChars(jmuxFile, cMuxFile);
686 env->ReleaseStringUTFChars(jCfgParams, cCfgParams);
687 env->ReleaseStringUTFChars(jSeparator, cSeparator);
688
689 return isPass;
690 }
691
registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv * env)692 int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env) {
693 const JNINativeMethod methodTable[] = {
694 {"nativeTestSimpleEncode",
695 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
696 "String;Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/"
697 "StringBuilder;I)Z",
698 (void*)nativeTestSimpleEncode},
699 };
700 jclass c = env->FindClass("android/mediav2/cts/CodecEncoderSurfaceTest");
701 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
702 }
703
JNI_OnLoad(JavaVM * vm,void *)704 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
705 JNIEnv* env;
706 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
707 if (registerAndroidMediaV2CtsEncoderSurfaceTest(env) != JNI_OK) return JNI_ERR;
708 return JNI_VERSION_1_6;
709 }
710