slesTestDecodeToBuffQueue.cpp revision 7f5cc1afe49395fefaad9b2bbd728a45d1bfda6a
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 for decoding, SL_IID_METADATAEXTRACTION for retrieving the
52 * format of the decoded audio */
53#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 3
54
55/* Size of the decode buffer queue */
56#define NB_BUFFERS_IN_QUEUE 4
57/* Size of each buffer in the queue */
58#define BUFFER_SIZE_IN_SAMPLES 1152 // number of samples per MP3 frame
59#define BUFFER_SIZE_IN_BYTES   (2*BUFFER_SIZE_IN_SAMPLES)
60
61/* Local storage for decoded audio data */
62int8_t pcmData[NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES];
63
64/* destination for decoded data */
65static FILE* gFp;
66
67/* to display the number of decode iterations */
68static int counter=0;
69
70/* to signal to the test app the end of the stream to decode has been reached */
71bool eos = false;
72android::Mutex eosLock;
73android::Condition eosCondition;
74
75/* used to detect errors likely to have occured when the OpenSL ES framework fails to open
76 * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond.
77 */
78#define PREFETCHEVENT_ERROR_CANDIDATE \
79        (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)
80
81//-----------------------------------------------------------------
82/* Exits the application if an error is encountered */
83#define ExitOnError(x) ExitOnErrorFunc(x,__LINE__)
84
85void ExitOnErrorFunc( SLresult result , int line)
86{
87    if (SL_RESULT_SUCCESS != result) {
88        fprintf(stdout, "Error code %u encountered at line %d, exiting\n", result, line);
89        exit(EXIT_FAILURE);
90    }
91}
92
93/* Used to signal prefetching failures */
94bool prefetchError = false;
95
96//-----------------------------------------------------------------
97/* Structure for passing information to callback function */
98typedef struct CallbackCntxt_ {
99    SLPlayItf playItf;
100    SLuint32  size;
101    SLint8*   pDataBase;    // Base address of local audio data storage
102    SLint8*   pData;        // Current address of local audio data storage
103} CallbackCntxt;
104
105//-----------------------------------------------------------------
106void SignalEos() {
107    android::Mutex::Autolock autoLock(eosLock);
108    eos = true;
109    eosCondition.signal();
110}
111
112//-----------------------------------------------------------------
113/* Callback for "prefetch" events, here used to detect audio resource opening errors */
114void PrefetchEventCallback( SLPrefetchStatusItf caller,  void *pContext, SLuint32 event)
115{
116    SLpermille level = 0;
117    (*caller)->GetFillLevel(caller, &level);
118    SLuint32 status;
119    //fprintf(stdout, "PrefetchEventCallback: received event %u\n", event);
120    (*caller)->GetPrefetchStatus(caller, &status);
121    if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
122            && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
123        fprintf(stdout, "PrefetchEventCallback: Error while prefetching data, exiting\n");
124        prefetchError = true;
125        SignalEos();
126    }
127}
128
129//-----------------------------------------------------------------
130/* Callback for "playback" events, i.e. event happening during decoding */
131void DecCallback(
132        SLPlayItf caller,
133        void *pContext,
134        SLuint32 event)
135{
136    if (SL_PLAYEVENT_HEADATEND & event) {
137        fprintf(stdout, "SL_PLAYEVENT_HEADATEND reached\n");
138        SignalEos();
139    }
140
141    if (SL_PLAYEVENT_HEADATNEWPOS & event) {
142        SLmillisecond pMsec = 0;
143        (*caller)->GetPosition(caller, &pMsec);
144        fprintf(stdout, "SL_PLAYEVENT_HEADATNEWPOS current position=%ums\n", pMsec);
145    }
146
147    if (SL_PLAYEVENT_HEADATMARKER & event) {
148        SLmillisecond pMsec = 0;
149        (*caller)->GetPosition(caller, &pMsec);
150        fprintf(stdout, "SL_PLAYEVENT_HEADATMARKER current position=%ums\n", pMsec);
151    }
152}
153
154//-----------------------------------------------------------------
155/* Callback for decoding buffer queue events */
156void DecBufferQueueCallback(
157        SLAndroidSimpleBufferQueueItf queueItf,
158        void *pContext)
159{
160    counter++;
161    fprintf(stdout, "DecBufferQueueCallback called (iteration %d)   ", counter);
162
163    CallbackCntxt *pCntxt = (CallbackCntxt*)pContext;
164
165    /* Save the decoded data  */
166    if (fwrite(pCntxt->pDataBase, 1, BUFFER_SIZE_IN_BYTES, gFp) < BUFFER_SIZE_IN_BYTES) {
167        fprintf(stdout, "Error writing to output file, signaling EOS\n");
168        SignalEos();
169        return;
170    }
171
172    /* Increase data pointer by buffer size */
173    pCntxt->pData += BUFFER_SIZE_IN_BYTES;
174
175    if (pCntxt->pData >= pCntxt->pDataBase + (NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES)) {
176        pCntxt->pData = pCntxt->pDataBase;
177    }
178
179    ExitOnError( (*queueItf)->Enqueue(queueItf, pCntxt->pDataBase, BUFFER_SIZE_IN_BYTES) );
180    // Note: adding a sleep here or any sync point is a way to slow down the decoding, or
181    //  synchronize it with some other event, as the OpenSL ES framework will block until the
182    //  buffer queue callback return to proceed with the decoding.
183
184#if 0
185    /* Example buffer queue state display */
186    SLAndroidSimpleBufferQueueState decQueueState;
187    ExitOnError( (*queueItf)->GetState(queueItf, &decQueueState) );
188
189    fprintf(stderr, "\DecBufferQueueCallback now has pCntxt->pData=%p queue: "
190            "count=%u playIndex=%u\n",
191            pCntxt->pData, decQueueState.count, decQueueState.index);
192#endif
193
194#if 0
195    /* Example of duration display in callback where we use the callback context for the SLPlayItf*/
196    SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
197    SLresult result = (*pCntxt->playItf)->GetDuration(pCntxt->playItf, &durationInMsec);
198    ExitOnError(result);
199    if (durationInMsec == SL_TIME_UNKNOWN) {
200        fprintf(stdout, "Content duration is unknown (in dec callback)\n");
201    } else {
202        fprintf(stdout, "Content duration is %ums (in dec callback)\n",
203                durationInMsec);
204    }
205#endif
206}
207
208//-----------------------------------------------------------------
209
210/* Decode an audio path by opening a file descriptor on that path  */
211void TestDecToBuffQueue( SLObjectItf sl, const char* path)
212{
213    size_t len = strlen((const char *) path);
214    char* outputPath = (char*) malloc(len + 4 + 1); // save room to concatenate ".raw"
215    if (NULL == outputPath) {
216        ExitOnError(SL_RESULT_RESOURCE_ERROR);
217    }
218    memcpy(outputPath, path, len + 1);
219    strcat(outputPath, ".raw");
220    gFp = fopen(outputPath, "w");
221    if (NULL == gFp) {
222        ExitOnError(SL_RESULT_RESOURCE_ERROR);
223    }
224
225    SLresult  result;
226    SLEngineItf EngineItf;
227
228    /* Objects this application uses: one audio player */
229    SLObjectItf  player;
230
231    /* Interfaces for the audio player */
232    SLAndroidSimpleBufferQueueItf decBuffQueueItf;
233    SLPrefetchStatusItf           prefetchItf;
234    SLPlayItf                     playItf;
235    SLMetadataExtractionItf       mdExtrItf;
236
237    /* Source of audio data for the decoding */
238    SLDataSource      decSource;
239    SLDataLocator_URI decUri;
240    SLDataFormat_MIME decMime;
241
242    /* Data sink for decoded audio */
243    SLDataSink                decDest;
244    SLDataLocator_AndroidSimpleBufferQueue decBuffQueue;
245    SLDataFormat_PCM          pcm;
246
247    SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
248    SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
249
250    /* Get the SL Engine Interface which is implicit */
251    result = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
252    ExitOnError(result);
253
254    /* Initialize arrays required[] and iidArray[] */
255    for (int i=0 ; i < NUM_EXPLICIT_INTERFACES_FOR_PLAYER ; i++) {
256        required[i] = SL_BOOLEAN_FALSE;
257        iidArray[i] = SL_IID_NULL;
258    }
259
260
261    /* ------------------------------------------------------ */
262    /* Configuration of the player  */
263
264    /* Request the AndroidSimpleBufferQueue interface */
265    required[0] = SL_BOOLEAN_TRUE;
266    iidArray[0] = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
267    /* Request the PrefetchStatus interface */
268    required[1] = SL_BOOLEAN_TRUE;
269    iidArray[1] = SL_IID_PREFETCHSTATUS;
270    /* Request the PrefetchStatus interface */
271    required[2] = SL_BOOLEAN_TRUE;
272    iidArray[2] = SL_IID_METADATAEXTRACTION;
273
274    /* Setup the data source */
275    decUri.locatorType = SL_DATALOCATOR_URI;
276    decUri.URI = (SLchar*)path;
277    decMime.formatType = SL_DATAFORMAT_MIME;
278    /*     this is how ignored mime information is specified, according to OpenSL ES spec
279     *     in 9.1.6 SLDataFormat_MIME and 8.23 SLMetadataTraversalItf GetChildInfo */
280    decMime.mimeType      = (SLchar*)NULL;
281    decMime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
282    decSource.pLocator = (void *) &decUri;
283    decSource.pFormat  = (void *) &decMime;
284
285    /* Setup the data sink */
286    decBuffQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
287    decBuffQueue.numBuffers = NB_BUFFERS_IN_QUEUE;
288    /*    set up the format of the data in the buffer queue */
289    pcm.formatType = SL_DATAFORMAT_PCM;
290    // FIXME valid value required but currently ignored
291    pcm.numChannels = 1;
292    pcm.samplesPerSec = SL_SAMPLINGRATE_8;
293    pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
294    pcm.containerSize = 16;
295    pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
296    pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
297
298    decDest.pLocator = (void *) &decBuffQueue;
299    decDest.pFormat = (void * ) &pcm;
300
301    /* Create the audio player */
302    result = (*EngineItf)->CreateAudioPlayer(EngineItf, &player, &decSource, &decDest,
303            NUM_EXPLICIT_INTERFACES_FOR_PLAYER, iidArray, required);
304    ExitOnError(result);
305    fprintf(stdout, "Player created\n");
306
307    /* Realize the player in synchronous mode. */
308    result = (*player)->Realize(player, SL_BOOLEAN_FALSE);
309    ExitOnError(result);
310    fprintf(stdout, "Player realized\n");
311
312    /* Get the play interface which is implicit */
313    result = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
314    ExitOnError(result);
315
316    /* Set up the player callback to get events during the decoding */
317    // FIXME currently ignored
318    result = (*playItf)->SetMarkerPosition(playItf, 2000);
319    ExitOnError(result);
320    result = (*playItf)->SetPositionUpdatePeriod(playItf, 500);
321    ExitOnError(result);
322    result = (*playItf)->SetCallbackEventsMask(playItf,
323            SL_PLAYEVENT_HEADATMARKER | SL_PLAYEVENT_HEADATNEWPOS | SL_PLAYEVENT_HEADATEND);
324    ExitOnError(result);
325    result = (*playItf)->RegisterCallback(playItf, DecCallback, NULL);
326    ExitOnError(result);
327    fprintf(stdout, "Play callback registered\n");
328
329    /* Get the buffer queue interface which was explicitly requested */
330    result = (*player)->GetInterface(player, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
331            (void*)&decBuffQueueItf);
332    ExitOnError(result);
333
334    /* Get the prefetch status interface which was explicitly requested */
335    result = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
336    ExitOnError(result);
337
338    /* ------------------------------------------------------ */
339    /* Initialize the callback and its context for the decoding buffer queue */
340    CallbackCntxt cntxt;
341    cntxt.playItf = playItf;
342    cntxt.pDataBase = (int8_t*)&pcmData;
343    cntxt.pData = cntxt.pDataBase;
344    cntxt.size = sizeof(pcmData);
345    result = (*decBuffQueueItf)->RegisterCallback(decBuffQueueItf, DecBufferQueueCallback, &cntxt);
346    ExitOnError(result);
347
348    /* Enqueue buffers to map the region of memory allocated to store the decoded data */
349    fprintf(stdout,"Enqueueing buffer ");
350    for(int i = 0 ; i < NB_BUFFERS_IN_QUEUE ; i++) {
351        fprintf(stdout,"%d \n", i);
352        result = (*decBuffQueueItf)->Enqueue(decBuffQueueItf, cntxt.pData, BUFFER_SIZE_IN_BYTES);
353        ExitOnError(result);
354        cntxt.pData += BUFFER_SIZE_IN_BYTES;
355    }
356    fprintf(stdout,"\n");
357    cntxt.pData = cntxt.pDataBase;
358
359    /* ------------------------------------------------------ */
360    /* Initialize the callback for prefetch errors, if we can't open the resource to decode */
361    result = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, &prefetchItf);
362    ExitOnError(result);
363    result = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, PREFETCHEVENT_ERROR_CANDIDATE);
364    ExitOnError(result);
365
366    /* ------------------------------------------------------ */
367    /* Prefetch the data so we can get information about the format before starting to decode */
368    /*     1/ cause the player to prefetch the data */
369    result = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PAUSED );
370    ExitOnError(result);
371    /*     2/ block until data has been prefetched */
372    SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
373    SLuint32 timeOutIndex = 50; // time out prefetching after 5s
374    while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) &&
375            !prefetchError) {
376        usleep(10 * 1000);
377        (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus);
378        timeOutIndex--;
379    }
380    if (timeOutIndex == 0 || prefetchError) {
381        fprintf(stderr, "Failure to prefetch data in time, exiting\n");
382        ExitOnError(SL_RESULT_CONTENT_NOT_FOUND);
383    }
384
385    /* ------------------------------------------------------ */
386    /* Display duration */
387    SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
388    result = (*playItf)->GetDuration(playItf, &durationInMsec);
389    ExitOnError(result);
390    if (durationInMsec == SL_TIME_UNKNOWN) {
391        fprintf(stdout, "Content duration is unknown\n");
392    } else {
393        fprintf(stdout, "Content duration is %ums\n", durationInMsec);
394    }
395
396    /* ------------------------------------------------------ */
397    /* Display the metadata obtained from the decoder */
398    //   This is for test / demonstration purposes only where we discover the key and value sizes
399    //   of a PCM decoder. An application that would want to directly get access to those values
400    //   can make assumptions about the size of the keys and their matching values (all SLuint32)
401    result = (*player)->GetInterface(player, SL_IID_METADATAEXTRACTION,
402            (void*)&mdExtrItf);
403    ExitOnError(result);
404    SLuint32 itemCount;
405    result = (*mdExtrItf)->GetItemCount(mdExtrItf, &itemCount);
406    SLuint32 i, keySize, valueSize;
407    SLMetadataInfo *keyInfo, *value;
408    for(i=0 ; i<itemCount ; i++) {
409        keyInfo = NULL; keySize = 0;
410        value = NULL;   valueSize = 0;
411        result = (*mdExtrItf)->GetKeySize(mdExtrItf, i, &keySize);
412        ExitOnError(result);
413        result = (*mdExtrItf)->GetValueSize(mdExtrItf, i, &valueSize);
414        ExitOnError(result);
415        keyInfo = (SLMetadataInfo*) malloc(keySize);
416        value   = (SLMetadataInfo*) malloc(valueSize);
417        if ((NULL != keyInfo) && (NULL != value)) {
418            result = (*mdExtrItf)->GetKey(mdExtrItf, i, keySize, keyInfo);
419            ExitOnError(result);
420            result = (*mdExtrItf)->GetValue(mdExtrItf, i, valueSize, value);
421            ExitOnError(result);
422            if ((value->encoding == SL_CHARACTERENCODING_BINARY)
423                    && (value->size == sizeof(SLuint32))) {
424                fprintf(stdout, "key[%d] size=%d, name=%s \tvalue size=%d value=%d\n",
425                        i, keyInfo->size, keyInfo->data, value->size, *((SLuint32*)value->data));
426            }
427            free(keyInfo);
428            free(value);
429        }
430    }
431
432    /* ------------------------------------------------------ */
433    /* Start decoding */
434    result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
435    ExitOnError(result);
436    fprintf(stdout, "Starting to decode\n");
437
438    /* Decode until the end of the stream is reached */
439    {
440        android::Mutex::Autolock autoLock(eosLock);
441        while (!eos) {
442            eosCondition.wait(eosLock);
443        }
444    }
445    fprintf(stdout, "EOS signaled\n");
446
447    /* ------------------------------------------------------ */
448    /* End of decoding */
449
450    /* Stop decoding */
451    result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
452    ExitOnError(result);
453    fprintf(stdout, "Stopped decoding\n");
454
455    /* Destroy the AudioPlayer object */
456    (*player)->Destroy(player);
457
458    //sleep(1);
459
460    fclose(gFp);
461}
462
463//-----------------------------------------------------------------
464int main(int argc, char* const argv[])
465{
466    SLresult    result;
467    SLObjectItf sl;
468
469    fprintf(stdout, "OpenSL ES test %s: exercises SLPlayItf and SLAndroidSimpleBufferQueueItf ",
470            argv[0]);
471    fprintf(stdout, "on an AudioPlayer object to decode a URI to PCM\n");
472
473    if (argc != 2) {
474        fprintf(stdout, "Usage: \t%s source_file\n", argv[0]);
475        fprintf(stdout, "Example: \"%s /sdcard/myFile.mp3\n", argv[0]);
476        exit(EXIT_FAILURE);
477    }
478
479    SLEngineOption EngineOption[] = {
480            {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}
481    };
482
483    result = slCreateEngine( &sl, 1, EngineOption, 0, NULL, NULL);
484    ExitOnError(result);
485
486    /* Realizing the SL Engine in synchronous mode. */
487    result = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
488    ExitOnError(result);
489
490    TestDecToBuffQueue(sl, argv[1]);
491
492    /* Shutdown OpenSL ES */
493    (*sl)->Destroy(sl);
494
495    return EXIT_SUCCESS;
496}
497