Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6bde4671 authored by Marco Nelissen's avatar Marco Nelissen
Browse files

Remove FragmentedMP4Extractor

MPEG4Extractor now supports fragmented mp4 files.

Change-Id: I5659a51f4e5e4407a12535e69238fe3abffda7dc
parent 62ad9071
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ LOCAL_SRC_FILES:= \
        ESDS.cpp                          \
        FileSource.cpp                    \
        FLACExtractor.cpp                 \
        FragmentedMP4Extractor.cpp        \
        HTTPBase.cpp                      \
        JPEGSource.cpp                    \
        MP3Extractor.cpp                  \
+0 −2
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@
#include "include/AACExtractor.h"
#include "include/DRMExtractor.h"
#include "include/FLACExtractor.h"
#include "include/FragmentedMP4Extractor.h"
#include "include/HTTPBase.h"
#include "include/MP3Extractor.h"
#include "include/MPEG2PSExtractor.h"
@@ -137,7 +136,6 @@ void DataSource::RegisterSniffer(SnifferFunc func) {
// static
void DataSource::RegisterDefaultSniffers() {
    RegisterSniffer(SniffMPEG4);
    RegisterSniffer(SniffFragmentedMP4);
    RegisterSniffer(SniffMatroska);
    RegisterSniffer(SniffOgg);
    RegisterSniffer(SniffWAV);
+0 −464
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "FragmentedMP4Extractor"
#include <utils/Log.h>

#include "include/FragmentedMP4Extractor.h"
#include "include/SampleTable.h"
#include "include/ESDS.h"

#include <arpa/inet.h>

#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <cutils/properties.h> // for property_get

#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
#include <utils/String8.h>

namespace android {

class FragmentedMPEG4Source : public MediaSource {
public:
    // Caller retains ownership of the Parser
    FragmentedMPEG4Source(bool audio,
                const sp<MetaData> &format,
                const sp<FragmentedMP4Parser> &parser,
                const sp<FragmentedMP4Extractor> &extractor);

    virtual status_t start(MetaData *params = NULL);
    virtual status_t stop();

    virtual sp<MetaData> getFormat();

    virtual status_t read(
            MediaBuffer **buffer, const ReadOptions *options = NULL);

protected:
    virtual ~FragmentedMPEG4Source();

private:
    Mutex mLock;

    sp<MetaData> mFormat;
    sp<FragmentedMP4Parser> mParser;
    sp<FragmentedMP4Extractor> mExtractor;
    bool mIsAudioTrack;
    uint32_t mCurrentSampleIndex;

    bool mIsAVC;
    size_t mNALLengthSize;

    bool mStarted;

    MediaBufferGroup *mGroup;

    bool mWantsNALFragments;

    uint8_t *mSrcBuffer;

    FragmentedMPEG4Source(const FragmentedMPEG4Source &);
    FragmentedMPEG4Source &operator=(const FragmentedMPEG4Source &);
};


FragmentedMP4Extractor::FragmentedMP4Extractor(const sp<DataSource> &source)
    : mLooper(new ALooper),
      mParser(new FragmentedMP4Parser()),
      mDataSource(source),
      mInitCheck(NO_INIT),
      mFileMetaData(new MetaData) {
    ALOGV("FragmentedMP4Extractor");
    mLooper->registerHandler(mParser);
    mLooper->start(false /* runOnCallingThread */);
    mParser->start(mDataSource);

    bool hasVideo = mParser->getFormat(false /* audio */, true /* synchronous */) != NULL;
    bool hasAudio = mParser->getFormat(true /* audio */, true /* synchronous */) != NULL;

    ALOGV("number of tracks: %d", countTracks());

    if (hasVideo) {
        mFileMetaData->setCString(
                kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
    } else if (hasAudio) {
        mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
    } else {
        ALOGE("no audio and no video, no idea what file type this is");
    }
    // tracks are numbered such that video track is first, audio track is second
    if (hasAudio && hasVideo) {
        mTrackCount = 2;
        mAudioTrackIndex = 1;
    } else if (hasAudio) {
        mTrackCount = 1;
        mAudioTrackIndex = 0;
    } else if (hasVideo) {
        mTrackCount = 1;
        mAudioTrackIndex = -1;
    } else {
        mTrackCount = 0;
        mAudioTrackIndex = -1;
    }
}

FragmentedMP4Extractor::~FragmentedMP4Extractor() {
    ALOGV("~FragmentedMP4Extractor");
    mLooper->stop();
}

uint32_t FragmentedMP4Extractor::flags() const {
    return CAN_PAUSE |
            (mParser->isSeekable() ? (CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
}

sp<MetaData> FragmentedMP4Extractor::getMetaData() {
    return mFileMetaData;
}

size_t FragmentedMP4Extractor::countTracks() {
    return mTrackCount;
}


sp<MetaData> FragmentedMP4Extractor::getTrackMetaData(
        size_t index, uint32_t flags) {
    if (index >= countTracks()) {
        return NULL;
    }

    sp<AMessage> msg = mParser->getFormat(index == mAudioTrackIndex, true /* synchronous */);

    if (msg == NULL) {
        ALOGV("got null format for track %d", index);
        return NULL;
    }

    sp<MetaData> meta = new MetaData();
    convertMessageToMetaData(msg, meta);
    return meta;
}

static void MakeFourCCString(uint32_t x, char *s) {
    s[0] = x >> 24;
    s[1] = (x >> 16) & 0xff;
    s[2] = (x >> 8) & 0xff;
    s[3] = x & 0xff;
    s[4] = '\0';
}

sp<MediaSource> FragmentedMP4Extractor::getTrack(size_t index) {
    if (index >= countTracks()) {
        return NULL;
    }
    return new FragmentedMPEG4Source(index == mAudioTrackIndex, getTrackMetaData(index, 0), mParser, this);
}


////////////////////////////////////////////////////////////////////////////////

FragmentedMPEG4Source::FragmentedMPEG4Source(
        bool audio,
        const sp<MetaData> &format,
        const sp<FragmentedMP4Parser> &parser,
        const sp<FragmentedMP4Extractor> &extractor)
    : mFormat(format),
      mParser(parser),
      mExtractor(extractor),
      mIsAudioTrack(audio),
      mStarted(false),
      mGroup(NULL),
      mWantsNALFragments(false),
      mSrcBuffer(NULL) {
}

FragmentedMPEG4Source::~FragmentedMPEG4Source() {
    if (mStarted) {
        stop();
    }
}

status_t FragmentedMPEG4Source::start(MetaData *params) {
    Mutex::Autolock autoLock(mLock);

    CHECK(!mStarted);

    int32_t val;
    if (params && params->findInt32(kKeyWantsNALFragments, &val)
        && val != 0) {
        mWantsNALFragments = true;
    } else {
        mWantsNALFragments = false;
    }
    ALOGV("caller wants NAL fragments: %s", mWantsNALFragments ? "yes" : "no");

    mGroup = new MediaBufferGroup;

    // for video, make the buffer big enough for an extremely poorly compressed 1080p frame.
    int32_t max_size = mIsAudioTrack ? 65536 : 3110400;

    mGroup->add_buffer(new MediaBuffer(max_size));

    mSrcBuffer = new uint8_t[max_size];

    mStarted = true;

    return OK;
}

status_t FragmentedMPEG4Source::stop() {
    Mutex::Autolock autoLock(mLock);

    CHECK(mStarted);

    delete[] mSrcBuffer;
    mSrcBuffer = NULL;

    delete mGroup;
    mGroup = NULL;

    mStarted = false;
    mCurrentSampleIndex = 0;

    return OK;
}

sp<MetaData> FragmentedMPEG4Source::getFormat() {
    Mutex::Autolock autoLock(mLock);

    return mFormat;
}


status_t FragmentedMPEG4Source::read(
        MediaBuffer **out, const ReadOptions *options) {
    int64_t seekTimeUs;
    ReadOptions::SeekMode mode;
    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
        mParser->seekTo(mIsAudioTrack, seekTimeUs);
    }
    MediaBuffer *buffer = NULL;
    mGroup->acquire_buffer(&buffer);
    sp<ABuffer> parseBuffer;

    status_t ret = mParser->dequeueAccessUnit(mIsAudioTrack, &parseBuffer, true /* synchronous */);
    if (ret != OK) {
        buffer->release();
        ALOGV("returning %d", ret);
        return ret;
    }
    sp<AMessage> meta = parseBuffer->meta();
    int64_t timeUs;
    CHECK(meta->findInt64("timeUs", &timeUs));
    int32_t isSync;
    if (meta->findInt32("is-sync-frame", &isSync) && isSync != 0) {
        buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
    }
    buffer->meta_data()->setInt64(kKeyTime, timeUs);
    buffer->set_range(0, parseBuffer->size());
    memcpy(buffer->data(), parseBuffer->data(), parseBuffer->size());
    *out = buffer;
    return OK;
}


static bool isCompatibleBrand(uint32_t fourcc) {
    static const uint32_t kCompatibleBrands[] = {
        FOURCC('i', 's', 'o', 'm'),
        FOURCC('i', 's', 'o', '2'),
        FOURCC('a', 'v', 'c', '1'),
        FOURCC('3', 'g', 'p', '4'),
        FOURCC('m', 'p', '4', '1'),
        FOURCC('m', 'p', '4', '2'),

        // Won't promise that the following file types can be played.
        // Just give these file types a chance.
        FOURCC('q', 't', ' ', ' '),  // Apple's QuickTime
        FOURCC('M', 'S', 'N', 'V'),  // Sony's PSP

        FOURCC('3', 'g', '2', 'a'),  // 3GPP2
        FOURCC('3', 'g', '2', 'b'),
    };

    for (size_t i = 0;
         i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]);
         ++i) {
        if (kCompatibleBrands[i] == fourcc) {
            return true;
        }
    }

    return false;
}

// Attempt to actually parse the 'ftyp' atom and determine if a suitable
// compatible brand is present.
// Also try to identify where this file's metadata ends
// (end of the 'moov' atom) and report it to the caller as part of
// the metadata.
static bool Sniff(
        const sp<DataSource> &source, String8 *mimeType, float *confidence,
        sp<AMessage> *meta) {
    // We scan up to 128k bytes to identify this file as an MP4.
    static const off64_t kMaxScanOffset = 128ll * 1024ll;

    off64_t offset = 0ll;
    bool foundGoodFileType = false;
    bool isFragmented = false;
    off64_t moovAtomEndOffset = -1ll;
    bool done = false;

    while (!done && offset < kMaxScanOffset) {
        uint32_t hdr[2];
        if (source->readAt(offset, hdr, 8) < 8) {
            return false;
        }

        uint64_t chunkSize = ntohl(hdr[0]);
        uint32_t chunkType = ntohl(hdr[1]);
        off64_t chunkDataOffset = offset + 8;

        if (chunkSize == 1) {
            if (source->readAt(offset + 8, &chunkSize, 8) < 8) {
                return false;
            }

            chunkSize = ntoh64(chunkSize);
            chunkDataOffset += 8;

            if (chunkSize < 16) {
                // The smallest valid chunk is 16 bytes long in this case.
                return false;
            }
        } else if (chunkSize < 8) {
            // The smallest valid chunk is 8 bytes long.
            return false;
        }

        off64_t chunkDataSize = offset + chunkSize - chunkDataOffset;

        char chunkstring[5];
        MakeFourCCString(chunkType, chunkstring);
        ALOGV("saw chunk type %s, size %lld @ %lld", chunkstring, chunkSize, offset);
        switch (chunkType) {
            case FOURCC('f', 't', 'y', 'p'):
            {
                if (chunkDataSize < 8) {
                    return false;
                }

                uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
                for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
                    if (i == 1) {
                        // Skip this index, it refers to the minorVersion,
                        // not a brand.
                        continue;
                    }

                    uint32_t brand;
                    if (source->readAt(
                                chunkDataOffset + 4 * i, &brand, 4) < 4) {
                        return false;
                    }

                    brand = ntohl(brand);
                    char brandstring[5];
                    MakeFourCCString(brand, brandstring);
                    ALOGV("Brand: %s", brandstring);

                    if (isCompatibleBrand(brand)) {
                        foundGoodFileType = true;
                        break;
                    }
                }

                if (!foundGoodFileType) {
                    return false;
                }

                break;
            }

            case FOURCC('m', 'o', 'o', 'v'):
            {
                moovAtomEndOffset = offset + chunkSize;
                break;
            }

            case FOURCC('m', 'o', 'o', 'f'):
            {
                // this is kind of broken, since we might not actually find a
                // moof box in the first 128k.
                isFragmented = true;
                done = true;
                break;
            }

            default:
                break;
        }

        offset += chunkSize;
    }

    if (!foundGoodFileType || !isFragmented) {
        return false;
    }

    *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
    *confidence = 0.5f; // slightly more than MPEG4Extractor

    if (moovAtomEndOffset >= 0) {
        *meta = new AMessage;
        (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
        (*meta)->setInt32("fragmented", 1); // tell MediaExtractor what to instantiate

        ALOGV("found metadata size: %lld", moovAtomEndOffset);
    }

    return true;
}

// used by DataSource::RegisterDefaultSniffers
bool SniffFragmentedMP4(
        const sp<DataSource> &source, String8 *mimeType, float *confidence,
        sp<AMessage> *meta) {
    ALOGV("SniffFragmentedMP4");
    char prop[PROPERTY_VALUE_MAX];
    if (property_get("media.stagefright.use-fragmp4", prop, NULL)
            && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) {
        return Sniff(source, mimeType, confidence, meta);
    }

    return false;
}

}  // namespace android
+1 −7
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@
#include "include/AMRExtractor.h"
#include "include/MP3Extractor.h"
#include "include/MPEG4Extractor.h"
#include "include/FragmentedMP4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/MPEG2PSExtractor.h"
@@ -94,12 +93,7 @@ sp<MediaExtractor> MediaExtractor::Create(
    MediaExtractor *ret = NULL;
    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
            || !strcasecmp(mime, "audio/mp4")) {
        int fragmented = 0;
        if (meta != NULL && meta->findInt32("fragmented", &fragmented) && fragmented) {
            ret = new FragmentedMP4Extractor(source);
        } else {
        ret = new MPEG4Extractor(source);
        }
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
        ret = new MP3Extractor(source, meta);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
+0 −70
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FRAGMENTED_MP4_EXTRACTOR_H_

#define FRAGMENTED_MP4_EXTRACTOR_H_

#include "include/FragmentedMP4Parser.h"

#include <media/stagefright/MediaExtractor.h>
#include <utils/Vector.h>
#include <utils/String8.h>

namespace android {

struct AMessage;
class DataSource;
class SampleTable;
class String8;

class FragmentedMP4Extractor : public MediaExtractor {
public:
    // Extractor assumes ownership of "source".
    FragmentedMP4Extractor(const sp<DataSource> &source);

    virtual size_t countTracks();
    virtual sp<MediaSource> getTrack(size_t index);
    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
    virtual sp<MetaData> getMetaData();
    virtual uint32_t flags() const;

protected:
    virtual ~FragmentedMP4Extractor();

private:
    sp<ALooper> mLooper;
    sp<FragmentedMP4Parser> mParser;
    sp<DataSource> mDataSource;
    status_t mInitCheck;
    size_t mAudioTrackIndex;
    size_t mTrackCount;

    sp<MetaData> mFileMetaData;

    Vector<uint32_t> mPath;

    FragmentedMP4Extractor(const FragmentedMP4Extractor &);
    FragmentedMP4Extractor &operator=(const FragmentedMP4Extractor &);
};

bool SniffFragmentedMP4(
        const sp<DataSource> &source, String8 *mimeType, float *confidence,
        sp<AMessage> *);

}  // namespace android

#endif  // MPEG4_EXTRACTOR_H_