1 /* GStreamer
2 * Copyright (C) 2013 CableLabs, Louisville, CO 80027
3 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
4 * @Author: Chengjun Wang <cjun.wang@samsung.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21 #include <gst/gst.h>
22 #include <pango/pangocairo.h>
23 #include <gstcea708decoder.h>
24 #include <string.h>
25
26 #define GST_CAT_DEFAULT gst_cea708_decoder_debug
27 GST_DEBUG_CATEGORY (gst_cea708_decoder_debug);
28
29 void
gst_cea708_decoder_init_debug(void)30 gst_cea708_decoder_init_debug (void)
31 {
32 GST_DEBUG_CATEGORY_INIT (gst_cea708_decoder_debug, "cc708decoder", 0,
33 "CEA708 Closed Caption Decoder");
34 }
35
36 /* 708 colors are defined by 2 bits each for R,G,&B for a total of 64 color combinations */
37 static const gchar *color_names[] = {
38 "black",
39 "white",
40 "red",
41 "green",
42 "blue",
43 "yellow",
44 "magenta",
45 "cyan",
46 NULL
47 };
48
49 static const gchar *font_names[] = {
50 "serif",
51 "courier",
52 "times new roman",
53 "helvetica",
54 "Arial",
55 "Dom Casual",
56 "Coronet",
57 "Gothic",
58 NULL
59 };
60
61 static const gchar *pen_size_names[] = {
62 "30", /*"small" */
63 "36", /*"medium" */
64 "42", /*"large" */
65 NULL
66 };
67
68 /* G2 table defined in EIA/CEA-708 Spec */
69 static const gunichar g2_table[CC_MAX_CODE_SET_SIZE] = {
70 ' ', 0xA0, 0, 0, 0, 0x2026, 0, 0,
71 0, 0, 0x160, 0, 0x152, 0, 0, 0,
72 0x2588, 0x2018, 0x2019, 0x201c, 0x201d, 0xB7, 0, 0,
73 0, 0x2122, 0x161, 0, 0x153, 0x2120, 0, 0x178,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0,
80 0, 0, 0, 0, 0, 0, 0x215b, 0x215c,
81 0x215d, 0x215e, 0x2502, 0x2510, 0x2514, 0x2500, 0x2518, 0x250c,
82 };
83
84 static void gst_cea708dec_print_command_name (Cea708Dec * decoder, guint8 c);
85 static void gst_cea708dec_render_pangocairo (cea708Window * window);
86 static void
87 gst_cea708dec_adjust_values_with_fontdesc (cea708Window * window,
88 PangoFontDescription * desc);
89 static gint
90 gst_cea708dec_text_list_add (GSList ** text_list,
91 gint len, const gchar * format, ...);
92 static const PangoAlignment gst_cea708dec_get_align_mode (guint8 justify_mode);
93 static const gchar *gst_cea708dec_get_color_name (guint8 color);
94 static guint8 gst_cea708dec_map_minimum_color (guint8 color);
95 static void
96 gst_cea708dec_set_pen_color (Cea708Dec * decoder,
97 guint8 * dtvcc_buffer, int index);
98 static void
99 gst_cea708dec_set_window_attributes (Cea708Dec * decoder,
100 guint8 * dtvcc_buffer, int index);
101 static void
102 gst_cea708dec_set_pen_style (Cea708Dec * decoder, guint8 pen_style_id);
103 static void
104 gst_cea708dec_set_window_style (Cea708Dec * decoder, guint8 style_id);
105 static void
106 gst_cea708dec_define_window (Cea708Dec * decoder,
107 guint8 * dtvcc_buffer, int index);
108 static inline void
109 pango_span_markup_init (cea708PangoSpanControl * span_control);
110 static inline void
111 pango_span_markup_start (cea708PangoSpanControl * span_control,
112 gchar * line_buffer, guint16 * index);
113 static inline void
114 pango_span_markup_txt (cea708PangoSpanControl * span_control,
115 gchar * line_buffer, guint16 * index);
116 static inline void
117 pango_span_markup_end (cea708PangoSpanControl * span_control,
118 gchar * line_buffer, guint16 * index);
119 static void
120 gst_cea708dec_show_pango_window (Cea708Dec * decoder, guint window_id);
121 static void
122 gst_cea708dec_clear_window_text (Cea708Dec * decoder, guint window_id);
123 static void
124 gst_cea708dec_scroll_window_up (Cea708Dec * decoder, guint window_id);
125 static void gst_cea708dec_init_window (Cea708Dec * decoder, guint window_id);
126 static void gst_cea708dec_clear_window (Cea708Dec * decoder, cea708Window * w);
127 static void
128 gst_cea708dec_set_pen_attributes (Cea708Dec * decoder,
129 guint8 * dtvcc_buffer, int index);
130 static void
131 gst_cea708dec_for_each_window (Cea708Dec * decoder,
132 guint8 window_list, VisibilityControl visibility_control,
133 const gchar * log_message, void (*function) (Cea708Dec * decoder,
134 guint window_id));
135 static void
136 gst_cea708dec_process_command (Cea708Dec * decoder,
137 guint8 * dtvcc_buffer, int index);
138 static void get_cea708dec_bufcat (gpointer data, gpointer whole_buf);
139 static gboolean
140 gst_cea708dec_render_text (Cea708Dec * decoder, GSList ** text_list,
141 gint length, guint window_id);
142 static void gst_cea708dec_window_add_char (Cea708Dec * decoder, gunichar c);
143 static void
144 gst_cea708dec_process_c2 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
145 int index);
146 static void
147 gst_cea708dec_process_g2 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
148 int index);
149 static void
150 gst_cea708dec_process_c3 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
151 int index);
152 static void
153 gst_cea708dec_process_g3 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
154 int index);
155 static void
156 gst_cea708dec_process_dtvcc_byte (Cea708Dec * decoder,
157 guint8 * dtvcc_buffer, int index);
158
159 /* For debug, print name of 708 command */
160 Cea708Dec *
gst_cea708dec_create(PangoContext * pango_context)161 gst_cea708dec_create (PangoContext * pango_context)
162 {
163 int i;
164 Cea708Dec *decoder = g_malloc (sizeof (Cea708Dec));;
165 memset (decoder, 0, sizeof (Cea708Dec));
166
167 /* Initialize 708 variables */
168 for (i = 0; i < MAX_708_WINDOWS; i++) {
169 decoder->cc_windows[i] = g_malloc (sizeof (cea708Window));
170 gst_cea708dec_init_window (decoder, i);
171 }
172 decoder->desired_service = 1;
173 decoder->use_ARGB = FALSE;
174 decoder->pango_context = pango_context;
175 return decoder;
176 }
177
178 void
gst_cea708dec_free(Cea708Dec * dec)179 gst_cea708dec_free (Cea708Dec * dec)
180 {
181 int i;
182
183 for (i = 0; i < MAX_708_WINDOWS; i++) {
184 cea708Window *window = dec->cc_windows[i];
185 gst_cea708dec_clear_window (dec, window);
186 g_free (window);
187 }
188 memset (dec, 0, sizeof (Cea708Dec));
189 g_free (dec);
190 }
191
192 void
gst_cea708dec_set_service_number(Cea708Dec * decoder,gint8 desired_service)193 gst_cea708dec_set_service_number (Cea708Dec * decoder, gint8 desired_service)
194 {
195 int i = 0;
196 gint8 previous_desired_service;
197 previous_desired_service = decoder->desired_service;
198 decoder->desired_service = desired_service;
199 /* If there has been a change in the desired service number, then clear
200 * the windows for the new service. */
201 if (decoder->desired_service != previous_desired_service) {
202 for (i = 0; i < MAX_708_WINDOWS; i++) {
203 gst_cea708dec_init_window (decoder, i);
204 }
205 decoder->current_window = 0;
206 }
207 }
208
209 gboolean
gst_cea708dec_process_dtvcc_packet(Cea708Dec * decoder,guint8 * dtvcc_buffer,gsize dtvcc_size)210 gst_cea708dec_process_dtvcc_packet (Cea708Dec * decoder, guint8 * dtvcc_buffer,
211 gsize dtvcc_size)
212 {
213 guint i;
214 gboolean need_render = FALSE;
215 cea708Window *window = NULL;
216 guint window_id;
217
218 /* Service block header (see CEA-708 6.2.1) */
219 guint8 block_size;
220 guint8 service_number;
221
222 guint parse_index = 0;
223 #ifndef GST_DISABLE_GST_DEBUG
224 guint8 sequence_number = (dtvcc_buffer[parse_index] & 0xC0) >> 6;
225 guint8 pkt_size = DTVCC_PKT_SIZE (dtvcc_buffer[parse_index] & 0x3F);
226 #endif
227
228 parse_index += 1;
229
230 block_size = dtvcc_buffer[parse_index] & 0x1F;
231 service_number = (dtvcc_buffer[parse_index] & 0xE0) >> 5;
232 parse_index += 1;
233
234 if (service_number == 7) {
235 /* Get extended service number */
236 service_number = dtvcc_buffer[parse_index] & 0x3F;
237 parse_index += 1;
238 }
239
240 GST_LOG ("full_size:%" G_GSIZE_FORMAT
241 " size=%d seq=%d block_size=%d service_num=%d", dtvcc_size, pkt_size,
242 sequence_number, block_size, service_number);
243
244
245 /* Process desired_service cc data */
246 if (decoder->desired_service == service_number) {
247 for (i = 0; i < block_size; i++) {
248 /* The Dtvcc buffer contains a stream of commands, command parameters,
249 * and characters which are the actual captions. Process commands and
250 * store captions in simulated 708 windows: */
251 gst_cea708dec_process_dtvcc_byte (decoder, dtvcc_buffer, parse_index + i);
252 }
253
254 for (window_id = 0; window_id < 8; window_id++) {
255 window = decoder->cc_windows[window_id];
256 GST_LOG ("window #%02d deleted:%d visible:%d updated:%d", window_id,
257 window->deleted, window->visible, window->updated);
258 if (!window->updated) {
259 continue;
260 }
261 need_render = TRUE;
262 }
263 }
264
265 return need_render;
266 }
267
268 static void
gst_cea708dec_process_dtvcc_byte(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)269 gst_cea708dec_process_dtvcc_byte (Cea708Dec * decoder,
270 guint8 * dtvcc_buffer, int index)
271 {
272 guint8 c = dtvcc_buffer[index];
273
274 if (decoder->output_ignore) {
275 /* Ignore characters/parameters after a command. */
276 /* GST_TRACE ("[%d] ignore %X", decoder->output_ignore, c); */
277 decoder->output_ignore--;
278 return;
279 }
280 GST_DEBUG ("processing 0x%02X", c);
281
282 if (c >= 0x00 && c <= 0x1F) { /* C0 */
283 if (c == 0x03) { /* ETX */
284 gst_cea708dec_process_command (decoder, dtvcc_buffer, index);
285 } else if (c == 0x00 || c == 0x08 || c == 0x0C || c == 0x0D || c == 0x0E) {
286 gst_cea708dec_window_add_char (decoder, c);
287 } else if (c == 0x10) { /* EXT1 */
288 guint8 next_c = dtvcc_buffer[index + 1];
289 if (next_c >= 0x00 && next_c <= 0x1F) { /* C2 */
290 gst_cea708dec_process_c2 (decoder, dtvcc_buffer, index + 1);
291 } else if (next_c >= 0x20 && next_c <= 0x7F) { /* G2 */
292 gst_cea708dec_process_g2 (decoder, dtvcc_buffer, index + 1);
293 } else if (next_c >= 0x80 && next_c <= 0x9F) { /* C3 */
294 gst_cea708dec_process_c3 (decoder, dtvcc_buffer, index + 1);
295 } else if (next_c >= 0xA0 && next_c <= 0xFF) { /* G3 */
296 gst_cea708dec_process_g3 (decoder, dtvcc_buffer, index + 1);
297 }
298 } else if (c > 0x10 && c < 0x18) {
299 decoder->output_ignore = 1;
300 GST_INFO ("do not support 0x11-0x17");
301 } else if (c >= 0x18) { /* P16 */
302 /*P16 do not support now */
303 decoder->output_ignore = 2;
304 GST_INFO ("do not support 0x18-0x1F");
305 }
306 } else if ((c >= 0x20) && (c <= 0x7F)) { /* G0 */
307 if (c == 0x7F) {
308 gst_cea708dec_window_add_char (decoder, CC_SPECIAL_CODE_MUSIC_NOTE);
309 } else {
310 gst_cea708dec_window_add_char (decoder, c);
311 }
312 } else if ((c >= 0x80) && (c <= 0x9F)) { /* C1 */
313 gst_cea708dec_process_command (decoder, dtvcc_buffer, index);
314 } else if ((c >= 0xA0) && (c <= 0xFF)) { /* G1 */
315 gst_cea708dec_window_add_char (decoder, c);
316 }
317 }
318
319 /* For debug, print name of 708 command */
320 static void
gst_cea708dec_print_command_name(Cea708Dec * decoder,guint8 c)321 gst_cea708dec_print_command_name (Cea708Dec * decoder, guint8 c)
322 {
323 gchar buffer[32];
324 const gchar *command = NULL;
325
326 switch (c) {
327 case CC_COMMAND_ETX:
328 command = (const gchar *) "End of text";
329 break;
330
331 case CC_COMMAND_CW0:
332 case CC_COMMAND_CW1:
333 case CC_COMMAND_CW2:
334 case CC_COMMAND_CW3:
335 case CC_COMMAND_CW4:
336 case CC_COMMAND_CW5:
337 case CC_COMMAND_CW6:
338 case CC_COMMAND_CW7:
339 /* Set current window, no parameters */
340 g_snprintf (buffer, sizeof (buffer), "Set current window %d", c & 0x3);
341 command = buffer;
342 break;
343
344 case CC_COMMAND_CLW:
345 command = (const gchar *) "Clear windows";
346 break;
347
348 case CC_COMMAND_DSW:
349 command = (const gchar *) "Display windows";
350 break;
351
352 case CC_COMMAND_HDW:
353 command = (const gchar *) "Hide windows";
354 break;
355
356 case CC_COMMAND_TGW:
357 command = (const gchar *) "Toggle windows";
358 break;
359
360 case CC_COMMAND_DLW:
361 command = (const gchar *) "Delete windows";
362 break;
363
364 case CC_COMMAND_DLY:
365 command = (const gchar *) "Delay";
366 break;
367
368 case CC_COMMAND_DLC:
369 command = (const gchar *) "Delay cancel";
370 break;
371
372 case CC_COMMAND_RST:
373 command = (const gchar *) "Reset";
374 break;
375
376 case CC_COMMAND_SPA:
377 command = (const gchar *) "Set pen attributes";
378 break;
379
380 case CC_COMMAND_SPC:
381 command = (const gchar *) "Set pen color";
382 break;
383
384 case CC_COMMAND_SPL:
385 command = (const gchar *) "Set pen location";
386 break;
387
388 case CC_COMMAND_SWA:
389 command = (const gchar *) "Set window attributes";
390 break;
391
392 case CC_COMMAND_DF0:
393 case CC_COMMAND_DF1:
394 case CC_COMMAND_DF2:
395 case CC_COMMAND_DF3:
396 case CC_COMMAND_DF4:
397 case CC_COMMAND_DF5:
398 case CC_COMMAND_DF6:
399 case CC_COMMAND_DF7:
400 g_snprintf (buffer, sizeof (buffer), "define window %d", c & 0x3);
401 command = buffer;
402 break;
403
404 default:
405 if ((c > 0x80) && (c < 0x9F))
406 command = (const gchar *) "Unknown";
407 break;
408 } /* switch */
409
410 if (NULL != command) {
411 GST_LOG ("Process 708 command (%02X): %s", c, command);
412 }
413 }
414
415 static void
gst_cea708dec_render_pangocairo(cea708Window * window)416 gst_cea708dec_render_pangocairo (cea708Window * window)
417 {
418 cairo_t *crt;
419 cairo_surface_t *surf;
420 cairo_t *shadow;
421 cairo_surface_t *surf_shadow;
422 PangoRectangle ink_rec, logical_rec;
423 gint width, height;
424
425 pango_layout_get_pixel_extents (window->layout, &ink_rec, &logical_rec);
426
427 width = logical_rec.width + window->shadow_offset;
428 height = logical_rec.height + logical_rec.y + window->shadow_offset;
429
430 surf_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
431 shadow = cairo_create (surf_shadow);
432
433 /* clear shadow surface */
434 cairo_set_operator (shadow, CAIRO_OPERATOR_CLEAR);
435 cairo_paint (shadow);
436 cairo_set_operator (shadow, CAIRO_OPERATOR_OVER);
437
438 /* draw shadow text */
439 cairo_save (shadow);
440 cairo_set_source_rgba (shadow, 0.0, 0.0, 0.0, 0.5);
441 cairo_translate (shadow, window->shadow_offset, window->shadow_offset);
442 pango_cairo_show_layout (shadow, window->layout);
443 cairo_restore (shadow);
444
445 /* draw outline text */
446 cairo_save (shadow);
447 cairo_set_source_rgb (shadow, 0.0, 0.0, 0.0);
448 cairo_set_line_width (shadow, window->outline_offset);
449 pango_cairo_layout_path (shadow, window->layout);
450 cairo_stroke (shadow);
451 cairo_restore (shadow);
452
453 cairo_destroy (shadow);
454
455 window->text_image = g_realloc (window->text_image, 4 * width * height);
456
457 surf = cairo_image_surface_create_for_data (window->text_image,
458 CAIRO_FORMAT_ARGB32, width, height, width * 4);
459 crt = cairo_create (surf);
460 cairo_set_operator (crt, CAIRO_OPERATOR_CLEAR);
461 cairo_paint (crt);
462 cairo_set_operator (crt, CAIRO_OPERATOR_OVER);
463
464 /* set default color */
465 cairo_set_source_rgb (crt, 1.0, 1.0, 1.0);
466
467 cairo_save (crt);
468 /* draw text */
469 pango_cairo_show_layout (crt, window->layout);
470 cairo_restore (crt);
471
472 /* composite shadow with offset */
473 cairo_set_operator (crt, CAIRO_OPERATOR_DEST_OVER);
474 cairo_set_source_surface (crt, surf_shadow, 0.0, 0.0);
475 cairo_paint (crt);
476
477 cairo_destroy (crt);
478 cairo_surface_destroy (surf_shadow);
479 cairo_surface_destroy (surf);
480 window->image_width = width;
481 window->image_height = height;
482 }
483
484 static void
gst_cea708dec_adjust_values_with_fontdesc(cea708Window * window,PangoFontDescription * desc)485 gst_cea708dec_adjust_values_with_fontdesc (cea708Window * window,
486 PangoFontDescription * desc)
487 {
488 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
489
490 window->shadow_offset = (double) (font_size) / 13.0;
491 window->outline_offset = (double) (font_size) / 15.0;
492 if (window->outline_offset < MINIMUM_OUTLINE_OFFSET)
493 window->outline_offset = MINIMUM_OUTLINE_OFFSET;
494 }
495
496 static gint
gst_cea708dec_text_list_add(GSList ** text_list,gint len,const gchar * format,...)497 gst_cea708dec_text_list_add (GSList ** text_list,
498 gint len, const gchar * format, ...)
499 {
500 va_list args;
501 gchar *str;
502
503 va_start (args, format);
504
505 str = g_malloc0 (len);
506 len = g_vsnprintf (str, len, format, args);
507 *text_list = g_slist_append (*text_list, str);
508 GST_LOG ("added %p str[%d]: %s", str, len, str);
509
510 va_end (args);
511 return len;
512 }
513
514 static const PangoAlignment
gst_cea708dec_get_align_mode(guint8 justify_mode)515 gst_cea708dec_get_align_mode (guint8 justify_mode)
516 {
517 guint align_mode = PANGO_ALIGN_LEFT;
518
519 switch (justify_mode) {
520 case JUSTIFY_LEFT:
521 align_mode = PANGO_ALIGN_LEFT;
522 break;
523 case JUSTIFY_RIGHT:
524 align_mode = PANGO_ALIGN_RIGHT;
525 break;
526 case JUSTIFY_CENTER:
527 align_mode = PANGO_ALIGN_CENTER;
528 break;
529 case JUSTIFY_FULL:
530 default:
531 align_mode = PANGO_ALIGN_LEFT;
532 }
533 return align_mode;
534 }
535
536 static const gchar *
gst_cea708dec_get_color_name(guint8 color)537 gst_cea708dec_get_color_name (guint8 color)
538 {
539 guint index = 0;
540
541 switch (color) {
542 case CEA708_COLOR_BLACK:
543 index = COLOR_TYPE_BLACK;
544 break;
545 case CEA708_COLOR_WHITE:
546 index = COLOR_TYPE_WHITE;
547 break;
548 case CEA708_COLOR_RED:
549 index = COLOR_TYPE_RED;
550 break;
551 case CEA708_COLOR_GREEN:
552 index = COLOR_TYPE_GREEN;
553 break;
554 case CEA708_COLOR_BLUE:
555 index = COLOR_TYPE_BLUE;
556 break;
557 case CEA708_COLOR_YELLOW:
558 index = COLOR_TYPE_YELLOW;
559 break;
560 case CEA708_COLOR_MAGENTA:
561 index = COLOR_TYPE_MAGENTA;
562 break;
563 case CEA708_COLOR_CYAN:
564 index = COLOR_TYPE_CYAN;
565 break;
566 default:
567 break;
568 }
569
570 return color_names[index];
571 }
572
573 static guint8
gst_cea708dec_map_minimum_color(guint8 color)574 gst_cea708dec_map_minimum_color (guint8 color)
575 {
576 /*According to spec minimum color list define */
577 /*check R */
578 switch ((color & 0x30) >> 4) {
579 case 1:
580 color &= 0xF;
581 break;
582 case 3:
583 color &= 0x2F;
584 break;
585 default:
586 break;
587 }
588 /*check G */
589 switch ((color & 0xC) >> 2) {
590 case 1:
591 color &= 0x33;
592 break;
593 case 3:
594 color &= 0x3B;
595 break;
596 default:
597 break;
598 }
599 /*check B */
600 switch (color & 0x3) {
601 case 1:
602 color &= 0x3C;
603 break;
604 case 3:
605 color &= 0x3E;
606 break;
607 default:
608 break;
609 }
610
611 return color;
612 }
613
614 static void
gst_cea708dec_set_pen_color(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)615 gst_cea708dec_set_pen_color (Cea708Dec * decoder,
616 guint8 * dtvcc_buffer, int index)
617 {
618 cea708Window *window = decoder->cc_windows[decoder->current_window];
619
620 /* format
621 fo1 fo0 fr1 fr0 fg1 fg0 fb1 fb0
622 bo1 bo0 br1 br0 bg1 bg0 bb1 bb0
623 0 0 er1 er0 eg1 eg0 eb1 eb0 */
624 window->pen_color.fg_color =
625 gst_cea708dec_map_minimum_color (dtvcc_buffer[index] & 0x3F);
626 window->pen_color.fg_opacity = (dtvcc_buffer[index] & 0xC0) >> 6;
627 window->pen_color.bg_color =
628 gst_cea708dec_map_minimum_color (dtvcc_buffer[index + 1] & 0x3F);
629 window->pen_color.bg_opacity = (dtvcc_buffer[index + 1] & 0xC0) >> 6;
630 window->pen_color.edge_color =
631 gst_cea708dec_map_minimum_color (dtvcc_buffer[index + 2] & 0x3F);
632 GST_LOG ("pen_color fg=0x%x fg_op=0x%x bg=0x%x bg_op=0x%x edge=0x%x",
633 window->pen_color.fg_color, window->pen_color.fg_opacity,
634 window->pen_color.bg_color, window->pen_color.bg_opacity,
635 window->pen_color.edge_color);
636 }
637
638 static void
gst_cea708dec_set_window_attributes(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)639 gst_cea708dec_set_window_attributes (Cea708Dec * decoder,
640 guint8 * dtvcc_buffer, int index)
641 {
642 cea708Window *window = decoder->cc_windows[decoder->current_window];
643
644 /* format
645 fo1 fo0 fr1 fr0 fg1 fg0 fb1 fb0
646 bt1 bt0 br1 br0 bg1 bg0 bb1 bb0
647 bt2 ww pd1 pd0 sd1 sd0 j1 j0
648 es3 es2 es1 es0 ed1 ed0 de1 de0 */
649 window->fill_color =
650 gst_cea708dec_map_minimum_color (dtvcc_buffer[index] & 0x3F);
651 window->fill_opacity = (dtvcc_buffer[index] & 0xC0) >> 6;
652 window->border_color =
653 gst_cea708dec_map_minimum_color (dtvcc_buffer[index + 1] & 0x3F);
654 window->border_type =
655 ((dtvcc_buffer[index + 1] & 0xC0) >> 6) | ((dtvcc_buffer[index +
656 2] & 0x80) >> 5);
657 window->word_wrap = (dtvcc_buffer[index + 2] & 0x40) ? TRUE : FALSE;
658 window->justify_mode = dtvcc_buffer[index + 2] & 0x3;
659 window->scroll_direction = (dtvcc_buffer[index + 2] & 0xC) >> 2;
660 window->print_direction = (dtvcc_buffer[index + 2] & 0x30) >> 2;
661 window->display_effect = (dtvcc_buffer[index + 3] & 0x3);
662 window->effect_direction = (dtvcc_buffer[index + 3] & 0xC);
663 window->effect_speed = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
664
665 GST_LOG ("Print direction = %d", window->print_direction);
666 }
667
668 static void
gst_cea708dec_set_pen_style(Cea708Dec * decoder,guint8 pen_style_id)669 gst_cea708dec_set_pen_style (Cea708Dec * decoder, guint8 pen_style_id)
670 {
671 cea708Window *window = decoder->cc_windows[decoder->current_window];
672
673 window->pen_attributes.pen_size = PEN_SIZE_STANDARD;
674 window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
675 window->pen_attributes.offset = PEN_OFFSET_NORMAL;
676 window->pen_attributes.italics = FALSE;
677 window->pen_attributes.underline = FALSE;
678 window->pen_attributes.edge_type = EDGE_TYPE_NONE;
679 window->pen_color.fg_color = CEA708_COLOR_WHITE;
680 window->pen_color.fg_opacity = SOLID;
681 window->pen_color.bg_color = CEA708_COLOR_BLACK;
682 window->pen_color.bg_opacity = SOLID;
683 window->pen_color.edge_color = CEA708_COLOR_BLACK;
684
685 /* CEA-708 predefined pen style ids */
686 switch (pen_style_id) {
687 default:
688 case PEN_STYLE_DEFAULT:
689 window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
690 break;
691
692 case PEN_STYLE_MONO_SERIF:
693 window->pen_attributes.font_style = FONT_STYLE_MONO_SERIF;
694 break;
695
696 case PEN_STYLE_PROP_SERIF:
697 window->pen_attributes.font_style = FONT_STYLE_PROP_SERIF;
698 break;
699
700 case PEN_STYLE_MONO_SANS:
701 window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
702 break;
703
704 case PEN_STYLE_PROP_SANS:
705 window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
706 break;
707
708 case PEN_STYLE_MONO_SANS_TRANSPARENT:
709 window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
710 window->pen_color.bg_opacity = TRANSPARENT;
711 break;
712
713 case PEN_STYLE_PROP_SANS_TRANSPARENT:
714 window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
715 window->pen_color.bg_opacity = TRANSPARENT;
716 break;
717 }
718 }
719
720 static void
gst_cea708dec_set_window_style(Cea708Dec * decoder,guint8 style_id)721 gst_cea708dec_set_window_style (Cea708Dec * decoder, guint8 style_id)
722 {
723 cea708Window *window = decoder->cc_windows[decoder->current_window];
724
725 /* set the 'normal' styles first, then deviate in special cases below... */
726 window->justify_mode = JUSTIFY_LEFT;
727 window->print_direction = PRINT_DIR_LEFT_TO_RIGHT;
728 window->scroll_direction = SCROLL_DIR_BOTTOM_TO_TOP;
729 window->word_wrap = FALSE;
730 window->effect_direction = EFFECT_DIR_LEFT_TO_RIGHT;
731 window->display_effect = DISPLAY_EFFECT_SNAP;
732 window->effect_speed = 0;
733 window->fill_color = CEA708_COLOR_BLACK;
734 window->fill_opacity = SOLID;
735
736 /* CEA-708 predefined window style ids */
737 switch (style_id) {
738 default:
739 case WIN_STYLE_NORMAL:
740 break;
741
742 case WIN_STYLE_TRANSPARENT:
743 window->fill_opacity = TRANSPARENT;
744 break;
745
746 case WIN_STYLE_NORMAL_CENTERED:
747 window->justify_mode = JUSTIFY_CENTER;
748 break;
749
750 case WIN_STYLE_NORMAL_WORD_WRAP:
751 window->word_wrap = TRUE;
752 break;
753
754 case WIN_STYLE_TRANSPARENT_WORD_WRAP:
755 window->fill_opacity = TRANSPARENT;
756 window->word_wrap = TRUE;
757 break;
758
759 case WIN_STYLE_TRANSPARENT_CENTERED:
760 window->fill_opacity = TRANSPARENT;
761 window->justify_mode = JUSTIFY_CENTER;
762 break;
763
764 case WIN_STYLE_ROTATED:
765 window->print_direction = PRINT_DIR_TOP_TO_BOTTOM;
766 window->scroll_direction = SCROLL_DIR_RIGHT_TO_LEFT;
767 break;
768 }
769 }
770
771 /* Define window - window size, window style, pen style, anchor position, etc */
772 static void
gst_cea708dec_define_window(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)773 gst_cea708dec_define_window (Cea708Dec * decoder,
774 guint8 * dtvcc_buffer, int index)
775 {
776 cea708Window *window = decoder->cc_windows[decoder->current_window];
777 guint8 priority = 0;
778 guint8 anchor_point = 0;
779 guint8 relative_position = 0;
780 guint8 anchor_vertical = 0;
781 guint8 anchor_horizontal = 0;
782 guint8 row_count = 0;
783 guint8 column_count = 0;
784 guint8 row_lock = 0;
785 guint8 column_lock = 0;
786 gboolean visible = FALSE;
787 guint8 style_id = 0;
788 guint8 pen_style_id = 0;
789 #ifndef GST_DISABLE_GST_DEBUG
790 guint v_anchor = 0;
791 guint h_anchor = 0;
792 #endif
793
794 GST_LOG ("current_window=%d", decoder->current_window);
795 GST_LOG ("dtvcc_buffer %02x %02x %02x %02x %02x %02x",
796 dtvcc_buffer[index + 0], dtvcc_buffer[index + 1],
797 dtvcc_buffer[index + 2], dtvcc_buffer[index + 3],
798 dtvcc_buffer[index + 4], dtvcc_buffer[index + 5]);
799
800 /* Initialize window structure */
801 if (NULL != window) {
802 if (window->deleted) {
803 /* Spec says on window create (but not re-definition) the pen position
804 * must be reset to 0
805 * TODO: also set all text positions to the fill color */
806 window->deleted = FALSE;
807 window->pen_row = 0;
808 window->pen_col = 0;
809 }
810 /* format of parameters:
811 0 0 v rl cl p2 p1 p0
812 rp av7 av6 av5 av4 av3 av1 av0
813 ah7 ah6 ah5 ah4 ah3 ah2 ah1 ah0
814 ap3 ap2 ap1 ap0 rc3 rc2 rc1 rc0
815 0 0 cc5 cc4 cc3 cc2 cc1 cc0
816 0 0 ws2 ws1 ws0 ps2 ps1 ps0 */
817
818 /* parameter byte 0 */
819 priority = dtvcc_buffer[index] & 0x7;
820 column_lock = (dtvcc_buffer[index] & 0x8) ? TRUE : FALSE;
821 row_lock = (dtvcc_buffer[index] & 0x10) ? TRUE : FALSE;
822 visible = (dtvcc_buffer[index] & 0x20) ? TRUE : FALSE;
823
824 /* parameter byte 1 */
825 relative_position = (dtvcc_buffer[index + 1] & 0x80) ? TRUE : FALSE;
826 anchor_vertical = dtvcc_buffer[index + 1] & 0x7F;
827
828 /* parameter byte 2 */
829 anchor_horizontal = dtvcc_buffer[index + 2];
830
831 /* parameter byte 3 */
832 anchor_point = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
833 row_count = (dtvcc_buffer[index + 3] & 0xF) + 1;
834
835 /* parameter byte 4 */
836 column_count = (dtvcc_buffer[index + 4] & 0x3F) + 1;
837
838 /* parameter byte 5 */
839 style_id = (dtvcc_buffer[index + 5] & 0x38) >> 3;
840 pen_style_id = dtvcc_buffer[index + 5] & 0x7;
841
842 window->screen_vertical = anchor_vertical;
843 window->screen_horizontal = anchor_horizontal;
844
845 if (relative_position == FALSE) {
846 /* If position is in absolute coords, convert to percent */
847 if (decoder->width == 0 || decoder->height == 0) {
848 window->screen_vertical /= 100;
849 window->screen_horizontal /= 100;
850 } else if ((decoder->width * 9) % (decoder->height * 16) == 0) {
851 window->screen_vertical /= SCREEN_HEIGHT_16_9;
852 window->screen_horizontal /= SCREEN_WIDTH_16_9;
853 } else if ((decoder->width * 3) % (decoder->height * 4) == 0) {
854 window->screen_vertical /= SCREEN_HEIGHT_4_3;
855 window->screen_horizontal /= SCREEN_WIDTH_4_3;
856 } else {
857 window->screen_vertical /= 100;
858 window->screen_horizontal /= 100;
859 }
860 window->screen_vertical *= 100;
861 window->screen_horizontal *= 100;
862 }
863
864 window->priority = priority;
865 window->anchor_point = anchor_point;
866 window->relative_position = relative_position;
867 window->anchor_vertical = anchor_vertical;
868 window->anchor_horizontal = anchor_horizontal;
869 window->row_count = row_count;
870 window->column_count = column_count;
871 window->row_lock = row_lock;
872 window->column_lock = column_lock;
873 window->visible = visible;
874
875 /* Make sure row/col limits are not too large */
876 if (window->row_count > WINDOW_MAX_ROWS) {
877 GST_WARNING ("window row count %d is too large", window->row_count);
878 window->row_count = WINDOW_MAX_ROWS;
879 }
880
881 if (window->column_count > WINDOW_MAX_COLS) {
882 GST_WARNING ("window column count %d is too large", window->column_count);
883 window->column_count = WINDOW_MAX_COLS;
884 }
885
886 if (style_id != 0) {
887 window->style_id = style_id;
888 }
889
890 if (pen_style_id != 0) {
891 window->pen_style_id = pen_style_id;
892 }
893
894 gst_cea708dec_set_window_style (decoder, window->style_id);
895 gst_cea708dec_set_pen_style (decoder, window->pen_style_id);
896 }
897
898 GST_LOG ("priority=%d anchor=%d relative_pos=%d anchor_v=%d anchor_h=%d",
899 window->priority,
900 window->anchor_point,
901 window->relative_position,
902 window->anchor_vertical, window->anchor_horizontal);
903
904 GST_LOG ("row_count=%d col_count=%d row_lock=%d col_lock=%d visible=%d",
905 window->row_count,
906 window->column_count,
907 window->row_lock, window->column_lock, window->visible);
908
909 GST_LOG ("style_id=%d pen_style_id=%d screenH=%f screenV=%f v_offset=%d "
910 "h_offset=%d v_anchor=%d h_anchor=%d",
911 window->style_id,
912 window->pen_style_id,
913 window->screen_horizontal,
914 window->screen_vertical,
915 window->v_offset, window->h_offset, v_anchor, h_anchor);
916 }
917
918 static inline void
pango_span_markup_init(cea708PangoSpanControl * span_control)919 pango_span_markup_init (cea708PangoSpanControl * span_control)
920 {
921 memset (span_control, 0, sizeof (cea708PangoSpanControl));
922 span_control->size = PEN_SIZE_STANDARD;
923 span_control->fg_color = CEA708_COLOR_WHITE;
924 span_control->bg_color = CEA708_COLOR_INVALID;
925 span_control->size = PEN_SIZE_STANDARD;
926 span_control->font_style = FONT_STYLE_DEFAULT;
927 }
928
929 static inline void
pango_span_markup_start(cea708PangoSpanControl * span_control,gchar * line_buffer,guint16 * index)930 pango_span_markup_start (cea708PangoSpanControl * span_control,
931 gchar * line_buffer, guint16 * index)
932 {
933 GST_LOG ("span_control start_flag:%d end_flag:%d txt_flag:%d",
934 span_control->span_start_flag, span_control->span_end_flag,
935 span_control->span_txt_flag);
936 if (!span_control->span_start_flag) {
937 g_strlcat (line_buffer, CEA708_PANGO_SPAN_MARKUP_START, LINEBUFFER_SIZE);
938 *index += strlen (CEA708_PANGO_SPAN_MARKUP_START);
939 span_control->span_start_flag = TRUE;
940 span_control->span_end_flag = FALSE;
941 } else {
942 GST_WARNING ("warning span start !!!");
943 }
944 }
945
946 static inline void
pango_span_markup_txt(cea708PangoSpanControl * span_control,gchar * line_buffer,guint16 * index)947 pango_span_markup_txt (cea708PangoSpanControl * span_control,
948 gchar * line_buffer, guint16 * index)
949 {
950 GST_LOG ("span_control start_flag:%d end_flag:%d txt_flag:%d",
951 span_control->span_start_flag, span_control->span_end_flag,
952 span_control->span_txt_flag);
953 if (span_control->span_start_flag && !span_control->span_txt_flag) {
954 line_buffer[*index] = '>';
955 *index = *index + 1;
956 span_control->span_txt_flag = TRUE;
957 } else {
958 GST_WARNING ("warning span txt !!!");
959 }
960 }
961
962 static inline void
pango_span_markup_end(cea708PangoSpanControl * span_control,gchar * line_buffer,guint16 * index)963 pango_span_markup_end (cea708PangoSpanControl * span_control,
964 gchar * line_buffer, guint16 * index)
965 {
966 GST_LOG ("span_control start_flag:%d end_flag:%d txt_flag:%d",
967 span_control->span_start_flag, span_control->span_end_flag,
968 span_control->span_txt_flag);
969 if (span_control->span_start_flag && !span_control->span_end_flag) {
970 g_strlcat (line_buffer, CEA708_PANGO_SPAN_MARKUP_END, LINEBUFFER_SIZE);
971 *index = *index + strlen (CEA708_PANGO_SPAN_MARKUP_END);
972 span_control->span_start_flag = FALSE;
973 span_control->span_txt_flag = FALSE;
974 span_control->span_end_flag = TRUE;
975 } else {
976 GST_WARNING ("line_buffer=%s", line_buffer);
977 GST_WARNING ("warning span end !!!");
978 }
979 }
980
981 /* FIXME: Convert to GString ! */
982 static void
gst_cea708dec_show_pango_window(Cea708Dec * decoder,guint window_id)983 gst_cea708dec_show_pango_window (Cea708Dec * decoder, guint window_id)
984 {
985 cea708Window *window = decoder->cc_windows[window_id];
986 guint16 row, col;
987 gboolean display = FALSE; /* = TRUE when text lines should be written */
988 gchar line_buffer[LINEBUFFER_SIZE];
989 gchar outchar_utf8[CC_UTF8_MAX_LENGTH + 1] = { 0 };
990 guint8 utf8_char_length;
991 guint16 i, j;
992 guint16 right_index; /* within a single line of window text, the
993 * index of the rightmost non-blank character */
994 guint16 index;
995 guint len = 0;
996
997 cea708PangoSpanControl span_control;
998 const gchar *fg_color = NULL;
999 const gchar *bg_color = NULL;
1000 const gchar *pen_size = NULL;
1001 const gchar *font = NULL;
1002
1003 GST_DEBUG ("window #%02d (visible:%d)", window_id, window->visible);
1004
1005 window->updated = TRUE;
1006
1007 if (!window->visible) {
1008 GST_DEBUG ("Window is not visible, skipping rendering");
1009 return;
1010 }
1011
1012 for (row = 0; row < window->row_count; row++) {
1013 for (col = 0; col < window->column_count; col++) {
1014 GST_LOG ("window->text[%d][%d].c '%c'", row, col,
1015 window->text[row][col].c);
1016 if (window->text[row][col].c != ' ') {
1017 /* If there is a non-blank line, then display from there */
1018 display = TRUE;
1019 }
1020 }
1021 }
1022
1023 if (!display) {
1024 GST_DEBUG ("No visible text, skiping rendering");
1025 return;
1026 }
1027
1028 for (row = 0; row < window->row_count; row++) {
1029 for (col = 0; col < window->column_count; col++) {
1030 if (window->text[row][col].c != ' ') {
1031
1032 memset (line_buffer, '\0', LINEBUFFER_SIZE);
1033 pango_span_markup_init (&span_control);
1034 /* Find the rightmost non-blank character on this line: */
1035 for (i = right_index = WINDOW_MAX_COLS - 1; i >= col; i--) {
1036 if (window->text[row][i].c != ' ') {
1037 right_index = i;
1038 break;
1039 }
1040 }
1041
1042 /* Copy all of the characters in this row, from the current position
1043 * to the right edge */
1044 for (i = 0, index = 0;
1045 (i <= right_index) && (index < LINEBUFFER_SIZE - 15); i++) {
1046 cea708char *current = &window->text[row][i];
1047 GST_LOG ("Adding row=%d i=%d c=%c %d", row,
1048 i, current->c, current->c);
1049
1050 do {
1051 GST_MEMDUMP ("line_buffer", (guint8 *) line_buffer, index);
1052 GST_INFO
1053 ("text[%d][%d] '%c' underline:%d , italics:%d , font_style:%d , pen_size : %d",
1054 row, i, current->c,
1055 current->pen_attributes.underline,
1056 current->pen_attributes.italics,
1057 current->pen_attributes.font_style,
1058 current->pen_attributes.pen_size);
1059 GST_INFO ("text[%d][%d] '%c' pen_color fg:0x%02X bg:0x%02x", row, i,
1060 current->c, current->pen_color.fg_color,
1061 current->pen_color.bg_color);
1062 GST_INFO
1063 ("span_control: span_next_flag = %d, underline = %d, italics = %d, font_style = %d, size = %d, fg_color = 0x%02X, bg_color = 0x%02X",
1064 span_control.span_next_flag, span_control.underline,
1065 span_control.italics, span_control.font_style,
1066 span_control.size, span_control.fg_color,
1067 span_control.bg_color);
1068
1069 if ((current->pen_attributes.underline != span_control.underline)
1070 || (current->pen_attributes.italics != span_control.italics)
1071 || (current->pen_attributes.font_style !=
1072 span_control.font_style)
1073 || (current->pen_attributes.pen_size != span_control.size)
1074 || (current->pen_color.fg_color != span_control.fg_color)
1075 || (current->pen_color.bg_color != span_control.bg_color)
1076 ) {
1077 GST_LOG ("Markup changed");
1078
1079 /* check end span to next span start */
1080 if (!span_control.span_next_flag) {
1081 pango_span_markup_end (&span_control, line_buffer, &index);
1082 if (span_control.span_end_flag) {
1083 pango_span_markup_init (&span_control);
1084 span_control.span_next_flag = TRUE;
1085 GST_INFO ("continue check next span !!!");
1086 continue;
1087 }
1088 }
1089
1090 pango_span_markup_start (&span_control, line_buffer, &index);
1091
1092 /* Check for transitions to/from underline: */
1093 if (current->pen_attributes.underline) {
1094 g_strlcat (line_buffer,
1095 CEA708_PANGO_SPAN_ATTRIBUTES_UNDERLINE_SINGLE,
1096 sizeof (line_buffer));
1097 index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_UNDERLINE_SINGLE);
1098 span_control.underline = TRUE;
1099 }
1100
1101 /* Check for transitions to/from italics: */
1102 if (current->pen_attributes.italics) {
1103 g_strlcat (line_buffer,
1104 CEA708_PANGO_SPAN_ATTRIBUTES_STYLE_ITALIC,
1105 sizeof (line_buffer));
1106 index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_STYLE_ITALIC);
1107 span_control.italics = TRUE;
1108 }
1109
1110 /* FIXME : Something is totally wrong with the way fonts
1111 * are being handled. Shouldn't the font description (if
1112 * specified by the user) be written for everything ? */
1113 if (!decoder->default_font_desc) {
1114 font = font_names[current->pen_attributes.font_style];
1115
1116 if (font) {
1117 g_strlcat (line_buffer, CEA708_PANGO_SPAN_ATTRIBUTES_FONT,
1118 sizeof (line_buffer));
1119 index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_FONT);
1120 line_buffer[index++] = 0x27; /* ' */
1121 g_strlcat (line_buffer, font, sizeof (line_buffer));
1122 index += strlen (font);
1123 span_control.font_style = current->pen_attributes.font_style;
1124
1125 /* Check for transitions to/from pen size */
1126 pen_size = pen_size_names[current->pen_attributes.pen_size];
1127
1128 line_buffer[index++] = ' ';
1129 g_strlcat (line_buffer, pen_size, sizeof (line_buffer));
1130 index += strlen (pen_size);
1131 line_buffer[index++] = 0x27; /* ' */
1132 span_control.size = current->pen_attributes.pen_size;
1133 }
1134 }
1135 /* Regardless of the above, we want to remember the latest changes */
1136 span_control.font_style = current->pen_attributes.font_style;
1137 span_control.size = current->pen_attributes.pen_size;
1138 ;
1139 /* Check for transitions to/from foreground color */
1140 fg_color =
1141 gst_cea708dec_get_color_name (current->pen_color.fg_color);
1142 if (fg_color && current->pen_color.bg_opacity != TRANSPARENT) {
1143 g_strlcat (line_buffer, CEA708_PANGO_SPAN_ATTRIBUTES_FOREGROUND,
1144 sizeof (line_buffer));
1145 index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_FOREGROUND);
1146 line_buffer[index++] = 0x27; /* ' */
1147 g_strlcat (line_buffer, fg_color, sizeof (line_buffer));
1148 index += strlen (fg_color);
1149 line_buffer[index++] = 0x27; /* ' */
1150 span_control.fg_color = current->pen_color.fg_color;
1151 GST_DEBUG ("span_control.fg_color updated to 0x%02x",
1152 span_control.fg_color);
1153 } else
1154 GST_DEBUG
1155 ("span_control.fg_color was NOT updated (still 0x%02x)",
1156 span_control.fg_color);
1157
1158 /* Check for transitions to/from background color */
1159 bg_color =
1160 gst_cea708dec_get_color_name (current->pen_color.bg_color);
1161 if (bg_color && current->pen_color.bg_opacity != TRANSPARENT) {
1162 g_strlcat (line_buffer, CEA708_PANGO_SPAN_ATTRIBUTES_BACKGROUND,
1163 sizeof (line_buffer));
1164 index += strlen (CEA708_PANGO_SPAN_ATTRIBUTES_BACKGROUND);
1165 line_buffer[index++] = 0x27; /* ' */
1166 g_strlcat (line_buffer, bg_color, sizeof (line_buffer));
1167 index += strlen (bg_color);
1168 line_buffer[index++] = 0x27; /* ' */
1169 span_control.bg_color = current->pen_color.bg_color;
1170 GST_DEBUG ("span_control.bg_color updated to 0x%02x",
1171 span_control.bg_color);
1172 } else
1173 GST_DEBUG
1174 ("span_control.bg_color was NOT updated (still 0x%02x)",
1175 span_control.bg_color);
1176
1177
1178 /*span text start */
1179 pango_span_markup_txt (&span_control, line_buffer, &index);
1180 GST_INFO ("span_next_flag = %d", span_control.span_next_flag);
1181 }
1182 span_control.span_next_flag = FALSE;
1183 } while (span_control.span_next_flag);
1184
1185
1186 /* Finally write the character */
1187
1188 j = 0;
1189
1190
1191 switch (current->c) {
1192 case '&':
1193 g_snprintf (&(line_buffer[index]),
1194 sizeof (line_buffer) - index - 1, "&");
1195 index += 5;
1196 break;
1197
1198 case '<':
1199 g_snprintf (&(line_buffer[index]),
1200 sizeof (line_buffer) - index - 1, "<");
1201 index += 4;
1202 break;
1203
1204 case '>':
1205 g_snprintf (&(line_buffer[index]),
1206 sizeof (line_buffer) - index - 1, ">");
1207 index += 4;
1208 break;
1209
1210 case '\'':
1211 g_snprintf (&(line_buffer[index]),
1212 sizeof (line_buffer) - index - 1, "'");
1213 index += 6;
1214 break;
1215
1216 case '"':
1217 g_snprintf (&(line_buffer[index]),
1218 sizeof (line_buffer) - index - 1, """);
1219 index += 6;
1220 break;
1221
1222 default:
1223 /* FIXME : Use g_string_append_unichar() when switched to GString */
1224 utf8_char_length = g_unichar_to_utf8 (current->c, outchar_utf8);
1225 while (utf8_char_length > 0) {
1226 line_buffer[index++] = outchar_utf8[j++];
1227 utf8_char_length--;
1228 }
1229 }
1230
1231 }
1232
1233 /* pango markup span mode ends */
1234 if (span_control.underline || span_control.italics
1235 || (span_control.font_style != FONT_STYLE_DEFAULT)
1236 || (span_control.size != PEN_SIZE_STANDARD)
1237 || (span_control.fg_color != CEA708_COLOR_WHITE)
1238 || (span_control.bg_color != CEA708_COLOR_INVALID)
1239 ) {
1240 pango_span_markup_end (&span_control, line_buffer, &index);
1241 pango_span_markup_init (&span_control);
1242 }
1243
1244 GST_LOG ("adding row[%d]: %s\nlength:%d", row, line_buffer, index);
1245
1246 if (row != window->row_count - 1) {
1247 line_buffer[index++] = '\n';
1248 }
1249
1250 len +=
1251 gst_cea708dec_text_list_add (&decoder->text_list, index + 1, "%s",
1252 line_buffer);
1253 break;
1254 }
1255 }
1256
1257 if (col == window->column_count && row != window->row_count - 1) {
1258 len +=
1259 gst_cea708dec_text_list_add (&decoder->text_list, strlen ("\n") + 1,
1260 "\n");
1261 }
1262 }
1263
1264 if (len == 0) {
1265 GST_LOG ("window %d had no text", window_id);
1266 } else {
1267 /* send text to output pad */
1268 gst_cea708dec_render_text (decoder, &decoder->text_list, len, window_id);
1269 }
1270 }
1271
1272 static void
gst_cea708dec_clear_window_text(Cea708Dec * decoder,guint window_id)1273 gst_cea708dec_clear_window_text (Cea708Dec * decoder, guint window_id)
1274 {
1275 cea708Window *window = decoder->cc_windows[window_id];
1276 guint row, col;
1277
1278 for (row = 0; row < WINDOW_MAX_ROWS; row++) {
1279 for (col = 0; col < WINDOW_MAX_COLS; col++) {
1280 window->text[row][col].c = ' ';
1281 window->text[row][col].justify_mode = window->justify_mode;
1282 window->text[row][col].pen_attributes = window->pen_attributes;
1283 window->text[row][col].pen_color = window->pen_color;
1284 }
1285 }
1286 }
1287
1288 static void
gst_cea708dec_scroll_window_up(Cea708Dec * decoder,guint window_id)1289 gst_cea708dec_scroll_window_up (Cea708Dec * decoder, guint window_id)
1290 {
1291 cea708Window *window = decoder->cc_windows[window_id];
1292 guint row, col;
1293
1294 /* This function should be called to scroll the window up if bottom to
1295 * top scrolling is enabled and a carraige-return is encountered, or
1296 * word-wrapping */
1297 GST_LOG_OBJECT (decoder, "called for window: %d", window_id);
1298
1299 /* start at row 1 to copy into row 0 (scrolling up) */
1300 for (row = 1; row < WINDOW_MAX_ROWS; row++) {
1301 for (col = 0; col < WINDOW_MAX_COLS; col++) {
1302 window->text[row - 1][col] = window->text[row][col];
1303 }
1304 }
1305
1306 /* Clear the bottom row: */
1307 row = WINDOW_MAX_ROWS - 1;
1308 for (col = 0; col < WINDOW_MAX_COLS; col++) {
1309 window->text[row][col].c = ' ';
1310 window->text[row][col].justify_mode = window->justify_mode;
1311 window->text[row][col].pen_attributes = window->pen_attributes;
1312 window->text[row][col].pen_color = window->pen_color;
1313 }
1314 }
1315
1316 static void
gst_cea708dec_clear_window(Cea708Dec * decoder,cea708Window * window)1317 gst_cea708dec_clear_window (Cea708Dec * decoder, cea708Window * window)
1318 {
1319 g_free (window->text_image);
1320 memset (window, 0, sizeof (cea708Window));
1321 }
1322
1323 static void
gst_cea708dec_init_window(Cea708Dec * decoder,guint window_id)1324 gst_cea708dec_init_window (Cea708Dec * decoder, guint window_id)
1325 {
1326 cea708Window *window = decoder->cc_windows[window_id];
1327
1328 if (window_id >= MAX_708_WINDOWS) {
1329 GST_ERROR ("window_id outside of range %d", window_id);
1330 return;
1331 }
1332
1333 window->priority = 0;
1334 window->anchor_point = 0;
1335 window->relative_position = 0;
1336 window->anchor_vertical = 0;
1337 window->anchor_horizontal = 0;
1338 window->screen_vertical = 0;
1339 window->screen_horizontal = 0;
1340
1341 window->row_count = WINDOW_MAX_ROWS;
1342 window->column_count = WINDOW_MAX_COLS;
1343 window->row_lock = 0;
1344 window->column_lock = 0;
1345 window->visible = FALSE;
1346 window->style_id = 0;
1347 window->pen_style_id = 0;
1348 window->deleted = TRUE;
1349 window->pen_color.fg_color = CEA708_COLOR_WHITE;
1350 window->pen_color.fg_opacity = SOLID;
1351 window->pen_color.bg_color = CEA708_COLOR_BLACK;
1352 window->pen_color.bg_opacity = SOLID;
1353 window->pen_color.edge_color = CEA708_COLOR_BLACK;
1354
1355 window->pen_attributes.pen_size = PEN_SIZE_STANDARD;
1356 window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
1357 window->pen_attributes.offset = PEN_OFFSET_NORMAL;
1358 window->pen_attributes.italics = FALSE;
1359 window->pen_attributes.text_tag = TAG_DIALOG;
1360 window->pen_attributes.underline = FALSE;
1361 window->pen_attributes.edge_type = EDGE_TYPE_NONE;
1362
1363 /* Init pen position */
1364 window->pen_row = 0;
1365 window->pen_col = 0;
1366
1367 /* Initialize text array to all spaces. When sending window text, only
1368 * send if there are non-blank rows */
1369 gst_cea708dec_clear_window_text (decoder, window_id);
1370
1371 /* window attributes */
1372 window->justify_mode = JUSTIFY_LEFT;
1373 window->print_direction = PRINT_DIR_LEFT_TO_RIGHT;
1374 window->scroll_direction = SCROLL_DIR_BOTTOM_TO_TOP;
1375 window->word_wrap = FALSE;
1376 window->display_effect = DISPLAY_EFFECT_SNAP;
1377 window->effect_direction = EFFECT_DIR_LEFT_TO_RIGHT;
1378 window->effect_speed = 0;
1379 window->fill_color = CEA708_COLOR_BLACK;
1380 window->fill_opacity = TRANSPARENT;
1381 window->border_type = BORDER_TYPE_NONE;
1382 window->border_color = CEA708_COLOR_BLACK;
1383
1384 window->v_offset = 0;
1385 window->h_offset = 0;
1386 window->layout = NULL;
1387 window->shadow_offset = 0;
1388 window->outline_offset = 0;
1389 window->image_width = 0;
1390 window->image_height = 0;
1391 window->text_image = NULL;
1392
1393 }
1394
1395 static void
gst_cea708dec_set_pen_attributes(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)1396 gst_cea708dec_set_pen_attributes (Cea708Dec * decoder,
1397 guint8 * dtvcc_buffer, int index)
1398 {
1399 cea708Window *window = decoder->cc_windows[decoder->current_window];
1400
1401 /* format
1402 tt3 tt2 tt1 tt0 o1 o0 s1 s0
1403 i u et2 et1 et0 fs2 fs1 fs0 */
1404 window->pen_attributes.pen_size = dtvcc_buffer[index] & 0x3;
1405 window->pen_attributes.text_tag = (dtvcc_buffer[index] & 0xF0) >> 4;
1406 window->pen_attributes.offset = (dtvcc_buffer[index] & 0xC0) >> 2;
1407 window->pen_attributes.font_style = dtvcc_buffer[index + 1] & 0x7;
1408 window->pen_attributes.italics =
1409 ((dtvcc_buffer[index + 1] & 0x80) >> 7) ? TRUE : FALSE;
1410 window->pen_attributes.underline =
1411 ((dtvcc_buffer[index + 1] & 0x40) >> 6) ? TRUE : FALSE;
1412 window->pen_attributes.edge_type = (dtvcc_buffer[index + 1] & 0x38) >> 3;
1413
1414 GST_LOG ("pen_size=%d font=%d text_tag=%d offset=%d",
1415 window->pen_attributes.pen_size,
1416 window->pen_attributes.font_style,
1417 window->pen_attributes.text_tag, window->pen_attributes.offset);
1418
1419 GST_LOG ("italics=%d underline=%d edge_type=%d",
1420 window->pen_attributes.italics,
1421 window->pen_attributes.underline, window->pen_attributes.edge_type);
1422 }
1423
1424 static void
gst_cea708dec_for_each_window(Cea708Dec * decoder,guint8 window_list,VisibilityControl visibility_control,const gchar * log_message,void (* function)(Cea708Dec * decoder,guint window_id))1425 gst_cea708dec_for_each_window (Cea708Dec * decoder,
1426 guint8 window_list, VisibilityControl visibility_control,
1427 const gchar * log_message, void (*function) (Cea708Dec * decoder,
1428 guint window_id))
1429 {
1430 guint i;
1431
1432 GST_LOG ("window_list: %02x", window_list);
1433
1434 for (i = 0; i < MAX_708_WINDOWS; i++) {
1435 if (WINDOW_IN_LIST_IS_ACTIVE (window_list)) {
1436 GST_LOG ("%s[%d]:%d %s v_offset=%d h_offset=%d",
1437 log_message, i, WINDOW_IN_LIST_IS_ACTIVE (window_list),
1438 (decoder->cc_windows[i]->visible) ? "visible" : "hidden",
1439 decoder->cc_windows[i]->v_offset, decoder->cc_windows[i]->h_offset);
1440 switch (visibility_control) {
1441 default:
1442 case NO_CHANGE:
1443 break;
1444
1445 case SWITCH_TO_HIDE:
1446 decoder->cc_windows[i]->visible = FALSE;
1447 break;
1448
1449 case SWITCH_TO_SHOW:
1450 decoder->cc_windows[i]->visible = TRUE;
1451 break;
1452
1453 case TOGGLE:
1454 decoder->cc_windows[i]->visible = !decoder->cc_windows[i]->visible;
1455 break;
1456 }
1457
1458 if (NULL != function) {
1459 function (decoder, i);
1460 }
1461 }
1462
1463 window_list >>= 1;
1464 }
1465 }
1466
1467 static void
gst_cea708dec_process_command(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)1468 gst_cea708dec_process_command (Cea708Dec * decoder,
1469 guint8 * dtvcc_buffer, int index)
1470 {
1471 cea708Window *window = decoder->cc_windows[decoder->current_window];
1472 guint8 c = dtvcc_buffer[index];
1473 guint8 window_list = dtvcc_buffer[index + 1]; /* always the first arg (if any) */
1474
1475 /* Process command codes */
1476 gst_cea708dec_print_command_name (decoder, c);
1477 switch (c) {
1478 case CC_COMMAND_ETX: /* End of text */
1479 window->visible = TRUE;
1480 gst_cea708dec_show_pango_window (decoder, decoder->current_window);
1481 return;
1482
1483 case CC_COMMAND_CW0: /* Set current window */
1484 case CC_COMMAND_CW1:
1485 case CC_COMMAND_CW2:
1486 case CC_COMMAND_CW3:
1487 case CC_COMMAND_CW4:
1488 case CC_COMMAND_CW5:
1489 case CC_COMMAND_CW6:
1490 case CC_COMMAND_CW7:
1491 decoder->current_window = c & 0x03;
1492 GST_LOG ("Current window=%d", decoder->current_window);
1493 return;
1494
1495 case CC_COMMAND_CLW: /* Clear windows */
1496 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1497
1498 /* Clear window data */
1499 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1500 "clear_window", gst_cea708dec_clear_window_text);
1501 return;
1502
1503 case CC_COMMAND_DSW: /* Display windows */
1504 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1505
1506 /* Show window */
1507 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1508 "display_window", gst_cea708dec_show_pango_window);
1509 return;
1510
1511 case CC_COMMAND_HDW: /* Hide windows */
1512 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1513
1514 /* Hide window */
1515 gst_cea708dec_for_each_window (decoder, window_list, SWITCH_TO_HIDE,
1516 "hide_window", NULL);
1517 return;
1518
1519 case CC_COMMAND_TGW: /* Toggle windows */
1520 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1521
1522 /* Toggle windows - hide displayed windows, display hidden windows */
1523 gst_cea708dec_for_each_window (decoder, window_list, TOGGLE,
1524 "toggle_window", gst_cea708dec_show_pango_window);
1525 return;
1526
1527 case CC_COMMAND_DLW: /* Delete windows */
1528 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1529
1530 /* Delete window */
1531 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1532 "delete_window", gst_cea708dec_init_window);
1533 return;
1534
1535 case CC_COMMAND_DLY: /* Delay */
1536 decoder->output_ignore = 1; /* 1 byte parameter = delay in 1/10 sec */
1537 /* TODO: - process this command. */
1538 return;
1539
1540 case CC_COMMAND_DLC: /* Delay cancel */
1541 /* TODO: - process this command. */
1542 return;
1543
1544 /* Reset */
1545 case CC_COMMAND_RST:
1546 /* Reset - cancel any delay, delete all windows */
1547 window_list = 0xFF; /* all windows... */
1548
1549 /* Delete window */
1550 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1551 "reset_window", gst_cea708dec_init_window);
1552 return;
1553
1554 case CC_COMMAND_SPA: /* Set pen attributes */
1555 decoder->output_ignore = 2; /* 2 byte parameter = pen attributes */
1556 gst_cea708dec_set_pen_attributes (decoder, dtvcc_buffer, index + 1);
1557 return;
1558
1559 case CC_COMMAND_SPC: /* Set pen color */
1560 decoder->output_ignore = 3; /* 3 byte parameter = color & opacity */
1561 gst_cea708dec_set_pen_color (decoder, dtvcc_buffer, index + 1);
1562 return;
1563
1564 case CC_COMMAND_SPL: /* Set pen location */
1565 /* Set pen location - row, column address within the current window */
1566 decoder->output_ignore = 2; /* 2 byte parameter = row, col */
1567 window->pen_row = dtvcc_buffer[index + 1] & 0xF;
1568 window->pen_col = dtvcc_buffer[index + 2] & 0x3F;
1569 GST_LOG ("Pen location: row=%d col=%d", window->pen_row, window->pen_col);
1570 return;
1571
1572 case CC_COMMAND_SWA: /* Set window attributes */
1573 /* Set window attributes - color, word wrap, border, scroll effect, etc */
1574 decoder->output_ignore = 4; /* 4 byte parameter = window attributes */
1575 gst_cea708dec_set_window_attributes (decoder, dtvcc_buffer, index + 1);
1576 return;
1577
1578 case CC_COMMAND_DF0: /* Define window */
1579 case CC_COMMAND_DF1:
1580 case CC_COMMAND_DF2:
1581 case CC_COMMAND_DF3:
1582 case CC_COMMAND_DF4:
1583 case CC_COMMAND_DF5:
1584 case CC_COMMAND_DF6:
1585 case CC_COMMAND_DF7:
1586 {
1587 window_list = 0xFF; /* all windows... */
1588
1589 /* set window - size, style, pen style, anchor position, etc. */
1590 decoder->output_ignore = 6; /* 6 byte parameter = window definition */
1591 decoder->current_window = c & 0x7;
1592 gst_cea708dec_define_window (decoder, dtvcc_buffer, index + 1);
1593 return;
1594 }
1595 }
1596 }
1597
1598 static void
get_cea708dec_bufcat(gpointer data,gpointer whole_buf)1599 get_cea708dec_bufcat (gpointer data, gpointer whole_buf)
1600 {
1601 gchar *buf = whole_buf;
1602 strcat ((gchar *) buf, data);
1603 g_free (data);
1604 }
1605
1606 static gboolean
gst_cea708dec_render_text(Cea708Dec * decoder,GSList ** text_list,gint length,guint window_id)1607 gst_cea708dec_render_text (Cea708Dec * decoder, GSList ** text_list,
1608 gint length, guint window_id)
1609 {
1610 gchar *out_str = NULL;
1611 PangoAlignment align_mode;
1612 PangoFontDescription *desc;
1613 gchar *font_desc;
1614 cea708Window *window = decoder->cc_windows[window_id];
1615
1616 if (length > 0) {
1617 out_str = g_malloc0 (length + 1);
1618 memset (out_str, 0, length + 1);
1619
1620 g_slist_foreach (*text_list, get_cea708dec_bufcat, out_str);
1621 GST_LOG ("rendering '%s'", out_str);
1622 g_slist_free (*text_list);
1623 window->layout = pango_layout_new (decoder->pango_context);
1624 align_mode = gst_cea708dec_get_align_mode (window->justify_mode);
1625 pango_layout_set_alignment (window->layout, (PangoAlignment) align_mode);
1626 pango_layout_set_markup (window->layout, out_str, length);
1627 if (!decoder->default_font_desc)
1628 font_desc = g_strdup_printf ("%s %s", font_names[0], pen_size_names[1]);
1629 else
1630 font_desc = g_strdup (decoder->default_font_desc);
1631 desc = pango_font_description_from_string (font_desc);
1632 if (desc) {
1633 GST_INFO ("font description set: %s", font_desc);
1634 pango_layout_set_font_description (window->layout, desc);
1635 gst_cea708dec_adjust_values_with_fontdesc (window, desc);
1636 pango_font_description_free (desc);
1637 gst_cea708dec_render_pangocairo (window);
1638 } else {
1639 GST_ERROR ("font description parse failed: %s", font_desc);
1640 }
1641 g_free (font_desc);
1642 g_free (out_str);
1643 /* data freed in slist loop!
1644 *g_slist_free_full (*text_list, g_free); */
1645 *text_list = NULL;
1646 return TRUE;
1647 }
1648
1649 return FALSE;
1650 }
1651
1652 static void
gst_cea708dec_window_add_char(Cea708Dec * decoder,gunichar c)1653 gst_cea708dec_window_add_char (Cea708Dec * decoder, gunichar c)
1654 {
1655 cea708Window *window = decoder->cc_windows[decoder->current_window];
1656 guint16 pen_row;
1657 guint16 pen_col;
1658
1659 /* Add one character to the current window, using current pen location.
1660 * Wrap pen location if necessary */
1661 if (c == 0) /* NULL */
1662 return;
1663
1664 if (c == 0x0E) { /* HCR,moves the pen location to the beginning of the current line and deletes its contents */
1665 for (pen_col = window->pen_col; pen_col >= 0; pen_col--) {
1666 window->text[window->pen_row][pen_col].c = ' ';
1667 }
1668 window->pen_col = 0;
1669 return;
1670 }
1671
1672 if (c == 0x08) { /* BS */
1673 switch (window->print_direction) {
1674 case PRINT_DIR_LEFT_TO_RIGHT:
1675 if (window->pen_col) {
1676 window->pen_col--;
1677 }
1678 break;
1679
1680 case PRINT_DIR_RIGHT_TO_LEFT:
1681 window->pen_col++;
1682 break;
1683
1684 case PRINT_DIR_TOP_TO_BOTTOM:
1685 if (window->pen_row) {
1686 window->pen_row--;
1687 }
1688 break;
1689
1690 case PRINT_DIR_BOTTOM_TO_TOP:
1691 window->pen_row++;
1692 break;
1693 }
1694 pen_row = window->pen_row;
1695 pen_col = window->pen_col;
1696 window->text[pen_row][pen_col].c = ' ';
1697 return;
1698 }
1699
1700 if (c == 0x0C) { /* FF clears the screen and moves the pen location to (0,0) */
1701 window->pen_row = 0;
1702 window->pen_col = 0;
1703 gst_cea708dec_clear_window_text (decoder, decoder->current_window);
1704 return;
1705 }
1706
1707 if (c == 0x0D) {
1708 GST_DEBUG
1709 ("carriage return, window->word_wrap=%d,window->scroll_direction=%d",
1710 window->word_wrap, window->scroll_direction);
1711 window->pen_col = 0;
1712 window->pen_row++;
1713 }
1714
1715 if (window->pen_col >= window->column_count) {
1716 window->pen_col = 0;
1717 window->pen_row++;
1718 }
1719 /* Wrap row position if too large */
1720 if (window->pen_row >= window->row_count) {
1721 if (window->scroll_direction == SCROLL_DIR_BOTTOM_TO_TOP) {
1722 gst_cea708dec_scroll_window_up (decoder, decoder->current_window);
1723 }
1724 window->pen_row = window->row_count - 1;
1725 GST_WARNING ("pen row exceed window row count,scroll up");
1726 }
1727
1728 if ((c != '\r') && (c != '\n')) {
1729 pen_row = window->pen_row;
1730 pen_col = window->pen_col;
1731
1732 GST_LOG ("[text x=%d y=%d fgcolor=%d win=%d vis=%d] '%c' 0x%02X", pen_col,
1733 pen_row, window->pen_color.fg_color, decoder->current_window,
1734 window->visible, c, c);
1735
1736 /* Each cell in the window should get the current pen color and
1737 * attributes as it is written */
1738 window->text[pen_row][pen_col].c = c;
1739 window->text[pen_row][pen_col].justify_mode = window->justify_mode;
1740 window->text[pen_row][pen_col].pen_color = window->pen_color;
1741 window->text[pen_row][pen_col].pen_attributes = window->pen_attributes;
1742
1743 switch (window->print_direction) {
1744 case PRINT_DIR_LEFT_TO_RIGHT:
1745 window->pen_col++;
1746 break;
1747
1748 case PRINT_DIR_RIGHT_TO_LEFT:
1749 if (window->pen_col) {
1750 window->pen_col--;
1751 }
1752 break;
1753
1754 case PRINT_DIR_TOP_TO_BOTTOM:
1755 window->pen_row++;
1756 break;
1757
1758 case PRINT_DIR_BOTTOM_TO_TOP:
1759 if (window->pen_row) {
1760 window->pen_row--;
1761 }
1762 break;
1763 } /* switch (print_direction) */
1764 }
1765 }
1766
1767 static void
gst_cea708dec_process_c2(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)1768 gst_cea708dec_process_c2 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
1769 {
1770 guint8 c = dtvcc_buffer[index];
1771 if (c >= 0x00 && c <= 0x07) {
1772 decoder->output_ignore = 1;
1773 } else if (c >= 0x08 && c <= 0x0F) {
1774 decoder->output_ignore = 2;
1775 } else if (c >= 0x10 && c <= 0x17) {
1776 decoder->output_ignore = 3;
1777 } else if (c >= 0x18 && c <= 0x1F) {
1778 decoder->output_ignore = 4;
1779 }
1780 }
1781
1782 static void
gst_cea708dec_process_g2(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)1783 gst_cea708dec_process_g2 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
1784 {
1785 guint8 c = dtvcc_buffer[index];
1786 gst_cea708dec_window_add_char (decoder, g2_table[c - 0x20]);
1787 decoder->output_ignore = 1;
1788 }
1789
1790 static void
gst_cea708dec_process_c3(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)1791 gst_cea708dec_process_c3 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
1792 {
1793 guint8 c = dtvcc_buffer[index];
1794 int command_length = 0;
1795 if (c >= 0x80 && c <= 0x87) {
1796 decoder->output_ignore = 5;
1797 } else if (c >= 0x88 && c <= 0x8F) {
1798 decoder->output_ignore = 6;
1799 } else if (c >= 0x90 && c <= 0x9F) {
1800 command_length = dtvcc_buffer[index + 1] & 0x3F;
1801 decoder->output_ignore = command_length + 2;
1802 }
1803 }
1804
1805 static void
gst_cea708dec_process_g3(Cea708Dec * decoder,guint8 * dtvcc_buffer,int index)1806 gst_cea708dec_process_g3 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
1807 {
1808 gst_cea708dec_window_add_char (decoder, 0x5F);
1809 decoder->output_ignore = 1;
1810 }
1811
1812 void
gst_cea708dec_set_video_width_height(Cea708Dec * decoder,gint width,gint height)1813 gst_cea708dec_set_video_width_height (Cea708Dec * decoder, gint width,
1814 gint height)
1815 {
1816 decoder->width = width;
1817 decoder->height = height;
1818 }
1819