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    CHECK_EQ((status_t)OK, source->start());
309
310    mSelectedTracks.push();
311    TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
312
313    info->mSource = source;
314    info->mTrackIndex = index;
315    info->mFinalResult = OK;
316    info->mSample = NULL;
317    info->mSampleTimeUs = -1ll;
318    info->mTrackFlags = 0;
319
320    const char *mime;
321    CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
322
323    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
324        info->mTrackFlags |= kIsVorbis;
325    }
326
327    return OK;
328}
329
330status_t NuMediaExtractor::unselectTrack(size_t index) {
331    Mutex::Autolock autoLock(mLock);
332
333    if (mImpl == NULL) {
334        return -EINVAL;
335    }
336
337    if (index >= mImpl->countTracks()) {
338        return -ERANGE;
339    }
340
341    size_t i;
342    for (i = 0; i < mSelectedTracks.size(); ++i) {
343        TrackInfo *info = &mSelectedTracks.editItemAt(i);
344
345        if (info->mTrackIndex == index) {
346            break;
347        }
348    }
349
350    if (i == mSelectedTracks.size()) {
351        // Not selected.
352        return OK;
353    }
354
355    TrackInfo *info = &mSelectedTracks.editItemAt(i);
356
357    if (info->mSample != NULL) {
358        info->mSample->release();
359        info->mSample = NULL;
360
361        info->mSampleTimeUs = -1ll;
362    }
363
364    CHECK_EQ((status_t)OK, info->mSource->stop());
365
366    mSelectedTracks.removeAt(i);
367
368    return OK;
369}
370
371void NuMediaExtractor::releaseTrackSamples() {
372    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
373        TrackInfo *info = &mSelectedTracks.editItemAt(i);
374
375        if (info->mSample != NULL) {
376            info->mSample->release();
377            info->mSample = NULL;
378
379            info->mSampleTimeUs = -1ll;
380        }
381    }
382}
383
384ssize_t NuMediaExtractor::fetchTrackSamples(
385        int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
386    TrackInfo *minInfo = NULL;
387    ssize_t minIndex = -1;
388
389    for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
390        TrackInfo *info = &mSelectedTracks.editItemAt(i);
391
392        if (seekTimeUs >= 0ll) {
393            info->mFinalResult = OK;
394
395            if (info->mSample != NULL) {
396                info->mSample->release();
397                info->mSample = NULL;
398                info->mSampleTimeUs = -1ll;
399            }
400        } else if (info->mFinalResult != OK) {
401            continue;
402        }
403
404        if (info->mSample == NULL) {
405            MediaSource::ReadOptions options;
406            if (seekTimeUs >= 0ll) {
407                options.setSeekTo(seekTimeUs, mode);
408            }
409            status_t err = info->mSource->read(&info->mSample, &options);
410
411            if (err != OK) {
412                CHECK(info->mSample == NULL);
413
414                info->mFinalResult = err;
415
416                if (info->mFinalResult != ERROR_END_OF_STREAM) {
417                    ALOGW("read on track %zu failed with error %d",
418                          info->mTrackIndex, err);
419                }
420
421                info->mSampleTimeUs = -1ll;
422                continue;
423            } else {
424                CHECK(info->mSample != NULL);
425                CHECK(info->mSample->meta_data()->findInt64(
426                            kKeyTime, &info->mSampleTimeUs));
427            }
428        }
429
430        if (minInfo == NULL  || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
431            minInfo = info;
432            minIndex = i;
433        }
434    }
435
436    return minIndex;
437}
438
439status_t NuMediaExtractor::seekTo(
440        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
441    Mutex::Autolock autoLock(mLock);
442
443    ssize_t minIndex = fetchTrackSamples(timeUs, mode);
444
445    if (minIndex < 0) {
446        return ERROR_END_OF_STREAM;
447    }
448
449    return OK;
450}
451
452status_t NuMediaExtractor::advance() {
453    Mutex::Autolock autoLock(mLock);
454
455    ssize_t minIndex = fetchTrackSamples();
456
457    if (minIndex < 0) {
458        return ERROR_END_OF_STREAM;
459    }
460
461    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
462
463    info->mSample->release();
464    info->mSample = NULL;
465    info->mSampleTimeUs = -1ll;
466
467    return OK;
468}
469
470status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) {
471    int32_t numPageSamples;
472    if (!info->mSample->meta_data()->findInt32(
473            kKeyValidSamples, &numPageSamples)) {
474        numPageSamples = -1;
475    }
476
477    memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
478           &numPageSamples,
479           sizeof(numPageSamples));
480
481    uint32_t type;
482    const void *data;
483    size_t size, size2;
484    if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
485        // Signal numPageSamples (a plain int32_t) is appended at the end,
486        // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
487        if (SIZE_MAX - size < sizeof(int32_t)) {
488            return -ENOMEM;
489        }
490
491        size_t newSize = size + sizeof(int32_t);
492        sp<ABuffer> abuf = new ABuffer(newSize);
493        uint8_t *adata = static_cast<uint8_t *>(abuf->data());
494        if (adata == NULL) {
495            return -ENOMEM;
496        }
497
498        // append 0 to encrypted sizes
499        int32_t zero = 0;
500        memcpy(adata, data, size);
501        memcpy(adata + size, &zero, sizeof(zero));
502        info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
503
504        if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
505            if (size2 != size) {
506                return ERROR_MALFORMED;
507            }
508            memcpy(adata, data, size);
509        } else {
510            // if sample meta data does not include plain size array, assume filled with zeros,
511            // i.e. entire buffer is encrypted
512            memset(adata, 0, size);
513        }
514        // append sizeof(numPageSamples) to plain sizes.
515        int32_t int32Size = sizeof(numPageSamples);
516        memcpy(adata + size, &int32Size, sizeof(int32Size));
517        info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
518    }
519
520    return OK;
521}
522
523status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
524    Mutex::Autolock autoLock(mLock);
525
526    ssize_t minIndex = fetchTrackSamples();
527
528    if (minIndex < 0) {
529        return ERROR_END_OF_STREAM;
530    }
531
532    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
533
534    size_t sampleSize = info->mSample->range_length();
535
536    if (info->mTrackFlags & kIsVorbis) {
537        // Each sample's data is suffixed by the number of page samples
538        // or -1 if not available.
539        sampleSize += sizeof(int32_t);
540    }
541
542    if (buffer->capacity() < sampleSize) {
543        return -ENOMEM;
544    }
545
546    const uint8_t *src =
547        (const uint8_t *)info->mSample->data()
548            + info->mSample->range_offset();
549
550    memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
551
552    status_t err = OK;
553    if (info->mTrackFlags & kIsVorbis) {
554        err = appendVorbisNumPageSamples(info, buffer);
555    }
556
557    if (err == OK) {
558        buffer->setRange(0, sampleSize);
559    }
560
561    return err;
562}
563
564status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
565    Mutex::Autolock autoLock(mLock);
566
567    ssize_t minIndex = fetchTrackSamples();
568
569    if (minIndex < 0) {
570        return ERROR_END_OF_STREAM;
571    }
572
573    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
574    *trackIndex = info->mTrackIndex;
575
576    return OK;
577}
578
579status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
580    Mutex::Autolock autoLock(mLock);
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    *sampleTimeUs = info->mSampleTimeUs;
590
591    return OK;
592}
593
594status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
595    Mutex::Autolock autoLock(mLock);
596
597    *sampleMeta = NULL;
598
599    ssize_t minIndex = fetchTrackSamples();
600
601    if (minIndex < 0) {
602        return ERROR_END_OF_STREAM;
603    }
604
605    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
606    *sampleMeta = info->mSample->meta_data();
607
608    return OK;
609}
610
611bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
612    if (mTotalBitrate >= 0) {
613        *bitrate = mTotalBitrate;
614        return true;
615    }
616
617    off64_t size;
618    if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) {
619        *bitrate = size * 8000000ll / mDurationUs;  // in bits/sec
620        return true;
621    }
622
623    return false;
624}
625
626// Returns true iff cached duration is available/applicable.
627bool NuMediaExtractor::getCachedDuration(
628        int64_t *durationUs, bool *eos) const {
629    Mutex::Autolock autoLock(mLock);
630
631    int64_t bitrate;
632    if (mIsWidevineExtractor) {
633        sp<WVMExtractor> wvmExtractor =
634            static_cast<WVMExtractor *>(mImpl.get());
635
636        status_t finalStatus;
637        *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus);
638        *eos = (finalStatus != OK);
639        return true;
640    } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
641            && getTotalBitrate(&bitrate)) {
642        sp<NuCachedSource2> cachedSource =
643            static_cast<NuCachedSource2 *>(mDataSource.get());
644
645        status_t finalStatus;
646        size_t cachedDataRemaining =
647            cachedSource->approxDataRemaining(&finalStatus);
648
649        *durationUs = cachedDataRemaining * 8000000ll / bitrate;
650        *eos = (finalStatus != OK);
651        return true;
652    }
653
654    return false;
655}
656
657}  // namespace android
658