LCOV - code coverage report
Current view: top level - filters - dmx_dash.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1174 1445 81.2 %
Date: 2021-04-29 23:48:07 Functions: 42 45 93.3 %

          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, &timescale);
    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, &timescale);
    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, &timescale);
    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, &timescale);
    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             : }

Generated by: LCOV version 1.13