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