MatroskaExtractor.cpp revision 072f5247ef893e683728263a540bb93daafda376
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/DataSource.h>
26#include <media/stagefright/MediaBuffer.h>
27#include <media/stagefright/MediaDebug.h>
28#include <media/stagefright/MediaDefs.h>
29#include <media/stagefright/MediaErrors.h>
30#include <media/stagefright/MediaSource.h>
31#include <media/stagefright/MetaData.h>
32#include <utils/String8.h>
33
34namespace android {
35
36struct DataSourceReader : public mkvparser::IMkvReader {
37    DataSourceReader(const sp<DataSource> &source)
38        : mSource(source) {
39    }
40
41    virtual int Read(long long position, long length, unsigned char* buffer) {
42        CHECK(position >= 0);
43        CHECK(length >= 0);
44
45        if (length == 0) {
46            return 0;
47        }
48
49        ssize_t n = mSource->readAt(position, buffer, length);
50
51        if (n <= 0) {
52            return -1;
53        }
54
55        return 0;
56    }
57
58    virtual int Length(long long* total, long long* available) {
59        off_t size;
60        if (mSource->getSize(&size) != OK) {
61            return -1;
62        }
63
64        if (total) {
65            *total = size;
66        }
67
68        if (available) {
69            *available = size;
70        }
71
72        return 0;
73    }
74
75private:
76    sp<DataSource> mSource;
77
78    DataSourceReader(const DataSourceReader &);
79    DataSourceReader &operator=(const DataSourceReader &);
80};
81
82////////////////////////////////////////////////////////////////////////////////
83
84#include <ctype.h>
85static void hexdump(const void *_data, size_t size) {
86    const uint8_t *data = (const uint8_t *)_data;
87    size_t offset = 0;
88    while (offset < size) {
89        printf("0x%04x  ", offset);
90
91        size_t n = size - offset;
92        if (n > 16) {
93            n = 16;
94        }
95
96        for (size_t i = 0; i < 16; ++i) {
97            if (i == 8) {
98                printf(" ");
99            }
100
101            if (offset + i < size) {
102                printf("%02x ", data[offset + i]);
103            } else {
104                printf("   ");
105            }
106        }
107
108        printf(" ");
109
110        for (size_t i = 0; i < n; ++i) {
111            if (isprint(data[offset + i])) {
112                printf("%c", data[offset + i]);
113            } else {
114                printf(".");
115            }
116        }
117
118        printf("\n");
119
120        offset += 16;
121    }
122}
123
124struct MatroskaSource : public MediaSource {
125    MatroskaSource(
126            const sp<MatroskaExtractor> &extractor, size_t index);
127
128    virtual status_t start(MetaData *params);
129    virtual status_t stop();
130
131    virtual sp<MetaData> getFormat();
132
133    virtual status_t read(
134            MediaBuffer **buffer, const ReadOptions *options);
135
136private:
137    enum Type {
138        AVC,
139        AAC,
140        OTHER
141    };
142
143    sp<MatroskaExtractor> mExtractor;
144    size_t mTrackIndex;
145    unsigned long mTrackNum;
146    Type mType;
147    mkvparser::Cluster *mCluster;
148    const mkvparser::BlockEntry *mBlockEntry;
149
150    status_t advance();
151
152    MatroskaSource(const MatroskaSource &);
153    MatroskaSource &operator=(const MatroskaSource &);
154};
155
156MatroskaSource::MatroskaSource(
157        const sp<MatroskaExtractor> &extractor, size_t index)
158    : mExtractor(extractor),
159      mTrackIndex(index),
160      mType(OTHER),
161      mCluster(NULL),
162      mBlockEntry(NULL) {
163    mTrackNum = mExtractor->mTracks.itemAt(index).mTrackNum;
164
165    const char *mime;
166    CHECK(mExtractor->mTracks.itemAt(index).mMeta->
167            findCString(kKeyMIMEType, &mime));
168
169    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
170        mType = AVC;
171    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
172        mType = AAC;
173    }
174}
175
176status_t MatroskaSource::start(MetaData *params) {
177    mCluster = NULL;
178    mBlockEntry = NULL;
179
180    return OK;
181}
182
183status_t MatroskaSource::stop() {
184    return OK;
185}
186
187sp<MetaData> MatroskaSource::getFormat() {
188    return mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
189}
190
191status_t MatroskaSource::advance() {
192    for (;;) {
193        if (mBlockEntry == NULL || mBlockEntry->EOS()) {
194            if (mCluster == NULL) {
195                mCluster = mExtractor->mSegment->GetFirst();
196            } else {
197                mCluster = mExtractor->mSegment->GetNext(mCluster);
198            }
199            if (mCluster == NULL || mCluster->EOS()) {
200                return ERROR_END_OF_STREAM;
201            }
202            mBlockEntry = mCluster->GetFirst();
203        }
204
205        if (mBlockEntry->GetBlock()->GetTrackNumber() != mTrackNum) {
206            mBlockEntry = mCluster->GetNext(mBlockEntry);
207            continue;
208        }
209
210        break;
211    }
212
213    return OK;
214}
215
216status_t MatroskaSource::read(
217        MediaBuffer **out, const ReadOptions *options) {
218    *out = NULL;
219
220    int64_t seekTimeUs;
221    if (options && options->getSeekTo(&seekTimeUs)) {
222        mBlockEntry = NULL;
223        mCluster = mExtractor->mSegment->GetCluster(seekTimeUs * 1000ll);
224
225        status_t err;
226        while ((err = advance()) == OK && !mBlockEntry->GetBlock()->IsKey()) {
227            mBlockEntry = mCluster->GetNext(mBlockEntry);
228        }
229
230        if (err != OK) {
231            return ERROR_END_OF_STREAM;
232        }
233    }
234
235    if (advance() != OK) {
236        return ERROR_END_OF_STREAM;
237    }
238
239    const mkvparser::Block *block = mBlockEntry->GetBlock();
240    size_t size = block->GetSize();
241    long long timeNs = block->GetTime(mCluster);
242
243    MediaBuffer *buffer = new MediaBuffer(size + 2);
244    buffer->meta_data()->setInt64(kKeyTime, (timeNs + 500) / 1000);
245
246    long res = block->Read(
247            mExtractor->mReader, (unsigned char *)buffer->data() + 2);
248
249    if (res != 0) {
250        return ERROR_END_OF_STREAM;
251    }
252
253    buffer->set_range(2, size);
254
255    if (mType == AVC) {
256        CHECK(size >= 2);
257
258        uint8_t *data = (uint8_t *)buffer->data();
259
260        unsigned NALsize = data[2] << 8 | data[3];
261        CHECK_EQ(size, NALsize + 2);
262
263        memcpy(data, "\x00\x00\x00\x01", 4);
264        buffer->set_range(0, size + 2);
265    } else if (mType == AAC) {
266        // There's strange junk at the beginning...
267
268        const uint8_t *data = (const uint8_t *)buffer->data() + 2;
269        size_t offset = 0;
270        while (offset < size && data[offset] != 0x21) {
271            ++offset;
272        }
273        buffer->set_range(2 + offset, size - offset);
274    }
275
276    *out = buffer;
277
278#if 0
279    hexdump((const uint8_t *)buffer->data() + buffer->range_offset(),
280            buffer->range_length());
281#endif
282
283    mBlockEntry = mCluster->GetNext(mBlockEntry);
284
285    return OK;
286}
287
288////////////////////////////////////////////////////////////////////////////////
289
290MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source)
291    : mDataSource(source),
292      mReader(new DataSourceReader(mDataSource)),
293      mSegment(NULL) {
294    mkvparser::EBMLHeader ebmlHeader;
295    long long pos;
296    if (ebmlHeader.Parse(mReader, pos) < 0) {
297        return;
298    }
299
300    long long ret =
301        mkvparser::Segment::CreateInstance(mReader, pos, mSegment);
302
303    if (ret) {
304        CHECK(mSegment == NULL);
305        return;
306    }
307
308    ret = mSegment->Load();
309
310    if (ret < 0) {
311        delete mSegment;
312        mSegment = NULL;
313        return;
314    }
315
316    addTracks();
317}
318
319MatroskaExtractor::~MatroskaExtractor() {
320    delete mSegment;
321    mSegment = NULL;
322
323    delete mReader;
324    mReader = NULL;
325}
326
327size_t MatroskaExtractor::countTracks() {
328    return mTracks.size();
329}
330
331sp<MediaSource> MatroskaExtractor::getTrack(size_t index) {
332    if (index >= mTracks.size()) {
333        return NULL;
334    }
335
336    return new MatroskaSource(this, index);
337}
338
339sp<MetaData> MatroskaExtractor::getTrackMetaData(
340        size_t index, uint32_t flags) {
341    if (index >= mTracks.size()) {
342        return NULL;
343    }
344
345    return mTracks.itemAt(index).mMeta;
346}
347
348static void addESDSFromAudioSpecificInfo(
349        const sp<MetaData> &meta, const void *asi, size_t asiSize) {
350    static const uint8_t kStaticESDS[] = {
351        0x03, 22,
352        0x00, 0x00,     // ES_ID
353        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
354
355        0x04, 17,
356        0x40,                       // Audio ISO/IEC 14496-3
357        0x00, 0x00, 0x00, 0x00,
358        0x00, 0x00, 0x00, 0x00,
359        0x00, 0x00, 0x00, 0x00,
360
361        0x05,
362        // AudioSpecificInfo (with size prefix) follows
363    };
364
365    CHECK(asiSize < 128);
366    size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1;
367    uint8_t *esds = new uint8_t[esdsSize];
368    memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
369    uint8_t *ptr = esds + sizeof(kStaticESDS);
370    *ptr++ = asiSize;
371    memcpy(ptr, asi, asiSize);
372
373    meta->setData(kKeyESDS, 0, esds, esdsSize);
374
375    delete[] esds;
376    esds = NULL;
377}
378
379void addVorbisCodecInfo(
380        const sp<MetaData> &meta,
381        const void *_codecPrivate, size_t codecPrivateSize) {
382    // printf("vorbis private data follows:\n");
383    // hexdump(_codecPrivate, codecPrivateSize);
384
385    CHECK(codecPrivateSize >= 3);
386
387    const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate;
388    CHECK(codecPrivate[0] == 0x02);
389
390    size_t len1 = codecPrivate[1];
391    size_t len2 = codecPrivate[2];
392
393    CHECK(codecPrivateSize > 3 + len1 + len2);
394
395    CHECK(codecPrivate[3] == 0x01);
396    meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1);
397
398    CHECK(codecPrivate[len1 + 3] == 0x03);
399
400    CHECK(codecPrivate[len1 + len2 + 3] == 0x05);
401    meta->setData(
402            kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3],
403            codecPrivateSize - len1 - len2 - 3);
404}
405
406void MatroskaExtractor::addTracks() {
407    const mkvparser::Tracks *tracks = mSegment->GetTracks();
408
409    for (size_t index = 0; index < tracks->GetTracksCount(); ++index) {
410        const mkvparser::Track *track = tracks->GetTrackByIndex(index);
411
412        const char *const codecID = track->GetCodecId();
413        LOGV("codec id = %s", codecID);
414        LOGV("codec name = %s", track->GetCodecNameAsUTF8());
415
416        size_t codecPrivateSize;
417        const unsigned char *codecPrivate =
418            track->GetCodecPrivate(&codecPrivateSize);
419
420        enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
421
422        sp<MetaData> meta = new MetaData;
423
424        switch (track->GetType()) {
425            case VIDEO_TRACK:
426            {
427                const mkvparser::VideoTrack *vtrack =
428                    static_cast<const mkvparser::VideoTrack *>(track);
429
430                if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
431                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
432                    meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
433                } else if (!strcmp("V_VP8", codecID)) {
434                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX);
435                } else {
436                    continue;
437                }
438
439                meta->setInt32(kKeyWidth, vtrack->GetWidth());
440                meta->setInt32(kKeyHeight, vtrack->GetHeight());
441                break;
442            }
443
444            case AUDIO_TRACK:
445            {
446                const mkvparser::AudioTrack *atrack =
447                    static_cast<const mkvparser::AudioTrack *>(track);
448
449                if (!strcmp("A_AAC", codecID)) {
450                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
451                    CHECK(codecPrivateSize >= 2);
452
453                    addESDSFromAudioSpecificInfo(
454                            meta, codecPrivate, codecPrivateSize);
455                } else if (!strcmp("A_VORBIS", codecID)) {
456                    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
457
458                    addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize);
459                } else {
460                    continue;
461                }
462
463                meta->setInt32(kKeySampleRate, atrack->GetSamplingRate());
464                meta->setInt32(kKeyChannelCount, atrack->GetChannels());
465                break;
466            }
467
468            default:
469                continue;
470        }
471
472        long long durationNs = mSegment->GetDuration();
473        meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
474
475        mTracks.push();
476        TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
477        trackInfo->mTrackNum = track->GetNumber();
478        trackInfo->mMeta = meta;
479    }
480}
481
482sp<MetaData> MatroskaExtractor::getMetaData() {
483    sp<MetaData> meta = new MetaData;
484    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA);
485
486    return meta;
487}
488
489bool SniffMatroska(
490        const sp<DataSource> &source, String8 *mimeType, float *confidence) {
491    DataSourceReader reader(source);
492    mkvparser::EBMLHeader ebmlHeader;
493    long long pos;
494    if (ebmlHeader.Parse(&reader, pos) < 0) {
495        return false;
496    }
497
498    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA);
499    *confidence = 0.6;
500
501    return true;
502}
503
504}  // namespace android
505