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