1/*
2 * Copyright (C) 2014 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#include <inttypes.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "NdkMediaCodec"
21
22#include <media/NdkMediaCodec.h>
23#include <media/NdkMediaError.h>
24#include "NdkMediaCryptoPriv.h"
25#include "NdkMediaFormatPriv.h"
26
27#include <utils/Log.h>
28#include <utils/StrongPointer.h>
29#include <gui/Surface.h>
30
31#include <media/stagefright/foundation/ALooper.h>
32#include <media/stagefright/foundation/AMessage.h>
33
34#include <media/stagefright/PersistentSurface.h>
35#include <media/stagefright/MediaCodec.h>
36#include <media/stagefright/MediaErrors.h>
37#include <media/MediaCodecBuffer.h>
38#include <android/native_window.h>
39
40using namespace android;
41
42
43static media_status_t translate_error(status_t err) {
44    if (err == OK) {
45        return AMEDIA_OK;
46    } else if (err == -EAGAIN) {
47        return (media_status_t) AMEDIACODEC_INFO_TRY_AGAIN_LATER;
48    }
49    ALOGE("sf error code: %d", err);
50    return AMEDIA_ERROR_UNKNOWN;
51}
52
53enum {
54    kWhatActivityNotify,
55    kWhatAsyncNotify,
56    kWhatRequestActivityNotifications,
57    kWhatStopActivityNotifications,
58};
59
60struct AMediaCodecPersistentSurface : public Surface {
61    sp<PersistentSurface> mPersistentSurface;
62    AMediaCodecPersistentSurface(
63            const sp<IGraphicBufferProducer>& igbp,
64            const sp<PersistentSurface>& ps)
65            : Surface(igbp) {
66        mPersistentSurface = ps;
67    }
68    virtual ~AMediaCodecPersistentSurface() {
69        //mPersistentSurface ref will be let go off here
70    }
71};
72
73class CodecHandler: public AHandler {
74private:
75    AMediaCodec* mCodec;
76public:
77    explicit CodecHandler(AMediaCodec *codec);
78    virtual void onMessageReceived(const sp<AMessage> &msg);
79};
80
81typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
82
83struct AMediaCodec {
84    sp<android::MediaCodec> mCodec;
85    sp<ALooper> mLooper;
86    sp<CodecHandler> mHandler;
87    sp<AMessage> mActivityNotification;
88    int32_t mGeneration;
89    bool mRequestedActivityNotification;
90    OnCodecEvent mCallback;
91    void *mCallbackUserData;
92
93    sp<AMessage> mAsyncNotify;
94    mutable Mutex mAsyncCallbackLock;
95    AMediaCodecOnAsyncNotifyCallback mAsyncCallback;
96    void *mAsyncCallbackUserData;
97};
98
99CodecHandler::CodecHandler(AMediaCodec *codec) {
100    mCodec = codec;
101}
102
103void CodecHandler::onMessageReceived(const sp<AMessage> &msg) {
104
105    switch (msg->what()) {
106        case kWhatRequestActivityNotifications:
107        {
108            if (mCodec->mRequestedActivityNotification) {
109                break;
110            }
111
112            mCodec->mCodec->requestActivityNotification(mCodec->mActivityNotification);
113            mCodec->mRequestedActivityNotification = true;
114            break;
115        }
116
117        case kWhatActivityNotify:
118        {
119            {
120                int32_t generation;
121                msg->findInt32("generation", &generation);
122
123                if (generation != mCodec->mGeneration) {
124                    // stale
125                    break;
126                }
127
128                mCodec->mRequestedActivityNotification = false;
129            }
130
131            if (mCodec->mCallback) {
132                mCodec->mCallback(mCodec, mCodec->mCallbackUserData);
133            }
134            break;
135        }
136
137        case kWhatAsyncNotify:
138        {
139             int32_t cbID;
140             if (!msg->findInt32("callbackID", &cbID)) {
141                 ALOGE("kWhatAsyncNotify: callbackID is expected.");
142                 break;
143             }
144
145             ALOGV("kWhatAsyncNotify: cbID = %d", cbID);
146
147             switch (cbID) {
148                 case MediaCodec::CB_INPUT_AVAILABLE:
149                 {
150                     int32_t index;
151                     if (!msg->findInt32("index", &index)) {
152                         ALOGE("CB_INPUT_AVAILABLE: index is expected.");
153                         break;
154                     }
155
156                     Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
157                     if (mCodec->mAsyncCallbackUserData != NULL
158                         || mCodec->mAsyncCallback.onAsyncInputAvailable != NULL) {
159                         mCodec->mAsyncCallback.onAsyncInputAvailable(
160                                 mCodec,
161                                 mCodec->mAsyncCallbackUserData,
162                                 index);
163                     }
164
165                     break;
166                 }
167
168                 case MediaCodec::CB_OUTPUT_AVAILABLE:
169                 {
170                     int32_t index;
171                     size_t offset;
172                     size_t size;
173                     int64_t timeUs;
174                     int32_t flags;
175
176                     if (!msg->findInt32("index", &index)) {
177                         ALOGE("CB_OUTPUT_AVAILABLE: index is expected.");
178                         break;
179                     }
180                     if (!msg->findSize("offset", &offset)) {
181                         ALOGE("CB_OUTPUT_AVAILABLE: offset is expected.");
182                         break;
183                     }
184                     if (!msg->findSize("size", &size)) {
185                         ALOGE("CB_OUTPUT_AVAILABLE: size is expected.");
186                         break;
187                     }
188                     if (!msg->findInt64("timeUs", &timeUs)) {
189                         ALOGE("CB_OUTPUT_AVAILABLE: timeUs is expected.");
190                         break;
191                     }
192                     if (!msg->findInt32("flags", &flags)) {
193                         ALOGE("CB_OUTPUT_AVAILABLE: flags is expected.");
194                         break;
195                     }
196
197                     AMediaCodecBufferInfo bufferInfo = {
198                         (int32_t)offset,
199                         (int32_t)size,
200                         timeUs,
201                         (uint32_t)flags};
202
203                     Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
204                     if (mCodec->mAsyncCallbackUserData != NULL
205                         || mCodec->mAsyncCallback.onAsyncOutputAvailable != NULL) {
206                         mCodec->mAsyncCallback.onAsyncOutputAvailable(
207                                 mCodec,
208                                 mCodec->mAsyncCallbackUserData,
209                                 index,
210                                 &bufferInfo);
211                     }
212
213                     break;
214                 }
215
216                 case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
217                 {
218                     sp<AMessage> format;
219                     if (!msg->findMessage("format", &format)) {
220                         ALOGE("CB_OUTPUT_FORMAT_CHANGED: format is expected.");
221                         break;
222                     }
223
224                     AMediaFormat *aMediaFormat = AMediaFormat_fromMsg(&format);
225
226                     Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
227                     if (mCodec->mAsyncCallbackUserData != NULL
228                         || mCodec->mAsyncCallback.onAsyncFormatChanged != NULL) {
229                         mCodec->mAsyncCallback.onAsyncFormatChanged(
230                                 mCodec,
231                                 mCodec->mAsyncCallbackUserData,
232                                 aMediaFormat);
233                     }
234
235                     break;
236                 }
237
238                 case MediaCodec::CB_ERROR:
239                 {
240                     status_t err;
241                     int32_t actionCode;
242                     AString detail;
243                     if (!msg->findInt32("err", &err)) {
244                         ALOGE("CB_ERROR: err is expected.");
245                         break;
246                     }
247                     if (!msg->findInt32("action", &actionCode)) {
248                         ALOGE("CB_ERROR: action is expected.");
249                         break;
250                     }
251                     msg->findString("detail", &detail);
252                     ALOGE("Decoder reported error(0x%x), actionCode(%d), detail(%s)",
253                           err, actionCode, detail.c_str());
254
255                     Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
256                     if (mCodec->mAsyncCallbackUserData != NULL
257                         || mCodec->mAsyncCallback.onAsyncError != NULL) {
258                         mCodec->mAsyncCallback.onAsyncError(
259                                 mCodec,
260                                 mCodec->mAsyncCallbackUserData,
261                                 translate_error(err),
262                                 actionCode,
263                                 detail.c_str());
264                     }
265
266                     break;
267                 }
268
269                 default:
270                 {
271                     ALOGE("kWhatAsyncNotify: callbackID(%d) is unexpected.", cbID);
272                     break;
273                 }
274             }
275             break;
276        }
277
278        case kWhatStopActivityNotifications:
279        {
280            sp<AReplyToken> replyID;
281            msg->senderAwaitsResponse(&replyID);
282
283            mCodec->mGeneration++;
284            mCodec->mRequestedActivityNotification = false;
285
286            sp<AMessage> response = new AMessage;
287            response->postReply(replyID);
288            break;
289        }
290
291        default:
292            ALOGE("shouldn't be here");
293            break;
294    }
295
296}
297
298
299static void requestActivityNotification(AMediaCodec *codec) {
300    (new AMessage(kWhatRequestActivityNotifications, codec->mHandler))->post();
301}
302
303extern "C" {
304
305static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) {
306    AMediaCodec *mData = new AMediaCodec();
307    mData->mLooper = new ALooper;
308    mData->mLooper->setName("NDK MediaCodec_looper");
309    size_t res = mData->mLooper->start(
310            false,      // runOnCallingThread
311            true,       // canCallJava XXX
312            PRIORITY_AUDIO);
313    if (res != OK) {
314        ALOGE("Failed to start the looper");
315        AMediaCodec_delete(mData);
316        return NULL;
317    }
318    if (name_is_type) {
319        mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder);
320    } else {
321        mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
322    }
323    if (mData->mCodec == NULL) {  // failed to create codec
324        AMediaCodec_delete(mData);
325        return NULL;
326    }
327    mData->mHandler = new CodecHandler(mData);
328    mData->mLooper->registerHandler(mData->mHandler);
329    mData->mGeneration = 1;
330    mData->mRequestedActivityNotification = false;
331    mData->mCallback = NULL;
332
333    mData->mAsyncCallback = {};
334    mData->mAsyncCallbackUserData = NULL;
335
336    return mData;
337}
338
339EXPORT
340AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
341    return createAMediaCodec(name, false, false);
342}
343
344EXPORT
345AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
346    return createAMediaCodec(mime_type, true, false);
347}
348
349EXPORT
350AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
351    return createAMediaCodec(name, true, true);
352}
353
354EXPORT
355media_status_t AMediaCodec_delete(AMediaCodec *mData) {
356    if (mData != NULL) {
357        if (mData->mCodec != NULL) {
358            mData->mCodec->release();
359            mData->mCodec.clear();
360        }
361
362        if (mData->mLooper != NULL) {
363            if (mData->mHandler != NULL) {
364                mData->mLooper->unregisterHandler(mData->mHandler->id());
365            }
366            mData->mLooper->stop();
367            mData->mLooper.clear();
368        }
369        delete mData;
370    }
371    return AMEDIA_OK;
372}
373
374EXPORT
375media_status_t AMediaCodec_getName(
376        AMediaCodec *mData,
377        char** out_name) {
378    if (out_name == NULL) {
379        return AMEDIA_ERROR_INVALID_PARAMETER;
380    }
381
382    AString compName;
383    status_t err = mData->mCodec->getName(&compName);
384    if (err != OK) {
385        return translate_error(err);
386    }
387    *out_name = strdup(compName.c_str());
388    return AMEDIA_OK;
389}
390
391EXPORT
392void AMediaCodec_releaseName(
393        AMediaCodec * /* mData */,
394        char* name) {
395    if (name != NULL) {
396        free(name);
397    }
398}
399
400EXPORT
401media_status_t AMediaCodec_configure(
402        AMediaCodec *mData,
403        const AMediaFormat* format,
404        ANativeWindow* window,
405        AMediaCrypto *crypto,
406        uint32_t flags) {
407    sp<AMessage> nativeFormat;
408    AMediaFormat_getFormat(format, &nativeFormat);
409    ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str());
410    sp<Surface> surface = NULL;
411    if (window != NULL) {
412        surface = (Surface*) window;
413    }
414
415    status_t err = mData->mCodec->configure(nativeFormat, surface,
416            crypto ? crypto->mCrypto : NULL, flags);
417    if (err != OK) {
418        ALOGE("configure: err(%d), failed with format: %s",
419              err, nativeFormat->debugString(0).c_str());
420    }
421    return translate_error(err);
422}
423
424EXPORT
425media_status_t AMediaCodec_setAsyncNotifyCallback(
426        AMediaCodec *mData,
427        AMediaCodecOnAsyncNotifyCallback callback,
428        void *userdata) {
429    if (mData->mAsyncNotify == NULL && userdata != NULL) {
430        mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
431        status_t err = mData->mCodec->setCallback(mData->mAsyncNotify);
432        if (err != OK) {
433            ALOGE("setAsyncNotifyCallback: err(%d), failed to set async callback", err);
434            return translate_error(err);
435        }
436    }
437
438    Mutex::Autolock _l(mData->mAsyncCallbackLock);
439    mData->mAsyncCallback = callback;
440    mData->mAsyncCallbackUserData = userdata;
441
442    return AMEDIA_OK;
443}
444
445
446EXPORT
447media_status_t AMediaCodec_releaseCrypto(AMediaCodec *mData) {
448    return translate_error(mData->mCodec->releaseCrypto());
449}
450
451EXPORT
452media_status_t AMediaCodec_start(AMediaCodec *mData) {
453    status_t ret =  mData->mCodec->start();
454    if (ret != OK) {
455        return translate_error(ret);
456    }
457    mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler);
458    mData->mActivityNotification->setInt32("generation", mData->mGeneration);
459    requestActivityNotification(mData);
460    return AMEDIA_OK;
461}
462
463EXPORT
464media_status_t AMediaCodec_stop(AMediaCodec *mData) {
465    media_status_t ret = translate_error(mData->mCodec->stop());
466
467    sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler);
468    sp<AMessage> response;
469    msg->postAndAwaitResponse(&response);
470    mData->mActivityNotification.clear();
471
472    return ret;
473}
474
475EXPORT
476media_status_t AMediaCodec_flush(AMediaCodec *mData) {
477    return translate_error(mData->mCodec->flush());
478}
479
480EXPORT
481ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
482    size_t idx;
483    status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs);
484    requestActivityNotification(mData);
485    if (ret == OK) {
486        return idx;
487    }
488    return translate_error(ret);
489}
490
491EXPORT
492uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
493    if (mData->mAsyncNotify != NULL) {
494        // Asynchronous mode
495        sp<MediaCodecBuffer> abuf;
496        if (mData->mCodec->getInputBuffer(idx, &abuf) != 0) {
497            return NULL;
498        }
499
500        if (out_size != NULL) {
501            *out_size = abuf->capacity();
502        }
503        return abuf->data();
504    }
505
506    android::Vector<android::sp<android::MediaCodecBuffer> > abufs;
507    if (mData->mCodec->getInputBuffers(&abufs) == 0) {
508        size_t n = abufs.size();
509        if (idx >= n) {
510            ALOGE("buffer index %zu out of range", idx);
511            return NULL;
512        }
513        if (abufs[idx] == NULL) {
514            ALOGE("buffer index %zu is NULL", idx);
515            return NULL;
516        }
517        if (out_size != NULL) {
518            *out_size = abufs[idx]->capacity();
519        }
520        return abufs[idx]->data();
521    }
522    ALOGE("couldn't get input buffers");
523    return NULL;
524}
525
526EXPORT
527uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
528    if (mData->mAsyncNotify != NULL) {
529        // Asynchronous mode
530        sp<MediaCodecBuffer> abuf;
531        if (mData->mCodec->getOutputBuffer(idx, &abuf) != 0) {
532            return NULL;
533        }
534
535        if (out_size != NULL) {
536            *out_size = abuf->capacity();
537        }
538        return abuf->data();
539    }
540
541    android::Vector<android::sp<android::MediaCodecBuffer> > abufs;
542    if (mData->mCodec->getOutputBuffers(&abufs) == 0) {
543        size_t n = abufs.size();
544        if (idx >= n) {
545            ALOGE("buffer index %zu out of range", idx);
546            return NULL;
547        }
548        if (out_size != NULL) {
549            *out_size = abufs[idx]->capacity();
550        }
551        return abufs[idx]->data();
552    }
553    ALOGE("couldn't get output buffers");
554    return NULL;
555}
556
557EXPORT
558media_status_t AMediaCodec_queueInputBuffer(AMediaCodec *mData,
559        size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
560
561    AString errorMsg;
562    status_t ret = mData->mCodec->queueInputBuffer(idx, offset, size, time, flags, &errorMsg);
563    return translate_error(ret);
564}
565
566EXPORT
567ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
568        AMediaCodecBufferInfo *info, int64_t timeoutUs) {
569    size_t idx;
570    size_t offset;
571    size_t size;
572    uint32_t flags;
573    int64_t presentationTimeUs;
574    status_t ret = mData->mCodec->dequeueOutputBuffer(&idx, &offset, &size, &presentationTimeUs,
575            &flags, timeoutUs);
576    requestActivityNotification(mData);
577    switch (ret) {
578        case OK:
579            info->offset = offset;
580            info->size = size;
581            info->flags = flags;
582            info->presentationTimeUs = presentationTimeUs;
583            return idx;
584        case -EAGAIN:
585            return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
586        case android::INFO_FORMAT_CHANGED:
587            return AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED;
588        case INFO_OUTPUT_BUFFERS_CHANGED:
589            return AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED;
590        default:
591            break;
592    }
593    return translate_error(ret);
594}
595
596EXPORT
597AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) {
598    sp<AMessage> format;
599    mData->mCodec->getOutputFormat(&format);
600    return AMediaFormat_fromMsg(&format);
601}
602
603EXPORT
604AMediaFormat* AMediaCodec_getInputFormat(AMediaCodec *mData) {
605    sp<AMessage> format;
606    mData->mCodec->getInputFormat(&format);
607    return AMediaFormat_fromMsg(&format);
608}
609
610EXPORT
611AMediaFormat* AMediaCodec_getBufferFormat(AMediaCodec *mData, size_t index) {
612    sp<AMessage> format;
613    mData->mCodec->getOutputFormat(index, &format);
614    return AMediaFormat_fromMsg(&format);
615}
616
617EXPORT
618media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
619    if (render) {
620        return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
621    } else {
622        return translate_error(mData->mCodec->releaseOutputBuffer(idx));
623    }
624}
625
626EXPORT
627media_status_t AMediaCodec_releaseOutputBufferAtTime(
628        AMediaCodec *mData, size_t idx, int64_t timestampNs) {
629    ALOGV("render @ %" PRId64, timestampNs);
630    return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
631}
632
633EXPORT
634media_status_t AMediaCodec_setOutputSurface(AMediaCodec *mData, ANativeWindow* window) {
635    sp<Surface> surface = NULL;
636    if (window != NULL) {
637        surface = (Surface*) window;
638    }
639    return translate_error(mData->mCodec->setSurface(surface));
640}
641
642EXPORT
643media_status_t AMediaCodec_createInputSurface(AMediaCodec *mData, ANativeWindow **surface) {
644    if (surface == NULL || mData == NULL) {
645        return AMEDIA_ERROR_INVALID_PARAMETER;
646    }
647    *surface = NULL;
648
649    sp<IGraphicBufferProducer> igbp = NULL;
650    status_t err = mData->mCodec->createInputSurface(&igbp);
651    if (err != NO_ERROR) {
652        return translate_error(err);
653    }
654
655    *surface = new Surface(igbp);
656    ANativeWindow_acquire(*surface);
657    return AMEDIA_OK;
658}
659
660EXPORT
661media_status_t AMediaCodec_createPersistentInputSurface(ANativeWindow **surface) {
662    if (surface == NULL) {
663        return AMEDIA_ERROR_INVALID_PARAMETER;
664    }
665    *surface = NULL;
666
667    sp<PersistentSurface> ps = MediaCodec::CreatePersistentInputSurface();
668    if (ps == NULL) {
669        return AMEDIA_ERROR_UNKNOWN;
670    }
671
672    sp<IGraphicBufferProducer> igbp = ps->getBufferProducer();
673    if (igbp == NULL) {
674        return AMEDIA_ERROR_UNKNOWN;
675    }
676
677    *surface = new AMediaCodecPersistentSurface(igbp, ps);
678    ANativeWindow_acquire(*surface);
679
680    return AMEDIA_OK;
681}
682
683EXPORT
684media_status_t AMediaCodec_setInputSurface(
685        AMediaCodec *mData, ANativeWindow *surface) {
686
687    if (surface == NULL || mData == NULL) {
688        return AMEDIA_ERROR_INVALID_PARAMETER;
689    }
690
691    AMediaCodecPersistentSurface *aMediaPersistentSurface =
692            static_cast<AMediaCodecPersistentSurface *>(surface);
693    if (aMediaPersistentSurface->mPersistentSurface == NULL) {
694        return AMEDIA_ERROR_INVALID_PARAMETER;
695    }
696
697    return translate_error(mData->mCodec->setInputSurface(
698            aMediaPersistentSurface->mPersistentSurface));
699}
700
701EXPORT
702media_status_t AMediaCodec_setParameters(
703        AMediaCodec *mData, const AMediaFormat* params) {
704    if (params == NULL || mData == NULL) {
705        return AMEDIA_ERROR_INVALID_PARAMETER;
706    }
707    sp<AMessage> nativeParams;
708    AMediaFormat_getFormat(params, &nativeParams);
709    ALOGV("setParameters: %s", nativeParams->debugString(0).c_str());
710
711    return translate_error(mData->mCodec->setParameters(nativeParams));
712}
713
714EXPORT
715media_status_t AMediaCodec_signalEndOfInputStream(AMediaCodec *mData) {
716
717    if (mData == NULL) {
718        return AMEDIA_ERROR_INVALID_PARAMETER;
719    }
720
721    status_t err = mData->mCodec->signalEndOfInputStream();
722    if (err == INVALID_OPERATION) {
723        return AMEDIA_ERROR_INVALID_OPERATION;
724    }
725
726    return translate_error(err);
727
728}
729
730//EXPORT
731media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback,
732        void *userdata) {
733    mData->mCallback = callback;
734    mData->mCallbackUserData = userdata;
735    return AMEDIA_OK;
736}
737
738typedef struct AMediaCodecCryptoInfo {
739        int numsubsamples;
740        uint8_t key[16];
741        uint8_t iv[16];
742        cryptoinfo_mode_t mode;
743        cryptoinfo_pattern_t pattern;
744        size_t *clearbytes;
745        size_t *encryptedbytes;
746} AMediaCodecCryptoInfo;
747
748EXPORT
749media_status_t AMediaCodec_queueSecureInputBuffer(
750        AMediaCodec* codec,
751        size_t idx,
752        off_t offset,
753        AMediaCodecCryptoInfo* crypto,
754        uint64_t time,
755        uint32_t flags) {
756
757    CryptoPlugin::SubSample *subSamples = new CryptoPlugin::SubSample[crypto->numsubsamples];
758    for (int i = 0; i < crypto->numsubsamples; i++) {
759        subSamples[i].mNumBytesOfClearData = crypto->clearbytes[i];
760        subSamples[i].mNumBytesOfEncryptedData = crypto->encryptedbytes[i];
761    }
762
763    CryptoPlugin::Pattern pattern;
764    pattern.mEncryptBlocks = crypto->pattern.encryptBlocks;
765    pattern.mSkipBlocks = crypto->pattern.skipBlocks;
766
767    AString errormsg;
768    status_t err  = codec->mCodec->queueSecureInputBuffer(idx,
769            offset,
770            subSamples,
771            crypto->numsubsamples,
772            crypto->key,
773            crypto->iv,
774            (CryptoPlugin::Mode)crypto->mode,
775            pattern,
776            time,
777            flags,
778            &errormsg);
779    if (err != 0) {
780        ALOGE("queSecureInputBuffer: %s", errormsg.c_str());
781    }
782    delete [] subSamples;
783    return translate_error(err);
784}
785
786EXPORT
787bool AMediaCodecActionCode_isRecoverable(int32_t actionCode) {
788    return (actionCode == ACTION_CODE_RECOVERABLE);
789}
790
791EXPORT
792bool AMediaCodecActionCode_isTransient(int32_t actionCode) {
793    return (actionCode == ACTION_CODE_TRANSIENT);
794}
795
796
797EXPORT
798void AMediaCodecCryptoInfo_setPattern(AMediaCodecCryptoInfo *info,
799        cryptoinfo_pattern_t *pattern) {
800    info->pattern.encryptBlocks = pattern->encryptBlocks;
801    info->pattern.skipBlocks = pattern->skipBlocks;
802}
803
804EXPORT
805AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
806        int numsubsamples,
807        uint8_t key[16],
808        uint8_t iv[16],
809        cryptoinfo_mode_t mode,
810        size_t *clearbytes,
811        size_t *encryptedbytes) {
812
813    // size needed to store all the crypto data
814    size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
815    AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
816    if (!ret) {
817        ALOGE("couldn't allocate %zu bytes", cryptosize);
818        return NULL;
819    }
820    ret->numsubsamples = numsubsamples;
821    memcpy(ret->key, key, 16);
822    memcpy(ret->iv, iv, 16);
823    ret->mode = mode;
824    ret->pattern.encryptBlocks = 0;
825    ret->pattern.skipBlocks = 0;
826
827    // clearbytes and encryptedbytes point at the actual data, which follows
828    ret->clearbytes = (size_t*) (ret + 1); // point immediately after the struct
829    ret->encryptedbytes = ret->clearbytes + numsubsamples; // point after the clear sizes
830
831    memcpy(ret->clearbytes, clearbytes, numsubsamples * sizeof(size_t));
832    memcpy(ret->encryptedbytes, encryptedbytes, numsubsamples * sizeof(size_t));
833
834    return ret;
835}
836
837
838EXPORT
839media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
840    free(info);
841    return AMEDIA_OK;
842}
843
844EXPORT
845size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo* ci) {
846    return ci->numsubsamples;
847}
848
849EXPORT
850media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
851    if (!ci) {
852        return AMEDIA_ERROR_INVALID_OBJECT;
853    }
854    if (!dst) {
855        return AMEDIA_ERROR_INVALID_PARAMETER;
856    }
857    memcpy(dst, ci->key, 16);
858    return AMEDIA_OK;
859}
860
861EXPORT
862media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
863    if (!ci) {
864        return AMEDIA_ERROR_INVALID_OBJECT;
865    }
866    if (!dst) {
867        return AMEDIA_ERROR_INVALID_PARAMETER;
868    }
869    memcpy(dst, ci->iv, 16);
870    return AMEDIA_OK;
871}
872
873EXPORT
874cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
875    if (!ci) {
876        return (cryptoinfo_mode_t) AMEDIA_ERROR_INVALID_OBJECT;
877    }
878    return ci->mode;
879}
880
881EXPORT
882media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
883    if (!ci) {
884        return AMEDIA_ERROR_INVALID_OBJECT;
885    }
886    if (!dst) {
887        return AMEDIA_ERROR_INVALID_PARAMETER;
888    }
889    memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
890    return AMEDIA_OK;
891}
892
893EXPORT
894media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
895    if (!ci) {
896        return AMEDIA_ERROR_INVALID_OBJECT;
897    }
898    if (!dst) {
899        return AMEDIA_ERROR_INVALID_PARAMETER;
900    }
901    memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
902    return AMEDIA_OK;
903}
904
905} // extern "C"
906
907