1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2000-2020
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / Scene Compositor sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms 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 <gpac/internal/compositor_dev.h>
27 #include <gpac/options.h>
28 #include <gpac/utf.h>
29 #include <gpac/modules/hardcoded_proto.h>
30 #include <gpac/modules/compositor_ext.h>
31 
32 #include "nodes_stacks.h"
33 
34 #include "visual_manager.h"
35 #include "texturing.h"
36 
37 static void gf_sc_recompute_ar(GF_Compositor *compositor, GF_Node *top_node);
38 
39 #define SC_DEF_WIDTH	320
40 #define SC_DEF_HEIGHT	240
41 
42 
43 GF_EXPORT
gf_sc_send_event(GF_Compositor * compositor,GF_Event * evt)44 Bool gf_sc_send_event(GF_Compositor *compositor, GF_Event *evt)
45 {
46 	return gf_filter_forward_gf_event(compositor->filter, evt, GF_FALSE, GF_FALSE);
47 }
48 
49 
gf_sc_next_frame_state(GF_Compositor * compositor,u32 state)50 void gf_sc_next_frame_state(GF_Compositor *compositor, u32 state)
51 {
52 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Forcing frame redraw state: %d\n", state));
53 	if (state==GF_SC_DRAW_FLUSH) {
54 		if (!compositor->skip_flush)
55 			compositor->skip_flush = 2;
56 
57 		//if in openGL mode ignore refresh events (content of the window is still OK). This is only used for overlays in 2d
58 		if (!compositor->frame_draw_type
59 #ifndef GPAC_DISABLE_3D
60 		        && !compositor->visual->type_3d && !compositor->hybrid_opengl
61 #endif
62 		   ) {
63 			compositor->frame_draw_type = state;
64 		}
65 	} else {
66 		compositor->frame_draw_type = state;
67 	}
68 }
69 
70 
gf_sc_set_fullscreen(GF_Compositor * compositor)71 static void gf_sc_set_fullscreen(GF_Compositor *compositor)
72 {
73 	GF_Err e;
74 	if (!compositor->video_out->SetFullScreen) return;
75 
76 	GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Switching fullscreen %s\n", compositor->fullscreen ? "off" : "on"));
77 	/*move to FS*/
78 	compositor->fullscreen = !compositor->fullscreen;
79 
80 	//gf_sc_ar_control(compositor->audio_renderer, GF_SC_AR_PAUSE);
81 
82 	//in windows (and other?) we may get blocked by SetWindowPos in the fullscreen method until another window thread dispatches a resize event,
83 	//which would try to grab the compositor mutex and thus deadlock us
84 	//to avoid this, unlock the compositor just for the SetFullscreen
85 	gf_mx_v(compositor->mx);
86 	if (compositor->fullscreen && (compositor->scene_width>=compositor->scene_height)
87 #ifndef GPAC_DISABLE_3D
88 	        && !compositor->visual->type_3d
89 #endif
90 	   ) {
91 		e = compositor->video_out->SetFullScreen(compositor->video_out, 2, &compositor->display_width, &compositor->display_height);
92 	} else {
93 		e = compositor->video_out->SetFullScreen(compositor->video_out, compositor->fullscreen, &compositor->display_width, &compositor->display_height);
94 	}
95 	gf_mx_p(compositor->mx);
96 
97 	//gf_sc_ar_control(compositor->audio_renderer, GF_SC_AR_RESUME);
98 
99 	if (e) {
100 		GF_Event evt;
101 		memset(&evt, 0, sizeof(GF_Event));
102 		evt.type = GF_EVENT_MESSAGE;
103 		evt.message.message = "Cannot switch to fullscreen";
104 		evt.message.error = e;
105 		gf_sc_send_event(compositor, &evt);
106 		compositor->fullscreen = 0;
107 		compositor->video_out->SetFullScreen(compositor->video_out, 0, &compositor->display_width, &compositor->display_height);
108 	}
109 	GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] recomputing aspect ratio\n"));
110 	compositor->recompute_ar = 1;
111 	/*force signaling graphics reset*/
112 	if (!compositor->reset_graphics) gf_sc_reset_graphics(compositor);
113 }
114 
115 
116 /*this is needed for:
117 - audio: since the audio compositor may not be threaded, it must be reconfigured by another thread otherwise
118 we lock the audio module
119 - video: this is typical to OpenGL&co: multithreaded is forbidden, so resizing/fullscreen MUST be done by the same
120 thread accessing the HW ressources
121 */
gf_sc_reconfig_task(GF_Compositor * compositor)122 static void gf_sc_reconfig_task(GF_Compositor *compositor)
123 {
124 	GF_Event evt;
125 	Bool notif_size=GF_FALSE;
126 	u32 width,height;
127 
128 	/*reconfig audio if needed (non-threaded compositors)*/
129 	if (compositor->audio_renderer
130 #ifdef ENABLE_AOUT
131 	 	&& !compositor->audio_renderer->th
132 #endif
133 	)
134 	if (compositor->msg_type) {
135 
136 		compositor->msg_type |= GF_SR_IN_RECONFIG;
137 
138 		if (compositor->msg_type & GF_SR_CFG_INITIAL_RESIZE) {
139 			memset(&evt, 0, sizeof(GF_Event));
140 			evt.type = GF_EVENT_VIDEO_SETUP;
141 			evt.setup.width = compositor->new_width;
142 			evt.setup.height = compositor->new_height;
143 			evt.setup.system_memory = compositor->video_memory ? GF_FALSE : GF_TRUE;
144 			evt.setup.disable_vsync = compositor->bench_mode ? GF_TRUE : GF_FALSE;
145 
146 #ifndef GPAC_DISABLE_3D
147 			if (compositor->hybrid_opengl || compositor->force_opengl_2d) {
148 				evt.setup.use_opengl = GF_TRUE;
149 				evt.setup.system_memory = GF_FALSE;
150 				evt.setup.back_buffer = GF_TRUE;
151 			}
152 
153 #endif
154 			compositor->video_out->ProcessEvent(compositor->video_out, &evt);
155 
156 			if (evt.setup.use_opengl) {
157 				gf_opengl_init();
158 			}
159 
160 			compositor->msg_type &= ~GF_SR_CFG_INITIAL_RESIZE;
161 		}
162 		/*scene size has been overriden*/
163 		if (compositor->msg_type & GF_SR_CFG_OVERRIDE_SIZE) {
164 			assert(!(compositor->override_size_flags & 2));
165 			compositor->msg_type &= ~GF_SR_CFG_OVERRIDE_SIZE;
166 			compositor->override_size_flags |= 2;
167 			width = compositor->scene_width;
168 			height = compositor->scene_height;
169 			compositor->has_size_info = 1;
170 			gf_sc_set_size(compositor, width, height);
171 
172 			evt.type = GF_EVENT_SIZE;
173 			evt.size.width = width;
174 			evt.size.height = height;
175 			gf_sc_send_event(compositor, &evt);
176 		}
177 		/*size changed from scene cfg: resize window first*/
178 		if (compositor->msg_type & GF_SR_CFG_SET_SIZE) {
179 			u32 fs_width, fs_height;
180 			Bool restore_fs = compositor->fullscreen;
181 
182 			GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Changing display size to %d x %d\n", compositor->new_width, compositor->new_height));
183 			fs_width = fs_height = 0;
184 			if (restore_fs) {
185 #if defined(GPAC_CONFIG_ANDROID) || defined(GPAC_CONFIG_IOS)
186 				if ((compositor->new_width>compositor->display_width) || (compositor->new_height>compositor->display_height)) {
187 					u32 w = compositor->display_width;
188 					compositor->display_width = compositor->display_height;
189 					compositor->display_height = w;
190 					compositor->recompute_ar = 1;
191 				}
192 #endif
193 				fs_width = compositor->display_width;
194 				fs_height = compositor->display_height;
195 			}
196 			evt.type = GF_EVENT_SIZE;
197 			evt.size.width = compositor->new_width;
198 			evt.size.height = compositor->new_height;
199 
200 			/*send resize event*/
201 			if ( !(compositor->msg_type & GF_SR_CFG_WINDOWSIZE_NOTIF)) {
202 				compositor->video_out->ProcessEvent(compositor->video_out, &evt);
203 			}
204 
205 			compositor->msg_type &= ~GF_SR_CFG_WINDOWSIZE_NOTIF;
206 
207 			if (restore_fs) {
208 				if ((compositor->display_width != fs_width) || (compositor->display_height != fs_height)) {
209 					compositor->display_width = fs_width;
210 					compositor->display_height = fs_height;
211 					compositor->recompute_ar = 1;
212 				}
213 			} else {
214 				compositor->display_width = evt.size.width;
215 				compositor->display_height = evt.size.height;
216 				compositor->recompute_ar = 1;
217 				gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
218 			}
219 			notif_size=1;
220 			compositor->new_width = compositor->new_height = 0;
221 			compositor->msg_type &= ~GF_SR_CFG_SET_SIZE;
222 			GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Display size changed to %d x %d\n", compositor->new_width, compositor->new_height));
223 		}
224 		/*aspect ratio modif*/
225 		if (compositor->msg_type & GF_SR_CFG_AR) {
226 			compositor->msg_type &= ~GF_SR_CFG_AR;
227 			compositor->recompute_ar = 1;
228 		}
229 		/*fullscreen on/off request*/
230 		if (compositor->msg_type & GF_SR_CFG_FULLSCREEN) {
231 			compositor->msg_type &= ~GF_SR_CFG_FULLSCREEN;
232 			//video is about to resetup, wait for the setup
233 			if (compositor->recompute_ar) {
234 				compositor->fullscreen_postponed = 1;
235 			} else {
236 				gf_sc_set_fullscreen(compositor);
237 				gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
238 				notif_size=1;
239 			}
240 		}
241 		compositor->msg_type &= ~GF_SR_IN_RECONFIG;
242 	}
243 	if (notif_size) {
244 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_WIDTH, &PROP_UINT(compositor->display_width));
245 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_HEIGHT, &PROP_UINT(compositor->display_height));
246 	}
247 
248 	/*3D driver changed message, recheck extensions*/
249 	if (compositor->reset_graphics) {
250 #ifndef GPAC_DISABLE_3D
251 		compositor->offscreen_width = compositor->offscreen_height = 0;
252 #endif
253 		gf_sc_lock(compositor, 0);
254 		evt.type = GF_EVENT_SYS_COLORS;
255 		if (compositor->video_out->ProcessEvent(compositor->video_out, &evt) ) {
256 			u32 i;
257 			for (i=0; i<28; i++) {
258 				compositor->sys_colors[i] = evt.sys_cols.sys_colors[i] & 0x00FFFFFF;
259 			}
260 		}
261 		gf_sc_lock(compositor, 1);
262 	}
263 }
264 
265 
gf_sc_frame_ifce_done(GF_Filter * filter,GF_FilterPid * pid,GF_FilterPacket * pck)266 static void gf_sc_frame_ifce_done(GF_Filter *filter, GF_FilterPid *pid, GF_FilterPacket *pck)
267 {
268 	GF_FilterFrameInterface *frame_ifce = gf_filter_pck_get_frame_interface(pck);
269 	GF_Compositor *compositor = gf_filter_get_udta(filter);
270 	if (frame_ifce) {
271 		compositor->frame_ifce.user_data = NULL;
272 		if (compositor->fb.video_buffer) {
273 			gf_sc_release_screen_buffer(compositor, &compositor->fb);
274 			compositor->fb.video_buffer = NULL;
275 		}
276 	}
277 	compositor->flush_pending = (compositor->skip_flush!=1) ? GF_TRUE : GF_FALSE;
278 	compositor->skip_flush = 0;
279 }
280 
gf_sc_frame_ifce_get_plane(GF_FilterFrameInterface * frame_ifce,u32 plane_idx,const u8 ** outPlane,u32 * outStride)281 GF_Err gf_sc_frame_ifce_get_plane(GF_FilterFrameInterface *frame_ifce, u32 plane_idx, const u8 **outPlane, u32 *outStride)
282 {
283 	GF_Err e = GF_BAD_PARAM;
284 	GF_Compositor *compositor = frame_ifce->user_data;
285 
286 	if (plane_idx==0) {
287 		e = GF_OK;
288 		if (!compositor->fb.video_buffer)
289 			e = gf_sc_get_screen_buffer(compositor, &compositor->fb, 0);
290 	}
291 	*outPlane = compositor->fb.video_buffer;
292 	*outStride = compositor->fb.pitch_y;
293 	return e;
294 }
295 #ifndef GPAC_DISABLE_3D
gf_sc_frame_ifce_get_gl_texture(GF_FilterFrameInterface * frame_ifce,u32 plane_idx,u32 * gl_tex_format,u32 * gl_tex_id,GF_Matrix_unexposed * texcoordmatrix)296 GF_Err gf_sc_frame_ifce_get_gl_texture(GF_FilterFrameInterface *frame_ifce, u32 plane_idx, u32 *gl_tex_format, u32 *gl_tex_id, GF_Matrix_unexposed * texcoordmatrix)
297 {
298 	GF_Compositor *compositor = frame_ifce->user_data;
299 	if (!compositor->fbo_tx_id) return GF_BAD_PARAM;
300 	if (plane_idx) return GF_BAD_PARAM;
301 	if (gl_tex_id) *gl_tex_id = compositor->fbo_tx_id;
302 	if (gl_tex_format) *gl_tex_format = compositor_3d_get_fbo_pixfmt();
303 	//framebuffer is already oriented as a GL texture not as an image
304 	if (texcoordmatrix)
305 		gf_mx_add_scale(texcoordmatrix, FIX_ONE, -FIX_ONE, FIX_ONE);
306 	return GF_OK;
307 }
308 #endif
309 
gf_sc_flush_video(GF_Compositor * compositor,Bool locked)310 static void gf_sc_flush_video(GF_Compositor *compositor, Bool locked)
311 {
312 	GF_Window rc;
313 
314 	//release compositor in case we have vsync
315 	if (locked) gf_sc_lock(compositor, 0);
316 	rc.x = rc.y = 0;
317 	rc.w = compositor->display_width;
318 	rc.h = compositor->display_height;
319 	compositor->video_out->Flush(compositor->video_out, &rc);
320 	compositor->flush_pending = GF_FALSE;
321 	if (locked) gf_sc_lock(compositor, 1);
322 }
323 
324 void gf_sc_render_frame(GF_Compositor *compositor);
325 
326 GF_EXPORT
gf_sc_draw_frame(GF_Compositor * compositor,Bool no_flush,s32 * ms_till_next)327 Bool gf_sc_draw_frame(GF_Compositor *compositor, Bool no_flush, s32 *ms_till_next)
328 {
329 	Bool ret = GF_FALSE;
330 
331 	gf_sc_ar_send_or_reconfig(compositor->audio_renderer);
332 
333 	//frame still pending
334 	if (compositor->frame_ifce.user_data)
335 		return GF_TRUE;
336 
337 	if (compositor->flush_pending) {
338 		gf_sc_flush_video(compositor, GF_FALSE);
339 	}
340 	compositor->skip_flush = no_flush ? 1 : 0;
341 
342 	gf_sc_render_frame(compositor);
343 
344 	if (ms_till_next) {
345 		if ( compositor->ms_until_next_frame == GF_INT_MAX)
346 			*ms_till_next = compositor->frame_duration;
347 		else
348 			*ms_till_next = compositor->ms_until_next_frame;
349 	}
350 	//next frame is late, we should redraw
351 	if (compositor->ms_until_next_frame < 0) ret = GF_TRUE;
352 	else if (compositor->frame_draw_type) ret = GF_TRUE;
353 	else if (compositor->fonts_pending>0) ret = GF_TRUE;
354 
355 	return ret;
356 }
357 
358 
359 /*forces graphics redraw*/
360 GF_EXPORT
gf_sc_reset_graphics(GF_Compositor * compositor)361 void gf_sc_reset_graphics(GF_Compositor *compositor)
362 {
363 	if (compositor) {
364 		Bool locked = gf_mx_try_lock(compositor->mx);
365 		compositor->reset_graphics = 1;
366 		if (locked) gf_mx_v(compositor->mx);
367 	}
368 }
369 
gf_sc_reset_framerate(GF_Compositor * compositor)370 static void gf_sc_reset_framerate(GF_Compositor *compositor)
371 {
372 	u32 i;
373 	for (i=0; i<GF_SR_FPS_COMPUTE_SIZE; i++) compositor->frame_time[i] = compositor->frame_dur[i] = 0;
374 	compositor->current_frame = 0;
375 }
376 
377 
378 enum
379 {
380 	GF_COMPOSITOR_THREAD_START = 0,
381 	GF_COMPOSITOR_THREAD_RUN,
382 	GF_COMPOSITOR_THREAD_ABORTING,
383 	GF_COMPOSITOR_THREAD_DONE,
384 	GF_COMPOSITOR_THREAD_INIT_FAILED,
385 };
386 
nullvout_setup(struct _video_out * vout,void * os_handle,void * os_display,u32 init_flags)387 static GF_Err nullvout_setup(struct _video_out *vout, void *os_handle, void *os_display, u32 init_flags)
388 {
389 	return GF_OK;
390 }
nullvout_shutdown(struct _video_out * vout)391 static void nullvout_shutdown(struct _video_out *vout)
392 {
393 	return;
394 }
nullvout_flush(struct _video_out * vout,GF_Window * dest)395 static GF_Err nullvout_flush(struct _video_out *vout, GF_Window *dest)
396 {
397 	return GF_OK;
398 }
nullvout_fullscreen(struct _video_out * vout,Bool fs_on,u32 * new_disp_width,u32 * new_disp_height)399 static GF_Err nullvout_fullscreen(struct _video_out *vout, Bool fs_on, u32 *new_disp_width, u32 *new_disp_height)
400 {
401 	return GF_OK;
402 }
nullvout_evt(struct _video_out * vout,GF_Event * event)403 static GF_Err nullvout_evt(struct _video_out *vout, GF_Event *event)
404 {
405 	return GF_OK;
406 }
nullvout_lock(struct _video_out * vout,GF_VideoSurface * video_info,Bool do_lock)407 static GF_Err nullvout_lock(struct _video_out *vout, GF_VideoSurface *video_info, Bool do_lock)
408 {
409 	return GF_IO_ERR;
410 }
nullvout_blit(struct _video_out * vout,GF_VideoSurface * video_src,GF_Window * src_wnd,GF_Window * dst_wnd,u32 overlay_type)411 static GF_Err nullvout_blit(struct _video_out *vout, GF_VideoSurface *video_src, GF_Window *src_wnd, GF_Window *dst_wnd, u32 overlay_type)
412 {
413 	return GF_OK;
414 }
415 
416 GF_VideoOutput null_vout = {
417 	.Setup = nullvout_setup,
418 	.Shutdown = nullvout_shutdown,
419 	.Flush = nullvout_flush,
420 	.SetFullScreen = nullvout_fullscreen,
421 	.ProcessEvent = nullvout_evt,
422 	.LockBackBuffer = nullvout_lock,
423 	.Blit = nullvout_blit,
424 	.hw_caps = GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_RGBA | GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY | GF_VIDEO_HW_HAS_STRETCH
425 };
426 
gl_vout_evt(struct _video_out * vout,GF_Event * evt)427 static GF_Err gl_vout_evt(struct _video_out *vout, GF_Event *evt)
428 {
429 	GF_Compositor *compositor = (GF_Compositor *)vout->opaque;
430 	if (!evt || (evt->type != GF_EVENT_VIDEO_SETUP)) return GF_OK;
431 
432 	if (!compositor->player && (compositor->passthrough_pfmt != GF_PIXEL_RGB)) {
433 		u32 pfmt = compositor->dyn_filter_mode ? GF_PIXEL_RGBA : GF_PIXEL_RGB;
434 		compositor->passthrough_pfmt = pfmt;
435 		compositor->opfmt = pfmt;
436 		if (compositor->vout) {
437 			u32 stride = compositor->output_width * ( (pfmt == GF_PIXEL_RGBA) ? 4 : 3 );
438 			gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_PIXFMT, &PROP_UINT(pfmt));
439 			gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_STRIDE, &PROP_UINT(stride));
440 		}
441 	}
442 
443 
444 #ifndef GPAC_DISABLE_3D
445 	return compositor_3d_setup_fbo(evt->setup.width, evt->setup.height, &compositor->fbo_id, &compositor->fbo_tx_id, &compositor->fbo_depth_id);
446 #else
447 	return GF_NOT_SUPPORTED;
448 #endif
449 }
450 
451 GF_VideoOutput gl_vout = {
452 	.Setup = nullvout_setup,
453 	.Shutdown = nullvout_shutdown,
454 	.Flush = nullvout_flush,
455 	.SetFullScreen = nullvout_fullscreen,
456 	.ProcessEvent = gl_vout_evt,
457 	.LockBackBuffer = NULL,
458 	.Blit = NULL,
459 	.hw_caps = GF_VIDEO_HW_OPENGL
460 };
461 
rawvout_lock(struct _video_out * vout,GF_VideoSurface * vi,Bool do_lock)462 static GF_Err rawvout_lock(struct _video_out *vout, GF_VideoSurface *vi, Bool do_lock)
463 {
464 	GF_Compositor *compositor = (GF_Compositor *)vout->opaque;
465 
466 	if (do_lock) {
467 		u32 pfmt;
468 		if (!vi) return GF_BAD_PARAM;
469 		pfmt = compositor->opfmt;
470 		if (!pfmt && compositor->passthrough_txh) pfmt = compositor->passthrough_txh->pixelformat;
471 
472 		if (!pfmt) {
473 			pfmt = compositor->dyn_filter_mode ? GF_PIXEL_RGBA : GF_PIXEL_RGB;
474 		}
475 
476 		memset(vi, 0, sizeof(GF_VideoSurface));
477 		vi->width = compositor->display_width;
478 		vi->height = compositor->display_height;
479 		gf_pixel_get_size_info(pfmt, compositor->display_width, compositor->display_height, NULL, &vi->pitch_y, NULL, NULL, NULL);
480 		if (compositor->passthrough_txh && !compositor->passthrough_txh->frame_ifce && (pfmt == compositor->passthrough_txh->pixelformat)) {
481 			if (!compositor->passthrough_pck)
482 				compositor->passthrough_pck = gf_filter_pck_new_clone(compositor->vout, compositor->passthrough_txh->stream->pck, &compositor->passthrough_data);
483 
484 			vi->video_buffer = compositor->passthrough_data;
485 			vi->pitch_y = compositor->passthrough_txh->stride;
486 		} else {
487 			vi->video_buffer = compositor->framebuffer;
488 		}
489 		vi->pitch_x = gf_pixel_get_bytes_per_pixel(pfmt);
490 		vi->pixel_format = pfmt;
491 		compositor->passthrough_pfmt = pfmt;
492 	}
493 	return GF_OK;
494 }
495 
rawvout_evt(struct _video_out * vout,GF_Event * evt)496 static GF_Err rawvout_evt(struct _video_out *vout, GF_Event *evt)
497 {
498 	u32 pfmt, stride, stride_uv;
499 	GF_Compositor *compositor = (GF_Compositor *)vout->opaque;
500 	if (!evt || (evt->type != GF_EVENT_VIDEO_SETUP)) return GF_OK;
501 
502 	pfmt = compositor->opfmt;
503 	if (!pfmt) {
504 		pfmt = compositor->dyn_filter_mode ? GF_PIXEL_RGBA : GF_PIXEL_RGB;
505 	}
506 
507 	compositor->passthrough_pfmt = pfmt;
508 	stride=0;
509 	stride_uv = 0;
510 	gf_pixel_get_size_info(pfmt, evt->setup.width, evt->setup.height, &compositor->framebuffer_size, &stride, &stride_uv, NULL, NULL);
511 
512 	if (compositor->vout) {
513 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_PIXFMT, &PROP_UINT(pfmt));
514 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_STRIDE, &PROP_UINT(stride));
515 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_STRIDE_UV, stride_uv ? &PROP_UINT(stride_uv) : NULL);
516 	}
517 
518 	if (compositor->framebuffer_size > compositor->framebuffer_alloc) {
519 		compositor->framebuffer_alloc = compositor->framebuffer_size;
520 		compositor->framebuffer = gf_realloc(compositor->framebuffer, sizeof(char)*compositor->framebuffer_size);
521 	}
522 	if (!compositor->framebuffer) return GF_OUT_OF_MEM;
523 	memset(compositor->framebuffer, 0, sizeof(char)*compositor->framebuffer_size);
524 
525 #ifndef GPAC_DISABLE_3D
526 	if (compositor->needs_offscreen_gl && (compositor->ogl != GF_SC_GLMODE_OFF))
527 		return compositor_3d_setup_fbo(evt->setup.width, evt->setup.height, &compositor->fbo_id, &compositor->fbo_tx_id, &compositor->fbo_depth_id);
528 #endif
529 
530 	evt->setup.use_opengl = GF_FALSE;
531 	return GF_OK;
532 }
533 
534 GF_VideoOutput raw_vout = {
535 	.Setup = nullvout_setup,
536 	.Shutdown = nullvout_shutdown,
537 	.Flush = nullvout_flush,
538 	.SetFullScreen = nullvout_fullscreen,
539 	.ProcessEvent = rawvout_evt,
540 	.LockBackBuffer = rawvout_lock,
541 	.Blit = NULL,
542 	.hw_caps = GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_RGBA
543 };
544 
gf_sc_setup_passthrough(GF_Compositor * compositor)545 void gf_sc_setup_passthrough(GF_Compositor *compositor)
546 {
547 	u32 timescale;
548 	Bool is_raw_out = GF_FALSE;
549 	u32 update_pfmt = 0;
550 	compositor->passthrough_inplace = GF_FALSE;
551 
552 	if (!compositor->passthrough_txh) {
553 		compositor->passthrough_timescale = 0;
554 		return;
555 	}
556 
557 	timescale = gf_filter_pck_get_timescale(compositor->passthrough_txh->stream->pck);
558 	if (compositor->passthrough_timescale != timescale) {
559 
560 		compositor->passthrough_timescale = timescale;
561 		gf_filter_pid_copy_properties(compositor->vout, compositor->passthrough_txh->stream->odm->pid);
562 		if  (compositor->video_out==&raw_vout) {
563 			update_pfmt = compositor->opfmt ? compositor->opfmt :  compositor->passthrough_txh->pixelformat;
564 		} else {
565 			update_pfmt = GF_PIXEL_RGB;
566 		}
567 	}
568 	if (compositor->video_out == &raw_vout) {
569 		is_raw_out = GF_TRUE;
570 		if (!compositor->opfmt || (compositor->opfmt == compositor->passthrough_txh->pixelformat) ) {
571 			if (!compositor->passthrough_txh->frame_ifce) {
572 				compositor->passthrough_inplace = GF_TRUE;
573 				update_pfmt = GF_FALSE;
574 			}
575 			if (!compositor->opfmt && (compositor->passthrough_pfmt != compositor->passthrough_txh->pixelformat)) {
576 				compositor->passthrough_pfmt = compositor->passthrough_txh->pixelformat;
577 				gf_filter_pid_copy_properties(compositor->vout, compositor->passthrough_txh->stream->odm->pid);
578 			}
579 		}
580 	}
581 
582 	if (update_pfmt) {
583 		u32 stride=0, stride_uv=0, out_size=0;
584 		gf_pixel_get_size_info(update_pfmt, compositor->display_width, compositor->display_width, &out_size, &stride, &stride_uv, NULL, NULL);
585 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_PIXFMT, &PROP_UINT(update_pfmt));
586 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_STRIDE, &PROP_UINT(stride));
587 		gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_STRIDE_UV, stride_uv ? &PROP_UINT(stride_uv) : NULL);
588 
589 		if (is_raw_out && !compositor->passthrough_inplace && (compositor->framebuffer_size != out_size) ) {
590 			compositor->framebuffer_size = out_size;
591 			if (out_size > compositor->framebuffer_alloc) {
592 				compositor->framebuffer_alloc = out_size;
593 				compositor->framebuffer = gf_realloc(compositor->framebuffer, out_size);
594 			}
595 		}
596 	}
597 }
598 
gf_sc_load_driver(GF_Compositor * compositor)599 static GF_Err gf_sc_load_driver(GF_Compositor *compositor)
600 {
601 	GF_Err e;
602 	const char *sOpt;
603 	void *os_disp=NULL;
604 
605 	//filter mode
606 	if (!compositor->player) {
607 		compositor->video_out = &null_vout;
608 
609 #ifdef GPAC_ENABLE_COVERAGE
610 		if (gf_sys_is_cov_mode()) {
611 			nullvout_setup(NULL, NULL, NULL, 0);
612 			nullvout_fullscreen(NULL, GF_FALSE, NULL, NULL);
613 			nullvout_evt(NULL, NULL);
614 			nullvout_lock(NULL, NULL, GF_FALSE);
615 			nullvout_blit(NULL, NULL, NULL, NULL, 0);
616 		}
617 #endif
618 
619 		e = gf_filter_request_opengl(compositor->filter);
620 		if (e) return e;
621 #ifndef GPAC_DISABLE_3D
622 		gf_sc_load_opengl_extensions(compositor, GF_TRUE);
623 		if (!compositor->gl_caps.fbo)
624 #endif
625 		{
626 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Compositor] No support for OpenGL framebuffer object, cannot run in GL mode.\n"));
627 			compositor->drv = GF_SC_DRV_OFF;
628 			return GF_NOT_SUPPORTED;
629 		}
630 		compositor->video_out = &gl_vout;
631 		compositor->video_out->opaque = compositor;
632 		return GF_OK;
633 	}
634 
635 	//player mode
636 #ifndef GPAC_DISABLE_3D
637 	sOpt = gf_opts_get_key("core", "video-output");
638 	if (sOpt && !strcmp(sOpt, "glfbo")) {
639 		compositor->video_out = &gl_vout;
640 		compositor->video_out->opaque = compositor;
641 		sOpt = gf_opts_get_key("core", "glfbo-txid");
642 		if (sOpt) {
643 		 	compositor->fbo_tx_id = atoi(sOpt);
644 		 	compositor->external_tx_id = GF_TRUE;
645 		}
646 		if (!compositor->fbo_tx_id) {
647 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Compositor] glfbo driver specified but no target texture ID found, cannot initialize\n"));
648 			compositor->drv = GF_SC_DRV_OFF;
649 			return GF_BAD_PARAM;
650 		}
651 		gf_sc_load_opengl_extensions(compositor, GF_TRUE);
652 		if (!compositor->gl_caps.fbo) {
653 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Compositor] No support for OpenGL framebuffer object, cannot run in glfbo mode.\n"));
654 			compositor->drv = GF_SC_DRV_OFF;
655 			return GF_NOT_SUPPORTED;
656 		}
657 		return GF_OK;
658 	}
659 #endif
660 
661 	compositor->video_out = (GF_VideoOutput *) gf_module_load(GF_VIDEO_OUTPUT_INTERFACE, NULL);
662 
663 	if (!compositor->video_out ) {
664 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Compositor] Failed to load a video output module.\n"));
665 		return GF_IO_ERR;
666 	}
667 
668 	sOpt = gf_opts_get_key("Temp", "OSDisp");
669 	if (sOpt) sscanf(sOpt, "%p", &os_disp);
670 
671 	compositor->video_out->evt_cbk_hdl = compositor;
672 	compositor->video_out->on_event = gf_sc_on_event;
673 	/*init hw*/
674 	e = compositor->video_out->Setup(compositor->video_out, compositor->os_wnd, os_disp, compositor->init_flags);
675 	if (e != GF_OK) {
676 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Compositor] Error setuing up video output: %s\n", gf_error_to_string(e) ));
677 		return e;
678 	}
679 	if (!gf_opts_get_key("core", "video-output")) {
680 		gf_opts_set_key("core", "video-output", compositor->video_out->module_name);
681 	}
682 	gf_filter_register_opengl_provider(compositor->filter, GF_TRUE);
683 	return GF_OK;
684 }
685 
686 #ifndef GPAC_DISABLE_3D
gf_sc_check_video_driver(GF_Compositor * compositor)687 static void gf_sc_check_video_driver(GF_Compositor *compositor)
688 {
689 	Bool force_gl_out = GF_FALSE;
690 	if (compositor->player) return;
691 	if (compositor->video_out == &null_vout) return;
692 
693 	if (compositor->visual->type_3d || compositor->hybrid_opengl) {
694 		force_gl_out = GF_TRUE;
695 	} else if (compositor->needs_offscreen_gl && (compositor->ogl!=GF_SC_GLMODE_OFF) ) {
696 		force_gl_out = GF_TRUE;
697 	}
698 
699 	if (force_gl_out) {
700 		GF_Err e;
701 		if (compositor->video_out != &raw_vout) return;
702 		e = gf_sc_load_driver(compositor);
703 		if (e) {
704 			compositor->video_out = &raw_vout;
705 			compositor->video_memory = 2;
706 		}
707 		return;
708 	}
709 
710 	if (compositor->video_out == &raw_vout) {
711 		if (compositor->needs_offscreen_gl) {
712 			gf_filter_request_opengl(compositor->filter);
713 		}
714 		return;
715 	}
716 	compositor->video_out = &raw_vout;
717 	compositor->video_memory = 2;
718 }
719 #endif
720 
gf_sc_load(GF_Compositor * compositor)721 GF_Err gf_sc_load(GF_Compositor *compositor)
722 {
723 	u32 i;
724 	const char *sOpt;
725 
726 	compositor->mx = gf_mx_new("Compositor");
727 	if (!compositor->mx) return GF_OUT_OF_MEM;
728 
729 	/*load proto modules*/
730 	compositor->proto_modules = gf_list_new();
731 	if (!compositor->proto_modules) return GF_OUT_OF_MEM;
732 	for (i=0; i< gf_modules_count(); i++) {
733 		GF_HardcodedProto *ifce = (GF_HardcodedProto *) gf_modules_load(i, GF_HARDCODED_PROTO_INTERFACE);
734 		if (ifce) gf_list_add(compositor->proto_modules, ifce);
735 	}
736 
737 	compositor->init_flags = 0;
738 	compositor->os_wnd = NULL;
739 	sOpt = gf_opts_get_key("Temp", "OSWnd");
740 	if (sOpt) sscanf(sOpt, "%p", &compositor->os_wnd);
741 	sOpt = gf_opts_get_key("Temp", "InitFlags");
742 	if (sOpt) compositor->init_flags = atoi(sOpt);
743 
744 	if (compositor->noaudio)
745 		compositor->init_flags |= GF_TERM_NO_AUDIO;
746 
747 	/*force initial for 2D/3D setup*/
748 	compositor->msg_type |= GF_SR_CFG_INITIAL_RESIZE;
749 	/*set default size if owning output*/
750 	if (!compositor->os_wnd) {
751 		compositor->new_width = compositor->osize.x>0 ? compositor->osize.x : SC_DEF_WIDTH;
752 		compositor->new_height = compositor->osize.y>0 ? compositor->osize.y : SC_DEF_HEIGHT;
753 		compositor->msg_type |= GF_SR_CFG_SET_SIZE;
754 	}
755 
756 	if (!compositor->player)
757 		compositor->init_flags = GF_TERM_INIT_HIDE;
758 
759 	if (gf_opts_get_bool("temp", "gpac-help")) {
760 		compositor->init_flags |= GF_TERM_NO_VIDEO | GF_TERM_NO_AUDIO;
761 	}
762 
763 	if (compositor->init_flags & GF_TERM_NO_VIDEO) {
764 		compositor->video_out = &null_vout;
765 		compositor->ogl = GF_SC_GLMODE_OFF;
766 	} else if (compositor->player || (compositor->drv==GF_SC_DRV_ON)  || (compositor->ogl==GF_SC_GLMODE_HYBRID) ){
767 		GF_Err e;
768 
769 		e = gf_sc_load_driver(compositor);
770 		if (e) return e;
771 	} else {
772 		raw_vout.opaque = compositor;
773 		compositor->video_out = &raw_vout;
774 		compositor->video_memory = 2;
775 	}
776 
777 	compositor->strike_bank = gf_list_new();
778 	if (!compositor->strike_bank) return GF_OUT_OF_MEM;
779 	compositor->visuals = gf_list_new();
780 	if (!compositor->visuals) return GF_OUT_OF_MEM;
781 
782 	GF_SAFEALLOC(compositor->traverse_state, GF_TraverseState);
783 	if (!compositor->traverse_state) return GF_OUT_OF_MEM;
784 
785 	compositor->traverse_state->vrml_sensors = gf_list_new();
786 	if (!compositor->traverse_state->vrml_sensors) return GF_OUT_OF_MEM;
787 	compositor->traverse_state->use_stack = gf_list_new();
788 	if (!compositor->traverse_state->use_stack) return GF_OUT_OF_MEM;
789 #ifndef GPAC_DISABLE_3D
790 	compositor->traverse_state->local_lights = gf_list_new();
791 	if (!compositor->traverse_state->local_lights) return GF_OUT_OF_MEM;
792 #endif
793 	compositor->sensors = gf_list_new();
794 	if (!compositor->sensors) return GF_OUT_OF_MEM;
795 	compositor->previous_sensors = gf_list_new();
796 	if (!compositor->previous_sensors) return GF_OUT_OF_MEM;
797 	compositor->hit_use_stack = gf_list_new();
798 	if (!compositor->hit_use_stack) return GF_OUT_OF_MEM;
799 	compositor->prev_hit_use_stack = gf_list_new();
800 	if (!compositor->prev_hit_use_stack) return GF_OUT_OF_MEM;
801 	compositor->focus_ancestors = gf_list_new();
802 	if (!compositor->focus_ancestors) return GF_OUT_OF_MEM;
803 	compositor->focus_use_stack = gf_list_new();
804 	if (!compositor->focus_use_stack) return GF_OUT_OF_MEM;
805 
806 	compositor->env_tests = gf_list_new();
807 	if (!compositor->env_tests) return GF_OUT_OF_MEM;
808 
809 	/*setup main visual*/
810 	compositor->visual = visual_new(compositor);
811 	if (!compositor->visual) return GF_OUT_OF_MEM;
812 	compositor->visual->GetSurfaceAccess = compositor_2d_get_video_access;
813 	compositor->visual->ReleaseSurfaceAccess = compositor_2d_release_video_access;
814 	compositor->visual->CheckAttached = compositor_2d_check_attached;
815 	compositor->visual->ClearSurface = compositor_2d_clear_surface;
816 
817 	compositor_2d_init_callbacks(compositor);
818 
819 	if (compositor->video_out->FlushRectangles)
820 		compositor->visual->direct_flush = GF_TRUE;
821 
822 	// default value for depth gain is not zero
823 #ifdef GF_SR_USE_DEPTH
824 	compositor->traverse_state->depth_gain = FIX_ONE;
825 #endif
826 
827 	gf_list_add(compositor->visuals, compositor->visual);
828 
829 	compositor->zoom = compositor->scale_x = compositor->scale_y = FIX_ONE;
830 
831 	/*create a drawable for focus highlight*/
832 	compositor->focus_highlight = drawable_new();
833 	if (!compositor->focus_highlight) return GF_OUT_OF_MEM;
834 	/*associate a dummy node for traversing*/
835 	compositor->focus_highlight->node = gf_node_new(NULL, TAG_UndefinedNode);
836 	if (!compositor->focus_highlight->node) return GF_OUT_OF_MEM;
837 	gf_node_register(compositor->focus_highlight->node, NULL);
838 	gf_node_set_callback_function(compositor->focus_highlight->node, drawable_traverse_focus);
839 
840 
841 #ifndef GPAC_DISABLE_3D
842 	/*default collision mode*/
843 	compositor->collide_mode = GF_COLLISION_DISPLACEMENT; //GF_COLLISION_NORMAL
844 	compositor->gravity_on = GF_TRUE;
845 
846 	/*create default unit sphere and box for bounds*/
847 	compositor->unit_bbox = new_mesh();
848 	if (!compositor->unit_bbox) return GF_OUT_OF_MEM;
849 	mesh_new_unit_bbox(compositor->unit_bbox);
850 	gf_mx_init(compositor->traverse_state->model_matrix);
851 #endif
852 
853 	compositor->was_system_memory=1;
854 
855 	compositor->systems_pids = gf_list_new();
856 	if (!compositor->systems_pids) return GF_OUT_OF_MEM;
857 	compositor->textures = gf_list_new();
858 	if (!compositor->textures) return GF_OUT_OF_MEM;
859 	compositor->textures_gc = gf_list_new();
860 	if (!compositor->textures_gc) return GF_OUT_OF_MEM;
861 	if (!compositor->fps.num || !compositor->fps.den) {
862 		compositor->fps.num = 30;
863 		compositor->fps.den = 1;
864 	}
865 	compositor->frame_duration = compositor->fps.num * 1000;
866 	compositor->frame_duration /= compositor->fps.den;
867 
868 	compositor->time_nodes = gf_list_new();
869 	if (!compositor->time_nodes) return GF_OUT_OF_MEM;
870 	compositor->event_queue = gf_list_new();
871 	if (!compositor->event_queue) return GF_OUT_OF_MEM;
872 	compositor->event_queue_back = gf_list_new();
873 	if (!compositor->event_queue_back) return GF_OUT_OF_MEM;
874 	compositor->evq_mx = gf_mx_new("EventQueue");
875 	if (!compositor->evq_mx) return GF_OUT_OF_MEM;
876 
877 #ifdef GF_SR_USE_VIDEO_CACHE
878 	compositor->cached_groups = gf_list_new();
879 	if (!compositor->cached_groups) return GF_OUT_OF_MEM;
880 	compositor->cached_groups_queue = gf_list_new();
881 	if (!compositor->cached_groups_queue) return GF_OUT_OF_MEM;
882 #endif
883 
884 	/*load audio renderer*/
885 	if (!compositor->audio_renderer)
886 		compositor->audio_renderer = gf_sc_ar_load(compositor, compositor->init_flags);
887 
888 	gf_sc_reset_framerate(compositor);
889 	compositor->font_manager = gf_filter_get_font_manager(compositor->filter);
890 	if (!compositor->font_manager) return GF_OUT_OF_MEM;
891 
892 	compositor->extra_scenes = gf_list_new();
893 	if (!compositor->extra_scenes) return GF_OUT_OF_MEM;
894 	compositor->interaction_level = GF_INTERACT_NORMAL | GF_INTERACT_INPUT_SENSOR | GF_INTERACT_NAVIGATION;
895 
896 	compositor->scene_sampled_clock = 0;
897 	compositor->video_th_id = gf_th_id();
898 
899 	/*load extensions*/
900 	compositor->extensions = gf_list_new();
901 	if (!compositor->extensions) return GF_OUT_OF_MEM;
902 	compositor->unthreaded_extensions = gf_list_new();
903 	if (!compositor->unthreaded_extensions) return GF_OUT_OF_MEM;
904 	for (i=0; i< gf_modules_count(); i++) {
905 		GF_CompositorExt *ifce = (GF_CompositorExt *) gf_modules_load(i, GF_COMPOSITOR_EXT_INTERFACE);
906 		if (ifce) {
907 
908 			if (!ifce->process(ifce, GF_COMPOSITOR_EXT_START, compositor)) {
909 				gf_modules_close_interface((GF_BaseInterface *) ifce);
910 				continue;
911 			}
912 			gf_list_add(compositor->extensions, ifce);
913 			if (ifce->caps & GF_COMPOSITOR_EXTENSION_NOT_THREADED)
914 				gf_list_add(compositor->unthreaded_extensions, ifce);
915 		}
916 	}
917 
918 	if (!gf_list_count(compositor->unthreaded_extensions)) {
919 		gf_list_del(compositor->unthreaded_extensions);
920 		compositor->unthreaded_extensions = NULL;
921 	}
922 
923 	compositor->display_width = 320;
924 	compositor->display_height = 240;
925 	compositor->recompute_ar = GF_TRUE;
926 	compositor->scene_sampled_clock = 0;
927 	if (compositor->autoconfig_opengl || compositor->hybrid_opengl)
928 		gf_sc_recompute_ar(compositor, NULL);
929 
930 	/*try to load GL extensions*/
931 #ifndef GPAC_DISABLE_3D
932 	gf_sc_load_opengl_extensions(compositor, GF_FALSE);
933 #endif
934 
935 	gf_sc_reload_config(compositor);
936 
937 
938 	GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI]\tCompositor Cycle Log\tNetworks\tDecoders\tFrame\tDirect Draw\tVisual Config\tEvent\tRoute\tSMIL Timing\tTime node\tTexture\tSMIL Anim\tTraverse setup\tTraverse (and direct Draw)\tTraverse (and direct Draw) without anim\tIndirect Draw\tTraverse And Draw (Indirect or Not)\tFlush\tCycle\n"));
939 	return GF_OK;
940 }
941 
gf_sc_unload(GF_Compositor * compositor)942 void gf_sc_unload(GF_Compositor *compositor)
943 {
944 	u32 i;
945 	if (!compositor) return;
946 
947 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Destroying\n"));
948 	compositor->discard_input_events = GF_TRUE;
949 	gf_sc_lock(compositor, GF_TRUE);
950 
951 #ifndef GPAC_DISABLE_3D
952 	compositor_2d_reset_gl_auto(compositor);
953 #endif
954 	gf_sc_texture_cleanup_hw(compositor);
955 
956 	if (compositor->video_out) {
957 		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Closing video output\n"));
958 		compositor->video_out->Shutdown(compositor->video_out);
959 		gf_modules_close_interface((GF_BaseInterface *)compositor->video_out);
960 	}
961 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Closing visual compositor\n"));
962 
963 	if (compositor->focus_highlight) {
964 		gf_node_unregister(compositor->focus_highlight->node, NULL);
965 		drawable_del_ex(compositor->focus_highlight, compositor);
966 	}
967 	if (compositor->selected_text) gf_free(compositor->selected_text);
968 	if (compositor->sel_buffer) gf_free(compositor->sel_buffer);
969 
970 	if (compositor->visual) visual_del(compositor->visual);
971 	if (compositor->sensors) gf_list_del(compositor->sensors);
972 	if (compositor->previous_sensors) gf_list_del(compositor->previous_sensors);
973 	if (compositor->visuals) gf_list_del(compositor->visuals);
974 	if (compositor->strike_bank) gf_list_del(compositor->strike_bank);
975 	if (compositor->hit_use_stack) gf_list_del(compositor->hit_use_stack);
976 	if (compositor->prev_hit_use_stack) gf_list_del(compositor->prev_hit_use_stack);
977 	if (compositor->focus_ancestors) gf_list_del(compositor->focus_ancestors);
978 	if (compositor->focus_use_stack) gf_list_del(compositor->focus_use_stack);
979 	if (compositor->env_tests) gf_list_del(compositor->env_tests);
980 	if (compositor->systems_pids) gf_list_del(compositor->systems_pids);
981 
982 	if (compositor->traverse_state) {
983 		gf_list_del(compositor->traverse_state->vrml_sensors);
984 		gf_list_del(compositor->traverse_state->use_stack);
985 #ifndef GPAC_DISABLE_3D
986 		gf_list_del(compositor->traverse_state->local_lights);
987 #endif
988 		gf_free(compositor->traverse_state);
989 	}
990 
991 #ifndef GPAC_DISABLE_3D
992 	if (compositor->unit_bbox) mesh_free(compositor->unit_bbox);
993 
994 	if (compositor->screen_buffer) gf_free(compositor->screen_buffer);
995 	if (compositor->line_buffer) gf_free(compositor->line_buffer);
996 #endif
997 	if (compositor->framebuffer) gf_free(compositor->framebuffer);
998 
999 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Unloading visual compositor module\n"));
1000 
1001 	if (compositor->audio_renderer) gf_sc_ar_del(compositor->audio_renderer);
1002 	compositor->audio_renderer = NULL;
1003 
1004 	/*unload proto modules*/
1005 	if (compositor->proto_modules) {
1006 		for (i=0; i< gf_list_count(compositor->proto_modules); i++) {
1007 			GF_HardcodedProto *ifce = gf_list_get(compositor->proto_modules, i);
1008 			gf_modules_close_interface((GF_BaseInterface *) ifce);
1009 		}
1010 		gf_list_del(compositor->proto_modules);
1011 	}
1012 	if (compositor->evq_mx) gf_mx_p(compositor->evq_mx);
1013 	while (gf_list_count(compositor->event_queue)) {
1014 		GF_QueuedEvent *qev = (GF_QueuedEvent *)gf_list_get(compositor->event_queue, 0);
1015 		gf_list_rem(compositor->event_queue, 0);
1016 		gf_free(qev);
1017 	}
1018 	while (gf_list_count(compositor->event_queue_back)) {
1019 		GF_QueuedEvent *qev = (GF_QueuedEvent *)gf_list_get(compositor->event_queue_back, 0);
1020 		gf_list_rem(compositor->event_queue, 0);
1021 		gf_free(qev);
1022 	}
1023 	if (compositor->evq_mx) {
1024 		gf_mx_v(compositor->evq_mx);
1025 		gf_mx_del(compositor->evq_mx);
1026 	}
1027 	gf_list_del(compositor->event_queue);
1028 	gf_list_del(compositor->event_queue_back);
1029 
1030 #ifdef GF_SR_USE_VIDEO_CACHE
1031 	gf_list_del(compositor->cached_groups);
1032 	gf_list_del(compositor->cached_groups_queue);
1033 #endif
1034 
1035 	if (compositor->textures) gf_list_del(compositor->textures);
1036 	if (compositor->textures_gc) gf_list_del(compositor->textures_gc);
1037 	if (compositor->time_nodes) gf_list_del(compositor->time_nodes);
1038 	if (compositor->extra_scenes) gf_list_del(compositor->extra_scenes);
1039 	if (compositor->input_streams) gf_list_del(compositor->input_streams);
1040 	if (compositor->x3d_sensors) gf_list_del(compositor->x3d_sensors);
1041 
1042 
1043 	/*unload extensions*/
1044 	for (i=0; i< gf_list_count(compositor->extensions); i++) {
1045 		GF_CompositorExt *ifce = gf_list_get(compositor->extensions, i);
1046 		ifce->process(ifce, GF_COMPOSITOR_EXT_STOP, NULL);
1047 	}
1048 
1049 	/*remove all event filters*/
1050 //	gf_list_reset(term->event_filters);
1051 
1052 	/*unload extensions*/
1053 	for (i=0; i< gf_list_count(compositor->extensions); i++) {
1054 		GF_CompositorExt *ifce = gf_list_get(compositor->extensions, i);
1055 		gf_modules_close_interface((GF_BaseInterface *) ifce);
1056 	}
1057 	gf_list_del(compositor->extensions);
1058 	gf_list_del(compositor->unthreaded_extensions);
1059 
1060 	gf_sc_lock(compositor, GF_FALSE);
1061 	gf_mx_del(compositor->mx);
1062 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Destroyed\n"));
1063 }
1064 
gf_sc_set_fps(GF_Compositor * compositor,GF_Fraction fps)1065 void gf_sc_set_fps(GF_Compositor *compositor, GF_Fraction fps)
1066 {
1067 	if (fps.den) {
1068 		u64 dur;
1069 		compositor->fps = fps;
1070 		dur = fps.den;
1071 		dur *= 1000;
1072 		dur /= fps.num;
1073 
1074 		compositor->frame_duration = (u32) dur;
1075 		gf_sc_reset_framerate(compositor);
1076 
1077 		if (compositor->vout && !compositor->timescale) {
1078 			gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_TIMESCALE, &PROP_UINT(fps.num) );
1079 
1080 		}
1081 	}
1082 }
1083 
gf_sc_set_play_state(GF_Compositor * compositor,u32 PlayState)1084 static void gf_sc_set_play_state(GF_Compositor *compositor, u32 PlayState)
1085 {
1086 	if (!compositor || !compositor->audio_renderer) return;
1087 	if (!compositor->paused && !PlayState) return;
1088 	if (compositor->paused && (PlayState==GF_STATE_PAUSED)) return;
1089 
1090 	/*step mode*/
1091 	if (PlayState==GF_STATE_STEP_PAUSE) {
1092 		compositor->step_mode = GF_TRUE;
1093 		compositor->paused = 1;
1094 	} else {
1095 		compositor->step_mode = GF_FALSE;
1096 		if (compositor->audio_renderer) gf_sc_ar_control(compositor->audio_renderer, (compositor->paused && (PlayState==0xFF)) ? GF_SC_AR_RESET_HW_AND_PLAY : (compositor->paused ? GF_SC_AR_RESUME : GF_SC_AR_PAUSE) );
1097 		compositor->paused = (PlayState==GF_STATE_PAUSED) ? 1 : 0;
1098 	}
1099 }
1100 
1101 GF_EXPORT
gf_sc_get_clock(GF_Compositor * compositor)1102 u32 gf_sc_get_clock(GF_Compositor *compositor)
1103 {
1104 	if (!compositor->bench_mode && compositor->player) {
1105 		return gf_sc_ar_get_clock(compositor->audio_renderer);
1106 	}
1107 	return compositor->scene_sampled_clock;
1108 }
1109 
1110 GF_EXPORT
gf_sc_set_scene_size(GF_Compositor * compositor,u32 Width,u32 Height,Bool force_size)1111 GF_Err gf_sc_set_scene_size(GF_Compositor *compositor, u32 Width, u32 Height, Bool force_size)
1112 {
1113 	if (!Width || !Height) {
1114 		if (compositor->override_size_flags) {
1115 			/*specify a small size to detect biggest bitmap but not 0 in case only audio..*/
1116 			compositor->scene_height = SC_DEF_HEIGHT;
1117 			compositor->scene_width = SC_DEF_WIDTH;
1118 		} else {
1119 			/*use current res*/
1120 			compositor->scene_width = compositor->new_width ? compositor->new_width : compositor->display_width;
1121 			compositor->scene_height = compositor->new_height ? compositor->new_height : compositor->display_height;
1122 		}
1123 	} else {
1124 		compositor->scene_height = Height;
1125 		compositor->scene_width = Width;
1126 	}
1127 	if (force_size)
1128 		compositor->has_size_info = 1;
1129 	return GF_OK;
1130 }
1131 
1132 #ifndef GPAC_DISABLE_SVG
1133 GF_EXPORT
gf_sc_svg_convert_length_to_display(GF_Compositor * compositor,SVG_Length * length)1134 Fixed gf_sc_svg_convert_length_to_display(GF_Compositor *compositor, SVG_Length *length)
1135 {
1136 	/* Assuming the environment is 90dpi*/
1137 	u32 dpi = 90;
1138 	if (!length) return 0;
1139 
1140 	switch (length->type) {
1141 	case SVG_NUMBER_PERCENTAGE:
1142 		break;
1143 	case SVG_NUMBER_EMS:
1144 		break;
1145 	case SVG_NUMBER_EXS:
1146 		break;
1147 	case SVG_NUMBER_VALUE:
1148 		break;
1149 	case SVG_NUMBER_PX:
1150 		return length->value;
1151 	case SVG_NUMBER_CM:
1152 		return gf_mulfix(length->value, dpi*FLT2FIX(0.39));
1153 		break;
1154 	case SVG_NUMBER_MM:
1155 		return gf_mulfix(length->value, dpi*FLT2FIX(0.039));
1156 	case SVG_NUMBER_IN:
1157 		return length->value * dpi;
1158 	case SVG_NUMBER_PT:
1159 		return (dpi * length->value) / 12;
1160 	case SVG_NUMBER_PC:
1161 		return (dpi*length->value) / 6;
1162 	case SVG_NUMBER_INHERIT:
1163 		break;
1164 	}
1165 	return length->value;
1166 }
1167 #endif
1168 
compositor_set_ar_scale(GF_Compositor * compositor,Fixed scaleX,Fixed scaleY)1169 void compositor_set_ar_scale(GF_Compositor *compositor, Fixed scaleX, Fixed scaleY)
1170 {
1171 	compositor->trans_x = gf_muldiv(compositor->trans_x, scaleX, compositor->scale_x);
1172 	compositor->trans_y = gf_muldiv(compositor->trans_y, scaleY, compositor->scale_y);
1173 
1174 	compositor->zoom_changed = 1;
1175 	compositor->scale_x = scaleX;
1176 	compositor->scale_y = scaleY;
1177 
1178 	compositor_2d_set_user_transform(compositor, compositor->zoom, compositor->trans_x, compositor->trans_y, 1);
1179 }
1180 
gf_sc_reset(GF_Compositor * compositor,Bool has_scene)1181 static void gf_sc_reset(GF_Compositor *compositor, Bool has_scene)
1182 {
1183 	Bool mode2d;
1184 	GF_VisualManager *visual;
1185 	u32 i=0;
1186 
1187 	//init failed
1188 	if (!compositor->traverse_state)
1189 		return;
1190 
1191 	while ((visual = (GF_VisualManager *)gf_list_enum(compositor->visuals, &i))) {
1192 		/*reset display list*/
1193 		visual->cur_context = visual->context;
1194 		if (visual->cur_context) visual->cur_context->drawable = NULL;
1195 		while (visual->prev_nodes) {
1196 			struct _drawable_store *cur = visual->prev_nodes;
1197 			visual->prev_nodes = cur->next;
1198 			gf_free(cur);
1199 		}
1200 		visual->last_prev_entry = NULL;
1201 		visual->to_redraw.count = 0;
1202 
1203 		/*reset the surface as well*/
1204 		if (visual->raster_surface) gf_evg_surface_delete(visual->raster_surface);
1205 		visual->raster_surface = NULL;
1206 	}
1207 
1208 	gf_list_reset(compositor->sensors);
1209 	gf_list_reset(compositor->previous_sensors);
1210 
1211 	/*reset traverse state*/
1212 	mode2d = compositor->traverse_state->immediate_draw;
1213 	gf_list_del(compositor->traverse_state->vrml_sensors);
1214 	gf_list_del(compositor->traverse_state->use_stack);
1215 #ifndef GPAC_DISABLE_3D
1216 	gf_list_del(compositor->traverse_state->local_lights);
1217 
1218 	compositor_3d_delete_fbo(&compositor->fbo_id, &compositor->fbo_tx_id, &compositor->fbo_depth_id, compositor->external_tx_id);
1219 #endif
1220 
1221 	memset(compositor->traverse_state, 0, sizeof(GF_TraverseState));
1222 
1223 	compositor->traverse_state->vrml_sensors = gf_list_new();
1224 	compositor->traverse_state->use_stack = gf_list_new();
1225 #ifndef GPAC_DISABLE_3D
1226 	compositor->traverse_state->local_lights = gf_list_new();
1227 #endif
1228 
1229 	gf_mx2d_init(compositor->traverse_state->transform);
1230 	gf_cmx_init(&compositor->traverse_state->color_mat);
1231 	compositor->traverse_state->immediate_draw = mode2d;
1232 
1233 #ifdef GF_SR_USE_DEPTH
1234 	compositor->traverse_state->depth_gain = FIX_ONE;
1235 	compositor->traverse_state->depth_offset = 0;
1236 #endif
1237 
1238 #ifndef GPAC_DISABLE_3D
1239 	//force a recompute of the canvas
1240 	if (has_scene && compositor->hybgl_txh) {
1241 		compositor->hybgl_txh->width = compositor->hybgl_txh->height = 0;
1242 	}
1243 #endif
1244 
1245 	assert(!compositor->visual->overlays);
1246 
1247 	compositor->reset_graphics = 0;
1248 	compositor->trans_x = compositor->trans_y = 0;
1249 	compositor->zoom = FIX_ONE;
1250 	compositor->grab_node = NULL;
1251 	compositor->grab_use = NULL;
1252 	compositor->focus_node = NULL;
1253 	compositor->focus_text_type = 0;
1254 	compositor->frame_number = 0;
1255 	if (compositor->video_memory!=2)
1256 		compositor->video_memory = compositor->was_system_memory ? 0 : 1;
1257 	compositor->rotation = 0;
1258 
1259 	gf_list_reset(compositor->focus_ancestors);
1260 	gf_list_reset(compositor->focus_use_stack);
1261 
1262 #ifdef GF_SR_USE_VIDEO_CACHE
1263 	gf_list_reset(compositor->cached_groups);
1264 	compositor->video_cache_current_size = 0;
1265 	gf_list_reset(compositor->cached_groups_queue);
1266 #endif
1267 
1268 	/*force resetup in case we're switching coord system*/
1269 	compositor->root_visual_setup = 0;
1270 	compositor_set_ar_scale(compositor, compositor->scale_x, compositor->scale_x);
1271 }
1272 
gf_sc_set_scene(GF_Compositor * compositor,GF_SceneGraph * scene_graph)1273 GF_Err gf_sc_set_scene(GF_Compositor *compositor, GF_SceneGraph *scene_graph)
1274 {
1275 	u32 width, height;
1276 	Bool do_notif;
1277 
1278 	if (!compositor) return GF_BAD_PARAM;
1279 
1280 	gf_sc_lock(compositor, 1);
1281 	if (scene_graph && !compositor->scene) {
1282 		GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Attaching new scene\n"));
1283 	} else if (!scene_graph && compositor->scene) {
1284 		GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Detaching scene\n"));
1285 	}
1286 
1287 	if (compositor->audio_renderer && (compositor->scene != scene_graph)) {
1288 		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Reseting audio compositor\n"));
1289 		gf_sc_ar_reset(compositor->audio_renderer);
1290 	}
1291 
1292 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Reseting event queue\n"));
1293 	gf_mx_p(compositor->evq_mx);
1294 	while (gf_list_count(compositor->event_queue)) {
1295 		GF_QueuedEvent *qev = (GF_QueuedEvent*)gf_list_get(compositor->event_queue, 0);
1296 		gf_list_rem(compositor->event_queue, 0);
1297 		gf_free(qev);
1298 	}
1299 	gf_mx_v(compositor->evq_mx);
1300 
1301 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Reseting compositor module\n"));
1302 	/*reset main surface*/
1303 	gf_sc_reset(compositor, scene_graph ? 1 : 0);
1304 
1305 	/*set current graph*/
1306 	compositor->scene = scene_graph;
1307 	do_notif = GF_FALSE;
1308 	if (scene_graph) {
1309 		GF_Scene *scene_ctx = gf_sg_get_private(scene_graph);
1310 #ifndef GPAC_DISABLE_SVG
1311 		SVG_Length *w, *h;
1312 		SVG_ViewBox *vb;
1313 		Bool is_svg = GF_FALSE;
1314 		u32 tag;
1315 		GF_Node *top_node;
1316 #endif
1317 		Bool had_size_info = compositor->has_size_info;
1318 
1319 		compositor->timed_nodes_valid = GF_TRUE;
1320 		if (scene_ctx && scene_ctx->is_dynamic_scene)
1321 			compositor->timed_nodes_valid = GF_FALSE;
1322 
1323 		/*get pixel size if any*/
1324 		gf_sg_get_scene_size_info(compositor->scene, &width, &height);
1325 		compositor->has_size_info = (width && height) ? 1 : 0;
1326 		if (compositor->has_size_info != had_size_info) compositor->scene_width = compositor->scene_height = 0;
1327 
1328 		if (compositor->video_memory!=2)
1329 			compositor->video_memory = gf_scene_is_dynamic_scene(scene_graph);
1330 
1331 #ifndef GPAC_DISABLE_3D
1332 		compositor->visual->camera.world_bbox.is_set = 0;
1333 #endif
1334 
1335 		/*default back color is black*/
1336 		if (! (compositor->init_flags & GF_TERM_WINDOWLESS)) {
1337 			if (compositor->bc) {
1338 				compositor->back_color = compositor->bc;
1339 			} else {
1340 				compositor->back_color = 0xFF000000;
1341 			}
1342 		}
1343 
1344 #ifndef GPAC_DISABLE_SVG
1345 		top_node = gf_sg_get_root_node(compositor->scene);
1346 		tag = 0;
1347 		if (top_node) tag = gf_node_get_tag(top_node);
1348 
1349 		w = h = NULL;
1350 		vb = NULL;
1351 		if ((tag>=GF_NODE_RANGE_FIRST_SVG) && (tag<=GF_NODE_RANGE_LAST_SVG)) {
1352 			GF_FieldInfo info;
1353 			is_svg = 1;
1354 			if (gf_node_get_attribute_by_tag(top_node, TAG_SVG_ATT_width, 0, 0, &info)==GF_OK)
1355 				w = info.far_ptr;
1356 			if (gf_node_get_attribute_by_tag(top_node, TAG_SVG_ATT_height, 0, 0, &info)==GF_OK)
1357 				h = info.far_ptr;
1358 			if (gf_node_get_attribute_by_tag(top_node, TAG_SVG_ATT_viewBox, 0, 0, &info)==GF_OK)
1359 				vb = info.far_ptr;
1360 		}
1361 		/*default back color is white*/
1362 		if (is_svg && ! (compositor->init_flags & GF_TERM_WINDOWLESS)) compositor->back_color = 0xFFFFFFFF;
1363 
1364 		/*hack for SVG where size is set in % - negotiate a canvas size*/
1365 		if (!compositor->has_size_info && w && h && vb) {
1366 			do_notif = 1;
1367 			if (w->type!=SVG_NUMBER_PERCENTAGE) {
1368 				width = FIX2INT(gf_sc_svg_convert_length_to_display(compositor, w) );
1369 			} else if ((u32) FIX2INT(vb->width)<compositor->video_out->max_screen_width)  {
1370 				width = FIX2INT(vb->width);
1371 			} else {
1372 				width = SC_DEF_WIDTH;
1373 				do_notif = 0;
1374 			}
1375 			if (h->type!=SVG_NUMBER_PERCENTAGE) {
1376 				height = FIX2INT(gf_sc_svg_convert_length_to_display(compositor, h) );
1377 			} else if ((u32) FIX2INT(vb->height)<compositor->video_out->max_screen_height)  {
1378 				height = FIX2INT(vb->height);
1379 			} else {
1380 				height = SC_DEF_HEIGHT;
1381 				do_notif = 0;
1382 			}
1383 		}
1384 		/*we consider that SVG has no size onfo per say, everything is handled by the viewBox if any*/
1385 		if (is_svg) {
1386 			compositor->has_size_info = 0;
1387 			gf_sc_focus_switch_ring(compositor, 0, NULL, 0);
1388 		} else
1389 #endif
1390 		{
1391 			GF_Node *keynav = gf_scene_get_keynav(compositor->scene, NULL);
1392 			if (keynav) gf_sc_change_key_navigator(compositor, keynav);
1393 		}
1394 
1395 		/*default back color is key color*/
1396 		if (compositor->init_flags & GF_TERM_WINDOWLESS) {
1397 			if (compositor->ckey) compositor->back_color = compositor->ckey;
1398 		}
1399 
1400 		/*set scene size only if different, otherwise keep scaling/FS*/
1401 		if ( !width || (compositor->scene_width!=width) || !height || (compositor->scene_height!=height)) {
1402 			do_notif = do_notif || compositor->has_size_info || (!compositor->scene_width && !compositor->scene_height);
1403 			gf_sc_set_scene_size(compositor, width, height, 0);
1404 
1405 			/*get actual size in pixels*/
1406 			width = compositor->scene_width;
1407 			height = compositor->scene_height;
1408 
1409 			if (!compositor->os_wnd) {
1410 				/*only notify user if we are attached to a window*/
1411 				//do_notif = 0;
1412 				if (compositor->video_out->max_screen_width && (width > compositor->video_out->max_screen_width))
1413 					width = compositor->video_out->max_screen_width;
1414 				if (compositor->video_out->max_screen_height && (height > compositor->video_out->max_screen_height))
1415 					height = compositor->video_out->max_screen_height;
1416 
1417 				gf_sc_set_size(compositor,width, height);
1418 			}
1419 		}
1420 	} else {
1421 		gf_sc_ar_reset(compositor->audio_renderer);
1422 		compositor->needs_offscreen_gl = GF_FALSE;
1423 	}
1424 
1425 	gf_sc_reset_framerate(compositor);
1426 
1427 	gf_sc_lock(compositor, 0);
1428 	if (scene_graph)
1429 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1430 
1431 	/*here's a nasty trick: the app may respond to this by calling a gf_sc_set_size from a different
1432 	thread, but in an atomic way (typically happen on Win32 when changing the window size). WE MUST
1433 	NOTIFY THE SIZE CHANGE AFTER RELEASING THE RENDERER MUTEX*/
1434 	if (do_notif) {
1435 		GF_Event evt;
1436 		/*wait for user ack*/
1437 //		gf_sc_next_frame_state(compositor, GF_SC_DRAW_NONE);
1438 
1439 		evt.type = GF_EVENT_SCENE_SIZE;
1440 		evt.size.width = width;
1441 		evt.size.height = height;
1442 		gf_sc_send_event(compositor, &evt);
1443 	}
1444 	return GF_OK;
1445 }
1446 
1447 GF_EXPORT
gf_sc_lock(GF_Compositor * compositor,Bool doLock)1448 void gf_sc_lock(GF_Compositor *compositor, Bool doLock)
1449 {
1450 	//GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Compositor] Thread ID %d is %s the scene\n", gf_th_id(), doLock ? "locking" : "unlocking" ));
1451 	if (doLock)
1452 		gf_mx_p(compositor->mx);
1453 	else {
1454 		gf_mx_v(compositor->mx);
1455 	}
1456 }
1457 
1458 GF_EXPORT
gf_sc_set_size(GF_Compositor * compositor,u32 NewWidth,u32 NewHeight)1459 GF_Err gf_sc_set_size(GF_Compositor *compositor, u32 NewWidth, u32 NewHeight)
1460 {
1461 	Bool lock_ok;
1462 
1463 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("sc_set_size %dx%d\n", NewWidth, NewHeight));
1464 	if (compositor->osize.x && compositor->osize.y) {
1465 		NewWidth = compositor->osize.x;
1466 		NewHeight = compositor->osize.y;
1467 	}
1468 
1469 	if ((compositor->display_width == NewWidth) && (compositor->display_height == NewHeight))
1470 		return GF_OK;
1471 
1472 	if (!NewWidth || !NewHeight) {
1473 		compositor->override_size_flags &= ~2;
1474 		compositor->msg_type |= GF_SR_CFG_AR;
1475 		return GF_OK;
1476 	}
1477 	/*EXTRA CARE HERE: the caller (user app) is likely a different thread than the compositor one, and depending on window
1478 	manager we may get called here as a result of a message sent to user but not yet returned */
1479 	lock_ok = gf_mx_try_lock(compositor->mx);
1480 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("line %d lock_ok %d\n", __LINE__, lock_ok));
1481 
1482 	compositor->new_width = NewWidth;
1483 	compositor->new_height = NewHeight;
1484 	compositor->msg_type |= GF_SR_CFG_SET_SIZE;
1485 	assert(compositor->new_width);
1486 
1487 	/*if same size only request for video setup */
1488 	compositor->msg_type &= ~GF_SR_CFG_WINDOWSIZE_NOTIF;
1489 	if ((compositor->display_width == NewWidth) && (compositor->display_height == NewHeight) ) {
1490 		compositor->msg_type |= GF_SR_CFG_WINDOWSIZE_NOTIF;
1491 	}
1492 	if (lock_ok) gf_sc_lock(compositor, 0);
1493 
1494 	//forward scene size event before actual resize in case the user cancels the resize
1495 	{
1496 		GF_Event evt;
1497 		evt.type = GF_EVENT_SCENE_SIZE;
1498 		evt.size.width = NewWidth;
1499 		evt.size.height = NewHeight;
1500 		gf_sc_send_event(compositor, &evt);
1501 	}
1502 
1503 	return GF_OK;
1504 }
1505 
1506 GF_EXPORT
gf_sc_reload_config(GF_Compositor * compositor)1507 void gf_sc_reload_config(GF_Compositor *compositor)
1508 {
1509 	/*changing drivers needs exclusive access*/
1510 	gf_sc_lock(compositor, 1);
1511 
1512 	gf_sc_set_fps(compositor, compositor->fps);
1513 
1514 	if (compositor->fsize)
1515 		compositor->override_size_flags |= 1;
1516 	else
1517 		compositor->override_size_flags &= ~1;
1518 
1519 
1520 #ifndef GPAC_DISABLE_3D
1521 
1522 	if (! (compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL)) {
1523 		if (compositor->player && (compositor->ogl > GF_SC_GLMODE_OFF)) {
1524 			GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] OpenGL mode requested but no opengl-capable output - disabling openGL\n"));
1525 		}
1526 		compositor->force_opengl_2d = 0;
1527 		compositor->autoconfig_opengl = 0;
1528 		compositor->hybrid_opengl = 0;
1529 	} else {
1530 		compositor->force_opengl_2d = (compositor->ogl==GF_SC_GLMODE_ON) ? 1 : 0;
1531 		if (compositor->ogl == GF_SC_GLMODE_AUTO) {
1532 			compositor->recompute_ar = 1;
1533 			compositor->autoconfig_opengl = 1;
1534 		} else {
1535 			compositor->hybrid_opengl = (compositor->ogl==GF_SC_GLMODE_HYBRID) ? 1 : 0;
1536 		}
1537 	}
1538 
1539 #ifdef GPAC_USE_GLES1X
1540 	compositor->linegl = 1;
1541 	compositor->epow2 = 1;
1542 #endif
1543 
1544 	compositor->visual->nb_views = compositor->nbviews;
1545 	compositor->visual->camlay = compositor->camlay;
1546 	compositor->visual->autostereo_type = compositor->stereo;
1547 	if (compositor->visual->autostereo_type == GF_3D_STEREO_5VSP19) {
1548 		if (compositor->visual->nb_views != 5) {
1549 			if (compositor->visual->nb_views) {
1550 				GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] SPV19 interleaving used but only %d views indicated - adjusting to 5 view\n", compositor->visual->nb_views ));
1551 			}
1552 			compositor->visual->nb_views = 5;
1553 		}
1554 	}
1555 	else if (compositor->visual->autostereo_type == GF_3D_STEREO_8VALIO) {
1556 		if (compositor->visual->nb_views != 8) {
1557 			if (compositor->visual->nb_views) {
1558 				GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] ALIO interleaving used but only %d views indicated - adjusting to 8 view\n", compositor->visual->nb_views ));
1559 				compositor->visual->nb_views = 8;
1560 			}
1561 		}
1562 	}
1563 
1564 	if ((compositor->visual->autostereo_type!=GF_3D_STEREO_NONE) && (compositor->visual->autostereo_type <= GF_3D_STEREO_HEADSET)) {
1565 		compositor->visual->nb_views = 2;
1566 	}
1567 
1568 	if (compositor->visual->autostereo_type)
1569 		compositor->force_opengl_2d = 1;
1570 
1571 	switch (compositor->visual->autostereo_type) {
1572 	case GF_3D_STEREO_ANAGLYPH:
1573 	case GF_3D_STEREO_COLUMNS:
1574 	case GF_3D_STEREO_ROWS:
1575 		if (compositor->visual->nb_views != 2) {
1576 			if (compositor->visual->nb_views) {
1577 				GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] Stereo interleaving used but %d views indicated - adjusting to 2 view\n", compositor->visual->nb_views ));
1578 			}
1579 			compositor->visual->nb_views = 2;
1580 		}
1581 		break;
1582 	}
1583 	if (!compositor->visual->nb_views) compositor->visual->nb_views = 1;
1584 
1585 #endif //GPAC_DISABLE_3D
1586 
1587 	/*load defer mode only once hybrid_opengl is known. If no hybrid openGL and no backbuffer 2D, disable defer rendering*/
1588 	if (!compositor->hybrid_opengl && compositor->video_out->hw_caps & GF_VIDEO_HW_DIRECT_ONLY) {
1589 		compositor->traverse_state->immediate_draw = 1;
1590 	} else {
1591 		if (compositor->mode2d==GF_DRAW_MODE_IMMEDIATE)
1592 			compositor->traverse_state->immediate_draw = 1;
1593 		else if (compositor->mode2d==GF_DRAW_MODE_DEFER_DEBUG) {
1594 			compositor->traverse_state->immediate_draw = 0;
1595 			compositor->debug_defer = 1;
1596 		} else {
1597 		 	compositor->traverse_state->immediate_draw = 0;
1598 		}
1599 	}
1600 
1601 
1602 #ifdef GF_SR_USE_DEPTH
1603 
1604 #ifndef GPAC_DISABLE_3D
1605 	/*if auto-stereo mode, turn on display depth by default*/
1606 	if (compositor->visual->autostereo_type && !compositor->dispdepth) {
1607 		compositor->dispdepth = -1;
1608 	}
1609 #endif
1610 
1611 #endif
1612 
1613 	if (!compositor->video_out->dispdist) {
1614 		compositor->video_out->dispdist = compositor->dispdist;
1615 	}
1616 	if (compositor->sgaze) compositor->gazer_enabled = GF_TRUE;
1617 
1618 	if (!compositor->video_out->dpi_x) {
1619 		compositor->video_out->dpi_x = compositor->dpi.x;
1620 		compositor->video_out->dpi_y = compositor->dpi.y;
1621 	}
1622 
1623 	gf_sc_reset_graphics(compositor);
1624 	gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1625 
1626 	if (compositor->audio_renderer) {
1627 		gf_sc_ar_set_volume(compositor->audio_renderer, compositor->avol);
1628 		gf_sc_ar_set_pan(compositor->audio_renderer, compositor->apan);
1629 	}
1630 	gf_sc_lock(compositor, 0);
1631 }
1632 
1633 GF_EXPORT
gf_sc_set_option(GF_Compositor * compositor,u32 type,u32 value)1634 GF_Err gf_sc_set_option(GF_Compositor *compositor, u32 type, u32 value)
1635 {
1636 	GF_Err e;
1637 	gf_sc_lock(compositor, 1);
1638 
1639 	e = GF_OK;
1640 	switch (type) {
1641 	case GF_OPT_PLAY_STATE:
1642 		gf_sc_set_play_state(compositor, value);
1643 		break;
1644 	case GF_OPT_AUDIO_VOLUME:
1645 		gf_sc_ar_set_volume(compositor->audio_renderer, value);
1646 		break;
1647 	case GF_OPT_AUDIO_PAN:
1648 		gf_sc_ar_set_pan(compositor->audio_renderer, value);
1649 		break;
1650 	case GF_OPT_AUDIO_MUTE:
1651 		gf_sc_ar_mute(compositor->audio_renderer, value);
1652 		break;
1653 	case GF_OPT_OVERRIDE_SIZE:
1654 		compositor->override_size_flags = value ? 1 : 0;
1655 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1656 		break;
1657 	case GF_OPT_STRESS_MODE:
1658 		compositor->stress = value;
1659 		break;
1660 	case GF_OPT_ANTIALIAS:
1661 		compositor->aa = value;
1662 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1663 		break;
1664 	case GF_OPT_HIGHSPEED:
1665 		compositor->fast = value;
1666 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1667 		break;
1668 	case GF_OPT_DRAW_BOUNDS:
1669 		compositor->bvol = value;
1670 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1671 		break;
1672 	case GF_OPT_TEXTURE_TEXT:
1673 		compositor->textxt = value;
1674 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1675 		break;
1676 	case GF_OPT_ASPECT_RATIO:
1677 		compositor->aspect_ratio = value;
1678 		compositor->msg_type |= GF_SR_CFG_AR;
1679 		break;
1680 	case GF_OPT_INTERACTION_LEVEL:
1681 		compositor->interaction_level = value;
1682 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1683 		break;
1684 	case GF_OPT_REFRESH:
1685 		gf_sc_reset_graphics(compositor);
1686 		compositor->traverse_state->invalidate_all = 1;
1687 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1688 		break;
1689 	case GF_OPT_FULLSCREEN:
1690 		if (compositor->fullscreen != value) compositor->msg_type |= GF_SR_CFG_FULLSCREEN;
1691 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1692 		break;
1693 	case GF_OPT_ORIGINAL_VIEW:
1694 		compositor_2d_set_user_transform(compositor, FIX_ONE, 0, 0, 0);
1695 		gf_sc_set_size(compositor, compositor->scene_width, compositor->scene_height);
1696 		break;
1697 	case GF_OPT_VISIBLE:
1698 		compositor->is_hidden = !value;
1699 		if (compositor->video_out->ProcessEvent) {
1700 			GF_Event evt;
1701 			evt.type = GF_EVENT_SHOWHIDE;
1702 			evt.show.show_type = value ? 1 : 0;
1703 			e = compositor->video_out->ProcessEvent(compositor->video_out, &evt);
1704 		}
1705 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1706 		break;
1707 	case GF_OPT_FREEZE_DISPLAY:
1708 		compositor->freeze_display = value;
1709 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1710 		break;
1711 	case GF_OPT_FORCE_AUDIO_CONFIG:
1712 		if (value) {
1713 			compositor->audio_renderer->config_forced++;
1714 		} else if (compositor->audio_renderer->config_forced) {
1715 			compositor->audio_renderer->config_forced --;
1716 		}
1717 		break;
1718 	case GF_OPT_RELOAD_CONFIG:
1719 		gf_sc_reload_config(compositor);
1720 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1721 		break;
1722 	case GF_OPT_DRAW_MODE:
1723 		if (!(compositor->video_out->hw_caps & GF_VIDEO_HW_DIRECT_ONLY)) {
1724 			compositor->traverse_state->immediate_draw = (value==GF_DRAW_MODE_IMMEDIATE) ? 1 : 0;
1725 			compositor->debug_defer = (value==GF_DRAW_MODE_DEFER_DEBUG) ? 1 : 0;
1726 			/*force redraw*/
1727 			gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1728 		}
1729 		break;
1730 	case GF_OPT_SCALABLE_ZOOM:
1731 		compositor->sz = value;
1732 		/*emulate size message to force AR recompute*/
1733 		compositor->msg_type |= GF_SR_CFG_AR;
1734 		break;
1735 	case GF_OPT_USE_OPENGL:
1736 		if (compositor->force_opengl_2d != value) {
1737 			compositor->force_opengl_2d = value;
1738 			/*force resetup*/
1739 			compositor->root_visual_setup = 0;
1740 			/*force texture setup when switching to OpenGL*/
1741 			if (value) gf_sc_reset_graphics(compositor);
1742 			/*force redraw*/
1743 			gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1744 		}
1745 		break;
1746 	case GF_OPT_VIDEO_BENCH:
1747 		compositor->bench_mode = value ? GF_TRUE : GF_FALSE;
1748 		break;
1749 
1750 	case GF_OPT_YUV_HARDWARE:
1751 		compositor->yuvhw = value;
1752 		break;
1753 	case GF_OPT_NAVIGATION_TYPE:
1754 		compositor->rotation = 0;
1755 		compositor_2d_set_user_transform(compositor, FIX_ONE, 0, 0, 0);
1756 #ifndef GPAC_DISABLE_3D
1757 		compositor_3d_reset_camera(compositor);
1758 #endif
1759 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1760 		break;
1761 	case GF_OPT_NAVIGATION:
1762 		if (! gf_sc_navigation_supported(compositor, value)) {
1763 			e = GF_NOT_SUPPORTED;
1764 		}
1765 #ifndef GPAC_DISABLE_3D
1766 		else if (compositor->visual->type_3d || compositor->active_layer) {
1767 			GF_Camera *cam = compositor_3d_get_camera(compositor);
1768 			cam->navigate_mode = value;
1769 		}
1770 #endif
1771 		else {
1772 			compositor->navigate_mode = value;
1773 		}
1774 		break;
1775 
1776 	case GF_OPT_HEADLIGHT:
1777 #ifndef GPAC_DISABLE_3D
1778 		if (compositor->visual->type_3d || compositor->active_layer) {
1779 			GF_Camera *cam = compositor_3d_get_camera(compositor);
1780 			if (cam->navigation_flags & NAV_ANY) {
1781 				if (value)
1782 					cam->navigation_flags |= NAV_HEADLIGHT;
1783 				else
1784 					cam->navigation_flags &= ~NAV_HEADLIGHT;
1785 				break;
1786 			}
1787 		}
1788 #endif
1789 		e = GF_NOT_SUPPORTED;
1790 		break;
1791 
1792 #ifndef GPAC_DISABLE_3D
1793 
1794 	case GF_OPT_EMULATE_POW2:
1795 		compositor->epow2 = value;
1796 		break;
1797 	case GF_OPT_POLYGON_ANTIALIAS:
1798 		compositor->paa = value;
1799 		break;
1800 	case GF_OPT_BACK_CULL:
1801 		compositor->bcull = value;
1802 		break;
1803 	case GF_OPT_WIREFRAME:
1804 		compositor->wire = value;
1805 		break;
1806 	case GF_OPT_NORMALS:
1807 		compositor->norms = value;
1808 		break;
1809 #ifdef GPAC_HAS_GLU
1810 	case GF_OPT_RASTER_OUTLINES:
1811 		compositor->linegl = value;
1812 		break;
1813 #endif
1814 
1815 	case GF_OPT_NO_RECT_TEXTURE:
1816 		if (value != compositor->rext) {
1817 			compositor->rext = value;
1818 			/*RECT texture support - we must reload HW*/
1819 			gf_sc_reset_graphics(compositor);
1820 		}
1821 		break;
1822 	case GF_OPT_COLLISION:
1823 		compositor->collide_mode = value;
1824 		break;
1825 	case GF_OPT_GRAVITY:
1826 	{
1827 		GF_Camera *cam = compositor_3d_get_camera(compositor);
1828 		compositor->gravity_on = value;
1829 		/*force collision pass*/
1830 		cam->last_pos.z -= 1;
1831 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1832 	}
1833 	break;
1834 #endif
1835 
1836 	case GF_OPT_VIDEO_CACHE_SIZE:
1837 #ifdef GF_SR_USE_VIDEO_CACHE
1838 		compositor_set_cache_memory(compositor, 1024*value);
1839 #else
1840 		e = GF_NOT_SUPPORTED;
1841 #endif
1842 		break;
1843 	case GF_OPT_MULTIVIEW_MODE:
1844 		compositor->multiview_mode = value;
1845 		break;
1846 
1847 
1848 	default:
1849 		e = GF_BAD_PARAM;
1850 		break;
1851 	}
1852 	gf_sc_lock(compositor, 0);
1853 	return e;
1854 }
1855 
1856 GF_EXPORT
gf_sc_is_over(GF_Compositor * compositor,GF_SceneGraph * scene_graph)1857 Bool gf_sc_is_over(GF_Compositor *compositor, GF_SceneGraph *scene_graph)
1858 {
1859 	u32 i, count;
1860 	count = gf_list_count(compositor->time_nodes);
1861 	for (i=0; i<count; i++) {
1862 		GF_TimeNode *tn = (GF_TimeNode *)gf_list_get(compositor->time_nodes, i);
1863 		if (tn->needs_unregister) continue;
1864 
1865 		if (scene_graph && (gf_node_get_graph((GF_Node *)tn->udta) != scene_graph))
1866 			continue;
1867 
1868 		switch (gf_node_get_tag((GF_Node *)tn->udta)) {
1869 #ifndef GPAC_DISABLE_VRML
1870 		case TAG_MPEG4_TimeSensor:
1871 #endif
1872 #ifndef GPAC_DISABLE_X3D
1873 		case TAG_X3D_TimeSensor:
1874 #endif
1875 			return 0;
1876 
1877 #ifndef GPAC_DISABLE_VRML
1878 		case TAG_MPEG4_MovieTexture:
1879 #ifndef GPAC_DISABLE_X3D
1880 		case TAG_X3D_MovieTexture:
1881 #endif
1882 			if (((M_MovieTexture *)tn->udta)->loop) return 0;
1883 			break;
1884 		case TAG_MPEG4_AudioClip:
1885 #ifndef GPAC_DISABLE_X3D
1886 		case TAG_X3D_AudioClip:
1887 #endif
1888 			if (((M_AudioClip*)tn->udta)->loop) return 0;
1889 			break;
1890 		case TAG_MPEG4_AnimationStream:
1891 			if (((M_AnimationStream*)tn->udta)->loop) return 0;
1892 			break;
1893 #endif
1894 		}
1895 	}
1896 	/*FIXME - this does not work with SVG/SMIL*/
1897 	return 1;
1898 }
1899 
1900 GF_EXPORT
gf_sc_get_option(GF_Compositor * compositor,u32 type)1901 u32 gf_sc_get_option(GF_Compositor *compositor, u32 type)
1902 {
1903 	switch (type) {
1904 	case GF_OPT_PLAY_STATE:
1905 		return compositor->paused ? 1 : 0;
1906 	case GF_OPT_OVERRIDE_SIZE:
1907 		return (compositor->override_size_flags & 1) ? 1 : 0;
1908 	case GF_OPT_IS_FINISHED:
1909 		if (compositor->interaction_sensors) return 0;
1910 	case GF_OPT_IS_OVER:
1911 		return gf_sc_is_over(compositor, NULL);
1912 	case GF_OPT_STRESS_MODE:
1913 		return compositor->stress;
1914 	case GF_OPT_AUDIO_VOLUME:
1915 		return compositor->audio_renderer->volume;
1916 	case GF_OPT_AUDIO_PAN:
1917 		return compositor->audio_renderer->pan;
1918 	case GF_OPT_AUDIO_MUTE:
1919 		return compositor->audio_renderer->mute;
1920 
1921 	case GF_OPT_ANTIALIAS:
1922 		return compositor->aa;
1923 	case GF_OPT_HIGHSPEED:
1924 		return compositor->fast;
1925 	case GF_OPT_ASPECT_RATIO:
1926 		return compositor->aspect_ratio;
1927 	case GF_OPT_FULLSCREEN:
1928 		return compositor->fullscreen;
1929 	case GF_OPT_INTERACTION_LEVEL:
1930 		return compositor->interaction_level;
1931 	case GF_OPT_VISIBLE:
1932 		return !compositor->is_hidden;
1933 	case GF_OPT_FREEZE_DISPLAY:
1934 		return compositor->freeze_display;
1935 	case GF_OPT_TEXTURE_TEXT:
1936 		return compositor->textxt;
1937 	case GF_OPT_USE_OPENGL:
1938 		return compositor->force_opengl_2d;
1939 
1940 	case GF_OPT_DRAW_MODE:
1941 		if (compositor->traverse_state->immediate_draw) return GF_DRAW_MODE_IMMEDIATE;
1942 		if (compositor->debug_defer) return GF_DRAW_MODE_DEFER_DEBUG;
1943 		return GF_DRAW_MODE_DEFER;
1944 	case GF_OPT_SCALABLE_ZOOM:
1945 		return compositor->sz;
1946 	case GF_OPT_YUV_HARDWARE:
1947 		return compositor->yuvhw;
1948 	case GF_OPT_YUV_FORMAT:
1949 		return compositor->yuvhw ? compositor->video_out->yuv_pixel_format : 0;
1950 	case GF_OPT_NAVIGATION_TYPE:
1951 #ifndef GPAC_DISABLE_3D
1952 		if (compositor->navigation_disabled) return GF_NAVIGATE_TYPE_NONE;
1953 		if (compositor->visual->type_3d || compositor->active_layer) {
1954 			GF_Camera *cam = compositor_3d_get_camera(compositor);
1955 			if ((cam->navigation_flags & NAV_SELECTABLE)) return GF_NAVIGATE_TYPE_3D;
1956 			if (!(cam->navigation_flags & NAV_ANY)) return GF_NAVIGATE_TYPE_NONE;
1957 //			return ((cam->is_3D || compositor->active_layer) ? GF_NAVIGATE_TYPE_3D : GF_NAVIGATE_TYPE_2D);
1958 			return GF_NAVIGATE_TYPE_3D;
1959 		} else
1960 #endif
1961 		{
1962 			return GF_NAVIGATE_TYPE_2D;
1963 		}
1964 	case GF_OPT_NAVIGATION:
1965 #ifndef GPAC_DISABLE_3D
1966 		if (compositor->visual->type_3d || compositor->active_layer) {
1967 			GF_Camera *cam = compositor_3d_get_camera(compositor);
1968 			return cam->navigate_mode;
1969 		}
1970 #endif
1971 		return compositor->navigate_mode;
1972 
1973 	case GF_OPT_HEADLIGHT:
1974 		return 0;
1975 	case GF_OPT_COLLISION:
1976 		return GF_COLLISION_NONE;
1977 	case GF_OPT_GRAVITY:
1978 		return 0;
1979 
1980 	case GF_OPT_VIDEO_CACHE_SIZE:
1981 #ifdef GF_SR_USE_VIDEO_CACHE
1982 		return compositor->vcsize/1024;
1983 #else
1984 		return 0;
1985 #endif
1986 		break;
1987 	case GF_OPT_NUM_STEREO_VIEWS:
1988 #ifndef GPAC_DISABLE_3D
1989 		if (compositor->visual->type_3d) {
1990 			if (compositor->visual->nb_views && compositor->visual->autostereo_type > GF_3D_STEREO_LAST_SINGLE_BUFFER)
1991 				return compositor->visual->nb_views;
1992 		}
1993 #endif
1994 		return 1;
1995 
1996 	default:
1997 		return 0;
1998 	}
1999 }
2000 
2001 GF_EXPORT
gf_sc_map_point(GF_Compositor * compositor,s32 X,s32 Y,Fixed * bifsX,Fixed * bifsY)2002 void gf_sc_map_point(GF_Compositor *compositor, s32 X, s32 Y, Fixed *bifsX, Fixed *bifsY)
2003 {
2004 	/*coordinates are in user-like OS....*/
2005 	X = X - compositor->display_width/2;
2006 	Y = compositor->display_height/2 - Y;
2007 	*bifsX = INT2FIX(X);
2008 	*bifsY = INT2FIX(Y);
2009 }
2010 
2011 
2012 GF_EXPORT
gf_sc_get_screen_buffer(GF_Compositor * compositor,GF_VideoSurface * framebuffer,u32 depth_dump_mode)2013 GF_Err gf_sc_get_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer, u32 depth_dump_mode)
2014 {
2015 	GF_Err e;
2016 	if (!compositor || !framebuffer) return GF_BAD_PARAM;
2017 	gf_mx_p(compositor->mx);
2018 
2019 #ifndef GPAC_DISABLE_3D
2020 	if (compositor->visual->type_3d || compositor->hybrid_opengl)
2021 		e = compositor_3d_get_screen_buffer(compositor, framebuffer, depth_dump_mode);
2022 	else
2023 #endif
2024 		/*no depth dump in 2D mode*/
2025 		if (depth_dump_mode) e = GF_NOT_SUPPORTED;
2026 		else e = compositor->video_out->LockBackBuffer(compositor->video_out, framebuffer, 1);
2027 
2028 	if (e != GF_OK) gf_mx_v(compositor->mx);
2029 	return e;
2030 }
2031 
gf_sc_get_offscreen_buffer(GF_Compositor * compositor,GF_VideoSurface * framebuffer,u32 view_idx,GF_CompositorGrabMode depth_dump_mode)2032 GF_Err gf_sc_get_offscreen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer, u32 view_idx, GF_CompositorGrabMode depth_dump_mode)
2033 {
2034 	if (!compositor || !framebuffer) return GF_BAD_PARAM;
2035 #ifndef GPAC_DISABLE_3D
2036 	if (compositor->visual->type_3d && compositor->visual->nb_views && (compositor->visual->autostereo_type > GF_3D_STEREO_LAST_SINGLE_BUFFER)) {
2037 		GF_Err e;
2038 		gf_mx_p(compositor->mx);
2039 		e = compositor_3d_get_offscreen_buffer(compositor, framebuffer, view_idx, depth_dump_mode);
2040 		if (e != GF_OK) gf_mx_v(compositor->mx);
2041 		return e;
2042 	}
2043 #endif
2044 	return GF_BAD_PARAM;
2045 }
2046 
2047 
2048 GF_EXPORT
gf_sc_release_screen_buffer(GF_Compositor * compositor,GF_VideoSurface * framebuffer)2049 GF_Err gf_sc_release_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer)
2050 {
2051 	GF_Err e;
2052 	if (!compositor || !framebuffer) return GF_BAD_PARAM;
2053 
2054 #ifndef GPAC_DISABLE_3D
2055 	if (compositor->visual->type_3d || compositor->hybrid_opengl)
2056 		e = compositor_3d_release_screen_buffer(compositor, framebuffer);
2057 	else
2058 #endif
2059 		e = compositor->video_out->LockBackBuffer(compositor->video_out, framebuffer, 0);
2060 
2061 	gf_mx_v(compositor->mx);
2062 	return e;
2063 }
2064 
2065 GF_EXPORT
gf_sc_get_fps(GF_Compositor * compositor,Bool absoluteFPS)2066 Double gf_sc_get_fps(GF_Compositor *compositor, Bool absoluteFPS)
2067 {
2068 	Double fps;
2069 	u32 fidx, num, frames, run_time;
2070 
2071 	gf_mx_p(compositor->mx);
2072 
2073 	if (absoluteFPS) {
2074 		/*start from last frame and get first frame time*/
2075 		fidx = compositor->current_frame;
2076 		frames = 0;
2077 		run_time = compositor->frame_dur[fidx];
2078 		for (num=0; num<GF_SR_FPS_COMPUTE_SIZE; num++) {
2079 			run_time += compositor->frame_dur[fidx];
2080 			frames++;
2081 			if (frames==GF_SR_FPS_COMPUTE_SIZE) break;
2082 			if (!fidx) {
2083 				fidx = GF_SR_FPS_COMPUTE_SIZE-1;
2084 			} else {
2085 				fidx--;
2086 			}
2087 		}
2088 	} else {
2089 		/*start from last frame and get first frame time*/
2090 		fidx = compositor->current_frame;
2091 		run_time = compositor->frame_time[fidx];
2092 		fidx = (fidx+1)% GF_SR_FPS_COMPUTE_SIZE;
2093 		assert(run_time >= compositor->frame_time[fidx]);
2094 		run_time -= compositor->frame_time[fidx];
2095 		frames = GF_SR_FPS_COMPUTE_SIZE-1;
2096 	}
2097 
2098 
2099 	gf_mx_v(compositor->mx);
2100 
2101 	if (!run_time) return ((Double) compositor->fps.num)/compositor->fps.den;
2102 	fps = 1000*frames;
2103 	fps /= run_time;
2104 	return fps;
2105 }
2106 
2107 GF_EXPORT
gf_sc_register_time_node(GF_Compositor * compositor,GF_TimeNode * tn)2108 void gf_sc_register_time_node(GF_Compositor *compositor, GF_TimeNode *tn)
2109 {
2110 	/*may happen with DEF/USE */
2111 	if (tn->is_registered) return;
2112 	if (tn->needs_unregister) return;
2113 	gf_list_add(compositor->time_nodes, tn);
2114 	tn->is_registered = 1;
2115 }
2116 
2117 GF_EXPORT
gf_sc_unregister_time_node(GF_Compositor * compositor,GF_TimeNode * tn)2118 void gf_sc_unregister_time_node(GF_Compositor *compositor, GF_TimeNode *tn)
2119 {
2120 	gf_list_del_item(compositor->time_nodes, tn);
2121 }
2122 
gf_sc_recompute_ar(GF_Compositor * compositor,GF_Node * top_node)2123 static void gf_sc_recompute_ar(GF_Compositor *compositor, GF_Node *top_node)
2124 {
2125 	Bool force_pause;
2126 
2127 //	force_pause = compositor->audio_renderer->Frozen ? 0 : 1;
2128 	force_pause = GF_FALSE;
2129 
2130 #ifndef GPAC_DISABLE_LOG
2131 	compositor->visual_config_time = 0;
2132 #endif
2133 	if (compositor->recompute_ar) {
2134 #ifndef GPAC_DISABLE_3D
2135 		u32 prev_type_3d = compositor->visual->type_3d;
2136 #endif
2137 #ifndef GPAC_DISABLE_LOG
2138 		u32 time=0;
2139 
2140 		if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) {
2141 			time = gf_sys_clock();
2142 		}
2143 #endif
2144 		if (force_pause)
2145 			gf_sc_ar_control(compositor->audio_renderer, GF_SC_AR_PAUSE);
2146 
2147 #ifndef GPAC_DISABLE_3D
2148 		if (compositor->autoconfig_opengl) {
2149 			compositor->visual->type_3d = 1;
2150 		}
2151 		if (compositor->visual->type_3d) {
2152 			compositor_3d_set_aspect_ratio(compositor);
2153 			gf_sc_load_opengl_extensions(compositor, compositor->visual->type_3d ? GF_TRUE : GF_FALSE);
2154 #ifndef GPAC_USE_GLES1X
2155 			visual_3d_init_shaders(compositor->visual);
2156 #endif
2157 			if (compositor->autoconfig_opengl) {
2158 				u32 mode = compositor->ogl;
2159 				compositor->autoconfig_opengl = 0;
2160 				compositor->force_opengl_2d = 0;
2161 				compositor->hybrid_opengl = GF_FALSE;
2162 				compositor->visual->type_3d = prev_type_3d;
2163 
2164 #if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
2165 				//enable hybrid mode by default
2166 				if (compositor->visual->compositor->shader_mode_disabled && (mode==GF_SC_GLMODE_HYBRID) ) {
2167 					mode = GF_SC_GLMODE_OFF;
2168 				}
2169 #endif
2170 				switch (mode) {
2171 				case GF_SC_GLMODE_OFF:
2172 					break;
2173 				case GF_SC_GLMODE_ON:
2174 					compositor->force_opengl_2d = 1;
2175 					if (!compositor->visual->type_3d)
2176 						compositor->visual->type_3d = 1;
2177 					break;
2178 				case GF_SC_GLMODE_AUTO:
2179 				case GF_SC_GLMODE_HYBRID:
2180 					compositor->hybrid_opengl = GF_TRUE;
2181 					break;
2182 				}
2183 			}
2184 
2185 		}
2186 		if (!compositor->visual->type_3d)
2187 #endif
2188 		{
2189 			compositor_2d_set_aspect_ratio(compositor);
2190 #ifndef GPAC_DISABLE_3D
2191 			if (compositor->hybrid_opengl) {
2192 				gf_sc_load_opengl_extensions(compositor, GF_TRUE);
2193 #ifndef GPAC_USE_GLES1X
2194 				visual_3d_init_shaders(compositor->visual);
2195 #endif
2196 				if (!compositor->visual->hybgl_drawn.list) {
2197 					ra_init(&compositor->visual->hybgl_drawn);
2198 				}
2199 			}
2200 #endif
2201 		}
2202 
2203 		if (force_pause )
2204 			gf_sc_ar_control(compositor->audio_renderer, GF_SC_AR_RESUME);
2205 
2206 #ifndef GPAC_DISABLE_LOG
2207 		if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) {
2208 			compositor->visual_config_time = gf_sys_clock() - time;
2209 		}
2210 #endif
2211 
2212 #ifndef GPAC_DISABLE_VRML
2213 		compositor_evaluate_envtests(compositor, 0);
2214 #endif
2215 		//fullscreen was postponed, retry now that the AR has been recomputed
2216 		if (compositor->fullscreen_postponed) {
2217 			compositor->fullscreen_postponed = 0;
2218 			compositor->msg_type |= GF_SR_CFG_FULLSCREEN;
2219 		}
2220 
2221 		if (compositor->vout) {
2222 			gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_WIDTH, &PROP_UINT(compositor->output_width) );
2223 			gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_HEIGHT, &PROP_UINT(compositor->output_height) );
2224 			gf_filter_pid_set_property(compositor->vout, GF_PROP_PID_FPS, &PROP_FRAC(compositor->fps) );
2225 		}
2226 	}
2227 }
2228 
2229 
2230 
gf_sc_setup_root_visual(GF_Compositor * compositor,GF_Node * top_node)2231 static void gf_sc_setup_root_visual(GF_Compositor *compositor, GF_Node *top_node)
2232 {
2233 	if (top_node && !compositor->root_visual_setup) {
2234 		GF_SceneGraph *scene = compositor->scene;
2235 		u32 node_tag;
2236 #ifndef GPAC_DISABLE_3D
2237 		Bool force_navigate=0;
2238 		Bool was_3d = compositor->visual->type_3d;
2239 #endif
2240 		compositor->root_visual_setup = 1;
2241 		compositor->visual->center_coords = 1;
2242 
2243 		compositor->traverse_state->pixel_metrics = 1;
2244 		compositor->traverse_state->min_hsize = INT2FIX(MIN(compositor->scene_width, compositor->scene_height)) / 2;
2245 
2246 		node_tag = gf_node_get_tag(top_node);
2247 		switch (node_tag) {
2248 
2249 #ifndef GPAC_DISABLE_VRML
2250 		case TAG_MPEG4_OrderedGroup:
2251 		case TAG_MPEG4_Layer2D:
2252 #ifndef GPAC_DISABLE_3D
2253 			/*move to perspective 3D when simulating depth*/
2254 #ifdef GF_SR_USE_DEPTH
2255 			if (compositor->dispdepth) {
2256 				compositor->visual->type_3d = 0;
2257 				compositor->visual->camera.is_3D = 0;
2258 			} else
2259 #endif
2260 			{
2261 				compositor->visual->type_3d = 0;
2262 				compositor->visual->camera.is_3D = 0;
2263 			}
2264 #endif
2265 			compositor->traverse_state->pixel_metrics = gf_sg_use_pixel_metrics(scene);
2266 			break;
2267 		case TAG_MPEG4_Group:
2268 		case TAG_MPEG4_Layer3D:
2269 #ifndef GPAC_DISABLE_3D
2270 			compositor->visual->type_3d = 2;
2271 			compositor->visual->camera.is_3D = 1;
2272 #endif
2273 			compositor->traverse_state->pixel_metrics = gf_sg_use_pixel_metrics(scene);
2274 			break;
2275 #ifndef GPAC_DISABLE_X3D
2276 		case TAG_X3D_Group:
2277 #ifndef GPAC_DISABLE_3D
2278 			compositor->visual->type_3d = 3;
2279 #endif
2280 			compositor->traverse_state->pixel_metrics = gf_sg_use_pixel_metrics(scene);
2281 			break;
2282 #endif /*GPAC_DISABLE_X3D*/
2283 
2284 
2285 
2286 #endif /*GPAC_DISABLE_VRML*/
2287 
2288 #ifndef GPAC_DISABLE_SVG
2289 		case TAG_SVG_svg:
2290 #ifndef GPAC_DISABLE_3D
2291 #ifdef GF_SR_USE_DEPTH
2292 			if (compositor->dispdepth>=0) {
2293 				compositor->visual->type_3d = 2;
2294 				compositor->visual->camera.is_3D = 1;
2295 			} else
2296 #endif
2297 			{
2298 				compositor->visual->type_3d = 0;
2299 				compositor->visual->camera.is_3D = 0;
2300 			}
2301 #endif
2302 			compositor->visual->center_coords = 0;
2303 			compositor->root_visual_setup = 2;
2304 			break;
2305 #endif /*GPAC_DISABLE_SVG*/
2306 		}
2307 
2308 		/*!! by default we don't set the focus on the content - this is conform to SVG and avoids displaying the
2309 		focus box for MPEG-4 !! */
2310 
2311 		/*setup OpenGL & camera mode*/
2312 #ifndef GPAC_DISABLE_3D
2313 		if (compositor->inherit_type_3d && !compositor->visual->type_3d) {
2314 			compositor->visual->type_3d = 2;
2315 			compositor->visual->camera.is_3D = 1;
2316 		}
2317 		/*request for OpenGL drawing in 2D*/
2318 		else if ( (compositor->force_opengl_2d && !compositor->visual->type_3d)
2319 		|| (compositor->hybrid_opengl && compositor->force_type_3d)) {
2320 
2321 			compositor->force_type_3d=0;
2322 			compositor->visual->type_3d = 1;
2323 			if (compositor->force_opengl_2d==2) force_navigate=1;
2324 		}
2325 
2326 		if (compositor->drv==GF_SC_DRV_AUTO)
2327 			gf_sc_check_video_driver(compositor);
2328 
2329 		if (! (compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL)) {
2330 			compositor->visual->type_3d = 0;
2331 			compositor->visual->camera.is_3D = 0;
2332 		}
2333 		compositor->visual->camera.is_3D = (compositor->visual->type_3d>1) ? 1 : 0;
2334 		if (!compositor->visual->camera.is_3D)
2335 			camera_set_2d(&compositor->visual->camera);
2336 
2337 		camera_invalidate(&compositor->visual->camera);
2338 		if (force_navigate) {
2339 			compositor->visual->camera.navigate_mode = GF_NAVIGATE_EXAMINE;
2340 			compositor->visual->camera.had_nav_info = 0;
2341 		}
2342 #endif
2343 
2344 		GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Main scene setup - pixel metrics %d - center coords %d\n", compositor->traverse_state->pixel_metrics, compositor->visual->center_coords));
2345 
2346 		compositor->recompute_ar = 1;
2347 #ifndef GPAC_DISABLE_3D
2348 		/*change in 2D/3D config, force AR recompute/video restup*/
2349 		if (was_3d != compositor->visual->type_3d) compositor->recompute_ar = was_3d ? 1 : 2;
2350 #endif
2351 	}
2352 }
2353 
2354 
gf_sc_draw_scene(GF_Compositor * compositor)2355 static Bool gf_sc_draw_scene(GF_Compositor *compositor)
2356 {
2357 	u32 flags;
2358 
2359 	GF_Node *top_node = gf_sg_get_root_node(compositor->scene);
2360 
2361 	if (!top_node && !compositor->visual->last_had_back && !compositor->visual->cur_context) {
2362 		//GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Scene has no root node, nothing to draw\n"));
2363 		//nothing to draw, skip flush
2364 		compositor->skip_flush = 1;
2365 		return GF_FALSE;
2366 	}
2367 
2368 #ifdef GF_SR_USE_VIDEO_CACHE
2369 	if (!compositor->vcsize)
2370 		compositor->traverse_state->in_group_cache = 1;
2371 #endif
2372 
2373 	flags = compositor->traverse_state->immediate_draw;
2374 	if (compositor->video_setup_failed) {
2375 		compositor->skip_flush = 1;
2376 	}
2377 	else {
2378 		if (compositor->nodes_pending) {
2379 			u32 i, count, n_count;
2380 			i=0;
2381 			count = gf_list_count(compositor->nodes_pending);
2382 			while (i<count) {
2383 				GF_Node *n = (GF_Node *)gf_list_get(compositor->nodes_pending, i);
2384 				gf_node_traverse(n, NULL);
2385 				if (!compositor->nodes_pending) break;
2386 				n_count = gf_list_count(compositor->nodes_pending);
2387 				if (n_count==count) i++;
2388 				else count=n_count;
2389 			}
2390 		}
2391 		if (compositor->passthrough_txh) {
2392 			gf_sc_setup_passthrough(compositor);
2393 			compositor->traverse_state->immediate_draw = 1;
2394 		}
2395 
2396 		if (! visual_draw_frame(compositor->visual, top_node, compositor->traverse_state, 1)) {
2397 			/*android backend uses opengl without telling it to us, we need an ugly hack here ...*/
2398 #ifdef GPAC_CONFIG_ANDROID
2399 			compositor->skip_flush = 0;
2400 #else
2401 			if (compositor->skip_flush==2) {
2402 				compositor->skip_flush = 0;
2403 			} else {
2404 				compositor->skip_flush = 1;
2405 			}
2406 #endif
2407 		}
2408 
2409 	}
2410 
2411 
2412 	compositor->traverse_state->immediate_draw = flags;
2413 #ifdef GF_SR_USE_VIDEO_CACHE
2414 	gf_list_reset(compositor->cached_groups_queue);
2415 #endif
2416 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Frame %d - drawing done\n", compositor->frame_number));
2417 
2418 	/*only send the resize notification once the frame has been dra*/
2419 	if (compositor->recompute_ar) {
2420 		compositor_send_resize_event(compositor, NULL, 0, 0, 0, 1);
2421 		compositor->recompute_ar = 0;
2422 	}
2423 	compositor->zoom_changed = 0;
2424 	compositor->audio_renderer->scene_ready = GF_TRUE;
2425 	return GF_TRUE;
2426 }
2427 
2428 
2429 #ifndef GPAC_DISABLE_LOG
2430 //defined in smil_anim ...
2431 extern u32 time_spent_in_anim;
2432 #endif
2433 
compositor_release_textures(GF_Compositor * compositor,Bool frame_drawn)2434 static void compositor_release_textures(GF_Compositor *compositor, Bool frame_drawn)
2435 {
2436 	u32 i, count;
2437 	/*release all textures - we must release them to handle a same OD being used by several textures*/
2438 	count = gf_list_count(compositor->textures);
2439 	for (i=0; i<count; i++) {
2440 		GF_TextureHandler *txh = (GF_TextureHandler *)gf_list_get(compositor->textures, i);
2441 		gf_sc_texture_release_stream(txh);
2442 		if (frame_drawn && txh->tx_io && !(txh->flags & GF_SR_TEXTURE_USED))
2443 			gf_sc_texture_reset(txh);
2444 		/*remove the use flag*/
2445 		txh->flags &= ~GF_SR_TEXTURE_USED;
2446 	}
2447 }
2448 
2449 
gf_sc_render_frame(GF_Compositor * compositor)2450 void gf_sc_render_frame(GF_Compositor *compositor)
2451 {
2452 #ifndef GPAC_DISABLE_SCENEGRAPH
2453 	GF_SceneGraph *sg;
2454 #endif
2455 	GF_List *temp_queue;
2456 	u32 in_time, end_time, i, count, frame_duration, frame_ts, frame_draw_type_bck;
2457 	Bool frame_drawn, has_timed_nodes=GF_FALSE, has_tx_streams=GF_FALSE, all_tx_done=GF_TRUE;
2458 
2459 #ifndef GPAC_DISABLE_LOG
2460 	s32 event_time, route_time, smil_timing_time=0, time_node_time, texture_time, traverse_time, flush_time, txtime;
2461 #endif
2462 
2463 	/*lock compositor for the whole cycle*/
2464 	gf_sc_lock(compositor, 1);
2465 //	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Entering render_frame \n"));
2466 
2467 	in_time = gf_sys_clock();
2468 
2469 	gf_sc_texture_cleanup_hw(compositor);
2470 
2471 	/*first thing to do, let the video output handle user event if it is not threaded*/
2472 	compositor->video_out->ProcessEvent(compositor->video_out, NULL);
2473 
2474 	//handle all unthreaded extensions
2475 	if (compositor->unthreaded_extensions) {
2476 		count = gf_list_count(compositor->unthreaded_extensions);
2477 		for (i=0; i<count; i++) {
2478 			GF_CompositorExt *ifce = gf_list_get(compositor->unthreaded_extensions, i);
2479 			ifce->process(ifce, GF_COMPOSITOR_EXT_PROCESS, NULL);
2480 		}
2481 	}
2482 
2483 	if (compositor->freeze_display) {
2484 		gf_sc_lock(compositor, 0);
2485 		if (!compositor->bench_mode && compositor->player) {
2486 			compositor->scene_sampled_clock = gf_sc_ar_get_clock(compositor->audio_renderer);
2487 		}
2488 		return;
2489 	}
2490 
2491 	gf_sc_reconfig_task(compositor);
2492 
2493 	/* if there is no scene*/
2494 	if (!compositor->scene && !gf_list_count(compositor->extra_scenes) ) {
2495 		gf_sc_draw_scene(compositor);
2496 		if (compositor->player) {
2497 			//increase clock in bench mode before releasing mutex
2498 			if (compositor->bench_mode && (compositor->force_bench_frame==1)) {
2499 				compositor->scene_sampled_clock += compositor->frame_duration;
2500 			}
2501 		} else {
2502 			if (compositor->root_scene && compositor->root_scene->graph_attached) {
2503 				compositor->check_eos_state = 2;
2504 			}
2505 		}
2506 		gf_sc_lock(compositor, 0);
2507 		compositor->force_bench_frame=0;
2508 		compositor->frame_draw_type = 0;
2509 		compositor->recompute_ar = 0;
2510 		compositor->ms_until_next_frame = compositor->frame_duration;
2511 		return;
2512 	}
2513 
2514 	if (compositor->reset_graphics) {
2515 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
2516 		visual_reset_graphics(compositor->visual);
2517 
2518 #ifndef GPAC_DISABLE_3D
2519 		compositor_3d_delete_fbo(&compositor->fbo_id, &compositor->fbo_tx_id, &compositor->fbo_depth_id, compositor->external_tx_id);
2520 #endif
2521 
2522 	}
2523 
2524 	/*process pending user events*/
2525 #ifndef GPAC_DISABLE_LOG
2526 	event_time = gf_sys_clock();
2527 #endif
2528 	//swap event queus
2529 	gf_mx_p(compositor->evq_mx);
2530 	temp_queue = compositor->event_queue;
2531 	compositor->event_queue = compositor->event_queue_back;
2532 	compositor->event_queue_back = temp_queue;
2533 	gf_mx_v(compositor->evq_mx);
2534 	while (gf_list_count(compositor->event_queue_back)) {
2535 		GF_QueuedEvent *qev = (GF_QueuedEvent*)gf_list_get(compositor->event_queue_back, 0);
2536 		gf_list_rem(compositor->event_queue_back, 0);
2537 
2538 		if (qev->target) {
2539 #ifndef GPAC_DISABLE_SVG
2540 			gf_sg_fire_dom_event(qev->target, &qev->dom_evt, qev->sg, NULL);
2541 #endif
2542 		} else if (qev->node) {
2543 #ifndef GPAC_DISABLE_SVG
2544 			gf_dom_event_fire(qev->node, &qev->dom_evt);
2545 #endif
2546 		} else {
2547 			gf_sc_exec_event(compositor, &qev->evt);
2548 		}
2549 		gf_free(qev);
2550 	}
2551 #ifndef GPAC_DISABLE_LOG
2552 	event_time = gf_sys_clock() - event_time;
2553 #endif
2554 
2555 	if (compositor->player) {
2556 		if (!compositor->bench_mode) {
2557 			compositor->scene_sampled_clock = gf_sc_ar_get_clock(compositor->audio_renderer);
2558 		} else {
2559 			if (compositor->force_bench_frame==1) {
2560 				//a system frame is pending on a future frame - we must increase our time
2561 				compositor->scene_sampled_clock += compositor->frame_duration;
2562 			}
2563 			compositor->force_bench_frame = 0;
2564 		}
2565 	}
2566 
2567 
2568 	//first update all natural textures to figure out timing
2569 	compositor->frame_delay = 0;
2570 	compositor->ms_until_next_frame = GF_INT_MAX;
2571 	frame_duration = compositor->frame_duration;
2572 
2573 	if (compositor->auto_rotate)
2574 		compositor_handle_auto_navigation(compositor);
2575 
2576 	/*first update time nodes since they may trigger play/stop request*/
2577 
2578 	compositor->force_late_frame_draw = GF_FALSE;
2579 
2580 #ifndef GPAC_DISABLE_SVG
2581 
2582 #ifndef GPAC_DISABLE_LOG
2583 	smil_timing_time = gf_sys_clock();
2584 #endif
2585 	if (gf_smil_notify_timed_elements(compositor->scene)) {
2586 		gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
2587 	}
2588 #ifndef GPAC_DISABLE_SCENEGRAPH
2589 	i = 0;
2590 	while ((sg = (GF_SceneGraph*)gf_list_enum(compositor->extra_scenes, &i))) {
2591 		if (gf_smil_notify_timed_elements(sg)) {
2592 			gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
2593 		}
2594 	}
2595 #endif
2596 
2597 #ifndef GPAC_DISABLE_LOG
2598 	smil_timing_time = gf_sys_clock() - smil_timing_time;
2599 #endif
2600 
2601 #endif //GPAC_DISABLE_SVG
2602 
2603 
2604 #ifndef GPAC_DISABLE_LOG
2605 	time_node_time = gf_sys_clock();
2606 #endif
2607 	/*update all timed nodes */
2608 	count = gf_list_count(compositor->time_nodes);
2609 	for (i=0; i<count; i++) {
2610 		GF_TimeNode *tn = (GF_TimeNode *)gf_list_get(compositor->time_nodes, i);
2611 		if (!tn->needs_unregister) tn->UpdateTimeNode(tn);
2612 		if (tn->needs_unregister) {
2613 			tn->is_registered = 0;
2614 			tn->needs_unregister = 0;
2615 			gf_list_rem(compositor->time_nodes, i);
2616 			i--;
2617 			count--;
2618 			continue;
2619 		}
2620 		has_timed_nodes = compositor->timed_nodes_valid;
2621 	}
2622 #ifndef GPAC_DISABLE_LOG
2623 	time_node_time = gf_sys_clock() - time_node_time;
2624 #endif
2625 
2626 
2627 	compositor->passthrough_txh = NULL;
2628 #ifndef GPAC_DISABLE_LOG
2629 	texture_time = gf_sys_clock();
2630 #endif
2631 	//compute earliest frame TS in all textures that need refresh (skip textures with same timing)
2632 	frame_ts = (u32) -1;
2633 
2634 	/*update all video textures*/
2635 	frame_draw_type_bck = compositor->frame_draw_type;
2636 	compositor->frame_draw_type = 0;
2637 	has_tx_streams = GF_FALSE;
2638 	count = gf_list_count(compositor->textures);
2639 	for (i=0; i<count; i++) {
2640 		GF_TextureHandler *txh = (GF_TextureHandler *)gf_list_get(compositor->textures, i);
2641 		if (!txh) break;
2642 		//this is not a natural (video) texture
2643 		if (! txh->stream) continue;
2644 		has_tx_streams = GF_TRUE;
2645 
2646 		/*signal graphics reset before updating*/
2647 		if (compositor->reset_graphics && txh->tx_io) gf_sc_texture_reset(txh);
2648 		txh->update_texture_fcnt(txh);
2649 
2650 		if (!txh->stream_finished) {
2651 			u32 d = gf_mo_get_min_frame_dur(txh->stream);
2652 			if (d && (d < frame_duration)) frame_duration = d;
2653 			//if the texture needs update (new frame), compute its timestamp in system timebase
2654 			if (txh->needs_refresh) {
2655 				u32 ts = gf_mo_map_timestamp_to_sys_clock(txh->stream, txh->stream->timestamp);
2656 				if (ts<frame_ts) frame_ts = ts;
2657 			}
2658 			all_tx_done=0;
2659 		}
2660 	}
2661 #ifndef GPAC_DISABLE_LOG
2662 	texture_time = gf_sys_clock() - texture_time;
2663 #endif
2664 	if (compositor->player) {
2665 		if (frame_draw_type_bck)
2666 			compositor->frame_draw_type = frame_draw_type_bck;
2667 	}
2668 	//in non player mode, don't redraw frame if due to end of streams on textures
2669 	else if (!frame_draw_type_bck && compositor->frame_draw_type && all_tx_done)
2670 		compositor->frame_draw_type = 0;
2671 
2672 
2673 	if (!compositor->player) {
2674 		if (compositor->passthrough_txh && compositor->passthrough_txh->frame_ifce && !compositor->passthrough_txh->frame_ifce->get_plane) {
2675 			compositor->passthrough_txh = NULL;
2676 		}
2677 		if (!compositor->passthrough_txh && frame_draw_type_bck)
2678 			compositor->frame_draw_type = frame_draw_type_bck;
2679 
2680 		if (compositor->passthrough_txh && compositor->passthrough_txh->width && compositor->passthrough_txh->needs_refresh) {
2681 			compositor->scene_sampled_clock = compositor->passthrough_txh->last_frame_time/* + dur*/;
2682 		}
2683 		//we were buffering and are now no longer, update scene clock
2684 		if (compositor->passthrough_txh && compositor->passthrough_check_buffer && !gf_mo_is_buffering(compositor->passthrough_txh->stream)) {
2685 			u32 dur = gf_mo_get_min_frame_dur(compositor->passthrough_txh->stream);
2686 			compositor->passthrough_check_buffer = GF_FALSE;
2687 			compositor->scene_sampled_clock = compositor->passthrough_txh->last_frame_time + dur;
2688 		}
2689 	}
2690 
2691 	//it may happen that we have a reconfigure request at this stage, especially if updating one of the textures
2692 	//forced a relayout - do it right away
2693 	if (compositor->msg_type) {
2694 		gf_sc_lock(compositor, 0);
2695 		return;
2696 	}
2697 
2698 
2699 	if (compositor->focus_text_type) {
2700 		if (!compositor->caret_next_draw_time) {
2701 			compositor->caret_next_draw_time = gf_sys_clock();
2702 			compositor->show_caret = 1;
2703 		}
2704 		if (compositor->caret_next_draw_time <= compositor->last_frame_time) {
2705 			compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2706 			compositor->caret_next_draw_time+=500;
2707 			compositor->show_caret = !compositor->show_caret;
2708 			compositor->text_edit_changed = 1;
2709 		}
2710 	}
2711 
2712 
2713 #ifndef GPAC_DISABLE_VRML
2714 	/*execute all routes:
2715 		before updating composite textures, otherwise nodes inside composite texture may never see their dirty flag set
2716 		after updating timed nodes to trigger all events based on time
2717 	*/
2718 #ifndef GPAC_DISABLE_LOG
2719 	route_time = gf_sys_clock();
2720 #endif
2721 
2722 	gf_sg_activate_routes(compositor->scene);
2723 
2724 #ifndef GPAC_DISABLE_SCENEGRAPH
2725 	i = 0;
2726 	while ((sg = (GF_SceneGraph*)gf_list_enum(compositor->extra_scenes, &i))) {
2727 		gf_sg_activate_routes(sg);
2728 	}
2729 #endif
2730 
2731 #ifndef GPAC_DISABLE_LOG
2732 	route_time = gf_sys_clock() - route_time;
2733 #endif
2734 
2735 #else
2736 #ifndef GPAC_DISABLE_LOG
2737 	route_time = 0;
2738 #endif
2739 #endif /*GPAC_DISABLE_VRML*/
2740 
2741 
2742 	/*setup root visual BEFORE updating the composite textures (since they may depend on root setup)*/
2743 	gf_sc_setup_root_visual(compositor, gf_sg_get_root_node(compositor->scene));
2744 
2745 	/*setup display before updating composite textures (some may require a valid openGL context)*/
2746 	gf_sc_recompute_ar(compositor, gf_sg_get_root_node(compositor->scene) );
2747 
2748 	if (compositor->video_setup_failed)	{
2749 		gf_sc_lock(compositor, 0);
2750 		return;
2751 	}
2752 
2753 #ifndef GPAC_DISABLE_LOG
2754 	txtime = gf_sys_clock();
2755 #endif
2756 	/*update all composite textures*/
2757 	compositor->texture_inserted = GF_FALSE;
2758 	count = gf_list_count(compositor->textures);
2759 	for (i=0; i<count; i++) {
2760 		GF_TextureHandler *txh = (GF_TextureHandler *)gf_list_get(compositor->textures, i);
2761 		if (!txh) break;
2762 		//this is not a composite texture
2763 		if (txh->stream) continue;
2764 		/*signal graphics reset before updating*/
2765 		if (compositor->reset_graphics && txh->tx_io) gf_sc_texture_reset(txh);
2766 		txh->update_texture_fcnt(txh);
2767 		if (compositor->texture_inserted) {
2768 			compositor->texture_inserted = GF_FALSE;
2769 			count = gf_list_count(compositor->textures);
2770 			i = gf_list_find(compositor->textures, txh);
2771 		}
2772 	}
2773 
2774 	//it may happen that we have a reconfigure request at this stage, especially if updating one of the textures update
2775 	//forced a relayout - do it right away
2776 	if (compositor->msg_type) {
2777 		//reset AR recompute flag, it will be reset when msg is handled
2778 		compositor->recompute_ar = 0;
2779 		gf_sc_lock(compositor, 0);
2780 		return;
2781 	}
2782 #ifndef GPAC_DISABLE_LOG
2783 	texture_time += gf_sys_clock() - txtime;
2784 #endif
2785 
2786 	compositor->text_edit_changed = 0;
2787 	compositor->rebuild_offscreen_textures = 0;
2788 
2789 	if (compositor->force_next_frame_redraw) {
2790 		compositor->force_next_frame_redraw=0;
2791 		compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2792 	}
2793 
2794 	//if hidden and player mode, do not draw the scene
2795 	if (compositor->is_hidden && compositor->player) {
2796 		compositor->frame_draw_type = 0;
2797 	}
2798 
2799 	if (!compositor->player) {
2800 		if (compositor->check_eos_state<=1) {
2801 			compositor->check_eos_state = 0;
2802 			/*check if we have to force a frame dispatch */
2803 
2804 			//no passthrough texture
2805 			if (!compositor->passthrough_txh) {
2806 				if (!compositor->vfr) {
2807 					//in CFR and no texture associated, always force a redraw
2808 					if (!has_tx_streams && has_timed_nodes)
2809 						compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2810 					//otherwise if texture(s) but not all done, force a redraw
2811 					else if (!all_tx_done)
2812 						compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2813 					//we still have active system pids (BIFS/LASeR/DIMS commands), force a redraw
2814 					if (gf_list_count(compositor->systems_pids))
2815 						compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2816 					//validator always triggers redraw to make sure the scene clock is incremented so that events can be fired
2817 					if (compositor->validator_mode)
2818 						compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2819 				}
2820 			}
2821 			//we have a passthrough texture, only generate an output frame when we have a new input on the passthrough
2822 			else {
2823 				//still waiting for initialization either from the passthrough stream or a texture used in the scene
2824 				if (!compositor->passthrough_txh->width || !compositor->passthrough_txh->stream->pck || (compositor->ms_until_next_frame<0) ) {
2825 					compositor->frame_draw_type = GF_SC_DRAW_NONE;
2826 					//prevent release of frame
2827 					if (compositor->passthrough_txh->needs_release)
2828 						compositor->passthrough_txh->needs_release = 2;
2829 				}
2830 				//done
2831 				else if ((compositor->passthrough_txh->stream_finished) && (compositor->ms_until_next_frame >= 0)) {
2832 					compositor->check_eos_state = 2;
2833 				}
2834 			}
2835 		}
2836 
2837 	}
2838 
2839 	frame_drawn = (compositor->frame_draw_type==GF_SC_DRAW_FRAME) ? 1 : 0;
2840 
2841 	/*if invalidated, draw*/
2842 	if (compositor->frame_draw_type) {
2843 		Bool emit_frame = GF_FALSE;
2844 		Bool textures_released = 0;
2845 
2846 #ifndef GPAC_DISABLE_LOG
2847 		traverse_time = gf_sys_clock();
2848 		time_spent_in_anim = 0;
2849 #endif
2850 
2851 		if (compositor->traverse_state->immediate_draw) {
2852 			compositor->frame_draw_type = GF_SC_DRAW_FRAME;
2853 		}
2854 
2855 		/*video flush only*/
2856 		if (compositor->frame_draw_type==GF_SC_DRAW_FLUSH) {
2857 			compositor->frame_draw_type = 0;
2858 		}
2859 		/*full render*/
2860 		else {
2861 			Bool scene_drawn;
2862 			compositor->frame_draw_type = 0;
2863 
2864 			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Redrawing scene - STB %d\n", compositor->scene_sampled_clock));
2865 
2866 			scene_drawn = gf_sc_draw_scene(compositor);
2867 
2868 #ifndef GPAC_DISABLE_LOG
2869 			traverse_time = gf_sys_clock() - traverse_time;
2870 #endif
2871 
2872 			if (compositor->ms_until_next_frame < 0) {
2873 				emit_frame = GF_FALSE;
2874 				compositor->last_error = GF_OK;
2875 			}
2876 			else if (!scene_drawn) emit_frame = GF_FALSE;
2877 			else if (compositor->frame_draw_type) emit_frame = GF_FALSE;
2878 			else if (compositor->fonts_pending>0) emit_frame = GF_FALSE;
2879 			else emit_frame = GF_TRUE;
2880 		}
2881 		/*and flush*/
2882 #ifndef GPAC_DISABLE_LOG
2883 		flush_time = gf_sys_clock();
2884 #endif
2885 
2886 		if (compositor->init_flags & GF_TERM_INIT_HIDE)
2887 			compositor->skip_flush = 1;
2888 
2889 		//new frame generated, emit packet
2890 		if (emit_frame) {
2891 			u64 pck_frame_ts=0;
2892 			GF_FilterPacket *pck;
2893 			frame_ts = 0;
2894 			if (compositor->passthrough_pck) {
2895 				pck = compositor->passthrough_pck;
2896 				compositor->passthrough_pck = NULL;
2897 				pck_frame_ts = gf_filter_pck_get_cts(pck);
2898 			} else {
2899 				if (compositor->video_out==&raw_vout) {
2900 					pck = gf_filter_pck_new_shared(compositor->vout, compositor->framebuffer, compositor->framebuffer_size, gf_sc_frame_ifce_done);
2901 				} else {
2902 					compositor->frame_ifce.user_data = compositor;
2903 					compositor->frame_ifce.get_plane = gf_sc_frame_ifce_get_plane;
2904 					compositor->frame_ifce.get_gl_texture = NULL;
2905 #ifndef GPAC_DISABLE_3D
2906 					if (compositor->fbo_tx_id) {
2907 						compositor->frame_ifce.get_gl_texture = gf_sc_frame_ifce_get_gl_texture;
2908 					}
2909 #endif
2910 					compositor->frame_ifce.flags = GF_FRAME_IFCE_BLOCKING;
2911 					pck = gf_filter_pck_new_frame_interface(compositor->vout, &compositor->frame_ifce, gf_sc_frame_ifce_done);
2912 				}
2913 
2914 				if (compositor->passthrough_txh) {
2915 					gf_filter_pck_merge_properties(compositor->passthrough_txh->stream->pck, pck);
2916 					pck_frame_ts = gf_filter_pck_get_cts(compositor->passthrough_txh->stream->pck);
2917 				} else {
2918 					//no texture found/updated, use scene sampled clock
2919 					if (compositor->timescale) {
2920 						frame_ts = compositor->frame_number * compositor->fps.den * compositor->timescale;
2921 						frame_ts /= compositor->fps.num;
2922 					} else {
2923 						frame_ts = compositor->frame_number * compositor->fps.den;
2924 					}
2925 					gf_filter_pck_set_cts(pck, frame_ts);
2926 				}
2927 			}
2928 			if (pck_frame_ts) {
2929 				u64 ts = pck_frame_ts;
2930 				ts *= 1000;
2931 				ts /= compositor->passthrough_timescale;
2932 				frame_ts = (u32) ts;
2933 			}
2934 			gf_filter_pck_send(pck);
2935 			gf_sc_ar_update_video_clock(compositor->audio_renderer, frame_ts);
2936 
2937 			if (!compositor->player) {
2938 				if (compositor->passthrough_txh) {
2939 					// update clock if not buffering
2940 					if (!gf_mo_is_buffering(compositor->passthrough_txh->stream))
2941 					{
2942 						u64 next_frame;
2943 						next_frame = pck_frame_ts + gf_filter_pck_get_duration(pck);
2944 						next_frame *= 1000;
2945 						next_frame /= compositor->passthrough_timescale;
2946 						assert(next_frame>=compositor->scene_sampled_clock);
2947 						compositor->scene_sampled_clock = (u32) next_frame;
2948 					}
2949 					// if buffering, remember to update clock at next frame
2950 					else {
2951 						compositor->passthrough_check_buffer = GF_TRUE;
2952 					}
2953 					if (compositor->passthrough_txh->stream_finished)
2954 						compositor->check_eos_state = 2;
2955 				} else {
2956 					u64 res;
2957 					compositor->last_frame_ts = frame_ts;
2958 					res = (compositor->frame_number+1);
2959 					res *= compositor->fps.den;
2960 					res *= 1000;
2961 					res /= compositor->fps.num;
2962 					assert(res>=compositor->scene_sampled_clock);
2963 					compositor->scene_sampled_clock = (u32) res;
2964 				}
2965 				GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Send video frame TS %u (%u ms) - next frame scene clock %d ms - passthrough %p\n", frame_ts, (frame_ts*1000)/compositor->fps.num, compositor->scene_sampled_clock, compositor->passthrough_txh));
2966 			} else {
2967 				GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Send video frame TS %u - AR clock %d\n", frame_ts, compositor->audio_renderer->current_time));
2968 			 	if (compositor->bench_mode) {
2969 					compositor->force_bench_frame = 1;
2970 				}
2971 				compositor->frame_was_produced = GF_TRUE;
2972 			}
2973 		} else if (!compositor->player) {
2974 			frame_drawn = GF_FALSE;
2975 			//prevent release of frame
2976 			if (compositor->passthrough_txh && compositor->passthrough_txh->needs_release)
2977 				compositor->passthrough_txh->needs_release = 2;
2978 		}
2979 
2980 		if (compositor->passthrough_pck) {
2981 		 	gf_filter_pck_discard(compositor->passthrough_pck);
2982 		 	compositor->passthrough_pck = NULL;
2983 		}
2984 
2985 		//if no overlays, release textures before flushing, otherwise we might loose time waiting for vsync
2986 		if (!compositor->visual->has_overlays) {
2987 			compositor_release_textures(compositor, frame_drawn);
2988 			textures_released = 1;
2989 		}
2990 
2991 		//no flush pending
2992 		if (!compositor->flush_pending && !compositor->skip_flush) {
2993 			gf_sc_flush_video(compositor, GF_TRUE);
2994 		}
2995 
2996 #ifndef GPAC_DISABLE_LOG
2997 		flush_time = gf_sys_clock() - flush_time;
2998 		GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Compositor] done flushing frame in %d ms\n", flush_time));
2999 #endif
3000 
3001 		visual_2d_draw_overlays(compositor->visual);
3002 		compositor->last_had_overlays = compositor->visual->has_overlays;
3003 
3004 		if (!textures_released) {
3005 			GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Compositor] Releasing textures after flush\n" ));
3006 			compositor_release_textures(compositor, frame_drawn);
3007 		}
3008 
3009 		if (compositor->stress) {
3010 			gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
3011 			gf_sc_reset_graphics(compositor);
3012 		}
3013 		compositor->reset_fonts = 0;
3014 
3015 	} else {
3016 
3017 		//frame not drawn, release textures
3018 		compositor_release_textures(compositor, frame_drawn);
3019 
3020 #ifndef GPAC_DISABLE_LOG
3021 		traverse_time = 0;
3022 		time_spent_in_anim = 0;
3023 		flush_time = 0;
3024 		compositor->traverse_setup_time = 0;
3025 		compositor->traverse_and_direct_draw_time = 0;
3026 		compositor->indirect_draw_time = 0;
3027 #endif
3028 
3029 		if (!compositor->player) {
3030 			//if systems frames are pending (not executed because too early), increase clock
3031 			if ((compositor->sys_frames_pending && (compositor->ms_until_next_frame>=0) )
3032 			//if timed nodes or validator mode (which acts as a timed node firing events), increase scene clock
3033 			//in vfr mode
3034 			|| (compositor->vfr && (has_timed_nodes || compositor->validator_mode) )
3035 			) {
3036 				u64 res;
3037 				compositor->sys_frames_pending = GF_FALSE;
3038 				compositor->frame_number++;
3039 				res = compositor->frame_number;
3040 				res *= compositor->fps.den;
3041 				res *= 1000;
3042 				res /= compositor->fps.num;
3043 				assert(res >= compositor->scene_sampled_clock);
3044 				compositor->scene_sampled_clock = (u32) res;
3045 			}
3046 		}
3047 	}
3048 	compositor->reset_graphics = 0;
3049 
3050 	compositor->last_frame_time = gf_sys_clock();
3051 	end_time = compositor->last_frame_time - in_time;
3052 
3053 	GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI]\tCompositor Cycle Log\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
3054 	                                  compositor->networks_time,
3055 	                                  compositor->decoders_time,
3056 	                                  compositor->frame_number,
3057 	                                  compositor->traverse_state->immediate_draw,
3058 	                                  compositor->visual_config_time,
3059 	                                  event_time,
3060 	                                  route_time,
3061 	                                  smil_timing_time,
3062 	                                  time_node_time,
3063 	                                  texture_time,
3064 	                                  time_spent_in_anim,
3065 	                                  compositor->traverse_setup_time,
3066 	                                  compositor->traverse_and_direct_draw_time,
3067 	                                  compositor->traverse_and_direct_draw_time - time_spent_in_anim,
3068 	                                  compositor->indirect_draw_time,
3069 	                                  traverse_time,
3070 	                                  flush_time,
3071 	                                  end_time));
3072 
3073 	if (frame_drawn) {
3074 		compositor->current_frame = (compositor->current_frame+1) % GF_SR_FPS_COMPUTE_SIZE;
3075 		compositor->frame_dur[compositor->current_frame] = end_time;
3076 		compositor->frame_time[compositor->current_frame] = compositor->last_frame_time;
3077 		compositor->frame_number++;
3078 	}
3079 	if (compositor->bench_mode && (frame_drawn || (has_timed_nodes&&all_tx_done) )) {
3080 		//in bench mode we always increase the clock of the fixed target simulation rate - this needs refinement if video is used ...
3081 		compositor->scene_sampled_clock += frame_duration;
3082 	}
3083 	gf_sc_lock(compositor, 0);
3084 
3085 	if (frame_drawn) compositor->step_mode = GF_FALSE;
3086 
3087 	/*old arch code kept for reference*/
3088 #if 0
3089 	/*let the owner decide*/
3090 	if (compositor->no_regulation)
3091 		return;
3092 
3093 	/*we are in bench mode, just release for a moment the composition, oherwise we will constantly lock the compositor wich may have impact on scene decoding*/
3094 	if (compositor->bench_mode) {
3095 		gf_sleep(0);
3096 		return;
3097 	}
3098 
3099 	//we have a pending frame, return asap - we could sleep until frames matures but this give weird regulation
3100 	if (compositor->ms_until_next_frame != GF_INT_MAX) {
3101 		compositor->ms_until_next_frame -= end_time;
3102 
3103 		if (compositor->ms_until_next_frame<=0) {
3104 			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Next frame already due (%d ms late) - not going to sleep\n", - compositor->ms_until_next_frame));
3105 			compositor->ms_until_next_frame=0;
3106 			return;
3107 		}
3108 
3109 		compositor->ms_until_next_frame = MIN(compositor->ms_until_next_frame, (s32) frame_duration );
3110 		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Next frame due in %d ms\n", compositor->ms_until_next_frame));
3111 		if (compositor->ms_until_next_frame > 2) {
3112 			u64 start = gf_sys_clock_high_res();
3113 			u64 diff=0;
3114 			u64 wait_for = 1000 * (u64) compositor->ms_until_next_frame;
3115 			while (! compositor->msg_type) {
3116 				gf_sleep(1);
3117 				diff = gf_sys_clock_high_res() - start;
3118 				if (diff >= wait_for)
3119 					break;
3120 			}
3121 			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Compositor slept %d ms until next frame (msg type %d)\n", diff/1000, compositor->msg_type));
3122 		}
3123 		return;
3124 	}
3125 
3126 	if (end_time > frame_duration) {
3127 		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Compositor did not go to sleep\n"));
3128 		return;
3129 	}
3130 
3131 	/*compute sleep time till next frame*/
3132 	end_time %= frame_duration;
3133 	gf_sleep(frame_duration - end_time);
3134 	GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Compositor slept for %d ms\n", frame_duration - end_time));
3135 #endif
3136 
3137 }
3138 
gf_sc_visual_is_registered(GF_Compositor * compositor,GF_VisualManager * visual)3139 Bool gf_sc_visual_is_registered(GF_Compositor *compositor, GF_VisualManager *visual)
3140 {
3141 	GF_VisualManager *tmp;
3142 	u32 i = 0;
3143 	while ((tmp = (GF_VisualManager *)gf_list_enum(compositor->visuals, &i))) {
3144 		if (tmp == visual) return 1;
3145 	}
3146 	return 0;
3147 }
3148 
gf_sc_visual_register(GF_Compositor * compositor,GF_VisualManager * visual)3149 void gf_sc_visual_register(GF_Compositor *compositor, GF_VisualManager *visual)
3150 {
3151 	if (gf_sc_visual_is_registered(compositor, visual)) return;
3152 	gf_list_add(compositor->visuals, visual);
3153 }
3154 
gf_sc_visual_unregister(GF_Compositor * compositor,GF_VisualManager * visual)3155 void gf_sc_visual_unregister(GF_Compositor *compositor, GF_VisualManager *visual)
3156 {
3157 	gf_list_del_item(compositor->visuals, visual);
3158 }
3159 
gf_sc_traverse_subscene_ex(GF_Compositor * compositor,GF_Node * inline_parent,GF_SceneGraph * subscene,void * rs)3160 void gf_sc_traverse_subscene_ex(GF_Compositor *compositor, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs)
3161 {
3162 	Fixed min_hsize, vp_scale;
3163 	Bool use_pm, prev_pm, prev_coord;
3164 	SFVec2f prev_vp;
3165 	s32 flip_coords;
3166 	u32 h, w, tag;
3167 	GF_Matrix2D transf;
3168 	GF_SceneGraph *in_scene;
3169 	GF_Node *inline_root;
3170 	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
3171 
3172 	/*we don't traverse subscenes until the root one is setup*/
3173 	if (!compositor->root_visual_setup) return;
3174 
3175 	inline_root = gf_sg_get_root_node(subscene);
3176 	if (!inline_root) return;
3177 
3178 	if (!gf_scene_is_over(subscene))
3179 		tr_state->subscene_not_over ++;
3180 
3181 	flip_coords = 0;
3182 	in_scene = gf_node_get_graph(inline_root);
3183 	w = h = 0;
3184 
3185 	/*check parent/child doc orientation*/
3186 
3187 	/*check child doc*/
3188 	tag = gf_node_get_tag(inline_root);
3189 	if (tag < GF_NODE_RANGE_LAST_VRML) {
3190 #ifndef GPAC_DISABLE_VRML
3191 		u32 new_tag = 0;
3192 		use_pm = gf_sg_use_pixel_metrics(in_scene);
3193 		if (gf_node_get_tag(inline_parent)>GF_NODE_RANGE_LAST_VRML) {
3194 			/*moving from SVG to VRML-based, need positive translation*/
3195 			flip_coords = 1;
3196 
3197 			/*if our root nodes are not LayerXD ones, insert one. This prevents mixing
3198 			of bindable stacks and gives us free 2D/3D integration*/
3199 			if (tag==TAG_MPEG4_OrderedGroup) {
3200 				new_tag = TAG_MPEG4_Layer2D;
3201 			} else if (tag==TAG_MPEG4_Group) {
3202 				new_tag = TAG_MPEG4_Layer3D;
3203 			}
3204 #ifndef GPAC_DISABLE_X3D
3205 			else if (tag==TAG_X3D_Group) {
3206 				new_tag = TAG_MPEG4_Layer3D;
3207 			}
3208 #endif
3209 		}
3210 #if !defined(GPAC_DISABLE_X3D) && !defined(GPAC_DISABLE_3D)
3211 		/*if the inlined root node is a 3D one except Layer3D and we are not in a 3D context, insert
3212 		a Layer3D at the root*/
3213 		else if (!tr_state->visual->type_3d && ((tag==TAG_MPEG4_Group) || (tag==TAG_X3D_Group))) {
3214 			new_tag = TAG_MPEG4_Layer3D;
3215 		}
3216 #endif
3217 
3218 		/*create new root*/
3219 		if (new_tag) {
3220 			GF_SceneGraph *sg = gf_node_get_graph(inline_root);
3221 			GF_Node *new_root = gf_node_new(sg, new_tag);
3222 			gf_node_register(new_root, NULL);
3223 			gf_sg_set_root_node(sg, new_root);
3224 			gf_node_list_add_child(& ((GF_ParentNode*)new_root)->children, inline_root);
3225 			gf_node_register(inline_root, new_root);
3226 			gf_node_unregister(inline_root, NULL);
3227 			inline_root = new_root;
3228 			gf_node_init(new_root);
3229 		}
3230 
3231 #endif /*GPAC_DISABLE_VRML*/
3232 
3233 		gf_sg_get_scene_size_info(in_scene, &w, &h);
3234 
3235 	} else {
3236 		use_pm = 1;
3237 		if (gf_node_get_tag(inline_parent)<GF_NODE_RANGE_LAST_VRML) {
3238 			/*moving from VRML-based to SVG, need negative translation*/
3239 			flip_coords = -1;
3240 		}
3241 	}
3242 
3243 	min_hsize = tr_state->min_hsize;
3244 	prev_pm = tr_state->pixel_metrics;
3245 	prev_vp = tr_state->vp_size;
3246 	prev_coord = tr_state->fliped_coords;
3247 	vp_scale = FIX_ONE;
3248 	gf_mx2d_init(transf);
3249 
3250 	/*compute center<->top-left transform*/
3251 	if (flip_coords)
3252 		gf_mx2d_add_scale(&transf, FIX_ONE, -FIX_ONE);
3253 
3254 	/*if scene size is given in the child document, scale to fit the entire vp unless our VP is the full output*/
3255 	if (w && h) {
3256 		if ((compositor->scene_width != tr_state->vp_size.x) || (compositor->scene_height != tr_state->vp_size.y)) {
3257 			Fixed scale_w = tr_state->vp_size.x/w;
3258 			Fixed scale_h = tr_state->vp_size.y/h;
3259 			vp_scale = MIN(scale_w, scale_h);
3260 			gf_mx2d_add_scale(&transf, vp_scale, vp_scale);
3261 		}
3262 	}
3263 	if (flip_coords) {
3264 		gf_mx2d_add_translation(&transf, flip_coords * tr_state->vp_size.x/2, tr_state->vp_size.y/2);
3265 		tr_state->fliped_coords = !tr_state->fliped_coords;
3266 	}
3267 
3268 	/*if scene size is given in the child document, scale back vp to take into account the above scale
3269 	otherwise the scene won't be properly clipped*/
3270 	if (w && h) {
3271 		tr_state->vp_size.x = gf_divfix(tr_state->vp_size.x, vp_scale);
3272 		tr_state->vp_size.y = gf_divfix(tr_state->vp_size.y, vp_scale);
3273 	}
3274 
3275 
3276 	/*compute pixel<->meter transform*/
3277 	if (use_pm != tr_state->pixel_metrics) {
3278 		/*override aspect ratio if any size info is given in the scene*/
3279 		if (w && h) {
3280 			Fixed scale = INT2FIX( MIN(w, h) / 2);
3281 			if (scale) tr_state->min_hsize = scale;
3282 		}
3283 		if (!use_pm) {
3284 			gf_mx2d_add_scale(&transf, tr_state->min_hsize, tr_state->min_hsize);
3285 		} else {
3286 			Fixed inv_scale = gf_invfix(tr_state->min_hsize);
3287 			gf_mx2d_add_scale(&transf, inv_scale, inv_scale);
3288 		}
3289 		tr_state->pixel_metrics = use_pm;
3290 	}
3291 
3292 #ifndef GPAC_DISABLE_3D
3293 	if (tr_state->visual->type_3d) {
3294 		GF_Matrix mx_bck, mx;
3295 		gf_mx_copy(mx_bck, tr_state->model_matrix);
3296 
3297 		gf_mx_from_mx2d(&mx, &transf);
3298 
3299 		/*copy over z scale*/
3300 		mx.m[10] = mx.m[5];
3301 		gf_mx_add_matrix(&tr_state->model_matrix, &mx);
3302 		gf_node_traverse(inline_root, rs);
3303 		gf_mx_copy(tr_state->model_matrix, mx_bck);
3304 
3305 	} else
3306 #endif
3307 	{
3308 		GF_Matrix2D mx_bck;
3309 		gf_mx2d_copy(mx_bck, tr_state->transform);
3310 		gf_mx2d_pre_multiply(&tr_state->transform, &transf);
3311 		gf_node_traverse(inline_root, rs);
3312 		gf_mx2d_copy(tr_state->transform, mx_bck);
3313 	}
3314 
3315 	tr_state->pixel_metrics = prev_pm;
3316 	tr_state->min_hsize = min_hsize;
3317 	tr_state->vp_size = prev_vp;
3318 	tr_state->fliped_coords = prev_coord;
3319 }
3320 
3321 
gf_sc_handle_event_intern(GF_Compositor * compositor,GF_Event * event,Bool from_user)3322 static Bool gf_sc_handle_event_intern(GF_Compositor *compositor, GF_Event *event, Bool from_user)
3323 {
3324 	Bool ret;
3325 
3326 	if ( (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) && (event->type<=GF_EVENT_MOUSEWHEEL)) {
3327 		GF_Event evt = *event;
3328 		gf_sc_input_sensor_mouse_input(compositor, &evt.mouse);
3329 	}
3330 
3331 	/*	if (!compositor->interaction_level || (compositor->interaction_level==GF_INTERACT_INPUT_SENSOR) ) {
3332 			if (!from_user) {
3333 				GF_USER_SENDEVENT(compositor->user, event);
3334 			}
3335 			return 0;
3336 		}
3337 	*/
3338 	gf_mx_p(compositor->mx);
3339 	ret = gf_sc_exec_event(compositor, event);
3340 	gf_sc_lock(compositor, GF_FALSE);
3341 
3342 //	if (!from_user) { }
3343 	return ret;
3344 }
3345 
gf_sc_traverse_subscene(GF_Compositor * compositor,GF_Node * inline_parent,GF_SceneGraph * subscene,void * rs)3346 void gf_sc_traverse_subscene(GF_Compositor *compositor, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs)
3347 {
3348 	u32 i=0;
3349 	GF_SceneGraph *subsg;
3350 
3351 	gf_sc_traverse_subscene_ex(compositor, inline_parent, subscene, rs);
3352 
3353 	while ( (subsg = gf_scene_enum_extra_scene(subscene, &i)))
3354 		gf_sc_traverse_subscene_ex(compositor, inline_parent, subsg, rs);
3355 
3356 }
3357 
gf_sc_on_event_ex(GF_Compositor * compositor,GF_Event * event,Bool from_user)3358 static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool from_user)
3359 {
3360 	/*not assigned yet*/
3361 	if (!compositor || !compositor->visual || compositor->discard_input_events) return GF_FALSE;
3362 	/*we're reconfiguring the video output, cancel all messages except GL reconfig (context lost)*/
3363 	if (compositor->msg_type & GF_SR_IN_RECONFIG) {
3364 		if (event->type==GF_EVENT_VIDEO_SETUP) {
3365 			if (event->setup.hw_reset)
3366 				gf_sc_reset_graphics(compositor);
3367 
3368 			if (event->setup.back_buffer)
3369 				compositor->recompute_ar = 1;
3370 		}
3371 		return GF_FALSE;
3372 	}
3373 	switch (event->type) {
3374 	case GF_EVENT_SHOWHIDE:
3375 	case GF_EVENT_SET_CAPTION:
3376 	case GF_EVENT_MOVE:
3377 		compositor->video_out->ProcessEvent(compositor->video_out, event);
3378 		break;
3379 
3380 	case GF_EVENT_MOVE_NOTIF:
3381 		if (compositor->last_had_overlays) {
3382 			gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
3383 		}
3384 		break;
3385 	case GF_EVENT_REFRESH:
3386 		/*when refreshing a window with overlays we redraw the scene */
3387 		gf_sc_next_frame_state(compositor, compositor->last_had_overlays ? GF_SC_DRAW_FRAME : GF_SC_DRAW_FLUSH);
3388 		break;
3389 	case GF_EVENT_VIDEO_SETUP:
3390 	{
3391 		Bool locked = gf_mx_try_lock(compositor->mx);
3392 		if (event->setup.hw_reset) {
3393 			gf_sc_reset_graphics(compositor);
3394 			compositor->reset_graphics = 2;
3395 		}
3396 
3397 		if (event->setup.back_buffer)
3398 			compositor->recompute_ar = 1;
3399 		if (locked) gf_mx_v(compositor->mx);
3400 	}
3401 	break;
3402 	case GF_EVENT_SIZE:
3403 		/*user consummed the resize event, do nothing*/
3404 		if ( gf_sc_send_event(compositor, event) )
3405 			return GF_TRUE;
3406 
3407 		/*not consummed and compositor "owns" the output window (created by the output module), resize*/
3408 		if (!compositor->os_wnd) {
3409 			/*EXTRA CARE HERE: the caller (video output) is likely a different thread than the compositor one, and the
3410 			compositor may be locked on the video output (flush or whatever)!!
3411 			*/
3412 			Bool lock_ok = gf_mx_try_lock(compositor->mx);
3413 			if ((compositor->display_width!=event->size.width)
3414 					|| (compositor->display_height!=event->size.height)
3415 					|| (compositor->new_width && (compositor->new_width!=event->size.width))
3416 					|| (compositor->new_width && (compositor->new_height!=event->size.height))
3417 				) {
3418 
3419 				//OSX bug with SDL when requesting 4k window we get max screen height but 4k width ...
3420 #if defined(GPAC_CONFIG_DARWIN) && !defined(GPAC_CONFIG_IOS)
3421 				if (compositor->display_width==event->size.width) {
3422 					if (compositor->display_height > 2*event->size.height) {
3423 						event->size.width = compositor->display_width * event->size.height / compositor->display_height;
3424 					}
3425 				}
3426 #endif
3427 				compositor->new_width = event->size.width;
3428 				compositor->new_height = event->size.height;
3429 
3430 				compositor->msg_type |= GF_SR_CFG_SET_SIZE;
3431 				if (from_user) compositor->msg_type &= ~GF_SR_CFG_WINDOWSIZE_NOTIF;
3432 			} else {
3433 				/*remove pending resize notif but not resize requests*/
3434 				compositor->msg_type &= ~GF_SR_CFG_WINDOWSIZE_NOTIF;
3435 			}
3436 			if (lock_ok) gf_sc_lock(compositor, GF_FALSE);
3437 		}
3438 		return GF_FALSE;
3439 
3440 	case GF_EVENT_KEYDOWN:
3441 	case GF_EVENT_KEYUP:
3442 	{
3443 		Bool ret;
3444 		switch (event->key.key_code) {
3445 		case GF_KEY_SHIFT:
3446 			if (event->type==GF_EVENT_KEYDOWN) {
3447 				compositor->key_states |= GF_KEY_MOD_SHIFT;
3448 			} else {
3449 				compositor->key_states &= ~GF_KEY_MOD_SHIFT;
3450 			}
3451 			break;
3452 		case GF_KEY_CONTROL:
3453 			if (event->type==GF_EVENT_KEYDOWN) {
3454 				compositor->key_states |= GF_KEY_MOD_CTRL;
3455 			} else {
3456 				compositor->key_states &= ~GF_KEY_MOD_CTRL;
3457 			}
3458 			break;
3459 		case GF_KEY_ALT:
3460 			if (event->type==GF_EVENT_KEYDOWN) {
3461 				compositor->key_states |= GF_KEY_MOD_ALT;
3462 			} else {
3463 				compositor->key_states &= ~GF_KEY_MOD_ALT;
3464 			}
3465 			break;
3466 
3467 		}
3468 
3469 		ret = GF_FALSE;
3470 		event->key.flags |= compositor->key_states;
3471 		/*key sensor*/
3472 		if ((compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) ) {
3473 			ret = gf_sc_input_sensor_keyboard_input(compositor, event->key.key_code, event->key.hw_code, (event->type==GF_EVENT_KEYUP) ? GF_TRUE : GF_FALSE);
3474 		}
3475 		ret += gf_sc_handle_event_intern(compositor, event, from_user);
3476 		return ret;
3477 	}
3478 
3479 	case GF_EVENT_TEXTINPUT:
3480 		if (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR)
3481 			gf_sc_input_sensor_string_input(compositor , event->character.unicode_char);
3482 
3483 		return gf_sc_handle_event_intern(compositor, event, from_user);
3484 	/*switch fullscreen off!!!*/
3485 	case GF_EVENT_SHOWHIDE_NOTIF:
3486 		compositor->is_hidden = event->show.show_type ? GF_FALSE : GF_TRUE;
3487 		break;
3488 
3489 	case GF_EVENT_MOUSEMOVE:
3490 		event->mouse.button = 0;
3491 	case GF_EVENT_MOUSEDOWN:
3492 	case GF_EVENT_MOUSEUP:
3493 	case GF_EVENT_MOUSEWHEEL:
3494 		event->mouse.key_states = compositor->key_states;
3495 	case GF_EVENT_SENSOR_ORIENTATION:
3496 	case GF_EVENT_MULTITOUCH:
3497 		return gf_sc_handle_event_intern(compositor, event, from_user);
3498 
3499 	case GF_EVENT_PASTE_TEXT:
3500 		gf_sc_paste_text(compositor, event->message.message);
3501 		break;
3502 	case GF_EVENT_COPY_TEXT:
3503 		if (gf_sc_has_text_selection(compositor)) {
3504 			event->message.message = gf_sc_get_selected_text(compositor);
3505 		} else {
3506 			event->message.message = NULL;
3507 		}
3508 		break;
3509 	/*when we process events we don't forward them to the user*/
3510 	default:
3511 		return gf_sc_send_event(compositor, event);
3512 	}
3513 	/*if we get here, event has been consumed*/
3514 	return GF_TRUE;
3515 }
3516 
3517 GF_EXPORT
gf_sc_on_event(void * cbck,GF_Event * event)3518 Bool gf_sc_on_event(void *cbck, GF_Event *event)
3519 {
3520 	return gf_sc_on_event_ex((GF_Compositor *)cbck, event, GF_FALSE);
3521 }
3522 
3523 GF_EXPORT
gf_sc_user_event(GF_Compositor * compositor,GF_Event * event)3524 Bool gf_sc_user_event(GF_Compositor *compositor, GF_Event *event)
3525 {
3526 	switch (event->type) {
3527 	case GF_EVENT_SHOWHIDE:
3528 	case GF_EVENT_MOVE:
3529 	case GF_EVENT_SET_CAPTION:
3530 		compositor->video_out->ProcessEvent(compositor->video_out, event);
3531 		return GF_FALSE;
3532 	default:
3533 		return gf_sc_on_event_ex(compositor, event, GF_TRUE);
3534 	}
3535 }
3536 
3537 GF_EXPORT
gf_sc_register_extra_graph(GF_Compositor * compositor,GF_SceneGraph * extra_scene,Bool do_remove)3538 void gf_sc_register_extra_graph(GF_Compositor *compositor, GF_SceneGraph *extra_scene, Bool do_remove)
3539 {
3540 	gf_sc_lock(compositor, GF_TRUE);
3541 	if (do_remove) gf_list_del_item(compositor->extra_scenes, extra_scene);
3542 	else if (gf_list_find(compositor->extra_scenes, extra_scene)<0) gf_list_add(compositor->extra_scenes, extra_scene);
3543 	gf_sc_lock(compositor, GF_FALSE);
3544 }
3545 
gf_sc_script_action(GF_Compositor * compositor,GF_JSAPIActionType type,GF_Node * n,GF_JSAPIParam * param)3546 Bool gf_sc_script_action(GF_Compositor *compositor, GF_JSAPIActionType type, GF_Node *n, GF_JSAPIParam *param)
3547 {
3548 	switch (type) {
3549 	case GF_JSAPI_OP_GET_SCALE:
3550 		param->val = compositor->zoom;
3551 		return GF_TRUE;
3552 	case GF_JSAPI_OP_SET_SCALE:
3553 		compositor_2d_set_user_transform(compositor, param->val, compositor->trans_x, compositor->trans_y, GF_FALSE);
3554 		return GF_TRUE;
3555 	case GF_JSAPI_OP_GET_ROTATION:
3556 		param->val = gf_divfix(180*compositor->rotation, GF_PI);
3557 		return GF_TRUE;
3558 	case GF_JSAPI_OP_SET_ROTATION:
3559 		compositor->rotation = gf_mulfix(GF_PI, param->val/180);
3560 		compositor_2d_set_user_transform(compositor, compositor->zoom, compositor->trans_x, compositor->trans_y, GF_FALSE);
3561 		return GF_TRUE;
3562 	case GF_JSAPI_OP_GET_TRANSLATE:
3563 		param->pt.x = compositor->trans_x;
3564 		param->pt.y = compositor->trans_y;
3565 		return GF_TRUE;
3566 	case GF_JSAPI_OP_SET_TRANSLATE:
3567 		compositor_2d_set_user_transform(compositor, compositor->zoom, param->pt.x, param->pt.y, GF_FALSE);
3568 		return GF_TRUE;
3569 	/*FIXME - better SMIL timelines support*/
3570 	case GF_JSAPI_OP_GET_TIME:
3571 		param->time = gf_node_get_scene_time(n);
3572 		return GF_TRUE;
3573 	case GF_JSAPI_OP_SET_TIME: /*seek_to(param->time);*/
3574 		return GF_FALSE;
3575 	/*FIXME - this will not work for inline docs, we will have to store the clipper at the <svg> level*/
3576 	case GF_JSAPI_OP_GET_VIEWPORT:
3577 #ifndef GPAC_DISABLE_SVG
3578 		if (compositor_svg_get_viewport(n, &param->rc)) return GF_TRUE;
3579 #endif
3580 		param->rc = gf_rect_center(compositor->traverse_state->vp_size.x, compositor->traverse_state->vp_size.y);
3581 		if (!compositor->visual->center_coords) {
3582 			param->rc.x = 0;
3583 			param->rc.y = 0;
3584 		}
3585 		return GF_TRUE;
3586 	case GF_JSAPI_OP_SET_FOCUS:
3587 		compositor->focus_node = param->node;
3588 		return GF_TRUE;
3589 	case GF_JSAPI_OP_GET_FOCUS:
3590 		param->node = compositor->focus_node;
3591 		return GF_TRUE;
3592 
3593 	/*same routine: traverse tree from root to target, collecting both bounds and transform matrix*/
3594 	case GF_JSAPI_OP_GET_LOCAL_BBOX:
3595 	case GF_JSAPI_OP_GET_SCREEN_BBOX:
3596 	case GF_JSAPI_OP_GET_TRANSFORM:
3597 	{
3598 		GF_TraverseState tr_state;
3599 		memset(&tr_state, 0, sizeof(tr_state));
3600 		tr_state.traversing_mode = TRAVERSE_GET_BOUNDS;
3601 		tr_state.visual = compositor->visual;
3602 		tr_state.vp_size = compositor->traverse_state->vp_size;
3603 		tr_state.for_node = n;
3604 		tr_state.ignore_strike = GF_TRUE;
3605 		tr_state.min_hsize = compositor->traverse_state->min_hsize;
3606 		tr_state.pixel_metrics = compositor->traverse_state->pixel_metrics;
3607 		gf_mx2d_init(tr_state.transform);
3608 		gf_mx2d_init(tr_state.mx_at_node);
3609 
3610 
3611 		if (type==GF_JSAPI_OP_GET_LOCAL_BBOX) {
3612 #ifndef GPAC_DISABLE_SVG
3613 			GF_SAFEALLOC(tr_state.svg_props, SVGPropertiesPointers);
3614 			if (tr_state.svg_props) {
3615 				gf_svg_properties_init_pointers(tr_state.svg_props);
3616 				tr_state.abort_bounds_traverse = GF_TRUE;
3617 				gf_node_traverse(n, &tr_state);
3618 				gf_svg_properties_reset_pointers(tr_state.svg_props);
3619 				gf_free(tr_state.svg_props);
3620 			}
3621 #endif
3622 		} else {
3623 			gf_node_traverse(gf_sg_get_root_node(compositor->scene), &tr_state);
3624 		}
3625 		if (!tr_state.bounds.height && !tr_state.bounds.width && !tr_state.bounds.x && !tr_state.bounds.y)
3626 			tr_state.abort_bounds_traverse = GF_FALSE;
3627 
3628 		gf_mx2d_pre_multiply(&tr_state.mx_at_node, &compositor->traverse_state->transform);
3629 
3630 		if (type==GF_JSAPI_OP_GET_LOCAL_BBOX) {
3631 			gf_bbox_from_rect(&param->bbox, &tr_state.bounds);
3632 		} else if (type==GF_JSAPI_OP_GET_TRANSFORM) {
3633 			gf_mx_from_mx2d(&param->mx, &tr_state.mx_at_node);
3634 		} else {
3635 			gf_mx2d_apply_rect(&tr_state.mx_at_node, &tr_state.bounds);
3636 			gf_bbox_from_rect(&param->bbox, &tr_state.bounds);
3637 		}
3638 		if (!tr_state.abort_bounds_traverse) param->bbox.is_set = GF_FALSE;
3639 	}
3640 	return GF_TRUE;
3641 	case GF_JSAPI_OP_LOAD_URL:
3642 	{
3643 #ifndef GPAC_DISABLE_VRML
3644 		GF_Node *target;
3645 		char *sub_url = strrchr(param->uri.url, '#');
3646 		if (!sub_url) return GF_FALSE;
3647 		target = gf_sg_find_node_by_name(gf_node_get_graph(n), sub_url+1);
3648 		if (target && (gf_node_get_tag(target)==TAG_MPEG4_Viewport) ) {
3649 			((M_Viewport *)target)->set_bind = 1;
3650 			((M_Viewport *)target)->on_set_bind(n, NULL);
3651 			return GF_TRUE;
3652 		}
3653 #endif
3654 		return GF_FALSE;
3655 	}
3656 	case GF_JSAPI_OP_GET_FPS:
3657 		param->time = gf_sc_get_fps(compositor, GF_FALSE);
3658 		return GF_TRUE;
3659 	case GF_JSAPI_OP_GET_SPEED:
3660 		param->time = 0;
3661 #ifndef GPAC_DISABLE_3D
3662 		if (compositor->visual->type_3d==2) {
3663 			param->time = FIX2FLT(compositor->visual->camera.speed);
3664 		}
3665 #endif
3666 		return GF_TRUE;
3667 	case GF_JSAPI_OP_PAUSE_SVG:
3668 		if (n) {
3669 			u32 tag = gf_node_get_tag(n);
3670 			switch(tag) {
3671 #ifndef GPAC_DISABLE_SVG
3672 			case TAG_SVG_audio:
3673 				svg_pause_audio(n, GF_TRUE);
3674 				break;
3675 			case TAG_SVG_video:
3676 				svg_pause_video(n, GF_TRUE);
3677 				break;
3678 			case TAG_SVG_animation:
3679 				svg_pause_animation(n, GF_TRUE);
3680 				break;
3681 #endif
3682 			}
3683 		} else {
3684 			return GF_FALSE;
3685 		}
3686 		return GF_TRUE;
3687 	case GF_JSAPI_OP_RESUME_SVG:
3688 	{
3689 		u32 tag = gf_node_get_tag(n);
3690 		switch(tag) {
3691 #ifndef GPAC_DISABLE_SVG
3692 		case TAG_SVG_audio:
3693 			svg_pause_audio(n, GF_FALSE);
3694 			break;
3695 		case TAG_SVG_video:
3696 			svg_pause_video(n, GF_FALSE);
3697 			break;
3698 		case TAG_SVG_animation:
3699 			svg_pause_animation(n, GF_FALSE);
3700 			break;
3701 #endif
3702 		}
3703 	}
3704 	return GF_TRUE;
3705 	case GF_JSAPI_OP_GET_DPI_X:
3706 		param->opt = compositor->video_out->dpi_x;
3707 		return GF_TRUE;
3708 	case GF_JSAPI_OP_GET_DPI_Y:
3709 		param->opt = compositor->video_out->dpi_y;
3710 		return GF_TRUE;
3711 	default:
3712 		return GF_FALSE;
3713 	}
3714 	return GF_FALSE;
3715 }
3716 
gf_sc_pick_in_clipper(GF_TraverseState * tr_state,GF_Rect * clip)3717 Bool gf_sc_pick_in_clipper(GF_TraverseState *tr_state, GF_Rect *clip)
3718 {
3719 #ifndef GPAC_DISABLE_3D
3720 	if (tr_state->visual->type_3d) {
3721 		SFVec3f pos;
3722 		GF_Matrix mx;
3723 		GF_Ray r;
3724 		gf_mx_copy(mx, tr_state->model_matrix);
3725 		gf_mx_inverse(&mx);
3726 		r = tr_state->ray;
3727 		gf_mx_apply_ray(&mx, &r);
3728 		if (!compositor_get_2d_plane_intersection(&r, &pos)) return GF_FALSE;
3729 		if ( (pos.x < clip->x) || (pos.y > clip->y)
3730 		        || (pos.x > clip->x + clip->width) || (pos.y < clip->y - clip->height) ) return GF_FALSE;
3731 
3732 	} else
3733 #endif
3734 	{
3735 		GF_Rect rc = *clip;
3736 		GF_Point2D pt;
3737 		gf_mx2d_apply_rect(&tr_state->transform, &rc);
3738 		pt.x = tr_state->ray.orig.x;
3739 		pt.y = tr_state->ray.orig.y;
3740 
3741 		if ( (pt.x < rc.x) || (pt.y > rc.y)
3742 		        || (pt.x > rc.x + rc.width) || (pt.y < rc.y - rc.height) ) return GF_FALSE;
3743 	}
3744 	return GF_TRUE;
3745 }
3746 
3747 
gf_sc_has_text_selection(GF_Compositor * compositor)3748 Bool gf_sc_has_text_selection(GF_Compositor *compositor)
3749 {
3750 	return (compositor->store_text_state==GF_SC_TSEL_FROZEN) ? GF_TRUE : GF_FALSE;
3751 }
3752 
3753 GF_EXPORT
gf_sc_get_selected_text(GF_Compositor * compositor)3754 const char *gf_sc_get_selected_text(GF_Compositor *compositor)
3755 {
3756 	const u16 *srcp;
3757 	size_t len;
3758 	if (compositor->store_text_state != GF_SC_TSEL_FROZEN) return NULL;
3759 
3760 	gf_sc_lock(compositor, GF_TRUE);
3761 
3762 	compositor->traverse_state->traversing_mode = TRAVERSE_GET_TEXT;
3763 	if (compositor->sel_buffer) {
3764 		gf_free(compositor->sel_buffer);
3765 		compositor->sel_buffer = NULL;
3766 	}
3767 	compositor->sel_buffer_len = 0;
3768 	compositor->sel_buffer_alloc = 0;
3769 	gf_node_traverse(compositor->text_selection, compositor->traverse_state);
3770 	compositor->traverse_state->traversing_mode = 0;
3771 	if (compositor->sel_buffer) compositor->sel_buffer[compositor->sel_buffer_len]=0;
3772 	srcp = compositor->sel_buffer;
3773 
3774 	if (compositor->selected_text) gf_free(compositor->selected_text);
3775 	compositor->selected_text = gf_malloc(sizeof(char)*2*compositor->sel_buffer_len);
3776 	len = gf_utf8_wcstombs((char *) compositor->selected_text, 2*compositor->sel_buffer_len, &srcp);
3777 	if ((s32)len<0) len = 0;
3778 	compositor->selected_text[len] = 0;
3779 	gf_sc_lock(compositor, GF_FALSE);
3780 
3781 	return (const char *) compositor->selected_text;
3782 }
3783 
3784 
gf_sc_check_focus_upon_destroy(GF_Node * n)3785 void gf_sc_check_focus_upon_destroy(GF_Node *n)
3786 {
3787 	GF_Compositor *compositor = gf_sc_get_compositor(n);
3788 	if (!compositor) return;
3789 
3790 	if (compositor->focus_node==n) {
3791 		compositor->focus_node = NULL;
3792 		compositor->focus_text_type = 0;
3793 		compositor->focus_uses_dom_events = GF_FALSE;
3794 		gf_list_reset(compositor->focus_ancestors);
3795 		gf_list_reset(compositor->focus_use_stack);
3796 	}
3797 	if (compositor->hit_node==n) compositor->hit_node = NULL;
3798 	if (compositor->hit_text==n) compositor->hit_text = NULL;
3799 }
3800 
3801 #if 0 //unused
3802 void gf_sc_set_system_pending_frame(GF_Compositor *compositor, Bool frame_pending)
3803 {
3804 	if (frame_pending) {
3805 		if (!compositor->force_bench_frame)
3806 			compositor->force_bench_frame = 1;
3807 	} else {
3808 		//do not increase clock
3809 		compositor->force_bench_frame = 2;
3810 	}
3811 }
3812 
3813 void gf_sc_queue_event(GF_Compositor *compositor, GF_Event *evt)
3814 {
3815 	u32 i, count;
3816 	GF_QueuedEvent *qev;
3817 	gf_mx_p(compositor->evq_mx);
3818 
3819 	count = gf_list_count(compositor->event_queue);
3820 	for (i=0; i<count; i++) {
3821 		qev = gf_list_get(compositor->event_queue, i);
3822 		if (!qev->node && (qev->evt.type==evt->type)) {
3823 			qev->evt = *evt;
3824 			gf_mx_v(compositor->evq_mx);
3825 			return;
3826 		}
3827 	}
3828 	GF_SAFEALLOC(qev, GF_QueuedEvent);
3829 	if (!qev) {
3830 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate event for queuing\n"));
3831 	} else {
3832 		qev->evt = *evt;
3833 		gf_list_add(compositor->event_queue, qev);
3834 	}
3835 	gf_mx_v(compositor->evq_mx);
3836 }
3837 #endif
3838 
3839 
gf_sc_queue_dom_event(GF_Compositor * compositor,GF_Node * node,GF_DOM_Event * evt)3840 void gf_sc_queue_dom_event(GF_Compositor *compositor, GF_Node *node, GF_DOM_Event *evt)
3841 {
3842 	u32 i, count;
3843 	GF_QueuedEvent *qev;
3844 	gf_mx_p(compositor->evq_mx);
3845 
3846 	count = gf_list_count(compositor->event_queue);
3847 	for (i=0; i<count; i++) {
3848 		qev = gf_list_get(compositor->event_queue, i);
3849 		if ((qev->node==node) && (qev->dom_evt.type==evt->type)) {
3850 			qev->dom_evt = *evt;
3851 			gf_mx_v(compositor->evq_mx);
3852 			return;
3853 		}
3854 	}
3855 	GF_SAFEALLOC(qev, GF_QueuedEvent);
3856 	if (!qev) {
3857 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate event for queuing\n"));
3858 	} else {
3859 		qev->node = node;
3860 		qev->dom_evt = *evt;
3861 		gf_list_add(compositor->event_queue, qev);
3862 	}
3863 	gf_mx_v(compositor->evq_mx);
3864 }
3865 
gf_sc_queue_dom_event_on_target(GF_Compositor * compositor,GF_DOM_Event * evt,GF_DOMEventTarget * target,GF_SceneGraph * sg)3866 void gf_sc_queue_dom_event_on_target(GF_Compositor *compositor, GF_DOM_Event *evt, GF_DOMEventTarget *target, GF_SceneGraph *sg)
3867 {
3868 	u32 i, count;
3869 	GF_QueuedEvent *qev;
3870 	gf_mx_p(compositor->evq_mx);
3871 
3872 	count = gf_list_count(compositor->event_queue);
3873 	for (i=0; i<count; i++) {
3874 		qev = gf_list_get(compositor->event_queue, i);
3875 		if ((qev->target==target) && (qev->dom_evt.type==evt->type) && (qev->sg==sg) ) {
3876 			//do not override any pending dowload progress event by new buffer state events
3877 			if ((evt->type!=GF_EVENT_MEDIA_PROGRESS) || !qev->dom_evt.media_event.loaded_size) {
3878 				qev->dom_evt = *evt;
3879 			}
3880 			gf_mx_v(compositor->evq_mx);
3881 			return;
3882 		}
3883 	}
3884 
3885 	GF_SAFEALLOC(qev, GF_QueuedEvent);
3886 	if (!qev) {
3887 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate event for queuing\n"));
3888 	} else {
3889 		qev->sg = sg;
3890 		qev->target = target;
3891 		qev->dom_evt = *evt;
3892 		gf_list_add(compositor->event_queue, qev);
3893 	}
3894 	gf_mx_v(compositor->evq_mx);
3895 }
3896 
sc_cleanup_event_queue(GF_List * evq,GF_Node * node,GF_SceneGraph * sg)3897 static void sc_cleanup_event_queue(GF_List *evq, GF_Node *node, GF_SceneGraph *sg)
3898 {
3899 	u32 i, count = gf_list_count(evq);
3900 	for (i=0; i<count; i++) {
3901 		Bool del = 0;
3902 		GF_QueuedEvent *qev = gf_list_get(evq, i);
3903 		if (qev->node) {
3904 			if (node == qev->node)
3905 				del = 1;
3906 			if (sg && (gf_node_get_graph(qev->node)==sg))
3907 				del = 1;
3908 		}
3909 		if (qev->sg && (qev->sg==sg))
3910 			del = 1;
3911 		else if (qev->target && (qev->target->ptr_type == GF_DOM_EVENT_TARGET_NODE)) {
3912 			if (node && ((GF_Node *)qev->target->ptr==node))
3913 				del = 1;
3914 			if (sg && (gf_node_get_graph((GF_Node *)qev->target->ptr)==sg))
3915 				del = 1;
3916 		}
3917 
3918 		if (del) {
3919 			gf_list_rem(evq, i);
3920 			i--;
3921 			count--;
3922 			gf_free(qev);
3923 		}
3924 	}
3925 }
3926 
gf_sc_node_destroy(GF_Compositor * compositor,GF_Node * node,GF_SceneGraph * sg)3927 void gf_sc_node_destroy(GF_Compositor *compositor, GF_Node *node, GF_SceneGraph *sg)
3928 {
3929 	gf_mx_p(compositor->evq_mx);
3930 	sc_cleanup_event_queue(compositor->event_queue, node, sg);
3931 	sc_cleanup_event_queue(compositor->event_queue_back, node, sg);
3932 	gf_mx_v(compositor->evq_mx);
3933 }
3934 
3935 GF_EXPORT
gf_sc_navigation_supported(GF_Compositor * compositor,u32 type)3936 Bool gf_sc_navigation_supported(GF_Compositor *compositor, u32 type)
3937 {
3938 	if (compositor->navigation_disabled ) return GF_FALSE;
3939 #ifndef GPAC_DISABLE_3D
3940 	if (compositor->visual->type_3d || compositor->active_layer) {
3941 		GF_Camera *cam = compositor_3d_get_camera(compositor);
3942 		if (cam->navigation_flags & NAV_ANY) {
3943 			return GF_TRUE;
3944 		} else {
3945 #ifndef GPAC_DISABLE_VRML
3946 			M_NavigationInfo *ni = (M_NavigationInfo *)gf_list_get(compositor->visual->navigation_stack, 0);
3947 			if (ni) {
3948 				u32 i;
3949 				for (i=0; i<ni->type.count; i++) {
3950 					if (!ni->type.vals[i]) continue;
3951 					if (!stricmp(ni->type.vals[i], "WALK") && (type==GF_NAVIGATE_WALK)) return GF_TRUE;
3952 					else if (!stricmp(ni->type.vals[i], "NONE") && (type==GF_NAVIGATE_NONE)) return GF_TRUE;
3953 					else if (!stricmp(ni->type.vals[i], "EXAMINE") && (type==GF_NAVIGATE_EXAMINE)) return GF_TRUE;
3954 					else if (!stricmp(ni->type.vals[i], "FLY") && (type==GF_NAVIGATE_FLY)) return GF_TRUE;
3955 					else if (!stricmp(ni->type.vals[i], "VR") && (type==GF_NAVIGATE_VR)) return GF_TRUE;
3956 					else if (!stricmp(ni->type.vals[i], "GAME") && (type==GF_NAVIGATE_GAME)) return GF_TRUE;
3957 					else if (!stricmp(ni->type.vals[i], "ORBIT") && (type==GF_NAVIGATE_ORBIT)) return GF_TRUE;
3958 				}
3959 			}
3960 #endif
3961 			return GF_FALSE;
3962 		}
3963 	} else
3964 #endif
3965 		if ((type!=GF_NAVIGATE_NONE) && (type!=GF_NAVIGATE_SLIDE) && (type!=GF_NAVIGATE_EXAMINE)) {
3966 			return GF_FALSE;
3967 		}
3968 	return GF_TRUE;
3969 }
3970 
3971 
gf_sc_check_end_of_scene(GF_Compositor * compositor,Bool skip_interactions)3972 u32 gf_sc_check_end_of_scene(GF_Compositor *compositor, Bool skip_interactions)
3973 {
3974 	if (!compositor->root_scene || !compositor->root_scene->root_od || !compositor->root_scene->root_od->scene_ns) return 1;
3975 
3976 	if (!skip_interactions) {
3977 		/*if input sensors consider the scene runs forever*/
3978 		if (gf_list_count(compositor->input_streams)) return 0;
3979 		if (gf_list_count(compositor->x3d_sensors)) return 0;
3980 	}
3981 
3982 	/*check no clocks are still running*/
3983 	if (!gf_scene_check_clocks(compositor->root_scene->root_od->scene_ns, compositor->root_scene, 0)) return 0;
3984 	if (compositor->root_scene->is_dynamic_scene) return 1;
3985 
3986 	/*ask compositor if there are sensors*/
3987 	return gf_sc_get_option(compositor, skip_interactions ? GF_OPT_IS_OVER : GF_OPT_IS_FINISHED);
3988 }
3989 
gf_sc_queue_node_traverse(GF_Compositor * compositor,GF_Node * node)3990 void gf_sc_queue_node_traverse(GF_Compositor *compositor, GF_Node *node)
3991 {
3992 	gf_sc_lock(compositor, GF_TRUE);
3993 	if (!compositor->nodes_pending) compositor->nodes_pending = gf_list_new();
3994 	gf_list_add(compositor->nodes_pending, node);
3995 	gf_sc_lock(compositor, GF_FALSE);
3996 }
gf_sc_unqueue_node_traverse(GF_Compositor * compositor,GF_Node * node)3997 void gf_sc_unqueue_node_traverse(GF_Compositor *compositor, GF_Node *node)
3998 {
3999 	gf_sc_lock(compositor, GF_TRUE);
4000 	if (compositor->nodes_pending) {
4001 		gf_list_del_item(compositor->nodes_pending, node);
4002 		if (!gf_list_count(compositor->nodes_pending)) {
4003 			gf_list_del(compositor->nodes_pending);
4004 			compositor->nodes_pending = NULL;
4005 		}
4006 	}
4007 	gf_sc_lock(compositor, GF_FALSE);
4008 }
4009 
4010 GF_EXPORT
gf_sc_get_downloader(GF_Compositor * compositor)4011 GF_DownloadManager *gf_sc_get_downloader(GF_Compositor *compositor)
4012 {
4013 	return gf_filter_get_download_manager(compositor->filter);
4014 }
4015 
gf_sc_sys_frame_pending(GF_Compositor * compositor,Double ts_offset,u32 obj_time,GF_Filter * from_filter)4016 void gf_sc_sys_frame_pending(GF_Compositor *compositor, Double ts_offset, u32 obj_time, GF_Filter *from_filter)
4017 {
4018 	if (!compositor->player) {
4019 		compositor->sys_frames_pending = GF_TRUE;
4020 		if (from_filter)
4021 			gf_filter_ask_rt_reschedule(from_filter, 0);
4022 	} else {
4023 		u32 wait_ms = (u32) (ts_offset * 1000 - obj_time);
4024 
4025 		if (!compositor->ms_until_next_frame || ((s32) wait_ms < compositor->ms_until_next_frame)) {
4026 			compositor->ms_until_next_frame = (s32) wait_ms;
4027 		}
4028 		if (from_filter) {
4029 			gf_filter_ask_rt_reschedule(from_filter, wait_ms*500);
4030 		}
4031 	}
4032 }
4033 
gf_sc_check_gl_support(GF_Compositor * compositor)4034 Bool gf_sc_check_gl_support(GF_Compositor *compositor)
4035 {
4036 #ifdef GPAC_DISABLE_3D
4037 	return GF_FALSE;
4038 #else
4039 	if (!compositor->player && !compositor->is_opengl) {
4040 		if (compositor->drv==GF_SC_DRV_OFF) {
4041 			return GF_FALSE;
4042 		}
4043 		compositor->needs_offscreen_gl = GF_TRUE;
4044 		compositor->autoconfig_opengl = 1;
4045 		compositor->recompute_ar = 1;
4046 		return GF_TRUE;
4047 	}
4048 	return GF_TRUE;
4049 #endif
4050 
4051 }
4052 
4053