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 */ 39 bool eos = false; 40 android::Mutex eosLock; 41 android::Condition eosCondition; 42 43 //----------------------------------------------------------------- 44 //* Exits the application if an error is encountered */ 45 #define CheckErr(x) ExitOnErrorFunc(x,__LINE__) 46 47 void 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 55 bool prefetchError = false; 56 57 //----------------------------------------------------------------- 58 void SignalEos() { 59 android::Mutex::Autolock autoLock(eosLock); 60 eos = true; 61 eosCondition.signal(); 62 } 63 64 //----------------------------------------------------------------- 65 /* PrefetchStatusItf callback for an audio player */ 66 void PrefetchEventCallback( SLPrefetchStatusItf caller, void *pContext __unused, 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 */ 90 void PlayEventCallback( 91 SLPlayItf caller, 92 void *pContext __unused, 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 */ 127 void TestGetPositionUri( SLObjectItf sl, const char* path) 128 { 129 SLEngineItf EngineItf; 130 131 SLresult res; 132 133 SLDataSource audioSource; 134 SLDataLocator_URI uri; 135 SLDataFormat_MIME mime; 136 137 SLDataSink audioSink; 138 SLDataLocator_OutputMix locator_outputmix; 139 140 SLObjectItf player; 141 SLPlayItf playItf; 142 SLVolumeItf volItf; 143 SLPrefetchStatusItf prefetchItf; 144 145 SLObjectItf OutputMix; 146 147 /* variables for the duration and position tests */ 148 SLuint16 counter = 0; 149 SLmillisecond posInMsec = SL_TIME_UNKNOWN; 150 SLmillisecond durationInMsec = SL_TIME_UNKNOWN; 151 152 SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER]; 153 SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER]; 154 155 /* Get the SL Engine Interface which is implicit */ 156 res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); 157 CheckErr(res); 158 159 /* Initialize arrays required[] and iidArray[] */ 160 for (int i=0 ; i < NUM_EXPLICIT_INTERFACES_FOR_PLAYER ; i++) { 161 required[i] = SL_BOOLEAN_FALSE; 162 iidArray[i] = SL_IID_NULL; 163 } 164 165 // Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface 166 required[0] = SL_BOOLEAN_TRUE; 167 iidArray[0] = SL_IID_VOLUME; 168 required[1] = SL_BOOLEAN_TRUE; 169 iidArray[1] = SL_IID_PREFETCHSTATUS; 170 // Create Output Mix object to be used by player 171 res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, 172 iidArray, required); CheckErr(res); 173 174 // Realizing the Output Mix object in synchronous mode. 175 res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); 176 CheckErr(res); 177 178 /* Setup the data source structure for the URI */ 179 uri.locatorType = SL_DATALOCATOR_URI; 180 uri.URI = (SLchar*) path; 181 mime.formatType = SL_DATAFORMAT_MIME; 182 mime.mimeType = (SLchar*)NULL; 183 mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED; 184 185 audioSource.pFormat = (void *)&mime; 186 audioSource.pLocator = (void *)&uri; 187 188 /* Setup the data sink structure */ 189 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 190 locator_outputmix.outputMix = OutputMix; 191 audioSink.pLocator = (void *)&locator_outputmix; 192 audioSink.pFormat = NULL; 193 194 /* Create the audio player */ 195 res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 196 NUM_EXPLICIT_INTERFACES_FOR_PLAYER, iidArray, required); CheckErr(res); 197 198 /* Realizing the player in synchronous mode. */ 199 res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res); 200 fprintf(stdout, "URI example: after Realize\n"); 201 202 /* Get interfaces */ 203 res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); 204 CheckErr(res); 205 206 res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volItf); 207 CheckErr(res); 208 209 res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf); 210 CheckErr(res); 211 res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf); 212 CheckErr(res); 213 res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, 214 SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE); 215 CheckErr(res); 216 217 /* Configure fill level updates every 5 percent */ 218 res = (*prefetchItf)->SetFillUpdatePeriod(prefetchItf, 50); CheckErr(res); 219 220 /* Set up the player callback to get events during the decoding */ 221 res = (*playItf)->SetMarkerPosition(playItf, 2000); 222 CheckErr(res); 223 res = (*playItf)->SetPositionUpdatePeriod(playItf, 500); 224 CheckErr(res); 225 res = (*playItf)->SetCallbackEventsMask(playItf, 226 SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADATEND); 227 CheckErr(res); 228 res = (*playItf)->RegisterCallback(playItf, PlayEventCallback, NULL); 229 CheckErr(res); 230 231 /* Set the player volume */ 232 res = (*volItf)->SetVolumeLevel( volItf, -300); 233 CheckErr(res); 234 235 /* Play the URI */ 236 /* first cause the player to prefetch the data */ 237 fprintf(stdout, "Setting the player to PAUSED to cause it to prefetch the data\n"); 238 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED ); CheckErr(res); 239 240 usleep(100 * 1000); 241 /* wait until there's data to play */ 242 SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; 243 SLuint32 timeOutIndex = 100; // 10s 244 while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) && 245 !prefetchError) { 246 usleep(100 * 1000); 247 (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); 248 timeOutIndex--; 249 } 250 251 if (timeOutIndex == 0 || prefetchError) { 252 fprintf(stderr, "We're done waiting, failed to prefetch data in time, exiting\n"); 253 goto destroyRes; 254 } 255 256 /* Display duration */ 257 res = (*playItf)->GetDuration(playItf, &durationInMsec); CheckErr(res); 258 if (durationInMsec == SL_TIME_UNKNOWN) { 259 fprintf(stderr, "Error: Content duration is unknown after prefetch completed, exiting\n"); 260 goto destroyRes; 261 } else { 262 fprintf(stdout, "Content duration is %u ms (after prefetch completed)\n", durationInMsec); 263 } 264 265 fprintf(stdout, "Setting the player to PLAYING\n"); 266 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); CheckErr(res); 267 268 /* Test GetPosition every second */ 269 while ((counter*1000) < durationInMsec) { 270 counter++; 271 usleep(1 * 1000 * 1000); //1s 272 res = (*playItf)->GetPosition(playItf, &posInMsec); CheckErr(res); 273 if (posInMsec == SL_TIME_UNKNOWN) { 274 fprintf(stderr, "Error: position is SL_TIME_UNKNOWN %ds after start, exiting\n", 275 counter); 276 goto destroyRes; 277 } else { 278 fprintf(stderr, "position is %dms %ds after start\n", posInMsec, counter); 279 } 280 // this test would probably deserve to be improved by relying on drift relative to 281 // a clock, as the operations between two consecutive sleep() are taking time as well 282 // and can add up 283 if (((SLint32)posInMsec > (counter*1000 + TIME_TOLERANCE_MS)) || 284 ((SLint32)posInMsec < (counter*1000 - TIME_TOLERANCE_MS))) { 285 fprintf(stderr, "Error: position drifted too much, exiting\n"); 286 goto destroyRes; 287 } 288 } 289 290 /* Play until the end of file is reached */ 291 { 292 android::Mutex::Autolock autoLock(eosLock); 293 while (!eos) { 294 eosCondition.wait(eosLock); 295 } 296 } 297 fprintf(stdout, "EOS signaled, stopping playback\n"); 298 res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); CheckErr(res); 299 300 destroyRes: 301 302 /* Destroy the player */ 303 fprintf(stdout, "Destroying the player\n"); 304 (*player)->Destroy(player); 305 306 /* Destroy Output Mix object */ 307 (*OutputMix)->Destroy(OutputMix); 308 } 309 310 //----------------------------------------------------------------- 311 int main(int argc, char* const argv[]) 312 { 313 SLresult res; 314 SLObjectItf sl; 315 316 fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf", argv[0]); 317 fprintf(stdout, "and AudioPlayer with SLDataLocator_URI source / OutputMix sink\n"); 318 fprintf(stdout, "Plays a sound and requests position at various times\n\n"); 319 320 if (argc == 1) { 321 fprintf(stdout, "Usage: %s path \n\t%s url\n", argv[0], argv[0]); 322 fprintf(stdout, "Example: \"%s /sdcard/my.mp3\" or \"%s file:///sdcard/my.mp3\"\n", 323 argv[0], argv[0]); 324 exit(EXIT_FAILURE); 325 } 326 327 SLEngineOption EngineOption[] = { 328 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, 329 (SLuint32) SL_BOOLEAN_TRUE}}; 330 331 res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); 332 CheckErr(res); 333 /* Realizing the SL Engine in synchronous mode. */ 334 res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); 335 CheckErr(res); 336 337 TestGetPositionUri(sl, argv[1]); 338 339 /* Shutdown OpenSL ES */ 340 (*sl)->Destroy(sl); 341 342 return EXIT_SUCCESS; 343 } 344