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         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     status_t err = mData->mImpl->advance();
    151     if (err == ERROR_END_OF_STREAM) {
    152         return false;
    153     } else if (err != OK) {
    154         ALOGE("sf error code: %d", err);
    155         return false;
    156     }
    157     return true;
    158 }
    159 
    160 EXPORT
    161 media_status_t AMediaExtractor_seekTo(AMediaExtractor *ex, int64_t seekPosUs, SeekMode mode) {
    162     android::MediaSource::ReadOptions::SeekMode sfmode;
    163     if (mode == AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC) {
    164         sfmode = android::MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
    165     } else if (mode == AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC) {
    166         sfmode = android::MediaSource::ReadOptions::SEEK_CLOSEST_SYNC;
    167     } else {
    168         sfmode = android::MediaSource::ReadOptions::SEEK_NEXT_SYNC;
    169     }
    170 
    171     return translate_error(ex->mImpl->seekTo(seekPosUs, sfmode));
    172 }
    173 
    174 EXPORT
    175 ssize_t AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
    176     //ALOGV("readSampleData");
    177     sp<ABuffer> tmp = new ABuffer(buffer, capacity);
    178     if (mData->mImpl->readSampleData(tmp) == OK) {
    179         return tmp->size();
    180     }
    181     return -1;
    182 }
    183 
    184 EXPORT
    185 uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
    186     int sampleFlags = 0;
    187     sp<MetaData> meta;
    188     status_t err = mData->mImpl->getSampleMeta(&meta);
    189     if (err != OK) {
    190         return -1;
    191     }
    192     int32_t val;
    193     if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
    194         sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC;
    195     }
    196 
    197     uint32_t type;
    198     const void *data;
    199     size_t size;
    200     if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
    201         sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED;
    202     }
    203     return sampleFlags;
    204 }
    205 
    206 EXPORT
    207 int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) {
    208     size_t idx;
    209     if (mData->mImpl->getSampleTrackIndex(&idx) != OK) {
    210         return -1;
    211     }
    212     return idx;
    213 }
    214 
    215 EXPORT
    216 int64_t AMediaExtractor_getSampleTime(AMediaExtractor *mData) {
    217     int64_t time;
    218     if (mData->mImpl->getSampleTime(&time) != OK) {
    219         return -1;
    220     }
    221     return time;
    222 }
    223 
    224 EXPORT
    225 PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) {
    226 
    227     if (ex->mPsshBuf != NULL) {
    228         return (PsshInfo*) ex->mPsshBuf->data();
    229     }
    230 
    231     sp<AMessage> format;
    232     ex->mImpl->getFileFormat(&format);
    233     sp<ABuffer> buffer;
    234     if(!format->findBuffer("pssh", &buffer)) {
    235         return NULL;
    236     }
    237 
    238     // the format of the buffer is 1 or more of:
    239     //    {
    240     //        16 byte uuid
    241     //        4 byte data length N
    242     //        N bytes of data
    243     //    }
    244 
    245     // Determine the number of entries in the source data.
    246     // Since we got the data from stagefright, we trust it is valid and properly formatted.
    247     const uint8_t* data = buffer->data();
    248     size_t len = buffer->size();
    249     size_t numentries = 0;
    250     while (len > 0) {
    251         numentries++;
    252 
    253         if (len < 16) {
    254             ALOGE("invalid PSSH data");
    255             return NULL;
    256         }
    257         // skip uuid
    258         data += 16;
    259         len -= 16;
    260 
    261         // get data length
    262         if (len < 4) {
    263             ALOGE("invalid PSSH data");
    264             return NULL;
    265         }
    266         uint32_t datalen = *((uint32_t*)data);
    267         data += 4;
    268         len -= 4;
    269 
    270         if (len < datalen) {
    271             ALOGE("invalid PSSH data");
    272             return NULL;
    273         }
    274         // skip the data
    275         data += datalen;
    276         len -= datalen;
    277     }
    278 
    279     // there are <numentries> in the source buffer, we need
    280     // (source buffer size) - (sizeof(uint32_t) * numentries) + sizeof(size_t)
    281     //  + ((sizeof(void*) + sizeof(size_t)) * numentries) bytes for the PsshInfo structure
    282     // Or in other words, the data lengths in the source structure are replaced by size_t
    283     // (which may be the same size or larger, for 64 bit), and in addition there is an
    284     // extra pointer for each entry, and an extra size_t for the entire PsshInfo.
    285     size_t newsize = buffer->size() - (sizeof(uint32_t) * numentries) + sizeof(size_t)
    286             + ((sizeof(void*) + sizeof(size_t)) * numentries);
    287     if (newsize <= buffer->size()) {
    288         ALOGE("invalid PSSH data");
    289         return NULL;
    290     }
    291     ex->mPsshBuf = new ABuffer(newsize);
    292     ex->mPsshBuf->setRange(0, newsize);
    293 
    294     // copy data
    295     const uint8_t* src = buffer->data();
    296     uint8_t* dst = ex->mPsshBuf->data();
    297     uint8_t* dstdata = dst + sizeof(size_t) + numentries * sizeof(PsshEntry);
    298     *((size_t*)dst) = numentries;
    299     dst += sizeof(size_t);
    300     for (size_t i = 0; i < numentries; i++) {
    301         // copy uuid
    302         memcpy(dst, src, 16);
    303         src += 16;
    304         dst += 16;
    305 
    306         // get/copy data length
    307         uint32_t datalen = *((uint32_t*)src);
    308         *((size_t*)dst) = datalen;
    309         src += sizeof(uint32_t);
    310         dst += sizeof(size_t);
    311 
    312         // the next entry in the destination is a pointer to the actual data, which we store
    313         // after the array of PsshEntry
    314         *((void**)dst) = dstdata;
    315         dst += sizeof(void*);
    316 
    317         // copy the actual data
    318         memcpy(dstdata, src, datalen);
    319         dstdata += datalen;
    320         src += datalen;
    321     }
    322 
    323     return (PsshInfo*) ex->mPsshBuf->data();
    324 }
    325 
    326 EXPORT
    327 AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) {
    328     sp<MetaData> meta;
    329     if(ex->mImpl->getSampleMeta(&meta) != 0) {
    330         return NULL;
    331     }
    332 
    333     uint32_t type;
    334     const void *crypteddata;
    335     size_t cryptedsize;
    336     if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
    337         return NULL;
    338     }
    339     size_t numSubSamples = cryptedsize / sizeof(size_t);
    340 
    341     const void *cleardata;
    342     size_t clearsize;
    343     if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
    344         if (clearsize != cryptedsize) {
    345             // The two must be of the same length.
    346             return NULL;
    347         }
    348     }
    349 
    350     const void *key;
    351     size_t keysize;
    352     if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
    353         if (keysize != 16) {
    354             // Keys must be 16 bytes in length.
    355             return NULL;
    356         }
    357     }
    358 
    359     const void *iv;
    360     size_t ivsize;
    361     if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
    362         if (ivsize != 16) {
    363             // IVs must be 16 bytes in length.
    364             return NULL;
    365         }
    366     }
    367 
    368     int32_t mode;
    369     if (!meta->findInt32(kKeyCryptoMode, &mode)) {
    370         mode = CryptoPlugin::kMode_AES_CTR;
    371     }
    372 
    373     return AMediaCodecCryptoInfo_new(
    374             numSubSamples,
    375             (uint8_t*) key,
    376             (uint8_t*) iv,
    377             (cryptoinfo_mode_t) mode,
    378             (size_t*) cleardata,
    379             (size_t*) crypteddata);
    380 }
    381 
    382 
    383 } // extern "C"
    384 
    385