root/rat/trunk/statistics.c @ 1865

Revision 1865, 18.6 KB (checked in by ucacoxh, 16 years ago)

- Fixed div by zero error when calculating the jitter.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[1310]1/*
2 * FILE:        statistics.c
3 *
4 * PROGRAM:     RAT
5 *
[1394]6 * AUTHOR: V.J.Hardman + I.Kouvelas + O.Hodson
[1310]7 *
8 * CREATED: 23/03/95
9 *
10 * $Id$
11 *
[1394]12 * Copyright (c) 1995-98 University College London
[1310]13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, is permitted, for non-commercial use only, provided
17 * that the following conditions are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 * 3. All advertising materials mentioning features or use of this software
24 *    must display the following acknowledgement:
25 *      This product includes software developed by the Computer Science
26 *      Department at University College London
27 * 4. Neither the name of the University nor of the Department may be used
28 *    to endorse or promote products derived from this software without
29 *    specific prior written permission.
30 * Use of this software for commercial purposes is explicitly forbidden
31 * unless prior written permission is obtained from the authors.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
34 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 */
45
46#include "statistics.h"
47#include "session.h"
48#include "receive.h"
49#include "interfaces.h"
50#include "rtcp_pckt.h"
51#include "rtcp_db.h"
52#include "util.h"
53#include "audio.h"
[1521]54#include "cushion.h"
[1310]55#include "speaker_table.h"
56#include "codec.h"
[1394]57#include "channel.h"
[1537]58#include "ui_control.h"
[1411]59#include "mbus.h"
[1310]60
61static rtcp_dbentry *
62update_database(session_struct *sp, u_int32 ssrc, u_int32 cur_time)
63{
64        rtcp_dbentry   *dbe_source;
65
66        /* This function gets the relevant data base entry */
67        dbe_source = rtcp_get_dbentry(sp, ssrc);
68        if (dbe_source == NULL) {
69                /* We haven't received an RTCP packet for this source, so we must throw the   */
70                /* packets away. This seems a little extreme, but there are actually a couple */
71                /* of good reasons for it:                                                    */
72                /*   1) If we're receiving encrypted data, but we don't have the decryption   */
73                /*      key, then every RTP packet we receive will have a different SSRC      */
74                /*      and if we create a new database entry for it, we fill up the database */
75                /*      with garbage (which is then displayed in the list of participants...) */
76                /*   2) The RTP specification says that we should do it this way (sec 6.2.1)  */
77                /*                                                                     [csp]  */
78                return NULL;
79        }
80        dbe_source->last_active = cur_time;
81        dbe_source->is_sender   = 1;
82
83        sp->db->pckts_received++;
84
85        return dbe_source;
86}
87
88static int
[1394]89split_block(u_int32 playout_pt,
90            codec_t *cp,
91            char *data_ptr,
92            int len,
93            rtcp_dbentry *src,
94            rx_queue_struct *unitsrx_queue_ptr,
95            int talks,
96            rtp_hdr_t *hdr,
97            session_struct *sp,
98            u_int32 cur_time)
[1310]99{
[1394]100        int     units, i, j, k, trailing;
[1310]101        rx_queue_element_struct *p;
[1394]102        cc_unit *ccu;
[1600]103
[1394]104        /* we no longer break data units across rx elements here.
105         * instead we leave channel coded data in first block and
106         * remove channel coding when we are ready to decode and play
107         * samples.
108         */
[1310]109
[1394]110        ccu = (cc_unit*)block_alloc(sizeof(cc_unit));
111        memset(ccu,0,sizeof(cc_unit));
[1613]112        block_trash_chk();
[1608]113        units = validate_and_split(hdr->pt, data_ptr, len, ccu, &trailing, &src->inter_pkt_gap);
[1613]114        block_trash_chk();
[1394]115        if (units <=0) {
[1631]116                debug_msg("Validate and split failed!\n");
[1411]117                block_free(ccu,sizeof(cc_unit));
[1310]118                return 0;
119        }
120
[1394]121        for(i=0;i<ccu->iovc;i++) {
[1600]122                ccu->iov[i].iov_base = (caddr_t)block_alloc(ccu->iov[i].iov_len);
123                memcpy(ccu->iov[i].iov_base,
124                       data_ptr,
125                       ccu->iov[i].iov_len);
126                data_ptr += ccu->iov[i].iov_len;
[1394]127        }
[1310]128
[1394]129        for(i=0;i<trailing;i++) {
[1321]130                p = new_rx_unit();
[1310]131                p->unit_size        = cp->unit_len;
132                p->units_per_pckt   = units;
133                p->mixed            = FALSE;
134                p->dbe_source[0]    = src;
[1460]135                p->playoutpt        = playout_pt + i * cp->unit_len;
136                p->src_ts           = hdr->ts + i * cp->unit_len;
[1394]137                p->comp_count       = 0;
138                p->cc_pt            = ccu->cc->pt;
[1310]139                for (j = 0, k = 1; j < hdr->cc; j++) {
140                        p->dbe_source[k] = update_database(sp, ntohl(hdr->csrc[j]), cur_time);
141                        if (p->dbe_source[k] != NULL) {
142                                mark_active_sender(p->dbe_source[k], sp);
143                                k++;
144                        }
145                }
146                p->dbe_source_count = k;
[1394]147                p->native_count = 0;
[1673]148                if (i == 0) {
[1394]149                    p->ccu[0]  = ccu;
150                    p->ccu_cnt = 1;
151                    p->talk_spurt_start = talks;
152                } else {
153                    p->ccu[0]  = NULL;
154                    p->ccu_cnt = 0;
155                    p->talk_spurt_start = FALSE;
156                }
157                put_on_rx_queue(p, unitsrx_queue_ptr);
[1310]158        }
[1460]159        mark_active_sender(src, sp);
[1310]160        return (units);
161}
162
[1699]163
[1310]164static u_int32
[1535]165adapt_playout(rtp_hdr_t *hdr,
166              int arrival_ts,
167              rtcp_dbentry *src,
[1521]168              session_struct *sp,
169              struct s_cushion_struct *cushion,
[1699]170              u_int32 real_time)
[1310]171{
172        u_int32 playout, var;
[1535]173        u_int32 minv, maxv;
174
[1310]175        int     delay, diff;
176        codec_t *cp;
[1756]177        u_int32 ntptime, play_time;
178        u_int32 sendtime = 0;
179        int     ntp_delay = 0;
[1699]180        u_int32 rtp_time;
[1849]181
[1846]182        int64 since_last_sr;
183   
[1310]184        arrival_ts = convert_time(arrival_ts, sp->device_clock, src->clock);
185        delay = arrival_ts - hdr->ts;
[1830]186
[1310]187        if (src->first_pckt_flag == TRUE) {
188                src->first_pckt_flag = FALSE;
189                diff                 = 0;
190                src->delay           = delay;
191                src->last_ts         = hdr->ts - 1;
192                hdr->m               = TRUE;
[1843]193                cp = get_codec_by_pt(src->enc);
[1790]194                if (cp) {
195                        src->jitter  = 3 * 20 * cp->freq / 1000;
196                } else {
197                        src->jitter  = 240;
198                }
[1310]199        } else {
[1865]200                if (hdr->seq != src->last_seq) {
201                        diff       = abs(delay - src->delay) / abs(hdr->seq - src->last_seq);
202                } else { /* Duplicate */
203                        diff       = abs(delay - src->delay);
204                }
[1849]205                /* Jitter calculation as in RTP spec */
206                src->jitter += (((double) diff - src->jitter) / 16.0);
[1851]207                src->delay = delay;
[1310]208        }
209
[1735]210        /*
211         * besides having the lip-sync option enabled, we also need to have
212         * a SR received [dm]
213         */     
[1731]214        if (sp->sync_on && src->mapping_valid) {
[1699]215                /* calculate delay in absolute (real) time [dm] */ 
216                ntptime = (src->last_ntp_sec & 0xffff) << 16 | src->last_ntp_frac >> 16;
[1708]217                if (hdr->ts > src->last_rtp_ts) {
[1721]218                        since_last_sr = hdr->ts - src->last_rtp_ts;     
[1756]219                } else {
[1721]220                        since_last_sr = src->last_rtp_ts - hdr->ts;
[1708]221                }
[1756]222                since_last_sr = (since_last_sr << 16) / get_freq(src->clock);
223                sendtime = ntptime + since_last_sr; /* (since_last_sr << 16) / get_freq(src->clock); */
[1721]224
[1699]225                ntp_delay = real_time - sendtime;
[1729]226
[1699]227                if (src->first_pckt_flag == TRUE) {
228                        src->sync_playout_delay = ntp_delay;
229                }
230        }
231
[1310]232        if (ts_gt(hdr->ts, src->last_ts)) {
[1843]233                cp = get_codec_by_pt(src->enc);
[1498]234                /* IF (a) TS start
[1587]235                   OR (b) we've thrown 4 consecutive packets away
[1498]236                   OR (c) ts have jumped by 8 packets worth
[1591]237                   OR (d) playout buffer running dry.
[1498]238                   THEN adapt playout and communicate it
239                   */
[1460]240                if ((hdr->m) ||
[1779]241                    src->cont_toged ||
[1608]242                    ts_gt(hdr->ts, (src->last_ts + (hdr->seq - src->last_seq) * src->inter_pkt_gap * 8 + 1)) ||
[1591]243                    src->playout_danger) {
244#ifdef DEBUG
245                        if (hdr->m) {
[1631]246                                debug_msg("New talkspurt\n");
[1779]247                        } else if (src->cont_toged) {
248                                debug_msg("Cont_toged\n");
[1591]249                        } else if (src->playout_danger) {
[1631]250                                debug_msg("playout danger\n");
[1591]251                        } else {
[1631]252                                debug_msg("Time stamp jump %ld %ld\n", hdr->ts, src->last_ts);
[1591]253                        }
254#endif
[1310]255                        var = (u_int32) src->jitter * 3;
[1591]256
[1830]257                        if (var < (unsigned)src->inter_pkt_gap) var = src->inter_pkt_gap;
[1849]258                       
[1779]259                        if (src->playout_danger) {
[1790]260                                /* This is usually a sign that src clock is
261                                 * slower than ours. */
[1830]262                                var = max(var, 3 * cushion_get_size(cushion)); ;
[1790]263                                debug_msg("Playout danger, var (%ld)\n", var);
264                        } else {
265                                var = max(var, cushion_get_size(cushion));
266                                debug_msg("Playout var (%ld)\n", var);
[1779]267                        }
[1535]268
[1795]269                        minv = sp->min_playout * get_freq(src->clock) / 1000;
270                        maxv = sp->max_playout * get_freq(src->clock) / 1000;
271
272                        assert(maxv > minv);
273                        if (sp->limit_playout) {
274                                var = max(minv, var);
275                                var = min(maxv, var);
276                        }
[1849]277
[1535]278                        assert(var > 0);
[1849]279                       
280                        if (playout_buffer_exists(sp->playout_buf_list , src) ||
281                            hdr->ts - src->last_ts < (unsigned)get_freq(src->clock)) {
282                                /* If playout buffer exists
283                                 * or, difference in time stamps is less than 1 sec,
284                                 * we don't want playout point to be before that of existing data.
285                                 */
286                                src->playout = max((unsigned)src->playout, src->delay + var);
287                        } else {
288                                src->playout = src->delay + var;
289                        }
[1533]290
[1849]291
[1735]292                        if (sp->sync_on && src->mapping_valid) {
[1699]293                                /* use the jitter value as calculated but convert it to a ntp_ts freq [dm] */ 
294                                src->sync_playout_delay = ntp_delay + ((var << 16) / get_freq(src->clock));
295                               
[1411]296                                /* Communicate our playout delay to the video tool... */
[1699]297                                ui_update_video_playout(src->sentry->cname, src->sync_playout_delay);
298               
299                                /* If the video tool is slower than us, then
300                                 * adjust to match it...  src->video_playout is
301                                 * the delay of the video in real time
302                                */
[1729]303                                debug_msg("ad=%d\tvd=%d\n", src->sync_playout_delay, src->video_playout);
[1699]304                                if (src->video_playout_received == TRUE &&
305                                    src->video_playout > src->sync_playout_delay) {
306                                        src->sync_playout_delay = src->video_playout;
307                                }
[1460]308
[1699]309                        }
310                } else {
[1310]311                        /* Do not set encoding on TS start packets as they do not show if redundancy is used...   */
[1640]312                        /*      src->encoding = hdr->pt; */
[1310]313                }
[1776]314
[1591]315                src->playout_danger = FALSE;
[1310]316        }
[1849]317
318        if (hdr->seq - src->last_seq != 1) {
319                debug_msg("seq jump last (%ld) cur (%ld)\n", src->last_seq, hdr->seq);
320        }
321
[1830]322        src->last_ts        = hdr->ts;
323        src->last_seq       = hdr->seq;
[1310]324
[1411]325        /* Calculate the playout point in local source time for this packet. */
[1735]326        if (sp->sync_on && src->mapping_valid) {
[1699]327                /*     
[1735]328                 * Use the NTP to RTP ts mapping to calculate the playout time
329                 * converted to the clock base of the receiver
[1699]330                 */
331                play_time = sendtime + src->sync_playout_delay;
332                rtp_time = sp->db->map_rtp_time + (((play_time - sp->db->map_ntp_time) * get_freq(src->clock)) >> 16);
333                playout = rtp_time;
334                src->playout = playout - hdr->ts;
[1776]335        } else {
[1699]336                playout = hdr->ts + src->playout;
337        }
338
[1535]339        if (src->cont_toged > 12) {
340                /* something has gone wrong if this assertion fails*/
[1624]341                if (playout < get_time(src->clock)) {
[1631]342                        debug_msg("playout before now.\n");
[1626]343                        src->first_pckt_flag = TRUE;
[1624]344                }
[1535]345        }
[1776]346
[1411]347        return playout;
[1310]348}
349
[1427]350static int
[1476]351rtp_header_validation(rtp_hdr_t *hdr, int *len, int *extlen)
[1310]352{
353        /* This function checks the header info to make sure that the packet */
354        /* is valid. We return TRUE if the packet is valid, FALSE otherwise. */
355        /* This follows from page 52 of RFC1889.            [csp 22-10-1996] */
356
357        /* We only accept RTPv2 packets... */
358        if (hdr->type != 2) {
[1631]359                debug_msg("rtp_header_validation: version != 2\n");
[1310]360                return FALSE;
361        }
362
363        /* Check for valid audio payload types... */
364        if (((hdr->pt > 23) && (hdr->pt < 96)) || (hdr->pt > 127)) {
[1631]365                debug_msg("rtp_header_validation: payload-type out of audio range\n");
[1310]366                return FALSE;
367        }
368
369        /* If padding or header-extension is set, we punt on this one... */
370        /* We should really deal with it though...                       */
[1427]371        if (hdr->p) {
372                int pad = *((unsigned char *)hdr + *len - 1);
373                if (pad < 1) {
[1631]374                        debug_msg("rtp_header_validation: padding but 0 len\n");
[1427]375                        return FALSE;
376                }
377                *len -= pad;
378        }
[1310]379
[1427]380        if (hdr->x) {
381                *extlen = *((u_int32*)((unsigned char*)hdr + 4*(3+hdr->cc)))&0x0000ffff;
382        } else {
383                *extlen = 0;
384        }
385
[1310]386        return (TRUE);
387}
388
389static void
390receiver_change_format(rtcp_dbentry *dbe, codec_t *cp)
391{
[1673]392        debug_msg("Changing Format. %d %d\n", dbe->enc, cp->pt);
393        dbe->first_pckt_flag = TRUE;
394        dbe->enc             = cp->pt;
[1310]395        change_freq(dbe->clock, cp->freq);
[1673]396
[1310]397
398void
399statistics(session_struct    *sp,
400           pckt_queue_struct *netrx_pckt_queue,
401           rx_queue_struct   *unitsrx_queue_ptr,
[1521]402           struct s_cushion_struct    *cushion,
[1699]403           u_int32       cur_time,
404           u_int32       real_time)
[1310]405{
406        /*
407         * We expect to take in an RTP packet, and decode it - read fields
408         * etc. This module should do statistics, and keep information on
409         * losses, and re-orderings. Duplicates will be dealt with in the
410         * receive buffer module.
411         *
412         * Late packets will not be counted as lost. RTP stats reports count
413         * duplicates in with the number of packets correctly received, thus
414         * sometimes invalidating stats reports. We can, if necessary, keep
415         * track of some duplicates, and throw away in the receive module. It
416         * has not yet been decided whether or not loss will be 'indicated'
417         * to later modules - put a dummy unit(s) on the queue for the receive
418         * buffer
419         */
420
421        rtp_hdr_t       *hdr;
[1394]422        u_char          *data_ptr;
[1771]423        int              len,extlen;
[1310]424        rtcp_dbentry    *src;
[1849]425        u_int32          playout;
[1310]426        pckt_queue_element_struct *e_ptr;
[1394]427        codec_t         *pcp;
[1771]428        char             update_req = FALSE;
[1830]429        int pkt_cnt = 0;
[1310]430
[1771]431        /* Process incoming packets */
[1535]432        while(netrx_pckt_queue->queue_empty == FALSE) {
433                e_ptr = get_pckt_off_queue(netrx_pckt_queue);
434                /* Impose RTP formating on it... */
435                hdr = (rtp_hdr_t *) (e_ptr->pckt_ptr);
436       
437                if (rtp_header_validation(hdr, &e_ptr->len, &extlen) == FALSE) {
[1631]438                        debug_msg("RTP Packet failed header validation!\n");
[1427]439                        goto release;
440                }
[1460]441       
[1535]442                /* Convert from network byte-order */
443                hdr->seq  = ntohs(hdr->seq);
444                hdr->ts   = ntohl(hdr->ts);
445                hdr->ssrc = ntohl(hdr->ssrc);
446       
[1771]447                if ((hdr->ssrc == sp->db->myssrc) && sp->filter_loopback) {
[1535]448                        /* Discard loopback packets...unless we have asked for them ;-) */
449                        goto release;
450                }
451       
452                /* Get database entry of participant that sent this packet */
453                src = update_database(sp, hdr->ssrc, cur_time);
454                if (src == NULL) {
[1771]455                        debug_msg("Packet from unknown participant discarded\n");
[1535]456                        goto release;
457                }
458                rtcp_update_seq(src, hdr->seq);
[1427]459
[1580]460                if (sp->have_device == FALSE) {
[1771]461                        /* we don't have the audio device so there is no point processing data any further. */
[1580]462                        goto release;
463                }
464
[1630]465                data_ptr =  (unsigned char *)e_ptr->pckt_ptr + 4 * (3 + hdr->cc) + extlen;
[1580]466                len      = e_ptr->len - 4 * (3 + hdr->cc) - extlen;
[1427]467       
[1846]468                if ( ((pcp = get_codec_by_pt(hdr->pt)) == NULL &&
469                    (pcp = get_codec_by_pt(get_wrapped_payload(hdr->pt, (char*) data_ptr, len))) == NULL) ||
470                    (pcp != NULL && pcp->decode == NULL)) {
471                        /* We don't recognise this payload, or we don't have a decoder for it. */
472                        assert(pcp->decode);
473                                debug_msg("Cannot decode data (pt %d).\n",hdr->pt);
[1535]474                                goto release;
475                }
476       
[1673]477                if ((src->enc == -1) || (src->enc != pcp->pt))
[1535]478                        receiver_change_format(src, pcp);
479               
[1656]480                if (src->enc != pcp->pt) {
[1535]481                        /* we should tell update more about coded format */
[1656]482                        src->enc = pcp->pt;
483                        debug_msg("src enc %d pcp enc %d\n", src->enc, pcp->pt);
[1535]484                        update_req   = TRUE;
485                }
486               
[1849]487                playout = adapt_playout(hdr, e_ptr->arrival_timestamp, src, sp, cushion, real_time);
488
489                src->units_per_packet = split_block(playout, pcp, (char *) data_ptr, len, src, unitsrx_queue_ptr, hdr->m, hdr, sp, cur_time);
[1535]490               
[1771]491                if (update_req && (src->units_per_packet != 0)) {
492                        ui_update_stats(src, sp);
[1535]493                }
[1830]494                pkt_cnt++;
[1427]495        release:
[1535]496                free_pckt_queue_element(&e_ptr);
497        }
[1830]498        if (pkt_cnt > 5) {
499                debug_msg("Processed lots of packets(%d).\n", pkt_cnt);
500        }
[1310]501}
Note: See TracBrowser for help on using the browser.