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