Line data Source code
1 : /*
2 : * GPAC Multimedia Framework
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2020-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / TTML decoder 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/filters.h>
28 : #include <gpac/constants.h>
29 :
30 : #if !defined(GPAC_DISABLE_SVG) && defined(GPAC_HAS_QJS)
31 :
32 : #include <gpac/internal/scenegraph_dev.h>
33 : #include <gpac/internal/compositor_dev.h>
34 : #include <gpac/nodes_svg.h>
35 : #include <gpac/xml.h>
36 :
37 : #include "../scenegraph/qjs_common.h"
38 :
39 : typedef struct
40 : {
41 : //opts
42 : char *script;
43 : char *color, *font;
44 : Float fontSize, lineSpacing, txtx, txty;
45 : u32 txtw, txth, valign;
46 :
47 : GF_FilterPid *ipid, *opid;
48 :
49 : /* Boolean indicating the internal graph is registered with the compositor */
50 : Bool graph_registered;
51 : /* Boolean indicating the stream is playing */
52 : Bool is_playing;
53 :
54 : /* Parent scene to which this TTML stream is linked */
55 : GF_Scene *scene;
56 : /* root object manager of parent scene (used to get clock info)*/
57 : GF_ObjectManager *odm;
58 :
59 : /* Scene graph for the subtitle content */
60 : GF_SceneGraph *scenegraph;
61 :
62 : Bool update_args;
63 :
64 : GF_BitStream *subs_bs;
65 : s32 notify_clock;
66 :
67 : } GF_TTMLDec;
68 :
69 2 : void ttmldec_update_size_info(GF_TTMLDec *ctx)
70 : {
71 : u32 w, h;
72 : GF_FieldInfo info;
73 : Bool has_size;
74 : char szVB[100];
75 2 : GF_Node *root = gf_sg_get_root_node(ctx->scenegraph);
76 2 : if (!root) return;
77 :
78 2 : has_size = gf_sg_get_scene_size_info(ctx->scene->graph, &w, &h);
79 : /*no size info is given in main scene, override by associated video size if any, or by text track size*/
80 2 : if (!has_size) {
81 : const GF_PropertyValue *p;
82 1 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_WIDTH);
83 1 : if (p) w = p->value.uint;
84 1 : p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_HEIGHT);
85 1 : if (p) h = p->value.uint;
86 :
87 1 : if (!w) w = ctx->txtw;
88 1 : if (!h) h = ctx->txth;
89 :
90 1 : gf_sg_set_scene_size_info(ctx->scenegraph, w, h, GF_TRUE);
91 1 : gf_scene_force_size(ctx->scene, w, h);
92 : }
93 :
94 2 : gf_sg_set_scene_size_info(ctx->scenegraph, w, h, GF_TRUE);
95 :
96 2 : sprintf(szVB, "0 0 %d %d", w, h);
97 2 : gf_node_get_attribute_by_tag(root, TAG_SVG_ATT_viewBox, GF_TRUE, GF_FALSE, &info);
98 2 : gf_svg_parse_attribute(root, &info, szVB, 0);
99 :
100 : /*apply*/
101 2 : gf_sg_set_scene_size_info(ctx->scenegraph, w, h, GF_TRUE);
102 :
103 2 : sprintf(szVB, "0 0 %d %d", w, h);
104 2 : gf_node_get_attribute_by_tag(root, TAG_SVG_ATT_viewBox, GF_TRUE, GF_FALSE, &info);
105 2 : gf_svg_parse_attribute(root, &info, szVB, 0);
106 :
107 : }
108 :
109 1 : void ttmldec_setup_scene(GF_TTMLDec *ctx)
110 : {
111 : GF_Node *n, *root;
112 : GF_FieldInfo info;
113 :
114 1 : ctx->scenegraph = gf_sg_new_subscene(ctx->scene->graph);
115 :
116 1 : if (!ctx->scenegraph) return;
117 1 : gf_sg_add_namespace(ctx->scenegraph, "http://www.w3.org/2000/svg", NULL);
118 1 : gf_sg_add_namespace(ctx->scenegraph, "http://www.w3.org/1999/xlink", "xlink");
119 1 : gf_sg_add_namespace(ctx->scenegraph, "http://www.w3.org/2001/xml-events", "ev");
120 1 : gf_sg_set_scene_size_info(ctx->scenegraph, 800, 600, GF_TRUE);
121 :
122 : /* modify the scene with an Inline/Animation pointing to the TTML Renderer */
123 1 : n = root = gf_node_new(ctx->scenegraph, TAG_SVG_svg);
124 1 : gf_node_register(root, NULL);
125 1 : gf_sg_set_root_node(ctx->scenegraph, root);
126 1 : gf_node_get_attribute_by_name(n, "xmlns", 0, GF_TRUE, GF_FALSE, &info);
127 1 : gf_svg_parse_attribute(n, &info, "http://www.w3.org/2000/svg", 0);
128 1 : ttmldec_update_size_info(ctx);
129 1 : gf_node_init(n);
130 :
131 1 : n = gf_node_new(ctx->scenegraph, TAG_SVG_script);
132 1 : gf_node_register(n, root);
133 1 : gf_node_list_add_child(&((GF_ParentNode *)root)->children, n);
134 :
135 1 : gf_node_get_attribute_by_tag(n, TAG_XLINK_ATT_href, GF_TRUE, GF_FALSE, &info);
136 1 : if (strstr(ctx->script, ":\\")) {
137 0 : gf_svg_parse_attribute(n, &info, (char *) ctx->script, 0);
138 : } else {
139 : char szPath[GF_MAX_PATH];
140 : strcpy(szPath, "file://");
141 : strcat(szPath, ctx->script);
142 1 : gf_svg_parse_attribute(n, &info, (char *) szPath, 0);
143 : }
144 :
145 1 : gf_node_init(n);
146 :
147 : }
148 :
149 1 : static GF_Err ttmldec_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
150 : {
151 1 : GF_TTMLDec *ctx = (GF_TTMLDec *)gf_filter_get_udta(filter);
152 :
153 1 : if (is_remove) {
154 0 : if (ctx->opid) {
155 0 : gf_filter_pid_remove(ctx->opid);
156 0 : ctx->opid = NULL;
157 : }
158 0 : ctx->ipid = NULL;
159 0 : return GF_OK;
160 : }
161 : //TODO: we need to cleanup cap checking upon reconfigure
162 1 : if (ctx->ipid && !gf_filter_pid_check_caps(pid)) return GF_NOT_SUPPORTED;
163 : assert(!ctx->ipid || (ctx->ipid == pid));
164 :
165 1 : ctx->ipid = pid;
166 1 : if (!ctx->opid) {
167 1 : ctx->opid = gf_filter_pid_new(filter);
168 : }
169 :
170 : //copy properties at init or reconfig
171 1 : gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);
172 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_TEXT));
173 1 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_RAW));
174 :
175 1 : return GF_OK;
176 : }
177 :
178 :
179 3 : static void ttmldec_toggle_display(GF_TTMLDec *ctx)
180 : {
181 3 : if (!ctx->scenegraph) return;
182 :
183 3 : if (ctx->is_playing) {
184 1 : if (!ctx->graph_registered) {
185 1 : ttmldec_update_size_info(ctx);
186 1 : gf_scene_register_extra_graph(ctx->scene, ctx->scenegraph, GF_FALSE);
187 1 : ctx->graph_registered = GF_TRUE;
188 : }
189 : } else {
190 2 : if (ctx->graph_registered) {
191 1 : gf_scene_register_extra_graph(ctx->scene, ctx->scenegraph, GF_TRUE);
192 1 : ctx->graph_registered = GF_FALSE;
193 : }
194 : }
195 : }
196 :
197 627 : static Bool ttmldec_process_event(GF_Filter *filter, const GF_FilterEvent *com)
198 : {
199 627 : GF_TTMLDec *ctx = gf_filter_get_udta(filter);
200 :
201 : //check for scene attach
202 627 : switch (com->base.type) {
203 : case GF_FEVT_ATTACH_SCENE:
204 : break;
205 1 : case GF_FEVT_RESET_SCENE:
206 1 : if (ctx->opid != com->attach_scene.on_pid) return GF_TRUE;
207 1 : ctx->is_playing = GF_FALSE;
208 1 : ttmldec_toggle_display(ctx);
209 :
210 1 : gf_sg_del(ctx->scenegraph);
211 1 : ctx->scenegraph = NULL;
212 1 : ctx->scene = NULL;
213 1 : return GF_TRUE;
214 1 : case GF_FEVT_PLAY:
215 1 : ctx->is_playing = GF_TRUE;
216 1 : ttmldec_toggle_display(ctx);
217 1 : return GF_FALSE;
218 0 : case GF_FEVT_STOP:
219 0 : ctx->is_playing = GF_FALSE;
220 0 : ttmldec_toggle_display(ctx);
221 0 : return GF_FALSE;
222 : default:
223 : return GF_FALSE;
224 : }
225 1 : if (ctx->opid != com->attach_scene.on_pid) return GF_TRUE;
226 :
227 1 : ctx->odm = com->attach_scene.object_manager;
228 1 : ctx->scene = ctx->odm->subscene ? ctx->odm->subscene : ctx->odm->parentscene;
229 :
230 : /*timedtext cannot be a root scene object*/
231 1 : if (ctx->odm->subscene) {
232 0 : ctx->odm = NULL;
233 0 : ctx->scene = NULL;
234 : } else {
235 1 : ttmldec_setup_scene(ctx);
236 1 : ttmldec_toggle_display(ctx);
237 : }
238 : return GF_TRUE;
239 : }
240 :
241 : void js_dump_error(JSContext *ctx);
242 :
243 311 : JSContext *ttmldec_get_js_context(GF_TTMLDec *ctx)
244 : {
245 311 : GF_SceneGraph *sg = ctx->scenegraph->RootNode->sgprivate->scenegraph;
246 : JSContext *svg_script_get_context(GF_SceneGraph *sg);
247 311 : JSContext *c = svg_script_get_context(sg);
248 :
249 311 : if (ctx->update_args) {
250 1 : JSValue global = JS_GetGlobalObject(c);
251 :
252 2 : JS_SetPropertyStr(c, global, "fontSize", JS_NewFloat64(c, ctx->fontSize));
253 1 : JS_SetPropertyStr(c, global, "fontFamily", JS_NewString(c, ctx->font));
254 1 : JS_SetPropertyStr(c, global, "textColor", JS_NewString(c, ctx->color));
255 2 : JS_SetPropertyStr(c, global, "lineSpaceFactor", JS_NewFloat64(c, ctx->lineSpacing));
256 2 : JS_SetPropertyStr(c, global, "xOffset", JS_NewFloat64(c, ctx->txtx));
257 2 : JS_SetPropertyStr(c, global, "yOffset", JS_NewFloat64(c, ctx->txty));
258 2 : JS_SetPropertyStr(c, global, "v_align", JS_NewInt32(c, ctx->valign));
259 :
260 : JS_FreeValue(c, global);
261 1 : ctx->update_args = GF_FALSE;
262 : }
263 311 : return c;
264 : }
265 :
266 :
267 :
268 312 : static GF_Err ttmldec_process(GF_Filter *filter)
269 : {
270 : GF_Err e = GF_OK;
271 : const GF_PropertyValue *subs;
272 : GF_FilterPacket *pck;
273 : const u8 *pck_data;
274 : char *pck_alloc=NULL, *ttml_doc;
275 : u64 cts;
276 : u32 timescale, obj_time;
277 : u32 pck_size;
278 : JSValue fun_val;
279 : JSValue global;
280 : JSContext *c;
281 312 : GF_TTMLDec *ctx = (GF_TTMLDec *) gf_filter_get_udta(filter);
282 :
283 312 : if (!ctx->scene) {
284 0 : if (ctx->is_playing) {
285 0 : gf_filter_pid_set_eos(ctx->opid);
286 0 : return GF_EOS;
287 : }
288 : return GF_OK;
289 : }
290 :
291 312 : pck = gf_filter_pid_get_packet(ctx->ipid);
292 312 : if (!pck) {
293 1 : if (gf_filter_pid_is_eos(ctx->ipid)) {
294 1 : gf_filter_pid_set_eos(ctx->opid);
295 1 : return GF_EOS;
296 : }
297 : return GF_OK;
298 : }
299 :
300 311 : c = ttmldec_get_js_context(ctx);
301 311 : if (!c) {
302 0 : gf_filter_pid_drop_packet(ctx->ipid);
303 0 : return GF_SERVICE_ERROR;
304 : }
305 :
306 : //object clock shall be valid
307 311 : if (!ctx->odm->ck)
308 : return GF_OK;
309 :
310 311 : cts = gf_filter_pck_get_cts( pck );
311 311 : timescale = gf_filter_pck_get_timescale(pck);
312 :
313 311 : gf_odm_check_buffering(ctx->odm, ctx->ipid);
314 :
315 : //we still process any frame before our clock time even when buffering
316 311 : obj_time = gf_clock_time(ctx->odm->ck);
317 :
318 311 : if (ctx->notify_clock>0) {
319 155 : gf_js_lock(c, GF_TRUE);
320 155 : global = JS_GetGlobalObject(c);
321 155 : fun_val = JS_GetPropertyStr(c, global, "on_ttml_clock");
322 155 : if (JS_IsFunction(c, fun_val) ) {
323 : JSValue ret, argv[1];
324 310 : argv[0] = JS_NewInt64(c, obj_time);
325 :
326 155 : ret = JS_Call(c, fun_val, global, 1, argv);
327 155 : if (JS_IsException(ret)) {
328 0 : js_dump_error(c);
329 : e = GF_BAD_PARAM;
330 : }
331 :
332 155 : if (JS_IsNumber(ret)) {
333 155 : JS_ToInt32(c, &ctx->notify_clock, ret);
334 : } else {
335 0 : ctx->notify_clock = 0;
336 : }
337 :
338 : JS_FreeValue(c, ret);
339 : JS_FreeValue(c, argv[0]);
340 : }
341 : JS_FreeValue(c, global);
342 : JS_FreeValue(c, fun_val);
343 155 : gf_js_lock(c, GF_FALSE);
344 155 : if (e) return e;
345 : }
346 :
347 311 : if (cts * 1000 > obj_time * timescale) {
348 305 : gf_sc_sys_frame_pending(ctx->scene->compositor, ((Double) cts / timescale), obj_time, filter);
349 305 : return GF_OK;
350 : }
351 6 : pck_data = gf_filter_pck_get_data(pck, &pck_size);
352 6 : subs = gf_filter_pck_get_property(pck, GF_PROP_PCK_SUBS);
353 :
354 : ttml_doc = (char *) pck_data;
355 6 : if (subs) {
356 : Bool first = GF_TRUE;
357 0 : gf_bs_reassign_buffer(ctx->subs_bs, subs->value.data.ptr, subs->value.data.size);
358 0 : while (gf_bs_available(ctx->subs_bs)) {
359 0 : /*u32 flags = */gf_bs_read_u32(ctx->subs_bs);
360 0 : u32 subs_size = gf_bs_read_u32(ctx->subs_bs);
361 0 : /*u32 reserved = */gf_bs_read_u32(ctx->subs_bs);
362 0 : /*u8 priority = */gf_bs_read_u8(ctx->subs_bs);
363 0 : /*u8 discardable = */gf_bs_read_u8(ctx->subs_bs);
364 :
365 0 : if (first) {
366 : first = GF_FALSE;
367 0 : if (subs_size>pck_size) {
368 0 : gf_filter_pid_drop_packet(ctx->ipid);
369 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[TTMLDec] Invalid subsample size %d for packet size %d\n", subs_size, pck_size));
370 : return GF_NON_COMPLIANT_BITSTREAM;
371 : }
372 0 : pck_alloc = gf_malloc(sizeof(char)*(subs_size+2));
373 0 : memcpy(pck_alloc, pck_data, sizeof(char)*subs_size);
374 0 : pck_alloc[subs_size] = 0;
375 0 : pck_alloc[subs_size+1] = 0;
376 : ttml_doc = pck_alloc;
377 : }
378 : }
379 : } else {
380 : //we cannot assume the doc ends with 0
381 6 : pck_alloc = gf_malloc(sizeof(char)*(pck_size+2));
382 6 : memcpy(pck_alloc, ttml_doc, sizeof(char)*pck_size);
383 6 : pck_alloc[pck_size] = 0;
384 6 : pck_alloc[pck_size+1] = 0;
385 : ttml_doc = pck_alloc;
386 : }
387 :
388 6 : GF_DOMParser *dom = gf_xml_dom_new();
389 6 : gf_xml_dom_parse_string(dom, ttml_doc);
390 :
391 :
392 6 : gf_js_lock(c, GF_TRUE);
393 6 : global = JS_GetGlobalObject(c);
394 6 : fun_val = JS_GetPropertyStr(c, global, "on_ttml_sample");
395 6 : if (!JS_IsFunction(c, fun_val) ) {
396 : e = GF_BAD_PARAM;
397 : } else {
398 : JSValue ret, argv[3];
399 : Double scene_ck;
400 : GF_SceneGraph *doc;
401 :
402 6 : doc = gf_sg_new();
403 6 : doc->reference_count = 1;
404 6 : e = gf_sg_init_from_xml_node(doc, gf_xml_dom_get_root(dom));
405 6 : argv[0] = dom_document_construct_external(c, doc);
406 :
407 12 : argv[1] = JS_NewInt64(c, obj_time);
408 6 : scene_ck = gf_filter_pck_get_duration(pck);
409 6 : scene_ck /= timescale;
410 6 : argv[2] = JS_NewFloat64(c, scene_ck);
411 :
412 6 : ret = JS_Call(c, fun_val, global, 3, argv);
413 6 : if (JS_IsException(ret)) {
414 0 : js_dump_error(c);
415 : e = GF_BAD_PARAM;
416 : }
417 6 : if (JS_IsNumber(ret)) {
418 6 : JS_ToInt32(c, &ctx->notify_clock, ret);
419 : } else {
420 0 : ctx->notify_clock = 0;
421 : }
422 :
423 6 : doc->reference_count --;
424 : JS_FreeValue(c, ret);
425 : JS_FreeValue(c, argv[0]);
426 : JS_FreeValue(c, argv[1]);
427 : JS_FreeValue(c, argv[2]);
428 : }
429 : JS_FreeValue(c, global);
430 : JS_FreeValue(c, fun_val);
431 :
432 6 : gf_js_lock(c, GF_FALSE);
433 :
434 :
435 6 : gf_xml_dom_del(dom);
436 :
437 6 : if (pck_alloc) gf_free(pck_alloc);
438 6 : gf_filter_pid_drop_packet(ctx->ipid);
439 :
440 6 : if (ctx->notify_clock>0) {
441 6 : if (ctx->scene && ctx->scene->compositor->player)
442 0 : gf_filter_ask_rt_reschedule(filter, ctx->notify_clock*1000);
443 : else
444 6 : gf_filter_post_process_task(filter);
445 : }
446 : return e;
447 : }
448 :
449 0 : static GF_Err ttmldec_update_arg(GF_Filter *filter, const char *arg_name, const GF_PropertyValue *new_val)
450 : {
451 1 : GF_TTMLDec *ctx = gf_filter_get_udta(filter);
452 1 : ctx->update_args = GF_TRUE;
453 0 : return GF_OK;
454 : }
455 :
456 1 : static GF_Err ttmldec_initialize(GF_Filter *filter)
457 : {
458 1 : GF_TTMLDec *ctx = gf_filter_get_udta(filter);
459 :
460 1 : if (!ctx->script) {
461 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[TTMLDec] TTML JS renderer script not set\n"));
462 : return GF_BAD_PARAM;
463 1 : } else if (!gf_file_exists(ctx->script)) {
464 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[TTMLDec] TTML JS renderer script %s not found\n", ctx->script));
465 : return GF_URL_ERROR;
466 : }
467 1 : ctx->subs_bs = gf_bs_new((u8 *)ctx, 1, GF_BITSTREAM_READ);
468 :
469 1 : ctx->update_args = GF_TRUE;
470 : #ifdef GPAC_ENABLE_COVERAGE
471 : ttmldec_update_arg(filter, NULL, NULL);
472 : #endif
473 1 : return GF_OK;
474 : }
475 :
476 1 : void ttmldec_finalize(GF_Filter *filter)
477 : {
478 1 : GF_TTMLDec *ctx = (GF_TTMLDec *) gf_filter_get_udta(filter);
479 :
480 1 : if (ctx->scenegraph) {
481 0 : ctx->is_playing = GF_FALSE;
482 0 : ttmldec_toggle_display(ctx);
483 0 : gf_sg_del(ctx->scenegraph);
484 : }
485 1 : gf_bs_del(ctx->subs_bs);
486 1 : }
487 :
488 : #define OFFS(_n) #_n, offsetof(GF_TTMLDec, _n)
489 : static const GF_FilterArgs TTMLDecArgs[] =
490 : {
491 : { OFFS(script), "location of TTML SVG JS renderer", GF_PROP_STRING, "$GSHARE/scripts/ttml-renderer.js", NULL, GF_FS_ARG_HINT_EXPERT},
492 : { OFFS(font), "font to use", GF_PROP_STRING, "SANS", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
493 : { OFFS(fontSize), "font size to use", GF_PROP_FLOAT, "20", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
494 : { OFFS(color), "color to use", GF_PROP_STRING, "white", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
495 : { OFFS(valign), "vertical alignment\n"
496 : "- bottom: align text at bottom of text area\n"
497 : "- center: align text at center of text area\n"
498 : "- top: align text at top of text area", GF_PROP_UINT, "bottom", "bottom|center|top", GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
499 : { OFFS(lineSpacing), "line spacing as scaling factor to font size", GF_PROP_FLOAT, "1.0", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
500 : { OFFS(txtx), "horizontal offset", GF_PROP_FLOAT, "5", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
501 : { OFFS(txty), "vertical offset", GF_PROP_FLOAT, "5", NULL, GF_FS_ARG_HINT_ADVANCED|GF_FS_ARG_UPDATE},
502 : { OFFS(txtw), "default width in standalone rendering", GF_PROP_UINT, "400", NULL, 0},
503 : { OFFS(txth), "default height in standalone rendering", GF_PROP_UINT, "200", NULL, 0},
504 : {0}
505 : };
506 :
507 : static const GF_FilterCapability TTMLDecCaps[] =
508 : {
509 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
510 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
511 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_CODECID, GF_CODECID_SUBS_XML),
512 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
513 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_CODECID, GF_CODECID_RAW),
514 : };
515 :
516 : GF_FilterRegister TTMLDecRegister = {
517 : .name = "ttmldec",
518 : GF_FS_SET_DESCRIPTION("TTML decoder")
519 : GF_FS_SET_HELP("This filter decodes TTML streams into a SVG scene graph of the compositor filter.\n"
520 : "The scene graph creation is done through JavaScript.\n"
521 : "The filter options are used to override the JS global variables of the TTML renderer.")
522 : .private_size = sizeof(GF_TTMLDec),
523 : .flags = GF_FS_REG_MAIN_THREAD,
524 : .args = TTMLDecArgs,
525 : .priority = 1,
526 : SETCAPS(TTMLDecCaps),
527 : .initialize = ttmldec_initialize,
528 : .finalize = ttmldec_finalize,
529 : .process = ttmldec_process,
530 : .configure_pid = ttmldec_configure_pid,
531 : .process_event = ttmldec_process_event,
532 : .update_arg = ttmldec_update_arg
533 : };
534 :
535 : #endif
536 :
537 2877 : const GF_FilterRegister *ttmldec_register(GF_FilterSession *session)
538 : {
539 : #if !defined(GPAC_DISABLE_SVG) && defined(GPAC_HAS_QJS)
540 2877 : return &TTMLDecRegister;
541 : #else
542 : return NULL;
543 : #endif
544 : }
545 :
546 :
|