2
mediastreamer2 library - modular sound and video processing and streaming
3
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
5
This program is free software; you can redistribute it and/or
6
modify it under the terms of the GNU General Public License
7
as published by the Free Software Foundation; either version 2
8
of the License, or (at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
#include "mediastreamer2/msfilter.h"
23
#define CONF_GRAN_MAX 12 /* limit for 'too much data' */
27
#define CONF_GRAN (160)
29
#define CONF_NSAMPLES (CONF_GRAN/2)
31
#define CONF_MAX_PINS 32
34
typedef struct Channel{
36
int16_t input[CONF_NSAMPLES];
37
bool_t has_contributed;
45
typedef struct ConfState{
46
Channel channels[CONF_MAX_PINS];
47
int sum[CONF_NSAMPLES];
51
static void channel_init(Channel *chan){
52
ms_bufferizer_init(&chan->buff);
55
static void channel_uninit(Channel *chan){
56
ms_bufferizer_uninit(&chan->buff);
59
static void conf_init(MSFilter *f){
60
ConfState *s=ms_new0(ConfState,1);
62
for (i=0;i<CONF_MAX_PINS;i++)
63
channel_init(&s->channels[i]);
67
static void conf_uninit(MSFilter *f){
68
ConfState *s=(ConfState*)f->data;
70
for (i=0;i<CONF_MAX_PINS;i++)
71
channel_uninit(&s->channels[i]);
75
static void conf_preprocess(MSFilter *f){
77
ConfState *s=(ConfState*)f->data;
79
for (i=0;i<CONF_MAX_PINS;i++)
81
s->channels[i].is_used=FALSE;
82
s->channels[i].missed=0;
89
static bool_t should_process(MSFilter *f, ConfState *s){
94
bool_t has_too_much_data=FALSE;
96
for (i=0;i<CONF_MAX_PINS;++i){
97
if (f->inputs[i]!=NULL){
100
if (ms_bufferizer_get_avail(&chan->buff)>=CONF_GRAN)
102
if (ms_bufferizer_get_avail(&chan->buff)>=CONF_GRAN*CONF_GRAN_MAX){
103
has_too_much_data=TRUE;
109
return has_too_much_data || (connected==has_data);
114
static bool_t should_process(MSFilter *f, ConfState *s){
116
int do_process=FALSE;
121
bool_t has_too_much_data=FALSE;
124
if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>CONF_GRAN
125
&& s->channels[0].is_used==FALSE)
127
/* soundread has just started */
128
s->channels[0].is_used=TRUE;
130
else if (s->channels[0].is_used==FALSE)
135
/* check wheter streams are used */
136
for (i=1;i<CONF_MAX_PINS;++i){
137
if (f->inputs[i]!=NULL && (i%2==1) && s->channels[i].is_used==FALSE){
138
chan=&s->channels[i];
139
if (ms_bufferizer_get_avail(&chan->buff)>=3*CONF_GRAN)
142
/* new contributing stream :) */
143
ms_message("msconf: new contributing stream %i", i);
144
s->channels[i].is_used=TRUE;
145
s->channels[i].missed=0;
147
/* reinitialize checking streams */
148
s->channels[i].diff=0;
149
for (k=0;k<CONF_MAX_PINS;++k){
150
if (f->inputs[k]!=NULL){
151
chan=&s->channels[k];
152
s->channels[i].diff = ms_bufferizer_get_avail(&chan->buff);
159
/* decide wheter to process or not */
160
if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)<CONF_GRAN)
164
else if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>=CONF_GRAN*CONF_GRAN_MAX)
169
/* disable streams that are not contributing any more */
170
for (i=1;i<CONF_MAX_PINS;++i){
171
if (f->inputs[i]!=NULL && (i%2==1) && s->channels[i].is_used==TRUE){
172
chan=&s->channels[i];
173
if (ms_bufferizer_get_avail(&chan->buff)<CONF_GRAN)
174
s->channels[i].missed++;
175
if (ms_bufferizer_get_avail(&chan->buff)<CONF_GRAN
176
&& s->channels[i].missed==4)
178
/* delete from contributing stream :( */
179
s->channels[i].is_used=FALSE;
180
s->channels[i].missed=0;
181
ms_message("msconf: contributing stream deleted %i", i);
189
/* if a stream is_used, then check its availability.
190
if a stream !is_used, then don't check it.
191
only check RTP incoming stream pins.
193
for (i=1;i<CONF_MAX_PINS;++i){
194
if (f->inputs[i]!=NULL && (i%2==1) && s->channels[i].is_used==TRUE){
195
chan=&s->channels[i];
197
if (ms_bufferizer_get_avail(&chan->buff)>=CONF_GRAN)
201
if (connected==has_data)
205
if (do_process==TRUE && s->channels[0].missed==500)
207
s->channels[0].missed=0;
208
/* compare incoming and soundread streams */
209
for (i=1;i<CONF_MAX_PINS;++i){
210
if (f->inputs[i]!=NULL && (i%2==1) && s->channels[i].is_used==TRUE){
213
chan=&s->channels[i];
214
old_diff = s->channels[0].diff - s->channels[i].diff;
215
new_diff = ms_bufferizer_get_avail(&s->channels[0].buff) - ms_bufferizer_get_avail(&s->channels[i].buff);
216
if (new_diff-old_diff>(CONF_GRAN*2))
218
ms_message("msconf: missing data from contributing stream", new_diff-old_diff);
220
else if (old_diff-new_diff>(CONF_GRAN*2))
222
int xtra_data = ms_bufferizer_get_avail(&chan->buff) - 6*CONF_GRAN;
226
while (ms_bufferizer_get_avail(&chan->buff)>CONF_GRAN*6)
229
ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,CONF_GRAN);
232
ms_message("msconf: extra data from contributing stream %i", old_diff-new_diff, k*CONF_GRAN);
238
else if (do_process==TRUE)
239
s->channels[0].missed++;
242
return (do_process==TRUE);
244
/* case where: every one has enough data */
245
if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)<CONF_GRAN)
249
if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>=CONF_GRAN*CONF_GRAN_MAX)
251
for (i=1;i<CONF_MAX_PINS;++i){
252
if (f->inputs[i]!=NULL && (i%2==1)){
253
chan=&s->channels[i];
255
if (ms_bufferizer_get_avail(&chan->buff)>=CONF_GRAN)
257
if (ms_bufferizer_get_avail(&chan->buff)>=CONF_GRAN*CONF_GRAN_MAX){
258
has_too_much_data=TRUE;
264
/* don't wait for missing incoming RTP packets */
265
/* return has_too_much_data || (connected==has_data); */
270
/* The conversation timing HAS TO BE driven by soundread wich is
271
the best clock we can found.
273
To make the whole process obey this clock, the decision to
274
process data for conference is decided by pin0 (soundread).
277
if (s->is_starting==TRUE)
279
/* we don't want to wait for incoming stream before
280
we send outgoing stream! */
282
for (i=1;i<CONF_MAX_PINS;++i){
283
if (f->inputs[i]!=NULL && (i%2==1)){
284
chan=&s->channels[i];
285
if (ms_bufferizer_get_avail(&chan->buff)>=CONF_GRAN)
287
s->is_starting=FALSE;
292
if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>=CONF_GRAN)
299
if (has_too_much_data==TRUE)
302
chan=&s->channels[0];
303
discarded_data = ms_bufferizer_get_avail(&chan->buff);
304
/* check if the inputs provide enough data */
305
if (discarded_data>=CONF_GRAN*CONF_GRAN_MAX)
307
while (ms_bufferizer_get_avail(&chan->buff)>CONF_GRAN*4)
309
ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,CONF_GRAN);
311
ms_message("msconf: data from soundread -> (%i discarded)", discarded_data - ms_bufferizer_get_avail(&chan->buff));
313
for (i=1;i<CONF_MAX_PINS;i=i+2){
314
if (f->inputs[i]!=NULL){
315
chan=&s->channels[i];
316
discarded_data = ms_bufferizer_get_avail(&chan->buff);
317
while (ms_bufferizer_get_avail(&chan->buff)>CONF_GRAN*4)
319
ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,CONF_GRAN);
321
ms_message("msconf: data from channel%i -> (%i discarded)", i, discarded_data - ms_bufferizer_get_avail(&chan->buff));
327
for (i=1;i<CONF_MAX_PINS;i=i+2){
328
if (f->inputs[i]!=NULL){
329
chan=&s->channels[i];
330
discarded_data = ms_bufferizer_get_avail(&chan->buff);
331
if (discarded_data>=CONF_GRAN*CONF_GRAN_MAX)
333
while (ms_bufferizer_get_avail(&chan->buff)>CONF_GRAN*4)
335
ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,CONF_GRAN);
337
ms_message("msconf: data from channel%i -> (%i discarded)", i, discarded_data - ms_bufferizer_get_avail(&chan->buff));
344
return has_too_much_data || (connected==has_data);
350
static void conf_sum(ConfState *s){
353
memset(s->sum,0,CONF_NSAMPLES*sizeof(int));
354
for (i=0;i<CONF_MAX_PINS;++i){
355
chan=&s->channels[i];
356
if (ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,CONF_GRAN)
358
for(j=0;j<CONF_NSAMPLES;++j){
359
s->sum[j]+=chan->input[j];
361
chan->has_contributed=TRUE;
363
chan->has_contributed=FALSE;
368
static inline int16_t saturate(int sample){
371
else if (sample<-32000)
373
return (int16_t)sample;
376
static mblk_t * conf_output(ConfState *s, Channel *chan){
377
mblk_t *m=allocb(CONF_GRAN,0);
380
if (chan->has_contributed==TRUE){
381
for (i=0;i<CONF_NSAMPLES;++i){
382
tmp=s->sum[i]-(int)chan->input[i];
383
*((int16_t*)m->b_wptr)=saturate(tmp);
387
for (i=0;i<CONF_NSAMPLES;++i){
389
*((int16_t*)m->b_wptr)=saturate(tmp);
396
static void conf_dispatch(MSFilter *f, ConfState *s){
400
//memset(s->sum,0,CONF_NSAMPLES*sizeof(int));
401
for (i=0;i<CONF_MAX_PINS;++i){
402
if (f->outputs[i]!=NULL){
403
chan=&s->channels[i];
404
m=conf_output(s,chan);
405
ms_queue_put(f->outputs[i],m);
410
static void conf_process(MSFilter *f){
412
ConfState *s=(ConfState*)f->data;
414
/*read from all inputs and put into bufferizers*/
415
for (i=0;i<CONF_MAX_PINS;++i){
416
if (f->inputs[i]!=NULL){
417
chan=&s->channels[i];
418
ms_bufferizer_put_from_queue(&chan->buff,f->inputs[i]);
423
while(should_process(f,s)==TRUE){
431
MSFilterDesc ms_conf_desc={
434
"A filter to make conferencing",
449
MSFilterDesc ms_conf_desc={
452
.text="A filter to make conferencing",
453
.category=MS_FILTER_OTHER,
454
.ninputs=CONF_MAX_PINS,
455
.noutputs=CONF_MAX_PINS,
457
.preprocess=conf_preprocess,
458
.process=conf_process,
464
MS_FILTER_DESC_EXPORT(ms_conf_desc)