Skip to content

Commit

Permalink
fixed possible infinite loop in pid info query and added filter graph…
Browse files Browse the repository at this point in the history
… helpers
  • Loading branch information
jeanlf committed Jan 29, 2024
1 parent ecefac9 commit 8f9e764
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 38 deletions.
46 changes: 44 additions & 2 deletions include/gpac/filters.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,31 @@ void gf_fs_del(GF_FilterSession *session);
Value can be omitted for boolean, defaulting to true (eg :allt). Using '!' before the name negates the result (eg :!moof_first).
Name can be omitted for enumerations (eg :disp=pbo is equivalent to :pbo), provided that filter developers pay attention to not reuse enum names in one filter.
Options are either options for the target filter class, options to be inherited between the new filter and its implicit source(s) or implici destination(s), or generic filter options.
Generic filter options are:
- FID: filter identifier (string value)
- SID: filter source(s) (string value)
- N=NAME: filter name (string value)
- FS: sub-session identifier (unsigned int value)
- RSID: require sourceID to be present on target filters (no value)
- TAG: filter tag (string value)
- ITAG: filter inherited tag (string value)
- FBT: buffer time in microseconds (unsigned int value)
- FBU: buffer units (unsigned int value)
- FBD: decode buffer time in microseconds (unsigned int value)
- clone: explicitly enable/disable filter cloning flag (no value)
- nomux: enable/disable direct file copy (no value)
- gfreg: preferred filter registry names for link solving (string value)
- gfloc: following options are local to filter declaration, not inherited (no value)
- gfopt: following options are not tracked (no value)
- gpac: argument separator for URLs (no value)
- ccp: filter replacement control (string list value)
- NCID: ID of netcap configuration to use (string)
- DBG: debug missing input PID property (`=pid`), missing input packet property (`=pck`) or both (`=all`)
- DL: enable defer linkling of filter (no value) - the filter output pids will not be connected until a call to \ref gf_filter_reconnect_output
\param session filter session
\param name name and arguments of the filter register to instantiate.
\param err_code set to error code if any - may be NULL. If initially set to GF_EOS, disables log messages.
Expand Down Expand Up @@ -348,7 +373,7 @@ void gf_fs_register_test_filters(GF_FilterSession *session);
/*! Loads a source filter from a URL and arguments
\param session filter session
\param url URL of the source to load. Can be a local file name, a full path (/.., \\...) or a full URL with scheme (eg http://, tcp://)
\param args arguments for the filter, see \ref gf_fs_load_filter
\param args arguments for the filter, see \ref gf_fs_load_filter - the arguments can also be set in the url, typycally using `:gpac:` option delimiter
\param parent_url parent URL of the source, or NULL if none
\param err if not NULL, is set to error code if any
\return the filter loaded or NULL if error
Expand All @@ -358,7 +383,7 @@ GF_Filter *gf_fs_load_source(GF_FilterSession *session, const char *url, const c
/*! Loads a destination filter from a URL and arguments
\param session filter session
\param url URL of the source to load. Can be a local file name, a full path (/.., \\...) or a full URL with scheme (eg http://, tcp://)
\param args arguments for the filter, see \ref gf_fs_load_filter
\param args arguments for the filter, see \ref gf_fs_load_filter - the arguments can also be set in the url, typycally using `:gpac:` option delimiter
\param parent_url parent URL of the source, or NULL if none
\param err if not NULL, is set to error code if any
\return the filter loaded or NULL if error
Expand Down Expand Up @@ -3252,6 +3277,23 @@ const GF_FilterArgs *gf_filter_enumerate_args(GF_Filter *filter, u32 idx);
Bool gf_filter_relocate_url(GF_Filter *filter, const char *service_url, const char *parent_url, char *out_relocated_url, char *out_localized_url);


/*! Probes for link resolution towards a given filter description
\param filter target filter
\param opid_idx output pid index of target filter
\param fname textual description of filter - If a source is used, returns an error. Destination can be identified using dst=URL pattern
\param result_chain resulting chain as comma-separated list, or NULL if error. MUST be freed by caller
\return error if any
*/
GF_Err gf_filter_probe_link(GF_Filter *filter, u32 opid_idx, const char *fname, char **result_chain);

/*! Gets list of possible destinations for this filter
\param filter target filter
\param opid_idx output pid index of target filter. If negative, will check destinations for any of the output pids
\param result_list resulting list as comma-separated list, or NULL if error. MUST be freed by caller
\return error if any
*/
GF_Err gf_filter_get_possible_destinations(GF_Filter *filter, s32 opid_idx, char **result_list);

/*! Returns the register of a given filter
\param filter target filter
\return the register object, or NULL if error
Expand Down
2 changes: 2 additions & 0 deletions src/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,8 @@
#pragma comment (linker, EXPORT_SYMBOL(gf_filter_is_temporary ) )
#pragma comment (linker, EXPORT_SYMBOL(gf_filter_meta_set_instances ) )
#pragma comment (linker, EXPORT_SYMBOL(gf_filter_meta_get_instances ) )
#pragma comment (linker, EXPORT_SYMBOL(gf_filter_probe_link ) )
#pragma comment (linker, EXPORT_SYMBOL(gf_filter_get_possible_destinations ) )

#pragma comment (linker, EXPORT_SYMBOL(gf_filter_pck_discard ) )
#pragma comment (linker, EXPORT_SYMBOL(gf_filter_pck_ref ) )
Expand Down
168 changes: 144 additions & 24 deletions src/filter_core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,11 @@ static void filter_push_args(GF_FilterSession *fsess, char **out_args, char *in_
}
else if (!strncmp(in_args, "RSID", 4) && (!in_args[4] || (in_args[4]==fsess->sep_args))) {
}
else if (!strncmp(in_args, "FBT", 2) && (in_args[3]==fsess->sep_name)) {
else if (!strncmp(in_args, "FBT", 3) && (in_args[3]==fsess->sep_name)) {
}
else if (!strncmp(in_args, "FBU", 2) && (in_args[3]==fsess->sep_name)) {
else if (!strncmp(in_args, "FBU", 3) && (in_args[3]==fsess->sep_name)) {
}
else if (!strncmp(in_args, "DL", 2) && (in_args[2]==fsess->sep_name)) {
}
else if (!is_src && (in_args[0]==fsess->sep_frag)) {

Expand Down Expand Up @@ -516,6 +518,18 @@ GF_Filter *gf_filter_new(GF_FilterSession *fsess, const GF_FilterRegister *freg,
return filter;
}

void gf_filter_check_pending_pids(GF_Filter *filter)
{
//flush all pending pid init requests
if (filter->has_pending_pids && !filter->deferred_link) {
filter->has_pending_pids=GF_FALSE;
while (gf_fq_count(filter->pending_pids)) {
GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
gf_filter_pid_post_init_task(filter, pid);
}
}
}

GF_Err gf_filter_new_finalize(GF_Filter *filter, const char *args, GF_FilterArgType arg_type)
{
gf_filter_set_name(filter, NULL);
Expand Down Expand Up @@ -555,13 +569,7 @@ GF_Err gf_filter_new_finalize(GF_Filter *filter, const char *args, GF_FilterArgT
}

//flush all pending pid init requests
if (filter->has_pending_pids) {
filter->has_pending_pids=GF_FALSE;
while (gf_fq_count(filter->pending_pids)) {
GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
gf_filter_pid_post_init_task(filter, pid);
}
}
gf_filter_check_pending_pids(filter);

#ifdef GPAC_HAS_QJS
jsfs_on_filter_created(filter);
Expand Down Expand Up @@ -1989,6 +1997,14 @@ static void filter_parse_dyn_args(GF_Filter *filter, const char *args, GF_Filter
found = GF_TRUE;
internal_arg = GF_TRUE;
}
else if (!strcmp("DL", szArg)) {
if (! filter->dynamic_filter) {
filter->deferred_link = GF_TRUE;
GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Deferred linking enabled for filter %s\n", filter->freg->name));
}
found = GF_TRUE;
internal_arg = GF_TRUE;
}
//internal options, nothing to do here
else if (
//generic encoder load
Expand Down Expand Up @@ -3014,13 +3030,9 @@ static void gf_filter_process_task(GF_FSTask *task)
GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s process done\n", filter->name));

//flush all pending pid init requests following the call to init
if (filter->has_pending_pids) {
filter->has_pending_pids=GF_FALSE;
while (gf_fq_count(filter->pending_pids)) {
GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
gf_filter_pid_post_init_task(filter, pid);
}
}
gf_filter_check_pending_pids(filter);


//no requeue if end of session
if (session_should_abort(filter->session)) {
return;
Expand Down Expand Up @@ -3152,13 +3164,9 @@ void gf_filter_process_inline(GF_Filter *filter)
filter->in_process = GF_FALSE;

//flush all pending pid init requests following the call to init
if (filter->has_pending_pids) {
filter->has_pending_pids=GF_FALSE;
while (gf_fq_count(filter->pending_pids)) {
GF_FilterPid *pid=gf_fq_pop(filter->pending_pids);
gf_filter_pid_post_init_task(filter, pid);
}
}
gf_filter_check_pending_pids(filter);


//no requeue if end of session
if (session_should_abort(filter->session)) {
return;
Expand Down Expand Up @@ -3996,7 +4004,7 @@ GF_Filter *gf_filter_connect_source(GF_Filter *filter, const char *url, const ch

args = inherit_args ? gf_filter_get_dst_args(filter) : NULL;
if (args) {
char *rem_opts[] = {"FID", "SID", "N", "RSID", "clone", NULL};
char *rem_opts[] = {"FID", "SID", "N", "RSID", "clone", "DL", NULL};
char szSep[10];
char *loc_args;
u32 opt_idx;
Expand Down Expand Up @@ -5077,6 +5085,13 @@ GF_Err gf_filter_reconnect_output(GF_Filter *filter, GF_FilterPid *for_pid)
if (for_pid) {
if (PID_IS_INPUT(for_pid)) return GF_BAD_PARAM;
}
//in case we had pending output pids
if (filter->deferred_link && filter->has_pending_pids) {
filter->deferred_link = GF_FALSE;
gf_filter_check_pending_pids(filter);
if (!for_pid) return GF_OK;
}

for (i=0; i<filter->num_output_pids; i++) {
GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
if (for_pid && (pid != for_pid)) continue;
Expand Down Expand Up @@ -5409,3 +5424,108 @@ void gf_filter_dump_buffers(GF_Filter *f)
}
#endif

GF_EXPORT
GF_Err gf_filter_probe_link(GF_Filter *filter, u32 opid_idx, const char *fname, char **res_chain)
{
char *fdesc=NULL;
char szFmt[20];
GF_Filter *new_f;
GF_Err e;
GF_FilterPid *opid=NULL;
GF_FilterSession *fs;
if (!filter || !fname || !res_chain) return GF_BAD_PARAM;
*res_chain = NULL;
fs = filter->session;

opid = gf_filter_get_opid(filter, opid_idx);
if (!opid) return GF_BAD_PARAM;

if (!strncmp(fname, "src=", 4) || !strlen(fname)) return GF_BAD_PARAM;

fdesc = gf_strdup(fname);
sprintf(szFmt, "%cgpac", fs->sep_args);
if (strstr(fdesc, szFmt)) {
sprintf(szFmt, "%c_GFTMP", fs->sep_args);
} else {
sprintf(szFmt, "%c%cgpac%c%c_GFTMP", fs->sep_args, fs->sep_args, fs->sep_args, fs->sep_args);
}
gf_dynstrcat(&fdesc, szFmt, NULL);

gf_fs_lock_filters(fs, GF_TRUE);
if (!strncmp(fname, "dst=", 4)) {
new_f = gf_fs_load_destination(fs, fdesc+4, NULL, NULL, &e);
} else {
new_f = gf_fs_load_filter(fs, fdesc, &e);
}
gf_free(fdesc);

if (!new_f) {
gf_fs_lock_filters(fs, GF_FALSE);
return e;
}

GF_List *fchain = gf_filter_pid_compute_link(opid, new_f);
if (fchain) {
u32 i, count = gf_list_count(fchain);
for (i=0; i<count; i+=2) {
const GF_FilterRegister *freg = gf_list_get(fchain, i);
gf_dynstrcat(res_chain, freg->name, ",");
}
gf_list_del(fchain);
} else {
e = GF_FILTER_NOT_FOUND;
}
gf_list_del_item(fs->filters, new_f);
if (!new_f->finalized && new_f->freg->finalize) {
new_f->freg->finalize(new_f);
}
gf_filter_del(new_f);
gf_fs_lock_filters(fs, GF_FALSE);
return e;
}



GF_EXPORT
GF_Err gf_filter_get_possible_destinations(GF_Filter *filter, s32 opid_idx, char **res_list)
{
u32 i, j, k, count;
GF_FilterPid *opid=NULL;
if (!filter || !res_list) return GF_BAD_PARAM;
if (opid_idx>=0) {
opid = gf_list_get(filter->output_pids, opid_idx);
if (opid==NULL) return GF_BAD_PARAM;
}
*res_list = NULL;
count = gf_list_count(filter->session->links);
for (i=0; i<count; i++) {
Bool is_match=GF_FALSE;
const GF_FilterRegDesc *src = gf_list_get(filter->session->links, i);
if (!src->nb_edges)
continue;

for (j=0; j<src->nb_edges; j++) {
GF_FilterRegEdge *edge = &src->edges[j];
if (edge->src_reg->freg != filter->freg) continue;
//check pid caps match

for (k=0; k<filter->num_output_pids; k++) {
if ((opid_idx>=0) && ((u32)opid_idx==k)) continue;
opid = gf_list_get(filter->output_pids, k);
if (!opid) break;

u8 priority=0;
u32 dst_bundle_idx;
//check path weight for the given dst cap - we MUST give the target cap otherwise we might get a default match to another cap
u32 path_weight = gf_filter_pid_caps_match(opid, src->freg, NULL, &priority, &dst_bundle_idx, opid->filter->dst_filter, edge->dst_cap_idx);
if (!path_weight) continue;
is_match = GF_TRUE;
break;
}
}
if (is_match) {
gf_dynstrcat(res_list, src->freg->name, ",");
}
}
return GF_OK;
}
Loading

0 comments on commit 8f9e764

Please sign in to comment.