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

Commit f7617d36 authored by Andreas Huber's avatar Andreas Huber Committed by Android (Google) Code Review
Browse files

Merge "Support for thumbnail extraction in the Matroska extractor." into kraken

parents 06169761 6bdf2edb
Loading
Loading
Loading
Loading
+131 −41
Original line number Diff line number Diff line
@@ -121,6 +121,29 @@ static void hexdump(const void *_data, size_t size) {
    }
}

struct BlockIterator {
    BlockIterator(mkvparser::Segment *segment, unsigned long trackNum);

    bool eos() const;

    void advance();
    void reset();
    void seek(int64_t seekTimeUs);

    const mkvparser::Block *block() const;
    int64_t blockTimeUs() const;

private:
    mkvparser::Segment *mSegment;
    unsigned long mTrackNum;

    mkvparser::Cluster *mCluster;
    const mkvparser::BlockEntry *mBlockEntry;

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

struct MatroskaSource : public MediaSource {
    MatroskaSource(
            const sp<MatroskaExtractor> &extractor, size_t index);
@@ -142,10 +165,8 @@ private:

    sp<MatroskaExtractor> mExtractor;
    size_t mTrackIndex;
    unsigned long mTrackNum;
    Type mType;
    mkvparser::Cluster *mCluster;
    const mkvparser::BlockEntry *mBlockEntry;
    BlockIterator mBlockIter;

    status_t advance();

@@ -158,10 +179,8 @@ MatroskaSource::MatroskaSource(
    : mExtractor(extractor),
      mTrackIndex(index),
      mType(OTHER),
      mCluster(NULL),
      mBlockEntry(NULL) {
    mTrackNum = mExtractor->mTracks.itemAt(index).mTrackNum;

      mBlockIter(mExtractor->mSegment,
                 mExtractor->mTracks.itemAt(index).mTrackNum) {
    const char *mime;
    CHECK(mExtractor->mTracks.itemAt(index).mMeta->
            findCString(kKeyMIMEType, &mime));
@@ -174,8 +193,7 @@ MatroskaSource::MatroskaSource(
}

status_t MatroskaSource::start(MetaData *params) {
    mCluster = NULL;
    mBlockEntry = NULL;
    mBlockIter.reset();

    return OK;
}
@@ -188,60 +206,95 @@ sp<MetaData> MatroskaSource::getFormat() {
    return mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
}

status_t MatroskaSource::advance() {
    for (;;) {
        if (mBlockEntry == NULL || mBlockEntry->EOS()) {
            if (mCluster == NULL) {
                mCluster = mExtractor->mSegment->GetFirst();
            } else {
                mCluster = mExtractor->mSegment->GetNext(mCluster);
            }
            if (mCluster == NULL || mCluster->EOS()) {
                return ERROR_END_OF_STREAM;
////////////////////////////////////////////////////////////////////////////////

BlockIterator::BlockIterator(
        mkvparser::Segment *segment, unsigned long trackNum)
    : mSegment(segment),
      mTrackNum(trackNum),
      mCluster(NULL),
      mBlockEntry(NULL) {
    reset();
}
            mBlockEntry = mCluster->GetFirst();

bool BlockIterator::eos() const {
    return mCluster == NULL || mCluster->EOS();
}

        if (mBlockEntry->GetBlock()->GetTrackNumber() != mTrackNum) {
void BlockIterator::advance() {
    while (!eos()) {
        if (mBlockEntry != NULL) {
            mBlockEntry = mCluster->GetNext(mBlockEntry);
            continue;
        } else if (mCluster != NULL) {
            mCluster = mSegment->GetNext(mCluster);

            if (eos()) {
                break;
            }

            mBlockEntry = mCluster->GetFirst();
        }

        if (mBlockEntry != NULL
                && mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) {
            break;
        }
    }
}

    return OK;
void BlockIterator::reset() {
    mCluster = mSegment->GetFirst();
    mBlockEntry = mCluster->GetFirst();

    while (!eos() && block()->GetTrackNumber() != mTrackNum) {
        advance();
    }
}

void BlockIterator::seek(int64_t seekTimeUs) {
    mCluster = mSegment->GetCluster(seekTimeUs * 1000ll);
    mBlockEntry = mCluster != NULL ? mCluster->GetFirst() : NULL;

    while (!eos() && block()->GetTrackNumber() != mTrackNum) {
        advance();
    }

    while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
        advance();
    }
}

const mkvparser::Block *BlockIterator::block() const {
    CHECK(!eos());

    return mBlockEntry->GetBlock();
}

int64_t BlockIterator::blockTimeUs() const {
    return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll;
}

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

status_t MatroskaSource::read(
        MediaBuffer **out, const ReadOptions *options) {
    *out = NULL;

    int64_t seekTimeUs;
    if (options && options->getSeekTo(&seekTimeUs)) {
        mBlockEntry = NULL;
        mCluster = mExtractor->mSegment->GetCluster(seekTimeUs * 1000ll);

        status_t err;
        while ((err = advance()) == OK && !mBlockEntry->GetBlock()->IsKey()) {
            mBlockEntry = mCluster->GetNext(mBlockEntry);
        mBlockIter.seek(seekTimeUs);
    }

        if (err != OK) {
    if (mBlockIter.eos()) {
        return ERROR_END_OF_STREAM;
    }
    }

    if (advance() != OK) {
        return ERROR_END_OF_STREAM;
    }

    const mkvparser::Block *block = mBlockEntry->GetBlock();
    const mkvparser::Block *block = mBlockIter.block();
    size_t size = block->GetSize();
    long long timeNs = block->GetTime(mCluster);
    int64_t timeUs = mBlockIter.blockTimeUs();

    MediaBuffer *buffer = new MediaBuffer(size + 2);
    buffer->meta_data()->setInt64(kKeyTime, (timeNs + 500) / 1000);
    buffer->meta_data()->setInt64(kKeyTime, timeUs);

    long res = block->Read(
            mExtractor->mReader, (unsigned char *)buffer->data() + 2);
@@ -280,7 +333,7 @@ status_t MatroskaSource::read(
            buffer->range_length());
#endif

    mBlockEntry = mCluster->GetNext(mBlockEntry);
    mBlockIter.advance();

    return OK;
}
@@ -290,7 +343,8 @@ status_t MatroskaSource::read(
MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source)
    : mDataSource(source),
      mReader(new DataSourceReader(mDataSource)),
      mSegment(NULL) {
      mSegment(NULL),
      mExtractedThumbnails(false) {
    mkvparser::EBMLHeader ebmlHeader;
    long long pos;
    if (ebmlHeader.Parse(mReader, pos) < 0) {
@@ -342,6 +396,11 @@ sp<MetaData> MatroskaExtractor::getTrackMetaData(
        return NULL;
    }

    if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails) {
        findThumbnails();
        mExtractedThumbnails = true;
    }

    return mTracks.itemAt(index).mMeta;
}

@@ -479,6 +538,37 @@ void MatroskaExtractor::addTracks() {
    }
}

void MatroskaExtractor::findThumbnails() {
    for (size_t i = 0; i < mTracks.size(); ++i) {
        TrackInfo *info = &mTracks.editItemAt(i);

        const char *mime;
        CHECK(info->mMeta->findCString(kKeyMIMEType, &mime));

        if (strncasecmp(mime, "video/", 6)) {
            continue;
        }

        BlockIterator iter(mSegment, info->mTrackNum);
        int32_t i = 0;
        int64_t thumbnailTimeUs = 0;
        size_t maxBlockSize = 0;
        while (!iter.eos() && i < 20) {
            if (iter.block()->IsKey()) {
                ++i;

                size_t blockSize = iter.block()->GetSize();
                if (blockSize > maxBlockSize) {
                    maxBlockSize = blockSize;
                    thumbnailTimeUs = iter.blockTimeUs();
                }
            }
            iter.advance();
        }
        info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
    }
}

sp<MetaData> MatroskaExtractor::getMetaData() {
    sp<MetaData> meta = new MetaData;
    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA);
+2 −0
Original line number Diff line number Diff line
@@ -59,8 +59,10 @@ private:
    sp<DataSource> mDataSource;
    DataSourceReader *mReader;
    mkvparser::Segment *mSegment;
    bool mExtractedThumbnails;

    void addTracks();
    void findThumbnails();

    MatroskaExtractor(const MatroskaExtractor &);
    MatroskaExtractor &operator=(const MatroskaExtractor &);