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