Coverage Report

Created: 2026-02-26 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/scenegraph/smil_timing.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Cyril Concolato
5
 *      Copyright (c) Telecom ParisTech 2004-2022
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
static void gf_smil_timing_null_timed_function(SMIL_Timing_RTI *rti, Fixed normalized_scene_time, GF_SGSMILTimingEvalState state)
32
0
{
33
0
}
34
35
static void gf_smil_timing_print_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval)
36
0
{
37
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, (current ? "Current " : "   Next "));
39
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("Interval - "));
40
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("begin: %.2f", interval->begin));
41
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, (" - end: %.2f", interval->end));
42
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, (" - simple dur: %.2f - active dur: %.2f\n",interval->simple_duration, interval->active_duration));
43
0
}
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
static void gf_smil_timing_compute_active_duration(SMIL_Timing_RTI *rti, SMIL_Interval *interval)
49
0
{
50
0
  Bool clamp_active_duration;
51
0
  Bool isDurDefined, isRepeatCountDefined, isRepeatDurDefined, isMinDefined, isMaxDefined, isRepeatDurIndefinite, isRepeatCountIndefinite, isMediaDuration;
52
0
  SMILTimingAttributesPointers *timingp = rti->timingp;
53
54
  /* TODO: check if the test on begin is right and needed */
55
0
  if (!timingp/* || interval->begin == -1*/) return;
56
57
0
  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
0
  }
62
63
0
  isDurDefined = (timingp->dur && timingp->dur->type == SMIL_DURATION_DEFINED);
64
0
  isMediaDuration = (timingp->dur && (timingp->dur->type == SMIL_DURATION_MEDIA) && (rti->media_duration>=0) );
65
0
  isRepeatCountDefined = (timingp->repeatCount && timingp->repeatCount->type == SMIL_REPEATCOUNT_DEFINED);
66
0
  isRepeatCountIndefinite = (timingp->repeatCount && timingp->repeatCount->type == SMIL_REPEATCOUNT_INDEFINITE);
67
0
  isRepeatDurDefined = (timingp->repeatDur && timingp->repeatDur->type == SMIL_DURATION_DEFINED);
68
0
  isRepeatDurIndefinite = (timingp->repeatDur && timingp->repeatDur->type == SMIL_DURATION_INDEFINITE);
69
70
  /* Step 1: Computing active duration using repeatDur and repeatCount */
71
0
  if (isDurDefined || isMediaDuration) {
72
0
    interval->simple_duration = isMediaDuration ? rti->media_duration : timingp->dur->clock_value;
73
74
0
    if (isRepeatCountDefined && !isRepeatDurDefined) {
75
0
      interval->repeat_duration = FIX2FLT(timingp->repeatCount->count) * interval->simple_duration;
76
0
    } else if (!isRepeatCountDefined && isRepeatDurDefined) {
77
0
      interval->repeat_duration = timingp->repeatDur->clock_value;
78
0
    } else if (!isRepeatCountDefined && !isRepeatDurDefined) {
79
0
      if (isRepeatDurIndefinite || isRepeatCountIndefinite) {
80
0
        interval->repeat_duration = -1;
81
0
      } else {
82
0
        interval->repeat_duration = interval->simple_duration;
83
0
      }
84
0
    } else {
85
0
      interval->repeat_duration = MIN(timingp->repeatDur->clock_value,
86
0
                                      FIX2FLT(timingp->repeatCount->count) * interval->simple_duration);
87
0
    }
88
0
  } else {
89
90
    /* simple_duration is indefinite */
91
0
    interval->simple_duration = -1;
92
93
    /* we can ignore repeatCount to compute active_duration */
94
0
    if (!isRepeatDurDefined) {
95
0
      interval->repeat_duration = -1;
96
0
    } else {
97
0
      interval->repeat_duration = timingp->repeatDur->clock_value;
98
0
    }
99
0
  }
100
101
0
  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
0
  if (interval->end < 0) {
105
    /* interval->active_duration stays as is */
106
0
  } else {
107
0
    if (interval->active_duration >= 0)
108
0
      interval->active_duration = MIN(interval->active_duration, interval->end - interval->begin);
109
0
    else
110
0
      interval->active_duration = interval->end - interval->begin;
111
0
  }
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
0
  clamp_active_duration = 1;
118
  /* testing for presence of min and max because some elements may not have them: eg SVG audio */
119
0
  isMinDefined = (timingp->min && timingp->min->type == SMIL_DURATION_DEFINED);
120
0
  isMaxDefined = (timingp->max && timingp->max->type == SMIL_DURATION_DEFINED);
121
0
  if (isMinDefined && isMaxDefined &&
122
0
          timingp->max->clock_value < timingp->min->clock_value) {
123
0
    clamp_active_duration = 0;
124
0
  }
125
0
  if (clamp_active_duration) {
126
0
    if (isMinDefined) {
127
0
      if ((interval->active_duration >= 0) &&
128
0
              (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
0
      }
139
0
    }
140
0
    if (isMaxDefined) {
141
0
      if ((interval->active_duration >= 0 && interval->active_duration >= timingp->max->clock_value) ||
142
0
              interval->active_duration == -1) {
143
0
        interval->active_duration = timingp->max->clock_value;
144
0
      }
145
0
    }
146
0
  }
147
148
0
}
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
void gf_smil_set_media_duration(SMIL_Timing_RTI *rti, Double media_duration)
154
0
{
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
static void gf_smil_timing_get_interval_end(SMIL_Timing_RTI *rti, SMIL_Interval *interval)
170
0
{
171
0
  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
0
  interval->end = -2;
177
178
0
  end_count = (rti->timingp->end ? gf_list_count(*rti->timingp->end) : 0);
179
  /* trying to find a matching end */
180
0
  if (end_count > 0) {
181
0
    for (j = 0; j < end_count; j++) {
182
0
      SMIL_Time *end = (SMIL_Time*)gf_list_get(*rti->timingp->end, j);
183
0
      if ( GF_SMIL_TIME_IS_CLOCK(end->type) )  {
184
0
        if( end->clock >= interval->begin) {
185
0
          interval->end = end->clock;
186
0
          break;
187
0
        }
188
0
      } else {
189
        /* an unresolved or indefinite value is always good */
190
0
        interval->end = -1;
191
0
        break;
192
0
      }
193
0
    }
194
0
  } else {
195
0
    interval->end = -1;
196
0
  }
197
0
}
198
199
static void gf_smil_timing_get_first_interval(SMIL_Timing_RTI *rti)
200
0
{
201
0
  u32 i, count;
202
0
  if (!rti || !rti->current_interval) return;
203
  
204
0
  memset(rti->current_interval, 0, sizeof(SMIL_Interval));
205
0
  rti->current_interval->begin = -1;
206
0
  count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0);
207
0
  for (i = 0; i < count; i ++) {
208
0
    SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i);
209
0
    if (GF_SMIL_TIME_IS_CLOCK(begin->type)) {
210
0
      rti->current_interval->begin = begin->clock;
211
0
      break;
212
0
    }
213
0
  }
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
0
  if (rti->current_interval->begin == -1 && count == 0) {
217
    /* except for LASeR Conditional element*/
218
0
    if (rti->timed_elt->sgprivate->tag != TAG_LSR_conditional) {
219
0
      rti->current_interval->begin = 0;
220
0
    } else {
221
0
      return;
222
0
    }
223
0
  }
224
225
  /* this is the first time we check the interval */
226
0
  gf_smil_timing_get_interval_end(rti, rti->current_interval);
227
0
  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
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
    rti->current_interval->begin = -1;
232
0
    rti->current_interval->end = -1;
233
0
    return;
234
0
  }
235
236
0
  gf_smil_timing_compute_active_duration(rti, rti->current_interval);
237
0
  gf_smil_timing_print_interval(rti, 1, rti->current_interval);
238
0
}
239
240
static Bool gf_smil_timing_get_next_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval, Double scene_time)
241
0
{
242
0
  u32 i, count;
243
0
  if (!interval) return GF_FALSE;
244
  
245
0
  memset(interval, 0, sizeof(SMIL_Interval));
246
0
  interval->begin = -1;
247
248
0
  count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0);
249
0
  for (i = 0; i < count; i ++) {
250
0
    SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i);
251
0
    if (GF_SMIL_TIME_IS_CLOCK(begin->type)) {
252
0
      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
0
      interval->begin = begin->clock;
255
0
      break;
256
//      }
257
0
    }
258
0
  }
259
0
  if (interval->begin != -1) {
260
0
    gf_smil_timing_get_interval_end(rti, interval);
261
0
    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_COMPTIME, ("[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
0
      return 0;
267
0
    }
268
0
    gf_smil_timing_compute_active_duration(rti, interval);
269
0
    gf_smil_timing_print_interval(rti, current, interval);
270
0
    return 1;
271
0
  } else {
272
0
    return 0;
273
0
  }
274
0
}
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
static Bool gf_smil_timing_add_to_sg(GF_SceneGraph *sg, SMIL_Timing_RTI *rti)
282
0
{
283
0
  if (rti->current_interval->begin != -1) {
284
0
    SMIL_Timing_RTI *cur_rti = NULL;
285
0
    u32 i;
286
287
0
    for (i = 0; i < gf_list_count(sg->smil_timed_elements); i++) {
288
0
      cur_rti = (SMIL_Timing_RTI *)gf_list_get(sg->smil_timed_elements, i);
289
0
      if (cur_rti->current_interval->begin > rti->current_interval->begin) break;
290
0
    }
291
0
    gf_list_insert(sg->smil_timed_elements, rti, i);
292
0
    return 1;
293
0
  }
294
0
  return 0;
295
0
}
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
static void gf_smil_mark_modified(SMIL_Timing_RTI *rti, Bool remove)
301
0
{
302
0
  GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph;
303
0
  while (sg->parent_scene) sg = sg->parent_scene;
304
0
  if (remove) {
305
0
    gf_list_del_item(sg->modified_smil_timed_elements, rti);
306
0
  } else {
307
0
    if (gf_list_find(sg->modified_smil_timed_elements, rti) == -1) {
308
0
      gf_list_add(sg->modified_smil_timed_elements, rti);
309
0
    }
310
0
  }
311
0
}
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
void gf_smil_timing_init_runtime_info(GF_Node *timed_elt)
319
0
{
320
0
  GF_SceneGraph *sg;
321
0
  SMIL_Timing_RTI *rti;
322
0
  SMILTimingAttributesPointers *timingp;
323
0
  u32 tag = gf_node_get_tag(timed_elt);
324
0
  SVGAllAttributes all_atts;
325
0
  SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)timed_elt;
326
327
0
  gf_svg_flatten_attributes((SVG_Element *)e, &all_atts);
328
0
  e->timingp = gf_malloc(sizeof(SMILTimingAttributesPointers));
329
0
  e->timingp->begin   = all_atts.begin;
330
0
  e->timingp->clipBegin = all_atts.clipBegin;
331
0
  e->timingp->clipEnd   = all_atts.clipEnd;
332
0
  e->timingp->dur     = all_atts.dur;
333
0
  e->timingp->end     = all_atts.end;
334
0
  e->timingp->fill    = all_atts.smil_fill;
335
0
  e->timingp->max     = all_atts.max;
336
0
  e->timingp->min     = all_atts.min;
337
0
  e->timingp->repeatCount = all_atts.repeatCount;
338
0
  e->timingp->repeatDur = all_atts.repeatDur;
339
0
  e->timingp->restart   = all_atts.restart;
340
0
  timingp = e->timingp;
341
0
  if (!timingp) return;
342
343
0
  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
0
    if (!e->timingp->dur) {
352
0
      GF_FieldInfo info;
353
0
      gf_node_get_attribute_by_tag((GF_Node *)e, TAG_SVG_ATT_dur, 1, 0, &info);
354
0
      e->timingp->dur = (SMIL_Duration *)info.far_ptr;
355
0
      e->timingp->dur->type = SMIL_DURATION_MEDIA;
356
0
    }
357
0
  }
358
359
0
  GF_SAFEALLOC(rti, SMIL_Timing_RTI)
360
0
  if (!rti) {
361
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Timing] Failed to alloc SMIL timing RTI\n"));
362
0
    return;
363
0
  }
364
0
  timingp->runtime = rti;
365
0
  rti->timed_elt = timed_elt;
366
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
  rti->timingp = timingp;
369
0
  rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
370
0
  rti->evaluate_status = SMIL_TIMING_EVAL_NONE;
371
0
  rti->evaluate = gf_smil_timing_null_timed_function;
372
0
  rti->scene_time = -1;
373
0
  rti->force_reevaluation = 0;
374
0
  rti->media_duration = -1;
375
376
0
  GF_SAFEALLOC(rti->current_interval, SMIL_Interval);
377
0
  if (!rti->current_interval) {
378
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Timing] Failed to alloc SMIL timing current interval\n"));
379
0
    return;
380
0
  }
381
0
  gf_smil_timing_get_first_interval(rti);
382
0
  GF_SAFEALLOC(rti->next_interval, SMIL_Interval);
383
0
  if (!rti->next_interval) {
384
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Timing] Failed to alloc SMIL timing next interval\n"));
385
0
    return;
386
0
  }
387
0
  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
0
  sg = timed_elt->sgprivate->scenegraph;
393
0
  while (sg->parent_scene) sg = sg->parent_scene;
394
0
  gf_smil_timing_add_to_sg(sg, rti);
395
0
}
396
397
398
static void gf_smil_timing_reset_time_list(GF_List *times)
399
0
{
400
0
  GF_DOMEventTarget *evt;
401
0
  u32 i;
402
0
  for (i=0; i<gf_list_count(times); i++) {
403
0
    SMIL_Time *t = gf_list_get(times, i);
404
0
    if (!t->listener) continue;
405
406
    /*detach the listener from the observed node*/
407
0
    evt = t->listener->sgprivate->UserPrivate;
408
0
    t->listener->sgprivate->UserPrivate = NULL;
409
0
    gf_dom_listener_del(t->listener, evt);
410
411
    /*release our listener*/
412
0
    gf_node_unregister(t->listener, NULL);
413
0
    t->listener = NULL;
414
0
  }
415
0
}
416
417
void gf_smil_timing_delete_runtime_info(GF_Node *timed_elt, SMIL_Timing_RTI *rti)
418
0
{
419
0
  GF_SceneGraph *sg;
420
421
0
  if (!rti || !timed_elt) return;
422
423
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
  gf_free(rti->current_interval);
425
0
  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
0
  sg = timed_elt->sgprivate->scenegraph;
429
0
  while (sg->parent_scene) sg = sg->parent_scene;
430
0
  gf_list_del_item(sg->smil_timed_elements, rti);
431
0
  gf_list_del_item(sg->modified_smil_timed_elements, rti);
432
433
  /*remove all associated listeners*/
434
0
  if (rti->timingp->begin) gf_smil_timing_reset_time_list(* rti->timingp->begin);
435
0
  if (rti->timingp->end) gf_smil_timing_reset_time_list(* rti->timingp->end);
436
437
0
  gf_free(rti);
438
0
}
439
440
GF_EXPORT
441
Bool gf_smil_timing_is_active(GF_Node *node)
442
0
{
443
0
  SMILTimingAttributesPointers *timingp = ((SVGTimedAnimBaseElement *)node)->timingp;
444
0
  if (!timingp || !timingp->runtime) return 0;
445
0
  return (timingp->runtime->status == SMIL_STATUS_ACTIVE);
446
0
}
447
448
Bool gf_smil_notify_timed_elements(GF_SceneGraph *sg)
449
0
{
450
0
  SMIL_Timing_RTI *rti;
451
0
  u32 active_count, i;
452
0
  s32 ret;
453
0
  Bool do_loop;
454
0
  if (!sg) return 0;
455
456
0
  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
0
  i = 0;
469
0
  do_loop = 1;
470
0
  while(do_loop && (rti = (SMIL_Timing_RTI *)gf_list_enum(sg->smil_timed_elements, &i))) {
471
0
    ret = gf_smil_timing_notify_time(rti, gf_node_get_scene_time((GF_Node*)rti->timed_elt) );
472
0
    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
0
    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
0
      do_loop = 0;
484
0
      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
0
    case 1:
493
0
      active_count++;
494
0
      gf_node_dirty_parent_graph(rti->timed_elt);
495
0
      break;
496
0
    case 0:
497
0
    default:
498
0
      break;
499
0
    }
500
0
  }
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
0
  while (gf_list_count(sg->modified_smil_timed_elements)) {
505
    /* first remove the modified smil timed element */
506
0
    rti = gf_list_get(sg->modified_smil_timed_elements, 0);
507
0
    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
0
    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
0
    gf_smil_timing_add_to_sg(sg, rti);
514
515
    /* finally again notify this timed element */
516
0
    rti->force_reevaluation = 1;
517
0
    ret = gf_smil_timing_notify_time(rti, gf_node_get_scene_time((GF_Node*)rti->timed_elt) );
518
0
    switch (ret) {
519
0
    case -1:
520
0
      break;
521
0
    case -2:
522
0
      break;
523
0
    case -3:
524
0
      active_count++;
525
0
      gf_node_dirty_parent_graph(rti->timed_elt);
526
0
      break;
527
0
    case 1:
528
0
      active_count++;
529
0
      gf_node_dirty_parent_graph(rti->timed_elt);
530
0
      break;
531
0
    case 0:
532
0
    default:
533
0
      break;
534
0
    }
535
536
0
  }
537
0
  return (active_count>0);
538
0
}
539
540
/* evaluation function for the discard element
541
   returns 1 if the discard was executed
542
           0 otherwise
543
*/
544
static Bool gf_smil_discard(SMIL_Timing_RTI *rti, Fixed scene_time)
545
0
{
546
0
  u32 nb_inst;
547
0
  SMIL_Time *begin;
548
0
  SVGTimedAnimBaseElement *tb = (SVGTimedAnimBaseElement *)rti->timed_elt;
549
0
  SMILTimingAttributesPointers *timingp = (SMILTimingAttributesPointers *)rti->timingp;
550
0
  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_COMPTIME, ("[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
0
  } else {
576
0
    gf_node_unregister(rti->timed_elt, NULL);
577
0
  }
578
0
  return 1;
579
0
}
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
static void gf_smil_reorder_anim(SMIL_Timing_RTI *rti)
584
0
{
585
0
  SMIL_Anim_RTI *rai = rti->rai;
586
0
  if (rai) {
587
0
    gf_list_del_item(rai->owner->anims, rai);
588
0
    gf_list_add(rai->owner->anims, rai);
589
0
    gf_smil_anim_reset_variables(rai);
590
0
  }
591
0
}
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
s32 gf_smil_timing_notify_time(SMIL_Timing_RTI *rti, Double in_scene_time)
602
0
{
603
0
  s32 ret = 0;
604
0
  GF_DOM_Event evt;
605
0
  SMILTimingAttributesPointers *timingp = rti->timingp;
606
0
  Bool force_end = 0;
607
608
0
  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
0
  if ((rti->scene_time == in_scene_time) && (rti->force_reevaluation == 0)) return 0;
613
0
  if (!rti->paused) rti->scene_time = in_scene_time;
614
0
  rti->force_reevaluation = 0;
615
616
  /* for fraction events, in all cases we indicate that the scene needs redraw */
617
0
  if (rti->evaluate_status == SMIL_TIMING_EVAL_FRACTION)
618
0
    return 1;
619
620
0
  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
0
  }
628
629
0
  gf_node_register(rti->timed_elt, NULL);
630
631
0
waiting_to_begin:
632
0
  if (rti->status == SMIL_STATUS_WAITING_TO_BEGIN) {
633
0
    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
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
      rti->status = SMIL_STATUS_ACTIVE;
638
639
0
      if (rti->timed_elt->sgprivate->tag==TAG_LSR_conditional) {
640
0
        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
0
      } else {
645
0
        gf_smil_reorder_anim(rti);
646
0
      }
647
648
0
      memset(&evt, 0, sizeof(evt));
649
0
      evt.type = GF_EVENT_BEGIN_EVENT;
650
0
      evt.smil_event_time = rti->current_interval->begin;
651
0
      gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
652
0
    } else {
653
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
      ret = -2;
655
0
      goto exit;
656
0
    }
657
0
  }
658
659
0
  if (rti->status == SMIL_STATUS_ACTIVE) {
660
0
    u32 cur_id;
661
662
0
    if (rti->current_interval->active_duration >= 0
663
0
            && rti->scene_time >= (rti->current_interval->begin + rti->current_interval->active_duration)) {
664
0
force_end:
665
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
      rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, NULL);
668
0
      ret = rti->postpone;
669
670
0
      if (timingp->fill && *timingp->fill == SMIL_FILL_FREEZE) {
671
0
        rti->status = SMIL_STATUS_FROZEN;
672
0
        rti->evaluate_status = SMIL_TIMING_EVAL_FREEZE;
673
0
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
        if (!rti->postpone) {
675
0
          rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
676
0
        }
677
0
      } else {
678
0
        rti->status = SMIL_STATUS_DONE;
679
0
        rti->evaluate_status = SMIL_TIMING_EVAL_REMOVE;
680
0
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
        if (!rti->postpone) {
682
0
          rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
683
0
        }
684
0
      }
685
686
0
      memset(&evt, 0, sizeof(evt));
687
0
      evt.type = GF_EVENT_END_EVENT;
688
      /* WARNING: begin + active_duration may be greater than 'now' because of force_end cases */
689
0
      evt.smil_event_time = rti->current_interval->begin + rti->current_interval->active_duration;
690
0
      gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
691
692
0
    } else { /* the animation is still active */
693
694
0
      if (!timingp->restart || *timingp->restart == SMIL_RESTART_ALWAYS) {
695
0
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
        if (rti->next_interval->begin != -1 && rti->next_interval->begin < rti->scene_time) {
698
0
          *rti->current_interval = *rti->next_interval;
699
0
          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
0
          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
0
          gf_smil_reorder_anim(rti);
708
709
0
          memset(&evt, 0, sizeof(evt));
710
0
          evt.type = GF_EVENT_BEGIN_EVENT;
711
0
          evt.smil_event_time = rti->current_interval->begin;
712
0
          gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
713
0
        }
714
0
      }
715
716
0
      ret = rti->postpone;
717
718
0
      cur_id = rti->current_interval->nb_iterations;
719
0
      rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, &force_end);
720
0
      if (force_end) {
721
0
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
        goto force_end;
723
0
      }
724
0
      if (cur_id < rti->current_interval->nb_iterations) {
725
0
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
        memset(&evt, 0, sizeof(evt));
727
0
        evt.type = GF_EVENT_REPEAT_EVENT;
728
0
        evt.smil_event_time = rti->current_interval->begin + rti->current_interval->nb_iterations*rti->current_interval->simple_duration;
729
0
        evt.detail = rti->current_interval->nb_iterations;
730
0
        gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);
731
732
0
        rti->evaluate_status = SMIL_TIMING_EVAL_REPEAT;
733
0
      } else {
734
0
        GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
        rti->evaluate_status = SMIL_TIMING_EVAL_UPDATE;
736
0
      }
737
738
0
      if (!rti->postpone) {
739
0
        rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
740
0
      }
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
0
      if (gf_svg_is_animation_tag(rti->timed_elt->sgprivate->tag)
746
0
              && (rti->current_interval->simple_duration==-1)
747
0
              && (rti->current_interval->active_duration==-1)
748
0
         ) {
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
0
        ret = 1;
754
0
      }
755
0
    }
756
0
  }
757
758
0
  if ((rti->status == SMIL_STATUS_DONE) || (rti->status == SMIL_STATUS_FROZEN)) {
759
0
    if (!timingp->restart || *timingp->restart != SMIL_RESTART_NEVER) {
760
      /* Check changes in begin or end attributes */
761
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
      if (rti->next_interval->begin != -1) {
763
0
        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
0
          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
0
        } else {
779
0
          rti->next_interval->begin = -1;
780
0
        }
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_COMPTIME, ("[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
0
          ret = 0;
788
0
          goto waiting_to_begin;
789
0
        }
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
0
        else {
793
0
          rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
794
0
        }
795
0
      } else {
796
        /*??? what is this ???*/
797
        //ret = 0;
798
0
      }
799
0
    } else if ((rti->status == SMIL_STATUS_DONE) &&
800
0
               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
0
      ret = -1;
806
0
    }
807
0
  }
808
809
0
exit:
810
0
  gf_node_unregister(rti->timed_elt, NULL);
811
0
  return ret;
812
0
}
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
Fixed gf_smil_timing_get_normalized_simple_time(SMIL_Timing_RTI *rti, Double scene_time, Bool *force_end)
820
0
{
821
0
  Double activeTime;
822
0
  Double simpleTime;
823
0
  Fixed normalizedSimpleTime;
824
825
0
  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
0
  activeTime = scene_time - rti->current_interval->begin;
829
830
  /* Is the animation reaching the end of its active duration ? */
831
0
  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
0
    activeTime = rti->current_interval->active_duration;
835
836
    /* if the simple duration is defined, then we can take iterations into account */
837
0
    if (rti->current_interval->simple_duration>0) {
838
839
0
      if (activeTime == rti->current_interval->simple_duration*(rti->current_interval->nb_iterations+1)) {
840
0
        return FIX_ONE;
841
0
      } else {
842
0
        goto end;
843
0
      }
844
0
    } 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
0
          return FIX_ONE;
852
0
        } else {
853
0
          return  rti->normalized_simple_time;
854
0
        }
855
0
      } else {
856
0
        return 0;
857
0
      }
858
0
    }
859
0
  }
860
861
0
end:
862
  /* if the simple duration is defined, then we can take iterations into account */
863
0
  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
0
    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
0
          return FIX_ONE;
873
0
        } else {
874
0
          return  rti->normalized_simple_time;
875
0
        }
876
0
      }
877
0
    }
878
    /* we update the number of iterations */
879
0
    rti->current_interval->nb_iterations = (u32)floor(activeTime / rti->current_interval->simple_duration);
880
0
  } else {
881
    /* If the element does not define its simple duration, we assume it's blocked in final state
882
       Is this correct ? */
883
0
    rti->current_interval->nb_iterations = 0;
884
    //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
    return FIX_ONE;
886
0
  }
887
888
  /* We compute the simple time by removing time taken by previous iterations */
889
0
  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
0
  simpleTime = MAX(0, simpleTime);
893
0
  simpleTime = MIN(rti->current_interval->simple_duration, simpleTime);
894
895
  /* Then we normalize to have a value between 0 and 1 */
896
0
  normalizedSimpleTime = FLT2FIX(simpleTime / rti->current_interval->simple_duration);
897
898
0
  return normalizedSimpleTime;
899
0
}
900
901
/* This function is called when a modification to the node has been made (scripts, updates or events ...) */
902
void gf_smil_timing_modified(GF_Node *node, GF_FieldInfo *field)
903
0
{
904
0
  SMIL_Timing_RTI *rti;
905
0
  SMILTimingAttributesPointers *timingp = ((SVGTimedAnimBaseElement *)node)->timingp;
906
907
0
  if (!timingp) return;
908
0
  rti = timingp->runtime;
909
0
  if (!rti) return;
910
911
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
  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
0
  } else {
915
    /* we don't have the right to modify the end of an element if it's not in unresolved state */
916
0
    if (rti->current_interval->end == -1) gf_smil_timing_get_interval_end(rti, rti->current_interval);
917
0
    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
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPTIME, ("[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
0
      rti->current_interval->begin = -1;
922
0
      rti->current_interval->end = -1;
923
0
      return;
924
0
    }
925
926
0
    gf_smil_timing_compute_active_duration(rti, rti->current_interval);
927
0
    gf_smil_timing_print_interval(rti, 1, rti->current_interval);
928
0
  }
929
0
  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
0
  gf_smil_mark_modified(rti, 0);
934
0
}
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
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
0
{
942
0
  u32 i, done, count;
943
944
0
  done = 0;
945
0
  count = gf_list_count(smil_times);
946
0
  for (i=0; i<count; i++) {
947
0
    SMIL_Time *t = (SMIL_Time *)gf_list_get(smil_times, i);
948
949
0
    if (t->type != GF_SMIL_TIME_EVENT) {
950
0
      done++;
951
0
      continue;
952
0
    }
953
0
    if (!t->element_id) {
954
0
      if (!t->element) t->element = (GF_Node *)event_base_element;
955
0
      done++;
956
0
      continue;
957
0
    }
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
0
    t->element = gf_sg_find_node_by_name(anim->sgprivate->scenegraph, t->element_id);
962
0
    if (t->element) {
963
0
      gf_free(t->element_id);
964
0
      t->element_id = NULL;
965
0
      done++;
966
0
    }
967
0
  }
968
  /*lacuna value of discard is 0*/
969
0
  if (!count && !is_end && (anim->sgprivate->tag==TAG_SVG_discard) ) {
970
0
    SMIL_Time *t;
971
0
    GF_SAFEALLOC(t, SMIL_Time);
972
0
    if (!t) {
973
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Timing] Failed to alloc SMIL time for discard\n"));
974
0
      return 0;
975
0
    }
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
0
  }
981
982
0
  if (done!=count) return 0;
983
0
  return 1;
984
0
}
985
986
GF_EXPORT
987
void gf_smil_timing_insert_clock(GF_Node *elt, Bool is_end, Double clock)
988
0
{
989
0
  u32 i, count, found;
990
0
  SVGTimedAnimBaseElement *timed = (SVGTimedAnimBaseElement*)elt;
991
0
  SMIL_Time *begin;
992
0
  GF_List *l;
993
0
  GF_SAFEALLOC(begin, SMIL_Time);
994
0
  if (!begin) {
995
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_COMPTIME, ("[SMIL Timing] Failed to alloc SMIL begin value\n"));
996
0
    return;
997
0
  }
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
0
  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
0
    }
1016
0
    if ( (first->type == GF_SMIL_TIME_INDEFINITE)
1017
0
            || ( (first->type == GF_SMIL_TIME_CLOCK) && (first->clock > begin->clock) )
1018
0
       ) {
1019
0
      gf_list_insert(l, begin, i);
1020
0
      found = 1;
1021
0
      break;
1022
0
    }
1023
0
  }
1024
0
  if (!found) gf_list_add(l, begin);
1025
1026
  /* call gf_smil_timing_modified */
1027
0
  gf_node_changed(elt, NULL);
1028
0
}
1029
1030
void gf_smil_timing_pause(GF_Node *node)
1031
0
{
1032
0
  if (node && ((SVGTimedAnimBaseElement *)node)->timingp  && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) {
1033
0
    SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime;
1034
0
    if (rti->status<=SMIL_STATUS_ACTIVE) rti->paused = 1;
1035
0
  }
1036
0
}
1037
1038
void gf_smil_timing_resume(GF_Node *node)
1039
0
{
1040
0
  if (node && ((SVGTimedAnimBaseElement *)node)->timingp  && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) {
1041
0
    SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime;
1042
0
    rti->paused = 0;
1043
0
  }
1044
0
}
1045
1046
void gf_smil_set_evaluation_callback(GF_Node *node, gf_sg_smil_evaluate smil_evaluate)
1047
0
{
1048
0
  if (node && ((SVGTimedAnimBaseElement *)node)->timingp  && ((SVGTimedAnimBaseElement *)node)->timingp->runtime) {
1049
0
    SMIL_Timing_RTI *rti = ((SVGTimedAnimBaseElement *)node)->timingp->runtime;
1050
0
    rti->evaluate = smil_evaluate;
1051
0
  }
1052
0
}
1053
1054
GF_Node *gf_smil_get_element(SMIL_Timing_RTI *rti)
1055
0
{
1056
0
  return rti->timed_elt;
1057
0
}
1058
1059
Double gf_smil_get_media_duration(SMIL_Timing_RTI *rti)
1060
0
{
1061
0
  return rti ? rti->media_duration : 0.0;
1062
0
}
1063
1064
1065
#endif /*GPAC_DISABLE_SVG*/