/* The mediastreamer library aims at providing modular media processing and I/O for linphone, but also for any telephony application. Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "msfilter.h" void ms_filter_init(MSFilter *filter) { filter->finputs=0; filter->foutputs=0; filter->qinputs=0; filter->qoutputs=0; filter->infifos=NULL; filter->outfifos=NULL; filter->inqueues=NULL; filter->outqueues=NULL; filter->lock=g_mutex_new(); filter->min_fifo_size=0x7fff; filter->notify_event=NULL; filter->userdata=NULL; } void ms_filter_uninit(MSFilter *filter) { g_mutex_free(filter->lock); } void ms_filter_class_init(MSFilterClass *filterclass) { filterclass->name=NULL; filterclass->max_finputs=0; filterclass->max_foutputs=0; filterclass->max_qinputs=0; filterclass->max_qoutputs=0; filterclass->r_maxgran=0; filterclass->w_maxgran=0; filterclass->r_offset=0; filterclass->w_offset=0; filterclass->set_property=NULL; filterclass->get_property=NULL; filterclass->setup=NULL; filterclass->unsetup=NULL; filterclass->process=NULL; filterclass->destroy=NULL; filterclass->attributes=0; filterclass->ref_count=0; } /* find output queue */ gint find_oq(MSFilter *m1,MSQueue *oq) { gint i; for (i=0;imax_qoutputs;i++){ if (m1->outqueues[i]==oq) return i; } return -1; } /* find input queue */ gint find_iq(MSFilter *m1,MSQueue *iq) { gint i; for (i=0;imax_qinputs;i++){ if (m1->inqueues[i]==iq) return i; } return -1; } /* find output fifo */ gint find_of(MSFilter *m1,MSFifo *of) { gint i; for (i=0;imax_foutputs;i++){ if (m1->outfifos[i]==of) return i; } return -1; } /* find input fifo */ gint find_if(MSFilter *m1,MSFifo *inf) { gint i; for (i=0;imax_finputs;i++){ if (m1->infifos[i]==inf) return i; } return -1; } #define find_free_iq(_m1) find_iq(_m1,NULL) #define find_free_oq(_m1) find_oq(_m1,NULL) #define find_free_if(_m1) find_if(_m1,NULL) #define find_free_of(_m1) find_of(_m1,NULL) int ms_filter_add_link(MSFilter *m1, MSFilter *m2) { gint m1_q=-1; gint m1_f=-1; gint m2_q=-1; gint m2_f=-1; /* determine the type of link we can add */ m1_q=find_free_oq(m1); m1_f=find_free_of(m1); m2_q=find_free_iq(m2); m2_f=find_free_if(m2); if ((m1_q!=-1) && (m2_q!=-1)){ /* link with queues */ ms_trace("m1_q=%i , m2_q=%i",m1_q,m2_q); return ms_filter_link(m1,m1_q,m2,m2_q,LINK_QUEUE); } if ((m1_f!=-1) && (m2_f!=-1)){ /* link with queues */ ms_trace("m1_f=%i , m2_f=%i",m1_f,m2_f); return ms_filter_link(m1,m1_f,m2,m2_f,LINK_FIFO); } g_warning("ms_filter_add_link: could not link."); return -1; } /** * ms_filter_link: * @m1: A #MSFilter object. * @pin1: The pin number on @m1. * @m2: A #MSFilter object. * @pin2: The pin number on @m2. * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS. * * This function links two MSFilter object between them. It must be used to make chains of filters. * All data outgoing from pin1 of m1 will go to the input pin2 of m2. * The way to communicate can be fifos or queues, depending of the nature of the filters. Filters can have * multiple queue pins and multiple fifo pins, but most of them have only one queue input/output or only one * fifo input/output. Fifos are usally used by filters doing audio processing, while queues are used by filters doing * video processing. * * Returns: 0 if successfull, a negative value reprensenting the errno.h error. */ int ms_filter_link(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2, int linktype) { MSQueue *q; MSFifo *fifo; g_message("ms_filter_add_link: %s,%i -> %s,%i",m1->klass->name,pin1,m2->klass->name,pin2); switch(linktype) { case LINK_QUEUE: /* Are filter m1 and m2 able to accept more queues connections ?*/ g_return_val_if_fail(m1->qoutputsmax_qoutputs,-EMLINK); g_return_val_if_fail(m2->qinputsmax_qinputs,-EMLINK); /* Are filter m1 and m2 valid with their inputs and outputs ?*/ g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT); g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT); /* are the requested pins exists ?*/ g_return_val_if_fail(pin1max_qoutputs,-EINVAL); g_return_val_if_fail(pin2max_qinputs,-EINVAL); /* are the requested pins free ?*/ g_return_val_if_fail(m1->outqueues[pin1]==NULL,-EBUSY); g_return_val_if_fail(m2->inqueues[pin2]==NULL,-EBUSY); q=ms_queue_new(); m1->outqueues[pin1]=m2->inqueues[pin2]=q; m1->qoutputs++; m2->qinputs++; q->prev_data=(void*)m1; q->next_data=(void*)m2; break; case LINK_FIFO: /* Are filter m1 and m2 able to accept more fifo connections ?*/ g_return_val_if_fail(m1->foutputsmax_foutputs,-EMLINK); g_return_val_if_fail(m2->finputsmax_finputs,-EMLINK); /* Are filter m1 and m2 valid with their inputs and outputs ?*/ g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT); g_return_val_if_fail(m2->infifos!=NULL,-EFAULT); /* are the requested pins exists ?*/ g_return_val_if_fail(pin1max_foutputs,-EINVAL); g_return_val_if_fail(pin2max_finputs,-EINVAL); /* are the requested pins free ?*/ g_return_val_if_fail(m1->outfifos[pin1]==NULL,-EBUSY); g_return_val_if_fail(m2->infifos[pin2]==NULL,-EBUSY); if (MS_FILTER_GET_CLASS(m1)->attributes & FILTER_IS_SOURCE) { /* configure min_fifo_size */ fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran, MS_FILTER_GET_CLASS(m1)->w_maxgran, MS_FILTER_GET_CLASS(m2)->r_offset, MS_FILTER_GET_CLASS(m1)->w_offset, MS_FILTER_GET_CLASS(m1)->w_maxgran); m2->min_fifo_size=MS_FILTER_GET_CLASS(m1)->w_maxgran; } else { gint next_size; ms_trace("ms_filter_add_link: min_fifo_size=%i",m1->min_fifo_size); fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran, MS_FILTER_GET_CLASS(m1)->w_maxgran, MS_FILTER_GET_CLASS(m2)->r_offset, MS_FILTER_GET_CLASS(m1)->w_offset, m1->min_fifo_size); if (MS_FILTER_GET_CLASS(m2)->r_maxgran>0){ next_size=(m1->min_fifo_size* (MS_FILTER_GET_CLASS(m2)->w_maxgran)) / (MS_FILTER_GET_CLASS(m2)->r_maxgran); }else next_size=m1->min_fifo_size; ms_trace("ms_filter_add_link: next_size=%i",next_size); m2->min_fifo_size=next_size; } m1->outfifos[pin1]=m2->infifos[pin2]=fifo; m1->foutputs++; m2->finputs++; fifo->prev_data=(void*)m1; fifo->next_data=(void*)m2; break; } return 0; } /** * ms_filter_unlink: * @m1: A #MSFilter object. * @pin1: The pin number on @m1. * @m2: A #MSFilter object. * @pin2: The pin number on @m2. * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS. * * Unlink @pin1 of filter @m1 from @pin2 of filter @m2. @linktype specifies what type of connection is removed. * * Returns: 0 if successfull, a negative value reprensenting the errno.h error. */ int ms_filter_unlink(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2,gint linktype) { switch(linktype) { case LINK_QUEUE: /* Are filter m1 and m2 valid with their inputs and outputs ?*/ g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT); g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT); /* are the requested pins exists ?*/ g_return_val_if_fail(pin1max_qoutputs,-EINVAL); g_return_val_if_fail(pin2max_qinputs,-EINVAL); /* are the requested pins busy ?*/ g_return_val_if_fail(m1->outqueues[pin1]!=NULL,-ENOENT); g_return_val_if_fail(m2->inqueues[pin2]!=NULL,-ENOENT); /* are the two pins connected together ?*/ g_return_val_if_fail(m1->outqueues[pin1]==m2->inqueues[pin2],-EINVAL); ms_queue_destroy(m1->outqueues[pin1]); m1->outqueues[pin1]=m2->inqueues[pin2]=NULL; m1->qoutputs--; m2->qinputs--; break; case LINK_FIFO: /* Are filter m1 and m2 valid with their inputs and outputs ?*/ g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT); g_return_val_if_fail(m2->infifos!=NULL,-EFAULT); /* are the requested pins exists ?*/ g_return_val_if_fail(pin1max_foutputs,-EINVAL); g_return_val_if_fail(pin2max_finputs,-EINVAL); /* are the requested pins busy ?*/ g_return_val_if_fail(m1->outfifos[pin1]!=NULL,-ENOENT); g_return_val_if_fail(m2->infifos[pin2]!=NULL,-ENOENT); /* are the two pins connected together ?*/ g_return_val_if_fail(m1->outfifos[pin1]==m2->infifos[pin2],-EINVAL); ms_fifo_destroy_with_buffer(m1->outfifos[pin1]); m1->outfifos[pin1]=m2->infifos[pin2]=NULL; m1->foutputs--; m2->finputs--; break; } return 0; } /** *ms_filter_remove_links: *@m1: a filter *@m2: another filter. * * Removes all links between m1 and m2. * *Returns: 0 if one more link have been removed, -1 if not. **/ gint ms_filter_remove_links(MSFilter *m1, MSFilter *m2) { int i,j; int removed=-1; MSQueue *qo; MSFifo *fo; /* takes all outputs of m1, and removes the one that goes to m2 */ if (m1->outqueues!=NULL){ for (i=0;imax_qoutputs;i++) { qo=m1->outqueues[i]; if (qo!=NULL){ MSFilter *rmf; /* test if the queue connects to m2 */ rmf=(MSFilter*)qo->next_data; if (rmf==m2){ j=find_iq(rmf,qo); if (j==-1) g_error("Could not find input queue: impossible case."); ms_filter_unlink(m1,i,m2,j,LINK_QUEUE); removed=0; } } } } if (m1->outfifos!=NULL){ for (i=0;imax_foutputs;i++) { fo=m1->outfifos[i]; if (fo!=NULL){ MSFilter *rmf; /* test if the queue connects to m2 */ rmf=(MSFilter*)fo->next_data; if (rmf==m2){ j=find_if(rmf,fo); if (j==-1) g_error("Could not find input fifo: impossible case."); ms_filter_unlink(m1,i,m2,j,LINK_FIFO); removed=0; } } } } return removed; } /** * ms_filter_fifos_have_data: * @f: a #MSFilter object. * * Tells if the filter has enough data in its input fifos in order to be executed succesfully. * * Returns: 1 if it can be executed, 0 else. */ gint ms_filter_fifos_have_data(MSFilter *f) { gint i,j; gint max_inputs=f->klass->max_finputs; gint con_inputs=f->finputs; MSFifo *fifo; /* test fifos */ for(i=0,j=0; (iinfifos[i]; if (fifo!=NULL) { j++; if (fifo->readsize==0) return 0; if (fifo->readsize>=f->r_mingran) return 1; } } return 0; } /** * ms_filter_queues_have_data: * @f: a #MSFilter object. * * Tells if the filter has enough data in its input queues in order to be executed succesfully. * * Returns: 1 if it can be executed, 0 else. */ gint ms_filter_queues_have_data(MSFilter *f) { gint i,j; gint max_inputs=f->klass->max_qinputs; gint con_inputs=f->qinputs; MSQueue *q; /* test queues */ for(i=0,j=0; (iinqueues[i]; if (q!=NULL) { j++; if (ms_queue_can_get(q)) return 1; } } return 0; } void ms_filter_destroy(MSFilter *f) { /* first check if the filter is disconnected from any others */ g_return_if_fail(f->finputs==0); g_return_if_fail(f->foutputs==0); g_return_if_fail(f->qinputs==0); g_return_if_fail(f->qoutputs==0); f->klass->destroy(f); } GList *filter_list=NULL; void ms_filter_register(MSFilterInfo *info) { gpointer tmp; tmp=g_list_find(filter_list,info); if (tmp==NULL) filter_list=g_list_append(filter_list,(gpointer)info); } void ms_filter_unregister(MSFilterInfo *info) { filter_list=g_list_remove(filter_list,(gpointer)info); } static gint compare_names(gpointer info, gpointer name) { MSFilterInfo *i=(MSFilterInfo*) info; return (strcmp(i->name,name)); } MSFilterInfo * ms_filter_get_by_name(const gchar *name) { GList *elem=g_list_find_custom(filter_list, (gpointer)name,(GCompareFunc)compare_names); if (elem!=NULL){ return (MSFilterInfo*)elem->data; } return NULL; } MSFilter * ms_filter_new_with_name(const gchar *name) { MSFilterInfo *info=ms_filter_get_by_name(name); if (info!=NULL) return info->constructor(); g_warning("ms_filter_new_with_name: no filter named %s found.",name); return NULL; } /* find the first codec in the left part of the stream */ MSFilter * ms_filter_search_upstream_by_type(MSFilter *f,MSFilterType type) { MSFilter *tmp=f; MSFilterInfo *info; if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL)){ tmp=(MSFilter*) tmp->infifos[0]->prev_data; while(1){ info=MS_FILTER_GET_CLASS(tmp)->info; if (info!=NULL){ if ( (info->type==type) ){ return tmp; } } if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL)) tmp=(MSFilter*) tmp->infifos[0]->prev_data; else break; } } tmp=f; if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL)){ tmp=(MSFilter*) tmp->inqueues[0]->prev_data; while(1){ info=MS_FILTER_GET_CLASS(tmp)->info; if (info!=NULL){ if ( (info->type==type)){ return tmp; } }else g_warning("ms_filter_search_upstream_by_type: filter %s has no info." ,MS_FILTER_GET_CLASS(tmp)->name); if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL)) tmp=(MSFilter*) tmp->inqueues[0]->prev_data; else break; } } return NULL; } int ms_filter_set_property(MSFilter *f, MSFilterProperty prop,void *value) { if (f->klass->set_property!=NULL){ return f->klass->set_property(f,prop,value); } return 0; } int ms_filter_get_property(MSFilter *f, MSFilterProperty prop,void *value) { if (f->klass->get_property!=NULL){ return f->klass->get_property(f,prop,value); } return -1; } void ms_filter_set_notify_func(MSFilter* filter,MSFilterNotifyFunc func, gpointer userdata) { filter->notify_event=func; filter->userdata=userdata; } void ms_filter_notify_event(MSFilter *filter,gint event, gpointer arg) { if (filter->notify_event!=NULL){ filter->notify_event(filter,event,arg,filter->userdata); } } void swap_buffer(gchar *buffer, gint len) { int i; gchar tmp; for (i=0;i