src/rtcp.c

00001 /*
00002   The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) stack.
00003   Copyright (C) 2001  Simon MORLAT simon.morlat@linphone.org
00004 
00005   This library is free software; you can redistribute it and/or
00006   modify it under the terms of the GNU Lesser General Public
00007   License as published by the Free Software Foundation; either
00008   version 2.1 of the License, or (at your option) any later version.
00009 
00010   This library is distributed in the hope that it will be useful,
00011   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013   Lesser General Public License for more details.
00014 
00015   You should have received a copy of the GNU Lesser General Public
00016   License along with this library; if not, write to the Free Software
00017   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 */
00019 
00020 /***************************************************************************
00021  *            rtcp.c
00022  *
00023  *  Wed Dec  1 11:45:30 2004
00024  *  Copyright  2004  Simon Morlat
00025  *  Email simon dot morlat at linphone dot org
00026  ****************************************************************************/
00027 #include "ortp/ortp.h"
00028 #include "ortp/rtpsession.h"
00029 #include "ortp/rtcp.h"
00030 #include "utils.h"
00031 #include "rtpsession_priv.h"
00032 
00033 #define rtcp_bye_set_ssrc(b,pos,ssrc)   (b)->ssrc[pos]=htonl(ssrc)
00034 #define rtcp_bye_get_ssrc(b,pos)                ntohl((b)->ssrc[pos])
00035 
00036 
00037 void rtcp_common_header_init(rtcp_common_header_t *ch, RtpSession *s,int type, int rc, int bytes_len){
00038         rtcp_common_header_set_version(ch,2);
00039         rtcp_common_header_set_padbit(ch,0);
00040         rtcp_common_header_set_packet_type(ch,type);
00041         rtcp_common_header_set_rc(ch,rc);       /* as we don't yet support multi source receiving */
00042         rtcp_common_header_set_length(ch,(bytes_len/4)-1);
00043 }
00044 
00045 static mblk_t *sdes_chunk_new(uint32_t ssrc){
00046         mblk_t *m=allocb(RTCP_SDES_CHUNK_DEFAULT_SIZE,0);
00047         sdes_chunk_t *sc=(sdes_chunk_t*)m->b_rptr;
00048         sc->csrc=htonl(ssrc);
00049         m->b_wptr+=sizeof(sc->csrc);
00050         return m;
00051 }
00052 
00053 
00054 static mblk_t * sdes_chunk_append_item(mblk_t *m, rtcp_sdes_type_t sdes_type, const char *content)
00055 {       
00056         if ( content )
00057         {
00058                 sdes_item_t si;
00059                 si.item_type=sdes_type;
00060                 si.len=(uint8_t) MIN(strlen(content),RTCP_SDES_MAX_STRING_SIZE);
00061                 m=appendb(m,(char*)&si,RTCP_SDES_ITEM_HEADER_SIZE,FALSE);
00062                 m=appendb(m,content,si.len,FALSE);
00063         }
00064         return m;
00065 }
00066 
00067 static void sdes_chunk_set_ssrc(mblk_t *m, uint32_t ssrc){
00068         sdes_chunk_t *sc=(sdes_chunk_t*)m->b_rptr;
00069         sc->csrc=htonl(ssrc);
00070 }
00071 
00072 #define sdes_chunk_get_ssrc(m) ntohl(((sdes_chunk_t*)((m)->b_rptr))->csrc)
00073 
00074 static mblk_t * sdes_chunk_pad(mblk_t *m){
00075         return appendb(m,"",1,TRUE);
00076 }
00077 
00082 void rtp_session_set_source_description(RtpSession *session, 
00083     const char *cname, const char *name, const char *email, const char *phone, 
00084     const char *loc, const char *tool, const char *note){
00085         mblk_t *chunk = sdes_chunk_new(session->snd.ssrc);
00086         mblk_t *m=chunk;
00087         const char *_cname=cname;
00088         if (_cname==NULL)
00089         {
00090                 _cname="Unknown";
00091         }
00092         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_CNAME, _cname);
00093         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NAME, name);
00094         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_EMAIL, email);
00095         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_PHONE, phone);
00096         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_LOC, loc);
00097         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_TOOL, tool);
00098         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NOTE, note);
00099         chunk=sdes_chunk_pad(chunk);
00100         if (session->sd!=NULL) freemsg(session->sd);
00101         session->sd=m;
00102 }
00103 
00104 void
00105 rtp_session_add_contributing_source(RtpSession *session, uint32_t csrc, 
00106     const char *cname, const char *name, const char *email, const char *phone, 
00107     const char *loc, const char *tool, const char *note)
00108 {
00109         mblk_t *chunk = sdes_chunk_new(csrc);
00110         mblk_t *m=chunk;
00111         char *_cname=(char*)cname;
00112         if (_cname==NULL)
00113         {
00114                 _cname="toto";
00115         }
00116         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_CNAME, cname);
00117         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NAME, name);
00118         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_EMAIL, email);
00119         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_PHONE, phone);
00120         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_LOC, loc);
00121         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_TOOL, tool);
00122         chunk=sdes_chunk_append_item(chunk, RTCP_SDES_NOTE, note);
00123         chunk=sdes_chunk_pad(chunk);
00124         putq(&session->contributing_sources,m);
00125 }
00126 
00127 
00128 
00129 mblk_t* rtp_session_create_rtcp_sdes_packet(RtpSession *session)
00130 {
00131     mblk_t *mp=allocb(sizeof(rtcp_common_header_t),0);
00132         rtcp_common_header_t *rtcp;
00133     mblk_t *tmp,*m=mp;
00134         queue_t *q;
00135         int rc=0;
00136     rtcp = (rtcp_common_header_t*)mp->b_wptr;
00137         mp->b_wptr+=sizeof(rtcp_common_header_t);
00138         
00139         /* concatenate all sdes chunks */
00140         sdes_chunk_set_ssrc(session->sd,session->snd.ssrc);
00141         m=concatb(m,dupmsg(session->sd));
00142         rc++;
00143         
00144         q=&session->contributing_sources;
00145     for (tmp=qbegin(q); !qend(q,tmp); tmp=qnext(q,mp)){
00146                 m=concatb(m,dupmsg(tmp));
00147                 rc++;
00148         }
00149         rtcp_common_header_init(rtcp,session,RTCP_SDES,rc,msgdsize(mp));
00150     return mp;
00151 }
00152  
00153 
00154 mblk_t *rtcp_create_simple_bye_packet(uint32_t ssrc, const char *reason)
00155 {       
00156         int packet_size;
00157         int strsize = 0;
00158         int strpadding = 0;
00159         mblk_t *mp;
00160         rtcp_bye_t *rtcp;
00161 
00162         packet_size     = RTCP_BYE_HEADER_SIZE;
00163         if (reason!=NULL) {
00164                 strsize=(int)MIN(strlen(reason),RTCP_BYE_REASON_MAX_STRING_SIZE);
00165                 if (strsize > 0) {
00166                         strpadding = 3 - (strsize % 4);
00167                         packet_size += 1 + strsize + strpadding;
00168                 }
00169         }
00170         mp      = allocb(packet_size, 0);
00171 
00172         rtcp = (rtcp_bye_t*)mp->b_rptr;
00173         rtcp_common_header_init(&rtcp->ch,NULL,RTCP_BYE,1,packet_size);
00174         rtcp->ssrc[0] = htonl(ssrc);
00175         mp->b_wptr += RTCP_BYE_HEADER_SIZE;
00176         /* append the reason if any*/
00177         if (reason!=NULL) {
00178                 const char pad[] = {0, 0, 0};
00179                 unsigned char strsize_octet = (unsigned char)strsize;
00180                 
00181                 appendb(mp, (const char*)&strsize_octet, 1, FALSE);
00182                 appendb(mp, reason,strsize, FALSE);
00183                 appendb(mp, pad,strpadding, FALSE);
00184         }
00185         return mp;
00186 }
00187 
00188 void rtp_session_remove_contributing_sources(RtpSession *session, uint32_t ssrc)
00189 {
00190         queue_t *q=&session->contributing_sources;
00191         mblk_t *tmp;
00192         for (tmp=qbegin(q); !qend(q,tmp); tmp=qnext(q,tmp)){
00193                 uint32_t csrc=sdes_chunk_get_ssrc(tmp);
00194                 if (csrc==ssrc) {
00195                         remq(q,tmp);
00196                         break;
00197                 }
00198         }
00199         tmp=rtcp_create_simple_bye_packet(ssrc, NULL);
00200         rtp_session_rtcp_send(session,tmp);
00201 }
00202 
00203 static void sender_info_init(sender_info_t *info, RtpSession *session){
00204         struct timeval tv;
00205         uint32_t tmp;
00206         gettimeofday(&tv,NULL);
00207         info->ntp_timestamp_msw=htonl(tv.tv_sec + 0x83AA7E80); /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */
00208 #if defined(_WIN32_WCE)
00209         tmp=(uint32_t)((double)tv.tv_usec*(double)(((uint64_t)1)<<32)*1.0e-6);
00210 #else
00211         tmp=(uint32_t)((double)tv.tv_usec*(double)(1LL<<32)*1.0e-6);
00212 #endif
00213         info->ntp_timestamp_lsw=htonl(tmp);
00214         info->rtp_timestamp=htonl(session->rtp.snd_last_ts);
00215         info->senders_packet_count=(uint32_t) htonl((u_long) session->rtp.stats.packet_sent);
00216         info->senders_octet_count=(uint32_t) htonl((u_long) session->rtp.stats.sent);
00217         session->rtp.last_rtcp_packet_count=session->rtp.stats.packet_sent;
00218 }
00219 
00220 
00221 
00222 static void report_block_init(report_block_t *b, RtpSession *session){
00223         int packet_loss=0;
00224         uint8_t loss_fraction=0;
00225         RtpStream *stream=&session->rtp;
00226         uint32_t delay_snc_last_sr=0;
00227         uint32_t fl_cnpl;
00228         
00229         /* compute the statistics */
00230         /*printf("hwrcv_extseq.one=%u, hwrcv_seq_at_last_SR=%u hwrcv_since_last_SR=%u\n",
00231                 stream->hwrcv_extseq.one,
00232                 stream->hwrcv_seq_at_last_SR,
00233                 stream->hwrcv_since_last_SR
00234                 );*/
00235         if (stream->hwrcv_seq_at_last_SR!=0){
00236                 packet_loss=(stream->hwrcv_extseq - stream->hwrcv_seq_at_last_SR) - stream->hwrcv_since_last_SR;
00237                 if (packet_loss<0)
00238                         packet_loss=0;
00239                 stream->stats.cum_packet_loss+=packet_loss;
00240                 loss_fraction=(int)(256.0*(float)packet_loss/(float)stream->hwrcv_since_last_SR);
00241         }
00242         /* reset them */
00243         stream->hwrcv_since_last_SR=0;
00244         stream->hwrcv_seq_at_last_SR=stream->hwrcv_extseq;
00245         
00246         if (stream->last_rcv_SR_time.tv_sec!=0){
00247                 struct timeval now;
00248                 float delay;
00249                 gettimeofday(&now,NULL);
00250                 delay=(float) ((now.tv_sec-stream->last_rcv_SR_time.tv_sec)*1e6 ) + (now.tv_usec-stream->last_rcv_SR_time.tv_usec);
00251                 delay=(float) (delay*65536*1e-6);
00252                 delay_snc_last_sr=(uint32_t) delay;
00253         }
00254         
00255         b->ssrc=htonl(session->rcv.ssrc);
00256         fl_cnpl=((loss_fraction&0xFF)<<24) | (stream->stats.cum_packet_loss & 0xFFFFFF);
00257         b->fl_cnpl=htonl(fl_cnpl);
00258         b->interarrival_jitter=htonl((uint32_t) stream->jittctl.inter_jitter);
00259         b->ext_high_seq_num_rec=htonl(stream->hwrcv_extseq);
00260         b->lsr=htonl(stream->last_rcv_SR_ts);
00261         b->delay_snc_last_sr=htonl(delay_snc_last_sr);
00262 }
00263 
00264 
00265 
00266 static int rtcp_sr_init(RtpSession *session, uint8_t *buf, int size){
00267         rtcp_sr_t *sr=(rtcp_sr_t*)buf;
00268         int rr=(session->rtp.stats.packet_recv>0);
00269         int sr_size=sizeof(rtcp_sr_t)-sizeof(report_block_t)+(rr*sizeof(report_block_t));
00270         if (size<sr_size) return 0;
00271         rtcp_common_header_init(&sr->ch,session,RTCP_SR,rr,sr_size);
00272         sr->ssrc=htonl(session->snd.ssrc);
00273         sender_info_init(&sr->si,session);
00274         /*only include a report block if packets were received*/
00275         if (rr)
00276                 report_block_init(&sr->rb[0],session);
00277         return sr_size;
00278 }
00279 
00280 static int rtcp_rr_init(RtpSession *session, uint8_t *buf, int size){
00281         rtcp_rr_t *rr=(rtcp_rr_t*)buf;
00282         if (size<sizeof(rtcp_rr_t)) return 0;
00283         rtcp_common_header_init(&rr->ch,session,RTCP_RR,1,sizeof(rtcp_rr_t));
00284         rr->ssrc=htonl(session->snd.ssrc);
00285         report_block_init(&rr->rb[0],session);
00286         return sizeof(rtcp_rr_t);
00287 }
00288 
00289 static int rtcp_app_init(RtpSession *session, uint8_t *buf, uint8_t subtype, const char *name, int size){
00290         rtcp_app_t *app=(rtcp_app_t*)buf;
00291         if (size<sizeof(rtcp_app_t)) return 0;
00292         rtcp_common_header_init(&app->ch,session,RTCP_APP,subtype,size);
00293         app->ssrc=htonl(session->snd.ssrc);
00294         memset(app->name,0,4);
00295         strncpy(app->name,name,4);
00296         return sizeof(rtcp_app_t);
00297 }
00298 
00299 static mblk_t * make_rr(RtpSession *session){
00300         mblk_t *cm=NULL;
00301         mblk_t *sdes=NULL;
00302         
00303         cm=allocb(sizeof(rtcp_sr_t),0);
00304         cm->b_wptr+=rtcp_rr_init(session,cm->b_wptr,sizeof(rtcp_rr_t));
00305         /* make a SDES packet */
00306         if (session->sd!=NULL)
00307                 sdes=rtp_session_create_rtcp_sdes_packet(session);
00308         /* link them */
00309         cm->b_cont=sdes;
00310         return cm;
00311 }
00312 
00313 
00314 static mblk_t * make_sr(RtpSession *session){
00315         mblk_t *cm=NULL;
00316         mblk_t *sdes=NULL;
00317         
00318         cm=allocb(sizeof(rtcp_sr_t),0);
00319         cm->b_wptr+=rtcp_sr_init(session,cm->b_wptr,sizeof(rtcp_sr_t));
00320         /* make a SDES packet */
00321         if (session->sd!=NULL)
00322                 sdes=rtp_session_create_rtcp_sdes_packet(session);
00323         /* link them */
00324         cm->b_cont=sdes;
00325         return cm;
00326 }
00327 
00328 void rtp_session_rtcp_process_send(RtpSession *session){
00329         RtpStream *st=&session->rtp;
00330         mblk_t *m;
00331         if (st->rcv_last_app_ts - st->last_rtcp_report_snt_r > st->rtcp_report_snt_interval 
00332                 || st->snd_last_ts - st->last_rtcp_report_snt_s > st->rtcp_report_snt_interval){
00333                 st->last_rtcp_report_snt_r=st->rcv_last_app_ts;
00334                 st->last_rtcp_report_snt_s=st->snd_last_ts;
00335                 m=make_sr(session);
00336                 /* send the compound packet */
00337                 rtp_session_rtcp_send(session,m);
00338                 ortp_debug("Rtcp compound message sent.");
00339         }
00340 }
00341 
00342 void rtp_session_rtcp_process_recv(RtpSession *session){
00343         RtpStream *st=&session->rtp;
00344         mblk_t *m=NULL;
00345         if (st->rcv_last_app_ts - st->last_rtcp_report_snt_r > st->rtcp_report_snt_interval 
00346                 || st->snd_last_ts - st->last_rtcp_report_snt_s > st->rtcp_report_snt_interval){
00347                 st->last_rtcp_report_snt_r=st->rcv_last_app_ts;
00348                 st->last_rtcp_report_snt_s=st->snd_last_ts;
00349 
00350                 if (session->rtp.last_rtcp_packet_count<session->rtp.stats.packet_sent){
00351                         m=make_sr(session);
00352                         session->rtp.last_rtcp_packet_count=session->rtp.stats.packet_sent;
00353                 }else if (session->rtp.stats.packet_recv>0){
00354                         /*don't send RR when no packet are received yet*/
00355                         m=make_rr(session);
00356                 }
00357                 if (m!=NULL){
00358                         /* send the compound packet */
00359                         rtp_session_rtcp_send(session,m);
00360                         ortp_debug("Rtcp compound message sent.");
00361                 }
00362         }
00363 }
00364 
00365 void rtp_session_send_rtcp_APP(RtpSession *session, uint8_t subtype, const char *name, const uint8_t *data, int datalen){
00366         mblk_t *h=allocb(sizeof(rtcp_app_t),0);
00367         mblk_t *d;
00368         h->b_wptr+=rtcp_app_init(session,h->b_wptr,subtype,name,datalen+sizeof(rtcp_app_t));
00369         d=esballoc((uint8_t*)data,datalen,0,NULL);
00370         h->b_cont=d;
00371         rtp_session_rtcp_send(session,h);
00372 }
00373 
00379 int
00380 rtp_session_bye(RtpSession *session, const char *reason)
00381 {
00382     mblk_t *cm;
00383     mblk_t *sdes = NULL;
00384     mblk_t *bye = NULL;
00385     int ret;
00386 
00387     /* Make a BYE packet (will be on the end of the compund packet). */
00388     bye = rtcp_create_simple_bye_packet(session->snd.ssrc, reason);
00389 
00390     /* SR or RR is determined by the fact whether stream was sent*/
00391     if (session->rtp.stats.packet_sent>0)
00392     {
00393         cm = allocb(sizeof(rtcp_sr_t), 0);
00394         cm->b_wptr += rtcp_sr_init(session,cm->b_wptr, sizeof(rtcp_sr_t));
00395         /* make a SDES packet */
00396         sdes = rtp_session_create_rtcp_sdes_packet(session);
00397         /* link them */
00398         concatb(concatb(cm, sdes), bye);
00399     } else if (session->rtp.stats.packet_recv>0){
00400         /* make a RR packet */
00401         cm = allocb(sizeof(rtcp_rr_t), 0);
00402         cm->b_wptr += rtcp_rr_init(session, cm->b_wptr, sizeof(rtcp_rr_t));
00403         /* link them */
00404         cm->b_cont = bye;
00405     }else cm=bye;
00406 
00407     /* Send compound packet. */
00408     ret = rtp_session_rtcp_send(session, cm);
00409 
00410     return ret;
00411 }
00412 

Generated on Fri Feb 15 00:07:36 2008 for oRTP by  doxygen 1.5.4