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/DataSource.h>
27 #include <media/MediaExtractor.h>
28 #include <media/MediaSource.h>
29 #include <media/stagefright/foundation/ABuffer.h>
30 #include <media/stagefright/foundation/ADebug.h>
31 #include <media/stagefright/foundation/AMessage.h>
32 #include <media/stagefright/DataSourceFactory.h>
33 #include <media/stagefright/FileSource.h>
34 #include <media/stagefright/MediaBuffer.h>
35 #include <media/stagefright/MediaDefs.h>
36 #include <media/stagefright/MediaErrors.h>
37 #include <media/stagefright/MediaExtractorFactory.h>
38 #include <media/stagefright/MetaData.h>
39 #include <media/stagefright/Utils.h>
40
41 namespace android {
42
Sample()43 NuMediaExtractor::Sample::Sample()
44 : mBuffer(NULL),
45 mSampleTimeUs(-1ll) {
46 }
47
Sample(MediaBufferBase * buffer,int64_t timeUs)48 NuMediaExtractor::Sample::Sample(MediaBufferBase *buffer, int64_t timeUs)
49 : mBuffer(buffer),
50 mSampleTimeUs(timeUs) {
51 }
52
NuMediaExtractor()53 NuMediaExtractor::NuMediaExtractor()
54 : mTotalBitrate(-1ll),
55 mDurationUs(-1ll) {
56 }
57
~NuMediaExtractor()58 NuMediaExtractor::~NuMediaExtractor() {
59 releaseAllTrackSamples();
60
61 for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
62 TrackInfo *info = &mSelectedTracks.editItemAt(i);
63
64 status_t err = info->mSource->stop();
65 ALOGE_IF(err != OK, "error %d stopping track %zu", err, i);
66 }
67
68 mSelectedTracks.clear();
69 if (mDataSource != NULL) {
70 mDataSource->close();
71 }
72 }
73
setDataSource(const sp<MediaHTTPService> & httpService,const char * path,const KeyedVector<String8,String8> * headers)74 status_t NuMediaExtractor::setDataSource(
75 const sp<MediaHTTPService> &httpService,
76 const char *path,
77 const KeyedVector<String8, String8> *headers) {
78 Mutex::Autolock autoLock(mLock);
79
80 if (mImpl != NULL || path == NULL) {
81 return -EINVAL;
82 }
83
84 sp<DataSource> dataSource =
85 DataSourceFactory::CreateFromURI(httpService, path, headers);
86
87 if (dataSource == NULL) {
88 return -ENOENT;
89 }
90
91 mImpl = MediaExtractorFactory::Create(dataSource);
92
93 if (mImpl == NULL) {
94 return ERROR_UNSUPPORTED;
95 }
96
97 if (!mCasToken.empty()) {
98 mImpl->setMediaCas(mCasToken);
99 }
100
101 status_t err = updateDurationAndBitrate();
102 if (err == OK) {
103 mDataSource = dataSource;
104 }
105
106 return OK;
107 }
108
setDataSource(int fd,off64_t offset,off64_t size)109 status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
110
111 ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld",
112 fd, nameForFd(fd).c_str(), (long long) offset, (long long) size);
113
114 Mutex::Autolock autoLock(mLock);
115
116 if (mImpl != NULL) {
117 return -EINVAL;
118 }
119
120 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
121
122 status_t err = fileSource->initCheck();
123 if (err != OK) {
124 return err;
125 }
126
127 mImpl = MediaExtractorFactory::Create(fileSource);
128
129 if (mImpl == NULL) {
130 return ERROR_UNSUPPORTED;
131 }
132
133 if (!mCasToken.empty()) {
134 mImpl->setMediaCas(mCasToken);
135 }
136
137 err = updateDurationAndBitrate();
138 if (err == OK) {
139 mDataSource = fileSource;
140 }
141
142 return OK;
143 }
144
setDataSource(const sp<DataSource> & source)145 status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) {
146 Mutex::Autolock autoLock(mLock);
147
148 if (mImpl != NULL) {
149 return -EINVAL;
150 }
151
152 status_t err = source->initCheck();
153 if (err != OK) {
154 return err;
155 }
156
157 mImpl = MediaExtractorFactory::Create(source);
158
159 if (mImpl == NULL) {
160 return ERROR_UNSUPPORTED;
161 }
162
163 if (!mCasToken.empty()) {
164 mImpl->setMediaCas(mCasToken);
165 }
166
167 err = updateDurationAndBitrate();
168 if (err == OK) {
169 mDataSource = source;
170 }
171
172 return err;
173 }
174
arrayToString(const std::vector<uint8_t> & array)175 static String8 arrayToString(const std::vector<uint8_t> &array) {
176 String8 result;
177 for (size_t i = 0; i < array.size(); i++) {
178 result.appendFormat("%02x ", array[i]);
179 }
180 if (result.isEmpty()) {
181 result.append("(null)");
182 }
183 return result;
184 }
185
setMediaCas(const HInterfaceToken & casToken)186 status_t NuMediaExtractor::setMediaCas(const HInterfaceToken &casToken) {
187 ALOGV("setMediaCas: casToken={%s}", arrayToString(casToken).c_str());
188
189 Mutex::Autolock autoLock(mLock);
190
191 if (casToken.empty()) {
192 return BAD_VALUE;
193 }
194
195 mCasToken = casToken;
196
197 if (mImpl != NULL) {
198 mImpl->setMediaCas(casToken);
199 status_t err = updateDurationAndBitrate();
200 if (err != OK) {
201 return err;
202 }
203 }
204
205 return OK;
206 }
207
disconnect()208 void NuMediaExtractor::disconnect() {
209 if (mDataSource != NULL) {
210 // disconnect data source
211 if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
212 static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect();
213 }
214 }
215 }
216
updateDurationAndBitrate()217 status_t NuMediaExtractor::updateDurationAndBitrate() {
218 if (mImpl->countTracks() > kMaxTrackCount) {
219 return ERROR_UNSUPPORTED;
220 }
221
222 mTotalBitrate = 0ll;
223 mDurationUs = -1ll;
224
225 for (size_t i = 0; i < mImpl->countTracks(); ++i) {
226 sp<MetaData> meta = mImpl->getTrackMetaData(i);
227 if (meta == NULL) {
228 ALOGW("no metadata for track %zu", i);
229 continue;
230 }
231
232 int32_t bitrate;
233 if (!meta->findInt32(kKeyBitRate, &bitrate)) {
234 const char *mime;
235 CHECK(meta->findCString(kKeyMIMEType, &mime));
236 ALOGV("track of type '%s' does not publish bitrate", mime);
237
238 mTotalBitrate = -1ll;
239 } else if (mTotalBitrate >= 0ll) {
240 mTotalBitrate += bitrate;
241 }
242
243 int64_t durationUs;
244 if (meta->findInt64(kKeyDuration, &durationUs)
245 && durationUs > mDurationUs) {
246 mDurationUs = durationUs;
247 }
248 }
249 return OK;
250 }
251
countTracks() const252 size_t NuMediaExtractor::countTracks() const {
253 Mutex::Autolock autoLock(mLock);
254
255 return mImpl == NULL ? 0 : mImpl->countTracks();
256 }
257
getTrackFormat(size_t index,sp<AMessage> * format,uint32_t flags) const258 status_t NuMediaExtractor::getTrackFormat(
259 size_t index, sp<AMessage> *format, uint32_t flags) const {
260 Mutex::Autolock autoLock(mLock);
261
262 *format = NULL;
263
264 if (mImpl == NULL) {
265 return -EINVAL;
266 }
267
268 if (index >= mImpl->countTracks()) {
269 return -ERANGE;
270 }
271
272 sp<MetaData> meta = mImpl->getTrackMetaData(index, flags);
273 // Extractors either support trackID-s or not, so either all tracks have trackIDs or none.
274 // Generate trackID if missing.
275 int32_t trackID;
276 if (meta != NULL && !meta->findInt32(kKeyTrackID, &trackID)) {
277 meta->setInt32(kKeyTrackID, (int32_t)index + 1);
278 }
279 return convertMetaDataToMessage(meta, format);
280 }
281
getFileFormat(sp<AMessage> * format) const282 status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const {
283 Mutex::Autolock autoLock(mLock);
284
285 *format = NULL;
286
287 if (mImpl == NULL) {
288 return -EINVAL;
289 }
290
291 sp<MetaData> meta = mImpl->getMetaData();
292
293 const char *mime;
294 CHECK(meta->findCString(kKeyMIMEType, &mime));
295 *format = new AMessage();
296 (*format)->setString("mime", mime);
297
298 uint32_t type;
299 const void *pssh;
300 size_t psshsize;
301 if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
302 sp<ABuffer> buf = new ABuffer(psshsize);
303 memcpy(buf->data(), pssh, psshsize);
304 (*format)->setBuffer("pssh", buf);
305 }
306
307 return OK;
308 }
309
getExifOffsetSize(off64_t * offset,size_t * size) const310 status_t NuMediaExtractor::getExifOffsetSize(off64_t *offset, size_t *size) const {
311 Mutex::Autolock autoLock(mLock);
312
313 if (mImpl == NULL) {
314 return -EINVAL;
315 }
316
317 sp<MetaData> meta = mImpl->getMetaData();
318
319 int64_t exifOffset, exifSize;
320 if (meta->findInt64(kKeyExifOffset, &exifOffset)
321 && meta->findInt64(kKeyExifSize, &exifSize)) {
322 *offset = (off64_t) exifOffset;
323 *size = (size_t) exifSize;
324
325 return OK;
326 }
327 return ERROR_UNSUPPORTED;
328 }
329
selectTrack(size_t index,int64_t startTimeUs,MediaSource::ReadOptions::SeekMode mode)330 status_t NuMediaExtractor::selectTrack(size_t index,
331 int64_t startTimeUs, MediaSource::ReadOptions::SeekMode mode) {
332 Mutex::Autolock autoLock(mLock);
333
334 if (mImpl == NULL) {
335 return -EINVAL;
336 }
337
338 if (index >= mImpl->countTracks()) {
339 return -ERANGE;
340 }
341
342 for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
343 TrackInfo *info = &mSelectedTracks.editItemAt(i);
344
345 if (info->mTrackIndex == index) {
346 // This track has already been selected.
347 return OK;
348 }
349 }
350
351 sp<IMediaSource> source = mImpl->getTrack(index);
352
353 if (source == nullptr) {
354 ALOGE("track %zu is empty", index);
355 return ERROR_MALFORMED;
356 }
357
358 status_t ret = source->start();
359 if (ret != OK) {
360 ALOGE("track %zu failed to start", index);
361 return ret;
362 }
363
364 sp<MetaData> meta = source->getFormat();
365 if (meta == NULL) {
366 ALOGE("track %zu has no meta data", index);
367 return ERROR_MALFORMED;
368 }
369
370 const char *mime;
371 if (!meta->findCString(kKeyMIMEType, &mime)) {
372 ALOGE("track %zu has no mime type in meta data", index);
373 return ERROR_MALFORMED;
374 }
375 ALOGV("selectTrack, track[%zu]: %s", index, mime);
376
377 mSelectedTracks.push();
378 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
379
380 info->mSource = source;
381 info->mTrackIndex = index;
382 if (!strncasecmp(mime, "audio/", 6)) {
383 info->mTrackType = MEDIA_TRACK_TYPE_AUDIO;
384 info->mMaxFetchCount = 64;
385 } else if (!strncasecmp(mime, "video/", 6)) {
386 info->mTrackType = MEDIA_TRACK_TYPE_VIDEO;
387 info->mMaxFetchCount = 8;
388 } else {
389 info->mTrackType = MEDIA_TRACK_TYPE_UNKNOWN;
390 info->mMaxFetchCount = 1;
391 }
392 info->mFinalResult = OK;
393 releaseTrackSamples(info);
394 info->mTrackFlags = 0;
395
396 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
397 info->mTrackFlags |= kIsVorbis;
398 }
399
400 if (startTimeUs >= 0) {
401 fetchTrackSamples(info, startTimeUs, mode);
402 }
403
404 return OK;
405 }
406
unselectTrack(size_t index)407 status_t NuMediaExtractor::unselectTrack(size_t index) {
408 Mutex::Autolock autoLock(mLock);
409
410 if (mImpl == NULL) {
411 return -EINVAL;
412 }
413
414 if (index >= mImpl->countTracks()) {
415 return -ERANGE;
416 }
417
418 size_t i;
419 for (i = 0; i < mSelectedTracks.size(); ++i) {
420 TrackInfo *info = &mSelectedTracks.editItemAt(i);
421
422 if (info->mTrackIndex == index) {
423 break;
424 }
425 }
426
427 if (i == mSelectedTracks.size()) {
428 // Not selected.
429 return OK;
430 }
431
432 TrackInfo *info = &mSelectedTracks.editItemAt(i);
433
434 releaseTrackSamples(info);
435
436 CHECK_EQ((status_t)OK, info->mSource->stop());
437
438 mSelectedTracks.removeAt(i);
439
440 return OK;
441 }
442
releaseOneSample(TrackInfo * info)443 void NuMediaExtractor::releaseOneSample(TrackInfo *info) {
444 if (info == NULL || info->mSamples.empty()) {
445 return;
446 }
447
448 auto it = info->mSamples.begin();
449 if (it->mBuffer != NULL) {
450 it->mBuffer->release();
451 }
452 info->mSamples.erase(it);
453 }
454
releaseTrackSamples(TrackInfo * info)455 void NuMediaExtractor::releaseTrackSamples(TrackInfo *info) {
456 if (info == NULL) {
457 return;
458 }
459
460 auto it = info->mSamples.begin();
461 while (it != info->mSamples.end()) {
462 if (it->mBuffer != NULL) {
463 it->mBuffer->release();
464 }
465 it = info->mSamples.erase(it);
466 }
467 }
468
releaseAllTrackSamples()469 void NuMediaExtractor::releaseAllTrackSamples() {
470 for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
471 releaseTrackSamples(&mSelectedTracks.editItemAt(i));
472 }
473 }
474
fetchAllTrackSamples(int64_t seekTimeUs,MediaSource::ReadOptions::SeekMode mode)475 ssize_t NuMediaExtractor::fetchAllTrackSamples(
476 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
477 TrackInfo *minInfo = NULL;
478 ssize_t minIndex = ERROR_END_OF_STREAM;
479
480 for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
481 TrackInfo *info = &mSelectedTracks.editItemAt(i);
482 fetchTrackSamples(info, seekTimeUs, mode);
483
484 status_t err = info->mFinalResult;
485 if (err != OK && err != ERROR_END_OF_STREAM) {
486 return err;
487 }
488
489 if (info->mSamples.empty()) {
490 continue;
491 }
492
493 if (minInfo == NULL) {
494 minInfo = info;
495 minIndex = i;
496 } else {
497 auto it = info->mSamples.begin();
498 auto itMin = minInfo->mSamples.begin();
499 if (it->mSampleTimeUs < itMin->mSampleTimeUs) {
500 minInfo = info;
501 minIndex = i;
502 }
503 }
504 }
505
506 return minIndex;
507 }
508
fetchTrackSamples(TrackInfo * info,int64_t seekTimeUs,MediaSource::ReadOptions::SeekMode mode)509 void NuMediaExtractor::fetchTrackSamples(TrackInfo *info,
510 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) {
511 if (info == NULL) {
512 return;
513 }
514
515 MediaSource::ReadOptions options;
516 if (seekTimeUs >= 0ll) {
517 options.setSeekTo(seekTimeUs, mode);
518 info->mFinalResult = OK;
519 releaseTrackSamples(info);
520 } else if (info->mFinalResult != OK || !info->mSamples.empty()) {
521 return;
522 }
523
524 status_t err = OK;
525 Vector<MediaBufferBase *> mediaBuffers;
526 if (info->mSource->supportReadMultiple()) {
527 options.setNonBlocking();
528 err = info->mSource->readMultiple(&mediaBuffers, info->mMaxFetchCount, &options);
529 } else {
530 MediaBufferBase *mbuf = NULL;
531 err = info->mSource->read(&mbuf, &options);
532 if (err == OK && mbuf != NULL) {
533 mediaBuffers.push_back(mbuf);
534 }
535 }
536
537 info->mFinalResult = err;
538 if (err != OK && err != ERROR_END_OF_STREAM) {
539 ALOGW("read on track %zu failed with error %d", info->mTrackIndex, err);
540 size_t count = mediaBuffers.size();
541 for (size_t id = 0; id < count; ++id) {
542 MediaBufferBase *mbuf = mediaBuffers[id];
543 if (mbuf != NULL) {
544 mbuf->release();
545 }
546 }
547 return;
548 }
549
550 size_t count = mediaBuffers.size();
551 bool releaseRemaining = false;
552 for (size_t id = 0; id < count; ++id) {
553 int64_t timeUs;
554 MediaBufferBase *mbuf = mediaBuffers[id];
555 if (mbuf == NULL) {
556 continue;
557 }
558 if (releaseRemaining) {
559 mbuf->release();
560 continue;
561 }
562 if (mbuf->meta_data().findInt64(kKeyTime, &timeUs)) {
563 info->mSamples.emplace_back(mbuf, timeUs);
564 } else {
565 mbuf->meta_data().dumpToLog();
566 info->mFinalResult = ERROR_MALFORMED;
567 mbuf->release();
568 releaseRemaining = true;
569 }
570 }
571 }
572
seekTo(int64_t timeUs,MediaSource::ReadOptions::SeekMode mode)573 status_t NuMediaExtractor::seekTo(
574 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) {
575 Mutex::Autolock autoLock(mLock);
576
577 ssize_t minIndex = fetchAllTrackSamples(timeUs, mode);
578
579 if (minIndex < 0) {
580 return ERROR_END_OF_STREAM;
581 }
582
583 return OK;
584 }
585
advance()586 status_t NuMediaExtractor::advance() {
587 Mutex::Autolock autoLock(mLock);
588
589 ssize_t minIndex = fetchAllTrackSamples();
590
591 if (minIndex < 0) {
592 return ERROR_END_OF_STREAM;
593 }
594
595 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
596
597 releaseOneSample(info);
598
599 return OK;
600 }
601
appendVorbisNumPageSamples(MediaBufferBase * mbuf,const sp<ABuffer> & buffer)602 status_t NuMediaExtractor::appendVorbisNumPageSamples(
603 MediaBufferBase *mbuf, const sp<ABuffer> &buffer) {
604 int32_t numPageSamples;
605 if (!mbuf->meta_data().findInt32(
606 kKeyValidSamples, &numPageSamples)) {
607 numPageSamples = -1;
608 }
609
610 memcpy((uint8_t *)buffer->data() + mbuf->range_length(),
611 &numPageSamples,
612 sizeof(numPageSamples));
613
614 uint32_t type;
615 const void *data;
616 size_t size, size2;
617 if (mbuf->meta_data().findData(kKeyEncryptedSizes, &type, &data, &size)) {
618 // Signal numPageSamples (a plain int32_t) is appended at the end,
619 // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
620 if (SIZE_MAX - size < sizeof(int32_t)) {
621 return -ENOMEM;
622 }
623
624 size_t newSize = size + sizeof(int32_t);
625 sp<ABuffer> abuf = new ABuffer(newSize);
626 uint8_t *adata = static_cast<uint8_t *>(abuf->data());
627 if (adata == NULL) {
628 return -ENOMEM;
629 }
630
631 // append 0 to encrypted sizes
632 int32_t zero = 0;
633 memcpy(adata, data, size);
634 memcpy(adata + size, &zero, sizeof(zero));
635 mbuf->meta_data().setData(kKeyEncryptedSizes, type, adata, newSize);
636
637 if (mbuf->meta_data().findData(kKeyPlainSizes, &type, &data, &size2)) {
638 if (size2 != size) {
639 return ERROR_MALFORMED;
640 }
641 memcpy(adata, data, size);
642 } else {
643 // if sample meta data does not include plain size array, assume filled with zeros,
644 // i.e. entire buffer is encrypted
645 memset(adata, 0, size);
646 }
647 // append sizeof(numPageSamples) to plain sizes.
648 int32_t int32Size = sizeof(numPageSamples);
649 memcpy(adata + size, &int32Size, sizeof(int32Size));
650 mbuf->meta_data().setData(kKeyPlainSizes, type, adata, newSize);
651 }
652
653 return OK;
654 }
655
readSampleData(const sp<ABuffer> & buffer)656 status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
657 Mutex::Autolock autoLock(mLock);
658
659 ssize_t minIndex = fetchAllTrackSamples();
660
661 if (minIndex < 0) {
662 return ERROR_END_OF_STREAM;
663 }
664
665 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
666
667 auto it = info->mSamples.begin();
668 size_t sampleSize = it->mBuffer->range_length();
669
670 if (info->mTrackFlags & kIsVorbis) {
671 // Each sample's data is suffixed by the number of page samples
672 // or -1 if not available.
673 sampleSize += sizeof(int32_t);
674 }
675
676 if (buffer->capacity() < sampleSize) {
677 return -ENOMEM;
678 }
679
680 const uint8_t *src =
681 (const uint8_t *)it->mBuffer->data()
682 + it->mBuffer->range_offset();
683
684 memcpy((uint8_t *)buffer->data(), src, it->mBuffer->range_length());
685
686 status_t err = OK;
687 if (info->mTrackFlags & kIsVorbis) {
688 err = appendVorbisNumPageSamples(it->mBuffer, buffer);
689 }
690
691 if (err == OK) {
692 buffer->setRange(0, sampleSize);
693 }
694
695 return err;
696 }
697
getSampleSize(size_t * sampleSize)698 status_t NuMediaExtractor::getSampleSize(size_t *sampleSize) {
699 Mutex::Autolock autoLock(mLock);
700
701 ssize_t minIndex = fetchAllTrackSamples();
702
703 if (minIndex < 0) {
704 return ERROR_END_OF_STREAM;
705 }
706
707 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
708 auto it = info->mSamples.begin();
709 *sampleSize = it->mBuffer->range_length();
710
711 if (info->mTrackFlags & kIsVorbis) {
712 // Each sample's data is suffixed by the number of page samples
713 // or -1 if not available.
714 *sampleSize += sizeof(int32_t);
715 }
716
717 return OK;
718 }
719
getSampleTrackIndex(size_t * trackIndex)720 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
721 Mutex::Autolock autoLock(mLock);
722
723 ssize_t minIndex = fetchAllTrackSamples();
724
725 if (minIndex < 0) {
726 return ERROR_END_OF_STREAM;
727 }
728
729 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
730 *trackIndex = info->mTrackIndex;
731
732 return OK;
733 }
734
getSampleTime(int64_t * sampleTimeUs)735 status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
736 Mutex::Autolock autoLock(mLock);
737
738 ssize_t minIndex = fetchAllTrackSamples();
739
740 if (minIndex < 0) {
741 return ERROR_END_OF_STREAM;
742 }
743
744 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
745 *sampleTimeUs = info->mSamples.begin()->mSampleTimeUs;
746
747 return OK;
748 }
749
getSampleMeta(sp<MetaData> * sampleMeta)750 status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
751 Mutex::Autolock autoLock(mLock);
752
753 *sampleMeta = NULL;
754
755 ssize_t minIndex = fetchAllTrackSamples();
756
757 if (minIndex < 0) {
758 status_t err = minIndex;
759 return err;
760 }
761
762 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
763 *sampleMeta = new MetaData(info->mSamples.begin()->mBuffer->meta_data());
764
765 return OK;
766 }
767
getMetrics(Parcel * reply)768 status_t NuMediaExtractor::getMetrics(Parcel *reply) {
769 status_t status = mImpl->getMetrics(reply);
770 return status;
771 }
772
getTotalBitrate(int64_t * bitrate) const773 bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const {
774 if (mTotalBitrate > 0) {
775 *bitrate = mTotalBitrate;
776 return true;
777 }
778
779 off64_t size;
780 if (mDurationUs > 0 && mDataSource->getSize(&size) == OK) {
781 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec
782 return true;
783 }
784
785 return false;
786 }
787
788 // Returns true iff cached duration is available/applicable.
getCachedDuration(int64_t * durationUs,bool * eos) const789 bool NuMediaExtractor::getCachedDuration(
790 int64_t *durationUs, bool *eos) const {
791 Mutex::Autolock autoLock(mLock);
792
793 int64_t bitrate;
794 if ((mDataSource->flags() & DataSource::kIsCachingDataSource)
795 && getTotalBitrate(&bitrate)) {
796 sp<NuCachedSource2> cachedSource =
797 static_cast<NuCachedSource2 *>(mDataSource.get());
798
799 status_t finalStatus;
800 size_t cachedDataRemaining =
801 cachedSource->approxDataRemaining(&finalStatus);
802
803 *durationUs = cachedDataRemaining * 8000000ll / bitrate;
804 *eos = (finalStatus != OK);
805 return true;
806 }
807
808 return false;
809 }
810
811 } // namespace android
812