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