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