2001-07-22 17:18:56 +03:00
/*
* UDP prototype streaming system
2009-01-19 17:46:40 +02:00
* Copyright ( c ) 2000 , 2001 , 2002 Fabrice Bellard
2001-07-22 17:18:56 +03:00
*
2006-10-07 18:30:46 +03:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2002-05-26 01:34:32 +03:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 18:30:46 +03:00
* version 2.1 of the License , or ( at your option ) any later version .
2001-07-22 17:18:56 +03:00
*
2006-10-07 18:30:46 +03:00
* FFmpeg is distributed in the hope that it will be useful ,
2001-07-22 17:18:56 +03:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2002-05-26 01:34:32 +03:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2001-07-22 17:18:56 +03:00
*
2002-05-26 01:34:32 +03:00
* You should have received a copy of the GNU Lesser General Public
2006-10-07 18:30:46 +03:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-13 00:43:26 +02:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-07-22 17:18:56 +03:00
*/
2008-04-16 19:52:35 +03:00
/**
2010-04-20 17:45:34 +03:00
* @ file
2008-04-16 19:52:35 +03:00
* UDP protocol
*/
2015-10-03 14:41:00 +02:00
# define _DEFAULT_SOURCE
2008-08-25 09:20:00 +03:00
# define _BSD_SOURCE /* Needed for using struct ip_mreq with recent glibc */
2011-05-11 18:52:51 +03:00
2001-08-14 00:37:10 +03:00
# include "avformat.h"
2011-03-08 11:35:52 +02:00
# include "avio_internal.h"
2016-05-25 00:06:39 +02:00
# include "libavutil/avassert.h"
2011-02-16 10:52:38 +02:00
# include "libavutil/parseutils.h"
2011-05-13 18:16:15 +03:00
# include "libavutil/fifo.h"
2011-08-25 22:33:54 +03:00
# include "libavutil/intreadwrite.h"
2011-11-09 12:45:01 +03:00
# include "libavutil/avstring.h"
2012-09-13 14:10:26 +03:00
# include "libavutil/opt.h"
# include "libavutil/log.h"
2013-03-12 17:25:59 +03:00
# include "libavutil/time.h"
2010-03-15 01:59:48 +02:00
# include "internal.h"
2007-02-04 19:05:44 +02:00
# include "network.h"
2007-11-21 18:33:06 +02:00
# include "os_support.h"
2011-04-04 21:40:38 +03:00
# include "url.h"
2018-09-14 01:17:35 +02:00
# include "ip.h"
2011-06-04 17:55:22 +03:00
2016-09-03 15:18:40 +02:00
# ifdef __APPLE__
# include "TargetConditionals.h"
# endif
2014-11-05 11:59:44 +02:00
# if HAVE_UDPLITE_H
# include "udplite.h"
# else
/* On many Linux systems, udplite.h is missing but the kernel supports UDP-Lite.
* So , we provide a fallback here .
*/
# define UDPLITE_SEND_CSCOV 10
# define UDPLITE_RECV_CSCOV 11
# endif
# ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136
# endif
2020-03-02 22:48:41 +02:00
# if HAVE_W32THREADS
# undef HAVE_PTHREAD_CANCEL
# define HAVE_PTHREAD_CANCEL 1
# endif
2012-05-08 20:36:06 +03:00
# if HAVE_PTHREAD_CANCEL
2020-03-02 22:48:41 +02:00
# include "libavutil/thread.h"
2011-06-04 17:55:22 +03:00
# endif
2005-08-12 17:01:03 +03:00
# ifndef IPV6_ADD_MEMBERSHIP
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
# endif
2011-08-25 22:33:54 +03:00
# define UDP_TX_BUF_SIZE 32768
2020-01-14 20:18:01 +02:00
# define UDP_RX_BUF_SIZE 393216
2011-08-25 22:33:54 +03:00
# define UDP_MAX_PKT_SIZE 65536
2014-11-05 11:59:44 +02:00
# define UDP_HEADER_SIZE 8
2011-08-25 22:33:54 +03:00
2014-09-22 10:19:33 +03:00
typedef struct UDPContext {
2012-09-13 14:10:26 +03:00
const AVClass * class ;
2002-07-24 20:45:41 +03:00
int udp_fd ;
int ttl ;
2014-11-05 11:59:44 +02:00
int udplite_coverage ;
2008-11-12 23:40:53 +02:00
int buffer_size ;
2015-03-13 18:00:12 +02:00
int pkt_size ;
2002-07-24 20:45:41 +03:00
int is_multicast ;
2014-04-09 06:21:52 +03:00
int is_broadcast ;
2002-07-24 20:45:41 +03:00
int local_port ;
2006-10-28 20:16:18 +03:00
int reuse_socket ;
2012-03-12 18:09:53 +03:00
int overrun_nonfatal ;
2004-11-09 02:27:16 +02:00
struct sockaddr_storage dest_addr ;
2008-05-11 13:53:21 +03:00
int dest_addr_len ;
2010-10-08 11:42:30 +03:00
int is_connected ;
2011-05-13 17:28:42 +03:00
/* Circular Buffer variables for use in UDP receive code */
int circular_buffer_size ;
2011-05-13 18:16:15 +03:00
AVFifoBuffer * fifo ;
2011-05-13 17:28:42 +03:00
int circular_buffer_error ;
2016-06-10 02:32:21 +02:00
int64_t bitrate ; /* number of bits to send per second */
int64_t burst_bits ;
2016-05-25 00:06:39 +02:00
int close_req ;
2012-05-08 20:36:06 +03:00
# if HAVE_PTHREAD_CANCEL
2011-05-13 17:28:42 +03:00
pthread_t circular_buffer_thread ;
2011-12-23 03:17:18 +03:00
pthread_mutex_t mutex ;
pthread_cond_t cond ;
2012-01-14 19:14:18 +03:00
int thread_started ;
2011-06-04 17:55:22 +03:00
# endif
2011-08-25 22:33:54 +03:00
uint8_t tmp [ UDP_MAX_PKT_SIZE + 4 ] ;
int remaining_in_dg ;
2015-03-13 18:00:12 +02:00
char * localaddr ;
2012-09-13 14:10:26 +03:00
int timeout ;
2013-12-13 13:49:34 +03:00
struct sockaddr_storage local_addr_storage ;
2015-03-13 18:00:12 +02:00
char * sources ;
char * block ;
2018-09-14 01:17:35 +02:00
IPSourceFilters filters ;
2001-07-22 17:18:56 +03:00
} UDPContext ;
2012-09-13 14:10:26 +03:00
# define OFFSET(x) offsetof(UDPContext, x)
# define D AV_OPT_FLAG_DECODING_PARAM
# define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options [ ] = {
2015-03-13 18:00:12 +02:00
{ " buffer_size " , " System data size (in bytes) " , OFFSET ( buffer_size ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
2016-06-10 02:32:21 +02:00
{ " bitrate " , " Bits to send per second " , OFFSET ( bitrate ) , AV_OPT_TYPE_INT64 , { . i64 = 0 } , 0 , INT64_MAX , . flags = E } ,
{ " burst_bits " , " Max length of bursts in bits (when using bitrate) " , OFFSET ( burst_bits ) , AV_OPT_TYPE_INT64 , { . i64 = 0 } , 0 , INT64_MAX , . flags = E } ,
2015-03-16 00:25:33 +02:00
{ " localport " , " Local port " , OFFSET ( local_port ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , D | E } ,
2015-03-13 18:00:12 +02:00
{ " local_port " , " Local port " , OFFSET ( local_port ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
{ " localaddr " , " Local address " , OFFSET ( localaddr ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = D | E } ,
2015-03-16 00:25:33 +02:00
{ " udplite_coverage " , " choose UDPLite head size which should be validated by checksum " , OFFSET ( udplite_coverage ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , D | E } ,
{ " pkt_size " , " Maximum UDP packet size " , OFFSET ( pkt_size ) , AV_OPT_TYPE_INT , { . i64 = 1472 } , - 1 , INT_MAX , . flags = D | E } ,
2015-11-21 23:05:07 +02:00
{ " reuse " , " explicitly allow reusing UDP sockets " , OFFSET ( reuse_socket ) , AV_OPT_TYPE_BOOL , { . i64 = - 1 } , - 1 , 1 , D | E } ,
{ " reuse_socket " , " explicitly allow reusing UDP sockets " , OFFSET ( reuse_socket ) , AV_OPT_TYPE_BOOL , { . i64 = - 1 } , - 1 , 1 , . flags = D | E } ,
{ " broadcast " , " explicitly allow or disallow broadcast destination " , OFFSET ( is_broadcast ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , E } ,
2015-03-16 00:25:33 +02:00
{ " ttl " , " Time to live (multicast only) " , OFFSET ( ttl ) , AV_OPT_TYPE_INT , { . i64 = 16 } , 0 , INT_MAX , E } ,
2015-11-21 23:05:07 +02:00
{ " connect " , " set if connect() should be called on socket " , OFFSET ( is_connected ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , . flags = D | E } ,
2015-03-16 00:25:33 +02:00
{ " fifo_size " , " set the UDP receiving circular buffer size, expressed as a number of packets with size of 188 bytes " , OFFSET ( circular_buffer_size ) , AV_OPT_TYPE_INT , { . i64 = 7 * 4096 } , 0 , INT_MAX , D } ,
2015-11-21 23:05:07 +02:00
{ " overrun_nonfatal " , " survive in case of UDP receiving circular buffer overrun " , OFFSET ( overrun_nonfatal ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , D } ,
2015-03-16 00:25:33 +02:00
{ " timeout " , " set raise error timeout (only in read mode) " , OFFSET ( timeout ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , INT_MAX , D } ,
2015-03-13 18:00:12 +02:00
{ " sources " , " Source list " , OFFSET ( sources ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = D | E } ,
{ " block " , " Block list " , OFFSET ( block ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = D | E } ,
{ NULL }
2012-09-13 14:10:26 +03:00
} ;
2015-03-13 18:00:12 +02:00
static const AVClass udp_class = {
. class_name = " udp " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
2012-09-13 14:10:26 +03:00
} ;
2014-11-05 11:59:44 +02:00
static const AVClass udplite_context_class = {
. class_name = " udplite " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2010-01-22 18:12:55 +02:00
static int udp_set_multicast_ttl ( int sockfd , int mcastTTL ,
struct sockaddr * addr )
{
2007-11-14 09:42:46 +02:00
# ifdef IP_MULTICAST_TTL
2004-11-09 02:27:16 +02:00
if ( addr - > sa_family = = AF_INET ) {
if ( setsockopt ( sockfd , IPPROTO_IP , IP_MULTICAST_TTL , & mcastTTL , sizeof ( mcastTTL ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(IP_MULTICAST_TTL) " ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
}
2007-11-14 09:42:46 +02:00
# endif
2010-01-21 17:42:05 +02:00
# if defined(IPPROTO_IPV6) && defined(IPV6_MULTICAST_HOPS)
2004-11-09 02:27:16 +02:00
if ( addr - > sa_family = = AF_INET6 ) {
if ( setsockopt ( sockfd , IPPROTO_IPV6 , IPV6_MULTICAST_HOPS , & mcastTTL , sizeof ( mcastTTL ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(IPV6_MULTICAST_HOPS) " ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
}
2007-11-14 09:42:46 +02:00
# endif
2004-11-09 02:27:16 +02:00
return 0 ;
}
2013-12-13 13:49:34 +03:00
static int udp_join_multicast_group ( int sockfd , struct sockaddr * addr , struct sockaddr * local_addr )
2010-01-22 18:12:55 +02:00
{
2007-11-14 09:42:46 +02:00
# ifdef IP_ADD_MEMBERSHIP
2004-11-09 02:27:16 +02:00
if ( addr - > sa_family = = AF_INET ) {
2007-11-14 09:42:46 +02:00
struct ip_mreq mreq ;
2004-11-09 02:27:16 +02:00
mreq . imr_multiaddr . s_addr = ( ( struct sockaddr_in * ) addr ) - > sin_addr . s_addr ;
2013-12-13 13:49:34 +03:00
if ( local_addr )
mreq . imr_interface = ( ( struct sockaddr_in * ) local_addr ) - > sin_addr ;
else
mreq . imr_interface . s_addr = INADDR_ANY ;
2004-11-09 02:27:16 +02:00
if ( setsockopt ( sockfd , IPPROTO_IP , IP_ADD_MEMBERSHIP , ( const void * ) & mreq , sizeof ( mreq ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(IP_ADD_MEMBERSHIP) " ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
}
2007-11-14 09:42:46 +02:00
# endif
2010-09-20 09:38:40 +03:00
# if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6)
2004-11-09 02:27:16 +02:00
if ( addr - > sa_family = = AF_INET6 ) {
2007-11-14 09:42:46 +02:00
struct ipv6_mreq mreq6 ;
2004-11-09 02:27:16 +02:00
memcpy ( & mreq6 . ipv6mr_multiaddr , & ( ( ( struct sockaddr_in6 * ) addr ) - > sin6_addr ) , sizeof ( struct in6_addr ) ) ;
2018-09-21 22:27:02 +02:00
//TODO: Interface index should be looked up from local_addr
2004-11-09 02:27:16 +02:00
mreq6 . ipv6mr_interface = 0 ;
if ( setsockopt ( sockfd , IPPROTO_IPV6 , IPV6_ADD_MEMBERSHIP , & mreq6 , sizeof ( mreq6 ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(IPV6_ADD_MEMBERSHIP) " ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
}
2007-11-14 09:42:46 +02:00
# endif
2004-11-09 02:27:16 +02:00
return 0 ;
}
2013-12-13 13:49:34 +03:00
static int udp_leave_multicast_group ( int sockfd , struct sockaddr * addr , struct sockaddr * local_addr )
2010-01-22 18:12:55 +02:00
{
2007-11-14 09:42:46 +02:00
# ifdef IP_DROP_MEMBERSHIP
2004-11-09 02:27:16 +02:00
if ( addr - > sa_family = = AF_INET ) {
2007-11-14 09:42:46 +02:00
struct ip_mreq mreq ;
2004-11-09 02:27:16 +02:00
mreq . imr_multiaddr . s_addr = ( ( struct sockaddr_in * ) addr ) - > sin_addr . s_addr ;
2013-12-13 13:49:34 +03:00
if ( local_addr )
mreq . imr_interface = ( ( struct sockaddr_in * ) local_addr ) - > sin_addr ;
else
mreq . imr_interface . s_addr = INADDR_ANY ;
2004-11-09 02:27:16 +02:00
if ( setsockopt ( sockfd , IPPROTO_IP , IP_DROP_MEMBERSHIP , ( const void * ) & mreq , sizeof ( mreq ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(IP_DROP_MEMBERSHIP) " ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
}
2007-11-14 09:42:46 +02:00
# endif
2010-09-20 09:38:40 +03:00
# if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6)
2004-11-09 02:27:16 +02:00
if ( addr - > sa_family = = AF_INET6 ) {
2007-11-14 09:42:46 +02:00
struct ipv6_mreq mreq6 ;
2004-11-09 02:27:16 +02:00
memcpy ( & mreq6 . ipv6mr_multiaddr , & ( ( ( struct sockaddr_in6 * ) addr ) - > sin6_addr ) , sizeof ( struct in6_addr ) ) ;
2018-09-21 22:27:02 +02:00
//TODO: Interface index should be looked up from local_addr
2004-11-09 02:27:16 +02:00
mreq6 . ipv6mr_interface = 0 ;
if ( setsockopt ( sockfd , IPPROTO_IPV6 , IPV6_DROP_MEMBERSHIP , & mreq6 , sizeof ( mreq6 ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(IPV6_DROP_MEMBERSHIP) " ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
}
2007-11-14 09:42:46 +02:00
# endif
2004-11-09 02:27:16 +02:00
return 0 ;
}
2015-11-24 02:09:33 +02:00
static int udp_set_multicast_sources ( URLContext * h ,
int sockfd , struct sockaddr * addr ,
2018-09-21 22:27:02 +02:00
int addr_len , struct sockaddr_storage * local_addr ,
struct sockaddr_storage * sources ,
2012-06-21 14:19:56 +03:00
int nb_sources , int include )
{
int i ;
2018-09-21 23:34:50 +02:00
if ( addr - > sa_family ! = AF_INET ) {
# if HAVE_STRUCT_GROUP_SOURCE_REQ && defined(MCAST_BLOCK_SOURCE)
/* For IPv4 prefer the old approach, as that alone works reliably on
* Windows and it also supports supplying the interface based on its
* address . */
int i ;
for ( i = 0 ; i < nb_sources ; i + + ) {
struct group_source_req mreqs ;
int level = addr - > sa_family = = AF_INET ? IPPROTO_IP : IPPROTO_IPV6 ;
2012-06-21 14:19:56 +03:00
2018-09-21 23:34:50 +02:00
//TODO: Interface index should be looked up from local_addr
mreqs . gsr_interface = 0 ;
memcpy ( & mreqs . gsr_group , addr , addr_len ) ;
memcpy ( & mreqs . gsr_source , & sources [ i ] , sizeof ( * sources ) ) ;
2012-06-21 14:19:56 +03:00
2018-09-21 23:34:50 +02:00
if ( setsockopt ( sockfd , level ,
include ? MCAST_JOIN_SOURCE_GROUP : MCAST_BLOCK_SOURCE ,
( const void * ) & mreqs , sizeof ( mreqs ) ) < 0 ) {
if ( include )
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(MCAST_JOIN_SOURCE_GROUP) " ) ;
else
ff_log_net_error ( NULL , AV_LOG_ERROR , " setsockopt(MCAST_BLOCK_SOURCE) " ) ;
return ff_neterrno ( ) ;
}
2012-06-21 14:19:56 +03:00
}
2018-09-21 23:34:50 +02:00
return 0 ;
# else
2019-09-30 08:34:17 +02:00
av_log ( h , AV_LOG_ERROR ,
2012-06-21 14:19:56 +03:00
" Setting multicast sources only supported for IPv4 \n " ) ;
return AVERROR ( EINVAL ) ;
2018-09-21 23:34:50 +02:00
# endif
2012-06-21 14:19:56 +03:00
}
2018-09-21 23:34:50 +02:00
# if HAVE_STRUCT_IP_MREQ_SOURCE && defined(IP_BLOCK_SOURCE)
2012-06-21 14:19:56 +03:00
for ( i = 0 ; i < nb_sources ; i + + ) {
struct ip_mreq_source mreqs ;
2018-09-14 01:17:35 +02:00
if ( sources [ i ] . ss_family ! = AF_INET ) {
2019-09-30 08:34:17 +02:00
av_log ( h , AV_LOG_ERROR , " Source/block address %d is of incorrect protocol family \n " , i + 1 ) ;
2012-06-21 14:19:56 +03:00
return AVERROR ( EINVAL ) ;
}
mreqs . imr_multiaddr . s_addr = ( ( struct sockaddr_in * ) addr ) - > sin_addr . s_addr ;
2018-09-21 22:27:02 +02:00
if ( local_addr )
mreqs . imr_interface = ( ( struct sockaddr_in * ) local_addr ) - > sin_addr ;
else
mreqs . imr_interface . s_addr = INADDR_ANY ;
2018-09-14 01:17:35 +02:00
mreqs . imr_sourceaddr . s_addr = ( ( struct sockaddr_in * ) & sources [ i ] ) - > sin_addr . s_addr ;
2012-06-21 14:19:56 +03:00
if ( setsockopt ( sockfd , IPPROTO_IP ,
include ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE ,
( const void * ) & mreqs , sizeof ( mreqs ) ) < 0 ) {
if ( include )
2019-09-30 08:34:17 +02:00
ff_log_net_error ( h , AV_LOG_ERROR , " setsockopt(IP_ADD_SOURCE_MEMBERSHIP) " ) ;
2012-06-21 14:19:56 +03:00
else
2019-09-30 08:34:17 +02:00
ff_log_net_error ( h , AV_LOG_ERROR , " setsockopt(IP_BLOCK_SOURCE) " ) ;
2012-06-21 14:19:56 +03:00
return ff_neterrno ( ) ;
}
}
# else
return AVERROR ( ENOSYS ) ;
# endif
return 0 ;
}
2015-11-24 02:09:33 +02:00
static int udp_set_url ( URLContext * h ,
struct sockaddr_storage * addr ,
2010-01-22 18:12:55 +02:00
const char * hostname , int port )
{
2004-11-09 02:27:16 +02:00
struct addrinfo * res0 ;
2007-11-14 10:16:51 +02:00
int addr_len ;
2018-09-14 01:17:35 +02:00
res0 = ff_ip_resolve_host ( h , hostname , port , SOCK_DGRAM , AF_UNSPEC , 0 ) ;
2014-08-16 01:43:27 +03:00
if ( ! res0 ) return AVERROR ( EIO ) ;
2007-11-14 10:16:51 +02:00
memcpy ( addr , res0 - > ai_addr , res0 - > ai_addrlen ) ;
addr_len = res0 - > ai_addrlen ;
2004-11-09 02:27:16 +02:00
freeaddrinfo ( res0 ) ;
2007-11-14 10:16:51 +02:00
return addr_len ;
2004-11-09 02:27:16 +02:00
}
2015-11-24 02:09:33 +02:00
static int udp_socket_create ( URLContext * h , struct sockaddr_storage * addr ,
2012-10-26 21:46:37 +03:00
socklen_t * addr_len , const char * localaddr )
2007-11-15 16:26:52 +02:00
{
2015-11-24 02:09:33 +02:00
UDPContext * s = h - > priv_data ;
2004-11-09 02:27:16 +02:00
int udp_fd = - 1 ;
2014-08-16 16:50:27 +03:00
struct addrinfo * res0 , * res ;
2007-10-31 09:27:38 +02:00
int family = AF_UNSPEC ;
2005-12-17 20:14:38 +02:00
2007-10-31 09:27:38 +02:00
if ( ( ( struct sockaddr * ) & s - > dest_addr ) - > sa_family )
family = ( ( struct sockaddr * ) & s - > dest_addr ) - > sa_family ;
2018-09-14 01:17:35 +02:00
res0 = ff_ip_resolve_host ( h , ( localaddr & & localaddr [ 0 ] ) ? localaddr : NULL ,
2015-11-24 02:09:33 +02:00
s - > local_port ,
2011-11-09 12:45:01 +03:00
SOCK_DGRAM , family , AI_PASSIVE ) ;
2014-08-16 01:43:27 +03:00
if ( ! res0 )
2007-10-31 09:08:12 +02:00
goto fail ;
for ( res = res0 ; res ; res = res - > ai_next ) {
2014-11-05 11:59:44 +02:00
if ( s - > udplite_coverage )
udp_fd = ff_socket ( res - > ai_family , SOCK_DGRAM , IPPROTO_UDPLITE ) ;
else
udp_fd = ff_socket ( res - > ai_family , SOCK_DGRAM , 0 ) ;
2012-06-19 17:59:57 +03:00
if ( udp_fd ! = - 1 ) break ;
2018-08-04 11:49:57 +02:00
ff_log_net_error ( NULL , AV_LOG_ERROR , " socket " ) ;
2007-10-31 09:08:12 +02:00
}
2005-07-22 00:10:23 +03:00
if ( udp_fd < 0 )
2004-11-09 02:27:16 +02:00
goto fail ;
2005-12-17 20:14:38 +02:00
2007-11-15 16:26:52 +02:00
memcpy ( addr , res - > ai_addr , res - > ai_addrlen ) ;
* addr_len = res - > ai_addrlen ;
2004-11-09 02:27:16 +02:00
2007-11-15 16:26:52 +02:00
freeaddrinfo ( res0 ) ;
2005-12-17 20:14:38 +02:00
2004-11-09 02:27:16 +02:00
return udp_fd ;
2005-12-17 20:14:38 +02:00
2004-11-09 02:27:16 +02:00
fail :
if ( udp_fd > = 0 )
closesocket ( udp_fd ) ;
2005-02-24 21:08:50 +02:00
if ( res0 )
freeaddrinfo ( res0 ) ;
2004-11-09 02:27:16 +02:00
return - 1 ;
}
2007-11-15 16:26:52 +02:00
static int udp_port ( struct sockaddr_storage * addr , int addr_len )
{
2008-02-11 05:30:42 +02:00
char sbuf [ sizeof ( int ) * 3 + 1 ] ;
2012-06-19 15:28:48 +03:00
int error ;
2007-11-15 16:26:52 +02:00
2012-06-19 15:28:48 +03:00
if ( ( error = getnameinfo ( ( struct sockaddr * ) addr , addr_len , NULL , 0 , sbuf , sizeof ( sbuf ) , NI_NUMERICSERV ) ) ! = 0 ) {
av_log ( NULL , AV_LOG_ERROR , " getnameinfo: %s \n " , gai_strerror ( error ) ) ;
2007-11-15 16:26:52 +02:00
return - 1 ;
}
return strtol ( sbuf , NULL , 10 ) ;
}
2004-11-09 02:27:16 +02:00
2002-07-24 20:45:41 +03:00
/**
* If no filename is given to av_open_input_file because you want to
* get the local port first , then you must call this function to set
* the remote server address .
*
* url syntax : udp : //host:port[?option=val...]
2008-04-15 14:23:07 +03:00
* option : ' ttl = n ' : set the ttl value ( for multicast only )
2002-07-24 20:45:41 +03:00
* ' localport = n ' : set the local port
2003-02-09 20:06:23 +02:00
* ' pkt_size = n ' : set max packet size
2006-10-28 20:16:18 +03:00
* ' reuse = 1 ' : enable reusing the socket
2012-03-12 18:09:53 +03:00
* ' overrun_nonfatal = 1 ' : survive in case of circular buffer overrun
2002-07-24 20:45:41 +03:00
*
2010-07-02 13:49:29 +03:00
* @ param h media file context
2002-07-24 20:45:41 +03:00
* @ param uri of the remote server
* @ return zero if no error .
*/
2011-03-08 11:35:52 +02:00
int ff_udp_set_remote_url ( URLContext * h , const char * uri )
2002-07-24 20:45:41 +03:00
{
UDPContext * s = h - > priv_data ;
2011-01-06 17:16:50 +02:00
char hostname [ 256 ] , buf [ 10 ] ;
2002-07-24 20:45:41 +03:00
int port ;
2011-01-06 17:16:50 +02:00
const char * p ;
2005-12-17 20:14:38 +02:00
2010-06-27 17:16:46 +03:00
av_url_split ( NULL , 0 , NULL , 0 , hostname , sizeof ( hostname ) , & port , NULL , 0 , uri ) ;
2001-07-22 17:18:56 +03:00
2002-07-24 20:45:41 +03:00
/* set the destination address */
2015-11-24 02:09:33 +02:00
s - > dest_addr_len = udp_set_url ( h , & s - > dest_addr , hostname , port ) ;
2007-11-14 10:16:51 +02:00
if ( s - > dest_addr_len < 0 ) {
2007-07-19 18:23:32 +03:00
return AVERROR ( EIO ) ;
2007-11-14 10:16:51 +02:00
}
2010-10-07 10:58:56 +03:00
s - > is_multicast = ff_is_multicast_address ( ( struct sockaddr * ) & s - > dest_addr ) ;
2011-01-06 17:16:50 +02:00
p = strchr ( uri , ' ? ' ) ;
if ( p ) {
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " connect " , p ) ) {
2011-01-06 17:16:50 +02:00
int was_connected = s - > is_connected ;
s - > is_connected = strtol ( buf , NULL , 10 ) ;
if ( s - > is_connected & & ! was_connected ) {
if ( connect ( s - > udp_fd , ( struct sockaddr * ) & s - > dest_addr ,
s - > dest_addr_len ) ) {
s - > is_connected = 0 ;
2018-08-04 11:49:57 +02:00
ff_log_net_error ( h , AV_LOG_ERROR , " connect " ) ;
2011-01-06 17:16:50 +02:00
return AVERROR ( EIO ) ;
}
}
}
}
2007-11-14 10:16:51 +02:00
2002-07-24 20:45:41 +03:00
return 0 ;
}
/**
2010-06-14 12:09:59 +03:00
* Return the local port used by the UDP connection
2010-07-02 13:49:29 +03:00
* @ param h media file context
2002-07-24 20:45:41 +03:00
* @ return the local port number
*/
2011-03-08 11:35:52 +02:00
int ff_udp_get_local_port ( URLContext * h )
2002-07-24 20:45:41 +03:00
{
UDPContext * s = h - > priv_data ;
return s - > local_port ;
}
/**
* Return the udp file handle for select ( ) usage to wait for several RTP
* streams at the same time .
* @ param h media file context
*/
2011-02-03 13:39:11 +02:00
static int udp_get_file_handle ( URLContext * h )
2002-07-24 20:45:41 +03:00
{
UDPContext * s = h - > priv_data ;
return s - > udp_fd ;
}
2012-05-08 20:36:06 +03:00
# if HAVE_PTHREAD_CANCEL
2016-03-08 22:27:45 +02:00
static void * circular_buffer_task_rx ( void * _URLContext )
2011-05-13 17:28:42 +03:00
{
URLContext * h = _URLContext ;
UDPContext * s = h - > priv_data ;
2012-03-15 15:03:38 +03:00
int old_cancelstate ;
2011-05-13 17:28:42 +03:00
2012-03-15 15:03:38 +03:00
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE , & old_cancelstate ) ;
2012-03-15 15:35:27 +03:00
pthread_mutex_lock ( & s - > mutex ) ;
2012-11-05 23:21:04 +03:00
if ( ff_socket_nonblock ( s - > udp_fd , 0 ) < 0 ) {
av_log ( h , AV_LOG_ERROR , " Failed to set blocking mode " ) ;
s - > circular_buffer_error = AVERROR ( EIO ) ;
goto end ;
}
2012-03-15 15:03:38 +03:00
while ( 1 ) {
2011-05-13 17:28:42 +03:00
int len ;
2018-09-20 01:23:58 +02:00
struct sockaddr_storage addr ;
socklen_t addr_len = sizeof ( addr ) ;
2011-05-13 17:28:42 +03:00
2012-03-15 15:35:27 +03:00
pthread_mutex_unlock ( & s - > mutex ) ;
2012-03-15 15:03:38 +03:00
/* Blocking operations are always cancellation points;
see " General Information " / " Thread Cancelation Overview "
in Single Unix . */
pthread_setcancelstate ( PTHREAD_CANCEL_ENABLE , & old_cancelstate ) ;
2018-09-20 01:23:58 +02:00
len = recvfrom ( s - > udp_fd , s - > tmp + 4 , sizeof ( s - > tmp ) - 4 , 0 , ( struct sockaddr * ) & addr , & addr_len ) ;
2012-03-15 15:03:38 +03:00
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE , & old_cancelstate ) ;
2012-03-15 15:35:27 +03:00
pthread_mutex_lock ( & s - > mutex ) ;
2011-05-13 17:28:42 +03:00
if ( len < 0 ) {
if ( ff_neterrno ( ) ! = AVERROR ( EAGAIN ) & & ff_neterrno ( ) ! = AVERROR ( EINTR ) ) {
2012-03-15 15:42:34 +03:00
s - > circular_buffer_error = ff_neterrno ( ) ;
2011-12-23 03:17:18 +03:00
goto end ;
2011-05-13 17:28:42 +03:00
}
2011-08-25 22:43:30 +03:00
continue ;
2011-05-13 17:28:42 +03:00
}
2018-09-20 01:23:58 +02:00
if ( ff_ip_check_source_lists ( & addr , & s - > filters ) )
continue ;
2011-08-25 22:33:54 +03:00
AV_WL32 ( s - > tmp , len ) ;
2012-03-15 15:11:06 +03:00
if ( av_fifo_space ( s - > fifo ) < len + 4 ) {
2012-03-12 18:09:53 +03:00
/* No Space left */
if ( s - > overrun_nonfatal ) {
av_log ( h , AV_LOG_WARNING , " Circular buffer overrun. "
" Surviving due to overrun_nonfatal option \n " ) ;
continue ;
} else {
av_log ( h , AV_LOG_ERROR , " Circular buffer overrun. "
" To avoid, increase fifo_size URL option. "
" To survive in such case, use overrun_nonfatal option \n " ) ;
s - > circular_buffer_error = AVERROR ( EIO ) ;
goto end ;
}
}
2011-08-25 22:33:54 +03:00
av_fifo_generic_write ( s - > fifo , s - > tmp , len + 4 , NULL ) ;
2011-12-23 03:17:18 +03:00
pthread_cond_signal ( & s - > cond ) ;
2011-05-13 17:28:42 +03:00
}
2011-12-23 03:17:18 +03:00
end :
pthread_cond_signal ( & s - > cond ) ;
pthread_mutex_unlock ( & s - > mutex ) ;
2011-05-13 17:28:42 +03:00
return NULL ;
}
2016-03-08 22:27:45 +02:00
static void * circular_buffer_task_tx ( void * _URLContext )
{
URLContext * h = _URLContext ;
UDPContext * s = h - > priv_data ;
2016-06-10 02:32:21 +02:00
int64_t target_timestamp = av_gettime_relative ( ) ;
int64_t start_timestamp = av_gettime_relative ( ) ;
int64_t sent_bits = 0 ;
int64_t burst_interval = s - > bitrate ? ( s - > burst_bits * 1000000 / s - > bitrate ) : 0 ;
int64_t max_delay = s - > bitrate ? ( ( int64_t ) h - > max_packet_size * 8 * 1000000 / s - > bitrate + 1 ) : 0 ;
2016-03-08 22:27:45 +02:00
2016-05-25 00:06:39 +02:00
pthread_mutex_lock ( & s - > mutex ) ;
if ( ff_socket_nonblock ( s - > udp_fd , 0 ) < 0 ) {
av_log ( h , AV_LOG_ERROR , " Failed to set blocking mode " ) ;
s - > circular_buffer_error = AVERROR ( EIO ) ;
goto end ;
}
2016-03-08 22:27:45 +02:00
for ( ; ; ) {
int len ;
2016-05-25 00:06:39 +02:00
const uint8_t * p ;
2016-03-08 22:27:45 +02:00
uint8_t tmp [ 4 ] ;
2016-06-09 23:56:22 +02:00
int64_t timestamp ;
2016-03-08 22:27:45 +02:00
len = av_fifo_size ( s - > fifo ) ;
while ( len < 4 ) {
2016-05-25 00:06:39 +02:00
if ( s - > close_req )
goto end ;
2016-03-08 22:27:45 +02:00
if ( pthread_cond_wait ( & s - > cond , & s - > mutex ) < 0 ) {
goto end ;
}
len = av_fifo_size ( s - > fifo ) ;
}
2016-05-25 00:06:39 +02:00
av_fifo_generic_read ( s - > fifo , tmp , 4 , NULL ) ;
2016-03-08 22:27:45 +02:00
len = AV_RL32 ( tmp ) ;
2016-05-25 00:06:39 +02:00
av_assert0 ( len > = 0 ) ;
av_assert0 ( len < = sizeof ( s - > tmp ) ) ;
av_fifo_generic_read ( s - > fifo , s - > tmp , len , NULL ) ;
pthread_mutex_unlock ( & s - > mutex ) ;
2016-06-10 02:32:21 +02:00
if ( s - > bitrate ) {
2016-06-09 23:56:22 +02:00
timestamp = av_gettime_relative ( ) ;
if ( timestamp < target_timestamp ) {
2016-06-10 02:32:21 +02:00
int64_t delay = target_timestamp - timestamp ;
if ( delay > max_delay ) {
delay = max_delay ;
start_timestamp = timestamp + delay ;
sent_bits = 0 ;
}
av_usleep ( delay ) ;
2016-06-09 23:56:22 +02:00
} else {
2016-06-10 02:32:21 +02:00
if ( timestamp - burst_interval > target_timestamp ) {
start_timestamp = timestamp - burst_interval ;
sent_bits = 0 ;
}
2016-06-09 23:56:22 +02:00
}
2016-06-10 02:32:21 +02:00
sent_bits + = len * 8 ;
target_timestamp = start_timestamp + sent_bits * 1000000 / s - > bitrate ;
2016-06-09 23:56:22 +02:00
}
2016-05-25 00:06:39 +02:00
p = s - > tmp ;
while ( len ) {
int ret ;
av_assert0 ( len > 0 ) ;
if ( ! s - > is_connected ) {
ret = sendto ( s - > udp_fd , p , len , 0 ,
( struct sockaddr * ) & s - > dest_addr ,
s - > dest_addr_len ) ;
} else
ret = send ( s - > udp_fd , p , len , 0 ) ;
if ( ret > = 0 ) {
len - = ret ;
p + = ret ;
} else {
ret = ff_neterrno ( ) ;
if ( ret ! = AVERROR ( EAGAIN ) & & ret ! = AVERROR ( EINTR ) ) {
2016-06-02 13:30:12 +02:00
pthread_mutex_lock ( & s - > mutex ) ;
2016-05-25 00:06:39 +02:00
s - > circular_buffer_error = ret ;
2016-06-02 13:30:12 +02:00
pthread_mutex_unlock ( & s - > mutex ) ;
2016-05-25 00:06:39 +02:00
return NULL ;
}
2016-03-08 22:27:45 +02:00
}
}
2016-05-25 00:06:39 +02:00
pthread_mutex_lock ( & s - > mutex ) ;
2016-03-08 22:27:45 +02:00
}
end :
pthread_mutex_unlock ( & s - > mutex ) ;
return NULL ;
}
2011-12-23 03:17:18 +03:00
# endif
2011-05-13 17:28:42 +03:00
2002-07-24 20:45:41 +03:00
/* put it in UDP context */
2001-07-22 17:18:56 +03:00
/* return non zero if error */
static int udp_open ( URLContext * h , const char * uri , int flags )
{
2011-11-09 12:45:01 +03:00
char hostname [ 1024 ] , localaddr [ 1024 ] = " " ;
2014-05-23 16:26:32 +03:00
int port , udp_fd = - 1 , tmp , bind_ret = - 1 , dscp = - 1 ;
2011-12-01 12:44:21 +03:00
UDPContext * s = h - > priv_data ;
2005-02-24 21:08:50 +02:00
int is_output ;
2002-07-24 20:45:41 +03:00
const char * p ;
char buf [ 256 ] ;
2007-11-15 16:26:52 +02:00
struct sockaddr_storage my_addr ;
2012-10-26 21:46:37 +03:00
socklen_t len ;
2001-07-22 17:18:56 +03:00
h - > is_streamed = 1 ;
2011-05-09 16:56:56 +03:00
is_output = ! ( flags & AVIO_FLAG_READ ) ;
2015-03-13 18:00:12 +02:00
if ( s - > buffer_size < 0 )
2020-01-14 20:18:01 +02:00
s - > buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_RX_BUF_SIZE ;
2011-05-13 17:28:42 +03:00
2015-03-13 18:00:12 +02:00
if ( s - > sources ) {
2018-09-14 01:17:35 +02:00
if ( ff_ip_parse_sources ( h , s - > sources , & s - > filters ) < 0 )
2015-03-13 18:00:12 +02:00
goto fail ;
}
if ( s - > block ) {
2018-09-14 01:17:35 +02:00
if ( ff_ip_parse_blocks ( h , s - > block , & s - > filters ) < 0 )
2015-03-13 18:00:12 +02:00
goto fail ;
}
2015-03-29 15:19:35 +02:00
if ( s - > pkt_size > 0 )
2015-03-13 18:00:12 +02:00
h - > max_packet_size = s - > pkt_size ;
2008-11-12 23:40:53 +02:00
2002-07-24 20:45:41 +03:00
p = strchr ( uri , ' ? ' ) ;
if ( p ) {
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " reuse " , p ) ) {
2011-06-17 10:08:23 +03:00
char * endptr = NULL ;
2011-02-07 23:14:43 +02:00
s - > reuse_socket = strtol ( buf , & endptr , 10 ) ;
/* assume if no digits were found it is a request to enable it */
if ( buf = = endptr )
s - > reuse_socket = 1 ;
}
2012-03-12 18:09:53 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " overrun_nonfatal " , p ) ) {
char * endptr = NULL ;
s - > overrun_nonfatal = strtol ( buf , & endptr , 10 ) ;
/* assume if no digits were found it is a request to enable it */
if ( buf = = endptr )
s - > overrun_nonfatal = 1 ;
2012-07-18 17:33:24 +03:00
if ( ! HAVE_PTHREAD_CANCEL )
av_log ( h , AV_LOG_WARNING ,
" 'overrun_nonfatal' option was set but it is not supported "
" on this build (pthread support is required) \n " ) ;
2012-03-12 18:09:53 +03:00
}
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " ttl " , p ) ) {
2002-07-24 20:45:41 +03:00
s - > ttl = strtol ( buf , NULL , 10 ) ;
}
2014-11-05 11:59:44 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " udplite_coverage " , p ) ) {
s - > udplite_coverage = strtol ( buf , NULL , 10 ) ;
}
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " localport " , p ) ) {
2002-07-24 20:45:41 +03:00
s - > local_port = strtol ( buf , NULL , 10 ) ;
}
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " pkt_size " , p ) ) {
2015-03-16 00:25:33 +02:00
s - > pkt_size = strtol ( buf , NULL , 10 ) ;
2003-02-09 20:06:23 +02:00
}
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " buffer_size " , p ) ) {
2008-11-12 23:40:53 +02:00
s - > buffer_size = strtol ( buf , NULL , 10 ) ;
}
2011-02-16 10:52:38 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " connect " , p ) ) {
2010-10-08 11:42:30 +03:00
s - > is_connected = strtol ( buf , NULL , 10 ) ;
}
2014-05-23 16:26:32 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " dscp " , p ) ) {
dscp = strtol ( buf , NULL , 10 ) ;
}
2011-07-25 16:50:13 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " fifo_size " , p ) ) {
2012-09-13 14:10:26 +03:00
s - > circular_buffer_size = strtol ( buf , NULL , 10 ) ;
2012-07-18 17:33:24 +03:00
if ( ! HAVE_PTHREAD_CANCEL )
av_log ( h , AV_LOG_WARNING ,
" 'circular_buffer_size' option was set but it is not supported "
" on this build (pthread support is required) \n " ) ;
2011-05-13 17:28:42 +03:00
}
2016-06-10 02:32:21 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " bitrate " , p ) ) {
s - > bitrate = strtoll ( buf , NULL , 10 ) ;
2016-03-08 22:27:45 +02:00
if ( ! HAVE_PTHREAD_CANCEL )
av_log ( h , AV_LOG_WARNING ,
2016-06-10 02:32:21 +02:00
" 'bitrate' option was set but it is not supported "
2016-03-08 22:27:45 +02:00
" on this build (pthread support is required) \n " ) ;
}
2016-06-10 02:32:21 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " burst_bits " , p ) ) {
s - > burst_bits = strtoll ( buf , NULL , 10 ) ;
}
2011-11-09 12:45:01 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " localaddr " , p ) ) {
av_strlcpy ( localaddr , buf , sizeof ( localaddr ) ) ;
}
2013-07-26 22:05:21 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " sources " , p ) ) {
2018-09-14 01:17:35 +02:00
if ( ff_ip_parse_sources ( h , buf , & s - > filters ) < 0 )
2013-07-26 22:05:21 +03:00
goto fail ;
}
if ( av_find_info_tag ( buf , sizeof ( buf ) , " block " , p ) ) {
2018-09-14 01:17:35 +02:00
if ( ff_ip_parse_blocks ( h , buf , & s - > filters ) < 0 )
2013-07-26 22:05:21 +03:00
goto fail ;
2012-06-21 14:19:56 +03:00
}
2012-08-27 16:31:09 +03:00
if ( ! is_output & & av_find_info_tag ( buf , sizeof ( buf ) , " timeout " , p ) )
2012-09-13 14:10:26 +03:00
s - > timeout = strtol ( buf , NULL , 10 ) ;
2014-04-09 06:21:52 +03:00
if ( is_output & & av_find_info_tag ( buf , sizeof ( buf ) , " broadcast " , p ) )
s - > is_broadcast = strtol ( buf , NULL , 10 ) ;
2001-07-22 17:18:56 +03:00
}
2012-09-13 14:10:26 +03:00
/* handling needed to support options picking from both AVOption and URL */
s - > circular_buffer_size * = 188 ;
2013-06-29 00:58:13 +03:00
if ( flags & AVIO_FLAG_WRITE ) {
2015-03-16 00:25:33 +02:00
h - > max_packet_size = s - > pkt_size ;
2013-06-29 00:58:13 +03:00
} else {
h - > max_packet_size = UDP_MAX_PKT_SIZE ;
}
2012-09-13 14:10:26 +03:00
h - > rw_timeout = s - > timeout ;
2002-07-24 20:45:41 +03:00
/* fill the dest addr */
2010-06-27 17:16:46 +03:00
av_url_split ( NULL , 0 , NULL , 0 , hostname , sizeof ( hostname ) , & port , NULL , 0 , uri ) ;
2005-12-17 20:14:38 +02:00
2010-06-27 17:16:46 +03:00
/* XXX: fix av_url_split */
2002-07-24 20:45:41 +03:00
if ( hostname [ 0 ] = = ' \0 ' | | hostname [ 0 ] = = ' ? ' ) {
/* only accepts null hostname if input */
2011-05-09 16:56:56 +03:00
if ( ! ( flags & AVIO_FLAG_READ ) )
2002-07-24 20:45:41 +03:00
goto fail ;
} else {
2011-03-08 11:35:52 +02:00
if ( ff_udp_set_remote_url ( h , uri ) < 0 )
2010-07-28 19:27:16 +03:00
goto fail ;
2002-07-24 20:45:41 +03:00
}
2001-07-22 17:18:56 +03:00
2015-03-16 00:25:33 +02:00
if ( ( s - > is_multicast | | s - > local_port < = 0 ) & & ( h - > flags & AVIO_FLAG_READ ) )
2007-11-15 15:34:56 +02:00
s - > local_port = port ;
2015-03-13 18:00:12 +02:00
if ( localaddr [ 0 ] )
2015-11-24 02:09:33 +02:00
udp_fd = udp_socket_create ( h , & my_addr , & len , localaddr ) ;
2015-03-13 18:00:12 +02:00
else
2015-11-24 02:09:33 +02:00
udp_fd = udp_socket_create ( h , & my_addr , & len , s - > localaddr ) ;
2002-07-24 20:45:41 +03:00
if ( udp_fd < 0 )
goto fail ;
2013-12-13 13:49:34 +03:00
s - > local_addr_storage = my_addr ; //store for future multicast join
2011-02-07 23:14:43 +02:00
/* Follow the requested reuse option, unless it's multicast in which
2011-10-05 15:12:42 +03:00
* case enable reuse unless explicitly disabled .
2011-02-07 23:14:43 +02:00
*/
2015-03-13 18:00:12 +02:00
if ( s - > reuse_socket > 0 | | ( s - > is_multicast & & s - > reuse_socket < 0 ) ) {
2011-02-07 23:14:43 +02:00
s - > reuse_socket = 1 ;
2006-10-28 20:16:18 +03:00
if ( setsockopt ( udp_fd , SOL_SOCKET , SO_REUSEADDR , & ( s - > reuse_socket ) , sizeof ( s - > reuse_socket ) ) ! = 0 )
goto fail ;
2011-02-07 23:14:43 +02:00
}
2006-10-28 20:16:18 +03:00
2014-04-09 06:21:52 +03:00
if ( s - > is_broadcast ) {
2014-06-07 15:43:07 +03:00
# ifdef SO_BROADCAST
2014-04-09 06:21:52 +03:00
if ( setsockopt ( udp_fd , SOL_SOCKET , SO_BROADCAST , & ( s - > is_broadcast ) , sizeof ( s - > is_broadcast ) ) ! = 0 )
2014-06-07 15:43:07 +03:00
# endif
2014-04-09 06:21:52 +03:00
goto fail ;
}
2014-11-05 11:59:44 +02:00
/* Set the checksum coverage for UDP-Lite (RFC 3828) for sending and receiving.
* The receiver coverage has to be less than or equal to the sender coverage .
* Otherwise , the receiver will drop all packets .
*/
if ( s - > udplite_coverage ) {
if ( setsockopt ( udp_fd , IPPROTO_UDPLITE , UDPLITE_SEND_CSCOV , & ( s - > udplite_coverage ) , sizeof ( s - > udplite_coverage ) ) ! = 0 )
av_log ( h , AV_LOG_WARNING , " socket option UDPLITE_SEND_CSCOV not available " ) ;
if ( setsockopt ( udp_fd , IPPROTO_UDPLITE , UDPLITE_RECV_CSCOV , & ( s - > udplite_coverage ) , sizeof ( s - > udplite_coverage ) ) ! = 0 )
av_log ( h , AV_LOG_WARNING , " socket option UDPLITE_RECV_CSCOV not available " ) ;
}
2014-05-23 16:26:32 +03:00
if ( dscp > = 0 ) {
dscp < < = 2 ;
if ( setsockopt ( udp_fd , IPPROTO_IP , IP_TOS , & dscp , sizeof ( dscp ) ) ! = 0 )
goto fail ;
}
2012-03-08 16:15:11 +03:00
/* If multicast, try binding the multicast address first, to avoid
* receiving UDP packets from other sources aimed at the same UDP
2012-03-08 16:17:15 +03:00
* port . This fails on windows . This makes sending to the same address
* using sendto ( ) fail , so only do it if we ' re opened in read - only mode . */
2019-11-26 09:02:06 +02:00
if ( s - > is_multicast & & ( h - > flags & AVIO_FLAG_READ ) ) {
2008-12-07 19:29:09 +02:00
bind_ret = bind ( udp_fd , ( struct sockaddr * ) & s - > dest_addr , len ) ;
}
/* bind to the local address if not multicast or if the multicast
* bind failed */
2012-03-08 01:38:02 +03:00
/* the bind is needed to give a port to the socket now */
2012-03-07 22:40:29 +03:00
if ( bind_ret < 0 & & bind ( udp_fd , ( struct sockaddr * ) & my_addr , len ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( h , AV_LOG_ERROR , " bind failed " ) ;
2001-07-22 17:18:56 +03:00
goto fail ;
2012-03-07 22:40:29 +03:00
}
2001-07-22 17:18:56 +03:00
2007-11-15 15:41:51 +02:00
len = sizeof ( my_addr ) ;
getsockname ( udp_fd , ( struct sockaddr * ) & my_addr , & len ) ;
2007-11-15 16:26:52 +02:00
s - > local_port = udp_port ( & my_addr , len ) ;
2004-11-09 02:27:16 +02:00
if ( s - > is_multicast ) {
2012-03-08 16:31:45 +03:00
if ( h - > flags & AVIO_FLAG_WRITE ) {
2007-11-14 09:42:46 +02:00
/* output */
2007-11-14 09:43:51 +02:00
if ( udp_set_multicast_ttl ( udp_fd , s - > ttl , ( struct sockaddr * ) & s - > dest_addr ) < 0 )
2004-11-09 02:27:16 +02:00
goto fail ;
2012-03-08 16:31:45 +03:00
}
if ( h - > flags & AVIO_FLAG_READ ) {
2007-11-14 09:42:46 +02:00
/* input */
2018-09-14 01:17:35 +02:00
if ( s - > filters . nb_include_addrs ) {
2015-11-24 02:09:33 +02:00
if ( udp_set_multicast_sources ( h , udp_fd ,
( struct sockaddr * ) & s - > dest_addr ,
2018-09-21 22:27:02 +02:00
s - > dest_addr_len , & s - > local_addr_storage ,
2018-09-14 01:17:35 +02:00
s - > filters . include_addrs ,
s - > filters . nb_include_addrs , 1 ) < 0 )
2013-07-26 22:05:21 +03:00
goto fail ;
} else {
2013-12-13 13:49:34 +03:00
if ( udp_join_multicast_group ( udp_fd , ( struct sockaddr * ) & s - > dest_addr , ( struct sockaddr * ) & s - > local_addr_storage ) < 0 )
2012-06-21 14:19:56 +03:00
goto fail ;
2013-07-26 22:05:21 +03:00
}
2018-09-14 01:17:35 +02:00
if ( s - > filters . nb_exclude_addrs ) {
2015-11-24 02:09:33 +02:00
if ( udp_set_multicast_sources ( h , udp_fd ,
( struct sockaddr * ) & s - > dest_addr ,
2018-09-21 22:27:02 +02:00
s - > dest_addr_len , & s - > local_addr_storage ,
2018-09-14 01:17:35 +02:00
s - > filters . exclude_addrs ,
s - > filters . nb_exclude_addrs , 0 ) < 0 )
2012-06-21 14:19:56 +03:00
goto fail ;
}
2004-11-09 02:27:16 +02:00
}
}
2001-07-22 17:18:56 +03:00
2002-07-24 20:45:41 +03:00
if ( is_output ) {
/* limit the tx buf size to limit latency */
2008-11-12 23:40:53 +02:00
tmp = s - > buffer_size ;
2002-07-24 20:45:41 +03:00
if ( setsockopt ( udp_fd , SOL_SOCKET , SO_SNDBUF , & tmp , sizeof ( tmp ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( h , AV_LOG_ERROR , " setsockopt(SO_SNDBUF) " ) ;
2002-07-24 20:45:41 +03:00
goto fail ;
}
2007-08-17 01:12:03 +03:00
} else {
2020-07-11 13:09:36 +02:00
/* set udp recv buffer size to the requested value (default UDP_RX_BUF_SIZE) */
2008-11-12 23:40:53 +02:00
tmp = s - > buffer_size ;
if ( setsockopt ( udp_fd , SOL_SOCKET , SO_RCVBUF , & tmp , sizeof ( tmp ) ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( h , AV_LOG_WARNING , " setsockopt(SO_RECVBUF) " ) ;
2008-11-12 23:40:53 +02:00
}
2014-01-06 22:57:52 +03:00
len = sizeof ( tmp ) ;
if ( getsockopt ( udp_fd , SOL_SOCKET , SO_RCVBUF , & tmp , & len ) < 0 ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( h , AV_LOG_WARNING , " getsockopt(SO_RCVBUF) " ) ;
2014-03-22 02:37:42 +03:00
} else {
2014-01-06 22:57:52 +03:00
av_log ( h , AV_LOG_DEBUG , " end receive buffer size reported is %d \n " , tmp ) ;
2014-03-22 02:37:42 +03:00
if ( tmp < s - > buffer_size )
2020-01-14 19:51:02 +02:00
av_log ( h , AV_LOG_WARNING , " attempted to set receive buffer to size %d but it only ended up set as %d \n " , s - > buffer_size , tmp ) ;
2014-03-22 02:37:42 +03:00
}
2014-01-06 22:57:52 +03:00
2008-11-17 09:50:25 +02:00
/* make the socket non-blocking */
ff_socket_nonblock ( udp_fd , 1 ) ;
2002-07-24 20:45:41 +03:00
}
2010-10-08 11:42:30 +03:00
if ( s - > is_connected ) {
if ( connect ( udp_fd , ( struct sockaddr * ) & s - > dest_addr , s - > dest_addr_len ) ) {
2018-08-04 11:49:57 +02:00
ff_log_net_error ( h , AV_LOG_ERROR , " connect " ) ;
2010-10-08 11:42:30 +03:00
goto fail ;
}
}
2002-07-24 20:45:41 +03:00
s - > udp_fd = udp_fd ;
2011-05-13 17:28:42 +03:00
2012-05-08 20:36:06 +03:00
# if HAVE_PTHREAD_CANCEL
2016-03-08 22:27:45 +02:00
/*
Create thread in case of :
1. Input and circular_buffer_size is set
2016-06-10 02:32:21 +02:00
2. Output and bitrate and circular_buffer_size is set
2016-03-08 22:27:45 +02:00
*/
2016-06-10 02:32:21 +02:00
if ( is_output & & s - > bitrate & & ! s - > circular_buffer_size ) {
2016-03-08 22:27:45 +02:00
/* Warn user in case of 'circular_buffer_size' is not set */
2016-06-10 02:32:21 +02:00
av_log ( h , AV_LOG_WARNING , " 'bitrate' option was set but 'circular_buffer_size' is not, but required \n " ) ;
2016-03-08 22:27:45 +02:00
}
2016-06-10 02:32:21 +02:00
if ( ( ! is_output & & s - > circular_buffer_size ) | | ( is_output & & s - > bitrate & & s - > circular_buffer_size ) ) {
2012-01-14 19:14:18 +03:00
int ret ;
2011-05-13 17:28:42 +03:00
/* start the task going */
2011-05-13 18:16:15 +03:00
s - > fifo = av_fifo_alloc ( s - > circular_buffer_size ) ;
2012-01-14 19:14:18 +03:00
ret = pthread_mutex_init ( & s - > mutex , NULL ) ;
if ( ret ! = 0 ) {
av_log ( h , AV_LOG_ERROR , " pthread_mutex_init failed : %s \n " , strerror ( ret ) ) ;
2011-05-13 17:28:42 +03:00
goto fail ;
}
2012-01-14 19:14:18 +03:00
ret = pthread_cond_init ( & s - > cond , NULL ) ;
if ( ret ! = 0 ) {
av_log ( h , AV_LOG_ERROR , " pthread_cond_init failed : %s \n " , strerror ( ret ) ) ;
goto cond_fail ;
}
2016-03-08 22:27:45 +02:00
ret = pthread_create ( & s - > circular_buffer_thread , NULL , is_output ? circular_buffer_task_tx : circular_buffer_task_rx , h ) ;
2012-01-14 19:14:18 +03:00
if ( ret ! = 0 ) {
av_log ( h , AV_LOG_ERROR , " pthread_create failed : %s \n " , strerror ( ret ) ) ;
goto thread_fail ;
}
s - > thread_started = 1 ;
2011-05-13 17:28:42 +03:00
}
2011-06-04 17:55:22 +03:00
# endif
2011-05-13 17:28:42 +03:00
2001-07-22 17:18:56 +03:00
return 0 ;
2012-05-08 20:36:06 +03:00
# if HAVE_PTHREAD_CANCEL
2012-01-14 19:14:18 +03:00
thread_fail :
pthread_cond_destroy ( & s - > cond ) ;
cond_fail :
pthread_mutex_destroy ( & s - > mutex ) ;
# endif
2001-07-22 17:18:56 +03:00
fail :
2002-07-24 20:45:41 +03:00
if ( udp_fd > = 0 )
2002-11-02 12:35:07 +02:00
closesocket ( udp_fd ) ;
2014-05-06 22:48:14 +03:00
av_fifo_freep ( & s - > fifo ) ;
2018-09-14 01:17:35 +02:00
ff_ip_reset_filters ( & s - > filters ) ;
2007-07-19 18:23:32 +03:00
return AVERROR ( EIO ) ;
2001-07-22 17:18:56 +03:00
}
2014-11-05 11:59:44 +02:00
static int udplite_open ( URLContext * h , const char * uri , int flags )
{
UDPContext * s = h - > priv_data ;
// set default checksum coverage
s - > udplite_coverage = UDP_HEADER_SIZE ;
return udp_open ( h , uri , flags ) ;
}
2003-02-11 18:35:48 +02:00
static int udp_read ( URLContext * h , uint8_t * buf , int size )
2001-07-22 17:18:56 +03:00
{
UDPContext * s = h - > priv_data ;
2008-11-14 21:33:22 +02:00
int ret ;
2018-09-20 01:23:58 +02:00
struct sockaddr_storage addr ;
socklen_t addr_len = sizeof ( addr ) ;
2014-08-30 13:42:16 +03:00
# if HAVE_PTHREAD_CANCEL
2012-03-15 14:19:37 +03:00
int avail , nonblock = h - > flags & AVIO_FLAG_NONBLOCK ;
2011-05-13 17:28:42 +03:00
2011-05-14 14:34:08 +03:00
if ( s - > fifo ) {
2011-12-23 03:17:18 +03:00
pthread_mutex_lock ( & s - > mutex ) ;
2011-05-13 17:28:42 +03:00
do {
2011-05-13 18:16:15 +03:00
avail = av_fifo_size ( s - > fifo ) ;
2011-05-13 17:28:42 +03:00
if ( avail ) { // >=size) {
2011-08-25 22:33:54 +03:00
uint8_t tmp [ 4 ] ;
av_fifo_generic_read ( s - > fifo , tmp , 4 , NULL ) ;
avail = AV_RL32 ( tmp ) ;
if ( avail > size ) {
av_log ( h , AV_LOG_WARNING , " Part of datagram lost due to insufficient buffer size \n " ) ;
avail = size ;
}
2011-05-13 17:28:42 +03:00
2011-08-25 22:33:54 +03:00
av_fifo_generic_read ( s - > fifo , buf , avail , NULL ) ;
2011-11-09 05:51:08 +03:00
av_fifo_drain ( s - > fifo , AV_RL32 ( tmp ) - avail ) ;
2012-03-15 15:35:27 +03:00
pthread_mutex_unlock ( & s - > mutex ) ;
2011-08-25 22:33:54 +03:00
return avail ;
2011-12-23 03:41:15 +03:00
} else if ( s - > circular_buffer_error ) {
2012-03-15 15:35:27 +03:00
int err = s - > circular_buffer_error ;
2011-12-23 03:17:18 +03:00
pthread_mutex_unlock ( & s - > mutex ) ;
2012-03-15 15:35:27 +03:00
return err ;
2012-03-15 14:19:37 +03:00
} else if ( nonblock ) {
2011-12-23 03:51:47 +03:00
pthread_mutex_unlock ( & s - > mutex ) ;
return AVERROR ( EAGAIN ) ;
2011-05-13 17:28:42 +03:00
}
else {
2012-03-15 14:19:37 +03:00
/* FIXME: using the monotonic clock would be better,
but it does not exist on all supported platforms . */
int64_t t = av_gettime ( ) + 100000 ;
struct timespec tv = { . tv_sec = t / 1000000 ,
. tv_nsec = ( t % 1000000 ) * 1000 } ;
2020-01-14 19:53:45 +02:00
int err = pthread_cond_timedwait ( & s - > cond , & s - > mutex , & tv ) ;
if ( err ) {
2012-10-21 00:31:32 +03:00
pthread_mutex_unlock ( & s - > mutex ) ;
2020-01-14 19:53:45 +02:00
return AVERROR ( err = = ETIMEDOUT ? EAGAIN : err ) ;
2012-10-21 00:31:32 +03:00
}
2012-03-15 14:19:37 +03:00
nonblock = 1 ;
2011-05-13 17:28:42 +03:00
}
} while ( 1 ) ;
}
2011-12-23 03:17:18 +03:00
# endif
2002-07-24 20:45:41 +03:00
2011-04-04 21:11:19 +03:00
if ( ! ( h - > flags & AVIO_FLAG_NONBLOCK ) ) {
2011-04-04 19:17:12 +03:00
ret = ff_network_wait_fd ( s - > udp_fd , 0 ) ;
if ( ret < 0 )
return ret ;
2002-07-24 20:45:41 +03:00
}
2018-09-20 01:23:58 +02:00
ret = recvfrom ( s - > udp_fd , buf , size , 0 , ( struct sockaddr * ) & addr , & addr_len ) ;
if ( ret < 0 )
return ff_neterrno ( ) ;
if ( ff_ip_check_source_lists ( & addr , & s - > filters ) )
return AVERROR ( EINTR ) ;
return ret ;
2001-07-22 17:18:56 +03:00
}
2010-06-01 10:46:23 +03:00
static int udp_write ( URLContext * h , const uint8_t * buf , int size )
2001-07-22 17:18:56 +03:00
{
UDPContext * s = h - > priv_data ;
2002-07-24 20:45:41 +03:00
int ret ;
2016-03-08 22:27:45 +02:00
# if HAVE_PTHREAD_CANCEL
if ( s - > fifo ) {
uint8_t tmp [ 4 ] ;
pthread_mutex_lock ( & s - > mutex ) ;
/*
Return error if last tx failed .
Here we can ' t know on which packet error was , but it needs to know that error exists .
*/
if ( s - > circular_buffer_error < 0 ) {
int err = s - > circular_buffer_error ;
pthread_mutex_unlock ( & s - > mutex ) ;
return err ;
}
if ( av_fifo_space ( s - > fifo ) < size + 4 ) {
/* What about a partial packet tx ? */
pthread_mutex_unlock ( & s - > mutex ) ;
return AVERROR ( ENOMEM ) ;
}
AV_WL32 ( tmp , size ) ;
av_fifo_generic_write ( s - > fifo , tmp , 4 , NULL ) ; /* size of packet */
av_fifo_generic_write ( s - > fifo , ( uint8_t * ) buf , size , NULL ) ; /* the data */
pthread_cond_signal ( & s - > cond ) ;
pthread_mutex_unlock ( & s - > mutex ) ;
return size ;
}
# endif
2011-04-04 21:11:19 +03:00
if ( ! ( h - > flags & AVIO_FLAG_NONBLOCK ) ) {
2011-04-04 19:17:12 +03:00
ret = ff_network_wait_fd ( s - > udp_fd , 1 ) ;
if ( ret < 0 )
return ret ;
2001-07-22 17:18:56 +03:00
}
2011-04-04 19:17:12 +03:00
if ( ! s - > is_connected ) {
ret = sendto ( s - > udp_fd , buf , size , 0 ,
( struct sockaddr * ) & s - > dest_addr ,
s - > dest_addr_len ) ;
} else
ret = send ( s - > udp_fd , buf , size , 0 ) ;
return ret < 0 ? ff_neterrno ( ) : ret ;
2002-07-24 20:45:41 +03:00
}
static int udp_close ( URLContext * h )
{
UDPContext * s = h - > priv_data ;
2016-05-25 00:06:39 +02:00
# if HAVE_PTHREAD_CANCEL
// Request close once writing is finished
if ( s - > thread_started & & ! ( h - > flags & AVIO_FLAG_READ ) ) {
pthread_mutex_lock ( & s - > mutex ) ;
s - > close_req = 1 ;
pthread_cond_signal ( & s - > cond ) ;
pthread_mutex_unlock ( & s - > mutex ) ;
}
# endif
2011-05-09 16:56:56 +03:00
if ( s - > is_multicast & & ( h - > flags & AVIO_FLAG_READ ) )
2013-12-13 13:49:34 +03:00
udp_leave_multicast_group ( s - > udp_fd , ( struct sockaddr * ) & s - > dest_addr , ( struct sockaddr * ) & s - > local_addr_storage ) ;
2012-05-08 20:36:06 +03:00
# if HAVE_PTHREAD_CANCEL
2012-01-14 19:14:18 +03:00
if ( s - > thread_started ) {
2014-08-30 13:42:16 +03:00
int ret ;
2016-05-25 00:06:39 +02:00
// Cancel only read, as write has been signaled as success to the user
2020-01-26 23:13:50 +02:00
if ( h - > flags & AVIO_FLAG_READ ) {
# ifdef _WIN32
/* recvfrom() is not a cancellation point for win32, so we shutdown
* the socket and abort pending IO , subsequent recvfrom ( ) calls
* will fail with WSAESHUTDOWN causing the thread to exit . */
shutdown ( s - > udp_fd , SD_RECEIVE ) ;
CancelIoEx ( ( HANDLE ) ( SOCKET ) s - > udp_fd , NULL ) ;
# else
2016-05-25 00:06:39 +02:00
pthread_cancel ( s - > circular_buffer_thread ) ;
2020-01-26 23:13:50 +02:00
# endif
}
2012-01-14 19:14:18 +03:00
ret = pthread_join ( s - > circular_buffer_thread , NULL ) ;
if ( ret ! = 0 )
av_log ( h , AV_LOG_ERROR , " pthread_join(): %s \n " , strerror ( ret ) ) ;
2012-08-06 10:56:50 +03:00
pthread_mutex_destroy ( & s - > mutex ) ;
pthread_cond_destroy ( & s - > cond ) ;
2012-01-14 19:14:18 +03:00
}
2011-12-23 03:17:18 +03:00
# endif
2016-05-25 00:59:18 +02:00
closesocket ( s - > udp_fd ) ;
2014-05-06 22:48:14 +03:00
av_fifo_freep ( & s - > fifo ) ;
2018-09-14 01:17:35 +02:00
ff_ip_reset_filters ( & s - > filters ) ;
2002-07-24 20:45:41 +03:00
return 0 ;
2001-07-22 17:18:56 +03:00
}
2016-02-19 11:39:29 +02:00
const URLProtocol ff_udp_protocol = {
2011-04-08 08:41:47 +03:00
. name = " udp " ,
. url_open = udp_open ,
. url_read = udp_read ,
. url_write = udp_write ,
. url_close = udp_close ,
2009-03-03 19:04:51 +02:00
. url_get_file_handle = udp_get_file_handle ,
2011-12-01 12:44:21 +03:00
. priv_data_size = sizeof ( UDPContext ) ,
2015-03-13 18:00:12 +02:00
. priv_data_class = & udp_class ,
2011-12-30 12:38:05 +03:00
. flags = URL_PROTOCOL_FLAG_NETWORK ,
2001-07-22 17:18:56 +03:00
} ;
2014-11-05 11:59:44 +02:00
2016-02-29 18:50:39 +02:00
const URLProtocol ff_udplite_protocol = {
2014-11-05 11:59:44 +02:00
. name = " udplite " ,
. url_open = udplite_open ,
. url_read = udp_read ,
. url_write = udp_write ,
. url_close = udp_close ,
. url_get_file_handle = udp_get_file_handle ,
. priv_data_size = sizeof ( UDPContext ) ,
. priv_data_class = & udplite_context_class ,
. flags = URL_PROTOCOL_FLAG_NETWORK ,
} ;