You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c

538 lines
15 KiB

/*
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 <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;
}
}