LCOV - code coverage report
Current view: top level - filters - dec_ttml.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 177 221 80.1 %
Date: 2021-04-29 23:48:07 Functions: 10 11 90.9 %

          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             : 

Generated by: LCOV version 1.13