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; }
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 }
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); }
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; }
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); } } }
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; }