1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre , Cyril Concolato
5  *			Copyright (c) Telecom ParisTech 2000-2018
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / Media Tools sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/media_tools.h>
27 #include <gpac/network.h>
28 #include <gpac/mpd.h>
29 #include <gpac/filters.h>
30 
31 struct __gf_dash_segmenter
32 {
33 	GF_FilterSession *fsess;
34 	GF_Filter *output;
35 
36 	GF_List *inputs;
37 
38 	char *title, *copyright, *moreInfoURL, *sourceInfo, *lang;
39 	char *locations, *base_urls;
40 	char *mpd_name;
41 	GF_DashProfile profile;
42 
43 	GF_DashDynamicMode dash_mode;
44 	u32 use_url_template;
45 	Bool use_segment_timeline;
46 	Bool single_segment;
47 	Bool single_file;
48 	GF_DashSwitchingMode bitstream_switching_mode;
49 	Bool segments_start_with_rap;
50 
51 	Double segment_duration;
52 	Double fragment_duration;
53 	Double sub_duration;
54 	//has to be freed
55 	char *seg_rad_name;
56 	const char *seg_ext;
57 	const char *seg_init_ext;
58 	u32 segment_marker_4cc;
59 	Bool enable_sidx;
60 	u32 subsegs_per_sidx;
61 	Bool daisy_chain_sidx, use_ssix;
62 
63 	Bool fragments_start_with_rap;
64 	char *tmpdir;
65 	Double mpd_update_time;
66 	s32 time_shift_depth;
67 	u32 min_buffer_time;
68 	s32 ast_offset_ms;
69 	u32 dash_scale;
70 	Bool fragments_in_memory;
71 	u32 initial_moof_sn;
72 	u64 initial_tfdt;
73 	Bool no_fragments_defaults;
74 
75 	GF_DASHPSSHMode pssh_mode;
76 	Bool samplegroups_in_traf;
77     Bool single_traf_per_moof, single_trun_per_traf;
78 	Bool tfdt_per_traf;
79 	Double mpd_live_duration;
80 	Bool insert_utc;
81 	Bool real_time;
82 	char *utc_start_date;
83 
84 	const char *dash_profile_extension;
85 
86 	GF_DASH_ContentLocationMode cp_location_mode;
87 
88 	Bool no_cache;
89 
90 	Bool disable_loop;
91 	GF_DASH_SplitMode split_mode;
92 
93 	Bool mvex_after_traks;
94 	u32 sdtp_in_traf;
95 
96 	//some HLS options
97 	Bool hls_clock;
98 
99 	const char *cues_file;
100 	Bool strict_cues;
101 
102 	//not yet exposed through API
103 	Bool disable_segment_alignment;
104 	Bool enable_mix_codecs;
105 	Bool enable_sar_mix;
106 	Bool check_duration;
107 
108 	const char *dash_state;
109 
110 	u64 next_gen_ntp_ms;
111 	u64 mpd_time_ms;
112 
113 	Bool dash_mode_changed;
114 	u32 print_stats_graph;
115 	s32 dash_filter_idx_plus_one;
116 	u32 last_prog;
117 };
118 
119 
120 GF_EXPORT
gf_dasher_next_update_time(GF_DASHSegmenter * dasher,u64 * ms_in_session)121 u32 gf_dasher_next_update_time(GF_DASHSegmenter *dasher, u64 *ms_in_session)
122 {
123 	s64 diff = 0;
124 	if (dasher->next_gen_ntp_ms) {
125 		diff = (s64) dasher->next_gen_ntp_ms;
126 		diff -= (s64) gf_net_get_ntp_ms();
127 	}
128 	if (ms_in_session) *ms_in_session = dasher->mpd_time_ms;
129 	return diff>0 ? (u32) diff : 1;
130 }
131 
132 
133 GF_EXPORT
gf_dasher_new(const char * mpdName,GF_DashProfile dash_profile,const char * tmp_dir,u32 dash_timescale,const char * dasher_context_file)134 GF_DASHSegmenter *gf_dasher_new(const char *mpdName, GF_DashProfile dash_profile, const char *tmp_dir, u32 dash_timescale, const char *dasher_context_file)
135 {
136 	GF_DASHSegmenter *dasher;
137 	GF_SAFEALLOC(dasher, GF_DASHSegmenter);
138 	if (!dasher) return NULL;
139 
140 	dasher->mpd_name = gf_strdup(mpdName);
141 
142 	dasher->dash_scale = dash_timescale ? dash_timescale : 1000;
143 	if (tmp_dir) dasher->tmpdir = gf_strdup(tmp_dir);
144 	dasher->profile = dash_profile;
145 	dasher->dash_state = dasher_context_file;
146 	dasher->inputs = gf_list_new();
147 	return dasher;
148 }
149 
150 GF_EXPORT
gf_dasher_set_start_date(GF_DASHSegmenter * dasher,const char * dash_utc_start_date)151 void gf_dasher_set_start_date(GF_DASHSegmenter *dasher, const char *dash_utc_start_date)
152 {
153 	if (!dasher) return;
154 	if (dasher->utc_start_date) gf_free(dasher->utc_start_date);
155 	dasher->utc_start_date = dash_utc_start_date ? gf_strdup(dash_utc_start_date) : NULL;
156 }
157 
158 GF_EXPORT
gf_dasher_clean_inputs(GF_DASHSegmenter * dasher)159 void gf_dasher_clean_inputs(GF_DASHSegmenter *dasher)
160 {
161 	gf_list_reset(dasher->inputs);
162 	if (dasher->fsess) {
163 		gf_fs_del(dasher->fsess);
164 		dasher->fsess = NULL;
165 	}
166 }
167 
168 GF_EXPORT
gf_dasher_del(GF_DASHSegmenter * dasher)169 void gf_dasher_del(GF_DASHSegmenter *dasher)
170 {
171 	if (dasher->seg_rad_name) gf_free(dasher->seg_rad_name);
172 	gf_dasher_clean_inputs(dasher);
173 	gf_free(dasher->tmpdir);
174 	gf_free(dasher->mpd_name);
175 	if (dasher->title) gf_free(dasher->title);
176 	if (dasher->moreInfoURL) gf_free(dasher->moreInfoURL);
177 	if (dasher->sourceInfo) gf_free(dasher->sourceInfo);
178 	if (dasher->copyright) gf_free(dasher->copyright);
179 	if (dasher->lang) gf_free(dasher->lang);
180 	if (dasher->locations) gf_free(dasher->locations);
181 	if (dasher->base_urls) gf_free(dasher->base_urls);
182 	if (dasher->utc_start_date) gf_free(dasher->utc_start_date);
183 	gf_list_del(dasher->inputs);
184 	gf_free(dasher);
185 }
186 
187 GF_EXPORT
gf_dasher_set_info(GF_DASHSegmenter * dasher,const char * title,const char * copyright,const char * moreInfoURL,const char * sourceInfo,const char * lang)188 GF_Err gf_dasher_set_info(GF_DASHSegmenter *dasher, const char *title, const char *copyright, const char *moreInfoURL, const char *sourceInfo, const char *lang)
189 {
190 	if (!dasher) return GF_BAD_PARAM;
191 
192 #define DOSET(_field) \
193 	if (dasher->_field) gf_free(dasher->_field);\
194 	dasher->_field = _field ? gf_strdup(_field) : NULL;\
195 
196 	DOSET(title)
197 	DOSET(copyright)
198 	DOSET(moreInfoURL)
199 	DOSET(sourceInfo);
200 	DOSET(lang);
201 	return GF_OK;
202 }
203 
204 GF_EXPORT
gf_dasher_set_location(GF_DASHSegmenter * dasher,const char * location)205 GF_Err gf_dasher_set_location(GF_DASHSegmenter *dasher, const char *location)
206 {
207 	if (!dasher) return GF_BAD_PARAM;
208 
209 	if (!location) return GF_OK;
210 	return gf_dynstrcat(&dasher->locations, location, ",");
211 }
212 
213 GF_EXPORT
gf_dasher_add_base_url(GF_DASHSegmenter * dasher,const char * base_url)214 GF_Err gf_dasher_add_base_url(GF_DASHSegmenter *dasher, const char *base_url)
215 {
216 	if (!dasher) return GF_BAD_PARAM;
217 
218 	if (!base_url) return GF_OK;
219 	return gf_dynstrcat(&dasher->base_urls, base_url, ",");
220 }
221 
dasher_format_seg_name(GF_DASHSegmenter * dasher,const char * inName)222 static void dasher_format_seg_name(GF_DASHSegmenter *dasher, const char *inName)
223 {
224 	if (dasher->seg_rad_name) gf_free(dasher->seg_rad_name);
225 	dasher->seg_rad_name = NULL;
226 	if (inName) dasher->seg_rad_name = gf_strdup(inName);
227 }
228 
229 GF_EXPORT
gf_dasher_enable_url_template(GF_DASHSegmenter * dasher,Bool enable,const char * default_template,const char * default_extension,const char * default_init_extension)230 GF_Err gf_dasher_enable_url_template(GF_DASHSegmenter *dasher, Bool enable, const char *default_template, const char *default_extension, const char *default_init_extension)
231 {
232 	if (!dasher) return GF_BAD_PARAM;
233 	dasher->use_url_template = enable;
234 	dasher->seg_ext = default_extension;
235 	dasher->seg_init_ext = default_init_extension;
236 	dasher_format_seg_name(dasher, default_template);
237 	return GF_OK;
238 }
239 
240 GF_EXPORT
gf_dasher_enable_segment_timeline(GF_DASHSegmenter * dasher,Bool enable)241 GF_Err gf_dasher_enable_segment_timeline(GF_DASHSegmenter *dasher, Bool enable)
242 {
243 	if (!dasher) return GF_BAD_PARAM;
244 	dasher->use_segment_timeline = enable;
245 	return GF_OK;
246 }
247 
248 GF_EXPORT
gf_dasher_enable_single_segment(GF_DASHSegmenter * dasher,Bool enable)249 GF_Err gf_dasher_enable_single_segment(GF_DASHSegmenter *dasher, Bool enable)
250 {
251 	if (!dasher) return GF_BAD_PARAM;
252 	dasher->single_segment = enable;
253 	return GF_OK;
254 }
255 
256 GF_EXPORT
gf_dasher_enable_single_file(GF_DASHSegmenter * dasher,Bool enable)257 GF_Err gf_dasher_enable_single_file(GF_DASHSegmenter *dasher, Bool enable)
258 {
259 	if (!dasher) return GF_BAD_PARAM;
260 	dasher->single_file = enable;
261 	return GF_OK;
262 }
263 
264 GF_EXPORT
gf_dasher_set_switch_mode(GF_DASHSegmenter * dasher,GF_DashSwitchingMode bitstream_switching)265 GF_Err gf_dasher_set_switch_mode(GF_DASHSegmenter *dasher, GF_DashSwitchingMode bitstream_switching)
266 {
267 	if (!dasher) return GF_BAD_PARAM;
268 	dasher->bitstream_switching_mode = bitstream_switching;
269 	return GF_OK;
270 }
271 
272 GF_EXPORT
gf_dasher_set_durations(GF_DASHSegmenter * dasher,Double default_segment_duration,Double default_fragment_duration,Double sub_duration)273 GF_Err gf_dasher_set_durations(GF_DASHSegmenter *dasher, Double default_segment_duration, Double default_fragment_duration, Double sub_duration)
274 {
275 	if (!dasher) return GF_BAD_PARAM;
276 	dasher->segment_duration = default_segment_duration * 1000 / dasher->dash_scale;
277 	if (default_fragment_duration)
278 		dasher->fragment_duration = default_fragment_duration * 1000 / dasher->dash_scale;
279 	else
280 		dasher->fragment_duration = dasher->segment_duration;
281 	dasher->sub_duration = sub_duration;
282 	return GF_OK;
283 }
284 
285 GF_EXPORT
gf_dasher_enable_rap_splitting(GF_DASHSegmenter * dasher,Bool segments_start_with_rap,Bool fragments_start_with_rap)286 GF_Err gf_dasher_enable_rap_splitting(GF_DASHSegmenter *dasher, Bool segments_start_with_rap, Bool fragments_start_with_rap)
287 {
288 	if (!dasher) return GF_BAD_PARAM;
289 	dasher->segments_start_with_rap = segments_start_with_rap;
290 	dasher->fragments_start_with_rap = fragments_start_with_rap;
291 	return GF_OK;
292 }
293 
294 GF_EXPORT
gf_dasher_set_segment_marker(GF_DASHSegmenter * dasher,u32 segment_marker_4cc)295 GF_Err gf_dasher_set_segment_marker(GF_DASHSegmenter *dasher, u32 segment_marker_4cc)
296 {
297 	if (!dasher) return GF_BAD_PARAM;
298 	dasher->segment_marker_4cc = segment_marker_4cc;
299 	return GF_OK;
300 }
301 
302 GF_EXPORT
gf_dasher_print_session_info(GF_DASHSegmenter * dasher,u32 fs_print_flags)303 GF_Err gf_dasher_print_session_info(GF_DASHSegmenter *dasher, u32 fs_print_flags)
304 {
305 	if (!dasher) return GF_BAD_PARAM;
306 	dasher->print_stats_graph = fs_print_flags;
307 	return GF_OK;
308 
309 }
310 
311 
312 GF_EXPORT
gf_dasher_enable_sidx(GF_DASHSegmenter * dasher,Bool enable_sidx,u32 subsegs_per_sidx,Bool daisy_chain_sidx,Bool use_ssix)313 GF_Err gf_dasher_enable_sidx(GF_DASHSegmenter *dasher, Bool enable_sidx, u32 subsegs_per_sidx, Bool daisy_chain_sidx, Bool use_ssix)
314 {
315 	if (!dasher) return GF_BAD_PARAM;
316 	dasher->enable_sidx = enable_sidx;
317 	dasher->subsegs_per_sidx = subsegs_per_sidx;
318 	dasher->daisy_chain_sidx = daisy_chain_sidx;
319 	dasher->use_ssix = use_ssix;
320 	return GF_OK;
321 }
322 
323 GF_EXPORT
gf_dasher_set_dynamic_mode(GF_DASHSegmenter * dasher,GF_DashDynamicMode dash_mode,Double mpd_update_time,s32 time_shift_depth,Double mpd_live_duration)324 GF_Err gf_dasher_set_dynamic_mode(GF_DASHSegmenter *dasher, GF_DashDynamicMode dash_mode, Double mpd_update_time, s32 time_shift_depth, Double mpd_live_duration)
325 {
326 	if (!dasher) return GF_BAD_PARAM;
327 	if (dasher->dash_mode != dash_mode) {
328 		dasher->dash_mode = dash_mode;
329 		dasher->dash_mode_changed = GF_TRUE;
330 	}
331 	dasher->time_shift_depth = time_shift_depth;
332 	dasher->mpd_update_time = mpd_update_time;
333 	dasher->mpd_live_duration = mpd_live_duration;
334 	return GF_OK;
335 }
336 
337 GF_EXPORT
gf_dasher_set_min_buffer(GF_DASHSegmenter * dasher,Double min_buffer)338 GF_Err gf_dasher_set_min_buffer(GF_DASHSegmenter *dasher, Double min_buffer)
339 {
340 	if (!dasher) return GF_BAD_PARAM;
341 	dasher->min_buffer_time = (u32)(min_buffer*1000);
342 	return GF_OK;
343 }
344 
345 GF_EXPORT
gf_dasher_set_ast_offset(GF_DASHSegmenter * dasher,s32 ast_offset_ms)346 GF_Err gf_dasher_set_ast_offset(GF_DASHSegmenter *dasher, s32 ast_offset_ms)
347 {
348 	if (!dasher) return GF_BAD_PARAM;
349 	dasher->ast_offset_ms = ast_offset_ms;
350 	return GF_OK;
351 }
352 
353 GF_EXPORT
gf_dasher_enable_memory_fragmenting(GF_DASHSegmenter * dasher,Bool fragments_in_memory)354 GF_Err gf_dasher_enable_memory_fragmenting(GF_DASHSegmenter *dasher, Bool fragments_in_memory)
355 {
356 	if (!dasher) return GF_BAD_PARAM;
357 	dasher->fragments_in_memory = fragments_in_memory;
358 	return GF_OK;
359 }
360 
361 GF_EXPORT
gf_dasher_set_initial_isobmf(GF_DASHSegmenter * dasher,u32 initial_moof_sn,u64 initial_tfdt)362 GF_Err gf_dasher_set_initial_isobmf(GF_DASHSegmenter *dasher, u32 initial_moof_sn, u64 initial_tfdt)
363 {
364 	if (!dasher) return GF_BAD_PARAM;
365 	dasher->initial_moof_sn = initial_moof_sn;
366 	dasher->initial_tfdt = initial_tfdt;
367 	return GF_OK;
368 }
369 
370 GF_EXPORT
gf_dasher_configure_isobmf_default(GF_DASHSegmenter * dasher,Bool no_fragments_defaults,GF_DASHPSSHMode pssh_mode,Bool samplegroups_in_traf,Bool single_traf_per_moof,Bool tfdt_per_traf,Bool mvex_after_traks,u32 sdtp_in_traf)371 GF_Err gf_dasher_configure_isobmf_default(GF_DASHSegmenter *dasher, Bool no_fragments_defaults, GF_DASHPSSHMode pssh_mode, Bool samplegroups_in_traf, Bool single_traf_per_moof, Bool tfdt_per_traf, Bool mvex_after_traks, u32 sdtp_in_traf)
372 {
373 	if (!dasher) return GF_BAD_PARAM;
374 	dasher->no_fragments_defaults = no_fragments_defaults;
375 	dasher->pssh_mode = pssh_mode;
376 	dasher->samplegroups_in_traf = samplegroups_in_traf;
377 	dasher->single_traf_per_moof = single_traf_per_moof;
378     dasher->tfdt_per_traf = tfdt_per_traf;
379     dasher->mvex_after_traks = mvex_after_traks;
380     dasher->sdtp_in_traf = sdtp_in_traf;
381 	return GF_OK;
382 }
383 
384 GF_EXPORT
gf_dasher_enable_utc_ref(GF_DASHSegmenter * dasher,Bool insert_utc)385 GF_Err gf_dasher_enable_utc_ref(GF_DASHSegmenter *dasher, Bool insert_utc)
386 {
387 	if (!dasher) return GF_BAD_PARAM;
388 	dasher->insert_utc = insert_utc;
389 	return GF_OK;
390 }
391 
392 GF_EXPORT
gf_dasher_enable_real_time(GF_DASHSegmenter * dasher,Bool real_time)393 GF_Err gf_dasher_enable_real_time(GF_DASHSegmenter *dasher, Bool real_time)
394 {
395 	if (!dasher) return GF_BAD_PARAM;
396 	dasher->real_time = real_time;
397 	return GF_OK;
398 }
399 
400 GF_EXPORT
gf_dasher_set_content_protection_location_mode(GF_DASHSegmenter * dasher,GF_DASH_ContentLocationMode mode)401 GF_Err gf_dasher_set_content_protection_location_mode(GF_DASHSegmenter *dasher, GF_DASH_ContentLocationMode mode)
402 {
403 	if (!dasher) return GF_BAD_PARAM;
404 	dasher->cp_location_mode = mode;
405 	return GF_OK;
406 }
407 
408 GF_EXPORT
gf_dasher_set_profile_extension(GF_DASHSegmenter * dasher,const char * dash_profile_extension)409 GF_Err gf_dasher_set_profile_extension(GF_DASHSegmenter *dasher, const char *dash_profile_extension)
410 {
411 	if (!dasher) return GF_BAD_PARAM;
412 	dasher->dash_profile_extension = dash_profile_extension;
413 	return GF_OK;
414 }
415 
416 GF_EXPORT
gf_dasher_enable_cached_inputs(GF_DASHSegmenter * dasher,Bool no_cache)417 GF_Err gf_dasher_enable_cached_inputs(GF_DASHSegmenter *dasher, Bool no_cache)
418 {
419 	if (!dasher) return GF_BAD_PARAM;
420 	dasher->no_cache = no_cache;
421 	return GF_OK;
422 }
423 
424 GF_EXPORT
gf_dasher_enable_loop_inputs(GF_DASHSegmenter * dasher,Bool do_loop)425 GF_Err gf_dasher_enable_loop_inputs(GF_DASHSegmenter *dasher, Bool do_loop)
426 {
427 	if (!dasher) return GF_BAD_PARAM;
428 	dasher->disable_loop = do_loop ? GF_FALSE : GF_TRUE;
429 	return GF_OK;
430 }
431 
432 GF_EXPORT
gf_dasher_set_hls_clock(GF_DASHSegmenter * dasher,Bool insert_clock)433 GF_Err gf_dasher_set_hls_clock(GF_DASHSegmenter *dasher, Bool insert_clock)
434 {
435        if (!dasher) return GF_BAD_PARAM;
436        dasher->hls_clock = insert_clock;
437        return GF_OK;
438 }
439 
440 GF_EXPORT
gf_dasher_set_split_mode(GF_DASHSegmenter * dasher,GF_DASH_SplitMode split_mode)441 GF_Err gf_dasher_set_split_mode(GF_DASHSegmenter *dasher, GF_DASH_SplitMode split_mode)
442 {
443 	if (!dasher) return GF_BAD_PARAM;
444 	dasher->split_mode = split_mode;
445 	return GF_OK;
446 }
447 
448 GF_EXPORT
gf_dasher_set_cues(GF_DASHSegmenter * dasher,const char * cues_file,Bool strict_cues)449 GF_Err gf_dasher_set_cues(GF_DASHSegmenter *dasher, const char *cues_file, Bool strict_cues)
450 {
451 	dasher->cues_file = cues_file;
452 	dasher->strict_cues = strict_cues;
453 	return GF_OK;
454 }
455 
456 GF_EXPORT
gf_dasher_add_input(GF_DASHSegmenter * dasher,const GF_DashSegmenterInput * input)457 GF_Err gf_dasher_add_input(GF_DASHSegmenter *dasher, const GF_DashSegmenterInput *input)
458 {
459 	if (!dasher) return GF_BAD_PARAM;
460 
461 	if (!stricmp(input->file_name, "NULL") || !strcmp(input->file_name, "") || !input->file_name) {
462 		if (!input->xlink || !strcmp(input->xlink, "")) {
463 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] No input file specified and no xlink set - cannot dash\n"));
464 			return GF_BAD_PARAM;
465 		}
466 	}
467 
468 	gf_list_add(dasher->inputs, (void *) input);
469 	return GF_OK;
470 }
471 
on_dasher_event(void * _udta,GF_Event * evt)472 static Bool on_dasher_event(void *_udta, GF_Event *evt)
473 {
474 	u32 i, count;
475 	GF_FilterStats stats;
476 	GF_DASHSegmenter *dasher = (GF_DASHSegmenter *)_udta;
477 	if (evt && (evt->type != GF_EVENT_PROGRESS)) return GF_FALSE;
478 
479 	stats.report_updated = GF_FALSE;
480 	if (!dasher->dash_filter_idx_plus_one) {
481 		count = gf_fs_get_filters_count(dasher->fsess);
482 		for (i=0; i<count; i++) {
483 			if (gf_fs_get_filter_stats(dasher->fsess, i, &stats) != GF_OK) continue;
484 			if (strcmp(stats.reg_name, "dasher")) continue;
485 			dasher->dash_filter_idx_plus_one = i+1;
486 			break;
487 		}
488 		if (!dasher->dash_filter_idx_plus_one) return GF_FALSE;
489 	} else {
490 		if (gf_fs_get_filter_stats(dasher->fsess, dasher->dash_filter_idx_plus_one-1, &stats) != GF_OK)
491 			return GF_FALSE;
492 	}
493 	if (! stats.report_updated) return GF_FALSE;
494 	if (stats.percent/100 == dasher->last_prog) return GF_FALSE;
495 	dasher->last_prog = stats.percent / 100;
496 
497 	if ( stats.status) {
498 		GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Dashing %s\r", stats.status));
499 	} else if (stats.percent>0) {
500 		GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Dashing: % 2.2f %%\r", ((Double)stats.percent) / 100));
501 	}
502 	return GF_FALSE;
503 }
504 
505 /*create filter session, setup destination options and setup sources options*/
gf_dasher_setup(GF_DASHSegmenter * dasher)506 static GF_Err gf_dasher_setup(GF_DASHSegmenter *dasher)
507 {
508 	GF_Err e;
509 	u32 i, count;
510 	char *sep_ext;
511 	char *args=NULL, szArg[1024];
512 	Bool multi_period = GF_FALSE;
513 
514 	if (!dasher->mpd_name) {
515 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Missing MPD name\n"));
516 		return GF_OUT_OF_MEM;
517 	}
518 
519 	dasher->fsess = gf_fs_new_defaults(0);
520 
521 #ifndef GPAC_DISABLE_LOG
522 	if (!gf_sys_is_test_mode() && (gf_log_get_tool_level(GF_LOG_APP)!=GF_LOG_QUIET) && !gf_sys_is_quiet() ) {
523 		gf_fs_enable_reporting(dasher->fsess, GF_TRUE);
524 		gf_fs_set_ui_callback(dasher->fsess, on_dasher_event, dasher);
525 	}
526 #endif
527 
528 	if (!dasher->fsess) {
529 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to create filter session\n"));
530 		return GF_OUT_OF_MEM;
531 	}
532 
533 	sep_ext = gf_url_colon_suffix(dasher->mpd_name);
534 	if (sep_ext) {
535 		if (sep_ext[1] == '\\') sep_ext = strchr(sep_ext+1, ':');
536 		else if (sep_ext[1]=='/') {
537 			sep_ext = strchr(sep_ext+1, '/');
538 			if (sep_ext) sep_ext = strchr(sep_ext, ':');
539 		}
540 	}
541 	if (sep_ext) {
542 		sep_ext[0] = 0;
543 	}
544 
545 	sprintf(szArg, "segdur=%g", dasher->segment_duration);
546 	e = gf_dynstrcat(&args, szArg, ":");
547 
548 	if (sep_ext)
549 		e |= gf_dynstrcat(&args, sep_ext+1, ":");
550 
551 	if (dasher->single_segment) e |= gf_dynstrcat(&args, "sseg", ":");
552 	if (dasher->single_file) e |= gf_dynstrcat(&args, "sfile", ":");
553 	if (dasher->use_url_template) e |= gf_dynstrcat(&args, "tpl", ":");
554 	if (dasher->use_segment_timeline) e |= gf_dynstrcat(&args, "stl", ":");
555 	if (dasher->dash_mode) {
556 		e |= gf_dynstrcat(&args, (dasher->dash_mode == GF_DASH_DYNAMIC_LAST) ? "dynlast" : "dynamic", ":");
557 		//make dasher reschedule by default for MP4Box
558 		e |= gf_dynstrcat(&args, "reschedule", ":");
559 	}
560 	if (dasher->disable_segment_alignment) e |= gf_dynstrcat(&args, "!align", ":");
561 	if (dasher->enable_mix_codecs) e |= gf_dynstrcat(&args, "mix_codecs", ":");
562 	if (dasher->insert_utc) e |= gf_dynstrcat(&args, "ntp=yes", ":");
563 	if (dasher->enable_sar_mix) e |= gf_dynstrcat(&args, "no_sar", ":");
564 	//forcep not mapped
565 	switch (dasher->bitstream_switching_mode) {
566 	case GF_DASH_BSMODE_DEFAULT:
567 		break;
568 	case GF_DASH_BSMODE_NONE:
569 		e |= gf_dynstrcat(&args, "bs_switch=off", ":");
570 		break;
571 	case GF_DASH_BSMODE_INBAND:
572 		e |= gf_dynstrcat(&args, "bs_switch=inband", ":");
573 		break;
574 	case GF_DASH_BSMODE_MERGED:
575 		e |= gf_dynstrcat(&args, "bs_switch=on", ":");
576 		break;
577 	case GF_DASH_BSMODE_MULTIPLE_ENTRIES:
578 		e |= gf_dynstrcat(&args, "bs_switch=multi", ":");
579 		break;
580 	case GF_DASH_BSMODE_SINGLE:
581 		e |= gf_dynstrcat(&args, "bs_switch=force", ":");
582 		break;
583 	}
584 	//avcp, hvcp, aacp not mapped
585 	if (dasher->seg_rad_name) {
586 		sprintf(szArg, "template=%s", dasher->seg_rad_name);
587 		e |= gf_dynstrcat(&args, szArg, ":");
588 	}
589 	if (dasher->seg_ext) {
590 		sprintf(szArg, "segext=%s", dasher->seg_ext);
591 		e |= gf_dynstrcat(&args, szArg, ":");
592 	}
593 	if (dasher->seg_init_ext) {
594 		sprintf(szArg, "initext=%s", dasher->seg_init_ext);
595 		e |= gf_dynstrcat(&args, szArg, ":");
596 	}
597 	if (dasher->ast_offset_ms) {
598 		sprintf(szArg, "asto=%d", -dasher->ast_offset_ms);
599 		e |= gf_dynstrcat(&args, szArg, ":");
600 	}
601 	switch (dasher->profile) {
602 	case GF_DASH_PROFILE_AUTO:
603 		break;
604 	case GF_DASH_PROFILE_LIVE:
605 		e |= gf_dynstrcat(&args, "profile=live", ":");
606 		break;
607  	case GF_DASH_PROFILE_ONDEMAND:
608 		e |= gf_dynstrcat(&args, "profile=onDemand", ":");
609 		break;
610 	case GF_DASH_PROFILE_MAIN:
611 		e |= gf_dynstrcat(&args, "profile=main", ":");
612 		break;
613 	case GF_DASH_PROFILE_FULL:
614 		e |= gf_dynstrcat(&args, "profile=full", ":");
615 		if (!dasher->segments_start_with_rap) e |= gf_dynstrcat(&args, "!sap", ":");
616 		break;
617 	case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
618 		e |= gf_dynstrcat(&args, "profile=hbbtv1.5.live", ":");
619 		break;
620 	case GF_DASH_PROFILE_AVC264_LIVE:
621 		e |= gf_dynstrcat(&args, "profile=dashavc264.live", ":");
622 		break;
623 	case GF_DASH_PROFILE_AVC264_ONDEMAND:
624 		e |= gf_dynstrcat(&args, "profile=dashavc264.onDemand", ":");
625 		break;
626 	}
627 	if (dasher->cp_location_mode==GF_DASH_CPMODE_REPRESENTATION) e |= gf_dynstrcat(&args, "cp=rep", ":");
628 	else if (dasher->cp_location_mode==GF_DASH_CPMODE_BOTH) e |= gf_dynstrcat(&args, "cp=both", ":");
629 
630 	if (dasher->min_buffer_time) {
631 		sprintf(szArg, "buf=%d", dasher->min_buffer_time);
632 		e |= gf_dynstrcat(&args, szArg, ":");
633 	}
634 	if (dasher->dash_scale != 1000) {
635 		sprintf(szArg, "timescale=%d", dasher->dash_scale);
636 		e |= gf_dynstrcat(&args, szArg, ":");
637 	}
638 	if (!dasher->check_duration) e |= gf_dynstrcat(&args, "!check_dur", ":");
639 	//skip_seg not exposed
640 
641 
642 
643 	if (dasher->dash_mode >= GF_DASH_DYNAMIC) {
644 		if (dasher->time_shift_depth<0) e |= gf_dynstrcat(&args, "tsb=-1", ":");
645 		else {
646 			sprintf(szArg, "tsb=%g", ((Double)dasher->time_shift_depth)/1000);
647 			e |= gf_dynstrcat(&args, szArg, ":");
648 		}
649 
650 		if (dasher->utc_start_date) {
651 			sprintf(szArg, "ast=%s", dasher->utc_start_date);
652 			e |= gf_dynstrcat(&args, szArg, ":");
653 		}
654 		if (dasher->mpd_update_time) {
655 			sprintf(szArg, "refresh=%g", dasher->mpd_update_time);
656 			e |= gf_dynstrcat(&args, szArg, ":");
657 		}
658 		else {
659 			sprintf(szArg, "refresh=-%g", dasher->mpd_live_duration);
660 			e |= gf_dynstrcat(&args, szArg, ":");
661 		}
662 	}
663 	if (dasher->sub_duration) {
664 		sprintf(szArg, "subdur=%g", dasher->sub_duration);
665 		e |= gf_dynstrcat(&args, szArg, ":");
666 	}
667 	if (dasher->dash_state) {
668 		sprintf(szArg, "state=%s", dasher->dash_state);
669 		e |= gf_dynstrcat(&args, szArg, ":");
670 	}
671 	if (! dasher->disable_loop && dasher->dash_state) e |= gf_dynstrcat(&args, "loop", ":");
672 	if (dasher->hls_clock) e |= gf_dynstrcat(&args, "hlsc", ":");
673 
674 	//the rest is not yet exposed through the old api, but can be set through output file name
675 
676 	if (dasher->dash_mode>=GF_DASH_DYNAMIC) {
677 	 	sprintf(szArg, "_p_gentime=%p", &dasher->next_gen_ntp_ms);
678 	 	e |= gf_dynstrcat(&args, szArg, ":");
679 	 	sprintf(szArg, "_p_mpdtime=%p", &dasher->mpd_time_ms);
680 	 	e |= gf_dynstrcat(&args, szArg, ":");
681 	}
682 
683 	//append ISOBMFF options
684 	if (dasher->fragment_duration) {
685 		Double diff = dasher->fragment_duration;
686 		diff -= dasher->segment_duration;
687 		if (diff<0) diff = -diff;
688 		if (diff > 0.01) {
689 			sprintf(szArg, "cdur=%g", dasher->fragment_duration);
690 			e |= gf_dynstrcat(&args, szArg, ":");
691 		}
692 	}
693 	if (dasher->segment_marker_4cc) {
694 		sprintf(szArg, "m4cc=%s", gf_4cc_to_str(dasher->segment_marker_4cc) );
695 		e |= gf_dynstrcat(&args, szArg, ":");
696 	}
697 	if (dasher->daisy_chain_sidx) e |= gf_dynstrcat(&args, "chain_sidx", ":");
698 	if (dasher->use_ssix) e |= gf_dynstrcat(&args, "ssix", ":");
699 	if (dasher->initial_moof_sn) {
700 		sprintf(szArg, "msn=%d", dasher->initial_moof_sn );
701 		e |= gf_dynstrcat(&args, szArg, ":");
702 	}
703 	if (dasher->initial_tfdt) {
704 		sprintf(szArg, "tfdt="LLU"", dasher->initial_tfdt );
705 		e |= gf_dynstrcat(&args, szArg, ":");
706 	}
707 	if (dasher->no_fragments_defaults) e |= gf_dynstrcat(&args, "nofragdef", ":");
708 	if (dasher->single_traf_per_moof) e |= gf_dynstrcat(&args, "straf", ":");
709 	if (dasher->single_trun_per_traf) e |= gf_dynstrcat(&args, "strun", ":");
710 	switch (dasher->pssh_mode) {
711 	case GF_DASH_PSSH_MOOV:
712 		e |= gf_dynstrcat(&args, "pssh=v", ":");
713 		break;
714 	case GF_DASH_PSSH_MOOV_MPD:
715 		e |= gf_dynstrcat(&args, "pssh=mv", ":");
716 		break;
717 	case GF_DASH_PSSH_MOOF:
718 		e |= gf_dynstrcat(&args, "pssh=f", ":");
719 		break;
720 	case GF_DASH_PSSH_MOOF_MPD:
721 		e |= gf_dynstrcat(&args, "pssh=mf", ":");
722 		break;
723 	case GF_DASH_PSSH_MPD:
724 		e |= gf_dynstrcat(&args, "pssh=m", ":");
725 		break;
726 	}
727 
728 
729 	if (dasher->samplegroups_in_traf) e |= gf_dynstrcat(&args, "sgpd_traf", ":");
730 	if (dasher->enable_sidx) {
731 		sprintf(szArg, "subs_sidx=%d", dasher->subsegs_per_sidx );
732 		e |= gf_dynstrcat(&args, szArg, ":");
733 	}
734 
735 	if (dasher->fragments_start_with_rap) e |= gf_dynstrcat(&args, "sfrag", ":");
736 	if (dasher->tmpdir) {
737 		sprintf(szArg, "tmpd=%s", dasher->tmpdir );
738 		e |= gf_dynstrcat(&args, szArg, ":");
739 	}
740 
741 	if (dasher->cues_file) {
742 		sprintf(szArg, "cues=%s", dasher->cues_file );
743 		e |= gf_dynstrcat(&args, szArg, ":");
744 	}
745 	if (dasher->strict_cues) e |= gf_dynstrcat(&args, "strict_cues", ":");
746 
747 	if (dasher->mvex_after_traks) e |= gf_dynstrcat(&args, "mvex", ":");
748 	if (dasher->sdtp_in_traf==1) e |= gf_dynstrcat(&args, "sdtp_traf=sdtp", ":");
749 	else if (dasher->sdtp_in_traf==2) e |= gf_dynstrcat(&args, "sdtp_traf=both", ":");
750 
751 	if (dasher->split_mode==GF_DASH_SPLIT_CLOSEST)
752 		e |= gf_dynstrcat(&args, "sbound=closest", ":");
753 	else if (dasher->split_mode==GF_DASH_SPLIT_IN)
754 		e |= gf_dynstrcat(&args, "sbound=in", ":");
755 
756 	//finally append profiles/info/etc with double separators as these may contain ':'
757 	if (dasher->dash_profile_extension) {
758 		sprintf(szArg, "profX=%s", dasher->dash_profile_extension);
759 		e |= gf_dynstrcat(&args, szArg, "::");
760 	}
761 	if (dasher->title) {
762 		sprintf(szArg, "title=%s", dasher->title);
763 		e |= gf_dynstrcat(&args, szArg, "::");
764 	}
765 	if (dasher->sourceInfo) {
766 		sprintf(szArg, "source=%s", dasher->sourceInfo);
767 		e |= gf_dynstrcat(&args, szArg, "::");
768 	}
769 	if (dasher->moreInfoURL) {
770 		sprintf(szArg, "info=%s", dasher->moreInfoURL);
771 		e |= gf_dynstrcat(&args, szArg, "::");
772 	}
773 	if (dasher->copyright) {
774 		sprintf(szArg, "cprt=%s", dasher->copyright);
775 		e |= gf_dynstrcat(&args, szArg, "::");
776 	}
777 	if (dasher->lang) {
778 		sprintf(szArg, "lang=%s", dasher->lang);
779 		e |= gf_dynstrcat(&args, szArg, "::");
780 	}
781 	if (dasher->locations) {
782 		sprintf(szArg, "location=%s", dasher->locations);
783 		e |= gf_dynstrcat(&args, szArg, "::");
784 	}
785 	if (dasher->base_urls) {
786 		sprintf(szArg, "base=%s", dasher->base_urls);
787 		e |= gf_dynstrcat(&args, szArg, "::");
788 	}
789 
790 	dasher->dash_mode_changed = GF_FALSE;
791 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Instantiating dasher filter for dst %s with args %s\n", dasher->mpd_name, args));
792 
793 	if (e) {
794 		if (args) gf_free(args);
795 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to setup DASH filter arguments\n"));
796 		return e;
797 	}
798 	dasher->output = gf_fs_load_destination(dasher->fsess, dasher->mpd_name, args, NULL, &e);
799 
800 	if (args) gf_free(args);
801 
802 	if (sep_ext) {
803 		sep_ext[0] = ':';
804 	}
805 
806 	if (!dasher->output) {
807 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to load DASH filter\n"));
808 		return e;
809 	}
810 
811 	//and setup sources
812 	count = gf_list_count(dasher->inputs);
813 
814 	for (i=0; i<count; i++) {
815 		GF_DashSegmenterInput *di = gf_list_get(dasher->inputs, i);
816 		if (di->periodID || di->period_duration || di->xlink) {
817 			multi_period = GF_TRUE;
818 		}
819 		di->period_order=0;
820 	}
821 	if (multi_period) {
822 		u32 cur_period_order = 1;
823 		for (i=0; i<count; i++) {
824 			u32 j;
825 			GF_DashSegmenterInput *a_di = NULL;
826 			GF_DashSegmenterInput *di = gf_list_get(dasher->inputs, i);
827 			if (!di->periodID) {
828 				di->period_order = 0;
829 				continue;
830 			}
831 			for (j=0; j<count; j++) {
832 				a_di = gf_list_get(dasher->inputs, j);
833 				if ((a_di != di) && a_di->periodID && !strcmp(a_di->periodID, di->periodID))
834 					break;
835 				a_di = NULL;
836 			}
837 			if (a_di) {
838 				di->period_order = a_di->period_order;
839 				continue;
840 			}
841 			di->period_order = cur_period_order;
842 			cur_period_order++;
843 		}
844 	}
845 
846 	for (i=0; i<count; i++) {
847 		u32 j;
848 		GF_Filter *src = NULL;
849 		GF_Filter *rt = NULL;
850 		const char *url = "null";
851 		char *frag=NULL;
852 		GF_DashSegmenterInput *di = gf_list_get(dasher->inputs, i);
853 
854 		if (dasher->real_time) {
855 			rt = gf_fs_load_filter(dasher->fsess, "reframer:rt=sync", NULL);
856 		}
857 		if (di->file_name && strlen(di->file_name)) url = di->file_name;
858 		if (!stricmp(url, "null")) url = NULL;
859 		if (url) {
860 			frag = strrchr(di->file_name, '#');
861 			if (frag) frag[0] = 0;
862 		}
863 
864 		args = NULL;
865 
866 		//if source is isobmf using extractors, we want to keep the extractors
867 		e = gf_dynstrcat(&args, "smode=splitx", ":");
868 
869 		if (frag) {
870 			if (!strncmp(frag+1, "trackID=", 8)) {
871 				sprintf(szArg, "tkid=%s", frag+9 );
872 			} else {
873 				sprintf(szArg, "tkid=%s", frag+1);
874 			}
875 			e |= gf_dynstrcat(&args, szArg, ":");
876 		} else if (di->track_id) {
877 			sprintf(szArg, "tkid=%d", di->track_id );
878 			e |= gf_dynstrcat(&args, szArg, ":");
879 		}
880 
881 		if (di->source_opts) {
882 			e |= gf_dynstrcat(&args, di->source_opts, ":");
883 		}
884 
885 		//set all args
886 		if (di->representationID && strcmp(di->representationID, "NULL")) {
887 			sprintf(szArg, "#Representation=%s", di->representationID );
888 			e |= gf_dynstrcat(&args, szArg, ":");
889 		}
890 		if (di->periodID) {
891 			sprintf(szArg, "#Period=%s", di->periodID );
892 			e |= gf_dynstrcat(&args, szArg, ":");
893 		}
894 		if (di->asID)  {
895 			sprintf(szArg, "#ASID=%d", di->asID );
896 			e |= gf_dynstrcat(&args, szArg, ":");
897 		}
898 		//period start as negative to keep declaration order
899 		if (multi_period && di->period_order) {
900 			sprintf(szArg, "#PStart=-%d", di->period_order);
901 			e |= gf_dynstrcat(&args, szArg, ":");
902 		}
903 		if (di->period_duration) {
904 			if (!url) {
905 				sprintf(szArg, "#PDur=%g", di->period_duration );
906 				e |= gf_dynstrcat(&args, szArg, ":");
907 			} else {
908 				sprintf(szArg, "#DashDur=%g", di->period_duration );
909 				e |= gf_dynstrcat(&args, szArg, ":");
910 			}
911 		}
912 
913 		if (di->dash_duration) {
914 			sprintf(szArg, "#DashDur=%g", di->period_duration );
915 			e |= gf_dynstrcat(&args, szArg, ":");
916 		}
917 		if (url && di->media_duration) {
918 			sprintf(szArg, "#CDur=%g", di->media_duration );
919 			e |= gf_dynstrcat(&args, szArg, ":");
920 		}
921 
922 		if (di->xlink) {
923 			sprintf(szArg, "#xlink=%s", di->xlink );
924 			e |= gf_dynstrcat(&args, szArg, ":");
925 		}
926 		if (di->bandwidth)  {
927 			sprintf(szArg, "#Bitrate=%d", di->bandwidth );
928 			e |= gf_dynstrcat(&args, szArg, ":");
929 			sprintf(szArg, "#Maxrate=%d", di->bandwidth );
930 			e |= gf_dynstrcat(&args, szArg, ":");
931 		}
932 
933 		for (j=0;j<di->nb_baseURL; j++) {
934 			if (!j) {
935 				sprintf(szArg, "#BUrl=%s", di->baseURL[j] );
936 				e |= gf_dynstrcat(&args, szArg, ":");
937 			} else {
938 				e |= gf_dynstrcat(&args, di->baseURL[j], ",");
939 			}
940 		}
941 		for (j=0;j<di->nb_roles; j++) {
942 			if (!j) {
943 				sprintf(szArg, "#Role=%s", di->roles[j] );
944 				e |= gf_dynstrcat(&args, szArg, ":");
945 			} else {
946 				e |= gf_dynstrcat(&args, di->roles[j], ",");
947 			}
948 		}
949 
950 		for (j=0;j<di->nb_rep_descs; j++) {
951 			if (!j) {
952 				sprintf(szArg, "#RDesc=%s", di->rep_descs[j] );
953 				e |= gf_dynstrcat(&args, szArg, ":");
954 			} else {
955 				e |= gf_dynstrcat(&args, di->rep_descs[j], ",");
956 			}
957 		}
958 
959 		for (j=0;j<di->nb_p_descs; j++) {
960 			if (!j) {
961 				sprintf(szArg, "#PDesc=%s", di->p_descs[j] );
962 				e |= gf_dynstrcat(&args, szArg, ":");
963 			} else {
964 				e |= gf_dynstrcat(&args, di->p_descs[j], ",");
965 			}
966 		}
967 
968 		for (j=0;j<di->nb_as_descs; j++) {
969 			if (!j) {
970 				sprintf(szArg, "#ASDesc=%s", di->as_descs[j] );
971 				e |= gf_dynstrcat(&args, szArg, ":");
972 			} else {
973 				e |= gf_dynstrcat(&args, di->as_descs[j], ",");
974 			}
975 		}
976 
977 		for (j=0;j<di->nb_as_c_descs; j++) {
978 			if (!j) {
979 				sprintf(szArg, "#ASCDesc=%s", di->as_c_descs[j] );
980 				e |= gf_dynstrcat(&args, szArg, ":");
981 			} else {
982 				e |= gf_dynstrcat(&args, di->as_c_descs[j], ",");
983 			}
984 		}
985 
986 		if (di->startNumber) {
987 			sprintf(szArg, "#StartNumber=%d", di->startNumber );
988 			e |= gf_dynstrcat(&args, szArg, ":");
989 		}
990 		if (di->seg_template) {
991 			sprintf(szArg, "#Template=%s", di->seg_template );
992 			e |= gf_dynstrcat(&args, szArg, ":");
993 		}
994 		if (di->hls_pl) {
995 			sprintf(szArg, "#HLSPL=%s", di->hls_pl );
996 			e |= gf_dynstrcat(&args, szArg, ":");
997 		}
998 
999 		if (di->sscale) e |= gf_dynstrcat(&args, "#SingleScale=true", ":");
1000 
1001 
1002 		if (e) {
1003 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to setup source arguments for %s\n", di->file_name));
1004 			if (frag) frag[0] = '#';
1005 			if (args) gf_free(args);
1006 			return e;
1007 		}
1008 
1009 		if (!url) url = "null";
1010 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Instantiating dasher source %s with args %s\n", url, args));
1011 		src = gf_fs_load_source(dasher->fsess, url, args, NULL, &e);
1012 		if (args) gf_free(args);
1013 		if (frag) frag[0] = '#';
1014 
1015 		if (!src) {
1016 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to load source filter for %s\n", di->file_name));
1017 			return e;
1018 		}
1019 
1020 		if (rt) {
1021 			gf_filter_set_source(rt, src, NULL);
1022 			src = rt;
1023 		}
1024 
1025 		if (!di->filter_chain) continue;
1026 
1027 		//create the filter chain between source (or rt if it was set) and dasher
1028 
1029 		//filter chain
1030 		GF_Filter *prev_filter=src;
1031 		char *fargs = (char *) di->filter_chain;
1032 		while (fargs) {
1033 			GF_Filter *f;
1034 			char *sep = strstr(fargs, "@@");
1035 			if (sep) sep[0] = 0;
1036 			f = gf_fs_load_filter(dasher->fsess, fargs, &e);
1037 			if (!f) {
1038 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to load filter %s: %s\n", fargs, gf_error_to_string(e) ));
1039 				return e;
1040 			}
1041 			if (prev_filter) {
1042 				gf_filter_set_source(f, prev_filter, NULL);
1043 			}
1044 			prev_filter = f;
1045 			if (!sep) break;
1046 			sep[0] = '@';
1047 			fargs = sep+2;
1048 		}
1049 		if (prev_filter) {
1050 			gf_filter_set_source(dasher->output, prev_filter, NULL);
1051 		}
1052 	}
1053 
1054 	return GF_OK;
1055 }
1056 
dash_state_check_timing(const char * dash_state,u64 * next_gen_ntp_ms,u32 * next_time_ms)1057 GF_Err dash_state_check_timing(const char *dash_state, u64 *next_gen_ntp_ms, u32 *next_time_ms)
1058 {
1059 	u64 next_gen_ntp = 0;
1060 	GF_Err e = GF_OK;
1061 	GF_DOMParser *mpd_parser;
1062 
1063 	*next_gen_ntp_ms = 0;
1064 	*next_time_ms = 0;
1065 	if (!gf_file_exists(dash_state))
1066 		return GF_OK;
1067 
1068 	/* parse the MPD XML */
1069 	mpd_parser = gf_xml_dom_new();
1070 	e = gf_xml_dom_parse(mpd_parser, dash_state, NULL, NULL);
1071 	if (!e) {
1072 		GF_XMLNode *root = gf_xml_dom_get_root(mpd_parser);
1073 		GF_XMLAttribute *att;
1074 		u32 i=0;
1075 		e = GF_NON_COMPLIANT_BITSTREAM;
1076 		//extract "gpac:next_gen_time" but don't load a full MPD, not needed
1077 		while (root && (att = gf_list_enum(root->attributes, &i))) {
1078 			if (!strcmp(att->name, "gpac:next_gen_time")) {
1079 				sscanf(att->value, LLU, &next_gen_ntp);
1080 				e = GF_OK;
1081 				break;
1082 			}
1083 		}
1084 		gf_xml_dom_del(mpd_parser);
1085 	}
1086 	if (e) return e;
1087 
1088 	if (next_gen_ntp) {
1089 		u64 ntp_ms = gf_net_get_ntp_ms();
1090 		if (ntp_ms < next_gen_ntp) {
1091 			*next_time_ms = (u32) (next_gen_ntp - ntp_ms);
1092 			return GF_EOS;
1093 		}
1094 	}
1095 	return GF_OK;
1096 }
1097 
1098 GF_EXPORT
gf_dasher_process(GF_DASHSegmenter * dasher)1099 GF_Err gf_dasher_process(GF_DASHSegmenter *dasher)
1100 {
1101 	GF_Err e;
1102 	Bool need_seek = GF_TRUE;
1103 
1104 	/*first run, we need to extract the next gen time from context*/
1105 	if (dasher->dash_state && gf_file_exists(dasher->dash_state) && (dasher->dash_mode>=GF_DASH_DYNAMIC) && !dasher->next_gen_ntp_ms) {
1106 		u32 diff;
1107 		e = dash_state_check_timing(dasher->dash_state, &dasher->next_gen_ntp_ms, &diff);
1108 		if (e<0) return e;
1109 		if (e==GF_EOS) {
1110 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] generation called too early by %d ms\n", (s32) diff));
1111 			return e;
1112 		}
1113 	}
1114 
1115 	if (!dasher->fsess) {
1116 		e = gf_dasher_setup(dasher);
1117 		if (e) return e;
1118 		need_seek = GF_FALSE;
1119 	}
1120 	gf_fs_get_last_connect_error(dasher->fsess);
1121 	gf_fs_get_last_process_error(dasher->fsess);
1122 
1123 	//send change mode before sending the resume request, as the seek checks for last mode
1124 	if (dasher->dash_mode_changed) {
1125 		gf_filter_send_update(dasher->output, NULL, "dmode", (dasher->dash_mode == GF_DASH_DYNAMIC_LAST)  ? "dynlast" : "dynamic", GF_FILTER_UPDATE_DOWNSTREAM);
1126 	}
1127 
1128 	if (need_seek) {
1129 		GF_FilterEvent evt;
1130 		GF_FEVT_INIT(evt, GF_FEVT_RESUME, NULL);
1131 		evt.base.on_pid = gf_filter_get_ipid(dasher->output, 0);
1132 		gf_filter_send_event(dasher->output, &evt, GF_FALSE);
1133 	}
1134 
1135 	e = gf_fs_run(dasher->fsess);
1136 	if (e>0) e = GF_OK;
1137 
1138 	if (dasher->print_stats_graph & 1) gf_fs_print_stats(dasher->fsess);
1139 	if (dasher->print_stats_graph & 2) gf_fs_print_connections(dasher->fsess);
1140 
1141 	if (!e) e = gf_fs_get_last_connect_error(dasher->fsess);
1142 	if (!e) e = gf_fs_get_last_process_error(dasher->fsess);
1143 	if (e<0) return e;
1144 
1145 	on_dasher_event(dasher, NULL);
1146 	GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("\n"));
1147 
1148 	if (dasher->no_cache) {
1149 		gf_fs_del(dasher->fsess);
1150 		dasher->fsess = NULL;
1151 	}
1152 	return GF_OK;
1153 }
1154 
1155