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