@ -42,54 +42,38 @@
# include <pulsecore/rtpoll.h>
# include <SLES/OpenSLES.h>
# include <SLES/OpenSLES_Android.h>
//Only certain interfaces are supported by the fast mixer. These are:
//SL_IID_ANDROIDSIMPLEBUFFERQUEUE
//SL_IID_VOLUME
//SL_IID_MUTESOLO
# define USE_ANDROID_SIMPLE_BUFFER_QUEUE
# ifdef USE_ANDROID_SIMPLE_BUFFER_QUEUE
# include <SLES/OpenSLES_Android.h>
# define DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
# define IID_BUFFERQUEUE SL_IID_ANDROIDSIMPLEBUFFERQUEUE
# define BufferQueueItf SLAndroidSimpleBufferQueueItf
# define BufferQueueState SLAndroidSimpleBufferQueueState
# define IID_BUFFERQUEUE_USED SL_IID_ANDROIDSIMPLEBUFFERQUEUE
# define INDEX index
# define DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
# define IID_BUFFERQUEUE SL_IID_ANDROIDSIMPLEBUFFERQUEUE
# define BufferQueueItf SLAndroidSimpleBufferQueueItf
# define BufferQueueState SLAndroidSimpleBufferQueueState
# define IID_BUFFERQUEUE_USED SL_IID_ANDROIDSIMPLEBUFFERQUEUE
# define INDEX index
# else
# define DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_BUFFERQUEUE
# define IID_BUFFERQUEUE SL_IID_BUFFERQUEUE
# define BufferQueueItf SLBufferQueueItf
# define BufferQueueState SLBufferQueueState
# define IID_BUFFERQUEUE_USED IID_BUFFERQUEUE
# define INDEX playIndex
# define DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_BUFFERQUEUE
# define IID_BUFFERQUEUE SL_IID_BUFFERQUEUE
# define BufferQueueItf SLBufferQueueItf
# define BufferQueueState SLBufferQueueState
# define IID_BUFFERQUEUE_USED IID_BUFFERQUEUE
# define INDEX playIndex
# endif
# define checkResult(r) do { \
if ( ( r ) ! = SL_RESULT_SUCCESS ) { \
if ( ( r ) = = SL_RESULT_PARAMETER_INVALID ) fprintf ( stderr , " error SL_RESULT_PARAMETER_INVALID at %s:%d \n " , __FILE__ , __LINE__ ) ; \
else if ( ( r ) = = SL_RESULT_PRECONDITIONS_VIOLATED ) fprintf ( stderr , " error SL_RESULT_PRECONDITIONS_VIOLATED at %s:%d \n " , __FILE__ , __LINE__ ) ; \
else fprintf ( stderr , " error %d at %s:%d \n " , ( int ) r , __FILE__ , __LINE__ ) ; \
} \
} while ( 0 )
PA_MODULE_AUTHOR ( " Lennart Poettering, Nathan Martynov " ) ;
PA_MODULE_DESCRIPTION ( " Android OpenSL ES sink " ) ;
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( false ) ;
PA_MODULE_USAGE (
" sink_name=<name for the sink> "
" sink_properties=<properties for the sink> "
" rate=<sampling rate> " ) ;
" sink_name=<name for the sink> "
" sink_properties=<properties for the sink> "
" rate=<sampling rate> "
) ;
# define DEFAULT_SINK_NAME "OpenSL ES sink"
# define BLOCK_USEC (PA_USEC_PER_SEC * 2)
typedef struct pa_memblock_queue_t {
pa_memblock * memblock ;
struct pa_memblock_queue_t * next ;
} pa_memblock_queue ;
# define BLOCK_USEC (PA_USEC_PER_MSEC * 125)
struct userdata {
pa_core * core ;
@ -101,23 +85,19 @@ struct userdata {
pa_rtpoll * rtpoll ;
pa_usec_t block_usec ;
pa_usec_t timestamp ;
pa_memchunk memchunk ;
SLObjectItf engineObject ;
SLEngineItf engineEngine ;
SLObjectItf engineObject ;
SLEngineItf engineEngine ;
// output mix interfaces
SLObjectItf outputMixObject ;
// output mix interfaces
SLObjectItf outputMixObject ;
// buffer queue player interfaces
SLObjectItf bqPlayerObject ;
SLPlayItf bqPlayerPlay ;
BufferQueueItf bqPlayerBufferQueue ;
pa_memblock_queue * current ;
pa_memblock_queue * last ;
// buffer queue player interfaces
SLObjectItf bqPlayerObject ;
SLPlayItf bqPlayerPlay ;
BufferQueueItf bqPlayerBufferQueue ;
} ;
static const char * const valid_modargs [ ] = {
@ -127,174 +107,111 @@ static const char* const valid_modargs[] = {
NULL
} ;
static int sink_process_msg (
pa_msgobject * o ,
int code ,
void * data ,
int64_t offset ,
pa_memchunk * chunk ) {
struct userdata * u = PA_SINK ( o ) - > userdata ;
static void process_render ( BufferQueueItf bq , void * context ) {
struct userdata * u = ( struct userdata * ) context ;
void * p ;
switch ( code ) {
case PA_SINK_MESSAGE_SET_STATE :
if ( pa_sink_get_state ( u - > sink ) = = PA_SINK_SUSPENDED | | pa_sink_get_state ( u - > sink ) = = PA_SINK_INIT ) {
if ( PA_PTR_TO_UINT ( data ) = = PA_SINK_RUNNING | | PA_PTR_TO_UINT ( data ) = = PA_SINK_IDLE )
u - > timestamp = pa_rtclock_now ( ) ;
}
break ;
case PA_SINK_MESSAGE_GET_LATENCY : {
pa_usec_t now ;
now = pa_rtclock_now ( ) ;
* ( ( pa_usec_t * ) data ) = u - > timestamp > now ? u - > timestamp - now : 0ULL ;
pa_assert ( u ) ;
//pa_log_debug("Called\n");
return 0 ;
}
if ( ! pa_thread_mq_get ( ) ) {
pa_log_debug ( " Thread starting up " ) ;
pa_thread_mq_install ( & u - > thread_mq ) ;
}
return pa_sink_process_msg ( o , code , data , offset , chunk ) ;
}
static void sink_update_requested_latency_cb ( pa_sink * s ) {
struct userdata * u ;
size_t nbytes ;
pa_sink_assert_ref ( s ) ;
pa_assert_se ( u = s - > userdata ) ;
u - > block_usec = pa_sink_get_requested_latency_within_thread ( s ) ;
if ( u - > block_usec = = ( pa_usec_t ) - 1 )
u - > block_usec = s - > thread_info . max_latency ;
if ( PA_UNLIKELY ( u - > sink - > thread_info . rewind_requested ) ) {
//pa_log_debug("Rewinded\n");
pa_sink_process_rewind ( u - > sink , 0 ) ;
}
nbytes = pa_usec_to_bytes ( u - > block_usec , & s - > sample_spec ) ;
pa_sink_set_max_rewind_within_thread ( s , nbytes ) ;
pa_sink_set_max_request_within_thread ( s , nbytes ) ;
pa_sink_render ( u - > sink , u - > sink - > thread_info . max_request , & u - > memchunk ) ;
p = pa_memblock_acquire_chunk ( & u - > memchunk ) ;
( * bq ) - > Enqueue ( bq , p , u - > memchunk . length ) ;
//pa_log_debug("Written: %zu\n", u->memchunk.length);
pa_memblock_release ( u - > memchunk . memblock ) ;
pa_memblock_unref ( u - > memchunk . memblock ) ;
}
static void pa_sles_callback ( BufferQueueItf bq , void * context ) {
struct userdata * s = ( struct userdata * ) context ;
pa_memblock_queue * next ;
if ( s - > current ! = NULL ) {
if ( s - > current - > memblock ! = NULL ) pa_memblock_unref ( s - > current - > memblock ) ;
next = s - > current - > next ;
free ( s - > current ) ;
s - > current = next ;
}
# define CHK(stmt) { \
SLresult res = stmt ; \
if ( res ! = SL_RESULT_SUCCESS ) { \
fprintf ( stderr , " error %d at %s:%d \n " , res , __FILE__ , __LINE__ ) ; \
goto fail ; \
} \
}
static int pa_init_sles_player ( struct userdata * s , SLint32 sl_rate )
{
if ( s = = NULL ) return - 1 ;
SLresult result ;
if ( s = = NULL ) return - 1 ;
// create engine
result = slCreateEngine ( & ( s - > engineObject ) , 0 , NULL , 0 , NULL , NULL ) ; checkResult ( result ) ;
result = ( * s - > engineObject ) - > Realize ( s - > engineObject , SL_BOOLEAN_FALSE ) ; checkResult ( result ) ;
// create engine
CHK ( slCreateEngine ( & ( s - > engineObject ) , 0 , NULL , 0 , NULL , NULL ) ) ;
CHK ( ( * s - > engineObject ) - > Realize ( s - > engineObject , SL_BOOLEAN_FALSE ) ) ;
result = ( * s - > engineObject ) - > GetInterface ( s - > engineObject , SL_IID_ENGINE , & ( s - > engineEngine ) ) ; checkResult ( result ) ;
CHK ( ( * s - > engineObject ) - > GetInterface ( s - > engineObject , SL_IID_ENGINE , & ( s - > engineEngine ) ) ) ;
// create output mix
result = ( * s - > engineEngine ) - > CreateOutputMix ( s - > engineEngine , & ( s - > outputMixObject ) , 0 , NULL , NULL ) ; checkResult ( result ) ;
result = ( * s - > outputMixObject ) - > Realize ( s - > outputMixObject , SL_BOOLEAN_FALSE ) ; checkResult ( result ) ;
// create output mix
CHK ( ( * s - > engineEngine ) - > CreateOutputMix ( s - > engineEngine , & ( s - > outputMixObject ) , 0 , NULL , NULL ) ) ;
CHK ( ( * s - > outputMixObject ) - > Realize ( s - > outputMixObject , SL_BOOLEAN_FALSE ) ) ;
// create audio player
// create audio player
SLDataLocator_OutputMix locator_outputmix ;
locator_outputmix . locatorType = SL_DATALOCATOR_OUTPUTMIX ;
locator_outputmix . outputMix = s - > outputMixObject ;
SLDataLocator_OutputMix locator_outputmix ;
locator_outputmix . locatorType = SL_DATALOCATOR_OUTPUTMIX ;
locator_outputmix . outputMix = s - > outputMixObject ;
SLDataLocator_BufferQueue locator_bufferqueue ;
locator_bufferqueue . locatorType = DATALOCATOR_BUFFERQUEUE ;
locator_bufferqueue . numBuffers = 50 ;
SLDataLocator_BufferQueue locator_bufferqueue ;
locator_bufferqueue . locatorType = DATALOCATOR_BUFFERQUEUE ;
locator_bufferqueue . numBuffers = 1 ;
if ( sl_rate < SL_SAMPLINGRATE_8 | | sl_rate > SL_SAMPLINGRATE_192 ) {
pa_log ( " Incompatible sample rate " ) ;
return - 1 ;
}
if ( sl_rate < 8000 | | sl_rate > 192000 ) {
pa_log ( " Incompatible sample rate " ) ;
return - 1 ;
}
SLDataFormat_PCM pcm ;
pcm . formatType = SL_DATAFORMAT_PCM ;
pcm . numChannels = 2 ;
pcm . samplesPerSec = sl_rate ;
pcm . bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16 ;
pcm . containerSize = 16 ;
pcm . channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT ;
pcm . endianness = SL_BYTEORDER_LITTLEENDIAN ;
SLDataFormat_PCM pcm ;
pcm . formatType = SL_DATAFORMAT_PCM ;
pcm . numChannels = 2 ;
pcm . samplesPerSec = sl_rate * 1000 ;
pcm . bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16 ;
pcm . containerSize = 16 ;
pcm . channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT ;
pcm . endianness = SL_BYTEORDER_LITTLEENDIAN ;
SLDataSource audiosrc ;
audiosrc . pLocator = & locator_bufferqueue ;
audiosrc . pFormat = & pcm ;
SLDataSource audiosrc ;
audiosrc . pLocator = & locator_bufferqueue ;
audiosrc . pFormat = & pcm ;
SLDataSink audiosnk ;
audiosnk . pLocator = & locator_outputmix ;
audiosnk . pFormat = NULL ;
SLDataSink audiosnk ;
audiosnk . pLocator = & locator_outputmix ;
audiosnk . pFormat = NULL ;
SLInterfaceID ids [ 1 ] = { IID_BUFFERQUEUE } ;
SLboolean flags [ 1 ] = { SL_BOOLEAN_TRUE } ;
result = ( * s - > engineEngine ) - > CreateAudioPlayer ( s - > engineEngine , & s - > bqPlayerObject , & audiosrc , & audiosnk , 1 , ids , flags ) ; checkResult ( result ) ;
result = ( * s - > bqPlayerObject ) - > Realize ( s - > bqPlayerObject , SL_BOOLEAN_FALSE ) ; checkResult ( result ) ;
SLInterfaceID ids [ 1 ] = { IID_BUFFERQUEUE } ;
SLboolean flags [ 1 ] = { SL_BOOLEAN_TRUE } ;
CHK ( ( * s - > engineEngine ) - > CreateAudioPlayer ( s - > engineEngine , & s - > bqPlayerObject , & audiosrc , & audiosnk , 1 , ids , flags ) ) ;
CHK ( ( * s - > bqPlayerObject ) - > Realize ( s - > bqPlayerObject , SL_BOOLEAN_FALSE ) ) ;
result = ( * s - > bqPlayerObject ) - > GetInterface ( s - > bqPlayerObject , SL_IID_PLAY , & s - > bqPlayerPlay ) ; checkResult ( result ) ;
result = ( * s - > bqPlayerObject ) - > GetInterface ( s - > bqPlayerObject , IID_BUFFERQUEUE_USED , & s - > bqPlayerBufferQueue ) ; checkResult ( result ) ;
CHK ( ( * s - > bqPlayerObject ) - > GetInterface ( s - > bqPlayerObject , SL_IID_PLAY , & s - > bqPlayerPlay ) ) ;
CHK ( ( * s - > bqPlayerObject ) - > GetInterface ( s - > bqPlayerObject , IID_BUFFERQUEUE_USED , & s - > bqPlayerBufferQueue ) ) ;
result = ( * s - > bqPlayerBufferQueue ) - > RegisterCallback ( s - > bqPlayerBufferQueue , pa_sles_callback , s ) ; checkResult ( result ) ;
CHK ( ( * s - > bqPlayerBufferQueue ) - > RegisterCallback ( s - > bqPlayerBufferQueue , process_render , s ) ) ;
result = ( * s - > bqPlayerPlay ) - > SetPlayState ( s - > bqPlayerPlay , SL_PLAYSTATE_PLAYING ) ; checkResult ( result ) ;
CHK ( ( * s - > bqPlayerPlay ) - > SetPlayState ( s - > bqPlayerPlay , SL_PLAYSTATE_PLAYING ) ) ;
return 0 ;
}
return 0 ;
static void pa_destroy_sles_player ( struct userdata * s ) {
if ( s = = NULL ) return ;
( * s - > bqPlayerPlay ) - > SetPlayState ( s - > bqPlayerPlay , SL_PLAYSTATE_STOPPED ) ;
( * s - > bqPlayerObject ) - > Destroy ( s - > bqPlayerObject ) ;
( * s - > outputMixObject ) - > Destroy ( s - > outputMixObject ) ;
( * s - > engineObject ) - > Destroy ( s - > engineObject ) ;
fail :
return - 1 ;
}
static void process_render ( struct userdata * u , pa_usec_t now ) {
pa_memblock_queue * current_block ;
size_t ate = 0 ;
pa_assert ( u ) ;
# undef CHK
/* This is the configured latency. Sink inputs connected to us
might not have a single frame more than the maxrequest value
queued . Hence : at maximum read this many bytes from the sink
inputs . */
/* Fill the buffer up the latency size */
while ( u - > timestamp < now + u - > block_usec ) {
void * p ;
pa_sink_render ( u - > sink , u - > sink - > thread_info . max_request , & u - > memchunk ) ;
p = pa_memblock_acquire ( u - > memchunk . memblock ) ;
( * u - > bqPlayerBufferQueue ) - > Enqueue ( u - > bqPlayerBufferQueue , ( uint8_t * ) p + u - > memchunk . index , u - > memchunk . length ) ;
pa_memblock_release ( u - > memchunk . memblock ) ;
u - > timestamp + = pa_bytes_to_usec ( u - > memchunk . length , & u - > sink - > sample_spec ) ;
ate + = u - > memchunk . length ;
current_block = malloc ( sizeof ( pa_memblock_queue ) ) ;
memset ( current_block , 0 , sizeof ( pa_memblock_queue ) ) ;
current_block - > memblock = u - > memchunk . memblock ;
if ( u - > current = = NULL ) { u - > current = current_block ; }
if ( u - > last = = NULL ) { u - > last = current_block ; }
else {
u - > last - > next = current_block ;
u - > last = current_block ;
}
//pa_memblock_unref(u->memchunk.memblock);
pa_memchunk_reset ( & u - > memchunk ) ;
if ( ate > = u - > sink - > thread_info . max_request ) break ;
}
static void pa_destroy_sles_player ( struct userdata * s ) {
if ( s = = NULL ) return ;
( * s - > bqPlayerPlay ) - > SetPlayState ( s - > bqPlayerPlay , SL_PLAYSTATE_STOPPED ) ;
( * s - > bqPlayerObject ) - > Destroy ( s - > bqPlayerObject ) ;
( * s - > outputMixObject ) - > Destroy ( s - > outputMixObject ) ;
( * s - > engineObject ) - > Destroy ( s - > engineObject ) ;
}
static void thread_func ( void * userdata ) {
@ -302,30 +219,25 @@ static void thread_func(void *userdata) {
pa_assert ( u ) ;
pa_log_debug ( " Thread starting up " ) ;
pa_thread_mq_install ( & u - > thread_mq ) ;
u - > timestamp = pa_rtclock_now ( ) ;
for ( ; ; ) {
pa_usec_t now = 0 ;
int ret ;
if ( PA_SINK_IS_OPENED ( u - > sink - > thread_info . state ) )
now = pa_rtclock_now ( ) ;
if ( PA_UNLIKELY ( u - > sink - > thread_info . rewind_requested ) )
pa_sink_process_rewind ( u - > sink , 0 ) ;
/* Render some data and drop it immediately */
if ( PA_SINK_IS_OPENED ( u - > sink - > thread_info . state ) ) {
if ( u - > timestamp < = now )
process_render ( u , now ) ;
process_render ( u - > bqPlayerBufferQueue , u ) ;
break ;
}
/* Hmm, nothing to do. Let's sleep */
if ( ( ret = pa_rtpoll_run ( u - > rtpoll ) ) < 0 )
goto fail ;
pa_rtpoll_set_timer_absolute ( u - > rtpoll , u - > timestamp ) ;
} else
pa_rtpoll_set_timer_disabled ( u - > rtpoll ) ;
if ( ret = = 0 )
goto finish ;
}
for ( ; ; ) {
int ret ;
/* Hmm, nothing to do. Let's sleep */
if ( ( ret = pa_rtpoll_run ( u - > rtpoll ) ) < 0 )
@ -345,13 +257,6 @@ finish:
pa_log_debug ( " Thread shutting down " ) ;
}
static int getenv_int ( const char * env , size_t min_len ) {
char * got_env = getenv ( env ) ;
int ret = 0 ;
if ( got_env ! = NULL & & strlen ( got_env ) > = min_len ) ret = atoi ( got_env ) ; //"8000" is 4 symbols
return ret ;
}
int pa__init ( pa_module * m ) {
struct userdata * u = NULL ;
pa_sample_spec ss ;
@ -367,12 +272,6 @@ int pa__init(pa_module*m) {
goto fail ;
}
// High rate causes glitches on some devices, this is needed to prevent it
//ss.rate = 32000;
//ss.channels = 2;
//ss.format = PA_SAMPLE_S16LE;
//OK. That will allow users to define sampling rate under his responsibility
ss = m - > core - > default_sample_spec ;
map = m - > core - > default_channel_map ;
if ( pa_modargs_get_sample_spec_and_channel_map ( ma , & ss , & map , PA_CHANNEL_MAP_DEFAULT ) < 0 ) {
@ -380,28 +279,18 @@ int pa__init(pa_module*m) {
goto fail ;
}
//Needed. Don't touch
ss . channels = 2 ;
ss . format = PA_SAMPLE_S16LE ;
m - > userdata = u = pa_xnew0 ( struct userdata , 1 ) ;
int forceFormat = getenv_int ( " PROPERTY_OUTPUT_SAMPLE_RATE " , 4 ) ; //"8000" is 4 symbols
if ( forceFormat > = 8000 & & forceFormat < = 192000 ) {
ss . rate = forceFormat ;
pa_log_info ( " Sample rate was forced to be %u \n " , ss . rate ) ;
}
u - > core = m - > core ;
u - > module = m ;
u - > rtpoll = pa_rtpoll_new ( ) ;
pa_thread_mq_init ( & u - > thread_mq , m - > core - > mainloop , u - > rtpoll ) ;
//Pulseaudio uses samples per sec but OpenSL ES uses samples per ms
if ( pa_init_sles_player ( u , ss . rate * 1000 ) < 0 )
goto fail ;
//int buff[2] = {0, 0};
//(*u->bqPlayerBufferQueue)->Enqueue(u->bqPlayerBufferQueue, buff, 1);
if ( pa_init_sles_player ( u , ss . rate ) < 0 )
goto fail ;
pa_sink_new_data_init ( & data ) ;
data . driver = __FILE__ ;
@ -418,7 +307,7 @@ int pa__init(pa_module*m) {
goto fail ;
}
u - > sink = pa_sink_new ( m - > core , & data , PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY ) ;
u - > sink = pa_sink_new ( m - > core , & data , 0 ) ;
pa_sink_new_data_done ( & data ) ;
if ( ! u - > sink ) {
@ -426,8 +315,7 @@ int pa__init(pa_module*m) {
goto fail ;
}
u - > sink - > parent . process_msg = sink_process_msg ;
u - > sink - > update_requested_latency = sink_update_requested_latency_cb ;
u - > sink - > parent . process_msg = pa_sink_process_msg ;
u - > sink - > userdata = u ;
pa_sink_set_asyncmsgq ( u - > sink , u - > thread_mq . inq ) ;
@ -443,7 +331,7 @@ int pa__init(pa_module*m) {
goto fail ;
}
pa_sink_set_latency_range ( u - > sink , 0 , BLOCK_USEC ) ;
pa_sink_set_fixed_ latency ( u - > sink , u - > block_usec ) ;
pa_sink_put ( u - > sink ) ;
@ -486,8 +374,8 @@ void pa__done(pa_module*m) {
}
if ( u - > engineObject ) {
pa_destroy_sles_player ( u ) ;
}
pa_destroy_sles_player ( u ) ;
}
pa_thread_mq_done ( & u - > thread_mq ) ;