slesTestDecodeToBuffQueue.cpp revision 1fd0cd18598d76e9a0f9e6675e4d988be41644f7
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/* Audio Decode Test
18
19First run the program from shell:
20  # slesTest_decodeToBuffQueue /sdcard/myFile.mp3 4
21
22These use adb on host to retrieve the decoded file:
23  % adb pull /sdcard/myFile.mp3.raw myFile.raw
24
25How to examine the output with Audacity:
26 Project / Import raw data
27 Select myFile.raw file, then click Open button
28 Choose these options:
29  Signed 16-bit PCM
30  Little-endian
31  1 Channel (Mono) / 2 Channels (Stereo) based on the selected file
32  Sample rate same as the selected file
33 Click Import button
34
35*/
36
37
38#include <stdlib.h>
39#include <stdio.h>
40#include <string.h>
41#include <unistd.h>
42#include <sys/time.h>
43#include <fcntl.h>
44#include <utils/threads.h>
45
46#include <SLES/OpenSLES.h>
47#include <SLES/OpenSLES_Android.h>
48#include <SLES/OpenSLES_AndroidConfiguration.h>
49
50/* Explicitly requesting SL_IID_ANDROIDSIMPLEBUFFERQUEUE and SL_IID_PREFETCHSTATUS
51 * on the AudioPlayer object */
52#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 2
53
54/* Size of the decode buffer queue */
55#define NB_BUFFERS_IN_QUEUE 4
56/* Size of each buffer in the queue */
57#define BUFFER_SIZE_IN_SAMPLES 1152 // number of samples per MP3 frame
58#define BUFFER_SIZE_IN_BYTES   (2*BUFFER_SIZE_IN_SAMPLES)
59
60/* Local storage for decoded audio data */
61int8_t pcmData[NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES];
62
63/* destination for decoded data */
64static FILE* gFp;
65
66/* to display the number of decode iterations */
67static int counter=0;
68
69/* to signal to the test app the end of the stream to decode has been reached */
70bool eos = false;
71android::Mutex eosLock;
72android::Condition eosCondition;
73
74/* used to detect errors likely to have occured when the OpenSL ES framework fails to open
75 * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond.
76 */
77#define PREFETCHEVENT_ERROR_CANDIDATE \
78        (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)
79
80//-----------------------------------------------------------------
81/* Exits the application if an error is encountered */
82#define ExitOnError(x) ExitOnErrorFunc(x,__LINE__)
83
84void ExitOnErrorFunc( SLresult result , int line)
85{
86    if (SL_RESULT_SUCCESS != result) {
87        fprintf(stdout, "%lu error code encountered at line %d, exiting\n", result, line);
88        exit(EXIT_FAILURE);
89    }
90}
91
92//-----------------------------------------------------------------
93/* Structure for passing information to callback function */
94typedef struct CallbackCntxt_ {
95    SLPlayItf playItf;
96    SLuint32  size;
97    SLint8*   pDataBase;    // Base address of local audio data storage
98    SLint8*   pData;        // Current address of local audio data storage
99} CallbackCntxt;
100
101//-----------------------------------------------------------------
102void SignalEos() {
103    android::Mutex::Autolock autoLock(eosLock);
104    eos = true;
105    eosCondition.signal();
106}
107
108//-----------------------------------------------------------------
109/* Callback for "prefetch" events, here used to detect audio resource opening errors */
110void PrefetchEventCallback( SLPrefetchStatusItf caller,  void *pContext, SLuint32 event)
111{
112    SLpermille level = 0;
113    (*caller)->GetFillLevel(caller, &level);
114    SLuint32 status;
115    //fprintf(stdout, "PrefetchEventCallback: received event %lu\n", event);
116    (*caller)->GetPrefetchStatus(caller, &status);
117    if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
118            && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
119        fprintf(stdout, "PrefetchEventCallback: Error while prefetching data, exiting\n");
120        SignalEos();
121    }
122}
123
124//-----------------------------------------------------------------
125/* Callback for "playback" events, i.e. event happening during decoding */
126void DecCallback(
127        SLPlayItf caller,
128        void *pContext,
129        SLuint32 event)
130{
131    if (SL_PLAYEVENT_HEADATEND & event) {
132        fprintf(stdout, "SL_PLAYEVENT_HEADATEND reached\n");
133        SignalEos();
134    }
135
136    if (SL_PLAYEVENT_HEADATNEWPOS & event) {
137        SLmillisecond pMsec = 0;
138        (*caller)->GetPosition(caller, &pMsec);
139        fprintf(stdout, "SL_PLAYEVENT_HEADATNEWPOS current position=%lums\n", pMsec);
140    }
141
142    if (SL_PLAYEVENT_HEADATMARKER & event) {
143        SLmillisecond pMsec = 0;
144        (*caller)->GetPosition(caller, &pMsec);
145        fprintf(stdout, "SL_PLAYEVENT_HEADATMARKER current position=%lums\n", pMsec);
146    }
147}
148
149//-----------------------------------------------------------------
150/* Callback for decoding buffer queue events */
151void DecBufferQueueCallback(
152        SLAndroidSimpleBufferQueueItf queueItf,
153        void *pContext)
154{
155    counter++;
156    fprintf(stdout, "DecBufferQueueCallback called (iteration %d)\n", counter);
157
158    CallbackCntxt *pCntxt = (CallbackCntxt*)pContext;
159
160    /* Save the decoded data  */
161    if (fwrite(pCntxt->pDataBase, 1, BUFFER_SIZE_IN_BYTES, gFp) < BUFFER_SIZE_IN_BYTES) {
162        fprintf(stdout, "Error writing to output file, signaling EOS\n");
163        SignalEos();
164        return;
165    }
166
167    /* Increase data pointer by buffer size */
168    pCntxt->pData += BUFFER_SIZE_IN_BYTES;
169
170    if (pCntxt->pData >= pCntxt->pDataBase + (NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES)) {
171        pCntxt->pData = pCntxt->pDataBase;
172    }
173
174    ExitOnError( (*queueItf)->Enqueue(queueItf, pCntxt->pDataBase, BUFFER_SIZE_IN_BYTES) );
175    // Note: adding a sleep here or any sync point is a way to slow down the decoding, or
176    //  synchronize it with some other event, as the OpenSL ES framework will block until the
177    //  buffer queue callback return to proceed with the decoding.
178
179/*
180    SLAndroidSimpleBufferQueueState decQueueState;
181    ExitOnError( (*queueItf)->GetState(queueItf, &decQueueState) );
182
183    fprintf(stderr, "\DecBufferQueueCallback now has pCntxt->pData=%p queue: "
184            "count=%lu playIndex=%lu\n",
185            pCntxt->pData, decQueueState.count, decQueueState.index);
186*/
187}
188
189//-----------------------------------------------------------------
190
191/* Decode an audio path by opening a file descriptor on that path  */
192void TestDecToBuffQueue( SLObjectItf sl, const char* path)
193{
194    size_t len = strlen((const char *) path);
195    char* outputPath = (char*) malloc(len + 4 + 1); // save room to concatenate ".raw"
196    if (NULL == outputPath) {
197        ExitOnError(SL_RESULT_RESOURCE_ERROR);
198    }
199    memcpy(outputPath, path, len + 1);
200    strcat(outputPath, ".raw");
201    gFp = fopen(outputPath, "w");
202    if (NULL == gFp) {
203        ExitOnError(SL_RESULT_RESOURCE_ERROR);
204    }
205
206    SLresult  result;
207    SLEngineItf EngineItf;
208
209    /* Objects this application uses: one audio player */
210    SLObjectItf  player;
211
212    /* Interfaces for the audio player */
213    SLAndroidSimpleBufferQueueItf decBuffQueueItf;
214    SLPrefetchStatusItf           prefetchItf;
215    SLPlayItf                     playItf;
216
217    /* Source of audio data for the decoding */
218    SLDataSource      decSource;
219    SLDataLocator_URI decUri;
220    SLDataFormat_MIME decMime;
221
222    /* Data sink for decoded audio */
223    SLDataSink                decDest;
224    SLDataLocator_AndroidSimpleBufferQueue decBuffQueue;
225    SLDataFormat_PCM          pcm;
226
227    SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
228    SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
229
230    /* Get the SL Engine Interface which is implicit */
231    result = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
232    ExitOnError(result);
233
234    /* Initialize arrays required[] and iidArray[] */
235    for (int i=0 ; i < NUM_EXPLICIT_INTERFACES_FOR_PLAYER ; i++) {
236        required[i] = SL_BOOLEAN_FALSE;
237        iidArray[i] = SL_IID_NULL;
238    }
239
240
241    /* ------------------------------------------------------ */
242    /* Configuration of the player  */
243
244    /* Request the AndroidSimpleBufferQueue interface */
245    required[0] = SL_BOOLEAN_TRUE;
246    iidArray[0] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
247    /* Request the PrefetchStatus interface */
248    required[1] = SL_BOOLEAN_TRUE;
249    iidArray[1] = SL_IID_PREFETCHSTATUS;
250
251    /* Setup the data source */
252    decUri.locatorType = SL_DATALOCATOR_URI;
253    decUri.URI = (SLchar*)path;
254    decMime.formatType = SL_DATAFORMAT_MIME;
255    /*     this is how ignored mime information is specified, according to OpenSL ES spec
256     *     in 9.1.6 SLDataFormat_MIME and 8.23 SLMetadataTraversalItf GetChildInfo */
257    decMime.mimeType      = (SLchar*)NULL;
258    decMime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
259    decSource.pLocator = (void *) &decUri;
260    decSource.pFormat  = (void *) &decMime;
261
262    /* Setup the data sink */
263    decBuffQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
264    decBuffQueue.numBuffers = NB_BUFFERS_IN_QUEUE;
265    /*    set up the format of the data in the buffer queue */
266    pcm.formatType = SL_DATAFORMAT_PCM;
267    // FIXME valid value required but currently ignored
268    pcm.numChannels = 1;
269    pcm.samplesPerSec = SL_SAMPLINGRATE_44_1;
270    pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
271    pcm.containerSize = 16;
272    pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
273    pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
274
275    decDest.pLocator = (void *) &decBuffQueue;
276    decDest.pFormat = (void * ) &pcm;
277
278    /* Create the audio player */
279    result = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &decSource, &decDest,
280            NUM_EXPLICIT_INTERFACES_FOR_PLAYER, iidArray, required);
281    ExitOnError(result);
282    fprintf(stdout, "Player created\n");
283
284    /* Realize the player in synchronous mode. */
285    result = (*player)->Realize(player, SL_BOOLEAN_FALSE);
286    ExitOnError(result);
287    fprintf(stdout, "Player realized\n");
288
289    /* Get the play interface which is implicit */
290    result = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
291    ExitOnError(result);
292
293    /* Set up the player callback to get events during the decoding */
294    // FIXME currently ignored
295    result = (*playItf)->SetMarkerPosition(playItf, 2000);
296    ExitOnError(result);
297    result = (*playItf)->SetPositionUpdatePeriod(playItf, 500);
298    ExitOnError(result);
299    result = (*playItf)->SetCallbackEventsMask(playItf,
300            SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADATEND);
301    ExitOnError(result);
302    result = (*playItf)->RegisterCallback(playItf, DecCallback, NULL);
303    ExitOnError(result);
304    fprintf(stdout, "Play callback registered\n");
305
306    /* Get the buffer queue interface which was explicitly requested */
307    result = (*player)->GetInterface(player, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
308            (void*)&decBuffQueueItf);
309    ExitOnError(result);
310
311    /* Get the prefetch status interface which was explicitly requested */
312    result = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
313    ExitOnError(result);
314
315    /* ------------------------------------------------------ */
316    /* Initialize the callback and its context for the decoding buffer queue */
317    CallbackCntxt cntxt;
318    cntxt.playItf = playItf;
319    cntxt.pDataBase = (int8_t*)&pcmData;
320    cntxt.pData = cntxt.pDataBase;
321    cntxt.size = sizeof(pcmData);
322    result = (*decBuffQueueItf)->RegisterCallback(decBuffQueueItf, DecBufferQueueCallback, &cntxt);
323    ExitOnError(result);
324
325    /* Enqueue buffers to map the region of memory allocated to store the decoded data */
326    fprintf(stdout,"Enqueueing buffer ");
327    for(int i = 0 ; i < NB_BUFFERS_IN_QUEUE ; i++) {
328        fprintf(stdout,"%d \n", i);
329        result = (*decBuffQueueItf)->Enqueue(decBuffQueueItf, cntxt.pData, BUFFER_SIZE_IN_BYTES);
330        ExitOnError(result);
331        cntxt.pData += BUFFER_SIZE_IN_BYTES;
332    }
333    fprintf(stdout,"\n");
334    cntxt.pData = cntxt.pDataBase;
335
336    /* ------------------------------------------------------ */
337    /* Initialize the callback for prefetch errors, if we can't open the resource to decode */
338    result = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf);
339    ExitOnError(result);
340    result = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, PREFETCHEVENT_ERROR_CANDIDATE);
341    ExitOnError(result);
342
343    /* ------------------------------------------------------ */
344    /* Start decoding */
345    result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
346    ExitOnError(result);
347    fprintf(stdout, "Starting to decode\n");
348
349    /* Decode until the end of the stream is reached */
350    {
351        android::Mutex::Autolock autoLock(eosLock);
352        while (!eos) {
353            eosCondition.wait(eosLock);
354        }
355    }
356    fprintf(stdout, "EOS signaled\n");
357
358    /* ------------------------------------------------------ */
359    /* End of decoding */
360
361    /* Stop decoding */
362    result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
363    ExitOnError(result);
364    fprintf(stdout, "Stopped decoding\n");
365
366    /* Destroy the AudioPlayer object */
367    (*player)->Destroy(player);
368
369    fclose(gFp);
370}
371
372//-----------------------------------------------------------------
373int main(int argc, char* const argv[])
374{
375    SLresult    result;
376    SLObjectItf sl;
377
378    fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf and SLAndroidSimpleBufferQueueItf ",
379            argv[0]);
380    fprintf(stdout, "on an AudioPlayer object to decode a URI to PCM\n");
381
382    if (argc != 2) {
383        fprintf(stdout, "Usage: \t%s source_file\n", argv[0]);
384        fprintf(stdout, "Example: \"%s /sdcard/myFile.mp3\n", argv[0]);
385        exit(EXIT_FAILURE);
386    }
387
388    SLEngineOption EngineOption[] = {
389            {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}
390    };
391
392    result = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL);
393    ExitOnError(result);
394
395    /* Realizing the SL Engine in synchronous mode. */
396    result = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
397    ExitOnError(result);
398
399    TestDecToBuffQueue(sl, argv[1]);
400
401    /* Shutdown OpenSL ES */
402    (*sl)->Destroy(sl);
403
404    return EXIT_SUCCESS;
405}
406