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