X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fopensles_android.c;h=73bdac2e21f3b1bc9dc471d60ae24c16efa5ad09;hb=c42f8e52f94176aa69e92ded29717c99364002e5;hp=3b32f7c265c60162e7f88c3738eb7c0847a8ebc7;hpb=a40a943927df92302930cba792f1afa13680bb14;p=vlc diff --git a/modules/audio_output/opensles_android.c b/modules/audio_output/opensles_android.c index 3b32f7c265..73bdac2e21 100644 --- a/modules/audio_output/opensles_android.c +++ b/modules/audio_output/opensles_android.c @@ -35,11 +35,30 @@ #include #include #include +#include // For native audio #include #include +#define OPENSLES_BUFFERS 255 /* maximum number of buffers */ +#define OPENSLES_BUFLEN 10 /* ms */ +/* + * 10ms of precision when mesasuring latency should be enough, + * with 255 buffers we can buffer 2.55s of audio. + */ + +#define CHECK_OPENSL_ERROR(msg) \ + if (unlikely(result != SL_RESULT_SUCCESS)) \ + { \ + msg_Err(aout, msg" (%lu)", result); \ + goto error; \ + } + +typedef SLresult (*slCreateEngine_t)( + SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, + const SLInterfaceID*, const SLboolean*); + #define Destroy(a) (*a)->Destroy(a); #define SetPlayState(a, b) (*a)->SetPlayState(a, b) #define RegisterCallback(a, b, c) (*a)->RegisterCallback(a, b, c) @@ -51,315 +70,302 @@ #define Enqueue(a, b, c) (*a)->Enqueue(a, b, c) #define Clear(a) (*a)->Clear(a) #define GetState(a, b) (*a)->GetState(a, b) +#define SetPositionUpdatePeriod(a, b) (*a)->SetPositionUpdatePeriod(a, b) +#define SetVolumeLevel(a, b) (*a)->SetVolumeLevel(a, b) +#define SetMute(a, b) (*a)->SetMute(a, b) /***************************************************************************** - * aout_sys_t: audio output method descriptor - ***************************************************************************** - * This structure is part of the audio output thread descriptor. - * It describes the direct sound specific properties of an audio device. + * *****************************************************************************/ struct aout_sys_t { + /* OpenSL objects */ SLObjectItf engineObject; SLObjectItf outputMixObject; SLAndroidSimpleBufferQueueItf playerBufferQueue; SLObjectItf playerObject; - + SLVolumeItf volumeItf; + SLEngineItf engineEngine; SLPlayItf playerPlay; + /* OpenSL symbols */ + void *p_so_handle; + + slCreateEngine_t slCreateEnginePtr; + SLInterfaceID SL_IID_ENGINE; + SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + SLInterfaceID SL_IID_VOLUME; + SLInterfaceID SL_IID_PLAY; + + /* */ + vlc_mutex_t lock; - mtime_t length; /* audio buffered through opensles */ - block_t *p_chain; - block_t **pp_last; + uint8_t *buf; + size_t samples_per_buf; + int next_buf; + + int rate; + + /* if we can measure latency already */ + bool started; /* audio not yet buffered through opensles */ block_t *p_buffer_chain; block_t **pp_buffer_last; - - void *p_so_handle; - audio_sample_format_t format; + size_t samples; }; /***************************************************************************** * Local prototypes. *****************************************************************************/ -static int Open ( vlc_object_t * ); +static int Open (vlc_object_t *); +static void Close (vlc_object_t *); /***************************************************************************** * Module descriptor *****************************************************************************/ vlc_module_begin () - set_description( N_("OpenSLES audio output") ) - set_shortname( N_("OpenSLES") ) - set_category( CAT_AUDIO ) - set_subcategory( SUBCAT_AUDIO_AOUT ) - - set_capability( "audio output", 170 ) - add_shortcut( "opensles", "android" ) - set_callbacks( Open, NULL ) + set_description(N_("OpenSLES audio output")) + set_shortname(N_("OpenSLES")) + set_category(CAT_AUDIO) + set_subcategory(SUBCAT_AUDIO_AOUT) + + set_capability("audio output", 170) + add_shortcut("opensles", "android") + set_callbacks(Open, Close) vlc_module_end () +/***************************************************************************** + * + *****************************************************************************/ -static void Clean( aout_sys_t *p_sys ) +static inline int bytesPerSample(void) { - if( p_sys->playerObject ) - Destroy( p_sys->playerObject ); - if( p_sys->outputMixObject ) - Destroy( p_sys->outputMixObject ); - if( p_sys->engineObject ) - Destroy( p_sys->engineObject ); + return 2 /* S16 */ * 2 /* stereo */; +} - if( p_sys->p_so_handle ) - dlclose( p_sys->p_so_handle ); +static int TimeGet(audio_output_t* aout, mtime_t* restrict drift) +{ + aout_sys_t *sys = aout->sys; + + SLAndroidSimpleBufferQueueState st; + SLresult res = GetState(sys->playerBufferQueue, &st); + if (unlikely(res != SL_RESULT_SUCCESS)) { + msg_Err(aout, "Could not query buffer queue state in TimeGet (%lu)", res); + return -1; + } + + vlc_mutex_lock(&sys->lock); + bool started = sys->started; + vlc_mutex_unlock(&sys->lock); + + if (!started) + return -1; - free( p_sys ); + *drift = (CLOCK_FREQ * OPENSLES_BUFLEN * st.count / 1000) + + sys->samples * CLOCK_FREQ / sys->rate; + + msg_Dbg(aout, "latency %"PRId64" ms, %d/%d buffers", *drift / 1000, + (int)st.count, OPENSLES_BUFFERS); + + return 0; } -static void Flush(audio_output_t *p_aout, bool drain) +static void Flush(audio_output_t *aout, bool drain) { - aout_sys_t *p_sys = p_aout->sys; + aout_sys_t *sys = aout->sys; if (drain) { mtime_t delay; - vlc_mutex_lock( &p_sys->lock ); - delay = p_sys->length; - vlc_mutex_unlock( &p_sys->lock ); - msleep(delay); + if (!TimeGet(aout, &delay)) + msleep(delay); } else { - vlc_mutex_lock( &p_sys->lock ); - SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_STOPPED ); - Clear( p_sys->playerBufferQueue ); - SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_PLAYING ); - - p_sys->length = 0; + vlc_mutex_lock(&sys->lock); + SetPlayState(sys->playerPlay, SL_PLAYSTATE_STOPPED); + Clear(sys->playerBufferQueue); + SetPlayState(sys->playerPlay, SL_PLAYSTATE_PLAYING); /* release audio data not yet written to opensles */ - block_ChainRelease( p_sys->p_buffer_chain ); - p_sys->p_buffer_chain = NULL; - p_sys->pp_buffer_last = &p_sys->p_buffer_chain; + block_ChainRelease(sys->p_buffer_chain); + sys->p_buffer_chain = NULL; + sys->pp_buffer_last = &sys->p_buffer_chain; - /* release audio data written to opensles, but not yet - * played on hardware */ - block_ChainRelease( p_sys->p_chain ); - p_sys->p_chain = NULL; - p_sys->pp_last = &p_sys->p_chain; + sys->samples = 0; + sys->started = false; - vlc_mutex_unlock( &p_sys->lock ); + vlc_mutex_unlock(&sys->lock); } } -static void Pause(audio_output_t *p_aout, bool pause, mtime_t date) +static int VolumeSet(audio_output_t *aout, float vol) { - (void)date; - aout_sys_t *p_sys = p_aout->sys; - SetPlayState( p_sys->playerPlay, - pause ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_PLAYING ); + /* Convert UI volume to linear factor (cube) */ + vol = vol * vol * vol; + + /* millibels from linear amplification */ + int mb = lroundf(2000.f * log10f(vol)); + if (mb < SL_MILLIBEL_MIN) + mb = SL_MILLIBEL_MIN; + else if (mb > 0) + mb = 0; /* maximum supported level could be higher: GetMaxVolumeLevel */ + + SLresult r = SetVolumeLevel(aout->sys->volumeItf, mb); + return (r == SL_RESULT_SUCCESS) ? 0 : -1; } -static int TimeGet(audio_output_t* p_aout, mtime_t* restrict drift) +static int MuteSet(audio_output_t *aout, bool mute) { - aout_sys_t *p_sys = p_aout->sys; - - vlc_mutex_lock( &p_sys->lock ); - mtime_t delay = p_sys->length; - vlc_mutex_unlock( &p_sys->lock ); - - SLAndroidSimpleBufferQueueState st; - SLresult res = GetState(p_sys->playerBufferQueue, &st); - if (unlikely(res != SL_RESULT_SUCCESS)) { - msg_Err(p_aout, "Could not query buffer queue state in TimeGet (%lu)", res); - return -1; - } + SLresult r = SetMute(aout->sys->volumeItf, mute); + return (r == SL_RESULT_SUCCESS) ? 0 : -1; +} - *drift = (delay && st.count) ? delay : 0; - return 0; +static void Pause(audio_output_t *aout, bool pause, mtime_t date) +{ + (void)date; + aout_sys_t *sys = aout->sys; + SetPlayState(sys->playerPlay, + pause ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_PLAYING); } -static int WriteBuffer(audio_output_t *p_aout) +static int WriteBuffer(audio_output_t *aout) { - aout_sys_t *p_sys = p_aout->sys; + aout_sys_t *sys = aout->sys; + const size_t unit_size = sys->samples_per_buf * bytesPerSample(); - block_t *b = p_sys->p_buffer_chain; + block_t *b = sys->p_buffer_chain; if (!b) return false; - if (!b->i_length) - b->i_length = (mtime_t)(b->i_buffer / 2 / p_sys->format.i_channels) * CLOCK_FREQ / p_sys->format.i_rate; - - /* If something bad happens, we must remove this buffer from the FIFO */ - block_t **pp_last_saved = p_sys->pp_last; - block_t *p_last_saved = *pp_last_saved; - block_t *next_saved = b->p_next; - b->p_next = NULL; + /* Check if we can fill at least one buffer unit by chaining blocks */ + if (b->i_buffer < unit_size) { + if (!b->p_next) + return false; + ssize_t needed = unit_size - b->i_buffer; + for (block_t *next = b->p_next; next; next = next->p_next) { + needed -= next->i_buffer; + if (needed <= 0) + break; + } + + if (needed > 0) + return false; + } - /* Put this block in the list of audio already written to opensles */ - block_ChainLastAppend( &p_sys->pp_last, b ); + SLAndroidSimpleBufferQueueState st; + SLresult res = GetState(sys->playerBufferQueue, &st); + if (unlikely(res != SL_RESULT_SUCCESS)) { + msg_Err(aout, "Could not query buffer queue state in %s (%lu)", __func__, res); + return false; + } - mtime_t len = b->i_length; - p_sys->length += len; - block_t *next = b->p_next; + if (st.count == OPENSLES_BUFFERS) + return false; - vlc_mutex_unlock( &p_sys->lock ); - SLresult r = Enqueue( p_sys->playerBufferQueue, b->p_buffer, b->i_buffer ); - vlc_mutex_lock( &p_sys->lock ); + size_t done = 0; + while (done < unit_size) { + size_t cur = b->i_buffer; + if (cur > unit_size - done) + cur = unit_size - done; + + memcpy(&sys->buf[unit_size * sys->next_buf + done], b->p_buffer, cur); + b->i_buffer -= cur; + b->p_buffer += cur; + done += cur; + + block_t *next = b->p_next; + if (b->i_buffer == 0) { + block_Release(b); + b = NULL; + } + + if (done == unit_size) + break; + else + b = next; + } - if (r == SL_RESULT_SUCCESS) { - /* Remove that block from the list of audio not yet written */ - p_sys->p_buffer_chain = next; - if (!p_sys->p_buffer_chain) - p_sys->pp_buffer_last = &p_sys->p_buffer_chain; - } else { - /* Remove that block from the list of audio already written */ - msg_Err( p_aout, "error %lu%s (%d bytes)", r, (r == SL_RESULT_BUFFER_INSUFFICIENT) - ? " buffer insufficient" - : "", b->i_buffer); + sys->p_buffer_chain = b; + if (!b) + sys->pp_buffer_last = &sys->p_buffer_chain; - p_sys->pp_last = pp_last_saved; - *pp_last_saved = p_last_saved; + SLresult r = Enqueue(sys->playerBufferQueue, + &sys->buf[unit_size * sys->next_buf], unit_size); - b->p_next = next_saved; + sys->samples -= sys->samples_per_buf; - p_sys->length -= len; - next = NULL; /* We'll try again next time */ + if (r == SL_RESULT_SUCCESS) { + if (++sys->next_buf == OPENSLES_BUFFERS) + sys->next_buf = 0; + return true; + } else { + /* XXX : if writing fails, we don't retry */ + msg_Err(aout, "error %lu when writing %d bytes %s", + r, b->i_buffer, + (r == SL_RESULT_BUFFER_INSUFFICIENT) ? " (buffer insufficient)" : ""); + return false; } - - return next != NULL; } /***************************************************************************** * Play: play a sound *****************************************************************************/ -static void Play( audio_output_t *p_aout, block_t *p_buffer ) +static void Play(audio_output_t *aout, block_t *p_buffer) { - aout_sys_t *p_sys = p_aout->sys; - - SLAndroidSimpleBufferQueueState st; - SLresult res = GetState(p_sys->playerBufferQueue, &st); - if (unlikely(res != SL_RESULT_SUCCESS)) { - msg_Err(p_aout, "Could not query buffer queue state (%lu)", res); - st.count = 0; - } + aout_sys_t *sys = aout->sys; p_buffer->p_next = NULL; /* Make sur our linked list doesn't use old references */ - vlc_mutex_lock(&p_sys->lock); - block_ChainLastAppend( &p_sys->pp_buffer_last, p_buffer ); - while (WriteBuffer(p_aout)) - ; - vlc_mutex_unlock( &p_sys->lock ); -} - -static void PlayedCallback (SLAndroidSimpleBufferQueueItf caller, void *pContext ) -{ - (void)caller; - block_t *p_block; - audio_output_t *p_aout = pContext; - aout_sys_t *p_sys = p_aout->sys; + vlc_mutex_lock(&sys->lock); - assert (caller == p_sys->playerBufferQueue); + sys->samples += p_buffer->i_buffer / bytesPerSample(); - vlc_mutex_lock( &p_sys->lock ); + /* Hold this block until we can write it into the OpenSL buffer */ + block_ChainLastAppend(&sys->pp_buffer_last, p_buffer); - p_block = p_sys->p_chain; - assert( p_block ); + /* Fill OpenSL buffer */ + while (WriteBuffer(aout)) + ; - p_sys->p_chain = p_sys->p_chain->p_next; - /* if we exhausted our fifo, we must reset the pointer to the last - * appended block */ - if (!p_sys->p_chain) - p_sys->pp_last = &p_sys->p_chain; + vlc_mutex_unlock(&sys->lock); +} - p_sys->length -= p_block->i_length; +static void PlayedCallback (SLAndroidSimpleBufferQueueItf caller, void *pContext) +{ + (void)caller; + audio_output_t *aout = pContext; + aout_sys_t *sys = aout->sys; - vlc_mutex_unlock( &p_sys->lock ); + assert (caller == sys->playerBufferQueue); - block_Release( p_block ); + vlc_mutex_lock(&sys->lock); + sys->started = true; + vlc_mutex_unlock(&sys->lock); } /***************************************************************************** - * Open + * *****************************************************************************/ -static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) +static void Clean(aout_sys_t *sys) { - SLresult result; - SLEngineItf engineEngine; - - /* Allocate structure */ - p_aout->sys = calloc( 1, sizeof( aout_sys_t ) ); - if( unlikely( p_aout->sys == NULL ) ) - return VLC_ENOMEM; - - aout_sys_t *p_sys = p_aout->sys; - - //Acquiring LibOpenSLES symbols : - p_sys->p_so_handle = dlopen( "libOpenSLES.so", RTLD_NOW ); - if( p_sys->p_so_handle == NULL ) - { - msg_Err( p_aout, "Failed to load libOpenSLES" ); - goto error; - } - - typedef SLresult (*slCreateEngine_t)( - SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, - const SLInterfaceID*, const SLboolean* ); - slCreateEngine_t slCreateEnginePtr = NULL; - - SLInterfaceID *SL_IID_ENGINE; - SLInterfaceID *SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - SLInterfaceID *SL_IID_VOLUME; - SLInterfaceID *SL_IID_PLAY; - -#define OPENSL_DLSYM( dest, handle, name ) \ - dest = dlsym( handle, name ); \ - if( dest == NULL ) \ - { \ - msg_Err( p_aout, "Failed to load symbol %s", name ); \ - goto error; \ - } - - OPENSL_DLSYM( slCreateEnginePtr, p_sys->p_so_handle, "slCreateEngine" ); - OPENSL_DLSYM( SL_IID_ANDROIDSIMPLEBUFFERQUEUE, p_sys->p_so_handle, - "SL_IID_ANDROIDSIMPLEBUFFERQUEUE" ); - OPENSL_DLSYM( SL_IID_ENGINE, p_sys->p_so_handle, "SL_IID_ENGINE" ); - OPENSL_DLSYM( SL_IID_PLAY, p_sys->p_so_handle, "SL_IID_PLAY" ); - OPENSL_DLSYM( SL_IID_VOLUME, p_sys->p_so_handle, "SL_IID_VOLUME" ); - - -#define CHECK_OPENSL_ERROR( msg ) \ - if( unlikely( result != SL_RESULT_SUCCESS ) ) \ - { \ - msg_Err( p_aout, msg" (%lu)", result ); \ - goto error; \ - } - - // create engine - result = slCreateEnginePtr( &p_sys->engineObject, 0, NULL, 0, NULL, NULL ); - CHECK_OPENSL_ERROR( "Failed to create engine" ); - - // realize the engine in synchronous mode - result = Realize( p_sys->engineObject, SL_BOOLEAN_FALSE ); - CHECK_OPENSL_ERROR( "Failed to realize engine" ); - - // get the engine interface, needed to create other objects - result = GetInterface( p_sys->engineObject, *SL_IID_ENGINE, &engineEngine ); - CHECK_OPENSL_ERROR( "Failed to get the engine interface" ); - - // create output mix, with environmental reverb specified as a non-required interface - const SLInterfaceID ids1[] = { *SL_IID_VOLUME }; - const SLboolean req1[] = { SL_BOOLEAN_FALSE }; - result = CreateOutputMix( engineEngine, &p_sys->outputMixObject, 1, ids1, req1 ); - CHECK_OPENSL_ERROR( "Failed to create output mix" ); + if (sys->playerObject) + Destroy(sys->playerObject); + if (sys->outputMixObject) + Destroy(sys->outputMixObject); + if (sys->engineObject) + Destroy(sys->engineObject); +} - // realize the output mix in synchronous mode - result = Realize( p_sys->outputMixObject, SL_BOOLEAN_FALSE ); - CHECK_OPENSL_ERROR( "Failed to realize output mix" ); +static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt) +{ + SLresult result; + aout_sys_t *sys = aout->sys; // configure audio source - this defines the number of samples you can enqueue. SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, - 255 // Maximum number of buffers to enqueue. + OPENSLES_BUFFERS }; SLDataFormat_PCM format_pcm; @@ -376,83 +382,174 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) // configure audio sink SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, - p_sys->outputMixObject + sys->outputMixObject }; SLDataSink audioSnk = {&loc_outmix, NULL}; //create audio player - const SLInterfaceID ids2[] = { *SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; - static const SLboolean req2[] = { SL_BOOLEAN_TRUE }; - result = CreateAudioPlayer( engineEngine, &p_sys->playerObject, &audioSrc, - &audioSnk, sizeof( ids2 ) / sizeof( *ids2 ), - ids2, req2 ); - CHECK_OPENSL_ERROR( "Failed to create audio player" ); + const SLInterfaceID ids2[] = { sys->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, sys->SL_IID_VOLUME }; + static const SLboolean req2[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; + result = CreateAudioPlayer(sys->engineEngine, &sys->playerObject, &audioSrc, + &audioSnk, sizeof(ids2) / sizeof(*ids2), + ids2, req2); + CHECK_OPENSL_ERROR("Failed to create audio player"); - result = Realize( p_sys->playerObject, SL_BOOLEAN_FALSE ); - CHECK_OPENSL_ERROR( "Failed to realize player object." ); + result = Realize(sys->playerObject, SL_BOOLEAN_FALSE); + CHECK_OPENSL_ERROR("Failed to realize player object."); - result = GetInterface( p_sys->playerObject, *SL_IID_PLAY, &p_sys->playerPlay ); - CHECK_OPENSL_ERROR( "Failed to get player interface." ); + result = GetInterface(sys->playerObject, sys->SL_IID_PLAY, &sys->playerPlay); + CHECK_OPENSL_ERROR("Failed to get player interface."); - result = GetInterface( p_sys->playerObject, *SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &p_sys->playerBufferQueue ); - CHECK_OPENSL_ERROR( "Failed to get buff queue interface" ); + result = GetInterface(sys->playerObject, sys->SL_IID_VOLUME, &sys->volumeItf); + CHECK_OPENSL_ERROR("failed to get volume interface."); - result = RegisterCallback( p_sys->playerBufferQueue, PlayedCallback, - (void*)p_aout); - CHECK_OPENSL_ERROR( "Failed to register buff queue callback." ); + result = GetInterface(sys->playerObject, sys->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &sys->playerBufferQueue); + CHECK_OPENSL_ERROR("Failed to get buff queue interface"); + result = RegisterCallback(sys->playerBufferQueue, PlayedCallback, + (void*)aout); + CHECK_OPENSL_ERROR("Failed to register buff queue callback."); // set the player's state to playing - result = SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_PLAYING ); - CHECK_OPENSL_ERROR( "Failed to switch to playing state" ); + result = SetPlayState(sys->playerPlay, SL_PLAYSTATE_PLAYING); + CHECK_OPENSL_ERROR("Failed to switch to playing state"); + + /* XXX: rounding shouldn't affect us at normal sampling rate */ + sys->rate = fmt->i_rate; + sys->samples_per_buf = OPENSLES_BUFLEN * fmt->i_rate / 1000; + sys->buf = malloc(OPENSLES_BUFFERS * sys->samples_per_buf * bytesPerSample()); + if (!sys->buf) + goto error; - vlc_mutex_init( &p_sys->lock ); - p_sys->p_chain = NULL; - p_sys->pp_last = &p_sys->p_chain; - p_sys->p_buffer_chain = NULL; - p_sys->pp_buffer_last = &p_sys->p_buffer_chain; + sys->started = false; + sys->next_buf = 0; - // we want 16bit signed data little endian. - fmt->i_format = VLC_CODEC_S16L; + sys->p_buffer_chain = NULL; + sys->pp_buffer_last = &sys->p_buffer_chain; + sys->samples = 0; + + // we want 16bit signed data native endian. + fmt->i_format = VLC_CODEC_S16N; fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; - p_aout->play = Play; - p_aout->pause = Pause; - p_aout->flush = Flush; - aout_FormatPrepare( fmt ); + SetPositionUpdatePeriod(sys->playerPlay, AOUT_MIN_PREPARE_TIME * 1000 / CLOCK_FREQ); - p_sys->format = *fmt; + aout_FormatPrepare(fmt); return VLC_SUCCESS; + error: - Clean( p_sys ); + Clean(sys); return VLC_EGENERIC; } +static void Stop(audio_output_t *aout) +{ + aout_sys_t *sys = aout->sys; + + SetPlayState(sys->playerPlay, SL_PLAYSTATE_STOPPED); + //Flush remaining buffers if any. + Clear(sys->playerBufferQueue); + + free(sys->buf); + block_ChainRelease(sys->p_buffer_chain); +} + /***************************************************************************** - * Close + * *****************************************************************************/ -static void Stop( audio_output_t *p_aout ) +static void Close(vlc_object_t *obj) { - aout_sys_t *p_sys = p_aout->sys; + audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys = aout->sys; - SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_STOPPED ); - //Flush remaining buffers if any. - Clear( p_sys->playerBufferQueue ); - block_ChainRelease( p_sys->p_chain ); - block_ChainRelease( p_sys->p_buffer_chain); - vlc_mutex_destroy( &p_sys->lock ); - Clean( p_sys ); + Clean(sys); + dlclose(sys->p_so_handle); + vlc_mutex_destroy(&sys->lock); + free(sys); } static int Open (vlc_object_t *obj) { audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys; + SLresult result; + + aout->sys = sys = calloc(1, sizeof(*sys)); + if (unlikely(sys == NULL)) + return VLC_ENOMEM; + + sys->p_so_handle = dlopen("libOpenSLES.so", RTLD_NOW); + if (sys->p_so_handle == NULL) + { + msg_Err(aout, "Failed to load libOpenSLES"); + goto error; + } + + sys->slCreateEnginePtr = dlsym(sys->p_so_handle, "slCreateEngine"); + if (unlikely(sys->slCreateEnginePtr == NULL)) + { + msg_Err(aout, "Failed to load symbol slCreateEngine"); + goto error; + } + +#define OPENSL_DLSYM(dest, name) \ + do { \ + const SLInterfaceID *sym = dlsym(sys->p_so_handle, "SL_IID_"name); \ + if (unlikely(sym == NULL)) \ + { \ + msg_Err(aout, "Failed to load symbol SL_IID_"name); \ + goto error; \ + } \ + sys->dest = *sym; \ + } while(0) + + OPENSL_DLSYM(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, "ANDROIDSIMPLEBUFFERQUEUE"); + OPENSL_DLSYM(SL_IID_ENGINE, "ENGINE"); + OPENSL_DLSYM(SL_IID_PLAY, "PLAY"); + OPENSL_DLSYM(SL_IID_VOLUME, "VOLUME"); +#undef OPENSL_DLSYM + + // create engine + result = sys->slCreateEnginePtr(&sys->engineObject, 0, NULL, 0, NULL, NULL); + CHECK_OPENSL_ERROR("Failed to create engine"); + + // realize the engine in synchronous mode + result = Realize(sys->engineObject, SL_BOOLEAN_FALSE); + CHECK_OPENSL_ERROR("Failed to realize engine"); + + // get the engine interface, needed to create other objects + result = GetInterface(sys->engineObject, sys->SL_IID_ENGINE, &sys->engineEngine); + CHECK_OPENSL_ERROR("Failed to get the engine interface"); + + // create output mix, with environmental reverb specified as a non-required interface + const SLInterfaceID ids1[] = { sys->SL_IID_VOLUME }; + const SLboolean req1[] = { SL_BOOLEAN_FALSE }; + result = CreateOutputMix(sys->engineEngine, &sys->outputMixObject, 1, ids1, req1); + CHECK_OPENSL_ERROR("Failed to create output mix"); + + // realize the output mix in synchronous mode + result = Realize(sys->outputMixObject, SL_BOOLEAN_FALSE); + CHECK_OPENSL_ERROR("Failed to realize output mix"); + + vlc_mutex_init(&sys->lock); + + aout->start = Start; + aout->stop = Stop; + aout->time_get = TimeGet; + aout->play = Play; + aout->pause = Pause; + aout->flush = Flush; + aout->mute_set = MuteSet; + aout->volume_set = VolumeSet; - /* FIXME: set volume/mute here */ - aout->start = Start; - aout->stop = Stop; - aout->time_get = TimeGet; return VLC_SUCCESS; + +error: + Clean(sys); + if (sys->p_so_handle) + dlclose(sys->p_so_handle); + free(sys); + return VLC_EGENERIC; }