| /* |
| 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| |
| #include <errno.h> |
| #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;i<MS_FILTER_GET_CLASS(m1)->max_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;i<MS_FILTER_GET_CLASS(m1)->max_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;i<MS_FILTER_GET_CLASS(m1)->max_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;i<MS_FILTER_GET_CLASS(m1)->max_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->qoutputs<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EMLINK); |
| g_return_val_if_fail(m2->qinputs<MS_FILTER_GET_CLASS(m2)->max_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(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL); |
| g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_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->foutputs<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EMLINK); |
| g_return_val_if_fail(m2->finputs<MS_FILTER_GET_CLASS(m2)->max_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(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL); |
| g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_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(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL); |
| g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_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(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL); |
| g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_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;i<MS_FILTER_GET_CLASS(m1)->max_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;i<MS_FILTER_GET_CLASS(m1)->max_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; (i<max_inputs) && (j<con_inputs);i++) |
| { |
| fifo=f->infifos[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; (i<max_inputs) && (j<con_inputs);i++) |
| { |
| q=f->inqueues[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<len;i+=2){ |
| tmp=buffer[i]; |
| buffer[i]=buffer[i+1]; |
| buffer[i+1]=tmp; |
| } |
| } |