MatroskaExtractor.cpp revision df64d15042bbd5e0e4933ac49bf3c177dd94752c
1/*
2 * Copyright (C) 2010 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 "MatroskaExtractor"
19#include <utils/Log.h>
20
21#include "MatroskaExtractor.h"
22
23#include "mkvparser.hpp"
24
25#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/foundation/hexdump.h>
27#include <media/stagefright/DataSource.h>
28#include <media/stagefright/MediaBuffer.h>
29#include <media/stagefright/MediaDefs.h>
30#include <media/stagefright/MediaErrors.h>
31#include <media/stagefright/MediaSource.h>
32#include <media/stagefright/MetaData.h>
33#include <media/stagefright/Utils.h>
34#include <utils/String8.h>
35
36namespace android {
37
38struct DataSourceReader : public mkvparser::IMkvReader {
39    DataSourceReader(const sp<DataSource> &source)
40        : mSource(source) {
41    }
42
43    virtual int Read(long long position, long length, unsigned char* buffer) {
44        CHECK(position >= 0);
45        CHECK(length >= 0);
46
47        if (length == 0) {
48            return 0;
49        }
50
51        ssize_t n = mSource->readAt(position, buffer, length);
52
53        if (n <= 0) {
54            return -1;
55        }
56
57        return 0;
58    }
59
60    virtual int Length(long long* total, long long* available) {
61        off64_t size;
62        if (mSource->getSize(&size) != OK) {
63            *total = -1;
64            *available = (long long)((1ull << 63) - 1);
65
66            return 0;
67        }
68
69        if (total) {
70            *total = size;
71        }
72
73        if (available) {
74            *available = size;
75        }
76
77        return 0;
78    }
79
80private:
81    sp<DataSource> mSource;
82
83    DataSourceReader(const DataSourceReader &);
84    DataSourceReader &operator=(const DataSourceReader &);
85};
86
87////////////////////////////////////////////////////////////////////////////////
88
89struct BlockIterator {
90    BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum);
91
92    bool eos() const;
93
94    void advance();
95    void reset();
96    void seek(int64_t seekTimeUs, bool seekToKeyFrame);
97
98    const mkvparser::Block *block() const;
99    int64_t blockTimeUs() const;
100
101private:
102    MatroskaExtractor *mExtractor;
103    unsigned long mTrackNum;
104
105    const mkvparser::Cluster *mCluster;
106    const mkvparser::BlockEntry *mBlockEntry;
107    long mBlockEntryIndex;
108
109    void advance_l();
110
111    BlockIterator(const BlockIterator &);
112    BlockIterator &operator=(const BlockIterator &);
113};
114
115struct MatroskaSource : public MediaSource {
116    MatroskaSource(
117            const sp<MatroskaExtractor> &extractor, size_t index);
118
119    virtual status_t start(MetaData *params);
120    virtual status_t stop();
121
122    virtual sp<MetaData> getFormat();
123
124    virtual status_t read(
125            MediaBuffer **buffer, const ReadOptions *options);
126
127protected:
128    virtual ~MatroskaSource();
129
130private:
131    enum Type {
132        AVC,
133        AAC,
134        OTHER
135    };
136
137    sp<MatroskaExtractor> mExtractor;
138    size_t mTrackIndex;
139    Type mType;
140    bool mIsAudio;
141    BlockIterator mBlockIter;
142    size_t mNALSizeLen;  // for type AVC
143
144    List<MediaBuffer *> mPendingFrames;
145
146    status_t advance();
147
148    status_t readBlock();
149    void clearPendingFrames();
150
151    MatroskaSource(const MatroskaSource &);
152    MatroskaSource &operator=(const MatroskaSource &);
153};
154
155MatroskaSource::MatroskaSource(
156        const sp<MatroskaExtractor> &extractor, size_t index)
157    : mExtractor(extractor),
158      mTrackIndex(index),
159      mType(OTHER),
160      mIsAudio(false),
161      mBlockIter(mExtractor.get(),
162                 mExtractor->mTracks.itemAt(index).mTrackNum),
163      mNALSizeLen(0) {
164    sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta;
165
166    const char *mime;
167    CHECK(meta->findCString(kKeyMIMEType, &mime));
168
169    mIsAudio = !strncasecmp("audio/", mime, 6);
170
171    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
172        mType = AVC;
173
174        uint32_t dummy;
175        const uint8_t *avcc;
176        size_t avccSize;
177        CHECK(meta->findData(
178                    kKeyAVCC, &dummy, (const void **)&avcc, &avccSize));
179
180        CHECK_GE(avccSize, 5u);
181
182        mNALSizeLen = 1 + (avcc[4] & 3);
183        ALOGV("mNALSizeLen = %d", mNALSizeLen);
184    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
185        mType = AAC;
186    }
187}
188
189MatroskaSource::~MatroskaSource() {
190    clearPendingFrames();
191}
192
193status_t MatroskaSource::start(MetaData *params) {
194    mBlockIter.reset();
195
196    return OK;
197}
198
199status_t MatroskaSource::stop() {
200    clearPendingFrames();
201
202    return OK;
203}
204
205sp<MetaData> MatroskaSource::getFormat() {
206    return mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
207}
208
209////////////////////////////////////////////////////////////////////////////////
210
211BlockIterator::BlockIterator(
212        MatroskaExtractor *extractor, unsigned long trackNum)
213    : mExtractor(extractor),
214      mTrackNum(trackNum),
215      mCluster(NULL),
216      mBlockEntry(NULL),
217      mBlockEntryIndex(0) {
218    reset();
219}
220
221bool BlockIterator::eos() const {
222    return mCluster == NULL || mCluster->EOS();
223}
224
225void BlockIterator::advance() {
226    Mutex::Autolock autoLock(mExtractor->mLock);
227    advance_l();
228}
229
230void BlockIterator::advance_l() {
231    for (;;) {
232        long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry);
233        ALOGV("GetEntry returned %ld", res);
234
235        long long pos;
236        long len;
237        if (res < 0) {
238            // Need to parse this cluster some more
239
240            CHECK_EQ(res, mkvparser::E_BUFFER_NOT_FULL);
241
242            res = mCluster->Parse(pos, len);
243            ALOGV("Parse returned %ld", res);
244
245            if (res < 0) {
246                // I/O error
247
248                LOGE("Cluster::Parse returned result %ld", res);
249
250                mCluster = NULL;
251                break;
252            }
253
254            continue;
255        } else if (res == 0) {
256            // We're done with this cluster
257
258            const mkvparser::Cluster *nextCluster;
259            res = mExtractor->mSegment->ParseNext(
260                    mCluster, nextCluster, pos, len);
261            ALOGV("ParseNext returned %ld", res);
262
263            if (res > 0) {
264                // EOF
265
266                mCluster = NULL;
267                break;
268            }
269
270            CHECK_EQ(res, 0);
271            CHECK(nextCluster != NULL);
272            CHECK(!nextCluster->EOS());
273
274            mCluster = nextCluster;
275
276            res = mCluster->Parse(pos, len);
277            ALOGV("Parse (2) returned %ld", res);
278            CHECK_GE(res, 0);
279
280            mBlockEntryIndex = 0;
281            continue;
282        }
283
284        CHECK(mBlockEntry != NULL);
285        CHECK(mBlockEntry->GetBlock() != NULL);
286        ++mBlockEntryIndex;
287
288        if (mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) {
289            break;
290        }
291    }
292}
293
294void BlockIterator::reset() {
295    Mutex::Autolock autoLock(mExtractor->mLock);
296
297    mCluster = mExtractor->mSegment->GetFirst();
298    mBlockEntry = NULL;
299    mBlockEntryIndex = 0;
300
301    do {
302        advance_l();
303    } while (!eos() && block()->GetTrackNumber() != mTrackNum);
304}
305
306void BlockIterator::seek(int64_t seekTimeUs, bool seekToKeyFrame) {
307    Mutex::Autolock autoLock(mExtractor->mLock);
308
309    mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
310    mBlockEntry = NULL;
311    mBlockEntryIndex = 0;
312
313    do {
314        advance_l();
315    }
316    while (!eos() && block()->GetTrackNumber() != mTrackNum);
317
318    if (seekToKeyFrame) {
319        while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
320            advance_l();
321        }
322    }
323}
324
325const mkvparser::Block *BlockIterator::block() const {
326    CHECK(!eos());
327
328    return mBlockEntry->GetBlock();
329}
330
331int64_t BlockIterator::blockTimeUs() const {
332    return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll;
333}
334
335////////////////////////////////////////////////////////////////////////////////
336
337static unsigned U24_AT(const uint8_t *ptr) {
338    return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
339}
340
341static size_t clz(uint8_t x) {
342    size_t numLeadingZeroes = 0;
343
344    while (!(x & 0x80)) {
345        ++numLeadingZeroes;
346        x = x << 1;
347    }
348
349    return numLeadingZeroes;
350}
351
352void MatroskaSource::clearPendingFrames() {
353    while (!mPendingFrames.empty()) {
354        MediaBuffer *frame = *mPendingFrames.begin();
355        mPendingFrames.erase(mPendingFrames.begin());
356
357        frame->release();
358        frame = NULL;
359    }
360}
361
362status_t MatroskaSource::readBlock() {
363    CHECK(mPendingFrames.empty());
364
365    if (mBlockIter.eos()) {
366        return ERROR_END_OF_STREAM;
367    }
368
369    const mkvparser::Block *block = mBlockIter.block();
370
371    int64_t timeUs = mBlockIter.blockTimeUs();
372
373    for (int i = 0; i < block->GetFrameCount(); ++i) {
374        const mkvparser::Block::Frame &frame = block->GetFrame(i);
375
376        MediaBuffer *mbuf = new MediaBuffer(frame.len);
377        mbuf->meta_data()->setInt64(kKeyTime, timeUs);
378        mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
379
380        long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data());
381        if (n != 0) {
382            mPendingFrames.clear();
383
384            mBlockIter.advance();
385            return ERROR_IO;
386        }
387
388        mPendingFrames.push_back(mbuf);
389    }
390
391    mBlockIter.advance();
392
393    return OK;
394}
395
396status_t MatroskaSource::read(
397        MediaBuffer **out, const ReadOptions *options) {
398    *out = NULL;
399
400    int64_t seekTimeUs;
401    ReadOptions::SeekMode mode;
402    if (options && options->getSeekTo(&seekTimeUs, &mode)
403            && !mExtractor->isLiveStreaming()) {
404        clearPendingFrames();
405
406        // Apparently keyframe indication in audio tracks is unreliable,
407        // fortunately in all our currently supported audio encodings every
408        // frame is effectively a keyframe.
409        mBlockIter.seek(seekTimeUs, !mIsAudio);
410    }
411
412again:
413    while (mPendingFrames.empty()) {
414        status_t err = readBlock();
415
416        if (err != OK) {
417            clearPendingFrames();
418
419            return err;
420        }
421    }
422
423    MediaBuffer *frame = *mPendingFrames.begin();
424    mPendingFrames.erase(mPendingFrames.begin());
425
426    if (mType != AVC) {
427        *out = frame;
428
429        return OK;
430    }
431
432    // Each input frame contains one or more NAL fragments, each fragment
433    // is prefixed by mNALSizeLen bytes giving the fragment length,
434    // followed by a corresponding number of bytes containing the fragment.
435    // We output all these fragments into a single large buffer separated
436    // by startcodes (0x00 0x00 0x00 0x01).
437
438    const uint8_t *srcPtr =
439        (const uint8_t *)frame->data() + frame->range_offset();
440
441    size_t srcSize = frame->range_length();
442
443    size_t dstSize = 0;
444    MediaBuffer *buffer = NULL;
445    uint8_t *dstPtr = NULL;
446
447    for (int32_t pass = 0; pass < 2; ++pass) {
448        size_t srcOffset = 0;
449        size_t dstOffset = 0;
450        while (srcOffset + mNALSizeLen <= srcSize) {
451            size_t NALsize;
452            switch (mNALSizeLen) {
453                case 1: NALsize = srcPtr[srcOffset]; break;
454                case 2: NALsize = U16_AT(srcPtr + srcOffset); break;
455                case 3: NALsize = U24_AT(srcPtr + srcOffset); break;
456                case 4: NALsize = U32_AT(srcPtr + srcOffset); break;
457                default:
458                    TRESPASS();
459            }
460
461            if (srcOffset + mNALSizeLen + NALsize > srcSize) {
462                break;
463            }
464
465            if (pass == 1) {
466                memcpy(&dstPtr[dstOffset], "\x00\x00\x00\x01", 4);
467
468                memcpy(&dstPtr[dstOffset + 4],
469                       &srcPtr[srcOffset + mNALSizeLen],
470                       NALsize);
471            }
472
473            dstOffset += 4;  // 0x00 00 00 01
474            dstOffset += NALsize;
475
476            srcOffset += mNALSizeLen + NALsize;
477        }
478
479        if (srcOffset < srcSize) {
480            // There were trailing bytes or not enough data to complete
481            // a fragment.
482
483            frame->release();
484            frame = NULL;
485
486            return ERROR_MALFORMED;
487        }
488
489        if (pass == 0) {
490            dstSize = dstOffset;
491
492            buffer = new MediaBuffer(dstSize);
493
494            int64_t timeUs;
495            CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs));
496            int32_t isSync;
497            CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync));
498
499            buffer->meta_data()->setInt64(kKeyTime, timeUs);
500            buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
501
502            dstPtr = (uint8_t *)buffer->data();
503        }
504    }
505
506    frame->release();
507    frame = NULL;
508
509    *out = buffer;
510
511    return OK;
512}
513
514////////////////////////////////////////////////////////////////////////////////
515
516MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source)
517    : mDataSource(source),
518      mReader(new DataSourceReader(mDataSource)),
519      mSegment(NULL),
520      mExtractedThumbnails(false),
521      mIsWebm(false) {
522    off64_t size;
523    mIsLiveStreaming =
524        (mDataSource->flags()
525            & (DataSource::kWantsPrefetching
526                | DataSource::kIsCachingDataSource))
527        && mDataSource->getSize(&size) != OK;
528
529    mkvparser::EBMLHeader ebmlHeader;
530    long long pos;
531    if (ebmlHeader.Parse(mReader, pos) < 0) {
532        return;
533    }
534
535    if (ebmlHeader.m_docType && !strcmp("webm", ebmlHeader.m_docType)) {
536        mIsWebm = true;
537    }
538
539    long long ret =
540        mkvparser::Segment::CreateInstance(mReader, pos, mSegment);
541
542    if (ret) {
543        CHECK(mSegment == NULL);
544        return;
545    }
546
547    if (isLiveStreaming()) {
548        ret = mSegment->ParseHeaders();
549        CHECK_EQ(ret, 0);
550
551        long len;
552        ret = mSegment->LoadCluster(pos, len);
553        CHECK_EQ(ret, 0);
554    } else {
555        ret = mSegment->Load();
556    }
557
558    if (ret < 0) {
559        delete mSegment;
560        mSegment = NULL;
561        return;
562    }
563
564#if 0
565    const mkvparser::SegmentInfo *info = mSegment->GetInfo();
566    ALOGI("muxing app: %s, writing app: %s",
567         info->GetMuxingAppAsUTF8(),
568         info->GetWritingAppAsUTF8());
569#endif
570
571    addTracks();
572}
573
574MatroskaExtractor::~MatroskaExtractor() {
575    delete mSegment;
576    mSegment = NULL;
577
578    delete mReader;
579    mReader = NULL;
580}
581
582size_t MatroskaExtractor::countTracks() {
583    return mTracks.size();
584}
585
586sp<MediaSource> MatroskaExtractor::getTrack(size_t index) {
587    if (index >= mTracks.size()) {
588        return NULL;
589    }
590
591    return new MatroskaSource(this, index);
592}
593
594sp<MetaData> MatroskaExtractor::getTrackMetaData(
595        size_t index, uint32_t flags) {
596    if (index >= mTracks.size()) {
597        return NULL;
598    }
599
600    if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails
601            && !isLiveStreaming()) {
602        findThumbnails();
603        mExtractedThumbnails = true;
604    }
605
606    return mTracks.itemAt(index).mMeta;
607}
608
609bool MatroskaExtractor::isLiveStreaming() const {
610    return mIsLiveStreaming;
611}
612
613static void addESDSFromAudioSpecificInfo(
614        const sp<MetaData> &meta, const void *asi, size_t asiSize) {
615    static const uint8_t kStaticESDS[] = {
616        0x03, 22,
617        0x00, 0x00,     // ES_ID
618        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
619
620        0x04, 17,
621        0x40,                       // Audio ISO/IEC 14496-3
622        0x00, 0x00, 0x00, 0x00,
623        0x00, 0x00, 0x00, 0x00,
624        0x00, 0x00, 0x00, 0x00,
625
626        0x05,
627        // AudioSpecificInfo (with size prefix) follows
628    };
629
630    // Make sure all sizes can be coded in a single byte.
631    CHECK(asiSize + 22 - 2 < 128);
632    size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1;
633    uint8_t *esds = new uint8_t[esdsSize];
634    memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
635    uint8_t *ptr = esds + sizeof(kStaticESDS);
636    *ptr++ = asiSize;
637    memcpy(ptr, asi, asiSize);
638
639    // Increment by codecPrivateSize less 2 bytes that are accounted for
640    // already in lengths of 22/17
641    esds[1] += asiSize - 2;
642    esds[6] += asiSize - 2;
643
644    meta->setData(kKeyESDS, 0, esds, esdsSize);
645
646    delete[] esds;
647    esds = NULL;
648}
649
650void addVorbisCodecInfo(
651        const sp<MetaData> &meta,
652        const void *_codecPrivate, size_t codecPrivateSize) {
653    // printf("vorbis private data follows:\n");
654    // hexdump(_codecPrivate, codecPrivateSize);
655
656    CHECK(codecPrivateSize >= 3);
657
658    const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate;
659    CHECK(codecPrivate[0] == 0x02);
660
661    size_t len1 = codecPrivate[1];
662    size_t len2 = codecPrivate[2];
663
664    CHECK(codecPrivateSize > 3 + len1 + len2);
665
666    CHECK(codecPrivate[3] == 0x01);
667    meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1);
668
669    CHECK(codecPrivate[len1 + 3] == 0x03);
670
671    CHECK(codecPrivate[len1 + len2 + 3] == 0x05);
672    meta->setData(
673            kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3],
674            codecPrivateSize - len1 - len2 - 3);
675}
676
677void MatroskaExtractor::addTracks() {
678    const mkvparser::Tracks *tracks = mSegment->GetTracks();
679
680    for (size_t index = 0; index < tracks->GetTracksCount(); ++index) {
681        const mkvparser::Track *track = tracks->GetTrackByIndex(index);
682
683        if (track == NULL) {
684            // Apparently this is currently valid (if unexpected) behaviour
685            // of the mkv parser lib.
686            continue;
687        }
688
689        const char *const codecID = track->GetCodecId();
690        ALOGV("codec id = %s", codecID);
691        ALOGV("codec name = %s", track->GetCodecNameAsUTF8());
692
693        size_t codecPrivateSize;
694        const unsigned char *codecPrivate =
695            track->GetCodecPrivate(codecPrivateSize);
696
697        enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
698
699        sp<MetaData> meta = new MetaData;
700
701        switch (track->GetType()) {
702            case VIDEO_TRACK:
703            {
704                const mkvparser::VideoTrack *vtrack =
705                    static_cast<const mkvparser::VideoTrack *>(track);
706
707                if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
708                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
709                    meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
710                } else if (!strcmp("V_VP8", codecID)) {
711                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX);
712                } else {
713                    continue;
714                }
715
716                meta->setInt32(kKeyWidth, vtrack->GetWidth());
717                meta->setInt32(kKeyHeight, vtrack->GetHeight());
718                break;
719            }
720
721            case AUDIO_TRACK:
722            {
723                const mkvparser::AudioTrack *atrack =
724                    static_cast<const mkvparser::AudioTrack *>(track);
725
726                if (!strcmp("A_AAC", codecID)) {
727                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
728                    CHECK(codecPrivateSize >= 2);
729
730                    addESDSFromAudioSpecificInfo(
731                            meta, codecPrivate, codecPrivateSize);
732                } else if (!strcmp("A_VORBIS", codecID)) {
733                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
734
735                    addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize);
736                } else {
737                    continue;
738                }
739
740                meta->setInt32(kKeySampleRate, atrack->GetSamplingRate());
741                meta->setInt32(kKeyChannelCount, atrack->GetChannels());
742                break;
743            }
744
745            default:
746                continue;
747        }
748
749        long long durationNs = mSegment->GetDuration();
750        meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
751
752        mTracks.push();
753        TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
754        trackInfo->mTrackNum = track->GetNumber();
755        trackInfo->mMeta = meta;
756    }
757}
758
759void MatroskaExtractor::findThumbnails() {
760    for (size_t i = 0; i < mTracks.size(); ++i) {
761        TrackInfo *info = &mTracks.editItemAt(i);
762
763        const char *mime;
764        CHECK(info->mMeta->findCString(kKeyMIMEType, &mime));
765
766        if (strncasecmp(mime, "video/", 6)) {
767            continue;
768        }
769
770        BlockIterator iter(this, info->mTrackNum);
771        int32_t i = 0;
772        int64_t thumbnailTimeUs = 0;
773        size_t maxBlockSize = 0;
774        while (!iter.eos() && i < 20) {
775            if (iter.block()->IsKey()) {
776                ++i;
777
778                size_t blockSize = 0;
779                for (int i = 0; i < iter.block()->GetFrameCount(); ++i) {
780                    blockSize += iter.block()->GetFrame(i).len;
781                }
782
783                if (blockSize > maxBlockSize) {
784                    maxBlockSize = blockSize;
785                    thumbnailTimeUs = iter.blockTimeUs();
786                }
787            }
788            iter.advance();
789        }
790        info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
791    }
792}
793
794sp<MetaData> MatroskaExtractor::getMetaData() {
795    sp<MetaData> meta = new MetaData;
796
797    meta->setCString(
798            kKeyMIMEType,
799            mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA);
800
801    return meta;
802}
803
804uint32_t MatroskaExtractor::flags() const {
805    uint32_t x = CAN_PAUSE;
806    if (!isLiveStreaming()) {
807        x |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
808    }
809
810    return x;
811}
812
813bool SniffMatroska(
814        const sp<DataSource> &source, String8 *mimeType, float *confidence,
815        sp<AMessage> *) {
816    DataSourceReader reader(source);
817    mkvparser::EBMLHeader ebmlHeader;
818    long long pos;
819    if (ebmlHeader.Parse(&reader, pos) < 0) {
820        return false;
821    }
822
823    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA);
824    *confidence = 0.6;
825
826    return true;
827}
828
829}  // namespace android
830