From: Jean-Paul Saman Date: Fri, 3 Mar 2006 21:43:48 +0000 (+0000) Subject: Structures and functions for RTCP support. BIG FAT WARNING >>>> This code is untested... X-Git-Tag: 0.9.0-test0~12065 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=e2afd14a3facd197464e43b3dd3e8f76c173741f;p=vlc Structures and functions for RTCP support. BIG FAT WARNING >>>> This code is untested <<<< but the request for it is high. --- diff --git a/modules/mux/rtp/rtcp.c b/modules/mux/rtp/rtcp.c new file mode 100644 index 0000000000..126c6d9561 --- /dev/null +++ b/modules/mux/rtp/rtcp.c @@ -0,0 +1,403 @@ +/***************************************************************************** + * rtcp.c: RTP/RTCP source file + ***************************************************************************** + * Copyright (C) 2005 M2X + * + * $Id$ + * + * Authors: Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#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 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 ); + +static int rtcp_decode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +{ + unsigned int u_ssrc_count; + unsigned int i = 0; + + if( !p_rtcp && !p_buffer ) + 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++ ) + { + 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 ); + } + return VLC_SUCCESS; +} + +static int rtcp_decode_RR( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +{ + unsigned int u_ssrc_count; + unsigned int i = 0; + + if( !p_rtcp && !p_buffer ) + 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++ ) + { + 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 ); + } + return VLC_SUCCESS; +} + +static int rtcp_decode_SDES( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +{ + if( !p_rtcp && !p_buffer ) + return VLC_EGENERIC; + + msg_Dbg( p_this, "decoding record: SDES" ); + + switch( p_buffer[8] ) + { + 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; + } + return VLC_SUCCESS; +} + +static int rtcp_decode_BYE( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +{ + if( !p_rtcp && !p_buffer ) + return VLC_EGENERIC; + + msg_Dbg( p_this, "decoding record: BYE" ); + return VLC_SUCCESS; +} + +static int rtcp_decode_APP( vlc_object_t *p_this, rtcp_t *p_rtcp, uint8_t *p_buffer ) +{ + if( !p_rtcp && !p_buffer ) + return VLC_EGENERIC; + + msg_Dbg( p_this, "decoding record: APP" ); + /* Just ignore this packet */ + return VLC_SUCCESS; +} + +/* 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 ) +{ + uint8_t *p_buffer = NULL; + unsigned int i_length = 0; + unsigned int i; + + if( !p_rtcp && !p_block ) + return VLC_EGENERIC; + + i_length = p_block->i_buffer; + p_buffer = p_block->p_buffer; + + for( i=0; iu_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 ) + { + case RTCP_SR: + rtcp_decode_SR( p_this, p_rtcp, p_buffer ); + break; + case RTCP_RR: + rtcp_decode_RR( p_this, p_rtcp, p_buffer ); + break; + case RTCP_SDES: + rtcp_decode_SDES( p_this, p_rtcp, p_buffer ); + break; + case RTCP_BYE: + rtcp_decode_BYE( p_this, p_rtcp, p_buffer ); + break; + case RTCP_APP: + rtcp_decode_APP( p_this, p_rtcp, p_buffer ); + break; + default: + return VLC_EGENERIC; + } + } + return VLC_SUCCESS; +} + +/* + * Create RTCP records for reporting to server. + */ +block_t *rtcp_encode( vlc_object_t *p_this, int type ) +{ + rtcp_t *p_rtcp = NULL; + block_t *p_block = NULL; + + p_rtcp = (rtcp_t *) malloc( sizeof( rtcp_t ) ); + if( !p_rtcp ) + 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; + + 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_SDES: + p_rtcp->u_length += sizeof(rtcp_SDES) + strlen(p_rtcp->report.sdes.p_data); + break; + case RTCP_BYE: p_rtcp->u_length += sizeof(rtcp_BYE); break; + case RTCP_APP: /* ignore: p_rtcp->u_length += sizeof(rtcp_APP); */ break; + } + + /* FIXME: Maybe this should be a buffer pool instead */ + p_block = block_New( p_this, p_rtcp->u_length ); + return p_block; +} + +static block_t *rtcp_encode_SR( vlc_object_t *p_this, rtcp_t *p_rtcp ) +{ + mtime_t ntp_time; + bs_t bits, *s = &bits; + block_t *p_block = NULL; + + /* FIXME: Maybe this should be a buffer pool instead */ + p_block = block_New( p_this, p_rtcp->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, 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 ) ); + + 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 */ + + /* FIXME: Make sure to generate a good RTP server timestamp. + p_rtcp->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 ) ); + + return p_block; +} + +static block_t *rtcp_encode_RR( vlc_object_t *p_this, rtcp_t *p_rtcp ) +{ + bs_t bits, *s = &bits; + block_t *p_block = NULL; + + /* FIXME: Maybe this should be a buffer pool instead */ + p_block = block_New( p_this, p_rtcp->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, 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 ); + + /* fill in record */ + bs_write( s, 32, htonl( p_rtcp->report.rr.u_ssrc ) ); + + return p_block; +} + +static block_t *rtcp_encode_SDES( vlc_object_t *p_this, rtcp_t *p_rtcp ) +{ + bs_t bits, *s = &bits; + block_t *p_block = NULL; + char *p_hostname = strdup("hostname"); + int i_length; + int i; + + /* FIXME: Maybe this should be a buffer pool instead */ + p_block = block_New( p_this, p_rtcp->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, 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 ); + + /* 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_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, 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 ); + + /* fill in record */ + bs_write( s, 32, htonl( p_rtcp->report.sdes.u_ssrc ) ); + + i_length = strlen(p_reason); + bs_write( s, 8, i_length ); + for( i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef _RTCP_H +#define _RTCP_H 1 + +/* RTCP packet types */ +#define RTCP_SR 200 +#define RTCP_RR 201 +#define RTCP_SDES 202 +#define RTCP_BYE 203 +#define RTCP_APP 204 +/* End of RTCP packet types */ + +/* RTCP Info */ +#define RTCP_INFO_CNAME 1 +#define RTCP_INFO_NAME 2 +#define RTCP_INFO_EMAIL 3 +#define RTCP_INFO_PHONE 4 +#define RTCP_INFO_LOC 5 +#define RTCP_INFO_TOOL 6 +#define RTCP_INFO_NOTE 7 +#define RTCP_INFO_PRIV 8 +/* End of RTCP Info */ + +#define RTCP_HEADER_LEN 3 + +typedef struct +{ + unsigned int u_ssrc; + unsigned int ntp_timestampH; + unsigned int ntp_timestampL; + unsigned int rtp_timestamp; + unsigned int u_pkt_count; + unsigned int u_octet_count; +} rtcp_SR; + +typedef struct _RTCP_header_RR +{ + unsigned int u_ssrc; +} rtcp_RR; + +typedef struct +{ + unsigned long u_ssrc; + unsigned char u_fract_lost; + unsigned char u_pck_lost[3]; + unsigned int u_highest_seq_no; + unsigned int u_jitter; + unsigned int u_last_SR; + unsigned int u_delay_last_SR; +} rtcp_report_block_SR; + +typedef struct +{ + unsigned int u_ssrc; + unsigned char u_attr_name; + unsigned char u_length; + char *p_data; +} rtcp_SDES; + +typedef struct +{ + unsigned int u_ssrc; + unsigned char u_length; +} rtcp_BYE; + +typedef struct +{ + unsigned char u_length; + char *p_data; +} rtcp_APP; + +/** + * structure rtcp_stats_t + */ +typedef struct +{ + unsigned int u_RR_received; /*< RR records received */ + unsigned int u_SR_received; /*< SR records received */ + unsigned long l_dest_SSRC; /*< SSRC */ + unsigned int u_pkt_count; /*< count of packets received */ + unsigned int u_octet_count; /*< ? */ + unsigned int u_pkt_lost; /*< count of packets lost */ + unsigned int u_fract_lost; /*< count of fractional packets lost */ + unsigned int u_highest_seq_no;/*< highest sequence number found */ + unsigned int u_jitter; /*< jitter calculated */ + unsigned int u_last_SR; /*< last SR record received */ + unsigned int u_last_RR; /*< last RR record received */ + mtime_t u_delay_since_last_SR; /*< delay since last SR record received */ + mtime_t u_delay_since_last_RR; /*< delay since last RR record received */ +} rtcp_stats_t; + +typedef struct { + int fd; /*< socket descriptor of rtcp stream */ + + unsigned int u_count; /*< packet count */ + unsigned int u_version; /*< RTCP version number */ + unsigned int u_length; /*< lenght of packet */ + unsigned int u_payload_type; /*< type of RTCP payload */ + rtcp_stats_t stats; /*< RTCP statistics */ + + union { + rtcp_SR sr; /*< SR record */ + rtcp_RR rr; /*< RR record */ + rtcp_SDES sdes; /*< SDES record */ + rtcp_BYE bye; /*< BYE record */ + rtcp_APP app; /*< APP record */ + } report; + + /*int (*pf_connect)( void *p_userdata, char *p_server, int i_port ); + int (*pf_disconnect)( void *p_userdata ); + int (*pf_read)( void *p_userdata, uint8_t *p_buffer, int i_buffer ); + int (*pf_write)( void *p_userdata, uint8_t *p_buffer, int i_buffer );*/ +} rtcp_t; + + +/** + * 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 ); +//VLC_EXPORT( int, rtcp_decode, ( rtcp_t *p_rtcp, block_t *p_block ) ); +block_t *rtcp_encode( vlc_object_t *p_this, int type ); + +#endif /* RTCP_H */ diff --git a/modules/mux/rtp/rtp.h b/modules/mux/rtp/rtp.h new file mode 100644 index 0000000000..7e5c4cadc3 --- /dev/null +++ b/modules/mux/rtp/rtp.h @@ -0,0 +1,54 @@ +/***************************************************************************** + * rtp.h: RTP/RTCP headerfile + ***************************************************************************** + * Copyright (C) 2005 M2X + * + * $Id$ + * + * Authors: Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef _RTP_H +#define _RTP_H 1 + +#define RTP_HEADER_LEN 12 +#define RTP_SEQ_NUM_SIZE 65536 + +/* + * See RFC 1889 & RFC 2250. + */ +typedef struct { + int fd; /*< socket descriptor of rtp stream */ + + unsigned int u_version; /*< rtp version number */ + unsigned int u_CSRC_count; /*< CSRC count */ + unsigned int u_payload_type; /*< type of RTP payload stream */ + vlc_bool_t b_extension; /*< The header is followed by exactly one header extension */ + unsigned int u_marker; /*< marker field expect 1 */ + unsigned int u_seq_no; /*< sequence number of RTP stream */ + uint32_t i_timestamp; /*< timestamp of stream */ + unsigned int ssrc; /*< stream number is used here. */ + + unsigned int u_ext_length; /*< lenght of extension field */ + + /*int (*pf_connect)( void *p_userdata, char *p_server, int i_port ); + int (*pf_disconnect)( void *p_userdata ); + int (*pf_read)( void *p_userdata, uint8_t *p_buffer, int i_buffer ); + int (*pf_write)( void *p_userdata, uint8_t *p_buffer, int i_buffer );*/ +} rtp_t; + +#endif /* RTP_H */