blob: a4a57f8f16e8cc68b0466f5c9995f57c74a74000 [file] [log] [blame]
/*
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
*/
#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;
}