blob: d480a86e33d3ed4ef7ae7c4b25b587854dc47240 [file] [log] [blame]
/*
* 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"
namespace android {
//--------------------------------------------------------------------------------------------------
AudioSfDecoder::AudioSfDecoder(const AudioPlayback_Parameters* params) : GenericPlayer(params),
mBitrate(-1),
mNumChannels(1),
mSampleRateHz(0),
mDurationUsec(-1),
mDecodeBuffer(NULL),
mCacheStatus(kStatusEmpty),
mCacheFill(0),
mLastNotifiedCacheFill(0),
mCacheFillNotifThreshold(100),
mTimeDelta(-1),
mSeekTimeMsec(0),
mLastDecodedPositionUs(-1)
{
SL_LOGV("AudioSfDecoder::AudioSfDecoder()");
}
AudioSfDecoder::~AudioSfDecoder() {
SL_LOGV("AudioSfDecoder::~AudioSfDecoder()");
}
//--------------------------------------------------
void AudioSfDecoder::play() {
SL_LOGV("AudioSfDecoder::play");
GenericPlayer::play();
(new AMessage(kWhatDecode, id()))->post();
}
void AudioSfDecoder::startPrefetch_async() {
SL_LOGV("AudioSfDecoder::startPrefetch_async()");
if (wantPrefetch()) {
SL_LOGV("AudioSfDecoder::startPrefetch_async(): sending check cache msg");
mStateFlags |= kFlagPreparing | kFlagBuffering;
(new AMessage(kWhatCheckCache, id()))->post();
}
}
//--------------------------------------------------
// Event handlers
void AudioSfDecoder::onPrepare() {
SL_LOGI("AudioSfDecoder::onPrepare()");
sp<DataSource> dataSource;
switch (mDataLocatorType) {
case kDataLocatorNone:
SL_LOGE("AudioSfDecoder::onPrepare: no data locator set");
notifyPrepared(MEDIA_ERROR_BASE);
return;
case kDataLocatorUri:
dataSource = DataSource::CreateFromURI(mDataLocator.uriRef);
if (dataSource == NULL) {
SL_LOGE("AudioSfDecoder::onPrepare(): Error opening %s", mDataLocator.uriRef);
notifyPrepared(MEDIA_ERROR_BASE);
return;
}
break;
case kDataLocatorFd:
{
dataSource = new FileSource(
mDataLocator.fdi.fd, mDataLocator.fdi.offset, mDataLocator.fdi.length);
status_t err = dataSource->initCheck();
if (err != OK) {
notifyPrepared(err);
return;
}
break;
}
default:
TRESPASS();
}
sp<MediaExtractor> extractor = MediaExtractor::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)) {
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 = extractor->getTrack(audioTrackIndex);
sp<MetaData> meta = source->getFormat();
off64_t size;
int64_t durationUs;
if (dataSource->getSize(&size) == OK
&& meta->findInt64(kKeyDuration, &durationUs)) {
mBitrate = size * 8000000ll / durationUs; // in bits/sec
mDurationUsec = durationUs;
} else {
mBitrate = -1;
mDurationUsec = -1;
}
if (!isRawAudio) {
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
source = OMXCodec::Create(
client.interface(), meta, false /* createEncoder */,
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;
}
mDataSource = dataSource;
mAudioSource = source;
CHECK(meta->findInt32(kKeyChannelCount, &mNumChannels));
CHECK(meta->findInt32(kKeySampleRate, &mSampleRateHz));
if (!wantPrefetch()) {
SL_LOGV("AudioSfDecoder::onPrepare: no need to prefetch");
// doesn't need prefetching, notify good to go
mCacheStatus = kStatusHigh;
mCacheFill = 1000;
notifyStatus();
notifyCacheFill();
}
// at this point we have enough information about the source to create the sink that
// will consume the data
createAudioSink();
GenericPlayer::onPrepare();
SL_LOGI("AudioSfDecoder::onPrepare() done, mStateFlags=0x%x", mStateFlags);
}
void AudioSfDecoder::onPause() {
SL_LOGI("AudioSfDecoder::onPause()");
GenericPlayer::onPause();
pauseAudioSink();
}
void AudioSfDecoder::onPlay() {
SL_LOGI("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(mSeekLock);
mStateFlags |= kFlagSeeking;
mSeekTimeMsec = timeMsec;
mTimeDelta = -1;
mLastDecodedPositionUs = -1;
}
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;
}
}
void AudioSfDecoder::onCheckCache(const sp<AMessage> &msg) {
//SL_LOGV("AudioSfDecoder::onCheckCache");
bool eos;
CacheStatus 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;
}
mTimeDelta = -1;
if (mStateFlags & kFlagPlaying) {
(new AMessage(kWhatDecode, id()))->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, id()))->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) {
readOptions.setSeekTo(mSeekTimeMsec * 1000);
}
{
Mutex::Autolock _l(mDecodeBufferLock);
if (NULL != mDecodeBuffer) {
// the current decoded buffer hasn't been rendered, drop it
mDecodeBuffer->release();
mDecodeBuffer = NULL;
}
err = mAudioSource->read(&mDecodeBuffer, &readOptions);
if (err == OK) {
CHECK(mDecodeBuffer->meta_data()->findInt64(kKeyTime, &mLastDecodedPositionUs));
}
}
{
Mutex::Autolock _l(mSeekLock);
if (mStateFlags & kFlagSeeking) {
mStateFlags &= ~kFlagSeeking;
}
}
//-------------------------------- Handle return of decode
if (err != OK) {
bool continueDecoding = false;
switch(err) {
case ERROR_END_OF_STREAM:
if (0 < mDurationUsec) {
mLastDecodedPositionUs = mDurationUsec;
}
// handle notification and looping at end of stream
if (mStateFlags & kFlagPlaying) {
notify(PLAYEREVENT_ENDOFSTREAM, 1, true);
}
if (mStateFlags & kFlagLooping) {
seek(0);
// kick-off decoding again
continueDecoding = true;
}
break;
case INFO_FORMAT_CHANGED:
SL_LOGI("MediaSource::read encountered INFO_FORMAT_CHANGED");
// reconfigure output
updateAudioSink();
continueDecoding = true;
break;
case INFO_DISCONTINUITY:
SL_LOGI("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, id()))->post();
return;
}
} else {
return;
}
}
//-------------------------------- Render
sp<AMessage> msg = new AMessage(kWhatRender, id());
msg->post();
}
void AudioSfDecoder::onRender() {
//SL_LOGV("AudioSfDecoder::onRender");
Mutex::Autolock _l(mDecodeBufferLock);
if (NULL == mDecodeBuffer) {
// nothing to render, move along
SL_LOGV("AudioSfDecoder::onRender NULL buffer, exiting");
return;
}
mDecodeBuffer->release();
mDecodeBuffer = NULL;
}
void AudioSfDecoder::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatPrepare:
onPrepare();
break;
case kWhatDecode:
onDecode();
break;
case kWhatRender:
onRender();
break;
case kWhatCheckCache:
onCheckCache(msg);
break;
case kWhatNotif:
onNotify(msg);
break;
case kWhatPlay:
onPlay();
break;
case kWhatPause:
onPause();
break;
/*
case kWhatSeek:
onSeek(msg);
break;
case kWhatLoop:
onLoop(msg);
break;
*/
default:
GenericPlayer::onMessageReceived(msg);
break;
}
}
//--------------------------------------------------
// Prepared state, prefetch status notifications
void AudioSfDecoder::notifyPrepared(status_t prepareRes) {
notify(PLAYEREVENT_PREPARED, (int32_t)prepareRes, true);
}
void AudioSfDecoder::notifyStatus() {
notify(PLAYEREVENT_PREFETCHSTATUSCHANGE, (int32_t)mCacheStatus, true);
}
void AudioSfDecoder::notifyCacheFill() {
mLastNotifiedCacheFill = mCacheFill;
notify(PLAYEREVENT_PREFETCHFILLLEVELUPDATE, (int32_t)mLastNotifiedCacheFill, true);
}
void AudioSfDecoder::onNotify(const sp<AMessage> &msg) {
if (NULL == mNotifyClient) {
return;
}
int32_t val;
if (msg->findInt32(PLAYEREVENT_PREFETCHSTATUSCHANGE, &val)) {
SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHSTATUSCHANGE, val);
mNotifyClient(kEventPrefetchStatusChange, val, mNotifyUser);
}
else if (msg->findInt32(PLAYEREVENT_PREFETCHFILLLEVELUPDATE, &val)) {
SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_PREFETCHFILLLEVELUPDATE, val);
mNotifyClient(kEventPrefetchFillLevelUpdate, val, mNotifyUser);
}
else if (msg->findInt32(PLAYEREVENT_ENDOFSTREAM, &val)) {
SL_LOGV("\tASfPlayer notifying %s = %d", PLAYEREVENT_ENDOFSTREAM, val);
mNotifyClient(kEventEndOfStream, val, mNotifyUser);
}
else {
GenericPlayer::onNotify(msg);
}
}
//--------------------------------------------------
// Private utility functions
bool AudioSfDecoder::wantPrefetch() {
return (mDataSource->flags() & DataSource::kWantsPrefetching);
}
int64_t AudioSfDecoder::getPositionUsec() {
Mutex::Autolock _l(mSeekLock);
if (mStateFlags & kFlagSeeking) {
return mSeekTimeMsec * 1000;
} else {
if (mLastDecodedPositionUs < 0) {
return 0;
} else {
return mLastDecodedPositionUs;
}
}
}
AudioSfDecoder::CacheStatus AudioSfDecoder::getCacheRemaining(bool *eos) {
sp<NuCachedSource2> cachedSource =
static_cast<NuCachedSource2 *>(mDataSource.get());
CacheStatus 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
uint32_t currentPositionUsec = getPositionUsec();
mCacheFill = (int16_t) ((1000.0
* (double)(currentPositionUsec + dataRemainingUs) / mDurationUsec));
//SL_LOGV("cacheFill = %d", mCacheFill);
// cache status is evaluated against duration thresholds
if (dataRemainingUs > DURATION_CACHED_HIGH_US) {
mCacheStatus = kStatusHigh;
//LOGV("high");
} else if (dataRemainingUs > DURATION_CACHED_MED_US) {
//LOGV("enough");
mCacheStatus = kStatusEnough;
} else if (dataRemainingUs < DURATION_CACHED_LOW_US) {
//LOGV("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;
}
} // namespace android