LCOV - code coverage report
Current view: top level - filter_core - filter.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1789 2207 81.1 %
Date: 2021-04-29 23:48:07 Functions: 129 137 94.2 %

          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 / filters sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terfsess 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 "filter_session.h"
      27             : #include <gpac/network.h>
      28             : 
      29             : //helper functions
      30      258472 : void gf_void_del(void *p)
      31             : {
      32      258472 :         gf_free(p);
      33      258472 : }
      34       83265 : void gf_filterpacket_del(void *p)
      35             : {
      36             :         GF_FilterPacket *pck=(GF_FilterPacket *)p;
      37       83265 :         if (pck->data) gf_free(pck->data);
      38       83265 :         gf_free(p);
      39       83265 : }
      40             : 
      41             : static void gf_filter_parse_args(GF_Filter *filter, const char *args, GF_FilterArgType arg_type, Bool for_script);
      42             : 
      43       50529 : const char *gf_fs_path_escape_colon(GF_FilterSession *sess, const char *path)
      44             : {
      45             :         const char *res, *arg;
      46       50529 :         if (!path) return NULL;
      47       50529 :         if (sess->sep_args != ':')
      48           0 :                 return strchr(path, sess->sep_args);
      49             : 
      50       50529 :         res = gf_url_colon_suffix(path);
      51             :         //if path is one of this proto, check if we have a port specified
      52       50529 :         if (!strncmp(path, "tcp://", 6)
      53       50509 :                 || !strncmp(path, "udp://", 6)
      54       50499 :                 || !strncmp(path, "tcpu://", 7)
      55       50499 :                 || !strncmp(path, "udpu://", 7)
      56       50499 :                 || !strncmp(path, "rtp://", 6)
      57       50494 :                 || !strncmp(path, "route://", 8)
      58             :         ) {
      59          55 :                 char *sep2 = res ? strchr(res+1, ':') : NULL;
      60          55 :                 char *sep3 = res ? strchr(res+1, '/') : NULL;
      61          55 :                 if (sep2 && sep3 && (sep2>sep3)) {
      62           0 :                         sep2 = strchr(sep3, ':');
      63             :                 }
      64          55 :                 if (sep2 || sep3 || res) {
      65          52 :                         u32 port = 0;
      66          52 :                         if (sep2) {
      67          41 :                                 sep2[0] = 0;
      68          41 :                                 if (sep3) sep3[0] = 0;
      69             :                         }
      70          11 :                         else if (sep3) sep3[0] = 0;
      71          52 :                         if (sscanf(res+1, "%d", &port)==1) {
      72             :                                 char szPort[20];
      73          37 :                                 snprintf(szPort, 20, "%d", port);
      74          37 :                                 if (strcmp(res+1, szPort))
      75           0 :                                         port = 0;
      76             :                         }
      77          52 :                         if (sep2) sep2[0] = ':';
      78          52 :                         if (sep3) sep3[0] = '/';
      79             : 
      80          52 :                         if (port) res = sep2;
      81             :                 }
      82             :         }
      83             : 
      84             : 
      85       50529 :         arg = strchr(path, sess->sep_name);
      86       50529 :         if (arg && res && (res > arg))
      87       19679 :                 res = gf_url_colon_suffix(arg+1);
      88             :         return res;
      89             : }
      90             : 
      91       20668 : static const char *gf_filter_get_args_stripped(GF_FilterSession *fsess, const char *in_args, Bool is_dst)
      92             : {
      93             :         char szEscape[7];
      94             :         char *args_striped = NULL;
      95       20668 :         if (in_args) {
      96             :                 const char *key;
      97       12659 :                 if (is_dst) {
      98             :                         key = "dst";
      99             :                 } else {
     100             :                         key = "src";
     101             :                 }
     102       12659 :                 if (!strncmp(in_args, key, 3) && (in_args[3]==fsess->sep_name)) {
     103             :                         args_striped = (char *) in_args;
     104             :                 } else {
     105             :                         char szDst[6];
     106        6135 :                         sprintf(szDst, "%c%s%c", fsess->sep_name, key, fsess->sep_name);
     107        6135 :                         args_striped = strstr(in_args, szDst);
     108             :                 }
     109             : 
     110       12659 :                 if (args_striped) {
     111        6524 :                         args_striped += 4;
     112        6524 :                         if (!strncmp(args_striped, "gcryp://", 8))
     113           0 :                                 args_striped += 8;
     114        6524 :                         args_striped = (char *)gf_fs_path_escape_colon(fsess, args_striped);
     115        6524 :                         if (args_striped) args_striped ++;
     116             :                 } else {
     117             :                         args_striped = (char *)in_args;
     118             :                 }
     119             :         }
     120       20668 :         sprintf(szEscape, "gpac%c", fsess->sep_args);
     121       20668 :         if (args_striped && !strncmp(args_striped, szEscape, 5))
     122        1848 :                 return args_striped + 5;
     123             : 
     124             :         return args_striped;
     125             : }
     126             : 
     127         344 : const char *gf_filter_get_dst_args(GF_Filter *filter)
     128             : {
     129         366 :         return gf_filter_get_args_stripped(filter->session, filter->dst_args, GF_TRUE);
     130             : }
     131           0 : const char *gf_filter_get_src_args(GF_Filter *filter)
     132             : {
     133           0 :         return filter->orig_args ? filter->orig_args : filter->src_args;
     134             : }
     135             : 
     136         665 : char *gf_filter_get_dst_name(GF_Filter *filter)
     137             : {
     138             :         char szDst[5];
     139             :         char *dst, *arg_sep, *res, *dst_args;
     140         665 :         sprintf(szDst, "dst%c", filter->session->sep_name);
     141             : 
     142         665 :         dst_args = filter->dst_args;
     143         665 :         if (!dst_args) {
     144           1 :                 GF_FilterPid *outpid = gf_list_get(filter->output_pids, 0);
     145           1 :                 if (outpid) dst_args = outpid->filter->dst_args;
     146             : 
     147           1 :                 if (!dst_args) {
     148           1 :                         GF_Filter *outf = gf_list_get(filter->destination_links, 0);
     149           1 :                         if (!outf || !outf->dst_args)
     150           1 :                                 outf = gf_list_get(filter->destination_filters, 0);
     151           1 :                         if (outf)
     152           0 :                                 dst_args = filter->dst_args;
     153             :                 }
     154             :         }
     155         665 :         dst = dst_args ? strstr(dst_args, szDst) : NULL;
     156         664 :         if (!dst) return NULL;
     157             : 
     158         664 :         arg_sep = (char*) gf_fs_path_escape_colon(filter->session, dst+4);
     159             : 
     160         664 :         if (arg_sep) {
     161         574 :                 arg_sep[0] = 0;
     162         574 :                 res = gf_strdup(dst+4);
     163         574 :                 arg_sep[0] = filter->session->sep_args;
     164             :         } else {
     165          90 :                 res = gf_strdup(dst+4);
     166             :         }
     167             :         return res;
     168             : }
     169             : 
     170             : /*push arguments from source and dest into the final argument string. We strip the following
     171             : - FID: will be inherited from last explicit filter in the chain
     172             : - SID: is cloned during the resolution while loading the filter chain
     173             : - TAG: TAG is never inherited
     174             : - local options: by definition these options only apply to the loaded filter, and are never inherited
     175             : - user-assigned PID properties on destination (they only apply after the destination, not in the new chain).
     176             : 
     177             : Note that user-assigned PID properties assigned on source are inherited so that
     178             :         -i SRC:#PidProp=PidVal -o dst
     179             : 
     180             : will have all intermediate filters loaded for the resolution (eg demux) set this property.
     181             : 
     182             : */
     183        3664 : static void filter_push_args(GF_FilterSession *fsess, char **out_args, char *in_args, Bool is_src, Bool first_sep_inserted)
     184             : {
     185             :         char szSep[2];
     186             :         Bool prev_is_db_sep = GF_FALSE;
     187        3664 :         szSep[0] = fsess->sep_args;
     188        3664 :         szSep[1] = 0;
     189       18128 :         while (in_args) {
     190       14464 :                 char *sep = strchr(in_args, fsess->sep_args);
     191       14464 :                 if (sep) sep[0] = 0;
     192             : 
     193       14464 :                 if (!strncmp(in_args, "gfloc", 5) && (!in_args[5] || (in_args[5]==fsess->sep_args))) {
     194           1 :                         if (sep) sep[0] = fsess->sep_args;
     195           1 :                         return;
     196             :                 }
     197       14463 :                 if (!strncmp(in_args, "FID", 3) && (in_args[3]==fsess->sep_name)) {
     198             :                 }
     199       14187 :                 else if (!strncmp(in_args, "SID", 3) && (in_args[3]==fsess->sep_name)) {
     200             :                 }
     201       13898 :                 else if (!strncmp(in_args, "TAG", 3) && (in_args[3]==fsess->sep_name)) {
     202             :                 }
     203       13898 :                 else if (!is_src && (in_args[0]==fsess->sep_frag)) {
     204             : 
     205             :                 } else {
     206       13898 :                         if (*out_args && !first_sep_inserted) {
     207       10321 :                                 gf_dynstrcat(out_args, szSep, NULL);
     208       10321 :                                 if (prev_is_db_sep)
     209           3 :                                         gf_dynstrcat(out_args, szSep, NULL);
     210             :                         }
     211       13898 :                         gf_dynstrcat(out_args, in_args, NULL);
     212             :                         first_sep_inserted = GF_FALSE;
     213             :                 }
     214       14463 :                 if (!sep) break;
     215       10800 :                 sep[0] = fsess->sep_args;
     216       10800 :                 in_args = sep+1;
     217             :                 prev_is_db_sep = GF_FALSE;
     218       10800 :                 if (in_args[0] == fsess->sep_args) {
     219           3 :                         in_args ++;
     220             :                         prev_is_db_sep = GF_TRUE;
     221             :                 }
     222             :         }
     223             : }
     224             : 
     225       13733 : GF_Filter *gf_filter_new(GF_FilterSession *fsess, const GF_FilterRegister *freg, const char *src_args, const char *dst_args, GF_FilterArgType arg_type, GF_Err *err, GF_Filter *multi_sink_target, Bool is_dynamic_filter)
     226             : {
     227             :         char szName[200];
     228             :         const char *dst_striped = NULL;
     229             :         const char *src_striped = NULL;
     230             :         GF_Filter *filter;
     231             :         GF_Err e;
     232             :         u32 i;
     233             :         assert(fsess);
     234             : 
     235       13733 :         GF_SAFEALLOC(filter, GF_Filter);
     236       13733 :         if (!filter) {
     237           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to alloc filter for %s\n", freg->name));
     238             :                 return NULL;
     239             :         }
     240       13733 :         filter->freg = freg;
     241       13733 :         filter->session = fsess;
     242       13733 :         filter->max_extra_pids = freg->max_extra_pids;
     243       13733 :         filter->dynamic_filter = is_dynamic_filter;
     244             : 
     245       13733 :         if (fsess->use_locks) {
     246         123 :                 snprintf(szName, 200, "Filter%sPackets", filter->freg->name);
     247         123 :                 filter->pcks_mx = gf_mx_new(szName);
     248             :         }
     249             : 
     250             :         //for now we always use a lock on the filter task lists
     251             :         //this mutex protects the task list and the number of process virtual tasks
     252             :         //we cannot remove it in non-threaded mode since we have no garantee that a filter won't use threading on its own
     253       13733 :         snprintf(szName, 200, "Filter%sTasks", filter->freg->name);
     254       13733 :         filter->tasks_mx = gf_mx_new(szName);
     255             : 
     256       13733 :         filter->tasks = gf_fq_new(filter->tasks_mx);
     257             : 
     258       13733 :         if (! (filter->session->flags & GF_FS_FLAG_NO_RESERVOIR)) {
     259       13733 :                 filter->pcks_shared_reservoir = gf_fq_new(filter->pcks_mx);
     260       13733 :                 filter->pcks_alloc_reservoir = gf_fq_new(filter->pcks_mx);
     261       13733 :                 filter->pcks_inst_reservoir = gf_fq_new(filter->pcks_mx);
     262             :         }
     263             : 
     264       13733 :         filter->pending_pids = gf_fq_new(NULL);
     265             : 
     266       13733 :         filter->blacklisted = gf_list_new();
     267       13733 :         filter->destination_filters = gf_list_new();
     268       13733 :         filter->destination_links = gf_list_new();
     269       13733 :         filter->temp_input_pids = gf_list_new();
     270             : 
     271       13733 :         filter->bundle_idx_at_resolution = -1;
     272       13733 :         filter->cap_idx_at_resolution = -1;
     273             : 
     274       13733 :         if (fsess->filters_mx) gf_mx_p(fsess->filters_mx);
     275       13733 :         gf_list_add(fsess->filters, filter);
     276       13733 :         if (fsess->filters_mx) gf_mx_v(fsess->filters_mx);
     277             : 
     278       13733 :         filter->multi_sink_target = multi_sink_target;
     279             : 
     280             :         src_striped = src_args;
     281             :         dst_striped = NULL;
     282       13733 :         switch (arg_type) {
     283         496 :         case GF_FILTER_ARG_EXPLICIT_SOURCE_NO_DST_INHERIT:
     284         496 :                 filter->arg_type = arg_type = GF_FILTER_ARG_EXPLICIT_SOURCE;
     285         496 :                 filter->no_dst_arg_inherit = GF_TRUE;
     286         496 :                 break;
     287         671 :         case GF_FILTER_ARG_INHERIT_SOURCE_ONLY:
     288         671 :                 filter->arg_type = arg_type = GF_FILTER_ARG_INHERIT;
     289         671 :                 filter->no_dst_arg_inherit = GF_TRUE;
     290         671 :                 break;
     291       12566 :         default:
     292       12566 :                 filter->arg_type = arg_type;
     293       12566 :                 dst_striped = gf_filter_get_args_stripped(fsess, dst_args, GF_TRUE);
     294       12566 :                 break;
     295             :         }
     296             : 
     297       13733 :         if ((arg_type!=GF_FILTER_ARG_EXPLICIT_SOURCE) && (arg_type!=GF_FILTER_ARG_EXPLICIT)) {
     298        7736 :                 src_striped = gf_filter_get_args_stripped(fsess, src_args, GF_FALSE);
     299             :         }
     300             : 
     301             :         //if we already concatenated our dst args to this source filter (eg this is an intermediate dynamically loaded one)
     302             :         //don't reappend the args
     303       13733 :         if (dst_striped && src_striped && strstr(src_striped, dst_striped) != NULL) {
     304             :                 dst_striped = NULL;
     305             :         }
     306             : 
     307       13733 :         if (src_striped && dst_striped) {
     308             :                 char *all_args;
     309             :                 const char *dbsep;
     310             :                 char *localarg_marker;
     311             :                 u32 nb_db_sep=0, src_arg_len;
     312             :                 char szDBSep[3];
     313             :                 Bool insert_escape = GF_FALSE;
     314             :                 Bool dst_sep_inserted = GF_FALSE;
     315             :                 //source has a URL (local or not), escape it to make sure we don't pass dst args as params to the URL
     316        1832 :                 if ((strstr(src_striped, "src=") || strstr(src_striped, "dst=")) && strstr(src_striped, "://")){
     317             :                         char szEscape[10];
     318           0 :                         sprintf(szEscape, "%cgpac", fsess->sep_args);
     319           0 :                         if (strstr(src_striped, szEscape)==NULL) {
     320             :                                 insert_escape = GF_TRUE;
     321             :                         }
     322             :                 }
     323             : 
     324        1832 :                 szDBSep[0] = szDBSep[1] = filter->session->sep_args;
     325        1832 :                 szDBSep[2] = 0;
     326             : 
     327             :                 //handle the case where the first src arg was escaped ("filter::opt"), src args is now ":opt
     328             :                 //consider we have one double sep
     329        1832 :                 if (src_striped[0] == filter->session->sep_args)
     330             :                         nb_db_sep = 1;
     331             : 
     332             :                 dbsep = src_striped;
     333        1833 :                 while (dbsep) {
     334        1833 :                         char *next_dbsep = strstr(dbsep, szDBSep);
     335        1833 :                         if (!next_dbsep) break;
     336           1 :                         nb_db_sep++;
     337           1 :                         dbsep = next_dbsep+2;
     338             :                 }
     339        1832 :                 if (nb_db_sep % 2) nb_db_sep=1;
     340             :                 else nb_db_sep=0;
     341             : 
     342             : 
     343             :                 if (!nb_db_sep) {
     344        1831 :                         szDBSep[1] = 0;
     345             :                 }
     346             :                 //src_striped is ending with our separator, don't insert a new one
     347        1832 :                 src_arg_len = (u32)strlen(src_striped);
     348        1832 :                 if (src_arg_len && (src_striped[src_arg_len-1] == filter->session->sep_args)) {
     349           0 :                         szDBSep[0] = 0;
     350             :                 }
     351             : 
     352             :                 //push src args
     353        1832 :                 all_args = NULL;
     354        1832 :                 filter_push_args(fsess, &all_args, (char *) src_striped, GF_TRUE, GF_FALSE);
     355             : 
     356        1832 :                 if (all_args && insert_escape) {
     357           0 :                         gf_dynstrcat(&all_args, szDBSep, NULL);
     358           0 :                         gf_dynstrcat(&all_args, "gpac", NULL);
     359           0 :                         dst_sep_inserted = GF_TRUE;
     360        1832 :                 } else if (all_args) {
     361        1781 :                         gf_dynstrcat(&all_args, szDBSep, NULL);
     362             :                         dst_sep_inserted = GF_TRUE;
     363             :                 }
     364             :                 //push dst args
     365        1832 :                 filter_push_args(fsess, &all_args, (char *) dst_striped, GF_FALSE, dst_sep_inserted);
     366             : 
     367             : 
     368        1832 :                 localarg_marker = all_args ? strstr(all_args, "gfloc") : NULL;
     369        1796 :                 if (localarg_marker) {
     370           0 :                         localarg_marker[0]=0;
     371           0 :                         if (strlen(all_args) && (localarg_marker[-1]==fsess->sep_args))
     372           0 :                                 localarg_marker[-1]=0;
     373             :                 }
     374        1832 :                 e = gf_filter_new_finalize(filter, all_args, arg_type);
     375        1832 :                 filter->orig_args = all_args;
     376             :                 src_striped = NULL;
     377       11901 :         } else if (dst_striped) {
     378         911 :                 e = gf_filter_new_finalize(filter, dst_striped, arg_type);
     379         911 :                 filter->orig_args = gf_strdup(dst_striped);
     380             :                 src_striped = NULL;
     381             :         } else {
     382       10990 :                 e = gf_filter_new_finalize(filter, src_striped, arg_type);
     383             :         }
     384       13733 :         filter->dst_args = dst_args ? gf_strdup(dst_args) : NULL;
     385             : 
     386       13733 :         if (e) {
     387           3 :                 if (!filter->setup_notified && (e<0) ) {
     388           3 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Error %s while instantiating filter %s\n", gf_error_to_string(e),freg->name));
     389           3 :                         gf_filter_setup_failure(filter, e);
     390             :                 }
     391           3 :                 if (err) *err = e;
     392             :                 //filter requested cancelation of filter session upon init
     393           3 :                 if (e==GF_EOS) {
     394           0 :                         fsess->run_status = GF_EOS;
     395           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s requested cancelation of filter session\n", freg->name));
     396             :                 }
     397             :                 return NULL;
     398             :         }
     399       13730 :         if (filter && src_striped)
     400        9576 :                 filter->orig_args = gf_strdup(src_striped);
     401             : 
     402       45758 :         for (i=0; i<freg->nb_caps; i++) {
     403       42843 :                 if (freg->caps[i].flags & GF_CAPFLAG_OUTPUT) {
     404       10815 :                         filter->has_out_caps = GF_TRUE;
     405       10815 :                         break;
     406             :                 }
     407             :         }
     408             :         if (filter) {
     409       13730 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Created filter register %s args %s\n", freg->name, filter->orig_args ? filter->orig_args : "none"));
     410             :         }
     411             :         return filter;
     412             : }
     413             : 
     414       13736 : GF_Err gf_filter_new_finalize(GF_Filter *filter, const char *args, GF_FilterArgType arg_type)
     415             : {
     416       13736 :         gf_filter_set_name(filter, NULL);
     417             : 
     418       13736 :         gf_filter_parse_args(filter, args, arg_type, GF_FALSE);
     419             : 
     420       13736 :         if (filter->freg->initialize) {
     421             :                 GF_Err e;
     422             :                 FSESS_CHECK_THREAD(filter)
     423       11838 :                 if (((arg_type==GF_FILTER_ARG_EXPLICIT_SOURCE) || (arg_type==GF_FILTER_ARG_EXPLICIT_SINK)) && !filter->orig_args) {
     424        5168 :                         filter->orig_args = (char *)args;
     425        5168 :                         e = filter->freg->initialize(filter);
     426        5168 :                         filter->orig_args = NULL;
     427             :                 } else {
     428        6670 :                         e = filter->freg->initialize(filter);
     429             :                 }
     430       11838 :                 if (e) return e;
     431             :         }
     432       13734 :         if ((filter->freg->flags & GF_FS_REG_SCRIPT) && filter->freg->update_arg) {
     433             :                 GF_Err e;
     434          41 :                 gf_filter_parse_args(filter, args, arg_type, GF_TRUE);
     435          41 :                 e = filter->freg->update_arg(filter, NULL, NULL);
     436          41 :                 if (e) return e;
     437             :         }
     438             : 
     439             :         //flush all pending pid init requests
     440       13733 :         if (filter->has_pending_pids) {
     441        1076 :                 filter->has_pending_pids=GF_FALSE;
     442        3282 :                 while (gf_fq_count(filter->pending_pids)) {
     443        1130 :                         GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
     444        1130 :                         gf_filter_pid_post_init_task(filter, pid);
     445             :                 }
     446             :         }
     447             : 
     448             : #ifdef GPAC_HAS_QJS
     449       13733 :         jsfs_on_filter_created(filter);
     450             : #endif
     451       13733 :         if (filter->session->on_filter_create_destroy)
     452          22 :                 filter->session->on_filter_create_destroy(filter->session->rt_udta, filter, GF_FALSE);
     453             :         return GF_OK;
     454             : }
     455             : 
     456         138 : void gf_filter_reset_pending_packets(GF_Filter *filter)
     457             : {
     458             :         //may happen when a filter is removed from the chain
     459         138 :         if (filter->postponed_packets) {
     460           2 :                 while (gf_list_count(filter->postponed_packets)) {
     461           1 :                         GF_FilterPacket *pck = gf_list_pop_front(filter->postponed_packets);
     462           1 :                         gf_filter_packet_destroy(pck);
     463             :                 }
     464           1 :                 gf_list_del(filter->postponed_packets);
     465           1 :                 filter->postponed_packets = NULL;
     466             :         }
     467         138 : }
     468             : 
     469             : static void reset_filter_args(GF_Filter *filter);
     470             : 
     471             : //when destroying the filter queue we have to skip tasks marked as notified, since they are also present in the
     472             : //session task list
     473         990 : void task_del(void *_task)
     474             : {
     475             :         GF_FSTask *task = _task;
     476         990 :         if (!task->notified) gf_free(task);
     477         990 : }
     478             : 
     479       13724 : void gf_filter_del(GF_Filter *filter)
     480             : {
     481       13724 :         GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s destruction\n", filter->name));
     482             :         assert(filter);
     483             : //      assert(!filter->detached_pid_inst);
     484             :         assert(!filter->detach_pid_tasks_pending);
     485             : //      assert(!filter->swap_pidinst_dst);
     486             :         assert(!filter->swap_pidinst_src);
     487             : 
     488             : 
     489             : #ifdef GPAC_HAS_QJS
     490       13724 :         jsfs_on_filter_destroyed(filter);
     491             : #endif
     492             : 
     493       13724 :         if (filter->session->on_filter_create_destroy)
     494          22 :                 filter->session->on_filter_create_destroy(filter->session->rt_udta, filter, GF_TRUE);
     495             : 
     496             : #ifndef GPAC_DISABLE_3D
     497       13724 :         gf_list_del_item(filter->session->gl_providers, filter);
     498       13724 :         gf_fs_check_gl_provider(filter->session);
     499             : #endif
     500             : 
     501             : #ifdef GPAC_MEMORY_TRACKING
     502       13724 :         if (filter->session->check_allocs) {
     503       13361 :                 if (filter->max_nb_process>10 && (filter->max_nb_consecutive_process * 10 < filter->max_nb_process)) {
     504           8 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("\nFilter %s extensively uses memory alloc/free in process(): \n", filter->name));
     505           8 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("\tmax stats of over %d calls (%d consecutive calls with no alloc/free):\n", filter->max_nb_process, filter->max_nb_consecutive_process));
     506           8 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("\t\t%d allocs %d callocs %d reallocs %d free\n", filter->max_stats_nb_alloc, filter->max_stats_nb_calloc, filter->max_stats_nb_realloc, filter->max_stats_nb_free));
     507           8 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("\tPlease consider rewriting the code\n"));
     508             :                 }
     509             :         }
     510             : #endif
     511             : 
     512             :         //may happen when a filter is removed from the chain
     513       13724 :         if (filter->postponed_packets) {
     514           0 :                 while (gf_list_count(filter->postponed_packets)) {
     515           0 :                         GF_FilterPacket *pck = gf_list_pop_front(filter->postponed_packets);
     516           0 :                         gf_filter_packet_destroy(pck);
     517             :                 }
     518           0 :                 gf_list_del(filter->postponed_packets);
     519           0 :                 filter->postponed_packets = NULL;
     520             :         }
     521             : 
     522             :         //delete output pids before the packet reservoir
     523       24595 :         while (gf_list_count(filter->output_pids)) {
     524       10871 :                 gf_filter_pid_del(gf_list_pop_back(filter->output_pids));
     525             :         }
     526       13724 :         gf_list_del(filter->output_pids);
     527             : 
     528       13724 :         gf_list_del(filter->blacklisted);
     529       13724 :         gf_list_del(filter->destination_filters);
     530       13724 :         gf_list_del(filter->destination_links);
     531       13724 :         gf_list_del(filter->source_filters);
     532       13724 :         gf_list_del(filter->temp_input_pids);
     533             : 
     534       13724 :         gf_list_del(filter->input_pids);
     535       13724 :         gf_fq_del(filter->tasks, task_del);
     536       13724 :         gf_fq_del(filter->pending_pids, NULL);
     537             : 
     538       13724 :         reset_filter_args(filter);
     539       13724 :         if (filter->src_args) gf_free(filter->src_args);
     540             : 
     541       13724 :         if (filter->pcks_shared_reservoir)
     542       13724 :                 gf_fq_del(filter->pcks_shared_reservoir, gf_void_del);
     543       13724 :         if (filter->pcks_inst_reservoir)
     544       13724 :                 gf_fq_del(filter->pcks_inst_reservoir, gf_void_del);
     545       13724 :         if (filter->pcks_alloc_reservoir)
     546       13724 :                 gf_fq_del(filter->pcks_alloc_reservoir, gf_filterpacket_del);
     547             : 
     548       13724 :         gf_mx_del(filter->pcks_mx);
     549       13724 :         if (filter->tasks_mx)
     550       13724 :                 gf_mx_del(filter->tasks_mx);
     551             : 
     552       13724 :         if (filter->id) gf_free(filter->id);
     553       13724 :         if (filter->source_ids) gf_free(filter->source_ids);
     554       13724 :         if (filter->dynamic_source_ids) gf_free(filter->dynamic_source_ids);
     555       13724 :         if (filter->filter_udta) gf_free(filter->filter_udta);
     556       13724 :         if (filter->orig_args) gf_free(filter->orig_args);
     557       13724 :         if (filter->dst_args) gf_free(filter->dst_args);
     558       13724 :         if (filter->name) gf_free(filter->name);
     559       13724 :         if (filter->status_str) gf_free(filter->status_str);
     560       13724 :         if (filter->restricted_source_id) gf_free(filter->restricted_source_id);
     561             : 
     562       13724 :         if (!filter->session->in_final_flush && !filter->session->run_status) {
     563             :                 u32 i, count;
     564         211 :                 gf_mx_p(filter->session->filters_mx);
     565         211 :                 count = gf_list_count(filter->session->filters);
     566        2218 :                 for (i=0; i<count; i++) {
     567        2007 :                         GF_Filter *a_filter = gf_list_get(filter->session->filters, i);
     568        2007 :                         gf_mx_p(a_filter->tasks_mx);
     569        2007 :                         gf_list_del_item(a_filter->destination_filters, filter);
     570        2007 :                         gf_list_del_item(a_filter->destination_links, filter);
     571        2007 :                         gf_list_del_item(a_filter->source_filters, filter);
     572        2007 :                         if (a_filter->cap_dst_filter==filter)
     573           0 :                                 a_filter->cap_dst_filter = NULL;
     574        2007 :                         if (a_filter->cloned_from == filter)
     575           0 :                                 a_filter->cloned_from = NULL;
     576        2007 :                         if (a_filter->on_setup_error_filter == filter)
     577           0 :                                 a_filter->on_setup_error_filter = NULL;
     578        2007 :                         if (a_filter->target_filter == filter)
     579           0 :                                 a_filter->target_filter = NULL;
     580        2007 :                         if (a_filter->dst_filter == filter)
     581           6 :                                 a_filter->dst_filter = NULL;
     582        2007 :                         gf_mx_v(a_filter->tasks_mx);
     583             :                 }
     584         211 :                 gf_mx_v(filter->session->filters_mx);
     585             :         }
     586       13724 :         if (filter->instance_description)
     587          41 :                 gf_free(filter->instance_description);
     588       13724 :         if (filter->instance_version)
     589          41 :                 gf_free(filter->instance_version);
     590       13724 :         if (filter->instance_author)
     591          41 :                 gf_free(filter->instance_author);
     592       13724 :         if (filter->instance_help)
     593          41 :                 gf_free(filter->instance_help);
     594             : 
     595             : #ifdef GPAC_HAS_QJS
     596       13724 :         if (filter->iname)
     597          15 :                 gf_free(filter->iname);
     598             : #endif
     599             : 
     600       13724 :         if (filter->freg && (filter->freg->flags & GF_FS_REG_CUSTOM)) {
     601           4 :                 if (! (filter->freg->flags & GF_FS_REG_SCRIPT))
     602           3 :                         if (filter->forced_caps) gf_free( (void *) filter->forced_caps);
     603           4 :                 gf_free( (char *) filter->freg->name);
     604           4 :                 gf_free( (void *) filter->freg);
     605             :         }
     606       13724 :         gf_free(filter);
     607       13724 : }
     608             : 
     609             : GF_EXPORT
     610    37075399 : void *gf_filter_get_udta(GF_Filter *filter)
     611             : {
     612             :         assert(filter);
     613             : 
     614    37075399 :         return filter->filter_udta;
     615             : }
     616             : 
     617             : GF_EXPORT
     618        1450 : const char * gf_filter_get_name(GF_Filter *filter)
     619             : {
     620             :         assert(filter);
     621        1450 :         if (filter->name)
     622             :                 return (const char *)filter->name;
     623           0 :         return (const char *)filter->freg->name;
     624             : }
     625             : 
     626             : GF_EXPORT
     627       14274 : void gf_filter_set_name(GF_Filter *filter, const char *name)
     628             : {
     629             :         assert(filter);
     630             : 
     631       14274 :         if (filter->name) gf_free(filter->name);
     632       14274 :         filter->name = gf_strdup(name ? name : filter->freg->name);
     633       14274 : }
     634             : 
     635         391 : static void gf_filter_set_id(GF_Filter *filter, const char *ID)
     636             : {
     637             :         assert(filter);
     638             : 
     639         391 :         if (filter->id) gf_free(filter->id);
     640         391 :         filter->id = ID ? gf_strdup(ID) : NULL;
     641         391 : }
     642             : 
     643             : GF_EXPORT
     644         347 : void gf_filter_reset_source(GF_Filter *filter)
     645             : {
     646         347 :         if (filter && filter->source_ids) {
     647           6 :                 gf_mx_p(filter->session->filters_mx);
     648           6 :                 gf_free(filter->source_ids);
     649           6 :                 filter->source_ids = NULL;
     650           6 :                 gf_mx_v(filter->session->filters_mx);
     651             :         }
     652         347 : }
     653             : 
     654        2458 : static void gf_filter_set_sources(GF_Filter *filter, const char *sources_ID)
     655             : {
     656             :         assert(filter);
     657             : 
     658        2458 :         gf_mx_p(filter->session->filters_mx);
     659             : 
     660        2458 :         if (!sources_ID) {
     661           0 :                 if (filter->source_ids) gf_free(filter->source_ids);
     662           0 :                 filter->source_ids = NULL;
     663        2458 :         } else if (!filter->source_ids) {
     664        2330 :                 filter->source_ids = gf_strdup(sources_ID);
     665             :         } else {
     666         128 :                 gf_dynstrcat(&filter->source_ids, sources_ID, ",");
     667             :         }
     668             : 
     669        2458 :         gf_mx_v(filter->session->filters_mx);
     670        2458 : }
     671             : 
     672      501814 : static void gf_filter_set_arg(GF_Filter *filter, const GF_FilterArgs *a, GF_PropertyValue *argv)
     673             : {
     674             : #ifdef WIN32
     675             :         void *ptr = (void *) (((char*) filter->filter_udta) + a->offset_in_private);
     676             : #else
     677      501814 :         void *ptr = filter->filter_udta + a->offset_in_private;
     678             : #endif
     679             :         Bool res = GF_FALSE;
     680      501814 :         if (a->offset_in_private<0) return;
     681             : 
     682      501587 :         switch (argv->type) {
     683      209251 :         case GF_PROP_BOOL:
     684      209251 :                 if (a->offset_in_private + sizeof(Bool) <= filter->freg->private_size) {
     685      209251 :                         *(Bool *)ptr = argv->value.boolean;
     686             :                         res = GF_TRUE;
     687             :                 }
     688             :                 break;
     689       16282 :         case GF_PROP_SINT:
     690       16282 :                 if (a->offset_in_private + sizeof(s32) <= filter->freg->private_size) {
     691       16282 :                         *(s32 *)ptr = argv->value.sint;
     692             :                         res = GF_TRUE;
     693             :                 }
     694             :                 break;
     695      158397 :         case GF_PROP_UINT:
     696             :         case GF_PROP_4CC:
     697      158397 :                 if (a->offset_in_private + sizeof(u32) <= filter->freg->private_size) {
     698      158397 :                         *(u32 *)ptr = argv->value.uint;
     699             :                         res = GF_TRUE;
     700             :                 }
     701             :                 break;
     702         866 :         case GF_PROP_LSINT:
     703         866 :                 if (a->offset_in_private + sizeof(s64) <= filter->freg->private_size) {
     704         866 :                         *(s64 *)ptr = argv->value.longsint;
     705             :                         res = GF_TRUE;
     706             :                 }
     707             :                 break;
     708         963 :         case GF_PROP_LUINT:
     709         963 :                 if (a->offset_in_private + sizeof(u64) <= filter->freg->private_size) {
     710         963 :                         *(u64 *)ptr = argv->value.longuint;
     711             :                         res = GF_TRUE;
     712             :                 }
     713             :                 break;
     714        9250 :         case GF_PROP_FLOAT:
     715        9250 :                 if (a->offset_in_private + sizeof(Fixed) <= filter->freg->private_size) {
     716        9250 :                         *(Fixed *)ptr = argv->value.fnumber;
     717             :                         res = GF_TRUE;
     718             :                 }
     719             :                 break;
     720       23220 :         case GF_PROP_DOUBLE:
     721       23220 :                 if (a->offset_in_private + sizeof(Double) <= filter->freg->private_size) {
     722       23220 :                         *(Double *)ptr = argv->value.number;
     723             :                         res = GF_TRUE;
     724             :                 }
     725             :                 break;
     726       15269 :         case GF_PROP_FRACTION:
     727       15269 :                 if (a->offset_in_private + sizeof(GF_Fraction) <= filter->freg->private_size) {
     728       15269 :                         *(GF_Fraction *)ptr = argv->value.frac;
     729             :                         res = GF_TRUE;
     730             :                 }
     731             :                 break;
     732        9198 :         case GF_PROP_FRACTION64:
     733        9198 :                 if (a->offset_in_private + sizeof(GF_Fraction64) <= filter->freg->private_size) {
     734        9198 :                         *(GF_Fraction64 *)ptr = argv->value.lfrac;
     735             :                         res = GF_TRUE;
     736             :                 }
     737             :                 break;
     738        3885 :         case GF_PROP_VEC2I:
     739        3885 :                 if (a->offset_in_private + sizeof(GF_PropVec2i) <= filter->freg->private_size) {
     740        3885 :                         *(GF_PropVec2i *)ptr = argv->value.vec2i;
     741             :                         res = GF_TRUE;
     742             :                 }
     743             :                 break;
     744           0 :         case GF_PROP_VEC2:
     745           0 :                 if (a->offset_in_private + sizeof(GF_PropVec2) <= filter->freg->private_size) {
     746           0 :                         *(GF_PropVec2 *)ptr = argv->value.vec2;
     747             :                         res = GF_TRUE;
     748             :                 }
     749             :                 break;
     750           0 :         case GF_PROP_VEC3I:
     751           0 :                 if (a->offset_in_private + sizeof(GF_PropVec3i) <= filter->freg->private_size) {
     752           0 :                         *(GF_PropVec3i *)ptr = argv->value.vec3i;
     753             :                         res = GF_TRUE;
     754             :                 }
     755             :                 break;
     756          28 :         case GF_PROP_VEC4I:
     757          28 :                 if (a->offset_in_private + sizeof(GF_PropVec4i) <= filter->freg->private_size) {
     758          28 :                         *(GF_PropVec4i *)ptr = argv->value.vec4i;
     759             :                         res = GF_TRUE;
     760             :                 }
     761             :                 break;
     762             : 
     763       41123 :         case GF_PROP_NAME:
     764             :         case GF_PROP_STRING:
     765       41123 :                 if (a->offset_in_private + sizeof(char *) <= filter->freg->private_size) {
     766       41123 :                         if (*(char **)ptr) gf_free( * (char **)ptr);
     767             :                         //we don't strdup since we don't free the string at the caller site
     768       41123 :                         *(char **)ptr = argv->value.string;
     769             :                         res = GF_TRUE;
     770             :                 }
     771             :                 break;
     772          76 :         case GF_PROP_DATA:
     773             :         case GF_PROP_DATA_NO_COPY:
     774             :         case GF_PROP_CONST_DATA:
     775          76 :                 if (a->offset_in_private + sizeof(GF_PropData) <= filter->freg->private_size) {
     776             :                         GF_PropData *pd = (GF_PropData *) ptr;
     777          76 :                         if ((argv->type!=GF_PROP_CONST_DATA) && pd->ptr) gf_free(pd->ptr);
     778             :                         //we don't free/alloc  since we don't free the string at the caller site
     779          76 :                         pd->size = argv->value.data.size;
     780          76 :                         pd->ptr = argv->value.data.ptr;
     781             :                         res = GF_TRUE;
     782             :                 }
     783             :                 break;
     784        4411 :         case GF_PROP_POINTER:
     785        4411 :                 if (a->offset_in_private + sizeof(void *) <= filter->freg->private_size) {
     786        4411 :                         *(void **)ptr = argv->value.ptr;
     787             :                         res = GF_TRUE;
     788             :                 }
     789             :                 break;
     790        1299 :         case GF_PROP_STRING_LIST:
     791        1299 :                 if (a->offset_in_private + sizeof(void *) <= filter->freg->private_size) {
     792             :                         u32 k;
     793             :                         GF_PropStringList *l = (GF_PropStringList *)ptr;
     794         112 :                         for (k=0; k<l->nb_items; k++) {
     795         112 :                                 gf_free(l->vals[k]);
     796             :                         }
     797        1299 :                         if (l->vals) gf_free(l->vals);
     798             :                         //we don't clone since we don't free the string at the caller site
     799        1299 :                         *l = argv->value.string_list;
     800             :                         res = GF_TRUE;
     801             :                 }
     802             :                 break;
     803         718 :         case GF_PROP_UINT_LIST:
     804             :         case GF_PROP_4CC_LIST:
     805             :         case GF_PROP_SINT_LIST:
     806             :         case GF_PROP_VEC2I_LIST:
     807             :                 //use uint_list as base type for lists
     808         718 :                 if (a->offset_in_private + sizeof(void *) <= filter->freg->private_size) {
     809             :                         GF_PropUIntList *l = (GF_PropUIntList *)ptr;
     810         718 :                         if (l->vals) gf_free(l->vals);
     811         718 :                         *l = argv->value.uint_list;
     812             :                         res = GF_TRUE;
     813             :                 }
     814             :                 break;
     815        7351 :         default:
     816        7351 :                 if (gf_props_type_is_enum(argv->type)) {
     817        7351 :                         if (a->offset_in_private + sizeof(u32) <= filter->freg->private_size) {
     818        7351 :                                 *(u32 *)ptr = argv->value.uint;
     819             :                                 res = GF_TRUE;
     820             :                         }
     821             :                         break;
     822             :                 }
     823           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Property type %s not supported for filter argument\n", gf_props_get_type_name(argv->type) ));
     824             :                 return;
     825             :         }
     826             :         if (!res) {
     827           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to set argument %s: memory offset %d overwrite structure size %f\n", a->arg_name, a->offset_in_private, filter->freg->private_size));
     828             :         }
     829             : }
     830             : 
     831             : 
     832           6 : static void filter_translate_autoinc(GF_Filter *filter, char *value)
     833             : {
     834             :         u32 ainc_crc, i;
     835             :         GF_FSAutoIncNum *auto_int=NULL;
     836             :         u32 inc_count;
     837             :         Bool assigned=GF_FALSE;
     838             :         s32 max_int = 0;
     839           6 :         s32 increment=1;
     840             :         char szInt[100];
     841             : 
     842             :         while (1) {
     843             :                 char *step_sep;
     844             :                 char *inc_end, *inc_sep;
     845          12 :                 inc_sep = strstr(value, "$GINC(");
     846          12 :                 if (!inc_sep) return;
     847           6 :                 inc_end = strstr(inc_sep, ")");
     848           6 :                 if (!inc_end) return;
     849             : 
     850           6 :                 inc_sep[0] = 0;
     851           6 :                 inc_end[0] = 0;
     852           6 :                 inc_end+=1;
     853           6 :                 strcpy(szInt, inc_sep+6);
     854           6 :                 ainc_crc = (u32) gf_crc_32(szInt, (u32) strlen(szInt) );
     855           6 :                 step_sep = strchr(szInt, ',');
     856           6 :                 if (step_sep) {
     857           6 :                         step_sep[0] = 0;
     858           6 :                         sscanf(step_sep+1, "%d", &increment);
     859             :                 }
     860             : 
     861           6 :                 inc_count = gf_list_count(filter->session->auto_inc_nums);
     862          21 :                 for (i=0; i<inc_count; i++) {
     863          15 :                         auto_int = gf_list_get(filter->session->auto_inc_nums, i);
     864          15 :                         if (auto_int->crc != ainc_crc) {
     865             :                                 auto_int=NULL;
     866           0 :                                 continue;
     867             :                         }
     868          15 :                         if (auto_int->filter == filter) {
     869           0 :                                 sprintf(szInt, "%d", auto_int->inc_val);
     870             :                                 break;
     871             :                         }
     872          15 :                         if (!assigned)
     873           5 :                                 max_int = auto_int->inc_val;
     874          10 :                         else if ((increment>0) && (max_int < auto_int->inc_val))
     875             :                                 max_int = auto_int->inc_val;
     876           0 :                         else if ((increment<0) && (max_int > auto_int->inc_val))
     877             :                                 max_int = auto_int->inc_val;
     878             : 
     879             :                         assigned = GF_TRUE;
     880             :                         auto_int = NULL;
     881             :                 }
     882           6 :                 if (!auto_int) {
     883           6 :                         GF_SAFEALLOC(auto_int, GF_FSAutoIncNum);
     884           6 :                         if (auto_int) {
     885           6 :                                 auto_int->filter = filter;
     886           6 :                                 auto_int->crc = ainc_crc;
     887           6 :                                 if (assigned) auto_int->inc_val = max_int + increment;
     888           1 :                                 else sscanf(szInt, "%d", &auto_int->inc_val);
     889           6 :                                 gf_list_add(filter->session->auto_inc_nums, auto_int);
     890             :                         }
     891             :                 }
     892           6 :                 if (auto_int) {
     893           6 :                         sprintf(szInt, "%d", auto_int->inc_val);
     894             :                         strcat(value, szInt);
     895             :                         strcat(value, inc_end);
     896             :                 }
     897             :         }
     898             : }
     899             : 
     900      241894 : static GF_PropertyValue gf_filter_parse_prop_solve_env_var(GF_Filter *filter, u32 type, const char *name, const char *value, const char *enum_values)
     901             : {
     902             :         char szPath[GF_MAX_PATH];
     903             :         GF_PropertyValue argv;
     904             : 
     905      241894 :         if (!value) return gf_props_parse_value(type, name, NULL, enum_values, filter->session->sep_list);
     906             : 
     907             : 
     908      238825 :         if (!strnicmp(value, "$GSHARE", 7)) {
     909           2 :                 if (gf_opts_default_shared_directory(szPath)) {
     910           2 :                         strcat(szPath, value+7);
     911             :                         value = szPath;
     912             :                 } else {
     913           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to query GPAC shared resource directory location\n"));
     914             :                 }
     915             :         }
     916      238823 :         else if (!strnicmp(value, "$GJS", 4)) {
     917             :                 Bool gf_fs_solve_js_script(char *szPath, const char *file_name, const char *file_ext);
     918             : 
     919           0 :                 Bool found = gf_fs_solve_js_script(szPath, value+4, NULL);
     920             : 
     921           0 :                 if (!found) {
     922           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed solve to %s in GPAC script directories, file not found\n", value));
     923             :                 }
     924             :         }
     925      238823 :         else if (!strnicmp(value, "$GLANG", 6)) {
     926          20 :                 value = gf_opts_get_key("core", "lang");
     927          20 :                 if (!value) value = "en";
     928             :         }
     929      238803 :         else if (!strnicmp(value, "$GUA", 4)) {
     930          71 :                 value = gf_opts_get_key("core", "user-agent");
     931          71 :                 if (!value) value = "GPAC " GPAC_VERSION;
     932      238732 :         } else if (strstr(value, "$GINC(")) {
     933           6 :                 char *a_value = gf_strdup(value);
     934           6 :                 filter_translate_autoinc(filter, a_value);
     935           6 :                 argv = gf_props_parse_value(type, name, a_value, enum_values, filter->session->sep_list);
     936           6 :                 gf_free(a_value);
     937           6 :                 return argv;
     938             :         }
     939      238819 :         argv = gf_props_parse_value(type, name, value, enum_values, filter->session->sep_list);
     940      238819 :         return argv;
     941             : }
     942             : 
     943          28 : Bool gf_filter_update_arg_apply(GF_Filter *filter, const char *arg_name, const char *arg_value, Bool is_sync_call)
     944             : {
     945             :         u32 i=0;
     946             :         //find arg
     947         295 :         while (filter->freg->args) {
     948             :                 GF_PropertyValue argv;
     949         267 :                 const GF_FilterArgs *a = &filter->freg->args[i];
     950         267 :                 i++;
     951             :                 Bool is_meta = GF_FALSE;
     952         267 :                 if (!a || !a->arg_name) break;
     953             : 
     954         257 :                 if ((a->flags & GF_FS_ARG_META) && !strcmp(a->arg_name, "*")) {
     955           0 :                         if (!filter->freg->update_arg)
     956         239 :                                 continue;
     957             :                         is_meta = GF_TRUE;
     958         257 :                 } else if (strcmp(a->arg_name, arg_name)) {
     959         239 :                         continue;
     960             :                 }
     961             :                 //we found the argument
     962             : 
     963          18 :                 if (!is_meta && ! (a->flags & (GF_FS_ARG_UPDATE|GF_FS_ARG_UPDATE_SYNC) ) ) {
     964           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Argument %s of filter %s is not updatable - ignoring\n", a->arg_name, filter->name));
     965          18 :                         return GF_TRUE;
     966             :                 }
     967             : 
     968          18 :                 if (a->flags & GF_FS_ARG_UPDATE_SYNC) {
     969           0 :                         if (!is_sync_call) return GF_TRUE;
     970             :                 }
     971             : 
     972          18 :                 argv = gf_filter_parse_prop_solve_env_var(filter, a->arg_type, a->arg_name, arg_value, a->min_max_enum);
     973             : 
     974          18 :                 if (argv.type != GF_PROP_FORBIDEN) {
     975             :                         GF_Err e = GF_OK;
     976             :                         if (!is_sync_call) {
     977             :                                 FSESS_CHECK_THREAD(filter)
     978             :                         }
     979             :                         //if no update function consider the arg OK
     980          18 :                         if (filter->freg->update_arg) {
     981           9 :                                 e = filter->freg->update_arg(filter, arg_name, &argv);
     982             :                         }
     983           9 :                         if (e==GF_OK) {
     984          18 :                                 if (!is_meta)
     985          18 :                                         gf_filter_set_arg(filter, a, &argv);
     986           0 :                         } else if (e!=GF_NOT_FOUND) {
     987           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Filter %s did not accept update of arg %s to value %s: %s\n", filter->name, arg_name, arg_value, gf_error_to_string(e) ));
     988             :                         }
     989             :                 } else {
     990           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to parse argument %s value %s\n", a->arg_name, a->arg_default_val));
     991             :                 }
     992             :                 return GF_TRUE;
     993             :         }
     994             :         return GF_FALSE;
     995             : }
     996          28 : void gf_filter_update_arg_task(GF_FSTask *task)
     997             : {
     998          28 :         GF_FilterUpdate *arg=task->udta;
     999             : 
    1000          28 :         Bool found = gf_filter_update_arg_apply(task->filter, arg->name, arg->val, GF_FALSE);
    1001             : 
    1002          28 :         if (!found) {
    1003          10 :                 if (arg->recursive) {
    1004             :                         u32 i;
    1005           9 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Failed to locate argument %s in filter %s, propagating %s the filter chain\n", arg->name, task->filter->freg->name,
    1006             :                                 (arg->recursive & (GF_FILTER_UPDATE_UPSTREAM|GF_FILTER_UPDATE_DOWNSTREAM)) ? "up and down" : ((arg->recursive & GF_FILTER_UPDATE_UPSTREAM) ? "up" : "down") ));
    1007             : 
    1008           9 :                         GF_List *flist = gf_list_new();
    1009           9 :                         if (arg->recursive & GF_FILTER_UPDATE_UPSTREAM) {
    1010           0 :                                 for (i=0; i<task->filter->num_output_pids; i++) {
    1011           0 :                                         GF_FilterPid *pid = gf_list_get(task->filter->output_pids, i);
    1012           0 :                                         if (gf_list_find(flist, pid->filter)<0) gf_list_add(flist, pid->filter);
    1013             :                                 }
    1014           0 :                                 for (i=0; i<gf_list_count(flist); i++) {
    1015           0 :                                         GF_Filter *a_f = gf_list_get(flist, i);
    1016             :                                         //only allow upstream propagation
    1017           0 :                                         gf_fs_send_update(task->filter->session, NULL, a_f, arg->name, arg->val, GF_FILTER_UPDATE_UPSTREAM);
    1018             :                                 }
    1019           0 :                                 gf_list_reset(flist);
    1020             : 
    1021             :                         }
    1022           9 :                         if (arg->recursive & GF_FILTER_UPDATE_DOWNSTREAM) {
    1023           9 :                                 gf_mx_p(task->filter->tasks_mx);
    1024          19 :                                 for (i=0; i<task->filter->num_input_pids; i++) {
    1025          10 :                                         GF_FilterPidInst *pidi = gf_list_get(task->filter->input_pids, i);
    1026          10 :                                         if (gf_list_find(flist, pidi->pid->filter)<0) gf_list_add(flist, pidi->pid->filter);
    1027             :                                 }
    1028             : 
    1029          10 :                                 for (i=0; i<gf_list_count(flist); i++) {
    1030          10 :                                         GF_Filter *a_f = gf_list_get(flist, i);
    1031             :                                         //only allow downstream propagation
    1032          10 :                                         gf_fs_send_update(task->filter->session, NULL, a_f, arg->name, arg->val, GF_FILTER_UPDATE_DOWNSTREAM);
    1033             :                                 }
    1034             : 
    1035           9 :                                 gf_mx_v(task->filter->tasks_mx);
    1036             :                         }
    1037             : 
    1038           9 :                         gf_list_del(flist);
    1039             :                 } else {
    1040           1 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Failed to locate argument %s in filter %s\n", arg->name, task->filter->freg->name));
    1041             :                 }
    1042             :         }
    1043          28 :         gf_free(arg->name);
    1044          28 :         gf_free(arg->val);
    1045          28 :         gf_free(arg);
    1046          28 : }
    1047             : 
    1048      260142 : static const char *gf_filter_load_arg_config(GF_Filter *filter, const char *sec_name, const char *arg_name, const char *arg_val)
    1049             : {
    1050             :         char szArg[101];
    1051             :         Bool gf_sys_has_filter_global_args();
    1052             :         const char *opt;
    1053      260142 :         GF_FilterSession *session = filter->session;
    1054             : 
    1055             :         //look in global args
    1056      260142 :         if (gf_sys_has_filter_global_args()) {
    1057        8011 :                 u32 alen = (u32) strlen(arg_name);
    1058        8011 :                 u32 i, nb_args = gf_sys_get_argc();
    1059      114086 :                 for (i=0; i<nb_args; i++) {
    1060             :                         u32 flen = 0;
    1061             :                         const char *per_filter;
    1062      106174 :                         const char *o_arg, *arg = gf_sys_get_arg(i);
    1063      106174 :                         if (arg[0]!='-') continue;
    1064       76805 :                         if (arg[1]!='-') continue;
    1065             : 
    1066       10942 :                         arg += 2;
    1067             :                         o_arg = arg;
    1068       10942 :                         per_filter = strchr(arg, '@');
    1069       10942 :                         if (per_filter) {
    1070           0 :                                 flen = (u32) (per_filter - arg);
    1071           0 :                                 if (!flen || strncmp(filter->freg->name, arg, flen))
    1072           0 :                                         continue;
    1073           0 :                                 flen++;
    1074           0 :                                 arg += flen;
    1075             :                         }
    1076             : 
    1077       10942 :                         if (!strncmp(arg, arg_name, alen)) {
    1078             :                                 u32 len=0;
    1079         102 :                                 char *sep = strchr(arg, '=');
    1080         102 :                                 if (sep) {
    1081          87 :                                         len = (u32) (sep - (arg));
    1082             :                                 } else {
    1083          15 :                                         len = (u32) strlen(arg);
    1084             :                                 }
    1085         102 :                                 if (len != alen) continue;
    1086             :                                 strncpy(szArg, o_arg, 100);
    1087          99 :                                 szArg[ MIN(flen + alen, 100) ] = 0;
    1088          99 :                                 gf_fs_push_arg(session, szArg, GF_TRUE, 0);
    1089             : 
    1090          99 :                                 if (sep) return sep+1;
    1091             :                                 //no arg value means boolean true
    1092             :                                 else return "true";
    1093             :                         }
    1094             :                 }
    1095             :         }
    1096             : 
    1097             :         //look in config file
    1098      260043 :         opt = gf_opts_get_key(sec_name, arg_name);
    1099      260043 :         if (opt)
    1100             :                 return opt;
    1101             : 
    1102             :         //ifce (used by socket and keep MP4Client behavior: some options are set in MP4Client main apply them
    1103      258833 :         if (!strcmp(arg_name, "ifce")) {
    1104         137 :                 opt = gf_opts_get_key("core", "ifce");
    1105         137 :                 if (opt)
    1106             :                         return opt;
    1107         137 :                 return NULL;
    1108             :         }
    1109             : 
    1110             :         return arg_val;
    1111             : }
    1112             : 
    1113         228 : static void gf_filter_load_meta_args_config(const char *sec_name, GF_Filter *filter)
    1114             : {
    1115             :         GF_PropertyValue argv;
    1116             :         Bool gf_sys_has_filter_global_meta_args();
    1117         228 :         u32 i, key_count = gf_opts_get_key_count(sec_name);
    1118             : 
    1119             :         FSESS_CHECK_THREAD(filter)
    1120             : 
    1121         228 :         for (i=0; i<key_count; i++) {
    1122             :                 Bool arg_found = GF_FALSE;
    1123             :                 u32 k=0;
    1124           0 :                 const char *arg_val, *arg_name = gf_opts_get_key_name(sec_name, i);
    1125             :                 //check if this is a regular arg, if so don't process it
    1126           0 :                 while (filter->freg->args) {
    1127           0 :                         const GF_FilterArgs *a = &filter->freg->args[k];
    1128           0 :                         if (!a || !a->arg_name) break;
    1129           0 :                         k++;
    1130           0 :                         if (!strcmp(a->arg_name, arg_name)) {
    1131             :                                 arg_found = GF_TRUE;
    1132             :                                 break;
    1133             :                         }
    1134             :                 }
    1135           0 :                 if (arg_found) continue;
    1136             : 
    1137           0 :                 arg_val = gf_opts_get_key(sec_name, arg_name);
    1138           0 :                 if (!arg_val) continue;
    1139             : 
    1140             :                 memset(&argv, 0, sizeof(GF_PropertyValue));
    1141           0 :                 argv.type = GF_PROP_STRING;
    1142           0 :                 argv.value.string = (char *) arg_val;
    1143           0 :                 filter->freg->update_arg(filter, arg_name, &argv);
    1144             :         }
    1145         456 :         if (!gf_sys_has_filter_global_meta_args()) return;
    1146             : 
    1147           0 :         key_count = gf_sys_get_argc();
    1148           0 :         for (i=0; i<key_count; i++) {
    1149             : #define META_MAX_ARG    1000
    1150             :                 char szArg[META_MAX_ARG+1];
    1151             :                 GF_Err e;
    1152             :                 u32 len = 0;
    1153             :                 const char *per_filter;
    1154           0 :                 const char *sep, *o_arg, *arg = gf_sys_get_arg(i);
    1155           0 :                 if (arg[0] != '-') continue;
    1156           0 :                 if (arg[1] != '+') continue;
    1157           0 :                 arg+=2;
    1158             : 
    1159             :                 o_arg = arg;
    1160           0 :                 per_filter = strchr(arg, '@');
    1161           0 :                 if (per_filter) {
    1162           0 :                         len = (u32) (per_filter - arg);
    1163           0 :                         if (!len || strncmp(filter->freg->name, arg, len))
    1164           0 :                                 continue;
    1165           0 :                         len++;
    1166           0 :                         arg += len;
    1167             :                 }
    1168             : 
    1169           0 :                 sep = strchr(arg, '=');
    1170             :                 memset(&argv, 0, sizeof(GF_PropertyValue));
    1171           0 :                 argv.type = GF_PROP_STRING;
    1172           0 :                 if (sep) {
    1173           0 :                         u32 cplen = (u32) (sep - o_arg);
    1174           0 :                         if (cplen>=META_MAX_ARG) cplen=META_MAX_ARG;
    1175           0 :                         strncpy(szArg, o_arg, cplen);
    1176           0 :                         szArg[cplen] = 0;
    1177           0 :                         argv.value.string = (char *) sep+1;
    1178             :                 } else {
    1179           0 :                         u32 cplen = (u32) strlen(o_arg);
    1180           0 :                         if (cplen>=META_MAX_ARG) cplen=META_MAX_ARG;
    1181           0 :                         memcpy(szArg, o_arg, cplen);
    1182           0 :                         szArg[cplen] = 0;
    1183             :                 }
    1184             : #undef META_MAX_ARG
    1185             : 
    1186           0 :                 e = filter->freg->update_arg(filter, szArg + len, &argv);
    1187           0 :                 gf_fs_push_arg(filter->session, szArg, (e==GF_OK) ? GF_TRUE : GF_FALSE, 2);
    1188             :         }
    1189             : }
    1190             : 
    1191       13781 : static void filter_parse_dyn_args(GF_Filter *filter, const char *args, GF_FilterArgType arg_type, Bool for_script, char *szSrc, char *szDst, char *szEscape, char *szSecName, Bool has_meta_args, u32 argfile_level)
    1192             : {
    1193             :         char *szArg=NULL;
    1194             :         u32 i=0;
    1195             :         u32 alloc_len=1024;
    1196             :         const GF_FilterArgs *f_args = NULL;
    1197             :         Bool opts_optional = GF_FALSE;
    1198             : 
    1199       13781 :         if (args)
    1200       12334 :                 szArg = gf_malloc(sizeof(char)*1024);
    1201             : 
    1202             :         //parse each arg
    1203       58259 :         while (args) {
    1204             :                 char *value;
    1205             :                 u32 len;
    1206             :                 Bool found=GF_FALSE;
    1207             :                 char *escaped = NULL;
    1208             :                 Bool opaque_arg = GF_FALSE;
    1209             :                 Bool absolute_url = GF_FALSE;
    1210             :                 Bool internal_url = GF_FALSE;
    1211             :                 Bool internal_arg = GF_FALSE;
    1212             :                 char *sep = NULL;
    1213             : 
    1214             :                 //look for our arg separator - if arg[0] is also a separator, consider the entire string until next double sep as the parameter
    1215       44478 :                 if (args[0] != filter->session->sep_args)
    1216       44448 :                         sep = strchr(args, filter->session->sep_args);
    1217             :                 else {
    1218          60 :                         while (args[0] == filter->session->sep_args) {
    1219          30 :                                 args++;
    1220             :                         }
    1221          30 :                         if (!args[0])
    1222             :                                 break;
    1223             : 
    1224          30 :                         sep = (char *) args + 1;
    1225             :                         while (1) {
    1226          38 :                                 sep = strchr(sep, filter->session->sep_args);
    1227          34 :                                 if (!sep) break;
    1228          15 :                                 if (sep[1]==filter->session->sep_args) {
    1229             :                                         break;
    1230             :                                 }
    1231           4 :                                 sep = sep+1;
    1232             :                         }
    1233             :                         opaque_arg = GF_TRUE;
    1234             :                 }
    1235             : 
    1236       44478 :                 if (!opaque_arg) {
    1237             :                         //we don't use gf_fs_path_escape_colon here because we also analyse whether the URL is internal or not, and we don't want to do that on each arg
    1238             : 
    1239       44448 :                         if (filter->session->sep_args == ':') {
    1240       44448 :                                 if (sep && !strncmp(args, szSrc, 4) && !strncmp(args+4, "gcryp://", 8)) {
    1241           0 :                                         sep = strstr(args+12, "://");
    1242             :                                 }
    1243       44677 :                                 while (sep && !strncmp(sep, "://", 3)) {
    1244             :                                         absolute_url = GF_TRUE;
    1245             : 
    1246             :                                         //filter internal url schemes
    1247         452 :                                         if ((!strncmp(args, szSrc, 4) || !strncmp(args, szDst, 4) ) &&
    1248         223 :                                                 (!strncmp(args+4, "video://", 8)
    1249         221 :                                                 || !strncmp(args+4, "audio://", 8)
    1250         221 :                                                 || !strncmp(args+4, "av://", 5)
    1251         221 :                                                 || !strncmp(args+4, "gmem://", 7)
    1252         221 :                                                 || !strncmp(args+4, "gpac://", 7)
    1253         221 :                                                 || !strncmp(args+4, "pipe://", 7)
    1254         205 :                                                 || !strncmp(args+4, "tcp://", 6)
    1255         193 :                                                 || !strncmp(args+4, "udp://", 6)
    1256         186 :                                                 || !strncmp(args+4, "tcpu://", 7)
    1257         186 :                                                 || !strncmp(args+4, "udpu://", 7)
    1258         186 :                                                 || !strncmp(args+4, "rtp://", 6)
    1259         184 :                                                 || !strncmp(args+4, "atsc://", 7)
    1260         178 :                                                 || !strncmp(args+4, "gfio://", 7)
    1261         169 :                                                 || !strncmp(args+4, "route://", 8)
    1262             :                                                 )
    1263             :                                         ) {
    1264             :                                                 internal_url = GF_TRUE;
    1265          65 :                                                 sep = strchr(sep+3, ':');
    1266          65 :                                                 if (!strncmp(args+4, "tcp://", 6)
    1267          53 :                                                         || !strncmp(args+4, "udp://", 6)
    1268          46 :                                                         || !strncmp(args+4, "tcpu://", 7)
    1269          46 :                                                         || !strncmp(args+4, "udpu://", 7)
    1270          46 :                                                         || !strncmp(args+4, "rtp://", 6)
    1271          44 :                                                         || !strncmp(args+4, "route://", 8)
    1272             :                                                 ) {
    1273          32 :                                                         char *sep2 = sep ? strchr(sep+1, ':') : NULL;
    1274          32 :                                                         char *sep3 = sep ? strchr(sep+1, '/') : NULL;
    1275          32 :                                                         if (sep2 && sep3 && (sep2>sep3)) {
    1276           7 :                                                                 sep2 = strchr(sep3, ':');
    1277             :                                                         }
    1278          32 :                                                         if (sep2 || sep3 || sep) {
    1279          32 :                                                                 u32 port = 0;
    1280          32 :                                                                 if (sep2) {
    1281          26 :                                                                         sep2[0] = 0;
    1282          26 :                                                                         if (sep3) sep3[0] = 0;
    1283             :                                                                 }
    1284           6 :                                                                 else if (sep3) sep3[0] = 0;
    1285          32 :                                                                 if (sscanf(sep+1, "%d", &port)==1) {
    1286             :                                                                         char szPort[20];
    1287          32 :                                                                         snprintf(szPort, 20, "%d", port);
    1288          32 :                                                                         if (strcmp(sep+1, szPort))
    1289           0 :                                                                                 port = 0;
    1290             :                                                                 }
    1291          32 :                                                                 if (sep2) sep2[0] = ':';
    1292          32 :                                                                 if (sep3) sep3[0] = '/';
    1293             : 
    1294          32 :                                                                 if (port) sep = sep2;
    1295             :                                                         }
    1296             :                                                 }
    1297             : 
    1298             :                                         } else {
    1299             :                                                 //loog for '::' vs ':gfopt' and ':gpac:' - if '::' appears before these, jump to '::'
    1300         164 :                                                 char *sep2 = strstr(sep+3, ":gfopt:");
    1301         164 :                                                 char *sep3 = strstr(sep+3, szEscape);
    1302         164 :                                                 if (sep2 && sep3 && sep2<sep3)
    1303             :                                                         sep3 = sep2;
    1304         164 :                                                 else if (!sep3)
    1305             :                                                         sep3 = sep2;
    1306             : 
    1307         164 :                                                 sep2 = strstr(sep+3, "::");
    1308         164 :                                                 if (sep2 && sep3 && sep3<sep2)
    1309             :                                                         sep2 = sep3;
    1310         164 :                                                 else if (sep2)
    1311             :                                                         opaque_arg = GF_TRUE; //skip an extra ':' at the end of the arg parsing
    1312             : 
    1313             :                                                 //escape sequence present after this argument, use it
    1314         164 :                                                 if (sep2) {
    1315             :                                                         sep = sep2;
    1316             :                                                 } else {
    1317             :                                                         //get root /
    1318         164 :                                                         sep = strchr(sep+3, '/');
    1319             :                                                         //get first : after root
    1320         164 :                                                         if (sep) sep = strchr(sep+1, ':');
    1321             :                                                 }
    1322             :                                         }
    1323             :                                 }
    1324             : 
    1325             :                                 //watchout for "C:\\" or "C:/"
    1326       44448 :                                 while (sep && (sep[1]=='\\' || sep[1]=='/')) {
    1327           0 :                                         sep = strchr(sep+1, ':');
    1328             :                                 }
    1329             :                                 //escape data/time
    1330       44448 :                                 if (sep) {
    1331       32148 :                                         char *prev_date = sep-3;
    1332       32148 :                                         if (prev_date[0]=='T') {}
    1333       32060 :                                         else if (prev_date[1]=='T') { prev_date ++; }
    1334             :                                         else { prev_date = NULL; }
    1335             : 
    1336             : skip_date:
    1337       32210 :                                         if (prev_date) {
    1338             :                                                 char *search_after = NULL;
    1339             :                                                 //assume date Th:m:s or Th:mm:s or Thh:m:s or Thh:mm:s
    1340         152 :                                                 if ((prev_date[2] == ':') && (prev_date[4] == ':')) search_after = prev_date+5;
    1341         152 :                                                 else if ((prev_date[2] == ':') && (prev_date[5] == ':')) search_after = prev_date+6;
    1342         152 :                                                 else if ((prev_date[3] == ':') && (prev_date[5] == ':')) search_after = prev_date+6;
    1343         152 :                                                 else if ((prev_date[3] == ':') && (prev_date[6] == ':')) search_after = prev_date+7;
    1344             : 
    1345         150 :                                                 if (search_after) {
    1346             :                                                         //take care of lists
    1347         150 :                                                         char *next_date = strchr(search_after, 'T');
    1348         150 :                                                         char *next_sep = strchr(search_after, ':');
    1349         150 :                                                         if (next_date && next_sep && (next_date<next_sep)) {
    1350             :                                                                 prev_date = next_date - 1;
    1351          62 :                                                                 if (prev_date[0] == filter->session->sep_list) {
    1352             :                                                                         prev_date = next_date;
    1353             :                                                                         goto skip_date;
    1354             :                                                                 }
    1355             :                                                         }
    1356             :                                                         sep = strchr(search_after, ':');
    1357             :                                                 }
    1358             :                                         }
    1359             :                                 }
    1360             :                         }
    1361       44448 :                         if (sep) {
    1362       32133 :                                 escaped = strstr(sep, szEscape);
    1363       32133 :                                 if (escaped) sep = escaped;
    1364             :                         }
    1365             : 
    1366       44448 :                         if (sep && !strncmp(args, szSrc, 4) && !escaped && absolute_url && !internal_url) {
    1367             :                                 Bool file_exists;
    1368           0 :                                 sep[0]=0;
    1369           0 :                                 if (!strcmp(args+4, "null")) file_exists = GF_TRUE;
    1370           0 :                                 else if (!strncmp(args+4, "tcp://", 6)) file_exists = GF_TRUE;
    1371           0 :                                 else if (!strncmp(args+4, "udp://", 6)) file_exists = GF_TRUE;
    1372           0 :                                 else if (!strncmp(args+4, "route://", 8)) file_exists = GF_TRUE;
    1373           0 :                                 else file_exists = gf_file_exists(args+4);
    1374             : 
    1375           0 :                                 if (!file_exists) {
    1376           0 :                                         char *fsep = strchr(args+4, filter->session->sep_frag);
    1377           0 :                                         if (fsep) {
    1378           0 :                                                 fsep[0] = 0;
    1379           0 :                                                 file_exists = gf_file_exists(args+4);
    1380           0 :                                                 fsep[0] = filter->session->sep_frag;
    1381             :                                         }
    1382             :                                 }
    1383           0 :                                 sep[0]= filter->session->sep_args;
    1384           0 :                                 if (!file_exists) {
    1385           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Non-escaped argument pattern \"%s\" in src %s, assuming arguments are part of source URL. Use src=PATH:gpac:ARGS to differentiate, or change separators\n", sep, args));
    1386             :                                         sep = NULL;
    1387             :                                 }
    1388             :                         }
    1389             :                 }
    1390             : 
    1391             : 
    1392             :                 //escape some XML inputs
    1393       44478 :                 if (sep) {
    1394       32144 :                         char *xml_start = strchr(args, '<');
    1395       32144 :                         len = (u32) (sep-args);
    1396       32144 :                         if (xml_start) {
    1397         111 :                                 u32 xlen = (u32) (xml_start-args);
    1398         111 :                                 if ((xlen < len) && (args[len-1] != '>')) {
    1399             :                                         while (1) {
    1400           0 :                                                 sep = strchr(sep+1, filter->session->sep_args);
    1401           0 :                                                 if (!sep) {
    1402           0 :                                                         len = (u32) strlen(args);
    1403           0 :                                                         break;
    1404             :                                                 }
    1405           0 :                                                 len = (u32) (sep-args);
    1406           0 :                                                 if (args[len-1] != '>') break;
    1407             :                                         }
    1408             :                                 }
    1409             : 
    1410             :                         }
    1411             :                 }
    1412       12334 :                 else len = (u32) strlen(args);
    1413             : 
    1414       44478 :                 if (len>=alloc_len) {
    1415           0 :                         alloc_len = len+1;
    1416           0 :                         szArg = gf_realloc(szArg, sizeof(char)*len);
    1417             :                 }
    1418       44478 :                 strncpy(szArg, args, len);
    1419       44478 :                 szArg[len]=0;
    1420             : 
    1421       44478 :                 value = strchr(szArg, filter->session->sep_name);
    1422       44478 :                 if (value) {
    1423       35033 :                         value[0] = 0;
    1424       35033 :                         value++;
    1425             :                 }
    1426             : 
    1427             :                 //arg is a PID property assignment
    1428       44478 :                 if (szArg[0] == filter->session->sep_frag) {
    1429        3400 :                         filter->user_pid_props = GF_TRUE;
    1430        3400 :                         goto skip_arg;
    1431             :                 }
    1432             : 
    1433       61646 :                 if ((arg_type == GF_FILTER_ARG_INHERIT) && (!strcmp(szArg, "src") || !strcmp(szArg, "dst")) )
    1434             :                         goto skip_arg;
    1435             : 
    1436             :                 i=0;
    1437       41078 :                 f_args = filter->freg->args;
    1438       41078 :                 if (for_script)
    1439          68 :                         f_args = filter->instance_args;
    1440             : 
    1441      716891 :                 while (filter->filter_udta && f_args) {
    1442             :                         Bool is_my_arg = GF_FALSE;
    1443             :                         Bool reverse_bool = GF_FALSE;
    1444             :                         const char *restricted=NULL;
    1445      716753 :                         const GF_FilterArgs *a = &f_args[i];
    1446      716753 :                         i++;
    1447      716753 :                         if (!a || !a->arg_name) break;
    1448             : 
    1449      693872 :                         if (!strcmp(a->arg_name, szArg))
    1450             :                                 is_my_arg = GF_TRUE;
    1451      676449 :                         else if ( (szArg[0]==filter->session->sep_neg) && !strcmp(a->arg_name, szArg+1)) {
    1452             :                                 is_my_arg = GF_TRUE;
    1453             :                                 reverse_bool = GF_TRUE;
    1454             :                         }
    1455             :                         //little optim here, if no value check for enums
    1456      676183 :                         else if (!value && a->min_max_enum && strchr(a->min_max_enum, '|') ) {
    1457       23344 :                                 char *arg_found = strstr(a->min_max_enum, szArg);
    1458       23344 :                                 if (arg_found) {
    1459         388 :                                         char c = arg_found[strlen(szArg)];
    1460         388 :                                         if (!c || (c=='|')) {
    1461             :                                                 is_my_arg = GF_TRUE;
    1462             :                                                 value = szArg;
    1463             :                                         }
    1464             :                                 }
    1465             :                         }
    1466             : 
    1467      693872 :                         if (is_my_arg) restricted = gf_opts_get_key_restricted(szSecName, a->arg_name);
    1468       18059 :                         if (restricted) {
    1469           0 :                                 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Argument %s of filter %s is restricted to %s by system-wide configuration, ignoring\n", szArg, filter->freg->name, restricted));
    1470             :                                 found=GF_TRUE;
    1471             :                                 break;
    1472             :                         }
    1473             : 
    1474      693872 :                         if (is_my_arg) {
    1475             :                                 GF_PropertyValue argv;
    1476             :                                 found=GF_TRUE;
    1477             : 
    1478       18059 :                                 argv = gf_filter_parse_prop_solve_env_var(filter, (a->flags & GF_FS_ARG_META) ? GF_PROP_STRING : a->arg_type, a->arg_name, value, a->min_max_enum);
    1479             : 
    1480       18059 :                                 if (reverse_bool && (argv.type==GF_PROP_BOOL))
    1481         266 :                                         argv.value.boolean = !argv.value.boolean;
    1482             : 
    1483       18059 :                                 if (argv.type != GF_PROP_FORBIDEN) {
    1484       18059 :                                         if (!for_script && (a->offset_in_private>=0)) {
    1485       18032 :                                                 gf_filter_set_arg(filter, a, &argv);
    1486          27 :                                         } else if (filter->freg->update_arg) {
    1487             :                                                 FSESS_CHECK_THREAD(filter)
    1488          27 :                                                 filter->freg->update_arg(filter, a->arg_name, &argv);
    1489             :                                                 opaque_arg = GF_FALSE;
    1490             : 
    1491          27 :                                                 if ((argv.type==GF_PROP_STRING) && argv.value.string)
    1492           7 :                                                         gf_free(argv.value.string);
    1493             :                                         }
    1494             :                                 }
    1495             :                                 break;
    1496             :                         }
    1497             :                 }
    1498       41078 :                 if (!strlen(szArg)) {
    1499             :                         found = GF_TRUE;
    1500       41069 :                 } else if (!found) {
    1501             :                         //filter ID
    1502       23010 :                         if (!strcmp("FID", szArg)) {
    1503         393 :                                 if (arg_type != GF_FILTER_ARG_INHERIT)
    1504         391 :                                         gf_filter_set_id(filter, value);
    1505             :                                 found = GF_TRUE;
    1506             :                                 internal_arg = GF_TRUE;
    1507             :                         }
    1508             :                         //filter sources
    1509       22617 :                         else if (!strcmp("SID", szArg)) {
    1510         443 :                                 if (arg_type!=GF_FILTER_ARG_INHERIT)
    1511         407 :                                         gf_filter_set_sources(filter, value);
    1512             :                                 found = GF_TRUE;
    1513             :                                 internal_arg = GF_TRUE;
    1514             :                         }
    1515             :                         //clonable filter
    1516       22174 :                         else if (!strcmp("clone", szArg)) {
    1517          69 :                                 if ((arg_type==GF_FILTER_ARG_EXPLICIT_SINK) || (arg_type==GF_FILTER_ARG_EXPLICIT))
    1518          43 :                                         filter->clonable=GF_TRUE;
    1519             :                                 found = GF_TRUE;
    1520             :                                 internal_arg = GF_TRUE;
    1521             :                         }
    1522             :                         //filter name
    1523       22105 :                         else if (!strcmp("N", szArg)) {
    1524           0 :                                 if ((arg_type==GF_FILTER_ARG_EXPLICIT_SINK) || (arg_type==GF_FILTER_ARG_EXPLICIT) || (arg_type==GF_FILTER_ARG_EXPLICIT_SOURCE)) {
    1525           0 :                                         gf_filter_set_name(filter, value);
    1526             :                                 }
    1527             :                                 found = GF_TRUE;
    1528             :                                 internal_arg = GF_TRUE;
    1529             :                         }
    1530             :                         //internal options, nothing to do here
    1531       22105 :                         else if (
    1532             :                                 //generic encoder load
    1533       22105 :                                 !strcmp("c", szArg)
    1534             :                                 //preferred registry to use
    1535       22050 :                                 || !strcmp("gfreg", szArg)
    1536             :                                 //non inherited options
    1537       22042 :                                 || !strcmp("gfloc", szArg)
    1538             :                         ) {
    1539             :                                 found = GF_TRUE;
    1540             :                                 internal_arg = GF_TRUE;
    1541             :                         }
    1542             :                         //non tracked options
    1543       22041 :                         else if (!strcmp("gfopt", szArg)) {
    1544             :                                 found = GF_TRUE;
    1545             :                                 internal_arg = GF_TRUE;
    1546             :                                 opts_optional = GF_TRUE;
    1547             :                         }
    1548             :                         //filter tag
    1549       21346 :                         else if (!strcmp("TAG", szArg)) {
    1550           0 :                                 if (! filter->dynamic_filter) {
    1551           0 :                                         if (filter->tag) gf_free(filter->tag);
    1552           0 :                                         filter->tag = value ? gf_strdup(value) : NULL;
    1553             :                                 }
    1554             :                                 found = GF_TRUE;
    1555             :                                 internal_arg = GF_TRUE;
    1556             :                         }
    1557       21346 :                         else if (gf_file_exists(szArg)) {
    1558           4 :                                 if (!for_script && (argfile_level<5) ) {
    1559             :                                         char szLine[2001];
    1560           4 :                                         FILE *arg_file = gf_fopen(szArg, "rt");
    1561           4 :                                         szLine[2000]=0;
    1562          12 :                                         while (!gf_feof(arg_file)) {
    1563             :                                                 u32 llen;
    1564             :                                                 char *subarg, *res_line;
    1565           8 :                                                 szLine[0] = 0;
    1566           8 :                                                 res_line = gf_fgets(szLine, 2000, arg_file);
    1567           8 :                                                 if (!res_line) break;
    1568           4 :                                                 llen = (u32) strlen(szLine);
    1569          12 :                                                 while (llen && strchr(" \n\r\t", szLine[llen-1])) {
    1570           4 :                                                         szLine[llen-1]=0;
    1571             :                                                         llen--;
    1572             :                                                 }
    1573           4 :                                                 if (!llen)
    1574           0 :                                                         continue;
    1575             : 
    1576             :                                                 subarg = szLine;
    1577           4 :                                                 while (subarg[0] && strchr(" \n\r\t", subarg[0]))
    1578           0 :                                                         subarg++;
    1579           4 :                                                 if ((subarg[0] == '/') && (subarg[1] == '/'))
    1580           0 :                                                         continue;
    1581             : 
    1582           4 :                                                 filter_parse_dyn_args(filter, subarg, arg_type, for_script, szSrc, szDst, szEscape, szSecName, has_meta_args, argfile_level+1);
    1583             :                                         }
    1584           4 :                                         gf_fclose(arg_file);
    1585           0 :                                 } else if (!for_script) {
    1586           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Filter argument file has too many nested levels of sub-files, maximum allowed is 5\n"));
    1587             :                                 }
    1588             :                                 internal_arg = GF_TRUE;
    1589             :                         }
    1590       21342 :                         else if (has_meta_args && filter->freg->update_arg) {
    1591             :                                 GF_Err e = GF_OK;
    1592         115 :                                 if (for_script || !(filter->freg->flags&GF_FS_REG_SCRIPT) ) {
    1593          88 :                                         GF_PropertyValue argv = gf_props_parse_value(GF_PROP_STRING, szArg, value, NULL, filter->session->sep_list);
    1594             :                                         FSESS_CHECK_THREAD(filter)
    1595          88 :                                         e = filter->freg->update_arg(filter, szArg, &argv);
    1596          88 :                                         if (argv.value.string) gf_free(argv.value.string);
    1597             :                                 }
    1598         115 :                                 if (!(filter->freg->flags&GF_FS_REG_SCRIPT) && (e==GF_OK) )
    1599             :                                         found = GF_TRUE;
    1600             :                         }
    1601             :                 }
    1602             : 
    1603       41078 :                 if (!internal_arg && !opaque_arg && !opts_optional)
    1604       32475 :                         gf_fs_push_arg(filter->session, szArg, found, 0);
    1605             : 
    1606       53081 : skip_arg:
    1607       44478 :                 if (escaped) {
    1608        1408 :                         args=sep+6;
    1609       43070 :                 } else if (sep) {
    1610       30736 :                         args=sep+1;
    1611       30736 :                         if (opaque_arg)
    1612          11 :                                 args += 1;
    1613             :                 } else {
    1614             :                         args=NULL;
    1615             :                 }
    1616             :         }
    1617       13781 :         if (szArg) gf_free(szArg);
    1618       13781 : }
    1619             : 
    1620             : 
    1621       13777 : static void gf_filter_parse_args(GF_Filter *filter, const char *args, GF_FilterArgType arg_type, Bool for_script)
    1622             : {
    1623             :         u32 i=0;
    1624             :         char szSecName[200];
    1625             :         char szEscape[7];
    1626             :         char szSrc[5], szDst[5];
    1627             :         Bool has_meta_args = GF_FALSE;
    1628             :         const GF_FilterArgs *f_args = NULL;
    1629       13777 :         if (!filter) return;
    1630             : 
    1631       13777 :         if (!for_script) {
    1632       13736 :                 if (!filter->freg->private_size) {
    1633           4 :                         if (filter->freg->args && filter->freg->args[0].arg_name) {
    1634           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Filter with arguments but no private stack size, no arg passing\n"));
    1635             :                         }
    1636             :                 } else {
    1637       13732 :                         filter->filter_udta = gf_malloc(filter->freg->private_size);
    1638       13732 :                         if (!filter->filter_udta) {
    1639           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to allocate private data stack\n"));
    1640             :                                 return;
    1641             :                         }
    1642       13732 :                         memset(filter->filter_udta, 0, filter->freg->private_size);
    1643             :                 }
    1644             :         }
    1645             : 
    1646       13777 :         sprintf(szEscape, "%cgpac%c", filter->session->sep_args, filter->session->sep_args);
    1647       13777 :         sprintf(szSrc, "src%c", filter->session->sep_name);
    1648       13777 :         sprintf(szDst, "dst%c", filter->session->sep_name);
    1649             : 
    1650       13777 :         snprintf(szSecName, 200, "filter@%s", filter->freg->name);
    1651             : 
    1652             :         //instantiate all args with defauts value
    1653       13777 :         f_args = filter->freg->args;
    1654       13777 :         if (for_script)
    1655          41 :                 f_args = filter->instance_args;
    1656             :         i=0;
    1657      287924 :         while (f_args) {
    1658             :                 GF_PropertyValue argv;
    1659             :                 const char *def_val;
    1660      273805 :                 const GF_FilterArgs *a = &f_args[i];
    1661      273805 :                 if (!a || !a->arg_name) break;
    1662      260370 :                 i++;
    1663      260370 :                 if (a->flags & GF_FS_ARG_META) {
    1664             :                         has_meta_args = GF_TRUE;
    1665       36781 :                         continue;
    1666             :                 }
    1667             : 
    1668      260142 :                 def_val = gf_filter_load_arg_config(filter, szSecName, a->arg_name, a->arg_default_val);
    1669             : 
    1670      260142 :                 if (!def_val) continue;
    1671             : 
    1672      223817 :                 argv = gf_filter_parse_prop_solve_env_var(filter, a->arg_type, a->arg_name, def_val, a->min_max_enum);
    1673             : 
    1674      223817 :                 if (argv.type != GF_PROP_FORBIDEN) {
    1675      223817 :                         if (!for_script && (a->offset_in_private>=0)) {
    1676      223685 :                                 gf_filter_set_arg(filter, a, &argv);
    1677         132 :                         } else if (filter->freg->update_arg) {
    1678             :                                 FSESS_CHECK_THREAD(filter)
    1679         132 :                                 filter->freg->update_arg(filter, a->arg_name, &argv);
    1680         132 :                                 gf_props_reset_single(&argv);
    1681             :                         }
    1682             :                 } else {
    1683           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to parse argument %s value %s\n", a->arg_name, a->arg_default_val));
    1684             :                 }
    1685             :         }
    1686             :         //handle meta filter options, not exposed in registry
    1687       13777 :         if (has_meta_args && filter->freg->update_arg && !for_script)
    1688         228 :                 gf_filter_load_meta_args_config(szSecName, filter);
    1689             : 
    1690             : 
    1691       13777 :         filter_parse_dyn_args(filter, args, arg_type, for_script, szSrc, szDst, szEscape, szSecName, has_meta_args, 0);
    1692             : }
    1693             : 
    1694       13724 : static void reset_filter_args(GF_Filter *filter)
    1695             : {
    1696             :         u32 i=0;
    1697             :         //removed or no stack
    1698       13724 :         if (!filter->filter_udta) return;
    1699             : 
    1700             :         //instantiate all args with defauts value
    1701      273800 :         while (filter->freg->args) {
    1702             :                 GF_PropertyValue argv;
    1703      273466 :                 const GF_FilterArgs *a = &filter->freg->args[i];
    1704      273466 :                 i++;
    1705      273466 :                 if (!a || !a->arg_name) break;
    1706             : 
    1707      260079 :                 if (a->arg_type != GF_PROP_FORBIDEN) {
    1708             :                         memset(&argv, 0, sizeof(GF_PropertyValue));
    1709      260079 :                         argv.type = a->arg_type;
    1710      260079 :                         gf_filter_set_arg(filter, a, &argv);
    1711             :                 }
    1712             :         }
    1713             : }
    1714             : 
    1715         134 : void gf_filter_check_output_reconfig(GF_Filter *filter)
    1716             : {
    1717             :         u32 i, j;
    1718             :         //not needed
    1719         134 :         if (!filter->reconfigure_outputs) return;
    1720          35 :         filter->reconfigure_outputs = GF_FALSE;
    1721             :         //check destinations of all output pids
    1722          71 :         for (i=0; i<filter->num_output_pids; i++) {
    1723          36 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    1724          72 :                 for (j=0; j<pid->num_destinations; j++) {
    1725          36 :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    1726             :                         //PID was reconfigured, update props
    1727          36 :                         if (pidi->reconfig_pid_props) {
    1728             :                                 assert(pidi->props);
    1729          36 :                                 if (pidi->props != pidi->reconfig_pid_props) {
    1730             :                                         //unassign old property list and set the new one
    1731             :                                         assert(pidi->props->reference_count);
    1732          35 :                                         if (safe_int_dec(& pidi->props->reference_count) == 0) {
    1733             :                                                 //see \ref gf_filter_pid_merge_properties_internal for mutex
    1734          34 :                                                 gf_mx_p(pidi->pid->filter->tasks_mx);
    1735          34 :                                                 gf_list_del_item(pidi->pid->properties, pidi->props);
    1736          34 :                                                 gf_mx_v(pidi->pid->filter->tasks_mx);
    1737          34 :                                                 gf_props_del(pidi->props);
    1738             :                                         }
    1739          35 :                                         pidi->props = pidi->reconfig_pid_props;
    1740          35 :                                         safe_int_inc( & pidi->props->reference_count );
    1741             :                                 }
    1742          36 :                                 pidi->reconfig_pid_props = NULL;
    1743          36 :                                 gf_fs_post_task(filter->session, gf_filter_pid_reconfigure_task, pidi->filter, pid, "pidinst_reconfigure", NULL);
    1744             :                         }
    1745             :                 }
    1746             :         }
    1747             : }
    1748             : 
    1749           4 : static GF_FilterPidInst *filter_relink_get_upper_pid(GF_FilterPidInst *cur_pidinst, Bool *needs_flush)
    1750             : {
    1751             :         GF_FilterPidInst *pidinst = cur_pidinst;
    1752           4 :         *needs_flush = GF_FALSE;
    1753             :         //locate the true destination
    1754             :         while (1) {
    1755             :                 GF_FilterPid *opid;
    1756           7 :                 if (pidinst->filter->num_input_pids != 1) break;
    1757           7 :                 if (pidinst->filter->num_output_pids != 1) break;
    1758             :                 //filter was explicitly loaded, cannot go beyond
    1759           6 :                 if (! pidinst->filter->dynamic_filter) break;
    1760           3 :                 opid = gf_list_get(pidinst->filter->output_pids, 0);
    1761             :                 assert(opid);
    1762             :                 //we have a fan-out, we cannot replace the filter graph after that point
    1763             :                 //this would affect the other branches of the upper graph
    1764           3 :                 if (opid->num_destinations != 1) break;
    1765           3 :                 pidinst = gf_list_get(opid->destinations, 0);
    1766             : 
    1767           3 :                 if (gf_fq_count(pidinst->packets))
    1768           0 :                         (*needs_flush) = GF_TRUE;
    1769             :         }
    1770           4 :         return pidinst;
    1771             : }
    1772             : 
    1773           0 : void gf_filter_relink_task(GF_FSTask *task)
    1774             : {
    1775             :         Bool needs_flush;
    1776           0 :         GF_FilterPidInst *cur_pidinst = task->udta;
    1777           0 :         /*GF_FilterPidInst *pidinst = */filter_relink_get_upper_pid(cur_pidinst, &needs_flush);
    1778           0 :         if (needs_flush) {
    1779           0 :                 task->requeue_request = GF_TRUE;
    1780           0 :                 return;
    1781             :         }
    1782             :         //good do go, unprotect pid
    1783             :         assert(cur_pidinst->detach_pending);
    1784           0 :         safe_int_dec(&cur_pidinst->detach_pending);
    1785           0 :         task->filter->removed = GF_FALSE;
    1786             : 
    1787           0 :         gf_filter_relink_dst(cur_pidinst);
    1788             : }
    1789             : 
    1790           4 : void gf_filter_relink_dst(GF_FilterPidInst *from_pidinst)
    1791             : {
    1792             :         GF_Filter *filter_dst;
    1793             :         GF_FilterPid *link_from_pid = NULL;
    1794             :         u32 min_chain_len = 0;
    1795           4 :         Bool needs_flush = GF_FALSE;
    1796             :         GF_FilterPidInst *src_pidinst = from_pidinst;
    1797             :         GF_FilterPidInst *dst_pidinst;
    1798           4 :         GF_Filter *cur_filter = from_pidinst->filter;
    1799             : 
    1800             :         //locate the true destination
    1801           4 :         dst_pidinst = filter_relink_get_upper_pid(src_pidinst, &needs_flush);
    1802             :         assert(dst_pidinst);
    1803             : 
    1804             :         //make sure we flush the end of the pipeline  !
    1805           4 :         if (needs_flush) {
    1806           0 :                 cur_filter->removed = 2;
    1807             :                 //prevent any fetch from pid
    1808           0 :                 safe_int_inc(&src_pidinst->detach_pending);
    1809           0 :                 gf_fs_post_task(cur_filter->session, gf_filter_relink_task, cur_filter, NULL, "relink_dst", src_pidinst);
    1810           0 :                 return;
    1811             :         }
    1812           4 :         filter_dst = dst_pidinst->filter;
    1813             : 
    1814           4 :         gf_fs_check_graph_load(cur_filter->session, GF_TRUE);
    1815             : 
    1816             :         //walk down the filter chain and find the shortest path to our destination
    1817             :         //stop when the current filter is not a one-to-one filter
    1818           2 :         while (1) {
    1819             :                 u32 fchain_len;
    1820             :                 GF_FilterPidInst *an_inpid = NULL;
    1821           6 :                 gf_mx_p(cur_filter->tasks_mx);
    1822           6 :                 if ((cur_filter->num_input_pids>1) || (cur_filter->num_output_pids>1)) {
    1823           2 :                         gf_mx_v(cur_filter->tasks_mx);
    1824           2 :                         break;
    1825             :                 }
    1826             : 
    1827           4 :                 an_inpid = gf_list_get(cur_filter->input_pids, 0);
    1828           4 :                 if (!an_inpid) {
    1829           0 :                         gf_mx_v(cur_filter->tasks_mx);
    1830           0 :                         break;
    1831             :                 }
    1832           4 :                 if (gf_filter_pid_caps_match((GF_FilterPid *)an_inpid, filter_dst->freg, filter_dst, NULL, NULL, NULL, -1)) {
    1833           2 :                         link_from_pid = an_inpid->pid;
    1834           2 :                         gf_mx_v(cur_filter->tasks_mx);
    1835           2 :                         break;
    1836             :                 }
    1837           2 :                 fchain_len = gf_filter_pid_resolve_link_length(an_inpid->pid, filter_dst);
    1838           2 :                 if (fchain_len && (!min_chain_len || (min_chain_len > fchain_len))) {
    1839             :                         min_chain_len = fchain_len;
    1840           2 :                         link_from_pid = an_inpid->pid;
    1841             :                 }
    1842           2 :                 gf_mx_v(cur_filter->tasks_mx);
    1843           2 :                 cur_filter = an_inpid->pid->filter;
    1844             :         }
    1845             : 
    1846           4 :         if (!link_from_pid) {
    1847           0 :                 gf_fs_check_graph_load(cur_filter->session, GF_FALSE);
    1848           0 :                 return;
    1849             :         }
    1850             :         //detach the pidinst, and relink from the new input pid
    1851           4 :         gf_filter_renegociate_output_dst(link_from_pid, link_from_pid->filter, filter_dst, dst_pidinst, src_pidinst);
    1852             : }
    1853             : 
    1854          99 : void gf_filter_renegociate_output_dst(GF_FilterPid *pid, GF_Filter *filter, GF_Filter *filter_dst, GF_FilterPidInst *dst_pidi, GF_FilterPidInst *src_pidi)
    1855             : {
    1856             :         Bool is_new_chain = GF_TRUE;
    1857             :         GF_Filter *new_f;
    1858          99 :         Bool reconfig_only = src_pidi ? GF_FALSE: GF_TRUE;
    1859             : 
    1860             :         assert(filter);
    1861             : 
    1862          99 :         if (!filter_dst) {
    1863           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Internal error, lost destination for pid %s in filter %s while negotiating caps !!\n", pid->name, filter->name));
    1864             :                 return;
    1865             :         }
    1866             : 
    1867             :         //try to load filters to reconnect output pid
    1868             :         //we pass the source pid if given, so that we are sure the active property set is used to match the caps
    1869         101 :         if (!reconfig_only && gf_filter_pid_caps_match(src_pidi ? (GF_FilterPid *)src_pidi : pid, filter_dst->freg, filter_dst, NULL, NULL, NULL, -1)) {
    1870             :                 GF_FilterPidInst *a_dst_pidi;
    1871           2 :                 new_f = pid->filter;
    1872             :                 assert(pid->num_destinations==1);
    1873           2 :                 a_dst_pidi = gf_list_get(pid->destinations, 0);
    1874             :                 //we are replacing the chain, remove filters until dest, keeping the final PID connected since we will detach
    1875             :                 // and reattach it
    1876           2 :                 if (!filter_dst->sticky) filter_dst->sticky = 2;
    1877           2 :                 gf_filter_remove_internal(a_dst_pidi->filter, filter_dst, GF_TRUE);
    1878             :                 is_new_chain = GF_FALSE;
    1879             : 
    1880             :                 //we will reassign packets from that pid instance to the new connection
    1881           2 :                 filter_dst->swap_pidinst_dst = a_dst_pidi;
    1882             : 
    1883           2 :                 src_pidi->filter->removed = 2;
    1884             : 
    1885             :         }
    1886             :         //we are inserting a new chain for reconfiguration only
    1887          97 :         else if (reconfig_only) {
    1888          95 :                 gf_fs_check_graph_load(filter_dst->session, GF_TRUE);
    1889             :                 //make sure we don't try the PID parent filter since we just failed reconfiguring it
    1890          95 :                 gf_list_add(pid->filter->blacklisted, (void *) pid->filter->freg);
    1891          95 :                 new_f = gf_filter_pid_resolve_link_for_caps(pid, filter_dst, GF_TRUE);
    1892             : 
    1893          95 :                 gf_list_del_item(pid->filter->blacklisted, (void *)pid->filter->freg);
    1894             : 
    1895             :                 //special case: no adaptation filter found but destination filter has forced caps set, try to load a filter chain allowing for new caps
    1896          95 :                 if (!new_f && filter_dst->forced_caps) {
    1897           0 :                         new_f = gf_filter_pid_resolve_link_for_caps(pid, filter_dst, GF_FALSE);
    1898           0 :                         if (new_f) {
    1899             :                                 //drop caps negociate
    1900             :                                 reconfig_only = GF_FALSE;
    1901             :                         }
    1902             :                 }
    1903             :         }
    1904             :         //we are inserting a new chain
    1905             :         else {
    1906             :                 Bool reassigned;
    1907           2 :                 gf_fs_check_graph_load(filter_dst->session, GF_TRUE);
    1908           2 :                 new_f = gf_filter_pid_resolve_link(pid, filter_dst, &reassigned);
    1909             :         }
    1910             : 
    1911          99 :         gf_fs_check_graph_load(filter_dst->session, GF_FALSE);
    1912             : 
    1913          99 :         if (! new_f) {
    1914           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("No suitable filter to adapt caps between pid %s in filter %s to filter %s, disconnecting pid!\n", pid->name, filter->name, filter_dst->name));
    1915           0 :                 filter->session->last_connect_error = GF_FILTER_NOT_FOUND;
    1916             : 
    1917           0 :                 if (pid->adapters_blacklist) {
    1918           0 :                         gf_list_del(pid->adapters_blacklist);
    1919           0 :                         pid->adapters_blacklist = NULL;
    1920             :                 }
    1921           0 :                 if (pid->num_destinations==1) {
    1922             :                         GF_FilterEvent evt;
    1923           0 :                         GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
    1924           0 :                         gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
    1925           0 :                         GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
    1926           0 :                         gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
    1927             :                 }
    1928           0 :                 if (dst_pidi) {
    1929           0 :                         gf_fs_post_task(filter->session, gf_filter_pid_disconnect_task, dst_pidi->filter, dst_pidi->pid, "pidinst_disconnect", NULL);
    1930             :                 }
    1931             :                 return;
    1932             :         }
    1933             :         //detach pid instance from its source pid
    1934          99 :         if (dst_pidi) {
    1935             :                 //signal as detached, this will prevent any further packet access
    1936          99 :                 safe_int_inc(&dst_pidi->detach_pending);
    1937             : 
    1938             :                 //we need to first reconnect the pid and then detach the output
    1939          99 :                 if (is_new_chain) {
    1940             :                         //signal a stream reset is pending to prevent filter entering endless loop
    1941          97 :                         safe_int_inc(&dst_pidi->filter->stream_reset_pending);
    1942             :                         //keep track of the pidinst being detached in the target filter
    1943          97 :                         new_f->swap_pidinst_dst = dst_pidi;
    1944             :                         //keep track of the pidinst being detached from the source filter
    1945          97 :                         new_f->swap_pidinst_src = src_pidi;
    1946          97 :                         new_f->swap_needs_init = GF_TRUE;
    1947             :                 }
    1948             :                 //we directly detach the pid
    1949             :                 else {
    1950           2 :                         safe_int_inc(&dst_pidi->pid->filter->detach_pid_tasks_pending);
    1951           2 :                         safe_int_inc(&filter_dst->detach_pid_tasks_pending);
    1952           2 :                         gf_fs_post_task(filter->session, gf_filter_pid_detach_task, filter_dst, dst_pidi->pid, "pidinst_detach", filter_dst);
    1953             :                 }
    1954             :         }
    1955             : 
    1956          99 :         if (reconfig_only) {
    1957             :                 assert(pid->caps_negociate);
    1958          95 :                 new_f->caps_negociate = pid->caps_negociate;
    1959          95 :                 safe_int_inc(&new_f->caps_negociate->reference_count);
    1960             :         }
    1961             : 
    1962          99 :         if (is_new_chain) {
    1963             :                 //mark this filter has having pid connection pending to prevent packet dispatch until the connection is done
    1964          97 :                 safe_int_inc(&pid->filter->out_pid_connection_pending);
    1965          97 :                 gf_filter_pid_post_connect_task(new_f, pid);
    1966             :         } else {
    1967           2 :                 gf_fs_post_task(filter->session, gf_filter_pid_reconfigure_task, filter_dst, pid, "pidinst_reconfigure", NULL);
    1968             :         }
    1969             : }
    1970             : 
    1971          95 : Bool gf_filter_reconf_output(GF_Filter *filter, GF_FilterPid *pid)
    1972             : {
    1973             :         GF_Err e;
    1974             :         GF_FilterPidInst *src_pidi;
    1975             :         GF_FilterPid *src_pid;
    1976             : 
    1977          95 :         gf_mx_p(filter->tasks_mx);
    1978          95 :         src_pidi = gf_list_get(filter->input_pids, 0);
    1979          95 :         src_pid = src_pidi->pid;
    1980          95 :         if (filter->is_pid_adaptation_filter) {
    1981             :                 //do not remove from destination_filters, needed for end of pid_init task
    1982          95 :                 if (!filter->dst_filter) filter->dst_filter = gf_list_get(filter->destination_filters, 0);
    1983             :                 assert(filter->dst_filter);
    1984             :                 assert(filter->num_input_pids==1);
    1985             :         }
    1986             :         assert(filter->freg->reconfigure_output);
    1987             :         //swap to pid
    1988          95 :         pid->caps_negociate = filter->caps_negociate;
    1989          95 :         filter->caps_negociate = NULL;
    1990          95 :         e = filter->freg->reconfigure_output(filter, pid);
    1991             : 
    1992             : 
    1993          95 :         if (e!=GF_OK) {
    1994           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("PID Adaptation Filter %s output reconfiguration error %s, discarding filter and reloading new adaptation chain\n", filter->name, gf_error_to_string(e)));
    1995           0 :                 gf_filter_pid_retry_caps_negotiate(src_pid, pid, filter->dst_filter);
    1996           0 :                 gf_mx_v(filter->tasks_mx);
    1997           0 :                 return GF_FALSE;
    1998             :         }
    1999          95 :         GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("PID Adaptation Filter %s output reconfiguration OK (between filters %s and %s)\n", filter->name, src_pid->filter->name, filter->dst_filter->name));
    2000             : 
    2001          95 :         gf_filter_check_output_reconfig(filter);
    2002             : 
    2003             :         //success !
    2004          95 :         if (src_pid->adapters_blacklist) {
    2005           0 :                 gf_list_del(pid->adapters_blacklist);
    2006           0 :                 src_pid->adapters_blacklist = NULL;
    2007             :         }
    2008             :         assert(pid->caps_negociate->reference_count);
    2009          95 :         if (safe_int_dec(&pid->caps_negociate->reference_count) == 0) {
    2010          95 :                 gf_props_del(pid->caps_negociate);
    2011             :         }
    2012          95 :         pid->caps_negociate = NULL;
    2013          95 :         if (filter->is_pid_adaptation_filter) {
    2014          95 :                 filter->dst_filter = NULL;
    2015             :         }
    2016          95 :         gf_mx_v(filter->tasks_mx);
    2017          95 :         return GF_TRUE;
    2018             : }
    2019             : 
    2020           0 : void gf_filter_reconfigure_output_task(GF_FSTask *task)
    2021             : {
    2022           0 :         GF_Filter *filter = task->filter;
    2023             :         GF_FilterPid *pid;
    2024             :         assert(filter->num_output_pids==1);
    2025           0 :         pid = gf_list_get(filter->output_pids, 0);
    2026           0 :         gf_filter_reconf_output(filter, pid);
    2027           0 : }
    2028             : 
    2029             : 
    2030         134 : static void gf_filter_renegociate_output(GF_Filter *filter, Bool force_afchain_insert)
    2031             : {
    2032             :         u32 i, j;
    2033             :         assert(filter->nb_caps_renegociate );
    2034         134 :         safe_int_dec(& filter->nb_caps_renegociate );
    2035             : 
    2036         134 :         gf_mx_p(filter->tasks_mx);
    2037             : 
    2038         270 :         for (i=0; i<filter->num_output_pids; i++) {
    2039         136 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    2040         136 :                 if (pid->caps_negociate) {
    2041             :                         Bool is_ok = GF_FALSE;
    2042             :                         Bool reconfig_direct = GF_FALSE;
    2043             : 
    2044             :                         //the caps_negociate property map is create with ref count 1
    2045             : 
    2046             :                         //no fanout, we can try direct reconfigure of the filter
    2047         134 :                         if (pid->num_destinations<=1)
    2048             :                                 reconfig_direct = GF_TRUE;
    2049             :                         //fanout but we have as many pid instances being negociated as there are destinations, we can try direct reconfigure
    2050           0 :                         else if (pid->num_destinations==gf_list_count(pid->caps_negociate_pidi_list) && pid->caps_negociate_direct)
    2051             :                                 reconfig_direct = GF_TRUE;
    2052             : 
    2053             :                         //we cannot reconfigure output if more than one destination
    2054         134 :                         if (reconfig_direct && filter->freg->reconfigure_output && !force_afchain_insert) {
    2055          39 :                                 GF_Err e = filter->freg->reconfigure_output(filter, pid);
    2056          39 :                                 if (e) {
    2057           0 :                                         if (filter->is_pid_adaptation_filter) {
    2058           0 :                                                 GF_FilterPidInst *src_pidi = gf_list_get(filter->input_pids, 0);
    2059           0 :                                                 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, 0);
    2060             : 
    2061           0 :                                                 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("PID Adaptation Filter %s output reconfiguration error %s, discarding filter and reloading new adaptation chain\n", filter->name, gf_error_to_string(e)));
    2062             : 
    2063             :                                                 assert(filter->num_input_pids==1);
    2064             : 
    2065           0 :                                                 gf_filter_pid_retry_caps_negotiate(src_pidi->pid, pid, pidi->filter);
    2066             : 
    2067           0 :                                                 continue;
    2068             :                                         }
    2069           0 :                                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Filter %s output reconfiguration error %s, loading filter chain for renegociation\n", filter->name, gf_error_to_string(e)));
    2070             :                                 } else {
    2071             :                                         is_ok = GF_TRUE;
    2072          39 :                                         gf_filter_check_output_reconfig(filter);
    2073             :                                 }
    2074             :                         } else {
    2075          95 :                                 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s cannot reconfigure output pids, loading filter chain for renegociation\n", filter->name));
    2076             :                         }
    2077             : 
    2078             :                         if (!is_ok) {
    2079             :                                 GF_Filter *filter_dst;
    2080             :                                 //we are currently connected to output
    2081          95 :                                 if (pid->num_destinations) {
    2082          95 :                                         for (j=0; j<pid->num_destinations; j++) {
    2083          95 :                                                 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    2084          95 :                                                 if (gf_list_find(pid->caps_negociate_pidi_list, pidi)<0)
    2085           0 :                                                         continue;
    2086          95 :                                                 filter_dst = pidi->filter;
    2087             : 
    2088          95 :                                                 if (filter_dst->freg->reconfigure_output) {
    2089             :                                                         assert(!filter_dst->caps_negociate);
    2090           0 :                                                         filter_dst->caps_negociate = pid->caps_negociate;
    2091           0 :                                                         safe_int_inc(&filter_dst->caps_negociate->reference_count);
    2092             : 
    2093           0 :                                                         gf_fs_post_task(filter->session, gf_filter_reconfigure_output_task, filter_dst, NULL, "filter reconfigure output", NULL);
    2094             :                                                 } else {
    2095             :                                                         //disconnect pid, but prevent filter from unloading
    2096          95 :                                                         if (!filter_dst->sticky) filter_dst->sticky = 2;
    2097          95 :                                                         gf_filter_renegociate_output_dst(pid, filter, filter_dst, pidi, NULL);
    2098             :                                                 }
    2099             :                                         }
    2100             :                                 }
    2101             :                                 //we are disconnected (unload of a previous adaptation filter)
    2102             :                                 else {
    2103           0 :                                         filter_dst = pid->caps_dst_filter;
    2104             :                                         assert(pid->num_destinations==0);
    2105           0 :                                         pid->caps_dst_filter = NULL;
    2106           0 :                                         gf_filter_renegociate_output_dst(pid, filter, filter_dst, NULL, NULL);
    2107             :                                 }
    2108             :                         }
    2109             :                         assert(pid->caps_negociate->reference_count);
    2110         134 :                         if (safe_int_dec(&pid->caps_negociate->reference_count) == 0) {
    2111          39 :                                 gf_props_del(pid->caps_negociate);
    2112             :                         }
    2113         134 :                         pid->caps_negociate = NULL;
    2114         134 :                         if (pid->caps_negociate_pidi_list) {
    2115         134 :                                 gf_list_del(pid->caps_negociate_pidi_list);
    2116         134 :                                 pid->caps_negociate_pidi_list = NULL;
    2117             :                         }
    2118             :                 }
    2119             :         }
    2120         134 :         gf_mx_v(filter->tasks_mx);
    2121         134 : }
    2122             : 
    2123           8 : void gf_filter_renegociate_output_task(GF_FSTask *task)
    2124             : {
    2125             :         //it is possible that the cap renegociation was already done at the time we process this task
    2126           8 :         if (task->filter->nb_caps_renegociate)
    2127           2 :                 gf_filter_renegociate_output(task->filter, GF_TRUE);
    2128           8 : }
    2129             : 
    2130    15992564 : static void gf_filter_check_pending_tasks(GF_Filter *filter, GF_FSTask *task)
    2131             : {
    2132    15992564 :         if (filter->session->run_status!=GF_OK) {
    2133             :                 return;
    2134             :         }
    2135             : 
    2136             :         //lock task mx to take the decision whether to requeue a new task or not (cf gf_filter_post_process_task)
    2137             :         //TODO: find a way to bypass this mutex ?
    2138    15992570 :         gf_mx_p(filter->tasks_mx);
    2139             : 
    2140             :         assert(filter->scheduled_for_next_task || filter->session->direct_mode);
    2141             :         assert(filter->process_task_queued);
    2142    15992575 :         if (safe_int_dec(&filter->process_task_queued) == 0) {
    2143             :                 //we have pending packets, auto-post and requeue
    2144     3297519 :                 if (filter->pending_packets) {
    2145     2582855 :                         safe_int_inc(&filter->process_task_queued);
    2146     2582855 :                         task->requeue_request = GF_TRUE;
    2147             :                 }
    2148             :                 //special case here: no more pending packets, filter has detected eos on one of its input but is still generating packet,
    2149             :                 //we reschedule (typically flush of decoder)
    2150      714664 :                 else if (filter->eos_probe_state == 2) {
    2151       44748 :                         safe_int_inc(&filter->process_task_queued);
    2152       44748 :                         task->requeue_request = GF_TRUE;
    2153       44748 :                         filter->eos_probe_state = 0;
    2154             :                 }
    2155             :                 //we are done for now
    2156             :                 else {
    2157      669916 :                         task->requeue_request = GF_FALSE;
    2158             :                 }
    2159             :         } else {
    2160             :                 //we have some more process() requests queued, requeue
    2161    12695056 :                 task->requeue_request = GF_TRUE;
    2162             :         }
    2163    15992575 :         if (task->requeue_request) {
    2164    15322659 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("[Filter] %s kept in scheduler blocking %d\n", filter->name, filter->would_block));
    2165             :         } else {
    2166      669916 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("[Filter] %s removed from scheduler - blocking %d\n", filter->name, filter->would_block));
    2167             :         }
    2168    15992575 :         gf_mx_v(filter->tasks_mx);
    2169             : }
    2170             : 
    2171             : #ifdef GPAC_MEMORY_TRACKING
    2172    29407580 : static GF_Err gf_filter_process_check_alloc(GF_Filter *filter)
    2173             : {
    2174             :         GF_Err e;
    2175    29407580 :         u32 nb_allocs=0, nb_callocs=0, nb_reallocs=0, nb_free=0;
    2176    29407580 :         u32 prev_nb_allocs=0, prev_nb_callocs=0, prev_nb_reallocs=0, prev_nb_free=0;
    2177             : 
    2178             :         //reset alloc/realloc stats of filter
    2179    29407580 :         filter->session->nb_alloc_pck = 0;
    2180    29407580 :         filter->session->nb_realloc_pck = 0;
    2181             :         //get current alloc state
    2182    29407580 :         gf_mem_get_stats(&prev_nb_allocs, &prev_nb_callocs, &prev_nb_reallocs, &prev_nb_free);
    2183    29407580 :         e = filter->freg->process(filter);
    2184             : 
    2185             :         //get new alloc state
    2186    29407580 :         gf_mem_get_stats(&nb_allocs, &nb_callocs, &nb_reallocs, &nb_free);
    2187             :         //remove prev alloc stats
    2188    29407580 :         nb_allocs -= prev_nb_allocs;
    2189    29407580 :         nb_callocs -= prev_nb_callocs;
    2190    29407580 :         nb_reallocs -= prev_nb_reallocs;
    2191    29407580 :         nb_free -= prev_nb_free;
    2192             : 
    2193             :         //remove internal allocs/reallocs due to filter lib
    2194    29407580 :         if (nb_allocs>filter->session->nb_alloc_pck)
    2195      174642 :                 nb_allocs -= filter->session->nb_alloc_pck;
    2196             :         else
    2197    29232938 :                 nb_allocs = 0;
    2198             : 
    2199    29407580 :         if (nb_reallocs>filter->session->nb_realloc_pck)
    2200       56170 :                 nb_reallocs -= filter->session->nb_realloc_pck;
    2201             :         else
    2202    29351410 :                 nb_reallocs = 0;
    2203             : 
    2204             :         //we now have nomber of allocs/realloc used by the filter internally during its process
    2205    29407580 :         if (nb_allocs || nb_callocs || nb_reallocs /* || nb_free */) {
    2206      201941 :                 filter->stats_nb_alloc += nb_allocs;
    2207      201941 :                 filter->stats_nb_calloc += nb_callocs;
    2208      201941 :                 filter->stats_nb_realloc += nb_reallocs;
    2209      201941 :                 filter->stats_nb_free += nb_free;
    2210             :         } else {
    2211    29205639 :                 filter->nb_consecutive_process ++;
    2212             :         }
    2213    29407580 :         filter->nb_process_since_reset++;
    2214    29407580 :         return e;
    2215             : 
    2216             : }
    2217             : #endif
    2218             : 
    2219    29460915 : static GFINLINE void check_filter_error(GF_Filter *filter, GF_Err e, Bool for_reconnection)
    2220             : {
    2221             :         GF_Err out_e = e;
    2222             :         Bool kill_filter = GF_FALSE;
    2223    29460915 :         if (e>GF_OK) e = GF_OK;
    2224    29460919 :         else if (e==GF_IP_NETWORK_EMPTY) e = GF_OK;
    2225             : 
    2226    29459015 :         if (e) {
    2227             :                 u64 diff;
    2228           3 :                 filter->session->last_process_error = e;
    2229             : 
    2230           3 :                 filter->nb_errors ++;
    2231           3 :                 if (!filter->nb_consecutive_errors) filter->time_at_first_error = gf_sys_clock_high_res();
    2232             : 
    2233           3 :                 filter->nb_consecutive_errors ++;
    2234           3 :                 if (filter->nb_pck_io && !filter->session->in_final_flush)
    2235           3 :                         filter->nb_consecutive_errors = 0;
    2236             :                 //give it at most one second
    2237           3 :                 diff = gf_sys_clock_high_res() - filter->time_at_first_error;
    2238           3 :                 if (diff >= 1000000) {
    2239           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("[Filter] %s in error / not responding properly: %d consecutive errors in "LLU" us with no packet discarded or sent\n\tdiscarding all inputs and notifying end of stream on all outputs\n", filter->name, filter->nb_consecutive_errors, diff));
    2240             :                         kill_filter = GF_TRUE;
    2241             :                 }
    2242             :         } else {
    2243    29460912 :                 if ((!filter->nb_pck_io && filter->pending_packets && (filter->nb_pids_playing>0) ) || for_reconnection) {
    2244       49203 :                         filter->nb_consecutive_errors++;
    2245             : 
    2246             :                         out_e = GF_SERVICE_ERROR;
    2247       49203 :                         if (filter->nb_consecutive_errors >= 100000) {
    2248           0 :                                 if (for_reconnection) {
    2249           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("[Filter] %s not responding properly: %d consecutive attempts at reconfiguring\n\tdiscarding all inputs and notifying end of stream on all outputs\n", filter->name, filter->nb_consecutive_errors));
    2250           0 :                                 } else if (!filter->session->in_final_flush) {
    2251           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("[Filter] %s not responding properly: %d consecutive process with no packet discarded or sent, but %d packets pending\n\tdiscarding all inputs and notifying end of stream on all outputs\n", filter->name, filter->nb_consecutive_errors, filter->pending_packets));
    2252             :                                 } else {
    2253             :                                         out_e = GF_OK;
    2254             :                                 }
    2255             :                                 kill_filter = GF_TRUE;
    2256             :                         }
    2257             :                 } else {
    2258    29411709 :                         filter->nb_consecutive_errors = 0;
    2259    29411709 :                         filter->nb_pck_io = 0;
    2260             :                 }
    2261             :         }
    2262             : 
    2263             :         if (kill_filter) {
    2264             :                 u32 i;
    2265           0 :                 gf_mx_p(filter->tasks_mx);
    2266           0 :                 for (i=0; i<filter->num_input_pids; i++) {
    2267           0 :                         GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
    2268           0 :                         gf_filter_pid_set_discard((GF_FilterPid *)pidi, GF_TRUE);
    2269             :                 }
    2270           0 :                 for (i=0; i<filter->num_output_pids; i++) {
    2271           0 :                         GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    2272           0 :                         gf_filter_pid_set_eos(pid);
    2273             :                 }
    2274           0 :                 gf_mx_v(filter->tasks_mx);
    2275           0 :                 filter->session->last_process_error = out_e;
    2276           0 :                 filter->disabled = GF_TRUE;
    2277             :         }
    2278    29460915 : }
    2279             : 
    2280    30403573 : static void gf_filter_process_task(GF_FSTask *task)
    2281             : {
    2282             :         GF_Err e;
    2283             :         Bool skip_block_mode = GF_FALSE;
    2284    30403573 :         GF_Filter *filter = task->filter;
    2285             :         Bool force_block_state_check=GF_FALSE;
    2286             :         assert(task->filter);
    2287             :         assert(filter->freg);
    2288             :         assert(filter->freg->process);
    2289    30403573 :         task->can_swap = GF_TRUE;
    2290             : 
    2291    30403573 :         filter->schedule_next_time = 0;
    2292             : 
    2293    30403573 :         if (filter->disabled) {
    2294          69 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s is disabled, cancelling process\n", filter->name));
    2295          69 :                 gf_mx_p(task->filter->tasks_mx);
    2296          69 :                 task->filter->process_task_queued = 0;
    2297          69 :                 gf_mx_v(task->filter->tasks_mx);
    2298          69 :                 return;
    2299             :         }
    2300             : 
    2301    30403504 :         if (filter->out_pid_connection_pending || filter->detached_pid_inst || filter->caps_negociate) {
    2302      770460 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has %s pending, requeuing process\n", filter->name, filter->out_pid_connection_pending ? "connections" : filter->caps_negociate ? "caps negociation" : "input pid reassignments"));
    2303             :                 //do not cancel the process task since it might have been triggered by the filter itself,
    2304             :                 //we would not longer call it
    2305      770460 :                 task->requeue_request = GF_TRUE;
    2306             : 
    2307             :                 assert(filter->process_task_queued);
    2308             :                 //in we are during the graph resolution phase, to not ask for RT reschedule: this can post-pone process tasks and change their initial
    2309             :                 //scheduling order, resulting in random change of input pid declaration, for example:
    2310             :                 //fin1 -> reframe1 -> fA
    2311             :                 //fin2 -> reframe2 -> fA
    2312             :                 //if we postpone by 10 us finX process while wating for rfX->fA setup, depending on the CPU charge fin2 might be rescheduled before fin1
    2313             :                 //leading to pushing new/pending packets ro reframe2 before reframe1, and having fA declare its pid in the reverse order as the one expected
    2314             :                 //note that this is only valid for single-thread case, as in multithread we do not guarantee PID declaration order
    2315      770460 :                 if (!filter->out_pid_connection_pending) {
    2316          16 :                         task->schedule_next_time = gf_sys_clock_high_res() + 10000;
    2317          16 :                         check_filter_error(filter, GF_OK, GF_TRUE);
    2318             :                 }
    2319             :                 return;
    2320             :         }
    2321    29633044 :         if (filter->removed || filter->finalized) {
    2322          80 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has been %s, skipping process\n", filter->name, filter->finalized ? "finalized" : "removed"));
    2323             :                 return;
    2324             :         }
    2325             : 
    2326    29632964 :         if (filter->prevent_blocking) skip_block_mode = GF_TRUE;
    2327    29630627 :         else if (filter->session->in_final_flush) skip_block_mode = GF_TRUE;
    2328             : 
    2329             :         //blocking filter: remove filter process task - task will be reinserted upon unblock()
    2330    27968806 :         if (!skip_block_mode && filter->would_block && (filter->would_block + filter->num_out_pids_not_connected == filter->num_output_pids ) ) {
    2331      177199 :                 gf_mx_p(task->filter->tasks_mx);
    2332             :                 //it may happen that by the time we get the lock, the filter has been unblocked by another thread. If so, don't skip task
    2333      177199 :                 if (filter->would_block) {
    2334      177167 :                         filter->nb_tasks_done--;
    2335      177167 :                         task->filter->process_task_queued = 0;
    2336      177167 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s blocked, skipping process\n", filter->name));
    2337      177167 :                         gf_mx_v(task->filter->tasks_mx);
    2338      177167 :                         return;
    2339             :                 }
    2340          32 :                 gf_mx_v(task->filter->tasks_mx);
    2341             :         }
    2342    29455798 :         if (filter->stream_reset_pending) {
    2343         738 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has stream reset pending, postponing process\n", filter->name));
    2344         738 :                 filter->nb_tasks_done--;
    2345         738 :                 task->requeue_request = GF_TRUE;
    2346             :                 assert(filter->process_task_queued);
    2347         738 :                 return;
    2348             :         }
    2349             :         assert(filter->process_task_queued);
    2350             :         assert(!filter->multi_sink_target);
    2351             : 
    2352             :         //the following breaks demuxers where PIDs are not all known from start: if we filter some pids due to user request,
    2353             :         //we may end up with the following test true but not all PIDs yet declared, hence no more processing
    2354             :         //we could add a filter cap for that, but for now we simply rely on the blocking mode algo only
    2355             : #if 0
    2356             :         if (filter->num_output_pids && (filter->num_out_pids_not_connected==filter->num_output_pids)) {
    2357             :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has no valid connected outputs, skipping process\n", filter->name));
    2358             :                 return;
    2359             :         }
    2360             : #endif
    2361             : 
    2362             :         //we have postponed packets on filter, flush them
    2363    29455060 :         if (task->filter->postponed_packets) {
    2364        7425 :                 while (gf_list_count(task->filter->postponed_packets)) {
    2365        4186 :                         GF_FilterPacket *pck = gf_list_pop_front(task->filter->postponed_packets);
    2366        4186 :                         e = gf_filter_pck_send_internal(pck, GF_FALSE);
    2367        4186 :                         if (e==GF_PENDING_PACKET) {
    2368             :                                 //packet is pending so was added at the end of our postponed queue - remove from queue and reinsert in front
    2369           2 :                                 gf_list_del_item(task->filter->postponed_packets, pck);
    2370           2 :                                 gf_list_insert(task->filter->postponed_packets, pck, 0);
    2371           2 :                                 task->requeue_request = GF_TRUE;
    2372           2 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s still has postponed packets, postponing process\n", filter->name));
    2373             :                                 return;
    2374             :                         }
    2375             :                 }
    2376        3239 :                 gf_list_del(task->filter->postponed_packets);
    2377        3239 :                 task->filter->postponed_packets = NULL;
    2378             :         }
    2379             :         FSESS_CHECK_THREAD(filter)
    2380             : 
    2381    29455058 :         filter->nb_pck_io = 0;
    2382             : 
    2383    29455058 :         if (filter->nb_caps_renegociate) {
    2384         132 :                 gf_filter_renegociate_output(filter, GF_FALSE);
    2385             :         }
    2386             : 
    2387    29455058 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s process\n", filter->name));
    2388    29455058 :         gf_rmt_begin_hash(filter->name, GF_RMT_AGGREGATE, &filter->rmt_hash);
    2389             : 
    2390    29455057 :         filter->in_process_callback = GF_TRUE;
    2391             : 
    2392             : #ifdef GPAC_MEMORY_TRACKING
    2393    29455057 :         if (filter->session->check_allocs)
    2394    29401711 :                 e = gf_filter_process_check_alloc(filter);
    2395             :         else
    2396             : #endif
    2397       53346 :                 e = filter->freg->process(filter);
    2398             : 
    2399    29455032 :         filter->in_process_callback = GF_FALSE;
    2400    29455032 :         gf_rmt_end();
    2401             : 
    2402             :         //flush all pending pid init requests following the call to init
    2403    29455035 :         if (filter->has_pending_pids) {
    2404        4527 :                 filter->has_pending_pids=GF_FALSE;
    2405       13768 :                 while (gf_fq_count(filter->pending_pids)) {
    2406        4714 :                         GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
    2407        4714 :                         gf_filter_pid_post_init_task(filter, pid);
    2408             :                 }
    2409             :         }
    2410             :         //no requeue if end of session
    2411    29455035 :         if (filter->session->run_status != GF_OK) {
    2412             :                 return;
    2413             :         }
    2414             :         //if eos but we still have pending packets or process tasks queued, move to GF_OK so that
    2415             :         //we evaluate the blocking state
    2416    29455029 :         if (e==GF_EOS) {
    2417      386727 :                 if (filter->postponed_packets) {
    2418             :                         e = GF_OK;
    2419      383812 :                 } else if (filter->process_task_queued) {
    2420             :                         e = GF_OK;
    2421             :                         force_block_state_check = GF_TRUE;
    2422             :                 }
    2423             :         }
    2424             : 
    2425    29068296 :         if ((e==GF_EOS) || filter->removed || filter->finalized) {
    2426           0 :                 gf_mx_p(filter->tasks_mx);
    2427           1 :                 filter->process_task_queued = 0;
    2428           1 :                 gf_mx_v(filter->tasks_mx);
    2429           1 :                 return;
    2430             :         }
    2431             : 
    2432    29455038 :         if ((e==GF_PROFILE_NOT_SUPPORTED) && filter->has_out_caps && !(filter->session->flags & GF_FS_FLAG_NO_REASSIGN)) {
    2433             :                 u32 i;
    2434             :                 //disconnect all other inputs, and post a re-init
    2435           0 :                 gf_mx_p(filter->tasks_mx);
    2436           0 :                 for (i=0; i<filter->num_input_pids; i++) {
    2437           0 :                         GF_FilterPidInst *a_pidinst = gf_list_get(filter->input_pids, i);
    2438             : 
    2439           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Not supported profile for filter %s - blacklisting as output from %s and retrying connections\n", filter->name, a_pidinst->pid->filter->name));
    2440             : 
    2441           0 :                         gf_list_add(a_pidinst->pid->filter->blacklisted, (void *) filter->freg);
    2442             : 
    2443           0 :                         gf_filter_relink_dst(a_pidinst);
    2444             :                 }
    2445           0 :                 filter->process_task_queued = 0;
    2446           0 :                 gf_mx_v(filter->tasks_mx);
    2447           0 :                 return;
    2448             :         }
    2449    29455038 :         check_filter_error(filter, e, GF_FALSE);
    2450             : 
    2451             :         //source filters, flush data if enough space available.
    2452    29455035 :         if ( (!filter->num_output_pids || (filter->would_block + filter->num_out_pids_not_connected < filter->num_output_pids) )
    2453    28342849 :                 && !filter->input_pids
    2454     1303703 :                 && (e!=GF_EOS)
    2455     1303703 :                 && !force_block_state_check
    2456             :         ) {
    2457     1295580 :                 if (filter->schedule_next_time)
    2458      189230 :                         task->schedule_next_time = filter->schedule_next_time;
    2459     1295580 :                 task->requeue_request = GF_TRUE;
    2460     1295580 :                 assert(filter->process_task_queued);
    2461             :         }
    2462             :         //filter requested a requeue
    2463    28159455 :         else if (filter->schedule_next_time) {
    2464     7832969 :                 if (!filter->session->in_final_flush) {
    2465     7832969 :                         task->schedule_next_time = filter->schedule_next_time;
    2466     7832969 :                         task->requeue_request = GF_TRUE;
    2467             :                         assert(filter->process_task_queued);
    2468             :                 }
    2469             :         }
    2470             :         //last task for filter but pending packets and not blocking, requeue in main scheduler
    2471    20326486 :         else if ((filter->would_block < filter->num_output_pids)
    2472    15391332 :                         && filter->pending_packets
    2473     4337077 :                         && (gf_fq_count(filter->tasks)<=1)
    2474             :         ) {
    2475             :                 //prune eos packets that could still be present
    2476     4333921 :                 if (filter->pending_packets && filter->session->in_final_flush) {
    2477             :                         u32 i;
    2478     1180497 :                         for (i=0; i<filter->num_input_pids; i++) {
    2479     1180497 :                                 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
    2480     1180497 :                                 gf_filter_pid_get_packet((GF_FilterPid *)pidi);
    2481             :                         }
    2482             :                 }
    2483     4333921 :                 task->requeue_request = GF_TRUE;
    2484     4333921 :                 assert(filter->process_task_queued);
    2485             :         }
    2486             :         else {
    2487             :                 assert (!filter->schedule_next_time);
    2488    15992565 :                 gf_filter_check_pending_tasks(filter, task);
    2489             :                 if (task->requeue_request) {
    2490             :                         assert(filter->process_task_queued);
    2491             :                 }
    2492             :         }
    2493             : }
    2494             : 
    2495        5869 : void gf_filter_process_inline(GF_Filter *filter)
    2496             : {
    2497             :         GF_Err e;
    2498        5869 :         if (filter->out_pid_connection_pending || filter->removed || filter->stream_reset_pending || filter->multi_sink_target) {
    2499             :                 return;
    2500             :         }
    2501        5869 :         if (filter->would_block && (filter->would_block == filter->num_output_pids) ) {
    2502             :                 return;
    2503             :         }
    2504        5869 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s inline process\n", filter->name));
    2505             : 
    2506        5869 :         if (filter->postponed_packets) {
    2507           0 :                 while (gf_list_count(filter->postponed_packets)) {
    2508           0 :                         GF_FilterPacket *pck = gf_list_pop_front(filter->postponed_packets);
    2509           0 :                         gf_filter_pck_send(pck);
    2510             :                 }
    2511           0 :                 gf_list_del(filter->postponed_packets);
    2512           0 :                 filter->postponed_packets = NULL;
    2513           0 :                 if (filter->process_task_queued==1) {
    2514           0 :                         gf_mx_p(filter->tasks_mx);
    2515           0 :                         filter->process_task_queued = 0;
    2516           0 :                         gf_mx_v(filter->tasks_mx);
    2517           0 :                         return;
    2518             :                 }
    2519             :         }
    2520             :         FSESS_CHECK_THREAD(filter)
    2521             : 
    2522             : #ifdef GPAC_MEMORY_TRACKING
    2523        5869 :         if (filter->session->check_allocs)
    2524        5869 :                 e = gf_filter_process_check_alloc(filter);
    2525             :         else
    2526             : #endif
    2527           0 :                 e = filter->freg->process(filter);
    2528             : 
    2529             :         //flush all pending pid init requests following the call to init
    2530        5869 :         if (filter->has_pending_pids) {
    2531           0 :                 filter->has_pending_pids=GF_FALSE;
    2532           0 :                 while (gf_fq_count(filter->pending_pids)) {
    2533           0 :                         GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
    2534           0 :                         gf_filter_pid_post_init_task(filter, pid);
    2535             :                 }
    2536             :         }
    2537             :         //no requeue if end of session
    2538        5869 :         if (filter->session->run_status != GF_OK) {
    2539             :                 return;
    2540             :         }
    2541        5869 :         if ((e==GF_EOS) || filter->removed || filter->finalized) {
    2542           5 :                 gf_mx_p(filter->tasks_mx);
    2543           5 :                 filter->process_task_queued = 0;
    2544           5 :                 gf_mx_v(filter->tasks_mx);
    2545           5 :                 return;
    2546             :         }
    2547        5864 :         check_filter_error(filter, e, GF_FALSE);
    2548             : }
    2549             : 
    2550             : GF_EXPORT
    2551          18 : void gf_filter_send_update(GF_Filter *filter, const char *fid, const char *name, const char *val, GF_EventPropagateType propagate_mask)
    2552             : {
    2553          18 :         if (filter) gf_fs_send_update(filter->session, fid, fid ? NULL : filter, name, val, propagate_mask);
    2554          18 : }
    2555             : 
    2556          53 : GF_Filter *gf_filter_clone(GF_Filter *filter)
    2557             : {
    2558          53 :         GF_Filter *new_filter = gf_filter_new(filter->session, filter->freg, filter->orig_args, NULL, filter->arg_type, NULL, NULL, GF_FALSE);
    2559          53 :         if (!new_filter) return NULL;
    2560          53 :         new_filter->cloned_from = filter;
    2561          53 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter cloned (register %s, args %s)\n", filter->freg->name, filter->orig_args ? filter->orig_args : "none"));
    2562             : 
    2563             :         return new_filter;
    2564             : }
    2565             : 
    2566             : GF_EXPORT
    2567      862244 : u32 gf_filter_get_ipid_count(GF_Filter *filter)
    2568             : {
    2569      862244 :         return filter->num_input_pids;
    2570             : }
    2571             : 
    2572             : GF_EXPORT
    2573     1836281 : GF_FilterPid *gf_filter_get_ipid(GF_Filter *filter, u32 idx)
    2574             : {
    2575     1836281 :         return gf_list_get(filter->input_pids, idx);
    2576             : }
    2577             : 
    2578             : GF_EXPORT
    2579       52460 : u32 gf_filter_get_opid_count(GF_Filter *filter)
    2580             : {
    2581       52460 :         return filter->num_output_pids;
    2582             : }
    2583             : 
    2584             : GF_EXPORT
    2585      308398 : GF_FilterPid *gf_filter_get_opid(GF_Filter *filter, u32 idx)
    2586             : {
    2587      308398 :         return gf_list_get(filter->output_pids, idx);
    2588             : }
    2589             : 
    2590    14557519 : void gf_filter_post_process_task_internal(GF_Filter *filter, Bool use_direct_dispatch)
    2591             : {
    2592    14557519 :         if (filter->finalized || filter->removed)
    2593             :                 return;
    2594             : 
    2595             :         //although this is theoretically OK, it breaks quite some tests. Need further investigation
    2596             : #if 0
    2597             :         //if regular posting (not direct) and our caller is the main process function, no need to lock task mutex, just increase
    2598             :         //the next scheduled time
    2599             :         if (!use_direct_dispatch && filter->in_process_callback) {
    2600             :                 filter->schedule_next_time = 1 + gf_sys_clock_high_res();
    2601             :                 return;
    2602             :         }
    2603             : #endif
    2604             : 
    2605             :         //lock task mx to take the decision whether to post a new task or not (cf gf_filter_check_pending_tasks)
    2606    14557480 :         gf_mx_p(filter->tasks_mx);
    2607             :         assert((s32)filter->process_task_queued>=0);
    2608             : 
    2609    14557480 :         if (use_direct_dispatch) {
    2610       24808 :                 safe_int_inc(&filter->process_task_queued);
    2611       24808 :                 gf_fs_post_task_ex(filter->session, gf_filter_process_task, filter, NULL, "process", NULL, GF_FALSE, GF_TRUE);
    2612    14532672 :         } else if (safe_int_inc(&filter->process_task_queued) <= 1) {
    2613      822457 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s added to scheduler\n", filter->freg->name));
    2614      822457 :                 gf_fs_post_task(filter->session, gf_filter_process_task, filter, NULL, "process", NULL);
    2615             :         } else {
    2616    13710215 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s skip post process task\n", filter->freg->name));
    2617             :                 assert(filter->session->run_status
    2618             :                                 || filter->session->in_final_flush
    2619             :                                 || filter->disabled
    2620             :                                 || filter->scheduled_for_next_task
    2621             :                                 || filter->session->direct_mode
    2622             :                                 || gf_fq_count(filter->tasks)
    2623             :                 );
    2624             :         }
    2625             :         if (!filter->session->direct_mode && !use_direct_dispatch) {
    2626             :                 assert(filter->process_task_queued);
    2627             :         }
    2628    14557480 :         gf_mx_v(filter->tasks_mx);
    2629             : }
    2630             : 
    2631             : GF_EXPORT
    2632    12590111 : void gf_filter_post_process_task(GF_Filter *filter)
    2633             : {
    2634    12590111 :         gf_filter_post_process_task_internal(filter, GF_FALSE);
    2635    12590111 : }
    2636             : GF_EXPORT
    2637    15925523 : void gf_filter_ask_rt_reschedule(GF_Filter *filter, u32 us_until_next)
    2638             : {
    2639    15925523 :         if (!filter->in_process) {
    2640           0 :                 if (filter->session->direct_mode) return;
    2641           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Filter %s request for real-time reschedule but filter is not in process\n", filter->name));
    2642             :                 return;
    2643             :         }
    2644    15925523 :         if (filter->session->in_final_flush)
    2645             :                 us_until_next = 0;
    2646             : 
    2647             :         //if the filter requests rescheduling, consider it is in a valid state and increment pck IOs to avoid flagging it as broken
    2648    15925523 :         filter->nb_pck_io++;
    2649    15925523 :         if (!us_until_next) {
    2650             :                 return;
    2651             :         }
    2652     8022608 :         filter->schedule_next_time = 1+us_until_next + gf_sys_clock_high_res();
    2653     8022608 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SCHEDULER, ("Filter %s real-time reschedule in %d us (at "LLU" sys clock)\n", filter->name, us_until_next, filter->schedule_next_time));
    2654             : }
    2655             : 
    2656             : GF_EXPORT
    2657         557 : void gf_filter_set_setup_failure_callback(GF_Filter *filter, GF_Filter *source_filter, void (*on_setup_error)(GF_Filter *f, void *on_setup_error_udta, GF_Err e), void *udta)
    2658             : {
    2659         557 :         if (!filter) return;
    2660         557 :         if (!source_filter) return;
    2661         557 :         source_filter->on_setup_error = on_setup_error;
    2662         557 :         source_filter->on_setup_error_filter = filter;
    2663         557 :         source_filter->on_setup_error_udta = udta;
    2664             : }
    2665             : 
    2666             : struct _gf_filter_setup_failure
    2667             : {
    2668             :         GF_Err e;
    2669             :         GF_Filter *filter;
    2670             :         GF_Filter *notify_filter;
    2671             :         Bool do_disconnect;
    2672             : } filter_setup_failure;
    2673             : 
    2674           3 : static void gf_filter_setup_failure_task(GF_FSTask *task)
    2675             : {
    2676             :         s32 res;
    2677             :         GF_Err e;
    2678           3 :         GF_Filter *f = ((struct _gf_filter_setup_failure *)task->udta)->filter;
    2679             :         if (task->udta) {
    2680           3 :                 e = ((struct _gf_filter_setup_failure *)task->udta)->e;
    2681           3 :                 gf_free(task->udta);
    2682           3 :                 f->session->last_connect_error = e;
    2683             :         }
    2684             : 
    2685           3 :         if (!f->finalized && f->freg->finalize) {
    2686             :                 FSESS_CHECK_THREAD(f)
    2687           3 :                 f->freg->finalize(f);
    2688             :         }
    2689           3 :         if (f->session->filters_mx) gf_mx_p(f->session->filters_mx);
    2690             : 
    2691           3 :         res = gf_list_del_item(f->session->filters, f);
    2692           3 :         if (res < 0) {
    2693           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Filter %s task failure callback on already removed filter!\n", f->name));
    2694             :         }
    2695             : 
    2696           3 :         if (f->session->filters_mx) gf_mx_v(f->session->filters_mx);
    2697             : 
    2698           3 :         gf_mx_p(f->tasks_mx);
    2699             :         //detach all input pids
    2700           6 :         while (gf_list_count(f->input_pids)) {
    2701           0 :                 GF_FilterPidInst *pidinst = gf_list_pop_back(f->input_pids);
    2702           0 :                 pidinst->filter = NULL;
    2703             :         }
    2704             :         //detach all output pids
    2705           3 :         while (gf_list_count(f->output_pids)) {
    2706             :                 u32 j;
    2707           0 :                 GF_FilterPid *pid = gf_list_pop_back(f->output_pids);
    2708           0 :                 for (j=0; j<pid->num_destinations; j++) {
    2709           0 :                         GF_FilterPid *pidinst = gf_list_get(pid->destinations, j);
    2710           0 :                         pidinst->pid = NULL;
    2711             :                 }
    2712             :         }
    2713           3 :         gf_mx_v(f->tasks_mx);
    2714             : 
    2715           3 :         gf_filter_del(f);
    2716           3 : }
    2717             : 
    2718           9 : static void gf_filter_setup_failure_notify_task(GF_FSTask *task)
    2719             : {
    2720           9 :         struct _gf_filter_setup_failure *st = (struct _gf_filter_setup_failure *)task->udta;
    2721           9 :         if (st->notify_filter && st->filter->on_setup_error)
    2722           9 :                 st->filter->on_setup_error(st->filter, st->filter->on_setup_error_udta, st->e);
    2723             : 
    2724           9 :         if (st->do_disconnect) {
    2725           0 :                 gf_fs_post_task(st->filter->session, gf_filter_setup_failure_task, NULL, NULL, "setup_failure", st);
    2726             :         } else {
    2727           9 :                 gf_free(st);
    2728             :         }
    2729           9 : }
    2730             : 
    2731             : GF_EXPORT
    2732          13 : void gf_filter_notification_failure(GF_Filter *filter, GF_Err reason, Bool force_disconnect)
    2733             : {
    2734             :         struct _gf_filter_setup_failure *stack;
    2735          13 :         if (!filter->on_setup_error_filter && !force_disconnect) return;
    2736             : 
    2737          13 :         stack = gf_malloc(sizeof(struct _gf_filter_setup_failure));
    2738          13 :         stack->e = reason;
    2739          13 :         stack->notify_filter = filter->on_setup_error_filter;
    2740          13 :         stack->filter = filter;
    2741          13 :         stack->do_disconnect = force_disconnect;
    2742          13 :         if (force_disconnect) {
    2743           4 :                 filter->removed = GF_TRUE;
    2744             :         }
    2745          13 :         if (filter->on_setup_error_filter) {
    2746           9 :                 gf_fs_post_task(filter->session, gf_filter_setup_failure_notify_task, filter->on_setup_error_filter, NULL, "setup_failure_notify", stack);
    2747           4 :         } else if (force_disconnect) {
    2748           4 :                 gf_fs_post_task(filter->session, gf_filter_setup_failure_task, NULL, NULL, "setup_failure", stack);
    2749             :         }
    2750             : }
    2751             : 
    2752             : GF_EXPORT
    2753           4 : void gf_filter_setup_failure(GF_Filter *filter, GF_Err reason)
    2754             : {
    2755             : 
    2756           4 :         if (filter->in_connect_err) {
    2757           0 :                 filter->in_connect_err = reason;
    2758           0 :                 return;
    2759             :         }
    2760             : 
    2761             :         //filter was already connected, trigger removal of all pid instances
    2762           4 :         if (filter->num_input_pids) {
    2763           0 :                 gf_filter_reset_pending_packets(filter);
    2764           0 :                 filter->removed = GF_TRUE;
    2765           0 :                 gf_mx_p(filter->tasks_mx);
    2766           0 :                 while (filter->num_input_pids) {
    2767           0 :                         GF_FilterPidInst *pidinst = gf_list_get(filter->input_pids, 0);
    2768           0 :                         GF_Filter *sfilter = pidinst->pid->filter;
    2769           0 :                         gf_list_del_item(filter->input_pids, pidinst);
    2770           0 :                         pidinst->filter = NULL;
    2771           0 :                         filter->num_input_pids = gf_list_count(filter->input_pids);
    2772             : 
    2773             :                         //post a pid_delete task to also trigger removal of the filter if needed
    2774           0 :                         gf_fs_post_task(filter->session, gf_filter_pid_inst_delete_task, sfilter, pidinst->pid, "pid_inst_delete", pidinst);
    2775             :                 }
    2776           0 :                 gf_mx_v(filter->tasks_mx);
    2777             : 
    2778           0 :                 filter->session->last_connect_error = reason;
    2779           0 :                 return;
    2780             :         }
    2781             : 
    2782             :         //don't accept twice a notif
    2783           4 :         if (filter->setup_notified) return;
    2784           4 :         filter->setup_notified = GF_TRUE;
    2785             : 
    2786           4 :         gf_filter_notification_failure(filter, reason, GF_TRUE);
    2787             : }
    2788             : 
    2789             : #ifndef GPAC_DISABLE_LOG
    2790           0 : static Bool task_postponed_log(void *udta, void *item)
    2791             : {
    2792             :         GF_FSTask *at = (GF_FSTask *)item;
    2793           0 :         GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("\ttask %s\n", at->log_name));
    2794           0 :         return GF_FALSE;
    2795             : }
    2796             : #endif
    2797             : 
    2798         226 : void gf_filter_remove_task(GF_FSTask *task)
    2799             : {
    2800             :         s32 res;
    2801         226 :         GF_Filter *f = task->filter;
    2802         226 :         u32 count = gf_fq_count(f->tasks);
    2803             : 
    2804         226 :         if (f->out_pid_connection_pending || f->detach_pid_tasks_pending) {
    2805           0 :                 task->requeue_request = GF_TRUE;
    2806           0 :                 return;
    2807             :         }
    2808             : 
    2809             :         assert(f->finalized);
    2810             : 
    2811         226 :         if (count!=1) {
    2812           0 :                 task->requeue_request = GF_TRUE;
    2813           0 :                 task->can_swap = GF_TRUE;
    2814             : #ifndef GPAC_DISABLE_LOG
    2815           0 :                 if (gf_log_tool_level_on(GF_LOG_FILTER, GF_LOG_DEBUG) ) {
    2816           0 :                         gf_fq_enum(f->tasks, task_postponed_log, NULL);
    2817             :                 }
    2818             : #endif //GPAC_DISABLE_LOG
    2819             :                 return;
    2820             :         }
    2821         226 :         GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s destruction task\n", f->name));
    2822             : 
    2823             :         //avoid destruction of the task
    2824         226 :         gf_fq_pop(f->tasks);
    2825             : 
    2826         226 :         if (f->freg->finalize) {
    2827             :                 FSESS_CHECK_THREAD(f)
    2828         160 :                 f->freg->finalize(f);
    2829             :         }
    2830             : 
    2831         226 :         if (f->session->filters_mx) gf_mx_p(f->session->filters_mx);
    2832             : 
    2833         226 :         res = gf_list_del_item(f->session->filters, f);
    2834         226 :         if (res<0) {
    2835           0 :                 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Filter %s destruction task on already removed filter\n", f->name));
    2836             :         }
    2837             : 
    2838         226 :         if (f->session->filters_mx) gf_mx_v(f->session->filters_mx);
    2839             : 
    2840         226 :         gf_mx_p(f->tasks_mx);
    2841             :         //detach all input pids
    2842         452 :         while (gf_list_count(f->input_pids)) {
    2843           0 :                 GF_FilterPidInst *pidinst = gf_list_pop_back(f->input_pids);
    2844           0 :                 pidinst->filter = NULL;
    2845             :         }
    2846         226 :         gf_mx_v(f->tasks_mx);
    2847             : 
    2848         226 :         gf_filter_del(f);
    2849         226 :         task->filter = NULL;
    2850         226 :         task->requeue_request = GF_FALSE;
    2851             : }
    2852             : 
    2853         226 : void gf_filter_post_remove(GF_Filter *filter)
    2854             : {
    2855             :         assert(!filter->swap_pidinst_dst);
    2856             :         assert(!filter->swap_pidinst_src);
    2857             :         assert(!filter->finalized);
    2858         226 :         filter->finalized = GF_TRUE;
    2859         226 :         gf_fs_post_task(filter->session, gf_filter_remove_task, filter, NULL, "filter_destroy", NULL);
    2860         226 : }
    2861             : 
    2862        1201 : static void gf_filter_tag_remove(GF_Filter *filter, GF_Filter *source_filter, GF_Filter *until_filter, Bool keep_end_connections)
    2863             : {
    2864             :         u32 i, count, j, nb_inst;
    2865             :         u32 nb_rem_inst=0;
    2866             :         Bool mark_only = GF_FALSE;
    2867             :         Bool do_unlock;
    2868        1201 :         if (filter==until_filter) return;
    2869             : 
    2870             :         //we do a try-lock here, as the filter could be locked by another thread
    2871             :         //this typically happens upon compositor source disconnection while an event or packet drop is still being processed on that filter
    2872         673 :         do_unlock = gf_mx_try_lock(filter->tasks_mx);
    2873        1547 :         for (i=0; i<filter->num_input_pids; i++) {
    2874         874 :                 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
    2875         874 :                 if (pidi->pid->filter==source_filter) nb_rem_inst++;
    2876             :         }
    2877         673 :         if (!nb_rem_inst) {
    2878           0 :                 if (do_unlock) gf_mx_v(filter->tasks_mx);
    2879             :                 return;
    2880             :         }
    2881         673 :         filter->marked_for_removal = GF_TRUE;
    2882         673 :         if (nb_rem_inst != filter->num_input_pids)
    2883             :                 mark_only = GF_TRUE;
    2884             : 
    2885             :         //already removed
    2886         673 :         if (filter->removed) {
    2887          18 :                 if (do_unlock) gf_mx_v(filter->tasks_mx);
    2888             :                 return;
    2889             :         }
    2890             :         //filter will be removed, propagate on all output pids
    2891         655 :         if (!mark_only)
    2892         652 :                 filter->removed = GF_TRUE;
    2893             : 
    2894         655 :         count = gf_list_count(filter->output_pids);
    2895        1403 :         for (i=0; i<count; i++) {
    2896         748 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    2897         748 :                 pid->has_seen_eos = GF_TRUE;
    2898         748 :                 nb_inst = pid->num_destinations;
    2899        1495 :                 for (j=0; j<nb_inst; j++) {
    2900         747 :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    2901         747 :                         gf_filter_tag_remove(pidi->filter, filter, until_filter, keep_end_connections);
    2902         747 :                         if (!mark_only && (!keep_end_connections || (pidi->filter != until_filter)) ) {
    2903             :                                 //unlock filter before posting remove task on other filter
    2904         726 :                                 if (do_unlock) gf_mx_v(filter->tasks_mx);
    2905         726 :                                 gf_fs_post_task(filter->session, gf_filter_pid_disconnect_task, pidi->filter, pid, "pidinst_disconnect", NULL);
    2906         726 :                                 do_unlock = gf_mx_try_lock(filter->tasks_mx);
    2907             :                         }
    2908             :                 }
    2909             :         }
    2910         655 :         if (do_unlock) gf_mx_v(filter->tasks_mx);
    2911             : }
    2912             : 
    2913         777 : void gf_filter_remove_internal(GF_Filter *filter, GF_Filter *until_filter, Bool keep_end_connections)
    2914             : {
    2915             :         u32 i, j, count;
    2916             : 
    2917         777 :         if (!filter) return;
    2918             : 
    2919         503 :         if (filter->removed)
    2920             :                 return;
    2921             : 
    2922         482 :         if (filter==until_filter)
    2923             :                 return;
    2924             : 
    2925         482 :         if (until_filter) {
    2926             :                 //check if filter has not been removed
    2927         481 :                 s32 res = gf_list_find(until_filter->session->filters, filter);
    2928         481 :                 if (res<0)
    2929             :                         return;
    2930         481 :                 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Disconnecting filter %s up to %s\n", filter->name, until_filter->name));
    2931             :         } else {
    2932           1 :                 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Disconnecting filter %s from session\n", filter->name));
    2933             :         }
    2934             :         //get all dest pids, post disconnect and mark filters as removed
    2935             :         assert(!filter->removed);
    2936         482 :         filter->removed = GF_TRUE;
    2937         964 :         for (i=0; i<filter->num_output_pids; i++) {
    2938         482 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    2939         482 :                 count = pid->num_destinations;
    2940         937 :                 for (j=0; j<count; j++) {
    2941         455 :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    2942             : 
    2943         455 :                         if (until_filter) {
    2944         454 :                                 gf_filter_tag_remove(pidi->filter, filter, until_filter, keep_end_connections);
    2945             :                         }
    2946             : 
    2947         455 :                         if (keep_end_connections && (pidi->filter == until_filter)) {
    2948             : 
    2949             :                         } else {
    2950         453 :                                 gf_fs_post_task(filter->session, gf_filter_pid_disconnect_task, pidi->filter, pid, "pidinst_disconnect", NULL);
    2951             :                         }
    2952             :                 }
    2953             :         }
    2954         482 :         if (keep_end_connections) return;
    2955             : 
    2956         480 :         gf_mx_p(filter->tasks_mx);
    2957             :         //check all pids connected to this filter, ensure their owner is only connected to this filter
    2958         480 :         for (i=0; i<filter->num_input_pids; i++) {
    2959             :                 GF_FilterPid *pid;
    2960           0 :                 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
    2961             :                 Bool can_remove = GF_TRUE;
    2962             :                 //check all output pids of the filter owning this pid are connected to ourselves
    2963           0 :                 pid = pidi->pid;
    2964           0 :                 count = pid->num_destinations;
    2965           0 :                 for (j=0; j<count; j++) {
    2966           0 :                         GF_FilterPidInst *pidi_o = gf_list_get(pid->destinations, j);
    2967           0 :                         if (pidi_o->filter != filter) {
    2968             :                                 can_remove = GF_FALSE;
    2969             :                                 break;
    2970             :                         }
    2971             :                 }
    2972           0 :                 if (can_remove && !pid->filter->removed) {
    2973           0 :                         gf_filter_remove_internal(pid->filter, NULL, GF_FALSE);
    2974             :                 }
    2975             :         }
    2976         480 :         gf_mx_v(filter->tasks_mx);
    2977             : }
    2978             : 
    2979             : GF_EXPORT
    2980         774 : void gf_filter_remove_src(GF_Filter *filter, GF_Filter *src_filter)
    2981             : {
    2982         774 :         gf_filter_remove_internal(src_filter, filter, GF_FALSE);
    2983         774 : }
    2984             : 
    2985             : GF_EXPORT
    2986           2 : void gf_filter_remove(GF_Filter *filter)
    2987             : {
    2988             :         u32 i;
    2989           2 :         if (!filter) return;
    2990             : 
    2991             :         //locate source filter(s)
    2992           1 :         gf_mx_p(filter->tasks_mx);
    2993           2 :         for (i=0; i<filter->num_input_pids; i++) {
    2994           1 :                 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
    2995             :                 //fanout, only disconnect this pid instance
    2996           1 :                 if (pidi->pid->num_destinations>1) {
    2997             :                         //post disconnect
    2998           0 :                         gf_fs_post_task(filter->session, gf_filter_pid_disconnect_task, filter, pidi->pid, "pidinst_disconnect", NULL);
    2999             :                 }
    3000             :                 //this is a source for the chain
    3001           1 :                 else if (!pidi->pid->filter->num_input_pids) {
    3002           1 :                         gf_filter_remove_internal(pidi->pid->filter, NULL, GF_FALSE);
    3003             :                 }
    3004             :                 //otherwise walk down the chain
    3005             :                 else {
    3006           0 :                         gf_filter_remove(pidi->pid->filter);
    3007             :                 }
    3008             :         }
    3009           1 :         gf_mx_v(filter->tasks_mx);
    3010             : }
    3011             : 
    3012             : #if 0
    3013             : GF_EXPORT
    3014             : void gf_filter_remove_dst(GF_Filter *filter, GF_Filter *dst_filter)
    3015             : {
    3016             :         u32 i, j;
    3017             :         Bool removed;
    3018             :         if (!filter) return;
    3019             :         removed = filter->removed;
    3020             :         filter->removed = GF_TRUE;
    3021             :         gf_filter_remove_internal(dst_filter, filter, GF_FALSE);
    3022             :         filter->removed = removed;
    3023             : 
    3024             :         for (i=0; i<filter->num_output_pids; i++) {
    3025             :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    3026             :                 for (j=0; j<pid->num_destinations; j++) {
    3027             :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    3028             :                         if (pidi->filter->removed) {
    3029             :                                 gf_fs_post_task(pidi->filter->session, gf_filter_pid_disconnect_task, pidi->filter, pidi->pid, "pidinst_disconnect", NULL);
    3030             :                         }
    3031             :                 }
    3032             :         }
    3033             : }
    3034             : #endif
    3035             : 
    3036           3 : Bool gf_filter_swap_source_register(GF_Filter *filter)
    3037             : {
    3038             :         u32 i;
    3039             :         char *src_url=NULL;
    3040             :         GF_Filter *target_filter=NULL;
    3041             :         GF_Err e;
    3042             :         const GF_FilterArgs *src_arg=NULL;
    3043             : 
    3044           9 :         while (gf_list_count(filter->postponed_packets)) {
    3045           3 :                 GF_FilterPacket *pck = gf_list_pop_front(filter->postponed_packets);
    3046           3 :                 gf_filter_packet_destroy(pck);
    3047             :         }
    3048             : 
    3049           6 :         while (gf_list_count(filter->output_pids)) {
    3050           3 :                 GF_FilterPid *pid = gf_list_pop_back(filter->output_pids);
    3051           3 :                 pid->destroyed = GF_TRUE;
    3052           3 :                 gf_fs_post_task(filter->session, gf_filter_pid_del_task, filter, pid, "pid_delete", NULL);
    3053             :         }
    3054           3 :         gf_mx_p(filter->tasks_mx);
    3055           3 :         filter->num_output_pids = 0;
    3056           3 :         gf_mx_v(filter->tasks_mx);
    3057             : 
    3058           3 :         if (filter->freg->finalize) {
    3059             :                 FSESS_CHECK_THREAD(filter)
    3060           3 :                 filter->freg->finalize(filter);
    3061           3 :                 filter->finalized = GF_TRUE;
    3062             :         }
    3063           3 :         gf_list_add(filter->blacklisted, (void *)filter->freg);
    3064             : 
    3065             :         i=0;
    3066           6 :         while (filter->freg->args) {
    3067           3 :                 src_arg = &filter->freg->args[i];
    3068           3 :                 if (!src_arg || !src_arg->arg_name) {
    3069             :                         src_arg=NULL;
    3070             :                         break;
    3071             :                 }
    3072           3 :                 i++;
    3073           3 :                 if (strcmp(src_arg->arg_name, "src")) continue;
    3074             :                 //found it, get the url
    3075           3 :                 if (src_arg->offset_in_private<0) continue;
    3076             : 
    3077             : #ifdef WIN32
    3078             :                 src_url = *(char **)( ((char *)filter->filter_udta) + src_arg->offset_in_private);
    3079             :                 *(char **)(((char *)filter->filter_udta) + src_arg->offset_in_private) = NULL;
    3080             : #else
    3081           3 :                 src_url = *(char **) (filter->filter_udta + src_arg->offset_in_private);
    3082           3 :                 *(char **)(filter->filter_udta + src_arg->offset_in_private) = NULL;
    3083             : #endif
    3084           3 :                  break;
    3085             :         }
    3086             : 
    3087           3 :         gf_free(filter->filter_udta);
    3088           3 :         filter->filter_udta = NULL;
    3089           3 :         if (!src_url) return GF_FALSE;
    3090           3 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Swaping source filter for URL %s\n", src_url));
    3091             : 
    3092           3 :         target_filter = filter->target_filter;
    3093           3 :         filter->finalized = GF_FALSE;
    3094           3 :         gf_fs_load_source_dest_internal(filter->session, src_url, NULL, NULL, &e, filter, filter->target_filter ? filter->target_filter : filter->dst_filter, GF_TRUE, filter->no_dst_arg_inherit, NULL);
    3095             :         //we manage to reassign an input registry
    3096           3 :         if (e==GF_OK) {
    3097           3 :                 gf_free(src_url);
    3098           3 :                 if (target_filter) filter->dst_filter = NULL;
    3099             :                 return GF_TRUE;
    3100             :         }
    3101           0 :         if (!filter->finalized) {
    3102           0 :                 gf_free(src_url);
    3103           0 :                 return gf_filter_swap_source_register(filter);
    3104             :         }
    3105             : 
    3106           0 :         for (i=0; i<gf_list_count(filter->destination_links); i++) {
    3107           0 :                 GF_Filter *af = gf_list_get(filter->destination_links, i);
    3108           0 :                 gf_mx_p(af->tasks_mx);
    3109           0 :                 if (af->num_input_pids) {
    3110             :                         u32 j;
    3111           0 :                         for (j=0; j<af->num_input_pids; j++) {
    3112           0 :                                 GF_FilterPidInst *pidi = gf_list_get(af->input_pids, j);
    3113           0 :                                 pidi->is_end_of_stream = GF_TRUE;
    3114             :                         }
    3115             :                 }
    3116           0 :                 gf_mx_v(af->tasks_mx);
    3117           0 :                 if (af->sticky) {
    3118           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to find any filter for URL %s\n", src_url));
    3119             :                 } else {
    3120           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to find any filter for URL %s, disabling destination filter %s\n", src_url, af->name));
    3121           0 :                         af->removed = GF_TRUE;
    3122             :                 }
    3123             :         }
    3124           0 :         if (e==GF_NOT_SUPPORTED)
    3125           0 :                 e = GF_FILTER_NOT_FOUND;
    3126             :         //nope ...
    3127           0 :         gf_filter_setup_failure(filter, e);
    3128           0 :         gf_free(src_url);
    3129           0 :         return GF_FALSE;
    3130             : }
    3131             : 
    3132     4389437 : void gf_filter_forward_clock(GF_Filter *filter)
    3133             : {
    3134             :         u32 i;
    3135             :         u64 clock_val;
    3136     4389437 :         if (!filter->next_clock_dispatch_type) return;
    3137       78428 :         if (!filter->num_output_pids) return;
    3138             : 
    3139       43096 :         for (i=0; i<filter->num_output_pids; i++) {
    3140             :                 GF_FilterPacket *pck;
    3141             :                 Bool req_props_map, info_modified;
    3142       43096 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    3143             :                 GF_PropertyMap *map;
    3144             : 
    3145             :                 //see \ref gf_filter_pid_merge_properties_internal for mutex
    3146       43096 :                 gf_mx_p(pid->filter->tasks_mx);
    3147       43096 :                 map = gf_list_last(pid->properties);
    3148       43096 :                 gf_mx_v(pid->filter->tasks_mx);
    3149             : 
    3150       43096 :                 clock_val = filter->next_clock_dispatch;
    3151       43096 :                 if (map->timescale != filter->next_clock_dispatch_timescale) {
    3152       13697 :                         clock_val *= map->timescale;
    3153       13697 :                         clock_val /= filter->next_clock_dispatch_timescale;
    3154             :                 }
    3155       43096 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s internal forward of clock reference\n", pid->filter->name, pid->name));
    3156       43096 :                 pck = gf_filter_pck_new_shared((GF_FilterPid *)pid, NULL, 0, NULL);
    3157       43096 :                 if (!pck) {
    3158           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Filter %s PID %s failted to allocate packet for clock reference forward\n", pid->filter->name, pid->name));
    3159           0 :                         continue;
    3160             :                 }
    3161       43096 :                 gf_filter_pck_set_cts(pck, clock_val);
    3162       43096 :                 gf_filter_pck_set_clock_type(pck, filter->next_clock_dispatch_type);
    3163             : 
    3164             :                 //do not let the clock packet carry the props/info change flags since it is an internal
    3165             :                 //packet discarded before processing these flags
    3166       43096 :                 req_props_map = pid->request_property_map;
    3167       43096 :                 pid->request_property_map = GF_TRUE;
    3168       43096 :                 info_modified = pid->pid_info_changed;
    3169       43096 :                 pid->pid_info_changed = GF_FALSE;
    3170             : 
    3171       43096 :                 gf_filter_pck_send(pck);
    3172       43096 :                 pid->request_property_map = req_props_map;
    3173       43096 :                 pid->pid_info_changed = info_modified;
    3174             :         }
    3175       35320 :         filter->next_clock_dispatch_type = 0;
    3176             : }
    3177             : 
    3178             : GF_EXPORT
    3179           1 : Bool gf_filter_is_supported_source(GF_Filter *filter, const char *url, const char *parent_url)
    3180             : {
    3181             :         GF_Err e;
    3182           1 :         Bool is_supported = GF_FALSE;
    3183           1 :         gf_fs_load_source_dest_internal(filter->session, url, NULL, parent_url, &e, NULL, filter, GF_TRUE, GF_TRUE, &is_supported);
    3184           1 :         return is_supported;
    3185             : }
    3186             : 
    3187             : GF_EXPORT
    3188         499 : GF_Filter *gf_filter_connect_source(GF_Filter *filter, const char *url, const char *parent_url, Bool inherit_args, GF_Err *err)
    3189             : {
    3190             :         GF_Filter *filter_src;
    3191             :         const char *args;
    3192         499 :         char *full_args = NULL;
    3193         499 :         if (!filter) {
    3194           0 :                 if (err) *err = GF_BAD_PARAM;
    3195             :                 return NULL;
    3196             :         }
    3197         499 :         args = inherit_args ? gf_filter_get_dst_args(filter) : NULL;
    3198          22 :         if (args) {
    3199             :                 char szSep[10];
    3200             :                 char *loc_args;
    3201          14 :                 u32 len = (u32) strlen(args);
    3202          14 :                 sprintf(szSep, "%cgfloc%c", filter->session->sep_args, filter->session->sep_args);
    3203          14 :                 loc_args = strstr(args, szSep);
    3204          14 :                 if (loc_args) {
    3205           0 :                         len = (u32) (ptrdiff_t) (loc_args - args);
    3206             :                 }
    3207          14 :                 if (len) {
    3208          14 :                         gf_dynstrcat(&full_args, url, NULL);
    3209          14 :                         sprintf(szSep, "%cgpac%c", filter->session->sep_args, filter->session->sep_args);
    3210          14 :                         if ((filter->session->sep_args==':') && strstr(url, "://") && !strstr(url, szSep)) {
    3211           0 :                                 gf_dynstrcat(&full_args, szSep, NULL);
    3212             :                         } else {
    3213          14 :                                 sprintf(szSep, "%c", filter->session->sep_args);
    3214          14 :                                 gf_dynstrcat(&full_args, szSep, NULL);
    3215             :                         }
    3216          14 :                         gf_dynstrcat(&full_args, args, NULL);
    3217          14 :                         sprintf(szSep, "%cgfloc%c", filter->session->sep_args, filter->session->sep_args);
    3218          14 :                         loc_args = strstr(full_args, "gfloc");
    3219          14 :                         if (loc_args) loc_args[0] = 0;
    3220             : 
    3221          14 :                         url = full_args;
    3222             :                 }
    3223             :         }
    3224             : 
    3225         499 :         filter_src = gf_fs_load_source_dest_internal(filter->session, url, NULL, parent_url, err, NULL, filter, GF_TRUE, GF_TRUE, NULL);
    3226         499 :         if (full_args) gf_free(full_args);
    3227             : 
    3228         499 :         if (!filter_src) return NULL;
    3229             : 
    3230         496 :         gf_mx_p(filter->tasks_mx);
    3231         496 :         if (!filter->source_filters)
    3232         312 :                 filter->source_filters = gf_list_new();
    3233         496 :         gf_list_add(filter->source_filters, filter_src);
    3234         496 :         gf_mx_v(filter->tasks_mx);
    3235         496 :         return filter_src;
    3236             : }
    3237             : 
    3238             : GF_EXPORT
    3239         347 : GF_Filter *gf_filter_connect_destination(GF_Filter *filter, const char *url, GF_Err *err)
    3240             : {
    3241         347 :         return gf_fs_load_source_dest_internal(filter->session, url, NULL, NULL, err, NULL, filter, GF_FALSE, GF_FALSE, NULL);
    3242             : }
    3243             : 
    3244             : 
    3245             : GF_EXPORT
    3246        2528 : void gf_filter_get_output_buffer_max(GF_Filter *filter, u32 *max_buf, u32 *max_playout_buf)
    3247             : {
    3248             :         u32 i;
    3249             :         u32 buf_max = 0;
    3250             :         u32 buf_play_max = 0;
    3251        5005 :         for (i=0; i<filter->num_output_pids; i++) {
    3252             :                 u32 j;
    3253        2477 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    3254        2477 :                 if (buf_max < pid->user_max_buffer_time) buf_max = (u32) pid->user_max_buffer_time;
    3255        2477 :                 if (buf_max < pid->max_buffer_time) buf_max = (u32) pid->max_buffer_time;
    3256             : 
    3257        2477 :                 if (buf_play_max < pid->user_max_playout_time) buf_play_max = pid->user_max_playout_time;
    3258        2477 :                 if (buf_play_max < pid->max_buffer_time) buf_play_max = (u32) pid->max_buffer_time;
    3259             : 
    3260        2267 :                 for (j=0; j<pid->num_destinations; j++) {
    3261             :                         u32 mb, pb;
    3262        2267 :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    3263        2267 :                         gf_filter_get_output_buffer_max(pidi->filter, &mb, &pb);
    3264        2267 :                         if (buf_max < mb) buf_max = mb;
    3265        2267 :                         if (buf_play_max < pb) buf_play_max = pb;
    3266             :                 }
    3267             :         }
    3268        2528 :         if (max_buf) *max_buf = buf_max;
    3269        2528 :         if (max_playout_buf) *max_playout_buf = buf_play_max;
    3270        2528 :         return;
    3271             : }
    3272             : 
    3273             : GF_EXPORT
    3274          35 : void gf_filter_make_sticky(GF_Filter *filter)
    3275             : {
    3276          35 :         if (filter) filter->sticky = 1;
    3277          35 : }
    3278             : 
    3279           1 : static u32 gf_filter_get_num_events_queued_internal(GF_Filter *filter)
    3280             : {
    3281             :         u32 i;
    3282             :         u32 nb_events = 0;
    3283           1 :         if (!filter) return 0;
    3284           1 :         nb_events = filter->num_events_queued;
    3285             : 
    3286           2 :         for (i=0; i<filter->num_output_pids; i++) {
    3287             :                 u32 k;
    3288           1 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    3289           1 :                 for (k=0; k<pid->num_destinations; k++) {
    3290           0 :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, k);
    3291           0 :                         nb_events += gf_filter_get_num_events_queued(pidi->filter);
    3292             :                 }
    3293             :         }
    3294             :         return nb_events;
    3295             : }
    3296             : GF_EXPORT
    3297           2 : u32 gf_filter_get_num_events_queued(GF_Filter *filter)
    3298             : {
    3299             :         u32 res;
    3300             :         GF_FilterSession *fsess;
    3301           2 :         if (!filter) return 0;
    3302           1 :         fsess = filter->session;
    3303             : 
    3304           1 :         if (fsess->filters_mx)
    3305           0 :                 gf_mx_p(fsess->filters_mx);
    3306             : 
    3307           1 :         res = gf_filter_get_num_events_queued_internal(filter);
    3308           1 :         if (fsess->filters_mx)
    3309           0 :                 gf_mx_v(fsess->filters_mx);
    3310             : 
    3311             :         return res;
    3312             : }
    3313             : 
    3314             : GF_EXPORT
    3315          25 : void gf_filter_hint_single_clock(GF_Filter *filter, u64 time_in_us, GF_Fraction64 media_timestamp)
    3316             : {
    3317             :         //for now only one clock hint possible ...
    3318          25 :         filter->session->hint_clock_us = time_in_us;
    3319          25 :         filter->session->hint_timestamp = media_timestamp;
    3320          25 : }
    3321             : 
    3322             : GF_EXPORT
    3323       34597 : void gf_filter_get_clock_hint(GF_Filter *filter, u64 *time_in_us, GF_Fraction64 *media_timestamp)
    3324             : {
    3325             :         //for now only one clock hint possible ...
    3326       34597 :         if (time_in_us) *time_in_us = filter->session->hint_clock_us;
    3327       34597 :         if (media_timestamp) *media_timestamp = filter->session->hint_timestamp;
    3328       34597 : }
    3329             : 
    3330             : GF_EXPORT
    3331        1647 : GF_Err gf_filter_assign_id(GF_Filter *filter, const char *id)
    3332             : {
    3333        1647 :         if (!filter || filter->id) return GF_BAD_PARAM;
    3334             : 
    3335        1647 :         if (!id) {
    3336             :                 char szID[1024];
    3337             :                 sprintf(szID, "_%p_", filter);
    3338        1647 :                 filter->id = gf_strdup(szID);
    3339             :         } else {
    3340           0 :                 filter->id = gf_strdup(id);
    3341             :         }
    3342             :         return GF_OK;
    3343             : }
    3344             : 
    3345             : GF_EXPORT
    3346          14 : const char *gf_filter_get_id(GF_Filter *filter)
    3347             : {
    3348          14 :         if (filter) return filter->id;
    3349             :         return NULL;
    3350             : }
    3351             : 
    3352             : GF_EXPORT
    3353        2051 : GF_Err gf_filter_set_source(GF_Filter *filter, GF_Filter *link_from, const char *link_ext)
    3354             : {
    3355        2051 :         if (!filter || !link_from) return GF_BAD_PARAM;
    3356        2051 :         if (filter == link_from) return GF_OK;
    3357             :         //don't allow loops
    3358        2051 :         if (gf_filter_in_parent_chain(filter, link_from)) return GF_BAD_PARAM;
    3359             : 
    3360        2051 :         if (!link_from->id) {
    3361        1645 :                 gf_filter_assign_id(link_from, NULL);
    3362             :         }
    3363        2051 :         if (link_ext) {
    3364             :                 char szSep[2];
    3365         439 :                 char *id = NULL;
    3366         439 :                 gf_dynstrcat(&id, link_from->id, NULL);
    3367         439 :                 szSep[0] = link_from->session->sep_frag;
    3368         439 :                 szSep[1] = 0;
    3369         439 :                 gf_dynstrcat(&id, link_ext, szSep);
    3370         439 :                 gf_filter_set_sources(filter, id);
    3371         439 :                 gf_free(id);
    3372             :         } else {
    3373        1612 :                 gf_filter_set_sources(filter, link_from->id);
    3374             :         }
    3375        2051 :         if (link_from->target_filter != filter) {
    3376        1780 :                 filter->target_filter = link_from->target_filter;
    3377        1780 :                 link_from->target_filter = NULL;
    3378             :         }
    3379             :         return GF_OK;
    3380             : }
    3381             : 
    3382             : GF_EXPORT
    3383         271 : GF_Err gf_filter_set_source_restricted(GF_Filter *filter, GF_Filter *link_from, const char *link_ext)
    3384             : {
    3385         271 :         GF_Err e =  gf_filter_set_source(filter, link_from, link_ext);
    3386         271 :         if (e) return e;
    3387         271 :         if (link_from->restricted_source_id)
    3388           0 :                 gf_free(link_from->restricted_source_id);
    3389             : 
    3390         271 :         link_from->restricted_source_id = gf_strdup(link_from->id);
    3391         271 :         return GF_OK;
    3392             : }
    3393             : 
    3394             : 
    3395             : GF_EXPORT
    3396        2718 : GF_Err gf_filter_override_caps(GF_Filter *filter, const GF_FilterCapability *caps, u32 nb_caps )
    3397             : {
    3398        2718 :         if (!filter) return GF_BAD_PARAM;
    3399             :         //we accept caps override event on a running filter, this will only impact the next link solving
    3400        2718 :         filter->forced_caps = nb_caps ? caps : NULL;
    3401        2718 :         filter->nb_forced_caps = nb_caps;
    3402        2718 :         filter->bundle_idx_at_resolution = -1;
    3403        2718 :         return GF_OK;
    3404             : }
    3405             : 
    3406             : GF_EXPORT
    3407         611 : GF_Err gf_filter_act_as_sink(GF_Filter *filter)
    3408             : {
    3409         611 :         if (!filter) return GF_BAD_PARAM;
    3410         611 :         filter->act_as_sink = GF_TRUE;
    3411         611 :         return GF_OK;
    3412             : }
    3413             : 
    3414             : 
    3415             : GF_EXPORT
    3416        3292 : void gf_filter_pid_init_play_event(GF_FilterPid *pid, GF_FilterEvent *evt, Double start, Double speed, const char *log_name)
    3417             : {
    3418             :         u32 pmode = GF_PLAYBACK_MODE_NONE;
    3419             :         const GF_PropertyValue *p;
    3420             :         Bool was_end = GF_FALSE;
    3421             :         memset(evt, 0, sizeof(GF_FilterEvent));
    3422        3292 :         evt->base.type = GF_FEVT_PLAY;
    3423        3292 :         evt->base.on_pid = pid;
    3424             : 
    3425        3292 :         evt->play.speed = 1.0;
    3426             : 
    3427        3292 :         if ((speed<0) && !start) start = -1.0;
    3428             : 
    3429        3292 :         p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_PLAYBACK_MODE);
    3430        3292 :         if (p) pmode = p->value.uint;
    3431             : 
    3432        3292 :         evt->play.start_range = start;
    3433        3292 :         if (start<0) {
    3434             :                 was_end = GF_TRUE;
    3435           6 :                 p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_DURATION);
    3436           6 :                 if (p && p->value.lfrac.den) {
    3437           6 :                         evt->play.start_range *= -100;
    3438           6 :                         evt->play.start_range *= p->value.lfrac.num;
    3439           6 :                         evt->play.start_range /= 100 * p->value.lfrac.den;
    3440             :                 }
    3441             :         }
    3442        3292 :         switch (pmode) {
    3443        1135 :         case GF_PLAYBACK_MODE_NONE:
    3444        1135 :                 evt->play.start_range = 0;
    3445        1135 :                 if (start) {
    3446           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Media PID does not support seek, ignoring start directive\n", log_name));
    3447             :                 }
    3448             :                 break;
    3449           6 :         case GF_PLAYBACK_MODE_SEEK:
    3450           6 :                 if (speed != 1.0) {
    3451           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Media PID does not support speed, ignoring speed directive\n", log_name));
    3452             :                 }
    3453             :                 break;
    3454         817 :         case GF_PLAYBACK_MODE_FASTFORWARD:
    3455         817 :                 if (speed<0) {
    3456           0 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Media PID does not support negative speed, ignoring speed directive\n", log_name));
    3457           0 :                         if (was_end) evt->play.start_range = 0;
    3458             :                 } else {
    3459         817 :                         evt->play.speed = speed;
    3460             :                 }
    3461             :                 break;
    3462        1334 :         default:
    3463        1334 :                 evt->play.speed = speed;
    3464        1334 :                 break;
    3465             :         }
    3466        3292 : }
    3467             : 
    3468             : GF_EXPORT
    3469        1795 : void gf_filter_set_max_extra_input_pids(GF_Filter *filter, u32 max_extra_pids)
    3470             : {
    3471        1795 :         if (filter) filter->max_extra_pids = max_extra_pids;
    3472        1795 : }
    3473             : GF_EXPORT
    3474           1 : u32 gf_filter_get_max_extra_input_pids(GF_Filter *filter)
    3475             : {
    3476           1 :         if (filter) return filter->max_extra_pids;
    3477             :         return 0;
    3478             : }
    3479             : GF_EXPORT
    3480          38 : Bool gf_filter_block_enabled(GF_Filter *filter)
    3481             : {
    3482          38 :         if (!filter) return GF_FALSE;
    3483          38 :         return (filter->session->blocking_mode==GF_FS_NOBLOCK) ? GF_FALSE : GF_TRUE;
    3484             : }
    3485             : 
    3486             : GF_EXPORT
    3487        6001 : GF_Err gf_filter_pid_raw_new(GF_Filter *filter, const char *url, const char *local_file, const char *mime_type, const char *fext, u8 *probe_data, u32 probe_size, Bool trust_mime, GF_FilterPid **out_pid)
    3488             : {
    3489             :         char tmp_ext[50];
    3490             :         u32 ext_len=0;
    3491             :         Bool ext_not_trusted, is_new_pid = GF_FALSE;
    3492        6001 :         GF_FilterPid *pid = *out_pid;
    3493        6001 :         if (!pid) {
    3494        2924 :                 pid = gf_filter_pid_new(filter);
    3495        2924 :                 if (!pid) {
    3496             :                         return GF_OUT_OF_MEM;
    3497             :                 }
    3498        2924 :                 pid->max_buffer_unit = 1;
    3499             :                 is_new_pid = GF_TRUE;
    3500        2924 :                 *out_pid = pid;
    3501             :         }
    3502             : 
    3503        6001 :         gf_filter_pid_set_property(pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
    3504             : 
    3505        6001 :         if (local_file)
    3506        5978 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_FILEPATH, &PROP_STRING(local_file));
    3507             : 
    3508             : 
    3509        6001 :         if (url) {
    3510        6001 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_URL, &PROP_STRING(url));
    3511             : 
    3512        6001 :                 if (!strnicmp(url, "isobmff://", 10)) {
    3513           6 :                         gf_filter_pid_set_name(pid, "isobmff://");
    3514             :                         fext = "mp4";
    3515             :                         mime_type = "video/mp4";
    3516           6 :                         if (is_new_pid)
    3517           5 :                                 gf_filter_pid_set_eos(pid);
    3518             :                 } else {
    3519        5995 :                         char *sep = gf_file_basename(url);
    3520             : 
    3521        5995 :                         gf_filter_pid_set_name(pid, sep);
    3522             :                 }
    3523             : 
    3524        6001 :                 if (fext) {
    3525             :                         strncpy(tmp_ext, fext, 20);
    3526          28 :                         tmp_ext[20] = 0;
    3527          28 :                         strlwr(tmp_ext);
    3528          28 :                         gf_filter_pid_set_property(pid, GF_PROP_PID_FILE_EXT, &PROP_STRING(tmp_ext));
    3529          28 :                         ext_len = (u32) strlen(tmp_ext);
    3530             :                 } else {
    3531        5973 :                         char *ext = gf_file_ext_start(url);
    3532        5973 :                         if (ext) ext++;
    3533             : 
    3534        5973 :                         if (ext) {
    3535        5944 :                                 char *s = strchr(ext, '#');
    3536        5944 :                                 if (s) s[0] = 0;
    3537             : 
    3538             :                                 strncpy(tmp_ext, ext, 20);
    3539        5944 :                                 tmp_ext[20] = 0;
    3540        5944 :                                 strlwr(tmp_ext);
    3541        5944 :                                 gf_filter_pid_set_property(pid, GF_PROP_PID_FILE_EXT, &PROP_STRING(tmp_ext));
    3542        5944 :                                 ext_len = (u32) strlen(tmp_ext);
    3543        5944 :                                 if (s) s[0] = '#';
    3544             :                         }
    3545             :                 }
    3546             :         }
    3547             : 
    3548             :         ext_not_trusted = GF_FALSE;
    3549             :         //probe data
    3550        6001 :         if ((!mime_type || !trust_mime) && !filter->no_probe && is_new_pid && probe_data && probe_size && !(filter->session->flags & GF_FS_FLAG_NO_PROBE)) {
    3551             :                 u32 i, count;
    3552             :                 GF_FilterProbeScore score, max_score = GF_FPROBE_NOT_SUPPORTED;
    3553             :                 const char *probe_mime = NULL;
    3554        2912 :                 gf_mx_p(filter->session->filters_mx);
    3555        2912 :                 count = gf_list_count(filter->session->registry);
    3556      305124 :                 for (i=0; i<count; i++) {
    3557             :                         const char *a_mime;
    3558      302221 :                         const GF_FilterRegister *freg = gf_list_get(filter->session->registry, i);
    3559      302221 :                         if (!freg || !freg->probe_data) continue;
    3560       81423 :                         score = GF_FPROBE_NOT_SUPPORTED;
    3561       81423 :                         a_mime = freg->probe_data(probe_data, probe_size, &score);
    3562       81423 :                         if (score==GF_FPROBE_NOT_SUPPORTED) {
    3563             :                                 u32 k;
    3564      813014 :                                 for (k=0;k<freg->nb_caps && !ext_not_trusted && ext_len; k++) {
    3565             :                                         const char *value;
    3566      813014 :                                         const GF_FilterCapability *cap = &freg->caps[k];
    3567      813014 :                                         if (!(cap->flags & GF_CAPFLAG_IN_BUNDLE)) continue;
    3568      771161 :                                         if (!(cap->flags & GF_CAPFLAG_INPUT)) continue;
    3569      386359 :                                         if (cap->code != GF_PROP_PID_FILE_EXT) continue;
    3570       69000 :                                         value = cap->val.value.string;
    3571      138018 :                                         while (value && ext_len) {
    3572       69018 :                                                 const char *match = strstr(value, tmp_ext);
    3573       69018 :                                                 if (!match) break;
    3574          70 :                                                 if (!match[ext_len] || (match[ext_len]=='|')) {
    3575             :                                                         ext_not_trusted = GF_TRUE;
    3576             :                                                         break;
    3577             :                                                 }
    3578             :                                                 value = match+ext_len;
    3579             :                                         }
    3580             :                                 }
    3581        8449 :                         } else if (score==GF_FPROBE_EXT_MATCH) {
    3582        5822 :                                 if (a_mime && ext_len && strstr(a_mime, tmp_ext)) {
    3583             :                                         ext_not_trusted = GF_FALSE;
    3584             :                                         probe_mime = NULL;
    3585             :                                         break;
    3586             :                                 }
    3587             :                         } else {
    3588        2627 :                                 if (a_mime) {
    3589        2627 :                                         GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Data Prober (filter %s) detected format is%s mime %s\n", freg->name, (score==GF_FPROBE_MAYBE_SUPPORTED) ? " maybe" : "", a_mime));
    3590             :                                 }
    3591        2627 :                                 if (a_mime && (score > max_score)) {
    3592             :                                         probe_mime = a_mime;
    3593             :                                         max_score = score;
    3594             :                                 }
    3595             :                         }
    3596             :                 }
    3597        2912 :                 gf_mx_v(filter->session->filters_mx);
    3598             : 
    3599        2912 :                 pid->ext_not_trusted = ext_not_trusted;
    3600             : 
    3601        2912 :                 if (probe_mime) {
    3602             :                         mime_type = probe_mime;
    3603             :                 }
    3604             :         }
    3605             :         //we ignore mime type on pid reconfigure - some server will overwrite valid type to application/octet-string or binary/octet-string
    3606             :         //on a byte-range request or secondary fetch
    3607             :         //this is safe for now as the only reconfig we do is in HTTP streaming context
    3608             :         //we furthermore blacklist *octet-* mimes
    3609        6001 :         if (mime_type && is_new_pid && !strstr(mime_type, "/octet-")) {
    3610             :                 strncpy(tmp_ext, mime_type, 50);
    3611        2572 :                 tmp_ext[49] = 0;
    3612        2572 :                 strlwr(tmp_ext);
    3613        2572 :                 gf_filter_pid_set_property(pid, GF_PROP_PID_MIME, &PROP_STRING( tmp_ext ));
    3614             :                 //we have a mime, disable extension checking
    3615        2572 :                 pid->ext_not_trusted = GF_TRUE;
    3616             :         }
    3617             : 
    3618             :         return GF_OK;
    3619             : }
    3620             : 
    3621             : GF_EXPORT
    3622         162 : const char *gf_filter_probe_data(GF_Filter *filter, u8 *data, u32 size)
    3623             : {
    3624             :         u32 i, count;
    3625             :         GF_FilterProbeScore score, max_score = GF_FPROBE_NOT_SUPPORTED;
    3626             :         const char *probe_mime = NULL;
    3627         162 :         if (!size) return NULL;
    3628         162 :         gf_mx_p(filter->session->filters_mx);
    3629         162 :         count = gf_list_count(filter->session->registry);
    3630       17010 :         for (i=0; i<count; i++) {
    3631             :                 const char *a_mime;
    3632       16848 :                 const GF_FilterRegister *freg = gf_list_get(filter->session->registry, i);
    3633       16848 :                 if (!freg || !freg->probe_data) continue;
    3634        4536 :                 score = GF_FPROBE_NOT_SUPPORTED;
    3635        4536 :                 a_mime = freg->probe_data(data, size, &score);
    3636        4536 :                 if (score==GF_FPROBE_NOT_SUPPORTED) {
    3637         587 :                 } else if (score==GF_FPROBE_EXT_MATCH) {
    3638             :                         probe_mime = NULL;
    3639             :                 } else {
    3640         263 :                         if (a_mime && (score > max_score)) {
    3641             :                                 probe_mime = a_mime;
    3642             :                                 max_score = score;
    3643             :                         }
    3644             :                 }
    3645             :         }
    3646         162 :         gf_mx_v(filter->session->filters_mx);
    3647         162 :         return probe_mime;
    3648             : }
    3649             : 
    3650         771 : static Bool gf_filter_get_arg_internal(GF_Filter *filter, const char *arg_name, GF_PropertyValue *prop, const char **min_max_enum)
    3651             : {
    3652             :         u32 i=0;
    3653         771 :         if (!filter || !arg_name) return GF_FALSE;
    3654             : 
    3655       47194 :         while (1) {
    3656             :                 GF_PropertyValue p;
    3657       47965 :                 const GF_FilterArgs *arg = &filter->freg->args[i];
    3658       47965 :                 if (!arg || !arg->arg_name) break;
    3659       47965 :                 i++;
    3660             : 
    3661       95159 :                 if (strcmp(arg->arg_name, arg_name)) continue;
    3662         771 :                 if (arg->offset_in_private < 0) continue;
    3663             : 
    3664         771 :                 p.type = arg->arg_type;
    3665         771 :                 switch (arg->arg_type) {
    3666         104 :                 case GF_PROP_BOOL:
    3667         104 :                         p.value.boolean = * (Bool *) ((char *)filter->filter_udta + arg->offset_in_private);
    3668         104 :                         break;
    3669         605 :                 case GF_PROP_UINT:
    3670             :                 case GF_PROP_4CC:
    3671         605 :                         p.value.uint = * (u32 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3672         605 :                         break;
    3673           0 :                 case GF_PROP_SINT:
    3674           0 :                         p.value.sint = * (s32 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3675           0 :                         break;
    3676           0 :                 case GF_PROP_LUINT:
    3677           0 :                         p.value.longuint = * (u64 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3678           0 :                         break;
    3679           0 :                 case GF_PROP_LSINT:
    3680           0 :                         p.value.longsint = * (s64 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3681           0 :                         break;
    3682           0 :                 case GF_PROP_FLOAT:
    3683           0 :                         p.value.fnumber = * (Fixed *) ((char *)filter->filter_udta + arg->offset_in_private);
    3684           0 :                         break;
    3685          36 :                 case GF_PROP_DOUBLE:
    3686          36 :                         p.value.number = * (Double *) ((char *)filter->filter_udta + arg->offset_in_private);
    3687          36 :                         break;
    3688           0 :                 case GF_PROP_VEC2I:
    3689           0 :                         p.value.vec2i = * (GF_PropVec2i *) ((char *)filter->filter_udta + arg->offset_in_private);
    3690           0 :                         break;
    3691           0 :                 case GF_PROP_VEC2:
    3692           0 :                         p.value.vec2 = * (GF_PropVec2 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3693           0 :                         break;
    3694           0 :                 case GF_PROP_VEC3I:
    3695           0 :                         p.value.vec3i = * (GF_PropVec3i *) ((char *)filter->filter_udta + arg->offset_in_private);
    3696           0 :                         break;
    3697           0 :                 case GF_PROP_VEC4I:
    3698           0 :                         p.value.vec4i = * (GF_PropVec4i *) ((char *)filter->filter_udta + arg->offset_in_private);
    3699           0 :                         break;
    3700           4 :                 case GF_PROP_FRACTION:
    3701           4 :                         p.value.frac = * (GF_Fraction *) ((char *)filter->filter_udta + arg->offset_in_private);
    3702           4 :                         break;
    3703           3 :                 case GF_PROP_FRACTION64:
    3704           3 :                         p.value.lfrac = * (GF_Fraction64 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3705           3 :                         break;
    3706           0 :                 case GF_PROP_DATA:
    3707             :                 case GF_PROP_DATA_NO_COPY:
    3708             :                 case GF_PROP_CONST_DATA:
    3709           0 :                         p.value.data = * (GF_PropData *) ((char *)filter->filter_udta + arg->offset_in_private);
    3710           0 :                         break;
    3711           0 :                 case GF_PROP_POINTER:
    3712           0 :                         p.value.ptr = * (void **) ((char *)filter->filter_udta + arg->offset_in_private);
    3713           0 :                         break;
    3714          19 :                 case GF_PROP_STRING_NO_COPY:
    3715             :                 case GF_PROP_STRING:
    3716             :                 case GF_PROP_NAME:
    3717          19 :                         p.value.ptr = * (char **) ((char *)filter->filter_udta + arg->offset_in_private);
    3718          19 :                         break;
    3719             :                 //use uint_list as base type for lists
    3720           0 :                 case GF_PROP_STRING_LIST:
    3721             :                 case GF_PROP_UINT_LIST:
    3722             :                 case GF_PROP_4CC_LIST:
    3723             :                 case GF_PROP_SINT_LIST:
    3724             :                 case GF_PROP_VEC2I_LIST:
    3725           0 :                         p.value.uint_list = * (GF_PropUIntList *) ((char *)filter->filter_udta + arg->offset_in_private);
    3726           0 :                         break;
    3727           0 :                 default:
    3728           0 :                         if (gf_props_type_is_enum(arg->arg_type)) {
    3729           0 :                                 p.value.uint = * (u32 *) ((char *)filter->filter_udta + arg->offset_in_private);
    3730           0 :                                 break;
    3731             :                         }
    3732         771 :                         return GF_FALSE;
    3733             :                 }
    3734         771 :                 if (min_max_enum) *min_max_enum = arg->min_max_enum;
    3735         771 :                 *prop = p;
    3736         771 :                 return GF_TRUE;
    3737             :         }
    3738           0 :         return GF_FALSE;
    3739             : }
    3740             : 
    3741             : GF_EXPORT
    3742           2 : const char *gf_filter_get_arg_str(GF_Filter *filter, const char *arg_name, char dump[GF_PROP_DUMP_ARG_SIZE])
    3743             : {
    3744             :         GF_PropertyValue p;
    3745           2 :         const char *arg_min_max = NULL;
    3746           2 :         if (!dump || !gf_filter_get_arg_internal(filter, arg_name, &p, &arg_min_max))
    3747             :                 return NULL;
    3748           0 :         return gf_props_dump_val(&p, dump, GF_PROP_DUMP_DATA_NONE, arg_min_max);
    3749             : }
    3750             : 
    3751             : GF_EXPORT
    3752         771 : Bool gf_filter_get_arg(GF_Filter *filter, const char *arg_name, GF_PropertyValue *prop)
    3753             : {
    3754         771 :         const char *arg_min_max = NULL;
    3755         771 :         if (!prop) return GF_FALSE;
    3756         771 :         return gf_filter_get_arg_internal(filter, arg_name, prop, &arg_min_max);
    3757             : }
    3758             : 
    3759             : GF_EXPORT
    3760         151 : Bool gf_filter_is_supported_mime(GF_Filter *filter, const char *mime)
    3761             : {
    3762         151 :         return gf_fs_is_supported_mime(filter->session, mime);
    3763             : }
    3764             : 
    3765             : GF_EXPORT
    3766         101 : Bool gf_filter_ui_event(GF_Filter *filter, GF_Event *uievt)
    3767             : {
    3768         101 :         return gf_fs_ui_event(filter->session, uievt);
    3769             : }
    3770             : 
    3771             : GF_EXPORT
    3772          12 : Bool gf_filter_all_sinks_done(GF_Filter *filter)
    3773             : {
    3774             :         u32 i, count;
    3775             :         Bool res = GF_TRUE;
    3776          12 :         if (!filter || filter->session->in_final_flush || (filter->session->run_status==GF_EOS) )
    3777             :                 return GF_TRUE;
    3778             : 
    3779          11 :         gf_mx_p(filter->session->filters_mx);
    3780          11 :         count = gf_list_count(filter->session->filters);
    3781          61 :         for (i=0; i<count; i++) {
    3782             :                 u32 j;
    3783          50 :                 GF_Filter *f = gf_list_get(filter->session->filters, i);
    3784          50 :                 if (f->num_output_pids) continue;
    3785          11 :                 gf_mx_p(f->tasks_mx);
    3786          21 :                 for (j=0; j<f->num_input_pids; j++) {
    3787          10 :                         GF_FilterPidInst *pidi = gf_list_get(f->input_pids, j);
    3788          10 :                         if (pidi->pid->is_playing && !pidi->is_end_of_stream) {
    3789             :                                 res = GF_FALSE;
    3790           0 :                                 gf_mx_v(f->tasks_mx);
    3791           0 :                                 goto exit;
    3792             :                         }
    3793             :                 }
    3794          11 :                 gf_mx_v(f->tasks_mx);
    3795             :         }
    3796          11 : exit:
    3797          11 :         gf_mx_v(filter->session->filters_mx);
    3798          11 :         return res;
    3799             : }
    3800             : 
    3801             : 
    3802             : GF_EXPORT
    3803          31 : void gf_filter_register_opengl_provider(GF_Filter *filter, Bool do_register)
    3804             : {
    3805             : #ifndef GPAC_DISABLE_3D
    3806             :         GF_Err e;
    3807          31 :         if (filter->removed || filter->finalized) return;
    3808             : 
    3809          31 :         if (do_register) {
    3810          31 :                 if (gf_list_find(filter->session->gl_providers, filter)<0)
    3811          31 :                         gf_list_add(filter->session->gl_providers, filter);
    3812             :                 return;
    3813             :         }
    3814           0 :         gf_list_del_item(filter->session->gl_providers, filter);
    3815           0 :         e = gf_fs_check_gl_provider(filter->session);
    3816           0 :         if (e && filter->session->nb_gl_filters) {
    3817           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to reload an OpenGL provider and some filters require openGL, aborting\n"));
    3818           0 :                 gf_fs_abort(filter->session, GF_FALSE);
    3819             :         }
    3820             : #endif
    3821             : 
    3822             : }
    3823             : 
    3824             : GF_EXPORT
    3825         124 : GF_Err gf_filter_request_opengl(GF_Filter *filter)
    3826             : {
    3827             : #ifndef GPAC_DISABLE_3D
    3828             :         GF_Err e;
    3829         124 :         if (filter->finalized || filter->removed) return GF_OK;
    3830             : 
    3831         124 :         filter->session->nb_gl_filters++;
    3832         124 :         e = gf_fs_check_gl_provider(filter->session);
    3833         124 :         if (e) {
    3834           0 :                 filter->session->nb_gl_filters--;
    3835           0 :                 filter->main_thread_forced = GF_FALSE;
    3836           0 :                 return e;
    3837             :         }
    3838         124 :         if (!(filter->freg->flags & GF_FS_REG_CONFIGURE_MAIN_THREAD))
    3839         124 :                 filter->main_thread_forced = GF_TRUE;
    3840             :         return GF_OK;
    3841             : #else
    3842             :         return GF_NOT_SUPPORTED;
    3843             : #endif
    3844             : }
    3845             : 
    3846             : GF_EXPORT
    3847         607 : GF_Err gf_filter_set_active_opengl_context(GF_Filter *filter)
    3848             : {
    3849             : #ifndef GPAC_DISABLE_3D
    3850         607 :         if (filter->finalized || filter->removed) return GF_OK;
    3851         607 :         return gf_fs_set_gl(filter->session);
    3852             : #else
    3853             :         return GF_NOT_SUPPORTED;
    3854             : #endif
    3855             : }
    3856             : 
    3857             : 
    3858             : GF_EXPORT
    3859           2 : u32 gf_filter_count_source_by_protocol(GF_Filter *filter, const char *protocol_scheme, Bool expand_proto, GF_FilterPid * (*enum_pids)(void *udta, u32 *idx), void *udta)
    3860             : {
    3861             :         u32 i, count, len, res=0;
    3862           2 :         if (!filter || !protocol_scheme) return 0;
    3863           2 :         gf_mx_p(filter->session->filters_mx);
    3864           2 :         len = (u32) strlen(protocol_scheme);
    3865           2 :         count = gf_list_count(filter->session->filters);
    3866          28 :         for (i=0; i<count; i++) {
    3867             :                 const char *args;
    3868          26 :                 GF_Filter *src = gf_list_get(filter->session->filters, i);
    3869             :                 //check only sinks
    3870          26 :                 if (src->freg->configure_pid) continue;
    3871          14 :                 args = src->src_args;
    3872          14 :                 if (!args) args = src->orig_args;
    3873          14 :                 if (!args || (strlen(args)<5) ) continue;
    3874             : 
    3875          14 :                 if (strncmp(args + 4, protocol_scheme, len)) continue;
    3876           4 :                 if (!expand_proto && (args[len] != ':')) continue;
    3877             : 
    3878           4 :                 if (! gf_filter_in_parent_chain(filter, src)) {
    3879           2 :                         u32 j=0;
    3880             :                         Bool found=GF_FALSE;
    3881           2 :                         if (!enum_pids) continue;
    3882             :                         while (1) {
    3883           2 :                                 GF_FilterPid *pid = enum_pids(udta, &j);
    3884           2 :                                 if (!pid) break;
    3885           2 :                                 j++;
    3886           2 :                                 if ( gf_filter_in_parent_chain(pid->pid->filter, src)) {
    3887             :                                         found=GF_TRUE;
    3888             :                                         break;
    3889             :                                 }
    3890             :                         }
    3891           2 :                         if (!found) continue;
    3892             :                 }
    3893             : 
    3894           4 :                 res++;
    3895             :         }
    3896             : 
    3897           2 :         gf_mx_v(filter->session->filters_mx);
    3898           2 :         return res;
    3899             : }
    3900             : 
    3901             : GF_EXPORT
    3902           4 : void gf_filter_disable_probe(GF_Filter *filter)
    3903             : {
    3904           4 :         if (filter)
    3905           4 :                 filter->no_probe = GF_TRUE;
    3906           4 : }
    3907             : 
    3908             : GF_EXPORT
    3909           8 : void gf_filter_disable_inputs(GF_Filter *filter)
    3910             : {
    3911           8 :         if (filter)
    3912           8 :                 filter->no_inputs = GF_TRUE;
    3913           8 : }
    3914             : 
    3915        1779 : static Bool gf_filter_has_pid_connection_pending_internal(GF_Filter *filter, GF_Filter *stop_at_filter)
    3916             : {
    3917             :         u32 i, j;
    3918        1779 :         if (filter == stop_at_filter) return GF_FALSE;
    3919             : 
    3920        1469 :         if (filter->has_pending_pids) return GF_TRUE;
    3921        1363 :         if (filter->in_pid_connection_pending) return GF_TRUE;
    3922        1363 :         if (filter->out_pid_connection_pending) return GF_TRUE;
    3923             : 
    3924        1339 :         if (!filter->num_output_pids) {
    3925         324 :                 if (!filter->act_as_sink && !filter->multi_sink_target) {
    3926         324 :                         if (filter->forced_caps) {
    3927           0 :                                 if (gf_filter_has_out_caps(filter->forced_caps, filter->nb_forced_caps))
    3928             :                                         return GF_TRUE;
    3929             :                         } else {
    3930         324 :                                 if (gf_filter_has_out_caps(filter->freg->caps, filter->freg->nb_caps))
    3931             :                                         return GF_TRUE;
    3932             :                         }
    3933             :                 }
    3934             :                 return GF_FALSE;
    3935             :         }
    3936             : 
    3937         622 :         for (i=0; i<filter->num_output_pids; i++) {
    3938        1015 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    3939        1015 :                 if (pid->init_task_pending) return GF_TRUE;
    3940         621 :                 for (j=0; j<pid->num_destinations; j++) {
    3941        1002 :                         GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
    3942        1002 :                         Bool res = gf_filter_has_pid_connection_pending_internal(pidi->filter, stop_at_filter);
    3943        1002 :                         if (res) return GF_TRUE;
    3944             :                 }
    3945             :         }
    3946             :         return GF_FALSE;
    3947             : }
    3948             : 
    3949             : GF_EXPORT
    3950         777 : Bool gf_filter_has_pid_connection_pending(GF_Filter *filter, GF_Filter *stop_at_filter)
    3951             : {
    3952             :         GF_FilterSession *fsess;
    3953             :         Bool res;
    3954         777 :         if (!filter) return GF_FALSE;
    3955             :         //lock session, this is an unsafe call
    3956         777 :         fsess = filter->session;
    3957         777 :         if (fsess->filters_mx) gf_mx_p(fsess->filters_mx);
    3958         777 :         res = gf_filter_has_pid_connection_pending_internal(filter, stop_at_filter);
    3959         777 :         if (fsess->filters_mx) gf_mx_v(fsess->filters_mx);
    3960             :         return res;
    3961             : }
    3962             : 
    3963             : GF_EXPORT
    3964    13136694 : Bool gf_filter_reporting_enabled(GF_Filter *filter)
    3965             : {
    3966    13136694 :         if (filter) return filter->session->reporting_on;
    3967             :         return GF_FALSE;
    3968             : }
    3969             : 
    3970             : GF_EXPORT
    3971          52 : GF_Err gf_filter_update_status(GF_Filter *filter, u32 percent, char *szStatus)
    3972             : {
    3973             :         GF_Event evt;
    3974             :         u32 len;
    3975          52 :         if (!filter) return GF_BAD_PARAM;
    3976          52 :         if (!filter->session->reporting_on)
    3977             :                 return GF_OK;
    3978             : 
    3979          51 :         if (!szStatus) {
    3980           0 :                 if (filter->status_str) filter->status_str[0] = 0;
    3981             :                 return GF_OK;
    3982             :         }
    3983          51 :         len = (u32) strlen(szStatus);
    3984          51 :         if (len>=filter->status_str_alloc) {
    3985          17 :                 filter->status_str_alloc = len+1;
    3986          17 :                 filter->status_str = gf_realloc(filter->status_str, filter->status_str_alloc);
    3987          17 :                 if (!filter->status_str) {
    3988           0 :                         filter->status_str_alloc = 0;
    3989           0 :                         return GF_OUT_OF_MEM;
    3990             :                 }
    3991             :         }
    3992          51 :         memcpy(filter->status_str, szStatus, len+1);
    3993          51 :         filter->status_percent = percent;
    3994          51 :         filter->report_updated = GF_TRUE;
    3995             : 
    3996             :         memset(&evt, 0, sizeof(GF_Event));
    3997          51 :         evt.type = GF_EVENT_PROGRESS;
    3998          51 :         evt.progress.progress_type = 3;
    3999          51 :         evt.progress.done = percent;
    4000          51 :         evt.progress.total = ((s32)percent>0) ? 10000 : 0;
    4001          51 :         evt.progress.filter_idx = gf_list_find(filter->session->filters, filter);
    4002          51 :         gf_fs_ui_event(filter->session, &evt);
    4003          51 :         return GF_OK;
    4004             : }
    4005             : 
    4006          74 : void gf_filter_report_unused_meta_option(GF_Filter *filter, const char *arg)
    4007             : {
    4008          74 :         if (!filter->session || filter->removed || filter->finalized) return;
    4009          74 :         if (filter->orig_args) {
    4010          74 :                 char *opt_arg = strstr(filter->orig_args, "gfopt");
    4011          74 :                 if (opt_arg) {
    4012          14 :                         char *arg_pos = strstr(filter->orig_args, arg);
    4013          14 :                         if (arg_pos && (arg_pos>opt_arg))
    4014             :                                 return;
    4015             :                 }
    4016             :         }
    4017          60 :         gf_mx_p(filter->session->filters_mx);
    4018          60 :         gf_fs_push_arg(filter->session, arg, GF_FALSE, 2);
    4019          60 :         gf_mx_v(filter->session->filters_mx);
    4020             : }
    4021             : 
    4022          41 : GF_Err gf_filter_set_description(GF_Filter *filter, const char *new_desc)
    4023             : {
    4024          41 :         if (!filter) return GF_BAD_PARAM;
    4025          41 :         if (filter->instance_description)
    4026           0 :                 gf_free(filter->instance_description);
    4027             : 
    4028          41 :         filter->instance_description = new_desc ? gf_strdup(new_desc) : NULL;
    4029          41 :         return GF_OK;
    4030             : }
    4031             : GF_EXPORT
    4032           6 : const char *gf_filter_get_description(GF_Filter *filter)
    4033             : {
    4034           6 :         return filter ? filter->instance_description : NULL;
    4035             : }
    4036             : 
    4037          41 : GF_Err gf_filter_set_version(GF_Filter *filter, const char *new_desc)
    4038             : {
    4039          41 :         if (!filter) return GF_BAD_PARAM;
    4040          41 :         if (filter->instance_version)
    4041           0 :                 gf_free(filter->instance_version);
    4042             : 
    4043          41 :         filter->instance_version = new_desc ? gf_strdup(new_desc) : NULL;
    4044          41 :         return GF_OK;
    4045             : }
    4046             : GF_EXPORT
    4047           2 : const char *gf_filter_get_version(GF_Filter *filter)
    4048             : {
    4049           2 :         return filter ? filter->instance_version : NULL;
    4050             : }
    4051             : 
    4052          41 : GF_Err gf_filter_set_author(GF_Filter *filter, const char *new_desc)
    4053             : {
    4054          41 :         if (!filter) return GF_BAD_PARAM;
    4055          41 :         if (filter->instance_author)
    4056           0 :                 gf_free(filter->instance_author);
    4057             : 
    4058          41 :         filter->instance_author = new_desc ? gf_strdup(new_desc) : NULL;
    4059          41 :         return GF_OK;
    4060             : }
    4061             : GF_EXPORT
    4062           3 : const char *gf_filter_get_author(GF_Filter *filter)
    4063             : {
    4064           3 :         return filter ? filter->instance_author : NULL;
    4065             : }
    4066             : 
    4067          41 : GF_Err gf_filter_set_help(GF_Filter *filter, const char *new_desc)
    4068             : {
    4069          41 :         if (!filter) return GF_BAD_PARAM;
    4070          41 :         if (filter->instance_help)
    4071           0 :                 gf_free(filter->instance_help);
    4072             : 
    4073          41 :         filter->instance_help = new_desc ? gf_strdup(new_desc) : NULL;
    4074          41 :         return GF_OK;
    4075             : }
    4076             : GF_EXPORT
    4077           6 : const char *gf_filter_get_help(GF_Filter *filter)
    4078             : {
    4079           6 :         return filter ? filter->instance_help : NULL;
    4080             : }
    4081             : 
    4082         146 : GF_Err gf_filter_define_args(GF_Filter *filter, GF_FilterArgs *args)
    4083             : {
    4084         146 :         if (!filter) return GF_BAD_PARAM;
    4085         146 :         filter->instance_args = args;
    4086         146 :         return GF_OK;
    4087             : }
    4088             : GF_EXPORT
    4089           3 : GF_FilterArgs *gf_filter_get_args(GF_Filter *filter)
    4090             : {
    4091           3 :         return filter ? filter->instance_args : NULL;
    4092             : }
    4093             : 
    4094             : GF_EXPORT
    4095           0 : const GF_FilterCapability *gf_filter_get_caps(GF_Filter *filter, u32 *nb_caps)
    4096             : {
    4097           0 :         if (!filter || !filter->forced_caps || !nb_caps) return NULL;
    4098           0 :         *nb_caps = filter->nb_forced_caps;
    4099           0 :         return filter->forced_caps;
    4100             : }
    4101             : 
    4102             : GF_EXPORT
    4103           7 : GF_Filter *gf_filter_load_filter(GF_Filter *filter, const char *name, GF_Err *err_code)
    4104             : {
    4105           7 :         if (!filter) return NULL;
    4106           7 :         return gf_fs_load_filter(filter->session, name, err_code);
    4107             : }
    4108             : 
    4109             : GF_EXPORT
    4110         168 : Bool gf_filter_end_of_session(GF_Filter *filter)
    4111             : {
    4112         168 :         if (!filter) return GF_TRUE;
    4113         168 :         return filter->session->in_final_flush;
    4114             : }
    4115             : 
    4116             : GF_EXPORT
    4117         118 : Bool gf_filter_is_alias(GF_Filter *filter)
    4118             : {
    4119         118 :         if (filter && filter->multi_sink_target) return GF_TRUE;
    4120          74 :         return GF_FALSE;
    4121             : }
    4122             : 
    4123             : /*! checks if the some PID connection tasks are still pending at the session level
    4124             : \param filter target filter
    4125             : \return GF_TRUE if some connection tasks are pending, GF_FALSE otherwise
    4126             : */
    4127             : GF_EXPORT
    4128        1090 : Bool gf_filter_connections_pending(GF_Filter *filter)
    4129             : {
    4130             :         u32 i, count;
    4131             :         Bool res = GF_FALSE;
    4132        1090 :         if (!filter) return GF_FALSE;
    4133        1090 :         if (filter->session->pid_connect_tasks_pending) return GF_TRUE;
    4134         829 :         gf_mx_p(filter->session->filters_mx);
    4135         829 :         count = gf_list_count(filter->session->filters);
    4136        5823 :         for (i=0; i<count; i++) {
    4137             :                 u32 j;
    4138        5002 :                 GF_Filter *f = gf_list_get(filter->session->filters, i);
    4139             : 
    4140        5002 :                 gf_mx_p(f->tasks_mx);
    4141       10336 :                 for (j=0; j<f->num_output_pids; j++) {
    4142        5334 :                         GF_FilterPid *pid = gf_list_get(f->output_pids, j);
    4143        5334 :                         if (pid->init_task_pending) {
    4144             :                                 res = GF_TRUE;
    4145             :                                 break;
    4146             :                         }
    4147             :                 }
    4148        5002 :                 if (res) {
    4149           0 :                         gf_mx_v(f->tasks_mx);
    4150           0 :                         break;
    4151             :                 }
    4152             : 
    4153        5002 :                 if (f==filter) {
    4154         829 :                         gf_mx_v(f->tasks_mx);
    4155         829 :                         continue;
    4156             :                 }
    4157             : 
    4158        4173 :                 if (f->in_pid_connection_pending || f->out_pid_connection_pending) {
    4159             :                         res = GF_TRUE;
    4160             :                 }
    4161             :                 //filter has no output, check if it is expected or not
    4162        4173 :                 else if (!f->num_output_pids && !f->act_as_sink && !f->multi_sink_target) {
    4163        1289 :                         if (f->forced_caps) {
    4164        1275 :                                 res = gf_filter_has_out_caps(f->forced_caps, f->nb_forced_caps);
    4165             :                         } else {
    4166          14 :                                 res = gf_filter_has_out_caps(f->freg->caps, f->freg->nb_caps);
    4167             :                         }
    4168             :                 }
    4169        4173 :                 gf_mx_v(f->tasks_mx);
    4170        4173 :                 if (res)
    4171             :                         break;
    4172             :         }
    4173         829 :         gf_mx_v(filter->session->filters_mx);
    4174             : 
    4175         829 :         return res;
    4176             : }
    4177             : 
    4178             : GF_EXPORT
    4179        4593 : GF_Err gf_filter_prevent_blocking(GF_Filter *filter, Bool prevent_blocking_enabled)
    4180             : {
    4181        4593 :         if (!filter) return GF_BAD_PARAM;
    4182        4593 :         filter->prevent_blocking = prevent_blocking_enabled;
    4183        4593 :         return GF_OK;
    4184             : }
    4185             : 
    4186             : GF_EXPORT
    4187         627 : Bool gf_filter_is_dynamic(GF_Filter *filter)
    4188             : {
    4189         627 :         return filter ? filter->dynamic_filter : GF_FALSE;
    4190             : }
    4191             : 
    4192             : GF_EXPORT
    4193         119 : void gf_filter_block_eos(GF_Filter *filter, Bool do_block)
    4194             : {
    4195         119 :         if (filter) filter->block_eos = do_block;
    4196         119 : }
    4197             : 
    4198             : GF_EXPORT
    4199           2 : GF_Err gf_filter_reconnect_output(GF_Filter *filter)
    4200             : {
    4201             :         u32 i;
    4202           2 :         if (!filter) return GF_BAD_PARAM;
    4203           1 :         for (i=0; i<filter->num_output_pids; i++) {
    4204           1 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    4205           1 :                 gf_filter_pid_post_init_task(filter, pid);
    4206             :         }
    4207             :         return GF_OK;
    4208             : }
    4209             : 
    4210             : GF_EXPORT
    4211         633 : GF_Err gf_filter_set_event_target(GF_Filter *filter, Bool enable_events)
    4212             : {
    4213         633 :         if (!filter) return GF_BAD_PARAM;
    4214         633 :         filter->event_target = enable_events;
    4215         633 :         return GF_OK;
    4216             : }
    4217             : 
    4218             : GF_EXPORT
    4219           3 : GF_Err gf_filter_push_caps(GF_Filter *filter, u32 code, GF_PropertyValue *value, const char *name, u32 flags, u8 priority)
    4220             : {
    4221             :         u32 nb_caps;
    4222             :         GF_FilterCapability *caps;
    4223           3 :         if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
    4224           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to push cap on non custom filter %s\n", filter->freg->name));
    4225             :                 return GF_BAD_PARAM;
    4226             :         }
    4227           3 :         caps = (GF_FilterCapability *)filter->forced_caps;
    4228           3 :         nb_caps = filter->nb_forced_caps;
    4229           3 :         caps = gf_realloc(caps, sizeof(GF_FilterCapability)*(nb_caps+1) );
    4230           3 :         if (!caps) return GF_OUT_OF_MEM;
    4231           3 :         caps[nb_caps].code = code;
    4232           3 :         caps[nb_caps].val = *value;
    4233           3 :         caps[nb_caps].name = name ? gf_strdup(name) : NULL;
    4234           3 :         caps[nb_caps].priority = priority;
    4235           3 :         caps[nb_caps].flags = flags;
    4236           3 :         filter->nb_forced_caps++;
    4237           3 :         filter->forced_caps = caps;
    4238           3 :         return GF_OK;
    4239             : }
    4240             : 
    4241             : GF_EXPORT
    4242           3 : GF_Err gf_filter_set_process_ckb(GF_Filter *filter, GF_Err (*process_cbk)(GF_Filter *filter) )
    4243             : {
    4244           3 :         if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
    4245           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to assign filter callback on non custom filter %s\n", filter->freg->name));
    4246             :                 return GF_BAD_PARAM;
    4247             :         }
    4248           3 :         ((GF_FilterRegister *) filter->freg)->process = process_cbk;
    4249           3 :         return GF_OK;
    4250             : }
    4251             : 
    4252             : 
    4253             : GF_EXPORT
    4254           3 : GF_Err gf_filter_set_configure_ckb(GF_Filter *filter, GF_Err (*configure_cbk)(GF_Filter *filter, GF_FilterPid *PID, Bool is_remove) )
    4255             : {
    4256           3 :         if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
    4257           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to assign filter callback on non custom filter %s\n", filter->freg->name));
    4258             :                 return GF_BAD_PARAM;
    4259             :         }
    4260           3 :         ((GF_FilterRegister *) filter->freg)->configure_pid = configure_cbk;
    4261           3 :         return GF_OK;
    4262             : }
    4263             : 
    4264             : GF_EXPORT
    4265           1 : GF_Err gf_filter_set_process_event_ckb(GF_Filter *filter, Bool (*process_event_cbk)(GF_Filter *filter, const GF_FilterEvent *evt) )
    4266             : {
    4267           1 :         if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
    4268           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to assign filter callback on non custom filter %s\n", filter->freg->name));
    4269             :                 return GF_BAD_PARAM;
    4270             :         }
    4271           1 :         ((GF_FilterRegister *) filter->freg)->process_event = process_event_cbk;
    4272           1 :         return GF_OK;
    4273             : }
    4274             : 
    4275             : GF_EXPORT
    4276           1 : GF_Err gf_filter_set_reconfigure_output_ckb(GF_Filter *filter, GF_Err (*reconfigure_output_cbk)(GF_Filter *filter, GF_FilterPid *PID) )
    4277             : {
    4278           1 :         if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
    4279           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to assign filter callback on non custom filter %s\n", filter->freg->name));
    4280             :                 return GF_BAD_PARAM;
    4281             :         }
    4282           1 :         ((GF_FilterRegister *) filter->freg)->reconfigure_output = reconfigure_output_cbk;
    4283           1 :         return GF_OK;
    4284             : }
    4285             : 
    4286             : GF_EXPORT
    4287           1 : GF_Err gf_filter_set_probe_data_cbk(GF_Filter *filter, const char * (*probe_data_cbk)(const u8 *data, u32 size, GF_FilterProbeScore *score) )
    4288             : {
    4289           1 :         if (! (filter->freg->flags & GF_FS_REG_CUSTOM)) {
    4290           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to assign filter callback on non custom filter %s\n", filter->freg->name));
    4291             :                 return GF_BAD_PARAM;
    4292             :         }
    4293           1 :         ((GF_FilterRegister *) filter->freg)->probe_data = probe_data_cbk;
    4294           1 :         return GF_OK;
    4295             : }
    4296             : 
    4297             : 
    4298             : GF_EXPORT
    4299          45 : const GF_FilterArgs *gf_filter_enumerate_args(GF_Filter *filter, u32 idx)
    4300             : {
    4301             :         u32 i;
    4302          45 :         if (!filter) return NULL;
    4303          45 :         if (!filter->freg->args) return NULL;
    4304             : 
    4305         424 :         for (i=0; i<=idx; i++) {
    4306         427 :                 if (! filter->freg->args[i].arg_name)
    4307             :                         return NULL;
    4308             :         }
    4309          42 :         return &filter->freg->args[idx];
    4310             : }
    4311             : 
    4312             : GF_EXPORT
    4313           2 : GF_Err gf_filter_set_rt_udta(GF_Filter *filter, void *udta)
    4314             : {
    4315           2 :         if (!filter) return GF_BAD_PARAM;
    4316           2 :         filter->rt_udta = udta;
    4317           2 :         return GF_OK;
    4318             : }
    4319             : 
    4320             : GF_EXPORT
    4321         361 : void *gf_filter_get_rt_udta(GF_Filter *filter)
    4322             : {
    4323         361 :         if (!filter) return NULL;
    4324         361 :         return filter->rt_udta;
    4325             : }
    4326             : 
    4327             : 
    4328           2 : Bool gf_filter_is_instance_of(GF_Filter *filter, const GF_FilterRegister *freg)
    4329             : {
    4330           2 :         if (filter && freg && (filter->freg==freg))
    4331             :                 return GF_TRUE;
    4332           0 :         return GF_FALSE;
    4333             : }
    4334             : 
    4335             : GF_EXPORT
    4336           0 : void gf_filter_abort(GF_Filter *filter)
    4337             : {
    4338             :         u32 i;
    4339             :         GF_FilterEvent evt;
    4340           0 :         if (!filter) return;
    4341           0 :         gf_mx_p(filter->tasks_mx);
    4342           0 :         GF_FEVT_INIT(evt, GF_FEVT_STOP, NULL);
    4343           0 :         for (i=0; i<filter->num_input_pids; i++) {
    4344           0 :                 GF_FilterPid *pid = gf_list_get(filter->input_pids, i);
    4345           0 :                 gf_filter_pid_set_discard(pid, GF_TRUE);
    4346           0 :                 evt.base.on_pid = pid;
    4347           0 :                 gf_filter_pid_send_event(pid, &evt);
    4348             :         }
    4349           0 :         for (i=0; i<filter->num_output_pids; i++) {
    4350           0 :                 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
    4351           0 :                 gf_filter_pid_set_eos(pid);
    4352             :         }
    4353           0 :         gf_mx_v(filter->tasks_mx);
    4354             : }
    4355             : 
    4356             : GF_EXPORT
    4357           0 : void gf_filter_lock(GF_Filter *filter, Bool do_lock)
    4358             : {
    4359           0 :         if (!filter) return;
    4360           0 :         if (do_lock)
    4361           0 :                 gf_mx_p(filter->tasks_mx);
    4362             :         else
    4363           0 :                 gf_mx_v(filter->tasks_mx);
    4364             : }
    4365             : 
    4366           0 : void gf_filter_mirror_forced_caps(GF_Filter *filter, GF_Filter *dst_filter)
    4367             : {
    4368           0 :         if (filter && dst_filter) {
    4369           0 :                 filter->forced_caps = dst_filter->forced_caps;
    4370           0 :                 filter->nb_forced_caps = dst_filter->nb_forced_caps;
    4371             :         }
    4372           0 : }

Generated by: LCOV version 1.13