1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdlib.h> 18#include <stdio.h> 19#include <unistd.h> 20#include <utils/threads.h> 21 22#include <SLES/OpenSLES.h> 23 24/* tolerance in ms for this test in time difference between reported position and time since 25 * playback was requested to start. This is reasonable for a local file. 26 */ 27#define TIME_TOLERANCE_MS 600 28 29/* explicitly requesting SL_IID_VOLUME and SL_IID_PREFETCHSTATUS 30 * on the AudioPlayer object */ 31#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 2 32 33/* used to detect errors likely to have occured when the OpenSL ES framework fails to open 34 * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond. */ 35#define PREFETCHEVENT_ERROR_CANDIDATE \ 36 (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE) 37 38/* to signal to the test app the end of the stream to decode has been reached */ 39bool eos = false; 40android::Mutex eosLock; 41android::Condition eosCondition; 42 43//----------------------------------------------------------------- 44//* Exits the application if an error is encountered */ 45#define CheckErr(x) ExitOnErrorFunc(x,__LINE__) 46 47void ExitOnErrorFunc( SLresult result , int line) 48{ 49 if (SL_RESULT_SUCCESS != result) { 50 fprintf(stderr, "%u error code encountered at line %d, exiting\n", result, line); 51 exit(EXIT_FAILURE); 52 } 53} 54 55bool prefetchError = false; 56 57//----------------------------------------------------------------- 58void SignalEos() { 59 android::Mutex::Autolock autoLock(eosLock); 60 eos = true; 61 eosCondition.signal(); 62} 63 64//----------------------------------------------------------------- 65/* PrefetchStatusItf callback for an audio player */ 66void PrefetchEventCallback( SLPrefetchStatusItf caller, void *pContext, SLuint32 event) 67{ 68 SLpermille level = 0; 69 SLresult res = (*caller)->GetFillLevel(caller, &level); CheckErr(res); 70 SLuint32 status; 71 //fprintf(stdout, "PrefetchEventCallback: received event %u\n", event); 72 res = (*caller)->GetPrefetchStatus(caller, &status); CheckErr(res); 73 if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE)) 74 && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) { 75 fprintf(stdout, "PrefetchEventCallback: Error while prefetching data, exiting\n"); 76 prefetchError = true; 77 return; 78 } 79 if (event & SL_PREFETCHEVENT_FILLLEVELCHANGE) { 80 fprintf(stdout, "PrefetchEventCallback: Buffer fill level is = %d\n", level); 81 } 82 if (event & SL_PREFETCHEVENT_STATUSCHANGE) { 83 fprintf(stdout, "PrefetchEventCallback: Prefetch Status is = %u\n", status); 84 } 85} 86 87 88//----------------------------------------------------------------- 89/* PlayItf callback for playback events */ 90void PlayEventCallback( 91 SLPlayItf caller, 92 void *pContext, 93 SLuint32 event) 94{ 95 SLmillisecond posMsec = SL_TIME_UNKNOWN; 96 SLresult res; 97 if (SL_PLAYEVENT_HEADATEND & event) { 98 fprintf(stdout, "SL_PLAYEVENT_HEADATEND reached\n"); 99#if 0 100 res = (*caller)->GetPosition(caller, &posMsec); CheckErr(res); 101 fprintf(stdout, "after getPosition in SL_PLAYEVENT_HEADATEND handler\n"); 102 if (posMsec == SL_TIME_UNKNOWN) { 103 fprintf(stderr, "Error: position is SL_TIME_UNKNOWN at SL_PLAYEVENT_HEADATEND\n"); 104 } else { 105 fprintf(stdout, "position is %d at SL_PLAYEVENT_HEADATEND\n", posMsec); 106 } 107 // FIXME compare position against duration 108#endif 109 SignalEos(); 110 } 111 112 if (SL_PLAYEVENT_HEADATNEWPOS & event) { 113 res = (*caller)->GetPosition(caller, &posMsec); CheckErr(res); 114 fprintf(stdout, "SL_PLAYEVENT_HEADATNEWPOS current position=%ums\n", posMsec); 115 } 116 117 if (SL_PLAYEVENT_HEADATMARKER & event) { 118 res = (*caller)->GetPosition(caller, &posMsec); CheckErr(res); 119 fprintf(stdout, "SL_PLAYEVENT_HEADATMARKER current position=%ums\n", posMsec); 120 } 121} 122 123 124//----------------------------------------------------------------- 125 126/* Play some audio from a URI and regularly query the position */ 127void TestGetPositionUri( SLObjectItf sl, const char* path) 128{ 129 SLEngineItf EngineItf; 130 131 SLint32 numOutputs = 0; 132 SLuint32 deviceID = 0; 133 134 SLresult res; 135 136 SLDataSource audioSource; 137 SLDataLocator_URI uri; 138 SLDataFormat_MIME mime; 139 140 SLDataSink audioSink; 141 SLDataLocator_OutputMix locator_outputmix; 142 143 SLObjectItf player; 144 SLPlayItf playItf; 145 SLVolumeItf volItf; 146 SLPrefetchStatusItf prefetchItf; 147 148 SLObjectItf OutputMix; 149 150 /* variables for the duration and position tests */ 151 SLuint16 counter = 0; 152 SLmillisecond posInMsec = SL_TIME_UNKNOWN; 153 SLmillisecond durationInMsec = SL_TIME_UNKNOWN; 154 155 SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER]; 156 SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER]; 157 158 /* Get the SL Engine Interface which is implicit */ 159 res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); 160 CheckErr(res); 161 162 /* Initialize arrays required[] and iidArray[] */ 163 for (int i=0 ; i < NUM_EXPLICIT_INTERFACES_FOR_PLAYER ; i++) { 164 required[i] = SL_BOOLEAN_FALSE; 165 iidArray[i] = SL_IID_NULL; 166 } 167 168 // Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface 169 required[0] = SL_BOOLEAN_TRUE; 170 iidArray[0] = SL_IID_VOLUME; 171 required[1] = SL_BOOLEAN_TRUE; 172 iidArray[1] = SL_IID_PREFETCHSTATUS; 173 // Create Output Mix object to be used by player 174 res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, 175 iidArray, required); CheckErr(res); 176 177 // Realizing the Output Mix object in synchronous mode. 178 res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); 179 CheckErr(res); 180 181 /* Setup the data source structure for the URI */ 182 uri.locatorType = SL_DATALOCATOR_URI; 183 uri.URI = (SLchar*) path; 184 mime.formatType = SL_DATAFORMAT_MIME; 185 mime.mimeType = (SLchar*)NULL; 186 mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED; 187 188 audioSource.pFormat = (void *)&mime; 189 audioSource.pLocator = (void *)&uri; 190 191 /* Setup the data sink structure */ 192 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 193 locator_outputmix.outputMix = OutputMix; 194 audioSink.pLocator = (void *)&locator_outputmix; 195 audioSink.pFormat = NULL; 196 197 /* Create the audio player */ 198 res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 199 NUM_EXPLICIT_INTERFACES_FOR_PLAYER, iidArray, required); CheckErr(res); 200 201 /* Realizing the player in synchronous mode. */ 202 res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res); 203 fprintf(stdout, "URI example: after Realize\n"); 204 205 /* Get interfaces */ 206 res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); 207 CheckErr(res); 208 209 res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volItf); 210 CheckErr(res); 211 212 res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf); 213 CheckErr(res); 214 res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf); 215 CheckErr(res); 216 res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, 217 SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE); 218 CheckErr(res); 219 220 /* Configure fill level updates every 5 percent */ 221 res = (*prefetchItf)->SetFillUpdatePeriod(prefetchItf, 50); CheckErr(res); 222 223 /* Set up the player callback to get events during the decoding */ 224 res = (*playItf)->SetMarkerPosition(playItf, 2000); 225 CheckErr(res); 226 res = (*playItf)->SetPositionUpdatePeriod(playItf, 500); 227 CheckErr(res); 228 res = (*playItf)->SetCallbackEventsMask(playItf, 229 SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADATEND); 230 CheckErr(res); 231 res = (*playItf)->RegisterCallback(playItf, PlayEventCallback, NULL); 232 CheckErr(res); 233 234 /* Set the player volume */ 235 res = (*volItf)->SetVolumeLevel( volItf, -300); 236 CheckErr(res); 237 238 /* Play the URI */ 239 /* first cause the player to prefetch the data */ 240 fprintf(stdout, "Setting the player to PAUSED to cause it to prefetch the data\n"); 241 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED ); CheckErr(res); 242 243 usleep(100 * 1000); 244 /* wait until there's data to play */ 245 SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; 246 SLuint32 timeOutIndex = 100; // 10s 247 while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) && 248 !prefetchError) { 249 usleep(100 * 1000); 250 (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); 251 timeOutIndex--; 252 } 253 254 if (timeOutIndex == 0 || prefetchError) { 255 fprintf(stderr, "We're done waiting, failed to prefetch data in time, exiting\n"); 256 goto destroyRes; 257 } 258 259 /* Display duration */ 260 res = (*playItf)->GetDuration(playItf, &durationInMsec); CheckErr(res); 261 if (durationInMsec == SL_TIME_UNKNOWN) { 262 fprintf(stderr, "Error: Content duration is unknown after prefetch completed, exiting\n"); 263 goto destroyRes; 264 } else { 265 fprintf(stdout, "Content duration is %u ms (after prefetch completed)\n", durationInMsec); 266 } 267 268 fprintf(stdout, "Setting the player to PLAYING\n"); 269 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); CheckErr(res); 270 271 /* Test GetPosition every second */ 272 while ((counter*1000) < durationInMsec) { 273 counter++; 274 usleep(1 * 1000 * 1000); //1s 275 res = (*playItf)->GetPosition(playItf, &posInMsec); CheckErr(res); 276 if (posInMsec == SL_TIME_UNKNOWN) { 277 fprintf(stderr, "Error: position is SL_TIME_UNKNOWN %ds after start, exiting\n", 278 counter); 279 goto destroyRes; 280 } else { 281 fprintf(stderr, "position is %dms %ds after start\n", posInMsec, counter); 282 } 283 // this test would probably deserve to be improved by relying on drift relative to 284 // a clock, as the operations between two consecutive sleep() are taking time as well 285 // and can add up 286 if (((SLint32)posInMsec > (counter*1000 + TIME_TOLERANCE_MS)) || 287 ((SLint32)posInMsec < (counter*1000 - TIME_TOLERANCE_MS))) { 288 fprintf(stderr, "Error: position drifted too much, exiting\n"); 289 goto destroyRes; 290 } 291 } 292 293 /* Play until the end of file is reached */ 294 { 295 android::Mutex::Autolock autoLock(eosLock); 296 while (!eos) { 297 eosCondition.wait(eosLock); 298 } 299 } 300 fprintf(stdout, "EOS signaled, stopping playback\n"); 301 res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); CheckErr(res); 302 303destroyRes: 304 305 /* Destroy the player */ 306 fprintf(stdout, "Destroying the player\n"); 307 (*player)->Destroy(player); 308 309 /* Destroy Output Mix object */ 310 (*OutputMix)->Destroy(OutputMix); 311} 312 313//----------------------------------------------------------------- 314int main(int argc, char* const argv[]) 315{ 316 SLresult res; 317 SLObjectItf sl; 318 319 fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf", argv[0]); 320 fprintf(stdout, "and AudioPlayer with SLDataLocator_URI source / OutputMix sink\n"); 321 fprintf(stdout, "Plays a sound and requests position at various times\n\n"); 322 323 if (argc == 1) { 324 fprintf(stdout, "Usage: %s path \n\t%s url\n", argv[0], argv[0]); 325 fprintf(stdout, "Example: \"%s /sdcard/my.mp3\" or \"%s file:///sdcard/my.mp3\"\n", 326 argv[0], argv[0]); 327 exit(EXIT_FAILURE); 328 } 329 330 SLEngineOption EngineOption[] = { 331 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, 332 (SLuint32) SL_BOOLEAN_TRUE}}; 333 334 res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); 335 CheckErr(res); 336 /* Realizing the SL Engine in synchronous mode. */ 337 res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); 338 CheckErr(res); 339 340 TestGetPositionUri(sl, argv[1]); 341 342 /* Shutdown OpenSL ES */ 343 (*sl)->Destroy(sl); 344 345 return EXIT_SUCCESS; 346} 347