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 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "NdkMediaExtractor"
     19 
     20 
     21 #include "NdkMediaError.h"
     22 #include "NdkMediaExtractor.h"
     23 #include "NdkMediaFormatPriv.h"
     24 
     25 
     26 #include <utils/Log.h>
     27 #include <utils/StrongPointer.h>
     28 #include <media/hardware/CryptoAPI.h>
     29 #include <media/stagefright/foundation/ABuffer.h>
     30 #include <media/stagefright/foundation/AMessage.h>
     31 #include <media/stagefright/MetaData.h>
     32 #include <media/stagefright/NuMediaExtractor.h>
     33 #include <media/IMediaHTTPService.h>
     34 #include <android_runtime/AndroidRuntime.h>
     35 #include <android_util_Binder.h>
     36 
     37 #include <jni.h>
     38 
     39 using namespace android;
     40 
     41 static media_status_t translate_error(status_t err) {
     42     if (err == OK) {
     43         return AMEDIA_OK;
     44     }
     45     ALOGE("sf error code: %d", err);
     46     return AMEDIA_ERROR_UNKNOWN;
     47 }
     48 
     49 struct AMediaExtractor {
     50     sp<NuMediaExtractor> mImpl;
     51     sp<ABuffer> mPsshBuf;
     52 
     53 };
     54 
     55 extern "C" {
     56 
     57 EXPORT
     58 AMediaExtractor* AMediaExtractor_new() {
     59     ALOGV("ctor");
     60     AMediaExtractor *mData = new AMediaExtractor();
     61     mData->mImpl = new NuMediaExtractor();
     62     return mData;
     63 }
     64 
     65 EXPORT
     66 media_status_t AMediaExtractor_delete(AMediaExtractor *mData) {
     67     ALOGV("dtor");
     68     delete mData;
     69     return AMEDIA_OK;
     70 }
     71 
     72 EXPORT
     73 media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset,
     74         off64_t length) {
     75     ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
     76     return translate_error(mData->mImpl->setDataSource(fd, offset, length));
     77 }
     78 
     79 EXPORT
     80 media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
     81     ALOGV("setDataSource(%s)", location);
     82     // TODO: add header support
     83 
     84     JNIEnv *env = AndroidRuntime::getJNIEnv();
     85     jobject service = NULL;
     86     if (env == NULL) {
     87         ALOGE("setDataSource(path) must be called from Java thread");
     88         env->ExceptionClear();
     89         return AMEDIA_ERROR_UNSUPPORTED;
     90     }
     91 
     92     jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService");
     93     if (mediahttpclass == NULL) {
     94         ALOGE("can't find MediaHttpService");
     95         env->ExceptionClear();
     96         return AMEDIA_ERROR_UNSUPPORTED;
     97     }
     98 
     99     jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass,
    100             "createHttpServiceBinderIfNecessary", "(Ljava/lang/String;)Landroid/os/IBinder;");
    101     if (mediaHttpCreateMethod == NULL) {
    102         ALOGE("can't find method");
    103         env->ExceptionClear();
    104         return AMEDIA_ERROR_UNSUPPORTED;
    105     }
    106 
    107     jstring jloc = env->NewStringUTF(location);
    108 
    109     service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc);
    110     env->DeleteLocalRef(jloc);
    111 
    112     sp<IMediaHTTPService> httpService;
    113     if (service != NULL) {
    114         sp<IBinder> binder = ibinderForJavaObject(env, service);
    115         httpService = interface_cast<IMediaHTTPService>(binder);
    116     }
    117 
    118     status_t err = mData->mImpl->setDataSource(httpService, location, NULL);
    119     env->ExceptionClear();
    120     return translate_error(err);
    121 }
    122 
    123 EXPORT
    124 size_t AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
    125     return mData->mImpl->countTracks();
    126 }
    127 
    128 EXPORT
    129 AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) {
    130     sp<AMessage> format;
    131     mData->mImpl->getTrackFormat(idx, &format);
    132     return AMediaFormat_fromMsg(&format);
    133 }
    134 
    135 EXPORT
    136 media_status_t AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
    137     ALOGV("selectTrack(%zu)", idx);
    138     return translate_error(mData->mImpl->selectTrack(idx));
    139 }
    140 
    141 EXPORT
    142 media_status_t AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
    143     ALOGV("unselectTrack(%zu)", idx);
    144     return translate_error(mData->mImpl->unselectTrack(idx));
    145 }
    146 
    147 EXPORT
    148 bool AMediaExtractor_advance(AMediaExtractor *mData) {
    149     //ALOGV("advance");
    150     return mData->mImpl->advance();
    151 }
    152 
    153 EXPORT
    154 media_status_t AMediaExtractor_seekTo(AMediaExtractor *ex, int64_t seekPosUs, SeekMode mode) {
    155     android::MediaSource::ReadOptions::SeekMode sfmode;
    156     if (mode == AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC) {
    157         sfmode = android::MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
    158     } else if (mode == AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC) {
    159         sfmode = android::MediaSource::ReadOptions::SEEK_CLOSEST_SYNC;
    160     } else {
    161         sfmode = android::MediaSource::ReadOptions::SEEK_NEXT_SYNC;
    162     }
    163 
    164     return translate_error(ex->mImpl->seekTo(seekPosUs, sfmode));
    165 }
    166 
    167 EXPORT
    168 ssize_t AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
    169     //ALOGV("readSampleData");
    170     sp<ABuffer> tmp = new ABuffer(buffer, capacity);
    171     if (mData->mImpl->readSampleData(tmp) == OK) {
    172         return tmp->size();
    173     }
    174     return -1;
    175 }
    176 
    177 EXPORT
    178 uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
    179     int sampleFlags = 0;
    180     sp<MetaData> meta;
    181     status_t err = mData->mImpl->getSampleMeta(&meta);
    182     if (err != OK) {
    183         return -1;
    184     }
    185     int32_t val;
    186     if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
    187         sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC;
    188     }
    189 
    190     uint32_t type;
    191     const void *data;
    192     size_t size;
    193     if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
    194         sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED;
    195     }
    196     return sampleFlags;
    197 }
    198 
    199 EXPORT
    200 int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) {
    201     size_t idx;
    202     if (mData->mImpl->getSampleTrackIndex(&idx) != OK) {
    203         return -1;
    204     }
    205     return idx;
    206 }
    207 
    208 EXPORT
    209 int64_t AMediaExtractor_getSampleTime(AMediaExtractor *mData) {
    210     int64_t time;
    211     if (mData->mImpl->getSampleTime(&time) != OK) {
    212         return -1;
    213     }
    214     return time;
    215 }
    216 
    217 EXPORT
    218 PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) {
    219 
    220     if (ex->mPsshBuf != NULL) {
    221         return (PsshInfo*) ex->mPsshBuf->data();
    222     }
    223 
    224     sp<AMessage> format;
    225     ex->mImpl->getFileFormat(&format);
    226     sp<ABuffer> buffer;
    227     if(!format->findBuffer("pssh", &buffer)) {
    228         return NULL;
    229     }
    230 
    231     // the format of the buffer is 1 or more of:
    232     //    {
    233     //        16 byte uuid
    234     //        4 byte data length N
    235     //        N bytes of data
    236     //    }
    237 
    238     // Determine the number of entries in the source data.
    239     // Since we got the data from stagefright, we trust it is valid and properly formatted.
    240     const uint8_t* data = buffer->data();
    241     size_t len = buffer->size();
    242     size_t numentries = 0;
    243     while (len > 0) {
    244         numentries++;
    245 
    246         // skip uuid
    247         data += 16;
    248         len -= 16;
    249 
    250         // get data length
    251         uint32_t datalen = *((uint32_t*)data);
    252         data += 4;
    253         len -= 4;
    254 
    255         // skip the data
    256         data += datalen;
    257         len -= datalen;
    258     }
    259 
    260     // there are <numentries> in the source buffer, we need
    261     // (source buffer size) - (sizeof(uint32_t) * numentries) + sizeof(size_t)
    262     //  + ((sizeof(void*) + sizeof(size_t)) * numentries) bytes for the PsshInfo structure
    263     // Or in other words, the data lengths in the source structure are replaced by size_t
    264     // (which may be the same size or larger, for 64 bit), and in addition there is an
    265     // extra pointer for each entry, and an extra size_t for the entire PsshInfo.
    266     size_t newsize = buffer->size() - (sizeof(uint32_t) * numentries) + sizeof(size_t)
    267             + ((sizeof(void*) + sizeof(size_t)) * numentries);
    268     ex->mPsshBuf = new ABuffer(newsize);
    269     ex->mPsshBuf->setRange(0, newsize);
    270 
    271     // copy data
    272     const uint8_t* src = buffer->data();
    273     uint8_t* dst = ex->mPsshBuf->data();
    274     uint8_t* dstdata = dst + sizeof(size_t) + numentries * sizeof(PsshEntry);
    275     *((size_t*)dst) = numentries;
    276     dst += sizeof(size_t);
    277     for (size_t i = 0; i < numentries; i++) {
    278         // copy uuid
    279         memcpy(dst, src, 16);
    280         src += 16;
    281         dst += 16;
    282 
    283         // get/copy data length
    284         uint32_t datalen = *((uint32_t*)src);
    285         *((size_t*)dst) = datalen;
    286         src += sizeof(uint32_t);
    287         dst += sizeof(size_t);
    288 
    289         // the next entry in the destination is a pointer to the actual data, which we store
    290         // after the array of PsshEntry
    291         *((void**)dst) = dstdata;
    292         dst += sizeof(void*);
    293 
    294         // copy the actual data
    295         memcpy(dstdata, src, datalen);
    296         dstdata += datalen;
    297         src += datalen;
    298     }
    299 
    300     return (PsshInfo*) ex->mPsshBuf->data();
    301 }
    302 
    303 EXPORT
    304 AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) {
    305     sp<MetaData> meta;
    306     if(ex->mImpl->getSampleMeta(&meta) != 0) {
    307         return NULL;
    308     }
    309 
    310     uint32_t type;
    311     const void *crypteddata;
    312     size_t cryptedsize;
    313     if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
    314         return NULL;
    315     }
    316     size_t numSubSamples = cryptedsize / sizeof(size_t);
    317 
    318     const void *cleardata;
    319     size_t clearsize;
    320     if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
    321         if (clearsize != cryptedsize) {
    322             // The two must be of the same length.
    323             return NULL;
    324         }
    325     }
    326 
    327     const void *key;
    328     size_t keysize;
    329     if (meta->findData(kKeyCryptoIV, &type, &key, &keysize)) {
    330         if (keysize != 16) {
    331             // IVs must be 16 bytes in length.
    332             return NULL;
    333         }
    334     }
    335 
    336     const void *iv;
    337     size_t ivsize;
    338     if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
    339         if (ivsize != 16) {
    340             // IVs must be 16 bytes in length.
    341             return NULL;
    342         }
    343     }
    344 
    345     int32_t mode;
    346     if (!meta->findInt32(kKeyCryptoMode, &mode)) {
    347         mode = CryptoPlugin::kMode_AES_CTR;
    348     }
    349 
    350     return AMediaCodecCryptoInfo_new(
    351             numSubSamples,
    352             (uint8_t*) key,
    353             (uint8_t*) iv,
    354             (cryptoinfo_mode_t) mode,
    355             (size_t*) cleardata,
    356             (size_t*) crypteddata);
    357 }
    358 
    359 
    360 } // extern "C"
    361 
    362