comparison src/mediastreamer/msfilter.c @ 12024:e67993da8a22

[gaim-migrate @ 14317] I strongly suspect CruiseControl is going to yell at me for this. A voice chat API, GUI + mediastreamer. This is what I'm using for Google Talk. This doesn't actually do anything at all. There's no code in the Jabber plugin yet to use this API (although it Works For Me). All it will do is compile and link. If you're lucky. To build this, you should install oRTP from Linphone, Speex and iLBC (also from linphone, I believe). To not build this, ./configure --disable-vv. Most of the configure.ac and Makefile.am hackery was lifted right out of Linphone with a few modifications. It seems to work if you have everything installed or if you --disable-vv. I haven't really tested not having everything installed and not --disabling-vv. It's kinda funky to include all of mediastreamer in the source tree like this, but linphone doesn't build it as a separate library. I'll probably wind up writing them a patch to build it as a .so so we can link it dynamically instead. This code certainly isn't finished. It'll adapt as I progress on the Google code, but it's certainly of more use here in CVS than in my personal tree. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Wed, 09 Nov 2005 08:07:20 +0000
parents
children
comparison
equal deleted inserted replaced
12023:80faf1ca5280 12024:e67993da8a22
1 /*
2 The mediastreamer library aims at providing modular media processing and I/O
3 for linphone, but also for any telephony application.
4 Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 #include <errno.h>
23 #include "msfilter.h"
24
25
26
27 void ms_filter_init(MSFilter *filter)
28 {
29 filter->finputs=0;
30 filter->foutputs=0;
31 filter->qinputs=0;
32 filter->qoutputs=0;
33 filter->infifos=NULL;
34 filter->outfifos=NULL;
35 filter->inqueues=NULL;
36 filter->outqueues=NULL;
37 filter->lock=g_mutex_new();
38 filter->min_fifo_size=0x7fff;
39 filter->notify_event=NULL;
40 filter->userdata=NULL;
41 }
42
43 void ms_filter_uninit(MSFilter *filter)
44 {
45 g_mutex_free(filter->lock);
46 }
47
48 void ms_filter_class_init(MSFilterClass *filterclass)
49 {
50 filterclass->name=NULL;
51 filterclass->max_finputs=0;
52 filterclass->max_foutputs=0;
53 filterclass->max_qinputs=0;
54 filterclass->max_qoutputs=0;
55 filterclass->r_maxgran=0;
56 filterclass->w_maxgran=0;
57 filterclass->r_offset=0;
58 filterclass->w_offset=0;
59 filterclass->set_property=NULL;
60 filterclass->get_property=NULL;
61 filterclass->setup=NULL;
62 filterclass->unsetup=NULL;
63 filterclass->process=NULL;
64 filterclass->destroy=NULL;
65 filterclass->attributes=0;
66 filterclass->ref_count=0;
67 }
68
69 /* find output queue */
70 gint find_oq(MSFilter *m1,MSQueue *oq)
71 {
72 gint i;
73
74 for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qoutputs;i++){
75 if (m1->outqueues[i]==oq) return i;
76 }
77
78 return -1;
79 }
80
81 /* find input queue */
82 gint find_iq(MSFilter *m1,MSQueue *iq)
83 {
84 gint i;
85 for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qinputs;i++){
86 if (m1->inqueues[i]==iq) return i;
87 }
88 return -1;
89 }
90
91 /* find output fifo */
92 gint find_of(MSFilter *m1,MSFifo *of)
93 {
94 gint i;
95 for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_foutputs;i++){
96 if (m1->outfifos[i]==of) return i;
97 }
98
99 return -1;
100 }
101
102 /* find input fifo */
103 gint find_if(MSFilter *m1,MSFifo *inf)
104 {
105 gint i;
106
107 for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_finputs;i++){
108 if (m1->infifos[i]==inf) return i;
109 }
110
111 return -1;
112 }
113
114 #define find_free_iq(_m1) find_iq(_m1,NULL)
115 #define find_free_oq(_m1) find_oq(_m1,NULL)
116 #define find_free_if(_m1) find_if(_m1,NULL)
117 #define find_free_of(_m1) find_of(_m1,NULL)
118
119 int ms_filter_add_link(MSFilter *m1, MSFilter *m2)
120 {
121 gint m1_q=-1;
122 gint m1_f=-1;
123 gint m2_q=-1;
124 gint m2_f=-1;
125 /* determine the type of link we can add */
126 m1_q=find_free_oq(m1);
127 m1_f=find_free_of(m1);
128 m2_q=find_free_iq(m2);
129 m2_f=find_free_if(m2);
130 if ((m1_q!=-1) && (m2_q!=-1)){
131 /* link with queues */
132 ms_trace("m1_q=%i , m2_q=%i",m1_q,m2_q);
133 return ms_filter_link(m1,m1_q,m2,m2_q,LINK_QUEUE);
134 }
135 if ((m1_f!=-1) && (m2_f!=-1)){
136 /* link with queues */
137 ms_trace("m1_f=%i , m2_f=%i",m1_f,m2_f);
138 return ms_filter_link(m1,m1_f,m2,m2_f,LINK_FIFO);
139 }
140 g_warning("ms_filter_add_link: could not link.");
141 return -1;
142 }
143 /**
144 * ms_filter_link:
145 * @m1: A #MSFilter object.
146 * @pin1: The pin number on @m1.
147 * @m2: A #MSFilter object.
148 * @pin2: The pin number on @m2.
149 * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS.
150 *
151 * This function links two MSFilter object between them. It must be used to make chains of filters.
152 * All data outgoing from pin1 of m1 will go to the input pin2 of m2.
153 * The way to communicate can be fifos or queues, depending of the nature of the filters. Filters can have
154 * multiple queue pins and multiple fifo pins, but most of them have only one queue input/output or only one
155 * fifo input/output. Fifos are usally used by filters doing audio processing, while queues are used by filters doing
156 * video processing.
157 *
158 * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
159 */
160 int ms_filter_link(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2, int linktype)
161 {
162 MSQueue *q;
163 MSFifo *fifo;
164
165 g_message("ms_filter_add_link: %s,%i -> %s,%i",m1->klass->name,pin1,m2->klass->name,pin2);
166 switch(linktype)
167 {
168 case LINK_QUEUE:
169 /* Are filter m1 and m2 able to accept more queues connections ?*/
170 g_return_val_if_fail(m1->qoutputs<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EMLINK);
171 g_return_val_if_fail(m2->qinputs<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EMLINK);
172 /* Are filter m1 and m2 valid with their inputs and outputs ?*/
173 g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT);
174 g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT);
175 /* are the requested pins exists ?*/
176 g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL);
177 g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EINVAL);
178 /* are the requested pins free ?*/
179 g_return_val_if_fail(m1->outqueues[pin1]==NULL,-EBUSY);
180 g_return_val_if_fail(m2->inqueues[pin2]==NULL,-EBUSY);
181
182 q=ms_queue_new();
183 m1->outqueues[pin1]=m2->inqueues[pin2]=q;
184 m1->qoutputs++;
185 m2->qinputs++;
186 q->prev_data=(void*)m1;
187 q->next_data=(void*)m2;
188 break;
189 case LINK_FIFO:
190 /* Are filter m1 and m2 able to accept more fifo connections ?*/
191 g_return_val_if_fail(m1->foutputs<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EMLINK);
192 g_return_val_if_fail(m2->finputs<MS_FILTER_GET_CLASS(m2)->max_finputs,-EMLINK);
193 /* Are filter m1 and m2 valid with their inputs and outputs ?*/
194 g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT);
195 g_return_val_if_fail(m2->infifos!=NULL,-EFAULT);
196 /* are the requested pins exists ?*/
197 g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL);
198 g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_finputs,-EINVAL);
199 /* are the requested pins free ?*/
200 g_return_val_if_fail(m1->outfifos[pin1]==NULL,-EBUSY);
201 g_return_val_if_fail(m2->infifos[pin2]==NULL,-EBUSY);
202
203 if (MS_FILTER_GET_CLASS(m1)->attributes & FILTER_IS_SOURCE)
204 {
205 /* configure min_fifo_size */
206 fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran,
207 MS_FILTER_GET_CLASS(m1)->w_maxgran,
208 MS_FILTER_GET_CLASS(m2)->r_offset,
209 MS_FILTER_GET_CLASS(m1)->w_offset,
210 MS_FILTER_GET_CLASS(m1)->w_maxgran);
211 m2->min_fifo_size=MS_FILTER_GET_CLASS(m1)->w_maxgran;
212 }
213 else
214 {
215 gint next_size;
216 ms_trace("ms_filter_add_link: min_fifo_size=%i",m1->min_fifo_size);
217 fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran,
218 MS_FILTER_GET_CLASS(m1)->w_maxgran,
219 MS_FILTER_GET_CLASS(m2)->r_offset,
220 MS_FILTER_GET_CLASS(m1)->w_offset,
221 m1->min_fifo_size);
222 if (MS_FILTER_GET_CLASS(m2)->r_maxgran>0){
223 next_size=(m1->min_fifo_size*
224 (MS_FILTER_GET_CLASS(m2)->w_maxgran)) /
225 (MS_FILTER_GET_CLASS(m2)->r_maxgran);
226 }else next_size=m1->min_fifo_size;
227 ms_trace("ms_filter_add_link: next_size=%i",next_size);
228 m2->min_fifo_size=next_size;
229 }
230
231
232 m1->outfifos[pin1]=m2->infifos[pin2]=fifo;
233 m1->foutputs++;
234 m2->finputs++;
235 fifo->prev_data=(void*)m1;
236 fifo->next_data=(void*)m2;
237 break;
238 }
239 return 0;
240 }
241 /**
242 * ms_filter_unlink:
243 * @m1: A #MSFilter object.
244 * @pin1: The pin number on @m1.
245 * @m2: A #MSFilter object.
246 * @pin2: The pin number on @m2.
247 * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS.
248 *
249 * Unlink @pin1 of filter @m1 from @pin2 of filter @m2. @linktype specifies what type of connection is removed.
250 *
251 * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
252 */
253 int ms_filter_unlink(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2,gint linktype)
254 {
255 switch(linktype)
256 {
257 case LINK_QUEUE:
258 /* Are filter m1 and m2 valid with their inputs and outputs ?*/
259 g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT);
260 g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT);
261 /* are the requested pins exists ?*/
262 g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL);
263 g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EINVAL);
264 /* are the requested pins busy ?*/
265 g_return_val_if_fail(m1->outqueues[pin1]!=NULL,-ENOENT);
266 g_return_val_if_fail(m2->inqueues[pin2]!=NULL,-ENOENT);
267 /* are the two pins connected together ?*/
268 g_return_val_if_fail(m1->outqueues[pin1]==m2->inqueues[pin2],-EINVAL);
269
270 ms_queue_destroy(m1->outqueues[pin1]);
271 m1->outqueues[pin1]=m2->inqueues[pin2]=NULL;
272 m1->qoutputs--;
273 m2->qinputs--;
274
275 break;
276 case LINK_FIFO:
277 /* Are filter m1 and m2 valid with their inputs and outputs ?*/
278 g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT);
279 g_return_val_if_fail(m2->infifos!=NULL,-EFAULT);
280 /* are the requested pins exists ?*/
281 g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL);
282 g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_finputs,-EINVAL);
283 /* are the requested pins busy ?*/
284 g_return_val_if_fail(m1->outfifos[pin1]!=NULL,-ENOENT);
285 g_return_val_if_fail(m2->infifos[pin2]!=NULL,-ENOENT);
286 /* are the two pins connected together ?*/
287 g_return_val_if_fail(m1->outfifos[pin1]==m2->infifos[pin2],-EINVAL);
288 ms_fifo_destroy_with_buffer(m1->outfifos[pin1]);
289 m1->outfifos[pin1]=m2->infifos[pin2]=NULL;
290 m1->foutputs--;
291 m2->finputs--;
292 break;
293 }
294 return 0;
295 }
296
297 /**
298 *ms_filter_remove_links:
299 *@m1: a filter
300 *@m2: another filter.
301 *
302 * Removes all links between m1 and m2.
303 *
304 *Returns: 0 if one more link have been removed, -1 if not.
305 **/
306 gint ms_filter_remove_links(MSFilter *m1, MSFilter *m2)
307 {
308 int i,j;
309 int removed=-1;
310 MSQueue *qo;
311 MSFifo *fo;
312 /* takes all outputs of m1, and removes the one that goes to m2 */
313 if (m1->outqueues!=NULL){
314 for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qoutputs;i++)
315 {
316 qo=m1->outqueues[i];
317 if (qo!=NULL){
318 MSFilter *rmf;
319 /* test if the queue connects to m2 */
320 rmf=(MSFilter*)qo->next_data;
321 if (rmf==m2){
322 j=find_iq(rmf,qo);
323 if (j==-1) g_error("Could not find input queue: impossible case.");
324 ms_filter_unlink(m1,i,m2,j,LINK_QUEUE);
325 removed=0;
326 }
327 }
328 }
329 }
330 if (m1->outfifos!=NULL){
331 for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_foutputs;i++)
332 {
333 fo=m1->outfifos[i];
334 if (fo!=NULL){
335 MSFilter *rmf;
336 /* test if the queue connects to m2 */
337 rmf=(MSFilter*)fo->next_data;
338 if (rmf==m2){
339 j=find_if(rmf,fo);
340 if (j==-1) g_error("Could not find input fifo: impossible case.");
341 ms_filter_unlink(m1,i,m2,j,LINK_FIFO);
342 removed=0;
343 }
344 }
345 }
346 }
347 return removed;
348 }
349
350 /**
351 * ms_filter_fifos_have_data:
352 * @f: a #MSFilter object.
353 *
354 * Tells if the filter has enough data in its input fifos in order to be executed succesfully.
355 *
356 * Returns: 1 if it can be executed, 0 else.
357 */
358 gint ms_filter_fifos_have_data(MSFilter *f)
359 {
360 gint i,j;
361 gint max_inputs=f->klass->max_finputs;
362 gint con_inputs=f->finputs;
363 MSFifo *fifo;
364 /* test fifos */
365 for(i=0,j=0; (i<max_inputs) && (j<con_inputs);i++)
366 {
367 fifo=f->infifos[i];
368 if (fifo!=NULL)
369 {
370 j++;
371 if (fifo->readsize==0) return 0;
372 if (fifo->readsize>=f->r_mingran) return 1;
373 }
374 }
375 return 0;
376 }
377
378 /**
379 * ms_filter_queues_have_data:
380 * @f: a #MSFilter object.
381 *
382 * Tells if the filter has enough data in its input queues in order to be executed succesfully.
383 *
384 * Returns: 1 if it can be executed, 0 else.
385 */
386 gint ms_filter_queues_have_data(MSFilter *f)
387 {
388 gint i,j;
389 gint max_inputs=f->klass->max_qinputs;
390 gint con_inputs=f->qinputs;
391 MSQueue *q;
392 /* test queues */
393 for(i=0,j=0; (i<max_inputs) && (j<con_inputs);i++)
394 {
395 q=f->inqueues[i];
396 if (q!=NULL)
397 {
398 j++;
399 if (ms_queue_can_get(q)) return 1;
400 }
401 }
402 return 0;
403 }
404
405
406
407 void ms_filter_destroy(MSFilter *f)
408 {
409 /* first check if the filter is disconnected from any others */
410 g_return_if_fail(f->finputs==0);
411 g_return_if_fail(f->foutputs==0);
412 g_return_if_fail(f->qinputs==0);
413 g_return_if_fail(f->qoutputs==0);
414 f->klass->destroy(f);
415 }
416
417 GList *filter_list=NULL;
418
419 void ms_filter_register(MSFilterInfo *info)
420 {
421 gpointer tmp;
422 tmp=g_list_find(filter_list,info);
423 if (tmp==NULL) filter_list=g_list_append(filter_list,(gpointer)info);
424 }
425
426 void ms_filter_unregister(MSFilterInfo *info)
427 {
428 filter_list=g_list_remove(filter_list,(gpointer)info);
429 }
430
431 static gint compare_names(gpointer info, gpointer name)
432 {
433 MSFilterInfo *i=(MSFilterInfo*) info;
434 return (strcmp(i->name,name));
435 }
436
437 MSFilterInfo * ms_filter_get_by_name(const gchar *name)
438 {
439 GList *elem=g_list_find_custom(filter_list,
440 (gpointer)name,(GCompareFunc)compare_names);
441 if (elem!=NULL){
442 return (MSFilterInfo*)elem->data;
443 }
444 return NULL;
445 }
446
447
448
449 MSFilter * ms_filter_new_with_name(const gchar *name)
450 {
451 MSFilterInfo *info=ms_filter_get_by_name(name);
452 if (info!=NULL) return info->constructor();
453 g_warning("ms_filter_new_with_name: no filter named %s found.",name);
454 return NULL;
455 }
456
457
458 /* find the first codec in the left part of the stream */
459 MSFilter * ms_filter_search_upstream_by_type(MSFilter *f,MSFilterType type)
460 {
461 MSFilter *tmp=f;
462 MSFilterInfo *info;
463
464 if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL)){
465 tmp=(MSFilter*) tmp->infifos[0]->prev_data;
466 while(1){
467 info=MS_FILTER_GET_CLASS(tmp)->info;
468 if (info!=NULL){
469 if ( (info->type==type) ){
470 return tmp;
471 }
472 }
473 if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL))
474 tmp=(MSFilter*) tmp->infifos[0]->prev_data;
475 else break;
476 }
477 }
478 tmp=f;
479 if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL)){
480 tmp=(MSFilter*) tmp->inqueues[0]->prev_data;
481 while(1){
482
483 info=MS_FILTER_GET_CLASS(tmp)->info;
484 if (info!=NULL){
485 if ( (info->type==type)){
486 return tmp;
487 }
488 }else g_warning("ms_filter_search_upstream_by_type: filter %s has no info."
489 ,MS_FILTER_GET_CLASS(tmp)->name);
490 if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL))
491 tmp=(MSFilter*) tmp->inqueues[0]->prev_data;
492 else break;
493 }
494 }
495 return NULL;
496 }
497
498
499 int ms_filter_set_property(MSFilter *f, MSFilterProperty prop,void *value)
500 {
501 if (f->klass->set_property!=NULL){
502 return f->klass->set_property(f,prop,value);
503 }
504 return 0;
505 }
506
507 int ms_filter_get_property(MSFilter *f, MSFilterProperty prop,void *value)
508 {
509 if (f->klass->get_property!=NULL){
510 return f->klass->get_property(f,prop,value);
511 }
512 return -1;
513 }
514
515 void ms_filter_set_notify_func(MSFilter* filter,MSFilterNotifyFunc func, gpointer userdata)
516 {
517 filter->notify_event=func;
518 filter->userdata=userdata;
519 }
520
521 void ms_filter_notify_event(MSFilter *filter,gint event, gpointer arg)
522 {
523 if (filter->notify_event!=NULL){
524 filter->notify_event(filter,event,arg,filter->userdata);
525 }
526 }
527
528 void swap_buffer(gchar *buffer, gint len)
529 {
530 int i;
531 gchar tmp;
532 for (i=0;i<len;i+=2){
533 tmp=buffer[i];
534 buffer[i]=buffer[i+1];
535 buffer[i+1]=tmp;
536 }
537 }