NuMediaExtractor.cpp revision e4d40ad91f3516bde106658673b2cb076b5dfcb7
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 used in AwesomePlayer.
114        // TODO: change default mode
115        static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true);
116    } else if (mImpl->getDrmFlag()) {
117        // For all other drm content, we don't want to expose decrypted
118        // content to Java application.
119        mImpl.clear();
120        mImpl = NULL;
121        return ERROR_UNSUPPORTED;
122    }
123
124    mDataSource = dataSource;
125
126    updateDurationAndBitrate();
127
128    return OK;
129}
130
131status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
132
133    ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld",
134            fd, nameForFd(fd).c_str(), (long long) offset, (long long) size);
135
136    Mutex::Autolock autoLock(mLock);
137
138    if (mImpl != NULL) {
139        return -EINVAL;
140    }
141
142    sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
143
144    status_t err = fileSource->initCheck();
145    if (err != OK) {
146        return err;
147    }
148
149    mImpl = MediaExtractor::Create(fileSource);
150
151    if (mImpl == NULL) {
152        return ERROR_UNSUPPORTED;
153    }
154
155    mDataSource = fileSource;
156
157    updateDurationAndBitrate();
158
159    return OK;
160}
161
162status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) {
163    Mutex::Autolock autoLock(mLock);
164
165    if (mImpl != NULL) {
166        return -EINVAL;
167    }
168
169    status_t err = source->initCheck();
170    if (err != OK) {
171        return err;
172    }
173
174    mImpl = MediaExtractor::Create(source);
175
176    if (mImpl == NULL) {
177        return ERROR_UNSUPPORTED;
178    }
179
180    mDataSource = source;
181
182    updateDurationAndBitrate();
183
184    return OK;
185}
186
187void NuMediaExtractor::updateDurationAndBitrate() {
188    mTotalBitrate = 0ll;
189    mDurationUs = -1ll;
190
191    for (size_t i = 0; i < mImpl->countTracks(); ++i) {
192        sp<MetaData> meta = mImpl->getTrackMetaData(i);
193        if (meta == NULL) {
194            ALOGW("no metadata for track %zu", i);
195            continue;
196        }
197
198        int32_t bitrate;
199        if (!meta->findInt32(kKeyBitRate, &bitrate)) {
200            const char *mime;
201            CHECK(meta->findCString(kKeyMIMEType, &mime));
202            ALOGV("track of type '%s' does not publish bitrate", mime);
203
204            mTotalBitrate = -1ll;
205        } else if (mTotalBitrate >= 0ll) {
206            mTotalBitrate += bitrate;
207        }
208
209        int64_t durationUs;
210        if (meta->findInt64(kKeyDuration, &durationUs)
211                && durationUs > mDurationUs) {
212            mDurationUs = durationUs;
213        }
214    }
215}
216
217size_t NuMediaExtractor::countTracks() const {
218    Mutex::Autolock autoLock(mLock);
219
220    return mImpl == NULL ? 0 : mImpl->countTracks();
221}
222
223status_t NuMediaExtractor::getTrackFormat(
224        size_t index, sp<AMessage> *format) const {
225    Mutex::Autolock autoLock(mLock);
226
227    *format = NULL;
228
229    if (mImpl == NULL) {
230        return -EINVAL;
231    }
232
233    if (index >= mImpl->countTracks()) {
234        return -ERANGE;
235    }
236
237    sp<MetaData> meta = mImpl->getTrackMetaData(index);
238    return convertMetaDataToMessage(meta, format);
239}
240
241status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const {
242    Mutex::Autolock autoLock(mLock);
243
244    *format = NULL;
245
246    if (mImpl == NULL) {
247        return -EINVAL;
248    }
249
250    sp<MetaData> meta = mImpl->getMetaData();
251
252    const char *mime;
253    CHECK(meta->findCString(kKeyMIMEType, &mime));
254    *format = new AMessage();
255    (*format)->setString("mime", mime);
256
257    uint32_t type;
258    const void *pssh;
259    size_t psshsize;
260    if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
261        sp<ABuffer> buf = new ABuffer(psshsize);
262        memcpy(buf->data(), pssh, psshsize);
263        (*format)->setBuffer("pssh", buf);
264    }
265
266    return OK;
267}
268
269status_t NuMediaExtractor::selectTrack(size_t index) {
270    Mutex::Autolock autoLock(mLock);
271
272    if (mImpl == NULL) {
273        return -EINVAL;
274    }
275
276    if (index >= mImpl->countTracks()) {
277        return -ERANGE;
278    }
279
280    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
281        TrackInfo *info = &mSelectedTracks.editItemAt(i);
282
283        if (info->mTrackIndex == index) {
284            // This track has already been selected.
285            return OK;
286        }
287    }
288
289    sp<IMediaSource> source = mImpl->getTrack(index);
290
291    CHECK_EQ((status_t)OK, source->start());
292
293    mSelectedTracks.push();
294    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
295
296    info->mSource = source;
297    info->mTrackIndex = index;
298    info->mFinalResult = OK;
299    info->mSample = NULL;
300    info->mSampleTimeUs = -1ll;
301    info->mTrackFlags = 0;
302
303    const char *mime;
304    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
305
306    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
307        info->mTrackFlags |= kIsVorbis;
308    }
309
310    return OK;
311}
312
313status_t NuMediaExtractor::unselectTrack(size_t index) {
314    Mutex::Autolock autoLock(mLock);
315
316    if (mImpl == NULL) {
317        return -EINVAL;
318    }
319
320    if (index >= mImpl->countTracks()) {
321        return -ERANGE;
322    }
323
324    size_t i;
325    for (i = 0; i < mSelectedTracks.size(); ++i) {
326        TrackInfo *info = &mSelectedTracks.editItemAt(i);
327
328        if (info->mTrackIndex == index) {
329            break;
330        }
331    }
332
333    if (i == mSelectedTracks.size()) {
334        // Not selected.
335        return OK;
336    }
337
338    TrackInfo *info = &mSelectedTracks.editItemAt(i);
339
340    if (info->mSample != NULL) {
341        info->mSample->release();
342        info->mSample = NULL;
343
344        info->mSampleTimeUs = -1ll;
345    }
346
347    CHECK_EQ((status_t)OK, info->mSource->stop());
348
349    mSelectedTracks.removeAt(i);
350
351    return OK;
352}
353
354void NuMediaExtractor::releaseTrackSamples() {
355    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
356        TrackInfo *info = &mSelectedTracks.editItemAt(i);
357
358        if (info->mSample != NULL) {
359            info->mSample->release();
360            info->mSample = NULL;
361
362            info->mSampleTimeUs = -1ll;
363        }
364    }
365}
366
367ssize_t NuMediaExtractor::fetchTrackSamples(
368        int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
369    TrackInfo *minInfo = NULL;
370    ssize_t minIndex = -1;
371
372    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
373        TrackInfo *info = &mSelectedTracks.editItemAt(i);
374
375        if (seekTimeUs >= 0ll) {
376            info->mFinalResult = OK;
377
378            if (info->mSample != NULL) {
379                info->mSample->release();
380                info->mSample = NULL;
381                info->mSampleTimeUs = -1ll;
382            }
383        } else if (info->mFinalResult != OK) {
384            continue;
385        }
386
387        if (info->mSample == NULL) {
388            MediaSource::ReadOptions options;
389            if (seekTimeUs >= 0ll) {
390                options.setSeekTo(seekTimeUs, mode);
391            }
392            status_t err = info->mSource->read(&info->mSample, &options);
393
394            if (err != OK) {
395                CHECK(info->mSample == NULL);
396
397                info->mFinalResult = err;
398
399                if (info->mFinalResult != ERROR_END_OF_STREAM) {
400                    ALOGW("read on track %zu failed with error %d",
401                          info->mTrackIndex, err);
402                }
403
404                info->mSampleTimeUs = -1ll;
405                continue;
406            } else {
407                CHECK(info->mSample != NULL);
408                CHECK(info->mSample->meta_data()->findInt64(
409                            kKeyTime, &info->mSampleTimeUs));
410            }
411        }
412
413        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
414            minInfo = info;
415            minIndex = i;
416        }
417    }
418
419    return minIndex;
420}
421
422status_t NuMediaExtractor::seekTo(
423        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
424    Mutex::Autolock autoLock(mLock);
425
426    ssize_t minIndex = fetchTrackSamples(timeUs, mode);
427
428    if (minIndex < 0) {
429        return ERROR_END_OF_STREAM;
430    }
431
432    return OK;
433}
434
435status_t NuMediaExtractor::advance() {
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
446    info->mSample->release();
447    info->mSample = NULL;
448    info->mSampleTimeUs = -1ll;
449
450    return OK;
451}
452
453status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) {
454    int32_t numPageSamples;
455    if (!info->mSample->meta_data()->findInt32(
456            kKeyValidSamples, &numPageSamples)) {
457        numPageSamples = -1;
458    }
459
460    memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
461           &numPageSamples,
462           sizeof(numPageSamples));
463
464    uint32_t type;
465    const void *data;
466    size_t size, size2;
467    if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
468        // Signal numPageSamples (a plain int32_t) is appended at the end,
469        // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
470        if (SIZE_MAX - size < sizeof(int32_t)) {
471            return -ENOMEM;
472        }
473
474        size_t newSize = size + sizeof(int32_t);
475        sp<ABuffer> abuf = new ABuffer(newSize);
476        uint8_t *adata = static_cast<uint8_t *>(abuf->data());
477        if (adata == NULL) {
478            return -ENOMEM;
479        }
480
481        // append 0 to encrypted sizes
482        int32_t zero = 0;
483        memcpy(adata, data, size);
484        memcpy(adata + size, &zero, sizeof(zero));
485        info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
486
487        if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
488            if (size2 != size) {
489                return ERROR_MALFORMED;
490            }
491            memcpy(adata, data, size);
492        } else {
493            // if sample meta data does not include plain size array, assume filled with zeros,
494            // i.e. entire buffer is encrypted
495            memset(adata, 0, size);
496        }
497        // append sizeof(numPageSamples) to plain sizes.
498        int32_t int32Size = sizeof(numPageSamples);
499        memcpy(adata + size, &int32Size, sizeof(int32Size));
500        info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
501    }
502
503    return OK;
504}
505
506status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
507    Mutex::Autolock autoLock(mLock);
508
509    ssize_t minIndex = fetchTrackSamples();
510
511    if (minIndex < 0) {
512        return ERROR_END_OF_STREAM;
513    }
514
515    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
516
517    size_t sampleSize = info->mSample->range_length();
518
519    if (info->mTrackFlags & kIsVorbis) {
520        // Each sample's data is suffixed by the number of page samples
521        // or -1 if not available.
522        sampleSize += sizeof(int32_t);
523    }
524
525    if (buffer->capacity() < sampleSize) {
526        return -ENOMEM;
527    }
528
529    const uint8_t *src =
530        (const uint8_t *)info->mSample->data()
531            + info->mSample->range_offset();
532
533    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
534
535    status_t err = OK;
536    if (info->mTrackFlags & kIsVorbis) {
537        err = appendVorbisNumPageSamples(info, buffer);
538    }
539
540    if (err == OK) {
541        buffer->setRange(0, sampleSize);
542    }
543
544    return err;
545}
546
547status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
548    Mutex::Autolock autoLock(mLock);
549
550    ssize_t minIndex = fetchTrackSamples();
551
552    if (minIndex < 0) {
553        return ERROR_END_OF_STREAM;
554    }
555
556    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
557    *trackIndex = info->mTrackIndex;
558
559    return OK;
560}
561
562status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
563    Mutex::Autolock autoLock(mLock);
564
565    ssize_t minIndex = fetchTrackSamples();
566
567    if (minIndex < 0) {
568        return ERROR_END_OF_STREAM;
569    }
570
571    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
572    *sampleTimeUs = info->mSampleTimeUs;
573
574    return OK;
575}
576
577status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
578    Mutex::Autolock autoLock(mLock);
579
580    *sampleMeta = NULL;
581
582    ssize_t minIndex = fetchTrackSamples();
583
584    if (minIndex < 0) {
585        return ERROR_END_OF_STREAM;
586    }
587
588    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
589    *sampleMeta = info->mSample->meta_data();
590
591    return OK;
592}
593
594bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
595    if (mTotalBitrate >= 0) {
596        *bitrate = mTotalBitrate;
597        return true;
598    }
599
600    off64_t size;
601    if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) {
602        *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
603        return true;
604    }
605
606    return false;
607}
608
609// Returns true iff cached duration is available/applicable.
610bool NuMediaExtractor::getCachedDuration(
611        int64_t *durationUs, bool *eos) const {
612    Mutex::Autolock autoLock(mLock);
613
614    int64_t bitrate;
615    if (mIsWidevineExtractor) {
616        sp<WVMExtractor> wvmExtractor =
617            static_cast<WVMExtractor *>(mImpl.get());
618
619        status_t finalStatus;
620        *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
621        *eos = (finalStatus != OK);
622        return true;
623    } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
624            && getTotalBitrate(&bitrate)) {
625        sp<NuCachedSource2> cachedSource =
626            static_cast<NuCachedSource2 *>(mDataSource.get());
627
628        status_t finalStatus;
629        size_t cachedDataRemaining =
630            cachedSource->approxDataRemaining(&finalStatus);
631
632        *durationUs = cachedDataRemaining * 8000000ll / bitrate;
633        *eos = (finalStatus != OK);
634        return true;
635    }
636
637    return false;
638}
639
640}  // namespace android
641