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.
343 lines
8.0 KiB
343 lines
8.0 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
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "ms.h"
|
|
#include "sndcard.h"
|
|
#include "mscodec.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef VIDEO_ENABLED
|
|
extern void ms_video_source_register_all();
|
|
#endif
|
|
#ifdef HAVE_ILBC
|
|
extern void ms_ilbc_codec_init();
|
|
#endif
|
|
|
|
/**
|
|
* ms_init:
|
|
*
|
|
*
|
|
* Initialize the mediastreamer. This must be the first function called in a program
|
|
* using the mediastreamer library.
|
|
*
|
|
*
|
|
*/
|
|
void ms_init()
|
|
{
|
|
if (!g_thread_supported()) g_thread_init (NULL);
|
|
#ifdef HAVE_GLIB
|
|
if (!g_module_supported()){
|
|
g_error("GModule is not supported.");
|
|
}
|
|
#endif
|
|
/* initialize the oss subsystem */
|
|
snd_card_manager_init(snd_card_manager);
|
|
/* register the statically linked codecs */
|
|
ms_codec_register_all();
|
|
#ifdef VIDEO_ENABLED
|
|
ms_video_source_register_all();
|
|
#endif
|
|
#ifdef HAVE_ILBC
|
|
ms_ilbc_codec_init();
|
|
#endif
|
|
}
|
|
|
|
|
|
static gint compare(gconstpointer a, gconstpointer b)
|
|
{
|
|
MSFilter *f1=(MSFilter*)a,*f2=(MSFilter*)b;
|
|
if (f1->klass<f2->klass) return -1;
|
|
if (f1->klass==f2->klass) return 0;
|
|
/* if f1->klass>f2->klass ....*/
|
|
return 1;
|
|
}
|
|
|
|
static GList *g_list_append_if_new(GList *l,gpointer data)
|
|
{
|
|
GList *res=l;
|
|
if (g_list_find(res,data)==NULL)
|
|
res=g_list_append(res,data);
|
|
return(res);
|
|
}
|
|
|
|
static GList *get_nexts(MSFilter *f,GList *l)
|
|
{
|
|
int i;
|
|
MSFifo *fifo;
|
|
MSQueue *q;
|
|
GList *res=l;
|
|
|
|
/* check fifos*/
|
|
for (i=0;i <f->klass->max_foutputs;i++)
|
|
{
|
|
fifo=f->outfifos[i];
|
|
if (fifo!=NULL) res=g_list_append_if_new(res,(gpointer)fifo->next_data);
|
|
}
|
|
/* check queues*/
|
|
for (i=0;i <f->klass->max_qoutputs;i++)
|
|
{
|
|
q=f->outqueues[i];
|
|
if (q!=NULL) res=g_list_append_if_new(res,(gpointer)q->next_data);
|
|
}
|
|
return(res);
|
|
}
|
|
|
|
/* compile graphs attached to a sync source*/
|
|
int ms_compile(MSSync *sync)
|
|
{
|
|
int i;
|
|
GList *list1=NULL,*list2=NULL,*elem;
|
|
GList *proc_chain=NULL;
|
|
MSFilter *f;
|
|
|
|
/* first free the old list if we are just updating*/
|
|
if (sync->execution_list!=NULL) g_list_free(sync->execution_list);
|
|
/* get the list of filters attached to this sync*/
|
|
for (i=0;i<sync->filters;i++)
|
|
{
|
|
/* //printf("found filter !\n"); */
|
|
list1=g_list_append(list1,sync->attached_filters[i]);
|
|
}
|
|
/* find the processing chain */
|
|
while (list1!=NULL)
|
|
{
|
|
list2=NULL;
|
|
/* sort the list by types of filter*/
|
|
list1=g_list_sort(list1,compare);
|
|
/* save into the processing chain list*/
|
|
/* //printf("list1 :%i elements\n",g_list_length(list1)); */
|
|
proc_chain=g_list_concat(proc_chain,list1);
|
|
/* get all following filters. They are appended to list2*/
|
|
elem=list1;
|
|
while (elem!=NULL)
|
|
{
|
|
f=(MSFilter*)(elem->data);
|
|
/* check if filter 's status */
|
|
if (f->klass->attributes & FILTER_CAN_SYNC)
|
|
{
|
|
sync->samples_per_tick=0;
|
|
}
|
|
list2=get_nexts(f,list2);
|
|
elem=g_list_next(elem);
|
|
}
|
|
list1=list2;
|
|
}
|
|
sync->execution_list=proc_chain;
|
|
sync->flags&=~MS_SYNC_NEED_UPDATE;
|
|
ms_trace("%i filters successfully compiled in a processing chain.",g_list_length(sync->execution_list));
|
|
return 0;
|
|
}
|
|
|
|
/*execute the processing chain attached to a sync source. It is called as a thread by ms_main()*/
|
|
void *ms_thread_run(void *sync_ptr)
|
|
{
|
|
MSSync *sync=(MSSync*) sync_ptr;
|
|
GList *filter;
|
|
MSFilter *f;
|
|
|
|
|
|
ms_sync_lock(sync);
|
|
while(sync->run)
|
|
{
|
|
/* //g_message("sync->run=%i",sync->run); */
|
|
if (sync->samples_per_tick==0) ms_sync_suspend(sync);
|
|
if (sync->flags & MS_SYNC_NEED_UPDATE){
|
|
ms_compile(sync);
|
|
ms_sync_setup(sync);
|
|
}
|
|
filter=sync->execution_list;
|
|
ms_sync_unlock(sync);
|
|
/* //ms_trace("Calling synchronisation"); */
|
|
ms_sync_synchronize(sync);
|
|
while(filter!=NULL)
|
|
{
|
|
f=(MSFilter*)filter->data;
|
|
if (MS_FILTER_GET_CLASS(f)->attributes & FILTER_IS_SOURCE)
|
|
{
|
|
/* execute it once */
|
|
ms_trace("Running source filter %s.",f->klass->name);
|
|
ms_filter_process(f);
|
|
}
|
|
else
|
|
{
|
|
/* make the filter process its input data until it has no more */
|
|
while ( ms_filter_fifos_have_data(f) || ms_filter_queues_have_data(f) )
|
|
{
|
|
ms_trace("Running filter %s.",f->klass->name);
|
|
ms_filter_process(f);
|
|
}
|
|
}
|
|
filter=g_list_next(filter);
|
|
}
|
|
ms_sync_lock(sync);
|
|
}
|
|
g_cond_signal(sync->stop_cond); /* signal that the sync thread has finished */
|
|
ms_sync_unlock(sync);
|
|
g_message("Mediastreamer processing thread is exiting.");
|
|
return NULL;
|
|
}
|
|
|
|
/* stop the processing chain attached to a sync source.*/
|
|
void ms_thread_stop(MSSync *sync)
|
|
{
|
|
if (sync->thread!=NULL)
|
|
{
|
|
if (sync->samples_per_tick==0)
|
|
{
|
|
/* to wakeup the thread */
|
|
/* //g_cond_signal(sync->thread_cond); */
|
|
}
|
|
g_mutex_lock(sync->lock);
|
|
sync->run=0;
|
|
sync->thread=NULL;
|
|
g_cond_wait(sync->stop_cond,sync->lock);
|
|
g_mutex_unlock(sync->lock);
|
|
}
|
|
/* //g_message("ms_thread_stop() finished."); */
|
|
}
|
|
|
|
/**
|
|
* ms_start:
|
|
* @sync: A synchronisation source to be started.
|
|
*
|
|
* Starts a thread that will shedule all processing chains attached to the synchronisation source @sync.
|
|
*
|
|
*
|
|
*/
|
|
void ms_start(MSSync *sync)
|
|
{
|
|
if (sync->run==1) return; /*already running*/
|
|
ms_compile(sync);
|
|
ms_sync_setup(sync);
|
|
/* this is to avoid race conditions, for example:
|
|
ms_start(sync);
|
|
ms_oss_write_start(ossw);
|
|
here tge ossw filter need to be compiled to run ms_oss_write_start()
|
|
*/
|
|
ms_trace("ms_start: creating new thread.");
|
|
sync->run=1;
|
|
sync->thread=g_thread_create((GThreadFunc)ms_thread_run,(gpointer)sync,TRUE,NULL);
|
|
if (sync->thread==NULL){
|
|
g_warning("Could not create thread !");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ms_stop:
|
|
* @sync: A synchronisation source to be stopped.
|
|
*
|
|
* Stop the thread that was sheduling the processing chains attached to the synchronisation source @sync.
|
|
* The processing chains are kept unchanged, no object is freed. The synchronisation source can be restarted using ms_start().
|
|
*
|
|
*
|
|
*/
|
|
void ms_stop(MSSync *sync)
|
|
{
|
|
ms_thread_stop(sync);
|
|
ms_sync_unsetup(sync);
|
|
}
|
|
|
|
|
|
gint ms_load_plugin(gchar *path)
|
|
{
|
|
#ifdef HAVE_GLIB
|
|
g_module_open(path,0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
gchar * ms_proc_get_param(gchar *parameter)
|
|
{
|
|
gchar *file;
|
|
int fd;
|
|
int err,len;
|
|
gchar *p,*begin,*end;
|
|
gchar *ret;
|
|
fd=open("/proc/cpuinfo",O_RDONLY);
|
|
if (fd<0){
|
|
g_warning("Could not open /proc/cpuinfo.");
|
|
return NULL;
|
|
}
|
|
file=g_malloc(1024);
|
|
err=read(fd,file,1024);
|
|
file[err-1]='\0';
|
|
/* find the parameter */
|
|
p=strstr(file,parameter);
|
|
if (p==NULL){
|
|
/* parameter not found */
|
|
g_free(file);
|
|
return NULL;
|
|
}
|
|
/* find the following ':' */
|
|
p=strchr(p,':');
|
|
if (p==NULL){
|
|
g_free(file);
|
|
return NULL;
|
|
}
|
|
/* find the value*/
|
|
begin=p+2;
|
|
end=strchr(begin,'\n');
|
|
if (end==NULL) end=strchr(begin,'\0');
|
|
len=end-begin+1;
|
|
ret=g_malloc(len+1);
|
|
snprintf(ret,len,"%s",begin);
|
|
/* //printf("%s=%s\n",parameter,ret); */
|
|
g_free(file);
|
|
return ret;
|
|
}
|
|
|
|
gint ms_proc_get_type()
|
|
{
|
|
static int proc_type=0;
|
|
gchar *value;
|
|
if (proc_type==0){
|
|
value=ms_proc_get_param("cpu family");
|
|
if (value!=NULL) {
|
|
proc_type=atoi(value);
|
|
g_free(value);
|
|
}else return -1;
|
|
}
|
|
return proc_type;
|
|
}
|
|
|
|
gint ms_proc_get_speed()
|
|
{
|
|
char *value;
|
|
static int proc_speed=0;
|
|
if (proc_speed==0){
|
|
value=ms_proc_get_param("cpu MHz");
|
|
if (value!=NULL){
|
|
proc_speed=atoi(value);
|
|
g_free(value);
|
|
}else return -1;
|
|
}
|
|
/* //printf("proc_speed=%i\n",proc_speed); */
|
|
return proc_speed;
|
|
}
|