Beispiel #1
0
GF_ClientService *gf_term_service_new(GF_Terminal *term, struct _od_manager *owner, const char *url, const char *parent_url, GF_Err *ret_code)
{
	GF_DownloadSession *download_session = NULL;
	char *sURL;
	char *mime;
	GF_ClientService *serv;
	GF_InputService *ifce = gf_term_can_handle_service(term, url, parent_url, 0, &sURL, ret_code, &download_session, &mime);
	if (!ifce) {
		if (owner->subscene) gf_scene_notify_event(owner->subscene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, *ret_code, GF_FALSE);
		return NULL;
	}
	GF_SAFEALLOC(serv, GF_ClientService);
	if (!serv) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Failed to allocate network service\n"));
		return NULL;
	}
	serv->term = term;
	serv->owner = owner;
	serv->ifce = ifce;
	serv->url = sURL;
	serv->mime = mime;
	serv->Clocks = gf_list_new();
	serv->dnloads = gf_list_new();
	serv->pending_service_session = download_session;

	gf_term_lock_media_queue(term, 1);
	gf_list_add(term->net_services, serv);
	gf_term_lock_media_queue(term, 0);


	serv->fn_connect_ack = term_on_connect;
	serv->fn_disconnect_ack = term_on_disconnect;
	serv->fn_command = term_on_command;
	serv->fn_data_packet = term_on_data_packet;
	serv->fn_add_media = term_on_media_add;
	return serv;
}
Beispiel #2
0
static void term_on_connect(void *user_priv, GF_ClientService *service, LPNETCHANNEL netch, GF_Err err)
{
	GF_Channel *ch;
	GF_ObjectManager *root;
	GET_TERM();

	GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] %s connection ACK received from %s - %s\n", netch ? "Channel" : "Service", service->url, gf_error_to_string(err) ));

	root = service->owner;
	if (root && (root->net_service != service)) {
		gf_term_message(term, service->url, "Incompatible module type", GF_SERVICE_ERROR);
		return;
	}
	/*this is service connection*/
	if (!netch) {
		gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_SETUP_DONE);
		if (err) {
			char msg[5000];
			snprintf(msg, sizeof(msg)-1, "Cannot open %s", service->url);
			gf_term_message(term, service->url, msg, err);

			gf_term_service_media_event(service->owner, GF_EVENT_ERROR);

			/*destroy service only if attached*/
			if (root) {
				gf_term_lock_media_queue(term, 1);
				service->ifce->CloseService(service->ifce);
				root->net_service = NULL;
				if (service->owner && service->nb_odm_users) service->nb_odm_users--;
				service->owner = NULL;
				/*depends on module: some module could forget to call gf_term_on_disconnect */
				if ( gf_list_del_item(term->net_services, service) >= 0) {
					/*and queue for destroy*/
					gf_list_add(term->net_services_to_remove, service);
				}
				gf_term_lock_media_queue(term, 0);

				if (!root->parentscene) {
					GF_Event evt;
					evt.type = GF_EVENT_CONNECT;
					evt.connect.is_connected = 0;
					gf_term_send_event(term, &evt);
				} else {
					if (root->subscene) gf_scene_notify_event(root->subscene, GF_EVENT_SCENE_ATTACHED, NULL, NULL, err);
					/*try to reinsert OD for VRML/X3D with multiple URLs:
					1- first remove from parent scene without destroying object, this will trigger a re-setup
					if other URLs are present
					2- then destroy object*/
					gf_scene_remove_object(root->parentscene, root, 0);
					gf_odm_disconnect(root, 1);
				}
				return;
			}
		}

		if (!root) {
			/*channel service connect*/
			u32 i;
			GF_ChannelSetup *cs;
			GF_List *ODs;

			if (!gf_list_count(term->channels_pending)) {
				return;
			}
			ODs = gf_list_new();
			gf_term_lock_net(term, 1);
			i=0;
			while ((cs = (GF_ChannelSetup*)gf_list_enum(term->channels_pending, &i))) {
				if (cs->ch->service != service) continue;
				gf_list_rem(term->channels_pending, i-1);
				i--;
				/*even if error do setup (channel needs to be deleted)*/
				if (gf_odm_post_es_setup(cs->ch, cs->dec, err) == GF_OK) {
					if (cs->ch->odm && (gf_list_find(ODs, cs->ch->odm)==-1) ) gf_list_add(ODs, cs->ch->odm);
				}
				gf_free(cs);
			}
			gf_term_lock_net(term, 0);
			/*finally setup all ODs concerned (we do this later in case of scalability)*/
			while (gf_list_count(ODs)) {
				GF_ObjectManager *odm = (GF_ObjectManager*)gf_list_get(ODs, 0);
				gf_list_rem(ODs, 0);
				/*force re-setup*/
				gf_scene_setup_object(odm->parentscene, odm);
			}
			gf_list_del(ODs);
		} else {
			/*setup od*/
			gf_odm_setup_entry_point(root, service->url);
		}
		/*load cache if requested*/
		if (!err && term->enable_cache) {
			err = gf_term_service_cache_load(service);
			/*not a fatal error*/
			if (err) gf_term_message(term, "GPAC Cache", "Cannot load cache", err);
		}
		return;
	}

	/*this is channel connection*/
	ch = gf_term_get_channel(service, netch);
	if (!ch) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Channel connection ACK error: channel not found\n"));
		return;
	}

	/*confirm channel connection even if error - this allow playback of objects even if not all streams are setup*/
	gf_term_lock_net(term, 1);
	gf_es_on_connect(ch);
	gf_term_lock_net(term, 0);

	if (err && ((err!=GF_STREAM_NOT_FOUND) || (ch->esd->decoderConfig->streamType!=GF_STREAM_INTERACT))) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Terminal] Channel %d connection error: %s\n", ch->esd->ESID, gf_error_to_string(err) ));
		ch->es_state = GF_ESM_ES_UNAVAILABLE;
/*		return;*/
	}

	if (ch->odm->mo) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Channel %d connected - %d objects opened\n", ch->esd->ESID, ch->odm->mo->num_open ));
	} else {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Terminal] Channel %d connected - not attached to the scene\n", ch->esd->ESID));
	}
	/*Plays request are skiped until all channels are connected. We send a PLAY on the objecy in case
		1-OD user has requested a play
		2-this is a channel of the root OD
	*/
	if ( (ch->odm->mo && ch->odm->mo->num_open)
		|| !ch->odm->parentscene
	) {
		gf_odm_start(ch->odm, 0);
	}
#if 0
	else if (ch->odm->codec && ch->odm->codec->ck && ch->odm->codec->ck->no_time_ctrl) {
		gf_odm_play(ch->odm);
	}
#endif
}
Beispiel #3
0
void gf_inline_on_modified(GF_Node *node)
{
	u32 ODID;
	GF_MediaObject *mo;
	M_Inline *pInline = (M_Inline *) node;
	GF_Scene *scene = (GF_Scene *)gf_node_get_private(node);

	ODID = gf_mo_get_od_id(&pInline->url);
	if (scene) {
		mo = (scene->root_od) ? scene->root_od->mo : NULL;

		/*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/
		if (mo) {
			Bool changed = 1;
			if (ODID != GF_MEDIA_EXTERNAL_ID) {
				if (ODID && (ODID==scene->root_od->OD->objectDescriptorID)) changed = 0;
			} else {
				if (gf_mo_is_same_url(mo, &pInline->url, NULL, 0) ) changed = 0;
			}
			if (mo->num_open) {
				if (!changed) return;

				gf_scene_notify_event(scene, GF_EVENT_UNLOAD, node, NULL, GF_OK);
				gf_node_dirty_parents(node);
				gf_list_del_item(mo->nodes, node);

				/*reset the scene pointer as it may get destroyed*/
				switch (gf_node_get_tag(node)) {
				case TAG_MPEG4_Inline:
#ifndef GPAC_DISABLE_X3D
				case TAG_X3D_Inline:
#endif
					gf_node_set_private(node, NULL);
					break;
				}

				mo->num_open --;
				if (!mo->num_open) {
					if (ODID == GF_MEDIA_EXTERNAL_ID) {
						GF_Scene *parent = scene->root_od->parentscene;
						/*!!! THIS WILL DESTROY THE INLINE SCENE OBJECT !!!*/
						gf_odm_disconnect(scene->root_od, 1);
						/*and force removal of the media object*/
						if (parent) {
							if (gf_list_del_item(parent->scene_objects, mo)>=0) {
								gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL);
								gf_list_del(mo->nodes);
								gf_free(mo);
							}
						}
					} else {
						gf_term_lock_media_queue(scene->root_od->term, 1);
						
						/*external media are completely unloaded*/
						if (scene->root_od->OD->objectDescriptorID==GF_MEDIA_EXTERNAL_ID) {
							scene->root_od->action_type = GF_ODM_ACTION_DELETE;
						} else {
							scene->root_od->action_type = GF_ODM_ACTION_STOP;
						}
						if (gf_list_find(scene->root_od->term->media_queue, scene->root_od)<0)
							gf_list_add(scene->root_od->term->media_queue, scene->root_od);

						gf_term_lock_media_queue(scene->root_od->term, 0);
					}
				}
			}
		}
	} else {
		gf_node_dirty_parents(node);
	}
	if (ODID) gf_inline_set_scene(pInline);
}
Beispiel #4
0
static void gf_inline_traverse(GF_Node *n, void *rs, Bool is_destroy)
{
	MFURL *current_url;
	GF_Scene *scene = (GF_Scene *)gf_node_get_private(n);

	if (is_destroy) {
		GF_MediaObject *mo;
		if (!scene) return;
		mo = scene->root_od ? scene->root_od->mo : NULL;

		gf_scene_notify_event(scene, GF_EVENT_UNLOAD, n, NULL, GF_OK);
		if (!mo) return;
		gf_list_del_item(mo->nodes, n);

		/*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/
		if (mo->num_open) {
			mo->num_open --;
			if (!mo->num_open) {
				gf_term_lock_media_queue(scene->root_od->term, 1);

				/*this is unspecified in the spec: whenever an inline not using the 
				OD framework is destroyed, destroy the associated resource*/
				if (mo->OD_ID == GF_MEDIA_EXTERNAL_ID) {
					/*get parent scene and remove MediaObject in case the ressource
					gets re-requested later on*/
					GF_Scene *parent_scene = (GF_Scene *)gf_sg_get_private(gf_node_get_graph((GF_Node *) n) );
					if (gf_list_del_item(parent_scene->scene_objects, mo)>=0) {
						gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL);
						gf_list_del(mo->nodes);
						if (mo->odm) mo->odm->mo = NULL;
						gf_free(mo);
					}
					scene->root_od->action_type = GF_ODM_ACTION_DELETE;
					gf_list_add(scene->root_od->term->media_queue, scene->root_od);
				} else {
					scene->root_od->action_type = GF_ODM_ACTION_SCENE_DISCONNECT;
					gf_list_add(scene->root_od->term->media_queue, scene->root_od);
				}
				gf_term_lock_media_queue(scene->root_od->term, 0);
			}
		}
		return;
	}


	//if no private scene is associated	get the node parent graph, retrieve the IS and find the OD
	if (!scene) {
		M_Inline *inl = (M_Inline *)n;
		gf_inline_set_scene(inl);
		scene = (GF_Scene *)gf_node_get_private(n);
		if (!scene) {
			/*just like protos, we must invalidate parent graph until attached*/
			if (inl->url.count) {
				if (!inl->url.vals[0].OD_ID && (!inl->url.vals[0].url || !strlen(inl->url.vals[0].url) ) ) {
					gf_sg_vrml_mf_reset(&inl->url, GF_SG_VRML_MFURL);
				} else {
					gf_node_dirty_set(n, 0, 1);
				}
			}
			return;
		}
	}

	gf_inline_check_restart(scene);

	/*if we need to restart, shutdown graph and do it*/
	if (scene->needs_restart) {
		/*special case: scene change*/
		if (scene->needs_restart==2) {
			scene->needs_restart = 0;
			gf_inline_on_modified(n);
			return;
		}

		scene->needs_restart = 0;
		gf_term_lock_media_queue(scene->root_od->term, 1);
		scene->root_od->action_type = GF_ODM_ACTION_SCENE_INLINE_RESTART;
		gf_list_add(scene->root_od->term->media_queue, scene->root_od);
		gf_term_lock_media_queue(scene->root_od->term, 0);

		gf_node_dirty_set(n, 0, 1);
		return;
	} 
	
	/*if not attached return (attaching the graph cannot be done in render since render is not called while unattached :) */
	if (!scene->graph_attached) {
		/*just like protos, we must invalidate parent graph until attached*/
		gf_node_dirty_set(n, 0, 1);
		return;
	}
	/*clear dirty flags for any sub-inlines, bitmaps or protos*/
	gf_node_dirty_clear(n, 0);

	current_url = scene->current_url;
	scene->current_url = & ((M_Inline*)n)->url;
	gf_sc_traverse_subscene(scene->root_od->term->compositor, n, scene->graph, rs);
	scene->current_url = current_url;
}
Beispiel #5
0
void gf_inline_on_modified(GF_Node *node)
{
	u32 ODID;
	GF_MediaObject *mo;
	M_Inline *pInline = (M_Inline *) node;
	GF_Scene *scene = (GF_Scene *)gf_node_get_private(node);

	ODID = gf_mo_get_od_id(&pInline->url);
	if (scene) {
		mo = (scene->root_od) ? scene->root_od->mo : NULL;

		/*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/
		if (mo) {
			Bool changed = GF_TRUE;
			if (ODID != GF_MEDIA_EXTERNAL_ID) {
				if (ODID && (ODID==scene->root_od->OD->objectDescriptorID)) changed = GF_FALSE;
			} else {
				if (gf_mo_is_same_url(mo, &pInline->url, NULL, 0) ) changed = GF_FALSE;
			}
			if (mo->num_open) {
				if (!changed) return;

				gf_scene_notify_event(scene, GF_EVENT_UNLOAD, node, NULL, GF_OK, GF_TRUE);
				gf_node_dirty_parents(node);
				gf_mo_event_target_remove_by_node(mo, node);

				/*reset the scene pointer as it may get destroyed*/
				switch (gf_node_get_tag(node)) {
				case TAG_MPEG4_Inline:
#ifndef GPAC_DISABLE_X3D
				case TAG_X3D_Inline:
#endif
					gf_node_set_private(node, NULL);
					break;
				}

				mo->num_open --;
				if (!mo->num_open) {
					if (ODID == GF_MEDIA_EXTERNAL_ID) {
						GF_Scene *parent = scene->root_od->parentscene;
						/*!!! THIS WILL DESTROY THE INLINE SCENE OBJECT !!!*/
						gf_odm_disconnect(scene->root_od, GF_TRUE);
						/*and force removal of the media object*/
						if (parent) {
							if (gf_list_del_item(parent->scene_objects, mo)>=0) {
								gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL);
								gf_mo_del(mo);
							}
						}
					} else {
						gf_term_lock_media_queue(scene->root_od->term, GF_TRUE);

						/*external media are completely unloaded, except addons which are only declared once */
						if (!scene->root_od->addon && (scene->root_od->OD->objectDescriptorID==GF_MEDIA_EXTERNAL_ID)) {
							scene->root_od->action_type = GF_ODM_ACTION_DELETE;
						} else {
							scene->root_od->action_type = GF_ODM_ACTION_STOP;
						}
						if (gf_list_find(scene->root_od->term->media_queue, scene->root_od)<0)
							gf_list_add(scene->root_od->term->media_queue, scene->root_od);

						gf_term_lock_media_queue(scene->root_od->term, GF_FALSE);
					}
				}
			}
		}
	}
	/*force a redraw and load scene at next pass - we cannot load the scene now because
		- we can be in a JS call (eg JS mutex blocked)
		- locating scene objects matching the new url needs exclusive access to the MediaObject list, achieved with the term net mutex
		- another service may already be setting up objects (eg exclusive access to the net mutex grabbed), which can trigger event forwarding
		- some event forwarders may request JS context (eg access to JS mutex)

		In such a case we would end up in a deadlock - this needs urgent fixing ...
	*/
	if (ODID) {
		/*if no parent we must process the url change as we may not be traversed later on (not in the scene tree)*/
		if (gf_node_get_parent(node, 0)==NULL) {
			gf_inline_set_scene(pInline);
		} else {
			gf_node_dirty_parents(node);
		}
	}
}
Beispiel #6
0
static void svg_traverse_svg(GF_Node *node, void *rs, Bool is_destroy)
{
	Bool rootmost_svg, send_resize;
	u32 viewport_color;
	SVGsvgStack *stack;
	GF_Matrix2D backup_matrix, vb_bck;
#ifndef GPAC_DISABLE_3D
	GF_Matrix bck_mx;
#endif
	Bool is_dirty;
	GF_IRect top_clip;
	SFVec2f prev_vp;
	SVGPropertiesPointers backup_props, *prev_props;
	u32 backup_flags;
	Bool invalidate_flag;
	u32 styling_size = sizeof(SVGPropertiesPointers);
	GF_TraverseState *tr_state = (GF_TraverseState *) rs;
	SVGAllAttributes all_atts;
	stack = gf_node_get_private(node);

	if (is_destroy) {
		if (stack->svg_props) {
			gf_svg_properties_reset_pointers(stack->svg_props);
			gf_free(stack->svg_props);
		}
		gf_sc_check_focus_upon_destroy(node);
		if (stack->vp_fill) drawable_del(stack->vp_fill);
		gf_free(stack);
		return;
	}

	prev_props = tr_state->svg_props;
	/*SVG props not set: we are either the root-most <svg> of the compositor
	or an <svg> inside an <animation>*/
	if (!tr_state->svg_props) {
		tr_state->svg_props = stack->svg_props;
		if (!tr_state->svg_props) return;
	}
	
	gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
	if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) {
		tr_state->svg_props = prev_props;
		return;
	}

	/*enable or disable navigation*/
	tr_state->visual->compositor->navigation_disabled = (all_atts.zoomAndPan && *all_atts.zoomAndPan == SVG_ZOOMANDPAN_DISABLE) ? 1 : 0;

	if (compositor_svg_is_display_off(tr_state->svg_props)) {
		memcpy(tr_state->svg_props, &backup_props, styling_size);
		tr_state->svg_flags = backup_flags;
		return;
	}

	top_clip = tr_state->visual->top_clipper;
	gf_mx2d_copy(backup_matrix, tr_state->transform);
	gf_mx2d_copy(vb_bck, tr_state->vb_transform);

#ifndef GPAC_DISABLE_3D
	if (tr_state->visual->type_3d) gf_mx_copy(bck_mx, tr_state->model_matrix);
#endif
	
	invalidate_flag = tr_state->invalidate_all;

	is_dirty = gf_node_dirty_get(node);
	if (is_dirty  & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node);
	gf_node_dirty_clear(node, 0);

	send_resize = 0;
	if ((stack->parent_vp.x != tr_state->vp_size.x) || (stack->parent_vp.y != tr_state->vp_size.y)) {
		is_dirty = 1;
		send_resize = 1;
	}

	if (is_dirty || tr_state->visual->compositor->recompute_ar) {
		svg_recompute_viewport_transformation(node, stack, tr_state, &all_atts);
	}

	gf_mx2d_copy(tr_state->vb_transform, stack->viewbox_mx);

	rootmost_svg = (stack->root_svg && !tr_state->parent_anim_atts) ? 1 : 0;	
	if (tr_state->traversing_mode == TRAVERSE_SORT) {
		SVG_Paint *vp_fill = NULL;
		Fixed vp_opacity;

		if (tr_state->parent_anim_atts) {
			vp_fill = tr_state->parent_anim_atts->viewport_fill;
			vp_opacity = tr_state->parent_anim_atts->viewport_fill_opacity ? tr_state->parent_anim_atts->viewport_fill_opacity->value : FIX_ONE;
		} else {
			vp_fill = tr_state->svg_props->viewport_fill;
			vp_opacity = tr_state->svg_props->viewport_fill_opacity ? tr_state->svg_props->viewport_fill_opacity->value : FIX_ONE;
		} 

		if (vp_fill && (vp_fill->type != SVG_PAINT_NONE) && vp_opacity) {
			Bool col_dirty = 0;
			viewport_color = GF_COL_ARGB_FIXED(vp_opacity, vp_fill->color.red, vp_fill->color.green, vp_fill->color.blue);

			if (stack->prev_color != viewport_color) {
				stack->prev_color = viewport_color;
				col_dirty = 1;
			}

			if (!rootmost_svg) {
				DrawableContext *ctx;
				Fixed width = tr_state->parent_anim_atts->width->value;
				Fixed height = tr_state->parent_anim_atts->height->value;

				if (!stack->vp_fill) {
					stack->vp_fill = drawable_new();
					stack->vp_fill->node = node;
				}
				if ((width != stack->vp_fill->path->bbox.width) || (height != stack->vp_fill->path->bbox.height)) {
					drawable_reset_path(stack->vp_fill);
					gf_path_add_rect(stack->vp_fill->path, 0, 0, width, -height);
				}

				ctx = drawable_init_context_svg(stack->vp_fill, tr_state);
				if (ctx) {
					ctx->flags &= ~CTX_IS_TRANSPARENT;
					ctx->aspect.pen_props.width = 0;
					ctx->aspect.fill_color = viewport_color;
					ctx->aspect.fill_texture = NULL;
					if (col_dirty) ctx->flags |= CTX_APP_DIRTY;
					drawable_finalize_sort(ctx, tr_state, NULL);
				}

			} else if (col_dirty) {
				tr_state->visual->compositor->back_color = viewport_color;
				/*invalidate the entire visual*/
				tr_state->invalidate_all = 1;
			}
		}
	}


	if (!stack->root_svg && (all_atts.x || all_atts.y)) 
		gf_mx2d_add_translation(&tr_state->vb_transform, all_atts.x->value, all_atts.y->value);

#ifndef GPAC_DISABLE_3D
	if (tr_state->visual->type_3d) {
		if (tr_state->traversing_mode==TRAVERSE_SORT) {
			GF_Matrix tmp;
			visual_3d_matrix_push(tr_state->visual);

			gf_mx_from_mx2d(&tmp, &tr_state->vb_transform);
			visual_3d_matrix_add(tr_state->visual, tmp.m);
		} else {
			gf_mx_add_matrix_2d(&tr_state->model_matrix, &tr_state->vb_transform);
		}
	} else 
#endif
	{
		gf_mx2d_pre_multiply(&tr_state->transform, &tr_state->vb_transform);
	}

	/*store VP and move it to current VP (eg, the one used to compute the vb_transform)*/
	prev_vp = tr_state->vp_size;
	tr_state->vp_size = stack->vp;

	/*the event may trigger scripts which may delete nodes / modify the scene. We therefore send the resize event 
	before traversing the scene*/
	if (send_resize) {
		GF_DOM_Event evt;
		memset(&evt, 0, sizeof(GF_DOM_Event));
		evt.bubbles = 1;
		evt.type = GF_EVENT_RESIZE;
		gf_dom_event_fire(node, &evt);
	}
	if ((stack->vp.x != prev_vp.x) || (stack->vp.y != prev_vp.y)) {
		GF_Scene *scene = node->sgprivate->scenegraph->userpriv;
		
		if (scene) { 
			GF_DOM_Event evt;
			memset(&evt, 0, sizeof(GF_DOM_Event));
			evt.bubbles = 0;
			evt.screen_rect.width = stack->vpw;
			evt.screen_rect.height = stack->vph;
			evt.screen_rect.x = stack->dx;
			evt.screen_rect.y = stack->dy;
			evt.prev_translate.x = stack->vp.x;
			evt.prev_translate.y = stack->vp.y;
			evt.type = GF_EVENT_VP_RESIZE;
			gf_scene_notify_event(scene, 0, NULL, &evt, GF_OK);
		}
	}

	if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
		gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL);
	} else {
		compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state);
	}
	tr_state->vp_size = prev_vp;

#ifndef GPAC_DISABLE_3D
	if (tr_state->visual->type_3d) {
		if (tr_state->traversing_mode==TRAVERSE_SORT) visual_3d_matrix_pop(tr_state->visual);
		gf_mx_copy(tr_state->model_matrix, bck_mx);
	}
#endif
	gf_mx2d_copy(tr_state->transform, backup_matrix);  
	gf_mx2d_copy(tr_state->vb_transform, vb_bck);
	memcpy(tr_state->svg_props, &backup_props, styling_size);
	tr_state->svg_flags = backup_flags;
	tr_state->visual->top_clipper = top_clip;
	if (!stack->root_svg) {
		tr_state->invalidate_all = invalidate_flag;
	}
	tr_state->svg_props = prev_props;
}