}
	GF_SAFEALLOC(tr, DashTemplateRecord)
	tr->tpl = gf_strdup(tpl);
	gf_list_add(ctx->tpl_records, tr);
	return 0;
}

static void dasher_setup_sources(GF_Filter *filter, GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
{
	char szDASHTemplate[GF_MAX_PATH];
	char szTemplate[GF_MAX_PATH];
	char szSegmentName[GF_MAX_PATH];
	char szInitSegmentTemplate[GF_MAX_PATH];
	char szInitSegmentFilename[GF_MAX_PATH];
	char szIndexSegmentName[GF_MAX_PATH];
	char szSetFileSuffix[200], szDASHSuffix[220];
	const char *template = NULL;
	u32 as_id = 0;
	Bool single_template = GF_TRUE;
	u32 i, j, count, nb_base, nb_streams;
	GF_List *multi_pids = NULL;
	u32 set_timescale = 0;
	Bool init_template_done=GF_FALSE;
	Bool use_inband = ((ctx->bs_switch==DASHER_BS_SWITCH_INBAND) || (ctx->bs_switch==DASHER_BS_SWITCH_INBAND_PPS)) ? GF_TRUE : GF_FALSE;
	Bool template_use_source = GF_FALSE;
	Bool split_rep_names = GF_FALSE;
	Bool split_set_names = GF_FALSE;
	u32 force_ds_id;
	GF_DashStream *ds;
	GF_MPD_Representation *rep = gf_list_get(set->representations, 0);
	if (!rep) {
		assert(0);
		return;
	}
	ds = rep->playback.udta;

	count = gf_list_count(set->representations);

	if (!ctx->sigfrag) {
		assert(ctx->template);
		template = ((GF_DashStream *)set->udta)->template;
	}

	for (i=0; i<count; i++) {
		rep = gf_list_get(set->representations, i);
		ds = rep->playback.udta;
		if (!ds->template && !template) {}
		else if (ds->template && template && !strcmp(ds->template, template) ) {
		} else {
			single_template = GF_FALSE;
		}
		if (ds->template) template_use_source = dasher_template_use_source_url(ds->template);

		if (template_use_source) {
			single_template = GF_FALSE;
		}

		if (ds->as_id && !as_id)
			as_id = ds->as_id;

		if (ds->fps.den && ( (ds->fps.num*set->max_framerate.den) >= (s32) (set->max_framerate.num*ds->fps.den) )) {
			set->max_framerate.num = ds->fps.num;
			set->max_framerate.den = ds->fps.den;
			gf_media_get_reduced_frame_rate(&set->max_framerate.num, &set->max_framerate.den);
		}
		if (ds->width && ds->height) {
			if (!set->par) {
				GF_SAFEALLOC(set->par, GF_MPD_Fractional);
			}
			if (set->par) {
				set->par->num = ds->width;
				set->par->den = ds->height;
				gf_media_reduce_aspect_ratio(&set->par->num, &set->par->den);
			}
		}
	}
	if (!template) template = ctx->template;

	if (as_id) {
		set->id = ds->as_id;
	}
	if (ctx->sseg) {
		set->segment_alignment = GF_TRUE;
		set->starts_with_sap = 1;
	}

	if (count==1)
		single_template = GF_TRUE;
	else if (single_template) {
		//for regular reps, if we depend on filename we cannot mutualize the template
		if (dasher_template_use_source_url(template) ) {
			single_template = GF_FALSE;
		}
		//and for scalable reps, if we don't have bandwidth /repID we cannot mutualize the template
		else if (gf_list_count(ds->complementary_streams) ) {
			if (strstr(template, "$Bandwidth$") != NULL) single_template = GF_FALSE;
			else if (strstr(template, "$RepresentationId$") != NULL) single_template = GF_FALSE;
		}
	}

	if (set->lang) gf_free(set->lang);
	set->lang = gf_strdup(ds->lang ? ds->lang : "und");

	//check all streams in active period not in this set
	force_ds_id = ds->id;
	nb_streams = gf_list_count(ctx->current_period->streams);
	for (i=0; i<nb_streams; i++) {
		char *frag_uri;
		u32 len1, len2;
		GF_DashStream *ads = gf_list_get(ctx->current_period->streams, i);

		if (force_ds_id && (ads != ds) && (ads->id == ds->id)) {
			force_ds_id = 0;
		}

		if (ads->set == set) continue;
		frag_uri = strrchr(ds->src_url, '#');
		if (frag_uri) len1 = (u32) (frag_uri-1 - ds->src_url);
		else len1 = (u32) strlen(ds->src_url);
		frag_uri = strrchr(ads->src_url, '#');
		if (frag_uri) len2 = (u32) (frag_uri-1 - ads->src_url);
		else len2 = (u32) strlen(ads->src_url);

		if ((len1==len2) && !strncmp(ds->src_url, ads->src_url, len1)) {
			split_set_names = GF_TRUE;
			break;
		}
	}

	if (ctx->timescale>0) {
		set_timescale = ctx->timescale;
	} else {
		u32 first_timescale;
		rep = gf_list_get(set->representations, 0);
		ds = rep->playback.udta;
		first_timescale = ds->timescale;
		for (i=1; i<count; i++) {
			rep = gf_list_get(set->representations, i);
			ds = rep->playback.udta;
			if (ds->timescale != first_timescale) {
				//we cannot use a single template if enforcing timescales which are not identical
				single_template = GF_FALSE;
				break;
			}
		}
	}

	for (i=0; i<count; i++) {
		if (ctx->sigfrag)
			break;

		rep = gf_list_get(set->representations, i);
		ds = rep->playback.udta;

		if (!dasher_template_use_source_url(template))
			continue;

		if (ds->muxed_base)
			continue;

		for (j=i+1; j<count; j++) {
			const GF_PropertyValue *p1, *p2;
			GF_DashStream *a_ds;
			rep = gf_list_get(set->representations, j);
			a_ds = rep->playback.udta;

			if (a_ds->muxed_base == ds)
				continue;

			p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_FILEPATH);
			p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_FILEPATH);
			if (p1 && p2 && gf_props_equal(p1, p2)) split_rep_names = GF_TRUE;
			else if (!p1 && !p2) split_rep_names = GF_TRUE;
			p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
			p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_URL);
			if (p1 && p2 && gf_props_equal(p1, p2)) split_rep_names = GF_TRUE;
			else if (!p1 && !p2) split_rep_names = GF_TRUE;

			if (split_rep_names) break;
		}
		if (split_rep_names) break;
	}

	if (split_set_names) {
		if (!force_ds_id) {
			if (split_rep_names || !ds->split_set_names)
				force_ds_id = ds->id;
			else
				force_ds_id = gf_list_find(ctx->pids, ds) + 1;
		}
		sprintf(szSetFileSuffix, "_track%d_", force_ds_id);
	}

	//assign PID IDs - we assume only one component of a given media type per adaptation set
	//and assign the same PID ID for each component of the same type
	//we could refine this using roles, but most HAS solutions don't use roles at the mulitplexed level
	for (i=0; i<count; i++) {
		Bool is_bs_switching;
		rep = gf_list_get(set->representations, i);
		ds = rep->playback.udta;
		if (ds->pid_id) continue;
		//in bitstream switching mode, ensure each track in the set has the same ID
		//except when tile merge is used
		is_bs_switching = ds->tile_dep_id_merged ? GF_FALSE : set->bitstream_switching;
		if (is_bs_switching) {
			ctx->next_pid_id_in_period++;
			//except for base tile track where we force using input PID ID
			//to avoid messing up sabt/tbas references
			if (ds->tile_base) {
				ds->pid_id = ds->id;
				if (ctx->next_pid_id_in_period <= ds->pid_id)
					ctx->next_pid_id_in_period = ds->pid_id;
			} else {
				ds->pid_id = ctx->next_pid_id_in_period;
			}

			for (j=i+1; j<count; j++) {
				GF_DashStream *a_ds;
				rep = gf_list_get(set->representations, j);
				a_ds = rep->playback.udta;
				if (a_ds->pid_id) continue;
				if (a_ds->dep_id) continue;
				if (a_ds->stream_type == ds->stream_type) a_ds->pid_id = ds->pid_id;
			}
		}
		//otherwise copy over the source PID
		else {
			ds->pid_id = ds->id;
		}
	}

	for (i=0; i<count; i++) {
		rep = gf_list_get(set->representations, i);
		ds = rep->playback.udta;
		if (!ds->dep_id) continue;

		for (j=i+1; j<count; j++) {
			GF_DashStream *a_ds;
			rep = gf_list_get(set->representations, j);
			a_ds = rep->playback.udta;
			if (ds->dep_id == a_ds->id) {
				ds->dep_pid_id = a_ds->pid_id;
				break;
			}
		}
	}

	//this is crude because we don't copy the properties, we just pass a list of pids to the destination muxer !!
	//we should cleanup one of these days
	if (set->bitstream_switching && (ctx->bs_switch==DASHER_BS_SWITCH_MULTI)) {
		multi_pids = gf_list_new();
		for (i=0; i<count; i++) {
			rep = gf_list_get(set->representations, i);
			ds = rep->playback.udta;
			if (ds->owns_set) ds->multi_pids = multi_pids;
			gf_list_add(multi_pids, ds->ipid);
		}
	}

	if (ctx->cp!=GF_DASH_CPMODE_REPRESENTATION) {
		gf_mpd_del_list(set->content_protection, gf_mpd_descriptor_free, 0);
		set->content_protection = dasher_get_content_protection_desc(ctx, NULL, set);
	}

	for (i=0; i<count; i++) {
		GF_Err e;
		char szRawExt[20];
		Bool use_dash_suffix = GF_FALSE;
		Bool is_source_template = GF_FALSE;
		const char *seg_ext, *init_ext, *idx_ext, *force_init_seg_tpl;
#if 0
		GF_MPD_URL *force_init_seg_sl;
#endif
		Bool skip_init = GF_FALSE;
		GF_DashStream *tile_base_ds = NULL;
		Bool is_bs_switch;
		u32 reused_template_idx;
		u32 init_template_mode = GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE;
		rep = gf_list_get(set->representations, i);
		ds = rep->playback.udta;

		//remove representations for streams muxed with others, but still open the output
		if (ds->muxed_base) {
			GF_DashStream *ds_set = set->udta;
			gf_list_rem(set->representations, i);
			i--;
			count--;
			assert(ds_set->nb_rep);
			ds_set->nb_rep--;
			assert(ctx->sigfrag || ds->muxed_base->dst_filter);
			gf_list_transfer(ds->muxed_base->rep->audio_channels, rep->audio_channels);
			gf_list_transfer(ds->muxed_base->rep->base_URLs, rep->base_URLs);
			gf_list_transfer(ds->muxed_base->rep->content_protection , rep->content_protection);
			gf_list_transfer(ds->muxed_base->rep->essential_properties , rep->essential_properties);
			gf_list_transfer(ds->muxed_base->rep->frame_packing , rep->frame_packing);
			if (rep->x_children) {
				if (!ds->muxed_base->rep->x_children) ds->muxed_base->rep->x_children = gf_list_new();
				gf_list_transfer(ds->muxed_base->rep->x_children, rep->x_children);
			}
			gf_list_transfer(ds->muxed_base->rep->supplemental_properties , rep->supplemental_properties);

			gf_mpd_representation_free(ds->rep);
			ds->rep = NULL;

			if (!gf_list_count(ds->set->content_component)) {
				dasher_set_content_components(ds->muxed_base);
			}
			dasher_set_content_components(ds);
			assert(!multi_pids);
			//open PID
			dasher_open_pid(filter, ctx, ds, NULL, GF_FALSE);
			continue;
		}
		if (ds->template) {
			strcpy(szTemplate, ds->template);
			if (ctx->sigfrag) {
				const GF_PropertyValue *p = gf_filter_pid_get_property_str(ds->ipid, "source_template");
				if (p && p->value.boolean)
					is_source_template = GF_TRUE;
			}
		} else {
			strcpy(szTemplate, ctx->template ? ctx->template : "");
		}

		if (use_inband) {
			ds->inband_params = 1;
			if ((ds->codec_id==GF_CODECID_VVC) && (ctx->bs_switch==DASHER_BS_SWITCH_INBAND_PPS))
				ds->inband_params = 2;
		}

		//if bitstream switching and templating, only set for the first one
		if (i && set->bitstream_switching && ds->stl && single_template) continue;

		if (!set_timescale) set_timescale = ds->timescale;

		if (ctx->timescale<0) ds->mpd_timescale = ds->timescale;
		else ds->mpd_timescale = set_timescale;

		if (ctx->sigfrag && gf_filter_pid_get_property_str(ds->ipid, "source_template")) {
			const GF_PropertyValue *p = gf_filter_pid_get_property_str(ds->ipid, "stl_timescale");
			if (p && p->value.uint) {
				ds->mpd_timescale = p->value.uint;
			}
		}

		if (ds->nb_repeat && !ctx->loop) {
			if (split_set_names) {
				sprintf(szDASHSuffix, "%sp%d_", szSetFileSuffix, ds->nb_repeat+1);
			} else {
				sprintf(szDASHSuffix, "p%d_", ds->nb_repeat);
			}
			use_dash_suffix = GF_TRUE;
		} else if (split_set_names) {
			strcpy(szDASHSuffix, szSetFileSuffix);
			use_dash_suffix = GF_TRUE;
		}
		//we need dash suffix in template, but the template may be user-provided without dash suffix. If so add it
		//we don't add suffic if we have $RepresentationID or $Path set, we assume the user knows what he's doing
		if (use_dash_suffix && !strstr(szTemplate, "$FS$") && !strstr(szTemplate, "$RepresentationID$") && !strstr(szTemplate, "$Path=")) {
			strcat(szTemplate, "$FS$");
		}

		//resolve segment template
		e = gf_filter_pid_resolve_file_template(ds->ipid, szTemplate, szDASHTemplate, 0, use_dash_suffix ? szDASHSuffix : NULL);
		if (e) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot resolve template name %s, cannot derive output segment names, disabling rep %s\n", szTemplate, ds->src_url));
			gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
			ds->done = 1;
			continue;
		}


		if (single_template && ds->split_set_names && !use_dash_suffix) {
			char szStrName[20];
			sprintf(szStrName, "_set%d", 1 + gf_list_find(ctx->current_period->period->adaptation_sets, set)  );
			strcat(szDASHTemplate, szStrName);
		}
		else if (split_rep_names) {
			char szStrName[20];
			sprintf(szStrName, "_rep%d", 1 + gf_list_find(set->representations, ds->rep)  );
			strcat(szDASHTemplate, szStrName);
		}

		ds->rawmux = GF_FALSE;
		idx_ext = NULL;


		const char *def_ext = NULL;
		seg_ext = init_ext = NULL;

		if (ctx->muxtype==DASHER_MUX_TS) {
			def_ext = seg_ext = init_ext = "ts";
			if (!ctx->do_m3u8 && (ctx->subs_sidx>=0) )
				idx_ext = "idx";
		}
		else if (ctx->muxtype==DASHER_MUX_MKV) def_ext = "mkv";
		else if (ctx->muxtype==DASHER_MUX_WEBM) def_ext = "webm";
		else if (ctx->muxtype==DASHER_MUX_OGG) def_ext = "ogg";
		else if (ctx->muxtype==DASHER_MUX_RAW) {
			char *ext = (char *) gf_codecid_file_ext(ds->codec_id);
			if (ds->codec_id==GF_CODECID_RAW) {
				const GF_PropertyValue *p;
				if (ds->stream_type==GF_STREAM_VISUAL) {
					p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PIXFMT);
					if (p) ext = (char *) gf_pixel_fmt_sname(p->value.uint);
				}
				else if (ds->stream_type==GF_STREAM_AUDIO) {
					p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_AUDIO_FORMAT);
					if (p) ext = (char *) gf_audio_fmt_sname(p->value.uint);
				}
			}
			strncpy(szRawExt, ext ? ext : "raw", 19);
			szRawExt[19] = 0;
			ext = strchr(szRawExt, '|');
			if (ext) ext[0] = 0;
			def_ext = szRawExt;
		}

		if (!ds->muxed_base && ctx->rawsub && (ds->stream_type==GF_STREAM_TEXT) ) {
			char *ext_sub = (char *) gf_codecid_file_ext(ds->codec_id);
			if (ext_sub) {
				if (!strcmp(ext_sub, "subx"))
					ext_sub = "ttml";

				strncpy(szRawExt, ext_sub, 19);
				szRawExt[19] = 0;
				ext_sub = strchr(szRawExt, '|');
				if (ext_sub) ext_sub[0] = 0;
				def_ext = szRawExt;
				skip_init = GF_TRUE;
				ds->rawmux = GF_TRUE;
			}
		}

		if (ctx->segext && !stricmp(ctx->segext, "null")) {
			seg_ext = NULL;
		} else {
			seg_ext = ctx->segext;
			if (!seg_ext) seg_ext = def_ext ? def_ext : "m4s";
		}
		if (ctx->initext && !stricmp(ctx->initext, "null")) {
			init_ext = NULL;
		} else {
			init_ext = ctx->initext;
			if (!init_ext) init_ext = def_ext ? def_ext : "mp4";
		}

		//source template is used, do not use any extensions, they are present in the template
		if (is_source_template) {
			def_ext = NULL;
			init_ext = NULL;
			seg_ext = NULL;
		}

		is_bs_switch = set->bitstream_switching;
		//only used to force _init in default templates
		if (ds->tile_base) is_bs_switch = GF_FALSE;


		//check we are not reusing an existing template from previous periods, if so append a suffix
		//we check the final init name
		if (!ds->skip_tpl_reuse) {
			//1- init will not work in tiling as the base track and the first rep could end up having the same
			//segment template since base uses "init" while tile tracks don't for the init template
			//2- because of that, all evaluated templates must be the segment ones, otherwise we may check the audio init template
			//against a tile segment template, which will not match even though they use the same base template...
			//ex: template: seg_ => seg_trackN_init.mp4 for non-tiled vs seg_trackN_$number$.m4s for tiled, resulting in same N
			//being used for 2 track
			//cf issue 1849
			//we however don't want to change templates if they are indeed reused but resolve to something different due to representationID
			//we therefore resolve the segment template with startNumber 0 time 0, use this resolved name as base check.

			gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, is_bs_switch, szInitSegmentFilename, ds->rep_id, NULL, szDASHTemplate, is_source_template ? NULL : "mp4", 0, ds->bitrate, 0, ds->stl);

			reused_template_idx = dasher_check_template_reuse(ctx, szInitSegmentFilename);
			if (reused_template_idx) {
				char szExName[20];
				sprintf(szExName, "_r%d_", reused_template_idx);
				strcat(szDASHTemplate, szExName);
				//force template at representation level if more than one rep and templates have been reused
				if (gf_list_count(ds->set->representations)>1)
					single_template = GF_FALSE;
			}
		}
		
		//get final segment template with path resolution - output file name is NULL, we already have solved this
		gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE_WITH_PATH, is_bs_switch, szSegmentName, ds->rep_id, NULL, szDASHTemplate, seg_ext, 0, 0, 0, ds->stl);
		ds->seg_template = gf_strdup(szSegmentName);

		//get final segment template - output file name is NULL, we already have solved this
		gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE, is_bs_switch, szSegmentName, ds->rep_id, NULL, szDASHTemplate, seg_ext, 0, 0, 0, ds->stl);

		//get index templates
		if (idx_ext) {
			gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX_TEMPLATE_WITH_PATH, is_bs_switch, szIndexSegmentName, ds->rep_id, NULL, szDASHTemplate, idx_ext, 0, 0, 0, ds->stl);
			ds->idx_template = gf_strdup(szIndexSegmentName);

			gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX_TEMPLATE, is_bs_switch, szIndexSegmentName, ds->rep_id, NULL, szDASHTemplate, idx_ext, 0, 0, 0, ds->stl);
		}

		//get final init name - output file name is NULL, we already have solved this
		gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, is_bs_switch, szInitSegmentFilename, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, ds->bitrate, 0, ds->stl);

		//get final init template name - output file name is NULL, we already have solved this
		gf_media_mpd_format_segment_name(init_template_mode, is_bs_switch, szInitSegmentTemplate, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, 0, 0, ds->stl);

		if (ctx->sigfrag) {
			if (ctx->template || ds->template) {
				 if (is_source_template) {
					const GF_PropertyValue *mpd_url = gf_filter_pid_get_property_str(ds->ipid, "manifest_url");
					strcpy(szInitSegmentFilename, gf_file_basename(ds->src_url));

					if (ctx->out_path && mpd_url) {
						char *url, *mpd_out;
						mpd_out = gf_file_basename(ctx->out_path);
						u32 len = (u32) (mpd_out - ctx->out_path);
						char *mpd_src = mpd_url->value.string;
						if (!strncmp(ctx->out_path, mpd_url->value.string, len))
							mpd_src += len;

						const GF_PropertyValue *init_url = gf_filter_pid_get_property_str(ds->ipid, "init_url");
						if (init_url) {
							url = gf_url_concatenate(mpd_src, init_url->value.string);
							strcpy(szInitSegmentFilename, url);
							strcpy(szInitSegmentTemplate, url);
							gf_free(url);
						} else {
							//no init segment URL
							strcpy(szInitSegmentFilename, "");
							strcpy(szInitSegmentTemplate, "");
						}

						url = gf_url_concatenate(mpd_src, ds->template);
						strcpy(szSegmentName, url);
						gf_free(url);
					}
				 }
			} else {
				strcpy(szInitSegmentFilename, gf_file_basename(ds->src_url));
				strcpy(szSegmentName, gf_file_basename(ds->src_url));
			}
		}

		if (ctx->store_seg_states) {
			assert(!ds->pending_segment_states);
			ds->pending_segment_states = gf_list_new();
		}
		/* baseURLs */
		nb_base = ds->p_base_url ? ds->p_base_url->value.string_list.nb_items : 0;
		for (j=0; j<nb_base; j++) {
			GF_MPD_BaseURL *base_url;
			char *url = ds->p_base_url->value.string_list.vals[j];
			GF_SAFEALLOC(base_url, GF_MPD_BaseURL);
			if (base_url) {
				base_url->URL = gf_strdup(url);
				gf_list_add(rep->base_URLs, base_url);
			}
		}

		force_init_seg_tpl = NULL;
#if 0
		force_init_seg_sl = NULL;
#endif
		if (ds->codec_id==GF_CODECID_HEVC_TILES) {
			tile_base_ds = get_base_ds(ctx, ds);
			skip_init = GF_TRUE;
			if (tile_base_ds->rep->segment_template) force_init_seg_tpl = tile_base_ds->rep->segment_template->initialization;
			if (!force_init_seg_tpl && tile_base_ds->set->segment_template) force_init_seg_tpl = tile_base_ds->set->segment_template->initialization;

#if 0
			if (tile_base_ds->rep->segment_list) force_init_seg_sl = tile_base_ds->rep->segment_list->initialization_segment;
			if (!force_init_seg_sl && tile_base_ds->set->segment_list) force_init_seg_sl = tile_base_ds->set->segment_list->initialization_segment;
#endif
		}
		if (ctx->muxtype==DASHER_MUX_RAW) skip_init = GF_TRUE;
		else if (ctx->muxtype==DASHER_MUX_TS) skip_init = GF_TRUE;
		else if (ctx->muxtype==DASHER_MUX_OGG) skip_init = GF_TRUE;


		//forward mode, change segment names
		if (ctx->forward_mode) {
			u32 k, nb_pids = gf_list_count(ctx->pids);
			char *src = NULL;
			const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PCK_FILENAME);

			if (!p || !p->value.string) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL in forward mode, cannot forward\n"));
				ctx->in_error = GF_TRUE;
				return;
			}
			src = p->value.string;

			for (k=0; k<nb_pids; k++) {
				GF_DashStream *a_ds = gf_list_get(ctx->pids, k);
				if (ds == a_ds) continue;
				if (!a_ds->dst_filter) continue;

				p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PCK_FILENAME);
				if (!p || !p->value.string) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL in forward mode, cannot forward\n"));
					ctx->in_error = GF_TRUE;
					return;
				}
				//same init segment used (bs switching)
				if (!strcmp(p->value.string, src))
					skip_init = GF_TRUE;
			}
			strcpy(szInitSegmentFilename, src);
			strcpy(szInitSegmentTemplate, src);

			if (ctx->tpl) {
				p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_TEMPLATE);
				if (!p || !p->value.string) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source template in forward mode, cannot forward\n"));
					ctx->in_error = GF_TRUE;
					return;
				}
				strcpy(szSegmentName, p->value.string);
			}
		}

		ds->init_seg = gf_strdup(szInitSegmentFilename);

		//we use segment template
		if (ctx->tpl) {
			Bool use_single_init;
			GF_MPD_SegmentTemplate *seg_template;
			u32 start_number = ds->startNumber ? ds->startNumber : 1;
			u64 seg_duration = (u64)(ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;

			if (ds->inband_params && (ctx->bs_switch==DASHER_BS_SWITCH_INBAND_PPS)) {
				use_single_init = GF_FALSE;
				single_template = GF_FALSE;
			} else {
				use_single_init = (set->bitstream_switching || single_template) ? GF_TRUE : GF_FALSE;
			}

			//first rep in set and bs switching or single template, create segment template at set level
			if (!i && use_single_init ) {
				init_template_done = GF_TRUE;
				seg_template = NULL;
				if (!skip_init || force_init_seg_tpl || single_template) {
					GF_SAFEALLOC(seg_template, GF_MPD_SegmentTemplate);
					if (seg_template) {
						if (skip_init) {
							seg_template->initialization = force_init_seg_tpl ? gf_strdup(force_init_seg_tpl) : NULL;
							seg_template->hls_init_name = force_init_seg_tpl ? tile_base_ds->init_seg : NULL;
						} else {
							seg_template->initialization = gf_strdup(szInitSegmentTemplate);
							seg_template->hls_init_name = ds->init_seg;
						}
					}
				}
				set->segment_template = seg_template;

				dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);

				if (single_template) {
					seg_template->media = gf_strdup(szSegmentName);
					if (ds->idx_template)
						seg_template->index = gf_strdup(szIndexSegmentName);

					seg_template->timescale = ds->mpd_timescale;
					seg_template->start_number = start_number;
					seg_template->duration = seg_duration;

					if (ctx->asto>0) {
						seg_template->availability_time_offset = ctx->asto;
					}
				} else if (seg_template) {
					seg_template->start_number = (u32)-1;
				}
			}
			//non-first rep in set and single template, only open destination
			if (i && single_template) {
				dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, (set->bitstream_switching || skip_init) ? GF_TRUE : GF_FALSE);
			}
			//first rep in set and no bs switching or mutliple templates, create segment template at rep level
			else if (i || !single_template) {
				GF_SAFEALLOC(seg_template, GF_MPD_SegmentTemplate);
				if (seg_template) {
					rep->segment_template = seg_template;
					if (!init_template_done) {
						if (skip_init) {
							seg_template->initialization = force_init_seg_tpl ? gf_strdup(force_init_seg_tpl) : NULL;
							seg_template->hls_init_name = force_init_seg_tpl ? tile_base_ds->init_seg : NULL;
						} else {
							seg_template->initialization = gf_strdup(szInitSegmentTemplate);
							seg_template->hls_init_name = ds->init_seg;
						}
						dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
					} else if (i) {
						dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, (set->bitstream_switching || skip_init) ? GF_TRUE : GF_FALSE);
					}
					seg_template->media = gf_strdup(szSegmentName);
					if (ds->idx_template)
						seg_template->index = gf_strdup(szIndexSegmentName);
					seg_template->duration = seg_duration;
					seg_template->timescale = ds->mpd_timescale;
					seg_template->start_number = start_number;
					if (ctx->asto > 0) {
						seg_template->availability_time_offset = ctx->asto;
					}
				}
			}
		}
		/*we are using a single file or segment, use base url*/
		else if (ctx->sseg || ctx->sfile) {
			GF_MPD_BaseURL *baseURL;

			if (ds->init_seg) gf_free(ds->init_seg);
			ds->init_seg = gf_strdup(szInitSegmentFilename);

			GF_SAFEALLOC(baseURL, GF_MPD_BaseURL);
			if (!baseURL) continue;

			if (!rep->base_URLs) rep->base_URLs = gf_list_new();
			gf_list_add(rep->base_URLs, baseURL);

			if (ctx->sseg) {
				GF_MPD_SegmentBase *segment_base;
				baseURL->URL = gf_strdup(szInitSegmentFilename);
				GF_SAFEALLOC(segment_base, GF_MPD_SegmentBase);
				if (!segment_base) continue;
				rep->segment_base = segment_base;
				dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, GF_FALSE);
			} else {
				GF_MPD_SegmentList *seg_list;
				GF_SAFEALLOC(seg_list, GF_MPD_SegmentList);
				if (!seg_list) continue;
				GF_SAFEALLOC(seg_list->initialization_segment, GF_MPD_URL);
				if (!seg_list->initialization_segment) continue;
				seg_list->start_number = (u32) -1;
				baseURL->URL = gf_strdup(szInitSegmentFilename);
				seg_list->dasher_segment_name = gf_strdup(szSegmentName);
				seg_list->timescale = ds->mpd_timescale;
				seg_list->duration = (u64) (ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
				seg_list->segment_URLs = gf_list_new();
				rep->segment_list = seg_list;
				ds->pending_segment_urls = gf_list_new();

				dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
			}
		}
		//no template, no single file, we need a file list
		else {
			GF_MPD_SegmentList *seg_list;
			GF_SAFEALLOC(seg_list, GF_MPD_SegmentList);
			if (!seg_list) continue;

			if (!skip_init) {
				GF_SAFEALLOC(seg_list->initialization_segment, GF_MPD_URL);
				if (!seg_list->initialization_segment) continue;

				seg_list->initialization_segment->sourceURL = gf_strdup(szInitSegmentFilename);
			}
			seg_list->dasher_segment_name = gf_strdup(szSegmentName);
			seg_list->timescale = ds->mpd_timescale;
			seg_list->segment_URLs = gf_list_new();
			seg_list->start_number = (u32) -1;
			seg_list->duration = (u64) (ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
			rep->segment_list = seg_list;
			ds->pending_segment_urls = gf_list_new();