2004-11-14 02:36:35 +00:00
/* $Id$ */
/***
2006-06-19 21:53:48 +00:00
This file is part of PulseAudio .
2007-01-04 13:43:45 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is free software ; you can redistribute it and / or modify
2004-11-14 14:58:54 +00:00
it under the terms of the GNU Lesser General Public License as published
2004-11-14 02:36:35 +00:00
by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
2007-01-04 13:43:45 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is distributed in the hope that it will be useful , but
2004-11-14 02:36:35 +00:00
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 .
2007-01-04 13:43:45 +00:00
2004-11-14 14:58:54 +00:00
You should have received a copy of the GNU Lesser General Public License
2006-06-19 21:53:48 +00:00
along with PulseAudio ; if not , write to the Free Software
2004-11-14 02:36:35 +00:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdlib.h>
# include <sys/stat.h>
# include <stdio.h>
# include <assert.h>
# include <errno.h>
# include <string.h>
# include <fcntl.h>
# include <unistd.h>
# include <limits.h>
2006-06-19 21:53:48 +00:00
# include <pulse/xmalloc.h>
2006-05-17 16:34:18 +00:00
2006-06-19 21:53:48 +00:00
# include <pulsecore/core-error.h>
# include <pulsecore/iochannel.h>
# include <pulsecore/sink.h>
# include <pulsecore/module.h>
# include <pulsecore/core-util.h>
# include <pulsecore/modargs.h>
# include <pulsecore/log.h>
# include <pulsecore/socket-client.h>
# include <pulsecore/esound.h>
# include <pulsecore/authkey.h>
2006-02-16 19:19:58 +00:00
2004-11-14 02:36:35 +00:00
# include "module-esound-sink-symdef.h"
PA_MODULE_AUTHOR ( " Lennart Poettering " )
2006-01-27 16:25:31 +00:00
PA_MODULE_DESCRIPTION ( " ESOUND Sink " )
2004-11-14 02:36:35 +00:00
PA_MODULE_VERSION ( PACKAGE_VERSION )
PA_MODULE_USAGE ( " sink_name=<name for the sink> server=<address> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> " )
# define DEFAULT_SINK_NAME "esound_output"
struct userdata {
2006-01-11 01:17:39 +00:00
pa_core * core ;
2004-11-14 02:36:35 +00:00
2006-01-11 01:17:39 +00:00
pa_sink * sink ;
pa_iochannel * io ;
pa_socket_client * client ;
2004-11-14 02:36:35 +00:00
2006-01-11 01:17:39 +00:00
pa_defer_event * defer_event ;
2004-11-14 02:36:35 +00:00
2006-01-11 01:17:39 +00:00
pa_memchunk memchunk ;
pa_module * module ;
2004-11-14 02:36:35 +00:00
void * write_data ;
size_t write_length , write_index ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
void * read_data ;
size_t read_length , read_index ;
enum { STATE_AUTH , STATE_LATENCY , STATE_RUNNING , STATE_DEAD } state ;
pa_usec_t latency ;
esd_format_t format ;
int32_t rate ;
} ;
static const char * const valid_modargs [ ] = {
" server " ,
" cookie " ,
" rate " ,
" format " ,
" channels " ,
" sink_name " ,
NULL
} ;
static void cancel ( struct userdata * u ) {
assert ( u ) ;
u - > state = STATE_DEAD ;
if ( u - > io ) {
pa_iochannel_free ( u - > io ) ;
u - > io = NULL ;
}
if ( u - > defer_event ) {
u - > core - > mainloop - > defer_free ( u - > defer_event ) ;
u - > defer_event = NULL ;
}
if ( u - > sink ) {
pa_sink_disconnect ( u - > sink ) ;
pa_sink_unref ( u - > sink ) ;
u - > sink = NULL ;
}
if ( u - > module ) {
pa_module_unload_request ( u - > module ) ;
u - > module = NULL ;
}
}
static int do_write ( struct userdata * u ) {
ssize_t r ;
assert ( u ) ;
if ( ! pa_iochannel_is_writable ( u - > io ) )
return 0 ;
if ( u - > write_data ) {
assert ( u - > write_index < u - > write_length ) ;
if ( ( r = pa_iochannel_write ( u - > io , ( uint8_t * ) u - > write_data + u - > write_index , u - > write_length - u - > write_index ) ) < = 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " write() failed: %s " , pa_cstrerror ( errno ) ) ;
2004-11-14 02:36:35 +00:00
return - 1 ;
}
u - > write_index + = r ;
assert ( u - > write_index < = u - > write_length ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( u - > write_index = = u - > write_length ) {
free ( u - > write_data ) ;
u - > write_data = NULL ;
u - > write_index = u - > write_length = 0 ;
}
} else if ( u - > state = = STATE_RUNNING ) {
2006-08-12 17:06:05 +00:00
pa_module_set_used ( u - > module , pa_sink_used_by ( u - > sink ) ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( ! u - > memchunk . length )
2006-01-10 17:51:06 +00:00
if ( pa_sink_render ( u - > sink , 8192 , & u - > memchunk ) < 0 )
2004-11-14 02:36:35 +00:00
return 0 ;
2006-11-06 13:06:01 +00:00
assert ( u - > memchunk . memblock & & u - > memchunk . length ) ;
2007-01-04 13:43:45 +00:00
2006-11-06 13:06:01 +00:00
if ( ( r = pa_iochannel_write ( u - > io , ( uint8_t * ) u - > memchunk . memblock - > data + u - > memchunk . index , u - > memchunk . length ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " write() failed: %s " , pa_cstrerror ( errno ) ) ;
2004-11-14 02:36:35 +00:00
return - 1 ;
}
u - > memchunk . index + = r ;
u - > memchunk . length - = r ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( u - > memchunk . length < = 0 ) {
pa_memblock_unref ( u - > memchunk . memblock ) ;
u - > memchunk . memblock = NULL ;
}
}
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
return 0 ;
}
static int handle_response ( struct userdata * u ) {
assert ( u ) ;
switch ( u - > state ) {
case STATE_AUTH :
assert ( u - > read_length = = sizeof ( int32_t ) ) ;
/* Process auth data */
if ( ! * ( int32_t * ) u - > read_data ) {
2006-08-18 21:38:40 +00:00
pa_log ( " Authentication failed: %s " , pa_cstrerror ( errno ) ) ;
2004-11-14 02:36:35 +00:00
return - 1 ;
}
/* Request latency data */
assert ( ! u - > write_data ) ;
* ( int32_t * ) ( u - > write_data = pa_xmalloc ( u - > write_length = sizeof ( int32_t ) ) ) = ESD_PROTO_LATENCY ;
u - > write_index = 0 ;
u - > state = STATE_LATENCY ;
/* Space for next response */
assert ( u - > read_length > = sizeof ( int32_t ) ) ;
u - > read_index = 0 ;
u - > read_length = sizeof ( int32_t ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
break ;
case STATE_LATENCY : {
int32_t * p ;
assert ( u - > read_length = = sizeof ( int32_t ) ) ;
/* Process latency info */
u - > latency = ( pa_usec_t ) ( ( double ) ( * ( int32_t * ) u - > read_data ) * 1000000 / 44100 ) ;
if ( u - > latency > 10000000 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " WARNING! Invalid latency information received from server " ) ;
2004-11-14 02:36:35 +00:00
u - > latency = 0 ;
}
/* Create stream */
assert ( ! u - > write_data ) ;
p = u - > write_data = pa_xmalloc0 ( u - > write_length = sizeof ( int32_t ) * 3 + ESD_NAME_MAX ) ;
* ( p + + ) = ESD_PROTO_STREAM_PLAY ;
* ( p + + ) = u - > format ;
* ( p + + ) = u - > rate ;
2006-06-19 22:11:49 +00:00
pa_strlcpy ( ( char * ) p , " PulseAudio Tunnel " , ESD_NAME_MAX ) ;
2004-11-14 02:36:35 +00:00
u - > write_index = 0 ;
u - > state = STATE_RUNNING ;
/* Don't read any further */
pa_xfree ( u - > read_data ) ;
u - > read_data = NULL ;
u - > read_index = u - > read_length = 0 ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
break ;
}
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
default :
abort ( ) ;
}
return 0 ;
}
static int do_read ( struct userdata * u ) {
assert ( u ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( ! pa_iochannel_is_readable ( u - > io ) )
return 0 ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( u - > state = = STATE_AUTH | | u - > state = = STATE_LATENCY ) {
ssize_t r ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( ! u - > read_data )
return 0 ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
assert ( u - > read_index < u - > read_length ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( ( r = pa_iochannel_read ( u - > io , ( uint8_t * ) u - > read_data + u - > read_index , u - > read_length - u - > read_index ) ) < = 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " read() failed: %s " , r < 0 ? pa_cstrerror ( errno ) : " EOF " ) ;
2004-11-14 02:36:35 +00:00
cancel ( u ) ;
return - 1 ;
}
u - > read_index + = r ;
assert ( u - > read_index < = u - > read_length ) ;
if ( u - > read_index = = u - > read_length )
return handle_response ( u ) ;
}
return 0 ;
}
static void do_work ( struct userdata * u ) {
assert ( u ) ;
u - > core - > mainloop - > defer_enable ( u - > defer_event , 0 ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( do_read ( u ) < 0 | | do_write ( u ) < 0 )
cancel ( u ) ;
}
2006-01-11 01:17:39 +00:00
static void notify_cb ( pa_sink * s ) {
2004-11-14 02:36:35 +00:00
struct userdata * u = s - > userdata ;
assert ( s & & u ) ;
if ( pa_iochannel_is_writable ( u - > io ) )
u - > core - > mainloop - > defer_enable ( u - > defer_event , 1 ) ;
}
2006-01-11 01:17:39 +00:00
static pa_usec_t get_latency_cb ( pa_sink * s ) {
2004-11-14 02:36:35 +00:00
struct userdata * u = s - > userdata ;
assert ( s & & u ) ;
return
u - > latency +
( u - > memchunk . memblock ? pa_bytes_to_usec ( u - > memchunk . length , & s - > sample_spec ) : 0 ) ;
}
2006-01-11 01:17:39 +00:00
static void defer_callback ( PA_GCC_UNUSED pa_mainloop_api * m , PA_GCC_UNUSED pa_defer_event * e , void * userdata ) {
2004-11-14 02:36:35 +00:00
struct userdata * u = userdata ;
assert ( u ) ;
do_work ( u ) ;
}
2006-01-11 01:17:39 +00:00
static void io_callback ( PA_GCC_UNUSED pa_iochannel * io , void * userdata ) {
2004-11-14 02:36:35 +00:00
struct userdata * u = userdata ;
assert ( u ) ;
do_work ( u ) ;
}
2006-01-11 01:17:39 +00:00
static void on_connection ( PA_GCC_UNUSED pa_socket_client * c , pa_iochannel * io , void * userdata ) {
2004-11-14 02:36:35 +00:00
struct userdata * u = userdata ;
pa_socket_client_unref ( u - > client ) ;
u - > client = NULL ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( ! io ) {
2006-08-18 21:38:40 +00:00
pa_log ( " connection failed: %s " , pa_cstrerror ( errno ) ) ;
2004-11-14 02:36:35 +00:00
cancel ( u ) ;
return ;
}
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
u - > io = io ;
pa_iochannel_set_callback ( u - > io , io_callback , u ) ;
}
2006-01-11 01:17:39 +00:00
int pa__init ( pa_core * c , pa_module * m ) {
2004-11-14 02:36:35 +00:00
struct userdata * u = NULL ;
const char * p ;
2006-01-11 01:17:39 +00:00
pa_sample_spec ss ;
pa_modargs * ma = NULL ;
2006-08-11 23:58:55 +00:00
char * t ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
assert ( c & & m ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
2006-08-18 21:38:40 +00:00
pa_log ( " failed to parse module arguments " ) ;
2004-11-14 02:36:35 +00:00
goto fail ;
}
ss = c - > default_sample_spec ;
if ( pa_modargs_get_sample_spec ( ma , & ss ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " invalid sample format specification " ) ;
2004-11-14 02:36:35 +00:00
goto fail ;
}
if ( ( ss . format ! = PA_SAMPLE_U8 & & ss . format ! = PA_SAMPLE_S16NE ) | |
( ss . channels > 2 ) ) {
2006-08-18 21:38:40 +00:00
pa_log ( " esound sample type support is limited to mono/stereo and U8 or S16NE sample data " ) ;
2004-11-14 02:36:35 +00:00
goto fail ;
}
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
u = pa_xmalloc0 ( sizeof ( struct userdata ) ) ;
u - > core = c ;
u - > module = m ;
m - > userdata = u ;
u - > format =
( ss . format = = PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16 ) |
( ss . channels = = 2 ? ESD_STEREO : ESD_MONO ) ;
u - > rate = ss . rate ;
u - > sink = NULL ;
u - > client = NULL ;
u - > io = NULL ;
u - > read_data = u - > write_data = NULL ;
u - > read_index = u - > write_index = u - > read_length = u - > write_length = 0 ;
u - > state = STATE_AUTH ;
u - > latency = 0 ;
2006-01-27 16:25:31 +00:00
if ( ! ( u - > sink = pa_sink_new ( c , __FILE__ , pa_modargs_get_value ( ma , " sink_name " , DEFAULT_SINK_NAME ) , 0 , & ss , NULL ) ) ) {
2006-08-18 21:38:40 +00:00
pa_log ( " failed to create sink. " ) ;
2004-11-14 02:36:35 +00:00
goto fail ;
}
if ( ! ( u - > client = pa_socket_client_new_string ( u - > core - > mainloop , p = pa_modargs_get_value ( ma , " server " , ESD_UNIX_SOCKET_NAME ) , ESD_DEFAULT_PORT ) ) ) {
2006-08-18 21:38:40 +00:00
pa_log ( " failed to connect to server. " ) ;
2004-11-14 02:36:35 +00:00
goto fail ;
}
pa_socket_client_set_callback ( u - > client , on_connection , u ) ;
/* Prepare the initial request */
u - > write_data = pa_xmalloc ( u - > write_length = ESD_KEY_LEN + sizeof ( int32_t ) ) ;
if ( pa_authkey_load_auto ( pa_modargs_get_value ( ma , " cookie " , " .esd_auth " ) , u - > write_data , ESD_KEY_LEN ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " failed to load cookie " ) ;
2004-11-14 02:36:35 +00:00
goto fail ;
}
* ( int32_t * ) ( ( uint8_t * ) u - > write_data + ESD_KEY_LEN ) = ESD_ENDIAN_KEY ;
/* Reserve space for the response */
u - > read_data = pa_xmalloc ( u - > read_length = sizeof ( int32_t ) ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
u - > sink - > notify = notify_cb ;
u - > sink - > get_latency = get_latency_cb ;
u - > sink - > userdata = u ;
pa_sink_set_owner ( u - > sink , m ) ;
2006-08-11 23:58:55 +00:00
pa_sink_set_description ( u - > sink , t = pa_sprintf_malloc ( " Esound sink '%s' " , p ) ) ;
pa_xfree ( t ) ;
2004-11-14 02:36:35 +00:00
u - > memchunk . memblock = NULL ;
u - > memchunk . length = 0 ;
u - > defer_event = c - > mainloop - > defer_new ( c - > mainloop , defer_callback , u ) ;
c - > mainloop - > defer_enable ( u - > defer_event , 0 ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
pa_modargs_free ( ma ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
return 0 ;
fail :
if ( ma )
pa_modargs_free ( ma ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
pa__done ( c , m ) ;
return - 1 ;
}
2006-01-11 01:17:39 +00:00
void pa__done ( pa_core * c , pa_module * m ) {
2004-11-14 02:36:35 +00:00
struct userdata * u ;
assert ( c & & m ) ;
if ( ! ( u = m - > userdata ) )
return ;
u - > module = NULL ;
cancel ( u ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
if ( u - > memchunk . memblock )
pa_memblock_unref ( u - > memchunk . memblock ) ;
if ( u - > client )
pa_socket_client_unref ( u - > client ) ;
2007-01-04 13:43:45 +00:00
2004-11-14 02:36:35 +00:00
pa_xfree ( u - > read_data ) ;
pa_xfree ( u - > write_data ) ;
pa_xfree ( u ) ;
}