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*/
|