From 67537364adc48cd6fa56e36d4201428b5d9dedaf Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Tue, 28 Sep 2010 18:08:55 -0700 Subject: [PATCH] Fix issues with SLPlaybackRateItf interface Set rate was not applied when the AudioTrack is created after prepare completed. Modified playback rate functions in AudioPlayer to pass a CAudioPlayer pointer, rather than a pointer to IPlaybackRate so the rate update function can be queried from the CAudioPlayer. Added test that exercises the SLPlaybackRateItf interface as well as content looping. Simplified evaluation of delay between decode and render to use a fixed (small) delay that balances time to run the event looper, and prevents clicks. Change-Id: I598e9a96e93ef7a7c3603c8467f9d6b36dacd26d --- opensles/libopensles/Android.mk | 2 +- opensles/libopensles/IPlaybackRate.c | 28 ++- opensles/libopensles/android_AudioPlayer.cpp | 28 ++- opensles/libopensles/android_AudioPlayer.h | 6 +- opensles/libopensles/android_SfPlayer.cpp | 17 +- opensles/libopensles/android_SfPlayer.h | 5 + opensles/libopensles/android_prompts.h | 2 + opensles/libopensles/classes.c | 12 +- opensles/libopensles/sles.c | 2 +- opensles/tests/mimeUri/Android.mk | 24 ++ opensles/tests/mimeUri/slesTestSlowDownUri.cpp | 322 +++++++++++++++++++++++++ 11 files changed, 419 insertions(+), 29 deletions(-) create mode 100644 opensles/tests/mimeUri/slesTestSlowDownUri.cpp diff --git a/opensles/libopensles/Android.mk b/opensles/libopensles/Android.mk index 2063b297..ddafe07a 100644 --- a/opensles/libopensles/Android.mk +++ b/opensles/libopensles/Android.mk @@ -65,6 +65,7 @@ LOCAL_SRC_FILES:= \ IMuteSolo.c \ IObject.c \ IPlay.c \ + IPlaybackRate.c \ IPrefetchStatus.c \ IPresetReverb.c \ IRecord.c \ @@ -96,7 +97,6 @@ EXCLUDE_SRC := \ IMetadataTraversal.c \ IOutputMix.c \ IPitch.c \ - IPlaybackRate.c \ IRatePitch.c \ IThreadSync.c \ IVibra.c \ diff --git a/opensles/libopensles/IPlaybackRate.c b/opensles/libopensles/IPlaybackRate.c index c09d2ac2..e13b7a47 100644 --- a/opensles/libopensles/IPlaybackRate.c +++ b/opensles/libopensles/IPlaybackRate.c @@ -28,11 +28,17 @@ static SLresult IPlaybackRate_SetRate(SLPlaybackRateItf self, SLpermille rate) if (!(this->mMinRate <= rate && rate <= this->mMaxRate)) { result = SL_RESULT_PARAMETER_INVALID; } else { - interface_lock_poke(this); + interface_lock_exclusive(this); this->mRate = rate; - interface_unlock_poke(this); + interface_unlock_exclusive(this); #ifdef ANDROID - result = android_audioPlayer_setPlayRate(this, rate); + CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? + (CAudioPlayer *) this->mThis : NULL; + if (NULL != ap) { + result = android_audioPlayer_setPlayRate(ap, rate); + } else { + result = SL_RESULT_PARAMETER_INVALID; + } #else result = SL_RESULT_SUCCESS; #endif @@ -69,7 +75,14 @@ static SLresult IPlaybackRate_SetPropertyConstraints(SLPlaybackRateItf self, SLu this->mProperties = constraints; #ifdef ANDROID // verify property support before storing - result = android_audioPlayer_setPlaybackRateBehavior(this, constraints); + CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? + (CAudioPlayer *) this->mThis : NULL; + if (NULL != ap) { + result = android_audioPlayer_setPlaybackRateBehavior(ap, constraints); + } else { + result = SL_RESULT_PARAMETER_INVALID; + } + #else result = SL_RESULT_SUCCESS; #endif @@ -117,7 +130,11 @@ static SLresult IPlaybackRate_GetCapabilitiesOfRate(SLPlaybackRateItf self, } else { SLuint32 capabilities = 0; #ifdef ANDROID - android_audioPlayer_getCapabilitiesOfRate(this, &capabilities); + CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? + (CAudioPlayer *) this->mThis : NULL; + if (NULL != ap) { + android_audioPlayer_getCapabilitiesOfRate(ap, &capabilities); + } #else capabilities = this->mCapabilities; #endif @@ -169,6 +186,7 @@ static const struct SLPlaybackRateItf_ IPlaybackRate_Itf = { void IPlaybackRate_init(void *self) { + //SL_LOGV("IPlaybackRate_init called"); IPlaybackRate *this = (IPlaybackRate *) self; this->mItf = &IPlaybackRate_Itf; this->mProperties = SL_RATEPROP_NOPITCHCORAUDIO; diff --git a/opensles/libopensles/android_AudioPlayer.cpp b/opensles/libopensles/android_AudioPlayer.cpp index 7a01b818..90a0ce66 100644 --- a/opensles/libopensles/android_AudioPlayer.cpp +++ b/opensles/libopensles/android_AudioPlayer.cpp @@ -207,6 +207,7 @@ void audioTrack_handleUnderrun_lockPlay(CAudioPlayer* ap) { } } + //----------------------------------------------------------------------------- /** * post-condition: play state of AudioPlayer is SL_PLAYSTATE_PAUSED if setPlayStateToPaused is true @@ -383,7 +384,7 @@ static void sfplayer_handlePrefetchEvent(const int event, const int data1, void* } CAudioPlayer *ap = (CAudioPlayer *)user; - SL_LOGV("received event %d, data %d from SfAudioPlayer", event, data1); + //SL_LOGV("received event %d, data %d from SfAudioPlayer", event, data1); switch(event) { case(android::SfPlayer::kEventPrepared): { @@ -401,6 +402,11 @@ static void sfplayer_handlePrefetchEvent(const int event, const int data1, void* ap->mSampleRateMilliHz = android_to_sles_sampleRate(ap->mSfPlayer->getSampleRateHz()); ap->mSfPlayer->startPrefetch_async(); + // update the new track with the current settings + android_audioPlayer_useEventMask(ap); + android_audioPlayer_volumeUpdate(ap); + android_audioPlayer_setPlayRate(ap, ap->mPlaybackRate.mRate); + ap->mAndroidObjState = ANDROID_READY; } @@ -459,7 +465,7 @@ static void sfplayer_handlePrefetchEvent(const int event, const int data1, void* case(android::SfPlayer::kEventEndOfStream): { audioPlayer_dispatch_headAtEnd_lockPlay(ap, true /*set state to paused?*/, true); - if (NULL != ap->mAudioTrack) { + if ((NULL != ap->mAudioTrack) && (!ap->mSeek.mLoopEnabled)) { ap->mAudioTrack->stop(); } } break; @@ -838,6 +844,11 @@ SLresult android_audioPlayer_create( pAudioPlayer->mAndroidEffect.mEffects = new android::KeyedVector(); + // initialize interface-specific fields that can be used regardless of whether the interface + // is exposed on the AudioPlayer or not + pAudioPlayer->mSeek.mLoopEnabled = SL_BOOLEAN_FALSE; + pAudioPlayer->mPlaybackRate.mRate = 1000; + return result; } @@ -1096,16 +1107,15 @@ SLresult android_audioPlayer_destroy(CAudioPlayer *pAudioPlayer) { //----------------------------------------------------------------------------- // called with no lock held -SLresult android_audioPlayer_setPlayRate(IPlaybackRate *pRateItf, SLpermille rate) { +SLresult android_audioPlayer_setPlayRate(CAudioPlayer *ap, SLpermille rate) { SLresult result = SL_RESULT_SUCCESS; - CAudioPlayer *ap = (CAudioPlayer *)pRateItf->mThis; + uint32_t contentRate = 0; switch(ap->mAndroidObjType) { case AUDIOTRACK_PULL: case MEDIAPLAYER: { // get the content sample rate object_lock_peek(ap); - uint32_t contentRate = - sles_to_android_sampleRate(ap->mDataSource.mFormat.mPCM.samplesPerSec); + uint32_t contentRate = sles_to_android_sampleRate(ap->mSampleRateMilliHz); object_unlock_peek(ap); // apply the SL ES playback rate on the AudioTrack as a factor of its content sample rate ap->mpLock->lock(); @@ -1127,10 +1137,9 @@ SLresult android_audioPlayer_setPlayRate(IPlaybackRate *pRateItf, SLpermille rat //----------------------------------------------------------------------------- // called with no lock held -SLresult android_audioPlayer_setPlaybackRateBehavior(IPlaybackRate *pRateItf, +SLresult android_audioPlayer_setPlaybackRateBehavior(CAudioPlayer *ap, SLuint32 constraints) { SLresult result = SL_RESULT_SUCCESS; - CAudioPlayer *ap = (CAudioPlayer *)pRateItf->mThis; switch(ap->mAndroidObjType) { case AUDIOTRACK_PULL: case MEDIAPLAYER: @@ -1149,9 +1158,8 @@ SLresult android_audioPlayer_setPlaybackRateBehavior(IPlaybackRate *pRateItf, //----------------------------------------------------------------------------- // called with no lock held -SLresult android_audioPlayer_getCapabilitiesOfRate(IPlaybackRate *pRateItf, +SLresult android_audioPlayer_getCapabilitiesOfRate(CAudioPlayer *ap, SLuint32 *pCapabilities) { - CAudioPlayer *ap = (CAudioPlayer *)pRateItf->mThis; switch(ap->mAndroidObjType) { case AUDIOTRACK_PULL: case MEDIAPLAYER: diff --git a/opensles/libopensles/android_AudioPlayer.h b/opensles/libopensles/android_AudioPlayer.h index ac86b6e3..ecf04b9b 100644 --- a/opensles/libopensles/android_AudioPlayer.h +++ b/opensles/libopensles/android_AudioPlayer.h @@ -75,12 +75,12 @@ extern SLresult android_audioPlayer_destroy(CAudioPlayer *pAudioPlayer); /************************************************************************************************** * Configuration ****************************/ -extern SLresult android_audioPlayer_setPlayRate(IPlaybackRate *pRateItf, SLpermille rate); +extern SLresult android_audioPlayer_setPlayRate(CAudioPlayer *pAudioPlayer, SLpermille rate); -extern SLresult android_audioPlayer_setPlaybackRateBehavior(IPlaybackRate *pRateItf, +extern SLresult android_audioPlayer_setPlaybackRateBehavior(CAudioPlayer *pAudioPlayer, SLuint32 constraints); -extern SLresult android_audioPlayer_getCapabilitiesOfRate(IPlaybackRate *pRateItf, +extern SLresult android_audioPlayer_getCapabilitiesOfRate(CAudioPlayer *pAudioPlayer, SLuint32 *pCapabilities); extern SLresult android_audioPlayer_getDuration(IPlay *pPlayItf, SLmillisecond *pDurMsec); diff --git a/opensles/libopensles/android_SfPlayer.cpp b/opensles/libopensles/android_SfPlayer.cpp index 94101da3..b1a8cce2 100644 --- a/opensles/libopensles/android_SfPlayer.cpp +++ b/opensles/libopensles/android_SfPlayer.cpp @@ -549,11 +549,22 @@ void SfPlayer::onDecode() { mTimeDelta = ALooper::GetNowUs() - mLastDecodedPositionUs; } - int64_t delayUs = mLastDecodedPositionUs + mTimeDelta - ALooper::GetNowUs(); + int64_t delayUs = mLastDecodedPositionUs + mTimeDelta - ALooper::GetNowUs() + - RENDER_SAFETY_DELAY_US; // negative delays are ignored - msg->post(delayUs); // negative delays are ignored + + if ((NULL != mAudioTrack) && (mAudioTrack->getSampleRate() > mSampleRateHz)) { + // we're speeding up playback, feed data faster + // FIXME not the right formula, delays need to be evaluated differently + delayUs = RENDER_SAFETY_DELAY_US; + //SL_LOGV("delayUs=%lld new", delayUs); + } + + // FIXME clicks can be observed if solely relying on delayUs, this is a safe compromise + msg->post(delayUs > RENDER_SAFETY_DELAY_US ? RENDER_SAFETY_DELAY_US : delayUs); + //msg->post(delayUs); // negative delays are ignored //SL_LOGV("timeUs=%lld, mTimeDelta=%lld, delayUs=%lld", - //mLastDecodedPositionUs, mTimeDelta, delayUs); + // mLastDecodedPositionUs, mTimeDelta, delayUs); } diff --git a/opensles/libopensles/android_SfPlayer.h b/opensles/libopensles/android_SfPlayer.h index 15467b50..9aeef663 100644 --- a/opensles/libopensles/android_SfPlayer.h +++ b/opensles/libopensles/android_SfPlayer.h @@ -36,6 +36,11 @@ #define DURATION_CACHED_MED_US 10000000 // 10s #define DURATION_CACHED_LOW_US 2000000 // 2s +/* + * by how much time data is written to the AudioTrack ahead of the scheduled render time + */ +#define RENDER_SAFETY_DELAY_US 5000 // 5ms + #define SIZE_CACHED_HIGH_BYTES 1000000 #define SIZE_CACHED_MED_BYTES 700000 #define SIZE_CACHED_LOW_BYTES 400000 diff --git a/opensles/libopensles/android_prompts.h b/opensles/libopensles/android_prompts.h index 8dbbd957..71504904 100644 --- a/opensles/libopensles/android_prompts.h +++ b/opensles/libopensles/android_prompts.h @@ -24,6 +24,8 @@ "Cannot set stream type: audio player already realized" #define ERROR_PLAYERREALIZE_UNKNOWN_DATASOURCE_LOCATOR \ "Cannot realize AudioPlayer: with unknown data source locator" +#define ERROR_PLAYER_NEW_NULL_TRACK \ + "Internal error: new AudioTrack shouldn't be NULL" //----------------------------------------------------------------------------- // Android AudioRecorder errors diff --git a/opensles/libopensles/classes.c b/opensles/libopensles/classes.c index 14fc741c..6fb3a87c 100644 --- a/opensles/libopensles/classes.c +++ b/opensles/libopensles/classes.c @@ -78,15 +78,15 @@ static const struct iid_vtable AudioPlayer_interfaces[INTERFACES_AudioPlayer] = // The base Volume interface is explicit, but portions are only for Game and Music profiles {MPH_VOLUME, INTERFACE_EXPLICIT, offsetof(CAudioPlayer, mVolume)}, {MPH_3DMACROSCOPIC, INTERFACE_OPTIONAL, offsetof(CAudioPlayer, m3DMacroscopic)}, - {MPH_BASSBOOST, INTERFACE_DYNAMIC_OPTIONAL, offsetof(CAudioPlayer, mBassBoost)}, + {MPH_BASSBOOST, INTERFACE_DYNAMIC, offsetof(CAudioPlayer, mBassBoost)}, {MPH_DYNAMICSOURCE, INTERFACE_OPTIONAL, offsetof(CAudioPlayer, mDynamicSource)}, - {MPH_ENVIRONMENTALREVERB, INTERFACE_DYNAMIC_OPTIONAL, + {MPH_ENVIRONMENTALREVERB, INTERFACE_DYNAMIC, offsetof(CAudioPlayer, mEnvironmentalReverb)}, - {MPH_EQUALIZER, INTERFACE_DYNAMIC_OPTIONAL, offsetof(CAudioPlayer, mEqualizer)}, + {MPH_EQUALIZER, INTERFACE_DYNAMIC, offsetof(CAudioPlayer, mEqualizer)}, {MPH_PITCH, INTERFACE_DYNAMIC_OPTIONAL, offsetof(CAudioPlayer, mPitch)}, - {MPH_PRESETREVERB, INTERFACE_DYNAMIC_OPTIONAL, offsetof(CAudioPlayer, mPresetReverb)}, - {MPH_PLAYBACKRATE, INTERFACE_DYNAMIC_OPTIONAL, offsetof(CAudioPlayer, mPlaybackRate)}, - {MPH_VIRTUALIZER, INTERFACE_DYNAMIC_OPTIONAL, offsetof(CAudioPlayer, mVirtualizer)}, + {MPH_PRESETREVERB, INTERFACE_DYNAMIC, offsetof(CAudioPlayer, mPresetReverb)}, + {MPH_PLAYBACKRATE, INTERFACE_DYNAMIC, offsetof(CAudioPlayer, mPlaybackRate)}, + {MPH_VIRTUALIZER, INTERFACE_DYNAMIC, offsetof(CAudioPlayer, mVirtualizer)}, {MPH_VISUALIZATION, INTERFACE_OPTIONAL, offsetof(CAudioPlayer, mVisualization)}, #ifdef ANDROID {MPH_ANDROIDEFFECT, INTERFACE_EXPLICIT, offsetof(CAudioPlayer, mAndroidEffect)}, diff --git a/opensles/libopensles/sles.c b/opensles/libopensles/sles.c index 686fe605..3ddb41cc 100644 --- a/opensles/libopensles/sles.c +++ b/opensles/libopensles/sles.c @@ -890,7 +890,7 @@ extern void #define IDynamicSource_init NULL #define IMetadataExtraction_init NULL #define IMetadataTraversal_init NULL -#define IPlaybackRate_init NULL +//#define IPlaybackRate_init NULL #define IVisualization_init NULL #endif diff --git a/opensles/tests/mimeUri/Android.mk b/opensles/tests/mimeUri/Android.mk index f7c6240d..466e25f3 100644 --- a/opensles/tests/mimeUri/Android.mk +++ b/opensles/tests/mimeUri/Android.mk @@ -119,3 +119,27 @@ endif LOCAL_MODULE:= slesTest_playUri2 include $(BUILD_EXECUTABLE) + +# slesTest_slowDownUri + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_C_INCLUDES:= \ + system/media/opensles/include + +LOCAL_SRC_FILES:= \ + slesTestSlowDownUri.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libOpenSLES + +ifeq ($(TARGET_OS),linux) + LOCAL_CFLAGS += -DXP_UNIX +endif + +LOCAL_MODULE:= slesTest_slowDownUri + +include $(BUILD_EXECUTABLE) \ No newline at end of file diff --git a/opensles/tests/mimeUri/slesTestSlowDownUri.cpp b/opensles/tests/mimeUri/slesTestSlowDownUri.cpp new file mode 100644 index 00000000..fe5aa331 --- /dev/null +++ b/opensles/tests/mimeUri/slesTestSlowDownUri.cpp @@ -0,0 +1,322 @@ +/* + * 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. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "slesTestSlowDownUri" + +#ifdef ANDROID +#include +#else +#define LOGV printf +#endif +#include +#include +#include +#include +#include +#include + +#include "SLES/OpenSLES.h" + + +#define MAX_NUMBER_INTERFACES 3 + +#define REPETITIONS 4 // 4 repetitions, but will stop the looping before the end + +#define INITIAL_RATE 2000 // 2x normal playback speed + +//----------------------------------------------------------------- +//* Exits the application if an error is encountered */ +#define CheckErr(x) ExitOnErrorFunc(x,__LINE__) + +void ExitOnErrorFunc( SLresult result , int line) +{ + if (SL_RESULT_SUCCESS != result) { + fprintf(stderr, "%lu error code encountered at line %d, exiting\n", result, line); + exit(1); + } +} + +//----------------------------------------------------------------- +/* PlayItf callback for an audio player */ +void PlayEventCallback( SLPlayItf caller, void *pContext, SLuint32 event) +{ + fprintf(stdout, "PlayEventCallback event = "); + if (event & SL_PLAYEVENT_HEADATEND) { + fprintf(stdout, "SL_PLAYEVENT_HEADATEND \n"); + /* slow playback down by 2x for next loop, if possible */ + SLpermille minRate, maxRate, stepSize, rate = 1000; + SLuint32 capa; + SLPlaybackRateItf* pRateItf = (SLPlaybackRateItf*)pContext; + SLresult res = (**pRateItf)->GetRate(*pRateItf, &rate); CheckErr(res); + res = (**pRateItf)->GetRateRange(*pRateItf, 0, &minRate, &maxRate, &stepSize, &capa); + CheckErr(res); + fprintf(stdout, "old rate = %d, minRate=%d, maxRate=%d\n", rate, minRate, maxRate); + rate /= 2; + if (rate < minRate) { + rate = minRate; + } + fprintf(stdout, "new rate = %d\n", rate); + res = (**pRateItf)->SetRate(*pRateItf, rate); CheckErr(res); + } + if (event & SL_PLAYEVENT_HEADATMARKER) { + fprintf(stdout, "SL_PLAYEVENT_HEADATMARKER "); + } + if (event & SL_PLAYEVENT_HEADATNEWPOS) { + fprintf(stdout, "SL_PLAYEVENT_HEADATNEWPOS "); + } + if (event & SL_PLAYEVENT_HEADMOVING) { + fprintf(stdout, "SL_PLAYEVENT_HEADMOVING "); + } + if (event & SL_PLAYEVENT_HEADSTALLED) { + fprintf(stdout, "SL_PLAYEVENT_HEADSTALLED"); + } + fprintf(stdout, "\n"); +} + +//----------------------------------------------------------------- +/* PrefetchStatusItf callback for an audio player */ +void PrefetchEventCallback( SLPrefetchStatusItf caller, void *pContext, SLuint32 event) +{ + SLpermille level = 0; + (*caller)->GetFillLevel(caller, &level); + SLuint32 status; + //fprintf(stdout, "\t\tPrefetchEventCallback: received event %lu\n", event); + (*caller)->GetPrefetchStatus(caller, &status); + if ((event & (SL_PREFETCHEVENT_STATUSCHANGE|SL_PREFETCHEVENT_FILLLEVELCHANGE)) + && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) { + fprintf(stdout, "\t\tPrefetchEventCallback: Error while prefetching data, exiting\n"); + //exit(1); + } + if (event & SL_PREFETCHEVENT_FILLLEVELCHANGE) { + fprintf(stdout, "\t\tPrefetchEventCallback: Buffer fill level is = %d\n", level); + } + if (event & SL_PREFETCHEVENT_STATUSCHANGE) { + fprintf(stdout, "\t\tPrefetchEventCallback: Prefetch Status is = %lu\n", status); + } + +} + + +//----------------------------------------------------------------- + +/* Play some music from a URI */ +void TestSlowDownUri( SLObjectItf sl, const char* path) +{ + SLEngineItf EngineItf; + + SLint32 numOutputs = 0; + SLuint32 deviceID = 0; + + SLresult res; + + SLDataSource audioSource; + SLDataLocator_URI uri; + SLDataFormat_MIME mime; + + SLDataSink audioSink; + SLDataLocator_OutputMix locator_outputmix; + + SLObjectItf player; + SLPlayItf playItf; + SLSeekItf seekItf; + SLPrefetchStatusItf prefetchItf; + SLPlaybackRateItf rateItf; + + SLObjectItf OutputMix; + + SLboolean required[MAX_NUMBER_INTERFACES]; + SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + + /* Get the SL Engine Interface which is implicit */ + res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); CheckErr(res); + + /* Initialize arrays required[] and iidArray[] */ + for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) { + required[i] = SL_BOOLEAN_FALSE; + iidArray[i] = SL_IID_NULL; + } + + required[0] = SL_BOOLEAN_TRUE; + iidArray[0] = SL_IID_VOLUME; + // Create Output Mix object to be used by player + res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 1, + iidArray, required); CheckErr(res); + + // Realizing the Output Mix object in synchronous mode. + res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); CheckErr(res); + + /* Setup the data source structure for the URI */ + uri.locatorType = SL_DATALOCATOR_URI; + uri.URI = (SLchar*) path; + mime.formatType = SL_DATAFORMAT_MIME; + mime.mimeType = (SLchar*)NULL; + mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED; + + audioSource.pFormat = (void *)&mime; + audioSource.pLocator = (void *)&uri; + + /* Setup the data sink structure */ + locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + locator_outputmix.outputMix = OutputMix; + audioSink.pLocator = (void *)&locator_outputmix; + audioSink.pFormat = NULL; + + /******************************************************/ + /* Create the audio player */ + required[0] = SL_BOOLEAN_TRUE; + iidArray[0] = SL_IID_SEEK; + required[1] = SL_BOOLEAN_TRUE; + iidArray[1] = SL_IID_PREFETCHSTATUS; + required[2] = SL_BOOLEAN_TRUE; + iidArray[2] = SL_IID_PLAYBACKRATE; + res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, + MAX_NUMBER_INTERFACES, iidArray, required); CheckErr(res); + + /* Realizing the player in synchronous mode. */ + res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res); + fprintf(stdout, "URI example: after Realize\n"); + + /* Get interfaces */ + res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); CheckErr(res); + + res = (*player)->GetInterface(player, SL_IID_SEEK, (void*)&seekItf); CheckErr(res); + + res = (*player)->GetInterface(player, SL_IID_PLAYBACKRATE, (void*)&rateItf); CheckErr(res); + + res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf); + CheckErr(res); + res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf); + CheckErr(res); + res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, + SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE); CheckErr(res); + + /* Configure fill level updates every 5 percent */ + (*prefetchItf)->SetFillUpdatePeriod(prefetchItf, 50); CheckErr(res); + + /* Display duration */ + SLmillisecond durationInMsec = SL_TIME_UNKNOWN; + res = (*playItf)->GetDuration(playItf, &durationInMsec); CheckErr(res); + if (durationInMsec == SL_TIME_UNKNOWN) { + fprintf(stdout, "Content duration is unknown (before starting to prefetch)\n"); + } else { + fprintf(stdout, "Content duration is %lu ms (before starting to prefetch)\n", + durationInMsec); + } + + /* Loop on the whole of the content */ + res = (*seekItf)->SetLoop(seekItf, SL_BOOLEAN_TRUE, 0, SL_TIME_UNKNOWN); CheckErr(res); + + /* Set up marker and position callbacks */ + res = (*playItf)->RegisterCallback(playItf, PlayEventCallback, &rateItf); CheckErr(res); + res = (*playItf)->SetCallbackEventsMask(playItf, + SL_PLAYEVENT_HEADATEND | SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS); + res = (*playItf)->SetMarkerPosition(playItf, 1500); CheckErr(res); + res = (*playItf)->SetPositionUpdatePeriod(playItf, 500); CheckErr(res); + + /* Change the playback rate before playback */ + res = (*rateItf)->SetRate(rateItf, INITIAL_RATE); CheckErr(res); + + /******************************************************/ + /* Play the URI */ + /* first cause the player to prefetch the data */ + res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED ); CheckErr(res); + + /* wait until there's data to play */ + SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; + SLuint32 timeOutIndex = 100; // 10s + while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0)) { + usleep(100 * 1000); + (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); + timeOutIndex--; + } + + if (timeOutIndex == 0) { + fprintf(stderr, "\nWe\'re done waiting, failed to prefetch data in time, exiting\n"); + goto destroyRes; + } + + /* Display duration again, */ + res = (*playItf)->GetDuration(playItf, &durationInMsec); CheckErr(res); + if (durationInMsec == SL_TIME_UNKNOWN) { + fprintf(stdout, "Content duration is unknown (after prefetch completed)\n"); + } else { + fprintf(stdout, "Content duration is %lu ms (after prefetch completed)\n", durationInMsec); + } + + fprintf(stdout, "starting to play\n"); + res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); CheckErr(res); + + /* Wait as long as the duration of the content, times the repetitions, + * before stopping the loop */ + usleep( (REPETITIONS-1) * durationInMsec * 1100); + res = (*seekItf)->SetLoop(seekItf, SL_BOOLEAN_FALSE, 0, SL_TIME_UNKNOWN); CheckErr(res); + fprintf(stdout, "As of now, stopped looping (sound shouldn't repeat from now on)\n"); + /* wait some more to make sure it doesn't repeat */ + usleep(durationInMsec * 1000); + + /* Stop playback */ + fprintf(stdout, "stopping playback\n"); + res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); CheckErr(res); + +destroyRes: + + /* Destroy the player */ + (*player)->Destroy(player); + + /* Destroy Output Mix object */ + (*OutputMix)->Destroy(OutputMix); +} + +//----------------------------------------------------------------- +int main(int argc, char* const argv[]) +{ + LOGV("Starting slesTestSlowDownUri\n"); + + SLresult res; + SLObjectItf sl; + + fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf, SLSeekItf, SLPlaybackRateItf\n", + argv[0]); + fprintf(stdout, "and AudioPlayer with SLDataLocator_URI source / OutputMix sink\n"); + fprintf(stdout, "Plays a sound and loops it %d times while changing the \n", REPETITIONS); + fprintf(stdout, "playback rate each time.\n\n"); + + if (argc == 1) { + fprintf(stdout, "Usage: \n\t%s path \n\t%s url\n", argv[0], argv[0]); + fprintf(stdout, "Example: \"%s /sdcard/my.mp3\" or \"%s file:///sdcard/my.mp3\"\n", + argv[0], argv[0]); + exit(1); + } + + SLEngineOption EngineOption[] = { + {(SLuint32) SL_ENGINEOPTION_THREADSAFE, + (SLuint32) SL_BOOLEAN_TRUE}}; + + res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); + CheckErr(res); + /* Realizing the SL Engine in synchronous mode. */ + res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); + CheckErr(res); + + TestSlowDownUri(sl, argv[1]); + + /* Shutdown OpenSL ES */ + (*sl)->Destroy(sl); + exit(0); + + return 0; +} -- 2.11.0