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