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