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