2006-04-14 23:47:33 +00:00
/***
2006-06-19 21:53:48 +00:00
This file is part of PulseAudio .
2007-02-13 15:35:19 +00:00
Copyright 2006 Lennart Poettering
2007-05-29 17:24:48 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is free software ; you can redistribute it and / or modify
2006-04-14 23:47:33 +00:00
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
2007-05-29 17:24:48 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is distributed in the hope that it will be useful , but
2006-04-14 23:47:33 +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-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +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
2006-04-14 23:47:33 +00:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdio.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <errno.h>
# include <string.h>
# include <unistd.h>
2006-06-19 21:53:48 +00:00
# include <pulse/timeval.h>
# include <pulse/util.h>
# 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/module.h>
# include <pulsecore/llist.h>
# include <pulsecore/source.h>
# include <pulsecore/source-output.h>
# include <pulsecore/memblockq.h>
# include <pulsecore/log.h>
# include <pulsecore/core-util.h>
# include <pulsecore/modargs.h>
# include <pulsecore/namereg.h>
2007-10-28 19:13:50 +00:00
# include <pulsecore/sample-util.h>
# include <pulsecore/macro.h>
# include <pulsecore/socket-util.h>
2006-04-14 23:47:33 +00:00
2006-04-16 10:56:45 +00:00
# include "module-rtp-send-symdef.h"
2006-04-14 23:47:33 +00:00
# include "rtp.h"
# include "sdp.h"
# include "sap.h"
2007-11-09 18:25:40 +00:00
PA_MODULE_AUTHOR ( " Lennart Poettering " ) ;
PA_MODULE_DESCRIPTION ( " Read data from source and send it to the network via RTP/SAP/SDP " ) ;
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( FALSE ) ;
2006-04-14 23:47:33 +00:00
PA_MODULE_USAGE (
2006-04-16 00:16:53 +00:00
" source=<name of the source> "
2006-04-14 23:47:33 +00:00
" format=<sample format> "
" channels=<number of channels> "
" rate=<sample rate> "
2006-04-16 12:44:15 +00:00
" destination=<destination IP address> "
2006-04-14 23:47:33 +00:00
" port=<port number> "
" mtu=<maximum transfer unit> "
2006-04-16 00:16:53 +00:00
" loop=<loopback to local host?> "
2007-11-09 18:25:40 +00:00
) ;
2006-04-14 23:47:33 +00:00
2006-04-16 09:14:55 +00:00
# define DEFAULT_PORT 46000
2006-04-14 23:47:33 +00:00
# define SAP_PORT 9875
2006-04-16 11:13:20 +00:00
# define DEFAULT_DESTINATION "224.0.0.56"
2006-04-14 23:47:33 +00:00
# define MEMBLOCKQ_MAXLENGTH (1024*170)
2006-04-16 09:14:55 +00:00
# define DEFAULT_MTU 1280
2007-10-28 19:13:50 +00:00
# define SAP_INTERVAL 5
2006-04-14 23:47:33 +00:00
static const char * const valid_modargs [ ] = {
" source " ,
" format " ,
" channels " ,
" rate " ,
" destination " ,
" port " ,
2006-08-23 22:28:53 +00:00
" mtu " ,
2006-04-16 00:16:53 +00:00
" loop " ,
2006-04-14 23:47:33 +00:00
NULL
} ;
struct userdata {
pa_module * module ;
pa_source_output * source_output ;
pa_memblockq * memblockq ;
pa_rtp_context rtp_context ;
pa_sap_context sap_context ;
size_t mtu ;
pa_time_event * sap_event ;
} ;
2007-10-28 19:13:50 +00:00
/* Called from I/O thread context */
static int source_output_process_msg ( pa_msgobject * o , int code , void * data , int64_t offset , pa_memchunk * chunk ) {
struct userdata * u ;
pa_assert_se ( u = PA_SOURCE_OUTPUT ( o ) - > userdata ) ;
switch ( code ) {
case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY :
* ( ( pa_usec_t * ) data ) = pa_bytes_to_usec ( pa_memblockq_get_length ( u - > memblockq ) , & u - > source_output - > sample_spec ) ;
/* Fall through, the default handler will add in the extra
* latency added by the resampler */
break ;
}
return pa_source_output_process_msg ( o , code , data , offset , chunk ) ;
}
/* Called from I/O thread context */
2006-04-14 23:47:33 +00:00
static void source_output_push ( pa_source_output * o , const pa_memchunk * chunk ) {
struct userdata * u ;
2007-10-28 19:13:50 +00:00
pa_source_output_assert_ref ( o ) ;
pa_assert_se ( u = o - > userdata ) ;
2006-04-14 23:47:33 +00:00
if ( pa_memblockq_push ( u - > memblockq , chunk ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Failed to push chunk into memblockq. " ) ;
2006-04-14 23:47:33 +00:00
return ;
}
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
pa_rtp_send ( & u - > rtp_context , u - > mtu , u - > memblockq ) ;
}
2007-10-28 19:13:50 +00:00
/* Called from main context */
2006-04-14 23:47:33 +00:00
static void source_output_kill ( pa_source_output * o ) {
struct userdata * u ;
2007-10-28 19:13:50 +00:00
pa_source_output_assert_ref ( o ) ;
pa_assert_se ( u = o - > userdata ) ;
2006-04-14 23:47:33 +00:00
2008-08-05 23:56:25 +02:00
pa_module_unload_request ( u - > module , TRUE ) ;
2006-04-14 23:47:33 +00:00
2007-10-28 19:13:50 +00:00
pa_source_output_unlink ( u - > source_output ) ;
2006-04-14 23:47:33 +00:00
pa_source_output_unref ( u - > source_output ) ;
u - > source_output = NULL ;
}
2006-04-16 00:16:53 +00:00
static void sap_event_cb ( pa_mainloop_api * m , pa_time_event * t , const struct timeval * tv , void * userdata ) {
2006-04-14 23:47:33 +00:00
struct userdata * u = userdata ;
struct timeval next ;
2007-05-29 17:24:48 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( m ) ;
pa_assert ( t ) ;
pa_assert ( tv ) ;
pa_assert ( u ) ;
2006-04-14 23:47:33 +00:00
pa_sap_send ( & u - > sap_context , 0 ) ;
pa_gettimeofday ( & next ) ;
2007-10-28 19:13:50 +00:00
pa_timeval_add ( & next , SAP_INTERVAL * PA_USEC_PER_SEC ) ;
2006-04-14 23:47:33 +00:00
m - > time_restart ( t , & next ) ;
}
2007-10-28 19:13:50 +00:00
int pa__init ( pa_module * m ) {
2006-04-14 23:47:33 +00:00
struct userdata * u ;
pa_modargs * ma = NULL ;
const char * dest ;
uint32_t port = DEFAULT_PORT , mtu ;
int af , fd = - 1 , sap_fd = - 1 ;
pa_source * s ;
pa_sample_spec ss ;
pa_channel_map cm ;
struct sockaddr_in sa4 , sap_sa4 ;
struct sockaddr_in6 sa6 , sap_sa6 ;
struct sockaddr_storage sa_dst ;
pa_source_output * o = NULL ;
uint8_t payload ;
char * p ;
2007-11-13 17:37:44 +00:00
int r , j ;
2006-04-14 23:47:33 +00:00
socklen_t k ;
struct timeval tv ;
2006-04-16 00:16:53 +00:00
char hn [ 128 ] , * n ;
2007-11-13 17:37:44 +00:00
pa_bool_t loop = FALSE ;
2006-08-13 19:55:17 +00:00
pa_source_output_new_data data ;
2007-05-29 17:24:48 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( m ) ;
2006-04-14 23:47:33 +00:00
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to parse module arguments " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
if ( ! ( s = pa_namereg_get ( m - > core , pa_modargs_get_value ( ma , " source " , NULL ) , PA_NAMEREG_SOURCE , 1 ) ) ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Source does not exist. " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
2006-04-16 00:16:53 +00:00
if ( pa_modargs_get_value_boolean ( ma , " loop " , & loop ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to parse \" loop \" parameter. " ) ;
2006-04-16 00:16:53 +00:00
goto fail ;
}
2006-04-14 23:47:33 +00:00
ss = s - > sample_spec ;
pa_rtp_sample_spec_fixup ( & ss ) ;
cm = s - > channel_map ;
if ( pa_modargs_get_sample_spec ( ma , & ss ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to parse sample specification " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
if ( ! pa_rtp_sample_spec_valid ( & ss ) ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Specified sample type not compatible with RTP " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
if ( ss . channels ! = cm . channels )
2006-05-16 23:47:38 +00:00
pa_channel_map_init_auto ( & cm , ss . channels , PA_CHANNEL_MAP_AIFF ) ;
2006-04-14 23:47:33 +00:00
2006-04-16 00:16:53 +00:00
payload = pa_rtp_payload_from_sample_spec ( & ss ) ;
2006-04-14 23:47:33 +00:00
2007-10-28 19:13:50 +00:00
mtu = pa_frame_align ( DEFAULT_MTU , & ss ) ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
if ( pa_modargs_get_value_u32 ( ma , " mtu " , & mtu ) < 0 | | mtu < 1 | | mtu % pa_frame_size ( & ss ) ! = 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Invalid MTU. " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
2006-04-16 09:14:55 +00:00
2006-04-16 10:53:27 +00:00
port = DEFAULT_PORT + ( ( rand ( ) % 512 ) < < 1 ) ;
2006-04-14 23:47:33 +00:00
if ( pa_modargs_get_value_u32 ( ma , " port " , & port ) < 0 | | port < 1 | | port > 0xFFFF ) {
2006-08-18 21:38:40 +00:00
pa_log ( " port= expects a numerical argument between 1 and 65535. " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
2006-04-16 10:53:27 +00:00
if ( port & 1 )
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Port number not even as suggested in RFC3550! " ) ;
2006-04-16 10:53:27 +00:00
2006-04-16 00:16:53 +00:00
dest = pa_modargs_get_value ( ma , " destination " , DEFAULT_DESTINATION ) ;
if ( inet_pton ( AF_INET6 , dest , & sa6 . sin6_addr ) > 0 ) {
sa6 . sin6_family = af = AF_INET6 ;
sa6 . sin6_port = htons ( port ) ;
sap_sa6 = sa6 ;
sap_sa6 . sin6_port = htons ( SAP_PORT ) ;
} else if ( inet_pton ( AF_INET , dest , & sa4 . sin_addr ) > 0 ) {
sa4 . sin_family = af = AF_INET ;
sa4 . sin_port = htons ( port ) ;
sap_sa4 = sa4 ;
sap_sa4 . sin_port = htons ( SAP_PORT ) ;
} else {
2007-10-28 19:13:50 +00:00
pa_log ( " Invalid destination '%s' " , dest ) ;
2006-04-16 00:16:53 +00:00
goto fail ;
2006-04-14 23:47:33 +00:00
}
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
if ( ( fd = socket ( af , SOCK_DGRAM , 0 ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " socket() failed: %s " , pa_cstrerror ( errno ) ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
if ( connect ( fd , af = = AF_INET ? ( struct sockaddr * ) & sa4 : ( struct sockaddr * ) & sa6 , af = = AF_INET ? sizeof ( sa4 ) : sizeof ( sa6 ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " connect() failed: %s " , pa_cstrerror ( errno ) ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
if ( ( sap_fd = socket ( af , SOCK_DGRAM , 0 ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " socket() failed: %s " , pa_cstrerror ( errno ) ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
if ( connect ( sap_fd , af = = AF_INET ? ( struct sockaddr * ) & sap_sa4 : ( struct sockaddr * ) & sap_sa6 , af = = AF_INET ? sizeof ( sap_sa4 ) : sizeof ( sap_sa6 ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " connect() failed: %s " , pa_cstrerror ( errno ) ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
2007-11-13 17:37:44 +00:00
j = ! ! loop ;
if ( setsockopt ( fd , IPPROTO_IP , IP_MULTICAST_LOOP , & j , sizeof ( j ) ) < 0 | |
setsockopt ( sap_fd , IPPROTO_IP , IP_MULTICAST_LOOP , & j , sizeof ( j ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " IP_MULTICAST_LOOP failed: %s " , pa_cstrerror ( errno ) ) ;
2006-04-16 00:16:53 +00:00
goto fail ;
}
2006-08-13 19:55:17 +00:00
2007-10-28 19:13:50 +00:00
/* If the socket queue is full, let's drop packets */
pa_make_fd_nonblock ( fd ) ;
pa_make_udp_socket_low_delay ( fd ) ;
pa_make_fd_cloexec ( fd ) ;
pa_make_fd_cloexec ( sap_fd ) ;
2006-08-13 19:55:17 +00:00
pa_source_output_new_data_init ( & data ) ;
2008-05-15 23:34:41 +00:00
pa_proplist_sets ( data . proplist , PA_PROP_MEDIA_NAME , " RTP Monitor Stream " ) ;
pa_proplist_sets ( data . proplist , " rtp.destination " , dest ) ;
pa_proplist_setf ( data . proplist , " rtp.mtu " , " %lu " , ( unsigned long ) mtu ) ;
pa_proplist_setf ( data . proplist , " rtp.port " , " %lu " , ( unsigned long ) port ) ;
2006-08-13 19:55:17 +00:00
data . driver = __FILE__ ;
data . module = m ;
2006-08-13 20:44:00 +00:00
data . source = s ;
2006-08-13 19:55:17 +00:00
pa_source_output_new_data_set_sample_spec ( & data , & ss ) ;
pa_source_output_new_data_set_channel_map ( & data , & cm ) ;
2007-05-29 17:24:48 +00:00
2008-05-15 23:34:41 +00:00
o = pa_source_output_new ( m - > core , & data , 0 ) ;
pa_source_output_new_data_done ( & data ) ;
if ( ! o ) {
2006-08-18 21:38:40 +00:00
pa_log ( " failed to create source output. " ) ;
2006-04-14 23:47:33 +00:00
goto fail ;
}
2007-10-28 19:13:50 +00:00
o - > parent . process_msg = source_output_process_msg ;
2006-04-14 23:47:33 +00:00
o - > push = source_output_push ;
o - > kill = source_output_kill ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
u = pa_xnew ( struct userdata , 1 ) ;
m - > userdata = u ;
o - > userdata = u ;
u - > module = m ;
u - > source_output = o ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
u - > memblockq = pa_memblockq_new (
0 ,
MEMBLOCKQ_MAXLENGTH ,
MEMBLOCKQ_MAXLENGTH ,
pa_frame_size ( & ss ) ,
1 ,
0 ,
2008-05-15 23:34:41 +00:00
0 ,
2006-08-18 19:55:18 +00:00
NULL ) ;
2006-04-14 23:47:33 +00:00
u - > mtu = mtu ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
k = sizeof ( sa_dst ) ;
2007-10-28 19:13:50 +00:00
pa_assert_se ( ( r = getsockname ( fd , ( struct sockaddr * ) & sa_dst , & k ) ) > = 0 ) ;
2006-04-16 00:16:53 +00:00
2006-06-20 13:02:34 +00:00
n = pa_sprintf_malloc ( " PulseAudio RTP Stream on %s " , pa_get_fqdn ( hn , sizeof ( hn ) ) ) ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
p = pa_sdp_build ( af ,
af = = AF_INET ? ( void * ) & ( ( struct sockaddr_in * ) & sa_dst ) - > sin_addr : ( void * ) & ( ( struct sockaddr_in6 * ) & sa_dst ) - > sin6_addr ,
af = = AF_INET ? ( void * ) & sa4 . sin_addr : ( void * ) & sa6 . sin6_addr ,
2006-04-16 00:16:53 +00:00
n , port , payload , & ss ) ;
pa_xfree ( n ) ;
2007-05-29 17:24:48 +00:00
2007-10-28 19:13:50 +00:00
pa_rtp_context_init_send ( & u - > rtp_context , fd , m - > core - > cookie , payload , pa_frame_size ( & ss ) ) ;
2006-04-14 23:47:33 +00:00
pa_sap_context_init_send ( & u - > sap_context , sap_fd , p ) ;
2006-08-18 21:38:40 +00:00
pa_log_info ( " RTP stream initialized with mtu %u on %s:%u, SSRC=0x%08x, payload=%u, initial sequence #%u " , mtu , dest , port , u - > rtp_context . ssrc , payload , u - > rtp_context . sequence ) ;
2007-10-28 19:13:50 +00:00
pa_log_info ( " SDP-Data: \n %s \n EOF " , p ) ;
2006-04-16 00:16:53 +00:00
2006-04-14 23:47:33 +00:00
pa_sap_send ( & u - > sap_context , 0 ) ;
pa_gettimeofday ( & tv ) ;
2007-10-28 19:13:50 +00:00
pa_timeval_add ( & tv , SAP_INTERVAL * PA_USEC_PER_SEC ) ;
u - > sap_event = m - > core - > mainloop - > time_new ( m - > core - > mainloop , & tv , sap_event_cb , u ) ;
pa_source_output_put ( u - > source_output ) ;
2006-04-14 23:47:33 +00:00
pa_modargs_free ( ma ) ;
return 0 ;
fail :
if ( ma )
pa_modargs_free ( ma ) ;
if ( fd > = 0 )
2007-10-28 19:13:50 +00:00
pa_close ( fd ) ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
if ( sap_fd > = 0 )
2007-10-28 19:13:50 +00:00
pa_close ( sap_fd ) ;
2006-04-14 23:47:33 +00:00
if ( o ) {
2007-10-28 19:13:50 +00:00
pa_source_output_unlink ( o ) ;
2006-04-14 23:47:33 +00:00
pa_source_output_unref ( o ) ;
}
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
return - 1 ;
}
2007-10-28 19:13:50 +00:00
void pa__done ( pa_module * m ) {
2006-04-14 23:47:33 +00:00
struct userdata * u ;
2007-10-28 19:13:50 +00:00
pa_assert ( m ) ;
2006-04-14 23:47:33 +00:00
if ( ! ( u = m - > userdata ) )
return ;
2007-10-28 19:13:50 +00:00
if ( u - > sap_event )
m - > core - > mainloop - > time_free ( u - > sap_event ) ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
if ( u - > source_output ) {
2007-10-28 19:13:50 +00:00
pa_source_output_unlink ( u - > source_output ) ;
2006-04-14 23:47:33 +00:00
pa_source_output_unref ( u - > source_output ) ;
}
pa_rtp_context_destroy ( & u - > rtp_context ) ;
pa_sap_send ( & u - > sap_context , 1 ) ;
pa_sap_context_destroy ( & u - > sap_context ) ;
2007-10-28 19:13:50 +00:00
if ( u - > memblockq )
pa_memblockq_free ( u - > memblockq ) ;
2007-05-29 17:24:48 +00:00
2006-04-14 23:47:33 +00:00
pa_xfree ( u ) ;
}