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