msfilter.c 10 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "mediastreamer2/msfilter.h"
21
#include "mediastreamer2/msticker.h"
aymeric's avatar
aymeric committed
22

23
#define MS_FILTER_METHOD_GET_FID(id)	(((id)>>16) & 0xFFFF)
24
#define MS_FILTER_METHOD_GET_INDEX(id) ( ((id)>>8) & 0XFF)
Ghislain MARY's avatar
Ghislain MARY committed
25

aymeric's avatar
aymeric committed
26 27

void ms_filter_register(MSFilterDesc *desc){
Simon Morlat's avatar
Simon Morlat committed
28
	ms_factory_register_filter(ms_factory_get_fallback(),desc);
aymeric's avatar
aymeric committed
29 30 31 32 33 34
}

void ms_filter_unregister_all(){
}

bool_t ms_filter_codec_supported(const char *mime){
Simon Morlat's avatar
Simon Morlat committed
35
	return ms_factory_codec_supported(ms_factory_get_fallback(),mime);
aymeric's avatar
aymeric committed
36 37
}

38
MSFilterDesc * ms_filter_get_encoding_capturer(const char *mime) {
Simon Morlat's avatar
Simon Morlat committed
39
	return ms_factory_get_encoding_capturer(ms_factory_get_fallback(),mime);
40 41 42
}

MSFilterDesc * ms_filter_get_decoding_renderer(const char *mime) {
Simon Morlat's avatar
Simon Morlat committed
43
	return ms_factory_get_decoding_renderer(ms_factory_get_fallback(),mime);
44 45
}

aymeric's avatar
aymeric committed
46
MSFilterDesc * ms_filter_get_encoder(const char *mime){
Simon Morlat's avatar
Simon Morlat committed
47
	return ms_factory_get_encoder(ms_factory_get_fallback(),mime);
aymeric's avatar
aymeric committed
48 49 50
}

MSFilterDesc * ms_filter_get_decoder(const char *mime){
Simon Morlat's avatar
Simon Morlat committed
51
	return ms_factory_get_decoder(ms_factory_get_fallback(),mime);
aymeric's avatar
aymeric committed
52 53 54
}

MSFilter * ms_filter_create_encoder(const char *mime){
Simon Morlat's avatar
Simon Morlat committed
55
	return ms_factory_create_encoder(ms_factory_get_fallback(),mime);
aymeric's avatar
aymeric committed
56 57 58
}

MSFilter * ms_filter_create_decoder(const char *mime){
Simon Morlat's avatar
Simon Morlat committed
59
	return ms_factory_create_decoder(ms_factory_get_fallback(),mime);
aymeric's avatar
aymeric committed
60 61 62
}

MSFilter *ms_filter_new_from_desc(MSFilterDesc *desc){
Simon Morlat's avatar
Simon Morlat committed
63
	return ms_factory_create_filter_from_desc(ms_factory_get_fallback(),desc);
aymeric's avatar
aymeric committed
64 65 66
}

MSFilter *ms_filter_new(MSFilterId id){
Simon Morlat's avatar
Simon Morlat committed
67
	return ms_factory_create_filter(ms_factory_get_fallback(),id);
aymeric's avatar
aymeric committed
68 69
}

70
MSFilterDesc *ms_filter_lookup_by_name(const char *filter_name){
Simon Morlat's avatar
Simon Morlat committed
71
	return ms_factory_lookup_filter_by_name(ms_factory_get_fallback(),filter_name);
aymeric's avatar
aymeric committed
72 73
}

74 75 76 77 78 79 80 81 82 83
bool_t ms_filter_desc_implements_interface(MSFilterDesc *desc, MSFilterInterfaceId id){
	MSFilterMethod *methods=desc->methods;
	if (!methods) return FALSE;
	for(;methods->id!=0;methods++){
		unsigned int fid=MS_FILTER_METHOD_GET_FID(methods->id);
		if (fid==id) return TRUE;
	}
	return FALSE;
}

84 85 86 87
bool_t ms_filter_implements_interface(MSFilter *f, MSFilterInterfaceId id){
	return ms_filter_desc_implements_interface(f->desc,id);
}

88
MSList *ms_filter_lookup_by_interface(MSFilterInterfaceId id){
Simon Morlat's avatar
Simon Morlat committed
89
	return ms_factory_lookup_filter_by_interface(ms_factory_get_fallback(),id);
90 91
}

92
MSFilter *ms_filter_new_from_name(const char *filter_name){
Simon Morlat's avatar
Simon Morlat committed
93
	return ms_factory_create_filter_from_name(ms_factory_get_fallback(),filter_name);
94 95
}

aymeric's avatar
aymeric committed
96 97 98 99 100

MSFilterId ms_filter_get_id(MSFilter *f){
	return f->desc->id;
}

101 102 103 104
const char * ms_filter_get_name(MSFilter *f) {
	return f->desc->name;
}

aymeric's avatar
aymeric committed
105 106
int ms_filter_link(MSFilter *f1, int pin1, MSFilter *f2, int pin2){
	MSQueue *q;
107
	ms_message("ms_filter_link: %s:%p,%i-->%s:%p,%i",f1->desc->name,f1,pin1,f2->desc->name,f2,pin2);
aymeric's avatar
aymeric committed
108 109 110 111 112 113 114 115 116 117 118 119
	ms_return_val_if_fail(pin1<f1->desc->noutputs, -1);
	ms_return_val_if_fail(pin2<f2->desc->ninputs, -1);
	ms_return_val_if_fail(f1->outputs[pin1]==NULL,-1);
	ms_return_val_if_fail(f2->inputs[pin2]==NULL,-1);
	q=ms_queue_new(f1,pin1,f2,pin2);
	f1->outputs[pin1]=q;
	f2->inputs[pin2]=q;
	return 0;
}

int ms_filter_unlink(MSFilter *f1, int pin1, MSFilter *f2, int pin2){
	MSQueue *q;
120
	ms_message("ms_filter_unlink: %s:%p,%i-->%s:%p,%i",f1 ? f1->desc->name : "!NULL!",f1,pin1,f2 ? f2->desc->name : "!NULL!",f2,pin2);
aymeric's avatar
aymeric committed
121 122 123 124 125 126 127 128 129 130 131
	ms_return_val_if_fail(pin1<f1->desc->noutputs, -1);
	ms_return_val_if_fail(pin2<f2->desc->ninputs, -1);
	ms_return_val_if_fail(f1->outputs[pin1]!=NULL,-1);
	ms_return_val_if_fail(f2->inputs[pin2]!=NULL,-1);
	ms_return_val_if_fail(f1->outputs[pin1]==f2->inputs[pin2],-1);
	q=f1->outputs[pin1];
	f1->outputs[pin1]=f2->inputs[pin2]=0;
	ms_queue_destroy(q);
	return 0;
}

132
static MS2_INLINE bool_t is_interface_method(unsigned int magic){
133 134 135
	return magic==MS_FILTER_BASE_ID || magic>MSFilterInterfaceBegin;
}

Simon Morlat's avatar
Simon Morlat committed
136
static int _ms_filter_call_method(MSFilter *f, unsigned int id, void *arg){
aymeric's avatar
aymeric committed
137 138 139
	MSFilterMethod *methods=f->desc->methods;
	int i;
	unsigned int magic=MS_FILTER_METHOD_GET_FID(id);
140
	if (!is_interface_method(magic) && magic!=f->desc->id) {
141
		ms_fatal("Method type checking failed when calling %u on filter %s",id,f->desc->name);
aymeric's avatar
aymeric committed
142 143 144 145
		return -1;
	}
	for(i=0;methods!=NULL && methods[i].method!=NULL; i++){
		unsigned int mm=MS_FILTER_METHOD_GET_FID(methods[i].id);
146
		if (mm!=f->desc->id && !is_interface_method(mm)) {
147
			ms_fatal("Bad method definition on filter %s. fid=%u , mm=%u",f->desc->name,f->desc->id,mm);
aymeric's avatar
aymeric committed
148 149 150 151 152 153
			return -1;
		}
		if (methods[i].id==id){
			return methods[i].method(f,arg);
		}
	}
154 155
	if (magic!=MS_FILTER_BASE_ID) ms_error("no such method on filter %s, fid=%i method index=%i",f->desc->name,magic,
	                           MS_FILTER_METHOD_GET_INDEX(id) );
aymeric's avatar
aymeric committed
156 157 158
	return -1;
}

Simon Morlat's avatar
Simon Morlat committed
159 160 161 162 163 164 165
int ms_filter_call_method(MSFilter *f, unsigned int id, void *arg){
	/*compatibility stuff*/
	if (id==MS_AUDIO_DECODER_SET_RTP_PAYLOAD_PICKER && !ms_filter_has_method(f,MS_AUDIO_DECODER_SET_RTP_PAYLOAD_PICKER))
		id=MS_FILTER_SET_RTP_PAYLOAD_PICKER;
	return _ms_filter_call_method(f,id,arg);
}

Simon Morlat's avatar
Simon Morlat committed
166 167 168 169 170 171 172 173
bool_t ms_filter_has_method(MSFilter *f, unsigned int id){
	MSFilterMethod *methods=f->desc->methods;
	int i;
	for(i=0;methods!=NULL && methods[i].method!=NULL; i++)
		if (methods[i].id==id) return TRUE;
	return FALSE;
}

aymeric's avatar
aymeric committed
174 175 176 177 178 179 180 181 182 183
int ms_filter_call_method_noarg(MSFilter *f, unsigned int id){
	return ms_filter_call_method(f,id,NULL);
}

void ms_filter_destroy(MSFilter *f){
	if (f->desc->uninit!=NULL)
		f->desc->uninit(f);
	if (f->inputs!=NULL)	ms_free(f->inputs);
	if (f->outputs!=NULL)	ms_free(f->outputs);
	ms_mutex_destroy(&f->lock);
184
	ms_filter_clear_notify_callback(f);
185
	ms_filter_clean_pending_events(f);
aymeric's avatar
aymeric committed
186 187 188 189
	ms_free(f);
}

void ms_filter_process(MSFilter *f){
190
	MSTimeSpec start,stop;
aymeric's avatar
aymeric committed
191
	ms_debug("Executing process of filter %s:%p",f->desc->name,f);
192 193

	if (f->stats)
194
		ms_get_cur_time(&start);
195

aymeric's avatar
aymeric committed
196
	f->desc->process(f);
197
	if (f->stats){
198
		ms_get_cur_time(&stop);
199
		f->stats->count++;
200
		f->stats->elapsed+=(stop.tv_sec-start.tv_sec)*1000000000LL + (stop.tv_nsec-start.tv_nsec);
201
	}
202

aymeric's avatar
aymeric committed
203 204
}

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
void ms_filter_task_process(MSFilterTask *task){
	MSTimeSpec start,stop;
	MSFilter *f=task->f;
	/*ms_message("Executing task of filter %s:%p",f->desc->name,f);*/

	if (f->stats)
		ms_get_cur_time(&start);

	task->taskfunc(f);
	if (f->stats){
		ms_get_cur_time(&stop);
		f->stats->count++;
		f->stats->elapsed+=(stop.tv_sec-start.tv_sec)*1000000000LL + (stop.tv_nsec-start.tv_nsec);
	}
	f->postponed_task--;
}

aymeric's avatar
aymeric committed
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
void ms_filter_preprocess(MSFilter *f, struct _MSTicker *t){
	f->last_tick=0;
	f->ticker=t;
	if (f->desc->preprocess!=NULL)
		f->desc->preprocess(f);
}

void ms_filter_postprocess(MSFilter *f){
	if (f->desc->postprocess!=NULL)
		f->desc->postprocess(f);
	f->ticker=NULL;
}

bool_t ms_filter_inputs_have_data(MSFilter *f){
	int i;
	for(i=0;i<f->desc->ninputs;i++){
		MSQueue *q=f->inputs[i];
		if (q!=NULL && q->q.q_mcount>0) return TRUE;
	}
	return FALSE;
}

244 245 246 247 248 249 250
void ms_filter_postpone_task(MSFilter *f, MSFilterFunc taskfunc){
	MSFilterTask *task;
	MSTicker *ticker=f->ticker;
	if (ticker==NULL){
		ms_error("ms_filter_postpone_task(): this method cannot be called outside of filter's process method.");
		return;
	}
251
	task=ms_new0(MSFilterTask,1);
252 253 254 255 256
	task->f=f;
	task->taskfunc=taskfunc;
	ticker->task_list=ms_list_prepend(ticker->task_list,task);
	f->postponed_task++;
}
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297

static void find_filters(MSList **filters, MSFilter *f ){
	int i,found;
	MSQueue *link;
	if (f==NULL) ms_fatal("Bad graph.");
	/*ms_message("seeing %s, seen=%i",f->desc->name,f->seen);*/
	if (f->seen){
		return;
	}
	f->seen=TRUE;
	*filters=ms_list_append(*filters,f);
	/* go upstream */
	for(i=0;i<f->desc->ninputs;i++){
		link=f->inputs[i];
		if (link!=NULL) find_filters(filters,link->prev.filter);
	}
	/* go downstream */
	for(i=0,found=0;i<f->desc->noutputs;i++){
		link=f->outputs[i];
		if (link!=NULL) {
			found++;
			find_filters(filters,link->next.filter);
		}
	}
	if (f->desc->noutputs>=1 && found==0){
		ms_fatal("Bad graph: filter %s has %i outputs, none is connected.",f->desc->name,f->desc->noutputs);
	}
}

MSList * ms_filter_find_neighbours(MSFilter *me){
	MSList *l=NULL;
	MSList *it;
	find_filters(&l,me);
	/*reset seen boolean for further lookups to succeed !*/
	for(it=l;it!=NULL;it=it->next){
		MSFilter *f=(MSFilter*)it->data;
		f->seen=FALSE;
	}
	return l;
}

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
void ms_connection_helper_start(MSConnectionHelper *h){
	h->last.filter=0;
	h->last.pin=-1;
}

int ms_connection_helper_link(MSConnectionHelper *h, MSFilter *f, int inpin, int outpin){
	int err=0;
	if (h->last.filter==NULL){
		h->last.filter=f;
		h->last.pin=outpin;
	}else{
		err=ms_filter_link(h->last.filter,h->last.pin,f,inpin);
		if (err==0){
			h->last.filter=f;
			h->last.pin=outpin;
		}
	}
	return err;
}

int ms_connection_helper_unlink(MSConnectionHelper *h, MSFilter *f, int inpin, int outpin){
	int err=0;
	if (h->last.filter==NULL){
		h->last.filter=f;
		h->last.pin=outpin;
	}else{
		err=ms_filter_unlink(h->last.filter,h->last.pin,f,inpin);
		if (err==0){
			h->last.filter=f;
			h->last.pin=outpin;
		}
	}
	return err;
}

333
void ms_filter_enable_statistics(bool_t enabled){
Simon Morlat's avatar
Simon Morlat committed
334
	ms_factory_enable_statistics(ms_factory_get_fallback(),enabled);
335 336 337
}

const MSList * ms_filter_get_statistics(void){
Simon Morlat's avatar
Simon Morlat committed
338
	return ms_factory_get_statistics(ms_factory_get_fallback());
339 340
}

341
void ms_filter_reset_statistics(void){
Simon Morlat's avatar
Simon Morlat committed
342
	ms_factory_reset_statistics(ms_factory_get_fallback());
343 344
}

Simon Morlat's avatar
Simon Morlat committed
345 346
void ms_filter_log_statistics(void){
	ms_factory_log_statistics(ms_factory_get_fallback());
347 348
}

Simon Morlat's avatar
Simon Morlat committed
349 350 351 352
const char * ms_format_type_to_string(MSFormatType type){
	switch(type){
		case MSAudio: return "MSAudio";
		case MSVideo: return "MSVideo";
353
	}
Simon Morlat's avatar
Simon Morlat committed
354
	return "invalid";
355
}