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