1 /* 2 * Copyright (C) 2010 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 18 #include <stdlib.h> 19 #include <stdio.h> 20 //#include <string.h> 21 #include <unistd.h> 22 //#include <sys/time.h> 23 24 #include <SLES/OpenSLES.h> 25 #include <SLES/OpenSLES_Android.h> 26 27 //#define TEST_DISPLAY_FIRST_BUFFER_ITEM 28 //#define TEST_CLEAR 29 //#define TEST_PTS 30 31 #define MAX_NUMBER_INTERFACES 2 32 33 #define PREFETCHEVENT_ERROR_CANDIDATE \ 34 (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE) 35 36 #define NB_BUFFERS 16 37 #define MPEG2_TS_BLOCK_SIZE 188 38 #define BUFFER_SIZE (20*MPEG2_TS_BLOCK_SIZE) 39 #define DISCONTINUITY_MAGIC 1977 40 41 /* Where we store the data to play */ 42 char dataCache[BUFFER_SIZE * NB_BUFFERS]; 43 /* From where we read the data to play */ 44 FILE *file; 45 /* Has the app reached the end of the file */ 46 bool reachedEof = false; 47 /* Special discontinuity buffer context */ 48 int myDiscBufferContext = DISCONTINUITY_MAGIC; 49 50 /* structure to store my discontinuity with PTS command */ 51 typedef struct { 52 SLuint32 discKey; // identifies the item 53 SLuint32 discSize; 54 SLAuint64 discPts; 55 } DiscPts; 56 57 //----------------------------------------------------------------- 58 /* Exits the application if an error is encountered */ 59 #define CheckErr(x) ExitOnErrorFunc(x,__LINE__) 60 61 void ExitOnErrorFunc( SLresult result , int line) 62 { 63 if (SL_RESULT_SUCCESS != result) { 64 fprintf(stderr, "%u error code encountered at line %d, exiting\n", result, line); 65 exit(EXIT_FAILURE); 66 } 67 } 68 69 bool prefetchError = false; 70 71 //----------------------------------------------------------------- 72 /* AndroidBufferQueueItf callback for an audio player */ 73 SLresult AndroidBufferQueueCallback( 74 SLAndroidBufferQueueItf caller, 75 void *pCallbackContext __unused, /* input */ 76 void *pBufferContext, /* input */ 77 void *pBufferData __unused, /* input */ 78 SLuint32 dataSize __unused, /* input */ 79 SLuint32 dataUsed __unused, /* input */ 80 const SLAndroidBufferItem *pItems __unused, /* input */ 81 SLuint32 itemsLength __unused /* input */) 82 { 83 // assert(BUFFER_SIZE <= dataSize); 84 85 //-------------------------------------------------------------------------------- 86 // this section is for testing only, this is NOT an example of how to use the API 87 // to play a .ts file, but rather shows more ways to exercise the API 88 //-------------------------------------------------------------------------------- 89 SLAndroidBufferQueueState state; 90 (*caller)->GetState(caller, &state); 91 //fprintf(stdout, "ABQ state count=%lu, index=%lu\n", state.count, state.index); 92 93 // just to test, clear the queue to see what happens 94 if (state.index == 500) { 95 #ifdef TEST_CLEAR 96 (*caller)->Clear(caller); 97 // we've cleared the queue, and have introduced a discontinuity, so signal it 98 SLAndroidBufferItem msgDiscontinuity; 99 msgDiscontinuity.itemKey = SL_ANDROID_ITEMKEY_DISCONTINUITY; 100 msgDiscontinuity.itemSize = 0; 101 // message has no parameters, so the total size of the message is the size of the key 102 // plus the size if itemSize, both SLuint32 103 (*caller)->Enqueue(caller, (void*)&myDiscBufferContext /*pBufferContext*/, 104 NULL /*pData*/, 0 /*dataLength*/, 105 &msgDiscontinuity /*pMsg*/, 106 sizeof(SLuint32)*2 /*msgLength*/); 107 // we've cleared the queue, it's now empty: let's rebuffer a bit so playback doesn't starve 108 size_t nbRead = fread((void*)dataCache, 1, BUFFER_SIZE*(NB_BUFFERS/2), file); 109 if (nbRead == BUFFER_SIZE*(NB_BUFFERS/2)) { 110 for (int i=0 ; i < NB_BUFFERS/2 ; i++) { 111 SLresult res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/, 112 dataCache + i*BUFFER_SIZE, 113 BUFFER_SIZE, NULL, 0); 114 CheckErr(res); 115 } 116 } 117 #endif 118 #ifdef TEST_PTS 119 DiscPts msgDiscontinuity = { SL_ANDROID_ITEMKEY_DISCONTINUITY, 120 sizeof(SLAuint64), 15*90*1000 /*15s in 90kHz clock */ }; 121 // enqueue discontinuity message with our PTS 122 (*caller)->Enqueue(caller, (void*)&myDiscBufferContext /*pBufferContext*/, 123 NULL /*pData*/, 0 /*dataLength*/, 124 (SLAndroidBufferItem*)&msgDiscontinuity, 125 sizeof(DiscPts) ); 126 fprintf(stdout, "rendering will resume at 15s mark"); 127 128 #endif 129 return SL_RESULT_SUCCESS; 130 } 131 //-------------------------------------------------------------------------------- 132 // end of test only section 133 //-------------------------------------------------------------------------------- 134 else { 135 136 #ifdef TEST_DISPLAY_FIRST_BUFFER_ITEM 137 // display item data (only parsing first item) 138 if (itemsLength !=0) { 139 fprintf(stdout, "item key=0x%lx size=%lu data=0x%lx\n", 140 pItems->itemKey, pItems->itemSize, *((SLuint32*)&pItems->itemData)); 141 } 142 #endif 143 144 // pBufferData can be null if the last consumed buffer contained only a command 145 // just like we do for signalling DISCONTINUITY (above) or EOS (below) 146 if ((pBufferContext != NULL) && (*((int*)pBufferContext) == DISCONTINUITY_MAGIC)) { 147 fprintf(stdout, "Successfully detected my discontinuity buffer having been consumed\n"); 148 } 149 if (pBufferData != NULL) { 150 size_t nbRead = fread((void*)pBufferData, 1, BUFFER_SIZE, file); 151 if (nbRead > 0) { 152 (*caller)->Enqueue(caller, NULL /*pBufferContext*/, 153 pBufferData /*pData*/, 154 nbRead /*dataLength*/, 155 NULL /*pMsg*/, 156 0 /*msgLength*/); 157 } else if (!reachedEof) { 158 // signal EOS 159 SLAndroidBufferItem msgEos; 160 msgEos.itemKey = SL_ANDROID_ITEMKEY_EOS; 161 msgEos.itemSize = 0; 162 // EOS message has no parameters, so the total size of the message is the size of 163 // the key plus the size if itemSize, both SLuint32 164 (*caller)->Enqueue(caller, NULL /*pBufferContext*/, 165 NULL /*pData*/, 0 /*dataLength*/, 166 &msgEos /*pMsg*/, 167 sizeof(SLuint32)*2 /*msgLength*/); 168 reachedEof = true; 169 } 170 } 171 172 return SL_RESULT_SUCCESS; 173 } 174 } 175 176 177 //----------------------------------------------------------------- 178 179 /* Play some music from a URI */ 180 void TestPlayStream( SLObjectItf sl, const char* path) 181 { 182 SLEngineItf EngineItf; 183 184 SLint32 numOutputs = 0; 185 SLuint32 deviceID = 0; 186 187 SLresult res; 188 189 SLDataSource audioSource; 190 SLDataLocator_AndroidBufferQueue streamLocator; 191 SLDataFormat_MIME mime; 192 193 SLDataSink audioSink; 194 SLDataLocator_OutputMix locator_outputmix; 195 196 SLObjectItf player; 197 SLPlayItf playItf; 198 SLVolumeItf volItf; 199 SLAndroidBufferQueueItf abqItf; 200 201 SLObjectItf OutputMix; 202 203 SLboolean required[MAX_NUMBER_INTERFACES]; 204 SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; 205 206 int playTimeInSec = 60; 207 208 file = fopen(path, "rb"); 209 210 /* Get the SL Engine Interface which is implicit */ 211 res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); 212 CheckErr(res); 213 214 /* Initialize arrays required[] and iidArray[] */ 215 for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) { 216 required[i] = SL_BOOLEAN_FALSE; 217 iidArray[i] = SL_IID_NULL; 218 } 219 220 // Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface 221 required[0] = SL_BOOLEAN_TRUE; 222 iidArray[0] = SL_IID_VOLUME; 223 required[1] = SL_BOOLEAN_TRUE; 224 iidArray[1] = SL_IID_ANDROIDBUFFERQUEUESOURCE; 225 // Create Output Mix object to be used by player 226 res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, 227 iidArray, required); CheckErr(res); 228 229 // Realizing the Output Mix object in synchronous mode. 230 res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); 231 CheckErr(res); 232 233 /* Setup the data source structure for the URI */ 234 streamLocator.locatorType = SL_DATALOCATOR_ANDROIDBUFFERQUEUE; 235 streamLocator.numBuffers = NB_BUFFERS; 236 mime.formatType = SL_DATAFORMAT_MIME; 237 mime.mimeType = (SLchar *) "video/mp2ts";//(SLchar*)NULL; 238 mime.containerType = SL_CONTAINERTYPE_MPEG_TS; 239 240 audioSource.pFormat = (void *)&mime; 241 audioSource.pLocator = (void *)&streamLocator; 242 243 /* Setup the data sink structure */ 244 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 245 locator_outputmix.outputMix = OutputMix; 246 audioSink.pLocator = (void *)&locator_outputmix; 247 audioSink.pFormat = NULL; 248 249 /* Create the audio player */ 250 res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 251 MAX_NUMBER_INTERFACES, iidArray, required); CheckErr(res); 252 253 /* Realizing the player in synchronous mode. */ 254 res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res); 255 fprintf(stdout, "URI example: after Realize\n"); 256 257 /* Get interfaces */ 258 res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); CheckErr(res); 259 260 res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volItf); CheckErr(res); 261 262 res = (*player)->GetInterface(player, SL_IID_ANDROIDBUFFERQUEUESOURCE, (void*)&abqItf); 263 CheckErr(res); 264 265 res = (*abqItf)->RegisterCallback(abqItf, AndroidBufferQueueCallback, 266 // context is not used in the example, but can be used to track who registered 267 // the buffer queue callback 268 NULL /*pContext*/); CheckErr(res); 269 270 res = (*abqItf)->SetCallbackEventsMask(abqItf, SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED); 271 CheckErr(res); 272 273 /* Display duration */ 274 SLmillisecond durationInMsec = SL_TIME_UNKNOWN; 275 res = (*playItf)->GetDuration(playItf, &durationInMsec); 276 CheckErr(res); 277 if (durationInMsec == SL_TIME_UNKNOWN) { 278 fprintf(stdout, "Content duration is unknown (before starting to prefetch)\n"); 279 } else { 280 fprintf(stdout, "Content duration is %u ms (before starting to prefetch)\n", 281 durationInMsec); 282 } 283 284 /* Set the player volume */ 285 res = (*volItf)->SetVolumeLevel( volItf, 0);//-300); 286 CheckErr(res); 287 288 289 /* Play the URI */ 290 /* first cause the player to prefetch the data */ 291 fprintf(stdout, "Before set to PAUSED\n"); 292 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED ); 293 fprintf(stdout, "After set to PAUSED\n"); 294 CheckErr(res); 295 296 /* Fill our cache */ 297 if (fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file) <= 0) { 298 fprintf(stderr, "Error filling cache, exiting\n"); 299 goto destroyRes; 300 } 301 /* Enqueue the content of our cache before starting to play, 302 * we don't want to starve the player */ 303 for (int i=0 ; i < NB_BUFFERS ; i++) { 304 res = (*abqItf)->Enqueue(abqItf, NULL /*pBufferContext*/, 305 dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0); 306 CheckErr(res); 307 } 308 309 #if 0 // used to test ABQ starving where only one buffer is enqueued before playback 310 /* Fill our cache */ 311 if (fread(dataCache, 1, BUFFER_SIZE * 1, file) <= 0) { 312 fprintf(stderr, "Error filling cache, exiting\n"); 313 goto destroyRes; 314 } 315 /* Enqueue the content of our cache before starting to play, 316 * we don't want to starve the player */ 317 for (int i=0 ; i < 1 ; i++) { 318 res = (*abqItf)->Enqueue(abqItf, dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0); 319 CheckErr(res); 320 } 321 #endif 322 /* wait until there's data to play */ 323 //SLpermille fillLevel = 0; 324 /* 325 SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; 326 SLuint32 timeOutIndex = 2; 327 while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) && 328 !prefetchError) { 329 usleep(1 * 1000 * 1000); // 1s 330 //(*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); 331 timeOutIndex--; 332 } 333 334 if (timeOutIndex == 0 || prefetchError) { 335 fprintf(stderr, "We\'re done waiting, failed to prefetch data in time, exiting\n"); 336 goto destroyRes; 337 } 338 */ 339 340 /* Display duration again, */ 341 /* res = (*playItf)->GetDuration(playItf, &durationInMsec); 342 CheckErr(res); 343 if (durationInMsec == SL_TIME_UNKNOWN) { 344 fprintf(stdout, "Content duration is unknown (after prefetch completed)\n"); 345 } else { 346 fprintf(stdout, "Content duration is %lu ms (after prefetch completed)\n", durationInMsec); 347 } 348 */ 349 350 fprintf(stdout, "URI example: starting to play\n"); 351 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); 352 CheckErr(res); 353 354 /* Wait as long as the duration of the content before stopping */ 355 fprintf(stdout, "Letting playback go on for %d sec\n", playTimeInSec); 356 usleep(playTimeInSec /*s*/ * 1000 * 1000); 357 358 359 /* Make sure player is stopped */ 360 fprintf(stdout, "URI example: stopping playback\n"); 361 res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); 362 CheckErr(res); 363 364 fprintf(stdout, "sleeping to verify playback stopped\n"); 365 usleep(2 /*s*/ * 1000 * 1000); 366 367 destroyRes: 368 369 /* Destroy the player */ 370 (*player)->Destroy(player); 371 372 /* Destroy Output Mix object */ 373 (*OutputMix)->Destroy(OutputMix); 374 375 fclose(file); 376 } 377 378 //----------------------------------------------------------------- 379 int main(int argc, char* const argv[]) 380 { 381 SLresult res; 382 SLObjectItf sl; 383 384 fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf, SLVolumeItf, SLAndroidBufferQueue \n", 385 argv[0]); 386 fprintf(stdout, "and AudioPlayer with SL_DATALOCATOR_ANDROIDBUFFERQUEUE source / OutputMix " 387 "sink\n"); 388 fprintf(stdout, "Plays a sound and stops after its reported duration\n\n"); 389 390 if (argc != 2) { 391 fprintf(stdout, "Usage: %s path.ts\n", argv[0]); 392 exit(EXIT_FAILURE); 393 } 394 395 SLEngineOption EngineOption[] = { 396 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, 397 (SLuint32) SL_BOOLEAN_TRUE}}; 398 399 res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); 400 CheckErr(res); 401 /* Realizing the SL Engine in synchronous mode. */ 402 res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); 403 CheckErr(res); 404 405 TestPlayStream(sl, argv[1]); 406 407 /* Shutdown OpenSL ES */ 408 (*sl)->Destroy(sl); 409 410 return EXIT_SUCCESS; 411 } 412