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