Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2017
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/compositor.h>
34 : #include <gpac/nodes_x3d.h>
35 : #include <gpac/crypt.h>
36 :
37 : /*SVG properties*/
38 : #ifndef GPAC_DISABLE_SVG
39 : #include <gpac/scenegraph_svg.h>
40 : #endif
41 :
42 :
43 :
44 : #ifndef GPAC_DISABLE_VRML
45 :
46 1 : void gf_inline_restart(GF_Scene *scene)
47 : {
48 1 : scene->needs_restart = 1;
49 1 : gf_sc_invalidate(scene->compositor, NULL);
50 1 : }
51 :
52 :
53 5255 : static Bool gf_inline_set_scene(M_Inline *root)
54 : {
55 : GF_MediaObject *mo;
56 : GF_Scene *parent;
57 5255 : GF_SceneGraph *graph = gf_node_get_graph((GF_Node *) root);
58 5255 : parent = (GF_Scene *)gf_sg_get_private(graph);
59 5255 : if (!parent) return GF_FALSE;
60 :
61 5255 : mo = gf_scene_get_media_object_ex(parent, &root->url, GF_MEDIA_OBJECT_SCENE, GF_FALSE, NULL, GF_FALSE, (GF_Node*)root);
62 5255 : if (!mo) return GF_FALSE;
63 : //invalidate as soon as we have an mo (eg something is attached to the scene)
64 1464 : gf_sc_invalidate(parent->compositor, NULL);
65 :
66 1464 : if (!mo->odm) return GF_FALSE;
67 :
68 765 : if (!mo->odm->subscene) {
69 709 : gf_sc_invalidate(parent->compositor, NULL);
70 709 : return GF_FALSE;
71 : }
72 : /*assign inline scene as private stack of inline node, and remember inline node for event propagation*/
73 56 : gf_node_set_private((GF_Node *)root, mo->odm->subscene);
74 56 : mo->odm->subscene->object_attached = GF_TRUE;
75 56 : if (gf_list_find(mo->odm->subscene->attached_inlines, root)<0)
76 56 : gf_list_add(mo->odm->subscene->attached_inlines, root);
77 :
78 : /*play*/
79 56 : gf_mo_play(mo, 0, -1, GF_FALSE);
80 :
81 56 : return GF_TRUE;
82 : }
83 :
84 91 : void gf_inline_on_modified(GF_Node *node)
85 : {
86 : u32 ODID;
87 : GF_MediaObject *mo;
88 : M_Inline *pInline = (M_Inline *) node;
89 91 : GF_Scene *scene = (GF_Scene *)gf_node_get_private(node);
90 :
91 91 : ODID = gf_mo_get_od_id(&pInline->url);
92 91 : if (scene) {
93 0 : mo = (scene->root_od) ? scene->root_od->mo : NULL;
94 :
95 : /*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/
96 0 : if (mo) {
97 : Bool changed = GF_TRUE;
98 0 : if (ODID != GF_MEDIA_EXTERNAL_ID) {
99 0 : if (ODID && (ODID==scene->root_od->ID)) changed = GF_FALSE;
100 : } else {
101 0 : if (gf_mo_is_same_url(mo, &pInline->url, NULL, 0) ) changed = GF_FALSE;
102 : }
103 0 : if (mo->num_open) {
104 0 : if (!changed) return;
105 :
106 0 : gf_scene_notify_event(scene, GF_EVENT_UNLOAD, node, NULL, GF_OK, GF_TRUE);
107 0 : gf_node_dirty_parents(node);
108 0 : gf_mo_event_target_remove_by_node(mo, node);
109 :
110 : /*reset the scene pointer as it may get destroyed*/
111 0 : switch (gf_node_get_tag(node)) {
112 0 : case TAG_MPEG4_Inline:
113 : #ifndef GPAC_DISABLE_X3D
114 : case TAG_X3D_Inline:
115 : #endif
116 0 : gf_node_set_private(node, NULL);
117 0 : break;
118 : }
119 :
120 0 : scene->object_attached = GF_FALSE;
121 0 : mo->num_open --;
122 0 : if (!mo->num_open) {
123 0 : if (ODID == GF_MEDIA_EXTERNAL_ID) {
124 0 : GF_Scene *parent = scene->root_od->parentscene;
125 : /*!!! THIS WILL DESTROY THE INLINE SCENE OBJECT !!!*/
126 0 : gf_odm_disconnect(scene->root_od, GF_TRUE);
127 : /*and force removal of the media object*/
128 0 : if (parent) {
129 0 : if (gf_list_del_item(parent->scene_objects, mo)>=0) {
130 0 : gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL);
131 0 : gf_mo_del(mo);
132 : }
133 : }
134 : } else {
135 :
136 : /*external media are completely unloaded, except addons which are only declared once */
137 0 : if (!scene->root_od->addon && (scene->root_od->ID==GF_MEDIA_EXTERNAL_ID)) {
138 0 : gf_odm_disconnect(scene->root_od, 2);
139 : } else {
140 0 : gf_odm_stop_or_destroy(scene->root_od);
141 : }
142 : }
143 : }
144 : }
145 : }
146 : }
147 : /*force a redraw and load scene at next pass - we cannot load the scene now because
148 : - we can be in a JS call (eg JS mutex blocked)
149 : - locating scene objects matching the new url needs exclusive access to the MediaObject list, achieved with the term net mutex
150 : - another service may already be setting up objects (eg exclusive access to the net mutex grabbed), which can trigger event forwarding
151 : - some event forwarders may request JS context (eg access to JS mutex)
152 :
153 : In such a case we would end up in a deadlock - this needs urgent fixing ...
154 : */
155 91 : if (ODID) {
156 : /*if no parent we must process the url change as we may not be traversed later on (not in the scene tree)*/
157 87 : if (gf_node_get_parent(node, 0)==NULL) {
158 14 : gf_inline_set_scene(pInline);
159 : } else {
160 73 : gf_node_dirty_parents(node);
161 : }
162 : }
163 : }
164 :
165 4966 : static void gf_inline_check_restart(GF_Scene *scene)
166 : {
167 : /*no ctrl if no duration*/
168 4966 : if (!scene->duration) return;
169 680 : if (!scene->needs_restart) gf_odm_check_segment_switch(scene->root_od);
170 680 : if (scene->needs_restart) return;
171 :
172 679 : if (scene->root_od->media_ctrl && scene->root_od->media_ctrl->control->loop) {
173 2 : GF_Clock *ck = gf_odm_get_media_clock(scene->root_od);
174 2 : if (ck->has_seen_eos && !ck->nb_paused) {
175 0 : u32 now = gf_clock_time(ck);
176 0 : u64 dur = scene->duration;
177 0 : if (scene->root_od->media_ctrl->current_seg) {
178 : /*only process when all segments are played*/
179 0 : if (gf_list_count(scene->root_od->media_ctrl->seg) <= scene->root_od->media_ctrl->current_seg) {
180 0 : scene->needs_restart = 1;
181 0 : scene->root_od->media_ctrl->current_seg = 0;
182 : }
183 : }
184 : else {
185 : Double s, e;
186 0 : s = now;
187 0 : s/=1000;
188 0 : e = -1;
189 0 : MC_GetRange(scene->root_od->media_ctrl, &s, &e);
190 0 : if ((e>=0) && (e<GF_MAX_FLOAT)) dur = (u32) (e*1000);
191 0 : if (dur<=now) {
192 0 : scene->needs_restart = 1;
193 0 : scene->root_od->media_ctrl->current_seg = 0;
194 : } else {
195 : /*trigger render until to watch for restart...*/
196 0 : gf_sc_invalidate(scene->compositor, NULL);
197 : }
198 : }
199 : }
200 : }
201 : }
202 :
203 468 : void gf_scene_mpeg4_inline_check_restart(GF_Scene *scene)
204 : {
205 468 : gf_inline_check_restart(scene);
206 :
207 468 : if (scene->needs_restart) {
208 0 : gf_sc_invalidate(scene->compositor, NULL);
209 0 : return;
210 : }
211 : }
212 :
213 1 : void gf_scene_mpeg4_inline_restart(GF_Scene *scene)
214 : {
215 : u32 current_seg = 0;
216 :
217 1 : if (scene->root_od->media_ctrl) current_seg = scene->root_od->media_ctrl->current_seg;
218 :
219 1 : if (scene->is_dynamic_scene) {
220 : s64 from = 0;
221 0 : if (scene->root_od->media_ctrl) {
222 0 : if (scene->root_od->media_ctrl->media_stop<=0) {
223 0 : from = (s64) (scene->root_od->media_ctrl->media_stop * 1000) - 1;
224 : }
225 0 : else if (scene->root_od->media_ctrl->media_start>=0) {
226 0 : scene->root_od->media_ctrl->current_seg = current_seg;
227 0 : from = (s64) (scene->root_od->media_ctrl->media_start * 1000);
228 : }
229 : }
230 0 : gf_scene_restart_dynamic(scene, from, 0, 0);
231 : } else {
232 : /*we cannot use gf_mo_restart since it only sets the needs_restart for inline scenes.
233 : The rational is that gf_mo_restart can be called from the parent scene (OK) or from the scene itself, in
234 : which case shutting down the graph would crash the compositor. We therefore need two render passes to
235 : safely restart an inline scene*/
236 :
237 : /*1- stop main object from playing but don't disconnect channels*/
238 1 : gf_odm_stop(scene->root_od, GF_TRUE);
239 : /*2- close all ODs inside the scene and reset the graph*/
240 1 : gf_scene_disconnect(scene, GF_FALSE);
241 1 : if (scene->root_od->media_ctrl) scene->root_od->media_ctrl->current_seg = current_seg;
242 : /*3- restart the scene*/
243 1 : gf_odm_start(scene->root_od);
244 : }
245 1 : }
246 :
247 13416 : static void gf_inline_traverse(GF_Node *n, void *rs, Bool is_destroy)
248 : {
249 : MFURL *current_url;
250 13416 : GF_Scene *scene = (GF_Scene *)gf_node_get_private(n);
251 :
252 13416 : if (is_destroy) {
253 : GF_MediaObject *mo;
254 728 : if (!scene) return;
255 56 : mo = scene->root_od ? scene->root_od->mo : NULL;
256 :
257 56 : gf_list_del_item(scene->attached_inlines, n);
258 :
259 56 : gf_scene_notify_event(scene, GF_EVENT_UNLOAD, n, NULL, GF_OK, GF_TRUE);
260 56 : if (!mo) return;
261 56 : gf_mo_event_target_remove_by_node(mo, n);
262 :
263 : /*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/
264 56 : if (mo->num_open) {
265 56 : mo->num_open --;
266 56 : if (!mo->num_open) {
267 : /*this is unspecified in the spec: whenever an inline not using the
268 : OD framework is destroyed, destroy the associated resource*/
269 56 : if (mo->OD_ID == GF_MEDIA_EXTERNAL_ID) {
270 : /*get parent scene and remove MediaObject in case the resource
271 : gets re-requested later on*/
272 52 : GF_Scene *parent_scene = (GF_Scene *)gf_sg_get_private(gf_node_get_graph((GF_Node *) n) );
273 52 : if (gf_list_del_item(parent_scene->scene_objects, mo)>=0) {
274 52 : gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL);
275 52 : if (mo->odm) {
276 52 : gf_odm_reset_media_control(mo->odm, 1);
277 52 : mo->odm->mo = NULL;
278 : }
279 52 : gf_mo_del(mo);
280 : }
281 52 : gf_odm_disconnect(scene->root_od, 2);
282 : } else {
283 4 : gf_odm_stop(scene->root_od, 1);
284 4 : gf_scene_disconnect(scene->root_od->subscene, GF_TRUE);
285 : }
286 : }
287 : }
288 : return;
289 : }
290 :
291 :
292 : //if no private scene is associated get the node parent graph, retrieve the IS and find the OD
293 12688 : if (!scene) {
294 : M_Inline *inl = (M_Inline *)n;
295 5241 : gf_inline_set_scene(inl);
296 5241 : scene = (GF_Scene *)gf_node_get_private(n);
297 5241 : if (!scene) {
298 : /*just like protos, we must invalidate parent graph until attached*/
299 5199 : if (inl->url.count) {
300 1409 : if (!inl->url.vals[0].OD_ID && (!inl->url.vals[0].url || !strlen(inl->url.vals[0].url) ) ) {
301 1 : gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL);
302 : } else {
303 1408 : gf_node_dirty_set(n, 0, GF_TRUE);
304 : }
305 : }
306 : return;
307 : }
308 : }
309 :
310 : /*if not attached return (attaching the graph cannot be done in render since render is not called while unattached :) */
311 7489 : if (!scene->graph_attached) {
312 : /*just like protos, we must invalidate parent graph until attached*/
313 2991 : gf_node_dirty_set(n, 0, GF_TRUE);
314 : //and request bew anim frame until attached
315 2991 : if (scene->object_attached)
316 2990 : gf_sc_invalidate(scene->compositor, NULL);
317 : return;
318 : }
319 : /*clear dirty flags for any sub-inlines, bitmaps or protos*/
320 4498 : gf_node_dirty_clear(n, 0);
321 :
322 4498 : current_url = scene->current_url;
323 4498 : scene->current_url = & ((M_Inline*)n)->url;
324 4498 : gf_sc_traverse_subscene(scene->compositor, n, scene->graph, rs);
325 4498 : scene->current_url = current_url;
326 :
327 : //do we have to restart for next frame ? If so let's do it
328 4498 : gf_inline_check_restart(scene);
329 :
330 : /*if we need to restart, shutdown graph and do it*/
331 4498 : if (scene->needs_restart) {
332 : /*special case: scene change*/
333 1 : if (scene->needs_restart==2) {
334 0 : scene->needs_restart = 0;
335 0 : gf_inline_on_modified(n);
336 0 : return;
337 : }
338 :
339 1 : scene->needs_restart = 0;
340 1 : gf_scene_mpeg4_inline_restart(scene);
341 1 : gf_node_dirty_set(n, 0, GF_TRUE);
342 1 : return;
343 : }
344 :
345 : }
346 :
347 :
348 460 : static Bool gf_inline_is_hardcoded_proto(GF_Compositor *compositor, MFURL *url)
349 : {
350 : u32 i;
351 833 : for (i=0; i<url->count; i++) {
352 460 : if (!url->vals[i].url) continue;
353 393 : if (strstr(url->vals[i].url, "urn:inet:gpac:builtin")) return GF_TRUE;
354 :
355 306 : if (gf_sc_uri_is_hardcoded_proto(compositor, url->vals[i].url))
356 : return GF_TRUE;
357 : }
358 : return GF_FALSE;
359 : }
360 :
361 326 : GF_SceneGraph *gf_inline_get_proto_lib(void *_is, MFURL *lib_url)
362 : {
363 : GF_ProtoLink *pl;
364 : u32 i;
365 : GF_Scene *scene = (GF_Scene *) _is;
366 326 : if (!scene) return NULL;
367 :
368 : //this is a scene reset, destroy all proto links
369 326 : if (!lib_url) {
370 51 : while (gf_list_count(scene->extern_protos)) {
371 5 : pl = gf_list_pop_back(scene->extern_protos);
372 5 : if (pl->mo) {
373 : //proto link was not attached, manual discard
374 5 : if (!pl->mo->odm) {
375 0 : gf_sg_vrml_mf_reset(&pl->mo->URLs, GF_SG_VRML_MFURL);
376 0 : gf_list_del_item(scene->scene_objects, pl->mo);
377 0 : gf_mo_del(pl->mo);
378 : }
379 : }
380 5 : gf_free(pl);
381 : }
382 : return NULL;
383 : }
384 280 : if (!lib_url->count)
385 : return NULL;
386 :
387 280 : if (gf_inline_is_hardcoded_proto(scene->compositor, lib_url)) return (void *) GF_SG_INTERNAL_PROTO;
388 :
389 193 : i=0;
390 562 : while ((pl = (GF_ProtoLink*)gf_list_enum(scene->extern_protos, &i))) {
391 : /*not ready yet*/
392 189 : if (!pl->mo || !pl->mo->odm) continue;
393 :
394 165 : if (gf_mo_get_od_id(pl->url) != GF_MEDIA_EXTERNAL_ID) {
395 13 : if (gf_mo_get_od_id(pl->url) == gf_mo_get_od_id(lib_url)) {
396 13 : if (!pl->mo->odm || !pl->mo->odm->subscene) return NULL;
397 13 : return pl->mo->odm->subscene->graph;
398 : }
399 : }
400 : }
401 :
402 : /*for string URL based protos, recursively check until top if the proto lib is not already present*/
403 180 : if (lib_url->vals[0].url) {
404 : GF_Scene *check_scene = scene;
405 306 : while (check_scene) {
406 153 : i=0;
407 458 : while ((pl = (GF_ProtoLink*)gf_list_enum(check_scene->extern_protos, &i))) {
408 : char *url1, *url2;
409 : Bool ok;
410 152 : if (!pl->mo || !pl->mo->odm) continue;
411 :
412 152 : if (gf_mo_get_od_id(pl->url) != GF_MEDIA_EXTERNAL_ID) continue;
413 : /*not the same url*/
414 152 : if (!gf_mo_is_same_url(pl->mo, lib_url, NULL, 0)) continue;
415 : ok = GF_FALSE;
416 :
417 : /*check the url path is the same*/
418 152 : url1 = gf_url_concatenate(pl->mo->odm->scene_ns->url, lib_url->vals[0].url);
419 152 : url2 = gf_url_concatenate(scene->root_od->scene_ns->url, lib_url->vals[0].url);
420 152 : if (url1 && url2 && !strcmp(url1, url2)) ok=GF_TRUE;
421 152 : if (url1) gf_free(url1);
422 152 : if (url2) gf_free(url2);
423 :
424 152 : if (!ok) continue;
425 0 : if (!pl->mo->odm || !pl->mo->odm->subscene) return NULL;
426 0 : return pl->mo->odm->subscene->graph;
427 : }
428 153 : check_scene = check_scene->root_od->parentscene;
429 : }
430 : }
431 :
432 : /*not found, let's try to load it*/
433 :
434 180 : if (!lib_url || !lib_url->count) return NULL;
435 :
436 : /*internal, don't waste resources*/
437 180 : if (gf_inline_is_hardcoded_proto(scene->compositor, lib_url)) return NULL;
438 :
439 180 : i=0;
440 364 : while ((pl = (GF_ProtoLink*)gf_list_enum(scene->extern_protos, &i)) ) {
441 173 : if (pl->url == lib_url) return NULL;
442 4 : if (pl->url->vals[0].OD_ID && (pl->url->vals[0].OD_ID == lib_url->vals[0].OD_ID)) return NULL;
443 4 : if (pl->url->vals[0].url && lib_url->vals[0].url && !stricmp(pl->url->vals[0].url, lib_url->vals[0].url) ) return NULL;
444 : }
445 11 : pl = (GF_ProtoLink*)gf_malloc(sizeof(GF_ProtoLink));
446 11 : pl->url = lib_url;
447 11 : gf_list_add(scene->extern_protos, pl);
448 11 : pl->mo = gf_scene_get_media_object(scene, lib_url, GF_MEDIA_OBJECT_SCENE, GF_FALSE);
449 : /*this may already be destroyed*/
450 11 : if (pl->mo) gf_mo_play(pl->mo, 0, -1, GF_FALSE);
451 :
452 : /*and return NULL*/
453 : return NULL;
454 : }
455 :
456 :
457 8 : Bool gf_inline_is_default_viewpoint(GF_Node *node)
458 : {
459 : const char *nname, *seg_name;
460 8 : GF_SceneGraph *sg = gf_node_get_graph(node);
461 8 : GF_Scene *scene = sg ? (GF_Scene *) gf_sg_get_private(sg) : NULL;
462 8 : if (!scene) return GF_FALSE;
463 :
464 8 : nname = gf_node_get_name(node);
465 8 : if (!nname) return GF_FALSE;
466 :
467 : /*check any viewpoint*/
468 4 : seg_name = strrchr(scene->root_od->scene_ns->url, '#');
469 :
470 : /*check the URL of the parent*/
471 4 : if (!seg_name && scene->current_url) {
472 0 : if (scene->current_url->count && scene->current_url->vals[0].url)
473 0 : seg_name = strrchr(scene->current_url->vals[0].url, '#');
474 4 : } else if (!seg_name && scene->root_od->mo && scene->root_od->mo->URLs.count && scene->root_od->mo->URLs.vals[0].url) {
475 0 : seg_name = strrchr(scene->root_od->mo->URLs.vals[0].url, '#');
476 : }
477 4 : if (!seg_name) return GF_FALSE;
478 0 : seg_name += 1;
479 : /*look for a media segment with this name - if none found, this is a viewpoint name*/
480 0 : if (gf_odm_find_segment(scene->root_od, (char *) seg_name) != NULL) return GF_FALSE;
481 :
482 0 : return (!strcmp(nname, seg_name) ? GF_TRUE : GF_FALSE);
483 : }
484 :
485 :
486 728 : void gf_init_inline(GF_Scene *scene, GF_Node *node)
487 : {
488 728 : gf_node_set_callback_function(node, gf_inline_traverse);
489 728 : }
490 :
491 2 : static char *storage_get_section(M_Storage *storage)
492 : {
493 : GF_Scene *scene;
494 : char *szPath;
495 : u8 hash[20];
496 : char name[50];
497 : u32 i;
498 : size_t len;
499 :
500 2 : scene = (GF_Scene *)gf_node_get_private((GF_Node*)storage);
501 :
502 2 : len = strlen(scene->root_od->scene_ns->url)+strlen(storage->name.buffer)+2;
503 2 : szPath = (char *)gf_malloc(sizeof(char)* len);
504 2 : strcpy(szPath, scene->root_od->scene_ns->url);
505 : strcat(szPath, "@");
506 2 : strcat(szPath, storage->name.buffer);
507 2 : gf_sha1_csum((u8*)szPath, (u32) strlen(szPath), hash);
508 2 : gf_free(szPath);
509 :
510 : strcpy(name, "@cache=");
511 42 : for (i=0; i<20; i++) {
512 : char t[3];
513 40 : t[2] = 0;
514 40 : sprintf(t, "%02X", hash[i]);
515 : strcat(name, t);
516 : }
517 2 : return gf_strdup(name);
518 : }
519 :
520 0 : static void storage_parse_sf(void *ptr, u32 fieldType, char *opt)
521 : {
522 : Float v1, v2, v3;
523 0 : switch (fieldType) {
524 1 : case GF_SG_VRML_SFBOOL:
525 1 : sscanf(opt, "%d", ((SFBool *)ptr));
526 0 : break;
527 0 : case GF_SG_VRML_SFINT32:
528 0 : sscanf(opt, "%d", ((SFInt32 *)ptr) );
529 0 : break;
530 0 : case GF_SG_VRML_SFTIME:
531 0 : sscanf(opt, "%lf", ((SFTime *)ptr) );
532 0 : break;
533 0 : case GF_SG_VRML_SFFLOAT:
534 0 : sscanf(opt, "%g", &v1);
535 0 : * (SFFloat *)ptr = FLT2FIX(v1);
536 0 : break;
537 0 : case GF_SG_VRML_SFVEC2F:
538 0 : sscanf(opt, "%g %g", &v1, &v2);
539 0 : ((SFVec2f*)ptr)->x = FLT2FIX(v1);
540 0 : ((SFVec2f*)ptr)->y = FLT2FIX(v2);
541 0 : break;
542 0 : case GF_SG_VRML_SFVEC3F:
543 0 : sscanf(opt, "%g %g %g", &v1, &v2, &v3);
544 0 : ((SFVec3f*)ptr)->x = FLT2FIX(v1);
545 0 : ((SFVec3f*)ptr)->y = FLT2FIX(v2);
546 0 : ((SFVec3f*)ptr)->z = FLT2FIX(v3);
547 0 : break;
548 0 : case GF_SG_VRML_SFSTRING:
549 0 : if ( ((SFString *)ptr)->buffer) gf_free(((SFString *)ptr)->buffer);
550 0 : ((SFString *)ptr)->buffer = gf_strdup(opt);
551 0 : break;
552 : default:
553 : break;
554 : }
555 0 : }
556 :
557 1 : static void gf_storage_load(M_Storage *storage)
558 : {
559 : const char *opt;
560 : char szID[20];
561 : u32 i, count;
562 : u32 sec, exp, frac;
563 1 : char *section = storage_get_section(storage);
564 2 : if (!section) return;
565 :
566 1 : if (!gf_opts_get_key_count(section)) {
567 1 : gf_free(section);
568 1 : return;
569 : }
570 0 : opt = gf_opts_get_key(section, "expireAfterNTP");
571 0 : gf_net_get_ntp(&sec, &frac);
572 0 : sscanf(opt, "%u", &exp);
573 0 : if (exp && (exp<=sec)) {
574 0 : gf_opts_del_section(section);
575 0 : gf_free(section);
576 0 : return;
577 : }
578 :
579 0 : count = gf_opts_get_key_count(section)-1;
580 0 : if (!count || (count!=storage->storageList.count)) {
581 0 : gf_opts_del_section(section);
582 0 : gf_free(section);
583 0 : return;
584 : }
585 :
586 0 : for (i=0; i<count; i++) {
587 : GF_FieldInfo info;
588 : sprintf(szID, "%d", i);
589 0 : opt = gf_opts_get_key(section, szID);
590 0 : if (!opt) break;
591 0 : if (!storage->storageList.vals[i].node) break;
592 0 : if (gf_node_get_field(storage->storageList.vals[i].node, storage->storageList.vals[i].fieldIndex, &info) != GF_OK) break;
593 :
594 0 : if (gf_sg_vrml_is_sf_field(info.fieldType)) {
595 0 : storage_parse_sf(info.far_ptr, info.fieldType, (char *) opt);
596 : } else {
597 0 : u32 sftype = gf_sg_vrml_get_sf_type(info.fieldType);
598 : char *sep, *val;
599 : void *slot;
600 0 : gf_sg_vrml_mf_reset(info.far_ptr, info.fieldType);
601 : while (1) {
602 0 : val = (char *)strchr(opt, '\'');
603 0 : sep = val ? strchr(val+1, '\'') : NULL;
604 0 : if (!val || !sep) break;
605 :
606 0 : sep[0] = 0;
607 0 : gf_sg_vrml_mf_append(info.far_ptr, info.fieldType, &slot);
608 0 : storage_parse_sf(slot, sftype, val+1);
609 0 : sep[0] = '\'';
610 0 : opt = sep+1;
611 : }
612 : }
613 0 : gf_node_changed(storage->storageList.vals[i].node, &info);
614 : }
615 0 : gf_free(section);
616 : }
617 :
618 2 : char *storage_serialize_sf(void *ptr, u32 fieldType)
619 : {
620 : char szVal[50];
621 2 : switch (fieldType) {
622 0 : case GF_SG_VRML_SFBOOL:
623 0 : sprintf(szVal, "%d", *((SFBool *)ptr) ? 1 : 0);
624 0 : return gf_strdup(szVal);
625 0 : case GF_SG_VRML_SFINT32:
626 0 : sprintf(szVal, "%d", *((SFInt32 *)ptr) );
627 0 : return gf_strdup(szVal);
628 0 : case GF_SG_VRML_SFTIME:
629 0 : sprintf(szVal, "%g", *((SFTime *)ptr) );
630 0 : return gf_strdup(szVal);
631 0 : case GF_SG_VRML_SFFLOAT:
632 0 : sprintf(szVal, "%g", FIX2FLT( *((SFFloat *)ptr) ) );
633 0 : return gf_strdup(szVal);
634 2 : case GF_SG_VRML_SFVEC2F:
635 2 : sprintf(szVal, "%g %g", FIX2FLT( ((SFVec2f *)ptr)->x), FIX2FLT( ((SFVec2f *)ptr)->y) );
636 2 : return gf_strdup(szVal);
637 0 : case GF_SG_VRML_SFVEC3F:
638 0 : sprintf(szVal, "%g %g %g", FIX2FLT( ((SFVec3f *)ptr)->x), FIX2FLT( ((SFVec3f *)ptr)->y) , FIX2FLT( ((SFVec3f *)ptr)->z) );
639 0 : return gf_strdup(szVal);
640 0 : case GF_SG_VRML_SFSTRING:
641 0 : return gf_strdup( ((SFString *)ptr)->buffer ? ((SFString *)ptr)->buffer : "");
642 :
643 : default:
644 : break;
645 : }
646 : return NULL;
647 : }
648 :
649 1 : void gf_storage_save(M_Storage *storage)
650 : {
651 : char szID[20];
652 : u32 i, j;
653 1 : char *section = storage_get_section(storage);
654 1 : if (!section) return;
655 :
656 1 : gf_opts_del_section(section);
657 :
658 1 : if (storage->expireAfter) {
659 : u32 sec, frac;
660 : char szNTP[100];
661 1 : gf_net_get_ntp(&sec, &frac);
662 1 : sec += storage->expireAfter;
663 : sprintf(szNTP, "%u", sec);
664 1 : gf_opts_set_key(section, "expireAfterNTP", szNTP);
665 : } else {
666 0 : gf_opts_set_key(section, "expireAfterNTP", "0");
667 : }
668 :
669 2 : for (i=0; i<storage->storageList.count; i++) {
670 : char *val;
671 : GF_FieldInfo info;
672 : sprintf(szID, "%d", i);
673 :
674 2 : if (!storage->storageList.vals[i].node) break;
675 2 : if (gf_node_get_field(storage->storageList.vals[i].node, storage->storageList.vals[i].fieldIndex, &info) != GF_OK) break;
676 :
677 2 : if (gf_sg_vrml_is_sf_field(info.fieldType)) {
678 2 : val = storage_serialize_sf(info.far_ptr, info.fieldType);
679 : } else {
680 : //u32 sftype = gf_sg_vrml_get_sf_type(info.fieldType);
681 : val = NULL;
682 0 : for (j=0; j<((GenMFField *)info.far_ptr)->count; j++) {
683 : char *slotval;
684 : void *slot;
685 0 : if (gf_sg_vrml_mf_get_item(info.far_ptr, info.fieldType, &slot, j) != GF_OK) break;
686 0 : slotval = storage_serialize_sf(info.far_ptr, info.fieldType);
687 0 : if (!slotval) break;
688 0 : if (val) {
689 0 : val = (char *)gf_realloc(val, strlen(val) + 3 + strlen((const char *)slot));
690 : } else {
691 0 : val = (char *)gf_malloc(3 + strlen((const char *)slot));
692 0 : val[0] = 0;
693 : }
694 : strcat(val, "'");
695 : strcat(val, slotval);
696 : strcat(val, "'");
697 0 : gf_free(slot);
698 : }
699 : }
700 2 : if (val) {
701 2 : gf_opts_set_key(section, szID, val);
702 2 : gf_free(val);
703 : }
704 : }
705 1 : gf_free(section);
706 : }
707 :
708 :
709 40 : static void gf_storage_traverse(GF_Node *n, void *rs, Bool is_destroy)
710 : {
711 40 : if (is_destroy) {
712 1 : GF_Scene *scene = (GF_Scene *)gf_node_get_private(n);
713 1 : GF_SceneNamespace *scene_ns = scene->root_od->scene_ns;
714 2 : while (scene->root_od->parentscene) {
715 0 : if (scene->root_od->parentscene->root_od->scene_ns != scene_ns)
716 : break;
717 : scene = scene->root_od->parentscene;
718 : }
719 1 : gf_list_del_item(scene->storages, n);
720 : }
721 40 : }
722 :
723 0 : static void on_force_restore(GF_Node *n, struct _route *_route)
724 : {
725 0 : if (n) gf_storage_load((M_Storage *)n);
726 0 : }
727 0 : static void on_force_save(GF_Node *n, struct _route *_route)
728 : {
729 0 : if (n) gf_storage_save((M_Storage *)n);
730 0 : }
731 :
732 1 : void gf_scene_init_storage(GF_Scene *scene, GF_Node *node)
733 : {
734 : GF_SceneNamespace *scene_ns;
735 : M_Storage *storage = (M_Storage *) node;
736 :
737 1 : if (!storage->name.buffer || !strlen(storage->name.buffer) ) return;
738 1 : if (!storage->storageList.count) return;
739 :
740 1 : storage->on_forceSave = on_force_save;
741 1 : storage->on_forceRestore = on_force_restore;
742 1 : gf_node_set_callback_function(node, gf_storage_traverse);
743 1 : gf_node_set_private(node, scene);
744 :
745 1 : scene_ns = scene->root_od->scene_ns;
746 2 : while (scene->root_od->parentscene) {
747 0 : if (scene->root_od->parentscene->root_od->scene_ns != scene_ns)
748 : break;
749 : scene = scene->root_od->parentscene;
750 : }
751 :
752 1 : gf_list_add(scene->storages, node);
753 1 : if (storage->_auto) gf_storage_load(storage);
754 :
755 : #ifdef GPAC_ENABLE_COVERAGE
756 1 : if (gf_sys_is_cov_mode()) {
757 : Bool aptr;
758 : on_force_save(NULL, NULL);
759 : on_force_restore(NULL, NULL);
760 : storage_parse_sf(&aptr, GF_SG_VRML_SFBOOL, "true");
761 :
762 : }
763 : #endif
764 : }
765 :
766 : #endif // GPAC_DISABLE_VRML
767 :
768 3157 : GF_Node *gf_scene_get_keynav(GF_SceneGraph *sg, GF_Node *sensor)
769 : {
770 : #ifndef GPAC_DISABLE_VRML
771 : u32 i, count;
772 3157 : GF_Scene *scene = (GF_Scene *)gf_sg_get_private(sg);
773 3157 : if (!scene) return NULL;
774 3157 : if (!sensor) return (GF_Node *)gf_list_get(scene->keynavigators, 0);
775 :
776 2089 : count = gf_list_count(scene->keynavigators);
777 2113 : for (i=0; i<count; i++) {
778 44 : M_KeyNavigator *kn = (M_KeyNavigator *)gf_list_get(scene->keynavigators, i);
779 44 : if (kn->sensor==sensor) return (GF_Node *) kn;
780 : }
781 : #endif
782 : return NULL;
783 : }
784 :
785 :
|