Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MPEG-DASH/HLS segmenter
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/filters.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/iso639.h>
29 : #include <gpac/mpd.h>
30 : #include <gpac/internal/media_dev.h>
31 : #include <gpac/base_coding.h>
32 : #include <gpac/network.h>
33 : #include <gpac/crypt_tools.h>
34 :
35 : #define DEFAULT_PERIOD_ID "_gf_dash_def_period"
36 :
37 : typedef struct
38 : {
39 : GF_List *streams;
40 :
41 : //period element we will fill
42 : GF_MPD_Period *period;
43 : } GF_DasherPeriod;
44 :
45 : enum
46 : {
47 : DASHER_BS_SWITCH_DEF=0,
48 : DASHER_BS_SWITCH_OFF,
49 : DASHER_BS_SWITCH_ON,
50 : DASHER_BS_SWITCH_INBAND,
51 : DASHER_BS_SWITCH_FORCE,
52 : DASHER_BS_SWITCH_MULTI,
53 : };
54 :
55 : typedef enum
56 : {
57 : DASHER_UTCREF_NONE=0,
58 : DASHER_UTCREF_NTP,
59 : DASHER_UTCREF_HTTP_HEAD,
60 : DASHER_UTCREF_ISO,
61 : DASHER_UTCREF_XSDATE,
62 : DASHER_UTCREF_INBAND,
63 : } DasherUTCTimingType;
64 :
65 : enum
66 : {
67 : DASHER_NTP_REM=0,
68 : DASHER_NTP_YES,
69 : DASHER_NTP_KEEP,
70 : };
71 :
72 : enum
73 : {
74 : DASHER_SAP_OFF=0,
75 : DASHER_SAP_SIG,
76 : DASHER_SAP_ON,
77 : };
78 :
79 : enum
80 : {
81 : DASHER_BOUNDS_OUT=0,
82 : DASHER_BOUNDS_CLOSEST,
83 : DASHER_BOUNDS_IN,
84 : };
85 :
86 : enum
87 : {
88 : DASHER_MUX_ISOM=0,
89 : DASHER_MUX_TS,
90 : DASHER_MUX_MKV,
91 : DASHER_MUX_WEBM,
92 : DASHER_MUX_OGG,
93 : DASHER_MUX_RAW,
94 : DASHER_MUX_AUTO,
95 : };
96 :
97 : enum
98 : {
99 : DASHER_MPHA_NO=0,
100 : DASHER_MPHA_COMP_ONLY,
101 : DASHER_MPHA_ALL
102 : };
103 :
104 : enum
105 : {
106 : DASHER_FWD_NO = 0,
107 : DASHER_FWD_SEGS,
108 : DASHER_FWD_ALL,
109 : };
110 :
111 : enum
112 : {
113 : //unknown samples sync state at startup
114 : DASHER_SYNC_UNKNOWN=0,
115 : //all samples sync
116 : DASHER_SYNC_NONE,
117 : //some samples sync
118 : DASHER_SYNC_PRESENT,
119 : };
120 :
121 : enum
122 : {
123 : DASHER_CMAF_NONE=0,
124 : DASHER_CMAF_CMFC,
125 : DASHER_CMAF_CMF2
126 : };
127 :
128 : typedef struct
129 : {
130 : u32 bs_switch, profile, cp, ntp;
131 : s32 subs_sidx;
132 : s32 buf, timescale;
133 : Bool sfile, sseg, no_sar, mix_codecs, stl, tpl, align, sap, no_frag_def, sidx, split, hlsc, strict_cues, force_flush, last_seg_merge;
134 : u32 mha_compat;
135 : u32 strict_sap;
136 : u32 pssh;
137 : u32 cmaf;
138 : GF_Fraction segdur;
139 : u32 dmode;
140 : char *template;
141 : char *segext;
142 : char *initext;
143 : u32 muxtype;
144 : char *profX;
145 : Double asto;
146 : char *ast;
147 : char *state;
148 : char *cues;
149 : char *title, *source, *info, *cprt, *lang;
150 : GF_PropStringList location, base;
151 : Bool check_dur, skip_seg, loop, reschedule, scope_deps;
152 : Double refresh, tsb, subdur;
153 : u64 *_p_gentime, *_p_mpdtime;
154 : Bool m2ts;
155 : Bool cmpd, dual, sreg, pswitch;
156 : char *styp;
157 : Bool sigfrag;
158 : u32 sbound;
159 : char *utcs;
160 : char *mname;
161 : char *hlsdrm;
162 : u32 llhls;
163 : //inherited from mp4mx
164 : GF_Fraction cdur;
165 :
166 : //internal
167 : Bool in_error;
168 :
169 : //Manifest output pid
170 : GF_FilterPid *opid;
171 :
172 : GF_FilterPid *opid_alt;
173 : GF_Filter *alt_dst;
174 : Bool opid_alt_m3u8;
175 :
176 : GF_MPD *mpd;
177 :
178 : GF_DasherPeriod *current_period, *next_period;
179 : GF_List *pids;
180 : Bool template_use_source;
181 : s32 period_idx;
182 :
183 : GF_List *tpl_records;
184 : Bool use_xlink, use_cenc, check_main_role;
185 :
186 : //options for muxers, constrained by profile
187 : Bool no_fragments_defaults;
188 :
189 : Bool is_eos;
190 : u32 nb_seg_url_pending;
191 : u64 last_evt_check_time;
192 : Bool on_demand_done;
193 : Bool subdur_done;
194 : char *out_path;
195 :
196 : GF_Err setup_failure;
197 :
198 : Double nb_secs_to_discard;
199 : Bool first_context_load, store_init_params;
200 : Bool do_m3u8, do_mpd;
201 : Bool is_period_restore, is_empty_period;
202 :
203 : Bool store_seg_states;
204 :
205 : GF_List *postponed_pids;
206 : u32 last_dyn_period_id;
207 : u32 next_pid_id_in_period;
208 : Bool post_play_events;
209 :
210 : Bool force_period_switch;
211 : Bool period_not_ready;
212 : Bool check_connections;
213 :
214 : //-1 forces report update, otherwise this is a packet count
215 : s32 update_report;
216 :
217 : Bool purge_segments;
218 :
219 : Bool is_playing;
220 :
221 : Bool no_seg_dur;
222 :
223 : Bool utc_initialized;
224 : DasherUTCTimingType utc_timing_type;
225 : s32 utc_diff;
226 :
227 : Bool is_route;
228 :
229 : Bool force_hls_ll_manifest;
230 :
231 : u32 forward_mode;
232 :
233 : u8 last_hls_signature[GF_SHA1_DIGEST_SIZE], last_mpd_signature[GF_SHA1_DIGEST_SIZE], last_hls2_signature[GF_SHA1_DIGEST_SIZE];
234 :
235 : GF_CryptInfo *cinfo;
236 :
237 : Bool use_cues;
238 : Bool dyn_rate;
239 : } GF_DasherCtx;
240 :
241 : typedef enum
242 : {
243 : DASHER_HDR_NONE=0,
244 : DASHER_HDR_PQ10,
245 : DASHER_HDR_HLG,
246 : } DasherHDRType;
247 :
248 : typedef struct _dash_stream
249 : {
250 : GF_FilterPid *ipid, *opid;
251 :
252 : //stream properties
253 : u32 codec_id, timescale, stream_type, dsi_crc, dsi_enh_crc, id, dep_id, src_id;
254 : GF_Fraction sar, fps;
255 : u32 width, height;
256 : u32 sr, nb_ch;
257 : char *lang;
258 : Bool interlaced;
259 : const GF_PropertyValue *p_role;
260 : const GF_PropertyValue *p_period_desc;
261 : const GF_PropertyValue *p_as_desc;
262 : const GF_PropertyValue *p_as_any_desc;
263 : const GF_PropertyValue *p_rep_desc;
264 : const GF_PropertyValue *p_base_url;
265 : char *template;
266 : char *xlink;
267 : char *hls_vp_name;
268 : u32 nb_surround, nb_lfe;
269 : u64 ch_layout;
270 : GF_PropVec4i srd;
271 : DasherHDRType hdr_type;
272 : Bool sscale;
273 :
274 : //TODO: get the values for all below
275 : u32 view_id;
276 : //end of TODO
277 :
278 :
279 : u32 bitrate;
280 : GF_DasherPeriod *period;
281 : GF_MPD_Period *last_period;
282 :
283 : GF_Fraction dash_dur;
284 :
285 : char *period_id;
286 : char *period_continuity_id;
287 : GF_Fraction64 period_start;
288 : GF_Fraction64 period_dur;
289 : //0: not done, 1: eos/abort, 2: subdur exceeded
290 : u32 done;
291 : Bool seg_done;
292 :
293 : u32 nb_comp, nb_comp_done;
294 :
295 : u32 nb_rep, nb_rep_done;
296 : Double set_seg_duration;
297 :
298 : //repID for this stream, generated if not found
299 : char *rep_id;
300 : //AS ID for this stream, may be 0
301 : u32 as_id;
302 : u32 sync_as_id;
303 : struct _dash_stream *muxed_base;
304 : GF_List *complementary_streams;
305 : GF_List *comp_pids;
306 :
307 : //the one and only representation element
308 : GF_MPD_Representation *rep;
309 : //the parent adaptation set
310 : GF_MPD_AdaptationSet *set;
311 : Bool owns_set;
312 : //set to true to use inband params
313 : Bool inband_params;
314 : GF_List *multi_pids;
315 : GF_List *multi_tracks;
316 : //in case we share the same init segment, we MUST use the same timescale
317 : u32 force_timescale;
318 :
319 :
320 : u32 startNumber, seg_number;
321 : Bool rep_init;
322 : u64 first_cts;
323 : u64 first_dts;
324 : s64 pts_minus_cts;
325 : Bool is_encrypted;
326 :
327 : //target MPD timescale
328 : u32 mpd_timescale;
329 : //segment start time in target MPD timescale
330 : u64 seg_start_time;
331 : Bool split_set_names;
332 : Bool skip_tpl_reuse;
333 : u64 max_period_dur;
334 :
335 : GF_Filter *dst_filter;
336 :
337 : char *src_url;
338 :
339 : char *init_seg, *seg_template, *idx_template;
340 : u32 nb_sap_3, nb_sap_4;
341 : //ID of output pid (renumbered), used for content component and making sure output muxers use the same IDs
342 : u32 pid_id;
343 : //dependency ID of output pid (renumbered)
344 : u32 dep_pid_id;
345 : u32 nb_samples_in_source;
346 : u32 sync_points_type;
347 : //seg urls not yet handled (waiting for size/index callbacks)
348 : GF_List *pending_segment_urls;
349 : //segment states not yet handled (waiting for size/index/etc callbacks), used for M3U8 and state mode
350 : GF_List *pending_segment_states;
351 : //next segment start time in this stream timescale (NOT MPD timescale)
352 : u64 next_seg_start;
353 : //adjusted next segment start time in this stream timescale (NOT MPD timescale)
354 : //the value is the same as next_seg_start until the end of segment is found (SAP)
355 : //in which case it is adjusted to the SAP time
356 : u64 adjusted_next_seg_start;
357 :
358 : //force representation time end in this stream timescale (NOT MPD timescale)
359 : u64 force_rep_end;
360 :
361 : Bool segment_started;
362 : u64 first_cts_in_seg;
363 : u64 first_cts_in_next_seg;
364 : //used for last segment computation of segmentTimeline
365 : u64 est_first_cts_in_next_seg;
366 : u64 last_cts, last_dts;
367 : u64 cumulated_dur;
368 : Double cumulated_subdur;
369 : Bool subdur_done;
370 : u64 subdur_forced_use_period_dur;
371 : u64 nb_pck;
372 : u64 est_next_dts;
373 : u64 seek_to_pck;
374 : u64 ts_offset;
375 : u32 nb_repeat;
376 :
377 : Bool splitable;
378 : u32 loop_state;
379 : u32 split_dur_next;
380 :
381 : u32 moof_sn_inc, moof_sn;
382 : GF_Fraction64 clamped_dur;
383 :
384 : u32 nb_segments_purged;
385 : Double dur_purged;
386 : Bool tile_base;
387 : Bool tile_dep_id_merged;
388 : struct _dash_stream *merged_tile_dep;
389 :
390 : u32 cues_timescale;
391 : u32 nb_cues;
392 : GF_DASHCueInfo *cues;
393 : Bool cues_use_edits;
394 : s32 cues_ts_offset;
395 : Bool inband_cues;
396 :
397 : Bool clamp_done;
398 : u32 dcd_not_ready;
399 :
400 : Bool reschedule;
401 :
402 : GF_Fraction64 duration;
403 : GF_List *packet_queue;
404 : u32 nb_sap_in_queue;
405 : u32 sbound;
406 :
407 : u32 request_period_switch;
408 :
409 : //gm_ for gen manifest
410 : Double gm_duration_total, gm_duration_min, gm_duration_max;
411 : u32 gm_nb_segments;
412 :
413 : Bool no_seg_dur;
414 : //for route
415 : u64 hls_ref_id;
416 : GF_DASH_SegmentContext *current_seg_state;
417 :
418 : Bool transcode_detected;
419 :
420 : //HLS full seg encryption
421 : GF_CryptInfo *cinfo;
422 : GF_TrackCryptInfo *tci;
423 : u64 iv_low, iv_high;
424 : u32 key_idx;
425 : u32 nb_crypt_seg;
426 :
427 : Bool dyn_bitrate;
428 : u64 rate_first_dts_plus_one, rate_last_dts;
429 : u64 rate_media_size;
430 :
431 : u64 period_continuity_next_cts;
432 : } GF_DashStream;
433 :
434 : static void dasher_flush_segment(GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_last_in_period);
435 : static void dasher_update_rep(GF_DasherCtx *ctx, GF_DashStream *ds);
436 : static void dasher_reset_stream(GF_Filter *filter, GF_DashStream *ds, Bool is_destroy);
437 : static void dasher_update_period_duration(GF_DasherCtx *ctx, Bool is_period_switch);
438 : static GF_Err dasher_setup_period(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *for_ds);
439 :
440 488 : static GF_DasherPeriod *dasher_new_period()
441 : {
442 : GF_DasherPeriod *period;
443 488 : GF_SAFEALLOC(period, GF_DasherPeriod);
444 488 : if (period)
445 488 : period->streams = gf_list_new();
446 488 : return period;
447 : }
448 :
449 : #ifndef GPAC_DISABLE_AV_PARSERS
450 126 : static GF_Err dasher_get_audio_info_with_m4a_sbr_ps(GF_DashStream *ds, const GF_PropertyValue *dsi, u32 *SampleRate, u32 *Channels)
451 : {
452 : GF_M4ADecSpecInfo a_cfg;
453 : GF_Err e;
454 126 : if (SampleRate) *SampleRate = ds->sr;
455 126 : if (Channels) *Channels = ds->nb_ch;
456 :
457 126 : if (!dsi) {
458 0 : if (!ds->dcd_not_ready) {
459 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] missing AAC config\n"));
460 : }
461 : return GF_OK;
462 : }
463 126 : e = gf_m4a_get_config(dsi->value.data.ptr, dsi->value.data.size, &a_cfg);
464 126 : if (e) {
465 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] corrupted AAC Config, %s\n", gf_error_to_string(e)));
466 : return GF_NOT_SUPPORTED;
467 : }
468 126 : if (SampleRate && a_cfg.has_sbr) {
469 0 : *SampleRate = a_cfg.sbr_sr;
470 : }
471 126 : if (Channels) *Channels = a_cfg.nb_chan;
472 : return e;
473 : }
474 : #endif
475 :
476 :
477 250 : static void dasher_check_outpath(GF_DasherCtx *ctx)
478 : {
479 250 : if (!ctx->out_path) {
480 248 : ctx->out_path = gf_filter_pid_get_destination(ctx->opid);
481 248 : if (!ctx->out_path) return;
482 :
483 244 : if (ctx->mname) {
484 0 : char *sep = strstr(ctx->out_path, "://");
485 0 : if (sep) {
486 0 : char *opath = gf_url_concatenate(ctx->out_path, ctx->mname);
487 0 : if (opath) {
488 0 : gf_free(ctx->out_path);
489 0 : ctx->out_path = opath;
490 : }
491 : }
492 : }
493 : //check if we have a route/atsc output, in which we will case assign hls ref prop
494 244 : if (!strncmp(ctx->out_path, "route://", 8) || !strncmp(ctx->out_path, "atsc://", 7))
495 4 : ctx->is_route = GF_TRUE;
496 : }
497 : //for routeout
498 246 : if (ctx->opid)
499 246 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_URL, &PROP_STRING(ctx->out_path) );
500 246 : if (ctx->opid_alt)
501 2 : gf_filter_pid_set_property(ctx->opid_alt, GF_PROP_PID_URL, &PROP_STRING(ctx->out_path) );
502 : }
503 :
504 :
505 577 : static GF_Err dasher_hls_setup_crypto(GF_DasherCtx *ctx, GF_DashStream *ds)
506 : {
507 : GF_Err e;
508 : u32 pid_id=1;
509 : u32 i, count;
510 : const GF_PropertyValue *p;
511 : GF_CryptInfo *cinfo = NULL;
512 577 : const char *drm = ctx->hlsdrm;
513 577 : if (!ctx->do_m3u8) return GF_OK;
514 60 : if (ds->is_encrypted) return GF_OK;
515 31 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_CRYPT_INFO);
516 31 : if (p)
517 0 : drm = p->value.string;
518 : else
519 31 : cinfo = ctx->cinfo;
520 31 : if (!drm && !cinfo) return GF_OK;
521 :
522 0 : if (ctx->sfile) {
523 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Dasher] Cannot use HLS segment encryption with single file output\n"));
524 : return GF_BAD_PARAM;
525 : }
526 :
527 0 : if (!cinfo) {
528 0 : cinfo = gf_crypt_info_load(drm, &e);
529 0 : if (!cinfo) {
530 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[Dasher] Cannot load HLS DRM file %s\n", drm ));
531 0 : return e;
532 : }
533 0 : if (p) {
534 0 : if (ds->cinfo) gf_crypt_info_del(ds->cinfo);
535 0 : ds->cinfo = cinfo;
536 : } else {
537 0 : ctx->cinfo = cinfo;
538 : }
539 : }
540 0 : ds->tci = NULL;
541 0 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ID);
542 0 : if (p) pid_id = p->value.uint;
543 0 : count = gf_list_count(cinfo->tcis);
544 0 : for (i=0; i<count; i++) {
545 0 : GF_TrackCryptInfo *tci = gf_list_get(cinfo->tcis, i);
546 0 : if (tci->trackID && (tci->trackID != pid_id)) continue;
547 :
548 0 : ds->tci = tci;
549 0 : break;
550 : }
551 0 : if (!ds->tci) return GF_OK;
552 :
553 0 : ds->key_idx = 0;
554 0 : ds->iv_low = ds->iv_high = 0;
555 0 : for (i=0; i<8; i++) {
556 0 : ds->iv_high |= ds->tci->keys[ds->key_idx].IV[i];
557 0 : ds->iv_low |= ds->tci->keys[ds->key_idx].IV[i + 8];
558 0 : if (i<7) {
559 0 : ds->iv_high <<= 8;
560 0 : ds->iv_low <<= 8;
561 : }
562 : }
563 :
564 : return GF_OK;
565 : }
566 :
567 78 : static u32 dasher_get_dep_bitrate(GF_DasherCtx *ctx, GF_DashStream *ds)
568 : {
569 78 : u32 bitrate = ds->bitrate;
570 78 : if (ds->dep_id) {
571 39 : u32 i, count = gf_list_count(ctx->current_period->streams);
572 587 : for (i=0; i<count; i++) {
573 548 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
574 548 : if (a_ds == ds) continue;
575 :
576 509 : if (gf_list_find(a_ds->complementary_streams, ds)>=0) {
577 :
578 39 : bitrate += dasher_get_dep_bitrate(ctx, a_ds);
579 : }
580 : }
581 : }
582 78 : return bitrate;
583 : }
584 :
585 282 : static void dasher_update_bitrate(GF_DasherCtx *ctx, GF_DashStream *ds)
586 : {
587 : u64 rate;
588 : u32 scaler;
589 282 : if (!ds->dyn_bitrate || ds->bitrate) {
590 : return;
591 : }
592 :
593 215 : if (!ds->rate_first_dts_plus_one) {
594 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't compute bitrate in time for manifest generation, please report to GPAC devs !\n"));
595 : return;
596 : }
597 :
598 215 : rate = ds->rate_media_size;
599 215 : rate *= 8;
600 215 : if (ds->rate_last_dts > ds->rate_first_dts_plus_one - 1) {
601 215 : rate *= ds->timescale;
602 215 : rate /= (ds->rate_last_dts - ds->rate_first_dts_plus_one + 1);
603 : } else {
604 0 : rate *= ds->dash_dur.den;
605 0 : rate /= ds->dash_dur.num;
606 : }
607 : //express rates in 100kbps or 10kbps, and if ds is done, trust the average, otherwise add 10%
608 215 : scaler = (rate > 1000000) ? 100000 : 10000;
609 215 : if (rate > 10*scaler) {
610 187 : rate /= scaler;
611 187 : if (!ds->done) scaler = 11 * scaler / 10;
612 187 : rate *= scaler;
613 : }
614 :
615 215 : ds->bitrate = (u32) rate;
616 :
617 215 : if (ds->rep)
618 210 : ds->rep->bandwidth = ds->bitrate;
619 :
620 215 : if (ds->dep_id) {
621 0 : ds->rep->bandwidth = dasher_get_dep_bitrate(ctx, ds);
622 215 : } else if (ds->nb_comp && !ds->muxed_base) {
623 210 : u32 i, count = gf_list_count(ctx->current_period->streams);
624 443 : for (i=0; i<count; i++) {
625 233 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
626 233 : if (ds == a_ds) continue;;
627 23 : if (a_ds->muxed_base != ds) continue;
628 5 : if (a_ds->dyn_bitrate) {
629 5 : dasher_update_bitrate(ctx, a_ds);
630 : }
631 5 : ds->rep->bandwidth += a_ds->bitrate;
632 : }
633 : }
634 :
635 : //keep refreshing our rate estimation
636 215 : if (!ds->done && (ds->dyn_bitrate==1))
637 157 : ds->bitrate = 0;
638 : }
639 :
640 :
641 357 : static GF_Err dasher_stream_period_changed(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_new_period_request)
642 : {
643 : //period switching, check the stream is still scheduled - if so and not done, flush it, update period duration
644 357 : s32 res = gf_list_find(ctx->current_period->streams, ds);
645 : //force end of segment if stream is not yet done and in current period
646 357 : if ((res>=0) && !ds->done && !ds->seg_done) {
647 : GF_DashStream *base_ds;
648 :
649 0 : base_ds = ds->muxed_base ? ds->muxed_base : ds;
650 0 : if (is_new_period_request) {
651 0 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] New period requested during PID %s reconfiguration\n", gf_filter_pid_get_name(ds->ipid) ));
652 : } else {
653 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] PID %s config changed during active period, forcing period switch\n", gf_filter_pid_get_name(ds->ipid) ));
654 : }
655 0 : ds->seg_done = GF_TRUE;
656 0 : if(base_ds->nb_comp_done >= base_ds->nb_comp) {
657 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Invalid new period: %u components processed (max %u expected)\n", base_ds->nb_comp_done, base_ds->nb_comp));
658 : return GF_BAD_PARAM;
659 : }
660 0 : base_ds->nb_comp_done ++;
661 0 : ds->first_cts_in_next_seg = ds->est_first_cts_in_next_seg;;
662 :
663 0 : if (base_ds->nb_comp_done == base_ds->nb_comp) {
664 0 : dasher_flush_segment(ctx, base_ds, GF_TRUE);
665 : }
666 :
667 0 : ctx->force_period_switch = GF_TRUE;
668 0 : dasher_update_period_duration(ctx, GF_TRUE);
669 : }
670 : //remove stream from period
671 357 : if (res>=0) {
672 : //force an EOS on this stream for ondemand / side index generation flush
673 0 : if (ds->opid)
674 0 : gf_filter_pid_set_eos(ds->opid);
675 0 : ds->rep_init = GF_FALSE;
676 0 : gf_list_rem(ctx->current_period->streams, res);
677 : }
678 357 : ds->request_period_switch = 0;
679 :
680 : //this is tricky, when reassigning period IDs in the middle of a stream, we may have cases where some streams
681 : //are ready several packets before other streams due to processing delay, which results in period switch signal not
682 : //happening at the same time
683 357 : if (is_new_period_request && !ds->rep && ctx->current_period->period && gf_list_count(ctx->current_period->streams)) {
684 : Bool inject_in_period = GF_FALSE;
685 0 : if (ds->period_id && ctx->current_period->period->ID && !strcmp(ds->period_id, ctx->current_period->period->ID))
686 : inject_in_period = GF_TRUE;
687 0 : else if ((ctx->period_idx>0) && (ds->period_start.num<0) && ((s32) -ds->period_start.num == ctx->period_idx))
688 : inject_in_period = GF_TRUE;
689 0 : else if (ds->period_start.num * 1000 == ctx->current_period->period->start * ds->period_start.den)
690 : inject_in_period = GF_TRUE;
691 :
692 : if (inject_in_period) {
693 0 : gf_list_add(ctx->current_period->streams, ds);
694 0 : ds->period = ctx->current_period;
695 0 : dasher_setup_period(filter, ctx, ds);
696 : //force a MPD publish asap
697 0 : if (ctx->dmode != GF_DASH_STATIC)
698 0 : ctx->mpd->publishTime = 0;
699 : return GF_OK;
700 : }
701 : }
702 357 : gf_list_add(ctx->next_period->streams, ds);
703 357 : ds->period = ctx->next_period;
704 357 : return GF_OK;
705 : }
706 :
707 32 : static void dasher_send_encode_hints(GF_DasherCtx *ctx, GF_DashStream *ds)
708 : {
709 32 : if (!ctx->sfile && !ctx->stl && !ctx->use_cues) {
710 : GF_FilterEvent evt;
711 32 : GF_FEVT_INIT(evt, GF_FEVT_ENCODE_HINTS, ds->ipid)
712 32 : evt.encode_hints.intra_period = ds->dash_dur;
713 32 : gf_filter_pid_send_event(ds->ipid, &evt);
714 : }
715 32 : }
716 :
717 577 : static GF_Err dasher_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
718 : {
719 : Bool period_switch = GF_FALSE;
720 : const GF_PropertyValue *p, *dsi=NULL;
721 : u32 dc_crc, dc_enh_crc;
722 : GF_Err e;
723 : GF_DashStream *ds;
724 : Bool old_period_switch;
725 : u32 prev_stream_type;
726 : Bool new_period_request = GF_FALSE;
727 : const char *cue_file=NULL;
728 : s64 old_clamp_dur = 0;
729 577 : GF_DasherCtx *ctx = gf_filter_get_udta(filter);
730 :
731 577 : if (is_remove) {
732 0 : ds = gf_filter_pid_get_udta(pid);
733 0 : if (ds) {
734 0 : if (ds->dyn_bitrate) dasher_update_bitrate(ctx, ds);
735 0 : gf_list_del_item(ctx->pids, ds);
736 0 : gf_list_del_item(ctx->current_period->streams, ds);
737 0 : if (ctx->next_period)
738 0 : gf_list_del_item(ctx->next_period->streams, ds);
739 0 : dasher_reset_stream(filter, ds, GF_TRUE);
740 0 : gf_free(ds);
741 : }
742 : return GF_OK;
743 : }
744 577 : ctx->check_connections = GF_TRUE;
745 577 : if (!ctx->opid) {
746 244 : u32 i, nb_opids = ctx->dual ? 2 : 1;
747 490 : for (i=0; i < nb_opids; i++) {
748 : char *segext=NULL;
749 : char *force_ext=NULL;
750 : GF_FilterPid *opid;
751 246 : if (i==0) {
752 244 : ctx->opid = gf_filter_pid_new(filter);
753 244 : gf_filter_pid_set_name(ctx->opid, "MANIFEST");
754 244 : opid = ctx->opid;
755 : } else {
756 2 : if (!ctx->alt_dst && ctx->out_path) {
757 : char szSRC[100];
758 : GF_FileIO *gfio = NULL;
759 : char *mpath = ctx->out_path;
760 : u32 len;
761 2 : if (!strncmp(mpath, "gfio://", 7)) {
762 0 : gfio = gf_fileio_from_url(mpath);
763 0 : if (!gfio) return GF_BAD_PARAM;
764 : //only use basename as we will create the new resource through factory
765 0 : mpath = (char *) gf_file_basename(gf_fileio_resource_url(gfio));
766 0 : if (!mpath) return GF_OUT_OF_MEM;
767 : }
768 :
769 2 : len = (u32) strlen(mpath);
770 2 : char *out_path = gf_malloc(len+10);
771 2 : if (!out_path) return GF_OUT_OF_MEM;
772 : memcpy(out_path, mpath, len);
773 2 : out_path[len]=0;
774 2 : char *sep = gf_file_ext_start(out_path);
775 2 : if (sep) sep[0] = 0;
776 2 : if (ctx->do_m3u8) {
777 : strcat(out_path, ".mpd");
778 : force_ext = "mpd";
779 : } else {
780 0 : ctx->opid_alt_m3u8 = GF_TRUE;
781 0 : ctx->do_m3u8 = GF_TRUE;
782 : strcat(out_path, ".m3u8");
783 : force_ext = "m3u8";
784 : }
785 2 : if (gfio) {
786 0 : const char *rel = gf_fileio_factory(gfio, out_path);
787 0 : gf_free(out_path);
788 0 : out_path = gf_strdup(rel);
789 0 : if (!out_path) return GF_OUT_OF_MEM;
790 : }
791 :
792 2 : ctx->alt_dst = gf_filter_connect_destination(filter, out_path, &e);
793 2 : if (e) {
794 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't create secondary manifest output %s: %s\n", out_path, gf_error_to_string(e) ));
795 0 : gf_free(out_path);
796 0 : break;
797 : }
798 2 : gf_free(out_path);
799 :
800 : //reset any sourceID given in the dst_arg and assign sourceID to be the dasher filter
801 2 : gf_filter_reset_source(ctx->alt_dst);
802 2 : snprintf(szSRC, 100, "MuxSrc%cdasher_%p", gf_filter_get_sep(filter, GF_FS_SEP_NAME), ctx->alt_dst);
803 2 : gf_filter_set_source(ctx->alt_dst, filter, szSRC);
804 :
805 2 : ctx->opid_alt = gf_filter_pid_new(filter);
806 2 : gf_filter_pid_set_name(ctx->opid_alt, "MANIFEST_ALT");
807 :
808 2 : snprintf(szSRC, 100, "dasher_%p", ctx->alt_dst);
809 2 : gf_filter_pid_set_property(ctx->opid_alt, GF_PROP_PID_MUX_SRC, &PROP_STRING(szSRC) );
810 : //we also need to set the property on main output just to avoid the connection
811 : snprintf(szSRC, 100, "dasher_%p", ctx);
812 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MUX_SRC, &PROP_STRING(szSRC) );
813 : }
814 2 : opid = ctx->opid_alt;
815 : }
816 246 : if (!opid)
817 0 : continue;
818 :
819 : //copy properties at init or reconfig
820 246 : gf_filter_pid_copy_properties(opid, pid);
821 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_DECODER_CONFIG, NULL);
822 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT, NULL);
823 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_CODECID, NULL);
824 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_UNFRAMED, NULL);
825 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
826 : //for routeout
827 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
828 :
829 246 : dasher_check_outpath(ctx);
830 :
831 246 : p = gf_filter_pid_caps_query(pid, GF_PROP_PID_FILE_EXT);
832 246 : if (p) {
833 0 : gf_filter_pid_set_property(opid, GF_PROP_PID_FILE_EXT, p );
834 0 : segext = p->value.string;
835 : } else {
836 : segext = NULL;
837 246 : if (ctx->out_path) {
838 242 : segext = gf_file_ext_start(ctx->out_path);
839 4 : } else if (ctx->mname) {
840 0 : segext = gf_file_ext_start(ctx->mname);
841 : }
842 242 : if (!segext) segext = "mpd";
843 242 : else segext++;
844 246 : if (force_ext)
845 : segext = force_ext;
846 246 : gf_filter_pid_set_property(opid, GF_PROP_PID_FILE_EXT, &PROP_STRING(segext) );
847 :
848 246 : if (!strcmp(segext, "m3u8")) {
849 :
850 24 : gf_filter_pid_set_property(opid, GF_PROP_PID_MIME, &PROP_STRING("video/mpegurl"));
851 : } else {
852 222 : gf_filter_pid_set_property(opid, GF_PROP_PID_MIME, &PROP_STRING("application/dash+xml"));
853 : }
854 : }
855 :
856 246 : if (!strcmp(segext, "m3u8")) {
857 24 : ctx->do_m3u8 = GF_TRUE;
858 24 : gf_filter_pid_set_name(opid, "manifest_m3u8" );
859 : } else {
860 222 : ctx->do_mpd = GF_TRUE;
861 222 : gf_filter_pid_set_name(opid, "manifest_mpd" );
862 : }
863 : }
864 :
865 244 : ctx->store_seg_states = GF_FALSE;
866 : //in m3u8 mode, always store all seg states. In MPD only if state, not ondemand
867 244 : if (((ctx->state || ctx->purge_segments) && !ctx->sseg) || ctx->do_m3u8) ctx->store_seg_states = GF_TRUE;
868 : }
869 :
870 577 : ds = gf_filter_pid_get_udta(pid);
871 577 : if (!ds) {
872 357 : GF_SAFEALLOC(ds, GF_DashStream);
873 357 : if (!ds) return GF_OUT_OF_MEM;
874 357 : ds->ipid = pid;
875 357 : gf_list_add(ctx->pids, ds);
876 357 : ds->complementary_streams = gf_list_new();
877 : period_switch = GF_TRUE;
878 357 : gf_filter_pid_set_udta(pid, ds);
879 357 : ds->sbound = ctx->sbound;
880 357 : if (ctx->sbound!=DASHER_BOUNDS_OUT)
881 6 : ds->packet_queue = gf_list_new();
882 :
883 : /*initial connection and we already have sent play event, send a PLAY on this new PID
884 : TODO: we need to send STOP/PLAY depending on period
885 : */
886 357 : if (ctx->is_playing) {
887 : GF_FilterEvent evt;
888 :
889 2 : dasher_send_encode_hints(ctx, ds);
890 :
891 2 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
892 2 : evt.play.speed = 1.0;
893 2 : gf_filter_pid_send_event(ds->ipid, &evt);
894 : }
895 : //don't create pid at this time
896 : }
897 :
898 577 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
899 :
900 : #define CHECK_PROP(_type, _mem, _e) \
901 : p = gf_filter_pid_get_property(pid, _type); \
902 : if (!p && (_e<=0) ) return _e; \
903 : if (p && (p->value.uint != _mem) && _mem) period_switch = GF_TRUE; \
904 : if (p) _mem = p->value.uint; \
905 :
906 : #define CHECK_PROPL(_type, _mem, _e) \
907 : p = gf_filter_pid_get_property(pid, _type); \
908 : if (!p && (_e<=0) ) return _e; \
909 : if (p && (p->value.longuint != _mem) && _mem) period_switch = GF_TRUE; \
910 : if (p) _mem = p->value.longuint; \
911 :
912 : #define CHECK_PROP_BOOL(_type, _mem, _e) \
913 : p = gf_filter_pid_get_property(pid, _type); \
914 : if (!p && (_e<=0) ) return _e; \
915 : if (p && (p->value.boolean != _mem) && _mem) period_switch = GF_TRUE; \
916 : if (p) _mem = p->value.uint; \
917 :
918 : #define CHECK_PROP_FRAC(_type, _mem, _e) \
919 : p = gf_filter_pid_get_property(pid, _type); \
920 : if (!p && (_e<=0) ) return _e; \
921 : if (p && (p->value.frac.num * _mem.den != p->value.frac.den * _mem.num) && _mem.den && _mem.num) period_switch = GF_TRUE; \
922 : if (p) _mem = p->value.frac; \
923 :
924 : #define CHECK_PROP_FRAC64(_type, _mem, _e) \
925 : p = gf_filter_pid_get_property(pid, _type); \
926 : if (!p && (_e<=0) ) return _e; \
927 : if (p && (p->value.lfrac.num * _mem.den != p->value.lfrac.den * _mem.num) && _mem.den && _mem.num) period_switch = GF_TRUE; \
928 : if (p) _mem = p->value.lfrac; \
929 :
930 :
931 : #define CHECK_PROP_STR(_type, _mem, _e) \
932 : p = gf_filter_pid_get_property(pid, _type); \
933 : if (!p && (_e<=0) ) return _e; \
934 : if (p && _mem && strcmp(_mem, p->value.string)) period_switch = GF_TRUE; \
935 : if (p) { \
936 : if (_mem) gf_free(_mem); \
937 : _mem = gf_strdup(p->value.string); \
938 : }\
939 :
940 :
941 : #define CHECK_PROP_PROP(_type, _mem, _e) \
942 : p = gf_filter_pid_get_property(pid, _type); \
943 : if (!p && (_e<=0) ) return _e; \
944 : if (p != _mem) period_switch = GF_TRUE; \
945 : _mem = p; \
946 :
947 :
948 577 : prev_stream_type = ds->stream_type;
949 577 : CHECK_PROP(GF_PROP_PID_STREAM_TYPE, ds->stream_type, GF_NOT_SUPPORTED)
950 :
951 577 : ds->tile_base = GF_FALSE;
952 :
953 577 : if (ds->stream_type != GF_STREAM_FILE) {
954 576 : u32 prev_bitrate = ds->bitrate;
955 576 : if (ds->stream_type==GF_STREAM_ENCRYPTED) {
956 310 : CHECK_PROP(GF_PROP_PID_ORIG_STREAM_TYPE, ds->stream_type, GF_EOS)
957 310 : ds->is_encrypted = GF_TRUE;
958 : }
959 576 : if (prev_stream_type==ds->stream_type)
960 : period_switch = GF_FALSE;
961 :
962 576 : CHECK_PROP(GF_PROP_PID_BITRATE, ds->bitrate, GF_EOS)
963 576 : if (!ds->bitrate && prev_bitrate) {
964 0 : ds->bitrate = prev_bitrate;
965 : period_switch = GF_FALSE;
966 : }
967 576 : if (ds->bitrate && period_switch) {
968 : //allow 20% variation in bitrate, otherwise force period switch
969 298 : if ((ds->bitrate <= 120 * prev_bitrate / 100) && (ds->bitrate >= 80 * prev_bitrate / 100)) {
970 : period_switch = GF_FALSE;
971 : }
972 : }
973 :
974 576 : CHECK_PROP(GF_PROP_PID_CODECID, ds->codec_id, GF_NOT_SUPPORTED)
975 576 : CHECK_PROP(GF_PROP_PID_TIMESCALE, ds->timescale, GF_NOT_SUPPORTED)
976 :
977 576 : if (ds->stream_type==GF_STREAM_VISUAL) {
978 439 : CHECK_PROP(GF_PROP_PID_WIDTH, ds->width, GF_EOS)
979 439 : CHECK_PROP(GF_PROP_PID_HEIGHT, ds->height, GF_EOS)
980 : //don't return if not defined
981 439 : CHECK_PROP_FRAC(GF_PROP_PID_SAR, ds->sar, GF_EOS)
982 439 : if (!ds->sar.num) ds->sar.num = ds->sar.den = 1;
983 439 : CHECK_PROP_FRAC(GF_PROP_PID_FPS, ds->fps, GF_EOS)
984 :
985 :
986 439 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CROP_POS);
987 439 : if (p && ((p->value.vec2i.x != ds->srd.x) || (p->value.vec2i.y != ds->srd.y) ) ) period_switch = GF_TRUE;
988 439 : if (p) {
989 36 : ds->srd.x = p->value.vec2i.x;
990 36 : ds->srd.y = p->value.vec2i.y;
991 36 : ds->srd.z = ds->width;
992 36 : ds->srd.w = ds->height;
993 : } else {
994 403 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TILE_BASE);
995 403 : if (p) {
996 4 : ds->srd.x = ds->srd.y = 0;
997 4 : ds->srd.z = ds->width;
998 4 : ds->srd.w = ds->height;
999 4 : ds->tile_base = GF_TRUE;
1000 : }
1001 : }
1002 137 : } else if (ds->stream_type==GF_STREAM_AUDIO) {
1003 135 : CHECK_PROP(GF_PROP_PID_SAMPLE_RATE, ds->sr, GF_EOS)
1004 135 : CHECK_PROP(GF_PROP_PID_NUM_CHANNELS, ds->nb_ch, GF_EOS)
1005 135 : CHECK_PROPL(GF_PROP_PID_CHANNEL_LAYOUT, ds->ch_layout, GF_EOS)
1006 : }
1007 :
1008 : old_period_switch = period_switch;
1009 :
1010 : //these ones can change without triggering period switch
1011 576 : CHECK_PROP(GF_PROP_PID_NB_FRAMES, ds->nb_samples_in_source, GF_EOS)
1012 576 : CHECK_PROP_FRAC64(GF_PROP_PID_DURATION, ds->duration, GF_EOS)
1013 576 : CHECK_PROP_STR(GF_PROP_PID_URL, ds->src_url, GF_EOS)
1014 : period_switch = old_period_switch;
1015 :
1016 576 : CHECK_PROP(GF_PROP_PID_ID, ds->id, GF_EOS)
1017 576 : CHECK_PROP(GF_PROP_PID_DEPENDENCY_ID, ds->dep_id, GF_EOS)
1018 :
1019 576 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HAS_SYNC);
1020 : u32 sync_type = DASHER_SYNC_UNKNOWN;
1021 576 : if (p) sync_type = p->value.boolean ? DASHER_SYNC_PRESENT : DASHER_SYNC_NONE;
1022 576 : if (sync_type != ds->sync_points_type) period_switch = GF_TRUE;
1023 576 : ds->sync_points_type = sync_type;
1024 :
1025 576 : if (ds->inband_cues)
1026 : period_switch = old_period_switch;
1027 :
1028 576 : if (ctx->scope_deps) {
1029 576 : const char *src_args = gf_filter_pid_orig_src_args(pid, GF_TRUE);
1030 576 : if (src_args) {
1031 576 : ds->src_id = gf_crc_32(src_args, (u32) strlen(src_args));
1032 : }
1033 : }
1034 : dc_crc = 0;
1035 576 : dsi = p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
1036 576 : if (p && (p->type==GF_PROP_DATA))
1037 526 : dc_crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
1038 :
1039 : dc_enh_crc = 0;
1040 576 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
1041 576 : if (p && (p->type==GF_PROP_DATA)) dc_enh_crc = gf_crc_32(p->value.data.ptr, p->value.data.size);
1042 :
1043 576 : if (((dc_crc != ds->dsi_crc) && ds->dsi_crc)
1044 569 : || ((dc_enh_crc != ds->dsi_enh_crc) && ds->dsi_enh_crc)
1045 : ) {
1046 : //check which codecs can support inband param sets
1047 11 : switch (ds->codec_id) {
1048 11 : case GF_CODECID_AVC:
1049 : case GF_CODECID_SVC:
1050 : case GF_CODECID_MVC:
1051 : case GF_CODECID_HEVC:
1052 : case GF_CODECID_LHVC:
1053 11 : if (!ctx->bs_switch)
1054 : period_switch = GF_TRUE;
1055 : break;
1056 : default:
1057 : period_switch = GF_TRUE;
1058 : break;
1059 : }
1060 565 : }
1061 : //check if input is ready
1062 576 : ds->dcd_not_ready = 0;
1063 576 : if (!dc_crc && !dc_enh_crc) {
1064 48 : switch (ds->codec_id) {
1065 15 : case GF_CODECID_AVC:
1066 : case GF_CODECID_SVC:
1067 : case GF_CODECID_MVC:
1068 : case GF_CODECID_HEVC:
1069 : case GF_CODECID_LHVC:
1070 : case GF_CODECID_AAC_MPEG4:
1071 : case GF_CODECID_AAC_MPEG2_MP:
1072 : case GF_CODECID_AAC_MPEG2_LCP:
1073 : case GF_CODECID_AAC_MPEG2_SSRP:
1074 : case GF_CODECID_USAC:
1075 : case GF_CODECID_AC3:
1076 : case GF_CODECID_EAC3:
1077 : case GF_CODECID_AV1:
1078 : case GF_CODECID_VP8:
1079 : case GF_CODECID_VP9:
1080 15 : ds->dcd_not_ready = gf_sys_clock();
1081 15 : break;
1082 : default:
1083 : break;
1084 : }
1085 528 : }
1086 576 : ds->dsi_crc = dc_crc;
1087 :
1088 576 : CHECK_PROP_STR(GF_PROP_PID_TEMPLATE, ds->template, GF_EOS)
1089 576 : CHECK_PROP_STR(GF_PROP_PID_LANGUAGE, ds->lang, GF_EOS)
1090 576 : CHECK_PROP_BOOL(GF_PROP_PID_INTERLACED, ds->interlaced, GF_EOS)
1091 576 : CHECK_PROP_PROP(GF_PROP_PID_AS_COND_DESC, ds->p_as_desc, GF_EOS)
1092 576 : CHECK_PROP_PROP(GF_PROP_PID_AS_ANY_DESC, ds->p_as_any_desc, GF_EOS)
1093 576 : CHECK_PROP_PROP(GF_PROP_PID_REP_DESC, ds->p_rep_desc, GF_EOS)
1094 576 : CHECK_PROP_PROP(GF_PROP_PID_BASE_URL, ds->p_base_url, GF_EOS)
1095 576 : CHECK_PROP_PROP(GF_PROP_PID_ROLE, ds->p_role, GF_EOS)
1096 576 : CHECK_PROP_STR(GF_PROP_PID_HLS_PLAYLIST, ds->hls_vp_name, GF_EOS)
1097 576 : CHECK_PROP_BOOL(GF_PROP_PID_SINGLE_SCALE, ds->sscale, GF_EOS)
1098 :
1099 :
1100 576 : if (ds->rate_first_dts_plus_one)
1101 0 : dasher_update_bitrate(ctx, ds);
1102 :
1103 576 : if (!ds->bitrate) {
1104 65 : char *tpl = ds->template ? ds->template : ctx->template;
1105 65 : if (tpl && strstr(tpl, "$Bandwidth$")) {
1106 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] No bitrate property assigned to PID %s but template uses $Bandwidth$, cannot initialize !\n\tTry specifying bitrate property after your source, e.g. -i source.raw:#Bitrate=VAL\n", gf_filter_pid_get_name(ds->ipid)));
1107 0 : ctx->in_error = GF_TRUE;
1108 0 : return GF_BAD_PARAM;
1109 : } else {
1110 65 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] No bitrate property assigned to PID %s, computing from bitstream\n", gf_filter_pid_get_name(ds->ipid)));
1111 65 : ds->dyn_bitrate = GF_TRUE;
1112 65 : ds->rate_first_dts_plus_one = 0;
1113 65 : ds->rate_media_size = 0;
1114 : }
1115 : } else {
1116 511 : ds->dyn_bitrate = GF_FALSE;
1117 : }
1118 :
1119 576 : if (!ds->src_url)
1120 0 : ds->src_url = gf_strdup("file");
1121 576 : ds->startNumber = 1;
1122 576 : CHECK_PROP(GF_PROP_PID_START_NUMBER, ds->startNumber, GF_EOS)
1123 576 : ds->dash_dur = ctx->segdur;
1124 576 : ds->no_seg_dur = ctx->no_seg_dur;
1125 576 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_DUR);
1126 576 : if (p) {
1127 45 : ds->dash_dur = p->value.frac;
1128 45 : ds->no_seg_dur = GF_FALSE;
1129 45 : if (!ds->dash_dur.num || !ds->dash_dur.den) {
1130 0 : ds->dash_dur.num = 1;
1131 0 : ds->dash_dur.den = 1;
1132 : }
1133 : }
1134 : //this avoids very weird cases where (u64) (dash_dur*timescale) is 0. we limit the max segment duration to 1M sec, a bit more than 11.5 days
1135 576 : if ((u64) ds->dash_dur.num > (u64)ds->dash_dur.den * 1000000) {
1136 0 : ds->dash_dur.num = 1000000;
1137 0 : ds->dash_dur.den = 1;
1138 : }
1139 :
1140 576 : ds->splitable = GF_FALSE;
1141 576 : switch (ds->stream_type) {
1142 2 : case GF_STREAM_TEXT:
1143 : case GF_STREAM_METADATA:
1144 : case GF_STREAM_OD:
1145 : case GF_STREAM_SCENE:
1146 2 : ds->splitable = ctx->split;
1147 2 : break;
1148 : }
1149 :
1150 576 : old_clamp_dur = ds->clamped_dur.num;
1151 576 : ds->clamped_dur.num = 0;
1152 576 : ds->clamped_dur.den = 1;
1153 576 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CLAMP_DUR);
1154 576 : if (p && p->value.lfrac.den) ds->clamped_dur = p->value.lfrac;
1155 :
1156 : //HDR
1157 : #if !defined(GPAC_DISABLE_AV_PARSERS)
1158 576 : if (dsi) {
1159 : #if !defined(GPAC_DISABLE_HEVC)
1160 526 : if (ds->codec_id == GF_CODECID_LHVC || ds->codec_id == GF_CODECID_HEVC_TILES || ds->codec_id == GF_CODECID_HEVC) {
1161 86 : GF_HEVCConfig* hevccfg = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, GF_FALSE);
1162 86 : if (hevccfg) {
1163 : Bool is_interlaced;
1164 : HEVCState hevc;
1165 : HEVC_SPS* sps;
1166 : memset(&hevc, 0, sizeof(HEVCState));
1167 86 : gf_hevc_parse_ps(hevccfg, &hevc, GF_HEVC_NALU_VID_PARAM);
1168 86 : gf_hevc_parse_ps(hevccfg, &hevc, GF_HEVC_NALU_SEQ_PARAM);
1169 86 : sps = &hevc.sps[hevc.sps_active_idx];
1170 86 : if (sps && sps->colour_description_present_flag) {
1171 0 : DasherHDRType old_hdr_type = ds->hdr_type;
1172 0 : if (sps->colour_primaries == 9 && sps->matrix_coeffs == 9) {
1173 0 : if (sps->transfer_characteristic == 14) ds->hdr_type = DASHER_HDR_HLG; //TODO: parse alternative_transfer_characteristics SEI
1174 0 : if (sps->transfer_characteristic == 16) ds->hdr_type = DASHER_HDR_PQ10;
1175 : }
1176 0 : if (old_hdr_type != ds->hdr_type) period_switch = GF_TRUE;
1177 : }
1178 86 : is_interlaced = hevccfg->interlaced_source_flag ? GF_TRUE : GF_FALSE;
1179 86 : if (ds->interlaced != is_interlaced) period_switch = GF_TRUE;
1180 86 : ds->interlaced = is_interlaced;
1181 :
1182 86 : gf_odf_hevc_cfg_del(hevccfg);
1183 : }
1184 : }
1185 : else
1186 : #endif
1187 440 : if (ds->codec_id == GF_CODECID_AVC || ds->codec_id == GF_CODECID_SVC || ds->codec_id == GF_CODECID_MVC) {
1188 : AVCState avc;
1189 213 : GF_AVCConfig* avccfg = gf_odf_avc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
1190 213 : GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(avccfg->sequenceParameterSets, 0);
1191 213 : if (sl) {
1192 : s32 idx;
1193 : memset(&avc, 0, sizeof(AVCState));
1194 211 : idx = gf_avc_read_sps(sl->data, sl->size, &avc, 0, NULL);
1195 211 : if (idx>=0) {
1196 211 : Bool is_interlaced = avc.sps[idx].frame_mbs_only_flag ? GF_FALSE : GF_TRUE;
1197 211 : if (ds->interlaced != is_interlaced) period_switch = GF_TRUE;
1198 211 : ds->interlaced = is_interlaced;
1199 : }
1200 : }
1201 213 : gf_odf_avc_cfg_del(avccfg);
1202 : }
1203 : }
1204 : #endif /*!GPAC_DISABLE_AV_PARSERS*/
1205 :
1206 576 : if (ds->stream_type==GF_STREAM_AUDIO) {
1207 135 : u32 _sr=0, _nb_ch=0;
1208 : #ifndef GPAC_DISABLE_AV_PARSERS
1209 135 : switch (ds->codec_id) {
1210 130 : case GF_CODECID_AAC_MPEG4:
1211 : case GF_CODECID_AAC_MPEG2_MP:
1212 : case GF_CODECID_AAC_MPEG2_LCP:
1213 : case GF_CODECID_AAC_MPEG2_SSRP:
1214 : case GF_CODECID_USAC:
1215 : //DASH-IF and MPEG disagree here:
1216 260 : if ((ctx->profile == GF_DASH_PROFILE_AVC264_LIVE)
1217 : || (ctx->profile == GF_DASH_PROFILE_AVC264_ONDEMAND)
1218 130 : || (ctx->profile == GF_DASH_PROFILE_DASHIF_LL)
1219 : ) {
1220 5 : GF_Err res = dasher_get_audio_info_with_m4a_sbr_ps(ds, dsi, &_sr, &_nb_ch);
1221 5 : if (res) {
1222 : //DASH-IF IOP 3.3 mandates the SBR/PS info
1223 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Could not get AAC info, %s\n", gf_error_to_string(res)));
1224 : }
1225 125 : } else if (dsi) {
1226 121 : dasher_get_audio_info_with_m4a_sbr_ps(ds, dsi, NULL, &_nb_ch);
1227 : }
1228 : break;
1229 1 : case GF_CODECID_AC3:
1230 : case GF_CODECID_EAC3:
1231 1 : if (dsi) {
1232 : u32 i;
1233 : GF_AC3Config ac3;
1234 0 : gf_odf_ac3_config_parse(dsi->value.data.ptr, dsi->value.data.size, (ds->codec_id==GF_CODECID_EAC3) ? GF_TRUE : GF_FALSE, &ac3);
1235 :
1236 0 : ds->nb_lfe = ac3.streams[0].lfon ? 1 : 0;
1237 0 : _nb_ch = gf_ac3_get_channels(ac3.streams[0].acmod);
1238 0 : for (i=0; i<ac3.streams[0].nb_dep_sub; ++i) {
1239 : assert(ac3.streams[0].nb_dep_sub == 1);
1240 0 : _nb_ch += gf_ac3_get_channels(ac3.streams[0].chan_loc);
1241 : }
1242 : }
1243 : break;
1244 : }
1245 : #endif
1246 135 : if (_sr > ds->sr) ds->sr = _sr;
1247 135 : if (_nb_ch > ds->nb_ch) ds->nb_ch = _nb_ch;
1248 : }
1249 :
1250 :
1251 576 : ds->pts_minus_cts = 0;
1252 576 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DELAY);
1253 576 : if (p && p->value.longsint) {
1254 78 : ds->pts_minus_cts = p->value.longsint;
1255 : }
1256 :
1257 : //only reload queues if we detected a period switch
1258 576 : if (period_switch) {
1259 361 : cue_file = ctx->cues;
1260 361 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_CUE);
1261 361 : if (p) cue_file = p->value.string;
1262 :
1263 361 : if (ds->cues) gf_free(ds->cues);
1264 361 : ds->cues = NULL;
1265 361 : ds->nb_cues = 0;
1266 361 : ds->inband_cues = GF_FALSE;
1267 361 : if (cue_file) {
1268 37 : if (!strcmp(cue_file, "inband")) {
1269 27 : ds->inband_cues = GF_TRUE;
1270 27 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_FWD);
1271 27 : if (p && p->value.uint)
1272 27 : ctx->forward_mode = p->value.uint;
1273 : } else {
1274 10 : e = gf_mpd_load_cues(cue_file, ds->id, &ds->cues_timescale, &ds->cues_use_edits, &ds->cues_ts_offset, &ds->cues, &ds->nb_cues);
1275 10 : if (e) return e;
1276 10 : if (!ds->cues_timescale)
1277 0 : ds->cues_timescale = ds->timescale;
1278 : }
1279 : }
1280 : }
1281 : } else {
1282 :
1283 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
1284 1 : if (!p) p = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
1285 1 : if (p) return GF_NOT_SUPPORTED;
1286 :
1287 1 : CHECK_PROP_STR(GF_PROP_PID_XLINK, ds->xlink, GF_EOS)
1288 : }
1289 :
1290 : //stream representation was not yet setup but is scheduled for this period, do not trigger period switch
1291 : //this typically happens when we post-poned representation setup waiting for the decoder config
1292 577 : if (!ds->rep && (gf_list_find(ctx->current_period->streams, ds)>=0))
1293 : period_switch = GF_FALSE;
1294 :
1295 : old_period_switch = period_switch;
1296 : period_switch = GF_FALSE;
1297 577 : CHECK_PROP_STR(GF_PROP_PID_PERIOD_ID, ds->period_id, GF_EOS)
1298 577 : CHECK_PROP_PROP(GF_PROP_PID_PERIOD_DESC, ds->p_period_desc, GF_EOS)
1299 574 : if (!period_switch && ctx->pswitch)
1300 : period_switch = GF_TRUE;
1301 :
1302 577 : if (gf_filter_pid_get_property_str(pid, "period_switch"))
1303 : period_switch = GF_TRUE;
1304 :
1305 577 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PERIOD_START);
1306 577 : if (p) {
1307 3 : if (ds->period_start.num * p->value.lfrac.den != p->value.lfrac.num * ds->period_start.den) period_switch = GF_TRUE;
1308 3 : ds->period_start = p->value.lfrac;
1309 : } else {
1310 574 : if (ds->period_start.num) period_switch = GF_TRUE;
1311 574 : ds->period_start.num = 0;
1312 574 : ds->period_start.den = 1;
1313 : }
1314 : assert(ds->period_start.den);
1315 :
1316 577 : if (period_switch) {
1317 : new_period_request = GF_TRUE;
1318 : } else {
1319 : period_switch = old_period_switch;
1320 : }
1321 :
1322 577 : if (ds->period_continuity_id) gf_free(ds->period_continuity_id);
1323 577 : ds->period_continuity_id = NULL;
1324 577 : p = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
1325 577 : if (!ctx->mpd || (gf_list_find(ctx->mpd->periods, ds->last_period)<0))
1326 567 : ds->last_period = NULL;
1327 :
1328 577 : if (p && p->value.string && ds->last_period) {
1329 0 : if (!ds->last_period->ID) {
1330 0 : if (p->value.string[0]) {
1331 0 : ds->last_period->ID = p->value.string;
1332 : } else {
1333 : char szPName[50];
1334 0 : sprintf(szPName, "P%d", 1 + gf_list_find(ctx->mpd->periods, ds->last_period));
1335 0 : ds->last_period->ID = gf_strdup(szPName);
1336 : }
1337 : }
1338 0 : if (ds->set && (ds->set->id<0)) {
1339 : //period may be NULL (no longer scheduled)
1340 0 : if (!ds->as_id && ds->period && ds->period->period)
1341 0 : ds->as_id = gf_list_find(ds->period->period->adaptation_sets, ds->set) + 1;
1342 0 : ds->set->id = ds->as_id;
1343 : }
1344 0 : ds->period_continuity_id = gf_strdup(ds->last_period->ID);
1345 : }
1346 577 : ds->last_period = NULL;
1347 :
1348 577 : ds->period_dur.num = 0;
1349 577 : ds->period_dur.den = 1;
1350 577 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PERIOD_DUR);
1351 577 : if (p) ds->period_dur = p->value.lfrac;
1352 :
1353 577 : if (ds->stream_type==GF_STREAM_FILE) {
1354 1 : if (!ds->xlink && !ds->period_start.num && !ds->period_dur.num) {
1355 0 : ds->done = 1;
1356 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] null PID specified without any XLINK/start/duration, ignoring\n"));
1357 1 : } else if (ds->xlink) {
1358 1 : ctx->use_xlink = GF_TRUE;
1359 : }
1360 : } else {
1361 576 : if (ds->xlink) gf_free(ds->xlink);
1362 576 : ds->xlink = NULL;
1363 576 : CHECK_PROP_STR(GF_PROP_PID_XLINK, ds->xlink, GF_EOS)
1364 576 : if (ds->xlink)
1365 0 : ctx->use_xlink = GF_TRUE;
1366 : }
1367 :
1368 : //input was done due to clamp but forced to new period, reschedule
1369 577 : if (new_period_request && ds->done && old_clamp_dur) {
1370 0 : gf_list_del_item(ctx->next_period->streams, ds);
1371 : //reset discard, blocking mode on output (set by EOS) and reset dasher EOS state
1372 0 : gf_filter_pid_set_discard(ds->ipid, GF_FALSE);
1373 0 : if (ds->opid) {
1374 0 : gf_filter_pid_discard_block(ds->opid);
1375 0 : gf_filter_pid_remove(ds->opid);
1376 0 : ds->opid = NULL;
1377 : }
1378 0 : if (ctx->is_eos) {
1379 0 : ctx->is_eos = GF_FALSE;
1380 0 : gf_filter_pid_discard_block(ctx->opid);
1381 0 : if (ctx->opid_alt)
1382 0 : gf_filter_pid_discard_block(ctx->opid_alt);
1383 : }
1384 0 : ds->rep_init = GF_FALSE;
1385 0 : ds->rep = NULL;
1386 0 : ds->set = NULL;
1387 0 : ds->period = NULL;
1388 0 : ds->done = 0;
1389 : // gf_filter_post_process_task(filter);
1390 : // dasher_reset_stream(filter, ds, GF_FALSE);
1391 : }
1392 :
1393 : //our stream is already scheduled for next period, don't do anything
1394 577 : if (gf_list_find(ctx->next_period->streams, ds)>=0)
1395 : period_switch = GF_FALSE;
1396 :
1397 : //assign default ID
1398 577 : if (!ds->period_id)
1399 350 : ds->period_id = gf_strdup(DEFAULT_PERIOD_ID);
1400 :
1401 577 : e = dasher_hls_setup_crypto(ctx, ds);
1402 577 : if (e) return e;
1403 :
1404 577 : if (!period_switch) {
1405 220 : if (ds->opid) {
1406 181 : gf_filter_pid_copy_properties(ds->opid, pid);
1407 : //for route out
1408 181 : if (ctx->is_route && ctx->do_m3u8)
1409 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
1410 181 : if (ctx->llhls)
1411 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_LLHLS, &PROP_UINT(ctx->llhls) );
1412 : }
1413 220 : if (ds->rep)
1414 181 : dasher_update_rep(ctx, ds);
1415 : return GF_OK;
1416 : }
1417 : //period switch !
1418 :
1419 : //we have queued packets (sbound modes), we cannot switch period for this stream now, force queue flush
1420 357 : if (gf_list_count(ds->packet_queue)) {
1421 0 : ds->request_period_switch = new_period_request ? 2 : 1;
1422 0 : return GF_OK;
1423 : }
1424 : //done for this stream
1425 357 : return dasher_stream_period_changed(filter, ctx, ds, new_period_request);
1426 : }
1427 :
1428 :
1429 876 : static GF_Err dasher_update_mpd(GF_DasherCtx *ctx)
1430 : {
1431 : char profiles_string[GF_MAX_PATH];
1432 : GF_XMLAttribute *cenc_att = NULL;
1433 : GF_XMLAttribute *xlink_att = NULL;
1434 :
1435 876 : u32 i, count=gf_list_count(ctx->mpd->x_attributes);
1436 921 : for (i=0; i<count; i++) {
1437 45 : GF_XMLAttribute * att = gf_list_get(ctx->mpd->x_attributes, i);
1438 45 : if (!strcmp(att->name, "xmlns:cenc")) cenc_att = att;
1439 45 : if (!strcmp(att->name, "xmlns:xlink")) xlink_att = att;
1440 :
1441 : }
1442 876 : if (ctx->dmode==GF_MPD_TYPE_DYNAMIC) {
1443 433 : ctx->mpd->type = GF_MPD_TYPE_DYNAMIC;
1444 : } else {
1445 443 : ctx->mpd->type = GF_MPD_TYPE_STATIC;
1446 443 : ctx->mpd->availabilityStartTime = 0;
1447 : }
1448 :
1449 876 : if (ctx->profile==GF_DASH_PROFILE_LIVE) {
1450 328 : if (ctx->use_xlink && !ctx->m2ts) {
1451 : strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-segext-live:2014");
1452 : } else {
1453 324 : sprintf(profiles_string, "urn:mpeg:dash:profile:%s:2011", ctx->m2ts ? "mp2t-simple" : "isoff-live");
1454 : }
1455 548 : } else if (ctx->profile==GF_DASH_PROFILE_ONDEMAND) {
1456 30 : if (ctx->use_xlink) {
1457 : strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-segext-on-demand:2014");
1458 : } else {
1459 : strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-on-demand:2011");
1460 : }
1461 518 : } else if (ctx->profile==GF_DASH_PROFILE_MAIN) {
1462 22 : sprintf(profiles_string, "urn:mpeg:dash:profile:%s:2011", ctx->m2ts ? "mp2t-main" : "isoff-main");
1463 496 : } else if (ctx->profile==GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
1464 : strcpy(profiles_string, "urn:hbbtv:dash:profile:isoff-live:2012");
1465 496 : } else if (ctx->profile==GF_DASH_PROFILE_AVC264_LIVE) {
1466 : strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash264");
1467 490 : } else if (ctx->profile==GF_DASH_PROFILE_AVC264_ONDEMAND) {
1468 : strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-on-demand:2011,http://dashif.org/guidelines/dash264");
1469 486 : } else if (ctx->profile==GF_DASH_PROFILE_DASHIF_LL) {
1470 : strcpy(profiles_string, "urn:mpeg:dash:profile:isoff-live:2011,http://www.dashif.org/guidelines/low-latency-live-v5");
1471 : } else {
1472 : strcpy(profiles_string, "urn:mpeg:dash:profile:full:2011");
1473 : }
1474 :
1475 876 : if (ctx->cmaf) {
1476 0 : const size_t offset = strlen(profiles_string);
1477 0 : strncat(profiles_string+offset, ",urn:mpeg:dash:profile:cmaf:2019", GF_MAX_PATH-offset-1);
1478 : }
1479 :
1480 876 : if (ctx->profX) {
1481 : char profiles_w_ext[GF_MAX_PATH+256];
1482 : sprintf(profiles_w_ext, "%s,%s", profiles_string, ctx->profX);
1483 0 : if (ctx->mpd->profiles) gf_free(ctx->mpd->profiles);
1484 0 : ctx->mpd->profiles = gf_strdup(profiles_w_ext);
1485 : } else {
1486 876 : if (ctx->mpd->profiles) gf_free(ctx->mpd->profiles);
1487 876 : ctx->mpd->profiles = gf_strdup(profiles_string);
1488 : }
1489 :
1490 876 : if (ctx->use_cenc && !cenc_att) {
1491 88 : cenc_att = gf_xml_dom_create_attribute("xmlns:cenc", "urn:mpeg:cenc:2013");
1492 88 : gf_list_add(ctx->mpd->x_attributes, cenc_att);
1493 : }
1494 876 : if (ctx->use_xlink && !xlink_att) {
1495 1 : xlink_att = gf_xml_dom_create_attribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
1496 1 : gf_list_add(ctx->mpd->x_attributes, xlink_att);
1497 : }
1498 :
1499 876 : ctx->mpd->time_shift_buffer_depth = 0;
1500 876 : ctx->mpd->minimum_update_period = 0;
1501 :
1502 876 : if (ctx->dmode==GF_MPD_TYPE_DYNAMIC) {
1503 433 : ctx->mpd->time_shift_buffer_depth = (u32) -1;
1504 433 : if (ctx->tsb>=0) ctx->mpd->time_shift_buffer_depth = (u32) (1000*ctx->tsb);
1505 :
1506 433 : if (ctx->refresh>=0) {
1507 433 : if (ctx->refresh) {
1508 39 : ctx->mpd->minimum_update_period = (u32) (1000*ctx->refresh);
1509 : } else {
1510 394 : ctx->mpd->minimum_update_period = ctx->segdur.num * 1000;
1511 394 : ctx->mpd->minimum_update_period /= ctx->segdur.den;
1512 : }
1513 : } else {
1514 0 : ctx->mpd->minimum_update_period = 0;
1515 : }
1516 : }
1517 876 : return GF_OK;
1518 : }
1519 241 : static GF_Err dasher_setup_mpd(GF_DasherCtx *ctx)
1520 : {
1521 : u32 i, count;
1522 : GF_MPD_ProgramInfo *info;
1523 241 : ctx->mpd = gf_mpd_new();
1524 241 : ctx->mpd->xml_namespace = "urn:mpeg:dash:schema:mpd:2011";
1525 241 : ctx->mpd->base_URLs = gf_list_new();
1526 241 : ctx->mpd->locations = gf_list_new();
1527 241 : ctx->mpd->program_infos = gf_list_new();
1528 241 : ctx->mpd->periods = gf_list_new();
1529 : //created by default because we store xmlns in it
1530 241 : ctx->mpd->x_attributes = gf_list_new();
1531 241 : if (ctx->buf<0) {
1532 68 : s32 buf = -ctx->buf;
1533 68 : ctx->mpd->min_buffer_time = (u32) ( ctx->segdur.num * 10 * buf / ctx->segdur.den); //*1000 (ms) / 100 (percent)
1534 : } else
1535 173 : ctx->mpd->min_buffer_time = ctx->buf;
1536 :
1537 241 : GF_SAFEALLOC(info, GF_MPD_ProgramInfo);
1538 241 : if (info) {
1539 241 : gf_list_add(ctx->mpd->program_infos, info);
1540 241 : if (ctx->title)
1541 0 : info->title = gf_strdup(ctx->title);
1542 : else {
1543 : char tmp[256];
1544 : const char *name = NULL;
1545 241 : if (ctx->out_path) {
1546 : const char *url = ctx->out_path;
1547 241 : if (!strncmp(ctx->out_path, "gfio://", 7)) {
1548 1 : url = gf_fileio_translate_url(ctx->out_path);
1549 1 : if (!url) url = "";
1550 : }
1551 241 : name = strrchr(url, '/');
1552 241 : if (!name) name = strrchr(url, '\\');
1553 241 : if (!name) name = url;
1554 239 : else name++;
1555 : }
1556 241 : snprintf(tmp, 255, "%s generated by GPAC", name ? name : "");
1557 241 : tmp[255]=0;
1558 241 : info->title = gf_strdup(tmp);
1559 : }
1560 241 : if (ctx->cprt) info->copyright = gf_strdup(ctx->cprt);
1561 241 : if (ctx->info) info->more_info_url = gf_strdup(ctx->info);
1562 241 : else info->more_info_url = gf_strdup("http://gpac.io");
1563 241 : if (ctx->source) info->source = gf_strdup(ctx->source);
1564 241 : if (ctx->lang) info->lang = gf_strdup(ctx->lang);
1565 : }
1566 :
1567 241 : count = ctx->location.nb_items;
1568 241 : for (i=0; i<count; i++) {
1569 0 : char *l = ctx->location.vals[i];
1570 0 : gf_list_add(ctx->mpd->locations, gf_strdup(l));
1571 : }
1572 241 : count = ctx->base.nb_items;
1573 242 : for (i=0; i<count; i++) {
1574 : GF_MPD_BaseURL *base;
1575 1 : char *b = ctx->base.vals[i];
1576 1 : GF_SAFEALLOC(base, GF_MPD_BaseURL);
1577 1 : if (base) {
1578 1 : base->URL = gf_strdup(b);
1579 1 : gf_list_add(ctx->mpd->base_URLs, base);
1580 : }
1581 : }
1582 241 : return dasher_update_mpd(ctx);
1583 : }
1584 :
1585 :
1586 : GF_Err rfc_6381_get_codec_aac(char *szCodec, u32 codec_id, u8 *dsi, u32 dsi_size, Bool force_sbr);
1587 : GF_Err rfc_6381_get_codec_m4v(char *szCodec, u32 codec_id, u8 *dsi, u32 dsi_size);
1588 : GF_Err rfc_6381_get_codec_avc(char *szCodec, u32 subtype, GF_AVCConfig *avcc);
1589 : GF_Err rfc_6381_get_codec_hevc(char *szCodec, u32 subtype, GF_HEVCConfig *hvcc);
1590 : GF_Err rfc_6381_get_codec_av1(char *szCodec, u32 subtype, GF_AV1Config *av1c);
1591 : GF_Err rfc_6381_get_codec_vpx(char *szCodec, u32 subtype, GF_VPConfig *vpcc);
1592 : GF_Err rfc_6381_get_codec_dolby_vision(char *szCodec, u32 subtype, GF_DOVIDecoderConfigurationRecord *dovi);
1593 : GF_Err rfc_6381_get_codec_vvc(char *szCodec, u32 subtype, GF_VVCConfig *vvcc);
1594 : GF_Err rfc_6381_get_codec_mpegha(char *szCodec, u32 subtype, u8 *dsi, u32 dsi_size, s32 pl);
1595 : GF_Err rfc6381_codec_name_default(char *szCodec, u32 subtype, u32 codec_id);
1596 :
1597 :
1598 538 : static GF_Err dasher_get_rfc_6381_codec_name(GF_DasherCtx *ctx, GF_DashStream *ds, char *szCodec, Bool force_inband, Bool force_sbr)
1599 : {
1600 : u32 subtype=0, subtype_src=0;
1601 : s32 mha_pl=-1;
1602 : const GF_PropertyValue *dcd, *dcd_enh, *dovi, *codec;
1603 :
1604 538 : dcd = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ISOM_SUBTYPE);
1605 538 : if (dcd) subtype_src = dcd->value.uint;
1606 :
1607 538 : dcd = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DECODER_CONFIG);
1608 538 : dcd_enh = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
1609 :
1610 538 : if (!force_inband) {
1611 538 : force_inband = ds->inband_params;
1612 : }
1613 538 : if (!force_inband) {
1614 530 : const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ISOM_SUBTYPE);
1615 530 : if (p) {
1616 : //input uses inband parameters, force it on output regardless of bitstream switching mode
1617 427 : switch (p->value.uint) {
1618 3 : case GF_ISOM_SUBTYPE_AVC3_H264:
1619 : case GF_ISOM_SUBTYPE_AVC4_H264:
1620 : case GF_ISOM_SUBTYPE_LHE1:
1621 : case GF_ISOM_SUBTYPE_HEV1:
1622 : force_inband = GF_TRUE;
1623 3 : ds->inband_params = GF_TRUE;
1624 : break;
1625 : }
1626 : }
1627 : }
1628 :
1629 538 : codec = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_CODEC);
1630 538 : if (codec && (codec->type==GF_PROP_STRING) && codec->value.string) {
1631 : const char *codec_str = codec->value.string;
1632 24 : if (codec_str[0] != '.') {
1633 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", codec_str);
1634 : return GF_OK;
1635 : }
1636 0 : if (!subtype_src)
1637 0 : subtype_src = gf_codecid_4cc_type(ds->codec_id);
1638 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s%s", gf_4cc_to_str(subtype_src), codec_str);
1639 : return GF_OK;
1640 : }
1641 :
1642 514 : dovi = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DOLBY_VISION);
1643 514 : if (dovi) {
1644 1 : GF_BitStream *bs = gf_bs_new(dovi->value.data.ptr, dovi->value.data.size, GF_BITSTREAM_READ);
1645 1 : GF_DOVIDecoderConfigurationRecord *dvcc = gf_odf_dovi_cfg_read_bs(bs);
1646 1 : gf_bs_del(bs);
1647 1 : if (!dvcc) {
1648 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[ISOM Tools] No config found for Dolby Vision file (\"%s\") when computing RFC6381.\n", gf_4cc_to_str(subtype)));
1649 : return GF_BAD_PARAM;
1650 : }
1651 1 : GF_Err e = rfc_6381_get_codec_dolby_vision(szCodec, GF_ISOM_SUBTYPE_DVHE, dvcc);
1652 1 : gf_odf_dovi_cfg_del(dvcc);
1653 : return e;
1654 : }
1655 :
1656 513 : switch (ds->codec_id) {
1657 121 : case GF_CODECID_AAC_MPEG4:
1658 : case GF_CODECID_AAC_MPEG2_MP:
1659 : case GF_CODECID_AAC_MPEG2_LCP:
1660 : case GF_CODECID_AAC_MPEG2_SSRP:
1661 : case GF_CODECID_USAC:
1662 121 : return rfc_6381_get_codec_aac(szCodec, ds->codec_id, dcd ? dcd->value.data.ptr : NULL, dcd ? dcd->value.data.size : 0, force_sbr);
1663 :
1664 2 : case GF_CODECID_MPEG4_PART2:
1665 2 : return rfc_6381_get_codec_m4v(szCodec, ds->codec_id, dcd ? dcd->value.data.ptr : NULL, dcd ? dcd->value.data.size : 0);
1666 : break;
1667 2 : case GF_CODECID_SVC:
1668 : case GF_CODECID_MVC:
1669 2 : if (dcd_enh) dcd = dcd_enh;
1670 2 : subtype = (ds->codec_id==GF_CODECID_SVC) ? GF_ISOM_SUBTYPE_SVC_H264 : GF_ISOM_SUBTYPE_MVC_H264;
1671 : case GF_CODECID_AVC:
1672 : if (!subtype) {
1673 172 : if (force_inband) {
1674 7 : subtype = dcd_enh ? GF_ISOM_SUBTYPE_AVC4_H264 : GF_ISOM_SUBTYPE_AVC3_H264;
1675 : } else {
1676 165 : subtype = dcd_enh ? GF_ISOM_SUBTYPE_AVC2_H264 : GF_ISOM_SUBTYPE_AVC_H264;
1677 : }
1678 : }
1679 174 : if (dcd) {
1680 174 : GF_AVCConfig *avcc = gf_odf_avc_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
1681 174 : if (avcc) {
1682 174 : GF_Err e = rfc_6381_get_codec_avc(szCodec, subtype, avcc);
1683 174 : gf_odf_avc_cfg_del(avcc);
1684 : return e;
1685 : }
1686 : }
1687 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
1688 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find AVC config, using default %s\n", szCodec));
1689 : return GF_OK;
1690 :
1691 : #ifndef GPAC_DISABLE_HEVC
1692 0 : case GF_CODECID_LHVC:
1693 0 : subtype = force_inband ? GF_ISOM_SUBTYPE_LHE1 : GF_ISOM_SUBTYPE_LHV1;
1694 : //fallthrough
1695 : case GF_CODECID_HEVC_TILES:
1696 : if (!subtype) subtype = GF_ISOM_SUBTYPE_HVT1;
1697 36 : if (!dcd && ds->dep_id) {
1698 27 : u32 i, count = gf_list_count(ctx->current_period->streams);
1699 27 : for (i=0; i<count; i++) {
1700 27 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
1701 27 : if (a_ds->id != ds->dep_id) continue;
1702 27 : dcd = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_DECODER_CONFIG);
1703 : break;
1704 : }
1705 : }
1706 : //fallthrough
1707 : case GF_CODECID_HEVC:
1708 111 : if (!subtype) {
1709 75 : if (ds->tile_base) {
1710 4 : subtype = force_inband ? GF_ISOM_SUBTYPE_HEV2 : GF_ISOM_SUBTYPE_HVC2;
1711 71 : } else if (dcd_enh) {
1712 2 : if (dcd) {
1713 2 : subtype = force_inband ? GF_ISOM_SUBTYPE_HEV2 : GF_ISOM_SUBTYPE_HVC2;
1714 : } else {
1715 0 : subtype = force_inband ? GF_ISOM_SUBTYPE_LHE1 : GF_ISOM_SUBTYPE_LHV1;
1716 : }
1717 : } else {
1718 69 : subtype = force_inband ? GF_ISOM_SUBTYPE_HEV1 : GF_ISOM_SUBTYPE_HVC1;
1719 : }
1720 : }
1721 111 : if (dcd || dcd_enh) {
1722 111 : GF_HEVCConfig *hvcc = dcd ? gf_odf_hevc_cfg_read(dcd->value.data.ptr, dcd->value.data.size, GF_FALSE) : NULL;
1723 111 : if (hvcc) {
1724 111 : GF_Err e = rfc_6381_get_codec_hevc(szCodec, subtype, hvcc);
1725 111 : gf_odf_hevc_cfg_del(hvcc);
1726 : return e;
1727 : }
1728 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[Dasher] HEVC config not compliant !\n"));
1729 : return GF_NON_COMPLIANT_BITSTREAM;
1730 : }
1731 :
1732 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
1733 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find HEVC config, using default %s\n", szCodec));
1734 : return GF_OK;
1735 : #endif
1736 :
1737 : #ifndef GPAC_DISABLE_AV1
1738 : case GF_CODECID_AV1:
1739 : if (!subtype) subtype = GF_ISOM_SUBTYPE_AV01;
1740 :
1741 54 : if (dcd) {
1742 54 : GF_AV1Config *av1c = gf_odf_av1_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
1743 54 : if (av1c) {
1744 54 : GF_Err e = rfc_6381_get_codec_av1(szCodec, subtype, av1c);
1745 54 : gf_odf_av1_cfg_del(av1c);
1746 : return e;
1747 : }
1748 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[DASHER] AV1 config not conformant\n"));
1749 : return GF_NON_COMPLIANT_BITSTREAM;
1750 : }
1751 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
1752 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find AV1 config, using default %s\n", szCodec));
1753 : return GF_OK;
1754 : #endif /*GPAC_DISABLE_AV1*/
1755 :
1756 :
1757 : case GF_CODECID_VP8:
1758 : if (!subtype) subtype = GF_ISOM_SUBTYPE_VP08;
1759 : case GF_CODECID_VP9:
1760 : if (!subtype) subtype = GF_ISOM_SUBTYPE_VP09;
1761 :
1762 43 : if (dcd) {
1763 43 : GF_VPConfig *vpcc = gf_odf_vp_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
1764 :
1765 43 : if (vpcc) {
1766 43 : GF_Err e = rfc_6381_get_codec_vpx(szCodec, subtype, vpcc);
1767 43 : gf_odf_vp_cfg_del(vpcc);
1768 : return e;
1769 : }
1770 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[Dasher] No config found for VP file (\"%s\") when computing RFC6381.\n", gf_4cc_to_str(subtype)));
1771 : return GF_NON_COMPLIANT_BITSTREAM;
1772 : }
1773 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
1774 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find VPX config, using default %s\n", szCodec));
1775 : return GF_OK;
1776 :
1777 0 : case GF_CODECID_MHAS:
1778 0 : subtype = subtype_src ? subtype_src : GF_ISOM_SUBTYPE_MH3D_MHM1;
1779 0 : if (!dcd) {
1780 0 : const GF_PropertyValue *pl = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_PROFILE_LEVEL);
1781 0 : if (pl) mha_pl = (s32) pl->value.uint;
1782 : }
1783 : //fallthrough
1784 : case GF_CODECID_MPHA:
1785 0 : if (!subtype)
1786 0 : subtype = subtype_src ? subtype_src : GF_ISOM_SUBTYPE_MH3D_MHA1;
1787 :
1788 0 : return rfc_6381_get_codec_mpegha(szCodec, subtype, dcd ? dcd->value.data.ptr : NULL, dcd ? dcd->value.data.size : 0, mha_pl);
1789 :
1790 : case GF_CODECID_VVC:
1791 : if (!subtype) {
1792 0 : subtype = force_inband ? GF_ISOM_SUBTYPE_VVI1 : GF_ISOM_SUBTYPE_VVC1;
1793 : }
1794 0 : if (dcd) {
1795 0 : GF_VVCConfig *vvcc = gf_odf_vvc_cfg_read(dcd->value.data.ptr, dcd->value.data.size);
1796 :
1797 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s.", gf_4cc_to_str(subtype));
1798 0 : if (vvcc) {
1799 0 : GF_Err e = rfc_6381_get_codec_vvc(szCodec, subtype, vvcc);
1800 0 : gf_odf_vvc_cfg_del(vvcc);
1801 : return e;
1802 : }
1803 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_AUTHOR, ("[Dasher] No config found for VP file (\"%s\") when computing RFC6381.\n", gf_4cc_to_str(subtype)));
1804 : return GF_NON_COMPLIANT_BITSTREAM;
1805 : }
1806 0 : snprintf(szCodec, RFC6381_CODEC_NAME_SIZE_MAX, "%s", gf_4cc_to_str(subtype));
1807 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[Dasher] Cannot find VVC config, using default %s\n", szCodec));
1808 : return GF_OK;
1809 :
1810 8 : default:
1811 8 : subtype = gf_codecid_4cc_type(ds->codec_id);
1812 8 : if (!subtype) {
1813 0 : const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_ISOM_SUBTYPE);
1814 0 : if (p) subtype = p->value.uint;
1815 : }
1816 8 : if (!subtype) {
1817 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[Dasher] codec parameters not known, cannot set codec string\n" ));
1818 : strcpy(szCodec, "unkn");
1819 : return GF_OK;
1820 : }
1821 :
1822 8 : return rfc6381_codec_name_default(szCodec, subtype, ds->codec_id);
1823 : }
1824 : return GF_OK;
1825 : }
1826 :
1827 :
1828 45 : static GF_DashStream *get_base_ds(GF_DasherCtx *ctx, GF_DashStream *for_ds)
1829 : {
1830 : u32 i, count;
1831 45 : if (!for_ds->dep_id) return NULL;
1832 45 : count = gf_list_count(ctx->pids);
1833 0 : for (i=0; i<count; i++) {
1834 45 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
1835 45 : if (ds->id == for_ds->dep_id)
1836 : return ds;
1837 : }
1838 : return NULL;
1839 : }
1840 :
1841 109 : static void get_canon_urn(bin128 URN, char *res)
1842 : {
1843 : char sres[4];
1844 : u32 i;
1845 : /* Output canonical UIID form */
1846 : strcpy(res, "");
1847 545 : for (i=0; i<4; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
1848 : strcat(res, "-");
1849 327 : for (i=4; i<6; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
1850 : strcat(res, "-");
1851 327 : for (i=6; i<8; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
1852 : strcat(res, "-");
1853 327 : for (i=8; i<10; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
1854 : strcat(res, "-");
1855 763 : for (i=10; i<16; i++) { sprintf(sres, "%02x", URN[i]); strcat(res, sres); }
1856 109 : }
1857 :
1858 0 : static const char *get_drm_kms_name(const char *canURN)
1859 : {
1860 0 : if (!stricmp(canURN, "67706163-6365-6E63-6472-6D746F6F6C31")) return "GPAC1.0";
1861 0 : else if (!stricmp(canURN, "5E629AF5-38DA-4063-8977-97FFBD9902D4")) return "Marlin1.0";
1862 0 : else if (!strcmp(canURN, "adb41c24-2dbf-4a6d-958b-4457c0d27b95")) return "MediaAccess3.0";
1863 0 : else if (!strcmp(canURN, "A68129D3-575B-4F1A-9CBA-3223846CF7C3")) return "VideoGuard";
1864 0 : else if (!strcmp(canURN, "9a04f079-9840-4286-ab92-e65be0885f95")) return "PlayReady";
1865 0 : else if (!strcmp(canURN, "9a27dd82-fde2-4725-8cbc-4234aa06ec09")) return "VCAS";
1866 0 : else if (!strcmp(canURN, "F239E769-EFA3-4850-9C16-A903C6932EFB")) return "Adobe";
1867 0 : else if (!strcmp(canURN, "1f83e1e8-6ee9-4f0d-ba2f-5ec4e3ed1a66")) return "SecureMedia";
1868 0 : else if (!strcmp(canURN, "644FE7B5-260F-4FAD-949A-0762FFB054B4")) return "CMLA (OMA DRM)";
1869 0 : else if (!strcmp(canURN, "6a99532d-869f-5922-9a91-113ab7b1e2f3")) return "MobiTVDRM";
1870 0 : else if (!strcmp(canURN, "35BF197B-530E-42D7-8B65-1B4BF415070F")) return "DivX DRM";
1871 0 : else if (!strcmp(canURN, "B4413586-C58C-FFB0-94A5-D4896C1AF6C3")) return "VODRM";
1872 0 : else if (!strcmp(canURN, "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed")) return "Widevine";
1873 0 : else if (!strcmp(canURN, "80a6be7e-1448-4c37-9e70-d5aebe04c8d2")) return "Irdeto";
1874 0 : else if (!strcmp(canURN, "dcf4e3e3-62f1-5818-7ba6-0a6fe33ff3dd")) return "CA 1.0, DRM+ 2.0";
1875 0 : else if (!strcmp(canURN, "45d481cb-8fe0-49c0-ada9-ab2d2455b2f2")) return "CoreCrypt";
1876 0 : else if (!strcmp(canURN, "616C7469-6361-7374-2D50-726F74656374")) return "altiProtect";
1877 0 : else if (!strcmp(canURN, "992c46e6-c437-4899-b6a0-50fa91ad0e39")) return "Arris SecureMedia SteelKnot version 1";
1878 0 : else if (!strcmp(canURN, "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b")) return "cenc initData";
1879 0 : else if (!strcmp(canURN, "e2719d58-a985-b3c9-781a-b030af78d30e")) return "ClearKey1.0";
1880 0 : else if (!strcmp(canURN, "94CE86FB-07FF-4F43-ADB8-93D2FA968CA2")) return "FairPlay";
1881 0 : else if (!strcmp(canURN, "279fe473-512c-48fe-ade8-d176fee6b40f")) return "Arris Titanium";
1882 0 : else if (!strcmp(canURN, "aa11967f-cc01-4a4a-8e99-c5d3dddfea2d")) return "UDRM";
1883 0 : return "unknown";
1884 : }
1885 :
1886 323 : static GF_List *dasher_get_content_protection_desc(GF_DasherCtx *ctx, GF_DashStream *ds, GF_MPD_AdaptationSet *for_set)
1887 : {
1888 : char sCan[40];
1889 : u32 prot_scheme=0;
1890 : u32 i, count;
1891 : const GF_PropertyValue *p;
1892 : GF_List *res = NULL;
1893 : GF_BitStream *bs_r;
1894 :
1895 323 : count = gf_list_count(ctx->current_period->streams);
1896 323 : bs_r = gf_bs_new((const char *) &count, 1, GF_BITSTREAM_READ);
1897 :
1898 1167 : for (i=0; i<count; i++) {
1899 : GF_MPD_Descriptor *desc;
1900 844 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
1901 844 : if (!a_ds->is_encrypted) continue;
1902 :
1903 174 : if (for_set) {
1904 174 : if (a_ds->set != for_set) continue;
1905 : //for now only insert for the stream holding the set
1906 125 : if (!a_ds->owns_set) continue;
1907 0 : } else if ((a_ds != ds) && (a_ds->muxed_base != ds) ) {
1908 0 : continue;
1909 : }
1910 :
1911 119 : p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_PROTECTION_SCHEME_TYPE);
1912 119 : if (p) prot_scheme = p->value.uint;
1913 :
1914 119 : if ((prot_scheme==GF_ISOM_CENC_SCHEME) || (prot_scheme==GF_ISOM_CBC_SCHEME) || (prot_scheme==GF_ISOM_CENS_SCHEME) || (prot_scheme==GF_ISOM_CBCS_SCHEME)
1915 0 : ) {
1916 : const GF_PropertyValue *ki;
1917 : u32 j, nb_pssh;
1918 : GF_XMLAttribute *att;
1919 : char szVal[GF_MAX_PATH];
1920 :
1921 109 : ctx->use_cenc = GF_TRUE;
1922 :
1923 109 : ki = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_CENC_KEY_INFO);
1924 109 : if (!ki || !ki->value.data.ptr) {
1925 109 : continue;
1926 : }
1927 :
1928 109 : if (!res) res = gf_list_new();
1929 109 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:mp4protection:2011", gf_4cc_to_str(prot_scheme));
1930 109 : gf_list_add(res, desc);
1931 :
1932 109 : get_canon_urn(ki->value.data.ptr + 4, sCan);
1933 109 : att = gf_xml_dom_create_attribute("cenc:default_KID", sCan);
1934 109 : if (!desc->x_attributes) desc->x_attributes = gf_list_new();
1935 109 : gf_list_add(desc->x_attributes, att);
1936 :
1937 109 : if (ctx->pssh <= GF_DASH_PSSH_MOOF) {
1938 109 : continue;
1939 : }
1940 : //(data) binary blob containing (u32)N [(bin128)SystemID(u32)version(u32)KID_count[(bin128)keyID](u32)priv_size(char*priv_size)priv_data]
1941 0 : p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_CENC_PSSH);
1942 0 : if (!p) continue;
1943 :
1944 0 : gf_bs_reassign_buffer(bs_r, p->value.data.ptr, p->value.data.size);
1945 0 : nb_pssh = gf_bs_read_u32(bs_r);
1946 :
1947 : //add pssh
1948 0 : for (j=0; j<nb_pssh; j++) {
1949 : u32 pssh_idx;
1950 : bin128 sysID;
1951 : GF_XMLNode *node;
1952 : u32 version, k_count;
1953 0 : u8 *pssh_data=NULL;
1954 : u32 pssh_len, size_64;
1955 0 : GF_BitStream *bs_w = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
1956 :
1957 : //rewrite PSSH box
1958 0 : gf_bs_write_u32(bs_w, 0);
1959 0 : gf_bs_write_u32(bs_w, GF_ISOM_BOX_TYPE_PSSH);
1960 :
1961 0 : gf_bs_read_data(bs_r, sysID, 16);
1962 0 : version = gf_bs_read_u32(bs_r);
1963 :
1964 0 : k_count = gf_bs_read_u32(bs_r);
1965 0 : if (k_count) version = 1;
1966 0 : gf_bs_write_u8(bs_w, version);
1967 0 : gf_bs_write_u24(bs_w, 0);
1968 0 : gf_bs_write_data(bs_w, sysID, 16);
1969 0 : if (version) {
1970 0 : gf_bs_write_u32(bs_w, k_count);
1971 0 : for (j=0; j<k_count; j++) {
1972 : bin128 keyID;
1973 0 : gf_bs_read_data(bs_r, keyID, 16);
1974 0 : gf_bs_write_data(bs_w, keyID, 16);
1975 : }
1976 : }
1977 0 : k_count = gf_bs_read_u32(bs_r);
1978 0 : gf_bs_write_u32(bs_w, k_count);
1979 0 : for (pssh_idx=0; pssh_idx<k_count; pssh_idx++) {
1980 0 : gf_bs_write_u8(bs_w, gf_bs_read_u8(bs_r) );
1981 : }
1982 0 : pssh_len = (u32) gf_bs_get_position(bs_w);
1983 0 : gf_bs_seek(bs_w, 0);
1984 0 : gf_bs_write_u32(bs_w, pssh_len);
1985 0 : gf_bs_seek(bs_w, pssh_len);
1986 0 : gf_bs_get_content(bs_w, &pssh_data, &pssh_len);
1987 0 : gf_bs_del(bs_w);
1988 :
1989 0 : get_canon_urn(sysID, sCan);
1990 0 : desc = gf_mpd_descriptor_new(NULL, NULL, NULL);
1991 0 : desc->x_children = gf_list_new();
1992 : sprintf(szVal, "urn:uuid:%s", sCan);
1993 0 : desc->scheme_id_uri = gf_strdup(szVal);
1994 0 : desc->value = gf_strdup(get_drm_kms_name(sCan));
1995 0 : gf_list_add(res, desc);
1996 :
1997 0 : GF_SAFEALLOC(node, GF_XMLNode);
1998 0 : if (node) {
1999 : GF_XMLNode *pnode;
2000 0 : node->type = GF_XML_NODE_TYPE;
2001 0 : node->name = gf_strdup("cenc:pssh");
2002 0 : node->content = gf_list_new();
2003 0 : gf_list_add(desc->x_children, node);
2004 :
2005 0 : GF_SAFEALLOC(pnode, GF_XMLNode);
2006 0 : if (pnode) {
2007 0 : pnode->type = GF_XML_TEXT_TYPE;
2008 0 : gf_list_add(node->content, pnode);
2009 :
2010 0 : size_64 = 2*pssh_len;
2011 0 : pnode->name = gf_malloc(size_64);
2012 0 : if (pnode->name) {
2013 0 : size_64 = gf_base64_encode((const char *)pssh_data, pssh_len, (char *)pnode->name, size_64);
2014 0 : pnode->name[size_64] = 0;
2015 : }
2016 : }
2017 : }
2018 0 : gf_free(pssh_data);
2019 : }
2020 : } else {
2021 10 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Protection scheme %s has no official DASH mapping, using URI \"urn:gpac:dash:mp4protection:2018\"\n", gf_4cc_to_str(prot_scheme)));
2022 10 : if (!res) res = gf_list_new();
2023 10 : desc = gf_mpd_descriptor_new(NULL, "urn:gpac:dash:mp4protection:2018", gf_4cc_to_str(prot_scheme));
2024 10 : gf_list_add(res, desc);
2025 : }
2026 : }
2027 323 : gf_bs_del(bs_r);
2028 323 : return res;
2029 : }
2030 :
2031 357 : static void dasher_get_mime_and_ext(GF_DasherCtx *ctx, GF_DashStream *ds, const char **out_subtype, const char **out_ext)
2032 : {
2033 : const char *subtype = NULL;
2034 : const char *mux_ext = NULL;
2035 : const char *cstr;
2036 :
2037 357 : if (ctx->m2ts) {
2038 : subtype = "mp2t";
2039 346 : } else if (ctx->muxtype!=DASHER_MUX_AUTO) {
2040 345 : switch (ctx->muxtype) {
2041 : case DASHER_MUX_ISOM: subtype = "mp4"; mux_ext = "mp4"; break;
2042 0 : case DASHER_MUX_TS: subtype = "mp2t"; mux_ext = "ts"; break;
2043 1 : case DASHER_MUX_MKV: subtype = "x-matroska"; mux_ext = "mkv"; break;
2044 0 : case DASHER_MUX_WEBM: subtype = "webm"; mux_ext = "webm"; break;
2045 0 : case DASHER_MUX_OGG: subtype = "ogg"; mux_ext = "ogg"; break;
2046 8 : case DASHER_MUX_RAW:
2047 8 : cstr = gf_codecid_mime(ds->codec_id);
2048 8 : if (cstr) {
2049 8 : subtype = strchr(cstr, '/');
2050 8 : if (subtype) subtype++;
2051 : else subtype = "raw";
2052 : }
2053 8 : if (out_ext) {
2054 0 : cstr = gf_codecid_file_ext(ds->codec_id);
2055 0 : if (cstr) *out_ext = cstr;
2056 : }
2057 : break;
2058 : }
2059 1 : } else if (ctx->initext) {
2060 : mux_ext = ctx->initext;
2061 1 : if (!strcmp(ctx->initext, "ts") || !strcmp(ctx->initext, "m2ts")) {
2062 : subtype = "mp2t";
2063 0 : ctx->muxtype = DASHER_MUX_TS;
2064 1 : } else if (!strcmp(ctx->initext, "mkv") || !strcmp(ctx->initext, "mka") || !strcmp(ctx->initext, "mks") || !strcmp(ctx->initext, "mk3d")) {
2065 : subtype = "x-matroska";
2066 1 : ctx->muxtype = DASHER_MUX_MKV;
2067 0 : } else if (!strcmp(ctx->initext, "webm") || !strcmp(ctx->initext, "weba")) {
2068 : subtype = "webm";
2069 0 : ctx->muxtype = DASHER_MUX_WEBM;
2070 0 : } else if (!strcmp(ctx->initext, "ogg") || !strcmp(ctx->initext, "oga") || !strcmp(ctx->initext, "ogv") || !strcmp(ctx->initext, "spx") || !strcmp(ctx->initext, "oggm") || !strcmp(ctx->initext, "opus")) {
2071 : subtype = "ogg";
2072 0 : ctx->muxtype = DASHER_MUX_OGG;
2073 : }
2074 0 : else if (!strcmp(ctx->initext, "null")) {
2075 : mux_ext = "mp4";
2076 0 : ctx->muxtype = DASHER_MUX_ISOM;
2077 : }
2078 : }
2079 357 : if (!subtype) subtype = "mp4";
2080 357 : if (out_subtype) *out_subtype = subtype;
2081 357 : if (!mux_ext) mux_ext = "mp4";
2082 357 : if (out_ext) *out_ext = mux_ext;
2083 357 : }
2084 :
2085 :
2086 538 : static void dasher_update_rep(GF_DasherCtx *ctx, GF_DashStream *ds)
2087 : {
2088 : char szCodec[RFC6381_CODEC_NAME_SIZE_MAX];
2089 :
2090 : //Outputs are not yet connected, derive mime from init segment extension
2091 538 : if (!ds->rep->mime_type) {
2092 357 : const char *subtype = NULL;
2093 357 : dasher_get_mime_and_ext(ctx, ds, &subtype, NULL);
2094 :
2095 357 : if (ds->stream_type==GF_STREAM_VISUAL)
2096 271 : gf_dynstrcat(&ds->rep->mime_type, "video/", NULL);
2097 86 : else if (ds->stream_type==GF_STREAM_AUDIO)
2098 84 : gf_dynstrcat(&ds->rep->mime_type, "audio/", NULL);
2099 : else
2100 2 : gf_dynstrcat(&ds->rep->mime_type, "application/", NULL);
2101 :
2102 357 : gf_dynstrcat(&ds->rep->mime_type, subtype, NULL);
2103 : }
2104 :
2105 538 : ds->rep->bandwidth = ds->bitrate;
2106 538 : if (ds->stream_type==GF_STREAM_VISUAL) {
2107 405 : ds->rep->width = ds->width;
2108 405 : ds->rep->height = ds->height;
2109 :
2110 :
2111 405 : if (!ds->rep->sar) {
2112 271 : GF_SAFEALLOC(ds->rep->sar, GF_MPD_Fractional);
2113 : }
2114 405 : if (ds->rep->sar) {
2115 405 : ds->rep->sar->num = ds->sar.num;
2116 405 : ds->rep->sar->den = ds->sar.den;
2117 : }
2118 405 : if (ds->fps.num && ds->fps.den) {
2119 376 : if (!ds->rep->framerate) {
2120 242 : GF_SAFEALLOC(ds->rep->framerate, GF_MPD_Fractional);
2121 : }
2122 376 : if (ds->rep->framerate) {
2123 376 : ds->rep->framerate->num = ds->fps.num;
2124 376 : ds->rep->framerate->den = ds->fps.den;
2125 376 : gf_media_get_reduced_frame_rate(&ds->rep->framerate->num, &ds->rep->framerate->den);
2126 : }
2127 : }
2128 : }
2129 133 : else if (ds->stream_type==GF_STREAM_AUDIO) {
2130 : Bool use_cicp = GF_FALSE;
2131 : Bool use_dolbyx = GF_FALSE;
2132 : GF_MPD_Descriptor *desc;
2133 : char value[256];
2134 131 : ds->rep->samplerate = ds->sr;
2135 :
2136 131 : if (ds->nb_surround || ds->nb_lfe) use_cicp = GF_TRUE;
2137 131 : if ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA)) use_cicp = GF_TRUE;
2138 :
2139 131 : if ((ds->codec_id==GF_CODECID_AC3) || (ds->codec_id==GF_CODECID_EAC3)) {
2140 : //if regular MPEG-DASH, use CICP, otherwise use Dolby signaling
2141 1 : if (ctx->profile > GF_DASH_PROFILE_FULL) {
2142 : use_dolbyx = GF_TRUE;
2143 : }
2144 : }
2145 : if (use_dolbyx) {
2146 : u32 cicp_layout = 0;
2147 0 : if (ds->ch_layout)
2148 0 : cicp_layout = gf_audio_fmt_get_cicp_from_layout(ds->ch_layout);
2149 0 : if (!cicp_layout)
2150 0 : cicp_layout = gf_audio_fmt_get_cicp_layout(ds->nb_ch, ds->nb_surround, ds->nb_lfe);
2151 :
2152 0 : sprintf(value, "%X", gf_audio_fmt_get_dolby_chanmap(cicp_layout) );
2153 0 : desc = gf_mpd_descriptor_new(NULL, "tag:dolby.com,2014:dash:audio_channel_configuration:2011", value);
2154 : }
2155 131 : else if (!use_cicp) {
2156 131 : sprintf(value, "%d", ds->nb_ch);
2157 131 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", value);
2158 : } else {
2159 0 : sprintf(value, "%d", gf_audio_fmt_get_cicp_layout(ds->nb_ch, ds->nb_surround, ds->nb_lfe));
2160 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:ChannelConfiguration", value);
2161 : }
2162 :
2163 131 : gf_mpd_del_list(ds->rep->audio_channels, gf_mpd_descriptor_free, GF_TRUE);
2164 :
2165 131 : gf_list_add(ds->rep->audio_channels, desc);
2166 : } else {
2167 : }
2168 :
2169 538 : dasher_get_rfc_6381_codec_name(ctx, ds, szCodec, (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) ? GF_TRUE : GF_FALSE, GF_TRUE);
2170 538 : if (ds->rep->codecs) gf_free(ds->rep->codecs);
2171 538 : ds->rep->codecs = gf_strdup(szCodec);
2172 :
2173 538 : if (ds->interlaced) ds->rep->scan_type = GF_MPD_SCANTYPE_INTERLACED;
2174 : else {
2175 : //profiles forcing scanType=progressive for progressive
2176 538 : switch (ctx->profile) {
2177 0 : case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
2178 0 : ds->rep->scan_type = GF_MPD_SCANTYPE_PROGRESSIVE;
2179 0 : break;
2180 : }
2181 : }
2182 :
2183 538 : if (ctx->cp!=GF_DASH_CPMODE_ADAPTATION_SET) {
2184 0 : gf_mpd_del_list(ds->rep->content_protection, gf_mpd_descriptor_free, 0);
2185 0 : ds->rep->content_protection = dasher_get_content_protection_desc(ctx, ds, NULL);
2186 : }
2187 538 : }
2188 :
2189 357 : static void dasher_setup_rep(GF_DasherCtx *ctx, GF_DashStream *ds, u32 *srd_rep_idx)
2190 : {
2191 : const GF_PropertyValue *p;
2192 :
2193 : assert(ds->rep==NULL);
2194 357 : ds->rep = gf_mpd_representation_new();
2195 357 : ds->rep->playback.udta = ds;
2196 357 : if (ds->tci)
2197 0 : ds->rep->crypto_type = 1;
2198 : else
2199 357 : ds->rep->crypto_type = ds->is_encrypted ? 2 : 0;
2200 :
2201 357 : dasher_update_rep(ctx, ds);
2202 :
2203 357 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_AS_ID);
2204 : //do not reset as id in case of period continuity
2205 357 : if (p) {
2206 0 : if (ds->as_id != p->value.uint) {
2207 0 : if (ds->period_continuity_id) gf_free(ds->period_continuity_id);
2208 0 : ds->period_continuity_id = NULL;
2209 : }
2210 0 : ds->as_id = p->value.uint;
2211 : }
2212 :
2213 357 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_REP_ID);
2214 357 : if (p) {
2215 279 : if (ds->rep_id) gf_free(ds->rep_id);
2216 :
2217 306 : if (!ds->tile_base && (ds->srd.w || ds->srd.z) && !ctx->sseg && !ctx->sfile) {
2218 27 : char *rep_name = gf_malloc(sizeof(char) * (strlen(p->value.string) + 15) );
2219 27 : sprintf(rep_name, "%s_%d", p->value.string, *srd_rep_idx);
2220 27 : ds->rep_id = rep_name;
2221 27 : (*srd_rep_idx) ++;
2222 : } else {
2223 252 : ds->rep_id = gf_strdup(p->value.string);
2224 : }
2225 :
2226 78 : } else if (!ds->rep_id) {
2227 : char szRepID[20];
2228 78 : sprintf(szRepID, "%d", 1 + gf_list_find(ctx->pids, ds));
2229 78 : ds->rep_id = gf_strdup(szRepID);
2230 : }
2231 357 : ds->rep->id = gf_strdup(ds->rep_id);
2232 357 : }
2233 :
2234 258 : static Bool dasher_same_roles(GF_DashStream *ds1, GF_DashStream *ds2)
2235 : {
2236 : const GF_PropStringList *slist;
2237 258 : if (ds1->p_role && ds2->p_role) {
2238 0 : if (gf_props_equal(ds1->p_role, ds2->p_role)) return GF_TRUE;
2239 : }
2240 258 : if (!ds1->p_role && !ds2->p_role)
2241 : return GF_TRUE;
2242 :
2243 : //special case, if one is set and the other is not, compare with "main" role
2244 0 : slist = ds2->p_role ? &ds2->p_role->value.string_list : &ds1->p_role->value.string_list;
2245 0 : if (slist->nb_items==1) {
2246 0 : if (!strcmp(slist->vals[0], "main")) return GF_TRUE;
2247 : }
2248 : return GF_FALSE;
2249 : }
2250 :
2251 0 : static u32 dasher_get_next_as_id(GF_DasherCtx *ctx)
2252 : {
2253 : u32 check_id = 1;
2254 0 : u32 i, count = gf_list_count(ctx->current_period->streams);
2255 0 : for (i=0; i<count; i++) {
2256 0 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
2257 0 : if (ds->as_id == check_id) {
2258 0 : check_id++;
2259 : i = -1;
2260 0 : continue;
2261 : }
2262 : }
2263 0 : return check_id;
2264 : }
2265 :
2266 316 : static Bool dasher_same_adaptation_set(GF_DasherCtx *ctx, GF_DashStream *ds, GF_DashStream *ds_test)
2267 : {
2268 : const char *lang1, *lang2;
2269 : const GF_PropertyValue *p1, *p2;
2270 :
2271 : //in all forward mode we don't rewrite the manifest, make each source file a single as
2272 316 : if (ctx->forward_mode==DASHER_FWD_ALL)
2273 : return GF_FALSE;
2274 :
2275 : //muxed representations
2276 307 : if (ds_test->muxed_base) {
2277 10 : if (ds_test->muxed_base == ds)
2278 : return GF_TRUE;
2279 : //if muxed base rep has been registered with this AdaptationSet, also register this stream
2280 0 : if (gf_list_find(ds->set->representations, ds_test->muxed_base->rep)>=0)
2281 : return GF_TRUE;
2282 : }
2283 :
2284 : //otherwise we have to be of same type
2285 297 : if (ds->stream_type != ds_test->stream_type) return GF_FALSE;
2286 :
2287 : //not the same roles
2288 258 : if (!dasher_same_roles(ds, ds_test)) return GF_FALSE;
2289 :
2290 : //intra-only trick mode belongs to a separate AS
2291 258 : if ((ds->stream_type == GF_STREAM_VISUAL) && (ds->sync_points_type != ds_test->sync_points_type)) {
2292 : //assign trickmode as id for dashif
2293 0 : if (ds_test->sync_points_type == DASHER_SYNC_NONE) {
2294 0 : if (!ds->as_id) ds->as_id = dasher_get_next_as_id(ctx);
2295 0 : ds_test->sync_as_id = ds->as_id;
2296 : }
2297 0 : else if (ds->sync_points_type == DASHER_SYNC_NONE) {
2298 0 : if (!ds_test->as_id) ds_test->as_id = dasher_get_next_as_id(ctx);
2299 0 : ds->sync_as_id = ds_test->as_id;
2300 : }
2301 : return GF_FALSE;
2302 : }
2303 :
2304 : /* if two inputs don't have the same (number and value) as_desc they don't belong to the same AdaptationSet
2305 : (use c_as_desc for AdaptationSet descriptors common to all inputs in an AS) */
2306 258 : if (!ds->p_as_desc && ds_test->p_as_desc)
2307 : return GF_FALSE;
2308 258 : if (ds->p_as_desc && !ds_test->p_as_desc)
2309 : return GF_FALSE;
2310 258 : if (ds->p_as_desc && ! gf_props_equal(ds->p_as_desc, ds_test->p_as_desc))
2311 : return GF_FALSE;
2312 :
2313 : //need same AS ID if specified
2314 257 : if (ds->as_id && ds_test->as_id &&(ds->as_id != ds_test->as_id) )
2315 : return GF_FALSE;
2316 :
2317 : //need same dash duration if aligned
2318 257 : if (ctx->align) {
2319 257 : if ((u64) ds->dash_dur.num * ds_test->dash_dur.den != (u64) ds_test->dash_dur.num * ds->dash_dur.den) return GF_FALSE;
2320 : }
2321 :
2322 : //if one of the pid is marked with period resume and the other is not, one is a spliced media the other no
2323 : //cf flist filter
2324 257 : p1 = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
2325 257 : p2 = gf_filter_pid_get_property_str(ds_test->ipid, "period_resume");
2326 257 : if ((!p1 && p2) || (p1 && !p2) || (p1 && gf_props_equal(p1, p2)))
2327 : return GF_FALSE;
2328 :
2329 257 : if (ds->srd.x != ds_test->srd.x) return GF_FALSE;
2330 92 : if (ds->srd.y != ds_test->srd.y) return GF_FALSE;
2331 37 : if (ds->srd.z != ds_test->srd.z) return GF_FALSE;
2332 32 : if (ds->srd.w != ds_test->srd.w) return GF_FALSE;
2333 :
2334 32 : if (ds->view_id != ds_test->view_id) return GF_FALSE;
2335 : //according to DASH spec mixing interlaced and progressive is OK
2336 : //if (ds->interlaced != ds_test->interlaced) return GF_FALSE;
2337 32 : if (ds->nb_ch != ds_test->nb_ch) return GF_FALSE;
2338 :
2339 32 : lang1 = ds->lang ? ds->lang : "und";
2340 32 : lang2 = ds_test->lang ? ds_test->lang : "und";
2341 32 : if (strcmp(lang1, lang2)) return GF_FALSE;
2342 :
2343 30 : if (ds->stream_type==GF_STREAM_VISUAL) {
2344 : u32 w, h, tw, th;
2345 30 : if (ctx->no_sar) {
2346 0 : w = ds->width;
2347 0 : h = ds->height;
2348 0 : tw = ds_test->width;
2349 0 : th = ds_test->height;
2350 : } else {
2351 30 : w = ds->width * ds->sar.num;
2352 30 : h = ds->height * ds->sar.den;
2353 30 : tw = ds_test->width * ds_test->sar.num;
2354 30 : th = ds_test->height * ds_test->sar.den;
2355 : }
2356 :
2357 : //not the same aspect ratio
2358 30 : if (w * th != h * tw)
2359 : return GF_FALSE;
2360 0 : } else if (ds->stream_type==GF_STREAM_AUDIO) {
2361 0 : if (!ctx->mix_codecs && (ds->codec_id != ds_test->codec_id) )
2362 : return GF_FALSE;
2363 : //we allow mix of channels config
2364 : } else {
2365 0 : if (!ctx->mix_codecs && strcmp(ds->rep->codecs, ds_test->rep->codecs)) return GF_FALSE;
2366 0 : return GF_TRUE;
2367 : }
2368 : //ok, we are video or audio with mixed codecs
2369 30 : if (ctx->mix_codecs) return GF_TRUE;
2370 : //we need dependencies, unless SRD case
2371 30 : if (!ds_test->srd.z && !ds_test->srd.w) {
2372 20 : if (ds_test->dep_id && (ds_test->src_id==ds->src_id) && gf_list_find(ds->complementary_streams, ds_test) < 0) {
2373 : return GF_FALSE;
2374 : }
2375 : }
2376 : //we should be good
2377 : return GF_TRUE;
2378 : }
2379 :
2380 1388 : static void dasher_add_descriptors(GF_List **p_dst_list, const GF_PropertyValue *desc_val)
2381 : {
2382 : u32 j, count;
2383 : GF_List *dst_list;
2384 1388 : if (!desc_val) return;
2385 8 : if (desc_val->type != GF_PROP_STRING_LIST) return;
2386 8 : count = desc_val->value.string_list.nb_items;
2387 8 : if (!count) return;
2388 8 : if ( ! (*p_dst_list)) *p_dst_list = gf_list_new();
2389 8 : dst_list = *p_dst_list;
2390 16 : for (j=0; j<count; j++) {
2391 8 : char *desc = desc_val->value.string_list.vals[j];
2392 8 : if (desc[0] == '<') {
2393 : GF_XMLNode *d;
2394 8 : GF_SAFEALLOC(d, GF_XMLNode);
2395 8 : if (d) {
2396 8 : d->type = GF_XML_TEXT_TYPE;
2397 8 : d->name = gf_strdup(desc);
2398 8 : gf_list_add(dst_list, d);
2399 : }
2400 : } else {
2401 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Invalid descriptor %s, expecting '<' as first character\n", desc));
2402 : }
2403 : }
2404 : }
2405 :
2406 317 : static void dasher_setup_set_defaults(GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
2407 : {
2408 : u32 i, count;
2409 : Bool main_role_set = GF_FALSE;
2410 : //by default setup alignment
2411 317 : if (ctx->sseg) set->subsegment_alignment = ctx->align;
2412 296 : else set->segment_alignment = ctx->align;
2413 :
2414 : //startWithSAP is set when the first packet comes in
2415 :
2416 : //the rest depends on the various profiles/iop, to check
2417 317 : count = gf_list_count(set->representations);
2418 356 : for (i=0; i<count; i++) {
2419 356 : GF_MPD_Representation *rep = gf_list_get(set->representations, i);
2420 356 : GF_DashStream *ds = rep->playback.udta;
2421 :
2422 356 : if (set->max_width < ds->width) set->max_width = ds->width;
2423 356 : if (set->max_height < ds->height) set->max_height = ds->height;
2424 : /* if (set->max_bandwidth < ds->rep->bandwidth) set->max_bandwidth = ds->rep->bandwidth;
2425 : if (set->max_framerate * ds->fps.den < ds->fps.num) set->max_framerate = (u32) (ds->fps.num / ds->fps.den);
2426 : */
2427 :
2428 : /*set trick mode*/
2429 356 : if (set->intra_only && (ds->stream_type==GF_STREAM_VISUAL)) {
2430 : char value[256];
2431 : GF_MPD_Descriptor* desc;
2432 1 : sprintf(value, "%d", ds->sync_as_id);
2433 1 : desc = gf_mpd_descriptor_new(NULL, "http://dashif.org/guidelines/trickmode", value);
2434 1 : gf_list_add(set->essential_properties, desc);
2435 : }
2436 : /*set role*/
2437 356 : if (ds->p_role) {
2438 : u32 j, role_count;
2439 0 : role_count = ds->p_role->value.string_list.nb_items;
2440 0 : for (j=0; j<role_count; j++) {
2441 0 : char *role = ds->p_role->value.string_list.vals[j];
2442 : GF_MPD_Descriptor *desc;
2443 : char *uri;
2444 0 : if (!strcmp(role, "caption") || !strcmp(role, "subtitle") || !strcmp(role, "main")
2445 0 : || !strcmp(role, "alternate") || !strcmp(role, "supplementary") || !strcmp(role, "commentary")
2446 0 : || !strcmp(role, "dub") || !strcmp(role, "description") || !strcmp(role, "sign")
2447 0 : || !strcmp(role, "metadata") || !strcmp(role, "enhanced-audio- intelligibility")
2448 : ) {
2449 : uri = "urn:mpeg:dash:role:2011";
2450 0 : if (!strcmp(role, "main")) main_role_set = GF_TRUE;
2451 : } else {
2452 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Unrecognized role %s - using GPAC urn for schemaID\n", role));
2453 : uri = "urn:gpac:dash:role:2013";
2454 : }
2455 0 : desc = gf_mpd_descriptor_new(NULL, uri, role);
2456 0 : gf_list_add(set->role, desc);
2457 : }
2458 : }
2459 : //set SRD
2460 356 : if (!i && ds->srd.z && ds->srd.w) {
2461 : char value[256];
2462 : GF_MPD_Descriptor *desc;
2463 30 : if (ds->dep_id) {
2464 27 : sprintf(value, "1,%d,%d,%d,%d", ds->srd.x, ds->srd.y, ds->srd.z, ds->srd.w);
2465 27 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:srd:2014", value);
2466 27 : gf_list_add(set->supplemental_properties, desc);
2467 : } else {
2468 : sprintf(value, "1,0,0,0,0,%d,%d", ds->srd.z, ds->srd.w);
2469 3 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:srd:2014", value);
2470 3 : gf_list_add(set->essential_properties, desc);
2471 : }
2472 : }
2473 : //set HDR
2474 356 : if (ds->hdr_type > DASHER_HDR_NONE) {
2475 : char value[256];
2476 : GF_MPD_Descriptor* desc;
2477 : sprintf(value, "9");
2478 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:ColourPrimaries", value);
2479 0 : gf_list_add(set->essential_properties, desc);
2480 : sprintf(value, "9");
2481 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:MatrixCoefficients", value);
2482 0 : gf_list_add(set->essential_properties, desc);
2483 :
2484 0 : if (ds->hdr_type==DASHER_HDR_PQ10) {
2485 : sprintf(value, "16");
2486 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:TransferCharacteristics", value);
2487 0 : gf_list_add(set->essential_properties, desc);
2488 : }
2489 :
2490 0 : if (ds->hdr_type == DASHER_HDR_HLG) {
2491 : sprintf(value, "14");
2492 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:TransferCharacteristics", value);
2493 0 : gf_list_add(set->essential_properties, desc);
2494 : sprintf(value, "18");
2495 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:mpegB:cicp:TransferCharacteristics", value);
2496 0 : gf_list_add(set->supplemental_properties, desc);
2497 : }
2498 : }
2499 : }
2500 317 : if (ctx->check_main_role && !main_role_set) {
2501 : GF_MPD_Descriptor *desc;
2502 0 : desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:role:2011", "main");
2503 0 : gf_list_add(set->role, desc);
2504 : }
2505 317 : }
2506 :
2507 1 : static void rewrite_dep_ids(GF_DasherCtx *ctx, GF_DashStream *base_ds)
2508 : {
2509 1 : u32 i, count = gf_list_count(ctx->pids);
2510 20 : for (i=0; i<count; i++) {
2511 20 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
2512 20 : if (ds->src_id != base_ds->src_id) continue;
2513 10 : if (!ds->dep_id || !ds->rep) continue;
2514 9 : if (ds->dep_id != base_ds->id) continue;
2515 :
2516 9 : ds->tile_dep_id_merged = GF_TRUE;
2517 9 : if (ds->rep->dependency_id) gf_free(ds->rep->dependency_id);
2518 9 : ds->rep->dependency_id = gf_strdup(base_ds->merged_tile_dep->rep->id);
2519 : }
2520 1 : }
2521 :
2522 317 : static void dasher_check_bitstream_swicthing(GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
2523 : {
2524 : u32 i, j, count;
2525 317 : Bool use_inband = (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) ? GF_TRUE : GF_FALSE;
2526 317 : Bool use_multi = (ctx->bs_switch==DASHER_BS_SWITCH_MULTI) ? GF_TRUE : GF_FALSE;
2527 317 : GF_MPD_Representation *base_rep = gf_list_get(set->representations, 0);
2528 : GF_DashStream *base_ds;
2529 :
2530 317 : if (ctx->m2ts) {
2531 8 : set->bitstream_switching = GF_TRUE;
2532 : return;
2533 : }
2534 309 : if (ctx->bs_switch==DASHER_BS_SWITCH_OFF) return;
2535 287 : if (!base_rep) return;
2536 287 : base_ds = base_rep->playback.udta;
2537 :
2538 287 : count = gf_list_count(set->representations);
2539 287 : if (count==1) {
2540 253 : if (ctx->bs_switch==DASHER_BS_SWITCH_FORCE) set->bitstream_switching=GF_TRUE;
2541 251 : else if (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) {
2542 0 : base_ds->inband_params = GF_TRUE;
2543 : }
2544 : return;
2545 : }
2546 :
2547 15 : for (i=1; i<count; i++) {
2548 34 : GF_MPD_Representation *rep = gf_list_get(set->representations, i);
2549 34 : GF_DashStream *ds = rep->playback.udta;
2550 : //same codec ID
2551 34 : if (ds->codec_id == base_ds->codec_id) {
2552 : //we will use inband params, so bs switching is OK
2553 30 : if (use_inband || use_multi) continue;
2554 : //we have deps, cannot use bitstream switching except for merged tile base
2555 30 : if (ds->dep_id) {
2556 10 : if (ds->codec_id==GF_CODECID_HEVC_TILES) {
2557 : u32 id;
2558 9 : GF_DashStream *tile_base = get_base_ds(ctx, ds);
2559 9 : if (!tile_base) return;
2560 9 : id = tile_base->merged_tile_dep ? tile_base->merged_tile_dep->id : tile_base->id;
2561 9 : if (base_ds->dep_id==id) continue;
2562 : }
2563 : return;
2564 : }
2565 : //we consider we can switch in non-inband only if we have same CRC for the decoder config
2566 20 : if (base_ds->dsi_crc == ds->dsi_crc) continue;
2567 : //not the same config, no BS switching
2568 : return;
2569 : }
2570 : //dependencies / different codec IDs, cannot use bitstream switching
2571 : return;
2572 : }
2573 : //ok we can use BS switching, ensure we use the same timescale for every stream
2574 15 : set->bitstream_switching = GF_TRUE;
2575 :
2576 45 : for (i=0; i<count; i++) {
2577 30 : GF_MPD_Representation *rep = gf_list_get(set->representations, i);
2578 30 : GF_DashStream *ds = rep->playback.udta;
2579 30 : if (base_ds->tile_base && ds->tile_base && (base_ds != ds) ) {
2580 1 : ds->merged_tile_dep = base_ds;
2581 1 : if (ds->rep) {
2582 1 : gf_list_rem(set->representations, i);
2583 1 : i--;
2584 1 : count--;
2585 1 : gf_mpd_representation_free(ds->rep);
2586 1 : ds->rep = NULL;
2587 : //switch dependencyID of all reps depending on this one to the new base
2588 1 : rewrite_dep_ids(ctx, ds);
2589 : }
2590 : }
2591 45 : for (j=i+1; j<count; j++) {
2592 : GF_DashStream *a_ds;
2593 15 : rep = gf_list_get(set->representations, j);
2594 15 : a_ds = rep->playback.udta;
2595 15 : if (a_ds->stream_type != ds->stream_type) continue;
2596 15 : if (a_ds->timescale != ds->timescale)
2597 0 : a_ds->force_timescale = ds->timescale;
2598 : }
2599 : }
2600 : }
2601 :
2602 : GF_Err gf_cryptfout_push_key(GF_Filter *filter, bin128 *key, bin128 *IV);
2603 :
2604 349 : static void dasher_open_destination(GF_Filter *filter, GF_DasherCtx *ctx, GF_MPD_Representation *rep, const char *szInitURL, Bool trash_init)
2605 : {
2606 : GF_Err e;
2607 : Bool has_frag=GF_FALSE;
2608 : Bool has_subs=GF_FALSE;
2609 : Bool has_strun=GF_FALSE;
2610 : Bool has_vodcache=GF_FALSE;
2611 : Bool has_cmaf=GF_FALSE;
2612 349 : char sep_args = gf_filter_get_sep(filter, GF_FS_SEP_ARGS);
2613 349 : char sep_name = gf_filter_get_sep(filter, GF_FS_SEP_NAME);
2614 : const char *dst_args, *trailer_args=NULL;
2615 349 : char *szDST = NULL;
2616 : char szSRC[100];
2617 :
2618 349 : if (ctx->sigfrag)
2619 6 : return;
2620 :
2621 343 : GF_DashStream *ds = rep->playback.udta;
2622 343 : if (ds->muxed_base) return;
2623 :
2624 :
2625 343 : ctx->check_connections = GF_TRUE;
2626 343 : gf_dynstrcat(&szDST, szInitURL, NULL);
2627 343 : if (ctx->out_path) {
2628 : char *rel = NULL;
2629 343 : if (ctx->do_m3u8 && ds->hls_vp_name) {
2630 0 : char *tmp = gf_url_concatenate(ctx->out_path, ds->hls_vp_name);
2631 0 : if (tmp) {
2632 0 : rel = gf_url_concatenate(tmp, szInitURL);
2633 0 : gf_free(tmp);
2634 : }
2635 : }
2636 0 : if (!rel)
2637 343 : rel = gf_url_concatenate(ctx->out_path, szInitURL);
2638 343 : if (rel) {
2639 343 : gf_free(szDST);
2640 343 : szDST = rel;
2641 : }
2642 : }
2643 343 : if (ds->tci) {
2644 0 : char *tmp = szDST;
2645 0 : szDST = NULL;
2646 0 : gf_dynstrcat(&szDST, "gcryp://", NULL);
2647 0 : gf_dynstrcat(&szDST, tmp, NULL);
2648 0 : gf_free(tmp);
2649 : }
2650 :
2651 343 : sprintf(szSRC, "%cgfopt", sep_args);
2652 343 : gf_dynstrcat(&szDST, szSRC, NULL);
2653 :
2654 343 : dst_args = gf_filter_get_dst_args(filter);
2655 343 : if (dst_args) {
2656 : char szKey[20], *sep;
2657 : sprintf(szSRC, "%c", sep_args);
2658 282 : gf_dynstrcat(&szDST, szSRC, NULL);
2659 :
2660 282 : gf_dynstrcat(&szDST, dst_args, NULL);
2661 : sprintf(szKey, "%c%c", sep_args, sep_args);
2662 282 : sep = strstr(szDST, szKey);
2663 282 : if (sep) {
2664 1 : sep[0] = 0;
2665 1 : trailer_args = strstr(dst_args, szKey);
2666 : }
2667 : //look for frag arg
2668 : sprintf(szKey, "%cfrag", sep_args);
2669 282 : if (strstr(dst_args, szKey)) has_frag = GF_TRUE;
2670 : else {
2671 : sprintf(szKey, "%csfrag", sep_args);
2672 282 : if (strstr(dst_args, szKey)) has_frag = GF_TRUE;
2673 : }
2674 : //look for subs_sidx arg
2675 : sprintf(szKey, "%csubs_sidx", sep_args);
2676 282 : if (strstr(dst_args, szKey)) has_subs = GF_TRUE;
2677 :
2678 : sprintf(szKey, "%cstrun", sep_args);
2679 282 : if (strstr(dst_args, szKey)) has_strun = GF_TRUE;
2680 :
2681 : sprintf(szKey, "%cvodcache", sep_args);
2682 282 : if (strstr(dst_args, szKey)) has_vodcache = GF_TRUE;
2683 :
2684 : sprintf(szKey, "%ccmaf", sep_args);
2685 282 : if (strstr(dst_args, szKey)) has_cmaf = GF_TRUE;
2686 : }
2687 :
2688 343 : if (trash_init) {
2689 : sprintf(szSRC, "%cnoinit", sep_args);
2690 56 : gf_dynstrcat(&szDST, szSRC, NULL);
2691 : }
2692 343 : if (!has_frag) {
2693 : sprintf(szSRC, "%cfrag", sep_args);
2694 343 : gf_dynstrcat(&szDST, szSRC, NULL);
2695 : }
2696 343 : if (!ctx->forward_mode) {
2697 316 : if (!has_subs && ctx->sseg) {
2698 4 : sprintf(szSRC, "%csubs_sidx%c0", sep_args, sep_name);
2699 4 : gf_dynstrcat(&szDST, szSRC, NULL);
2700 : }
2701 316 : if (ctx->cues && !has_strun) {
2702 : sprintf(szSRC, "%cstrun", sep_args);
2703 10 : gf_dynstrcat(&szDST, szSRC, NULL);
2704 : }
2705 316 : if (ctx->styp) {
2706 : sprintf(szSRC, "%cstyp=%s", sep_args, ctx->styp);
2707 0 : gf_dynstrcat(&szDST, szSRC, NULL);
2708 : }
2709 : }
2710 :
2711 : //override xps inband declaration in args
2712 343 : sprintf(szSRC, "%cxps_inband%c%s", sep_args, sep_name, ds->inband_params ? "all" : "no");
2713 343 : gf_dynstrcat(&szDST, szSRC, NULL);
2714 :
2715 343 : if (ctx->no_fragments_defaults) {
2716 : sprintf(szSRC, "%cnofragdef", sep_args );
2717 10 : gf_dynstrcat(&szDST, szSRC, NULL);
2718 : }
2719 343 : switch (ctx->pssh) {
2720 0 : case GF_DASH_PSSH_MPD:
2721 : sprintf(szSRC, "%cpsshs%cnone", sep_args, sep_name);
2722 : break;
2723 0 : case GF_DASH_PSSH_MOOF:
2724 : case GF_DASH_PSSH_MOOF_MPD:
2725 : sprintf(szSRC, "%cpsshs%cmoof", sep_args, sep_name);
2726 : break;
2727 343 : default:
2728 : sprintf(szSRC, "%cpsshs%cmoov", sep_args, sep_name);
2729 : break;
2730 : }
2731 343 : gf_dynstrcat(&szDST, szSRC, NULL);
2732 :
2733 : //patch for old arch: make sure we don't have any extra free box before the sidx
2734 : //we could also use vodcache=insert but this might break http outputs
2735 343 : if (gf_sys_old_arch_compat() && !has_vodcache && ctx->sseg) {
2736 : sprintf(szSRC, "%cvodcache=on", sep_args );
2737 19 : if (!strstr(szDST, szSRC))
2738 19 : gf_dynstrcat(&szDST, szSRC, NULL);
2739 : }
2740 :
2741 : //we don't append mime in case of raw streams, raw format (writegen doesn't use mime types for raw media, only file ext)
2742 343 : if ((ctx->muxtype!=DASHER_MUX_RAW) || (ds->codec_id != GF_CODECID_RAW)) {
2743 343 : sprintf(szSRC, "%cmime=%s", sep_args, rep->mime_type);
2744 343 : gf_dynstrcat(&szDST, szSRC, NULL);
2745 : }
2746 :
2747 343 : if (ds->moof_sn>1) {
2748 : sprintf(szSRC, "%cmsn%c%d", sep_args, sep_name, ds->moof_sn);
2749 36 : gf_dynstrcat(&szDST, szSRC, NULL);
2750 : }
2751 343 : if (ds->moof_sn_inc>1) {
2752 : sprintf(szSRC, "%cmsninc%c%d", sep_args, sep_name, ds->moof_sn_inc);
2753 41 : gf_dynstrcat(&szDST, szSRC, NULL);
2754 : }
2755 343 : if (ds->sscale) {
2756 : sprintf(szSRC, "%cmoovts%c-1", sep_args, sep_name);
2757 2 : gf_dynstrcat(&szDST, szSRC, NULL);
2758 : }
2759 :
2760 343 : if (!has_cmaf && ctx->cmaf) {
2761 0 : sprintf(szSRC, "%ccmaf%c%s", sep_args, sep_name, (ctx->cmaf==DASHER_CMAF_CMF2) ? "cmf2" : "cmfc");
2762 0 : gf_dynstrcat(&szDST, szSRC, NULL);
2763 : }
2764 :
2765 :
2766 343 : if (trailer_args)
2767 1 : gf_dynstrcat(&szDST, trailer_args, NULL);
2768 :
2769 343 : ds->dst_filter = gf_filter_connect_destination(filter, szDST, &e);
2770 343 : gf_free(szDST);
2771 343 : szDST = NULL;
2772 343 : if (e) {
2773 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't create output file %s: %s\n", szInitURL, gf_error_to_string(e) ));
2774 0 : ctx->in_error = GF_TRUE;
2775 : return;
2776 : }
2777 : //reset any sourceID given in the dst_arg and assign sourceID to be the dasher filter
2778 343 : sprintf(szSRC, "MuxSrc%cdasher_%p", sep_name, ds->dst_filter);
2779 343 : gf_filter_reset_source(ds->dst_filter);
2780 343 : gf_filter_set_source(ds->dst_filter, filter, szSRC);
2781 :
2782 343 : if (ds->tci && !trash_init) {
2783 : //push NULL key, we are not encrypting the init segment
2784 0 : gf_cryptfout_push_key(ds->dst_filter, NULL, NULL);
2785 : }
2786 : }
2787 :
2788 3 : static void dasher_gather_deps(GF_DasherCtx *ctx, u32 dependency_id, GF_List *multi_tracks)
2789 : {
2790 3 : u32 i, count = gf_list_count(ctx->current_period->streams);
2791 11 : for (i=0; i<count; i++) {
2792 8 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
2793 8 : if (ds->id == dependency_id) {
2794 3 : if (ds->tile_base) continue;
2795 :
2796 : assert(ds->opid);
2797 3 : gf_list_insert(multi_tracks, ds->opid, 0);
2798 3 : if (ds->dep_id) dasher_gather_deps(ctx, ds->dep_id, multi_tracks);
2799 : }
2800 : }
2801 3 : }
2802 :
2803 16 : static void dasher_update_dep_list(GF_DasherCtx *ctx, GF_DashStream *ds, const char *ref_type)
2804 : {
2805 : u32 i, j, count, base_id;
2806 16 : GF_PropertyValue *p = (GF_PropertyValue *) gf_filter_pid_get_property_str(ds->opid, ref_type);
2807 16 : if (!p) return;
2808 0 : base_id = ds->dep_id ? ds->dep_id : ds->id;
2809 0 : count = gf_list_count(ctx->current_period->streams);
2810 0 : for (i=0; i<p->value.uint_list.nb_items; i++) {
2811 0 : for (j=0; j<count; j++) {
2812 0 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
2813 0 : if (a_ds->dep_id != base_id) continue;
2814 0 : if ((a_ds->id == p->value.uint_list.vals[i]) && a_ds->pid_id) {
2815 0 : p->value.uint_list.vals[i] = a_ds->pid_id;
2816 : }
2817 : }
2818 : }
2819 : }
2820 :
2821 364 : static void dasher_open_pid(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *ds, GF_List *multi_pids, Bool init_trashed)
2822 : {
2823 364 : GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
2824 : char szSRC[1024];
2825 :
2826 364 : if (ctx->sigfrag || ctx->in_error)
2827 9 : return;
2828 :
2829 : assert(!ds->opid);
2830 : assert(base_ds->dst_filter);
2831 :
2832 361 : if (ds->tile_base && !init_trashed) {
2833 6 : s32 res = gf_list_find(ctx->postponed_pids, ds);
2834 6 : if (res < 0) {
2835 3 : gf_list_add(ctx->postponed_pids, ds);
2836 3 : return;
2837 : } else {
2838 3 : gf_list_rem(ctx->postponed_pids, res);
2839 : }
2840 352 : } else if (!ds->tile_base) {
2841 352 : gf_list_del_item(ctx->postponed_pids, ds);
2842 : }
2843 :
2844 : //tile base not live profile, make sure all our deps are ready
2845 355 : if (ds->tile_base && !ctx->sseg) {
2846 3 : u32 i, count = gf_list_count(ds->complementary_streams);
2847 30 : for (i=0; i<count; i++) {
2848 27 : GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, i);
2849 : //dep not ready
2850 27 : if (!a_ds->opid) {
2851 0 : if (gf_list_find(ctx->postponed_pids, a_ds)<0) {
2852 0 : gf_list_add(ctx->postponed_pids, a_ds);
2853 : }
2854 0 : gf_list_del_item(ctx->postponed_pids, ds);
2855 0 : gf_list_add(ctx->postponed_pids, ds);
2856 0 : return;
2857 : }
2858 : }
2859 : }
2860 :
2861 355 : if (ctx->sigfrag)
2862 : return;
2863 :
2864 355 : sprintf(szSRC, "dasher_%p", base_ds->dst_filter);
2865 355 : ds->opid = gf_filter_pid_new(filter);
2866 :
2867 : #ifdef GPAC_64_BITS
2868 355 : ds->hls_ref_id = (u64) ds->opid;
2869 : #else
2870 : ds->hls_ref_id = (u64) ((u32) ds->opid);
2871 : #endif
2872 :
2873 355 : gf_filter_pid_copy_properties(ds->opid, ds->ipid);
2874 355 : if (!ds->muxed_base) {
2875 343 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("*"));
2876 343 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_MIME, &PROP_STRING(ds->rep->mime_type));
2877 : }
2878 355 : if (ds->nb_cues) {
2879 : u32 ncues = ds->nb_cues;
2880 10 : if ((ds->cues[0].sample_num>0) || (ds->cues[0].cts>0) || (ds->cues[0].dts>0))
2881 10 : ncues++;
2882 10 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_SEGMENTS, &PROP_UINT(ncues) );
2883 : }
2884 : //for route out
2885 355 : if (ctx->is_route) {
2886 4 : if (ctx->do_m3u8)
2887 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
2888 :
2889 4 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_REP_ID, &PROP_STRING( ds->rep->id ) );
2890 :
2891 :
2892 4 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_DUR, &PROP_FRAC( ds->dash_dur ) );
2893 : }
2894 :
2895 355 : gf_filter_pid_require_source_id(ds->opid);
2896 :
2897 355 : if (ctx->pssh == GF_DASH_PSSH_MPD) {
2898 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_CENC_PSSH, NULL);
2899 : }
2900 :
2901 :
2902 : //force PID ID
2903 355 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_ID, &PROP_UINT(ds->pid_id) );
2904 355 : if (ds->dep_pid_id)
2905 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DEPENDENCY_ID, &PROP_UINT(ds->dep_pid_id) );
2906 355 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_MUX_SRC, &PROP_STRING(szSRC) );
2907 355 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MODE, &PROP_UINT(ctx->sseg ? 2 : 1) );
2908 355 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_DUR, &PROP_FRAC(ds->dash_dur) );
2909 :
2910 355 : if (ds->id != ds->pid_id) {
2911 8 : dasher_update_dep_list(ctx, ds, "isom:scal");
2912 8 : dasher_update_dep_list(ctx, ds, "isom:sabt");
2913 : }
2914 :
2915 : /*timescale forced (bitstream switching) */
2916 355 : if (ds->force_timescale)
2917 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ds->force_timescale) );
2918 :
2919 355 : if (ds->rep && ds->rep->segment_template)
2920 57 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_TEMPLATE, &PROP_STRING(ds->rep->segment_template->media));
2921 298 : else if (ds->set && ds->set->segment_template)
2922 237 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_TEMPLATE, &PROP_STRING(ds->set->segment_template->media));
2923 :
2924 355 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_BITRATE, &PROP_UINT(ds->bitrate));
2925 355 : gf_filter_pid_set_property(ds->opid, GF_PROP_PCK_FILENAME, &PROP_STRING(ds->init_seg));
2926 :
2927 355 : if (ds->rep && ds->rep->codecs)
2928 343 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_CODEC, &PROP_STRING(ds->rep->codecs));
2929 :
2930 :
2931 355 : if (multi_pids) {
2932 0 : s32 idx = 1+gf_list_find(multi_pids, ds->ipid);
2933 : assert(idx>0);
2934 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_PID, &PROP_POINTER(multi_pids) );
2935 0 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_PID_IDX, &PROP_UINT(idx) );
2936 : }
2937 :
2938 :
2939 355 : if (ds->tile_base && !ctx->sseg && !ctx->sfile) {
2940 3 : u32 i, count = gf_list_count(ds->complementary_streams);
2941 3 : if (!ds->multi_tracks) ds->multi_tracks = gf_list_new();
2942 3 : gf_list_reset(ds->multi_tracks);
2943 :
2944 : //gather all streams depending on our base
2945 30 : for (i=0; i<count; i++) {
2946 27 : GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, i);
2947 : assert(a_ds->opid);
2948 27 : gf_list_add(ds->multi_tracks, a_ds->opid);
2949 : }
2950 3 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_TRACK, &PROP_POINTER(ds->multi_tracks) );
2951 : }
2952 355 : if (ds->dep_id && !init_trashed) {
2953 3 : if (!ds->multi_tracks) ds->multi_tracks = gf_list_new();
2954 3 : gf_list_reset(ds->multi_tracks);
2955 3 : dasher_gather_deps(ctx, ds->dep_id, ds->multi_tracks);
2956 3 : if (gf_list_count(ds->multi_tracks)) {
2957 3 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_DASH_MULTI_TRACK, &PROP_POINTER(ds->multi_tracks) );
2958 : } else {
2959 0 : gf_list_del(ds->multi_tracks);
2960 0 : ds->multi_tracks = NULL;
2961 : }
2962 : }
2963 :
2964 355 : if (ctx->llhls) {
2965 12 : gf_filter_pid_set_property(ds->opid, GF_PROP_PID_LLHLS, &PROP_UINT(ctx->llhls) );
2966 : }
2967 : }
2968 :
2969 880 : static Bool dasher_template_use_source_url(const char *template)
2970 : {
2971 880 : if (strstr(template, "$File$") != NULL) return GF_TRUE;
2972 111 : else if (strstr(template, "$FSRC$") != NULL) return GF_TRUE;
2973 111 : else if (strstr(template, "$SourcePath$") != NULL) return GF_TRUE;
2974 111 : else if (strstr(template, "$FURL$") != NULL) return GF_TRUE;
2975 111 : else if (strstr(template, "$URL$") != NULL) return GF_TRUE;
2976 111 : return GF_FALSE;
2977 : }
2978 :
2979 19 : static void dasher_set_content_components(GF_DashStream *ds)
2980 : {
2981 : GF_MPD_ContentComponent *component;
2982 19 : GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
2983 :
2984 19 : GF_SAFEALLOC(component, GF_MPD_ContentComponent);
2985 19 : if (!component) return;
2986 :
2987 19 : component->id = ds->pid_id;
2988 19 : switch (ds->stream_type) {
2989 0 : case GF_STREAM_TEXT:
2990 0 : component->type = gf_strdup("text");
2991 0 : break;
2992 12 : case GF_STREAM_VISUAL:
2993 12 : component->type = gf_strdup("video");
2994 12 : break;
2995 7 : case GF_STREAM_AUDIO:
2996 7 : component->type = gf_strdup("audio");
2997 7 : break;
2998 0 : case GF_STREAM_SCENE:
2999 : case GF_STREAM_OD:
3000 : default:
3001 0 : component->type = gf_strdup("application");
3002 0 : break;
3003 : }
3004 : /*if lang not specified at adaptationSet level, put it here*/
3005 19 : if (!base_ds->set->lang && ds->lang && strcmp(ds->lang, "und")) {
3006 0 : component->lang = gf_strdup(ds->lang);
3007 : }
3008 19 : gf_list_add(base_ds->set->content_component, component);
3009 : }
3010 :
3011 : typedef struct
3012 : {
3013 : char *tpl;
3014 : u32 nb_reused;
3015 : } DashTemplateRecord;
3016 :
3017 346 : static u32 dasher_check_template_reuse(GF_DasherCtx *ctx, const char *tpl)
3018 : {
3019 : DashTemplateRecord *tr;
3020 346 : u32 i, count = gf_list_count(ctx->tpl_records);
3021 362 : for (i=0; i<count; i++) {
3022 362 : tr = gf_list_get(ctx->tpl_records, i);
3023 362 : if (!strcmp(tr->tpl, tpl)) {
3024 0 : tr->nb_reused++;
3025 : return tr->nb_reused;
3026 : }
3027 : }
3028 346 : GF_SAFEALLOC(tr, DashTemplateRecord)
3029 346 : tr->tpl = gf_strdup(tpl);
3030 346 : gf_list_add(ctx->tpl_records, tr);
3031 : return 0;
3032 : }
3033 :
3034 317 : static void dasher_setup_sources(GF_Filter *filter, GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
3035 : {
3036 : char szDASHTemplate[GF_MAX_PATH];
3037 : char szTemplate[GF_MAX_PATH];
3038 : char szSegmentName[GF_MAX_PATH];
3039 : char szInitSegmentTemplate[GF_MAX_PATH];
3040 : char szInitSegmentFilename[GF_MAX_PATH];
3041 : char szIndexSegmentName[GF_MAX_PATH];
3042 : char szSetFileSuffix[200], szDASHSuffix[220];
3043 : const char *template = NULL;
3044 : u32 as_id = 0;
3045 : Bool single_template = GF_TRUE;
3046 : u32 i, j, count, nb_base, nb_streams;
3047 : GF_List *multi_pids = NULL;
3048 : u32 set_timescale = 0;
3049 : Bool init_template_done=GF_FALSE;
3050 317 : Bool use_inband = (ctx->bs_switch==DASHER_BS_SWITCH_INBAND) ? GF_TRUE : GF_FALSE;
3051 : Bool template_use_source = GF_FALSE;
3052 : Bool split_rep_names = GF_FALSE;
3053 : Bool split_set_names = GF_FALSE;
3054 : u32 force_ds_id;
3055 : GF_DashStream *ds;
3056 317 : GF_MPD_Representation *rep = gf_list_get(set->representations, 0);
3057 317 : if (!rep) {
3058 : assert(0);
3059 0 : return;
3060 : }
3061 317 : ds = rep->playback.udta;
3062 :
3063 317 : count = gf_list_count(set->representations);
3064 :
3065 317 : if (!ctx->sigfrag) {
3066 : assert(ctx->template);
3067 311 : template = ((GF_DashStream *)set->udta)->template;
3068 : }
3069 :
3070 673 : for (i=0; i<count; i++) {
3071 356 : rep = gf_list_get(set->representations, i);
3072 356 : ds = rep->playback.udta;
3073 356 : if (!ds->template && !template) {}
3074 27 : else if (ds->template && template && !strcmp(ds->template, template) ) {
3075 : } else {
3076 : single_template = GF_FALSE;
3077 : }
3078 356 : if (ds->template) template_use_source = dasher_template_use_source_url(ds->template);
3079 :
3080 356 : if (template_use_source) {
3081 : single_template = GF_FALSE;
3082 : }
3083 :
3084 356 : if (ds->as_id && !as_id)
3085 : as_id = ds->as_id;
3086 :
3087 356 : if (ds->fps.den && ( (ds->fps.num*set->max_framerate.den) >= (s32) (set->max_framerate.num*ds->fps.den) )) {
3088 241 : set->max_framerate.num = ds->fps.num;
3089 241 : set->max_framerate.den = ds->fps.den;
3090 241 : gf_media_get_reduced_frame_rate(&set->max_framerate.num, &set->max_framerate.den);
3091 : }
3092 356 : if (ds->width && ds->height) {
3093 265 : if (!set->par) {
3094 0 : GF_SAFEALLOC(set->par, GF_MPD_Fractional);
3095 : }
3096 265 : if (set->par) {
3097 265 : set->par->num = ds->width;
3098 265 : set->par->den = ds->height;
3099 265 : gf_media_reduce_aspect_ratio(&set->par->num, &set->par->den);
3100 : }
3101 : }
3102 : }
3103 317 : if (!template) template = ctx->template;
3104 :
3105 317 : if (as_id) {
3106 0 : set->id = ds->as_id;
3107 : }
3108 317 : if (ctx->sseg) {
3109 21 : set->segment_alignment = GF_TRUE;
3110 21 : set->starts_with_sap = 1;
3111 : }
3112 :
3113 317 : if (count==1)
3114 : single_template = GF_TRUE;
3115 38 : else if (single_template) {
3116 : //for regular reps, if we depend on filename we cannot mutualize the template
3117 32 : if (dasher_template_use_source_url(template) ) {
3118 : single_template = GF_FALSE;
3119 : }
3120 : //and for scalable reps, if we don't have bandwidth /repID we cannot mutualize the template
3121 3 : else if (gf_list_count(ds->complementary_streams) ) {
3122 0 : if (strstr(template, "$Bandwidth$") != NULL) single_template = GF_FALSE;
3123 0 : else if (strstr(template, "$RepresentationId$") != NULL) single_template = GF_FALSE;
3124 : }
3125 : }
3126 :
3127 317 : if (set->lang) gf_free(set->lang);
3128 317 : set->lang = gf_strdup(ds->lang ? ds->lang : "und");
3129 :
3130 : //check all streams in active period not in this set
3131 317 : force_ds_id = ds->id;
3132 317 : nb_streams = gf_list_count(ctx->current_period->streams);
3133 763 : for (i=0; i<nb_streams; i++) {
3134 : char *frag_uri;
3135 : u32 len1, len2;
3136 522 : GF_DashStream *ads = gf_list_get(ctx->current_period->streams, i);
3137 :
3138 522 : if (force_ds_id && (ads != ds) && (ads->id == ds->id)) {
3139 : force_ds_id = 0;
3140 : }
3141 :
3142 522 : if (ads->set == set) continue;
3143 217 : frag_uri = strrchr(ds->src_url, '#');
3144 217 : if (frag_uri) len1 = (u32) (frag_uri-1 - ds->src_url);
3145 217 : else len1 = (u32) strlen(ds->src_url);
3146 217 : frag_uri = strrchr(ads->src_url, '#');
3147 217 : if (frag_uri) len2 = (u32) (frag_uri-1 - ads->src_url);
3148 217 : else len2 = (u32) strlen(ads->src_url);
3149 :
3150 217 : if ((len1==len2) && !strncmp(ds->src_url, ads->src_url, len1)) {
3151 : split_set_names = GF_TRUE;
3152 : break;
3153 : }
3154 : }
3155 :
3156 317 : if (ctx->timescale>0) {
3157 0 : set_timescale = ctx->timescale;
3158 : } else {
3159 : u32 first_timescale;
3160 317 : rep = gf_list_get(set->representations, 0);
3161 317 : ds = rep->playback.udta;
3162 317 : first_timescale = ds->timescale;
3163 351 : for (i=1; i<count; i++) {
3164 39 : rep = gf_list_get(set->representations, i);
3165 39 : ds = rep->playback.udta;
3166 39 : if (ds->timescale != first_timescale) {
3167 : //we cannot use a single template if enforcing timescales which are not identical
3168 : single_template = GF_FALSE;
3169 : break;
3170 : }
3171 : }
3172 : }
3173 :
3174 655 : for (i=0; i<count; i++) {
3175 350 : if (ctx->sigfrag)
3176 : break;
3177 :
3178 344 : rep = gf_list_get(set->representations, i);
3179 344 : ds = rep->playback.udta;
3180 :
3181 344 : if (!dasher_template_use_source_url(template))
3182 45 : continue;
3183 :
3184 299 : if (ds->muxed_base)
3185 8 : continue;
3186 :
3187 315 : for (j=i+1; j<count; j++) {
3188 : const GF_PropertyValue *p1, *p2;
3189 : GF_DashStream *a_ds;
3190 30 : rep = gf_list_get(set->representations, j);
3191 30 : a_ds = rep->playback.udta;
3192 :
3193 30 : if (a_ds->muxed_base == ds)
3194 8 : continue;
3195 :
3196 22 : p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_FILEPATH);
3197 22 : p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_FILEPATH);
3198 22 : if (p1 && p2 && gf_props_equal(p1, p2)) split_rep_names = GF_TRUE;
3199 16 : else if (!p1 && !p2) split_rep_names = GF_TRUE;
3200 22 : p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
3201 22 : p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_URL);
3202 22 : if (p1 && p2 && gf_props_equal(p1, p2)) split_rep_names = GF_TRUE;
3203 16 : else if (!p1 && !p2) split_rep_names = GF_TRUE;
3204 :
3205 16 : if (split_rep_names) break;
3206 : }
3207 291 : if (split_rep_names) break;
3208 : }
3209 :
3210 317 : if (split_set_names) {
3211 76 : if (!force_ds_id) {
3212 17 : if (split_rep_names || !ds->split_set_names)
3213 11 : force_ds_id = ds->id;
3214 : else
3215 6 : force_ds_id = gf_list_find(ctx->pids, ds) + 1;
3216 : }
3217 : sprintf(szSetFileSuffix, "_track%d_", force_ds_id);
3218 : }
3219 :
3220 : //assign PID IDs - we assume only one component of a given media type per adaptation set
3221 : //and assign the same PID ID for each component of the same type
3222 : //we could refine this using roles, but most HAS solutions don't use roles at the mulitplexed level
3223 356 : for (i=0; i<count; i++) {
3224 : Bool is_bs_switching;
3225 356 : rep = gf_list_get(set->representations, i);
3226 356 : ds = rep->playback.udta;
3227 356 : if (ds->pid_id) continue;
3228 : //in bitstream switching mode, ensure each track in the set has the same ID
3229 : //except when tile merge is used
3230 351 : is_bs_switching = ds->tile_dep_id_merged ? GF_FALSE : set->bitstream_switching;
3231 342 : if (is_bs_switching) {
3232 28 : ctx->next_pid_id_in_period++;
3233 28 : ds->pid_id = ctx->next_pid_id_in_period;
3234 :
3235 45 : for (j=i+1; j<count; j++) {
3236 : GF_DashStream *a_ds;
3237 17 : rep = gf_list_get(set->representations, j);
3238 17 : a_ds = rep->playback.udta;
3239 17 : if (a_ds->pid_id) continue;
3240 17 : if (a_ds->dep_id) continue;
3241 8 : if (a_ds->stream_type == ds->stream_type) a_ds->pid_id = ds->pid_id;
3242 : }
3243 : }
3244 : //otherwise copy over the source PID
3245 : else {
3246 323 : ds->pid_id = ds->id;
3247 : }
3248 : }
3249 :
3250 356 : for (i=0; i<count; i++) {
3251 356 : rep = gf_list_get(set->representations, i);
3252 356 : ds = rep->playback.udta;
3253 356 : if (!ds->dep_id) continue;
3254 :
3255 49 : for (j=i+1; j<count; j++) {
3256 : GF_DashStream *a_ds;
3257 10 : rep = gf_list_get(set->representations, j);
3258 10 : a_ds = rep->playback.udta;
3259 10 : if (ds->dep_id == a_ds->id) {
3260 0 : ds->dep_pid_id = a_ds->pid_id;
3261 0 : break;
3262 : }
3263 : }
3264 : }
3265 :
3266 : //this is crude because we don't copy the properties, we just pass a list of pids to the destination muxer !!
3267 : //we should cleanup one of these days
3268 317 : if (set->bitstream_switching && (ctx->bs_switch==DASHER_BS_SWITCH_MULTI)) {
3269 0 : multi_pids = gf_list_new();
3270 0 : for (i=0; i<count; i++) {
3271 0 : rep = gf_list_get(set->representations, i);
3272 0 : ds = rep->playback.udta;
3273 0 : if (ds->owns_set) ds->multi_pids = multi_pids;
3274 0 : gf_list_add(multi_pids, ds->ipid);
3275 : }
3276 : }
3277 :
3278 317 : if (ctx->cp!=GF_DASH_CPMODE_REPRESENTATION) {
3279 317 : gf_mpd_del_list(set->content_protection, gf_mpd_descriptor_free, 0);
3280 317 : set->content_protection = dasher_get_content_protection_desc(ctx, NULL, set);
3281 : }
3282 :
3283 356 : for (i=0; i<count; i++) {
3284 : GF_Err e;
3285 : char szRawExt[20];
3286 : Bool use_dash_suffix = GF_FALSE;
3287 : const char *seg_ext, *init_ext, *idx_ext, *force_init_seg_tpl;
3288 : #if 0
3289 : GF_MPD_URL *force_init_seg_sl;
3290 : #endif
3291 : Bool skip_init = GF_FALSE;
3292 : GF_DashStream *tile_base_ds = NULL;
3293 : Bool is_bs_switch;
3294 : u32 reused_template_idx;
3295 : u32 init_template_mode = GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE;
3296 356 : rep = gf_list_get(set->representations, i);
3297 356 : ds = rep->playback.udta;
3298 :
3299 : //remove representations for streams muxed with others, but still open the output
3300 356 : if (ds->muxed_base) {
3301 10 : GF_DashStream *ds_set = set->udta;
3302 10 : gf_list_rem(set->representations, i);
3303 10 : i--;
3304 10 : count--;
3305 : assert(ds_set->nb_rep);
3306 10 : ds_set->nb_rep--;
3307 : assert(ds->muxed_base->dst_filter);
3308 10 : gf_list_transfer(ds->muxed_base->rep->audio_channels, rep->audio_channels);
3309 10 : gf_list_transfer(ds->muxed_base->rep->base_URLs, rep->base_URLs);
3310 10 : gf_list_transfer(ds->muxed_base->rep->content_protection , rep->content_protection);
3311 10 : gf_list_transfer(ds->muxed_base->rep->essential_properties , rep->essential_properties);
3312 10 : gf_list_transfer(ds->muxed_base->rep->frame_packing , rep->frame_packing);
3313 10 : if (rep->x_children) {
3314 0 : if (!ds->muxed_base->rep->x_children) ds->muxed_base->rep->x_children = gf_list_new();
3315 0 : gf_list_transfer(ds->muxed_base->rep->x_children, rep->x_children);
3316 : }
3317 10 : gf_list_transfer(ds->muxed_base->rep->supplemental_properties , rep->supplemental_properties);
3318 :
3319 10 : gf_mpd_representation_free(ds->rep);
3320 10 : ds->rep = NULL;
3321 :
3322 10 : if (!gf_list_count(ds->set->content_component)) {
3323 9 : dasher_set_content_components(ds->muxed_base);
3324 : }
3325 10 : dasher_set_content_components(ds);
3326 : assert(!multi_pids);
3327 : //open PID
3328 10 : dasher_open_pid(filter, ctx, ds, NULL, GF_FALSE);
3329 20 : continue;
3330 : }
3331 346 : if (ds->template) strcpy(szTemplate, ds->template);
3332 319 : else strcpy(szTemplate, ctx->template ? ctx->template : "");
3333 :
3334 346 : if (use_inband)
3335 0 : ds->inband_params = GF_TRUE;
3336 :
3337 : //if bitstream switching and templating, only set for the first one
3338 346 : if (i && set->bitstream_switching && ctx->stl && single_template) continue;
3339 :
3340 346 : if (!set_timescale) set_timescale = ds->timescale;
3341 :
3342 346 : if (ctx->timescale<0) ds->mpd_timescale = ds->timescale;
3343 346 : else ds->mpd_timescale = set_timescale;
3344 :
3345 346 : if (ds->nb_repeat && !ctx->loop) {
3346 6 : if (split_set_names) {
3347 6 : sprintf(szDASHSuffix, "%sp%d_", szSetFileSuffix, ds->nb_repeat+1);
3348 : } else {
3349 : sprintf(szDASHSuffix, "p%d_", ds->nb_repeat);
3350 : }
3351 : use_dash_suffix = GF_TRUE;
3352 340 : } else if (split_set_names) {
3353 : strcpy(szDASHSuffix, szSetFileSuffix);
3354 : use_dash_suffix = GF_TRUE;
3355 : }
3356 :
3357 : //resolve segment template
3358 346 : e = gf_filter_pid_resolve_file_template(ds->ipid, szTemplate, szDASHTemplate, 0, use_dash_suffix ? szDASHSuffix : NULL);
3359 346 : if (e) {
3360 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot resolve template name %s, cannot derive output segment names, disabling rep %s\n", szTemplate, ds->src_url));
3361 0 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
3362 0 : ds->done = 1;
3363 0 : continue;
3364 : }
3365 :
3366 :
3367 346 : if (single_template && ds->split_set_names && !use_dash_suffix) {
3368 : char szStrName[20];
3369 0 : sprintf(szStrName, "_set%d", 1 + gf_list_find(ctx->current_period->period->adaptation_sets, set) );
3370 : strcat(szDASHTemplate, szStrName);
3371 : }
3372 346 : else if (split_rep_names) {
3373 : char szStrName[20];
3374 12 : sprintf(szStrName, "_rep%d", 1 + gf_list_find(set->representations, ds->rep) );
3375 : strcat(szDASHTemplate, szStrName);
3376 : }
3377 :
3378 : idx_ext = NULL;
3379 346 : if (ctx->m2ts) {
3380 : seg_ext = init_ext = "ts";
3381 8 : if (!ctx->do_m3u8 && (ctx->subs_sidx>=0) )
3382 : idx_ext = "idx";
3383 : } else {
3384 : const char *def_ext = NULL;
3385 :
3386 : seg_ext = init_ext = NULL;
3387 :
3388 338 : if (ctx->muxtype==DASHER_MUX_TS) def_ext = "ts";
3389 338 : else if (ctx->muxtype==DASHER_MUX_MKV) def_ext = "mkv";
3390 336 : else if (ctx->muxtype==DASHER_MUX_WEBM) def_ext = "webm";
3391 336 : else if (ctx->muxtype==DASHER_MUX_OGG) def_ext = "ogg";
3392 336 : else if (ctx->muxtype==DASHER_MUX_RAW) {
3393 8 : char *ext = (char *) gf_codecid_file_ext(ds->codec_id);
3394 8 : if (ds->codec_id==GF_CODECID_RAW) {
3395 : const GF_PropertyValue *p;
3396 0 : if (ds->stream_type==GF_STREAM_VISUAL) {
3397 0 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PIXFMT);
3398 0 : if (p) ext = (char *) gf_pixel_fmt_sname(p->value.uint);
3399 : }
3400 0 : else if (ds->stream_type==GF_STREAM_AUDIO) {
3401 0 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_AUDIO_FORMAT);
3402 0 : if (p) ext = (char *) gf_audio_fmt_sname(p->value.uint);
3403 : }
3404 : }
3405 8 : strncpy(szRawExt, ext ? ext : "raw", 19);
3406 8 : szRawExt[19] = 0;
3407 8 : ext = strchr(szRawExt, '|');
3408 8 : if (ext) ext[0] = 0;
3409 : def_ext = szRawExt;
3410 : }
3411 :
3412 338 : if (ctx->segext && !stricmp(ctx->segext, "null")) {
3413 : seg_ext = NULL;
3414 : } else {
3415 : seg_ext = ctx->segext;
3416 338 : if (!seg_ext) seg_ext = def_ext ? def_ext : "m4s";
3417 : }
3418 338 : if (ctx->initext && !stricmp(ctx->initext, "null")) {
3419 : init_ext = NULL;
3420 : } else {
3421 : init_ext = ctx->initext;
3422 338 : if (!init_ext) init_ext = def_ext ? def_ext : "mp4";
3423 : }
3424 : }
3425 :
3426 346 : is_bs_switch = set->bitstream_switching;
3427 : //only used to force _init in default templates
3428 346 : if (ds->tile_base) is_bs_switch = GF_FALSE;
3429 :
3430 :
3431 : //check we are not reusing an existing template from previous periods, if so append a suffix
3432 : //we check the final init name
3433 346 : if (!ds->skip_tpl_reuse) {
3434 346 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, is_bs_switch, szInitSegmentFilename, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, ds->bitrate, 0, ctx->stl);
3435 346 : reused_template_idx = dasher_check_template_reuse(ctx, szInitSegmentFilename);
3436 346 : if (reused_template_idx) {
3437 : char szExName[20];
3438 : sprintf(szExName, "_r%d_", reused_template_idx);
3439 : strcat(szDASHTemplate, szExName);
3440 : }
3441 : }
3442 :
3443 : //get final segment template with path resolution - output file name is NULL, we already have solved this
3444 346 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE_WITH_PATH, is_bs_switch, szSegmentName, ds->rep_id, NULL, szDASHTemplate, seg_ext, 0, 0, 0, ctx->stl);
3445 346 : ds->seg_template = gf_strdup(szSegmentName);
3446 :
3447 : //get final segment template - output file name is NULL, we already have solved this
3448 346 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE, is_bs_switch, szSegmentName, ds->rep_id, NULL, szDASHTemplate, seg_ext, 0, 0, 0, ctx->stl);
3449 :
3450 : //get index templates
3451 346 : if (idx_ext) {
3452 1 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX_TEMPLATE_WITH_PATH, is_bs_switch, szIndexSegmentName, ds->rep_id, NULL, szDASHTemplate, idx_ext, 0, 0, 0, ctx->stl);
3453 1 : ds->idx_template = gf_strdup(szIndexSegmentName);
3454 :
3455 1 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX_TEMPLATE, is_bs_switch, szIndexSegmentName, ds->rep_id, NULL, szDASHTemplate, idx_ext, 0, 0, 0, ctx->stl);
3456 : }
3457 :
3458 : //get final init name - output file name is NULL, we already have solved this
3459 346 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, is_bs_switch, szInitSegmentFilename, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, ds->bitrate, 0, ctx->stl);
3460 :
3461 : //get final init template name - output file name is NULL, we already have solved this
3462 346 : gf_media_mpd_format_segment_name(init_template_mode, is_bs_switch, szInitSegmentTemplate, ds->rep_id, NULL, szDASHTemplate, init_ext, 0, 0, 0, ctx->stl);
3463 :
3464 346 : if (ctx->sigfrag) {
3465 6 : if (ctx->template || ds->template) {
3466 : } else {
3467 5 : strcpy(szInitSegmentFilename, gf_file_basename(ds->src_url));
3468 5 : strcpy(szSegmentName, gf_file_basename(ds->src_url));
3469 : }
3470 : }
3471 :
3472 346 : if (ctx->store_seg_states) {
3473 : assert(!ds->pending_segment_states);
3474 74 : ds->pending_segment_states = gf_list_new();
3475 : }
3476 : /* baseURLs */
3477 346 : nb_base = ds->p_base_url ? ds->p_base_url->value.string_list.nb_items : 0;
3478 347 : for (j=0; j<nb_base; j++) {
3479 : GF_MPD_BaseURL *base_url;
3480 1 : char *url = ds->p_base_url->value.string_list.vals[j];
3481 1 : GF_SAFEALLOC(base_url, GF_MPD_BaseURL);
3482 1 : if (base_url) {
3483 1 : base_url->URL = gf_strdup(url);
3484 1 : gf_list_add(rep->base_URLs, base_url);
3485 : }
3486 : }
3487 :
3488 : force_init_seg_tpl = NULL;
3489 : #if 0
3490 : force_init_seg_sl = NULL;
3491 : #endif
3492 346 : if (ds->codec_id==GF_CODECID_HEVC_TILES) {
3493 36 : tile_base_ds = get_base_ds(ctx, ds);
3494 : skip_init = GF_TRUE;
3495 36 : if (tile_base_ds->rep->segment_template) force_init_seg_tpl = tile_base_ds->rep->segment_template->initialization;
3496 0 : if (!force_init_seg_tpl && tile_base_ds->set->segment_template) force_init_seg_tpl = tile_base_ds->set->segment_template->initialization;
3497 :
3498 : #if 0
3499 : if (tile_base_ds->rep->segment_list) force_init_seg_sl = tile_base_ds->rep->segment_list->initialization_segment;
3500 : if (!force_init_seg_sl && tile_base_ds->set->segment_list) force_init_seg_sl = tile_base_ds->set->segment_list->initialization_segment;
3501 : #endif
3502 : }
3503 346 : if (ctx->m2ts) skip_init = GF_TRUE;
3504 338 : else if (ctx->muxtype==DASHER_MUX_RAW) skip_init = GF_TRUE;
3505 330 : else if (ctx->muxtype==DASHER_MUX_TS) skip_init = GF_TRUE;
3506 :
3507 :
3508 : //forward mode, change segment names
3509 346 : if (ctx->forward_mode) {
3510 27 : u32 k, nb_pids = gf_list_count(ctx->pids);
3511 : char *src = NULL;
3512 27 : const GF_PropertyValue *p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PCK_FILENAME);
3513 :
3514 27 : if (!p || !p->value.string) {
3515 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL in forward mode, cannot forward\n"));
3516 0 : ctx->in_error = GF_TRUE;
3517 0 : return;
3518 : }
3519 : src = p->value.string;
3520 :
3521 81 : for (k=0; k<nb_pids; k++) {
3522 81 : GF_DashStream *a_ds = gf_list_get(ctx->pids, k);
3523 81 : if (ds == a_ds) continue;
3524 54 : if (!a_ds->dst_filter) continue;
3525 :
3526 27 : p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PCK_FILENAME);
3527 27 : if (!p || !p->value.string) {
3528 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL in forward mode, cannot forward\n"));
3529 0 : ctx->in_error = GF_TRUE;
3530 0 : return;
3531 : }
3532 : //same init segment used (bs switching)
3533 27 : if (!strcmp(p->value.string, src))
3534 : skip_init = GF_TRUE;
3535 : }
3536 : strcpy(szInitSegmentFilename, src);
3537 : strcpy(szInitSegmentTemplate, src);
3538 :
3539 27 : if (ctx->tpl) {
3540 27 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_TEMPLATE);
3541 27 : if (!p || !p->value.string) {
3542 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source template in forward mode, cannot forward\n"));
3543 0 : ctx->in_error = GF_TRUE;
3544 0 : return;
3545 : }
3546 : strcpy(szSegmentName, p->value.string);
3547 : }
3548 : }
3549 :
3550 346 : ds->init_seg = gf_strdup(szInitSegmentFilename);
3551 :
3552 : //we use segment template
3553 346 : if (ctx->tpl) {
3554 : GF_MPD_SegmentTemplate *seg_template;
3555 289 : u32 start_number = ds->startNumber ? ds->startNumber : 1;
3556 289 : u64 seg_duration = (u64)(ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
3557 :
3558 : //first rep in set and bs switching or single template, create segment template at set level
3559 289 : if (!i && (set->bitstream_switching || single_template) ) {
3560 : init_template_done = GF_TRUE;
3561 : seg_template = NULL;
3562 247 : if (!skip_init || force_init_seg_tpl || single_template) {
3563 245 : GF_SAFEALLOC(seg_template, GF_MPD_SegmentTemplate);
3564 245 : if (seg_template) {
3565 245 : if (skip_init) {
3566 35 : seg_template->initialization = force_init_seg_tpl ? gf_strdup(force_init_seg_tpl) : NULL;
3567 35 : seg_template->hls_init_name = force_init_seg_tpl ? tile_base_ds->init_seg : NULL;
3568 : } else {
3569 210 : seg_template->initialization = gf_strdup(szInitSegmentTemplate);
3570 210 : seg_template->hls_init_name = ds->init_seg;
3571 : }
3572 : }
3573 : }
3574 247 : set->segment_template = seg_template;
3575 :
3576 247 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
3577 :
3578 247 : if (single_template) {
3579 233 : seg_template->media = gf_strdup(szSegmentName);
3580 233 : if (ds->idx_template)
3581 0 : seg_template->index = gf_strdup(szIndexSegmentName);
3582 :
3583 233 : seg_template->timescale = ds->mpd_timescale;
3584 233 : seg_template->start_number = start_number;
3585 233 : seg_template->duration = seg_duration;
3586 :
3587 233 : if (ctx->asto>0) {
3588 2 : seg_template->availability_time_offset = ctx->asto;
3589 : }
3590 14 : } else if (seg_template) {
3591 12 : seg_template->start_number = (u32)-1;
3592 : }
3593 : }
3594 : //non-first rep in set and single template, only open destination
3595 289 : if (i && single_template) {
3596 1 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, (set->bitstream_switching || skip_init) ? GF_TRUE : GF_FALSE);
3597 : }
3598 : //first rep in set and no bs switching or mutliple templates, create segment template at rep level
3599 288 : else if (i || !single_template) {
3600 55 : GF_SAFEALLOC(seg_template, GF_MPD_SegmentTemplate);
3601 55 : if (seg_template) {
3602 55 : rep->segment_template = seg_template;
3603 55 : if (!init_template_done) {
3604 29 : if (skip_init) {
3605 0 : seg_template->initialization = force_init_seg_tpl ? gf_strdup(force_init_seg_tpl) : NULL;
3606 0 : seg_template->hls_init_name = force_init_seg_tpl ? tile_base_ds->init_seg : NULL;
3607 : } else {
3608 29 : seg_template->initialization = gf_strdup(szInitSegmentTemplate);
3609 29 : seg_template->hls_init_name = ds->init_seg;
3610 : }
3611 29 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
3612 26 : } else if (i) {
3613 12 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, (set->bitstream_switching || skip_init) ? GF_TRUE : GF_FALSE);
3614 : }
3615 55 : seg_template->media = gf_strdup(szSegmentName);
3616 55 : if (ds->idx_template)
3617 1 : seg_template->index = gf_strdup(szIndexSegmentName);
3618 55 : seg_template->duration = seg_duration;
3619 55 : seg_template->timescale = ds->mpd_timescale;
3620 55 : seg_template->start_number = start_number;
3621 55 : if (ctx->asto > 0) {
3622 0 : seg_template->availability_time_offset = ctx->asto;
3623 : }
3624 : }
3625 : }
3626 : }
3627 : /*we are using a single file or segment, use base url*/
3628 57 : else if (ctx->sseg || ctx->sfile) {
3629 : GF_MPD_BaseURL *baseURL;
3630 : /* char *segext = (ctx->segext && !stricmp(ctx->segext, "null")) ? NULL : "mp4";
3631 : if (ctx->m2ts) segext = "ts";
3632 :
3633 : //use GF_DASH_TEMPLATE_INITIALIZATION_SKIPINIT to get rid of default "init" added for init templates
3634 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION, set->bitstream_switching, szInitSegmentName, ds->rep_id, NULL, szDASHTemplate, segext, 0, 0, 0, ctx->stl);
3635 : */
3636 :
3637 55 : if (ds->init_seg) gf_free(ds->init_seg);
3638 55 : ds->init_seg = gf_strdup(szInitSegmentFilename);
3639 :
3640 55 : GF_SAFEALLOC(baseURL, GF_MPD_BaseURL);
3641 55 : if (!baseURL) continue;
3642 :
3643 55 : if (!rep->base_URLs) rep->base_URLs = gf_list_new();
3644 55 : gf_list_add(rep->base_URLs, baseURL);
3645 :
3646 55 : if (ctx->sseg) {
3647 : GF_MPD_SegmentBase *segment_base;
3648 21 : baseURL->URL = gf_strdup(szInitSegmentFilename);
3649 21 : GF_SAFEALLOC(segment_base, GF_MPD_SegmentBase);
3650 21 : if (!segment_base) continue;
3651 21 : rep->segment_base = segment_base;
3652 21 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, GF_FALSE);
3653 : } else {
3654 : GF_MPD_SegmentList *seg_list;
3655 34 : GF_SAFEALLOC(seg_list, GF_MPD_SegmentList);
3656 34 : if (!seg_list) continue;
3657 34 : GF_SAFEALLOC(seg_list->initialization_segment, GF_MPD_URL);
3658 34 : if (!seg_list->initialization_segment) continue;
3659 34 : seg_list->start_number = (u32) -1;
3660 34 : baseURL->URL = gf_strdup(szInitSegmentFilename);
3661 34 : seg_list->dasher_segment_name = gf_strdup(szSegmentName);
3662 34 : seg_list->timescale = ds->mpd_timescale;
3663 34 : seg_list->duration = (u64) (ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
3664 34 : seg_list->segment_URLs = gf_list_new();
3665 34 : rep->segment_list = seg_list;
3666 34 : ds->pending_segment_urls = gf_list_new();
3667 :
3668 34 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
3669 : }
3670 : }
3671 : //no template, no single file, we need a file list
3672 : else {
3673 : GF_MPD_SegmentList *seg_list;
3674 2 : GF_SAFEALLOC(seg_list, GF_MPD_SegmentList);
3675 2 : if (!seg_list) continue;
3676 :
3677 2 : if (!skip_init) {
3678 1 : GF_SAFEALLOC(seg_list->initialization_segment, GF_MPD_URL);
3679 1 : if (!seg_list->initialization_segment) continue;
3680 :
3681 1 : seg_list->initialization_segment->sourceURL = gf_strdup(szInitSegmentFilename);
3682 : }
3683 2 : seg_list->dasher_segment_name = gf_strdup(szSegmentName);
3684 2 : seg_list->timescale = ds->mpd_timescale;
3685 2 : seg_list->segment_URLs = gf_list_new();
3686 2 : seg_list->start_number = (u32) -1;
3687 2 : seg_list->duration = (u64) (ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
3688 2 : rep->segment_list = seg_list;
3689 2 : ds->pending_segment_urls = gf_list_new();
3690 :
3691 2 : dasher_open_destination(filter, ctx, rep, szInitSegmentFilename, skip_init);
3692 : }
3693 :
3694 : //open PID
3695 346 : dasher_open_pid(filter, ctx, ds, multi_pids, skip_init);
3696 : }
3697 : }
3698 :
3699 1 : static void dahser_purge_segment_timeline(GF_DashStream *ds, GF_MPD_SegmentTimeline *stl, GF_DASH_SegmentContext *sctx)
3700 : {
3701 1 : GF_MPD_SegmentTimelineEntry *stl_e = gf_list_get(stl->entries, 0);
3702 1 : if (!stl_e) return;
3703 :
3704 1 : if (stl_e->repeat_count) {
3705 1 : stl_e->repeat_count--;
3706 1 : stl_e->start_time += stl_e->duration;
3707 : } else {
3708 0 : u64 start_time = stl_e->start_time + stl_e->duration;
3709 0 : gf_list_rem(stl->entries, 0);
3710 0 : gf_free(stl_e);
3711 0 : stl_e = gf_list_get(stl->entries, 0);
3712 0 : if (!stl_e) {
3713 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] No timeline entry after currently removed segment, cannot update start time\n" ));
3714 : return;
3715 : }
3716 :
3717 0 : if (!stl_e->start_time) stl_e->start_time = start_time;
3718 0 : else if (stl_e->start_time != start_time) {
3719 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Mismatch in segment timeline while purging, new start time "LLU" but entry indicates "LLU", keeping original one\n", start_time, stl_e->start_time ));
3720 : }
3721 : }
3722 : }
3723 :
3724 356 : static void dasher_purge_segments(GF_DasherCtx *ctx, u64 *period_dur)
3725 : {
3726 : Double min_valid_mpd_time;
3727 : u64 max_rem_dur = 0;
3728 : u32 i, count;
3729 :
3730 : //non-static mode, purge segments
3731 356 : if (ctx->dmode == GF_MPD_TYPE_STATIC) return;
3732 136 : if (ctx->tsb<0) return;
3733 :
3734 :
3735 114 : min_valid_mpd_time = (Double) *period_dur;
3736 114 : min_valid_mpd_time /= 1000;
3737 114 : min_valid_mpd_time -= ctx->tsb;
3738 : //negative asto, we produce segments earlier but we don't want to delete them before the asto
3739 114 : if (ctx->asto<0) {
3740 0 : min_valid_mpd_time += ctx->asto;
3741 : }
3742 114 : if (min_valid_mpd_time<=0) return;
3743 :
3744 11 : count = gf_list_count(ctx->current_period->streams);
3745 26 : for (i=0; i<count; i++) {
3746 15 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
3747 15 : if (ds->muxed_base) continue;
3748 11 : if (!ds->rep) continue;
3749 10 : if (!ds->rep->state_seg_list) continue;
3750 :
3751 3 : while (1) {
3752 : Double time, dur;
3753 : Bool seg_url_found = GF_FALSE;
3754 : Bool has_seg_list = GF_FALSE;
3755 13 : GF_DASH_SegmentContext *sctx = gf_list_get(ds->rep->state_seg_list, 0);
3756 13 : if (!sctx) break;
3757 : /*not yet flushed*/
3758 13 : if (gf_list_find(ds->pending_segment_states, sctx)>=0) break;
3759 8 : time = (Double) sctx->time;
3760 8 : time /= ds->mpd_timescale;
3761 8 : dur = (Double) sctx->dur;
3762 8 : dur/= ds->timescale;
3763 8 : if (time + dur >= min_valid_mpd_time) break;
3764 3 : if (sctx->filepath) {
3765 : GF_FilterEvent evt;
3766 3 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] removing segment %s\n", sctx->filename ? sctx->filename : sctx->filepath));
3767 :
3768 3 : GF_FEVT_INIT(evt, GF_FEVT_FILE_DELETE, ds->opid);
3769 3 : evt.file_del.url = sctx->filepath;
3770 3 : gf_filter_pid_send_event(ds->opid, &evt);
3771 3 : gf_free(sctx->filepath);
3772 : }
3773 :
3774 3 : if (ds->rep->segment_list) {
3775 0 : GF_MPD_SegmentURL *surl = gf_list_pop_front(ds->rep->segment_list->segment_URLs);
3776 : has_seg_list = GF_TRUE;
3777 : //can be NULL if we mutualize everything at AdaptatioSet level
3778 0 : if (surl) {
3779 0 : gf_mpd_segment_url_free(surl);
3780 : seg_url_found = GF_TRUE;
3781 : }
3782 : }
3783 : //not an else due to inheritance
3784 3 : if (ds->owns_set && ds->set->segment_list) {
3785 0 : GF_MPD_SegmentURL *surl = gf_list_pop_front(ds->set->segment_list->segment_URLs);
3786 : has_seg_list = GF_TRUE;
3787 : //can be NULL if we don't mutualize at AdaptatioSet level
3788 0 : if (surl) {
3789 0 : gf_mpd_segment_url_free(surl);
3790 : seg_url_found = GF_TRUE;
3791 : }
3792 : }
3793 : //but we must have at least one segment URL entry
3794 3 : if (has_seg_list && !seg_url_found) {
3795 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] purging segment %s for AS %d rep %s but segment list is empty!\n",
3796 : sctx->filename ? sctx->filename : "", ds->set->id, ds->rep->id));
3797 : }
3798 :
3799 3 : if (ds->rep->segment_template) {
3800 1 : if (ds->rep->segment_template->segment_timeline) {
3801 0 : dahser_purge_segment_timeline(ds, ds->rep->segment_template->segment_timeline, sctx);
3802 : }
3803 : }
3804 : //not an else due to inheritance
3805 3 : if (ds->owns_set && ds->set->segment_template) {
3806 3 : if (ds->set->segment_template->segment_timeline) {
3807 1 : dahser_purge_segment_timeline(ds, ds->set->segment_template->segment_timeline, sctx);
3808 : }
3809 : }
3810 :
3811 3 : ds->nb_segments_purged ++;
3812 3 : ds->dur_purged += dur;
3813 : assert(gf_list_find(ds->pending_segment_states, sctx)<0);
3814 3 : if (sctx->filename) gf_free(sctx->filename);
3815 3 : if (sctx->hls_key_uri) gf_free(sctx->hls_key_uri);
3816 3 : gf_free(sctx);
3817 3 : gf_list_rem(ds->rep->state_seg_list, 0);
3818 : }
3819 10 : if (max_rem_dur < ds->dur_purged*1000) max_rem_dur = (u64) (ds->dur_purged * 1000);
3820 : //final flush to static of live session: update start number
3821 10 : if (ctx->dmode!=GF_MPD_TYPE_DYNAMIC) {
3822 0 : if (ds->owns_set && ds->set && ds->set->segment_template) {
3823 0 : ds->set->segment_template->start_number += ds->nb_segments_purged;
3824 0 : } else if (ds->rep && ds->rep->segment_template) {
3825 0 : ds->rep->segment_template->start_number += ds->nb_segments_purged;
3826 : }
3827 0 : ds->nb_segments_purged = 0;
3828 : }
3829 : }
3830 : //final flush to static of live session: update period duration
3831 11 : if (ctx->dmode!=GF_MPD_TYPE_DYNAMIC) {
3832 1 : if (max_rem_dur > *period_dur) *period_dur = 0;
3833 1 : else *period_dur = *period_dur - max_rem_dur;
3834 : }
3835 : }
3836 :
3837 356 : static void dasher_update_period_duration(GF_DasherCtx *ctx, Bool is_period_switch)
3838 : {
3839 : u32 i, count;
3840 356 : u64 pdur = 0;
3841 : u64 min_dur = 0;
3842 : u64 p_start=0;
3843 : GF_MPD_Period *prev_p = NULL;
3844 356 : count = gf_list_count(ctx->current_period->streams);
3845 530 : for (i=0; i<count; i++) {
3846 530 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
3847 530 : if (ds->muxed_base) continue;
3848 :
3849 516 : if (ds->xlink && (ds->stream_type==GF_STREAM_FILE) ) {
3850 0 : pdur = (u32) (1000*(s64)ds->period_dur.num / ds->period_dur.den);
3851 : } else {
3852 516 : u64 ds_dur = ds->max_period_dur;
3853 :
3854 : //we had to generate one extra segment to unlock looping, but we don't want to advertise it in the manifest duration
3855 : //because other sets may not be ready for this time interval
3856 516 : if (ds->subdur_forced_use_period_dur)
3857 : ds_dur = ds->subdur_forced_use_period_dur;
3858 :
3859 516 : if (ds->clamped_dur.num && !ctx->loop) {
3860 177 : u64 clamp_dur = (u64) (ds->clamped_dur.num * 1000);
3861 177 : if (clamp_dur < ds->clamped_dur.den * ds_dur) ds_dur = clamp_dur / ds->clamped_dur.den;
3862 : }
3863 :
3864 516 : if (ds->dur_purged && (ctx->mpd->type != GF_MPD_TYPE_DYNAMIC)) {
3865 0 : u64 rem_dur = (u64) (ds->dur_purged * 1000);
3866 0 : if (ds_dur>rem_dur) ds_dur -= rem_dur;
3867 : else ds_dur = 0;
3868 : }
3869 :
3870 516 : if (!min_dur || (min_dur > ds_dur)) min_dur = ds->max_period_dur;
3871 516 : if (pdur < ds_dur) pdur = ds_dur;
3872 : }
3873 : }
3874 :
3875 356 : if (!count) {
3876 1 : if (ctx->current_period->period && ctx->current_period->period->duration)
3877 1 : pdur = ctx->current_period->period->duration;
3878 : }
3879 :
3880 356 : if (!ctx->check_dur) {
3881 202 : s32 diff = (s32) ((s64) pdur - (s64) min_dur);
3882 202 : if (ABS(diff)>2000) {
3883 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Adaptation sets in period are of unequal duration min %g max %g seconds\n", ((Double)min_dur)/1000, ((Double)pdur)/1000));
3884 : }
3885 : }
3886 :
3887 356 : dasher_purge_segments(ctx, &pdur);
3888 :
3889 356 : if (ctx->current_period->period) {
3890 356 : ctx->current_period->period->duration = pdur;
3891 :
3892 : //update MPD duration in any case
3893 356 : if (ctx->current_period->period->start) {
3894 3 : ctx->mpd->media_presentation_duration = ctx->current_period->period->start + pdur;
3895 : } else {
3896 353 : u32 k, pcount = gf_list_count(ctx->mpd->periods);
3897 353 : ctx->mpd->media_presentation_duration = 0;
3898 8 : for (k=0; k<pcount; k++) {
3899 361 : GF_MPD_Period *p = gf_list_get(ctx->mpd->periods, k);
3900 361 : if (p->start)
3901 1 : ctx->mpd->media_presentation_duration = p->start + p->duration;
3902 : else
3903 360 : ctx->mpd->media_presentation_duration += p->duration;
3904 361 : if (p==ctx->current_period->period)
3905 : break;
3906 : }
3907 : }
3908 : }
3909 :
3910 356 : if (ctx->refresh<0)
3911 0 : ctx->mpd->media_presentation_duration = (u32) ( (-ctx->refresh) * 1000 );
3912 :
3913 : //static mode, done
3914 356 : if (ctx->dmode != GF_MPD_TYPE_DYNAMIC) {
3915 228 : return;
3916 : }
3917 : assert(ctx->current_period->period);
3918 : //dynamic mode only, reset durations
3919 :
3920 128 : ctx->mpd->gpac_mpd_time = ctx->mpd->media_presentation_duration;
3921 :
3922 : //not done yet for this period, keep duration to 0
3923 128 : if (ctx->subdur_done) {
3924 22 : if (ctx->mpd->media_presentation_duration > ctx->current_period->period->duration)
3925 3 : ctx->mpd->media_presentation_duration -= ctx->current_period->period->duration;
3926 : else
3927 19 : ctx->mpd->media_presentation_duration = 0;
3928 22 : ctx->current_period->period->duration = 0;
3929 : }
3930 :
3931 128 : ctx->mpd->gpac_next_ntp_ms = ctx->mpd->gpac_init_ntp_ms + ctx->mpd->gpac_mpd_time;
3932 128 : if (ctx->asto<0)
3933 0 : ctx->mpd->gpac_next_ntp_ms -= (u64) (-ctx->asto * 1000);
3934 128 : if (ctx->_p_gentime) (*ctx->_p_gentime) = ctx->mpd->gpac_next_ntp_ms;
3935 128 : if (ctx->_p_mpdtime) (*ctx->_p_mpdtime) = ctx->mpd->gpac_mpd_time;
3936 :
3937 128 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] updated period %s duration "LLU" MPD time "LLU"\n", ctx->current_period->period->ID, pdur, ctx->mpd->gpac_mpd_time ));
3938 :
3939 128 : count = gf_list_count(ctx->mpd->periods);
3940 133 : for (i=0; i<count; i++) {
3941 133 : GF_MPD_Period *p = gf_list_get(ctx->mpd->periods, i);
3942 133 : if (p->start) {
3943 : p_start = p->start;
3944 : } else {
3945 131 : p->start = p_start;
3946 : }
3947 133 : if (prev_p && (prev_p->start + prev_p->duration == p_start)) {
3948 3 : prev_p->duration = 0;
3949 : }
3950 133 : p_start += p->duration;
3951 : prev_p = p;
3952 : }
3953 : }
3954 :
3955 1014 : static void dasher_transfer_file(FILE *f, GF_FilterPid *opid, const char *name, GF_DashStream *ds)
3956 : {
3957 : GF_FilterPacket *pck;
3958 : u32 size, nb_read;
3959 : u8 *output;
3960 :
3961 1014 : size = (u32) gf_fsize(f);
3962 :
3963 1014 : pck = gf_filter_pck_new_alloc(opid, size, &output);
3964 1014 : if (!pck) return;
3965 :
3966 1014 : nb_read = (u32) gf_fread(output, size, f);
3967 1014 : if (nb_read != size) {
3968 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Error reading temp MPD file, read %d bytes but file size is %d\n", nb_read, size ));
3969 : }
3970 1014 : gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
3971 1014 : gf_filter_pck_set_seek_flag(pck, GF_TRUE);
3972 1014 : if (name) {
3973 612 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(name) );
3974 : }
3975 1014 : if (ds) {
3976 596 : gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
3977 : }
3978 1014 : gf_filter_pck_send(pck);
3979 : }
3980 :
3981 :
3982 : static u64 dasher_get_utc(GF_DasherCtx *ctx)
3983 : {
3984 825 : return gf_net_get_utc() - ctx->utc_diff;
3985 : }
3986 :
3987 6 : static void dasher_get_set_and_rep(GF_MPD_Period *period, const char *repid, GF_MPD_AdaptationSet **out_set, GF_MPD_Representation **out_rep)
3988 : {
3989 6 : u32 i, nb_sets = gf_list_count(period->adaptation_sets);
3990 2 : for (i=0; i<nb_sets; i++) {
3991 : u32 j, nb_reps;
3992 8 : GF_MPD_AdaptationSet *set = gf_list_get(period->adaptation_sets, i);
3993 8 : nb_reps = gf_list_count(set->representations);
3994 6 : for (j=0; j<nb_reps; j++) {
3995 12 : GF_MPD_Representation *rep = gf_list_get(set->representations, j);
3996 12 : if (rep->id && !strcmp(rep->id, repid)) {
3997 6 : *out_set = set;
3998 6 : *out_rep = rep;
3999 : return;
4000 : }
4001 : }
4002 : }
4003 : }
4004 :
4005 6 : static Bool dasher_merge_rep(GF_DashStream *ds, GF_MPD_Representation *rep)
4006 : {
4007 : Bool transcode_detected = GF_FALSE;
4008 : Bool recompute_set = GF_FALSE;
4009 6 : GF_MPD_Representation *n_rep = ds->rep;
4010 :
4011 : //TODO: copy other properties in case we transcode ?
4012 :
4013 : #define CHECK_VAL(_name, _v) if (rep->_name != n_rep->_name) { rep->_name = n_rep->_name; transcode_detected = GF_TRUE; if (_v) recompute_set = GF_TRUE; }
4014 :
4015 : #define CHECK_STR(_name) if (rep->_name && n_rep->_name && !strcmp(rep->_name, n_rep->_name)) {} \
4016 : else if (!rep->_name && !n_rep->_name) {}\
4017 : else { \
4018 : if (rep->_name) gf_free(rep->_name); rep->_name = n_rep->_name ? gf_strdup(n_rep->_name) : NULL; \
4019 : transcode_detected = GF_TRUE; \
4020 : }
4021 :
4022 : //for frac, if not set on source PID, consider it unchanged
4023 : #define CHECK_FRAC(_name) if (rep->_name && n_rep->_name && (rep->_name->num * n_rep->_name->den == rep->_name->den * n_rep->_name->num)) {} \
4024 : else if (!n_rep->_name) {}\
4025 : else { \
4026 : if (rep->_name) gf_free(rep->_name); \
4027 : if (n_rep->_name) { rep->_name = gf_malloc(sizeof(GF_MPD_Fractional)); memcpy(rep->_name, n_rep->_name, sizeof(GF_MPD_Fractional)); } \
4028 : else rep->_name = NULL; \
4029 : transcode_detected = GF_TRUE; \
4030 : }
4031 :
4032 6 : CHECK_STR(codecs)
4033 6 : CHECK_STR(profiles)
4034 6 : CHECK_STR(mime_type)
4035 6 : CHECK_STR(segmentProfiles)
4036 6 : CHECK_VAL(width, 1)
4037 6 : CHECK_VAL(height, 1)
4038 6 : CHECK_VAL(bandwidth, 0)
4039 6 : CHECK_VAL(samplerate, 0)
4040 6 : CHECK_VAL(scan_type, 0)
4041 6 : CHECK_FRAC(sar)
4042 6 : CHECK_FRAC(framerate)
4043 :
4044 6 : if (transcode_detected && !ds->transcode_detected) {
4045 0 : ds->transcode_detected = GF_TRUE;
4046 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Transcoded detected in forward mode, not fully tested !\n"));
4047 : }
4048 : #undef CHECK_VAL
4049 : #undef CHECK_STR
4050 : #undef CHECK_FRAC
4051 :
4052 6 : return recompute_set;
4053 : }
4054 4 : static void dasher_forward_manifest_raw(GF_DasherCtx *ctx, GF_DashStream *ds, const char *manifest, const char *manifest_name)
4055 : {
4056 : GF_FilterPacket *pck;
4057 : u32 size;
4058 : u8 *output;
4059 :
4060 4 : size = (u32) strlen(manifest);
4061 :
4062 4 : pck = gf_filter_pck_new_alloc(ctx->opid, size+1, &output);
4063 4 : if (!pck) return;
4064 :
4065 4 : memcpy(output, manifest, size);
4066 4 : output[size] = 0;
4067 4 : gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
4068 4 : gf_filter_pck_set_seek_flag(pck, GF_TRUE);
4069 4 : if (manifest_name) {
4070 3 : if (ctx->out_path) {
4071 3 : char *url = gf_url_concatenate(ctx->out_path, manifest_name);
4072 3 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING_NO_COPY(url) );
4073 : } else {
4074 0 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(manifest_name) );
4075 : }
4076 3 : if (ds)
4077 3 : gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( ds->hls_ref_id ) );
4078 : }
4079 4 : gf_filter_pck_send(pck);
4080 :
4081 : }
4082 2 : static void dasher_forward_mpd(GF_DasherCtx *ctx, const char *manifest)
4083 : {
4084 : u32 i, nb_periods, nb_streams;
4085 : FILE *tmp = NULL;
4086 2 : GF_MPD *mpd = gf_mpd_new();
4087 : GF_List *recompute_sets = NULL;
4088 2 : GF_DOMParser *dom = gf_xml_dom_new();
4089 2 : GF_Err e = gf_xml_dom_parse_string(dom, (char *)manifest);
4090 2 : if (e) goto err_exit;
4091 :
4092 2 : e = gf_mpd_init_from_dom(gf_xml_dom_get_root(dom), mpd, NULL);
4093 2 : if (e) goto err_exit;
4094 :
4095 2 : nb_streams = gf_list_count(ctx->pids);
4096 2 : nb_periods = gf_list_count(mpd->periods);
4097 4 : for (i=0; i<nb_periods; i++) {
4098 : u32 j;
4099 2 : GF_MPD_AdaptationSet *set = NULL;
4100 2 : GF_MPD_Representation *rep = NULL;
4101 2 : GF_MPD_Period *period = gf_list_get(mpd->periods, i);
4102 8 : for (j=0; j<nb_streams; j++) {
4103 : Bool invalidate_set;
4104 6 : GF_DashStream *ds = gf_list_get(ctx->pids, j);
4105 6 : if (ds->muxed_base) continue;
4106 6 : const GF_PropertyValue *ps = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DASH_PERIOD_START);
4107 6 : const GF_PropertyValue *repid = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_REP_ID);
4108 6 : if (!ps || !repid) {
4109 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch period start or rep ID in forward mode, cannot forward\n"));
4110 0 : goto err_exit;
4111 : }
4112 6 : if (period->start != ps->value.longuint) continue;
4113 6 : dasher_get_set_and_rep(period, repid->value.string, &set, &rep);
4114 6 : if (!set || !rep) {
4115 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't locate adaptation set and period in source manifest in forward mode, cannot forward\n"));
4116 : goto err_exit;
4117 : }
4118 : //copy/reset common encryption
4119 6 : if (set->content_protection) {
4120 6 : gf_mpd_del_list(set->content_protection, gf_mpd_descriptor_free, 1);
4121 : }
4122 6 : if (rep->content_protection) {
4123 6 : gf_mpd_del_list(rep->content_protection, gf_mpd_descriptor_free, 1);
4124 : }
4125 6 : if (gf_list_count(ds->rep->content_protection)) {
4126 0 : gf_list_del(rep->content_protection);
4127 0 : rep->content_protection = dasher_get_content_protection_desc(ctx, ds, NULL);
4128 : }
4129 6 : if (gf_list_count(ds->set->content_protection)) {
4130 6 : gf_list_del(set->content_protection);
4131 6 : set->content_protection = dasher_get_content_protection_desc(ctx, ds, ds->set);
4132 : }
4133 6 : invalidate_set = dasher_merge_rep(ds, rep);
4134 : //wait until we are all done
4135 6 : if (invalidate_set) {
4136 0 : if (!recompute_sets) recompute_sets = gf_list_new();
4137 0 : if (gf_list_find(recompute_sets, set)<0)
4138 0 : gf_list_add(recompute_sets, set);
4139 :
4140 : }
4141 : }
4142 : }
4143 : //update sets - TODO
4144 :
4145 : //and send
4146 2 : tmp = gf_file_temp(NULL);
4147 2 : mpd->xml_namespace = ctx->mpd->xml_namespace;
4148 2 : mpd->publishTime = dasher_get_utc(ctx);
4149 2 : e = gf_mpd_write(mpd, tmp, ctx->cmpd);
4150 2 : if (e) {
4151 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Error serializing manifest in forward mode: %s\n", gf_error_to_string(e) ));
4152 : e = GF_OK;
4153 : goto err_exit;
4154 : }
4155 2 : dasher_transfer_file(tmp, ctx->opid, NULL, NULL);
4156 :
4157 2 : err_exit:
4158 2 : if (tmp) gf_fclose(tmp);
4159 2 : gf_mpd_del(mpd);
4160 2 : gf_xml_dom_del(dom);
4161 2 : gf_list_del(recompute_sets);
4162 2 : if (e)
4163 0 : ctx->in_error = GF_TRUE;
4164 2 : }
4165 :
4166 658 : static GF_Err dasher_write_and_send_manifest(GF_DasherCtx *ctx, u64 last_period_dur, Bool do_m3u8, Bool m3u8_second_pass, GF_FilterPid *opid, char *alt_name)
4167 : {
4168 : void *last_signature;
4169 : u8 sig[GF_SHA1_DIGEST_SIZE];
4170 : GF_Err e;
4171 658 : FILE *tmp = gf_file_temp(NULL);
4172 658 : if (do_m3u8) {
4173 386 : ctx->mpd->m3u8_time = ctx->hlsc;
4174 386 : if (ctx->llhls==3)
4175 42 : ctx->mpd->force_llhls_mode = m3u8_second_pass ? 2 : 1;
4176 : else
4177 344 : ctx->mpd->force_llhls_mode = 0;
4178 :
4179 386 : if (m3u8_second_pass) {
4180 21 : e = gf_mpd_write_m3u8_master_playlist(ctx->mpd, tmp, ctx->out_path, gf_list_get(ctx->mpd->periods, 0) );
4181 : } else {
4182 365 : e = gf_mpd_write_m3u8_master_playlist(ctx->mpd, tmp, ctx->out_path, gf_list_get(ctx->mpd->periods, 0) );
4183 : }
4184 : } else {
4185 272 : e = gf_mpd_write(ctx->mpd, tmp, ctx->cmpd);
4186 : }
4187 :
4188 658 : if (e) {
4189 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to write %s file: %s\n", do_m3u8 ? "M3U8" : "MPD", gf_error_to_string(e) ));
4190 0 : gf_fclose(tmp);
4191 0 : if (ctx->current_period->period)
4192 0 : ctx->current_period->period->duration = last_period_dur;
4193 : return e;
4194 : }
4195 :
4196 658 : if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
4197 0 : if (gf_ftell(tmp) > 100 * 1024)
4198 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] manifest MPD is too big for HbbTV 1.5. Limit is 100kB, current size is "LLU"kB\n", gf_ftell(tmp) / 1024));
4199 : }
4200 :
4201 658 : gf_sha1_file_ptr(tmp, sig);
4202 658 : if (do_m3u8) {
4203 386 : last_signature = (void *) m3u8_second_pass ? ctx->last_hls2_signature : ctx->last_hls_signature;
4204 : } else {
4205 272 : last_signature = (void *) ctx->last_mpd_signature;
4206 : }
4207 :
4208 658 : if (memcmp(sig, last_signature, GF_SHA1_DIGEST_SIZE)) {
4209 : memcpy(last_signature, sig, GF_SHA1_DIGEST_SIZE);
4210 :
4211 416 : dasher_transfer_file(tmp, opid, alt_name, NULL);
4212 : }
4213 658 : gf_fclose(tmp);
4214 658 : return GF_OK;
4215 : }
4216 :
4217 253 : static void dasher_update_dyn_bitrates(GF_DasherCtx *ctx)
4218 : {
4219 253 : u32 i, count = gf_list_count(ctx->current_period->streams);
4220 530 : for (i=0; i<count; i++) {
4221 277 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
4222 277 : if (ds->dyn_bitrate) dasher_update_bitrate(ctx, ds);
4223 : }
4224 253 : }
4225 :
4226 638 : GF_Err dasher_send_manifest(GF_Filter *filter, GF_DasherCtx *ctx, Bool for_mpd_only)
4227 : {
4228 : GF_Err e;
4229 : u32 i, max_opid;
4230 : FILE *tmp;
4231 : u64 store_mpd_dur=0;
4232 : u64 max_seg_dur=0;
4233 : u64 last_period_dur;
4234 :
4235 : //manifest forwarding
4236 638 : if (ctx->forward_mode == DASHER_FWD_ALL)
4237 : return GF_OK;
4238 :
4239 635 : if (ctx->dyn_rate)
4240 209 : dasher_update_dyn_bitrates(ctx);
4241 :
4242 : //UGLY PATCH, to remove - we don't have the same algos in old arch and new arch, which result in slightly different max segment duration
4243 : //on audio for our test suite - patch it manually to avoid hash failures :(
4244 : //TODO, remove as soon as we switch archs
4245 635 : if (gf_sys_old_arch_compat() && (ctx->mpd->max_segment_duration==1022) && (ctx->mpd->media_presentation_duration==10160) ) {
4246 1 : ctx->mpd->max_segment_duration = 1080;
4247 1 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] patch for old regression tests hit, changing max seg dur from 1022 to 1080\nPlease notify GPAC devs to remove this, and do not use fot_test modes in dash filter\n"));
4248 : }
4249 :
4250 1270 : ctx->mpd->publishTime = dasher_get_utc(ctx);
4251 635 : if (ctx->utc_timing_type==DASHER_UTCREF_INBAND) {
4252 0 : GF_MPD_Descriptor *d = gf_list_get(ctx->mpd->utc_timings, 0);
4253 0 : if (d) {
4254 : time_t gtime;
4255 : struct tm *t;
4256 : u32 sec;
4257 : u32 ms;
4258 : char szTime[100];
4259 0 : if (d->value) gf_free(d->value);
4260 :
4261 0 : gtime = ctx->mpd->publishTime / 1000;
4262 0 : sec = (u32)(ctx->mpd->publishTime / 1000);
4263 0 : ms = (u32)(ctx->mpd->publishTime - ((u64)sec) * 1000);
4264 :
4265 0 : t = gf_gmtime(>ime);
4266 0 : sec = t->tm_sec;
4267 : //see issue #859, no clue how this happened...
4268 0 : if (sec > 60)
4269 : sec = 60;
4270 0 : snprintf(szTime, 100, "%d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, sec, ms);
4271 0 : d->value = gf_strdup(szTime);
4272 : }
4273 : }
4274 635 : dasher_update_mpd(ctx);
4275 635 : ctx->mpd->write_context = GF_FALSE;
4276 635 : ctx->mpd->was_dynamic = GF_FALSE;
4277 635 : if (ctx->dmode==GF_DASH_DYNAMIC_LAST)
4278 8 : ctx->mpd->was_dynamic = GF_TRUE;
4279 :
4280 635 : if ((ctx->refresh>=0) && (ctx->dmode==GF_DASH_DYNAMIC)) {
4281 408 : store_mpd_dur= ctx->mpd->media_presentation_duration;
4282 : }
4283 :
4284 635 : if (ctx->sseg && ctx->mpd->max_segment_duration) {
4285 18 : max_seg_dur = ctx->mpd->max_subsegment_duration = ctx->mpd->max_segment_duration;
4286 18 : ctx->mpd->max_segment_duration = 0;
4287 : }
4288 :
4289 : last_period_dur = 0;
4290 635 : if (ctx->current_period->period) {
4291 635 : last_period_dur = ctx->current_period->period->duration;
4292 635 : if (ctx->dmode==GF_DASH_DYNAMIC) {
4293 408 : ctx->current_period->period->duration = 0;
4294 408 : ctx->mpd->media_presentation_duration = 0;
4295 : }
4296 : }
4297 :
4298 635 : max_opid = (ctx->dual && ctx->opid_alt) ? 2 : 1;
4299 1272 : for (i=0; i < max_opid; i++) {
4300 : Bool do_m3u8 = GF_FALSE;
4301 : GF_FilterPid *opid;
4302 :
4303 637 : if (i==0) {
4304 635 : if (max_opid>1) {
4305 2 : do_m3u8 = ctx->opid_alt_m3u8 ? GF_FALSE : GF_TRUE;
4306 : } else {
4307 633 : do_m3u8 = ctx->do_m3u8;
4308 : }
4309 635 : opid = ctx->opid;
4310 : } else {
4311 2 : do_m3u8 = ctx->opid_alt_m3u8;
4312 2 : opid = ctx->opid_alt;
4313 : }
4314 637 : if (do_m3u8 && for_mpd_only) {
4315 0 : continue;
4316 : }
4317 637 : if ((ctx->llhls==3) && do_m3u8)
4318 21 : ctx->mpd->force_llhls_mode = 1;
4319 637 : e = dasher_write_and_send_manifest(ctx, last_period_dur, do_m3u8, GF_FALSE, opid, NULL);
4320 637 : if (e) return e;
4321 :
4322 637 : ctx->mpd->force_llhls_mode = 0;
4323 : }
4324 :
4325 635 : if (ctx->current_period->period)
4326 635 : ctx->current_period->period->duration = last_period_dur;
4327 :
4328 635 : if (store_mpd_dur) {
4329 303 : ctx->mpd->media_presentation_duration = store_mpd_dur;
4330 : }
4331 635 : if (max_seg_dur) {
4332 18 : ctx->mpd->max_segment_duration = (u32) max_seg_dur;
4333 18 : ctx->mpd->max_subsegment_duration = 0;
4334 : }
4335 :
4336 635 : if (ctx->do_m3u8) {
4337 : Bool m3u8_second_pass = GF_FALSE;
4338 : u32 j;
4339 365 : GF_MPD_Period *period = gf_list_get(ctx->mpd->periods, 0);
4340 : GF_MPD_AdaptationSet *as;
4341 : GF_MPD_Representation *rep;
4342 : GF_FilterPid *opid;
4343 : assert(period);
4344 365 : if (ctx->opid_alt_m3u8) opid = ctx->opid_alt;
4345 365 : else opid = ctx->opid;
4346 :
4347 365 : resend:
4348 386 : i=0;
4349 1168 : while ( (as = (GF_MPD_AdaptationSet *) gf_list_enum(period->adaptation_sets, &i))) {
4350 396 : j=0;
4351 1388 : while ( (rep = (GF_MPD_Representation *) gf_list_enum(as->representations, &j))) {
4352 596 : if (rep->m3u8_var_file) {
4353 : GF_DashStream *ds;
4354 596 : char *outfile = rep->m3u8_var_name;
4355 : Bool do_free = GF_FALSE;
4356 :
4357 596 : if (rep->m3u8_name) {
4358 : outfile = (char *) rep->m3u8_name;
4359 0 : if (ctx->out_path) {
4360 0 : outfile = gf_url_concatenate(ctx->out_path, rep->m3u8_name);
4361 : do_free = GF_TRUE;
4362 : }
4363 : }
4364 596 : if (m3u8_second_pass) {
4365 : char *sep;
4366 21 : char *new_name = gf_strdup(outfile);
4367 :
4368 21 : sep = gf_file_ext_start(new_name);
4369 21 : if (sep) sep[0] = 0;
4370 21 : gf_dynstrcat(&new_name, "_IF", NULL);
4371 21 : sep = gf_file_ext_start(outfile);
4372 21 : if (sep)
4373 21 : gf_dynstrcat(&new_name, sep, NULL);
4374 :
4375 21 : if (do_free) gf_free(outfile);
4376 21 : outfile = new_name;
4377 : do_free = GF_TRUE;
4378 : }
4379 596 : ds = rep->playback.udta;
4380 596 : dasher_transfer_file(rep->m3u8_var_file, opid, outfile, ds);
4381 596 : gf_fclose(rep->m3u8_var_file);
4382 596 : rep->m3u8_var_file = NULL;
4383 596 : if (do_free) gf_free(outfile);
4384 : }
4385 : }
4386 : }
4387 :
4388 386 : if ((ctx->llhls==3) && !m3u8_second_pass && ctx->out_path) {
4389 : char *sep;
4390 : char szAltName[GF_MAX_PATH];
4391 : strcpy(szAltName, ctx->out_path);
4392 21 : sep = gf_file_ext_start(szAltName);
4393 21 : if (sep) sep[0] = 0;
4394 : strcat(szAltName, "_IF");
4395 21 : sep = gf_file_ext_start(ctx->out_path);
4396 21 : if (sep) strcat(szAltName, sep);
4397 :
4398 21 : ctx->mpd->force_llhls_mode = 2;
4399 21 : e = dasher_write_and_send_manifest(ctx, last_period_dur, GF_TRUE, GF_TRUE, ctx->opid, szAltName);
4400 21 : if (e) return e;
4401 :
4402 : m3u8_second_pass = GF_TRUE;
4403 21 : goto resend;
4404 :
4405 : }
4406 : }
4407 :
4408 :
4409 635 : if (ctx->state) {
4410 49 : tmp = gf_fopen(ctx->state, "w");
4411 49 : if (!tmp) {
4412 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to open context MPD %s for write\n", ctx->state ));
4413 : return GF_IO_ERR;
4414 : }
4415 49 : ctx->mpd->write_context = GF_TRUE;
4416 49 : e = gf_mpd_write(ctx->mpd, tmp, ctx->cmpd);
4417 49 : gf_fclose(tmp);
4418 49 : ctx->mpd->write_context = GF_FALSE;
4419 49 : if (e) {
4420 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to write MPD file: %s\n", gf_error_to_string(e) ));
4421 : }
4422 : return e;
4423 : }
4424 : return GF_OK;
4425 : }
4426 :
4427 731 : static void dasher_reset_stream(GF_Filter *filter, GF_DashStream *ds, Bool is_destroy)
4428 : {
4429 : //we do not remove the destination filter, it will be removed automatically once all remove_pids are called
4430 : //removing it explicetly will discard the upper chain and any packets not yet processed
4431 :
4432 731 : ds->dst_filter = NULL;
4433 731 : if (ds->seg_template) gf_free(ds->seg_template);
4434 731 : if (ds->idx_template) gf_free(ds->idx_template);
4435 731 : if (ds->init_seg) gf_free(ds->init_seg);
4436 731 : if (ds->multi_pids) gf_list_del(ds->multi_pids);
4437 731 : ds->multi_pids = NULL;
4438 731 : if (ds->multi_tracks) gf_list_del(ds->multi_tracks);
4439 731 : ds->multi_tracks = NULL;
4440 :
4441 731 : if (ds->pending_segment_urls) gf_list_del(ds->pending_segment_urls);
4442 731 : ds->pending_segment_urls = NULL;
4443 731 : if (ds->pending_segment_states) gf_list_del(ds->pending_segment_states);
4444 731 : ds->pending_segment_states = NULL;
4445 :
4446 731 : if (is_destroy) {
4447 357 : if (ds->cues) gf_free(ds->cues);
4448 357 : gf_list_del(ds->complementary_streams);
4449 357 : gf_free(ds->rep_id);
4450 : //string properties are locally copied
4451 : #define RESET_PROP_STR(_prop) \
4452 : if (_prop) gf_free(_prop);
4453 :
4454 357 : RESET_PROP_STR(ds->src_url)
4455 357 : RESET_PROP_STR(ds->template)
4456 357 : RESET_PROP_STR(ds->lang)
4457 357 : RESET_PROP_STR(ds->hls_vp_name)
4458 357 : RESET_PROP_STR(ds->xlink)
4459 357 : RESET_PROP_STR(ds->period_id)
4460 357 : RESET_PROP_STR(ds->period_continuity_id)
4461 :
4462 : #undef RESET_PROP_STR
4463 : return;
4464 : }
4465 374 : ds->init_seg = ds->seg_template = ds->idx_template = NULL;
4466 374 : ds->split_set_names = GF_FALSE;
4467 374 : ds->nb_sap_3 = 0;
4468 374 : ds->nb_sap_4 = 0;
4469 374 : ds->pid_id = 0;
4470 374 : ds->force_timescale = 0;
4471 374 : ds->set = NULL;
4472 374 : ds->owns_set = GF_FALSE;
4473 374 : ds->rep = NULL;
4474 374 : ds->muxed_base = NULL;
4475 374 : ds->nb_comp = ds->nb_comp_done = 0;
4476 374 : gf_list_reset(ds->complementary_streams);
4477 374 : ds->inband_params = GF_FALSE;
4478 374 : ds->seg_start_time = 0;
4479 374 : ds->seg_number = ds->startNumber;
4480 374 : ds->nb_segments_purged = 0;
4481 374 : ds->dur_purged = 0;
4482 374 : ds->moof_sn_inc = 0;
4483 374 : ds->moof_sn = 0;
4484 374 : ds->seg_done = 0;
4485 374 : ds->subdur_done = 0;
4486 374 : if (ds->packet_queue) {
4487 6 : while (gf_list_count(ds->packet_queue)) {
4488 0 : GF_FilterPacket *pck = gf_list_pop_front(ds->packet_queue);
4489 0 : gf_filter_pck_unref(pck);
4490 : }
4491 6 : ds->nb_sap_in_queue = 0;
4492 : }
4493 : }
4494 :
4495 32 : void dasher_context_update_period_end(GF_DasherCtx *ctx)
4496 : {
4497 : u32 i, count;
4498 :
4499 32 : if (!ctx->mpd) return;
4500 :
4501 32 : count = gf_list_count(ctx->current_period->streams);
4502 89 : for (i=0; i<count; i++) {
4503 57 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
4504 57 : if (!ds->rep) continue;
4505 37 : if (!ds->rep->dasher_ctx) continue;
4506 37 : if (ds->done == 1) {
4507 6 : ds->rep->dasher_ctx->done = 1;
4508 : } else {
4509 : //store all dynamic parameters of the rep
4510 31 : ds->rep->dasher_ctx->last_pck_idx = ds->nb_pck;
4511 31 : ds->seek_to_pck = ds->nb_pck;
4512 31 : ds->rep->dasher_ctx->seg_number = ds->seg_number;
4513 31 : ds->rep->dasher_ctx->next_seg_start = ds->next_seg_start;
4514 31 : ds->rep->dasher_ctx->first_cts = ds->first_cts;
4515 31 : ds->rep->dasher_ctx->first_dts = ds->first_dts;
4516 31 : ds->rep->dasher_ctx->ts_offset = ds->ts_offset;
4517 31 : ds->rep->dasher_ctx->segs_purged = ds->nb_segments_purged;
4518 31 : ds->rep->dasher_ctx->dur_purged = ds->dur_purged;
4519 31 : ds->rep->dasher_ctx->moof_sn = ds->moof_sn;
4520 31 : ds->rep->dasher_ctx->moof_sn_inc = ds->moof_sn_inc;
4521 31 : ds->rep->dasher_ctx->subdur_forced = ds->subdur_forced_use_period_dur ? GF_TRUE : GF_FALSE;
4522 : }
4523 37 : if (ctx->subdur) {
4524 35 : ds->rep->dasher_ctx->cumulated_subdur = ds->cumulated_subdur + ctx->subdur;
4525 35 : ds->rep->dasher_ctx->cumulated_dur = ((Double)ds->cumulated_dur) / ds->timescale;
4526 :
4527 : }
4528 37 : ds->rep->dasher_ctx->nb_repeat = ds->nb_repeat;
4529 37 : ds->rep->dasher_ctx->est_next_dts = ds->est_next_dts;
4530 37 : ds->rep->dasher_ctx->source_pid = ds->id;
4531 37 : ds->rep->dasher_ctx->mpd_timescale = ds->mpd_timescale;
4532 37 : ds->rep->dasher_ctx->last_dyn_period_id = ctx->last_dyn_period_id;
4533 :
4534 : assert(ds->rep->dasher_ctx->init_seg);
4535 : assert(ds->rep->dasher_ctx->src_url);
4536 : assert(ds->rep->dasher_ctx->template_seg);
4537 : }
4538 : }
4539 :
4540 14 : void dasher_context_update_period_start(GF_DasherCtx *ctx)
4541 : {
4542 : u32 i, j, count;
4543 :
4544 14 : if (!ctx->mpd) return;
4545 14 : count = gf_list_count(ctx->current_period->streams);
4546 39 : for (i=0; i<count; i++) {
4547 25 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
4548 25 : if (!ds->rep) continue;
4549 24 : if (ds->rep->dasher_ctx) continue;
4550 :
4551 : //store all static parameters of the rep
4552 24 : GF_SAFEALLOC(ds->rep->dasher_ctx, GF_DASH_SegmenterContext);
4553 24 : if (!ds->rep->dasher_ctx) return;
4554 :
4555 24 : ds->rep->dasher_ctx->done = 0;
4556 :
4557 : assert(ds->init_seg);
4558 24 : ds->rep->dasher_ctx->init_seg = gf_strdup(ds->init_seg);
4559 : assert(ds->src_url);
4560 24 : ds->rep->dasher_ctx->src_url = gf_strdup(ds->src_url);
4561 : assert(ds->seg_template);
4562 24 : ds->rep->dasher_ctx->template_seg = gf_strdup(ds->seg_template);
4563 24 : if (ds->idx_template)
4564 0 : ds->rep->dasher_ctx->template_idx = gf_strdup(ds->idx_template);
4565 :
4566 24 : ds->rep->dasher_ctx->pid_id = ds->pid_id;
4567 24 : ds->rep->dasher_ctx->dep_pid_id = ds->dep_pid_id;
4568 24 : ds->rep->dasher_ctx->period_start = ds->period_start;
4569 24 : ds->rep->dasher_ctx->period_duration = ds->period_dur;
4570 24 : ds->rep->dasher_ctx->multi_pids = ds->multi_pids ? GF_TRUE : GF_FALSE;
4571 24 : ds->rep->dasher_ctx->dash_dur = ds->dash_dur;
4572 :
4573 24 : if (strcmp(ds->period_id, DEFAULT_PERIOD_ID))
4574 0 : ds->rep->dasher_ctx->period_id = ds->period_id;
4575 :
4576 24 : ds->rep->dasher_ctx->owns_set = (ds->set->udta == ds) ? GF_TRUE : GF_FALSE;
4577 :
4578 24 : if (ds->rep->dasher_ctx->mux_pids) gf_free(ds->rep->dasher_ctx->mux_pids);
4579 24 : ds->rep->dasher_ctx->mux_pids = NULL;
4580 69 : for (j=0; j<count; j++) {
4581 : char szMuxPID[10];
4582 45 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
4583 89 : if (a_ds==ds) continue;
4584 21 : if (a_ds->muxed_base != ds) continue;
4585 :
4586 1 : if (ds->rep->dasher_ctx->mux_pids)
4587 0 : sprintf(szMuxPID, " %d", a_ds->id);
4588 : else
4589 1 : sprintf(szMuxPID, "%d", a_ds->id);
4590 :
4591 1 : gf_dynstrcat(&ds->rep->dasher_ctx->mux_pids, szMuxPID, NULL);
4592 : }
4593 :
4594 : }
4595 : }
4596 :
4597 34 : static GF_DashStream *dasher_get_stream(GF_DasherCtx *ctx, const char *src_url, u32 original_pid, u32 pid_id)
4598 : {
4599 34 : u32 i, count = gf_list_count(ctx->pids);
4600 14 : for (i=0; i<count; i++) {
4601 48 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
4602 48 : if (pid_id && (ds->pid_id==pid_id)) return ds;
4603 48 : if (src_url && ds->src_url && !strcmp(ds->src_url, src_url) && (ds->id == original_pid) ) return ds;
4604 : }
4605 : return NULL;
4606 : }
4607 :
4608 4 : static GF_Err dasher_reload_muxed_comp(GF_DasherCtx *ctx, GF_DashStream *base_ds, char *mux_pids, Bool check_only)
4609 : {
4610 : GF_Err e = GF_OK;
4611 8 : while (mux_pids) {
4612 : u32 pid_id;
4613 : GF_DashStream *ds;
4614 4 : char *sep = strchr(mux_pids, ' ');
4615 4 : if (sep) sep[0] = 0;
4616 :
4617 4 : pid_id = atoi(mux_pids);
4618 4 : ds = dasher_get_stream(ctx, base_ds->src_url, pid_id, 0);
4619 4 : if (ds) {
4620 4 : if (!check_only) {
4621 2 : if (ds->rep) gf_mpd_representation_free(ds->rep);
4622 2 : ds->rep = NULL;
4623 2 : ds->set = base_ds->set;
4624 2 : ds->muxed_base = base_ds;
4625 2 : base_ds->nb_comp ++;
4626 2 : ds->nb_comp = 1;
4627 2 : ds->done = base_ds->done;
4628 2 : ds->subdur_done = base_ds->subdur_done;
4629 2 : ds->period = ctx->current_period;
4630 :
4631 2 : gf_list_del_item(ctx->next_period->streams, ds);
4632 2 : gf_list_add(ctx->current_period->streams, ds);
4633 : }
4634 : } else {
4635 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't find muxed PID %d in source %s, did you modify the source ?\n", pid_id, base_ds->src_url));
4636 : e = GF_BAD_PARAM;
4637 : }
4638 :
4639 4 : if (!sep) break;
4640 0 : sep[0] = ' ';
4641 0 : mux_pids = sep+1;
4642 :
4643 0 : if (e) return e;
4644 : }
4645 : return GF_OK;
4646 : }
4647 :
4648 21 : static GF_Err dasher_reload_context(GF_Filter *filter, GF_DasherCtx *ctx)
4649 : {
4650 : GF_Err e;
4651 : Bool last_period_active = GF_FALSE;
4652 : u32 i, j, k, nb_p, nb_as, nb_rep, count;
4653 : GF_DOMParser *mpd_parser;
4654 :
4655 21 : ctx->first_context_load = GF_FALSE;
4656 :
4657 21 : if (!gf_file_exists(ctx->state)) return GF_OK;
4658 :
4659 : /* parse the MPD */
4660 10 : mpd_parser = gf_xml_dom_new();
4661 10 : e = gf_xml_dom_parse(mpd_parser, ctx->state, NULL, NULL);
4662 :
4663 10 : if (e != GF_OK) {
4664 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot parse MPD state %s: %s\n", ctx->state, gf_xml_dom_get_error(mpd_parser) ));
4665 0 : gf_xml_dom_del(mpd_parser);
4666 0 : return GF_URL_ERROR;
4667 : }
4668 10 : if (ctx->mpd) gf_mpd_del(ctx->mpd);
4669 10 : ctx->mpd = gf_mpd_new();
4670 10 : e = gf_mpd_init_from_dom(gf_xml_dom_get_root(mpd_parser), ctx->mpd, ctx->state);
4671 10 : gf_xml_dom_del(mpd_parser);
4672 : //test mode, strip URL path
4673 10 : if (gf_sys_is_test_mode()) {
4674 10 : count = gf_list_count(ctx->mpd->program_infos);
4675 20 : for (i=0; i<count; i++) {
4676 10 : GF_MPD_ProgramInfo *info = gf_list_get(ctx->mpd->program_infos, i);
4677 10 : if (info->title && strstr(info->title, "generated by GPAC")) {
4678 10 : gf_free(info->title);
4679 : char tmp[256];
4680 10 : char *name = strrchr(ctx->out_path, '/');
4681 10 : if (!name) name = strrchr(ctx->out_path, '\\');
4682 10 : if (!name) name = ctx->out_path;
4683 10 : else name++;
4684 : sprintf(tmp, "%s generated by GPAC", name);
4685 10 : info->title = gf_strdup(tmp);
4686 : }
4687 : }
4688 : }
4689 :
4690 10 : if (!ctx->mpd->xml_namespace)
4691 10 : ctx->mpd->xml_namespace = "urn:mpeg:dash:schema:mpd:2011";
4692 :
4693 10 : if (e != GF_OK) {
4694 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Cannot reload MPD state %s: %s\n", ctx->state, gf_error_to_string(e) ));
4695 0 : gf_mpd_del(ctx->mpd);
4696 0 : ctx->mpd = NULL;
4697 0 : return GF_URL_ERROR;
4698 : }
4699 :
4700 : //do a first pass to detect any potential changes in input config, if so consider the period over.
4701 10 : nb_p = gf_list_count(ctx->mpd->periods);
4702 20 : for (i=0; i<nb_p; i++) {
4703 : u32 nb_done_in_period = 0;
4704 : u32 nb_remain_in_period = 0;
4705 10 : GF_MPD_Period *p = gf_list_get(ctx->mpd->periods, i);
4706 10 : nb_as = gf_list_count(p->adaptation_sets);
4707 25 : for (j=0; j<nb_as; j++) {
4708 15 : GF_MPD_AdaptationSet *set = gf_list_get(p->adaptation_sets, j);
4709 15 : nb_rep = gf_list_count(set->representations);
4710 30 : for (k=0; k<nb_rep; k++) {
4711 : GF_DashStream *ds;
4712 : char *p_id;
4713 15 : GF_MPD_Representation *rep = gf_list_get(set->representations, k);
4714 15 : if (! rep->dasher_ctx) continue;
4715 :
4716 : //ensure we have the same settings - if not consider the dash stream has been resetup for a new period
4717 15 : ds = dasher_get_stream(ctx, rep->dasher_ctx->src_url, rep->dasher_ctx->source_pid, 0);
4718 15 : if (!ds) {
4719 0 : rep->dasher_ctx->done = 1;
4720 0 : nb_done_in_period++;
4721 0 : if (rep->dasher_ctx->last_dyn_period_id >= ctx->last_dyn_period_id)
4722 0 : ctx->last_dyn_period_id = 1 + rep->dasher_ctx->last_dyn_period_id;
4723 0 : continue;
4724 : }
4725 :
4726 15 : if (rep->dasher_ctx->done) {
4727 0 : nb_done_in_period++;
4728 0 : ds->nb_repeat = rep->dasher_ctx->nb_repeat + 1;
4729 0 : if (rep->dasher_ctx->last_dyn_period_id > ctx->last_dyn_period_id)
4730 0 : ctx->last_dyn_period_id = rep->dasher_ctx->last_dyn_period_id;
4731 0 : continue;
4732 : }
4733 :
4734 : p_id = DEFAULT_PERIOD_ID;
4735 15 : if (rep->dasher_ctx->period_id) p_id = rep->dasher_ctx->period_id;
4736 :
4737 15 : if (ds->period_id && p_id && !strcmp(ds->period_id, p_id)) {
4738 0 : } else if (!ds->period_id && !rep->dasher_ctx->period_id) {
4739 : } else {
4740 0 : rep->dasher_ctx->done = 1;
4741 0 : nb_done_in_period++;
4742 0 : continue;
4743 : }
4744 15 : if (ds->period_start.num * rep->dasher_ctx->period_start.den != rep->dasher_ctx->period_start.num * ds->period_start.den) {
4745 0 : rep->dasher_ctx->done = 1;
4746 0 : nb_done_in_period++;
4747 0 : continue;
4748 : }
4749 15 : if (ds->period_dur.num * rep->dasher_ctx->period_duration.den != rep->dasher_ctx->period_duration.num * ds->period_dur.den) {
4750 0 : rep->dasher_ctx->done = 1;
4751 0 : nb_done_in_period++;
4752 0 : continue;
4753 : }
4754 : //check we can reload muxed components - if not consider this source as removed
4755 15 : if (rep->dasher_ctx->mux_pids) {
4756 2 : e = dasher_reload_muxed_comp(ctx, ds, rep->dasher_ctx->mux_pids, GF_TRUE);
4757 2 : if (e) {
4758 0 : rep->dasher_ctx->done = 1;
4759 0 : nb_done_in_period++;
4760 0 : continue;
4761 : }
4762 : }
4763 15 : nb_remain_in_period++;
4764 : }
4765 : }
4766 10 : if (nb_remain_in_period) {
4767 : assert(i+1==nb_p);
4768 : last_period_active = GF_TRUE;
4769 : }
4770 0 : else if (nb_done_in_period && ctx->subdur ) {
4771 : //we are done but we loop the entire streams
4772 0 : for (j=0; j<gf_list_count(ctx->pids); j++) {
4773 0 : GF_DashStream *ds = gf_list_get(ctx->pids, j);
4774 0 : ds->done = 0;
4775 0 : ds->segment_started = GF_FALSE;
4776 0 : ds->seg_done = GF_FALSE;
4777 0 : ds->cumulated_dur = 0;
4778 0 : ds->cumulated_subdur = 0;
4779 : }
4780 : }
4781 : }
4782 :
4783 10 : if (!last_period_active) return GF_OK;
4784 10 : ctx->current_period->period = gf_list_last(ctx->mpd->periods);
4785 10 : gf_list_reset(ctx->current_period->streams);
4786 10 : gf_list_del(ctx->next_period->streams);
4787 10 : ctx->next_period->streams = gf_list_clone(ctx->pids);
4788 :
4789 10 : if (ctx->current_period->period->duration) {
4790 : //reset last period duration and cumulated dur of MPD
4791 1 : if (ctx->mpd->media_presentation_duration>ctx->current_period->period->duration)
4792 0 : ctx->mpd->media_presentation_duration -= ctx->current_period->period->duration;
4793 : else
4794 1 : ctx->mpd->media_presentation_duration = 0;
4795 1 : ctx->current_period->period->duration = 0;
4796 : }
4797 :
4798 10 : nb_as = gf_list_count(ctx->current_period->period->adaptation_sets);
4799 25 : for (j=0; j<nb_as; j++) {
4800 : GF_DashStream *set_ds = NULL;
4801 : GF_List *multi_pids = NULL;
4802 : Bool use_multi_pid_init = GF_FALSE;
4803 15 : GF_MPD_AdaptationSet *set = gf_list_get(ctx->current_period->period->adaptation_sets, j);
4804 15 : nb_rep = gf_list_count(set->representations);
4805 30 : for (k=0; k<nb_rep; k++) {
4806 : GF_DashStream *ds;
4807 15 : GF_MPD_Representation *rep = gf_list_get(set->representations, k);
4808 15 : if (! rep->dasher_ctx) continue;
4809 :
4810 15 : ds = dasher_get_stream(ctx, rep->dasher_ctx->src_url, rep->dasher_ctx->source_pid, 0);
4811 15 : if (!ds) continue;
4812 :
4813 : //restore everything
4814 15 : ds->done = rep->dasher_ctx->done;
4815 15 : ds->seg_number = rep->dasher_ctx->seg_number;
4816 :
4817 15 : if (ds->init_seg) gf_free(ds->init_seg);
4818 15 : ds->init_seg = gf_strdup(rep->dasher_ctx->init_seg);
4819 :
4820 15 : if (ds->seg_template) gf_free(ds->seg_template);
4821 15 : ds->seg_template = gf_strdup(rep->dasher_ctx->template_seg);
4822 :
4823 15 : if (ds->idx_template) gf_free(ds->idx_template);
4824 15 : ds->idx_template = rep->dasher_ctx->template_idx ? gf_strdup(rep->dasher_ctx->template_idx) : NULL;
4825 :
4826 15 : if (rep->dasher_ctx->period_id) {
4827 0 : if (ds->period_id) gf_free(ds->period_id);
4828 0 : ds->period_id = gf_strdup(rep->dasher_ctx->period_id);
4829 : }
4830 :
4831 15 : ds->period_start = rep->dasher_ctx->period_start;
4832 15 : if (!ds->period_start.den) {
4833 15 : ds->period_start.num = 0;
4834 15 : ds->period_start.den = 1;
4835 : }
4836 15 : ds->period_dur = rep->dasher_ctx->period_duration;
4837 15 : if (!ds->period_dur.den) {
4838 15 : ds->period_dur.num = 0;
4839 15 : ds->period_dur.den = 1;
4840 : }
4841 15 : ds->pid_id = rep->dasher_ctx->pid_id;
4842 15 : ds->dep_pid_id = rep->dasher_ctx->dep_pid_id;
4843 15 : ds->seek_to_pck = rep->dasher_ctx->last_pck_idx;
4844 15 : ds->dash_dur = rep->dasher_ctx->dash_dur;
4845 15 : if (!ds->dash_dur.den) {
4846 0 : ds->dash_dur.num = 0;
4847 0 : ds->dash_dur.den = 1;
4848 : }
4849 15 : ds->next_seg_start = rep->dasher_ctx->next_seg_start;
4850 15 : ds->adjusted_next_seg_start = ds->next_seg_start;
4851 15 : ds->first_cts = rep->dasher_ctx->first_cts;
4852 15 : ds->first_dts = rep->dasher_ctx->first_dts;
4853 15 : ds->ts_offset = rep->dasher_ctx->ts_offset;
4854 15 : ds->est_next_dts = rep->dasher_ctx->est_next_dts;
4855 15 : ds->mpd_timescale = rep->dasher_ctx->mpd_timescale;
4856 15 : ds->cumulated_dur = (u64) (rep->dasher_ctx->cumulated_dur * ds->timescale);
4857 15 : ds->cumulated_subdur = rep->dasher_ctx->cumulated_subdur;
4858 15 : ds->rep_init = GF_TRUE;
4859 15 : ds->subdur_done = rep->dasher_ctx->subdur_forced ? GF_TRUE : GF_FALSE;
4860 15 : ds->subdur_forced_use_period_dur = 0;
4861 15 : ds->nb_pck = 0;
4862 15 : if (!ctx->subdur) {
4863 0 : ds->nb_pck = ds->seek_to_pck;
4864 0 : ds->seek_to_pck = 0;
4865 : }
4866 15 : ds->nb_segments_purged = rep->dasher_ctx->segs_purged;
4867 15 : ds->dur_purged = rep->dasher_ctx->dur_purged;
4868 15 : ds->moof_sn = rep->dasher_ctx->moof_sn;
4869 15 : ds->moof_sn_inc = rep->dasher_ctx->moof_sn_inc;
4870 15 : ctx->last_dyn_period_id = rep->dasher_ctx->last_dyn_period_id;
4871 :
4872 15 : if (ctx->store_seg_states && !ds->pending_segment_states)
4873 3 : ds->pending_segment_states = gf_list_new();
4874 :
4875 15 : if (rep->segment_list && !ds->pending_segment_urls)
4876 0 : ds->pending_segment_urls = gf_list_new();
4877 :
4878 15 : ds->owns_set = rep->dasher_ctx->owns_set;
4879 15 : if (ds->owns_set) set_ds = ds;
4880 :
4881 15 : if (rep->dasher_ctx->done) {
4882 0 : ds->done = 1;
4883 0 : if (ds->rep) gf_mpd_representation_free(ds->rep);
4884 0 : ds->rep = NULL;
4885 0 : continue;
4886 : }
4887 :
4888 15 : ds->nb_comp = 1;
4889 :
4890 15 : if (ds->rep) gf_mpd_representation_free(ds->rep);
4891 15 : ds->rep = rep;
4892 15 : ds->set = set;
4893 15 : rep->playback.udta = ds;
4894 15 : if (ds->owns_set)
4895 15 : set->udta = ds;
4896 15 : if (rep->dasher_ctx->multi_pids)
4897 : use_multi_pid_init = GF_TRUE;
4898 :
4899 15 : ds->period = ctx->current_period;
4900 15 : if ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA)) {
4901 0 : const GF_PropertyValue *prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_MHA_COMPATIBLE_PROFILES);
4902 0 : if (prop) {
4903 0 : ds->set->nb_alt_mha_profiles = prop->value.uint_list.nb_items;
4904 0 : ds->set->alt_mha_profiles = prop->value.uint_list.vals;
4905 : }
4906 : }
4907 :
4908 :
4909 15 : gf_list_del_item(ctx->next_period->streams, ds);
4910 :
4911 : //non-muxed component or main comp of muxed goes first in the list
4912 15 : if (ds->nb_comp>1) {
4913 0 : gf_list_insert(ctx->current_period->streams, ds, 0);
4914 : } else {
4915 15 : gf_list_add(ctx->current_period->streams, ds);
4916 : }
4917 :
4918 15 : if (rep->dasher_ctx->mux_pids) {
4919 2 : e = dasher_reload_muxed_comp(ctx, ds, rep->dasher_ctx->mux_pids, GF_FALSE);
4920 2 : if (e) return e;
4921 : }
4922 : }
4923 : assert(set_ds);
4924 15 : set_ds->nb_rep = gf_list_count(set->representations);
4925 :
4926 : //if multi PID init, gather pids
4927 15 : if (use_multi_pid_init) {
4928 0 : multi_pids = gf_list_new();
4929 0 : for (i=0; i<nb_rep; i++) {
4930 0 : GF_MPD_Representation *rep = gf_list_get(set->representations, i);
4931 0 : GF_DashStream *ds = rep->playback.udta;
4932 0 : if (ds->owns_set) ds->multi_pids = multi_pids;
4933 0 : gf_list_add(multi_pids, ds->ipid);
4934 : }
4935 : }
4936 15 : count = gf_list_count(ctx->current_period->streams);
4937 30 : for (i=0; i<nb_rep; i++) {
4938 15 : GF_MPD_Representation *rep = gf_list_get(set->representations, i);
4939 15 : GF_DashStream *ds = rep->playback.udta;
4940 15 : if (!ds || ds->done) continue;
4941 : //happens when reloading context without closing the filter
4942 15 : if (ds->dst_filter || ds->opid) continue;
4943 :
4944 : //open destination, trashing init
4945 : assert(!ds->muxed_base);
4946 3 : dasher_open_destination(filter, ctx, rep, ds->init_seg, GF_TRUE);
4947 :
4948 3 : dasher_open_pid(filter, ctx, ds, multi_pids, GF_TRUE);
4949 :
4950 8 : for (j=0; j<count; j++) {
4951 5 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
4952 5 : if (a_ds->muxed_base != ds) continue;
4953 :
4954 2 : dasher_open_pid(filter, ctx, a_ds, multi_pids, GF_TRUE);
4955 : }
4956 : }
4957 : }
4958 :
4959 : return GF_OK;
4960 : }
4961 :
4962 261 : static void dasher_udpate_periods_and_manifest(GF_Filter *filter, GF_DasherCtx *ctx)
4963 : {
4964 261 : if (!ctx->subdur_done) {
4965 241 : ctx->last_dyn_period_id++;
4966 241 : ctx->next_pid_id_in_period = 0;
4967 : }
4968 : //update duration
4969 261 : dasher_update_period_duration(ctx, GF_TRUE);
4970 :
4971 261 : if (ctx->state)
4972 32 : dasher_context_update_period_end(ctx);
4973 :
4974 : //we have a MPD ready, flush it
4975 261 : if (ctx->mpd)
4976 261 : dasher_send_manifest(filter, ctx, GF_FALSE);
4977 261 : }
4978 :
4979 : typedef struct
4980 : {
4981 : GF_Fraction64 period_start;
4982 : const char *period_id;
4983 : } PeriodInfo;
4984 :
4985 0 : static u32 dasher_period_count(GF_List *streams_in /*GF_DashStream*/)
4986 : {
4987 : u32 nb_periods, i, j;
4988 : PeriodInfo *info;
4989 0 : GF_List *pinfos = gf_list_new();
4990 :
4991 0 : for (i=0; i < gf_list_count(streams_in); i++) {
4992 : Bool same_period = GF_FALSE;
4993 0 : GF_DashStream *ds = gf_list_get(streams_in, i);
4994 : //check if we already have a period info with same ID or same start time
4995 0 : nb_periods = gf_list_count(pinfos);
4996 0 : for (j=0; j < nb_periods; j++) {
4997 0 : info = gf_list_get(pinfos, j);
4998 0 : if (info->period_start.num * ds->period_start.den == ds->period_start.num * info->period_start.den) {
4999 : same_period = GF_TRUE;
5000 : break;
5001 : }
5002 0 : if (info->period_id && ds->period_id && !strcmp(info->period_id, ds->period_id)) {
5003 : same_period = GF_TRUE;
5004 : break;
5005 : }
5006 : }
5007 : //nope, register it
5008 0 : if (!same_period) {
5009 0 : GF_SAFEALLOC(info, PeriodInfo);
5010 0 : if (info) {
5011 0 : info->period_start = ds->period_start;
5012 0 : info->period_id = ds->period_id;
5013 0 : gf_list_add(pinfos, info);
5014 : }
5015 : }
5016 : }
5017 0 : nb_periods = gf_list_count(pinfos);
5018 : while (1) {
5019 0 : info = gf_list_pop_back(pinfos);
5020 0 : if (!info) break;
5021 0 : gf_free(info);
5022 : }
5023 0 : gf_list_del(pinfos);
5024 :
5025 0 : return nb_periods;
5026 : }
5027 :
5028 25 : static void dasher_init_utc(GF_Filter *filter, GF_DasherCtx *ctx)
5029 : {
5030 : const char *cache_name;
5031 : u32 size;
5032 : u8 *data;
5033 : u64 remote_utc;
5034 : GF_Err e;
5035 : GF_DownloadSession *sess;
5036 : GF_DownloadManager *dm;
5037 : char *url;
5038 : DasherUTCTimingType def_type = DASHER_UTCREF_NONE;
5039 :
5040 25 : ctx->utc_initialized = GF_TRUE;
5041 25 : ctx->utc_timing_type = DASHER_UTCREF_NONE;
5042 25 : if (!ctx->utcs) {
5043 25 : return;
5044 : }
5045 : url = ctx->utcs;
5046 0 : if (!strncmp(url, "xsd@", 4)) {
5047 : def_type = DASHER_UTCREF_XSDATE;
5048 0 : url += 4;
5049 : }
5050 :
5051 0 : dm = gf_filter_get_download_manager(filter);
5052 0 : if (!dm) {
5053 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to get download manager, cannot sync to remote UTC clock\n"));
5054 : return;
5055 : }
5056 0 : if (!strcmp(ctx->utcs, "inband")) {
5057 0 : ctx->utc_timing_type = DASHER_UTCREF_INBAND;
5058 0 : return;
5059 : }
5060 :
5061 0 : sess = gf_dm_sess_new(dm, url, GF_NETIO_SESSION_MEMORY_CACHE|GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
5062 0 : if (e) {
5063 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to create session for remote UTC source %s: %s - local clock will be used instead\n", url, gf_error_to_string(e) ));
5064 : return;
5065 : }
5066 0 : while (1) {
5067 : GF_NetIOStatus status;
5068 0 : e = gf_dm_sess_process(sess);
5069 0 : if (e) break;
5070 0 : gf_dm_sess_get_stats(sess, NULL, NULL, NULL, NULL, NULL, &status);
5071 0 : if (status>=GF_NETIO_DATA_TRANSFERED) break;
5072 : }
5073 0 : if (e<0) {
5074 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to fetch remote UTC source %s: %s\n", url, gf_error_to_string(e) ));
5075 0 : gf_dm_sess_del(sess);
5076 0 : return;
5077 : }
5078 0 : cache_name = gf_dm_sess_get_cache_name(sess);
5079 0 : gf_blob_get(cache_name, &data, &size, NULL);
5080 0 : if (data) {
5081 : //xsDate or isoDate - we always signal using iso
5082 0 : if (strchr(data, 'T')) {
5083 0 : remote_utc = gf_net_parse_date(data);
5084 0 : if (remote_utc)
5085 0 : ctx->utc_timing_type = def_type ? def_type : DASHER_UTCREF_ISO;
5086 : }
5087 : //ntp
5088 0 : else if (sscanf(data, LLU, &remote_utc) == 1) {
5089 : //ntp value not counted since 1900, assume format is seconds till 1 jan 1970
5090 0 : if (remote_utc<=GF_NTP_SEC_1900_TO_1970) {
5091 0 : remote_utc = remote_utc*1000;
5092 : } else {
5093 0 : remote_utc = gf_net_ntp_to_utc(remote_utc);
5094 : }
5095 0 : if (remote_utc)
5096 0 : ctx->utc_timing_type = DASHER_UTCREF_NTP;
5097 : }
5098 : }
5099 0 : gf_blob_release(cache_name);
5100 :
5101 : //not match, try http date
5102 0 : if (!ctx->utc_timing_type) {
5103 0 : const char *hdr = gf_dm_sess_get_header(sess, "Date");
5104 0 : if (hdr) {
5105 : //http-head
5106 0 : remote_utc = gf_net_parse_date(hdr);
5107 0 : if (remote_utc)
5108 0 : ctx->utc_timing_type = DASHER_UTCREF_HTTP_HEAD;
5109 : }
5110 : }
5111 :
5112 0 : if (!ctx->utc_timing_type) {
5113 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to parse response %s from remote UTC source %s\n", data, url ));
5114 : } else {
5115 0 : ctx->utc_diff = (s32) ( (s64) gf_net_get_utc() - (s64) remote_utc );
5116 0 : if (ABS(ctx->utc_diff) > 3600000) {
5117 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Diff between local clock and remote %s is %d, way too large! Assuming 0 ms UTC diff\n", url, ctx->utc_diff));
5118 0 : ctx->utc_diff = 0;
5119 : } else {
5120 0 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] Synchronized clock to remote %s - UTC diff (local - remote) %d ms\n", url, ctx->utc_diff));
5121 : }
5122 :
5123 0 : if (!gf_list_count(ctx->mpd->utc_timings) ) {
5124 : Bool dashif_ok = GF_FALSE;
5125 : GF_MPD_Descriptor *utc_t;
5126 0 : GF_SAFEALLOC(utc_t, GF_MPD_Descriptor);
5127 0 : utc_t->value = gf_strdup(url);
5128 0 : switch (ctx->utc_timing_type) {
5129 0 : case DASHER_UTCREF_HTTP_HEAD:
5130 0 : utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-head:2014");
5131 : break;
5132 0 : case DASHER_UTCREF_XSDATE:
5133 0 : utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-xsdate:2014");
5134 : dashif_ok = GF_TRUE;
5135 : break;
5136 0 : case DASHER_UTCREF_ISO:
5137 0 : utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-iso:2014");
5138 : dashif_ok = GF_TRUE;
5139 : break;
5140 0 : case DASHER_UTCREF_NTP:
5141 0 : utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:http-ntp:2014");
5142 : dashif_ok = GF_TRUE;
5143 : break;
5144 0 : case DASHER_UTCREF_INBAND:
5145 0 : utc_t->scheme_id_uri = gf_strdup("urn:mpeg:dash:utc:direct:2014");
5146 : break;
5147 : default:
5148 : break;
5149 : }
5150 0 : if (!dashif_ok && (ctx->profile==GF_DASH_PROFILE_DASHIF_LL)) {
5151 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] UTC reference %s allowed in DASH-IF Low Latency profile\n\tswitching to regular live profile\n", utc_t->scheme_id_uri));
5152 0 : ctx->profile = GF_DASH_PROFILE_LIVE;
5153 : }
5154 0 : if (!ctx->mpd->utc_timings)
5155 0 : ctx->mpd->utc_timings = gf_list_new();
5156 0 : gf_list_add(ctx->mpd->utc_timings, utc_t);
5157 : }
5158 : }
5159 0 : gf_dm_sess_del(sess);
5160 : }
5161 :
5162 :
5163 511 : static GF_Err dasher_switch_period(GF_Filter *filter, GF_DasherCtx *ctx)
5164 : {
5165 : u32 i, count, nb_done;
5166 : char *period_id;
5167 : const char *remote_xlink = NULL;
5168 : const char *period_xlink = NULL;
5169 : u64 remote_dur = 0;
5170 : GF_DasherPeriod *p;
5171 : GF_Fraction64 period_start, next_period_start;
5172 : GF_DashStream *first_in_period=NULL;
5173 511 : p = ctx->current_period;
5174 :
5175 511 : if (!ctx->out_path) {
5176 4 : dasher_check_outpath(ctx);
5177 : }
5178 511 : if (ctx->current_period->period) {
5179 260 : if (ctx->dyn_rate)
5180 44 : dasher_update_dyn_bitrates(ctx);
5181 :
5182 260 : dasher_udpate_periods_and_manifest(filter, ctx);
5183 : }
5184 :
5185 511 : if (ctx->subdur_done || (ctx->current_period->period && (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST)) )
5186 : return GF_EOS;
5187 :
5188 484 : if (ctx->current_period->period) {
5189 233 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of Period %s\n", ctx->current_period->period->ID ? ctx->current_period->period->ID : ""));
5190 : }
5191 484 : ctx->is_period_restore = GF_FALSE;
5192 484 : ctx->is_empty_period = GF_FALSE;
5193 :
5194 : //safety check at period switch, probe each first packet in case we have a reconfigure pending
5195 484 : count = gf_list_count(ctx->pids);
5196 1200 : for (i=0; i<count;i++) {
5197 716 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
5198 716 : gf_filter_pid_get_packet(ds->ipid);
5199 : }
5200 :
5201 : //reset - don't destroy, it is in the MPD
5202 484 : ctx->current_period->period = NULL;
5203 : //switch
5204 484 : ctx->current_period = ctx->next_period;
5205 484 : ctx->next_period = p;
5206 484 : ctx->on_demand_done = GF_FALSE;
5207 :
5208 : //reset input pids and detach output pids
5209 484 : count = gf_list_count(ctx->current_period->streams);
5210 858 : for (i=0; i<count;i++) {
5211 374 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5212 374 : if (ds->opid) {
5213 9 : gf_filter_pid_remove(ds->opid);
5214 9 : ds->opid = NULL;
5215 : }
5216 374 : dasher_reset_stream(filter, ds, GF_FALSE);
5217 374 : if (ds->reschedule) {
5218 6 : ds->reschedule = GF_FALSE;
5219 6 : ds->done = 0;
5220 : }
5221 : }
5222 :
5223 : //figure out next period
5224 484 : count = gf_list_count(ctx->current_period->streams);
5225 484 : ctx->period_idx = 0;
5226 : period_start.num = -1;
5227 : period_start.den = 1;
5228 858 : for (i=0; i<count; i++) {
5229 374 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5230 :
5231 374 : if (ds->done) continue;
5232 368 : if (ds->period_start.num < 0) {
5233 6 : s32 pstart = (s32) -ds->period_start.num;
5234 6 : if (!ctx->period_idx || (pstart < ctx->period_idx)) ctx->period_idx = pstart;
5235 : } else {
5236 362 : if ((period_start.num<0) || (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den)) {
5237 247 : period_start = ds->period_start;
5238 : assert(ds->period_start.den);
5239 : }
5240 : }
5241 : }
5242 :
5243 484 : if (period_start.num >= 0)
5244 247 : ctx->period_idx = 0;
5245 :
5246 484 : if (ctx->first_context_load) {
5247 21 : GF_Err e = dasher_reload_context(filter, ctx);
5248 21 : if (e) {
5249 0 : ctx->setup_failure = e;
5250 0 : return e;
5251 : }
5252 21 : if (ctx->current_period->period) ctx->is_period_restore = GF_TRUE;
5253 :
5254 21 : if (ctx->dmode==GF_DASH_DYNAMIC_LAST) {
5255 1 : dasher_udpate_periods_and_manifest(filter, ctx);
5256 1 : count = gf_list_count(ctx->pids);
5257 3 : for (i=0; i<count; i++) {
5258 2 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
5259 2 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
5260 : }
5261 : return GF_EOS;
5262 : }
5263 : }
5264 :
5265 : //filter out PIDs not for this period
5266 483 : count = gf_list_count(ctx->current_period->streams);
5267 : period_id = NULL;
5268 867 : for (i=0; i<count; i++) {
5269 : Bool in_period=GF_TRUE;
5270 384 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5271 :
5272 384 : if (ds->done) {
5273 : in_period=GF_FALSE;
5274 378 : } else if (!period_id) {
5275 257 : period_id = ds->period_id;
5276 : first_in_period = ds;
5277 121 : } else if (strcmp(period_id, ds->period_id)) {
5278 : in_period = GF_FALSE;
5279 : }
5280 384 : if (in_period) {
5281 374 : if ((period_start.num>=0) && (ds->period_start.num * period_start.den != period_start.num * ds->period_start.den))
5282 : in_period = GF_FALSE;
5283 374 : else if ((ctx->period_idx>0) && ((s32) -ds->period_start.num != ctx->period_idx))
5284 : in_period = GF_FALSE;
5285 :
5286 374 : if (!in_period && (first_in_period == ds))
5287 : period_id = NULL;
5288 : }
5289 :
5290 : //if not in period, move to next period
5291 384 : if (!in_period) {
5292 11 : gf_list_rem(ctx->current_period->streams, i);
5293 11 : i--;
5294 11 : count--;
5295 11 : ds->period = NULL;
5296 11 : gf_list_add(ctx->next_period->streams, ds);
5297 11 : continue;
5298 : }
5299 373 : if (ds->stream_type == GF_STREAM_FILE) {
5300 1 : if (ds->xlink) remote_xlink = ds->xlink;
5301 0 : else ctx->is_empty_period = GF_TRUE;
5302 : remote_dur = 0;
5303 1 : if (ds->period_dur.den)
5304 1 : remote_dur = (u64) (ds->period_dur.num * 1000) / ds->period_dur.den;
5305 372 : } else if (!ctx->is_period_restore) {
5306 357 : if (ds->xlink)
5307 : period_xlink = ds->xlink;
5308 :
5309 357 : if (ctx->post_play_events) {
5310 : GF_FilterEvent evt;
5311 :
5312 6 : GF_FEVT_INIT(evt, GF_FEVT_STOP, ds->ipid);
5313 6 : gf_filter_pid_send_event(ds->ipid, &evt);
5314 :
5315 6 : gf_filter_pid_set_discard(ds->ipid, GF_FALSE);
5316 :
5317 6 : dasher_send_encode_hints(ctx, ds);
5318 :
5319 6 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
5320 6 : evt.play.speed = 1.0;
5321 6 : gf_filter_pid_send_event(ds->ipid, &evt);
5322 : }
5323 : }
5324 : }
5325 483 : ctx->post_play_events = GF_FALSE;
5326 :
5327 483 : count = gf_list_count(ctx->current_period->streams);
5328 483 : if (!count) {
5329 227 : count = gf_list_count(ctx->next_period->streams);
5330 : nb_done = 0;
5331 558 : for (i=0; i<count; i++) {
5332 331 : GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
5333 331 : if (ds->done) nb_done++;
5334 : }
5335 227 : if (nb_done == count) {
5336 227 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] End of MPD (no more active streams)\n"));
5337 227 : ctx->on_demand_done = GF_TRUE;
5338 227 : return GF_EOS;
5339 : }
5340 : }
5341 :
5342 : //we need a new period unless created during reload, create it
5343 256 : if (!ctx->is_period_restore) {
5344 247 : ctx->current_period->period = gf_mpd_period_new();
5345 247 : if (!ctx->mpd) dasher_setup_mpd(ctx);
5346 247 : gf_list_add(ctx->mpd->periods, ctx->current_period->period);
5347 : }
5348 :
5349 :
5350 256 : if (remote_xlink) {
5351 1 : ctx->current_period->period->xlink_href = gf_strdup(remote_xlink);
5352 1 : ctx->current_period->period->duration = remote_dur;
5353 : }
5354 255 : else if (period_xlink) {
5355 0 : ctx->current_period->period->xlink_href = gf_strdup(period_xlink);
5356 : }
5357 :
5358 :
5359 : assert(period_id);
5360 :
5361 : next_period_start.num = -1;
5362 : next_period_start.den = 1;
5363 256 : if (period_start.num >= 0) {
5364 246 : ctx->current_period->period->start = (u64)(period_start.num*1000 / period_start.den);
5365 : //check next period start
5366 246 : count = gf_list_count(ctx->next_period->streams);
5367 256 : for (i=0; i<count; i++) {
5368 10 : GF_DashStream *ds = gf_list_get(ctx->next_period->streams, i);
5369 10 : if (ds->done) continue;
5370 8 : if (ds->period_start.num * period_start.den < period_start.num * ds->period_start.den) continue;
5371 8 : if ((next_period_start.num<0) || (next_period_start.num * ds->period_start.den > ds->period_start.num * next_period_start.den)) {
5372 : next_period_start = ds->period_start;
5373 : }
5374 : }
5375 : //check current period dur
5376 246 : count = gf_list_count(ctx->current_period->streams);
5377 604 : for (i=0; i<count; i++) {
5378 : GF_Fraction64 dur;
5379 358 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5380 358 : if (!ds->period_dur.den) continue;
5381 : dur = period_start;
5382 : if (ds->period_dur.den) {
5383 358 : if (dur.den != ds->period_dur.den)
5384 0 : dur.num += ds->period_dur.num * dur.den / ds->period_dur.den;
5385 : else
5386 358 : dur.num += ds->period_dur.num;
5387 : }
5388 :
5389 358 : if ((next_period_start.num < 0) || (next_period_start.num * dur.den > dur.num * next_period_start.den))
5390 : next_period_start = dur;
5391 : }
5392 246 : if (next_period_start.num > 0) {
5393 0 : u64 next = next_period_start.num;
5394 0 : if (next_period_start.den != period_start.den) {
5395 0 : next *= period_start.den;
5396 0 : next /= next_period_start.den;
5397 : }
5398 0 : ctx->current_period->period->duration = (u32) ( (next - period_start.num) * 1000 / period_start.den);
5399 : }
5400 : }
5401 :
5402 : //assign period ID if none specified
5403 256 : if (strcmp(period_id, DEFAULT_PERIOD_ID))
5404 5 : ctx->current_period->period->ID = gf_strdup(period_id);
5405 : //assign ID if dynamic - if dash_ctx also assign ID since we could have moved from dynamic to static
5406 251 : else if (!ctx->current_period->period->ID && ((ctx->dmode != GF_MPD_TYPE_STATIC) || ctx->state) ) {
5407 : char szPName[50];
5408 30 : sprintf(szPName, "DID%d", ctx->last_dyn_period_id + 1);
5409 30 : ctx->current_period->period->ID = gf_strdup(szPName);
5410 : }
5411 :
5412 : //check all streams are ready
5413 256 : ctx->period_not_ready = GF_FALSE;
5414 256 : count = gf_list_count(ctx->current_period->streams);
5415 629 : for (i=0; i<count; i++) {
5416 373 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5417 : //assign force_rep_end
5418 373 : if (next_period_start.num > 0) {
5419 0 : u64 next = next_period_start.num;
5420 0 : if (next_period_start.den != period_start.den) {
5421 0 : next *= period_start.den;
5422 0 : next /= next_period_start.den;
5423 : }
5424 :
5425 0 : ds->force_rep_end = (u64) ((next - period_start.num) * ds->timescale / period_start.den);
5426 : }
5427 373 : if (ds->dcd_not_ready) {
5428 9 : ctx->period_not_ready = GF_TRUE;
5429 : }
5430 : }
5431 : //not all streams are ready, cannot setup period yet
5432 256 : if (ctx->period_not_ready)
5433 : return GF_OK;
5434 :
5435 251 : return dasher_setup_period(filter, ctx, NULL);
5436 : }
5437 :
5438 256 : static GF_Err dasher_setup_period(GF_Filter *filter, GF_DasherCtx *ctx, GF_DashStream *inject_ds)
5439 : {
5440 : u32 i, count, j, nb_sets;
5441 : Bool has_muxed_bases=GF_FALSE;
5442 : const char *remote_xlink = NULL;
5443 : Bool has_as_id = GF_FALSE;
5444 : Bool has_deps = GF_FALSE;
5445 : const GF_PropertyValue *prop;
5446 : GF_Fraction64 min_dur, min_adur, max_adur;
5447 : u32 srd_rep_idx;
5448 :
5449 256 : ctx->dyn_rate = GF_FALSE;
5450 256 : ctx->use_cues = GF_FALSE;
5451 : min_dur.num = min_adur.num = max_adur.num = 0;
5452 : min_dur.den = min_adur.den = max_adur.den = 1;
5453 256 : srd_rep_idx = 2; //2 for compat with old arch
5454 :
5455 256 : count = gf_list_count(ctx->current_period->streams);
5456 : //setup representations
5457 629 : for (i=0; i<count; i++) {
5458 373 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5459 373 : if (inject_ds && (ds != inject_ds))
5460 0 : continue;
5461 :
5462 373 : if (ds->stream_type == GF_STREAM_FILE) {
5463 1 : if (ds->xlink) remote_xlink = ds->xlink;
5464 372 : } else if (!ctx->is_period_restore) {
5465 : //setup representation - the representation is created independently from the period
5466 357 : dasher_setup_rep(ctx, ds, &srd_rep_idx);
5467 : }
5468 : }
5469 :
5470 : //setup representation dependency / components (muxed)
5471 373 : for (i=0; i<count; i++) {
5472 : Bool remove = GF_FALSE;
5473 : GF_DashStream *ds_video=NULL;
5474 373 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5475 373 : if (inject_ds && (ds != inject_ds))
5476 0 : continue;
5477 :
5478 373 : ds->period = ctx->current_period;
5479 373 : ds->last_period = ds->period->period;
5480 :
5481 373 : if (ds->dyn_bitrate) ctx->dyn_rate = GF_TRUE;
5482 373 : if (ds->inband_cues || ds->cues)
5483 37 : ctx->use_cues = GF_TRUE;
5484 :
5485 373 : if (ctx->loop) {
5486 28 : prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_DURATION);
5487 28 : if (prop && prop->value.lfrac.den) {
5488 : GF_Fraction64 d;
5489 28 : d.num = prop->value.lfrac.num;
5490 : d.den = prop->value.lfrac.den;
5491 28 : if (ds->clamped_dur.num && (ds->clamped_dur.num * d.den < d.num * ds->clamped_dur.den)) {
5492 : d = ds->clamped_dur;
5493 : }
5494 :
5495 28 : if (ds->stream_type == GF_STREAM_AUDIO) {
5496 14 : if (d.num * max_adur.den > max_adur.num * d.den) max_adur = d;
5497 14 : if (!min_adur.num || (d.num * min_adur.den < min_adur.num * d.den)) min_adur = d;
5498 : } else {
5499 14 : if (!min_dur.num || (d.num * min_dur.den < min_dur.num * d.den)) min_dur = d;
5500 : }
5501 : }
5502 : }
5503 :
5504 373 : if (ds->stream_type == GF_STREAM_FILE) {
5505 : remove = GF_TRUE;
5506 372 : } else if (remote_xlink) {
5507 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] period uses xlink but other media source %s, ignoring source\n", ds->src_url));
5508 : remove = GF_TRUE;
5509 372 : } else if (ctx->is_empty_period) {
5510 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] empty period defined but other media source %s, ignoring source\n", ds->src_url));
5511 : remove = GF_TRUE;
5512 : }
5513 :
5514 : if (remove) {
5515 1 : ds->done = 1;
5516 1 : ds->period = NULL;
5517 1 : gf_list_rem(ctx->current_period->streams, i);
5518 1 : gf_list_add(ctx->next_period->streams, ds);
5519 1 : i--;
5520 1 : count--;
5521 1 : continue;
5522 : }
5523 :
5524 387 : if (ctx->is_period_restore) continue;
5525 :
5526 : //add period descriptors
5527 357 : dasher_add_descriptors(&ctx->current_period->period->x_children, ds->p_period_desc);
5528 : //add representation descriptors
5529 357 : dasher_add_descriptors(&ds->rep->x_children, ds->p_rep_desc);
5530 :
5531 357 : if (ds->muxed_base) continue;
5532 :
5533 347 : if (ds->stream_type==GF_STREAM_VISUAL)
5534 : ds_video = ds;
5535 :
5536 347 : ds->skip_tpl_reuse = GF_FALSE;
5537 : // period resume (end of content replacement/splice/...): if using templates, check if period ID is used, if not force startNumber to resume
5538 347 : prop = gf_filter_pid_get_property_str(ds->ipid, "period_resume");
5539 347 : if (prop && prop->value.string && ctx->tpl && ds->mpd_timescale) {
5540 0 : char *template = ds->template;
5541 0 : if (!template) template = ctx->template;
5542 0 : if (
5543 : //undefined period name
5544 0 : !prop->value.string[0]
5545 : //template dor not resolve against period name
5546 0 : || (template && !strstr(template, "$Period$"))
5547 : ) {
5548 0 : u64 seg_duration = (u64)(ds->dash_dur.num) * ds->mpd_timescale / ds->dash_dur.den;
5549 0 : u64 period_start = ds->mpd_timescale * ctx->mpd->media_presentation_duration / 1000;
5550 0 : u64 num = period_start / seg_duration;
5551 0 : if (num * seg_duration < period_start)
5552 0 : num++;
5553 0 : ds->startNumber = (u32) (num+1);
5554 0 : ds->skip_tpl_reuse = GF_TRUE;
5555 : }
5556 : }
5557 :
5558 347 : ds->nb_comp = 1;
5559 :
5560 1424 : for (j=0; j<count; j++) {
5561 : GF_DashStream *a_ds;
5562 1077 : if (i==j) continue;
5563 730 : a_ds = gf_list_get(ctx->current_period->streams, j);
5564 730 : if (a_ds->dep_id && (a_ds->src_id==ds->src_id) && (a_ds->dep_id==ds->id) ) {
5565 39 : gf_list_add(ds->complementary_streams, a_ds);
5566 : has_deps = GF_TRUE;
5567 39 : if (!a_ds->rep->dependency_id) {
5568 39 : a_ds->rep->dependency_id = gf_strdup(ds->rep->id);
5569 : }
5570 : }
5571 : //check if this rep should be muxed: same rep ID, not raw format, not CMAF
5572 730 : if (a_ds->muxed_base) continue;
5573 730 : if (ctx->muxtype==DASHER_MUX_RAW) continue;
5574 728 : if (ctx->cmaf) continue;
5575 728 : if (strcmp(a_ds->rep_id, ds->rep_id)) continue;
5576 :
5577 :
5578 10 : a_ds->muxed_base = ds;
5579 10 : a_ds->dash_dur = ds->dash_dur;
5580 : has_muxed_bases = GF_TRUE;
5581 10 : ds->nb_comp++;
5582 :
5583 10 : if (ctx->bs_switch==DASHER_BS_SWITCH_MULTI) {
5584 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Bitstream Swicthing mode \"multi\" is not supported with multiplexed representations, disabling bitstream switching\n"));
5585 0 : ctx->bs_switch = DASHER_BS_SWITCH_OFF;
5586 : }
5587 10 : if (!ds->rep->codecs || !strstr(ds->rep->codecs, a_ds->rep->codecs)) {
5588 10 : gf_dynstrcat(&ds->rep->codecs, a_ds->rep->codecs, ",");
5589 : }
5590 :
5591 10 : if (ctx->profile == GF_DASH_PROFILE_AVC264_LIVE) {
5592 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF AVC264 live profile\n\tswitching to regular live profile\n"));
5593 0 : ctx->profile = GF_DASH_PROFILE_LIVE;
5594 10 : } else if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
5595 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in HbbTV 1.5 ISOBMF live profile\n\tswitching to regular live profile\n"));
5596 0 : ctx->profile = GF_DASH_PROFILE_LIVE;
5597 10 : } else if (ctx->profile == GF_DASH_PROFILE_AVC264_ONDEMAND) {
5598 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF AVC264 onDemand profile\n\tswitching to regular onDemand profile\n"));
5599 0 : ctx->profile = GF_DASH_PROFILE_ONDEMAND;
5600 10 : } else if (ctx->profile == GF_DASH_PROFILE_DASHIF_LL) {
5601 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Muxed representations not allowed in DASH-IF Low Latency profile\n\tswitching to regular live profile\n"));
5602 0 : ctx->profile = GF_DASH_PROFILE_LIVE;
5603 : }
5604 : }
5605 : //use video as main stream for segmentation of muxed sources
5606 347 : if (ds_video != ds) {
5607 137 : for (j=0; j<count; j++) {
5608 137 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
5609 137 : if ((a_ds->muxed_base==ds) || (a_ds==ds)) {
5610 79 : if (a_ds == ds_video)
5611 0 : a_ds->muxed_base = NULL;
5612 : else
5613 79 : a_ds->muxed_base = ds_video;
5614 : }
5615 : }
5616 : }
5617 : }
5618 :
5619 :
5620 256 : if (ctx->loop && max_adur.num) {
5621 14 : if (max_adur.num * min_adur.den != min_adur.num * max_adur.den) {
5622 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Audio streams in the period have different durations (min "LLU"/"LLD", max "LLU"/"LLD"), may result in bad synchronization while looping\n", min_adur.num, min_adur.den, max_adur.num, max_adur.den));
5623 : }
5624 25 : for (i=0; i<count; i++) {
5625 25 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5626 25 : if (ds->duration.num * max_adur.den > max_adur.num * ds->duration.den) {
5627 15 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Input %s: max audio duration "LLU"/"LLD" in the period is less than duration "LLU"/"LLD", clamping will happen\n", ds->src_url, max_adur.num, max_adur.den, ds->duration.num, ds->duration.den ));
5628 : }
5629 25 : ds->clamped_dur = max_adur;
5630 : }
5631 : }
5632 :
5633 :
5634 256 : if (ctx->is_period_restore) return GF_OK;
5635 :
5636 247 : if (has_deps) {
5637 45 : for (i=0; i<count; i++) {
5638 45 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5639 : //assign rep bitrates
5640 45 : if (ds->dep_id)
5641 39 : ds->rep->bandwidth = dasher_get_dep_bitrate(ctx, ds);
5642 :
5643 45 : if (gf_list_count(ds->complementary_streams)) {
5644 6 : u32 nb_str = gf_list_count(ds->complementary_streams);
5645 6 : ds->moof_sn_inc = 1+nb_str;
5646 6 : ds->moof_sn = 1;
5647 45 : for (j=0; j<nb_str; j++) {
5648 39 : GF_DashStream *a_ds = gf_list_get(ds->complementary_streams, j);
5649 39 : a_ds->moof_sn_inc = ds->moof_sn_inc;
5650 39 : a_ds->moof_sn = ds->moof_sn + 1 + j;
5651 : }
5652 : }
5653 : }
5654 : }
5655 :
5656 : //moved all mux components after the base one, so that we do the segmentation on the main component
5657 247 : if (has_muxed_bases) {
5658 : //setup reps in adaptation sets
5659 19 : for (i=0; i<count; i++) {
5660 19 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5661 19 : if (!ds->muxed_base) continue;
5662 10 : gf_list_rem(ctx->current_period->streams, i);
5663 10 : gf_list_add(ctx->current_period->streams, ds);
5664 : }
5665 : }
5666 :
5667 : //setup reps in adaptation sets
5668 357 : for (i=0; i<count; i++) {
5669 357 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5670 357 : if (ds->muxed_base) continue;
5671 : //already setup
5672 347 : if (ds->set) continue;
5673 :
5674 : //not setup, create new AS
5675 317 : ds->set = gf_mpd_adaptation_set_new();
5676 317 : ds->owns_set = GF_TRUE;
5677 : //only set hls intra for visual stream if we have know for sure
5678 317 : if ((ds->stream_type==GF_STREAM_VISUAL) && (ds->sync_points_type==DASHER_SYNC_NONE)) {
5679 1 : ds->set->intra_only = GF_TRUE;
5680 : }
5681 317 : if (ctx->llhls) {
5682 9 : ds->set->use_hls_ll = GF_TRUE;
5683 9 : if (ctx->cdur.den)
5684 9 : ds->set->hls_ll_frag_dur = ((Double)ctx->cdur.num) / ctx->cdur.den;
5685 : }
5686 317 : ds->set->udta = ds;
5687 317 : if (ds->period_continuity_id) {
5688 0 : GF_FilterPacket *pck = gf_filter_pid_get_packet(ds->ipid);
5689 0 : if (pck && (gf_filter_pck_get_cts(pck) != ds->period_continuity_next_cts)) {
5690 0 : gf_free(ds->period_continuity_id);
5691 0 : ds->period_continuity_id = NULL;
5692 : } else {
5693 0 : GF_MPD_Descriptor *desc = gf_mpd_descriptor_new(NULL, "urn:mpeg:dash:period-continuity:2015", ds->period_continuity_id);
5694 0 : gf_list_add(ds->set->supplemental_properties, desc);
5695 : }
5696 : }
5697 317 : ds->period_continuity_next_cts = 0;
5698 :
5699 :
5700 317 : if (ctx->mha_compat && ((ds->codec_id==GF_CODECID_MHAS) || (ds->codec_id==GF_CODECID_MPHA))) {
5701 0 : const GF_PropertyValue *prop = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_MHA_COMPATIBLE_PROFILES);
5702 0 : if (prop) {
5703 0 : ds->set->nb_alt_mha_profiles = prop->value.uint_list.nb_items;
5704 0 : ds->set->alt_mha_profiles = prop->value.uint_list.vals;
5705 0 : ds->set->alt_mha_profiles_only = (ctx->mha_compat==DASHER_MPHA_COMP_ONLY) ? GF_TRUE : GF_FALSE;
5706 : }
5707 : }
5708 :
5709 317 : if (!ds->set->representations)
5710 0 : ds->set->representations = gf_list_new();
5711 317 : if (!ds->period->period->adaptation_sets)
5712 0 : ds->period->period->adaptation_sets = gf_list_new();
5713 317 : gf_list_add(ds->period->period->adaptation_sets, ds->set);
5714 :
5715 317 : gf_list_add(ds->set->representations, ds->rep);
5716 317 : ds->nb_rep = 1;
5717 :
5718 : //add non-conditional adaptation set descriptors
5719 317 : dasher_add_descriptors(&ds->set->x_children, ds->p_as_any_desc);
5720 : //new AS, add conditional adaptation set descriptors
5721 317 : dasher_add_descriptors(&ds->set->x_children, ds->p_as_desc);
5722 :
5723 317 : if (ds->as_id) has_as_id = GF_TRUE;
5724 : //for each following, check if same AS is possible
5725 633 : for (j=i+1; j<count; j++) {
5726 : GF_DashStream *a_ds;
5727 316 : a_ds = gf_list_get(ctx->current_period->streams, j);
5728 : //we add to the adaptation set even if shared rep, we will remove it when assigning templates and pids
5729 316 : if (dasher_same_adaptation_set(ctx, ds, a_ds)) {
5730 40 : a_ds->set = ds->set;
5731 :
5732 40 : gf_list_add(ds->set->representations, a_ds->rep);
5733 40 : ds->nb_rep++;
5734 : //add non-conditional adaptation set descriptors
5735 40 : dasher_add_descriptors(&ds->set->x_children, a_ds->p_as_any_desc);
5736 : }
5737 : }
5738 : }
5739 247 : if (has_as_id) {
5740 0 : for (i=0; i<count; i++) {
5741 0 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5742 0 : if (!ds->owns_set) continue;
5743 0 : for (j=i+1; j<count; j++) {
5744 0 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
5745 : //avoid as id duplicates
5746 0 : if (ds->owns_set && a_ds->owns_set && (a_ds->as_id == ds->as_id)) {
5747 0 : a_ds->as_id = 0;
5748 : }
5749 : }
5750 : }
5751 : }
5752 :
5753 : //we need a pass on adaptation sets to figure out if they share the same source URL
5754 : //in case we use file name in templates
5755 247 : nb_sets = gf_list_count(ctx->current_period->period->adaptation_sets);
5756 558 : for (i=0; i<nb_sets; i++) {
5757 : GF_DashStream *ds;
5758 : GF_MPD_Representation *rep;
5759 : GF_MPD_AdaptationSet *set;
5760 :
5761 317 : if (ctx->sigfrag)
5762 : break;
5763 :
5764 311 : set = gf_list_get(ctx->current_period->period->adaptation_sets, i);
5765 : assert(set);
5766 311 : rep = gf_list_get(set->representations, 0);
5767 : assert(rep);
5768 311 : ds = rep->playback.udta;
5769 :
5770 311 : if (inject_ds && (ds != inject_ds))
5771 0 : continue;
5772 :
5773 311 : if (!dasher_template_use_source_url(ds->template ? ds->template : ctx->template))
5774 36 : continue;
5775 :
5776 441 : for (j=i+1; j<nb_sets; j++) {
5777 : Bool split_init = GF_FALSE;
5778 : const GF_PropertyValue *p1, *p2;
5779 : GF_DashStream *a_ds;
5780 :
5781 166 : set = gf_list_get(ctx->current_period->period->adaptation_sets, j);
5782 166 : rep = gf_list_get(set->representations, 0);
5783 : assert(rep);
5784 166 : a_ds = rep->playback.udta;
5785 :
5786 166 : if (!dasher_template_use_source_url(a_ds->template ? a_ds->template : ctx->template))
5787 0 : continue;
5788 :
5789 166 : p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
5790 166 : p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_URL);
5791 166 : if (p1 && p2) {
5792 166 : if (gf_props_equal(p1, p2)) split_init = GF_TRUE;
5793 : } else {
5794 0 : p1 = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_FILEPATH);
5795 0 : p2 = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_FILEPATH);
5796 0 : if (p1 && p2 && gf_props_equal(p1, p2)) split_init = GF_TRUE;
5797 : }
5798 :
5799 : if (split_init) {
5800 156 : ds->split_set_names = GF_TRUE;
5801 156 : a_ds->split_set_names = GF_TRUE;
5802 : }
5803 : }
5804 : }
5805 :
5806 : /*HbbTV 1.5 ISO live specific checks*/
5807 247 : if (ctx->profile == GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE) {
5808 0 : u32 nb_periods = dasher_period_count(ctx->current_period->streams);
5809 0 : if (nb_sets > 16) {
5810 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max 16 adaptation sets in HbbTV 1.5 ISO live profile\n\tswitching to DASH AVC/264 live profile\n"));
5811 0 : ctx->profile = GF_DASH_PROFILE_AVC264_LIVE;
5812 : }
5813 0 : if (nb_periods > 32) {
5814 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max 32 periods in HbbTV 1.5 ISO live profile\n\tswitching to regular DASH AVC/264 live profile\n"));
5815 0 : ctx->profile = GF_DASH_PROFILE_AVC264_LIVE;
5816 : }
5817 0 : if (ctx->segdur.num < (s32) ctx->segdur.den) {
5818 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Min segment duration 1s in HbbTV 1.5 ISO live profile\n\tcapping to 1s\n"));
5819 0 : ctx->segdur.num = 1;
5820 0 : ctx->segdur.den = 1;
5821 : }
5822 0 : if (ctx->segdur.num > 15 * (s32) ctx->segdur.den) {
5823 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Max segment duration 15s in HbbTV 1.5 ISO live profile\n\tcapping to 15s\n"));
5824 0 : ctx->segdur.num = 15;
5825 0 : ctx->segdur.den = 1;
5826 : }
5827 : }
5828 :
5829 : //init UTC reference time for dynamic
5830 247 : if (!ctx->mpd->availabilityStartTime && (ctx->dmode!=GF_MPD_TYPE_STATIC) && !inject_ds) {
5831 25 : u64 dash_start_date = ctx->ast ? gf_net_parse_date(ctx->ast) : 0;
5832 :
5833 25 : if (!ctx->utc_initialized) {
5834 25 : dasher_init_utc(filter, ctx);
5835 :
5836 : //setup service description
5837 25 : if (ctx->profile == GF_DASH_PROFILE_DASHIF_LL) {
5838 0 : ctx->mpd->inject_service_desc = GF_TRUE;
5839 : }
5840 : }
5841 :
5842 25 : ctx->mpd->gpac_init_ntp_ms = gf_net_get_ntp_ms();
5843 50 : ctx->mpd->availabilityStartTime = dasher_get_utc(ctx);
5844 25 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] MPD Availability start time initialized to "LLU" ms\n", ctx->mpd->availabilityStartTime));
5845 :
5846 25 : if (dash_start_date && (dash_start_date < ctx->mpd->availabilityStartTime)) {
5847 : u64 start_date_sec_ntp, secs;
5848 : Double ms;
5849 : //recompute NTP init time matching the required ast
5850 0 : secs = dash_start_date/1000;
5851 : start_date_sec_ntp = (u32) secs;
5852 0 : start_date_sec_ntp += GF_NTP_SEC_1900_TO_1970;
5853 0 : ms = (Double) (dash_start_date - secs*1000);
5854 0 : ms /= 1000.0;
5855 0 : ctx->mpd->gpac_init_ntp_ms = (u64) (start_date_sec_ntp * 1000 + ms);
5856 : //compute number of seconds to discard
5857 0 : ctx->nb_secs_to_discard = (Double) (ctx->mpd->availabilityStartTime - dash_start_date);
5858 0 : ctx->nb_secs_to_discard /= 1000;
5859 : //don't discard TSB, this will be done automatically
5860 :
5861 0 : ctx->mpd->availabilityStartTime = dash_start_date;
5862 :
5863 25 : } else if (dash_start_date) {
5864 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] specified AST %s seems in the future, ignoring it\n", ctx->ast));
5865 : }
5866 : }
5867 :
5868 : //setup adaptation sets bitstream switching
5869 357 : for (i=0; i<count; i++) {
5870 357 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5871 357 : if (!ds->owns_set) continue;
5872 317 : if (inject_ds && (ds != inject_ds))
5873 0 : continue;
5874 : //check bitstream switching
5875 317 : dasher_check_bitstream_swicthing(ctx, ds->set);
5876 : //setup AS defaults, roles and co
5877 317 : dasher_setup_set_defaults(ctx, ds->set);
5878 : //setup sources, templates & co
5879 317 : dasher_setup_sources(filter, ctx, ds->set);
5880 : }
5881 :
5882 250 : while (gf_list_count(ctx->postponed_pids)) {
5883 3 : GF_DashStream *ds = gf_list_get(ctx->postponed_pids, 0);
5884 3 : dasher_open_pid(filter, ctx, ds, ds->multi_pids, GF_FALSE);
5885 : }
5886 :
5887 : //good to go !
5888 357 : for (i=0; i<count; i++) {
5889 357 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
5890 357 : if (inject_ds && (ds != inject_ds))
5891 0 : continue;
5892 : //setup segmentation
5893 357 : ds->rep_init = GF_FALSE;
5894 357 : ds->seg_done = GF_FALSE;
5895 357 : ds->next_seg_start = (u32) ( ((u64) ds->dash_dur.num * ds->timescale) / ds->dash_dur.den);
5896 357 : ds->adjusted_next_seg_start = ds->next_seg_start;
5897 357 : ds->segment_started = GF_FALSE;
5898 357 : ds->seg_number = ds->startNumber;
5899 357 : ds->first_cts = ds->first_dts = ds->max_period_dur = 0;
5900 :
5901 : //simulate N loops of the source
5902 357 : if (ctx->nb_secs_to_discard) {
5903 : u64 period_dur, seg_dur;
5904 : u32 nb_skip=0;
5905 :
5906 0 : period_dur = (u64) (ctx->nb_secs_to_discard * ds->timescale);
5907 : seg_dur = (u64) (ds->dash_dur.num) * ds->timescale / ds->dash_dur.den;
5908 :
5909 0 : nb_skip = (u32) (period_dur / seg_dur);
5910 0 : ds->ts_offset += nb_skip*seg_dur;
5911 0 : ds->seg_number += nb_skip;
5912 :
5913 0 : ds->max_period_dur = ds->cumulated_dur;
5914 0 : ds->adjusted_next_seg_start += ds->ts_offset;
5915 0 : ds->next_seg_start += ds->ts_offset;
5916 : }
5917 : }
5918 :
5919 247 : ctx->nb_secs_to_discard = 0;
5920 :
5921 247 : if (ctx->state)
5922 14 : dasher_context_update_period_start(ctx);
5923 :
5924 : return GF_OK;
5925 : }
5926 :
5927 3333 : static void dasher_insert_timeline_entry(GF_DasherCtx *ctx, GF_DashStream *ds)
5928 : {
5929 : GF_MPD_SegmentTimelineEntry *s;
5930 : u64 duration;
5931 : Bool is_first = GF_FALSE;
5932 : Bool seg_align = GF_FALSE;
5933 : GF_MPD_SegmentTimeline *tl=NULL;
5934 :
5935 : //we only store segment timeline for the main component in the representation
5936 3333 : if (ds->muxed_base) return;
5937 :
5938 3333 : if (ds->rep && ds->rep->state_seg_list) {
5939 528 : GF_DASH_SegmentContext *sctx = gf_list_last(ds->rep->state_seg_list);
5940 528 : if (sctx)
5941 528 : sctx->dur = ds->first_cts_in_next_seg - ds->first_cts_in_seg;
5942 : }
5943 :
5944 : //we only use segment timeline with templates
5945 3333 : if (!ctx->stl) return;
5946 :
5947 4 : if (gf_list_find(ds->set->representations, ds->rep)==0) is_first = GF_TRUE;
5948 : assert(ds->first_cts_in_next_seg > ds->first_cts_in_seg);
5949 4 : duration = ds->first_cts_in_next_seg - ds->first_cts_in_seg;
5950 4 : if (ds->timescale != ds->mpd_timescale) {
5951 0 : duration *= ds->mpd_timescale;
5952 0 : duration /= ds->timescale;
5953 : }
5954 4 : seg_align = (ds->set->segment_alignment || ds->set->subsegment_alignment) ? GF_TRUE : GF_FALSE;
5955 : //not first and segment alignment, ignore
5956 4 : if (!is_first && seg_align) {
5957 : return;
5958 : }
5959 :
5960 : //no segment alignment store in each rep
5961 4 : if (!seg_align) {
5962 : GF_MPD_SegmentTimeline **p_tl=NULL;
5963 0 : if (!ds->rep) {
5964 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to store timeline entru, no representation assigned !\n"));
5965 : return;
5966 : }
5967 :
5968 0 : if (ctx->tpl) {
5969 0 : p_tl = &ds->rep->segment_template->segment_timeline;
5970 0 : ds->rep->segment_template->duration = 0;
5971 : } else {
5972 0 : p_tl = &ds->rep->segment_list->segment_timeline;
5973 0 : ds->rep->segment_list->duration = 0;
5974 : }
5975 0 : if (! (*p_tl)) {
5976 0 : (*p_tl) = gf_mpd_segmentimeline_new();
5977 : }
5978 0 : tl = (*p_tl);
5979 : } else {
5980 : Bool new_tl = GF_FALSE;
5981 : GF_MPD_SegmentTimeline **p_tl=NULL;
5982 :
5983 : if (!ds->set) {
5984 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] failed to store timeline entru, no AdpatationSet assigned !\n"));
5985 : return;
5986 : }
5987 : assert(ds->set);
5988 4 : if (ctx->tpl) {
5989 : //in case we had no template at set level
5990 4 : if (!ds->set->segment_template) {
5991 1 : GF_SAFEALLOC(ds->set->segment_template, GF_MPD_SegmentTemplate);
5992 1 : if (ds->set->segment_template) {
5993 1 : ds->set->segment_template->start_number = (u32) -1;
5994 1 : ds->set->segment_template->timescale = ds->timescale;
5995 : }
5996 : new_tl = GF_TRUE;
5997 : }
5998 4 : p_tl = &ds->set->segment_template->segment_timeline;
5999 4 : ds->set->segment_template->duration = 0;
6000 : } else {
6001 : //in case we had no template at set level
6002 0 : if (!ds->set->segment_list) {
6003 0 : GF_SAFEALLOC(ds->set->segment_list, GF_MPD_SegmentList);
6004 0 : if (ds->set->segment_list) {
6005 0 : ds->set->segment_list->start_number = (u32) -1;
6006 0 : ds->set->segment_list->timescale = ds->timescale;
6007 : }
6008 : new_tl = GF_TRUE;
6009 : }
6010 0 : p_tl = &ds->set->segment_list->segment_timeline;
6011 0 : ds->set->segment_list->duration = 0;
6012 : }
6013 :
6014 4 : if (! (*p_tl) ) {
6015 2 : (*p_tl) = gf_mpd_segmentimeline_new();
6016 : }
6017 4 : tl = (*p_tl);
6018 4 : if (new_tl) {
6019 1 : u32 i, count = gf_list_count(ds->set->representations);
6020 1 : for (i=0; i<count; i++) {
6021 1 : GF_MPD_Representation *arep = gf_list_get(ds->set->representations, i);
6022 1 : if (arep && arep->segment_template) arep->segment_template->duration = 0;
6023 1 : if (arep && arep->segment_list) arep->segment_list->duration = 0;
6024 : }
6025 : }
6026 : }
6027 :
6028 : //append to previous entry if possible
6029 4 : s = gf_list_last(tl->entries);
6030 4 : if (s && (s->duration == duration) && (s->start_time + (s->repeat_count+1) * s->duration == ds->seg_start_time)) {
6031 2 : s->repeat_count++;
6032 : return;
6033 : }
6034 : //nope, allocate
6035 2 : GF_SAFEALLOC(s, GF_MPD_SegmentTimelineEntry);
6036 2 : if (!s) return;
6037 2 : s->start_time = ds->seg_start_time;
6038 2 : s->duration = (u32) duration;
6039 2 : gf_list_add(tl->entries, s);
6040 : }
6041 :
6042 0 : static void dasher_copy_segment_timelines(GF_DasherCtx *ctx, GF_MPD_AdaptationSet *set)
6043 : {
6044 : GF_MPD_SegmentTimeline *src_tl = NULL;
6045 : u32 i, j, count, nb_s;
6046 0 : if (!ctx->stl) return;
6047 : //get as level segment timeline, set it to NULL, reassign it to first rep and clone for other reps
6048 0 : if (ctx->tpl) {
6049 : assert(set->segment_template->segment_timeline);
6050 0 : src_tl = set->segment_template->segment_timeline;
6051 0 : set->segment_template->segment_timeline = NULL;
6052 : } else {
6053 : assert(set->segment_list->segment_timeline);
6054 0 : src_tl = set->segment_list->segment_timeline;
6055 0 : set->segment_list->segment_timeline = NULL;
6056 : }
6057 0 : nb_s = gf_list_count(src_tl->entries);
6058 :
6059 0 : count = gf_list_count(set->representations);
6060 0 : for (i=0; i<count; i++) {
6061 : GF_MPD_SegmentTimeline *tl = NULL;
6062 0 : GF_MPD_Representation *rep = gf_list_get(set->representations, i);
6063 0 : if (ctx->tpl) {
6064 0 : if (!rep->segment_template) {
6065 0 : GF_SAFEALLOC(rep->segment_template, GF_MPD_SegmentTemplate);
6066 0 : if (!rep->segment_template) continue;
6067 : }
6068 0 : if (!i) {
6069 0 : rep->segment_template->segment_timeline = src_tl;
6070 0 : continue;
6071 : }
6072 0 : if (!rep->segment_template->segment_timeline) {
6073 0 : rep->segment_template->segment_timeline = gf_mpd_segmentimeline_new();
6074 : }
6075 0 : tl = rep->segment_template->segment_timeline;
6076 : } else {
6077 0 : if (!rep->segment_list) {
6078 0 : GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList);
6079 0 : if (!rep->segment_list) continue;
6080 0 : rep->segment_list->start_number = (u32) -1;
6081 : }
6082 0 : if (!i) {
6083 0 : rep->segment_list->segment_timeline = src_tl;
6084 0 : continue;
6085 : }
6086 0 : if (!rep->segment_list->segment_timeline) {
6087 0 : rep->segment_list->segment_timeline = gf_mpd_segmentimeline_new();
6088 : }
6089 0 : tl = rep->segment_list->segment_timeline;
6090 : }
6091 : assert(tl);
6092 0 : for (j=0; j<nb_s; j++) {
6093 : GF_MPD_SegmentTimelineEntry *s;
6094 0 : GF_MPD_SegmentTimelineEntry *src_s = gf_list_get(src_tl->entries, j);
6095 0 : GF_SAFEALLOC(s, GF_MPD_SegmentTimelineEntry);
6096 0 : if (!s) continue;
6097 :
6098 0 : s->duration = src_s->duration;
6099 0 : s->repeat_count = src_s->repeat_count;
6100 0 : s->start_time = src_s->start_time;
6101 0 : gf_list_add(tl->entries, s);
6102 : }
6103 : }
6104 : }
6105 :
6106 3334 : static void dasher_flush_segment(GF_DasherCtx *ctx, GF_DashStream *ds, Bool is_last_in_period)
6107 : {
6108 : u32 i, count;
6109 : GF_DashStream *ds_done = NULL, *ds_not_done = NULL;
6110 3334 : GF_DashStream *set_ds = ds->set->udta;
6111 3334 : GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
6112 : Bool has_ds_done = GF_FALSE;
6113 : u32 seg_dur_ms=0;
6114 : GF_DashStream *ds_log = NULL;
6115 : u64 first_cts_in_cur_seg=0;
6116 :
6117 3334 : ctx->update_report = -1;
6118 :
6119 3334 : if (ds->segment_started) {
6120 : Double seg_duration;
6121 3333 : u64 seg_duration_unscale = base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
6122 : //seg_duration /= base_ds->timescale;
6123 3333 : if (!seg_duration_unscale) {
6124 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Segment %d is empty - pid end of stream %d\n", ds->seg_number, gf_filter_pid_is_eos(ds->ipid) ));
6125 : }
6126 3333 : seg_dur_ms = (u32) (seg_duration_unscale*1000 / base_ds->timescale);
6127 3333 : if (seg_dur_ms * base_ds->timescale < seg_duration_unscale* 1000) seg_dur_ms++;
6128 :
6129 3333 : first_cts_in_cur_seg = ds->first_cts_in_seg;
6130 3333 : if (ctx->mpd->max_segment_duration < seg_dur_ms)
6131 291 : ctx->mpd->max_segment_duration = seg_dur_ms;
6132 :
6133 3333 : seg_duration = (Double) base_ds->first_cts_in_next_seg - ds->first_cts_in_seg;
6134 3333 : seg_duration /= base_ds->timescale;
6135 :
6136 3333 : if (ctx->sigfrag) {
6137 72 : if (ds->no_seg_dur) {
6138 72 : ds->gm_duration_total += seg_duration;
6139 72 : ds->gm_nb_segments++;
6140 72 : if (!ds->gm_duration_min || (ds->gm_duration_min>seg_duration) )
6141 6 : ds->gm_duration_min = seg_duration;
6142 72 : if (ds->gm_duration_max<seg_duration)
6143 6 : ds->gm_duration_max = seg_duration;
6144 72 : ds->dash_dur.num = (s32) (1000.0 * ds->gm_duration_total / ds->gm_nb_segments);
6145 72 : ds->dash_dur.den = 1000;
6146 72 : ds->rep->dash_dur = ds->dash_dur;
6147 : }
6148 :
6149 72 : if (ds->rep->segment_list && (ds->rep->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
6150 48 : ds->rep->segment_list->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_list->timescale / ds->dash_dur.den;
6151 : }
6152 72 : if (ds->set->segment_list && (ds->set->segment_list->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
6153 0 : ds->set->segment_list->duration = (u64) (ds->dash_dur.num) * ds->set->segment_list->timescale / ds->dash_dur.den;
6154 : }
6155 72 : if (ds->rep->segment_template && (ds->rep->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
6156 0 : ds->rep->segment_template->duration = (u64) (ds->dash_dur.num) * ds->rep->segment_template->timescale / ds->dash_dur.den;
6157 : }
6158 72 : if (ds->set && ds->set->segment_template && (ds->set->segment_template->duration * ds->dash_dur.den != ds->dash_dur.num) ) {
6159 9 : ds->set->segment_template->duration = (u64) (ds->dash_dur.num) * ds->set->segment_template->timescale / ds->dash_dur.den;
6160 : }
6161 : }
6162 3333 : if (!base_ds->done && !ctx->stl && ctx->tpl && !ctx->cues && !ctx->forward_mode && !is_last_in_period) {
6163 :
6164 2267 : if (2 * seg_duration * ds->dash_dur.den < ds->dash_dur.num) {
6165 :
6166 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g less than half DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
6167 2267 : } else if (2 * seg_duration * ds->dash_dur.den > 3 * ds->dash_dur.num) {
6168 24 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segment %d duration %g more than 3/2 DASH duration, consider reencoding or using segment timeline\n", ds->seg_number, seg_duration));
6169 : }
6170 : }
6171 3333 : dasher_insert_timeline_entry(ctx, base_ds);
6172 :
6173 3333 : if (ctx->align) {
6174 3333 : if (!set_ds->nb_rep_done || !set_ds->set_seg_duration) {
6175 2933 : set_ds->set_seg_duration = seg_duration;
6176 : } else {
6177 400 : Double diff = set_ds->set_seg_duration - seg_duration;
6178 :
6179 400 : if (ABS(diff) > 0.001) {
6180 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not aligned across representations: first rep segment duration %g but new segment duration %g for the same segment %d\n", set_ds->set_seg_duration, seg_duration, set_ds->seg_number));
6181 :
6182 0 : if (ctx->profile != GF_DASH_PROFILE_FULL) {
6183 0 : set_ds->set->segment_alignment = GF_FALSE;
6184 0 : set_ds->set->subsegment_alignment = GF_FALSE;
6185 0 : ctx->profile = GF_DASH_PROFILE_FULL;
6186 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] No segment alignment, switching to full profile\n"));
6187 0 : dasher_copy_segment_timelines(ctx, set_ds->set);
6188 : }
6189 : }
6190 : }
6191 3333 : set_ds->nb_rep_done++;
6192 3333 : if (set_ds->nb_rep_done < set_ds->nb_rep) {
6193 401 : if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
6194 0 : ds->subdur_done = GF_TRUE;
6195 : return;
6196 : }
6197 2932 : set_ds->set_seg_duration = 0;
6198 2932 : set_ds->nb_rep_done = 0;
6199 : }
6200 :
6201 : ds_log = ds;
6202 : } else {
6203 1 : if (ctx->align) {
6204 1 : set_ds->nb_rep_done++;
6205 1 : if (set_ds->nb_rep_done < set_ds->nb_rep) return;
6206 :
6207 1 : set_ds->set_seg_duration = 0;
6208 1 : set_ds->nb_rep_done = 0;
6209 : }
6210 : }
6211 :
6212 2933 : if (ctx->subdur && (ds->cumulated_dur >= 0.8 * (ds->cumulated_subdur + ctx->subdur) * ds->timescale))
6213 34 : ds->subdur_done = GF_TRUE;
6214 :
6215 2933 : count = gf_list_count(ctx->current_period->streams);
6216 :
6217 2933 : if (ctx->subdur) {
6218 : u32 nb_sub_done=0;
6219 38 : if (ctx->subdur_done) return;
6220 70 : for (i=0; i<count; i++) {
6221 70 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
6222 70 : if (a_ds->muxed_base) {
6223 2 : if (a_ds->muxed_base->subdur_done) a_ds->subdur_done = GF_TRUE;
6224 : }
6225 :
6226 70 : if (a_ds->subdur_done) {
6227 48 : nb_sub_done++;
6228 : }
6229 : }
6230 : // if one of the AS is done and we are at 30% of target subdur, abort
6231 38 : if (nb_sub_done && !ds->subdur_done
6232 0 : && (ctx->subdur && (ds->cumulated_dur >= (0.7 * (ds->cumulated_subdur + ctx->subdur)) * ds->timescale))
6233 : ) {
6234 0 : ds->subdur_done = GF_TRUE;
6235 0 : nb_sub_done++;
6236 : }
6237 38 : if (nb_sub_done==count)
6238 20 : ctx->subdur_done = GF_TRUE;
6239 : }
6240 :
6241 : //reset all streams from our rep or our set
6242 15387 : for (i=0; i<count; i++) {
6243 15387 : ds = gf_list_get(ctx->current_period->streams, i);
6244 : //reset all in set if segment alignment
6245 15387 : if (ctx->align) {
6246 15387 : if (ds->set != set_ds->set) continue;
6247 : } else {
6248 : //otherwise reset only media components for this rep
6249 0 : if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;
6250 : }
6251 :
6252 3520 : if (!ds->done) {
6253 3151 : ds->first_cts_in_next_seg = ds->first_cts_in_seg = ds->est_first_cts_in_next_seg = 0;
6254 : }
6255 :
6256 3520 : if (ds->muxed_base) {
6257 157 : if (!ds->done) {
6258 148 : ds->segment_started = GF_FALSE;
6259 148 : ds->seg_done = GF_FALSE;
6260 : } else {
6261 : has_ds_done = GF_TRUE;
6262 : }
6263 157 : continue;
6264 : }
6265 : base_ds = ds;
6266 :
6267 3363 : if (base_ds->done)
6268 : ds_done = base_ds;
6269 3003 : else if (base_ds->nb_comp_done==base_ds->nb_comp) ds_not_done = base_ds;
6270 :
6271 3363 : if (!base_ds->done && base_ds->seg_done) {
6272 3003 : base_ds->seg_done = GF_FALSE;
6273 3003 : base_ds->nb_comp_done = 0;
6274 :
6275 : #ifndef GPAC_DISABLE_LOG
6276 3003 : if (ctx->dmode>=GF_DASH_DYNAMIC) {
6277 : u32 asid;
6278 : s64 ast_diff;
6279 103 : u64 seg_ast = ctx->mpd->availabilityStartTime;
6280 103 : seg_ast += ctx->current_period->period->start;
6281 103 : seg_ast += (base_ds->adjusted_next_seg_start*1000) / base_ds->timescale;
6282 :
6283 : //if theoretical AST of the segment is less than the current UTC, we are producing the segment too late.
6284 : ast_diff = (s64) dasher_get_utc(ctx);
6285 103 : ast_diff -= seg_ast;
6286 :
6287 103 : asid = base_ds->set->id;
6288 103 : if (!asid)
6289 0 : asid = gf_list_find(ctx->current_period->period->adaptation_sets, base_ds->set) + 1;
6290 :
6291 103 : if (ast_diff>10) {
6292 5 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done TOO LATE by %d ms\n", asid, base_ds->rep->id, base_ds->seg_number, (s32) ast_diff));
6293 : } else {
6294 98 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] AS%d Rep %s segment %d done %d ms %s UTC due time\n", asid, base_ds->rep->id, base_ds->seg_number, ABS(ast_diff), (ast_diff<0) ? "before" : "after"));
6295 : }
6296 : }
6297 : #endif
6298 :
6299 : assert(base_ds->segment_started);
6300 3003 : base_ds->segment_started = GF_FALSE;
6301 :
6302 3003 : base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
6303 6188 : while (base_ds->next_seg_start <= base_ds->adjusted_next_seg_start) {
6304 182 : base_ds->next_seg_start += (u64) (base_ds->dash_dur.num) * base_ds->timescale / base_ds->dash_dur.den;
6305 182 : if (ctx->skip_seg)
6306 0 : base_ds->seg_number++;
6307 : }
6308 3003 : base_ds->adjusted_next_seg_start = base_ds->next_seg_start;
6309 3003 : base_ds->seg_number++;
6310 : }
6311 : }
6312 :
6313 2933 : if (ds_log) {
6314 2932 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[Dasher] Rep#%s flush seg %d start %g duration %g next seg end time %g\n", ds_log->rep->id, ds_log->seg_number-1, ((Double)first_cts_in_cur_seg)/ds_log->timescale, ((Double)seg_dur_ms)/1000, ((Double)ds_log->adjusted_next_seg_start)/ds_log->timescale));
6315 : }
6316 :
6317 : //muxed representation with unaligned duration,
6318 2933 : if (has_ds_done) {
6319 17 : for (i=0; i<count; i++) {
6320 17 : ds = gf_list_get(ctx->current_period->streams, i);
6321 : //otherwise reset only media components for this rep
6322 17 : if ((ds->muxed_base != base_ds) && (ds != base_ds)) continue;
6323 :
6324 17 : if (ds->done && (base_ds->nb_comp_done < base_ds->nb_comp)) {
6325 0 : base_ds->nb_comp_done++;
6326 : }
6327 : }
6328 : }
6329 :
6330 : //some reps are done, other not, force a max time on all AS in the period
6331 2933 : if (ds_done && ds_not_done) {
6332 580 : for (i=0; i<count; i++) {
6333 580 : ds = gf_list_get(ctx->current_period->streams, i);
6334 :
6335 580 : if (ds->done) {
6336 515 : if (ds->set->udta == set_ds)
6337 29 : set_ds->nb_rep_done++;
6338 65 : } else if (ctx->check_dur && !ds->force_rep_end) {
6339 0 : ds->force_rep_end = ds_done->first_cts_in_next_seg * ds->timescale / ds_done->timescale;
6340 : }
6341 : }
6342 : }
6343 : }
6344 :
6345 31 : static char *dasher_strip_base(GF_DasherCtx *ctx, char *url)
6346 : {
6347 31 : char *manifest_path = ctx->out_path;
6348 : char *file_path = url;
6349 : char *res = url;
6350 :
6351 31 : if (!manifest_path || !url) return NULL;
6352 :
6353 31 : if (!strncmp(manifest_path, "./", 2)) manifest_path+=2;
6354 31 : if (!strncmp(file_path, "./", 2)) file_path+=2;
6355 :
6356 31 : const char *base_manifest = gf_file_basename(manifest_path);
6357 31 : u32 diff = (u32) (base_manifest - manifest_path);
6358 31 : if (!strncmp(file_path, manifest_path, diff)) {
6359 31 : res = file_path + diff;
6360 : }
6361 : return res;
6362 : }
6363 :
6364 : static GFINLINE
6365 : u64 dasher_translate_cts(GF_DashStream *ds, u64 cts)
6366 : {
6367 134282 : if (ds->cues) {
6368 6247 : cts -= ds->first_cts;
6369 128035 : } else if (cts < ds->first_dts) {
6370 : cts = 0;
6371 128035 : } else if (ds->pts_minus_cts<0) {
6372 9280 : if ((s64) (cts - ds->first_dts) >= -ds->pts_minus_cts) {
6373 9278 : cts = cts - ds->first_dts + ds->pts_minus_cts;
6374 : } else {
6375 : cts = 0;
6376 : }
6377 : } else {
6378 118755 : cts -= ds->first_cts;
6379 : }
6380 : return cts;
6381 : }
6382 :
6383 3490 : static void dasher_mark_segment_start(GF_DasherCtx *ctx, GF_DashStream *ds, GF_FilterPacket *pck, GF_FilterPacket *in_pck)
6384 : {
6385 : GF_DASH_SegmentContext *seg_state=NULL;
6386 : char szSegmentName[GF_MAX_PATH], szSegmentFullPath[GF_MAX_PATH], szIndexName[GF_MAX_PATH];
6387 3490 : GF_DashStream *base_ds = ds->muxed_base ? ds->muxed_base : ds;
6388 :
6389 3490 : if (ctx->forward_mode) {
6390 : const GF_PropertyValue *p_fname, *p_manifest;
6391 :
6392 54 : p_fname = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
6393 54 : p_manifest = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
6394 54 : if (!p_fname || !p_fname->value.string || !p_manifest) {
6395 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Couldn't fetch source URL / idx of segment in forward mode, cannot forward\n"));
6396 0 : ctx->in_error = GF_TRUE;
6397 830 : return;
6398 : }
6399 : strcpy(szSegmentName, p_fname->value.string);
6400 : //remove filename property
6401 54 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, NULL);
6402 :
6403 : //check for manifest update
6404 54 : p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_DASH_MANIFEST);
6405 54 : if (p_manifest) {
6406 3 : if (p_manifest->value.string) {
6407 3 : if (strstr(p_manifest->value.string, "<MPD")) {
6408 2 : if (ctx->do_m3u8) {
6409 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest forward mode got DASH MPD but output is HLS M3U8, cannot operate - change formats or dasher forward mode\n"));
6410 0 : ctx->in_error = GF_TRUE;
6411 0 : return;
6412 : } else {
6413 2 : dasher_forward_mpd(ctx, p_manifest->value.string);
6414 : }
6415 : } else {
6416 1 : if (ctx->do_mpd) {
6417 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest forward mode got M3U6 but output is DASH MPD, cannot operate - change formats or dasher forward mode\n"));
6418 0 : ctx->in_error = GF_TRUE;
6419 0 : return;
6420 : } else {
6421 1 : dasher_forward_manifest_raw(ctx, ds, p_manifest->value.string, NULL);
6422 : }
6423 : }
6424 : }
6425 3 : gf_filter_pck_set_property(pck, GF_PROP_PCK_DASH_MANIFEST, NULL);
6426 : }
6427 :
6428 : //check for HLS child playlist update
6429 54 : p_manifest = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_HLS_VARIANT);
6430 54 : p_fname = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_HLS_VARIANT_NAME);
6431 54 : if (p_manifest && p_fname && (p_fname->value.string_list.nb_items==p_manifest->value.string_list.nb_items)) {
6432 : u32 i, count = p_fname->value.string_list.nb_items;
6433 3 : for (i=0; i<count; i++)
6434 3 : dasher_forward_manifest_raw(ctx, ds, p_manifest->value.string_list.vals[i], p_fname->value.string_list.vals[i]);
6435 : }
6436 54 : if (p_manifest)
6437 1 : gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_VARIANT, NULL);
6438 54 : if (p_fname)
6439 1 : gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_VARIANT_NAME, NULL);
6440 :
6441 : //we need to move from segment name to output name
6442 54 : if (ctx->forward_mode==DASHER_FWD_ALL)
6443 : goto send_packet;
6444 :
6445 : }
6446 3472 : if (pck) {
6447 3400 : if (ctx->ntp==DASHER_NTP_YES) {
6448 0 : u64 ntpts = gf_net_get_ntp_ts();
6449 0 : gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, &PROP_LONGUINT(ntpts));
6450 3400 : } else if (ctx->ntp==DASHER_NTP_REM) {
6451 3400 : gf_filter_pck_set_property(pck, GF_PROP_PCK_SENDER_NTP, NULL);
6452 : }
6453 :
6454 3400 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENUM, &PROP_UINT(base_ds->seg_number ) );
6455 : }
6456 :
6457 : //only signal file name & insert timelines on one stream for muxed representations
6458 3472 : if (ds->muxed_base) return;
6459 :
6460 3315 : ds->seg_start_time = ds->first_cts_in_seg;
6461 3315 : if (ds->timescale != ds->mpd_timescale) {
6462 0 : ds->seg_start_time *= ds->mpd_timescale;
6463 0 : ds->seg_start_time /= ds->timescale;
6464 : }
6465 :
6466 3315 : if (ctx->store_seg_states) {
6467 : char *kms_uri;
6468 : const GF_PropertyValue *p;
6469 528 : if (!ds->rep->state_seg_list) {
6470 71 : ds->rep->state_seg_list = gf_list_new();
6471 : }
6472 528 : if (!ds->rep->dash_dur.num) {
6473 85 : ds->rep->timescale = ds->timescale;
6474 85 : ds->rep->streamtype = ds->stream_type;
6475 85 : ds->rep->timescale_mpd = ds->mpd_timescale;
6476 85 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_HLS_GROUPID);
6477 85 : if (p)
6478 2 : ds->rep->groupID = p->value.string;
6479 :
6480 85 : ds->rep->dash_dur = ds->dash_dur;
6481 85 : if (ctx->sigfrag) {
6482 2 : const GF_PropertyValue *pid_url = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
6483 2 : if (pid_url && pid_url->value.string) {
6484 2 : ds->rep->hls_single_file_name = dasher_strip_base(ctx, pid_url->value.string);
6485 : }
6486 : }
6487 :
6488 85 : if (!ds->rep->hls_single_file_name && !ctx->m2ts) {
6489 80 : switch (ctx->muxtype) {
6490 : case DASHER_MUX_TS:
6491 : case DASHER_MUX_OGG:
6492 : case DASHER_MUX_RAW:
6493 : break;
6494 80 : default:
6495 80 : if (ds->set->bitstream_switching && ds->set->segment_template)
6496 4 : ds->rep->hls_single_file_name = ds->set->segment_template->hls_init_name;
6497 : else
6498 76 : ds->rep->hls_single_file_name = ds->init_seg;
6499 : }
6500 : }
6501 85 : ds->rep->nb_chan = ds->nb_ch;
6502 85 : ds->rep->m3u8_name = ds->hls_vp_name;
6503 85 : if (ds->fps.den) {
6504 48 : ds->rep->fps = ds->fps.num;
6505 48 : ds->rep->fps /= ds->fps.den;
6506 : }
6507 : }
6508 528 : GF_SAFEALLOC(seg_state, GF_DASH_SegmentContext);
6509 528 : if (!seg_state) return;
6510 528 : seg_state->time = ds->seg_start_time;
6511 528 : seg_state->seg_num = ds->seg_number;
6512 528 : seg_state->llhls_mode = ctx->llhls;
6513 528 : ds->current_seg_state = seg_state;
6514 528 : seg_state->encrypted = GF_FALSE;
6515 :
6516 528 : p = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_HLS_KMS);
6517 528 : kms_uri = (p && p->value.string) ? p->value.string : NULL;
6518 528 : if (ds->tci) {
6519 : u32 s;
6520 0 : ds->iv_low++;
6521 0 : if (ds->iv_low == 0)
6522 0 : ds->iv_high++;
6523 0 : for (s=0; s<8; s++)
6524 0 : seg_state->hls_iv[s] = (ds->iv_high >> 8*(7-s) ) & 0xFF;
6525 0 : for (s=0; s<8; s++)
6526 0 : seg_state->hls_iv[s+8] = (ds->iv_low >> 8*(7-s) ) & 0xFF;
6527 :
6528 0 : seg_state->encrypted = GF_TRUE;
6529 0 : gf_cryptfout_push_key(ds->dst_filter, & ds->tci->keys[ds->key_idx].key, &seg_state->hls_iv);
6530 :
6531 0 : if (ds->tci->keys[ds->key_idx].hls_info)
6532 : kms_uri = ds->tci->keys[ds->key_idx].hls_info;
6533 :
6534 0 : ds->nb_crypt_seg++;
6535 0 : if (ds->tci->keyRoll) {
6536 0 : if (ds->nb_crypt_seg == ds->tci->keyRoll) {
6537 0 : ds->nb_crypt_seg = 0;
6538 0 : ds->key_idx = (ds->key_idx+1) % ds->tci->nb_keys;
6539 : }
6540 : }
6541 : }
6542 : //we need a hard copy as the pid may reconfigure before we flush the segment
6543 528 : if (kms_uri)
6544 24 : seg_state->hls_key_uri = gf_strdup(kms_uri);
6545 :
6546 528 : gf_list_add(ds->rep->state_seg_list, seg_state);
6547 528 : if (ctx->sigfrag) {
6548 24 : const GF_PropertyValue *frag_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FRAG_RANGE);
6549 24 : const GF_PropertyValue *frag_url = gf_filter_pck_get_property(in_pck, GF_PROP_PID_URL);
6550 33 : if (frag_url && frag_url->value.string) {
6551 9 : char *f_url = dasher_strip_base(ctx, frag_url->value.string);
6552 9 : seg_state->filename = gf_strdup(f_url);
6553 : }
6554 15 : else if (frag_range) {
6555 15 : seg_state->file_offset = frag_range->value.lfrac.num;
6556 15 : seg_state->file_size = (u32) (frag_range->value.lfrac.den - seg_state->file_offset);
6557 :
6558 15 : if (ds->rep->segment_base && !ds->rep->segment_base->initialization_segment) {
6559 : GF_MPD_URL *url;
6560 0 : GF_SAFEALLOC(url, GF_MPD_URL);
6561 0 : if (url) {
6562 0 : GF_SAFEALLOC(url->byte_range, GF_MPD_ByteRange);
6563 0 : if (url->byte_range) {
6564 0 : url->byte_range->start_range = 0;
6565 0 : url->byte_range->end_range = seg_state->file_offset-1;
6566 : }
6567 : }
6568 0 : ds->rep->segment_base->initialization_segment = url;
6569 : }
6570 : } else {
6571 0 : gf_list_del_item(ds->rep->state_seg_list, seg_state);
6572 0 : gf_free(seg_state);
6573 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Manifest generation only but not fragment information in packet, source demux not properly configured\n"));
6574 0 : ctx->in_error = GF_TRUE;
6575 : }
6576 : } else {
6577 504 : gf_list_add(ds->pending_segment_states, seg_state);
6578 504 : ctx->nb_seg_url_pending++;
6579 : }
6580 : }
6581 :
6582 3315 : szIndexName[0] = 0;
6583 3315 : if (ds->idx_template) {
6584 : //get final segment template - output file name is NULL, we already have solved this in source_setup
6585 8 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, ds->set->bitstream_switching, szIndexName, base_ds->rep_id, NULL, base_ds->idx_template, NULL, base_ds->seg_start_time, base_ds->rep->bandwidth, base_ds->seg_number, ctx->stl);
6586 :
6587 : strcpy(szSegmentFullPath, szIndexName);
6588 8 : if (ctx->out_path) {
6589 8 : char *rel = gf_url_concatenate(ctx->out_path, szIndexName);
6590 8 : if (rel) {
6591 : strcpy(szSegmentFullPath, rel);
6592 8 : gf_free(rel);
6593 : }
6594 : }
6595 8 : if (pck)
6596 8 : gf_filter_pck_set_property(pck, GF_PROP_PCK_IDXFILENAME, &PROP_STRING(szSegmentFullPath) );
6597 : }
6598 :
6599 3315 : if (ctx->sseg) {
6600 324 : if (ctx->sigfrag) {
6601 15 : const GF_PropertyValue *p = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_SIDX_RANGE);
6602 15 : if (p) {
6603 1 : if (ds->rep->segment_base && !ds->rep->segment_base->index_range) {
6604 1 : GF_SAFEALLOC(ds->rep->segment_base->index_range, GF_MPD_ByteRange);
6605 1 : if (ds->rep->segment_base->index_range) {
6606 1 : ds->rep->segment_base->index_range->start_range = p->value.lfrac.num;
6607 1 : ds->rep->segment_base->index_range->end_range = p->value.lfrac.den;
6608 1 : ds->rep->segment_base->index_range_exact = GF_TRUE;
6609 : }
6610 :
6611 1 : if (!ds->rep->segment_base->initialization_segment) {
6612 1 : GF_SAFEALLOC(ds->rep->segment_base->initialization_segment, GF_MPD_URL);
6613 : }
6614 1 : if (ds->rep->segment_base->initialization_segment && !ds->rep->segment_base->initialization_segment->byte_range) {
6615 1 : GF_SAFEALLOC(ds->rep->segment_base->initialization_segment->byte_range, GF_MPD_ByteRange);
6616 1 : if (ds->rep->segment_base->initialization_segment->byte_range) {
6617 1 : ds->rep->segment_base->initialization_segment->byte_range->start_range = 0;
6618 1 : ds->rep->segment_base->initialization_segment->byte_range->end_range = p->value.lfrac.num-1;
6619 : }
6620 : }
6621 : } else {
6622 0 : ctx->in_error = GF_TRUE;
6623 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Several SIDX found but trying to regenerate an on-demand MPD, source file is not compatible. Try re-dashing the content or use main or full profiles\n"));
6624 : }
6625 : }
6626 : }
6627 : return;
6628 : }
6629 :
6630 2991 : if (ctx->sfile) {
6631 : GF_MPD_SegmentURL *seg_url;
6632 : assert(ds->rep->segment_list);
6633 349 : GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
6634 349 : if (!seg_url) return;
6635 :
6636 349 : gf_list_add(ds->rep->segment_list->segment_URLs, seg_url);
6637 349 : if (szIndexName[0])
6638 0 : seg_url->index = gf_strdup(szIndexName);
6639 :
6640 349 : if (ctx->sigfrag) {
6641 48 : const GF_PropertyValue *frag_range = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_FRAG_RANGE);
6642 48 : const GF_PropertyValue *frag_url = gf_filter_pck_get_property(in_pck, GF_PROP_PID_URL);
6643 48 : if (frag_url && frag_url->value.string) {
6644 18 : seg_url->media = gf_strdup(dasher_strip_base(ctx, frag_url->value.string));
6645 18 : if (ds->rep->segment_list && ds->rep->segment_list->initialization_segment && !ds->rep->segment_list->initialization_segment->sourceURL) {
6646 :
6647 2 : frag_url = gf_filter_pid_get_property(ds->ipid, GF_PROP_PID_URL);
6648 2 : if (frag_url && frag_url->value.string) {
6649 : u32 j, nb_base;
6650 2 : ds->rep->segment_list->initialization_segment->sourceURL = gf_strdup(dasher_strip_base(ctx, frag_url->value.string) );
6651 :
6652 2 : nb_base = gf_list_count(ds->rep->base_URLs);
6653 4 : for (j=0; j<nb_base; j++) {
6654 2 : GF_MPD_BaseURL *burl = gf_list_get(ds->rep->base_URLs, j);
6655 2 : if (! strcmp(burl->URL, frag_url->value.string)) {
6656 0 : gf_list_rem(ds->rep->base_URLs, j);
6657 0 : gf_mpd_base_url_free(burl);
6658 0 : break;
6659 : }
6660 : }
6661 : }
6662 : }
6663 : }
6664 30 : else if (frag_range) {
6665 30 : GF_SAFEALLOC(seg_url->media_range, GF_MPD_ByteRange);
6666 30 : if (seg_url->media_range) {
6667 30 : seg_url->media_range->start_range = frag_range->value.lfrac.num;
6668 30 : seg_url->media_range->end_range = frag_range->value.lfrac.den - 1;
6669 : }
6670 30 : if (ds->rep->segment_list && ds->rep->segment_list->initialization_segment && !ds->rep->segment_list->initialization_segment->byte_range) {
6671 2 : GF_SAFEALLOC(ds->rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
6672 2 : if (ds->rep->segment_list->initialization_segment->byte_range) {
6673 2 : ds->rep->segment_list->initialization_segment->byte_range->start_range = 0;
6674 2 : ds->rep->segment_list->initialization_segment->byte_range->end_range = frag_range->value.lfrac.num-1;
6675 : }
6676 : }
6677 : }
6678 : } else {
6679 301 : gf_list_add(ds->pending_segment_urls, seg_url);
6680 301 : ctx->nb_seg_url_pending++;
6681 : }
6682 : return;
6683 : }
6684 :
6685 2642 : if (!ctx->stl && !ctx->cues && !ctx->forward_mode) {
6686 2545 : Double drift, seg_start = (Double) ds->seg_start_time;
6687 2545 : seg_start /= ds->mpd_timescale;
6688 2545 : drift = seg_start - ((Double)(ds->seg_number - ds->startNumber)) * ds->dash_dur.num / ds->dash_dur.den;
6689 :
6690 2545 : if ((ds->dash_dur.num>0) && (ABS(drift) * 2 * ds->dash_dur.den > ds->dash_dur.num)) {
6691 : u64 cts = 0;
6692 24 : if (pck) {
6693 24 : cts = dasher_translate_cts(ds, gf_filter_pck_get_cts(pck) );
6694 : }
6695 24 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] First CTS "LLU" in segment %d drifting by %g (more than half a segment duration) from segment time, consider reencoding or using segment timeline\n", cts, ds->seg_number, drift));
6696 : }
6697 : }
6698 :
6699 2642 : if (!ctx->forward_mode) {
6700 : //get final segment template - output file name is NULL, we already have solved this in source_setup
6701 2606 : gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, ds->set->bitstream_switching, szSegmentName, base_ds->rep_id, NULL, base_ds->seg_template, NULL, base_ds->seg_start_time, base_ds->rep->bandwidth, base_ds->seg_number, ctx->stl);
6702 : }
6703 :
6704 :
6705 2714 : send_packet:
6706 : strcpy(szSegmentFullPath, szSegmentName);
6707 :
6708 2660 : if (ctx->out_path) {
6709 : char *rel = NULL;
6710 2660 : if (ctx->do_m3u8 && ds->hls_vp_name && !ctx->forward_mode) {
6711 0 : char *tmp = gf_url_concatenate(ctx->out_path, ds->hls_vp_name);
6712 0 : if (tmp) {
6713 0 : rel = gf_url_concatenate(tmp, szSegmentName);
6714 0 : gf_free(tmp);
6715 : }
6716 : }
6717 0 : if (!rel)
6718 2660 : rel = gf_url_concatenate(ctx->out_path, szSegmentName);
6719 :
6720 2660 : if (rel) {
6721 : strcpy(szSegmentFullPath, rel);
6722 2660 : gf_free(rel);
6723 : }
6724 : }
6725 :
6726 2660 : if (seg_state) {
6727 443 : seg_state->filepath = gf_strdup(szSegmentFullPath);
6728 443 : seg_state->filename = gf_strdup(szSegmentName);
6729 : }
6730 :
6731 2660 : if (ds->rep->segment_list && (ctx->forward_mode!=DASHER_FWD_ALL) ) {
6732 : GF_MPD_SegmentURL *seg_url;
6733 41 : GF_SAFEALLOC(seg_url, GF_MPD_SegmentURL);
6734 41 : if (seg_url) {
6735 41 : gf_list_add(ds->rep->segment_list->segment_URLs, seg_url);
6736 41 : seg_url->media = gf_strdup(szSegmentName);
6737 41 : gf_list_add(ds->pending_segment_urls, seg_url);
6738 41 : if (szIndexName[0])
6739 0 : seg_url->index = gf_strdup(szIndexName);
6740 : }
6741 41 : ctx->nb_seg_url_pending++;
6742 : }
6743 2660 : if (pck)
6744 2651 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(szSegmentFullPath) );
6745 : }
6746 :
6747 140 : static Bool dasher_check_loop(GF_DasherCtx *ctx, GF_DashStream *ds)
6748 : {
6749 : u32 i, count;
6750 : u32 pmode = GF_PLAYBACK_MODE_NONE;
6751 : u64 ts_offset, max_ts_offset, max_ts_scale;
6752 : const GF_PropertyValue *p;
6753 140 : if (!ds->src_url) return GF_FALSE;
6754 :
6755 : //loop requested
6756 140 : if (ds->loop_state==2) return GF_TRUE;
6757 :
6758 140 : count = gf_list_count(ctx->current_period->streams);
6759 140 : if (!ds->loop_state) {
6760 28 : for (i=0; i<count; i++) {
6761 28 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
6762 :
6763 28 : p = gf_filter_pid_get_property(a_ds->ipid, GF_PROP_PID_PLAYBACK_MODE);
6764 28 : if (p) pmode = p->value.uint;
6765 28 : if (pmode == GF_PLAYBACK_MODE_NONE) {
6766 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Loop requested in subdur mode, but source cannot seek, defaulting to multi period for all streams\n"));
6767 0 : ctx->loop = GF_FALSE;
6768 0 : return GF_FALSE;
6769 : }
6770 : }
6771 14 : ds->loop_state = 1;
6772 : }
6773 :
6774 : max_ts_offset = 0;
6775 : max_ts_scale = 1;
6776 : //check all input media duration
6777 110 : for (i=0; i<count; i++) {
6778 243 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
6779 :
6780 : //one pid is waiting for loop while another has done its subdur and won't process any new segment until the next subdur call, which
6781 : //will never happen since the first PID waits for loop. We must force early generation in this case
6782 243 : if (a_ds->subdur_done) {
6783 3 : a_ds->subdur_done = GF_FALSE;
6784 : //remember the max period dur before this forced segment generation
6785 3 : a_ds->subdur_forced_use_period_dur = a_ds->max_period_dur;
6786 : }
6787 :
6788 : //wait for each input to query loop
6789 243 : if (!a_ds->loop_state) {
6790 133 : a_ds->done = 0;
6791 133 : return GF_TRUE;
6792 : }
6793 :
6794 : //get max duration
6795 110 : ts_offset = a_ds->est_next_dts;
6796 :
6797 110 : if (max_ts_offset * a_ds->timescale < ts_offset * max_ts_scale) {
6798 : max_ts_offset = ts_offset;
6799 : max_ts_scale = a_ds->timescale;
6800 : }
6801 : }
6802 :
6803 : //assign ts offset and send stop/play
6804 14 : for (i=0; i<count; i++) {
6805 14 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, i);
6806 :
6807 14 : if (a_ds->subdur_done)
6808 0 : continue;
6809 :
6810 : ts_offset = max_ts_offset;
6811 14 : ts_offset *= a_ds->timescale;
6812 14 : ts_offset /= max_ts_scale;
6813 :
6814 14 : a_ds->ts_offset = ts_offset;
6815 14 : if (a_ds->done) continue;
6816 13 : if (a_ds->ts_offset > a_ds->est_next_dts) {
6817 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Looping streams of unequal duration, inserting "LLU" us of timestamp delay in pid %s from %s\n", ((a_ds->ts_offset - a_ds->est_next_dts) * 1000000) / a_ds->timescale, gf_filter_pid_get_name(a_ds->ipid), a_ds->src_url));
6818 : }
6819 :
6820 13 : a_ds->seek_to_pck = 0;
6821 13 : a_ds->nb_pck = 0;
6822 13 : a_ds->clamp_done = GF_FALSE;
6823 :
6824 13 : a_ds->loop_state = 2;
6825 :
6826 13 : if (ctx->subdur) {
6827 : GF_FilterEvent evt;
6828 :
6829 12 : GF_FEVT_INIT(evt, GF_FEVT_STOP, a_ds->ipid);
6830 12 : gf_filter_pid_send_event(a_ds->ipid, &evt);
6831 :
6832 12 : gf_filter_pid_set_discard(a_ds->ipid, GF_FALSE);
6833 :
6834 12 : dasher_send_encode_hints(ctx, ds);
6835 :
6836 12 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, a_ds->ipid);
6837 12 : evt.play.speed = 1.0;
6838 12 : gf_filter_pid_send_event(a_ds->ipid, &evt);
6839 : }
6840 : }
6841 :
6842 : return GF_TRUE;
6843 : }
6844 :
6845 : //depending on input formats, streams may be declared with or without DCD. For streams requiring the config, wait for it
6846 100 : static Bool dasher_check_period_ready(GF_DasherCtx *ctx, Bool is_session_end)
6847 : {
6848 100 : u32 i=0;
6849 : GF_DashStream *ds;
6850 100 : ctx->period_not_ready = GF_FALSE;
6851 286 : while ((ds = gf_list_enum(ctx->current_period->streams, &i))) {
6852 :
6853 181 : if (is_session_end)
6854 0 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
6855 :
6856 181 : if (ds->dcd_not_ready) {
6857 : GF_FilterPacket *pck;
6858 : u32 prev = ds->dcd_not_ready;
6859 104 : ds->dcd_not_ready = 0;
6860 104 : pck = gf_filter_pid_get_packet(ds->ipid);
6861 104 : if (!pck) {
6862 95 : u32 diff = gf_sys_clock() - prev;
6863 95 : if (diff > 10000) {
6864 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Failed to initialize PID %s, no packets after %d ms, aborting\n", gf_filter_pid_get_name(ds->ipid), diff));
6865 0 : ctx->in_error = GF_TRUE;
6866 0 : return GF_FALSE;
6867 : }
6868 95 : ds->dcd_not_ready = prev;
6869 95 : ctx->period_not_ready = GF_TRUE;
6870 95 : return GF_FALSE;
6871 : }
6872 : }
6873 : }
6874 : return GF_TRUE;
6875 : }
6876 :
6877 5720281 : void dasher_format_report(GF_Filter *filter, GF_DasherCtx *ctx)
6878 : {
6879 : u32 i, count;
6880 : Double max_ts=0;
6881 : u32 total_pc = 0;
6882 : char szDS[200];
6883 5720281 : char *szStatus = NULL;
6884 :
6885 5720281 : if (!gf_filter_reporting_enabled(filter))
6886 5720281 : return;
6887 0 : if (!ctx->update_report)
6888 : return;
6889 : //don't update at each packet, this would be too much
6890 0 : if ((ctx->update_report>0) && (ctx->update_report < 20))
6891 : return;
6892 :
6893 0 : ctx->update_report = 0;
6894 :
6895 0 : sprintf(szDS, "P%s", ctx->current_period->period->ID ? ctx->current_period->period->ID : "1");
6896 0 : gf_dynstrcat(&szStatus, szDS, NULL);
6897 :
6898 0 : count = gf_list_count(ctx->current_period->streams);
6899 0 : for (i=0; i<count; i++) {
6900 : s32 pc=-1;
6901 : Double mpdtime;
6902 : u32 set_idx;
6903 : u32 rep_idx;
6904 : u8 stype;
6905 0 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
6906 0 : if (ds->muxed_base) continue;
6907 :
6908 0 : set_idx = 1 + gf_list_find(ctx->current_period->period->adaptation_sets, ds->set);
6909 0 : rep_idx = 1 + gf_list_find(ds->set->representations, ds->rep);
6910 0 : if (ds->stream_type==GF_STREAM_VISUAL) stype='V';
6911 0 : else if (ds->stream_type==GF_STREAM_AUDIO) stype='A';
6912 0 : else if (ds->stream_type==GF_STREAM_TEXT) stype='T';
6913 : else stype='M';
6914 :
6915 0 : if (ds->done || ds->subdur_done) {
6916 0 : sprintf(szDS, "AS#%d.%d(%c) done (%d segs)", set_idx, rep_idx, stype, ds->seg_number);
6917 0 : pc = 10000;
6918 : } else {
6919 : Double done;
6920 0 : if (ctx->cues) {
6921 0 : done = (Double) (ds->last_dts);
6922 0 : done /= ds->timescale;
6923 0 : snprintf(szDS, 200, "AS#%d.%d(%c) seg #%d %.2fs", set_idx, rep_idx, stype, ds->seg_number, done);
6924 : } else {
6925 : Double pcent, ddur;
6926 0 : done = (Double) ds->adjusted_next_seg_start;
6927 0 : done -= (Double) ds->last_dts;
6928 0 : if (done<0)
6929 : done=0;
6930 0 : done /= ds->timescale;
6931 0 : ddur = ((Double)ds->dash_dur.num) / ds->dash_dur.den;
6932 0 : done = ddur - done;
6933 : //this may happen since we don't print info at segment start
6934 0 : if (done<0)
6935 : done=0;
6936 0 : pcent = done / ddur;
6937 0 : pc = (s32) (done * 10000);
6938 0 : snprintf(szDS, 200, "AS#%d.%d(%c) seg #%d %.2fs (%.2f %%)", set_idx, rep_idx, stype, ds->seg_number, done, 100*pcent);
6939 : }
6940 :
6941 0 : mpdtime = (Double) ds->last_dts;
6942 0 : mpdtime -= (Double) ds->first_dts;
6943 0 : if (mpdtime<0) mpdtime=0;
6944 0 : mpdtime /= ds->timescale;
6945 :
6946 0 : if (ds->duration.den && ds->duration.num) {
6947 : done = mpdtime;
6948 :
6949 0 : done *= ds->duration.den;
6950 0 : done /= ds->duration.num;
6951 0 : pc = (u32) (10000*done);
6952 : }
6953 0 : if (max_ts<mpdtime)
6954 : max_ts = mpdtime;
6955 : }
6956 0 : if (pc > (s32) total_pc) total_pc = (u32) pc;
6957 0 : gf_dynstrcat(&szStatus, szDS, " ");
6958 : }
6959 0 : if (total_pc!=10000) {
6960 0 : sprintf(szDS, " / MPD %.2fs %d %%", max_ts, total_pc/100);
6961 0 : gf_dynstrcat(&szStatus, szDS, NULL);
6962 : }
6963 0 : gf_filter_update_status(filter, total_pc, szStatus);
6964 0 : gf_free(szStatus);
6965 : }
6966 :
6967 123044 : static void dasher_drop_input(GF_DasherCtx *ctx, GF_DashStream *ds, Bool discard_all)
6968 : {
6969 123044 : if (ds->sbound) {
6970 1152 : while (gf_list_count(ds->packet_queue)) {
6971 1143 : GF_FilterPacket *pck = gf_list_pop_front(ds->packet_queue);
6972 1143 : if (gf_filter_pck_get_sap(pck)) {
6973 : assert(ds->nb_sap_in_queue);
6974 699 : ds->nb_sap_in_queue --;
6975 : }
6976 1143 : gf_filter_pck_unref(pck);
6977 1143 : if (!discard_all) break;
6978 : }
6979 : } else {
6980 121978 : gf_filter_pid_drop_packet(ds->ipid);
6981 : }
6982 123044 : if (discard_all) {
6983 82 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
6984 : }
6985 123044 : }
6986 :
6987 :
6988 :
6989 5730092 : static GF_Err dasher_process(GF_Filter *filter)
6990 : {
6991 : u32 i, count, nb_init, has_init, nb_reg_done;
6992 5730092 : GF_DasherCtx *ctx = gf_filter_get_udta(filter);
6993 : GF_Err e;
6994 : Bool seg_done = GF_FALSE;
6995 :
6996 5730092 : if (ctx->in_error) {
6997 0 : gf_filter_abort(filter);
6998 0 : return GF_SERVICE_ERROR;
6999 : }
7000 :
7001 : //session regulation is on and we have a an MPD (setup done) and a next time (first seg processed)
7002 : //check if we have reached the next time
7003 5730092 : if (ctx->sreg && !ctx->state && ctx->mpd && ctx->mpd->gpac_next_ntp_ms) {
7004 0 : s64 diff = (s64) ctx->mpd->gpac_next_ntp_ms;
7005 0 : diff -= (s64) gf_net_get_ntp_ms();
7006 0 : if (diff>100) {
7007 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[Dasher] Next generation scheduled in %d ms, nothing to do\n", diff));
7008 0 : gf_filter_ask_rt_reschedule(filter, (u32) (diff*1000));
7009 0 : return GF_OK;
7010 : }
7011 : }
7012 :
7013 : //streams in period are not all ready, wait for them
7014 5730092 : if (ctx->period_not_ready) {
7015 100 : Bool is_eos = gf_filter_end_of_session(filter);
7016 100 : if (! dasher_check_period_ready(ctx, is_eos)) {
7017 95 : return is_eos ? GF_SERVICE_ERROR : GF_OK;
7018 : }
7019 5 : e = dasher_setup_period(filter, ctx, NULL);
7020 5 : if (e) return e;
7021 : }
7022 5729997 : if (ctx->check_connections) {
7023 925 : if (gf_filter_connections_pending(filter))
7024 : return GF_OK;
7025 670 : ctx->check_connections = GF_FALSE;
7026 : }
7027 :
7028 5729742 : if (ctx->is_eos)
7029 : return GF_EOS;
7030 5720283 : if (ctx->setup_failure) return ctx->setup_failure;
7031 :
7032 : nb_init = has_init = nb_reg_done = 0;
7033 :
7034 5720283 : count = gf_list_count(ctx->current_period->streams);
7035 11538225 : for (i=0; i<count; i++) {
7036 : GF_DashStream *base_ds;
7037 5817944 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
7038 : assert(ds);
7039 5817944 : if (ds->done) continue;
7040 5726729 : base_ds = ds->muxed_base ? ds->muxed_base : ds;
7041 : //subdur mode abort, don't process
7042 5726729 : if (ds->subdur_done) {
7043 250 : continue;
7044 : }
7045 5726479 : if (ds->seg_done) continue;
7046 :
7047 5724793 : if (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST) {
7048 0 : ds->done = 1;
7049 0 : continue;
7050 : }
7051 :
7052 : //flush as much as possible
7053 : while (1) {
7054 : u32 sap_type, dur, o_dur, split_dur;
7055 : s32 check_dur;
7056 : u64 cts, orig_cts, dts, split_dur_next, pcont_cts;
7057 : Bool seg_over = GF_FALSE;
7058 : Bool is_packet_split = GF_FALSE;
7059 : Bool is_queue_flush = GF_FALSE;
7060 : GF_FilterPacket *dst;
7061 5851433 : GF_FilterPacket *pck = NULL;
7062 :
7063 5851433 : if (!ds->request_period_switch) {
7064 : assert(ds->period == ctx->current_period);
7065 5851433 : pck = gf_filter_pid_get_packet(ds->ipid);
7066 : //we may change period after a packet fetch (reconfigure of input pid)
7067 5851433 : if ((ds->period != ctx->current_period) || ds->request_period_switch) {
7068 : //in closest mode, flush queue
7069 0 : if (!ds->sbound || !gf_list_count(ds->packet_queue)) {
7070 : assert(gf_list_find(ctx->current_period->streams, ds)<0);
7071 0 : count = gf_list_count(ctx->current_period->streams);
7072 0 : i--;
7073 5724791 : break;
7074 : }
7075 : is_queue_flush = GF_TRUE;
7076 : }
7077 : } else {
7078 : is_queue_flush = GF_TRUE;
7079 : }
7080 5851433 : if (ds->sbound && pck && gf_filter_pck_is_blocking_ref(pck)) {
7081 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Cannot use `sbound` with blocking input packet references, disabling packet buffering for PID %s\n", gf_filter_pid_get_name(ds->ipid) ));
7082 0 : ds->sbound = DASHER_BOUNDS_OUT;
7083 : }
7084 :
7085 : //skipped merged tile base
7086 5851433 : if (ds->merged_tile_dep) {
7087 780 : if (pck) gf_filter_pid_drop_packet(ds->ipid);
7088 780 : pck = NULL;
7089 : }
7090 : //queue mode
7091 5850653 : else if (ds->sbound) {
7092 2224 : if (!is_queue_flush && pck) {
7093 1143 : gf_filter_pck_ref(&pck);
7094 1143 : gf_filter_pid_drop_packet(ds->ipid);
7095 1143 : gf_list_add(ds->packet_queue, pck);
7096 1143 : if (gf_filter_pck_get_sap(pck))
7097 699 : ds->nb_sap_in_queue ++;
7098 : }
7099 2224 : if (
7100 : //we are flushing due to period switch
7101 : is_queue_flush
7102 : //we are flushing due to end of stream
7103 2224 : || gf_filter_pid_is_eos(ds->ipid) || ds->clamp_done
7104 : ) {
7105 246 : pck = gf_list_get(ds->packet_queue, 0);
7106 246 : is_queue_flush = GF_TRUE;
7107 1978 : } else if (
7108 : //if current segment is not started, always get packet from queue
7109 1978 : !ds->segment_started
7110 : //wait until we have more than 2 saps to get packet from queue, to check if next sap will be closer or not
7111 1962 : || (ds->nb_sap_in_queue>=2)
7112 : ) {
7113 838 : pck = gf_list_get(ds->packet_queue, 0);
7114 : } else {
7115 1140 : pck = NULL;
7116 : }
7117 : }
7118 :
7119 :
7120 5851433 : if (!pck) {
7121 5717672 : if (ds->request_period_switch) {
7122 0 : e = dasher_stream_period_changed(filter, ctx, ds, (ds->request_period_switch==2) ? GF_TRUE : GF_FALSE);
7123 0 : if (e < 0) {
7124 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Period switch request failed.\n"));
7125 0 : i--;
7126 0 : break;
7127 : }
7128 : assert(gf_list_find(ctx->current_period->streams, ds)<0);
7129 0 : count = gf_list_count(ctx->current_period->streams);
7130 0 : i--;
7131 0 : break;
7132 : }
7133 :
7134 :
7135 5717672 : if (gf_filter_pid_is_eos(ds->ipid) || ds->clamp_done) {
7136 : u32 ds_done = 1;
7137 480 : if (ctx->loop && dasher_check_loop(ctx, ds)) {
7138 140 : if (ctx->subdur)
7139 : break;
7140 : //loop on the entire source, consider the stream not done for segment flush
7141 : ds_done = 0;
7142 : }
7143 :
7144 342 : ds->clamp_done = GF_FALSE;
7145 :
7146 342 : ctx->update_report = -1;
7147 : //opid may be NULL for skipped tile rep
7148 342 : if (!ctx->sigfrag && ds->opid)
7149 335 : gf_filter_pid_set_eos(ds->opid);
7150 :
7151 342 : if (!ds->done) ds->done = ds_done;
7152 342 : ds->seg_done = GF_TRUE;
7153 : seg_done = GF_TRUE;
7154 342 : ds->first_cts_in_next_seg = ds->est_first_cts_in_next_seg;
7155 342 : ds->est_first_cts_in_next_seg = 0;
7156 : assert(base_ds->nb_comp_done < base_ds->nb_comp);
7157 342 : base_ds->nb_comp_done ++;
7158 342 : if (base_ds->nb_comp_done == base_ds->nb_comp) {
7159 333 : dasher_flush_segment(ctx, base_ds, GF_FALSE);
7160 333 : base_ds->nb_comp_done = 0;
7161 : }
7162 : //loop on the entire source, mark as done for subdur and check if all other streams are done
7163 342 : if (!ds->done) {
7164 : u32 j;
7165 2 : ds->done = 2;
7166 2 : ds->subdur_done = GF_TRUE;
7167 : u32 nb_sub_done=0;
7168 6 : for (j=0; j<count; j++) {
7169 4 : GF_DashStream *a_ds = gf_list_get(ctx->current_period->streams, j);
7170 4 : if (a_ds->muxed_base) a_ds = a_ds->muxed_base;
7171 4 : if (a_ds->subdur_done) {
7172 2 : nb_sub_done++;
7173 : }
7174 : }
7175 2 : if (nb_sub_done==count)
7176 0 : ctx->subdur_done = GF_TRUE;
7177 340 : } else if (ctx->reschedule && !ctx->loop && (ctx->dmode==GF_MPD_TYPE_DYNAMIC) && !strcmp(ds->period_id, DEFAULT_PERIOD_ID) ) {
7178 8 : if (gf_list_find(ctx->next_period->streams, ds)<0) {
7179 4 : gf_list_add(ctx->next_period->streams, ds);
7180 : }
7181 8 : ctx->post_play_events = GF_TRUE;
7182 8 : ds->nb_repeat++;
7183 8 : ds->reschedule = GF_TRUE;
7184 8 : gf_filter_pid_discard_block(ds->opid);
7185 : }
7186 : }
7187 : break;
7188 : }
7189 133761 : if (ds->seek_to_pck) {
7190 200 : u32 sn = gf_filter_pck_get_seq_num(pck);
7191 200 : if (sn) {
7192 200 : if (sn <= ds->seek_to_pck) {
7193 186 : dasher_drop_input(ctx, ds, GF_FALSE);
7194 4024 : continue;
7195 : }
7196 14 : ds->nb_pck = sn-1;
7197 : } else {
7198 : //no sn signaled, this implies we played from the beginning
7199 0 : if (ds->nb_pck < ds->seek_to_pck) {
7200 0 : ds->nb_pck ++;
7201 0 : dasher_drop_input(ctx, ds, GF_FALSE);
7202 0 : continue;
7203 : }
7204 : }
7205 : }
7206 133575 : sap_type = gf_filter_pck_get_sap(pck);
7207 133575 : ds->loop_state = 0;
7208 :
7209 133575 : cts = gf_filter_pck_get_cts(pck);
7210 133575 : dts = gf_filter_pck_get_dts(pck);
7211 133575 : if (dts==GF_FILTER_NO_TS) dts = cts;
7212 :
7213 : pcont_cts = cts;
7214 :
7215 133575 : if (!ds->rep_init) {
7216 : u32 set_start_with_sap;
7217 357 : if (!sap_type) {
7218 0 : dasher_drop_input(ctx, ds, GF_FALSE);
7219 0 : break;
7220 : }
7221 :
7222 357 : set_start_with_sap = ctx->sseg ? base_ds->set->subsegment_starts_with_sap : base_ds->set->starts_with_sap;
7223 357 : if (!ds->muxed_base) {
7224 : //force sap type to 1 for non-visual streams if strict_sap is set to off
7225 425 : if ((ds->stream_type!=GF_STREAM_VISUAL) && (ctx->strict_sap==DASHER_SAP_OFF) ) {
7226 79 : switch (ds->codec_id) {
7227 : //MPEG-H requires saps
7228 : case GF_CODECID_MPHA:
7229 : case GF_CODECID_MHAS:
7230 : break;
7231 79 : default:
7232 : sap_type = 1;
7233 79 : break;
7234 : }
7235 267 : }
7236 : //set AS sap type
7237 346 : if (!set_start_with_sap) {
7238 : //don't set SAP type if not a base rep - could be further checked
7239 : //if (!gf_list_count(ds->complementary_streams) )
7240 : {
7241 317 : if (ctx->sseg) {
7242 21 : ds->set->subsegment_starts_with_sap = sap_type;
7243 : } else {
7244 296 : ds->set->starts_with_sap = sap_type;
7245 : }
7246 : }
7247 : }
7248 29 : else if (set_start_with_sap != sap_type) {
7249 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Segments do not start with the same SAP types: set initialized with %d but first packet got %d - bitstream will not be compliant\n", set_start_with_sap, sap_type));
7250 : }
7251 : //TODO setup proper PTO, the code below will break sync by realigning first AU of each stream
7252 346 : if ((s64) cts + ds->pts_minus_cts > 0) {
7253 3 : u64 pto = cts + ds->pts_minus_cts;
7254 3 : if (ds->rep->segment_list)
7255 2 : ds->rep->segment_list->presentation_time_offset = pto;
7256 1 : else if (ds->rep->segment_template)
7257 0 : ds->rep->segment_template->presentation_time_offset = pto;
7258 1 : else if (ds->set->segment_template)
7259 1 : ds->set->segment_template->presentation_time_offset = pto;
7260 0 : else if (ds->rep->segment_base)
7261 0 : ds->rep->segment_base->presentation_time_offset = pto;
7262 : }
7263 : //period continuity, skip priming in new periods
7264 346 : if (ds->period_continuity_id)
7265 0 : ds->pts_minus_cts = 0;
7266 : }
7267 :
7268 357 : ds->first_cts = cts;
7269 357 : ds->first_dts = dts;
7270 357 : ds->rep_init++;
7271 357 : has_init++;
7272 : }
7273 :
7274 133575 : nb_init++;
7275 :
7276 133575 : if (ds->ts_offset) {
7277 579 : cts += ds->ts_offset;
7278 579 : dts += ds->ts_offset;
7279 : }
7280 :
7281 : //ready to write MPD for the first time in dynamic mode with template
7282 133575 : if (has_init && (nb_init==count) && (ctx->dmode==GF_MPD_TYPE_DYNAMIC) && ctx->tpl && ctx->do_mpd && !ctx->dyn_rate) {
7283 13 : e = dasher_send_manifest(filter, ctx, GF_TRUE);
7284 15 : if (e) return e;
7285 : }
7286 :
7287 : cts = dasher_translate_cts(ds, cts);
7288 133575 : dts -= ds->first_dts;
7289 :
7290 133575 : if (ctx->sreg && ctx->mpd->gpac_mpd_time && (dts * 1000 > ctx->mpd->gpac_mpd_time * ds->timescale)) {
7291 0 : nb_reg_done++;
7292 0 : break;
7293 : }
7294 :
7295 133575 : dur = o_dur = gf_filter_pck_get_duration(pck);
7296 133575 : pcont_cts += dur;
7297 133575 : if (ds->period_continuity_next_cts < pcont_cts)
7298 116120 : ds->period_continuity_next_cts = pcont_cts;
7299 :
7300 : split_dur = 0;
7301 : split_dur_next = 0;
7302 :
7303 : //patch to align old arch with new
7304 : check_dur = 0;
7305 133575 : if (ds->stream_type==GF_STREAM_AUDIO)
7306 42376 : check_dur = dur;
7307 :
7308 : //adjust duration and cts
7309 : orig_cts = cts;
7310 133575 : if (ds->split_dur_next) {
7311 52 : cts += ds->split_dur_next;
7312 : assert(dur > ds->split_dur_next);
7313 52 : dur -= ds->split_dur_next;
7314 : split_dur_next = ds->split_dur_next;
7315 52 : ds->split_dur_next = 0;
7316 : is_packet_split = GF_TRUE;
7317 : }
7318 :
7319 133575 : if (ds->splitable && !ds->split_dur_next && !ds->cues && !ds->inband_cues) {
7320 : Bool do_split = GF_FALSE;
7321 : //adding this sample would exceed the segment duration
7322 74 : if (gf_sys_old_arch_compat()) {
7323 74 : if ( (cts + dur) * base_ds->timescale >= base_ds->adjusted_next_seg_start * ds->timescale )
7324 : do_split = GF_TRUE;
7325 : } else {
7326 0 : if ( (cts + dur) * base_ds->timescale > base_ds->adjusted_next_seg_start * ds->timescale )
7327 : do_split = GF_TRUE;
7328 : }
7329 : if (do_split) {
7330 : //this sample starts in the current segment - split it
7331 52 : if (cts * base_ds->timescale < base_ds->adjusted_next_seg_start * ds->timescale ) {
7332 26 : split_dur = (u32) (base_ds->adjusted_next_seg_start * ds->timescale / base_ds->timescale - ds->last_cts);
7333 :
7334 26 : if (gf_sys_old_arch_compat() && (split_dur==dur))
7335 : split_dur=0;
7336 :
7337 26 : if (split_dur>=dur)
7338 : split_dur=0;
7339 : }
7340 : }
7341 : }
7342 :
7343 : //mux rep, wait for a CTS more than our base if base not yet over
7344 133575 : if ((base_ds != ds) && !base_ds->seg_done && (cts * base_ds->timescale > base_ds->last_cts * ds->timescale ) )
7345 : break;
7346 :
7347 129605 : if (ds->seek_to_pck) {
7348 14 : ds->seek_to_pck = 0;
7349 : }
7350 : //force flush mode, segment is done upon eos
7351 129591 : else if (ctx->force_flush) {
7352 : }
7353 : //source-driven fragmentation check for segment start
7354 129591 : else if (ctx->sigfrag) {
7355 3666 : const GF_PropertyValue *p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FRAG_START);
7356 3666 : if (p && (p->value.uint>=1) && base_ds->segment_started) {
7357 : seg_over = GF_TRUE;
7358 66 : if (ds == base_ds) {
7359 66 : base_ds->adjusted_next_seg_start = cts;
7360 : }
7361 : }
7362 : }
7363 : //inband-cue based segmentation
7364 125925 : else if (ds->inband_cues) {
7365 3375 : const GF_PropertyValue *p = gf_filter_pck_get_property(pck, GF_PROP_PCK_CUE_START);
7366 3375 : if (p && p->value.boolean && base_ds->segment_started) {
7367 : seg_over = GF_TRUE;
7368 27 : if (ds == base_ds) {
7369 27 : base_ds->adjusted_next_seg_start = cts;
7370 : }
7371 : }
7372 : }
7373 : //cue-list based segmentation
7374 122550 : else if (ds->cues) {
7375 : u32 cidx;
7376 : GF_DASHCueInfo *cue=NULL;
7377 : Bool is_cue_split = GF_FALSE;
7378 : s32 has_mismatch = -1;
7379 :
7380 2 : for (cidx=0;cidx<ds->nb_cues; cidx++) {
7381 5049 : cue = &ds->cues[cidx];
7382 5049 : if (cue->sample_num) {
7383 1212 : if (cue->sample_num == ds->nb_pck + 1) {
7384 : is_cue_split = GF_TRUE;
7385 : break;
7386 1200 : } else if (cue->sample_num < ds->nb_pck) {
7387 0 : has_mismatch = cidx;
7388 : } else {
7389 : break;
7390 : }
7391 : }
7392 3837 : else if (cue->dts) {
7393 1212 : u64 ts = (cue->dts - ds->cues_ts_offset) * ds->timescale;
7394 1212 : u64 ts2 = dts * ds->cues_timescale;
7395 1212 : if (ts == ts2) {
7396 : is_cue_split = GF_TRUE;
7397 : break;
7398 1200 : } else if (ts < ts2) {
7399 0 : has_mismatch = cidx;
7400 : } else {
7401 : break;
7402 : }
7403 : }
7404 2625 : else if (cue->cts) {
7405 2625 : s64 ts = (cue->cts - ds->cues_ts_offset) * ds->timescale;
7406 2625 : s64 ts2 = (cts + ds->first_cts) * ds->cues_timescale;
7407 :
7408 : //cues are given in track timeline (presentation time), subtract the media time to pres time offset
7409 2625 : if (ds->cues_use_edits) {
7410 1212 : ts2 += (s64) (ds->pts_minus_cts) * ds->cues_timescale;
7411 : }
7412 2625 : if (ts == ts2) {
7413 : is_cue_split = GF_TRUE;
7414 : break;
7415 2602 : } else if (ts < ts2) {
7416 2 : has_mismatch = cidx;
7417 : } else {
7418 : break;
7419 : }
7420 : }
7421 : }
7422 : //start of first segment
7423 6247 : if (is_cue_split && !ds->segment_started) {
7424 0 : memmove(ds->cues, &ds->cues[cidx+1], (ds->nb_cues-cidx-1) * sizeof(GF_DASHCueInfo));
7425 0 : ds->nb_cues -= cidx+1;
7426 : is_cue_split = 0;
7427 : }
7428 :
7429 6247 : if (is_cue_split) {
7430 47 : if (!sap_type) {
7431 0 : GF_LOG(ctx->strict_cues ? GF_LOG_ERROR : GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] cue found (sn %d - dts "LLD" - cts "LLD") for PID %s but packet %d is not RAP !\n", cue->sample_num, cue->dts, cue->cts, gf_filter_pid_get_name(ds->ipid), ds->nb_pck));
7432 0 : if (ctx->strict_cues) {
7433 0 : gf_filter_pid_drop_packet(ds->ipid);
7434 0 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
7435 0 : return GF_BAD_PARAM;
7436 : }
7437 : }
7438 47 : memmove(ds->cues, &ds->cues[cidx+1], (ds->nb_cues-cidx-1) * sizeof(GF_DASHCueInfo));
7439 47 : ds->nb_cues -= cidx+1;
7440 :
7441 47 : if (sap_type==3)
7442 47 : ds->nb_sap_3 ++;
7443 0 : else if (sap_type>3)
7444 0 : ds->nb_sap_4 ++;
7445 :
7446 : /*check requested profiles can be generated, or adjust them*/
7447 47 : if (
7448 94 : (ds->nb_sap_4 || (ds->nb_sap_3 > 1))
7449 39 : && (ctx->profile != GF_DASH_PROFILE_FULL)
7450 : /*TODO: store at DS level whether the usage of sap4 is ok or not (eg roll info for AAC is OK, not for xHEAAC-v2)
7451 : for now we only complain for video*/
7452 8 : && ((ds->stream_type==GF_STREAM_VISUAL) || (ctx->strict_sap==DASHER_SAP_ON) )
7453 : ) {
7454 8 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] WARNING! Max SAP type %d detected - switching to FULL profile\n", ds->nb_sap_4 ? 4 : 3));
7455 8 : ctx->profile = GF_DASH_PROFILE_FULL;
7456 8 : if (ctx->sseg)
7457 0 : ds->set->subsegment_starts_with_sap = sap_type;
7458 : else
7459 8 : ds->set->starts_with_sap = sap_type;
7460 : }
7461 :
7462 :
7463 : seg_over = GF_TRUE;
7464 47 : if (ds == base_ds) {
7465 47 : base_ds->adjusted_next_seg_start = cts;
7466 : }
7467 : }
7468 :
7469 6247 : if (has_mismatch>=0) {
7470 2 : cue = &ds->cues[has_mismatch];
7471 2 : GF_LOG(ctx->strict_cues ? GF_LOG_ERROR : GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] found cue (sn %d - dts "LLD" - cts "LLD") in stream %s before current packet (sn %d - dts "LLD" - cts "LLD") , buggy source cues ?\n", cue->sample_num, cue->dts, cue->cts, gf_filter_pid_get_name(ds->ipid), ds->nb_pck+1, dts + ds->first_cts, cts + ds->first_cts));
7472 2 : if (ctx->strict_cues) {
7473 2 : gf_filter_pid_drop_packet(ds->ipid);
7474 2 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
7475 2 : return GF_BAD_PARAM;
7476 : }
7477 : }
7478 : }
7479 : //forcing max time
7480 116303 : else if (
7481 116303 : (base_ds->force_rep_end && (cts * base_ds->timescale >= base_ds->force_rep_end * ds->timescale) )
7482 116303 : || (base_ds->clamped_dur.num && (cts + o_dur > ds->ts_offset + base_ds->clamped_dur.num * ds->timescale / base_ds->clamped_dur.den))
7483 : ) {
7484 52 : if (!base_ds->period->period->duration && base_ds->force_rep_end) {
7485 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Inputs duration do not match, %s truncated to %g duration\n", ds->src_url, ((Double)base_ds->force_rep_end)/base_ds->timescale ));
7486 : }
7487 52 : dasher_drop_input(ctx, ds, GF_TRUE);
7488 52 : ds->clamp_done = GF_TRUE;
7489 52 : continue;
7490 : }
7491 : //we have a SAP and we work in closest mode: check the next SAP in the queue, and decide if we
7492 : //split the segment at this SAP or wait for the next one
7493 116251 : else if (ds->segment_started && ds->sbound && sap_type) {
7494 683 : u32 idx, nb_queued, nb_pck = gf_list_count(ds->packet_queue);
7495 : nb_queued = nb_pck;
7496 683 : if (is_queue_flush) nb_queued += 1;
7497 :
7498 1550 : for (idx=1; idx<nb_queued; idx++) {
7499 : GF_FilterPacket *next;
7500 883 : if (idx==nb_pck) {
7501 5 : next = gf_list_last(ds->packet_queue);
7502 : } else {
7503 878 : next = gf_list_get(ds->packet_queue, idx);
7504 878 : u32 sap_next = gf_filter_pck_get_sap(next);
7505 878 : if (!sap_next) continue;
7506 : }
7507 683 : u32 next_dur = gf_filter_pck_get_duration(next);
7508 : //compute cts next
7509 683 : u64 cts_next = gf_filter_pck_get_cts(next);
7510 683 : if (ds->ts_offset) {
7511 72 : cts_next += ds->ts_offset;
7512 : }
7513 : cts_next = dasher_translate_cts(ds, cts_next);
7514 :
7515 683 : if ((idx==nb_pck) && ctx->last_seg_merge) {
7516 0 : u64 next_seg_dur = (cts_next + next_dur - cts);
7517 0 : if (next_seg_dur * ds->dash_dur.den < (u64) ds->dash_dur.num * ds->timescale / 2)
7518 0 : continue;
7519 : }
7520 :
7521 : //same rule as above
7522 683 : if ((cts_next + next_dur) * base_ds->timescale >= base_ds->adjusted_next_seg_start * ds->timescale ) {
7523 : Bool force_seg_flush = GF_FALSE;
7524 31 : s64 diff_next = cts_next * base_ds->timescale / ds->timescale;
7525 31 : diff_next -= base_ds->adjusted_next_seg_start;
7526 : //bounds at closest: if this SAP is closer to the target next segment start than the next SAP, split at this packet
7527 31 : if (ds->sbound==DASHER_BOUNDS_CLOSEST) {
7528 16 : s64 diff = cts * base_ds->timescale / ds->timescale;
7529 16 : diff -= base_ds->adjusted_next_seg_start;
7530 : //this one may be negative, but we always want diff_next positive (next SAP in next segment)
7531 16 : if (diff<0)
7532 12 : diff = -diff;
7533 : //old arch was only using closest for tracks with sync points
7534 16 : if (gf_sys_old_arch_compat() && (base_ds->sync_points_type==DASHER_SYNC_NONE) ) {
7535 8 : if (diff_next > 0) {
7536 : force_seg_flush = GF_TRUE;
7537 : }
7538 : }
7539 8 : else if (diff<diff_next) {
7540 : force_seg_flush = GF_TRUE;
7541 : }
7542 : }
7543 : //bounds always in: if the next SAP is strictly greater than the target next segment start, split at this packet
7544 : else {
7545 15 : if (diff_next > 0) {
7546 : force_seg_flush = GF_TRUE;
7547 : }
7548 : }
7549 : if (force_seg_flush) {
7550 : seg_over = GF_TRUE;
7551 16 : if (ds == base_ds) {
7552 16 : base_ds->adjusted_next_seg_start = cts;
7553 : }
7554 : break;
7555 : }
7556 : }
7557 : }
7558 : }
7559 : //we exceed segment duration - if segment was started, check if we need to stop segment
7560 : //if segment was not started we insert the packet anyway
7561 115568 : else if (!ds->sbound && ds->segment_started && ((cts + check_dur) * base_ds->timescale >= base_ds->adjusted_next_seg_start * ds->timescale ) ) {
7562 : //no sap, segment is over
7563 6568 : if (! ctx->sap) {
7564 : seg_over = GF_TRUE;
7565 : }
7566 6501 : else if ((ds->stream_type==GF_STREAM_AUDIO)
7567 653 : && ((cts + check_dur) * base_ds->timescale == base_ds->adjusted_next_seg_start * ds->timescale)
7568 : ) {
7569 :
7570 : }
7571 : // sap, segment is over
7572 6500 : else if (sap_type) {
7573 :
7574 2939 : if (sap_type==3)
7575 8 : ds->nb_sap_3 ++;
7576 2931 : else if (sap_type>3)
7577 14 : ds->nb_sap_4 ++;
7578 :
7579 : /*check requested profiles can be generated, or adjust them*/
7580 2939 : if ((ctx->profile != GF_DASH_PROFILE_FULL)
7581 2621 : && (ds->nb_sap_4 || (ds->nb_sap_3 > 1))
7582 : /*TODO: store at DS level whether the usage of sap4 is ok or not (eg roll info for AAC is OK, not for xHEAAC-v2)
7583 : for now we only complain for video*/
7584 2 : && ((ds->stream_type==GF_STREAM_VISUAL) || (ctx->strict_sap==DASHER_SAP_ON) )
7585 : ) {
7586 2 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] WARNING! Max SAP type %d detected - switching to FULL profile\n", ds->nb_sap_4 ? 4 : 3));
7587 2 : ctx->profile = GF_DASH_PROFILE_FULL;
7588 2 : if (ctx->sseg)
7589 2 : ds->set->subsegment_starts_with_sap = sap_type;
7590 : else
7591 0 : ds->set->starts_with_sap = sap_type;
7592 : }
7593 :
7594 : seg_over = GF_TRUE;
7595 2939 : if (ds == base_ds) {
7596 2828 : base_ds->adjusted_next_seg_start = cts;
7597 : }
7598 : }
7599 : }
7600 :
7601 :
7602 129551 : if (ds->muxed_base && ds->muxed_base->done) {
7603 : seg_over = GF_FALSE;
7604 : }
7605 : //if flushing now will result in a one sample fragment afterwards
7606 : //because this is the before-last sample, don't flush unless:
7607 : //- we have an asto set (low latency)
7608 : //- this is not an audio stream or all samples are SAPs
7609 : //- we use cues
7610 129502 : else if (seg_over && ds->nb_samples_in_source && !ctx->loop
7611 2081 : && (ds->nb_pck+1 == ds->nb_samples_in_source)
7612 13 : && !ds->inband_cues && !ds->cues
7613 13 : && !ctx->asto
7614 13 : && ! ((ds->sync_points_type==DASHER_SYNC_NONE) && (ds->stream_type!=GF_STREAM_AUDIO))
7615 : ) {
7616 : seg_over = GF_FALSE;
7617 : }
7618 : //if dur=0 (some text streams), don't flush segment
7619 129551 : if (seg_over && dur) {
7620 : assert(!ds->seg_done);
7621 :
7622 3149 : if (ds->request_period_switch && !gf_list_count(ds->packet_queue)) {
7623 0 : e = dasher_stream_period_changed(filter, ctx, ds, (ds->request_period_switch==2) ? GF_TRUE : GF_FALSE);
7624 0 : if (e < 0) {
7625 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Period switch request failed.\n"));
7626 : break;
7627 : }
7628 : assert(gf_list_find(ctx->current_period->streams, ds)<0);
7629 0 : count = gf_list_count(ctx->current_period->streams);
7630 0 : i--;
7631 0 : break;
7632 : }
7633 :
7634 3149 : ds->seg_done = GF_TRUE;
7635 :
7636 : //in dynamic mode, send end of dash segment marker to flush segment right away, otherwise we will
7637 : //flush the segment at next segment start which could be after the segment AST => 404
7638 3149 : if (!ctx->subdur && (ctx->dmode>=GF_DASH_DYNAMIC)) {
7639 : GF_FilterPacket *eods_pck;
7640 74 : eods_pck = gf_filter_pck_new_alloc(ds->opid, 0, NULL);
7641 74 : if (eods_pck) {
7642 74 : gf_filter_pck_set_property(eods_pck, GF_PROP_PCK_EODS, &PROP_BOOL(GF_TRUE) );
7643 74 : gf_filter_pck_send(eods_pck);
7644 : }
7645 : }
7646 :
7647 3149 : ds->first_cts_in_next_seg = cts;
7648 : assert(base_ds->nb_comp_done < base_ds->nb_comp);
7649 3149 : base_ds->nb_comp_done ++;
7650 :
7651 3149 : if (split_dur_next)
7652 26 : ds->split_dur_next = (u32) split_dur_next;
7653 :
7654 3149 : if (base_ds->nb_comp_done == base_ds->nb_comp) {
7655 3001 : dasher_flush_segment(ctx, base_ds, GF_FALSE);
7656 : seg_done = GF_TRUE;
7657 : }
7658 : break;
7659 : }
7660 :
7661 126402 : if (cts==GF_FILTER_NO_TS) {
7662 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] WARNING! Source packet has no timestamp !\n"));
7663 :
7664 0 : cts = ds->last_cts;
7665 0 : dts = ds->last_dts;
7666 : } else {
7667 126402 : u64 ncts = cts + (split_dur ? split_dur : dur);
7668 126402 : if (ncts>ds->est_first_cts_in_next_seg)
7669 116645 : ds->est_first_cts_in_next_seg = ncts;
7670 :
7671 126402 : ncts *= 1000;
7672 126402 : ncts /= ds->timescale;
7673 126402 : if (ncts>base_ds->max_period_dur)
7674 110988 : base_ds->max_period_dur = ncts;
7675 :
7676 126402 : ds->last_cts = cts + (split_dur ? split_dur : dur);
7677 126402 : ds->last_dts = dts;
7678 126402 : ds->est_next_dts = dts + o_dur;
7679 : }
7680 126402 : ds->nb_pck ++;
7681 :
7682 126402 : if (ctx->sigfrag) {
7683 3600 : if (!ds->segment_started) {
7684 72 : ds->first_cts_in_seg = cts;
7685 72 : dasher_mark_segment_start(ctx, ds, NULL, pck);
7686 72 : ds->segment_started = GF_TRUE;
7687 : }
7688 :
7689 3600 : ds->cumulated_dur += dur;
7690 :
7691 : //drop packet if not splitting
7692 3600 : if (!ds->split_dur_next)
7693 3600 : gf_filter_pid_drop_packet(ds->ipid);
7694 :
7695 3600 : if (ctx->in_error) {
7696 0 : gf_filter_pid_set_discard(ds->ipid, GF_TRUE);
7697 0 : gf_filter_pid_set_eos(ctx->opid);
7698 0 : return GF_BAD_PARAM;
7699 : }
7700 3600 : continue;
7701 : }
7702 : //create new ref to input
7703 122802 : dst = gf_filter_pck_new_ref(ds->opid, 0, 0, pck);
7704 122802 : if (!dst) return GF_OUT_OF_MEM;
7705 :
7706 : //merge all props
7707 122802 : gf_filter_pck_merge_properties(pck, dst);
7708 : //we have ts offset, use computed cts and dts
7709 122802 : if (ds->ts_offset) {
7710 563 : gf_filter_pck_set_cts(dst, gf_filter_pck_get_cts(pck) + ds->ts_offset);
7711 563 : gf_filter_pck_set_dts(dst, gf_filter_pck_get_dts(pck) + ds->ts_offset);
7712 : }
7713 :
7714 122802 : if (gf_sys_old_arch_compat() && ds->clamped_dur.num && ctx->loop
7715 1917 : && (cts + 2*o_dur >= ds->ts_offset + base_ds->clamped_dur.num * ds->timescale / base_ds->clamped_dur.den)
7716 : ) {
7717 : u32 _dur = dur;
7718 : /* simple round with (int)+.5 to avoid trucating .99999 to 0 */
7719 21 : dur = (u32) (ds->clamped_dur.num * ds->timescale / ds->clamped_dur.den - (dts - ds->ts_offset) + 0.5);
7720 : //it may happen that the sample duration is 0 if the clamp duration is right after the sample DTS and timescale is not big enough to express it - force to 1
7721 21 : if (dur==0)
7722 : dur=1;
7723 :
7724 21 : gf_filter_pck_set_duration(dst, dur);
7725 21 : ds->est_next_dts += (s32) dur - (s32) _dur;;
7726 : }
7727 :
7728 122802 : if (!ds->segment_started) {
7729 3418 : ds->first_cts_in_seg = cts;
7730 3418 : dasher_mark_segment_start(ctx, ds, dst, pck);
7731 3418 : ds->segment_started = GF_TRUE;
7732 : }
7733 : //prev packet was split
7734 122802 : if (is_packet_split) {
7735 : u64 diff=0;
7736 26 : u8 dep_flags = gf_filter_pck_get_dependency_flags(pck);
7737 26 : u64 ts = gf_filter_pck_get_cts(pck);
7738 26 : if (ts != GF_FILTER_NO_TS) {
7739 26 : cts += ds->first_cts;
7740 : assert(cts >= ts);
7741 26 : diff = cts - ts;
7742 : } else {
7743 0 : cts = ds->last_cts;
7744 : }
7745 26 : gf_filter_pck_set_cts(dst, cts + ds->ts_offset);
7746 :
7747 26 : ts = gf_filter_pck_get_dts(pck);
7748 26 : if (ts != GF_FILTER_NO_TS)
7749 26 : gf_filter_pck_set_dts(dst, ts + diff + ds->ts_offset);
7750 :
7751 : //add sample is redundant flag
7752 26 : dep_flags |= 0x1;
7753 26 : gf_filter_pck_set_dependency_flags(dst, dep_flags);
7754 : //this one might be incorrect of this split packet is also split, but we update the duration right below
7755 26 : gf_filter_pck_set_duration(dst, dur);
7756 : }
7757 :
7758 : //if split, adjust duration - this may happen on a split packet, if it covered 3 or more segments
7759 122802 : if (split_dur) {
7760 : u32 cumulated_split_dur = split_dur;
7761 26 : gf_filter_pck_set_duration(dst, split_dur);
7762 : //adjust dur
7763 26 : cumulated_split_dur += (u32) (cts - orig_cts);
7764 : assert( dur > split_dur);
7765 26 : ds->split_dur_next = cumulated_split_dur;
7766 : dur = split_dur;
7767 : }
7768 :
7769 : //remove NTP
7770 122802 : if (ctx->ntp != DASHER_NTP_KEEP)
7771 122802 : gf_filter_pck_set_property(dst, GF_PROP_PCK_SENDER_NTP, NULL);
7772 :
7773 : //change packet times
7774 122802 : if (ds->force_timescale) {
7775 : u64 ats;
7776 0 : ats = gf_filter_pck_get_dts(dst);
7777 0 : if (ats!=GF_FILTER_NO_TS) {
7778 0 : ats *= ds->force_timescale;
7779 0 : ats /= ds->timescale;
7780 0 : gf_filter_pck_set_dts(dst, ats);
7781 : }
7782 0 : ats = gf_filter_pck_get_cts(dst);
7783 0 : if (ats!=GF_FILTER_NO_TS) {
7784 0 : ats *= ds->force_timescale;
7785 0 : ats /= ds->timescale;
7786 0 : gf_filter_pck_set_cts(dst, ats);
7787 : }
7788 0 : ats = (u64) gf_filter_pck_get_duration(dst);
7789 0 : if (ats) {
7790 0 : ats *= ds->force_timescale;
7791 0 : ats /= ds->timescale;
7792 0 : gf_filter_pck_set_duration(dst, (u32) ats);
7793 : }
7794 : }
7795 :
7796 122802 : ds->cumulated_dur += dur;
7797 :
7798 122802 : if (ds->current_seg_state && gf_filter_pck_get_crypt_flags(pck))
7799 1416 : ds->current_seg_state->encrypted = GF_TRUE;
7800 : //TODO check drift between MPD start time and min CTS in segment (not just first CTS in segment)
7801 :
7802 : //send packet
7803 122802 : gf_filter_pck_send(dst);
7804 :
7805 122802 : if (ctx->update_report>=0)
7806 21539 : ctx->update_report++;
7807 :
7808 122802 : if (ds->dyn_bitrate) {
7809 : u32 dsize;
7810 26262 : u64 dts = gf_filter_pck_get_dts(pck);
7811 26262 : gf_filter_pck_get_data(pck, &dsize);
7812 26262 : if (!ds->rate_first_dts_plus_one)
7813 60 : ds->rate_first_dts_plus_one = 1 + dts;
7814 26262 : ds->rate_last_dts = dts;
7815 26262 : ds->rate_media_size += dsize;
7816 : }
7817 :
7818 : //drop packet if not splitting
7819 122802 : if (!ds->split_dur_next)
7820 122776 : dasher_drop_input(ctx, ds, GF_FALSE);
7821 :
7822 : }
7823 : }
7824 : nb_init = 0;
7825 5817942 : for (i=0; i<count; i++) {
7826 5817942 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
7827 : //if (ds->muxed_base) ds = ds->muxed_base;
7828 5817942 : if (ds->done || ds->subdur_done) nb_init++;
7829 5726104 : else if (ds->seg_done && ctx->force_period_switch) nb_init++;
7830 5726104 : else if (ds->seg_done && ds->muxed_base && ds->muxed_base->done) {
7831 0 : nb_init++;
7832 0 : ds->done = 1;
7833 : }
7834 : }
7835 :
7836 5720281 : if (nb_reg_done && (nb_reg_done == count)) {
7837 0 : ctx->mpd->gpac_mpd_time = 0;
7838 : }
7839 :
7840 5720281 : dasher_format_report(filter, ctx);
7841 :
7842 5720281 : if (seg_done) {
7843 : Bool update_period = GF_FALSE;
7844 : Bool update_manifest = GF_FALSE;
7845 2142 : if (ctx->purge_segments) update_period = GF_TRUE;
7846 2142 : if (ctx->mpd) {
7847 : //segment timeline used, always update manifest
7848 2142 : if (ctx->stl)
7849 : update_manifest = GF_TRUE;
7850 2138 : else if (ctx->dmode==GF_DASH_DYNAMIC) {
7851 : //publish time not set, we never send the manifest, do it
7852 128 : if (!ctx->mpd->publishTime) {
7853 : update_manifest = GF_TRUE;
7854 : }
7855 : //whenever we have a new seg in HLS, push new manifest
7856 122 : else if (ctx->do_m3u8) {
7857 : update_manifest = GF_TRUE;
7858 : }
7859 : //we have a minimum ipdate period
7860 60 : else if (ctx->mpd->minimum_update_period) {
7861 60 : u64 diff = dasher_get_utc(ctx) - ctx->mpd->publishTime;
7862 60 : if (diff >= ctx->mpd->minimum_update_period)
7863 : update_manifest = GF_TRUE;
7864 : }
7865 : }
7866 2142 : if (update_period)
7867 95 : dasher_update_period_duration(ctx, GF_FALSE);
7868 :
7869 2142 : if (update_manifest)
7870 85 : dasher_send_manifest(filter, ctx, GF_FALSE);
7871 : }
7872 5718139 : } else if (ctx->force_hls_ll_manifest) {
7873 279 : ctx->force_hls_ll_manifest = GF_FALSE;
7874 279 : dasher_send_manifest(filter, ctx, GF_FALSE);
7875 : }
7876 :
7877 : //still some running streams in period
7878 5720281 : if (count && (nb_init<count)) {
7879 5662697 : gf_filter_post_process_task(filter);
7880 5662697 : return GF_OK;
7881 : }
7882 :
7883 : //in subdur mode once we are done, flush output pids and discard all input packets
7884 : //this is done at the end to be able to resume dashing when loop is requested
7885 57584 : if (ctx->subdur) {
7886 139 : for (i=0; i<count; i++) {
7887 : GF_FilterPacket *eods_pck;
7888 139 : GF_DashStream *ds = gf_list_get(ctx->current_period->streams, i);
7889 139 : if (ds->done) continue;
7890 30 : eods_pck = gf_filter_pck_new_alloc(ds->opid, 0, NULL);
7891 30 : if (!eods_pck) return GF_OUT_OF_MEM;
7892 30 : ds->done = 2;
7893 30 : ds->subdur_done = GF_TRUE;
7894 30 : gf_filter_pck_set_property(eods_pck, GF_PROP_PCK_EODS, &PROP_BOOL(GF_TRUE) );
7895 30 : gf_filter_pck_send(eods_pck);
7896 :
7897 30 : dasher_drop_input(ctx, ds, GF_TRUE);
7898 : }
7899 : }
7900 :
7901 : //we need to wait for full flush of packets before switching periods in order to get the
7902 : //proper segment size for segment_list+byte_range mode
7903 57584 : if (ctx->nb_seg_url_pending) {
7904 : u64 diff;
7905 57042 : if (!ctx->last_evt_check_time) ctx->last_evt_check_time = gf_sys_clock_high_res();
7906 :
7907 57042 : diff = gf_sys_clock_high_res() - ctx->last_evt_check_time;
7908 57042 : if (diff < 10000000) {
7909 57042 : gf_filter_post_process_task(filter);
7910 57042 : return GF_OK;
7911 : }
7912 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] timeout %d segment info still pending but no event from muxer after "LLU" us, aborting\n", ctx->nb_seg_url_pending, diff));
7913 0 : ctx->nb_seg_url_pending = 0;
7914 0 : return GF_SERVICE_ERROR;
7915 : }
7916 542 : if (ctx->sseg && !ctx->on_demand_done && !ctx->sigfrag) {
7917 : return GF_OK;
7918 : }
7919 511 : ctx->force_period_switch = GF_FALSE;
7920 : //done with this period, do period switch - this will update the MPD if needed
7921 511 : e = dasher_switch_period(filter, ctx);
7922 : //no more periods
7923 511 : if (e==GF_EOS) {
7924 255 : if (!ctx->is_eos) {
7925 255 : ctx->is_eos = GF_TRUE;
7926 255 : gf_filter_pid_set_eos(ctx->opid);
7927 : }
7928 : }
7929 : return e;
7930 : }
7931 :
7932 :
7933 :
7934 17 : static void dasher_resume_subdur(GF_Filter *filter, GF_DasherCtx *ctx)
7935 : {
7936 : GF_FilterEvent evt;
7937 : u32 i, count;
7938 17 : Bool is_last = (ctx->dmode == GF_MPD_TYPE_DYNAMIC_LAST) ? GF_TRUE : GF_FALSE;
7939 17 : if (!ctx->state) return;
7940 :
7941 17 : count = gf_list_count(ctx->pids);
7942 48 : for (i=0; i<count; i++) {
7943 31 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
7944 31 : ds->rep = NULL;
7945 31 : if ((ds->done==1) && !ctx->subdur && ctx->loop) {}
7946 31 : else if (ds->reschedule) {
7947 : //we possibly dispatched end of stream on all outputs, we need to force unblockink to get called again
7948 6 : gf_filter_pid_discard_block(ds->opid);
7949 6 : continue;
7950 : }
7951 25 : else if (ds->done != 2) continue;
7952 :
7953 25 : if (is_last) continue;
7954 :
7955 12 : gf_filter_pid_set_discard(ds->ipid, GF_FALSE);
7956 :
7957 : //send stop and play
7958 12 : GF_FEVT_INIT(evt, GF_FEVT_STOP, ds->ipid);
7959 12 : gf_filter_pid_send_event(ds->ipid, &evt);
7960 :
7961 12 : dasher_send_encode_hints(ctx, ds);
7962 12 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, ds->ipid);
7963 12 : evt.play.speed = 1.0;
7964 12 : if (!ctx->subdur || !ctx->loop) {
7965 2 : ds->seek_to_pck = 0;
7966 : } else {
7967 : //request start after the last packet we processed
7968 10 : evt.play.from_pck = (u32) ds->seek_to_pck+1;
7969 : }
7970 12 : gf_filter_pid_send_event(ds->ipid, &evt);
7971 :
7972 : //full stream looping
7973 12 : if (ds->subdur_done && !ctx->subdur) {
7974 0 : ds->loop_state = 0;
7975 : //mark as subdur done to force a context reload through period switch
7976 0 : ds->done = 2;
7977 0 : ds->seg_done = GF_FALSE;
7978 0 : ds->subdur_done = GF_FALSE;
7979 : }
7980 : }
7981 :
7982 17 : ctx->subdur_done = GF_FALSE;
7983 17 : ctx->is_eos = GF_FALSE;
7984 17 : if (!ctx->post_play_events && !is_last) {
7985 7 : ctx->current_period->period = NULL;
7986 7 : ctx->first_context_load = GF_TRUE;
7987 7 : ctx->post_play_events = GF_TRUE;
7988 : }
7989 17 : gf_filter_post_process_task(filter);
7990 : }
7991 :
7992 330 : static void dasher_process_hls_ll(GF_DasherCtx *ctx, const GF_FilterEvent *evt)
7993 : {
7994 330 : u32 i, count = gf_list_count(ctx->pids);
7995 : GF_DASH_SegmentContext *sctx;
7996 : GF_DashStream *ds = NULL;
7997 :
7998 330 : if (ctx->forward_mode)
7999 : return;
8000 :
8001 330 : if (!ctx->store_seg_states) {
8002 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Received fragment size info event but no associated segment state\n"));
8003 : return;
8004 : }
8005 90 : for (i=0; i<count; i++) {
8006 420 : ds = gf_list_get(ctx->pids, i);
8007 420 : if (ds->opid == evt->base.on_pid) break;
8008 : ds = NULL;
8009 : }
8010 330 : if (!ds) {
8011 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Received fragment size info event but no associated pid\n"));
8012 : return;
8013 : }
8014 330 : if (ds->muxed_base)
8015 : ds = ds->muxed_base;
8016 :
8017 330 : sctx = gf_list_get(ds->pending_segment_states, 0);
8018 330 : if (!sctx || !ctx->nb_seg_url_pending) {
8019 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Received segment size info event but no pending segments\n"));
8020 : return;
8021 : }
8022 330 : sctx->frags = gf_realloc(sctx->frags, sizeof (GF_DASH_FragmentContext) * (sctx->nb_frags+1));
8023 330 : if (!sctx->frags) {
8024 0 : sctx->nb_frags = 0;
8025 0 : return;
8026 : }
8027 330 : sctx->frags[sctx->nb_frags].size = evt->frag_size.size;
8028 330 : sctx->frags[sctx->nb_frags].offset = evt->frag_size.offset;
8029 330 : if (evt->frag_size.duration.den) {
8030 330 : sctx->frags[sctx->nb_frags].duration = (u32) ((u64) evt->frag_size.duration.num * ds->rep->timescale / evt->frag_size.duration.den);
8031 : } else {
8032 0 : sctx->frags[sctx->nb_frags].duration = 0;
8033 : }
8034 330 : sctx->frags[sctx->nb_frags].independent = evt->frag_size.independent;
8035 330 : sctx->nb_frags++;
8036 330 : if (evt->frag_size.is_last) {
8037 51 : sctx->llhls_done = GF_TRUE;
8038 : } else {
8039 279 : ctx->force_hls_ll_manifest = GF_TRUE;
8040 : }
8041 : }
8042 :
8043 27868 : static Bool dasher_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
8044 : {
8045 : u32 i, count;
8046 : Bool flush_mpd = GF_FALSE;
8047 27868 : GF_DasherCtx *ctx = gf_filter_get_udta(filter);
8048 :
8049 27868 : ctx->last_evt_check_time = 0;
8050 :
8051 27868 : if (evt->base.type == GF_FEVT_RESUME) {
8052 : //only process resume event when coming from main output PID, but always cancel it
8053 : //this is needed in case the output filter where the resume event was initiated consumes both
8054 : //manifest and segment PIDs, as is the case with httpout
8055 20 : if (evt->base.on_pid == ctx->opid)
8056 17 : dasher_resume_subdur(filter, ctx);
8057 : return GF_TRUE;
8058 : }
8059 27848 : if (evt->base.type == GF_FEVT_CONNECT_FAIL) {
8060 0 : ctx->in_error = GF_TRUE;
8061 0 : gf_filter_pid_set_eos(ctx->opid);
8062 0 : if (ctx->opid_alt)
8063 0 : gf_filter_pid_set_eos(ctx->opid_alt);
8064 : return GF_TRUE;
8065 : }
8066 :
8067 27848 : if (evt->base.type == GF_FEVT_PLAY) {
8068 599 : ctx->is_playing = GF_TRUE;
8069 599 : if (!ctx->sfile && !ctx->stl && !ctx->use_cues) {
8070 : GF_FilterEvent anevt;
8071 452 : GF_FEVT_INIT(anevt, GF_FEVT_ENCODE_HINTS, NULL)
8072 452 : count = gf_list_count(ctx->pids);
8073 1654 : for (i=0; i<count; i++) {
8074 1202 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
8075 1202 : anevt.base.on_pid = ds->ipid;
8076 1202 : anevt.encode_hints.intra_period = ds->dash_dur;
8077 1202 : gf_filter_pid_send_event(ds->ipid, &anevt);
8078 : }
8079 : }
8080 : return GF_FALSE;
8081 : }
8082 27249 : if (evt->base.type == GF_FEVT_STOP) {
8083 0 : ctx->is_playing = GF_FALSE;
8084 0 : return GF_FALSE;
8085 : }
8086 :
8087 27249 : if (evt->base.type == GF_FEVT_FRAGMENT_SIZE) {
8088 330 : dasher_process_hls_ll(ctx, evt);
8089 330 : return GF_TRUE;
8090 : }
8091 26919 : if (evt->base.type != GF_FEVT_SEGMENT_SIZE) return GF_FALSE;
8092 :
8093 3567 : if (ctx->forward_mode==DASHER_FWD_ALL)
8094 : return GF_TRUE;
8095 :
8096 3541 : count = gf_list_count(ctx->pids);
8097 25040 : for (i=0; i<count; i++) {
8098 : u64 r_start, r_end;
8099 21499 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
8100 21499 : if (ds->opid != evt->base.on_pid) continue;
8101 :
8102 3540 : if (ds->muxed_base)
8103 : ds = ds->muxed_base;
8104 :
8105 3540 : if (ctx->store_seg_states && !evt->seg_size.is_init) {
8106 504 : GF_DASH_SegmentContext *sctx = gf_list_pop_front(ds->pending_segment_states);
8107 504 : if (!sctx || !ctx->nb_seg_url_pending) {
8108 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Broken muxer, received segment size info event but no pending segments\n"));
8109 : return GF_TRUE;
8110 : }
8111 : assert(sctx);
8112 : assert(ctx->nb_seg_url_pending);
8113 504 : ctx->nb_seg_url_pending--;
8114 504 : sctx->file_size = 1 + (u32) (evt->seg_size.media_range_end - evt->seg_size.media_range_start);
8115 504 : sctx->file_offset = evt->seg_size.media_range_start;
8116 504 : sctx->index_size = 1 + (u32) (evt->seg_size.idx_range_end - evt->seg_size.idx_range_start);
8117 504 : sctx->index_offset = evt->seg_size.idx_range_start;
8118 :
8119 504 : if (sctx->llhls_mode) {
8120 51 : sctx->llhls_done = GF_TRUE;
8121 : //reset frags of past segments
8122 51 : s32 idx, reset_until = gf_list_find(ds->rep->state_seg_list, sctx);
8123 63 : for (idx=reset_until-4; idx>=0; idx--) {
8124 18 : GF_DASH_SegmentContext *prev_sctx = gf_list_get(ds->rep->state_seg_list, idx);
8125 18 : if (!prev_sctx->llhls_mode)
8126 : break;
8127 :
8128 : //send file delete events
8129 12 : if (prev_sctx->llhls_mode>1) {
8130 : u32 k;
8131 24 : for (k=0; k<prev_sctx->nb_frags; k++) {
8132 : GF_FilterEvent anevt;
8133 : char szPath[GF_MAX_PATH];
8134 20 : sprintf(szPath, "%s.%d", prev_sctx->filepath, k+1);
8135 20 : GF_FEVT_INIT(anevt, GF_FEVT_FILE_DELETE, ds->opid);
8136 20 : anevt.file_del.url = szPath;
8137 20 : gf_filter_pid_send_event(ds->opid, &anevt);
8138 : }
8139 : }
8140 12 : prev_sctx->llhls_mode = 0;
8141 : }
8142 : }
8143 : }
8144 :
8145 : //in state mode we store everything
8146 : //don't set segment sizes in template mode
8147 3540 : if (ctx->tpl) continue;
8148 : //only set size/index size for init segment when doing onDemand/single index
8149 719 : if (ctx->sseg && !evt->seg_size.is_init) continue;
8150 :
8151 410 : if (evt->seg_size.media_range_end) {
8152 387 : r_start = evt->seg_size.media_range_start;
8153 : r_end = evt->seg_size.media_range_end;
8154 : } else {
8155 23 : r_start = evt->seg_size.idx_range_start;
8156 23 : r_end = evt->seg_size.idx_range_end;
8157 : }
8158 : //init segment or representation index, set it in on demand and main single source
8159 454 : if ((ctx->sfile || ctx->sseg) && (evt->seg_size.is_init==1)) {
8160 : GF_MPD_URL *url, **s_url;
8161 :
8162 66 : if (ds->rep->segment_list) {
8163 29 : if (!evt->seg_size.media_range_start && !evt->seg_size.media_range_end) {
8164 2 : if (ds->rep->segment_list->initialization_segment) {
8165 2 : gf_mpd_url_free(ds->rep->segment_list->initialization_segment);
8166 2 : ds->rep->segment_list->initialization_segment = NULL;
8167 : }
8168 2 : continue;
8169 : }
8170 : }
8171 :
8172 64 : if (ds->rep->segment_base && !evt->seg_size.media_range_end) {
8173 20 : if (! ds->rep->segment_base->index_range) {
8174 20 : GF_SAFEALLOC(ds->rep->segment_base->index_range, GF_MPD_ByteRange);
8175 : }
8176 20 : if (ds->rep->segment_base->index_range) {
8177 20 : ds->rep->segment_base->index_range->start_range = r_start;
8178 20 : ds->rep->segment_base->index_range->end_range = r_end;
8179 20 : ds->rep->segment_base->index_range_exact = GF_TRUE;
8180 : }
8181 : flush_mpd = GF_TRUE;
8182 20 : continue;
8183 : }
8184 :
8185 44 : GF_SAFEALLOC(url, GF_MPD_URL);
8186 44 : if (!url) return GF_TRUE;
8187 :
8188 44 : GF_SAFEALLOC(url->byte_range, GF_MPD_ByteRange);
8189 44 : if (!url->byte_range) return GF_TRUE;
8190 44 : url->byte_range->start_range = r_start;
8191 44 : url->byte_range->end_range = r_end;
8192 :
8193 : s_url = NULL;
8194 44 : if (ds->rep->segment_base) {
8195 17 : if (evt->seg_size.media_range_end) s_url = &ds->rep->segment_base->initialization_segment;
8196 : } else {
8197 : assert(ds->rep->segment_list);
8198 27 : if (evt->seg_size.media_range_end) s_url = &ds->rep->segment_list->initialization_segment;
8199 0 : else s_url = &ds->rep->segment_list->representation_index;
8200 : }
8201 : assert(s_url);
8202 44 : if (*s_url) gf_mpd_url_free(*s_url);
8203 44 : *s_url = url;
8204 344 : } else if (ds->rep->segment_list && !evt->seg_size.is_init) {
8205 342 : GF_MPD_SegmentURL *url = gf_list_pop_front(ds->pending_segment_urls);
8206 342 : if (!url || !ctx->nb_seg_url_pending) {
8207 0 : if (!ds->done) {
8208 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[Dasher] Broken muxer, received segment size info event but no pending segments\n"));
8209 : }
8210 : return GF_TRUE;
8211 : }
8212 342 : ctx->nb_seg_url_pending--;
8213 :
8214 342 : if (!url->media && ctx->sfile) {
8215 301 : GF_SAFEALLOC(url->media_range, GF_MPD_ByteRange);
8216 301 : if (url->media_range) {
8217 301 : url->media_range->start_range = evt->seg_size.media_range_start;
8218 301 : url->media_range->end_range = evt->seg_size.media_range_end;
8219 : }
8220 : }
8221 : //patch in test mode, old arch was not generating the index size for segment lists
8222 342 : if (evt->seg_size.idx_range_end && (!gf_sys_old_arch_compat() || ctx->sfile) ) {
8223 263 : GF_SAFEALLOC(url->index_range, GF_MPD_ByteRange);
8224 263 : if (url->index_range) {
8225 263 : url->index_range->start_range = evt->seg_size.idx_range_start;
8226 263 : url->index_range->end_range = evt->seg_size.idx_range_end;
8227 : }
8228 : }
8229 : }
8230 : }
8231 3541 : if (!ctx->sseg || !flush_mpd) return GF_TRUE;
8232 :
8233 : flush_mpd = GF_TRUE;
8234 23 : for (i=0; i<count; i++) {
8235 26 : GF_DashStream *ds = gf_list_get(ctx->pids, i);
8236 26 : if (!ds->rep) continue;
8237 25 : if (! ds->rep->segment_base) continue;
8238 25 : if (ds->rep->segment_base->index_range) continue;
8239 : flush_mpd = GF_FALSE;
8240 : break;
8241 : }
8242 20 : if (flush_mpd) {
8243 17 : ctx->on_demand_done = GF_TRUE;
8244 17 : gf_filter_post_process_task(filter);
8245 : }
8246 : return GF_TRUE;
8247 : }
8248 :
8249 247 : static GF_Err dasher_setup_profile(GF_DasherCtx *ctx)
8250 : {
8251 247 : switch (ctx->profile) {
8252 5 : case GF_DASH_PROFILE_AVC264_LIVE:
8253 : case GF_DASH_PROFILE_AVC264_ONDEMAND:
8254 : case GF_DASH_PROFILE_DASHIF_LL:
8255 5 : if (ctx->cp == GF_DASH_CPMODE_REPRESENTATION) {
8256 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] ERROR! The selected DASH profile (DASH-IF IOP) requires the ContentProtection element to be present in the AdaptationSet element, updating.\n"));
8257 0 : ctx->cp = GF_DASH_CPMODE_ADAPTATION_SET;
8258 : }
8259 : default:
8260 : break;
8261 : }
8262 247 : if (ctx->m2ts) {
8263 5 : switch (ctx->profile) {
8264 0 : case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
8265 : case GF_DASH_PROFILE_AVC264_LIVE:
8266 : case GF_DASH_PROFILE_DASHIF_LL:
8267 0 : ctx->profile = GF_DASH_PROFILE_LIVE;
8268 0 : break;
8269 1 : case GF_DASH_PROFILE_ONDEMAND:
8270 : case GF_DASH_PROFILE_AVC264_ONDEMAND:
8271 1 : ctx->profile = GF_DASH_PROFILE_ONDEMAND;
8272 1 : break;
8273 : }
8274 : }
8275 :
8276 : /*adjust params based on profiles*/
8277 247 : switch (ctx->profile) {
8278 146 : case GF_DASH_PROFILE_LIVE:
8279 146 : ctx->sseg = ctx->sfile = GF_FALSE;
8280 146 : ctx->tpl = ctx->align = ctx->sap = GF_TRUE;
8281 146 : break;
8282 0 : case GF_DASH_PROFILE_HBBTV_1_5_ISOBMF_LIVE:
8283 0 : ctx->check_main_role = GF_TRUE;
8284 0 : ctx->bs_switch = DASHER_BS_SWITCH_MULTI;
8285 : //FALLTHROUGH
8286 3 : case GF_DASH_PROFILE_AVC264_LIVE:
8287 3 : ctx->sseg = ctx->sfile = GF_FALSE;
8288 3 : ctx->no_fragments_defaults = ctx->align = ctx->tpl = ctx->sap = GF_TRUE;
8289 3 : break;
8290 2 : case GF_DASH_PROFILE_AVC264_ONDEMAND:
8291 2 : ctx->tpl = GF_FALSE;
8292 2 : ctx->no_fragments_defaults = ctx->align = ctx->sseg = ctx->sap = GF_TRUE;
8293 2 : break;
8294 17 : case GF_DASH_PROFILE_ONDEMAND:
8295 17 : ctx->sseg = ctx->align = ctx->sap = ctx->sfile = GF_TRUE;
8296 17 : ctx->tpl = GF_FALSE;
8297 :
8298 17 : if (ctx->m2ts) {
8299 1 : ctx->sseg = GF_FALSE;
8300 1 : ctx->tpl = GF_TRUE;
8301 1 : ctx->profile = GF_DASH_PROFILE_MAIN;
8302 : } else {
8303 16 : if ((ctx->bs_switch != DASHER_BS_SWITCH_DEF) && (ctx->bs_switch != DASHER_BS_SWITCH_OFF)) {
8304 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] onDemand profile, bitstream switching mode cannot be used, defaulting to off.\n"));
8305 : }
8306 : }
8307 : /*BS switching is meaningless in onDemand profile*/
8308 17 : ctx->bs_switch = DASHER_BS_SWITCH_OFF;
8309 17 : break;
8310 10 : case GF_DASH_PROFILE_MAIN:
8311 10 : ctx->align = ctx->sap = GF_TRUE;
8312 10 : ctx->sseg = ctx->tpl = GF_FALSE;
8313 10 : break;
8314 0 : case GF_DASH_PROFILE_DASHIF_LL:
8315 0 : ctx->sseg = ctx->sfile = GF_FALSE;
8316 0 : ctx->no_fragments_defaults = ctx->align = ctx->tpl = ctx->sap = GF_TRUE;
8317 0 : if (!ctx->utcs) {
8318 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] DASH-IF LL requires UTCTiming but none specified, using http://time.akamai.com/?iso \n"));
8319 0 : ctx->utcs = gf_strdup("http://time.akamai.com/?iso");
8320 : }
8321 : break;
8322 : default:
8323 : break;
8324 : }
8325 :
8326 247 : if (ctx->sseg)
8327 18 : ctx->tpl = GF_FALSE;
8328 :
8329 247 : if (ctx->bs_switch == DASHER_BS_SWITCH_DEF) {
8330 224 : ctx->bs_switch = DASHER_BS_SWITCH_ON;
8331 : }
8332 :
8333 247 : if (ctx->cmaf) {
8334 0 : ctx->align = GF_TRUE;
8335 0 : ctx->sap = GF_TRUE;
8336 : }
8337 :
8338 247 : if (! ctx->align) {
8339 0 : if (ctx->profile != GF_DASH_PROFILE_FULL) {
8340 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not time-aligned in each representation of each period\n\tswitching to FULL profile\n"));
8341 0 : ctx->profile = GF_DASH_PROFILE_FULL;
8342 : }
8343 : //commented out, this does not seem correct since BS switching is orthogonal to segment alignment
8344 : //one could have inband params working even in non time-aligned setup
8345 : #if 0
8346 : if (ctx->bs_switch != DASHER_BS_SWITCH_OFF) {
8347 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Segments are not time-aligned in each representation of each period\n\tdisabling bitstream switching\n"));
8348 : ctx->bs_switch = DASHER_BS_SWITCH_OFF;
8349 : }
8350 : #endif
8351 :
8352 : }
8353 :
8354 : //check we have a segment template
8355 247 : if (!ctx->template) {
8356 235 : if (!ctx->sigfrag) {
8357 227 : ctx->template = gf_strdup( ctx->sfile ? "$File$$FS$_dash" : (ctx->stl ? "$File$_dash$FS$$Time$" : "$File$_dash$FS$$Number$") );
8358 227 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[Dasher] No template assigned, using %s\n", ctx->template));
8359 : }
8360 :
8361 235 : if (ctx->profile == GF_DASH_PROFILE_FULL) {
8362 19 : ctx->sfile = GF_TRUE;
8363 : }
8364 : }
8365 : //backward compatibility with old arch using %s
8366 : else {
8367 12 : char *sep = strstr(ctx->template, "%s");
8368 12 : if (sep) {
8369 0 : char *new_template = NULL;
8370 0 : sep[0] = 0;
8371 0 : gf_dynstrcat(&new_template, ctx->template, NULL);
8372 0 : gf_dynstrcat(&new_template, "$File$", NULL);
8373 0 : gf_dynstrcat(&new_template, sep+2, NULL);
8374 0 : gf_free(ctx->template);
8375 0 : ctx->template = new_template;
8376 : }
8377 :
8378 : }
8379 247 : return GF_OK;
8380 : }
8381 :
8382 244 : static GF_Err dasher_initialize(GF_Filter *filter)
8383 : {
8384 : GF_Err e;
8385 244 : GF_DasherCtx *ctx = gf_filter_get_udta(filter);
8386 244 : gf_filter_set_max_extra_input_pids(filter, -1);
8387 :
8388 244 : ctx->pids = gf_list_new();
8389 244 : ctx->postponed_pids = gf_list_new();
8390 244 : ctx->tpl_records = gf_list_new();
8391 :
8392 244 : if (!ctx->initext && (ctx->muxtype==DASHER_MUX_AUTO))
8393 234 : ctx->muxtype = DASHER_MUX_ISOM;
8394 :
8395 244 : if ((ctx->segdur.num <= 0) || !ctx->segdur.den) {
8396 54 : ctx->segdur.num = 1;
8397 54 : ctx->segdur.den = 1;
8398 54 : ctx->no_seg_dur = GF_TRUE;
8399 : }
8400 :
8401 244 : e = dasher_setup_profile(ctx);
8402 244 : if (e) return e;
8403 :
8404 244 : if (ctx->sfile && ctx->tpl)
8405 21 : ctx->tpl = GF_FALSE;
8406 :
8407 244 : ctx->current_period = dasher_new_period();
8408 244 : ctx->next_period = dasher_new_period();
8409 244 : ctx->on_demand_done = GF_TRUE;
8410 :
8411 244 : if (ctx->state) {
8412 14 : ctx->first_context_load = GF_TRUE;
8413 : }
8414 244 : if (ctx->subdur && !ctx->state) {
8415 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] subdur mode specified but no context set, will only dash %g seconds of media\n", ctx->subdur));
8416 : }
8417 : //we build manifest from input frag/seg, always use single frag
8418 244 : if (ctx->sigfrag) {
8419 6 : if (ctx->tpl) {
8420 4 : if (!ctx->template) {
8421 3 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] Warning, manifest generation only mode requested for live-based profile but no template provided, switching to main profile.\n"));
8422 3 : ctx->profile = GF_DASH_PROFILE_MAIN;
8423 3 : ctx->tpl = GF_FALSE;
8424 3 : dasher_setup_profile(ctx);
8425 : //we force single file in this mode, but we will replace byte ranges by source URL
8426 3 : ctx->sfile = GF_TRUE;
8427 : } else {
8428 1 : ctx->sseg = GF_FALSE;
8429 1 : ctx->sfile = GF_FALSE;
8430 : }
8431 : } else {
8432 2 : if (!ctx->sseg)
8433 1 : ctx->sfile = GF_TRUE;
8434 : }
8435 : }
8436 :
8437 244 : if (!ctx->sap || ctx->sigfrag || ctx->cues)
8438 19 : ctx->sbound = DASHER_BOUNDS_OUT;
8439 :
8440 244 : if ((ctx->tsb>=0) && (ctx->dmode!=GF_DASH_STATIC))
8441 20 : ctx->purge_segments = GF_TRUE;
8442 :
8443 244 : if (ctx->state && ctx->sreg) {
8444 : u32 diff;
8445 : u64 next_gen_ntp;
8446 : GF_Err dash_state_check_timing(const char *dash_state, u64 *next_gen_ntp_ms, u32 *next_time_ms);
8447 :
8448 0 : e = dash_state_check_timing(ctx->state, &next_gen_ntp, &diff);
8449 0 : if (e<0) return e;
8450 0 : if (e==GF_EOS) {
8451 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[Dasher] generation called too early by %d ms\n", (s32) diff));
8452 : return e;
8453 : }
8454 : }
8455 : return GF_OK;
8456 : }
8457 :
8458 :
8459 244 : static void dasher_finalize(GF_Filter *filter)
8460 : {
8461 244 : GF_DasherCtx *ctx = gf_filter_get_udta(filter);
8462 :
8463 845 : while (gf_list_count(ctx->pids)) {
8464 357 : GF_DashStream *ds = gf_list_pop_back(ctx->pids);
8465 357 : dasher_reset_stream(filter, ds, GF_TRUE);
8466 357 : if (ds->packet_queue) gf_list_del(ds->packet_queue);
8467 357 : if (ds->cinfo) gf_crypt_info_del(ds->cinfo);
8468 357 : gf_free(ds);
8469 : }
8470 244 : gf_list_del(ctx->pids);
8471 244 : if (ctx->mpd) gf_mpd_del(ctx->mpd);
8472 :
8473 590 : while (gf_list_count(ctx->tpl_records)) {
8474 346 : DashTemplateRecord *tr = gf_list_pop_back(ctx->tpl_records);
8475 346 : gf_free(tr->tpl);
8476 346 : gf_free(tr);
8477 : }
8478 244 : gf_list_del(ctx->tpl_records);
8479 :
8480 244 : if (ctx->next_period->period) gf_mpd_period_free(ctx->next_period->period);
8481 244 : gf_list_del(ctx->current_period->streams);
8482 244 : gf_free(ctx->current_period);
8483 244 : gf_list_del(ctx->next_period->streams);
8484 244 : gf_free(ctx->next_period);
8485 244 : if (ctx->out_path) gf_free(ctx->out_path);
8486 244 : gf_list_del(ctx->postponed_pids);
8487 244 : if (ctx->cinfo) gf_crypt_info_del(ctx->cinfo);
8488 244 : }
8489 :
8490 : #define MPD_EXTS "mpd|m3u8|3gm|ism"
8491 : #define MPD_MIMES "application/dash+xml|video/vnd.3gpp.mpd|audio/vnd.3gpp.mpd|video/vnd.mpeg.dash.mpd|audio/vnd.mpeg.dash.mpd|audio/mpegurl|video/mpegurl|application/vnd.ms-sstr+xml"
8492 :
8493 : static const GF_FilterCapability DasherCaps[] =
8494 : {
8495 : //we accept files as input, but only for NULL file (no source)
8496 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8497 : //only with no source
8498 : CAP_STRING(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_URL, "*"),
8499 : CAP_STRING(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_FILEPATH, "*"),
8500 :
8501 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8502 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, MPD_EXTS),
8503 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, MPD_MIMES),
8504 : {0},
8505 : //anything AV pid framed result in manifest PID
8506 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
8507 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
8508 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
8509 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
8510 :
8511 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8512 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, MPD_EXTS),
8513 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, MPD_MIMES),
8514 : {0},
8515 : //anything else (not file, not AV and framed) in compressed format result in manifest PID
8516 : //we cannot handle RAW format for such streams as these are in-memory data (scene graph, decoded text, etc ..)
8517 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8518 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
8519 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
8520 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
8521 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
8522 :
8523 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8524 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, MPD_EXTS),
8525 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, MPD_MIMES),
8526 : {0},
8527 : //anything else (not file and framed) result in media pids not file
8528 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8529 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_CODECID, GF_CODECID_NONE),
8530 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_UNFRAMED, GF_TRUE),
8531 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
8532 :
8533 : };
8534 :
8535 :
8536 : #define OFFS(_n) #_n, offsetof(GF_DasherCtx, _n)
8537 : static const GF_FilterArgs DasherArgs[] =
8538 : {
8539 : { OFFS(segdur), "target segment duration in seconds. A value less than or equal to 0 means to 1.0 second", GF_PROP_FRACTION, "0/0", NULL, 0},
8540 : { OFFS(tpl), "use template mode (multiple segment, template URLs)", GF_PROP_BOOL, "true", NULL, 0},
8541 : { OFFS(stl), "use segment timeline (ignored in on_demand mode)", GF_PROP_BOOL, "false", NULL, 0},
8542 : { OFFS(dmode), "dash content mode\n"
8543 : "- static: static content\n"
8544 : "- dynamic: live generation\n"
8545 : "- dynlast: last call for live, will turn the MPD into static"
8546 : "", GF_PROP_UINT, "static", "static|dynamic|dynlast", GF_FS_ARG_UPDATE},
8547 : { OFFS(sseg), "single segment is used", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
8548 : { OFFS(sfile), "use a single file for all segments (default in on_demand)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
8549 : { OFFS(align), "enable segment time alignment between representations", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
8550 : { OFFS(sap), "enable splitting segments at SAP boundaries", GF_PROP_BOOL, "true", NULL, 0},
8551 : { OFFS(mix_codecs), "enable mixing different codecs in an adaptation set", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8552 : { OFFS(ntp), "insert/override NTP clock at the beginning of each segment\n"
8553 : "- rem: removes NTP from all input packets\n"
8554 : "- yes: inserts NTP at each segment start\n"
8555 : "- keep: leaves input packet NTP untouched", GF_PROP_UINT, "rem", "rem|yes|keep", GF_FS_ARG_HINT_ADVANCED},
8556 : { OFFS(no_sar), "do not check for identical sample aspect ratio for adaptation sets", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8557 : { OFFS(m2ts), "generate MPEG-2 TS output", GF_PROP_BOOL, "false", NULL, 0},
8558 : { OFFS(bs_switch), "bitstream switching mode (single init segment)\n"
8559 : "- def: resolves to off for onDemand and inband for live\n"
8560 : "- off: disables BS switching\n"
8561 : "- on: enables it if same decoder configuration is possible\n"
8562 : "- inband: moves decoder config inband if possible\n"
8563 : "- force: enables it even if only one representation\n"
8564 : "- multi: uses multiple stsd entries in ISOBMFF", GF_PROP_UINT, "def", "def|off|on|inband|force|multi", GF_FS_ARG_HINT_ADVANCED},
8565 : { OFFS(template), "template string to use to generate segment name - see filter help", GF_PROP_STRING, NULL, NULL, 0},
8566 : { OFFS(segext), "file extension to use for segments", GF_PROP_STRING, NULL, NULL, 0},
8567 : { OFFS(initext), "file extension to use for the init segment", GF_PROP_STRING, NULL, NULL, 0},
8568 : { OFFS(muxtype), "muxtype to use for the segments\n"
8569 : "- mp4: uses ISOBMFF format\n"
8570 : "- ts: uses MPEG-2 TS format\n"
8571 : "- mkv: uses Matroska format\n"
8572 : "- webm: uses WebM format\n"
8573 : "- ogg: uses OGG format\n"
8574 : "- raw: uses raw media format (disables muxed representations)\n"
8575 : "- auto: guess format based on extension, default to mp4 if no extension", GF_PROP_UINT, "auto", "mp4|ts|mkv|webm|ogg|raw|auto", 0},
8576 : { OFFS(asto), "availabilityStartTimeOffset to use in seconds. A negative value simply increases the AST, a positive value sets the ASToffset to representations", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
8577 : { OFFS(profile), "target DASH profile. This will set default option values to ensure conformance to the desired profile. For MPEG-2 TS, only main and live are used, others default to main\n"
8578 : "- auto: turns profile to live for dynamic and full for non-dynamic\n"
8579 : "- live: DASH live profile, using segment template\n"
8580 : "- onDemand: MPEG-DASH live profile\n"
8581 : "- main: MPEG-DASH main profile, using segment list\n"
8582 : "- full: MPEG-DASH full profile\n"
8583 : "- hbbtv1.5.live: HBBTV 1.5 DASH profile\n"
8584 : "- dashavc264.live: DASH-IF live profile\n"
8585 : "- dashavc264.onDemand: DASH-IF onDemand profile\n"
8586 : "- dashif.ll: DASH IF low-latency profile (set UTC server to time.akamai.com if none set)"
8587 : "", GF_PROP_UINT, "auto", "auto|live|onDemand|main|full|hbbtv1.5.live|dashavc264.live|dashavc264.onDemand|dashif.ll", 0 },
8588 : { OFFS(profX), "list of profile extensions, as used by DASH-IF and DVB. The string will be colon-concatenated with the profile used", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED },
8589 : { OFFS(cp), "content protection element location\n"
8590 : "- set: in adaptation set element\n"
8591 : "- rep: in representation element\n"
8592 : "- both: in both adaptation set and representation elements"
8593 : "", GF_PROP_UINT, "set", "set|rep|both", GF_FS_ARG_HINT_ADVANCED },
8594 : { OFFS(pssh), "storage mode for PSSH box\n"
8595 : "- f: stores in movie fragment only\n"
8596 : "- v: stores in movie only\n"
8597 : "- m: stores in mpd only\n"
8598 : "- mf: stores in mpd and movie fragment\n"
8599 : "- mv: stores in mpd and movie\n"
8600 : "- n: discard pssh from mpd and segments", GF_PROP_UINT, "v", "v|f|mv|mf|m|n", GF_FS_ARG_HINT_ADVANCED},
8601 : { OFFS(buf), "min buffer duration in ms. negative value means percent of segment duration (eg -150 = 1.5*seg_dur)", GF_PROP_SINT, "-100", NULL, 0},
8602 : { OFFS(timescale), "set timescale for timeline and segment list/template. A value of 0 picks up the first timescale of the first stream in an adaptation set. A negative value forces using stream timescales for each timed element (multiplication of segment list/template/timelines). A positive value enforces the MPD timescale", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
8603 : { OFFS(check_dur), "check duration of sources in period, trying to have roughly equal duration. Enforced whenever period start times are used", GF_PROP_BOOL, "true", NULL, 0},
8604 : { OFFS(skip_seg), "increment segment number whenever an empty segment would be produced - NOT DASH COMPLIANT", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8605 : { OFFS(title), "MPD title", GF_PROP_STRING, NULL, NULL, 0},
8606 : { OFFS(source), "MPD Source", GF_PROP_STRING, NULL, NULL, 0},
8607 : { OFFS(info), "MPD info url", GF_PROP_STRING, NULL, NULL, 0},
8608 : { OFFS(cprt), "MPD copyright string", GF_PROP_STRING, NULL, NULL, 0},
8609 : { OFFS(lang), "language of MPD Info", GF_PROP_STRING, NULL, NULL, 0},
8610 : { OFFS(location), "set MPD locations to given URL", GF_PROP_STRING_LIST, NULL, NULL, 0},
8611 : { OFFS(base), "set base URLs of MPD", GF_PROP_STRING_LIST, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
8612 : { OFFS(refresh), "refresh rate for dynamic manifests, in seconds. A negative value sets the MPD duration. If 0, uses dash duration", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
8613 : { OFFS(tsb), "time-shift buffer depth in seconds. A negative value means infinity", GF_PROP_DOUBLE, "30", NULL, 0},
8614 : { OFFS(subdur), "maximum duration of the input file to be segmented. This does not change the segment duration, segmentation stops once segments produced exceeded the duration", GF_PROP_DOUBLE, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
8615 : { OFFS(ast), "set start date (as xs:date, eg YYYY-MM-DDTHH:MM:SSZ) for live mode. Default is now. !! Do not use with multiple periods, nor when DASH duration is not a multiple of GOP size !!", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
8616 : { OFFS(state), "path to file used to store/reload state info when simulating live. This is stored as a valid MPD with GPAC XML extensions", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
8617 : { OFFS(loop), "loop sources when dashing with subdur and state. If not set, a new period is created once the sources are over", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
8618 : { OFFS(split), "enable cloning samples for text/metadata/scene description streams, marking further clones as redundant", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
8619 : { OFFS(hlsc), "insert clock reference in variant playlist in live HLS", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8620 : { OFFS(cues), "set cue file - see filter help", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
8621 : { OFFS(strict_cues), "strict mode for cues, complains if splitting is not on SAP type 1/2/3 or if unused cue is found", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8622 : { OFFS(strict_sap), "strict mode for sap\n"
8623 : "- off: ignore SAP types for PID other than video, enforcing _startsWithSAP=1_\n"
8624 : "- sig: same as [-off]() but keep _startsWithSAP_ to the true SAP value\n"
8625 : "- on: warn if any PID uses SAP 3 or 4 and switch to FULL profile", GF_PROP_UINT, "off", "off|sig|on", GF_FS_ARG_HINT_EXPERT},
8626 :
8627 : { OFFS(subs_sidx), "number of subsegments per sidx. negative value disables sidx. Only used to inherit sidx option of destination", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_EXPERT},
8628 : { OFFS(cmpd), "skip line feed and spaces in MPD XML for compactness", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8629 : { OFFS(styp), "indicate the 4CC to use for styp boxes when using ISOBMFF output", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
8630 : { OFFS(dual), "indicate to produce both MPD and M3U files", GF_PROP_BOOL, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
8631 : { OFFS(sigfrag), "use manifest generation only mode - see filter help", GF_PROP_BOOL, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
8632 : { OFFS(_p_gentime), "pointer to u64 holding the ntp clock in ms of next DASH generation in live mode", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
8633 : { OFFS(_p_mpdtime), "pointer to u64 holding the mpd time in ms of the last generated segment", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
8634 : { OFFS(sbound), "indicate how the theoretical segment start `TSS (= segment_number * duration)` should be handled\n"
8635 : "- out: segment split as soon as `TSS` is exceeded (`TSS` <= segment_start)\n"
8636 : "- closest: segment split at closest SAP to theoretical bound\n"
8637 : "- in: `TSS` is always in segment (`TSS` >= segment_start)", GF_PROP_UINT, "out", "out|closest|in", GF_FS_ARG_HINT_EXPERT},
8638 : { OFFS(reschedule), "reschedule sources with no period ID assigned once done (dynamic mode only)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8639 : { OFFS(sreg), "regulate the session\n"
8640 : "- when using subdur and context, only generate segments from the past up to live edge\n"
8641 : "- otherwise in dynamic mode without context, do not generate segments ahead of time", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8642 : { OFFS(scope_deps), "scope PID dependencies to be within source. If disabled, PID dependencies will be checked across all input PIDs regardless of their sources", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
8643 : { OFFS(utcs), "URL to use as time server / UTCTiming source. Special value `inband` enables inband UTC (same as publishTime), special prefix `xsd@` uses xsDateTime schemeURI rather than ISO", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
8644 : { OFFS(force_flush), "force generating a single segment for each input. This can be useful in batch mode when average source duration is known and used as segment duration but actual duration may sometimes be greater", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8645 : { OFFS(last_seg_merge), "force merging last segment if less than half the target duration", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
8646 : { OFFS(mha_compat), "adaptation set generation mode for compatible MPEG-H Audio profile\n"
8647 : "- no: only generate the adaptation set for the main profile\n"
8648 : "- comp: only generate the adaptation sets for all compatible profiles\n"
8649 : "- all: generate the adaptation set for the main profile and all compatible profiles"
8650 : , GF_PROP_UINT, "no", "no|comp|all", GF_FS_ARG_HINT_EXPERT},
8651 : { OFFS(mname), "output manifest name for ATSC3 muxing", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
8652 : { OFFS(llhls), "HLS low latency type\n"
8653 : "- off: do not use LL-HLS\n"
8654 : "- br: use LL-HLS with byte-range for segment parts, pointing to full segment (DASH-LL compatible)\n"
8655 : "- sf: use separated files for segment parts\n"
8656 : "- brsf: generate two sets of manifest, one for byte-range and one for files (`_IF` added before extension of manifest)", GF_PROP_UINT, "off", "off|br|sf|brsf", GF_FS_ARG_HINT_EXPERT},
8657 : { OFFS(cdur), "chunk duration for fragmentation modes", GF_PROP_FRACTION, "-1/1", NULL, GF_FS_ARG_HINT_HIDE},
8658 : { OFFS(hlsdrm), "cryp file info for HLS full segment encryption", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
8659 : { OFFS(cmaf), "use cmaf guidelines\n"
8660 : "- no: CMAF not enforced\n"
8661 : "- cmfc: use CMAF `cmfc` guidelines\n"
8662 : "- cmf2: use CMAF `cmf2` guidelines"
8663 : , GF_PROP_UINT, "no", "no|cmfc|cmf2", GF_FS_ARG_HINT_ADVANCED},
8664 : { OFFS(pswitch), "force period switch instead of absorbing PID reconfiguration (for splicing or add insertion not using periodID)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_HIDE},
8665 : {0}
8666 : };
8667 :
8668 :
8669 : GF_FilterRegister DasherRegister = {
8670 : .name = "dasher",
8671 : GF_FS_SET_DESCRIPTION("DASH and HLS segmenter")
8672 : GF_FS_SET_HELP(
8673 : "# GPAC DASH and HLS segmenter\n"
8674 : "This filter provides segmentation and manifest generation for MPEG-DASH and HLS formats.\n"
8675 : "The segmenter currently supports:\n"
8676 : "- MPD and m3u8 generation (potentially in parallel)\n"
8677 : "- ISOBMFF, MPEG-2 TS, MKV and raw bitstream segment formats\n"
8678 : "- override of profiles and levels in manifest for codecs\n"
8679 : "- most MPEG-DASH profiles\n"
8680 : "- static and dynamic (live) manifest offering\n"
8681 : "- context store and reload for batch processing of live/dynamic sessions\n"
8682 : "\n"
8683 : "The filter does perform per-segment real-time regulation using [-sreg]().\n"
8684 : "If you need per-frame real-time regulation on non-real-time inputs, insert a [reframer](reframer) before to perform real-time regulation.\n"
8685 : "EX src=file.mp4 reframer:rt=on @ -o live.mpd:dmode=dynamic\n"
8686 : "## Template strings\n"
8687 : "The segmenter uses templates to derive output file names, regardless of the DASH mode (even when templates are not used). "
8688 : "The default one is `$File$_dash` for ondemand and single file modes, and `$File$_$Number$` for separate segment files\n"
8689 : "EX template=Great_$File$_$Width$_$Number$\n"
8690 : "If input is foo.mp4 with 640x360 video, this will resolve in Great_foo_640_$Number$ for the DASH template.\n"
8691 : "EX template=Great_$File$_$Width$\n"
8692 : "If input is foo.mp4 with 640x360 video, this will resolve in Great_foo_640.mp4 for onDemand case.\n"
8693 : "\n"
8694 : "Standard DASH replacement strings: \n"
8695 : "- $Number[%%0Nd]$: replaced by the segment number, possibly prefixed with 0\n"
8696 : "- $RepresentationID$: replaced by representation name\n"
8697 : "- $Time$: replaced by segment start time\n"
8698 : "- $Bandwidth$: replaced by representation bandwidth.\n"
8699 : "Note: these strings are not replaced in the manifest templates elements.\n"
8700 : "\n"
8701 : "Additional replacement strings (not DASH, not generic GPAC replacements but may occur multiple times in template):\n"
8702 : "- $Init=NAME$: replaced by NAME for init segment, ignored otherwise\n"
8703 : "- $XInit=NAME$: complete replace by NAME for init segment, ignored otherwise\n"
8704 : "- $Index=NAME$: replaced by NAME for index segments, ignored otherwise\n"
8705 : "- $Path=PATH$: replaced by PATH when creating segments, ignored otherwise\n"
8706 : "- $Segment=NAME$: replaced by NAME for media segments, ignored for init segments\n"
8707 : "- $FS$ (FileSuffix): replaced by `_trackN` in case the input is an AV multiplex, or kept empty otherwise\n"
8708 : "Note: these strings are replaced in the manifest templates elements.\n"
8709 : "\n"
8710 : "## PID assignment and configuration\n"
8711 : "To assign PIDs into periods and adaptation sets and configure the session, the segmenter looks for the following properties on each input pid:\n"
8712 : "- Representation: assigns representation ID to input pid. If not set, the default behavior is to have each media component in different adaptation sets. Setting the RepresentationID allows explicit multiplexing of the source(s)\n"
8713 : "- Period: assigns period ID to input pid. If not set, the default behavior is to have all media in the same period with the same start time\n"
8714 : "- PStart: assigns period start. If not set, 0 is assumed, and periods appear in the Period ID declaration order. If negative, this gives the period order (-1 first, then -2 ...). If positive, this gives the true start time and will abort DASHing at period end\n"
8715 : "Note: When both positive and negative values are found, the by-order periods (negative) will be inserted AFTER the timed period (positive)\n"
8716 : "- ASID: assigns parent adaptation set ID. If not 0, only sources with same AS ID will be in the same adaptation set\n"
8717 : "Note: If multiple streams in source, only the first stream will have an AS ID assigned\n"
8718 : "- xlink: for remote periods, only checked for null pid\n"
8719 : "- Role, PDesc, ASDesc, ASCDesc, RDesc: various descriptors to set for period, AS or representation\n"
8720 : "- BUrl: overrides segmenter [-base] with a set of BaseURLs to use for the pid (per representation)\n"
8721 : "- Template: overrides segmenter [-template]() for this PID\n"
8722 : "- DashDur: overrides segmenter segment duration for this PID\n"
8723 : "- StartNumber: sets the start number for the first segment in the PID, default is 1\n"
8724 : "- IntraOnly: indicates input pid follows HLS EXT-X-I-FRAMES-ONLY guidelines\n"
8725 : "- Non-dash properties: Bitrate, SAR, Language, Width, Height, SampleRate, NumChannels, Language, ID, DependencyID, FPS, Interlaced, Codec. These properties are used to setup each representation and can be overridden on input PIDs using the general PID property settings (cf global help).\n"
8726 : " \n"
8727 : "EX src=test.mp4:#Bitrate=1M dst=test.mpd\n"
8728 : "This will force declaring a bitrate of 1M for the representation, regardless of actual input bitrate.\n"
8729 : "EX src=muxav.mp4 dst=test.mpd\n"
8730 : "This will create unmuxed DASH segments.\n"
8731 : "EX src=muxav.mp4:#Representation=1 dst=test.mpd\n"
8732 : "This will create muxed DASH segments.\n"
8733 : "EX src=m1.mp4 src=m2.mp4:#Period=Yep dst=test.mpd\n"
8734 : "This will put src m1.mp4 in first period, m2.mp4 in second period.\n"
8735 : "EX src=m1.mp4:#BUrl=http://foo/bar dst=test.mpd\n"
8736 : "This will assign a baseURL to src m1.mp4.\n"
8737 : "EX src=m1.mp4:#ASCDesc=<ElemName val=\"attval\">text</ElemName> dst=test.mpd\n"
8738 : "This will assign the specified XML descriptor to the adaptation set.\n"
8739 : "Note: this can be used to inject most DASH descriptors not natively handled by the segmenter.\n"
8740 : "The segmenter handles the XML descriptor as a string and does not attempt to validate it. Descriptors, as well as some segmenter filter arguments, are string lists (comma-separated by default), so that multiple descriptors can be added:\n"
8741 : "EX src=m1.mp4:#RDesc=<Elem attribute=\"1\"/>,<Elem2>text</Elem2> dst=test.mpd\n"
8742 : "This will insert two descriptors in the representation(s) of m1.mp4.\n"
8743 : "EX src=video.mp4:#Template=foo$Number$ src=audio.mp4:#Template=bar$Number$ dst=test.mpd\n"
8744 : "This will assign different templates to the audio and video sources.\n"
8745 : "EX src=null:#xlink=http://foo/bar.xml:#PDur=4 src=m.mp4:#PStart=-1\n"
8746 : "This will insert an create an MPD with first a remote period then a regular one.\n"
8747 : "EX src=null:#xlink=http://foo/bar.xml:#PStart=6 src=m.mp4\n"
8748 : "This will create an MPD with first a regular period, dashing ony 6s of content, then a remote one.\n"
8749 : "\n"
8750 : "The segmenter will create muxing filter chains for each representation and will reassign PID IDs so that each media component (video, audio, ...) in an adaptation set has the same ID.\n"
8751 : "\n"
8752 : "For HLS, the output pid will deliver the master playlist **and** the variant playlists.\n"
8753 : "The default variant playlist are $NAME_$N.m3u8, where $NAME is the radical of the output file name and $N is the 1-based index of the variant.\n"
8754 : "\n"
8755 : "## Segmentation\n"
8756 : "The default behavior of the segmenter is to estimate the theoretical start time of each segment based on target segment duration, and start a new segment when a packet with SAP type 1,2,3 or 4 with time greater than the theoretical time is found.\n"
8757 : "This behavior can be changed to find the best SAP packet around a segment theoretical boundary using [-sbound]():\n"
8758 : "- closest mode: the segment will start at the closest SAP of the theoretical boundary\n"
8759 : "- in mode: the segment will start at or before the theoretical boundary\n"
8760 : "Warning: These modes will introduce delay in the segmenter (typically buffering of one GOP) and should not be used for low-latency modes.\n"
8761 : "The segmenter can also be configured to:\n"
8762 : "- completely ignore SAP when segmenting using [-sap]().\n"
8763 : "- ignore SAP on non-video streams when segmenting using [-strict_sap]().\n"
8764 : "\n"
8765 : "## Dynamic (real-time live) Mode\n"
8766 : "The dasher does not perform real-time regulation by default.\n"
8767 : "For regular segmentation, you should enable segment regulation [-sreg]() if your sources are not real-time.\n"
8768 : "EX gpac -i source.mp4 -o live.mpd:segdur=2:profile=live:dmode=dynamic:sreg\n"
8769 : "\n"
8770 : "For low latency segmentation with fMP4, you will need to specify the following options:\n"
8771 : "- cdur: set the fMP4 fragment duration\n"
8772 : "- asto: set the availability time offset for DASH. This value should be equal or slightly greater than segment duration minus cdur\n"
8773 : "- llhls: enable low latency for HLS\n"
8774 : "If your sources are not real-time, insert a reframer filter with real-time regulation\n"
8775 : "EX gpac -i source.mp4 reframer:rt=on @ -o live.mpd:segdur=2:cdur=0.2:asto=1.8:profile=live:dmode=dynamic\n"
8776 : "This will create DASH segments of 2 seconds made of fragments of 200 ms and indicate to the client that requests can be made 1.8 seconds earlier than segment complete availability on server.\n"
8777 : "EX gpac -i source.mp4 reframer:rt=on @ -o live.m3u8:segdur=2:cdur=0.2:llhls=br:dmode=dynamic\n"
8778 : "This will create DASH segments of 2 seconds made of fragments of 200 ms and produce HLS low latency parts using byte ranges in the final segment.\n"
8779 : "EX gpac -i source.mp4 reframer:rt=on @ -o live.m3u8:segdur=2:cdur=0.2:llhls=sf:dmode=dynamic\n"
8780 : "This will create DASH segments of 2 seconds made of fragments of 200 ms and produce HLS low latency parts using dedicated files.\n"
8781 : "\n"
8782 : "You can combine LL-HLS and DASH-LL generation:\n"
8783 : "EX gpac -i source.mp4 reframer:rt=on @ -o live.mpd:dual:segdur=2:cdur=0.2:asto=1.8:llhls=br:profile=live:dmode=dynamic\n"
8784 : "\n"
8785 : "For DASH, the filter will use the local clock for UTC anchor points in DASH.\n"
8786 : "The filter can fetch and signal clock in other ways using [-utcs]().\n"
8787 : "EX [opts]:utcs=inband\n"
8788 : "This will use the local clock and insert in the MPD a UTCTiming descriptor containing the local clock.\n"
8789 : "EX [opts]::utcs=http://time.akamai.com[::opts]\n"
8790 : "This will fetch time from `http://time.akamai.com`, use it as the UTC reference for segment generation and insert in the MPD a UTCTiming descriptor containing the time server URL.\n"
8791 : "Note: if not set as a global option using `--utcs=`, you must escape the url using double `::` or use other separators.\n"
8792 : "\n"
8793 : "## Cue-driven segmentation\n"
8794 : "The segmenter can take a list of instructions, or Cues, to use for the segmentation process, in which case only these are used to derive segment boundaries. Cues can be set through XML files or injected in input packets.\n"
8795 : "\n"
8796 : "Cue files can be specified for the entire segmenter, or per PID using `DashCue` property.\n"
8797 : "Cues are given in an XML file with a root element called <DASHCues>, with currently no attribute specified. The children are one or more <Stream> elements, with attributes:\n"
8798 : "- id: integer for stream/track/pid ID\n"
8799 : "- timescale: integer giving the units of following timestamps\n"
8800 : "- mode: if present and value is `edit`, the timestamp are in presentation time (edit list applied) otherwise they are in media time\n"
8801 : "- ts_offset: integer giving a value (in timescale) to subtract to the DTS/CTS values listed\n"
8802 : "\nThe children of <Stream> are one or more <Cue> elements, with attributes:\n"
8803 : "- sample: integer giving the sample/frame number of a sample at which splitting shall happen\n"
8804 : "- dts: long integer giving the decoding time stamp of a sample at which splitting shall happen\n"
8805 : "- cts: long integer giving the composition / presentation time stamp of a sample at which splitting shall happen\n"
8806 : "Warning: Cues shall be listed in decoding order.\n"
8807 : "\n"
8808 : "If the `DashCue` property of a PID equals `inband`, the PID will be segmented according to the `CueStart` property of input packets.\n"
8809 : "This feature is typically combined with a list of files as input:\n"
8810 : "EX -i list.m3u:sigcues -o res/live.mpd"
8811 : "This will load the `flist` filter in cue mode, generating continuous timelines from the sources and injecting a `CueStart` property at each new file."
8812 : "\n"
8813 : "## Manifest Generation only mode\n"
8814 : "The segmenter can be used to generate manifests from already fragmented ISOBMFF inputs using [-sigfrag]().\n"
8815 : "In this case, segment boundaries are attached to each packet starting a segment and used to drive the segmentation.\n"
8816 : "This can be used with single-track ISOBMFF sources, either single file or multi file.\n"
8817 : "For single file source:\n"
8818 : "- if onDemand [-profile]() is requested, sources have to be formatted as a DASH self-initializing media segment with the proper sidx.\n"
8819 : "- templates are disabled.\n"
8820 : "- [-sseg]() is forced for all profiles except onDemand ones.\n"
8821 : "For multi files source:\n"
8822 : "- input shall be a playlist containing the initial file followed by the ordered list of segments.\n"
8823 : "- if no [-template]() is provided, the full or main [-profile]() will be used\n"
8824 : "- if [-template]() is provided, it shall be correct: the filter will not try to guess one from the input file names and will not validate it either.\n"
8825 : "\n"
8826 : "The manifest generation-only mode supports both MPD and HLS generation.\n"
8827 : "\n"
8828 : "EX -i ondemand_src.mp4 -o dash.mpd:sigfrag:profile=onDemand\n"
8829 : "This will generate a DASH manifest for onDemand Profile based on the input file.\n"
8830 : "EX -i ondemand_src.mp4 -o dash.m3u8:sigfrag\n"
8831 : "This will generate a HLS manifest based on the input file.\n"
8832 : "EX -i seglist.txt -o dash.mpd:sigfrag\n"
8833 : "This will generate a DASH manifest in Main Profile based on the input files.\n"
8834 : "EX -i seglist.txt:Template=$XInit=init$$q1/$Number$ -o dash.mpd:sigfrag:profile=live\n"
8835 : "This will generate a DASH manifest in live Profile based on the input files. The input file will contain `init.mp4`, `q1/1.m4s`, `q1/2.m4s`...\n"
8836 : "\n"
8837 : "## Muxer development considerations\n"
8838 : "Output muxers allowing segmented output must obey the following:\n"
8839 : "- inspect packet properties\n"
8840 : " - FileNumber: if set, indicate the start of a new DASH segment\n"
8841 : " - FileName: if set, indicate the file name. If not present, output shall be a single file. This is only set for packet carrying the `FileNumber` property, and only on one PID (usually the first) for multiplexed outputs\n"
8842 : " - IDXName: gives the optional index name (if not present, index shall be in the same file as dash segment). Only used for MPEG-2 TS for now\n"
8843 : " - EODS: property is set on packets with no payload and no timestamp to signal the end of a DASH segment. This is only used when stopping/resuming the segmentation process, in order to flush segments without dispatching an EOS (see [-subdur]() )\n"
8844 : "- for each segment done, send a downstream event on the first connected PID signaling the size of the segment and the size of its index if any\n"
8845 : "- for muxers with init data, send a downstream event signaling the size of the init and the size of the global index if any\n"
8846 : "- the following filter options are passed to muxers, which should declare them as arguments:\n"
8847 : " - noinit: disables output of init segment for the muxer (used to handle bitstream switching with single init in DASH)\n"
8848 : " - frag: indicates muxer shall use fragmented format (used for ISOBMFF mostly)\n"
8849 : " - subs_sidx=0: indicates an SIDX shall be generated - only added if not already specified by user\n"
8850 : " - xps_inband=all|no: indicates AVC/HEVC/... parameter sets shall be sent inband or out of band\n"
8851 : " - nofragdef: indicates fragment defaults should be set in each segment rather than in init segment\n"
8852 : "\n"
8853 : "The segmenter will add the following properties to the output PIDs:\n"
8854 : "- DashMode: identifies VoD (single file with global index) or regular DASH mode used by segmenter\n"
8855 : "- DashDur: identifies target DASH segment duration - this can be used to estimate the SIDX size for example\n"
8856 : )
8857 : .private_size = sizeof(GF_DasherCtx),
8858 : .args = DasherArgs,
8859 : .initialize = dasher_initialize,
8860 : .finalize = dasher_finalize,
8861 : SETCAPS(DasherCaps),
8862 : .flags = GF_FS_REG_REQUIRES_RESOLVER,
8863 : .configure_pid = dasher_configure_pid,
8864 : .process = dasher_process,
8865 : .process_event = dasher_process_event,
8866 : };
8867 :
8868 :
8869 2877 : const GF_FilterRegister *dasher_register(GF_FilterSession *session)
8870 : {
8871 2877 : return &DasherRegister;
8872 : }
|