Home | History | Annotate | Download | only in ndk
      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 "NdkMediaCodec.h"
     23 #include "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 #include <media/stagefright/foundation/ABuffer.h>
     34 
     35 #include <media/stagefright/MediaCodec.h>
     36 #include <media/stagefright/MediaErrors.h>
     37 
     38 using namespace android;
     39 
     40 
     41 static media_status_t translate_error(status_t err) {
     42     if (err == OK) {
     43         return AMEDIA_OK;
     44     } else if (err == -EAGAIN) {
     45         return (media_status_t) AMEDIACODEC_INFO_TRY_AGAIN_LATER;
     46     }
     47     ALOGE("sf error code: %d", err);
     48     return AMEDIA_ERROR_UNKNOWN;
     49 }
     50 
     51 enum {
     52     kWhatActivityNotify,
     53     kWhatRequestActivityNotifications,
     54     kWhatStopActivityNotifications,
     55 };
     56 
     57 
     58 class CodecHandler: public AHandler {
     59 private:
     60     AMediaCodec* mCodec;
     61 public:
     62     CodecHandler(AMediaCodec *codec);
     63     virtual void onMessageReceived(const sp<AMessage> &msg);
     64 };
     65 
     66 typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
     67 
     68 struct AMediaCodec {
     69     sp<android::MediaCodec> mCodec;
     70     sp<ALooper> mLooper;
     71     sp<CodecHandler> mHandler;
     72     sp<AMessage> mActivityNotification;
     73     int32_t mGeneration;
     74     bool mRequestedActivityNotification;
     75     OnCodecEvent mCallback;
     76     void *mCallbackUserData;
     77 };
     78 
     79 CodecHandler::CodecHandler(AMediaCodec *codec) {
     80     mCodec = codec;
     81 }
     82 
     83 void CodecHandler::onMessageReceived(const sp<AMessage> &msg) {
     84 
     85     switch (msg->what()) {
     86         case kWhatRequestActivityNotifications:
     87         {
     88             if (mCodec->mRequestedActivityNotification) {
     89                 break;
     90             }
     91 
     92             mCodec->mCodec->requestActivityNotification(mCodec->mActivityNotification);
     93             mCodec->mRequestedActivityNotification = true;
     94             break;
     95         }
     96 
     97         case kWhatActivityNotify:
     98         {
     99             {
    100                 int32_t generation;
    101                 msg->findInt32("generation", &generation);
    102 
    103                 if (generation != mCodec->mGeneration) {
    104                     // stale
    105                     break;
    106                 }
    107 
    108                 mCodec->mRequestedActivityNotification = false;
    109             }
    110 
    111             if (mCodec->mCallback) {
    112                 mCodec->mCallback(mCodec, mCodec->mCallbackUserData);
    113             }
    114             break;
    115         }
    116 
    117         case kWhatStopActivityNotifications:
    118         {
    119             uint32_t replyID;
    120             msg->senderAwaitsResponse(&replyID);
    121 
    122             mCodec->mGeneration++;
    123             mCodec->mRequestedActivityNotification = false;
    124 
    125             sp<AMessage> response = new AMessage;
    126             response->postReply(replyID);
    127             break;
    128         }
    129 
    130         default:
    131             ALOGE("shouldn't be here");
    132             break;
    133     }
    134 
    135 }
    136 
    137 
    138 static void requestActivityNotification(AMediaCodec *codec) {
    139     (new AMessage(kWhatRequestActivityNotifications, codec->mHandler->id()))->post();
    140 }
    141 
    142 extern "C" {
    143 
    144 static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) {
    145     AMediaCodec *mData = new AMediaCodec();
    146     mData->mLooper = new ALooper;
    147     mData->mLooper->setName("NDK MediaCodec_looper");
    148     status_t ret = mData->mLooper->start(
    149             false,      // runOnCallingThread
    150             true,       // canCallJava XXX
    151             PRIORITY_FOREGROUND);
    152     if (name_is_type) {
    153         mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder);
    154     } else {
    155         mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
    156     }
    157     mData->mHandler = new CodecHandler(mData);
    158     mData->mLooper->registerHandler(mData->mHandler);
    159     mData->mGeneration = 1;
    160     mData->mRequestedActivityNotification = false;
    161     mData->mCallback = NULL;
    162 
    163     return mData;
    164 }
    165 
    166 EXPORT
    167 AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
    168     return createAMediaCodec(name, false, false);
    169 }
    170 
    171 EXPORT
    172 AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
    173     return createAMediaCodec(mime_type, true, false);
    174 }
    175 
    176 EXPORT
    177 AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
    178     return createAMediaCodec(name, true, true);
    179 }
    180 
    181 EXPORT
    182 media_status_t AMediaCodec_delete(AMediaCodec *mData) {
    183     if (mData->mCodec != NULL) {
    184         mData->mCodec->release();
    185         mData->mCodec.clear();
    186     }
    187 
    188     if (mData->mLooper != NULL) {
    189         mData->mLooper->unregisterHandler(mData->mHandler->id());
    190         mData->mLooper->stop();
    191         mData->mLooper.clear();
    192     }
    193     delete mData;
    194     return AMEDIA_OK;
    195 }
    196 
    197 EXPORT
    198 media_status_t AMediaCodec_configure(
    199         AMediaCodec *mData,
    200         const AMediaFormat* format,
    201         ANativeWindow* window,
    202         AMediaCrypto *crypto,
    203         uint32_t flags) {
    204     sp<AMessage> nativeFormat;
    205     AMediaFormat_getFormat(format, &nativeFormat);
    206     ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str());
    207     sp<Surface> surface = NULL;
    208     if (window != NULL) {
    209         surface = (Surface*) window;
    210     }
    211 
    212     return translate_error(mData->mCodec->configure(nativeFormat, surface,
    213             crypto ? crypto->mCrypto : NULL, flags));
    214 }
    215 
    216 EXPORT
    217 media_status_t AMediaCodec_start(AMediaCodec *mData) {
    218     status_t ret =  mData->mCodec->start();
    219     if (ret != OK) {
    220         return translate_error(ret);
    221     }
    222     mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler->id());
    223     mData->mActivityNotification->setInt32("generation", mData->mGeneration);
    224     requestActivityNotification(mData);
    225     return AMEDIA_OK;
    226 }
    227 
    228 EXPORT
    229 media_status_t AMediaCodec_stop(AMediaCodec *mData) {
    230     media_status_t ret = translate_error(mData->mCodec->stop());
    231 
    232     sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler->id());
    233     sp<AMessage> response;
    234     msg->postAndAwaitResponse(&response);
    235     mData->mActivityNotification.clear();
    236 
    237     return ret;
    238 }
    239 
    240 EXPORT
    241 media_status_t AMediaCodec_flush(AMediaCodec *mData) {
    242     return translate_error(mData->mCodec->flush());
    243 }
    244 
    245 EXPORT
    246 ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
    247     size_t idx;
    248     status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs);
    249     requestActivityNotification(mData);
    250     if (ret == OK) {
    251         return idx;
    252     }
    253     return translate_error(ret);
    254 }
    255 
    256 EXPORT
    257 uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
    258     android::Vector<android::sp<android::ABuffer> > abufs;
    259     if (mData->mCodec->getInputBuffers(&abufs) == 0) {
    260         size_t n = abufs.size();
    261         if (idx >= n) {
    262             ALOGE("buffer index %zu out of range", idx);
    263             return NULL;
    264         }
    265         if (out_size != NULL) {
    266             *out_size = abufs[idx]->capacity();
    267         }
    268         return abufs[idx]->data();
    269     }
    270     ALOGE("couldn't get input buffers");
    271     return NULL;
    272 }
    273 
    274 EXPORT
    275 uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
    276     android::Vector<android::sp<android::ABuffer> > abufs;
    277     if (mData->mCodec->getOutputBuffers(&abufs) == 0) {
    278         size_t n = abufs.size();
    279         if (idx >= n) {
    280             ALOGE("buffer index %zu out of range", idx);
    281             return NULL;
    282         }
    283         if (out_size != NULL) {
    284             *out_size = abufs[idx]->capacity();
    285         }
    286         return abufs[idx]->data();
    287     }
    288     ALOGE("couldn't get output buffers");
    289     return NULL;
    290 }
    291 
    292 EXPORT
    293 media_status_t AMediaCodec_queueInputBuffer(AMediaCodec *mData,
    294         size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
    295 
    296     AString errorMsg;
    297     status_t ret = mData->mCodec->queueInputBuffer(idx, offset, size, time, flags, &errorMsg);
    298     return translate_error(ret);
    299 }
    300 
    301 EXPORT
    302 ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
    303         AMediaCodecBufferInfo *info, int64_t timeoutUs) {
    304     size_t idx;
    305     size_t offset;
    306     size_t size;
    307     uint32_t flags;
    308     int64_t presentationTimeUs;
    309     status_t ret = mData->mCodec->dequeueOutputBuffer(&idx, &offset, &size, &presentationTimeUs,
    310             &flags, timeoutUs);
    311     requestActivityNotification(mData);
    312     switch (ret) {
    313         case OK:
    314             info->offset = offset;
    315             info->size = size;
    316             info->flags = flags;
    317             info->presentationTimeUs = presentationTimeUs;
    318             return idx;
    319         case -EAGAIN:
    320             return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
    321         case android::INFO_FORMAT_CHANGED:
    322             return AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED;
    323         case INFO_OUTPUT_BUFFERS_CHANGED:
    324             return AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED;
    325         default:
    326             break;
    327     }
    328     return translate_error(ret);
    329 }
    330 
    331 EXPORT
    332 AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) {
    333     sp<AMessage> format;
    334     mData->mCodec->getOutputFormat(&format);
    335     return AMediaFormat_fromMsg(&format);
    336 }
    337 
    338 EXPORT
    339 media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
    340     if (render) {
    341         return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
    342     } else {
    343         return translate_error(mData->mCodec->releaseOutputBuffer(idx));
    344     }
    345 }
    346 
    347 EXPORT
    348 media_status_t AMediaCodec_releaseOutputBufferAtTime(
    349         AMediaCodec *mData, size_t idx, int64_t timestampNs) {
    350     ALOGV("render @ %" PRId64, timestampNs);
    351     return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
    352 }
    353 
    354 //EXPORT
    355 media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
    356     mData->mCallback = callback;
    357     mData->mCallbackUserData = userdata;
    358     return AMEDIA_OK;
    359 }
    360 
    361 typedef struct AMediaCodecCryptoInfo {
    362         int numsubsamples;
    363         uint8_t key[16];
    364         uint8_t iv[16];
    365         cryptoinfo_mode_t mode;
    366         size_t *clearbytes;
    367         size_t *encryptedbytes;
    368 } AMediaCodecCryptoInfo;
    369 
    370 EXPORT
    371 media_status_t AMediaCodec_queueSecureInputBuffer(
    372         AMediaCodec* codec,
    373         size_t idx,
    374         off_t offset,
    375         AMediaCodecCryptoInfo* crypto,
    376         uint64_t time,
    377         uint32_t flags) {
    378 
    379     CryptoPlugin::SubSample *subSamples = new CryptoPlugin::SubSample[crypto->numsubsamples];
    380     for (int i = 0; i < crypto->numsubsamples; i++) {
    381         subSamples[i].mNumBytesOfClearData = crypto->clearbytes[i];
    382         subSamples[i].mNumBytesOfEncryptedData = crypto->encryptedbytes[i];
    383     }
    384 
    385     AString errormsg;
    386     status_t err  = codec->mCodec->queueSecureInputBuffer(idx,
    387             offset,
    388             subSamples,
    389             crypto->numsubsamples,
    390             crypto->key,
    391             crypto->iv,
    392             (CryptoPlugin::Mode) crypto->mode,
    393             time,
    394             flags,
    395             &errormsg);
    396     if (err != 0) {
    397         ALOGE("queSecureInputBuffer: %s", errormsg.c_str());
    398     }
    399     delete [] subSamples;
    400     return translate_error(err);
    401 }
    402 
    403 
    404 
    405 EXPORT
    406 AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
    407         int numsubsamples,
    408         uint8_t key[16],
    409         uint8_t iv[16],
    410         cryptoinfo_mode_t mode,
    411         size_t *clearbytes,
    412         size_t *encryptedbytes) {
    413 
    414     // size needed to store all the crypto data
    415     size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
    416     AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
    417     if (!ret) {
    418         ALOGE("couldn't allocate %zu bytes", cryptosize);
    419         return NULL;
    420     }
    421     ret->numsubsamples = numsubsamples;
    422     memcpy(ret->key, key, 16);
    423     memcpy(ret->iv, iv, 16);
    424     ret->mode = mode;
    425 
    426     // clearbytes and encryptedbytes point at the actual data, which follows
    427     ret->clearbytes = (size_t*) (ret + 1); // point immediately after the struct
    428     ret->encryptedbytes = ret->clearbytes + numsubsamples; // point after the clear sizes
    429 
    430     memcpy(ret->clearbytes, clearbytes, numsubsamples * sizeof(size_t));
    431     memcpy(ret->encryptedbytes, encryptedbytes, numsubsamples * sizeof(size_t));
    432 
    433     return ret;
    434 }
    435 
    436 
    437 EXPORT
    438 media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
    439     free(info);
    440     return AMEDIA_OK;
    441 }
    442 
    443 EXPORT
    444 size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo* ci) {
    445     return ci->numsubsamples;
    446 }
    447 
    448 EXPORT
    449 media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
    450     if (!ci) {
    451         return AMEDIA_ERROR_INVALID_OBJECT;
    452     }
    453     if (!dst) {
    454         return AMEDIA_ERROR_INVALID_PARAMETER;
    455     }
    456     memcpy(dst, ci->key, 16);
    457     return AMEDIA_OK;
    458 }
    459 
    460 EXPORT
    461 media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
    462     if (!ci) {
    463         return AMEDIA_ERROR_INVALID_OBJECT;
    464     }
    465     if (!dst) {
    466         return AMEDIA_ERROR_INVALID_PARAMETER;
    467     }
    468     memcpy(dst, ci->iv, 16);
    469     return AMEDIA_OK;
    470 }
    471 
    472 EXPORT
    473 cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
    474     if (!ci) {
    475         return (cryptoinfo_mode_t) AMEDIA_ERROR_INVALID_OBJECT;
    476     }
    477     return ci->mode;
    478 }
    479 
    480 EXPORT
    481 media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
    482     if (!ci) {
    483         return AMEDIA_ERROR_INVALID_OBJECT;
    484     }
    485     if (!dst) {
    486         return AMEDIA_ERROR_INVALID_PARAMETER;
    487     }
    488     memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
    489     return AMEDIA_OK;
    490 }
    491 
    492 EXPORT
    493 media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
    494     if (!ci) {
    495         return AMEDIA_ERROR_INVALID_OBJECT;
    496     }
    497     if (!dst) {
    498         return AMEDIA_ERROR_INVALID_PARAMETER;
    499     }
    500     memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
    501     return AMEDIA_OK;
    502 }
    503 
    504 } // extern "C"
    505 
    506