GF_Err gf_dasher_process()

in src/media_tools/dash_segmenter.c [5247:6163]


GF_Err gf_dasher_process(GF_DASHSegmenter *dasher, Double sub_duration)
{
	u32 i, j, max_period, cur_period;
	Bool has_role = GF_FALSE;
	char *sep, szSolvedSegName[GF_MAX_PATH], szTempMPD[GF_MAX_PATH], szOpt[GF_MAX_PATH];
	const char *opt;
	GF_Err e;
	Bool uses_xlink = GF_FALSE;
	Bool has_mpeg2 = GF_FALSE;
	Bool none_supported = GF_TRUE;
	u32 valid_inputs = 0;
	u32 cur_adaptation_set;
	u32 max_adaptation_set = 0;
	u32 cur_group_id = 0;
	u32 max_sap_type = 0;
	u32 nb_sap_type_greater_than_3 = 0;
	u32 nb_sap_type_greater_than_4 = 0;
	u32 max_comp_per_input;
	Bool use_cenc = GF_FALSE;
	Bool segment_alignment = GF_TRUE;
	GF_List *period_links = NULL;
	Double presentation_duration = 0;
	Double active_period_start = 0;
	u32 last_period_rep_idx_plus_one = 0;
	FILE *mpd;
	PeriodEntry *p;
	if (!dasher) return GF_BAD_PARAM;

	dasher->force_period_end = GF_FALSE;
	dasher->content_protection_in_adaptation_set = GF_FALSE;

	if (dasher->dash_mode && !dasher->mpd_update_time && !dasher->mpd_live_duration) {
		if (dasher->dash_mode == GF_DASH_DYNAMIC_LAST) {
			dasher->force_period_end = GF_TRUE;
			dasher->dash_mode = GF_DASH_DYNAMIC;
		} else {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Either MPD refresh (update) period or MPD duration shall be set in dynamic mode.\n"));
			return GF_BAD_PARAM;
		}
	}
	if (dasher->real_time && (dasher->nb_inputs>1)) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] real-time simulation is only supported with a single representation.\n"));
		return GF_BAD_PARAM;
	}


	/*update dash context*/
	if (dasher->dash_ctx) {
		Bool regenerate;
		e = gf_dasher_init_context(dasher->dash_ctx, &dasher->dash_mode, &dasher->time_shift_depth, NULL, dasher->ast_offset_ms);
		if (e) return e;
		opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "MaxSegmentDuration");
		if (opt) {
			Double seg_dur = atof(opt);
			dasher->segment_duration = seg_dur;
		} else {
			sprintf(szOpt, "%f", dasher->segment_duration);
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "MaxSegmentDuration", szOpt);
		}
		sprintf(szOpt, "%u", dasher->dash_scale);
		gf_cfg_set_key(dasher->dash_ctx, "DASH", "TimeScale", szOpt);

		/*peform all file cleanup*/
		regenerate = gf_dasher_cleanup(dasher);
		if (!regenerate) return GF_OK;

		opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "HasXLINK");
		if (opt && !strcmp(opt, "yes")) uses_xlink = GF_TRUE;
	}

	max_period = 0;
	for (i=0; i<dasher->nb_inputs; i++) {
		GF_DashSegInput *dash_input = &dasher->inputs[i];
		dash_input->period = 0;

		dash_input->moof_seqnum_increase = 2 + (u32) (dasher->segments_start_with_rap / dasher->fragments_in_memory );

		/*layered coded representation*/
		if (dash_input->idx_representations) {
			continue;
		}

		if (dash_input->xlink) uses_xlink = GF_TRUE;

		if (dash_input->role && strcmp(dash_input->role, "main"))
			has_role = GF_TRUE;

		if (dash_input->period_id_not_specified) {
			max_period = 1;
		}

		if (!strcmp(dash_input->szMime, "video/mp2t")) has_mpeg2 = GF_TRUE;
		valid_inputs++;
	}
	if (!valid_inputs) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error: no suitable file found for dashing.\n"));
		return GF_BAD_PARAM;
	}

	//assign periods
	for (i=0; i<dasher->nb_inputs; i++) {
		GF_DashSegInput *dash_input = &dasher->inputs[i];

		/*set all default roles to main if needed*/
		if (has_role && !dash_input->role)
			dash_input->role = "main";

		//reset adaptation classifier
		dash_input->adaptation_set = 0;


		if (dash_input->period)
			continue;

		if (dash_input->period_id_not_specified) {
			dash_input->period = 1;
		}
		else if (dash_input->periodID) {
			max_period++;
			dash_input->period = max_period;

			for (j=i+1; j<dasher->nb_inputs; j++) {
				if (!strcmp(dasher->inputs[j].periodID, dasher->inputs[i].periodID))
					dasher->inputs[j].period = dasher->inputs[i].period;
			}
		}
	}

	if (dasher->dash_mode && (max_period > 1) ) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Multiple periods in live mode not supported\n"));
		return GF_NOT_SUPPORTED;
	}

	for (cur_period=0; cur_period<max_period; cur_period++) {
		Double seg_duration_in_period = 0;
		/*classify all input in possible adaptation sets*/
		for (i=0; i<dasher->nb_inputs; i++) {
			GF_DashSegInput *dash_input = &dasher->inputs[i];
			/*this file does not belong to our current period*/
			if (dash_input->period != cur_period+1) continue;

			if (!seg_duration_in_period) {
				seg_duration_in_period = dash_input->segment_duration ? dash_input->segment_duration : dasher->segment_duration;
			} else if (dash_input->segment_duration && (dash_input->segment_duration != seg_duration_in_period)) {
				segment_alignment = GF_FALSE;
			}

			/*this file already belongs to an adaptation set*/
			if (dash_input->adaptation_set) continue;

			/*we cannot fragment the input file*/
			if (!dash_input->dasher_input_classify || !dash_input->dasher_segment_file) {
				//this is a remote period insertion
				if (dash_input->xlink) {
					//assign a AS for this fake source (otherwise first active period detection will fail)
					dash_input->adaptation_set = 1;
					none_supported = GF_FALSE;
				}
				continue;
			}

			max_adaptation_set++;
			dash_input->adaptation_set = max_adaptation_set;
			dash_input->nb_rep_in_adaptation_set = 1;

			e = dash_input->dasher_input_classify(dasher->inputs, dasher->nb_inputs, i, &cur_group_id, &max_sap_type);
			if (e) return e;
			none_supported = GF_FALSE;
			if (max_sap_type>=3) nb_sap_type_greater_than_3++;
			if (max_sap_type>3) nb_sap_type_greater_than_4++;
		}
		if (none_supported) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH]: None of the input types are supported for DASHing - nothing to do ...\n"));
			return GF_OK;
		}
	}

	/*check requested profiles can be generated, or adjust them*/
	if (nb_sap_type_greater_than_4 || (nb_sap_type_greater_than_3 > 1)) {
		if (dasher->profile) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH]: WARNING! Max SAP type %d detected\n\tswitching to FULL profile\n", max_sap_type));
		}
		dasher->profile = GF_DASH_PROFILE_FULL;
	}


	/*adjust params based on profiles*/
	switch (dasher->profile) {
	case GF_DASH_PROFILE_LIVE:
		dasher->segments_start_with_rap = GF_TRUE;
		dasher->use_url_template = 1;
		dasher->single_segment = dasher->single_file = GF_FALSE;
		break;
	case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE: {
		dasher->bitstream_switching_mode = GF_DASH_BSMODE_MULTIPLE_ENTRIES;
		for (i=0; i<dasher->nb_inputs; i++) {
			if (dasher->inputs[i].role && !strcmp(dasher->inputs[i].role, "main"))
				break;
		}
		if (i == dasher->nb_inputs) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] HbbTV 1.5 ISO live profile requires to have at least one Adaptation Set\nlabelled with a Role@value of \"main\". Consider adding \":role=main\" to your inputs.\n"));
			e = GF_BAD_PARAM;
			goto exit;
		}
	}
	case GF_DASH_PROFILE_AVC264_LIVE:
		dasher->segments_start_with_rap = GF_TRUE;
		dasher->no_fragments_defaults = GF_TRUE;
		dasher->use_url_template = 1;
		dasher->single_segment = dasher->single_file = GF_FALSE;
		dasher->content_protection_in_adaptation_set = GF_TRUE;
		break;
	case GF_DASH_PROFILE_AVC264_ONDEMAND:
		dasher->segments_start_with_rap = GF_TRUE;
		dasher->no_fragments_defaults = GF_TRUE;
		dasher->content_protection_in_adaptation_set = GF_TRUE;
		if (dasher->seg_rad_name) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Segment-name not allowed in DASH-AVC/264 onDemand profile.\n"));
			return GF_BAD_PARAM;
		}
	case GF_DASH_PROFILE_ONDEMAND:
		dasher->segments_start_with_rap = GF_TRUE;
		dasher->single_segment = GF_TRUE;
		if ((dasher->bitstream_switching_mode != GF_DASH_BSMODE_DEFAULT) && (dasher->bitstream_switching_mode != GF_DASH_BSMODE_NONE)) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] onDemand profile, bitstream switching mode cannot be used.\n"));
			return GF_BAD_PARAM;
		}
		/*BS switching is meaningless in onDemand profile*/
		dasher->bitstream_switching_mode = GF_DASH_BSMODE_NONE;
		dasher->use_url_template = dasher->single_file = GF_FALSE;
		break;
	case GF_DASH_PROFILE_MAIN:
		dasher->segments_start_with_rap = GF_TRUE;
		dasher->single_segment = GF_FALSE;
		break;
	default:
		break;
	}
	if (dasher->bitstream_switching_mode == GF_DASH_BSMODE_DEFAULT) {
		dasher->bitstream_switching_mode = GF_DASH_BSMODE_INBAND;
	}

	//we allow using inband param set when not time aligned. set the option now before overriding flags for non time aligned
	dasher->inband_param_set = ((dasher->bitstream_switching_mode == GF_DASH_BSMODE_INBAND) || (dasher->bitstream_switching_mode == GF_DASH_BSMODE_SINGLE) ) ? GF_TRUE : GF_FALSE;

	if (!segment_alignment) {
		if (dasher->profile) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH]: Segments are not time-aligned in each representation of each period\n\tswitching to FULL profile\n"));
			dasher->profile = GF_DASH_PROFILE_FULL;
		}
		if (dasher->bitstream_switching_mode != GF_DASH_BSMODE_NONE) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH]: Segments are not time-aligned in each representation of each period\n\tdisabling bitstream switching\n"));
			dasher->bitstream_switching_mode = GF_DASH_BSMODE_NONE;
		}
	}

	//check we have a segment template
	if (dasher->use_url_template && !dasher->seg_rad_name) {
		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH]: WARNING! DASH Live profile requested but no -segment-name\n\tusing \"%%s_dash\" by default\n\n"));
		dasher_format_seg_name(dasher, "%s_dash");
	}

	if (dasher->single_segment) {
		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("DASH-ing file%s - single segment\nSubsegment duration %.3f - Fragment duration: %.3f secs\n", (dasher->nb_inputs>1) ? "s" : "", dasher->segment_duration, dasher->fragment_duration));
		dasher->subsegs_per_sidx = 0;
	} else {
		if (!dasher->seg_ext) dasher->seg_ext = "m4s";

		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("DASH-ing file%s: %.2fs segments %.2fs fragments ", (dasher->nb_inputs>1) ? "s" : "", dasher->segment_duration, dasher->fragment_duration));
		if (!dasher->enable_sidx) {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("no sidx used"));
		} else if (dasher->subsegs_per_sidx) {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("%d subsegments per sidx", dasher->subsegs_per_sidx));
		} else {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("single sidx per segment"));
		}
		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("\n"));
	}

	if (dasher->fragments_start_with_rap) dasher->segments_start_with_rap = GF_TRUE;

	if (dasher->segments_start_with_rap) {
		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("Spliting segments %sat GOP boundaries\n", dasher->fragments_start_with_rap ? "and fragments " : ""));
	}

	dasher->single_file_mode = (dasher->single_segment) ? 1 : (dasher->single_file ? 2 : 0);

	dasher->subduration = sub_duration * 1000 / dasher->dash_scale;

	max_comp_per_input = 0;
	for (cur_period=0; cur_period<max_period; cur_period++) {
		u32 first_in_period = 0;
		Double period_duration=0;

		for (i=0; i<dasher->nb_inputs; i++) {
			Double dur = 0;
			GF_DashSegInput *dash_input = &dasher->inputs[i];
			/*this file does not belongs to any adaptation set*/
			if (dash_input->period != cur_period+1) continue;

			/*this file does not belongs to any adaptation set*/
			if (!dash_input->adaptation_set) continue;

			if (!first_in_period) {
				first_in_period = i+1;
				last_period_rep_idx_plus_one = first_in_period;
			}
			
			//only get component info once
			if (!dash_input->get_component_info_done) {
				dash_input->get_component_info_done = GF_TRUE;
				if (dash_input->dasher_get_components_info) {
					e = dash_input->dasher_get_components_info(dash_input, dasher);
					if (e) return e;
				}
			}
			dur = dash_input->duration;
			if (sub_duration && (sub_duration < dur * dasher->dash_scale) ) {
				dur = dasher->subduration;
			} 

			if (dur > period_duration)
				period_duration = dur;

			switch (dash_input->protection_scheme_type) {
			case GF_ISOM_CENC_SCHEME:
			case GF_ISOM_CBC_SCHEME:
				use_cenc = GF_TRUE;
				break;
			}
			if (max_comp_per_input < dash_input->nb_components)
				max_comp_per_input = dash_input->nb_components;
		}
		if (first_in_period) {
			dasher->inputs[first_in_period-1].period_duration = period_duration;
		}
		presentation_duration += period_duration;
	}

	if (max_comp_per_input > 1) {
		if (dasher->profile == GF_DASH_PROFILE_AVC264_LIVE) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Muxed representations not allowed in DASH-IF AVC264 live profile\n\tswitching to regular live profile\n"));
			dasher->profile = GF_DASH_PROFILE_LIVE;
		} else if (dasher->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Muxed representations not allowed in HbbTV 1.5 ISOBMF live profile\n\tswitching to regular live profile\n"));
			dasher->profile = GF_DASH_PROFILE_LIVE;
		} else if (dasher->profile == GF_DASH_PROFILE_AVC264_ONDEMAND) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Muxed representations not allowed in DASH-IF AVC264 onDemand profile\n\tswitching to regular onDemand profile\n"));
			dasher->profile = GF_DASH_PROFILE_ONDEMAND;
		}
	}

	/*HbbTV 1.5 ISO live specific checks*/
	if (dasher->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
		if (max_adaptation_set > 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"));
			dasher->profile = GF_DASH_PROFILE_AVC264_LIVE;
		}
		if (max_period > 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"));
			dasher->profile = GF_DASH_PROFILE_AVC264_LIVE;
		}
		if (dasher->segment_duration < 1.0) {
			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"));
			dasher->segment_duration = 1.0;
		}
		if (dasher->segment_duration > 15.0) {
			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"));
			dasher->segment_duration = 15.0;
		}
	}


	active_period_start = 0;
	if (dasher->dash_ctx) {
		Double duration;
		char *id = "";
		if (last_period_rep_idx_plus_one) id = dasher->inputs[last_period_rep_idx_plus_one-1].periodID;
		if (!id) id = "";

		//restore max segment duration
		opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "MaxSegmentDuration");
		if (opt) dasher->max_segment_duration = atof(opt);

		opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "LastActivePeriod");
		//period has changed
		if (opt && strcmp(opt, id)) {
			Bool prev_period_not_done = GF_FALSE;
			const char *prev_id = opt;
			Double last_period_dur = 0;
			opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "CumulatedPastPeriodsDuration");
			duration = opt ? atof(opt) : 0.0;

			opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "LastPeriodDuration");
			if (opt) last_period_dur = atof(opt);
			
			if (!last_period_dur) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] last period has no known duration, cannot insert new period. Try re-dashing previous period and specifying its duration\n"));
				return GF_BAD_PARAM;
			}

			//we may have called the segmenter with the end of the previous active period and the start of the new one - locate the
			//previous period and update cumulated duration
			for (i=0; i<dasher->nb_inputs; i++) {
				char *p_id = dasher->inputs[i].periodID;
				if (!p_id) p_id = "";
				if (!strcmp(p_id, prev_id)) {
					last_period_dur += dasher->inputs[last_period_rep_idx_plus_one-1].period_duration;
					presentation_duration -= dasher->inputs[last_period_rep_idx_plus_one-1].period_duration;;
					prev_period_not_done = GF_TRUE;
					break;
				}
			}
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastPeriodDuration", NULL);

			duration += last_period_dur;
			presentation_duration += duration;

			sprintf(szOpt, "%g", duration);
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "CumulatedPastPeriodsDuration", szOpt);

			//get active period start
			opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "LastActivePeriodStart");
			if (opt) active_period_start = atof(opt);
			active_period_start += last_period_dur;

			//and add period to past periods, storing their duration and xlink
			if (! prev_period_not_done) {
				opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "LastPeriodXLINK");
				sprintf(szOpt, "%g,%s", last_period_dur, opt ? opt : "INLINE");
				gf_cfg_set_key(dasher->dash_ctx, "PastPeriods", prev_id, szOpt);
				if (opt) {
					gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastPeriodXLINK", NULL);
				}

				//update active period start
				sprintf(szOpt, "%g", active_period_start);
				gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastActivePeriodStart", szOpt);

				purge_dash_context(dasher->dash_ctx);
			}

			//and finally switch active period
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastActivePeriod", id);

			sprintf(szOpt, "%g", dasher->inputs[last_period_rep_idx_plus_one-1].period_duration);
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastPeriodDuration", szOpt);
		} else {
			opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "LastPeriodDuration");
			if (opt) {
				duration = atof(opt);
				dasher->inputs[last_period_rep_idx_plus_one-1].period_duration += duration;
				presentation_duration += duration;
			}
			sprintf(szOpt, "%g", dasher->inputs[last_period_rep_idx_plus_one-1].period_duration);
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastPeriodDuration", szOpt);
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastActivePeriod", id);

			opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "CumulatedPastPeriodsDuration");
			presentation_duration += opt ? atof(opt) : 0.0;
		}
	}

	strcpy(szTempMPD, dasher->mpd_name);
	if (dasher->dash_mode) strcat(szTempMPD, ".tmp");

	mpd = gf_fopen(szTempMPD, "wt");
	if (!mpd) {
		GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[MPD] Cannot open MPD file %s for writing\n", szTempMPD));
		return GF_IO_ERR;
	}


	period_links = gf_list_new();

	if (dasher->dash_ctx) {
		u32 count = gf_cfg_get_key_count(dasher->dash_ctx, "PastPeriods");
		for (i=0; i<count; i++) {
			const char *id = gf_cfg_get_key_name(dasher->dash_ctx, "PastPeriods", i);
			const char *xlink = gf_cfg_get_key(dasher->dash_ctx, "PastPeriods", id);
			char *sep = (char *)strchr(xlink, ',');
			if (!sep) continue;

			GF_SAFEALLOC(p, PeriodEntry);
			gf_list_add(period_links, p);
			p->id = id;

			sep[0] = 0;
			p->period_duration = (Double) atof(xlink);
			sep[0]=',';
			xlink = &sep[1];
			if (!strcmp(xlink, "INLINE")) {
				strcpy(p->szPeriodXML, dasher->mpd_name);
				sep = strrchr(p->szPeriodXML, '.');
				if (sep) sep[0] = 0;
				strcat(p->szPeriodXML, "-Period_");
				strcat(p->szPeriodXML, id);
				strcat(p->szPeriodXML, ".mpd");
			} else {
				char *url = gf_url_concatenate(dasher->mpd_name, xlink);
				strcpy(p->szPeriodXML, url ? url : xlink);
				if (url) gf_free(url);
				p->xlink = xlink;
				p->is_xlink = GF_TRUE;
			}
		}
	}

	for (cur_period=0; cur_period<max_period; cur_period++) {
		Bool flush_period = GF_FALSE;
		FILE *period_mpd;
		Double period_duration = 0;
		const char *id=NULL;
		const char *xlink = NULL;

		/* Find period information for this period */
		for (i=0; i< dasher->nb_inputs; i++) {
			if (dasher->inputs[i].adaptation_set && (dasher->inputs[i].period==cur_period+1)) {
				period_duration = dasher->inputs[i].period_duration;
				id = dasher->inputs[i].periodID;
				if (dasher->inputs[i].xlink)
					xlink = dasher->inputs[i].xlink;
				break;
			}
		}

		GF_SAFEALLOC(p, PeriodEntry);
		gf_list_add(period_links, p);
		p->period_duration = period_duration;
		p->period_idx = cur_period+1;
		p->id = id;

		if (xlink) {
			char *url = gf_url_concatenate(dasher->mpd_name, xlink);
			strcpy(p->szPeriodXML, url ? url : xlink);
			if (url) gf_free(url);
				p->xlink = xlink;
			p->is_xlink = GF_TRUE;
		} else {
			strcpy(p->szPeriodXML, dasher->mpd_name);
			sep = strrchr(p->szPeriodXML, '.');
			if (sep) sep[0] = 0;
			strcat(p->szPeriodXML, "-Period_");
			strcat(p->szPeriodXML, id ? id : "");
			strcat(p->szPeriodXML, ".mpd");
		}

		period_mpd = NULL;

		//only open file if we are to dash something (max_adaptation_set=0: no source file, only period xlink insertion)
		if (max_adaptation_set) {
			period_mpd = gf_fopen(p->szPeriodXML, "wb");

			if (!period_mpd) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot open period MPD %s for writing, aborting\n", p->szPeriodXML));
				e = GF_IO_ERR;
				goto exit;
			}

			dasher->mpd = period_mpd;

			e = write_period_header(dasher, period_mpd, id, 0.0, period_duration, NULL, cur_period+1, (xlink!=NULL) ? GF_TRUE : GF_FALSE);
			if (e) goto exit;
		}
		//keep track of the last period xlink
		if (dasher->dash_ctx) 
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastPeriodXLINK", xlink);

		/*for each identified adaptationSets, write MPD and perform segmentation of input files*/
		for (cur_adaptation_set=0; cur_adaptation_set < max_adaptation_set; cur_adaptation_set++) {
			char szInit[GF_MAX_PATH], tmp[GF_MAX_PATH];
			u32 first_rep_in_set=0;
			u32 max_width = 0;
			u32 max_height = 0;
			u32 fps_num = 0;
			u32 fps_denum = 0;
			Double seg_duration_in_as = 0;
			Bool has_scalability = GF_FALSE;
			Bool use_bs_switching = (dasher->bitstream_switching_mode==GF_DASH_BSMODE_NONE) ? GF_FALSE : GF_TRUE;
			char *lang;
			char szFPS[100];
			Bool is_first_rep = GF_FALSE;
			Bool skip_init_segment_creation = GF_FALSE;

			dasher->segment_alignment_disabled = GF_FALSE;

			while (dasher->inputs[first_rep_in_set].adaptation_set!=cur_adaptation_set+1)
				first_rep_in_set++;

			/*not in our period*/
			if (dasher->inputs[first_rep_in_set].period != cur_period+1)
				continue;

			if (!dasher->inputs[first_rep_in_set].init_segment_generated) {

				strcpy(tmp, dasher->mpd_name);
				sep = strrchr(tmp, '.');
				if (sep) sep[0] = 0;

				if (max_adaptation_set==1) {
					strcpy(szInit, tmp);
					strcat(szInit, "_init.mp4");
				} else {
					sprintf(szInit, "%s_set%d_init.mp4", tmp, cur_adaptation_set+1);
				}
				/*unless asked to do BS switching on single rep, don't do it ...*/
				if ((dasher->bitstream_switching_mode < GF_DASH_BSMODE_SINGLE) && dasher->inputs[first_rep_in_set].nb_rep_in_adaptation_set==1)
					use_bs_switching = GF_FALSE;

				if (! use_bs_switching) {
					skip_init_segment_creation = GF_TRUE;
				}
				else if (! dasher->inputs[first_rep_in_set].dasher_create_init_segment) {
					skip_init_segment_creation = GF_TRUE;
					strcpy(szInit, "");
				}
				/*if we already wrote the InitializationSegment, get rid of it (no overwrite) and let the dashing routine open it*/
				else if (dasher->dash_ctx) {
					char RepSecName[GF_MAX_PATH];
					const char *init_seg_name;
					sprintf(RepSecName, "Representation_%s", dasher->inputs[first_rep_in_set].representationID);
					if ((init_seg_name = gf_cfg_get_key(dasher->dash_ctx, RepSecName, "InitializationSegment")) != NULL) {
						skip_init_segment_creation = GF_TRUE;
					}
				}

				if (!skip_init_segment_creation) {
					Bool disable_bs_switching = GF_FALSE;
					e = dasher->inputs[first_rep_in_set].dasher_create_init_segment(dasher->inputs, dasher->nb_inputs, cur_adaptation_set+1, szInit, dasher->tmpdir, dasher, dasher->bitstream_switching_mode, &disable_bs_switching);
					if (e) goto exit;
					if (disable_bs_switching)
						use_bs_switching = GF_FALSE;
				}

				dasher->inputs[first_rep_in_set].init_seg_url = use_bs_switching ? gf_strdup(szInit) : NULL;
				dasher->inputs[first_rep_in_set].use_bs_switching = use_bs_switching;
				dasher->inputs[first_rep_in_set].init_segment_generated = GF_TRUE;
			} else {
				use_bs_switching = dasher->inputs[first_rep_in_set].use_bs_switching;
			}

			dasher->bs_switch_segment_file = use_bs_switching ? dasher->inputs[first_rep_in_set].init_seg_url : NULL;


			dasher->segment_alignment_disabled = GF_FALSE;
			szFPS[0] = 0;
			lang = NULL;
			/*compute some max defaults*/
			for (i=0; i<dasher->nb_inputs && !e; i++) {
				GF_DashSegInput *dash_input = &dasher->inputs[i];

				if (dash_input->adaptation_set!=cur_adaptation_set+1)
					continue;

				if (dash_input->idx_representations)
					has_scalability = GF_TRUE;

				for (j=0; j<dash_input->nb_components; j++) {
					if (dash_input->components[j].width > max_width)
						max_width = dash_input->components[j].width;
					if (dash_input->components[j].height > max_height)
						max_height = dash_input->components[j].height;

					if (! fps_num || !fps_denum) {
						fps_num = dash_input->components[j].fps_num;
						fps_denum = dash_input->components[j].fps_denum;
					}
					else if (fps_num * dash_input->components[j].fps_denum < dash_input->components[j].fps_num * fps_denum) {
						fps_num = dash_input->components[j].fps_num;
						fps_denum = dash_input->components[j].fps_denum;
					}

					if (dash_input->components[j].lang) {
						if (lang && strcmp(lang, dash_input->components[j].lang) ) {
							if (!strcmp(lang, "und") || !strcmp(dash_input->components[j].lang, "lang")) {
								GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] two languages in adaptation set: %s will be kept %s will be ignored\n", lang, dash_input->components[j].lang));
							}
						} else if (!lang) {
							lang = gf_strdup(dash_input->components[j].lang);
						}
					}

					if (dash_input->segment_duration) {
						if (!seg_duration_in_as) 
							seg_duration_in_as = dash_input->segment_duration;
						else if (dash_input->segment_duration != seg_duration_in_as) 
							dasher->segment_alignment_disabled = GF_TRUE;
					}
				}
			}

			if (max_width && max_height) {
				gf_media_get_reduced_frame_rate(&fps_num, &fps_denum);
				if (fps_denum>1)
					sprintf(szFPS, "%d/%d", fps_num, fps_denum);
				else if (fps_num)
					sprintf(szFPS, "%d", fps_num);
			}

			e = write_adaptation_header(period_mpd, dasher->profile, dasher->use_url_template, dasher->single_file_mode, dasher->inputs, dasher->nb_inputs, cur_period+1, cur_adaptation_set+1, first_rep_in_set, 
				use_bs_switching, max_width, max_height, szFPS, lang, szInit, dasher->segment_alignment_disabled, dasher->mpd_name);

			if (e) goto exit;

			is_first_rep = GF_TRUE;
			for (i=0; i<dasher->nb_inputs && !e; i++) {
				char szOutName[GF_MAX_PATH], *segment_name, *orig_seg_name;
				GF_DashSegInput *dash_input = &dasher->inputs[i];
				Double segdur, fragdur;
				if (dash_input->adaptation_set!=cur_adaptation_set+1)
					continue;

				orig_seg_name = segment_name = dasher->seg_rad_name;

				strcpy(szOutName, gf_url_get_resource_name(dash_input->file_name));
				sep = strrchr(szOutName, '.');
				if (sep) sep[0] = 0;

				/*in scalable case: seg_name is variable*/
				dasher->variable_seg_rad_name = has_scalability ? GF_TRUE : GF_FALSE;
				if (segment_name) {
					if (strstr(segment_name, "%s")) {
						sprintf(szSolvedSegName, segment_name, szOutName);
						dasher->variable_seg_rad_name = GF_TRUE;
						if (dash_input->single_track_num) {
							char szTkNum[50];
							sprintf(szTkNum, "_track%d_", dash_input->single_track_num);
							strcat(szSolvedSegName, szTkNum);
						}
					} else {
						strcpy(szSolvedSegName, segment_name);
					}

					segment_name = szSolvedSegName;
				}

				/* user added a relative-path baseURL */
				if (dash_input->nb_baseURL) {
					if (gf_url_is_local(dash_input->baseURL[0])) {
						const char *name;
						char tmp_segment_name[GF_MAX_PATH];
						if (dash_input->nb_baseURL > 1)
							GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Several relative path baseURL for input %s: selecting \"%s\"\n", dash_input->file_name, dash_input->baseURL[0]));
						name = gf_url_get_resource_name(szOutName);
						strcpy(tmp_segment_name, name);
						gf_url_get_resource_path(dash_input->baseURL[0], szOutName);
						if (szOutName[strlen(szOutName)] != GF_PATH_SEPARATOR) {
							char ext[2]; ext[0] = GF_PATH_SEPARATOR; ext[1] = 0;
							strcat(szOutName, ext);
						}
						strcat(szOutName, tmp_segment_name);
					} else {
						GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Found baseURL for input %s but not a relative path (%s)\n", dash_input->file_name, dash_input->baseURL[0]));
					}
				}

				if ((dash_input->trackNum || dash_input->single_track_num) && (!dasher->seg_rad_name || !strstr(dasher->seg_rad_name, "$RepresentationID$") ) ) {
					char tmp[10];
					sprintf(tmp, "_track%d", dash_input->trackNum ? dash_input->trackNum : dash_input->single_track_num);
					strcat(szOutName, tmp);
				}
				strcat(szOutName, "_dash");

				if (gf_url_get_resource_path(dasher->mpd_name, tmp)) {
					strcat(tmp, szOutName);
					strcpy(szOutName, tmp);
				}
				if (segment_name && dash_input->trackNum && !strstr(dasher->seg_rad_name, "$RepresentationID$") ) {
					char tmp[10];
					sprintf(tmp, "_track%d_", dash_input->trackNum);
					strcat(segment_name, tmp);
				}
				dasher->seg_rad_name = segment_name;

				/*in scalable case, we need also the bandwidth of dependent representation*/
				if (dash_input->dependencyID) {
					dash_input->dependency_bandwidth = gf_dash_get_dependency_bandwidth(dasher->inputs, dasher->nb_inputs, dash_input->file_name, dash_input->lower_layer_track);
				}

				segdur = dasher->segment_duration;
				fragdur = dasher->fragment_duration;

				if (dash_input->segment_duration) 
					dasher->segment_duration = dash_input->segment_duration;
				
				if (dash_input->segment_duration && (dasher->fragment_duration * dasher->dash_scale > dash_input->segment_duration)) {
					dasher->fragment_duration = dasher->segment_duration;
				} 


				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("DASHing file %s\n", dash_input->file_name));
				e = dash_input->dasher_segment_file(dash_input, szOutName, dasher, is_first_rep);

				dasher->seg_rad_name = orig_seg_name;
				dasher->segment_duration = segdur;
				dasher->fragment_duration = fragdur;

				if (e) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("Error while DASH-ing file: %s\n", gf_error_to_string(e)));
					goto exit;
				}
				is_first_rep = GF_FALSE;
			}
			/*close adaptation set*/
			fprintf(period_mpd, "  </AdaptationSet>\n");
		}
		
		if (period_mpd) {
			fprintf(period_mpd, " </Period>\n");
			gf_fclose(period_mpd);
		}

		dasher->mpd = period_mpd;

		//and add periods done to past periods, storing their start time
		if (!id) id="";
		if (last_period_rep_idx_plus_one && dasher->inputs[last_period_rep_idx_plus_one-1].periodID && strcmp(id, dasher->inputs[last_period_rep_idx_plus_one-1].periodID) ) {
			flush_period = GF_TRUE;
		}
		else if (dasher->force_period_end) {
			flush_period = GF_TRUE;
		}
		if (flush_period) {
			sprintf(szOpt, "%g", active_period_start);
			gf_cfg_set_key(dasher->dash_ctx, "PastPeriods", id, szOpt);
			active_period_start += period_duration;

			sprintf(szOpt, "%g", active_period_start);
			gf_cfg_set_key(dasher->dash_ctx, "DASH", "LastActivePeriodStart", szOpt);

			if (dasher->dash_ctx) purge_dash_context(dasher->dash_ctx);
		}
	}

	if (dasher->dash_ctx) {
		sprintf(szOpt, "%g", dasher->max_segment_duration);
		gf_cfg_set_key(dasher->dash_ctx, "DASH", "MaxSegmentDuration", szOpt);
	}



	/*ready to write the MPD*/
	dasher->mpd = mpd;

	if (dasher->dash_mode && !dasher->mpd_update_time) {
		presentation_duration = dasher->mpd_live_duration;
	}

	if (dasher->force_period_end && !dasher->mpd_update_time && !presentation_duration) {
		presentation_duration = 0;
		i=0;
		while ((p = (PeriodEntry *) gf_list_enum(period_links, &i))) {
			presentation_duration = p->period_duration;
		}
	}
	
	e = write_mpd_header(dasher, mpd, has_mpeg2, presentation_duration, use_cenc, uses_xlink);
	if (e) goto exit;


	i=0;
	while ((p = (PeriodEntry *) gf_list_enum(period_links, &i))) {
		if (!p->period_idx) {
			if (p->is_xlink) {
				write_period_header(dasher, mpd, p->id, 0.0, p->period_duration, p->xlink, 0, GF_FALSE);
			} else {
				dash_insert_period_xml(mpd, p->szPeriodXML);
			}
		} else {
			if (p->is_xlink) {
				write_period_header(dasher, mpd, p->id, 0.0, p->period_duration, p->xlink, p->period_idx, GF_FALSE);
			} else {
				dash_insert_period_xml(mpd, p->szPeriodXML);
			}
			
			if (!dasher->dash_ctx && !p->is_xlink) {
				gf_delete_file(p->szPeriodXML);
			}
			
		}
	}

	fprintf(mpd, "</MPD>");


	if (dasher->dash_ctx && dasher->dash_mode) {
		const char *opt = gf_cfg_get_key(dasher->dash_ctx, "DASH", "LastPeriodDuration");
		if (opt) {
			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Current Period Duration: %s\n", opt) );
		}
	}

	if (dasher->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
		if (gf_ftell(mpd) > 100*1024)
				GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[DASH] Manifest MPD is too big for HbbTV 1.5. Limit is 100kB, current size is "LLU"kB\n", gf_ftell(mpd)/1024));
	}

exit:
	if (mpd) {
		gf_fclose(mpd);
		if (!e && dasher->dash_mode) {
			gf_delete_file(dasher->mpd_name);
			e = gf_move_file(szTempMPD, dasher->mpd_name);
			if (e) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[DASH] Error moving file %s to %s: %s\n", szTempMPD, dasher->mpd_name, gf_error_to_string(e) ));
			}
		}
	}

	if (period_links) {
		while (gf_list_count(period_links)) {
			PeriodEntry *p = (PeriodEntry *) gf_list_pop_back(period_links);
			if (p) gf_free(p);
		}
	}
	return e;
}