root/rat/trunk/statistics.c @ 1710

Revision 1710, 17.3 KB (checked in by ucaccsp, 16 years ago)

sigh. compiles now.

  • 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 cur_time,
171              u_int32 real_time)
172{
173        u_int32 playout, var;
174        u_int32 minv, maxv;
175
176        int     delay, diff;
177        codec_t *cp;
178        u_int32 ntptime, sendtime, play_time;
179        int     ntp_delay;
180        u_int32 rtp_time;
181       
182        arrival_ts = convert_time(arrival_ts, sp->device_clock, src->clock);
183        delay = arrival_ts - hdr->ts;
184       
185        if (src->first_pckt_flag == TRUE) {
186                src->first_pckt_flag = FALSE;
187                diff                 = 0;
188                src->delay           = delay;
189                src->jitter          = 80;
190                src->last_ts         = hdr->ts - 1;
191                src->playout_ceil    = 0;
192                hdr->m               = TRUE;
193        } else {
194                diff       = abs(delay - src->delay);
195                src->delay = delay;
196                /* Jitter calculation as in RTP spec */
197                src->jitter = src->jitter + (((double) diff - src->jitter) / 16);
198        }
199
200        if (sp->sync_on) {
201                //real_time = ntp_time32(); /* cur_time should be passed be adapt_playout instead [dm] */
202                /* calculate delay in absolute (real) time [dm] */ 
203                ntptime = (src->last_ntp_sec & 0xffff) << 16 | src->last_ntp_frac >> 16;
204                if (hdr->ts > src->last_rtp_ts) {
205#ifdef WIN32
206                        sendtime = ntptime + ((__int64)(hdr->ts - src->last_rtp_ts) << 16) / get_freq(src->clock);
207#else
208                        sendtime = ntptime + ((long long)(hdr->ts - src->last_rtp_ts) << 16) / get_freq(src->clock);
209#endif
210                }
211/*              else {
212                        sendtime = ntptime + ((long long)(src->last_rtp_ts - hdr->ts) << 16) / get_freq(src->clock);
213                }
214*/
215                ntp_delay = real_time - sendtime;
216                if (src->first_pckt_flag == TRUE) {
217                        src->sync_playout_delay = ntp_delay;
218                }
219        }
220
221        if (ts_gt(hdr->ts, src->last_ts)) {
222                cp = get_codec(src->enc);
223                /* IF (a) TS start
224                   OR (b) we've thrown 4 consecutive packets away
225                   OR (c) ts have jumped by 8 packets worth
226                   OR (d) playout buffer running dry.
227                   THEN adapt playout and communicate it
228                   */
229                if ((hdr->m) ||
230                    src->cont_toged > 4 ||
231                    ts_gt(hdr->ts, (src->last_ts + (hdr->seq - src->last_seq) * src->inter_pkt_gap * 8 + 1)) ||
232                    src->playout_danger) {
233#ifdef DEBUG
234                        if (hdr->m) {
235                                debug_msg("New talkspurt\n");
236                        } else if (src->cont_toged > 4) {
237                                debug_msg("Cont_toged > 4\n");
238                        } else if (src->playout_danger) {
239                                debug_msg("playout danger\n");
240                        } else {
241                                debug_msg("Time stamp jump %ld %ld\n", hdr->ts, src->last_ts);
242                        }
243#endif
244                        var = (u_int32) src->jitter * 3;
245
246                        minv = sp->min_playout * get_freq(src->clock) / 1000;
247                        maxv = sp->max_playout * get_freq(src->clock) / 1000;
248
249                        assert(maxv > minv);
250                        if (sp->limit_playout) {
251                                var = max(minv, var);
252                                var = min(maxv, var);
253                        }
254
255                        var += cushion_get_size(cushion);
256                        if (src->clock!=sp->device_clock) {
257                                var += cp->unit_len;
258                        }
259
260                        assert(var > 0);
261                        src->playout = src->delay + var;
262
263                        if (sp->sync_on) {
264                                /* use the jitter value as calculated but convert it to a ntp_ts freq [dm] */ 
265                                src->sync_playout_delay = ntp_delay + ((var << 16) / get_freq(src->clock));
266                               
267                                /* Communicate our playout delay to the video tool... */
268                                ui_update_video_playout(src->sentry->cname, src->sync_playout_delay);
269               
270                                /* If the video tool is slower than us, then
271                                 * adjust to match it...  src->video_playout is
272                                 * the delay of the video in real time
273                                */
274                                printf("ad=%d\tvd=%d\n", src->sync_playout_delay, src->video_playout);
275                                if (src->video_playout_received == TRUE &&
276                                    src->video_playout > src->sync_playout_delay) {
277                                        src->sync_playout_delay = src->video_playout;
278                                }
279
280                        }
281                } else {
282                        /* Do not set encoding on TS start packets as they do not show if redundancy is used...   */
283                        /*      src->encoding = hdr->pt; */
284                }
285                src->last_ts        = hdr->ts;
286                src->last_seq       = hdr->seq;
287                src->playout_danger = FALSE;
288        }
289
290        /* Calculate the playout point in local source time for this packet. */
291        if (sp->sync_on) {
292                /*     
293                 * Use the NTP to RTP ts mapping to calculate the playout time converted to
294                 * the clock base of the receiver
295                 */
296                play_time = sendtime + src->sync_playout_delay;
297                rtp_time = sp->db->map_rtp_time + (((play_time - sp->db->map_ntp_time) * get_freq(src->clock)) >> 16);
298                playout = rtp_time;
299                src->playout = playout - hdr->ts;
300        }
301        else {
302                playout = hdr->ts + src->playout;
303        }
304
305        if (src->playout - src->delay > src->playout_ceil) {
306                src->playout_ceil = src->playout - src->delay;
307        }
308
309        if (src->cont_toged > 12) {
310                /* something has gone wrong if this assertion fails*/
311                if (playout < get_time(src->clock)) {
312                        debug_msg("playout before now.\n");
313                        src->first_pckt_flag = TRUE;
314                }
315        }
316        return playout;
317}
318
319static int
320rtp_header_validation(rtp_hdr_t *hdr, int *len, int *extlen)
321{
322        /* This function checks the header info to make sure that the packet */
323        /* is valid. We return TRUE if the packet is valid, FALSE otherwise. */
324        /* This follows from page 52 of RFC1889.            [csp 22-10-1996] */
325
326        /* We only accept RTPv2 packets... */
327        if (hdr->type != 2) {
328                debug_msg("rtp_header_validation: version != 2\n");
329                return FALSE;
330        }
331
332        /* Check for valid audio payload types... */
333        if (((hdr->pt > 23) && (hdr->pt < 96)) || (hdr->pt > 127)) {
334                debug_msg("rtp_header_validation: payload-type out of audio range\n");
335                return FALSE;
336        }
337
338        /* If padding or header-extension is set, we punt on this one... */
339        /* We should really deal with it though...                       */
340        if (hdr->p) {
341                int pad = *((unsigned char *)hdr + *len - 1);
342                if (pad < 1) {
343                        debug_msg("rtp_header_validation: padding but 0 len\n");
344                        return FALSE;
345                }
346                *len -= pad;
347        }
348
349        if (hdr->x) {
350                *extlen = *((u_int32*)((unsigned char*)hdr + 4*(3+hdr->cc)))&0x0000ffff;
351        } else {
352                *extlen = 0;
353        }
354
355        return (TRUE);
356}
357
358static void
359receiver_change_format(rtcp_dbentry *dbe, codec_t *cp)
360{
361        debug_msg("Changing Format. %d %d\n", dbe->enc, cp->pt);
362        dbe->first_pckt_flag = TRUE;
363        dbe->enc             = cp->pt;
364        change_freq(dbe->clock, cp->freq);
365
366
367void
368statistics(session_struct    *sp,
369           pckt_queue_struct *netrx_pckt_queue,
370           rx_queue_struct   *unitsrx_queue_ptr,
371           struct s_cushion_struct    *cushion,
372           u_int32       cur_time,
373           u_int32       real_time)
374{
375        /*
376         * We expect to take in an RTP packet, and decode it - read fields
377         * etc. This module should do statistics, and keep information on
378         * losses, and re-orderings. Duplicates will be dealt with in the
379         * receive buffer module.
380         *
381         * Late packets will not be counted as lost. RTP stats reports count
382         * duplicates in with the number of packets correctly received, thus
383         * sometimes invalidating stats reports. We can, if necessary, keep
384         * track of some duplicates, and throw away in the receive module. It
385         * has not yet been decided whether or not loss will be 'indicated'
386         * to later modules - put a dummy unit(s) on the queue for the receive
387         * buffer
388         */
389
390        rtp_hdr_t       *hdr;
391        u_char          *data_ptr;
392        int             len,extlen,compat;
393        rtcp_dbentry    *src;
394        u_int32         playout_pt;
395        pckt_queue_element_struct *e_ptr;
396        codec_t         *pcp;
397
398        char update_req = FALSE;
399
400        /* Process an incoming packets */
401        while(netrx_pckt_queue->queue_empty == FALSE) {
402                e_ptr = get_pckt_off_queue(netrx_pckt_queue);
403                /* Impose RTP formating on it... */
404                hdr = (rtp_hdr_t *) (e_ptr->pckt_ptr);
405       
406                if (rtp_header_validation(hdr, &e_ptr->len, &extlen) == FALSE) {
407                        debug_msg("RTP Packet failed header validation!\n");
408                        /* XXX log as bad packet */
409                        goto release;
410                }
411       
412                /* Convert from network byte-order */
413                hdr->seq  = ntohs(hdr->seq);
414                hdr->ts   = ntohl(hdr->ts);
415                hdr->ssrc = ntohl(hdr->ssrc);
416       
417                if ((hdr->ssrc == sp->db->myssrc) &&
418                    sp->filter_loopback) {
419                        /* Discard loopback packets...unless we have asked for them ;-) */
420                        goto release;
421                }
422       
423                /* Get database entry of participant that sent this packet */
424                src = update_database(sp, hdr->ssrc, cur_time);
425                if (src == NULL) {
426                        debug_msg("Packet from unknown participant\n");
427                        /* Discard packets from unknown participant */
428                        goto release;
429                }
430                rtcp_update_seq(src, hdr->seq);
431
432                if (sp->have_device == FALSE) {
433                        /* we don't have the audio device so there is no point
434                         * processing data any further.
435                         */
436                        goto release;
437                }
438
439                data_ptr =  (unsigned char *)e_ptr->pckt_ptr + 4 * (3 + hdr->cc) + extlen;
440                len      = e_ptr->len - 4 * (3 + hdr->cc) - extlen;
441       
442                if (!(pcp = get_codec(hdr->pt))) {
443                        /* this is either a channel coded block or we can't decode it */
444                        if (!(pcp = get_codec(get_wrapped_payload(hdr->pt, (char *) data_ptr, len)))) {
445                                debug_msg("Cannot decode data.\n");
446                                goto release;
447                        }
448                }
449       
450                compat = codec_compatible(pcp, get_codec(sp->encodings[0]));
451                if (!compat && !sp->auto_convert) {
452                        debug_msg("Format conversion not enabled (%s received).\n", pcp->name);
453                        goto release;
454                }
455               
456                if ((src->enc == -1) || (src->enc != pcp->pt))
457                        receiver_change_format(src, pcp);
458               
459                if (src->enc != pcp->pt) {
460                        /* we should tell update more about coded format */
461                        src->enc = pcp->pt;
462                        debug_msg("src enc %d pcp enc %d\n", src->enc, pcp->pt);
463                        update_req   = TRUE;
464                }
465               
466                playout_pt = adapt_playout(hdr, e_ptr->arrival_timestamp, src, sp, cushion, cur_time, real_time);
467                src->units_per_packet = split_block(playout_pt, pcp, (char *) data_ptr, len, src, unitsrx_queue_ptr, hdr->m, hdr, sp, cur_time);
468               
469                if (!src->units_per_packet) {
470                        goto release;
471                }
472       
473                if (update_req) ui_update_stats(src, sp);
474        release:
475                free_pckt_queue_element(&e_ptr);
476        }
477}
Note: See TracBrowser for help on using the browser.