GF_Filter *gf_filter_new(GF_FilterSession *fsess, const GF_FilterRegister *freg, const char *src_args, const char *dst_args, GF_FilterArgType arg_type, GF_Err *err, GF_Filter *multi_sink_target, Bool is_dynamic_filter)
{
	char szName[200];
	const char *dst_striped = NULL;
	const char *src_striped = NULL;
	GF_Filter *filter;
	GF_Err e;
	u32 i;
	assert(fsess);

	GF_SAFEALLOC(filter, GF_Filter);
	if (!filter) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to alloc filter for %s\n", freg->name));
		return NULL;
	}
	filter->freg = freg;
	filter->session = fsess;
	filter->max_extra_pids = freg->max_extra_pids;
	filter->dynamic_filter = is_dynamic_filter;

	if (fsess->use_locks) {
		snprintf(szName, 200, "Filter%sPackets", filter->freg->name);
		filter->pcks_mx = gf_mx_new(szName);
	}

	//for now we always use a lock on the filter task lists
	//this mutex protects the task list and the number of process virtual tasks
	//we cannot remove it in non-threaded mode since we have no garantee that a filter won't use threading on its own
	snprintf(szName, 200, "Filter%sTasks", filter->freg->name);
	filter->tasks_mx = gf_mx_new(szName);

	filter->tasks = gf_fq_new(filter->tasks_mx);

	if (! (filter->session->flags & GF_FS_FLAG_NO_RESERVOIR)) {
		filter->pcks_shared_reservoir = gf_fq_new(filter->pcks_mx);
		filter->pcks_alloc_reservoir = gf_fq_new(filter->pcks_mx);
		filter->pcks_inst_reservoir = gf_fq_new(filter->pcks_mx);
	}

	filter->pending_pids = gf_fq_new(NULL);

	filter->blacklisted = gf_list_new();
	filter->destination_filters = gf_list_new();
	filter->destination_links = gf_list_new();
	filter->temp_input_pids = gf_list_new();

	filter->bundle_idx_at_resolution = -1;
	filter->cap_idx_at_resolution = -1;

	gf_mx_p(fsess->filters_mx);
	gf_list_add(fsess->filters, filter);
	gf_mx_v(fsess->filters_mx);

	filter->multi_sink_target = multi_sink_target;

	src_striped = src_args;
	dst_striped = NULL;
	switch (arg_type) {
	case GF_FILTER_ARG_EXPLICIT_SOURCE_NO_DST_INHERIT:
		filter->arg_type = arg_type = GF_FILTER_ARG_EXPLICIT_SOURCE;
		filter->no_dst_arg_inherit = GF_TRUE;
		break;
	case GF_FILTER_ARG_INHERIT_SOURCE_ONLY:
		filter->arg_type = arg_type = GF_FILTER_ARG_INHERIT;
		filter->no_dst_arg_inherit = GF_TRUE;
		break;
	default:
		filter->arg_type = arg_type;
		dst_striped = gf_filter_get_args_stripped(fsess, dst_args, GF_TRUE);
		break;
	}

	if ((arg_type!=GF_FILTER_ARG_EXPLICIT_SOURCE) && (arg_type!=GF_FILTER_ARG_EXPLICIT)) {
		src_striped = gf_filter_get_args_stripped(fsess, src_args, GF_FALSE);
	}

	//if we already concatenated our dst args to this source filter (eg this is an intermediate dynamically loaded one)
	//don't reappend the args
	if (dst_striped && src_striped && strstr(src_striped, dst_striped) != NULL) {
		dst_striped = NULL;
	}

	if (src_striped && dst_striped) {
		char *all_args;
		const char *dbsep;
		char *localarg_marker;
		u32 nb_db_sep=0, src_arg_len;
		char szDBSep[3];
		Bool insert_escape = GF_FALSE;
		Bool dst_sep_inserted = GF_FALSE;
		//source has a URL (local or not), escape it to make sure we don't pass dst args as params to the URL
		if ((strstr(src_striped, "src=") || strstr(src_striped, "dst=")) && strstr(src_striped, "://")){
			char szEscape[10];
			sprintf(szEscape, "%cgpac", fsess->sep_args);
			if (strstr(src_striped, szEscape)==NULL) {
				insert_escape = GF_TRUE;
			}
		}

		szDBSep[0] = szDBSep[1] = filter->session->sep_args;
		szDBSep[2] = 0;

		//handle the case where the first src arg was escaped ("filter::opt"), src args is now ":opt
		//consider we have one double sep
		if (src_striped[0] == filter->session->sep_args)
			nb_db_sep = 1;

		dbsep = src_striped;
		while (dbsep) {
			char *next_dbsep = strstr(dbsep, szDBSep);
			if (!next_dbsep) break;
			nb_db_sep++;
			dbsep = next_dbsep+2;
		}
		if (nb_db_sep % 2) nb_db_sep=1;
		else nb_db_sep=0;


		if (!nb_db_sep) {
			szDBSep[1] = 0;
		}
		//src_striped is ending with our separator, don't insert a new one
		src_arg_len = (u32)strlen(src_striped);
		if (src_arg_len && (src_striped[src_arg_len-1] == filter->session->sep_args)) {
			szDBSep[0] = 0;
		}

		//push src args
		all_args = NULL;
		filter_push_args(fsess, &all_args, (char *) src_striped, GF_TRUE, GF_FALSE);

		if (all_args && insert_escape) {
			gf_dynstrcat(&all_args, szDBSep, NULL);
			gf_dynstrcat(&all_args, "gpac", NULL);
			dst_sep_inserted = GF_TRUE;
		} else if (all_args) {
			gf_dynstrcat(&all_args, szDBSep, NULL);
			dst_sep_inserted = GF_TRUE;
		}
		//push dst args
		filter_push_args(fsess, &all_args, (char *) dst_striped, GF_FALSE, dst_sep_inserted);


		localarg_marker = all_args ? strstr(all_args, "gfloc") : NULL;
		if (localarg_marker) {
			localarg_marker[0]=0;
			if (strlen(all_args) && (localarg_marker[-1]==fsess->sep_args))
				localarg_marker[-1]=0;
		}
		e = gf_filter_new_finalize(filter, all_args, arg_type);
		filter->orig_args = all_args;
		src_striped = NULL;
	} else if (dst_striped) {
		e = gf_filter_new_finalize(filter, dst_striped, arg_type);
		filter->orig_args = gf_strdup(dst_striped);
		src_striped = NULL;
	} else {
		e = gf_filter_new_finalize(filter, src_striped, arg_type);
	}
	filter->dst_args = dst_args ? gf_strdup(dst_args) : NULL;

	if (e) {
		if (!filter->setup_notified && (e<0) ) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Error %s while instantiating filter %s\n", gf_error_to_string(e),freg->name));
			gf_filter_setup_failure(filter, e);
		}
		if (err) *err = e;
		//filter requested cancelation of filter session upon init
		if (e==GF_EOS) {
			fsess->run_status = GF_EOS;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s requested cancelation of filter session\n", freg->name));
		}
		return NULL;
	}
	if (filter && src_striped)
		filter->orig_args = gf_strdup(src_striped);

	for (i=0; i<freg->nb_caps; i++) {
		if (freg->caps[i].flags & GF_CAPFLAG_OUTPUT) {
			filter->has_out_caps = GF_TRUE;
			break;
		}
	}
	if (filter) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Created filter register %s args %s\n", freg->name, filter->orig_args ? filter->orig_args : "none"));
	}
	return filter;
}