~ubuntu-branches/debian/experimental/gpac/experimental

« back to all changes in this revision

Viewing changes to src/compositor/svg_media.c

  • Committer: Package Import Robot
  • Author(s): Andres Mejia
  • Date: 2012-02-04 00:12:54 UTC
  • Revision ID: package-import@ubuntu.com-20120204001254-l7v7u4kc4m7cxcqn
Tags: upstream-0.4.5+svn3450~dfsg3
ImportĀ upstreamĀ versionĀ 0.4.5+svn3450~dfsg3

Show diffs side-by-side

added added

removed removed

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