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