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