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 09:52:38 +01:00
#include "libavutil/parseutils.h"
2008-05-09 11:56:36 +00:00
#include "libavutil/avstring.h"
2015-03-22 18:08:43 +01:00
#include "libavutil/opt.h"
2002-07-04 10:42:57 +00:00
#include "avformat.h"
2011-03-08 10:35:52 +01:00
#include "avio_internal.h"
2013-07-31 12:29:32 +03:00
#include "rtp.h"
#include "rtpproto.h"
2011-03-31 16:25:10 +02:00
#include "url.h"
2018-09-14 01:33:32 +02:00
#include "ip.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 03:12:21 +01:00
#if HAVE_POLL_H
2019-01-14 18:21:15 +01:00
#include <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 18:08:43 +01:00
const AVClass * class ;
2016-06-02 16:28:13 +02:00
URLContext * rtp_hd , * rtcp_hd , * fec_hd ;
2018-09-14 01:33:32 +02:00
int rtp_fd , rtcp_fd ;
IPSourceFilters filters ;
2013-08-13 13:20:42 +03: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 18:08:43 +01:00
int ttl ;
2015-03-29 06:26:53 +02:00
int buffer_size ;
2015-03-22 18:08:43 +01:00
int rtcp_port , local_rtpport , local_rtcpport ;
int connect ;
int pkt_size ;
2015-04-11 18:53:54 +02:00
int dscp ;
2015-03-22 18:08:43 +01:00
char * sources ;
char * block ;
2016-06-02 16:28:13 +02:00
char * fec_options_str ;
2020-10-10 20:35:44 +08:00
int64_t rw_timeout ;
2021-11-26 16:55:08 +08:00
char * localaddr ;
2002-07-04 10:42:57 +00:00
} RTPContext ;
2015-03-22 18:08:43 +01: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 [] = {
2022-01-27 00:23:00 +08:00
{ "ttl" , "Time to live (multicast only)" , OFFSET ( ttl ), AV_OPT_TYPE_INT , { . i64 = - 1 }, - 1 , 255 , . flags = D | E },
2015-03-29 06:26:53 +02: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 18:08:43 +01: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 22:05:07 +01: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 18:08:43 +01:00
{ "pkt_size" , "Maximum packet size" , OFFSET ( pkt_size ), AV_OPT_TYPE_INT , { . i64 = - 1 }, - 1 , INT_MAX , . flags = D | E },
2015-04-11 18:53:54 +02:00
{ "dscp" , "DSCP class" , OFFSET ( dscp ), AV_OPT_TYPE_INT , { . i64 = - 1 }, - 1 , INT_MAX , . flags = D | E },
2020-10-10 20:35:44 +08:00
{ "timeout" , "set timeout (in microseconds) of socket I/O operations" , OFFSET ( rw_timeout ), AV_OPT_TYPE_INT64 , { . i64 = - 1 }, - 1 , INT64_MAX , . flags = D | E },
2015-03-22 18:08:43 +01: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 16:28:13 +02:00
{ "fec" , "FEC" , OFFSET ( fec_options_str ), AV_OPT_TYPE_STRING , { . str = NULL }, . flags = E },
2021-11-26 16:55:08 +08:00
{ "localaddr" , "Local address" , OFFSET ( localaddr ), AV_OPT_TYPE_STRING , { . str = NULL }, . flags = D | E },
2015-03-22 18:08:43 +01: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 12:37:42 +03: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 11:48:28 +03: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 11:48:28 +03: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 10:35:52 +01:00
ff_udp_set_remote_url ( s -> rtp_hd , buf );
2002-07-04 10:42:57 +00:00
2013-07-31 11:48:28 +03:00
ff_url_join ( buf , sizeof ( buf ), "udp" , NULL , hostname , rtcp_port , "%s" , path );
2011-03-08 10:35:52 +01:00
ff_udp_set_remote_url ( s -> rtcp_hd , buf );
2002-07-04 10:42:57 +00:00
return 0 ;
}
2013-08-13 13:20:42 +03:00
static int get_port ( const struct sockaddr_storage * ss )
{
if ( ss -> ss_family == AF_INET )
2014-09-16 23:11:47 +02:00
return ntohs ((( const struct sockaddr_in * ) ss ) -> sin_port );
2013-08-14 15:22:13 -07:00
#if HAVE_STRUCT_SOCKADDR_IN6
2013-08-13 13:20:42 +03:00
if ( ss -> ss_family == AF_INET6 )
2014-09-16 23:11:47 +02:00
return ntohs ((( const struct sockaddr_in6 * ) ss ) -> sin6_port );
2013-08-13 13:20:42 +03:00
#endif
return 0 ;
}
static void set_port ( struct sockaddr_storage * ss , int port )
{
if ( ss -> ss_family == AF_INET )
2014-09-16 23:11:47 +02:00
(( struct sockaddr_in * ) ss ) -> sin_port = htons ( port );
2013-08-14 15:22:13 -07:00
#if HAVE_STRUCT_SOCKADDR_IN6
2013-08-13 13:20:42 +03:00
else if ( ss -> ss_family == AF_INET6 )
2014-09-16 23:11:47 +02:00
(( struct sockaddr_in6 * ) ss ) -> sin6_port = htons ( port );
2013-08-13 13:20:42 +03:00
#endif
}
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 10:31:11 +03: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 18:08:43 +01:00
static void build_udp_url ( RTPContext * s ,
char * buf , int buf_size ,
const char * hostname ,
2021-11-26 16:55:08 +08:00
const char * localaddr ,
2015-03-22 18:08:43 +01:00
int port , int local_port ,
2013-07-26 22:37:00 +03: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 18:08:43 +01:00
if ( s -> ttl >= 0 )
url_add_option ( buf , buf_size , "ttl=%d" , s -> ttl );
2015-03-29 06:26:53 +02:00
if ( s -> buffer_size >= 0 )
url_add_option ( buf , buf_size , "buffer_size=%d" , s -> buffer_size );
2015-03-22 18:08:43 +01: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 18:53:54 +02:00
if ( s -> dscp >= 0 )
url_add_option ( buf , buf_size , "dscp=%d" , s -> dscp );
2011-07-25 15:50:56 +02:00
url_add_option ( buf , buf_size , "fifo_size=0" );
2013-07-26 22:37:00 +03: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 );
2021-11-26 16:55:08 +08:00
if ( localaddr && localaddr [ 0 ])
url_add_option ( buf , buf_size , "localaddr=%s" , localaddr );
2013-07-26 22:37:00 +03: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 12:19:57 +03:00
* 'sources=ip[,ip]' : list allowed source IP addresses
* 'block=ip[,ip]' : list disallowed source IP addresses
2013-08-13 13:20:42 +03:00
* 'write_to_source=0/1' : send packets to the source address of the latest received packet
2014-05-23 15:31:17 +02: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 11:44:21 +02:00
RTPContext * s = h -> priv_data ;
2016-06-02 16:28:13 +02:00
AVDictionary * fec_opts = NULL ;
2015-03-22 18:08:43 +01:00
int rtp_port ;
2013-07-26 22:37:00 +03:00
char hostname [ 256 ], include_sources [ 1024 ] = "" , exclude_sources [ 1024 ] = "" ;
2015-03-22 18:08:43 +01:00
char * sources = include_sources , * block = exclude_sources ;
2016-06-02 16:28:13 +02: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 16:53:09 +08:00
int i , max_retry_count = 3 ;
2016-01-07 18:55:50 +08: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 18:08:43 +01: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 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "ttl" , p )) {
2015-03-22 18:08:43 +01:00
s -> ttl = strtol ( buf , NULL , 10 );
2002-07-24 17:38:20 +00:00
}
2011-02-16 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "rtcpport" , p )) {
2015-03-22 18:08:43 +01:00
s -> rtcp_port = strtol ( buf , NULL , 10 );
2010-04-19 11:40:45 +00:00
}
2011-02-16 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "localport" , p )) {
2015-03-22 18:08:43 +01:00
s -> local_rtpport = strtol ( buf , NULL , 10 );
2010-04-19 11:40:45 +00:00
}
2011-02-16 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "localrtpport" , p )) {
2015-03-22 18:08:43 +01:00
s -> local_rtpport = strtol ( buf , NULL , 10 );
2010-04-19 11:40:45 +00:00
}
2011-02-16 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "localrtcpport" , p )) {
2015-03-22 18:08:43 +01:00
s -> local_rtcpport = strtol ( buf , NULL , 10 );
2002-07-24 17:38:20 +00:00
}
2011-02-16 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "pkt_size" , p )) {
2015-03-22 18:08:43 +01:00
s -> pkt_size = strtol ( buf , NULL , 10 );
2008-04-15 19:27:39 +00:00
}
2011-02-16 09:52:38 +01:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "connect" , p )) {
2015-03-22 18:08:43 +01:00
s -> connect = strtol ( buf , NULL , 10 );
2011-01-06 15:16:09 +00:00
}
2013-08-13 13:20:42 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "write_to_source" , p )) {
s -> write_to_source = strtol ( buf , NULL , 10 );
}
2014-05-23 15:31:17 +02:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "dscp" , p )) {
2015-04-11 18:53:54 +02:00
s -> dscp = strtol ( buf , NULL , 10 );
2014-05-23 15:31:17 +02:00
}
2020-10-10 20:35:44 +08:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "timeout" , p )) {
s -> rw_timeout = strtol ( buf , NULL , 10 );
}
2013-07-18 20:04:16 +03:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "sources" , p )) {
2013-07-26 22:37:00 +03:00
av_strlcpy ( include_sources , buf , sizeof ( include_sources ));
2018-09-14 01:33:32 +02:00
ff_ip_parse_sources ( h , buf , & s -> filters );
2015-03-22 18:08:43 +01:00
} else {
2018-09-14 01:33:32 +02:00
ff_ip_parse_sources ( h , s -> sources , & s -> filters );
2015-03-22 18:08:43 +01:00
sources = s -> sources ;
2013-07-26 22:37:00 +03:00
}
if ( av_find_info_tag ( buf , sizeof ( buf ), "block" , p )) {
av_strlcpy ( exclude_sources , buf , sizeof ( exclude_sources ));
2018-09-14 01:33:32 +02:00
ff_ip_parse_blocks ( h , buf , & s -> filters );
2015-03-22 18:08:43 +01:00
} else {
2018-09-14 01:33:32 +02:00
ff_ip_parse_blocks ( h , s -> block , & s -> filters );
2015-03-22 18:08:43 +01:00
block = s -> block ;
2013-06-27 08:53:00 +01:00
}
2021-11-26 16:55:08 +08:00
if ( av_find_info_tag ( buf , sizeof ( buf ), "localaddr" , p )) {
av_freep ( & s -> localaddr );
s -> localaddr = av_strdup ( buf );
if ( ! s -> localaddr )
goto fail ;
}
2002-07-24 17:38:20 +00:00
}
2020-10-10 20:35:44 +08:00
if ( s -> rw_timeout >= 0 )
h -> rw_timeout = s -> rw_timeout ;
2002-07-04 10:42:57 +00:00
2016-06-02 16:28:13 +02: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 ) {
2020-05-25 20:19:09 +08:00
av_dict_set_int ( & fec_opts , "ttl" , s -> ttl , 0 );
2016-06-02 16:28:13 +02:00
}
}
2015-04-01 21:03:10 +02:00
for ( i = 0 ; i < max_retry_count ; i ++ ) {
build_udp_url ( s , buf , sizeof ( buf ),
2021-11-26 16:55:08 +08:00
hostname , s -> localaddr , rtp_port , s -> local_rtpport ,
2015-04-11 18:53:54 +02:00
sources , block );
2016-01-30 02:17:51 +01:00
if ( ffurl_open_whitelist ( & s -> rtp_hd , buf , flags , & h -> interrupt_callback ,
2016-04-21 15:55:09 +01:00
NULL , h -> protocol_whitelist , h -> protocol_blacklist , h ) < 0 )
2013-10-20 16:53:09 +08:00
goto fail ;
2015-04-01 21:03:10 +02:00
s -> local_rtpport = ff_udp_get_local_port ( s -> rtp_hd );
if ( s -> local_rtpport == 65535 ) {
s -> local_rtpport = - 1 ;
2013-10-20 16:53:09 +08:00
continue ;
}
2016-01-07 18:55:50 +08:00
rtcpflags = flags | AVIO_FLAG_WRITE ;
2015-04-01 21:03:10 +02:00
if ( s -> local_rtcpport < 0 ) {
s -> local_rtcpport = s -> local_rtpport + 1 ;
build_udp_url ( s , buf , sizeof ( buf ),
2021-11-26 16:55:08 +08:00
hostname , s -> localaddr , s -> rtcp_port , s -> local_rtcpport ,
2015-04-11 18:53:54 +02:00
sources , block );
2016-01-07 18:55:50 +08:00
if ( ffurl_open_whitelist ( & s -> rtcp_hd , buf , rtcpflags ,
2016-01-30 02:17:51 +01:00
& h -> interrupt_callback , NULL ,
2016-04-21 15:55:09 +01:00
h -> protocol_whitelist , h -> protocol_blacklist , h ) < 0 ) {
2015-04-01 21:03:10 +02:00
s -> local_rtpport = s -> local_rtcpport = - 1 ;
2013-10-20 16:53:09 +08:00
continue ;
}
break ;
}
2015-04-01 21:03:10 +02:00
build_udp_url ( s , buf , sizeof ( buf ),
2021-11-26 16:55:08 +08:00
hostname , s -> localaddr , s -> rtcp_port , s -> local_rtcpport ,
2015-04-11 18:53:54 +02:00
sources , block );
2016-01-07 18:55:50 +08:00
if ( ffurl_open_whitelist ( & s -> rtcp_hd , buf , rtcpflags , & h -> interrupt_callback ,
2016-04-21 15:55:09 +01:00
NULL , h -> protocol_whitelist , h -> protocol_blacklist , h ) < 0 )
2013-10-20 16:53:09 +08:00
goto fail ;
break ;
}
2005-12-17 18:14:38 +00:00
2016-06-02 16:28:13 +02: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 17:51:24 +02: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 17:58:04 +02: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 16:28:13 +02:00
av_free ( fec_protocol );
av_dict_free ( & fec_opts );
2002-07-04 10:42:57 +00:00
return 0 ;
fail :
2020-04-03 17:03:38 +02:00
ffurl_closep ( & s -> rtp_hd );
ffurl_closep ( & s -> rtcp_hd );
2016-06-02 16:28:13 +02: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 12:21:47 +03:00
int len , n , i ;
2011-01-28 03:12:21 +01:00
struct pollfd p [ 2 ] = {{ s -> rtp_fd , POLLIN , 0 }, { s -> rtcp_fd , POLLIN , 0 }};
2020-10-10 20:35:44 +08:00
int poll_delay = h -> flags & AVIO_FLAG_NONBLOCK ? 0 : POLLING_TIME ;
2013-08-13 13:20:42 +03: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 };
2020-10-10 20:35:44 +08:00
int runs = h -> rw_timeout / 1000 / POLLING_TIME ;
2011-01-28 03:12:21 +01:00
2002-07-04 10:42:57 +00:00
for (;;) {
2011-11-06 22:34:24 +02:00
if ( ff_check_interrupt ( & h -> interrupt_callback ))
2011-03-13 00:42:27 +01:00
return AVERROR_EXIT ;
2013-07-26 20:24:06 +03:00
n = poll ( p , 2 , poll_delay );
2002-07-04 10:42:57 +00:00
if ( n > 0 ) {
2013-07-31 12:21:47 +03:00
/* first try RTCP, then RTP */
for ( i = 1 ; i >= 0 ; i -- ) {
if ( ! ( p [ i ]. revents & POLLIN ))
continue ;
2013-08-13 13:20:42 +03:00
* addr_lens [ i ] = sizeof ( * addrs [ i ]);
2013-07-31 12:21:47 +03:00
len = recvfrom ( p [ i ]. fd , buf , size , 0 ,
2013-08-13 13:20:42 +03:00
( struct sockaddr * ) addrs [ i ], addr_lens [ i ]);
2002-07-04 10:42:57 +00:00
if ( len < 0 ) {
2011-02-19 19:14:11 +01: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
}
2018-09-14 01:33:32 +02:00
if ( ff_ip_check_source_lists ( addrs [ i ], & s -> filters ))
2013-07-18 21:12:14 +03:00
continue ;
2013-07-31 12:21:47 +03:00
return len ;
2002-07-04 10:42:57 +00:00
}
2020-10-10 20:35:44 +08:00
} else if ( n == 0 && h -> rw_timeout > 0 && -- runs <= 0 ) {
return AVERROR ( ETIMEDOUT );
2010-03-05 08:15:20 +00:00
} else if ( n < 0 ) {
2011-02-19 19:14:11 +01: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 20:24:06 +03: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 16:28:13 +02: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 12:45:33 +03:00
if ( size < 2 )
return AVERROR ( EINVAL );
2014-12-09 16:05:14 +02:00
if (( buf [ 0 ] & 0xc0 ) != ( RTP_VERSION << 6 ))
2014-12-07 01:42:06 +02: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 13:20:42 +03: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 17:31:03 +01: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 16:28:13 +02: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 22:37:00 +03:00
2018-09-14 01:33:32 +02:00
ff_ip_reset_filters ( & s -> filters );
2002-07-04 10:42:57 +00:00
2020-04-03 17:03:38 +02:00
ffurl_closep ( & s -> rtp_hd );
ffurl_closep ( & s -> rtcp_hd );
2016-06-02 16:28:13 +02: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 12:37:42 +03: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 10:35:52 +01: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
*/
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 18:38:59 +02: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 18:38:59 +02: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 10:39:29 +01:00
const URLProtocol ff_rtp_protocol = {
2012-08-17 18:38:59 +02: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 18:08:43 +01:00
. priv_data_class = & rtp_class ,
2002-07-04 10:42:57 +00:00
};