LCOV - code coverage report
Current view: top level - scenegraph - smil_timing.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 336 448 75.0 %
Date: 2021-04-29 23:48:07 Functions: 23 26 88.5 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Cyril Concolato
       5             :  *                      Copyright (c) Telecom ParisTech 2004-2012
       6             :  *
       7             :  *  This file is part of GPAC / SVG Scene Graph sub-project
       8             :  *
       9             :  *  GPAC is free software; you can redistribute it and/or modify
      10             :  *  it under the terms of the GNU Lesser General Public License as published by
      11             :  *  the Free Software Foundation; either version 2, or (at your option)
      12             :  *  any later version.
      13             :  *
      14             :  *  GPAC is distributed in the hope that it will be useful,
      15             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :  *  GNU Lesser General Public License for more details.
      18             :  *
      19             :  *  You should have received a copy of the GNU Lesser General Public
      20             :  *  License along with this library; see the file COPYING.  If not, write to
      21             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      22             :  *
      23             :  */
      24             : 
      25             : #include <gpac/internal/scenegraph_dev.h>
      26             : #include <gpac/events.h>
      27             : #include <gpac/nodes_svg.h>
      28             : 
      29             : #ifndef GPAC_DISABLE_SVG
      30             : 
      31         150 : static void gf_smil_timing_null_timed_function(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, GF_SGSMILTimingEvalState state)
      32             : {
      33         150 : }
      34             : 
      35          88 : static void gf_smil_timing_print_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval)
      36             : {
      37          88 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - ", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
      38          88 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (current ? "Current " : "   Next "));
      39          88 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("Interval - "));
      40          88 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("begin: %.2f", interval->begin));
      41          88 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (" - end: %.2f", interval->end));
      42          88 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (" - simple dur: %.2f - active dur: %.2f\n",interval->simple_duration, interval->active_duration));
      43          88 : }
      44             : 
      45             : /* Computes the active duration for the given interval,
      46             :    assumes that the values of begin and end have been set (>0 for real duration or -1 if infinite)
      47             :    and that begin is defined (i.e. a positive value, not infinite)*/
      48          88 : static void gf_smil_timing_compute_active_duration(SMIL_Timing_RTI *rti, SMIL_Interval *interval)
      49             : {
      50             :         Bool clamp_active_duration;
      51             :         Bool isDurDefined, isRepeatCountDefined, isRepeatDurDefined, isMinDefined, isMaxDefined, isRepeatDurIndefinite, isRepeatCountIndefinite, isMediaDuration;
      52          88 :         SMILTimingAttributesPointers *timingp = rti->timingp;
      53             : 
      54             :         /* TODO: check if the test on begin is right and needed */
      55          88 :         if (!timingp/* || interval->begin == -1*/) return;
      56             : 
      57          88 :         switch (gf_node_get_tag((GF_Node *)rti->timed_elt)) {
      58           0 :         case TAG_SVG_discard:
      59           0 :                 interval->active_duration = -1;
      60           0 :                 return;
      61             :         }
      62             : 
      63          88 :         isDurDefined = (timingp->dur && timingp->dur->type == SMIL_DURATION_DEFINED);
      64          88 :         isMediaDuration = (timingp->dur && (timingp->dur->type == SMIL_DURATION_MEDIA) && (rti->media_duration>=0) );
      65          88 :         isRepeatCountDefined = (timingp->repeatCount && timingp->repeatCount->type == SMIL_REPEATCOUNT_DEFINED);
      66          88 :         isRepeatCountIndefinite = (timingp->repeatCount && timingp->repeatCount->type == SMIL_REPEATCOUNT_INDEFINITE);
      67          88 :         isRepeatDurDefined = (timingp->repeatDur && timingp->repeatDur->type == SMIL_DURATION_DEFINED);
      68          88 :         isRepeatDurIndefinite = (timingp->repeatDur && timingp->repeatDur->type == SMIL_DURATION_INDEFINITE);
      69             : 
      70             :         /* Step 1: Computing active duration using repeatDur and repeatCount */
      71          88 :         if (isDurDefined || isMediaDuration) {
      72          81 :                 interval->simple_duration = isMediaDuration ? rti->media_duration : timingp->dur->clock_value;
      73             : 
      74          81 :                 if (isRepeatCountDefined && !isRepeatDurDefined) {
      75           6 :                         interval->repeat_duration = FIX2FLT(timingp->repeatCount->count) * interval->simple_duration;
      76          75 :                 } else if (!isRepeatCountDefined && isRepeatDurDefined) {
      77           1 :                         interval->repeat_duration = timingp->repeatDur->clock_value;
      78          74 :                 } else if (!isRepeatCountDefined && !isRepeatDurDefined) {
      79          73 :                         if (isRepeatDurIndefinite || isRepeatCountIndefinite) {
      80           5 :                                 interval->repeat_duration = -1;
      81             :                         } else {
      82          68 :                                 interval->repeat_duration = interval->simple_duration;
      83             :                         }
      84             :                 } else {
      85           1 :                         interval->repeat_duration = MIN(timingp->repeatDur->clock_value,
      86             :                                                         FIX2FLT(timingp->repeatCount->count) * interval->simple_duration);
      87             :                 }
      88             :         } else {
      89             : 
      90             :                 /* simple_duration is indefinite */
      91           7 :                 interval->simple_duration = -1;
      92             : 
      93             :                 /* we can ignore repeatCount to compute active_duration */
      94           7 :                 if (!isRepeatDurDefined) {
      95           7 :                         interval->repeat_duration = -1;
      96             :                 } else {
      97           0 :                         interval->repeat_duration = timingp->repeatDur->clock_value;
      98             :                 }
      99             :         }
     100             : 
     101          88 :         interval->active_duration = interval->repeat_duration;
     102             :         /* Step 2: if end is defined in the document, clamp active duration to end-begin
     103             :         otherwise return*/
     104          88 :         if (interval->end < 0) {
     105             :                 /* interval->active_duration stays as is */
     106             :         } else {
     107           2 :                 if (interval->active_duration >= 0)
     108           2 :                         interval->active_duration = MIN(interval->active_duration, interval->end - interval->begin);
     109             :                 else
     110           0 :                         interval->active_duration = interval->end - interval->begin;
     111             :         }
     112             : 
     113             :         /* min and max check should be checked last,
     114             :            to ensure that they have greater priority than the end attribute
     115             :            see (animate-elem-223-t.svg) */
     116             :         /* Step 3: clamp the active duration with min and max */
     117             :         clamp_active_duration = 1;
     118             :         /* testing for presence of min and max because some elements may not have them: eg SVG audio */
     119          88 :         isMinDefined = (timingp->min && timingp->min->type == SMIL_DURATION_DEFINED);
     120          88 :         isMaxDefined = (timingp->max && timingp->max->type == SMIL_DURATION_DEFINED);
     121          89 :         if (isMinDefined && isMaxDefined &&
     122           1 :                 timingp->max->clock_value < timingp->min->clock_value) {
     123             :                 clamp_active_duration = 0;
     124             :         }
     125             :         if (clamp_active_duration) {
     126          88 :                 if (isMinDefined) {
     127           2 :                         if ((interval->active_duration >= 0) &&
     128           1 :                                 (interval->active_duration <= timingp->min->clock_value)) {
     129             :                                 /* see http://www.w3.org/TR/2005/REC-SMIL2-20051213/smil-timing.html#Timing-MinMax
     130             :                                   - if repeat duration or simple duration is smaller than min,
     131             :                                   then the (active ? / simple ?) duration shall be set to min
     132             :                                   (cf 6th row in animate-elem-65-t.svg)
     133             :                                   - if the min > dur > end, the element is played normally for its simple duration
     134             :                                   and then is frozen or not shown depending on the value of the fill attribute.
     135             :                                   (cf animate-elem-222-t.svg)*/
     136           0 :                                 interval->active_duration = timingp->min->clock_value;
     137           0 :                                 interval->min_active = 1;
     138             :                         }
     139             :                 }
     140          88 :                 if (isMaxDefined) {
     141           1 :                         if ((interval->active_duration >= 0 && interval->active_duration >= timingp->max->clock_value) ||
     142             :                                 interval->active_duration == -1) {
     143           0 :                                 interval->active_duration = timingp->max->clock_value;
     144             :                         }
     145             :                 }
     146             :         }
     147             : 
     148             : }
     149             : 
     150             : /* This should be called when the dur attribute is set to media and when the media duration is known.
     151             :    The function recomputes the active duration of the current interval according to the given media duration */
     152             : GF_EXPORT
     153           0 : void gf_smil_set_media_duration(SMIL_Timing_RTI *rti, Double media_duration)
     154             : {
     155           0 :         rti->media_duration = media_duration;
     156           0 :         gf_smil_timing_compute_active_duration(rti, rti->current_interval);
     157           0 : }
     158             : 
     159             : /* the end value of this interval needs to be initialized before computing the active duration
     160             :    the begin value must be >= 0
     161             :    The result can be:
     162             :     - a positive value meaning that a resolved and non-indefinite value was found
     163             :         - the value -1 meaning indefinite or unresolved
     164             :                 TODO: we should make a difference between indefinite and unresolved because
     165             :                 if an interval is created with a value of indefinite, this value should not
     166             :                 be replaced by a resolved event. (Not sure ?!!)
     167             :         - the value -2 meaning that a valid end value (including indefinite) could not be found
     168             : */
     169          88 : static void gf_smil_timing_get_interval_end(SMIL_Timing_RTI *rti, SMIL_Interval *interval)
     170             : {
     171             :         u32 end_count, j;
     172             : 
     173             :         /* we set the value to indicate that this is an illegal end,
     174             :            if it stays like that after searching through the values,
     175             :            then the whole interval must be discarded */
     176          88 :         interval->end = -2;
     177             : 
     178          88 :         end_count = (rti->timingp->end ? gf_list_count(*rti->timingp->end) : 0);
     179             :         /* trying to find a matching end */
     180          88 :         if (end_count > 0) {
     181           0 :                 for (j = 0; j < end_count; j++) {
     182           2 :                         SMIL_Time *end = (SMIL_Time*)gf_list_get(*rti->timingp->end, j);
     183           2 :                         if ( GF_SMIL_TIME_IS_CLOCK(end->type) )  {
     184           2 :                                 if( end->clock >= interval->begin) {
     185           2 :                                         interval->end = end->clock;
     186             :                                         break;
     187             :                                 }
     188             :                         } else {
     189             :                                 /* an unresolved or indefinite value is always good */
     190           0 :                                 interval->end = -1;
     191             :                                 break;
     192             :                         }
     193             :                 }
     194             :         } else {
     195          86 :                 interval->end = -1;
     196             :         }
     197          88 : }
     198             : 
     199          80 : static void gf_smil_timing_get_first_interval(SMIL_Timing_RTI *rti)
     200             : {
     201             :         u32 i, count;
     202          80 :         if (!rti || !rti->current_interval) return;
     203             :         
     204             :         memset(rti->current_interval, 0, sizeof(SMIL_Interval));
     205          80 :         rti->current_interval->begin = -1;
     206          80 :         count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0);
     207          85 :         for (i = 0; i < count; i ++) {
     208          46 :                 SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i);
     209          46 :                 if (GF_SMIL_TIME_IS_CLOCK(begin->type)) {
     210          41 :                         rti->current_interval->begin = begin->clock;
     211          41 :                         break;
     212             :                 }
     213             :         }
     214             :         /*In SVG, if no 'begin' is specified, the default timing of the time container
     215             :         is equivalent to an offset value of '0'.*/
     216          80 :         if (rti->current_interval->begin == -1 && count == 0) {
     217             :                 /* except for LASeR Conditional element*/
     218          34 :                 if (rti->timed_elt->sgprivate->tag != TAG_LSR_conditional) {
     219          33 :                         rti->current_interval->begin = 0;
     220             :                 } else {
     221             :                         return;
     222             :                 }
     223             :         }
     224             : 
     225             :         /* this is the first time we check the interval */
     226          79 :         gf_smil_timing_get_interval_end(rti, rti->current_interval);
     227             :         if ((0) && rti->current_interval->end == -2) {
     228             :                 /* TODO: check if the interval can be discarded (i.e. if end is specified with an invalid end value (return -2)),
     229             :                    probably yes, but next time we call the evaluation of interval, we should call get_first_interval */
     230             :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     231             :                 rti->current_interval->begin = -1;
     232             :                 rti->current_interval->end = -1;
     233             :                 return;
     234             :         }
     235             : 
     236          79 :         gf_smil_timing_compute_active_duration(rti, rti->current_interval);
     237          79 :         gf_smil_timing_print_interval(rti, 1, rti->current_interval);
     238             : }
     239             : 
     240          89 : static Bool gf_smil_timing_get_next_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval, Double scene_time)
     241             : {
     242             :         u32 i, count;
     243          89 :         if (!interval) return GF_FALSE;
     244             :         
     245             :         memset(interval, 0, sizeof(SMIL_Interval));
     246          89 :         interval->begin = -1;
     247             : 
     248          89 :         count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0);
     249          54 :         for (i = 0; i < count; i ++) {
     250          55 :                 SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i);
     251          55 :                 if (GF_SMIL_TIME_IS_CLOCK(begin->type)) {
     252          50 :                         if (rti->current_interval->begin != -1 && begin->clock <= rti->current_interval->begin) continue;
     253             : //                      if (rti->current_interval->begin == -1 || begin->clock <= scene_time) {
     254           1 :                         interval->begin = begin->clock;
     255             :                         break;
     256             : //                      }
     257             :                 }
     258             :         }
     259          89 :         if (interval->begin != -1) {
     260           1 :                 gf_smil_timing_get_interval_end(rti, interval);
     261           1 :                 if (interval->end == -2) {
     262             :                         /* this is a wrong interval see first animation in animate-elem-201-t.svg */
     263           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     264           0 :                         interval->begin = -1;
     265           0 :                         interval->end = -1;
     266             :                         return 0;
     267             :                 }
     268           1 :                 gf_smil_timing_compute_active_duration(rti, interval);
     269           1 :                 gf_smil_timing_print_interval(rti, current, interval);
     270             :                 return 1;
     271             :         } else {
     272             :                 return 0;
     273             :         }
     274             : }
     275             : 
     276             : /* To reduce the process of notifying the time to all timed elements, we add to the scene graph
     277             :    only the timed elements which have a resolved current interval, other timed elements will be
     278             :    added at runtime when an event leads to the creation of a new interval.
     279             :    We also insert the new timed element in the order of the current_interval begin value, to stop
     280             :    the notification of time when not necessary */
     281          83 : static Bool gf_smil_timing_add_to_sg(GF_SceneGraph *sg, SMIL_Timing_RTI *rti)
     282             : {
     283          83 :         if (rti->current_interval->begin != -1) {
     284             :                 SMIL_Timing_RTI *cur_rti = NULL;
     285             :                 u32 i;
     286             : 
     287         714 :                 for (i = 0; i < gf_list_count(sg->smil_timed_elements); i++) {
     288         750 :                         cur_rti = (SMIL_Timing_RTI *)gf_list_get(sg->smil_timed_elements, i);
     289         750 :                         if (cur_rti->current_interval->begin > rti->current_interval->begin) break;
     290             :                 }
     291          77 :                 gf_list_insert(sg->smil_timed_elements, rti, i);
     292             :                 return 1;
     293             :         }
     294             :         return 0;
     295             : }
     296             : 
     297             : /* when a timed element restarts, since the list of timed elements in the scene graph,
     298             :    to which scene time is notified at each rendering cycle, is sorted, we need to remove
     299             :    and reinsert this timed element as if it was a new one, to make sure the sorting is correct */
     300           9 : static void gf_smil_mark_modified(SMIL_Timing_RTI *rti, Bool remove)
     301             : {
     302           9 :         GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph;
     303           9 :         while (sg->parent_scene) sg = sg->parent_scene;
     304           9 :         if (remove) {
     305           0 :                 gf_list_del_item(sg->modified_smil_timed_elements, rti);
     306             :         } else {
     307           9 :                 if (gf_list_find(sg->modified_smil_timed_elements, rti) == -1) {
     308           3 :                         gf_list_add(sg->modified_smil_timed_elements, rti);
     309             :                 }
     310             :         }
     311           9 : }
     312             : 
     313             : /* Attributes from the timed elements are not easy to use during runtime,
     314             :    the runtime info is a set of easy to use structures.
     315             :    This function initializes them (intervals, status ...)
     316             :    and registers the element with the scenegraph */
     317             : GF_EXPORT
     318          80 : void gf_smil_timing_init_runtime_info(GF_Node *timed_elt)
     319             : {
     320             :         GF_SceneGraph *sg;
     321             :         SMIL_Timing_RTI *rti;
     322             :         SMILTimingAttributesPointers *timingp;
     323          80 :         u32 tag = gf_node_get_tag(timed_elt);
     324             :         SVGAllAttributes all_atts;
     325             :         SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)timed_elt;
     326             : 
     327          80 :         gf_svg_flatten_attributes((SVG_Element *)e, &all_atts);
     328          80 :         e->timingp = gf_malloc(sizeof(SMILTimingAttributesPointers));
     329          80 :         e->timingp->begin         = all_atts.begin;
     330          80 :         e->timingp->clipBegin     = all_atts.clipBegin;
     331          80 :         e->timingp->clipEnd               = all_atts.clipEnd;
     332          80 :         e->timingp->dur                   = all_atts.dur;
     333          80 :         e->timingp->end                   = all_atts.end;
     334          80 :         e->timingp->fill          = all_atts.smil_fill;
     335          80 :         e->timingp->max                   = all_atts.max;
     336          80 :         e->timingp->min                   = all_atts.min;
     337          80 :         e->timingp->repeatCount = all_atts.repeatCount;
     338          80 :         e->timingp->repeatDur     = all_atts.repeatDur;
     339          80 :         e->timingp->restart               = all_atts.restart;
     340          80 :         timingp = e->timingp;
     341          80 :         if (!timingp) return;
     342             : 
     343          80 :         if (tag == TAG_SVG_audio || tag == TAG_SVG_video) {
     344             :                 /* if the dur attribute is not set, then it should be set to media
     345             :                    as this is the default for media elements see
     346             :                    http://www.w3.org/TR/2005/REC-SMIL2-20051213/smil-timing.html#Timing-DurValueSemantics
     347             :                    "For simple media elements that specify continuous media (i.e. media with an inherent notion of time),
     348             :                    the implicit duration is the intrinsic duration of the media itself - e.g. video and audio files
     349             :                    have a defined duration."
     350             :                 TODO: Check if this should work with the animation element */
     351           4 :                 if (!e->timingp->dur) {
     352             :                         GF_FieldInfo info;
     353           4 :                         gf_node_get_attribute_by_tag((GF_Node *)e, TAG_SVG_ATT_dur, 1, 0, &info);
     354           4 :                         e->timingp->dur = (SMIL_Duration *)info.far_ptr;
     355           4 :                         e->timingp->dur->type = SMIL_DURATION_MEDIA;
     356             :                 }
     357             :         }
     358             : 
     359          80 :         GF_SAFEALLOC(rti, SMIL_Timing_RTI)
     360          80 :         if (!rti) {
     361           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Timing] Failed to alloc SMIL timing RTI\n"));
     362             :                 return;
     363             :         }
     364          80 :         timingp->runtime = rti;
     365          80 :         rti->timed_elt = timed_elt;
     366          80 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Initialization\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     367             : 
     368          80 :         rti->timingp = timingp;
     369          80 :         rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
     370          80 :         rti->evaluate_status = SMIL_TIMING_EVAL_NONE;
     371          80 :         rti->evaluate = gf_smil_timing_null_timed_function;
     372          80 :         rti->scene_time = -1;
     373          80 :         rti->force_reevaluation = 0;
     374          80 :         rti->media_duration = -1;
     375             : 
     376          80 :         GF_SAFEALLOC(rti->current_interval, SMIL_Interval);
     377          80 :         if (!rti->current_interval) {
     378           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Timing] Failed to alloc SMIL timing current interval\n"));
     379             :                 return;
     380             :         }
     381          80 :         gf_smil_timing_get_first_interval(rti);
     382          80 :         GF_SAFEALLOC(rti->next_interval, SMIL_Interval);
     383          80 :         if (!rti->next_interval) {
     384           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Timing] Failed to alloc SMIL timing next interval\n"));
     385             :                 return;
     386             :         }
     387          80 :         gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->current_interval->begin);
     388             : 
     389             :         /* Now that the runtime info for this timed element is initialized, we can tell the scene graph that it can start
     390             :            notifying the scene time to this element. Because of the 'animation' element, we can have many scene graphs
     391             :            sharing the same scene time, we therefore add this timed element to the rootmost scene graph. */
     392          80 :         sg = timed_elt->sgprivate->scenegraph;
     393          80 :         while (sg->parent_scene) sg = sg->parent_scene;
     394          80 :         gf_smil_timing_add_to_sg(sg, rti);
     395             : }
     396             : 
     397             : 
     398         140 : static void gf_smil_timing_reset_time_list(GF_List *times)
     399             : {
     400             :         GF_DOMEventTarget *evt;
     401             :         u32 i;
     402         189 :         for (i=0; i<gf_list_count(times); i++) {
     403          49 :                 SMIL_Time *t = gf_list_get(times, i);
     404          49 :                 if (!t->listener) continue;
     405             : 
     406             :                 /*detach the listener from the observed node*/
     407           3 :                 evt = t->listener->sgprivate->UserPrivate;
     408           3 :                 t->listener->sgprivate->UserPrivate = NULL;
     409           3 :                 gf_dom_listener_del(t->listener, evt);
     410             : 
     411             :                 /*release our listener*/
     412           3 :                 gf_node_unregister(t->listener, NULL);
     413           3 :                 t->listener = NULL;
     414             :         }
     415         140 : }
     416             : 
     417          80 : void gf_smil_timing_delete_runtime_info(GF_Node *timed_elt, SMIL_Timing_RTI *rti)
     418             : {
     419             :         GF_SceneGraph *sg;
     420             : 
     421          80 :         if (!rti || !timed_elt) return;
     422             : 
     423          80 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Destruction\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     424          80 :         gf_free(rti->current_interval);
     425          80 :         gf_free(rti->next_interval);
     426             : 
     427             :         /* we inform the rootmost scene graph that this node will not need notification of the scene time anymore */
     428          80 :         sg = timed_elt->sgprivate->scenegraph;
     429          80 :         while (sg->parent_scene) sg = sg->parent_scene;
     430          80 :         gf_list_del_item(sg->smil_timed_elements, rti);
     431          80 :         gf_list_del_item(sg->modified_smil_timed_elements, rti);
     432             : 
     433             :         /*remove all associated listeners*/
     434          80 :         if (rti->timingp->begin) gf_smil_timing_reset_time_list(* rti->timingp->begin);
     435          80 :         if (rti->timingp->end) gf_smil_timing_reset_time_list(* rti->timingp->end);
     436             : 
     437          80 :         gf_free(rti);
     438             : }
     439             : 
     440             : GF_EXPORT
     441           1 : Bool gf_smil_timing_is_active(GF_Node *node)
     442             : {
     443           1 :         SMILTimingAttributesPointers *timingp = ((SVGTimedAnimBaseElement *)node)->timingp;
     444           1 :         if (!timingp || !timingp->runtime) return 0;
     445           1 :         return (timingp->runtime->status == SMIL_STATUS_ACTIVE);
     446             : }
     447             : 
     448       44179 : Bool gf_smil_notify_timed_elements(GF_SceneGraph *sg)
     449             : {
     450             :         SMIL_Timing_RTI *rti;
     451             :         u32 active_count, i;
     452             :         s32 ret;
     453             :         Bool do_loop;
     454       44179 :         if (!sg) return 0;
     455             : 
     456             :         active_count = 0;
     457             : 
     458             :         /*
     459             :                 Note: whenever a timed element is active, we trigger a gf_node_dirty_parent_graph so that the parent graph
     460             :                 is aware that some modifications may happen in the subtree. This is needed for cases where the subtree
     461             :                 is in an offscreen surface, to force retraversing of the subtree and thus apply the animation.
     462             : 
     463             :         */
     464             : 
     465             :         /* notify the new scene time to the register timed elements
     466             :            this might modify other timed elements or the element itself
     467             :            in which case it will be added to the list of modified elements */
     468       44179 :         i = 0;
     469             :         do_loop = 1;
     470       95752 :         while(do_loop && (rti = (SMIL_Timing_RTI *)gf_list_enum(sg->smil_timed_elements, &i))) {
     471        7729 :                 ret = gf_smil_timing_notify_time(rti, gf_node_get_scene_time((GF_Node*)rti->timed_elt) );
     472        7729 :                 switch (ret) {
     473           0 :                 case -1:
     474             :                         /* special case for discard element
     475             :                            when a discard element is executed, it automatically removes itself from the list of timed element
     476             :                            in the scene graph, we need to fix the index i. */
     477           0 :                         i--;
     478           0 :                         break;
     479             :                 case -2:
     480             :                         /* special return value, -2 means that the tested timed element is waiting to begin
     481             :                            Assuming that the timed elements are sorted by begin order,
     482             :                            the next ones don't need to be checked */
     483             :                         do_loop = 0;
     484             :                         break;
     485           0 :                 case -3:
     486             :                         /* special case for animation elements which do not need to be notified anymore,
     487             :                            but which require a tree traversal */
     488           0 :                         i--;
     489           0 :                         active_count ++;
     490           0 :                         gf_node_dirty_parent_graph(rti->timed_elt);
     491           0 :                         break;
     492        5534 :                 case 1:
     493        5534 :                         active_count++;
     494        5534 :                         gf_node_dirty_parent_graph(rti->timed_elt);
     495        5534 :                         break;
     496             :                 case 0:
     497             :                 default:
     498             :                         break;
     499             :                 }
     500             :         }
     501             : 
     502             :         /* notify the timed elements which have been modified either since the previous frame (updates, scripts) or
     503             :            because of the start/end/repeat of the previous notifications */
     504       44182 :         while (gf_list_count(sg->modified_smil_timed_elements)) {
     505             :                 /* first remove the modified smil timed element */
     506           3 :                 rti = gf_list_get(sg->modified_smil_timed_elements, 0);
     507           3 :                 gf_list_rem(sg->modified_smil_timed_elements, 0);
     508             : 
     509             :                 /* then remove it in the list of non modified (if it was there) */
     510           3 :                 gf_list_del_item(sg->smil_timed_elements, rti);
     511             : 
     512             :                 /* then insert it at its right position (in the sorted list of timed elements) */
     513           3 :                 gf_smil_timing_add_to_sg(sg, rti);
     514             : 
     515             :                 /* finally again notify this timed element */
     516           3 :                 rti->force_reevaluation = 1;
     517           3 :                 ret = gf_smil_timing_notify_time(rti, gf_node_get_scene_time((GF_Node*)rti->timed_elt) );
     518           3 :                 switch (ret) {
     519             :                 case -1:
     520             :                         break;
     521             :                 case -2:
     522             :                         break;
     523           0 :                 case -3:
     524           0 :                         active_count++;
     525           0 :                         gf_node_dirty_parent_graph(rti->timed_elt);
     526           0 :                         break;
     527           2 :                 case 1:
     528           2 :                         active_count++;
     529           2 :                         gf_node_dirty_parent_graph(rti->timed_elt);
     530           2 :                         break;
     531             :                 case 0:
     532             :                 default:
     533             :                         break;
     534             :                 }
     535             : 
     536             :         }
     537       44179 :         return (active_count>0);
     538             : }
     539             : 
     540             : /* evaluation function for the discard element
     541             :    returns 1 if the discard was executed
     542             :            0 otherwise
     543             : */
     544           0 : static Bool gf_smil_discard(SMIL_Timing_RTI *rti, Fixed scene_time)
     545             : {
     546             :         u32 nb_inst;
     547             :         SMIL_Time *begin;
     548           0 :         SVGTimedAnimBaseElement *tb = (SVGTimedAnimBaseElement *)rti->timed_elt;
     549           0 :         SMILTimingAttributesPointers *timingp = (SMILTimingAttributesPointers *)rti->timingp;
     550             :         GF_Node *target;
     551             : 
     552           0 :         if (!timingp) return 0;
     553             : 
     554           0 :         target = tb->xlinkp->href ? tb->xlinkp->href->target : NULL;
     555             : 
     556           0 :         begin = (timingp->begin ? (SMIL_Time *)gf_list_get(*timingp->begin, 0) : NULL);
     557             : 
     558           0 :         if (!begin) return 0;
     559           0 :         if (!GF_SMIL_TIME_IS_CLOCK(begin->type) ) return 0;
     560             : 
     561           0 :         if (begin->clock > scene_time) return 0;
     562             : 
     563           0 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SVG Composer] discarding element %s at time %f\n", target ? gf_node_get_log_name(target) : "None", scene_time));
     564             : 
     565           0 :         gf_smil_mark_modified(rti, 1);
     566             : 
     567             :         /*this takes care of cases where discard is a child of its target*/
     568           0 :         gf_node_register(rti->timed_elt, NULL);
     569           0 :         nb_inst = gf_node_get_num_instances(rti->timed_elt);
     570           0 :         if (target) gf_node_replace(target, NULL, 0);
     571           0 :         if (nb_inst == gf_node_get_num_instances(rti->timed_elt)) {
     572           0 :                 gf_node_unregister(rti->timed_elt, NULL);
     573             :                 /*after this the stack may be free'd*/
     574           0 :                 gf_node_replace(rti->timed_elt, NULL, 0);
     575             :         } else {
     576           0 :                 gf_node_unregister(rti->timed_elt, NULL);
     577             :         }
     578             :         return 1;
     579             : }
     580             : 
     581             : /* Animations are applied in their begin order first and then in document order.
     582             :    Whenever an animation (re)starts, it is placed at the end of the queue (potentially after frozen animations) */
     583          61 : static void gf_smil_reorder_anim(SMIL_Timing_RTI *rti)
     584             : {
     585          61 :         SMIL_Anim_RTI *rai = rti->rai;
     586          61 :         if (rai) {
     587          49 :                 gf_list_del_item(rai->owner->anims, rai);
     588          49 :                 gf_list_add(rai->owner->anims, rai);
     589          49 :                 gf_smil_anim_reset_variables(rai);
     590             :         }
     591          61 : }
     592             : 
     593             : /* Notifies the scene time to a timed element, potentially changing its status and triggering its evaluation
     594             :    Returns:
     595             :         0 if no rendering traversal is required,
     596             :         1 if a rendering traversal is required,
     597             :    -1 if the time node is a discard which has been deleted during this notification,
     598             :    -2 means that the timed element is waiting to begin,
     599             :    -3 means that the timed element is active but does not need further notifications (set without dur)
     600             :              but still requires a rendering traversal */
     601        7732 : s32 gf_smil_timing_notify_time(SMIL_Timing_RTI *rti, Double in_scene_time)
     602             : {
     603             :         s32 ret = 0;
     604             :         GF_DOM_Event evt;
     605        7732 :         SMILTimingAttributesPointers *timingp = rti->timingp;
     606        7732 :         Bool force_end = 0;
     607             : 
     608        7732 :         if (!timingp) return 0;
     609             : 
     610             :         /* if the scene time is the same as it was during the previous notification, it means that the
     611             :            animations are paused and we don't need to evaluate it again unless the force_reevaluation flag is set */
     612        7732 :         if ((rti->scene_time == in_scene_time) && (rti->force_reevaluation == 0)) return 0;
     613        7729 :         if (!rti->paused) rti->scene_time = in_scene_time;
     614        7729 :         rti->force_reevaluation = 0;
     615             : 
     616             :         /* for fraction events, in all cases we indicate that the scene needs redraw */
     617        7729 :         if (rti->evaluate_status == SMIL_TIMING_EVAL_FRACTION)
     618             :                 return 1;
     619             : 
     620        7729 :         if (rti->evaluate_status == SMIL_TIMING_EVAL_DISCARD) {
     621             :                 /* TODO: FIX ME discarding should send a begin event ? */
     622             :                 /* Since the discard can only be evaluated once, it unregisters itself
     623             :                    from the list of timed elements to be notified, so for this special case
     624             :                    we return -1 when the discard has actually been executed */
     625           0 :                 if (gf_smil_discard(rti, FLT2FIX(rti->scene_time))) return -1;
     626           0 :                 else return 0;
     627             :         }
     628             : 
     629        7729 :         gf_node_register(rti->timed_elt, NULL);
     630             : 
     631        7729 : waiting_to_begin:
     632        7729 :         if (rti->status == SMIL_STATUS_WAITING_TO_BEGIN) {
     633         396 :                 if (rti->current_interval->begin != -1 && rti->scene_time >= rti->current_interval->begin) {
     634             :                         /* if there is a computed interval with a definite begin value
     635             :                            and if that value is lesser than the scene time, then the animation becomes active */
     636          60 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Activating\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     637          60 :                         rti->status = SMIL_STATUS_ACTIVE;
     638             : 
     639          60 :                         if (rti->timed_elt->sgprivate->tag==TAG_LSR_conditional) {
     640             :                                 SVG_Element *e = (SVG_Element *)rti->timed_elt;
     641             :                                 /*activate conditional*/
     642           0 :                                 if (e->children) gf_node_traverse(e->children->node, NULL);
     643           0 :                                 rti->status = SMIL_STATUS_DONE;
     644             :                         } else {
     645          60 :                                 gf_smil_reorder_anim(rti);
     646             :                         }
     647             : 
     648             :                         memset(&evt, 0, sizeof(evt));
     649          60 :                         evt.type = GF_EVENT_BEGIN_EVENT;
     650          60 :                         evt.smil_event_time = rti->current_interval->begin;
     651          60 :                         gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
     652             :                 } else {
     653         336 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Evaluating (Not starting)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     654             :                         ret = -2;
     655             :                         goto exit;
     656             :                 }
     657             :         }
     658             : 
     659        7393 :         if (rti->status == SMIL_STATUS_ACTIVE) {
     660             :                 u32 cur_id;
     661             : 
     662        6952 :                 if (rti->current_interval->active_duration >= 0
     663        5327 :                         && rti->scene_time >= (rti->current_interval->begin + rti->current_interval->active_duration)) {
     664           9 : force_end:
     665           9 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Stopping \n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     666             : 
     667           9 :                         rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, NULL);
     668           9 :                         ret = rti->postpone;
     669             : 
     670           9 :                         if (timingp->fill && *timingp->fill == SMIL_FILL_FREEZE) {
     671           5 :                                 rti->status = SMIL_STATUS_FROZEN;
     672           5 :                                 rti->evaluate_status = SMIL_TIMING_EVAL_FREEZE;
     673           5 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to freeze\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     674           5 :                                 if (!rti->postpone) {
     675           0 :                                         rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
     676             :                                 }
     677             :                         } else {
     678           4 :                                 rti->status = SMIL_STATUS_DONE;
     679           4 :                                 rti->evaluate_status = SMIL_TIMING_EVAL_REMOVE;
     680           4 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to remove\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     681           4 :                                 if (!rti->postpone) {
     682           1 :                                         rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
     683             :                                 }
     684             :                         }
     685             : 
     686             :                         memset(&evt, 0, sizeof(evt));
     687           9 :                         evt.type = GF_EVENT_END_EVENT;
     688             :                         /* WARNING: begin + active_duration may be greater than 'now' because of force_end cases */
     689           9 :                         evt.smil_event_time = rti->current_interval->begin + rti->current_interval->active_duration;
     690           9 :                         gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
     691             : 
     692             :                 } else { /* the animation is still active */
     693             : 
     694        6943 :                         if (!timingp->restart || *timingp->restart == SMIL_RESTART_ALWAYS) {
     695        6838 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Checking for restart (always)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     696             : 
     697        6838 :                                 if (rti->next_interval->begin != -1 && rti->next_interval->begin < rti->scene_time) {
     698           1 :                                         *rti->current_interval = *rti->next_interval;
     699           1 :                                         gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time);
     700             : 
     701             :                                         /* mark that this element has been modified and
     702             :                                            need to be reinserted at its proper place in the list of timed elements in the scenegraph */
     703           1 :                                         gf_smil_mark_modified(rti, 0);
     704             : 
     705             :                                         /* if this is animation, reinserting the animation in the list of animations
     706             :                                            that targets this attribute, so that it is the last one */
     707           1 :                                         gf_smil_reorder_anim(rti);
     708             : 
     709             :                                         memset(&evt, 0, sizeof(evt));
     710           1 :                                         evt.type = GF_EVENT_BEGIN_EVENT;
     711           1 :                                         evt.smil_event_time = rti->current_interval->begin;
     712           1 :                                         gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
     713             :                                 }
     714             :                         }
     715             : 
     716        6943 :                         ret = rti->postpone;
     717             : 
     718        6943 :                         cur_id = rti->current_interval->nb_iterations;
     719        6943 :                         rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, &force_end);
     720        6943 :                         if (force_end) {
     721           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Forcing end (fill or remove)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     722             :                                 goto force_end;
     723             :                         }
     724        6943 :                         if (cur_id < rti->current_interval->nb_iterations) {
     725           6 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to repeat\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     726             :                                 memset(&evt, 0, sizeof(evt));
     727           6 :                                 evt.type = GF_EVENT_REPEAT_EVENT;
     728           6 :                                 evt.smil_event_time = rti->current_interval->begin + rti->current_interval->nb_iterations*rti->current_interval->simple_duration;
     729           6 :                                 evt.detail = rti->current_interval->nb_iterations;
     730           6 :                                 gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
     731             : 
     732           6 :                                 rti->evaluate_status = SMIL_TIMING_EVAL_REPEAT;
     733             :                         } else {
     734        6937 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to update\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     735        6937 :                                 rti->evaluate_status = SMIL_TIMING_EVAL_UPDATE;
     736             :                         }
     737             : 
     738        6943 :                         if (!rti->postpone) {
     739        1565 :                                 rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
     740             :                         }
     741             : 
     742             :                         /* special case for animations with unspecified simpleDur (not with media timed elements)
     743             :                            we need to indicate that this anim does not need to be notified anymore and that
     744             :                            it does not require tree traversal */
     745        6943 :                         if (gf_svg_is_animation_tag(rti->timed_elt->sgprivate->tag)
     746        5528 :                                 && (rti->current_interval->simple_duration==-1)
     747         450 :                                 && (rti->current_interval->active_duration==-1)
     748             :                            ) {
     749             :                                 /*GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph;
     750             :                                 while (sg->parent_scene) sg = sg->parent_scene;
     751             :                                 gf_list_del_item(sg->smil_timed_elements, rti);
     752             :                                 ret = -3;*/
     753             :                                 ret = 1;
     754             :                         }
     755             :                 }
     756             :         }
     757             : 
     758        7393 :         if ((rti->status == SMIL_STATUS_DONE) || (rti->status == SMIL_STATUS_FROZEN)) {
     759         450 :                 if (!timingp->restart || *timingp->restart != SMIL_RESTART_NEVER) {
     760             :                         /* Check changes in begin or end attributes */
     761         450 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Checking for restart when not active\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     762         450 :                         if (rti->next_interval->begin != -1) {
     763             :                                 Bool restart_timing = 0;
     764             :                                 /*next interval is right now*/
     765           0 :                                 if (rti->next_interval->begin == rti->current_interval->begin+rti->current_interval->active_duration)
     766             :                                         restart_timing = 1;
     767             : 
     768             :                                 /*switch intervals*/
     769           0 :                                 if (rti->next_interval->begin >= rti->current_interval->begin+rti->current_interval->active_duration) {
     770           0 :                                         *rti->current_interval = *rti->next_interval;
     771             : 
     772           0 :                                         gf_smil_timing_print_interval(rti, 1, rti->current_interval);
     773           0 :                                         gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time);
     774             : 
     775             :                                         /* mark that this element has been modified and
     776             :                                            need to be reinserted at its proper place in the list of timed elements in the scenegraph */
     777           0 :                                         gf_smil_mark_modified(rti, 0);
     778             :                                 } else {
     779           0 :                                         rti->next_interval->begin = -1;
     780             :                                 }
     781             : 
     782             :                                 /*if chaining to new interval, go to wait_for begin right now*/
     783           0 :                                 if (restart_timing) {
     784           0 :                                         rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
     785           0 :                                         rti->evaluate_status = SMIL_TIMING_EVAL_NONE;
     786           0 :                                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Returning to eval none status\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     787             :                                         ret = 0;
     788             :                                         goto waiting_to_begin;
     789             :                                 }
     790             :                                 /*otherwise move state to waiting for begin for next smil_timing evaluation, but
     791             :                                 don't change evaluate status for next anim evaluation*/
     792             :                                 else {
     793           0 :                                         rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
     794             :                                 }
     795             :                         } else {
     796             :                                 /*??? what is this ???*/
     797             :                                 //ret = 0;
     798             :                         }
     799           0 :                 } else if ((rti->status == SMIL_STATUS_DONE) &&
     800             :                            timingp->restart && (*timingp->restart == SMIL_RESTART_NEVER)) {
     801             :                         /* the timed element is done and cannot restart, we don't need to evaluate it anymore */
     802           0 :                         GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph;
     803           0 :                         while (sg->parent_scene) sg = sg->parent_scene;
     804           0 :                         gf_list_del_item(sg->smil_timed_elements, rti);
     805             :                         ret = -1;
     806             :                 }
     807             :         }
     808             : 
     809       14672 : exit:
     810        7729 :         gf_node_unregister(rti->timed_elt, NULL);
     811        7729 :         return ret;
     812             : }
     813             : 
     814             : /* returns a fraction between 0 and 1 of the elapsed time in the simple duration */
     815             : /* WARNING: According to SMIL (http://www.w3.org/TR/2005/REC-SMIL2-20051213/animation.html#animationNS-Fill,
     816             : see "Illustration of animation combining a partial repeat and fill="freeze"")
     817             : When a element is frozen, its normalized simple time is not necessarily 1,
     818             : an animation can be frozen in the middle of a repeatition */
     819        6952 : Fixed gf_smil_timing_get_normalized_simple_time(SMIL_Timing_RTI *rti, Double scene_time, Bool *force_end)
     820             : {
     821             :         Double activeTime;
     822             :         Double simpleTime;
     823             :         Fixed normalizedSimpleTime;
     824             : 
     825        6952 :         if (rti->current_interval->begin == -1) return 0;
     826             : 
     827             :         /* we define the active time as the elapsed time from the current activation of the element */
     828        6952 :         activeTime = scene_time - rti->current_interval->begin;
     829             : 
     830             :         /* Is the animation reaching the end of its active duration ? */
     831        6952 :         if (rti->current_interval->active_duration != -1 && activeTime >= rti->current_interval->active_duration) {
     832             : 
     833             :                 /* we clamp the active time to its maximum value */
     834             :                 activeTime = rti->current_interval->active_duration;
     835             : 
     836             :                 /* if the simple duration is defined, then we can take iterations into account */
     837           9 :                 if (rti->current_interval->simple_duration>0) {
     838             : 
     839           9 :                         if (activeTime == rti->current_interval->simple_duration*(rti->current_interval->nb_iterations+1)) {
     840             :                                 return FIX_ONE;
     841             :                         } else {
     842             :                                 goto end;
     843             :                         }
     844             :                 } else {
     845             :                         /* If the element does not define its simple duration, but it has an active duration,
     846             :                            and we are past this active duration, we assume it's blocked in final state
     847             :                            We should take into account fill behavior*/
     848           0 :                         rti->current_interval->nb_iterations = 0;
     849           0 :                         if (rti->timingp->fill && *(rti->timingp->fill) == SMIL_FILL_FREEZE) {
     850           0 :                                 if (rti->current_interval->repeat_duration == rti->current_interval->simple_duration) {
     851             :                                         return FIX_ONE;
     852             :                                 } else {
     853           0 :                                         return  rti->normalized_simple_time;
     854             :                                 }
     855             :                         } else {
     856             :                                 return 0;
     857             :                         }
     858             :                 }
     859             :         }
     860             : 
     861        6944 : end:
     862             :         /* if the simple duration is defined, then we can take iterations into account */
     863        6944 :         if (rti->current_interval->simple_duration>0) {
     864             : 
     865             :                 /* if we are active but frozen or done (animate-elem-65-t, animate-elem-222-t)
     866             :                 (see The rule to apply to compute the active duration of an element with min or max specified) */
     867        5919 :                 if ((activeTime >= rti->current_interval->repeat_duration) && rti->current_interval->min_active) {
     868             :                         /* freeze the normalized simple time */
     869           0 :                         if (force_end) *force_end = 1;
     870           0 :                         if (rti->timingp->fill && *(rti->timingp->fill) == SMIL_FILL_FREEZE) {
     871           0 :                                 if (rti->current_interval->repeat_duration == rti->current_interval->simple_duration) {
     872             :                                         return FIX_ONE;
     873             :                                 } else {
     874           0 :                                         return  rti->normalized_simple_time;
     875             :                                 }
     876             :                         }
     877             :                 }
     878             :                 /* we update the number of iterations */
     879        5919 :                 rti->current_interval->nb_iterations = (u32)floor(activeTime / rti->current_interval->simple_duration);
     880             :         } else {
     881             :                 /* If the element does not define its simple duration, we assume it's blocked in final state
     882             :                    Is this correct ? */
     883        1025 :                 rti->current_interval->nb_iterations = 0;
     884             :                 //GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Error Computing Normalized Simple Time while simple duration is indefinite\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     885        1025 :                 return FIX_ONE;
     886             :         }
     887             : 
     888             :         /* We compute the simple time by removing time taken by previous iterations */
     889        5919 :         simpleTime = activeTime - rti->current_interval->simple_duration * rti->current_interval->nb_iterations;
     890             : 
     891             :         /* Then we clamp the simple time to be sure it is between 0 and simple duration */
     892        5919 :         simpleTime = MAX(0, simpleTime);
     893        5919 :         simpleTime = MIN(rti->current_interval->simple_duration, simpleTime);
     894             : 
     895             :         /* Then we normalize to have a value between 0 and 1 */
     896        5919 :         normalizedSimpleTime = FLT2FIX(simpleTime / rti->current_interval->simple_duration);
     897             : 
     898        5919 :         return normalizedSimpleTime;
     899             : }
     900             : 
     901             : /* This function is called when a modification to the node has been made (scripts, updates or events ...) */
     902           8 : void gf_smil_timing_modified(GF_Node *node, GF_FieldInfo *field)
     903             : {
     904             :         SMIL_Timing_RTI *rti;
     905           8 :         SMILTimingAttributesPointers *timingp = ((SVGTimedAnimBaseElement *)node)->timingp;
     906             : 
     907           8 :         if (!timingp) return;
     908           8 :         rti = timingp->runtime;
     909           8 :         if (!rti) return;
     910             : 
     911           8 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Modification\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     912           8 :         if (rti->current_interval->begin == -1) {
     913           0 :                 gf_smil_timing_get_next_interval(rti, 1, rti->current_interval, gf_node_get_scene_time((GF_Node*)rti->timed_elt));
     914             :         } else {
     915             :                 /* we don't have the right to modify the end of an element if it's not in unresolved state */
     916           8 :                 if (rti->current_interval->end == -1) gf_smil_timing_get_interval_end(rti, rti->current_interval);
     917             :                 if ((0) && rti->current_interval->end == -2) {
     918             :                         /* TODO: check if the interval can be discarded if end = -2,
     919             :                            probably no, because the interval is currently running*/
     920             :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
     921             :                         rti->current_interval->begin = -1;
     922             :                         rti->current_interval->end = -1;
     923             :                         return;
     924             :                 }
     925             : 
     926           8 :                 gf_smil_timing_compute_active_duration(rti, rti->current_interval);
     927           8 :                 gf_smil_timing_print_interval(rti, 1, rti->current_interval);
     928             :         }
     929           8 :         gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, gf_node_get_scene_time((GF_Node*)rti->timed_elt));
     930             : 
     931             :         /* mark that this element has been modified and
     932             :            need to be reinserted at its proper place in the list of timed elements in the scenegraph */
     933           8 :         gf_smil_mark_modified(rti, 0);
     934             : }
     935             : 
     936             : 
     937             : /* Tries to resolve event-based or sync-based time values
     938             :    Used in parsing, to determine if a timed element can be initialized */
     939         324 : Bool gf_svg_resolve_smil_times(GF_Node *anim, void *event_base_element,
     940             :                                GF_List *smil_times, Bool is_end, const char *node_name)
     941             : {
     942             :         u32 i, done, count;
     943             : 
     944             :         done = 0;
     945         324 :         count = gf_list_count(smil_times);
     946         411 :         for (i=0; i<count; i++) {
     947          87 :                 SMIL_Time *t = (SMIL_Time *)gf_list_get(smil_times, i);
     948             : 
     949          87 :                 if (t->type != GF_SMIL_TIME_EVENT) {
     950          81 :                         done++;
     951          81 :                         continue;
     952             :                 }
     953           6 :                 if (!t->element_id) {
     954           2 :                         if (!t->element) t->element = (GF_Node *)event_base_element;
     955           2 :                         done++;
     956           2 :                         continue;
     957             :                 }
     958             :                 /*commented out because it breaks regular anims (cf interact-pevents-07-t.svg)*/
     959             : //              if (node_name && strcmp(node_name, t->element_id)) continue;
     960             : 
     961           4 :                 t->element = gf_sg_find_node_by_name(anim->sgprivate->scenegraph, t->element_id);
     962           4 :                 if (t->element) {
     963           4 :                         gf_free(t->element_id);
     964           4 :                         t->element_id = NULL;
     965           4 :                         done++;
     966             :                 }
     967             :         }
     968             :         /*lacuna value of discard is 0*/
     969         324 :         if (!count && !is_end && (anim->sgprivate->tag==TAG_SVG_discard) ) {
     970             :                 SMIL_Time *t;
     971           0 :                 GF_SAFEALLOC(t, SMIL_Time);
     972           0 :                 if (!t) {
     973           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Timing] Failed to alloc SMIL time for discard\n"));
     974             :                         return 0;
     975             :                 }
     976           0 :                 t->clock = 0;
     977           0 :                 t->type = GF_SMIL_TIME_CLOCK;
     978           0 :                 gf_list_add(smil_times, t);
     979           0 :                 return 1;
     980             :         }
     981             : 
     982         324 :         if (done!=count) return 0;
     983         324 :         return 1;
     984             : }
     985             : 
     986             : GF_EXPORT
     987           0 : void gf_smil_timing_insert_clock(GF_Node *elt, Bool is_end, Double clock)
     988             : {
     989             :         u32 i, count, found;
     990             :         SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)elt;
     991             :         SMIL_Time *begin;
     992             :         GF_List *l;
     993           0 :         GF_SAFEALLOC(begin, SMIL_Time);
     994           0 :         if (!begin) {
     995           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Timing] Failed to alloc SMIL begin value\n"));
     996             :                 return;
     997             :         }
     998             : 
     999           0 :         begin->type = GF_SMIL_TIME_EVENT_RESOLVED;
    1000           0 :         begin->clock = clock;
    1001             : 
    1002           0 :         l = is_end ? *timed->timingp->end : *timed->timingp->begin;
    1003             : 
    1004             :         found = 0;
    1005           0 :         count = gf_list_count(l);
    1006           0 :         for (i=0; i<count; i++) {
    1007           0 :                 SMIL_Time *first = (SMIL_Time *)gf_list_get(l, i);
    1008             :                 /*remove past instanciations*/
    1009           0 :                 if ((first->type==GF_SMIL_TIME_EVENT_RESOLVED) && (first->clock < begin->clock)) {
    1010           0 :                         gf_list_rem(l, i);
    1011           0 :                         gf_free(first);
    1012           0 :                         i--;
    1013           0 :                         count--;
    1014           0 :                         continue;
    1015             :                 }
    1016           0 :                 if ( (first->type == GF_SMIL_TIME_INDEFINITE)
    1017           0 :                         || ( (first->type == GF_SMIL_TIME_CLOCK) && (first->clock > begin->clock) )
    1018             :                    ) {
    1019           0 :                         gf_list_insert(l, begin, i);
    1020             :                         found = 1;
    1021             :                         break;
    1022             :                 }
    1023             :         }
    1024           0 :         if (!found) gf_list_add(l, begin);
    1025             : 
    1026             :         /* call gf_smil_timing_modified */
    1027           0 :         gf_node_changed(elt, NULL);
    1028             : }
    1029             : 
    1030        2807 : void gf_smil_timing_pause(GF_Node *node)
    1031             : {
    1032        2807 :         if (node && ((SVGTimedAnimBaseElement *)node)->timingp  && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) {
    1033             :                 SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime;
    1034           0 :                 if (rti->status<=SMIL_STATUS_ACTIVE) rti->paused = 1;
    1035             :         }
    1036        2807 : }
    1037             : 
    1038        2807 : void gf_smil_timing_resume(GF_Node *node)
    1039             : {
    1040        2807 :         if (node && ((SVGTimedAnimBaseElement *)node)->timingp  && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) {
    1041             :                 SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime;
    1042           0 :                 rti->paused = 0;
    1043             :         }
    1044        2807 : }
    1045             : 
    1046          11 : void gf_smil_set_evaluation_callback(GF_Node *node, gf_sg_smil_evaluate smil_evaluate)
    1047             : {
    1048          11 :         if (node && ((SVGTimedAnimBaseElement *)node)->timingp  && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) {
    1049             :                 SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime;
    1050          11 :                 rti->evaluate = smil_evaluate;
    1051             :         }
    1052          11 : }
    1053             : 
    1054        1416 : GF_Node *gf_smil_get_element(SMIL_Timing_RTI *rti)
    1055             : {
    1056        1416 :         return rti->timed_elt;
    1057             : }
    1058             : 
    1059        2807 : Double gf_smil_get_media_duration(SMIL_Timing_RTI *rti)
    1060             : {
    1061        2807 :         return rti ? rti->media_duration : 0.0;
    1062             : }
    1063             : 
    1064             : 
    1065             : #endif /*GPAC_DISABLE_SVG*/

Generated by: LCOV version 1.13