			gf_list_add(ctx->mpd->utc_timings, utc_t);
		}
	}
	gf_dm_sess_del(sess);
}


static GF_Err dasher_switch_period(GF_Filter *filter, GF_DasherCtx *ctx)
{
	u32 i, count, nb_done;
	char *period_id;
	const char *remote_xlink = NULL;
	const char *period_xlink = NULL;
	u64 remote_dur = 0;
	GF_DasherPeriod *p;
	GF_Fraction64 period_start, next_period_start;
	GF_DashStream *first_in_period=NULL;
	p = ctx->current_period;

	if (!ctx->gencues) {
		if (!ctx->out_path) {
			dasher_check_outpath(ctx);
		}
		if (ctx->current_period->period) {
			if (ctx->dyn_rate)
				dasher_update_dyn_bitrates(ctx);

			dasher_udpate_periods_and_manifest(filter, ctx);
		}
	}

	if (ctx->subdur_done || (ctx->current_period->period && (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST)) )
		return GF_EOS;

	if (ctx->current_period->period) {
		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of Period %s\n", ctx->current_period->period->ID ? ctx->current_period->period->ID : ""));
	}
	ctx->is_period_restore = GF_FALSE;
	ctx->is_empty_period = GF_FALSE;

	//safety check at period switch, probe each first packet in case we have a reconfigure pending
	count = gf_list_count(ctx->pids);
	for (i=0; i<count;i++) {
		GF_DashStream *ds = gf_list_get(ctx->pids, i);
		gf_filter_pid_get_packet(ds->ipid);
	}

	//reset - don't destroy, it is in the MPD
	ctx->current_period->period = NULL;
	//switch
	ctx->current_period = ctx->next_period;
	ctx->next_period = p;
	ctx->on_demand_done = GF_FALSE;
	ctx->min_segment_start_time = ctx->last_min_segment_start_time = 0;

	//reset input pids and detach output pids
	count = gf_list_count(ctx->current_period->streams);
	for (i=0; i<count;i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (ds->opid && !ctx->gencues) {
			gf_filter_pid_remove(ds->opid);
			ds->opid = NULL;
		}
		dasher_reset_stream(filter, ds, GF_FALSE);
		if (ds->reschedule) {
			ds->reschedule = GF_FALSE;
			ds->done = 0;
		}
	}

	//figure out next period
	count = gf_list_count(ctx->current_period->streams);
	ctx->period_idx = 0;
	period_start.num = -1;
	period_start.den = 1;
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);

		if (ds->done) continue;
		if (ds->period_start.num < 0) {
			s32 pstart = (s32) -ds->period_start.num;
			if (!ctx->period_idx || (pstart < ctx->period_idx)) ctx->period_idx = pstart;
		} else {
			if ((period_start.num<0) || (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den)) {
				period_start = ds->period_start;
				assert(ds->period_start.den);
			}
		}
	}

	if (period_start.num >= 0)
		ctx->period_idx = 0;

	if (ctx->first_context_load) {
		GF_Err e = dasher_reload_context(filter, ctx);
		if (e) {
			ctx->setup_failure = e;
			return e;
		}
		if (ctx->current_period->period) ctx->is_period_restore = GF_TRUE;

		if (ctx->dmode==GF_DASH_DYNAMIC_LAST) {
			dasher_udpate_periods_and_manifest(filter, ctx);
			count = gf_list_count(ctx->pids);
			for (i=0; i<count; i++) {
				GF_DashStream *ds = gf_list_get(ctx->pids, i);
				gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
			}
			return GF_EOS;
		}
	}

	//filter out PIDs not for this period
	count = gf_list_count(ctx->current_period->streams);
	period_id = NULL;
	for (i=0; i<count; i++) {
		Bool in_period=GF_TRUE;
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);

		if (ds->done) {
			in_period=GF_FALSE;
		} else if (!period_id) {
			period_id = ds->period_id;
			first_in_period = ds;
		} else if (strcmp(period_id, ds->period_id)) {
			in_period = GF_FALSE;
		}
		if (in_period) {
			if ((period_start.num>=0) && (ds->period_start.num * period_start.den != period_start.num * ds->period_start.den))
				in_period = GF_FALSE;
			else if ((ctx->period_idx>0) && ((s32) -ds->period_start.num != ctx->period_idx))
				in_period = GF_FALSE;

			if (!in_period && (first_in_period == ds))
				period_id = NULL;
		}

		//if not in period, move to next period
		if (!in_period) {
			gf_list_rem(ctx->current_period->streams, i);
			i--;
			count--;
			ds->period = NULL;
			gf_list_add(ctx->next_period->streams, ds);
			continue;
		}
		if (ds->stream_type == GF_STREAM_FILE) {
			if (ds->xlink) remote_xlink = ds->xlink;
			else ctx->is_empty_period = GF_TRUE;
			remote_dur = 0;
			if (ds->period_dur.den)
				remote_dur = (u64) (ds->period_dur.num * 1000) / ds->period_dur.den;
		} else if (!ctx->is_period_restore) {
			if (ds->xlink)
				period_xlink = ds->xlink;

			if (ctx->post_play_events) {
				GF_FilterEvent evt;

				GF_FEVT_INIT(evt, GF_FEVT_STOP, ds->ipid);
				gf_filter_pid_send_event(ds->ipid, &evt);

				gf_filter_pid_set_discard(ds->ipid, GF_FALSE);

				dasher_send_encode_hints(ctx, ds);

				GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
				evt.play.speed = 1.0;
				gf_filter_pid_send_event(ds->ipid, &evt);
			}
		}
	}
	ctx->post_play_events = GF_FALSE;

	count = gf_list_count(ctx->current_period->streams);
	if (!count) {
		count = gf_list_count(ctx->next_period->streams);
		nb_done = 0;
		for (i=0; i<count; i++)	 {
			GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
			if (ds->done) nb_done++;
		}
		if (nb_done == count) {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of MPD (no more active streams)\n"));
			ctx->on_demand_done = GF_TRUE;
			return GF_EOS;
		}
	}

	//we need a new period unless created during reload, create it
	if (!ctx->is_period_restore) {
		ctx->current_period->period = gf_mpd_period_new();
		if (!ctx->mpd) dasher_setup_mpd(ctx);
		gf_list_add(ctx->mpd->periods, ctx->current_period->period);
	}


	if (remote_xlink) {
		ctx->current_period->period->xlink_href = gf_strdup(remote_xlink);
		ctx->current_period->period->duration = remote_dur;
	}
	else if (period_xlink) {
		ctx->current_period->period->xlink_href = gf_strdup(period_xlink);
	}


	assert(period_id);

	next_period_start.num = -1;
	next_period_start.den = 1;
	if (period_start.num >= 0) {
		ctx->current_period->period->start = (u64)(period_start.num*1000 / period_start.den);
		//check next period start
		count = gf_list_count(ctx->next_period->streams);
		for (i=0; i<count; i++)	 {
			GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
			if (ds->done) continue;
			if (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den) continue;
			if ((next_period_start.num<0) || (next_period_start.num * ds->period_start.den > ds->period_start.num * next_period_start.den)) {
				next_period_start = ds->period_start;
			}
		}
		//check current period dur
		count = gf_list_count(ctx->current_period->streams);
		for (i=0; i<count; i++)	 {
			GF_Fraction64 dur;
			GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
			if (!ds->period_dur.den) continue;
			dur = period_start;
			if (ds->period_dur.den) {
				if (dur.den != ds->period_dur.den)
					dur.num += ds->period_dur.num * dur.den / ds->period_dur.den;
				else
					dur.num += ds->period_dur.num;
			}
			
			if ((next_period_start.num < 0) || (next_period_start.num * dur.den > dur.num * next_period_start.den))
				next_period_start = dur;
		}
		if (next_period_start.num > 0) {
			u64 next = next_period_start.num;
			if (next_period_start.den != period_start.den) {
				next *= period_start.den;
				next /= next_period_start.den;
			}
			ctx->current_period->period->duration = (u32) ( (next - period_start.num) * 1000 / period_start.den);
		}
	}

	//assign period ID if none specified
	if (strcmp(period_id, DEFAULT_PERIOD_ID))
		ctx->current_period->period->ID = gf_strdup(period_id);
	//assign ID if dynamic - if dash_ctx also assign ID since we could have moved from dynamic to static
	else if (!ctx->current_period->period->ID && ((ctx->dmode != GF_MPD_TYPE_STATIC) || ctx->state) ) {
		char szPName[50];
		sprintf(szPName, "DID%d", ctx->last_dyn_period_id + 1);
		ctx->current_period->period->ID = gf_strdup(szPName);
	}

	//check all streams are ready
	ctx->period_not_ready = GF_FALSE;
	count = gf_list_count(ctx->current_period->streams);
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		//assign force_rep_end
		if (next_period_start.num > 0) {
			u64 next = next_period_start.num;
			if (next_period_start.den != period_start.den) {
				next *= period_start.den;
				next /= next_period_start.den;
			}

			ds->force_rep_end = (u64) ((next - period_start.num) * ds->timescale / period_start.den);
		}
		if (ds->dcd_not_ready) {
			ctx->period_not_ready = GF_TRUE;
		}
			gf_list_add(ctx->mpd->utc_timings, utc_t);
		}
	}
	gf_dm_sess_del(sess);
}


static GF_Err dasher_switch_period(GF_Filter *filter, GF_DasherCtx *ctx)
{
	u32 i, count, nb_done;
	char *period_id;
	const char *remote_xlink = NULL;
	const char *period_xlink = NULL;
	u64 remote_dur = 0;
	GF_DasherPeriod *p;
	GF_Fraction64 period_start, next_period_start;
	GF_DashStream *first_in_period=NULL;
	p = ctx->current_period;

	if (!ctx->gencues) {
		if (!ctx->out_path) {
			dasher_check_outpath(ctx);
		}
		if (ctx->current_period->period) {
			if (ctx->dyn_rate)
				dasher_update_dyn_bitrates(ctx);

			dasher_udpate_periods_and_manifest(filter, ctx);
		}
	}

	if (ctx->subdur_done || (ctx->current_period->period && (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST)) )
		return GF_EOS;

	if (ctx->current_period->period) {
		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of Period %s\n", ctx->current_period->period->ID ? ctx->current_period->period->ID : ""));
	}
	ctx->is_period_restore = GF_FALSE;
	ctx->is_empty_period = GF_FALSE;

	 
	count = gf_list_count(ctx->pids);
	for (i=0; i<count;i++) {
		GF_DashStream *ds = gf_list_get(ctx->pids, i);
		gf_filter_pid_get_packet(ds->ipid);
	}

	 
	ctx->current_period->period = NULL;
	 
	ctx->current_period = ctx->next_period;
	ctx->next_period = p;
	ctx->on_demand_done = GF_FALSE;
	ctx->min_segment_start_time = ctx->last_min_segment_start_time = 0;

	 
	count = gf_list_count(ctx->current_period->streams);
	for (i=0; i<count;i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		if (ds->opid && !ctx->gencues) {
			gf_filter_pid_remove(ds->opid);
			ds->opid = NULL;
		}
		dasher_reset_stream(filter, ds, GF_FALSE);
		if (ds->reschedule) {
			ds->reschedule = GF_FALSE;
			ds->done = 0;
		}
	}

	 
	count = gf_list_count(ctx->current_period->streams);
	ctx->period_idx = 0;
	period_start.num = -1;
	period_start.den = 1;
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);

		if (ds->done) continue;
		if (ds->period_start.num < 0) {
			s32 pstart = (s32) -ds->period_start.num;
			if (!ctx->period_idx || (pstart < ctx->period_idx)) ctx->period_idx = pstart;
		} else {
			if ((period_start.num<0) || (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den)) {
				period_start = ds->period_start;
				assert(ds->period_start.den);
			}
		}
	}

	if (period_start.num >= 0)
		ctx->period_idx = 0;

	if (ctx->first_context_load) {
		GF_Err e = dasher_reload_context(filter, ctx);
		if (e) {
			ctx->setup_failure = e;
			return e;
		}
		if (ctx->current_period->period) ctx->is_period_restore = GF_TRUE;

		if (ctx->dmode==GF_DASH_DYNAMIC_LAST) {
			dasher_udpate_periods_and_manifest(filter, ctx);
			count = gf_list_count(ctx->pids);
			for (i=0; i<count; i++) {
				GF_DashStream *ds = gf_list_get(ctx->pids, i);
				gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
			}
			return GF_EOS;
		}
	}

	 
	count = gf_list_count(ctx->current_period->streams);
	period_id = NULL;
	for (i=0; i<count; i++) {
		Bool in_period=GF_TRUE;
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);

		if (ds->done) {
			in_period=GF_FALSE;
		} else if (!period_id) {
			period_id = ds->period_id;
			first_in_period = ds;
		} else if (strcmp(period_id, ds->period_id)) {
			in_period = GF_FALSE;
		}
		if (in_period) {
			if ((period_start.num>=0) && (ds->period_start.num * period_start.den != period_start.num * ds->period_start.den))
				in_period = GF_FALSE;
			else if ((ctx->period_idx>0) && ((s32) -ds->period_start.num != ctx->period_idx))
				in_period = GF_FALSE;

			if (!in_period && (first_in_period == ds))
				period_id = NULL;
		}

		 
		if (!in_period) {
			gf_list_rem(ctx->current_period->streams, i);
			i--;
			count--;
			ds->period = NULL;
			gf_list_add(ctx->next_period->streams, ds);
			continue;
		}
		if (ds->stream_type == GF_STREAM_FILE) {
			if (ds->xlink) remote_xlink = ds->xlink;
			else ctx->is_empty_period = GF_TRUE;
			remote_dur = 0;
			if (ds->period_dur.den)
				remote_dur = (u64) (ds->period_dur.num * 1000) / ds->period_dur.den;
		} else if (!ctx->is_period_restore) {
			if (ds->xlink)
				period_xlink = ds->xlink;

			if (ctx->post_play_events) {
				GF_FilterEvent evt;

				GF_FEVT_INIT(evt, GF_FEVT_STOP, ds->ipid);
				gf_filter_pid_send_event(ds->ipid, &evt);

				gf_filter_pid_set_discard(ds->ipid, GF_FALSE);

				dasher_send_encode_hints(ctx, ds);

				GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
				evt.play.speed = 1.0;
				gf_filter_pid_send_event(ds->ipid, &evt);
			}
		}
	}
	ctx->post_play_events = GF_FALSE;

	count = gf_list_count(ctx->current_period->streams);
	if (!count) {
		count = gf_list_count(ctx->next_period->streams);
		nb_done = 0;
		for (i=0; i<count; i++)	 {
			GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
			if (ds->done) nb_done++;
		}
		if (nb_done == count) {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of MPD (no more active streams)\n"));
			ctx->on_demand_done = GF_TRUE;
			return GF_EOS;
		}
	}

	 
	if (!ctx->is_period_restore) {
		ctx->current_period->period = gf_mpd_period_new();
		if (!ctx->mpd) dasher_setup_mpd(ctx);
		gf_list_add(ctx->mpd->periods, ctx->current_period->period);
	}


	if (remote_xlink) {
		ctx->current_period->period->xlink_href = gf_strdup(remote_xlink);
		ctx->current_period->period->duration = remote_dur;
	}
	else if (period_xlink) {
		ctx->current_period->period->xlink_href = gf_strdup(period_xlink);
	}


	assert(period_id);

	next_period_start.num = -1;
	next_period_start.den = 1;
	if (period_start.num >= 0) {
		ctx->current_period->period->start = (u64)(period_start.num*1000 / period_start.den);
		 
		count = gf_list_count(ctx->next_period->streams);
		for (i=0; i<count; i++)	 {
			GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
			if (ds->done) continue;
			if (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den) continue;
			if ((next_period_start.num<0) || (next_period_start.num * ds->period_start.den > ds->period_start.num * next_period_start.den)) {
				next_period_start = ds->period_start;
			}
		}
		 
		count = gf_list_count(ctx->current_period->streams);
		for (i=0; i<count; i++)	 {
			GF_Fraction64 dur;
			GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
			if (!ds->period_dur.den) continue;
			dur = period_start;
			if (ds->period_dur.den) {
				if (dur.den != ds->period_dur.den)
					dur.num += ds->period_dur.num * dur.den / ds->period_dur.den;
				else
					dur.num += ds->period_dur.num;
			}
			
			if ((next_period_start.num < 0) || (next_period_start.num * dur.den > dur.num * next_period_start.den))
				next_period_start = dur;
		}
		if (next_period_start.num > 0) {
			u64 next = next_period_start.num;
			if (next_period_start.den != period_start.den) {
				next *= period_start.den;
				next /= next_period_start.den;
			}
			ctx->current_period->period->duration = (u32) ( (next - period_start.num) * 1000 / period_start.den);
		}
	}

	 
	if (strcmp(period_id, DEFAULT_PERIOD_ID))
		ctx->current_period->period->ID = gf_strdup(period_id);
	 
	else if (!ctx->current_period->period->ID && ((ctx->dmode != GF_MPD_TYPE_STATIC) || ctx->state) ) {
		char szPName[50];
		sprintf(szPName, "DID%d", ctx->last_dyn_period_id + 1);
		ctx->current_period->period->ID = gf_strdup(szPName);
	}

	 
	ctx->period_not_ready = GF_FALSE;
	count = gf_list_count(ctx->current_period->streams);
	for (i=0; i<count; i++) {
		GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
		 
		if (next_period_start.num > 0) {
			u64 next = next_period_start.num;
			if (next_period_start.den != period_start.den) {
				next *= period_start.den;
				next /= next_period_start.den;
			}

			ds->force_rep_end = (u64) ((next - period_start.num) * ds->timescale / period_start.den);
		}
		if (ds->dcd_not_ready) {
			ctx->period_not_ready = GF_TRUE;
		}