1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2017-2018
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/constants.h>
28
pcki_del(GF_FilterPacketInstance * pcki)29 void pcki_del(GF_FilterPacketInstance *pcki)
30 {
31 assert(pcki->pck->reference_count);
32 if (safe_int_dec(&pcki->pck->reference_count) == 0) {
33 gf_filter_packet_destroy(pcki->pck);
34 }
35 gf_free(pcki);
36 }
37
gf_filter_pid_inst_reset(GF_FilterPidInst * pidinst)38 void gf_filter_pid_inst_reset(GF_FilterPidInst *pidinst)
39 {
40 assert(pidinst);
41 while (gf_fq_count(pidinst->packets)) {
42 GF_FilterPacketInstance *pcki = gf_fq_pop(pidinst->packets);
43 pcki_del(pcki);
44 }
45
46 while (gf_list_count(pidinst->pck_reassembly)) {
47 GF_FilterPacketInstance *pcki = gf_list_pop_back(pidinst->pck_reassembly);
48 pcki_del(pcki);
49 }
50 }
51
gf_filter_pid_inst_del(GF_FilterPidInst * pidinst)52 void gf_filter_pid_inst_del(GF_FilterPidInst *pidinst)
53 {
54 assert(pidinst);
55 gf_filter_pid_inst_reset(pidinst);
56
57 gf_fq_del(pidinst->packets, (gf_destruct_fun) pcki_del);
58 gf_mx_del(pidinst->pck_mx);
59 gf_list_del(pidinst->pck_reassembly);
60 if (pidinst->props) {
61 assert(pidinst->props->reference_count);
62 if (safe_int_dec(&pidinst->props->reference_count) == 0) {
63 //see \ref gf_filter_pid_merge_properties_internal for mutex
64 gf_mx_p(pidinst->pid->filter->tasks_mx);
65 gf_list_del_item(pidinst->pid->properties, pidinst->props);
66 gf_mx_v(pidinst->pid->filter->tasks_mx);
67 gf_props_del(pidinst->props);
68 }
69 }
70 gf_free(pidinst);
71 }
72
gf_filter_pid_inst_new(GF_Filter * filter,GF_FilterPid * pid)73 static GF_FilterPidInst *gf_filter_pid_inst_new(GF_Filter *filter, GF_FilterPid *pid)
74 {
75 GF_FilterPidInst *pidinst;
76 GF_SAFEALLOC(pidinst, GF_FilterPidInst);
77 if (!pidinst) return NULL;
78 pidinst->pid = pid;
79 pidinst->filter = filter;
80
81 if (filter->session->use_locks) {
82 char szName[200];
83 u32 pid_idx = 1 + gf_list_find(pid->filter->output_pids, pid);
84 u32 dst_idx = 1 + pid->num_destinations;
85 snprintf(szName, 200, "F%sPid%dDest%dPackets", filter->name, pid_idx, dst_idx);
86 pidinst->pck_mx = gf_mx_new(szName);
87 }
88
89 pidinst->packets = gf_fq_new(pidinst->pck_mx);
90
91 pidinst->pck_reassembly = gf_list_new();
92 pidinst->last_block_ended = GF_TRUE;
93 return pidinst;
94 }
95
gf_filter_pid_check_unblock(GF_FilterPid * pid)96 static void gf_filter_pid_check_unblock(GF_FilterPid *pid)
97 {
98 Bool unblock;
99
100 //if we are in end of stream state and done with all packets, stay blocked
101 if (pid->has_seen_eos && !pid->nb_buffer_unit) {
102 if (!pid->would_block) {
103 safe_int_inc(&pid->would_block);
104 safe_int_inc(&pid->filter->would_block);
105 assert(pid->filter->would_block + pid->filter->num_out_pids_not_connected <= pid->filter->num_output_pids);
106 }
107 return;
108 }
109
110 unblock=GF_FALSE;
111
112 assert(pid->playback_speed_scaler);
113
114 //we block according to the number of dispatched units (decoder output) or to the requested buffer duration
115 //for other streams - unblock accordingly
116 if (pid->max_buffer_unit) {
117 if (pid->nb_buffer_unit * GF_FILTER_SPEED_SCALER < pid->max_buffer_unit * pid->playback_speed_scaler) {
118 unblock=GF_TRUE;
119 }
120 } else if (pid->buffer_duration * GF_FILTER_SPEED_SCALER < pid->max_buffer_time * pid->playback_speed_scaler) {
121 unblock=GF_TRUE;
122 }
123
124 gf_mx_p(pid->filter->tasks_mx);
125 if (pid->would_block && unblock) {
126 assert(pid->would_block);
127 safe_int_dec(&pid->would_block);
128
129 assert(pid->filter->would_block);
130 safe_int_dec(&pid->filter->would_block);
131 assert((s32)pid->filter->would_block>=0);
132 assert(pid->filter->would_block + pid->filter->num_out_pids_not_connected <= pid->filter->num_output_pids);
133
134 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s unblocked (filter has %d blocking pids)\n", pid->pid->filter->name, pid->pid->name, pid->pid->filter->would_block));
135
136 if (pid->filter->would_block + pid->filter->num_out_pids_not_connected < pid->filter->num_output_pids) {
137 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has only %d / %d blocked pids, requesting process task (%d queued)\n", pid->filter->name, pid->filter->would_block + pid->filter->num_out_pids_not_connected, pid->filter->num_output_pids, pid->filter->process_task_queued));
138
139 //post a process task
140 gf_filter_post_process_task(pid->filter);
141 }
142 }
143 gf_mx_v(pid->filter->tasks_mx);
144 }
145
gf_filter_pid_inst_check_dependencies(GF_FilterPidInst * pidi)146 static void gf_filter_pid_inst_check_dependencies(GF_FilterPidInst *pidi)
147 {
148 const GF_PropertyValue *p;
149 u32 i, dep_id = 0;
150 GF_FilterPid *pid = pidi->pid;
151 GF_Filter *filter = pid->filter;
152
153 //check pid dependency
154 p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_DEPENDENCY_ID);
155 if (p) dep_id = p->value.uint;
156
157 if (!dep_id) return;
158
159 for (i=0; i<filter->num_output_pids; i++) {
160 u32 j;
161 GF_FilterPid *a_pid = gf_list_get(filter->output_pids, i);
162 if (a_pid==pid) continue;
163 p = gf_filter_pid_get_property_first(a_pid, GF_PROP_PID_ID);
164 if (!p) p = gf_filter_pid_get_property_first(a_pid, GF_PROP_PID_ESID);
165 if (!p || (p->value.uint != dep_id)) continue;
166
167 for (j=0; j<a_pid->num_destinations; j++) {
168 GF_FilterPidInst *a_pidi = gf_list_get(a_pid->destinations, j);
169 if (a_pidi == pidi) continue;
170 if (! a_pidi->is_decoder_input) continue;
171
172 if (a_pidi->filter == pidi->filter) continue;
173
174 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Filter %s PID %s connected to decoder %s, but dependent stream %s connected to %s - switching pid destination\n", a_pid->filter->name, a_pid->name, a_pidi->filter->name, pidi->pid->name, pidi->filter->name));
175
176 //disconnect this pid instance from its current decoder
177 gf_fs_post_task(filter->session, gf_filter_pid_disconnect_task, a_pidi->filter, a_pid, "pidinst_disconnect", NULL);
178
179 //reconnect this pid instance to the new decoder
180 safe_int_inc(&pid->filter->out_pid_connection_pending);
181 gf_filter_pid_post_connect_task(pidi->filter, a_pid);
182
183 }
184 }
185 }
186
gf_filter_pid_update_caps(GF_FilterPid * pid)187 static void gf_filter_pid_update_caps(GF_FilterPid *pid)
188 {
189 u32 mtype=0, codecid=0;
190 u32 i, count;
191 const GF_PropertyValue *p;
192
193 pid->raw_media = GF_FALSE;
194 p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_CODECID);
195 if (p) codecid = p->value.uint;
196
197 p = gf_filter_pid_get_property_first(pid, GF_PROP_PID_STREAM_TYPE);
198 if (p) mtype = p->value.uint;
199
200 pid->stream_type = mtype;
201 pid->codecid = codecid;
202
203 if (pid->user_max_buffer_time) {
204 pid->max_buffer_time = pid->user_max_buffer_time;
205 pid->max_buffer_unit = 0;
206 } else {
207 pid->max_buffer_time = pid->filter->session->default_pid_buffer_max_us;
208 pid->max_buffer_unit = pid->filter->session->default_pid_buffer_max_units;
209 }
210 pid->raw_media = GF_FALSE;
211
212 if (codecid!=GF_CODECID_RAW) {
213 count=pid->filter->num_input_pids;
214 for (i=0; i<count; i++) {
215 GF_FilterPidInst *pidi = gf_list_get(pid->filter->input_pids, i);
216 u32 i_codecid=0, i_type=0;
217 p = gf_filter_pid_get_property_first(pidi->pid, GF_PROP_PID_STREAM_TYPE);
218 if (p) i_type = p->value.uint;
219
220 p = gf_filter_pid_get_property_first(pidi->pid, GF_PROP_PID_CODECID);
221 if (p) i_codecid = p->value.uint;
222 //same stream type but changing from raw to not raw: this is an encoder input pid
223 if ((mtype==i_type) && (i_codecid==GF_CODECID_RAW)) {
224 pidi->is_encoder_input = GF_TRUE;
225 }
226 }
227 return;
228 }
229
230 if (pid->user_max_buffer_time) {
231 pid->max_buffer_time = pid->user_max_buffer_time;
232 pid->max_buffer_unit = 0;
233 }
234
235
236 //output is a decoded raw stream: if some input has same type but different codecid this is a decoder
237 //set input buffer size
238 count=pid->filter->num_input_pids;
239 for (i=0; i<count; i++) {
240 u32 i_codecid=0, i_type=0;
241 GF_FilterPidInst *pidi = gf_list_get(pid->filter->input_pids, i);
242
243 p = gf_filter_pid_get_property_first(pidi->pid, GF_PROP_PID_STREAM_TYPE);
244 if (p) i_type = p->value.uint;
245
246 p = gf_filter_pid_get_property_first(pidi->pid, GF_PROP_PID_CODECID);
247 if (p) i_codecid = p->value.uint;
248
249 //same stream type but changing format type: this is a decoder input pid, set buffer req
250 if ((mtype==i_type) && (codecid != i_codecid)) {
251 //default decoder buffer
252 if (pidi->pid->user_max_buffer_time)
253 pidi->pid->max_buffer_time = pidi->pid->user_max_buffer_time;
254 else
255 pidi->pid->max_buffer_time = pidi->pid->filter->session->decoder_pid_buffer_max_us;
256 pidi->pid->max_buffer_unit = 0;
257
258
259 if (mtype==GF_STREAM_VISUAL) {
260 pid->max_buffer_unit = 4;
261 } else if (mtype==GF_STREAM_AUDIO) {
262 pid->max_buffer_unit = 20;
263 }
264
265 if (!pidi->is_decoder_input) {
266 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s pid instance %s marked as decoder input\n", pidi->pid->filter->name, pidi->pid->name));
267 pidi->is_decoder_input = GF_TRUE;
268 safe_int_inc(&pidi->pid->nb_decoder_inputs);
269
270 if ((i_type == GF_STREAM_AUDIO) || (i_type == GF_STREAM_VISUAL))
271 gf_filter_pid_inst_check_dependencies(pidi);
272 }
273 }
274 //same media type, different codec if is raw stream
275 else if (mtype==i_type) {
276 pid->raw_media = GF_TRUE;
277 }
278 //input is file, output is not and codec ID is raw, this is a raw media pid
279 else if ((i_type==GF_STREAM_FILE) && (mtype!=GF_STREAM_FILE) && (codecid==GF_CODECID_RAW) ) {
280 pid->raw_media = GF_TRUE;
281 }
282 }
283 //source pid, mark raw media
284 if (!count && pid->num_destinations) {
285 pid->raw_media = GF_TRUE;
286 }
287 }
288
289 #define TASK_REQUEUE(_t) \
290 _t->requeue_request = GF_TRUE; \
291 _t->schedule_next_time = gf_sys_clock_high_res() + 50; \
292
gf_filter_pid_inst_delete_task(GF_FSTask * task)293 void gf_filter_pid_inst_delete_task(GF_FSTask *task)
294 {
295 GF_FilterPid *pid = task->pid;
296 GF_FilterPidInst *pidinst = task->udta;
297 GF_Filter *filter = pid->filter;
298 Bool pid_still_alive = GF_FALSE;
299
300 assert(filter);
301 //reset in process
302 if ((pidinst->filter && pidinst->discard_packets) || filter->stream_reset_pending) {
303 TASK_REQUEUE(task)
304 return;
305 }
306
307 //reset PID instance buffers before checking number of output shared packets
308 //otherwise we may block because some of the shared packets are in the
309 //pid instance buffer (not consumed)
310 gf_filter_pid_inst_reset(pidinst);
311
312 //we still have packets out there!
313 if (pidinst->pid->nb_shared_packets_out) {
314 //special case for disconnect of fanouts, destroy even if shared packets out
315 if (!pid->num_destinations || ((pid->num_destinations>=1) && (gf_list_find(pid->destinations, pidinst)>=0))) {
316 TASK_REQUEUE(task)
317 return;
318 }
319 }
320
321 //WARNING at this point pidinst->filter may be destroyed
322 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid instance %s destruction (%d fan-out)\n", filter->name, pid->name, pid->num_destinations));
323 gf_mx_p(filter->tasks_mx);
324 gf_list_del_item(pid->destinations, pidinst);
325 pid->num_destinations = gf_list_count(pid->destinations);
326 if (pidinst->pid->num_pidinst_del_pending) {
327 pidinst->pid->num_pidinst_del_pending--;
328 if (pidinst->pid->num_pidinst_del_pending)
329 pid_still_alive = GF_TRUE;
330 }
331 gf_mx_v(filter->tasks_mx);
332
333 if (pidinst->is_decoder_input) {
334 assert(pid->nb_decoder_inputs);
335 safe_int_dec(&pid->nb_decoder_inputs);
336 }
337 gf_filter_pid_inst_del(pidinst);
338 //recompute max buf dur and nb units to update blocking state
339 if (pid->num_destinations) {
340 u32 i;
341 u32 nb_pck = 0;
342 s64 buf_dur = 0;
343 for (i = 0; i < pid->num_destinations; i++) {
344 GF_FilterPidInst *apidi = gf_list_get(pid->destinations, i);
345 u32 npck = gf_fq_count(apidi->packets);
346 if (npck > nb_pck) nb_pck = npck;
347 if (apidi->buffer_duration > buf_dur) buf_dur = apidi->buffer_duration;
348 }
349 pid->nb_buffer_unit = nb_pck;
350 pid->buffer_duration = buf_dur;
351 } else {
352 pid->nb_buffer_unit = 0;
353 pid->buffer_duration = 0;
354 }
355
356 assert(pid->filter == filter);
357
358 if (pid_still_alive)
359 return;
360
361 //some more destinations on pid, update blocking state
362 if (pid->num_destinations || pid->init_task_pending) {
363 if (pid->would_block)
364 gf_filter_pid_check_unblock(pid);
365 else
366 gf_filter_pid_would_block(pid);
367
368 return;
369 }
370 //still some input to the filter, cannot destroy the output pid
371 if (gf_list_count(filter->input_pids))
372 return;
373 //no more destinations on pid, destroy it
374 if (pid->would_block) {
375 assert(pid->filter->would_block);
376 safe_int_dec(&pid->filter->would_block);
377 }
378
379 gf_mx_p(filter->tasks_mx);
380 gf_list_del_item(filter->output_pids, pid);
381 filter->num_output_pids = gf_list_count(filter->output_pids);
382 gf_filter_pid_del(pid);
383 gf_mx_v(filter->tasks_mx);
384
385 //no more pids on filter, destroy it
386 if (!gf_list_count(filter->output_pids) && !gf_list_count(filter->input_pids)) {
387 gf_filter_post_remove(filter);
388 }
389 }
390
gf_filter_pid_inst_swap_delete(GF_Filter * filter,GF_FilterPid * pid,GF_FilterPidInst * pidinst,GF_FilterPidInst * dst_swapinst)391 void gf_filter_pid_inst_swap_delete(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPidInst *pidinst, GF_FilterPidInst *dst_swapinst)
392 {
393 u32 i, j;
394
395 //reset PID instance buffers before checking number of output shared packets
396 //otherwise we may block because some of the shared packets are in the
397 //pid instance buffer (not consumed)
398 gf_filter_pid_inst_reset(pidinst);
399
400 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid instance %s swap destruction\n", filter->name, pidinst->pid->name));
401 gf_mx_p(filter->tasks_mx);
402 gf_list_del_item(filter->input_pids, pidinst);
403 filter->num_input_pids = gf_list_count(filter->input_pids);
404 gf_mx_v(filter->tasks_mx);
405
406 gf_mx_p(pid->filter->tasks_mx);
407 gf_list_del_item(pid->destinations, pidinst);
408 pid->num_destinations = gf_list_count(pid->destinations);
409 gf_mx_v(pid->filter->tasks_mx);
410
411
412 if (pidinst->is_decoder_input) {
413 assert(pid->nb_decoder_inputs);
414 safe_int_dec(&pid->nb_decoder_inputs);
415 }
416 //this pid instance is registered to destination filter for chain reconfigure, don't discard it
417 if (filter->detached_pid_inst && (gf_list_find(filter->detached_pid_inst, pidinst)>=0) )
418 return;
419
420 gf_filter_pid_inst_del(pidinst);
421
422 if (filter->num_input_pids) return;
423 //we still have other pid instances registered for chain reconfigure, don't discard the filter
424 if (filter->detached_pid_inst) return;
425
426 //filter no longer used, disconnect chain
427 for (i=0; i<filter->num_output_pids; i++) {
428 GF_FilterPid *a_pid = gf_list_get(filter->output_pids, i);
429 for (j=0; j<a_pid->num_destinations; j++) {
430 GF_FilterPidInst *a_pidi = gf_list_get(a_pid->destinations, j);
431 if (a_pidi == dst_swapinst) continue;
432
433 gf_filter_pid_inst_swap_delete(a_pidi->filter, a_pid, a_pidi, dst_swapinst);
434 }
435 }
436 filter->swap_pidinst_dst = NULL;
437 filter->swap_pidinst_src = NULL;
438 gf_filter_post_remove(filter);
439 }
440
gf_filter_pid_inst_swap_delete_task(GF_FSTask * task)441 void gf_filter_pid_inst_swap_delete_task(GF_FSTask *task)
442 {
443 GF_FilterPidInst *pidinst = task->udta;
444 GF_Filter *filter = pidinst->filter;
445 GF_FilterPid *pid = task->pid ? task->pid : pidinst->pid;
446 GF_FilterPidInst *dst_swapinst = pidinst->filter->swap_pidinst_dst;
447
448 //reset in process
449 if ((pidinst->filter && pidinst->discard_packets)
450 || filter->stream_reset_pending
451 || filter->nb_shared_packets_out
452 ) {
453 TASK_REQUEUE(task)
454 return;
455 }
456 if (pidinst->filter)
457 pidinst->filter->swap_pidinst_dst = NULL;
458
459 gf_filter_pid_inst_swap_delete(filter, pid, pidinst, dst_swapinst);
460 }
461
gf_filter_pid_inst_swap(GF_Filter * filter,GF_FilterPidInst * dst)462 void gf_filter_pid_inst_swap(GF_Filter *filter, GF_FilterPidInst *dst)
463 {
464 GF_PropertyMap *prev_dst_props;
465 u32 nb_pck_transfer=0;
466 GF_FilterPidInst *src = filter->swap_pidinst_src;
467 if (!src) src = filter->swap_pidinst_dst;
468
469 if (src) {
470 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s swaping PID %s to PID %s\n", filter->name, src->pid->name, dst->pid->name));
471 }
472
473
474 if (filter->swap_needs_init) {
475 //we are in detach state, the packet queue of the old PID is never read
476 assert(filter->swap_pidinst_dst && filter->swap_pidinst_dst->detach_pending);
477 //we are in pending stete, the origin of the old PID is never dispatching
478 assert(dst->pid && dst->pid->filter && dst->pid->filter->out_pid_connection_pending);
479 //we can therefore swap the packet queues safely and other important info
480 }
481 //otherwise we actually swap the pid instance on the same PID
482 else {
483 gf_mx_p(dst->pid->filter->tasks_mx);
484 if (src)
485 gf_list_del_item(dst->pid->destinations, src);
486 if (gf_list_find(dst->pid->destinations, dst)<0)
487 gf_list_add(dst->pid->destinations, dst);
488 if (gf_list_find(dst->filter->input_pids, dst)<0) {
489 gf_list_add(dst->filter->input_pids, dst);
490 dst->filter->num_input_pids = gf_list_count(dst->filter->input_pids);
491 }
492 gf_mx_v(dst->pid->filter->tasks_mx);
493 }
494
495 if (src) {
496 GF_FilterPacketInstance *pcki;
497 while (1) {
498 pcki = gf_fq_pop(src->packets);
499 if (!pcki) break;
500 assert(src->filter->pending_packets);
501 safe_int_dec(&src->filter->pending_packets);
502
503 pcki->pid = dst;
504 gf_fq_add(dst->packets, pcki);
505 safe_int_inc(&dst->filter->pending_packets);
506 nb_pck_transfer++;
507 }
508 if (src->requires_full_data_block && gf_list_count(src->pck_reassembly)) {
509 dst->requires_full_data_block = src->requires_full_data_block;
510 dst->last_block_ended = src->last_block_ended;
511 dst->first_block_started = src->first_block_started;
512 if (!dst->pck_reassembly) dst->pck_reassembly = gf_list_new();
513 while (gf_list_count(src->pck_reassembly)) {
514 pcki = gf_list_pop_front(src->pck_reassembly);
515 pcki->pid = dst;
516 gf_list_add(dst->pck_reassembly, pcki);
517 }
518 }
519 //copy over state
520 dst->is_end_of_stream = src->is_end_of_stream;
521 dst->nb_eos_signaled = src->nb_eos_signaled;
522 dst->buffer_duration = src->buffer_duration;
523 dst->nb_clocks_signaled = src->nb_clocks_signaled;
524
525 //switch previous src property map to this new pid (this avoids rewriting props of already dispatched packets)
526 //it may happen that we already have props on dest, due to configure of the pid
527 //use the old props as new ones and merge the previous props of dst in the new props
528 prev_dst_props = dst->props;
529 dst->props = src->props;
530 dst->force_reconfig = GF_TRUE;
531 src->force_reconfig = GF_TRUE;
532 src->props = NULL;
533 if (prev_dst_props) {
534 gf_props_merge_property(dst->props, prev_dst_props, NULL, NULL);
535 assert(prev_dst_props->reference_count);
536 if (safe_int_dec(&prev_dst_props->reference_count) == 0) {
537 gf_props_del(prev_dst_props);
538 }
539 }
540
541 if (nb_pck_transfer && !dst->filter->process_task_queued) {
542 gf_filter_post_process_task(dst->filter);
543 }
544 }
545
546
547 src = filter->swap_pidinst_dst;
548 if (src) {
549 if (filter->swap_needs_init) {
550 //exit out special handling of the pid since we are ready to detach
551 assert(src->filter->stream_reset_pending);
552 safe_int_dec(&src->filter->stream_reset_pending);
553
554 //post detach task, we will reset the swap_pidinst only once truly deconnected from filter
555 safe_int_inc(&src->pid->filter->detach_pid_tasks_pending);
556 safe_int_inc(&filter->detach_pid_tasks_pending);
557 gf_fs_post_task(filter->session, gf_filter_pid_detach_task, src->filter, src->pid, "pidinst_detach", filter);
558 } else {
559 GF_Filter *src_filter = src->filter;
560 assert(!src->filter->sticky);
561 assert(src->filter->num_input_pids==1);
562
563 gf_mx_p(src_filter->tasks_mx);
564 gf_list_del_item(src_filter->input_pids, src);
565 src_filter->num_input_pids = gf_list_count(src_filter->input_pids);
566 gf_mx_v(src_filter->tasks_mx);
567
568 gf_list_del_item(src->pid->destinations, src);
569 src->pid->num_destinations = gf_list_count(src->pid->destinations);
570 gf_filter_pid_inst_del(src);
571
572 filter->swap_pidinst_dst = NULL;
573 filter->swap_pidinst_src = NULL;
574 gf_filter_post_remove(src_filter);
575 }
576 }
577
578 if (filter->swap_pidinst_src) {
579 src = filter->swap_pidinst_src;
580 src->filter->swap_pidinst_dst = filter->swap_pidinst_dst;
581 gf_fs_post_task(filter->session, gf_filter_pid_inst_swap_delete_task, src->filter, src->pid, "pid_inst_delete", src);
582 }
583 }
584
585
586 typedef enum {
587 GF_PID_CONF_CONNECT,
588 GF_PID_CONF_RECONFIG,
589 GF_PID_CONF_REMOVE,
590 } GF_PidConnectType;
591
gf_filter_pid_configure(GF_Filter * filter,GF_FilterPid * pid,GF_PidConnectType ctype)592 static GF_Err gf_filter_pid_configure(GF_Filter *filter, GF_FilterPid *pid, GF_PidConnectType ctype)
593 {
594 u32 i, count;
595 GF_Err e;
596 Bool new_pid_inst=GF_FALSE;
597 GF_FilterPidInst *pidinst=NULL;
598 GF_Filter *alias_orig = NULL;
599
600 if (filter->multi_sink_target) {
601 alias_orig = filter;
602 filter = filter->multi_sink_target;
603 }
604
605 assert(filter->freg->configure_pid);
606 if (filter->finalized) {
607 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Trying to configure PID %s in finalized filter %s\n", pid->name, filter->name));
608 if (ctype==GF_PID_CONF_CONNECT) {
609 assert(pid->filter->out_pid_connection_pending);
610 safe_int_dec(&pid->filter->out_pid_connection_pending);
611 }
612 return GF_SERVICE_ERROR;
613 }
614
615 if (filter->detached_pid_inst) {
616 count = gf_list_count(filter->detached_pid_inst);
617 for (i=0; i<count; i++) {
618 pidinst = gf_list_get(filter->detached_pid_inst, i);
619 if (pidinst->filter==filter) {
620 gf_list_rem(filter->detached_pid_inst, i);
621 //reattach new filter and pid
622 pidinst->filter = filter;
623 pidinst->pid = pid;
624
625 assert(!pidinst->props);
626
627 //and treat as new pid inst
628 if (ctype == GF_PID_CONF_CONNECT) {
629 new_pid_inst=GF_TRUE;
630 }
631 assert(pidinst->detach_pending);
632 safe_int_dec(&pidinst->detach_pending);
633 break;
634 }
635 pidinst=NULL;
636 }
637 if (! gf_list_count(filter->detached_pid_inst)) {
638 gf_list_del(filter->detached_pid_inst);
639 filter->detached_pid_inst = NULL;
640 }
641 }
642 if (!pidinst) {
643 count = pid->num_destinations;
644 for (i=0; i<count; i++) {
645 pidinst = gf_list_get(pid->destinations, i);
646 if (pidinst->filter==filter) {
647 break;
648 }
649 pidinst=NULL;
650 }
651 }
652
653 //first connection of this PID to this filter
654 if (!pidinst) {
655 if (ctype != GF_PID_CONF_CONNECT) {
656 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Trying to disconnect PID %s not found in filter %s inputs\n", pid->name, filter->name));
657 return GF_SERVICE_ERROR;
658 }
659 pidinst = gf_filter_pid_inst_new(filter, pid);
660 new_pid_inst=GF_TRUE;
661 pidinst->alias_orig = alias_orig;
662 }
663
664 //if new, add the PID to input/output before calling configure
665 if (new_pid_inst) {
666 assert(pidinst);
667 gf_mx_p(pid->filter->tasks_mx);
668
669 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Registering %s:%s as destination for %s:%s\n", pid->filter->name, pid->name, pidinst->filter->name, pidinst->pid->name));
670 gf_list_add(pid->destinations, pidinst);
671 pid->num_destinations = gf_list_count(pid->destinations);
672
673 if (!filter->input_pids) filter->input_pids = gf_list_new();
674 gf_list_add(filter->input_pids, pidinst);
675 filter->num_input_pids = gf_list_count(filter->input_pids);
676
677 gf_mx_v(pid->filter->tasks_mx);
678
679 //new connection, update caps in case we have events using caps (buffer req) being sent
680 //while processing the configure (they would be dispatched on the source filter, not the dest one being
681 //processed here)
682 gf_filter_pid_update_caps(pid);
683 }
684
685 //we are swaping a PID instance (dyn insert of a filter), do it before reconnecting
686 //in order to have properties in place
687 //TODO: handle error case, we might need to re-switch the pid inst!
688 if (filter->swap_pidinst_src || filter->swap_pidinst_dst) {
689 gf_filter_pid_inst_swap(filter, pidinst);
690 }
691
692 filter->in_connect_err = GF_EOS;
693 //commented out: audio thread may be pulling packets out of the pid but not in the compositor:process, which
694 //could be called for video at the same time...
695 #if 0
696 FSESS_CHECK_THREAD(filter)
697 #endif
698
699 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s reconfigure\n", pidinst->filter->name, pidinst->pid->name));
700 e = filter->freg->configure_pid(filter, (GF_FilterPid*) pidinst, (ctype==GF_PID_CONF_REMOVE) ? GF_TRUE : GF_FALSE);
701
702 #ifdef GPAC_MEMORY_TRACKING
703 if (filter->session->check_allocs) {
704 if (filter->nb_consecutive_process >= filter->max_nb_consecutive_process) {
705 filter->max_nb_consecutive_process = filter->nb_consecutive_process;
706 filter->max_nb_process = filter->nb_process_since_reset;
707 filter->max_stats_nb_alloc = filter->stats_nb_alloc;
708 filter->max_stats_nb_calloc = filter->stats_nb_calloc;
709 filter->max_stats_nb_realloc = filter->stats_nb_realloc;
710 filter->max_stats_nb_free = filter->stats_nb_free;
711 }
712 filter->stats_mem_allocated = 0;
713 filter->stats_nb_alloc = filter->stats_nb_realloc = filter->stats_nb_free = 0;
714 filter->nb_process_since_reset = filter->nb_consecutive_process = 0;
715 }
716 #endif
717 if ((e==GF_OK) && (filter->in_connect_err<GF_OK))
718 e = filter->in_connect_err;
719
720 filter->in_connect_err = GF_OK;
721
722 if (e==GF_OK) {
723 //if new, register the new pid instance, and the source pid as input to this filter
724 if (new_pid_inst) {
725 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Connected filter %s (%p) PID %s (%p) (%d fan-out) to filter %s (%p)\n", pid->filter->name, pid->filter, pid->name, pid, pid->num_destinations, filter->name, filter));
726 }
727 }
728 //failure on reconfigure, try reloading a filter chain
729 else if ((ctype==GF_PID_CONF_RECONFIG) && (e != GF_FILTER_NOT_SUPPORTED)) {
730 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to reconfigure PID %s:%s in filter %s: %s, reloading filter graph\n", pid->filter->name, pid->name, filter->name, gf_error_to_string(e) ));
731
732 if (e==GF_BAD_PARAM) {
733 filter->session->last_connect_error = e;
734 } else {
735 gf_filter_relink_dst(pidinst);
736 }
737 } else {
738
739 //error, remove from input and output
740 gf_mx_p(filter->tasks_mx);
741 gf_list_del_item(filter->input_pids, pidinst);
742 filter->num_input_pids = gf_list_count(filter->input_pids);
743 filter->freg->configure_pid(filter, (GF_FilterPid *) pidinst, GF_TRUE);
744 gf_mx_v(filter->tasks_mx);
745
746 gf_mx_p(pidinst->pid->filter->tasks_mx);
747 gf_list_del_item(pidinst->pid->destinations, pidinst);
748 pidinst->pid->num_destinations = gf_list_count(pidinst->pid->destinations);
749 //detach filter from pid instance
750 pidinst->filter = NULL;
751 gf_mx_v(pidinst->pid->filter->tasks_mx);
752
753 //if connect and error, direct delete of pid
754 if (new_pid_inst) {
755 gf_mx_p(pid->filter->tasks_mx);
756 gf_list_del_item(pid->destinations, pidinst);
757 pid->num_destinations = gf_list_count(pid->destinations);
758 //destroy pid instance
759 gf_filter_pid_inst_del(pidinst);
760 gf_mx_v(pid->filter->tasks_mx);
761 }
762
763
764 if (e==GF_REQUIRES_NEW_INSTANCE) {
765 //TODO: copy over args from current filter
766 GF_Filter *new_filter = gf_filter_clone(filter);
767 if (new_filter) {
768 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Clone filter %s, new instance for pid %s\n", filter->name, pid->name));
769 gf_filter_pid_post_connect_task(new_filter, pid);
770 return GF_OK;
771 } else {
772 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to clone filter %s\n", filter->name));
773 e = GF_OUT_OF_MEM;
774 }
775 }
776 if (e && (ctype==GF_PID_CONF_REMOVE)) {
777 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to disconnect filter %s PID %s from filter %s: %s\n", pid->filter->name, pid->name, filter->name, gf_error_to_string(e) ));
778 }
779 else if (e) {
780 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to connect filter %s PID %s to filter %s: %s\n", pid->filter->name, pid->name, filter->name, gf_error_to_string(e) ));
781
782 if ((e==GF_BAD_PARAM) || (e==GF_FILTER_NOT_SUPPORTED) || (filter->session->flags & GF_FS_FLAG_NO_REASSIGN)) {
783 if (e!=GF_FILTER_NOT_SUPPORTED) {
784 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Filter reassignment disabled, skippping chain reload for filter %s PID %s\n", pid->filter->name, pid->name ));
785 }
786 filter->session->last_connect_error = e;
787
788 if (ctype==GF_PID_CONF_CONNECT) {
789 GF_FilterEvent evt;
790 GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
791 gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
792
793 GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
794 gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
795
796 gf_filter_pid_set_eos(pid);
797
798 if (pid->filter->freg->process_event) {
799 GF_FEVT_INIT(evt, GF_FEVT_CONNECT_FAIL, pid);
800 gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
801 }
802 }
803 } else if (filter->has_out_caps) {
804 Bool unload_filter = GF_TRUE;
805 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Blacklisting %s as output from %s and retrying connections\n", filter->name, pid->filter->name));
806 //try to load another filter to handle that connection
807 //1-blacklist this filter
808 gf_list_add(pid->filter->blacklisted, (void *) filter->freg);
809 //2-disconnect all other inputs, and post a re-init
810 gf_mx_p(filter->tasks_mx);
811 while (gf_list_count(filter->input_pids)) {
812 GF_FilterPidInst *a_pidinst = gf_list_pop_back(filter->input_pids);
813 FSESS_CHECK_THREAD(filter)
814 filter->freg->configure_pid(filter, (GF_FilterPid *) a_pidinst, GF_TRUE);
815
816 gf_filter_pid_post_init_task(a_pidinst->pid->filter, a_pidinst->pid);
817 gf_fs_post_task(filter->session, gf_filter_pid_inst_delete_task, a_pidinst->pid->filter, a_pidinst->pid, "pid_inst_delete", a_pidinst);
818
819 unload_filter = GF_FALSE;
820 }
821 filter->num_input_pids = 0;
822 filter->removed = GF_TRUE;
823 filter->has_pending_pids = GF_FALSE;
824 gf_mx_v(filter->tasks_mx);
825
826 //do not assign session->last_connect_error since we are retrying a connection
827
828 if (ctype==GF_PID_CONF_CONNECT) {
829 assert(pid->filter->out_pid_connection_pending);
830 safe_int_dec(&pid->filter->out_pid_connection_pending);
831 }
832 //3- post a re-init on this pid
833 gf_filter_pid_post_init_task(pid->filter, pid);
834
835 if (unload_filter) {
836 assert(!gf_list_count(filter->input_pids));
837
838 if (filter->num_output_pids) {
839 for (i=0; i<filter->num_output_pids; i++) {
840 u32 j;
841 GF_FilterPid *opid = gf_list_get(filter->output_pids, i);
842 for (j=0; j< opid->num_destinations; j++) {
843 GF_FilterPidInst *a_pidi = gf_list_get(opid->destinations, j);
844 a_pidi->pid = NULL;
845 }
846 gf_list_reset(opid->destinations);
847 opid->num_destinations = 0;
848 gf_filter_pid_remove(opid);
849 }
850 }
851 filter->swap_pidinst_src = NULL;
852 if (filter->swap_pidinst_dst) {
853 GF_Filter *target = filter->swap_pidinst_dst->filter;
854 assert(target);
855 if (!target->detached_pid_inst) {
856 target->detached_pid_inst = gf_list_new();
857 }
858 //detach props but don't delete them
859 if (filter->swap_pidinst_dst->props) {
860 filter->swap_pidinst_dst->props = NULL;
861 }
862 filter->swap_pidinst_dst->pid = NULL;
863 if (gf_list_find(target->detached_pid_inst, filter->swap_pidinst_dst)<0)
864 gf_list_add(target->detached_pid_inst, filter->swap_pidinst_dst);
865 }
866 filter->swap_pidinst_dst = NULL;
867 if (filter->on_setup_error) {
868 gf_filter_notification_failure(filter, e, GF_TRUE);
869 } else {
870 gf_filter_post_remove(filter);
871 }
872 }
873 return e;
874 } else {
875 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to reconfigure input of sink %s, cannot rebuild graph\n", filter->name));
876 if (pid->filter->freg->process_event) {
877 GF_FilterEvent evt;
878 GF_FEVT_INIT(evt, GF_FEVT_CONNECT_FAIL, pid);
879 pid->filter->freg->process_event(pid->filter, &evt);
880 }
881 filter->session->last_connect_error = e;
882 }
883 } else {
884 filter->session->last_connect_error = GF_OK;
885 }
886
887 //try to run filter no matter what
888 if (filter->session->requires_solved_graph)
889 return e;
890 }
891
892 //flush all pending pid init requests following the call to init
893 if (filter->has_pending_pids) {
894 filter->has_pending_pids = GF_FALSE;
895 while (gf_fq_count(filter->pending_pids)) {
896 GF_FilterPid *a_pid=gf_fq_pop(filter->pending_pids);
897
898 gf_filter_pid_post_init_task(filter, a_pid);
899 }
900 }
901
902 if (ctype==GF_PID_CONF_REMOVE) {
903 gf_mx_p(filter->tasks_mx);
904 gf_list_del_item(filter->input_pids, pidinst);
905 filter->num_input_pids = gf_list_count(filter->input_pids);
906 gf_mx_v(filter->tasks_mx);
907
908 //PID instance is no longer in graph, we must remove it from pid destination to avoid propagating events
909 //on to-be freed pid instance.
910 //however we must have fan-outs (N>1 pid instance per PID), and removing the pid inst would trigger a pid destruction
911 //on the first gf_filter_pid_inst_delete_task executed.
912 //we therefore track at the PID level the number of gf_filter_pid_inst_delete_task tasks pending and
913 //won't destroy the PID until that number is O
914 gf_mx_p(pidinst->pid->filter->tasks_mx);
915 pidinst->pid->num_pidinst_del_pending ++;
916 gf_list_del_item(pidinst->pid->destinations, pidinst);
917 pidinst->pid->num_destinations = gf_list_count(pidinst->pid->destinations);
918 pidinst->filter = NULL;
919 gf_mx_v(pidinst->pid->filter->tasks_mx);
920
921 //disconnected the last input, flag as removed
922 if (!filter->num_input_pids && !filter->sticky) {
923 gf_filter_reset_pending_packets(filter);
924 filter->removed = GF_TRUE;
925 }
926 //post a pid_delete task to also trigger removal of the filter if needed
927 gf_fs_post_task(filter->session, gf_filter_pid_inst_delete_task, pid->filter, pid, "pid_inst_delete", pidinst);
928
929 return e;
930 }
931
932 if (ctype==GF_PID_CONF_CONNECT) {
933 assert(pid->filter->out_pid_connection_pending);
934 if (safe_int_dec(&pid->filter->out_pid_connection_pending) == 0) {
935
936 if (e==GF_OK) {
937 //postponed packets dispatched by source while setting up PID, flush through process()
938 //pending packets (not yet consumed but in PID buffer), start processing
939 if (pid->filter->postponed_packets || pid->filter->pending_packets || pid->filter->nb_caps_renegociate) {
940 gf_filter_post_process_task(pid->filter);
941 }
942 }
943 }
944 }
945 //once all pid have been (re)connected, update any internal caps
946 gf_filter_pid_update_caps(pid);
947 return e;
948 }
949
gf_filter_pid_connect_task(GF_FSTask * task)950 static void gf_filter_pid_connect_task(GF_FSTask *task)
951 {
952 GF_Filter *filter = task->filter;
953 GF_FilterSession *fsess = filter->session;
954 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid %s connecting to %s\n", task->pid->pid->filter->name, task->pid->pid->name, task->filter->name));
955
956 //filter will require a new instance, clone it
957 if (filter->num_input_pids && (filter->max_extra_pids <= filter->num_input_pids - 1)) {
958 GF_Filter *new_filter = gf_filter_clone(filter);
959 if (new_filter) {
960 filter = new_filter;
961 } else {
962 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to clone filter %s\n", filter->name));
963 assert(filter->in_pid_connection_pending);
964 safe_int_dec(&filter->in_pid_connection_pending);
965 return;
966 }
967 }
968 if (task->pid->pid) {
969 gf_filter_pid_configure(filter, task->pid->pid, GF_PID_CONF_CONNECT);
970 //once connected, any set_property before the first packet dispatch will have to trigger a reconfigure
971 if (!task->pid->pid->nb_pck_sent) {
972 task->pid->pid->request_property_map = GF_TRUE;
973 task->pid->pid->pid_info_changed = GF_FALSE;
974 }
975 }
976
977 //filter may now be the clone, decrement on original filter
978 assert(task->filter->in_pid_connection_pending);
979 safe_int_dec(&task->filter->in_pid_connection_pending);
980
981 gf_fs_cleanup_filters(fsess);
982
983 }
984
gf_filter_pid_reconfigure_task(GF_FSTask * task)985 void gf_filter_pid_reconfigure_task(GF_FSTask *task)
986 {
987 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid %s reconfigure to %s\n", task->pid->pid->filter->name, task->pid->pid->name, task->filter->name));
988
989 if (task->pid->pid) {
990 gf_filter_pid_configure(task->filter, task->pid->pid, GF_PID_CONF_RECONFIG);
991 //once connected, any set_property before the first packet dispatch will have to trigger a reconfigure
992 if (!task->pid->pid->nb_pck_sent) {
993 task->pid->pid->request_property_map = GF_TRUE;
994 task->pid->pid->pid_info_changed = GF_FALSE;
995 }
996 }
997 }
998
gf_filter_pid_disconnect_task(GF_FSTask * task)999 void gf_filter_pid_disconnect_task(GF_FSTask *task)
1000 {
1001 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid %s disconnect from %s\n", task->pid->pid->filter->name, task->pid->pid->name, task->filter->name));
1002 gf_filter_pid_configure(task->filter, task->pid->pid, GF_PID_CONF_REMOVE);
1003
1004 //if the filter has no more connected ins and outs, remove it
1005 if (task->filter->removed && !gf_list_count(task->filter->output_pids) && !gf_list_count(task->filter->input_pids)) {
1006 Bool direct_mode = task->filter->session->direct_mode;
1007 gf_filter_post_remove(task->filter);
1008 if (direct_mode) task->filter = NULL;
1009 }
1010 }
1011
gf_filter_pid_detach_task(GF_FSTask * task)1012 void gf_filter_pid_detach_task(GF_FSTask *task)
1013 {
1014 u32 i, count;
1015 GF_Filter *filter = task->filter;
1016 GF_FilterPid *pid = task->pid->pid;
1017 GF_FilterPidInst *pidinst=NULL;
1018 GF_Filter *new_chain_input = task->udta;
1019
1020 //we may have concurrent reset (due to play/stop/seek) and caps renegotiation
1021 //wait for the pid to be reset before detaching
1022 if (pid->filter->stream_reset_pending) {
1023 TASK_REQUEUE(task)
1024 return;
1025 }
1026 if (new_chain_input->in_pid_connection_pending) {
1027 TASK_REQUEUE(task)
1028 return;
1029 }
1030
1031 assert(filter->freg->configure_pid);
1032 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid %s detach from %s\n", task->pid->pid->filter->name, task->pid->pid->name, task->filter->name));
1033
1034 assert(pid->filter->detach_pid_tasks_pending);
1035 safe_int_dec(&pid->filter->detach_pid_tasks_pending);
1036 count = pid->num_destinations;
1037 for (i=0; i<count; i++) {
1038 pidinst = gf_list_get(pid->destinations, i);
1039 if (pidinst->filter==filter) {
1040 break;
1041 }
1042 pidinst=NULL;
1043 }
1044
1045 //first connection of this PID to this filter
1046 if (!pidinst) {
1047 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Trying to detach PID %s not found in filter %s inputs\n", pid->name, filter->name));
1048 assert(!new_chain_input->swap_pidinst_dst);
1049 assert(!new_chain_input->swap_pidinst_src);
1050 new_chain_input->swap_needs_init = GF_FALSE;
1051 return;
1052 }
1053
1054 //detach props
1055 if (pidinst->props) {
1056 assert(pidinst->props->reference_count);
1057 if (safe_int_dec(& pidinst->props->reference_count) == 0) {
1058 //see \ref gf_filter_pid_merge_properties_internal for mutex
1059 gf_mx_p(pidinst->pid->filter->tasks_mx);
1060 gf_list_del_item(pidinst->pid->properties, pidinst->props);
1061 gf_mx_v(pidinst->pid->filter->tasks_mx);
1062 gf_props_del(pidinst->props);
1063 }
1064 }
1065 pidinst->props = NULL;
1066
1067 gf_mx_p(filter->tasks_mx);
1068 //detach pid - remove all packets in our pid instance and alos update filter pending_packets
1069 count = gf_fq_count(pidinst->packets);
1070 assert(!count || (count >= filter->pending_packets));
1071 safe_int_sub(&filter->pending_packets, (s32) count);
1072 gf_filter_pid_inst_reset(pidinst);
1073 pidinst->pid = NULL;
1074 gf_list_del_item(pid->destinations, pidinst);
1075 pid->num_destinations = gf_list_count(pid->destinations);
1076 gf_list_del_item(filter->input_pids, pidinst);
1077 filter->num_input_pids = gf_list_count(filter->input_pids);
1078 gf_mx_v(filter->tasks_mx);
1079
1080 if (!filter->detached_pid_inst) {
1081 filter->detached_pid_inst = gf_list_new();
1082 }
1083 if (gf_list_find(filter->detached_pid_inst, pidinst)<0)
1084 gf_list_add(filter->detached_pid_inst, pidinst);
1085
1086 //we are done, reset filter swap instance so that connection can take place
1087 if (new_chain_input->swap_needs_init) {
1088 new_chain_input->swap_pidinst_dst = NULL;
1089 new_chain_input->swap_pidinst_src = NULL;
1090 new_chain_input->swap_needs_init = GF_FALSE;
1091 }
1092 assert(new_chain_input->detach_pid_tasks_pending);
1093 safe_int_dec(&new_chain_input->detach_pid_tasks_pending);
1094 }
1095
1096 GF_EXPORT
gf_filter_pid_set_name(GF_FilterPid * pid,const char * name)1097 void gf_filter_pid_set_name(GF_FilterPid *pid, const char *name)
1098 {
1099 if (PID_IS_INPUT(pid)) {
1100 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Attempt to assign name %s to input PID %s in filter %s - ignoring\n", name, pid->pid->name, pid->pid->filter->name));
1101 } else if (name) {
1102 if (pid->name && !strcmp(pid->name, name)) return;
1103 if (pid->name) gf_free(pid->name);
1104 pid->name = gf_strdup(name);
1105 }
1106 }
1107
1108 GF_EXPORT
gf_filter_pid_get_name(GF_FilterPid * pid)1109 const char *gf_filter_pid_get_name(GF_FilterPid *pid)
1110 {
1111 return pid->pid->name;
1112 }
1113
1114 GF_EXPORT
gf_filter_pid_get_filter_name(GF_FilterPid * pid)1115 const char *gf_filter_pid_get_filter_name(GF_FilterPid *pid)
1116 {
1117 if (PID_IS_INPUT(pid)) {
1118 return pid->pid->filter->name;
1119 }
1120 return pid->filter->name;
1121 }
1122
1123 GF_EXPORT
gf_filter_pid_orig_src_args(GF_FilterPid * pid)1124 const char *gf_filter_pid_orig_src_args(GF_FilterPid *pid)
1125 {
1126 u32 i;
1127 const char *args;
1128 //move to true source pid
1129 pid = pid->pid;
1130 args = pid->filter->src_args;
1131 if (args && strstr(args, "src")) return args;
1132 if (!pid->filter->num_input_pids) return args;
1133 for (i=0; i<pid->filter->num_input_pids; i++) {
1134 GF_FilterPidInst *pidi = gf_list_get(pid->filter->input_pids, i);
1135 const char *arg_src = gf_filter_pid_orig_src_args(pidi->pid);
1136 if (arg_src) return arg_src;
1137 }
1138 return args;
1139 }
1140
1141 GF_EXPORT
gf_filter_pid_get_source_filter_name(GF_FilterPid * pid)1142 const char *gf_filter_pid_get_source_filter_name(GF_FilterPid *pid)
1143 {
1144 GF_Filter *filter = pid->pid->filter;
1145 while (filter && filter->num_input_pids) {
1146 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, 0);
1147 filter = pidi->pid->filter;
1148 }
1149 if (!filter) return NULL;
1150 return filter->name ? filter->name : filter->freg->name;
1151 }
1152
1153 GF_EXPORT
gf_filter_pid_get_buffer_occupancy(GF_FilterPid * pid,u32 * max_slots,u32 * nb_pck,u32 * max_duration,u32 * duration)1154 Bool gf_filter_pid_get_buffer_occupancy(GF_FilterPid *pid, u32 *max_slots, u32 *nb_pck, u32 *max_duration, u32 *duration)
1155 {
1156 if (max_slots) *max_slots = pid->pid->max_buffer_unit;
1157 if (max_duration) *max_duration = (u32) pid->pid->max_buffer_time;
1158
1159 if (pid->filter->session->in_final_flush) {
1160 if (duration) *duration = (u32) pid->pid->max_buffer_time;
1161 if (nb_pck) *nb_pck = pid->pid->nb_buffer_unit;
1162 return GF_FALSE;
1163 }
1164 if (nb_pck) *nb_pck = pid->pid->nb_buffer_unit;
1165 if (duration) *duration = (u32) pid->pid->buffer_duration;
1166 return GF_TRUE;
1167 }
1168
1169 GF_EXPORT
gf_filter_pid_set_udta(GF_FilterPid * pid,void * udta)1170 void gf_filter_pid_set_udta(GF_FilterPid *pid, void *udta)
1171 {
1172 if (PID_IS_INPUT(pid)) {
1173 ((GF_FilterPidInst *)pid)->udta = udta;
1174 } else {
1175 pid->udta = udta;
1176 }
1177 }
1178
1179 GF_EXPORT
gf_filter_pid_get_udta(GF_FilterPid * pid)1180 void *gf_filter_pid_get_udta(GF_FilterPid *pid)
1181 {
1182 if (PID_IS_INPUT(pid)) {
1183 return ((GF_FilterPidInst *)pid)->udta;
1184 } else {
1185 return pid->udta;
1186 }
1187 }
1188
filter_pid_check_fragment(GF_FilterPid * src_pid,char * frag_name,Bool * pid_excluded,Bool * needs_resolve,Bool * prop_not_found,char prop_dump_buffer[GF_PROP_DUMP_ARG_SIZE])1189 static Bool filter_pid_check_fragment(GF_FilterPid *src_pid, char *frag_name, Bool *pid_excluded, Bool *needs_resolve, Bool *prop_not_found, char prop_dump_buffer[GF_PROP_DUMP_ARG_SIZE])
1190 {
1191 char *psep;
1192 u32 comp_type=0;
1193 Bool is_neg = GF_FALSE;
1194 const GF_PropertyEntry *pent;
1195
1196 *needs_resolve = GF_FALSE;
1197 *prop_not_found = GF_FALSE;
1198
1199 if (frag_name[0] == src_pid->filter->session->sep_neg) {
1200 frag_name++;
1201 is_neg = GF_TRUE;
1202 }
1203 //special case for stream types filters
1204 pent = gf_filter_pid_get_property_entry(src_pid, GF_PROP_PID_STREAM_TYPE);
1205 if (pent) {
1206 u32 matched=0;
1207 u32 type=0;
1208 if (!strnicmp(frag_name, "audio", 5)) {
1209 matched=5;
1210 type=GF_STREAM_AUDIO;
1211 } else if (!strnicmp(frag_name, "video", 5)) {
1212 matched=5;
1213 type=GF_STREAM_VISUAL;
1214 } else if (!strnicmp(frag_name, "scene", 5)) {
1215 matched=5;
1216 type=GF_STREAM_SCENE;
1217 } else if (!strnicmp(frag_name, "font", 4)) {
1218 matched=4;
1219 type=GF_STREAM_FONT;
1220 } else if (!strnicmp(frag_name, "text", 4)) {
1221 matched=4;
1222 type=GF_STREAM_TEXT;
1223 }
1224 if (matched && (type != pent->prop.value.uint)) {
1225 //special case: if we request a non-file stream but the pid is a file, we will need a demux to
1226 //move from file to A/V/... streams, so we accept any #MEDIA from file streams
1227 if (pent->prop.value.uint == GF_STREAM_FILE) {
1228 *prop_not_found = GF_TRUE;
1229 return GF_TRUE;
1230 }
1231 *pid_excluded = GF_TRUE;
1232 return GF_FALSE;
1233 }
1234
1235 if (matched) {
1236 u32 idx=0;
1237 u32 k, count_pid;
1238 if (strlen(frag_name)==matched) return GF_TRUE;
1239 idx = atoi(frag_name+matched);
1240 count_pid = src_pid->filter->num_output_pids;
1241 for (k=0; k<count_pid; k++) {
1242 GF_FilterPid *p = gf_list_get(src_pid->filter->output_pids, k);
1243 pent = gf_filter_pid_get_property_entry(src_pid, GF_PROP_PID_STREAM_TYPE);
1244 if (pent && pent->prop.value.uint==type) {
1245 idx--;
1246 if (!idx) {
1247 if (p==src_pid) return GF_TRUE;
1248 break;
1249 }
1250 }
1251 }
1252 *pid_excluded = GF_TRUE;
1253 return GF_FALSE;
1254 }
1255 }
1256 //special case for codec type filters
1257 if (!strcmp(frag_name, "raw")) {
1258 pent = gf_filter_pid_get_property_entry(src_pid, GF_PROP_PID_CODECID);
1259 if (pent) {
1260 Bool is_eq = (pent->prop.value.uint==GF_CODECID_RAW) ? GF_TRUE : GF_FALSE;
1261 if (is_neg) is_eq = !is_eq;
1262 if (is_eq) return GF_TRUE;
1263 *pid_excluded = GF_TRUE;
1264 return GF_FALSE;
1265 }
1266 //not codec ID set for pid, assume match
1267 return GF_TRUE;
1268 }
1269
1270 //generic property addressing code(or builtin name)=val
1271 psep = strchr(frag_name, src_pid->filter->session->sep_name);
1272 if (!psep) {
1273 psep = strchr(frag_name, '-');
1274 if (psep) comp_type = 1;
1275 else {
1276 psep = strchr(frag_name, '+');
1277 if (psep) comp_type = 2;
1278 }
1279 }
1280
1281 if (!psep) {
1282 *prop_not_found = GF_TRUE;
1283 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("PID addressing %s not recognized, ignoring and assuming match\n", frag_name ));
1284 return GF_TRUE;
1285 }
1286
1287 Bool is_equal = GF_FALSE;
1288 Bool use_not_equal = GF_FALSE;
1289 GF_PropertyValue prop_val;
1290 u32 p4cc = 0;
1291 char c=psep[0];
1292 psep[0] = 0;
1293 pent=NULL;
1294
1295 //check for built-in property
1296 p4cc = gf_props_get_id(frag_name);
1297 if (!p4cc && !strcmp(frag_name, "PID") )
1298 p4cc = GF_PROP_PID_ID;
1299
1300 if (!p4cc && (strlen(frag_name)==4))
1301 p4cc = GF_4CC(frag_name[0], frag_name[1], frag_name[2], frag_name[3]);
1302
1303 if (p4cc) pent = gf_filter_pid_get_property_entry(src_pid, p4cc);
1304 //not a built-in property, find prop by name
1305 if (!pent) {
1306 pent = gf_filter_pid_get_property_entry_str(src_pid, frag_name);
1307 }
1308
1309 psep[0] = c;
1310
1311 //if the property is not found, we accept the connection
1312 if (!pent) {
1313 *prop_not_found = GF_TRUE;
1314 return GF_TRUE;
1315 }
1316 //check for dynamic assignment
1317 if ( (psep[0]==src_pid->filter->session->sep_name) && ((psep[1]=='*') || (psep[1]=='\0') ) ) {
1318 *needs_resolve = GF_TRUE;
1319 gf_props_dump_val(&pent->prop, prop_dump_buffer, GF_FALSE, NULL);
1320 return GF_FALSE;
1321 }
1322
1323 //check for negation
1324 if ( (psep[0]==src_pid->filter->session->sep_name) && (psep[1]==src_pid->filter->session->sep_neg) ) {
1325 psep++;
1326 use_not_equal = GF_TRUE;
1327 }
1328 //parse the property, based on its property type
1329 if (pent->p4cc==GF_PROP_PID_CODECID) {
1330 prop_val.type = GF_PROP_UINT;
1331 prop_val.value.uint = gf_codec_parse(psep+1);
1332 } else {
1333 prop_val = gf_props_parse_value(pent->prop.type, frag_name, psep+1, NULL, src_pid->filter->session->sep_list);
1334 }
1335 if (!comp_type) {
1336 is_equal = gf_props_equal(&pent->prop, &prop_val);
1337 if (use_not_equal) is_equal = !is_equal;
1338 } else {
1339 switch (prop_val.type) {
1340 case GF_PROP_SINT:
1341 if (pent->prop.value.sint<prop_val.value.sint) is_equal = GF_TRUE;
1342 if (comp_type==2) is_equal = !is_equal;
1343 break;
1344 case GF_PROP_UINT:
1345 if (pent->prop.value.uint<prop_val.value.uint) is_equal = GF_TRUE;
1346 if (comp_type==2) is_equal = !is_equal;
1347 break;
1348 case GF_PROP_LSINT:
1349 if (pent->prop.value.longsint<prop_val.value.longsint) is_equal = GF_TRUE;
1350 if (comp_type==2) is_equal = !is_equal;
1351 break;
1352 case GF_PROP_LUINT:
1353 if (pent->prop.value.longuint<prop_val.value.longuint) is_equal = GF_TRUE;
1354 if (comp_type==2) is_equal = !is_equal;
1355 break;
1356 case GF_PROP_FLOAT:
1357 if (pent->prop.value.fnumber<prop_val.value.fnumber) is_equal = GF_TRUE;
1358 if (comp_type==2) is_equal = !is_equal;
1359 break;
1360 case GF_PROP_DOUBLE:
1361 if (pent->prop.value.number<prop_val.value.number) is_equal = GF_TRUE;
1362 if (comp_type==2) is_equal = !is_equal;
1363 break;
1364 case GF_PROP_FRACTION:
1365 if (pent->prop.value.frac.num * prop_val.value.frac.den < pent->prop.value.frac.den * prop_val.value.frac.num) is_equal = GF_TRUE;
1366 if (comp_type == 2) is_equal = !is_equal;
1367 break;
1368 case GF_PROP_FRACTION64:
1369 if (pent->prop.value.lfrac.num * prop_val.value.lfrac.den < pent->prop.value.lfrac.den * prop_val.value.lfrac.num) is_equal = GF_TRUE;
1370 if (comp_type == 2) is_equal = !is_equal;
1371 break;
1372 default:
1373 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("PID addressing uses \'%s\' comparison on property %s which is not a number, defaulting to equal=true\n", (comp_type==1) ? "less than" : "more than", gf_props_4cc_get_name(p4cc) ));
1374 is_equal = GF_TRUE;
1375 break;
1376 }
1377 }
1378 gf_props_reset_single(&prop_val);
1379 if (!is_equal) *pid_excluded = GF_TRUE;
1380
1381 return is_equal;
1382 }
1383
filter_source_id_match(GF_FilterPid * src_pid,const char * id,GF_Filter * dst_filter,Bool * pid_excluded,Bool * needs_clone)1384 static Bool filter_source_id_match(GF_FilterPid *src_pid, const char *id, GF_Filter *dst_filter, Bool *pid_excluded, Bool *needs_clone)
1385 {
1386 const char *source_ids;
1387 char *resolved_source_ids = NULL;
1388 Bool result = GF_FALSE;
1389 Bool first_pass = GF_TRUE;
1390 Bool has_default_match;
1391 Bool is_pid_excluded;
1392 *pid_excluded = GF_FALSE;
1393 if (!dst_filter->source_ids)
1394 return GF_TRUE;
1395 if (!id)
1396 return GF_FALSE;
1397
1398 sourceid_reassign:
1399 source_ids = resolved_source_ids ? resolved_source_ids : dst_filter->source_ids;
1400 if (!first_pass) {
1401 assert(dst_filter->dynamic_source_ids);
1402 source_ids = dst_filter->dynamic_source_ids;
1403 }
1404 has_default_match = GF_FALSE;
1405 is_pid_excluded = GF_FALSE;
1406
1407 while (source_ids) {
1408 Bool all_matched = GF_TRUE;
1409 Bool all_frags_not_found = GF_TRUE;
1410 u32 len, sublen;
1411 Bool last=GF_FALSE;
1412 char *sep = strchr(source_ids, src_pid->filter->session->sep_list);
1413 char *frag_name, *frag_clone;
1414 if (sep) {
1415 len = (u32) (sep - source_ids);
1416 } else {
1417 len = (u32) strlen(source_ids);
1418 last=GF_TRUE;
1419 }
1420
1421 frag_name = strchr(source_ids, src_pid->filter->session->sep_frag);
1422 if (frag_name > source_ids + len) frag_name = NULL;
1423 sublen = frag_name ? (u32) (frag_name - source_ids) : len;
1424 //skip frag char
1425 if (frag_name) frag_name++;
1426
1427 //any ID, always match
1428 if (source_ids[0]=='*') { }
1429 // id does not match
1430 else if (strncmp(id, source_ids, sublen)) {
1431 source_ids += len+1;
1432 if (last) break;
1433 continue;
1434 }
1435 //no fragment or fragment match pid name, OK
1436 if (!frag_name || !strcmp(src_pid->name, frag_name)) {
1437 result = GF_TRUE;
1438 break;
1439 }
1440 frag_clone = NULL;
1441 if (!last) {
1442 frag_clone = gf_strdup(frag_name);
1443 char *nsep = strchr(frag_clone, src_pid->filter->session->sep_list);
1444 assert(nsep);
1445 nsep[0] = 0;
1446 frag_name = frag_clone;
1447 }
1448
1449 //for all listed fragment extensions
1450 while (frag_name && all_matched) {
1451 char prop_dump_buffer[GF_PROP_DUMP_ARG_SIZE];
1452 Bool needs_resolve = GF_FALSE;
1453 Bool prop_not_found = GF_FALSE;
1454 Bool local_pid_excluded = GF_FALSE;
1455 char *next_frag = strchr(frag_name, src_pid->filter->session->sep_frag);
1456 if (next_frag) next_frag[0] = 0;
1457
1458 if (! filter_pid_check_fragment(src_pid, frag_name, &local_pid_excluded, &needs_resolve, &prop_not_found, prop_dump_buffer)) {
1459 if (needs_resolve) {
1460 if (first_pass) {
1461 char *sid = resolved_source_ids ? resolved_source_ids : dst_filter->source_ids;
1462 char *frag_sep = strchr(frag_name, dst_filter->session->sep_name);
1463 assert(frag_sep);
1464 if (next_frag) next_frag[0] = src_pid->filter->session->sep_frag;
1465
1466 char *new_source_ids = gf_malloc(sizeof(char) * (strlen(sid) + strlen(prop_dump_buffer)+1));
1467 u32 clen = (u32) (1+frag_sep - sid);
1468 strncpy(new_source_ids, sid, clen);
1469 new_source_ids[clen]=0;
1470 strcat(new_source_ids, prop_dump_buffer);
1471 if (next_frag) strcat(new_source_ids, next_frag);
1472
1473 if (resolved_source_ids) gf_free(resolved_source_ids);
1474 resolved_source_ids = new_source_ids;
1475 if (frag_clone) gf_free(frag_clone);
1476 goto sourceid_reassign;
1477 }
1478 }
1479 else {
1480 all_matched = GF_FALSE;
1481 //remember we failed because PID was excluded by sourceID
1482 if (local_pid_excluded)
1483 is_pid_excluded = GF_TRUE;
1484 }
1485 } else {
1486 //remember we succeed because PID has no matching property
1487 if (!prop_not_found)
1488 all_frags_not_found = GF_FALSE;
1489 }
1490
1491 if (!next_frag) break;
1492
1493 next_frag[0] = src_pid->filter->session->sep_frag;
1494 frag_name = next_frag+1;
1495 }
1496 if (frag_clone) gf_free(frag_clone);
1497 if (all_matched) {
1498 //exact match on one or more properties, don't look any further
1499 if (!all_frags_not_found) {
1500 result = GF_TRUE;
1501 break;
1502 }
1503 //remember we had a default match, but don't set result yet and parse other SIDs
1504 has_default_match = GF_TRUE;
1505 }
1506 *needs_clone = GF_FALSE;
1507 if (!sep) break;
1508 source_ids = sep+1;
1509 }
1510
1511 if (!result) {
1512 //we had a default match and pid was not excluded by any SIDs, consider we pass
1513 if (has_default_match && !is_pid_excluded)
1514 result = GF_TRUE;
1515 }
1516
1517 if (!result) {
1518 if (resolved_source_ids) gf_free(resolved_source_ids);
1519 if (dst_filter->dynamic_source_ids && first_pass) {
1520 first_pass = GF_FALSE;
1521 goto sourceid_reassign;
1522 }
1523 *pid_excluded = is_pid_excluded;
1524 return GF_FALSE;
1525 }
1526 if (resolved_source_ids) {
1527 if (!dst_filter->dynamic_source_ids) {
1528 dst_filter->dynamic_source_ids = dst_filter->source_ids;
1529 dst_filter->source_ids = resolved_source_ids;
1530 } else {
1531 gf_free(dst_filter->source_ids);
1532 dst_filter->source_ids = resolved_source_ids;
1533 }
1534 }
1535 if (!first_pass) {
1536 *needs_clone = GF_TRUE;
1537 }
1538 return GF_TRUE;
1539 }
1540
filter_in_parent_chain(GF_Filter * parent,GF_Filter * filter)1541 Bool filter_in_parent_chain(GF_Filter *parent, GF_Filter *filter)
1542 {
1543 u32 i, count;
1544 if (parent == filter) return GF_TRUE;
1545 //browse all parent PIDs
1546 count = parent->num_input_pids;
1547 if (!count) return GF_FALSE;
1548 for (i=0; i<count; i++) {
1549 GF_FilterPidInst *pid = gf_list_get(parent->input_pids, i);
1550 if (filter_in_parent_chain(pid->pid->filter, filter)) return GF_TRUE;
1551 }
1552 return GF_FALSE;
1553 }
1554
cap_code_match(u32 c1,u32 c2)1555 static Bool cap_code_match(u32 c1, u32 c2)
1556 {
1557 if (c1==c2) return GF_TRUE;
1558 /*we consider GF_PROP_PID_FILE_EXT and GF_PROP_PID_MIME are the same so that we check
1559 if we have at least one match of file ext or mime in a given bundle*/
1560 if ((c1==GF_PROP_PID_FILE_EXT) && (c2==GF_PROP_PID_MIME)) return GF_TRUE;
1561 if ((c1==GF_PROP_PID_MIME) && (c2==GF_PROP_PID_FILE_EXT)) return GF_TRUE;
1562 return GF_FALSE;
1563 }
1564
1565
gf_filter_pid_caps_match(GF_FilterPid * src_pid_or_ipid,const GF_FilterRegister * freg,GF_Filter * filter_inst,u8 * priority,u32 * dst_bundle_idx,GF_Filter * dst_filter,s32 for_bundle_idx)1566 Bool gf_filter_pid_caps_match(GF_FilterPid *src_pid_or_ipid, const GF_FilterRegister *freg, GF_Filter *filter_inst, u8 *priority, u32 *dst_bundle_idx, GF_Filter *dst_filter, s32 for_bundle_idx)
1567 {
1568 u32 i=0;
1569 u32 cur_bundle_start = 0;
1570 u32 cap_bundle_idx = 0;
1571 u32 nb_subcaps=0;
1572 Bool skip_explicit_load = GF_FALSE;
1573 Bool all_caps_matched = GF_TRUE;
1574 Bool mime_matched = GF_FALSE;
1575 Bool has_file_ext_cap = GF_FALSE;
1576 Bool ext_not_trusted;
1577 GF_FilterPid *src_pid = src_pid_or_ipid->pid;
1578 const GF_FilterCapability *in_caps;
1579 u32 nb_in_caps;
1580
1581 if (!freg) {
1582 assert(dst_filter);
1583 freg = dst_filter->freg;
1584 skip_explicit_load = GF_TRUE;
1585 }
1586
1587 in_caps = freg->caps;
1588 nb_in_caps = freg->nb_caps;
1589 if (filter_inst && (filter_inst->freg==freg)) {
1590 skip_explicit_load = GF_TRUE;
1591 if (filter_inst->forced_caps) {
1592 in_caps = filter_inst->forced_caps;
1593 nb_in_caps = filter_inst->nb_forced_caps;
1594 }
1595 }
1596 ext_not_trusted = src_pid->ext_not_trusted;
1597 if (ext_not_trusted) {
1598 Bool has_mime_cap = GF_FALSE;
1599
1600 for (i=0; i<nb_in_caps; i++) {
1601 const GF_FilterCapability *cap = &in_caps[i];
1602 if (! (cap->flags & GF_CAPFLAG_INPUT) ) continue;
1603 if (cap->code == GF_PROP_PID_MIME) {
1604 has_mime_cap = GF_TRUE;
1605 break;
1606 }
1607 }
1608 if (!has_mime_cap) ext_not_trusted = GF_FALSE;
1609 }
1610
1611 if (filter_inst && filter_inst->encoder_stream_type) {
1612 const GF_PropertyValue *pid_st = gf_filter_pid_get_property_first(src_pid_or_ipid, GF_PROP_PID_STREAM_TYPE);
1613 if (pid_st && (pid_st->value.uint != filter_inst->encoder_stream_type))
1614 return GF_FALSE;
1615 }
1616
1617 if (priority)
1618 (*priority) = freg->priority;
1619
1620 if (dst_bundle_idx)
1621 (*dst_bundle_idx) = 0;
1622
1623 //filters with no explicit input cap accept anything for now, this should be refined ...
1624 if (!in_caps)
1625 return GF_TRUE;
1626
1627 //check all input caps of dst filter
1628 for (i=0; i<nb_in_caps; i++) {
1629 const GF_PropertyValue *pid_cap=NULL;
1630 const GF_FilterCapability *cap = &in_caps[i];
1631
1632 /*end of cap bundle*/
1633 if (i && !(cap->flags & GF_CAPFLAG_IN_BUNDLE) ) {
1634 if (has_file_ext_cap && ext_not_trusted && !mime_matched)
1635 all_caps_matched = GF_FALSE;
1636
1637 if (all_caps_matched) {
1638 if (dst_bundle_idx)
1639 (*dst_bundle_idx) = cap_bundle_idx;
1640 return GF_TRUE;
1641 }
1642 all_caps_matched = GF_TRUE;
1643 mime_matched = GF_FALSE;
1644 has_file_ext_cap = GF_FALSE;
1645 nb_subcaps=0;
1646 cur_bundle_start = i;
1647 cap_bundle_idx++;
1648 if ((for_bundle_idx>=0) && (cap_bundle_idx > (u32) for_bundle_idx)) {
1649 break;
1650 }
1651 continue;
1652 }
1653 if ((for_bundle_idx>=0) && (cap_bundle_idx < (u32) for_bundle_idx)) {
1654 all_caps_matched = 0;
1655 continue;
1656 }
1657
1658 //not an input cap
1659 if (! (cap->flags & GF_CAPFLAG_INPUT) ) {
1660 if (!skip_explicit_load && (cap->flags & GF_CAPFLAG_LOADED_FILTER) ) {
1661 all_caps_matched = 0;
1662 }
1663 continue;
1664 }
1665
1666 nb_subcaps++;
1667 //no match for this cap, go on until new one or end
1668 if (!all_caps_matched) continue;
1669
1670 if (cap->code) {
1671 pid_cap = gf_filter_pid_get_property_first(src_pid_or_ipid, cap->code);
1672
1673 //special case for file ext: the pid will likely have only one file extension defined, and the output as well
1674 //we browse all caps of the filter owning the pid, looking for the original file extension property
1675 if (pid_cap && (cap->code==GF_PROP_PID_FILE_EXT) ) {
1676 u32 j;
1677 for (j=0; j<src_pid->filter->freg->nb_caps; j++) {
1678 const GF_FilterCapability *out_cap = &src_pid->filter->freg->caps[j];
1679 if (!(out_cap->flags & GF_CAPFLAG_OUTPUT)) continue;
1680 if (out_cap->code != GF_PROP_PID_FILE_EXT) continue;
1681 if (! gf_props_equal(pid_cap, &out_cap->val)) continue;
1682 pid_cap = &out_cap->val;
1683 break;
1684 }
1685 }
1686 /*if PID prop for this cap is not found and the cap is MIME (resp. file ext), fetch file_ext (resp MIME)
1687 so that we check if we have at least one match of file ext or mime in a given bundle*/
1688 if (!pid_cap) {
1689 if (cap->code==GF_PROP_PID_FILE_EXT)
1690 pid_cap = gf_filter_pid_get_property_first(src_pid_or_ipid, GF_PROP_PID_MIME);
1691 else if (cap->code==GF_PROP_PID_MIME)
1692 pid_cap = gf_filter_pid_get_property_first(src_pid_or_ipid, GF_PROP_PID_FILE_EXT);
1693 }
1694 }
1695
1696 //optional cap
1697 if (cap->flags & GF_CAPFLAG_OPTIONAL) continue;
1698
1699 //try by name
1700 if (!pid_cap && cap->name) pid_cap = gf_filter_pid_get_property_str_first(src_pid_or_ipid, cap->name);
1701
1702 if (ext_not_trusted && (cap->code==GF_PROP_PID_FILE_EXT)) {
1703 has_file_ext_cap = GF_TRUE;
1704 continue;
1705 }
1706
1707
1708 //we found a property of that type and it is equal
1709 if (pid_cap) {
1710 u32 j;
1711 Bool prop_excluded = GF_FALSE;
1712 Bool prop_equal = GF_FALSE;
1713
1714 //this could be optimized by not checking several times the same cap
1715 for (j=0; j<nb_in_caps; j++) {
1716 const GF_FilterCapability *a_cap = &in_caps[j];
1717
1718 if ((j>cur_bundle_start) && ! (a_cap->flags & GF_CAPFLAG_IN_BUNDLE) ) {
1719 break;
1720 }
1721 //not an input cap
1722 if (! (a_cap->flags & GF_CAPFLAG_INPUT) ) continue;
1723 //not a static and not in bundle
1724 if (! (a_cap->flags & GF_CAPFLAG_STATIC)) {
1725 if (j<cur_bundle_start)
1726 continue;
1727 }
1728
1729 if (cap->code) {
1730 if (!cap_code_match(cap->code, a_cap->code) )
1731 continue;
1732 } else if (!cap->name || !a_cap->name || strcmp(cap->name, a_cap->name)) {
1733 continue;
1734 }
1735 if (!skip_explicit_load && (a_cap->flags & GF_CAPFLAG_LOADED_FILTER) ) {
1736 if (!dst_filter || (dst_filter != src_pid->filter->dst_filter)) {
1737 prop_equal = GF_FALSE;
1738 break;
1739 }
1740 if (dst_filter->freg != freg) {
1741 prop_equal = GF_FALSE;
1742 break;
1743 }
1744 }
1745
1746 if (!prop_equal) {
1747 prop_equal = gf_props_equal(pid_cap, &a_cap->val);
1748 //excluded cap: if value match, don't match this cap at all
1749 if (a_cap->flags & GF_CAPFLAG_EXCLUDED) {
1750 if (prop_equal) {
1751 prop_equal = GF_FALSE;
1752 prop_excluded = GF_FALSE;
1753 break;
1754 }
1755 prop_excluded = GF_TRUE;
1756 }
1757 if (prop_equal) break;
1758 }
1759 }
1760 if (!prop_equal && !prop_excluded) {
1761 all_caps_matched=GF_FALSE;
1762 } else if (priority && cap->priority) {
1763 (*priority) = cap->priority;
1764 }
1765 if (ext_not_trusted && prop_equal && (cap->code==GF_PROP_PID_MIME))
1766 mime_matched = GF_TRUE;
1767 }
1768 else if (! (cap->flags & (GF_CAPFLAG_EXCLUDED | GF_CAPFLAG_OPTIONAL) ) ) {
1769 all_caps_matched=GF_FALSE;
1770 }
1771 }
1772
1773 if (has_file_ext_cap && ext_not_trusted && !mime_matched)
1774 all_caps_matched = GF_FALSE;
1775
1776 if (nb_subcaps && all_caps_matched) {
1777 if (dst_bundle_idx)
1778 (*dst_bundle_idx) = cap_bundle_idx;
1779 return GF_TRUE;
1780 }
1781
1782 return GF_FALSE;
1783 }
1784
gf_filter_caps_bundle_count(const GF_FilterCapability * caps,u32 nb_caps)1785 u32 gf_filter_caps_bundle_count(const GF_FilterCapability *caps, u32 nb_caps)
1786 {
1787 u32 i, nb_bundles = 0, num_in_bundle=0;
1788 for (i=0; i<nb_caps; i++) {
1789 const GF_FilterCapability *cap = &caps[i];
1790 if (! (cap->flags & GF_CAPFLAG_IN_BUNDLE)) {
1791 if (num_in_bundle) nb_bundles++;
1792 num_in_bundle=0;
1793 continue;
1794 }
1795 num_in_bundle++;
1796 }
1797 if (num_in_bundle) nb_bundles++;
1798 return nb_bundles;
1799 }
1800
gf_filter_has_in_out_caps(const GF_FilterCapability * caps,u32 nb_caps,Bool check_in)1801 static Bool gf_filter_has_in_out_caps(const GF_FilterCapability *caps, u32 nb_caps, Bool check_in)
1802 {
1803 u32 i;
1804 //check all input caps of dst filter, count bundles
1805 for (i=0; i<nb_caps; i++) {
1806 const GF_FilterCapability *a_cap = &caps[i];
1807 if (check_in) {
1808 if (a_cap->flags & GF_CAPFLAG_INPUT) {
1809 return GF_TRUE;
1810 }
1811 } else {
1812 if (a_cap->flags & GF_CAPFLAG_OUTPUT) {
1813 return GF_TRUE;
1814 }
1815 }
1816 }
1817 return GF_FALSE;
1818
1819 }
gf_filter_has_out_caps(const GF_FilterCapability * caps,u32 nb_caps)1820 Bool gf_filter_has_out_caps(const GF_FilterCapability *caps, u32 nb_caps)
1821 {
1822 return gf_filter_has_in_out_caps(caps, nb_caps, GF_FALSE);
1823 }
gf_filter_has_in_caps(const GF_FilterCapability * caps,u32 nb_caps)1824 Bool gf_filter_has_in_caps(const GF_FilterCapability *caps, u32 nb_caps)
1825 {
1826 return gf_filter_has_in_out_caps(caps, nb_caps, GF_TRUE);
1827 }
1828
gf_filter_caps_to_caps_match(const GF_FilterRegister * src,u32 src_bundle_idx,const GF_FilterRegister * dst_reg,GF_Filter * dst_filter,u32 * dst_bundle_idx,s32 for_dst_bundle,u32 * loaded_filter_flags,GF_CapsBundleStore * capstore)1829 u32 gf_filter_caps_to_caps_match(const GF_FilterRegister *src, u32 src_bundle_idx, const GF_FilterRegister *dst_reg, GF_Filter *dst_filter, u32 *dst_bundle_idx, s32 for_dst_bundle, u32 *loaded_filter_flags, GF_CapsBundleStore *capstore)
1830 {
1831 u32 i=0;
1832 u32 cur_bundle_start = 0;
1833 u32 cur_bundle_idx = 0;
1834 u32 nb_matched=0;
1835 u32 nb_out_caps=0;
1836 u32 nb_in_bundles=0;
1837 u32 bundle_score = 0;
1838 u32 *bundles_in_ok = NULL;
1839 u32 *bundles_cap_found = NULL;
1840 u32 *bundles_in_scores = NULL;
1841 //initialize caps matched to true for first cap bundle
1842 Bool all_caps_matched = GF_TRUE;
1843 const GF_FilterCapability *dst_caps = dst_reg->caps;
1844 u32 nb_dst_caps = dst_reg->nb_caps;
1845
1846 if (dst_filter && dst_filter->freg==dst_reg && dst_filter->forced_caps) {
1847 dst_caps = dst_filter->forced_caps;
1848 nb_dst_caps = dst_filter->nb_forced_caps;
1849 }
1850
1851 //check all input caps of dst filter, count bundles
1852 if (! gf_filter_has_out_caps(src->caps, src->nb_caps)) {
1853 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has no output caps, cannot match filter %s inputs\n", src->name, dst_reg->name));
1854 return 0;
1855 }
1856
1857 //check all input caps of dst filter, count bundles
1858 nb_in_bundles = gf_filter_caps_bundle_count(dst_caps, nb_dst_caps);
1859 if (!nb_in_bundles) {
1860 if (dst_reg->configure_pid) {
1861 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has no caps but pid configure possible, assuming possible connection\n", dst_reg->name));
1862 return 1;
1863 }
1864 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s has no caps and no pid configure, no possible connection\n", dst_reg->name));
1865 return 0;
1866 }
1867 if (capstore->nb_allocs < nb_in_bundles) {
1868 capstore->nb_allocs = nb_in_bundles;
1869 capstore->bundles_in_ok = gf_realloc(capstore->bundles_in_ok, sizeof(u32) * nb_in_bundles);
1870 capstore->bundles_cap_found = gf_realloc(capstore->bundles_cap_found, sizeof(u32) * nb_in_bundles);
1871 capstore->bundles_in_scores = gf_realloc(capstore->bundles_in_scores, sizeof(u32) * nb_in_bundles);
1872 }
1873 bundles_in_ok = capstore->bundles_in_ok;
1874 bundles_cap_found = capstore->bundles_cap_found;
1875 bundles_in_scores = capstore->bundles_in_scores;
1876
1877 for (i=0; i<nb_in_bundles; i++) {
1878 bundles_in_ok[i] = 1;
1879 bundles_cap_found[i] = 0;
1880 bundles_in_scores[i] = 0;
1881 }
1882
1883 //check all output caps of src filter
1884 for (i=0; i<src->nb_caps; i++) {
1885 u32 j, k;
1886 Bool already_tested = GF_FALSE;
1887 const GF_FilterCapability *out_cap = &src->caps[i];
1888
1889 if (!(out_cap->flags & GF_CAPFLAG_IN_BUNDLE) ) {
1890 all_caps_matched = GF_TRUE;
1891 cur_bundle_start = i+1;
1892 cur_bundle_idx++;
1893 if (src_bundle_idx < cur_bundle_idx)
1894 break;
1895
1896 continue;
1897 }
1898
1899 //not our selected output and not static cap
1900 if ((src_bundle_idx != cur_bundle_idx) && ! (out_cap->flags & GF_CAPFLAG_STATIC) ) {
1901 continue;
1902 }
1903
1904 //not an output cap
1905 if (!(out_cap->flags & GF_CAPFLAG_OUTPUT) ) continue;
1906
1907 //no match possible for this cap, wait until next cap start
1908 if (!all_caps_matched) continue;
1909
1910 //check we didn't test a cap with same name/code before us
1911 for (k=cur_bundle_start; k<i; k++) {
1912 const GF_FilterCapability *an_out_cap = &src->caps[k];
1913 if (! (an_out_cap->flags & GF_CAPFLAG_IN_BUNDLE) ) {
1914 break;
1915 }
1916 if (! (an_out_cap->flags & GF_CAPFLAG_OUTPUT) ) {
1917 continue;
1918 }
1919 if (out_cap->code && (out_cap->code == an_out_cap->code) ) {
1920 already_tested = GF_TRUE;
1921 break;
1922 }
1923 if (out_cap->name && an_out_cap->name && !strcmp(out_cap->name, an_out_cap->name)) {
1924 already_tested = GF_TRUE;
1925 break;
1926 }
1927 }
1928 if (already_tested) {
1929 continue;
1930 }
1931 nb_out_caps++;
1932
1933 //set cap as OK in all bundles
1934 for (k=0; k<nb_in_bundles; k++) {
1935 bundles_cap_found[k] = 0;
1936 }
1937
1938 //check all output caps in this bundle with the same code/name, consider OK if one is matched
1939 for (k=cur_bundle_start; k<src->nb_caps; k++) {
1940 u32 cur_dst_bundle=0;
1941 Bool static_matched = GF_FALSE;
1942 u32 nb_caps_tested = 0;
1943 u32 cap_loaded_filter_only = 0;
1944 Bool matched=GF_FALSE;
1945 Bool exclude=GF_FALSE;
1946 Bool prop_found=GF_FALSE;
1947 const GF_FilterCapability *an_out_cap = &src->caps[k];
1948 if (! (an_out_cap->flags & GF_CAPFLAG_IN_BUNDLE) ) {
1949 break;
1950 }
1951 if (! (an_out_cap->flags & GF_CAPFLAG_OUTPUT) ) {
1952 continue;
1953 }
1954 if (out_cap->code && !cap_code_match(out_cap->code, an_out_cap->code) )
1955 continue;
1956
1957 if (out_cap->name && (!an_out_cap->name || strcmp(out_cap->name, an_out_cap->name)))
1958 continue;
1959
1960 //not our selected output and not static cap
1961 if ((src_bundle_idx != cur_bundle_idx) && ! (an_out_cap->flags & GF_CAPFLAG_STATIC) ) {
1962 continue;
1963 }
1964
1965 nb_matched = 0;
1966 //check all input caps of dst filter, count ones that are matched
1967 for (j=0; j<nb_dst_caps; j++) {
1968 Bool prop_equal;
1969 const GF_FilterCapability *in_cap = &dst_caps[j];
1970
1971 if (! (in_cap->flags & GF_CAPFLAG_IN_BUNDLE)) {
1972 if (!matched && !nb_caps_tested && (out_cap->flags & GF_CAPFLAG_EXCLUDED))
1973 matched = GF_TRUE;
1974
1975 //we found a prop, excluded but with != value hence acceptable, default matching to true
1976 if (!matched && prop_found) matched = GF_TRUE;
1977
1978 //match, flag this bundle as ok
1979 if (matched) {
1980 if (!bundles_cap_found[cur_dst_bundle])
1981 bundles_cap_found[cur_dst_bundle] = cap_loaded_filter_only ? 2 : 1;
1982
1983 nb_matched++;
1984 }
1985
1986 matched = static_matched ? GF_TRUE : GF_FALSE;
1987 exclude = GF_FALSE;
1988 prop_found = GF_FALSE;
1989 nb_caps_tested = 0;
1990 cur_dst_bundle++;
1991 if ((for_dst_bundle>=0) && (cur_dst_bundle > (u32) for_dst_bundle))
1992 break;
1993
1994 continue;
1995 }
1996 //not an input cap
1997 if (!(in_cap->flags & GF_CAPFLAG_INPUT) )
1998 continue;
1999
2000 //optional cap, ignore
2001 if (in_cap->flags & GF_CAPFLAG_OPTIONAL)
2002 continue;
2003
2004 if ((for_dst_bundle>=0) && (cur_dst_bundle < (u32)for_dst_bundle) && !(in_cap->flags & GF_CAPFLAG_STATIC))
2005 continue;
2006
2007 //prop was excluded, cannot match in bundle
2008 if (exclude) continue;
2009 //prop was matched, no need to check other caps in the current bundle
2010 if (matched) continue;
2011
2012 if (out_cap->code && !cap_code_match(out_cap->code, in_cap->code) )
2013 continue;
2014
2015 if (out_cap->name && (!in_cap->name || strcmp(out_cap->name, in_cap->name)))
2016 continue;
2017
2018 nb_caps_tested++;
2019 //we found a property of that type , check if equal equal
2020 prop_equal = gf_props_equal(&in_cap->val, &an_out_cap->val);
2021 if ((in_cap->flags & GF_CAPFLAG_EXCLUDED) && !(an_out_cap->flags & GF_CAPFLAG_EXCLUDED) ) {
2022 //prop type matched, output includes it and input excludes it: no match, don't look any further
2023 if (prop_equal) {
2024 matched = GF_FALSE;
2025 exclude = GF_TRUE;
2026 prop_found = GF_FALSE;
2027 } else {
2028 //remember we found a prop of same type but excluded value
2029 // we will match unless we match an excluded value
2030 prop_found = GF_TRUE;
2031 }
2032 } else if (!(in_cap->flags & GF_CAPFLAG_EXCLUDED) && (an_out_cap->flags & GF_CAPFLAG_EXCLUDED) ) {
2033 //prop type matched, input includes it and output excludes it: no match, don't look any further
2034 if (prop_equal) {
2035 matched = GF_FALSE;
2036 exclude = GF_TRUE;
2037 prop_found = GF_FALSE;
2038 } else {
2039 //remember we found a prop of same type but excluded value
2040 //we will match unless we match an excluded value
2041 prop_found = GF_TRUE;
2042 }
2043 } else if (prop_equal) {
2044 matched = GF_TRUE;
2045 // if (an_out_cap->flags & GF_CAPFLAG_STATIC)
2046 // static_matched = GF_TRUE;
2047 } else if ((in_cap->flags & GF_CAPFLAG_EXCLUDED) && (an_out_cap->flags & GF_CAPFLAG_EXCLUDED) ) {
2048 //prop type matched, input excludes it and output excludes it and no match, remmeber we found the prop type
2049 prop_found = GF_TRUE;
2050 }
2051
2052 if (prop_found && (in_cap->flags & GF_CAPFLAG_LOADED_FILTER))
2053 cap_loaded_filter_only = 1;
2054 }
2055 if (nb_caps_tested) {
2056 //we found a prop, excluded but with != value hence acceptable, default matching to true
2057 if (!matched && prop_found) matched = GF_TRUE;
2058 //not match, flag this bundle as not ok
2059 if (matched) {
2060 if (!bundles_cap_found[cur_dst_bundle])
2061 bundles_cap_found[cur_dst_bundle] = cap_loaded_filter_only ? 2 : 1;
2062
2063 nb_matched++;
2064 }
2065 } else if (!nb_dst_caps) {
2066 if (!bundles_cap_found[cur_dst_bundle])
2067 bundles_cap_found[cur_dst_bundle] = cap_loaded_filter_only ? 2 : 1;
2068
2069 nb_matched++;
2070 } else if (!nb_matched && !prop_found && (an_out_cap->flags & GF_CAPFLAG_EXCLUDED) ) {
2071 if (!bundles_cap_found[cur_dst_bundle])
2072 bundles_cap_found[cur_dst_bundle] = cap_loaded_filter_only ? 2 : 1;
2073
2074 nb_matched++;
2075 }
2076 }
2077 //merge bundle cap
2078 nb_matched=0;
2079 for (k=0; k<nb_in_bundles; k++) {
2080 if (!bundles_cap_found[k])
2081 bundles_in_ok[k] = 0;
2082 else {
2083 nb_matched += 1;
2084 //we matched this property, keep score for the bundle
2085 bundles_in_scores[k] ++;
2086 //mark if connection is only valid for loaded inputs
2087 if (bundles_cap_found[k]==2)
2088 bundles_in_ok[k] |= 1<<1;
2089 //mark if connection is only valid for loaded outputs
2090 if (out_cap->flags & GF_CAPFLAG_LOADED_FILTER)
2091 bundles_in_ok[k] |= 1<<2;
2092 }
2093 }
2094 //not matched and not excluded, skip until next bundle
2095 if (!nb_matched && !(out_cap->flags & GF_CAPFLAG_EXCLUDED)) {
2096 all_caps_matched = GF_FALSE;
2097 }
2098 }
2099
2100 //get bundle with highest score
2101 bundle_score = 0;
2102 nb_matched = 0;
2103
2104 for (i=0; i<nb_in_bundles; i++) {
2105 if (bundles_in_ok[i]) {
2106 nb_matched++;
2107 if (bundle_score < bundles_in_scores[i]) {
2108 *dst_bundle_idx = i;
2109 bundle_score = bundles_in_scores[i];
2110 if (loaded_filter_flags) {
2111 *loaded_filter_flags = (bundles_in_ok[i]>>1);
2112 }
2113 }
2114 if ((for_dst_bundle>=0) && (for_dst_bundle==i)) {
2115 *dst_bundle_idx = i;
2116 if (loaded_filter_flags) {
2117 *loaded_filter_flags = (bundles_in_ok[i]>>1);
2118 }
2119 return bundles_in_scores[i];
2120 }
2121 }
2122 }
2123 if (!bundle_score) {
2124 // GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s outputs cap bundle %d do not match filter %s inputs\n", src->name, src_bundle_idx, dst_reg->name));
2125 } else {
2126 // GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s outputs cap bundle %d matches filter %s inputs caps bundle %d (%d total bundle matches, bundle matched %d/%d caps)\n", src->name, src_bundle_idx, dst_reg->name, *dst_bundle_idx, nb_matched, bundle_score, nb_out_caps));
2127 }
2128 return bundle_score;
2129 }
2130
2131 GF_EXPORT
gf_filter_pid_check_caps(GF_FilterPid * pid)2132 Bool gf_filter_pid_check_caps(GF_FilterPid *pid)
2133 {
2134 u8 priority;
2135 Bool res;
2136 if (PID_IS_OUTPUT(pid)) return GF_FALSE;
2137 pid->pid->local_props = ((GF_FilterPidInst*)pid)->props;
2138 res = gf_filter_pid_caps_match(pid->pid, NULL, pid->filter, &priority, NULL, pid->filter, -1);
2139 pid->pid->local_props = NULL;
2140 return res;
2141 }
2142
2143
concat_reg(GF_FilterSession * sess,char prefRegister[1001],const char * reg_key,const char * args)2144 static void concat_reg(GF_FilterSession *sess, char prefRegister[1001], const char *reg_key, const char *args)
2145 {
2146 u32 len;
2147 char *forced_reg, *sep;
2148 if (!args) return;
2149 forced_reg = strstr(args, reg_key);
2150 if (!forced_reg) return;
2151 forced_reg += 6;
2152 sep = strchr(forced_reg, sess->sep_args);
2153 len = sep ? (u32) (sep-forced_reg) : (u32) strlen(forced_reg);
2154 if (len+2+strlen(prefRegister)>1000) {
2155 return;
2156 }
2157 if (prefRegister[0]) {
2158 char szSepChar[2];
2159 szSepChar[0] = sess->sep_args;
2160 szSepChar[1] = 0;
2161 strcat(prefRegister, szSepChar);
2162 }
2163 strncat(prefRegister, forced_reg, len);
2164 }
2165
gf_filter_out_caps_solved_by_connection(const GF_FilterRegister * freg,u32 bundle_idx)2166 static Bool gf_filter_out_caps_solved_by_connection(const GF_FilterRegister *freg, u32 bundle_idx)
2167 {
2168 u32 i, k, cur_bundle_idx = 0;
2169 u32 nb_out_caps=0;
2170 for (i=0; i<freg->nb_caps; i++) {
2171 u32 nb_caps = 0;
2172 u32 cap_bundle_idx = 0;
2173 const GF_FilterCapability *cap = &freg->caps[i];
2174 if (!(cap->flags & GF_CAPFLAG_IN_BUNDLE)) {
2175 cur_bundle_idx++;
2176 if (cur_bundle_idx>bundle_idx) return GF_FALSE;
2177 continue;
2178 }
2179 if (!(cap->flags & GF_CAPFLAG_STATIC) && (bundle_idx>cur_bundle_idx)) continue;
2180 if (!(cap->flags & GF_CAPFLAG_OUTPUT)) continue;
2181
2182 if (cap->flags & GF_CAPFLAG_OPTIONAL) continue;
2183
2184 for (k=0; k<freg->nb_caps; k++) {
2185 const GF_FilterCapability *acap = &freg->caps[k];
2186 if (!(acap->flags & GF_CAPFLAG_IN_BUNDLE)) {
2187 cap_bundle_idx++;
2188 continue;
2189 }
2190 if (!(acap->flags & GF_CAPFLAG_OUTPUT)) continue;
2191 if (acap->flags & GF_CAPFLAG_OPTIONAL) continue;
2192 if (!(acap->flags & GF_CAPFLAG_STATIC) && (cap_bundle_idx!=bundle_idx) ) continue;
2193
2194 if (cap->code && (acap->code==cap->code)) {
2195 nb_caps++;
2196 } else if (cap->name && acap->name && !strcmp(cap->name, acap->name)) {
2197 nb_caps++;
2198 }
2199 //if more than one cap with same code in same bundle, consider the filter is undecided
2200 if (nb_caps>1)
2201 return GF_TRUE;
2202 }
2203 if (nb_caps && !(cap->flags & GF_CAPFLAG_EXCLUDED))
2204 nb_out_caps++;
2205 }
2206 if (!nb_out_caps)
2207 return GF_TRUE;
2208 return GF_FALSE;
2209 }
2210
gf_filter_reg_get_bundle_stream_type(const GF_FilterRegister * freg,u32 cap_idx,Bool for_output)2211 static s32 gf_filter_reg_get_bundle_stream_type(const GF_FilterRegister *freg, u32 cap_idx, Bool for_output)
2212 {
2213 u32 i, cur_bundle, stype=0, nb_stype=0;
2214
2215 cur_bundle = 0;
2216 for (i=0; i<freg->nb_caps; i++) {
2217 u32 cap_stype=0;
2218 const GF_FilterCapability *cap = &freg->caps[i];
2219 if (!(cap->flags & GF_CAPFLAG_IN_BUNDLE)) {
2220 cur_bundle++;
2221 continue;
2222 }
2223 if (for_output) {
2224 if (!(cap->flags & GF_CAPFLAG_OUTPUT)) continue;
2225 } else {
2226 if (!(cap->flags & GF_CAPFLAG_INPUT)) continue;
2227 }
2228 if ((cur_bundle != cap_idx) && !(cap->flags & GF_CAPFLAG_STATIC) ) continue;
2229 //output type is file or same media type, allow looking for filter chains
2230 if (cap->flags & GF_CAPFLAG_EXCLUDED) continue;
2231
2232 if (cap->code == GF_PROP_PID_STREAM_TYPE)
2233 cap_stype = cap->val.value.uint;
2234 else if ((cap->code == GF_PROP_PID_MIME) || (cap->code == GF_PROP_PID_FILE_EXT) )
2235 cap_stype = GF_STREAM_FILE;
2236
2237 if (!cap_stype) continue;
2238
2239 if (stype != cap_stype) {
2240 stype = cap_stype;
2241 nb_stype++;
2242 }
2243 }
2244 if (nb_stype==1) return (s32) stype;
2245 if (nb_stype) return -1;
2246 return 0;
2247 }
2248
2249 /*recursively enable edges of the graph.
2250 returns 0 if subgraph shall be disabled (will marke edge at the root of the subgraph disabled)
2251 returns 1 if subgraph shall be enabled (will marke edge at the root of the subgraph enabled)
2252 returns 2 if no decision can be taken because the subgraph is too deep. We don't mark the parent edge as disabled because the same subgraph/edge can be used at other places in a shorter path
2253 */
gf_filter_pid_enable_edges(GF_FilterSession * fsess,GF_FilterRegDesc * reg_desc,u32 src_cap_idx,const GF_FilterRegister * src_freg,u32 rlevel,s32 dst_stream_type,GF_FilterRegDesc * parent_desc,GF_FilterPid * pid,u32 pid_stream_type)2254 static u32 gf_filter_pid_enable_edges(GF_FilterSession *fsess, GF_FilterRegDesc *reg_desc, u32 src_cap_idx, const GF_FilterRegister *src_freg, u32 rlevel, s32 dst_stream_type, GF_FilterRegDesc *parent_desc, GF_FilterPid *pid, u32 pid_stream_type)
2255 {
2256 u32 i=0;
2257 Bool enable_graph = GF_FALSE;
2258 Bool aborted_graph_too_deep = GF_FALSE;
2259
2260 //we found the source reg we want to connect to!
2261 if (src_freg == reg_desc->freg) {
2262 return 1;
2263 }
2264 //the subgraph is too deep, abort marking edges but don't decide
2265 if (rlevel > fsess->max_resolve_chain_len) {
2266 return 2;
2267 }
2268 //we don't allow loops in dynamic chain resolution, so consider the parent edge invalid
2269 if (reg_desc->in_edges_enabling)
2270 return 0;
2271
2272 /*if dst type is FILE, reg_desc is a muxer or the loaded destination (a demuxer or a file)
2273 we only accept dst type FILE for the first call (ie reg desc is the loaded destination), and forbid muxers in the middle of the chain
2274 for dynamic resolution. This avoids situations such as StreamTypeA->mux->demux->streamtypeB which cannot be resolved
2275
2276 note that it is still possible to use a mux or demux in the chain, but they have to be explicetly loaded
2277 */
2278 if ((rlevel>1) && (dst_stream_type==GF_STREAM_FILE))
2279 return 0;
2280
2281 reg_desc->in_edges_enabling = 1;
2282
2283 for (i=0; i<reg_desc->nb_edges; i++) {
2284 u32 res;
2285 s32 source_stream_type;
2286 GF_FilterRegEdge *edge = ®_desc->edges[i];
2287 //this edge is not for our target source cap bundle
2288 if (edge->dst_cap_idx != src_cap_idx) continue;
2289
2290 //edge is already disabled (the subgraph doesn't match our source), don't test it
2291 if (edge->status == EDGE_STATUS_DISABLED)
2292 continue;
2293
2294 //if source is not edge origin and edge is only valid for explicitly loaded filters, disable edge
2295 if (edge->loaded_filter_only && (edge->src_reg->freg != pid->filter->freg) ) {
2296 edge->status = EDGE_STATUS_DISABLED;
2297 edge->disabled_depth = rlevel+1;
2298 continue;
2299 }
2300
2301 //edge is already enabled (the subgraph matches our source), don't test it but remember to indicate the graph is valid
2302 if (edge->status == EDGE_STATUS_ENABLED) {
2303 enable_graph = GF_TRUE;
2304 continue;
2305 }
2306
2307 //candidate edge, check stream type
2308 source_stream_type = edge->src_stream_type;
2309
2310 if (pid->filter->freg == edge->src_reg->freg)
2311 source_stream_type = pid_stream_type;
2312
2313 //source edge cap indicates multiple stream types (demuxer/encoder/decoder dundle)
2314 if (source_stream_type<0) {
2315 //if destination type is known (>=0 and NOT file, inherit it
2316 //otherwise, we we can't filter out yet
2317 if ((dst_stream_type>0) && (dst_stream_type != GF_STREAM_FILE))
2318 source_stream_type = dst_stream_type;
2319 }
2320 //inherit source type if not specified
2321 if (!source_stream_type && dst_stream_type>0)
2322 source_stream_type = dst_stream_type;
2323 //if source is encrypted type and dest type is set, use dest type
2324 if ((source_stream_type==GF_STREAM_ENCRYPTED) && dst_stream_type>0)
2325 source_stream_type = dst_stream_type;
2326 //if dest is encrypted type and source type is set, use source type
2327 if ((dst_stream_type==GF_STREAM_ENCRYPTED) && source_stream_type>0)
2328 dst_stream_type = source_stream_type;
2329
2330 //if stream types are know (>0) and not source files, do not mark the edges if they mismatch
2331 //moving from non-file type A to non-file type B requires an explicit filter
2332 if ((dst_stream_type>0) && (source_stream_type>0) && (source_stream_type != GF_STREAM_FILE) && (dst_stream_type != GF_STREAM_FILE) && (source_stream_type != dst_stream_type)) {
2333
2334 //exception: we allow text|scene|od ->video for dynamic compositor
2335 if (!(reg_desc->freg->flags & GF_FS_REG_EXPLICIT_ONLY) && (dst_stream_type==GF_STREAM_VISUAL)
2336 && ((source_stream_type==GF_STREAM_TEXT) || (source_stream_type==GF_STREAM_SCENE) || (source_stream_type==GF_STREAM_OD) )
2337 ) {
2338
2339 } else {
2340 edge->status = EDGE_STATUS_DISABLED;
2341 edge->disabled_depth = rlevel+1;
2342 continue;
2343 }
2344 }
2345
2346 res = gf_filter_pid_enable_edges(fsess, edge->src_reg, edge->src_cap_idx, src_freg, rlevel+1, source_stream_type, reg_desc, pid, pid_stream_type);
2347 //if subgraph matches our source reg, mark the edge towards this subgraph as enabled
2348 if (res==1) {
2349 edge->status = EDGE_STATUS_ENABLED;
2350 enable_graph = GF_TRUE;
2351 }
2352 //if sub-graph below is too deep, don't mark the edge since we might need to resolve it again with a shorter subgraph
2353 else if (res==2) {
2354 aborted_graph_too_deep = GF_TRUE;
2355 }
2356 //otherwise the subgraph doesn't match our source reg, mark as disaled and never test again
2357 else if (res==0) {
2358 edge->status = EDGE_STATUS_DISABLED;
2359 edge->disabled_depth = rlevel+1;
2360 }
2361 }
2362 reg_desc->in_edges_enabling = 0;
2363 //we had enabled edges, the subgraph is valid
2364 if (enable_graph) return 1;
2365 //we aborted because too deep, indicate it to the caller so that the edge is not disabled
2366 if (aborted_graph_too_deep) return 2;
2367 //disable subgraph
2368 return 0;
2369 }
2370
2371
gf_filter_reg_build_graph(GF_List * links,const GF_FilterRegister * freg,GF_CapsBundleStore * capstore,GF_FilterPid * src_pid,GF_Filter * dst_filter)2372 static GF_FilterRegDesc *gf_filter_reg_build_graph(GF_List *links, const GF_FilterRegister *freg, GF_CapsBundleStore *capstore, GF_FilterPid *src_pid, GF_Filter *dst_filter)
2373 {
2374 u32 nb_dst_caps, nb_regs, i, nb_caps;
2375 Bool freg_has_output;
2376
2377 GF_FilterRegDesc *reg_desc = NULL;
2378 const GF_FilterCapability *caps = freg->caps;
2379 nb_caps = freg->nb_caps;
2380 if (dst_filter && (freg->flags & GF_FS_REG_SCRIPT)) {
2381 caps = dst_filter->forced_caps;
2382 nb_caps = dst_filter->nb_forced_caps;
2383 }
2384
2385 freg_has_output = gf_filter_has_out_caps(caps, nb_caps);
2386
2387 GF_SAFEALLOC(reg_desc, GF_FilterRegDesc);
2388 if (!reg_desc) return NULL;
2389
2390 reg_desc->freg = freg;
2391
2392 nb_dst_caps = gf_filter_caps_bundle_count(caps, nb_caps);
2393
2394
2395 //we are building a register descriptor acting as destination, ignore any output caps
2396 if (src_pid || dst_filter) freg_has_output = GF_FALSE;
2397
2398 //setup all connections
2399 nb_regs = gf_list_count(links);
2400 for (i=0; i<nb_regs; i++) {
2401 u32 nb_src_caps, k, l;
2402 u32 path_weight;
2403 GF_FilterRegDesc *a_reg = gf_list_get(links, i);
2404 if (a_reg->freg == freg) continue;
2405
2406 //check which cap of this filter matches our destination
2407 nb_src_caps = gf_filter_caps_bundle_count(a_reg->freg->caps, a_reg->freg->nb_caps);
2408 for (k=0; k<nb_src_caps; k++) {
2409 for (l=0; l<nb_dst_caps; l++) {
2410 s32 bundle_idx;
2411
2412 if (gf_filter_has_out_caps(a_reg->freg->caps, a_reg->freg->nb_caps)) {
2413 u32 loaded_filter_only_flags = 0;
2414
2415 path_weight = gf_filter_caps_to_caps_match(a_reg->freg, k, (const GF_FilterRegister *) freg, dst_filter, &bundle_idx, l, &loaded_filter_only_flags, capstore);
2416
2417 if (path_weight && (bundle_idx == l)) {
2418 GF_FilterRegEdge *edge;
2419 if (reg_desc->nb_edges==reg_desc->nb_alloc_edges) {
2420 reg_desc->nb_alloc_edges += 10;
2421 reg_desc->edges = gf_realloc(reg_desc->edges, sizeof(GF_FilterRegEdge) * reg_desc->nb_alloc_edges);
2422 }
2423 assert(path_weight<0xFF);
2424 assert(k<0xFFFF);
2425 assert(l<0xFFFF);
2426 edge = ®_desc->edges[reg_desc->nb_edges];
2427 memset(edge, 0, sizeof(GF_FilterRegEdge));
2428 edge->src_reg = a_reg;
2429 edge->weight = (u8) path_weight;
2430 edge->src_cap_idx = (u16) k;
2431 edge->dst_cap_idx = (u16) l;
2432
2433 //we inverted the caps, invert the flags
2434 if (loaded_filter_only_flags & EDGE_LOADED_SOURCE_ONLY)
2435 edge->loaded_filter_only |= EDGE_LOADED_DEST_ONLY;
2436 if (loaded_filter_only_flags & EDGE_LOADED_DEST_ONLY)
2437 edge->loaded_filter_only |= EDGE_LOADED_SOURCE_ONLY;
2438 edge->src_stream_type = gf_filter_reg_get_bundle_stream_type(edge->src_reg->freg, edge->src_cap_idx, GF_TRUE);
2439 reg_desc->nb_edges++;
2440 }
2441 }
2442
2443 if ( freg_has_output ) {
2444 u32 loaded_filter_only_flags = 0;
2445
2446 path_weight = gf_filter_caps_to_caps_match(freg, l, a_reg->freg, dst_filter, &bundle_idx, k, &loaded_filter_only_flags, capstore);
2447
2448 if (path_weight && (bundle_idx == k)) {
2449 GF_FilterRegEdge *edge;
2450 if (a_reg->nb_edges==a_reg->nb_alloc_edges) {
2451 a_reg->nb_alloc_edges += 10;
2452 a_reg->edges = gf_realloc(a_reg->edges, sizeof(GF_FilterRegEdge) * a_reg->nb_alloc_edges);
2453 }
2454 edge = &a_reg->edges[a_reg->nb_edges];
2455 edge->src_reg = reg_desc;
2456 edge->weight = (u8) path_weight;
2457 edge->src_cap_idx = (u16) l;
2458 edge->dst_cap_idx = (u16) k;
2459 edge->priority = 0;
2460 edge->loaded_filter_only = loaded_filter_only_flags;
2461 edge->src_stream_type = gf_filter_reg_get_bundle_stream_type(edge->src_reg->freg, edge->src_cap_idx, GF_TRUE);
2462 a_reg->nb_edges++;
2463 }
2464 }
2465 }
2466 }
2467 }
2468 return reg_desc;
2469 }
2470
gf_filter_sess_build_graph(GF_FilterSession * fsess,const GF_FilterRegister * for_reg)2471 void gf_filter_sess_build_graph(GF_FilterSession *fsess, const GF_FilterRegister *for_reg)
2472 {
2473 u32 i, count;
2474 GF_CapsBundleStore capstore;
2475 memset(&capstore, 0, sizeof(GF_CapsBundleStore));
2476
2477 if (!fsess->links) fsess->links = gf_list_new();
2478
2479 if (for_reg) {
2480 GF_FilterRegDesc *freg_desc = gf_filter_reg_build_graph(fsess->links, for_reg, &capstore, NULL, NULL);
2481 if (!freg_desc) {
2482 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to build graph entry for filter %s\n", for_reg->name));
2483 } else {
2484 gf_list_add(fsess->links, freg_desc);
2485 }
2486 } else {
2487 #ifndef GPAC_DISABLE_LOG
2488 u64 start_time = gf_sys_clock_high_res();
2489 #endif
2490 count = gf_list_count(fsess->registry);
2491 for (i=0; i<count; i++) {
2492 const GF_FilterRegister *freg = gf_list_get(fsess->registry, i);
2493 GF_FilterRegDesc *freg_desc = gf_filter_reg_build_graph(fsess->links, freg, &capstore, NULL, NULL);
2494 if (!freg_desc) {
2495 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Failed to build graph entry for filter %s\n", freg->name));
2496 } else {
2497 gf_list_add(fsess->links, freg_desc);
2498 }
2499 }
2500 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Build filter graph in "LLU" us\n", gf_sys_clock_high_res() - start_time));
2501
2502 if (fsess->flags & GF_FS_FLAG_PRINT_CONNECTIONS) {
2503 u32 j;
2504 count = gf_list_count(fsess->links);
2505 for (i=0; i<count; i++) {
2506 GF_FilterRegDesc *freg_desc = gf_list_get(fsess->links, i);
2507 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s sources:", freg_desc->freg->name));
2508 for (j=0; j<freg_desc->nb_edges; j++ ) {
2509 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, (" %s(%d,%d->%d)", freg_desc->edges[j].src_reg->freg->name, freg_desc->edges[j].weight, freg_desc->edges[j].src_cap_idx, freg_desc->edges[j].dst_cap_idx));
2510 }
2511 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("\n"));
2512 }
2513 }
2514 }
2515 if (capstore.bundles_cap_found) gf_free(capstore.bundles_cap_found);
2516 if (capstore.bundles_in_ok) gf_free(capstore.bundles_in_ok);
2517 if (capstore.bundles_in_scores) gf_free(capstore.bundles_in_scores);
2518 }
2519
gf_filter_sess_reset_graph(GF_FilterSession * fsess,const GF_FilterRegister * freg)2520 void gf_filter_sess_reset_graph(GF_FilterSession *fsess, const GF_FilterRegister *freg)
2521 {
2522 gf_mx_p(fsess->links_mx);
2523 //explicit registry removal and not destroying the session
2524 if (freg && fsess->filters) {
2525 s32 reg_idx=-1;
2526 u32 i, count = gf_list_count(fsess->links);
2527 for (i=0; i<count; i++) {
2528 u32 j;
2529 GF_FilterRegDesc *rdesc = gf_list_get(fsess->links, i);
2530 if (rdesc->freg == freg) {
2531 reg_idx = i;
2532 continue;
2533 }
2534 for (j=0; j<rdesc->nb_edges; j++) {
2535 if (rdesc->edges[j].src_reg->freg == freg) {
2536 if (rdesc->nb_edges > j + 1) {
2537 memmove(&rdesc->edges[j], &rdesc->edges[j+1], sizeof (GF_FilterRegEdge) * (rdesc->nb_edges - j - 1));
2538 }
2539 j--;
2540 rdesc->nb_edges--;
2541 }
2542 }
2543 }
2544 if (reg_idx>=0) {
2545 GF_FilterRegDesc *rdesc = gf_list_get(fsess->links, reg_idx);
2546 gf_list_rem(fsess->links, reg_idx);
2547 gf_free(rdesc->edges);
2548 gf_free(rdesc);
2549 }
2550 } else {
2551 while (gf_list_count(fsess->links)) {
2552 GF_FilterRegDesc *rdesc = gf_list_pop_back(fsess->links);
2553 gf_free(rdesc->edges);
2554 gf_free(rdesc);
2555 }
2556 }
2557 gf_mx_v(fsess->links_mx);
2558 }
2559
2560 #ifndef GPAC_DISABLE_LOG
dump_dijstra_edges(Bool is_before,GF_FilterRegDesc * reg_dst,GF_List * dijkstra_nodes)2561 void dump_dijstra_edges(Bool is_before, GF_FilterRegDesc *reg_dst, GF_List *dijkstra_nodes)
2562 {
2563 u32 i, count;
2564 if (! gf_log_tool_level_on(GF_LOG_FILTER, GF_LOG_DEBUG))
2565 return;
2566
2567 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Dijstra edges %s edge solving\n", is_before ? "before" : "after"));
2568
2569 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s sources: ", reg_dst->freg->name));
2570 for (i=0; i<reg_dst->nb_edges; i++) {
2571 GF_FilterRegEdge *edge = ®_dst->edges[i];
2572 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, (" %s(%d(%d),%d,%d->%d)", edge->src_reg->freg->name, edge->status, edge->disabled_depth, edge->weight, edge->src_cap_idx, edge->dst_cap_idx));
2573 }
2574 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("\n"));
2575
2576 count = gf_list_count(dijkstra_nodes);
2577 for (i=0; i<count; i++) {
2578 u32 j;
2579 GF_FilterRegDesc *rdesc = gf_list_get(dijkstra_nodes, i);
2580 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s sources: ", rdesc->freg->name));
2581 for (j=0; j<rdesc->nb_edges; j++) {
2582 GF_FilterRegEdge *edge = &rdesc->edges[j];
2583 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, (" %s(%d(%d),%d,%d->%d)", edge->src_reg->freg->name, edge->status, edge->disabled_depth, edge->weight, edge->src_cap_idx, edge->dst_cap_idx));
2584 }
2585 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("\n"));
2586 }
2587 }
2588 #endif
2589
gf_filter_pid_resolve_link_dijkstra(GF_FilterPid * pid,GF_Filter * dst,const char * prefRegister,Bool reconfigurable_only,GF_List * out_reg_chain)2590 static void gf_filter_pid_resolve_link_dijkstra(GF_FilterPid *pid, GF_Filter *dst, const char *prefRegister, Bool reconfigurable_only, GF_List *out_reg_chain)
2591 {
2592 GF_FilterRegDesc *reg_dst, *result;
2593 GF_List *dijkstra_nodes;
2594 GF_FilterSession *fsess = pid->filter->session;
2595 //build all edges
2596 u32 i, dijsktra_node_count, dijsktra_edge_count, count;
2597 GF_CapsBundleStore capstore;
2598 Bool first;
2599 u32 path_weight, pid_stream_type, max_weight=0;
2600 u64 dijkstra_time_us, sort_time_us, start_time_us = gf_sys_clock_high_res();
2601 const GF_PropertyValue *p;
2602 if (!fsess->links || ! gf_list_count( fsess->links))
2603 gf_filter_sess_build_graph(fsess, NULL);
2604
2605 dijkstra_nodes = gf_list_new();
2606
2607 result = NULL;
2608
2609 pid_stream_type = 0;
2610 p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
2611 if (p) pid_stream_type = p->value.uint;
2612
2613 //1: select all elligible filters for the graph resolution: exclude sources, sinks, explicits, blacklisted and not reconfigurable if we reconfigure
2614 count = gf_list_count(fsess->links);
2615 for (i=0; i<count; i++) {
2616 u32 j;
2617 Bool disable_filter = GF_FALSE;
2618 GF_FilterRegDesc *reg_desc = gf_list_get(fsess->links, i);
2619 const GF_FilterRegister *freg = reg_desc->freg;
2620
2621 //reset state, except for edges which are reseted after each dijkstra resolution
2622 reg_desc->destination = NULL;
2623 reg_desc->cap_idx = 0;
2624 reg_desc->in_edges_enabling = 0;
2625 //set node distance and priority to infinity, whether we are in the final dijsktra set or not
2626 reg_desc->dist = -1;
2627 reg_desc->priority = 0xFF;
2628
2629 //remember our source descriptor - it may be absent of the final node set in case we want reconfigurable only filters
2630 //and the source is not reconfigurable
2631 if (freg == pid->filter->freg)
2632 result = reg_desc;
2633
2634 //don't add source filters except if PID is from source
2635 if (!freg->configure_pid && (freg!=pid->filter->freg)) {
2636 assert(freg != dst->freg);
2637 disable_filter = GF_TRUE;
2638 }
2639 //freg shall be instantiated
2640 else if ((freg->flags & (GF_FS_REG_EXPLICIT_ONLY|GF_FS_REG_SCRIPT)) && (freg != pid->filter->freg) && (freg != dst->freg) ) {
2641 assert(freg != dst->freg);
2642 disable_filter = GF_TRUE;
2643 }
2644 //no output caps, cannot add
2645 else if ((freg != dst->freg) && !gf_filter_has_out_caps(freg->caps, freg->nb_caps)) {
2646 disable_filter = GF_TRUE;
2647 }
2648 //we only want reconfigurable output filters
2649 else if (reconfigurable_only && !freg->reconfigure_output && (freg != dst->freg)) {
2650 assert(freg != dst->freg);
2651 disable_filter = GF_TRUE;
2652 }
2653 //blacklisted filter
2654 else if (gf_list_find(pid->filter->blacklisted, (void *) freg)>=0) {
2655 assert(freg != dst->freg);
2656 if (!reconfigurable_only) {
2657 assert(freg != pid->filter->freg);
2658 }
2659 disable_filter = GF_TRUE;
2660 }
2661 //blacklisted adaptation filter
2662 else if (pid->adapters_blacklist && (gf_list_find(pid->adapters_blacklist, (void *) freg)>=0)) {
2663 assert(freg != dst->freg);
2664 disable_filter = GF_TRUE;
2665 }
2666
2667 //reset edge status
2668 for (j=0; j<reg_desc->nb_edges; j++) {
2669 GF_FilterRegEdge *edge = ®_desc->edges[j];
2670
2671 edge->disabled_depth = 0;
2672 if (disable_filter) {
2673 edge->status = EDGE_STATUS_DISABLED;
2674 continue;
2675 }
2676 edge->status = EDGE_STATUS_NONE;
2677
2678 //connection from source, disable edge if pid caps mismatch
2679 if (edge->src_reg->freg == pid->filter->freg) {
2680 u8 priority=0;
2681 u32 dst_bundle_idx;
2682 //check path weight for the given dst cap - we MUST give the target cap otherwise we might get a default match to another cap
2683 path_weight = gf_filter_pid_caps_match(pid, freg, NULL, &priority, &dst_bundle_idx, pid->filter->dst_filter, edge->dst_cap_idx);
2684 if (!path_weight) {
2685 edge->status = EDGE_STATUS_DISABLED;
2686 continue;
2687 }
2688 }
2689
2690 //if source is not edge origin and edge is only valid for explicitly loaded filters, disable edge
2691 if ((edge->loaded_filter_only & EDGE_LOADED_SOURCE_ONLY) && (edge->src_reg->freg != pid->filter->freg) ) {
2692 edge->status = EDGE_STATUS_DISABLED;
2693 continue;
2694 }
2695
2696 if ((u32) edge->weight + 1 > max_weight)
2697 max_weight = (u32) edge->weight + 1;
2698 }
2699 //not in set
2700 if (disable_filter)
2701 continue;
2702
2703
2704 //do not add destination filter
2705 if (dst->freg == reg_desc->freg) {
2706 reg_desc->dist = 0;
2707 reg_desc->priority = 0;
2708 } else {
2709 gf_list_add(dijkstra_nodes, reg_desc);
2710 }
2711 }
2712 //create a new node for the destination based on elligible filters in the graph
2713 memset(&capstore, 0, sizeof(GF_CapsBundleStore));
2714 reg_dst = gf_filter_reg_build_graph(dijkstra_nodes, dst->freg, &capstore, pid, dst);
2715 reg_dst->dist = 0;
2716 reg_dst->priority = 0;
2717 reg_dst->in_edges_enabling = 0;
2718
2719 //enable edges of destination, potentially disabling edges from source filters to dest
2720 for (i=0; i<reg_dst->nb_edges; i++) {
2721 GF_FilterRegEdge *edge = ®_dst->edges[i];
2722 edge->status = EDGE_STATUS_NONE;
2723
2724 //connection from source, disable edge if pid caps mismatch
2725 if (edge->src_reg->freg == pid->filter->freg) {
2726 u8 priority=0;
2727 u32 dst_bundle_idx;
2728 path_weight = gf_filter_pid_caps_match(pid, dst->freg, dst, &priority, &dst_bundle_idx, pid->filter->dst_filter, -1);
2729 if (!path_weight) {
2730 edge->status = EDGE_STATUS_DISABLED;
2731 continue;
2732 }
2733 if (dst_bundle_idx != edge->dst_cap_idx) {
2734 edge->status = EDGE_STATUS_DISABLED;
2735 continue;
2736 }
2737 }
2738 //the edge source filter is not loaded, disable edges marked for loaded filter only
2739 if ( (edge->loaded_filter_only & EDGE_LOADED_SOURCE_ONLY) && (edge->src_reg->freg != pid->filter->freg) ) {
2740 edge->status = EDGE_STATUS_DISABLED;
2741 continue;
2742 }
2743 //we are relinking to a dynamically loaded filter, only accept edges connecting to the same bundle as when
2744 //the initial resolution was done, unless the edge is marked as loaded destination filter only in which case
2745 //we accept connection
2746 if ((dst->bundle_idx_at_resolution>=0)
2747 && !(edge->loaded_filter_only & EDGE_LOADED_DEST_ONLY)
2748 && (edge->dst_cap_idx !=dst->bundle_idx_at_resolution)
2749 ) {
2750 edge->status = EDGE_STATUS_DISABLED;
2751 continue;
2752 }
2753
2754 if ((u32) edge->weight + 1 > max_weight)
2755 max_weight = edge->weight + 1;
2756 //enable edge and propagate down the graph
2757 edge->status = EDGE_STATUS_ENABLED;
2758
2759 gf_filter_pid_enable_edges(fsess, edge->src_reg, edge->src_cap_idx, pid->filter->freg, 1, edge->src_stream_type, reg_dst, pid, pid_stream_type);
2760 }
2761
2762 if (capstore.bundles_cap_found) gf_free(capstore.bundles_cap_found);
2763 if (capstore.bundles_in_ok) gf_free(capstore.bundles_in_ok);
2764 if (capstore.bundles_in_scores) gf_free(capstore.bundles_in_scores);
2765
2766 #ifndef GPAC_DISABLE_LOG
2767 if (fsess->flags & GF_FS_FLAG_PRINT_CONNECTIONS) {
2768 dump_dijstra_edges(GF_FALSE, reg_dst, dijkstra_nodes);
2769 }
2770 #endif
2771
2772 //remove all filters not used for this resolution (no enabled edges), except source one
2773 count = gf_list_count(dijkstra_nodes);
2774 for (i=0; i<count; i++) {
2775 u32 j, nb_edges;
2776 GF_FilterRegDesc *rdesc = gf_list_get(dijkstra_nodes, i);
2777 if (rdesc->freg == pid->filter->freg) continue;
2778
2779 nb_edges = 0;
2780 for (j=0; j<rdesc->nb_edges; j++) {
2781 GF_FilterRegEdge *edge = &rdesc->edges[j];
2782 if (edge->status == EDGE_STATUS_ENABLED) {
2783 nb_edges++;
2784 break;
2785 }
2786 }
2787
2788 if (!nb_edges) {
2789 gf_list_rem(dijkstra_nodes, i);
2790 i--;
2791 count--;
2792 }
2793 }
2794 #ifndef GPAC_DISABLE_LOG
2795 if (fsess->flags & GF_FS_FLAG_PRINT_CONNECTIONS) {
2796 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filters in dijkstra set:"));
2797 count = gf_list_count(dijkstra_nodes);
2798 for (i=0; i<count; i++) {
2799 GF_FilterRegDesc *rdesc = gf_list_get(dijkstra_nodes, i);
2800 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, (" %s", rdesc->freg->name));
2801 }
2802 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("\n"));
2803 }
2804 #endif
2805
2806 sort_time_us = gf_sys_clock_high_res();
2807
2808
2809 dijsktra_edge_count = 0;
2810 dijsktra_node_count = gf_list_count(dijkstra_nodes)+1;
2811 first = GF_TRUE;
2812 //OK we have the weighted graph, perform a dijkstra on the graph - we assign by weight, and if same weight we check the priority
2813 while (1) {
2814 GF_FilterRegDesc *current_node = NULL;
2815 u32 reg_idx = -1;
2816 u32 min_dist = -1;
2817
2818 count = gf_list_count(dijkstra_nodes);
2819 if (!count) break;
2820
2821 if (first) {
2822 current_node = reg_dst;
2823 } else {
2824 //pick up shortest distance
2825 for (i=0; i<count; i++) {
2826 GF_FilterRegDesc *reg_desc = gf_list_get(dijkstra_nodes, i);
2827 if (reg_desc->dist < min_dist) {
2828 min_dist = reg_desc->dist;
2829 current_node = reg_desc;
2830 reg_idx = i;
2831 }
2832 }
2833 //remove current
2834 if (!current_node)
2835 break;
2836 gf_list_rem(dijkstra_nodes, reg_idx);
2837 }
2838
2839 if (current_node->freg == pid->filter->freg) {
2840 result = current_node;
2841 }
2842 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("[Filters] Dijkstra: testing filter %s\n", current_node->freg->name));
2843
2844 //compute distances
2845 for (i=0; i<current_node->nb_edges; i++) {
2846 u8 priority=0;
2847 GF_FilterRegEdge *redge = ¤t_node->edges[i];
2848 u32 dist;
2849 Bool do_switch = GF_FALSE;
2850 dijsktra_edge_count++;
2851
2852 if (redge->status != EDGE_STATUS_ENABLED)
2853 continue;
2854
2855 dist = current_node->dist + 1;//(max_weight - redge->weight);
2856 if (current_node->freg->flags & GF_FS_REG_HIDE_WEIGHT) {
2857 dist = current_node->dist;
2858 }
2859
2860 priority = redge->priority;
2861 if (redge->src_reg->freg == pid->filter->freg) {
2862 s32 dst_bundle_idx;
2863 if (gf_filter_pid_caps_match(pid, current_node->freg, NULL, &priority, &dst_bundle_idx, dst, redge->dst_cap_idx)) {
2864
2865 } else {
2866 continue;
2867 }
2868 }
2869
2870 if (dist < redge->src_reg->dist) do_switch = GF_TRUE;
2871 else if (dist == redge->src_reg->dist) {
2872 if (prefRegister[0] && (redge->src_reg->destination != current_node) && strstr(prefRegister, current_node->freg->name)) {
2873 do_switch = GF_TRUE;
2874 priority = 0;
2875 } else if ( (dist == redge->src_reg->dist) && (priority < redge->src_reg->priority) )
2876 do_switch = GF_TRUE;
2877 }
2878
2879 if (do_switch) {
2880 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("[Filters] Dijkstra: assign filter %s distance %d destination to %s in cap %d out cap %d priority %d (previous destination %s distance %d priority %d)\n", redge->src_reg->freg->name, dist, current_node->freg->name, redge->src_cap_idx, redge->dst_cap_idx, redge->priority, redge->src_reg->destination ? redge->src_reg->destination->freg->name : "none", redge->src_reg->dist, redge->src_reg->priority ));
2881 redge->src_reg->dist = dist;
2882 redge->src_reg->priority = priority;
2883 redge->src_reg->destination = current_node;
2884 redge->src_reg->cap_idx = redge->src_cap_idx;
2885 } else if (fsess->flags & GF_FS_FLAG_PRINT_CONNECTIONS) {
2886 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("[Filters] Dijkstra: no shorter path from filter %s distance %d from destination %s priority %d (tested %s dist %d priority %d)\n", redge->src_reg->freg->name, redge->src_reg->dist, redge->src_reg->destination ? redge->src_reg->destination->freg->name : "none", redge->priority, current_node->freg->name, dist, redge->src_reg->priority));
2887 }
2888 }
2889 first = GF_FALSE;
2890 }
2891
2892 sort_time_us -= start_time_us;
2893 dijkstra_time_us = gf_sys_clock_high_res() - start_time_us;
2894 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("[Filters] Dijkstra: sorted filters in "LLU" us, Dijkstra done in "LLU" us on %d nodes %d edges\n", sort_time_us, dijkstra_time_us, dijsktra_node_count, dijsktra_edge_count));
2895
2896 if (result && result->destination) {
2897 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("[Filters] Dijkstra result: %s(%d)", result->freg->name, result->cap_idx));
2898 result = result->destination;
2899 while (result->destination) {
2900 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, (" %s(%d)", result->freg->name, result->cap_idx ));
2901 gf_list_add(out_reg_chain, (void *) result->freg);
2902 gf_list_add(out_reg_chain, (void *) &result->freg->caps[result->cap_idx]);
2903 result = result->destination;
2904 }
2905 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, (" %s\n", result->freg->name));
2906 } else {
2907 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("[Filters] Dijkstra: no results found!\n"));
2908 }
2909 gf_list_del(dijkstra_nodes);
2910
2911 gf_free(reg_dst->edges);
2912 gf_free(reg_dst);
2913 }
2914
2915
2916 /*
2917 !Resolves a link between a PID and a destination filter
2918
2919 \param pid source pid to connect
2920 \param dst destination filter to connect source PID to
2921 \param filter_reassigned indicates the filter has been destroyed and reassigned
2922 \param reconfigurable_only indicates the chain should be loaded for reconfigurable filters
2923 \return the first filter in the matching chain, or NULL if no match
2924 */
gf_filter_pid_resolve_link_internal(GF_FilterPid * pid,GF_Filter * dst,Bool * filter_reassigned,Bool reconfigurable_only,u32 * min_chain_len,GF_List * skip_if_in_filter_list,Bool * skipped)2925 static GF_Filter *gf_filter_pid_resolve_link_internal(GF_FilterPid *pid, GF_Filter *dst, Bool *filter_reassigned, Bool reconfigurable_only, u32 *min_chain_len, GF_List *skip_if_in_filter_list, Bool *skipped)
2926 {
2927 GF_Filter *chain_input = NULL;
2928 GF_FilterSession *fsess = pid->filter->session;
2929 GF_List *filter_chain;
2930 u32 i, count;
2931 char prefRegister[1001];
2932 char szForceReg[20];
2933
2934 if (!fsess->max_resolve_chain_len) return NULL;
2935
2936 filter_chain = gf_list_new();
2937
2938 if (!dst) return NULL;
2939
2940 sprintf(szForceReg, "gfreg%c", pid->filter->session->sep_name);
2941 prefRegister[0]=0;
2942 //look for reg given in
2943 concat_reg(pid->filter->session, prefRegister, szForceReg, pid->filter->orig_args ? pid->filter->orig_args : pid->filter->src_args);
2944 concat_reg(pid->filter->session, prefRegister, szForceReg, pid->filter->dst_args);
2945 concat_reg(pid->filter->session, prefRegister, szForceReg, dst->src_args);
2946 concat_reg(pid->filter->session, prefRegister, szForceReg, dst->dst_args);
2947
2948 gf_mx_p(fsess->links_mx);
2949 gf_filter_pid_resolve_link_dijkstra(pid, dst, prefRegister, reconfigurable_only, filter_chain);
2950 gf_mx_v(fsess->links_mx);
2951
2952 count = gf_list_count(filter_chain);
2953 if (min_chain_len) {
2954 *min_chain_len = count;
2955 } else if (count==0) {
2956 Bool can_reassign = GF_TRUE;
2957
2958 //reassign only for source filters
2959 if (pid->filter->num_input_pids) can_reassign = GF_FALSE;
2960 //sticky filters cannot be unloaded
2961 else if (pid->filter->sticky) can_reassign = GF_FALSE;
2962 //if we don't have pending PIDs to setup from the source
2963 else if (pid->filter->out_pid_connection_pending) can_reassign = GF_FALSE;
2964 //if we don't have pending PIDs to setup from the source
2965 else if (pid->filter->num_output_pids) {
2966 u32 k;
2967 for (k=0; k<pid->filter->num_output_pids; k++) {
2968 GF_FilterPid *apid = gf_list_get(pid->filter->output_pids, k);
2969 if (apid->num_destinations) can_reassign = GF_FALSE;
2970 else if ((apid==pid) && (apid->init_task_pending>1)) can_reassign = GF_FALSE;
2971 else if ((apid!=pid) && apid->init_task_pending) can_reassign = GF_FALSE;
2972 if (!can_reassign)
2973 break;
2974 }
2975 }
2976 //if source filter, try to load another filter - we should complete this with a cache of filter sources
2977 if (filter_reassigned && can_reassign) {
2978 if (! *filter_reassigned) {
2979 if (! gf_filter_swap_source_register(pid->filter) ) {
2980 //no filter found for this pid !
2981 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("No suitable filter chain found\n"));
2982 } else {
2983 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Swap source demux to %s\n", pid->filter->freg->name));
2984 }
2985 }
2986 *filter_reassigned = GF_TRUE;
2987 } else if (!reconfigurable_only) {
2988 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("No suitable filter found for pid %s from filter %s\n", pid->name, pid->filter->name));
2989 if (filter_reassigned)
2990 *filter_reassigned = GF_FALSE;
2991 }
2992 } else if (reconfigurable_only && (count>2)) {
2993 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Cannot find filter chain with only one filter handling reconfigurable output for pid %s from filter %s - not supported\n", pid->name, pid->filter->name));
2994 } else {
2995 const char *dst_args = NULL;
2996 const char *args = pid->filter->orig_args ? pid->filter->orig_args : pid->filter->src_args;
2997 GF_FilterPid *a_pid = pid;
2998 GF_Filter *prev_af;
2999
3000 if (skip_if_in_filter_list) {
3001 assert(skipped);
3002 *skipped = GF_FALSE;
3003 u32 nb_skip = gf_list_count(skip_if_in_filter_list);
3004 const GF_FilterRegister *chain_start_freg = gf_list_get(filter_chain, 0);
3005 for (i=0; i<nb_skip; i++) {
3006 GF_Filter *f = gf_list_get(skip_if_in_filter_list, i);
3007 u32 j;
3008 GF_Filter *dest_f = NULL;
3009 Bool true_skip = GF_FALSE;
3010
3011 for (j=0; j<gf_list_count(dst->destination_filters); j++) {
3012 dest_f = gf_list_get(dst->destination_filters, j);
3013 if ((gf_list_find(f->destination_filters, dest_f)>=0) || (gf_list_find(f->destination_links, dest_f)>=0)) {
3014 true_skip = GF_TRUE;
3015 break;
3016 }
3017 dest_f = NULL;
3018 }
3019
3020 for (j=0; j<gf_list_count(dst->destination_links) && !true_skip; j++) {
3021 dest_f = gf_list_get(dst->destination_links, j);
3022 if ((gf_list_find(f->destination_filters, dest_f)>=0) || (gf_list_find(f->destination_links, dest_f)>=0)) {
3023 true_skip = GF_TRUE;
3024 break;
3025 }
3026 dest_f = NULL;
3027 }
3028 if (true_skip) {
3029 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Skip link from %s:%s to %s because both filters share the same destination %s\n", pid->filter->name, pid->name, dst->name, dest_f->name));
3030 *skipped = GF_TRUE;
3031 gf_list_del(filter_chain);
3032 return NULL;
3033 }
3034
3035 if (f->freg == chain_start_freg) {
3036 //store destination as future destination link for this new filter
3037 if (gf_list_find(f->destination_links, dst)<0)
3038 gf_list_add(f->destination_links, dst);
3039
3040 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Skip link from %s:%s to %s because already connected to filter %s which can handle the connection\n", pid->filter->name, pid->name, dst->name, f->name));
3041
3042 *skipped = GF_TRUE;
3043 gf_list_del(filter_chain);
3044 return NULL;
3045 }
3046 }
3047 }
3048
3049 dst_args = dst->src_args ? dst->src_args : dst->orig_args;
3050
3051 while (a_pid) {
3052 GF_FilterPidInst *pidi;
3053 args = a_pid->filter->src_args;
3054 if (!args) args = a_pid->filter->orig_args;
3055 if (args) break;
3056 pidi = gf_list_get(a_pid->filter->input_pids, 0);
3057 if (!pidi) break;
3058 a_pid = pidi->pid;
3059 }
3060
3061 #ifndef GPAC_DISABLE_LOG
3062 if (gf_log_tool_level_on(GF_LOG_FILTER, GF_LOG_INFO)) {
3063 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Solved %sfilter chain from filter %s PID %s to filter %s - dumping chain:\n", reconfigurable_only ? "adaptation " : "", pid->filter->name, pid->name, dst->freg->name));
3064 }
3065 #endif
3066 prev_af = NULL;
3067 for (i=0; i<count; i++) {
3068 GF_Filter *af;
3069 Bool load_first_only = GF_FALSE;
3070 s32 cap_idx = -1;
3071 const GF_FilterRegister *freg;
3072 const GF_FilterCapability *cap = NULL;
3073 u32 k, cur_bundle, bundle_idx=0;
3074 if (i%2) continue;
3075 freg = gf_list_get(filter_chain, i);
3076 cap = gf_list_get(filter_chain, i + 1);
3077 //get the cap bundle index - the cap added to the list is the cap with the same index as the bundle start we want
3078 //(this avoids allocating integers to store the bundle)
3079 for (k=0; k<freg->nb_caps; k++) {
3080 if (&freg->caps[k]==cap) {
3081 bundle_idx = k;
3082 break;
3083 }
3084 }
3085 cur_bundle = 0;
3086 for (k=0; k<freg->nb_caps; k++) {
3087 cap = &freg->caps[k];
3088 if (cur_bundle==bundle_idx) {
3089 cap_idx = k;
3090 break;
3091 }
3092 if (!(cap->flags & GF_CAPFLAG_IN_BUNDLE)) {
3093 cur_bundle++;
3094 }
3095 }
3096 //if first filter has multiple possible outputs, don't bother loading the entire chain since it is likely wrong
3097 //(eg demuxers, we don't know yet what's in the file)
3098 if (!i && gf_filter_out_caps_solved_by_connection(freg, bundle_idx)) {
3099 load_first_only = GF_TRUE;
3100 } else if (i) {
3101 Bool break_chain = GF_FALSE;
3102 u32 j, nb_filters = gf_list_count(fsess->filters);
3103 for (j=0; j<nb_filters; j++) {
3104 GF_Filter *afilter = gf_list_get(fsess->filters, j);
3105 if (afilter->freg != freg) continue;
3106 if (!afilter->dynamic_filter) continue;
3107 if (gf_list_find(pid->filter->destination_links, dst)<0) continue;
3108 if (!afilter->max_extra_pids) continue;
3109
3110 //we load the same dynamic filter and it can accept multiple inputs (probably a mux), we might reuse this filter so stop link resolution now
3111 //not doing so would load e new mux filter which would accept the input pids but with potentially no possible output connections
3112 break_chain = GF_TRUE;
3113 if (prev_af) {
3114 //store destination as future destination link for this new filter
3115 if ( gf_list_find(pid->filter->destination_links, afilter)<0)
3116 gf_list_add(pid->filter->destination_links, afilter);
3117
3118 //remember to which filter we are trying to connect for cap resolution
3119 prev_af->cap_dst_filter = dst;
3120 }
3121 break;
3122 }
3123 if (break_chain) {
3124 break;
3125 }
3126 }
3127
3128 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("\t%s\n", freg->name));
3129
3130 af = gf_filter_new(fsess, freg, args, dst_args, pid->filter->no_dst_arg_inherit ? GF_FILTER_ARG_INHERIT_SOURCE_ONLY : GF_FILTER_ARG_INHERIT, NULL, NULL, GF_TRUE);
3131 if (!af) goto exit;
3132
3133 //remember our target cap bundle on that filter
3134 af->bundle_idx_at_resolution = bundle_idx;
3135 //remember our target cap on that filter
3136 af->cap_idx_at_resolution = cap_idx;
3137 //copy source IDs for all filters in the chain
3138 //we cannot figure out the destination sourceID when initializing PID connection tasks
3139 //by walking up the filter chain because PIDs connection might be pending
3140 //(upper chain not yet fully connected)
3141 if (dst->source_ids)
3142 af->source_ids = gf_strdup(dst->source_ids);
3143
3144 //remember our target filter
3145 if (prev_af)
3146 gf_list_add(prev_af->destination_filters, af);
3147
3148 //last in chain, add dst
3149 if (i+2==count) {
3150 gf_list_add(af->destination_filters, dst);
3151 }
3152 //we will load several filters in chain, add destination to each of the loaded filter so that we remember what was this filter target
3153 //this avoids browing the chain of filters->destination_filters when doing link resolution
3154 else if (!load_first_only) {
3155 gf_list_add(af->destination_filters, dst);
3156 }
3157
3158 //also remember our original target in case we got the link wrong
3159 af->target_filter = pid->filter->target_filter;
3160
3161 prev_af = af;
3162
3163 if (reconfigurable_only) af->is_pid_adaptation_filter = GF_TRUE;
3164
3165 //remember the first in the chain
3166 if (!i) chain_input = af;
3167
3168 if (load_first_only) {
3169 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s needs to be connected to decide its outputs, not loading end of the chain\n", freg->name));
3170 //store destination as future destination link for this new filter
3171 if ( gf_list_find(pid->filter->destination_links, dst)<0)
3172 gf_list_add(pid->filter->destination_links, dst);
3173
3174 //in case we added it, remove the destination filter
3175 gf_list_del_item(af->destination_filters, dst);
3176
3177 //remember to which filter we are trying to connect for cap resolution
3178 af->cap_dst_filter = dst;
3179 break;
3180 }
3181 }
3182 }
3183
3184 exit:
3185 gf_list_del(filter_chain);
3186 return chain_input;
3187 }
3188
gf_filter_pid_resolve_link(GF_FilterPid * pid,GF_Filter * dst,Bool * filter_reassigned)3189 GF_Filter *gf_filter_pid_resolve_link(GF_FilterPid *pid, GF_Filter *dst, Bool *filter_reassigned)
3190 {
3191 return gf_filter_pid_resolve_link_internal(pid, dst, filter_reassigned, GF_FALSE, NULL, NULL, NULL);
3192 }
3193
gf_filter_pid_resolve_link_check_loaded(GF_FilterPid * pid,GF_Filter * dst,Bool * filter_reassigned,GF_List * skip_if_in_filter_list,Bool * skipped)3194 GF_Filter *gf_filter_pid_resolve_link_check_loaded(GF_FilterPid *pid, GF_Filter *dst, Bool *filter_reassigned, GF_List *skip_if_in_filter_list, Bool *skipped)
3195 {
3196 return gf_filter_pid_resolve_link_internal(pid, dst, filter_reassigned, GF_FALSE, NULL, skip_if_in_filter_list, skipped);
3197 }
3198
gf_filter_pid_resolve_link_for_caps(GF_FilterPid * pid,GF_Filter * dst)3199 GF_Filter *gf_filter_pid_resolve_link_for_caps(GF_FilterPid *pid, GF_Filter *dst)
3200 {
3201 return gf_filter_pid_resolve_link_internal(pid, dst, NULL, GF_TRUE, NULL, NULL, NULL);
3202 }
3203
gf_filter_pid_resolve_link_length(GF_FilterPid * pid,GF_Filter * dst)3204 u32 gf_filter_pid_resolve_link_length(GF_FilterPid *pid, GF_Filter *dst)
3205 {
3206 u32 chain_len=0;
3207 gf_filter_pid_resolve_link_internal(pid, dst, NULL, GF_FALSE, &chain_len, NULL, NULL);
3208 return chain_len;
3209 }
3210
gf_filter_pid_set_args_internal(GF_Filter * filter,GF_FilterPid * pid,char * args,u32 argfile_level)3211 static void gf_filter_pid_set_args_internal(GF_Filter *filter, GF_FilterPid *pid, char *args, u32 argfile_level)
3212 {
3213 //parse each arg
3214 while (args) {
3215 u32 p4cc=0;
3216 u32 prop_type=GF_PROP_FORBIDEN;
3217 Bool parse_prop = GF_TRUE;
3218 char *value_next_list = NULL;
3219 char *value_sep = NULL;
3220 char *value, *name;
3221 //look for our arg separator
3222
3223 char *sep = (char *)gf_fs_path_escape_colon(filter->session, args);
3224
3225 if (sep) {
3226 char *xml_start = strchr(args, '<');
3227 u32 len = (u32) (sep-args);
3228 if (xml_start) {
3229 u32 xlen = (u32) (xml_start-args);
3230 if ((xlen < len) && (args[len-1] != '>')) {
3231 while (1) {
3232 sep = strchr(sep+1, filter->session->sep_args);
3233 if (!sep) {
3234 break;
3235 }
3236 len = (u32) (sep-args);
3237 if (args[len-1] == '>')
3238 break;
3239 }
3240 }
3241
3242 }
3243 }
3244
3245 if (sep) sep[0]=0;
3246
3247 if (args[0] != filter->session->sep_frag) {
3248 if (gf_file_exists(args)) {
3249 if (argfile_level<5) {
3250 char szLine[2001];
3251 FILE *arg_file = gf_fopen(args, "rt");
3252 szLine[2000]=0;
3253 while (!gf_feof(arg_file)) {
3254 u32 llen;
3255 char *subarg, *res;
3256 szLine[0] = 0;
3257 res = gf_fgets(szLine, 2000, arg_file);
3258 if (!res) break;
3259 llen = (u32) strlen(szLine);
3260 while (llen && strchr(" \n\r\t", szLine[llen-1])) {
3261 szLine[llen-1]=0;
3262 llen--;
3263 }
3264 if (!llen)
3265 continue;
3266
3267 subarg = szLine;
3268 while (subarg[0] && strchr(" \n\r\t", subarg[0]))
3269 subarg++;
3270 if ((subarg[0] == '/') && (subarg[1] == '/'))
3271 continue;
3272
3273 gf_filter_pid_set_args_internal(filter, pid, subarg, argfile_level+1);
3274 }
3275 gf_fclose(arg_file);
3276 } else {
3277 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Filter argument file has too many nested levels of sub-files, maximum allowed is 5\n"));
3278 }
3279 }
3280 goto skip_arg;
3281 }
3282
3283 value = NULL;
3284 value_sep = strchr(args, filter->session->sep_name);
3285 if (value_sep) {
3286 value_sep[0]=0;
3287 value = value_sep+1;
3288 }
3289 name = args+1;
3290
3291 if (strlen(name)==4) {
3292 p4cc = GF_4CC(name[0], name[1], name[2], name[3]);
3293 if (p4cc) prop_type = gf_props_4cc_get_type(p4cc);
3294 }
3295 if (prop_type==GF_PROP_FORBIDEN) {
3296 p4cc = gf_props_get_id(name);
3297 if (p4cc) prop_type = gf_props_4cc_get_type(p4cc);
3298 }
3299
3300 //look for conditional statements: "(PROP=VAL)VALUE"
3301 while (value && (value[0]=='(')) {
3302 Bool pid_excluded, needs_resolve, prop_not_found, prop_matched;
3303 char prop_dump_buffer[GF_PROP_DUMP_ARG_SIZE];
3304
3305 char *next_val = NULL;
3306 char *closing = strchr(value, ')');
3307 if (!closing) break;
3308
3309 if (!strncmp(value, "()", 2)) {
3310 value = closing+1;
3311 parse_prop = GF_TRUE;
3312 value_next_list = next_val;
3313 break;
3314 }
3315
3316 parse_prop = GF_FALSE;
3317
3318 next_val = strchr(closing, filter->session->sep_list);
3319 if (next_val) next_val[0] = 0;
3320
3321 while (closing) {
3322 char *next_closing;
3323 closing[0] = 0;
3324 prop_matched = filter_pid_check_fragment(pid, value+1, &pid_excluded, &needs_resolve, &prop_not_found, prop_dump_buffer);
3325 if (prop_not_found) prop_matched = GF_FALSE;
3326 closing[0] = ')';
3327
3328 if (!prop_matched)
3329 break;
3330 if (strncmp(closing, ")(", 2)) break;
3331 next_closing = strchr(closing+2, ')');
3332 if (!next_closing) break;
3333
3334 value = closing+1;
3335 closing = next_closing;
3336 }
3337
3338 if (prop_matched) {
3339 value = closing+1;
3340 parse_prop = GF_TRUE;
3341 value_next_list = next_val;
3342 break;
3343 }
3344 if (!next_val) break;
3345 next_val[0] = filter->session->sep_list;
3346 value = next_val+1;
3347 }
3348
3349 if (!parse_prop)
3350 goto skip_arg;
3351
3352
3353 if (prop_type != GF_PROP_FORBIDEN) {
3354 GF_PropertyValue p = gf_props_parse_value(prop_type, name, value, NULL, pid->filter->session->sep_list);
3355 if (prop_type==GF_PROP_NAME) {
3356 p.type = GF_PROP_STRING;
3357 gf_filter_pid_set_property(pid, p4cc, &p);
3358 p.type = GF_PROP_NAME;
3359 } else {
3360 gf_filter_pid_set_property(pid, p4cc, &p);
3361 }
3362 if (prop_type==GF_PROP_STRING_LIST) {
3363 p.value.string_list = NULL;
3364 }
3365 else if (prop_type==GF_PROP_UINT_LIST) {
3366 p.value.uint_list.vals = NULL;
3367 }
3368 gf_props_reset_single(&p);
3369 } else if (value) {
3370 Bool reset_prop=GF_FALSE;
3371 GF_PropertyValue p;
3372 if (!strncmp(value, "bxml@", 5)) {
3373 p = gf_props_parse_value(GF_PROP_DATA_NO_COPY, name, value, NULL, pid->filter->session->sep_list);
3374 } else if (!strncmp(value, "file@", 5)) {
3375 p = gf_props_parse_value(GF_PROP_STRING, name, value, NULL, pid->filter->session->sep_list);
3376 p.type = GF_PROP_STRING_NO_COPY;
3377 } else {
3378 u32 ptype = GF_PROP_FORBIDEN;
3379 char *type_sep = strchr(value, '@');
3380 if (type_sep) {
3381 type_sep[0] = 0;
3382 ptype = gf_props_parse_type(value);
3383 if (ptype==GF_PROP_FORBIDEN) {
3384 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Unrecognized property type %s, defaulting to string\n", value));
3385 } else {
3386 value = type_sep+1;
3387 }
3388 type_sep[0] = '@';
3389 }
3390 memset(&p, 0, sizeof(GF_PropertyValue));
3391 if (ptype == GF_PROP_FORBIDEN) {
3392 p.type = GF_PROP_STRING;
3393 p.value.string = value;
3394 } else {
3395 p = gf_props_parse_value(ptype, name, value, NULL, pid->filter->session->sep_list);
3396 reset_prop = GF_TRUE;
3397 }
3398 }
3399 gf_filter_pid_set_property_dyn(pid, name, &p);
3400 if (reset_prop) gf_props_reset_single(&p);
3401 }
3402 if (value_next_list)
3403 value_next_list[0] = filter->session->sep_list;
3404
3405 skip_arg:
3406 if (value_sep)
3407 value_sep[0] = filter->session->sep_name;
3408
3409 if (sep) {
3410 sep[0] = filter->session->sep_args;
3411 args=sep+1;
3412 } else {
3413 break;
3414 }
3415 }
3416 }
3417
gf_filter_pid_set_args(GF_Filter * filter,GF_FilterPid * pid)3418 static void gf_filter_pid_set_args(GF_Filter *filter, GF_FilterPid *pid)
3419 {
3420 char *args;
3421 if (!filter->src_args && !filter->orig_args) return;
3422 args = filter->orig_args ? filter->orig_args : filter->src_args;
3423
3424 gf_filter_pid_set_args_internal(filter, pid, args, 0);
3425
3426 }
gf_filter_last_id_in_chain(GF_Filter * filter,Bool ignore_first)3427 static const char *gf_filter_last_id_in_chain(GF_Filter *filter, Bool ignore_first)
3428 {
3429 u32 i;
3430 const char *id;
3431 if (!ignore_first) {
3432 if (filter->id) return filter->id;
3433 if (!filter->dynamic_filter) return NULL;
3434 }
3435
3436 for (i=0; i<filter->num_input_pids; i++) {
3437 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
3438 if (pidi->pid->filter->id) return pidi->pid->filter->id;
3439 //stop at first non dyn filter
3440 if (!pidi->pid->filter->dynamic_filter) continue;
3441 id = gf_filter_last_id_in_chain(pidi->pid->filter, GF_FALSE);
3442 if (id) return id;
3443 }
3444 return NULL;
3445 }
3446
gf_filter_pid_retry_caps_negotiate(GF_FilterPid * src_pid,GF_FilterPid * pid,GF_Filter * dst_filter)3447 void gf_filter_pid_retry_caps_negotiate(GF_FilterPid *src_pid, GF_FilterPid *pid, GF_Filter *dst_filter)
3448 {
3449 assert(dst_filter);
3450 src_pid->caps_negociate = pid->caps_negociate;
3451 pid->caps_negociate = NULL;
3452 src_pid->caps_dst_filter = dst_filter;
3453 //blacklist filter for adaptation
3454 if (!src_pid->adapters_blacklist) src_pid->adapters_blacklist = gf_list_new();
3455 gf_list_add(src_pid->adapters_blacklist, (void *) pid->filter->freg);
3456 //once != 0 will trigger reconfiguration, so set this once all vars have been set
3457 safe_int_inc(& src_pid->filter->nb_caps_renegociate );
3458
3459 //disconnect source pid from filter - this will unload the filter itself
3460 gf_fs_post_task(src_pid->filter->session, gf_filter_pid_disconnect_task, pid->filter, src_pid, "pidinst_disconnect", NULL);
3461 }
3462
3463
gf_filter_pid_needs_explicit_resolution(GF_FilterPid * pid,GF_Filter * dst)3464 static Bool gf_filter_pid_needs_explicit_resolution(GF_FilterPid *pid, GF_Filter *dst)
3465 {
3466 u32 i;
3467 const GF_FilterCapability *caps;
3468 u32 nb_caps;
3469 Bool dst_has_raw_cid_in = GF_FALSE;
3470 const GF_PropertyValue *stream_type = gf_filter_pid_get_property_first(pid, GF_PROP_PID_STREAM_TYPE);
3471 if (!stream_type) return GF_TRUE;
3472
3473 if (stream_type->value.uint==GF_STREAM_FILE) return GF_FALSE;
3474 if (stream_type->value.uint==GF_STREAM_ENCRYPTED) {
3475 stream_type = gf_filter_pid_get_property_first(pid, GF_PROP_PID_ORIG_STREAM_TYPE);
3476 if (!stream_type) return GF_TRUE;
3477 }
3478
3479 caps = dst->forced_caps ? dst->forced_caps : dst->freg->caps;
3480 nb_caps = dst->forced_caps ? dst->nb_forced_caps : dst->freg->nb_caps;
3481
3482 for (i=0; i<nb_caps; i++) {
3483 const GF_FilterCapability *cap = &caps[i];
3484 if (!(cap->flags & GF_CAPFLAG_INPUT)) continue;
3485
3486 if (cap->code != GF_PROP_PID_CODECID) continue;
3487 if (cap->val.value.uint==GF_CODECID_RAW)
3488 dst_has_raw_cid_in = GF_TRUE;
3489 }
3490
3491
3492 for (i=0; i<nb_caps; i++) {
3493 const GF_FilterCapability *cap = &caps[i];
3494 if (!(cap->flags & GF_CAPFLAG_INPUT)) continue;
3495
3496 if (cap->code != GF_PROP_PID_STREAM_TYPE) continue;
3497 //output type is file or same media type, allow looking for filter chains
3498 if ((cap->val.value.uint==GF_STREAM_FILE) || (cap->val.value.uint==stream_type->value.uint)) return GF_FALSE;
3499 //allow text|scene|video -> raw video for dynamic compositor
3500 if (dst_has_raw_cid_in && (cap->val.value.uint==GF_STREAM_VISUAL)) {
3501 switch (stream_type->value.uint) {
3502 case GF_STREAM_TEXT:
3503 case GF_STREAM_SCENE:
3504 case GF_STREAM_OD:
3505 return GF_FALSE;
3506 default:
3507 break;
3508 }
3509 }
3510 }
3511 //no mathing type found, we will need an explicit filter to solve this link (ie the link will be to the explicit filter)
3512 return GF_TRUE;
3513 }
3514
add_possible_link_destination(GF_List * possible_linked_resolutions,GF_Filter * filter_dst)3515 static void add_possible_link_destination(GF_List *possible_linked_resolutions, GF_Filter *filter_dst)
3516 {
3517 u32 i;
3518
3519 for (i=0; i<gf_list_count(possible_linked_resolutions); i++) {
3520 GF_Filter *parent = gf_list_get(possible_linked_resolutions, i);
3521 if (parent->max_extra_pids) continue;
3522
3523 if ((gf_list_find(filter_dst->destination_links, parent)>=0) || (gf_list_find(filter_dst->destination_filters, parent)>=0)) {
3524 gf_list_rem(possible_linked_resolutions, i);
3525 gf_list_insert(possible_linked_resolutions, filter_dst, i);
3526 return;
3527 }
3528 if ((gf_list_find(parent->destination_links, filter_dst)>=0) || (gf_list_find(parent->destination_filters, filter_dst)>=0)) {
3529 return;
3530 }
3531 }
3532 gf_list_add(possible_linked_resolutions, filter_dst);
3533 }
3534
3535 #if 0
3536 static void dump_pid_props(GF_FilterPid *pid)
3537 {
3538 u32 idx = 0;
3539 char szDump[GF_PROP_DUMP_ARG_SIZE];
3540 const GF_PropertyEntry *p;
3541 GF_PropertyMap *pmap = gf_list_get(pid->properties, 0);
3542 while (pmap && (p = gf_list_enum(pmap->properties, &idx))) {
3543 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Pid prop %s: %s\n", gf_props_4cc_get_name(p->p4cc), gf_props_dump(p->p4cc, &p->prop, szDump, GF_FALSE) ));
3544 }
3545 }
3546 #endif
3547
3548
gf_filter_pid_init_task(GF_FSTask * task)3549 static void gf_filter_pid_init_task(GF_FSTask *task)
3550 {
3551 u32 i, count;
3552 Bool found_dest=GF_FALSE;
3553 Bool found_matching_sourceid;
3554 Bool can_reassign_filter = GF_FALSE;
3555 Bool can_try_link_resolution=GF_FALSE;
3556 u32 num_pass=0;
3557 GF_List *loaded_filters = NULL;
3558 GF_List *linked_dest_filters = NULL;
3559 GF_List *force_link_resolutions = NULL;
3560 GF_List *possible_linked_resolutions = NULL;
3561 GF_Filter *filter = task->filter;
3562 GF_FilterPid *pid = task->pid;
3563 GF_Filter *dynamic_filter_clone = NULL;
3564 Bool filter_found_but_pid_excluded = GF_FALSE;
3565 Bool ignore_source_ids = GF_FALSE;
3566 const char *filter_id;
3567
3568 if (pid->destroyed || pid->removed) {
3569 assert(pid->init_task_pending);
3570 safe_int_dec(&pid->init_task_pending);
3571 return;
3572 }
3573 pid->props_changed_since_connect = GF_FALSE;
3574
3575 //swap pid is pending on the possible destination filter
3576 if (filter->swap_pidinst_src || filter->swap_pidinst_dst) {
3577 task->requeue_request = GF_TRUE;
3578 task->can_swap = GF_TRUE;
3579 return;
3580 }
3581 if (filter->caps_negociate) {
3582 if (! gf_filter_reconf_output(filter, pid))
3583 return;
3584 }
3585
3586 gf_fs_check_graph_load(filter->session, GF_TRUE);
3587
3588 if (filter->user_pid_props)
3589 gf_filter_pid_set_args(filter, pid);
3590
3591 //since we may have inserted filters in the middle (demuxers typically), get the last explicitely
3592 //loaded ID in the chain
3593 filter_id = gf_filter_last_id_in_chain(filter, GF_FALSE);
3594 if (!filter_id && filter->cloned_from)
3595 filter_id = gf_filter_last_id_in_chain(filter->cloned_from, GF_FALSE);
3596
3597 //we lock the instantiated filter list for the entire resolution process
3598 if (filter->session->filters_mx) gf_mx_p(filter->session->filters_mx);
3599
3600 linked_dest_filters = gf_list_new();
3601 force_link_resolutions = gf_list_new();
3602 possible_linked_resolutions = gf_list_new();
3603
3604 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s:%s init\n", pid->filter->name, pid->name));
3605
3606 //we use at max 3 passes:
3607 //pass 1: try direct connections without loading intermediate filter chains. If PID gets connected, skip other passes
3608 //pass 2: try loading intermediate filter chains, but disable filter register swapping. If PID gets connected, skip last
3609 //pass 3: try loading intermediate filter chains, potentially swapping the source register
3610 restart:
3611
3612 if (num_pass) {
3613 loaded_filters = gf_list_new();
3614 }
3615
3616 found_matching_sourceid = GF_FALSE;
3617 //try to connect pid to all running filters
3618 count = gf_list_count(filter->session->filters);
3619 for (i=0; i<count; i++) {
3620 Bool needs_clone;
3621 Bool cap_matched;
3622 GF_Filter *filter_dst;
3623
3624 single_retry:
3625
3626 filter_dst = gf_list_get(filter->session->filters, i);
3627 //source filter
3628 if (!filter_dst->freg->configure_pid) continue;
3629 if (filter_dst->finalized || filter_dst->removed || filter_dst->marked_for_removal || filter_dst->no_inputs) continue;
3630 if (filter_dst->target_filter == pid->filter) continue;
3631
3632 //we don't allow re-entrant filter registries (eg filter foo of type A output cannot connect to filter bar of type A)
3633 if (pid->pid->filter->freg == filter_dst->freg) {
3634 continue;
3635 }
3636 //we already linked to this one
3637 if (gf_list_find(linked_dest_filters, filter_dst)>=0) {
3638 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s already linked to filter %s\n", pid->name, filter_dst->name));
3639 continue;
3640 }
3641 if (gf_list_count(pid->filter->destination_filters)) {
3642 s32 ours = gf_list_find(pid->filter->destination_filters, filter_dst);
3643 if (ours<0) {
3644 ours = num_pass ? gf_list_del_item(pid->filter->destination_links, filter_dst) : -1;
3645 if (!filter_dst->source_ids && (ours<0)) {
3646 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has destination filters, filter %s not one of them\n", pid->name, filter_dst->name));
3647 continue;
3648 }
3649
3650 pid->filter->dst_filter = NULL;
3651 } else {
3652 pid->filter->dst_filter = filter_dst;
3653 }
3654 }
3655
3656 if (num_pass && gf_list_count(filter->destination_links)) {
3657 s32 ours = gf_list_find(pid->filter->destination_links, filter_dst);
3658 if (ours<0) {
3659 ours = gf_list_find(possible_linked_resolutions, filter_dst);
3660 if (ours<0) {
3661 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has destination links, filter %s not one of them\n", pid->name, filter_dst->name));
3662 continue;
3663 }
3664 }
3665 pid->filter->dst_filter = NULL;
3666 }
3667
3668 if (gf_list_count(filter_dst->source_filters)) {
3669 u32 j, count2 = gf_list_count(filter_dst->source_filters);
3670 for (j=0; j<count2; j++) {
3671 GF_Filter *srcf = gf_list_get(filter_dst->source_filters, j);
3672 if (filter_in_parent_chain(pid->filter, srcf)) {
3673 ignore_source_ids = GF_TRUE;
3674 break;
3675 }
3676 }
3677 }
3678
3679 //if destination accepts only one input and connected or connection pending
3680 //note that if destination uses dynamic clone through source ids, we need to check this filter
3681 if (!filter_dst->max_extra_pids
3682 && !filter_dst->dynamic_source_ids
3683 && (filter_dst->num_input_pids || filter_dst->in_pid_connection_pending)
3684 && (!filter->swap_pidinst_dst || (filter->swap_pidinst_dst->filter != filter_dst))
3685 ) {
3686 //not explicitly clonable, don't connect to it
3687 if (!filter_dst->clonable) {
3688 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s not clonable\n", filter_dst->name));
3689 continue;
3690 }
3691
3692 //explicitly clonable but caps don't match, don't connect to it
3693 if (!gf_filter_pid_caps_match(pid, filter_dst->freg, NULL, NULL, NULL, pid->filter->dst_filter, -1)) {
3694 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s caps does not match clonable filter %s\n", pid->name, filter_dst->name));
3695 continue;
3696 }
3697 }
3698
3699 if (gf_list_find(pid->filter->blacklisted, (void *) filter_dst->freg)>=0) continue;
3700
3701 //we try to load a filter chain, so don't test against filters loaded for another chain
3702 if (filter_dst->dynamic_filter && (filter_dst != pid->filter->dst_filter)) {
3703 //dst was explicitely set and does not match
3704 if (pid->filter->dst_filter) {
3705 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has explicit dest %s not %s\n", pid->name, pid->filter->dst_filter->name, filter_dst->name));
3706 continue;
3707 }
3708 //dst was not set, we may try to connect to this filter if it allows several input
3709 //this is typically the case for muxers instantiated dynamically
3710 if (!filter_dst->max_extra_pids) {
3711 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has explicit dest %s matching but no extra pid possible\n", pid->name, filter_dst->name));
3712 continue;
3713 }
3714 }
3715 //pid->filter->dst_filter NULL and pid->filter->target_filter is not: we had a wrong resolved chain to target
3716 //so only attempt to relink the chain if dst_filter is the expected target
3717 if (!pid->filter->dst_filter && pid->filter->target_filter && (filter_dst != pid->filter->target_filter)) {
3718 if (filter_dst->target_filter != pid->filter->target_filter) {
3719 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has target filter %s not matching %s->%s\n", pid->name, pid->filter->target_filter->name, filter_dst->name, filter_dst->target_filter ? filter_dst->target_filter->name : "null"));
3720 continue;
3721 }
3722 //if the target filter of this filter is the same as ours, try to connect - typically scalable streams decoding
3723 }
3724
3725 //dynamic filters only connect to their destination, unless explicit connections through sources
3726 //we could remove this but this highly complicates PID resolution
3727 if (!filter_dst->source_ids && pid->filter->dynamic_filter && pid->filter->dst_filter && (filter_dst!=pid->filter->dst_filter)) {
3728 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has dest filter %s not matching %s\n", pid->name, pid->filter->dst_filter->name, filter_dst->name));
3729 continue;
3730 }
3731 //walk up through the parent graph and check if this filter is already in. If so don't connect
3732 //since we don't allow re-entrant PIDs
3733 if (filter_in_parent_chain(filter, filter_dst) ) {
3734 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has filter %s in its parent chain\n", pid->name, filter_dst->name));
3735 continue;
3736 }
3737 //if the original filter is in the parent chain of this PID's filter, don't connect (equivalent to re-entrant)
3738 if (filter_dst->cloned_from) {
3739 if (filter_in_parent_chain(filter, filter_dst->cloned_from) ) {
3740 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s has the original of cloned filter %s in its parent chain\n", pid->name, filter_dst->name));
3741 continue;
3742 }
3743 }
3744
3745 //if the filter is in the parent chain of this PID's original filter, don't connect (equivalent to re-entrant)
3746 if (filter->cloned_from) {
3747 if (filter_in_parent_chain(filter->cloned_from, filter_dst) ) {
3748 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s filter is cloned and has filter %s in its clone parent chain\n", pid->name, filter_dst->name));
3749 continue;
3750 }
3751 }
3752
3753 //if we have sourceID info on the destination, check them
3754 needs_clone=GF_FALSE;
3755 if (filter_id) {
3756 if (filter_dst->source_ids) {
3757 Bool pid_excluded=GF_FALSE;
3758 if (!filter_source_id_match(pid, filter_id, filter_dst, &pid_excluded, &needs_clone)) {
3759 Bool not_ours=GF_TRUE;
3760 //if filter is a dynamic one with an ID set, fetch ID from previous filter in chain
3761 //this is need for cases such as "-i source filterFoo @ -o live.mpd":
3762 //the dasher filter will be dynamically loaded AND will also assign itself a filter ID
3763 //due to internal filter (dasher) logic
3764 //that dasher filter ID will be different from the ID assigned by '@' on dst file's sourceID,
3765 //which is the filter ID of filterFoo
3766 if (filter->dynamic_filter && filter->id) {
3767 const char *src_filter_id = gf_filter_last_id_in_chain(filter, GF_TRUE);
3768 if (filter_source_id_match(pid, src_filter_id, filter_dst, &pid_excluded, &needs_clone)) {
3769 not_ours = GF_FALSE;
3770 }
3771 }
3772 if (not_ours) {
3773 if (pid_excluded && !num_pass) filter_found_but_pid_excluded = GF_TRUE;
3774
3775 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s does not match source ID for filter %s\n", pid->name, filter_dst->name));
3776 continue;
3777 }
3778 }
3779 }
3780 //if no source ID on the dst filter, this means the dst filter accepts any possible connections from out filter
3781 //unless prevented for this pid
3782 else if (pid->require_source_id) {
3783 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s requires source ID, not set for filter %s\n", pid->name, filter_dst->name));
3784 continue;
3785 }
3786 }
3787 //no filterID and dst expects only specific filters, continue
3788 else if (filter_dst->source_ids && !ignore_source_ids) {
3789 Bool pid_excluded=GF_FALSE;
3790 if ( (filter_dst->source_ids[0]!='*') && (filter_dst->source_ids[0]!=filter->session->sep_frag)) {
3791 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s does not match filter %s source ID\n", pid->name, filter_dst->name));
3792 continue;
3793 }
3794 if (!filter_source_id_match(pid, "*", filter_dst, &pid_excluded, &needs_clone)) {
3795 if (pid_excluded && !num_pass) filter_found_but_pid_excluded = GF_TRUE;
3796 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s is excluded by filter %s source ID\n", pid->name, filter_dst->name));
3797 continue;
3798 }
3799 }
3800 if (needs_clone) {
3801 //remember this filter as clonable (dynamic source id scheme) if none yet found.
3802 //If we had a matching sourceID, clone is not needed
3803 if (!num_pass && !dynamic_filter_clone && !found_matching_sourceid) {
3804 dynamic_filter_clone = filter_dst;
3805 }
3806 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s needs cloning of filter %s\n", pid->name, filter_dst->name));
3807 continue;
3808 } else if (dynamic_filter_clone && dynamic_filter_clone->freg==filter_dst->freg) {
3809 dynamic_filter_clone = NULL;
3810 }
3811 //remember we had a sourceid match
3812 found_matching_sourceid = GF_TRUE;
3813
3814 can_try_link_resolution = GF_TRUE;
3815
3816 //this is the right destination filter. We however need to check if we don't have a possible destination link
3817 //whose destination is this destination (typically mux > fout/pipe/sock case). If that's the case, swap the destination
3818 //filter with the intermediate node before matching caps and resolving link
3819 if (num_pass) {
3820 u32 k, alt_count = gf_list_count(possible_linked_resolutions);
3821 for (k=0; k<alt_count; k++) {
3822 GF_Filter *adest = gf_list_get(possible_linked_resolutions, k);
3823 //we only apply this if the destination filter has the GF_FS_REG_DYNAMIC_REDIRECT flag set.
3824 //Not doing so could results in broken link resolution:
3825 //PID1(AVC) -> decoder1 -> compositor
3826 //PID2(PNG) -> decoder2 -> compositor
3827 //However this algo would force a connection of PID2 to decoder1 if decoder1 accepts multiple input, regardless of PID2 caps
3828 if (! (adest->freg->flags & GF_FS_REG_DYNAMIC_REDIRECT))
3829 continue;
3830 if ((gf_list_find(adest->destination_filters, filter_dst)>=0) || (gf_list_find(adest->destination_links, filter_dst)>=0) ) {
3831 filter_dst = adest;
3832 gf_list_rem(possible_linked_resolutions, k);
3833 break;
3834 }
3835 }
3836 }
3837
3838
3839 //we have a match, check if caps are OK
3840 cap_matched = gf_filter_pid_caps_match(pid, filter_dst->freg, filter_dst, NULL, NULL, pid->filter->dst_filter, -1);
3841
3842 //if clonable filter and no match, check if we would match the caps without caps override of dest
3843 //note we don't do this on sources for the time being, since this might trigger undesired resolution of file->file
3844 if (!cap_matched && filter_dst->clonable && pid->filter->num_input_pids) {
3845 cap_matched = gf_filter_pid_caps_match(pid, filter_dst->freg, NULL, NULL, NULL, pid->filter->dst_filter, -1);
3846 }
3847
3848 if (!cap_matched) {
3849 Bool skipped = GF_FALSE;
3850 Bool reassigned=GF_FALSE;
3851 GF_Filter *new_f;
3852
3853 //we don't load filter chains if we have a change of media type from anything except file to anything except file
3854 //i.e. transmodality (eg video->audio) can only be done through explicit filters
3855 if (gf_filter_pid_needs_explicit_resolution(pid, filter_dst)) {
3856 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s needs explicit resolution for linking to filter %s\n", pid->name, filter_dst->name));
3857 continue;
3858 }
3859
3860 //we had a destination set during link resolve, and we don't match that filter, consider the link resolution wrong
3861 if (pid->filter->dst_filter && (filter_dst == pid->filter->dst_filter)) {
3862 GF_Filter *old_dst = pid->filter->dst_filter;
3863 pid->filter->dst_filter = NULL;
3864 gf_list_del_item(pid->filter->destination_links, filter_dst);
3865 gf_list_del_item(pid->filter->destination_filters, filter_dst);
3866 //nobody using this filter, destroy
3867 if (old_dst->dynamic_filter
3868 && !old_dst->has_pending_pids
3869 && !old_dst->num_input_pids
3870 && !old_dst->out_pid_connection_pending
3871 ) {
3872 gf_filter_post_remove(old_dst);
3873 }
3874 }
3875 if (!num_pass) {
3876 //we have an explicit link instruction so we must try dynamic link even if we connect to another filter
3877 if (filter_dst->source_ids) {
3878 gf_list_add(force_link_resolutions, filter_dst);
3879 } else {
3880 //register as possible destination link. If a filter already registered is a destination of this possible link
3881 //only the possible link will be kept
3882 add_possible_link_destination(possible_linked_resolutions, filter_dst);
3883 }
3884 continue;
3885 }
3886 filter_found_but_pid_excluded = GF_FALSE;
3887
3888 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Attempting to solve link between PID %s and filter %s\n", pid->name, filter_dst->name));
3889
3890 if (num_pass==1) reassigned = GF_TRUE;
3891 else reassigned = GF_FALSE;
3892 //we pass the list of loaded filters for this pid, so that we don't instanciate twice the same chain start
3893 new_f = gf_filter_pid_resolve_link_check_loaded(pid, filter_dst, &reassigned, loaded_filters, &skipped);
3894
3895 //try to load filters
3896 if (! new_f) {
3897 if (skipped) {
3898 continue;
3899 }
3900 if (pid->filter->session->run_status!=GF_OK) {
3901 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("PID %s:%s init canceled (session abort)\n", pid->filter->name, pid->name));
3902 if (filter->session->filters_mx) gf_mx_v(filter->session->filters_mx);
3903 assert(pid->init_task_pending);
3904 safe_int_dec(&pid->init_task_pending);
3905 if (loaded_filters) gf_list_del(loaded_filters);
3906 gf_list_del(linked_dest_filters);
3907 gf_list_del(force_link_resolutions);
3908 gf_list_del(possible_linked_resolutions);
3909 return;
3910 }
3911
3912 //filter was reassigned (pid is destroyed), return
3913 if (reassigned) {
3914 if (num_pass==1) {
3915 can_reassign_filter = GF_TRUE;
3916 continue;
3917 }
3918 if (filter->session->filters_mx) gf_mx_v(filter->session->filters_mx);
3919 assert(pid->init_task_pending);
3920 safe_int_dec(&pid->init_task_pending);
3921 if (loaded_filters) gf_list_del(loaded_filters);
3922 gf_list_del(linked_dest_filters);
3923 gf_list_del(force_link_resolutions);
3924 gf_list_del(possible_linked_resolutions);
3925 return;
3926 }
3927 //we might had it wrong solving the chain initially, break the chain
3928 if (filter_dst->dynamic_filter && filter_dst->dst_filter) {
3929 GF_Filter *new_dst = filter_dst;
3930 while (new_dst->dst_filter && new_dst->dynamic_filter) {
3931 GF_Filter *f = new_dst;
3932 new_dst = new_dst->dst_filter;
3933 if (!f->num_input_pids && !f->num_output_pids && !f->in_pid_connection_pending) {
3934 gf_filter_post_remove(f);
3935 }
3936 }
3937
3938 pid->filter->dst_filter = NULL;
3939 new_f = gf_filter_pid_resolve_link(pid, new_dst, &reassigned);
3940 if (!new_f) {
3941 if (reassigned) {
3942 if (filter->session->filters_mx) gf_mx_v(filter->session->filters_mx);
3943 assert(pid->init_task_pending);
3944 safe_int_dec(&pid->init_task_pending);
3945 if (loaded_filters) gf_list_del(loaded_filters);
3946 gf_list_del(linked_dest_filters);
3947 gf_list_del(force_link_resolutions);
3948 gf_list_del(possible_linked_resolutions);
3949 return;
3950 } else {
3951 continue;
3952 }
3953 }
3954 //good to go !
3955 } else {
3956 continue;
3957 }
3958 }
3959 gf_list_del_item(filter->destination_links, filter_dst);
3960 filter_dst = new_f;
3961 gf_list_add(loaded_filters, new_f);
3962 }
3963
3964 assert(pid->pid->filter->freg != filter_dst->freg);
3965
3966 safe_int_inc(&pid->filter->out_pid_connection_pending);
3967 gf_filter_pid_post_connect_task(filter_dst, pid);
3968
3969 found_dest = GF_TRUE;
3970 gf_list_add(linked_dest_filters, filter_dst);
3971 gf_list_del_item(filter->destination_links, filter_dst);
3972 /*we are linking to a mux, check all destination filters registered with the muxer and remove them from our possible destination links*/
3973 if (filter_dst->max_extra_pids) {
3974 u32 k=0;
3975 for (k=0; k<gf_list_count(filter_dst->destination_filters); k++) {
3976 GF_Filter *dst_f = gf_list_get(filter_dst->destination_filters, k);
3977 gf_list_del_item(filter->destination_links, dst_f);
3978 }
3979 }
3980 }
3981
3982 if (!num_pass) {
3983 //cleanup forced link resolution
3984 for (i=0; i< gf_list_count(linked_dest_filters); i++) {
3985 GF_Filter *filter_dst = gf_list_get(linked_dest_filters, i);
3986 u32 k=0;
3987 for (k=0; k<gf_list_count(force_link_resolutions); k++) {
3988 GF_Filter *dst_link = gf_list_get(force_link_resolutions, k);
3989 if (//if forced filter is in parent chain (already connected filters), don't force a link
3990 filter_in_parent_chain(filter_dst, dst_link)
3991 || filter_in_parent_chain(dst_link, filter_dst)
3992 //if forced filter is in destination of filter (connection pending), don't force a link
3993 || (gf_list_find(filter_dst->destination_filters, dst_link)>=0)
3994 || (gf_list_find(filter_dst->destination_links, dst_link)>=0)
3995 //if forced filter's target is the same as what we connected to, don't force a link
3996 || (dst_link->target_filter == filter_dst)
3997 ) {
3998 gf_list_rem(force_link_resolutions, k);
3999 k--;
4000 }
4001 }
4002 }
4003 }
4004
4005 if (loaded_filters) {
4006 gf_list_del(loaded_filters);
4007 loaded_filters = NULL;
4008 }
4009
4010 //we still have possible destination links and we can try link resolution, do it
4011 if (!num_pass && gf_list_count(filter->destination_links) && can_try_link_resolution && filter->session->max_resolve_chain_len) {
4012 num_pass = 1;
4013 goto restart;
4014 }
4015 //we must do the second pass if a filter has an explicit link set through source ID
4016 if (!num_pass && gf_list_count(force_link_resolutions)) {
4017 num_pass = 1;
4018 goto restart;
4019 }
4020
4021 //connection task posted, nothing left to do
4022 if (found_dest) {
4023 assert(pid->init_task_pending);
4024 safe_int_dec(&pid->init_task_pending);
4025 if (filter->session->filters_mx) gf_mx_v(filter->session->filters_mx);
4026 pid->filter->disabled = GF_FALSE;
4027 gf_list_del(linked_dest_filters);
4028 gf_list_del(force_link_resolutions);
4029 gf_list_del(possible_linked_resolutions);
4030 gf_fs_check_graph_load(filter->session, GF_FALSE);
4031 return;
4032 }
4033
4034 //on first pass, if we found a clone (eg a filter using freg:#PropName=*), instantiate this clone and redo the pid linking to this clone (last entry in the filter list)
4035 if (dynamic_filter_clone && !num_pass) {
4036 GF_Filter *clone = gf_filter_clone(dynamic_filter_clone);
4037 if (clone) {
4038 assert(dynamic_filter_clone->dynamic_source_ids);
4039 gf_free(clone->source_ids);
4040 clone->source_ids = gf_strdup(dynamic_filter_clone->dynamic_source_ids);
4041 clone->cloned_from = NULL;
4042 count = gf_list_count(filter->session->filters);
4043 gf_list_add(pid->filter->destination_links, clone);
4044 i = count-1;
4045 num_pass = 1;
4046 goto single_retry;
4047 }
4048 }
4049
4050 //nothing found, redo a pass, this time allowing for link resolve
4051 if (!num_pass && can_try_link_resolution && filter->session->max_resolve_chain_len) {
4052 num_pass = 1;
4053 goto restart;
4054 }
4055 if ((num_pass==1) && can_reassign_filter) {
4056 if (filter->session->flags & GF_FS_FLAG_NO_REASSIGN) {
4057 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("PID %s in filter %s not connected, source reassignment was possible but is disabled\n", pid->name, pid->filter->name));
4058 } else {
4059 num_pass = 2;
4060 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("PID %s in filter %s not connected to any loaded filter, trying source reassignment\n", pid->name, pid->filter->name));
4061 goto restart;
4062 }
4063 }
4064
4065 gf_fs_check_graph_load(filter->session, GF_FALSE);
4066
4067 gf_list_del(linked_dest_filters);
4068 gf_list_del(force_link_resolutions);
4069 gf_list_del(possible_linked_resolutions);
4070 if (filter->session->filters_mx) gf_mx_v(filter->session->filters_mx);
4071
4072
4073 filter->num_out_pids_not_connected ++;
4074
4075 GF_FilterEvent evt;
4076 if (filter_found_but_pid_excluded) {
4077 //PID was not included in explicit connection lists
4078 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("PID %s in filter %s not connected to any loaded filter due to source directives\n", pid->name, pid->filter->name));
4079 pid->not_connected = 1;
4080 } else {
4081 //no filter found for this pid !
4082 if (!pid->not_connected_ok && (filter->session->flags & GF_FS_FLAG_FULL_LINK) ) {
4083 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("No filter chain found for PID %s in filter %s to any loaded filters - ABORTING!\n", pid->name, pid->filter->name));
4084 filter->session->last_connect_error = GF_FILTER_NOT_FOUND;
4085 filter->session->run_status = GF_FILTER_NOT_FOUND;
4086 filter->session->in_final_flush = GF_TRUE;
4087 assert(pid->init_task_pending);
4088 safe_int_dec(&pid->init_task_pending);
4089 return;
4090 }
4091
4092 GF_LOG(pid->not_connected_ok ? GF_LOG_DEBUG : GF_LOG_WARNING, GF_LOG_FILTER, ("No filter chain found for PID %s in filter %s to any loaded filters - NOT CONNECTED\n", pid->name, pid->filter->name));
4093 if (pid->filter->freg->process_event) {
4094 GF_FEVT_INIT(evt, GF_FEVT_CONNECT_FAIL, pid);
4095 pid->filter->freg->process_event(filter, &evt);
4096 }
4097 pid->not_connected = 1;
4098 }
4099 GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
4100 gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
4101
4102 GF_FEVT_INIT(evt, GF_FEVT_STOP, pid);
4103 gf_filter_pid_send_event_internal(pid, &evt, GF_TRUE);
4104
4105 gf_filter_pid_set_eos(pid);
4106 if (!pid->not_connected_ok && !(pid->filter->freg->flags & GF_FS_REG_DYNAMIC_PIDS ) && (pid->filter->num_out_pids_not_connected == pid->filter->num_output_pids)) {
4107 pid->filter->disabled = GF_TRUE;
4108
4109 if (can_reassign_filter) {
4110 gf_filter_setup_failure(pid->filter, GF_FILTER_NOT_FOUND);
4111 }
4112 }
4113
4114 if (!filter_found_but_pid_excluded && !pid->not_connected_ok && !filter->session->max_resolve_chain_len) {
4115 filter->session->last_connect_error = GF_FILTER_NOT_FOUND;
4116 }
4117
4118 assert(pid->init_task_pending);
4119 safe_int_dec(&pid->init_task_pending);
4120 return;
4121 }
4122
gf_filter_pid_post_connect_task(GF_Filter * filter,GF_FilterPid * pid)4123 void gf_filter_pid_post_connect_task(GF_Filter *filter, GF_FilterPid *pid)
4124 {
4125 assert(pid->pid);
4126 assert(pid->filter != filter);
4127 assert(pid->filter->freg != filter->freg);
4128 assert(filter->freg->configure_pid);
4129 safe_int_inc(&filter->session->pid_connect_tasks_pending);
4130 safe_int_inc(&filter->in_pid_connection_pending);
4131 gf_fs_post_task_ex(filter->session, gf_filter_pid_connect_task, filter, pid, "pid_connect", NULL, GF_TRUE, GF_FALSE);
4132 }
4133
4134
gf_filter_pid_post_init_task(GF_Filter * filter,GF_FilterPid * pid)4135 void gf_filter_pid_post_init_task(GF_Filter *filter, GF_FilterPid *pid)
4136 {
4137 if (pid->init_task_pending) return;
4138
4139 safe_int_inc(&pid->init_task_pending);
4140 gf_fs_post_task(filter->session, gf_filter_pid_init_task, filter, pid, "pid_init", NULL);
4141 }
4142
4143 GF_EXPORT
gf_filter_pid_set_framing_mode(GF_FilterPid * pid,Bool requires_full_blocks)4144 GF_Err gf_filter_pid_set_framing_mode(GF_FilterPid *pid, Bool requires_full_blocks)
4145 {
4146 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
4147
4148 if (PID_IS_OUTPUT(pid)) {
4149 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to set framing info on an output pid in filter %s\n", pid->filter->name));
4150 return GF_BAD_PARAM;
4151 }
4152 pidinst->requires_full_data_block = requires_full_blocks;
4153 return GF_OK;
4154 }
4155
4156 GF_EXPORT
gf_filter_pid_new(GF_Filter * filter)4157 GF_FilterPid *gf_filter_pid_new(GF_Filter *filter)
4158 {
4159 char szName[30];
4160 GF_FilterPid *pid;
4161 GF_SAFEALLOC(pid, GF_FilterPid);
4162 if (!pid) return NULL;
4163 pid->filter = filter;
4164 pid->destinations = gf_list_new();
4165 pid->properties = gf_list_new();
4166 if (!filter->output_pids) filter->output_pids = gf_list_new();
4167 gf_mx_p(filter->tasks_mx);
4168 gf_list_add(filter->output_pids, pid);
4169 filter->num_output_pids = gf_list_count(filter->output_pids);
4170 gf_mx_v(filter->tasks_mx);
4171 pid->pid = pid;
4172 pid->playback_speed_scaler = GF_FILTER_SPEED_SCALER;
4173
4174 sprintf(szName, "PID%d", filter->num_output_pids);
4175 pid->name = gf_strdup(szName);
4176
4177 filter->has_pending_pids = GF_TRUE;
4178 gf_fq_add(filter->pending_pids, pid);
4179
4180 //by default copy properties if only one input pid
4181 if (filter->num_input_pids==1) {
4182 GF_FilterPid *pidi = gf_list_get(filter->input_pids, 0);
4183 gf_filter_pid_copy_properties(pid, pidi);
4184 }
4185 return pid;
4186 }
4187
gf_filter_pid_del(GF_FilterPid * pid)4188 void gf_filter_pid_del(GF_FilterPid *pid)
4189 {
4190 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s pid %s destruction (%p)\n", pid->filter->name, pid->name, pid));
4191 while (gf_list_count(pid->destinations)) {
4192 gf_filter_pid_inst_del(gf_list_pop_back(pid->destinations));
4193 }
4194 gf_list_del(pid->destinations);
4195
4196 while (gf_list_count(pid->properties)) {
4197 GF_PropertyMap *prop = gf_list_pop_back(pid->properties);
4198 assert(prop->reference_count);
4199 if (safe_int_dec(&prop->reference_count) == 0) {
4200 gf_props_del(prop);
4201 }
4202 }
4203 gf_list_del(pid->properties);
4204
4205 if(pid->caps_negociate) {
4206 assert(pid->caps_negociate->reference_count);
4207 if (safe_int_dec(&pid->caps_negociate->reference_count) == 0) {
4208 gf_props_del(pid->caps_negociate);
4209 }
4210 }
4211
4212 if (pid->adapters_blacklist)
4213 gf_list_del(pid->adapters_blacklist);
4214
4215 if (pid->infos) {
4216 assert(pid->infos->reference_count);
4217 if (safe_int_dec(&pid->infos->reference_count) == 0) {
4218 gf_props_del(pid->infos);
4219 }
4220 }
4221 if (pid->name) gf_free(pid->name);
4222 gf_free(pid);
4223 }
4224
gf_filter_pid_del_task(GF_FSTask * task)4225 void gf_filter_pid_del_task(GF_FSTask *task)
4226 {
4227 gf_filter_pid_del(task->pid);
4228 }
4229
check_new_pid_props(GF_FilterPid * pid,Bool merge_props)4230 static GF_PropertyMap *check_new_pid_props(GF_FilterPid *pid, Bool merge_props)
4231 {
4232 u32 i, nb_recf;
4233 GF_PropertyMap *old_map;
4234 GF_PropertyMap *map;
4235
4236 //see \ref gf_filter_pid_merge_properties_internal for mutex
4237 gf_mx_p(pid->filter->tasks_mx);
4238 old_map = gf_list_last(pid->properties);
4239 gf_mx_v(pid->filter->tasks_mx);
4240
4241 pid->props_changed_since_connect = GF_TRUE;
4242 if (old_map && !pid->request_property_map) {
4243 return old_map;
4244 }
4245 map = gf_props_new(pid->filter);
4246 if (!map)
4247 return NULL;
4248 //see \ref gf_filter_pid_merge_properties_internal for mutex
4249 gf_mx_p(pid->filter->tasks_mx);
4250 gf_list_add(pid->properties, map);
4251 gf_mx_v(pid->filter->tasks_mx);
4252
4253 pid->request_property_map = GF_FALSE;
4254 pid->pid_info_changed = GF_FALSE;
4255
4256 //when creating a new map, ref_count of old map is decremented
4257 if (old_map) {
4258 if (merge_props)
4259 gf_props_merge_property(map, old_map, NULL, NULL);
4260
4261 assert(old_map->reference_count);
4262 if (safe_int_dec(&old_map->reference_count) == 0) {
4263 //see \ref gf_filter_pid_merge_properties_internal for mutex
4264 gf_mx_p(pid->filter->tasks_mx);
4265 gf_list_del_item(pid->properties, old_map);
4266 gf_mx_v(pid->filter->tasks_mx);
4267 gf_props_del(old_map);
4268 }
4269 }
4270
4271 //trick here: we may be reconfigured before any packet is being dispatched
4272 //so we need to manually trigger reconfigure of outputs
4273 nb_recf = 0;
4274 for (i=0; i<pid->num_destinations; i++) {
4275 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, i);
4276 if (!pidi->filter->process_task_queued) {
4277 //remember the pid prop map to use
4278 pidi->reconfig_pid_props = map;
4279 nb_recf++;
4280 }
4281 }
4282 if (nb_recf)
4283 pid->filter->reconfigure_outputs = GF_TRUE;
4284 return map;
4285 }
4286
gf_filter_pid_set_property_full(GF_FilterPid * pid,u32 prop_4cc,const char * prop_name,char * dyn_name,const GF_PropertyValue * value,Bool is_info)4287 static GF_Err gf_filter_pid_set_property_full(GF_FilterPid *pid, u32 prop_4cc, const char *prop_name, char *dyn_name, const GF_PropertyValue *value, Bool is_info)
4288 {
4289 GF_PropertyMap *map;
4290
4291 if (PID_IS_INPUT(pid)) {
4292 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to write property on input PID in filter %s - ignoring\n", pid->filter->name));
4293 return GF_BAD_PARAM;
4294 }
4295
4296 //info property, do not request a new property map
4297 if (is_info) {
4298 if (value && (value->type==GF_PROP_POINTER)) {
4299 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to set info property of type pointer is forbidden (filter %s) - ignoring\n", pid->filter->name));
4300 return GF_BAD_PARAM;
4301 }
4302 map = pid->infos;
4303 if (!map) {
4304 map = pid->infos = gf_props_new(pid->filter);
4305 }
4306 pid->pid_info_changed = GF_TRUE;
4307 } else {
4308 //always merge properties
4309 map = check_new_pid_props(pid, GF_TRUE);
4310 }
4311 if (!map) {
4312 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("No properties for destination pid in filter %s, ignoring reset\n", pid->filter->name));
4313 return GF_OUT_OF_MEM;
4314 }
4315 if (value && (prop_4cc==GF_PROP_PID_TIMESCALE))
4316 map->timescale = value->value.uint;
4317
4318 if (value && (prop_4cc == GF_PROP_PID_ID) && !pid->name) {
4319 char szName[100];
4320 sprintf(szName, "PID%d", value->value.uint);
4321 gf_filter_pid_set_name(pid, szName);
4322 }
4323 return gf_props_set_property(map, prop_4cc, prop_name, dyn_name, value);
4324 }
4325
4326 GF_EXPORT
gf_filter_pid_set_property(GF_FilterPid * pid,u32 prop_4cc,const GF_PropertyValue * value)4327 GF_Err gf_filter_pid_set_property(GF_FilterPid *pid, u32 prop_4cc, const GF_PropertyValue *value)
4328 {
4329 if (!prop_4cc) return GF_BAD_PARAM;
4330 return gf_filter_pid_set_property_full(pid, prop_4cc, NULL, NULL, value, GF_FALSE);
4331 }
4332
4333 GF_EXPORT
gf_filter_pid_set_property_str(GF_FilterPid * pid,const char * name,const GF_PropertyValue * value)4334 GF_Err gf_filter_pid_set_property_str(GF_FilterPid *pid, const char *name, const GF_PropertyValue *value)
4335 {
4336 if (!name) return GF_BAD_PARAM;
4337 return gf_filter_pid_set_property_full(pid, 0, name, NULL, value, GF_FALSE);
4338 }
4339
4340 GF_EXPORT
gf_filter_pid_set_property_dyn(GF_FilterPid * pid,char * name,const GF_PropertyValue * value)4341 GF_Err gf_filter_pid_set_property_dyn(GF_FilterPid *pid, char *name, const GF_PropertyValue *value)
4342 {
4343 if (!name) return GF_BAD_PARAM;
4344 return gf_filter_pid_set_property_full(pid, 0, NULL, name, value, GF_FALSE);
4345 }
4346
4347 GF_EXPORT
gf_filter_pid_set_info(GF_FilterPid * pid,u32 prop_4cc,const GF_PropertyValue * value)4348 GF_Err gf_filter_pid_set_info(GF_FilterPid *pid, u32 prop_4cc, const GF_PropertyValue *value)
4349 {
4350 if (!prop_4cc) return GF_BAD_PARAM;
4351 return gf_filter_pid_set_property_full(pid, prop_4cc, NULL, NULL, value, GF_TRUE);
4352 }
4353
4354 GF_EXPORT
gf_filter_pid_set_info_str(GF_FilterPid * pid,const char * name,const GF_PropertyValue * value)4355 GF_Err gf_filter_pid_set_info_str(GF_FilterPid *pid, const char *name, const GF_PropertyValue *value)
4356 {
4357 if (!name) return GF_BAD_PARAM;
4358 return gf_filter_pid_set_property_full(pid, 0, name, NULL, value, GF_TRUE);
4359 }
4360
4361 GF_EXPORT
gf_filter_pid_set_info_dyn(GF_FilterPid * pid,char * name,const GF_PropertyValue * value)4362 GF_Err gf_filter_pid_set_info_dyn(GF_FilterPid *pid, char *name, const GF_PropertyValue *value)
4363 {
4364 if (!name) return GF_BAD_PARAM;
4365 return gf_filter_pid_set_property_full(pid, 0, NULL, name, value, GF_TRUE);
4366 }
4367
gf_filter_pid_negociate_property_full(GF_FilterPid * pid,u32 prop_4cc,const char * prop_name,char * dyn_name,const GF_PropertyValue * value)4368 static GF_Err gf_filter_pid_negociate_property_full(GF_FilterPid *pid, u32 prop_4cc, const char *prop_name, char *dyn_name, const GF_PropertyValue *value)
4369 {
4370 GF_FilterPidInst *pidi = (GF_FilterPidInst *) pid;
4371 if (!prop_4cc) return GF_BAD_PARAM;
4372
4373 if (PID_IS_OUTPUT(pid)) {
4374 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to negociate property on output PID in filter %s - ignoring\n", pid->filter->name));
4375 return GF_BAD_PARAM;
4376 }
4377 pid = pid->pid;
4378 if (!pid->caps_negociate) {
4379 assert(!pid->caps_negociate_pidi);
4380 pid->caps_negociate = gf_props_new(pid->filter);
4381 pid->caps_negociate_pidi = pidi;
4382 //we start a new caps negotiation step, reset any blacklist on pid
4383 if (pid->adapters_blacklist) {
4384 gf_list_del(pid->adapters_blacklist);
4385 pid->adapters_blacklist = NULL;
4386 }
4387 safe_int_inc(&pid->filter->nb_caps_renegociate);
4388 }
4389 //pid is end of stream of pid instance has packet pendings, we will need a new chain to adapt these packets formats
4390 if (pid->has_seen_eos || gf_fq_count(pidi->packets)) {
4391 gf_fs_post_task(pid->filter->session, gf_filter_renegociate_output_task, pid->filter, NULL, "filter renegociate", NULL);
4392 }
4393 return gf_props_set_property(pid->caps_negociate, prop_4cc, prop_name, dyn_name, value);
4394 }
4395
4396 GF_EXPORT
gf_filter_pid_negociate_property(GF_FilterPid * pid,u32 prop_4cc,const GF_PropertyValue * value)4397 GF_Err gf_filter_pid_negociate_property(GF_FilterPid *pid, u32 prop_4cc, const GF_PropertyValue *value)
4398 {
4399 if (!prop_4cc) return GF_BAD_PARAM;
4400 return gf_filter_pid_negociate_property_full(pid, prop_4cc, NULL, NULL, value);
4401 }
4402
4403 GF_EXPORT
gf_filter_pid_negociate_property_str(GF_FilterPid * pid,const char * name,const GF_PropertyValue * value)4404 GF_Err gf_filter_pid_negociate_property_str(GF_FilterPid *pid, const char *name, const GF_PropertyValue *value)
4405 {
4406 if (!name) return GF_BAD_PARAM;
4407 return gf_filter_pid_negociate_property_full(pid, 0, name, NULL, value);
4408 }
4409
4410 GF_EXPORT
gf_filter_pid_negociate_property_dyn(GF_FilterPid * pid,char * name,const GF_PropertyValue * value)4411 GF_Err gf_filter_pid_negociate_property_dyn(GF_FilterPid *pid, char *name, const GF_PropertyValue *value)
4412 {
4413 if (!name) return GF_BAD_PARAM;
4414 return gf_filter_pid_negociate_property_full(pid, 0, NULL, name, value);
4415 }
4416
4417
filter_pid_get_prop_map(GF_FilterPid * pid,Bool first_prop_if_output)4418 static GF_PropertyMap *filter_pid_get_prop_map(GF_FilterPid *pid, Bool first_prop_if_output)
4419 {
4420 if (PID_IS_INPUT(pid)) {
4421 GF_FilterPidInst *pidi = (GF_FilterPidInst *) pid;
4422 //first time we access the props, use the first entry in the property list
4423 if (!pidi->props) {
4424 //see \ref gf_filter_pid_merge_properties_internal for mutex
4425 gf_mx_p(pid->filter->tasks_mx);
4426 pidi->props = gf_list_get(pid->pid->properties, 0);
4427 gf_mx_v(pid->filter->tasks_mx);
4428 assert(pidi->props);
4429 safe_int_inc(&pidi->props->reference_count);
4430 }
4431 return pidi->props;
4432 } else {
4433 GF_PropertyMap *res_map = NULL;
4434 pid = pid->pid;
4435 if (pid->local_props) return pid->local_props;
4436
4437 //see \ref gf_filter_pid_merge_properties_internal for mutex
4438 gf_mx_p(pid->filter->tasks_mx);
4439 if (first_prop_if_output)
4440 res_map = gf_list_get(pid->properties, 0);
4441 else
4442 res_map = gf_list_last(pid->properties);
4443 gf_mx_v(pid->filter->tasks_mx);
4444 return res_map;
4445 }
4446 return NULL;
4447 }
4448
4449 GF_EXPORT
gf_filter_pid_get_property(GF_FilterPid * pid,u32 prop_4cc)4450 const GF_PropertyValue *gf_filter_pid_get_property(GF_FilterPid *pid, u32 prop_4cc)
4451 {
4452 GF_PropertyMap *map = filter_pid_get_prop_map(pid, GF_FALSE);
4453 if (!map)
4454 return NULL;
4455 return gf_props_get_property(map, prop_4cc, NULL);
4456 }
4457
gf_filter_pid_get_property_first(GF_FilterPid * pid,u32 prop_4cc)4458 const GF_PropertyValue *gf_filter_pid_get_property_first(GF_FilterPid *pid, u32 prop_4cc)
4459 {
4460 GF_PropertyMap *map = filter_pid_get_prop_map(pid, GF_TRUE);
4461 if (!map)
4462 return NULL;
4463 return gf_props_get_property(map, prop_4cc, NULL);
4464 }
4465
4466 GF_EXPORT
gf_filter_pid_get_property_str(GF_FilterPid * pid,const char * prop_name)4467 const GF_PropertyValue *gf_filter_pid_get_property_str(GF_FilterPid *pid, const char *prop_name)
4468 {
4469 GF_PropertyMap *map = filter_pid_get_prop_map(pid, GF_FALSE);
4470 if (!map)
4471 return NULL;
4472 return gf_props_get_property(map, 0, prop_name);
4473 }
4474
gf_filter_pid_get_property_str_first(GF_FilterPid * pid,const char * prop_name)4475 const GF_PropertyValue *gf_filter_pid_get_property_str_first(GF_FilterPid *pid, const char *prop_name)
4476 {
4477 GF_PropertyMap *map = filter_pid_get_prop_map(pid, GF_TRUE);
4478 if (!map)
4479 return NULL;
4480 return gf_props_get_property(map, 0, prop_name);
4481 }
4482
gf_filter_pid_get_property_entry(GF_FilterPid * pid,u32 prop_4cc)4483 const GF_PropertyEntry *gf_filter_pid_get_property_entry(GF_FilterPid *pid, u32 prop_4cc)
4484 {
4485 GF_PropertyMap *map = filter_pid_get_prop_map(pid, GF_FALSE);
4486 if (!map)
4487 return NULL;
4488 return gf_props_get_property_entry(map, prop_4cc, NULL);
4489 }
4490
4491 GF_EXPORT
gf_filter_pid_get_property_entry_str(GF_FilterPid * pid,const char * prop_name)4492 const GF_PropertyEntry *gf_filter_pid_get_property_entry_str(GF_FilterPid *pid, const char *prop_name)
4493 {
4494 GF_PropertyMap *map = filter_pid_get_prop_map(pid, GF_FALSE);
4495 if (!map)
4496 return NULL;
4497 return gf_props_get_property_entry(map, 0, prop_name);
4498 }
4499
gf_filter_pid_get_info_internal(GF_FilterPid * pid,u32 prop_4cc,const char * prop_name,Bool first_call,GF_PropertyEntry ** propentry)4500 static const GF_PropertyValue *gf_filter_pid_get_info_internal(GF_FilterPid *pid, u32 prop_4cc, const char *prop_name, Bool first_call, GF_PropertyEntry **propentry)
4501 {
4502 u32 i, count;
4503 const GF_PropertyEntry *prop_ent = NULL;
4504 GF_PropertyMap *map;
4505 *propentry = NULL;
4506
4507 if (first_call) {
4508 gf_mx_p(pid->filter->session->info_mx);
4509 }
4510 map = filter_pid_get_prop_map(pid, GF_FALSE);
4511
4512 if (map) {
4513 prop_ent = gf_props_get_property_entry(map, prop_4cc, prop_name);
4514 if (prop_ent) goto exit;
4515 }
4516 if (pid->pid->infos) {
4517 prop_ent = gf_props_get_property_entry(pid->pid->infos, prop_4cc, prop_name);
4518 if (prop_ent) goto exit;
4519 }
4520 if (PID_IS_OUTPUT(pid)) {
4521 prop_ent = NULL;
4522 goto exit;
4523 }
4524 pid = pid->pid;
4525 if (pid->infos) {
4526 prop_ent = gf_props_get_property_entry(pid->infos, prop_4cc, prop_name);
4527 if (prop_ent) goto exit;
4528 }
4529
4530 count = gf_list_count(pid->filter->input_pids);
4531 for (i=0; i<count; i++) {
4532 const GF_PropertyValue *prop;
4533 GF_FilterPid *pidinst = gf_list_get(pid->filter->input_pids, i);
4534 if (!pidinst->pid) continue;
4535
4536 prop = gf_filter_pid_get_info_internal((GF_FilterPid *)pidinst, prop_4cc, prop_name, GF_FALSE, propentry);
4537 if (prop) {
4538 prop_ent = *propentry;
4539 goto exit;
4540 }
4541 }
4542 prop_ent = NULL;
4543
4544 exit:
4545 if (first_call) {
4546 gf_mx_v(pid->filter->session->info_mx);
4547 }
4548 if (!prop_ent) {
4549 *propentry = NULL;
4550 return NULL;
4551 }
4552 if (! (*propentry)) {
4553 *propentry = (GF_PropertyEntry *) prop_ent;
4554 safe_int_inc(&prop_ent->reference_count);
4555 }
4556 return &prop_ent->prop;
4557 }
4558
4559 GF_EXPORT
gf_filter_pid_get_info(GF_FilterPid * pid,u32 prop_4cc,GF_PropertyEntry ** propentry)4560 const GF_PropertyValue *gf_filter_pid_get_info(GF_FilterPid *pid, u32 prop_4cc, GF_PropertyEntry **propentry)
4561 {
4562 if (!propentry) return NULL;
4563 if (*propentry) {
4564 gf_filter_release_property(*propentry);
4565 *propentry = NULL;
4566 }
4567 return gf_filter_pid_get_info_internal(pid, prop_4cc, NULL, GF_TRUE, propentry);
4568 }
4569
4570 GF_EXPORT
gf_filter_pid_get_info_str(GF_FilterPid * pid,const char * prop_name,GF_PropertyEntry ** propentry)4571 const GF_PropertyValue *gf_filter_pid_get_info_str(GF_FilterPid *pid, const char *prop_name, GF_PropertyEntry **propentry)
4572 {
4573 if (!propentry) return NULL;
4574 if (*propentry) {
4575 gf_filter_release_property(*propentry);
4576 *propentry = NULL;
4577 }
4578 return gf_filter_pid_get_info_internal(pid, 0, prop_name, GF_TRUE, propentry);
4579 }
4580
4581 GF_EXPORT
gf_filter_pid_enum_info(GF_FilterPid * pid,u32 * idx,u32 * prop_4cc,const char ** prop_name)4582 const GF_PropertyValue *gf_filter_pid_enum_info(GF_FilterPid *pid, u32 *idx, u32 *prop_4cc, const char **prop_name)
4583 {
4584 u32 i, count, cur_idx=0, nb_in_pid=0;
4585
4586 if (PID_IS_OUTPUT(pid)) {
4587 return NULL;
4588 }
4589 pid = pid->pid;
4590 cur_idx = *idx;
4591 if (pid->infos) {
4592 cur_idx = *idx;
4593 const GF_PropertyValue *prop = gf_props_enum_property(pid->infos, &cur_idx, prop_4cc, prop_name);
4594 if (prop) {
4595 *idx = cur_idx;
4596 return prop;
4597 }
4598 nb_in_pid = cur_idx;
4599 cur_idx = *idx - nb_in_pid;
4600 }
4601
4602 count = gf_list_count(pid->filter->input_pids);
4603 for (i=0; i<count; i++) {
4604 u32 sub_idx = cur_idx;
4605 const GF_PropertyValue * prop;
4606 GF_FilterPid *pidinst = gf_list_get(pid->filter->input_pids, i);
4607 prop = gf_filter_pid_enum_info((GF_FilterPid *)pidinst, &sub_idx, prop_4cc, prop_name);
4608 if (prop) {
4609 *idx = nb_in_pid + sub_idx;
4610 return prop;
4611 }
4612 nb_in_pid += sub_idx;
4613 cur_idx = *idx - nb_in_pid;
4614 }
4615 return NULL;
4616 }
4617
4618
gf_filter_get_info_internal(GF_Filter * filter,u32 prop_4cc,const char * prop_name,GF_PropertyEntry ** propentry)4619 static const GF_PropertyValue *gf_filter_get_info_internal(GF_Filter *filter, u32 prop_4cc, const char *prop_name, GF_PropertyEntry **propentry)
4620 {
4621 u32 i, count;
4622 const GF_PropertyValue *prop=NULL;
4623
4624 gf_mx_p(filter->session->info_mx);
4625
4626 //TODO avoid doing back and forth ...
4627 count = gf_list_count(filter->output_pids);
4628 for (i=0; i<count; i++) {
4629 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
4630 prop = gf_filter_pid_get_info_internal(pid, prop_4cc, prop_name, GF_FALSE, propentry);
4631 if (prop) {
4632 gf_mx_v(filter->session->info_mx);
4633 return prop;
4634 }
4635 }
4636 count = gf_list_count(filter->input_pids);
4637 for (i=0; i<count; i++) {
4638 GF_FilterPidInst *pidinst = gf_list_get(filter->input_pids, i);
4639 prop = gf_filter_pid_get_info_internal(pidinst->pid, prop_4cc, prop_name, GF_FALSE, propentry);
4640 if (prop) {
4641 gf_mx_v(filter->session->info_mx);
4642 return prop;
4643 }
4644 }
4645 gf_mx_v(filter->session->info_mx);
4646 return NULL;
4647 }
4648
4649 GF_EXPORT
gf_filter_get_info(GF_Filter * filter,u32 prop_4cc,GF_PropertyEntry ** propentry)4650 const GF_PropertyValue *gf_filter_get_info(GF_Filter *filter, u32 prop_4cc, GF_PropertyEntry **propentry)
4651 {
4652 if (!propentry) return NULL;
4653 if (*propentry) {
4654 gf_filter_release_property(*propentry);
4655 *propentry = NULL;
4656 }
4657 return gf_filter_get_info_internal(filter, prop_4cc, NULL, propentry);
4658 }
4659
4660 GF_EXPORT
gf_filter_get_info_str(GF_Filter * filter,const char * prop_name,GF_PropertyEntry ** propentry)4661 const GF_PropertyValue *gf_filter_get_info_str(GF_Filter *filter, const char *prop_name, GF_PropertyEntry **propentry)
4662 {
4663 if (!propentry) return NULL;
4664 if (*propentry) {
4665 gf_filter_release_property(*propentry);
4666 *propentry = NULL;
4667 }
4668 return gf_filter_get_info_internal(filter, 0, prop_name, propentry);
4669 }
4670
4671 GF_EXPORT
gf_filter_release_property(GF_PropertyEntry * propentry)4672 void gf_filter_release_property(GF_PropertyEntry *propentry)
4673 {
4674 if (propentry) {
4675 gf_props_del_property(propentry);
4676 }
4677 }
4678
gf_filter_pid_reset_properties(GF_FilterPid * pid)4679 GF_Err gf_filter_pid_reset_properties(GF_FilterPid *pid)
4680 {
4681 GF_PropertyMap *map;
4682
4683 if (PID_IS_INPUT(pid)) {
4684 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to reset all properties on input PID in filter %s - ignoring\n", pid->filter->name));
4685 return GF_BAD_PARAM;
4686 }
4687 //don't merge properties, we will reset them anyway
4688 map = check_new_pid_props(pid, GF_FALSE);
4689
4690 if (!map) {
4691 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("No properties for destination pid in filter %s, ignoring reset\n", pid->filter->name));
4692 return GF_OUT_OF_MEM;
4693 }
4694 gf_props_reset(map);
4695 return GF_OK;
4696
4697 }
4698
gf_filter_pid_merge_properties_internal(GF_FilterPid * dst_pid,GF_FilterPid * src_pid,gf_filter_prop_filter filter_prop,void * cbk,Bool do_copy)4699 static GF_Err gf_filter_pid_merge_properties_internal(GF_FilterPid *dst_pid, GF_FilterPid *src_pid, gf_filter_prop_filter filter_prop, void *cbk, Bool do_copy)
4700 {
4701 GF_PropertyMap *dst_props, *src_props, *old_dst_props=NULL;
4702
4703 if (PID_IS_INPUT(dst_pid)) {
4704 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to reset all properties on input PID in filter %s - ignoring\n", dst_pid->filter->name));
4705 return GF_BAD_PARAM;
4706 }
4707 if (do_copy) {
4708 gf_mx_p(src_pid->filter->tasks_mx);
4709 old_dst_props = gf_list_last(dst_pid->properties);
4710 gf_mx_v(src_pid->filter->tasks_mx);
4711 }
4712
4713 //don't merge properties with old state we merge with source pid
4714 dst_props = check_new_pid_props(dst_pid, GF_FALSE);
4715
4716 if (!dst_props) {
4717 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("No properties for destination pid in filter %s, ignoring reset\n", dst_pid->filter->name));
4718 return GF_OUT_OF_MEM;
4719 }
4720
4721 src_pid = src_pid->pid;
4722 //our list is not thread-safe, so we must lock the filter when destroying the props
4723 //otherwise gf_list_last() (this caller) might use the last entry while another threads sets this last entry to NULL
4724 gf_mx_p(src_pid->filter->tasks_mx);
4725 src_props = gf_list_last(src_pid->properties);
4726 gf_mx_v(src_pid->filter->tasks_mx);
4727 if (!src_props) {
4728 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("No properties for source pid in filter %s, ignoring merge\n", src_pid->filter->name));
4729 return GF_OK;
4730 }
4731 if (src_pid->name && !old_dst_props)
4732 gf_filter_pid_set_name(dst_pid, src_pid->name);
4733
4734 gf_props_reset(dst_props);
4735 if (old_dst_props) {
4736 GF_Err e = gf_props_merge_property(dst_props, old_dst_props, NULL, NULL);
4737 if (e) return e;
4738 }
4739 return gf_props_merge_property(dst_props, src_props, filter_prop, cbk);
4740 }
4741
4742 GF_EXPORT
gf_filter_pid_merge_properties(GF_FilterPid * dst_pid,GF_FilterPid * src_pid,gf_filter_prop_filter filter_prop,void * cbk)4743 GF_Err gf_filter_pid_merge_properties(GF_FilterPid *dst_pid, GF_FilterPid *src_pid, gf_filter_prop_filter filter_prop, void *cbk )
4744 {
4745 return gf_filter_pid_merge_properties_internal(dst_pid, src_pid, filter_prop, cbk, GF_TRUE);
4746 }
4747 GF_EXPORT
gf_filter_pid_copy_properties(GF_FilterPid * dst_pid,GF_FilterPid * src_pid)4748 GF_Err gf_filter_pid_copy_properties(GF_FilterPid *dst_pid, GF_FilterPid *src_pid)
4749 {
4750 return gf_filter_pid_merge_properties_internal(dst_pid, src_pid, NULL, NULL, GF_FALSE);
4751 }
4752
4753 GF_EXPORT
gf_filter_pid_get_packet_count(GF_FilterPid * pid)4754 u32 gf_filter_pid_get_packet_count(GF_FilterPid *pid)
4755 {
4756 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
4757 if (PID_IS_OUTPUT(pid)) {
4758 pidinst = gf_list_get(pid->destinations, 0);
4759 if (! pidinst) return 0;
4760 return gf_fq_count(pidinst->packets) - pidinst->nb_eos_signaled - pidinst->nb_clocks_signaled;
4761
4762 } else {
4763 if (pidinst->discard_packets) return 0;
4764 return gf_fq_count(pidinst->packets) - pidinst->nb_eos_signaled - pidinst->nb_clocks_signaled;
4765 }
4766 }
4767
gf_filter_pid_filter_internal_packet(GF_FilterPidInst * pidi,GF_FilterPacketInstance * pcki)4768 static Bool gf_filter_pid_filter_internal_packet(GF_FilterPidInst *pidi, GF_FilterPacketInstance *pcki)
4769 {
4770 Bool is_internal = GF_FALSE;
4771 u32 ctype = (pcki->pck->info.flags & GF_PCK_CMD_MASK);
4772 if (ctype == GF_PCK_CMD_PID_EOS ) {
4773 pcki->pid->is_end_of_stream = pcki->pid->pid->has_seen_eos ? GF_TRUE : GF_FALSE;
4774 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Found EOS packet in PID %s in filter %s - eos %d\n", pidi->pid->name, pidi->filter->name, pcki->pid->pid->has_seen_eos));
4775 assert(pcki->pid->nb_eos_signaled);
4776 safe_int_dec(&pcki->pid->nb_eos_signaled);
4777 is_internal = GF_TRUE;
4778 } else if (ctype == GF_PCK_CMD_PID_REM) {
4779 gf_fs_post_task(pidi->filter->session, gf_filter_pid_disconnect_task, pidi->filter, pidi->pid, "pidinst_disconnect", NULL);
4780
4781 is_internal = GF_TRUE;
4782 }
4783 ctype = (pcki->pck->info.flags & GF_PCK_CKTYPE_MASK) >> GF_PCK_CKTYPE_POS;
4784
4785 if (ctype) {
4786 if (pcki->pid->handles_clock_references) return GF_FALSE;
4787 assert(pcki->pid->nb_clocks_signaled);
4788 safe_int_dec(&pcki->pid->nb_clocks_signaled);
4789 //signal destination
4790 assert(!pcki->pid->filter->next_clock_dispatch_type || !pcki->pid->filter->num_output_pids);
4791
4792 pcki->pid->filter->next_clock_dispatch = pcki->pck->info.cts;
4793 pcki->pid->filter->next_clock_dispatch_timescale = pcki->pck->pid_props->timescale;
4794 pcki->pid->filter->next_clock_dispatch_type = ctype;
4795
4796 //keep clock values but only override clock type if no discontinuity is pending
4797 pcki->pid->last_clock_value = pcki->pck->info.cts;
4798 pcki->pid->last_clock_timescale = pcki->pck->pid_props->timescale;
4799 if (pcki->pid->last_clock_type != GF_FILTER_CLOCK_PCR_DISC)
4800 pcki->pid->last_clock_type = ctype;
4801
4802 if (ctype == GF_FILTER_CLOCK_PCR_DISC) {
4803 assert(pcki->pid->last_clock_type == GF_FILTER_CLOCK_PCR_DISC);
4804 }
4805 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Internal clock reference packet filtered - PID %s clock ref "LLU"/%d - type %d\n", pcki->pid->pid->name, pcki->pid->last_clock_value, pcki->pid->last_clock_timescale, pcki->pid->last_clock_type));
4806 //the following call to drop_packet will trigger clock forwarding to all output pids
4807 is_internal = GF_TRUE;
4808 }
4809
4810 if (is_internal) gf_filter_pid_drop_packet((GF_FilterPid *)pidi);
4811 return is_internal;
4812 }
4813
4814 GF_EXPORT
gf_filter_pid_get_packet(GF_FilterPid * pid)4815 GF_FilterPacket *gf_filter_pid_get_packet(GF_FilterPid *pid)
4816 {
4817 GF_FilterPacketInstance *pcki;
4818 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
4819 if (PID_IS_OUTPUT(pid)) {
4820 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to fetch a packet on an output PID in filter %s\n", pid->filter->name));
4821 return NULL;
4822 }
4823 if (pidinst->discard_packets || pidinst->detach_pending) {
4824 pidinst->filter->nb_pck_io++;
4825 return NULL;
4826 }
4827
4828 pcki = (GF_FilterPacketInstance *)gf_fq_head(pidinst->packets);
4829 //no packets
4830 if (!pcki) {
4831 if (pidinst->pid->filter->disabled) {
4832 pidinst->is_end_of_stream = pidinst->pid->has_seen_eos = GF_TRUE;
4833 }
4834 if (!pidinst->is_end_of_stream && pidinst->pid->filter->would_block)
4835 gf_filter_pid_check_unblock(pidinst->pid);
4836 pidinst->filter->nb_pck_io++;
4837 return NULL;
4838 }
4839 assert(pcki->pck);
4840
4841 if (gf_filter_pid_filter_internal_packet(pidinst, pcki)) {
4842 return gf_filter_pid_get_packet(pid);
4843 }
4844 pcki->pid->is_end_of_stream = GF_FALSE;
4845
4846 if ( (pcki->pck->info.flags & GF_PCKF_PROPS_CHANGED) && !pcki->pid_props_change_done) {
4847 GF_Err e;
4848 Bool skip_props = GF_FALSE;
4849
4850 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s PID %s property changed at this packet, triggering reconfigure\n", pidinst->pid->filter->name, pidinst->pid->name));
4851 pcki->pid_props_change_done = 1;
4852
4853 //it may happen that:
4854 //- the props are not set when querying the first packet (no prop queries on pid)
4855 //- the new props are already set if filter_pid_get_property was queried before the first packet dispatch
4856 if (pidinst->props) {
4857 if (pidinst->force_reconfig || (pidinst->props != pcki->pck->pid_props)) {
4858 //destroy if last occurence, removing it from pid as well
4859 //only remove if last about to be destroyed, since we may have several pid instances consuming from this pid
4860 assert(pidinst->props->reference_count);
4861 if (safe_int_dec(& pidinst->props->reference_count) == 0) {
4862 //see \ref gf_filter_pid_merge_properties_internal for mutex
4863 gf_mx_p(pidinst->pid->filter->tasks_mx);
4864 gf_list_del_item(pidinst->pid->properties, pidinst->props);
4865 gf_mx_v(pidinst->pid->filter->tasks_mx);
4866 gf_props_del(pidinst->props);
4867 }
4868 pidinst->force_reconfig = GF_FALSE;
4869 //set new one
4870 pidinst->props = pcki->pck->pid_props;
4871 safe_int_inc( & pidinst->props->reference_count );
4872 } else {
4873 //it may happen that pid_configure for destination was called after packet being dispatched, in
4874 //which case we are already properly configured
4875 skip_props = GF_TRUE;
4876 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s PID %s was already configured with the last property set, ignoring reconfigure\n", pidinst->pid->filter->name, pidinst->pid->name));
4877 }
4878 }
4879 if (!skip_props) {
4880 assert(pidinst->filter->freg->configure_pid);
4881 //reset the blacklist whenever reconfiguring, since we may need to reload a new filter chain
4882 //in which a previously blacklisted filter (failing (re)configure for previous state) could
4883 //now work, eg moving from formatA to formatB then back to formatA
4884 gf_list_reset(pidinst->filter->blacklisted);
4885
4886 e = gf_filter_pid_configure(pidinst->filter, pidinst->pid, GF_PID_CONF_RECONFIG);
4887 if (e != GF_OK) return NULL;
4888 if (pidinst->pid->caps_negociate)
4889 return NULL;
4890 }
4891 }
4892 if ( (pcki->pck->info.flags & GF_PCKF_INFO_CHANGED) /* && !pcki->pid_info_change_done*/) {
4893 Bool res=GF_FALSE;
4894
4895 //it may happen that this filter pid is pulled from another thread than ours (eg audio callback), in which case
4896 //we cannot go reentrant, we have to wait until the filter is not in use ...
4897 if (pidinst->filter->freg->process_event && pidinst->filter->process_th_id && (pidinst->filter->process_th_id != gf_th_id()) ) {
4898 return NULL;
4899 }
4900 pcki->pid_info_change_done = 1;
4901
4902 if (pidinst->filter->freg->process_event) {
4903 GF_FilterEvent evt;
4904 GF_FEVT_INIT(evt, GF_FEVT_INFO_UPDATE, pid);
4905
4906 //the following may fail when some filters use threading on their own
4907 //FSESS_CHECK_THREAD(pidinst->filter)
4908 res = pidinst->filter->freg->process_event(pidinst->filter, &evt);
4909 }
4910
4911 if (!res) {
4912 pidinst->filter->pid_info_changed = GF_TRUE;
4913 }
4914 }
4915 pidinst->last_pck_fetch_time = gf_sys_clock_high_res();
4916
4917 return (GF_FilterPacket *)pcki;
4918 }
4919
4920 GF_EXPORT
gf_filter_pid_get_first_packet_cts(GF_FilterPid * pid,u64 * cts)4921 Bool gf_filter_pid_get_first_packet_cts(GF_FilterPid *pid, u64 *cts)
4922 {
4923 GF_FilterPacketInstance *pcki;
4924 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
4925 if (PID_IS_OUTPUT(pid)) {
4926 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to read packet CTS on an output PID in filter %s\n", pid->filter->name));
4927 return GF_FALSE;
4928 }
4929 if (pidinst->discard_packets) return GF_FALSE;
4930
4931 pcki = (GF_FilterPacketInstance *)gf_fq_head(pidinst->packets);
4932 //no packets
4933 if (!pcki) {
4934 return GF_FALSE;
4935 }
4936 assert(pcki->pck);
4937
4938 if (gf_filter_pid_filter_internal_packet(pidinst, pcki)) {
4939 return gf_filter_pid_get_first_packet_cts(pid, cts);
4940 }
4941
4942 if (pidinst->requires_full_data_block && !(pcki->pck->info.flags & GF_PCKF_BLOCK_END))
4943 return GF_FALSE;
4944 *cts = pcki->pck->info.cts;
4945 return GF_TRUE;
4946 }
4947
4948 GF_EXPORT
gf_filter_pid_first_packet_is_empty(GF_FilterPid * pid)4949 Bool gf_filter_pid_first_packet_is_empty(GF_FilterPid *pid)
4950 {
4951 GF_FilterPacketInstance *pcki;
4952 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
4953 if (PID_IS_OUTPUT(pid)) {
4954 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to read packet CTS on an output PID in filter %s\n", pid->filter->name));
4955 return GF_TRUE;
4956 }
4957 if (pidinst->discard_packets) return GF_TRUE;
4958
4959 pcki = (GF_FilterPacketInstance *)gf_fq_head(pidinst->packets);
4960 //no packets
4961 if (!pcki) {
4962 return GF_TRUE;
4963 }
4964 assert(pcki->pck);
4965
4966 if (pcki->pck->info.flags & (GF_PCK_CMD_MASK|GF_PCK_CKTYPE_MASK)) {
4967 return GF_TRUE;
4968 }
4969 if (pidinst->requires_full_data_block && !(pcki->pck->info.flags & GF_PCKF_BLOCK_END))
4970 return GF_TRUE;
4971 return (pcki->pck->data_length || pcki->pck->frame_ifce) ? GF_FALSE : GF_TRUE;
4972 }
4973
4974
gf_filter_pidinst_update_stats(GF_FilterPidInst * pidi,GF_FilterPacket * pck)4975 static void gf_filter_pidinst_update_stats(GF_FilterPidInst *pidi, GF_FilterPacket *pck)
4976 {
4977 u64 now = gf_sys_clock_high_res();
4978 u64 dec_time = now - pidi->last_pck_fetch_time;
4979 if (pck->info.flags & GF_PCK_CMD_MASK) return;
4980 if (!pidi->filter || pidi->pid->filter->removed) return;
4981
4982 pidi->filter->nb_pck_processed++;
4983 pidi->filter->nb_bytes_processed += pck->data_length;
4984
4985 pidi->total_process_time += dec_time;
4986 if (!pidi->nb_processed) {
4987 pidi->first_frame_time = pidi->last_pck_fetch_time;
4988 }
4989
4990 pidi->nb_processed++;
4991 if (pck->info.flags & GF_PCK_SAP_MASK) {
4992 pidi->nb_sap_processed ++;
4993 if (dec_time > pidi->max_sap_process_time) pidi->max_sap_process_time = dec_time;
4994 pidi->total_sap_process_time += dec_time;
4995 }
4996
4997 if (dec_time > pidi->max_process_time) pidi->max_process_time = dec_time;
4998
4999 if (pck->data_length) {
5000 Bool has_ts = GF_TRUE;
5001 u64 ts = (pck->info.dts != GF_FILTER_NO_TS) ? pck->info.dts : pck->info.cts;
5002 if ((ts != GF_FILTER_NO_TS) && (pck->pid_props->timescale)) {
5003 ts *= 1000000;
5004 ts /= pck->pid_props->timescale;
5005 } else {
5006 has_ts = GF_FALSE;
5007 }
5008
5009 if (!pidi->cur_bit_size) {
5010 pidi->stats_start_ts = ts;
5011 pidi->stats_start_us = now;
5012 pidi->cur_bit_size = 8*pck->data_length;
5013 } else {
5014 Bool flush_stats = GF_FALSE;
5015 pidi->cur_bit_size += 8*pck->data_length;
5016
5017 if (has_ts) {
5018 if (pidi->stats_start_ts + 1000000 <= ts) flush_stats = GF_TRUE;
5019 } else {
5020 if (pidi->stats_start_us + 1000000 <= now) flush_stats = GF_TRUE;
5021 }
5022
5023 if (flush_stats) {
5024 u64 rate;
5025 u64 diff_t;
5026
5027 if (has_ts) {
5028 rate = pidi->cur_bit_size;
5029 rate *= 1000000;
5030 diff_t = ts - pidi->stats_start_ts;
5031 if (!diff_t) diff_t = 1;
5032 rate /= diff_t;
5033 pidi->avg_bit_rate = (u32) rate;
5034 if (pidi->avg_bit_rate > pidi->max_bit_rate) pidi->max_bit_rate = pidi->avg_bit_rate;
5035 }
5036
5037 rate = pidi->cur_bit_size;
5038 rate *= 1000000;
5039 diff_t = now - pidi->stats_start_us;
5040 if (!diff_t) diff_t = 1;
5041 rate /= diff_t;
5042 pidi->avg_process_rate = (u32) rate;
5043 if (pidi->avg_process_rate > pidi->max_process_rate) pidi->max_process_rate = pidi->avg_process_rate;
5044
5045 //reset stats
5046 pidi->cur_bit_size = 0;
5047 }
5048 }
5049 }
5050 }
5051
gf_filter_pidinst_reset_stats(GF_FilterPidInst * pidi)5052 static void gf_filter_pidinst_reset_stats(GF_FilterPidInst *pidi)
5053 {
5054 pidi->last_pck_fetch_time = 0;
5055 pidi->stats_start_ts = 0;
5056 pidi->stats_start_us = 0;
5057 pidi->cur_bit_size = 0;
5058 pidi->avg_bit_rate = 0;
5059 pidi->max_bit_rate = 0;
5060 pidi->avg_process_rate = 0;
5061 pidi->max_process_rate = 0;
5062 pidi->nb_processed = 0;
5063 pidi->nb_sap_processed = 0;
5064 pidi->total_process_time = 0;
5065 pidi->total_sap_process_time = 0;
5066 pidi->max_process_time = 0;
5067 pidi->max_sap_process_time = 0;
5068 pidi->first_frame_time = 0;
5069 }
5070
5071 GF_EXPORT
gf_filter_pid_drop_packet(GF_FilterPid * pid)5072 void gf_filter_pid_drop_packet(GF_FilterPid *pid)
5073 {
5074 #ifdef GPAC_MEMORY_TRACKING
5075 u32 prev_nb_allocs, prev_nb_reallocs, nb_allocs, nb_reallocs;
5076 #endif
5077
5078 u32 nb_pck=0;
5079 GF_FilterPacket *pck=NULL;
5080 GF_FilterPacketInstance *pcki;
5081 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
5082
5083 if (PID_IS_OUTPUT(pid)) {
5084 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to discard a packet on an output PID in filter %s\n", pid->filter->name));
5085 return;
5086 }
5087 if (pidinst->filter)
5088 pidinst->filter->nb_pck_io++;
5089
5090 //remove pck instance
5091 pcki = gf_fq_pop(pidinst->packets);
5092
5093 if (!pcki) {
5094 if (pidinst->filter && !pidinst->filter->finalized) {
5095 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Attempt to discard a packet already discarded in filter %s\n", pid->filter->name));
5096 }
5097 return;
5098 }
5099
5100 gf_rmt_begin(pck_drop, GF_RMT_AGGREGATE);
5101 pck = pcki->pck;
5102 //move to source pid
5103 pid = pid->pid;
5104
5105 gf_filter_pidinst_update_stats(pidinst, pck);
5106
5107 //make sure we lock the tasks mutex before getting the packet count, otherwise we might end up with a wrong number of packets
5108 //if one thread (the caller here) consumes one packet while the dispatching thread is still upddating the state for that pid
5109 gf_mx_p(pid->filter->tasks_mx);
5110 nb_pck = gf_fq_count(pidinst->packets);
5111
5112 if (!nb_pck) {
5113 safe_int64_sub(&pidinst->buffer_duration, pidinst->buffer_duration);
5114 } else if (pck->info.duration && (pck->info.flags & GF_PCKF_BLOCK_START) && pck->pid_props->timescale) {
5115 s64 d = ((u64)pck->info.duration) * 1000000;
5116 d /= pck->pid_props->timescale;
5117 if (d > pidinst->buffer_duration) {
5118 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Corrupted buffer level in PID instance %s (%s -> %s), droping packet duration "LLD" us greater than buffer duration "LLU" us\n", pid->name, pid->filter->name, pidinst->filter ? pidinst->filter->name : "disconnected", d, pidinst->buffer_duration));
5119 d = pidinst->buffer_duration;
5120 }
5121 assert(d <= pidinst->buffer_duration);
5122 safe_int64_sub(&pidinst->buffer_duration, (s32) d);
5123 }
5124
5125 if ( (pid->num_destinations==1) || (pid->filter->session->blocking_mode==GF_FS_NOBLOCK_FANOUT)) {
5126 if (nb_pck<pid->nb_buffer_unit) {
5127 pid->nb_buffer_unit = nb_pck;
5128 }
5129 if (!pid->buffer_duration || (pidinst->buffer_duration < (s64) pid->buffer_duration)) {
5130 pid->buffer_duration = pidinst->buffer_duration;
5131 }
5132 }
5133 //handle fan-out: we must browse all other pid instances and compute max buffer/nb_pck per pids
5134 //so that we don't unblock the PID if some instance is still blocking
5135 else {
5136 u32 i;
5137 u32 min_pck = nb_pck;
5138 s64 min_dur = pidinst->buffer_duration;
5139 for (i=0; i<pid->num_destinations; i++) {
5140 GF_FilterPidInst *a_pidi = gf_list_get(pid->destinations, i);
5141 if (a_pidi==pidinst) continue;
5142 if (a_pidi->buffer_duration > min_dur)
5143 min_dur = a_pidi->buffer_duration;
5144 nb_pck = gf_fq_count(a_pidi->packets);
5145 if (nb_pck>min_pck)
5146 min_pck = nb_pck;
5147 }
5148 pid->buffer_duration = min_dur;
5149 pid->nb_buffer_unit = min_pck;
5150 }
5151 gf_filter_pid_check_unblock(pid);
5152
5153 gf_mx_v(pid->filter->tasks_mx);
5154
5155 #ifndef GPAC_DISABLE_LOG
5156 if (gf_log_tool_level_on(GF_LOG_FILTER, GF_LOG_DEBUG)) {
5157 u8 sap_type = (pck->info.flags & GF_PCK_SAP_MASK) >> GF_PCK_SAP_POS;
5158 Bool seek_flag = pck->info.flags & GF_PCKF_SEEK;
5159
5160 if ((pck->info.dts != GF_FILTER_NO_TS) && (pck->info.cts != GF_FILTER_NO_TS) ) {
5161 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s (%s) drop packet DTS "LLU" CTS "LLU" SAP %d Seek %d - %d packets remaining buffer "LLU" us\n", pidinst->filter ? pidinst->filter->name : "disconnected", pid->name, pid->filter->name, pck->info.dts, pck->info.cts, sap_type, seek_flag, nb_pck, pidinst->buffer_duration));
5162 } else if ((pck->info.cts != GF_FILTER_NO_TS) ) {
5163 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s (%s) drop packet CTS "LLU" SAP %d Seek %d - %d packets remaining buffer "LLU" us\n", pidinst->filter ? pidinst->filter->name : "disconnected", pid->name, pid->filter->name, pck->info.cts, sap_type, seek_flag, nb_pck, pidinst->buffer_duration));
5164 } else if ((pck->info.dts != GF_FILTER_NO_TS) ) {
5165 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s (%s) drop packet DTS "LLU" SAP %d Seek %d - %d packets remaining buffer "LLU" us\n", pidinst->filter ? pidinst->filter->name : "disconnected", pid->name, pid->filter->name, pck->info.dts, sap_type, seek_flag, nb_pck, pidinst->buffer_duration));
5166 } else {
5167 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s (%s) drop packet SAP %d Seek %d - %d packets remaining buffer "LLU" us\n", pidinst->filter ? pidinst->filter->name : "disconnected", pid->name, pid->filter->name, sap_type, seek_flag, nb_pck, pidinst->buffer_duration));
5168 }
5169 }
5170 #endif
5171
5172 //destroy pcki
5173 pcki->pck = NULL;
5174 pcki->pid = NULL;
5175
5176 #ifdef GPAC_MEMORY_TRACKING
5177 if (pid->filter && pid->filter->session->check_allocs) {
5178 gf_mem_get_stats(&prev_nb_allocs, NULL, &prev_nb_reallocs, NULL);
5179 }
5180 #endif
5181
5182 if (pid->filter->pcks_inst_reservoir) {
5183 gf_fq_add(pid->filter->pcks_inst_reservoir, pcki);
5184 } else {
5185 gf_free(pcki);
5186 }
5187 //unref pck
5188 assert(pck->reference_count);
5189 if (safe_int_dec(&pck->reference_count) == 0) {
5190 gf_filter_packet_destroy(pck);
5191 }
5192
5193 #ifdef GPAC_MEMORY_TRACKING
5194 if (pid->filter && pid->filter->session->check_allocs) {
5195 gf_mem_get_stats(&nb_allocs, NULL, &nb_reallocs, NULL);
5196
5197 pid->filter->session->nb_alloc_pck += (nb_allocs - prev_nb_allocs);
5198 pid->filter->session->nb_realloc_pck += (nb_reallocs - prev_nb_reallocs);
5199 }
5200 #endif
5201
5202 //decrement number of pending packet on target filter if this is not a destroy
5203 if (pidinst->filter) {
5204 assert(pidinst->filter->pending_packets);
5205 safe_int_dec(&pidinst->filter->pending_packets);
5206
5207 gf_filter_forward_clock(pidinst->filter);
5208 }
5209
5210 gf_rmt_end();
5211 }
5212
5213 GF_EXPORT
gf_filter_pid_is_eos(GF_FilterPid * pid)5214 Bool gf_filter_pid_is_eos(GF_FilterPid *pid)
5215 {
5216 GF_FilterPacketInstance *pcki;
5217 GF_FilterPidInst *pidi = (GF_FilterPidInst *)pid;
5218
5219 if (pidi->detach_pending)
5220 return GF_FALSE;
5221
5222 if (PID_IS_OUTPUT(pid)) {
5223 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to query EOS on output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
5224 return GF_FALSE;
5225 }
5226 if (!pid->pid->has_seen_eos && !pidi->discard_inputs && !pidi->discard_packets) {
5227 ((GF_FilterPidInst *)pid)->is_end_of_stream = GF_FALSE;
5228 return GF_FALSE;
5229 }
5230 //peek next for eos
5231 pcki = (GF_FilterPacketInstance *)gf_fq_head(pidi->packets);
5232 if (pcki)
5233 gf_filter_pid_filter_internal_packet(pidi, pcki);
5234
5235 if (pidi->discard_packets) return GF_FALSE;
5236 if (!pidi->is_end_of_stream) return GF_FALSE;
5237 if (!pidi->filter->eos_probe_state)
5238 pidi->filter->eos_probe_state = 1;
5239 return GF_TRUE;
5240 }
5241
5242 GF_EXPORT
gf_filter_pid_set_eos(GF_FilterPid * pid)5243 void gf_filter_pid_set_eos(GF_FilterPid *pid)
5244 {
5245 GF_FilterPacket *pck;
5246
5247 if (PID_IS_INPUT(pid)) {
5248 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to signal EOS on input PID %s in filter %s\n", pid->pid->name, pid->filter->name));
5249 return;
5250 }
5251 if (pid->has_seen_eos) return;
5252
5253 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("EOS signaled on PID %s in filter %s\n", pid->name, pid->filter->name));
5254 //we create a fake packet for eos signaling
5255 pck = gf_filter_pck_new_shared_internal(pid, NULL, 0, NULL, GF_TRUE);
5256 gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
5257 pck->pck->info.flags |= GF_PCK_CMD_PID_EOS;
5258 gf_filter_pck_send(pck);
5259 }
5260
5261 GF_EXPORT
gf_filter_pid_enum_properties(GF_FilterPid * pid,u32 * idx,u32 * prop_4cc,const char ** prop_name)5262 const GF_PropertyValue *gf_filter_pid_enum_properties(GF_FilterPid *pid, u32 *idx, u32 *prop_4cc, const char **prop_name)
5263 {
5264 GF_PropertyMap *props;
5265
5266 if (PID_IS_INPUT(pid)) {
5267 gf_mx_p(pid->filter->tasks_mx);
5268 props = gf_list_last(pid->pid->properties);
5269 gf_mx_v(pid->filter->tasks_mx);
5270 } else {
5271 props = check_new_pid_props(pid, GF_FALSE);
5272 }
5273 if (!props) {
5274 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("No properties for pid in filter %s, ignoring enum\n", pid->filter->name));
5275 *idx = 0xFFFFFFFF;
5276 return NULL;
5277 }
5278 return gf_props_enum_property(props, idx, prop_4cc, prop_name);
5279 }
5280
5281 GF_EXPORT
gf_filter_pid_would_block(GF_FilterPid * pid)5282 Bool gf_filter_pid_would_block(GF_FilterPid *pid)
5283 {
5284 Bool would_block=GF_FALSE;
5285 Bool blockmode_broken=GF_FALSE;
5286
5287 if (PID_IS_INPUT(pid)) {
5288 return GF_FALSE;
5289 }
5290
5291 if (pid->filter->session->blocking_mode==GF_FS_NOBLOCK)
5292 return GF_FALSE;
5293
5294 gf_mx_p(pid->filter->tasks_mx);
5295 //either block according to the number of dispatched units (decoder output) or to the requested buffer duration
5296 if (pid->max_buffer_unit) {
5297 if (pid->nb_buffer_unit * GF_FILTER_SPEED_SCALER >= pid->max_buffer_unit * pid->playback_speed_scaler) {
5298 would_block = GF_TRUE;
5299 }
5300 if ((pid->num_destinations==1) && !pid->filter->blockmode_broken && ( (pid->nb_buffer_unit * GF_FILTER_SPEED_SCALER > 100 * pid->max_buffer_unit * pid->playback_speed_scaler) ) ) {
5301 blockmode_broken = GF_TRUE;
5302 }
5303 } else if (pid->max_buffer_time) {
5304 if (pid->buffer_duration * GF_FILTER_SPEED_SCALER > pid->max_buffer_time * pid->playback_speed_scaler) {
5305 would_block = GF_TRUE;
5306 }
5307 if ((pid->num_destinations==1) && !pid->filter->blockmode_broken && (pid->buffer_duration * GF_FILTER_SPEED_SCALER > 100 * pid->max_buffer_time * pid->playback_speed_scaler) ) {
5308 blockmode_broken = GF_TRUE;
5309 }
5310 }
5311 if (blockmode_broken) {
5312 //don't throw a warning since some filters may dispatch a large burst of packets (eg isom muxer)
5313 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s block mode not respected: %u units "LLU" us vs %u max units "LLU" max buffer\n", pid->pid->filter->name, pid->pid->name, pid->nb_buffer_unit, pid->buffer_duration, pid->max_buffer_unit, pid->max_buffer_time));
5314
5315 pid->filter->blockmode_broken = GF_TRUE;
5316 }
5317
5318 if (would_block && !pid->would_block) {
5319 safe_int_inc(&pid->would_block);
5320 safe_int_inc(&pid->filter->would_block);
5321 assert(pid->filter->would_block + pid->filter->num_out_pids_not_connected <= pid->filter->num_output_pids);
5322
5323 #ifndef GPAC_DISABLE_LOG
5324 if (gf_log_tool_level_on(GF_LOG_FILTER, GF_LOG_DEBUG)) {
5325 if (pid->max_buffer_unit) {
5326 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s blocked (%d units vs %d max units) - %d filter PIDs blocked\n", pid->pid->filter->name, pid->pid->name, pid->nb_buffer_unit, pid->max_buffer_unit, pid->filter->would_block));
5327 } else {
5328 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter %s PID %s blocked ("LLU" us vs "LLU" max buffer) - %d filter PIDs blocked\n", pid->pid->filter->name, pid->pid->name, pid->buffer_duration, pid->max_buffer_time, pid->filter->would_block));
5329 }
5330 }
5331 #endif
5332 }
5333 assert(pid->filter->would_block <= pid->filter->num_output_pids);
5334 gf_mx_v(pid->filter->tasks_mx);
5335 return would_block;
5336 }
5337
5338 GF_EXPORT
gf_filter_pid_query_buffer_duration(GF_FilterPid * pid,Bool check_pid_full)5339 u64 gf_filter_pid_query_buffer_duration(GF_FilterPid *pid, Bool check_pid_full)
5340 {
5341 u32 count, i, j;
5342 u64 duration=0;
5343 if (!pid || pid->filter->session->in_final_flush)
5344 return GF_FILTER_NO_TS;
5345
5346 if (PID_IS_INPUT(pid)) {
5347 GF_Filter *filter;
5348 GF_FilterPidInst *pidinst = (GF_FilterPidInst *)pid;
5349 if (!pidinst->pid) return 0;
5350 filter = pidinst->pid->filter;
5351 if (check_pid_full) {
5352 Bool buffer_full = GF_FALSE;
5353
5354 if (pidinst->pid->max_buffer_unit && (pidinst->pid->max_buffer_unit<=pidinst->pid->nb_buffer_unit))
5355 buffer_full = GF_TRUE;
5356
5357 if (pidinst->pid->max_buffer_time && (pidinst->pid->max_buffer_time<=pidinst->pid->buffer_duration))
5358 buffer_full = GF_TRUE;
5359
5360 if (!buffer_full)
5361 return 0;
5362 }
5363 count = filter->num_input_pids;
5364 for (i=0; i<count; i++) {
5365 u64 dur = gf_filter_pid_query_buffer_duration( gf_list_get(filter->input_pids, i), 0);
5366 if (dur > duration)
5367 duration = dur;
5368 }
5369 duration += pidinst->buffer_duration;
5370 return duration;
5371 } else {
5372 u32 count2;
5373 u64 max_dur=0;
5374
5375 if (check_pid_full) {
5376 if (pid->max_buffer_unit && (pid->max_buffer_unit>pid->nb_buffer_unit))
5377 return 0;
5378 if (pid->max_buffer_time && (pid->max_buffer_time>pid->buffer_duration))
5379 return 0;
5380 }
5381
5382 count = pid->num_destinations;
5383 for (i=0; i<count; i++) {
5384 GF_FilterPidInst *pidinst = gf_list_get(pid->destinations, i);
5385
5386 count2 = pidinst->filter->num_output_pids;
5387 for (j=0; j<count2; j++) {
5388 GF_FilterPid *pid_n = gf_list_get(pidinst->filter->output_pids, i);
5389 u64 dur = gf_filter_pid_query_buffer_duration(pid_n, 0);
5390 if (dur > max_dur ) max_dur = dur;
5391 }
5392 }
5393 duration += max_dur;
5394 }
5395 return duration;
5396 }
5397
5398
5399 GF_EXPORT
gf_filter_pid_has_seen_eos(GF_FilterPid * pid)5400 Bool gf_filter_pid_has_seen_eos(GF_FilterPid *pid)
5401 {
5402 u32 i;
5403 if (PID_IS_OUTPUT(pid)) {
5404 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to query EOS on output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
5405 return GF_FALSE;
5406 }
5407 if (pid->pid->has_seen_eos) return GF_TRUE;
5408 if (pid->pid->filter->block_eos) return GF_FALSE;
5409 for (i=0; i<pid->pid->filter->num_input_pids; i++) {
5410 GF_FilterPidInst *pidi = gf_list_get(pid->pid->filter->input_pids, i);
5411 if (gf_filter_pid_has_seen_eos((GF_FilterPid *) pidi)) return GF_TRUE;
5412 }
5413 return GF_FALSE;
5414 }
5415
5416 GF_EXPORT
gf_filter_event_name(GF_FEventType type)5417 const char *gf_filter_event_name(GF_FEventType type)
5418 {
5419 switch (type) {
5420 case GF_FEVT_PLAY: return "PLAY";
5421 case GF_FEVT_SET_SPEED: return "SET_SPEED";
5422 case GF_FEVT_STOP: return "STOP";
5423 case GF_FEVT_SOURCE_SEEK: return "SOURCE_SEEK";
5424 case GF_FEVT_SOURCE_SWITCH: return "SOURCE_SWITCH";
5425 case GF_FEVT_ATTACH_SCENE: return "ATTACH_SCENE";
5426 case GF_FEVT_RESET_SCENE: return "RESET_SCENE";
5427 case GF_FEVT_PAUSE: return "PAUSE";
5428 case GF_FEVT_RESUME: return "RESUME";
5429 case GF_FEVT_QUALITY_SWITCH: return "QUALITY_SWITCH";
5430 case GF_FEVT_VISIBILITY_HINT: return "VISIBILITY_HINT";
5431 case GF_FEVT_INFO_UPDATE: return "INFO_UPDATE";
5432 case GF_FEVT_BUFFER_REQ: return "BUFFER_REQ";
5433 case GF_FEVT_USER: return "USER";
5434 case GF_FEVT_SEGMENT_SIZE: return "SEGMENT_SIZE";
5435 case GF_FEVT_CAPS_CHANGE: return "CAPS_CHANGED";
5436 case GF_FEVT_CONNECT_FAIL: return "CONNECT_FAIL";
5437 case GF_FEVT_PLAY_HINT: return "PLAY_HINT";
5438 default:
5439 return "UNKNOWN";
5440 }
5441 }
5442
gf_filter_pid_reset_task_ex(GF_FSTask * task,Bool * had_eos)5443 static void gf_filter_pid_reset_task_ex(GF_FSTask *task, Bool *had_eos)
5444 {
5445 GF_FilterPidInst *pidi = (GF_FilterPidInst *)task->udta;
5446 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s input PID %s (from %s) reseting buffer\n", task->filter->name, pidi->pid->name, pidi->pid->filter->name ));
5447 assert(pidi->pid->discard_input_packets);
5448
5449 if (had_eos) *had_eos = GF_FALSE;
5450
5451 while (gf_fq_count(pidi->packets)) {
5452 GF_FilterPacketInstance *pcki = gf_fq_head(pidi->packets);
5453 if ( (pcki->pck->info.flags & GF_PCK_CMD_MASK) == GF_PCK_CMD_PID_EOS) {
5454 if (had_eos)
5455 *had_eos = GF_TRUE;
5456 }
5457 gf_filter_pid_drop_packet((GF_FilterPid *) pidi);
5458 }
5459 while (gf_list_count(pidi->pck_reassembly)) {
5460 GF_FilterPacketInstance *pcki = gf_list_pop_back(pidi->pck_reassembly);
5461 pcki_del(pcki);
5462 }
5463 gf_filter_pidinst_reset_stats(pidi);
5464
5465 pidi->discard_packets = GF_FALSE;
5466 pidi->last_block_ended = GF_TRUE;
5467 pidi->first_block_started = GF_FALSE;
5468 pidi->is_end_of_stream = GF_FALSE;
5469 pidi->buffer_duration = 0;
5470 pidi->nb_eos_signaled = 0;
5471 pidi->pid->has_seen_eos = GF_FALSE;
5472
5473 assert(pidi->pid->filter->stream_reset_pending);
5474 safe_int_dec(& pidi->pid->filter->stream_reset_pending );
5475
5476 pidi->pid->nb_buffer_unit = 0;
5477 pidi->pid->buffer_duration = 0;
5478 gf_filter_pid_check_unblock(pidi->pid);
5479
5480 safe_int_dec(& pidi->pid->discard_input_packets );
5481 }
gf_filter_pid_reset_task(GF_FSTask * task)5482 static void gf_filter_pid_reset_task(GF_FSTask *task)
5483 {
5484 gf_filter_pid_reset_task_ex(task, NULL);
5485 }
5486
gf_filter_pid_reset_stop_task(GF_FSTask * task)5487 static void gf_filter_pid_reset_stop_task(GF_FSTask *task)
5488 {
5489 GF_FilterPidInst *pidi = (GF_FilterPidInst *)task->udta;
5490 Bool has_eos;
5491 gf_filter_pid_reset_task_ex(task, &has_eos);
5492 pidi->is_end_of_stream = has_eos;
5493 pidi->pid->has_seen_eos = has_eos;
5494 }
5495
5496 typedef struct
5497 {
5498 u32 ref_count;
5499 char string[1];
5500 } GF_RefString;
5501
5502 #define TO_REFSTRING(_v) (GF_RefString *) (_v - offsetof(GF_RefString, string))
5503
evt_get_refstr(GF_FilterEvent * evt)5504 static GF_RefString *evt_get_refstr(GF_FilterEvent *evt)
5505 {
5506 if (evt->base.type == GF_FEVT_FILE_DELETE) {
5507 return TO_REFSTRING(evt->file_del.url);
5508 }
5509 if (evt->base.type == GF_FEVT_SOURCE_SWITCH) {
5510 return TO_REFSTRING(evt->seek.source_switch);
5511 }
5512 if (evt->base.type == GF_FEVT_SEGMENT_SIZE) {
5513 return TO_REFSTRING(evt->seg_size.seg_url);
5514 }
5515 return NULL;
5516 }
dup_evt(GF_FilterEvent * evt)5517 static GF_FilterEvent *dup_evt(GF_FilterEvent *evt)
5518 {
5519 GF_FilterEvent *an_evt;
5520 GF_RefString *rstr = evt_get_refstr(evt);
5521 an_evt = gf_malloc(sizeof(GF_FilterEvent));
5522 memcpy(an_evt, evt, sizeof(GF_FilterEvent));
5523 if (rstr) {
5524 safe_int_inc(&rstr->ref_count);
5525 }
5526 return an_evt;
5527 }
5528
free_evt(GF_FilterEvent * evt)5529 static void free_evt(GF_FilterEvent *evt)
5530 {
5531 GF_RefString *rstr = evt_get_refstr(evt);
5532 if (rstr) {
5533 assert(rstr->ref_count);
5534 if (safe_int_dec(&rstr->ref_count) == 0) {
5535 gf_free(rstr);
5536 }
5537 }
5538 gf_free(evt);
5539 }
5540
init_evt(GF_FilterEvent * evt)5541 static GF_FilterEvent *init_evt(GF_FilterEvent *evt)
5542 {
5543 char **url_addr_src = NULL;
5544 char **url_addr_dst = NULL;
5545 GF_FilterEvent *an_evt = gf_malloc(sizeof(GF_FilterEvent));
5546 memcpy(an_evt, evt, sizeof(GF_FilterEvent));
5547
5548 if (evt->base.type==GF_FEVT_FILE_DELETE) {
5549 url_addr_src = (char **) &evt->file_del.url;
5550 url_addr_dst = (char **) &an_evt->file_del.url;
5551 } else if (evt->base.type==GF_FEVT_SOURCE_SWITCH) {
5552 url_addr_src = (char **) &evt->seek.source_switch;
5553 url_addr_dst = (char **) &an_evt->seek.source_switch;
5554 } else if (evt->base.type==GF_FEVT_SEGMENT_SIZE) {
5555 url_addr_src = (char **) &evt->seg_size.seg_url;
5556 url_addr_dst = (char **) &an_evt->seg_size.seg_url;
5557 }
5558 if (url_addr_src) {
5559 char *url = *url_addr_src;
5560 u32 len = url ? (u32) strlen(url) : 0;
5561 GF_RefString *rstr = gf_malloc(sizeof(GF_RefString) + sizeof(char)*len);
5562 rstr->ref_count=1;
5563 strcpy( (char *) &rstr->string[0], url ? url : "");
5564 *url_addr_dst = (char *) &rstr->string[0];
5565 }
5566 return an_evt;
5567 }
5568
gf_filter_pid_send_event_downstream(GF_FSTask * task)5569 void gf_filter_pid_send_event_downstream(GF_FSTask *task)
5570 {
5571 u32 i, count;
5572 Bool canceled = GF_FALSE;
5573 GF_FilterEvent *evt = task->udta;
5574 GF_Filter *f = task->filter;
5575 GF_List *dispatched_filters = NULL;
5576 Bool evt_reused=GF_FALSE;
5577
5578 //if stream reset task is posted, wait for it before processing this event
5579 if (f->stream_reset_pending) {
5580 TASK_REQUEUE(task)
5581 return;
5582 }
5583 //if some pids are still detached, wait for the connection before processing this event
5584 if (f->detached_pid_inst) {
5585 TASK_REQUEUE(task)
5586 task->can_swap = GF_TRUE;
5587 return;
5588 }
5589
5590 if (evt->base.on_pid) {
5591 assert(evt->base.on_pid->filter->num_events_queued);
5592 safe_int_dec(&evt->base.on_pid->filter->num_events_queued);
5593 }
5594
5595 if (evt->base.type == GF_FEVT_BUFFER_REQ) {
5596 if (!evt->base.on_pid) {
5597 free_evt(evt);
5598 return;
5599 }
5600 if (evt->base.on_pid->nb_decoder_inputs || evt->base.on_pid->raw_media || evt->buffer_req.pid_only) {
5601 evt->base.on_pid->max_buffer_time = evt->base.on_pid->user_max_buffer_time = evt->buffer_req.max_buffer_us;
5602 evt->base.on_pid->user_max_playout_time = evt->buffer_req.max_playout_us;
5603 evt->base.on_pid->user_min_playout_time = evt->buffer_req.min_playout_us;
5604 evt->base.on_pid->max_buffer_unit = 0;
5605 //update blocking state
5606 if (evt->base.on_pid->would_block)
5607 gf_filter_pid_check_unblock(evt->base.on_pid);
5608 else
5609 gf_filter_pid_would_block(evt->base.on_pid);
5610 canceled = GF_TRUE;
5611 }
5612 } else if (evt->base.on_pid && (evt->base.type == GF_FEVT_PLAY)
5613 && (evt->base.on_pid->pid->is_playing || (((GF_FilterPid *) evt->base.on_pid->pid)->not_connected==2))
5614 ) {
5615 if (evt->base.on_pid->pid->is_playing) {
5616 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s PID %s event %s but PID is already playing, discarding\n", f->name, evt->base.on_pid->name, gf_filter_event_name(evt->base.type)));
5617 }
5618 free_evt(evt);
5619 return;
5620 } else if (evt->base.on_pid && (evt->base.type == GF_FEVT_STOP) && !evt->base.on_pid->pid->is_playing) {
5621 GF_FilterPid *pid = (GF_FilterPid *) evt->base.on_pid->pid;
5622 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s PID %s event %s but PID is not playing, discarding\n", f->name, evt->base.on_pid->name, gf_filter_event_name(evt->base.type)));
5623
5624 for (i=0; i<pid->num_destinations; i++) {
5625 GF_FilterPidInst *pidi = (GF_FilterPidInst *) gf_list_get(pid->destinations, i);
5626 //don't forget we pre-processed stop by incrementing the discard counter and setting discard_packets on pid instances
5627 //undo this
5628 if (pidi->discard_packets) {
5629 assert(pidi->pid->discard_input_packets);
5630 safe_int_dec(& pidi->pid->discard_input_packets );
5631 pidi->discard_packets = GF_FALSE;
5632 }
5633 }
5634
5635 free_evt(evt);
5636 if ((f->num_input_pids==f->num_output_pids) && (f->num_input_pids==1)) {
5637 gf_filter_pid_set_discard(gf_list_get(f->input_pids, 0), GF_TRUE);
5638 }
5639 if (pid->not_connected)
5640 pid->not_connected = 2;
5641 return;
5642 } else if (f->freg->process_event) {
5643 FSESS_CHECK_THREAD(f)
5644 canceled = f->freg->process_event(f, evt);
5645 }
5646
5647 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s PID %s processed event %s - canceled %s\n", f->name, evt->base.on_pid ? evt->base.on_pid->name : "none", gf_filter_event_name(evt->base.type), canceled ? "yes" : "no" ));
5648
5649 if (evt->base.on_pid && ((evt->base.type == GF_FEVT_STOP) || (evt->base.type==GF_FEVT_SOURCE_SEEK) || (evt->base.type==GF_FEVT_PLAY)) ) {
5650 Bool do_reset = GF_TRUE;
5651 Bool is_play_reset = GF_FALSE;
5652 GF_FilterPidInst *p = (GF_FilterPidInst *) evt->base.on_pid;
5653 GF_FilterPid *pid = p->pid;
5654 //we need to force a PID reset when the first PLAY is > 0, since some filters may have dispatched packets during the initialization
5655 //phase
5656 if (evt->base.type==GF_FEVT_PLAY) {
5657 pid->is_playing = GF_TRUE;
5658 pid->filter->nb_pids_playing++;
5659 if (pid->initial_play_done) {
5660 do_reset = GF_FALSE;
5661 } else {
5662 pid->initial_play_done = GF_TRUE;
5663 is_play_reset = GF_TRUE;
5664 if (evt->play.start_range < 0.1)
5665 do_reset = GF_FALSE;
5666 }
5667 } else if (evt->base.type==GF_FEVT_STOP) {
5668 pid->is_playing = GF_FALSE;
5669 pid->filter->nb_pids_playing--;
5670
5671 if (pid->not_connected)
5672 pid->not_connected = 2;
5673 } else if (evt->base.type==GF_FEVT_SOURCE_SEEK) {
5674 pid->is_playing = GF_TRUE;
5675 pid->filter->nb_pids_playing++;
5676 }
5677 for (i=0; i<pid->num_destinations && do_reset; i++) {
5678 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, i);
5679 pidi->discard_packets = GF_TRUE;
5680 //if not play reset, the request was set only once so we need to do it if more than one destination
5681 if (is_play_reset || i)
5682 safe_int_inc(& pid->discard_input_packets );
5683
5684 safe_int_inc(& pid->filter->stream_reset_pending );
5685
5686 //post task on destination filter
5687 if (evt->base.type==GF_FEVT_STOP)
5688 gf_fs_post_task(pidi->filter->session, gf_filter_pid_reset_stop_task, pidi->filter, NULL, "reset_pid", pidi);
5689 else
5690 gf_fs_post_task(pidi->filter->session, gf_filter_pid_reset_task, pidi->filter, NULL, "reset_pid", pidi);
5691 }
5692 pid->nb_reaggregation_pending = 0;
5693 }
5694
5695 //after play or seek, request a process task for source filters or filters having pending packets
5696 if (!f->input_pids || f->pending_packets) {
5697 if ((evt->base.type==GF_FEVT_PLAY) || (evt->base.type==GF_FEVT_SOURCE_SEEK)) {
5698 gf_filter_post_process_task(f);
5699 }
5700 }
5701
5702 //quick hack for filters with one input pid and one outout pid, set discard on/off on the input
5703 //this avoids cases like TS demux dispatching data to inactive filters not checking their input
5704 //which ends up in session deadlock (filter still flagged as active and with pending packets)
5705 //if more than one input or more than one output, only the filter can decide what to do if some of the
5706 //streams are active and other not
5707 if ((f->num_input_pids==f->num_output_pids) && (f->num_input_pids==1)) {
5708 GF_FilterPidInst *apidi = gf_list_get(f->input_pids, 0);
5709 if (apidi->pid) {
5710 if (evt->base.type==GF_FEVT_STOP) {
5711 if (!canceled)
5712 gf_filter_pid_set_discard((GF_FilterPid *)apidi, GF_TRUE);
5713 } else if (evt->base.type==GF_FEVT_PLAY) {
5714 gf_filter_pid_set_discard((GF_FilterPid *)apidi, GF_FALSE);
5715 }
5716 }
5717 }
5718
5719 if ((evt->base.type==GF_FEVT_PLAY) || (evt->base.type==GF_FEVT_SET_SPEED)) {
5720 if (evt->base.on_pid) {
5721 u32 scaler = (u32) ( (evt->play.speed<0) ? -evt->play.speed : evt->play.speed ) * GF_FILTER_SPEED_SCALER;
5722 if (!scaler) scaler = GF_FILTER_SPEED_SCALER;
5723 if (scaler != evt->base.on_pid->playback_speed_scaler) {
5724 u32 prev_scaler = evt->base.on_pid->playback_speed_scaler;
5725 evt->base.on_pid->playback_speed_scaler = scaler;
5726 //lowering speed, we may need to trigger blocking
5727 if (scaler<prev_scaler)
5728 gf_filter_pid_would_block(evt->base.on_pid);
5729 //increasing speed, we may want to unblock
5730 else
5731 gf_filter_pid_check_unblock(evt->base.on_pid);
5732 }
5733 }
5734 }
5735 else if (evt->base.type==GF_FEVT_SOURCE_SWITCH) {
5736 for (i=0; i<f->num_output_pids; i++) {
5737 GF_FilterPid *apid = gf_list_get(f->output_pids, i);
5738 apid->has_seen_eos = GF_FALSE;
5739 gf_filter_pid_check_unblock(apid);
5740 }
5741 }
5742
5743 //no more input pids
5744 count = f->num_input_pids;
5745 if (count==0) canceled = GF_TRUE;
5746
5747 if (canceled) {
5748 free_evt(evt);
5749 return;
5750 }
5751 if (!task->pid) dispatched_filters = gf_list_new();
5752
5753 //otherwise forward event to each input PID
5754 for (i=0; i<count; i++) {
5755 GF_FilterEvent *an_evt;
5756 GF_FilterPidInst *pid_inst = gf_list_get(f->input_pids, i);
5757 GF_FilterPid *pid = pid_inst->pid;
5758 if (!pid) continue;
5759
5760 if (dispatched_filters) {
5761 if (gf_list_find(dispatched_filters, pid_inst->pid->filter) >=0 )
5762 continue;
5763
5764 gf_list_add(dispatched_filters, pid_inst->pid->filter);
5765 }
5766
5767 //mark pid instance as about to be reset to avoid processing PID destroy task before
5768 if ((evt->base.type == GF_FEVT_STOP) || (evt->base.type==GF_FEVT_SOURCE_SEEK)) {
5769 pid_inst->discard_packets = GF_TRUE;
5770 safe_int_inc(& pid_inst->pid->discard_input_packets );
5771 }
5772 //allocate a copy except for the last PID where we use the one from the input
5773 if (evt_reused) {
5774 an_evt = dup_evt(evt);
5775 } else {
5776 an_evt = evt;
5777 evt_reused = GF_TRUE;
5778 }
5779 an_evt->base.on_pid = task->pid ? pid : NULL;
5780
5781 safe_int_inc(&pid->filter->num_events_queued);
5782
5783 gf_fs_post_task(pid->filter->session, gf_filter_pid_send_event_downstream, pid->filter, task->pid ? pid : NULL, "downstream_event", an_evt);
5784 }
5785 if (dispatched_filters) gf_list_del(dispatched_filters);
5786 if (!evt_reused) free_evt(evt);
5787 return;
5788 }
5789
gf_filter_pid_send_event_upstream(GF_FSTask * task)5790 void gf_filter_pid_send_event_upstream(GF_FSTask *task)
5791 {
5792 u32 i, j;
5793 Bool canceled = GF_FALSE;
5794 GF_FilterEvent *evt = task->udta;
5795 GF_Filter *f = task->filter;
5796
5797 if (f->stream_reset_pending) {
5798 TASK_REQUEUE(task)
5799 return;
5800 }
5801
5802 canceled = f->freg->process_event ? f->freg->process_event(f, evt) : GF_TRUE;
5803 if (!canceled) {
5804 for (i=0; i<f->num_output_pids; i++) {
5805 GF_FilterPid *apid = gf_list_get(f->output_pids, i);
5806 for (j=0; j<apid->num_destinations; j++) {
5807 GF_FilterEvent *an_evt;
5808 GF_FilterPidInst *pidi = gf_list_get(apid->destinations, j);
5809
5810 an_evt = dup_evt(evt);
5811 an_evt->base.on_pid = (GF_FilterPid *)pidi;
5812 gf_fs_post_task(pidi->filter->session, gf_filter_pid_send_event_upstream, pidi->filter, NULL, "upstream_event", an_evt);
5813 }
5814 }
5815 }
5816 free_evt(evt);
5817 }
5818
gf_filter_pid_send_event_internal(GF_FilterPid * pid,GF_FilterEvent * evt,Bool force_downstream)5819 void gf_filter_pid_send_event_internal(GF_FilterPid *pid, GF_FilterEvent *evt, Bool force_downstream)
5820 {
5821 GF_FilterEvent *an_evt;
5822 GF_FilterPid *target_pid=NULL;
5823 Bool upstream=GF_FALSE;
5824 if (!pid) {
5825 pid = evt->base.on_pid;
5826 if (!pid) return;
5827 }
5828 //filter is being shut down, prevent any event posting
5829 if (pid->filter->finalized) return;
5830
5831 if ((evt->base.type==GF_FEVT_FILE_DELETE) && !evt->file_del.url) return;
5832
5833 if (!force_downstream && PID_IS_OUTPUT(pid)) {
5834 upstream = GF_TRUE;
5835 }
5836
5837 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s PID %s queuing %s event %s\n", pid->pid->filter->name, pid->pid->name, upstream ? "upstream" : "downstream", gf_filter_event_name(evt->base.type) ));
5838
5839 if (upstream) {
5840 u32 i, j;
5841
5842 an_evt = init_evt(evt);
5843
5844 for (i=0; i<pid->filter->num_output_pids; i++) {
5845 GF_FilterPid *apid = gf_list_get(pid->filter->output_pids, i);
5846 if (evt->base.on_pid && (apid != evt->base.on_pid)) continue;
5847 for (j=0; j<apid->num_destinations; j++) {
5848 GF_FilterEvent *up_evt;
5849 GF_FilterPidInst *pidi = gf_list_get(apid->destinations, j);
5850
5851 up_evt = dup_evt(an_evt);
5852 up_evt->base.on_pid = (GF_FilterPid *)pidi;
5853 gf_fs_post_task(pidi->filter->session, gf_filter_pid_send_event_upstream, pidi->filter, NULL, "upstream_event", up_evt);
5854 }
5855 }
5856 free_evt(an_evt);
5857 return;
5858 }
5859
5860
5861 if ((evt->base.type == GF_FEVT_STOP) || (evt->base.type == GF_FEVT_PLAY) || (evt->base.type==GF_FEVT_SOURCE_SEEK)) {
5862 u32 i, count = pid->pid->num_destinations;
5863 for (i=0; i<count; i++) {
5864 GF_FilterPidInst *pidi = gf_list_get(pid->pid->destinations, i);
5865 if (evt->base.type == GF_FEVT_PLAY) {
5866 pidi->is_end_of_stream = GF_FALSE;
5867 // gf_filter_pid_clear_eos(pid, GF_FALSE);
5868 } else {
5869 //flag pid instance to discard all packets (cf above note)
5870 pidi->discard_packets = GF_TRUE;
5871 safe_int_inc(& pidi->pid->discard_input_packets );
5872 }
5873 }
5874 }
5875
5876 an_evt = init_evt(evt);
5877 if (evt->base.on_pid) {
5878 target_pid = evt->base.on_pid->pid;
5879 an_evt->base.on_pid = target_pid;
5880 safe_int_inc(&target_pid->filter->num_events_queued);
5881 }
5882 gf_fs_post_task(pid->pid->filter->session, gf_filter_pid_send_event_downstream, pid->pid->filter, target_pid, "downstream_event", an_evt);
5883 }
5884
5885 GF_EXPORT
gf_filter_pid_send_event(GF_FilterPid * pid,GF_FilterEvent * evt)5886 void gf_filter_pid_send_event(GF_FilterPid *pid, GF_FilterEvent *evt)
5887 {
5888 if (!evt) return;
5889 if (evt->base.type==GF_FEVT_RESET_SCENE) return;
5890 if (evt->base.type==GF_FEVT_INFO_UPDATE) return;
5891
5892 gf_filter_pid_send_event_internal(pid, evt, GF_FALSE);
5893 }
5894
5895 GF_EXPORT
gf_filter_send_event(GF_Filter * filter,GF_FilterEvent * evt,Bool upstream)5896 void gf_filter_send_event(GF_Filter *filter, GF_FilterEvent *evt, Bool upstream)
5897 {
5898 GF_FilterEvent *an_evt;
5899 if (!filter) return;
5900 if (filter->multi_sink_target)
5901 filter = filter->multi_sink_target;
5902
5903 //filter is being shut down, prevent any event posting
5904 if (filter->finalized) return;
5905 if (!evt) return;
5906 if ((evt->base.type==GF_FEVT_FILE_DELETE) && !evt->file_del.url) return;
5907
5908 if (evt->base.type==GF_FEVT_RESET_SCENE)
5909 return;
5910
5911 if (evt->base.on_pid && PID_IS_OUTPUT(evt->base.on_pid)) {
5912 gf_filter_pid_send_event_internal(evt->base.on_pid, evt, GF_FALSE);
5913 return;
5914 }
5915
5916 //switch and seek events are only sent on source filters
5917 if ((evt->base.type==GF_FEVT_SOURCE_SWITCH) || (evt->base.type==GF_FEVT_SOURCE_SEEK)) {
5918 if (filter->num_input_pids) {
5919 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Sending %s event on non source filter %s is not allowed, discarding)\n", gf_filter_event_name(evt->base.type), filter->name));
5920 return;
5921 }
5922 }
5923
5924 an_evt = init_evt(evt);
5925
5926 if (evt->base.on_pid) {
5927 safe_int_inc(&evt->base.on_pid->filter->num_events_queued);
5928 }
5929
5930 gf_fs_post_task(filter->session, gf_filter_pid_send_event_downstream, filter, evt->base.on_pid, "downstream_event", an_evt);
5931 }
5932
5933
5934 GF_EXPORT
gf_filter_pid_exec_event(GF_FilterPid * pid,GF_FilterEvent * evt)5935 void gf_filter_pid_exec_event(GF_FilterPid *pid, GF_FilterEvent *evt)
5936 {
5937 //filter is being shut down, prevent any event posting
5938 if (pid->pid->filter->finalized) return;
5939 if (! (pid->pid->filter->freg->flags & GF_FS_REG_MAIN_THREAD)) {
5940 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Executing event on PID %s created by filter %s not running on main thread, not allowed\n", pid->pid->name, pid->filter->name));
5941 return;
5942 }
5943
5944 if (pid->pid->filter->freg->process_event) {
5945 if (evt->base.on_pid) evt->base.on_pid = evt->base.on_pid->pid;
5946 FSESS_CHECK_THREAD(pid->pid->filter)
5947 pid->pid->filter->freg->process_event(pid->pid->filter, evt);
5948 }
5949 }
5950
5951
5952 GF_EXPORT
gf_filter_pid_is_filter_in_parents(GF_FilterPid * pid,GF_Filter * filter)5953 Bool gf_filter_pid_is_filter_in_parents(GF_FilterPid *pid, GF_Filter *filter)
5954 {
5955 if (!pid || !filter) return GF_FALSE;
5956 pid = pid->pid;
5957 return filter_in_parent_chain(pid->pid->filter, filter);
5958 }
5959
filter_pid_collect_stats(GF_List * pidi_list,GF_FilterPidStatistics * stats)5960 static void filter_pid_collect_stats(GF_List *pidi_list, GF_FilterPidStatistics *stats)
5961 {
5962 u32 i;
5963 for (i=0; i<gf_list_count(pidi_list); i++) {
5964 GF_FilterPidInst *pidi = (GF_FilterPidInst *) gf_list_get(pidi_list, i);
5965 if (!pidi->pid) continue;
5966
5967 stats->avgerage_bitrate += pidi->avg_bit_rate;
5968 if (!stats->first_process_time || (stats->first_process_time > pidi->first_frame_time))
5969 stats->first_process_time = pidi->first_frame_time;
5970 if (stats->last_process_time < pidi->last_pck_fetch_time)
5971 stats->last_process_time = pidi->last_pck_fetch_time;
5972
5973 stats->max_bitrate += pidi->max_bit_rate;
5974
5975 if (stats->max_process_time < (u32) pidi->max_process_time)
5976 stats->max_process_time = (u32) pidi->max_process_time;
5977 if (stats->max_sap_process_time < (u32) pidi->max_sap_process_time)
5978 stats->max_sap_process_time = (u32) pidi->max_sap_process_time;
5979 if (!stats->min_frame_dur || (stats->min_frame_dur > pidi->pid->min_pck_duration))
5980 stats->min_frame_dur = pidi->pid->min_pck_duration;
5981 stats->nb_processed += pidi->nb_processed;
5982 stats->nb_saps += pidi->nb_sap_processed;
5983 stats->total_process_time += pidi->total_process_time;
5984 stats->total_sap_process_time += pidi->total_sap_process_time;
5985 stats->average_process_rate += pidi->avg_process_rate;
5986 stats->max_process_rate += pidi->max_process_rate;
5987
5988 if (stats->nb_buffer_units < pidi->pid->nb_buffer_unit)
5989 stats->nb_buffer_units = pidi->pid->nb_buffer_unit;
5990 if (stats->max_buffer_time < pidi->pid->max_buffer_time)
5991 stats->max_buffer_time = pidi->pid->max_buffer_time;
5992
5993 if (stats->max_playout_time < pidi->pid->user_max_playout_time)
5994 stats->max_playout_time = pidi->pid->user_max_playout_time;
5995 if (!stats->min_playout_time || (stats->min_playout_time > pidi->pid->user_min_playout_time))
5996 stats->min_playout_time = pidi->pid->user_min_playout_time;
5997
5998 if (stats->buffer_time < pidi->pid->buffer_duration)
5999 stats->buffer_time = pidi->pid->buffer_duration;
6000 }
6001 }
6002
filter_locate_enc_dec_sink(GF_Filter * filter,Bool locate_decoder)6003 static GF_Filter *filter_locate_enc_dec_sink(GF_Filter *filter, Bool locate_decoder)
6004 {
6005 u32 i, j;
6006
6007 for (i=0; i<filter->num_output_pids; i++) {
6008 GF_FilterPid *pid = gf_list_get(filter->output_pids, i);
6009 for (j=0; j<pid->num_destinations; j++) {
6010 GF_Filter *res;
6011 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, j);
6012 if (pidi->is_decoder_input) return pidi->filter;
6013 res = filter_locate_enc_dec_sink(pidi->filter, locate_decoder);
6014 if (res) return res;
6015 }
6016 }
6017 return NULL;
6018 }
6019
filter_locate_enc_dec_src(GF_Filter * filter,Bool locate_decoder)6020 static GF_Filter *filter_locate_enc_dec_src(GF_Filter *filter, Bool locate_decoder)
6021 {
6022 u32 i;
6023
6024 for (i=0; i<filter->num_input_pids; i++) {
6025 GF_Filter *res;
6026 GF_FilterPidInst *pidi = gf_list_get(filter->input_pids, i);
6027 if (pidi->is_decoder_input) return filter;
6028
6029 res = filter_locate_enc_dec_sink(pidi->pid->filter, locate_decoder);
6030 if (res) return res;
6031 }
6032 return NULL;
6033 }
6034
6035
6036 GF_EXPORT
gf_filter_pid_get_statistics(GF_FilterPid * pid,GF_FilterPidStatistics * stats,GF_FilterPidStatsLocation location)6037 GF_Err gf_filter_pid_get_statistics(GF_FilterPid *pid, GF_FilterPidStatistics *stats, GF_FilterPidStatsLocation location)
6038 {
6039 GF_FilterPidInst *pidi = (GF_FilterPidInst *)pid;
6040 GF_Filter *filter=NULL;
6041 Bool for_decoder=GF_TRUE;
6042
6043 memset(stats, 0, sizeof(GF_FilterPidStatistics) );
6044 if (!pidi->pid) {
6045 stats->disconnected = GF_TRUE;
6046 return GF_OK;
6047 }
6048
6049 switch (location) {
6050 case GF_STATS_LOCAL:
6051 if (PID_IS_OUTPUT(pid)) {
6052 filter_pid_collect_stats(pid->destinations, stats);
6053 return GF_OK;
6054 }
6055 stats->avgerage_bitrate = pidi->avg_bit_rate;
6056 stats->first_process_time = pidi->first_frame_time;
6057 stats->last_process_time = pidi->last_pck_fetch_time;
6058 stats->max_bitrate = pidi->max_bit_rate;
6059 stats->max_process_time = (u32) pidi->max_process_time;
6060 stats->max_sap_process_time = (u32) pidi->max_sap_process_time;
6061 stats->min_frame_dur = pidi->pid->min_pck_duration;
6062 stats->nb_processed = pidi->nb_processed;
6063 stats->nb_saps = pidi->nb_sap_processed;
6064 stats->total_process_time = pidi->total_process_time;
6065 stats->total_sap_process_time = pidi->total_sap_process_time;
6066
6067 stats->average_process_rate = pidi->avg_process_rate;
6068 stats->max_process_rate = pidi->max_process_rate;
6069 return GF_OK;
6070 case GF_STATS_LOCAL_INPUTS:
6071 if (PID_IS_OUTPUT(pid)) {
6072 filter_pid_collect_stats(pid->destinations, stats);
6073 return GF_OK;
6074 }
6075 filter = pidi->pid->filter;
6076 break;
6077 case GF_STATS_ENCODER_SOURCE:
6078 for_decoder = GF_FALSE;
6079 case GF_STATS_DECODER_SOURCE:
6080 filter = filter_locate_enc_dec_src(pidi->pid->filter, for_decoder);
6081 break;
6082 case GF_STATS_ENCODER_SINK:
6083 for_decoder = GF_FALSE;
6084 case GF_STATS_DECODER_SINK:
6085 filter = filter_locate_enc_dec_sink(pidi->pid->filter, for_decoder);
6086 break;
6087 }
6088 if (!filter) {
6089 return GF_NOT_FOUND;
6090 }
6091 filter_pid_collect_stats(filter->input_pids, stats);
6092 return GF_OK;
6093 }
6094
6095 GF_EXPORT
gf_filter_pid_remove(GF_FilterPid * pid)6096 void gf_filter_pid_remove(GF_FilterPid *pid)
6097 {
6098 GF_FilterPacket *pck;
6099 if (PID_IS_INPUT(pid)) {
6100 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Removing PID input filter (%s:%s) not allowed\n", pid->filter->name, pid->pid->name));
6101 }
6102 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Filter %s removed output PID %s\n", pid->filter->name, pid->pid->name));
6103
6104 if (pid->filter->removed) {
6105 return;
6106 }
6107 if (pid->removed) {
6108 return;
6109 }
6110 pid->removed = GF_TRUE;
6111 if (pid->filter->marked_for_removal || (pid->has_seen_eos && !pid->nb_buffer_unit)) {
6112 u32 i;
6113 for (i=0; i<pid->num_destinations; i++) {
6114 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, i);
6115 gf_fs_post_task(pidi->filter->session, gf_filter_pid_disconnect_task, pidi->filter, pidi->pid, "pidinst_disconnect", NULL);
6116 }
6117 return;
6118 }
6119
6120 //we create a fake packet for removal signaling
6121 pck = gf_filter_pck_new_shared_internal(pid, NULL, 0, NULL, GF_TRUE);
6122 gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
6123 pck->pck->info.flags |= GF_PCK_CMD_PID_REM;
6124 gf_filter_pck_send(pck);
6125 }
6126
6127 GF_EXPORT
gf_filter_pid_try_pull(GF_FilterPid * pid)6128 void gf_filter_pid_try_pull(GF_FilterPid *pid)
6129 {
6130 if (PID_IS_OUTPUT(pid)) {
6131 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to pull from output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
6132 return;
6133 }
6134 pid = pid->pid;
6135 if (pid->filter->session->threads) {
6136 GF_LOG(GF_LOG_DEBUG, GF_LOG_FILTER, ("Filter pull in multithread mode not yet implementing - defaulting to 1 ms sleep\n", pid->pid->name, pid->filter->name));
6137 gf_sleep(1);
6138 return;
6139 }
6140
6141 gf_filter_process_inline(pid->filter);
6142 }
6143
6144
6145 GF_EXPORT
gf_filter_pid_get_clock_info(GF_FilterPid * pid,u64 * clock_time,u32 * timescale)6146 GF_FilterClockType gf_filter_pid_get_clock_info(GF_FilterPid *pid, u64 *clock_time, u32 *timescale)
6147 {
6148 GF_FilterPidInst *pidi = (GF_FilterPidInst *)pid;
6149 GF_FilterClockType res;
6150 if (PID_IS_OUTPUT(pid)) {
6151 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Querying clock on output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
6152 return GF_FILTER_CLOCK_NONE;
6153 }
6154 if (clock_time) *clock_time = pidi->last_clock_value;
6155 if (timescale) *timescale = pidi->last_clock_timescale;
6156 res = pidi->last_clock_type;
6157 pidi->last_clock_type = 0;
6158 return res;
6159 }
6160
6161 GF_EXPORT
gf_filter_pid_get_timescale(GF_FilterPid * pid)6162 u32 gf_filter_pid_get_timescale(GF_FilterPid *pid)
6163 {
6164 GF_PropertyMap *map = pid ? gf_list_get(pid->pid->properties, 0) : 0;
6165 return map ? map->timescale : 0;
6166 }
6167
6168 GF_EXPORT
gf_filter_pid_clear_eos(GF_FilterPid * pid,Bool clear_all)6169 void gf_filter_pid_clear_eos(GF_FilterPid *pid, Bool clear_all)
6170 {
6171 u32 i, j;
6172 Bool was_blocking;
6173 GF_FilterPidInst *pidi = (GF_FilterPidInst *)pid;
6174 if (PID_IS_OUTPUT(pid)) {
6175 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Clearing EOS on output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
6176 return;
6177 }
6178 pid = pid->pid;
6179 was_blocking = pid->filter->would_block;
6180 for (i=0; i<pid->filter->num_output_pids; i++) {
6181 GF_FilterPid *apid = gf_list_get(pid->filter->output_pids, i);
6182 if (!clear_all && (pid != apid)) continue;
6183
6184 for (j=0; j<apid->num_destinations; j++) {
6185 GF_FilterPidInst *apidi = gf_list_get(apid->destinations, j);
6186 if (apidi->filter != pidi->filter) continue;
6187
6188 if (apidi->is_end_of_stream) {
6189 apidi->is_end_of_stream = GF_FALSE;
6190 }
6191 if (apid->has_seen_eos) {
6192 apid->has_seen_eos = GF_FALSE;
6193 gf_filter_pid_check_unblock(apid);
6194 }
6195
6196 if (apidi->pid->filter->would_block && apidi->pid->filter->num_input_pids) {
6197 u32 k;
6198 for (k=0; k<apidi->pid->filter->num_input_pids; k++) {
6199 GF_FilterPidInst *source_pid_inst = gf_list_get(apidi->pid->filter->input_pids, k);
6200 gf_filter_pid_clear_eos((GF_FilterPid *) source_pid_inst, clear_all);
6201 }
6202 }
6203 }
6204 }
6205 if (!clear_all || (was_blocking == pid->filter->would_block)) return;
6206
6207 //unblock parent
6208 for (i=0; i<pid->filter->num_input_pids; i++) {
6209 GF_FilterPidInst *apidi = gf_list_get(pid->filter->input_pids, i);
6210 gf_filter_pid_clear_eos((GF_FilterPid *) apidi, GF_TRUE);
6211 }
6212
6213 }
6214
6215 GF_EXPORT
gf_filter_pid_set_clock_mode(GF_FilterPid * pid,Bool filter_in_charge)6216 void gf_filter_pid_set_clock_mode(GF_FilterPid *pid, Bool filter_in_charge)
6217 {
6218 GF_FilterPidInst *pidi = (GF_FilterPidInst *)pid;
6219 if (PID_IS_OUTPUT(pid)) {
6220 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Changing clock mode on output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
6221 return;
6222 }
6223 pidi->handles_clock_references = filter_in_charge;
6224 }
6225
6226 GF_EXPORT
gf_filter_pid_get_args(GF_FilterPid * pid)6227 const char *gf_filter_pid_get_args(GF_FilterPid *pid)
6228 {
6229 if (PID_IS_OUTPUT(pid)) {
6230 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Querying args on output PID %s in filter %s\n", pid->pid->name, pid->filter->name));
6231 return NULL;
6232 }
6233 if (pid->pid->filter->src_args) return pid->pid->filter->src_args;
6234 return pid->pid->filter->orig_args;
6235 }
6236
6237 GF_EXPORT
gf_filter_pid_set_max_buffer(GF_FilterPid * pid,u32 total_duration_us)6238 void gf_filter_pid_set_max_buffer(GF_FilterPid *pid, u32 total_duration_us)
6239 {
6240 if (PID_IS_INPUT(pid)) {
6241 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Setting max buffer on input PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6242 return;
6243 }
6244 pid->max_buffer_time = pid->user_max_buffer_time = total_duration_us;
6245 }
6246
6247 GF_EXPORT
gf_filter_pid_get_max_buffer(GF_FilterPid * pid)6248 u32 gf_filter_pid_get_max_buffer(GF_FilterPid *pid)
6249 {
6250 if (PID_IS_OUTPUT(pid)) {
6251 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Querying max buffer on output PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6252 return 0;
6253 }
6254 return pid->pid->user_max_buffer_time;
6255 }
6256
6257
6258 GF_EXPORT
gf_filter_pid_set_loose_connect(GF_FilterPid * pid)6259 void gf_filter_pid_set_loose_connect(GF_FilterPid *pid)
6260 {
6261 if (PID_IS_INPUT(pid)) {
6262 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Setting loose connect on input PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6263 return;
6264 }
6265 pid->not_connected_ok = GF_TRUE;
6266 }
6267
6268 GF_EXPORT
gf_filter_pid_caps_query(GF_FilterPid * pid,u32 prop_4cc)6269 const GF_PropertyValue *gf_filter_pid_caps_query(GF_FilterPid *pid, u32 prop_4cc)
6270 {
6271 u32 i;
6272 GF_PropertyMap *map = pid->pid->caps_negociate;
6273 if (PID_IS_INPUT(pid)) {
6274 u32 k;
6275 GF_Filter *dst = pid->filter->cap_dst_filter;
6276 //the first entry in destination filters may be the fnal destination and won't hold any caps query
6277 //we therefore use the last entry which points to the next filter in the chain
6278 if (!dst) dst = gf_list_last(pid->filter->destination_filters);
6279 if (!dst) dst = gf_list_get(pid->filter->destination_links, 0);
6280
6281 if (!dst || (dst->cap_idx_at_resolution<0) ) {
6282 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Reconfig caps query on input PID %s in filter %s with no destination filter set\n", pid->pid->name, pid->filter->name));
6283 return NULL;
6284 }
6285 for (k=dst->cap_idx_at_resolution; k<dst->freg->nb_caps; k++) {
6286 const GF_FilterCapability *cap = &dst->freg->caps[k];
6287 if (!(cap->flags & GF_CAPFLAG_IN_BUNDLE)) return NULL;
6288
6289 if (!(cap->flags & GF_CAPFLAG_INPUT)) continue;
6290 if (cap->flags & GF_CAPFLAG_OPTIONAL) continue;
6291 if (cap->code == prop_4cc) return &cap->val;
6292 }
6293 return NULL;
6294 }
6295 if (map) return gf_props_get_property(map, prop_4cc, NULL);
6296 for (i=0; i<pid->num_destinations; i++) {
6297 u32 j;
6298 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, i);
6299 for (j=0; j<pidi->filter->nb_forced_caps; j++) {
6300 if (pidi->filter->forced_caps[j].code==prop_4cc)
6301 return &pidi->filter->forced_caps[j].val;
6302 }
6303 //walk up the chain
6304 for (j=0; j<pidi->filter->num_output_pids; j++) {
6305 GF_FilterPid *apid = gf_list_get(pidi->filter->output_pids, j);
6306 if (apid) {
6307 const GF_PropertyValue *p = gf_filter_pid_caps_query(apid, prop_4cc);
6308 if (p) return p;
6309 }
6310 }
6311
6312 }
6313
6314 //trick here: we may not be connected yet (called during a configure_pid), use the target destination
6315 //of the filter as caps source
6316 if (gf_list_count(pid->filter->destination_filters) ) {
6317 GF_Filter *a_filter = gf_list_get(pid->filter->destination_filters, 0);
6318 while (a_filter) {
6319 for (i=0; i<a_filter->nb_forced_caps; i++) {
6320 if (a_filter->forced_caps[i].code==prop_4cc)
6321 return &a_filter->forced_caps[i].val;
6322 }
6323 a_filter = gf_list_get(a_filter->destination_filters, 0);
6324 }
6325 }
6326
6327 //second trick here: we may not be connected yet (called during a configure_pid), use the target destination
6328 //of the filter as caps source
6329 if (pid->filter->cap_dst_filter) {
6330 GF_Filter *a_filter = pid->filter->cap_dst_filter;
6331 for (i=0; i<a_filter->nb_forced_caps; i++) {
6332 if (a_filter->forced_caps[i].code==prop_4cc)
6333 return &a_filter->forced_caps[i].val;
6334 }
6335 }
6336
6337 return NULL;
6338 }
6339
6340 GF_EXPORT
gf_filter_pid_caps_query_str(GF_FilterPid * pid,const char * prop_name)6341 const GF_PropertyValue *gf_filter_pid_caps_query_str(GF_FilterPid *pid, const char *prop_name)
6342 {
6343 GF_PropertyMap *map = pid->caps_negociate;
6344 if (PID_IS_INPUT(pid)) {
6345 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Reconfig caps query on input PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6346 return NULL;
6347 }
6348 return map ? gf_props_get_property(map, 0, prop_name) : NULL;
6349 }
6350
6351
6352 GF_EXPORT
gf_filter_pid_resolve_file_template(GF_FilterPid * pid,char szTemplate[GF_MAX_PATH],char szFinalName[GF_MAX_PATH],u32 file_idx,const char * file_suffix)6353 GF_Err gf_filter_pid_resolve_file_template(GF_FilterPid *pid, char szTemplate[GF_MAX_PATH], char szFinalName[GF_MAX_PATH], u32 file_idx, const char *file_suffix)
6354 {
6355 u32 k;
6356 char szFormat[30], szTemplateVal[GF_MAX_PATH], szPropVal[GF_PROP_DUMP_ARG_SIZE];
6357 char *name = szTemplate;
6358 if (!strchr(szTemplate, '$')) {
6359 strcpy(szFinalName, szTemplate);
6360 return GF_OK;
6361 }
6362
6363 k = 0;
6364 while (name[0]) {
6365 char *sep=NULL;
6366 char *fsep=NULL;
6367 const char *str_val = NULL;
6368 s64 value = 0;
6369 Bool is_ok = GF_TRUE;
6370 Bool do_skip = GF_FALSE;
6371 Bool has_val = GF_FALSE;
6372 Bool is_file_str = GF_FALSE;
6373 u32 prop_4cc = 0;
6374 GF_PropertyValue prop_val_patched;
6375 const GF_PropertyValue *prop_val = NULL;
6376
6377 if (k+1==GF_MAX_PATH) {
6378 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("[Filter] Not enough memory to solve file template %s\n", szTemplate));
6379 return GF_OUT_OF_MEM;
6380 }
6381 if (name[0] != '$') {
6382 szFinalName[k] = name[0];
6383 k++;
6384 name++;
6385 continue;
6386 }
6387 if (name[1]=='$') {
6388 szFinalName[k] = '$';
6389 name++;
6390 k++;
6391 continue;
6392 }
6393 sep = strchr(name+1, '$');
6394 if (!sep) {
6395 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] broken file template expecting $KEYWORD$, couln't find second '$'\n", szTemplate));
6396 strcpy(szFinalName, szTemplate);
6397 return GF_BAD_PARAM;
6398 }
6399 szFormat[0] = '%';
6400 szFormat[1] = 'd';
6401 szFormat[2] = 0;
6402
6403 szFinalName[k] = 0;
6404 name++;
6405 sep[0]=0;
6406 fsep = strchr(name, '%');
6407 if (fsep) {
6408 strcpy(szFormat, fsep);
6409 fsep[0]=0;
6410 }
6411
6412 if (!strcmp(name, "num")) {
6413 name += 3;
6414 value = file_idx;
6415 has_val = GF_TRUE;
6416 } else if (!strcmp(name, "URL")) {
6417 prop_val = gf_filter_pid_get_property_first(pid, GF_PROP_PID_URL);
6418 is_file_str = GF_TRUE;
6419 } else if (!strcmp(name, "File")) {
6420 prop_val = gf_filter_pid_get_property_first(pid, GF_PROP_PID_FILEPATH);
6421 if (!prop_val) prop_val = gf_filter_pid_get_property_first(pid, GF_PROP_PID_URL);
6422 is_file_str = GF_TRUE;
6423 } else if (!strcmp(name, "PID")) {
6424 prop_val = gf_filter_pid_get_property_first(pid, GF_PROP_PID_ID);
6425 } else if (!strcmp(name, "FS")) {
6426 str_val = file_suffix ? file_suffix : "";
6427 is_ok = GF_TRUE;
6428 } else if (!strncmp(name, "p4cc=", 5)) {
6429 if (strlen(name) != 9) {
6430 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] wrong length in 4CC template, expecting 4cc=ABCD\n", name));
6431 is_ok = GF_FALSE;
6432 } else {
6433 prop_4cc = GF_4CC(name[5],name[6],name[7],name[8]);
6434 prop_val = gf_filter_pid_get_property_first(pid, prop_4cc);
6435 if (!prop_val) {
6436 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] no pid property of type %s\n", name+5));
6437 is_ok = GF_FALSE;
6438 }
6439 }
6440 } else if (!strncmp(name, "pname=", 6)) {
6441 prop_val = gf_filter_pid_get_property_str_first(pid, name+6);
6442 if (!prop_val) {
6443 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] no pid property named %s\n", name+6));
6444 is_ok = GF_FALSE;
6445 }
6446 } else if (!strncmp(name, "Number", 6)) {
6447 do_skip = GF_TRUE;
6448 } else if (!strncmp(name, "Time", 4)) {
6449 do_skip = GF_TRUE;
6450 } else if (!strncmp(name, "RepresentationID", 16)) {
6451 do_skip = GF_TRUE;
6452 } else if (!strncmp(name, "Bandwidth", 9)) {
6453 do_skip = GF_TRUE;
6454 } else if (!strncmp(name, "SubNumber", 9)) {
6455 do_skip = GF_TRUE;
6456 } else if (!strncmp(name, "Init", 4)) {
6457 do_skip = GF_TRUE;
6458 } else if (!strncmp(name, "Path", 4)) {
6459 do_skip = GF_TRUE;
6460 } else {
6461 char *next_eq = strchr(name, '=');
6462 char *next_sep = strchr(name, '$');
6463 if (!next_eq || (next_eq - name < next_sep - name)) {
6464 prop_4cc = gf_props_get_id(name);
6465 //not matching, try with name
6466 if (!prop_4cc) {
6467 prop_val = gf_filter_pid_get_property_str_first(pid, name);
6468 if (!prop_val) {
6469 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] Unrecognized template %s\n", name));
6470 is_ok = GF_FALSE;
6471 }
6472 } else {
6473 prop_val = gf_filter_pid_get_property_first(pid, prop_4cc);
6474 if (!prop_val) {
6475 is_ok = GF_FALSE;
6476 }
6477 }
6478 } else {
6479 u32 i, len = (u32) (next_sep ? 1+(next_sep - name) : strlen(name) );
6480 szFinalName[k]='$';
6481 k++;
6482 for (i=0; i<len; i++) {
6483 szFinalName[k] = name[0];
6484 k++;
6485 name++;
6486 }
6487 szFinalName[k]='$';
6488 k++;
6489 sep[0] = '$';
6490 name = sep+1;
6491 continue;
6492 }
6493 }
6494 if (fsep) fsep[0] = '%';
6495 if (do_skip) {
6496 sep[0] = '$';
6497 szFinalName[k] = '$';
6498 k++;
6499 while (name[0] && (name[0] != '$')) {
6500 szFinalName[k] = name[0];
6501 k++;
6502 name++;
6503 }
6504 szFinalName[k] = '$';
6505 k++;
6506 name++;
6507
6508
6509 continue;
6510
6511 }
6512
6513
6514 if (!is_ok && !prop_val && prop_4cc) {
6515 if (prop_4cc==GF_PROP_PID_CROP_POS) {
6516 prop_val_patched.type = GF_PROP_VEC2I;
6517 prop_val_patched.value.vec2i.x = 0;
6518 prop_val_patched.value.vec2i.y = 0;
6519 prop_val = &prop_val_patched;
6520 is_ok=GF_TRUE;
6521 }
6522 else if (prop_4cc==GF_PROP_PID_ORIG_SIZE) {
6523 prop_val_patched.type = GF_PROP_VEC2I;
6524 prop_val = gf_filter_pid_get_property_first(pid, GF_PROP_PID_WIDTH);
6525 prop_val_patched.value.vec2i.x = prop_val ? prop_val->value.uint : 0;
6526 prop_val = gf_filter_pid_get_property_first(pid, GF_PROP_PID_HEIGHT);
6527 prop_val_patched.value.vec2i.y = prop_val ? prop_val->value.uint : 0;
6528 prop_val = &prop_val_patched;
6529 is_ok=GF_TRUE;
6530 } else {
6531 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] property %s not found for pid, cannot resolve template\n", name));
6532 return GF_BAD_PARAM;
6533 }
6534 }
6535
6536 if (!is_ok) {
6537 if (sep) sep[0] = '$';
6538 return GF_BAD_PARAM;
6539 }
6540 if (prop_val) {
6541 if ((prop_val->type==GF_PROP_UINT) || (prop_val->type==GF_PROP_SINT)) {
6542 value = prop_val->value.uint;
6543 has_val = GF_TRUE;
6544 } else {
6545 str_val = gf_props_dump_val(prop_val, szPropVal, GF_FALSE, NULL);
6546 }
6547 }
6548 szTemplateVal[0]=0;
6549 if (has_val) {
6550 sprintf(szTemplateVal, szFormat, value);
6551 } else if (str_val) {
6552 if (is_file_str) {
6553 char *ext;
6554 char *sname;
6555
6556 if (!strncmp(str_val, "gfio://", 7))
6557 str_val = gf_fileio_translate_url(str_val);
6558
6559 sname = strrchr(str_val, '/');
6560 if (!sname) sname = strrchr(str_val, '\\');
6561 if (!sname) sname = (char *) str_val;
6562 else sname++;
6563 ext = strrchr(str_val, '.');
6564
6565 if (ext) {
6566 u32 len = (u32) (ext - sname);
6567 strncpy(szTemplateVal, sname, ext - sname);
6568 szTemplateVal[len] = 0;
6569 } else {
6570 strcpy(szTemplateVal, sname);
6571 }
6572 } else {
6573 strcpy(szTemplateVal, str_val);
6574 }
6575 } else {
6576 GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[Filter] property %s not found for pid, cannot resolve template\n", name));
6577 return GF_BAD_PARAM;
6578 }
6579 if (k + strlen(szTemplateVal) > GF_MAX_PATH) {
6580 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("[Filter] Not enough memory to solve file template %s\n", szTemplate));
6581 return GF_OUT_OF_MEM;
6582 }
6583
6584 strcat(szFinalName, szTemplateVal);
6585 k = (u32) strlen(szFinalName);
6586
6587 if (!sep) break;
6588 sep[0] = '$';
6589 name = sep+1;
6590 }
6591 szFinalName[k] = 0;
6592 return GF_OK;
6593 }
6594
6595
6596 GF_EXPORT
gf_filter_pid_set_discard(GF_FilterPid * pid,Bool discard_on)6597 GF_Err gf_filter_pid_set_discard(GF_FilterPid *pid, Bool discard_on)
6598 {
6599 GF_FilterPidInst *pidi = (GF_FilterPidInst *) pid;
6600 if (PID_IS_OUTPUT(pid)) {
6601 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt at discarding packets on output PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6602 return GF_BAD_PARAM;
6603 }
6604 if (discard_on) {
6605 GF_LOG(GF_LOG_INFO, GF_LOG_FILTER, ("Discarding packets on PID %s (filter %s to %s)\n", pid->pid->name, pid->pid->filter->name, pid->filter->name));
6606 while (gf_filter_pid_get_packet(pid)) {
6607 gf_filter_pid_drop_packet(pid);
6608 }
6609 pidi->is_end_of_stream = GF_TRUE;
6610 } else {
6611 //no more packets in queue or postponed, we can trust the EOS signal on the PID
6612 //otherwise even though the PID has seen the EOS, it is not yet processed by the pid instance, signaling it
6613 //would break up filters (for example dash demux) relying on precise EOS signals which must be toggled at the EOS packet
6614 //once all previous packets have been processed
6615 if (!gf_fq_count(pidi->packets) && !pid->pid->filter->postponed_packets)
6616 pidi->is_end_of_stream = pid->pid->has_seen_eos;
6617 }
6618 pidi->discard_inputs = discard_on;
6619 return GF_OK;
6620 }
6621
gf_filter_pid_get_dst_string(GF_FilterSession * sess,const char * _args,Bool is_dst)6622 static char *gf_filter_pid_get_dst_string(GF_FilterSession *sess, const char *_args, Bool is_dst)
6623 {
6624 char *target, *sep;
6625 char szKey[6];
6626 u32 len;
6627 if (!_args) return NULL;
6628
6629 if (is_dst)
6630 sprintf(szKey, "dst%c", sess->sep_name);
6631 else
6632 sprintf(szKey, "src%c", sess->sep_name);
6633
6634 target = strstr(_args, szKey);
6635 if (!target) return NULL;
6636
6637 sep = (char *) gf_fs_path_escape_colon(sess, target + 4);
6638 target += 4;
6639 if (sep) len = (u32) (sep - target);
6640 else len = (u32) strlen(target);
6641
6642 char *res = gf_malloc(sizeof(char)* (len+1));
6643 memcpy(res, target, sizeof(char)* len);
6644 res[len]=0;
6645 return res;
6646 }
6647
6648
6649 GF_EXPORT
gf_filter_pid_get_destination(GF_FilterPid * pid)6650 char *gf_filter_pid_get_destination(GF_FilterPid *pid)
6651 {
6652 const char *dst_args;
6653 char *res;
6654 u32 i, j;
6655 if (PID_IS_INPUT(pid)) {
6656 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to query destination on input PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6657 return NULL;
6658 }
6659
6660 dst_args = pid->filter->dst_args;
6661 if (!dst_args) dst_args = pid->filter->src_args;
6662 res = gf_filter_pid_get_dst_string(pid->filter->session, dst_args, GF_TRUE);
6663 if (res) return res;
6664
6665 //if not set this means we have explicetly loaded the filter
6666 for (i=0; i<pid->num_destinations; i++) {
6667 GF_FilterPidInst *pidi = gf_list_get(pid->destinations, i);
6668
6669 dst_args = pidi->filter->dst_args;
6670 if (!dst_args) dst_args = pidi->filter->src_args;
6671 res = gf_filter_pid_get_dst_string(pid->filter->session, dst_args, GF_TRUE);
6672 if (res) return res;
6673
6674 for (j=0; j<pidi->filter->num_output_pids; j++) {
6675 GF_FilterPid *a_pid = gf_list_get(pidi->filter->output_pids, j);
6676 char *dst = gf_filter_pid_get_destination(a_pid);
6677 if (dst) return dst;
6678 }
6679 }
6680 return NULL;
6681 }
6682
6683 GF_EXPORT
gf_filter_pid_get_source(GF_FilterPid * pid)6684 char *gf_filter_pid_get_source(GF_FilterPid *pid)
6685 {
6686 const char *src_args;
6687 char *res;
6688 // GF_FilterPidInst *pidi = (GF_FilterPidInst *) pid;
6689 u32 i;
6690 if (PID_IS_OUTPUT(pid)) {
6691 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to query source on output PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6692 return NULL;
6693 }
6694 pid = pid->pid;
6695
6696 src_args = pid->filter->src_args;
6697 if (!src_args) src_args = pid->filter->dst_args;
6698 res = gf_filter_pid_get_dst_string(pid->filter->session, src_args, GF_FALSE);
6699 if (res) return res;
6700
6701 //if not set this means we have explicetly loaded the filter
6702 for (i=0; i<pid->filter->num_input_pids; i++) {
6703 GF_FilterPidInst *pidi = gf_list_get(pid->filter->input_pids, i);
6704
6705 src_args = pidi->pid->filter->src_args;
6706 if (!src_args) src_args = pidi->pid->filter->dst_args;
6707 res = gf_filter_pid_get_dst_string(pid->filter->session, src_args, GF_FALSE);
6708 if (res) return res;
6709 }
6710 return NULL;
6711 }
6712
6713 GF_EXPORT
gf_filter_pid_discard_block(GF_FilterPid * pid)6714 void gf_filter_pid_discard_block(GF_FilterPid *pid)
6715 {
6716 if (PID_IS_INPUT(pid)) {
6717 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to reset block mode on input PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6718 return;
6719 }
6720 if (!pid->has_seen_eos) {
6721 GF_LOG(GF_LOG_WARNING, GF_LOG_FILTER, ("Attempt to reset block mode on PID %s in filter %s not in end of stream, ignoring\n", pid->pid->name, pid->filter->name));
6722 return;
6723 }
6724 gf_mx_p(pid->filter->tasks_mx);
6725 if (pid->would_block) {
6726 safe_int_dec(&pid->would_block);
6727 assert(pid->filter->would_block);
6728 safe_int_dec(&pid->filter->would_block);
6729 }
6730 gf_mx_v(pid->filter->tasks_mx);
6731 }
6732
6733 GF_EXPORT
gf_filter_pid_require_source_id(GF_FilterPid * pid)6734 GF_Err gf_filter_pid_require_source_id(GF_FilterPid *pid)
6735 {
6736 if (PID_IS_INPUT(pid)) {
6737 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to set require_source_id input PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6738 return GF_BAD_PARAM;
6739 }
6740 pid->require_source_id = GF_TRUE;
6741 return GF_OK;
6742 }
6743
6744 GF_EXPORT
gf_filter_pid_get_min_pck_duration(GF_FilterPid * pid)6745 u32 gf_filter_pid_get_min_pck_duration(GF_FilterPid *pid)
6746 {
6747 if (PID_IS_OUTPUT(pid)) {
6748 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to query min_pck_duration on output pid PID %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6749 return 0;
6750 }
6751 return pid->pid->min_pck_duration;
6752 }
6753
6754 GF_EXPORT
gf_filter_pid_recompute_dts(GF_FilterPid * pid,Bool do_recompute)6755 void gf_filter_pid_recompute_dts(GF_FilterPid *pid, Bool do_recompute)
6756 {
6757 if (PID_IS_INPUT(pid)) {
6758 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to set recompute_dts on input pid %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6759 return;
6760 }
6761 pid->recompute_dts = do_recompute;
6762 }
6763
6764 GF_EXPORT
gf_filter_pid_is_playing(GF_FilterPid * pid)6765 Bool gf_filter_pid_is_playing(GF_FilterPid *pid)
6766 {
6767 if (!pid) return GF_FALSE;
6768 return pid->pid->is_playing;
6769
6770 }
6771
6772 GF_EXPORT
gf_filter_pid_allow_direct_dispatch(GF_FilterPid * pid)6773 GF_Err gf_filter_pid_allow_direct_dispatch(GF_FilterPid *pid)
6774 {
6775 if (PID_IS_INPUT(pid)) {
6776 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to set direct dispatch mode on input pid %s in filter %s not allowed\n", pid->pid->name, pid->filter->name));
6777 return GF_BAD_PARAM;
6778 }
6779 if (pid->filter->session->threads)
6780 return GF_OK;
6781 pid->direct_dispatch = GF_TRUE;
6782 return GF_OK;
6783 }
6784
6785 GF_EXPORT
gf_filter_pid_get_alias_udta(GF_FilterPid * _pid)6786 void *gf_filter_pid_get_alias_udta(GF_FilterPid *_pid)
6787 {
6788 GF_FilterPidInst *pidi;
6789 if (PID_IS_OUTPUT(_pid)) {
6790 GF_LOG(GF_LOG_ERROR, GF_LOG_FILTER, ("Attempt to query multi_sink original filter context on output pid %s in filter %s not allowed\n", _pid->pid->name, _pid->filter->name));
6791 return NULL;
6792 }
6793 pidi = (GF_FilterPidInst *) _pid;
6794 if (!pidi->alias_orig) return NULL;
6795 return pidi->alias_orig->filter_udta;
6796 }
6797