1 /*
2  * Copyright (C) 2022 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 /* Original code copied from NDK Native-media sample code */
18 
19 //#define LOG_NDEBUG 0
20 #define LOG_TAG "NativeMedia"
21 #include <log/log.h>
22 
23 #include <assert.h>
24 #include <jni.h>
25 #include <mutex>
26 #include <queue>
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 
32 #include <android/native_window_jni.h>
33 
34 #include "media/NdkMediaExtractor.h"
35 #include "media/NdkMediaCodec.h"
36 #include "media/NdkMediaDataSource.h"
37 #include "media/NdkMediaFormat.h"
38 template <class T>
39 class simplevector {
40     T *storage;
41     int capacity;
42     int numfilled;
43 public:
simplevector()44     simplevector() {
45         capacity = 16;
46         numfilled = 0;
47         storage = new T[capacity];
48     }
~simplevector()49     ~simplevector() {
50         delete[] storage;
51     }
52 
add(T item)53     void add(T item) {
54         if (numfilled == capacity) {
55             T *old = storage;
56             capacity *= 2;
57             storage = new T[capacity];
58             for (int i = 0; i < numfilled; i++) {
59                 storage[i] = old[i];
60             }
61             delete[] old;
62         }
63         storage[numfilled] = item;
64         numfilled++;
65     }
66 
size()67     int size() {
68         return numfilled;
69     }
70 
data()71     T* data() {
72         return storage;
73     }
74 };
75 
76 struct FdDataSource {
77 
FdDataSourceFdDataSource78     FdDataSource(int fd, jlong offset, jlong size)
79         : mFd(dup(fd)),
80           mOffset(offset),
81           mSize(size) {
82     }
83 
readAtFdDataSource84     ssize_t readAt(off64_t offset, void *data, size_t size) {
85         ssize_t ssize = size;
86         if (!data || offset < 0 || offset + ssize < offset) {
87             return -1;
88         }
89         if (offset >= mSize) {
90             return 0; // EOS
91         }
92         if (offset + ssize > mSize) {
93             ssize = mSize - offset;
94         }
95         if (lseek(mFd, mOffset + offset, SEEK_SET) < 0) {
96             return -1;
97         }
98         return read(mFd, data, ssize);
99     }
100 
getSizeFdDataSource101     ssize_t getSize() {
102         return mSize;
103     }
104 
closeFdDataSource105     void close() {
106         ::close(mFd);
107     }
108 
109 private:
110 
111     int mFd;
112     off64_t mOffset;
113     int64_t mSize;
114 
115 };
116 
FdSourceReadAt(void * userdata,off64_t offset,void * data,size_t size)117 static ssize_t FdSourceReadAt(void *userdata, off64_t offset, void *data, size_t size) {
118     FdDataSource *src = (FdDataSource*) userdata;
119     return src->readAt(offset, data, size);
120 }
121 
FdSourceGetSize(void * userdata)122 static ssize_t FdSourceGetSize(void *userdata) {
123     FdDataSource *src = (FdDataSource*) userdata;
124     return src->getSize();
125 }
126 
FdSourceClose(void * userdata)127 static void FdSourceClose(void *userdata) {
128     FdDataSource *src = (FdDataSource*) userdata;
129     src->close();
130 }
131 
132 class CallbackData {
133     std::mutex mMutex;
134     std::queue<int32_t> mInputBufferIds;
135     std::queue<int32_t> mOutputBufferIds;
136     std::queue<AMediaCodecBufferInfo> mOutputBufferInfos;
137     std::queue<AMediaFormat*> mFormats;
138 
139 public:
CallbackData()140     CallbackData() { }
141 
~CallbackData()142     ~CallbackData() {
143         mMutex.lock();
144         while (!mFormats.empty()) {
145             AMediaFormat* format = mFormats.front();
146             mFormats.pop();
147             AMediaFormat_delete(format);
148         }
149         mMutex.unlock();
150     }
151 
addInputBufferId(int32_t index)152     void addInputBufferId(int32_t index) {
153         mMutex.lock();
154         mInputBufferIds.push(index);
155         mMutex.unlock();
156     }
157 
getInputBufferId()158     int32_t getInputBufferId() {
159         int32_t id = -1;
160         mMutex.lock();
161         if (!mInputBufferIds.empty()) {
162             id = mInputBufferIds.front();
163             mInputBufferIds.pop();
164         }
165         mMutex.unlock();
166         return id;
167     }
168 
addOutputBuffer(int32_t index,AMediaCodecBufferInfo * bufferInfo)169     void addOutputBuffer(int32_t index, AMediaCodecBufferInfo *bufferInfo) {
170         mMutex.lock();
171         mOutputBufferIds.push(index);
172         mOutputBufferInfos.push(*bufferInfo);
173         mMutex.unlock();
174     }
175 
addOutputFormat(AMediaFormat * format)176     void addOutputFormat(AMediaFormat *format) {
177         mMutex.lock();
178         mOutputBufferIds.push(AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED);
179         mFormats.push(format);
180         mMutex.unlock();
181     }
182 
getOutput(AMediaCodecBufferInfo * bufferInfo,AMediaFormat ** format)183     int32_t getOutput(AMediaCodecBufferInfo *bufferInfo, AMediaFormat **format) {
184         int32_t id = AMEDIACODEC_INFO_TRY_AGAIN_LATER;
185         mMutex.lock();
186         if (!mOutputBufferIds.empty()) {
187             id = mOutputBufferIds.front();
188             mOutputBufferIds.pop();
189 
190             if (id >= 0) {
191                 *bufferInfo = mOutputBufferInfos.front();
192                 mOutputBufferInfos.pop();
193             } else {  // AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
194                 *format = mFormats.front();
195                 mFormats.pop();
196             }
197         }
198         mMutex.unlock();
199         return id;
200     }
201 };
202 
OnInputAvailableCB(AMediaCodec *,void * userdata,int32_t index)203 static void OnInputAvailableCB(
204         AMediaCodec * /* aMediaCodec */,
205         void *userdata,
206         int32_t index) {
207     ALOGV("OnInputAvailableCB: index(%d)", index);
208     CallbackData *callbackData = (CallbackData *)userdata;
209     callbackData->addInputBufferId(index);
210 }
211 
OnOutputAvailableCB(AMediaCodec *,void * userdata,int32_t index,AMediaCodecBufferInfo * bufferInfo)212 static void OnOutputAvailableCB(
213         AMediaCodec * /* aMediaCodec */,
214         void *userdata,
215         int32_t index,
216         AMediaCodecBufferInfo *bufferInfo) {
217     ALOGV("OnOutputAvailableCB: index(%d), (%d, %d, %lld, 0x%x)",
218           index, bufferInfo->offset, bufferInfo->size,
219           (long long)bufferInfo->presentationTimeUs, bufferInfo->flags);
220     CallbackData *callbackData = (CallbackData *)userdata;
221     callbackData->addOutputBuffer(index, bufferInfo);
222 }
223 
OnFormatChangedCB(AMediaCodec *,void * userdata,AMediaFormat * format)224 static void OnFormatChangedCB(
225         AMediaCodec * /* aMediaCodec */,
226         void *userdata,
227         AMediaFormat *format) {
228     ALOGV("OnFormatChangedCB: format(%s)", AMediaFormat_toString(format));
229     CallbackData *callbackData = (CallbackData *)userdata;
230     callbackData->addOutputFormat(format);
231 }
232 
OnErrorCB(AMediaCodec *,void *,media_status_t err,int32_t actionCode,const char * detail)233 static void OnErrorCB(
234         AMediaCodec * /* aMediaCodec */,
235         void * /* userdata */,
236         media_status_t err,
237         int32_t actionCode,
238         const char *detail) {
239     ALOGV("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
240 }
241 
adler32(const uint8_t * input,int len)242 static int adler32(const uint8_t *input, int len) {
243 
244     int a = 1;
245     int b = 0;
246     for (int i = 0; i < len; i++) {
247         a += input[i];
248         b += a;
249         a = a % 65521;
250         b = b % 65521;
251     }
252     int ret = b * 65536 + a;
253     ALOGV("adler %d/%d", len, ret);
254     return ret;
255 }
256 
checksum(const uint8_t * in,int len,AMediaFormat * format)257 static int checksum(const uint8_t *in, int len, AMediaFormat *format) {
258     int width, stride, height;
259     if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width)) {
260         width = len;
261     }
262     if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_STRIDE, &stride)) {
263         stride = width;
264     }
265     if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
266         height = 1;
267     }
268     uint8_t *bb = new uint8_t[width * height];
269     for (int i = 0; i < height; i++) {
270         memcpy(bb + i * width, in + i * stride, width);
271     }
272     // bb is filled with data
273     int sum = adler32(bb, width * height);
274     delete[] bb;
275     return sum;
276 }
277 
Java_android_media_decoder_cts_NativeDecoderTest_getDecodedDataNative(JNIEnv * env,jclass,int fd,jlong offset,jlong size,jboolean wrapFd,jboolean useCallback)278 extern "C" jobject Java_android_media_decoder_cts_NativeDecoderTest_getDecodedDataNative(
279         JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong size, jboolean wrapFd,
280         jboolean useCallback) {
281     ALOGV("getDecodedDataNative");
282 
283     FdDataSource fdSrc(fd, offset, size);
284     AMediaExtractor *ex = AMediaExtractor_new();
285     AMediaDataSource *ndkSrc = AMediaDataSource_new();
286 
287     int err;
288     if (wrapFd) {
289         AMediaDataSource_setUserdata(ndkSrc, &fdSrc);
290         AMediaDataSource_setReadAt(ndkSrc, FdSourceReadAt);
291         AMediaDataSource_setGetSize(ndkSrc, FdSourceGetSize);
292         AMediaDataSource_setClose(ndkSrc, FdSourceClose);
293         err = AMediaExtractor_setDataSourceCustom(ex, ndkSrc);
294     } else {
295         err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
296     }
297     if (err != 0) {
298         ALOGE("setDataSource error: %d", err);
299         return NULL;
300     }
301 
302     int numtracks = AMediaExtractor_getTrackCount(ex);
303 
304     std::unique_ptr<AMediaCodec*[]> codec(new AMediaCodec*[numtracks]());
305     std::unique_ptr<AMediaFormat*[]> format(new AMediaFormat*[numtracks]());
306     std::unique_ptr<bool[]> sawInputEOS(new bool[numtracks]);
307     std::unique_ptr<bool[]> sawOutputEOS(new bool[numtracks]);
308     std::unique_ptr<simplevector<int>[]> sizes(new simplevector<int>[numtracks]);
309     std::unique_ptr<CallbackData[]> callbackData(new CallbackData[numtracks]);
310 
311     ALOGV("input has %d tracks", numtracks);
312     for (int i = 0; i < numtracks; i++) {
313         AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
314         const char *s = AMediaFormat_toString(format);
315         ALOGI("track %d format: %s", i, s);
316         const char *mime;
317         if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
318             ALOGE("no mime type");
319             return NULL;
320         } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
321             codec[i] = AMediaCodec_createDecoderByType(mime);
322             AMediaCodec_configure(codec[i], format, NULL /* surface */, NULL /* crypto */, 0);
323             if (useCallback) {
324                 AMediaCodecOnAsyncNotifyCallback aCB = {
325                     OnInputAvailableCB,
326                     OnOutputAvailableCB,
327                     OnFormatChangedCB,
328                     OnErrorCB
329                 };
330                 AMediaCodec_setAsyncNotifyCallback(codec[i], aCB, &callbackData[i]);
331             }
332             AMediaCodec_start(codec[i]);
333             sawInputEOS[i] = false;
334             sawOutputEOS[i] = false;
335         } else {
336             ALOGE("expected audio or video mime type, got %s", mime);
337             return NULL;
338         }
339         AMediaFormat_delete(format);
340         AMediaExtractor_selectTrack(ex, i);
341     }
342     int eosCount = 0;
343     while(eosCount < numtracks) {
344         int t = AMediaExtractor_getSampleTrackIndex(ex);
345         if (t >=0) {
346             ssize_t bufidx;
347             if (useCallback) {
348                 bufidx = callbackData[t].getInputBufferId();
349             } else {
350                 bufidx = AMediaCodec_dequeueInputBuffer(codec[t], 5000);
351             }
352             ALOGV("track %d, input buffer %zd", t, bufidx);
353             if (bufidx >= 0) {
354                 size_t bufsize;
355                 uint8_t *buf = AMediaCodec_getInputBuffer(codec[t], bufidx, &bufsize);
356                 int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
357                 ALOGV("read %d", sampleSize);
358                 if (sampleSize < 0) {
359                     sampleSize = 0;
360                     sawInputEOS[t] = true;
361                     ALOGV("EOS");
362                     //break;
363                 }
364                 int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
365 
366                 AMediaCodec_queueInputBuffer(codec[t], bufidx, 0, sampleSize, presentationTimeUs,
367                         sawInputEOS[t] ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
368                 AMediaExtractor_advance(ex);
369             }
370         } else {
371             ALOGV("@@@@ no more input samples");
372             for (int tt = 0; tt < numtracks; tt++) {
373                 if (!sawInputEOS[tt]) {
374                     // we ran out of samples without ever signaling EOS to the codec,
375                     // so do that now
376                     int bufidx;
377                     if (useCallback) {
378                         bufidx = callbackData[tt].getInputBufferId();
379                     } else {
380                         bufidx = AMediaCodec_dequeueInputBuffer(codec[tt], 5000);
381                     }
382                     if (bufidx >= 0) {
383                         AMediaCodec_queueInputBuffer(codec[tt], bufidx, 0, 0, 0,
384                                 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
385                         sawInputEOS[tt] = true;
386                     }
387                 }
388             }
389         }
390 
391         // check all codecs for available data
392         AMediaCodecBufferInfo info;
393         AMediaFormat *outputFormat;
394         for (int tt = 0; tt < numtracks; tt++) {
395             if (!sawOutputEOS[tt]) {
396                 int status;
397                 if (useCallback) {
398                     status = callbackData[tt].getOutput(&info, &outputFormat);
399                 } else {
400                     status = AMediaCodec_dequeueOutputBuffer(codec[tt], &info, 1);
401                 }
402                 ALOGV("dequeueoutput on track %d: %d", tt, status);
403                 if (status >= 0) {
404                     if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
405                         ALOGV("EOS on track %d", tt);
406                         sawOutputEOS[tt] = true;
407                         eosCount++;
408                     }
409                     ALOGV("got decoded buffer for track %d, size %d", tt, info.size);
410                     if (info.size > 0) {
411                         size_t bufsize;
412                         uint8_t *buf = AMediaCodec_getOutputBuffer(codec[tt], status, &bufsize);
413                         int adler = checksum(buf, info.size, format[tt]);
414                         sizes[tt].add(adler);
415                     }
416                     AMediaCodec_releaseOutputBuffer(codec[tt], status, false);
417                 } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
418                     ALOGV("output buffers changed for track %d", tt);
419                 } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
420                     if (format[tt] != NULL) {
421                         AMediaFormat_delete(format[tt]);
422                     }
423                     if (useCallback) {
424                         format[tt] = outputFormat;
425                     } else {
426                         format[tt] = AMediaCodec_getOutputFormat(codec[tt]);
427                     }
428                     ALOGV("format changed for track %d: %s", tt, AMediaFormat_toString(format[tt]));
429                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
430                     ALOGV("no output buffer right now for track %d", tt);
431                 } else {
432                     ALOGV("unexpected info code for track %d : %d", tt, status);
433                 }
434             } else {
435                 ALOGV("already at EOS on track %d", tt);
436             }
437         }
438     }
439     ALOGV("decoding loop done");
440 
441     // allocate java int array for result and return it
442     int numsamples = 0;
443     for (int i = 0; i < numtracks; i++) {
444         numsamples += sizes[i].size();
445     }
446     ALOGV("checksums: %d", numsamples);
447     jintArray ret = env->NewIntArray(numsamples);
448     jboolean isCopy;
449     jint *org = env->GetIntArrayElements(ret, &isCopy);
450     jint *dst = org;
451     for (int i = 0; i < numtracks; i++) {
452         int *data = sizes[i].data();
453         int len = sizes[i].size();
454         ALOGV("copying %d", len);
455         for (int j = 0; j < len; j++) {
456             *dst++ = data[j];
457         }
458     }
459     env->ReleaseIntArrayElements(ret, org, 0);
460 
461     for (int i = 0; i < numtracks; i++) {
462         AMediaFormat_delete(format[i]);
463         AMediaCodec_stop(codec[i]);
464         AMediaCodec_delete(codec[i]);
465     }
466     AMediaExtractor_delete(ex);
467     AMediaDataSource_delete(ndkSrc);
468     return ret;
469 }
470 
arePtsListsIdentical(const std::vector<u_long> & refArray,const std::vector<u_long> & testArray)471 static bool arePtsListsIdentical(const std::vector<u_long>& refArray,
472                                  const std::vector<u_long>& testArray) {
473     bool isEqual = true;
474     u_long i;
475     if (refArray.size() != testArray.size()) {
476         ALOGE("Expected and received timestamps list sizes are not identical");
477         ALOGE("Expected pts list size is %zu", refArray.size());
478         ALOGE("Received pts list size is %zu", testArray.size());
479         isEqual = false;
480     } else {
481         for (i = 0; i < refArray.size(); i++) {
482             if (refArray[i] != testArray[i]) {
483                 isEqual = false;
484             }
485         }
486     }
487 
488     if (!isEqual) {
489         for (i = 0; i < std::min(refArray.size(), testArray.size()); i++) {
490             ALOGE("Frame idx %3lu, expected pts %9luus, received pts %9luus", i, refArray[i],
491                   testArray[i]);
492         }
493         if (refArray.size() < testArray.size()) {
494             for (i = refArray.size(); i < testArray.size(); i++) {
495                 ALOGE("Frame idx %3lu, expected pts %11s, received pts %9luus", i, "EMPTY",
496                       testArray[i]);
497             }
498         } else if (refArray.size() > testArray.size()) {
499             for (i = testArray.size(); i < refArray.size(); i++) {
500                 ALOGE("Frame idx %3lu, expected pts %9luus, received pts %11s", i, refArray[i],
501                       "EMPTY");
502             }
503         }
504     }
505 
506     return isEqual;
507 }
508 
testNonTunneledTrickPlay(const char * fileName,ANativeWindow * pWindow,bool isAsync)509 bool testNonTunneledTrickPlay(const char *fileName, ANativeWindow *pWindow, bool isAsync) {
510     FILE *fp = fopen(fileName, "rbe");
511     if (fp == nullptr) {
512         ALOGE("Unable to open input file: %s", fileName);
513         return false;
514     }
515 
516     struct stat buf {};
517     AMediaExtractor *extractor = AMediaExtractor_new();
518     if (!fstat(fileno(fp), &buf)) {
519         media_status_t res = AMediaExtractor_setDataSourceFd(extractor, fileno(fp), 0, buf.st_size);
520         if (res != AMEDIA_OK) {
521             ALOGE("AMediaExtractor_setDataSourceFd failed with error %d", res);
522             AMediaExtractor_delete(extractor);
523             return false;
524         }
525     }
526 
527     int trackIndex = -1;
528     for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(extractor); trackID++) {
529         AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, trackID);
530         const char *mediaType = nullptr;
531         AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mediaType);
532         bool isVideo = strncmp(mediaType, "video/", strlen("video/")) == 0;
533         AMediaFormat_delete(format);
534         if (isVideo) {
535             trackIndex = trackID;
536             ALOGV("mediaType = %s, prefix = \"video/\", trackId = %zu", mediaType, trackID);
537             break;
538         }
539     }
540 
541     if (trackIndex < 0) {
542         ALOGE("No video track found for track index: %d", trackIndex);
543         AMediaExtractor_delete(extractor);
544         return false;
545     }
546 
547     AMediaExtractor_selectTrack(extractor, trackIndex);
548     AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, trackIndex);
549     const char *mediaType = nullptr;
550     AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mediaType);
551     AMediaCodec *codec = AMediaCodec_createDecoderByType(mediaType);
552     CallbackData *callbackData = new CallbackData();
553 
554     if (isAsync) {
555         AMediaCodecOnAsyncNotifyCallback callBack = {OnInputAvailableCB, OnOutputAvailableCB,
556                                                      OnFormatChangedCB, OnErrorCB};
557         auto status = AMediaCodec_setAsyncNotifyCallback(codec, callBack, callbackData);
558         if (status != AMEDIA_OK) {
559             ALOGE("failed to set async callback");
560             delete callbackData;
561             AMediaFormat_delete(format);
562             AMediaCodec_delete(codec);
563             AMediaExtractor_delete(extractor);
564             return false;
565         }
566     }
567     AMediaCodec_configure(codec, format, pWindow, nullptr, 0);
568     AMediaCodec_start(codec);
569 
570     std::atomic<bool> done(false);
571     std::vector<u_long> expectedPresentationTimes;
572     std::vector<u_long> receivedPresentationTimes;
573     bool mEosQueued = false;
574     int mDecodeOnlyCounter = 0;
575     // keep looping until the codec receives the EOS frame
576     while (!done.load()) {
577         // enqueue
578         if (!mEosQueued) {
579             size_t inBufSize;
580             int32_t id;
581             if (isAsync) {
582                 id = callbackData->getInputBufferId();
583             } else {
584                 id = AMediaCodec_dequeueInputBuffer(codec, 5000);
585             }
586             if (id >= 0) {
587                 uint8_t *inBuf = AMediaCodec_getInputBuffer(codec, id, &inBufSize);
588                 if (inBuf == nullptr) {
589                     ALOGE("AMediaCodec_getInputBuffer returned nullptr");
590                     delete callbackData;
591                     AMediaFormat_delete(format);
592                     AMediaCodec_stop(codec);
593                     AMediaCodec_delete(codec);
594                     AMediaExtractor_delete(extractor);
595                     return false;
596                 }
597                 ssize_t sampleSize = AMediaExtractor_readSampleData(extractor, inBuf, inBufSize);
598                 int64_t presentationTime = AMediaExtractor_getSampleTime(extractor);
599                 uint32_t flags = 0;
600                 if (sampleSize < 0) {
601                     flags = AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
602                     sampleSize = 0;
603                     mEosQueued = true;
604                 } else if (mDecodeOnlyCounter % 2 == 0) {
605                     flags = AMEDIACODEC_BUFFER_FLAG_DECODE_ONLY;
606                 } else {
607                     expectedPresentationTimes.push_back(presentationTime);
608                 }
609                 mDecodeOnlyCounter++;
610                 AMediaCodec_queueInputBuffer(codec, id, 0, sampleSize, presentationTime, flags);
611                 AMediaExtractor_advance(extractor);
612             }
613         }
614 
615         // dequeue
616         AMediaCodecBufferInfo bufferInfo;
617         AMediaFormat *outputFormat;
618         int id;
619         if (isAsync) {
620             id = callbackData->getOutput(&bufferInfo, &outputFormat);
621         } else {
622             id = AMediaCodec_dequeueOutputBuffer(codec, &bufferInfo, 1);
623         }
624         if (id >= 0) {
625             AMediaCodec_releaseOutputBuffer(codec, id, false);
626             if ((bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
627                 done.store(true);
628             } else {
629                 receivedPresentationTimes.push_back(bufferInfo.presentationTimeUs);
630             }
631         }
632     }
633 
634     delete callbackData;
635     AMediaFormat_delete(format);
636     AMediaCodec_stop(codec);
637     AMediaCodec_delete(codec);
638     AMediaExtractor_delete(extractor);
639     std::sort(expectedPresentationTimes.begin(), expectedPresentationTimes.end());
640     return arePtsListsIdentical(expectedPresentationTimes, receivedPresentationTimes);
641 }
642 
Java_android_media_decoder_cts_DecodeOnlyTest_nativeTestNonTunneledTrickPlay(JNIEnv * env,jclass,jstring jFileName,jobject surface,jboolean isAsync)643 extern "C" jboolean Java_android_media_decoder_cts_DecodeOnlyTest_nativeTestNonTunneledTrickPlay(
644         JNIEnv *env, jclass /*clazz*/, jstring jFileName, jobject surface, jboolean isAsync) {
645     ALOGD("nativeTestNonTunneledTrickPlay");
646     const char *cFileName = env->GetStringUTFChars(jFileName, nullptr);
647     ANativeWindow *window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
648     bool isPass = testNonTunneledTrickPlay(cFileName, window, isAsync);
649     env->ReleaseStringUTFChars(jFileName, cFileName);
650     return static_cast<jboolean>(isPass);
651 }
652