From: Jean-Paul Saman Date: Tue, 21 Nov 2006 10:46:12 +0000 (+0000) Subject: Rewrote internal RTCP to support multiple client connections. TODO: rtcp scheduling... X-Git-Tag: 0.9.0-test0~9345 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=4d243c1d7853c7021befe5edd0ac4e051193ae8a;p=vlc Rewrote internal RTCP to support multiple client connections. TODO: rtcp scheduling function, server socket, hooking it up with RTP stream_out connections. --- diff --git a/configure.ac b/configure.ac index 9252a4f1ed..0714aa74db 100644 --- a/configure.ac +++ b/configure.ac @@ -1448,6 +1448,7 @@ then VLC_ADD_PLUGINS([stream_out_dummy stream_out_standard stream_out_es stream_out_rtp stream_out_description vod_rtsp]) VLC_ADD_PLUGINS([stream_out_duplicate stream_out_gather stream_out_display stream_out_transcode stream_out_bridge stream_out_mosaic_bridge stream_out_autodel]) # VLC_ADD_PLUGINS([stream_out_transrate]) + VLC_ADD_PLUGINS([rtcp]) VLC_ADD_PLUGINS([profile_parser]) AC_DEFINE(ENABLE_SOUT, 1, Define if you want the stream output support) diff --git a/modules/mux/rtp/Modules.am b/modules/mux/rtp/Modules.am index 072cfe1303..bd1284df27 100644 --- a/modules/mux/rtp/Modules.am +++ b/modules/mux/rtp/Modules.am @@ -1 +1 @@ -SOURCES_rtcp = rtcp.c rtcp.h rtp.h +SOURCES_rtcp = rtcp.c rtcp.h rtp.h diff --git a/modules/mux/rtp/rtcp.c b/modules/mux/rtp/rtcp.c index 126c6d9561..2d521a7da8 100644 --- a/modules/mux/rtp/rtcp.c +++ b/modules/mux/rtp/rtcp.c @@ -22,9 +22,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/ +#include #include #include -#include #include #include @@ -33,152 +33,576 @@ #include "rtp.h" #include "rtcp.h" -static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_block ); -static int rtcp_decode_RR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ); -static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ); -static int rtcp_decode_SDES( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ); -static int rtcp_decode_BYE( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ); -static int rtcp_decode_APP( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ); +static void send_RTCP( vlc_object_t *p_this, rtcp_event_t rtcp_event ) +{ +/* FIXME: higher level functions that should be in another file */ +} + +static void rtcp_schedule( vlc_object_t *p_this, uint64_t i_sched, rtcp_event_t rtcp_event ) +{ +/* FIXME: higher level functions that should be in another file */ +} + +/* SDES support functions */ +static int SDES_client_item_add( rtcp_client_t *p_client, int i_item, char *psz_name ) +{ + rtcp_SDES_item_t *p_item = NULL; + + p_item = (rtcp_SDES_item_t *) malloc( sizeof( rtcp_SDES_item_t ) ); + if( !p_item ) + return VLC_EGENERIC; + p_item->u_type = i_item; + p_item->psz_data = strdup( psz_name ); + p_item->i_index = p_client->i_items + 1;; + INSERT_ELEM( p_client->pp_sdes, p_client->i_items, + p_item->i_index, p_item ); + return VLC_EGENERIC; +} + +static int SDES_client_item_del( rtcp_client_t *p_client ) +{ + uint32_t i = 0; + + for( i=0; i < p_client->i_items; i++ ) + { + rtcp_SDES_item_t *p_old = p_client->pp_sdes[i]; + REMOVE_ELEM( p_client->pp_sdes, p_client->i_items, i ); + p_client->i_items--; + if( p_old->psz_data) + free( p_old->psz_data ); + free( p_old ); + } + return VLC_SUCCESS; +} + +int rtcp_add_client( vlc_object_t *p_this, uint32_t u_ssrc, uint32_t *i_pos ) +{ + rtcp_t *p_rtcp = (rtcp_t *) p_this; + rtcp_client_t *p_client = NULL; + + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = (rtcp_client_t*) malloc( sizeof(rtcp_client_t) ); + if( !p_client ) + return VLC_EGENERIC; + p_client->i_index = p_rtcp->i_clients + 1; + p_client->b_deleted = VLC_FALSE; + *i_pos = p_client->i_index ; + INSERT_ELEM( p_rtcp->pp_clients, p_rtcp->i_clients, + p_client->i_index, p_client ); + p_rtcp->i_clients++; + p_rtcp->u_clients++; + vlc_mutex_unlock( &p_rtcp->object_lock ); + return VLC_SUCCESS; +} + +int rtcp_del_client( vlc_object_t *p_this, uint32_t u_ssrc ) +{ + rtcp_t *p_rtcp = (rtcp_t *) p_this; + uint32_t i_pos = 0; + + vlc_mutex_lock( &p_rtcp->object_lock ); + if( p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ) == VLC_SUCCESS ) + { + rtcp_client_t *p_old = p_rtcp->pp_clients[i_pos]; + + p_old->b_deleted = VLC_TRUE; + p_old->i_timeout = 5 * (p_rtcp->i_date - p_rtcp->i_last_date) + + p_rtcp->i_next_date; + p_rtcp->u_clients--; + } + vlc_mutex_unlock( &p_rtcp->object_lock ); + return VLC_SUCCESS; +} + +/* rtcp_cleanup_clients should not be called too often */ +int rtcp_cleanup_clients( vlc_object_t *p_this ) +{ + rtcp_t *p_rtcp = (rtcp_t *) p_this; + uint32_t i = 0; + + vlc_mutex_lock( &p_rtcp->object_lock ); + for( i=0; i < p_rtcp->i_clients; i++ ) + { + rtcp_client_t *p_old = p_rtcp->pp_clients[i]; + + if( p_old->b_deleted && + (p_old->i_timeout > mdate()) ) + { + REMOVE_ELEM( p_rtcp->pp_clients, p_rtcp->i_clients, i ); + p_rtcp->i_clients--; + SDES_client_item_del( p_old ); + free( p_old ); + } + } + vlc_mutex_unlock( &p_rtcp->object_lock ); + return VLC_SUCCESS; +} + +/* rtcp_find_client should be called with the object lock held. + * vlc_mutex_lock( &p_rtcp->obj_lock ); + */ +int rtcp_find_client( vlc_object_t *p_this, uint32_t u_ssrc, uint32_t *i_pos ) +{ + rtcp_t *p_rtcp = (rtcp_t *) p_this; + uint32_t i = 0; + + for( i=0; i < p_rtcp->i_clients; i++ ) + { + if( p_rtcp->pp_clients[i]->u_ssrc == u_ssrc ) + { + *i_pos = i; + return VLC_SUCCESS; + } + } + *i_pos = -1; + return VLC_EGENERIC; +} + +/*-------------------------------------------------------------------------- + * rtcp_interval - Calculate the interval in seconds for sending RTCP packets. + *-------------------------------------------------------------------------- + */ +uint64_t rtcp_interval( vlc_object_t *p_this, uint64_t u_bandwidth, + vlc_bool_t b_sender, vlc_bool_t b_first ) +{ + rtcp_t *p_rtcp = (rtcp_t *) p_this; + rtcp_client_t *p_client = NULL; + uint32_t i_rtcp_min = 5; /* seconds */ + uint32_t i_pos = 0; + double i_bandwidth = u_bandwidth; + const double i_compensation = 2.71828 - 1.5; + double i_interval = 0; + int n = p_rtcp->i_clients; + + int u_ssrc = 0; /* FIXME: how to know which client we look for?? */ + + if( b_first ) + i_rtcp_min = (i_rtcp_min >> 1); + + if( (double)(p_rtcp->u_active) <= (double)(p_rtcp->u_clients * 0.25) ) + { + if( b_sender ) + { + i_bandwidth = i_bandwidth * 0.25; + n = p_rtcp->u_active; + } + else + { + i_bandwidth = i_bandwidth * ( 1 - 0.25 ); + n = n - p_rtcp->u_active; + } + } + /* calculate average time between reports */ + p_client = p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ); + if( !p_client ) + return -1; + + i_interval = p_client->p_stats->u_avg_pkt_size * ( n / i_bandwidth ); + if( i_interval < i_rtcp_min ) + i_interval = i_rtcp_min; + i_interval = i_interval * ( drand48() + 0.5 ); + i_interval = (double) (i_interval / i_compensation); + + return (uint64_t)i_interval; +} + +/*-------------------------------------------------------------------------- + * rtcp_expire - decides to sent an RTCP report or a BYE record + *-------------------------------------------------------------------------- + */ +void rtcp_expire( vlc_object_t *p_this, rtcp_event_t rtcp_event, + uint64_t u_bandwidth, vlc_bool_t b_sender, vlc_bool_t *b_first ) +{ + rtcp_t *p_rtcp = (rtcp_t *) p_this; + rtcp_client_t *p_client = NULL; + rtcp_stats_t *p_stats = NULL; + mtime_t i_interval = 0; + uint32_t i_pos = 0; + + int u_ssrc = 0; /* FIXME: how to know which client we look for?? */ + + p_client = p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ); + if( !p_client ) + return; + p_stats = (rtcp_stats_t*) p_client->p_stats; + i_interval = (mtime_t) rtcp_interval( p_this, u_bandwidth, + b_sender, *b_first ); + p_rtcp->i_next_date = p_rtcp->i_last_date + i_interval; + + switch( rtcp_event ) + { + case EVENT_BYE: + if( p_rtcp->i_next_date <= p_rtcp->i_date ) + send_RTCP( p_this, rtcp_event ); + else + rtcp_schedule( p_this, p_rtcp->i_next_date, rtcp_event ); + break; -static block_t *rtcp_encode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp ); -static block_t *rtcp_encode_RR( vlc_object_t *p_this, rtcp_t *p_rtcp ); -static block_t *rtcp_encode_SDES( vlc_object_t *p_this, rtcp_t *p_rtcp ); -static block_t *rtcp_encode_BYE( vlc_object_t *p_this, rtcp_t *p_rtcp ); + case EVENT_REPORT: + if( p_rtcp->i_next_date <= p_rtcp->i_date ) + { + send_RTCP( p_this, rtcp_event ); + + /* Magic numbers are from RFC 3550 page 92 + * 1.0/16.0 = 0.0625 and + * 15.0/16.0 = 0.9375 + */ + p_stats->u_avg_pkt_size = (uint64_t) + ( (double) ( (double)p_stats->u_sent_pkt_size / ((double)0.0625) ) + + ( ((double)0.9357) * p_stats->u_avg_pkt_size ) ); + + /* recalculate */ + p_rtcp->i_last_date = p_rtcp->i_date; + i_interval = rtcp_interval( p_this, u_bandwidth, + b_sender, *b_first ); + rtcp_schedule( p_this, p_rtcp->i_next_date + i_interval, rtcp_event ); + *b_first = VLC_FALSE; + } + else + { + rtcp_schedule( p_this, p_rtcp->i_next_date, rtcp_event ); + } + break; + } + p_rtcp->i_date = p_rtcp->i_next_date; +} -static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +/*-------------------------------------------------------------------------- + * Local functions prototoypes + *-------------------------------------------------------------------------- + */ +static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +static int rtcp_decode_RR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +static int rtcp_decode_SDES( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +static int rtcp_decode_BYE( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +static int rtcp_decode_APP( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); + +/*-------------------------------------------------------------------------- + * Local functions + *-------------------------------------------------------------------------- + */ +static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { - unsigned int u_ssrc_count; + rtcp_t *p_rtcp = (rtcp_t *) p_this; unsigned int i = 0; - if( !p_rtcp && !p_buffer ) + if( !p_pkt ) return VLC_EGENERIC; msg_Dbg( p_this, "decoding record: SR" ); - p_rtcp->stats.u_SR_received++; - p_rtcp->stats.u_pkt_count = p_buffer[20+RTCP_HEADER_LEN]; - p_rtcp->stats.u_octet_count = p_buffer[24+RTCP_HEADER_LEN]; - u_ssrc_count = p_buffer[RTCP_HEADER_LEN] & 0x1f; - msg_Dbg( p_this, "SR received %d, packet count %d, octect count %d, SSRC count %d", - p_rtcp->stats.u_SR_received, - p_rtcp->stats.u_pkt_count, - p_rtcp->stats.u_octet_count, - u_ssrc_count ); - - for( i=0; i < u_ssrc_count; i++ ) + + /* parse sender info */ + p_pkt->u_payload_type = RTCP_SR; + p_pkt->report.sr.ntp_timestampH = bs_read( p_rtcp->bs, 32 ); + p_pkt->report.sr.ntp_timestampL = bs_read( p_rtcp->bs, 32 ); + p_pkt->report.sr.rtp_timestamp = bs_read( p_rtcp->bs, 32 ); + p_pkt->report.sr.u_pkt_count = bs_read( p_rtcp->bs, 32 ); /*sender*/ + p_pkt->report.sr.u_octet_count = bs_read( p_rtcp->bs, 32 ); /*sender*/ + + /* parse report block */ + for( i=0; i < p_pkt->u_report; i++ ) { - unsigned char count[4]; - - p_rtcp->stats.u_fract_lost = p_buffer[32+RTCP_HEADER_LEN]; - - count[0] = 0; - count[1] = p_buffer[33+RTCP_HEADER_LEN]; - count[2] = p_buffer[34+RTCP_HEADER_LEN]; - count[3] = p_buffer[35+RTCP_HEADER_LEN]; - - /* FIXME: I don't like the sight of this */ - p_rtcp->stats.u_pkt_lost = ntohl((int)count); - - p_rtcp->stats.u_highest_seq_no = ntohl( p_buffer[36+RTCP_HEADER_LEN] ); - p_rtcp->stats.u_jitter = ntohl( p_buffer[40+RTCP_HEADER_LEN] ); - p_rtcp->stats.u_last_SR = ntohl( p_buffer[44+RTCP_HEADER_LEN] ); - p_rtcp->stats.u_delay_since_last_SR = (mtime_t) ntohl( p_buffer[48+RTCP_HEADER_LEN] ); - - msg_Dbg( p_this, "fract lost %d, packet lost %d, highest seqno %d, jitter %d, last SR %d, delay %lld", - p_rtcp->stats.u_fract_lost, - p_rtcp->stats.u_pkt_lost, - p_rtcp->stats.u_highest_seq_no, - p_rtcp->stats.u_jitter, - p_rtcp->stats.u_last_SR, - p_rtcp->stats.u_delay_since_last_SR ); + rtcp_client_t *p_client = NULL; + uint32_t i_pos = 0; + uint32_t u_ssrc = 0; + int result = 0; + + u_ssrc = bs_read( p_rtcp->bs, 32 ); + + result = p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + { + result = p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + return VLC_ENOMEM; + } + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = p_rtcp->pp_clients[i_pos]; + + p_client->p_stats->u_SR_received++; + p_client->p_stats->u_pkt_count++; + p_client->p_stats->u_octet_count++; + + msg_Dbg( p_this, "SR received %d, packet count %d, octect count %d, SSRC count %d", + p_client->p_stats->u_SR_received, + p_client->p_stats->u_pkt_count, + p_client->p_stats->u_octet_count, + p_pkt->u_ssrc ); + + p_client->p_stats->u_fraction_lost = bs_read( p_rtcp->bs, 8 ); + p_client->p_stats->u_pkt_lost = bs_read( p_rtcp->bs, 24 ); + p_client->p_stats->u_highest_seq_no = bs_read( p_rtcp->bs, 32 ); + p_client->p_stats->u_jitter = bs_read( p_rtcp->bs, 32 ); + p_client->p_stats->u_last_SR = bs_read( p_rtcp->bs, 32 ); + p_client->p_stats->u_delay_since_last_SR = (mtime_t) bs_read( p_rtcp->bs, 32 ); + + /* Magic numbers are from RFC 3550 page 92 + * 1.0/16.0 = 0.0625 and + * 15.0/16.0 = 0.9375 + */ + p_client->p_stats->u_avg_pkt_size = (uint64_t) + ( (double)((double)p_client->p_stats->u_sent_pkt_size * (double)(0.0625)) + + ((double)(0.9375) * p_client->p_stats->u_avg_pkt_size) ); + + msg_Dbg( p_this, "fract lost %d, packet lost %d, highest seqno %d, " + "jitter %d, last SR %d, delay %lld", + p_client->p_stats->u_fraction_lost, + p_client->p_stats->u_pkt_lost, + p_client->p_stats->u_highest_seq_no, + p_client->p_stats->u_jitter, + p_client->p_stats->u_last_SR, + p_client->p_stats->u_delay_since_last_SR ); + p_client = NULL; + vlc_mutex_unlock( &p_rtcp->object_lock ); } return VLC_SUCCESS; } -static int rtcp_decode_RR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +static int rtcp_decode_RR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { - unsigned int u_ssrc_count; + rtcp_t *p_rtcp = (rtcp_t *) p_this; unsigned int i = 0; - if( !p_rtcp && !p_buffer ) + if( !p_pkt ) return VLC_EGENERIC; msg_Dbg( p_this, "decoding record: RR" ); - p_rtcp->stats.u_RR_received++; - u_ssrc_count = (p_buffer[RTCP_HEADER_LEN] & 0x1f); - msg_Dbg( p_this, "RR received %d, SSRC count %d", p_rtcp->stats.u_RR_received, u_ssrc_count ); - - for( i=0; i < u_ssrc_count; i++ ) + for( i=0; i < p_pkt->u_report; i++ ) { - unsigned char count[4]; - - p_rtcp->stats.u_fract_lost = p_buffer[12+RTCP_HEADER_LEN]; - - count[0] = 0; - count[1] = p_buffer[13+RTCP_HEADER_LEN]; - count[2] = p_buffer[14+RTCP_HEADER_LEN]; - count[3] = p_buffer[15+RTCP_HEADER_LEN]; - - /* FIXME: I don't like the sight of this */ - p_rtcp->stats.u_pkt_lost = ntohl((int)count); - - p_rtcp->stats.u_highest_seq_no = ntohl( p_buffer[16+RTCP_HEADER_LEN] ); - p_rtcp->stats.u_jitter = ntohl( p_buffer[20+RTCP_HEADER_LEN] ); - p_rtcp->stats.u_last_RR = ntohl( p_buffer[24+RTCP_HEADER_LEN] ); - p_rtcp->stats.u_delay_since_last_RR = (mtime_t) ntohl( p_buffer[28+RTCP_HEADER_LEN] ); - - msg_Dbg( p_this, "fract lost %d, packet lost %d, highest seqno %d, jitter %d, last RR %d, delay %lld", - p_rtcp->stats.u_fract_lost, - p_rtcp->stats.u_pkt_lost, - p_rtcp->stats.u_highest_seq_no, - p_rtcp->stats.u_jitter, - p_rtcp->stats.u_last_RR, - p_rtcp->stats.u_delay_since_last_RR ); + rtcp_client_t *p_client = NULL; + uint32_t i_pos = 0; + uint32_t u_ssrc = 0; + int result = 0; + + u_ssrc = bs_read( p_rtcp->bs, 32 ); + + result = p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + { + result = p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + return VLC_ENOMEM; + } + + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = p_rtcp->pp_clients[i_pos]; + + p_client->p_stats->u_RR_received++; + msg_Dbg( p_this, "RR received %d, SSRC %d", + p_client->p_stats->u_RR_received, u_ssrc ); + + p_client->p_stats->u_fraction_lost = bs_read( p_rtcp->bs, 8 ); + p_client->p_stats->u_pkt_lost = bs_read( p_rtcp->bs, 24 ); + p_client->p_stats->u_highest_seq_no = bs_read( p_rtcp->bs, 32 ); + p_client->p_stats->u_jitter = bs_read( p_rtcp->bs, 32 ); + p_client->p_stats->u_last_SR = bs_read( p_rtcp->bs, 32 ); + p_client->p_stats->u_delay_since_last_SR = (mtime_t) bs_read( p_rtcp->bs, 32 ); + + /* Magic numbers are from RFC 3550 page 92 + * 1.0/16.0 = 0.0625 and + * 15.0/16.0 = 0.9375 + */ + p_client->p_stats->u_avg_pkt_size = (uint64_t) + ( (double)((double)p_client->p_stats->u_sent_pkt_size * (double)(0.0625)) + + ((double)(0.9375) * p_client->p_stats->u_avg_pkt_size) ); + + msg_Dbg( p_this, "fract lost %d, packet lost %d, highest seqno %d, " + "jitter %d, last SR %d, delay %lld", + p_client->p_stats->u_fraction_lost, + p_client->p_stats->u_pkt_lost, + p_client->p_stats->u_highest_seq_no, + p_client->p_stats->u_jitter, + p_client->p_stats->u_last_SR, + p_client->p_stats->u_delay_since_last_SR ); + p_client = NULL; + vlc_mutex_unlock( &p_rtcp->object_lock ); } return VLC_SUCCESS; } -static int rtcp_decode_SDES( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +static int rtcp_decode_SDES( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { - if( !p_rtcp && !p_buffer ) + rtcp_t *p_rtcp = (rtcp_t *) p_this; + unsigned int i = 0; + + if( !p_pkt ) return VLC_EGENERIC; msg_Dbg( p_this, "decoding record: SDES" ); - switch( p_buffer[8] ) + for( i = 0; i < p_pkt->u_report; i++ ) { - case RTCP_INFO_CNAME: - p_rtcp->stats.l_dest_SSRC = ntohs( (int)(p_buffer[4+RTCP_HEADER_LEN]) ); - break; - case RTCP_INFO_NAME: - case RTCP_INFO_EMAIL: - case RTCP_INFO_PHONE: - case RTCP_INFO_LOC: - case RTCP_INFO_TOOL: - case RTCP_INFO_NOTE: - case RTCP_INFO_PRIV: /* ignoring these */ - break; - default: - return VLC_EGENERIC; + rtcp_client_t *p_client = NULL; + uint32_t i_pos = 0; + uint32_t u_ssrc = 0; + uint8_t u_item = 0; + uint8_t u_length = 0; + int i = 0; + int result = 0; + + u_ssrc = bs_read( p_rtcp->bs, 32 ); + + result = p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + { + result = p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + return VLC_ENOMEM; + } + + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = p_rtcp->pp_clients[i_pos]; + + u_item = bs_read( p_rtcp->bs, 8 ); + switch( u_item ) + { + case RTCP_SDES_CNAME: + case RTCP_SDES_NAME: + case RTCP_SDES_EMAIL: + case RTCP_SDES_PHONE: + case RTCP_SDES_LOC: + case RTCP_SDES_TOOL: + case RTCP_SDES_NOTE: + { + char psz_name[255]; + + u_length = bs_read( p_rtcp->bs, 8 ); + for( i = 0 ; i < u_length; i++ ) + { + psz_name[i] = bs_read( p_rtcp->bs, 8 ); + } + SDES_client_item_add( p_client, u_item, psz_name ); + } + break; + + case RTCP_SDES_PRIV: /* ignoring these */ + { + uint8_t u_prefix_len = 0; + uint8_t u_length = 0; + char psz_prefix_name[255]; + char psz_name[255]; + + u_length = bs_read( p_rtcp->bs, 8 ); + u_prefix_len = bs_read( p_rtcp->bs, 8 ); + if( u_prefix_len > 254 ) + u_prefix_len = 254; + + for( i=0 ; i < u_prefix_len; i++ ) + { + psz_prefix_name[i] = bs_read( p_rtcp->bs, 8 ); + } + psz_prefix_name[255] = '\0'; + SDES_client_item_add( p_client, u_item, psz_prefix_name ); + + for( i=0 ; i < u_length; i++ ) + { + psz_name[i] = bs_read( p_rtcp->bs, 8 ); + } + psz_name[255] = '\0'; + SDES_client_item_add( p_client, u_item, psz_name ); + } + break; + + default: + return VLC_EGENERIC; + } + /* Magic numbers are from RFC 3550 page 92 + * 1.0/16.0 = 0.0625 and + * 15.0/16.0 = 0.9375 + */ + p_client->p_stats->u_avg_pkt_size = (uint64_t) + ( (double)((double)p_client->p_stats->u_sent_pkt_size * (double)(0.0625)) + + ((double)(0.9375) * p_client->p_stats->u_avg_pkt_size) ); + + p_client = NULL; + vlc_mutex_unlock( &p_rtcp->object_lock ); } return VLC_SUCCESS; } -static int rtcp_decode_BYE( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +static int rtcp_decode_BYE( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { - if( !p_rtcp && !p_buffer ) + rtcp_t *p_rtcp = (rtcp_t *) p_this; + uint32_t u_ssrc = 0; + uint8_t u_length = 0; + int i = 0; + + if( !p_pkt ) return VLC_EGENERIC; msg_Dbg( p_this, "decoding record: BYE" ); + + u_ssrc = bs_read( p_rtcp->bs, 32 ); + p_rtcp->pf_del_client( p_this, u_ssrc ); + u_length = p_pkt->u_length-1; + for( i = 0 ; i < u_length; i++ ) + { + u_ssrc = bs_read( p_rtcp->bs, 8 ); + p_rtcp->pf_del_client( p_this, u_ssrc ); + } return VLC_SUCCESS; } -static int rtcp_decode_APP( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +static int rtcp_decode_APP( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { - if( !p_rtcp && !p_buffer ) + rtcp_t *p_rtcp = (rtcp_t *) p_this; + rtcp_client_t *p_client = NULL; + char psz_name[4]; + char* psz_data = NULL; + uint32_t u_ssrc = 0; + uint32_t i_pos = 0; + uint32_t i = 0; + int result = 0; + + if( !p_pkt ) return VLC_EGENERIC; msg_Dbg( p_this, "decoding record: APP" ); + + u_ssrc = bs_read( p_rtcp->bs, 32 ); + + result = p_rtcp->pf_find_client( p_this, u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + { + result = p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + return VLC_ENOMEM; + } + + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = p_rtcp->pp_clients[i_pos]; + + for( i = 0 ; i < 4; i++ ) + { + psz_name[i] = bs_read( p_rtcp->bs, 8 ); + } + psz_name[4] = '\0'; + + p_pkt->u_payload_type = RTCP_APP; + p_pkt->report.app.psz_prefix = strdup( psz_name ); + p_pkt->report.app.u_prefix_len = 4; + p_pkt->u_length -= 4; + + psz_data = (char *) malloc( p_pkt->u_length ); + if( !psz_data ) { + vlc_mutex_unlock( &p_rtcp->object_lock ); + return VLC_EGENERIC; + } + + for( i = 0; i < p_pkt->u_length; i-- ) + { + psz_data[i] = bs_read( p_rtcp->bs, 8 ); + } + psz_data[p_pkt->u_length] = '\0'; + + p_pkt->report.app.psz_data = strdup( psz_data ); + p_pkt->report.app.u_length = p_pkt->u_length; + + p_client = NULL; + vlc_mutex_unlock( &p_rtcp->object_lock ); + /* Just ignore this packet */ return VLC_SUCCESS; } @@ -186,47 +610,90 @@ static int rtcp_decode_APP( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buf /* Decode RTCP packet * Decode incoming RTCP packet and inspect the records types. */ -int rtcp_decode( vlc_object_t *p_this, rtcp_t *p_rtcp, block_t *p_block ) +int rtcp_pkt_decode( vlc_object_t *p_this, rtcp_pkt_t *p_pkt, block_t *p_block ) { - uint8_t *p_buffer = NULL; - unsigned int i_length = 0; - unsigned int i; + rtcp_t *p_rtcp = (rtcp_t *) p_this; - if( !p_rtcp && !p_block ) + if( !p_pkt && !p_block ) return VLC_EGENERIC; - i_length = p_block->i_buffer; - p_buffer = p_block->p_buffer; + bs_init( p_rtcp->bs, p_block->p_buffer, p_block->i_buffer ); - for( i=0; iu_version = bs_read( p_rtcp->bs, 2 ); + p_pkt->b_padding = bs_read( p_rtcp->bs, 1 ) ? VLC_TRUE : VLC_FALSE; + p_pkt->u_report = bs_read( p_rtcp->bs, 5 ); + p_pkt->u_payload_type = bs_read( p_rtcp->bs, 8 ); + p_pkt->u_length = bs_read( p_rtcp->bs, 16 ); + + if( p_pkt->u_payload_type != RTCP_SDES ) + p_pkt->u_ssrc = bs_read( p_rtcp->bs, 32 ); + + msg_Dbg( p_this, "New RTCP packet: version %d, padding %s, count %d, " + "type %d, length %d, SSRC %d", + p_pkt->u_version, + p_pkt->b_padding ? "true" : "false", + p_pkt->u_report, + p_pkt->u_payload_type, + p_pkt->u_length, + p_pkt->u_ssrc ); + + while( !bs_eof( p_rtcp->bs ) ) { - p_rtcp->u_count = p_buffer[i] & 0xF8; - p_rtcp->u_version = p_buffer[i] & 0x03; - p_rtcp->u_payload_type = p_buffer[i+1]; - p_rtcp->u_length = (p_buffer[i+2]<<8) + p_buffer[i+3]; - msg_Dbg( p_this, "New RTCP packet: count %d, version %d, type %d, lenght %d", - p_rtcp->u_count, - p_rtcp->u_version, - p_rtcp->u_payload_type, - p_rtcp->u_length ); - - switch( p_rtcp->u_payload_type ) + uint32_t i_pos = 0; + + switch( p_pkt->u_payload_type ) { case RTCP_SR: - rtcp_decode_SR( p_this, p_rtcp, p_buffer ); + if( p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ) == VLC_EGENERIC ) + p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + rtcp_decode_SR( p_this, p_pkt ); break; + case RTCP_RR: - rtcp_decode_RR( p_this, p_rtcp, p_buffer ); + if( p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ) == VLC_EGENERIC ) + p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + rtcp_decode_RR( p_this, p_pkt ); break; + case RTCP_SDES: - rtcp_decode_SDES( p_this, p_rtcp, p_buffer ); + if( p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ) == VLC_EGENERIC ) + p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + rtcp_decode_SDES( p_this, p_pkt ); break; + case RTCP_BYE: - rtcp_decode_BYE( p_this, p_rtcp, p_buffer ); + rtcp_decode_BYE( p_this, p_pkt ); +#if 0 + if( p_rtcp->pf_find_sender( p_this, pkt->u_ssrc, &i_pos ) == VLC_EGENERIC ) + p_rtcp->pf_del_sender( p_this, p_pkt->u_ssrc ); +#endif + if( p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ) == VLC_EGENERIC ) + p_rtcp->pf_del_client( p_this, p_pkt->u_ssrc ); + + if( p_rtcp->u_active < p_rtcp->u_members ) + { + rtcp_event_t event = EVENT_BYE; + + p_rtcp->i_next_date = p_rtcp->i_date + + (mtime_t) ( (p_rtcp->u_active / p_rtcp->u_members) * + (p_rtcp->i_next_date - p_rtcp->i_date) ); + p_rtcp->i_last_date = p_rtcp->i_date - + (mtime_t) + ( (mtime_t)(p_rtcp->u_active / p_rtcp->u_members) * + (p_rtcp->i_date - p_rtcp->i_last_date) ); + /* schedule for next period */ + rtcp_schedule( VLC_OBJECT(p_rtcp), p_rtcp->i_next_date, event ); + p_rtcp->u_members = p_rtcp->u_active; + } + else p_rtcp->u_members++; break; + case RTCP_APP: - rtcp_decode_APP( p_this, p_rtcp, p_buffer ); + if( p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ) == VLC_EGENERIC ) + p_rtcp->pf_add_client( p_this, p_pkt->u_ssrc, &i_pos ); + rtcp_decode_APP( p_this, p_pkt ); break; + default: return VLC_EGENERIC; } @@ -237,167 +704,328 @@ int rtcp_decode( vlc_object_t *p_this, rtcp_t *p_rtcp, block_t *p_block ) /* * Create RTCP records for reporting to server. */ -block_t *rtcp_encode( vlc_object_t *p_this, int type ) +rtcp_pkt_t *rtcp_pkt_new( vlc_object_t *p_this, int type ) { - rtcp_t *p_rtcp = NULL; - block_t *p_block = NULL; + rtcp_pkt_t *p_pkt = NULL; - p_rtcp = (rtcp_t *) malloc( sizeof( rtcp_t ) ); - if( !p_rtcp ) + p_pkt = (rtcp_pkt_t *) malloc( sizeof( rtcp_pkt_t ) ); + if( !p_pkt ) return NULL; - memset( p_rtcp, 0 , sizeof( rtcp_t ) ); - p_rtcp->u_version = 2; - p_rtcp->u_payload_type = type; - p_rtcp->u_length = RTCP_HEADER_LEN; + + memset( p_pkt, 0 , sizeof( rtcp_pkt_t ) ); + p_pkt->u_version = 2; + p_pkt->u_payload_type = type; + p_pkt->u_length = RTCP_HEADER_LEN; switch( type ) { - case RTCP_SR: p_rtcp->u_length += sizeof(rtcp_SR); break; - case RTCP_RR: p_rtcp->u_length += sizeof(rtcp_RR); break; + case RTCP_SR: + p_pkt->u_length += sizeof(rtcp_SR_t); + break; + case RTCP_RR: + p_pkt->u_length += sizeof(rtcp_RR_t); + break; case RTCP_SDES: - p_rtcp->u_length += sizeof(rtcp_SDES) + strlen(p_rtcp->report.sdes.p_data); + p_pkt->u_length += sizeof(rtcp_SDES_t); + if( p_pkt->report.sdes.pp_items ) + p_pkt->u_length += p_pkt->report.sdes.u_items; + break; + case RTCP_BYE: + p_pkt->u_length += sizeof(rtcp_BYE_t); + break; + case RTCP_APP: + p_pkt->u_length += sizeof(rtcp_APP_t); break; - case RTCP_BYE: p_rtcp->u_length += sizeof(rtcp_BYE); break; - case RTCP_APP: /* ignore: p_rtcp->u_length += sizeof(rtcp_APP); */ break; + default: + free(p_pkt); + return NULL; } + return p_pkt; +} - /* FIXME: Maybe this should be a buffer pool instead */ - p_block = block_New( p_this, p_rtcp->u_length ); - return p_block; +void rtcp_pkt_del( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) +{ + if( !p_pkt ) + return; + + switch( p_pkt->u_payload_type ) + { + case RTCP_SR: + case RTCP_RR: + break; + case RTCP_SDES: + if( p_pkt->report.sdes.pp_items ) + { + uint32_t i = 0; + + for( i = 0; i < p_pkt->report.sdes.u_items; i++ ) + { + rtcp_SDES_item_t *p_old = + p_pkt->report.sdes.pp_items[i]; + REMOVE_ELEM( p_pkt->report.sdes.pp_items, + p_pkt->report.sdes.u_items, i ); + p_pkt->report.sdes.u_items--; + if( p_old->psz_data ) + free( p_old->psz_data ); + free( p_old ); + } + } + break; + case RTCP_BYE: + break; + case RTCP_APP: + if( p_pkt->report.app.psz_prefix ) + free( p_pkt->report.app.psz_prefix ); + if( p_pkt->report.app.psz_data ) + free( p_pkt->report.app.psz_data ); + break; + default: + msg_Err( p_this, "unknown RTCP packet type %d: " + "possible leaking of memory.", + p_pkt->u_payload_type ); + break; + } + free( p_pkt ); } -static block_t *rtcp_encode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp ) +block_t *rtcp_encode_SR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { + rtcp_t *p_rtcp = (rtcp_t *) p_this; mtime_t ntp_time; bs_t bits, *s = &bits; block_t *p_block = NULL; + rtcp_stats_t *p_stats = NULL; + rtcp_client_t *p_client = NULL; + uint32_t i_pos = 0; + int result = 0; + + if( p_pkt->u_payload_type != RTCP_SR ) + return NULL; /* FIXME: Maybe this should be a buffer pool instead */ - p_block = block_New( p_this, p_rtcp->u_length ); + p_block = block_New( p_this, p_pkt->u_length ); if( !p_block ) return NULL; bs_init( s, p_block->p_buffer, p_block->i_buffer ); /* Fill in header */ - bs_write( s, 5, p_rtcp->u_count ); + bs_write( s, 2, p_pkt->u_version ); bs_write( s, 1, 0 ); /* padding */ - bs_write( s, 2, p_rtcp->u_version ); - bs_write( s, 8, p_rtcp->u_payload_type ); - bs_write( s, 16, p_rtcp->u_length ); - - /* fill in record */ - bs_write( s, 32, htonl( p_rtcp->report.sr.u_ssrc ) ); + bs_write( s, 5, p_pkt->u_report ); + bs_write( s, 8, p_pkt->u_payload_type ); + bs_write( s, 16, p_pkt->u_length ); + bs_write( s, 32, p_pkt->u_ssrc ); + /* sender info */ ntp_time = mdate(); - bs_write( s, 32, htonl( ((unsigned int)(ntp_time>>32)) ) ); /* ntp_timestampH */ - bs_write( s, 32, htonl( ((unsigned int)ntp_time)) );/* ntp_timestampL */ + bs_write( s, 32, ((unsigned int)(ntp_time>>32)) ); /* ntp_timestampH */ + bs_write( s, 32, ((unsigned int)ntp_time) );/* ntp_timestampL */ /* FIXME: Make sure to generate a good RTP server timestamp. - p_rtcp->report.sr.rtp_timestamp = htonl( + p_pkt->report.sr.rtp_timestamp = htonl( (unsigned int) ((double)ntp_time.tv_sec + (double)ntp_time.tv_usec/1000000.) * p_mux->rate + session->start_rtptime ); */ - bs_write( s, 32, htonl( p_rtcp->report.sr.rtp_timestamp ) ); - bs_write( s, 32, htonl( p_rtcp->report.sr.u_pkt_count ) ); - bs_write( s, 32, htonl( p_rtcp->report.sr.u_octet_count ) ); + bs_write( s, 32, p_pkt->report.sr.rtp_timestamp ); + bs_write( s, 32, p_pkt->report.sr.u_pkt_count ); + bs_write( s, 32, p_pkt->report.sr.u_octet_count ); + + /* report block */ + result = p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + { + msg_Err( p_this, "SR: SSRC identifier doesn't exists", p_pkt->u_ssrc ); + free( p_block ); + return NULL; + } + + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = p_rtcp->pp_clients[i_pos]; + p_stats = p_client->p_stats; + if( !p_stats ) { + msg_Err( p_this, "SR: SSRC identifier doesn't exists", p_pkt->u_ssrc ); + free( p_block ); + return NULL; + } + bs_write( s, 32, p_stats->l_dest_SSRC ); + bs_write( s, 8, p_stats->u_fraction_lost ); + bs_write( s, 24, p_stats->u_pkt_lost ); + bs_write( s, 32, p_stats->u_highest_seq_no ); + bs_write( s, 32, p_stats->u_jitter ); + bs_write( s, 32, p_stats->u_last_SR ); + bs_write( s, 32, p_stats->u_delay_since_last_SR ); + + p_client = NULL; + vlc_mutex_unlock( &p_rtcp->object_lock ); + + /* possible SR extension */ return p_block; } -static block_t *rtcp_encode_RR( vlc_object_t *p_this, rtcp_t *p_rtcp ) +block_t *rtcp_encode_RR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { + rtcp_t *p_rtcp = (rtcp_t *) p_this; bs_t bits, *s = &bits; block_t *p_block = NULL; + rtcp_stats_t *p_stats = NULL; + rtcp_client_t *p_client = NULL; + uint32_t i_pos = 0; + int result = 0; + + if( p_pkt->u_payload_type != RTCP_RR ) + return NULL; /* FIXME: Maybe this should be a buffer pool instead */ - p_block = block_New( p_this, p_rtcp->u_length ); + p_block = block_New( p_this, p_pkt->u_length ); if( !p_block ) return NULL; bs_init( s, p_block->p_buffer, p_block->i_buffer ); /* Fill in header */ - bs_write( s, 5, p_rtcp->u_count ); + bs_write( s, 2, p_pkt->u_version ); bs_write( s, 1, 0 ); /* padding */ - bs_write( s, 2, p_rtcp->u_version ); - bs_write( s, 8, p_rtcp->u_payload_type ); - bs_write( s, 16, (p_rtcp->u_length >> 2) -1 ); + bs_write( s, 5, p_pkt->u_report ); + bs_write( s, 8, p_pkt->u_payload_type ); + bs_write( s, 16, p_pkt->u_length ); + bs_write( s, 32, p_pkt->u_ssrc ); + + /* report block */ + result = p_rtcp->pf_find_client( p_this, p_pkt->u_ssrc, &i_pos ); + if( result == VLC_EGENERIC ) + { + msg_Err( p_this, "RR: SSRC identifier doesn't exists", p_pkt->u_ssrc ); + free( p_block ); + return NULL; + } - /* fill in record */ - bs_write( s, 32, htonl( p_rtcp->report.rr.u_ssrc ) ); + vlc_mutex_lock( &p_rtcp->object_lock ); + p_client = p_rtcp->pp_clients[i_pos]; + p_stats = p_client->p_stats; + if( !p_stats ) { + msg_Err( p_this, "RR: SSRC identifier doesn't exists", p_pkt->u_ssrc ); + free( p_block ); + return NULL; + } + bs_write( s, 32, p_stats->l_dest_SSRC ); + bs_write( s, 8, p_stats->u_fraction_lost ); + bs_write( s, 24, p_stats->u_pkt_lost ); + bs_write( s, 32, p_stats->u_highest_seq_no ); + bs_write( s, 32, p_stats->u_jitter ); + bs_write( s, 32, p_stats->u_last_RR ); + bs_write( s, 32, p_stats->u_delay_since_last_RR ); + + p_client = NULL; + vlc_mutex_unlock( &p_rtcp->object_lock ); + + /* possible RR extension */ return p_block; } -static block_t *rtcp_encode_SDES( vlc_object_t *p_this, rtcp_t *p_rtcp ) +block_t *rtcp_encode_SDES( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ) { bs_t bits, *s = &bits; block_t *p_block = NULL; - char *p_hostname = strdup("hostname"); - int i_length; - int i; + uint32_t i_chunks; + + if( p_pkt->u_payload_type != RTCP_SDES ) + return NULL; /* FIXME: Maybe this should be a buffer pool instead */ - p_block = block_New( p_this, p_rtcp->u_length ); + p_block = block_New( p_this, p_pkt->u_length ); if( !p_block ) return NULL; bs_init( s, p_block->p_buffer, p_block->i_buffer ); /* Fill in header */ - bs_write( s, 5, p_rtcp->u_count ); + bs_write( s, 2, p_pkt->u_version ); bs_write( s, 1, 0 ); /* padding */ - bs_write( s, 2, p_rtcp->u_version ); - bs_write( s, 8, p_rtcp->u_payload_type ); - bs_write( s, 16, (p_rtcp->u_length >> 2) -1 ); + bs_write( s, 5, p_pkt->u_report ); /* Number of SSRC/CSRC chunks */ + bs_write( s, 8, p_pkt->u_payload_type ); + bs_write( s, 16, p_pkt->u_length ); + bs_write( s, 32, p_pkt->u_ssrc ); /* fill in record */ - bs_write( s, 32,htonl( p_rtcp->report.sdes.u_ssrc ) ); - bs_write( s, 8, htonl( p_rtcp->report.sdes.u_attr_name ) ); - bs_write( s, 8, htonl( p_rtcp->report.sdes.u_length ) ); - - i_length = strlen(p_hostname); - bs_write( s, 8, i_length ); - for( i=0; iu_report; i_chunks++ ) { - bs_write( s, 8, p_hostname[i] ); + uint32_t i_item; + + for( i_item = 0 ; i_item < p_pkt->report.sdes.u_items; i_item++ ) + { + uint32_t i_count = strlen( p_pkt->report.sdes.pp_items[i_item]->psz_data ); + uint8_t u_octet = i_count / 8; /* Octect count ??*/ + rtcp_SDES_item_t *p_item = p_pkt->report.sdes.pp_items[i_item]; + uint32_t i_pos, i_pad, i_padding; + + bs_write( s, 8, p_item->u_type ); + bs_write( s, 8, u_octet ); + + for( i_pos = 0; i_pos < i_count; i_pos++ ) + { + /* FIXME: must be UTF 8 encoded */ + bs_write( s, 8, p_item->psz_data[i_pos] ); + } + + /* do we need padding to 32 bit boundary? */ + i_padding = 0; + if( ((i_count + 2) % 4) != 0 ) + i_padding = (i_count + 2) - (((i_count + 2) % 4) << 2); + for( i_pad = 0; i_pad < i_padding; i_pad++ ) + { + bs_write( s, 8, 0 ); + } + } } - free(p_hostname); return p_block; } -static block_t *rtcp_encode_BYE( vlc_object_t *p_this, rtcp_t *p_rtcp ) +block_t *rtcp_encode_BYE( vlc_object_t *p_this, rtcp_pkt_t *p_pkt, char *psz_reason ) { bs_t bits, *s = &bits; block_t *p_block = NULL; - char *p_reason = strdup( "Stream ended." ); - int i_length; - int i; + uint32_t i_count = strlen( psz_reason ); + uint8_t u_octet = i_count / 8; /* Octect count ??*/ + uint32_t i_pos, i_pad, i_padding; + + if( p_pkt->u_payload_type != RTCP_BYE ) + return NULL; /* FIXME: Maybe this should be a buffer pool instead */ - p_block = block_New( p_this, p_rtcp->u_length ); + p_block = block_New( p_this, p_pkt->u_length ); if( !p_block ) return NULL; bs_init( s, p_block->p_buffer, p_block->i_buffer ); /* Fill in header */ - bs_write( s, 5, p_rtcp->u_count ); + bs_write( s, 2, p_pkt->u_version ); bs_write( s, 1, 0 ); /* padding */ - bs_write( s, 2, p_rtcp->u_version ); - bs_write( s, 8, p_rtcp->u_payload_type ); - bs_write( s, 16, (p_rtcp->u_length >> 2) -1 ); + bs_write( s, 5, p_pkt->u_report ); /* Number of SSRC/CSRC chunks */ + bs_write( s, 8, p_pkt->u_payload_type ); + bs_write( s, 16, p_pkt->u_length ); + bs_write( s, 32, p_pkt->u_ssrc ); - /* fill in record */ - bs_write( s, 32, htonl( p_rtcp->report.sdes.u_ssrc ) ); + /* Give reason for leaving */ + //FIXME: bs_write( s, 8, p_item->u_type ); + bs_write( s, 8, u_octet ); + + for( i_pos = 0; i_pos < i_count; i_pos++ ) + { + /* FIXME: must be UTF 8 encoded */ + bs_write( s, 8, psz_reason[i_pos] ); + } - i_length = strlen(p_reason); - bs_write( s, 8, i_length ); - for( i=0; iobjec_lock ) held */ +int rtcp_find_client( vlc_object_t *p_this, uint32_t u_ssrc, uint32_t *i_pos ); + + +/** + * rtcp_cleanup_clients - Permanently remove clients from the list + * Permanently delete struct rtcp_client_t from member list when it is there + * time to be removed. RFC 3550 recommends a grace period of five times the + * RTCP packet send/receive interval before permanent removal of session. This + * is to ignore out of order packets for the same SSRC that arrive after the + * BYE report for that SSRC has been received. + * Arguments: + * \param p_this VLC_OBJECT_T of type rtcp_t + */ +int rtcp_cleanup_clients( vlc_object_t *p_this ); +//VLC_EXPORT( int, rtcp_cleanup_clients, ( vlc_object_t * ) ); /** - * Decode RTCP packet + * rtcp_pkt_decode - Decode RTCP packet * Decode incoming RTCP packet and inspect the records types. + * Arguments: + * \param p_this + * \param p_pkt + * \param p_block + */ +int rtcp_pkt_decode( vlc_object_t *p_this, rtcp_pkt_t *p_pkt, block_t *p_block ); +//VLC_EXPORT( int, rtcp_decode, ( rtcp_pkt_t *, block_t * ) ); + +/** + * rtcp_pkt_new - Encode RTCP packet + * Create a new RTCP packet of type 'type' + * Arguments + * \param type type of RTCP packet @see + */ +rtcp_pkt_t *rtcp_pkt_new( vlc_object_t *p_this, int type ); +//VLC_EXPORT( block_t* , rtcp_encode, ( vlc_object_t *, int ) ); +void rtcp_pkt_del( vlc_object_t *p_this, rtcp_pkt_t *pkt ); + +block_t *rtcp_encode_SR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +block_t *rtcp_encode_RR( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +block_t *rtcp_encode_SDES( vlc_object_t *p_this, rtcp_pkt_t *p_pkt ); +block_t *rtcp_encode_BYE( vlc_object_t *p_this, rtcp_pkt_t *p_pkt, char *psz_reason ); + +/** + * rtcp_interval + * Calculate the interval at which RTCP compound packets are going to be + * sent or received. + * Arguments: + * \param p_this VLC_OBJECT of type rtcp_t + * \param u_bandwith bandwidth of RTP connection + * \param b_sender are we the sender or the receiver + * \param b_first the first time this function is called use only half + * of the initial waiting time + */ +uint64_t rtcp_interval( vlc_object_t *p_this, uint64_t u_bandwidth, + vlc_bool_t b_sender, vlc_bool_t b_first ); + +/** + * rtcp_expire + * Decides to sent an RTCP report or a BYE record + * \param p_this VLC_OBJECT of type rtcp_t + * \param u_bandwith bandwidth of RTP connection + * \param rtcp_event type of event received + * \param b_sender are we the sender or the receiver + * \param *b_first the first time this function is called use only half + * of the initial waiting time. If b_first is VLC_TRUE, then + * it will return *b_first = VLC_FALSE; + */ +void rtcp_expire( vlc_object_t *p_this, rtcp_event_t rtcp_event, + uint64_t u_bandwidth, vlc_bool_t b_sender, vlc_bool_t *b_first ); + +/** + * rtcp_received + * Determine what to do on the received Sender Report, decode it + * or leave the channel (BYE record). + * \param p_this VLC_OBJECT of type rtcp_t + * \param p_pkt RTCP packet that was received + * \param rtcp_event type of event received */ -int rtcp_decode( vlc_object_t *p_this, rtcp_t *p_rtcp, block_t *p_block ); -//VLC_EXPORT( int, rtcp_decode, ( rtcp_t *p_rtcp, block_t *p_block ) ); -block_t *rtcp_encode( vlc_object_t *p_this, int type ); +void rtcp_received( vlc_object_t *p_this, rtcp_pkt_t *pkt, + rtcp_event_t rtcp_event ); #endif /* RTCP_H */