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 <sys/time.h>
21 
22 #include <SLES/OpenSLES.h>
23 
24 
25 #define MAX_NUMBER_INTERFACES 2
26 #define MAX_NUMBER_PLAYERS 40
27 
28 #define PREFETCHEVENT_ERROR_CANDIDATE \
29         (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)
30 
31 /* the OpenSL ES engine from which we create all other resources */
32 SLObjectItf  slEngine;
33 SLEngineItf  engineItf;
34 SLObjectItf  outputMix;
35 
36 SLboolean     required[MAX_NUMBER_INTERFACES];
37 SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
38 
39 SLObjectItf  audioPlayer[MAX_NUMBER_PLAYERS];
40 bool         validplayer[MAX_NUMBER_PLAYERS];
41 int          playerNum[MAX_NUMBER_PLAYERS];
42 SLPlayItf    playItfs[MAX_NUMBER_PLAYERS];
43 SLVolumeItf  volItfs[MAX_NUMBER_PLAYERS];
44 SLPrefetchStatusItf prefetchItfs[MAX_NUMBER_PLAYERS];
45 
46 SLDataSource      audioSource;
47 SLDataLocator_URI uri;
48 SLDataFormat_MIME mime;
49 
50 SLDataSink              audioSink;
51 SLDataLocator_OutputMix locator_outputmix;
52 
53 
54 //-----------------------------------------------------------------
55 //* Exits the application if an error is encountered */
56 #define CheckErr(x) ExitOnErrorFunc(x, -1, __LINE__)
57 #define CheckErrPlyr(x, id) ExitOnErrorFunc(x, id, __LINE__)
58 
ExitOnErrorFunc(SLresult result,int playerId,int line)59 void ExitOnErrorFunc( SLresult result, int playerId, int line)
60 {
61     if (SL_RESULT_SUCCESS != result) {
62         if (playerId == -1) {
63             fprintf(stderr, "Error %u code encountered at line %d, exiting\n", result, line);
64         } else {
65             fprintf(stderr, "Error %u code encountered at line %d for player %d, exiting\n",
66                     result, line, playerId);
67         }
68         exit(EXIT_FAILURE);
69     }
70 }
71 
72 //-----------------------------------------------------------------
73 /* PrefetchStatusItf callback for an audio player */
PrefetchEventCallback(SLPrefetchStatusItf caller,void * pContext,SLuint32 event)74 void PrefetchEventCallback( SLPrefetchStatusItf caller,  void *pContext, SLuint32 event)
75 {
76     SLresult res;
77     SLpermille level = 0;
78     int* pPlayerId = (int*)pContext;
79     res = (*caller)->GetFillLevel(caller, &level); CheckErrPlyr(res, *pPlayerId);
80     SLuint32 status;
81     //fprintf(stdout, "PrefetchEventCallback: received event %u\n", event);
82     res = (*caller)->GetPrefetchStatus(caller, &status); CheckErrPlyr(res, *pPlayerId);
83     if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
84             && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
85         fprintf(stdout, "PrefetchEventCallback: Error while prefetching data for player %d, "
86                 "exiting\n", *pPlayerId);
87         exit(EXIT_FAILURE);
88     }
89     if (event & SL_PREFETCHEVENT_FILLLEVELCHANGE) {
90         fprintf(stdout, "PrefetchEventCallback: Buffer fill level is = %d for player %d\n",
91                 level, *pPlayerId);
92     }
93     if (event & SL_PREFETCHEVENT_STATUSCHANGE) {
94         fprintf(stdout, "PrefetchEventCallback: Prefetch Status is = %u for player %d\n",
95                 status, *pPlayerId);
96     }
97 }
98 
99 
100 //-----------------------------------------------------------------
101 /* PlayItf callback for playback events */
PlayEventCallback(SLPlayItf caller,void * pContext,SLuint32 event)102 void PlayEventCallback(
103         SLPlayItf caller,
104         void *pContext,
105         SLuint32 event)
106 {
107     SLresult res;
108     int* pPlayerId = (int*)pContext;
109     if (SL_PLAYEVENT_HEADATEND & event) {
110         fprintf(stdout, "SL_PLAYEVENT_HEADATEND reached for player %d\n", *pPlayerId);
111         //SignalEos();
112     }
113 
114     if (SL_PLAYEVENT_HEADATNEWPOS & event) {
115         SLmillisecond pMsec = 0;
116         res = (*caller)->GetPosition(caller, &pMsec); CheckErrPlyr(res, *pPlayerId);
117         fprintf(stdout, "SL_PLAYEVENT_HEADATNEWPOS current position=%ums for player %d\n",
118                 pMsec, *pPlayerId);
119     }
120 
121     if (SL_PLAYEVENT_HEADATMARKER & event) {
122         SLmillisecond pMsec = 0;
123         res = (*caller)->GetPosition(caller, &pMsec); CheckErrPlyr(res, *pPlayerId);
124         fprintf(stdout, "SL_PLAYEVENT_HEADATMARKER current position=%ums for player %d\n",
125                 pMsec, *pPlayerId);
126     }
127 }
128 
129 
130 //-----------------------------------------------------------------
TestSetup(const char * path)131 void TestSetup(const char* path) {
132     SLresult                res;
133 
134     /* Create the engine */
135     SLEngineOption EngineOption[] = {
136             {(SLuint32) SL_ENGINEOPTION_THREADSAFE,
137                     (SLuint32) SL_BOOLEAN_TRUE}};
138 
139     res = slCreateEngine( &slEngine, 1, EngineOption, 0, NULL, NULL);
140     CheckErr(res);
141     /* Realizing the SL Engine in synchronous mode. */
142     res = (*slEngine)->Realize(slEngine, SL_BOOLEAN_FALSE);
143     CheckErr(res);
144     /* Get the SL Engine Interface which is implicit */
145     res = (*slEngine)->GetInterface(slEngine, SL_IID_ENGINE, (void*)&engineItf);
146     CheckErr(res);
147 
148     /* Create Output Mix object to be used by player */
149     res = (*engineItf)->CreateOutputMix(engineItf, &outputMix, 0,
150             iidArray, required); CheckErr(res);
151     /* Realizing the Output Mix object in synchronous mode. */
152     res = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
153     CheckErr(res);
154 
155     /* Setup the data source structure for the URI */
156     // the syntax below is more future-proof than the individual field initialization
157     //  with regards to OpenSL ES 1.1 but adds scary compilation warnings
158     //uri = { SL_DATALOCATOR_URI /*locatorType*/, (SLchar*) path /*URI*/ };
159     //mime = { /*formatType*/ SL_DATAFORMAT_MIME, /*mimeType*/ (SLchar*)NULL,
160     //         /*containerType*/ SL_CONTAINERTYPE_UNSPECIFIED };
161     uri.locatorType = SL_DATALOCATOR_URI;
162     uri.URI         =  (SLchar*) path;
163     mime.formatType    = SL_DATAFORMAT_MIME;
164     mime.mimeType      = (SLchar*)NULL;
165     mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
166 
167     audioSource.pFormat      = (void *)&mime;
168     audioSource.pLocator     = (void *)&uri;
169 
170     /* Setup the data sink structure */
171     locator_outputmix.locatorType   = SL_DATALOCATOR_OUTPUTMIX;
172     locator_outputmix.outputMix    = outputMix;
173     audioSink.pLocator           = (void *)&locator_outputmix;
174     audioSink.pFormat            = NULL;
175 
176     /* Initialize arrays required[] and iidArray[] */
177     for (int i=0 ; i < MAX_NUMBER_INTERFACES ; i++) {
178         required[i] = SL_BOOLEAN_FALSE;
179         iidArray[i] = SL_IID_NULL;
180     }
181     /* Set arrays required[] and iidArray[] for VOLUME and PREFETCHSTATUS interface */
182     required[0] = SL_BOOLEAN_TRUE;
183     iidArray[0] = SL_IID_VOLUME;
184     required[1] = SL_BOOLEAN_TRUE;
185     iidArray[1] = SL_IID_PREFETCHSTATUS;
186 
187     fprintf(stdout, "TestSetup(%s) completed\n", path);
188 }
189 
190 
191 //-----------------------------------------------------------------
TestTeardown()192 void TestTeardown() {
193     /* Destroy Output Mix object */
194     (*outputMix)->Destroy(outputMix);
195 
196     /* Shutdown OpenSL ES */
197     (*slEngine)->Destroy(slEngine);
198 }
199 
200 
201 //-----------------------------------------------------------------
202 /**
203  * Create a player and, if the creation is successful,
204  * configure it, and start playing.
205  */
CreatePlayer(int playerId)206 void CreatePlayer(int playerId) {
207     SLresult res;
208     playerNum[playerId] = playerId;
209 
210     /* Create the audio player */
211     res = (*engineItf)->CreateAudioPlayer(engineItf, &audioPlayer[playerId],
212             &audioSource, &audioSink, MAX_NUMBER_INTERFACES, iidArray, required);
213     if (SL_RESULT_SUCCESS != res) {
214             // do not abort the test, just flag the player as not a candidate for destruction
215             fprintf(stdout, "CreateAudioPlayer for player %d failed\n", playerId);
216             validplayer[playerId] = false;
217             return;
218         }
219     validplayer[playerId] = true;
220 
221     /* Realizing the player in synchronous mode. */
222     res = (*audioPlayer[playerId])->Realize(audioPlayer[playerId], SL_BOOLEAN_FALSE);
223     if (SL_RESULT_SUCCESS != res) {
224         // do not abort the test, just stop the player initialization here
225         fprintf(stdout, "Realize for player %d failed\n", playerId);
226         // this player is still a candidate for destruction
227         return;
228     }
229     // after this point, any failure is a test failure
230 
231     /* Get interfaces */
232     res = (*audioPlayer[playerId])->GetInterface(audioPlayer[playerId], SL_IID_PLAY,
233             (void*)&playItfs[playerId]);
234     CheckErrPlyr(res, playerId);
235 
236     res = (*audioPlayer[playerId])->GetInterface(audioPlayer[playerId], SL_IID_VOLUME,
237             (void*)&volItfs[playerId]);
238     CheckErrPlyr(res, playerId);
239 
240     res = (*audioPlayer[playerId])->GetInterface(audioPlayer[playerId], SL_IID_PREFETCHSTATUS,
241             (void*)&prefetchItfs[playerId]);
242     CheckErrPlyr(res, playerId);
243     res = (*prefetchItfs[playerId])->RegisterCallback(prefetchItfs[playerId], PrefetchEventCallback,
244             &playerNum[playerId]);
245     CheckErrPlyr(res, playerId);
246     res = (*prefetchItfs[playerId])->SetCallbackEventsMask(prefetchItfs[playerId],
247             SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
248     CheckErrPlyr(res, playerId);
249 
250     /* Set the player volume */
251     res = (*volItfs[playerId])->SetVolumeLevel( volItfs[playerId], -300);
252     CheckErrPlyr(res, playerId);
253 
254     /* Set up the player callback to get events during the decoding */
255     res = (*playItfs[playerId])->SetMarkerPosition(playItfs[playerId], 2000);
256     CheckErrPlyr(res, playerId);
257     res = (*playItfs[playerId])->SetPositionUpdatePeriod(playItfs[playerId], 500);
258     CheckErrPlyr(res, playerId);
259     res = (*playItfs[playerId])->SetCallbackEventsMask(playItfs[playerId],
260             SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADATEND);
261     CheckErrPlyr(res, playerId);
262     res = (*playItfs[playerId])->RegisterCallback(playItfs[playerId], PlayEventCallback,
263             &playerNum[playerId]);
264     CheckErrPlyr(res, playerId);
265 
266     /* Configure fill level updates every 5 percent */
267     (*prefetchItfs[playerId])->SetFillUpdatePeriod(prefetchItfs[playerId], 50);
268 
269     /* Play the URI */
270     /*     first cause the player to prefetch the data */
271     fprintf(stdout, "Setting player %d  to PAUSED\n", playerId);
272     res = (*playItfs[playerId])->SetPlayState( playItfs[playerId], SL_PLAYSTATE_PAUSED );
273     CheckErrPlyr(res, playerId);
274     /*     wait until there's data to play */
275     SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
276     SLuint32 timeOutIndex = 10; // 1s, should be enough for a local file
277     while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0)) {
278         usleep(100 * 1000);
279         res = (*prefetchItfs[playerId])->GetPrefetchStatus(prefetchItfs[playerId], &prefetchStatus);
280         CheckErrPlyr(res, playerId);
281         timeOutIndex--;
282     }
283 
284     if (timeOutIndex == 0) {
285         fprintf(stderr, "Prefetch timed out for player %d\n", playerId);
286         return;
287     }
288     res = (*playItfs[playerId])->SetPlayState( playItfs[playerId], SL_PLAYSTATE_PLAYING );
289     CheckErrPlyr(res, playerId);
290 
291     /* Display duration */
292     SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
293     res = (*playItfs[playerId])->GetDuration(playItfs[playerId], &durationInMsec);
294     CheckErrPlyr(res, playerId);
295     if (durationInMsec == SL_TIME_UNKNOWN) {
296         fprintf(stdout, "Content duration is unknown for player %d\n", playerId);
297     } else {
298         fprintf(stdout, "Content duration is %u ms for player %d\n", durationInMsec, playerId);
299     }
300 }
301 
302 //-----------------------------------------------------------------
DestroyPlayer(int playerId)303 void DestroyPlayer(int playerId) {
304     fprintf(stdout, "About to destroy player %d\n", playerId);
305     /* Destroy the player */
306     (*audioPlayer[playerId])->Destroy(audioPlayer[playerId]);
307 }
308 
309 
310 //-----------------------------------------------------------------
main(int argc,char * const argv[])311 int main(int argc, char* const argv[])
312 {
313     fprintf(stdout, "OpenSL ES test %s: creates and destroys as many ", argv[0]);
314     fprintf(stdout, "AudioPlayer objects as possible (max=%d)\n\n", MAX_NUMBER_PLAYERS);
315 
316     if (argc == 1) {
317         fprintf(stdout, "Usage: %s path \n\t%s url\n", argv[0], argv[0]);
318         fprintf(stdout, "Example: \"%s /sdcard/my.mp3\"  or \"%s file:///sdcard/my.mp3\"\n",
319                 argv[0], argv[0]);
320         exit(EXIT_FAILURE);
321     }
322 
323     TestSetup(argv[1]);
324 
325     for (int i=0 ; i<MAX_NUMBER_PLAYERS ; i++) {
326         CreatePlayer(i);
327     }
328     fprintf(stdout, "After creating %d AudioPlayers\n", MAX_NUMBER_PLAYERS);
329 
330     /* Wait for an arbitrary amount of time. if playing a long file, the players will still
331        be playing while the destructions start. */
332     usleep(10*1000*1000); // 10s
333 
334     for (int i=0 ; i<MAX_NUMBER_PLAYERS ; i++) {
335         if (validplayer[i]) {
336             DestroyPlayer(i);
337         }
338     }
339     fprintf(stdout, "After destroying valid players among %d AudioPlayers\n", MAX_NUMBER_PLAYERS);
340 
341     TestTeardown();
342 
343     return EXIT_SUCCESS;
344 }
345