Home | History | Annotate | Download | only in libstagefright
      1 /*
      2  * Copyright (C) 2012 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 "FragmentedMP4Extractor"
     19 #include <utils/Log.h>
     20 
     21 #include "include/FragmentedMP4Extractor.h"
     22 #include "include/SampleTable.h"
     23 #include "include/ESDS.h"
     24 
     25 #include <arpa/inet.h>
     26 
     27 #include <ctype.h>
     28 #include <stdint.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 
     32 #include <cutils/properties.h> // for property_get
     33 
     34 #include <media/stagefright/foundation/ABitReader.h>
     35 #include <media/stagefright/foundation/ABuffer.h>
     36 #include <media/stagefright/foundation/ADebug.h>
     37 #include <media/stagefright/foundation/AMessage.h>
     38 #include <media/stagefright/DataSource.h>
     39 #include <media/stagefright/MediaBuffer.h>
     40 #include <media/stagefright/MediaBufferGroup.h>
     41 #include <media/stagefright/MediaDefs.h>
     42 #include <media/stagefright/MediaSource.h>
     43 #include <media/stagefright/MetaData.h>
     44 #include <media/stagefright/Utils.h>
     45 #include <utils/String8.h>
     46 
     47 namespace android {
     48 
     49 class FragmentedMPEG4Source : public MediaSource {
     50 public:
     51     // Caller retains ownership of the Parser
     52     FragmentedMPEG4Source(bool audio,
     53                 const sp<MetaData> &format,
     54                 const sp<FragmentedMP4Parser> &parser,
     55                 const sp<FragmentedMP4Extractor> &extractor);
     56 
     57     virtual status_t start(MetaData *params = NULL);
     58     virtual status_t stop();
     59 
     60     virtual sp<MetaData> getFormat();
     61 
     62     virtual status_t read(
     63             MediaBuffer **buffer, const ReadOptions *options = NULL);
     64 
     65 protected:
     66     virtual ~FragmentedMPEG4Source();
     67 
     68 private:
     69     Mutex mLock;
     70 
     71     sp<MetaData> mFormat;
     72     sp<FragmentedMP4Parser> mParser;
     73     sp<FragmentedMP4Extractor> mExtractor;
     74     bool mIsAudioTrack;
     75     uint32_t mCurrentSampleIndex;
     76 
     77     bool mIsAVC;
     78     size_t mNALLengthSize;
     79 
     80     bool mStarted;
     81 
     82     MediaBufferGroup *mGroup;
     83 
     84     bool mWantsNALFragments;
     85 
     86     uint8_t *mSrcBuffer;
     87 
     88     FragmentedMPEG4Source(const FragmentedMPEG4Source &);
     89     FragmentedMPEG4Source &operator=(const FragmentedMPEG4Source &);
     90 };
     91 
     92 
     93 FragmentedMP4Extractor::FragmentedMP4Extractor(const sp<DataSource> &source)
     94     : mLooper(new ALooper),
     95       mParser(new FragmentedMP4Parser()),
     96       mDataSource(source),
     97       mInitCheck(NO_INIT),
     98       mFileMetaData(new MetaData) {
     99     ALOGV("FragmentedMP4Extractor");
    100     mLooper->registerHandler(mParser);
    101     mLooper->start(false /* runOnCallingThread */);
    102     mParser->start(mDataSource);
    103 
    104     bool hasVideo = mParser->getFormat(false /* audio */, true /* synchronous */) != NULL;
    105     bool hasAudio = mParser->getFormat(true /* audio */, true /* synchronous */) != NULL;
    106 
    107     ALOGV("number of tracks: %d", countTracks());
    108 
    109     if (hasVideo) {
    110         mFileMetaData->setCString(
    111                 kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
    112     } else if (hasAudio) {
    113         mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
    114     } else {
    115         ALOGE("no audio and no video, no idea what file type this is");
    116     }
    117     // tracks are numbered such that video track is first, audio track is second
    118     if (hasAudio && hasVideo) {
    119         mTrackCount = 2;
    120         mAudioTrackIndex = 1;
    121     } else if (hasAudio) {
    122         mTrackCount = 1;
    123         mAudioTrackIndex = 0;
    124     } else if (hasVideo) {
    125         mTrackCount = 1;
    126         mAudioTrackIndex = -1;
    127     } else {
    128         mTrackCount = 0;
    129         mAudioTrackIndex = -1;
    130     }
    131 }
    132 
    133 FragmentedMP4Extractor::~FragmentedMP4Extractor() {
    134     ALOGV("~FragmentedMP4Extractor");
    135     mLooper->stop();
    136 }
    137 
    138 uint32_t FragmentedMP4Extractor::flags() const {
    139     return CAN_PAUSE |
    140             (mParser->isSeekable() ? (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
    141 }
    142 
    143 sp<MetaData> FragmentedMP4Extractor::getMetaData() {
    144     return mFileMetaData;
    145 }
    146 
    147 size_t FragmentedMP4Extractor::countTracks() {
    148     return mTrackCount;
    149 }
    150 
    151 
    152 sp<MetaData> FragmentedMP4Extractor::getTrackMetaData(
    153         size_t index, uint32_t flags) {
    154     if (index >= countTracks()) {
    155         return NULL;
    156     }
    157 
    158     sp<AMessage> msg = mParser->getFormat(index == mAudioTrackIndex, true /* synchronous */);
    159 
    160     if (msg == NULL) {
    161         ALOGV("got null format for track %d", index);
    162         return NULL;
    163     }
    164 
    165     sp<MetaData> meta = new MetaData();
    166     convertMessageToMetaData(msg, meta);
    167     return meta;
    168 }
    169 
    170 static void MakeFourCCString(uint32_t x, char *s) {
    171     s[0] = x >> 24;
    172     s[1] = (x >> 16) & 0xff;
    173     s[2] = (x >> 8) & 0xff;
    174     s[3] = x & 0xff;
    175     s[4] = '\0';
    176 }
    177 
    178 sp<MediaSource> FragmentedMP4Extractor::getTrack(size_t index) {
    179     if (index >= countTracks()) {
    180         return NULL;
    181     }
    182     return new FragmentedMPEG4Source(index == mAudioTrackIndex, getTrackMetaData(index, 0), mParser, this);
    183 }
    184 
    185 
    186 ////////////////////////////////////////////////////////////////////////////////
    187 
    188 FragmentedMPEG4Source::FragmentedMPEG4Source(
    189         bool audio,
    190         const sp<MetaData> &format,
    191         const sp<FragmentedMP4Parser> &parser,
    192         const sp<FragmentedMP4Extractor> &extractor)
    193     : mFormat(format),
    194       mParser(parser),
    195       mExtractor(extractor),
    196       mIsAudioTrack(audio),
    197       mStarted(false),
    198       mGroup(NULL),
    199       mWantsNALFragments(false),
    200       mSrcBuffer(NULL) {
    201 }
    202 
    203 FragmentedMPEG4Source::~FragmentedMPEG4Source() {
    204     if (mStarted) {
    205         stop();
    206     }
    207 }
    208 
    209 status_t FragmentedMPEG4Source::start(MetaData *params) {
    210     Mutex::Autolock autoLock(mLock);
    211 
    212     CHECK(!mStarted);
    213 
    214     int32_t val;
    215     if (params && params->findInt32(kKeyWantsNALFragments, &val)
    216         && val != 0) {
    217         mWantsNALFragments = true;
    218     } else {
    219         mWantsNALFragments = false;
    220     }
    221     ALOGV("caller wants NAL fragments: %s", mWantsNALFragments ? "yes" : "no");
    222 
    223     mGroup = new MediaBufferGroup;
    224 
    225     int32_t max_size = 65536;
    226     // XXX CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
    227 
    228     mGroup->add_buffer(new MediaBuffer(max_size));
    229 
    230     mSrcBuffer = new uint8_t[max_size];
    231 
    232     mStarted = true;
    233 
    234     return OK;
    235 }
    236 
    237 status_t FragmentedMPEG4Source::stop() {
    238     Mutex::Autolock autoLock(mLock);
    239 
    240     CHECK(mStarted);
    241 
    242     delete[] mSrcBuffer;
    243     mSrcBuffer = NULL;
    244 
    245     delete mGroup;
    246     mGroup = NULL;
    247 
    248     mStarted = false;
    249     mCurrentSampleIndex = 0;
    250 
    251     return OK;
    252 }
    253 
    254 sp<MetaData> FragmentedMPEG4Source::getFormat() {
    255     Mutex::Autolock autoLock(mLock);
    256 
    257     return mFormat;
    258 }
    259 
    260 
    261 status_t FragmentedMPEG4Source::read(
    262         MediaBuffer **out, const ReadOptions *options) {
    263     int64_t seekTimeUs;
    264     ReadOptions::SeekMode mode;
    265     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
    266         mParser->seekTo(mIsAudioTrack, seekTimeUs);
    267     }
    268     MediaBuffer *buffer = NULL;
    269     mGroup->acquire_buffer(&buffer);
    270     sp<ABuffer> parseBuffer;
    271 
    272     status_t ret = mParser->dequeueAccessUnit(mIsAudioTrack, &parseBuffer, true /* synchronous */);
    273     if (ret != OK) {
    274         buffer->release();
    275         ALOGV("returning %d", ret);
    276         return ret;
    277     }
    278     sp<AMessage> meta = parseBuffer->meta();
    279     int64_t timeUs;
    280     CHECK(meta->findInt64("timeUs", &timeUs));
    281     buffer->meta_data()->setInt64(kKeyTime, timeUs);
    282     buffer->set_range(0, parseBuffer->size());
    283     memcpy(buffer->data(), parseBuffer->data(), parseBuffer->size());
    284     *out = buffer;
    285     return OK;
    286 }
    287 
    288 
    289 static bool isCompatibleBrand(uint32_t fourcc) {
    290     static const uint32_t kCompatibleBrands[] = {
    291         FOURCC('i', 's', 'o', 'm'),
    292         FOURCC('i', 's', 'o', '2'),
    293         FOURCC('a', 'v', 'c', '1'),
    294         FOURCC('3', 'g', 'p', '4'),
    295         FOURCC('m', 'p', '4', '1'),
    296         FOURCC('m', 'p', '4', '2'),
    297 
    298         // Won't promise that the following file types can be played.
    299         // Just give these file types a chance.
    300         FOURCC('q', 't', ' ', ' '),  // Apple's QuickTime
    301         FOURCC('M', 'S', 'N', 'V'),  // Sony's PSP
    302 
    303         FOURCC('3', 'g', '2', 'a'),  // 3GPP2
    304         FOURCC('3', 'g', '2', 'b'),
    305     };
    306 
    307     for (size_t i = 0;
    308          i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]);
    309          ++i) {
    310         if (kCompatibleBrands[i] == fourcc) {
    311             return true;
    312         }
    313     }
    314 
    315     return false;
    316 }
    317 
    318 // Attempt to actually parse the 'ftyp' atom and determine if a suitable
    319 // compatible brand is present.
    320 // Also try to identify where this file's metadata ends
    321 // (end of the 'moov' atom) and report it to the caller as part of
    322 // the metadata.
    323 static bool Sniff(
    324         const sp<DataSource> &source, String8 *mimeType, float *confidence,
    325         sp<AMessage> *meta) {
    326     // We scan up to 128k bytes to identify this file as an MP4.
    327     static const off64_t kMaxScanOffset = 128ll * 1024ll;
    328 
    329     off64_t offset = 0ll;
    330     bool foundGoodFileType = false;
    331     bool isFragmented = false;
    332     off64_t moovAtomEndOffset = -1ll;
    333     bool done = false;
    334 
    335     while (!done && offset < kMaxScanOffset) {
    336         uint32_t hdr[2];
    337         if (source->readAt(offset, hdr, 8) < 8) {
    338             return false;
    339         }
    340 
    341         uint64_t chunkSize = ntohl(hdr[0]);
    342         uint32_t chunkType = ntohl(hdr[1]);
    343         off64_t chunkDataOffset = offset + 8;
    344 
    345         if (chunkSize == 1) {
    346             if (source->readAt(offset + 8, &chunkSize, 8) < 8) {
    347                 return false;
    348             }
    349 
    350             chunkSize = ntoh64(chunkSize);
    351             chunkDataOffset += 8;
    352 
    353             if (chunkSize < 16) {
    354                 // The smallest valid chunk is 16 bytes long in this case.
    355                 return false;
    356             }
    357         } else if (chunkSize < 8) {
    358             // The smallest valid chunk is 8 bytes long.
    359             return false;
    360         }
    361 
    362         off64_t chunkDataSize = offset + chunkSize - chunkDataOffset;
    363 
    364         char chunkstring[5];
    365         MakeFourCCString(chunkType, chunkstring);
    366         ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset);
    367         switch (chunkType) {
    368             case FOURCC('f', 't', 'y', 'p'):
    369             {
    370                 if (chunkDataSize < 8) {
    371                     return false;
    372                 }
    373 
    374                 uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
    375                 for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
    376                     if (i == 1) {
    377                         // Skip this index, it refers to the minorVersion,
    378                         // not a brand.
    379                         continue;
    380                     }
    381 
    382                     uint32_t brand;
    383                     if (source->readAt(
    384                                 chunkDataOffset + 4 * i, &brand, 4) < 4) {
    385                         return false;
    386                     }
    387 
    388                     brand = ntohl(brand);
    389                     char brandstring[5];
    390                     MakeFourCCString(brand, brandstring);
    391                     ALOGV("Brand: %s", brandstring);
    392 
    393                     if (isCompatibleBrand(brand)) {
    394                         foundGoodFileType = true;
    395                         break;
    396                     }
    397                 }
    398 
    399                 if (!foundGoodFileType) {
    400                     return false;
    401                 }
    402 
    403                 break;
    404             }
    405 
    406             case FOURCC('m', 'o', 'o', 'v'):
    407             {
    408                 moovAtomEndOffset = offset + chunkSize;
    409                 break;
    410             }
    411 
    412             case FOURCC('m', 'o', 'o', 'f'):
    413             {
    414                 // this is kind of broken, since we might not actually find a
    415                 // moof box in the first 128k.
    416                 isFragmented = true;
    417                 done = true;
    418                 break;
    419             }
    420 
    421             default:
    422                 break;
    423         }
    424 
    425         offset += chunkSize;
    426     }
    427 
    428     if (!foundGoodFileType || !isFragmented) {
    429         return false;
    430     }
    431 
    432     *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
    433     *confidence = 0.5f; // slightly more than MPEG4Extractor
    434 
    435     if (moovAtomEndOffset >= 0) {
    436         *meta = new AMessage;
    437         (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
    438         (*meta)->setInt32("fragmented", 1); // tell MediaExtractor what to instantiate
    439 
    440         ALOGV("found metadata size: %lld", moovAtomEndOffset);
    441     }
    442 
    443     return true;
    444 }
    445 
    446 // used by DataSource::RegisterDefaultSniffers
    447 bool SniffFragmentedMP4(
    448         const sp<DataSource> &source, String8 *mimeType, float *confidence,
    449         sp<AMessage> *meta) {
    450     ALOGV("SniffFragmentedMP4");
    451     char prop[PROPERTY_VALUE_MAX];
    452     if (property_get("media.stagefright.use-fragmp4", prop, NULL)
    453             && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) {
    454         return Sniff(source, mimeType, confidence, meta);
    455     }
    456 
    457     return false;
    458 }
    459 
    460 }  // namespace android
    461