//not all streams are ready, cannot setup period yet
	if (ctx->period_not_ready)
		return GF_OK;

	return dasher_setup_period(filter, ctx, NULL);
}

static GF_Err dasher_setup_period(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *inject_ds)
{
	u32 i, count, j, nb_sets;
	Bool has_muxed_bases=GF_FALSE;
	const char *remote_xlink = NULL;
	Bool has_as_id = GF_FALSE;
	Bool has_deps = GF_FALSE;
	const GF_PropertyValue *prop;
	GF_Fraction64 min_dur, min_adur, max_adur;
	u32 srd_rep_idx;

	ctx->dyn_rate = GF_FALSE;
	ctx->use_cues = GF_FALSE;
	min_dur.num = min_adur.num = max_adur.num = 0;
	min_dur.den = min_adur.den = max_adur.den = 1;
	srd_rep_idx = 2; //2 for compat with old arch

	count = gf_list_count(ctx->current_period->streams);
	//setup representations
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (inject_ds && (ds != inject_ds))
			continue;

		if (ds->stream_type == GF_STREAM_FILE) {
			if (ds->xlink) remote_xlink = ds->xlink;
		} else if (!ctx->is_period_restore) {
			//setup representation - the representation is created independently from the period
			dasher_setup_rep(ctx, ds, &srd_rep_idx);
		}
	}

	//setup representation dependency / components (muxed)
	for (i=0; i<count; i++) {
		Bool remove = GF_FALSE;
		GF_DashStream *ds_video=NULL;
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (inject_ds && (ds != inject_ds))
			continue;

		ds->period = ctx->current_period;
		ds->last_period = ds->period->period;

		if (ds->dyn_bitrate) ctx->dyn_rate = GF_TRUE;
		if (ds->inband_cues || ds->cues)
			ctx->use_cues = GF_TRUE;

		if (ctx->loop) {
			prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DURATION);
			if (prop && prop->value.lfrac.den) {
				GF_Fraction64 d;
				d.num = prop->value.lfrac.num;
				d.den = prop->value.lfrac.den;
				if (ds->clamped_dur.num && (ds->clamped_dur.num * d.den < d.num * ds->clamped_dur.den)) {
					d = ds->clamped_dur;
				}

				if (ds->stream_type == GF_STREAM_AUDIO) {
					if (d.num * max_adur.den > max_adur.num * d.den) max_adur = d;
					if (!min_adur.num || (d.num * min_adur.den < min_adur.num * d.den)) min_adur = d;
				} else {
					if (!min_dur.num || (d.num * min_dur.den < min_dur.num * d.den)) min_dur = d;
				}
			}
		}

		if (ds->stream_type == GF_STREAM_FILE) {
			remove = GF_TRUE;
		} else if (remote_xlink) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] period uses xlink but other media source %s, ignoring source\n", ds->src_url));
			remove = GF_TRUE;
		} else if (ctx->is_empty_period) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] empty period defined but other media source %s, ignoring source\n", ds->src_url));
			remove = GF_TRUE;
		}

		if (remove) {
			ds->done = 1;
			ds->period = NULL;
			gf_list_rem(ctx->current_period->streams, i);
			gf_list_add(ctx->next_period->streams, ds);
			i--;
			count--;
			continue;
		}

		if (ctx->gencues)
			ds->set_period_switch = GF_TRUE;

		if (ctx->is_period_restore) continue;

		//add period descriptors
		dasher_add_descriptors(&ctx->current_period->period->x_children, ds->p_period_desc);
		//add representation descriptors
		dasher_add_descriptors(&ds->rep->x_children, ds->p_rep_desc);

		if (ds->muxed_base) continue;

		if (ds->stream_type==GF_STREAM_VISUAL)
			ds_video = ds;

		ds->skip_tpl_reuse = GF_FALSE;
		// period resume (end of content replacement/splice/...): if using templates, check if period ID is used, if not force startNumber to resume
		prop = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
		if (prop && prop->value.string && ctx->tpl && ds->mpd_timescale) {
			char *template = ds->template;
			if (!template) template = ctx->template;
			if (
				//undefined period name
				!prop->value.string[0]
				//template dor not resolve against period name
				|| (template && !strstr(template, "$Period$"))
			) {
				u64 seg_duration = (u64)(ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
				u64 period_start = ds->mpd_timescale * ctx->mpd->media_presentation_duration / 1000;
				u64 num = period_start / seg_duration;
				if (num * seg_duration < period_start)
					num++;
				ds->startNumber = (u32) (num+1);
				ds->skip_tpl_reuse = GF_TRUE;
			}
		}

		ds->nb_comp = 1;

		for (j=0; j<count; j++) {
			GF_DashStream *a_ds;
			if (i==j) continue;
			a_ds = gf_list_get(ctx->current_period->streams, j);
			if (a_ds->dep_id && (a_ds->src_id==ds->src_id) && (a_ds->dep_id==ds->id) ) {
				gf_list_add(ds->complementary_streams, a_ds);
				has_deps = GF_TRUE;
				if (!a_ds->rep->dependency_id) {
					a_ds->rep->dependency_id = gf_strdup(ds->rep->id);
				}
			}
			//check if this rep should be muxed: same rep ID, not raw format, not CMAF
			if (a_ds->muxed_base) continue;
			if (ctx->muxtype==DASHER_MUX_RAW) continue;
			if (ctx->cmaf) continue;
			if (strcmp(a_ds->rep_id, ds->rep_id)) continue;
			if (a_ds->template && ds->template && strcmp(a_ds->template, ds->template)) continue;


			a_ds->muxed_base = ds;
			a_ds->dash_dur = ds->dash_dur;
			has_muxed_bases = GF_TRUE;
			ds->nb_comp++;

			if (ctx->bs_switch==DASHER_BS_SWITCH_MULTI) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Bitstream Swicthing mode \"multi\" is not supported with multiplexed representations, disabling bitstream switching\n"));
				ctx->bs_switch = DASHER_BS_SWITCH_OFF;
			}
			if (!ds->rep->codecs || !strstr(ds->rep->codecs, a_ds->rep->codecs)) {
				gf_dynstrcat(&ds->rep->codecs, a_ds->rep->codecs, ",");
			}

			if (ctx->profile == GF_DASH_PROFILE_AVC264_LIVE) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF AVC264 live profile\n\tswitching to regular live profile\n"));
				ctx->profile = GF_DASH_PROFILE_LIVE;
			} else if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in HbbTV 1.5 ISOBMFF live profile\n\tswitching to regular live profile\n"));
				ctx->profile = GF_DASH_PROFILE_LIVE;
			} else if (ctx->profile == GF_DASH_PROFILE_AVC264_ONDEMAND) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF AVC264 onDemand profile\n\tswitching to regular onDemand profile\n"));
				ctx->profile = GF_DASH_PROFILE_ONDEMAND;
			} else if (ctx->profile == GF_DASH_PROFILE_DASHIF_LL) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF Low Latency profile\n\tswitching to regular live profile\n"));
				ctx->profile = GF_DASH_PROFILE_LIVE;
			}
		}
		//use video as main stream for segmentation of muxed sources
		if (ds_video != ds) {
			for (j=0; j<count; j++) {
				GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
				if ((a_ds->muxed_base==ds) || (a_ds==ds)) {
					if (a_ds == ds_video)
						a_ds->muxed_base = NULL;
					else
						a_ds->muxed_base = ds_video;
				}
			}
		}

		if ((u32) ds->dash_dur.num * 1000 > ctx->def_max_seg_dur * ds->dash_dur.den )
			ctx->def_max_seg_dur = (u32) ((ds->dash_dur.num * 1000) / ds->dash_dur.den);
	}


	if (ctx->loop && max_adur.num) {
		if (max_adur.num * min_adur.den != min_adur.num * max_adur.den) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Audio streams in the period have different durations (min "LLU"/"LLD", max "LLU"/"LLD"), may result in bad synchronization while looping\n", min_adur.num, min_adur.den, max_adur.num, max_adur.den));
		}
		for (i=0; i<count; i++) {
			GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
			if (ds->duration.num * max_adur.den > max_adur.num * ds->duration.den) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Input %s: max audio duration "LLU"/"LLD" in the period is less than duration "LLU"/"LLD", clamping will happen\n", ds->src_url, max_adur.num, max_adur.den, ds->duration.num, ds->duration.den ));
			}
			ds->clamped_dur = max_adur;
		}
	}


	if (ctx->is_period_restore) return GF_OK;

	if (has_deps) {
		for (i=0; i<count; i++) {
			GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
			//assign rep bitrates
			if (ds->dep_id)
				ds->rep->bandwidth = dasher_get_dep_bitrate(ctx, ds);

			if (gf_list_count(ds->complementary_streams)) {
				u32 nb_str = gf_list_count(ds->complementary_streams);
				ds->moof_sn_inc = 1+nb_str;
				ds->moof_sn = 1;
				for (j=0; j<nb_str; j++) {
					GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, j);
					a_ds->moof_sn_inc = ds->moof_sn_inc;
					a_ds->moof_sn = ds->moof_sn + 1 + j;
				}
			}
		}
	}

	//moved all mux components after the base one, so that we do the segmentation on the main component
	if (has_muxed_bases) {
		//setup reps in adaptation sets
		for (i=0; i<count; i++) {
			GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
			if (!ds->muxed_base) continue;
			gf_list_rem(ctx->current_period->streams, i);
			gf_list_add(ctx->current_period->streams, ds);
		}
	}

	//setup reps in adaptation sets
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (ds->muxed_base) continue;
		//already setup
		if (ds->set) continue;

		//not setup, create new AS
		ds->set = gf_mpd_adaptation_set_new();
		ds->owns_set = GF_TRUE;
		//only set hls intra for visual stream if we have know for sure
		if ((ds->stream_type==GF_STREAM_VISUAL) && (ds->sync_points_type==DASHER_SYNC_NONE)) {
			ds->set->intra_only = GF_TRUE;
		}
		if (ctx->llhls) {
			ds->set->use_hls_ll = GF_TRUE;
			if (ctx->cdur.den)
				ds->set->hls_ll_target_frag_dur = ((Double)ctx->cdur.num) / ctx->cdur.den;
		}
		ds->set->udta = ds;
		if (ds->period_continuity_id) {
			GF_FilterPacket *pck = gf_filter_pid_get_packet(ds->ipid);
			if (pck && (gf_filter_pck_get_cts(pck) != ds->period_continuity_next_cts)) {
				gf_free(ds->period_continuity_id);
				ds->period_continuity_id = NULL;
			} else {
				GF_MPD_Descriptor *desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:period-continuity:2015", ds->period_continuity_id);
				gf_list_add(ds->set->supplemental_properties, desc);
			}
		}
		ds->period_continuity_next_cts = 0;


		if (ctx->mha_compat && ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA))) {
			prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_MHA_COMPATIBLE_PROFILES);
			if (prop) {
				ds->set->nb_alt_mha_profiles = prop->value.uint_list.nb_items;
				ds->set->alt_mha_profiles = prop->value.uint_list.vals;
				ds->set->alt_mha_profiles_only = (ctx->mha_compat==DASHER_MPHA_COMP_ONLY) ? GF_TRUE : GF_FALSE;
			}
		}

		if (!ds->set->representations)
			ds->set->representations = gf_list_new();
		if (!ds->period->period->adaptation_sets)
			ds->period->period->adaptation_sets = gf_list_new();
		gf_list_add(ds->period->period->adaptation_sets, ds->set);

		gf_list_add(ds->set->representations, ds->rep);
		ds->nb_rep = 1;

		//add non-conditional adaptation set descriptors
		dasher_add_descriptors(&ds->set->x_children, ds->p_as_any_desc);
		//new AS, add conditional adaptation set descriptors
		dasher_add_descriptors(&ds->set->x_children, ds->p_as_desc);

		if (ds->as_id) has_as_id = GF_TRUE;
		//for each following, check if same AS is possible
		for (j=i+1; j<count; j++) {
			GF_DashStream *a_ds;
			a_ds = gf_list_get(ctx->current_period->streams, j);
			//we add to the adaptation set even if shared rep, we will remove it when assigning templates and pids
			if (dasher_same_adaptation_set(ctx, ds, a_ds)) {
				a_ds->set = ds->set;

				gf_list_add(ds->set->representations, a_ds->rep);
				ds->nb_rep++;
				//add non-conditional adaptation set descriptors
				dasher_add_descriptors(&ds->set->x_children, a_ds->p_as_any_desc);
			}
		}
	}
	if (has_as_id) {
		for (i=0; i<count; i++) {
			GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
			if (!ds->owns_set) continue;
			for (j=i+1; j<count; j++) {
				GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
				//avoid as id duplicates
				if (ds->owns_set && a_ds->owns_set && (a_ds->as_id == ds->as_id)) {
					a_ds->as_id = 0;
				}
			}
		}
	}

	//we need a pass on adaptation sets to figure out if they share the same source URL
	//in case we use file name in templates
	nb_sets = gf_list_count(ctx->current_period->period->adaptation_sets);
	for (i=0; i<nb_sets; i++) {
		GF_DashStream *ds;
		GF_MPD_Representation *rep;
		GF_MPD_AdaptationSet *set;

		if (ctx->sigfrag)
			break;

		set = gf_list_get(ctx->current_period->period->adaptation_sets, i);
		assert(set);
		rep = gf_list_get(set->representations, 0);
		assert(rep);
		ds = rep->playback.udta;

		if (inject_ds && (ds != inject_ds))
			continue;

		if (!dasher_template_use_source_url(ds->template ? ds->template : ctx->template))
			continue;

		for (j=i+1; j<nb_sets; j++) {
			Bool split_init = GF_FALSE;
			const GF_PropertyValue *p1, *p2;
			GF_DashStream *a_ds;

			set = gf_list_get(ctx->current_period->period->adaptation_sets, j);
			rep = gf_list_get(set->representations, 0);
			assert(rep);
			a_ds = rep->playback.udta;

			if (!dasher_template_use_source_url(a_ds->template ? a_ds->template : ctx->template))
				continue;

			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) {
				if (gf_props_equal(p1, p2)) split_init = GF_TRUE;
			} else {
				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_init = GF_TRUE;
			}
			
			if (split_init) {
				ds->split_set_names = GF_TRUE;
				a_ds->split_set_names = GF_TRUE;
			}
		}
	}

	/*HbbTV 1.5 ISO live specific checks*/
	if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
		u32 nb_periods = dasher_period_count(ctx->current_period->streams);
		if (nb_sets > 16) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max 16 adaptation sets in HbbTV 1.5 ISO live profile\n\tswitching to DASH AVC/264 live profile\n"));
			ctx->profile = GF_DASH_PROFILE_AVC264_LIVE;
		}
		if (nb_periods > 32) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max 32 periods in HbbTV 1.5 ISO live profile\n\tswitching to regular DASH AVC/264 live profile\n"));
			ctx->profile = GF_DASH_PROFILE_AVC264_LIVE;
		}
		if (ctx->segdur.num < (s32) ctx->segdur.den) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Min segment duration 1s in HbbTV 1.5 ISO live profile\n\tcapping to 1s\n"));
			ctx->segdur.num = 1;
			ctx->segdur.den = 1;
		}
		if (ctx->segdur.num > 15 * (s32) ctx->segdur.den) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max segment duration 15s in HbbTV 1.5 ISO live profile\n\tcapping to 15s\n"));
			ctx->segdur.num = 15;
			ctx->segdur.den = 1;
		}
	}

	//init UTC reference time for dynamic
	if (!ctx->mpd->availabilityStartTime && (ctx->dmode!=GF_MPD_TYPE_STATIC) && !inject_ds) {
		u64 dash_start_date = ctx->ast ? gf_net_parse_date(ctx->ast) : 0;

		if (!ctx->utc_initialized) {
			dasher_init_utc(filter, ctx);

			//setup service description
			if (ctx->profile == GF_DASH_PROFILE_DASHIF_LL) {
				ctx->mpd->inject_service_desc = GF_TRUE;
			}
		}

		ctx->mpd->gpac_init_ntp_ms = gf_net_get_ntp_ms();
		ctx->mpd->availabilityStartTime = dasher_get_utc(ctx);
		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] MPD Availability start time initialized to "LLU" ms\n", ctx->mpd->availabilityStartTime));

		if (dash_start_date && (dash_start_date < ctx->mpd->availabilityStartTime)) {
			u64 start_date_sec_ntp, secs;
			Double ms;
			//recompute NTP init time matching the required ast
			secs = dash_start_date/1000;
			start_date_sec_ntp = (u32) secs;
			start_date_sec_ntp += GF_NTP_SEC_1900_TO_1970;
			ms = (Double) (dash_start_date - secs*1000);
			ms /= 1000.0;
			ctx->mpd->gpac_init_ntp_ms = (u64) (start_date_sec_ntp * 1000 + ms);
			//compute number of seconds to discard
			ctx->nb_secs_to_discard = (Double) (ctx->mpd->availabilityStartTime - dash_start_date);
			ctx->nb_secs_to_discard /= 1000;
			//don't discard TSB, this will be done automatically

			ctx->mpd->availabilityStartTime = dash_start_date;

		} else if (dash_start_date) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] specified AST %s seems in the future, ignoring it\n", ctx->ast));
		}
	}

	//setup adaptation sets bitstream switching
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (!ds->owns_set) continue;
		if (inject_ds && (ds != inject_ds))
			continue;
		//check bitstream switching
		dasher_check_bitstream_swicthing(ctx, ds->set);
		//setup AS defaults, roles and co
		dasher_setup_set_defaults(ctx, ds->set);
		//setup sources, templates & co
		dasher_setup_sources(filter, ctx, ds->set);
	}

	while (gf_list_count(ctx->postponed_pids)) {
		GF_DashStream *ds = gf_list_get(ctx->postponed_pids, 0);
		dasher_open_pid(filter, ctx, ds, ds->multi_pids, GF_FALSE);
	}

	//good to go !
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (inject_ds && (ds != inject_ds))
			continue;
		//setup segmentation
		ds->rep_init = GF_FALSE;
		ds->presentation_time_offset = 0;
		ds->seg_done = GF_FALSE;
		ds->next_seg_start = (u32) ( ((u64) ds->dash_dur.num * ds->timescale) / ds->dash_dur.den);
		ds->adjusted_next_seg_start = ds->next_seg_start;
		ds->segment_started = GF_FALSE;
		ds->seg_number = ds->startNumber;
		ds->first_cts = ds->first_dts = ds->max_period_dur = 0;

		//simulate N loops of the source
		if (ctx->nb_secs_to_discard) {
			u64 period_dur, seg_dur;
			u32 nb_skip=0;

			period_dur = (u64) (ctx->nb_secs_to_discard * ds->timescale);
			seg_dur = (u64) (ds->dash_dur.num) * ds->timescale / ds->dash_dur.den;

			nb_skip = (u32) (period_dur / seg_dur);
			ds->ts_offset += nb_skip*seg_dur;
			ds->seg_number += nb_skip;

			ds->max_period_dur = ds->cumulated_dur;
			ds->adjusted_next_seg_start += ds->ts_offset;
			ds->next_seg_start += ds->ts_offset;
		}
	}