X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fopensles_android.c;h=78407307f6ea72c922b0cb68c4b95b6f6b6c3269;hb=d9cf7d2fbd21d3e29e1a88d4b285ff2a687eb629;hp=242cfc5b7c3ce56048399a75bfaf6828aebc9960;hpb=281afe79d3e9ec90b0ba9634ce1937638e029973;p=vlc diff --git a/modules/audio_output/opensles_android.c b/modules/audio_output/opensles_android.c index 242cfc5b7c..78407307f6 100644 --- a/modules/audio_output/opensles_android.c +++ b/modules/audio_output/opensles_android.c @@ -1,22 +1,23 @@ /***************************************************************************** * opensles_android.c : audio output for android native code ***************************************************************************** - * Copyright © 2011 VideoLAN + * Copyright © 2011-2012 VideoLAN * * Authors: Dominique Martinet * Hugo Beauzée-Luyssen + * Rafaël Carré * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ @@ -42,6 +43,18 @@ // Maximum number of buffers to enqueue. #define BUFF_QUEUE 42 +#define Destroy(a) (*a)->Destroy(a); +#define SetPlayState(a, b) (*a)->SetPlayState(a, b) +#define RegisterCallback(a, b, c) (*a)->RegisterCallback(a, b, c) +#define GetInterface(a, b, c) (*a)->GetInterface(a, b, c) +#define Realize(a, b) (*a)->Realize(a, b) +#define CreateOutputMix(a, b, c, d, e) (*a)->CreateOutputMix(a, b, c, d, e) +#define CreateAudioPlayer(a, b, c, d, e, f, g) \ + (*a)->CreateAudioPlayer(a, b, c, d, e, f, g) +#define Enqueue(a, b, c) (*a)->Enqueue(a, b, c) +#define Clear(a) (*a)->Clear(a) +#define GetState(a, b) (*a)->GetState(a, b) + /***************************************************************************** * aout_sys_t: audio output method descriptor ***************************************************************************** @@ -56,28 +69,20 @@ struct aout_sys_t SLObjectItf playerObject; SLPlayItf playerPlay; - aout_buffer_t *p_buffer_array[BUFF_QUEUE]; - int i_toclean_buffer; - int i_toappend_buffer; - - SLInterfaceID *SL_IID_ENGINE; - SLInterfaceID *SL_IID_ANDROIDSIMPLEBUFFERQUEUE; - SLInterfaceID *SL_IID_VOLUME; - SLInterfaceID *SL_IID_PLAY; + + vlc_mutex_t lock; + block_t *p_chain; + block_t **pp_last; + mtime_t length; + void *p_so_handle; + audio_sample_format_t format; }; -typedef SLresult (*slCreateEngine_t)( - SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, - const SLInterfaceID*, const SLboolean* ); - /***************************************************************************** * Local prototypes. *****************************************************************************/ static int Open ( vlc_object_t * ); -static void Close ( vlc_object_t * ); -static void Play ( audio_output_t *, block_t * ); -static void PlayedCallback ( SLAndroidSimpleBufferQueueItf caller, void *pContext); /***************************************************************************** * Module descriptor @@ -91,48 +96,157 @@ vlc_module_begin () set_capability( "audio output", 170 ) add_shortcut( "opensles", "android" ) - set_callbacks( Open, Close ) + set_callbacks( Open, NULL ) vlc_module_end () -#define CHECK_OPENSL_ERROR( res, msg ) \ - if( unlikely( res != SL_RESULT_SUCCESS ) ) \ - { \ - msg_Err( p_aout, msg" (%lu)", res ); \ - goto error; \ - } +static void Clean( aout_sys_t *p_sys ) +{ + if( p_sys->playerObject ) + Destroy( p_sys->playerObject ); + if( p_sys->outputMixObject ) + Destroy( p_sys->outputMixObject ); + if( p_sys->engineObject ) + Destroy( p_sys->engineObject ); -#define OPENSL_DLSYM( dest, handle, name ) \ - dest = (typeof(dest))dlsym( handle, name ); \ - if( dest == NULL ) \ - { \ - msg_Err( p_aout, "Failed to load symbol %s", name ); \ - goto error; \ + if( p_sys->p_so_handle ) + dlclose( p_sys->p_so_handle ); + + free( p_sys ); +} + +static void Flush(audio_output_t *p_aout, bool wait) +{ + (void)wait; /* FIXME */ + aout_sys_t *p_sys = p_aout->sys; + + vlc_mutex_lock( &p_sys->lock ); + SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_STOPPED ); + Clear( p_sys->playerBufferQueue ); + SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_PLAYING ); + block_ChainRelease( p_sys->p_chain ); + p_sys->p_chain = NULL; + p_sys->pp_last = &p_sys->p_chain; + vlc_mutex_unlock( &p_sys->lock ); +} + +static void Pause(audio_output_t *p_aout, bool pause, mtime_t date) +{ + (void)date; + aout_sys_t *p_sys = p_aout->sys; + SetPlayState( p_sys->playerPlay, + pause ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_PLAYING ); +} + +static int TimeGet(audio_output_t* p_aout, mtime_t* restrict drift) +{ + aout_sys_t *p_sys = p_aout->sys; + mtime_t delay = p_sys->length; + 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; } -static void Clear( aout_sys_t *p_sys ) + *drift = (delay && st.count) ? delay : 0; + return 0; +} + +/***************************************************************************** + * Play: play a sound + *****************************************************************************/ +static void Play( audio_output_t *p_aout, block_t *p_buffer ) { - // Destroy buffer queue audio player object - // and invalidate all associated interfaces - (*p_sys->playerObject)->Destroy( p_sys->playerObject ); + aout_sys_t *p_sys = p_aout->sys; + int tries = 5; + block_t **pp_last, *p_block; + + 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; + } + + if (!p_buffer->i_length) { + p_buffer->i_length = (mtime_t)(p_buffer->i_buffer / 2 / p_sys->format.i_channels) * CLOCK_FREQ / p_sys->format.i_rate; + } - // destroy output mix object, and invalidate all associated interfaces - (*p_sys->outputMixObject)->Destroy( p_sys->outputMixObject ); + vlc_mutex_lock( &p_sys->lock ); - // destroy engine object, and invalidate all associated interfaces - (*p_sys->engineObject)->Destroy( p_sys->engineObject ); + p_sys->length += p_buffer->i_length; - dlclose( p_sys->p_so_handle ); + /* If something bad happens, we must remove this buffer from the FIFO */ + pp_last = p_sys->pp_last; + p_block = *pp_last; - free( p_sys ); + block_ChainLastAppend( &p_sys->pp_last, p_buffer ); + vlc_mutex_unlock( &p_sys->lock ); + + for (;;) + { + SLresult result = Enqueue( p_sys->playerBufferQueue, p_buffer->p_buffer, + p_buffer->i_buffer ); + + switch (result) + { + case SL_RESULT_BUFFER_INSUFFICIENT: + msg_Err( p_aout, "buffer insufficient"); + + if (tries--) + { + // Wait a bit to retry. + // FIXME: buffer in aout_sys_t and retry at next Play() call + msleep(CLOCK_FREQ); + continue; + } + + default: + msg_Warn( p_aout, "Error %lu, dropping buffer", result ); + vlc_mutex_lock( &p_sys->lock ); + p_sys->pp_last = pp_last; + *pp_last = p_block; + p_sys->length -= p_buffer->i_length; + vlc_mutex_unlock( &p_sys->lock ); + block_Release( p_buffer ); + case SL_RESULT_SUCCESS: + return; + } + } } +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; + + assert (caller == p_sys->playerBufferQueue); + + vlc_mutex_lock( &p_sys->lock ); + + p_block = p_sys->p_chain; + assert( p_block ); + + 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; + + p_sys->length -= p_block->i_length; + + vlc_mutex_unlock( &p_sys->lock ); + + block_Release( p_block ); +} /***************************************************************************** - * Open: open a dummy audio device + * Open *****************************************************************************/ -static int Open( vlc_object_t *p_this ) +static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) { - audio_output_t *p_aout = (audio_output_t *)p_this; SLresult result; SLEngineItf engineEngine; @@ -151,40 +265,60 @@ static int Open( vlc_object_t *p_this ) 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( p_sys->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, p_sys->p_so_handle, + OPENSL_DLSYM( SL_IID_ANDROIDSIMPLEBUFFERQUEUE, p_sys->p_so_handle, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE" ); - OPENSL_DLSYM( p_sys->SL_IID_ENGINE, p_sys->p_so_handle, "SL_IID_ENGINE" ); - OPENSL_DLSYM( p_sys->SL_IID_PLAY, p_sys->p_so_handle, "SL_IID_PLAY" ); - OPENSL_DLSYM( p_sys->SL_IID_VOLUME, p_sys->p_so_handle, "SL_IID_VOLUME" ); + 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( result, "Failed to create engine" ); + CHECK_OPENSL_ERROR( "Failed to create engine" ); // realize the engine in synchronous mode - result = (*p_sys->engineObject)->Realize( p_sys->engineObject, - SL_BOOLEAN_FALSE ); - CHECK_OPENSL_ERROR( result, "Failed to realize engine" ); + 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 = (*p_sys->engineObject)->GetInterface( p_sys->engineObject, - *p_sys->SL_IID_ENGINE, &engineEngine ); - CHECK_OPENSL_ERROR( result, "Failed to get the engine interface" ); + 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[] = { *p_sys->SL_IID_VOLUME }; + const SLInterfaceID ids1[] = { *SL_IID_VOLUME }; const SLboolean req1[] = { SL_BOOLEAN_FALSE }; - result = (*engineEngine)->CreateOutputMix( engineEngine, - &p_sys->outputMixObject, 1, ids1, req1 ); - CHECK_OPENSL_ERROR( result, "Failed to create output mix" ); + result = CreateOutputMix( engineEngine, &p_sys->outputMixObject, 1, ids1, req1 ); + CHECK_OPENSL_ERROR( "Failed to create output mix" ); // realize the output mix in synchronous mode - result = (*p_sys->outputMixObject)->Realize( p_sys->outputMixObject, - SL_BOOLEAN_FALSE ); - CHECK_OPENSL_ERROR( result, "Failed to realize output mix" ); + result = Realize( p_sys->outputMixObject, SL_BOOLEAN_FALSE ); + CHECK_OPENSL_ERROR( "Failed to realize output mix" ); // configure audio source - this defines the number of samples you can enqueue. @@ -196,7 +330,7 @@ static int Open( vlc_object_t *p_this ) SLDataFormat_PCM format_pcm; format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.numChannels = 2; - format_pcm.samplesPerSec = ((SLuint32) p_aout->format.i_rate * 1000) ; + format_pcm.samplesPerSec = ((SLuint32) fmt->i_rate * 1000) ; format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; @@ -212,117 +346,75 @@ static int Open( vlc_object_t *p_this ) SLDataSink audioSnk = {&loc_outmix, NULL}; //create audio player - const SLInterfaceID ids2[] = { *p_sys->SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; - const SLboolean req2[] = { SL_BOOLEAN_TRUE }; - result = (*engineEngine)->CreateAudioPlayer( engineEngine, - &p_sys->playerObject, &audioSrc, + 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( result, "Failed to create audio player" ); + CHECK_OPENSL_ERROR( "Failed to create audio player" ); - // realize the player - result = (*p_sys->playerObject)->Realize( p_sys->playerObject, - SL_BOOLEAN_FALSE ); - CHECK_OPENSL_ERROR( result, "Failed to realize player object." ); + result = Realize( p_sys->playerObject, SL_BOOLEAN_FALSE ); + CHECK_OPENSL_ERROR( "Failed to realize player object." ); - // get the play interface - result = (*p_sys->playerObject)->GetInterface( p_sys->playerObject, - *p_sys->SL_IID_PLAY, &p_sys->playerPlay ); - CHECK_OPENSL_ERROR( result, "Failed to get player interface." ); + result = GetInterface( p_sys->playerObject, *SL_IID_PLAY, &p_sys->playerPlay ); + CHECK_OPENSL_ERROR( "Failed to get player interface." ); - // get the buffer queue interface - result = (*p_sys->playerObject)->GetInterface( p_sys->playerObject, - *p_sys->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + result = GetInterface( p_sys->playerObject, *SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &p_sys->playerBufferQueue ); - CHECK_OPENSL_ERROR( result, "Failed to get buff queue interface" ); + CHECK_OPENSL_ERROR( "Failed to get buff queue interface" ); - result = (*p_sys->playerBufferQueue)->RegisterCallback( p_sys->playerBufferQueue, - PlayedCallback, - (void*)p_sys); - CHECK_OPENSL_ERROR( result, "Failed to register buff queue callback." ); + result = RegisterCallback( p_sys->playerBufferQueue, PlayedCallback, + (void*)p_aout); + CHECK_OPENSL_ERROR( "Failed to register buff queue callback." ); // set the player's state to playing - result = (*p_sys->playerPlay)->SetPlayState( p_sys->playerPlay, - SL_PLAYSTATE_PLAYING ); - CHECK_OPENSL_ERROR( result, "Failed to switch to playing state" ); + result = SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_PLAYING ); + CHECK_OPENSL_ERROR( "Failed to switch to playing state" ); + + vlc_mutex_init( &p_sys->lock ); + p_sys->p_chain = NULL; + p_sys->pp_last = &p_sys->p_chain; // we want 16bit signed data little endian. - p_aout->format.i_format = VLC_CODEC_S16L; - p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; - p_aout->pf_play = Play; - p_aout->pf_pause = NULL; - p_aout->pf_flush = NULL; + fmt->i_format = VLC_CODEC_S16L; + 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 ); - aout_FormatPrepare( &p_aout->format ); + p_sys->format = *fmt; return VLC_SUCCESS; error: - Clear( p_sys ); + Clean( p_sys ); return VLC_EGENERIC; } /***************************************************************************** - * Close: close our file + * Close *****************************************************************************/ -static void Close( vlc_object_t *p_this ) +static void Stop( audio_output_t *p_aout ) { - audio_output_t *p_aout = (audio_output_t*)p_this; aout_sys_t *p_sys = p_aout->sys; - (*p_sys->playerPlay)->SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_STOPPED ); + SetPlayState( p_sys->playerPlay, SL_PLAYSTATE_STOPPED ); //Flush remaining buffers if any. - (*p_sys->playerBufferQueue)->Clear( p_sys->playerBufferQueue ); - Clear( p_sys ); -} - -/***************************************************************************** - * Play: play a sound - *****************************************************************************/ -static void Play( audio_output_t *p_aout, block_t *p_buffer ) -{ - aout_sys_t *p_sys = p_aout->sys; - int tries = 5; - - for (;;) - { - SLresult result = (*p_sys->playerBufferQueue)->Enqueue( - p_sys->playerBufferQueue, p_buffer->p_buffer, - p_buffer->i_buffer ); - - switch (result) - { - case SL_RESULT_SUCCESS: - p_sys->p_buffer_array[p_sys->i_toappend_buffer] = p_buffer; - if( ++p_sys->i_toappend_buffer == BUFF_QUEUE ) - p_sys->i_toappend_buffer = 0; - return; - - case SL_RESULT_BUFFER_INSUFFICIENT: - msg_Err( p_aout, "buffer insufficient"); - - if (tries--) - { - // Wait a bit to retry. - msleep(CLOCK_FREQ); - continue; - } - - default: - msg_Warn( p_aout, "Error %lu, dropping buffer", result ); - aout_BufferFree( p_buffer ); - return; - } - } + Clear( p_sys->playerBufferQueue ); + block_ChainRelease( p_sys->p_chain ); + vlc_mutex_destroy( &p_sys->lock ); + Clean( p_sys ); } -static void PlayedCallback (SLAndroidSimpleBufferQueueItf caller, void *pContext ) +static int Open (vlc_object_t *obj) { - aout_sys_t *p_sys = pContext; - - assert (caller == p_sys->playerBufferQueue); + audio_output_t *aout = (audio_output_t *)obj; - aout_BufferFree( p_sys->p_buffer_array[p_sys->i_toclean_buffer] ); - if( ++p_sys->i_toclean_buffer == BUFF_QUEUE ) - p_sys->i_toclean_buffer = 0; + /* FIXME: set volume/mute here */ + aout->start = Start; + aout->stop = Stop; + aout->time_get = TimeGet; + return VLC_SUCCESS; }