X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess_output%2Fudp.c;h=40964d4e6929a237078e621040e491370bd83d0f;hb=a78e273ec53ff8a6c3993f3deda0b893f8dd709a;hp=cde2e600dadf99db771856b66ef05f188e6f0ff9;hpb=cf6e33afe3728fe6cac1f54285f72a8f4f6744bc;p=vlc diff --git a/modules/access_output/udp.c b/modules/access_output/udp.c index cde2e600da..40964d4e69 100644 --- a/modules/access_output/udp.c +++ b/modules/access_output/udp.c @@ -1,7 +1,7 @@ /***************************************************************************** * udp.c ***************************************************************************** - * Copyright (C) 2001, 2002 VideoLAN + * Copyright (C) 2001-2007 the VideoLAN team * $Id$ * * Authors: Laurent Aimar @@ -19,21 +19,26 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + #include #include -#include #include #include +#include -#include -#include +#include +#include #ifdef HAVE_UNISTD_H # include @@ -42,15 +47,13 @@ #ifdef WIN32 # include # include -# ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -# endif #else # include #endif -#include "network.h" +#include +#define MAX_EMPTY_BLOCKS 200 /***************************************************************************** * Module descriptor @@ -62,47 +65,34 @@ static void Close( vlc_object_t * ); #define CACHING_TEXT N_("Caching value (ms)") #define CACHING_LONGTEXT N_( \ - "Allows you to modify the default caching value for UDP streams. This " \ - "value should be set in millisecond units." ) - -#define TTL_TEXT N_("Time To Live") -#define TTL_LONGTEXT N_("Allows you to define the time to live of the " \ - "outgoing stream.") + "Default caching value for outbound UDP streams. This " \ + "value should be set in milliseconds." ) #define GROUP_TEXT N_("Group packets") #define GROUP_LONGTEXT N_("Packets can be sent one by one at the right time " \ - "or by groups. This allows you to give the number " \ + "or by groups. You can choose the number " \ "of packets that will be sent at a time. It " \ "helps reducing the scheduling load on " \ "heavily-loaded systems." ) -#define LATE_TEXT N_("Late delay (ms)" ) -#define LATE_LONGTEXT N_("Late packets are dropped. This allows you to give " \ - "the time (in milliseconds) a packet is allowed to be" \ - " late.") -#define RAW_TEXT N_("Raw write") -#define RAW_LONGTEXT N_("If you enable this option, packets will be sent " \ - "directly, without trying to fill the MTU (ie, " \ - "without trying to make the biggest possible packets " \ - "in order to improve streaming)." ) +#define AUTO_MCAST_TEXT N_("Automatic multicast streaming") +#define AUTO_MCAST_LONGTEXT N_("Allocates an outbound multicast address " \ + "automatically.") vlc_module_begin(); set_description( _("UDP stream output") ); - set_shortname( N_( "UDP" ) ); + set_shortname( "UDP" ); set_category( CAT_SOUT ); set_subcategory( SUBCAT_SOUT_ACO ); add_integer( SOUT_CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); - add_integer( SOUT_CFG_PREFIX "ttl", 0, NULL,TTL_TEXT, TTL_LONGTEXT, - VLC_TRUE ); add_integer( SOUT_CFG_PREFIX "group", 1, NULL, GROUP_TEXT, GROUP_LONGTEXT, VLC_TRUE ); - add_integer( SOUT_CFG_PREFIX "late", 0, NULL, LATE_TEXT, LATE_LONGTEXT, - VLC_TRUE ); - add_bool( SOUT_CFG_PREFIX "raw", 0, NULL, RAW_TEXT, RAW_LONGTEXT, - VLC_TRUE ); + add_obsolete_integer( SOUT_CFG_PREFIX "late" ); + add_obsolete_bool( SOUT_CFG_PREFIX "raw" ); + add_bool( SOUT_CFG_PREFIX "auto-mcast", VLC_FALSE, NULL, AUTO_MCAST_TEXT, + AUTO_MCAST_LONGTEXT, VLC_TRUE ); set_capability( "sout access", 100 ); add_shortcut( "udp" ); - add_shortcut( "rtp" ); // Will work only with ts muxer set_callbacks( Open, Close ); vlc_module_end(); @@ -110,21 +100,28 @@ vlc_module_end(); * Exported prototypes *****************************************************************************/ -static const char *ppsz_sout_options[] = { +static const char *const ppsz_sout_options[] = { + "auto-mcast", "caching", - "ttl", "group", - "late", - "raw", NULL }; -static int Write ( sout_access_out_t *, block_t * ); -static int WriteRaw( sout_access_out_t *, block_t * ); +/* Options handled by the libvlc network core */ +static const char *const ppsz_core_options[] = { + "dscp", + "ttl", + "miface", + "miface-addr", + NULL +}; + +static ssize_t Write ( sout_access_out_t *, block_t * ); static int Seek ( sout_access_out_t *, off_t ); static void ThreadWrite( vlc_object_t * ); static block_t *NewUDPPacket( sout_access_out_t *, mtime_t ); +static const char *MakeRandMulticast (int family, char *buf, size_t buflen); typedef struct sout_access_thread_t { @@ -137,27 +134,24 @@ typedef struct sout_access_thread_t int i_handle; int64_t i_caching; - int64_t i_late; int i_group; + block_fifo_t *p_empty_blocks; } sout_access_thread_t; struct sout_access_out_sys_t { - int b_rtpts; // 1 if add rtp/ts header - uint16_t i_sequence_number; - uint32_t i_ssrc; - int i_mtu; + vlc_bool_t b_mtu_warning; block_t *p_buffer; sout_access_thread_t *p_thread; - vlc_bool_t b_mtu_warning; }; #define DEFAULT_PORT 1234 +#define RTP_HEADER_LENGTH 12 /***************************************************************************** * Open: open the file @@ -167,137 +161,124 @@ static int Open( vlc_object_t *p_this ) sout_access_out_t *p_access = (sout_access_out_t*)p_this; sout_access_out_sys_t *p_sys; - char *psz_parser; - char *psz_dst_addr; + char *psz_dst_addr = NULL; int i_dst_port; - module_t *p_network; - network_socket_t socket_desc; + int i_handle; - vlc_value_t val; + config_ChainParse( p_access, SOUT_CFG_PREFIX, + ppsz_sout_options, p_access->p_cfg ); + config_ChainParse( p_access, "", + ppsz_core_options, p_access->p_cfg ); - sout_CfgParse( p_access, SOUT_CFG_PREFIX, - ppsz_sout_options, p_access->p_cfg ); + if (var_Create (p_access, "dst-port", VLC_VAR_INTEGER) + || var_Create (p_access, "src-port", VLC_VAR_INTEGER) + || var_Create (p_access, "dst-addr", VLC_VAR_STRING) + || var_Create (p_access, "src-addr", VLC_VAR_STRING)) + { + return VLC_ENOMEM; + } - if( !( p_sys = malloc( sizeof( sout_access_out_sys_t ) ) ) ) + if( !( p_sys = calloc ( 1, sizeof( sout_access_out_sys_t ) ) ) ) { msg_Err( p_access, "not enough memory" ); - return VLC_EGENERIC; + return VLC_ENOMEM; } - memset( p_sys, 0, sizeof(sout_access_out_sys_t) ); p_access->p_sys = p_sys; - if( p_access->psz_access != NULL && - !strcmp( p_access->psz_access, "rtp" ) ) + i_dst_port = DEFAULT_PORT; + if (var_GetBool (p_access, SOUT_CFG_PREFIX"auto-mcast")) { - msg_Warn( p_access, "be careful that rtp output only works with ts " - "payload (not an error)" ); - p_sys->b_rtpts = 1; + char buf[INET6_ADDRSTRLEN]; + if (MakeRandMulticast (AF_INET, buf, sizeof (buf)) != NULL) + psz_dst_addr = strdup (buf); } else { - p_sys->b_rtpts = 0; - } + char *psz_parser = psz_dst_addr = strdup( p_access->psz_path ); - psz_parser = strdup( p_access->psz_name ); + if (psz_parser[0] == '[') + psz_parser = strchr (psz_parser, ']'); - psz_dst_addr = psz_parser; - i_dst_port = 0; - - if ( *psz_parser == '[' ) - { - while( *psz_parser && *psz_parser != ']' ) + psz_parser = strchr (psz_parser ?: psz_dst_addr, ':'); + if (psz_parser != NULL) { - psz_parser++; + *psz_parser++ = '\0'; + i_dst_port = atoi (psz_parser); } } - while( *psz_parser && *psz_parser != ':' ) - { - psz_parser++; - } - if( *psz_parser == ':' ) - { - *psz_parser = '\0'; - psz_parser++; - i_dst_port = atoi( psz_parser ); - } - if( i_dst_port <= 0 ) - { - i_dst_port = DEFAULT_PORT; - } p_sys->p_thread = vlc_object_create( p_access, sizeof( sout_access_thread_t ) ); if( !p_sys->p_thread ) { msg_Err( p_access, "out of memory" ); - return VLC_EGENERIC; + free (p_sys); + free (psz_dst_addr); + return VLC_ENOMEM; } + vlc_object_attach( p_sys->p_thread, p_access ); p_sys->p_thread->p_sout = p_access->p_sout; p_sys->p_thread->b_die = 0; p_sys->p_thread->b_error= 0; p_sys->p_thread->p_fifo = block_FifoNew( p_access ); + p_sys->p_thread->p_empty_blocks = block_FifoNew( p_access ); - socket_desc.i_type = NETWORK_UDP; - socket_desc.psz_server_addr = psz_dst_addr; - socket_desc.i_server_port = i_dst_port; - socket_desc.psz_bind_addr = ""; - socket_desc.i_bind_port = 0; - - var_Get( p_access, SOUT_CFG_PREFIX "ttl", &val ); - socket_desc.i_ttl = val.i_int; + i_handle = net_ConnectDgram( p_this, psz_dst_addr, i_dst_port, -1, + IPPROTO_UDP ); + free (psz_dst_addr); - p_sys->p_thread->p_private = (void*)&socket_desc; - if( !( p_network = module_Need( p_sys->p_thread, "network", NULL, 0 ) ) ) + if( i_handle == -1 ) { - msg_Err( p_access, "failed to open a connection (udp)" ); - return VLC_EGENERIC; + msg_Err( p_access, "failed to create raw UDP socket" ); + vlc_object_release (p_sys->p_thread); + free (p_sys); + return VLC_EGENERIC; } - module_Unneed( p_sys->p_thread, p_network ); - - p_sys->p_thread->i_handle = socket_desc.i_handle; + else + { + char addr[NI_MAXNUMERICHOST]; + int port; - var_Get( p_access, SOUT_CFG_PREFIX "caching", &val ); - p_sys->p_thread->i_caching = (int64_t)val.i_int * 1000; + if (net_GetSockAddress (i_handle, addr, &port) == 0) + { + msg_Dbg (p_access, "source: %s port %d", addr, port); + var_SetString (p_access, "src-addr", addr); + var_SetInteger (p_access, "src-port", port); + } - var_Get( p_access, SOUT_CFG_PREFIX "group", &val ); - p_sys->p_thread->i_group = val.i_int; + if (net_GetPeerAddress (i_handle, addr, &port) == 0) + { + msg_Dbg (p_access, "destination: %s port %d", addr, port); + var_SetString (p_access, "dst-addr", addr); + var_SetInteger (p_access, "dst-port", port); + } + } + p_sys->p_thread->i_handle = i_handle; + shutdown( i_handle, SHUT_RD ); - var_Get( p_access, SOUT_CFG_PREFIX "late", &val ); - p_sys->p_thread->i_late = (int64_t)val.i_int * 1000; + p_sys->p_thread->i_caching = + (int64_t)1000 * var_GetInteger( p_access, SOUT_CFG_PREFIX "caching"); + p_sys->p_thread->i_group = + var_GetInteger( p_access, SOUT_CFG_PREFIX "group" ); - p_sys->i_mtu = socket_desc.i_mtu; + p_sys->i_mtu = var_CreateGetInteger( p_this, "mtu" ); + p_sys->p_buffer = NULL; -#ifdef WIN32 if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite, VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) ) -#else - if( vlc_thread_create( p_sys->p_thread, "sout write thread", ThreadWrite, - VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) ) -#endif { msg_Err( p_access->p_sout, "cannot spawn sout access thread" ); - vlc_object_destroy( p_sys->p_thread ); + net_Close (i_handle); + vlc_object_release( p_sys->p_thread ); + free (p_sys); return VLC_EGENERIC; } - srand( (uint32_t)mdate()); - p_sys->p_buffer = NULL; - p_sys->i_sequence_number = rand()&0xffff; - p_sys->i_ssrc = rand()&0xffffffff; - - var_Get( p_access, SOUT_CFG_PREFIX "raw", &val ); - if( val.b_bool ) p_access->pf_write = WriteRaw; - else p_access->pf_write = Write; - + p_access->pf_write = Write; p_access->pf_seek = Seek; - msg_Dbg( p_access, "udp access output opened(%s:%d)", - psz_dst_addr, i_dst_port ); - - free( psz_dst_addr ); - /* update p_sout->i_out_pace_nocontrol */ p_access->p_sout->i_out_pace_nocontrol++; @@ -313,41 +294,49 @@ static void Close( vlc_object_t * p_this ) sout_access_out_sys_t *p_sys = p_access->p_sys; int i; - p_sys->p_thread->b_die = 1; + vlc_object_kill( p_sys->p_thread ); + block_FifoWake( p_sys->p_thread->p_fifo ); + for( i = 0; i < 10; i++ ) { block_t *p_dummy = block_New( p_access, p_sys->i_mtu ); p_dummy->i_dts = 0; p_dummy->i_pts = 0; p_dummy->i_length = 0; + memset( p_dummy->p_buffer, 0, p_dummy->i_buffer ); block_FifoPut( p_sys->p_thread->p_fifo, p_dummy ); } vlc_thread_join( p_sys->p_thread ); block_FifoRelease( p_sys->p_thread->p_fifo ); + block_FifoRelease( p_sys->p_thread->p_empty_blocks ); if( p_sys->p_buffer ) block_Release( p_sys->p_buffer ); net_Close( p_sys->p_thread->i_handle ); + vlc_object_detach( p_sys->p_thread ); + vlc_object_release( p_sys->p_thread ); /* update p_sout->i_out_pace_nocontrol */ p_access->p_sout->i_out_pace_nocontrol--; - msg_Dbg( p_access, "udp access output closed" ); + msg_Dbg( p_access, "UDP access output closed" ); free( p_sys ); } /***************************************************************************** * Write: standard write on a file descriptor. *****************************************************************************/ -static int Write( sout_access_out_t *p_access, block_t *p_buffer ) +static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) { sout_access_out_sys_t *p_sys = p_access->p_sys; + int i_len = 0; while( p_buffer ) { block_t *p_next; int i_packets = 0; + mtime_t now = mdate(); if( !p_sys->b_mtu_warning && p_buffer->i_buffer > p_sys->i_mtu ) { @@ -360,13 +349,22 @@ static int Write( sout_access_out_t *p_access, block_t *p_buffer ) if( p_sys->p_buffer && p_sys->p_buffer->i_buffer + p_buffer->i_buffer > p_sys->i_mtu ) { + if( p_sys->p_buffer->i_dts + p_sys->p_thread->i_caching < now ) + { + msg_Dbg( p_access, "late packet for UDP input (" I64Fd ")", + now - p_sys->p_buffer->i_dts + - p_sys->p_thread->i_caching ); + } block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer ); p_sys->p_buffer = NULL; } + i_len += p_buffer->i_buffer; while( p_buffer->i_buffer ) { - int i_write = __MIN( p_buffer->i_buffer, p_sys->i_mtu ); + int i_payload_size = p_sys->i_mtu; + + int i_write = __MIN( p_buffer->i_buffer, i_payload_size ); i_packets++; @@ -392,6 +390,12 @@ static int Write( sout_access_out_t *p_access, block_t *p_buffer ) if( p_sys->p_buffer->i_buffer == p_sys->i_mtu || i_packets > 1 ) { /* Flush */ + if( p_sys->p_buffer->i_dts + p_sys->p_thread->i_caching < now ) + { + msg_Dbg( p_access, "late packet for udp input (" I64Fd ")", + mdate() - p_sys->p_buffer->i_dts + - p_sys->p_thread->i_caching ); + } block_FifoPut( p_sys->p_thread->p_fifo, p_sys->p_buffer ); p_sys->p_buffer = NULL; } @@ -402,19 +406,7 @@ static int Write( sout_access_out_t *p_access, block_t *p_buffer ) p_buffer = p_next; } - return( p_sys->p_thread->b_error ? -1 : 0 ); -} - -/***************************************************************************** - * WriteRaw: write p_buffer without trying to fill mtu - *****************************************************************************/ -static int WriteRaw( sout_access_out_t *p_access, block_t *p_buffer ) -{ - sout_access_out_sys_t *p_sys = p_access->p_sys; - - block_FifoPut( p_sys->p_thread->p_fifo, p_buffer ); - - return( p_sys->p_thread->b_error ? -1 : 0 ); + return( p_sys->p_thread->b_error ? -1 : i_len ); } /***************************************************************************** @@ -434,35 +426,26 @@ static block_t *NewUDPPacket( sout_access_out_t *p_access, mtime_t i_dts) sout_access_out_sys_t *p_sys = p_access->p_sys; block_t *p_buffer; - p_buffer = block_New( p_access->p_sout, p_sys->i_mtu ); - p_buffer->i_dts = i_dts; - p_buffer->i_buffer = 0; - - if( p_sys->b_rtpts ) + while ( block_FifoCount( p_sys->p_thread->p_empty_blocks ) > MAX_EMPTY_BLOCKS ) { - mtime_t i_timestamp = p_buffer->i_dts * 9 / 100; - - /* add rtp/ts header */ - p_buffer->p_buffer[0] = 0x80; - p_buffer->p_buffer[1] = 0x21; // mpeg2-ts - - p_buffer->p_buffer[2] = ( p_sys->i_sequence_number >> 8 )&0xff; - p_buffer->p_buffer[3] = p_sys->i_sequence_number&0xff; - p_sys->i_sequence_number++; - - p_buffer->p_buffer[4] = ( i_timestamp >> 24 )&0xff; - p_buffer->p_buffer[5] = ( i_timestamp >> 16 )&0xff; - p_buffer->p_buffer[6] = ( i_timestamp >> 8 )&0xff; - p_buffer->p_buffer[7] = i_timestamp&0xff; - - p_buffer->p_buffer[ 8] = ( p_sys->i_ssrc >> 24 )&0xff; - p_buffer->p_buffer[ 9] = ( p_sys->i_ssrc >> 16 )&0xff; - p_buffer->p_buffer[10] = ( p_sys->i_ssrc >> 8 )&0xff; - p_buffer->p_buffer[11] = p_sys->i_ssrc&0xff; + p_buffer = block_FifoGet( p_sys->p_thread->p_empty_blocks ); + block_Release( p_buffer ); + } - p_buffer->i_buffer = 12; + if( block_FifoCount( p_sys->p_thread->p_empty_blocks ) == 0 ) + { + p_buffer = block_New( p_access->p_sout, p_sys->i_mtu ); + } + else + { + p_buffer = block_FifoGet(p_sys->p_thread->p_empty_blocks ); + p_buffer->i_flags = 0; + p_buffer = block_Realloc( p_buffer, 0, p_sys->i_mtu ); } + p_buffer->i_dts = i_dts; + p_buffer->i_buffer = 0; + return p_buffer; } @@ -480,8 +463,21 @@ static void ThreadWrite( vlc_object_t *p_this ) { block_t *p_pk; mtime_t i_date, i_sent; - +#if 0 + if( (i++ % 1000)==0 ) { + int i = 0; + int j = 0; + block_t *p_tmp = p_thread->p_empty_blocks->p_first; + while( p_tmp ) { p_tmp = p_tmp->p_next; i++;} + p_tmp = p_thread->p_fifo->p_first; + while( p_tmp ) { p_tmp = p_tmp->p_next; j++;} + msg_Dbg( p_thread, "fifo depth: %d/%d, empty blocks: %d/%d", + p_thread->p_fifo->i_depth, j,p_thread->p_empty_blocks->i_depth,i ); + } +#endif p_pk = block_FifoGet( p_thread->p_fifo ); + if( p_pk == NULL ) + continue; /* forced wake-up */ i_date = p_thread->i_caching + p_pk->i_dts; if( i_date_last > 0 ) @@ -492,45 +488,32 @@ static void ThreadWrite( vlc_object_t *p_this ) msg_Dbg( p_thread, "mmh, hole ("I64Fd" > 2s) -> drop", i_date - i_date_last ); - block_Release( p_pk ); - i_date_last = i_date; - i_dropped_packets++; - continue; - } - else if( i_date - i_date_last < -15000 ) - { - if( !i_dropped_packets ) - msg_Dbg( p_thread, "mmh, packets in the past ("I64Fd")" - " -> drop", i_date - i_date_last ); + block_FifoPut( p_thread->p_empty_blocks, p_pk ); - block_Release( p_pk ); i_date_last = i_date; i_dropped_packets++; continue; } - } - - i_sent = mdate(); - if( p_thread->i_late > 0 && i_sent > i_date + p_thread->i_late ) - { - if( !i_dropped_packets ) + else if( i_date - i_date_last < -1000 ) { - msg_Dbg( p_thread, "late packet to send (" I64Fd ") -> drop", - i_sent - i_date ); + if( !i_dropped_packets ) + msg_Dbg( p_thread, "mmh, packets in the past ("I64Fd")", + i_date_last - i_date ); } - block_Release( p_pk ); - i_date_last = i_date; - i_dropped_packets++; - continue; } i_to_send--; - if ( !i_to_send || (p_pk->i_flags & BLOCK_FLAG_CLOCK) ) + if( !i_to_send || (p_pk->i_flags & BLOCK_FLAG_CLOCK) ) { mwait( i_date ); i_to_send = p_thread->i_group; } - send( p_thread->i_handle, p_pk->p_buffer, p_pk->i_buffer, 0 ); + ssize_t val = send( p_thread->i_handle, p_pk->p_buffer, + p_pk->i_buffer, 0 ); + if (val == -1) + { + msg_Warn( p_thread, "send error: %m" ); + } if( i_dropped_packets ) { @@ -547,7 +530,41 @@ static void ThreadWrite( vlc_object_t *p_this ) } #endif - block_Release( p_pk ); + block_FifoPut( p_thread->p_empty_blocks, p_pk ); + i_date_last = i_date; } } + + +static const char *MakeRandMulticast (int family, char *buf, size_t buflen) +{ + uint32_t rand = (getpid() & 0xffff) + | (uint32_t)(((mdate () >> 10) & 0xffff) << 16); + + switch (family) + { +#ifdef AF_INET6 + case AF_INET6: + { + struct in6_addr addr; + memcpy (&addr, "\xff\x38\x00\x00" "\x00\x00\x00\x00" + "\x00\x00\x00\x00", 12); + rand |= 0x80000000; + memcpy (addr.s6_addr + 12, &(uint32_t){ htonl (rand) }, 4); + return inet_ntop (family, &addr, buf, buflen); + } +#endif + + case AF_INET: + { + struct in_addr addr; + addr.s_addr = htonl ((rand & 0xffffff) | 0xe8000000); + return inet_ntop (family, &addr, buf, buflen); + } + } +#ifdef EAFNOSUPPORT + errno = EAFNOSUPPORT; +#endif + return NULL; +}