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
Line 
1/*
2 * FILE:        statistics.c
3 *
4 * PROGRAM:     RAT
5 *
6 * AUTHOR: V.J.Hardman + I.Kouvelas + O.Hodson
7 *
8 * CREATED: 23/03/95
9 *
10 * $Id$
11 *
12 * Copyright (c) 1995-98 University College London
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"
54#include "cushion.h"
55#include "speaker_table.h"
56#include "codec.h"
57#include "channel.h"
58#include "ui_control.h"
59#include "mbus.h"
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
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)
99{
100        int     units, i, j, k, trailing;
101        rx_queue_element_struct *p;
102        cc_unit *ccu;
103
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         */
109
110        ccu = (cc_unit*)block_alloc(sizeof(cc_unit));
111        memset(ccu,0,sizeof(cc_unit));
112        block_trash_chk();
113        units = validate_and_split(hdr->pt, data_ptr, len, ccu, &trailing, &src->inter_pkt_gap);
114        block_trash_chk();
115        if (units <=0) {
116                debug_msg("Validate and split failed!\n");
117                block_free(ccu,sizeof(cc_unit));
118                return 0;
119        }
120
121        for(i=0;i<ccu->iovc;i++) {
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;
127        }
128
129        for(i=0;i<trailing;i++) {
130                p = new_rx_unit();
131                p->unit_size        = cp->unit_len;
132                p->units_per_pckt   = units;
133                p->mixed            = FALSE;
134                p->dbe_source[0]    = src;
135                p->playoutpt        = playout_pt + i * cp->unit_len;
136                p->src_ts           = hdr->ts + i * cp->unit_len;
137                p->comp_count       = 0;
138                p->cc_pt            = ccu->cc->pt;
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;
147                p->native_count = 0;
148                if (i == 0) {
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);
158        }
159        mark_active_sender(src, sp);
160        return (units);
161}
162
163
164static u_int32
165adapt_playout(rtp_hdr_t *hdr,
166              int arrival_ts,
167              rtcp_dbentry *src,
168              session_struct *sp,
169              struct s_cushion_struct *cushion,
170              u_int32 real_time)
171{
172        u_int32 playout, var;
173        u_int32 minv, maxv;
174
175        int     delay, diff;
176        codec_t *cp;
177        u_int32 ntptime, play_time;
178        u_int32 sendtime = 0;
179        int     ntp_delay = 0;
180        u_int32 rtp_time;
181
182        int64 since_last_sr;
183   
184        arrival_ts = convert_time(arrival_ts, sp->device_clock, src->clock);
185        delay = arrival_ts - hdr->ts;
186
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;
193                cp = get_codec_by_pt(src->enc);
194                if (cp) {
195                        src->jitter  = 3 * 20 * cp->freq / 1000;
196                } else {
197                        src->jitter  = 240;
198                }
199        } else {
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                }
205                /* Jitter calculation as in RTP spec */
206                src->jitter += (((double) diff - src->jitter) / 16.0);
207                src->delay = delay;
208        }
209
210        /*
211         * besides having the lip-sync option enabled, we also need to have
212         * a SR received [dm]
213         */     
214        if (sp->sync_on && src->mapping_valid) {
215                /* calculate delay in absolute (real) time [dm] */ 
216                ntptime = (src->last_ntp_sec & 0xffff) << 16 | src->last_ntp_frac >> 16;
217                if (hdr->ts > src->last_rtp_ts) {
218                        since_last_sr = hdr->ts - src->last_rtp_ts;     
219                } else {
220                        since_last_sr = src->last_rtp_ts - hdr->ts;
221                }
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); */
224
225                ntp_delay = real_time - sendtime;
226
227                if (src->first_pckt_flag == TRUE) {
228                        src->sync_playout_delay = ntp_delay;
229                }
230        }
231
232        if (ts_gt(hdr->ts, src->last_ts)) {
233                cp = get_codec_by_pt(src->enc);
234                /* IF (a) TS start
235                   OR (b) we've thrown 4 consecutive packets away
236                   OR (c) ts have jumped by 8 packets worth
237                   OR (d) playout buffer running dry.
238                   THEN adapt playout and communicate it
239                   */
240                if ((hdr->m) ||
241                    src->cont_toged ||
242                    ts_gt(hdr->ts, (src->last_ts + (hdr->seq - src->last_seq) * src->inter_pkt_gap * 8 + 1)) ||
243                    src->playout_danger) {
244#ifdef DEBUG
245                        if (hdr->m) {
246                                debug_msg("New talkspurt\n");
247                        } else if (src->cont_toged) {
248                                debug_msg("Cont_toged\n");
249                        } else if (src->playout_danger) {
250                                debug_msg("playout danger\n");
251                        } else {
252                                debug_msg("Time stamp jump %ld %ld\n", hdr->ts, src->last_ts);
253                        }
254#endif
255                        var = (u_int32) src->jitter * 3;
256
257                        if (var < (unsigned)src->inter_pkt_gap) var = src->inter_pkt_gap;
258                       
259                        if (src->playout_danger) {
260                                /* This is usually a sign that src clock is
261                                 * slower than ours. */
262                                var = max(var, 3 * cushion_get_size(cushion)); ;
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);
267                        }
268
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                        }
277
278                        assert(var > 0);
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                        }
290
291
292                        if (sp->sync_on && src->mapping_valid) {
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                               
296                                /* Communicate our playout delay to the video tool... */
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                                */
303                                debug_msg("ad=%d\tvd=%d\n", src->sync_playout_delay, src->video_playout);
304                                if (src->video_playout_received == TRUE &&
305                                    src->video_playout > src->sync_playout_delay) {
306                                        src->sync_playout_delay = src->video_playout;
307                                }
308
309                        }
310                } else {
311                        /* Do not set encoding on TS start packets as they do not show if redundancy is used...   */
312                        /*      src->encoding = hdr->pt; */
313                }
314
315                src->playout_danger = FALSE;
316        }
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
322        src->last_ts        = hdr->ts;
323        src->last_seq       = hdr->seq;
324
325        /* Calculate the playout point in local source time for this packet. */
326        if (sp->sync_on && src->mapping_valid) {
327                /*     
328                 * Use the NTP to RTP ts mapping to calculate the playout time
329                 * converted to the clock base of the receiver
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;
335        } else {
336                playout = hdr->ts + src->playout;
337        }
338
339        if (src->cont_toged > 12) {
340                /* something has gone wrong if this assertion fails*/
341                if (playout < get_time(src->clock)) {
342                        debug_msg("playout before now.\n");
343                        src->first_pckt_flag = TRUE;
344                }
345        }
346
347        return playout;
348}
349
350static int
351rtp_header_validation(rtp_hdr_t *hdr, int *len, int *extlen)
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) {
359                debug_msg("rtp_header_validation: version != 2\n");
360                return FALSE;
361        }
362
363        /* Check for valid audio payload types... */
364        if (((hdr->pt > 23) && (hdr->pt < 96)) || (hdr->pt > 127)) {
365                debug_msg("rtp_header_validation: payload-type out of audio range\n");
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...                       */
371        if (hdr->p) {
372                int pad = *((unsigned char *)hdr + *len - 1);
373                if (pad < 1) {
374                        debug_msg("rtp_header_validation: padding but 0 len\n");
375                        return FALSE;
376                }
377                *len -= pad;
378        }
379
380        if (hdr->x) {
381                *extlen = *((u_int32*)((unsigned char*)hdr + 4*(3+hdr->cc)))&0x0000ffff;
382        } else {
383                *extlen = 0;
384        }
385
386        return (TRUE);
387}
388
389static void
390receiver_change_format(rtcp_dbentry *dbe, codec_t *cp)
391{
392        debug_msg("Changing Format. %d %d\n", dbe->enc, cp->pt);
393        dbe->first_pckt_flag = TRUE;
394        dbe->enc             = cp->pt;
395        change_freq(dbe->clock, cp->freq);
396
397
398void
399statistics(session_struct    *sp,
400           pckt_queue_struct *netrx_pckt_queue,
401           rx_queue_struct   *unitsrx_queue_ptr,
402           struct s_cushion_struct    *cushion,
403           u_int32       cur_time,
404           u_int32       real_time)
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;
422        u_char          *data_ptr;
423        int              len,extlen;
424        rtcp_dbentry    *src;
425        u_int32          playout;
426        pckt_queue_element_struct *e_ptr;
427        codec_t         *pcp;
428        char             update_req = FALSE;
429        int pkt_cnt = 0;
430
431        /* Process incoming packets */
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) {
438                        debug_msg("RTP Packet failed header validation!\n");
439                        goto release;
440                }
441       
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       
447                if ((hdr->ssrc == sp->db->myssrc) && sp->filter_loopback) {
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) {
455                        debug_msg("Packet from unknown participant discarded\n");
456                        goto release;
457                }
458                rtcp_update_seq(src, hdr->seq);
459
460                if (sp->have_device == FALSE) {
461                        /* we don't have the audio device so there is no point processing data any further. */
462                        goto release;
463                }
464
465                data_ptr =  (unsigned char *)e_ptr->pckt_ptr + 4 * (3 + hdr->cc) + extlen;
466                len      = e_ptr->len - 4 * (3 + hdr->cc) - extlen;
467       
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);
474                                goto release;
475                }
476       
477                if ((src->enc == -1) || (src->enc != pcp->pt))
478                        receiver_change_format(src, pcp);
479               
480                if (src->enc != pcp->pt) {
481                        /* we should tell update more about coded format */
482                        src->enc = pcp->pt;
483                        debug_msg("src enc %d pcp enc %d\n", src->enc, pcp->pt);
484                        update_req   = TRUE;
485                }
486               
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);
490               
491                if (update_req && (src->units_per_packet != 0)) {
492                        ui_update_stats(src, sp);
493                }
494                pkt_cnt++;
495        release:
496                free_pckt_queue_element(&e_ptr);
497        }
498        if (pkt_cnt > 5) {
499                debug_msg("Processed lots of packets(%d).\n", pkt_cnt);
500        }
501}
Note: See TracBrowser for help on using the browser.