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