2004-09-28 22:47:48 +00:00
/* $Id$ */
/***
This file is part of polypaudio .
polypaudio 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-09-28 22:47:48 +00:00
by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
polypaudio 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 .
2004-11-14 14:58:54 +00:00
You should have received a copy of the GNU Lesser General Public License
2004-09-28 22:47:48 +00:00
along with polypaudio ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <unistd.h>
# include <assert.h>
# include <string.h>
# include <errno.h>
# include <sys/types.h>
# include <stdio.h>
# include <stdlib.h>
2006-02-16 19:19:58 +00:00
# include <polypcore/module.h>
# include <polypcore/util.h>
# include <polypcore/modargs.h>
# include <polypcore/log.h>
2006-02-16 23:13:27 +00:00
# include <polypcore/core-subscribe.h>
2006-02-16 19:19:58 +00:00
# include <polypcore/xmalloc.h>
# include <polypcore/sink-input.h>
# include <polypcore/pdispatch.h>
# include <polypcore/pstream.h>
# include <polypcore/pstream-util.h>
# include <polypcore/authkey.h>
# include <polypcore/socket-client.h>
# include <polypcore/socket-util.h>
# include <polypcore/authkey-prop.h>
2004-09-28 22:47:48 +00:00
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-10-30 01:55:16 +00:00
# include "module-tunnel-sink-symdef.h"
2004-09-29 17:38:45 +00:00
PA_MODULE_DESCRIPTION ( " Tunnel module for sinks " )
2004-11-14 00:04:51 +00:00
PA_MODULE_USAGE ( " server=<address> sink=<remote sink name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> sink_name=<name for the local sink> " )
2004-09-29 17:38:45 +00:00
# else
2004-10-30 01:55:16 +00:00
# include "module-tunnel-source-symdef.h"
2004-09-29 17:38:45 +00:00
PA_MODULE_DESCRIPTION ( " Tunnel module for sources " )
2004-11-14 00:04:51 +00:00
PA_MODULE_USAGE ( " server=<address> source=<remote source name> cookie=<filename> format=<sample format> channels=<number of channels> rate=<sample rate> source_name=<name for the local source> " )
2004-09-29 17:38:45 +00:00
# endif
2004-10-30 01:55:16 +00:00
PA_MODULE_AUTHOR ( " Lennart Poettering " )
PA_MODULE_VERSION ( PACKAGE_VERSION )
2004-09-28 22:47:48 +00:00
# define DEFAULT_SINK_NAME "tunnel"
2004-09-29 17:38:45 +00:00
# define DEFAULT_SOURCE_NAME "tunnel"
2004-09-28 22:47:48 +00:00
2004-09-28 23:49:54 +00:00
# define DEFAULT_TLENGTH (44100*2*2 / 10) //(10240*8)
2004-09-28 22:47:48 +00:00
# define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3) / 2)
# define DEFAULT_MINREQ 512
2004-09-28 23:49:54 +00:00
# define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ)
2004-09-28 22:47:48 +00:00
# define DEFAULT_FRAGSIZE 1024
# define DEFAULT_TIMEOUT 5
2004-09-28 23:49:54 +00:00
# define LATENCY_INTERVAL 10
2004-09-28 22:47:48 +00:00
static const char * const valid_modargs [ ] = {
" server " ,
" cookie " ,
" format " ,
" channels " ,
" rate " ,
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 22:47:48 +00:00
" sink_name " ,
2004-09-29 17:38:45 +00:00
" sink " ,
# else
" source_name " ,
" source " ,
# endif
2004-09-28 22:47:48 +00:00
NULL ,
} ;
2006-01-11 01:17:39 +00:00
static void command_stream_killed ( pa_pdispatch * pd , uint32_t command , uint32_t tag , pa_tagstruct * t , void * userdata ) ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2006-01-11 01:17:39 +00:00
static void command_request ( pa_pdispatch * pd , uint32_t command , uint32_t tag , pa_tagstruct * t , void * userdata ) ;
2004-09-29 17:38:45 +00:00
# endif
2004-09-28 22:47:48 +00:00
2006-01-11 01:17:39 +00:00
static const pa_pdispatch_callback command_table [ PA_COMMAND_MAX ] = {
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2006-01-11 01:17:39 +00:00
[ PA_COMMAND_REQUEST ] = command_request ,
2004-09-29 17:38:45 +00:00
# endif
2006-01-11 01:17:39 +00:00
[ PA_COMMAND_PLAYBACK_STREAM_KILLED ] = command_stream_killed ,
[ PA_COMMAND_RECORD_STREAM_KILLED ] = command_stream_killed
2004-09-28 22:47:48 +00:00
} ;
struct userdata {
2006-01-11 01:17:39 +00:00
pa_socket_client * client ;
pa_pstream * pstream ;
pa_pdispatch * pdispatch ;
2004-09-28 22:47:48 +00:00
2004-09-29 17:38:45 +00:00
char * server_name ;
# ifdef TUNNEL_SINK
char * sink_name ;
2006-01-11 01:17:39 +00:00
pa_sink * sink ;
2004-09-29 17:38:45 +00:00
uint32_t requested_bytes ;
# else
char * source_name ;
2006-01-11 01:17:39 +00:00
pa_source * source ;
2004-09-29 17:38:45 +00:00
# endif
2006-01-11 01:17:39 +00:00
pa_module * module ;
pa_core * core ;
2004-09-28 22:47:48 +00:00
uint8_t auth_cookie [ PA_NATIVE_COOKIE_LENGTH ] ;
uint32_t ctag ;
uint32_t device_index ;
uint32_t channel ;
2004-09-29 17:38:45 +00:00
2004-09-28 23:49:54 +00:00
pa_usec_t host_latency ;
2006-01-11 01:17:39 +00:00
pa_time_event * time_event ;
2004-09-28 22:47:48 +00:00
2004-11-07 20:48:46 +00:00
int auth_cookie_in_property ;
} ;
2004-09-28 22:47:48 +00:00
static void close_stuff ( struct userdata * u ) {
2004-11-07 20:48:46 +00:00
assert ( u ) ;
2004-09-28 22:47:48 +00:00
if ( u - > pstream ) {
pa_pstream_close ( u - > pstream ) ;
pa_pstream_unref ( u - > pstream ) ;
u - > pstream = NULL ;
}
if ( u - > pdispatch ) {
pa_pdispatch_unref ( u - > pdispatch ) ;
u - > pdispatch = NULL ;
}
if ( u - > client ) {
pa_socket_client_unref ( u - > client ) ;
u - > client = NULL ;
}
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 22:47:48 +00:00
if ( u - > sink ) {
pa_sink_disconnect ( u - > sink ) ;
pa_sink_unref ( u - > sink ) ;
u - > sink = NULL ;
}
2004-09-29 17:38:45 +00:00
# else
if ( u - > source ) {
pa_source_disconnect ( u - > source ) ;
pa_source_unref ( u - > source ) ;
u - > source = NULL ;
}
# endif
2004-09-28 23:49:54 +00:00
if ( u - > time_event ) {
u - > core - > mainloop - > time_free ( u - > time_event ) ;
u - > time_event = NULL ;
}
2004-09-28 22:47:48 +00:00
}
static void die ( struct userdata * u ) {
assert ( u ) ;
close_stuff ( u ) ;
pa_module_unload_request ( u - > module ) ;
}
2006-01-11 01:17:39 +00:00
static void command_stream_killed ( pa_pdispatch * pd , PA_GCC_UNUSED uint32_t command , PA_GCC_UNUSED uint32_t tag , pa_tagstruct * t , void * userdata ) {
2004-09-29 17:38:45 +00:00
struct userdata * u = userdata ;
assert ( pd & & t & & u & & u - > pdispatch = = pd ) ;
pa_log ( __FILE__ " : stream killed \n " ) ;
die ( u ) ;
}
# ifdef TUNNEL_SINK
2004-09-28 23:49:54 +00:00
static void send_prebuf_request ( struct userdata * u ) {
2006-01-11 01:17:39 +00:00
pa_tagstruct * t ;
2004-09-28 23:49:54 +00:00
t = pa_tagstruct_new ( NULL , 0 ) ;
pa_tagstruct_putu32 ( t , PA_COMMAND_PREBUF_PLAYBACK_STREAM ) ;
pa_tagstruct_putu32 ( t , u - > ctag + + ) ;
pa_tagstruct_putu32 ( t , u - > channel ) ;
pa_pstream_send_tagstruct ( u - > pstream , t ) ;
}
static void send_bytes ( struct userdata * u ) {
2004-09-28 22:47:48 +00:00
assert ( u ) ;
if ( ! u - > pstream )
return ;
2004-09-28 23:49:54 +00:00
2004-09-28 22:47:48 +00:00
while ( u - > requested_bytes > 0 ) {
2006-01-11 01:17:39 +00:00
pa_memchunk chunk ;
2004-09-28 23:49:54 +00:00
if ( pa_sink_render ( u - > sink , u - > requested_bytes , & chunk ) < 0 ) {
if ( u - > requested_bytes > = DEFAULT_TLENGTH - DEFAULT_PREBUF )
send_prebuf_request ( u ) ;
2004-09-28 22:47:48 +00:00
return ;
2004-09-28 23:49:54 +00:00
}
2004-09-28 22:47:48 +00:00
2006-02-20 04:05:16 +00:00
pa_pstream_send_memblock ( u - > pstream , u - > channel , 0 , PA_SEEK_RELATIVE , & chunk ) ;
2004-09-28 22:47:48 +00:00
pa_memblock_unref ( chunk . memblock ) ;
if ( chunk . length > u - > requested_bytes )
u - > requested_bytes = 0 ;
else
u - > requested_bytes - = chunk . length ;
}
}
2006-01-11 01:17:39 +00:00
static void command_request ( pa_pdispatch * pd , uint32_t command , PA_GCC_UNUSED uint32_t tag , pa_tagstruct * t , void * userdata ) {
2004-09-28 22:47:48 +00:00
struct userdata * u = userdata ;
uint32_t bytes , channel ;
assert ( pd & & command = = PA_COMMAND_REQUEST & & t & & u & & u - > pdispatch = = pd ) ;
if ( pa_tagstruct_getu32 ( t , & channel ) < 0 | |
pa_tagstruct_getu32 ( t , & bytes ) < 0 | |
! pa_tagstruct_eof ( t ) ) {
pa_log ( __FILE__ " : invalid protocol reply \n " ) ;
die ( u ) ;
return ;
}
if ( channel ! = u - > channel ) {
pa_log ( __FILE__ " : recieved data for invalid channel \n " ) ;
die ( u ) ;
return ;
}
u - > requested_bytes + = bytes ;
2004-09-28 23:49:54 +00:00
send_bytes ( u ) ;
}
2004-09-29 17:38:45 +00:00
# endif
2006-01-11 01:17:39 +00:00
static void stream_get_latency_callback ( pa_pdispatch * pd , uint32_t command , PA_GCC_UNUSED uint32_t tag , pa_tagstruct * t , void * userdata ) {
2004-09-28 23:49:54 +00:00
struct userdata * u = userdata ;
pa_usec_t buffer_usec , sink_usec , source_usec , transport_usec ;
int playing ;
uint32_t queue_length ;
2004-11-17 01:22:43 +00:00
uint64_t counter ;
2004-09-28 23:49:54 +00:00
struct timeval local , remote , now ;
2004-11-17 01:22:43 +00:00
assert ( pd & & u ) ;
2004-09-28 23:49:54 +00:00
if ( command ! = PA_COMMAND_REPLY ) {
if ( command = = PA_COMMAND_ERROR )
pa_log ( __FILE__ " : failed to get latency. \n " ) ;
else
pa_log ( __FILE__ " : protocol error. \n " ) ;
die ( u ) ;
return ;
}
if ( pa_tagstruct_get_usec ( t , & buffer_usec ) < 0 | |
pa_tagstruct_get_usec ( t , & sink_usec ) < 0 | |
pa_tagstruct_get_usec ( t , & source_usec ) < 0 | |
pa_tagstruct_get_boolean ( t , & playing ) < 0 | |
pa_tagstruct_getu32 ( t , & queue_length ) < 0 | |
pa_tagstruct_get_timeval ( t , & local ) < 0 | |
pa_tagstruct_get_timeval ( t , & remote ) < 0 | |
2004-11-17 01:22:43 +00:00
pa_tagstruct_getu64 ( t , & counter ) < 0 | |
2004-09-28 23:49:54 +00:00
! pa_tagstruct_eof ( t ) ) {
pa_log ( __FILE__ " : invalid reply. \n " ) ;
die ( u ) ;
return ;
}
2006-01-10 17:51:06 +00:00
pa_gettimeofday ( & now ) ;
2004-09-28 23:49:54 +00:00
2004-09-29 17:38:45 +00:00
if ( pa_timeval_cmp ( & local , & remote ) < 0 & & pa_timeval_cmp ( & remote , & now ) ) {
2004-09-28 23:49:54 +00:00
/* local and remote seem to have synchronized clocks */
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 23:49:54 +00:00
transport_usec = pa_timeval_diff ( & remote , & local ) ;
2004-09-29 17:38:45 +00:00
# else
transport_usec = pa_timeval_diff ( & now , & remote ) ;
# endif
} else
2004-09-28 23:49:54 +00:00
transport_usec = pa_timeval_diff ( & now , & local ) / 2 ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 23:49:54 +00:00
u - > host_latency = sink_usec + transport_usec ;
2004-09-29 17:38:45 +00:00
# else
u - > host_latency = source_usec + transport_usec ;
if ( u - > host_latency > sink_usec )
u - > host_latency - = sink_usec ;
else
u - > host_latency = 0 ;
# endif
2004-09-28 23:49:54 +00:00
/* pa_log(__FILE__": estimated host latency: %0.0f usec\n", (double) u->host_latency); */
2004-09-28 22:47:48 +00:00
}
2004-09-28 23:49:54 +00:00
static void request_latency ( struct userdata * u ) {
2006-01-11 01:17:39 +00:00
pa_tagstruct * t ;
2004-09-28 23:49:54 +00:00
struct timeval now ;
uint32_t tag ;
assert ( u ) ;
t = pa_tagstruct_new ( NULL , 0 ) ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 23:49:54 +00:00
pa_tagstruct_putu32 ( t , PA_COMMAND_GET_PLAYBACK_LATENCY ) ;
2004-09-29 17:38:45 +00:00
# else
pa_tagstruct_putu32 ( t , PA_COMMAND_GET_RECORD_LATENCY ) ;
# endif
2004-09-28 23:49:54 +00:00
pa_tagstruct_putu32 ( t , tag = u - > ctag + + ) ;
pa_tagstruct_putu32 ( t , u - > channel ) ;
2006-01-10 17:51:06 +00:00
pa_gettimeofday ( & now ) ;
2004-09-28 23:49:54 +00:00
pa_tagstruct_put_timeval ( t , & now ) ;
2004-11-17 01:22:43 +00:00
pa_tagstruct_putu64 ( t , 0 ) ;
2004-09-28 23:49:54 +00:00
pa_pstream_send_tagstruct ( u - > pstream , t ) ;
pa_pdispatch_register_reply ( u - > pdispatch , tag , DEFAULT_TIMEOUT , stream_get_latency_callback , u ) ;
}
2004-09-28 22:47:48 +00:00
2006-01-11 01:17:39 +00:00
static void create_stream_callback ( pa_pdispatch * pd , uint32_t command , PA_GCC_UNUSED uint32_t tag , pa_tagstruct * t , void * userdata ) {
2004-09-28 22:47:48 +00:00
struct userdata * u = userdata ;
assert ( pd & & u & & u - > pdispatch = = pd ) ;
if ( command ! = PA_COMMAND_REPLY ) {
if ( command = = PA_COMMAND_ERROR )
pa_log ( __FILE__ " : failed to create stream. \n " ) ;
else
pa_log ( __FILE__ " : protocol error. \n " ) ;
die ( u ) ;
return ;
}
if ( pa_tagstruct_getu32 ( t , & u - > channel ) < 0 | |
pa_tagstruct_getu32 ( t , & u - > device_index ) < 0 | |
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 22:47:48 +00:00
pa_tagstruct_getu32 ( t , & u - > requested_bytes ) < 0 | |
2004-09-29 17:38:45 +00:00
# endif
2004-09-28 22:47:48 +00:00
! pa_tagstruct_eof ( t ) ) {
pa_log ( __FILE__ " : invalid reply. \n " ) ;
die ( u ) ;
return ;
}
2004-09-28 23:49:54 +00:00
request_latency ( u ) ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 23:49:54 +00:00
send_bytes ( u ) ;
2004-09-29 17:38:45 +00:00
# endif
2004-09-28 22:47:48 +00:00
}
2006-01-11 01:17:39 +00:00
static void setup_complete_callback ( pa_pdispatch * pd , uint32_t command , uint32_t tag , pa_tagstruct * t , void * userdata ) {
2004-09-28 22:47:48 +00:00
struct userdata * u = userdata ;
2006-01-11 01:17:39 +00:00
pa_tagstruct * reply ;
2004-09-28 22:47:48 +00:00
char name [ 256 ] , un [ 128 ] , hn [ 128 ] ;
assert ( pd & & u & & u - > pdispatch = = pd ) ;
if ( command ! = PA_COMMAND_REPLY | | ! pa_tagstruct_eof ( t ) ) {
if ( command = = PA_COMMAND_ERROR )
pa_log ( __FILE__ " : failed to authenticate \n " ) ;
else
pa_log ( __FILE__ " : protocol error. \n " ) ;
die ( u ) ;
return ;
}
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 22:47:48 +00:00
snprintf ( name , sizeof ( name ) , " Tunnel from host '%s', user '%s', sink '%s' " ,
pa_get_host_name ( hn , sizeof ( hn ) ) ,
pa_get_user_name ( un , sizeof ( un ) ) ,
u - > sink - > name ) ;
2004-09-29 17:38:45 +00:00
# else
snprintf ( name , sizeof ( name ) , " Tunnel from host '%s', user '%s', source '%s' " ,
pa_get_host_name ( hn , sizeof ( hn ) ) ,
pa_get_user_name ( un , sizeof ( un ) ) ,
u - > source - > name ) ;
# endif
2004-09-28 22:47:48 +00:00
reply = pa_tagstruct_new ( NULL , 0 ) ;
pa_tagstruct_putu32 ( reply , PA_COMMAND_SET_CLIENT_NAME ) ;
pa_tagstruct_putu32 ( reply , tag = u - > ctag + + ) ;
pa_tagstruct_puts ( reply , name ) ;
pa_pstream_send_tagstruct ( u - > pstream , reply ) ;
/* We ignore the server's reply here */
reply = pa_tagstruct_new ( NULL , 0 ) ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 22:47:48 +00:00
pa_tagstruct_putu32 ( reply , PA_COMMAND_CREATE_PLAYBACK_STREAM ) ;
pa_tagstruct_putu32 ( reply , tag = u - > ctag + + ) ;
pa_tagstruct_puts ( reply , name ) ;
pa_tagstruct_put_sample_spec ( reply , & u - > sink - > sample_spec ) ;
pa_tagstruct_putu32 ( reply , PA_INVALID_INDEX ) ;
pa_tagstruct_puts ( reply , u - > sink_name ) ;
pa_tagstruct_putu32 ( reply , DEFAULT_MAXLENGTH ) ;
pa_tagstruct_put_boolean ( reply , 0 ) ;
pa_tagstruct_putu32 ( reply , DEFAULT_TLENGTH ) ;
pa_tagstruct_putu32 ( reply , DEFAULT_PREBUF ) ;
pa_tagstruct_putu32 ( reply , DEFAULT_MINREQ ) ;
pa_tagstruct_putu32 ( reply , PA_VOLUME_NORM ) ;
2004-09-29 17:38:45 +00:00
# else
pa_tagstruct_putu32 ( reply , PA_COMMAND_CREATE_RECORD_STREAM ) ;
pa_tagstruct_putu32 ( reply , tag = u - > ctag + + ) ;
pa_tagstruct_puts ( reply , name ) ;
pa_tagstruct_put_sample_spec ( reply , & u - > source - > sample_spec ) ;
pa_tagstruct_putu32 ( reply , PA_INVALID_INDEX ) ;
pa_tagstruct_puts ( reply , u - > source_name ) ;
pa_tagstruct_putu32 ( reply , DEFAULT_MAXLENGTH ) ;
pa_tagstruct_put_boolean ( reply , 0 ) ;
pa_tagstruct_putu32 ( reply , DEFAULT_FRAGSIZE ) ;
# endif
2004-09-28 22:47:48 +00:00
pa_pstream_send_tagstruct ( u - > pstream , reply ) ;
pa_pdispatch_register_reply ( u - > pdispatch , tag , DEFAULT_TIMEOUT , create_stream_callback , u ) ;
}
2006-01-11 01:17:39 +00:00
static void pstream_die_callback ( pa_pstream * p , void * userdata ) {
2004-09-28 22:47:48 +00:00
struct userdata * u = userdata ;
assert ( p & & u ) ;
pa_log ( __FILE__ " : stream died. \n " ) ;
die ( u ) ;
}
2006-01-11 01:17:39 +00:00
static void pstream_packet_callback ( pa_pstream * p , pa_packet * packet , void * userdata ) {
2004-09-28 22:47:48 +00:00
struct userdata * u = userdata ;
assert ( p & & packet & & u ) ;
if ( pa_pdispatch_run ( u - > pdispatch , packet , u ) < 0 ) {
pa_log ( __FILE__ " : invalid packet \n " ) ;
die ( u ) ;
}
}
2004-09-29 17:38:45 +00:00
# ifndef TUNNEL_SINK
2006-02-20 04:05:16 +00:00
static void pstream_memblock_callback ( pa_pstream * p , uint32_t channel , int64_t offset , pa_seek_mode_t seek , const pa_memchunk * chunk , void * userdata ) {
2004-09-29 17:38:45 +00:00
struct userdata * u = userdata ;
assert ( p & & chunk & & u ) ;
if ( channel ! = u - > channel ) {
pa_log ( __FILE__ " : recieved memory block on bad channel. \n " ) ;
die ( u ) ;
return ;
}
pa_source_post ( u - > source , chunk ) ;
}
# endif
2006-01-11 01:17:39 +00:00
static void on_connection ( pa_socket_client * sc , pa_iochannel * io , void * userdata ) {
2004-09-28 22:47:48 +00:00
struct userdata * u = userdata ;
2006-01-11 01:17:39 +00:00
pa_tagstruct * t ;
2004-09-28 22:47:48 +00:00
uint32_t tag ;
2004-09-28 23:52:50 +00:00
assert ( sc & & u & & u - > client = = sc ) ;
2004-09-28 22:47:48 +00:00
pa_socket_client_unref ( u - > client ) ;
u - > client = NULL ;
if ( ! io ) {
pa_log ( __FILE__ " : connection failed. \n " ) ;
pa_module_unload_request ( u - > module ) ;
return ;
}
u - > pstream = pa_pstream_new ( u - > core - > mainloop , io , u - > core - > memblock_stat ) ;
u - > pdispatch = pa_pdispatch_new ( u - > core - > mainloop , command_table , PA_COMMAND_MAX ) ;
pa_pstream_set_die_callback ( u - > pstream , pstream_die_callback , u ) ;
pa_pstream_set_recieve_packet_callback ( u - > pstream , pstream_packet_callback , u ) ;
2004-09-29 17:38:45 +00:00
# ifndef TUNNEL_SINK
pa_pstream_set_recieve_memblock_callback ( u - > pstream , pstream_memblock_callback , u ) ;
# endif
2004-09-28 22:47:48 +00:00
t = pa_tagstruct_new ( NULL , 0 ) ;
pa_tagstruct_putu32 ( t , PA_COMMAND_AUTH ) ;
pa_tagstruct_putu32 ( t , tag = u - > ctag + + ) ;
pa_tagstruct_put_arbitrary ( t , u - > auth_cookie , sizeof ( u - > auth_cookie ) ) ;
pa_pstream_send_tagstruct ( u - > pstream , t ) ;
pa_pdispatch_register_reply ( u - > pdispatch , tag , DEFAULT_TIMEOUT , setup_complete_callback , u ) ;
}
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2006-01-11 01:17:39 +00:00
static void sink_notify ( pa_sink * sink ) {
2004-09-28 22:47:48 +00:00
struct userdata * u ;
assert ( sink & & sink - > userdata ) ;
u = sink - > userdata ;
2004-09-28 23:49:54 +00:00
send_bytes ( u ) ;
2004-09-28 22:47:48 +00:00
}
2006-01-11 01:17:39 +00:00
static pa_usec_t sink_get_latency ( pa_sink * sink ) {
2004-09-28 22:47:48 +00:00
struct userdata * u ;
2004-09-28 23:49:54 +00:00
uint32_t l ;
pa_usec_t usec = 0 ;
2004-09-28 22:47:48 +00:00
assert ( sink & & sink - > userdata ) ;
u = sink - > userdata ;
2004-09-28 23:49:54 +00:00
l = DEFAULT_TLENGTH ;
if ( l > u - > requested_bytes ) {
l - = u - > requested_bytes ;
usec + = pa_bytes_to_usec ( l , & u - > sink - > sample_spec ) ;
}
usec + = u - > host_latency ;
return usec ;
}
2004-09-29 17:38:45 +00:00
# else
2006-01-11 01:17:39 +00:00
static pa_usec_t source_get_latency ( pa_source * source ) {
2004-09-29 17:38:45 +00:00
struct userdata * u ;
assert ( source & & source - > userdata ) ;
u = source - > userdata ;
return u - > host_latency ;
}
# endif
2004-09-28 23:49:54 +00:00
2006-01-11 01:17:39 +00:00
static void timeout_callback ( pa_mainloop_api * m , pa_time_event * e , PA_GCC_UNUSED const struct timeval * tv , void * userdata ) {
2004-09-28 23:49:54 +00:00
struct userdata * u = userdata ;
struct timeval ntv ;
assert ( m & & e & & u ) ;
request_latency ( u ) ;
2006-01-10 17:51:06 +00:00
pa_gettimeofday ( & ntv ) ;
2004-09-28 23:49:54 +00:00
ntv . tv_sec + = LATENCY_INTERVAL ;
m - > time_restart ( e , & ntv ) ;
2004-09-28 22:47:48 +00:00
}
2004-11-07 20:48:46 +00:00
static int load_key ( struct userdata * u , const char * fn ) {
assert ( u ) ;
u - > auth_cookie_in_property = 0 ;
if ( ! fn & & pa_authkey_prop_get ( u - > core , PA_NATIVE_COOKIE_PROPERTY_NAME , u - > auth_cookie , sizeof ( u - > auth_cookie ) ) > = 0 ) {
2004-12-11 00:10:41 +00:00
pa_log_debug ( __FILE__ " : using already loaded auth cookie. \n " ) ;
2004-11-07 20:48:46 +00:00
pa_authkey_prop_ref ( u - > core , PA_NATIVE_COOKIE_PROPERTY_NAME ) ;
u - > auth_cookie_in_property = 1 ;
return 0 ;
}
if ( ! fn )
fn = PA_NATIVE_COOKIE_FILE ;
2004-11-21 21:31:28 +00:00
if ( pa_authkey_load_auto ( fn , u - > auth_cookie , sizeof ( u - > auth_cookie ) ) < 0 )
2004-11-07 20:48:46 +00:00
return - 1 ;
2004-12-11 00:10:41 +00:00
pa_log_debug ( __FILE__ " : loading cookie from disk. \n " ) ;
2004-11-07 20:48:46 +00:00
if ( pa_authkey_prop_put ( u - > core , PA_NATIVE_COOKIE_PROPERTY_NAME , u - > auth_cookie , sizeof ( u - > auth_cookie ) ) > = 0 )
u - > auth_cookie_in_property = 1 ;
return 0 ;
}
2006-01-11 01:17:39 +00:00
int pa__init ( pa_core * c , pa_module * m ) {
pa_modargs * ma = NULL ;
2004-09-28 22:47:48 +00:00
struct userdata * u = NULL ;
2006-01-11 01:17:39 +00:00
pa_sample_spec ss ;
2004-09-28 23:49:54 +00:00
struct timeval ntv ;
2004-09-28 22:47:48 +00:00
assert ( c & & m ) ;
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
pa_log ( __FILE__ " : failed to parse module arguments \n " ) ;
2004-09-28 23:49:54 +00:00
goto fail ;
2004-09-28 22:47:48 +00:00
}
u = pa_xmalloc ( sizeof ( struct userdata ) ) ;
m - > userdata = u ;
u - > module = m ;
u - > core = c ;
u - > client = NULL ;
u - > pdispatch = NULL ;
u - > pstream = NULL ;
2004-09-29 17:38:45 +00:00
u - > server_name = NULL ;
# ifdef TUNNEL_SINK
u - > sink_name = pa_xstrdup ( pa_modargs_get_value ( ma , " sink " , NULL ) ) ; ;
2004-09-28 22:47:48 +00:00
u - > sink = NULL ;
2004-09-29 17:38:45 +00:00
u - > requested_bytes = 0 ;
# else
u - > source_name = pa_xstrdup ( pa_modargs_get_value ( ma , " source " , NULL ) ) ; ;
u - > source = NULL ;
# endif
2004-09-28 22:47:48 +00:00
u - > ctag = 1 ;
u - > device_index = u - > channel = PA_INVALID_INDEX ;
2004-09-28 23:49:54 +00:00
u - > host_latency = 0 ;
2004-11-07 20:48:46 +00:00
u - > auth_cookie_in_property = 0 ;
u - > time_event = NULL ;
if ( load_key ( u , pa_modargs_get_value ( ma , " cookie " , NULL ) ) < 0 )
2004-09-28 22:47:48 +00:00
goto fail ;
if ( ! ( u - > server_name = pa_xstrdup ( pa_modargs_get_value ( ma , " server " , NULL ) ) ) ) {
pa_log ( __FILE__ " : no server specified. \n " ) ;
goto fail ;
}
ss = c - > default_sample_spec ;
if ( pa_modargs_get_sample_spec ( ma , & ss ) < 0 ) {
pa_log ( __FILE__ " : invalid sample format specification \n " ) ;
goto fail ;
}
2004-11-17 00:05:25 +00:00
if ( ! ( u - > client = pa_socket_client_new_string ( c - > mainloop , u - > server_name , PA_NATIVE_DEFAULT_PORT ) ) ) {
pa_log ( __FILE__ " : failed to connect to server '%s' \n " , u - > server_name ) ;
goto fail ;
2004-09-28 22:47:48 +00:00
}
if ( ! u - > client )
goto fail ;
pa_socket_client_set_callback ( u - > client , on_connection , u ) ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
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 ) ) ) {
2004-09-28 22:47:48 +00:00
pa_log ( __FILE__ " : failed to create sink. \n " ) ;
goto fail ;
}
u - > sink - > notify = sink_notify ;
u - > sink - > get_latency = sink_get_latency ;
u - > sink - > userdata = u ;
u - > sink - > description = pa_sprintf_malloc ( " Tunnel to '%s%s%s' " , u - > sink_name ? u - > sink_name : " " , u - > sink_name ? " @ " : " " , u - > server_name ) ;
2004-09-29 17:38:45 +00:00
pa_sink_set_owner ( u - > sink , m ) ;
# else
2006-01-27 16:25:31 +00:00
if ( ! ( u - > source = pa_source_new ( c , __FILE__ , pa_modargs_get_value ( ma , " source_name " , DEFAULT_SOURCE_NAME ) , 0 , & ss , NULL ) ) ) {
2004-09-29 17:38:45 +00:00
pa_log ( __FILE__ " : failed to create source. \n " ) ;
goto fail ;
}
u - > source - > get_latency = source_get_latency ;
u - > source - > userdata = u ;
u - > source - > description = pa_sprintf_malloc ( " Tunnel to '%s%s%s' " , u - > source_name ? u - > source_name : " " , u - > source_name ? " @ " : " " , u - > server_name ) ;
pa_source_set_owner ( u - > source , m ) ;
# endif
2006-01-10 17:51:06 +00:00
pa_gettimeofday ( & ntv ) ;
2004-09-28 23:49:54 +00:00
ntv . tv_sec + = LATENCY_INTERVAL ;
u - > time_event = c - > mainloop - > time_new ( c - > mainloop , & ntv , timeout_callback , u ) ;
2004-09-28 22:47:48 +00:00
pa_modargs_free ( ma ) ;
return 0 ;
fail :
pa__done ( c , m ) ;
if ( ma )
pa_modargs_free ( ma ) ;
return - 1 ;
}
2006-01-11 01:17:39 +00:00
void pa__done ( pa_core * c , pa_module * m ) {
2004-09-28 22:47:48 +00:00
struct userdata * u ;
assert ( c & & m ) ;
if ( ! ( u = m - > userdata ) )
return ;
close_stuff ( u ) ;
2004-11-07 20:48:46 +00:00
if ( u - > auth_cookie_in_property )
pa_authkey_prop_unref ( c , PA_NATIVE_COOKIE_PROPERTY_NAME ) ;
2004-09-29 17:38:45 +00:00
# ifdef TUNNEL_SINK
2004-09-28 22:47:48 +00:00
pa_xfree ( u - > sink_name ) ;
2004-09-29 17:38:45 +00:00
# else
pa_xfree ( u - > source_name ) ;
# endif
2004-09-28 22:47:48 +00:00
pa_xfree ( u - > server_name ) ;
pa_xfree ( u ) ;
}