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