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 // multiplay is a command-line test app that plays multiple files randomly 18 19 #include <SLES/OpenSLES.h> 20 #include <assert.h> 21 #include <string.h> 22 #include <stdlib.h> 23 #include <stdio.h> 24 #include <unistd.h> 25 26 // Describes the state of one player 27 28 typedef struct { 29 SLObjectItf mPlayerObject; 30 SLPlayItf mPlayerPlay; 31 SLSeekItf mPlayerSeek; 32 SLPrefetchStatusItf mPlayerPrefetchStatus; 33 SLVolumeItf mPlayerVolume; 34 SLmillisecond mPlayerDuration; 35 SLboolean mPlayerErrorInCallback; 36 SLboolean mPlayerErrorReported; 37 } Player; 38 39 // Strings corresponding to result codes; FIXME should move to a common test library 40 41 static const char *result_strings[] = { 42 "SUCCESS", 43 "PRECONDITIONS_VIOLATED", 44 "PARAMETER_INVALID", 45 "MEMORY_FAILURE", 46 "RESOURCE_ERROR", 47 "RESOURCE_LOST", 48 "IO_ERROR", 49 "BUFFER_INSUFFICIENT", 50 "CONTENT_CORRUPTED", 51 "CONTENT_UNSUPPORTED", 52 "CONTENT_NOT_FOUND", 53 "PERMISSION_DENIED", 54 "FEATURE_UNSUPPORTED", 55 "INTERNAL_ERROR", 56 "UNKNOWN_ERROR", 57 "OPERATION_ABORTED", 58 "CONTROL_LOST" 59 }; 60 61 // Convert result to string; FIXME should move to common test library 62 63 static const char *result_to_string(SLresult result) 64 { 65 static char buffer[32]; 66 if ( /* result >= 0 && */ result < sizeof(result_strings) / sizeof(result_strings[0])) 67 return result_strings[result]; 68 sprintf(buffer, "%d", (int) result); 69 return buffer; 70 } 71 72 // Compare result against expected and exit suddenly if wrong 73 74 void check2(SLresult result, int line) 75 { 76 if (SL_RESULT_SUCCESS != result) { 77 fprintf(stderr, "error %s at line %d\n", result_to_string(result), line); 78 exit(EXIT_FAILURE); 79 } 80 } 81 82 // Same as above but automatically adds the source code line number 83 84 #define check(result) check2(result, __LINE__) 85 86 // Prefetch status callback 87 88 #define PREFETCHEVENT_ERROR_CANDIDATE \ 89 (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE) 90 91 void prefetch_callback(SLPrefetchStatusItf caller, void *context, SLuint32 event) 92 { 93 SLresult result; 94 assert(context != NULL); 95 Player *p = (Player *) context; 96 assert(p->mPlayerPrefetchStatus == caller); 97 SLpermille level; 98 result = (*caller)->GetFillLevel(caller, &level); 99 check(result); 100 SLuint32 status; 101 result = (*caller)->GetPrefetchStatus(caller, &status); 102 check(result); 103 //fprintf(stderr, "PrefetchEventCallback: received event %u, level %u, status %u\n", 104 // event, level, status); 105 if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE)) 106 && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) { 107 p->mPlayerErrorInCallback = SL_BOOLEAN_TRUE; 108 } 109 } 110 111 // Main program 112 113 int main(int argc, char **argv) 114 { 115 int i; 116 const char *arg; 117 int numPlayers = 0; 118 int playTimeInMilliseconds = 0; // default to run forever 119 SLmillibel mixVolumeLevel = 0; 120 for (i = 1; i < argc; ++i) { 121 arg = argv[i]; 122 if (arg[0] != '-') 123 break; 124 if (!strncmp(arg, "-n", 2)) 125 numPlayers = atoi(&arg[2]); 126 else if (!strncmp(arg, "-v", 2)) 127 mixVolumeLevel = atoi(&arg[2]); 128 else if (!strncmp(arg, "-t", 2)) 129 playTimeInMilliseconds = atoi(&arg[2]) * 1000; 130 else 131 fprintf(stderr, "unknown option: %s\n", arg); 132 } 133 int numPathnames = argc - i; 134 if (numPathnames <= 0) { 135 fprintf(stderr, "usage: %s file.wav ...\n", argv[0]); 136 return EXIT_FAILURE; 137 } 138 if (numPlayers <= 0) { 139 numPlayers = numPathnames; 140 } 141 Player *players = (Player *) calloc(numPlayers, sizeof(Player)); 142 assert(NULL != players); 143 char **pathnames = &argv[i]; 144 SLresult result; 145 146 // engine 147 const SLInterfaceID engine_ids[] = {SL_IID_ENGINE}; 148 const SLboolean engine_req[] = {SL_BOOLEAN_TRUE}; 149 SLObjectItf engineObject; 150 result = slCreateEngine(&engineObject, 0, NULL, 1, engine_ids, engine_req); 151 check(result); 152 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); 153 check(result); 154 SLEngineItf engineEngine; 155 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); 156 check(result); 157 158 // mixer 159 const SLInterfaceID mix_ids[] = {SL_IID_VOLUME}; 160 const SLboolean mix_req[] = {SL_BOOLEAN_TRUE}; 161 SLObjectItf mixObject; 162 result = (*engineEngine)->CreateOutputMix(engineEngine, &mixObject, 0, mix_ids, mix_req); 163 check(result); 164 result = (*mixObject)->Realize(mixObject, SL_BOOLEAN_FALSE); 165 check(result); 166 #if 0 167 SLVolumeItf mixVolume; 168 result = (*mixObject)->GetInterface(mixObject, SL_IID_VOLUME, &mixVolume); 169 check(result); 170 SLmillibel mixVolumeLevelDefault; 171 result = (*mixVolume)->GetVolumeLevel(mixVolume, &mixVolumeLevelDefault); 172 check(result); 173 printf("default mix volume level = %d\n", mixVolumeLevelDefault); 174 #endif 175 176 printf("numPathnames=%d\n", numPathnames); 177 printf("numPlayers=%d\n", numPlayers); 178 Player *p; 179 180 // create all the players 181 for (i = 0; i < numPlayers; ++i) { 182 const SLInterfaceID player_ids[] = 183 {SL_IID_PLAY, SL_IID_VOLUME, SL_IID_SEEK, SL_IID_PREFETCHSTATUS}; 184 const SLboolean player_req[] = 185 {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; 186 p = &players[i]; 187 SLDataLocator_URI locURI = {SL_DATALOCATOR_URI, (SLchar *) pathnames[i % numPathnames]}; 188 SLDataFormat_MIME dfMIME = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED}; 189 SLDataSource audioSrc = {&locURI, &dfMIME}; 190 SLDataLocator_OutputMix locOutputMix = {SL_DATALOCATOR_OUTPUTMIX, mixObject}; 191 SLDataSink audioSnk = {&locOutputMix, NULL}; 192 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &p->mPlayerObject, &audioSrc, 193 &audioSnk, sizeof(player_ids)/sizeof(player_ids[0]), player_ids, player_req); 194 check(result); 195 result = (*p->mPlayerObject)->Realize(p->mPlayerObject, SL_BOOLEAN_FALSE); 196 check(result); 197 result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_PLAY, &p->mPlayerPlay); 198 check(result); 199 result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_VOLUME, 200 &p->mPlayerVolume); 201 check(result); 202 result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_SEEK, &p->mPlayerSeek); 203 check(result); 204 result = (*p->mPlayerObject)->GetInterface(p->mPlayerObject, SL_IID_PREFETCHSTATUS, 205 &p->mPlayerPrefetchStatus); 206 check(result); 207 result = (*p->mPlayerPrefetchStatus)->RegisterCallback(p->mPlayerPrefetchStatus, 208 prefetch_callback, p); 209 check(result); 210 result = (*p->mPlayerPrefetchStatus)->SetCallbackEventsMask(p->mPlayerPrefetchStatus, 211 SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE); 212 check(result); 213 } 214 215 // now loop randomly doing things to the players 216 for (;;) { 217 SLmillisecond delay = 100 + (rand() & 1023); 218 printf("sleep %u\n", (unsigned) delay); 219 usleep(delay * 1000); 220 i = (rand() & 0x7FFFFFFF) % numPlayers; 221 p = &players[i]; 222 if (p->mPlayerErrorReported) 223 continue; 224 printf("player %d (%s): ", i, pathnames[i]); 225 if (p->mPlayerErrorInCallback && !p->mPlayerErrorReported) { 226 printf("error, "); 227 p->mPlayerErrorReported = SL_BOOLEAN_TRUE; 228 } 229 result = (*p->mPlayerPlay)->GetDuration(p->mPlayerPlay, &p->mPlayerDuration); 230 check(result); 231 if (p->mPlayerDuration == SL_TIME_UNKNOWN) { 232 printf("duration unknown, "); 233 } else { 234 printf("duration %d ms, ", (int) p->mPlayerDuration); 235 } 236 SLuint32 state; 237 result = (*p->mPlayerPlay)->GetPlayState(p->mPlayerPlay, &state); 238 check(result); 239 printf("state = "); 240 switch (state) { 241 case SL_PLAYSTATE_STOPPED: 242 printf("STOPPED"); 243 break; 244 case SL_PLAYSTATE_PAUSED: 245 printf("PAUSED"); 246 break; 247 case SL_PLAYSTATE_PLAYING: 248 printf("PLAYING"); 249 break; 250 default: 251 printf("%u", (unsigned) state); 252 break; 253 } 254 printf("\n"); 255 if (state == SL_PLAYSTATE_STOPPED || state == SL_PLAYSTATE_PAUSED) { 256 SLmillibel volumeLevel = -((rand() & 0x7FFFFFFF) % ((SL_MILLIBEL_MIN + 1) / 10)); 257 printf("volume %d\n", volumeLevel); 258 result = (*p->mPlayerVolume)->SetVolumeLevel(p->mPlayerVolume, volumeLevel); 259 check(result); 260 result = (*p->mPlayerVolume)->EnableStereoPosition(p->mPlayerVolume, SL_BOOLEAN_TRUE); 261 check(result); 262 SLpermille stereoPosition = ((rand() & 0x7FFFFFFF) % 2001) - 1000; 263 printf("position %d\n", stereoPosition); 264 result = (*p->mPlayerVolume)->SetStereoPosition(p->mPlayerVolume, stereoPosition); 265 check(result); 266 if (state != SL_PLAYSTATE_STOPPED) { 267 result = (*p->mPlayerSeek)->SetPosition(p->mPlayerSeek, 0, SL_SEEKMODE_FAST); 268 check(result); 269 } 270 result = (*p->mPlayerPlay)->SetPlayState(p->mPlayerPlay, SL_PLAYSTATE_PLAYING); 271 check(result); 272 } 273 if ((playTimeInMilliseconds > 0) && ((playTimeInMilliseconds -= delay) < 0)) 274 break; 275 } 276 277 for (i = 0; i < numPlayers; ++i) { 278 SLObjectItf playerObject = players[i].mPlayerObject; 279 (*playerObject)->Destroy(playerObject); 280 } 281 (*mixObject)->Destroy(mixObject); 282 (*engineObject)->Destroy(engineObject); 283 284 return EXIT_SUCCESS; 285 } 286