Line data Source code
1 : /*
2 : * GPAC Multimedia Framework
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2005-2017
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / SVG loader filter
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 :
27 : #include <gpac/internal/compositor_dev.h>
28 : #include <gpac/scene_manager.h>
29 : #include <gpac/constants.h>
30 :
31 : #if !defined(GPAC_DISABLE_SVG) && !defined(GPAC_DISABLE_ZLIB)
32 :
33 : #include <zlib.h>
34 :
35 : typedef struct
36 : {
37 : //opts
38 : u32 sax_dur;
39 :
40 : //internal
41 : GF_FilterPid *in_pid, *out_pid;
42 : GF_SceneLoader loader;
43 : GF_Scene *scene;
44 : u32 codecid;
45 : const char *file_name;
46 : u32 file_size;
47 : u16 base_es_id;
48 : u32 file_pos;
49 : void *src;
50 : Bool is_playing;
51 : } SVGIn;
52 :
53 :
54 : #define SVG_PROGRESSIVE_BUFFER_SIZE 4096
55 :
56 : //static
57 1 : GF_Err svgin_deflate(SVGIn *svgin, const char *buffer, u32 buffer_len, Bool is_dims)
58 : {
59 : GF_Err e;
60 : char svg_data[2049];
61 : int err;
62 : u32 done = 0;
63 : z_stream d_stream;
64 : memset(&d_stream, 0, sizeof(z_stream));
65 1 : d_stream.next_in = (Bytef*)buffer;
66 1 : d_stream.avail_in = buffer_len;
67 1 : d_stream.next_out = (Bytef*)svg_data;
68 1 : d_stream.avail_out = 2048;
69 :
70 1 : if (is_dims==1) {
71 1 : err = inflateInit2(&d_stream, 16+MAX_WBITS);
72 : } else {
73 0 : err = inflateInit(&d_stream);
74 : }
75 :
76 1 : if (err == Z_OK) {
77 : e = GF_OK;
78 1 : while (d_stream.total_in < buffer_len) {
79 1 : err = inflate(&d_stream, Z_NO_FLUSH);
80 1 : if (err < Z_OK) {
81 : e = GF_NON_COMPLIANT_BITSTREAM;
82 : break;
83 : }
84 1 : svg_data[d_stream.total_out - done] = 0;
85 1 : e = gf_sm_load_string(&svgin->loader, svg_data, GF_FALSE);
86 1 : if (e || (err== Z_STREAM_END)) break;
87 0 : done = (u32) d_stream.total_out;
88 0 : d_stream.avail_out = 2048;
89 0 : d_stream.next_out = (Bytef*)svg_data;
90 : }
91 1 : inflateEnd(&d_stream);
92 1 : return e;
93 : }
94 : return GF_NON_COMPLIANT_BITSTREAM;
95 : }
96 :
97 156 : static GF_Err svgin_process(GF_Filter *filter)
98 : {
99 : Bool start, end;
100 : const u8 *data;
101 : u32 size;
102 : GF_Err e = GF_OK;
103 : GF_FilterPacket *pck;
104 156 : SVGIn *svgin = (SVGIn *) gf_filter_get_udta(filter);
105 :
106 : //no scene yet attached
107 156 : if (!svgin->scene) {
108 0 : if (svgin->is_playing) {
109 0 : gf_filter_pid_set_eos(svgin->out_pid);
110 0 : return GF_EOS;
111 : }
112 : return GF_OK;
113 : }
114 :
115 : #ifdef FILTER_FIXME
116 : if (stream_time==(u32)-1) {
117 : if (svgin->src) gf_gzclose(svgin->src);
118 : svgin->src = NULL;
119 : gf_sm_load_done(&svgin->loader);
120 : svgin->loader.fileName = NULL;
121 : svgin->file_pos = 0;
122 : gf_sg_reset(svgin->scene->graph);
123 : return GF_OK;
124 : }
125 : #endif
126 :
127 156 : switch (svgin->codecid) {
128 : /*! streaming SVG*/
129 0 : case GF_CODECID_SVG:
130 0 : pck = gf_filter_pid_get_packet(svgin->in_pid);
131 0 : if (!pck) {
132 0 : if (gf_filter_pid_is_eos(svgin->in_pid)) {
133 0 : gf_filter_pid_set_eos(svgin->out_pid);
134 0 : return GF_EOS;
135 : }
136 : return GF_OK;
137 : }
138 0 : data = gf_filter_pck_get_data(pck, &size);
139 0 : e = gf_sm_load_string(&svgin->loader, data, GF_FALSE);
140 0 : gf_filter_pid_drop_packet(svgin->in_pid);
141 0 : break;
142 :
143 : /*! streaming SVG + gz*/
144 0 : case GF_CODECID_SVG_GZ:
145 0 : pck = gf_filter_pid_get_packet(svgin->in_pid);
146 0 : if (!pck) {
147 0 : if (gf_filter_pid_is_eos(svgin->in_pid)) {
148 0 : gf_filter_pid_set_eos(svgin->out_pid);
149 0 : return GF_EOS;
150 : }
151 : return GF_OK;
152 : }
153 0 : data = gf_filter_pck_get_data(pck, &size);
154 0 : e = svgin_deflate(svgin, data, size, GF_FALSE);
155 0 : gf_filter_pid_drop_packet(svgin->in_pid);
156 0 : break;
157 :
158 : /*! DIMS (dsi = 3GPP DIMS configuration)*/
159 3 : case GF_CODECID_DIMS:
160 : {
161 : u8 prev, dims_hdr;
162 : u32 nb_bytes;
163 : u64 pos;
164 : char * buf2 ;
165 : GF_BitStream *bs;
166 :
167 3 : pck = gf_filter_pid_get_packet(svgin->in_pid);
168 3 : if (!pck) {
169 2 : if (gf_filter_pid_is_eos(svgin->in_pid)) {
170 1 : gf_filter_pid_set_eos(svgin->out_pid);
171 1 : return GF_EOS;
172 : }
173 : return GF_OK;
174 : }
175 1 : data = gf_filter_pck_get_data(pck, &size);
176 :
177 1 : buf2 = gf_malloc(size+1);
178 1 : bs = gf_bs_new((u8 *)data, size, GF_BITSTREAM_READ);
179 1 : memcpy(buf2, data, size);
180 1 : buf2[size] = 0;
181 :
182 1 : gf_filter_pid_drop_packet(svgin->in_pid);
183 : e = GF_OK;
184 3 : while (gf_bs_available(bs)) {
185 1 : pos = gf_bs_get_position(bs);
186 1 : size = gf_bs_read_u16(bs);
187 : nb_bytes = 2;
188 : /*GPAC internal hack*/
189 1 : if (!size) {
190 0 : size = gf_bs_read_u32(bs);
191 : nb_bytes = 6;
192 : }
193 :
194 1 : dims_hdr = gf_bs_read_u8(bs);
195 1 : prev = buf2[pos + nb_bytes + size];
196 :
197 1 : buf2[pos + nb_bytes + size] = 0;
198 1 : if (dims_hdr & GF_DIMS_UNIT_C) {
199 1 : e = svgin_deflate(svgin, buf2 + pos + nb_bytes + 1, size - 1, GF_TRUE);
200 : } else {
201 0 : e = gf_sm_load_string(&svgin->loader, buf2 + pos + nb_bytes + 1, GF_FALSE);
202 : }
203 1 : buf2[pos + nb_bytes + size] = prev;
204 1 : gf_bs_skip_bytes(bs, size-1);
205 1 : if (e) break;
206 :
207 : }
208 1 : gf_bs_del(bs);
209 1 : gf_free(buf2);
210 :
211 : if (e) goto exit;
212 : }
213 : break;
214 :
215 153 : default:
216 153 : if (!svgin->file_name) return GF_SERVICE_ERROR;
217 :
218 153 : pck = gf_filter_pid_get_packet(svgin->in_pid);
219 153 : if (!pck) return GF_OK;
220 97 : gf_filter_pck_get_framing(pck, &start, &end);
221 97 : gf_filter_pid_drop_packet( svgin->in_pid);
222 :
223 : /*full doc parsing*/
224 97 : if ((svgin->sax_dur == 0) && svgin->file_name) {
225 97 : if (end) {
226 48 : svgin->loader.fileName = svgin->file_name;
227 48 : e = gf_sm_load_init(&svgin->loader);
228 48 : if (!e) e = gf_sm_load_run(&svgin->loader);
229 : }
230 : }
231 : /*chunk parsing*/
232 : else {
233 : u32 entry_time;
234 : char file_buf[SVG_PROGRESSIVE_BUFFER_SIZE+2];
235 : /*initial load*/
236 0 : if (!svgin->src && !svgin->file_pos) {
237 0 : svgin->src = gf_gzopen(svgin->file_name, "rb");
238 0 : if (!svgin->src) return GF_URL_ERROR;
239 0 : svgin->loader.fileName = svgin->file_name;
240 0 : gf_sm_load_init(&svgin->loader);
241 : }
242 : e = GF_OK;
243 0 : entry_time = gf_sys_clock();
244 :
245 : while (1) {
246 : u32 diff;
247 : s32 nb_read;
248 0 : nb_read = gf_gzread(svgin->src, file_buf, SVG_PROGRESSIVE_BUFFER_SIZE);
249 : /*we may have read nothing but we still need to call parse in case the parser got suspended*/
250 0 : if (nb_read<=0) {
251 0 : if ((e==GF_EOS) && gf_gzeof(svgin->src)) {
252 0 : gf_set_progress("SVG Parsing", svgin->file_pos, svgin->file_size);
253 0 : gf_gzclose(svgin->src);
254 0 : svgin->src = NULL;
255 0 : gf_sm_load_done(&svgin->loader);
256 : }
257 0 : goto exit;
258 : }
259 :
260 0 : file_buf[nb_read] = file_buf[nb_read+1] = 0;
261 :
262 0 : e = gf_sm_load_string(&svgin->loader, file_buf, GF_FALSE);
263 0 : svgin->file_pos += nb_read;
264 :
265 :
266 :
267 : /*handle decompression*/
268 0 : if (svgin->file_pos > svgin->file_size) svgin->file_size = svgin->file_pos + 1;
269 0 : if (e) break;
270 :
271 0 : gf_set_progress("SVG Parsing", svgin->file_pos, svgin->file_size);
272 0 : diff = gf_sys_clock() - entry_time;
273 0 : if (diff > svgin->sax_dur) {
274 : break;
275 : }
276 : }
277 : }
278 : break;
279 : }
280 :
281 50 : exit:
282 49 : if ((e>=GF_OK) && (svgin->scene->graph_attached!=1) && (gf_sg_get_root_node(svgin->loader.scene_graph)!=NULL) ) {
283 48 : gf_scene_attach_to_compositor(svgin->scene);
284 : }
285 : /*prepare for next playback*/
286 98 : if (e) {
287 48 : gf_sm_load_done(&svgin->loader);
288 48 : svgin->loader.fileName = NULL;
289 48 : gf_filter_pid_set_eos(svgin->out_pid);
290 : }
291 : return e;
292 : }
293 :
294 70 : static GF_Err svgin_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
295 : {
296 70 : SVGIn *svgin = (SVGIn *) gf_filter_get_udta(filter);
297 : const GF_PropertyValue *prop;
298 :
299 70 : if (is_remove) {
300 21 : svgin->in_pid = NULL;
301 21 : return GF_OK;
302 : }
303 :
304 49 : if (! gf_filter_pid_check_caps(pid))
305 : return GF_NOT_SUPPORTED;
306 :
307 49 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
308 49 : if (prop && prop->value.uint) svgin->codecid = prop->value.uint;
309 :
310 49 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
311 49 : if (prop && (prop->value.uint==GF_STREAM_FILE)) {
312 : //we must have a file path or codecid
313 48 : prop = gf_filter_pid_get_property(pid, GF_PROP_PID_FILEPATH);
314 48 : if (prop && prop->value.string) svgin->file_name = prop->value.string;
315 : }
316 :
317 49 : if (!svgin->codecid && !svgin->file_name)
318 : return GF_NOT_SUPPORTED;
319 :
320 49 : svgin->loader.type = GF_SM_LOAD_SVG;
321 : /* decSpecInfo is not null only when reading from an SVG file (local or distant, cached or not) */
322 49 : switch (svgin->codecid) {
323 0 : case GF_CODECID_SVG:
324 : case GF_CODECID_SVG_GZ:
325 0 : svgin->loader.flags |= GF_SM_LOAD_CONTEXT_STREAMING;
326 : /*no decSpecInfo defined for streaming svg yet*/
327 0 : break;
328 1 : case GF_CODECID_DIMS:
329 1 : svgin->loader.type = GF_SM_LOAD_DIMS;
330 1 : svgin->loader.flags |= GF_SM_LOAD_CONTEXT_STREAMING;
331 : /*decSpecInfo not yet supported for DIMS svg - we need properties at the scene level to store the
332 : various indications*/
333 1 : break;
334 : default:
335 : break;
336 : }
337 :
338 49 : if (!svgin->in_pid) {
339 : GF_FilterEvent fevt;
340 49 : svgin->in_pid = pid;
341 :
342 : //we work with full file only, send a play event on source to indicate that
343 49 : GF_FEVT_INIT(fevt, GF_FEVT_PLAY, pid);
344 : fevt.play.start_range = 0;
345 49 : fevt.base.on_pid = svgin->in_pid;
346 49 : fevt.play.full_file_only = GF_TRUE;
347 49 : gf_filter_pid_send_event(svgin->in_pid, &fevt);
348 :
349 : } else {
350 0 : if (pid != svgin->in_pid) {
351 : return GF_REQUIRES_NEW_INSTANCE;
352 : }
353 : //check for filename change
354 0 : return GF_OK;
355 : }
356 :
357 : //declare a new output PID of type scene, codecid RAW
358 49 : svgin->out_pid = gf_filter_pid_new(filter);
359 :
360 49 : gf_filter_pid_copy_properties(svgin->out_pid, pid);
361 49 : gf_filter_pid_set_property(svgin->out_pid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_SCENE) );
362 49 : gf_filter_pid_set_property(svgin->out_pid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW) );
363 49 : gf_filter_pid_set_property(svgin->out_pid, GF_PROP_PID_IN_IOD, &PROP_BOOL(GF_TRUE) );
364 49 : gf_filter_pid_set_udta(pid, svgin->out_pid);
365 :
366 :
367 49 : if (svgin->file_name) {
368 48 : gf_filter_set_name(filter, ((svgin->sax_dur==0) && svgin->file_size) ? "SVGLoad" : "SVGLoad:Progressive");
369 1 : } else if (svgin->codecid==GF_CODECID_SVG) {
370 0 : gf_filter_set_name(filter, "SVG:Streaming");
371 1 : } else if (svgin->codecid==GF_CODECID_SVG_GZ) {
372 0 : gf_filter_set_name(filter, "SVG:Streaming:GZ");
373 1 : } else if (svgin->codecid==GF_CODECID_DIMS) {
374 1 : gf_filter_set_name(filter, "SVG:DIMS");
375 : }
376 :
377 : return GF_OK;
378 : }
379 :
380 294 : static Bool svgin_process_event(GF_Filter *filter, const GF_FilterEvent *com)
381 : {
382 : u32 count, i;
383 294 : SVGIn *svgin = gf_filter_get_udta(filter);
384 : //check for scene attach
385 294 : switch (com->base.type) {
386 49 : case GF_FEVT_PLAY:
387 : //cancel play event, we work with full file
388 49 : svgin->is_playing = GF_TRUE;
389 49 : return GF_TRUE;
390 : case GF_FEVT_ATTACH_SCENE:
391 : break;
392 49 : case GF_FEVT_RESET_SCENE:
393 49 : gf_sm_load_done(&svgin->loader);
394 49 : svgin->scene = NULL;
395 49 : return GF_FALSE;
396 : default:
397 : return GF_FALSE;
398 : }
399 49 : if (!com->attach_scene.on_pid) return GF_TRUE;
400 :
401 49 : count = gf_filter_get_ipid_count(filter);
402 49 : for (i=0; i<count; i++) {
403 49 : GF_FilterPid *ipid = gf_filter_get_ipid(filter, i);
404 49 : GF_FilterPid *opid = gf_filter_pid_get_udta(ipid);
405 : //we found our pid, set it up
406 49 : if (opid == com->attach_scene.on_pid) {
407 49 : if (!svgin->scene) {
408 49 : GF_ObjectManager *odm = com->attach_scene.object_manager;
409 49 : svgin->scene = odm->subscene ? odm->subscene : odm->parentscene;
410 :
411 49 : memset(&svgin->loader, 0, sizeof(GF_SceneLoader));
412 49 : svgin->loader.is = svgin->scene;
413 49 : svgin->loader.scene_graph = svgin->scene->graph;
414 49 : svgin->loader.localPath = gf_get_default_cache_directory();
415 :
416 : /*Warning: svgin->loader.type may be overridden in attach stream */
417 49 : svgin->loader.type = GF_SM_LOAD_SVG;
418 49 : svgin->loader.flags = GF_SM_LOAD_FOR_PLAYBACK;
419 :
420 49 : if (! svgin->file_name)
421 1 : gf_sm_load_init(&svgin->loader);
422 :
423 49 : if (svgin->scene->root_od->ck && !svgin->scene->root_od->ck->clock_init)
424 49 : gf_clock_set_time(svgin->scene->root_od->ck, 0);
425 :
426 : //init clocks
427 49 : gf_odm_check_buffering(svgin->scene->root_od, svgin->in_pid);
428 : }
429 : return GF_TRUE;
430 : }
431 : }
432 :
433 : return GF_FALSE;
434 : }
435 :
436 : static const GF_FilterCapability SVGInCaps[] =
437 : {
438 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
439 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "svg|svgz|svg.gz"),
440 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "image/svg+xml"),
441 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
442 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_CODECID, GF_CODECID_RAW),
443 : {0},
444 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
445 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SVG),
446 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SVG_GZ),
447 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_DIMS),
448 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
449 : };
450 :
451 :
452 : #define OFFS(_n) #_n, offsetof(SVGIn, _n)
453 :
454 : static const GF_FilterArgs SVGInArgs[] =
455 : {
456 : { OFFS(sax_dur), "loading duration for SAX parsing, 0 disables SAX parsing", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
457 : {0}
458 : };
459 : GF_FilterRegister SVGInRegister = {
460 : .name = "svgplay",
461 : GF_FS_SET_DESCRIPTION("SVG loader")
462 : GF_FS_SET_HELP("This filter parses SVG files directly into the scene graph of the compositor.")
463 : .private_size = sizeof(SVGIn),
464 : .flags = GF_FS_REG_MAIN_THREAD,
465 : .args = SVGInArgs,
466 : SETCAPS(SVGInCaps),
467 : .process = svgin_process,
468 : .configure_pid = svgin_configure_pid,
469 : .process_event = svgin_process_event,
470 : };
471 :
472 : #endif
473 :
474 :
475 2877 : const GF_FilterRegister *svgin_register(GF_FilterSession *session)
476 : {
477 : #if !defined(GPAC_DISABLE_SVG) && !defined(GPAC_DISABLE_ZLIB)
478 2877 : return &SVGInRegister;
479 : #else
480 : return NULL;
481 : #endif
482 : }
|