NuMediaExtractor.cpp revision a53d87c7b1428fe02f535c31dafd64cb1362fde9
1/*
2 * Copyright 2012, 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 "NuMediaExtractor"
19#include <utils/Log.h>
20
21#include <media/stagefright/NuMediaExtractor.h>
22
23#include "include/ESDS.h"
24#include "include/NuCachedSource2.h"
25#include "include/WVMExtractor.h"
26
27#include <media/stagefright/foundation/ABuffer.h>
28#include <media/stagefright/foundation/ADebug.h>
29#include <media/stagefright/foundation/AMessage.h>
30#include <media/stagefright/DataSource.h>
31#include <media/stagefright/FileSource.h>
32#include <media/stagefright/MediaBuffer.h>
33#include <media/stagefright/MediaDefs.h>
34#include <media/stagefright/MediaErrors.h>
35#include <media/stagefright/MediaExtractor.h>
36#include <media/stagefright/MediaSource.h>
37#include <media/stagefright/MetaData.h>
38#include <media/stagefright/Utils.h>
39
40namespace android {
41
42NuMediaExtractor::NuMediaExtractor()
43    : mIsWidevineExtractor(false),
44      mTotalBitrate(-1ll),
45      mDurationUs(-1ll) {
46}
47
48NuMediaExtractor::~NuMediaExtractor() {
49    releaseTrackSamples();
50
51    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
52        TrackInfo *info = &mSelectedTracks.editItemAt(i);
53
54        CHECK_EQ((status_t)OK, info->mSource->stop());
55    }
56
57    mSelectedTracks.clear();
58}
59
60status_t NuMediaExtractor::setDataSource(
61        const char *path, const KeyedVector<String8, String8> *headers) {
62    if (mImpl != NULL) {
63        return -EINVAL;
64    }
65
66    sp<DataSource> dataSource =
67        DataSource::CreateFromURI(path, headers);
68
69    if (dataSource == NULL) {
70        return -ENOENT;
71    }
72
73    mIsWidevineExtractor = false;
74    if (!strncasecmp("widevine://", path, 11)) {
75        String8 mimeType;
76        float confidence;
77        sp<AMessage> dummy;
78        bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
79
80        if (!success
81                || strcasecmp(
82                    mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
83            return ERROR_UNSUPPORTED;
84        }
85
86        sp<WVMExtractor> extractor = new WVMExtractor(dataSource);
87        extractor->setAdaptiveStreamingMode(true);
88
89        mImpl = extractor;
90        mIsWidevineExtractor = true;
91    } else {
92        mImpl = MediaExtractor::Create(dataSource);
93    }
94
95    if (mImpl == NULL) {
96        return ERROR_UNSUPPORTED;
97    }
98
99    mDataSource = dataSource;
100
101    updateDurationAndBitrate();
102
103    return OK;
104}
105
106status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
107    if (mImpl != NULL) {
108        return -EINVAL;
109    }
110
111    sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
112
113    status_t err = fileSource->initCheck();
114    if (err != OK) {
115        return err;
116    }
117
118    mImpl = MediaExtractor::Create(fileSource);
119
120    if (mImpl == NULL) {
121        return ERROR_UNSUPPORTED;
122    }
123
124    mDataSource = fileSource;
125
126    updateDurationAndBitrate();
127
128    return OK;
129}
130
131void NuMediaExtractor::updateDurationAndBitrate() {
132    mTotalBitrate = 0ll;
133    mDurationUs = -1ll;
134
135    for (size_t i = 0; i < mImpl->countTracks(); ++i) {
136        sp<MetaData> meta = mImpl->getTrackMetaData(i);
137
138        int32_t bitrate;
139        if (!meta->findInt32(kKeyBitRate, &bitrate)) {
140            const char *mime;
141            CHECK(meta->findCString(kKeyMIMEType, &mime));
142            ALOGV("track of type '%s' does not publish bitrate", mime);
143
144            mTotalBitrate = -1ll;
145        } else if (mTotalBitrate >= 0ll) {
146            mTotalBitrate += bitrate;
147        }
148
149        int64_t durationUs;
150        if (meta->findInt64(kKeyDuration, &durationUs)
151                && durationUs > mDurationUs) {
152            mDurationUs = durationUs;
153        }
154    }
155}
156
157size_t NuMediaExtractor::countTracks() const {
158    return mImpl == NULL ? 0 : mImpl->countTracks();
159}
160
161status_t NuMediaExtractor::getTrackFormat(
162        size_t index, sp<AMessage> *format) const {
163    *format = NULL;
164
165    if (mImpl == NULL) {
166        return -EINVAL;
167    }
168
169    if (index >= mImpl->countTracks()) {
170        return -ERANGE;
171    }
172
173    sp<MetaData> meta = mImpl->getTrackMetaData(index);
174
175    const char *mime;
176    CHECK(meta->findCString(kKeyMIMEType, &mime));
177
178    sp<AMessage> msg = new AMessage;
179    msg->setString("mime", mime);
180
181    int64_t durationUs;
182    if (meta->findInt64(kKeyDuration, &durationUs)) {
183        msg->setInt64("durationUs", durationUs);
184    }
185
186    if (!strncasecmp("video/", mime, 6)) {
187        int32_t width, height;
188        CHECK(meta->findInt32(kKeyWidth, &width));
189        CHECK(meta->findInt32(kKeyHeight, &height));
190
191        msg->setInt32("width", width);
192        msg->setInt32("height", height);
193    } else {
194        CHECK(!strncasecmp("audio/", mime, 6));
195
196        int32_t numChannels, sampleRate;
197        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
198        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
199
200        msg->setInt32("channel-count", numChannels);
201        msg->setInt32("sample-rate", sampleRate);
202
203        int32_t isADTS;
204        if (meta->findInt32(kKeyIsADTS, &isADTS)) {
205            msg->setInt32("is-adts", true);
206        }
207    }
208
209    int32_t maxInputSize;
210    if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
211        msg->setInt32("max-input-size", maxInputSize);
212    }
213
214    uint32_t type;
215    const void *data;
216    size_t size;
217    if (meta->findData(kKeyAVCC, &type, &data, &size)) {
218        // Parse the AVCDecoderConfigurationRecord
219
220        const uint8_t *ptr = (const uint8_t *)data;
221
222        CHECK(size >= 7);
223        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
224        uint8_t profile = ptr[1];
225        uint8_t level = ptr[3];
226
227        // There is decodable content out there that fails the following
228        // assertion, let's be lenient for now...
229        // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
230
231        size_t lengthSize = 1 + (ptr[4] & 3);
232
233        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
234        // violates it...
235        // CHECK((ptr[5] >> 5) == 7);  // reserved
236
237        size_t numSeqParameterSets = ptr[5] & 31;
238
239        ptr += 6;
240        size -= 6;
241
242        sp<ABuffer> buffer = new ABuffer(1024);
243        buffer->setRange(0, 0);
244
245        for (size_t i = 0; i < numSeqParameterSets; ++i) {
246            CHECK(size >= 2);
247            size_t length = U16_AT(ptr);
248
249            ptr += 2;
250            size -= 2;
251
252            CHECK(size >= length);
253
254            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
255            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
256            buffer->setRange(0, buffer->size() + 4 + length);
257
258            ptr += length;
259            size -= length;
260        }
261
262        buffer->meta()->setInt32("csd", true);
263        buffer->meta()->setInt64("timeUs", 0);
264
265        msg->setBuffer("csd-0", buffer);
266
267        buffer = new ABuffer(1024);
268        buffer->setRange(0, 0);
269
270        CHECK(size >= 1);
271        size_t numPictureParameterSets = *ptr;
272        ++ptr;
273        --size;
274
275        for (size_t i = 0; i < numPictureParameterSets; ++i) {
276            CHECK(size >= 2);
277            size_t length = U16_AT(ptr);
278
279            ptr += 2;
280            size -= 2;
281
282            CHECK(size >= length);
283
284            memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
285            memcpy(buffer->data() + buffer->size() + 4, ptr, length);
286            buffer->setRange(0, buffer->size() + 4 + length);
287
288            ptr += length;
289            size -= length;
290        }
291
292        buffer->meta()->setInt32("csd", true);
293        buffer->meta()->setInt64("timeUs", 0);
294        msg->setBuffer("csd-1", buffer);
295    } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
296        ESDS esds((const char *)data, size);
297        CHECK_EQ(esds.InitCheck(), (status_t)OK);
298
299        const void *codec_specific_data;
300        size_t codec_specific_data_size;
301        esds.getCodecSpecificInfo(
302                &codec_specific_data, &codec_specific_data_size);
303
304        sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
305
306        memcpy(buffer->data(), codec_specific_data,
307               codec_specific_data_size);
308
309        buffer->meta()->setInt32("csd", true);
310        buffer->meta()->setInt64("timeUs", 0);
311        msg->setBuffer("csd-0", buffer);
312    } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
313        sp<ABuffer> buffer = new ABuffer(size);
314        memcpy(buffer->data(), data, size);
315
316        buffer->meta()->setInt32("csd", true);
317        buffer->meta()->setInt64("timeUs", 0);
318        msg->setBuffer("csd-0", buffer);
319
320        if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
321            return -EINVAL;
322        }
323
324        buffer = new ABuffer(size);
325        memcpy(buffer->data(), data, size);
326
327        buffer->meta()->setInt32("csd", true);
328        buffer->meta()->setInt64("timeUs", 0);
329        msg->setBuffer("csd-1", buffer);
330    }
331
332    *format = msg;
333
334    return OK;
335}
336
337status_t NuMediaExtractor::selectTrack(size_t index) {
338    if (mImpl == NULL) {
339        return -EINVAL;
340    }
341
342    if (index >= mImpl->countTracks()) {
343        return -ERANGE;
344    }
345
346    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
347        TrackInfo *info = &mSelectedTracks.editItemAt(i);
348
349        if (info->mTrackIndex == index) {
350            // This track has already been selected.
351            return OK;
352        }
353    }
354
355    sp<MediaSource> source = mImpl->getTrack(index);
356
357    CHECK_EQ((status_t)OK, source->start());
358
359    mSelectedTracks.push();
360    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
361
362    info->mSource = source;
363    info->mTrackIndex = index;
364    info->mFinalResult = OK;
365    info->mSample = NULL;
366    info->mSampleTimeUs = -1ll;
367    info->mTrackFlags = 0;
368
369    const char *mime;
370    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
371
372    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
373        info->mTrackFlags |= kIsVorbis;
374    }
375
376    return OK;
377}
378
379void NuMediaExtractor::releaseTrackSamples() {
380    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
381        TrackInfo *info = &mSelectedTracks.editItemAt(i);
382
383        if (info->mSample != NULL) {
384            info->mSample->release();
385            info->mSample = NULL;
386
387            info->mSampleTimeUs = -1ll;
388        }
389    }
390}
391
392ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
393    TrackInfo *minInfo = NULL;
394    ssize_t minIndex = -1;
395
396    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
397        TrackInfo *info = &mSelectedTracks.editItemAt(i);
398
399        if (seekTimeUs >= 0ll) {
400            info->mFinalResult = OK;
401
402            if (info->mSample != NULL) {
403                info->mSample->release();
404                info->mSample = NULL;
405                info->mSampleTimeUs = -1ll;
406            }
407        } else if (info->mFinalResult != OK) {
408            continue;
409        }
410
411        if (info->mSample == NULL) {
412            MediaSource::ReadOptions options;
413            if (seekTimeUs >= 0ll) {
414                options.setSeekTo(seekTimeUs);
415            }
416            status_t err = info->mSource->read(&info->mSample, &options);
417
418            if (err != OK) {
419                CHECK(info->mSample == NULL);
420
421                info->mFinalResult = err;
422                info->mSampleTimeUs = -1ll;
423                continue;
424            } else {
425                CHECK(info->mSample != NULL);
426                CHECK(info->mSample->meta_data()->findInt64(
427                            kKeyTime, &info->mSampleTimeUs));
428            }
429        }
430
431        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
432            minInfo = info;
433            minIndex = i;
434        }
435    }
436
437    return minIndex;
438}
439
440status_t NuMediaExtractor::seekTo(int64_t timeUs) {
441    ssize_t minIndex = fetchTrackSamples(timeUs);
442
443    if (minIndex < 0) {
444        return ERROR_END_OF_STREAM;
445    }
446
447    return OK;
448}
449
450status_t NuMediaExtractor::advance() {
451    ssize_t minIndex = fetchTrackSamples();
452
453    if (minIndex < 0) {
454        return ERROR_END_OF_STREAM;
455    }
456
457    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
458
459    info->mSample->release();
460    info->mSample = NULL;
461    info->mSampleTimeUs = -1ll;
462
463    return OK;
464}
465
466status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
467    ssize_t minIndex = fetchTrackSamples();
468
469    if (minIndex < 0) {
470        return ERROR_END_OF_STREAM;
471    }
472
473    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
474
475    size_t sampleSize = info->mSample->range_length();
476
477    if (info->mTrackFlags & kIsVorbis) {
478        // Each sample's data is suffixed by the number of page samples
479        // or -1 if not available.
480        sampleSize += sizeof(int32_t);
481    }
482
483    if (buffer->capacity() < sampleSize) {
484        return -ENOMEM;
485    }
486
487    const uint8_t *src =
488        (const uint8_t *)info->mSample->data()
489            + info->mSample->range_offset();
490
491    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
492
493    if (info->mTrackFlags & kIsVorbis) {
494        int32_t numPageSamples;
495        if (!info->mSample->meta_data()->findInt32(
496                    kKeyValidSamples, &numPageSamples)) {
497            numPageSamples = -1;
498        }
499
500        memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
501               &numPageSamples,
502               sizeof(numPageSamples));
503    }
504
505    buffer->setRange(0, sampleSize);
506
507    return OK;
508}
509
510status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
511    ssize_t minIndex = fetchTrackSamples();
512
513    if (minIndex < 0) {
514        return ERROR_END_OF_STREAM;
515    }
516
517    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
518    *trackIndex = info->mTrackIndex;
519
520    return OK;
521}
522
523status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
524    ssize_t minIndex = fetchTrackSamples();
525
526    if (minIndex < 0) {
527        return ERROR_END_OF_STREAM;
528    }
529
530    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
531    *sampleTimeUs = info->mSampleTimeUs;
532
533    return OK;
534}
535
536status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
537    *sampleMeta = NULL;
538
539    ssize_t minIndex = fetchTrackSamples();
540
541    if (minIndex < 0) {
542        return ERROR_END_OF_STREAM;
543    }
544
545    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
546    *sampleMeta = info->mSample->meta_data();
547
548    return OK;
549}
550
551bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
552    if (mTotalBitrate >= 0) {
553        *bitrate = mTotalBitrate;
554        return true;
555    }
556
557    off64_t size;
558    if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) {
559        *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
560        return true;
561    }
562
563    return false;
564}
565
566// Returns true iff cached duration is available/applicable.
567bool NuMediaExtractor::getCachedDuration(
568        int64_t *durationUs, bool *eos) const {
569    int64_t bitrate;
570    if (mIsWidevineExtractor) {
571        sp<WVMExtractor> wvmExtractor =
572            static_cast<WVMExtractor *>(mImpl.get());
573
574        status_t finalStatus;
575        *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
576        *eos = (finalStatus != OK);
577        return true;
578    } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
579            && getTotalBitrate(&bitrate)) {
580        sp<NuCachedSource2> cachedSource =
581            static_cast<NuCachedSource2 *>(mDataSource.get());
582
583        status_t finalStatus;
584        size_t cachedDataRemaining =
585            cachedSource->approxDataRemaining(&finalStatus);
586
587        *durationUs = cachedDataRemaining * 8000000ll / bitrate;
588        *eos = (finalStatus != OK);
589        return true;
590    }
591
592    return false;
593}
594
595}  // namespace android
596