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 : }
|