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