Commit e2afd14a authored by Jean-Paul Saman's avatar Jean-Paul Saman

Structures and functions for RTCP support. BIG FAT WARNING >>>> This code is...

Structures and functions for RTCP support. BIG FAT WARNING >>>> This code is untested <<<< but the request for it is high.
parent 37c8f98c
/*****************************************************************************
* rtcp.c: RTP/RTCP source file
*****************************************************************************
* Copyright (C) 2005 M2X
*
* $Id$
*
* Authors: Jean-Paul Saman <jpsaman #_at_# videolan dot org>
*
* 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 <netinet/in.h>
#include <sys/time.h>
#include <sys/time.h>
#include <vlc/vlc.h>
#include <vlc_bits.h>
#include <vlc_block.h>
#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; i<i_length; ++i )
{
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 )
{
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; i<i_length; i++ )
{
bs_write( s, 8, p_hostname[i] );
}
free(p_hostname);
return p_block;
}
static block_t *rtcp_encode_BYE( vlc_object_t *p_this, rtcp_t *p_rtcp )
{
bs_t bits, *s = &bits;
block_t *p_block = NULL;
char *p_reason = strdup( "Stream ended." );
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 ) );
i_length = strlen(p_reason);
bs_write( s, 8, i_length );
for( i=0; i<i_length; i++ )
{
bs_write( s, 8, p_reason[i] );
}
free(p_reason);
return p_block;
}
/*****************************************************************************
* rtcp.h: RTP/RTCP headerfile
*****************************************************************************
* Copyright (C) 2005 M2X
*
* $Id$
*
* Authors: Jean-Paul Saman <jpsaman #_at_# videolan dot org>
*
* 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 */
/*****************************************************************************
* rtp.h: RTP/RTCP headerfile
*****************************************************************************
* Copyright (C) 2005 M2X
*
* $Id$
*
* Authors: Jean-Paul Saman <jpsaman #_at_# videolan dot org>
*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment