}
	} else {
		cts -= ds->first_cts;
	}
	return cts;
}

static void dasher_mark_segment_start(GF_DasherCtx *ctx, GF_DashStream *ds, GF_FilterPacket *pck, GF_FilterPacket *in_pck)
{
	GF_DASH_SegmentContext *seg_state=NULL;
	char szSegmentName[GF_MAX_PATH], szSegmentFullPath[GF_MAX_PATH], szIndexName[GF_MAX_PATH];
	GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;

	if (ctx->forward_mode) {
		const GF_PropertyValue *p_fname, *p_manifest;

		p_fname = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FILENAME);
		p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FILENUM);
		if (!p_fname || !p_fname->value.string || !p_manifest) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL / idx of segment in forward mode, cannot forward\n"));
			ctx->in_error = GF_TRUE;
			return;
		}
		strcpy(szSegmentName, p_fname->value.string);
		//remove filename property
		if (pck)
			gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, NULL);

		//check for manifest update
		p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_DASH_MANIFEST);
		if (p_manifest) {
			if (p_manifest->value.string) {
				if (strstr(p_manifest->value.string, "<MPD")) {
					if (ctx->do_m3u8) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest forward mode got DASH MPD but output is HLS M3U8, cannot operate - change formats or dasher forward mode\n"));
						ctx->in_error = GF_TRUE;
						return;
					} else {
						dasher_forward_mpd(ctx, p_manifest->value.string);
					}
				} else {
					if (ctx->do_mpd) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest forward mode got M3U8 but output is DASH MPD, cannot operate - change formats or dasher forward mode\n"));
						ctx->in_error = GF_TRUE;
						return;
					} else {
						dasher_forward_manifest_raw(ctx, ds, p_manifest->value.string, NULL);
					}
				}
			}
			if (pck)
				gf_filter_pck_set_property(pck, GF_PROP_PCK_DASH_MANIFEST, NULL);
		}

		//check for HLS child playlist update
		p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_HLS_VARIANT);
		p_fname = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_HLS_VARIANT_NAME);
		if (p_manifest && p_fname && (p_fname->value.string_list.nb_items==p_manifest->value.string_list.nb_items)) {
			u32 i, count = p_fname->value.string_list.nb_items;
			for (i=0; i<count; i++)
				dasher_forward_manifest_raw(ctx, ds, p_manifest->value.string_list.vals[i], p_fname->value.string_list.vals[i]);
		}
		if (pck) {
			if (p_manifest)
				gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_VARIANT, NULL);
			if (p_fname)
				gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_VARIANT_NAME, NULL);
		}

		//we need to move from segment name to output name
		if (ctx->forward_mode==DASHER_FWD_ALL)
			goto send_packet;
	}

	if (pck) {
		if (ctx->ntp==DASHER_NTP_YES) {
			u64 ntpts = gf_net_get_ntp_ts();
			gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, &PROP_LONGUINT(ntpts));
		} else if (ctx->ntp==DASHER_NTP_REM) {
			gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, NULL);
		}
		if (!ctx->gencues) {
			gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENUM, NULL );
			gf_filter_pck_set_property(pck, GF_PROP_PCK_IDXFILENAME, NULL );
			gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, NULL );
		}

		gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENUM, &PROP_UINT(base_ds->seg_number ) );

		if (ctx->gencues) {
			gf_filter_pck_set_property(pck, GF_PROP_PCK_CUE_START, &PROP_BOOL(GF_TRUE));
			if (ds->set_period_switch) {
				ds->set_period_switch = GF_FALSE;
				gf_filter_pck_set_property(pck, GF_PROP_PID_DASH_PERIOD_START, &PROP_LONGUINT(0) );
			}
		}
	}

	//only signal file name & insert timelines on one stream for muxed representations
	if (ds->muxed_base) return;

	ds->seg_start_time = ds->first_cts_in_seg;
	if (ds->timescale != ds->mpd_timescale) {
		ds->seg_start_time = gf_timestamp_rescale(ds->seg_start_time, ds->timescale, ds->mpd_timescale);
	}
	ds->last_min_segment_start_time = ds->first_cts_in_seg;
	ds->last_min_segment_start_time *= 1000;
	ds->last_min_segment_start_time /= ds->timescale;

	if (ds->last_min_segment_start_time > ctx->min_segment_start_time)
		ctx->min_segment_start_time = ds->last_min_segment_start_time;

	if (ctx->store_seg_states) {
		char *kms_uri;
		const GF_PropertyValue *p;
		assert(!ctx->gencues);
		if (!ds->rep->state_seg_list) {
			ds->rep->state_seg_list = gf_list_new();
		}
		if (!ds->rep->dash_dur.num) {
			ds->rep->timescale = ds->timescale;
			ds->rep->streamtype = ds->stream_type;
			ds->rep->timescale_mpd = ds->mpd_timescale;
			p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_HLS_GROUPID);
			if (p)
				ds->rep->groupID = p->value.string;

			ds->rep->dash_dur = ds->dash_dur;
			if (ctx->sigfrag) {
				const GF_PropertyValue *pid_url = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
				if (pid_url && pid_url->value.string) {
					ds->rep->hls_single_file_name = dasher_strip_base(ctx, pid_url->value.string);
				}
			}

			if (!ds->rep->hls_single_file_name) {
				switch (ctx->muxtype) {
				case DASHER_MUX_TS:
				case DASHER_MUX_OGG:
				case DASHER_MUX_RAW:
					break;
				default:
					if (ds->set->bitstream_switching && ds->set->segment_template)
						ds->rep->hls_single_file_name = ds->set->segment_template->hls_init_name;
					else
						ds->rep->hls_single_file_name = ds->init_seg;
				}
			}
			ds->rep->nb_chan = ds->nb_ch;
			ds->rep->m3u8_name = ds->hls_vp_name;
			if (ds->fps.den) {
				ds->rep->fps = ds->fps.num;
				ds->rep->fps /= ds->fps.den;
			}
		}
		GF_SAFEALLOC(seg_state, GF_DASH_SegmentContext);
		if (!seg_state) return;
		seg_state->time = ds->seg_start_time;
		seg_state->seg_num = ds->seg_number;
		seg_state->llhls_mode = ctx->llhls;
		ds->current_seg_state = seg_state;
		seg_state->encrypted = GF_FALSE;

		p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_HLS_KMS);
		kms_uri = (p && p->value.string) ? p->value.string : NULL;
		if (ds->tci) {
			u32 s;
			ds->iv_low++;
			if (ds->iv_low == 0)
				ds->iv_high++;
			for (s=0; s<8; s++)
				seg_state->hls_iv[s] = (ds->iv_high >> 8*(7-s) ) & 0xFF;
			for (s=0; s<8; s++)
				seg_state->hls_iv[s+8] = (ds->iv_low >> 8*(7-s) ) & 0xFF;

			seg_state->encrypted = GF_TRUE;
			gf_cryptfout_push_key(ds->dst_filter, & ds->tci->keys[ds->key_idx].key, &seg_state->hls_iv);

			if (ds->tci->keys[ds->key_idx].hls_info)
				kms_uri = ds->tci->keys[ds->key_idx].hls_info;

			ds->nb_crypt_seg++;
			if (ds->tci->keyRoll) {
				if (ds->nb_crypt_seg == ds->tci->keyRoll) {
					ds->nb_crypt_seg = 0;
					ds->key_idx = (ds->key_idx+1) % ds->tci->nb_keys;
				}
			}
		}
		//we need a hard copy as the pid may reconfigure before we flush the segment
		if (kms_uri) {
			//insert IV if not mp4
			if (!ds->tci && !strstr(kms_uri, "IV=") && (ctx->muxtype!=DASHER_MUX_ISOM)) {
				p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_CENC_KEY_INFO);
				if (p && (p->value.data.size==37)) {
					char *kms_iv=NULL;
					u8 *iv=p->value.data.ptr + 21;
					char szIV[40];
					u32 i;
					strcpy(szIV, "IV=0x");
					for (i=0; i<16; i++) {
						char szVal[3];
						sprintf(szVal, "%02X", iv[i]);
						strcat(szIV, szVal);
					}
					if (!strstr(kms_uri, "URI=")) {
						gf_dynstrcat(&kms_iv, "URI=\"", NULL);
						gf_dynstrcat(&kms_iv, kms_uri, NULL);
						gf_dynstrcat(&kms_iv, "\"", NULL);
					} else {
						gf_dynstrcat(&kms_iv, kms_uri, NULL);
					}
					gf_dynstrcat(&kms_iv, szIV, ",");
					seg_state->hls_key_uri = kms_iv;
				}
			}
			if (!seg_state->hls_key_uri) {
				if (!strstr(kms_uri, "URI=")) {
					gf_dynstrcat(&seg_state->hls_key_uri, "URI=\"", NULL);
					gf_dynstrcat(&seg_state->hls_key_uri, kms_uri, NULL);
					gf_dynstrcat(&seg_state->hls_key_uri, "\"", NULL);
				} else {
					seg_state->hls_key_uri = gf_strdup(kms_uri);
				}
			}
		}

		gf_list_add(ds->rep->state_seg_list, seg_state);
		if (ctx->sigfrag) {
			const GF_PropertyValue *frag_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FRAG_RANGE);
			const GF_PropertyValue *frag_url = gf_filter_pck_get_property(in_pck, GF_PROP_PID_URL);
			if (frag_url && frag_url->value.string) {
				char *f_url = dasher_strip_base(ctx, frag_url->value.string);
				seg_state->filename = gf_strdup(f_url);
			}
			else if (frag_range) {
				seg_state->file_offset = frag_range->value.lfrac.num;
				seg_state->file_size = (u32) (frag_range->value.lfrac.den - seg_state->file_offset);

				if (ds->rep->segment_base && !ds->rep->segment_base->initialization_segment) {
					GF_MPD_URL *url;
					GF_SAFEALLOC(url, GF_MPD_URL);
					if (url) {
						GF_SAFEALLOC(url->byte_range, GF_MPD_ByteRange);
						if (url->byte_range) {
							url->byte_range->start_range = 0;
							url->byte_range->end_range = seg_state->file_offset-1;
						}
					}
					ds->rep->segment_base->initialization_segment = url;

					//first seg, remove sidx if present
					//our frag_range includes sidx
					const GF_PropertyValue *sidx_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_SIDX_RANGE);
					if (sidx_range) {
						u64 sidx_size = (u64)sidx_range->value.lfrac.den;
						sidx_size -= (u64) sidx_range->value.lfrac.num;
						seg_state->file_offset += sidx_size;
						seg_state->file_size -= (u32) sidx_size;
					}
				}

			} else {
				gf_list_del_item(ds->rep->state_seg_list, seg_state);
				gf_free(seg_state);
				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest generation only but not fragment information in packet, source demux not properly configured\n"));
				ctx->in_error = GF_TRUE;
			}
		} else {
			gf_list_add(ds->pending_segment_states, seg_state);
			ctx->nb_seg_url_pending++;
		}
	}

	szIndexName[0] = 0;
	if (ds->idx_template) {
		//get final segment template - output file name is NULL, we already have solved this in source_setup
		gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, ds->set->bitstream_switching, szIndexName, base_ds->rep_id, NULL, base_ds->idx_template, NULL, base_ds->seg_start_time, base_ds->rep->bandwidth, base_ds->seg_number, base_ds->stl);

		strcpy(szSegmentFullPath, szIndexName);
		if (ctx->out_path) {
			char *rel = gf_url_concatenate(ctx->out_path, szIndexName);
			if (rel) {
				strcpy(szSegmentFullPath, rel);
				gf_free(rel);
			}
		}
		if (pck)
			gf_filter_pck_set_property(pck, GF_PROP_PCK_IDXFILENAME, &PROP_STRING(szSegmentFullPath) );
	}

	if (ctx->sseg) {
		if (ctx->gencues) return;

		if (ctx->sigfrag) {
			const GF_PropertyValue *p = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_SIDX_RANGE);
			if (p) {
				if (ds->rep->segment_base && !ds->rep->segment_base->index_range) {
					GF_SAFEALLOC(ds->rep->segment_base->index_range, GF_MPD_ByteRange);
					if (ds->rep->segment_base->index_range) {
						ds->rep->segment_base->index_range->start_range = p->value.lfrac.num;
						ds->rep->segment_base->index_range->end_range = p->value.lfrac.den-1;
						ds->rep->segment_base->index_range_exact = GF_TRUE;
					}

					if (!ds->rep->segment_base->initialization_segment) {
						GF_SAFEALLOC(ds->rep->segment_base->initialization_segment, GF_MPD_URL);
					}
					if (ds->rep->segment_base->initialization_segment && !ds->rep->segment_base->initialization_segment->byte_range) {
						GF_SAFEALLOC(ds->rep->segment_base->initialization_segment->byte_range, GF_MPD_ByteRange);
						if (ds->rep->segment_base->initialization_segment->byte_range) {
							ds->rep->segment_base->initialization_segment->byte_range->start_range = 0;
							ds->rep->segment_base->initialization_segment->byte_range->end_range = p->value.lfrac.num-1;
						}
					}
				} else {
					ctx->in_error = GF_TRUE;
					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Several SIDX found but trying to regenerate an on-demand MPD, source file is not compatible. Try re-dashing the content or use main or full profiles\n"));
				}
			}
		}
		return;
	}

	if (ctx->sfile) {
		GF_MPD_SegmentURL *seg_url;
		assert(ds->rep->segment_list);

		if (ctx->gencues) return;

		GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
		if (!seg_url) return;

		gf_list_add(ds->rep->segment_list->segment_URLs, seg_url);
		if (szIndexName[0])
			seg_url->index = gf_strdup(szIndexName);

		if (ctx->sigfrag) {
			const GF_PropertyValue *frag_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FRAG_RANGE);
			const GF_PropertyValue *frag_url = gf_filter_pck_get_property(in_pck, GF_PROP_PID_URL);
			if (frag_url && frag_url->value.string) {
				seg_url->media = gf_strdup(dasher_strip_base(ctx, frag_url->value.string));
				if (ds->rep->segment_list && ds->rep->segment_list->initialization_segment && !ds->rep->segment_list->initialization_segment->sourceURL) {

					frag_url = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
					if (frag_url && frag_url->value.string) {
						u32 j, nb_base;
						ds->rep->segment_list->initialization_segment->sourceURL = gf_strdup(dasher_strip_base(ctx, frag_url->value.string) );

						nb_base = gf_list_count(ds->rep->base_URLs);
						for (j=0; j<nb_base; j++) {
							GF_MPD_BaseURL *burl = gf_list_get(ds->rep->base_URLs, j);
							if (! strcmp(burl->URL, frag_url->value.string)) {
								gf_list_rem(ds->rep->base_URLs, j);
								gf_mpd_base_url_free(burl);
								break;
							}
						}
					}
				}
			}
			else if (frag_range) {
				GF_SAFEALLOC(seg_url->media_range, GF_MPD_ByteRange);
				if (seg_url->media_range) {
					seg_url->media_range->start_range = frag_range->value.lfrac.num;
					seg_url->media_range->end_range = frag_range->value.lfrac.den - 1;
				}
				if (ds->rep->segment_list && ds->rep->segment_list->initialization_segment && !ds->rep->segment_list->initialization_segment->byte_range) {
					GF_SAFEALLOC(ds->rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
					if (ds->rep->segment_list->initialization_segment->byte_range) {
						ds->rep->segment_list->initialization_segment->byte_range->start_range = 0;
						ds->rep->segment_list->initialization_segment->byte_range->end_range = frag_range->value.lfrac.num-1;
					}
				}
			}
		} else {
			gf_list_add(ds->pending_segment_urls, seg_url);
			ctx->nb_seg_url_pending++;
		}
		return;
	}

	if (!ds->stl && !ctx->cues && !ctx->forward_mode) {
		Double drift, seg_start = (Double) ds->seg_start_time;
		seg_start /= ds->mpd_timescale;
		drift = seg_start - ((Double)(ds->seg_number - ds->startNumber)) * ds->dash_dur.num / ds->dash_dur.den;

		if ((ds->dash_dur.num>0) && (ABS(drift) * 2 * ds->dash_dur.den > ds->dash_dur.num)) {
			u64 cts = 0;
			if (pck) {
				cts = dasher_translate_cts(ds, gf_filter_pck_get_cts(pck) );
			}
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] First CTS "LLU" in segment %d drifting by %g (more than half a segment duration) from segment time, consider reencoding or using segment timeline\n", cts, ds->seg_number,  drift));
		}
	}

	if (!ctx->forward_mode) {
		/*get final segment template - output file name is NULL, we already have solved this in source_setup
		segment time must be PTO-adjusted !*/
		u64 pto = ds->presentation_time_offset;
		if (base_ds->timescale != base_ds->mpd_timescale) {
			pto = gf_timestamp_rescale(pto, base_ds->timescale, base_ds->mpd_timescale);
		}

		gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, ds->set->bitstream_switching, szSegmentName, base_ds->rep_id, NULL, base_ds->seg_template, NULL, base_ds->seg_start_time + pto, base_ds->rep->bandwidth, base_ds->seg_number, ds->stl);
	}


send_packet:
	strcpy(szSegmentFullPath, szSegmentName);

	if (ctx->out_path) {
		char *rel = NULL;
		if (ctx->do_m3u8 && ds->hls_vp_name && !ctx->forward_mode) {
			char *tmp = gf_url_concatenate(ctx->out_path, ds->hls_vp_name);
			if (tmp) {
				rel = gf_url_concatenate(tmp, szSegmentName);
				gf_free(tmp);
			}
		}
		if (!rel)
			rel = gf_url_concatenate(ctx->out_path, szSegmentName);

		if (rel) {
			strcpy(szSegmentFullPath, rel);
			gf_free(rel);
		}
	}

	if (seg_state) {
		seg_state->filepath = gf_strdup(szSegmentFullPath);
		seg_state->filename = gf_strdup(szSegmentName);
	}

	if (ds->rep->segment_list && (ctx->forward_mode!=DASHER_FWD_ALL) && !ctx->gencues) {
		GF_MPD_SegmentURL *seg_url;
		GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
		if (seg_url) {
			gf_list_add(ds->rep->segment_list->segment_URLs, seg_url);
			seg_url->media = gf_strdup(szSegmentName);
			gf_list_add(ds->pending_segment_urls, seg_url);
			if (szIndexName[0])