| /* | |
| * Copyright (C) 2011 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| //#define USE_LOG SLAndroidLogLevel_Verbose | |
| #include "sles_allinclusive.h" | |
| #include "android/android_AudioSfDecoder.h" | |
| #include "android/channels.h" | |
| #include <binder/IServiceManager.h> | |
| #include <datasource/DataSourceFactory.h> | |
| #include <datasource/FileSource.h> | |
| #include <datasource/NuCachedSource2.h> | |
| #include <media/IMediaHTTPService.h> | |
| #include <media/stagefright/foundation/ADebug.h> | |
| #include <media/stagefright/InterfaceUtils.h> | |
| #include <media/stagefright/MediaBuffer.h> | |
| #include <media/stagefright/MediaExtractorFactory.h> | |
| #include <media/stagefright/MetaData.h> | |
| #include <media/stagefright/SimpleDecodingSource.h> | |
| #include <media/stagefright/foundation/MediaDefs.h> | |
| #define SIZE_CACHED_HIGH_BYTES 1000000 | |
| #define SIZE_CACHED_MED_BYTES 700000 | |
| #define SIZE_CACHED_LOW_BYTES 400000 | |
| namespace android { | |
| //-------------------------------------------------------------------------------------------------- | |
| AudioSfDecoder::AudioSfDecoder(const AudioPlayback_Parameters* params) : GenericPlayer(params), | |
| mDataSource(0), | |
| mAudioSource(0), | |
| mAudioSourceStarted(false), | |
| mBitrate(-1), | |
| mDurationUsec(ANDROID_UNKNOWN_TIME), | |
| mDecodeBuffer(NULL), | |
| mSeekTimeMsec(0), | |
| // play event logic depends on the initial time being zero not ANDROID_UNKNOWN_TIME | |
| mLastDecodedPositionUs(0) | |
| { | |
| SL_LOGD("AudioSfDecoder::AudioSfDecoder()"); | |
| } | |
| AudioSfDecoder::~AudioSfDecoder() { | |
| SL_LOGD("AudioSfDecoder::~AudioSfDecoder()"); | |
| } | |
| void AudioSfDecoder::preDestroy() { | |
| GenericPlayer::preDestroy(); | |
| SL_LOGD("AudioSfDecoder::preDestroy()"); | |
| { | |
| Mutex::Autolock _l(mBufferSourceLock); | |
| if (NULL != mDecodeBuffer) { | |
| mDecodeBuffer->release(); | |
| mDecodeBuffer = NULL; | |
| } | |
| if ((mAudioSource != 0) && mAudioSourceStarted) { | |
| mAudioSource->stop(); | |
| mAudioSourceStarted = false; | |
| } | |
| } | |
| } | |
| //-------------------------------------------------- | |
| void AudioSfDecoder::play() { | |
| SL_LOGD("AudioSfDecoder::play"); | |
| GenericPlayer::play(); | |
| (new AMessage(kWhatDecode, this))->post(); | |
| } | |
| void AudioSfDecoder::getPositionMsec(int* msec) { | |
| int64_t timeUsec = getPositionUsec(); | |
| if (timeUsec == ANDROID_UNKNOWN_TIME) { | |
| *msec = ANDROID_UNKNOWN_TIME; | |
| } else { | |
| *msec = timeUsec / 1000; | |
| } | |
| } | |
| //-------------------------------------------------- | |
| uint32_t AudioSfDecoder::getPcmFormatKeyCount() const { | |
| return NB_PCMMETADATA_KEYS; | |
| } | |
| //-------------------------------------------------- | |
| bool AudioSfDecoder::getPcmFormatKeySize(uint32_t index, uint32_t* pKeySize) { | |
| if (index >= NB_PCMMETADATA_KEYS) { | |
| return false; | |
| } else { | |
| *pKeySize = strlen(kPcmDecodeMetadataKeys[index]) +1; | |
| return true; | |
| } | |
| } | |
| //-------------------------------------------------- | |
| bool AudioSfDecoder::getPcmFormatKeyName(uint32_t index, uint32_t keySize, char* keyName) { | |
| uint32_t actualKeySize; | |
| if (!getPcmFormatKeySize(index, &actualKeySize)) { | |
| return false; | |
| } | |
| if (keySize < actualKeySize) { | |
| return false; | |
| } | |
| strncpy(keyName, kPcmDecodeMetadataKeys[index], actualKeySize); | |
| return true; | |
| } | |
| //-------------------------------------------------- | |
| bool AudioSfDecoder::getPcmFormatValueSize(uint32_t index, uint32_t* pValueSize) { | |
| if (index >= NB_PCMMETADATA_KEYS) { | |
| *pValueSize = 0; | |
| return false; | |
| } else { | |
| *pValueSize = sizeof(uint32_t); | |
| return true; | |
| } | |
| } | |
| //-------------------------------------------------- | |
| bool AudioSfDecoder::getPcmFormatKeyValue(uint32_t index, uint32_t size, uint32_t* pValue) { | |
| uint32_t valueSize = 0; | |
| if (!getPcmFormatValueSize(index, &valueSize)) { | |
| return false; | |
| } else if (size != valueSize) { | |
| // this ensures we are accessing mPcmFormatValues with a valid size for that index | |
| SL_LOGE("Error retrieving metadata value at index %d: using size of %d, should be %d", | |
| index, size, valueSize); | |
| return false; | |
| } else { | |
| android::Mutex::Autolock autoLock(mPcmFormatLock); | |
| *pValue = mPcmFormatValues[index]; | |
| return true; | |
| } | |
| } | |
| //-------------------------------------------------- | |
| // Event handlers | |
| // it is strictly verboten to call those methods outside of the event loop | |
| // Initializes the data and audio sources, and update the PCM format info | |
| // post-condition: upon successful initialization based on the player data locator | |
| // GenericPlayer::onPrepare() was called | |
| // mDataSource != 0 | |
| // mAudioSource != 0 | |
| // mAudioSourceStarted == true | |
| // All error returns from this method are via notifyPrepared(status) followed by "return". | |
| void AudioSfDecoder::onPrepare() { | |
| SL_LOGD("AudioSfDecoder::onPrepare()"); | |
| Mutex::Autolock _l(mBufferSourceLock); | |
| { | |
| android::Mutex::Autolock autoLock(mPcmFormatLock); | |
| // Initialize the PCM format info with the known parameters before the start of the decode | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_BITSPERSAMPLE] = SL_PCMSAMPLEFORMAT_FIXED_16; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CONTAINERSIZE] = 16; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_ENDIANNESS] = SL_BYTEORDER_LITTLEENDIAN; | |
| // initialization with the default values: they will be replaced by the actual values | |
| // once the decoder has figured them out | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = UNKNOWN_NUMCHANNELS; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = UNKNOWN_SAMPLERATE; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = SL_ANDROID_UNKNOWN_CHANNELMASK; | |
| } | |
| //--------------------------------- | |
| // Instantiate and initialize the data source for the decoder | |
| sp<DataSource> dataSource; | |
| switch (mDataLocatorType) { | |
| case kDataLocatorNone: | |
| SL_LOGE("AudioSfDecoder::onPrepare: no data locator set"); | |
| notifyPrepared(MEDIA_ERROR_BASE); | |
| return; | |
| case kDataLocatorUri: | |
| dataSource = DataSourceFactory::getInstance()->CreateFromURI( | |
| NULL /* XXX httpService */, mDataLocator.uriRef); | |
| if (dataSource == NULL) { | |
| SL_LOGE("AudioSfDecoder::onPrepare(): Error opening %s", mDataLocator.uriRef); | |
| notifyPrepared(MEDIA_ERROR_BASE); | |
| return; | |
| } | |
| break; | |
| case kDataLocatorFd: | |
| { | |
| // As FileSource unconditionally takes ownership of the fd and closes it, then | |
| // we have to make a dup for FileSource if the app wants to keep ownership itself | |
| int fd = mDataLocator.fdi.fd; | |
| if (mDataLocator.fdi.mCloseAfterUse) { | |
| mDataLocator.fdi.mCloseAfterUse = false; | |
| } else { | |
| fd = ::dup(fd); | |
| } | |
| dataSource = new FileSource(fd, mDataLocator.fdi.offset, mDataLocator.fdi.length); | |
| status_t err = dataSource->initCheck(); | |
| if (err != OK) { | |
| notifyPrepared(err); | |
| return; | |
| } | |
| break; | |
| } | |
| // AndroidBufferQueue data source is handled by a subclass, | |
| // which does not call up to this method. Hence, the missing case. | |
| default: | |
| TRESPASS(); | |
| } | |
| //--------------------------------- | |
| // Instantiate and initialize the decoder attached to the data source | |
| sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource); | |
| if (extractor == NULL) { | |
| SL_LOGE("AudioSfDecoder::onPrepare: Could not instantiate extractor."); | |
| notifyPrepared(ERROR_UNSUPPORTED); | |
| return; | |
| } | |
| ssize_t audioTrackIndex = -1; | |
| bool isRawAudio = false; | |
| for (size_t i = 0; i < extractor->countTracks(); ++i) { | |
| sp<MetaData> meta = extractor->getTrackMetaData(i); | |
| const char *mime; | |
| CHECK(meta->findCString(kKeyMIMEType, &mime)); | |
| if (!strncasecmp("audio/", mime, 6)) { | |
| if (isSupportedCodec(mime)) { | |
| audioTrackIndex = i; | |
| if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) { | |
| isRawAudio = true; | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| if (audioTrackIndex < 0) { | |
| SL_LOGE("AudioSfDecoder::onPrepare: Could not find a supported audio track."); | |
| notifyPrepared(ERROR_UNSUPPORTED); | |
| return; | |
| } | |
| sp<MediaSource> source = CreateMediaSourceFromIMediaSource( | |
| extractor->getTrack(audioTrackIndex)); | |
| sp<MetaData> meta = source->getFormat(); | |
| // we can't trust the OMXCodec (if there is one) to issue a INFO_FORMAT_CHANGED so we want | |
| // to have some meaningful values as soon as possible. | |
| int32_t channelCount; | |
| bool hasChannelCount = meta->findInt32(kKeyChannelCount, &channelCount); | |
| int32_t sr; | |
| bool hasSampleRate = meta->findInt32(kKeySampleRate, &sr); | |
| // first compute the duration | |
| off64_t size; | |
| int64_t durationUs; | |
| int32_t durationMsec; | |
| if (dataSource->getSize(&size) == OK | |
| && meta->findInt64(kKeyDuration, &durationUs)) { | |
| if (durationUs != 0) { | |
| mBitrate = size * 8000000LL / durationUs; // in bits/sec | |
| } else { | |
| mBitrate = -1; | |
| } | |
| mDurationUsec = durationUs; | |
| durationMsec = durationUs / 1000; | |
| } else { | |
| mBitrate = -1; | |
| mDurationUsec = ANDROID_UNKNOWN_TIME; | |
| durationMsec = ANDROID_UNKNOWN_TIME; | |
| } | |
| // then assign the duration under the settings lock | |
| { | |
| Mutex::Autolock _l(mSettingsLock); | |
| mDurationMsec = durationMsec; | |
| } | |
| // the audio content is not raw PCM, so we need a decoder | |
| if (!isRawAudio) { | |
| source = SimpleDecodingSource::Create(source); | |
| if (source == NULL) { | |
| SL_LOGE("AudioSfDecoder::onPrepare: Could not instantiate decoder."); | |
| notifyPrepared(ERROR_UNSUPPORTED); | |
| return; | |
| } | |
| meta = source->getFormat(); | |
| } | |
| if (source->start() != OK) { | |
| SL_LOGE("AudioSfDecoder::onPrepare: Failed to start source/decoder."); | |
| notifyPrepared(MEDIA_ERROR_BASE); | |
| return; | |
| } | |
| //--------------------------------- | |
| // The data source, and audio source (a decoder if required) are ready to be used | |
| mDataSource = dataSource; | |
| mAudioSource = source; | |
| mAudioSourceStarted = true; | |
| if (!hasChannelCount) { | |
| CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); | |
| } | |
| if (!hasSampleRate) { | |
| CHECK(meta->findInt32(kKeySampleRate, &sr)); | |
| } | |
| // FIXME add code below once channel mask support is in, currently initialized to default | |
| // value computed from the channel count | |
| // if (!hasChannelMask) { | |
| // CHECK(meta->findInt32(kKeyChannelMask, &channelMask)); | |
| // } | |
| if (!wantPrefetch()) { | |
| SL_LOGV("AudioSfDecoder::onPrepare: no need to prefetch"); | |
| // doesn't need prefetching, notify good to go | |
| mCacheStatus = kStatusHigh; | |
| mCacheFill = 1000; | |
| notifyStatus(); | |
| notifyCacheFill(); | |
| } | |
| { | |
| android::Mutex::Autolock autoLock(mPcmFormatLock); | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = sr; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = channelCount; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = | |
| sles_channel_out_mask_from_count(channelCount); | |
| } | |
| // at this point we have enough information about the source to create the sink that | |
| // will consume the data | |
| createAudioSink(); | |
| // signal successful completion of prepare | |
| mStateFlags |= kFlagPrepared; | |
| GenericPlayer::onPrepare(); | |
| SL_LOGD("AudioSfDecoder::onPrepare() done, mStateFlags=0x%x", mStateFlags); | |
| } | |
| void AudioSfDecoder::onPause() { | |
| SL_LOGV("AudioSfDecoder::onPause()"); | |
| GenericPlayer::onPause(); | |
| pauseAudioSink(); | |
| } | |
| void AudioSfDecoder::onPlay() { | |
| SL_LOGV("AudioSfDecoder::onPlay()"); | |
| GenericPlayer::onPlay(); | |
| startAudioSink(); | |
| } | |
| void AudioSfDecoder::onSeek(const sp<AMessage> &msg) { | |
| SL_LOGV("AudioSfDecoder::onSeek"); | |
| int64_t timeMsec; | |
| CHECK(msg->findInt64(WHATPARAM_SEEK_SEEKTIME_MS, &timeMsec)); | |
| Mutex::Autolock _l(mTimeLock); | |
| mStateFlags |= kFlagSeeking; | |
| mSeekTimeMsec = timeMsec; | |
| // don't set mLastDecodedPositionUs to ANDROID_UNKNOWN_TIME; getPositionUsec | |
| // ignores mLastDecodedPositionUs while seeking, and substitutes the seek goal instead | |
| // nop for now | |
| GenericPlayer::onSeek(msg); | |
| } | |
| void AudioSfDecoder::onLoop(const sp<AMessage> &msg) { | |
| SL_LOGV("AudioSfDecoder::onLoop"); | |
| int32_t loop; | |
| CHECK(msg->findInt32(WHATPARAM_LOOP_LOOPING, &loop)); | |
| if (loop) { | |
| //SL_LOGV("AudioSfDecoder::onLoop start looping"); | |
| mStateFlags |= kFlagLooping; | |
| } else { | |
| //SL_LOGV("AudioSfDecoder::onLoop stop looping"); | |
| mStateFlags &= ~kFlagLooping; | |
| } | |
| // nop for now | |
| GenericPlayer::onLoop(msg); | |
| } | |
| void AudioSfDecoder::onCheckCache(const sp<AMessage> &msg) { | |
| //SL_LOGV("AudioSfDecoder::onCheckCache"); | |
| bool eos; | |
| CacheStatus_t status = getCacheRemaining(&eos); | |
| if (eos || status == kStatusHigh | |
| || ((mStateFlags & kFlagPreparing) && (status >= kStatusEnough))) { | |
| if (mStateFlags & kFlagPlaying) { | |
| startAudioSink(); | |
| } | |
| mStateFlags &= ~kFlagBuffering; | |
| SL_LOGV("AudioSfDecoder::onCheckCache: buffering done."); | |
| if (mStateFlags & kFlagPreparing) { | |
| //SL_LOGV("AudioSfDecoder::onCheckCache: preparation done."); | |
| mStateFlags &= ~kFlagPreparing; | |
| } | |
| if (mStateFlags & kFlagPlaying) { | |
| (new AMessage(kWhatDecode, this))->post(); | |
| } | |
| return; | |
| } | |
| msg->post(100000); | |
| } | |
| void AudioSfDecoder::onDecode() { | |
| SL_LOGV("AudioSfDecoder::onDecode"); | |
| //-------------------------------- Need to buffer some more before decoding? | |
| bool eos; | |
| if (mDataSource == 0) { | |
| // application set play state to paused which failed, then set play state to playing | |
| return; | |
| } | |
| if (wantPrefetch() | |
| && (getCacheRemaining(&eos) == kStatusLow) | |
| && !eos) { | |
| SL_LOGV("buffering more."); | |
| if (mStateFlags & kFlagPlaying) { | |
| pauseAudioSink(); | |
| } | |
| mStateFlags |= kFlagBuffering; | |
| (new AMessage(kWhatCheckCache, this))->post(100000); | |
| return; | |
| } | |
| if (!(mStateFlags & (kFlagPlaying | kFlagBuffering | kFlagPreparing))) { | |
| // don't decode if we're not buffering, prefetching or playing | |
| //SL_LOGV("don't decode: not buffering, prefetching or playing"); | |
| return; | |
| } | |
| //-------------------------------- Decode | |
| status_t err; | |
| MediaSource::ReadOptions readOptions; | |
| if (mStateFlags & kFlagSeeking) { | |
| assert(mSeekTimeMsec != ANDROID_UNKNOWN_TIME); | |
| readOptions.setSeekTo(mSeekTimeMsec * 1000); | |
| } | |
| int64_t timeUsec = ANDROID_UNKNOWN_TIME; | |
| { | |
| Mutex::Autolock _l(mBufferSourceLock); | |
| if (NULL != mDecodeBuffer) { | |
| // the current decoded buffer hasn't been rendered, drop it | |
| mDecodeBuffer->release(); | |
| mDecodeBuffer = NULL; | |
| } | |
| if (!mAudioSourceStarted) { | |
| return; | |
| } | |
| err = mAudioSource->read(&mDecodeBuffer, &readOptions); | |
| if (err == OK) { | |
| // FIXME workaround apparent bug in AAC decoder: kKeyTime is 3 frames old if length is 0 | |
| if (mDecodeBuffer->range_length() == 0) { | |
| timeUsec = ANDROID_UNKNOWN_TIME; | |
| } else { | |
| CHECK(mDecodeBuffer->meta_data().findInt64(kKeyTime, &timeUsec)); | |
| } | |
| } else { | |
| // errors are handled below | |
| } | |
| } | |
| { | |
| Mutex::Autolock _l(mTimeLock); | |
| if (mStateFlags & kFlagSeeking) { | |
| mStateFlags &= ~kFlagSeeking; | |
| mSeekTimeMsec = ANDROID_UNKNOWN_TIME; | |
| } | |
| if (timeUsec != ANDROID_UNKNOWN_TIME) { | |
| // Note that though we've decoded this position, we haven't rendered it yet. | |
| // So a GetPosition called after this point will observe the advanced position, | |
| // even though the PCM may not have been supplied to the sink. That's OK as | |
| // we don't claim to provide AAC frame-accurate (let alone sample-accurate) GetPosition. | |
| mLastDecodedPositionUs = timeUsec; | |
| } | |
| } | |
| //-------------------------------- Handle return of decode | |
| if (err != OK) { | |
| bool continueDecoding = false; | |
| switch (err) { | |
| case ERROR_END_OF_STREAM: | |
| if (0 < mDurationUsec) { | |
| Mutex::Autolock _l(mTimeLock); | |
| mLastDecodedPositionUs = mDurationUsec; | |
| } | |
| // handle notification and looping at end of stream | |
| if (mStateFlags & kFlagPlaying) { | |
| notify(PLAYEREVENT_ENDOFSTREAM, 1, true /*async*/); | |
| } | |
| if (mStateFlags & kFlagLooping) { | |
| seek(0); | |
| // kick-off decoding again | |
| continueDecoding = true; | |
| } | |
| break; | |
| case INFO_FORMAT_CHANGED: | |
| SL_LOGD("MediaSource::read encountered INFO_FORMAT_CHANGED"); | |
| // reconfigure output | |
| { | |
| Mutex::Autolock _l(mBufferSourceLock); | |
| hasNewDecodeParams(); | |
| } | |
| continueDecoding = true; | |
| break; | |
| case INFO_DISCONTINUITY: | |
| SL_LOGD("MediaSource::read encountered INFO_DISCONTINUITY"); | |
| continueDecoding = true; | |
| break; | |
| default: | |
| SL_LOGE("MediaSource::read returned error %d", err); | |
| break; | |
| } | |
| if (continueDecoding) { | |
| if (NULL == mDecodeBuffer) { | |
| (new AMessage(kWhatDecode, this))->post(); | |
| return; | |
| } | |
| } else { | |
| return; | |
| } | |
| } | |
| //-------------------------------- Render | |
| sp<AMessage> msg = new AMessage(kWhatRender, this); | |
| msg->post(); | |
| } | |
| void AudioSfDecoder::onMessageReceived(const sp<AMessage> &msg) { | |
| switch (msg->what()) { | |
| case kWhatDecode: | |
| onDecode(); | |
| break; | |
| case kWhatRender: | |
| onRender(); | |
| break; | |
| case kWhatCheckCache: | |
| onCheckCache(msg); | |
| break; | |
| default: | |
| GenericPlayer::onMessageReceived(msg); | |
| break; | |
| } | |
| } | |
| //-------------------------------------------------- | |
| // Prepared state, prefetch status notifications | |
| void AudioSfDecoder::notifyPrepared(status_t prepareRes) { | |
| assert(!(mStateFlags & (kFlagPrepared | kFlagPreparedUnsuccessfully))); | |
| if (NO_ERROR == prepareRes) { | |
| // The "then" fork is not currently used, but is kept here to make it easier | |
| // to replace by a new signalPrepareCompletion(status) if we re-visit this later. | |
| mStateFlags |= kFlagPrepared; | |
| } else { | |
| mStateFlags |= kFlagPreparedUnsuccessfully; | |
| } | |
| // Do not call the superclass onPrepare to notify, because it uses a default error | |
| // status code but we can provide a more specific one. | |
| // GenericPlayer::onPrepare(); | |
| notify(PLAYEREVENT_PREPARED, (int32_t)prepareRes, true /*async*/); | |
| SL_LOGD("AudioSfDecoder::onPrepare() done, mStateFlags=0x%x", mStateFlags); | |
| } | |
| void AudioSfDecoder::onNotify(const sp<AMessage> &msg) { | |
| notif_cbf_t notifyClient; | |
| void* notifyUser; | |
| { | |
| android::Mutex::Autolock autoLock(mNotifyClientLock); | |
| if (NULL == mNotifyClient) { | |
| return; | |
| } else { | |
| notifyClient = mNotifyClient; | |
| notifyUser = mNotifyUser; | |
| } | |
| } | |
| int32_t val; | |
| if (msg->findInt32(PLAYEREVENT_PREFETCHSTATUSCHANGE, &val)) { | |
| SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHSTATUSCHANGE, val); | |
| notifyClient(kEventPrefetchStatusChange, val, 0, notifyUser); | |
| } | |
| else if (msg->findInt32(PLAYEREVENT_PREFETCHFILLLEVELUPDATE, &val)) { | |
| SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHFILLLEVELUPDATE, val); | |
| notifyClient(kEventPrefetchFillLevelUpdate, val, 0, notifyUser); | |
| } | |
| else if (msg->findInt32(PLAYEREVENT_ENDOFSTREAM, &val)) { | |
| SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_ENDOFSTREAM, val); | |
| notifyClient(kEventEndOfStream, val, 0, notifyUser); | |
| } | |
| else { | |
| GenericPlayer::onNotify(msg); | |
| } | |
| } | |
| //-------------------------------------------------- | |
| // Private utility functions | |
| bool AudioSfDecoder::wantPrefetch() { | |
| if (mDataSource != 0) { | |
| return (mDataSource->flags() & DataSource::kWantsPrefetching); | |
| } else { | |
| // happens if an improper data locator was passed, if the media extractor couldn't be | |
| // initialized, if there is no audio track in the media, if the OMX decoder couldn't be | |
| // instantiated, if the source couldn't be opened, or if the MediaSource | |
| // couldn't be started | |
| SL_LOGV("AudioSfDecoder::wantPrefetch() tries to access NULL mDataSource"); | |
| return false; | |
| } | |
| } | |
| int64_t AudioSfDecoder::getPositionUsec() { | |
| Mutex::Autolock _l(mTimeLock); | |
| if (mStateFlags & kFlagSeeking) { | |
| return mSeekTimeMsec * 1000; | |
| } else { | |
| return mLastDecodedPositionUs; | |
| } | |
| } | |
| CacheStatus_t AudioSfDecoder::getCacheRemaining(bool *eos) { | |
| sp<NuCachedSource2> cachedSource = | |
| static_cast<NuCachedSource2 *>(mDataSource.get()); | |
| CacheStatus_t oldStatus = mCacheStatus; | |
| status_t finalStatus; | |
| size_t dataRemaining = cachedSource->approxDataRemaining(&finalStatus); | |
| *eos = (finalStatus != OK); | |
| CHECK_GE(mBitrate, 0); | |
| int64_t dataRemainingUs = dataRemaining * 8000000LL / mBitrate; | |
| //SL_LOGV("AudioSfDecoder::getCacheRemaining: approx %.2f secs remaining (eos=%d)", | |
| // dataRemainingUs / 1E6, *eos); | |
| if (*eos) { | |
| // data is buffered up to the end of the stream, it can't get any better than this | |
| mCacheStatus = kStatusHigh; | |
| mCacheFill = 1000; | |
| } else { | |
| if (mDurationUsec > 0) { | |
| // known duration: | |
| // fill level is ratio of how much has been played + how much is | |
| // cached, divided by total duration | |
| int64_t currentPositionUsec = getPositionUsec(); | |
| if (currentPositionUsec == ANDROID_UNKNOWN_TIME) { | |
| // if we don't know where we are, assume the worst for the fill ratio | |
| currentPositionUsec = 0; | |
| } | |
| if (mDurationUsec > 0) { | |
| mCacheFill = (int16_t) ((1000.0 | |
| * (double)(currentPositionUsec + dataRemainingUs) / mDurationUsec)); | |
| } else { | |
| mCacheFill = 0; | |
| } | |
| //SL_LOGV("cacheFill = %d", mCacheFill); | |
| // cache status is evaluated against duration thresholds | |
| if (dataRemainingUs > DURATION_CACHED_HIGH_MS*1000) { | |
| mCacheStatus = kStatusHigh; | |
| //ALOGV("high"); | |
| } else if (dataRemainingUs > DURATION_CACHED_MED_MS*1000) { | |
| //ALOGV("enough"); | |
| mCacheStatus = kStatusEnough; | |
| } else if (dataRemainingUs < DURATION_CACHED_LOW_MS*1000) { | |
| //ALOGV("low"); | |
| mCacheStatus = kStatusLow; | |
| } else { | |
| mCacheStatus = kStatusIntermediate; | |
| } | |
| } else { | |
| // unknown duration: | |
| // cache status is evaluated against cache amount thresholds | |
| // (no duration so we don't have the bitrate either, could be derived from format?) | |
| if (dataRemaining > SIZE_CACHED_HIGH_BYTES) { | |
| mCacheStatus = kStatusHigh; | |
| } else if (dataRemaining > SIZE_CACHED_MED_BYTES) { | |
| mCacheStatus = kStatusEnough; | |
| } else if (dataRemaining < SIZE_CACHED_LOW_BYTES) { | |
| mCacheStatus = kStatusLow; | |
| } else { | |
| mCacheStatus = kStatusIntermediate; | |
| } | |
| } | |
| } | |
| if (oldStatus != mCacheStatus) { | |
| notifyStatus(); | |
| } | |
| if (abs(mCacheFill - mLastNotifiedCacheFill) > mCacheFillNotifThreshold) { | |
| notifyCacheFill(); | |
| } | |
| return mCacheStatus; | |
| } | |
| void AudioSfDecoder::hasNewDecodeParams() { | |
| if ((mAudioSource != 0) && mAudioSourceStarted) { | |
| sp<MetaData> meta = mAudioSource->getFormat(); | |
| int32_t channelCount; | |
| CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); | |
| int32_t sr; | |
| CHECK(meta->findInt32(kKeySampleRate, &sr)); | |
| // FIXME similar to onPrepare() | |
| { | |
| android::Mutex::Autolock autoLock(mPcmFormatLock); | |
| SL_LOGV("format changed: old sr=%d, channels=%d; new sr=%d, channels=%d", | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE], | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS], | |
| sr, channelCount); | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = channelCount; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = sr; | |
| mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = | |
| sles_channel_out_mask_from_count(channelCount); | |
| } | |
| // there's no need to do a notify of PLAYEREVENT_CHANNEL_COUNT, | |
| // because the only listener is for volume updates, and decoders don't support that | |
| } | |
| // alert users of those params | |
| updateAudioSink(); | |
| } | |
| static const char* const kPlaybackOnlyCodecs[] = { MEDIA_MIMETYPE_AUDIO_AMR_NB, | |
| MEDIA_MIMETYPE_AUDIO_AMR_WB }; | |
| #define NB_PLAYBACK_ONLY_CODECS (sizeof(kPlaybackOnlyCodecs)/sizeof(kPlaybackOnlyCodecs[0])) | |
| bool AudioSfDecoder::isSupportedCodec(const char* mime) { | |
| bool codecRequiresPermission = false; | |
| for (unsigned int i = 0 ; i < NB_PLAYBACK_ONLY_CODECS ; i++) { | |
| if (!strcasecmp(mime, kPlaybackOnlyCodecs[i])) { | |
| codecRequiresPermission = true; | |
| break; | |
| } | |
| } | |
| if (codecRequiresPermission) { | |
| // verify only the system can decode, for playback only | |
| return checkCallingPermission( | |
| String16("android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK")); | |
| } else { | |
| return true; | |
| } | |
| } | |
| } // namespace android |