blob: 57db6876d8cd27fc8f2fa15d3ac52241625973e9 [file] [log] [blame]
/*
* Copyright (C) 2010 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.
*/
#include "sles_allinclusive.h"
#include "android_prompts.h"
#include "android/android_AudioToCbRenderer.h"
#include "android/android_StreamPlayer.h"
#include "android/android_LocAVPlayer.h"
#include "android/include/AacBqToPcmCbRenderer.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <system/audio.h>
template class android::KeyedVector<SLuint32, android::AudioEffect* > ;
#define KEY_STREAM_TYPE_PARAMSIZE sizeof(SLint32)
#define AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE 500
#define AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE 2000
//-----------------------------------------------------------------------------
// FIXME this method will be absorbed into android_audioPlayer_setPlayState() once
// bufferqueue and uri/fd playback are moved under the GenericPlayer C++ object
SLresult aplayer_setPlayState(const android::sp<android::GenericPlayer> &ap, SLuint32 playState,
AndroidObjectState* pObjState) {
SLresult result = SL_RESULT_SUCCESS;
AndroidObjectState objState = *pObjState;
switch (playState) {
case SL_PLAYSTATE_STOPPED:
SL_LOGV("setting GenericPlayer to SL_PLAYSTATE_STOPPED");
ap->stop();
break;
case SL_PLAYSTATE_PAUSED:
SL_LOGV("setting GenericPlayer to SL_PLAYSTATE_PAUSED");
switch(objState) {
case ANDROID_UNINITIALIZED:
*pObjState = ANDROID_PREPARING;
ap->prepare();
break;
case ANDROID_PREPARING:
break;
case ANDROID_READY:
ap->pause();
break;
default:
SL_LOGE(ERROR_PLAYERSETPLAYSTATE_INVALID_OBJECT_STATE_D, playState);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
break;
case SL_PLAYSTATE_PLAYING: {
SL_LOGV("setting GenericPlayer to SL_PLAYSTATE_PLAYING");
switch(objState) {
case ANDROID_UNINITIALIZED:
*pObjState = ANDROID_PREPARING;
ap->prepare();
// intended fall through
case ANDROID_PREPARING:
// intended fall through
case ANDROID_READY:
ap->play();
break;
default:
SL_LOGE(ERROR_PLAYERSETPLAYSTATE_INVALID_OBJECT_STATE_D, playState);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
}
break;
default:
// checked by caller, should not happen
SL_LOGE(ERROR_SHOULDNT_BE_HERE_S, "aplayer_setPlayState");
result = SL_RESULT_INTERNAL_ERROR;
break;
}
return result;
}
//-----------------------------------------------------------------------------
// Callback associated with a AudioToCbRenderer of an SL ES AudioPlayer that gets its data
// from a URI or FD, to write the decoded audio data to a buffer queue
static size_t adecoder_writeToBufferQueue(const uint8_t *data, size_t size, CAudioPlayer* ap) {
if (!android::CallbackProtector::enterCbIfOk(ap->mCallbackProtector)) {
// it is not safe to enter the callback (the player is about to go away)
return 0;
}
size_t sizeConsumed = 0;
SL_LOGD("received %d bytes from decoder", size);
slBufferQueueCallback callback = NULL;
void * callbackPContext = NULL;
// push decoded data to the buffer queue
object_lock_exclusive(&ap->mObject);
if (ap->mBufferQueue.mState.count != 0) {
assert(ap->mBufferQueue.mFront != ap->mBufferQueue.mRear);
BufferHeader *oldFront = ap->mBufferQueue.mFront;
BufferHeader *newFront = &oldFront[1];
uint8_t *pDest = (uint8_t *)oldFront->mBuffer + ap->mBufferQueue.mSizeConsumed;
if (ap->mBufferQueue.mSizeConsumed + size < oldFront->mSize) {
// room to consume the whole or rest of the decoded data in one shot
ap->mBufferQueue.mSizeConsumed += size;
// consume data but no callback to the BufferQueue interface here
memcpy (pDest, data, size);
sizeConsumed = size;
} else {
// push as much as possible of the decoded data into the buffer queue
sizeConsumed = oldFront->mSize - ap->mBufferQueue.mSizeConsumed;
// the buffer at the head of the buffer queue is full, update the state
ap->mBufferQueue.mSizeConsumed = 0;
if (newFront == &ap->mBufferQueue.mArray[ap->mBufferQueue.mNumBuffers + 1]) {
newFront = ap->mBufferQueue.mArray;
}
ap->mBufferQueue.mFront = newFront;
ap->mBufferQueue.mState.count--;
ap->mBufferQueue.mState.playIndex++;
// consume data
memcpy (pDest, data, sizeConsumed);
// data has been copied to the buffer, and the buffer queue state has been updated
// we will notify the client if applicable
callback = ap->mBufferQueue.mCallback;
// save callback data
callbackPContext = ap->mBufferQueue.mContext;
}
} else {
// no available buffers in the queue to write the decoded data
sizeConsumed = 0;
}
object_unlock_exclusive(&ap->mObject);
// notify client
if (NULL != callback) {
(*callback)(&ap->mBufferQueue.mItf, callbackPContext);
}
ap->mCallbackProtector->exitCb();
return sizeConsumed;
}
//-----------------------------------------------------------------------------
int android_getMinFrameCount(uint32_t sampleRate) {
int afSampleRate;
if (android::AudioSystem::getOutputSamplingRate(&afSampleRate,
ANDROID_DEFAULT_OUTPUT_STREAM_TYPE) != android::NO_ERROR) {
return ANDROID_DEFAULT_AUDIOTRACK_BUFFER_SIZE;
}
int afFrameCount;
if (android::AudioSystem::getOutputFrameCount(&afFrameCount,
ANDROID_DEFAULT_OUTPUT_STREAM_TYPE) != android::NO_ERROR) {
return ANDROID_DEFAULT_AUDIOTRACK_BUFFER_SIZE;
}
uint32_t afLatency;
if (android::AudioSystem::getOutputLatency(&afLatency,
ANDROID_DEFAULT_OUTPUT_STREAM_TYPE) != android::NO_ERROR) {
return ANDROID_DEFAULT_AUDIOTRACK_BUFFER_SIZE;
}
// minimum nb of buffers to cover output latency, given the size of each hardware audio buffer
uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
if (minBufCount < 2) minBufCount = 2;
// minimum number of frames to cover output latency at the sample rate of the content
return (afFrameCount*sampleRate*minBufCount)/afSampleRate;
}
//-----------------------------------------------------------------------------
#define LEFT_CHANNEL_MASK 0x1 << 0
#define RIGHT_CHANNEL_MASK 0x1 << 1
void android_audioPlayer_volumeUpdate(CAudioPlayer* ap)
{
assert(ap != NULL);
// the source's channel count, where zero means unknown
SLuint8 channelCount = ap->mNumChannels;
// whether each channel is audible
bool leftAudibilityFactor, rightAudibilityFactor;
// mute has priority over solo
if (channelCount >= STEREO_CHANNELS) {
if (ap->mMuteMask & LEFT_CHANNEL_MASK) {
// left muted
leftAudibilityFactor = false;
} else {
// left not muted
if (ap->mSoloMask & LEFT_CHANNEL_MASK) {
// left soloed
leftAudibilityFactor = true;
} else {
// left not soloed
if (ap->mSoloMask & RIGHT_CHANNEL_MASK) {
// right solo silences left
leftAudibilityFactor = false;
} else {
// left and right are not soloed, and left is not muted
leftAudibilityFactor = true;
}
}
}
if (ap->mMuteMask & RIGHT_CHANNEL_MASK) {
// right muted
rightAudibilityFactor = false;
} else {
// right not muted
if (ap->mSoloMask & RIGHT_CHANNEL_MASK) {
// right soloed
rightAudibilityFactor = true;
} else {
// right not soloed
if (ap->mSoloMask & LEFT_CHANNEL_MASK) {
// left solo silences right
rightAudibilityFactor = false;
} else {
// left and right are not soloed, and right is not muted
rightAudibilityFactor = true;
}
}
}
// channel mute and solo are ignored for mono and unknown channel count sources
} else {
leftAudibilityFactor = true;
rightAudibilityFactor = true;
}
// compute volumes without setting
const bool audibilityFactors[2] = {leftAudibilityFactor, rightAudibilityFactor};
float volumes[2];
android_player_volumeUpdate(volumes, &ap->mVolume, channelCount, ap->mAmplFromDirectLevel,
audibilityFactors);
float leftVol = volumes[0], rightVol = volumes[1];
// set volume on the underlying media player or audio track
if (ap->mAPlayer != 0) {
ap->mAPlayer->setVolume(leftVol, rightVol);
} else if (ap->mAudioTrack != 0) {
ap->mAudioTrack->setVolume(leftVol, rightVol);
}
// changes in the AudioPlayer volume must be reflected in the send level:
// in SLEffectSendItf or in SLAndroidEffectSendItf?
// FIXME replace interface test by an internal API once we have one.
if (NULL != ap->mEffectSend.mItf) {
for (unsigned int i=0 ; i<AUX_MAX ; i++) {
if (ap->mEffectSend.mEnableLevels[i].mEnable) {
android_fxSend_setSendLevel(ap,
ap->mEffectSend.mEnableLevels[i].mSendLevel + ap->mVolume.mLevel);
// there's a single aux bus on Android, so we can stop looking once the first
// aux effect is found.
break;
}
}
} else if (NULL != ap->mAndroidEffectSend.mItf) {
android_fxSend_setSendLevel(ap, ap->mAndroidEffectSend.mSendLevel + ap->mVolume.mLevel);
}
}
// Called by android_audioPlayer_volumeUpdate and android_mediaPlayer_volumeUpdate to compute
// volumes, but setting volumes is handled by the caller.
void android_player_volumeUpdate(float *pVolumes /*[2]*/, const IVolume *volumeItf, unsigned
channelCount, float amplFromDirectLevel, const bool *audibilityFactors /*[2]*/)
{
assert(pVolumes != NULL);
assert(volumeItf != NULL);
// OK for audibilityFactors to be NULL
bool leftAudibilityFactor, rightAudibilityFactor;
// apply player mute factor
// note that AudioTrack has mute() but not MediaPlayer, so it's easier to use volume
// to mute for both rather than calling mute() for AudioTrack
// player is muted
if (volumeItf->mMute) {
leftAudibilityFactor = false;
rightAudibilityFactor = false;
// player isn't muted, and channel mute/solo audibility factors are available (AudioPlayer)
} else if (audibilityFactors != NULL) {
leftAudibilityFactor = audibilityFactors[0];
rightAudibilityFactor = audibilityFactors[1];
// player isn't muted, and channel mute/solo audibility factors aren't available (MediaPlayer)
} else {
leftAudibilityFactor = true;
rightAudibilityFactor = true;
}
// compute amplification as the combination of volume level and stereo position
// amplification (or attenuation) from volume level
float amplFromVolLevel = sles_to_android_amplification(volumeItf->mLevel);
// amplification from direct level (changed in SLEffectSendtItf and SLAndroidEffectSendItf)
float leftVol = amplFromVolLevel * amplFromDirectLevel;
float rightVol = leftVol;
// amplification from stereo position
if (volumeItf->mEnableStereoPosition) {
// Left/right amplification (can be attenuations) factors derived for the StereoPosition
float amplFromStereoPos[STEREO_CHANNELS];
// panning law depends on content channel count: mono to stereo panning vs stereo balance
if (1 == channelCount) {
// mono to stereo panning
double theta = (1000+volumeItf->mStereoPosition)*M_PI_4/1000.0f; // 0 <= theta <= Pi/2
amplFromStereoPos[0] = cos(theta);
amplFromStereoPos[1] = sin(theta);
// channel count is 0 (unknown), 2 (stereo), or > 2 (multi-channel)
} else {
// stereo balance
if (volumeItf->mStereoPosition > 0) {
amplFromStereoPos[0] = (1000-volumeItf->mStereoPosition)/1000.0f;
amplFromStereoPos[1] = 1.0f;
} else {
amplFromStereoPos[0] = 1.0f;
amplFromStereoPos[1] = (1000+volumeItf->mStereoPosition)/1000.0f;
}
}
leftVol *= amplFromStereoPos[0];
rightVol *= amplFromStereoPos[1];
}
// apply audibility factors
if (!leftAudibilityFactor) {
leftVol = 0.0;
}
if (!rightAudibilityFactor) {
rightVol = 0.0;
}
// return the computed volumes
pVolumes[0] = leftVol;
pVolumes[1] = rightVol;
}
//-----------------------------------------------------------------------------
void audioTrack_handleMarker_lockPlay(CAudioPlayer* ap) {
//SL_LOGV("received event EVENT_MARKER from AudioTrack");
slPlayCallback callback = NULL;
void* callbackPContext = NULL;
interface_lock_shared(&ap->mPlay);
callback = ap->mPlay.mCallback;
callbackPContext = ap->mPlay.mContext;
interface_unlock_shared(&ap->mPlay);
if (NULL != callback) {
// getting this event implies SL_PLAYEVENT_HEADATMARKER was set in the event mask
(*callback)(&ap->mPlay.mItf, callbackPContext, SL_PLAYEVENT_HEADATMARKER);
}
}
//-----------------------------------------------------------------------------
void audioTrack_handleNewPos_lockPlay(CAudioPlayer* ap) {
//SL_LOGV("received event EVENT_NEW_POS from AudioTrack");
slPlayCallback callback = NULL;
void* callbackPContext = NULL;
interface_lock_shared(&ap->mPlay);
callback = ap->mPlay.mCallback;
callbackPContext = ap->mPlay.mContext;
interface_unlock_shared(&ap->mPlay);
if (NULL != callback) {
// getting this event implies SL_PLAYEVENT_HEADATNEWPOS was set in the event mask
(*callback)(&ap->mPlay.mItf, callbackPContext, SL_PLAYEVENT_HEADATNEWPOS);
}
}
//-----------------------------------------------------------------------------
void audioTrack_handleUnderrun_lockPlay(CAudioPlayer* ap) {
slPlayCallback callback = NULL;
void* callbackPContext = NULL;
interface_lock_shared(&ap->mPlay);
callback = ap->mPlay.mCallback;
callbackPContext = ap->mPlay.mContext;
bool headStalled = (ap->mPlay.mEventFlags & SL_PLAYEVENT_HEADSTALLED) != 0;
interface_unlock_shared(&ap->mPlay);
if ((NULL != callback) && headStalled) {
(*callback)(&ap->mPlay.mItf, callbackPContext, SL_PLAYEVENT_HEADSTALLED);
}
}
//-----------------------------------------------------------------------------
/**
* post-condition: play state of AudioPlayer is SL_PLAYSTATE_PAUSED if setPlayStateToPaused is true
*
* note: a conditional flag, setPlayStateToPaused, is used here to specify whether the play state
* needs to be changed when the player reaches the end of the content to play. This is
* relative to what the specification describes for buffer queues vs the
* SL_PLAYEVENT_HEADATEND event. In the OpenSL ES specification 1.0.1:
* - section 8.12 SLBufferQueueItf states "In the case of starvation due to insufficient
* buffers in the queue, the playing of audio data stops. The player remains in the
* SL_PLAYSTATE_PLAYING state."
* - section 9.2.31 SL_PLAYEVENT states "SL_PLAYEVENT_HEADATEND Playback head is at the end
* of the current content and the player has paused."
*/
void audioPlayer_dispatch_headAtEnd_lockPlay(CAudioPlayer *ap, bool setPlayStateToPaused,
bool needToLock) {
//SL_LOGV("ap=%p, setPlayStateToPaused=%d, needToLock=%d", ap, setPlayStateToPaused,
// needToLock);
slPlayCallback playCallback = NULL;
void * playContext = NULL;
// SLPlayItf callback or no callback?
if (needToLock) {
interface_lock_exclusive(&ap->mPlay);
}
if (ap->mPlay.mEventFlags & SL_PLAYEVENT_HEADATEND) {
playCallback = ap->mPlay.mCallback;
playContext = ap->mPlay.mContext;
}
if (setPlayStateToPaused) {
ap->mPlay.mState = SL_PLAYSTATE_PAUSED;
}
if (needToLock) {
interface_unlock_exclusive(&ap->mPlay);
}
// enqueue callback with no lock held
if (NULL != playCallback) {
#ifndef USE_ASYNCHRONOUS_PLAY_CALLBACK
(*playCallback)(&ap->mPlay.mItf, playContext, SL_PLAYEVENT_HEADATEND);
#else
SLresult result = EnqueueAsyncCallback_ppi(ap, playCallback, &ap->mPlay.mItf, playContext,
SL_PLAYEVENT_HEADATEND);
if (SL_RESULT_SUCCESS != result) {
ALOGW("Callback %p(%p, %p, SL_PLAYEVENT_HEADATEND) dropped", playCallback,
&ap->mPlay.mItf, playContext);
}
#endif
}
}
//-----------------------------------------------------------------------------
SLresult audioPlayer_setStreamType(CAudioPlayer* ap, SLint32 type) {
SLresult result = SL_RESULT_SUCCESS;
SL_LOGV("type %d", type);
int newStreamType = ANDROID_DEFAULT_OUTPUT_STREAM_TYPE;
switch(type) {
case SL_ANDROID_STREAM_VOICE:
newStreamType = AUDIO_STREAM_VOICE_CALL;
break;
case SL_ANDROID_STREAM_SYSTEM:
newStreamType = AUDIO_STREAM_SYSTEM;
break;
case SL_ANDROID_STREAM_RING:
newStreamType = AUDIO_STREAM_RING;
break;
case SL_ANDROID_STREAM_MEDIA:
newStreamType = AUDIO_STREAM_MUSIC;
break;
case SL_ANDROID_STREAM_ALARM:
newStreamType = AUDIO_STREAM_ALARM;
break;
case SL_ANDROID_STREAM_NOTIFICATION:
newStreamType = AUDIO_STREAM_NOTIFICATION;
break;
default:
SL_LOGE(ERROR_PLAYERSTREAMTYPE_SET_UNKNOWN_TYPE);
result = SL_RESULT_PARAMETER_INVALID;
break;
}
// stream type needs to be set before the object is realized
// (ap->mAudioTrack is supposed to be NULL until then)
if (SL_OBJECT_STATE_UNREALIZED != ap->mObject.mState) {
SL_LOGE(ERROR_PLAYERSTREAMTYPE_REALIZED);
result = SL_RESULT_PRECONDITIONS_VIOLATED;
} else {
ap->mStreamType = newStreamType;
}
return result;
}
//-----------------------------------------------------------------------------
SLresult audioPlayer_getStreamType(CAudioPlayer* ap, SLint32 *pType) {
SLresult result = SL_RESULT_SUCCESS;
switch(ap->mStreamType) {
case AUDIO_STREAM_VOICE_CALL:
*pType = SL_ANDROID_STREAM_VOICE;
break;
case AUDIO_STREAM_SYSTEM:
*pType = SL_ANDROID_STREAM_SYSTEM;
break;
case AUDIO_STREAM_RING:
*pType = SL_ANDROID_STREAM_RING;
break;
case AUDIO_STREAM_DEFAULT:
case AUDIO_STREAM_MUSIC:
*pType = SL_ANDROID_STREAM_MEDIA;
break;
case AUDIO_STREAM_ALARM:
*pType = SL_ANDROID_STREAM_ALARM;
break;
case AUDIO_STREAM_NOTIFICATION:
*pType = SL_ANDROID_STREAM_NOTIFICATION;
break;
default:
result = SL_RESULT_INTERNAL_ERROR;
*pType = SL_ANDROID_STREAM_MEDIA;
break;
}
return result;
}
//-----------------------------------------------------------------------------
void audioPlayer_auxEffectUpdate(CAudioPlayer* ap) {
if ((ap->mAudioTrack != 0) && (ap->mAuxEffect != 0)) {
android_fxSend_attach(ap, true, ap->mAuxEffect, ap->mVolume.mLevel + ap->mAuxSendLevel);
}
}
//-----------------------------------------------------------------------------
/*
* returns true if the given data sink is supported by AudioPlayer that doesn't
* play to an OutputMix object, false otherwise
*
* pre-condition: the locator of the audio sink is not SL_DATALOCATOR_OUTPUTMIX
*/
bool audioPlayer_isSupportedNonOutputMixSink(const SLDataSink* pAudioSink) {
bool result = true;
const SLuint32 sinkLocatorType = *(SLuint32 *)pAudioSink->pLocator;
const SLuint32 sinkFormatType = *(SLuint32 *)pAudioSink->pFormat;
switch (sinkLocatorType) {
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
if (SL_DATAFORMAT_PCM != sinkFormatType) {
SL_LOGE("Unsupported sink format 0x%x, expected SL_DATAFORMAT_PCM",
(unsigned)sinkFormatType);
result = false;
}
// it's no use checking the PCM format fields because additional characteristics
// such as the number of channels, or sample size are unknown to the player at this stage
break;
default:
SL_LOGE("Unsupported sink locator type 0x%x", (unsigned)sinkLocatorType);
result = false;
break;
}
return result;
}
//-----------------------------------------------------------------------------
/*
* returns the Android object type if the locator type combinations for the source and sinks
* are supported by this implementation, INVALID_TYPE otherwise
*/
AndroidObjectType audioPlayer_getAndroidObjectTypeForSourceSink(CAudioPlayer *ap) {
const SLDataSource *pAudioSrc = &ap->mDataSource.u.mSource;
const SLDataSink *pAudioSnk = &ap->mDataSink.u.mSink;
const SLuint32 sourceLocatorType = *(SLuint32 *)pAudioSrc->pLocator;
const SLuint32 sinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator;
AndroidObjectType type = INVALID_TYPE;
//--------------------------------------
// Sink / source matching check:
// the following source / sink combinations are supported
// SL_DATALOCATOR_BUFFERQUEUE / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_URI / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_ANDROIDFD / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_ANDROIDBUFFERQUEUE / SL_DATALOCATOR_OUTPUTMIX
// SL_DATALOCATOR_ANDROIDBUFFERQUEUE / SL_DATALOCATOR_BUFFERQUEUE
// SL_DATALOCATOR_URI / SL_DATALOCATOR_BUFFERQUEUE
// SL_DATALOCATOR_ANDROIDFD / SL_DATALOCATOR_BUFFERQUEUE
// SL_DATALOCATOR_URI / SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
// SL_DATALOCATOR_ANDROIDFD / SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
switch (sinkLocatorType) {
case SL_DATALOCATOR_OUTPUTMIX: {
switch (sourceLocatorType) {
// Buffer Queue to AudioTrack
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
type = AUDIOPLAYER_FROM_PCM_BUFFERQUEUE;
break;
// URI or FD to MediaPlayer
case SL_DATALOCATOR_URI:
case SL_DATALOCATOR_ANDROIDFD:
type = AUDIOPLAYER_FROM_URIFD;
break;
// Android BufferQueue to MediaPlayer (shared memory streaming)
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
type = AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE;
break;
default:
SL_LOGE("Source data locator 0x%x not supported with SL_DATALOCATOR_OUTPUTMIX sink",
(unsigned)sourceLocatorType);
break;
}
}
break;
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
switch (sourceLocatorType) {
// URI or FD decoded to PCM in a buffer queue
case SL_DATALOCATOR_URI:
case SL_DATALOCATOR_ANDROIDFD:
type = AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE;
break;
// AAC ADTS Android buffer queue decoded to PCM in a buffer queue
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
type = AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE;
break;
default:
SL_LOGE("Source data locator 0x%x not supported with SL_DATALOCATOR_BUFFERQUEUE sink",
(unsigned)sourceLocatorType);
break;
}
break;
default:
SL_LOGE("Sink data locator 0x%x not supported", (unsigned)sinkLocatorType);
break;
}
return type;
}
//-----------------------------------------------------------------------------
/*
* Callback associated with an SfPlayer of an SL ES AudioPlayer that gets its data
* from a URI or FD, for prepare, prefetch, and play events
*/
static void sfplayer_handlePrefetchEvent(int event, int data1, int data2, void* user) {
// FIXME see similar code and comment in player_handleMediaPlayerEventNotifications
if (NULL == user) {
return;
}
CAudioPlayer *ap = (CAudioPlayer *)user;
if (!android::CallbackProtector::enterCbIfOk(ap->mCallbackProtector)) {
// it is not safe to enter the callback (the track is about to go away)
return;
}
union {
char c[sizeof(int)];
int i;
} u;
u.i = event;
SL_LOGV("sfplayer_handlePrefetchEvent(event='%c%c%c%c' (%d), data1=%d, data2=%d, user=%p) from "
"SfAudioPlayer", u.c[3], u.c[2], u.c[1], u.c[0], event, data1, data2, user);
switch(event) {
case android::GenericPlayer::kEventPrepared: {
SL_LOGV("Received GenericPlayer::kEventPrepared for CAudioPlayer %p", ap);
// assume no callback
slPrefetchCallback callback = NULL;
void* callbackPContext;
SLuint32 events;
object_lock_exclusive(&ap->mObject);
// mark object as prepared; same state is used for successful or unsuccessful prepare
assert(ap->mAndroidObjState == ANDROID_PREPARING);
ap->mAndroidObjState = ANDROID_READY;
if (PLAYER_SUCCESS == data1) {
// Most of successful prepare completion for ap->mAPlayer
// is handled by GenericPlayer and its subclasses.
} else {
// SfPlayer prepare() failed prefetching, there is no event in SLPrefetchStatus to
// indicate a prefetch error, so we signal it by sending simultaneously two events:
// - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0
// - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW
SL_LOGE(ERROR_PLAYER_PREFETCH_d, data1);
if (IsInterfaceInitialized(&(ap->mObject), MPH_PREFETCHSTATUS)) {
ap->mPrefetchStatus.mLevel = 0;
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
if (!(~ap->mPrefetchStatus.mCallbackEventsMask &
(SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
callback = ap->mPrefetchStatus.mCallback;
callbackPContext = ap->mPrefetchStatus.mContext;
events = SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE;
}
}
}
object_unlock_exclusive(&ap->mObject);
// callback with no lock held
if (NULL != callback) {
(*callback)(&ap->mPrefetchStatus.mItf, callbackPContext, events);
}
}
break;
case android::GenericPlayer::kEventPrefetchFillLevelUpdate : {
if (!IsInterfaceInitialized(&(ap->mObject), MPH_PREFETCHSTATUS)) {
break;
}
slPrefetchCallback callback = NULL;
void* callbackPContext = NULL;
// SLPrefetchStatusItf callback or no callback?
interface_lock_exclusive(&ap->mPrefetchStatus);
if (ap->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
callback = ap->mPrefetchStatus.mCallback;
callbackPContext = ap->mPrefetchStatus.mContext;
}
ap->mPrefetchStatus.mLevel = (SLpermille)data1;
interface_unlock_exclusive(&ap->mPrefetchStatus);
// callback with no lock held
if (NULL != callback) {
(*callback)(&ap->mPrefetchStatus.mItf, callbackPContext,
SL_PREFETCHEVENT_FILLLEVELCHANGE);
}
}
break;
case android::GenericPlayer::kEventPrefetchStatusChange: {
if (!IsInterfaceInitialized(&(ap->mObject), MPH_PREFETCHSTATUS)) {
break;
}
slPrefetchCallback callback = NULL;
void* callbackPContext = NULL;
// SLPrefetchStatusItf callback or no callback?
object_lock_exclusive(&ap->mObject);
if (ap->mPrefetchStatus.mCallbackEventsMask & SL_PREFETCHEVENT_STATUSCHANGE) {
callback = ap->mPrefetchStatus.mCallback;
callbackPContext = ap->mPrefetchStatus.mContext;
}
if (data1 >= android::kStatusIntermediate) {
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_SUFFICIENTDATA;
} else if (data1 < android::kStatusIntermediate) {
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
}
object_unlock_exclusive(&ap->mObject);
// callback with no lock held
if (NULL != callback) {
(*callback)(&ap->mPrefetchStatus.mItf, callbackPContext, SL_PREFETCHEVENT_STATUSCHANGE);
}
}
break;
case android::GenericPlayer::kEventEndOfStream: {
audioPlayer_dispatch_headAtEnd_lockPlay(ap, true /*set state to paused?*/, true);
if ((ap->mAudioTrack != 0) && (!ap->mSeek.mLoopEnabled)) {
ap->mAudioTrack->stop();
}
}
break;
case android::GenericPlayer::kEventChannelCount: {
object_lock_exclusive(&ap->mObject);
if (UNKNOWN_NUMCHANNELS == ap->mNumChannels && UNKNOWN_NUMCHANNELS != data1) {
ap->mNumChannels = data1;
android_audioPlayer_volumeUpdate(ap);
}
object_unlock_exclusive(&ap->mObject);
}
break;
case android::GenericPlayer::kEventPlay: {
slPlayCallback callback = NULL;
void* callbackPContext = NULL;
interface_lock_shared(&ap->mPlay);
callback = ap->mPlay.mCallback;
callbackPContext = ap->mPlay.mContext;
interface_unlock_shared(&ap->mPlay);
if (NULL != callback) {
SLuint32 event = (SLuint32) data1; // SL_PLAYEVENT_HEAD*
#ifndef USE_ASYNCHRONOUS_PLAY_CALLBACK
// synchronous callback requires a synchronous GetPosition implementation
(*callback)(&ap->mPlay.mItf, callbackPContext, event);
#else
// asynchronous callback works with any GetPosition implementation
SLresult result = EnqueueAsyncCallback_ppi(ap, callback, &ap->mPlay.mItf,
callbackPContext, event);
if (SL_RESULT_SUCCESS != result) {
ALOGW("Callback %p(%p, %p, 0x%x) dropped", callback,
&ap->mPlay.mItf, callbackPContext, event);
}
#endif
}
}
break;
case android::GenericPlayer::kEventErrorAfterPrepare: {
SL_LOGV("kEventErrorAfterPrepare");
// assume no callback
slPrefetchCallback callback = NULL;
void* callbackPContext = NULL;
object_lock_exclusive(&ap->mObject);
if (IsInterfaceInitialized(&ap->mObject, MPH_PREFETCHSTATUS)) {
ap->mPrefetchStatus.mLevel = 0;
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
if (!(~ap->mPrefetchStatus.mCallbackEventsMask &
(SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
callback = ap->mPrefetchStatus.mCallback;
callbackPContext = ap->mPrefetchStatus.mContext;
}
}
object_unlock_exclusive(&ap->mObject);
// FIXME there's interesting information in data1, but no API to convey it to client
SL_LOGE("Error after prepare: %d", data1);
// callback with no lock held
if (NULL != callback) {
(*callback)(&ap->mPrefetchStatus.mItf, callbackPContext,
SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
}
}
break;
case android::GenericPlayer::kEventHasVideoSize:
//SL_LOGW("Unexpected kEventHasVideoSize");
break;
default:
break;
}
ap->mCallbackProtector->exitCb();
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_checkSourceSink(CAudioPlayer *pAudioPlayer)
{
// verify that the locator types for the source / sink combination is supported
pAudioPlayer->mAndroidObjType = audioPlayer_getAndroidObjectTypeForSourceSink(pAudioPlayer);
if (INVALID_TYPE == pAudioPlayer->mAndroidObjType) {
return SL_RESULT_PARAMETER_INVALID;
}
const SLDataSource *pAudioSrc = &pAudioPlayer->mDataSource.u.mSource;
const SLDataSink *pAudioSnk = &pAudioPlayer->mDataSink.u.mSink;
// format check:
const SLuint32 sourceLocatorType = *(SLuint32 *)pAudioSrc->pLocator;
const SLuint32 sinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator;
const SLuint32 sourceFormatType = *(SLuint32 *)pAudioSrc->pFormat;
const SLuint32 sinkFormatType = *(SLuint32 *)pAudioSnk->pFormat;
switch (sourceLocatorType) {
//------------------
// Buffer Queues
case SL_DATALOCATOR_BUFFERQUEUE:
case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
{
SLDataLocator_BufferQueue *dl_bq = (SLDataLocator_BufferQueue *) pAudioSrc->pLocator;
// Buffer format
switch (sourceFormatType) {
// currently only PCM buffer queues are supported,
case SL_DATAFORMAT_PCM: {
SLDataFormat_PCM *df_pcm = (SLDataFormat_PCM *) pAudioSrc->pFormat;
switch (df_pcm->numChannels) {
case 1:
case 2:
break;
default:
// this should have already been rejected by checkDataFormat
SL_LOGE("Cannot create audio player: unsupported " \
"PCM data source with %u channels", (unsigned) df_pcm->numChannels);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
switch (df_pcm->samplesPerSec) {
case SL_SAMPLINGRATE_8:
case SL_SAMPLINGRATE_11_025:
case SL_SAMPLINGRATE_12:
case SL_SAMPLINGRATE_16:
case SL_SAMPLINGRATE_22_05:
case SL_SAMPLINGRATE_24:
case SL_SAMPLINGRATE_32:
case SL_SAMPLINGRATE_44_1:
case SL_SAMPLINGRATE_48:
break;
case SL_SAMPLINGRATE_64:
case SL_SAMPLINGRATE_88_2:
case SL_SAMPLINGRATE_96:
case SL_SAMPLINGRATE_192:
default:
SL_LOGE("Cannot create audio player: unsupported sample rate %u milliHz",
(unsigned) df_pcm->samplesPerSec);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
switch (df_pcm->bitsPerSample) {
case SL_PCMSAMPLEFORMAT_FIXED_8:
case SL_PCMSAMPLEFORMAT_FIXED_16:
break;
// others
default:
// this should have already been rejected by checkDataFormat
SL_LOGE("Cannot create audio player: unsupported sample bit depth %u",
(SLuint32)df_pcm->bitsPerSample);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
switch (df_pcm->containerSize) {
case 8:
case 16:
break;
// others
default:
SL_LOGE("Cannot create audio player: unsupported container size %u",
(unsigned) df_pcm->containerSize);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
// df_pcm->channelMask: the earlier platform-independent check and the
// upcoming check by sles_to_android_channelMaskOut are sufficient
switch (df_pcm->endianness) {
case SL_BYTEORDER_LITTLEENDIAN:
break;
case SL_BYTEORDER_BIGENDIAN:
SL_LOGE("Cannot create audio player: unsupported big-endian byte order");
return SL_RESULT_CONTENT_UNSUPPORTED;
// native is proposed but not yet in spec
default:
SL_LOGE("Cannot create audio player: unsupported byte order %u",
(unsigned) df_pcm->endianness);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
} //case SL_DATAFORMAT_PCM
break;
case SL_DATAFORMAT_MIME:
case XA_DATAFORMAT_RAWIMAGE:
SL_LOGE("Cannot create audio player with buffer queue data source "
"without SL_DATAFORMAT_PCM format");
return SL_RESULT_CONTENT_UNSUPPORTED;
default:
// invalid data format is detected earlier
assert(false);
return SL_RESULT_INTERNAL_ERROR;
} // switch (sourceFormatType)
} // case SL_DATALOCATOR_BUFFERQUEUE or SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
break;
//------------------
// URI
case SL_DATALOCATOR_URI:
{
SLDataLocator_URI *dl_uri = (SLDataLocator_URI *) pAudioSrc->pLocator;
if (NULL == dl_uri->URI) {
return SL_RESULT_PARAMETER_INVALID;
}
// URI format
switch (sourceFormatType) {
case SL_DATAFORMAT_MIME:
break;
case SL_DATAFORMAT_PCM:
case XA_DATAFORMAT_RAWIMAGE:
SL_LOGE("Cannot create audio player with SL_DATALOCATOR_URI data source without "
"SL_DATAFORMAT_MIME format");
return SL_RESULT_CONTENT_UNSUPPORTED;
} // switch (sourceFormatType)
// decoding format check
if ((sinkLocatorType != SL_DATALOCATOR_OUTPUTMIX) &&
!audioPlayer_isSupportedNonOutputMixSink(pAudioSnk)) {
return SL_RESULT_CONTENT_UNSUPPORTED;
}
} // case SL_DATALOCATOR_URI
break;
//------------------
// File Descriptor
case SL_DATALOCATOR_ANDROIDFD:
{
// fd is already non null
switch (sourceFormatType) {
case SL_DATAFORMAT_MIME:
break;
case SL_DATAFORMAT_PCM:
// FIXME implement
SL_LOGD("[ FIXME implement PCM FD data sources ]");
break;
case XA_DATAFORMAT_RAWIMAGE:
SL_LOGE("Cannot create audio player with SL_DATALOCATOR_ANDROIDFD data source "
"without SL_DATAFORMAT_MIME or SL_DATAFORMAT_PCM format");
return SL_RESULT_CONTENT_UNSUPPORTED;
default:
// invalid data format is detected earlier
assert(false);
return SL_RESULT_INTERNAL_ERROR;
} // switch (sourceFormatType)
if ((sinkLocatorType != SL_DATALOCATOR_OUTPUTMIX) &&
!audioPlayer_isSupportedNonOutputMixSink(pAudioSnk)) {
return SL_RESULT_CONTENT_UNSUPPORTED;
}
} // case SL_DATALOCATOR_ANDROIDFD
break;
//------------------
// Stream
case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
{
switch (sourceFormatType) {
case SL_DATAFORMAT_MIME:
{
SLDataFormat_MIME *df_mime = (SLDataFormat_MIME *) pAudioSrc->pFormat;
if (NULL == df_mime) {
SL_LOGE("MIME type null invalid");
return SL_RESULT_CONTENT_UNSUPPORTED;
}
SL_LOGD("source MIME is %s", (char*)df_mime->mimeType);
switch(df_mime->containerType) {
case SL_CONTAINERTYPE_MPEG_TS:
if (strcasecmp((char*)df_mime->mimeType, (const char *)XA_ANDROID_MIME_MP2TS)) {
SL_LOGE("Invalid MIME (%s) for container SL_CONTAINERTYPE_MPEG_TS, expects %s",
(char*)df_mime->mimeType, XA_ANDROID_MIME_MP2TS);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
break;
case SL_CONTAINERTYPE_RAW:
case SL_CONTAINERTYPE_AAC:
if (strcasecmp((char*)df_mime->mimeType, (const char *)SL_ANDROID_MIME_AACADTS) &&
strcasecmp((char*)df_mime->mimeType,
ANDROID_MIME_AACADTS_ANDROID_FRAMEWORK)) {
SL_LOGE("Invalid MIME (%s) for container type %d, expects %s",
(char*)df_mime->mimeType, df_mime->containerType,
SL_ANDROID_MIME_AACADTS);
return SL_RESULT_CONTENT_UNSUPPORTED;
}
break;
default:
SL_LOGE("Cannot create player with SL_DATALOCATOR_ANDROIDBUFFERQUEUE data source "
"that is not fed MPEG-2 TS data or AAC ADTS data");
return SL_RESULT_CONTENT_UNSUPPORTED;
}
}
break;
default:
SL_LOGE("Cannot create player with SL_DATALOCATOR_ANDROIDBUFFERQUEUE data source "
"without SL_DATAFORMAT_MIME format");
return SL_RESULT_CONTENT_UNSUPPORTED;
}
}
break; // case SL_DATALOCATOR_ANDROIDBUFFERQUEUE
//------------------
// Address
case SL_DATALOCATOR_ADDRESS:
case SL_DATALOCATOR_IODEVICE:
case SL_DATALOCATOR_OUTPUTMIX:
case XA_DATALOCATOR_NATIVEDISPLAY:
case SL_DATALOCATOR_MIDIBUFFERQUEUE:
SL_LOGE("Cannot create audio player with data locator type 0x%x",
(unsigned) sourceLocatorType);
return SL_RESULT_CONTENT_UNSUPPORTED;
default:
SL_LOGE("Cannot create audio player with invalid data locator type 0x%x",
(unsigned) sourceLocatorType);
return SL_RESULT_PARAMETER_INVALID;
}// switch (locatorType)
return SL_RESULT_SUCCESS;
}
//-----------------------------------------------------------------------------
// Callback associated with an AudioTrack of an SL ES AudioPlayer that gets its data
// from a buffer queue. This will not be called once the AudioTrack has been destroyed.
static void audioTrack_callBack_pullFromBuffQueue(int event, void* user, void *info) {
CAudioPlayer *ap = (CAudioPlayer *)user;
if (!android::CallbackProtector::enterCbIfOk(ap->mCallbackProtector)) {
// it is not safe to enter the callback (the track is about to go away)
return;
}
void * callbackPContext = NULL;
switch(event) {
case android::AudioTrack::EVENT_MORE_DATA: {
//SL_LOGV("received event EVENT_MORE_DATA from AudioTrack TID=%d", gettid());
slBufferQueueCallback callback = NULL;
slPrefetchCallback prefetchCallback = NULL;
void *prefetchContext = NULL;
SLuint32 prefetchEvents = SL_PREFETCHEVENT_NONE;
android::AudioTrack::Buffer* pBuff = (android::AudioTrack::Buffer*)info;
// retrieve data from the buffer queue
interface_lock_exclusive(&ap->mBufferQueue);
if (ap->mBufferQueue.mState.count != 0) {
//SL_LOGV("nbBuffers in queue = %u",ap->mBufferQueue.mState.count);
assert(ap->mBufferQueue.mFront != ap->mBufferQueue.mRear);
BufferHeader *oldFront = ap->mBufferQueue.mFront;
BufferHeader *newFront = &oldFront[1];
// declared as void * because this code supports both 8-bit and 16-bit PCM data
void *pSrc = (char *)oldFront->mBuffer + ap->mBufferQueue.mSizeConsumed;
if (ap->mBufferQueue.mSizeConsumed + pBuff->size < oldFront->mSize) {
// can't consume the whole or rest of the buffer in one shot
ap->mBufferQueue.mSizeConsumed += pBuff->size;
// leave pBuff->size untouched
// consume data
// FIXME can we avoid holding the lock during the copy?
memcpy (pBuff->raw, pSrc, pBuff->size);
} else {
// finish consuming the buffer or consume the buffer in one shot
pBuff->size = oldFront->mSize - ap->mBufferQueue.mSizeConsumed;
ap->mBufferQueue.mSizeConsumed = 0;
if (newFront ==
&ap->mBufferQueue.mArray
[ap->mBufferQueue.mNumBuffers + 1])
{
newFront = ap->mBufferQueue.mArray;
}
ap->mBufferQueue.mFront = newFront;
ap->mBufferQueue.mState.count--;
ap->mBufferQueue.mState.playIndex++;
// consume data
// FIXME can we avoid holding the lock during the copy?
memcpy (pBuff->raw, pSrc, pBuff->size);
// data has been consumed, and the buffer queue state has been updated
// we will notify the client if applicable
callback = ap->mBufferQueue.mCallback;
// save callback data
callbackPContext = ap->mBufferQueue.mContext;
}
} else { // empty queue
// signal no data available
pBuff->size = 0;
// signal we're at the end of the content, but don't pause (see note in function)
audioPlayer_dispatch_headAtEnd_lockPlay(ap, false /*set state to paused?*/, false);
// signal underflow to prefetch status itf
if (IsInterfaceInitialized(&(ap->mObject), MPH_PREFETCHSTATUS)) {
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
ap->mPrefetchStatus.mLevel = 0;
// callback or no callback?
prefetchEvents = ap->mPrefetchStatus.mCallbackEventsMask &
(SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE);
if (SL_PREFETCHEVENT_NONE != prefetchEvents) {
prefetchCallback = ap->mPrefetchStatus.mCallback;
prefetchContext = ap->mPrefetchStatus.mContext;
}
}
// stop the track so it restarts playing faster when new data is enqueued
ap->mAudioTrack->stop();
}
interface_unlock_exclusive(&ap->mBufferQueue);
// notify client
if (NULL != prefetchCallback) {
assert(SL_PREFETCHEVENT_NONE != prefetchEvents);
// spec requires separate callbacks for each event
if (prefetchEvents & SL_PREFETCHEVENT_STATUSCHANGE) {
(*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
SL_PREFETCHEVENT_STATUSCHANGE);
}
if (prefetchEvents & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
(*prefetchCallback)(&ap->mPrefetchStatus.mItf, prefetchContext,
SL_PREFETCHEVENT_FILLLEVELCHANGE);
}
}
if (NULL != callback) {
(*callback)(&ap->mBufferQueue.mItf, callbackPContext);
}
}
break;
case android::AudioTrack::EVENT_MARKER:
//SL_LOGI("received event EVENT_MARKER from AudioTrack");
audioTrack_handleMarker_lockPlay(ap);
break;
case android::AudioTrack::EVENT_NEW_POS:
//SL_LOGI("received event EVENT_NEW_POS from AudioTrack");
audioTrack_handleNewPos_lockPlay(ap);
break;
case android::AudioTrack::EVENT_UNDERRUN:
//SL_LOGI("received event EVENT_UNDERRUN from AudioTrack");
audioTrack_handleUnderrun_lockPlay(ap);
break;
case android::AudioTrack::EVENT_BUFFER_END:
case android::AudioTrack::EVENT_LOOP_END:
// These are unexpected so fall through
default:
// FIXME where does the notification of SL_PLAYEVENT_HEADMOVING fit?
SL_LOGE("Encountered unknown AudioTrack event %d for CAudioPlayer %p", event,
(CAudioPlayer *)user);
break;
}
ap->mCallbackProtector->exitCb();
}
//-----------------------------------------------------------------------------
void android_audioPlayer_create(CAudioPlayer *pAudioPlayer) {
// pAudioPlayer->mAndroidObjType has been set in android_audioPlayer_checkSourceSink()
// and if it was == INVALID_TYPE, then IEngine_CreateAudioPlayer would never call us
assert(INVALID_TYPE != pAudioPlayer->mAndroidObjType);
// These initializations are in the same order as the field declarations in classes.h
// FIXME Consolidate initializations (many of these already in IEngine_CreateAudioPlayer)
// mAndroidObjType: see above comment
pAudioPlayer->mAndroidObjState = ANDROID_UNINITIALIZED;
pAudioPlayer->mSessionId = android::AudioSystem::newAudioSessionId();
// placeholder: not necessary yet as session ID lifetime doesn't extend beyond player
// android::AudioSystem::acquireAudioSessionId(pAudioPlayer->mSessionId);
pAudioPlayer->mStreamType = ANDROID_DEFAULT_OUTPUT_STREAM_TYPE;
// mAudioTrack
pAudioPlayer->mCallbackProtector = new android::CallbackProtector();
// mAPLayer
// mAuxEffect
pAudioPlayer->mAuxSendLevel = 0;
pAudioPlayer->mAmplFromDirectLevel = 1.0f; // matches initial mDirectLevel value
pAudioPlayer->mDeferredStart = false;
// This section re-initializes interface-specific fields that
// can be set or used regardless of whether the interface is
// exposed on the AudioPlayer or not
// Only AudioTrack supports a non-trivial playback rate
switch (pAudioPlayer->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
pAudioPlayer->mPlaybackRate.mMinRate = AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE;
pAudioPlayer->mPlaybackRate.mMaxRate = AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE;
break;
default:
// use the default range
break;
}
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_setConfig(CAudioPlayer *ap, const SLchar *configKey,
const void *pConfigValue, SLuint32 valueSize) {
SLresult result;
assert(NULL != ap && NULL != configKey && NULL != pConfigValue);
if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_STREAM_TYPE) == 0) {
// stream type
if (KEY_STREAM_TYPE_PARAMSIZE > valueSize) {
SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW);
result = SL_RESULT_BUFFER_INSUFFICIENT;
} else {
result = audioPlayer_setStreamType(ap, *(SLuint32*)pConfigValue);
}
} else {
SL_LOGE(ERROR_CONFIG_UNKNOWN_KEY);
result = SL_RESULT_PARAMETER_INVALID;
}
return result;
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_getConfig(CAudioPlayer* ap, const SLchar *configKey,
SLuint32* pValueSize, void *pConfigValue) {
SLresult result;
assert(NULL != ap && NULL != configKey && NULL != pValueSize);
if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_STREAM_TYPE) == 0) {
// stream type
if (NULL == pConfigValue) {
result = SL_RESULT_SUCCESS;
} else if (KEY_STREAM_TYPE_PARAMSIZE > *pValueSize) {
SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW);
result = SL_RESULT_BUFFER_INSUFFICIENT;
} else {
result = audioPlayer_getStreamType(ap, (SLint32*)pConfigValue);
}
*pValueSize = KEY_STREAM_TYPE_PARAMSIZE;
} else {
SL_LOGE(ERROR_CONFIG_UNKNOWN_KEY);
result = SL_RESULT_PARAMETER_INVALID;
}
return result;
}
//-----------------------------------------------------------------------------
// FIXME abstract out the diff between CMediaPlayer and CAudioPlayer
SLresult android_audioPlayer_realize(CAudioPlayer *pAudioPlayer, SLboolean async) {
SLresult result = SL_RESULT_SUCCESS;
SL_LOGV("Realize pAudioPlayer=%p", pAudioPlayer);
AudioPlayback_Parameters app;
app.sessionId = pAudioPlayer->mSessionId;
app.streamType = pAudioPlayer->mStreamType;
switch (pAudioPlayer->mAndroidObjType) {
//-----------------------------------
// AudioTrack
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
{
// initialize platform-specific CAudioPlayer fields
SLDataLocator_BufferQueue *dl_bq = (SLDataLocator_BufferQueue *)
pAudioPlayer->mDynamicSource.mDataSource;
SLDataFormat_PCM *df_pcm = (SLDataFormat_PCM *)
pAudioPlayer->mDynamicSource.mDataSource->pFormat;
uint32_t sampleRate = sles_to_android_sampleRate(df_pcm->samplesPerSec);
pAudioPlayer->mAudioTrack = new android::AudioTrack(
pAudioPlayer->mStreamType, // streamType
sampleRate, // sampleRate
sles_to_android_sampleFormat(df_pcm->bitsPerSample), // format
sles_to_android_channelMaskOut(df_pcm->numChannels, df_pcm->channelMask),
//channel mask
0, // frameCount (here min)
0, // flags
audioTrack_callBack_pullFromBuffQueue, // callback
(void *) pAudioPlayer, // user
0 // FIXME find appropriate frame count // notificationFrame
, pAudioPlayer->mSessionId
);
android::status_t status = pAudioPlayer->mAudioTrack->initCheck();
if (status != android::NO_ERROR) {
SL_LOGE("AudioTrack::initCheck status %u", status);
result = SL_RESULT_CONTENT_UNSUPPORTED;
pAudioPlayer->mAudioTrack.clear();
return result;
}
// initialize platform-independent CAudioPlayer fields
pAudioPlayer->mNumChannels = df_pcm->numChannels;
pAudioPlayer->mSampleRateMilliHz = df_pcm->samplesPerSec; // Note: bad field name in SL ES
// This use case does not have a separate "prepare" step
pAudioPlayer->mAndroidObjState = ANDROID_READY;
}
break;
//-----------------------------------
// MediaPlayer
case AUDIOPLAYER_FROM_URIFD: {
pAudioPlayer->mAPlayer = new android::LocAVPlayer(&app, false /*hasVideo*/);
pAudioPlayer->mAPlayer->init(sfplayer_handlePrefetchEvent,
(void*)pAudioPlayer /*notifUSer*/);
switch (pAudioPlayer->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_URI: {
// The legacy implementation ran Stagefright within the application process, and
// so allowed local pathnames specified by URI that were openable by
// the application but were not openable by mediaserver.
// The current implementation runs Stagefright (mostly) within mediaserver,
// which runs as a different UID and likely a different current working directory.
// For backwards compatibility with any applications which may have relied on the
// previous behavior, we convert an openable file URI into an FD.
// Note that unlike SL_DATALOCATOR_ANDROIDFD, this FD is owned by us
// and so we close it as soon as we've passed it (via Binder dup) to mediaserver.
const char *uri = (const char *)pAudioPlayer->mDataSource.mLocator.mURI.URI;
if (!isDistantProtocol(uri)) {
// don't touch the original uri, we may need it later
const char *pathname = uri;
// skip over an optional leading file:// prefix
if (!strncasecmp(pathname, "file://", 7)) {
pathname += 7;
}
// attempt to open it as a file using the application's credentials
int fd = ::open(pathname, O_RDONLY);
if (fd >= 0) {
// if open is successful, then check to see if it's a regular file
struct stat statbuf;
if (!::fstat(fd, &statbuf) && S_ISREG(statbuf.st_mode)) {
// treat similarly to an FD data locator, but
// let setDataSource take responsibility for closing fd
pAudioPlayer->mAPlayer->setDataSource(fd, 0, statbuf.st_size, true);
break;
}
// we were able to open it, but it's not a file, so let mediaserver try
(void) ::close(fd);
}
}
// if either the URI didn't look like a file, or open failed, or not a file
pAudioPlayer->mAPlayer->setDataSource(uri);
} break;
case SL_DATALOCATOR_ANDROIDFD: {
int64_t offset = (int64_t)pAudioPlayer->mDataSource.mLocator.mFD.offset;
pAudioPlayer->mAPlayer->setDataSource(
(int)pAudioPlayer->mDataSource.mLocator.mFD.fd,
offset == SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE ?
(int64_t)PLAYER_FD_FIND_FILE_SIZE : offset,
(int64_t)pAudioPlayer->mDataSource.mLocator.mFD.length);
}
break;
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNKNOWN_DATASOURCE_LOCATOR);
break;
}
}
break;
//-----------------------------------
// StreamPlayer
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: {
android::StreamPlayer* splr = new android::StreamPlayer(&app, false /*hasVideo*/,
&pAudioPlayer->mAndroidBufferQueue, pAudioPlayer->mCallbackProtector);
pAudioPlayer->mAPlayer = splr;
splr->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
}
break;
//-----------------------------------
// AudioToCbRenderer
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: {
android::AudioToCbRenderer* decoder = new android::AudioToCbRenderer(&app);
pAudioPlayer->mAPlayer = decoder;
// configures the callback for the sink buffer queue
decoder->setDataPushListener(adecoder_writeToBufferQueue, pAudioPlayer);
// configures the callback for the notifications coming from the SF code
decoder->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
switch (pAudioPlayer->mDataSource.mLocator.mLocatorType) {
case SL_DATALOCATOR_URI:
decoder->setDataSource(
(const char*)pAudioPlayer->mDataSource.mLocator.mURI.URI);
break;
case SL_DATALOCATOR_ANDROIDFD: {
int64_t offset = (int64_t)pAudioPlayer->mDataSource.mLocator.mFD.offset;
decoder->setDataSource(
(int)pAudioPlayer->mDataSource.mLocator.mFD.fd,
offset == SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE ?
(int64_t)PLAYER_FD_FIND_FILE_SIZE : offset,
(int64_t)pAudioPlayer->mDataSource.mLocator.mFD.length);
}
break;
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNKNOWN_DATASOURCE_LOCATOR);
break;
}
}
break;
//-----------------------------------
// AacBqToPcmCbRenderer
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app,
&pAudioPlayer->mAndroidBufferQueue);
// configures the callback for the sink buffer queue
bqtobq->setDataPushListener(adecoder_writeToBufferQueue, pAudioPlayer);
pAudioPlayer->mAPlayer = bqtobq;
// configures the callback for the notifications coming from the SF code,
// but also implicitly configures the AndroidBufferQueue from which ADTS data is read
pAudioPlayer->mAPlayer->init(sfplayer_handlePrefetchEvent, (void*)pAudioPlayer);
}
break;
//-----------------------------------
default:
SL_LOGE(ERROR_PLAYERREALIZE_UNEXPECTED_OBJECT_TYPE_D, pAudioPlayer->mAndroidObjType);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
// proceed with effect initialization
// initialize EQ
// FIXME use a table of effect descriptors when adding support for more effects
if (memcmp(SL_IID_EQUALIZER, &pAudioPlayer->mEqualizer.mEqDescriptor.type,
sizeof(effect_uuid_t)) == 0) {
SL_LOGV("Need to initialize EQ for AudioPlayer=%p", pAudioPlayer);
android_eq_init(pAudioPlayer->mSessionId, &pAudioPlayer->mEqualizer);
}
// initialize BassBoost
if (memcmp(SL_IID_BASSBOOST, &pAudioPlayer->mBassBoost.mBassBoostDescriptor.type,
sizeof(effect_uuid_t)) == 0) {
SL_LOGV("Need to initialize BassBoost for AudioPlayer=%p", pAudioPlayer);
android_bb_init(pAudioPlayer->mSessionId, &pAudioPlayer->mBassBoost);
}
// initialize Virtualizer
if (memcmp(SL_IID_VIRTUALIZER, &pAudioPlayer->mVirtualizer.mVirtualizerDescriptor.type,
sizeof(effect_uuid_t)) == 0) {
SL_LOGV("Need to initialize Virtualizer for AudioPlayer=%p", pAudioPlayer);
android_virt_init(pAudioPlayer->mSessionId, &pAudioPlayer->mVirtualizer);
}
// initialize EffectSend
// FIXME initialize EffectSend
return result;
}
//-----------------------------------------------------------------------------
/**
* Called with a lock on AudioPlayer, and blocks until safe to destroy
*/
SLresult android_audioPlayer_preDestroy(CAudioPlayer *pAudioPlayer) {
SL_LOGD("android_audioPlayer_preDestroy(%p)", pAudioPlayer);
SLresult result = SL_RESULT_SUCCESS;
bool disableCallbacksBeforePreDestroy;
switch (pAudioPlayer->mAndroidObjType) {
// Not yet clear why this order is important, but it reduces detected deadlocks
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
disableCallbacksBeforePreDestroy = true;
break;
// Use the old behavior for all other use cases until proven
// case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
default:
disableCallbacksBeforePreDestroy = false;
break;
}
if (disableCallbacksBeforePreDestroy) {
object_unlock_exclusive(&pAudioPlayer->mObject);
if (pAudioPlayer->mCallbackProtector != 0) {
pAudioPlayer->mCallbackProtector->requestCbExitAndWait();
}
object_lock_exclusive(&pAudioPlayer->mObject);
}
if (pAudioPlayer->mAPlayer != 0) {
pAudioPlayer->mAPlayer->preDestroy();
}
SL_LOGD("android_audioPlayer_preDestroy(%p) after mAPlayer->preDestroy()", pAudioPlayer);
if (!disableCallbacksBeforePreDestroy) {
object_unlock_exclusive(&pAudioPlayer->mObject);
if (pAudioPlayer->mCallbackProtector != 0) {
pAudioPlayer->mCallbackProtector->requestCbExitAndWait();
}
object_lock_exclusive(&pAudioPlayer->mObject);
}
return result;
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_destroy(CAudioPlayer *pAudioPlayer) {
SLresult result = SL_RESULT_SUCCESS;
SL_LOGV("android_audioPlayer_destroy(%p)", pAudioPlayer);
switch (pAudioPlayer->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
// We own the audio track for PCM buffer queue players
if (pAudioPlayer->mAudioTrack != 0) {
pAudioPlayer->mAudioTrack->stop();
// Note that there may still be another reference in post-unlock phase of SetPlayState
pAudioPlayer->mAudioTrack.clear();
}
break;
case AUDIOPLAYER_FROM_URIFD: // intended fall-through
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
pAudioPlayer->mAPlayer.clear();
break;
//-----------------------------------
default:
SL_LOGE(ERROR_PLAYERDESTROY_UNEXPECTED_OBJECT_TYPE_D, pAudioPlayer->mAndroidObjType);
result = SL_RESULT_INTERNAL_ERROR;
break;
}
// placeholder: not necessary yet as session ID lifetime doesn't extend beyond player
// android::AudioSystem::releaseAudioSessionId(pAudioPlayer->mSessionId);
pAudioPlayer->mCallbackProtector.clear();
// explicit destructor
pAudioPlayer->mAudioTrack.~sp();
// note that SetPlayState(PLAYING) may still hold a reference
pAudioPlayer->mCallbackProtector.~sp();
pAudioPlayer->mAuxEffect.~sp();
pAudioPlayer->mAPlayer.~sp();
return result;
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_setPlaybackRateAndConstraints(CAudioPlayer *ap, SLpermille rate,
SLuint32 constraints) {
SLresult result = SL_RESULT_SUCCESS;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE: {
// these asserts were already checked by the platform-independent layer
assert((AUDIOTRACK_MIN_PLAYBACKRATE_PERMILLE <= rate) &&
(rate <= AUDIOTRACK_MAX_PLAYBACKRATE_PERMILLE));
assert(constraints & SL_RATEPROP_NOPITCHCORAUDIO);
// get the content sample rate
uint32_t contentRate = sles_to_android_sampleRate(ap->mSampleRateMilliHz);
// apply the SL ES playback rate on the AudioTrack as a factor of its content sample rate
if (ap->mAudioTrack != 0) {
ap->mAudioTrack->setSampleRate(contentRate * (rate/1000.0f));
}
}
break;
case AUDIOPLAYER_FROM_URIFD:
assert(rate == 1000);
assert(constraints & SL_RATEPROP_NOPITCHCORAUDIO);
// that was easy
break;
default:
SL_LOGE("Unexpected object type %d", ap->mAndroidObjType);
result = SL_RESULT_FEATURE_UNSUPPORTED;
break;
}
return result;
}
//-----------------------------------------------------------------------------
// precondition
// called with no lock held
// ap != NULL
// pItemCount != NULL
SLresult android_audioPlayer_metadata_getItemCount(CAudioPlayer *ap, SLuint32 *pItemCount) {
if (ap->mAPlayer == 0) {
return SL_RESULT_PARAMETER_INVALID;
}
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
{
android::AudioSfDecoder* decoder =
static_cast<android::AudioSfDecoder*>(ap->mAPlayer.get());
*pItemCount = decoder->getPcmFormatKeyCount();
}
break;
default:
*pItemCount = 0;
break;
}
return SL_RESULT_SUCCESS;
}
//-----------------------------------------------------------------------------
// precondition
// called with no lock held
// ap != NULL
// pKeySize != NULL
SLresult android_audioPlayer_metadata_getKeySize(CAudioPlayer *ap,
SLuint32 index, SLuint32 *pKeySize) {
if (ap->mAPlayer == 0) {
return SL_RESULT_PARAMETER_INVALID;
}
SLresult res = SL_RESULT_SUCCESS;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
{
android::AudioSfDecoder* decoder =
static_cast<android::AudioSfDecoder*>(ap->mAPlayer.get());
SLuint32 keyNameSize = 0;
if (!decoder->getPcmFormatKeySize(index, &keyNameSize)) {
res = SL_RESULT_PARAMETER_INVALID;
} else {
// *pKeySize is the size of the region used to store the key name AND
// the information about the key (size, lang, encoding)
*pKeySize = keyNameSize + sizeof(SLMetadataInfo);
}
}
break;
default:
*pKeySize = 0;
res = SL_RESULT_PARAMETER_INVALID;
break;
}
return res;
}
//-----------------------------------------------------------------------------
// precondition
// called with no lock held
// ap != NULL
// pKey != NULL
SLresult android_audioPlayer_metadata_getKey(CAudioPlayer *ap,
SLuint32 index, SLuint32 size, SLMetadataInfo *pKey) {
if (ap->mAPlayer == 0) {
return SL_RESULT_PARAMETER_INVALID;
}
SLresult res = SL_RESULT_SUCCESS;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
{
android::AudioSfDecoder* decoder =
static_cast<android::AudioSfDecoder*>(ap->mAPlayer.get());
if ((size < sizeof(SLMetadataInfo) ||
(!decoder->getPcmFormatKeyName(index, size - sizeof(SLMetadataInfo),
(char*)pKey->data)))) {
res = SL_RESULT_PARAMETER_INVALID;
} else {
// successfully retrieved the key value, update the other fields
pKey->encoding = SL_CHARACTERENCODING_UTF8;
memcpy((char *) pKey->langCountry, "en", 3);
pKey->size = strlen((char*)pKey->data) + 1;
}
}
break;
default:
res = SL_RESULT_PARAMETER_INVALID;
break;
}
return res;
}
//-----------------------------------------------------------------------------
// precondition
// called with no lock held
// ap != NULL
// pValueSize != NULL
SLresult android_audioPlayer_metadata_getValueSize(CAudioPlayer *ap,
SLuint32 index, SLuint32 *pValueSize) {
if (ap->mAPlayer == 0) {
return SL_RESULT_PARAMETER_INVALID;
}
SLresult res = SL_RESULT_SUCCESS;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
{
android::AudioSfDecoder* decoder =
static_cast<android::AudioSfDecoder*>(ap->mAPlayer.get());
SLuint32 valueSize = 0;
if (!decoder->getPcmFormatValueSize(index, &valueSize)) {
res = SL_RESULT_PARAMETER_INVALID;
} else {
// *pValueSize is the size of the region used to store the key value AND
// the information about the value (size, lang, encoding)
*pValueSize = valueSize + sizeof(SLMetadataInfo);
}
}
break;
default:
*pValueSize = 0;
res = SL_RESULT_PARAMETER_INVALID;
break;
}
return res;
}
//-----------------------------------------------------------------------------
// precondition
// called with no lock held
// ap != NULL
// pValue != NULL
SLresult android_audioPlayer_metadata_getValue(CAudioPlayer *ap,
SLuint32 index, SLuint32 size, SLMetadataInfo *pValue) {
if (ap->mAPlayer == 0) {
return SL_RESULT_PARAMETER_INVALID;
}
SLresult res = SL_RESULT_SUCCESS;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
{
android::AudioSfDecoder* decoder =
static_cast<android::AudioSfDecoder*>(ap->mAPlayer.get());
pValue->encoding = SL_CHARACTERENCODING_BINARY;
memcpy((char *) pValue->langCountry, "en", 3); // applicable here?
SLuint32 valueSize = 0;
if ((size < sizeof(SLMetadataInfo)
|| (!decoder->getPcmFormatValueSize(index, &valueSize))
|| (!decoder->getPcmFormatKeyValue(index, size - sizeof(SLMetadataInfo),
(SLuint32*)pValue->data)))) {
res = SL_RESULT_PARAMETER_INVALID;
} else {
pValue->size = valueSize;
}
}
break;
default:
res = SL_RESULT_PARAMETER_INVALID;
break;
}
return res;
}
//-----------------------------------------------------------------------------
// preconditions
// ap != NULL
// mutex is locked
// play state has changed
void android_audioPlayer_setPlayState(CAudioPlayer *ap) {
SLuint32 playState = ap->mPlay.mState;
AndroidObjectState objState = ap->mAndroidObjState;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
switch (playState) {
case SL_PLAYSTATE_STOPPED:
SL_LOGV("setting AudioPlayer to SL_PLAYSTATE_STOPPED");
if (ap->mAudioTrack != 0) {
ap->mAudioTrack->stop();
}
break;
case SL_PLAYSTATE_PAUSED:
SL_LOGV("setting AudioPlayer to SL_PLAYSTATE_PAUSED");
if (ap->mAudioTrack != 0) {
ap->mAudioTrack->pause();
}
break;
case SL_PLAYSTATE_PLAYING:
SL_LOGV("setting AudioPlayer to SL_PLAYSTATE_PLAYING");
if (ap->mAudioTrack != 0) {
// instead of ap->mAudioTrack->start();
ap->mDeferredStart = true;
}
break;
default:
// checked by caller, should not happen
break;
}
break;
case AUDIOPLAYER_FROM_URIFD: // intended fall-through
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
// FIXME report and use the return code to the lock mechanism, which is where play state
// changes are updated (see object_unlock_exclusive_attributes())
aplayer_setPlayState(ap->mAPlayer, playState, &(ap->mAndroidObjState));
break;
default:
SL_LOGE(ERROR_PLAYERSETPLAYSTATE_UNEXPECTED_OBJECT_TYPE_D, ap->mAndroidObjType);
break;
}
}
//-----------------------------------------------------------------------------
// call when either player event flags, marker position, or position update period changes
void android_audioPlayer_usePlayEventMask(CAudioPlayer *ap) {
IPlay *pPlayItf = &ap->mPlay;
SLuint32 eventFlags = pPlayItf->mEventFlags;
/*switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:*/
if (ap->mAPlayer != 0) {
assert(ap->mAudioTrack == 0);
ap->mAPlayer->setPlayEvents((int32_t) eventFlags, (int32_t) pPlayItf->mMarkerPosition,
(int32_t) pPlayItf->mPositionUpdatePeriod);
return;
}
if (ap->mAudioTrack == 0) {
return;
}
if (eventFlags & SL_PLAYEVENT_HEADATMARKER) {
ap->mAudioTrack->setMarkerPosition((uint32_t)((((int64_t)pPlayItf->mMarkerPosition
* sles_to_android_sampleRate(ap->mSampleRateMilliHz)))/1000));
} else {
// clear marker
ap->mAudioTrack->setMarkerPosition(0);
}
if (eventFlags & SL_PLAYEVENT_HEADATNEWPOS) {
ap->mAudioTrack->setPositionUpdatePeriod(
(uint32_t)((((int64_t)pPlayItf->mPositionUpdatePeriod
* sles_to_android_sampleRate(ap->mSampleRateMilliHz)))/1000));
} else {
// clear periodic update
ap->mAudioTrack->setPositionUpdatePeriod(0);
}
if (eventFlags & SL_PLAYEVENT_HEADATEND) {
// nothing to do for SL_PLAYEVENT_HEADATEND, callback event will be checked against mask
}
if (eventFlags & SL_PLAYEVENT_HEADMOVING) {
// FIXME support SL_PLAYEVENT_HEADMOVING
SL_LOGD("[ FIXME: IPlay_SetCallbackEventsMask(SL_PLAYEVENT_HEADMOVING) on an "
"SL_OBJECTID_AUDIOPLAYER to be implemented ]");
}
if (eventFlags & SL_PLAYEVENT_HEADSTALLED) {
// nothing to do for SL_PLAYEVENT_HEADSTALLED, callback event will be checked against mask
}
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_getDuration(IPlay *pPlayItf, SLmillisecond *pDurMsec) {
CAudioPlayer *ap = (CAudioPlayer *)pPlayItf->mThis;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD: // intended fall-through
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE: {
int32_t durationMsec = ANDROID_UNKNOWN_TIME;
if (ap->mAPlayer != 0) {
ap->mAPlayer->getDurationMsec(&durationMsec);
}
*pDurMsec = durationMsec == ANDROID_UNKNOWN_TIME ? SL_TIME_UNKNOWN : durationMsec;
break;
}
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
default: {
*pDurMsec = SL_TIME_UNKNOWN;
}
}
return SL_RESULT_SUCCESS;
}
//-----------------------------------------------------------------------------
void android_audioPlayer_getPosition(IPlay *pPlayItf, SLmillisecond *pPosMsec) {
CAudioPlayer *ap = (CAudioPlayer *)pPlayItf->mThis;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
if ((ap->mSampleRateMilliHz == UNKNOWN_SAMPLERATE) || (ap->mAudioTrack == 0)) {
*pPosMsec = 0;
} else {
uint32_t positionInFrames;
ap->mAudioTrack->getPosition(&positionInFrames);
*pPosMsec = ((int64_t)positionInFrames * 1000) /
sles_to_android_sampleRate(ap->mSampleRateMilliHz);
}
break;
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_URIFD:
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
int32_t posMsec = ANDROID_UNKNOWN_TIME;
if (ap->mAPlayer != 0) {
ap->mAPlayer->getPositionMsec(&posMsec);
}
*pPosMsec = posMsec == ANDROID_UNKNOWN_TIME ? 0 : posMsec;
break;
}
default:
*pPosMsec = 0;
}
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_seek(CAudioPlayer *ap, SLmillisecond posMsec) {
SLresult result = SL_RESULT_SUCCESS;
switch(ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE: // intended fall-through
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE:
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
result = SL_RESULT_FEATURE_UNSUPPORTED;
break;
case AUDIOPLAYER_FROM_URIFD: // intended fall-through
case AUDIOPLAYER_FROM_URIFD_TO_PCM_BUFFERQUEUE:
if (ap->mAPlayer != 0) {
ap->mAPlayer->seek(posMsec);
}
break;
default:
break;
}
return result;
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_loop(CAudioPlayer *ap, SLboolean loopEnable) {
SLresult result = SL_RESULT_SUCCESS;
switch (ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD:
// case AUDIOPLAY_FROM_URIFD_TO_PCM_BUFFERQUEUE:
// would actually work, but what's the point?
if (ap->mAPlayer != 0) {
ap->mAPlayer->loop((bool)loopEnable);
}
break;
default:
result = SL_RESULT_FEATURE_UNSUPPORTED;
break;
}
return result;
}
//-----------------------------------------------------------------------------
SLresult android_audioPlayer_setBufferingUpdateThresholdPerMille(CAudioPlayer *ap,
SLpermille threshold) {
SLresult result = SL_RESULT_SUCCESS;
switch (ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_URIFD:
if (ap->mAPlayer != 0) {
ap->mAPlayer->setBufferingUpdateThreshold(threshold / 10);
}
break;
default: {}
}
return result;
}
//-----------------------------------------------------------------------------
void android_audioPlayer_bufferQueue_onRefilled_l(CAudioPlayer *ap) {
// the AudioTrack associated with the AudioPlayer receiving audio from a PCM buffer
// queue was stopped when the queue become empty, we restart as soon as a new buffer
// has been enqueued since we're in playing state
if (ap->mAudioTrack != 0) {
// instead of ap->mAudioTrack->start();
ap->mDeferredStart = true;
}
// when the queue became empty, an underflow on the prefetch status itf was sent. Now the queue
// has received new data, signal it has sufficient data
if (IsInterfaceInitialized(&(ap->mObject), MPH_PREFETCHSTATUS)) {
// we wouldn't have been called unless we were previously in the underflow state
assert(SL_PREFETCHSTATUS_UNDERFLOW == ap->mPrefetchStatus.mStatus);
assert(0 == ap->mPrefetchStatus.mLevel);
ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_SUFFICIENTDATA;
ap->mPrefetchStatus.mLevel = 1000;
// callback or no callback?
SLuint32 prefetchEvents = ap->mPrefetchStatus.mCallbackEventsMask &
(SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE);
if (SL_PREFETCHEVENT_NONE != prefetchEvents) {
ap->mPrefetchStatus.mDeferredPrefetchCallback = ap->mPrefetchStatus.mCallback;
ap->mPrefetchStatus.mDeferredPrefetchContext = ap->mPrefetchStatus.mContext;
ap->mPrefetchStatus.mDeferredPrefetchEvents = prefetchEvents;
}
}
}
//-----------------------------------------------------------------------------
/*
* BufferQueue::Clear
*/
SLresult android_audioPlayer_bufferQueue_onClear(CAudioPlayer *ap) {
SLresult result = SL_RESULT_SUCCESS;
switch (ap->mAndroidObjType) {
//-----------------------------------
// AudioTrack
case AUDIOPLAYER_FROM_PCM_BUFFERQUEUE:
if (ap->mAudioTrack != 0) {
ap->mAudioTrack->flush();
}
break;
default:
result = SL_RESULT_INTERNAL_ERROR;
break;
}
return result;
}
//-----------------------------------------------------------------------------
void android_audioPlayer_androidBufferQueue_clear_l(CAudioPlayer *ap) {
switch (ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE:
if (ap->mAPlayer != 0) {
android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(ap->mAPlayer.get());
splr->appClear_l();
} break;
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
// nothing to do here, fall through
default:
break;
}
}
void android_audioPlayer_androidBufferQueue_onRefilled_l(CAudioPlayer *ap) {
switch (ap->mAndroidObjType) {
case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE:
if (ap->mAPlayer != 0) {
android::StreamPlayer* splr = static_cast<android::StreamPlayer*>(ap->mAPlayer.get());
splr->queueRefilled();
} break;
case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE:
// FIXME this may require waking up the decoder if it is currently starved and isn't polling
default:
break;
}
}