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