PageRenderTime 70ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/timedtext/timedtext_dec.c

https://github.com/svettom/gpac
C | 1197 lines | 926 code | 135 blank | 136 comment | 212 complexity | 531331e232e36fb915ef8dcf852e3714 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. /*
  2. * GPAC - Multimedia Framework C SDK
  3. *
  4. * Authors: Jean Le Feuvre
  5. * Copyright (c) Telecom ParisTech 2000-2012
  6. * All rights reserved
  7. *
  8. * This file is part of GPAC / 3GPP/MPEG4 timed text module
  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. #include "../../include/gpac/internal/terminal_dev.h"
  26. #include "../../include/gpac/internal/isomedia_dev.h"
  27. #include "../../include/gpac/utf.h"
  28. #include "../../include/gpac/constants.h"
  29. #include "../../include/gpac/nodes_mpeg4.h"
  30. #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_ISOM)
  31. /*
  32. this decoder is simply a scene decoder generating its own scene graph based on input data,
  33. this scene graph is then used as an extra graph by the renderer, and manipulated by the decoder
  34. for any time animation handling.
  35. Translation from text to MPEG-4 scene graph:
  36. * all modifiers (styles, hilight, etc) are unrolled into chunks forming a unique, linear
  37. sequence of text data (startChar, endChar) with associated styles & modifs
  38. * chunks are mapped to classic MPEG-4/VRML text
  39. * these chunks are then gathered in a Form node (supported by 2D and 3D renderers), with
  40. text truncation at each newline char.
  41. * the Form then performs all alignment of the chunks
  42. It could be possible to use Layout instead of form, BUT layout cannot handle new lines at the time being...
  43. Currently supported for 3GP text streams:
  44. * text box positioning & filling, dynamic text box
  45. * text color
  46. * proper alignment (H and V) with horizontal text. Vertical text may not be properly layed out (not fully tested)
  47. * style Records (font, size, fontstyles, and colors change) - any nb per sample supported
  48. * hilighting (static only) with color or reverse video - any nb per sample supported
  49. * hypertext links - any nb per sample supported
  50. * blinking - any nb per sample supported
  51. * complete scrolling: in, out, in+out, up, down, right and left directions. All other
  52. modifiers are supported when scrolling
  53. * scroll delay
  54. It does NOT support:
  55. * dynamic hilighting (karaoke)
  56. * wrap
  57. The decoder only accepts complete timed text units TTU(1). In band reconfig (TTU(5) is not supported,
  58. nor fragmented TTUs (2, 3, 4).
  59. UTF16 support should work but MP4Box does not support it at encoding time.
  60. */
  61. typedef struct
  62. {
  63. GF_Scene *inlineScene;
  64. GF_Terminal *app;
  65. u32 nb_streams;
  66. GF_TextConfig *cfg;
  67. GF_SceneGraph *sg;
  68. /*avoid searching the graph for things we know...*/
  69. M_Transform2D *tr_track, *tr_box, *tr_scroll;
  70. M_Material2D *mat_track, *mat_box;
  71. M_Layer2D *dlist;
  72. M_Rectangle *rec_box, *rec_track;
  73. M_TimeSensor *ts_blink, *ts_scroll;
  74. M_ScalarInterpolator *process_blink, *process_scroll;
  75. GF_Route *time_route;
  76. GF_List *blink_nodes;
  77. u32 scroll_type, scroll_mode;
  78. Fixed scroll_time, scroll_delay;
  79. Bool is_active, use_texture, outline;
  80. } TTDPriv;
  81. static void ttd_set_blink_fraction(GF_Node *node, GF_Route *route);
  82. static void ttd_set_scroll_fraction(GF_Node *node, GF_Route *route);
  83. static void TTD_ResetDisplay(TTDPriv *priv);
  84. /*the WORST thing about 3GP in MPEG4 is positioning of the text track...*/
  85. static void TTD_UpdateSizeInfo(TTDPriv *priv)
  86. {
  87. u32 w, h;
  88. Bool has_size;
  89. s32 offset, thw, thh, vw, vh;
  90. has_size = gf_sg_get_scene_size_info(priv->inlineScene->graph, &w, &h);
  91. /*no size info is given in main scene, override by associated video size if any, or by text track size*/
  92. if (!has_size) {
  93. if (priv->cfg->has_vid_info && priv->cfg->video_width && priv->cfg->video_height) {
  94. gf_sg_set_scene_size_info(priv->sg, priv->cfg->video_width, priv->cfg->video_height, 1);
  95. } else {
  96. gf_sg_set_scene_size_info(priv->sg, priv->cfg->text_width, priv->cfg->text_height, 1);
  97. }
  98. gf_sg_get_scene_size_info(priv->sg, &w, &h);
  99. if (!w || !h) return;
  100. gf_scene_force_size(priv->inlineScene, w, h);
  101. }
  102. if (!w || !h) return;
  103. /*apply*/
  104. gf_sg_set_scene_size_info(priv->sg, w, h, 1);
  105. /*make sure the scene size is big enough to contain the text track after positioning. RESULTS ARE UNDEFINED
  106. if offsets are negative: since MPEG-4 uses centered coord system, we must assume video is aligned to top-left*/
  107. if (priv->cfg->has_vid_info) {
  108. Bool set_size = 0;
  109. vw = priv->cfg->horiz_offset; if (vw<0) vw = 0;
  110. vh = priv->cfg->vert_offset; if (vh<0) vh = 0;
  111. if (priv->cfg->text_width + (u32) vw > w) {
  112. w = priv->cfg->text_width+vw;
  113. set_size = 1;
  114. }
  115. if (priv->cfg->text_height + (u32) vh > h) {
  116. h = priv->cfg->text_height+vh;
  117. set_size = 1;
  118. }
  119. if (set_size) {
  120. gf_sg_set_scene_size_info(priv->sg, w, h, 1);
  121. gf_scene_force_size(priv->inlineScene, w, h);
  122. }
  123. } else {
  124. /*otherwise override (mainly used for SRT & TTXT file direct loading*/
  125. priv->cfg->text_width = w;
  126. priv->cfg->text_height = h;
  127. }
  128. /*ok override video size with main scene size*/
  129. priv->cfg->video_width = w;
  130. priv->cfg->video_height = h;
  131. vw = (s32) w;
  132. vh = (s32) h;
  133. thw = priv->cfg->text_width / 2;
  134. thh = priv->cfg->text_height / 2;
  135. /*check translation, we must not get out of scene size - not supported in GPAC*/
  136. offset = priv->cfg->horiz_offset - vw/2 + thw;
  137. /*safety checks ?
  138. if (offset + thw < - vw/2) offset = - vw/2 + thw;
  139. else if (offset - thw > vw/2) offset = vw/2 - thw;
  140. */
  141. priv->tr_track->translation.x = INT2FIX(offset);
  142. offset = vh/2 - priv->cfg->vert_offset - thh;
  143. /*safety checks ?
  144. if (offset + thh > vh/2) offset = vh/2 - thh;
  145. else if (offset - thh < -vh/2) offset = -vh/2 + thh;
  146. */
  147. priv->tr_track->translation.y = INT2FIX(offset);
  148. gf_node_changed((GF_Node *)priv->tr_track, NULL);
  149. }
  150. static GF_Err TTD_GetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability *capability)
  151. {
  152. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  153. switch (capability->CapCode) {
  154. case GF_CODEC_WIDTH:
  155. capability->cap.valueInt = priv->cfg->text_width;
  156. return GF_OK;
  157. case GF_CODEC_HEIGHT:
  158. capability->cap.valueInt = priv->cfg->text_height;
  159. return GF_OK;
  160. case GF_CODEC_MEDIA_NOT_OVER:
  161. capability->cap.valueInt = priv->is_active;
  162. return GF_OK;
  163. default:
  164. capability->cap.valueInt = 0;
  165. return GF_OK;
  166. }
  167. }
  168. static GF_Err TTD_SetCapabilities(GF_BaseDecoder *plug, const GF_CodecCapability capability)
  169. {
  170. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  171. if (capability.CapCode==GF_CODEC_SHOW_SCENE) {
  172. if (capability.cap.valueInt) {
  173. TTD_ResetDisplay(priv);
  174. TTD_UpdateSizeInfo(priv);
  175. gf_scene_register_extra_graph(priv->inlineScene, priv->sg, 0);
  176. } else {
  177. gf_scene_register_extra_graph(priv->inlineScene, priv->sg, 1);
  178. }
  179. }
  180. return GF_OK;
  181. }
  182. GF_Err TTD_AttachScene(GF_SceneDecoder *plug, GF_Scene *scene, Bool is_scene_decoder)
  183. {
  184. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  185. if (priv->nb_streams) return GF_BAD_PARAM;
  186. /*timedtext cannot be a root scene object*/
  187. if (is_scene_decoder) return GF_BAD_PARAM;
  188. priv->inlineScene = scene;
  189. priv->app = scene->root_od->term;
  190. return GF_OK;
  191. }
  192. GF_Err TTD_ReleaseScene(GF_SceneDecoder *plug)
  193. {
  194. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  195. if (priv->nb_streams) return GF_BAD_PARAM;
  196. return GF_OK;
  197. }
  198. static GFINLINE void add_child(GF_Node *n1, GF_Node *par)
  199. {
  200. gf_node_list_add_child( & ((GF_ParentNode *)par)->children, n1);
  201. gf_node_register(n1, par);
  202. }
  203. static GFINLINE GF_Node *ttd_create_node(TTDPriv *ttd, u32 tag, const char *def_name)
  204. {
  205. GF_Node *n = gf_node_new(ttd->sg, tag);
  206. if (n) {
  207. if (def_name) gf_node_set_id(n, gf_sg_get_next_available_node_id(ttd->sg), def_name);
  208. gf_node_init(n);
  209. }
  210. return n;
  211. }
  212. static GF_Err TTD_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd)
  213. {
  214. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  215. GF_Err e;
  216. GF_Node *root, *n1, *n2;
  217. const char *opt;
  218. /*no scalable, no upstream*/
  219. if (priv->nb_streams || esd->decoderConfig->upstream) return GF_NOT_SUPPORTED;
  220. if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) return GF_NON_COMPLIANT_BITSTREAM;
  221. priv->cfg = (GF_TextConfig *) gf_odf_desc_new(GF_ODF_TEXT_CFG_TAG);
  222. e = gf_odf_get_text_config(esd->decoderConfig->decoderSpecificInfo, (u8) esd->decoderConfig->objectTypeIndication, priv->cfg);
  223. if (e) {
  224. gf_odf_desc_del((GF_Descriptor *) priv->cfg);
  225. priv->cfg = NULL;
  226. return e;
  227. }
  228. priv->nb_streams++;
  229. if (!priv->cfg->timescale) priv->cfg->timescale = 1000;
  230. priv->sg = gf_sg_new_subscene(priv->inlineScene->graph);
  231. root = ttd_create_node(priv, TAG_MPEG4_OrderedGroup, NULL);
  232. gf_sg_set_root_node(priv->sg, root);
  233. gf_node_register(root, NULL);
  234. /*root transform*/
  235. priv->tr_track = (M_Transform2D *)ttd_create_node(priv, TAG_MPEG4_Transform2D, NULL);
  236. add_child((GF_Node *) priv->tr_track, root);
  237. TTD_UpdateSizeInfo(priv);
  238. /*txt track background*/
  239. n1 = ttd_create_node(priv, TAG_MPEG4_Shape, NULL);
  240. add_child(n1, (GF_Node *) priv->tr_track);
  241. ((M_Shape *)n1)->appearance = ttd_create_node(priv, TAG_MPEG4_Appearance, NULL);
  242. gf_node_register(((M_Shape *)n1)->appearance, n1);
  243. priv->mat_track = (M_Material2D *) ttd_create_node(priv, TAG_MPEG4_Material2D, NULL);
  244. priv->mat_track->filled = 1;
  245. priv->mat_track->transparency = 1;
  246. ((M_Appearance *) ((M_Shape *)n1)->appearance)->material = (GF_Node *) priv->mat_track;
  247. gf_node_register((GF_Node *) priv->mat_track, ((M_Shape *)n1)->appearance);
  248. n2 = ttd_create_node(priv, TAG_MPEG4_Rectangle, NULL);
  249. ((M_Rectangle *)n2)->size.x = 0;
  250. ((M_Rectangle *)n2)->size.y = 0;
  251. ((M_Shape *)n1)->geometry = n2;
  252. gf_node_register(n2, n1);
  253. priv->rec_track = (M_Rectangle *)n2;
  254. /*txt box background*/
  255. priv->tr_box = (M_Transform2D *) ttd_create_node(priv, TAG_MPEG4_Transform2D, NULL);
  256. add_child((GF_Node*) priv->tr_box, (GF_Node*)priv->tr_track);
  257. n1 = ttd_create_node(priv, TAG_MPEG4_Shape, NULL);
  258. add_child(n1, (GF_Node*)priv->tr_box);
  259. ((M_Shape *)n1)->appearance = ttd_create_node(priv, TAG_MPEG4_Appearance, NULL);
  260. gf_node_register(((M_Shape *)n1)->appearance, n1);
  261. priv->mat_box = (M_Material2D *) ttd_create_node(priv, TAG_MPEG4_Material2D, NULL);
  262. priv->mat_box->filled = 1;
  263. priv->mat_box->transparency = 1;
  264. ((M_Appearance *) ((M_Shape *)n1)->appearance)->material = (GF_Node *)priv->mat_box;
  265. gf_node_register((GF_Node *)priv->mat_box, ((M_Shape *)n1)->appearance);
  266. priv->rec_box = (M_Rectangle *) ttd_create_node(priv, TAG_MPEG4_Rectangle, NULL);
  267. priv->rec_box->size.x = 0;
  268. priv->rec_box->size.y = 0;
  269. ((M_Shape *)n1)->geometry = (GF_Node *) priv->rec_box;
  270. gf_node_register((GF_Node *) priv->rec_box, n1);
  271. priv->dlist = (M_Layer2D *) ttd_create_node(priv, TAG_MPEG4_Layer2D, NULL);
  272. priv->dlist->size.x = priv->cfg->text_width;
  273. priv->dlist->size.y = priv->cfg->text_height;
  274. add_child((GF_Node *)priv->dlist, (GF_Node *)priv->tr_box);
  275. priv->blink_nodes = gf_list_new();
  276. priv->ts_blink = (M_TimeSensor *) ttd_create_node(priv, TAG_MPEG4_TimeSensor, "TimerBlink");
  277. priv->ts_blink->cycleInterval = 0.25;
  278. priv->ts_blink->startTime = 0.0;
  279. priv->ts_blink->loop = 1;
  280. priv->process_blink = (M_ScalarInterpolator *) ttd_create_node(priv, TAG_MPEG4_ScalarInterpolator, NULL);
  281. /*override set_fraction*/
  282. priv->process_blink->on_set_fraction = ttd_set_blink_fraction;
  283. gf_node_set_private((GF_Node *) priv->process_blink, priv);
  284. /*route from fraction_changed to set_fraction*/
  285. gf_sg_route_new(priv->sg, (GF_Node *) priv->ts_blink, 6, (GF_Node *) priv->process_blink, 0);
  286. priv->ts_scroll = (M_TimeSensor *) ttd_create_node(priv, TAG_MPEG4_TimeSensor, "TimerScroll");
  287. priv->ts_scroll->cycleInterval = 0;
  288. priv->ts_scroll->startTime = -1;
  289. priv->ts_scroll->loop = 0;
  290. priv->process_scroll = (M_ScalarInterpolator *) ttd_create_node(priv, TAG_MPEG4_ScalarInterpolator, NULL);
  291. /*override set_fraction*/
  292. priv->process_scroll->on_set_fraction = ttd_set_scroll_fraction;
  293. gf_node_set_private((GF_Node *) priv->process_scroll, priv);
  294. /*route from fraction_changed to set_fraction*/
  295. gf_sg_route_new(priv->sg, (GF_Node *) priv->ts_scroll, 6, (GF_Node *) priv->process_scroll, 0);
  296. gf_node_register((GF_Node *) priv->ts_blink, NULL);
  297. gf_node_register((GF_Node *) priv->process_blink, NULL);
  298. gf_node_register((GF_Node *) priv->ts_scroll, NULL);
  299. gf_node_register((GF_Node *) priv->process_scroll, NULL);
  300. /*option setup*/
  301. opt = gf_modules_get_option((GF_BaseInterface *)plug, "StreamingText", "UseTexturing");
  302. priv->use_texture = (opt && !strcmp(opt, "yes")) ? 1 : 0;
  303. opt = gf_modules_get_option((GF_BaseInterface *)plug, "StreamingText", "OutlineText");
  304. priv->outline = (opt && !strcmp(opt, "yes")) ? 1 : 0;
  305. return e;
  306. }
  307. static GF_Err TTD_DetachStream(GF_BaseDecoder *plug, u16 ES_ID)
  308. {
  309. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  310. if (!priv->nb_streams) return GF_BAD_PARAM;
  311. gf_scene_register_extra_graph(priv->inlineScene, priv->sg, 1);
  312. gf_node_unregister((GF_Node *) priv->ts_blink, NULL);
  313. gf_node_unregister((GF_Node *) priv->process_blink, NULL);
  314. gf_node_unregister((GF_Node *) priv->ts_scroll, NULL);
  315. gf_node_unregister((GF_Node *) priv->process_scroll, NULL);
  316. gf_sg_del(priv->sg);
  317. priv->sg = NULL;
  318. if (priv->cfg) gf_odf_desc_del((GF_Descriptor *) priv->cfg);
  319. priv->cfg = NULL;
  320. priv->nb_streams = 0;
  321. gf_list_del(priv->blink_nodes);
  322. return GF_OK;
  323. }
  324. static void ttd_set_blink_fraction(GF_Node *node, GF_Route *route)
  325. {
  326. M_Material2D *m;
  327. u32 i;
  328. TTDPriv *priv = (TTDPriv *)gf_node_get_private(node);
  329. Bool blink_on = 1;
  330. if (priv->process_blink->set_fraction>FIX_ONE/2) blink_on = 0;
  331. i=0;
  332. while ((m = (M_Material2D*)gf_list_enum(priv->blink_nodes, &i))) {
  333. if (m->filled != blink_on) {
  334. m->filled = blink_on;
  335. gf_node_changed((GF_Node *) m, NULL);
  336. }
  337. }
  338. }
  339. static void ttd_set_scroll_fraction(GF_Node *node, GF_Route *route)
  340. {
  341. Fixed frac;
  342. TTDPriv *priv = (TTDPriv *)gf_node_get_private(node);
  343. frac = priv->process_scroll->set_fraction;
  344. if (frac==FIX_ONE) priv->is_active = 0;
  345. if (!priv->tr_scroll) return;
  346. switch (priv->scroll_type - 1) {
  347. case GF_TXT_SCROLL_CREDITS:
  348. case GF_TXT_SCROLL_DOWN:
  349. priv->tr_scroll->translation.x = 0;
  350. if (priv->scroll_mode & GF_TXT_SCROLL_IN) {
  351. if (frac>priv->scroll_time) {
  352. priv->scroll_mode &= ~GF_TXT_SCROLL_IN;
  353. priv->tr_scroll->translation.y = 0;
  354. } else {
  355. priv->tr_scroll->translation.y = gf_muldiv(priv->dlist->size.y, frac, priv->scroll_time) - priv->dlist->size.y;
  356. }
  357. } else if (priv->scroll_mode & GF_TXT_SCROLL_OUT) {
  358. if (frac < FIX_ONE - priv->scroll_time) return;
  359. frac -= FIX_ONE - priv->scroll_time;
  360. if (priv->scroll_type - 1 == GF_TXT_SCROLL_DOWN) {
  361. priv->tr_scroll->translation.y = gf_muldiv(priv->dlist->size.y, frac, priv->scroll_time);
  362. } else {
  363. priv->tr_scroll->translation.y = gf_muldiv(priv->dlist->size.y, frac, priv->scroll_time);
  364. }
  365. }
  366. if (priv->scroll_type - 1 == GF_TXT_SCROLL_DOWN) priv->tr_scroll->translation.y *= -1;
  367. break;
  368. case GF_TXT_SCROLL_MARQUEE:
  369. case GF_TXT_SCROLL_RIGHT:
  370. priv->tr_scroll->translation.y = 0;
  371. if (priv->scroll_mode & GF_TXT_SCROLL_IN) {
  372. if (! (priv->scroll_mode & GF_TXT_SCROLL_OUT)) {
  373. if (frac<priv->scroll_delay) return;
  374. frac-=priv->scroll_delay;
  375. }
  376. if (frac>priv->scroll_time) {
  377. priv->scroll_mode &= ~GF_TXT_SCROLL_IN;
  378. priv->tr_scroll->translation.x = 0;
  379. } else {
  380. priv->tr_scroll->translation.x = gf_muldiv(priv->dlist->size.x, frac, priv->scroll_time) - priv->dlist->size.x;
  381. }
  382. } else if (priv->scroll_mode & GF_TXT_SCROLL_OUT) {
  383. if (frac < FIX_ONE - priv->scroll_time) return;
  384. frac -= FIX_ONE - priv->scroll_time;
  385. priv->tr_scroll->translation.x = gf_muldiv(priv->dlist->size.x, frac, priv->scroll_time);
  386. }
  387. if (priv->scroll_type - 1 == GF_TXT_SCROLL_MARQUEE) priv->tr_scroll->translation.x *= -1;
  388. break;
  389. default:
  390. break;
  391. }
  392. gf_node_changed((GF_Node *)priv->tr_scroll, NULL);
  393. }
  394. static void TTD_ResetDisplay(TTDPriv *priv)
  395. {
  396. gf_list_reset(priv->blink_nodes);
  397. gf_node_unregister_children((GF_Node*)priv->dlist, priv->dlist->children);
  398. priv->dlist->children = NULL;
  399. gf_node_changed((GF_Node *) priv->dlist, NULL);
  400. priv->tr_scroll = NULL;
  401. }
  402. char *TTD_FindFont(GF_TextSampleDescriptor *tsd, u32 ID)
  403. {
  404. u32 i;
  405. for (i=0; i<tsd->font_count; i++) {
  406. if (tsd->fonts[i].fontID==ID) return tsd->fonts[i].fontName;
  407. }
  408. return "SERIF";
  409. }
  410. static void ttd_add_item(M_Form *form)
  411. {
  412. s32 *new_gr;
  413. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &new_gr);
  414. (*new_gr) = gf_node_list_get_count(form->children);
  415. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &new_gr);
  416. (*new_gr) = -1;
  417. /*store line info*/
  418. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &new_gr);
  419. (*new_gr) = gf_node_list_get_count(form->children);
  420. }
  421. static void ttd_add_line(M_Form *form)
  422. {
  423. s32 *new_gr;
  424. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &new_gr);
  425. (*new_gr) = -1;
  426. }
  427. typedef struct
  428. {
  429. u32 start_char, end_char;
  430. GF_StyleRecord *srec;
  431. Bool is_hilight;
  432. u32 hilight_col; /*0 means RV*/
  433. GF_TextHyperTextBox *hlink;
  434. Bool has_blink;
  435. /*karaoke not done yet*/
  436. /*text wrapping is not supported - we will need to move to Layout (rather than form), and modify
  437. layout to handle new lines and proper scrolling*/
  438. } TTDTextChunk;
  439. static void TTD_NewTextChunk(TTDPriv *priv, GF_TextSampleDescriptor *tsd, M_Form *form, u16 *utf16_txt, TTDTextChunk *tc)
  440. {
  441. GF_Node *txt_model, *n2, *txt_material;
  442. M_Text *text;
  443. M_FontStyle *fs;
  444. char *fontName;
  445. char szStyle[1024];
  446. u32 fontSize, styleFlags, color, i, start_char;
  447. if (!tc->srec) {
  448. fontName = TTD_FindFont(tsd, tsd->default_style.fontID);
  449. fontSize = tsd->default_style.font_size;
  450. styleFlags = tsd->default_style.style_flags;
  451. color = tsd->default_style.text_color;
  452. } else {
  453. fontName = TTD_FindFont(tsd, tc->srec->fontID);
  454. fontSize = tc->srec->font_size;
  455. styleFlags = tc->srec->style_flags;
  456. color = tc->srec->text_color;
  457. }
  458. /*create base model for text node. It will then be cloned for each text item*/
  459. txt_model = ttd_create_node(priv, TAG_MPEG4_Shape, NULL);
  460. gf_node_register(txt_model, NULL);
  461. n2 = ttd_create_node(priv, TAG_MPEG4_Appearance, NULL);
  462. ((M_Shape *)txt_model)->appearance = n2;
  463. gf_node_register(n2, txt_model);
  464. txt_material = ttd_create_node(priv, TAG_MPEG4_Material2D, NULL);
  465. ((M_Appearance *)n2)->material = txt_material;
  466. gf_node_register(txt_material, n2);
  467. ((M_Material2D *)txt_material)->filled = 1;
  468. ((M_Material2D *)txt_material)->transparency = FIX_ONE - INT2FIX((color>>24) & 0xFF) / 255;
  469. ((M_Material2D *)txt_material)->emissiveColor.red = INT2FIX((color>>16) & 0xFF) / 255;
  470. ((M_Material2D *)txt_material)->emissiveColor.green = INT2FIX((color>>8) & 0xFF) / 255;
  471. ((M_Material2D *)txt_material)->emissiveColor.blue = INT2FIX((color) & 0xFF) / 255;
  472. /*force 0 lineWidth if blinking (the stupid MPEG-4 default values once again..)*/
  473. if (tc->has_blink) {
  474. ((M_Material2D *)txt_material)->lineProps = ttd_create_node(priv, TAG_MPEG4_LineProperties, NULL);
  475. ((M_LineProperties *)((M_Material2D *)txt_material)->lineProps)->width = 0;
  476. gf_node_register(((M_Material2D *)txt_material)->lineProps, txt_material);
  477. }
  478. n2 = ttd_create_node(priv, TAG_MPEG4_Text, NULL);
  479. ((M_Shape *)txt_model)->geometry = n2;
  480. gf_node_register(n2, txt_model);
  481. text = (M_Text *) n2;
  482. fs = (M_FontStyle *) ttd_create_node(priv, TAG_MPEG4_FontStyle, NULL);
  483. gf_free(fs->family.vals[0]);
  484. /*translate default fonts to MPEG-4/VRML names*/
  485. if (!stricmp(fontName, "Serif")) fs->family.vals[0] = gf_strdup("SERIF");
  486. else if (!stricmp(fontName, "Sans-Serif")) fs->family.vals[0] = gf_strdup("SANS");
  487. else if (!stricmp(fontName, "Monospace")) fs->family.vals[0] = gf_strdup("TYPEWRITER");
  488. else fs->family.vals[0] = gf_strdup(fontName);
  489. fs->size = INT2FIX(fontSize);
  490. gf_free(fs->style.buffer);
  491. strcpy(szStyle, "");
  492. if (styleFlags & GF_TXT_STYLE_BOLD) {
  493. if (styleFlags & GF_TXT_STYLE_ITALIC) strcpy(szStyle, "BOLDITALIC");
  494. else strcpy(szStyle, "BOLD");
  495. } else if (styleFlags & GF_TXT_STYLE_ITALIC) strcat(szStyle, "ITALIC");
  496. if (!strlen(szStyle)) strcpy(szStyle, "PLAIN");
  497. /*also underline for URLs*/
  498. if ((styleFlags & GF_TXT_STYLE_UNDERLINED) || (tc->hlink && tc->hlink->URL)) strcat(szStyle, " UNDERLINED");
  499. if (tc->is_hilight) {
  500. if (tc->hilight_col) {
  501. char szTxt[50];
  502. sprintf(szTxt, " HIGHLIGHT#%x", tc->hilight_col);
  503. strcat(szStyle, szTxt);
  504. } else {
  505. strcat(szStyle, " HIGHLIGHT#RV");
  506. }
  507. }
  508. /*a better way would be to draw the entire text box in a composite texture & bitmap but we can't really rely
  509. on text box size (in MP4Box, it actually defaults to the entire video area) and drawing a too large texture
  510. & bitmap could slow down rendering*/
  511. if (priv->use_texture) strcat(szStyle, " TEXTURED");
  512. if (priv->outline) strcat(szStyle, " OUTLINED");
  513. fs->style.buffer = gf_strdup(szStyle);
  514. fs->horizontal = (tsd->displayFlags & GF_TXT_VERTICAL) ? 0 : 1;
  515. text->fontStyle = (GF_Node *) fs;
  516. gf_node_register((GF_Node *)fs, (GF_Node *)text);
  517. gf_sg_vrml_mf_reset(&text->string, GF_SG_VRML_MFSTRING);
  518. if (tc->hlink && tc->hlink->URL) {
  519. SFURL *s;
  520. M_Anchor *anc = (M_Anchor *) ttd_create_node(priv, TAG_MPEG4_Anchor, NULL);
  521. gf_sg_vrml_mf_append(&anc->url, GF_SG_VRML_MFURL, (void **) &s);
  522. s->OD_ID = 0;
  523. s->url = gf_strdup(tc->hlink->URL);
  524. if (tc->hlink->URL_hint) anc->description.buffer = gf_strdup(tc->hlink->URL_hint);
  525. gf_node_list_add_child(& anc->children, txt_model);
  526. gf_node_register(txt_model, (GF_Node *)anc);
  527. txt_model = (GF_Node *)anc;
  528. gf_node_register((GF_Node *)anc, NULL);
  529. }
  530. start_char = tc->start_char;
  531. for (i=tc->start_char; i<tc->end_char; i++) {
  532. Bool new_line = 0;
  533. if ((utf16_txt[i] == '\n') || (utf16_txt[i] == '\r') || (utf16_txt[i] == 0x85) || (utf16_txt[i] == 0x2028) || (utf16_txt[i] == 0x2029)) new_line = 1;
  534. if (new_line || (i+1==tc->end_char) ) {
  535. SFString *st;
  536. if (i+1==tc->end_char) i++;
  537. if (i!=start_char) {
  538. char szLine[5000];
  539. u32 len;
  540. s16 wsChunk[5000], *sp;
  541. /*spliting lines, duplicate node*/
  542. n2 = gf_node_clone(priv->sg, txt_model, NULL, "", 1);
  543. if (tc->hlink && tc->hlink->URL) {
  544. GF_Node *t = ((M_Anchor *)n2)->children->node;
  545. text = (M_Text *) ((M_Shape *)t)->geometry;
  546. txt_material = ((M_Appearance *) ((M_Shape *)t)->appearance)->material;
  547. } else {
  548. text = (M_Text *) ((M_Shape *)n2)->geometry;
  549. txt_material = ((M_Appearance *) ((M_Shape *)n2)->appearance)->material;
  550. }
  551. gf_sg_vrml_mf_reset(&text->string, GF_SG_VRML_MFSTRING);
  552. gf_node_list_add_child( &form->children, n2);
  553. gf_node_register(n2, (GF_Node *) form);
  554. ttd_add_item(form);
  555. /*clone node always register by default*/
  556. gf_node_unregister(n2, NULL);
  557. if (tc->has_blink && txt_material) gf_list_add(priv->blink_nodes, txt_material);
  558. memcpy(wsChunk, &utf16_txt[start_char], sizeof(s16)*(i-start_char));
  559. wsChunk[i-start_char] = 0;
  560. sp = &wsChunk[0];
  561. len = (u32) gf_utf8_wcstombs(szLine, 5000, (const unsigned short **) &sp);
  562. szLine[len] = 0;
  563. gf_sg_vrml_mf_append(&text->string, GF_SG_VRML_MFSTRING, (void **) &st);
  564. st->buffer = gf_strdup(szLine);
  565. }
  566. start_char = i+1;
  567. if (new_line) {
  568. ttd_add_line(form);
  569. if ((utf16_txt[i]=='\r') && (utf16_txt[i+1]=='\n')) i++;
  570. }
  571. }
  572. }
  573. gf_node_unregister(txt_model, NULL);
  574. return;
  575. }
  576. /*mod can be any of TextHighlight, TextKaraoke, TextHyperText, TextBlink*/
  577. void TTD_SplitChunks(GF_TextSample *txt, u32 nb_chars, GF_List *chunks, GF_Box *mod)
  578. {
  579. TTDTextChunk *tc;
  580. u32 start_char, end_char;
  581. u32 i;
  582. switch (mod->type) {
  583. /*these 3 can be safelly typecasted to the same struct for start/end char*/
  584. case GF_ISOM_BOX_TYPE_HLIT:
  585. case GF_ISOM_BOX_TYPE_HREF:
  586. case GF_ISOM_BOX_TYPE_BLNK:
  587. start_char = ((GF_TextHighlightBox *)mod)->startcharoffset;
  588. end_char = ((GF_TextHighlightBox *)mod)->endcharoffset;
  589. break;
  590. case GF_ISOM_BOX_TYPE_KROK:
  591. default:
  592. return;
  593. }
  594. if (end_char>nb_chars) end_char = nb_chars;
  595. i=0;
  596. while ((tc = (TTDTextChunk *)gf_list_enum(chunks, &i))) {
  597. if (tc->end_char<=start_char) continue;
  598. /*need to split chunk at begin*/
  599. if (tc->start_char<start_char) {
  600. TTDTextChunk *tc2;
  601. tc2 = (TTDTextChunk *) gf_malloc(sizeof(TTDTextChunk));
  602. memcpy(tc2, tc, sizeof(TTDTextChunk));
  603. tc2->start_char = start_char;
  604. tc2->end_char = tc->end_char;
  605. tc->end_char = start_char;
  606. gf_list_insert(chunks, tc2, i+1);
  607. i++;
  608. tc = tc2;
  609. }
  610. /*need to split chunks at end*/
  611. if (tc->end_char>end_char) {
  612. TTDTextChunk *tc2;
  613. tc2 = (TTDTextChunk *) gf_malloc(sizeof(TTDTextChunk));
  614. memcpy(tc2, tc, sizeof(TTDTextChunk));
  615. tc2->start_char = tc->start_char;
  616. tc2->end_char = end_char;
  617. tc->start_char = end_char;
  618. gf_list_insert(chunks, tc2, i);
  619. i++;
  620. tc = tc2;
  621. }
  622. /*assign mod*/
  623. switch (mod->type) {
  624. case GF_ISOM_BOX_TYPE_HLIT:
  625. tc->is_hilight = 1;
  626. if (txt->highlight_color) tc->hilight_col = txt->highlight_color->hil_color;
  627. break;
  628. case GF_ISOM_BOX_TYPE_HREF:
  629. tc->hlink = (GF_TextHyperTextBox *) mod;
  630. break;
  631. case GF_ISOM_BOX_TYPE_BLNK:
  632. tc->has_blink = 1;
  633. break;
  634. }
  635. /*done*/
  636. if (tc->end_char==end_char) return;
  637. }
  638. }
  639. static void TTD_ApplySample(TTDPriv *priv, GF_TextSample *txt, u32 sdi, Bool is_utf_16, u32 sample_duration)
  640. {
  641. u32 i, nb_lines, start_idx, count;
  642. s32 *id, thw, thh, tw, th, offset;
  643. Bool vertical;
  644. MFInt32 idx;
  645. SFString *s;
  646. GF_BoxRecord br;
  647. M_Material2D *n;
  648. M_Form *form;
  649. u16 utf16_text[5000];
  650. u32 char_offset, char_count;
  651. GF_List *chunks;
  652. TTDTextChunk *tc;
  653. GF_Box *a;
  654. GF_TextSampleDescriptor *td = NULL;
  655. /*stop timer sensor*/
  656. if (gf_list_count(priv->blink_nodes)) {
  657. priv->ts_blink->stopTime = gf_node_get_scene_time((GF_Node *) priv->ts_blink);
  658. gf_node_changed((GF_Node *) priv->ts_blink, NULL);
  659. }
  660. priv->ts_scroll->stopTime = gf_node_get_scene_time((GF_Node *) priv->ts_scroll);
  661. gf_node_changed((GF_Node *) priv->ts_scroll, NULL);
  662. /*flush routes to avoid getting the set_fraction of the scroll sensor deactivation*/
  663. gf_sg_activate_routes(priv->inlineScene->graph);
  664. TTD_ResetDisplay(priv);
  665. if (!sdi || !txt || !txt->len) return;
  666. i=0;
  667. while ((td = (GF_TextSampleDescriptor *)gf_list_enum(priv->cfg->sample_descriptions, &i))) {
  668. if (td->sample_index==sdi) break;
  669. td = NULL;
  670. }
  671. if (!td) return;
  672. vertical = (td->displayFlags & GF_TXT_VERTICAL) ? 1 : 0;
  673. /*set back color*/
  674. /*do we fill the text box or the entire text track region*/
  675. if (td->displayFlags & GF_TXT_FILL_REGION) {
  676. priv->mat_box->transparency = FIX_ONE;
  677. n = priv->mat_track;
  678. } else {
  679. priv->mat_track->transparency = FIX_ONE;
  680. n = priv->mat_box;
  681. }
  682. n->transparency = FIX_ONE - INT2FIX((td->back_color>>24) & 0xFF) / 255;
  683. n->emissiveColor.red = INT2FIX((td->back_color>>16) & 0xFF) / 255;
  684. n->emissiveColor.green = INT2FIX((td->back_color>>8) & 0xFF) / 255;
  685. n->emissiveColor.blue = INT2FIX((td->back_color) & 0xFF) / 255;
  686. gf_node_changed((GF_Node *) n, NULL);
  687. if (txt->box) {
  688. br = txt->box->box;
  689. } else {
  690. br = td->default_pos;
  691. }
  692. if (!br.right || !br.bottom) {
  693. br.top = br.left = 0;
  694. br.right = priv->cfg->text_width;
  695. br.bottom = priv->cfg->text_height;
  696. }
  697. thw = br.right - br.left;
  698. thh = br.bottom - br.top;
  699. if (!thw || !thh) {
  700. br.top = br.left = 0;
  701. thw = priv->cfg->text_width;
  702. thh = priv->cfg->text_height;
  703. }
  704. priv->dlist->size.x = INT2FIX(thw);
  705. priv->dlist->size.y = INT2FIX(thh);
  706. /*disable backgrounds if not used*/
  707. if (priv->mat_track->transparency<FIX_ONE) {
  708. if (priv->rec_track->size.x != priv->cfg->text_width) {
  709. priv->rec_track->size.x = priv->cfg->text_width;
  710. priv->rec_track->size.y = priv->cfg->text_height;
  711. gf_node_changed((GF_Node *) priv->rec_track, NULL);
  712. }
  713. } else if (priv->rec_track->size.x) {
  714. priv->rec_track->size.x = priv->rec_track->size.y = 0;
  715. gf_node_changed((GF_Node *) priv->rec_box, NULL);
  716. }
  717. if (priv->mat_box->transparency<FIX_ONE) {
  718. if (priv->rec_box->size.x != priv->dlist->size.x) {
  719. priv->rec_box->size.x = priv->dlist->size.x;
  720. priv->rec_box->size.y = priv->dlist->size.y;
  721. gf_node_changed((GF_Node *) priv->rec_box, NULL);
  722. }
  723. } else if (priv->rec_box->size.x) {
  724. priv->rec_box->size.x = priv->rec_box->size.y = 0;
  725. gf_node_changed((GF_Node *) priv->rec_box, NULL);
  726. }
  727. form = (M_Form *) ttd_create_node(priv, TAG_MPEG4_Form, NULL);
  728. form->size.x = INT2FIX(thw);
  729. form->size.y = INT2FIX(thh);
  730. thw /= 2;
  731. thh /= 2;
  732. tw = priv->cfg->text_width;
  733. th = priv->cfg->text_height;
  734. /*check translation, we must not get out of scene size - not supported in GPAC*/
  735. offset = br.left - tw/2 + thw;
  736. if (offset + thw < - tw/2) offset = - tw/2 + thw;
  737. else if (offset - thw > tw/2) offset = tw/2 - thw;
  738. priv->tr_box->translation.x = INT2FIX(offset);
  739. offset = th/2 - br.top - thh;
  740. if (offset + thh > th/2) offset = th/2 - thh;
  741. else if (offset - thh < -th/2) offset = -th/2 + thh;
  742. priv->tr_box->translation.y = INT2FIX(offset);
  743. gf_node_dirty_set((GF_Node *)priv->tr_box, 0, 1);
  744. if (priv->scroll_type) {
  745. priv->ts_scroll->stopTime = gf_node_get_scene_time((GF_Node *) priv->ts_scroll);
  746. gf_node_changed((GF_Node *) priv->ts_scroll, NULL);
  747. }
  748. priv->scroll_mode = 0;
  749. if (td->displayFlags & GF_TXT_SCROLL_IN) priv->scroll_mode |= GF_TXT_SCROLL_IN;
  750. if (td->displayFlags & GF_TXT_SCROLL_OUT) priv->scroll_mode |= GF_TXT_SCROLL_OUT;
  751. priv->scroll_type = 0;
  752. if (priv->scroll_mode) {
  753. priv->scroll_type = (td->displayFlags & GF_TXT_SCROLL_DIRECTION)>>7;
  754. priv->scroll_type ++;
  755. }
  756. /*no sample duration, cannot determine scroll rate, so just show*/
  757. if (!sample_duration) priv->scroll_type = 0;
  758. /*no scroll*/
  759. if (!priv->scroll_mode) priv->scroll_type = 0;
  760. if (priv->scroll_type) {
  761. priv->tr_scroll = (M_Transform2D *) ttd_create_node(priv, TAG_MPEG4_Transform2D, NULL);
  762. gf_node_list_add_child( &priv->dlist->children, (GF_Node*)priv->tr_scroll);
  763. gf_node_register((GF_Node *) priv->tr_scroll, (GF_Node *) priv->dlist);
  764. gf_node_list_add_child( &priv->tr_scroll->children, (GF_Node*)form);
  765. gf_node_register((GF_Node *) form, (GF_Node *) priv->tr_scroll);
  766. priv->tr_scroll->translation.x = priv->tr_scroll->translation.y = (priv->scroll_mode & GF_TXT_SCROLL_IN) ? -INT2FIX(1000) : 0;
  767. /*if no delay, text is in motion for the duration of the sample*/
  768. priv->scroll_time = FIX_ONE;
  769. priv->scroll_delay = 0;
  770. if (txt->scroll_delay) {
  771. priv->scroll_delay = gf_divfix(INT2FIX(txt->scroll_delay->scroll_delay), INT2FIX(sample_duration));
  772. if (priv->scroll_delay>FIX_ONE) priv->scroll_delay = FIX_ONE;
  773. priv->scroll_time = (FIX_ONE - priv->scroll_delay);
  774. }
  775. /*if both scroll (in and out), use same scroll duration for both*/
  776. if ((priv->scroll_mode & GF_TXT_SCROLL_IN) && (priv->scroll_mode & GF_TXT_SCROLL_OUT)) priv->scroll_time /= 2;
  777. } else {
  778. gf_node_list_add_child( &priv->dlist->children, (GF_Node*)form);
  779. gf_node_register((GF_Node *) form, (GF_Node *) priv->dlist);
  780. priv->tr_scroll = NULL;
  781. }
  782. if (is_utf_16) {
  783. memcpy((char *) utf16_text, txt->text, sizeof(char) * txt->len);
  784. ((char *) utf16_text)[txt->len] = 0;
  785. ((char *) utf16_text)[txt->len+1] = 0;
  786. char_count = txt->len / 2;
  787. } else {
  788. char *p = txt->text;
  789. char_count = (u32) gf_utf8_mbstowcs(utf16_text, 2500, (const char **) &p);
  790. }
  791. chunks = gf_list_new();
  792. /*flatten all modifiers*/
  793. if (!txt->styles || !txt->styles->entry_count) {
  794. GF_SAFEALLOC(tc, TTDTextChunk);
  795. tc->end_char = char_count;
  796. gf_list_add(chunks, tc);
  797. } else {
  798. GF_StyleRecord *srec = NULL;
  799. char_offset = 0;
  800. for (i=0; i<txt->styles->entry_count; i++) {
  801. TTDTextChunk *tc;
  802. srec = &txt->styles->styles[i];
  803. if (srec->startCharOffset==srec->endCharOffset) continue;
  804. /*handle not continuous modifiers*/
  805. if (char_offset < srec->startCharOffset) {
  806. GF_SAFEALLOC(tc, TTDTextChunk);
  807. tc->start_char = char_offset;
  808. tc->end_char = srec->startCharOffset;
  809. gf_list_add(chunks, tc);
  810. }
  811. GF_SAFEALLOC(tc, TTDTextChunk);
  812. tc->start_char = srec->startCharOffset;
  813. tc->end_char = srec->endCharOffset;
  814. tc->srec = srec;
  815. gf_list_add(chunks, tc);
  816. char_offset = srec->endCharOffset;
  817. }
  818. if (srec->endCharOffset<char_count) {
  819. GF_SAFEALLOC(tc, TTDTextChunk);
  820. tc->start_char = char_offset;
  821. tc->end_char = char_count;
  822. gf_list_add(chunks, tc);
  823. }
  824. }
  825. /*apply all other modifiers*/
  826. i=0;
  827. while ((a = (GF_Box*)gf_list_enum(txt->others, &i))) {
  828. TTD_SplitChunks(txt, char_count, chunks, a);
  829. }
  830. while (gf_list_count(chunks)) {
  831. tc = (TTDTextChunk*)gf_list_get(chunks, 0);
  832. gf_list_rem(chunks, 0);
  833. TTD_NewTextChunk(priv, td, form, utf16_text, tc);
  834. gf_free(tc);
  835. }
  836. gf_list_del(chunks);
  837. if (form->groupsIndex.vals[form->groupsIndex.count-1] != -1)
  838. ttd_add_line(form);
  839. /*rewrite form groupIndex - group is fine (eg one child per group)*/
  840. idx.count = form->groupsIndex.count;
  841. idx.vals = form->groupsIndex.vals;
  842. form->groupsIndex.vals = NULL;
  843. form->groupsIndex.count = 0;
  844. nb_lines = 0;
  845. start_idx = 0;
  846. for (i=0; i<idx.count; i++) {
  847. if (idx.vals[i] == -1) {
  848. s32 *id;
  849. u32 j;
  850. /*only one item in line, no need for alignment, but still add a group (we could use the
  851. item as a group but that would complicate the alignment generation)*/
  852. if (start_idx==i-1) {
  853. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = idx.vals[start_idx];
  854. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  855. } else {
  856. /*spread horizontal 0 pixels (eg align) all items in line*/
  857. gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s); s->buffer = gf_strdup(vertical ? "SV 0" : "SH 0");
  858. for (j=start_idx; j<i; j++) {
  859. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = idx.vals[j];
  860. /*also add a group for the line, for final justif*/
  861. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = idx.vals[j];
  862. }
  863. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  864. /*mark end of group*/
  865. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  866. }
  867. start_idx = i+1;
  868. nb_lines ++;
  869. }
  870. }
  871. gf_free(idx.vals);
  872. /*finally add constraints on lines*/
  873. start_idx = gf_node_list_get_count(form->children) + 1;
  874. /*horizontal alignment*/
  875. gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s);
  876. if (vertical) {
  877. switch (td->vert_justif) {
  878. case 1: s->buffer = gf_strdup("AV"); break;/*center*/
  879. case -1: s->buffer = gf_strdup("AB"); break;/*bottom*/
  880. default: s->buffer = gf_strdup("AT"); break;/*top*/
  881. }
  882. } else {
  883. switch (td->horiz_justif) {
  884. case 1: s->buffer = gf_strdup("AH"); break;/*center*/
  885. case -1: s->buffer = gf_strdup("AR"); break;/*right*/
  886. default: s->buffer = gf_strdup("AL"); break;/*left*/
  887. }
  888. }
  889. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = 0;
  890. for (i=0; i<nb_lines; i++) {
  891. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = i+start_idx;
  892. }
  893. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  894. /*vertical alignment: first align all items vertically, 0 pixel */
  895. gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s); s->buffer = gf_strdup(vertical ? "SH 0" : "SV 0");
  896. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = 0;
  897. for (i=0; i<nb_lines; i++) {
  898. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = i+start_idx;
  899. }
  900. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  901. /*define a group with every item drawn*/
  902. count = gf_node_list_get_count(form->children);
  903. for (i=0; i<count; i++) {
  904. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = i+1;
  905. }
  906. gf_sg_vrml_mf_append(&form->groups, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  907. gf_sg_vrml_mf_append(&form->constraints, GF_SG_VRML_MFSTRING, (void **) &s);
  908. if (vertical) {
  909. switch (td->horiz_justif) {
  910. case 1: s->buffer = gf_strdup("AH"); break;/*center*/
  911. case -1: s->buffer = gf_strdup("AR"); break;/*right*/
  912. default: s->buffer = gf_strdup("AL"); break;/*left*/
  913. }
  914. } else {
  915. switch (td->vert_justif) {
  916. case 1: s->buffer = gf_strdup("AV"); break;/*center*/
  917. case -1: s->buffer = gf_strdup("AB"); break;/*bottom*/
  918. default: s->buffer = gf_strdup("AT"); break;/*top*/
  919. }
  920. }
  921. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = 0;
  922. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = start_idx + nb_lines;
  923. gf_sg_vrml_mf_append(&form->groupsIndex, GF_SG_VRML_MFINT32, (void **) &id); (*id) = -1;
  924. gf_node_dirty_set((GF_Node *)form, 0, 1);
  925. gf_node_changed((GF_Node *)form, NULL);
  926. gf_node_changed((GF_Node *) priv->dlist, NULL);
  927. if (gf_list_count(priv->blink_nodes)) {
  928. /*restart time sensor*/
  929. priv->ts_blink->startTime = gf_node_get_scene_time((GF_Node *) priv->ts_blink);
  930. gf_node_changed((GF_Node *) priv->ts_blink, NULL);
  931. }
  932. priv->is_active = 1;
  933. /*scroll timer also acts as AU timer*/
  934. priv->ts_scroll->startTime = gf_node_get_scene_time((GF_Node *) priv->ts_scroll);
  935. priv->ts_scroll->stopTime = priv->ts_scroll->startTime - 1.0;
  936. priv->ts_scroll->cycleInterval = sample_duration;
  937. priv->ts_scroll->cycleInterval /= priv->cfg->timescale;
  938. priv->ts_scroll->cycleInterval -= 0.1;
  939. gf_node_changed((GF_Node *) priv->ts_scroll, NULL);
  940. }
  941. static GF_Err TTD_ProcessData(GF_SceneDecoder*plug, const char *inBuffer, u32 inBufferLength,
  942. u16 ES_ID, u32 AU_time, u32 mmlevel)
  943. {
  944. GF_BitStream *bs;
  945. GF_Err e = GF_OK;
  946. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  947. bs = gf_bs_new(inBuffer, inBufferLength, GF_BITSTREAM_READ);
  948. while (gf_bs_available(bs)) {
  949. GF_TextSample *txt;
  950. Bool is_utf_16;
  951. u32 type, length, sample_index, sample_duration;
  952. is_utf_16 = gf_bs_read_int(bs, 1);
  953. gf_bs_read_int(bs, 4);
  954. type = gf_bs_read_int(bs, 3);
  955. length = gf_bs_read_u16(bs);
  956. /*currently only full text samples are supported*/
  957. if (type != 1) {
  958. gf_bs_del(bs);
  959. return GF_NOT_SUPPORTED;
  960. }
  961. sample_index = gf_bs_read_u8(bs);
  962. /*duration*/
  963. sample_duration = gf_bs_read_u24(bs);
  964. length -= 8;
  965. /*txt length is parsed with the sample*/
  966. txt = gf_isom_parse_texte_sample(bs);
  967. TTD_ApplySample(priv, txt, sample_index, is_utf_16, sample_duration);
  968. gf_isom_delete_text_sample(txt);
  969. /*since we support only TTU(1), no need to go on*/
  970. break;
  971. }
  972. gf_bs_del(bs);
  973. return e;
  974. }
  975. static u32 TTD_CanHandleStream(GF_BaseDecoder *ifce, u32 StreamType, GF_ESD *esd, u8 PL)
  976. {
  977. /*TTDPriv *priv = (TTDPriv *)ifce->privateStack;*/
  978. if (StreamType!=GF_STREAM_TEXT) return GF_CODEC_NOT_SUPPORTED;
  979. /*media type query*/
  980. if (!esd) return GF_CODEC_STREAM_TYPE_SUPPORTED;
  981. if (esd->decoderConfig->objectTypeIndication==0x08) return GF_CODEC_SUPPORTED;
  982. return GF_CODEC_NOT_SUPPORTED;
  983. }
  984. void DeleteTimedTextDec(GF_BaseDecoder *plug)
  985. {
  986. TTDPriv *priv = (TTDPriv *)plug->privateStack;
  987. /*in case something went wrong*/
  988. if (priv->cfg) gf_odf_desc_del((GF_Descriptor *) priv->cfg);
  989. gf_free(priv);
  990. gf_free(plug);
  991. }
  992. GF_BaseDecoder *NewTimedTextDec()
  993. {
  994. TTDPriv *priv;
  995. GF_SceneDecoder *tmp;
  996. GF_SAFEALLOC(tmp, GF_SceneDecoder);
  997. if (!tmp) return NULL;
  998. GF_SAFEALLOC(priv, TTDPriv);
  999. tmp->privateStack = priv;
  1000. tmp->AttachStream = TTD_AttachStream;
  1001. tmp->DetachStream = TTD_DetachStream;
  1002. tmp->GetCapabilities = TTD_GetCapabilities;
  1003. tmp->SetCapabilities = TTD_SetCapabilities;
  1004. tmp->ProcessData = TTD_ProcessData;
  1005. tmp->AttachScene = TTD_AttachScene;
  1006. tmp->CanHandleStream = TTD_CanHandleStream;
  1007. tmp->ReleaseScene = TTD_ReleaseScene;
  1008. GF_REGISTER_MODULE_INTERFACE(tmp, GF_SCENE_DECODER_INTERFACE, "GPAC TimedText Decoder", "gpac distribution")
  1009. return (GF_BaseDecoder *) tmp;
  1010. }
  1011. #if !defined(GPAC_DISABLE_ISOM_WRITE) && !defined(GPAC_DISABLE_MEDIA_IMPORT)
  1012. void DeleteTTReader(void *ifce);
  1013. void *NewTTReader();
  1014. #endif
  1015. GPAC_MODULE_EXPORT
  1016. GF_BaseInterface *LoadInterface(u32 InterfaceType)
  1017. {
  1018. switch (InterfaceType) {
  1019. case GF_SCENE_DECODER_INTERFACE: return (GF_BaseInterface *)NewTimedTextDec();
  1020. #if !defined(GPAC_DISABLE_ISOM_WRITE) && !defined(GPAC_DISABLE_MEDIA_IMPORT)
  1021. case GF_NET_CLIENT_INTERFACE: return (GF_BaseInterface *)NewTTReader();
  1022. #endif
  1023. default: return NULL;
  1024. }
  1025. }
  1026. GPAC_MODULE_EXPORT
  1027. void ShutdownInterface(GF_BaseInterface *ifce)
  1028. {
  1029. switch (ifce->InterfaceType) {
  1030. case GF_SCENE_DECODER_INTERFACE:
  1031. DeleteTimedTextDec((GF_BaseDecoder *)ifce);
  1032. break;
  1033. #if !defined(GPAC_DISABLE_ISOM_WRITE) && !defined(GPAC_DISABLE_MEDIA_IMPORT)
  1034. case GF_NET_CLIENT_INTERFACE:
  1035. DeleteTTReader(ifce);
  1036. break;
  1037. #endif
  1038. }
  1039. }
  1040. #else
  1041. GPAC_MODULE_EXPORT
  1042. GF_BaseInterface *LoadInterface(u32 InterfaceType) { return NULL; }
  1043. GPAC_MODULE_EXPORT
  1044. void ShutdownInterface(GF_BaseInterface *ifce) {}
  1045. #endif /*!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_ISOM)*/
  1046. GPAC_MODULE_EXPORT
  1047. const u32 *QueryInterfaces()
  1048. {
  1049. static u32 si [] = {
  1050. #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_ISOM)
  1051. GF_SCENE_DECODER_INTERFACE,
  1052. GF_NET_CLIENT_INTERFACE,
  1053. #endif
  1054. 0
  1055. };
  1056. return si;
  1057. }
  1058. GPAC_MODULE_STATIC_DELARATION( timedtext )