/src/gpac/src/compositor/scene.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2023 |
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 | | |
27 | | |
28 | | /*for OD service types*/ |
29 | | #include <gpac/constants.h> |
30 | | /*for URL concatenation*/ |
31 | | #include <gpac/network.h> |
32 | | #include <gpac/internal/compositor_dev.h> |
33 | | #include <gpac/nodes_x3d.h> |
34 | | |
35 | | #if !defined(GPAC_DISABLE_COMPOSITOR) |
36 | | |
37 | | /*SVG properties*/ |
38 | | #ifndef GPAC_DISABLE_SVG |
39 | | #include <gpac/scenegraph_svg.h> |
40 | | #endif |
41 | | |
42 | | GF_EXPORT |
43 | | Double gf_scene_get_time(void *_is) |
44 | 0 | { |
45 | 0 | GF_Scene *scene = (GF_Scene *)_is; |
46 | 0 | #if 1 |
47 | 0 | u64 ret; |
48 | 0 | GF_Clock *ck; |
49 | 0 | if (!scene || !scene->root_od || !scene->root_od->ck) return 0.0; |
50 | 0 | ck = scene->root_od->ck; |
51 | 0 | ret = gf_clock_time_absolute(ck); |
52 | 0 | if ((scene->root_od->media_stop_time>0) && ((u64) scene->root_od->media_stop_time<ret)) |
53 | 0 | ret = scene->root_od->media_stop_time; |
54 | 0 | return ret/1000.0; |
55 | | #else |
56 | | return scene->simulation_time; |
57 | | #endif |
58 | 0 | } |
59 | | |
60 | | #ifndef GPAC_DISABLE_VRML |
61 | | |
62 | | void gf_storage_save(M_Storage *storage); |
63 | | #endif |
64 | | |
65 | | static void inline_on_media_event(GF_Scene *scene, u32 type) |
66 | 0 | { |
67 | 0 | gf_odm_service_media_event(scene->root_od, type); |
68 | 0 | } |
69 | | |
70 | | |
71 | | void gf_scene_message_ex(GF_Scene *scene, const char *service, const char *message, GF_Err error, Bool no_filtering) |
72 | 0 | { |
73 | 0 | GF_Event evt; |
74 | 0 | if (!scene || !scene->compositor) return; |
75 | 0 | memset(&evt, 0, sizeof(GF_Event)); |
76 | 0 | evt.type = GF_EVENT_MESSAGE; |
77 | 0 | evt.message.service = service; |
78 | 0 | evt.message.message = message; |
79 | 0 | evt.message.error = error; |
80 | |
|
81 | 0 | if (no_filtering) { |
82 | 0 | gf_filter_ui_event(scene->compositor->filter, &evt); |
83 | 0 | } else { |
84 | 0 | gf_filter_send_gf_event(scene->compositor->filter, &evt); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | void gf_scene_message(GF_Scene *scene, const char *service, const char *message, GF_Err error) |
89 | 0 | { |
90 | 0 | gf_scene_message_ex(scene, service, message, error, 0); |
91 | 0 | } |
92 | | |
93 | | |
94 | | char *gf_scene_resolve_xlink(GF_Node *node, char *the_url) |
95 | 0 | { |
96 | 0 | char *url; |
97 | 0 | GF_Scene *scene = gf_sg_get_private(gf_node_get_graph(node)); |
98 | 0 | if (!scene) return gf_strdup(the_url); |
99 | | |
100 | 0 | url = gf_strdup(the_url); |
101 | 0 | #ifndef GPAC_DISABLE_SVG |
102 | | /*apply XML:base*/ |
103 | 0 | while (node) { |
104 | 0 | GF_FieldInfo info; |
105 | 0 | if (gf_node_get_attribute_by_tag(node, TAG_XML_ATT_base, 0, 0, &info)==GF_OK) { |
106 | 0 | char *new_url = gf_url_concatenate( ((XMLRI*)info.far_ptr)->string, url); |
107 | 0 | if (new_url) { |
108 | 0 | gf_free(url); |
109 | 0 | url = new_url; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | node = gf_node_get_parent(node, 0); |
113 | 0 | } |
114 | 0 | #endif |
115 | | |
116 | | /*if this is a fragment and no XML:BASE was found, this is a fragment of the current document*/ |
117 | 0 | if (url[0]=='#') return url; |
118 | | |
119 | 0 | if (scene->redirect_xml_base) { |
120 | 0 | the_url = gf_url_concatenate(scene->redirect_xml_base, url); |
121 | 0 | } else { |
122 | | // the_url = gf_url_concatenate(is->root_od->net_service->url, url); |
123 | | /*the root url of a document should be "." if not specified, so that the final URL resolve happens only once |
124 | | at the service level*/ |
125 | 0 | the_url = gf_strdup(url); |
126 | 0 | } |
127 | 0 | gf_free(url); |
128 | 0 | return the_url; |
129 | 0 | } |
130 | | |
131 | | static Bool gf_scene_script_action(void *opaque, GF_JSAPIActionType type, GF_Node *n, GF_JSAPIParam *param) |
132 | 0 | { |
133 | 0 | Bool ret; |
134 | 0 | GF_Scene *root_scene; |
135 | 0 | GF_Scene *scene = (GF_Scene *) opaque; |
136 | 0 | if (!scene) return GF_FALSE; |
137 | 0 | root_scene = gf_scene_get_root_scene(scene); |
138 | |
|
139 | 0 | if (type==GF_JSAPI_OP_MESSAGE) { |
140 | 0 | gf_scene_message_ex(scene, scene->root_od->scene_ns->url, param->info.msg, param->info.e, 1); |
141 | 0 | return 1; |
142 | 0 | } |
143 | 0 | if (type==GF_JSAPI_OP_GET_COMPOSITOR) { |
144 | 0 | param->compositor = scene->compositor; |
145 | 0 | return 1; |
146 | 0 | } |
147 | 0 | if (type==GF_JSAPI_OP_RESOLVE_XLINK) { |
148 | 0 | #ifndef GPAC_DISABLE_SVG |
149 | 0 | param->uri.url = (char *) gf_scene_resolve_xlink(n, (char *) param->uri.url); |
150 | 0 | return 1; |
151 | | #else |
152 | | return 0; |
153 | | #endif |
154 | 0 | } |
155 | 0 | if (type==GF_JSAPI_OP_GET_OPT) { |
156 | 0 | param->gpac_cfg.key_val = gf_opts_get_key(param->gpac_cfg.section, param->gpac_cfg.key); |
157 | 0 | return 1; |
158 | 0 | } |
159 | 0 | if (type==GF_JSAPI_OP_SET_OPT) { |
160 | 0 | gf_opts_set_key(param->gpac_cfg.section, param->gpac_cfg.key, param->gpac_cfg.key_val); |
161 | 0 | return 1; |
162 | 0 | } |
163 | 0 | if (type==GF_JSAPI_OP_GET_DOWNLOAD_MANAGER) { |
164 | 0 | param->dnld_man = gf_filter_get_download_manager(scene->compositor->filter); |
165 | 0 | return 1; |
166 | 0 | } |
167 | 0 | if (type==GF_JSAPI_OP_SET_TITLE) { |
168 | 0 | GF_Event evt; |
169 | 0 | evt.type = GF_EVENT_SET_CAPTION; |
170 | 0 | evt.caption.caption = param->uri.url; |
171 | 0 | gf_filter_send_gf_event(scene->compositor->filter, &evt); |
172 | 0 | return 1; |
173 | 0 | } |
174 | 0 | if (type==GF_JSAPI_OP_GET_SUBSCENE) { |
175 | 0 | GF_Scene *a_scene = (GF_Scene *)gf_node_get_private(n); |
176 | 0 | param->scene = a_scene->graph; |
177 | 0 | return 1; |
178 | 0 | } |
179 | | |
180 | 0 | if (type==GF_JSAPI_OP_RESOLVE_URI) { |
181 | 0 | char *url; |
182 | 0 | char new_url[GF_MAX_PATH]; |
183 | 0 | char localized_url[GF_MAX_PATH]; |
184 | |
|
185 | 0 | Bool result=GF_FALSE; |
186 | 0 | GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(gf_node_get_graph(n)); |
187 | 0 | url = (char *)param->uri.url; |
188 | 0 | if (!url) { |
189 | 0 | param->uri.url = gf_strdup(a_scene->root_od->scene_ns->url); |
190 | 0 | param->uri.nb_params = 0; |
191 | 0 | return 1; |
192 | 0 | } |
193 | | |
194 | 0 | new_url[0]=0; |
195 | 0 | result = gf_filter_relocate_url(a_scene->compositor->filter, url, a_scene->root_od->scene_ns->url, new_url, localized_url); |
196 | |
|
197 | 0 | if (result) param->uri.url = gf_strdup(new_url); |
198 | 0 | else param->uri.url = gf_url_concatenate(a_scene->root_od->scene_ns->url, url); |
199 | 0 | return 1; |
200 | 0 | } |
201 | | |
202 | | /*special case for pause/stop/resume*/ |
203 | 0 | if (type==GF_JSAPI_OP_PAUSE_SVG) { |
204 | 0 | GF_SceneGraph *graph = gf_node_get_graph(n); |
205 | 0 | if (n == gf_sg_get_root_node(graph)) { |
206 | 0 | GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph); |
207 | 0 | if (a_scene->root_od->ck) gf_clock_pause(a_scene->root_od->ck); |
208 | 0 | return 1; |
209 | 0 | } |
210 | 0 | } |
211 | 0 | if (type==GF_JSAPI_OP_RESUME_SVG) { |
212 | 0 | GF_SceneGraph *graph = gf_node_get_graph(n); |
213 | 0 | if (n == gf_sg_get_root_node(graph)) { |
214 | 0 | GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph); |
215 | 0 | if (a_scene->root_od->ck) gf_clock_resume(a_scene->root_od->ck); |
216 | 0 | return 1; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | if (type==GF_JSAPI_OP_RESTART_SVG) { |
220 | 0 | GF_SceneGraph *graph = gf_node_get_graph(n); |
221 | 0 | if (n == gf_sg_get_root_node(graph)) { |
222 | 0 | GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph); |
223 | 0 | GF_Clock *ck = a_scene->root_od->ck; |
224 | 0 | if (ck) { |
225 | 0 | Bool is_paused = ck->nb_paused ? GF_TRUE : GF_FALSE; |
226 | 0 | if (is_paused) gf_clock_resume(ck); |
227 | 0 | gf_scene_restart_dynamic(a_scene, 0, 0, 0); |
228 | 0 | if (is_paused) gf_clock_pause(ck); |
229 | 0 | } |
230 | 0 | return 1; |
231 | 0 | } |
232 | 0 | return 0; |
233 | 0 | } |
234 | 0 | if (type==GF_JSAPI_OP_SET_SCENE_SPEED) { |
235 | 0 | GF_SceneGraph *graph = gf_node_get_graph(n); |
236 | 0 | if (n == gf_sg_get_root_node(graph)) { |
237 | 0 | GF_Scene *a_scene = (GF_Scene *)gf_sg_get_private(graph); |
238 | 0 | GF_Clock *ck = a_scene->root_od->ck; |
239 | 0 | if (ck) { |
240 | 0 | gf_clock_set_speed(ck, param->val); |
241 | 0 | } |
242 | 0 | return 1; |
243 | 0 | } |
244 | 0 | return 0; |
245 | 0 | } |
246 | | |
247 | | |
248 | 0 | ret = gf_sc_script_action(scene->compositor, type, n, param); |
249 | 0 | if (ret) return ret; |
250 | | |
251 | 0 | if (type==GF_JSAPI_OP_LOAD_URL) { |
252 | 0 | if (gf_sg_get_private(gf_node_get_graph(n)) == root_scene) { |
253 | 0 | GF_Event evt; |
254 | 0 | evt.type = GF_EVENT_NAVIGATE; |
255 | 0 | evt.navigate.to_url = param->uri.url; |
256 | 0 | evt.navigate.parameters = param->uri.params; |
257 | 0 | evt.navigate.param_count = param->uri.nb_params; |
258 | 0 | return gf_filter_send_gf_event(scene->compositor->filter, &evt); |
259 | 0 | } else { |
260 | | /*TODO*/ |
261 | 0 | return 0; |
262 | 0 | } |
263 | 0 | } |
264 | 0 | return 0; |
265 | 0 | } |
266 | | |
267 | | |
268 | | Bool gf_scene_is_root(GF_Scene *scene) |
269 | 0 | { |
270 | 0 | GF_Scene *s = scene; |
271 | 0 | while (s->root_od->parentscene) s = s->root_od->parentscene; |
272 | 0 | return (s==scene) ? GF_TRUE : GF_FALSE; |
273 | 0 | } |
274 | | |
275 | | GF_Scene *gf_scene_get_root_scene(GF_Scene *scene) |
276 | 0 | { |
277 | 0 | while (scene && scene->root_od && scene->root_od->parentscene) |
278 | 0 | scene = scene->root_od->parentscene; |
279 | 0 | return scene; |
280 | 0 | } |
281 | | |
282 | | |
283 | | |
284 | | GF_EXPORT |
285 | | GF_Scene *gf_scene_new(GF_Compositor *compositor, GF_Scene *parentScene) |
286 | 0 | { |
287 | 0 | GF_Scene *tmp; |
288 | 0 | if (!compositor && !parentScene) return NULL; |
289 | | |
290 | 0 | GF_SAFEALLOC(tmp, GF_Scene); |
291 | 0 | if (! tmp) return NULL; |
292 | | |
293 | 0 | tmp->resources = gf_list_new(); |
294 | 0 | tmp->scene_objects = gf_list_new(); |
295 | 0 | tmp->extra_scenes = gf_list_new(); |
296 | 0 | tmp->declared_addons = gf_list_new(); |
297 | | /*init inline scene*/ |
298 | 0 | if (parentScene) { |
299 | 0 | tmp->graph = gf_sg_new_subscene(parentScene->graph); |
300 | 0 | gf_assert(!compositor || (compositor==parentScene->compositor)); |
301 | 0 | tmp->compositor = parentScene->compositor; |
302 | 0 | } else { |
303 | 0 | tmp->graph = gf_sg_new(); |
304 | 0 | tmp->compositor = compositor; |
305 | | //only for the top scene |
306 | 0 | tmp->namespaces = gf_list_new(); |
307 | 0 | } |
308 | |
|
309 | 0 | gf_sg_set_private(tmp->graph, tmp); |
310 | 0 | gf_sg_set_node_callback(tmp->graph, gf_scene_node_callback); |
311 | 0 | gf_sg_set_scene_time_callback(tmp->graph, gf_scene_get_time); |
312 | 0 | if (tmp->compositor && !tmp->compositor->nojs) |
313 | 0 | gf_sg_set_script_action(tmp->graph, gf_scene_script_action, tmp); |
314 | | |
315 | | //copy over pause_at_first_frame flag so that new subscene is not paused right away |
316 | 0 | if (parentScene) |
317 | 0 | tmp->first_frame_pause_type = parentScene->first_frame_pause_type; |
318 | |
|
319 | 0 | #ifndef GPAC_DISABLE_VRML |
320 | 0 | tmp->extern_protos = gf_list_new(); |
321 | 0 | gf_sg_set_proto_loader(tmp->graph, gf_inline_get_proto_lib); |
322 | |
|
323 | 0 | tmp->storages = gf_list_new(); |
324 | 0 | tmp->keynavigators = gf_list_new(); |
325 | 0 | tmp->attached_inlines = gf_list_new(); |
326 | 0 | #endif |
327 | |
|
328 | 0 | tmp->on_media_event = inline_on_media_event; |
329 | 0 | return tmp; |
330 | 0 | } |
331 | | |
332 | | static void gf_scene_reset_urls(GF_Scene *scene) |
333 | 0 | { |
334 | 0 | #define SFURL_RESET(__url) if (__url.url) gf_free(__url.url);\ |
335 | 0 | memset(&__url, 0, sizeof(SFURL)); |
336 | |
|
337 | 0 | SFURL_RESET(scene->audio_url); |
338 | 0 | SFURL_RESET(scene->visual_url); |
339 | 0 | SFURL_RESET(scene->text_url); |
340 | 0 | SFURL_RESET(scene->subs_url); |
341 | 0 | SFURL_RESET(scene->dims_url); |
342 | 0 | } |
343 | | |
344 | | GF_EXPORT |
345 | | void gf_scene_del(GF_Scene *scene) |
346 | 0 | { |
347 | 0 | gf_list_del(scene->resources); |
348 | 0 | gf_assert(!gf_list_count(scene->extra_scenes) ); |
349 | 0 | gf_list_del(scene->extra_scenes); |
350 | |
|
351 | 0 | #ifndef GPAC_DISABLE_VRML |
352 | 0 | while (gf_list_count(scene->extern_protos)) { |
353 | 0 | GF_ProtoLink *pl = (GF_ProtoLink *)gf_list_get(scene->extern_protos, 0); |
354 | 0 | gf_list_rem(scene->extern_protos, 0); |
355 | 0 | gf_free(pl); |
356 | 0 | } |
357 | 0 | gf_list_del(scene->extern_protos); |
358 | 0 | #endif |
359 | |
|
360 | 0 | gf_sc_node_destroy(scene->compositor, NULL, scene->graph); |
361 | | |
362 | | /*delete the scene graph*/ |
363 | 0 | gf_sg_del(scene->graph); |
364 | | |
365 | | /*don't touch the root_od, will be deleted by the parent scene*/ |
366 | | |
367 | | /*clean all remaining associations*/ |
368 | 0 | while (gf_list_count(scene->scene_objects)) { |
369 | 0 | GF_MediaObject *obj = (GF_MediaObject *)gf_list_get(scene->scene_objects, 0); |
370 | 0 | if (obj->odm) obj->odm->mo = NULL; |
371 | 0 | gf_list_rem(scene->scene_objects, 0); |
372 | 0 | gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); |
373 | 0 | gf_mo_del(obj); |
374 | 0 | } |
375 | 0 | gf_list_del(scene->scene_objects); |
376 | 0 | #ifndef GPAC_DISABLE_VRML |
377 | 0 | gf_list_del(scene->storages); |
378 | 0 | gf_list_del(scene->keynavigators); |
379 | 0 | #endif |
380 | |
|
381 | 0 | gf_list_del(scene->declared_addons); |
382 | |
|
383 | 0 | gf_scene_reset_urls(scene); |
384 | |
|
385 | 0 | if (scene->fragment_uri) gf_free(scene->fragment_uri); |
386 | 0 | if (scene->redirect_xml_base) gf_free(scene->redirect_xml_base); |
387 | |
|
388 | 0 | if (scene->namespaces) { |
389 | 0 | while (gf_list_count(scene->namespaces)) { |
390 | 0 | GF_SceneNamespace *sns = gf_list_pop_back(scene->namespaces); |
391 | 0 | gf_scene_ns_del(sns, scene); |
392 | 0 | } |
393 | 0 | gf_list_del(scene->namespaces); |
394 | 0 | } |
395 | |
|
396 | 0 | #ifndef GPAC_DISABLE_VRML |
397 | 0 | while (gf_list_count(scene->attached_inlines)) { |
398 | 0 | GF_Node *n_inline = gf_list_pop_back(scene->attached_inlines); |
399 | 0 | gf_node_set_private(n_inline, NULL); |
400 | 0 | } |
401 | 0 | gf_list_del(scene->attached_inlines); |
402 | 0 | #endif |
403 | |
|
404 | 0 | if (scene->compositor->root_scene == scene) |
405 | 0 | scene->compositor->root_scene = NULL; |
406 | |
|
407 | 0 | gf_free(scene); |
408 | 0 | } |
409 | | |
410 | | GF_EXPORT |
411 | | GF_ObjectManager *gf_scene_find_odm(GF_Scene *scene, u16 OD_ID) |
412 | 0 | { |
413 | 0 | GF_ObjectManager *odm; |
414 | 0 | u32 i=0; |
415 | 0 | while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) { |
416 | 0 | if (odm->ID == OD_ID) return odm; |
417 | 0 | } |
418 | 0 | return NULL; |
419 | 0 | } |
420 | | |
421 | | GF_EXPORT |
422 | | void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown) |
423 | 0 | { |
424 | 0 | u32 i; |
425 | 0 | GF_MediaObject *obj; |
426 | 0 | GF_ObjectManager *odm; |
427 | |
|
428 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[Scene] disconnecting\n")); |
429 | | |
430 | | |
431 | | /*force unregistering of inline nodes (for safety)*/ |
432 | 0 | if (for_shutdown && scene->root_od->mo) { |
433 | | /*reset private stack of all inline nodes still registered*/ |
434 | 0 | while (gf_mo_event_target_count(scene->root_od->mo)) { |
435 | 0 | gf_mo_event_target_remove_by_index(scene->root_od->mo, 0); |
436 | 0 | #ifndef GPAC_DISABLE_VRML |
437 | 0 | GF_Node *n = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(scene->root_od->mo, 0)); |
438 | 0 | if (n) { |
439 | 0 | switch (gf_node_get_tag(n)) { |
440 | 0 | case TAG_MPEG4_Inline: |
441 | 0 | #ifndef GPAC_DISABLE_X3D |
442 | 0 | case TAG_X3D_Inline: |
443 | 0 | #endif |
444 | 0 | gf_node_set_private(n, NULL); |
445 | 0 | break; |
446 | 0 | } |
447 | 0 | } |
448 | 0 | #endif |
449 | 0 | } |
450 | 0 | } |
451 | | |
452 | | //Ivica patch: Remove all Registered InputSensor nodes -> shut down the InputSensor threads -> prevent illegal access on deleted pointers |
453 | 0 | #ifndef GPAC_DISABLE_VRML |
454 | 0 | if (for_shutdown) { |
455 | 0 | i = 0; |
456 | 0 | while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) { |
457 | 0 | if (odm->mo) { |
458 | 0 | odm->ck = NULL; |
459 | 0 | obj = odm->mo; |
460 | 0 | while (gf_mo_event_target_count(obj)) { |
461 | 0 | GF_Node *n = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(obj, 0)); |
462 | 0 | if (n) { |
463 | 0 | switch (gf_node_get_tag(n)) { |
464 | 0 | case TAG_MPEG4_InputSensor: |
465 | 0 | { |
466 | 0 | M_InputSensor* is = (M_InputSensor*)n; |
467 | 0 | is->enabled = 0; |
468 | 0 | InputSensorModified(n); |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | } |
472 | 0 | } |
473 | 0 | gf_mo_event_target_remove_by_index(obj, 0); |
474 | 0 | } |
475 | 0 | } |
476 | 0 | } |
477 | 0 | } |
478 | 0 | #endif |
479 | | |
480 | | /*remove all associated eventTargets*/ |
481 | 0 | i=0; |
482 | 0 | while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) { |
483 | 0 | gf_mo_event_target_reset(obj); |
484 | 0 | } |
485 | |
|
486 | 0 | #ifndef GPAC_DISABLE_VRML |
487 | 0 | while (gf_list_count(scene->storages)) { |
488 | 0 | M_Storage *storage = (M_Storage *)gf_list_get(scene->storages, 0); |
489 | 0 | gf_list_rem(scene->storages, 0); |
490 | 0 | if (storage->_auto) gf_storage_save(storage); |
491 | 0 | } |
492 | 0 | #endif |
493 | |
|
494 | 0 | if (!scene->root_od->parentscene) { |
495 | 0 | gf_sc_set_scene(scene->compositor, NULL); |
496 | 0 | } |
497 | |
|
498 | 0 | gf_scene_reset_addons(scene); |
499 | | |
500 | | /*release the scene - at this stage, we no longer have any node stack referring to our media objects */ |
501 | |
|
502 | 0 | gf_sc_node_destroy(scene->compositor, NULL, scene->graph); |
503 | 0 | gf_sg_reset(scene->graph); |
504 | 0 | scene->graph_attached = 0; |
505 | | |
506 | | |
507 | | /*disconnect all objects*/ |
508 | 0 | if (!for_shutdown) { |
509 | 0 | i=0; |
510 | | /*stop all objects but DON'T DESTROY THEM*/ |
511 | 0 | while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) { |
512 | 0 | if (odm->state) gf_odm_disconnect(odm, GF_FALSE); |
513 | | //reset OD ID until we get a new OD re-attaching this object |
514 | 0 | odm->ID = 0; |
515 | 0 | } |
516 | | /*reset all stream associations*/ |
517 | 0 | i=0; |
518 | 0 | while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) { |
519 | 0 | gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); |
520 | 0 | gf_mo_event_target_reset(obj); |
521 | 0 | } |
522 | 0 | } |
523 | | /*disconnect and remove all objects*/ |
524 | 0 | else { |
525 | 0 | while (gf_list_count(scene->resources)) { |
526 | 0 | odm = (GF_ObjectManager *)gf_list_get(scene->resources, 0); |
527 | 0 | if (odm->skip_disconnect_state) { |
528 | 0 | gf_list_rem(scene->resources, 0); |
529 | 0 | } else { |
530 | 0 | gf_odm_disconnect(odm, for_shutdown ? 2 : 0); |
531 | 0 | } |
532 | 0 | } |
533 | 0 | #ifndef GPAC_DISABLE_VRML |
534 | 0 | while (gf_list_count(scene->extern_protos)) { |
535 | 0 | GF_ProtoLink *pl = (GF_ProtoLink *)gf_list_get(scene->extern_protos, 0); |
536 | 0 | gf_list_rem(scene->extern_protos, 0); |
537 | 0 | gf_free(pl); |
538 | 0 | } |
539 | 0 | #endif |
540 | 0 | } |
541 | | |
542 | | /*remove stream associations*/ |
543 | 0 | while (gf_list_count(scene->scene_objects)) { |
544 | 0 | obj = (GF_MediaObject*)gf_list_get(scene->scene_objects, 0); |
545 | 0 | gf_list_rem(scene->scene_objects, 0); |
546 | 0 | if (obj->odm) obj->odm->mo = NULL; |
547 | 0 | gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); |
548 | 0 | gf_mo_del(obj); |
549 | 0 | } |
550 | | |
551 | | //reset URLs |
552 | 0 | gf_scene_reset_urls(scene); |
553 | |
|
554 | 0 | scene->object_attached = 0; |
555 | 0 | } |
556 | | |
557 | | static void gf_scene_insert_object(GF_Scene *scene, GF_MediaObject *mo, Bool lock_timelines, GF_MediaObject *sync_ref, Bool keep_fragment, GF_Scene *original_parent_scene, Bool for_addon) |
558 | 0 | { |
559 | 0 | GF_ObjectManager *odm; |
560 | 0 | char *url, *final_url; |
561 | 0 | if (!mo || !scene) return; |
562 | | |
563 | 0 | odm = gf_odm_new(); |
564 | | /*remember OD*/ |
565 | 0 | odm->mo = mo; |
566 | 0 | mo->odm = odm; |
567 | 0 | odm->parentscene = scene; |
568 | 0 | odm->ID = GF_MEDIA_EXTERNAL_ID; |
569 | 0 | if (scene->force_single_timeline) lock_timelines = GF_TRUE; |
570 | |
|
571 | 0 | url = mo->URLs.vals[0].url; |
572 | |
|
573 | 0 | if (!url) return; |
574 | | |
575 | 0 | if (!stricmp(url, "KeySensor")) { |
576 | 0 | final_url = "gpac://KeySensor"; |
577 | 0 | } else if (!stricmp(url, "StringSensor")) { |
578 | 0 | final_url = "gpac://StringSensor"; |
579 | 0 | } else if (!stricmp(url, "Mouse")) { |
580 | 0 | final_url = "gpac://Mouse"; |
581 | 0 | } else { |
582 | 0 | final_url = mo->URLs.vals[0].url; |
583 | 0 | if (lock_timelines) odm->flags |= GF_ODM_INHERIT_TIMELINE; |
584 | 0 | } |
585 | | |
586 | | /*HACK - temp storage of sync ref*/ |
587 | 0 | if (sync_ref) odm->sync_ref = sync_ref; |
588 | |
|
589 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[Scene] Inserting new MediaObject %08x for resource %s\n", odm->mo, url)); |
590 | 0 | gf_list_add(scene->resources, odm); |
591 | |
|
592 | 0 | gf_odm_setup_remote_object(odm, original_parent_scene ? original_parent_scene->root_od->scene_ns : NULL, final_url, for_addon); |
593 | 0 | } |
594 | | |
595 | | static void gf_scene_reinsert_object(GF_Scene *scene, GF_MediaObject *mo) |
596 | 0 | { |
597 | 0 | u32 i; |
598 | 0 | gf_free(mo->URLs.vals[0].url); |
599 | 0 | mo->URLs.vals[0].url = NULL; |
600 | 0 | for (i=0; i<mo->URLs.count-1; i++) mo->URLs.vals[i].url = mo->URLs.vals[i+1].url; |
601 | 0 | mo->URLs.vals[mo->URLs.count-1].url = NULL; |
602 | 0 | mo->URLs.count-=1; |
603 | | /*FIXME - we should re-ananlyse whether the fragment is important or not ...*/ |
604 | 0 | gf_scene_insert_object(scene, mo, GF_FALSE, NULL, GF_FALSE, NULL, GF_FALSE); |
605 | 0 | } |
606 | | |
607 | | |
608 | | void gf_scene_remove_object(GF_Scene *scene, GF_ObjectManager *odm, u32 for_shutdown) |
609 | 0 | { |
610 | 0 | u32 i; |
611 | 0 | GF_MediaObject *obj; |
612 | |
|
613 | 0 | gf_list_del_item(scene->resources, odm); |
614 | |
|
615 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[Scene] removing ODM %d\n", odm->ID )); |
616 | | |
617 | |
|
618 | 0 | i=0; |
619 | 0 | while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) { |
620 | 0 | if ( |
621 | | /*assigned object*/ |
622 | 0 | (obj->odm==odm) || |
623 | | /*remote OD*/ |
624 | 0 | ((obj->OD_ID!=GF_MEDIA_EXTERNAL_ID) && (obj->OD_ID == odm->ID) ) |
625 | | /*dynamic OD*/ |
626 | 0 | || (obj->URLs.count && odm->scene_ns && (odm->scene_ns!=scene->root_od->scene_ns) && !stricmp(obj->URLs.vals[0].url, odm->scene_ns->url)) |
627 | 0 | ) { |
628 | 0 | u32 discard_obj = 0; |
629 | |
|
630 | 0 | obj->flags = 0; |
631 | 0 | if (obj->odm) obj->odm->mo = NULL; |
632 | 0 | odm->mo = NULL; |
633 | 0 | obj->odm = NULL; |
634 | 0 | if (obj->pck) { |
635 | 0 | gf_filter_pck_unref(obj->pck); |
636 | 0 | obj->pck = NULL; |
637 | 0 | } |
638 | |
|
639 | 0 | obj->frame = NULL; |
640 | 0 | obj->framesize = obj->timestamp = 0; |
641 | 0 | obj->config_changed = GF_TRUE; |
642 | | |
643 | | |
644 | | /*if graph not attached we can remove the link (this is likely scene shutdown for some error)*/ |
645 | 0 | if (!scene->graph_attached) { |
646 | 0 | #ifndef GPAC_DISABLE_VRML |
647 | 0 | GF_ProtoLink *pl; |
648 | 0 | u32 j=0; |
649 | 0 | while ((pl = (GF_ProtoLink *)gf_list_enum(scene->extern_protos, &j))) { |
650 | 0 | if (pl->mo==obj) { |
651 | 0 | pl->mo = NULL; |
652 | 0 | break; |
653 | 0 | } |
654 | 0 | } |
655 | 0 | #endif |
656 | 0 | discard_obj = 1; |
657 | 0 | } else if (!for_shutdown) { |
658 | | /*if dynamic OD and more than 1 URLs resetup*/ |
659 | 0 | if ((obj->OD_ID==GF_MEDIA_EXTERNAL_ID) && (obj->URLs.count>1)) { |
660 | 0 | discard_obj = 0; |
661 | 0 | gf_scene_reinsert_object(scene, obj); |
662 | 0 | } else { |
663 | 0 | discard_obj = 2; |
664 | 0 | } |
665 | 0 | } |
666 | | /*discard media object*/ |
667 | 0 | else if (for_shutdown==2) |
668 | 0 | discard_obj = 1; |
669 | | |
670 | | /*reset private stack of all inline nodes still registered*/ |
671 | 0 | if (discard_obj) { |
672 | 0 | while (gf_mo_event_target_count(obj)) { |
673 | 0 | gf_mo_event_target_remove_by_index(obj, 0); |
674 | 0 | #ifndef GPAC_DISABLE_VRML |
675 | 0 | GF_Node *n = (GF_Node *)gf_event_target_get_node(gf_mo_event_target_get(obj, 0)); |
676 | 0 | if (n) { |
677 | 0 | switch (gf_node_get_tag(n)) { |
678 | 0 | case TAG_MPEG4_Inline: |
679 | 0 | #ifndef GPAC_DISABLE_X3D |
680 | 0 | case TAG_X3D_Inline: |
681 | 0 | #endif |
682 | 0 | if (obj->num_open) gf_mo_stop(&obj); |
683 | 0 | gf_node_set_private(n, NULL); |
684 | 0 | break; |
685 | 0 | default: |
686 | 0 | gf_sc_mo_destroyed(n); |
687 | 0 | break; |
688 | 0 | } |
689 | 0 | } |
690 | 0 | #endif |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | 0 | if ((discard_obj==1) && !obj->num_open) { |
695 | 0 | gf_list_rem(scene->scene_objects, i-1); |
696 | 0 | i--; |
697 | 0 | gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); |
698 | 0 | gf_mo_del(obj); |
699 | 0 | } |
700 | | // return; |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | | |
706 | | //browse all channels and update buffering info |
707 | | void gf_scene_buffering_info(GF_Scene *scene, Bool rebuffer_done) |
708 | 0 | { |
709 | 0 | GF_ODMExtraPid *xpid; |
710 | 0 | u32 i, j; |
711 | 0 | u64 max_buffer, cur_buffer, min_time, max_buff_val=0; |
712 | 0 | u64 buf_val; |
713 | 0 | GF_Event evt; |
714 | 0 | GF_ObjectManager *odm; |
715 | 0 | if (!scene) return; |
716 | | |
717 | 0 | max_buffer = cur_buffer = 0; |
718 | 0 | min_time = (u64) -1; |
719 | | |
720 | | /*get buffering on root OD*/ |
721 | 0 | odm = scene->root_od; |
722 | 0 | if (!scene->is_dynamic_scene && odm->pid && odm->buffer_playout_ms) { |
723 | 0 | if (max_buff_val < odm->buffer_playout_ms) |
724 | 0 | max_buff_val = odm->buffer_playout_ms; |
725 | |
|
726 | 0 | if (odm->nb_buffering) { |
727 | 0 | max_buffer += odm->buffer_playout_ms; |
728 | 0 | buf_val = gf_filter_pid_query_buffer_duration(odm->pid, GF_FALSE) / 1000; |
729 | 0 | if (min_time>buf_val) min_time = buf_val; |
730 | |
|
731 | 0 | if (buf_val > max_buffer) buf_val = max_buffer; |
732 | 0 | cur_buffer += (buf_val>0) ? buf_val : 1; |
733 | 0 | i=0; |
734 | 0 | while ((xpid = gf_list_enum(odm->extra_pids, &i))) { |
735 | 0 | max_buffer += odm->buffer_playout_ms; |
736 | 0 | buf_val = gf_filter_pid_query_buffer_duration(xpid->pid, GF_FALSE) / 1000; |
737 | 0 | if (min_time>buf_val) min_time = buf_val; |
738 | |
|
739 | 0 | if (buf_val > max_buffer) buf_val = max_buffer; |
740 | 0 | cur_buffer += (buf_val>0) ? buf_val : 1; |
741 | 0 | } |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | /*get buffering on all ODs*/ |
746 | 0 | i=0; |
747 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
748 | 0 | if (!odm->buffer_playout_ms) continue; |
749 | 0 | if (max_buff_val < odm->buffer_playout_ms) |
750 | 0 | max_buff_val = odm->buffer_playout_ms; |
751 | |
|
752 | 0 | if (!odm->nb_buffering) continue; |
753 | | |
754 | 0 | max_buffer += odm->buffer_playout_ms; |
755 | 0 | buf_val = gf_filter_pid_query_buffer_duration(odm->pid, GF_FALSE) / 1000; |
756 | 0 | if (min_time>buf_val) min_time = buf_val; |
757 | |
|
758 | 0 | if (buf_val > max_buffer) buf_val = max_buffer; |
759 | 0 | cur_buffer += (buf_val>0) ? buf_val : 1; |
760 | 0 | j=0; |
761 | 0 | while ((xpid = gf_list_enum(odm->extra_pids, &j))) { |
762 | 0 | max_buffer += odm->buffer_playout_ms; |
763 | 0 | buf_val = gf_filter_pid_query_buffer_duration(xpid->pid, GF_FALSE) / 1000; |
764 | 0 | if (min_time>buf_val) min_time = buf_val; |
765 | |
|
766 | 0 | if (buf_val > max_buffer) buf_val = max_buffer; |
767 | 0 | cur_buffer += (buf_val>0) ? buf_val : 1; |
768 | 0 | } |
769 | 0 | } |
770 | | //likely local file playback with buffer disabled |
771 | 0 | if (!max_buff_val) |
772 | 0 | return; |
773 | | //destruction |
774 | 0 | if (!scene->root_od->scene_ns) |
775 | 0 | return; |
776 | | |
777 | | //if buffering, fire GF_EVENT_MEDIA_PROGRESS - use the min buffer we just computed |
778 | 0 | if ((rebuffer_done || scene->nb_buffering) && (max_buffer>0) ) { |
779 | 0 | gf_odm_service_media_event_with_download(scene->root_od, GF_EVENT_MEDIA_PROGRESS, 0, 0, 0, (u32) (100 * cur_buffer / max_buffer) + 1, (u32) min_time); |
780 | 0 | } |
781 | |
|
782 | 0 | evt.type = GF_EVENT_PROGRESS; |
783 | 0 | evt.progress.progress_type = 0; |
784 | 0 | evt.progress.service = scene->root_od->scene_ns->url; |
785 | |
|
786 | 0 | if (!max_buffer || !cur_buffer || (max_buffer <= cur_buffer)) { |
787 | 0 | if (!max_buffer) max_buffer=max_buff_val; |
788 | 0 | evt.progress.done = evt.progress.total = (u32) (max_buffer / 1000); |
789 | 0 | } else { |
790 | 0 | evt.progress.done = (u32) (cur_buffer / 1000); |
791 | 0 | evt.progress.total = (u32) (max_buffer / 1000); |
792 | 0 | } |
793 | 0 | gf_sc_send_event(scene->compositor, &evt); |
794 | 0 | } |
795 | | |
796 | | |
797 | | |
798 | | void gf_scene_notify_event(GF_Scene *scene, u32 event_type, GF_Node *n, void *_event, GF_Err code, Bool no_queuing) |
799 | 0 | { |
800 | | /*fire resize event*/ |
801 | 0 | #ifndef GPAC_DISABLE_SVG |
802 | 0 | GF_Node *root; |
803 | 0 | u32 i, count; |
804 | 0 | u32 w, h; |
805 | 0 | GF_DOM_Event evt, *dom_event; |
806 | 0 | dom_event = (GF_DOM_Event *)_event; |
807 | |
|
808 | 0 | if (!scene) return; |
809 | 0 | root = gf_sg_get_root_node(scene->graph); |
810 | |
|
811 | 0 | if (!dom_event) { |
812 | 0 | memset(&evt, 0, sizeof(GF_DOM_Event)); |
813 | 0 | dom_event = &evt; |
814 | 0 | w = h = 0; |
815 | 0 | gf_sg_get_scene_size_info(scene->graph, &w, &h); |
816 | 0 | evt.type = event_type; |
817 | 0 | evt.screen_rect.width = INT2FIX(w); |
818 | 0 | evt.screen_rect.height = INT2FIX(h); |
819 | 0 | evt.key_flags = scene->is_dynamic_scene ? (scene->vr_type ? 2 : 1) : 0; |
820 | 0 | if (root) { |
821 | 0 | #ifndef GPAC_DISABLE_VRML |
822 | 0 | switch (gf_node_get_tag(root)) { |
823 | 0 | case TAG_MPEG4_Group: |
824 | 0 | case TAG_MPEG4_Layer3D: |
825 | 0 | evt.detail = 1; |
826 | 0 | break; |
827 | 0 | #ifndef GPAC_DISABLE_X3D |
828 | 0 | case TAG_X3D_Group: |
829 | 0 | evt.detail = 2; |
830 | 0 | break; |
831 | 0 | #endif |
832 | 0 | } |
833 | 0 | #endif |
834 | 0 | } |
835 | | |
836 | 0 | evt.error_state = code; |
837 | 0 | } |
838 | 0 | if (n) { |
839 | 0 | if (no_queuing) { |
840 | 0 | gf_dom_event_fire(n, dom_event); |
841 | 0 | } else { |
842 | 0 | gf_sc_queue_dom_event(scene->compositor, n, dom_event); |
843 | 0 | } |
844 | 0 | } else { |
845 | 0 | if (root) { |
846 | 0 | if (no_queuing) { |
847 | 0 | gf_dom_event_fire(root, dom_event); |
848 | 0 | } else { |
849 | 0 | gf_sc_queue_dom_event(scene->compositor, root, dom_event); |
850 | 0 | } |
851 | 0 | } |
852 | |
|
853 | 0 | count=scene->root_od->mo ? gf_mo_event_target_count(scene->root_od->mo) : 0; |
854 | 0 | for (i=0; i<count; i++) { |
855 | 0 | GF_Node *an = gf_event_target_get_node(gf_mo_event_target_get(scene->root_od->mo, i)); |
856 | 0 | if (no_queuing) { |
857 | 0 | gf_dom_event_fire(an, dom_event); |
858 | 0 | } else { |
859 | 0 | gf_sc_queue_dom_event(scene->compositor, an, dom_event); |
860 | 0 | } |
861 | 0 | } |
862 | 0 | } |
863 | 0 | #endif |
864 | 0 | } |
865 | | |
866 | | |
867 | | GF_EXPORT |
868 | | void gf_scene_attach_to_compositor(GF_Scene *scene) |
869 | 0 | { |
870 | 0 | if (!scene->root_od) return; |
871 | 0 | if (scene->graph_attached==1) return; |
872 | | |
873 | 0 | scene->graph_attached = 1; |
874 | 0 | if (gf_sg_get_root_node(scene->graph)==NULL) { |
875 | 0 | gf_sc_invalidate(scene->compositor, NULL); |
876 | 0 | return; |
877 | 0 | } |
878 | | |
879 | | /*locate fragment IRI*/ |
880 | 0 | if (scene->root_od->scene_ns && scene->root_od->scene_ns->url) { |
881 | 0 | char *url; |
882 | 0 | if (scene->fragment_uri) { |
883 | 0 | gf_free(scene->fragment_uri); |
884 | 0 | scene->fragment_uri = NULL; |
885 | 0 | } |
886 | 0 | url = strchr(scene->root_od->scene_ns->url, '#'); |
887 | 0 | if (url) scene->fragment_uri = gf_strdup(url+1); |
888 | 0 | } |
889 | | |
890 | | /*main display scene, setup compositor*/ |
891 | 0 | if (!scene->root_od->parentscene) { |
892 | | //force a resize |
893 | 0 | if (gf_sg_get_scene_size_info(scene->graph, NULL, NULL)) { |
894 | 0 | scene->compositor->scene_width = 0; |
895 | 0 | scene->compositor->scene_height = 0; |
896 | 0 | } |
897 | 0 | gf_sc_set_scene(scene->compositor, scene->graph); |
898 | 0 | } |
899 | 0 | else { |
900 | 0 | u32 i, count; |
901 | 0 | count = scene->root_od->mo ? gf_mo_event_target_count(scene->root_od->mo) : 0; |
902 | 0 | for (i=0; i<count; i++) { |
903 | 0 | gf_node_dirty_parents( gf_event_target_get_node(gf_mo_event_target_get(scene->root_od->mo, i))); |
904 | 0 | } |
905 | 0 | gf_sc_invalidate(scene->compositor, NULL); |
906 | |
|
907 | 0 | if (scene->root_od->parentscene->is_dynamic_scene) { |
908 | 0 | u32 w, h; |
909 | 0 | gf_sg_get_scene_size_info(scene->graph, &w, &h); |
910 | 0 | gf_sc_set_size(scene->compositor, w, h); |
911 | 0 | } |
912 | | /*trigger a scene attach event*/ |
913 | 0 | gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, GF_OK, GF_FALSE); |
914 | 0 | } |
915 | 0 | } |
916 | | |
917 | | static GF_MediaObject *IS_CheckExistingObject(GF_Scene *scene, MFURL *urls, u32 type) |
918 | 0 | { |
919 | 0 | GF_MediaObject *obj; |
920 | 0 | u32 i = 0; |
921 | 0 | while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) { |
922 | 0 | if (type && (type != obj->type)) continue; |
923 | 0 | if ((obj->OD_ID == GF_MEDIA_EXTERNAL_ID) && gf_mo_is_same_url(obj, urls, NULL, 0)) return obj; |
924 | 0 | else if ((obj->OD_ID != GF_MEDIA_EXTERNAL_ID) && (obj->OD_ID == urls->vals[0].OD_ID)) return obj; |
925 | 0 | } |
926 | 0 | return NULL; |
927 | 0 | } |
928 | | |
929 | | static GFINLINE Bool is_match_obj_type(u32 type, u32 hint_type) |
930 | 0 | { |
931 | 0 | if (!hint_type) return GF_TRUE; |
932 | 0 | if (type==hint_type) return GF_TRUE; |
933 | | /*TEXT are used by animation stream*/ |
934 | 0 | if ((type==GF_MEDIA_OBJECT_TEXT) && (hint_type==GF_MEDIA_OBJECT_UPDATES)) return GF_TRUE; |
935 | 0 | return GF_FALSE; |
936 | 0 | } |
937 | | |
938 | | GF_EXPORT |
939 | | GF_MediaObject *gf_scene_get_media_object_ex(GF_Scene *scene, MFURL *url, u32 obj_type_hint, Bool lock_timelines, GF_MediaObject *sync_ref, Bool force_new_if_not_attached, GF_Node *node, GF_Scene *parent_scene_addon) |
940 | 0 | { |
941 | 0 | GF_MediaObject *obj; |
942 | 0 | Bool keep_fragment = GF_TRUE; |
943 | 0 | GF_Scene *original_parent_scene = parent_scene_addon; |
944 | 0 | Bool first_pass = force_new_if_not_attached ? GF_FALSE : GF_TRUE; |
945 | 0 | u32 i, OD_ID; |
946 | |
|
947 | 0 | OD_ID = gf_mo_get_od_id(url); |
948 | 0 | if (!OD_ID) return NULL; |
949 | | |
950 | | /*we may have overridden the time lines in parent scene, thus all objects in this scene have the same clock*/ |
951 | 0 | if (scene->root_od->parentscene && scene->root_od->parentscene->force_single_timeline) |
952 | 0 | lock_timelines = GF_TRUE; |
953 | | |
954 | | /*the first pass is needed to detect objects already inserted and registered with the given nodes, regardless of |
955 | | the force_new_if_not_attached flag. This ty^pically occurs when a resource is first created then linked to an animation/inline*/ |
956 | 0 | restart: |
957 | 0 | obj = NULL; |
958 | 0 | i=0; |
959 | 0 | while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) { |
960 | 0 | Bool odm_matches = GF_FALSE; |
961 | |
|
962 | 0 | if ( |
963 | | /*regular OD scheme*/ |
964 | 0 | (OD_ID != GF_MEDIA_EXTERNAL_ID && (obj->OD_ID==OD_ID)) |
965 | 0 | || |
966 | | /*dynamic OD scheme - !! obj->OD_ID may different from GF_MEDIA_EXTERNAL_ID when ODs are |
967 | | directly added to the compositor by the service*/ |
968 | 0 | ((OD_ID == GF_MEDIA_EXTERNAL_ID) |
969 | | /*if object type unknown (media control, media sensor), return first obj matching URL |
970 | | otherwise check types*/ |
971 | 0 | && is_match_obj_type(obj->type, obj_type_hint) |
972 | | /*locate sub-url in given one and handle fragments (viewpoint/segments/...)*/ |
973 | 0 | && gf_mo_is_same_url(obj, url, &keep_fragment, obj_type_hint) |
974 | 0 | ) |
975 | 0 | ) { |
976 | 0 | odm_matches = GF_TRUE; |
977 | 0 | } |
978 | |
|
979 | 0 | if (!odm_matches) continue; |
980 | | |
981 | 0 | if (obj->odm) { |
982 | 0 | Bool can_reuse = GF_TRUE; |
983 | 0 | Bool timeline_locked = (obj->odm->flags & GF_ODM_INHERIT_TIMELINE) ? GF_TRUE : GF_FALSE; |
984 | | |
985 | | //addon object always share the timeline |
986 | 0 | if (obj->odm->addon || obj->odm->parentscene->root_od->addon) |
987 | 0 | timeline_locked = lock_timelines = 1; |
988 | |
|
989 | 0 | if (timeline_locked != lock_timelines) |
990 | 0 | continue; |
991 | | |
992 | 0 | if (obj->odm->flags & GF_ODM_DESTROYED) can_reuse = GF_FALSE; |
993 | |
|
994 | 0 | if (!can_reuse) continue; |
995 | |
|
996 | 0 | } |
997 | | |
998 | 0 | if (!first_pass && !force_new_if_not_attached) { |
999 | 0 | if (node && (gf_mo_event_target_find_by_node(obj, node)<0)) |
1000 | 0 | gf_mo_event_target_add_node(obj, node); |
1001 | 0 | return obj; |
1002 | 0 | } |
1003 | | /*special case where the URL is requested twice for the same node: use the existing resource*/ |
1004 | 0 | else if (node && (gf_mo_event_target_find_by_node(obj, node)>=0)) { |
1005 | 0 | return obj; |
1006 | 0 | } |
1007 | 0 | } |
1008 | 0 | if (first_pass) { |
1009 | 0 | first_pass = GF_FALSE; |
1010 | 0 | goto restart; |
1011 | 0 | } |
1012 | | |
1013 | | /*we cannot create an OD manager at this point*/ |
1014 | 0 | if (obj_type_hint==GF_MEDIA_OBJECT_UNDEF) { |
1015 | 0 | return NULL; |
1016 | 0 | } |
1017 | | |
1018 | | /*create a new object identification*/ |
1019 | 0 | obj = gf_mo_new(); |
1020 | 0 | obj->OD_ID = OD_ID; |
1021 | 0 | obj->type = obj_type_hint; |
1022 | | |
1023 | | /*register node with object*/ |
1024 | 0 | if (node) { |
1025 | 0 | gf_mo_event_target_add_node(obj, node); |
1026 | |
|
1027 | 0 | original_parent_scene = (GF_Scene*) gf_sg_get_private(gf_node_get_graph(node)); |
1028 | 0 | } |
1029 | | |
1030 | | /*if animation stream object, remember originating node |
1031 | | !! FIXME - this should be cleaned up !! |
1032 | | */ |
1033 | 0 | if (obj->type == GF_MEDIA_OBJECT_UPDATES) |
1034 | 0 | obj->node_ptr = node; |
1035 | |
|
1036 | 0 | gf_list_add(scene->scene_objects, obj); |
1037 | 0 | if (OD_ID == GF_MEDIA_EXTERNAL_ID) { |
1038 | 0 | gf_sg_vrml_copy_mfurl(&obj->URLs, url); |
1039 | 0 | gf_scene_insert_object(scene, obj, lock_timelines, sync_ref, keep_fragment, original_parent_scene, parent_scene_addon ? GF_TRUE : GF_FALSE); |
1040 | | /*safety check!!!*/ |
1041 | 0 | if (gf_list_find(scene->scene_objects, obj)<0) { |
1042 | 0 | return NULL; |
1043 | 0 | } |
1044 | | |
1045 | 0 | if (obj->odm==NULL) { |
1046 | 0 | gf_list_del_item(scene->scene_objects, obj); |
1047 | 0 | gf_mo_event_target_reset(obj); |
1048 | 0 | gf_mo_del(obj); |
1049 | 0 | return NULL; |
1050 | 0 | } |
1051 | 0 | } else { |
1052 | 0 | u32 j; |
1053 | 0 | for (j=0; j<gf_list_count(scene->resources); j++) { |
1054 | 0 | GF_ObjectManager *odm = gf_list_get(scene->resources, j); |
1055 | 0 | if (odm->ID == obj->OD_ID) { |
1056 | 0 | obj->odm = odm; |
1057 | 0 | break; |
1058 | 0 | } |
1059 | 0 | } |
1060 | 0 | } |
1061 | 0 | return obj; |
1062 | 0 | } |
1063 | | |
1064 | | GF_MediaObject *gf_scene_get_media_object(GF_Scene *scene, MFURL *url, u32 obj_type_hint, Bool lock_timelines) |
1065 | 0 | { |
1066 | 0 | return gf_scene_get_media_object_ex(scene, url, obj_type_hint, lock_timelines, NULL, GF_FALSE, NULL, NULL); |
1067 | 0 | } |
1068 | | |
1069 | | |
1070 | | static void gf_scene_get_video_size(GF_MediaObject *mo, u32 *w, u32 *h) |
1071 | 0 | { |
1072 | 0 | u32 pixel_ar; |
1073 | 0 | if (!gf_mo_get_visual_info_ex(mo, w, h, NULL, &pixel_ar, NULL, NULL, GF_FALSE)) return; |
1074 | 0 | if (mo->c_w && mo->c_h) { |
1075 | 0 | *w = (u32) mo->c_w; |
1076 | 0 | *h = (u32) mo->c_h; |
1077 | 0 | } |
1078 | 0 | if (pixel_ar) { |
1079 | 0 | u32 n, d; |
1080 | 0 | n = (pixel_ar>>16) & 0x0000FFFF; |
1081 | 0 | d = (pixel_ar) & 0x0000FFFF; |
1082 | 0 | *w = (*w * n) / d; |
1083 | 0 | } |
1084 | 0 | if ((mo->rotate==1) || (mo->rotate==3)) { |
1085 | 0 | u32 t = *w; |
1086 | 0 | *w = *h; |
1087 | 0 | *h = t; |
1088 | 0 | } |
1089 | |
|
1090 | | #ifndef GPAC_DISABLE_3D |
1091 | | if (mo->odm) { |
1092 | | if (mo->odm->parentscene->compositor->fpack==GF_3D_STEREO_TOP) *h /= 2; |
1093 | | else if (mo->odm->parentscene->compositor->fpack==GF_3D_STEREO_SIDE) *w /= 2; |
1094 | | } |
1095 | | #endif |
1096 | 0 | } |
1097 | | |
1098 | | |
1099 | | GF_EXPORT |
1100 | | void gf_scene_setup_object(GF_Scene *scene, GF_ObjectManager *odm) |
1101 | 0 | { |
1102 | 0 | GF_MediaObject *obj; |
1103 | 0 | u32 i; |
1104 | |
|
1105 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[Scene] Setup object manager %d (MO %p)\n", odm->ID, odm->mo)); |
1106 | | |
1107 | | /*an object may already be assigned (when using ESD URLs, setup is performed twice)*/ |
1108 | 0 | if (odm->mo != NULL) goto existing; |
1109 | | |
1110 | 0 | i=0; |
1111 | 0 | while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) { |
1112 | | /*make sure services are different*/ |
1113 | 0 | if (obj->odm && (odm->source_id != obj->odm->source_id)) continue; |
1114 | | |
1115 | 0 | if (obj->OD_ID==GF_MEDIA_EXTERNAL_ID) { |
1116 | 0 | if (obj->odm == odm) { |
1117 | | /*assign FINAL OD, not parent*/ |
1118 | 0 | obj->odm = odm; |
1119 | 0 | odm->mo = obj; |
1120 | 0 | goto existing; |
1121 | 0 | } |
1122 | 0 | } |
1123 | 0 | else if (obj->OD_ID == odm->ID) { |
1124 | 0 | GF_ObjectManager *old_odm = NULL; |
1125 | | //OD update may trigger reassignment of ODM |
1126 | 0 | if (obj->odm && (obj->odm != odm)) { |
1127 | 0 | old_odm = obj->odm; |
1128 | 0 | } |
1129 | 0 | obj->odm = odm; |
1130 | 0 | if (old_odm) { |
1131 | 0 | if (old_odm->mo) old_odm->mo->odm = NULL; |
1132 | 0 | old_odm->mo = NULL; |
1133 | 0 | gf_odm_disconnect(old_odm, GF_TRUE); |
1134 | 0 | } |
1135 | 0 | odm->mo = obj; |
1136 | 0 | goto existing; |
1137 | 0 | } |
1138 | 0 | } |
1139 | | /*newly created OD*/ |
1140 | 0 | odm->mo = gf_mo_new(); |
1141 | 0 | gf_list_add(scene->scene_objects, odm->mo); |
1142 | 0 | odm->mo->odm = odm; |
1143 | 0 | odm->mo->OD_ID = odm->ID; |
1144 | |
|
1145 | 0 | existing: |
1146 | | /*setup object type*/ |
1147 | 0 | if (!odm->type) odm->type = GF_MEDIA_OBJECT_SCENE; |
1148 | 0 | else if (odm->type == GF_STREAM_VISUAL) odm->mo->type = GF_MEDIA_OBJECT_VIDEO; |
1149 | 0 | else if (odm->type == GF_STREAM_AUDIO) odm->mo->type = GF_MEDIA_OBJECT_AUDIO; |
1150 | 0 | else if (odm->type == GF_STREAM_TEXT) odm->mo->type = GF_MEDIA_OBJECT_TEXT; |
1151 | 0 | else if (odm->type == GF_STREAM_SCENE) odm->mo->type = GF_MEDIA_OBJECT_UPDATES; |
1152 | | |
1153 | | /*update info*/ |
1154 | 0 | gf_mo_update_caps(odm->mo); |
1155 | | /*media object playback has already been requested by the scene, trigger media start*/ |
1156 | 0 | if (odm->mo->num_open && !odm->state) { |
1157 | 0 | gf_odm_start(odm); |
1158 | 0 | if (odm->mo->speed != FIX_ONE) gf_odm_set_speed(odm, odm->mo->speed, GF_TRUE); |
1159 | 0 | } |
1160 | 0 | if ((odm->mo->type==GF_MEDIA_OBJECT_VIDEO) && scene->is_dynamic_scene && !odm->parentscene->root_od->addon) { |
1161 | 0 | gf_scene_force_size_to_video(scene, odm->mo); |
1162 | 0 | } |
1163 | 0 | else if (!odm->scene_ns->source_filter && (odm->flags & GF_ODM_PASSTHROUGH)) { |
1164 | 0 | u32 w, h; |
1165 | 0 | gf_scene_get_video_size(odm->mo, &w, &h); |
1166 | 0 | gf_sc_set_size(scene->compositor, w, h); |
1167 | 0 | } |
1168 | | /*invalidate scene for all nodes using the OD*/ |
1169 | 0 | gf_sc_invalidate(odm->parentscene->compositor, NULL); |
1170 | 0 | } |
1171 | | |
1172 | | GF_EXPORT |
1173 | | void gf_scene_set_duration(GF_Scene *scene) |
1174 | 0 | { |
1175 | 0 | Double dur; |
1176 | 0 | u32 i; |
1177 | 0 | u64 max_dur; |
1178 | 0 | GF_ObjectManager *odm; |
1179 | 0 | #ifndef GPAC_DISABLE_VRML |
1180 | 0 | MediaSensorStack *media_sens; |
1181 | 0 | #endif |
1182 | 0 | GF_Clock *ck; |
1183 | | |
1184 | | /*this is not normative but works in so many cases... set the duration to the max duration |
1185 | | of all streams sharing the clock*/ |
1186 | 0 | ck = gf_odm_get_media_clock(scene->root_od); |
1187 | 0 | max_dur = scene->root_od->duration; |
1188 | 0 | i=0; |
1189 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
1190 | 0 | if (!ck || gf_odm_shares_clock(odm, ck)) { |
1191 | 0 | if (odm->duration>max_dur) max_dur = odm->duration; |
1192 | 0 | } |
1193 | 0 | } |
1194 | 0 | if (scene->duration == max_dur) return; |
1195 | | |
1196 | 0 | scene->duration = max_dur; |
1197 | 0 | if (scene->is_dynamic_scene && !scene->root_od->duration) scene->root_od->duration = max_dur; |
1198 | |
|
1199 | 0 | dur = (Double) (s64) scene->duration; |
1200 | 0 | dur /= 1000; |
1201 | |
|
1202 | 0 | #ifndef GPAC_DISABLE_VRML |
1203 | 0 | i=0; |
1204 | 0 | while ((media_sens = (MediaSensorStack*)gf_list_enum(scene->root_od->ms_stack, &i))) { |
1205 | 0 | if (media_sens->sensor->isActive) { |
1206 | 0 | media_sens->sensor->mediaDuration = dur; |
1207 | 0 | gf_node_event_out((GF_Node *) media_sens->sensor, 3/*"mediaDuration"*/); |
1208 | 0 | } |
1209 | 0 | } |
1210 | 0 | #endif |
1211 | |
|
1212 | 0 | if (!scene->root_od->parentscene) { |
1213 | 0 | GF_Event evt; |
1214 | 0 | evt.type = GF_EVENT_DURATION; |
1215 | 0 | evt.duration.duration = dur; |
1216 | 0 | evt.duration.can_seek = (scene->root_od->flags & GF_ODM_NO_TIME_CTRL) ? GF_FALSE : GF_TRUE; |
1217 | 0 | if (dur<1.0) evt.duration.can_seek = 0; |
1218 | 0 | gf_sc_send_event(scene->compositor, &evt); |
1219 | 0 | } |
1220 | 0 | } |
1221 | | |
1222 | | GF_EXPORT |
1223 | | void gf_scene_set_timeshift_depth(GF_Scene *scene) |
1224 | 0 | { |
1225 | 0 | u32 i; |
1226 | 0 | u32 max_timeshift; |
1227 | 0 | GF_ObjectManager *odm; |
1228 | 0 | GF_Clock *ck; |
1229 | |
|
1230 | 0 | ck = gf_odm_get_media_clock(scene->root_od); |
1231 | 0 | max_timeshift = scene->root_od->timeshift_depth; |
1232 | 0 | i=0; |
1233 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
1234 | 0 | if (!ck || gf_odm_shares_clock(odm, ck)) { |
1235 | 0 | if (odm->timeshift_depth > max_timeshift) max_timeshift = odm->timeshift_depth; |
1236 | 0 | } |
1237 | 0 | } |
1238 | 0 | if (scene->timeshift_depth == max_timeshift) return; |
1239 | | |
1240 | 0 | scene->timeshift_depth = max_timeshift; |
1241 | 0 | if (scene->is_dynamic_scene && !scene->root_od->timeshift_depth) scene->root_od->timeshift_depth = max_timeshift; |
1242 | 0 | if (scene->root_od->addon && (scene->root_od->addon->addon_type==GF_ADDON_TYPE_MAIN)) { |
1243 | 0 | if (scene->root_od->parentscene->is_dynamic_scene && (scene->root_od->parentscene->timeshift_depth < max_timeshift)) { |
1244 | 0 | scene->root_od->parentscene->timeshift_depth = max_timeshift; |
1245 | 0 | scene->root_od->parentscene->root_od->timeshift_depth = max_timeshift; |
1246 | 0 | gf_scene_notify_event(scene->root_od->parentscene, GF_EVENT_TIMESHIFT_DEPTH, NULL, NULL, GF_OK, GF_FALSE); |
1247 | 0 | } |
1248 | 0 | } else { |
1249 | 0 | gf_scene_notify_event(scene, GF_EVENT_TIMESHIFT_DEPTH, NULL, NULL, GF_OK, GF_FALSE); |
1250 | 0 | } |
1251 | 0 | } |
1252 | | |
1253 | | |
1254 | | GF_MediaObject *gf_scene_find_object(GF_Scene *scene, u16 ODID, char *url) |
1255 | 0 | { |
1256 | 0 | u32 i; |
1257 | 0 | GF_MediaObject *mo; |
1258 | 0 | if (!url && !ODID) return NULL; |
1259 | 0 | i=0; |
1260 | 0 | while ((mo = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) { |
1261 | 0 | if ((ODID==GF_MEDIA_EXTERNAL_ID) && url) { |
1262 | 0 | if (mo->URLs.count && !stricmp(mo->URLs.vals[0].url, url)) return mo; |
1263 | 0 | } else if (mo->OD_ID==ODID) { |
1264 | 0 | return mo; |
1265 | 0 | } |
1266 | 0 | } |
1267 | 0 | return NULL; |
1268 | 0 | } |
1269 | | |
1270 | | |
1271 | | GF_EXPORT |
1272 | | void gf_scene_register_extra_graph(GF_Scene *scene, GF_SceneGraph *extra_scene, Bool do_remove) |
1273 | 0 | { |
1274 | 0 | if (do_remove) { |
1275 | 0 | if (gf_list_find(scene->extra_scenes, extra_scene)<0) return; |
1276 | 0 | gf_list_del_item(scene->extra_scenes, extra_scene); |
1277 | | /*for root scene*/ |
1278 | 0 | if (! scene->root_od->parentscene) { |
1279 | 0 | gf_sc_register_extra_graph(scene->compositor, extra_scene, 1); |
1280 | 0 | } |
1281 | 0 | } else { |
1282 | 0 | if (gf_list_find(scene->extra_scenes, extra_scene)>=0) return; |
1283 | 0 | gf_list_add(scene->extra_scenes, extra_scene); |
1284 | | /*for root scene*/ |
1285 | 0 | if (!scene->root_od->parentscene) { |
1286 | 0 | gf_sc_register_extra_graph(scene->compositor, extra_scene, 0); |
1287 | 0 | } |
1288 | 0 | } |
1289 | 0 | } |
1290 | | |
1291 | | void gf_scene_force_size_to_video(GF_Scene *scene, GF_MediaObject *mo) |
1292 | 0 | { |
1293 | 0 | const GF_PropertyValue *p; |
1294 | 0 | u32 w, h; |
1295 | 0 | Bool is_subs_pic = GF_FALSE; |
1296 | | //if picture subtitle don't resize unless standalone playback |
1297 | 0 | if (gf_list_count(scene->resources)>1) { |
1298 | 0 | p = gf_filter_pid_get_property(mo->odm->pid, GF_PROP_PID_ORIG_STREAM_TYPE); |
1299 | 0 | if (p && (p->value.uint==GF_STREAM_TEXT)) is_subs_pic = GF_TRUE; |
1300 | 0 | } |
1301 | 0 | if (!is_subs_pic) { |
1302 | 0 | gf_scene_get_video_size(mo, &w, &h); |
1303 | 0 | if (w && h) gf_scene_force_size(scene, w, h); |
1304 | 0 | return; |
1305 | 0 | } |
1306 | | //subpic stream, update props |
1307 | 0 | w = scene->compositor->scene_width; |
1308 | 0 | h = scene->compositor->scene_height; |
1309 | |
|
1310 | 0 | #ifndef GPAC_DISABLE_VRML |
1311 | 0 | M_Transform2D *tr = NULL; |
1312 | 0 | tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, "TR_SUBT_IMG"); |
1313 | 0 | if (!tr) return; |
1314 | | |
1315 | 0 | tr->translation.x = 0; |
1316 | 0 | p = gf_filter_pid_get_property(mo->odm->pid, GF_PROP_PID_TRANS_X_INV); |
1317 | 0 | if (p) { |
1318 | 0 | tr->translation.x = -INT2FIX(w/2) + INT2FIX(p->value.sint+mo->width/2); |
1319 | 0 | } else { |
1320 | 0 | p = gf_filter_pid_get_property(mo->odm->pid, GF_PROP_PID_TRANS_X); |
1321 | 0 | if (p) tr->translation.x = INT2FIX(p->value.sint); |
1322 | 0 | } |
1323 | 0 | tr->translation.x += scene->compositor->subtx; |
1324 | |
|
1325 | 0 | tr->translation.y = 0; |
1326 | 0 | p = gf_filter_pid_get_property(mo->odm->pid, GF_PROP_PID_TRANS_Y_INV); |
1327 | 0 | if (p) { |
1328 | 0 | tr->translation.y = INT2FIX( (s32)h/2 - p->value.sint); |
1329 | 0 | } else { |
1330 | 0 | p = gf_filter_pid_get_property(mo->odm->pid, GF_PROP_PID_TRANS_Y); |
1331 | 0 | if (p) tr->translation.y = INT2FIX(p->value.sint); |
1332 | 0 | } |
1333 | 0 | tr->translation.y += scene->compositor->subty; |
1334 | 0 | gf_node_changed((GF_Node *)tr, NULL); |
1335 | 0 | #endif /*GPAC_DISABLE_VRML*/ |
1336 | 0 | } |
1337 | | |
1338 | | #ifndef GPAC_DISABLE_VRML |
1339 | | |
1340 | | static void IS_UpdateVideoPos(GF_Scene *scene) |
1341 | 0 | { |
1342 | 0 | MFURL url; |
1343 | 0 | M_Transform2D *tr; |
1344 | 0 | GF_MediaObject *mo; |
1345 | 0 | u32 w, h, v_w, v_h; |
1346 | 0 | if (!scene->visual_url.OD_ID && !scene->visual_url.url) return; |
1347 | | |
1348 | 0 | if (scene->vr_type) return; |
1349 | | |
1350 | 0 | url.count = 1; |
1351 | 0 | url.vals = &scene->visual_url; |
1352 | 0 | mo = IS_CheckExistingObject(scene, &url, GF_MEDIA_OBJECT_VIDEO); |
1353 | 0 | if (!mo) return; |
1354 | 0 | tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, "DYN_TRANS"); |
1355 | 0 | if (!tr) return; |
1356 | | |
1357 | 0 | gf_sg_get_scene_size_info(scene->graph, &w, &h); |
1358 | 0 | if (!w || !h) return; |
1359 | | |
1360 | 0 | gf_scene_get_video_size(mo, &v_w, &v_h); |
1361 | 0 | if (scene->force_size_set) { |
1362 | 0 | if (v_w && v_h) { |
1363 | 0 | tr->scale.x = gf_divfix(INT2FIX(w), INT2FIX(v_w)); |
1364 | 0 | tr->scale.y = gf_divfix(INT2FIX(h), INT2FIX(v_h)); |
1365 | 0 | } |
1366 | 0 | tr->translation.x = tr->translation.y = 0; |
1367 | 0 | } else { |
1368 | 0 | tr->scale.x = tr->scale.y = FIX_ONE; |
1369 | 0 | tr->translation.x = INT2FIX((s32) (w - v_w)) / 2; |
1370 | 0 | tr->translation.y = INT2FIX((s32) (h - v_h)) / 2; |
1371 | 0 | } |
1372 | 0 | gf_node_dirty_set((GF_Node *)tr, 0, 0); |
1373 | |
|
1374 | 0 | gf_scene_set_addon_layout_info(scene, scene->addon_position, scene->addon_size_factor); |
1375 | |
|
1376 | 0 | } |
1377 | | |
1378 | | static GF_Node *is_create_node(GF_SceneGraph *sg, u32 tag, const char *def_name) |
1379 | 0 | { |
1380 | 0 | GF_Node *n = gf_node_new(sg, tag); |
1381 | 0 | if (n) { |
1382 | 0 | if (def_name) gf_node_set_id(n, gf_sg_get_next_available_node_id(sg), def_name); |
1383 | 0 | gf_node_init(n); |
1384 | 0 | } |
1385 | 0 | return n; |
1386 | 0 | } |
1387 | | |
1388 | | static Bool is_odm_url(SFURL *url, GF_ObjectManager *odm) |
1389 | 0 | { |
1390 | 0 | if (!url->OD_ID && !url->url) return 0; |
1391 | 0 | if (odm->ID != GF_MEDIA_EXTERNAL_ID) return (url->OD_ID==odm->ID) ? 1 : 0; |
1392 | | |
1393 | 0 | if (!url->url || !odm->scene_ns || !odm->scene_ns->url) return 0; |
1394 | 0 | return !stricmp(url->url, odm->scene_ns->url); |
1395 | 0 | } |
1396 | | |
1397 | | static void set_media_url(GF_Scene *scene, SFURL *media_url, GF_Node *node, MFURL *node_url, u32 type) |
1398 | 0 | { |
1399 | 0 | u32 w, h; |
1400 | 0 | SFURL *sfu; |
1401 | 0 | Bool url_changed = 0; |
1402 | | |
1403 | | /*scene url is not set, find the first one*/ |
1404 | 0 | if (!media_url->OD_ID ) { |
1405 | 0 | u32 count, i; |
1406 | 0 | GF_ObjectManager *odm = NULL; |
1407 | 0 | count = gf_list_count(scene->resources); |
1408 | 0 | for (i=0; i<count; i++) { |
1409 | 0 | odm = (GF_ObjectManager*)gf_list_get(scene->resources, i); |
1410 | 0 | if (!odm->ID) |
1411 | 0 | continue; |
1412 | | |
1413 | | //only used to set subtile pictures |
1414 | 0 | if (type==GF_STREAM_UNKNOWN) { |
1415 | 0 | if (odm->type != GF_STREAM_VISUAL) continue; |
1416 | 0 | const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_ORIG_STREAM_TYPE); |
1417 | 0 | if (!p || (p->value.uint!=GF_STREAM_TEXT)) continue; |
1418 | 0 | } |
1419 | 0 | else if (type==GF_STREAM_TEXT) { |
1420 | 0 | if (odm->type!=type) continue; |
1421 | 0 | } |
1422 | 0 | else if (type==GF_STREAM_SCENE) { |
1423 | 0 | if (!odm->subscene || !odm->subscene->is_dynamic_scene) continue; |
1424 | | |
1425 | 0 | if (odm->subscene->root_od->addon) |
1426 | 0 | continue; |
1427 | 0 | } |
1428 | 0 | else { |
1429 | 0 | if (odm->type!=type) continue; |
1430 | 0 | if (type==GF_STREAM_VISUAL) { |
1431 | 0 | const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_ORIG_STREAM_TYPE); |
1432 | 0 | if (p && (p->value.uint!=type)) continue; |
1433 | 0 | } |
1434 | 0 | } |
1435 | | //todo: select preferred media format ? |
1436 | | |
1437 | 0 | if (scene->selected_service_id && (scene->selected_service_id != odm->ServiceID)) { |
1438 | | //objects inserted from broadcast may have been played but not yet registered with the scene, we need to force a stop |
1439 | 0 | if ((odm->mo && !odm->mo->num_open) || !odm->mo) { |
1440 | 0 | if (odm->state==GF_ODM_STATE_PLAY) { |
1441 | |
|
1442 | 0 | gf_odm_stop(odm, GF_FALSE); |
1443 | 0 | } |
1444 | 0 | } |
1445 | 0 | continue; |
1446 | 0 | } |
1447 | | |
1448 | | |
1449 | 0 | media_url->OD_ID = odm->ID; |
1450 | 0 | if (media_url->OD_ID==GF_MEDIA_EXTERNAL_ID) media_url->url = gf_strdup(odm->scene_ns->url); |
1451 | | |
1452 | | //happens when switching service in a TS multiplex |
1453 | 0 | if (!scene->root_od->ck) { |
1454 | 0 | scene->root_od->ck = odm->ck; |
1455 | 0 | } |
1456 | |
|
1457 | 0 | if (odm->mo && (type==GF_STREAM_VISUAL)) { |
1458 | 0 | gf_scene_get_video_size(odm->mo, &w, &h); |
1459 | 0 | if (w && h) { |
1460 | 0 | scene->force_size_set = 0; |
1461 | 0 | gf_sg_set_scene_size_info(scene->graph, w, h, 1); |
1462 | 0 | gf_scene_force_size(scene, w, h); |
1463 | 0 | } |
1464 | 0 | } |
1465 | 0 | break; |
1466 | 0 | } |
1467 | 0 | if (!odm) { |
1468 | 0 | if (media_url->OD_ID ) url_changed = 1; |
1469 | 0 | media_url->OD_ID = 0; |
1470 | 0 | if (media_url->url) { |
1471 | 0 | gf_free(media_url->url); |
1472 | 0 | media_url->url = NULL; |
1473 | 0 | } |
1474 | 0 | } |
1475 | 0 | } |
1476 | |
|
1477 | 0 | if (media_url->OD_ID) { |
1478 | 0 | if (!node_url->count) url_changed = 1; |
1479 | 0 | else if (node_url->vals[0].OD_ID!=media_url->OD_ID) url_changed = 1; |
1480 | 0 | else if (media_url->OD_ID==GF_MEDIA_EXTERNAL_ID) { |
1481 | 0 | if (!node_url->vals[0].url || !media_url->url || strcmp(node_url->vals[0].url, media_url->url) ) url_changed = 1; |
1482 | 0 | } |
1483 | 0 | } else { |
1484 | 0 | if (node_url->count) url_changed = 1; |
1485 | 0 | } |
1486 | |
|
1487 | 0 | if (url_changed) { |
1488 | 0 | gf_sg_vrml_mf_reset(node_url, GF_SG_VRML_MFURL); |
1489 | 0 | gf_sg_vrml_mf_append(node_url, GF_SG_VRML_MFURL, (void **) &sfu); |
1490 | 0 | sfu->OD_ID = media_url->OD_ID; |
1491 | 0 | if (media_url->url) sfu->url = gf_strdup(media_url->url); |
1492 | |
|
1493 | 0 | gf_node_changed(node, NULL); |
1494 | 0 | } |
1495 | |
|
1496 | 0 | } |
1497 | | |
1498 | | static void scene_video_mouse_move(void *param, GF_FieldInfo *field) |
1499 | 0 | { |
1500 | 0 | u32 i, count; |
1501 | 0 | Bool supported = GF_FALSE; |
1502 | 0 | GF_Scene *scene = (GF_Scene *) param; |
1503 | 0 | SFVec2f tx_coord = * ((SFVec2f *) field->far_ptr); |
1504 | 0 | GF_Node *n = gf_sg_find_node_by_name(scene->graph, "DYN_TOUCH"); |
1505 | |
|
1506 | 0 | if (!scene->visual_url.OD_ID) return; |
1507 | | |
1508 | 0 | count = gf_list_count(scene->resources); |
1509 | 0 | for (i=0; i<count; i++) { |
1510 | 0 | const GF_PropertyValue *prop; |
1511 | 0 | GF_ObjectManager *odm = gf_list_get(scene->resources, i); |
1512 | 0 | if (!odm->mo || !odm->pid) continue; |
1513 | | |
1514 | 0 | prop = gf_filter_pid_get_property_str(odm->pid, "MouseEvents"); |
1515 | 0 | if (prop && prop->value.boolean) { |
1516 | 0 | GF_FilterEvent evt; |
1517 | 0 | supported = GF_TRUE; |
1518 | 0 | GF_FEVT_INIT(evt, GF_FEVT_USER, odm->pid); |
1519 | |
|
1520 | 0 | evt.user_event.event.type = ((M_TouchSensor *)n)->isActive ? GF_EVENT_MOUSEDOWN : GF_EVENT_MOUSEUP; |
1521 | 0 | evt.user_event.event.mouse.x = FIX2INT( tx_coord.x * odm->mo->width); |
1522 | 0 | evt.user_event.event.mouse.y = FIX2INT( tx_coord.y * odm->mo->height); |
1523 | |
|
1524 | 0 | gf_filter_pid_send_event(odm->pid, &evt); |
1525 | |
|
1526 | 0 | } |
1527 | 0 | } |
1528 | 0 | if (!supported) { |
1529 | 0 | if (n) ((M_TouchSensor *)n)->enabled = GF_FALSE; |
1530 | 0 | } |
1531 | 0 | } |
1532 | | |
1533 | | static GF_Node *load_vr_proto_node(GF_SceneGraph *sg, const char *name, const char *def_name) |
1534 | 0 | { |
1535 | 0 | GF_Proto *proto; |
1536 | 0 | GF_Node *node; |
1537 | 0 | if (!name) name = "urn:inet:gpac:builtin:VRGeometry"; |
1538 | |
|
1539 | 0 | proto = gf_sg_find_proto(sg, 0, (char *) name); |
1540 | 0 | if (!proto) { |
1541 | 0 | MFURL *url; |
1542 | 0 | proto = gf_sg_proto_new(sg, 0, (char *) name, GF_FALSE); |
1543 | 0 | url = gf_sg_proto_get_extern_url(proto); |
1544 | 0 | if (url) |
1545 | 0 | url->vals = gf_malloc(sizeof(SFURL)); |
1546 | 0 | if (!url || !url->vals) { |
1547 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[Compositor] Failed to allocate VR proto\n")); |
1548 | 0 | return NULL; |
1549 | 0 | } |
1550 | 0 | url->count=1; |
1551 | 0 | url->vals[0].url = gf_strdup(name); |
1552 | 0 | } |
1553 | 0 | node = gf_sg_proto_create_instance(sg, proto); |
1554 | 0 | if (node) { |
1555 | 0 | if (def_name) gf_node_set_id(node, gf_sg_get_next_available_node_id(sg), def_name); |
1556 | 0 | gf_node_init(node); |
1557 | 0 | } |
1558 | 0 | return node; |
1559 | 0 | } |
1560 | | |
1561 | | void transform2d_set_untransform(GF_Node *node); |
1562 | | |
1563 | | static void create_movie(GF_Scene *scene, GF_Node *root, const char *tr_name, const char *texture_name, const char *name_geo, Bool is_untransform) |
1564 | 0 | { |
1565 | 0 | M_MovieTexture *mt; |
1566 | 0 | GF_Node *n1, *n2; |
1567 | | |
1568 | | /*create a shape and bitmap node*/ |
1569 | 0 | if (is_untransform) { |
1570 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Transform2D, NULL); |
1571 | 0 | transform2d_set_untransform(n2); |
1572 | 0 | gf_node_list_add_child( &((GF_ParentNode *)root)->children, n2); |
1573 | 0 | gf_node_register(n2, root); |
1574 | 0 | root = n2; |
1575 | 0 | } |
1576 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Transform2D, tr_name); |
1577 | 0 | gf_node_list_add_child( &((GF_ParentNode *)root)->children, n2); |
1578 | 0 | gf_node_register(n2, root); |
1579 | 0 | n1 = n2; |
1580 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Shape, NULL); |
1581 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1582 | 0 | gf_node_register(n2, n1); |
1583 | 0 | n1 = n2; |
1584 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Appearance, NULL); |
1585 | 0 | ((M_Shape *)n1)->appearance = n2; |
1586 | 0 | gf_node_register(n2, n1); |
1587 | | |
1588 | | /*note we create a movie texture even for images...*/ |
1589 | 0 | mt = (M_MovieTexture *) is_create_node(scene->graph, TAG_MPEG4_MovieTexture, texture_name); |
1590 | 0 | mt->startTime = gf_scene_get_time(scene); |
1591 | 0 | ((M_Appearance *)n2)->texture = (GF_Node *)mt; |
1592 | 0 | gf_node_register((GF_Node *)mt, n2); |
1593 | |
|
1594 | 0 | if (scene->srd_type && !is_untransform) { |
1595 | 0 | GF_Node *app = n2; |
1596 | |
|
1597 | 0 | if (scene->vr_type) { |
1598 | 0 | n2 = load_vr_proto_node(scene->graph, NULL, name_geo); |
1599 | 0 | } else { |
1600 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Rectangle, name_geo); |
1601 | 0 | } |
1602 | |
|
1603 | 0 | ((M_Shape *)n1)->geometry = n2; |
1604 | 0 | gf_node_register(n2, n1); |
1605 | | //force appearance material2D.filled = TRUE |
1606 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Material2D, NULL); |
1607 | 0 | ((M_Material2D *)n2)->filled = GF_TRUE; |
1608 | 0 | ((M_Appearance *)app)->material = n2; |
1609 | 0 | gf_node_register(n2, app); |
1610 | 0 | } else if (scene->vr_type && !is_untransform) { |
1611 | 0 | if (scene->vr_type==2) { |
1612 | 0 | n2 = load_vr_proto_node(scene->graph, "urn:inet:gpac:builtin:SRDSphere", name_geo); |
1613 | 0 | } else { |
1614 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Sphere, name_geo); |
1615 | 0 | } |
1616 | 0 | ((M_Shape *)n1)->geometry = n2; |
1617 | 0 | gf_node_register(n2, n1); |
1618 | 0 | } else { |
1619 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Bitmap, name_geo); |
1620 | 0 | ((M_Shape *)n1)->geometry = n2; |
1621 | 0 | gf_node_register(n2, n1); |
1622 | 0 | } |
1623 | 0 | } |
1624 | | /*regenerates the scene graph for dynamic scene. |
1625 | | This will also try to reload any previously presented streams. Note that in the usual case the scene is generated |
1626 | | just once when receiving the first OD AU (resources are NOT destroyed when seeking), but since the network may need |
1627 | | to update the OD resources, we still take care of it*/ |
1628 | | void gf_scene_regenerate(GF_Scene *scene) |
1629 | 0 | { |
1630 | 0 | GF_Node *n1, *n2; |
1631 | 0 | GF_Event evt; |
1632 | 0 | M_AudioClip *ac; |
1633 | 0 | M_MovieTexture *mt; |
1634 | 0 | M_AnimationStream *as; |
1635 | 0 | M_Inline *dims; |
1636 | 0 | if (scene->is_dynamic_scene != 1) return; |
1637 | | |
1638 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[Inline] Regenerating scene graph for service %s\n", scene->root_od->scene_ns->url)); |
1639 | |
|
1640 | 0 | ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1"); |
1641 | | |
1642 | | /*this is the first time, generate a scene graph*/ |
1643 | 0 | if (!ac) { |
1644 | 0 | GF_Node *root; |
1645 | | |
1646 | | /*create an OrderedGroup*/ |
1647 | 0 | n1 = is_create_node(scene->graph, scene->vr_type ? TAG_MPEG4_Group : TAG_MPEG4_OrderedGroup, NULL); |
1648 | 0 | gf_sg_set_root_node(scene->graph, n1); |
1649 | 0 | gf_node_register(n1, NULL); |
1650 | 0 | root = n1; |
1651 | |
|
1652 | 0 | if (! scene->root_od->parentscene && !scene->compositor->forced_alpha) { |
1653 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Background2D, "DYN_BACK"); |
1654 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1655 | 0 | gf_node_register(n2, n1); |
1656 | 0 | } |
1657 | | |
1658 | | //create VP info regardless of VR type |
1659 | 0 | if (scene->vr_type) { |
1660 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Viewpoint, "DYN_VP"); |
1661 | 0 | ((M_Viewpoint *)n2)->position.z = 0; |
1662 | |
|
1663 | | #ifndef GPAC_DISABLE_3D |
1664 | | ((M_Viewpoint *)n2)->fieldOfView = scene->compositor->fov; |
1665 | | #else |
1666 | 0 | ((M_Viewpoint *)n2)->fieldOfView = GF_PI/2; |
1667 | 0 | #endif |
1668 | |
|
1669 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1670 | 0 | gf_node_register(n2, n1); |
1671 | |
|
1672 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_NavigationInfo, NULL); |
1673 | 0 | gf_free( ((M_NavigationInfo *)n2)->type.vals[0] ); |
1674 | 0 | ((M_NavigationInfo *)n2)->type.vals[0] = gf_strdup("VR"); |
1675 | 0 | gf_free( ((M_NavigationInfo *)n2)->type.vals[1] ); |
1676 | 0 | ((M_NavigationInfo *)n2)->type.vals[1] = gf_strdup("NONE"); |
1677 | 0 | ((M_NavigationInfo *)n2)->type.count = 2; |
1678 | 0 | ((M_NavigationInfo *)n2)->avatarSize.count = 0; |
1679 | |
|
1680 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1681 | 0 | gf_node_register(n2, n1); |
1682 | 0 | } |
1683 | | |
1684 | | /*create an sound2D and an audioClip node*/ |
1685 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Sound2D, NULL); |
1686 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1687 | 0 | gf_node_register(n2, n1); |
1688 | |
|
1689 | 0 | ac = (M_AudioClip *) is_create_node(scene->graph, TAG_MPEG4_AudioClip, "DYN_AUDIO1"); |
1690 | 0 | ac->startTime = gf_scene_get_time(scene); |
1691 | 0 | ((M_Sound2D *)n2)->source = (GF_Node *)ac; |
1692 | 0 | gf_node_register((GF_Node *)ac, n2); |
1693 | | |
1694 | | |
1695 | | /*transform for any translation due to scene resize (3GPP)*/ |
1696 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_Transform2D, "DYN_TRANS"); |
1697 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1698 | 0 | gf_node_register(n2, n1); |
1699 | 0 | n1 = n2; |
1700 | | |
1701 | | /*create a touch sensor for the video*/ |
1702 | 0 | n2 = is_create_node(scene->graph, TAG_MPEG4_TouchSensor, "DYN_TOUCH"); |
1703 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); |
1704 | 0 | gf_node_register(n2, n1); |
1705 | 0 | gf_sg_route_new_to_callback(scene->graph, n2, 3/*"hitTexCoord_changed"*/, scene, scene_video_mouse_move); |
1706 | |
|
1707 | 0 | create_movie(scene, n1, "TR1", "DYN_VIDEO1", "DYN_GEOM1", GF_FALSE); |
1708 | 0 | create_movie(scene, n1, "TR_SUBT_IMG", "DYN_SUBT_IMG", "DYN_GEOM_SUBT_IMG", GF_TRUE); |
1709 | |
|
1710 | 0 | if (! scene->vr_type) { |
1711 | 0 | M_Transform2D *addon_tr; |
1712 | 0 | M_Layer2D *addon_layer; |
1713 | 0 | M_Inline *addon_scene; |
1714 | | |
1715 | | /*text streams controlled through AnimationStream*/ |
1716 | 0 | n1 = gf_sg_get_root_node(scene->graph); |
1717 | 0 | as = (M_AnimationStream *) is_create_node(scene->graph, TAG_MPEG4_AnimationStream, "DYN_TEXT"); |
1718 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)as); |
1719 | 0 | gf_node_register((GF_Node *)as, n1); |
1720 | | |
1721 | | |
1722 | | /*3GPP DIMS streams controlled */ |
1723 | 0 | n1 = gf_sg_get_root_node(scene->graph); |
1724 | 0 | dims = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, "DIMS_SCENE"); |
1725 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)dims); |
1726 | 0 | gf_node_register((GF_Node *)dims, n1); |
1727 | | |
1728 | | /*PVR version of live content*/ |
1729 | 0 | n1 = gf_sg_get_root_node(scene->graph); |
1730 | 0 | addon_scene = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, "PVR_SCENE"); |
1731 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)addon_scene); |
1732 | 0 | gf_node_register((GF_Node *)addon_scene, (GF_Node *)n1); |
1733 | | |
1734 | | /*Media addon scene*/ |
1735 | 0 | n1 = gf_sg_get_root_node(scene->graph); |
1736 | 0 | addon_tr = (M_Transform2D *) is_create_node(scene->graph, TAG_MPEG4_Transform2D, "ADDON_TRANS"); |
1737 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, (GF_Node*)addon_tr); |
1738 | 0 | gf_node_register((GF_Node *)addon_tr, n1); |
1739 | |
|
1740 | 0 | addon_layer = (M_Layer2D *) is_create_node(scene->graph, TAG_MPEG4_Layer2D, "ADDON_LAYER"); |
1741 | 0 | gf_node_list_add_child( &((GF_ParentNode *)addon_tr)->children, (GF_Node*)addon_layer); |
1742 | 0 | gf_node_register((GF_Node *)addon_layer, (GF_Node *)addon_tr); |
1743 | |
|
1744 | 0 | addon_scene = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, "ADDON_SCENE"); |
1745 | 0 | gf_node_list_add_child( &((GF_ParentNode *)addon_layer)->children, (GF_Node*)addon_scene); |
1746 | 0 | gf_node_register((GF_Node *)addon_scene, (GF_Node *)addon_layer); |
1747 | |
|
1748 | 0 | } |
1749 | | //VR mode, add VR headup |
1750 | 0 | else { |
1751 | 0 | GF_Node *vrhud = load_vr_proto_node(scene->graph, "urn:inet:gpac:builtin:VRHUD", NULL); |
1752 | 0 | gf_node_list_add_child( &((GF_ParentNode *)root)->children, (GF_Node*)vrhud); |
1753 | 0 | gf_node_register(vrhud, root); |
1754 | 0 | } |
1755 | | |
1756 | | //send activation for sensors |
1757 | 0 | memset(&evt, 0, sizeof(GF_Event)); |
1758 | 0 | evt.type = GF_EVENT_SENSOR_REQUEST; |
1759 | 0 | evt.activate_sensor.activate = scene->vr_type; |
1760 | 0 | evt.activate_sensor.sensor_type = GF_EVENT_SENSOR_ORIENTATION; |
1761 | 0 | if (gf_sc_send_event(scene->compositor, &evt)==GF_TRUE) { |
1762 | 0 | scene->compositor->orientation_sensors_active = scene->vr_type; |
1763 | 0 | } else { |
1764 | 0 | scene->compositor->orientation_sensors_active = GF_FALSE; |
1765 | 0 | } |
1766 | 0 | } |
1767 | |
|
1768 | 0 | if (scene->ambisonic_type) { |
1769 | 0 | char szName[20]; |
1770 | 0 | SFURL url; |
1771 | 0 | u32 i, count; |
1772 | 0 | GF_Node *an, *root = gf_sg_get_root_node(scene->graph); |
1773 | 0 | url.url = NULL; |
1774 | 0 | url.OD_ID = 0; |
1775 | |
|
1776 | 0 | count = gf_list_count(scene->resources); |
1777 | 0 | for (i=0; i<count; i++) { |
1778 | 0 | GF_ObjectManager *odm = gf_list_get(scene->resources, i); |
1779 | 0 | if (!odm->ambi_ch_id) continue; |
1780 | | |
1781 | 0 | sprintf(szName, "DYN_AUDIO%d", odm->ambi_ch_id); |
1782 | 0 | an = gf_sg_find_node_by_name(scene->graph, szName); |
1783 | 0 | if (!an) { |
1784 | | /*create an sound2D and an audioClip node*/ |
1785 | 0 | an = is_create_node(scene->graph, TAG_MPEG4_Sound2D, NULL); |
1786 | 0 | gf_node_list_add_child( &((GF_ParentNode *)root)->children, an); |
1787 | 0 | gf_node_register(an, root); |
1788 | |
|
1789 | 0 | ac = (M_AudioClip *) is_create_node(scene->graph, TAG_MPEG4_AudioClip, szName); |
1790 | 0 | ac->startTime = gf_scene_get_time(scene); |
1791 | 0 | ((M_Sound2D *)an)->source = (GF_Node *)ac; |
1792 | 0 | gf_node_register((GF_Node *)ac, an); |
1793 | 0 | } |
1794 | 0 | ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, szName); |
1795 | |
|
1796 | 0 | url.OD_ID = odm->ID; |
1797 | 0 | set_media_url(scene, &url, (GF_Node*)ac, &ac->url, GF_STREAM_AUDIO); |
1798 | 0 | } |
1799 | 0 | } else { |
1800 | 0 | ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1"); |
1801 | 0 | set_media_url(scene, &scene->audio_url, (GF_Node*)ac, &ac->url, GF_STREAM_AUDIO); |
1802 | 0 | } |
1803 | | |
1804 | |
|
1805 | 0 | if (scene->srd_type) { |
1806 | 0 | char szName[20], szTex[20], szGeom[20]; |
1807 | 0 | u32 i, nb_srd = 0, srd_missing = 0; |
1808 | 0 | GF_ObjectManager *a_odm; |
1809 | 0 | SFURL url; |
1810 | 0 | u32 sw, sh; |
1811 | 0 | s32 min_x, max_x, min_y, max_y; |
1812 | 0 | i=0; |
1813 | | |
1814 | | //we use 0 (and not INT_MAX) to always display the same thing regardless of holes in the srd description |
1815 | 0 | min_x = min_y = 0; |
1816 | 0 | max_x = max_y = 0; |
1817 | |
|
1818 | 0 | while ((a_odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
1819 | 0 | if (!a_odm->mo || !a_odm->mo->srd_w) { |
1820 | 0 | srd_missing++; |
1821 | 0 | continue; |
1822 | 0 | } |
1823 | 0 | if ((s32) a_odm->mo->srd_x < min_x) min_x = (s32) a_odm->mo->srd_x; |
1824 | 0 | if ((s32) a_odm->mo->srd_y < min_y) min_y = (s32) a_odm->mo->srd_y; |
1825 | |
|
1826 | 0 | if (!max_x) |
1827 | 0 | max_x = a_odm->mo->srd_full_w; |
1828 | 0 | if ((s32) a_odm->mo->srd_x + (s32) a_odm->mo->srd_w > min_x + max_x) |
1829 | 0 | max_x = (s32) a_odm->mo->srd_x + (s32) a_odm->mo->srd_w - min_x; |
1830 | |
|
1831 | 0 | if (!max_y) |
1832 | 0 | max_y = a_odm->mo->srd_full_h; |
1833 | |
|
1834 | 0 | if ((s32) a_odm->mo->srd_y + (s32) a_odm->mo->srd_h > min_y + max_y) |
1835 | 0 | max_y = (s32) a_odm->mo->srd_y + (s32) a_odm->mo->srd_h - min_y; |
1836 | |
|
1837 | 0 | nb_srd++; |
1838 | 0 | } |
1839 | |
|
1840 | 0 | n1 = gf_sg_find_node_by_name(scene->graph, "DYN_TRANS"); |
1841 | 0 | for (i=1; i<nb_srd+srd_missing; i++) { |
1842 | 0 | sprintf(szName, "TR%d", i+1); |
1843 | 0 | sprintf(szTex, "DYN_VIDEO%d", i+1); |
1844 | 0 | sprintf(szGeom, "DYN_GEOM%d", i+1); |
1845 | 0 | n2 = gf_sg_find_node_by_name(scene->graph, szGeom); |
1846 | 0 | if (!n2) { |
1847 | 0 | create_movie(scene, n1, szName, szTex, szGeom, GF_FALSE); |
1848 | 0 | } |
1849 | 0 | } |
1850 | 0 | gf_assert(max_x>min_x); |
1851 | 0 | gf_assert(max_y>min_y); |
1852 | |
|
1853 | 0 | scene->srd_min_x = min_x; |
1854 | 0 | scene->srd_min_y = min_y; |
1855 | 0 | scene->srd_max_x = max_x; |
1856 | 0 | scene->srd_max_y = max_y; |
1857 | |
|
1858 | 0 | url.url = NULL; |
1859 | 0 | gf_sg_get_scene_size_info(scene->graph, &sw, &sh); |
1860 | 0 | i=0; |
1861 | 0 | while ((a_odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
1862 | 0 | if (a_odm->mo && a_odm->mo->srd_w) { |
1863 | 0 | Fixed tw, th, tx, ty; |
1864 | |
|
1865 | 0 | sprintf(szName, "TR%d", i); |
1866 | 0 | sprintf(szTex, "DYN_VIDEO%d", i); |
1867 | 0 | sprintf(szGeom, "DYN_GEOM%d", i); |
1868 | 0 | url.OD_ID = a_odm->ID; |
1869 | |
|
1870 | 0 | mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, szTex); |
1871 | 0 | if (!mt) continue; |
1872 | | |
1873 | 0 | set_media_url(scene, &url, (GF_Node*)mt, &mt->url, GF_STREAM_VISUAL); |
1874 | |
|
1875 | 0 | if (!scene->root_od->ck && a_odm->ck) { |
1876 | 0 | scene->root_od->ck = a_odm->ck; |
1877 | 0 | } |
1878 | |
|
1879 | 0 | if (scene->vr_type) { |
1880 | 0 | n2 = gf_sg_find_node_by_name(scene->graph, szGeom); |
1881 | 0 | gf_node_changed(n2, NULL); |
1882 | 0 | } else { |
1883 | 0 | M_Transform2D *addon_tr; |
1884 | |
|
1885 | 0 | tw = INT2FIX( sw * a_odm->mo->srd_w) / (max_x - min_x); |
1886 | 0 | th = INT2FIX(sh * a_odm->mo->srd_h) / (max_y - min_y); |
1887 | |
|
1888 | 0 | n2 = gf_sg_find_node_by_name(scene->graph, szGeom); |
1889 | 0 | ((M_Rectangle *)n2)->size.x = tw; |
1890 | 0 | ((M_Rectangle *)n2)->size.y = th; |
1891 | 0 | gf_node_changed(n2, NULL); |
1892 | |
|
1893 | 0 | tx = INT2FIX(a_odm->mo->srd_x * sw) / (max_x - min_x); |
1894 | 0 | tx = tx - INT2FIX(sw) / 2 + INT2FIX(tw) / 2; |
1895 | |
|
1896 | 0 | ty = INT2FIX(a_odm->mo->srd_y * sh) / (max_y - min_y); |
1897 | 0 | ty = INT2FIX(sh) / 2 - ty - INT2FIX(th) / 2; |
1898 | |
|
1899 | 0 | addon_tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, szName); |
1900 | 0 | addon_tr->translation.x = tx; |
1901 | 0 | addon_tr->translation.y = ty; |
1902 | 0 | gf_node_changed((GF_Node *)addon_tr, NULL); |
1903 | 0 | } |
1904 | 0 | } |
1905 | 0 | } |
1906 | 0 | } else { |
1907 | 0 | mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1"); |
1908 | 0 | set_media_url(scene, &scene->visual_url, (GF_Node*)mt, &mt->url, GF_STREAM_VISUAL); |
1909 | |
|
1910 | 0 | mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, "DYN_SUBT_IMG"); |
1911 | 0 | set_media_url(scene, &scene->subs_url, (GF_Node*)mt, &mt->url, GF_STREAM_UNKNOWN); |
1912 | 0 | } |
1913 | | |
1914 | |
|
1915 | 0 | if (! scene->vr_type) { |
1916 | 0 | as = (M_AnimationStream *) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT"); |
1917 | 0 | set_media_url(scene, &scene->text_url, (GF_Node*)as, &as->url, GF_STREAM_TEXT); |
1918 | |
|
1919 | 0 | dims = (M_Inline *) gf_sg_find_node_by_name(scene->graph, "DIMS_SCENE"); |
1920 | 0 | set_media_url(scene, &scene->dims_url, (GF_Node*)dims, &dims->url, GF_STREAM_SCENE); |
1921 | 0 | } |
1922 | | |
1923 | | /*disconnect to force resize*/ |
1924 | 0 | if (!scene->root_od->parentscene) { |
1925 | 0 | gf_sc_set_scene(scene->compositor, scene->graph); |
1926 | 0 | scene->graph_attached = 1; |
1927 | |
|
1928 | 0 | evt.type = GF_EVENT_STREAMLIST; |
1929 | 0 | gf_sc_send_event(scene->compositor, &evt); |
1930 | |
|
1931 | 0 | IS_UpdateVideoPos(scene); |
1932 | 0 | } else { |
1933 | 0 | gf_scene_notify_event(scene, scene->graph_attached ? GF_EVENT_STREAMLIST : GF_EVENT_SCENE_ATTACHED, NULL, NULL, GF_OK, GF_FALSE); |
1934 | 0 | scene->graph_attached = 1; |
1935 | 0 | gf_sc_invalidate(scene->compositor, NULL); |
1936 | 0 | } |
1937 | 0 | } |
1938 | | |
1939 | | void gf_scene_toggle_addons(GF_Scene *scene, Bool show_addons) |
1940 | 0 | { |
1941 | 0 | M_Inline *dscene = (M_Inline *) gf_sg_find_node_by_name(scene->graph, "ADDON_SCENE"); |
1942 | |
|
1943 | 0 | if (show_addons) { |
1944 | 0 | GF_AssociatedContentLocation addon_info; |
1945 | 0 | memset(&addon_info, 0, sizeof(GF_AssociatedContentLocation)); |
1946 | 0 | addon_info.timeline_id = -100; |
1947 | 0 | gf_scene_register_associated_media(scene, &addon_info); |
1948 | 0 | } else { |
1949 | 0 | gf_sg_vrml_mf_reset(&dscene->url, GF_SG_VRML_MFURL); |
1950 | 0 | } |
1951 | 0 | gf_node_changed((GF_Node *)dscene, NULL); |
1952 | 0 | } |
1953 | | |
1954 | | #else |
1955 | | /*!!fixme - we would need an SVG scene in case no VRML support is present !!!*/ |
1956 | | GF_EXPORT |
1957 | | void gf_scene_regenerate(GF_Scene *scene) {} |
1958 | | GF_EXPORT |
1959 | | void gf_scene_restart_dynamic(GF_Scene *scene, s64 from_time, Bool restart_only, Bool disable_addon_check) {} |
1960 | | GF_EXPORT |
1961 | | void gf_scene_select_object(GF_Scene *scene, GF_ObjectManager *odm) {} |
1962 | | GF_EXPORT |
1963 | | void gf_scene_toggle_addons(GF_Scene *scene, Bool show_addons) { } |
1964 | | GF_EXPORT |
1965 | | void gf_scene_resume_live(GF_Scene *subscene) { } |
1966 | | GF_EXPORT |
1967 | | void gf_scene_set_addon_layout_info(GF_Scene *scene, u32 position, u32 size_factor) {} |
1968 | | GF_EXPORT |
1969 | | void gf_scene_select_main_addon(GF_Scene *scene, GF_ObjectManager *odm, Bool set_on, u64 absolute_clock_time) { } |
1970 | | |
1971 | | #endif /*GPAC_DISABLE_VRML*/ |
1972 | | |
1973 | | #ifndef GPAC_DISABLE_VRML |
1974 | | |
1975 | | static Bool check_odm_deactivate(SFURL *url, GF_ObjectManager *odm, GF_Node *n) |
1976 | 0 | { |
1977 | 0 | GF_FieldInfo info; |
1978 | 0 | MFURL *mfurl; |
1979 | 0 | if (!is_odm_url(url, odm) || !n) return 0; |
1980 | | |
1981 | 0 | gf_node_get_field_by_name(n, "url", &info); |
1982 | 0 | mfurl = (MFURL *)info.far_ptr; |
1983 | 0 | if ((url->OD_ID!=GF_MEDIA_EXTERNAL_ID) && mfurl->count && (mfurl->vals[0].OD_ID==url->OD_ID)) |
1984 | 0 | return 1; |
1985 | | |
1986 | 0 | if (url->url) gf_free(url->url); |
1987 | 0 | url->url = NULL; |
1988 | 0 | url->OD_ID = 0; |
1989 | |
|
1990 | 0 | gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL); |
1991 | 0 | gf_node_get_field_by_name(n, "stopTime", &info); |
1992 | 0 | *((SFTime *)info.far_ptr) = gf_node_get_scene_time(n); |
1993 | 0 | gf_node_changed(n, NULL); |
1994 | 0 | return 1; |
1995 | 0 | } |
1996 | | |
1997 | | static void odm_deactivate(GF_Node *n) |
1998 | 0 | { |
1999 | 0 | GF_FieldInfo info; |
2000 | |
|
2001 | 0 | gf_node_get_field_by_name(n, "url", &info); |
2002 | 0 | gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL); |
2003 | 0 | gf_node_get_field_by_name(n, "stopTime", &info); |
2004 | 0 | *((SFTime *)info.far_ptr) = gf_node_get_scene_time(n); |
2005 | 0 | gf_node_changed(n, NULL); |
2006 | 0 | } |
2007 | | |
2008 | | static void odm_activate(SFURL *url, GF_Node *n) |
2009 | 0 | { |
2010 | 0 | SFURL *sfu; |
2011 | 0 | GF_FieldInfo info; |
2012 | |
|
2013 | 0 | gf_node_get_field_by_name(n, "url", &info); |
2014 | 0 | gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFURL); |
2015 | 0 | if (url->OD_ID || url->url) { |
2016 | 0 | gf_sg_vrml_mf_append(info.far_ptr, GF_SG_VRML_MFURL, (void **) &sfu); |
2017 | 0 | sfu->OD_ID = url->OD_ID; |
2018 | 0 | if (url->url) sfu->url = gf_strdup(url->url); |
2019 | |
|
2020 | 0 | gf_node_get_field_by_name(n, "startTime", &info); |
2021 | 0 | *((SFTime *)info.far_ptr) = 0.0; |
2022 | 0 | gf_node_get_field_by_name(n, "stopTime", &info); |
2023 | 0 | *((SFTime *)info.far_ptr) = 0.0; |
2024 | 0 | } |
2025 | |
|
2026 | 0 | gf_node_changed(n, NULL); |
2027 | 0 | } |
2028 | | #endif |
2029 | | |
2030 | | GF_EXPORT |
2031 | | void gf_scene_set_service_id(GF_Scene *scene, u32 service_id) |
2032 | 0 | { |
2033 | 0 | if (!scene->is_dynamic_scene) return; |
2034 | | |
2035 | 0 | gf_sc_lock(scene->compositor, 1); |
2036 | 0 | if (scene->selected_service_id != service_id) { |
2037 | 0 | u32 i; |
2038 | 0 | GF_ObjectManager *odm, *remote_odm = NULL; |
2039 | | //delete all objects with given service ID |
2040 | 0 | i=0; |
2041 | 0 | while ((odm = gf_list_enum(scene->resources, &i))) { |
2042 | 0 | if (odm->ServiceID != scene->selected_service_id) continue; |
2043 | 0 | if (odm->redirect_url) { |
2044 | 0 | remote_odm = odm; |
2045 | 0 | gf_assert(remote_odm->scene_ns->nb_odm_users); |
2046 | 0 | remote_odm->scene_ns->nb_odm_users--; |
2047 | 0 | remote_odm->scene_ns = scene->root_od->scene_ns; |
2048 | 0 | remote_odm->scene_ns->nb_odm_users++; |
2049 | 0 | } |
2050 | | //delete all objects from this service |
2051 | 0 | else if (remote_odm) { |
2052 | 0 | if (odm->scene_ns==remote_odm->scene_ns) odm->scene_ns->owner = odm; |
2053 | 0 | gf_odm_disconnect(odm, 2); |
2054 | 0 | } |
2055 | 0 | } |
2056 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPTIME, ("[Scene] Switching %s from service %d to service %d (media time %g)\n", scene->root_od->scene_ns->url, scene->selected_service_id, service_id, (Double)scene->root_od->media_start_time/1000.0)); |
2057 | |
|
2058 | 0 | scene->selected_service_id = service_id; |
2059 | 0 | scene->audio_url.OD_ID = 0; |
2060 | 0 | scene->visual_url.OD_ID = 0; |
2061 | 0 | scene->text_url.OD_ID = 0; |
2062 | 0 | scene->dims_url.OD_ID = 0; |
2063 | 0 | scene->subs_url.OD_ID = 0; |
2064 | 0 | scene->force_size_set = 0; |
2065 | | //reset clock since we change service IDs, but request a PLAY from the current time |
2066 | 0 | if (scene->root_od->ck) { |
2067 | 0 | scene->root_od->media_start_time = gf_clock_media_time(scene->root_od->ck); |
2068 | 0 | scene->root_od->ck = NULL; |
2069 | 0 | } |
2070 | |
|
2071 | 0 | if (remote_odm) { |
2072 | 0 | i=0; |
2073 | 0 | while ((odm = gf_list_enum(scene->resources, &i))) { |
2074 | 0 | if (odm->ServiceID!=scene->selected_service_id) continue; |
2075 | 0 | if (odm->redirect_url) { |
2076 | | //gf_odm_setup_object will increment the number of odms in net service (it's supposed to |
2077 | | //be called only upon startup, but we reuse the function). Since we are already registered |
2078 | | //with the service, decrement before calling |
2079 | 0 | odm->scene_ns->nb_odm_users--; |
2080 | 0 | gf_odm_setup_object(odm, odm->scene_ns, odm->pid); |
2081 | 0 | } |
2082 | 0 | break; |
2083 | 0 | } |
2084 | 0 | } |
2085 | 0 | gf_scene_regenerate(scene); |
2086 | 0 | } |
2087 | 0 | gf_sc_lock(scene->compositor, 0); |
2088 | 0 | } |
2089 | | |
2090 | | #ifndef GPAC_DISABLE_VRML |
2091 | | |
2092 | | GF_EXPORT |
2093 | | void gf_scene_select_object(GF_Scene *scene, GF_ObjectManager *odm) |
2094 | 0 | { |
2095 | 0 | char *url; |
2096 | 0 | if (!scene->is_dynamic_scene || !scene->graph_attached || !odm) return; |
2097 | | |
2098 | 0 | if (!odm->ID) { |
2099 | 0 | if (!odm->addon) return; |
2100 | 0 | } |
2101 | | |
2102 | 0 | if (odm->ServiceID && scene->selected_service_id && (scene->selected_service_id != odm->ServiceID)) { |
2103 | 0 | gf_scene_set_service_id(scene, odm->ServiceID); |
2104 | 0 | return; |
2105 | 0 | } |
2106 | | |
2107 | | |
2108 | | //check if we are asking to (de)select one of our active media |
2109 | 0 | Bool pre_selected = GF_FALSE; |
2110 | 0 | if (check_odm_deactivate(&scene->audio_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1")) ) pre_selected=GF_TRUE; |
2111 | 0 | else if (check_odm_deactivate(&scene->visual_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1") )) pre_selected=GF_TRUE; |
2112 | 0 | else if (check_odm_deactivate(&scene->text_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_TEXT") )) pre_selected=GF_TRUE; |
2113 | 0 | else if (check_odm_deactivate(&scene->subs_url, odm, gf_sg_find_node_by_name(scene->graph, "DYN_SUBT_IMG") )) pre_selected=GF_TRUE; |
2114 | 0 | if (pre_selected) { |
2115 | 0 | if (odm->state) { |
2116 | 0 | if (odm->pid) { |
2117 | 0 | const GF_PropertyValue *p = gf_filter_pid_get_property(odm->pid, GF_PROP_PID_FORCED_SUB); |
2118 | 0 | if (p && p->value.uint) { |
2119 | 0 | if (p->value.uint==2) return; |
2120 | | //toggle forced on/off |
2121 | 0 | GF_FilterEvent evt; |
2122 | 0 | GF_FEVT_INIT(evt, GF_FEVT_QUALITY_SWITCH, odm->pid); |
2123 | 0 | if (odm->flags & GF_ODM_SUB_FORCED) { |
2124 | 0 | evt.quality_switch.up = 0; |
2125 | 0 | odm->flags &= ~GF_ODM_SUB_FORCED; |
2126 | 0 | } else { |
2127 | 0 | evt.quality_switch.up = 1; |
2128 | 0 | odm->flags |= GF_ODM_SUB_FORCED; |
2129 | 0 | } |
2130 | 0 | gf_filter_pid_send_event(odm->pid, &evt); |
2131 | 0 | return; |
2132 | 0 | } |
2133 | 0 | } |
2134 | 0 | gf_odm_stop(odm, GF_FALSE); |
2135 | 0 | } else { |
2136 | 0 | gf_odm_play(odm); |
2137 | 0 | } |
2138 | 0 | return; |
2139 | 0 | } |
2140 | | |
2141 | | |
2142 | 0 | if (!odm->ID && odm->subscene) { |
2143 | 0 | M_Inline *dscene = (M_Inline *) gf_sg_find_node_by_name(scene->graph, "ADDON_SCENE"); |
2144 | |
|
2145 | 0 | if (!dscene) |
2146 | 0 | return; |
2147 | | |
2148 | 0 | if (odm->addon && odm->addon->addon_type==GF_ADDON_TYPE_MAIN) { |
2149 | 0 | return; |
2150 | 0 | } |
2151 | | |
2152 | 0 | gf_sg_vrml_field_copy(&dscene->url, &odm->mo->URLs, GF_SG_VRML_MFURL); |
2153 | 0 | gf_node_changed((GF_Node *)dscene, NULL); |
2154 | | //do not update video pos for addons, this is done only when setting up the main video object |
2155 | 0 | return; |
2156 | 0 | } |
2157 | | |
2158 | | //if same service jump at current media time |
2159 | 0 | if (scene->selected_service_id == odm->ServiceID) |
2160 | 0 | scene->root_od->media_start_time = gf_clock_media_time(odm->ck); |
2161 | 0 | else |
2162 | 0 | scene->root_od->media_start_time = 0; |
2163 | 0 | scene->selected_service_id = odm->ServiceID; |
2164 | |
|
2165 | 0 | if (odm->type == GF_STREAM_AUDIO) { |
2166 | 0 | M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1"); |
2167 | 0 | if (!ac) return; |
2168 | 0 | if (scene->audio_url.url) gf_free(scene->audio_url.url); |
2169 | 0 | scene->audio_url.url = NULL; |
2170 | 0 | scene->audio_url.OD_ID = odm->ID; |
2171 | 0 | if (!ac->url.count) gf_sg_vrml_mf_alloc(&ac->url, GF_SG_VRML_MFURL, 1); |
2172 | 0 | ac->url.vals[0].OD_ID = odm->ID; |
2173 | 0 | if (ac->url.vals[0].url) { |
2174 | 0 | gf_free(ac->url.vals[0].url); |
2175 | 0 | ac->url.vals[0].url = NULL; |
2176 | 0 | } |
2177 | 0 | url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; |
2178 | 0 | if (url) { |
2179 | 0 | scene->audio_url.url = gf_strdup(url); |
2180 | 0 | ac->url.vals[0].url = gf_strdup(url); |
2181 | 0 | } |
2182 | 0 | ac->startTime = gf_scene_get_time(scene); |
2183 | 0 | gf_node_changed((GF_Node *)ac, NULL); |
2184 | 0 | return; |
2185 | 0 | } |
2186 | | |
2187 | 0 | if (odm->type == GF_STREAM_VISUAL) { |
2188 | 0 | M_MovieTexture *mt = (M_MovieTexture*) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1"); |
2189 | 0 | if (!mt) return; |
2190 | 0 | if (scene->visual_url.url) gf_free(scene->visual_url.url); |
2191 | 0 | scene->visual_url.url = NULL; |
2192 | 0 | scene->visual_url.OD_ID = odm->ID; |
2193 | 0 | if (!mt->url.count) gf_sg_vrml_mf_alloc(&mt->url, GF_SG_VRML_MFURL, 1); |
2194 | 0 | mt->url.vals[0].OD_ID = odm->ID; |
2195 | 0 | if (mt->url.vals[0].url) gf_free(mt->url.vals[0].url); |
2196 | 0 | url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; |
2197 | 0 | if (url) { |
2198 | 0 | scene->visual_url.url = gf_strdup(url); |
2199 | 0 | mt->url.vals[0].url = gf_strdup(url); |
2200 | 0 | } |
2201 | 0 | mt->startTime = gf_scene_get_time(scene); |
2202 | 0 | if (odm->mo) gf_scene_force_size_to_video(scene, odm->mo); |
2203 | 0 | gf_node_changed((GF_Node *)mt, NULL); |
2204 | 0 | return; |
2205 | 0 | } |
2206 | | |
2207 | | |
2208 | 0 | if (odm->type == GF_STREAM_TEXT) { |
2209 | 0 | M_AnimationStream *as = (M_AnimationStream*) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT"); |
2210 | 0 | if (!as) return; |
2211 | 0 | if (scene->text_url.url) gf_free(scene->text_url.url); |
2212 | 0 | scene->text_url.url = NULL; |
2213 | 0 | scene->text_url.OD_ID = odm->ID; |
2214 | 0 | if (!as->url.count) gf_sg_vrml_mf_alloc(&as->url, GF_SG_VRML_MFURL, 1); |
2215 | 0 | as->url.vals[0].OD_ID = odm->ID; |
2216 | 0 | if (as->url.vals[0].url) gf_free(as->url.vals[0].url); |
2217 | 0 | url = odm->mo->URLs.count ? odm->mo->URLs.vals[0].url : NULL; |
2218 | 0 | if (url) { |
2219 | 0 | scene->text_url.url = gf_strdup(url); |
2220 | 0 | as->url.vals[0].url = gf_strdup(url); |
2221 | 0 | } |
2222 | 0 | as->startTime = gf_scene_get_time(scene); |
2223 | 0 | gf_node_changed((GF_Node *)as, NULL); |
2224 | 0 | return; |
2225 | 0 | } |
2226 | 0 | } |
2227 | | |
2228 | | void gf_scene_select_main_addon(GF_Scene *scene, GF_ObjectManager *odm, Bool set_on, u64 absolute_clock_time) |
2229 | 0 | { |
2230 | 0 | GF_DOM_Event devt; |
2231 | 0 | M_Inline *dscene = (M_Inline *) gf_sg_find_node_by_name(scene->graph, scene->compositor->dbgpvr ? "ADDON_SCENE" : "PVR_SCENE"); |
2232 | |
|
2233 | 0 | if (scene->main_addon_selected==set_on) return; |
2234 | 0 | scene->main_addon_selected = set_on; |
2235 | |
|
2236 | 0 | if (set_on) { |
2237 | 0 | odm_deactivate(gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1")); |
2238 | 0 | odm_deactivate(gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1")); |
2239 | 0 | odm_deactivate(gf_sg_find_node_by_name(scene->graph, "DYN_TEXT")); |
2240 | | |
2241 | |
|
2242 | 0 | if (!odm->subscene->graph_attached) { |
2243 | 0 | odm->flags &= ~GF_ODM_REGENERATE_SCENE; |
2244 | 0 | gf_scene_regenerate(odm->subscene); |
2245 | 0 | } else { |
2246 | 0 | odm->subscene->needs_restart = 1; |
2247 | 0 | } |
2248 | | |
2249 | | //main addon is vod not live, store clock |
2250 | 0 | if (! odm->timeshift_depth && !scene->sys_clock_at_main_activation) { |
2251 | 0 | scene->sys_clock_at_main_activation = gf_sys_clock(); |
2252 | 0 | scene->obj_clock_at_main_activation = absolute_clock_time; |
2253 | 0 | } |
2254 | | |
2255 | |
|
2256 | 0 | gf_sg_vrml_field_copy(&dscene->url, &odm->mo->URLs, GF_SG_VRML_MFURL); |
2257 | 0 | gf_node_changed((GF_Node *)dscene, NULL); |
2258 | 0 | } else { |
2259 | 0 | GF_Clock *ck = scene->root_od->ck; |
2260 | | //reactivating the main content will trigger a reset on the clock - remember where we are and resume from this point |
2261 | 0 | scene->root_od->media_start_time = gf_clock_media_time(ck); |
2262 | |
|
2263 | 0 | scene->sys_clock_at_main_activation = 0; |
2264 | 0 | scene->obj_clock_at_main_activation = 0; |
2265 | |
|
2266 | 0 | odm_activate(&scene->audio_url, gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1")); |
2267 | 0 | odm_activate(&scene->visual_url, gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1")); |
2268 | 0 | odm_activate(&scene->text_url, gf_sg_find_node_by_name(scene->graph, "DYN_TEXT")); |
2269 | 0 | odm_activate(&scene->subs_url, gf_sg_find_node_by_name(scene->graph, "DYN_SUBT_IMG")); |
2270 | |
|
2271 | 0 | gf_sg_vrml_mf_reset(&dscene->url, GF_SG_VRML_MFURL); |
2272 | 0 | gf_node_changed((GF_Node *)dscene, NULL); |
2273 | 0 | } |
2274 | |
|
2275 | 0 | memset(&devt, 0, sizeof(GF_DOM_Event)); |
2276 | 0 | devt.type = GF_EVENT_MAIN_ADDON_STATE; |
2277 | 0 | devt.detail = set_on; |
2278 | 0 | gf_scene_notify_event(scene, GF_EVENT_MAIN_ADDON_STATE, NULL, &devt, GF_OK, GF_FALSE); |
2279 | |
|
2280 | 0 | } |
2281 | | |
2282 | | GF_EXPORT |
2283 | | void gf_scene_set_addon_layout_info(GF_Scene *scene, u32 position, u32 size_factor) |
2284 | 0 | { |
2285 | 0 | MFURL url; |
2286 | 0 | M_Transform2D *tr; |
2287 | 0 | M_Layer2D *layer; |
2288 | 0 | GF_MediaObject *mo; |
2289 | 0 | s32 w, h, v_w, v_h; |
2290 | 0 | if (!scene->visual_url.OD_ID && !scene->visual_url.url) return; |
2291 | | |
2292 | 0 | url.count = 1; |
2293 | 0 | url.vals = &scene->visual_url; |
2294 | 0 | mo = IS_CheckExistingObject(scene, &url, GF_MEDIA_OBJECT_VIDEO); |
2295 | 0 | if (!mo) return; |
2296 | | |
2297 | 0 | scene->addon_position = position; |
2298 | 0 | scene->addon_size_factor = size_factor; |
2299 | |
|
2300 | 0 | gf_scene_get_video_size(mo, (u32 *) &v_w, (u32 *) &v_h); |
2301 | 0 | w = v_w; |
2302 | 0 | h = v_h; |
2303 | 0 | switch (size_factor) { |
2304 | 0 | case 0: |
2305 | 0 | v_w /= 2; |
2306 | 0 | v_h /= 2; |
2307 | 0 | break; |
2308 | 0 | case 1: |
2309 | 0 | v_w /= 3; |
2310 | 0 | v_h /= 3; |
2311 | 0 | break; |
2312 | 0 | case 2: |
2313 | 0 | default: |
2314 | 0 | v_w /= 4; |
2315 | 0 | v_h /= 4; |
2316 | 0 | break; |
2317 | 0 | } |
2318 | | |
2319 | 0 | layer = (M_Layer2D *) gf_sg_find_node_by_name(scene->graph, "ADDON_LAYER"); |
2320 | 0 | if (!layer) return; |
2321 | 0 | layer->size.x = INT2FIX(v_w); |
2322 | 0 | layer->size.y = INT2FIX(v_h); |
2323 | 0 | gf_node_dirty_set((GF_Node *)layer, 0, 0); |
2324 | |
|
2325 | 0 | tr = (M_Transform2D *) gf_sg_find_node_by_name(scene->graph, "ADDON_TRANS"); |
2326 | 0 | if (!tr) return; |
2327 | 0 | switch (position) { |
2328 | 0 | case 0: |
2329 | 0 | tr->translation.x = INT2FIX(w - v_w) / 2; |
2330 | 0 | tr->translation.y = INT2FIX(v_h - h) / 2; |
2331 | 0 | break; |
2332 | 0 | case 1: |
2333 | 0 | tr->translation.x = INT2FIX(w - v_w) / 2; |
2334 | 0 | tr->translation.y = INT2FIX(h - v_h) / 2; |
2335 | 0 | break; |
2336 | 0 | case 2: |
2337 | 0 | tr->translation.x = INT2FIX(v_w - w) / 2; |
2338 | 0 | tr->translation.y = INT2FIX(v_h - h) / 2; |
2339 | 0 | break; |
2340 | 0 | case 3: |
2341 | 0 | tr->translation.x = INT2FIX(v_w - w) / 2; |
2342 | 0 | tr->translation.y = INT2FIX(h - v_h) / 2; |
2343 | 0 | break; |
2344 | 0 | } |
2345 | 0 | gf_node_dirty_set((GF_Node *)tr, 0, 0); |
2346 | 0 | } |
2347 | | |
2348 | | GF_EXPORT |
2349 | | void gf_scene_resume_live(GF_Scene *subscene) |
2350 | 0 | { |
2351 | 0 | if (subscene->main_addon_selected) |
2352 | 0 | mediacontrol_resume(subscene->root_od, 1); |
2353 | 0 | } |
2354 | | |
2355 | | void gf_scene_restart_dynamic(GF_Scene *scene, s64 from_time, Bool restart_only, Bool disable_addon_check) |
2356 | 0 | { |
2357 | 0 | u32 i; |
2358 | 0 | GF_Clock *ck; |
2359 | 0 | GF_List *to_restart; |
2360 | 0 | GF_ObjectManager *odm; |
2361 | 0 | if (restart_only) { |
2362 | 0 | from_time = 0; |
2363 | 0 | } |
2364 | |
|
2365 | 0 | ck = scene->root_od->ck; |
2366 | 0 | if (!scene->is_dynamic_scene) ck = scene->root_od->ck; |
2367 | 0 | if (!ck) return; |
2368 | | |
2369 | | //first pass to check if we need to enable the addon acting as time shifting |
2370 | 0 | if (!disable_addon_check) { |
2371 | 0 | i=0; |
2372 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
2373 | |
|
2374 | 0 | if (odm->addon && (odm->addon->addon_type==GF_ADDON_TYPE_MAIN)) { |
2375 | | //assign clock if not yet available |
2376 | 0 | if (odm->addon->root_od->subscene && !odm->addon->root_od->ck) |
2377 | 0 | odm->addon->root_od->ck = scene->root_od->ck; |
2378 | | |
2379 | | //we're timeshifting through the main addon, activate it |
2380 | 0 | if (from_time < -1) { |
2381 | 0 | gf_scene_select_main_addon(scene, odm, GF_TRUE, gf_clock_time_absolute(ck) ); |
2382 | | |
2383 | | /*no timeshift, this is a VoD associated with the live broadcast: get current time*/ |
2384 | 0 | if (! odm->timeshift_depth) { |
2385 | 0 | s64 live_clock = scene->obj_clock_at_main_activation + gf_sys_clock() - scene->sys_clock_at_main_activation; |
2386 | |
|
2387 | 0 | from_time += 1; |
2388 | 0 | if (live_clock + from_time < 0) from_time = 0; |
2389 | 0 | else from_time = live_clock + from_time; |
2390 | 0 | } |
2391 | 0 | } else if (scene->main_addon_selected) { |
2392 | 0 | gf_scene_select_main_addon(scene, odm, GF_FALSE, 0); |
2393 | 0 | } |
2394 | 0 | } |
2395 | 0 | } |
2396 | 0 | } |
2397 | |
|
2398 | 0 | to_restart = gf_list_new(); |
2399 | 0 | i=0; |
2400 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
2401 | | //do not restart not selected objects |
2402 | 0 | if (odm->state != GF_ODM_STATE_PLAY) |
2403 | 0 | continue; |
2404 | | |
2405 | 0 | if (gf_odm_shares_clock(odm, ck)) { |
2406 | | //object is not an addon and main addon is selected, do not add |
2407 | 0 | if (!odm->addon && scene->main_addon_selected) { |
2408 | 0 | } |
2409 | | //object is an addon and enabled, restart if main and main is enabled, or if not main |
2410 | 0 | else if (odm->addon && odm->addon->enabled) { |
2411 | 0 | if (odm->addon->addon_type==GF_ADDON_TYPE_MAIN) { |
2412 | 0 | if (scene->main_addon_selected) { |
2413 | 0 | gf_list_add(to_restart, odm); |
2414 | 0 | } |
2415 | 0 | } else { |
2416 | 0 | gf_list_add(to_restart, odm); |
2417 | 0 | } |
2418 | 0 | } else if (!scene->selected_service_id || (scene->selected_service_id==odm->ServiceID) ) { |
2419 | 0 | gf_odm_stop(odm, 1); |
2420 | 0 | gf_list_add(to_restart, odm); |
2421 | 0 | } |
2422 | 0 | } |
2423 | 0 | } |
2424 | |
|
2425 | 0 | if (!restart_only) { |
2426 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPTIME, ("[Scene] Restarting from "LLD"\n", from_time)); |
2427 | | /*reset clock*/ |
2428 | 0 | gf_clock_reset(ck); |
2429 | | |
2430 | | //used by SVG for JSAPIs..; |
2431 | 0 | if (!scene->is_dynamic_scene) gf_clock_set_time(ck, 0, 1000); |
2432 | 0 | } else { |
2433 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPTIME, ("[Scene] Restarting scene from current clock %d\n", gf_clock_time(ck) )); |
2434 | 0 | } |
2435 | | |
2436 | | //in case we sent a stop on objects directly (to stop scene), collect stop objects bound to scene |
2437 | 0 | if (!scene->compositor->player && !gf_list_count(to_restart)) { |
2438 | 0 | i=0; |
2439 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { |
2440 | 0 | if ((odm->state == GF_ODM_STATE_STOP) && odm->mo && odm->mo->num_open && gf_odm_shares_clock(odm, ck)) { |
2441 | 0 | gf_list_add(to_restart, odm); |
2442 | 0 | } |
2443 | 0 | } |
2444 | 0 | } |
2445 | | |
2446 | | /*restart objects*/ |
2447 | 0 | i=0; |
2448 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) { |
2449 | 0 | if (from_time<0) { |
2450 | 0 | odm->media_stop_time = from_time + 1; |
2451 | 0 | } else { |
2452 | 0 | odm->media_start_time = from_time; |
2453 | 0 | } |
2454 | |
|
2455 | 0 | if (odm->subscene && odm->subscene->is_dynamic_scene) { |
2456 | 0 | gf_scene_restart_dynamic(odm->subscene, from_time, 0, 0); |
2457 | 0 | } else { |
2458 | 0 | gf_odm_start(odm); |
2459 | 0 | } |
2460 | 0 | } |
2461 | 0 | gf_list_del(to_restart); |
2462 | | |
2463 | | /*also check nodes since they may be deactivated (end of stream)*/ |
2464 | 0 | if (scene->is_dynamic_scene) { |
2465 | 0 | M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO1"); |
2466 | 0 | M_MovieTexture *mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO1"); |
2467 | 0 | M_AnimationStream *as = (M_AnimationStream *) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT"); |
2468 | 0 | if (ac) { |
2469 | 0 | ac->startTime = gf_scene_get_time(scene); |
2470 | 0 | gf_node_changed((GF_Node *)ac, NULL); |
2471 | 0 | } |
2472 | 0 | if (mt) { |
2473 | 0 | mt->startTime = gf_scene_get_time(scene); |
2474 | 0 | gf_node_changed((GF_Node *)mt, NULL); |
2475 | 0 | } |
2476 | 0 | if (as) { |
2477 | 0 | as->startTime = gf_scene_get_time(scene); |
2478 | 0 | gf_node_changed((GF_Node *)as, NULL); |
2479 | 0 | } |
2480 | 0 | } |
2481 | 0 | } |
2482 | | |
2483 | | #endif /*GPAC_DISABLE_VRML*/ |
2484 | | |
2485 | | |
2486 | | GF_EXPORT |
2487 | | void gf_scene_force_size(GF_Scene *scene, u32 width, u32 height) |
2488 | 0 | { |
2489 | 0 | Bool skip_notif = GF_FALSE; |
2490 | | |
2491 | | /*for now only allowed when no scene info*/ |
2492 | 0 | if (!scene->is_dynamic_scene) return; |
2493 | | |
2494 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Scene] Forcing scene size to %d x %d\n", width, height)); |
2495 | |
|
2496 | 0 | if (scene->vr_type) { |
2497 | | /*for 360 don't set scene size to full-size , only half of it*/ |
2498 | 0 | width /= 2; |
2499 | 0 | height /= 2; |
2500 | | /*if we already processed a force size in 360, don't do it again*/ |
2501 | 0 | if (scene->force_size_set) |
2502 | 0 | return; |
2503 | | |
2504 | 0 | #ifndef GPAC_DISABLE_VRML |
2505 | 0 | scene->force_size_set = GF_TRUE; |
2506 | 0 | if (! scene->srd_type) { |
2507 | 0 | GF_Node *node = gf_sg_find_node_by_name(scene->graph, "DYN_GEOM1"); |
2508 | 0 | if (node && (((M_Sphere *)node)->radius == FIX_ONE)) { |
2509 | 0 | u32 radius = MAX(width, height)/4; |
2510 | |
|
2511 | 0 | ((M_Sphere *)node)->radius = - INT2FIX(radius); |
2512 | 0 | gf_node_changed(node, NULL); |
2513 | 0 | } |
2514 | 0 | } |
2515 | 0 | #endif /* GPAC_DISABLE_VRML */ |
2516 | 0 | } |
2517 | | |
2518 | 0 | if (scene->is_dynamic_scene) { |
2519 | 0 | u32 serv_w=0, serv_h=0; |
2520 | 0 | GF_FilterPid *pid = gf_filter_get_ipid(scene->compositor->filter, 0); |
2521 | 0 | const GF_PropertyValue *prop; |
2522 | 0 | prop = gf_filter_pid_get_property(pid, GF_PROP_SERVICE_WIDTH); |
2523 | 0 | if (prop) serv_w = prop->value.uint; |
2524 | 0 | prop = gf_filter_pid_get_property(pid, GF_PROP_SERVICE_HEIGHT); |
2525 | 0 | if (prop) serv_h = prop->value.uint; |
2526 | | |
2527 | |
|
2528 | 0 | if (!scene->root_od->parentscene) { |
2529 | 0 | if (serv_w && serv_h) { |
2530 | 0 | gf_sc_set_scene_size(scene->compositor, width, height, 1); |
2531 | 0 | if (!scene->force_size_set) { |
2532 | 0 | gf_sc_set_size(scene->compositor, serv_w, serv_h); |
2533 | 0 | scene->force_size_set = 1; |
2534 | 0 | } else { |
2535 | 0 | gf_sc_set_size(scene->compositor, 0, 0); |
2536 | 0 | } |
2537 | 0 | } else { |
2538 | 0 | if (scene->vr_type) { |
2539 | 0 | width = MAX(width, height) / 2; |
2540 | 0 | gf_sg_set_scene_size_info(scene->graph, 0, 0, 1); |
2541 | 0 | } else { |
2542 | | /*need output resize*/ |
2543 | 0 | gf_sg_set_scene_size_info(scene->graph, width, height, 1); |
2544 | 0 | gf_sc_set_scene(scene->compositor, scene->graph); |
2545 | 0 | gf_sc_set_size(scene->compositor, width, height); |
2546 | 0 | } |
2547 | 0 | } |
2548 | |
|
2549 | 0 | } else if (!scene->force_size_set) { |
2550 | 0 | if (serv_w && serv_h) { |
2551 | 0 | width = serv_w; |
2552 | 0 | height = serv_h; |
2553 | 0 | } |
2554 | 0 | if (scene->vr_type) { |
2555 | 0 | gf_sg_set_scene_size_info(scene->graph, 0, 0, 1); |
2556 | 0 | } else { |
2557 | 0 | gf_sg_set_scene_size_info(scene->graph, width, height, 1); |
2558 | 0 | } |
2559 | 0 | scene->force_size_set = 1; |
2560 | 0 | } else { |
2561 | 0 | u32 w, h; |
2562 | 0 | gf_sg_get_scene_size_info(scene->graph, &w, &h); |
2563 | 0 | if (!serv_w && !serv_h && ((w<width) || (h<height)) ) { |
2564 | 0 | gf_sg_set_scene_size_info(scene->graph, width, height, 1); |
2565 | 0 | } else { |
2566 | 0 | GF_DOM_Event devt; |
2567 | 0 | memset(&devt, 0, sizeof(GF_DOM_Event)); |
2568 | 0 | devt.type = GF_EVENT_SCENE_SIZE; |
2569 | 0 | devt.screen_rect.width = INT2FIX(width); |
2570 | 0 | devt.screen_rect.height = INT2FIX(height); |
2571 | |
|
2572 | 0 | devt.key_flags = scene->is_dynamic_scene ? (scene->vr_type ? 2 : 1) : 0; |
2573 | |
|
2574 | 0 | gf_scene_notify_event(scene, GF_EVENT_SCENE_SIZE, NULL, &devt, GF_OK, GF_FALSE); |
2575 | |
|
2576 | 0 | skip_notif = GF_TRUE; |
2577 | |
|
2578 | 0 | width = w; |
2579 | 0 | height = h; |
2580 | 0 | } |
2581 | 0 | } |
2582 | 0 | } |
2583 | 0 | else if (scene->root_od->parentscene && scene->root_od->parentscene->is_dynamic_scene) { |
2584 | 0 | gf_sg_set_scene_size_info(scene->root_od->parentscene->graph, width, height, gf_sg_use_pixel_metrics(scene->root_od->parentscene->graph)); |
2585 | 0 | if (!scene->root_od->parentscene) { |
2586 | 0 | if (width && height) { |
2587 | 0 | gf_sc_set_scene_size(scene->compositor, width, height, GF_TRUE); |
2588 | 0 | gf_sc_set_size(scene->compositor, width, height); |
2589 | 0 | } |
2590 | 0 | } |
2591 | 0 | } |
2592 | |
|
2593 | 0 | if (scene->vr_type) { |
2594 | 0 | gf_sg_set_scene_size_info(scene->graph, 0, 0, GF_TRUE); |
2595 | 0 | } else { |
2596 | 0 | gf_sg_set_scene_size_info(scene->graph, width, height, GF_TRUE); |
2597 | 0 | } |
2598 | 0 | if (scene->srd_type) |
2599 | 0 | gf_scene_regenerate(scene); |
2600 | |
|
2601 | 0 | #ifndef GPAC_DISABLE_VRML |
2602 | 0 | IS_UpdateVideoPos(scene); |
2603 | 0 | #endif |
2604 | |
|
2605 | 0 | if (skip_notif) return; |
2606 | | |
2607 | 0 | gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, GF_OK, GF_FALSE); |
2608 | 0 | } |
2609 | | |
2610 | | |
2611 | | GF_EXPORT |
2612 | | Bool gf_scene_process_anchor(GF_Node *caller, GF_Event *evt) |
2613 | 0 | { |
2614 | 0 | #ifndef GPAC_DISABLE_VRML |
2615 | 0 | u32 i; |
2616 | 0 | M_Inline *inl; |
2617 | 0 | #endif |
2618 | 0 | GF_Scene *scene; |
2619 | 0 | GF_SceneGraph *sg = gf_node_get_graph(caller); |
2620 | 0 | if (!sg) return 1; |
2621 | 0 | scene = (GF_Scene *)gf_sg_get_private(sg); |
2622 | 0 | if (!scene) return 1; |
2623 | | |
2624 | | /*if main scene forward to user. If no params or first one not "self" forward to user*/ |
2625 | 0 | if (! scene->root_od->parentscene || !evt->navigate.parameters || !evt->navigate.param_count || (stricmp(evt->navigate.parameters[0], "self") && stricmp(evt->navigate.parameters[0], "_self"))) { |
2626 | |
|
2627 | 0 | return gf_filter_ui_event(scene->compositor->filter, evt); |
2628 | 0 | } |
2629 | | |
2630 | 0 | if (!scene->root_od->mo) return 1; |
2631 | | |
2632 | | /*FIXME this is too restrictive, we assume the navigate URL is really a presentation one...*/ |
2633 | 0 | #ifndef GPAC_DISABLE_VRML |
2634 | 0 | i=0; |
2635 | 0 | while ((inl = (M_Inline*)gf_mo_event_target_enum_node(scene->root_od->mo, &i))) { |
2636 | 0 | switch (gf_node_get_tag((GF_Node *)inl)) { |
2637 | 0 | case TAG_MPEG4_Inline: |
2638 | 0 | #ifndef GPAC_DISABLE_X3D |
2639 | 0 | case TAG_X3D_Inline: |
2640 | 0 | #endif |
2641 | 0 | gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL); |
2642 | 0 | gf_sg_vrml_mf_alloc(&inl->url, GF_SG_VRML_MFURL, 1); |
2643 | 0 | inl->url.vals[0].url = gf_strdup(evt->navigate.to_url ? evt->navigate.to_url : ""); |
2644 | | /*signal URL change but don't destroy inline scene now since we got this event from inside the scene, |
2645 | | this could crash compositors*/ |
2646 | 0 | scene->needs_restart = 2; |
2647 | 0 | break; |
2648 | 0 | } |
2649 | 0 | } |
2650 | 0 | #endif |
2651 | | |
2652 | 0 | return 1; |
2653 | 0 | } |
2654 | | |
2655 | | GF_EXPORT |
2656 | | GF_Compositor *gf_sc_get_compositor(GF_Node *node) |
2657 | 0 | { |
2658 | 0 | GF_Scene *scene; |
2659 | 0 | GF_SceneGraph *sg = gf_node_get_graph(node); |
2660 | 0 | if (!sg) return NULL; |
2661 | 0 | scene = (GF_Scene *)gf_sg_get_private(sg); |
2662 | 0 | if (!scene) return NULL; |
2663 | 0 | return scene->compositor; |
2664 | 0 | } |
2665 | | |
2666 | | const char *gf_scene_get_fragment_uri(GF_Node *node) |
2667 | 0 | { |
2668 | 0 | GF_SceneGraph *sg = gf_node_get_graph(node); |
2669 | 0 | GF_Scene *scene = sg ? (GF_Scene *) gf_sg_get_private(sg) : NULL; |
2670 | 0 | if (!scene) return NULL; |
2671 | 0 | return scene->fragment_uri; |
2672 | 0 | } |
2673 | | void gf_scene_set_fragment_uri(GF_Node *node, const char *uri) |
2674 | 0 | { |
2675 | 0 | GF_SceneGraph *sg = gf_node_get_graph(node); |
2676 | 0 | GF_Scene *scene = sg ? (GF_Scene *) gf_sg_get_private(sg) : NULL; |
2677 | 0 | if (!scene) return; |
2678 | 0 | if (scene->fragment_uri) { |
2679 | 0 | gf_free(scene->fragment_uri); |
2680 | 0 | scene->fragment_uri = NULL; |
2681 | 0 | } |
2682 | 0 | if (uri) scene->fragment_uri = gf_strdup(uri); |
2683 | 0 | } |
2684 | | |
2685 | | |
2686 | | GF_Node *gf_scene_get_subscene_root(GF_Node *node) |
2687 | 0 | { |
2688 | 0 | GF_Scene *scene; |
2689 | 0 | if (!node) return NULL; |
2690 | 0 | switch (gf_node_get_tag(node)) { |
2691 | 0 | #ifndef GPAC_DISABLE_VRML |
2692 | 0 | case TAG_MPEG4_Inline: |
2693 | 0 | #ifndef GPAC_DISABLE_X3D |
2694 | 0 | case TAG_X3D_Inline: |
2695 | 0 | #endif |
2696 | 0 | break; |
2697 | 0 | #endif |
2698 | 0 | default: |
2699 | 0 | return NULL; |
2700 | 0 | } |
2701 | 0 | scene = (GF_Scene *)gf_node_get_private(node); |
2702 | 0 | if (!scene) return NULL; |
2703 | 0 | if (!scene->graph) return NULL; |
2704 | 0 | return gf_sg_get_root_node(scene->graph); |
2705 | 0 | } |
2706 | | |
2707 | | /*returns 0 if any of the clock still hasn't seen EOS*/ |
2708 | | Bool gf_scene_check_clocks(GF_SceneNamespace *ns, GF_Scene *scene, Bool check_buffering) |
2709 | 0 | { |
2710 | 0 | GF_Clock *ck; |
2711 | 0 | Bool initialized = GF_FALSE; |
2712 | 0 | u32 i; |
2713 | |
|
2714 | 0 | if (scene) { |
2715 | 0 | GF_ObjectManager *odm; |
2716 | 0 | if (scene->root_od->scene_ns != ns) { |
2717 | 0 | if (!gf_scene_check_clocks(scene->root_od->scene_ns, scene, check_buffering)) return 0; |
2718 | 0 | } |
2719 | 0 | i=0; |
2720 | 0 | while ( (odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i)) ) { |
2721 | 0 | switch (odm->type) { |
2722 | 0 | case GF_STREAM_OCR: |
2723 | 0 | case GF_STREAM_INTERACT: |
2724 | 0 | continue; |
2725 | 0 | default: |
2726 | 0 | break; |
2727 | 0 | } |
2728 | | |
2729 | 0 | if (odm->scene_ns && (odm->scene_ns != ns)) { |
2730 | 0 | if (!gf_scene_check_clocks(odm->scene_ns, NULL, check_buffering)) return 0; |
2731 | 0 | } else if (odm->ck) { |
2732 | 0 | initialized = GF_TRUE; |
2733 | 0 | if (!check_buffering) { |
2734 | 0 | if (! odm->has_seen_eos && (odm->state != GF_ODM_STATE_STOP) ) { |
2735 | 0 | return 0; |
2736 | 0 | } |
2737 | 0 | } else { |
2738 | 0 | if (odm->ck->nb_buffering) { |
2739 | 0 | return 0; |
2740 | 0 | } |
2741 | 0 | } |
2742 | 0 | } |
2743 | 0 | } |
2744 | 0 | } |
2745 | | |
2746 | 0 | i=0; |
2747 | 0 | while (ns->clocks && (ck = (GF_Clock *)gf_list_enum(ns->clocks, &i) ) ) { |
2748 | 0 | initialized = GF_TRUE; |
2749 | 0 | if (!check_buffering) { |
2750 | 0 | Bool is_eos = ck->has_seen_eos; |
2751 | | //object not active consider it done |
2752 | 0 | if (!is_eos && ns->owner->mo && !ns->owner->mo->num_open) |
2753 | 0 | is_eos = GF_TRUE; |
2754 | |
|
2755 | 0 | if (!is_eos) return 0; |
2756 | 0 | } else { |
2757 | 0 | if (ck->nb_buffering) return 0; |
2758 | 0 | } |
2759 | |
|
2760 | 0 | } |
2761 | | |
2762 | 0 | if (!check_buffering && scene) { |
2763 | 0 | if (scene->root_od->pid_id) { |
2764 | 0 | initialized = GF_TRUE; |
2765 | 0 | if (scene->root_od->parentscene && (scene->root_od->state != GF_ODM_STATE_STOP)) return 0; |
2766 | 0 | } |
2767 | 0 | } |
2768 | 0 | if (!initialized) return 0; |
2769 | | |
2770 | 0 | return 1; |
2771 | 0 | } |
2772 | | |
2773 | | const char *gf_scene_get_service_url(GF_SceneGraph *sg) |
2774 | 0 | { |
2775 | 0 | GF_Scene *scene = gf_sg_get_private(sg); |
2776 | 0 | if (scene) return scene->root_od->scene_ns->url; |
2777 | 0 | return NULL; |
2778 | 0 | } |
2779 | | |
2780 | | Bool gf_scene_is_over(GF_SceneGraph *sg) |
2781 | 0 | { |
2782 | 0 | u32 i, count; |
2783 | 0 | GF_Scene *scene = gf_sg_get_private(sg); |
2784 | 0 | if (!scene) return GF_FALSE; |
2785 | 0 | if (scene->root_od->has_seen_eos) |
2786 | 0 | return scene->root_od->ck->has_seen_eos; |
2787 | | |
2788 | 0 | count = gf_list_count(scene->resources); |
2789 | 0 | for (i=0; i<count; i++) { |
2790 | 0 | GF_ObjectManager *odm = gf_list_get(scene->resources, i); |
2791 | 0 | if (!odm->has_seen_eos || !odm->ck->has_seen_eos) return GF_FALSE; |
2792 | 0 | if (odm->subscene && !gf_scene_is_over(odm->subscene->graph) ) return GF_FALSE; |
2793 | 0 | } |
2794 | 0 | return GF_TRUE; |
2795 | 0 | } |
2796 | | |
2797 | | GF_SceneGraph *gf_scene_enum_extra_scene(GF_SceneGraph *sg, u32 *i) |
2798 | 0 | { |
2799 | 0 | GF_Scene *scene = gf_sg_get_private(sg); |
2800 | 0 | if (!scene) return NULL; |
2801 | 0 | return gf_list_enum(scene->extra_scenes, i); |
2802 | 0 | } |
2803 | | |
2804 | | Bool gf_scene_is_dynamic_scene(GF_SceneGraph *sg) |
2805 | 0 | { |
2806 | 0 | GF_Scene *scene = gf_sg_get_private(sg); |
2807 | 0 | if (!scene) return 0; |
2808 | 0 | return scene->is_dynamic_scene ? 1 : 0; |
2809 | 0 | } |
2810 | | |
2811 | | #define USE_TEXTURES 0 |
2812 | | |
2813 | | void gf_scene_generate_views(GF_Scene *scene, char *url, char *parent_path) |
2814 | 0 | { |
2815 | 0 | #ifndef GPAC_DISABLE_VRML |
2816 | 0 | char *url_search; |
2817 | 0 | Bool use_old_syntax = 1; |
2818 | 0 | GF_Node *n1, *switcher; |
2819 | | #if USE_TEXTURES |
2820 | | GF_Node *n2; |
2821 | | M_MovieTexture *mt; |
2822 | | #else |
2823 | 0 | M_Inline *inl; |
2824 | 0 | #endif |
2825 | 0 | GF_Event evt; |
2826 | 0 | gf_sc_node_destroy(scene->compositor, NULL, scene->graph); |
2827 | 0 | gf_sg_reset(scene->graph); |
2828 | |
|
2829 | 0 | scene->force_single_timeline = 1; |
2830 | 0 | n1 = is_create_node(scene->graph, TAG_MPEG4_OrderedGroup, NULL); |
2831 | 0 | gf_sg_set_root_node(scene->graph, n1); |
2832 | 0 | gf_node_register(n1, NULL); |
2833 | |
|
2834 | 0 | switcher = is_create_node(scene->graph, TAG_MPEG4_Switch, NULL); |
2835 | 0 | gf_node_register(switcher, n1); |
2836 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, switcher); |
2837 | 0 | ((M_Switch*)switcher)->whichChoice = -2; |
2838 | |
|
2839 | 0 | if (strstr(url, "::")) use_old_syntax = 0; |
2840 | |
|
2841 | 0 | url_search = url; |
2842 | 0 | while (1) { |
2843 | 0 | char *sep; |
2844 | |
|
2845 | 0 | if (use_old_syntax) { |
2846 | 0 | sep = gf_url_colon_suffix(url_search, 0); |
2847 | 0 | } else { |
2848 | 0 | sep = strstr(url_search, "::"); |
2849 | 0 | } |
2850 | 0 | if (sep) sep[0] = 0; |
2851 | |
|
2852 | | #if USE_TEXTURES |
2853 | | /*create a shape and bitmap node*/ |
2854 | | n2 = is_create_node(scene->graph, TAG_MPEG4_Shape, NULL); |
2855 | | gf_node_list_add_child( &((M_Switch *)switcher)->choice, n2); |
2856 | | gf_node_register(n2, switcher); |
2857 | | n1 = n2; |
2858 | | n2 = is_create_node(scene->graph, TAG_MPEG4_Appearance, NULL); |
2859 | | ((M_Shape *)n1)->appearance = n2; |
2860 | | gf_node_register(n2, n1); |
2861 | | |
2862 | | /*note we create a movie texture even for images...*/ |
2863 | | mt = (M_MovieTexture *) is_create_node(scene->graph, TAG_MPEG4_MovieTexture, NULL); |
2864 | | mt->startTime = gf_scene_get_time(scene); |
2865 | | ((M_Appearance *)n2)->texture = (GF_Node *)mt; |
2866 | | gf_node_register((GF_Node *)mt, n2); |
2867 | | |
2868 | | n2 = is_create_node(scene->graph, TAG_MPEG4_Bitmap, NULL); |
2869 | | ((M_Shape *)n1)->geometry = n2; |
2870 | | gf_node_register(n2, n1); |
2871 | | |
2872 | | gf_sg_vrml_mf_reset(&mt->url, GF_SG_VRML_MFURL); |
2873 | | gf_sg_vrml_mf_append(&mt->url, GF_SG_VRML_MFURL, NULL); |
2874 | | mt->url.vals[0].url = gf_url_concatenate(parent_path, url); |
2875 | | #else |
2876 | 0 | inl = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, NULL); |
2877 | 0 | gf_node_list_add_child( &((M_Switch *)switcher)->choice, (GF_Node *)inl); |
2878 | 0 | gf_node_register((GF_Node*) inl, switcher); |
2879 | |
|
2880 | 0 | gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL); |
2881 | 0 | gf_sg_vrml_mf_append(&inl->url, GF_SG_VRML_MFURL, NULL); |
2882 | 0 | inl->url.vals[0].url = gf_url_concatenate(parent_path, url); |
2883 | 0 | #endif |
2884 | |
|
2885 | 0 | if (!sep) break; |
2886 | 0 | sep[0] = ':'; |
2887 | 0 | if (use_old_syntax) { |
2888 | 0 | url = sep+1; |
2889 | 0 | } else { |
2890 | 0 | url = sep+2; |
2891 | 0 | } |
2892 | 0 | url_search = url; |
2893 | 0 | } |
2894 | |
|
2895 | 0 | gf_sc_set_option(scene->compositor, GF_OPT_USE_OPENGL, 1); |
2896 | |
|
2897 | 0 | scene->is_dynamic_scene = 2; |
2898 | 0 | gf_sg_set_scene_size_info(scene->graph, 0, 0, 1); |
2899 | |
|
2900 | 0 | gf_scene_attach_to_compositor(scene); |
2901 | |
|
2902 | 0 | evt.type = GF_EVENT_CONNECT; |
2903 | 0 | evt.connect.is_connected = 1; |
2904 | 0 | gf_sc_send_event(scene->compositor, &evt); |
2905 | 0 | #endif |
2906 | 0 | } |
2907 | | |
2908 | | void gf_scene_generate_mosaic(GF_Scene *scene, char *url, char *parent_path) |
2909 | 0 | { |
2910 | 0 | #ifndef GPAC_DISABLE_VRML |
2911 | 0 | char *url_search, *cur_url; |
2912 | 0 | Bool use_old_syntax = 1; |
2913 | 0 | GF_Node *n1; |
2914 | 0 | M_Inline *inl; |
2915 | 0 | Bool first_pass = GF_TRUE; |
2916 | 0 | u32 nb_items=0, nb_rows=0, nb_cols=0; |
2917 | 0 | s32 width=1920, height=1080, x=0, y=0, tw=1920, th=1080; |
2918 | |
|
2919 | 0 | GF_Event evt; |
2920 | 0 | gf_sc_node_destroy(scene->compositor, NULL, scene->graph); |
2921 | 0 | gf_sg_reset(scene->graph); |
2922 | |
|
2923 | 0 | scene->force_single_timeline = GF_FALSE; |
2924 | 0 | n1 = is_create_node(scene->graph, TAG_MPEG4_OrderedGroup, NULL); |
2925 | 0 | gf_sg_set_root_node(scene->graph, n1); |
2926 | 0 | gf_node_register(n1, NULL); |
2927 | |
|
2928 | 0 | if (strstr(url, "::")) use_old_syntax = 0; |
2929 | |
|
2930 | 0 | restart: |
2931 | 0 | url_search = cur_url = url; |
2932 | 0 | x = y = 0; |
2933 | 0 | while (1) { |
2934 | 0 | char *sep; |
2935 | |
|
2936 | 0 | if (use_old_syntax) { |
2937 | 0 | sep = strchr(url_search, ':'); |
2938 | | /*if :// or :\ is found, skip it*/ |
2939 | 0 | if (sep && ( ((sep[1] == '/') && (sep[2] == '/')) || (sep[1] == '\\') ) ) { |
2940 | 0 | url_search = sep+1; |
2941 | 0 | continue; |
2942 | 0 | } |
2943 | 0 | } else { |
2944 | 0 | sep = strstr(url_search, "::"); |
2945 | 0 | } |
2946 | 0 | if (sep) sep[0] = 0; |
2947 | |
|
2948 | 0 | if (first_pass) { |
2949 | 0 | nb_items ++; |
2950 | 0 | } else { |
2951 | 0 | GF_Node *tr = is_create_node(scene->graph, TAG_MPEG4_Transform2D, NULL); |
2952 | 0 | GF_Node *layer = is_create_node(scene->graph, TAG_MPEG4_Layer2D, NULL); |
2953 | 0 | gf_node_register(tr, n1); |
2954 | 0 | gf_node_list_add_child( &((GF_ParentNode *)n1)->children, tr); |
2955 | |
|
2956 | 0 | ((M_Transform2D *)tr)->translation.x = INT2FIX( -width/2 + tw/2 + x*tw); |
2957 | 0 | ((M_Transform2D *)tr)->translation.y = INT2FIX( height/2 - th/2 - y*th); |
2958 | |
|
2959 | 0 | x++; |
2960 | 0 | if (x==nb_cols) { |
2961 | 0 | y++; |
2962 | 0 | x=0; |
2963 | 0 | } |
2964 | |
|
2965 | 0 | gf_node_register(layer, tr); |
2966 | 0 | gf_node_list_add_child( &((M_Transform2D *)tr)->children, layer); |
2967 | 0 | ((M_Layer2D *)layer)->size.x = INT2FIX(tw); |
2968 | 0 | ((M_Layer2D *)layer)->size.y = INT2FIX(th); |
2969 | |
|
2970 | 0 | inl = (M_Inline *) is_create_node(scene->graph, TAG_MPEG4_Inline, NULL); |
2971 | 0 | gf_node_list_add_child( &((M_Layer2D *)layer)->children, (GF_Node *)inl); |
2972 | 0 | gf_node_register((GF_Node*) inl, layer); |
2973 | |
|
2974 | 0 | gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL); |
2975 | 0 | gf_sg_vrml_mf_append(&inl->url, GF_SG_VRML_MFURL, NULL); |
2976 | 0 | inl->url.vals[0].url = gf_url_concatenate(parent_path, cur_url); |
2977 | 0 | } |
2978 | |
|
2979 | 0 | if (!sep) break; |
2980 | 0 | sep[0] = ':'; |
2981 | 0 | if (use_old_syntax) { |
2982 | 0 | cur_url = sep+1; |
2983 | 0 | } else { |
2984 | 0 | cur_url = sep+2; |
2985 | 0 | } |
2986 | 0 | url_search = cur_url; |
2987 | 0 | } |
2988 | 0 | if (first_pass) { |
2989 | 0 | first_pass = GF_FALSE; |
2990 | 0 | nb_cols=(u32) gf_ceil( gf_sqrt(nb_items) ); |
2991 | 0 | nb_rows=nb_items/nb_cols; |
2992 | 0 | if (nb_cols * nb_rows < nb_items) nb_rows++; |
2993 | 0 | tw = width/nb_cols; |
2994 | 0 | th = height/nb_rows; |
2995 | 0 | goto restart; |
2996 | 0 | } |
2997 | | |
2998 | 0 | scene->is_dynamic_scene = 2; |
2999 | 0 | gf_sg_set_scene_size_info(scene->graph, width, height, 1); |
3000 | |
|
3001 | 0 | gf_scene_attach_to_compositor(scene); |
3002 | |
|
3003 | 0 | evt.type = GF_EVENT_CONNECT; |
3004 | 0 | evt.connect.is_connected = 1; |
3005 | 0 | gf_sc_send_event(scene->compositor, &evt); |
3006 | 0 | #endif |
3007 | 0 | } |
3008 | | |
3009 | | |
3010 | | void gf_scene_reset_addon(GF_AddonMedia *addon, Bool disconnect) |
3011 | 0 | { |
3012 | 0 | if (addon->root_od) { |
3013 | 0 | addon->root_od->addon = NULL; |
3014 | 0 | if (disconnect) { |
3015 | 0 | gf_scene_remove_object(addon->root_od->parentscene, addon->root_od, 2); |
3016 | 0 | gf_odm_disconnect(addon->root_od, 1); |
3017 | 0 | } |
3018 | 0 | } |
3019 | |
|
3020 | 0 | if (addon->url) gf_free(addon->url); |
3021 | 0 | gf_free(addon); |
3022 | 0 | } |
3023 | | |
3024 | | void gf_scene_reset_addons(GF_Scene *scene) |
3025 | 0 | { |
3026 | 0 | while (gf_list_count(scene->declared_addons)) { |
3027 | 0 | GF_AddonMedia *addon = gf_list_last(scene->declared_addons); |
3028 | 0 | gf_list_rem_last(scene->declared_addons); |
3029 | |
|
3030 | 0 | gf_scene_reset_addon(addon, GF_FALSE); |
3031 | 0 | } |
3032 | 0 | } |
3033 | | |
3034 | | static void load_associated_media(GF_Scene *scene, GF_AddonMedia *addon) |
3035 | 0 | { |
3036 | 0 | GF_MediaObject *mo; |
3037 | 0 | MFURL url; |
3038 | 0 | SFURL sfurl; |
3039 | |
|
3040 | 0 | if (!addon->enabled) return; |
3041 | | |
3042 | 0 | url.count=1; |
3043 | 0 | url.vals = &sfurl; |
3044 | 0 | url.vals[0].OD_ID = GF_MEDIA_EXTERNAL_ID; |
3045 | 0 | url.vals[0].url = (char *)addon->url; |
3046 | | |
3047 | | //we may need to change the object type once we have more ideas what the external resource is about. |
3048 | | //By default we start with scene |
3049 | | //we force the timeline of the addon to be locked with the main scene |
3050 | 0 | mo = gf_scene_get_media_object_ex(scene, &url, GF_MEDIA_OBJECT_SCENE, GF_TRUE, NULL, GF_FALSE, NULL, scene); |
3051 | |
|
3052 | 0 | if (!mo || !mo->odm) { |
3053 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[Compositor] Failed to load media addon %s\n", addon->url)); |
3054 | 0 | return; |
3055 | 0 | } |
3056 | | |
3057 | 0 | addon->root_od = mo->odm; |
3058 | 0 | mo->odm->addon = addon; |
3059 | | //force graph_attached for scalable cases where the inserted pid never connects to the compositor |
3060 | 0 | if (mo->odm->subscene) |
3061 | 0 | mo->odm->subscene->graph_attached = 1; |
3062 | |
|
3063 | 0 | if (mo->odm->addon->addon_type == GF_ADDON_TYPE_ADDITIONAL) { |
3064 | 0 | gf_scene_select_object(mo->odm->parentscene, mo->odm); |
3065 | 0 | } |
3066 | 0 | } |
3067 | | |
3068 | | |
3069 | | GF_EXPORT |
3070 | | void gf_scene_register_associated_media(GF_Scene *scene, GF_AssociatedContentLocation *addon_info) |
3071 | 0 | { |
3072 | 0 | GF_AddonMedia *addon = NULL; |
3073 | 0 | GF_Event evt; |
3074 | 0 | u32 i, count; |
3075 | 0 | Bool new_addon = 0; |
3076 | |
|
3077 | 0 | if (!scene->is_dynamic_scene) return; |
3078 | | |
3079 | 0 | count = gf_list_count(scene->declared_addons); |
3080 | 0 | for (i=0; i<count; i++) { |
3081 | 0 | Bool my_addon = 0; |
3082 | 0 | addon = gf_list_get(scene->declared_addons, i); |
3083 | 0 | if ((addon_info->timeline_id>=0) && addon->timeline_id==addon_info->timeline_id) { |
3084 | 0 | my_addon = 1; |
3085 | 0 | } else if (addon->url && addon_info->external_URL && !strcmp(addon->url, addon_info->external_URL)) { |
3086 | 0 | my_addon = 1; |
3087 | | //send message to service handler |
3088 | 0 | } |
3089 | | //this is an already received addon |
3090 | 0 | if (my_addon) { |
3091 | 0 | if (addon_info->disable_if_defined) { |
3092 | 0 | addon->enabled = GF_FALSE; |
3093 | |
|
3094 | 0 | if (addon->root_od) { |
3095 | 0 | gf_scene_toggle_addons(scene, GF_FALSE); |
3096 | 0 | gf_scene_remove_object(addon->root_od->parentscene, addon->root_od, 2); |
3097 | 0 | gf_odm_disconnect(addon->root_od, 1); |
3098 | |
|
3099 | 0 | addon->root_od->addon = NULL; |
3100 | 0 | } |
3101 | 0 | return; |
3102 | 0 | } |
3103 | | |
3104 | 0 | if (addon_info->enable_if_defined) |
3105 | 0 | addon->enabled = GF_TRUE; |
3106 | | |
3107 | | //declaration of start time |
3108 | 0 | if (addon->is_splicing && (addon->splice_start<0) && addon_info->is_splicing) { |
3109 | 0 | addon->splice_in_pts = addon_info->splice_time_pts; |
3110 | |
|
3111 | 0 | if (addon->splice_in_pts) { |
3112 | 0 | addon->media_pts = (u64) (addon_info->splice_start_time); |
3113 | 0 | addon->splice_start = addon_info->splice_start_time / 90; |
3114 | 0 | addon->splice_end = addon_info->splice_end_time / 90; |
3115 | 0 | } else { |
3116 | 0 | addon->media_pts = (u64)(addon_info->splice_start_time * 90000); |
3117 | 0 | addon->splice_start = addon_info->splice_start_time * 1000; |
3118 | 0 | addon->splice_end = addon_info->splice_end_time * 1000; |
3119 | 0 | } |
3120 | 0 | } |
3121 | | |
3122 | | //restart addon |
3123 | 0 | if (!addon->root_od && addon->timeline_ready && addon->enabled) { |
3124 | 0 | load_associated_media(scene, addon); |
3125 | 0 | } |
3126 | | //nothing associated, deactivate addon |
3127 | 0 | if (!addon_info->external_URL || !strlen(addon_info->external_URL) ) { |
3128 | 0 | gf_list_rem(scene->declared_addons, i); |
3129 | 0 | gf_scene_reset_addon(addon, GF_TRUE); |
3130 | 0 | gf_scene_toggle_addons(scene, GF_FALSE); |
3131 | 0 | } else if (strcmp(addon_info->external_URL, addon->url)) { |
3132 | | //reconfigure addon |
3133 | 0 | gf_free(addon->url); |
3134 | 0 | addon->url = NULL; |
3135 | 0 | break; |
3136 | 0 | } |
3137 | 0 | return; |
3138 | 0 | } |
3139 | 0 | addon = NULL; |
3140 | 0 | } |
3141 | | |
3142 | 0 | if (!addon_info->external_URL || !strlen(addon_info->external_URL) ) { |
3143 | 0 | return; |
3144 | 0 | } |
3145 | | |
3146 | 0 | if (!addon) { |
3147 | 0 | GF_SAFEALLOC(addon, GF_AddonMedia); |
3148 | 0 | if (!addon) { |
3149 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[Compositor] Failed to allocate media addon\n")); |
3150 | 0 | return; |
3151 | 0 | } |
3152 | 0 | addon->timeline_id = addon_info->timeline_id; |
3153 | 0 | gf_list_add(scene->declared_addons, addon); |
3154 | 0 | new_addon = 1; |
3155 | 0 | } |
3156 | | |
3157 | 0 | addon->is_splicing = addon_info->is_splicing; |
3158 | 0 | addon->activation_time = gf_scene_get_time(scene); |
3159 | |
|
3160 | 0 | if (addon_info->activation_countdown.den) |
3161 | 0 | addon->activation_time += ((Double)addon_info->activation_countdown.num) / addon_info->activation_countdown.den; |
3162 | |
|
3163 | 0 | addon->url = gf_strdup(addon_info->external_URL); |
3164 | 0 | addon->media_timescale = 1; |
3165 | 0 | addon->timeline_ready = (addon_info->timeline_id<0) ? 1 : 0; |
3166 | 0 | addon->splice_in_pts = addon_info->splice_time_pts; |
3167 | 0 | if (addon_info->is_splicing) { |
3168 | 0 | addon->addon_type = GF_ADDON_TYPE_SPLICED; |
3169 | 0 | scene->has_splicing_addons = GF_TRUE; |
3170 | |
|
3171 | 0 | if (addon->splice_in_pts) { |
3172 | 0 | addon->media_pts = (u64) (addon_info->splice_start_time); |
3173 | 0 | addon->splice_start = addon_info->splice_start_time / 90; |
3174 | 0 | addon->splice_end = addon_info->splice_end_time / 90; |
3175 | 0 | } else { |
3176 | 0 | addon->media_pts = (u64)(addon_info->splice_start_time * 90000); |
3177 | |
|
3178 | 0 | addon->splice_start = addon_info->splice_start_time * 1000; |
3179 | 0 | addon->splice_end = addon_info->splice_end_time * 1000; |
3180 | 0 | } |
3181 | 0 | } else { |
3182 | 0 | addon->splice_start = addon_info->splice_start_time; |
3183 | 0 | addon->splice_end = addon_info->splice_end_time; |
3184 | 0 | } |
3185 | |
|
3186 | 0 | if (!new_addon) return; |
3187 | | |
3188 | | //notify we found a new addon |
3189 | | |
3190 | 0 | if (! scene->root_od->parentscene) { |
3191 | 0 | if (gf_sys_is_test_mode()) { |
3192 | 0 | addon->enabled = GF_TRUE; |
3193 | 0 | } else { |
3194 | 0 | evt.type = GF_EVENT_ADDON_DETECTED; |
3195 | 0 | evt.addon_connect.addon_url = addon->url; |
3196 | 0 | addon->enabled = gf_sc_send_event(scene->compositor, &evt); |
3197 | 0 | } |
3198 | |
|
3199 | 0 | if (addon->timeline_ready) |
3200 | 0 | load_associated_media(scene, addon); |
3201 | 0 | } else { |
3202 | 0 | GF_DOM_Event devt; |
3203 | 0 | memset(&devt, 0, sizeof(GF_DOM_Event)); |
3204 | 0 | devt.type = GF_EVENT_ADDON_DETECTED; |
3205 | 0 | devt.addon_url = addon->url; |
3206 | 0 | addon->enabled = 0; |
3207 | |
|
3208 | 0 | gf_scene_notify_event(scene, GF_EVENT_SCENE_ATTACHED, NULL, &devt, GF_OK, GF_TRUE); |
3209 | 0 | } |
3210 | |
|
3211 | 0 | } |
3212 | | |
3213 | | void gf_scene_notify_associated_media_timeline(GF_Scene *scene, GF_AssociatedContentTiming *addon_time) |
3214 | 0 | { |
3215 | 0 | Double prev_time; |
3216 | 0 | GF_AddonMedia *addon = NULL; |
3217 | |
|
3218 | 0 | u32 i, count = gf_list_count(scene->declared_addons); |
3219 | 0 | for (i=0; i<count; i++) { |
3220 | 0 | addon = gf_list_get(scene->declared_addons, i); |
3221 | 0 | if (addon->timeline_id==addon_time->timeline_id) |
3222 | 0 | break; |
3223 | 0 | addon = NULL; |
3224 | 0 | } |
3225 | 0 | if (!addon) return; |
3226 | | |
3227 | 0 | count = i; |
3228 | 0 | for (i=0; i<count; i++) { |
3229 | 0 | GF_AddonMedia *prev_addon = gf_list_get(scene->declared_addons, i); |
3230 | | //we are adding a non splicing point: discard all previously declared addons |
3231 | 0 | if (!addon->is_splicing |
3232 | | //this is a splicing point, discard all previously declared splicing addons |
3233 | 0 | || prev_addon->is_splicing |
3234 | 0 | ) { |
3235 | 0 | gf_scene_reset_addon(prev_addon, GF_TRUE); |
3236 | 0 | gf_list_rem(scene->declared_addons, i); |
3237 | 0 | i--; |
3238 | 0 | count--; |
3239 | 0 | } |
3240 | 0 | } |
3241 | | |
3242 | |
|
3243 | 0 | prev_time = (Double) addon->media_timestamp; |
3244 | 0 | prev_time /= addon->media_timescale; |
3245 | | |
3246 | | //loop has been detected |
3247 | 0 | if ( prev_time * addon_time->media_timescale > addon_time->media_timestamp + 1.5 * addon_time->media_timescale ) { |
3248 | 0 | if (!addon->loop_detected) { |
3249 | 0 | addon->loop_detected = GF_TRUE; |
3250 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Loop detected in addon - PTS "LLD" (CTS %d) - media time "LLD"\n", addon_time->media_pts, addon_time->media_pts/90, addon_time->media_timestamp)); |
3251 | 0 | addon->past_media_pts = addon_time->media_pts; |
3252 | 0 | addon->past_media_timestamp = addon_time->media_timestamp; |
3253 | 0 | addon->past_media_timescale = addon_time->media_timescale; |
3254 | 0 | addon->past_media_pts_scaled = addon_time->media_pts/90; |
3255 | 0 | } |
3256 | 0 | } else if (!addon->loop_detected) { |
3257 | 0 | addon->media_pts = addon_time->media_pts; |
3258 | 0 | addon->media_timestamp = addon_time->media_timestamp; |
3259 | 0 | addon->media_timescale = addon_time->media_timescale; |
3260 | 0 | gf_assert(addon_time->media_timescale); |
3261 | 0 | gf_assert(!addon->loop_detected); |
3262 | 0 | } |
3263 | |
|
3264 | 0 | if (!addon->timeline_ready) { |
3265 | 0 | addon->timeline_ready = GF_TRUE; |
3266 | 0 | load_associated_media(scene, addon); |
3267 | 0 | } |
3268 | |
|
3269 | 0 | if ((addon->addon_type==GF_ADDON_TYPE_MAIN) && addon->root_od && addon->root_od->duration && !addon->root_od->timeshift_depth) { |
3270 | 0 | Double dur, tsb; |
3271 | 0 | dur = (Double) addon->root_od->duration; |
3272 | 0 | dur /= 1000; |
3273 | 0 | tsb = (Double) addon->media_timestamp; |
3274 | 0 | tsb /= addon->media_timescale; |
3275 | 0 | if (tsb>dur) tsb = dur; |
3276 | 0 | addon->root_od->parentscene->root_od->timeshift_depth = (u32) (1000*tsb); |
3277 | 0 | gf_scene_set_timeshift_depth(scene); |
3278 | 0 | } |
3279 | | |
3280 | | //and forward ntp if any to underlying service |
3281 | 0 | if (addon_time->ntp && addon->root_od && addon->root_od->pid) { |
3282 | 0 | GF_FilterEvent evt; |
3283 | 0 | GF_FEVT_INIT(evt, GF_FEVT_PLAY, addon->root_od->pid); |
3284 | 0 | evt.ntp.ntp = addon_time->ntp; |
3285 | 0 | gf_filter_pid_send_event(addon->root_od->pid, &evt); |
3286 | 0 | } |
3287 | 0 | } |
3288 | | |
3289 | | |
3290 | | |
3291 | | Bool gf_scene_check_addon_restart(GF_AddonMedia *addon, u64 cts, u64 dts) |
3292 | 0 | { |
3293 | 0 | u32 i; |
3294 | 0 | GF_ObjectManager*odm; |
3295 | 0 | GF_Scene *subscene; |
3296 | 0 | GF_List *to_restart = NULL; |
3297 | |
|
3298 | 0 | if (!addon || !addon->loop_detected) return GF_FALSE; |
3299 | | //warning, we need to compare to media PTS/90 since we already rounded the media_ts to milliseconds (otherwise we would get rounding errors). |
3300 | 0 | if ((cts == addon->past_media_pts_scaled) || (dts >= addon->past_media_pts_scaled) ) { |
3301 | 0 | } else { |
3302 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Loop not yet active - CTS "LLD" DTS "LLD" media TS "LLD" \n", cts, dts, addon->past_media_pts_scaled)); |
3303 | 0 | return GF_FALSE; |
3304 | 0 | } |
3305 | | |
3306 | 0 | addon->loop_detected = 0; |
3307 | 0 | addon->media_pts = addon->past_media_pts; |
3308 | 0 | addon->media_timestamp = addon->past_media_timestamp; |
3309 | 0 | addon->media_timescale = addon->past_media_timescale; |
3310 | 0 | gf_assert(addon->past_media_timescale); |
3311 | 0 | addon->past_media_pts = 0; |
3312 | 0 | addon->past_media_timestamp = 0; |
3313 | 0 | addon->past_media_timescale = 0; |
3314 | |
|
3315 | 0 | subscene = addon->root_od->subscene; |
3316 | |
|
3317 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Looping addon - CTS "LLD" - addon media TS "LLD" (CTS "LLD") addon media time "LLD"\n", cts, addon->media_pts, addon->media_pts/90, addon->media_timestamp)); |
3318 | |
|
3319 | 0 | to_restart = gf_list_new(); |
3320 | |
|
3321 | 0 | i=0; |
3322 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(subscene->resources, &i))) { |
3323 | 0 | if (odm->state == GF_ODM_STATE_PLAY) { |
3324 | 0 | gf_list_add(to_restart, odm); |
3325 | 0 | } |
3326 | 0 | gf_odm_stop(odm, GF_FALSE); |
3327 | 0 | } |
3328 | |
|
3329 | 0 | i=0; |
3330 | 0 | while ((odm = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) { |
3331 | 0 | gf_odm_start(odm); |
3332 | 0 | } |
3333 | 0 | gf_list_del(to_restart); |
3334 | 0 | return GF_TRUE; |
3335 | 0 | } |
3336 | | |
3337 | | Double gf_scene_adjust_time_for_addon(GF_AddonMedia *addon, Double clock_time, u8 *timestamp_based) |
3338 | 0 | { |
3339 | 0 | Double media_time; |
3340 | 0 | if (!addon->timeline_ready) |
3341 | 0 | return clock_time; |
3342 | | |
3343 | 0 | if (timestamp_based) |
3344 | 0 | *timestamp_based = (addon->timeline_id>=0) ? 0 : 1; |
3345 | |
|
3346 | 0 | if (addon->is_splicing) { |
3347 | 0 | return ((Double)addon->media_timestamp) / addon->media_timescale; |
3348 | 0 | } |
3349 | | |
3350 | | //get PTS diff (clock is in ms, pt is in 90k) |
3351 | 0 | media_time = clock_time; |
3352 | 0 | media_time -= addon->media_pts/90000.0; |
3353 | |
|
3354 | 0 | media_time += ((Double)addon->media_timestamp) / addon->media_timescale; |
3355 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("Addon about to start - media time %g\n", media_time)); |
3356 | 0 | return media_time; |
3357 | 0 | } |
3358 | | |
3359 | | s64 gf_scene_adjust_timestamp_for_addon(GF_AddonMedia *addon, u64 orig_ts) |
3360 | 0 | { |
3361 | 0 | s64 media_ts_ms; |
3362 | 0 | gf_assert(addon->timeline_ready); |
3363 | 0 | if (addon->is_splicing) { |
3364 | 0 | if (!addon->min_dts_set || (orig_ts<addon->splice_min_dts)) { |
3365 | 0 | addon->splice_min_dts = orig_ts; |
3366 | 0 | addon->min_dts_set = GF_TRUE; |
3367 | 0 | } |
3368 | 0 | orig_ts -= addon->splice_min_dts; |
3369 | 0 | } |
3370 | 0 | media_ts_ms = orig_ts; |
3371 | 0 | media_ts_ms -= (addon->media_timestamp*1000) / addon->media_timescale; |
3372 | 0 | media_ts_ms += (addon->media_pts/90); |
3373 | 0 | return media_ts_ms; |
3374 | 0 | } |
3375 | | |
3376 | | GF_EXPORT |
3377 | | void gf_scene_switch_quality(GF_Scene *scene, Bool up) |
3378 | 0 | { |
3379 | 0 | u32 i; |
3380 | 0 | GF_ObjectManager *odm; |
3381 | 0 | GF_FilterEvent evt; |
3382 | |
|
3383 | 0 | if (!scene) return; |
3384 | | |
3385 | 0 | GF_FEVT_INIT(evt, GF_FEVT_QUALITY_SWITCH, NULL); |
3386 | 0 | evt.quality_switch.up = up; |
3387 | | |
3388 | 0 | if (scene->root_od->pid) { |
3389 | 0 | gf_filter_pid_send_event(scene->root_od->pid, &evt); |
3390 | 0 | if (scene->root_od->extra_pids) { |
3391 | 0 | GF_ODMExtraPid *xpid; |
3392 | 0 | i=0; |
3393 | 0 | while ( (xpid = gf_list_enum(scene->root_od->extra_pids, &i) ) ) { |
3394 | 0 | gf_filter_pid_send_event(xpid->pid, &evt); |
3395 | 0 | } |
3396 | 0 | } |
3397 | 0 | } |
3398 | |
|
3399 | 0 | i=0; |
3400 | 0 | while (NULL != (odm = gf_list_enum(scene->resources, &i))) { |
3401 | 0 | if (odm->pid) { |
3402 | 0 | gf_filter_pid_send_event(odm->pid, &evt); |
3403 | 0 | } |
3404 | 0 | if (odm->subscene) |
3405 | 0 | gf_scene_switch_quality(odm->subscene, up); |
3406 | 0 | } |
3407 | 0 | } |
3408 | | |
3409 | | #endif //!defined(GPAC_DISABLE_COMPOSITOR) |