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