LCOV - code coverage report
Current view: top level - compositor - svg_media.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 230 466 49.4 %
Date: 2021-04-29 23:48:07 Functions: 18 24 75.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Cyril Concolato - Jean le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2005-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Compositor sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include "visual_manager.h"
      27             : 
      28             : #ifndef GPAC_DISABLE_SVG
      29             : #include "nodes_stacks.h"
      30             : 
      31             : static void svg_audio_smil_evaluate_ex(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status, GF_Node *audio, GF_Node *video);
      32             : static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGPropertiesPointers *props);
      33             : 
      34             : 
      35             : 
      36        2389 : static Bool svg_video_get_transform_behavior(GF_TraverseState *tr_state, SVGAllAttributes *atts, Fixed *cx, Fixed *cy, Fixed *angle)
      37             : {
      38             :         SFVec2f pt;
      39        2389 :         if (!atts->transformBehavior) return GF_FALSE;
      40           0 :         if (*atts->transformBehavior == SVG_TRANSFORMBEHAVIOR_GEOMETRIC)
      41             :                 return GF_FALSE;
      42             : 
      43           0 :         pt.x = atts->x ? atts->x->value : 0;
      44           0 :         pt.y = atts->y ? atts->y->value : 0;
      45           0 :         gf_mx2d_apply_point(&tr_state->transform, &pt);
      46           0 :         *cx = pt.x;
      47           0 :         *cy = pt.y;
      48             : 
      49           0 :         *angle = 0;
      50           0 :         switch (*atts->transformBehavior) {
      51             :         case SVG_TRANSFORMBEHAVIOR_PINNED:
      52             :                 break;
      53           0 :         case SVG_TRANSFORMBEHAVIOR_PINNED180:
      54           0 :                 *angle = GF_PI;
      55           0 :                 break;
      56           0 :         case SVG_TRANSFORMBEHAVIOR_PINNED270:
      57           0 :                 *angle = -GF_PI/2;
      58           0 :                 break;
      59           0 :         case SVG_TRANSFORMBEHAVIOR_PINNED90:
      60           0 :                 *angle = GF_PI/2;
      61           0 :                 break;
      62             :         }
      63             :         return GF_TRUE;
      64             : }
      65             : 
      66             : 
      67         121 : static void SVG_Draw_bitmap(GF_TraverseState *tr_state)
      68             : {
      69         121 :         DrawableContext *ctx = tr_state->ctx;
      70         121 :         if (!tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx)) {
      71           0 :                 visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state);
      72             :         }
      73         121 : }
      74             : 
      75           3 : static void SVG_Build_Bitmap_Graph(SVG_video_stack *stack, GF_TraverseState *tr_state)
      76             : {
      77             :         u32 tag;
      78             :         GF_Rect rc, new_rc;
      79             :         Fixed x, y, width, height, txwidth, txheight;
      80             :         Fixed rectx, recty, rectwidth, rectheight;
      81             :         SVGAllAttributes atts;
      82             :         SVG_PreserveAspectRatio pAR;
      83           3 :         SVG_Element *e = (SVG_Element *)stack->drawable->node;
      84             : 
      85           3 :         gf_svg_flatten_attributes(e, &atts);
      86             : 
      87           3 :         tag = gf_node_get_tag(stack->drawable->node);
      88           3 :         switch (tag) {
      89           3 :         case TAG_SVG_image:
      90             :         case TAG_SVG_video:
      91           3 :                 x = (atts.x ? atts.x->value : 0);
      92           3 :                 y = (atts.y ? atts.y->value : 0);
      93           3 :                 width = (atts.width ? atts.width->value : 0);
      94           3 :                 height = (atts.height ? atts.height->value : 0);
      95             :                 break;
      96             :         default:
      97           0 :                 return;
      98             :         }
      99             : 
     100           3 :         if (!width || !height) return;
     101             : 
     102           3 :         txheight = INT2FIX(stack->txh.height);
     103           3 :         txwidth = INT2FIX(stack->txh.width);
     104             : 
     105           3 :         if (!txwidth || !txheight) return;
     106             : 
     107           3 :         if (!atts.preserveAspectRatio) {
     108             :                 pAR.defer = GF_FALSE;
     109             :                 pAR.meetOrSlice = SVG_MEETORSLICE_MEET;
     110             :                 pAR.align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
     111             :         } else {
     112           0 :                 pAR = *atts.preserveAspectRatio;
     113             :         }
     114           3 :         if (pAR.defer) {
     115             :                 /* TODO */
     116             :                 rectwidth = width;
     117             :                 rectheight = height;
     118           0 :                 rectx = x+rectwidth/2;
     119           0 :                 recty = y+rectheight/2;
     120             :         } else {
     121             : 
     122           3 :                 if (pAR.align==SVG_PRESERVEASPECTRATIO_NONE) {
     123             :                         rectwidth = width;
     124             :                         rectheight = height;
     125           0 :                         rectx = x+rectwidth/2;
     126           0 :                         recty = y+rectheight/2;
     127             :                 } else {
     128             :                         Fixed scale, scale_w, scale_h;
     129           3 :                         scale_w = gf_divfix(width, txwidth);
     130           3 :                         scale_h = gf_divfix(height, txheight);
     131           3 :                         if (pAR.meetOrSlice==SVG_MEETORSLICE_MEET) {
     132           3 :                                 if (scale_w > scale_h) {
     133             :                                         scale = scale_h;
     134           2 :                                         rectwidth = gf_mulfix(txwidth, scale);
     135             :                                         rectheight = height;
     136             :                                 } else {
     137             :                                         scale = scale_w;
     138             :                                         rectwidth = width;
     139           1 :                                         rectheight = gf_mulfix(txheight, scale);
     140             :                                 }
     141             :                         } else {
     142           0 :                                 if (scale_w < scale_h) {
     143             :                                         scale = scale_h;
     144           0 :                                         rectwidth = gf_mulfix(txwidth, scale);
     145             :                                         rectheight = height;
     146             :                                 } else {
     147             :                                         scale = scale_w;
     148             :                                         rectwidth = width;
     149           0 :                                         rectheight = gf_mulfix(txheight, scale);
     150             :                                 }
     151             :                         }
     152             : 
     153           3 :                         rectx = x + rectwidth/2;
     154           3 :                         recty = y + rectheight/2;
     155           3 :                         switch (pAR.align) {
     156             :                         case SVG_PRESERVEASPECTRATIO_XMINYMIN:
     157             :                                 break;
     158           0 :                         case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
     159           0 :                                 rectx += (width - rectwidth)/ 2;
     160           0 :                                 break;
     161           0 :                         case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
     162           0 :                                 rectx += width - rectwidth;
     163           0 :                                 break;
     164           0 :                         case SVG_PRESERVEASPECTRATIO_XMINYMID:
     165           0 :                                 recty += (height - rectheight)/ 2;
     166           0 :                                 break;
     167           3 :                         case SVG_PRESERVEASPECTRATIO_XMIDYMID:
     168           3 :                                 rectx += (width - rectwidth)/ 2;
     169           3 :                                 recty += (height - rectheight) / 2;
     170           3 :                                 break;
     171           0 :                         case SVG_PRESERVEASPECTRATIO_XMAXYMID:
     172           0 :                                 rectx += width - rectwidth;
     173           0 :                                 recty += ( txheight - rectheight) / 2;
     174           0 :                                 break;
     175           0 :                         case SVG_PRESERVEASPECTRATIO_XMINYMAX:
     176           0 :                                 recty += height - rectheight;
     177           0 :                                 break;
     178           0 :                         case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
     179           0 :                                 rectx += (width - rectwidth)/ 2;
     180           0 :                                 recty += height - rectheight;
     181           0 :                                 break;
     182           0 :                         case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
     183           0 :                                 rectx += width  - rectwidth;
     184           0 :                                 recty += height - rectheight;
     185           0 :                                 break;
     186             :                         }
     187             :                 }
     188             :         }
     189             : 
     190             : 
     191           3 :         gf_path_get_bounds(stack->drawable->path, &rc);
     192           3 :         drawable_reset_path(stack->drawable);
     193           3 :         gf_path_add_rect_center(stack->drawable->path, rectx, recty, rectwidth, rectheight);
     194           3 :         gf_path_get_bounds(stack->drawable->path, &new_rc);
     195           3 :         if (!gf_rect_equal(&rc, &new_rc))
     196           3 :                 drawable_mark_modified(stack->drawable, tr_state);
     197           0 :         else if (stack->txh.flags & GF_SR_TEXTURE_PRIVATE_MEDIA)
     198           0 :                 drawable_mark_modified(stack->drawable, tr_state);
     199             : 
     200           3 :         gf_node_dirty_clear(stack->drawable->node, GF_SG_SVG_GEOMETRY_DIRTY);
     201             : }
     202             : 
     203             : static void svg_open_texture(SVG_video_stack *stack)
     204             : {
     205         151 :         gf_sc_texture_open(&stack->txh, &stack->txurl, GF_FALSE);
     206             : }
     207             : 
     208           4 : static void svg_play_texture(SVG_video_stack *stack, SVGAllAttributes *atts)
     209             : {
     210             :         SVGAllAttributes all_atts;
     211             :         Bool lock_scene = GF_FALSE;
     212           4 :         if (stack->txh.is_open) gf_sc_texture_stop_no_unregister(&stack->txh);
     213             : 
     214           4 :         if (!atts) {
     215           0 :                 gf_svg_flatten_attributes((SVG_Element*)stack->txh.owner, &all_atts);
     216             :                 atts = &all_atts;
     217             :         }
     218           4 :         if (atts->syncBehavior) lock_scene = (*atts->syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE;
     219             : 
     220           8 :         gf_sc_texture_play_from_to(&stack->txh, &stack->txurl,
     221           4 :                                    atts->clipBegin ? (*atts->clipBegin) : 0.0,
     222           4 :                                    atts->clipEnd ? (*atts->clipEnd) : -1.0,
     223             :                                    GF_FALSE,
     224             :                                    lock_scene);
     225           4 : }
     226             : 
     227        2532 : static void svg_traverse_bitmap(GF_Node *node, void *rs, Bool is_destroy)
     228             : {
     229             :         Fixed cx, cy, angle;
     230             :         /*video stack is just an extension of image stack, type-casting is OK*/
     231        2532 :         SVG_video_stack *stack = (SVG_video_stack*)gf_node_get_private(node);
     232             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
     233             :         SVGPropertiesPointers backup_props;
     234             :         u32 backup_flags;
     235             :         GF_Matrix2D backup_matrix;
     236             :         GF_Matrix mx_3d;
     237             :         DrawableContext *ctx;
     238             :         SVGAllAttributes all_atts;
     239             : 
     240        2532 :         if (is_destroy) {
     241          18 :                 gf_sc_texture_destroy(&stack->txh);
     242          18 :                 gf_sg_mfurl_del(stack->txurl);
     243             : 
     244          18 :                 drawable_del(stack->drawable);
     245          18 :                 if (stack->audio) {
     246           0 :                         gf_node_unregister(stack->audio, NULL);
     247             :                 }
     248          18 :                 gf_free(stack);
     249          18 :                 return;
     250             :         }
     251             : 
     252        2514 :         if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) {
     253         121 :                 SVG_Draw_bitmap(tr_state);
     254         121 :                 return;
     255             :         }
     256        2393 :         else if (tr_state->traversing_mode==TRAVERSE_PICK) {
     257           4 :                 svg_drawable_pick(node, stack->drawable, tr_state);
     258           4 :                 return;
     259             :         }
     260             : #ifndef GPAC_DISABLE_3D
     261        2389 :         else if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) {
     262           0 :                 if (!stack->drawable->mesh) {
     263           0 :                         stack->drawable->mesh = new_mesh();
     264           0 :                         mesh_from_path(stack->drawable->mesh, stack->drawable->path);
     265             :                 }
     266           0 :                 compositor_3d_draw_bitmap(stack->drawable, &tr_state->ctx->aspect, tr_state, 0, 0, FIX_ONE, FIX_ONE);
     267           0 :                 return;
     268             :         }
     269             : #endif
     270             : 
     271             :         /*flatten attributes and apply animations + inheritance*/
     272        2389 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     273        2389 :         if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags))
     274             :                 return;
     275             : 
     276        2389 :         if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) {
     277          18 :                 if (!stack->txh.stream || gf_mo_url_changed(stack->txh.stream, &stack->txurl)) {
     278             : 
     279          18 :                         gf_sc_get_mfurl_from_xlink(node, &stack->txurl);
     280          18 :                         stack->txh.width = stack->txh.height = 0;
     281             : 
     282             :                         /*remove associated audio if any*/
     283          18 :                         if (stack->audio) {
     284           0 :                                 svg_audio_smil_evaluate_ex(NULL, 0, SMIL_TIMING_EVAL_REMOVE, stack->audio, stack->txh.owner);
     285           0 :                                 gf_node_unregister(stack->audio, NULL);
     286           0 :                                 stack->audio = NULL;
     287             :                         }
     288          18 :                         stack->audio_dirty = GF_TRUE;
     289             : 
     290          18 :                         if (stack->txurl.count) svg_play_texture(stack, &all_atts);
     291             :                 }
     292          18 :                 gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY);
     293             :         }
     294             : 
     295        2389 :         if (gf_node_dirty_get(node)) {
     296             :                 /*do not clear dirty state until the image is loaded*/
     297        2269 :                 if (stack->txh.width) {
     298           3 :                         gf_node_dirty_clear(node, 0);
     299           3 :                         SVG_Build_Bitmap_Graph((SVG_video_stack*)gf_node_get_private(node), tr_state);
     300             :                 }
     301             :         }
     302             : 
     303        2389 :         if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
     304           0 :                 if (!compositor_svg_is_display_off(tr_state->svg_props)) {
     305           0 :                         gf_path_get_bounds(stack->drawable->path, &tr_state->bounds);
     306           0 :                         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     307             : 
     308           0 :                         if (svg_video_get_transform_behavior(tr_state, &all_atts, &cx, &cy, &angle)) {
     309             :                                 GF_Matrix2D mx;
     310           0 :                                 tr_state->bounds.width = INT2FIX(stack->txh.width);
     311           0 :                                 tr_state->bounds.height = INT2FIX(stack->txh.height);
     312           0 :                                 tr_state->bounds.x = cx - tr_state->bounds.width/2;
     313           0 :                                 tr_state->bounds.y = cy + tr_state->bounds.height/2;
     314           0 :                                 gf_mx2d_init(mx);
     315           0 :                                 gf_mx2d_add_rotation(&mx, 0, 0, angle);
     316           0 :                                 gf_mx2d_apply_rect(&mx, &tr_state->bounds);
     317             :                         } else {
     318           0 :                                 gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds);
     319             :                         }
     320             : 
     321           0 :                         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     322             :                 }
     323        2389 :         } else if (tr_state->traversing_mode == TRAVERSE_SORT) {
     324        2389 :                 if (!compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) {
     325             :                         GF_Matrix mx_bck;
     326             :                         Bool restore_mx = GF_FALSE;
     327             : 
     328        2389 :                         compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);
     329             : 
     330        2389 :                         ctx = drawable_init_context_svg(stack->drawable, tr_state);
     331        2389 :                         if (!ctx || !ctx->aspect.fill_texture ) return;
     332             : 
     333        2389 :                         if (svg_video_get_transform_behavior(tr_state, &all_atts, &cx, &cy, &angle)) {
     334           0 :                                 drawable_reset_path(stack->drawable);
     335           0 :                                 gf_path_add_rect_center(stack->drawable->path, cx, cy, INT2FIX(stack->txh.width), INT2FIX(stack->txh.height));
     336             : 
     337           0 :                                 gf_mx2d_copy(mx_bck, tr_state->transform);
     338             :                                 restore_mx = GF_TRUE;
     339             : 
     340           0 :                                 gf_mx2d_init(tr_state->transform);
     341           0 :                                 gf_mx2d_add_rotation(&tr_state->transform, cx, cy, angle);
     342             :                         }
     343             : 
     344             :                         /*even if set this is not true*/
     345        2389 :                         ctx->aspect.pen_props.width = 0;
     346        2389 :                         ctx->flags |= CTX_NO_ANTIALIAS;
     347             : 
     348             :                         /*if rotation, transparent*/
     349        2389 :                         ctx->flags &= ~CTX_IS_TRANSPARENT;
     350        2389 :                         if (ctx->transform.m[1] || ctx->transform.m[3]) {
     351           0 :                                 ctx->flags |= CTX_IS_TRANSPARENT;
     352           0 :                                 ctx->flags &= ~CTX_NO_ANTIALIAS;
     353             :                         }
     354        2389 :                         else if (ctx->aspect.fill_texture->transparent)
     355           1 :                                 ctx->flags |= CTX_IS_TRANSPARENT;
     356        2388 :                         else if (tr_state->svg_props->opacity && (tr_state->svg_props->opacity->type==SVG_NUMBER_VALUE) && (tr_state->svg_props->opacity->value!=FIX_ONE)) {
     357           0 :                                 ctx->flags = CTX_IS_TRANSPARENT;
     358           0 :                                 ctx->aspect.fill_color = GF_COL_ARGB(FIX2INT(0xFF * tr_state->svg_props->opacity->value), 0, 0, 0);
     359             :                         }
     360             : 
     361             : #ifndef GPAC_DISABLE_3D
     362        2389 :                         if (tr_state->visual->type_3d) {
     363           0 :                                 if (!stack->drawable->mesh) {
     364           0 :                                         stack->drawable->mesh = new_mesh();
     365           0 :                                         mesh_from_path(stack->drawable->mesh, stack->drawable->path);
     366             :                                 }
     367           0 :                                 compositor_3d_draw_bitmap(stack->drawable, &ctx->aspect, tr_state, 0, 0, FIX_ONE, FIX_ONE);
     368           0 :                                 ctx->drawable = NULL;
     369             :                         } else
     370             : #endif
     371             :                         {
     372        2389 :                                 drawable_finalize_sort(ctx, tr_state, NULL);
     373             :                         }
     374             : 
     375        2389 :                         if (restore_mx) gf_mx2d_copy(tr_state->transform, mx_bck);
     376        2389 :                         compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
     377             :                 }
     378             :         }
     379        2389 :         if (stack->audio) svg_traverse_audio_ex(stack->audio, rs, GF_FALSE, tr_state->svg_props);
     380             : 
     381        2389 :         memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
     382        2389 :         tr_state->svg_flags = backup_flags;
     383             : }
     384             : 
     385             : /*********************/
     386             : /* SVG image element */
     387             : /*********************/
     388             : 
     389        2414 : static void SVG_Update_image(GF_TextureHandler *txh)
     390             : {
     391        2414 :         MFURL *txurl = &(((SVG_video_stack *)gf_node_get_private(txh->owner))->txurl);
     392             : 
     393             :         /*setup texture if needed*/
     394        2414 :         if (!txh->is_open && txurl->count) {
     395           0 :                 gf_sc_texture_play_from_to(txh, txurl, 0, -1, GF_FALSE, GF_FALSE);
     396             :         }
     397             : 
     398        2414 :         gf_sc_texture_update_frame(txh, GF_FALSE);
     399             :         /*URL is present but not opened - redraw till fetch*/
     400        2414 :         if (txh->stream && !txh->stream_finished && (!txh->tx_io || txh->needs_refresh) ) {
     401             :                 /*mark all subtrees using this image as dirty*/
     402         162 :                 gf_node_dirty_parents(txh->owner);
     403         162 :                 gf_sc_invalidate(txh->compositor, NULL);
     404             :         }
     405        2414 : }
     406             : 
     407        2140 : static void svg_traverse_image(GF_Node *node, void *rs, Bool is_destroy)
     408             : {
     409        2140 :         svg_traverse_bitmap(node, rs, is_destroy);
     410        2140 : }
     411             : 
     412          16 : void compositor_init_svg_image(GF_Compositor *compositor, GF_Node *node)
     413             : {
     414             :         SVG_video_stack *stack;
     415          16 :         GF_SAFEALLOC(stack, SVG_video_stack)
     416          16 :         if (!stack) {
     417           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg image stack\n"));
     418             :                 return;
     419             :         }
     420          16 :         stack->drawable = drawable_new();
     421          16 :         stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW;
     422          16 :         stack->drawable->node = node;
     423             : 
     424          16 :         gf_sc_texture_setup(&stack->txh, compositor, node);
     425          16 :         stack->txh.update_texture_fcnt = SVG_Update_image;
     426          16 :         stack->txh.flags = GF_SR_TEXTURE_SVG;
     427             : 
     428             :         /*force first processing of xlink-href*/
     429          16 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
     430             : 
     431          16 :         gf_node_set_private(node, stack);
     432          16 :         gf_node_set_callback_function(node, svg_traverse_image);
     433             : }
     434             : 
     435             : /*********************/
     436             : /* SVG video element */
     437             : /*********************/
     438         278 : static void SVG_Update_video(GF_TextureHandler *txh)
     439             : {
     440             :         GF_FieldInfo init_vis_info;
     441         278 :         SVG_video_stack *stack = (SVG_video_stack *) gf_node_get_private(txh->owner);
     442             : 
     443         278 :         if (!txh->stream) {
     444             :                 svg_open_texture(stack);
     445             : 
     446         151 :                 if (!txh->is_open) {
     447             :                         SVG_InitialVisibility init_vis;
     448         151 :                         if (stack->first_frame_fetched) return;
     449             : 
     450             :                         init_vis = SVG_INITIALVISIBILTY_WHENSTARTED;
     451             : 
     452           0 :                         if (gf_node_get_attribute_by_tag(txh->owner, TAG_SVG_ATT_initialVisibility, GF_FALSE, GF_FALSE, &init_vis_info) == GF_OK) {
     453           0 :                                 init_vis = *(SVG_InitialVisibility *)init_vis_info.far_ptr;
     454             :                         }
     455             : 
     456             :                         /*opens stream only at first access to fetch first frame if needed*/
     457           0 :                         if (init_vis == SVG_INITIALVISIBILTY_ALWAYS) {
     458           0 :                                 svg_play_texture((SVG_video_stack*)stack, NULL);
     459           0 :                                 gf_sc_invalidate(txh->compositor, NULL);
     460             :                         }
     461             :                 }
     462             :                 return;
     463             :         }
     464             : 
     465             :         /*when fetching the first frame disable resync*/
     466         127 :         gf_sc_texture_update_frame(txh, GF_FALSE);
     467             : 
     468             :         /* only when needs_refresh = 1, first frame is fetched */
     469         127 :         if (!stack->first_frame_fetched) {
     470          10 :                 if (txh->needs_refresh) {
     471           1 :                         stack->first_frame_fetched = GF_TRUE;
     472             :                         /*stop stream if needed*/
     473           1 :                         if (!gf_smil_timing_is_active(txh->owner)) {
     474           0 :                                 gf_sc_texture_stop_no_unregister(txh);
     475             :                                 //make sure the refresh flag is not cleared
     476           0 :                                 txh->needs_refresh = GF_TRUE;
     477             :                         }
     478             :                 }
     479             :         }
     480             : 
     481         127 :         if (!stack->audio && stack->audio_dirty) {
     482           1 :                 u32 res = gf_mo_has_audio(stack->txh.stream);
     483           1 :                 if (res != 2) {
     484           1 :                         stack->audio_dirty = GF_FALSE;
     485           1 :                         if (res) {
     486             :                                 GF_FieldInfo att_vid, att_aud;
     487           0 :                                 stack->audio = gf_node_new(gf_node_get_graph(stack->txh.owner), TAG_SVG_audio);
     488           0 :                                 gf_node_register(stack->audio, NULL);
     489           0 :                                 if (gf_node_get_attribute_by_tag(stack->txh.owner, TAG_XLINK_ATT_href, GF_FALSE, GF_FALSE, &att_vid)==GF_OK) {
     490           0 :                                         gf_node_get_attribute_by_tag(stack->audio, TAG_XLINK_ATT_href, GF_TRUE, GF_FALSE, &att_aud);
     491           0 :                                         gf_svg_attributes_copy(&att_aud, &att_vid, GF_FALSE);
     492             :                                 }
     493             :                                 /*BYPASS SMIL TIMING MODULE!!*/
     494           0 :                                 compositor_init_svg_audio(stack->txh.compositor, stack->audio, GF_TRUE);
     495             :                         }
     496             :                 }
     497             :         }
     498             : 
     499             :         /*we have no choice but retraversing the drawable until we're inactive since the movie framerate and
     500             :         the compositor framerate are likely to be different */
     501         127 :         if (!txh->stream_finished)
     502         127 :                 if (txh->needs_refresh)
     503         118 :                         gf_sc_invalidate(txh->compositor, NULL);
     504             : 
     505         127 :         if (stack->stop_requested) {
     506           0 :                 stack->stop_requested = GF_FALSE;
     507           0 :                 gf_sc_texture_stop_no_unregister(&stack->txh);
     508             :         }
     509             : }
     510             : 
     511         275 : static void svg_video_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, GF_SGSMILTimingEvalState status)
     512             : {
     513         275 :         SVG_video_stack *stack = (SVG_video_stack *)gf_node_get_private(gf_smil_get_element(rti));
     514             : 
     515         275 :         switch (status) {
     516         275 :         case SMIL_TIMING_EVAL_UPDATE:
     517         275 :                 if (!stack->txh.is_open) {
     518           2 :                         if (stack->txurl.count) {
     519           0 :                                 svg_play_texture((SVG_video_stack*)stack, NULL);
     520             :                         }
     521             :                 }
     522         273 :                 else if (stack->txh.stream_finished && (gf_smil_get_media_duration(rti)<0) ) {
     523           0 :                         Double dur = gf_mo_get_duration(stack->txh.stream);
     524           0 :                         if (dur <= 0) {
     525           0 :                                 dur = stack->txh.last_frame_time;
     526           0 :                                 dur /= 1000;
     527             :                         }
     528           0 :                         gf_smil_set_media_duration(rti, dur);
     529             :                 }
     530             :                 break;
     531           0 :         case SMIL_TIMING_EVAL_FREEZE:
     532             :         case SMIL_TIMING_EVAL_REMOVE:
     533           0 :                 stack->stop_requested = GF_TRUE;
     534           0 :                 break;
     535           0 :         case SMIL_TIMING_EVAL_REPEAT:
     536           0 :                 gf_sc_texture_restart(&stack->txh);
     537           0 :                 break;
     538             :         default:
     539             :                 break;
     540             :         }
     541         275 :         if (stack->audio) svg_audio_smil_evaluate_ex(rti, normalized_scene_time, status, stack->audio, stack->txh.owner);
     542         275 : }
     543             : 
     544         392 : static void svg_traverse_video(GF_Node *node, void *rs, Bool is_destroy)
     545             : {
     546         392 :         svg_traverse_bitmap(node, rs, is_destroy);
     547         392 : }
     548             : 
     549           2 : void compositor_init_svg_video(GF_Compositor *compositor, GF_Node *node)
     550             : {
     551             :         SVG_video_stack *stack;
     552           2 :         GF_SAFEALLOC(stack, SVG_video_stack)
     553           2 :         if (!stack) {
     554           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg video stack\n"));
     555             :                 return;
     556             :         }
     557           2 :         stack->drawable = drawable_new();
     558           2 :         stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW;
     559           2 :         stack->drawable->node = node;
     560             : 
     561           2 :         gf_sc_texture_setup(&stack->txh, compositor, node);
     562           2 :         stack->txh.update_texture_fcnt = SVG_Update_video;
     563           2 :         stack->txh.flags = GF_SR_TEXTURE_SVG;
     564             : 
     565             :         /*force first processing of xlink-href*/
     566           2 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
     567             : 
     568           2 :         gf_smil_set_evaluation_callback(node, svg_video_smil_evaluate);
     569             : 
     570           2 :         gf_node_set_private(node, stack);
     571           2 :         gf_node_set_callback_function(node, svg_traverse_video);
     572             : }
     573             : 
     574           0 : void svg_pause_video(GF_Node *n, Bool pause)
     575             : {
     576           0 :         SVG_video_stack *st = (SVG_video_stack *)gf_node_get_private(n);
     577           0 :         if (!st) return;
     578           0 :         if (pause) gf_mo_pause(st->txh.stream);
     579           0 :         else gf_mo_resume(st->txh.stream);
     580             : }
     581             : 
     582           0 : void compositor_svg_video_modified(GF_Compositor *compositor, GF_Node *node)
     583             : {
     584             :         /*if href has been modified, stop the video (and associated audio if any) right away - we cannot wait for next traversal to
     585             :         process this as the video could be in a hidden subtree not traversed*/
     586           0 :         if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) {
     587           0 :                 SVG_video_stack *st = (SVG_video_stack *)gf_node_get_private(node);
     588             :                 /*WARNING - stack may be NULL at this point when inserting the video from script*/
     589           0 :                 if (st && st->txh.is_open) {
     590           0 :                         if (st->audio) {
     591           0 :                                 svg_audio_smil_evaluate_ex(NULL, 0, SMIL_TIMING_EVAL_REMOVE, st->audio, st->txh.owner);
     592           0 :                                 gf_node_unregister(st->audio, NULL);
     593           0 :                                 st->audio = NULL;
     594             :                         }
     595             :                         /*reset cached URL to avoid reopening the resource in the smil timing callback*/
     596           0 :                         gf_sg_vrml_mf_reset(&st->txurl, GF_SG_VRML_MFURL);
     597           0 :                         gf_sc_texture_stop(&st->txh);
     598             :                 }
     599             :         }
     600           0 :         gf_node_dirty_set(node, 0, GF_FALSE);
     601             :         /*and force a redraw of next frame*/
     602           0 :         gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
     603           0 : }
     604             : 
     605             : 
     606             : /*********************/
     607             : /* SVG audio element */
     608             : /*********************/
     609             : 
     610         300 : static void svg_audio_smil_evaluate_ex(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, u32 status, GF_Node *slave_audio, GF_Node *video)
     611             : {
     612             :         GF_Node *audio;
     613             :         SVG_audio_stack *stack;
     614             : 
     615             :         audio = slave_audio;
     616         300 :         if (!audio) audio = gf_smil_get_element(rti);
     617             : 
     618         300 :         stack = (SVG_audio_stack *)gf_node_get_private(audio);
     619             : 
     620         300 :         switch (status) {
     621         300 :         case SMIL_TIMING_EVAL_UPDATE:
     622         300 :                 if (!stack->is_active && !stack->is_error) {
     623           2 :                         if (stack->aurl.count) {
     624             :                                 SVGAllAttributes atts;
     625             :                                 Bool lock_timeline = GF_FALSE;
     626           0 :                                 gf_svg_flatten_attributes((SVG_Element*) (video ? video : audio), &atts);
     627             : 
     628           0 :                                 if (atts.syncBehavior) lock_timeline = (*atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE;
     629             : 
     630           0 :                                 if (gf_sc_audio_open(&stack->input, &stack->aurl,
     631           0 :                                                      atts.clipBegin ? (*atts.clipBegin) : 0.0,
     632           0 :                                                      atts.clipEnd ? (*atts.clipEnd) : -1.0,
     633             :                                                      lock_timeline) == GF_OK)
     634             :                                 {
     635           0 :                                         gf_mo_set_speed(stack->input.stream, FIX_ONE);
     636           0 :                                         stack->is_active = GF_TRUE;
     637             :                                 } else {
     638           0 :                                         stack->is_error = GF_TRUE;
     639             :                                 }
     640             :                         }
     641             :                 }
     642         298 :                 else if (!slave_audio && stack->input.stream_finished && (gf_smil_get_media_duration(rti) < 0) ) {
     643           0 :                         Double dur = gf_mo_get_duration(stack->input.stream);
     644           0 :                         if (dur <= 0) {
     645           0 :                                 dur = stack->input.stream ? stack->input.stream->timestamp : 0;
     646           0 :                                 dur /= 1000;
     647             :                         }
     648           0 :                         gf_smil_set_media_duration(rti, dur);
     649             :                 }
     650             :                 break;
     651           0 :         case SMIL_TIMING_EVAL_REPEAT:
     652           0 :                 if (stack->is_active)
     653           0 :                         gf_sc_audio_restart(&stack->input);
     654             :                 break;
     655           0 :         case SMIL_TIMING_EVAL_FREEZE:
     656           0 :                 gf_sc_audio_stop(&stack->input);
     657           0 :                 stack->is_active = GF_FALSE;
     658             :                 break;
     659           0 :         case SMIL_TIMING_EVAL_REMOVE:
     660           0 :                 gf_sc_audio_stop(&stack->input);
     661           0 :                 stack->is_active = GF_FALSE;
     662             :                 break;
     663           0 :         case SMIL_TIMING_EVAL_DEACTIVATE:
     664           0 :                 if (stack->is_active) {
     665           0 :                         gf_sc_audio_stop(&stack->input);
     666           0 :                         gf_sc_audio_unregister(&stack->input);
     667           0 :                         stack->is_active = GF_FALSE;
     668             :                 }
     669             :                 break;
     670             :         }
     671         300 : }
     672             : 
     673         300 : static void svg_audio_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, GF_SGSMILTimingEvalState status)
     674             : {
     675         300 :         svg_audio_smil_evaluate_ex(rti, normalized_scene_time, status, NULL, NULL);
     676         300 : }
     677             : 
     678             : 
     679         155 : static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGPropertiesPointers *props)
     680             : {
     681             :         SVGAllAttributes all_atts;
     682             :         SVGPropertiesPointers backup_props;
     683             :         u32 backup_flags, restore;
     684             :         GF_TraverseState *tr_state = (GF_TraverseState*)rs;
     685         155 :         SVG_audio_stack *stack = (SVG_audio_stack *)gf_node_get_private(node);
     686             : 
     687         155 :         if (is_destroy) {
     688           2 :                 gf_sc_audio_predestroy(&stack->input);
     689           2 :                 gf_sg_mfurl_del(stack->aurl);
     690           2 :                 gf_free(stack);
     691           2 :                 return;
     692             :         }
     693         153 :         if (stack->is_active) {
     694         151 :                 gf_sc_audio_register(&stack->input, (GF_TraverseState*)rs);
     695             :         }
     696             : 
     697             :         restore = 0;
     698         153 :         if (!props) {
     699             :                 restore = 1;
     700         153 :                 gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     701         153 :                 if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags))
     702             :                         return;
     703         153 :                 props = tr_state->svg_props;
     704             :         }
     705             : 
     706         153 :         if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) {
     707             :                 SVGAllAttributes atts;
     708             :                 Bool lock_timeline = GF_FALSE;
     709           2 :                 if (stack->is_active)
     710           0 :                         gf_sc_audio_stop(&stack->input);
     711             : 
     712           2 :                 stack->is_error = GF_FALSE;
     713             : 
     714           2 :                 gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY);
     715           2 :                 gf_sc_get_mfurl_from_xlink(node, &(stack->aurl));
     716             : 
     717           2 :                 gf_svg_flatten_attributes((SVG_Element*) node, &atts);
     718           2 :                 if (atts.syncBehavior) lock_timeline = (*atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE;
     719             : 
     720           6 :                 if (stack->aurl.count && (gf_sc_audio_open(&stack->input, &stack->aurl,
     721           2 :                                           atts.clipBegin ? (*atts.clipBegin) : 0.0,
     722           2 :                                           atts.clipEnd ? (*atts.clipEnd) : -1.0,
     723             :                                           lock_timeline) == GF_OK)
     724             : 
     725             :                    ) {
     726           2 :                         gf_mo_set_speed(stack->input.stream, FIX_ONE);
     727           2 :                         stack->is_active = GF_TRUE;
     728           0 :                 } else if (stack->is_active) {
     729           0 :                         gf_sc_audio_unregister(&stack->input);
     730           0 :                         stack->is_active = GF_FALSE;
     731             :                 }
     732             :         }
     733             : 
     734             :         /*store mute flag*/
     735         153 :         stack->input.is_muted = GF_FALSE;
     736         153 :         if (tr_state->switched_off
     737         153 :                 || compositor_svg_is_display_off(props)
     738         153 :                 || (*(props->visibility) == SVG_VISIBILITY_HIDDEN) ) {
     739             : 
     740           0 :                 stack->input.is_muted = GF_TRUE;
     741             :         }
     742             : 
     743         153 :         stack->input.intensity = tr_state->svg_props->computed_audio_level;
     744             : 
     745         153 :         if (restore) {
     746         153 :                 memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
     747         153 :                 tr_state->svg_flags = backup_flags;
     748             :         }
     749             : }
     750         155 : static void svg_traverse_audio(GF_Node *node, void *rs, Bool is_destroy)
     751             : {
     752         155 :         svg_traverse_audio_ex(node, rs, is_destroy, NULL);
     753         155 : }
     754             : 
     755           2 : void compositor_init_svg_audio(GF_Compositor *compositor, GF_Node *node, Bool slaved_timing)
     756             : {
     757             :         SVG_audio_stack *stack;
     758           2 :         GF_SAFEALLOC(stack, SVG_audio_stack)
     759           2 :         if (!stack) return;
     760           2 :         gf_sc_audio_setup(&stack->input, compositor, node);
     761             : 
     762             :         /*force first processing of xlink-href*/
     763           2 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
     764             : 
     765           2 :         if (!slaved_timing)
     766           2 :                 gf_smil_set_evaluation_callback(node, svg_audio_smil_evaluate);
     767             : 
     768           2 :         gf_node_set_private(node, stack);
     769           2 :         gf_node_set_callback_function(node, svg_traverse_audio);
     770             : }
     771             : 
     772           0 : void svg_pause_audio(GF_Node *n, Bool pause)
     773             : {
     774           0 :         SVG_audio_stack *st = (SVG_audio_stack *)gf_node_get_private(n);
     775           0 :         if (!st) return;
     776           0 :         if (pause) gf_mo_pause(st->input.stream);
     777           0 :         else gf_mo_resume(st->input.stream);
     778             : }
     779             : 
     780        2389 : GF_TextureHandler *compositor_svg_get_image_texture(GF_Node *node)
     781             : {
     782        2389 :         SVG_video_stack *st = (SVG_video_stack *) gf_node_get_private(node);
     783        2389 :         return &(st->txh);
     784             : }
     785             : 
     786             : 
     787             : 
     788             : 
     789             : typedef struct
     790             : {
     791             :         /*media stream*/
     792             :         GF_MediaObject *resource;
     793             :         Bool stop_requested, is_open;
     794             :         Double clipBegin, clipEnd;
     795             : } SVG_updates_stack;
     796             : 
     797           0 : static void svg_updates_smil_evaluate(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, GF_SGSMILTimingEvalState status)
     798             : {
     799           0 :         SVG_updates_stack *stack = (SVG_updates_stack *)gf_node_get_private(gf_smil_get_element(rti));
     800             : 
     801           0 :         switch (status) {
     802           0 :         case SMIL_TIMING_EVAL_UPDATE:
     803           0 :                 if (!stack->is_open) {
     804           0 :                         if (stack->resource ) gf_mo_play(stack->resource, stack->clipBegin, stack->clipEnd, GF_FALSE);
     805           0 :                         stack->is_open = GF_TRUE;
     806             :                 }
     807           0 :                 else if (gf_mo_is_done(stack->resource) && (gf_smil_get_media_duration(rti)<0) ) {
     808           0 :                         Double dur = gf_mo_get_duration(stack->resource);
     809           0 :                         gf_smil_set_media_duration(rti, dur);
     810             :                 }
     811             :                 break;
     812           0 :         case SMIL_TIMING_EVAL_FREEZE:
     813             :         case SMIL_TIMING_EVAL_REMOVE:
     814           0 :                 stack->is_open = GF_FALSE;
     815           0 :                 gf_mo_set_flag(stack->resource, GF_MO_DISPLAY_REMOVE, GF_TRUE);
     816           0 :                 gf_mo_stop(&stack->resource);
     817           0 :                 break;
     818           0 :         case SMIL_TIMING_EVAL_REPEAT:
     819           0 :                 gf_mo_restart(stack->resource);
     820           0 :                 break;
     821             :         default:
     822             :                 break;
     823             :         }
     824           0 : }
     825             : 
     826           0 : static void svg_traverse_updates(GF_Node *node, void *rs, Bool is_destroy)
     827             : {
     828             :         /*video stack is just an extension of image stack, type-casting is OK*/
     829           0 :         SVG_updates_stack *stack = (SVG_updates_stack*)gf_node_get_private(node);
     830             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
     831             :         SVGAllAttributes all_atts;
     832             :         SVGPropertiesPointers backup_props;
     833             :         u32 backup_flags, dirty_flags;
     834             : 
     835           0 :         if (is_destroy) {
     836           0 :                 if (stack->resource) {
     837           0 :                         if (stack->is_open) {
     838           0 :                                 gf_mo_set_flag(stack->resource, GF_MO_DISPLAY_REMOVE, GF_TRUE);
     839           0 :                                 gf_mo_stop(&stack->resource);
     840             :                         }
     841           0 :                         gf_mo_unregister(node, stack->resource);
     842             :                 }
     843           0 :                 gf_free(stack);
     844           0 :                 return;
     845             :         }
     846             : 
     847           0 :         if (tr_state->traversing_mode!=TRAVERSE_SORT) return;
     848             : 
     849             :         /*flatten attributes and apply animations + inheritance*/
     850           0 :         gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);
     851           0 :         if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags))
     852             :                 return;
     853             : 
     854           0 :         dirty_flags = gf_node_dirty_get(node);
     855           0 :         if (dirty_flags) {
     856           0 :                 stack->clipBegin = all_atts.clipBegin ? *all_atts.clipBegin : 0;
     857           0 :                 stack->clipEnd = all_atts.clipEnd ? *all_atts.clipEnd : -1;
     858           0 :                 if (dirty_flags & GF_SG_SVG_XLINK_HREF_DIRTY) {
     859             :                         GF_MediaObject *new_res;
     860             :                         MFURL url;
     861             :                         Bool lock_timeline=GF_FALSE;
     862           0 :                         url.vals = NULL;
     863           0 :                         url.count = 0;
     864             : 
     865           0 :                         if (all_atts.syncBehavior) lock_timeline = (*all_atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? GF_TRUE : GF_FALSE;
     866             : 
     867           0 :                         gf_sc_get_mfurl_from_xlink(node, &url);
     868             : 
     869           0 :                         new_res = gf_mo_register(node, &url, lock_timeline, GF_FALSE);
     870           0 :                         gf_sg_mfurl_del(url);
     871             : 
     872           0 :                         if (stack->resource!=new_res) {
     873           0 :                                 if (stack->resource) {
     874           0 :                                         gf_mo_stop(&stack->resource);
     875           0 :                                         gf_mo_unregister(node, stack->resource);
     876             :                                 }
     877           0 :                                 stack->resource = new_res;
     878           0 :                                 if (stack->resource && stack->is_open) gf_mo_play(stack->resource, stack->clipBegin, stack->clipEnd, GF_FALSE);
     879             :                         }
     880             :                 }
     881           0 :                 gf_node_dirty_clear(node, 0);
     882             :         }
     883           0 :         memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
     884           0 :         tr_state->svg_flags = backup_flags;
     885             : }
     886             : 
     887           0 : void compositor_init_svg_updates(GF_Compositor *compositor, GF_Node *node)
     888             : {
     889             :         SVG_updates_stack *stack;
     890           0 :         GF_SAFEALLOC(stack, SVG_updates_stack)
     891           0 :         if (!stack) {
     892           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate laser updates stack\n"));
     893             :                 return;
     894             :         }
     895             : 
     896             :         /*force first processing of xlink-href*/
     897           0 :         gf_node_dirty_set(node, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
     898             : 
     899           0 :         gf_smil_set_evaluation_callback(node, svg_updates_smil_evaluate);
     900             : 
     901           0 :         gf_node_set_private(node, stack);
     902           0 :         gf_node_set_callback_function(node, svg_traverse_updates);
     903           0 :         stack->clipEnd = -1;
     904             : }
     905             : 
     906             : #endif //GPAC_DISABLE_SVG
     907             : 

Generated by: LCOV version 1.13