NuMediaExtractor.cpp revision dbcb4c17f16230dfc6af908f176681c49f61b13e
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    } else if (mImpl->getDrmFlag()) {
114        // For all other drm content, we don't want to expose decrypted
115        // content to Java application.
116        mImpl.clear();
117        mImpl = NULL;
118        return ERROR_UNSUPPORTED;
119    }
120
121    mDataSource = dataSource;
122
123    updateDurationAndBitrate();
124
125    return OK;
126}
127
128status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
129    Mutex::Autolock autoLock(mLock);
130
131    if (mImpl != NULL) {
132        return -EINVAL;
133    }
134
135    sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
136
137    return setDataSource(fileSource);
138}
139
140status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) {
141    Mutex::Autolock autoLock(mLock);
142
143    if (mImpl != NULL) {
144        return -EINVAL;
145    }
146
147    status_t err = source->initCheck();
148    if (err != OK) {
149        return err;
150    }
151
152    mImpl = MediaExtractor::Create(source);
153
154    if (mImpl == NULL) {
155        return ERROR_UNSUPPORTED;
156    }
157
158    mDataSource = source;
159
160    updateDurationAndBitrate();
161
162    return OK;
163}
164
165void NuMediaExtractor::updateDurationAndBitrate() {
166    mTotalBitrate = 0ll;
167    mDurationUs = -1ll;
168
169    for (size_t i = 0; i < mImpl->countTracks(); ++i) {
170        sp<MetaData> meta = mImpl->getTrackMetaData(i);
171
172        int32_t bitrate;
173        if (!meta->findInt32(kKeyBitRate, &bitrate)) {
174            const char *mime;
175            CHECK(meta->findCString(kKeyMIMEType, &mime));
176            ALOGV("track of type '%s' does not publish bitrate", mime);
177
178            mTotalBitrate = -1ll;
179        } else if (mTotalBitrate >= 0ll) {
180            mTotalBitrate += bitrate;
181        }
182
183        int64_t durationUs;
184        if (meta->findInt64(kKeyDuration, &durationUs)
185                && durationUs > mDurationUs) {
186            mDurationUs = durationUs;
187        }
188    }
189}
190
191size_t NuMediaExtractor::countTracks() const {
192    Mutex::Autolock autoLock(mLock);
193
194    return mImpl == NULL ? 0 : mImpl->countTracks();
195}
196
197status_t NuMediaExtractor::getTrackFormat(
198        size_t index, sp<AMessage> *format) const {
199    Mutex::Autolock autoLock(mLock);
200
201    *format = NULL;
202
203    if (mImpl == NULL) {
204        return -EINVAL;
205    }
206
207    if (index >= mImpl->countTracks()) {
208        return -ERANGE;
209    }
210
211    sp<MetaData> meta = mImpl->getTrackMetaData(index);
212    return convertMetaDataToMessage(meta, format);
213}
214
215status_t NuMediaExtractor::selectTrack(size_t index) {
216    Mutex::Autolock autoLock(mLock);
217
218    if (mImpl == NULL) {
219        return -EINVAL;
220    }
221
222    if (index >= mImpl->countTracks()) {
223        return -ERANGE;
224    }
225
226    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
227        TrackInfo *info = &mSelectedTracks.editItemAt(i);
228
229        if (info->mTrackIndex == index) {
230            // This track has already been selected.
231            return OK;
232        }
233    }
234
235    sp<MediaSource> source = mImpl->getTrack(index);
236
237    CHECK_EQ((status_t)OK, source->start());
238
239    mSelectedTracks.push();
240    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
241
242    info->mSource = source;
243    info->mTrackIndex = index;
244    info->mFinalResult = OK;
245    info->mSample = NULL;
246    info->mSampleTimeUs = -1ll;
247    info->mTrackFlags = 0;
248
249    const char *mime;
250    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
251
252    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
253        info->mTrackFlags |= kIsVorbis;
254    }
255
256    return OK;
257}
258
259status_t NuMediaExtractor::unselectTrack(size_t index) {
260    Mutex::Autolock autoLock(mLock);
261
262    if (mImpl == NULL) {
263        return -EINVAL;
264    }
265
266    if (index >= mImpl->countTracks()) {
267        return -ERANGE;
268    }
269
270    size_t i;
271    for (i = 0; i < mSelectedTracks.size(); ++i) {
272        TrackInfo *info = &mSelectedTracks.editItemAt(i);
273
274        if (info->mTrackIndex == index) {
275            break;
276        }
277    }
278
279    if (i == mSelectedTracks.size()) {
280        // Not selected.
281        return OK;
282    }
283
284    TrackInfo *info = &mSelectedTracks.editItemAt(i);
285
286    if (info->mSample != NULL) {
287        info->mSample->release();
288        info->mSample = NULL;
289
290        info->mSampleTimeUs = -1ll;
291    }
292
293    CHECK_EQ((status_t)OK, info->mSource->stop());
294
295    mSelectedTracks.removeAt(i);
296
297    return OK;
298}
299
300void NuMediaExtractor::releaseTrackSamples() {
301    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
302        TrackInfo *info = &mSelectedTracks.editItemAt(i);
303
304        if (info->mSample != NULL) {
305            info->mSample->release();
306            info->mSample = NULL;
307
308            info->mSampleTimeUs = -1ll;
309        }
310    }
311}
312
313ssize_t NuMediaExtractor::fetchTrackSamples(
314        int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
315    TrackInfo *minInfo = NULL;
316    ssize_t minIndex = -1;
317
318    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
319        TrackInfo *info = &mSelectedTracks.editItemAt(i);
320
321        if (seekTimeUs >= 0ll) {
322            info->mFinalResult = OK;
323
324            if (info->mSample != NULL) {
325                info->mSample->release();
326                info->mSample = NULL;
327                info->mSampleTimeUs = -1ll;
328            }
329        } else if (info->mFinalResult != OK) {
330            continue;
331        }
332
333        if (info->mSample == NULL) {
334            MediaSource::ReadOptions options;
335            if (seekTimeUs >= 0ll) {
336                options.setSeekTo(seekTimeUs, mode);
337            }
338            status_t err = info->mSource->read(&info->mSample, &options);
339
340            if (err != OK) {
341                CHECK(info->mSample == NULL);
342
343                info->mFinalResult = err;
344
345                if (info->mFinalResult != ERROR_END_OF_STREAM) {
346                    ALOGW("read on track %d failed with error %d",
347                          info->mTrackIndex, err);
348                }
349
350                info->mSampleTimeUs = -1ll;
351                continue;
352            } else {
353                CHECK(info->mSample != NULL);
354                CHECK(info->mSample->meta_data()->findInt64(
355                            kKeyTime, &info->mSampleTimeUs));
356            }
357        }
358
359        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
360            minInfo = info;
361            minIndex = i;
362        }
363    }
364
365    return minIndex;
366}
367
368status_t NuMediaExtractor::seekTo(
369        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
370    Mutex::Autolock autoLock(mLock);
371
372    ssize_t minIndex = fetchTrackSamples(timeUs, mode);
373
374    if (minIndex < 0) {
375        return ERROR_END_OF_STREAM;
376    }
377
378    return OK;
379}
380
381status_t NuMediaExtractor::advance() {
382    Mutex::Autolock autoLock(mLock);
383
384    ssize_t minIndex = fetchTrackSamples();
385
386    if (minIndex < 0) {
387        return ERROR_END_OF_STREAM;
388    }
389
390    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
391
392    info->mSample->release();
393    info->mSample = NULL;
394    info->mSampleTimeUs = -1ll;
395
396    return OK;
397}
398
399status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
400    Mutex::Autolock autoLock(mLock);
401
402    ssize_t minIndex = fetchTrackSamples();
403
404    if (minIndex < 0) {
405        return ERROR_END_OF_STREAM;
406    }
407
408    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
409
410    size_t sampleSize = info->mSample->range_length();
411
412    if (info->mTrackFlags & kIsVorbis) {
413        // Each sample's data is suffixed by the number of page samples
414        // or -1 if not available.
415        sampleSize += sizeof(int32_t);
416    }
417
418    if (buffer->capacity() < sampleSize) {
419        return -ENOMEM;
420    }
421
422    const uint8_t *src =
423        (const uint8_t *)info->mSample->data()
424            + info->mSample->range_offset();
425
426    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
427
428    if (info->mTrackFlags & kIsVorbis) {
429        int32_t numPageSamples;
430        if (!info->mSample->meta_data()->findInt32(
431                    kKeyValidSamples, &numPageSamples)) {
432            numPageSamples = -1;
433        }
434
435        memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
436               &numPageSamples,
437               sizeof(numPageSamples));
438    }
439
440    buffer->setRange(0, sampleSize);
441
442    return OK;
443}
444
445status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
446    Mutex::Autolock autoLock(mLock);
447
448    ssize_t minIndex = fetchTrackSamples();
449
450    if (minIndex < 0) {
451        return ERROR_END_OF_STREAM;
452    }
453
454    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
455    *trackIndex = info->mTrackIndex;
456
457    return OK;
458}
459
460status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
461    Mutex::Autolock autoLock(mLock);
462
463    ssize_t minIndex = fetchTrackSamples();
464
465    if (minIndex < 0) {
466        return ERROR_END_OF_STREAM;
467    }
468
469    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
470    *sampleTimeUs = info->mSampleTimeUs;
471
472    return OK;
473}
474
475status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
476    Mutex::Autolock autoLock(mLock);
477
478    *sampleMeta = NULL;
479
480    ssize_t minIndex = fetchTrackSamples();
481
482    if (minIndex < 0) {
483        return ERROR_END_OF_STREAM;
484    }
485
486    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
487    *sampleMeta = info->mSample->meta_data();
488
489    return OK;
490}
491
492bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
493    if (mTotalBitrate >= 0) {
494        *bitrate = mTotalBitrate;
495        return true;
496    }
497
498    off64_t size;
499    if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) {
500        *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
501        return true;
502    }
503
504    return false;
505}
506
507// Returns true iff cached duration is available/applicable.
508bool NuMediaExtractor::getCachedDuration(
509        int64_t *durationUs, bool *eos) const {
510    Mutex::Autolock autoLock(mLock);
511
512    int64_t bitrate;
513    if (mIsWidevineExtractor) {
514        sp<WVMExtractor> wvmExtractor =
515            static_cast<WVMExtractor *>(mImpl.get());
516
517        status_t finalStatus;
518        *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
519        *eos = (finalStatus != OK);
520        return true;
521    } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
522            && getTotalBitrate(&bitrate)) {
523        sp<NuCachedSource2> cachedSource =
524            static_cast<NuCachedSource2 *>(mDataSource.get());
525
526        status_t finalStatus;
527        size_t cachedDataRemaining =
528            cachedSource->approxDataRemaining(&finalStatus);
529
530        *durationUs = cachedDataRemaining * 8000000ll / bitrate;
531        *eos = (finalStatus != OK);
532        return true;
533    }
534
535    return false;
536}
537
538}  // namespace android
539