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