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