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         if (size<sizeof(rtcp_sr_t)) return 0;
00269         rtcp_common_header_init(&sr->ch,session,RTCP_SR,1,sizeof(rtcp_sr_t));
00270         sr->ssrc=htonl(session->snd.ssrc);
00271         sender_info_init(&sr->si,session);
00272         report_block_init(&sr->rb[0],session);
00273         return sizeof(rtcp_sr_t);
00274 }
00275 
00276 static int rtcp_rr_init(RtpSession *session, uint8_t *buf, int size){
00277         rtcp_rr_t *rr=(rtcp_rr_t*)buf;
00278         if (size<sizeof(rtcp_rr_t)) return 0;
00279         rtcp_common_header_init(&rr->ch,session,RTCP_RR,1,sizeof(rtcp_rr_t));
00280         rr->ssrc=htonl(session->snd.ssrc);
00281         report_block_init(&rr->rb[0],session);
00282         return sizeof(rtcp_rr_t);
00283 }
00284 
00285 static int rtcp_app_init(RtpSession *session, uint8_t *buf, uint8_t subtype, const char *name, int size){
00286         rtcp_app_t *app=(rtcp_app_t*)buf;
00287         if (size<sizeof(rtcp_app_t)) return 0;
00288         rtcp_common_header_init(&app->ch,session,RTCP_APP,subtype,size);
00289         app->ssrc=htonl(session->snd.ssrc);
00290         memset(app->name,0,4);
00291         strncpy(app->name,name,4);
00292         return sizeof(rtcp_app_t);
00293 }
00294 
00295 static mblk_t * make_rr(RtpSession *session){
00296         mblk_t *cm=NULL;
00297         mblk_t *sdes=NULL;
00298         
00299         cm=allocb(sizeof(rtcp_sr_t),0);
00300         cm->b_wptr+=rtcp_rr_init(session,cm->b_wptr,sizeof(rtcp_rr_t));
00301         /* make a SDES packet */
00302         if (session->sd!=NULL)
00303                 sdes=rtp_session_create_rtcp_sdes_packet(session);
00304         /* link them */
00305         cm->b_cont=sdes;
00306         return cm;
00307 }
00308 
00309 
00310 static mblk_t * make_sr(RtpSession *session){
00311         mblk_t *cm=NULL;
00312         mblk_t *sdes=NULL;
00313         
00314         cm=allocb(sizeof(rtcp_sr_t),0);
00315         cm->b_wptr+=rtcp_sr_init(session,cm->b_wptr,sizeof(rtcp_sr_t));
00316         /* make a SDES packet */
00317         if (session->sd!=NULL)
00318                 sdes=rtp_session_create_rtcp_sdes_packet(session);
00319         /* link them */
00320         cm->b_cont=sdes;
00321         return cm;
00322 }
00323 
00324 void rtp_session_rtcp_process_send(RtpSession *session){
00325         RtpStream *st=&session->rtp;
00326         mblk_t *m;
00327         if (st->rcv_last_app_ts - st->last_rtcp_report_snt_r > st->rtcp_report_snt_interval 
00328                 || st->snd_last_ts - st->last_rtcp_report_snt_s > st->rtcp_report_snt_interval){
00329                 st->last_rtcp_report_snt_r=st->rcv_last_app_ts;
00330                 st->last_rtcp_report_snt_s=st->snd_last_ts;
00331                 m=make_sr(session);
00332                 /* send the compound packet */
00333                 rtp_session_rtcp_send(session,m);
00334                 ortp_debug("Rtcp compound message sent.");
00335         }
00336 }
00337 
00338 void rtp_session_rtcp_process_recv(RtpSession *session){
00339         RtpStream *st=&session->rtp;
00340         mblk_t *m;
00341         if (st->rcv_last_app_ts - st->last_rtcp_report_snt_r > st->rtcp_report_snt_interval 
00342                 || st->snd_last_ts - st->last_rtcp_report_snt_s > st->rtcp_report_snt_interval){
00343                 st->last_rtcp_report_snt_r=st->rcv_last_app_ts;
00344                 st->last_rtcp_report_snt_s=st->snd_last_ts;
00345 
00346                 if (session->rtp.last_rtcp_packet_count<session->rtp.stats.packet_sent){
00347                         m=make_sr(session);
00348                         session->rtp.last_rtcp_packet_count=session->rtp.stats.packet_sent;
00349                 }else{
00350                         m=make_rr(session);
00351                 }
00352                 /* send the compound packet */
00353                 rtp_session_rtcp_send(session,m);
00354                 ortp_debug("Rtcp compound message sent.");
00355         }
00356 }
00357 
00358 void rtp_session_send_rtcp_APP(RtpSession *session, uint8_t subtype, const char *name, const uint8_t *data, int datalen){
00359         mblk_t *h=allocb(sizeof(rtcp_app_t),0);
00360         mblk_t *d;
00361         h->b_wptr+=rtcp_app_init(session,h->b_wptr,subtype,name,datalen+sizeof(rtcp_app_t));
00362         d=esballoc((uint8_t*)data,datalen,0,NULL);
00363         h->b_cont=d;
00364         rtp_session_rtcp_send(session,h);
00365 }
00366 
00372 int
00373 rtp_session_bye(RtpSession *session, const char *reason)
00374 {
00375     mblk_t *cm;
00376     mblk_t *sdes = NULL;
00377     mblk_t *bye = NULL;
00378     int ret;
00379 
00380     /* Make a BYE packet (will be on the end of the compund packet). */
00381     bye = rtcp_create_simple_bye_packet(session->snd.ssrc, reason);
00382 
00383     /* SR or RR should be determined by the fact whether st was sent,
00384        not on the mode. But we follow the current ortplib logic. */
00385     if (session->mode==RTP_SESSION_SENDONLY
00386         || session->mode==RTP_SESSION_SENDRECV)
00387     {
00388         cm = allocb(sizeof(rtcp_sr_t), 0);
00389         cm->b_wptr += rtcp_sr_init(session,cm->b_wptr, sizeof(rtcp_sr_t));
00390         /* make a SDES packet */
00391         sdes = rtp_session_create_rtcp_sdes_packet(session);
00392         /* link them */
00393         concatb(concatb(cm, sdes), bye);
00394     } else {
00395         /* make a RR packet */
00396         cm = allocb(sizeof(rtcp_rr_t), 0);
00397         cm->b_wptr += rtcp_rr_init(session, cm->b_wptr, sizeof(rtcp_rr_t));
00398         /* link them */
00399         cm->b_cont = bye;
00400     }
00401 
00402     /* Send compound packet. */
00403     ret = rtp_session_rtcp_send(session, cm);
00404 
00405     return ret;
00406 }
00407 

Generated on Fri Jun 22 17:30:36 2007 for oRTP by  doxygen 1.5.1