2002-07-04 10:42:57 +00:00
/*
* RTP network protocol
2009-01-19 15:46:40 +00:00
* Copyright ( c ) 2002 Fabrice Bellard
2002-07-04 10:42:57 +00:00
*
2006-10-07 15:30:46 +00:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2002-07-04 10:42:57 +00:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 15:30:46 +00:00
* version 2.1 of the License , or ( at your option ) any later version .
2002-07-04 10:42:57 +00:00
*
2006-10-07 15:30:46 +00:00
* FFmpeg is distributed in the hope that it will be useful ,
2002-07-04 10:42:57 +00:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2006-10-07 15:30:46 +00:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-12 22:43:26 +00:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2002-07-04 10:42:57 +00:00
*/
2008-04-15 22:22:49 +00:00
/**
2010-04-20 14:45:34 +00:00
* @ file
2008-04-15 22:22:49 +00:00
* RTP protocol
*/
2011-02-16 08:52:38 +00:00
# include "libavutil/parseutils.h"
2008-05-09 11:56:36 +00:00
# include "libavutil/avstring.h"
2015-03-22 17:08:43 +00:00
# include "libavutil/opt.h"
2002-07-04 10:42:57 +00:00
# include "avformat.h"
2011-03-08 09:35:52 +00:00
# include "avio_internal.h"
2013-07-31 09:29:32 +00:00
# include "rtp.h"
# include "rtpproto.h"
2011-03-31 14:25:10 +00:00
# include "url.h"
2002-07-04 10:42:57 +00:00
2002-07-24 17:38:20 +00:00
# include <stdarg.h>
2010-03-14 23:59:48 +00:00
# include "internal.h"
2007-02-04 17:05:44 +00:00
# include "network.h"
2007-11-27 11:42:09 +00:00
# include "os_support.h"
2002-07-04 10:42:57 +00:00
# include <fcntl.h>
2011-01-28 02:12:21 +00:00
# if HAVE_POLL_H
# include <sys/poll.h>
2008-08-14 14:29:18 +00:00
# endif
2002-07-04 10:42:57 +00:00
typedef struct RTPContext {
2015-03-22 17:08:43 +00:00
const AVClass * class ;
2016-06-02 14:28:13 +00:00
URLContext * rtp_hd , * rtcp_hd , * fec_hd ;
2013-07-26 19:37:00 +00:00
int rtp_fd , rtcp_fd , nb_ssm_include_addrs , nb_ssm_exclude_addrs ;
struct sockaddr_storage * * ssm_include_addrs , * * ssm_exclude_addrs ;
2013-08-13 10:20:42 +00:00
int write_to_source ;
struct sockaddr_storage last_rtp_source , last_rtcp_source ;
socklen_t last_rtp_source_len , last_rtcp_source_len ;
2015-03-22 17:08:43 +00:00
int ttl ;
2015-03-29 04:26:53 +00:00
int buffer_size ;
2015-03-22 17:08:43 +00:00
int rtcp_port , local_rtpport , local_rtcpport ;
int connect ;
int pkt_size ;
2015-04-11 16:53:54 +00:00
int dscp ;
2015-03-22 17:08:43 +00:00
char * sources ;
char * block ;
2016-06-02 14:28:13 +00:00
char * fec_options_str ;
2002-07-04 10:42:57 +00:00
} RTPContext ;
2015-03-22 17:08:43 +00:00
# define OFFSET(x) offsetof(RTPContext, x)
# define D AV_OPT_FLAG_DECODING_PARAM
# define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options [ ] = {
{ " ttl " , " Time to live (in milliseconds, multicast only) " , OFFSET ( ttl ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
2015-03-29 04:26:53 +00:00
{ " buffer_size " , " Send/Receive buffer size (in bytes) " , OFFSET ( buffer_size ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
2015-03-22 17:08:43 +00:00
{ " rtcp_port " , " Custom rtcp port " , OFFSET ( rtcp_port ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
{ " local_rtpport " , " Local rtp port " , OFFSET ( local_rtpport ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
{ " local_rtcpport " , " Local rtcp port " , OFFSET ( local_rtcpport ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
2015-11-21 21:05:07 +00:00
{ " connect " , " Connect socket " , OFFSET ( connect ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , . flags = D | E } ,
{ " write_to_source " , " Send packets to the source address of the latest received packet " , OFFSET ( write_to_source ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , . flags = D | E } ,
2015-03-22 17:08:43 +00:00
{ " pkt_size " , " Maximum packet size " , OFFSET ( pkt_size ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
2015-04-11 16:53:54 +00:00
{ " dscp " , " DSCP class " , OFFSET ( dscp ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , INT_MAX , . flags = D | E } ,
2015-03-22 17:08:43 +00: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 } ,
2016-06-02 14:28:13 +00:00
{ " fec " , " FEC " , OFFSET ( fec_options_str ) , AV_OPT_TYPE_STRING , { . str = NULL } , . flags = E } ,
2015-03-22 17:08:43 +00:00
{ NULL }
} ;
static const AVClass rtp_class = {
. class_name = " rtp " ,
. item_name = av_default_item_name ,
. option = options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2002-07-04 10:42:57 +00: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 .
*
2010-07-02 10:49:29 +00:00
* @ param h media file context
2002-07-04 10:42:57 +00:00
* @ param uri of the remote server
* @ return zero if no error .
*/
2008-04-15 22:22:49 +00:00
2011-10-12 09:37:42 +00:00
int ff_rtp_set_remote_url ( URLContext * h , const char * uri )
2002-07-04 10:42:57 +00:00
{
RTPContext * s = h - > priv_data ;
char hostname [ 256 ] ;
2013-07-31 08:48:28 +00:00
int port , rtcp_port ;
const char * p ;
2002-07-24 17:38:20 +00:00
2002-07-04 10:42:57 +00:00
char buf [ 1024 ] ;
char path [ 1024 ] ;
2005-12-17 18:14:38 +00:00
2010-06-27 14:16:46 +00:00
av_url_split ( NULL , 0 , NULL , 0 , hostname , sizeof ( hostname ) , & port ,
2010-03-08 09:05:03 +00:00
path , sizeof ( path ) , uri ) ;
2013-07-31 08:48:28 +00:00
rtcp_port = port + 1 ;
p = strchr ( uri , ' ? ' ) ;
if ( p ) {
if ( av_find_info_tag ( buf , sizeof ( buf ) , " rtcpport " , p ) ) {
rtcp_port = strtol ( buf , NULL , 10 ) ;
}
}
2002-07-04 10:42:57 +00:00
2010-03-05 22:35:21 +00:00
ff_url_join ( buf , sizeof ( buf ) , " udp " , NULL , hostname , port , " %s " , path ) ;
2011-03-08 09:35:52 +00:00
ff_udp_set_remote_url ( s - > rtp_hd , buf ) ;
2002-07-04 10:42:57 +00:00
2013-07-31 08:48:28 +00:00
ff_url_join ( buf , sizeof ( buf ) , " udp " , NULL , hostname , rtcp_port , " %s " , path ) ;
2011-03-08 09:35:52 +00:00
ff_udp_set_remote_url ( s - > rtcp_hd , buf ) ;
2002-07-04 10:42:57 +00:00
return 0 ;
}
2013-06-27 07:53:00 +00:00
static struct addrinfo * rtp_resolve_host ( const char * hostname , int port ,
int type , int family , int flags )
{
struct addrinfo hints = { 0 } , * res = 0 ;
int error ;
2013-07-18 18:12:14 +00:00
char service [ 16 ] ;
2013-06-27 07:53:00 +00:00
2013-07-18 18:12:14 +00:00
snprintf ( service , sizeof ( service ) , " %d " , port ) ;
2013-06-27 07:53:00 +00:00
hints . ai_socktype = type ;
hints . ai_family = family ;
2013-07-18 18:12:14 +00:00
hints . ai_flags = flags ;
if ( ( error = getaddrinfo ( hostname , service , & hints , & res ) ) ) {
2013-06-27 07:53:00 +00:00
res = NULL ;
av_log ( NULL , AV_LOG_ERROR , " rtp_resolve_host: %s \n " , gai_strerror ( error ) ) ;
}
return res ;
}
2002-07-04 10:42:57 +00:00
2013-07-18 18:12:14 +00:00
static int compare_addr ( const struct sockaddr_storage * a ,
const struct sockaddr_storage * b )
{
if ( a - > ss_family ! = b - > ss_family )
return 1 ;
if ( a - > ss_family = = AF_INET ) {
return ( ( ( const struct sockaddr_in * ) a ) - > sin_addr . s_addr ! =
( ( const struct sockaddr_in * ) b ) - > sin_addr . s_addr ) ;
}
2013-08-15 07:36:20 +00:00
# if HAVE_STRUCT_SOCKADDR_IN6
2013-07-18 18:12:14 +00:00
if ( a - > ss_family = = AF_INET6 ) {
const uint8_t * s6_addr_a = ( ( const struct sockaddr_in6 * ) a ) - > sin6_addr . s6_addr ;
const uint8_t * s6_addr_b = ( ( const struct sockaddr_in6 * ) b ) - > sin6_addr . s6_addr ;
return memcmp ( s6_addr_a , s6_addr_b , 16 ) ;
}
# endif
return 1 ;
}
2002-07-04 10:42:57 +00:00
2013-08-13 10:20:42 +00:00
static int get_port ( const struct sockaddr_storage * ss )
{
if ( ss - > ss_family = = AF_INET )
2014-09-16 21:11:47 +00:00
return ntohs ( ( ( const struct sockaddr_in * ) ss ) - > sin_port ) ;
2013-08-14 22:22:13 +00:00
# if HAVE_STRUCT_SOCKADDR_IN6
2013-08-13 10:20:42 +00:00
if ( ss - > ss_family = = AF_INET6 )
2014-09-16 21:11:47 +00:00
return ntohs ( ( ( const struct sockaddr_in6 * ) ss ) - > sin6_port ) ;
2013-08-13 10:20:42 +00:00
# endif
return 0 ;
}
static void set_port ( struct sockaddr_storage * ss , int port )
{
if ( ss - > ss_family = = AF_INET )
2014-09-16 21:11:47 +00:00
( ( struct sockaddr_in * ) ss ) - > sin_port = htons ( port ) ;
2013-08-14 22:22:13 +00:00
# if HAVE_STRUCT_SOCKADDR_IN6
2013-08-13 10:20:42 +00:00
else if ( ss - > ss_family = = AF_INET6 )
2014-09-16 21:11:47 +00:00
( ( struct sockaddr_in6 * ) ss ) - > sin6_port = htons ( port ) ;
2013-08-13 10:20:42 +00:00
# endif
}
2013-07-26 19:37:00 +00:00
static int rtp_check_source_lists ( RTPContext * s , struct sockaddr_storage * source_addr_ptr )
{
int i ;
if ( s - > nb_ssm_exclude_addrs ) {
for ( i = 0 ; i < s - > nb_ssm_exclude_addrs ; i + + ) {
if ( ! compare_addr ( source_addr_ptr , s - > ssm_exclude_addrs [ i ] ) )
return 1 ;
}
}
if ( s - > nb_ssm_include_addrs ) {
for ( i = 0 ; i < s - > nb_ssm_include_addrs ; i + + ) {
if ( ! compare_addr ( source_addr_ptr , s - > ssm_include_addrs [ i ] ) )
return 0 ;
}
return 1 ;
}
return 0 ;
}
2008-04-15 22:22:49 +00:00
/**
* add option to url of the form :
* " http://host:port/path?option1=val1&option2=val2...
*/
2011-06-17 07:31:11 +00:00
static av_printf_format ( 3 , 4 ) void url_add_option ( char * buf , int buf_size , const char * fmt , . . . )
2002-07-24 17:38:20 +00:00
{
char buf1 [ 1024 ] ;
va_list ap ;
va_start ( ap , fmt ) ;
if ( strchr ( buf , ' ? ' ) )
2007-06-24 11:27:12 +00:00
av_strlcat ( buf , " & " , buf_size ) ;
2002-07-24 17:38:20 +00:00
else
2007-06-24 11:27:12 +00:00
av_strlcat ( buf , " ? " , buf_size ) ;
2002-07-24 17:38:20 +00:00
vsnprintf ( buf1 , sizeof ( buf1 ) , fmt , ap ) ;
2007-06-24 11:27:12 +00:00
av_strlcat ( buf , buf1 , buf_size ) ;
2002-07-24 17:38:20 +00:00
va_end ( ap ) ;
}
2015-03-22 17:08:43 +00:00
static void build_udp_url ( RTPContext * s ,
char * buf , int buf_size ,
const char * hostname ,
int port , int local_port ,
2013-07-26 19:37:00 +00:00
const char * include_sources ,
const char * exclude_sources )
2002-07-24 17:38:20 +00:00
{
2010-03-05 22:35:21 +00:00
ff_url_join ( buf , buf_size , " udp " , NULL , hostname , port , NULL ) ;
2002-07-24 17:38:20 +00:00
if ( local_port > = 0 )
url_add_option ( buf , buf_size , " localport=%d " , local_port ) ;
2015-03-22 17:08:43 +00:00
if ( s - > ttl > = 0 )
url_add_option ( buf , buf_size , " ttl=%d " , s - > ttl ) ;
2015-03-29 04:26:53 +00:00
if ( s - > buffer_size > = 0 )
url_add_option ( buf , buf_size , " buffer_size=%d " , s - > buffer_size ) ;
2015-03-22 17:08:43 +00:00
if ( s - > pkt_size > = 0 )
url_add_option ( buf , buf_size , " pkt_size=%d " , s - > pkt_size ) ;
if ( s - > connect )
2011-01-06 15:16:09 +00:00
url_add_option ( buf , buf_size , " connect=1 " ) ;
2015-04-11 16:53:54 +00:00
if ( s - > dscp > = 0 )
url_add_option ( buf , buf_size , " dscp=%d " , s - > dscp ) ;
2011-07-25 13:50:56 +00:00
url_add_option ( buf , buf_size , " fifo_size=0 " ) ;
2013-07-26 19:37:00 +00:00
if ( include_sources & & include_sources [ 0 ] )
url_add_option ( buf , buf_size , " sources=%s " , include_sources ) ;
if ( exclude_sources & & exclude_sources [ 0 ] )
url_add_option ( buf , buf_size , " block=%s " , exclude_sources ) ;
}
static void rtp_parse_addr_list ( URLContext * h , char * buf ,
struct sockaddr_storage * * * address_list_ptr ,
int * address_list_size_ptr )
{
struct addrinfo * ai = NULL ;
struct sockaddr_storage * source_addr ;
char tmp = ' \0 ' , * p = buf , * next ;
/* Resolve all of the IPs */
while ( p & & p [ 0 ] ) {
next = strchr ( p , ' , ' ) ;
if ( next ) {
tmp = * next ;
* next = ' \0 ' ;
}
ai = rtp_resolve_host ( p , 0 , SOCK_DGRAM , AF_UNSPEC , 0 ) ;
if ( ai ) {
source_addr = av_mallocz ( sizeof ( struct sockaddr_storage ) ) ;
2014-10-18 15:58:41 +00:00
if ( ! source_addr ) {
freeaddrinfo ( ai ) ;
2013-07-26 19:37:00 +00:00
break ;
2014-10-18 15:58:41 +00:00
}
2013-07-26 19:37:00 +00:00
memcpy ( source_addr , ai - > ai_addr , ai - > ai_addrlen ) ;
freeaddrinfo ( ai ) ;
dynarray_add ( address_list_ptr , address_list_size_ptr , source_addr ) ;
} else {
av_log ( h , AV_LOG_WARNING , " Unable to resolve %s \n " , p ) ;
}
if ( next ) {
* next = tmp ;
p = next + 1 ;
} else {
p = NULL ;
}
}
2002-07-24 17:38:20 +00:00
}
2008-04-15 22:22:49 +00:00
/**
2002-07-24 17:38:20 +00:00
* url syntax : rtp : //host:port[?option=val...]
2010-04-19 11:40:45 +00:00
* option : ' ttl = n ' : set the ttl value ( for multicast only )
* ' rtcpport = n ' : set the remote rtcp port to n
* ' localrtpport = n ' : set the local rtp port to n
* ' localrtcpport = n ' : set the local rtcp port to n
* ' pkt_size = n ' : set max packet size
2011-01-06 15:16:09 +00:00
* ' connect = 0 / 1 ' : do a connect ( ) on the UDP socket
2013-08-13 09:19:57 +00:00
* ' sources = ip [ , ip ] ' : list allowed source IP addresses
* ' block = ip [ , ip ] ' : list disallowed source IP addresses
2013-08-13 10:20:42 +00:00
* ' write_to_source = 0 / 1 ' : send packets to the source address of the latest received packet
2014-05-23 13:31:17 +00:00
* ' dscp = n ' : set DSCP value to n ( QoS )
2010-04-19 11:40:45 +00:00
* deprecated option :
* ' localport = n ' : set the local port to n
2002-07-24 17:38:20 +00:00
*
2010-04-19 11:40:45 +00:00
* if rtcpport isn ' t set the rtcp port will be the rtp port + 1
* if local rtp port isn ' t set any available port will be used for the local
* rtp and rtcp ports
* if the local rtcp port is not set it will be the local rtp port + 1
2002-07-24 17:38:20 +00:00
*/
2008-04-15 22:22:49 +00:00
2002-07-04 10:42:57 +00:00
static int rtp_open ( URLContext * h , const char * uri , int flags )
{
2011-12-01 09:44:21 +00:00
RTPContext * s = h - > priv_data ;
2016-06-02 14:28:13 +00:00
AVDictionary * fec_opts = NULL ;
2015-03-22 17:08:43 +00:00
int rtp_port ;
2013-07-26 19:37:00 +00:00
char hostname [ 256 ] , include_sources [ 1024 ] = " " , exclude_sources [ 1024 ] = " " ;
2015-03-22 17:08:43 +00:00
char * sources = include_sources , * block = exclude_sources ;
2016-06-02 14:28:13 +00:00
char * fec_protocol = NULL ;
2002-07-04 10:42:57 +00:00
char buf [ 1024 ] ;
char path [ 1024 ] ;
2002-07-24 17:38:20 +00:00
const char * p ;
2013-10-20 08:53:09 +00:00
int i , max_retry_count = 3 ;
2016-01-07 10:55:50 +00:00
int rtcpflags ;
2005-12-17 18:14:38 +00:00
2010-06-27 14:16:46 +00:00
av_url_split ( NULL , 0 , NULL , 0 , hostname , sizeof ( hostname ) , & rtp_port ,
2010-03-08 09:05:03 +00:00
path , sizeof ( path ) , uri ) ;
2002-07-24 17:38:20 +00:00
/* extract parameters */
2015-03-22 17:08:43 +00:00
if ( s - > rtcp_port < 0 )
s - > rtcp_port = rtp_port + 1 ;
2008-04-15 19:27:39 +00:00
2002-07-24 17:38:20 +00:00
p = strchr ( uri , ' ? ' ) ;
if ( p ) {
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " ttl " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > ttl = strtol ( buf , NULL , 10 ) ;
2002-07-24 17:38:20 +00:00
}
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " rtcpport " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > rtcp_port = strtol ( buf , NULL , 10 ) ;
2010-04-19 11:40:45 +00:00
}
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " localport " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > local_rtpport = strtol ( buf , NULL , 10 ) ;
2010-04-19 11:40:45 +00:00
}
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " localrtpport " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > local_rtpport = strtol ( buf , NULL , 10 ) ;
2010-04-19 11:40:45 +00:00
}
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " localrtcpport " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > local_rtcpport = strtol ( buf , NULL , 10 ) ;
2002-07-24 17:38:20 +00:00
}
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " pkt_size " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > pkt_size = strtol ( buf , NULL , 10 ) ;
2008-04-15 19:27:39 +00:00
}
2011-02-16 08:52:38 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " connect " , p ) ) {
2015-03-22 17:08:43 +00:00
s - > connect = strtol ( buf , NULL , 10 ) ;
2011-01-06 15:16:09 +00:00
}
2013-08-13 10:20:42 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " write_to_source " , p ) ) {
s - > write_to_source = strtol ( buf , NULL , 10 ) ;
}
2014-05-23 13:31:17 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " dscp " , p ) ) {
2015-04-11 16:53:54 +00:00
s - > dscp = strtol ( buf , NULL , 10 ) ;
2014-05-23 13:31:17 +00:00
}
2013-07-18 17:04:16 +00:00
if ( av_find_info_tag ( buf , sizeof ( buf ) , " sources " , p ) ) {
2013-07-26 19:37:00 +00:00
av_strlcpy ( include_sources , buf , sizeof ( include_sources ) ) ;
2015-03-22 17:08:43 +00:00
2013-07-26 19:37:00 +00:00
rtp_parse_addr_list ( h , buf , & s - > ssm_include_addrs , & s - > nb_ssm_include_addrs ) ;
2015-03-22 17:08:43 +00:00
} else {
rtp_parse_addr_list ( h , s - > sources , & s - > ssm_include_addrs , & s - > nb_ssm_include_addrs ) ;
sources = s - > sources ;
2013-07-26 19:37:00 +00:00
}
if ( av_find_info_tag ( buf , sizeof ( buf ) , " block " , p ) ) {
av_strlcpy ( exclude_sources , buf , sizeof ( exclude_sources ) ) ;
rtp_parse_addr_list ( h , buf , & s - > ssm_exclude_addrs , & s - > nb_ssm_exclude_addrs ) ;
2015-03-22 17:08:43 +00:00
} else {
rtp_parse_addr_list ( h , s - > block , & s - > ssm_exclude_addrs , & s - > nb_ssm_exclude_addrs ) ;
block = s - > block ;
2013-06-27 07:53:00 +00:00
}
2002-07-24 17:38:20 +00:00
}
2002-07-04 10:42:57 +00:00
2016-06-02 14:28:13 +00:00
if ( s - > fec_options_str ) {
p = s - > fec_options_str ;
if ( ! ( fec_protocol = av_get_token ( & p , " = " ) ) ) {
av_log ( h , AV_LOG_ERROR , " Failed to parse the FEC protocol value \n " ) ;
goto fail ;
}
if ( strcmp ( fec_protocol , " prompeg " ) ) {
av_log ( h , AV_LOG_ERROR , " Unsupported FEC protocol %s \n " , fec_protocol ) ;
goto fail ;
}
p = s - > fec_options_str + strlen ( fec_protocol ) ;
while ( * p & & * p = = ' = ' ) p + + ;
if ( av_dict_parse_string ( & fec_opts , p , " = " , " : " , 0 ) < 0 ) {
av_log ( h , AV_LOG_ERROR , " Failed to parse the FEC options \n " ) ;
goto fail ;
}
if ( s - > ttl > 0 ) {
snprintf ( buf , sizeof ( buf ) , " %d " , s - > ttl ) ;
av_dict_set ( & fec_opts , " ttl " , buf , 0 ) ;
}
}
2015-04-01 19:03:10 +00:00
for ( i = 0 ; i < max_retry_count ; i + + ) {
build_udp_url ( s , buf , sizeof ( buf ) ,
hostname , rtp_port , s - > local_rtpport ,
2015-04-11 16:53:54 +00:00
sources , block ) ;
2016-01-30 01:17:51 +00:00
if ( ffurl_open_whitelist ( & s - > rtp_hd , buf , flags , & h - > interrupt_callback ,
2016-04-21 14:55:09 +00:00
NULL , h - > protocol_whitelist , h - > protocol_blacklist , h ) < 0 )
2013-10-20 08:53:09 +00:00
goto fail ;
2015-04-01 19:03:10 +00:00
s - > local_rtpport = ff_udp_get_local_port ( s - > rtp_hd ) ;
if ( s - > local_rtpport = = 65535 ) {
s - > local_rtpport = - 1 ;
2013-10-20 08:53:09 +00:00
continue ;
}
2016-01-07 10:55:50 +00:00
rtcpflags = flags | AVIO_FLAG_WRITE ;
2015-04-01 19:03:10 +00:00
if ( s - > local_rtcpport < 0 ) {
s - > local_rtcpport = s - > local_rtpport + 1 ;
build_udp_url ( s , buf , sizeof ( buf ) ,
hostname , s - > rtcp_port , s - > local_rtcpport ,
2015-04-11 16:53:54 +00:00
sources , block ) ;
2016-01-07 10:55:50 +00:00
if ( ffurl_open_whitelist ( & s - > rtcp_hd , buf , rtcpflags ,
2016-01-30 01:17:51 +00:00
& h - > interrupt_callback , NULL ,
2016-04-21 14:55:09 +00:00
h - > protocol_whitelist , h - > protocol_blacklist , h ) < 0 ) {
2015-04-01 19:03:10 +00:00
s - > local_rtpport = s - > local_rtcpport = - 1 ;
2013-10-20 08:53:09 +00:00
continue ;
}
break ;
}
2015-04-01 19:03:10 +00:00
build_udp_url ( s , buf , sizeof ( buf ) ,
hostname , s - > rtcp_port , s - > local_rtcpport ,
2015-04-11 16:53:54 +00:00
sources , block ) ;
2016-01-07 10:55:50 +00:00
if ( ffurl_open_whitelist ( & s - > rtcp_hd , buf , rtcpflags , & h - > interrupt_callback ,
2016-04-21 14:55:09 +00:00
NULL , h - > protocol_whitelist , h - > protocol_blacklist , h ) < 0 )
2013-10-20 08:53:09 +00:00
goto fail ;
break ;
}
2005-12-17 18:14:38 +00:00
2016-06-02 14:28:13 +00:00
s - > fec_hd = NULL ;
if ( fec_protocol ) {
ff_url_join ( buf , sizeof ( buf ) , fec_protocol , NULL , hostname , rtp_port , NULL ) ;
if ( ffurl_open_whitelist ( & s - > fec_hd , buf , flags , & h - > interrupt_callback ,
& fec_opts , h - > protocol_whitelist , h - > protocol_blacklist , h ) < 0 )
goto fail ;
}
2002-07-04 10:42:57 +00:00
/* just to ease handle access. XXX: need to suppress direct handle
access */
2011-03-31 15:51:24 +00:00
s - > rtp_fd = ffurl_get_file_handle ( s - > rtp_hd ) ;
s - > rtcp_fd = ffurl_get_file_handle ( s - > rtcp_hd ) ;
2002-07-04 10:42:57 +00:00
2011-03-31 15:58:04 +00:00
h - > max_packet_size = s - > rtp_hd - > max_packet_size ;
2002-07-04 10:42:57 +00:00
h - > is_streamed = 1 ;
2016-06-02 14:28:13 +00:00
av_free ( fec_protocol ) ;
av_dict_free ( & fec_opts ) ;
2002-07-04 10:42:57 +00:00
return 0 ;
fail :
if ( s - > rtp_hd )
2011-03-31 15:36:06 +00:00
ffurl_close ( s - > rtp_hd ) ;
2002-07-04 10:42:57 +00:00
if ( s - > rtcp_hd )
2011-03-31 15:36:06 +00:00
ffurl_close ( s - > rtcp_hd ) ;
2016-06-02 14:28:13 +00:00
ffurl_closep ( & s - > fec_hd ) ;
av_free ( fec_protocol ) ;
av_dict_free ( & fec_opts ) ;
2007-07-19 15:23:32 +00:00
return AVERROR ( EIO ) ;
2002-07-04 10:42:57 +00:00
}
2003-02-11 16:35:48 +00:00
static int rtp_read ( URLContext * h , uint8_t * buf , int size )
2002-07-04 10:42:57 +00:00
{
RTPContext * s = h - > priv_data ;
2013-07-31 09:21:47 +00:00
int len , n , i ;
2011-01-28 02:12:21 +00:00
struct pollfd p [ 2 ] = { { s - > rtp_fd , POLLIN , 0 } , { s - > rtcp_fd , POLLIN , 0 } } ;
2013-07-26 17:24:06 +00:00
int poll_delay = h - > flags & AVIO_FLAG_NONBLOCK ? 0 : 100 ;
2013-08-13 10:20:42 +00:00
struct sockaddr_storage * addrs [ 2 ] = { & s - > last_rtp_source , & s - > last_rtcp_source } ;
socklen_t * addr_lens [ 2 ] = { & s - > last_rtp_source_len , & s - > last_rtcp_source_len } ;
2011-01-28 02:12:21 +00:00
2002-07-04 10:42:57 +00:00
for ( ; ; ) {
2011-11-06 20:34:24 +00:00
if ( ff_check_interrupt ( & h - > interrupt_callback ) )
2011-03-12 23:42:27 +00:00
return AVERROR_EXIT ;
2013-07-26 17:24:06 +00:00
n = poll ( p , 2 , poll_delay ) ;
2002-07-04 10:42:57 +00:00
if ( n > 0 ) {
2013-07-31 09:21:47 +00:00
/* first try RTCP, then RTP */
for ( i = 1 ; i > = 0 ; i - - ) {
if ( ! ( p [ i ] . revents & POLLIN ) )
2013-07-18 18:12:14 +00:00
continue ;
2013-08-13 10:20:42 +00:00
* addr_lens [ i ] = sizeof ( * addrs [ i ] ) ;
2013-07-31 09:21:47 +00:00
len = recvfrom ( p [ i ] . fd , buf , size , 0 ,
2013-08-13 10:20:42 +00:00
( struct sockaddr * ) addrs [ i ] , addr_lens [ i ] ) ;
2002-07-04 10:42:57 +00:00
if ( len < 0 ) {
2011-02-19 18:14:11 +00:00
if ( ff_neterrno ( ) = = AVERROR ( EAGAIN ) | |
ff_neterrno ( ) = = AVERROR ( EINTR ) )
2002-07-04 10:42:57 +00:00
continue ;
2007-07-19 15:23:32 +00:00
return AVERROR ( EIO ) ;
2002-07-04 10:42:57 +00:00
}
2013-08-13 10:20:42 +00:00
if ( rtp_check_source_lists ( s , addrs [ i ] ) )
2013-07-18 18:12:14 +00:00
continue ;
2013-07-31 09:21:47 +00:00
return len ;
2002-07-04 10:42:57 +00:00
}
2010-03-05 08:15:20 +00:00
} else if ( n < 0 ) {
2011-02-19 18:14:11 +00:00
if ( ff_neterrno ( ) = = AVERROR ( EINTR ) )
2010-03-26 16:04:38 +00:00
continue ;
2010-03-05 08:15:20 +00:00
return AVERROR ( EIO ) ;
2002-07-04 10:42:57 +00:00
}
2013-07-26 17:24:06 +00:00
if ( h - > flags & AVIO_FLAG_NONBLOCK )
return AVERROR ( EAGAIN ) ;
2002-07-04 10:42:57 +00:00
}
}
2010-06-01 07:46:23 +00:00
static int rtp_write ( URLContext * h , const uint8_t * buf , int size )
2002-07-04 10:42:57 +00:00
{
RTPContext * s = h - > priv_data ;
2016-06-02 14:28:13 +00:00
int ret , ret_fec ;
2002-07-04 10:42:57 +00:00
URLContext * hd ;
2005-12-17 18:14:38 +00:00
2013-07-31 09:45:33 +00:00
if ( size < 2 )
return AVERROR ( EINVAL ) ;
2014-12-09 14:05:14 +00:00
if ( ( buf [ 0 ] & 0xc0 ) ! = ( RTP_VERSION < < 6 ) )
2014-12-06 23:42:06 +00:00
av_log ( h , AV_LOG_WARNING , " Data doesn't look like RTP packets, "
" make sure the RTP muxer is used \n " ) ;
2013-08-13 10:20:42 +00:00
if ( s - > write_to_source ) {
int fd ;
struct sockaddr_storage * source , temp_source ;
socklen_t * source_len , temp_len ;
if ( ! s - > last_rtp_source . ss_family & & ! s - > last_rtcp_source . ss_family ) {
av_log ( h , AV_LOG_ERROR ,
" Unable to send packet to source, no packets received yet \n " ) ;
// Intentionally not returning an error here
return size ;
}
if ( RTP_PT_IS_RTCP ( buf [ 1 ] ) ) {
fd = s - > rtcp_fd ;
source = & s - > last_rtcp_source ;
source_len = & s - > last_rtcp_source_len ;
} else {
fd = s - > rtp_fd ;
source = & s - > last_rtp_source ;
source_len = & s - > last_rtp_source_len ;
}
if ( ! source - > ss_family ) {
source = & temp_source ;
source_len = & temp_len ;
if ( RTP_PT_IS_RTCP ( buf [ 1 ] ) ) {
temp_source = s - > last_rtp_source ;
temp_len = s - > last_rtp_source_len ;
set_port ( source , get_port ( source ) + 1 ) ;
av_log ( h , AV_LOG_INFO ,
" Not received any RTCP packets yet, inferring peer port "
" from the RTP port \n " ) ;
} else {
temp_source = s - > last_rtcp_source ;
temp_len = s - > last_rtcp_source_len ;
set_port ( source , get_port ( source ) - 1 ) ;
av_log ( h , AV_LOG_INFO ,
" Not received any RTP packets yet, inferring peer port "
" from the RTCP port \n " ) ;
}
}
if ( ! ( h - > flags & AVIO_FLAG_NONBLOCK ) ) {
ret = ff_network_wait_fd ( fd , 1 ) ;
if ( ret < 0 )
return ret ;
}
ret = sendto ( fd , buf , size , 0 , ( struct sockaddr * ) source ,
* source_len ) ;
return ret < 0 ? ff_neterrno ( ) : ret ;
}
2012-02-16 16:31:03 +00:00
if ( RTP_PT_IS_RTCP ( buf [ 1 ] ) ) {
2002-07-04 10:42:57 +00:00
/* RTCP payload type */
hd = s - > rtcp_hd ;
} else {
/* RTP payload type */
hd = s - > rtp_hd ;
}
2016-06-02 14:28:13 +00:00
if ( ( ret = ffurl_write ( hd , buf , size ) ) < 0 ) {
return ret ;
}
if ( s - > fec_hd & & ! RTP_PT_IS_RTCP ( buf [ 1 ] ) ) {
if ( ( ret_fec = ffurl_write ( s - > fec_hd , buf , size ) ) < 0 ) {
av_log ( h , AV_LOG_ERROR , " Failed to send FEC \n " ) ;
return ret_fec ;
}
}
2002-07-04 10:42:57 +00:00
return ret ;
}
static int rtp_close ( URLContext * h )
{
RTPContext * s = h - > priv_data ;
2013-07-26 19:37:00 +00:00
int i ;
for ( i = 0 ; i < s - > nb_ssm_include_addrs ; i + + )
2014-12-24 12:00:34 +00:00
av_freep ( & s - > ssm_include_addrs [ i ] ) ;
2013-07-26 19:37:00 +00:00
av_freep ( & s - > ssm_include_addrs ) ;
for ( i = 0 ; i < s - > nb_ssm_exclude_addrs ; i + + )
2014-12-24 12:00:34 +00:00
av_freep ( & s - > ssm_exclude_addrs [ i ] ) ;
2013-07-26 19:37:00 +00:00
av_freep ( & s - > ssm_exclude_addrs ) ;
2002-07-04 10:42:57 +00:00
2011-03-31 15:36:06 +00:00
ffurl_close ( s - > rtp_hd ) ;
ffurl_close ( s - > rtcp_hd ) ;
2016-06-02 14:28:13 +00:00
ffurl_closep ( & s - > fec_hd ) ;
2002-07-04 10:42:57 +00:00
return 0 ;
}
/**
2010-04-19 11:40:45 +00:00
* Return the local rtp port used by the RTP connection
2010-07-02 10:49:29 +00:00
* @ param h media file context
2010-04-19 11:40:45 +00:00
* @ return the local port number
*/
2011-10-12 09:37:42 +00:00
int ff_rtp_get_local_rtp_port ( URLContext * h )
2010-04-19 11:40:45 +00:00
{
RTPContext * s = h - > priv_data ;
2011-03-08 09:35:52 +00:00
return ff_udp_get_local_port ( s - > rtp_hd ) ;
2002-07-04 10:42:57 +00:00
}
2010-04-19 11:40:45 +00:00
/**
* Return the local rtcp port used by the RTP connection
2010-07-02 10:49:29 +00:00
* @ param h media file context
2010-04-19 11:40:45 +00:00
* @ return the local port number
*/
2011-10-12 09:37:42 +00:00
int ff_rtp_get_local_rtcp_port ( URLContext * h )
2010-04-19 11:40:45 +00:00
{
RTPContext * s = h - > priv_data ;
2011-03-08 09:35:52 +00:00
return ff_udp_get_local_port ( s - > rtcp_hd ) ;
2010-04-19 11:40:45 +00:00
}
2009-03-03 17:04:51 +00:00
static int rtp_get_file_handle ( URLContext * h )
{
RTPContext * s = h - > priv_data ;
return s - > rtp_fd ;
}
2002-07-04 10:42:57 +00:00
2012-08-17 16:38:59 +00:00
static int rtp_get_multi_file_handle ( URLContext * h , int * * handles ,
int * numhandles )
{
2010-08-25 17:32:59 +00:00
RTPContext * s = h - > priv_data ;
2012-08-17 16:38:59 +00:00
int * hs = * handles = av_malloc ( sizeof ( * * handles ) * 2 ) ;
if ( ! hs )
return AVERROR ( ENOMEM ) ;
hs [ 0 ] = s - > rtp_fd ;
hs [ 1 ] = s - > rtcp_fd ;
* numhandles = 2 ;
return 0 ;
2010-08-25 17:32:59 +00:00
}
2016-02-19 09:39:29 +00:00
const URLProtocol ff_rtp_protocol = {
2012-08-17 16:38:59 +00:00
. name = " rtp " ,
. url_open = rtp_open ,
. url_read = rtp_read ,
. url_write = rtp_write ,
. url_close = rtp_close ,
. url_get_file_handle = rtp_get_file_handle ,
. url_get_multi_file_handle = rtp_get_multi_file_handle ,
. priv_data_size = sizeof ( RTPContext ) ,
. flags = URL_PROTOCOL_FLAG_NETWORK ,
2015-03-22 17:08:43 +00:00
. priv_data_class = & rtp_class ,
2002-07-04 10:42:57 +00:00
} ;