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