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