			s->repeat_count = src_s->repeat_count;
			s->start_time = src_s->start_time;
			gf_list_add(tl->entries, s);
		}
	}
}

static void dasher_flush_segment(GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_last_in_period)
{
	u32 i, count;
	GF_DashStream *ds_done = NULL, *ds_not_done = NULL;
	GF_DashStream *set_ds = ds->set->udta;
	GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
	Bool has_ds_done = GF_FALSE;
	u32 seg_dur_ms=0;
	GF_DashStream *ds_log = NULL;
	u64 first_cts_in_cur_seg=0;

	//these are ignored
	if (ds->merged_tile_dep)
		return;

	ctx->update_report = -1;

	if (ds->segment_started) {
		Double seg_duration;
		u64 seg_duration_unscale = base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
		//seg_duration /= base_ds->timescale;
		if (!seg_duration_unscale) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Segment %d is empty - pid end of stream %d\n", ds->seg_number, gf_filter_pid_is_eos(ds->ipid) ));
		}
		seg_dur_ms = (u32) (seg_duration_unscale*1000 / base_ds->timescale);
		if (seg_dur_ms * base_ds->timescale < seg_duration_unscale* 1000) seg_dur_ms++;

		first_cts_in_cur_seg = ds->first_cts_in_seg;
		if (ctx->mpd->max_segment_duration < seg_dur_ms)
			ctx->mpd->max_segment_duration = seg_dur_ms;
		ctx->def_max_seg_dur = 0;

		seg_duration = (Double) base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
		seg_duration /= base_ds->timescale;

		if (ctx->sigfrag) {
			if (ds->no_seg_dur) {
				ds->gm_duration_total += seg_duration;
				ds->gm_nb_segments++;
				if (!ds->gm_duration_min || (ds->gm_duration_min>seg_duration) )
					ds->gm_duration_min = seg_duration;
				if (ds->gm_duration_max<seg_duration)
					ds->gm_duration_max = seg_duration;
				ds->dash_dur.num = (s32) (1000.0 * ds->gm_duration_total / ds->gm_nb_segments);
				ds->dash_dur.den = 1000;
				ds->rep->dash_dur = ds->dash_dur;
			}

			if (ds->rep->segment_list && (ds->rep->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->rep->segment_list->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_list->timescale / ds->dash_dur.den;
			}
			if (ds->set->segment_list && (ds->set->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->set->segment_list->duration = (u64) (ds->dash_dur.num) * ds->set->segment_list->timescale / ds->dash_dur.den;
			}
			if (ds->rep->segment_template && (ds->rep->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->rep->segment_template->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_template->timescale / ds->dash_dur.den;
			}
			if (ds->set && ds->set->segment_template && (ds->set->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->set->segment_template->duration = (u64) (ds->dash_dur.num) * ds->set->segment_template->timescale / ds->dash_dur.den;
			}
		}
		if (!base_ds->done && !base_ds->stl && ctx->tpl && !ctx->cues && !ctx->forward_mode && !is_last_in_period) {

			if (2 * seg_duration * ds->dash_dur.den < ds->dash_dur.num) {

				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g less than half DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
			} else if (2 * seg_duration * ds->dash_dur.den > 3 * ds->dash_dur.num) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g more than 3/2 DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
			}
		}
		dasher_insert_timeline_entry(ctx, base_ds);

		if (ctx->align) {
			if (!set_ds->nb_rep_done || !set_ds->set_seg_duration) {
				set_ds->set_seg_duration = seg_duration;
			} else {
				Double diff = set_ds->set_seg_duration - seg_duration;

				if (ABS(diff) > 0.001) {
					if (set_ds->set->segment_alignment || set_ds->set->subsegment_alignment) {
						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not aligned across representations: first rep segment duration %g but new segment duration %g for the same segment %d\n", set_ds->set_seg_duration, seg_duration, set_ds->seg_number));
					}
					if (ctx->profile != GF_DASH_PROFILE_FULL) {
						set_ds->set->segment_alignment = GF_FALSE;
						set_ds->set->subsegment_alignment = GF_FALSE;
						ctx->profile = GF_DASH_PROFILE_FULL;
						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] No segment alignment, switching to full profile\n"));

						if (set_ds->stl)
							dasher_copy_segment_timelines(ctx, set_ds->set);
					}
				}
			}
			set_ds->nb_rep_done++;
			if (set_ds->nb_rep_done < set_ds->nb_rep) {
				if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
					ds->subdur_done = GF_TRUE;
				return;
			}
			set_ds->set_seg_duration = 0;
			set_ds->nb_rep_done = 0;
		}

		ds_log = ds;
	} else {
		if (ctx->align) {
			set_ds->nb_rep_done++;
			if (set_ds->nb_rep_done < set_ds->nb_rep) return;

			set_ds->set_seg_duration = 0;
			set_ds->nb_rep_done = 0;
		}
	}

	if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
		ds->subdur_done = GF_TRUE;

	count = gf_list_count(ctx->current_period->streams);

	if (ctx->subdur) {
		u32 nb_sub_done=0;
		if (ctx->subdur_done) return;
		for (i=0; i<count; i++) {
			GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
			if (a_ds->muxed_base) {
				if (a_ds->muxed_base->subdur_done) a_ds->subdur_done = GF_TRUE;
			}

			if (a_ds->subdur_done) {
				nb_sub_done++;
			}
		}
		// if one of the AS is done and we are at 30% of target subdur, abort
		if (nb_sub_done && !ds->subdur_done
		 	&& (ctx->subdur && (ds->cumulated_dur >= (0.7 * (ds->cumulated_subdur + ctx->subdur)) * ds->timescale))
		) {
			ds->subdur_done = GF_TRUE;
			nb_sub_done++;
		}
		if (nb_sub_done==count)
 			ctx->subdur_done = GF_TRUE;
	}

	//reset all streams from our rep or our set
	for (i=0; i<count; i++) {
		ds = gf_list_get(ctx->current_period->streams, i);
		//reset all in set if segment alignment
		if (ctx->align) {
			if (ds->set != set_ds->set) continue;
		} else {
			//otherwise reset only media components for this rep
			if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;
		}

		if (!ds->done) {
			ds->first_cts_in_next_seg = ds->first_cts_in_seg = ds->est_first_cts_in_next_seg = 0;
		}

		if (ds->muxed_base) {
			if (!ds->done) {
				ds->segment_started = GF_FALSE;
				ds->seg_done = GF_FALSE;
			} else {
				has_ds_done = GF_TRUE;
			}
			continue;
		}
		base_ds = ds;

		if (base_ds->done)
			ds_done = base_ds;
		else if (base_ds->nb_comp_done==base_ds->nb_comp) ds_not_done = base_ds;

		if (!base_ds->done && base_ds->seg_done) {
			base_ds->seg_done = GF_FALSE;
			base_ds->nb_comp_done = 0;

#ifndef GPAC_DISABLE_LOG
			if (ctx->dmode>=GF_DASH_DYNAMIC) {
				u32 asid;
				s64 ast_diff;
				u64 seg_ast = ctx->mpd->availabilityStartTime;
				seg_ast += ctx->current_period->period->start;
				seg_ast += gf_timestamp_rescale(base_ds->adjusted_next_seg_start, base_ds->timescale, 1000);

				//if theoretical AST of the segment is less than the current UTC, we are producing the segment too late.
				ast_diff = (s64) dasher_get_utc(ctx);
				ast_diff -= seg_ast;

				asid = base_ds->set->id;
				if (!asid)
					asid = gf_list_find(ctx->current_period->period->adaptation_sets, base_ds->set) + 1;

				if (ast_diff>10) {
					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done TOO LATE by %d ms\n", asid, base_ds->rep->id, base_ds->seg_number, (s32) ast_diff));
				} else {
					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done %d ms %s UTC due time\n", asid, base_ds->rep->id, base_ds->seg_number, ABS(ast_diff), (ast_diff<0) ? "before" : "after"));
				}
			}
#endif

			assert(base_ds->segment_started);
			base_ds->segment_started = GF_FALSE;

			base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
			while (base_ds->next_seg_start <= base_ds->adjusted_next_seg_start) {
				base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
				if (ctx->skip_seg)
					base_ds->seg_number ++;
			}
			base_ds->adjusted_next_seg_start = base_ds->next_seg_start;
			base_ds->seg_number ++;
		}
	}

	if (ds_log) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[Dasher] Rep#%s flush seg %d start %g duration %g next seg end time %g\n", ds_log->rep->id, ds_log->seg_number-1, ((Double)first_cts_in_cur_seg)/ds_log->timescale, ((Double)seg_dur_ms)/1000, ((Double)ds_log->adjusted_next_seg_start)/ds_log->timescale));
	}

	//muxed representation with unaligned duration,
	if (has_ds_done) {
		for (i=0; i<count; i++) {
			ds = gf_list_get(ctx->current_period->streams, i);
			//otherwise reset only media components for this rep
			if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;

			if (ds->done && (base_ds->nb_comp_done < base_ds->nb_comp)) {
				base_ds->nb_comp_done++;
			}
		}
	}

	//some reps are done, other not, force a max time on all AS in the period
	if (ds_done && ds_not_done) {
		for (i=0; i<count; i++) {
			ds = gf_list_get(ctx->current_period->streams, i);

			if (ds->done) {
				if (ds->set->udta == set_ds)
			s->repeat_count = src_s->repeat_count;
			s->start_time = src_s->start_time;
			gf_list_add(tl->entries, s);
		}
	}
}

static void dasher_flush_segment(GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_last_in_period)
{
	u32 i, count;
	GF_DashStream *ds_done = NULL, *ds_not_done = NULL;
	GF_DashStream *set_ds = ds->set->udta;
	GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
	Bool has_ds_done = GF_FALSE;
	u32 seg_dur_ms=0;
	GF_DashStream *ds_log = NULL;
	u64 first_cts_in_cur_seg=0;

	 
	if (ds->merged_tile_dep)
		return;

	ctx->update_report = -1;

	if (ds->segment_started) {
		Double seg_duration;
		u64 seg_duration_unscale = base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
		 
		if (!seg_duration_unscale) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Segment %d is empty - pid end of stream %d\n", ds->seg_number, gf_filter_pid_is_eos(ds->ipid) ));
		}
		seg_dur_ms = (u32) (seg_duration_unscale*1000 / base_ds->timescale);
		if (seg_dur_ms * base_ds->timescale < seg_duration_unscale* 1000) seg_dur_ms++;

		first_cts_in_cur_seg = ds->first_cts_in_seg;
		if (ctx->mpd->max_segment_duration < seg_dur_ms)
			ctx->mpd->max_segment_duration = seg_dur_ms;
		ctx->def_max_seg_dur = 0;

		seg_duration = (Double) base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
		seg_duration /= base_ds->timescale;

		if (ctx->sigfrag) {
			if (ds->no_seg_dur) {
				ds->gm_duration_total += seg_duration;
				ds->gm_nb_segments++;
				if (!ds->gm_duration_min || (ds->gm_duration_min>seg_duration) )
					ds->gm_duration_min = seg_duration;
				if (ds->gm_duration_max<seg_duration)
					ds->gm_duration_max = seg_duration;
				ds->dash_dur.num = (s32) (1000.0 * ds->gm_duration_total / ds->gm_nb_segments);
				ds->dash_dur.den = 1000;
				ds->rep->dash_dur = ds->dash_dur;
			}

			if (ds->rep->segment_list && (ds->rep->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->rep->segment_list->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_list->timescale / ds->dash_dur.den;
			}
			if (ds->set->segment_list && (ds->set->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->set->segment_list->duration = (u64) (ds->dash_dur.num) * ds->set->segment_list->timescale / ds->dash_dur.den;
			}
			if (ds->rep->segment_template && (ds->rep->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->rep->segment_template->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_template->timescale / ds->dash_dur.den;
			}
			if (ds->set && ds->set->segment_template && (ds->set->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
				ds->set->segment_template->duration = (u64) (ds->dash_dur.num) * ds->set->segment_template->timescale / ds->dash_dur.den;
			}
		}
		if (!base_ds->done && !base_ds->stl && ctx->tpl && !ctx->cues && !ctx->forward_mode && !is_last_in_period) {

			if (2 * seg_duration * ds->dash_dur.den < ds->dash_dur.num) {

				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g less than half DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
			} else if (2 * seg_duration * ds->dash_dur.den > 3 * ds->dash_dur.num) {
				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g more than 3/2 DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
			}
		}
		dasher_insert_timeline_entry(ctx, base_ds);

		if (ctx->align) {
			if (!set_ds->nb_rep_done || !set_ds->set_seg_duration) {
				set_ds->set_seg_duration = seg_duration;
			} else {
				Double diff = set_ds->set_seg_duration - seg_duration;

				if (ABS(diff) > 0.001) {
					if (set_ds->set->segment_alignment || set_ds->set->subsegment_alignment) {
						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not aligned across representations: first rep segment duration %g but new segment duration %g for the same segment %d\n", set_ds->set_seg_duration, seg_duration, set_ds->seg_number));
					}
					if (ctx->profile != GF_DASH_PROFILE_FULL) {
						set_ds->set->segment_alignment = GF_FALSE;
						set_ds->set->subsegment_alignment = GF_FALSE;
						ctx->profile = GF_DASH_PROFILE_FULL;
						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] No segment alignment, switching to full profile\n"));

						if (set_ds->stl)
							dasher_copy_segment_timelines(ctx, set_ds->set);
					}
				}
			}
			set_ds->nb_rep_done++;
			if (set_ds->nb_rep_done < set_ds->nb_rep) {
				if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
					ds->subdur_done = GF_TRUE;
				return;
			}
			set_ds->set_seg_duration = 0;
			set_ds->nb_rep_done = 0;
		}

		ds_log = ds;
	} else {
		if (ctx->align) {
			set_ds->nb_rep_done++;
			if (set_ds->nb_rep_done < set_ds->nb_rep) return;

			set_ds->set_seg_duration = 0;
			set_ds->nb_rep_done = 0;
		}
	}

	if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
		ds->subdur_done = GF_TRUE;

	count = gf_list_count(ctx->current_period->streams);

	if (ctx->subdur) {
		u32 nb_sub_done=0;
		if (ctx->subdur_done) return;
		for (i=0; i<count; i++) {
			GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
			if (a_ds->muxed_base) {
				if (a_ds->muxed_base->subdur_done) a_ds->subdur_done = GF_TRUE;
			}

			if (a_ds->subdur_done) {
				nb_sub_done++;
			}
		}
		 
		if (nb_sub_done && !ds->subdur_done
		 	&& (ctx->subdur && (ds->cumulated_dur >= (0.7 * (ds->cumulated_subdur + ctx->subdur)) * ds->timescale))
		) {
			ds->subdur_done = GF_TRUE;
			nb_sub_done++;
		}
		if (nb_sub_done==count)
 			ctx->subdur_done = GF_TRUE;
	}

	 
	for (i=0; i<count; i++) {
		ds = gf_list_get(ctx->current_period->streams, i);
		 
		if (ctx->align) {
			if (ds->set != set_ds->set) continue;
		} else {
			 
			if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;
		}

		if (!ds->done) {
			ds->first_cts_in_next_seg = ds->first_cts_in_seg = ds->est_first_cts_in_next_seg = 0;
		}

		if (ds->muxed_base) {
			if (!ds->done) {
				ds->segment_started = GF_FALSE;
				ds->seg_done = GF_FALSE;
			} else {
				has_ds_done = GF_TRUE;
			}
			continue;
		}
		base_ds = ds;

		if (base_ds->done)
			ds_done = base_ds;
		else if (base_ds->nb_comp_done==base_ds->nb_comp) ds_not_done = base_ds;

		if (!base_ds->done && base_ds->seg_done) {
			base_ds->seg_done = GF_FALSE;
			base_ds->nb_comp_done = 0;

#ifndef GPAC_DISABLE_LOG
			if (ctx->dmode>=GF_DASH_DYNAMIC) {
				u32 asid;
				s64 ast_diff;
				u64 seg_ast = ctx->mpd->availabilityStartTime;
				seg_ast += ctx->current_period->period->start;
				seg_ast += gf_timestamp_rescale(base_ds->adjusted_next_seg_start, base_ds->timescale, 1000);

				 
				ast_diff = (s64) dasher_get_utc(ctx);
				ast_diff -= seg_ast;

				asid = base_ds->set->id;
				if (!asid)
					asid = gf_list_find(ctx->current_period->period->adaptation_sets, base_ds->set) + 1;

				if (ast_diff>10) {
					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done TOO LATE by %d ms\n", asid, base_ds->rep->id, base_ds->seg_number, (s32) ast_diff));
				} else {
					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done %d ms %s UTC due time\n", asid, base_ds->rep->id, base_ds->seg_number, ABS(ast_diff), (ast_diff<0) ? "before" : "after"));
				}
			}
#endif

			assert(base_ds->segment_started);
			base_ds->segment_started = GF_FALSE;

			base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
			while (base_ds->next_seg_start <= base_ds->adjusted_next_seg_start) {
				base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
				if (ctx->skip_seg)
					base_ds->seg_number ++;
			}
			base_ds->adjusted_next_seg_start = base_ds->next_seg_start;
			base_ds->seg_number ++;
		}
	}

	if (ds_log) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[Dasher] Rep#%s flush seg %d start %g duration %g next seg end time %g\n", ds_log->rep->id, ds_log->seg_number-1, ((Double)first_cts_in_cur_seg)/ds_log->timescale, ((Double)seg_dur_ms)/1000, ((Double)ds_log->adjusted_next_seg_start)/ds_log->timescale));
	}

	 
	if (has_ds_done) {
		for (i=0; i<count; i++) {
			ds = gf_list_get(ctx->current_period->streams, i);
			 
			if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;

			if (ds->done && (base_ds->nb_comp_done < base_ds->nb_comp)) {
				base_ds->nb_comp_done++;
			}
		}
	}

	 
	if (ds_done && ds_not_done) {
		for (i=0; i<count; i++) {
			ds = gf_list_get(ctx->current_period->streams, i);

			if (ds->done) {
				if (ds->set->udta == set_ds)