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 
40 namespace android {
41 
NuMediaExtractor()42 NuMediaExtractor::NuMediaExtractor()
43     : mIsWidevineExtractor(false),
44       mTotalBitrate(-1ll),
45       mDurationUs(-1ll) {
46 }
47 
~NuMediaExtractor()48 NuMediaExtractor::~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 
setDataSource(const sp<IMediaHTTPService> & httpService,const char * path,const KeyedVector<String8,String8> * headers)63 status_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 
setDataSource(int fd,off64_t offset,off64_t size)135 status_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 
setDataSource(const sp<DataSource> & source)167 status_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 
updateDurationAndBitrate()193 status_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 
countTracks() const228 size_t NuMediaExtractor::countTracks() const {
229     Mutex::Autolock autoLock(mLock);
230 
231     return mImpl == NULL ? 0 : mImpl->countTracks();
232 }
233 
getTrackFormat(size_t index,sp<AMessage> * format,uint32_t flags) const234 status_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 
getFileFormat(sp<AMessage> * format) const258 status_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 
selectTrack(size_t index)286 status_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 
unselectTrack(size_t index)330 status_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 
releaseTrackSamples()371 void 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 
fetchTrackSamples(int64_t seekTimeUs,MediaSource::ReadOptions::SeekMode mode)384 ssize_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 
seekTo(int64_t timeUs,MediaSource::ReadOptions::SeekMode mode)439 status_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 
advance()452 status_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 
appendVorbisNumPageSamples(TrackInfo * info,const sp<ABuffer> & buffer)470 status_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 
readSampleData(const sp<ABuffer> & buffer)523 status_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 
getSampleTrackIndex(size_t * trackIndex)564 status_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 
getSampleTime(int64_t * sampleTimeUs)579 status_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 
getSampleMeta(sp<MetaData> * sampleMeta)594 status_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 
getTotalBitrate(int64_t * bitrate) const611 bool 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.
getCachedDuration(int64_t * durationUs,bool * eos) const627 bool 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