• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <unistd.h>
30 
31 #include <android/native_window_jni.h>
32 
33 #include "media/NdkMediaExtractor.h"
34 #include "media/NdkMediaCodec.h"
35 #include "media/NdkMediaDataSource.h"
36 #include "media/NdkMediaFormat.h"
37 template <class T>
38 class simplevector {
39     T *storage;
40     int capacity;
41     int numfilled;
42 public:
simplevector()43     simplevector() {
44         capacity = 16;
45         numfilled = 0;
46         storage = new T[capacity];
47     }
~simplevector()48     ~simplevector() {
49         delete[] storage;
50     }
51 
add(T item)52     void add(T item) {
53         if (numfilled == capacity) {
54             T *old = storage;
55             capacity *= 2;
56             storage = new T[capacity];
57             for (int i = 0; i < numfilled; i++) {
58                 storage[i] = old[i];
59             }
60             delete[] old;
61         }
62         storage[numfilled] = item;
63         numfilled++;
64     }
65 
size()66     int size() {
67         return numfilled;
68     }
69 
data()70     T* data() {
71         return storage;
72     }
73 };
74 
75 struct FdDataSource {
76 
FdDataSourceFdDataSource77     FdDataSource(int fd, jlong offset, jlong size)
78         : mFd(dup(fd)),
79           mOffset(offset),
80           mSize(size) {
81     }
82 
readAtFdDataSource83     ssize_t readAt(off64_t offset, void *data, size_t size) {
84         ssize_t ssize = size;
85         if (!data || offset < 0 || offset + ssize < offset) {
86             return -1;
87         }
88         if (offset >= mSize) {
89             return 0; // EOS
90         }
91         if (offset + ssize > mSize) {
92             ssize = mSize - offset;
93         }
94         if (lseek(mFd, mOffset + offset, SEEK_SET) < 0) {
95             return -1;
96         }
97         return read(mFd, data, ssize);
98     }
99 
getSizeFdDataSource100     ssize_t getSize() {
101         return mSize;
102     }
103 
closeFdDataSource104     void close() {
105         ::close(mFd);
106     }
107 
108 private:
109 
110     int mFd;
111     off64_t mOffset;
112     int64_t mSize;
113 
114 };
115 
FdSourceReadAt(void * userdata,off64_t offset,void * data,size_t size)116 static ssize_t FdSourceReadAt(void *userdata, off64_t offset, void *data, size_t size) {
117     FdDataSource *src = (FdDataSource*) userdata;
118     return src->readAt(offset, data, size);
119 }
120 
FdSourceGetSize(void * userdata)121 static ssize_t FdSourceGetSize(void *userdata) {
122     FdDataSource *src = (FdDataSource*) userdata;
123     return src->getSize();
124 }
125 
FdSourceClose(void * userdata)126 static void FdSourceClose(void *userdata) {
127     FdDataSource *src = (FdDataSource*) userdata;
128     src->close();
129 }
130 
131 class CallbackData {
132     std::mutex mMutex;
133     std::queue<int32_t> mInputBufferIds;
134     std::queue<int32_t> mOutputBufferIds;
135     std::queue<AMediaCodecBufferInfo> mOutputBufferInfos;
136     std::queue<AMediaFormat*> mFormats;
137 
138 public:
CallbackData()139     CallbackData() { }
140 
~CallbackData()141     ~CallbackData() {
142         mMutex.lock();
143         while (!mFormats.empty()) {
144             AMediaFormat* format = mFormats.front();
145             mFormats.pop();
146             AMediaFormat_delete(format);
147         }
148         mMutex.unlock();
149     }
150 
addInputBufferId(int32_t index)151     void addInputBufferId(int32_t index) {
152         mMutex.lock();
153         mInputBufferIds.push(index);
154         mMutex.unlock();
155     }
156 
getInputBufferId()157     int32_t getInputBufferId() {
158         int32_t id = -1;
159         mMutex.lock();
160         if (!mInputBufferIds.empty()) {
161             id = mInputBufferIds.front();
162             mInputBufferIds.pop();
163         }
164         mMutex.unlock();
165         return id;
166     }
167 
addOutputBuffer(int32_t index,AMediaCodecBufferInfo * bufferInfo)168     void addOutputBuffer(int32_t index, AMediaCodecBufferInfo *bufferInfo) {
169         mMutex.lock();
170         mOutputBufferIds.push(index);
171         mOutputBufferInfos.push(*bufferInfo);
172         mMutex.unlock();
173     }
174 
addOutputFormat(AMediaFormat * format)175     void addOutputFormat(AMediaFormat *format) {
176         mMutex.lock();
177         mOutputBufferIds.push(AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED);
178         mFormats.push(format);
179         mMutex.unlock();
180     }
181 
getOutput(AMediaCodecBufferInfo * bufferInfo,AMediaFormat ** format)182     int32_t getOutput(AMediaCodecBufferInfo *bufferInfo, AMediaFormat **format) {
183         int32_t id = AMEDIACODEC_INFO_TRY_AGAIN_LATER;
184         mMutex.lock();
185         if (!mOutputBufferIds.empty()) {
186             id = mOutputBufferIds.front();
187             mOutputBufferIds.pop();
188 
189             if (id >= 0) {
190                 *bufferInfo = mOutputBufferInfos.front();
191                 mOutputBufferInfos.pop();
192             } else {  // AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
193                 *format = mFormats.front();
194                 mFormats.pop();
195             }
196         }
197         mMutex.unlock();
198         return id;
199     }
200 };
201 
OnInputAvailableCB(AMediaCodec *,void * userdata,int32_t index)202 static void OnInputAvailableCB(
203         AMediaCodec * /* aMediaCodec */,
204         void *userdata,
205         int32_t index) {
206     ALOGV("OnInputAvailableCB: index(%d)", index);
207     CallbackData *callbackData = (CallbackData *)userdata;
208     callbackData->addInputBufferId(index);
209 }
210 
OnOutputAvailableCB(AMediaCodec *,void * userdata,int32_t index,AMediaCodecBufferInfo * bufferInfo)211 static void OnOutputAvailableCB(
212         AMediaCodec * /* aMediaCodec */,
213         void *userdata,
214         int32_t index,
215         AMediaCodecBufferInfo *bufferInfo) {
216     ALOGV("OnOutputAvailableCB: index(%d), (%d, %d, %lld, 0x%x)",
217           index, bufferInfo->offset, bufferInfo->size,
218           (long long)bufferInfo->presentationTimeUs, bufferInfo->flags);
219     CallbackData *callbackData = (CallbackData *)userdata;
220     callbackData->addOutputBuffer(index, bufferInfo);
221 }
222 
OnFormatChangedCB(AMediaCodec *,void * userdata,AMediaFormat * format)223 static void OnFormatChangedCB(
224         AMediaCodec * /* aMediaCodec */,
225         void *userdata,
226         AMediaFormat *format) {
227     ALOGV("OnFormatChangedCB: format(%s)", AMediaFormat_toString(format));
228     CallbackData *callbackData = (CallbackData *)userdata;
229     callbackData->addOutputFormat(format);
230 }
231 
OnErrorCB(AMediaCodec *,void *,media_status_t err,int32_t actionCode,const char * detail)232 static void OnErrorCB(
233         AMediaCodec * /* aMediaCodec */,
234         void * /* userdata */,
235         media_status_t err,
236         int32_t actionCode,
237         const char *detail) {
238     ALOGV("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
239 }
240 
adler32(const uint8_t * input,int len)241 static int adler32(const uint8_t *input, int len) {
242 
243     int a = 1;
244     int b = 0;
245     for (int i = 0; i < len; i++) {
246         a += input[i];
247         b += a;
248         a = a % 65521;
249         b = b % 65521;
250     }
251     int ret = b * 65536 + a;
252     ALOGV("adler %d/%d", len, ret);
253     return ret;
254 }
255 
checksum(const uint8_t * in,int len,AMediaFormat * format)256 static int checksum(const uint8_t *in, int len, AMediaFormat *format) {
257     int width, stride, height;
258     if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width)) {
259         width = len;
260     }
261     if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_STRIDE, &stride)) {
262         stride = width;
263     }
264     if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
265         height = 1;
266     }
267     uint8_t *bb = new uint8_t[width * height];
268     for (int i = 0; i < height; i++) {
269         memcpy(bb + i * width, in + i * stride, width);
270     }
271     // bb is filled with data
272     int sum = adler32(bb, width * height);
273     delete[] bb;
274     return sum;
275 }
276 
Java_android_media_decoder_cts_NativeDecoderTest_getDecodedDataNative(JNIEnv * env,jclass,int fd,jlong offset,jlong size,jboolean wrapFd,jboolean useCallback)277 extern "C" jobject Java_android_media_decoder_cts_NativeDecoderTest_getDecodedDataNative(
278         JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong size, jboolean wrapFd,
279         jboolean useCallback) {
280     ALOGV("getDecodedDataNative");
281 
282     FdDataSource fdSrc(fd, offset, size);
283     AMediaExtractor *ex = AMediaExtractor_new();
284     AMediaDataSource *ndkSrc = AMediaDataSource_new();
285 
286     int err;
287     if (wrapFd) {
288         AMediaDataSource_setUserdata(ndkSrc, &fdSrc);
289         AMediaDataSource_setReadAt(ndkSrc, FdSourceReadAt);
290         AMediaDataSource_setGetSize(ndkSrc, FdSourceGetSize);
291         AMediaDataSource_setClose(ndkSrc, FdSourceClose);
292         err = AMediaExtractor_setDataSourceCustom(ex, ndkSrc);
293     } else {
294         err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
295     }
296     if (err != 0) {
297         ALOGE("setDataSource error: %d", err);
298         return NULL;
299     }
300 
301     int numtracks = AMediaExtractor_getTrackCount(ex);
302 
303     std::unique_ptr<AMediaCodec*[]> codec(new AMediaCodec*[numtracks]());
304     std::unique_ptr<AMediaFormat*[]> format(new AMediaFormat*[numtracks]());
305     std::unique_ptr<bool[]> sawInputEOS(new bool[numtracks]);
306     std::unique_ptr<bool[]> sawOutputEOS(new bool[numtracks]);
307     std::unique_ptr<simplevector<int>[]> sizes(new simplevector<int>[numtracks]);
308     std::unique_ptr<CallbackData[]> callbackData(new CallbackData[numtracks]);
309 
310     ALOGV("input has %d tracks", numtracks);
311     for (int i = 0; i < numtracks; i++) {
312         AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
313         const char *s = AMediaFormat_toString(format);
314         ALOGI("track %d format: %s", i, s);
315         const char *mime;
316         if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
317             ALOGE("no mime type");
318             return NULL;
319         } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
320             codec[i] = AMediaCodec_createDecoderByType(mime);
321             AMediaCodec_configure(codec[i], format, NULL /* surface */, NULL /* crypto */, 0);
322             if (useCallback) {
323                 AMediaCodecOnAsyncNotifyCallback aCB = {
324                     OnInputAvailableCB,
325                     OnOutputAvailableCB,
326                     OnFormatChangedCB,
327                     OnErrorCB
328                 };
329                 AMediaCodec_setAsyncNotifyCallback(codec[i], aCB, &callbackData[i]);
330             }
331             AMediaCodec_start(codec[i]);
332             sawInputEOS[i] = false;
333             sawOutputEOS[i] = false;
334         } else {
335             ALOGE("expected audio or video mime type, got %s", mime);
336             return NULL;
337         }
338         AMediaFormat_delete(format);
339         AMediaExtractor_selectTrack(ex, i);
340     }
341     int eosCount = 0;
342     while(eosCount < numtracks) {
343         int t = AMediaExtractor_getSampleTrackIndex(ex);
344         if (t >=0) {
345             ssize_t bufidx;
346             if (useCallback) {
347                 bufidx = callbackData[t].getInputBufferId();
348             } else {
349                 bufidx = AMediaCodec_dequeueInputBuffer(codec[t], 5000);
350             }
351             ALOGV("track %d, input buffer %zd", t, bufidx);
352             if (bufidx >= 0) {
353                 size_t bufsize;
354                 uint8_t *buf = AMediaCodec_getInputBuffer(codec[t], bufidx, &bufsize);
355                 int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
356                 ALOGV("read %d", sampleSize);
357                 if (sampleSize < 0) {
358                     sampleSize = 0;
359                     sawInputEOS[t] = true;
360                     ALOGV("EOS");
361                     //break;
362                 }
363                 int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
364 
365                 AMediaCodec_queueInputBuffer(codec[t], bufidx, 0, sampleSize, presentationTimeUs,
366                         sawInputEOS[t] ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
367                 AMediaExtractor_advance(ex);
368             }
369         } else {
370             ALOGV("@@@@ no more input samples");
371             for (int tt = 0; tt < numtracks; tt++) {
372                 if (!sawInputEOS[tt]) {
373                     // we ran out of samples without ever signaling EOS to the codec,
374                     // so do that now
375                     int bufidx;
376                     if (useCallback) {
377                         bufidx = callbackData[tt].getInputBufferId();
378                     } else {
379                         bufidx = AMediaCodec_dequeueInputBuffer(codec[tt], 5000);
380                     }
381                     if (bufidx >= 0) {
382                         AMediaCodec_queueInputBuffer(codec[tt], bufidx, 0, 0, 0,
383                                 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
384                         sawInputEOS[tt] = true;
385                     }
386                 }
387             }
388         }
389 
390         // check all codecs for available data
391         AMediaCodecBufferInfo info;
392         AMediaFormat *outputFormat;
393         for (int tt = 0; tt < numtracks; tt++) {
394             if (!sawOutputEOS[tt]) {
395                 int status;
396                 if (useCallback) {
397                     status = callbackData[tt].getOutput(&info, &outputFormat);
398                 } else {
399                     status = AMediaCodec_dequeueOutputBuffer(codec[tt], &info, 1);
400                 }
401                 ALOGV("dequeueoutput on track %d: %d", tt, status);
402                 if (status >= 0) {
403                     if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
404                         ALOGV("EOS on track %d", tt);
405                         sawOutputEOS[tt] = true;
406                         eosCount++;
407                     }
408                     ALOGV("got decoded buffer for track %d, size %d", tt, info.size);
409                     if (info.size > 0) {
410                         size_t bufsize;
411                         uint8_t *buf = AMediaCodec_getOutputBuffer(codec[tt], status, &bufsize);
412                         int adler = checksum(buf, info.size, format[tt]);
413                         sizes[tt].add(adler);
414                     }
415                     AMediaCodec_releaseOutputBuffer(codec[tt], status, false);
416                 } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
417                     ALOGV("output buffers changed for track %d", tt);
418                 } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
419                     if (format[tt] != NULL) {
420                         AMediaFormat_delete(format[tt]);
421                     }
422                     if (useCallback) {
423                         format[tt] = outputFormat;
424                     } else {
425                         format[tt] = AMediaCodec_getOutputFormat(codec[tt]);
426                     }
427                     ALOGV("format changed for track %d: %s", tt, AMediaFormat_toString(format[tt]));
428                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
429                     ALOGV("no output buffer right now for track %d", tt);
430                 } else {
431                     ALOGV("unexpected info code for track %d : %d", tt, status);
432                 }
433             } else {
434                 ALOGV("already at EOS on track %d", tt);
435             }
436         }
437     }
438     ALOGV("decoding loop done");
439 
440     // allocate java int array for result and return it
441     int numsamples = 0;
442     for (int i = 0; i < numtracks; i++) {
443         numsamples += sizes[i].size();
444     }
445     ALOGV("checksums: %d", numsamples);
446     jintArray ret = env->NewIntArray(numsamples);
447     jboolean isCopy;
448     jint *org = env->GetIntArrayElements(ret, &isCopy);
449     jint *dst = org;
450     for (int i = 0; i < numtracks; i++) {
451         int *data = sizes[i].data();
452         int len = sizes[i].size();
453         ALOGV("copying %d", len);
454         for (int j = 0; j < len; j++) {
455             *dst++ = data[j];
456         }
457     }
458     env->ReleaseIntArrayElements(ret, org, 0);
459 
460     for (int i = 0; i < numtracks; i++) {
461         AMediaFormat_delete(format[i]);
462         AMediaCodec_stop(codec[i]);
463         AMediaCodec_delete(codec[i]);
464     }
465     AMediaExtractor_delete(ex);
466     AMediaDataSource_delete(ndkSrc);
467     return ret;
468 }
469