source: mediastreamer2/linphone/mediastreamer2/src/msconf.c @ 241:4eec3ed19919

Last change on this file since 241:4eec3ed19919 was 241:4eec3ed19919, checked in by aymeric <aymeric@…>, 4 years ago

add half duplex simulation with VAD for noisy environment.

git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@244 3f6dc0c8-ddfe-455d-9043-3cd528dc4637

File size: 16.5 KB
Line 
1/*
2mediastreamer2 library - modular sound and video processing and streaming
3Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18*/
19
20#include "mediastreamer2/msfilter.h"
21
22#if defined(_WIN32_WCE)
23#define DISABLE_SPEEX
24#endif
25
26#ifndef DISABLE_SPEEX
27#include <speex/speex_preprocess.h>
28#endif
29
30#ifndef CONF_GRAN_MAX
31#define CONF_GRAN_MAX 12 /* limit for 'too much data' */
32#endif
33
34//#ifndef CONF_GRAN
35//#define CONF_GRAN (160*4)
36//#endif
37#define CONF_NSAMPLES 160*4*4 /* (CONF_GRAN/2) */
38#ifndef CONF_MAX_PINS
39#define CONF_MAX_PINS 32
40#endif
41
42typedef struct Channel{
43        MSBufferizer buff;
44        int16_t input[CONF_NSAMPLES];
45        bool_t has_contributed;
46        bool_t is_used;
47
48        int is_speaking;
49
50        int count;
51        int missed;
52
53        int stat_discarded;
54        int stat_missed;
55        int stat_processed;
56
57#ifndef DISABLE_SPEEX
58        SpeexPreprocessState *speex_pp;
59#endif
60
61} Channel;
62
63typedef struct ConfState{
64        Channel channels[CONF_MAX_PINS];
65        int sum[CONF_NSAMPLES];
66        int enable_directmode;
67        int enable_vad;
68
69        int enable_halfduplex;
70        int vad_prob_start;
71        int vad_prob_continue;
72
73        int agc_level;
74        int mix_mode;
75        int samplerate;
76
77        int adaptative_msconf_buf;
78        int conf_gran;
79        int conf_nsamples;
80} ConfState;
81
82
83static void channel_init(ConfState *s, Channel *chan, int pos){
84        memset(chan, 0, sizeof(Channel));
85        ms_bufferizer_init(&chan->buff);
86#ifndef DISABLE_SPEEX
87        //chan->speex_pp = speex_preprocess_state_init((s->conf_gran/2) *(s->samplerate/8000), s->samplerate);
88        chan->speex_pp = speex_preprocess_state_init(s->conf_gran/2, s->samplerate);
89        if (chan->speex_pp!=NULL) {
90                float f;
91                int val;
92                if (pos==0)
93                val=1;
94    else
95                val=0;
96                speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DENOISE, &val);
97                /* enable VAD only on incoming RTP stream */
98                if (pos%2==1)
99                {
100                        val=1;
101                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_VAD, &val);
102                }
103                else if (pos==0 && s->enable_halfduplex>0)
104                {
105                        val=1;
106                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_VAD, &val);
107                        val = s->vad_prob_start; // xx%
108                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_PROB_START, &val);
109                        val = s->vad_prob_continue; // xx%
110                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &val);
111                }
112
113                /* enable AGC only on local soundcard */
114                if (s->agc_level>0 && pos==0)
115                {
116                        val=1;
117                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC, &val);
118                        f=s->agc_level;
119                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
120#if 0
121                        val=40;
122                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &val);
123#endif
124                }
125                else
126                {
127                        val=0;
128                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC, &val);
129                        f=8000;
130                        speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &f);
131                }
132                val=0;
133                speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB, &val);
134                f=.4;
135                speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
136                f=.3;
137                speex_preprocess_ctl(chan->speex_pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
138        }
139#endif
140}
141
142static void channel_uninit(Channel *chan){
143        ms_bufferizer_uninit(&chan->buff);
144#ifndef DISABLE_SPEEX
145        if (chan->speex_pp!=NULL)
146            speex_preprocess_state_destroy(chan->speex_pp);
147        chan->speex_pp=NULL;
148#endif
149}
150
151static void conf_init(MSFilter *f){
152        ConfState *s=(ConfState *)ms_new0(ConfState,1);
153        int i;
154        s->samplerate=8000;
155        s->conf_gran=((16 * s->samplerate) / 800) *2;
156        s->conf_nsamples=s->conf_gran/2;
157    for (i=0;i<CONF_MAX_PINS;i++)
158                channel_init(s, &s->channels[i], i);
159        s->enable_directmode=FALSE;
160        s->enable_vad=TRUE;
161        s->agc_level=0;
162        s->mix_mode=TRUE;
163        s->adaptative_msconf_buf=2;
164        f->data=s;
165}
166
167static void conf_uninit(MSFilter *f){
168        ConfState *s=(ConfState*)f->data;
169        int i;
170        for (i=0;i<CONF_MAX_PINS;i++)
171                channel_uninit(&s->channels[i]);
172        ms_free(f->data);
173}
174
175static void conf_preprocess(MSFilter *f){
176        ConfState *s=(ConfState*)f->data;
177        int i;
178        for (i=0;i<CONF_MAX_PINS;i++)
179          {
180            s->channels[i].is_used=FALSE;
181            s->channels[i].missed=0;
182            s->channels[i].stat_discarded=0;
183            s->channels[i].stat_missed=0;
184            s->channels[i].stat_processed=0;
185          }
186}
187
188static bool_t should_process(MSFilter *f, ConfState *s){
189        Channel *chan;
190        int active_channel=0;
191        int i;
192
193        if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>s->conf_gran
194            && s->channels[0].is_used==FALSE)
195          {
196            /* soundread has just started */
197            s->channels[0].is_used=TRUE;
198          }
199        else if (s->channels[0].is_used==FALSE)
200          {
201            return FALSE;
202          }
203
204        /* count active channel */
205        for (i=1;i<CONF_MAX_PINS;++i){
206                chan=&s->channels[i];
207                if (chan->is_used == TRUE)
208                {
209                        active_channel++;
210                }
211        }
212
213        if (active_channel<=1) /* disable mix mode when it's not needed */
214                s->mix_mode = FALSE;
215        else
216                s->mix_mode = TRUE;
217
218        if (s->enable_directmode==FALSE)
219        {
220                s->mix_mode = TRUE;
221        }
222
223        if (s->mix_mode == FALSE)
224                return FALSE;
225
226        if (ms_bufferizer_get_avail(&(&s->channels[0])->buff)>=s->conf_gran)
227        {
228                return TRUE;
229        }
230        return FALSE;
231}
232
233static void conf_sum(ConfState *s){
234        int i,j;
235        Channel *chan;
236        memset(s->sum,0,s->conf_nsamples*sizeof(int));
237
238        chan=&s->channels[0];
239        if (s->adaptative_msconf_buf*s->conf_gran<ms_bufferizer_get_avail(&chan->buff))
240        {
241                i = ms_bufferizer_get_avail(&chan->buff)/s->conf_gran;
242                if (i>5)
243                        ms_message("Increasing buffer because sound card is late. (nb_buf=%i /old=%i)", i, s->adaptative_msconf_buf);
244                s->adaptative_msconf_buf=i;
245                if (s->adaptative_msconf_buf>10)
246                {
247                        while (ms_bufferizer_get_avail(&chan->buff)> s->conf_gran*6)
248                        {
249                                ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
250                                ms_message("Deleting extra sound card data %i", ms_bufferizer_get_avail(&chan->buff));
251                        }
252                }
253        }
254        else if (s->adaptative_msconf_buf*s->conf_gran>ms_bufferizer_get_avail(&chan->buff))
255        {
256                if (s->adaptative_msconf_buf>3)
257                {
258                        s->adaptative_msconf_buf--;
259                        s->adaptative_msconf_buf=ms_bufferizer_get_avail(&chan->buff)/s->conf_gran;
260                        //ms_message("decreasing buffer because sound card is in advance. (nb_buf=%i)", s->adaptative_msconf_buf);
261                }
262        }
263
264        if (s->adaptative_msconf_buf>6)
265                s->adaptative_msconf_buf=6;
266
267        for (i=0;i<CONF_MAX_PINS;++i){
268                chan=&s->channels[i];
269
270                /* skip soundread and short buffer entry */
271                if (i>0 
272                        && ms_bufferizer_get_avail(&chan->buff)> s->conf_gran
273                        && ms_bufferizer_get_avail(&chan->buff)> (ms_bufferizer_get_avail(&s->channels[0].buff)+s->conf_gran*6) )
274                {
275                        while (ms_bufferizer_get_avail(&chan->buff)> (ms_bufferizer_get_avail(&s->channels[0].buff)) )
276                        {
277                                ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
278                                /* we want to remove 4 packets (40ms) in a near future: */
279#ifndef DISABLE_SPEEX
280                                if (chan->speex_pp!=NULL && s->enable_vad==TRUE)
281                                {
282                                        int vad;
283                                        vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
284                                        if (vad==1)
285                                                break; /* voice detected: process as usual */
286                                        if (ms_bufferizer_get_avail(&chan->buff)<s->conf_gran)
287                                                break; /* no more data to remove */
288                                        ms_message("No voice detected: discarding sample. (idx=%i - bufsize=%i sncardbufsize=%i)",
289                                                i, ms_bufferizer_get_avail(&chan->buff), ms_bufferizer_get_avail(&s->channels[0].buff));
290                                }
291                                if (ms_bufferizer_get_avail(&chan->buff) == (ms_bufferizer_get_avail(&s->channels[0].buff)))
292                                        ms_message("same data in soundcard and incoming rtp. (idx=%i - bufsize=%i sncardbufsize=%i)",
293                                                i, ms_bufferizer_get_avail(&chan->buff), ms_bufferizer_get_avail(&s->channels[0].buff));
294#endif
295                                chan->stat_discarded++;
296                        }
297
298                        if (s->channels[0].is_speaking<=0)
299                        {
300                                for(j=0;j<s->conf_nsamples;++j){
301                                        s->sum[j]+=chan->input[j];
302                                }
303                                chan->has_contributed=TRUE;
304                        }
305                        chan->stat_processed++;
306                }
307                else if (ms_bufferizer_get_avail(&chan->buff)>=s->conf_gran)
308                {
309                        ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran);
310#ifndef DISABLE_SPEEX
311                        if (chan->speex_pp!=NULL && s->enable_vad==TRUE && i==0)
312                        {
313                                int vad;
314                                vad = speex_preprocess(chan->speex_pp, (short*)chan->input, NULL);
315                                if (s->enable_halfduplex>0)
316                                {
317                                        if (vad>0)
318                                        {
319                                                /* speech detected */
320                                                chan->is_speaking++;
321                                                if (chan->is_speaking>5)
322                                                        chan->is_speaking=5;
323                                        }
324                                        else
325                                        {
326                                                chan->is_speaking--;
327                                                if (chan->is_speaking<-5)
328                                                        chan->is_speaking=-5;
329                                        }
330                                if (chan->is_speaking<=0)
331                                        ms_message("silence on! (%i)", chan->is_speaking);
332                                else
333                                        ms_message("speech on! (%i)", chan->is_speaking);
334                                }
335                        }
336                        else if (chan->speex_pp!=NULL && s->enable_vad==TRUE)
337                        {
338                                speex_preprocess_estimate_update(chan->speex_pp, (short*)chan->input);
339                        }
340#endif
341
342                        if (i==0
343                                || s->channels[0].is_speaking<=0)
344                        {
345                                for(j=0;j<s->conf_nsamples;++j){
346                                        s->sum[j]+=chan->input[j];
347                                }
348                                chan->has_contributed=TRUE;
349                        }
350                        chan->stat_processed++;
351                } else {
352                        chan->stat_missed++;
353                        if (i>0 && chan->is_used == TRUE)
354                        {
355                                chan->missed++;
356                                /* delete stream if data is missing since a long time */
357                                if (chan->missed>15)
358                                {
359                                        chan->is_used=FALSE;
360                                        ms_message("msconf: deleted contributing stream (pin=%i)", i);
361                                }
362                                /* couldn't we add confort noise for those outputs? */
363                        }
364                        chan->has_contributed=FALSE;
365                }
366        }
367        return;
368}
369
370static inline int16_t saturate(int sample){
371        if (sample>32000)
372                sample=32000;
373        else if (sample<-32000)
374                sample=-32000;
375        return (int16_t)sample;
376}
377
378static mblk_t * conf_output(ConfState *s, Channel *chan){
379        mblk_t *m=allocb(s->conf_gran,0);
380        int i;
381        int tmp;
382        if (chan->has_contributed==TRUE){
383                for (i=0;i<s->conf_nsamples;++i){
384                        tmp=s->sum[i]-(int)chan->input[i];
385                        *((int16_t*)m->b_wptr)=saturate(tmp);
386                        m->b_wptr+=2;
387                }
388        }else{
389                for (i=0;i<s->conf_nsamples;++i){
390                        tmp=s->sum[i];
391                        *((int16_t*)m->b_wptr)=saturate(tmp);
392                        m->b_wptr+=2;
393                }
394        }
395        return m;
396}
397
398static void conf_dispatch(MSFilter *f, ConfState *s){
399        int i;
400        Channel *chan;
401        mblk_t *m;
402        //memset(s->sum,0,s->conf_nsamples*sizeof(int));
403        for (i=0;i<CONF_MAX_PINS;++i){
404                if (f->outputs[i]!=NULL){
405                        chan=&s->channels[i];
406                        m=conf_output(s,chan);
407                        ms_queue_put(f->outputs[i],m);
408                }
409        }
410}
411
412static void conf_process(MSFilter *f){
413        int i;
414        ConfState *s=(ConfState*)f->data;
415        Channel *chan;
416        Channel *chan0;
417        /*read from all inputs and put into bufferizers*/
418        for (i=0;i<CONF_MAX_PINS;++i){
419                if (f->inputs[i]!=NULL){
420                        chan=&s->channels[i];
421                        ms_bufferizer_put_from_queue(&chan->buff,f->inputs[i]);
422                        if (ms_bufferizer_get_avail(&chan->buff)>0)
423                        {
424                                chan->missed=0; /* reset counter of missed packet */
425                                if (i>0 && chan->is_used==FALSE)
426                                {
427                                        chan->is_used=TRUE;
428                                        ms_message("msconf: new contributing stream", ms_bufferizer_get_avail(&chan->buff));
429                                }
430                        }
431                }
432        }
433
434        /*do the job */
435        while(should_process(f,s)==TRUE){
436                conf_sum(s);
437                conf_dispatch(f,s);
438        }
439
440        /* mixer is disabled! -> copy A->B and B->A*/
441        if (s->mix_mode == FALSE)
442        {
443                /* get the soundread data and copy it to pinX */
444                for (i=1;i<CONF_MAX_PINS;i=i+2){
445                        if (f->inputs[i]!=NULL){
446                                chan0=&s->channels[0];
447                                chan=&s->channels[i];
448                                if (chan->is_used==TRUE)
449                                {
450                                        while (ms_bufferizer_read(&chan->buff,(uint8_t*)chan->input,s->conf_gran)==s->conf_gran)
451                                        {
452                                                if (f->outputs[0]!=NULL)
453                                                {
454                                                        /* send in pin0 */
455                                                        mblk_t *m=allocb(s->conf_gran,0);
456                                                        memcpy(m->b_wptr, chan->input, s->conf_gran);
457                                                        m->b_wptr+=s->conf_gran;
458                                                        ms_queue_put(f->outputs[0],m);
459                                                }
460                                        }
461                                }
462
463                                if (chan0->is_used==TRUE)
464                                {
465                                        while (ms_bufferizer_read(&chan0->buff,(uint8_t*)chan0->input,s->conf_gran)==s->conf_gran)
466                                        {
467                                                if (f->outputs[i]!=NULL)
468                                                {
469                                                        /* send in pinI */
470                                                        mblk_t *m=allocb(s->conf_gran,0);
471                                                        memcpy(m->b_wptr, chan0->input, s->conf_gran);
472                                                        m->b_wptr+=s->conf_gran;
473                                                        ms_queue_put(f->outputs[i],m);
474                                                }
475                                        }
476                                }
477                                break;
478                        }
479                }
480        }
481
482}
483
484static void conf_postprocess(MSFilter *f){
485        int i;
486        ConfState *s=(ConfState*)f->data;
487        for (i=0;i<CONF_MAX_PINS;i++)
488                channel_uninit(&s->channels[i]);
489    for (i=0;i<CONF_MAX_PINS;i++)
490                channel_init(s, &s->channels[i], i);
491}
492
493static int msconf_set_sr(MSFilter *f, void *arg){
494        ConfState *s=(ConfState*)f->data;
495        int i;
496
497        s->samplerate = *(int*)arg;
498        s->conf_gran = ((16 * s->samplerate) / 800) *2;
499        s->conf_nsamples=s->conf_gran/2;
500        for (i=0;i<CONF_MAX_PINS;i++)
501                channel_uninit(&s->channels[i]);
502    for (i=0;i<CONF_MAX_PINS;i++)
503                channel_init(s, &s->channels[i], i);
504        return 0;
505}
506
507static int msconf_enable_directmode(MSFilter *f, void *arg){
508        ConfState *s=(ConfState*)f->data;
509        s->enable_directmode = *(int*)arg;
510        return 0;
511}
512
513static int msconf_enable_agc(MSFilter *f, void *arg){
514        ConfState *s=(ConfState*)f->data;
515        int i;
516        s->agc_level = *(int*)arg;
517
518        for (i=0;i<CONF_MAX_PINS;i++)
519                channel_uninit(&s->channels[i]);
520    for (i=0;i<CONF_MAX_PINS;i++)
521                channel_init(s, &s->channels[i], i);
522        return 0;
523}
524
525static int msconf_enable_halfduplex(MSFilter *f, void *arg){
526        ConfState *s=(ConfState*)f->data;
527        int i;
528        s->enable_halfduplex = *(int*)arg;
529
530        for (i=0;i<CONF_MAX_PINS;i++)
531                channel_uninit(&s->channels[i]);
532    for (i=0;i<CONF_MAX_PINS;i++)
533                channel_init(s, &s->channels[i], i);
534        return 0;
535}
536
537static int msconf_set_vad_prob_start(MSFilter *f, void *arg){
538        ConfState *s=(ConfState*)f->data;
539        int i;
540        s->vad_prob_start = *(int*)arg;
541
542        for (i=0;i<CONF_MAX_PINS;i++)
543                channel_uninit(&s->channels[i]);
544    for (i=0;i<CONF_MAX_PINS;i++)
545                channel_init(s, &s->channels[i], i);
546        return 0;
547}
548
549static int msconf_set_vad_prob_continue(MSFilter *f, void *arg){
550        ConfState *s=(ConfState*)f->data;
551        int i;
552        s->vad_prob_continue = *(int*)arg;
553
554        for (i=0;i<CONF_MAX_PINS;i++)
555                channel_uninit(&s->channels[i]);
556    for (i=0;i<CONF_MAX_PINS;i++)
557                channel_init(s, &s->channels[i], i);
558        return 0;
559}
560
561static int msconf_enable_vad(MSFilter *f, void *arg){
562        ConfState *s=(ConfState*)f->data;
563        int i;
564        s->enable_vad = *(int*)arg;
565
566        for (i=0;i<CONF_MAX_PINS;i++)
567                channel_uninit(&s->channels[i]);
568    for (i=0;i<CONF_MAX_PINS;i++)
569                channel_init(s, &s->channels[i], i);
570        return 0;
571}
572
573static int msconf_get_stat_discarded(MSFilter *f, void *arg){
574        ConfState *s=(ConfState*)f->data;
575        Channel *chan;
576        int i;
577        i = *(int*)arg;
578        /*read from all inputs and put into bufferizers*/
579        if (i<0 || i>CONF_MAX_PINS)
580          return -1;
581
582        if (f->inputs[i]!=NULL){
583                chan=&s->channels[i];
584                return chan->stat_discarded;
585        }
586        return -1;
587}
588
589static int msconf_get_stat_missed(MSFilter *f, void *arg){
590        ConfState *s=(ConfState*)f->data;
591        Channel *chan;
592        int i;
593        i = *(int*)arg;
594        /*read from all inputs and put into bufferizers*/
595        if (i<0 || i>CONF_MAX_PINS)
596          return -1;
597
598        if (f->inputs[i]!=NULL){
599                chan=&s->channels[i];
600                return chan->stat_missed;
601        }
602        return -1;
603}
604
605static int msconf_get_stat_processed(MSFilter *f, void *arg){
606        ConfState *s=(ConfState*)f->data;
607        Channel *chan;
608        int i;
609        i = *(int*)arg;
610        /*read from all inputs and put into bufferizers*/
611        if (i<0 || i>CONF_MAX_PINS)
612          return -1;
613
614        if (f->inputs[i]!=NULL){
615                chan=&s->channels[i];
616                return chan->stat_processed;
617        }
618        return -1;
619}
620
621static MSFilterMethod msconf_methods[]={
622        {       MS_FILTER_SET_SAMPLE_RATE, msconf_set_sr },
623        {       MS_FILTER_ENABLE_DIRECTMODE, msconf_enable_directmode },
624        {       MS_FILTER_ENABLE_VAD, msconf_enable_vad },
625        {       MS_FILTER_ENABLE_AGC, msconf_enable_agc },
626        {       MS_FILTER_GET_STAT_DISCARDED, msconf_get_stat_discarded },
627        {       MS_FILTER_GET_STAT_MISSED, msconf_get_stat_missed },
628        {       MS_FILTER_GET_STAT_OUTPUT, msconf_get_stat_processed },
629
630        {       MS_FILTER_ENABLE_HALFDUPLEX, msconf_enable_halfduplex },
631        {       MS_FILTER_SET_VAD_PROB_START, msconf_set_vad_prob_start },
632        {       MS_FILTER_SET_VAD_PROB_CONTINUE, msconf_set_vad_prob_continue },
633        {       0                       , NULL}
634};
635
636#ifdef _MSC_VER
637
638MSFilterDesc ms_conf_desc={
639        MS_CONF_ID,
640        "MSConf",
641        N_("A filter to make conferencing"),
642        MS_FILTER_OTHER,
643        NULL,
644        CONF_MAX_PINS,
645        CONF_MAX_PINS,
646        conf_init,
647        conf_preprocess,
648        conf_process,
649        conf_postprocess,
650        conf_uninit,
651        msconf_methods
652};
653
654#else
655
656MSFilterDesc ms_conf_desc={
657        .id=MS_CONF_ID,
658        .name="MSConf",
659        .text=N_("A filter to make conferencing"),
660        .category=MS_FILTER_OTHER,
661        .ninputs=CONF_MAX_PINS,
662        .noutputs=CONF_MAX_PINS,
663        .init=conf_init,
664        .preprocess=conf_preprocess,
665        .process=conf_process,
666        .postprocess=conf_postprocess,
667        .uninit=conf_uninit,
668        .methods=msconf_methods
669};
670
671#endif
672
673MS_FILTER_DESC_EXPORT(ms_conf_desc)
Note: See TracBrowser for help on using the repository browser.