/*
* 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.
*/
#include <variablespeed.h>
#include <unistd.h>
#include <stdlib.h>
#include <sola_time_scaler.h>
#include <ring_buffer.h>
#include <hlogging.h>
#include <vector>
#include <sys/system_properties.h>
// ****************************************************************************
// Constants, utility methods, structures and other miscellany used throughout
// this file.
namespace {
// These variables are used to determine the size of the buffer queue used by
// the decoder.
// This is not the same as the large buffer used to hold the uncompressed data
// - for that see the member variable decodeBuffer_.
// The choice of 1152 corresponds to the number of samples per mp3 frame, so is
// a good choice of size for a decoding buffer in the absence of other
// information (we don't know exactly what formats we will be working with).
const size_t kNumberOfBuffersInQueue = 4;
const size_t kNumberOfSamplesPerBuffer = 1152;
const size_t kBufferSizeInBytes = 2 * kNumberOfSamplesPerBuffer;
const size_t kSampleSizeInBytes = 4;
// When calculating play buffer size before pushing to audio player.
const size_t kNumberOfBytesPerInt16 = 2;
// How long to sleep during the main play loop and the decoding callback loop.
// In due course this should be replaced with the better signal and wait on
// condition rather than busy-looping.
const int kSleepTimeMicros = 1000;
// Used in detecting errors with the OpenSL ES framework.
const SLuint32 kPrefetchErrorCandidate =
SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE;
// Structure used when we perform a decoding callback.
typedef struct CallbackContext_ {
// Pointer to local storage buffers for decoded audio data.
int8_t* pDataBase;
// Pointer to the current buffer within local storage.
int8_t* pData;
// Used to read the sample rate and channels from the decoding stream during
// the first decoding callback.
SLMetadataExtractionItf decoderMetadata;
// The play interface used for reading duration.
SLPlayItf playItf;
} CallbackContext;
// Local storage for decoded audio data.
int8_t pcmData[kNumberOfBuffersInQueue * kBufferSizeInBytes];
#define CheckSLResult(message, result) \
CheckSLResult_Real(message, result, __LINE__)
// Helper function for debugging - checks the OpenSL result for success.
void CheckSLResult_Real(const char* message, SLresult result, int line) {
// This can be helpful when debugging.
// LOGD("sl result %d for %s", result, message);
if (SL_RESULT_SUCCESS != result) {
LOGE("slresult was %d at %s file variablespeed line %d",
static_cast<int>(result), message, line);
}
CHECK(SL_RESULT_SUCCESS == result);
}
// Whether logging should be enabled. Only used if LOG_OPENSL_API_CALL is
// defined to use it.
bool gLogEnabled = false;
// The property to set in order to enable logging.
const char *const kLogTagVariableSpeed = "log.tag.VariableSpeed";
bool ShouldLog() {
char buffer[PROP_VALUE_MAX];
__system_property_get(kLogTagVariableSpeed, buffer);
return strlen(buffer) > 0;
}
} // namespace
// ****************************************************************************
// Static instance of audio engine, and methods for getting, setting and
// deleting it.
// The single global audio engine instance.
AudioEngine* AudioEngine::audioEngine_ = NULL;
android::Mutex publishEngineLock_;
AudioEngine* AudioEngine::GetEngine() {
android::Mutex::Autolock autoLock(publishEngineLock_);
if (audioEngine_ == NULL) {
LOGE("you haven't initialized the audio engine");
CHECK(false);
return NULL;
}
return audioEngine_;
}
void AudioEngine::SetEngine(AudioEngine* engine) {
if (audioEngine_ != NULL) {
LOGE("you have already set the audio engine");
CHECK(false);
return;
}
audioEngine_ = engine;
}
void AudioEngine::DeleteEngine() {
if (audioEngine_ == NULL) {
LOGE("you haven't initialized the audio engine");
CHECK(false);
return;
}
delete audioEngine_;
audioEngine_ = NULL;
}
// ****************************************************************************
// The callbacks from the engine require static callback functions.
// Here are the static functions - they just delegate to instance methods on
// the engine.
static void PlayingBufferQueueCb(SLAndroidSimpleBufferQueueItf, void*) {
AudioEngine::GetEngine()->PlayingBufferQueueCallback();
}
static void PrefetchEventCb(SLPrefetchStatusItf caller, void*, SLuint32 event) {
AudioEngine::GetEngine()->PrefetchEventCallback(caller, event);
}
static void DecodingBufferQueueCb(SLAndroidSimpleBufferQueueItf queueItf,
void *context) {
AudioEngine::GetEngine()->DecodingBufferQueueCallback(queueItf, context);
}
static void DecodingEventCb(SLPlayItf caller, void*, SLuint32 event) {
AudioEngine::GetEngine()->DecodingEventCallback(caller, event);
}
// ****************************************************************************
// Macros for making working with OpenSL easier.
// Log based on the value of a property.
#define LOG_OPENSL_API_CALL(string) (gLogEnabled && LOGV(string))
// The regular macro: log an api call, make the api call, check the result.
#define OpenSL(obj, method, ...) \
{ \
LOG_OPENSL_API_CALL("OpenSL " #method "(" #obj ", " #__VA_ARGS__ ")"); \
SLresult result = (*obj)->method(obj, __VA_ARGS__); \
CheckSLResult("OpenSL " #method "(" #obj ", " #__VA_ARGS__ ")", result); \
}
// Special case call for api call that has void return value, can't be checked.
#define VoidOpenSL(obj, method) \
{ \
LOG_OPENSL_API_CALL("OpenSL (void) " #method "(" #obj ")"); \
(*obj)->method(obj); \
}
// Special case for api call with checked result but takes no arguments.
#define OpenSL0(obj, method) \
{ \
LOG_OPENSL_API_CALL("OpenSL " #method "(" #obj ")"); \
SLresult result = (*obj)->method(obj); \
CheckSLResult("OpenSL " #method "(" #obj ")", result); \
}
// Special case for api call whose result we want to store, not check.
// We have to encapsulate the two calls in braces, so that this expression
// evaluates to the last expression not the first.
#define ReturnOpenSL(obj, method, ...) \
( \
LOG_OPENSL_API_CALL("OpenSL (int) " \
#method "(" #obj ", " #__VA_ARGS__ ")"), \
(*obj)->method(obj, __VA_ARGS__) \
) \
// ****************************************************************************
// Static utility methods.
// Set the audio stream type for the player.
//
// Must be called before it is realized.
//
// The caller must have requested the SL_IID_ANDROIDCONFIGURATION interface when
// creating the player.
static void setAudioStreamType(SLObjectItf audioPlayer, SLint32 audioStreamType) {
SLAndroidConfigurationItf playerConfig;
OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
// The STREAM_XXX constants defined by android.media.AudioManager match the
// corresponding SL_ANDROID_STREAM_XXX constants defined by
// include/SLES/OpenSLES_AndroidConfiguration.h, so we can just pass the
// value across.
OpenSL(playerConfig, SetConfiguration, SL_ANDROID_KEY_STREAM_TYPE,
&audioStreamType, sizeof(audioStreamType));
}
// Must be called with callbackLock_ held.
static void ReadSampleRateAndChannelCount(CallbackContext *pContext,
SLuint32 *sampleRateOut, SLuint32 *channelsOut) {
SLMetadataExtractionItf decoderMetadata = pContext->decoderMetadata;
SLuint32 itemCount;
OpenSL(decoderMetadata, GetItemCount, &itemCount);
SLuint32 i, keySize, valueSize;
SLMetadataInfo *keyInfo, *value;
for (i = 0; i < itemCount; ++i) {
keyInfo = value = NULL;
keySize = valueSize = 0;
OpenSL(decoderMetadata, GetKeySize, i, &keySize);
keyInfo = static_cast<SLMetadataInfo*>(malloc(keySize));
if (keyInfo) {
OpenSL(decoderMetadata, GetKey, i, keySize, keyInfo);
if (keyInfo->encoding == SL_CHARACTERENCODING_ASCII
|| keyInfo->encoding == SL_CHARACTERENCODING_UTF8) {
OpenSL(decoderMetadata, GetValueSize, i, &valueSize);
value = static_cast<SLMetadataInfo*>(malloc(valueSize));
if (value) {
OpenSL(decoderMetadata, GetValue, i, valueSize, value);
if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_SAMPLERATE) == 0) {
SLuint32 sampleRate = *(reinterpret_cast<SLuint32*>(value->data));
LOGD("sample Rate: %d", sampleRate);
*sampleRateOut = sampleRate;
} else if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_NUMCHANNELS) == 0) {
SLuint32 channels = *(reinterpret_cast<SLuint32*>(value->data));
LOGD("channels: %d", channels);
*channelsOut = channels;
}
free(value);
}
}
free(keyInfo);
}
}
}
// Must be called with callbackLock_ held.
static void RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
SLAndroidSimpleBufferQueueItf decoderQueue, CallbackContext* context) {
// Register a callback on the decoder queue, so that we will be called
// throughout the decoding process (and can then extract the decoded audio
// for the next bit of the pipeline).
OpenSL(decoderQueue, RegisterCallback, DecodingBufferQueueCb, context);
// Enqueue buffers to map the region of memory allocated to store the
// decoded data.
for (size_t i = 0; i < kNumberOfBuffersInQueue; i++) {
OpenSL(decoderQueue, Enqueue, context->pData, kBufferSizeInBytes);
context->pData += kBufferSizeInBytes;
}
context->pData = context->pDataBase;
}
// ****************************************************************************
// Constructor and Destructor.
AudioEngine::AudioEngine(size_t targetFrames, float windowDuration,
float windowOverlapDuration, size_t maxPlayBufferCount, float initialRate,
size_t decodeInitialSize, size_t decodeMaxSize, size_t startPositionMillis,
int audioStreamType)
: decodeBuffer_(decodeInitialSize, decodeMaxSize),
playingBuffers_(), freeBuffers_(), timeScaler_(NULL),
floatBuffer_(NULL), injectBuffer_(NULL),
mSampleRate(0), mChannels(0),
targetFrames_(targetFrames),
windowDuration_(windowDuration),
windowOverlapDuration_(windowOverlapDuration),
maxPlayBufferCount_(maxPlayBufferCount), initialRate_(initialRate),
startPositionMillis_(startPositionMillis),
audioStreamType_(audioStreamType),
totalDurationMs_(0), decoderCurrentPosition_(0), startRequested_(false),
stopRequested_(false), finishedDecoding_(false) {
// Determine whether we should log calls.
gLogEnabled = ShouldLog();
}
AudioEngine::~AudioEngine() {
// destroy the time scaler
if (timeScaler_ != NULL) {
delete timeScaler_;
timeScaler_ = NULL;
}
// delete all outstanding playing and free buffers
android::Mutex::Autolock autoLock(playBufferLock_);
while (playingBuffers_.size() > 0) {
delete[] playingBuffers_.front();
playingBuffers_.pop();
}
while (freeBuffers_.size() > 0) {
delete[] freeBuffers_.top();
freeBuffers_.pop();
}
delete[] floatBuffer_;
floatBuffer_ = NULL;
delete[] injectBuffer_;
injectBuffer_ = NULL;
}
// ****************************************************************************
// Regular AudioEngine class methods.
void AudioEngine::SetVariableSpeed(float speed) {
// TODO: Mutex for shared time scaler accesses.
if (HasSampleRateAndChannels()) {
GetTimeScaler()->set_speed(speed);
} else {
// This is being called at a point where we have not yet processed enough
// data to determine the sample rate and number of channels.
// Ignore the call. See http://b/5140693.
LOGD("set varaible speed called, sample rate and channels not ready yet");
}
}
void AudioEngine::RequestStart() {
android::Mutex::Autolock autoLock(lock_);
startRequested_ = true;
}
void AudioEngine::ClearRequestStart() {
android::Mutex::Autolock autoLock(lock_);
startRequested_ = false;
}
bool AudioEngine::GetWasStartRequested() {
android::Mutex::Autolock autoLock(lock_);
return startRequested_;
}
void AudioEngine::RequestStop() {
android::Mutex::Autolock autoLock(lock_);
stopRequested_ = true;
}
int AudioEngine::GetCurrentPosition() {
android::Mutex::Autolock autoLock(decodeBufferLock_);
double result = decodeBuffer_.GetTotalAdvancedCount();
// TODO: This is horrible, but should be removed soon once the outstanding
// issue with get current position on decoder is fixed.
android::Mutex::Autolock autoLock2(callbackLock_);
return static_cast<int>(
(result * 1000) / mSampleRate / mChannels + startPositionMillis_);
}
int AudioEngine::GetTotalDuration() {
android::Mutex::Autolock autoLock(lock_);
return static_cast<int>(totalDurationMs_);
}
video_editing::SolaTimeScaler* AudioEngine::GetTimeScaler() {
if (timeScaler_ == NULL) {
CHECK(HasSampleRateAndChannels());
android::Mutex::Autolock autoLock(callbackLock_);
timeScaler_ = new video_editing::SolaTimeScaler();
timeScaler_->Init(mSampleRate, mChannels, initialRate_, windowDuration_,
windowOverlapDuration_);
}
return timeScaler_;
}
bool AudioEngine::EnqueueNextBufferOfAudio(
SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
size_t channels;
{
android::Mutex::Autolock autoLock(callbackLock_);
channels = mChannels;
}
size_t frameSizeInBytes = kSampleSizeInBytes * channels;
size_t frameCount = 0;
while (frameCount < targetFrames_) {
size_t framesLeft = targetFrames_ - frameCount;
// If there is data already in the time scaler, retrieve it.
if (GetTimeScaler()->available() > 0) {
size_t retrieveCount = min(GetTimeScaler()->available(), framesLeft);
int count = GetTimeScaler()->RetrieveSamples(
floatBuffer_ + frameCount * channels, retrieveCount);
if (count <= 0) {
LOGD("error: count was %d", count);
break;
}
frameCount += count;
continue;
}
// If there is no data in the time scaler, then feed some into it.
android::Mutex::Autolock autoLock(decodeBufferLock_);
size_t framesInDecodeBuffer =
decodeBuffer_.GetSizeInBytes() / frameSizeInBytes;
size_t framesScalerCanHandle = GetTimeScaler()->input_limit();
size_t framesToInject = min(framesInDecodeBuffer,
min(targetFrames_, framesScalerCanHandle));
if (framesToInject <= 0) {
// No more frames left to inject.
break;
}
for (size_t i = 0; i < framesToInject * channels; ++i) {
injectBuffer_[i] = decodeBuffer_.GetAtIndex(i);
}
int count = GetTimeScaler()->InjectSamples(injectBuffer_, framesToInject);
if (count <= 0) {
LOGD("error: count was %d", count);
break;
}
decodeBuffer_.AdvanceHeadPointerShorts(count * channels);
}
if (frameCount <= 0) {
// We must have finished playback.
if (GetEndOfDecoderReached()) {
// If we've finished decoding, clear the buffer - so we will terminate.
ClearDecodeBuffer();
}
return false;
}
// Get a free playing buffer.
int16* playBuffer;
{
android::Mutex::Autolock autoLock(playBufferLock_);
if (freeBuffers_.size() > 0) {
// If we have a free buffer, recycle it.
playBuffer = freeBuffers_.top();
freeBuffers_.pop();
} else {
// Otherwise allocate a new one.
playBuffer = new int16[targetFrames_ * channels];
}
}
// Try to play the buffer.
for (size_t i = 0; i < frameCount * channels; ++i) {
playBuffer[i] = floatBuffer_[i];
}
size_t sizeOfPlayBufferInBytes =
frameCount * channels * kNumberOfBytesPerInt16;
SLresult result = ReturnOpenSL(audioPlayerQueue, Enqueue, playBuffer,
sizeOfPlayBufferInBytes);
if (result == SL_RESULT_SUCCESS) {
android::Mutex::Autolock autoLock(playBufferLock_);
playingBuffers_.push(playBuffer);
} else {
LOGE("could not enqueue audio buffer");
delete[] playBuffer;
}
return (result == SL_RESULT_SUCCESS);
}
bool AudioEngine::GetEndOfDecoderReached() {
android::Mutex::Autolock autoLock(lock_);
return finishedDecoding_;
}
void AudioEngine::SetEndOfDecoderReached() {
android::Mutex::Autolock autoLock(lock_);
finishedDecoding_ = true;
}
bool AudioEngine::PlayFileDescriptor(int fd, int64 offset, int64 length) {
SLDataLocator_AndroidFD loc_fd = {
SL_DATALOCATOR_ANDROIDFD, fd, offset, length };
SLDataFormat_MIME format_mime = {
SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED };
SLDataSource audioSrc = { &loc_fd, &format_mime };
return PlayFromThisSource(audioSrc);
}
bool AudioEngine::PlayUri(const char* uri) {
// Source of audio data for the decoding
SLDataLocator_URI decUri = { SL_DATALOCATOR_URI,
const_cast<SLchar*>(reinterpret_cast<const SLchar*>(uri)) };
SLDataFormat_MIME decMime = {
SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED };
SLDataSource decSource = { &decUri, &decMime };
return PlayFromThisSource(decSource);
}
bool AudioEngine::IsDecodeBufferEmpty() {
android::Mutex::Autolock autoLock(decodeBufferLock_);
return decodeBuffer_.GetSizeInBytes() <= 0;
}
void AudioEngine::ClearDecodeBuffer() {
android::Mutex::Autolock autoLock(decodeBufferLock_);
decodeBuffer_.Clear();
}
static size_t ReadDuration(SLPlayItf playItf) {
SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
OpenSL(playItf, GetDuration, &durationInMsec);
if (durationInMsec == SL_TIME_UNKNOWN) {
LOGE("can't get duration");
return 0;
}
LOGD("duration: %d", static_cast<int>(durationInMsec));
return durationInMsec;
}
static size_t ReadPosition(SLPlayItf playItf) {
SLmillisecond positionInMsec = SL_TIME_UNKNOWN;
OpenSL(playItf, GetPosition, &positionInMsec);
if (positionInMsec == SL_TIME_UNKNOWN) {
LOGE("can't get position");
return 0;
}
LOGW("decoder position: %d", static_cast<int>(positionInMsec));
return positionInMsec;
}
static void CreateAndRealizeEngine(SLObjectItf &engine,
SLEngineItf &engineInterface) {
SLEngineOption EngineOption[] = { {
SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } };
SLresult result = slCreateEngine(&engine, 1, EngineOption, 0, NULL, NULL);
CheckSLResult("create engine", result);
OpenSL(engine, Realize, SL_BOOLEAN_FALSE);
OpenSL(engine, GetInterface, SL_IID_ENGINE, &engineInterface);
}
SLuint32 AudioEngine::GetSLSampleRate() {
android::Mutex::Autolock autoLock(callbackLock_);
return mSampleRate * 1000;
}
SLuint32 AudioEngine::GetSLChannels() {
android::Mutex::Autolock autoLock(callbackLock_);
switch (mChannels) {
case 2:
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
case 1:
return SL_SPEAKER_FRONT_CENTER;
default:
LOGE("unknown channels %d, using 2", mChannels);
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
}
}
SLuint32 AudioEngine::GetChannelCount() {
android::Mutex::Autolock autoLock(callbackLock_);
return mChannels;
}
static void CreateAndRealizeAudioPlayer(SLuint32 slSampleRate,
size_t channelCount, SLuint32 slChannels, SLint32 audioStreamType, SLObjectItf &outputMix,
SLObjectItf &audioPlayer, SLEngineItf &engineInterface) {
// Define the source and sink for the audio player: comes from a buffer queue
// and goes to the output mix.
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channelCount, slSampleRate,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
slChannels, SL_BYTEORDER_LITTLEENDIAN};
SLDataSource playingSrc = {&loc_bufq, &format_pcm};
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMix};
SLDataSink audioSnk = {&loc_outmix, NULL};
// Create the audio player, which will play from the buffer queue and send to
// the output mix.
const size_t playerInterfaceCount = 2;
const SLInterfaceID iids[playerInterfaceCount] = {
SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
const SLboolean reqs[playerInterfaceCount] = { SL_BOOLEAN_TRUE };
OpenSL(engineInterface, CreateAudioPlayer, &audioPlayer, &playingSrc,
&audioSnk, playerInterfaceCount, iids, reqs);
setAudioStreamType(audioPlayer, audioStreamType);
OpenSL(audioPlayer, Realize, SL_BOOLEAN_FALSE);
}
bool AudioEngine::HasSampleRateAndChannels() {
android::Mutex::Autolock autoLock(callbackLock_);
return mChannels != 0 && mSampleRate != 0;
}
bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
ClearDecodeBuffer();
SLObjectItf engine;
SLEngineItf engineInterface;
CreateAndRealizeEngine(engine, engineInterface);
// Define the source and sink for the decoding player: comes from the source
// this method was called with, is sent to another buffer queue.
SLDataLocator_AndroidSimpleBufferQueue decBuffQueue;
decBuffQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
decBuffQueue.numBuffers = kNumberOfBuffersInQueue;
// A valid value seems required here but is currently ignored.
SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_44_1,
SL_PCMSAMPLEFORMAT_FIXED_16, 16,
SL_SPEAKER_FRONT_LEFT, SL_BYTEORDER_LITTLEENDIAN};
SLDataSink decDest = { &decBuffQueue, &pcm };
// Create the decoder with the given source and sink.
const size_t decoderInterfaceCount = 5;
SLObjectItf decoder;
const SLInterfaceID decodePlayerInterfaces[decoderInterfaceCount] = {
SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_PREFETCHSTATUS, SL_IID_SEEK,
SL_IID_METADATAEXTRACTION, SL_IID_ANDROIDCONFIGURATION };
const SLboolean decodePlayerRequired[decoderInterfaceCount] = {
SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
SLDataSource sourceCopy(audioSrc);
OpenSL(engineInterface, CreateAudioPlayer, &decoder, &sourceCopy, &decDest,
decoderInterfaceCount, decodePlayerInterfaces, decodePlayerRequired);
// Not sure if this is necessary, but just in case.
setAudioStreamType(decoder, audioStreamType_);
OpenSL(decoder, Realize, SL_BOOLEAN_FALSE);
// Get the play interface from the decoder, and register event callbacks.
// Get the buffer queue, prefetch and seek interfaces.
SLPlayItf decoderPlay = NULL;
SLAndroidSimpleBufferQueueItf decoderQueue = NULL;
SLPrefetchStatusItf decoderPrefetch = NULL;
SLSeekItf decoderSeek = NULL;
SLMetadataExtractionItf decoderMetadata = NULL;
OpenSL(decoder, GetInterface, SL_IID_PLAY, &decoderPlay);
OpenSL(decoderPlay, SetCallbackEventsMask, SL_PLAYEVENT_HEADATEND);
OpenSL(decoderPlay, RegisterCallback, DecodingEventCb, NULL);
OpenSL(decoder, GetInterface, SL_IID_PREFETCHSTATUS, &decoderPrefetch);
OpenSL(decoder, GetInterface, SL_IID_SEEK, &decoderSeek);
OpenSL(decoder, GetInterface, SL_IID_METADATAEXTRACTION, &decoderMetadata);
OpenSL(decoder, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&decoderQueue);
// Initialize the callback structure, used during the decoding.
CallbackContext callbackContext;
{
android::Mutex::Autolock autoLock(callbackLock_);
callbackContext.pDataBase = pcmData;
callbackContext.pData = pcmData;
callbackContext.decoderMetadata = decoderMetadata;
callbackContext.playItf = decoderPlay;
RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
decoderQueue, &callbackContext);
}
// Initialize the callback for prefetch errors, if we can't open the
// resource to decode.
OpenSL(decoderPrefetch, SetCallbackEventsMask, kPrefetchErrorCandidate);
OpenSL(decoderPrefetch, RegisterCallback, PrefetchEventCb, &decoderPrefetch);
// Seek to the start position.
OpenSL(decoderSeek, SetPosition, startPositionMillis_, SL_SEEKMODE_ACCURATE);
// Start decoding immediately.
OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
// These variables hold the audio player and its output.
// They will only be constructed once the decoder has invoked the callback,
// and given us the correct sample rate, number of channels and duration.
SLObjectItf outputMix = NULL;
SLObjectItf audioPlayer = NULL;
SLPlayItf audioPlayerPlay = NULL;
SLAndroidSimpleBufferQueueItf audioPlayerQueue = NULL;
// The main loop - until we're told to stop: if there is audio data coming
// out of the decoder, feed it through the time scaler.
// As it comes out of the time scaler, feed it into the audio player.
while (!Finished()) {
if (GetWasStartRequested() && HasSampleRateAndChannels()) {
// Build the audio player.
// TODO: What happens if I maliciously call start lots of times?
floatBuffer_ = new float[targetFrames_ * mChannels];
injectBuffer_ = new float[targetFrames_ * mChannels];
OpenSL(engineInterface, CreateOutputMix, &outputMix, 0, NULL, NULL);
OpenSL(outputMix, Realize, SL_BOOLEAN_FALSE);
CreateAndRealizeAudioPlayer(GetSLSampleRate(), GetChannelCount(),
GetSLChannels(), audioStreamType_, outputMix, audioPlayer,
engineInterface);
OpenSL(audioPlayer, GetInterface, SL_IID_PLAY, &audioPlayerPlay);
OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&audioPlayerQueue);
OpenSL(audioPlayerQueue, RegisterCallback, PlayingBufferQueueCb, NULL);
ClearRequestStart();
OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
}
EnqueueMoreAudioIfNecessary(audioPlayerQueue);
usleep(kSleepTimeMicros);
}
// Delete the audio player and output mix, iff they have been created.
if (audioPlayer != NULL) {
OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_STOPPED);
OpenSL0(audioPlayerQueue, Clear);
OpenSL(audioPlayerQueue, RegisterCallback, NULL, NULL);
VoidOpenSL(audioPlayer, AbortAsyncOperation);
VoidOpenSL(audioPlayer, Destroy);
VoidOpenSL(outputMix, Destroy);
audioPlayer = NULL;
audioPlayerPlay = NULL;
audioPlayerQueue = NULL;
outputMix = NULL;
}
// Delete the decoder.
OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_STOPPED);
OpenSL(decoderPrefetch, RegisterCallback, NULL, NULL);
// This is returning slresult 13 if I do no playback.
// Repro is to comment out all before this line, and all after enqueueing
// my buffers.
// OpenSL0(decoderQueue, Clear);
OpenSL(decoderQueue, RegisterCallback, NULL, NULL);
decoderSeek = NULL;
decoderPrefetch = NULL;
decoderQueue = NULL;
OpenSL(decoderPlay, RegisterCallback, NULL, NULL);
VoidOpenSL(decoder, AbortAsyncOperation);
VoidOpenSL(decoder, Destroy);
decoderPlay = NULL;
// Delete the engine.
VoidOpenSL(engine, Destroy);
engineInterface = NULL;
return true;
}
bool AudioEngine::Finished() {
if (GetWasStopRequested()) {
return true;
}
android::Mutex::Autolock autoLock(playBufferLock_);
return playingBuffers_.size() <= 0 &&
IsDecodeBufferEmpty() &&
GetEndOfDecoderReached();
}
bool AudioEngine::GetWasStopRequested() {
android::Mutex::Autolock autoLock(lock_);
return stopRequested_;
}
bool AudioEngine::GetHasReachedPlayingBuffersLimit() {
android::Mutex::Autolock autoLock(playBufferLock_);
return playingBuffers_.size() >= maxPlayBufferCount_;
}
void AudioEngine::EnqueueMoreAudioIfNecessary(
SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
bool keepEnqueueing = true;
while (audioPlayerQueue != NULL &&
!GetWasStopRequested() &&
!IsDecodeBufferEmpty() &&
!GetHasReachedPlayingBuffersLimit() &&
keepEnqueueing) {
keepEnqueueing = EnqueueNextBufferOfAudio(audioPlayerQueue);
}
}
bool AudioEngine::DecodeBufferTooFull() {
android::Mutex::Autolock autoLock(decodeBufferLock_);
return decodeBuffer_.IsTooLarge();
}
// ****************************************************************************
// Code for handling the static callbacks.
void AudioEngine::PlayingBufferQueueCallback() {
// The head playing buffer is done, move it to the free list.
android::Mutex::Autolock autoLock(playBufferLock_);
if (playingBuffers_.size() > 0) {
freeBuffers_.push(playingBuffers_.front());
playingBuffers_.pop();
}
}
void AudioEngine::PrefetchEventCallback(
SLPrefetchStatusItf caller, SLuint32 event) {
// If there was a problem during decoding, then signal the end.
SLpermille level = 0;
SLuint32 status;
OpenSL(caller, GetFillLevel, &level);
OpenSL(caller, GetPrefetchStatus, &status);
if ((kPrefetchErrorCandidate == (event & kPrefetchErrorCandidate)) &&
(level == 0) &&
(status == SL_PREFETCHSTATUS_UNDERFLOW)) {
LOGI("prefetcheventcallback error while prefetching data");
SetEndOfDecoderReached();
}
if (SL_PREFETCHSTATUS_SUFFICIENTDATA == event) {
// android::Mutex::Autolock autoLock(prefetchLock_);
// prefetchCondition_.broadcast();
}
}
void AudioEngine::DecodingBufferQueueCallback(
SLAndroidSimpleBufferQueueItf queueItf, void *context) {
if (GetWasStopRequested()) {
return;
}
CallbackContext *pCntxt;
{
android::Mutex::Autolock autoLock(callbackLock_);
pCntxt = reinterpret_cast<CallbackContext*>(context);
}
{
android::Mutex::Autolock autoLock(decodeBufferLock_);
decodeBuffer_.AddData(pCntxt->pData, kBufferSizeInBytes);
}
if (!HasSampleRateAndChannels()) {
android::Mutex::Autolock autoLock(callbackLock_);
ReadSampleRateAndChannelCount(pCntxt, &mSampleRate, &mChannels);
}
{
android::Mutex::Autolock autoLock(lock_);
if (totalDurationMs_ == 0) {
totalDurationMs_ = ReadDuration(pCntxt->playItf);
}
// TODO: This isn't working, it always reports zero.
// ReadPosition(pCntxt->playItf);
}
OpenSL(queueItf, Enqueue, pCntxt->pData, kBufferSizeInBytes);
// Increase data pointer by buffer size
pCntxt->pData += kBufferSizeInBytes;
if (pCntxt->pData >= pCntxt->pDataBase +
(kNumberOfBuffersInQueue * kBufferSizeInBytes)) {
pCntxt->pData = pCntxt->pDataBase;
}
// If we get too much data into the decoder,
// sleep until the playback catches up.
while (!GetWasStopRequested() && DecodeBufferTooFull()) {
usleep(kSleepTimeMicros);
}
}
void AudioEngine::DecodingEventCallback(SLPlayItf, SLuint32 event) {
if (SL_PLAYEVENT_HEADATEND & event) {
SetEndOfDecoderReached();
}
}