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 SLresult res; 185 186 SLDataSource audioSource; 187 SLDataLocator_AndroidBufferQueue streamLocator; 188 SLDataFormat_MIME mime; 189 190 SLDataSink audioSink; 191 SLDataLocator_OutputMix locator_outputmix; 192 193 SLObjectItf player; 194 SLPlayItf playItf; 195 SLVolumeItf volItf; 196 SLAndroidBufferQueueItf abqItf; 197 198 SLObjectItf OutputMix; 199 200 SLboolean required[MAX_NUMBER_INTERFACES]; 201 SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; 202 203 int playTimeInSec = 60; 204 205 file = fopen(path, "rb"); 206 207 /* Get the SL Engine Interface which is implicit */ 208 res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf); 209 CheckErr(res); 210 211 /* Initialize arrays required[] and iidArray[] */ 212 for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) { 213 required[i] = SL_BOOLEAN_FALSE; 214 iidArray[i] = SL_IID_NULL; 215 } 216 217 // Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface 218 required[0] = SL_BOOLEAN_TRUE; 219 iidArray[0] = SL_IID_VOLUME; 220 required[1] = SL_BOOLEAN_TRUE; 221 iidArray[1] = SL_IID_ANDROIDBUFFERQUEUESOURCE; 222 // Create Output Mix object to be used by player 223 res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0, 224 iidArray, required); CheckErr(res); 225 226 // Realizing the Output Mix object in synchronous mode. 227 res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE); 228 CheckErr(res); 229 230 /* Setup the data source structure for the URI */ 231 streamLocator.locatorType = SL_DATALOCATOR_ANDROIDBUFFERQUEUE; 232 streamLocator.numBuffers = NB_BUFFERS; 233 mime.formatType = SL_DATAFORMAT_MIME; 234 mime.mimeType = (SLchar *) "video/mp2ts";//(SLchar*)NULL; 235 mime.containerType = SL_CONTAINERTYPE_MPEG_TS; 236 237 audioSource.pFormat = (void *)&mime; 238 audioSource.pLocator = (void *)&streamLocator; 239 240 /* Setup the data sink structure */ 241 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 242 locator_outputmix.outputMix = OutputMix; 243 audioSink.pLocator = (void *)&locator_outputmix; 244 audioSink.pFormat = NULL; 245 246 /* Create the audio player */ 247 res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &audioSource, &audioSink, 248 MAX_NUMBER_INTERFACES, iidArray, required); CheckErr(res); 249 250 /* Realizing the player in synchronous mode. */ 251 res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res); 252 fprintf(stdout, "URI example: after Realize\n"); 253 254 /* Get interfaces */ 255 res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); CheckErr(res); 256 257 res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volItf); CheckErr(res); 258 259 res = (*player)->GetInterface(player, SL_IID_ANDROIDBUFFERQUEUESOURCE, (void*)&abqItf); 260 CheckErr(res); 261 262 res = (*abqItf)->RegisterCallback(abqItf, AndroidBufferQueueCallback, 263 // context is not used in the example, but can be used to track who registered 264 // the buffer queue callback 265 NULL /*pContext*/); CheckErr(res); 266 267 res = (*abqItf)->SetCallbackEventsMask(abqItf, SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED); 268 CheckErr(res); 269 270 /* Display duration */ 271 SLmillisecond durationInMsec = SL_TIME_UNKNOWN; 272 res = (*playItf)->GetDuration(playItf, &durationInMsec); 273 CheckErr(res); 274 if (durationInMsec == SL_TIME_UNKNOWN) { 275 fprintf(stdout, "Content duration is unknown (before starting to prefetch)\n"); 276 } else { 277 fprintf(stdout, "Content duration is %u ms (before starting to prefetch)\n", 278 durationInMsec); 279 } 280 281 /* Set the player volume */ 282 res = (*volItf)->SetVolumeLevel( volItf, 0);//-300); 283 CheckErr(res); 284 285 286 /* Play the URI */ 287 /* first cause the player to prefetch the data */ 288 fprintf(stdout, "Before set to PAUSED\n"); 289 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED ); 290 fprintf(stdout, "After set to PAUSED\n"); 291 CheckErr(res); 292 293 /* Fill our cache */ 294 if (fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file) <= 0) { 295 fprintf(stderr, "Error filling cache, exiting\n"); 296 goto destroyRes; 297 } 298 /* Enqueue the content of our cache before starting to play, 299 * we don't want to starve the player */ 300 for (int i=0 ; i < NB_BUFFERS ; i++) { 301 res = (*abqItf)->Enqueue(abqItf, NULL /*pBufferContext*/, 302 dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0); 303 CheckErr(res); 304 } 305 306 #if 0 // used to test ABQ starving where only one buffer is enqueued before playback 307 /* Fill our cache */ 308 if (fread(dataCache, 1, BUFFER_SIZE * 1, file) <= 0) { 309 fprintf(stderr, "Error filling cache, exiting\n"); 310 goto destroyRes; 311 } 312 /* Enqueue the content of our cache before starting to play, 313 * we don't want to starve the player */ 314 for (int i=0 ; i < 1 ; i++) { 315 res = (*abqItf)->Enqueue(abqItf, dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0); 316 CheckErr(res); 317 } 318 #endif 319 /* wait until there's data to play */ 320 //SLpermille fillLevel = 0; 321 /* 322 SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW; 323 SLuint32 timeOutIndex = 2; 324 while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) && 325 !prefetchError) { 326 usleep(1 * 1000 * 1000); // 1s 327 //(*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); 328 timeOutIndex--; 329 } 330 331 if (timeOutIndex == 0 || prefetchError) { 332 fprintf(stderr, "We\'re done waiting, failed to prefetch data in time, exiting\n"); 333 goto destroyRes; 334 } 335 */ 336 337 /* Display duration again, */ 338 /* res = (*playItf)->GetDuration(playItf, &durationInMsec); 339 CheckErr(res); 340 if (durationInMsec == SL_TIME_UNKNOWN) { 341 fprintf(stdout, "Content duration is unknown (after prefetch completed)\n"); 342 } else { 343 fprintf(stdout, "Content duration is %lu ms (after prefetch completed)\n", durationInMsec); 344 } 345 */ 346 347 fprintf(stdout, "URI example: starting to play\n"); 348 res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); 349 CheckErr(res); 350 351 /* Wait as long as the duration of the content before stopping */ 352 fprintf(stdout, "Letting playback go on for %d sec\n", playTimeInSec); 353 usleep(playTimeInSec /*s*/ * 1000 * 1000); 354 355 356 /* Make sure player is stopped */ 357 fprintf(stdout, "URI example: stopping playback\n"); 358 res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED); 359 CheckErr(res); 360 361 fprintf(stdout, "sleeping to verify playback stopped\n"); 362 usleep(2 /*s*/ * 1000 * 1000); 363 364 destroyRes: 365 366 /* Destroy the player */ 367 (*player)->Destroy(player); 368 369 /* Destroy Output Mix object */ 370 (*OutputMix)->Destroy(OutputMix); 371 372 fclose(file); 373 } 374 375 //----------------------------------------------------------------- 376 int main(int argc, char* const argv[]) 377 { 378 SLresult res; 379 SLObjectItf sl; 380 381 fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf, SLVolumeItf, SLAndroidBufferQueue \n", 382 argv[0]); 383 fprintf(stdout, "and AudioPlayer with SL_DATALOCATOR_ANDROIDBUFFERQUEUE source / OutputMix " 384 "sink\n"); 385 fprintf(stdout, "Plays a sound and stops after its reported duration\n\n"); 386 387 if (argc != 2) { 388 fprintf(stdout, "Usage: %s path.ts\n", argv[0]); 389 exit(EXIT_FAILURE); 390 } 391 392 SLEngineOption EngineOption[] = { 393 {(SLuint32) SL_ENGINEOPTION_THREADSAFE, 394 (SLuint32) SL_BOOLEAN_TRUE}}; 395 396 res = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL); 397 CheckErr(res); 398 /* Realizing the SL Engine in synchronous mode. */ 399 res = (*sl)->Realize(sl, SL_BOOLEAN_FALSE); 400 CheckErr(res); 401 402 TestPlayStream(sl, argv[1]); 403 404 /* Shutdown OpenSL ES */ 405 (*sl)->Destroy(sl); 406 407 return EXIT_SUCCESS; 408 } 409