OSDN Git Service

Fix issues with SLPlaybackRateItf interface
authorJean-Michel Trivi <jmtrivi@google.com>
Wed, 29 Sep 2010 01:08:55 +0000 (18:08 -0700)
committerJean-Michel Trivi <jmtrivi@google.com>
Wed, 29 Sep 2010 16:37:30 +0000 (09:37 -0700)
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
opensles/libopensles/IPlaybackRate.c
opensles/libopensles/android_AudioPlayer.cpp
opensles/libopensles/android_AudioPlayer.h
opensles/libopensles/android_SfPlayer.cpp
opensles/libopensles/android_SfPlayer.h
opensles/libopensles/android_prompts.h
opensles/libopensles/classes.c
opensles/libopensles/sles.c
opensles/tests/mimeUri/Android.mk
opensles/tests/mimeUri/slesTestSlowDownUri.cpp [new file with mode: 0644]

index 2063b29..ddafe07 100644 (file)
@@ -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                      \
index c09d2ac..e13b7a4 100644 (file)
@@ -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;
index 7a01b81..90a0ce6 100644 (file)
@@ -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<SLuint32, android::AudioEffect* >();
 
+    // 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:
index ac86b6e..ecf04b9 100644 (file)
@@ -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);
index 94101da..b1a8cce 100644 (file)
@@ -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);
 }
 
 
index 15467b5..9aeef66 100644 (file)
 #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
index 8dbbd95..7150490 100644 (file)
@@ -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
index 14fc741..6fb3a87 100644 (file)
@@ -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)},
index 686fe60..3ddb41c 100644 (file)
@@ -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
 
index f7c6240..466e25f 100644 (file)
@@ -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 (file)
index 0000000..fe5aa33
--- /dev/null
@@ -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 <utils/Log.h>
+#else
+#define LOGV printf
+#endif
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#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;
+}