Coverage Report

Created: 2025-06-13 06:24

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