]> git.sesse.net Git - vlc/commitdiff
Rewrote internal RTCP to support multiple client connections. TODO: rtcp scheduling...
authorJean-Paul Saman <jpsaman@videolan.org>
Tue, 21 Nov 2006 10:46:12 +0000 (10:46 +0000)
committerJean-Paul Saman <jpsaman@videolan.org>
Tue, 21 Nov 2006 10:46:12 +0000 (10:46 +0000)
configure.ac
modules/mux/rtp/Modules.am
modules/mux/rtp/rtcp.c
modules/mux/rtp/rtcp.h

index 9252a4f1ed8e53417a95a2af29cca84e4a22eb10..0714aa74db7326b8751d1d1b207384535ef4a49a 100644 (file)
@@ -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)
index 072cfe1303dff68458f33ac4a11649923a4eb3a9..bd1284df27f28bc78bf5c9e4710ab401ea04440d 100644 (file)
@@ -1 +1 @@
-SOURCES_rtcp =         rtcp.c rtcp.h rtp.h
+SOURCES_rtcp = rtcp.c rtcp.h rtp.h
index 126c6d9561189a1657ec8eb35ddde9841bb61980..2d521a7da8b88d1a3574a9a55935ec026f34b159 100644 (file)
@@ -22,9 +22,9 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  *****************************************************************************/
 
+#include <stdlib.h>
 #include <netinet/in.h>
 #include <sys/time.h>
-#include <sys/time.h>
 
 #include <vlc/vlc.h>
 #include <vlc_bits.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 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; i<i_length; ++i )
+    p_pkt->u_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; i<i_length; i++ )
+    for( i_chunks = 0; i_chunks < p_pkt->u_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; i<i_length; i++ )
+    /* 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, p_reason[i] );
+        bs_write( s, 8, 0 );
     }
-    free(p_reason);
     return p_block;
 }
index 415a47966e43dcf5da44c5e21a7cef47e612d1b0..6b13b47bbcce0e9a46066112783d05ce4190bffe 100644 (file)
 #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 */
+/* SDES type */
+#define RTCP_SDES_CNAME 1
+#define RTCP_SDES_NAME  2
+#define RTCP_SDES_EMAIL 3
+#define RTCP_SDES_PHONE 4
+#define RTCP_SDES_LOC   5
+#define RTCP_SDES_TOOL  6
+#define RTCP_SDES_NOTE  7
+#define RTCP_SDES_PRIV  8
+/* End of SDES type */
 
 #define RTCP_HEADER_LEN 3
 
-typedef struct
+typedef enum rtcp_event_enum
 {
-    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
+    EVENT_BYE,
+    EVENT_REPORT
+
+} rtcp_event_t;
+
+typedef struct
 {
-    unsigned int u_ssrc;
-} rtcp_RR;
+    uint32_t ntp_timestampH;
+    uint32_t ntp_timestampL;
+    uint32_t rtp_timestamp;
+    uint32_t u_pkt_count;
+    uint32_t u_octet_count;
+
+} rtcp_SR_t;
 
 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;
+
+} rtcp_RR_t;
+
+typedef struct SDES_item_t
+{
+    uint32_t       i_index;     /*< index */
+    uint8_t        u_type;      /*< type field */
+    char           *psz_data;    /*< null terminated string */
+
+} rtcp_SDES_item_t;
 
 typedef struct
 {
-    unsigned int  u_ssrc;
-    unsigned char u_attr_name;
-    unsigned char u_length;
-    char          *p_data;
-} rtcp_SDES;
+    uint32_t    u_length;        /*< length of packet */
+    uint32_t    u_items;         /*< number of SDES_items */
+    rtcp_SDES_item_t **pp_items; /*< list of SDES_items */
+
+} rtcp_SDES_t;
 
 typedef struct
 {
-    unsigned int  u_ssrc;
-    unsigned char u_length;
-} rtcp_BYE;
+    uint32_t u_length;        /*< length of packet */
+
+} rtcp_BYE_t;
 
 typedef struct
 {
-    unsigned char u_length;
-    char          *p_data;
-} rtcp_APP;
+    uint32_t u_length;        /*< length of packet */
+    uint32_t u_prefix_len;    /*< length of prefix data */
+    unsigned char *psz_prefix;/*< prefix string (null terminated) */
+    unsigned char *psz_data;  /*< data string (null terminated) */
+
+} rtcp_APP_t;
 
 /**
  * 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;
+    uint32_t  u_RR_received;   /*< RR records received */
+    uint32_t  u_SR_received;   /*< SR records received */
+    uint64_t  l_dest_SSRC;     /*< SSRC of first source */
+    uint32_t  u_pkt_count;     /*< count of packets received */
+    uint32_t  u_octet_count;   /*< ? */
+    uint32_t  u_pkt_lost;      /*< cumulative count of packets lost */
+    uint8_t   u_fraction_lost; /*< count of fractional packets lost */
+    uint32_t  u_highest_seq_no;/*< highest sequence number found */
+    uint32_t  u_jitter;        /*< inter arrival jitter calculated */
+    uint32_t  u_last_SR;       /*< last SR record received */
+    uint32_t  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 */
+
+    uint64_t  u_avg_pkt_size;  /*< average RTCP packet size */
+    uint64_t  u_sent_pkt_size; /*< RTCP packet size sent */
 
-typedef struct {
-    int fd;                 /*< socket descriptor of rtcp stream */
+} rtcp_stats_t;
 
-    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 */
+typedef struct 
+{
+    uint32_t u_version;        /*< RTCP version number */
+    vlc_bool_t b_padding;      /*< indicates if packets has padding */
+    uint32_t u_report;         /*< reception packet count */
+    uint32_t u_payload_type;   /*< type of RTCP payload */
+    uint32_t u_length;         /*< length of packet */
+    uint32_t u_ssrc;           /*< channel name this packet belongs to */
 
     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 */
+        rtcp_SR_t sr;                /*< SR record */
+        rtcp_RR_t rr;                /*< RR record */
+        rtcp_SDES_t sdes;            /*< SDES record */
+        rtcp_BYE_t bye;              /*< BYE record */
+        rtcp_APP_t 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_pkt_t;
+
+typedef struct rtcp_client_t
+{
+    uint32_t    i_index;
+    uint32_t    u_ssrc;            /*< channel name */
+    vlc_bool_t  b_deleted;         /*< channel deleted ? */
+    mtime_t     i_timeout;         /*< remove timeout before real deletion,
+                                    * this is recommended by RFC 3550 at
+                                    * page 27 to ignore out-of-order packets.
+                                    */
+    rtcp_stats_t *p_stats;         /*< RTCP statistics */
+
+    uint32_t         i_items;      /*< count of SDES item structures */
+    rtcp_SDES_item_t **pp_sdes;    /*< SDES client items */
+} rtcp_client_t;
+
+/**
+ * structure rtcp_t
+ * This structure is a VLC_OBJECT and holds RTCP statistical information.
+ */
+typedef struct rtcp_t
+{
+    VLC_COMMON_MEMBERS
+
+    int fd;                        /*< socket descriptor of rtcp stream */
+
+    uint32_t u_clients;            /*< number of clients connected */
+    uint32_t u_active;             /*< number of active senders */
+    uint32_t u_members;            /*< number of clients in previous interval */
+
+    mtime_t i_date;                 /*< current RTCP packet interval */
+    mtime_t i_last_date;            /*< previous RTCP packet interval */
+    mtime_t i_next_date;            /*< next scheduled transmision time
+                                        of RTCP packet */
+
+    uint32_t i_clients;            /*< count of clients structures */
+    rtcp_client_t **pp_clients;    /*< RTCP clients */
+
+    /* bitstream data pointer used for decoding */
+    bs_t    *bs;                   /*< bitstream decoding data pointer */
+
+    int (*pf_add_client)( vlc_object_t *p_this, uint32_t u_ssrc, uint32_t *i_pos );
+    int (*pf_del_client)( vlc_object_t *p_this, uint32_t u_ssrc );
+    int (*pf_find_client)( vlc_object_t *p_this, uint32_t u_ssrc, uint32_t *i_pos );
+
 } rtcp_t;
 
+int rtcp_add_client( vlc_object_t *p_this, uint32_t u_ssrc, uint32_t *i_pos );
+int rtcp_del_client( vlc_object_t *p_this, uint32_t u_ssrc );
+/* Should be called with vlc_mutex_lock( &p_this->objec_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 */