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 = &reg_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 = &reg_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 = &reg_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 = &reg_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 = &reg_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 = &current_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