2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 //#define USE_LOG SLAndroidLogLevel_Verbose
19 #include "sles_allinclusive.h"
20 #include "android/android_AudioSfDecoder.h"
22 #include <media/stagefright/foundation/ADebug.h>
25 #define SIZE_CACHED_HIGH_BYTES 1000000
26 #define SIZE_CACHED_MED_BYTES 700000
27 #define SIZE_CACHED_LOW_BYTES 400000
31 //--------------------------------------------------------------------------------------------------
32 AudioSfDecoder::AudioSfDecoder(const AudioPlayback_Parameters* params) : GenericPlayer(params),
35 mAudioSourceStarted(false),
37 mDurationUsec(ANDROID_UNKNOWN_TIME),
40 // play event logic depends on the initial time being zero not ANDROID_UNKNOWN_TIME
41 mLastDecodedPositionUs(0)
43 SL_LOGD("AudioSfDecoder::AudioSfDecoder()");
47 AudioSfDecoder::~AudioSfDecoder() {
48 SL_LOGD("AudioSfDecoder::~AudioSfDecoder()");
52 void AudioSfDecoder::preDestroy() {
53 GenericPlayer::preDestroy();
54 SL_LOGD("AudioSfDecoder::preDestroy()");
56 Mutex::Autolock _l(mBufferSourceLock);
58 if (NULL != mDecodeBuffer) {
59 mDecodeBuffer->release();
63 if ((mAudioSource != 0) && mAudioSourceStarted) {
65 mAudioSourceStarted = false;
71 //--------------------------------------------------
72 void AudioSfDecoder::play() {
73 SL_LOGD("AudioSfDecoder::play");
75 GenericPlayer::play();
76 (new AMessage(kWhatDecode, id()))->post();
80 void AudioSfDecoder::getPositionMsec(int* msec) {
81 int64_t timeUsec = getPositionUsec();
82 if (timeUsec == ANDROID_UNKNOWN_TIME) {
83 *msec = ANDROID_UNKNOWN_TIME;
85 *msec = timeUsec / 1000;
90 //--------------------------------------------------
91 uint32_t AudioSfDecoder::getPcmFormatKeyCount() const {
92 return NB_PCMMETADATA_KEYS;
96 //--------------------------------------------------
97 bool AudioSfDecoder::getPcmFormatKeySize(uint32_t index, uint32_t* pKeySize) {
98 if (index >= NB_PCMMETADATA_KEYS) {
101 *pKeySize = strlen(kPcmDecodeMetadataKeys[index]) +1;
107 //--------------------------------------------------
108 bool AudioSfDecoder::getPcmFormatKeyName(uint32_t index, uint32_t keySize, char* keyName) {
109 uint32_t actualKeySize;
110 if (!getPcmFormatKeySize(index, &actualKeySize)) {
113 if (keySize < actualKeySize) {
116 strncpy(keyName, kPcmDecodeMetadataKeys[index], actualKeySize);
121 //--------------------------------------------------
122 bool AudioSfDecoder::getPcmFormatValueSize(uint32_t index, uint32_t* pValueSize) {
123 if (index >= NB_PCMMETADATA_KEYS) {
127 *pValueSize = sizeof(uint32_t);
133 //--------------------------------------------------
134 bool AudioSfDecoder::getPcmFormatKeyValue(uint32_t index, uint32_t size, uint32_t* pValue) {
135 uint32_t valueSize = 0;
136 if (!getPcmFormatValueSize(index, &valueSize)) {
138 } else if (size != valueSize) {
139 // this ensures we are accessing mPcmFormatValues with a valid size for that index
140 SL_LOGE("Error retrieving metadata value at index %d: using size of %d, should be %d",
141 index, size, valueSize);
144 android::Mutex::Autolock autoLock(mPcmFormatLock);
145 *pValue = mPcmFormatValues[index];
151 //--------------------------------------------------
153 // it is strictly verboten to call those methods outside of the event loop
155 // Initializes the data and audio sources, and update the PCM format info
156 // post-condition: upon successful initialization based on the player data locator
157 // GenericPlayer::onPrepare() was called
160 // mAudioSourceStarted == true
161 // All error returns from this method are via notifyPrepared(status) followed by "return".
162 void AudioSfDecoder::onPrepare() {
163 SL_LOGD("AudioSfDecoder::onPrepare()");
164 Mutex::Autolock _l(mBufferSourceLock);
167 android::Mutex::Autolock autoLock(mPcmFormatLock);
168 // Initialize the PCM format info with the known parameters before the start of the decode
169 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_BITSPERSAMPLE] = SL_PCMSAMPLEFORMAT_FIXED_16;
170 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CONTAINERSIZE] = 16;
171 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_ENDIANNESS] = SL_BYTEORDER_LITTLEENDIAN;
172 // initialization with the default values: they will be replaced by the actual values
173 // once the decoder has figured them out
174 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = UNKNOWN_NUMCHANNELS;
175 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = UNKNOWN_SAMPLERATE;
176 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = UNKNOWN_CHANNELMASK;
179 //---------------------------------
180 // Instantiate and initialize the data source for the decoder
181 sp<DataSource> dataSource;
183 switch (mDataLocatorType) {
185 case kDataLocatorNone:
186 SL_LOGE("AudioSfDecoder::onPrepare: no data locator set");
187 notifyPrepared(MEDIA_ERROR_BASE);
190 case kDataLocatorUri:
191 dataSource = DataSource::CreateFromURI(mDataLocator.uriRef);
192 if (dataSource == NULL) {
193 SL_LOGE("AudioSfDecoder::onPrepare(): Error opening %s", mDataLocator.uriRef);
194 notifyPrepared(MEDIA_ERROR_BASE);
201 // As FileSource unconditionally takes ownership of the fd and closes it, then
202 // we have to make a dup for FileSource if the app wants to keep ownership itself
203 int fd = mDataLocator.fdi.fd;
204 if (mDataLocator.fdi.mCloseAfterUse) {
205 mDataLocator.fdi.mCloseAfterUse = false;
209 dataSource = new FileSource(fd, mDataLocator.fdi.offset, mDataLocator.fdi.length);
210 status_t err = dataSource->initCheck();
222 //---------------------------------
223 // Instanciate and initialize the decoder attached to the data source
224 sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
225 if (extractor == NULL) {
226 SL_LOGE("AudioSfDecoder::onPrepare: Could not instantiate extractor.");
227 notifyPrepared(ERROR_UNSUPPORTED);
231 ssize_t audioTrackIndex = -1;
232 bool isRawAudio = false;
233 for (size_t i = 0; i < extractor->countTracks(); ++i) {
234 sp<MetaData> meta = extractor->getTrackMetaData(i);
237 CHECK(meta->findCString(kKeyMIMEType, &mime));
239 if (!strncasecmp("audio/", mime, 6)) {
240 if (isSupportedCodec(mime)) {
243 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) {
251 if (audioTrackIndex < 0) {
252 SL_LOGE("AudioSfDecoder::onPrepare: Could not find a supported audio track.");
253 notifyPrepared(ERROR_UNSUPPORTED);
257 sp<MediaSource> source = extractor->getTrack(audioTrackIndex);
258 sp<MetaData> meta = source->getFormat();
260 // we can't trust the OMXCodec (if there is one) to issue a INFO_FORMAT_CHANGED so we want
261 // to have some meaningful values as soon as possible.
262 int32_t channelCount;
263 bool hasChannelCount = meta->findInt32(kKeyChannelCount, &channelCount);
265 bool hasSampleRate = meta->findInt32(kKeySampleRate, &sr);
267 // first compute the duration
270 int32_t durationMsec;
271 if (dataSource->getSize(&size) == OK
272 && meta->findInt64(kKeyDuration, &durationUs)) {
273 if (durationUs != 0) {
274 mBitrate = size * 8000000ll / durationUs; // in bits/sec
278 mDurationUsec = durationUs;
279 durationMsec = durationUs / 1000;
282 mDurationUsec = ANDROID_UNKNOWN_TIME;
283 durationMsec = ANDROID_UNKNOWN_TIME;
286 // then assign the duration under the settings lock
288 Mutex::Autolock _l(mSettingsLock);
289 mDurationMsec = durationMsec;
292 // the audio content is not raw PCM, so we need a decoder
295 CHECK_EQ(client.connect(), (status_t)OK);
297 source = OMXCodec::Create(
298 client.interface(), meta, false /* createEncoder */,
301 if (source == NULL) {
302 SL_LOGE("AudioSfDecoder::onPrepare: Could not instantiate decoder.");
303 notifyPrepared(ERROR_UNSUPPORTED);
307 meta = source->getFormat();
311 if (source->start() != OK) {
312 SL_LOGE("AudioSfDecoder::onPrepare: Failed to start source/decoder.");
313 notifyPrepared(MEDIA_ERROR_BASE);
317 //---------------------------------
318 // The data source, and audio source (a decoder if required) are ready to be used
319 mDataSource = dataSource;
320 mAudioSource = source;
321 mAudioSourceStarted = true;
323 if (!hasChannelCount) {
324 CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
327 if (!hasSampleRate) {
328 CHECK(meta->findInt32(kKeySampleRate, &sr));
330 // FIXME add code below once channel mask support is in, currently initialized to default
331 // value computed from the channel count
332 // if (!hasChannelMask) {
333 // CHECK(meta->findInt32(kKeyChannelMask, &channelMask));
336 if (!wantPrefetch()) {
337 SL_LOGV("AudioSfDecoder::onPrepare: no need to prefetch");
338 // doesn't need prefetching, notify good to go
339 mCacheStatus = kStatusHigh;
346 android::Mutex::Autolock autoLock(mPcmFormatLock);
347 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = sr;
348 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = channelCount;
349 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] =
350 channelCountToMask(channelCount);
353 // at this point we have enough information about the source to create the sink that
354 // will consume the data
357 // signal successful completion of prepare
358 mStateFlags |= kFlagPrepared;
360 GenericPlayer::onPrepare();
361 SL_LOGD("AudioSfDecoder::onPrepare() done, mStateFlags=0x%x", mStateFlags);
365 void AudioSfDecoder::onPause() {
366 SL_LOGV("AudioSfDecoder::onPause()");
367 GenericPlayer::onPause();
372 void AudioSfDecoder::onPlay() {
373 SL_LOGV("AudioSfDecoder::onPlay()");
374 GenericPlayer::onPlay();
379 void AudioSfDecoder::onSeek(const sp<AMessage> &msg) {
380 SL_LOGV("AudioSfDecoder::onSeek");
382 CHECK(msg->findInt64(WHATPARAM_SEEK_SEEKTIME_MS, &timeMsec));
384 Mutex::Autolock _l(mTimeLock);
385 mStateFlags |= kFlagSeeking;
386 mSeekTimeMsec = timeMsec;
387 // don't set mLastDecodedPositionUs to ANDROID_UNKNOWN_TIME; getPositionUsec
388 // ignores mLastDecodedPositionUs while seeking, and substitutes the seek goal instead
391 GenericPlayer::onSeek(msg);
395 void AudioSfDecoder::onLoop(const sp<AMessage> &msg) {
396 SL_LOGV("AudioSfDecoder::onLoop");
398 CHECK(msg->findInt32(WHATPARAM_LOOP_LOOPING, &loop));
401 //SL_LOGV("AudioSfDecoder::onLoop start looping");
402 mStateFlags |= kFlagLooping;
404 //SL_LOGV("AudioSfDecoder::onLoop stop looping");
405 mStateFlags &= ~kFlagLooping;
409 GenericPlayer::onLoop(msg);
413 void AudioSfDecoder::onCheckCache(const sp<AMessage> &msg) {
414 //SL_LOGV("AudioSfDecoder::onCheckCache");
416 CacheStatus_t status = getCacheRemaining(&eos);
418 if (eos || status == kStatusHigh
419 || ((mStateFlags & kFlagPreparing) && (status >= kStatusEnough))) {
420 if (mStateFlags & kFlagPlaying) {
423 mStateFlags &= ~kFlagBuffering;
425 SL_LOGV("AudioSfDecoder::onCheckCache: buffering done.");
427 if (mStateFlags & kFlagPreparing) {
428 //SL_LOGV("AudioSfDecoder::onCheckCache: preparation done.");
429 mStateFlags &= ~kFlagPreparing;
432 if (mStateFlags & kFlagPlaying) {
433 (new AMessage(kWhatDecode, id()))->post();
442 void AudioSfDecoder::onDecode() {
443 SL_LOGV("AudioSfDecoder::onDecode");
445 //-------------------------------- Need to buffer some more before decoding?
447 if (mDataSource == 0) {
448 // application set play state to paused which failed, then set play state to playing
453 && (getCacheRemaining(&eos) == kStatusLow)
455 SL_LOGV("buffering more.");
457 if (mStateFlags & kFlagPlaying) {
460 mStateFlags |= kFlagBuffering;
461 (new AMessage(kWhatCheckCache, id()))->post(100000);
465 if (!(mStateFlags & (kFlagPlaying | kFlagBuffering | kFlagPreparing))) {
466 // don't decode if we're not buffering, prefetching or playing
467 //SL_LOGV("don't decode: not buffering, prefetching or playing");
471 //-------------------------------- Decode
473 MediaSource::ReadOptions readOptions;
474 if (mStateFlags & kFlagSeeking) {
475 assert(mSeekTimeMsec != ANDROID_UNKNOWN_TIME);
476 readOptions.setSeekTo(mSeekTimeMsec * 1000);
479 int64_t timeUsec = ANDROID_UNKNOWN_TIME;
481 Mutex::Autolock _l(mBufferSourceLock);
483 if (NULL != mDecodeBuffer) {
484 // the current decoded buffer hasn't been rendered, drop it
485 mDecodeBuffer->release();
486 mDecodeBuffer = NULL;
488 if(!mAudioSourceStarted) {
491 err = mAudioSource->read(&mDecodeBuffer, &readOptions);
493 // FIXME workaround apparent bug in AAC decoder: kKeyTime is 3 frames old if length is 0
494 if (mDecodeBuffer->range_length() == 0) {
495 timeUsec = ANDROID_UNKNOWN_TIME;
497 CHECK(mDecodeBuffer->meta_data()->findInt64(kKeyTime, &timeUsec));
503 Mutex::Autolock _l(mTimeLock);
504 if (mStateFlags & kFlagSeeking) {
505 mStateFlags &= ~kFlagSeeking;
506 mSeekTimeMsec = ANDROID_UNKNOWN_TIME;
508 if (timeUsec != ANDROID_UNKNOWN_TIME) {
509 // Note that though we've decoded this position, we haven't rendered it yet.
510 // So a GetPosition called after this point will observe the advanced position,
511 // even though the PCM may not have been supplied to the sink. That's OK as
512 // we don't claim to provide frame-accurate (let alone sample-accurate) GetPosition.
513 mLastDecodedPositionUs = timeUsec;
517 //-------------------------------- Handle return of decode
519 bool continueDecoding = false;
521 case ERROR_END_OF_STREAM:
522 if (0 < mDurationUsec) {
523 Mutex::Autolock _l(mTimeLock);
524 mLastDecodedPositionUs = mDurationUsec;
526 // handle notification and looping at end of stream
527 if (mStateFlags & kFlagPlaying) {
528 notify(PLAYEREVENT_ENDOFSTREAM, 1, true);
530 if (mStateFlags & kFlagLooping) {
532 // kick-off decoding again
533 continueDecoding = true;
536 case INFO_FORMAT_CHANGED:
537 SL_LOGD("MediaSource::read encountered INFO_FORMAT_CHANGED");
538 // reconfigure output
540 Mutex::Autolock _l(mBufferSourceLock);
541 hasNewDecodeParams();
543 continueDecoding = true;
545 case INFO_DISCONTINUITY:
546 SL_LOGD("MediaSource::read encountered INFO_DISCONTINUITY");
547 continueDecoding = true;
550 SL_LOGE("MediaSource::read returned error %d", err);
553 if (continueDecoding) {
554 if (NULL == mDecodeBuffer) {
555 (new AMessage(kWhatDecode, id()))->post();
563 //-------------------------------- Render
564 sp<AMessage> msg = new AMessage(kWhatRender, id());
570 void AudioSfDecoder::onMessageReceived(const sp<AMessage> &msg) {
571 switch (msg->what()) {
580 case kWhatCheckCache:
585 GenericPlayer::onMessageReceived(msg);
590 //--------------------------------------------------
591 // Prepared state, prefetch status notifications
592 void AudioSfDecoder::notifyPrepared(status_t prepareRes) {
593 assert(!(mStateFlags & (kFlagPrepared | kFlagPreparedUnsuccessfully)));
594 if (NO_ERROR == prepareRes) {
595 // The "then" fork is not currently used, but is kept here to make it easier
596 // to replace by a new signalPrepareCompletion(status) if we re-visit this later.
597 mStateFlags |= kFlagPrepared;
599 mStateFlags |= kFlagPreparedUnsuccessfully;
601 // Do not call the superclass onPrepare to notify, because it uses a default error
602 // status code but we can provide a more specific one.
603 // GenericPlayer::onPrepare();
604 notify(PLAYEREVENT_PREPARED, (int32_t)prepareRes, true);
605 SL_LOGD("AudioSfDecoder::onPrepare() done, mStateFlags=0x%x", mStateFlags);
609 void AudioSfDecoder::onNotify(const sp<AMessage> &msg) {
610 notif_cbf_t notifyClient;
613 android::Mutex::Autolock autoLock(mNotifyClientLock);
614 if (NULL == mNotifyClient) {
617 notifyClient = mNotifyClient;
618 notifyUser = mNotifyUser;
622 if (msg->findInt32(PLAYEREVENT_PREFETCHSTATUSCHANGE, &val)) {
623 SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHSTATUSCHANGE, val);
624 notifyClient(kEventPrefetchStatusChange, val, 0, notifyUser);
626 else if (msg->findInt32(PLAYEREVENT_PREFETCHFILLLEVELUPDATE, &val)) {
627 SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHFILLLEVELUPDATE, val);
628 notifyClient(kEventPrefetchFillLevelUpdate, val, 0, notifyUser);
630 else if (msg->findInt32(PLAYEREVENT_ENDOFSTREAM, &val)) {
631 SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_ENDOFSTREAM, val);
632 notifyClient(kEventEndOfStream, val, 0, notifyUser);
635 GenericPlayer::onNotify(msg);
640 //--------------------------------------------------
641 // Private utility functions
643 bool AudioSfDecoder::wantPrefetch() {
644 if (mDataSource != 0) {
645 return (mDataSource->flags() & DataSource::kWantsPrefetching);
647 // happens if an improper data locator was passed, if the media extractor couldn't be
648 // initialized, if there is no audio track in the media, if the OMX decoder couldn't be
649 // instantiated, if the source couldn't be opened, or if the MediaSource
650 // couldn't be started
651 SL_LOGV("AudioSfDecoder::wantPrefetch() tries to access NULL mDataSource");
657 int64_t AudioSfDecoder::getPositionUsec() {
658 Mutex::Autolock _l(mTimeLock);
659 if (mStateFlags & kFlagSeeking) {
660 return mSeekTimeMsec * 1000;
662 return mLastDecodedPositionUs;
667 CacheStatus_t AudioSfDecoder::getCacheRemaining(bool *eos) {
668 sp<NuCachedSource2> cachedSource =
669 static_cast<NuCachedSource2 *>(mDataSource.get());
671 CacheStatus_t oldStatus = mCacheStatus;
673 status_t finalStatus;
674 size_t dataRemaining = cachedSource->approxDataRemaining(&finalStatus);
675 *eos = (finalStatus != OK);
677 CHECK_GE(mBitrate, 0);
679 int64_t dataRemainingUs = dataRemaining * 8000000ll / mBitrate;
680 //SL_LOGV("AudioSfDecoder::getCacheRemaining: approx %.2f secs remaining (eos=%d)",
681 // dataRemainingUs / 1E6, *eos);
684 // data is buffered up to the end of the stream, it can't get any better than this
685 mCacheStatus = kStatusHigh;
689 if (mDurationUsec > 0) {
692 // fill level is ratio of how much has been played + how much is
693 // cached, divided by total duration
694 uint32_t currentPositionUsec = getPositionUsec();
695 if (currentPositionUsec == ANDROID_UNKNOWN_TIME) {
696 // if we don't know where we are, assume the worst for the fill ratio
697 currentPositionUsec = 0;
699 if (mDurationUsec > 0) {
700 mCacheFill = (int16_t) ((1000.0
701 * (double)(currentPositionUsec + dataRemainingUs) / mDurationUsec));
705 //SL_LOGV("cacheFill = %d", mCacheFill);
707 // cache status is evaluated against duration thresholds
708 if (dataRemainingUs > DURATION_CACHED_HIGH_MS*1000) {
709 mCacheStatus = kStatusHigh;
711 } else if (dataRemainingUs > DURATION_CACHED_MED_MS*1000) {
713 mCacheStatus = kStatusEnough;
714 } else if (dataRemainingUs < DURATION_CACHED_LOW_MS*1000) {
716 mCacheStatus = kStatusLow;
718 mCacheStatus = kStatusIntermediate;
724 // cache status is evaluated against cache amount thresholds
725 // (no duration so we don't have the bitrate either, could be derived from format?)
726 if (dataRemaining > SIZE_CACHED_HIGH_BYTES) {
727 mCacheStatus = kStatusHigh;
728 } else if (dataRemaining > SIZE_CACHED_MED_BYTES) {
729 mCacheStatus = kStatusEnough;
730 } else if (dataRemaining < SIZE_CACHED_LOW_BYTES) {
731 mCacheStatus = kStatusLow;
733 mCacheStatus = kStatusIntermediate;
739 if (oldStatus != mCacheStatus) {
743 if (abs(mCacheFill - mLastNotifiedCacheFill) > mCacheFillNotifThreshold) {
751 void AudioSfDecoder::hasNewDecodeParams() {
753 if ((mAudioSource != 0) && mAudioSourceStarted) {
754 sp<MetaData> meta = mAudioSource->getFormat();
756 int32_t channelCount;
757 CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
759 CHECK(meta->findInt32(kKeySampleRate, &sr));
761 // FIXME similar to onPrepare()
763 android::Mutex::Autolock autoLock(mPcmFormatLock);
764 SL_LOGV("format changed: old sr=%d, channels=%d; new sr=%d, channels=%d",
765 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE],
766 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS],
768 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = channelCount;
769 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = sr;
770 mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] =
771 channelCountToMask(channelCount);
775 // alert users of those params
779 static const char* const kUnsupportedCodecs[] = { MEDIA_MIMETYPE_AUDIO_AMR_NB,
780 MEDIA_MIMETYPE_AUDIO_AMR_WB };
781 #define NB_UNSUPPORTED_CODECS (sizeof(kUnsupportedCodecs)/sizeof(kUnsupportedCodecs[0]))
783 bool AudioSfDecoder::isSupportedCodec(const char* mime) {
784 for (unsigned int i = 0 ; i < NB_UNSUPPORTED_CODECS ; i++) {
785 if (!strcasecmp(mime, kUnsupportedCodecs[i])) {
792 } // namespace android