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