Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2017-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / DASH/HLS demux filter
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 :
29 : #ifndef GPAC_DISABLE_DASH_CLIENT
30 :
31 : #include <gpac/dash.h>
32 :
33 : #ifdef GPAC_HAS_QJS
34 : #include "../quickjs/quickjs.h"
35 : #include "../scenegraph/qjs_common.h"
36 : #endif
37 :
38 : enum
39 : {
40 : DFWD_OFF = 0,
41 : DFWD_FILE,
42 : //all modes below forward frames, not files
43 : DFWD_SBOUND,
44 : DFWD_SBOUND_MANIFEST,
45 : };
46 :
47 : typedef struct
48 : {
49 : //opts
50 : s32 shift_utc, route_shift;
51 : u32 max_buffer, auto_switch, tiles_rate, segstore, delay40X, exp_threshold, switch_count, bwcheck;
52 : s32 init_timeshift;
53 : Bool server_utc, screen_res, aggressive, speedadapt, fmodefwd, skip_lqt, llhls_merge, filemode;
54 : u32 forward;
55 : GF_PropUIntList debug_as;
56 : GF_DASHInitialSelectionMode start_with;
57 : GF_DASHTileAdaptationMode tile_mode;
58 : char *algo;
59 : Bool max_res, immediate, abort, use_bmin;
60 : char *query;
61 : Bool noxlink, split_as, noseek, groupsel;
62 : u32 lowlat;
63 :
64 : GF_FilterPid *mpd_pid;
65 : GF_Filter *filter;
66 :
67 : GF_FilterPid *output_mpd_pid;
68 :
69 : GF_DashClient *dash;
70 : //http io for manifest
71 : GF_DASHFileIO dash_io;
72 : GF_DownloadManager *dm;
73 :
74 : Bool reuse_download_session;
75 :
76 : Bool initial_setup_done;
77 : Bool in_error;
78 : u32 nb_playing;
79 :
80 : /*max width & height in all active representations*/
81 : u32 width, height;
82 :
83 : Double seek_request;
84 : Double media_start_range;
85 :
86 : Bool mpd_open;
87 : Bool initial_play;
88 : Bool check_eos;
89 :
90 : char *frag_url;
91 :
92 : char *manifest_payload;
93 : GF_List *hls_variants, *hls_variants_names;
94 :
95 : Bool is_dash;
96 : Bool manifest_stop_sent;
97 : #ifdef GPAC_HAS_QJS
98 : JSContext *js_ctx;
99 : Bool owns_context;
100 : JSValue js_obj, rate_fun, download_fun, new_group_fun, period_reset_fun;
101 : #endif
102 :
103 : void *rt_udta;
104 : void (*on_period_reset)(void *udta, u32 reset_type);
105 : void (*on_new_group)(void *udta, u32 group_idx, void *dash);
106 : s32 (*on_rate_adaptation)(void *udta, u32 group_idx, u32 base_group_idx, Bool force_low_complex, void *stats);
107 : s32 (*on_download_monitor)(void *udta, u32 group_idx, void *stats);
108 : } GF_DASHDmxCtx;
109 :
110 : typedef struct
111 : {
112 : GF_DASHDmxCtx *ctx;
113 : GF_Filter *seg_filter_src;
114 :
115 : u32 idx;
116 : Bool init_switch_seg_sent;
117 : Bool segment_sent, in_is_cryptfile;
118 :
119 : u32 nb_eos, nb_pids;
120 : Bool stats_uploaded;
121 : Bool wait_for_pck;
122 : Bool eos_detected;
123 : u32 next_dependent_rep_idx, current_dependent_rep_idx;
124 :
125 : GF_DownloadSession *sess;
126 : Bool is_timestamp_based, pto_setup;
127 : Bool prev_is_init_segment;
128 : u32 timescale;
129 : s64 pto;
130 : s64 max_cts_in_period;
131 : bin128 key_IV;
132 :
133 : Bool seg_was_not_ready;
134 : Bool in_error;
135 : Bool is_playing;
136 : Bool force_seg_switch;
137 : u32 nb_group_deps, current_group_dep;
138 : u32 last_bw_check;
139 : u64 us_at_seg_start;
140 : Bool signal_seg_name;
141 : Bool init_ok;
142 :
143 : u32 seg_discard_state;
144 :
145 : char *template;
146 : } GF_DASHGroup;
147 :
148 174 : static void dashdmx_set_string_list_prop(GF_FilterPacket *ref, u32 prop_name, GF_List **str_list)
149 : {
150 : u32 i, count;
151 : GF_PropertyValue v;
152 174 : GF_List *list = *str_list;
153 346 : if (!list) return;
154 :
155 2 : v.type = GF_PROP_STRING_LIST;
156 2 : v.value.string_list.nb_items = count = gf_list_count(list);
157 2 : v.value.string_list.vals = gf_malloc(sizeof(char *) * count);
158 8 : for (i=0; i<count;i++) {
159 6 : v.value.string_list.vals[i] = gf_list_pop_front(list);
160 : }
161 2 : gf_list_del(list);
162 2 : *str_list = NULL;
163 2 : gf_filter_pck_set_property(ref, prop_name, &v);
164 : }
165 :
166 :
167 121704 : static void dashdmx_forward_packet(GF_DASHDmxCtx *ctx, GF_FilterPacket *in_pck, GF_FilterPid *in_pid, GF_FilterPid *out_pid, GF_DASHGroup *group)
168 : {
169 : GF_FilterPacket *dst_pck;
170 : Bool do_map_time = GF_FALSE;
171 : Bool seek_flag = 0;
172 : u64 cts, dts;
173 :
174 121704 : if (ctx->forward) {
175 : Bool is_filemode;
176 3516 : Bool is_start = group->signal_seg_name;
177 : GF_FilterPacket *ref;
178 :
179 3516 : if (ctx->forward==DFWD_FILE) {
180 168 : Bool is_end = GF_FALSE;
181 168 : if (ctx->fmodefwd) {
182 168 : ref = gf_filter_pck_new_ref(out_pid, 0, 0, in_pck);
183 : } else {
184 : u8 *output;
185 0 : ref = gf_filter_pck_new_copy(out_pid, in_pck, &output);
186 : }
187 168 : if (!ref) return;
188 :
189 168 : group->signal_seg_name = 0;
190 168 : gf_filter_pck_get_framing(in_pck, NULL, &is_end);
191 168 : gf_filter_pid_drop_packet(in_pid);
192 168 : if (gf_filter_pid_is_eos(in_pid))
193 5 : is_end = GF_TRUE;
194 :
195 168 : gf_filter_pck_set_framing(ref, is_start, is_end);
196 : is_filemode = GF_TRUE;
197 : } else {
198 : const GF_PropertyValue *p;
199 3348 : ref = gf_filter_pck_new_ref(out_pid, 0, 0, in_pck);
200 3348 : if (!ref) return;
201 :
202 3348 : gf_filter_pck_merge_properties(in_pck, ref);
203 3348 : p = gf_filter_pck_get_property(in_pck, GF_PROP_PCK_CUE_START);
204 3348 : if (p && p->value.boolean) {
205 : is_start = GF_TRUE;
206 : }
207 3348 : group->pto_setup = GF_TRUE;
208 3348 : gf_filter_pid_drop_packet(in_pid);
209 : is_filemode = GF_FALSE;
210 : }
211 :
212 3516 : if (is_start) {
213 87 : if (group->prev_is_init_segment) {
214 7 : const char *init_segment = NULL;
215 7 : gf_dash_group_next_seg_info(ctx->dash, group->idx, 0, NULL, NULL, NULL, NULL, &init_segment);
216 7 : if (init_segment) {
217 7 : gf_filter_pck_set_property(ref, GF_PROP_PCK_FILENAME, &PROP_STRING(init_segment) );
218 7 : gf_filter_pck_set_property(ref, GF_PROP_PCK_INIT, &PROP_BOOL(GF_TRUE) );
219 : }
220 : } else {
221 : GF_Fraction64 seg_time;
222 80 : const char *seg_name = NULL;
223 : u32 seg_number, seg_dur;
224 80 : gf_dash_group_next_seg_info(ctx->dash, group->idx, group->current_dependent_rep_idx, &seg_name, &seg_number, &seg_time, &seg_dur, NULL);
225 80 : if (seg_name) {
226 80 : gf_filter_pck_set_property(ref, GF_PROP_PCK_FILENAME, &PROP_STRING(seg_name) );
227 80 : gf_filter_pck_set_property(ref, GF_PROP_PCK_FILENUM, &PROP_UINT(seg_number) );
228 :
229 80 : if (is_filemode) {
230 : u64 ts;
231 26 : gf_filter_pck_set_duration(ref, seg_dur);
232 : //hack to avoid using a property, we set the carousel version on the packet to signal the duration applies to the entire segment, not the packet
233 26 : gf_filter_pck_set_carousel_version(ref, 1);
234 26 : if (seg_time.den) {
235 26 : ts = seg_time.num;
236 26 : if (seg_time.den != 1000) {
237 26 : ts *= 1000;
238 26 : ts /= seg_time.den;
239 : }
240 : } else {
241 : ts = GF_FILTER_NO_TS;
242 : }
243 26 : gf_filter_pck_set_cts(ref, ts);
244 : }
245 : }
246 : }
247 :
248 87 : if (ctx->manifest_payload) {
249 3 : gf_filter_pck_set_property(ref, GF_PROP_PCK_DASH_MANIFEST, &PROP_STRING_NO_COPY(ctx->manifest_payload));
250 3 : ctx->manifest_payload = NULL;
251 : }
252 87 : dashdmx_set_string_list_prop(ref, GF_PROP_PCK_HLS_VARIANT, &ctx->hls_variants);
253 87 : dashdmx_set_string_list_prop(ref, GF_PROP_PCK_HLS_VARIANT_NAME, &ctx->hls_variants_names);
254 : }
255 3516 : gf_filter_pck_send(ref);
256 3516 : return;
257 : }
258 :
259 118188 : if (!ctx->is_dash) {
260 6483 : gf_filter_pck_forward(in_pck, out_pid);
261 :
262 6483 : if (!group->pto_setup) {
263 33 : cts = gf_filter_pck_get_cts(in_pck);
264 33 : gf_filter_pid_set_property_str(out_pid, "time:timestamp", &PROP_LONGUINT(cts) );
265 33 : gf_filter_pid_set_property_str(out_pid, "time:media", &PROP_DOUBLE(ctx->media_start_range) );
266 33 : group->pto_setup = GF_TRUE;
267 : }
268 :
269 6483 : gf_filter_pid_drop_packet(in_pid);
270 6483 : return;
271 : }
272 :
273 : //filter any packet outside the current period
274 111705 : dts = gf_filter_pck_get_dts(in_pck);
275 111705 : cts = gf_filter_pck_get_cts(in_pck);
276 111705 : seek_flag = gf_filter_pck_get_seek_flag(in_pck);
277 :
278 : //if sync is based on timestamps do not adjust the timestamps back
279 111705 : if (! group->is_timestamp_based) {
280 111705 : if (!group->pto_setup) {
281 : Double scale;
282 : s64 start, dur;
283 : u64 pto;
284 127 : u32 ts = gf_filter_pck_get_timescale(in_pck);
285 127 : gf_dash_group_get_presentation_time_offset(ctx->dash, group->idx, &pto, &group->timescale);
286 127 : group->pto = (s64) pto;
287 127 : group->pto_setup = 1;
288 :
289 127 : if (group->timescale && (group->timescale != ts)) {
290 4 : group->pto *= ts;
291 4 : group->pto /= group->timescale;
292 : }
293 127 : scale = ts;
294 127 : scale /= 1000;
295 :
296 127 : dur = (u64) (scale * gf_dash_get_period_duration(ctx->dash));
297 127 : if (dur) {
298 107 : group->max_cts_in_period = group->pto + dur;
299 : } else {
300 20 : group->max_cts_in_period = 0;
301 : }
302 :
303 127 : start = (u64) (scale * gf_dash_get_period_start(ctx->dash));
304 127 : group->pto -= start;
305 : }
306 :
307 111705 : if (group->max_cts_in_period && (s64) cts > group->max_cts_in_period) {
308 : u64 adj_cts = cts;
309 2814 : const GF_PropertyValue *p = gf_filter_pid_get_property(in_pid, GF_PROP_PID_DELAY);
310 2814 : if (p) adj_cts += p->value.longsint;
311 2814 : if ( (s64) adj_cts > group->max_cts_in_period) {
312 2801 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASHDmx] Packet timestamp "LLU" larger than max CTS in period "LLU" - forcing seek flag\n", adj_cts, group->max_cts_in_period));
313 : seek_flag = 1;
314 : }
315 : }
316 :
317 : //remap timestamps to our timeline
318 111705 : if (dts != GF_FILTER_NO_TS) {
319 88390 : if ((s64) dts >= group->pto)
320 88388 : dts -= group->pto;
321 : else {
322 2 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASHDmx] Packet DTS "LLU" less than PTO "LLU" - forcing DTS to 0\n", dts, group->pto));
323 : dts = 0;
324 : seek_flag = 1;
325 : }
326 : }
327 111705 : if (cts!=GF_FILTER_NO_TS) {
328 88390 : if ((s64) cts >= group->pto)
329 88390 : cts -= group->pto;
330 : else {
331 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASHDmx] Packet CTS "LLU" less than PTO "LLU" - forcing CTS to 0\n", cts, group->pto));
332 : cts = 0;
333 : seek_flag = 1;
334 : }
335 : }
336 0 : } else if (!group->pto_setup) {
337 : do_map_time = 1;
338 0 : group->pto_setup = 1;
339 : }
340 :
341 111705 : dst_pck = gf_filter_pck_new_ref(out_pid, 0, 0, in_pck);
342 111705 : if (!dst_pck) return;
343 :
344 : //this will copy over clock info for PCR in TS
345 111705 : gf_filter_pck_merge_properties(in_pck, dst_pck);
346 111705 : gf_filter_pck_set_dts(dst_pck, dts);
347 111705 : gf_filter_pck_set_cts(dst_pck, cts);
348 111705 : gf_filter_pck_set_seek_flag(dst_pck, seek_flag);
349 111705 : gf_filter_pck_send(dst_pck);
350 111705 : gf_filter_pid_drop_packet(in_pid);
351 :
352 111705 : if (do_map_time) {
353 0 : gf_filter_pid_set_property_str(out_pid, "time:timestamp", &PROP_LONGUINT(cts) );
354 0 : gf_filter_pid_set_property_str(out_pid, "time:media", &PROP_DOUBLE(ctx->media_start_range) );
355 : }
356 : }
357 :
358 :
359 9 : static void dashdmx_on_filter_setup_error(GF_Filter *failed_filter, void *udta, GF_Err err)
360 : {
361 : GF_DASHGroup *group = (GF_DASHGroup *)udta;
362 9 : if (!udta) return;
363 :
364 9 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d download setup error %s\n", group->idx, gf_error_to_string(err) ));
365 :
366 9 : gf_dash_set_group_download_state(group->ctx->dash, group->idx, group->current_dependent_rep_idx, err);
367 9 : if (err) {
368 9 : Bool group_done = gf_dash_get_group_done(group->ctx->dash, group->idx);
369 9 : group->stats_uploaded = GF_TRUE;
370 9 : group->segment_sent = GF_FALSE;
371 9 : gf_filter_post_process_task(group->ctx->filter);
372 9 : if (group_done) {
373 3 : group->eos_detected = GF_TRUE;
374 : } else {
375 6 : group->in_error = GF_TRUE;
376 : //failure at init, abort group
377 6 : if (!group->init_ok)
378 0 : group->seg_filter_src = NULL;
379 : }
380 : }
381 : }
382 :
383 : void gf_cryptfin_set_kms(GF_Filter *f, const char *key_url, bin128 key_IV);
384 :
385 : /*locates input service (demuxer) based on mime type or segment name*/
386 197 : static GF_Err dashdmx_load_source(GF_DASHDmxCtx *ctx, u32 group_index, const char *mime, const char *init_segment_name, u64 start_range, u64 end_range)
387 : {
388 : GF_DASHGroup *group;
389 : GF_Err e;
390 : u32 url_type=0;
391 : Bool has_sep = GF_FALSE;
392 : u32 crypto_type;
393 : bin128 key_IV;
394 : char szSep[2];
395 : const char *key_uri;
396 197 : char *sURL = NULL;
397 : const char *base_url;
398 197 : if (!init_segment_name) {
399 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] group %d Error locating input filter: no segment URL, mime type %s\n", group_index, mime));
400 : return GF_FILTER_NOT_FOUND;
401 : }
402 :
403 197 : GF_SAFEALLOC(group, GF_DASHGroup);
404 197 : if (!group) return GF_OUT_OF_MEM;
405 197 : group->ctx = ctx;
406 197 : group->idx = group_index;
407 197 : gf_dash_set_group_udta(ctx->dash, group_index, group);
408 :
409 197 : base_url = gf_dash_get_url(ctx->dash);
410 197 : if (!strnicmp(base_url, "http://", 7)) url_type=1;
411 148 : else if (!strnicmp(base_url, "https://", 7)) url_type=2;
412 : else url_type=0;
413 :
414 197 : key_uri = gf_dash_group_get_segment_init_keys(ctx->dash, group_index, &crypto_type, &key_IV);
415 197 : if (crypto_type==1) {
416 0 : gf_dynstrcat(&sURL, "gcryp://", NULL);
417 0 : group->in_is_cryptfile = GF_TRUE;
418 : }
419 :
420 197 : if (!strncmp(init_segment_name, "isobmff://", 10)) {
421 5 : if (url_type==1)
422 3 : gf_dynstrcat(&sURL, "http://", NULL);
423 2 : else if (url_type==2)
424 2 : gf_dynstrcat(&sURL, "https://", NULL);
425 : else
426 0 : gf_dynstrcat(&sURL, "file://", NULL);
427 : }
428 197 : gf_dynstrcat(&sURL, init_segment_name, NULL);
429 :
430 197 : szSep[0] = gf_filter_get_sep(ctx->filter, GF_FS_SEP_ARGS);
431 197 : szSep[1] = 0;
432 :
433 : //not from file system, set cache option
434 197 : if (url_type) {
435 67 : if (!ctx->segstore) {
436 67 : if (!has_sep) { gf_dynstrcat(&sURL, "gpac", szSep); has_sep = GF_TRUE; }
437 : //if operating in mem mode and we load a file decryptor, only store in mem cache the first seg, and no cache for segments
438 67 : if (crypto_type==1)
439 0 : gf_dynstrcat(&sURL, "cache=none_keep", szSep);
440 : else
441 67 : gf_dynstrcat(&sURL, "cache=mem_keep", szSep);
442 : }
443 0 : else if (ctx->segstore==2) {
444 0 : if (!has_sep) { gf_dynstrcat(&sURL, "gpac", szSep); has_sep = GF_TRUE; }
445 0 : gf_dynstrcat(&sURL, "cache=keep", szSep);
446 : }
447 : }
448 :
449 197 : if (start_range || end_range) {
450 : char szRange[500];
451 24 : if (!has_sep) { gf_dynstrcat(&sURL, "gpac", szSep); has_sep = GF_TRUE; }
452 : snprintf(szRange, 500, "range="LLU"-"LLU, start_range, end_range);
453 24 : gf_dynstrcat(&sURL, szRange, szSep);
454 : }
455 197 : if (ctx->forward>DFWD_FILE) {
456 27 : if (!has_sep) { gf_dynstrcat(&sURL, "gpac", szSep); }
457 27 : gf_dynstrcat(&sURL, "sigfrag", szSep);
458 : }
459 :
460 197 : group->seg_filter_src = gf_filter_connect_source(ctx->filter, sURL, NULL, GF_FALSE, &e);
461 197 : if (!group->seg_filter_src) {
462 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] group %d error locating plugin for segment - mime type %s name %s: %s\n", group_index, mime, sURL, gf_error_to_string(e) ));
463 0 : gf_free(sURL);
464 0 : gf_free(group);
465 0 : gf_dash_set_group_udta(ctx->dash, group_index, NULL);
466 0 : return e;
467 : }
468 197 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASHDmx] setting up group %d from %s\n", group->idx, init_segment_name));
469 197 : group->signal_seg_name = (ctx->forward==DFWD_FILE) ? GF_TRUE : GF_FALSE;
470 :
471 197 : gf_filter_set_setup_failure_callback(ctx->filter, group->seg_filter_src, dashdmx_on_filter_setup_error, group);
472 :
473 : //if HLS AES-CBC, set key BEFORE discarding segment URL (if TS, discarding the segment will discard the key uri)
474 197 : if (key_uri && (crypto_type==1)) {
475 0 : gf_cryptfin_set_kms(group->seg_filter_src, key_uri, key_IV);
476 : }
477 :
478 197 : gf_dash_group_discard_segment(ctx->dash, group->idx);
479 197 : group->prev_is_init_segment = GF_TRUE;
480 197 : group->nb_group_deps = gf_dash_group_get_num_groups_depending_on(ctx->dash, group_index);
481 197 : group->current_group_dep = 0;
482 197 : gf_free(sURL);
483 :
484 197 : return GF_OK;
485 : }
486 :
487 18 : void dashdmx_io_delete_cache_file(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, const char *cache_url)
488 : {
489 18 : if (!session) {
490 : return;
491 : }
492 10 : gf_dm_delete_cached_file_entry_session((GF_DownloadSession *)session, cache_url);
493 : }
494 :
495 52 : GF_DASHFileIOSession dashdmx_io_create(GF_DASHFileIO *dashio, Bool persistent, const char *url, s32 group_idx)
496 : {
497 : GF_DownloadSession *sess;
498 : GF_Err e;
499 : u32 flags = GF_NETIO_SESSION_NOT_THREADED;
500 52 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)dashio->udta;
501 :
502 : //we work in non-threaded mode, only MPD fetcher is allowed
503 52 : if (group_idx>=0)
504 : return NULL;
505 :
506 : //crude hack when using gpac downloader to initialize the MPD pid: get the pointer to the download session
507 : //this should be safe unless the mpd_pid is destroyed, which should only happen upon destruction of the DASH session
508 52 : if (group_idx==-1) {
509 52 : const GF_PropertyValue *p = gf_filter_pid_get_property(ctx->mpd_pid, GF_PROP_PID_DOWNLOAD_SESSION);
510 52 : if (p) {
511 39 : sess = (GF_DownloadSession *) p->value.ptr;
512 39 : if (!ctx->segstore) {
513 39 : gf_dm_sess_force_memory_mode(sess, 1);
514 : }
515 39 : if (ctx->reuse_download_session)
516 3 : gf_dm_sess_setup_from_url(sess, url, GF_FALSE);
517 39 : ctx->reuse_download_session = GF_TRUE;
518 39 : return (GF_DASHFileIOSession) sess;
519 : }
520 : }
521 :
522 13 : if (group_idx<-1) {
523 : flags |= GF_NETIO_SESSION_MEMORY_CACHE;
524 : } else {
525 13 : if (!ctx->segstore) flags |= GF_NETIO_SESSION_MEMORY_CACHE;
526 13 : if (persistent) flags |= GF_NETIO_SESSION_PERSISTENT;
527 : }
528 13 : sess = gf_dm_sess_new(ctx->dm, url, flags, NULL, NULL, &e);
529 13 : return (GF_DASHFileIOSession) sess;
530 : }
531 52 : void dashdmx_io_del(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
532 : {
533 52 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)dashio->udta;
534 52 : if (!ctx->reuse_download_session)
535 13 : gf_dm_sess_del((GF_DownloadSession *)session);
536 52 : }
537 209 : GF_Err dashdmx_io_init(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
538 : {
539 209 : return gf_dm_sess_process_headers((GF_DownloadSession *)session);
540 : }
541 209 : GF_Err dashdmx_io_run(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
542 : {
543 209 : return gf_dm_sess_process((GF_DownloadSession *)session);
544 : }
545 60 : const char *dashdmx_io_get_url(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
546 : {
547 60 : return gf_dm_sess_get_resource_name((GF_DownloadSession *)session);
548 : }
549 271 : const char *dashdmx_io_get_cache_name(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
550 : {
551 271 : return gf_dm_sess_get_cache_name((GF_DownloadSession *)session);
552 : }
553 99 : const char *dashdmx_io_get_mime(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
554 : {
555 99 : return gf_dm_sess_mime_type((GF_DownloadSession *)session);
556 : }
557 184 : const char *dashdmx_io_get_header_value(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, const char *header_name)
558 : {
559 184 : return gf_dm_sess_get_header((GF_DownloadSession *)session, header_name);
560 : }
561 133 : u64 dashdmx_io_get_utc_start_time(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
562 : {
563 133 : return gf_dm_sess_get_utc_start((GF_DownloadSession *)session);
564 : }
565 119 : GF_Err dashdmx_io_setup_from_url(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, const char *url, s32 group_idx)
566 : {
567 119 : return gf_dm_sess_setup_from_url((GF_DownloadSession *)session, url, GF_FALSE);
568 : }
569 50 : GF_Err dashdmx_io_set_range(GF_DASHFileIO *dashio, GF_DASHFileIOSession session, u64 start_range, u64 end_range, Bool discontinue_cache)
570 : {
571 50 : return gf_dm_sess_set_range((GF_DownloadSession *)session, start_range, end_range, discontinue_cache);
572 : }
573 :
574 : #if 0 //unused since we are in non threaded mode
575 : void dashdmx_io_abort(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
576 : {
577 : gf_dm_sess_abort((GF_DownloadSession *)session);
578 : }
579 :
580 : u32 dashdmx_io_get_bytes_per_sec(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
581 : {
582 : u32 bps=0;
583 : if (session) {
584 : gf_dm_sess_get_stats((GF_DownloadSession *)session, NULL, NULL, NULL, NULL, &bps, NULL);
585 : } else {
586 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)dashio->udta;
587 : bps = gf_dm_get_data_rate(ctx->dm);
588 : bps/=8;
589 : }
590 : return bps;
591 : }
592 : u32 dashdmx_io_get_total_size(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
593 : {
594 : u64 size=0;
595 : gf_dm_sess_get_stats((GF_DownloadSession *)session, NULL, NULL, &size, NULL, NULL, NULL);
596 : return (u32) size;
597 : }
598 : u32 dashdmx_io_get_bytes_done(GF_DASHFileIO *dashio, GF_DASHFileIOSession session)
599 : {
600 : u64 size=0;
601 : gf_dm_sess_get_stats((GF_DownloadSession *)session, NULL, NULL, NULL, &size, NULL, NULL);
602 : return (u32) size;
603 : }
604 : #endif
605 :
606 : #ifdef GPAC_HAS_QJS
607 : #include <gpac/mpd.h>
608 2 : void dashdmx_js_declare_group(GF_DASHDmxCtx *ctx, u32 group_idx)
609 : {
610 : u32 i, count;
611 : u32 srd_id, srd_x, srd_y, srd_w, srd_h, srd_fw, srd_fh;
612 : JSValue res, reps;
613 2 : JSValue obj = JS_NewObject(ctx->js_ctx);
614 :
615 4 : JS_SetPropertyStr(ctx->js_ctx, obj, "idx", JS_NewInt32(ctx->js_ctx, group_idx));
616 4 : JS_SetPropertyStr(ctx->js_ctx, obj, "duration", JS_NewInt64(ctx->js_ctx, gf_dash_get_period_duration(ctx->dash) ));
617 :
618 2 : srd_id = srd_x = srd_y = srd_w = srd_h = srd_fw = srd_fh = 0;
619 2 : gf_dash_group_get_srd_info(ctx->dash, group_idx, &srd_id, &srd_x, &srd_y, &srd_w, &srd_h, &srd_fw, &srd_fh);
620 :
621 2 : if (srd_fw && srd_fh) {
622 0 : JSValue srd = JS_NewObject(ctx->js_ctx);
623 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "ID", JS_NewInt32(ctx->js_ctx, srd_id));
624 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "x", JS_NewInt32(ctx->js_ctx, srd_x));
625 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "y", JS_NewInt32(ctx->js_ctx, srd_y));
626 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "w", JS_NewInt32(ctx->js_ctx, srd_w));
627 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "h", JS_NewInt32(ctx->js_ctx, srd_h));
628 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "fw", JS_NewInt32(ctx->js_ctx, srd_fw));
629 0 : JS_SetPropertyStr(ctx->js_ctx, srd, "fh", JS_NewInt32(ctx->js_ctx, srd_fh));
630 0 : JS_SetPropertyStr(ctx->js_ctx, obj, "SRD", srd);
631 : } else {
632 2 : JS_SetPropertyStr(ctx->js_ctx, obj, "SRD", JS_NULL);
633 : }
634 :
635 2 : reps = JS_NewArray(ctx->js_ctx);
636 :
637 2 : count = gf_dash_group_get_num_qualities(ctx->dash, group_idx);
638 6 : for (i=0; i<count; i++) {
639 : GF_DASHQualityInfo qinfo;
640 : JSValue rep;
641 : GF_Err e;
642 :
643 6 : e = gf_dash_group_get_quality_info(ctx->dash, group_idx, i, &qinfo);
644 6 : if (e) break;
645 6 : if (!qinfo.ID) qinfo.ID="";
646 6 : if (!qinfo.mime) qinfo.mime="unknown";
647 6 : if (!qinfo.codec) qinfo.codec="codec";
648 :
649 6 : rep = JS_NewObject(ctx->js_ctx);
650 6 : JS_SetPropertyStr(ctx->js_ctx, rep, "ID", JS_NewString(ctx->js_ctx, qinfo.ID));
651 6 : JS_SetPropertyStr(ctx->js_ctx, rep, "mime", JS_NewString(ctx->js_ctx, qinfo.mime));
652 6 : JS_SetPropertyStr(ctx->js_ctx, rep, "codec", JS_NewString(ctx->js_ctx, qinfo.codec));
653 12 : JS_SetPropertyStr(ctx->js_ctx, rep, "bitrate", JS_NewInt32(ctx->js_ctx, qinfo.bandwidth));
654 12 : JS_SetPropertyStr(ctx->js_ctx, rep, "disabled", JS_NewBool(ctx->js_ctx, qinfo.disabled));
655 6 : if (qinfo.width && qinfo.height) {
656 : JSValue frac;
657 8 : JS_SetPropertyStr(ctx->js_ctx, rep, "width", JS_NewInt32(ctx->js_ctx, qinfo.width));
658 8 : JS_SetPropertyStr(ctx->js_ctx, rep, "height", JS_NewInt32(ctx->js_ctx, qinfo.height));
659 8 : JS_SetPropertyStr(ctx->js_ctx, rep, "interlaced", JS_NewBool(ctx->js_ctx, qinfo.interlaced));
660 4 : frac = JS_NewObject(ctx->js_ctx);
661 8 : JS_SetPropertyStr(ctx->js_ctx, frac, "n", JS_NewInt32(ctx->js_ctx, qinfo.fps_num));
662 8 : JS_SetPropertyStr(ctx->js_ctx, frac, "d", JS_NewInt32(ctx->js_ctx, qinfo.fps_den ? qinfo.fps_den : 1));
663 4 : JS_SetPropertyStr(ctx->js_ctx, rep, "fps", frac);
664 :
665 4 : frac = JS_NewObject(ctx->js_ctx);
666 8 : JS_SetPropertyStr(ctx->js_ctx, frac, "n", JS_NewInt32(ctx->js_ctx, qinfo.par_num));
667 8 : JS_SetPropertyStr(ctx->js_ctx, frac, "d", JS_NewInt32(ctx->js_ctx, qinfo.par_den ? qinfo.par_den : qinfo.par_num));
668 4 : JS_SetPropertyStr(ctx->js_ctx, rep, "sar", frac);
669 : }
670 6 : if (qinfo.sample_rate) {
671 4 : JS_SetPropertyStr(ctx->js_ctx, rep, "samplerate", JS_NewInt32(ctx->js_ctx, qinfo.sample_rate));
672 4 : JS_SetPropertyStr(ctx->js_ctx, rep, "channels", JS_NewInt32(ctx->js_ctx, qinfo.nb_channels));
673 : }
674 12 : JS_SetPropertyStr(ctx->js_ctx, rep, "ast_offset", JS_NewFloat64(ctx->js_ctx, qinfo.ast_offset));
675 12 : JS_SetPropertyStr(ctx->js_ctx, rep, "avg_duration", JS_NewFloat64(ctx->js_ctx, qinfo.average_duration));
676 :
677 6 : if (qinfo.seg_urls) {
678 0 : u32 k=0, nb_segs=gf_list_count(qinfo.seg_urls);
679 0 : JSValue seg_sizes = JS_NewArray(ctx->js_ctx);
680 0 : for (k=0; k<nb_segs; k++) {
681 0 : GF_MPD_SegmentURL *surl = gf_list_get((GF_List *) qinfo.seg_urls, k);
682 0 : u64 size = (1 + surl->media_range->end_range - surl->media_range->start_range);
683 0 : JS_SetPropertyUint32(ctx->js_ctx, seg_sizes, k, JS_NewInt64(ctx->js_ctx, size) );
684 : }
685 0 : JS_SetPropertyStr(ctx->js_ctx, rep, "sizes", seg_sizes);
686 : } else {
687 6 : JS_SetPropertyStr(ctx->js_ctx, rep, "sizes", JS_NULL);
688 : }
689 6 : JS_SetPropertyUint32(ctx->js_ctx, reps, i, rep);
690 : }
691 :
692 2 : JS_SetPropertyStr(ctx->js_ctx, obj, "qualities", reps);
693 :
694 2 : res = JS_Call(ctx->js_ctx, ctx->new_group_fun, ctx->js_obj, 1, &obj);
695 2 : JS_FreeValue(ctx->js_ctx, obj);
696 2 : JS_FreeValue(ctx->js_ctx, res);
697 2 : }
698 : #endif
699 :
700 260 : static void dashdmx_declare_group(GF_DASHDmxCtx *ctx, u32 group_idx)
701 : {
702 260 : if (ctx->on_new_group)
703 3 : ctx->on_new_group(ctx->rt_udta, group_idx, ctx->dash);
704 : #ifdef GPAC_HAS_QJS
705 257 : else if (ctx->js_ctx && JS_IsFunction(ctx->js_ctx, ctx->new_group_fun)) {
706 2 : dashdmx_js_declare_group(ctx, group_idx);
707 : }
708 : #endif
709 260 : }
710 :
711 :
712 10 : void dashdmx_io_manifest_updated(GF_DASHFileIO *dashio, const char *manifest_name, const char *cache_url, s32 group_idx)
713 : {
714 : u8 *manifest_payload;
715 : u32 manifest_payload_len;
716 10 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)dashio->udta;
717 :
718 10 : if (gf_file_load_data(cache_url, &manifest_payload, &manifest_payload_len) == GF_OK) {
719 : u8 *output;
720 :
721 : //strip baseURL since we are recording, links are already resolved
722 10 : char *man_pay_start = manifest_payload;
723 0 : while (1) {
724 : u32 end_len, offset;
725 10 : manifest_payload_len = (u32) strlen(manifest_payload);
726 10 : char *base_url_start = strstr(man_pay_start, "<BaseURL>");
727 10 : if (!base_url_start) break;
728 0 : char *base_url_end = strstr(base_url_start, "</BaseURL>");
729 0 : if (!base_url_end) break;
730 : offset = 10;
731 0 : while (base_url_end[offset] == '\n')
732 0 : offset++;
733 0 : end_len = (u32) strlen(base_url_end+offset);
734 : memmove(base_url_start, base_url_end+offset, end_len);
735 0 : base_url_start[end_len]=0;
736 : man_pay_start = base_url_start;
737 : }
738 :
739 10 : if ((ctx->forward==DFWD_FILE) && ctx->output_mpd_pid) {
740 4 : GF_FilterPacket *pck = gf_filter_pck_new_alloc(ctx->output_mpd_pid, manifest_payload_len, &output);
741 4 : if (pck) {
742 4 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASHDmx] Manifest %s updated, forwarding\n", manifest_name));
743 4 : memcpy(output, manifest_payload, manifest_payload_len);
744 4 : gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
745 4 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(manifest_name));
746 4 : if (group_idx>=0)
747 0 : gf_filter_pck_set_property(pck, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( (u64) 1+group_idx) );
748 :
749 4 : gf_filter_pck_send(pck);
750 : }
751 6 : } else if (ctx->forward==DFWD_SBOUND_MANIFEST) {
752 6 : if (group_idx>=0) {
753 : assert(manifest_name);
754 3 : if (!ctx->hls_variants) ctx->hls_variants = gf_list_new();
755 3 : if (!ctx->hls_variants_names) ctx->hls_variants_names = gf_list_new();
756 3 : gf_list_add(ctx->hls_variants, manifest_payload);
757 3 : manifest_payload = NULL;
758 3 : gf_list_add(ctx->hls_variants_names, gf_strdup(manifest_name) );
759 :
760 : } else {
761 3 : if (ctx->manifest_payload) gf_free(ctx->manifest_payload);
762 3 : ctx->manifest_payload = manifest_payload;
763 3 : manifest_payload = NULL;
764 : }
765 : }
766 10 : if (manifest_payload)
767 4 : gf_free(manifest_payload);
768 : }
769 10 : }
770 :
771 51510 : GF_Err dashdmx_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_evt, s32 group_idx, GF_Err error_code)
772 : {
773 : GF_Err e;
774 : u32 i;
775 51510 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)dashio->udta;
776 :
777 51510 : if (dash_evt==GF_DASH_EVENT_PERIOD_SETUP_ERROR) {
778 0 : if (!ctx->initial_setup_done) {
779 0 : gf_filter_setup_failure(ctx->filter, error_code);
780 0 : ctx->initial_setup_done = GF_TRUE;
781 : }
782 : return GF_OK;
783 : }
784 :
785 51510 : if (dash_evt==GF_DASH_EVENT_SELECT_GROUPS) {
786 : #ifdef GPAC_ENABLE_COVERAGE
787 123 : if (gf_sys_is_cov_mode()) {
788 119 : gf_dash_groups_set_language(ctx->dash, gf_opts_get_key("core", "lang"));
789 : //these are not used in the test suite (require JS)
790 119 : gf_dash_switch_quality(ctx->dash, GF_TRUE, GF_TRUE);
791 : }
792 : #endif
793 123 : if (ctx->groupsel)
794 0 : gf_dash_groups_set_language(ctx->dash, gf_opts_get_key("core", "lang"));
795 :
796 : //let the player decide which group to play: we declare everything
797 : return GF_OK;
798 : }
799 :
800 : /*for all selected groups, create input service and connect to init/first segment*/
801 51387 : if (dash_evt==GF_DASH_EVENT_CREATE_PLAYBACK) {
802 : u32 nb_groups_selected = 0;
803 : //coverage of a few functions from old arch not deprecated (yet)
804 : #ifdef GPAC_ENABLE_COVERAGE
805 122 : if (gf_sys_is_cov_mode()) {
806 118 : gf_dash_is_group_selected(ctx->dash, 0);
807 118 : gf_dash_get_url(ctx->dash);
808 118 : gf_dash_group_loop_detected(ctx->dash, 0);
809 118 : gf_dash_is_dynamic_mpd(ctx->dash);
810 118 : gf_dash_group_get_language(ctx->dash, 0);
811 118 : gf_dash_group_get_num_components(ctx->dash, 0);
812 118 : gf_dash_get_utc_drift_estimate(ctx->dash);
813 :
814 :
815 : //these are not used in the test suite (require decoding)
816 118 : gf_dash_group_set_codec_stat(ctx->dash, 0, 0, 0, 0, 0, GF_FALSE, GF_FALSE);
817 118 : gf_dash_group_set_buffer_levels(ctx->dash, 0, 0, 0, 0);
818 :
819 : //these are not used in the test suite (require JS)
820 118 : if (!strcmp(ctx->algo, "none"))
821 6 : gf_dash_set_automatic_switching(ctx->dash, GF_FALSE);
822 118 : gf_dash_group_select_quality(ctx->dash, (u32) -1, NULL, 0);
823 :
824 : //these are not used yet in the test suite (require TEMI)
825 118 : gf_dash_override_ntp(ctx->dash, 0);
826 :
827 : //this is not used yet in the test suite (require user interaction)
828 118 : gf_dash_group_set_quality_degradation_hint(ctx->dash, 0, 0);
829 : }
830 : #endif
831 :
832 122 : if (ctx->on_period_reset)
833 2 : ctx->on_period_reset(ctx->rt_udta, gf_dash_is_dynamic_mpd(ctx->dash) ? 2 : 1);
834 : #ifdef GPAC_HAS_QJS
835 120 : else if (ctx->js_ctx && JS_IsFunction(ctx->js_ctx, ctx->period_reset_fun)) {
836 : JSValue arg;
837 2 : arg = JS_NewInt32(ctx->js_ctx, gf_dash_is_dynamic_mpd(ctx->dash) ? 2 : 1);
838 1 : JSValue res = JS_Call(ctx->js_ctx, ctx->period_reset_fun, ctx->js_obj, 1, &arg);
839 1 : JS_FreeValue(ctx->js_ctx, res);
840 : }
841 : #endif
842 :
843 :
844 : /*select input services if possible*/
845 260 : for (i=0; i<gf_dash_get_group_count(ctx->dash); i++) {
846 : const char *mime, *init_segment;
847 : u64 start_range, end_range;
848 : u32 j;
849 : Bool playable = GF_TRUE;
850 : //let the player decide which group to play
851 260 : if (!gf_dash_is_group_selectable(ctx->dash, i))
852 63 : continue;
853 :
854 260 : if (ctx->groupsel && !gf_dash_is_group_selected(ctx->dash, i))
855 0 : continue;
856 :
857 : j=0;
858 7 : while (1) {
859 : const char *desc_id, *desc_scheme, *desc_value;
860 267 : if (! gf_dash_group_enum_descriptor(ctx->dash, i, GF_MPD_DESC_ESSENTIAL_PROPERTIES, j, &desc_id, &desc_scheme, &desc_value))
861 : break;
862 7 : j++;
863 7 : if (!strcmp(desc_scheme, "urn:mpeg:dash:srd:2014")) {
864 : } else {
865 : playable = GF_FALSE;
866 : break;
867 : }
868 : }
869 260 : if (!playable) {
870 0 : gf_dash_group_select(ctx->dash, i, GF_FALSE);
871 0 : continue;
872 : }
873 :
874 260 : nb_groups_selected++;
875 :
876 260 : if (gf_dash_group_has_dependent_group(ctx->dash, i) >=0 ) {
877 63 : gf_dash_group_select(ctx->dash, i, GF_TRUE);
878 63 : dashdmx_declare_group(ctx, i);
879 63 : continue;
880 : }
881 :
882 197 : mime = gf_dash_group_get_segment_mime(ctx->dash, i);
883 197 : init_segment = gf_dash_group_get_segment_init_url(ctx->dash, i, &start_range, &end_range);
884 :
885 197 : e = dashdmx_load_source(ctx, i, mime, init_segment, start_range, end_range);
886 197 : if (e != GF_OK) {
887 0 : gf_dash_group_select(ctx->dash, i, GF_FALSE);
888 : } else {
889 : u32 w, h;
890 : /*connect our media service*/
891 197 : gf_dash_group_get_video_info(ctx->dash, i, &w, &h);
892 197 : if (w && h && w>ctx->width && h>ctx->height) {
893 114 : ctx->width = w;
894 114 : ctx->height = h;
895 : }
896 197 : if (gf_dash_group_get_srd_max_size_info(ctx->dash, i, &w, &h)) {
897 7 : ctx->width = w;
898 7 : ctx->height = h;
899 : }
900 197 : dashdmx_declare_group(ctx, i);
901 : }
902 : }
903 :
904 122 : if (!ctx->initial_setup_done) {
905 118 : ctx->initial_setup_done = GF_TRUE;
906 : }
907 :
908 122 : if (!nb_groups_selected) {
909 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] No groups selectable, not playing !\n"));
910 : }
911 :
912 :
913 : //we had a seek outside of the period we were setting up, during period setup !
914 : //request the seek again from the player
915 : if (ctx->seek_request>=0) {
916 : #ifdef FILTER_FIXME
917 : GF_NetworkCommand com;
918 : memset(&com, 0, sizeof(GF_NetworkCommand));
919 : com.command_type = GF_NET_SERVICE_SEEK;
920 : com.play.start_range = ctx->seek_request;
921 : ctx->seek_request = 0;
922 : gf_service_command(ctx->service, &com, GF_OK);
923 : #endif
924 :
925 : }
926 122 : return GF_OK;
927 : }
928 :
929 : /*for all running services, stop service*/
930 51265 : if (dash_evt==GF_DASH_EVENT_DESTROY_PLAYBACK) {
931 262 : for (i=0; i<gf_dash_get_group_count(ctx->dash); i++) {
932 262 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, i);
933 262 : if (!group) continue;
934 197 : if (group->seg_filter_src) {
935 197 : gf_filter_remove_src(ctx->filter, group->seg_filter_src);
936 197 : group->seg_filter_src = NULL;
937 : }
938 197 : if (group->template) gf_free(group->template);
939 197 : gf_free(group);
940 197 : gf_dash_set_group_udta(ctx->dash, i, NULL);
941 : }
942 275 : for (i=0; i<gf_filter_get_opid_count(ctx->filter); i++) {
943 275 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i);
944 275 : gf_filter_pid_set_udta(opid, NULL);
945 : }
946 :
947 244 : if (ctx->on_period_reset)
948 4 : ctx->on_period_reset(ctx->rt_udta, 0);
949 : #ifdef GPAC_HAS_QJS
950 240 : else if (ctx->js_ctx && JS_IsFunction(ctx->js_ctx, ctx->period_reset_fun)) {
951 4 : JSValue arg = JS_NewInt32(ctx->js_ctx, 0);
952 2 : JSValue res = JS_Call(ctx->js_ctx, ctx->period_reset_fun, ctx->js_obj, 1, &arg);
953 2 : JS_FreeValue(ctx->js_ctx, res);
954 : }
955 : #endif
956 :
957 : return GF_OK;
958 : }
959 51021 : if (dash_evt==GF_DASH_EVENT_ABORT_DOWNLOAD) {
960 0 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, group_idx);
961 0 : if (group) {
962 : GF_FilterEvent evt;
963 0 : GF_FEVT_INIT(evt, GF_FEVT_SOURCE_SWITCH, NULL);
964 0 : evt.seek.start_offset = -1;
965 : evt.seek.source_switch = NULL;
966 0 : gf_filter_send_event(group->seg_filter_src, &evt, GF_FALSE);
967 : }
968 : return GF_OK;
969 : }
970 :
971 51021 : if (dash_evt==GF_DASH_EVENT_QUALITY_SWITCH) {
972 518 : if (group_idx>=0) {
973 518 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, group_idx);
974 518 : if (!group) {
975 268 : group_idx = gf_dash_group_has_dependent_group(ctx->dash, group_idx);
976 268 : group = gf_dash_get_group_udta(ctx->dash, group_idx);
977 : }
978 518 : if (group) {
979 762 : for (i=0; i<gf_filter_get_opid_count(ctx->filter); i++) {
980 : s32 sel = -1;
981 762 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i);
982 762 : if (gf_filter_pid_get_udta(opid) != group) continue;
983 :
984 561 : sel = gf_dash_group_get_active_quality(ctx->dash, group_idx);
985 561 : if (!gf_sys_is_test_mode() || (ctx->forward==DFWD_FILE)) {
986 411 : if (sel>=0) {
987 411 : gf_filter_pid_set_property_str(opid, "has:selected", &PROP_UINT(sel) );
988 : }
989 411 : gf_filter_pid_set_property_str(opid, "has:auto", &PROP_UINT(gf_dash_get_automatic_switching(ctx->dash) ) );
990 411 : gf_filter_pid_set_property_str(opid, "has:tilemode", &PROP_UINT(gf_dash_get_tile_adaptation_mode(ctx->dash) ) );
991 : }
992 :
993 : //setup some info for consuming filters
994 561 : if (ctx->forward) {
995 : GF_DASHQualityInfo q;
996 :
997 : //we dispatch timing in milliseconds
998 78 : if (ctx->forward==DFWD_FILE) {
999 33 : gf_filter_pid_set_property(opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(1000));
1000 : }
1001 :
1002 78 : if (gf_dash_group_get_quality_info(ctx->dash, group_idx, sel, &q)==GF_OK) {
1003 78 : if (q.bandwidth)
1004 75 : gf_filter_pid_set_property(opid, GF_PROP_PID_BITRATE, &PROP_UINT(q.bandwidth));
1005 78 : if (q.codec)
1006 75 : gf_filter_pid_set_property(opid, GF_PROP_PID_CODEC, &PROP_STRING(q.codec));
1007 : }
1008 78 : if (group->template) gf_free(group->template);
1009 78 : group->template = gf_dash_group_get_template(ctx->dash, group_idx);
1010 78 : if (!group->template) {
1011 0 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASHDmx] Cannot extract template string for %s\n", gf_dash_get_url(ctx->dash) ));
1012 0 : gf_filter_pid_set_property(opid, GF_PROP_PID_TEMPLATE, NULL);
1013 : } else {
1014 78 : gf_filter_pid_set_property(opid, GF_PROP_PID_TEMPLATE, &PROP_STRING(group->template) );
1015 78 : char *sep = strchr(group->template, '$');
1016 78 : if (sep) sep[0] = 0;
1017 : }
1018 78 : if (ctx->forward==DFWD_FILE) {
1019 : u32 dur, timescale;
1020 33 : gf_filter_pid_set_property(opid, GF_PROP_PID_REP_ID, &PROP_STRING(q.ID) );
1021 :
1022 33 : gf_dash_group_get_segment_duration(ctx->dash, group->idx, &dur, ×cale);
1023 33 : gf_filter_pid_set_property(opid, GF_PROP_PID_DASH_DUR, &PROP_FRAC_INT(dur, timescale) );
1024 :
1025 : }
1026 : }
1027 : }
1028 : }
1029 : }
1030 : return GF_OK;
1031 : }
1032 50503 : if (dash_evt==GF_DASH_EVENT_TIMESHIFT_UPDATE) {
1033 192 : Double timeshift = gf_dash_get_timeshift_buffer_pos(ctx->dash);
1034 740 : for (i=0; i<gf_filter_get_opid_count(ctx->filter); i++) {
1035 548 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i);
1036 548 : gf_filter_pid_set_info(opid, GF_PROP_PID_TIMESHIFT_TIME, &PROP_DOUBLE(timeshift) );
1037 : }
1038 : return GF_OK;
1039 : }
1040 50311 : if (dash_evt==GF_DASH_EVENT_TIMESHIFT_OVERFLOW) {
1041 214 : u32 evttype = (group_idx>=0) ? 2 : 1;
1042 621 : for (i=0; i<gf_filter_get_opid_count(ctx->filter); i++) {
1043 407 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i);
1044 407 : gf_filter_pid_set_info(opid, GF_PROP_PID_TIMESHIFT_STATE, &PROP_UINT(evttype) );
1045 : }
1046 : }
1047 :
1048 50311 : if (dash_evt==GF_DASH_EVENT_CODEC_STAT_QUERY) {
1049 2467 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, group_idx);
1050 17107 : for (i=0; i<gf_filter_get_opid_count(ctx->filter); i++) {
1051 15667 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i);
1052 15667 : if (gf_filter_pid_get_udta(opid) == group) {
1053 : GF_FilterPidStatistics stats;
1054 : Bool is_decoder = GF_TRUE;
1055 : //collect decoder stats, or if not found direct output
1056 1027 : if (gf_filter_pid_get_statistics(opid, &stats, GF_STATS_DECODER_SINK) != GF_OK) {
1057 : is_decoder = GF_FALSE;
1058 1010 : if (gf_filter_pid_get_statistics(opid, &stats, GF_STATS_LOCAL_INPUTS) != GF_OK)
1059 0 : continue;
1060 : }
1061 :
1062 1027 : if (stats.disconnected)
1063 0 : continue;
1064 :
1065 1027 : if (is_decoder) {
1066 17 : if (!stats.nb_processed) stats.nb_processed=1;
1067 17 : if (!stats.nb_saps) stats.nb_saps=1;
1068 17 : gf_dash_group_set_codec_stat(ctx->dash, group_idx, (u32) (stats.total_process_time/stats.nb_processed), stats.max_process_time, (u32) (stats.total_sap_process_time/stats.nb_saps), stats.max_sap_process_time, GF_FALSE, GF_FALSE);
1069 : }
1070 :
1071 1027 : gf_dash_group_set_buffer_levels(ctx->dash, group_idx, (u32) (stats.min_playout_time/1000), (u32) (stats.max_buffer_time/1000), (u32) (stats.buffer_time/1000) );
1072 :
1073 1027 : break;
1074 : }
1075 : }
1076 : }
1077 : return GF_OK;
1078 : }
1079 :
1080 259 : static void dashdmx_setup_buffer(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
1081 : {
1082 : u32 buffer_ms, play_buf_ms;
1083 259 : gf_filter_get_output_buffer_max(ctx->filter, &buffer_ms, &play_buf_ms);
1084 259 : buffer_ms /= 1000;
1085 259 : play_buf_ms /= 1000;
1086 :
1087 : //use min buffer from MPD
1088 259 : if (ctx->use_bmin) {
1089 2 : u64 mpd_buffer_ms = gf_dash_get_min_buffer_time(ctx->dash);
1090 2 : if (mpd_buffer_ms > buffer_ms)
1091 2 : buffer_ms = (u32) mpd_buffer_ms;
1092 : }
1093 259 : if (buffer_ms) {
1094 259 : gf_dash_set_user_buffer(ctx->dash, buffer_ms);
1095 : }
1096 259 : gf_dash_group_set_max_buffer_playout(ctx->dash, group->idx, play_buf_ms);
1097 259 : }
1098 :
1099 : /*check in all groups if the service can support reverse playback (speed<0); return GF_OK only if service is supported in all groups*/
1100 553 : static u32 dashdmx_dash_playback_mode(GF_DASHDmxCtx *ctx)
1101 : {
1102 553 : u32 pmode, mode, i, count = gf_filter_get_ipid_count(ctx->filter);
1103 : mode = GF_PLAYBACK_MODE_REWIND;
1104 2304 : for (i=0; i<count; i++) {
1105 : const GF_PropertyValue *p;
1106 2304 : GF_FilterPid *pid = gf_filter_get_ipid(ctx->filter, i);
1107 2304 : if (ctx->mpd_pid == pid) continue;
1108 :
1109 1751 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAYBACK_MODE);
1110 1751 : pmode = p ? p->value.uint : GF_PLAYBACK_MODE_REWIND;
1111 1751 : if (pmode < mode) mode = pmode;
1112 : }
1113 553 : return mode;
1114 : }
1115 :
1116 :
1117 553 : static s32 dashdmx_group_idx_from_pid(GF_DASHDmxCtx *ctx, GF_FilterPid *src_pid)
1118 : {
1119 : s32 i;
1120 :
1121 134 : for (i=0; (u32) i < gf_dash_get_group_count(ctx->dash); i++) {
1122 687 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, i);
1123 687 : if (!group) continue;
1124 687 : if (gf_filter_pid_is_filter_in_parents(src_pid, group->seg_filter_src))
1125 : return i;
1126 : }
1127 : return -1;
1128 : }
1129 :
1130 159 : static GF_FilterPid *dashdmx_opid_from_group(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
1131 : {
1132 : s32 i;
1133 :
1134 0 : for (i=0; (u32) i < gf_filter_get_opid_count(ctx->filter); i++) {
1135 159 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i );
1136 159 : GF_DASHGroup *g = gf_filter_pid_get_udta( opid );
1137 159 : if (g == group) return opid;
1138 : }
1139 : return NULL;
1140 : }
1141 :
1142 267 : static GF_FilterPid *dashdmx_create_output_pid(GF_DASHDmxCtx *ctx, GF_FilterPid *input, u32 *run_status)
1143 : {
1144 : u32 global_score=0;
1145 : GF_FilterPid *output_pid = NULL;
1146 267 : u32 i, count = gf_filter_get_opid_count(ctx->filter);
1147 : const GF_PropertyValue *codec, *streamtype, *role, *lang;
1148 :
1149 267 : *run_status = 0;
1150 :
1151 267 : streamtype = gf_filter_pid_get_property(input, GF_PROP_PID_STREAM_TYPE);
1152 267 : if (streamtype && streamtype->value.uint==GF_STREAM_ENCRYPTED)
1153 19 : streamtype = gf_filter_pid_get_property(input, GF_PROP_PID_ORIG_STREAM_TYPE);
1154 :
1155 267 : codec = gf_filter_pid_get_property(input, GF_PROP_PID_CODECID);
1156 267 : role = gf_filter_pid_get_property(input, GF_PROP_PID_ROLE);
1157 267 : lang = gf_filter_pid_get_property(input, GF_PROP_PID_LANGUAGE);
1158 :
1159 429 : for (i=0; i<count; i++) {
1160 : u32 score;
1161 : const GF_PropertyValue *o_codec, *o_streamtype, *o_role, *o_lang;
1162 429 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i);
1163 : //in use by us
1164 429 : if (gf_filter_pid_get_udta(opid)) continue;
1165 15 : if (opid == ctx->output_mpd_pid) continue;
1166 :
1167 8 : o_streamtype = gf_filter_pid_get_property(opid, GF_PROP_PID_STREAM_TYPE);
1168 8 : if (o_streamtype && o_streamtype->value.uint==GF_STREAM_ENCRYPTED)
1169 0 : o_streamtype = gf_filter_pid_get_property(opid, GF_PROP_PID_ORIG_STREAM_TYPE);
1170 :
1171 8 : o_codec = gf_filter_pid_get_property(opid, GF_PROP_PID_CODECID);
1172 8 : o_role = gf_filter_pid_get_property(opid, GF_PROP_PID_ROLE);
1173 8 : o_lang = gf_filter_pid_get_property(opid, GF_PROP_PID_LANGUAGE);
1174 :
1175 8 : if (!o_streamtype || !streamtype || !gf_props_equal(streamtype, o_streamtype))
1176 2 : continue;
1177 :
1178 : score = 1;
1179 : //get highest priority for streams with same role
1180 6 : if (o_role && role && gf_props_equal(role, o_role)) score += 10;
1181 : //then high priority for streams with same lang
1182 6 : if (o_lang && lang && gf_props_equal(lang, o_lang)) score += 5;
1183 :
1184 : //otherwise favour streams with same codec
1185 6 : if (!o_codec && codec && gf_props_equal(codec, o_codec)) score++;
1186 :
1187 6 : if (global_score<score) {
1188 : global_score = score;
1189 : output_pid = opid;
1190 : }
1191 : }
1192 267 : if (output_pid) {
1193 6 : *run_status = gf_filter_pid_is_playing(output_pid) ? 1 : 2;
1194 : return output_pid;
1195 : }
1196 : //none found create a new PID
1197 261 : return gf_filter_pid_new(ctx->filter);
1198 : }
1199 :
1200 3918 : Bool dashdmx_merge_prop(void *cbk, u32 prop_4cc, const char *prop_name, const GF_PropertyValue *src_prop)
1201 : {
1202 : const GF_PropertyValue *p;
1203 : GF_FilterPid *pid = (GF_FilterPid *) cbk;
1204 :
1205 3918 : if (prop_4cc) p = gf_filter_pid_get_property(pid, prop_4cc);
1206 60 : else p = gf_filter_pid_get_property_str(pid, prop_name);
1207 3918 : if (p) return GF_FALSE;
1208 56 : return GF_TRUE;
1209 : }
1210 :
1211 553 : static void dashdmx_declare_properties(GF_DASHDmxCtx *ctx, GF_DASHGroup *group, u32 group_idx, GF_FilterPid *opid, GF_FilterPid *ipid, Bool is_period_switch)
1212 : {
1213 : GF_DASHQualityInfo qinfo;
1214 : GF_PropertyValue qualities, srd, srdref;
1215 : GF_Err e;
1216 : u32 stream_type = GF_STREAM_UNKNOWN;
1217 : u32 count, i;
1218 : u32 dur, mode;
1219 :
1220 553 : gf_filter_pid_copy_properties(opid, ipid);
1221 :
1222 553 : if (!group->nb_group_deps) {
1223 : s32 asid;
1224 : char as_name[100];
1225 413 : asid = gf_dash_group_get_as_id(ctx->dash, group_idx);
1226 : //TODO: remove and regenerate hashes
1227 413 : if (gf_sys_is_test_mode() && (asid<=0)) {
1228 368 : sprintf(as_name, "AS%d", group_idx+1);
1229 45 : } else if (asid<0) {
1230 5 : sprintf(as_name, "AS_%d", group_idx+1);
1231 : } else {
1232 : sprintf(as_name, "AS%d", asid);
1233 : }
1234 413 : gf_filter_pid_set_name(opid, as_name);
1235 : }
1236 :
1237 553 : mode = dashdmx_dash_playback_mode(ctx);
1238 553 : gf_filter_pid_set_property(opid, GF_PROP_PID_PLAYBACK_MODE, &PROP_UINT(mode));
1239 :
1240 553 : if (ctx->max_res && gf_filter_pid_get_property(ipid, GF_PROP_PID_WIDTH)) {
1241 445 : gf_filter_pid_set_property(opid, GF_PROP_SERVICE_WIDTH, &PROP_UINT(ctx->width));
1242 445 : gf_filter_pid_set_property(opid, GF_PROP_SERVICE_HEIGHT, &PROP_UINT(ctx->height));
1243 : }
1244 :
1245 553 : dur = (u32) (1000*gf_dash_get_duration(ctx->dash) );
1246 553 : if (dur>0)
1247 463 : gf_filter_pid_set_property(opid, GF_PROP_PID_DURATION, &PROP_FRAC64_INT(dur, 1000) );
1248 : else
1249 90 : gf_filter_pid_set_property(opid, GF_PROP_PID_DURATION, NULL);
1250 :
1251 553 : dur = (1000*gf_dash_group_get_time_shift_buffer_depth(ctx->dash, group_idx) );
1252 553 : if (dur>0)
1253 0 : gf_filter_pid_set_property(opid, GF_PROP_PID_TIMESHIFT_DEPTH, &PROP_FRAC_INT(dur, 1000) );
1254 :
1255 :
1256 553 : if (ctx->use_bmin) {
1257 3 : u32 max = gf_dash_get_min_buffer_time(ctx->dash);
1258 3 : gf_filter_pid_set_property(opid, GF_PROP_PID_PLAY_BUFFER, &PROP_UINT(max));
1259 : }
1260 :
1261 : memset(&qualities, 0, sizeof(GF_PropertyValue));
1262 553 : qualities.type = GF_PROP_STRING_LIST;
1263 :
1264 553 : count = gf_dash_group_get_num_qualities(ctx->dash, group_idx);
1265 821 : for (i=0; i<count; i++) {
1266 : char szInfo[500];
1267 821 : char *qdesc = NULL;
1268 :
1269 821 : e = gf_dash_group_get_quality_info(ctx->dash, group_idx, i, &qinfo);
1270 821 : if (e) break;
1271 821 : if (!qinfo.ID) qinfo.ID="";
1272 821 : if (!qinfo.mime) qinfo.mime="unknown";
1273 821 : if (!qinfo.codec) qinfo.codec="codec";
1274 :
1275 821 : if (qinfo.width && qinfo.height) {
1276 : stream_type = GF_STREAM_VISUAL;
1277 102 : } else if (qinfo.sample_rate || qinfo.nb_channels) {
1278 : stream_type = GF_STREAM_AUDIO;
1279 25 : } else if (strstr(qinfo.mime, "text")
1280 25 : || strstr(qinfo.codec, "vtt")
1281 23 : || strstr(qinfo.codec, "srt")
1282 23 : || strstr(qinfo.codec, "text")
1283 23 : || strstr(qinfo.codec, "tx3g")
1284 22 : || strstr(qinfo.codec, "stxt")
1285 22 : || strstr(qinfo.codec, "stpp")
1286 : ) {
1287 : stream_type = GF_STREAM_TEXT;
1288 : }
1289 :
1290 821 : snprintf(szInfo, 500, "id=%s", qinfo.ID);
1291 :
1292 821 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1293 821 : if (e) break;
1294 :
1295 821 : snprintf(szInfo, 500, "codec=%s", qinfo.codec);
1296 821 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1297 821 : if (e) break;
1298 :
1299 821 : snprintf(szInfo, 500, "mime=%s", qinfo.mime);
1300 821 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1301 821 : if (e) break;
1302 :
1303 821 : snprintf(szInfo, 500, "bw=%d", qinfo.bandwidth);
1304 821 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1305 821 : if (e) break;
1306 :
1307 821 : if (qinfo.disabled) {
1308 0 : e = gf_dynstrcat(&qdesc, "disabled", "::");
1309 0 : if (e) break;
1310 : }
1311 :
1312 821 : if (qinfo.width && qinfo.height) {
1313 : snprintf(szInfo, 500, "w=%d", qinfo.width);
1314 719 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1315 719 : if (e) break;
1316 :
1317 719 : snprintf(szInfo, 500, "h=%d", qinfo.height);
1318 719 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1319 719 : if (e) break;
1320 :
1321 719 : if (qinfo.interlaced) {
1322 0 : e = gf_dynstrcat(&qdesc, "interlaced", "::");
1323 0 : if (e) break;
1324 : }
1325 719 : if (qinfo.fps_den) {
1326 541 : snprintf(szInfo, 500, "fps=%d/%d", qinfo.fps_num, qinfo.fps_den);
1327 : } else {
1328 178 : snprintf(szInfo, 500, "fps=%d", qinfo.fps_num);
1329 : }
1330 719 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1331 719 : if (e) break;
1332 :
1333 719 : if (qinfo.par_den && qinfo.par_num && (qinfo.par_den != qinfo.par_num)) {
1334 : snprintf(szInfo, 500, "sar=%d/%d", qinfo.par_num, qinfo.par_den);
1335 0 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1336 0 : if (e) break;
1337 : }
1338 : }
1339 821 : if (qinfo.sample_rate) {
1340 : snprintf(szInfo, 500, "sr=%d", qinfo.sample_rate);
1341 77 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1342 77 : if (e) break;
1343 :
1344 77 : snprintf(szInfo, 500, "ch=%d", qinfo.nb_channels);
1345 77 : e = gf_dynstrcat(&qdesc, szInfo, "::");
1346 77 : if (e) break;
1347 : }
1348 821 : qualities.value.string_list.vals = gf_realloc(qualities.value.string_list.vals, sizeof(char *) * (qualities.value.string_list.nb_items+1));
1349 :
1350 821 : qualities.value.string_list.vals[qualities.value.string_list.nb_items] = qdesc;
1351 821 : qualities.value.string_list.nb_items++;
1352 : qdesc = NULL;
1353 : }
1354 553 : gf_filter_pid_set_info_str(opid, "has:qualities", &qualities);
1355 :
1356 553 : if ((ctx->forward==DFWD_FILE) && (stream_type!=GF_STREAM_UNKNOWN)) {
1357 33 : gf_filter_pid_set_property(opid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(stream_type) );
1358 :
1359 33 : gf_filter_pid_set_property(opid, GF_PROP_PCK_HLS_REF, &PROP_LONGUINT( (u64) 1+group->idx) );
1360 : }
1361 :
1362 : const char *title, *source;
1363 553 : gf_dash_get_info(ctx->dash, &title, &source);
1364 553 : gf_filter_pid_set_info(opid, GF_PROP_PID_SERVICE_NAME, &PROP_STRING(title) );
1365 553 : gf_filter_pid_set_info(opid, GF_PROP_PID_SERVICE_PROVIDER, &PROP_STRING(source) );
1366 :
1367 553 : title = NULL;
1368 553 : gf_dash_group_enum_descriptor(ctx->dash, group_idx, GF_MPD_DESC_ROLE, 0, NULL, NULL, &title);
1369 553 : if (title)
1370 3 : gf_filter_pid_set_property(opid, GF_PROP_PID_ROLE, &PROP_STRING(title) );
1371 :
1372 553 : title = NULL;
1373 553 : gf_dash_group_enum_descriptor(ctx->dash, group_idx, GF_MPD_DESC_ACCESSIBILITY, 0, NULL, NULL, &title);
1374 553 : if (title)
1375 0 : gf_filter_pid_set_property_str(opid, "accessibility", &PROP_STRING(title) );
1376 :
1377 553 : title = NULL;
1378 553 : gf_dash_group_enum_descriptor(ctx->dash, group_idx, GF_MPD_DESC_RATING, 0, NULL, NULL, &title);
1379 553 : if (title)
1380 0 : gf_filter_pid_set_property_str(opid, "rating", &PROP_STRING(title) );
1381 :
1382 553 : if (!gf_sys_is_test_mode()) {
1383 45 : gf_filter_pid_set_info_str(opid, "ntpdiff", &PROP_SINT(gf_dash_get_utc_drift_estimate(ctx->dash) ) );
1384 : }
1385 :
1386 : memset(&srd, 0, sizeof(GF_PropertyValue));
1387 : memset(&srdref, 0, sizeof(GF_PropertyValue));
1388 553 : srd.type = GF_PROP_VEC4I;
1389 553 : srdref.type = GF_PROP_VEC2I;
1390 553 : if (gf_dash_group_get_srd_info(ctx->dash, group_idx, NULL,
1391 : &srd.value.vec4i.x,
1392 : &srd.value.vec4i.y,
1393 : &srd.value.vec4i.z,
1394 : &srd.value.vec4i.w,
1395 : &srdref.value.vec2i.x,
1396 : &srdref.value.vec2i.y)) {
1397 :
1398 140 : gf_filter_pid_set_property(opid, GF_PROP_PID_SRD, &srd);
1399 140 : gf_filter_pid_set_property(opid, GF_PROP_PID_SRD_REF, &srdref);
1400 : }
1401 :
1402 : //setup initial quality - this is disabled in test mode for the time being (invalidates all dash playback hashes)
1403 : //in forward mode, always send the event to setup dash templates
1404 553 : if (!gf_sys_is_test_mode() || ctx->forward)
1405 123 : dashdmx_io_on_dash_event(&ctx->dash_io, GF_DASH_EVENT_QUALITY_SWITCH, group->idx, GF_OK);
1406 :
1407 :
1408 : //if MPD file pid is defined, merge its properties. This will allow forwarding user-defined properties,
1409 : // eg -i dash.mpd:#MyProp=toto to all PIDs coming from media sources
1410 553 : if (ctx->mpd_pid) {
1411 553 : gf_filter_pid_merge_properties(opid, ctx->mpd_pid, dashdmx_merge_prop, ipid);
1412 : }
1413 :
1414 553 : if (ctx->frag_url)
1415 0 : gf_filter_pid_set_property(opid, GF_PROP_PID_ORIG_FRAG_URL, &PROP_NAME(ctx->frag_url) );
1416 :
1417 553 : if (stream_type == GF_STREAM_AUDIO) {
1418 : Bool is_cont = GF_FALSE;
1419 65 : if (is_period_switch) {
1420 : u32 j=0;
1421 0 : while (1) {
1422 : const char *desc_id, *desc_scheme, *desc_value;
1423 2 : if (! gf_dash_group_enum_descriptor(ctx->dash, group_idx, GF_MPD_DESC_SUPPLEMENTAL_PROPERTIES, j, &desc_id, &desc_scheme, &desc_value))
1424 : break;
1425 0 : j++;
1426 0 : if (!strcmp(desc_scheme, "urn:mpeg:dash:period-continuity:2015") && desc_value) {
1427 : is_cont = GF_TRUE;
1428 : break;
1429 : }
1430 : }
1431 : }
1432 2 : gf_filter_pid_set_property(opid, GF_PROP_PID_NO_PRIMING, is_cont ? &PROP_BOOL(GF_TRUE) : NULL);
1433 : }
1434 553 : if (ctx->forward > DFWD_FILE) {
1435 : u64 pstart;
1436 : u32 timescale;
1437 45 : const char *str = NULL;
1438 45 : gf_filter_pid_set_property(opid, GF_PROP_PID_DASH_FWD, &PROP_UINT( (ctx->forward - DFWD_FILE) ) );
1439 45 : gf_filter_pid_set_property(opid, GF_PROP_PID_DASH_CUE, &PROP_STRING("inband") );
1440 :
1441 45 : gf_dash_group_get_segment_duration(ctx->dash, group->idx, &dur, ×cale);
1442 45 : gf_filter_pid_set_property(opid, GF_PROP_PID_DASH_DUR, &PROP_FRAC_INT(dur, timescale) );
1443 :
1444 45 : gf_dash_group_next_seg_info(ctx->dash, group->idx, group->current_dependent_rep_idx, NULL, NULL, NULL, NULL, &str);
1445 45 : if (str) {
1446 45 : gf_filter_pid_set_property(opid, GF_PROP_PCK_FILENAME, &PROP_STRING(str) );
1447 : }
1448 :
1449 : //forward representation ID so that dasher will match muxed streams and identify streams in the MPD
1450 45 : str = gf_dash_group_get_representation_id(ctx->dash, group->idx);
1451 45 : if (str) {
1452 45 : gf_filter_pid_set_property(opid, GF_PROP_PID_REP_ID, &PROP_STRING(str) );
1453 : }
1454 45 : pstart = gf_dash_get_period_start(ctx->dash);
1455 45 : gf_filter_pid_set_property(opid, GF_PROP_PID_DASH_PERIOD_START, &PROP_LONGUINT(pstart) );
1456 : }
1457 553 : }
1458 :
1459 701 : static GF_Err dashdmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
1460 : {
1461 : s32 group_idx;
1462 : Bool is_period_switch = GF_FALSE;
1463 : GF_FilterPid *opid;
1464 : GF_Err e;
1465 701 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) gf_filter_get_udta(filter);
1466 : GF_DASHGroup *group;
1467 :
1468 701 : if (is_remove) {
1469 : //TODO
1470 30 : if (pid==ctx->mpd_pid) {
1471 : u32 i;
1472 21 : for (i=0; i<gf_filter_get_opid_count(filter); i++) {
1473 21 : opid = gf_filter_get_opid(filter, i);
1474 21 : group = gf_filter_pid_get_udta(opid);
1475 21 : if (group && group->seg_filter_src) {
1476 21 : gf_filter_remove_src(filter, group->seg_filter_src);
1477 : }
1478 : }
1479 3 : gf_dash_close(ctx->dash);
1480 3 : ctx->mpd_pid = NULL;
1481 : } else {
1482 27 : opid = gf_filter_pid_get_udta(pid);
1483 27 : if (opid) {
1484 27 : if (!ctx->mpd_pid) {
1485 21 : gf_filter_pid_remove(opid);
1486 6 : } else if (gf_dash_all_groups_done(ctx->dash) && gf_dash_in_last_period(ctx->dash, GF_TRUE)) {
1487 0 : gf_filter_pid_remove(opid);
1488 : } else {
1489 6 : gf_filter_pid_set_udta(opid, NULL);
1490 6 : gf_filter_pid_set_udta(pid, NULL);
1491 : }
1492 : }
1493 : }
1494 : return GF_OK;
1495 : }
1496 671 : if (! gf_filter_pid_check_caps(pid)) {
1497 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASHDmx] Mismatch in input pid caps\n"));
1498 : return GF_NOT_SUPPORTED;
1499 : }
1500 :
1501 : //configure MPD pid
1502 671 : if (!ctx->mpd_pid) {
1503 : char *frag;
1504 : const GF_PropertyValue *p;
1505 118 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
1506 118 : if (!p || !p->value.string) {
1507 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASHDmx] no URL on MPD pid\n"));
1508 : return GF_NOT_SUPPORTED;
1509 : }
1510 :
1511 118 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
1512 118 : ctx->mpd_pid = pid;
1513 118 : ctx->seek_request = -1;
1514 118 : ctx->nb_playing = 0;
1515 :
1516 118 : if ((ctx->forward==DFWD_FILE) && !ctx->output_mpd_pid) {
1517 4 : ctx->output_mpd_pid = gf_filter_pid_new(filter);
1518 4 : gf_filter_pid_copy_properties(ctx->output_mpd_pid, pid);
1519 4 : gf_filter_pid_set_name(ctx->output_mpd_pid, "manifest");
1520 4 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASHDmx] Creating manifest output PID\n"));
1521 : //for route
1522 4 : gf_filter_pid_set_property(ctx->output_mpd_pid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE));
1523 : }
1524 :
1525 118 : e = gf_dash_open(ctx->dash, p->value.string);
1526 118 : if (e) {
1527 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] Error - cannot initialize DASH Client for %s: %s\n", p->value.string, gf_error_to_string(e)));
1528 0 : gf_filter_setup_failure(filter, e);
1529 0 : return e;
1530 : }
1531 118 : frag = strchr(p->value.string, '#');
1532 118 : if (frag) {
1533 0 : if (ctx->frag_url) gf_free(ctx->frag_url);
1534 0 : ctx->frag_url = gf_strdup(frag+1);
1535 : }
1536 118 : if (gf_dash_is_m3u8(ctx->dash) || gf_dash_is_smooth_streaming(ctx->dash))
1537 23 : ctx->is_dash = GF_FALSE;
1538 : else
1539 95 : ctx->is_dash = GF_TRUE;
1540 :
1541 : //we have a redirect URL on mpd pid, this means this comes from a service feeding the cache so we won't get any data on the pid.
1542 : //request a process task
1543 118 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_REDIRECT_URL);
1544 118 : if (p && p->value.string) {
1545 : GF_FilterEvent evt;
1546 5 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, ctx->mpd_pid);
1547 5 : gf_filter_pid_send_event(ctx->mpd_pid, &evt);
1548 5 : gf_filter_post_process_task(filter);
1549 : }
1550 : return GF_OK;
1551 553 : } else if (ctx->mpd_pid == pid) {
1552 : return GF_OK;
1553 : }
1554 :
1555 : //figure out group for this pid
1556 553 : group_idx = dashdmx_group_idx_from_pid(ctx, pid);
1557 553 : if (group_idx<0) {
1558 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASHDmx] Failed to locate adaptation set for input pid\n"));
1559 : return GF_SERVICE_ERROR;
1560 : }
1561 553 : group = gf_dash_get_group_udta(ctx->dash, group_idx);
1562 :
1563 : //initial configure
1564 553 : opid = gf_filter_pid_get_udta(pid);
1565 553 : if (opid == NULL) {
1566 : u32 run_status;
1567 267 : group = gf_dash_get_group_udta(ctx->dash, group_idx);
1568 : assert(group);
1569 : //for now we declare every component from the input source
1570 267 : opid = dashdmx_create_output_pid(ctx, pid, &run_status);
1571 267 : gf_filter_pid_set_udta(opid, group);
1572 267 : gf_filter_pid_set_udta(pid, opid);
1573 267 : group->nb_pids ++;
1574 :
1575 267 : if (run_status) {
1576 : GF_FilterEvent evt;
1577 6 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
1578 6 : gf_filter_pid_send_event(pid, &evt);
1579 6 : group->is_playing = GF_TRUE;
1580 6 : gf_dash_group_select(ctx->dash, group->idx, GF_TRUE);
1581 :
1582 6 : if (run_status==2) {
1583 0 : gf_dash_set_group_done(ctx->dash, group->idx, GF_TRUE);
1584 0 : gf_dash_group_select(ctx->dash, group->idx, GF_FALSE);
1585 :
1586 0 : GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
1587 0 : gf_filter_pid_send_event(pid, &evt);
1588 0 : group->is_playing = GF_FALSE;
1589 : } else {
1590 : is_period_switch = GF_TRUE;
1591 : }
1592 : }
1593 : }
1594 553 : dashdmx_declare_properties(ctx, group, group_idx, opid, pid, is_period_switch);
1595 :
1596 :
1597 : //reset the file cache property (init segment could be cached but not the rest)
1598 553 : gf_filter_pid_set_property(opid, GF_PROP_PID_FILE_CACHED, NULL);
1599 :
1600 553 : return GF_OK;
1601 : }
1602 :
1603 : #ifdef GPAC_HAS_QJS
1604 :
1605 21 : static s32 dashdmx_algo_js(void *udta, u32 group, u32 base_group, Bool force_lower_complexity, GF_DASHCustomAlgoInfo *stats)
1606 : {
1607 : s32 res;
1608 : JSValue ret, args[4];
1609 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)udta;
1610 :
1611 21 : gf_js_lock(ctx->js_ctx, GF_TRUE);
1612 42 : args[0] = JS_NewInt32(ctx->js_ctx, group);
1613 42 : args[1] = JS_NewInt32(ctx->js_ctx, base_group);
1614 21 : args[2] = JS_NewBool(ctx->js_ctx, force_lower_complexity);
1615 21 : args[3] = JS_NewObject(ctx->js_ctx);
1616 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "rate", JS_NewInt32(ctx->js_ctx, stats->download_rate) );
1617 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "filesize", JS_NewInt32(ctx->js_ctx, stats->file_size) );
1618 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "speed", JS_NewFloat64(ctx->js_ctx, stats->speed) );
1619 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "max_speed", JS_NewFloat64(ctx->js_ctx, stats->max_available_speed) );
1620 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "display_width", JS_NewInt32(ctx->js_ctx, stats->disp_width) );
1621 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "display_height", JS_NewInt32(ctx->js_ctx, stats->disp_height) );
1622 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "active_quality", JS_NewInt32(ctx->js_ctx, stats->active_quality_idx) );
1623 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer_min", JS_NewInt32(ctx->js_ctx, stats->buffer_min_ms) );
1624 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer_max", JS_NewInt32(ctx->js_ctx, stats->buffer_max_ms) );
1625 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer", JS_NewInt32(ctx->js_ctx, stats->buffer_occupancy_ms) );
1626 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "degradation_hint", JS_NewInt32(ctx->js_ctx, stats->quality_degradation_hint) );
1627 42 : JS_SetPropertyStr(ctx->js_ctx, args[3], "total_rate", JS_NewInt32(ctx->js_ctx, stats->total_rate) );
1628 21 : ret = JS_Call(ctx->js_ctx, ctx->rate_fun, ctx->js_obj, 4, args);
1629 21 : JS_FreeValue(ctx->js_ctx, args[3]);
1630 :
1631 21 : if (JS_IsException(ret)) {
1632 0 : js_dump_error(ctx->js_ctx);
1633 0 : res = -3;
1634 21 : } else if (JS_ToInt32(ctx->js_ctx, &res, ret))
1635 0 : res = -1;
1636 :
1637 21 : JS_FreeValue(ctx->js_ctx, ret);
1638 21 : gf_js_lock(ctx->js_ctx, GF_FALSE);
1639 21 : return res;
1640 : }
1641 :
1642 0 : s32 dashdmx_download_monitor_js(void *udta, u32 group, u32 bits_per_sec, u64 total_bytes, u64 bytes_done, u64 us_since_start, u32 buffer_dur_ms, u32 current_seg_dur)
1643 : {
1644 0 : s32 res = -1;
1645 : JSValue ret, args[2];
1646 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)udta;
1647 :
1648 0 : gf_js_lock(ctx->js_ctx, GF_TRUE);
1649 :
1650 0 : args[0] = JS_NewInt32(ctx->js_ctx, group);
1651 0 : args[1] = JS_NewObject(ctx->js_ctx);
1652 0 : JS_SetPropertyStr(ctx->js_ctx, args[1], "bits_per_second", JS_NewInt32(ctx->js_ctx, bits_per_sec) );
1653 0 : JS_SetPropertyStr(ctx->js_ctx, args[1], "total_bytes", JS_NewInt64(ctx->js_ctx, total_bytes) );
1654 0 : JS_SetPropertyStr(ctx->js_ctx, args[1], "bytes_done", JS_NewInt64(ctx->js_ctx, bytes_done) );
1655 0 : JS_SetPropertyStr(ctx->js_ctx, args[1], "us_since_start", JS_NewInt64(ctx->js_ctx, us_since_start) );
1656 0 : JS_SetPropertyStr(ctx->js_ctx, args[1], "buffer_duration", JS_NewInt32(ctx->js_ctx, buffer_dur_ms) );
1657 0 : JS_SetPropertyStr(ctx->js_ctx, args[1], "current_segment_duration", JS_NewInt32(ctx->js_ctx, current_seg_dur) );
1658 0 : ret = JS_Call(ctx->js_ctx, ctx->download_fun, ctx->js_obj, 2, args);
1659 0 : JS_FreeValue(ctx->js_ctx, args[1]);
1660 0 : if (JS_ToInt32(ctx->js_ctx, &res, ret))
1661 0 : res = -1;
1662 0 : JS_FreeValue(ctx->js_ctx, ret);
1663 :
1664 0 : gf_js_lock(ctx->js_ctx, GF_FALSE);
1665 0 : return res;
1666 : }
1667 118 : void dashdmx_cleanup_js(GF_DASHDmxCtx *ctx)
1668 : {
1669 118 : if (ctx->js_ctx) {
1670 1 : gf_js_lock(ctx->js_ctx, GF_TRUE);
1671 1 : JS_FreeValue(ctx->js_ctx, ctx->rate_fun);
1672 1 : JS_FreeValue(ctx->js_ctx, ctx->download_fun);
1673 1 : JS_FreeValue(ctx->js_ctx, ctx->new_group_fun);
1674 1 : JS_FreeValue(ctx->js_ctx, ctx->period_reset_fun);
1675 :
1676 1 : if (!ctx->owns_context)
1677 1 : JS_FreeValue(ctx->js_ctx, ctx->js_obj);
1678 :
1679 1 : gf_js_lock(ctx->js_ctx, GF_FALSE);
1680 1 : if (ctx->owns_context)
1681 0 : gf_js_delete_context(ctx->js_ctx);
1682 1 : ctx->js_ctx = NULL;
1683 1 : ctx->owns_context = GF_FALSE;
1684 1 : ctx->rate_fun = ctx->download_fun = ctx->new_group_fun= ctx->period_reset_fun = JS_UNDEFINED;
1685 : }
1686 118 : }
1687 :
1688 : #define GET_FUN(_field, _name) \
1689 : dashctx->_field = JS_GetPropertyStr(ctx, dashctx->js_obj, _name); \
1690 : if (! JS_IsFunction(ctx, dashctx->_field)) {\
1691 : JS_FreeValue(dashctx->js_ctx, dashctx->_field); \
1692 : dashctx->_field = JS_NULL;\
1693 : }
1694 :
1695 :
1696 1 : JSValue dashdmx_bind_js(GF_Filter *f, JSContext *ctx, JSValueConst obj)
1697 : {
1698 1 : GF_DASHDmxCtx *dashctx = (GF_DASHDmxCtx *) gf_filter_get_udta(f);
1699 : JSValue rate_fun;
1700 :
1701 1 : rate_fun = JS_GetPropertyStr(ctx, obj, "rate_adaptation");
1702 1 : if (! JS_IsFunction(ctx, rate_fun)) {
1703 : JS_FreeValue(ctx, rate_fun);
1704 0 : return js_throw_err_msg(ctx, GF_BAD_PARAM, "Object does not define a rate_adaptation function\n");
1705 : }
1706 :
1707 1 : if (dashctx->js_ctx) {
1708 0 : dashdmx_cleanup_js(dashctx);
1709 : }
1710 1 : dashctx->js_ctx = ctx;
1711 1 : dashctx->js_obj = JS_DupValue(ctx, obj);
1712 1 : dashctx->rate_fun = rate_fun;
1713 :
1714 1 : GET_FUN(download_fun, "download_monitor")
1715 1 : GET_FUN(new_group_fun, "new_group")
1716 1 : GET_FUN(period_reset_fun, "period_reset")
1717 :
1718 1 : if (JS_IsFunction(ctx, dashctx->download_fun)) {
1719 1 : gf_dash_set_algo_custom(dashctx->dash, dashctx, dashdmx_algo_js, dashdmx_download_monitor_js);
1720 1 : dashctx->abort = GF_TRUE;
1721 : } else {
1722 0 : gf_dash_set_algo_custom(dashctx->dash, dashctx, dashdmx_algo_js, NULL);
1723 0 : dashctx->abort = GF_FALSE;
1724 : }
1725 1 : return JS_UNDEFINED;
1726 : }
1727 :
1728 :
1729 0 : static GF_Err dashdmx_initialize_js(GF_DASHDmxCtx *dashctx, char *jsfile)
1730 : {
1731 : JSContext *ctx;
1732 : JSValue global_obj, ret;
1733 : u8 *buf;
1734 : u32 buf_len;
1735 : GF_Err e;
1736 : u32 flags = JS_EVAL_TYPE_GLOBAL;
1737 :
1738 0 : e = gf_file_load_data(jsfile, &buf, &buf_len);
1739 0 : if (e) return e;
1740 :
1741 0 : ctx = gf_js_create_context();
1742 0 : if (!ctx) {
1743 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DASHDmx] Failed to load QuickJS context\n"));
1744 0 : if (buf) gf_free(buf);
1745 : return GF_IO_ERR;
1746 : }
1747 0 : JS_SetContextOpaque(ctx, dashctx);
1748 0 : dashctx->owns_context = GF_TRUE;
1749 :
1750 0 : global_obj = JS_GetGlobalObject(ctx);
1751 0 : js_load_constants(ctx, global_obj);
1752 0 : dashctx->js_ctx = ctx;
1753 :
1754 0 : JS_SetPropertyStr(dashctx->js_ctx, global_obj, "_gpac_log_name", JS_NewString(dashctx->js_ctx, gf_file_basename(jsfile) ) );
1755 0 : dashctx->js_obj = JS_NewObject(dashctx->js_ctx);
1756 0 : JS_SetPropertyStr(dashctx->js_ctx, global_obj, "dashin", dashctx->js_obj);
1757 :
1758 0 : if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((char *)buf, buf_len)) {
1759 : //init modules, except webgl
1760 0 : qjs_module_init_gpaccore(dashctx->js_ctx);
1761 0 : qjs_module_init_xhr(dashctx->js_ctx);
1762 0 : qjs_module_init_evg(dashctx->js_ctx);
1763 0 : qjs_module_init_storage(dashctx->js_ctx);
1764 : flags = JS_EVAL_TYPE_MODULE;
1765 : }
1766 :
1767 0 : ret = JS_Eval(dashctx->js_ctx, (char *)buf, buf_len, jsfile, flags);
1768 0 : gf_free(buf);
1769 :
1770 0 : if (JS_IsException(ret)) {
1771 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DASHDmx] Error loading script %s\n", jsfile));
1772 0 : js_dump_error(dashctx->js_ctx);
1773 0 : JS_FreeValue(dashctx->js_ctx, ret);
1774 0 : JS_FreeValue(dashctx->js_ctx, global_obj);
1775 : return GF_BAD_PARAM;
1776 : }
1777 0 : JS_FreeValue(dashctx->js_ctx, ret);
1778 0 : JS_FreeValue(dashctx->js_ctx, global_obj);
1779 :
1780 0 : dashctx->rate_fun = JS_GetPropertyStr(ctx, dashctx->js_obj, "rate_adaptation");
1781 0 : if (! JS_IsFunction(ctx, dashctx->rate_fun)) {
1782 0 : JS_FreeValue(dashctx->js_ctx, dashctx->rate_fun);
1783 0 : dashctx->rate_fun = JS_UNDEFINED;
1784 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DASHDmx] JS file does not define a rate_adaptation function in dasher object\n"));
1785 : return GF_BAD_PARAM;
1786 : }
1787 :
1788 :
1789 0 : GET_FUN(download_fun, "download_monitor")
1790 0 : GET_FUN(new_group_fun, "new_group")
1791 0 : GET_FUN(period_reset_fun, "period_reset")
1792 : return GF_OK;
1793 : }
1794 :
1795 : #endif
1796 :
1797 : static const GF_FilterCapability DASHDmxFileModeCaps[] =
1798 : {
1799 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1800 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mpd|m3u8|3gm|ism"),
1801 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "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"),
1802 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
1803 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1804 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
1805 : {0},
1806 : //accept only file streams and produce them
1807 : { .code=GF_PROP_PID_STREAM_TYPE, .val.type=GF_PROP_UINT, .val.value.uint=GF_STREAM_FILE, .flags=(GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_OUTPUT|GF_CAPFLAG_LOADED_FILTER) },
1808 : };
1809 :
1810 118 : static GF_Err dashdmx_initialize(GF_Filter *filter)
1811 : {
1812 : u32 timeshift;
1813 118 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) gf_filter_get_udta(filter);
1814 : GF_DASHAdaptationAlgorithm algo = GF_DASH_ALGO_NONE;
1815 : const char *algo_str;
1816 118 : ctx->filter = filter;
1817 118 : ctx->dm = gf_filter_get_download_manager(filter);
1818 118 : if (!ctx->dm) return GF_SERVICE_ERROR;
1819 :
1820 : //old syntax
1821 118 : if (ctx->filemode) {
1822 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASHDmx] `filemode` option will soon be deprecated, update your script to use `:forward=file` option.\n"));
1823 0 : ctx->forward = DFWD_FILE;
1824 0 : ctx->filemode = GF_FALSE;
1825 : }
1826 :
1827 118 : ctx->dash_io.udta = ctx;
1828 118 : ctx->dash_io.delete_cache_file = dashdmx_io_delete_cache_file;
1829 118 : ctx->dash_io.create = dashdmx_io_create;
1830 118 : ctx->dash_io.del = dashdmx_io_del;
1831 118 : ctx->dash_io.init = dashdmx_io_init;
1832 118 : ctx->dash_io.run = dashdmx_io_run;
1833 118 : ctx->dash_io.get_url = dashdmx_io_get_url;
1834 118 : ctx->dash_io.get_cache_name = dashdmx_io_get_cache_name;
1835 118 : ctx->dash_io.get_mime = dashdmx_io_get_mime;
1836 118 : ctx->dash_io.get_header_value = dashdmx_io_get_header_value;
1837 118 : ctx->dash_io.get_utc_start_time = dashdmx_io_get_utc_start_time;
1838 118 : ctx->dash_io.setup_from_url = dashdmx_io_setup_from_url;
1839 118 : ctx->dash_io.set_range = dashdmx_io_set_range;
1840 118 : if ((ctx->forward==DFWD_FILE) || (ctx->forward==DFWD_SBOUND_MANIFEST))
1841 7 : ctx->dash_io.manifest_updated = dashdmx_io_manifest_updated;
1842 :
1843 : #if 0 //unused since we are in non threaded mode
1844 : ctx->dash_io.abort = dashdmx_io_abort;
1845 : ctx->dash_io.get_bytes_per_sec = dashdmx_io_get_bytes_per_sec;
1846 : ctx->dash_io.get_total_size = dashdmx_io_get_total_size;
1847 : ctx->dash_io.get_bytes_done = dashdmx_io_get_bytes_done;
1848 : #endif
1849 :
1850 118 : ctx->dash_io.on_dash_event = dashdmx_io_on_dash_event;
1851 :
1852 118 : if (ctx->init_timeshift<0) {
1853 0 : timeshift = -ctx->init_timeshift;
1854 0 : if (timeshift>100) timeshift = 100;
1855 : } else {
1856 118 : timeshift = ctx->init_timeshift;
1857 : }
1858 :
1859 118 : algo_str = ctx->algo;
1860 :
1861 118 : if (ctx->forward > DFWD_FILE) {
1862 9 : ctx->split_as = GF_TRUE;
1863 9 : ctx->screen_res = GF_FALSE;
1864 : algo_str = "none";
1865 9 : ctx->auto_switch = 0;
1866 9 : ctx->noseek = GF_TRUE;
1867 : }
1868 :
1869 118 : if (!strcmp(algo_str, "none")) algo = GF_DASH_ALGO_NONE;
1870 103 : else if (!strcmp(algo_str, "grate")) algo = GF_DASH_ALGO_GPAC_LEGACY_RATE;
1871 102 : else if (!strcmp(algo_str, "gbuf")) algo = GF_DASH_ALGO_GPAC_LEGACY_BUFFER;
1872 5 : else if (!strcmp(algo_str, "bba0")) algo = GF_DASH_ALGO_BBA0;
1873 4 : else if (!strcmp(algo_str, "bolaf")) algo = GF_DASH_ALGO_BOLA_FINITE;
1874 3 : else if (!strcmp(algo_str, "bolab")) algo = GF_DASH_ALGO_BOLA_BASIC;
1875 2 : else if (!strcmp(algo_str, "bolau")) algo = GF_DASH_ALGO_BOLA_U;
1876 1 : else if (!strcmp(algo_str, "bolao")) algo = GF_DASH_ALGO_BOLA_O;
1877 : else {
1878 : #ifndef GPAC_HAS_QJS
1879 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] No JS support, cannot use custom algo %s\n", algo_str));
1880 : return GF_BAD_PARAM;
1881 : #else
1882 : char szFile[GF_MAX_PATH];
1883 : Bool found = GF_FALSE;
1884 : GF_Err e;
1885 : //init to default, overwrite later
1886 : algo = GF_DASH_ALGO_GPAC_LEGACY_BUFFER;
1887 0 : if (gf_file_exists(algo_str)) {
1888 : strcpy(szFile, algo_str);
1889 : found = GF_TRUE;
1890 0 : } else if (!strstr(algo_str, ".js")) {
1891 : strcpy(szFile, algo_str);
1892 : strcat(szFile, ".js");
1893 0 : if (gf_file_exists(szFile)) {
1894 : found = GF_TRUE;
1895 : }
1896 : }
1897 : if (!found) {
1898 0 : gf_opts_default_shared_directory(szFile);
1899 : strcat(szFile, "/scripts/");
1900 : strcat(szFile, algo_str);
1901 0 : if (gf_file_exists(szFile)) {
1902 : found = GF_TRUE;
1903 0 : } else if (!strstr(algo_str, ".js")) {
1904 : strcat(szFile, ".js");
1905 0 : if (gf_file_exists(szFile))
1906 : found = GF_TRUE;
1907 : }
1908 : }
1909 0 : if (!found) {
1910 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] Custom algo %s not found\n", algo_str));
1911 0 : return GF_BAD_PARAM;
1912 : }
1913 0 : e = dashdmx_initialize_js(ctx, szFile);
1914 0 : if (e) {
1915 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] Failed to setup custom algo %s\n", algo_str));
1916 : return e;
1917 : }
1918 : #endif
1919 : }
1920 :
1921 :
1922 118 : ctx->dash = gf_dash_new(&ctx->dash_io, 0, ctx->auto_switch, (ctx->segstore==2) ? GF_TRUE : GF_FALSE, (algo==GF_DASH_ALGO_NONE) ? GF_TRUE : GF_FALSE, ctx->start_with, timeshift);
1923 :
1924 118 : if (!ctx->dash) {
1925 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] Error - cannot create DASH Client\n"));
1926 : return GF_IO_ERR;
1927 : }
1928 :
1929 118 : if (ctx->screen_res) {
1930 : GF_FilterSessionCaps caps;
1931 109 : gf_filter_get_session_caps(ctx->filter, &caps);
1932 109 : gf_dash_set_max_resolution(ctx->dash, caps.max_screen_width, caps.max_screen_height, caps.max_screen_bpp);
1933 : }
1934 :
1935 118 : gf_dash_set_algo(ctx->dash, algo);
1936 118 : gf_dash_set_utc_shift(ctx->dash, ctx->shift_utc);
1937 118 : gf_dash_set_route_ast_shift(ctx->dash, ctx->route_shift);
1938 118 : gf_dash_enable_utc_drift_compensation(ctx->dash, ctx->server_utc);
1939 118 : gf_dash_set_tile_adaptation_mode(ctx->dash, ctx->tile_mode, ctx->tiles_rate);
1940 :
1941 118 : gf_dash_set_min_timeout_between_404(ctx->dash, ctx->delay40X);
1942 118 : gf_dash_set_segment_expiration_threshold(ctx->dash, ctx->exp_threshold);
1943 118 : gf_dash_set_switching_probe_count(ctx->dash, ctx->switch_count);
1944 118 : gf_dash_set_agressive_adaptation(ctx->dash, ctx->aggressive);
1945 118 : gf_dash_enable_single_range_llhls(ctx->dash, ctx->llhls_merge);
1946 118 : gf_dash_debug_groups(ctx->dash, ctx->debug_as.vals, ctx->debug_as.nb_items);
1947 118 : gf_dash_disable_speed_adaptation(ctx->dash, !ctx->speedadapt);
1948 118 : gf_dash_ignore_xlink(ctx->dash, ctx->noxlink);
1949 118 : gf_dash_set_period_xlink_query_string(ctx->dash, ctx->query);
1950 118 : gf_dash_set_low_latency_mode(ctx->dash, ctx->lowlat);
1951 118 : if (ctx->split_as)
1952 10 : gf_dash_split_adaptation_sets(ctx->dash);
1953 118 : gf_dash_disable_low_quality_tiles(ctx->dash, ctx->skip_lqt);
1954 :
1955 : //in test mode, we disable seeking inside the segment: this initial seek range is dependent from tune-in time and would lead to different start range
1956 : //at each run, possibly breaking all tests
1957 118 : if (gf_sys_is_test_mode())
1958 114 : ctx->noseek = GF_TRUE;
1959 :
1960 118 : ctx->initial_play = GF_TRUE;
1961 118 : gf_filter_block_eos(filter, GF_TRUE);
1962 :
1963 : #ifdef GPAC_HAS_QJS
1964 118 : if (ctx->js_ctx) {
1965 0 : if (JS_IsFunction(ctx->js_ctx, ctx->download_fun)) {
1966 0 : gf_dash_set_algo_custom(ctx->dash, ctx, dashdmx_algo_js, dashdmx_download_monitor_js);
1967 0 : ctx->abort = GF_TRUE;
1968 : } else {
1969 0 : gf_dash_set_algo_custom(ctx->dash, ctx, dashdmx_algo_js, NULL);
1970 0 : ctx->abort = GF_FALSE;
1971 : }
1972 : }
1973 : #endif
1974 :
1975 118 : if (ctx->forward==DFWD_FILE) {
1976 4 : ctx->segstore = 0;
1977 4 : gf_filter_override_caps(filter, DASHDmxFileModeCaps, GF_ARRAY_LENGTH(DASHDmxFileModeCaps) );
1978 : }
1979 :
1980 : //for coverage
1981 : #ifdef GPAC_ENABLE_COVERAGE
1982 118 : if (gf_sys_is_cov_mode()) {
1983 : dashdmx_on_filter_setup_error(NULL, NULL, GF_OK);
1984 : }
1985 : #endif
1986 118 : return GF_OK;
1987 : }
1988 :
1989 118 : static void dashdmx_finalize(GF_Filter *filter)
1990 : {
1991 118 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) gf_filter_get_udta(filter);
1992 : assert(ctx);
1993 :
1994 118 : if (ctx->dash)
1995 118 : gf_dash_del(ctx->dash);
1996 :
1997 118 : if (ctx->frag_url)
1998 0 : gf_free(ctx->frag_url);
1999 :
2000 118 : if (ctx->manifest_payload)
2001 0 : gf_free(ctx->manifest_payload);
2002 :
2003 118 : if (ctx->hls_variants) {
2004 0 : while (gf_list_count(ctx->hls_variants))
2005 0 : gf_free(gf_list_pop_back(ctx->hls_variants));
2006 0 : gf_list_del(ctx->hls_variants);
2007 : }
2008 118 : if (ctx->hls_variants_names) {
2009 0 : while (gf_list_count(ctx->hls_variants_names))
2010 0 : gf_free(gf_list_pop_back(ctx->hls_variants_names));
2011 0 : gf_list_del(ctx->hls_variants_names);
2012 : }
2013 :
2014 : #ifdef GPAC_HAS_QJS
2015 118 : dashdmx_cleanup_js(ctx);
2016 : #endif
2017 118 : }
2018 :
2019 6472 : static Bool dashdmx_process_event(GF_Filter *filter, const GF_FilterEvent *fevt)
2020 : {
2021 : u32 i, count;
2022 : GF_FilterEvent src_evt;
2023 : GF_FilterPid *ipid;
2024 : Bool initial_play;
2025 : Double offset;
2026 6472 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) gf_filter_get_udta(filter);
2027 : GF_DASHGroup *group;
2028 :
2029 :
2030 6472 : switch (fevt->base.type) {
2031 3 : case GF_FEVT_QUALITY_SWITCH:
2032 3 : if (fevt->quality_switch.set_tile_mode_plus_one) {
2033 0 : GF_DASHTileAdaptationMode tile_mode = fevt->quality_switch.set_tile_mode_plus_one - 1;
2034 0 : gf_dash_set_tile_adaptation_mode(ctx->dash, tile_mode, 100);
2035 3 : } else if (fevt->quality_switch.q_idx < 0) {
2036 1 : gf_dash_set_automatic_switching(ctx->dash, 1);
2037 2 : } else if (fevt->base.on_pid) {
2038 : s32 idx;
2039 1 : group = gf_filter_pid_get_udta(fevt->base.on_pid);
2040 1 : if (!group) return GF_TRUE;
2041 1 : idx = group->idx;
2042 :
2043 1 : gf_dash_group_set_quality_degradation_hint(ctx->dash, group->idx, fevt->quality_switch.quality_degradation);
2044 :
2045 1 : if (fevt->quality_switch.dependent_group_index) {
2046 0 : if (fevt->quality_switch.dependent_group_index > gf_dash_group_get_num_groups_depending_on(ctx->dash, group->idx))
2047 : return GF_TRUE;
2048 :
2049 0 : idx = gf_dash_get_dependent_group_index(ctx->dash, group->idx, fevt->quality_switch.dependent_group_index-1);
2050 0 : if (idx==-1) return GF_TRUE;
2051 : }
2052 :
2053 1 : gf_dash_set_automatic_switching(ctx->dash, 0);
2054 1 : gf_dash_group_select_quality(ctx->dash, idx, NULL, fevt->quality_switch.q_idx);
2055 : } else {
2056 1 : gf_dash_switch_quality(ctx->dash, fevt->quality_switch.up, ctx->immediate);
2057 : }
2058 : return GF_TRUE;
2059 :
2060 : #ifdef FILTER_FIXME
2061 :
2062 : case GF_NET_ASSOCIATED_CONTENT_TIMING:
2063 : gf_dash_override_ntp(ctx->dash, com->addon_time.ntp);
2064 : return GF_TRUE;
2065 : #endif
2066 :
2067 0 : case GF_FEVT_FILE_DELETE:
2068 : //check if we want that in other forward mode
2069 0 : if (ctx->forward==DFWD_FILE) {
2070 0 : for (i=0; i<gf_dash_get_group_count(ctx->dash); i++) {
2071 0 : group = gf_dash_get_group_udta(ctx->dash, i);
2072 0 : if (!group || !group->template) continue;
2073 :
2074 0 : if (!strncmp(group->template, fevt->file_del.url, strlen(group->template) )) {
2075 0 : GF_FilterPid *pid = dashdmx_opid_from_group(ctx, group);
2076 0 : if (pid) {
2077 : GF_FilterEvent evt;
2078 0 : GF_FEVT_INIT(evt, GF_FEVT_FILE_DELETE, pid);
2079 0 : evt.file_del.url = fevt->file_del.url;
2080 0 : gf_filter_pid_send_event(pid, &evt);
2081 : }
2082 : return GF_TRUE;
2083 : }
2084 : }
2085 : }
2086 : return GF_TRUE;
2087 : default:
2088 : break;
2089 : }
2090 :
2091 : /*not supported*/
2092 6469 : if (!fevt->base.on_pid) return GF_TRUE;
2093 :
2094 6469 : if (fevt->base.on_pid == ctx->output_mpd_pid) {
2095 : return GF_TRUE;
2096 : }
2097 6460 : group = gf_filter_pid_get_udta(fevt->base.on_pid);
2098 6460 : if (!group) return GF_TRUE;
2099 6337 : count = gf_filter_get_ipid_count(filter);
2100 : ipid = NULL;
2101 32238 : for (i=0; i<count; i++) {
2102 26409 : ipid = gf_filter_get_ipid(filter, i);
2103 26409 : if (gf_filter_pid_get_udta(ipid) == fevt->base.on_pid) break;
2104 : ipid = NULL;
2105 : }
2106 :
2107 6337 : switch (fevt->base.type) {
2108 40 : case GF_FEVT_VISIBILITY_HINT:
2109 40 : group = gf_filter_pid_get_udta(fevt->base.on_pid);
2110 40 : if (!group) return GF_TRUE;
2111 :
2112 40 : gf_dash_group_set_visible_rect(ctx->dash, group->idx, fevt->visibility_hint.min_x, fevt->visibility_hint.max_x, fevt->visibility_hint.min_y, fevt->visibility_hint.max_y, fevt->visibility_hint.is_gaze);
2113 40 : return GF_TRUE;
2114 :
2115 261 : case GF_FEVT_PLAY:
2116 261 : src_evt = *fevt;
2117 261 : group->is_playing = GF_TRUE;
2118 261 : ctx->check_eos = GF_FALSE;
2119 :
2120 : //adjust play range from media timestamps to MPD time
2121 261 : if (fevt->play.timestamp_based) {
2122 :
2123 0 : if (fevt->play.timestamp_based==1) {
2124 : u64 pto;
2125 : u32 timescale;
2126 0 : gf_dash_group_get_presentation_time_offset(ctx->dash, group->idx, &pto, ×cale);
2127 0 : offset = (Double) pto;
2128 0 : offset /= timescale;
2129 0 : src_evt.play.start_range -= offset;
2130 0 : if (src_evt.play.start_range < 0) src_evt.play.start_range = 0;
2131 : }
2132 :
2133 0 : group->is_timestamp_based = 1;
2134 0 : group->pto_setup = 0;
2135 0 : ctx->media_start_range = fevt->play.start_range;
2136 : } else {
2137 261 : group->is_timestamp_based = 0;
2138 261 : group->pto_setup = 0;
2139 261 : if (fevt->play.start_range<0) src_evt.play.start_range = 0;
2140 : //in m3u8, we need also media start time for mapping time
2141 261 : if (gf_dash_is_m3u8(ctx->dash))
2142 37 : ctx->media_start_range = fevt->play.start_range;
2143 : }
2144 :
2145 : //we cannot handle seek request outside of a period being setup, this messes up our internal service setup
2146 : //we postpone the seek and will request it later on ...
2147 261 : if (gf_dash_in_period_setup(ctx->dash)) {
2148 0 : u64 p_end = gf_dash_get_period_duration(ctx->dash);
2149 0 : if (p_end) {
2150 0 : p_end += gf_dash_get_period_start(ctx->dash);
2151 0 : if (p_end<1000*fevt->play.start_range) {
2152 0 : ctx->seek_request = fevt->play.start_range;
2153 0 : return GF_TRUE;
2154 : }
2155 : }
2156 : }
2157 :
2158 261 : if (fevt->play.speed)
2159 261 : gf_dash_set_speed(ctx->dash, fevt->play.speed);
2160 :
2161 261 : initial_play = ctx->initial_play;
2162 261 : if (fevt->play.initial_broadcast_play) initial_play = GF_TRUE;
2163 :
2164 : /*don't seek if this command is the first PLAY request of objects declared by the subservice, unless start range is not default one (0) */
2165 261 : if (!ctx->nb_playing) {
2166 119 : if (!initial_play || (fevt->play.start_range>1.0)) {
2167 :
2168 3 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] Received Play command on group %d\n", group->idx));
2169 :
2170 3 : if (fevt->play.end_range<=0) {
2171 3 : u32 ms = (u32) ( 1000 * (-fevt->play.end_range) );
2172 3 : if (ms<1000) ms = 0;
2173 3 : gf_dash_set_timeshift(ctx->dash, ms);
2174 : }
2175 3 : gf_dash_seek(ctx->dash, fevt->play.start_range);
2176 :
2177 : //to remove once we manage to keep the service alive
2178 : /*don't forward commands if a switch of period is to be scheduled, we are killing the service anyway ...*/
2179 3 : if (gf_dash_get_period_switch_status(ctx->dash)) return GF_TRUE;
2180 : }
2181 : }
2182 : //otherwise in static mode, perform a group seek
2183 142 : else if (!initial_play && !gf_dash_is_dynamic_mpd(ctx->dash) ) {
2184 : /*don't forward commands if a switch of period is to be scheduled, we are killing the service anyway ...*/
2185 0 : if (gf_dash_get_period_switch_status(ctx->dash)) return GF_TRUE;
2186 :
2187 : //seek on a single group
2188 :
2189 0 : gf_dash_group_seek(ctx->dash, group->idx, fevt->play.start_range);
2190 : }
2191 :
2192 : //check if current segment playback should be aborted
2193 259 : src_evt.play.forced_dash_segment_switch = gf_dash_group_segment_switch_forced(ctx->dash, group->idx);
2194 :
2195 259 : gf_dash_group_select(ctx->dash, group->idx, GF_TRUE);
2196 259 : gf_dash_set_group_done(ctx->dash, (u32) group->idx, 0);
2197 :
2198 : //adjust start range from MPD time to media time
2199 259 : if (gf_dash_is_dynamic_mpd(ctx->dash) && ctx->noseek) {
2200 37 : src_evt.play.start_range=0;
2201 : } else {
2202 : u64 pto;
2203 : u32 timescale;
2204 222 : src_evt.play.start_range = gf_dash_group_get_start_range(ctx->dash, group->idx);
2205 222 : gf_dash_group_get_presentation_time_offset(ctx->dash, group->idx, &pto, ×cale);
2206 222 : src_evt.play.start_range += ((Double)pto) / timescale;
2207 : }
2208 259 : src_evt.play.no_byterange_forward = 1;
2209 259 : dashdmx_setup_buffer(ctx, group);
2210 :
2211 259 : gf_filter_prevent_blocking(filter, GF_TRUE);
2212 :
2213 259 : ctx->nb_playing++;
2214 : //forward new event to source pid
2215 259 : src_evt.base.on_pid = ipid;
2216 :
2217 259 : gf_filter_pid_send_event(ipid, &src_evt);
2218 259 : gf_filter_post_process_task(filter);
2219 : //cancel the event
2220 259 : return GF_TRUE;
2221 :
2222 105 : case GF_FEVT_STOP:
2223 105 : gf_dash_set_group_done(ctx->dash, (u32) group->idx, 1);
2224 105 : gf_dash_group_select(ctx->dash, (u32) group->idx, GF_FALSE);
2225 105 : group->is_playing = GF_FALSE;
2226 105 : group->prev_is_init_segment = GF_FALSE;
2227 105 : if (ctx->nb_playing) {
2228 101 : ctx->initial_play = GF_FALSE;
2229 101 : group->force_seg_switch = GF_TRUE;
2230 101 : ctx->nb_playing--;
2231 101 : if (!ctx->nb_playing) ctx->check_eos = GF_TRUE;
2232 : }
2233 : //forward new event to source pid
2234 105 : src_evt = *fevt;
2235 105 : src_evt.base.on_pid = ipid;
2236 105 : gf_filter_pid_send_event(ipid, &src_evt);
2237 :
2238 : //cancel the event
2239 105 : return GF_TRUE;
2240 21 : case GF_FEVT_SET_SPEED:
2241 21 : gf_dash_set_speed(ctx->dash, fevt->play.speed);
2242 21 : return GF_FALSE;
2243 :
2244 0 : case GF_FEVT_CAPS_CHANGE:
2245 0 : if (ctx->screen_res) {
2246 : GF_FilterSessionCaps caps;
2247 0 : gf_filter_get_session_caps(ctx->filter, &caps);
2248 0 : gf_dash_set_max_resolution(ctx->dash, caps.max_screen_width, caps.max_screen_height, caps.max_screen_bpp);
2249 : }
2250 : return GF_TRUE;
2251 : case GF_FEVT_INFO_UPDATE:
2252 : //propagate
2253 : return GF_FALSE;
2254 : default:
2255 : break;
2256 : }
2257 :
2258 : //by default cancel all events
2259 81 : return GF_TRUE;
2260 : }
2261 :
2262 :
2263 : GF_Err gf_dash_group_push_tfrf(GF_DashClient *dash, u32 idx, void *tfrf, u32 timescale);
2264 :
2265 125019 : static void dashdmx_update_group_stats(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
2266 : {
2267 : u32 bytes_per_sec = 0;
2268 : u64 file_size = 0;
2269 : u32 dep_rep_idx;
2270 : const GF_PropertyValue *p;
2271 125019 : GF_PropertyEntry *pe=NULL;
2272 : Bool broadcast_flag = GF_FALSE;
2273 247057 : if (group->stats_uploaded) return;
2274 17134 : if (group->prev_is_init_segment) return;
2275 7735 : if (!group->seg_filter_src) return;
2276 :
2277 7735 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_FILE_CACHED, &pe);
2278 7735 : if (!p || !p->value.boolean) {
2279 : u64 us_since_start = 0;
2280 : u64 down_bytes = 0;
2281 : u32 bits_per_sec = 0;
2282 4754 : u32 now = gf_sys_clock();
2283 :
2284 4754 : if (!ctx->abort || (now - group->last_bw_check < ctx->bwcheck)) {
2285 4636 : gf_filter_release_property(pe);
2286 4636 : return;
2287 : }
2288 118 : group->last_bw_check = now;
2289 : //we allow file abort, check the download
2290 :
2291 118 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_DOWN_RATE, &pe);
2292 118 : if (p) bits_per_sec = p->value.uint;
2293 :
2294 118 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_DOWN_SIZE, &pe);
2295 118 : if (p) file_size = p->value.longuint;
2296 :
2297 118 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_DOWN_BYTES, &pe);
2298 118 : if (p) down_bytes = p->value.longuint;
2299 :
2300 118 : us_since_start = gf_sys_clock_high_res() - group->us_at_seg_start;
2301 118 : gf_dash_group_check_bandwidth(ctx->dash, group->idx, bits_per_sec, file_size, down_bytes, us_since_start);
2302 :
2303 118 : gf_filter_release_property(pe);
2304 118 : return;
2305 : }
2306 2981 : group->stats_uploaded = GF_TRUE;
2307 :
2308 2981 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_DOWN_RATE, &pe);
2309 2981 : if (p) bytes_per_sec = p->value.uint / 8;
2310 :
2311 2981 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_DOWN_SIZE, &pe);
2312 2981 : if (p) file_size = p->value.longuint;
2313 :
2314 2981 : p = gf_filter_get_info_str(group->seg_filter_src, "x-route", &pe);
2315 2981 : if (p && p->value.string && !strcmp(p->value.string, "yes")) {
2316 : broadcast_flag = GF_TRUE;
2317 : }
2318 2981 : if (group->nb_group_deps)
2319 1607 : dep_rep_idx = group->current_group_dep ? (group->current_group_dep-1) : group->nb_group_deps;
2320 : else
2321 1374 : dep_rep_idx = group->current_dependent_rep_idx;
2322 :
2323 2981 : gf_dash_group_store_stats(ctx->dash, group->idx, dep_rep_idx, bytes_per_sec, file_size, broadcast_flag, gf_sys_clock_high_res() - group->us_at_seg_start);
2324 :
2325 2981 : p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_FILE_CACHED, &pe);
2326 2981 : if (p && p->value.boolean)
2327 2981 : group->stats_uploaded = GF_TRUE;
2328 :
2329 2981 : gf_filter_release_property(pe);
2330 : }
2331 :
2332 4326 : static void dashdmx_switch_segment(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
2333 : {
2334 : u32 dependent_representation_index;
2335 : GF_Err e;
2336 : Bool has_scalable_next;
2337 : Bool seg_disabled;
2338 : GF_FilterEvent evt;
2339 : const char *next_url, *next_url_init_or_switch_segment, *src_url, *key_url;
2340 : u64 start_range, end_range, switch_start_range, switch_end_range;
2341 : bin128 key_IV;
2342 : u32 group_idx;
2343 :
2344 4326 : group->init_ok = GF_TRUE;
2345 :
2346 4326 : fetch_next:
2347 : assert(group->nb_eos || group->seg_was_not_ready || group->in_error);
2348 4326 : group->wait_for_pck = GF_TRUE;
2349 4326 : group->in_error = GF_FALSE;
2350 4326 : if (group->segment_sent) {
2351 2946 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d drop current segment\n", group->idx));
2352 2946 : if (!group->current_group_dep && !group->next_dependent_rep_idx) {
2353 1500 : gf_dash_group_discard_segment(ctx->dash, group->idx);
2354 :
2355 : //special case for group dependencies (tiling): signal segment switch
2356 : //so that tileagg may detect losses
2357 1500 : if (group->nb_group_deps) {
2358 159 : GF_FilterPid *opid = dashdmx_opid_from_group(ctx, group);
2359 159 : if (opid) {
2360 159 : GF_FEVT_INIT(evt, GF_FEVT_PLAY_HINT, opid);
2361 159 : evt.play.forced_dash_segment_switch = GF_TRUE;
2362 159 : gf_filter_pid_send_event(opid, &evt);
2363 : }
2364 : }
2365 1500 : if (group->seg_discard_state==1)
2366 0 : group->seg_discard_state = 2;
2367 : }
2368 :
2369 2946 : group->segment_sent = GF_FALSE;
2370 : //no thread mode, we work with at most one entry in cache, call process right away to get the group next URL ready
2371 2946 : gf_dash_process(ctx->dash);
2372 : }
2373 :
2374 : #if 0
2375 : if (group_done) {
2376 : if (!gf_dash_get_period_switch_status(ctx->dash) && gf_dash_in_last_period(ctx->dash, GF_TRUE)) {
2377 : return;
2378 : }
2379 : }
2380 : #endif
2381 :
2382 : //special case if we had a discard of a dependent seg: we wait for the output PIDs to be flushed
2383 : //so that we don't send a GF_FEVT_PLAY_HINT event before the previous segment is completely produced
2384 : //this is mostly for tileagg for the time being and could need further refinements
2385 4326 : if (group->seg_discard_state == 2) {
2386 : u32 i;
2387 0 : for (i=0; i < gf_filter_get_opid_count(ctx->filter); i++) {
2388 0 : GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i );
2389 0 : GF_DASHGroup *g = gf_filter_pid_get_udta( opid );
2390 0 : if (g != group) continue;
2391 0 : if (gf_filter_pid_would_block(opid)) {
2392 0 : group->seg_was_not_ready = GF_TRUE;
2393 0 : group->stats_uploaded = GF_TRUE;
2394 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] some pids blocked on group with discard, waiting before fetching next segment\n"));
2395 1297 : return;
2396 : }
2397 : }
2398 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] all pids unblocked on group with discard, fetching next segment\n"));
2399 0 : group->seg_discard_state = 0;
2400 : }
2401 :
2402 :
2403 : dependent_representation_index = 0;
2404 4326 : group_idx = group->idx;
2405 4326 : if (group->nb_group_deps) {
2406 1612 : dependent_representation_index = group->current_group_dep;
2407 2714 : } else if (group->next_dependent_rep_idx) {
2408 0 : dashdmx_update_group_stats(ctx, group);
2409 0 : dependent_representation_index = group->current_dependent_rep_idx = group->next_dependent_rep_idx;
2410 2714 : } else if (group->current_dependent_rep_idx) {
2411 0 : dashdmx_update_group_stats(ctx, group);
2412 0 : group->current_dependent_rep_idx = 0;
2413 : }
2414 :
2415 4326 : group->stats_uploaded = GF_FALSE;
2416 :
2417 4326 : e = gf_dash_group_get_next_segment_location(ctx->dash, group_idx, dependent_representation_index, &next_url, &start_range, &end_range,
2418 : NULL, &next_url_init_or_switch_segment, &switch_start_range , &switch_end_range,
2419 : &src_url, &has_scalable_next, &key_url, &key_IV);
2420 :
2421 4326 : if (e == GF_EOS) {
2422 201 : group->eos_detected = GF_TRUE;
2423 201 : return;
2424 : }
2425 : seg_disabled = GF_FALSE;
2426 4125 : group->eos_detected = GF_FALSE;
2427 4125 : if (e == GF_URL_REMOVED) {
2428 : seg_disabled = GF_TRUE;
2429 : e = GF_OK;
2430 : }
2431 :
2432 4125 : if (e != GF_OK) {
2433 1044 : if (e == GF_BUFFER_TOO_SMALL) {
2434 1044 : group->seg_was_not_ready = GF_TRUE;
2435 1044 : group->stats_uploaded = GF_TRUE;
2436 1044 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d next segment name not known yet!\n", group->idx));
2437 1044 : gf_filter_ask_rt_reschedule(ctx->filter, 10000);
2438 : // gf_filter_post_process_task(ctx->filter);
2439 : } else {
2440 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] group %d error fetching next segment name: %s\n", group->idx, gf_error_to_string(e) ));
2441 : }
2442 : return;
2443 : }
2444 :
2445 3081 : if (!has_scalable_next) {
2446 1634 : group->next_dependent_rep_idx = 0;
2447 : } else {
2448 1447 : group->next_dependent_rep_idx++;
2449 : }
2450 :
2451 3081 : if (group->nb_group_deps) {
2452 1607 : group->current_group_dep++;
2453 1607 : if (group->current_group_dep>group->nb_group_deps)
2454 160 : group->current_group_dep = 0;
2455 : }
2456 :
2457 : assert(next_url);
2458 3081 : group->seg_was_not_ready = GF_FALSE;
2459 :
2460 3081 : if (next_url_init_or_switch_segment && !group->init_switch_seg_sent) {
2461 52 : if (group->in_is_cryptfile) {
2462 0 : gf_cryptfin_set_kms(group->seg_filter_src, key_url, key_IV);
2463 : }
2464 52 : GF_FEVT_INIT(evt, GF_FEVT_SOURCE_SWITCH, NULL);
2465 52 : evt.seek.start_offset = switch_start_range;
2466 52 : evt.seek.end_offset = switch_end_range;
2467 52 : evt.seek.source_switch = next_url_init_or_switch_segment;
2468 52 : evt.seek.is_init_segment = GF_TRUE;
2469 52 : evt.seek.skip_cache_expiration = GF_TRUE;
2470 :
2471 52 : group->prev_is_init_segment = GF_TRUE;
2472 :
2473 52 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d queuing next init/switching segment %s\n", group->idx, next_url_init_or_switch_segment));
2474 :
2475 52 : group->signal_seg_name = (ctx->forward==DFWD_FILE) ? GF_TRUE : GF_FALSE;
2476 52 : group->init_switch_seg_sent = GF_TRUE;
2477 52 : gf_filter_send_event(group->seg_filter_src, &evt, GF_FALSE);
2478 52 : return;
2479 : }
2480 3029 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d queuing next media segment %s\n", group->idx, next_url));
2481 :
2482 3029 : group->segment_sent = GF_TRUE;
2483 3029 : group->prev_is_init_segment = GF_FALSE;
2484 3029 : group->init_switch_seg_sent = GF_FALSE;
2485 3029 : group->signal_seg_name = (ctx->forward==DFWD_FILE) ? GF_TRUE : GF_FALSE;
2486 3029 : group->us_at_seg_start = gf_sys_clock_high_res();
2487 :
2488 3029 : if (seg_disabled) {
2489 : u32 dep_rep_idx;
2490 0 : if (group->nb_group_deps)
2491 0 : dep_rep_idx = group->current_group_dep ? (group->current_group_dep-1) : group->nb_group_deps;
2492 : else
2493 0 : dep_rep_idx = group->current_dependent_rep_idx;
2494 :
2495 0 : gf_dash_group_store_stats(ctx->dash, group->idx, dep_rep_idx, 0, 0, 0, 0);
2496 0 : if (!group->seg_discard_state)
2497 0 : group->seg_discard_state = 1;
2498 : goto fetch_next;
2499 : }
2500 :
2501 3029 : if (group->in_is_cryptfile) {
2502 0 : gf_cryptfin_set_kms(group->seg_filter_src, key_url, key_IV);
2503 : }
2504 :
2505 3029 : GF_FEVT_INIT(evt, GF_FEVT_SOURCE_SWITCH, NULL);
2506 3029 : evt.seek.source_switch = next_url;
2507 3029 : evt.seek.start_offset = start_range;
2508 3029 : evt.seek.end_offset = end_range;
2509 : evt.seek.is_init_segment = GF_FALSE;
2510 3029 : gf_filter_send_event(group->seg_filter_src, &evt, GF_FALSE);
2511 : }
2512 :
2513 0 : GF_Err dashin_abort(GF_DASHDmxCtx *ctx)
2514 : {
2515 : u32 i;
2516 0 : if (ctx->in_error) return GF_EOS;
2517 :
2518 0 : for (i=0; i<gf_filter_get_ipid_count(ctx->filter); i++) {
2519 : GF_FilterEvent evt;
2520 0 : GF_FilterPid *pid = gf_filter_get_ipid(ctx->filter, i);
2521 0 : gf_filter_pid_set_discard(pid, GF_TRUE);
2522 0 : GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
2523 0 : gf_filter_pid_send_event(pid, &evt);
2524 : }
2525 0 : for (i=0; i<gf_filter_get_opid_count(ctx->filter); i++) {
2526 0 : GF_FilterPid *pid = gf_filter_get_opid(ctx->filter, i);
2527 0 : gf_filter_pid_set_eos(pid);
2528 : }
2529 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASHDmx] Fatal error, aborting\n"));
2530 0 : ctx->in_error = GF_TRUE;
2531 0 : return GF_SERVICE_ERROR;
2532 : }
2533 :
2534 37674 : GF_Err dashdmx_process(GF_Filter *filter)
2535 : {
2536 : u32 i, count;
2537 : GF_FilterPacket *pck;
2538 : GF_Err e;
2539 : u32 next_time_ms = 0;
2540 37674 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) gf_filter_get_udta(filter);
2541 37674 : Bool check_eos = ctx->check_eos;
2542 : Bool has_pck = GF_FALSE;
2543 :
2544 : //reset group states and update stats
2545 37674 : count = gf_dash_get_group_count(ctx->dash);
2546 160142 : for (i=0; i<count; i++) {
2547 122468 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, i);
2548 122468 : if (!group) continue;
2549 69199 : group->nb_eos = 0;
2550 69199 : if (group->eos_detected) check_eos = GF_TRUE;
2551 : }
2552 :
2553 37674 : if (!ctx->mpd_pid)
2554 : return GF_EOS;
2555 :
2556 : //check MPD pid
2557 37673 : pck = gf_filter_pid_get_packet(ctx->mpd_pid);
2558 37673 : if (pck) {
2559 113 : gf_filter_pid_drop_packet(ctx->mpd_pid);
2560 : }
2561 37673 : e = gf_dash_process(ctx->dash);
2562 37673 : if (e == GF_IP_NETWORK_EMPTY) {
2563 37 : gf_filter_ask_rt_reschedule(filter, 100000);
2564 37 : return GF_OK;
2565 : }
2566 37636 : else if (e==GF_SERVICE_ERROR) {
2567 0 : return dashin_abort(ctx);
2568 : }
2569 37636 : if (e)
2570 : return e;
2571 :
2572 37636 : next_time_ms = gf_dash_get_min_wait_ms(ctx->dash);
2573 37636 : if (next_time_ms>1000)
2574 : next_time_ms=1000;
2575 :
2576 : //flush all media input
2577 37636 : count = gf_filter_get_ipid_count(filter);
2578 200775 : for (i=0; i<count; i++) {
2579 163139 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
2580 : GF_FilterPid *opid;
2581 : GF_DASHGroup *group;
2582 163139 : if (ipid == ctx->mpd_pid) continue;
2583 125503 : opid = gf_filter_pid_get_udta(ipid);
2584 125503 : group = gf_filter_pid_get_udta(opid);
2585 :
2586 125503 : if (!group)
2587 6 : continue;
2588 :
2589 : while (1) {
2590 247202 : pck = gf_filter_pid_get_packet(ipid);
2591 247202 : if (!group->is_playing) {
2592 21844 : if (pck) {
2593 : //in file mode, keep first packet (init seg) until we play
2594 8 : if ((ctx->forward!=DFWD_FILE) || !group->prev_is_init_segment) {
2595 1 : gf_filter_pid_drop_packet(ipid);
2596 1 : continue;
2597 : }
2598 : }
2599 : break;
2600 : }
2601 :
2602 225358 : if (!pck) {
2603 103654 : if (gf_filter_pid_is_eos(ipid) || !gf_filter_pid_is_playing(opid) || group->force_seg_switch) {
2604 22805 : group->nb_eos++;
2605 :
2606 : //wait until all our inputs are done
2607 22805 : if (group->nb_eos == group->nb_pids) {
2608 : u32 j, nb_block = 0;
2609 : //check all pids in this group, postpone segment switch if blocking
2610 33868 : for (j=0; j<count; j++) {
2611 33868 : GF_FilterPid *an_ipid = gf_filter_get_ipid(filter, j);
2612 33868 : GF_FilterPid *an_opid = gf_filter_pid_get_udta(an_ipid);
2613 : GF_DASHGroup *agroup;
2614 33868 : if (an_ipid == ctx->mpd_pid) continue;
2615 27252 : agroup = gf_filter_pid_get_udta(an_opid);
2616 27252 : if (!agroup || (agroup != group)) continue;
2617 :
2618 21267 : if (gf_filter_pid_would_block(an_opid)) {
2619 12110 : nb_block++;
2620 : }
2621 : }
2622 6616 : if (nb_block == group->nb_pids) {
2623 3423 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASHDmx] End of segment for group %d but %d output pid(s) would block, postponing\n", group->idx, nb_block));
2624 : break;
2625 : }
2626 :
2627 : //good to switch, cancel all end of stream signals on pids from this group and switch
2628 3193 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] End of segment for group %d, updating stats and switching segment\n", group->idx));
2629 22176 : for (j=0; j<count; j++) {
2630 : const GF_PropertyValue *p;
2631 22176 : GF_PropertyEntry *pe=NULL;
2632 22176 : GF_FilterPid *an_ipid = gf_filter_get_ipid(filter, j);
2633 22176 : GF_FilterPid *an_opid = gf_filter_pid_get_udta(an_ipid);
2634 : GF_DASHGroup *agroup;
2635 26508 : if (an_ipid == ctx->mpd_pid) continue;
2636 18983 : agroup = gf_filter_pid_get_udta(an_opid);
2637 18983 : if (!agroup || (agroup != group)) continue;
2638 :
2639 17844 : if (gf_filter_pid_is_eos(an_ipid)) {
2640 3199 : gf_filter_pid_clear_eos(an_ipid, GF_TRUE);
2641 3199 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] Clearing EOS on pids from group %d\n", group->idx));
2642 : }
2643 :
2644 17844 : p = gf_filter_pid_get_info_str(an_ipid, "smooth_tfrf", &pe);
2645 17844 : if (p) {
2646 0 : gf_dash_group_push_tfrf(ctx->dash, group->idx, p->value.ptr, gf_filter_pid_get_timescale(an_ipid));
2647 : }
2648 17844 : gf_filter_release_property(pe);
2649 : }
2650 3193 : dashdmx_update_group_stats(ctx, group);
2651 3193 : group->stats_uploaded = GF_TRUE;
2652 3193 : group->force_seg_switch = GF_FALSE;
2653 3193 : dashdmx_switch_segment(ctx, group);
2654 :
2655 3193 : gf_filter_prevent_blocking(filter, GF_FALSE);
2656 3193 : if (group->eos_detected && !has_pck) check_eos = GF_TRUE;
2657 : }
2658 : }
2659 : else {
2660 80849 : if (ctx->abort)
2661 122 : dashdmx_update_group_stats(ctx, group);
2662 : //GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] No source packet group %d and not in end of stream\n", group->idx));
2663 : }
2664 100231 : if (group->in_error || group->seg_was_not_ready) {
2665 1133 : dashdmx_switch_segment(ctx, group);
2666 1133 : gf_filter_prevent_blocking(filter, GF_FALSE);
2667 1133 : if (group->eos_detected && !has_pck) check_eos = GF_TRUE;
2668 : }
2669 : break;
2670 : }
2671 : has_pck = GF_TRUE;
2672 : check_eos = GF_FALSE;
2673 121704 : dashdmx_forward_packet(ctx, pck, ipid, opid, group);
2674 121704 : group->wait_for_pck = GF_FALSE;
2675 121704 : dashdmx_update_group_stats(ctx, group);
2676 : }
2677 : }
2678 :
2679 37636 : if (check_eos) {
2680 : Bool all_groups_done = GF_TRUE;
2681 : Bool groups_not_playing = GF_TRUE;
2682 6512 : Bool is_in_last_period = gf_dash_in_last_period(ctx->dash, GF_TRUE);
2683 :
2684 : //not last period, check if we are done playing all groups due to stop requests
2685 6512 : if (!is_in_last_period && !ctx->nb_playing) {
2686 : Bool groups_done=GF_TRUE;
2687 267 : for (i=0; i<count; i++) {
2688 267 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
2689 : GF_FilterPid *opid;
2690 : GF_DASHGroup *group;
2691 267 : if (ipid == ctx->mpd_pid) continue;
2692 178 : opid = gf_filter_pid_get_udta(ipid);
2693 178 : group = gf_filter_pid_get_udta(opid);
2694 178 : if (!group) continue;
2695 178 : if (!group->is_playing && group->eos_detected) continue;
2696 : groups_done=GF_FALSE;
2697 : }
2698 89 : if (groups_done)
2699 : is_in_last_period = GF_TRUE;
2700 : }
2701 :
2702 33765 : for (i=0; i<count; i++) {
2703 27253 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
2704 : GF_FilterPid *opid;
2705 : GF_DASHGroup *group;
2706 27253 : if (ipid == ctx->mpd_pid) continue;
2707 20741 : opid = gf_filter_pid_get_udta(ipid);
2708 20741 : group = gf_filter_pid_get_udta(opid);
2709 : //reset in progress
2710 20741 : if (!group) {
2711 : all_groups_done = GF_FALSE;
2712 0 : continue;
2713 : }
2714 20741 : if (group->is_playing)
2715 : groups_not_playing = GF_FALSE;
2716 :
2717 20741 : if (!group->eos_detected && group->is_playing) {
2718 : all_groups_done = GF_FALSE;
2719 20218 : } else if (is_in_last_period) {
2720 19988 : if (gf_filter_pid_is_eos(ipid) || group->eos_detected || gf_dash_get_group_done(ctx->dash, group->idx))
2721 19988 : gf_filter_pid_set_eos(opid);
2722 : else
2723 : all_groups_done = GF_FALSE;
2724 : }
2725 : }
2726 6512 : if (all_groups_done) {
2727 5989 : if (is_in_last_period || groups_not_playing) {
2728 5985 : if (!ctx->manifest_stop_sent) {
2729 : GF_FilterEvent evt;
2730 97 : ctx->manifest_stop_sent = GF_TRUE;
2731 97 : GF_FEVT_INIT(evt, GF_FEVT_STOP, ctx->mpd_pid)
2732 97 : gf_filter_pid_send_event(ctx->mpd_pid, &evt);
2733 : }
2734 : return GF_EOS;
2735 : }
2736 4 : if (!gf_dash_get_period_switch_status(ctx->dash)) {
2737 10 : for (i=0; i<count; i++) {
2738 10 : GF_DASHGroup *group = gf_dash_get_group_udta(ctx->dash, i);
2739 10 : if (!group) continue;
2740 6 : group->nb_eos = 0;
2741 6 : group->eos_detected = GF_FALSE;
2742 : }
2743 :
2744 4 : gf_dash_request_period_switch(ctx->dash);
2745 : }
2746 : }
2747 : }
2748 :
2749 31651 : if (gf_dash_is_in_setup(ctx->dash))
2750 8 : gf_filter_post_process_task(filter);
2751 31643 : else if (ctx->abort)
2752 129 : gf_filter_ask_rt_reschedule(filter, 50000);
2753 31514 : else if (next_time_ms)
2754 69 : gf_filter_ask_rt_reschedule(filter, 1000 * next_time_ms);
2755 :
2756 : return GF_OK;
2757 : }
2758 :
2759 3072 : static const char *dashdmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
2760 : {
2761 : char *d = (char *)data;
2762 : char *res_dash, *res_m3u, *res_smooth;
2763 3072 : char last_c = d[size-1];
2764 3072 : d[size-1] = 0;
2765 3072 : res_dash = strstr(data, "<MPD ");
2766 3072 : res_m3u = strstr(data, "#EXTM3U");
2767 3072 : res_smooth = strstr(data, "<SmoothStreamingMedia");
2768 3072 : d[size-1] = last_c;
2769 :
2770 3072 : if (res_dash) {
2771 95 : *score = GF_FPROBE_SUPPORTED;
2772 95 : return "application/dash+xml";
2773 : }
2774 2977 : if (res_m3u) {
2775 122 : *score = GF_FPROBE_SUPPORTED;
2776 122 : return "video/mpegurl";
2777 : }
2778 2855 : if (res_smooth) {
2779 2 : *score = GF_FPROBE_SUPPORTED;
2780 2 : return "application/vnd.ms-sstr+xml";
2781 : }
2782 : return NULL;
2783 : }
2784 :
2785 : #define OFFS(_n) #_n, offsetof(GF_DASHDmxCtx, _n)
2786 : static const GF_FilterArgs DASHDmxArgs[] =
2787 : {
2788 : { OFFS(auto_switch), "switch quality every N segments, disabled if 0", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
2789 : { OFFS(segstore), "enable file caching\n"
2790 : "- mem: all files are stored in memory, no disk IO\n"
2791 : "- disk: files are stored to disk but discarded once played\n"
2792 : "- cache: all files are stored to disk and kept"
2793 : "", GF_PROP_UINT, "mem", "mem|disk|cache", GF_FS_ARG_HINT_ADVANCED},
2794 : { OFFS(algo), "adaptation algorithm to use\n"
2795 : "- none: no adaptation logic\n"
2796 : "- grate: GPAC legacy algo based on available rate\n"
2797 : "- gbuf: GPAC legacy algo based on buffer occupancy\n"
2798 : "- bba0: BBA-0\n"
2799 : "- bolaf: BOLA Finite\n"
2800 : "- bolab: BOLA Basic\n"
2801 : "- bolau: BOLA-U\n"
2802 : "- bolao: BOLA-O\n"
2803 : "- JS: use file JS (either with specified path or in $GSHARE/scripts/) for algo (.js extension may be omitted)"
2804 : , GF_PROP_STRING, "gbuf", "none|grate|gbuf|bba0|bolaf|bolab|bolau|bolao|JS", GF_FS_ARG_HINT_ADVANCED},
2805 : { OFFS(start_with), "initial selection criteria\n"
2806 : "- min_q: start with lowest quality\n"
2807 : "- max_q: start with highest quality\n"
2808 : "- min_bw: start with lowest bitrate\n"
2809 : "- max_bw: start with highest bitrate; for tiles are used, all low priority tiles will have the lower (below max) bandwidth selected\n"
2810 : "- max_bw_tiles: start with highest bitrate; for tiles all low priority tiles will have their lowest bandwidth selected"
2811 : , GF_PROP_UINT, "max_bw", "min_q|max_q|min_bw|max_bw|max_bw_tiles", 0},
2812 :
2813 : { OFFS(max_res), "use max media resolution to configure display", GF_PROP_BOOL, "true", NULL, 0},
2814 : { OFFS(immediate), "when interactive switching is requested and immediate is set, the buffer segments are trashed", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
2815 : { OFFS(abort), "allow abort during a segment download", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
2816 : { OFFS(use_bmin), "use the indicated min buffer time of the MPD if true, otherwise uses default player settings", GF_PROP_BOOL, "false", NULL, 0},
2817 :
2818 : { OFFS(shift_utc), "shift DASH UTC clock in ms", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
2819 : { OFFS(route_shift), "shift ROUTE requests time by given ms", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
2820 : { OFFS(server_utc), "use ServerUTC: or Date: http headers instead of local UTC", GF_PROP_BOOL, "yes", NULL, GF_FS_ARG_HINT_ADVANCED},
2821 : { OFFS(screen_res), "use screen resolution in selection phase", GF_PROP_BOOL, "yes", NULL, GF_FS_ARG_HINT_ADVANCED},
2822 : { OFFS(init_timeshift), "set initial timeshift in ms (if >0) or in per-cent of timeshift buffer (if <0)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
2823 : { OFFS(tile_mode), "tile adaptation mode\n"
2824 : "- none: bitrate is shared equally across all tiles\n"
2825 : "- rows: bitrate decreases for each row of tiles starting from the top, same rate for each tile on the row\n"
2826 : "- rrows: bitrate decreases for each row of tiles starting from the bottom, same rate for each tile on the row\n"
2827 : "- mrows: bitrate decreased for top and bottom rows only, same rate for each tile on the row\n"
2828 : "- cols: bitrate decreases for each columns of tiles starting from the left, same rate for each tile on the columns\n"
2829 : "- rcols: bitrate decreases for each columns of tiles starting from the right, same rate for each tile on the columns\n"
2830 : "- mcols: bitrate decreased for left and right columns only, same rate for each tile on the columns\n"
2831 : "- center: bitrate decreased for all tiles on the edge of the picture\n"
2832 : "- edges: bitrate decreased for all tiles on the center of the picture"
2833 : , GF_PROP_UINT, "none", "none|rows|rrows|mrows|cols|rcols|mcols|center|edges", GF_FS_ARG_HINT_EXPERT},
2834 : { OFFS(tiles_rate), "indicate the amount of bandwidth to use at each quality level. The rate is recursively applied at each level, e.g. if 50%, Level1 gets 50%, level2 gets 25%, ... If 100, automatic rate allocation will be done by maximizing the quality in order of priority. If 0, bitstream will not be smoothed across tiles/qualities, and concurrency may happen between different media", GF_PROP_UINT, "100", NULL, GF_FS_ARG_HINT_EXPERT},
2835 : { OFFS(delay40X), "delay in milliseconds to wait between two 40X on the same segment", GF_PROP_UINT, "500", NULL, GF_FS_ARG_HINT_ADVANCED},
2836 : { OFFS(exp_threshold), "delay in milliseconds to wait after the segment AvailabilityEndDate before considering the segment lost", GF_PROP_UINT, "100", NULL, GF_FS_ARG_HINT_ADVANCED},
2837 : { OFFS(switch_count), "indicate how many segments the client shall wait before switching up bandwidth. If 0, switch will happen as soon as the bandwidth is enough, but this is more prone to network variations", GF_PROP_UINT, "1", NULL, GF_FS_ARG_HINT_ADVANCED},
2838 : { OFFS(aggressive), "if enabled, switching algo targets the closest bandwidth fitting the available download rate. If no, switching algo targets the lowest bitrate representation that is above the currently played (eg does not try to switch to max bandwidth)", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_EXPERT},
2839 : { OFFS(debug_as), "play only the adaptation sets indicated by their indices (0-based) in the MPD", GF_PROP_UINT_LIST, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
2840 : { OFFS(speedadapt), "enable adaptation based on playback speed", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_EXPERT},
2841 : { OFFS(noxlink), "disable xlink if period has both xlink and adaptation sets", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_ADVANCED},
2842 : { OFFS(query), "set query string (without initial '?') to append to xlink of periods", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
2843 : { OFFS(split_as), "separate all qualities into different adaptation sets and stream all qualities. Dependent representations (scalable) are treated as independent", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_ADVANCED},
2844 : { OFFS(noseek), "disable seeking of initial segment(s) in dynamic mode (useful when UTC clocks do not match)", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_EXPERT},
2845 : { OFFS(bwcheck), "minimum time in milliseconds between two bandwidth checks when allowing segment download abort", GF_PROP_UINT, "5", NULL, GF_FS_ARG_HINT_EXPERT},
2846 : { OFFS(lowlat), "segment scheduling policy in low latency mode\n"
2847 : "- no: disable low latency\n"
2848 : "- strict: strict respect of AST offset in low latency\n"
2849 : "- early: allow fetching segments earlier than their AST in low latency when input demux is empty", GF_PROP_UINT, "early", "no|strict|early", GF_FS_ARG_HINT_EXPERT},
2850 : { OFFS(forward), "segment forwarding mode -see filter help\n"
2851 : "- none: regular DASH read\n"
2852 : "- file: do not demux files and forward them as file pids (imply `segstore=mem`)\n"
2853 : "- segb: turn on [-split_as](), segment and fragment bounds signaling and DASH cue insertion\n"
2854 : "- mani: same as `segb` and also forward manifests"
2855 : , GF_PROP_UINT, "none", "none|file|segb|mani", GF_FS_ARG_HINT_ADVANCED},
2856 : { OFFS(fmodefwd), "forward packet rather than copy them in `file` forward mode. Packet copy might improve performances in low latency mode", GF_PROP_BOOL, "yes", NULL, GF_FS_ARG_HINT_EXPERT},
2857 :
2858 : { OFFS(skip_lqt), "disable decoding of tiles with highest degradation hints (not visible, not gazed at) for debug purposes", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_EXPERT},
2859 : { OFFS(llhls_merge), "merge LL-HLS byte range parts into a single open byte range request", GF_PROP_BOOL, "yes", NULL, GF_FS_ARG_HINT_EXPERT},
2860 : { OFFS(filemode), "alias for forward=file", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_HIDE},
2861 : { OFFS(groupsel), "select groups based on language (by default all playable groups are exposed)", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_ADVANCED},
2862 : {0}
2863 : };
2864 :
2865 :
2866 :
2867 : static const GF_FilterCapability DASHDmxCaps[] =
2868 : {
2869 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2870 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "mpd|m3u8|3gm|ism"),
2871 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "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"),
2872 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
2873 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
2874 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
2875 : {0},
2876 : //accept any stream but files, framed
2877 : { .code=GF_PROP_PID_STREAM_TYPE, .val.type=GF_PROP_UINT, .val.value.uint=GF_STREAM_FILE, .flags=(GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_EXCLUDED|GF_CAPFLAG_LOADED_FILTER) },
2878 : { .code=GF_PROP_PID_UNFRAMED, .val.type=GF_PROP_BOOL, .val.value.boolean=GF_TRUE, .flags=(GF_CAPFLAG_IN_BUNDLE|GF_CAPFLAG_INPUT|GF_CAPFLAG_EXCLUDED|GF_CAPFLAG_LOADED_FILTER) },
2879 : CAP_UINT(GF_CAPS_OUTPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
2880 : };
2881 :
2882 :
2883 : GF_FilterRegister DASHDmxRegister = {
2884 : .name = "dashin",
2885 : GF_FS_SET_DESCRIPTION("MPEG-DASH and HLS client")
2886 : GF_FS_SET_HELP("This filter reads MPEG-DASH, HLS and MS Smooth manifests and produce media output according to its mode.\n"
2887 : "\n"
2888 : "# Regular mode\n"
2889 : "This is the default mode, in which the filter produces media PIDs and frames from sources indicated in the manifest.\n"
2890 : "The default behavior is to perform adaptation according to [-algo](), but the filter can:\n"
2891 : "- run with no adaptation, to grab maximum quality.\n"
2892 : "EX gpac -i MANIFEST_URL:algo=none:start_with=max_bw -o dest.mp4\n"
2893 : "- run with no adaptation, fetching all qualities.\n"
2894 : "EX gpac -i MANIFEST_URL:split_as fout:dst=$File$.mp4:clone\n"
2895 : "\n"
2896 : "# File mode\n"
2897 : "When [-forward]() is set to `file`, the client forwards media files without demultiplexing them.\n"
2898 : "This is mostly used to expose the DASH session to a file server such as ROUTE or HTTP.\n"
2899 : "In this mode, the manifest is forwarded as an output PID.\n"
2900 : "Warning: This mode cannot be set through inheritance as it changes the link capabilities of the filter. The filter MUST be explicitly declared.\n"
2901 : "\n"
2902 : "To expose a live DASH session to route:\n"
2903 : "EX gpac -i MANIFEST_URL dashin:forward=file @ -o route://225.0.0.1:8000/\n"
2904 : "\n"
2905 : "Note: This mode used to be trigger by [-filemode]() option, still recognized.\n"
2906 : "\n"
2907 : "If the source has dependent media streams (scalability) and all qualities and initialization segments need to be forwarded, add [-split_as]().\n"
2908 : "\n"
2909 : "# Segment bound modes\n"
2910 : "When [-forward]() is set to `segs` or `mani`, the client forwards media frames (after demux) together with segment and fragment boundaries of source files.\n"
2911 : "\n"
2912 : "This mode can be used to process media data and regenerating the same manifest/segmentation.\n"
2913 : "\n"
2914 : "EX gpac -i MANIFEST_URL:forward=mani cecrypt:cfile=DRM.xml @ -o encrypted/live.mpd:pssh=mv\n"
2915 : "This will encrypt an existing DASH session, inject PSSH in manifest and segments.\n"
2916 : "\n"
2917 : "EX gpac -i MANIFEST_URL:forward=segb cecrypt:cfile=DRM.xml @ -o encrypted/live.m3u8\n"
2918 : "This will encrypt an existing DASH session and republish it as HLS, using same segment names and boundaries.\n"
2919 : "\n"
2920 : "This mode will force [-noseek]() to `true` to ensure the first segment fetched is complete, and [-split_as]() to `true` to fetch all qualities.\n"
2921 : "\n"
2922 : "Each first packet of a segment will have the following properties attached:\n"
2923 : "- `CueStart`: to indicate this is a segment start (set by demuxer if it offers `sigfrag` option)\n"
2924 : "- `FileNumber`: current segment number\n"
2925 : "- `FileName`: current segment file name without manifest base url\n"
2926 : "\n"
2927 : "If [-forward]() is set to `mani`, the first packet of a segment dispatched after a manifest update will also carry the manifest payload as a property:\n"
2928 : "- `DFManifest`: contains main manifest (MPD, M3U8 master)\n"
2929 : "- `DFVariant`: contains list of HLS child playlists as strings for the given quality\n"
2930 : "- `DFVariantName`: contains list of associated HLS child playlists name\n"
2931 : "\n"
2932 : "Each output PID will have the following properties assigned:\n"
2933 : "- `DFMode`: set to 1 for `segb` or 2 for `mani`\n"
2934 : "- `DCue`: set to `inband`\n"
2935 : "- `DFPStart`: set to current period start value\n"
2936 : "- `FileName`: set to associated init segment if any\n"
2937 : "- `Representation`: set to the associated representation ID in the manifest\n"
2938 : "- `DashDur`: set to the average segment duration as indicated in the manifest\n"
2939 : "\n"
2940 : "When the [dasher](dasher) is used together with this mode, this will force all generated segments to have the same name, duration and fragmentation properties as the input ones.\n"
2941 : "It is therefore not recommended for sessions stored/generated on local storage to generate the output in the same directory.\n"
2942 : )
2943 : .private_size = sizeof(GF_DASHDmxCtx),
2944 : .initialize = dashdmx_initialize,
2945 : .finalize = dashdmx_finalize,
2946 : .args = DASHDmxArgs,
2947 : SETCAPS(DASHDmxCaps),
2948 : .flags = GF_FS_REG_REQUIRES_RESOLVER,
2949 : .configure_pid = dashdmx_configure_pid,
2950 : .process = dashdmx_process,
2951 : .process_event = dashdmx_process_event,
2952 : .probe_data = dashdmx_probe_data,
2953 : //we accept as many input pids as loaded by the session
2954 : .max_extra_pids = (u32) -1,
2955 : };
2956 :
2957 :
2958 : #endif //GPAC_DISABLE_DASH_CLIENT
2959 :
2960 2877 : const GF_FilterRegister *dashdmx_register(GF_FilterSession *session)
2961 : {
2962 : #ifndef GPAC_DISABLE_DASH_CLIENT
2963 2877 : return &DASHDmxRegister;
2964 : #else
2965 : return NULL;
2966 : #endif
2967 : }
2968 :
2969 22 : static s32 dashdmx_rate_adaptation_ext(void *udta, u32 group_idx, u32 base_group_idx, Bool force_lower_complexity, GF_DASHCustomAlgoInfo *stats)
2970 : {
2971 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) udta;
2972 22 : return ctx->on_rate_adaptation(ctx->rt_udta, group_idx, base_group_idx, force_lower_complexity, stats);
2973 : }
2974 :
2975 :
2976 : typedef struct
2977 : {
2978 : u32 bits_per_sec;
2979 : u64 total_bytes;
2980 : u64 bytes_done;
2981 : u64 us_since_start;
2982 : u32 buffer_dur;
2983 : u32 current_seg_dur;
2984 : } GF_DASHDownloadStats;
2985 :
2986 10 : s32 dashdmx_download_monitor_ext(void *udta, u32 group_idx, u32 bits_per_sec, u64 total_bytes, u64 bytes_done, u64 us_since_start, u32 buffer_dur_ms, u32 current_seg_dur)
2987 : {
2988 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) udta;
2989 : GF_DASHDownloadStats stats;
2990 : memset(&stats, 0, sizeof(GF_DASHDownloadStats));
2991 10 : stats.bits_per_sec = bits_per_sec;
2992 10 : stats.total_bytes = total_bytes;
2993 10 : stats.bytes_done = bytes_done;
2994 10 : stats.us_since_start = us_since_start;
2995 10 : stats.buffer_dur = buffer_dur_ms;
2996 10 : stats.current_seg_dur = current_seg_dur;
2997 10 : return ctx->on_download_monitor(ctx->rt_udta, group_idx, &stats);
2998 : }
2999 :
3000 :
3001 : GF_EXPORT
3002 2 : GF_Err gf_filter_bind_dash_algo_callbacks(GF_Filter *filter, void *udta,
3003 : void (*period_reset)(void *rate_adaptation, u32 type),
3004 : void (*new_group)(void *udta, u32 group_idx, void *dash),
3005 : s32 (*rate_adaptation)(void *udta, u32 group_idx, u32 base_group_idx, Bool force_low_complex, void *stats),
3006 : s32 (*download_monitor)(void *udta, u32 group_idx, void *stats)
3007 : )
3008 : {
3009 : #ifdef GPAC_DISABLE_DASH_CLIENT
3010 : return GF_NOT_SUPPORTED;
3011 : #else
3012 2 : if (!gf_filter_is_instance_of(filter, &DASHDmxRegister))
3013 : return GF_BAD_PARAM;
3014 2 : GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) gf_filter_get_udta(filter);
3015 :
3016 2 : if (rate_adaptation) {
3017 2 : ctx->on_period_reset = period_reset;
3018 2 : ctx->on_new_group = new_group;
3019 2 : ctx->on_rate_adaptation = rate_adaptation;
3020 2 : ctx->on_download_monitor = download_monitor;
3021 2 : ctx->rt_udta = udta;
3022 2 : ctx->abort = download_monitor ? GF_TRUE : GF_FALSE;
3023 2 : gf_dash_set_algo_custom(ctx->dash, ctx, dashdmx_rate_adaptation_ext, dashdmx_download_monitor_ext);
3024 : } else {
3025 0 : ctx->on_period_reset = NULL;
3026 0 : ctx->on_new_group = NULL;
3027 0 : ctx->on_rate_adaptation = NULL;
3028 0 : ctx->on_download_monitor = NULL;
3029 0 : ctx->rt_udta = NULL;
3030 0 : ctx->abort = GF_FALSE;
3031 0 : gf_dash_set_algo(ctx->dash, GF_DASH_ALGO_GPAC_LEGACY_BUFFER);
3032 : }
3033 : return GF_OK;
3034 :
3035 : #endif
3036 : }
|