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