root/rat/trunk/transmit.c @ 3078

Revision 3078, 24.2 KB (checked in by ucacoxh, 14 years ago)

- Added Id strings to all c file.
- Replaced $Date$ and $Revision$ with $Id$ in headers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:    transmit.c
3 * PROGRAM: RAT
4 * AUTHOR:  Orion Hodson / Isidor Kouvelas
5 *
6 * Copyright (c) 1995-2000 University College London
7 * All rights reserved.
8 */
9 
10#ifndef HIDE_SOURCE_STRINGS
11static const char cvsid[] =
12        "$Id$";
13#endif /* HIDE_SOURCE_STRINGS */
14
15#include "config_unix.h"
16#include "config_win32.h"
17#include "memory.h"
18#include "debug.h"
19#include "audio_types.h"
20#include "codec_types.h"
21#include "codec.h"
22#include "codec_state.h"
23#include "playout.h"
24#include "channel_types.h"
25#include "channel.h"
26#include "session.h"
27#include "audio.h"
28#include "audio_util.h"
29#include "sndfile.h"
30#include "parameters.h"
31#include "pdb.h"
32#include "ui.h"
33#include "rtp.h"
34#include "timers.h"
35#include "transmit.h"
36#include "util.h"
37
38/* All this code can be greatly simplified and reduced by making
39 * better use of the playout buffer structure in playout.h.
40 */
41
42typedef struct s_tx_unit {
43        sample   *data;             /* pointer to raw data in read_buf */
44        uint32_t  dur_used;         /* number of time intervals filled */
45        uint16_t  energy;
46        u_char    silence;          /* First pass */
47        u_char    send;             /* Silence second pass */
48} tx_unit;
49
50typedef struct s_tx_buffer {
51        struct s_session   *sp;
52        struct s_sd          *sd_info;
53        struct s_vad         *vad;
54        struct s_agc         *agc;
55        struct s_time        *clock;
56        struct s_bias_ctl    *bc;
57        struct s_pb          *media_buffer;
58        struct s_pb          *channel_buffer;
59        struct s_pb          *audio_buffer; /* Audio buffer and it's iterators */
60        struct s_pb_iterator *reading;      /* Current read point iterator     */
61        struct s_pb_iterator *silence;      /* Silence classification iterator */
62        struct s_pb_iterator *transmit;     /* Transmission point iterator     */
63        struct s_codec_state_store *state_store;    /* Encoder states        */
64        uint32_t        sending_audio:1;
65        uint16_t channels;
66        uint16_t unit_dur; /* dur. in sampling intervals (excludes channels) */
67
68        /* Statistics log */
69        double          mean_read_dur;
70        /* These are a hack because we use playout buffer
71         * which expects time units of type ts_t so we need
72         * to be able to map to and from 32 bit no for
73         * packet timestamp */
74        ts_sequencer    down_seq;  /* used for 32 -> ts_t */
75        ts_sequencer    up_seq;    /* used for ts_t -> 32 */
76
77        /* place for the samples */
78        sample samples[DEVICE_REC_BUF];
79        int    last_sample; /* Stores the index of the last read buffer */
80
81        /* bandwidth estimate parameters */
82        int    bps_bytes_sent;
83        ts_t   bps_last_update;
84} tx_buffer;
85
86static sample dummy_buf[DEVICE_REC_BUF];
87
88static int
89tx_unit_create(tx_buffer *tb, tx_unit  **ptu, int n_samples)
90{
91        tx_unit *tu;
92        tu = block_alloc(sizeof(tx_unit));
93        if (tu) {
94                memset(tu, 0, sizeof(tx_unit));
95                *ptu = tu;
96                /* Position sample pointer */
97                if (tb->last_sample + n_samples > DEVICE_REC_BUF) {
98                        tb->last_sample = 0;
99                }
100                tu->data = tb->samples + tb->last_sample;
101                tu->energy = 555;
102                tb->last_sample += n_samples;
103                return TRUE;
104        }
105        debug_msg("Failed to allocate tx_unit\n");
106        return FALSE;
107}
108
109static void
110tx_unit_destroy(tx_unit **ptu, uint32_t len)
111{
112        tx_unit *tu = *ptu;
113        assert(tu != NULL);
114        assert(len == sizeof(tx_unit));
115
116        block_free(tu, sizeof(tx_unit));
117        *ptu = NULL;
118}
119
120int
121tx_create(tx_buffer     **ntb,
122          session_t *sp,
123          struct s_time  *clock,
124          uint16_t         unit_dur,
125          uint16_t         channels)
126{
127        tx_buffer *tb;
128        uint16_t    freq;
129
130        tb = (tx_buffer*)xmalloc(sizeof(tx_buffer));
131        if (tb) {
132                memset(tb, 0, sizeof(tx_buffer));
133                debug_msg("Unit duration %d channels %d\n", unit_dur, channels);
134                tb->sp      = sp;
135                tb->clock   = clock;
136                freq        = (uint16_t)get_freq(clock);
137                tb->bc      = bias_ctl_create(channels, freq);
138                tb->sd_info = sd_init    (unit_dur, freq);
139                tb->vad     = vad_create (unit_dur, freq);
140                tb->agc     = agc_create(sp);
141                tb->unit_dur = unit_dur;
142                tb->channels = channels;
143                tb->mean_read_dur = unit_dur;
144               
145                pb_create(&tb->audio_buffer, (playoutfreeproc)tx_unit_destroy);
146                pb_create(&tb->media_buffer, (playoutfreeproc)media_data_destroy);
147                pb_create(&tb->channel_buffer, (playoutfreeproc)channel_data_destroy);
148
149                *ntb = tb;
150                return TRUE;
151        }
152        return FALSE;
153}
154
155void
156tx_destroy(tx_buffer **ptb)
157{
158        tx_buffer *tb;
159
160        assert(ptb != NULL);
161        tb = *ptb;
162        assert(tb != NULL);
163
164        bias_ctl_destroy(tb->bc);
165        sd_destroy(tb->sd_info);
166        vad_destroy(tb->vad);
167        agc_destroy(tb->agc);
168
169        pb_destroy(&tb->audio_buffer);
170        pb_destroy(&tb->media_buffer);
171        pb_destroy(&tb->channel_buffer);
172
173        xfree(tb);
174        *ptb = NULL;
175}
176
177/* These routines are called when the button on the interface is toggled */
178void
179tx_start(tx_buffer *tb)
180{
181        tx_unit *tu_new;
182        ts_t     unit_start;
183
184        /* Not sure why this is here (?) */
185        if (tb->sending_audio) {
186                debug_msg("Why? Fix");
187                abort();
188                return;
189        }
190
191        tb->sending_audio = TRUE;
192
193        /* Turn off auto lecture */
194        tb->sp->auto_lecture = 1;       
195
196        /* Reset signal classification and auto-scaling */
197        sd_reset(tb->sd_info);
198        vad_reset(tb->vad);
199        agc_reset(tb->agc);
200
201        /* Attach iterator for silence classification */
202        pb_iterator_create(tb->audio_buffer, &tb->transmit);
203        pb_iterator_create(tb->audio_buffer, &tb->silence);
204        pb_iterator_create(tb->audio_buffer, &tb->reading);
205        assert(pb_iterator_count(tb->audio_buffer) == 3);
206
207        /* Add one unit to media buffer to kick off audio reading */
208        unit_start = tb->sp->cur_ts;
209        tx_unit_create(tb, &tu_new, tb->unit_dur * tb->channels);
210        assert(ts_valid(unit_start));
211        pb_add(tb->audio_buffer,
212               (u_char*)tu_new,
213               sizeof(tx_unit),
214               unit_start);
215
216        /* And then put reading iterator on it */
217        pb_iterator_advance(tb->reading);
218
219        assert(tb->state_store == NULL);
220        codec_state_store_create(&tb->state_store, ENCODER);
221        if (tb->sp->detect_silence == FALSE) {
222                ui_info_activate(tb->sp, rtp_my_ssrc(tb->sp->rtp_session[0]));
223        }
224
225        tb->bps_last_update = tb->sp->cur_ts;
226}
227
228void
229tx_stop(tx_buffer *tb)
230{
231        struct timeval tv;
232
233        if (tb->sending_audio == FALSE) {
234                return;
235        }
236
237        gettimeofday(&tv, NULL);
238        tb->sp->auto_lecture  = tv.tv_sec;
239        codec_state_store_destroy(&tb->state_store);
240        channel_encoder_reset(tb->sp->channel_coder);
241        ui_input_level(tb->sp, 0);
242        tb->sending_audio = FALSE;
243        tx_update_ui(tb);
244        /* Detach iterators      */
245        assert(pb_iterator_count(tb->audio_buffer) == 3);
246        pb_iterator_destroy(tb->audio_buffer, &tb->transmit);
247        pb_iterator_destroy(tb->audio_buffer, &tb->silence);
248        pb_iterator_destroy(tb->audio_buffer, &tb->reading);
249        assert(pb_iterator_count(tb->audio_buffer) == 0);
250
251        /* Drain playout buffers */
252        pb_flush(tb->audio_buffer);
253        pb_flush(tb->media_buffer);
254        pb_flush(tb->channel_buffer);
255
256        tb->bps_bytes_sent = 0;
257}
258
259
260int
261tx_read_audio(tx_buffer *tb)
262{
263        session_t  *sp;
264        tx_unit         *u;
265        uint32_t          read_dur = 0, this_read, ulen, freq;
266        ts_t             u_ts;
267
268        assert(tb->channels > 0 && tb->channels <= 2);
269
270        sp = tb->sp;
271        if (tb->sending_audio) {
272                int filled_unit;
273                assert(pb_iterator_count(tb->audio_buffer) == 3);
274                freq = get_freq(tb->clock);
275                do {
276                        if (pb_iterator_get_at(tb->reading, (u_char**)&u, &ulen, &u_ts) == FALSE) {
277                                debug_msg("Reading iterator failed to get unit!\n");
278                        }
279                        assert(u != NULL);
280
281                        this_read = audio_read(sp->audio_device,
282                                               u->data + u->dur_used * tb->channels,
283                                               (tb->unit_dur - u->dur_used) * tb->channels) / tb->channels;
284                        assert(this_read <= tb->unit_dur - u->dur_used);
285                        if (sp->in_file) {
286                                snd_read_audio(&sp->in_file,
287                                                u->data + u->dur_used * tb->channels,
288                                                (uint16_t)(this_read * tb->channels));
289                        }
290
291                        filled_unit = FALSE;
292                        u->dur_used += this_read;
293                        if (u->dur_used == tb->unit_dur) {
294                                read_dur += tb->unit_dur;
295                                time_advance(sp->clock, freq, tb->unit_dur);
296                                u_ts = ts_add(u_ts, ts_map32(get_freq(tb->clock), tb->unit_dur));
297                                tx_unit_create(tb, &u, tb->unit_dur * tb->channels);
298                                pb_add(tb->audio_buffer, (u_char*)u, ulen, u_ts);
299                                pb_iterator_advance(tb->reading);
300                                filled_unit = TRUE;
301                        }
302                } while (filled_unit == TRUE);
303                assert(pb_iterator_count(tb->audio_buffer) == 3);
304        } else {
305                int this_read = 0;
306                /* We're not sending, but have access to the audio device. Read the audio anyway. */
307                /* to get exact timing values, and then throw the data we've just read away...    */
308                do {
309                        this_read = audio_read(sp->audio_device, dummy_buf, DEVICE_REC_BUF / 4) / sp->tb->channels;
310                        read_dur += this_read;
311                } while (this_read > 0);
312                time_advance(sp->clock, get_freq(sp->tb->clock), read_dur);
313        }
314
315        if (read_dur >= (uint32_t)(DEVICE_REC_BUF / (4 * tb->channels))) {
316                debug_msg("Read a lot of audio %d\n", read_dur);
317                if (tb->sending_audio) {
318                        debug_msg("Resetting transmitter\n");
319                        tx_stop(tb);
320                        tx_start(tb);
321                }
322        }
323       
324        if (read_dur) {
325                sp->tb->mean_read_dur += ((double)read_dur - sp->tb->mean_read_dur) / 8.0;
326        }
327
328        assert(read_dur < 0x7fffffff);
329       
330        return read_dur;
331}
332
333int
334tx_process_audio(tx_buffer *tb)
335{
336        session_t       *sp;
337        struct s_pb_iterator *marker;
338        tx_unit              *u;
339        uint32_t               u_len;
340        ts_t                  u_ts;
341        int                   to_send;
342       
343        assert(tb->sending_audio);
344       
345        sp = tb->sp;
346        session_validate(sp);
347
348        /* Do signal classification up until read point, that
349         * is not a complete audio frame so cannot be done
350         */
351        assert(pb_iterator_count(tb->audio_buffer) == 3);
352        pb_iterator_get_at(tb->silence, (u_char**)&u, &u_len, &u_ts);
353        while (pb_iterators_equal(tb->silence, tb->reading) == FALSE) {
354                bias_remove(tb->bc, u->data, u->dur_used * tb->channels);
355                u->energy = avg_audio_energy(u->data, u->dur_used * tb->channels, tb->channels);
356                u->send   = FALSE;
357               
358                /* Silence classification on this block */
359                u->silence = sd(tb->sd_info, (uint16_t)u->energy);
360
361                /* Pass decision to voice activity detector (damps transients, etc) */
362                to_send    = vad_to_get(tb->vad,
363                                        (u_char)u->silence,
364                                        (u_char)((sp->lecture) ? VAD_MODE_LECT : VAD_MODE_CONF));           
365                agc_update(tb->agc, (uint16_t)u->energy, vad_talkspurt_no(tb->vad));
366               
367                if (sp->detect_silence) {
368                        if (to_send != 0) {
369                                pb_iterator_dup(&marker, tb->silence);
370                                while(u != NULL && to_send != 0) {
371                                        u->send = TRUE;
372                                        to_send --;
373                                        pb_iterator_retreat(marker);
374                                        pb_iterator_get_at(marker, (u_char**)&u, &u_len, &u_ts);
375                                }
376                                pb_iterator_destroy(tb->audio_buffer, &marker);
377                        }
378                        assert(pb_iterator_count(tb->audio_buffer) == 3);
379                } else {
380                        u->silence = FALSE;
381                        u->send    = TRUE;
382                }
383                pb_iterator_advance(tb->silence);
384                pb_iterator_get_at(tb->silence, (u_char**)&u, &u_len, &u_ts);
385        }
386
387        if (sp->agc_on == TRUE &&
388            agc_apply_changes(tb->agc) == TRUE) {
389                ui_update_input_gain(sp);
390        }
391
392        return TRUE;
393}
394
395static int
396tx_encode(struct s_codec_state_store *css,
397          sample     *buf,
398          uint32_t     dur_used,
399          uint32_t     encoding,
400          u_char     *payloads,
401          coded_unit **coded)
402{
403        codec_id_t id;
404        uint32_t    i;
405
406        id = codec_get_by_payload(payloads[encoding]);
407        assert(id);
408
409        /* Look to see if we have already coded this unit,
410         * i.e. we are using redundancy.  Don't want to code
411         * twice since it screws up encoder state.
412         */
413       
414        for (i = 0; i < encoding; i++) {
415                if (coded[i]->id == id) {
416                        break;
417                }
418        }
419
420        if (i == encoding) {
421                const codec_format_t *cf;
422                coded_unit native;
423                codec_state *cs;
424               
425                /* Unit does not exist already */
426                cf = codec_get_format(id);
427               
428                /* native is a temporary coded_unit that we use to pass to
429                 * codec_encode since this take a 'native' (raw) coded unit as
430                 * input and fills in coded with the transformed data.
431                 */
432                native.id        = codec_get_native_coding((uint16_t)cf->format.sample_rate,
433                                                           (uint16_t)cf->format.channels);
434                native.state     = NULL;
435                native.state_len = 0;
436                native.data      = (u_char*)buf;
437                native.data_len  = (uint16_t)(dur_used * sizeof(sample) * cf->format.channels);
438               
439                /* Get codec state from those stored for us */
440                cs = codec_state_store_get(css, id);
441                return codec_encode(cs, &native, coded[encoding]);
442        } else {
443                /* duplicate coded unit */
444                return coded_unit_dup(coded[encoding], coded[i]);
445        }
446}
447
448void
449tx_send(tx_buffer *tb)
450{
451        struct s_pb_iterator    *cpos;
452        channel_data            *cd;
453        channel_unit            *cu;
454        session_t               *sp;
455        tx_unit                 *u;
456        ts_t                     u_ts, u_sil_ts, delta;
457        ts_t                     time_ts;
458        uint32_t                 time_32, cd_len, freq;
459        uint32_t                 u_len, units, i, j, k, n, send, encoding;
460        int                      success;
461       
462        assert(pb_iterator_count(tb->audio_buffer) == 3);
463
464        if (pb_iterators_equal(tb->silence, tb->transmit)) {
465                /* Nothing to do */
466                debug_msg("Nothing to do\n");
467                return;
468        }
469
470#ifndef NDEBUG
471        {
472                struct s_pb *buf;
473                buf = pb_iterator_get_playout_buffer(tb->transmit);
474                assert(pb_iterator_count(buf) == 3);
475        }
476#endif /* NDEBUG */
477
478        pb_iterator_get_at(tb->silence,  (u_char**)&u, &u_len, &u_sil_ts);
479        pb_iterator_get_at(tb->transmit, (u_char**)&u, &u_len, &u_ts);
480
481        assert(ts_gt(u_sil_ts, u_ts));
482
483        delta = ts_sub(u_sil_ts, u_ts);
484        n = delta.ticks / tb->unit_dur;
485
486        sp = tb->sp;
487        session_validate(sp);
488        units = channel_encoder_get_units_per_packet(sp->channel_coder);
489        freq  = get_freq(tb->clock);
490       
491        while(n > units) {
492                send = FALSE;
493
494                /* Check whether we want to send this group of units */
495                for (i = 0; i < units; i++) {
496                        pb_iterator_get_at(tb->transmit, (u_char**)&u, &u_len, &u_ts);
497                        if (u->send) {
498                                send = TRUE;
499                                break;
500                        }
501                        pb_iterator_advance(tb->transmit);
502                }
503
504                /* Rewind transmit point to where it was before we did
505                 * last check */
506                while(i > 0) {
507                        pb_iterator_retreat(tb->transmit);
508                        i--;
509                }
510               
511                for (i = 0;i < units; i++) {
512                        media_data *m;
513                        success = pb_iterator_get_at(tb->transmit, (u_char**)&u, &u_len, &u_ts);
514                        assert(success);
515                        if (send) {
516                                media_data_create(&m, sp->num_encodings);
517                                for(encoding = 0; encoding < (uint32_t)sp->num_encodings; encoding ++) {
518                                        tx_encode(tb->state_store,
519                                                  u->data,
520                                                  u->dur_used,
521                                                  encoding,
522                                                  sp->encodings,
523                                                  m->rep);
524                                }
525                        } else {
526                                media_data_create(&m, 0);
527                        }
528                        assert(m != NULL);
529                        success = pb_add(tb->media_buffer,
530                                         (u_char*)m,
531                                         sizeof(media_data),
532                                         u_ts);
533                        assert(success);
534                        success = pb_iterator_advance(tb->transmit);
535                        assert(success);
536                }
537                n -= units;
538        }
539
540        channel_encoder_encode(sp->channel_coder, tb->media_buffer, tb->channel_buffer);
541
542        pb_iterator_create(tb->channel_buffer, &cpos);
543        pb_iterator_advance(cpos);
544        while(pb_iterator_detach_at(cpos, (u_char**)&cd, &cd_len, &time_ts)) {
545                uint32_t csrc[16];
546                char *data, pt;
547                int   data_len, done;
548                int  marker;
549
550                /* Set up fields for RTP header */
551                cu = cd->elem[0];
552                pt = channel_coder_get_payload(sp->channel_coder, cu->pt);
553                time_32 = ts_seq32_out(&tb->up_seq, freq, time_ts);
554                if (time_32 - sp->last_depart_ts != units * tb->unit_dur) {
555                        marker = 1;
556                        debug_msg("new talkspurt\n");
557                } else {
558                        marker = 0;
559                }   
560               
561                /* layer loop starts here */
562                for(j = 0; j < (uint32_t)sp->layers; j++) {
563                        data_len = 0;
564                        /* determine data length for packet.  This is a   */ 
565                        /* little over complicated because of layering... */
566                        for(i = j; i < cd->nelem; i += sp->layers) {
567                                data_len += (int) cd->elem[i]->data_len;
568                                k++;
569                        }
570
571                        /* Copy all out going data into one block (no scatter) */
572                        data = (char*)block_alloc(data_len);
573                        done = 0;
574                        for(i = j; i < cd->nelem; i += sp->layers) {
575                                memcpy(data + done, cd->elem[i]->data, cd->elem[i]->data_len);
576                                done += cd->elem[i]->data_len;
577                        }
578                        rtp_send_data(sp->rtp_session[j], time_32, pt, marker, 0, csrc, data, data_len, NULL, 0);
579                        block_free(data, data_len);
580                        tb->bps_bytes_sent += data_len;
581                }
582                /* layer loop ends here */
583               
584                sp->last_depart_ts  = time_32;
585                channel_data_destroy(&cd, sizeof(channel_data));
586        }
587        pb_iterator_destroy(tb->channel_buffer, &cpos);
588
589        /* Drain tb->audio, remove every older than silence position
590         * by two packets worth of audio.  Note tb->media is drained
591         * by the channel encoding stage and tb->channel is drained
592         * in the act of transmission with pbi_detach_at call.
593         */
594        u_ts = ts_map32(get_freq(tb->clock), 2 * units * tb->unit_dur);
595
596        {
597                struct s_pb *buf;
598                buf = pb_iterator_get_playout_buffer(tb->transmit);
599                assert(pb_iterator_count(buf) == 3);
600        }
601
602        assert(pb_iterator_count(tb->audio_buffer) == 3);       
603        n = pb_iterator_audit(tb->transmit, u_ts);
604}
605
606void
607tx_update_ui(tx_buffer *tb)
608{
609        session_t       *sp           = tb->sp;
610
611        if (sp->meter && tb->sending_audio) {
612                struct s_pb_iterator *prev; 
613                tx_unit              *u;
614                uint32_t               u_len;
615                ts_t                  u_ts;
616
617                /* Silence point should be upto read point here so use last
618                 * completely read unit.
619                 */
620                assert(pb_iterator_count(tb->audio_buffer) == 3);
621                pb_iterator_dup(&prev, tb->silence);
622                pb_iterator_retreat(prev);
623                if (pb_iterators_equal(tb->silence, prev)) {
624                        pb_iterator_destroy(tb->audio_buffer, &prev);
625                        return;
626                }
627                if (pb_iterator_get_at(prev, (u_char**)&u, &u_len, &u_ts) &&
628                    (vad_in_talkspurt(sp->tb->vad) == TRUE || sp->detect_silence == FALSE)) {
629                        ui_input_level(sp, lin2vu(u->energy, 100, VU_INPUT));
630                } else {
631                        ui_input_level(sp, 0);
632                }
633                pb_iterator_destroy(tb->audio_buffer, &prev);
634                assert(pb_iterator_count(tb->audio_buffer) == 3);
635        }
636        /* This next routine is really inefficient - we only need do ui_info_activate() */
637        /* when the state changes, else we flood the mbus with redundant messages.      */
638        if (sp->detect_silence && vad_in_talkspurt(sp->tb->vad) == TRUE) {
639                if (!sp->ui_activated) {
640                        sp->ui_activated = TRUE;
641                        ui_info_activate(sp, rtp_my_ssrc(sp->rtp_session[0]));
642                }
643                if (sp->lecture) {
644                        sp->lecture = FALSE;
645                        ui_update_lecture_mode(sp);
646                }
647        } else {
648                if (sp->ui_activated) {
649                        sp->ui_activated = FALSE;
650                        ui_info_deactivate(sp, rtp_my_ssrc(sp->rtp_session[0]));
651                }
652        }
653        if (tb->sending_audio == FALSE) {
654                ui_info_deactivate(sp, rtp_my_ssrc(sp->rtp_session[0]));
655        }
656}
657
658void
659tx_igain_update(tx_buffer *tb)
660{
661        sd_reset(tb->sd_info);
662        agc_reset(tb->agc);
663}
664
665int
666tx_is_sending(tx_buffer *tb)
667{
668        return tb->sending_audio;
669}
670
671double
672tx_get_bps(tx_buffer *tb)
673{
674        if (tb->bps_bytes_sent == 0) {
675                return 0.0;
676        } else {
677                uint32_t dms;
678                double  bps;
679                ts_t delta = ts_abs_diff(tb->bps_last_update, tb->sp->cur_ts);
680                dms        = ts_to_us(delta);
681                bps        = tb->bps_bytes_sent * 8e6 / (double)dms;
682                tb->bps_bytes_sent  = 0;
683                tb->bps_last_update = tb->sp->cur_ts;
684                return bps;
685        }
686}
Note: See TracBrowser for help on using the browser.