/* * 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 #include #include #include #include #include #include #include // **************************************************************************** // 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(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; } bool AudioEngine::CompareAndSetEngine(AudioEngine* expect, AudioEngine* update) { android::Mutex::Autolock autoLock(publishEngineLock_); if (audioEngine_ == expect) { DeleteEngine(); audioEngine_ = update; return true; } return false; } void AudioEngine::DeleteEngine() { if (audioEngine_ != NULL) { 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(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(malloc(valueSize)); if (value) { OpenSL(decoderMetadata, GetValue, i, valueSize, value); if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_SAMPLERATE) == 0) { SLuint32 sampleRate = *(reinterpret_cast(value->data)); LOGD("sample Rate: %d", sampleRate); *sampleRateOut = sampleRate; } else if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_NUMCHANNELS) == 0) { SLuint32 channels = *(reinterpret_cast(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( (result * 1000) / mSampleRate / mChannels + startPositionMillis_); } int AudioEngine::GetTotalDuration() { android::Mutex::Autolock autoLock(lock_); return static_cast(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(reinterpret_cast(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(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(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, SLuint32 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, 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, 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(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(); } }