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