MatroskaExtractor.cpp revision 74a0a0d7f766d63330a00c3fa8f133c44c1d5be6
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        LOGV("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        LOGV("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            LOGV("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            LOGV("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            LOGV("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    size_t size = frame->range_length();
427
428    if (mType != AVC) {
429        *out = frame;
430
431        return OK;
432    }
433
434    if (size < mNALSizeLen) {
435        frame->release();
436        frame = NULL;
437
438        return ERROR_MALFORMED;
439    }
440
441    // In the case of AVC content, each NAL unit is prefixed by
442    // mNALSizeLen bytes of length. We want to prefix the data with
443    // a four-byte 0x00000001 startcode instead of the length prefix.
444    // mNALSizeLen ranges from 1 through 4 bytes, so add an extra
445    // 3 bytes of padding to the buffer start.
446    static const size_t kPadding = 3;
447
448    MediaBuffer *buffer = new MediaBuffer(size + kPadding);
449
450    int64_t timeUs;
451    CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs));
452    int32_t isSync;
453    CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync));
454
455    buffer->meta_data()->setInt64(kKeyTime, timeUs);
456    buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
457
458    memcpy((uint8_t *)buffer->data() + kPadding,
459           (const uint8_t *)frame->data() + frame->range_offset(),
460           size);
461
462    buffer->set_range(kPadding, size);
463
464    frame->release();
465    frame = NULL;
466
467    uint8_t *data = (uint8_t *)buffer->data();
468
469    size_t NALsize;
470    switch (mNALSizeLen) {
471        case 1: NALsize = data[kPadding]; break;
472        case 2: NALsize = U16_AT(&data[kPadding]); break;
473        case 3: NALsize = U24_AT(&data[kPadding]); break;
474        case 4: NALsize = U32_AT(&data[kPadding]); break;
475        default:
476            TRESPASS();
477    }
478
479    if (size < NALsize + mNALSizeLen) {
480        buffer->release();
481        buffer = NULL;
482
483        return ERROR_MALFORMED;
484    }
485
486    if (size > NALsize + mNALSizeLen) {
487        LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen);
488    }
489
490    // actual data starts at &data[kPadding + mNALSizeLen]
491
492    memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4);
493    buffer->set_range(mNALSizeLen - 1, NALsize + 4);
494
495    *out = buffer;
496
497    return OK;
498}
499
500////////////////////////////////////////////////////////////////////////////////
501
502MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source)
503    : mDataSource(source),
504      mReader(new DataSourceReader(mDataSource)),
505      mSegment(NULL),
506      mExtractedThumbnails(false),
507      mIsWebm(false) {
508    off64_t size;
509    mIsLiveStreaming =
510        (mDataSource->flags()
511            & (DataSource::kWantsPrefetching
512                | DataSource::kIsCachingDataSource))
513        && mDataSource->getSize(&size) != OK;
514
515    mkvparser::EBMLHeader ebmlHeader;
516    long long pos;
517    if (ebmlHeader.Parse(mReader, pos) < 0) {
518        return;
519    }
520
521    if (ebmlHeader.m_docType && !strcmp("webm", ebmlHeader.m_docType)) {
522        mIsWebm = true;
523    }
524
525    long long ret =
526        mkvparser::Segment::CreateInstance(mReader, pos, mSegment);
527
528    if (ret) {
529        CHECK(mSegment == NULL);
530        return;
531    }
532
533    if (isLiveStreaming()) {
534        ret = mSegment->ParseHeaders();
535        CHECK_EQ(ret, 0);
536
537        long len;
538        ret = mSegment->LoadCluster(pos, len);
539        CHECK_EQ(ret, 0);
540    } else {
541        ret = mSegment->Load();
542    }
543
544    if (ret < 0) {
545        delete mSegment;
546        mSegment = NULL;
547        return;
548    }
549
550#if 0
551    const mkvparser::SegmentInfo *info = mSegment->GetInfo();
552    LOGI("muxing app: %s, writing app: %s",
553         info->GetMuxingAppAsUTF8(),
554         info->GetWritingAppAsUTF8());
555#endif
556
557    addTracks();
558}
559
560MatroskaExtractor::~MatroskaExtractor() {
561    delete mSegment;
562    mSegment = NULL;
563
564    delete mReader;
565    mReader = NULL;
566}
567
568size_t MatroskaExtractor::countTracks() {
569    return mTracks.size();
570}
571
572sp<MediaSource> MatroskaExtractor::getTrack(size_t index) {
573    if (index >= mTracks.size()) {
574        return NULL;
575    }
576
577    return new MatroskaSource(this, index);
578}
579
580sp<MetaData> MatroskaExtractor::getTrackMetaData(
581        size_t index, uint32_t flags) {
582    if (index >= mTracks.size()) {
583        return NULL;
584    }
585
586    if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails
587            && !isLiveStreaming()) {
588        findThumbnails();
589        mExtractedThumbnails = true;
590    }
591
592    return mTracks.itemAt(index).mMeta;
593}
594
595bool MatroskaExtractor::isLiveStreaming() const {
596    return mIsLiveStreaming;
597}
598
599static void addESDSFromAudioSpecificInfo(
600        const sp<MetaData> &meta, const void *asi, size_t asiSize) {
601    static const uint8_t kStaticESDS[] = {
602        0x03, 22,
603        0x00, 0x00,     // ES_ID
604        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
605
606        0x04, 17,
607        0x40,                       // Audio ISO/IEC 14496-3
608        0x00, 0x00, 0x00, 0x00,
609        0x00, 0x00, 0x00, 0x00,
610        0x00, 0x00, 0x00, 0x00,
611
612        0x05,
613        // AudioSpecificInfo (with size prefix) follows
614    };
615
616    // Make sure all sizes can be coded in a single byte.
617    CHECK(asiSize + 22 - 2 < 128);
618    size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1;
619    uint8_t *esds = new uint8_t[esdsSize];
620    memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
621    uint8_t *ptr = esds + sizeof(kStaticESDS);
622    *ptr++ = asiSize;
623    memcpy(ptr, asi, asiSize);
624
625    // Increment by codecPrivateSize less 2 bytes that are accounted for
626    // already in lengths of 22/17
627    esds[1] += asiSize - 2;
628    esds[6] += asiSize - 2;
629
630    meta->setData(kKeyESDS, 0, esds, esdsSize);
631
632    delete[] esds;
633    esds = NULL;
634}
635
636void addVorbisCodecInfo(
637        const sp<MetaData> &meta,
638        const void *_codecPrivate, size_t codecPrivateSize) {
639    // printf("vorbis private data follows:\n");
640    // hexdump(_codecPrivate, codecPrivateSize);
641
642    CHECK(codecPrivateSize >= 3);
643
644    const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate;
645    CHECK(codecPrivate[0] == 0x02);
646
647    size_t len1 = codecPrivate[1];
648    size_t len2 = codecPrivate[2];
649
650    CHECK(codecPrivateSize > 3 + len1 + len2);
651
652    CHECK(codecPrivate[3] == 0x01);
653    meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1);
654
655    CHECK(codecPrivate[len1 + 3] == 0x03);
656
657    CHECK(codecPrivate[len1 + len2 + 3] == 0x05);
658    meta->setData(
659            kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3],
660            codecPrivateSize - len1 - len2 - 3);
661}
662
663void MatroskaExtractor::addTracks() {
664    const mkvparser::Tracks *tracks = mSegment->GetTracks();
665
666    for (size_t index = 0; index < tracks->GetTracksCount(); ++index) {
667        const mkvparser::Track *track = tracks->GetTrackByIndex(index);
668
669        if (track == NULL) {
670            // Apparently this is currently valid (if unexpected) behaviour
671            // of the mkv parser lib.
672            continue;
673        }
674
675        const char *const codecID = track->GetCodecId();
676        LOGV("codec id = %s", codecID);
677        LOGV("codec name = %s", track->GetCodecNameAsUTF8());
678
679        size_t codecPrivateSize;
680        const unsigned char *codecPrivate =
681            track->GetCodecPrivate(codecPrivateSize);
682
683        enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
684
685        sp<MetaData> meta = new MetaData;
686
687        switch (track->GetType()) {
688            case VIDEO_TRACK:
689            {
690                const mkvparser::VideoTrack *vtrack =
691                    static_cast<const mkvparser::VideoTrack *>(track);
692
693                if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
694                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
695                    meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
696                } else if (!strcmp("V_VP8", codecID)) {
697                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX);
698                } else {
699                    continue;
700                }
701
702                meta->setInt32(kKeyWidth, vtrack->GetWidth());
703                meta->setInt32(kKeyHeight, vtrack->GetHeight());
704                break;
705            }
706
707            case AUDIO_TRACK:
708            {
709                const mkvparser::AudioTrack *atrack =
710                    static_cast<const mkvparser::AudioTrack *>(track);
711
712                if (!strcmp("A_AAC", codecID)) {
713                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
714                    CHECK(codecPrivateSize >= 2);
715
716                    addESDSFromAudioSpecificInfo(
717                            meta, codecPrivate, codecPrivateSize);
718                } else if (!strcmp("A_VORBIS", codecID)) {
719                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
720
721                    addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize);
722                } else {
723                    continue;
724                }
725
726                meta->setInt32(kKeySampleRate, atrack->GetSamplingRate());
727                meta->setInt32(kKeyChannelCount, atrack->GetChannels());
728                break;
729            }
730
731            default:
732                continue;
733        }
734
735        long long durationNs = mSegment->GetDuration();
736        meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
737
738        mTracks.push();
739        TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
740        trackInfo->mTrackNum = track->GetNumber();
741        trackInfo->mMeta = meta;
742    }
743}
744
745void MatroskaExtractor::findThumbnails() {
746    for (size_t i = 0; i < mTracks.size(); ++i) {
747        TrackInfo *info = &mTracks.editItemAt(i);
748
749        const char *mime;
750        CHECK(info->mMeta->findCString(kKeyMIMEType, &mime));
751
752        if (strncasecmp(mime, "video/", 6)) {
753            continue;
754        }
755
756        BlockIterator iter(this, info->mTrackNum);
757        int32_t i = 0;
758        int64_t thumbnailTimeUs = 0;
759        size_t maxBlockSize = 0;
760        while (!iter.eos() && i < 20) {
761            if (iter.block()->IsKey()) {
762                ++i;
763
764                size_t blockSize = 0;
765                for (int i = 0; i < iter.block()->GetFrameCount(); ++i) {
766                    blockSize += iter.block()->GetFrame(i).len;
767                }
768
769                if (blockSize > maxBlockSize) {
770                    maxBlockSize = blockSize;
771                    thumbnailTimeUs = iter.blockTimeUs();
772                }
773            }
774            iter.advance();
775        }
776        info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
777    }
778}
779
780sp<MetaData> MatroskaExtractor::getMetaData() {
781    sp<MetaData> meta = new MetaData;
782
783    meta->setCString(
784            kKeyMIMEType,
785            mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA);
786
787    return meta;
788}
789
790uint32_t MatroskaExtractor::flags() const {
791    uint32_t x = CAN_PAUSE;
792    if (!isLiveStreaming()) {
793        x |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
794    }
795
796    return x;
797}
798
799bool SniffMatroska(
800        const sp<DataSource> &source, String8 *mimeType, float *confidence,
801        sp<AMessage> *) {
802    DataSourceReader reader(source);
803    mkvparser::EBMLHeader ebmlHeader;
804    long long pos;
805    if (ebmlHeader.Parse(&reader, pos) < 0) {
806        return false;
807    }
808
809    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA);
810    *confidence = 0.6;
811
812    return true;
813}
814
815}  // namespace android
816