2 * Copyright (C) 2013 CableLabs, Louisville, CO 80027
3 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
4 * @Author: Chengjun Wang <cjun.wang@samsung.com>
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.
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.
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.
22 #include <pango/pangocairo.h>
23 #include <gstcea708decoder.h>
26 #define GST_CAT_DEFAULT gst_cea708_decoder_debug
27 GST_DEBUG_CATEGORY (gst_cea708_decoder_debug);
30 gst_cea708_decoder_init_debug (void)
32 GST_DEBUG_CATEGORY_INIT (gst_cea708_decoder_debug, "cc708decoder", 0,
33 "CEA708 Closed Caption Decoder");
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[] = {
49 static const gchar *font_names[] = {
61 static const gchar *pen_size_names[] = {
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,
84 static void gst_cea708dec_print_command_name (Cea708Dec * decoder, guint8 c);
85 static void gst_cea708dec_render_pangocairo (cea708Window * window);
87 gst_cea708dec_adjust_values_with_fontdesc (cea708Window * window,
88 PangoFontDescription * desc);
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);
96 gst_cea708dec_set_pen_color (Cea708Dec * decoder,
97 guint8 * dtvcc_buffer, int index);
99 gst_cea708dec_set_window_attributes (Cea708Dec * decoder,
100 guint8 * dtvcc_buffer, int index);
102 gst_cea708dec_set_pen_style (Cea708Dec * decoder, guint8 pen_style_id);
104 gst_cea708dec_set_window_style (Cea708Dec * decoder, guint8 style_id);
106 gst_cea708dec_define_window (Cea708Dec * decoder,
107 guint8 * dtvcc_buffer, int index);
109 pango_span_markup_init (cea708PangoSpanControl * span_control);
111 pango_span_markup_start (cea708PangoSpanControl * span_control,
112 gchar * line_buffer, guint16 * index);
114 pango_span_markup_txt (cea708PangoSpanControl * span_control,
115 gchar * line_buffer, guint16 * index);
117 pango_span_markup_end (cea708PangoSpanControl * span_control,
118 gchar * line_buffer, guint16 * index);
120 gst_cea708dec_show_pango_window (Cea708Dec * decoder, guint window_id);
122 gst_cea708dec_clear_window_text (Cea708Dec * decoder, guint window_id);
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);
128 gst_cea708dec_set_pen_attributes (Cea708Dec * decoder,
129 guint8 * dtvcc_buffer, int index);
131 gst_cea708dec_for_each_window (Cea708Dec * decoder,
132 guint8 window_list, VisibilityControl visibility_control,
133 const gchar * log_message, void (*function) (Cea708Dec * decoder,
136 gst_cea708dec_process_command (Cea708Dec * decoder,
137 guint8 * dtvcc_buffer, int index);
138 static void get_cea708dec_bufcat (gpointer data, gpointer whole_buf);
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);
144 gst_cea708dec_process_c2 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
147 gst_cea708dec_process_g2 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
150 gst_cea708dec_process_c3 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
153 gst_cea708dec_process_g3 (Cea708Dec * decoder, guint8 * dtvcc_buffer,
156 gst_cea708dec_process_dtvcc_byte (Cea708Dec * decoder,
157 guint8 * dtvcc_buffer, int index);
159 /* For debug, print name of 708 command */
161 gst_cea708dec_create (PangoContext * pango_context)
164 Cea708Dec *decoder = g_malloc (sizeof (Cea708Dec));;
165 memset (decoder, 0, sizeof (Cea708Dec));
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);
172 decoder->desired_service = 1;
173 decoder->use_ARGB = FALSE;
174 decoder->pango_context = pango_context;
179 gst_cea708dec_free (Cea708Dec * dec)
183 for (i = 0; i < MAX_708_WINDOWS; i++) {
184 cea708Window *window = dec->cc_windows[i];
185 gst_cea708dec_clear_window (dec, window);
188 memset (dec, 0, sizeof (Cea708Dec));
193 gst_cea708dec_set_service_number (Cea708Dec * decoder, gint8 desired_service)
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);
205 decoder->current_window = 0;
210 gst_cea708dec_process_dtvcc_packet (Cea708Dec * decoder, guint8 * dtvcc_buffer,
214 gboolean need_render = FALSE;
215 cea708Window *window = NULL;
218 /* Service block header (see CEA-708 6.2.1) */
220 guint8 service_number;
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);
230 block_size = dtvcc_buffer[parse_index] & 0x1F;
231 service_number = (dtvcc_buffer[parse_index] & 0xE0) >> 5;
234 if (service_number == 7) {
235 /* Get extended service number */
236 service_number = dtvcc_buffer[parse_index] & 0x3F;
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);
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);
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) {
269 gst_cea708dec_process_dtvcc_byte (Cea708Dec * decoder,
270 guint8 * dtvcc_buffer, int index)
272 guint8 c = dtvcc_buffer[index];
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--;
280 GST_DEBUG ("processing 0x%02X", c);
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);
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");
306 } else if ((c >= 0x20) && (c <= 0x7F)) { /* G0 */
308 gst_cea708dec_window_add_char (decoder, CC_SPECIAL_CODE_MUSIC_NOTE);
310 gst_cea708dec_window_add_char (decoder, c);
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);
319 /* For debug, print name of 708 command */
321 gst_cea708dec_print_command_name (Cea708Dec * decoder, guint8 c)
324 const gchar *command = NULL;
328 command = (const gchar *) "End of text";
339 /* Set current window, no parameters */
340 g_snprintf (buffer, sizeof (buffer), "Set current window %d", c & 0x3);
345 command = (const gchar *) "Clear windows";
349 command = (const gchar *) "Display windows";
353 command = (const gchar *) "Hide windows";
357 command = (const gchar *) "Toggle windows";
361 command = (const gchar *) "Delete windows";
365 command = (const gchar *) "Delay";
369 command = (const gchar *) "Delay cancel";
373 command = (const gchar *) "Reset";
377 command = (const gchar *) "Set pen attributes";
381 command = (const gchar *) "Set pen color";
385 command = (const gchar *) "Set pen location";
389 command = (const gchar *) "Set window attributes";
400 g_snprintf (buffer, sizeof (buffer), "define window %d", c & 0x3);
405 if ((c > 0x80) && (c < 0x9F))
406 command = (const gchar *) "Unknown";
410 if (NULL != command) {
411 GST_LOG ("Process 708 command (%02X): %s", c, command);
416 gst_cea708dec_render_pangocairo (cea708Window * window)
419 cairo_surface_t *surf;
421 cairo_surface_t *surf_shadow;
422 PangoRectangle ink_rec, logical_rec;
425 pango_layout_get_pixel_extents (window->layout, &ink_rec, &logical_rec);
427 width = logical_rec.width + window->shadow_offset;
428 height = logical_rec.height + logical_rec.y + window->shadow_offset;
430 surf_shadow = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
431 shadow = cairo_create (surf_shadow);
433 /* clear shadow surface */
434 cairo_set_operator (shadow, CAIRO_OPERATOR_CLEAR);
435 cairo_paint (shadow);
436 cairo_set_operator (shadow, CAIRO_OPERATOR_OVER);
438 /* draw shadow text */
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);
445 /* draw outline text */
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);
453 cairo_destroy (shadow);
455 window->text_image = g_realloc (window->text_image, 4 * width * height);
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);
462 cairo_set_operator (crt, CAIRO_OPERATOR_OVER);
464 /* set default color */
465 cairo_set_source_rgb (crt, 1.0, 1.0, 1.0);
469 pango_cairo_show_layout (crt, window->layout);
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);
478 cairo_surface_destroy (surf_shadow);
479 cairo_surface_destroy (surf);
480 window->image_width = width;
481 window->image_height = height;
485 gst_cea708dec_adjust_values_with_fontdesc (cea708Window * window,
486 PangoFontDescription * desc)
488 gint font_size = pango_font_description_get_size (desc) / PANGO_SCALE;
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;
497 gst_cea708dec_text_list_add (GSList ** text_list,
498 gint len, const gchar * format, ...)
503 va_start (args, format);
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);
514 static const PangoAlignment
515 gst_cea708dec_get_align_mode (guint8 justify_mode)
517 guint align_mode = PANGO_ALIGN_LEFT;
519 switch (justify_mode) {
521 align_mode = PANGO_ALIGN_LEFT;
524 align_mode = PANGO_ALIGN_RIGHT;
527 align_mode = PANGO_ALIGN_CENTER;
531 align_mode = PANGO_ALIGN_LEFT;
537 gst_cea708dec_get_color_name (guint8 color)
542 case CEA708_COLOR_BLACK:
543 index = COLOR_TYPE_BLACK;
545 case CEA708_COLOR_WHITE:
546 index = COLOR_TYPE_WHITE;
548 case CEA708_COLOR_RED:
549 index = COLOR_TYPE_RED;
551 case CEA708_COLOR_GREEN:
552 index = COLOR_TYPE_GREEN;
554 case CEA708_COLOR_BLUE:
555 index = COLOR_TYPE_BLUE;
557 case CEA708_COLOR_YELLOW:
558 index = COLOR_TYPE_YELLOW;
560 case CEA708_COLOR_MAGENTA:
561 index = COLOR_TYPE_MAGENTA;
563 case CEA708_COLOR_CYAN:
564 index = COLOR_TYPE_CYAN;
570 return color_names[index];
574 gst_cea708dec_map_minimum_color (guint8 color)
576 /*According to spec minimum color list define */
578 switch ((color & 0x30) >> 4) {
589 switch ((color & 0xC) >> 2) {
600 switch (color & 0x3) {
615 gst_cea708dec_set_pen_color (Cea708Dec * decoder,
616 guint8 * dtvcc_buffer, int index)
618 cea708Window *window = decoder->cc_windows[decoder->current_window];
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);
639 gst_cea708dec_set_window_attributes (Cea708Dec * decoder,
640 guint8 * dtvcc_buffer, int index)
642 cea708Window *window = decoder->cc_windows[decoder->current_window];
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 */
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 +
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;
665 GST_LOG ("Print direction = %d", window->print_direction);
669 gst_cea708dec_set_pen_style (Cea708Dec * decoder, guint8 pen_style_id)
671 cea708Window *window = decoder->cc_windows[decoder->current_window];
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;
685 /* CEA-708 predefined pen style ids */
686 switch (pen_style_id) {
688 case PEN_STYLE_DEFAULT:
689 window->pen_attributes.font_style = FONT_STYLE_DEFAULT;
692 case PEN_STYLE_MONO_SERIF:
693 window->pen_attributes.font_style = FONT_STYLE_MONO_SERIF;
696 case PEN_STYLE_PROP_SERIF:
697 window->pen_attributes.font_style = FONT_STYLE_PROP_SERIF;
700 case PEN_STYLE_MONO_SANS:
701 window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
704 case PEN_STYLE_PROP_SANS:
705 window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
708 case PEN_STYLE_MONO_SANS_TRANSPARENT:
709 window->pen_attributes.font_style = FONT_STYLE_MONO_SANS;
710 window->pen_color.bg_opacity = TRANSPARENT;
713 case PEN_STYLE_PROP_SANS_TRANSPARENT:
714 window->pen_attributes.font_style = FONT_STYLE_PROP_SANS;
715 window->pen_color.bg_opacity = TRANSPARENT;
721 gst_cea708dec_set_window_style (Cea708Dec * decoder, guint8 style_id)
723 cea708Window *window = decoder->cc_windows[decoder->current_window];
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;
736 /* CEA-708 predefined window style ids */
739 case WIN_STYLE_NORMAL:
742 case WIN_STYLE_TRANSPARENT:
743 window->fill_opacity = TRANSPARENT;
746 case WIN_STYLE_NORMAL_CENTERED:
747 window->justify_mode = JUSTIFY_CENTER;
750 case WIN_STYLE_NORMAL_WORD_WRAP:
751 window->word_wrap = TRUE;
754 case WIN_STYLE_TRANSPARENT_WORD_WRAP:
755 window->fill_opacity = TRANSPARENT;
756 window->word_wrap = TRUE;
759 case WIN_STYLE_TRANSPARENT_CENTERED:
760 window->fill_opacity = TRANSPARENT;
761 window->justify_mode = JUSTIFY_CENTER;
764 case WIN_STYLE_ROTATED:
765 window->print_direction = PRINT_DIR_TOP_TO_BOTTOM;
766 window->scroll_direction = SCROLL_DIR_RIGHT_TO_LEFT;
771 /* Define window - window size, window style, pen style, anchor position, etc */
773 gst_cea708dec_define_window (Cea708Dec * decoder,
774 guint8 * dtvcc_buffer, int index)
776 cea708Window *window = decoder->cc_windows[decoder->current_window];
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;
785 guint8 column_lock = 0;
786 gboolean visible = FALSE;
788 guint8 pen_style_id = 0;
789 #ifndef GST_DISABLE_GST_DEBUG
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]);
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
805 * TODO: also set all text positions to the fill color */
806 window->deleted = FALSE;
810 /* format of parameters:
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 */
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;
824 /* parameter byte 1 */
825 relative_position = (dtvcc_buffer[index + 1] & 0x80) ? TRUE : FALSE;
826 anchor_vertical = dtvcc_buffer[index + 1] & 0x7F;
828 /* parameter byte 2 */
829 anchor_horizontal = dtvcc_buffer[index + 2];
831 /* parameter byte 3 */
832 anchor_point = (dtvcc_buffer[index + 3] & 0xF0) >> 4;
833 row_count = (dtvcc_buffer[index + 3] & 0xF) + 1;
835 /* parameter byte 4 */
836 column_count = (dtvcc_buffer[index + 4] & 0x3F) + 1;
838 /* parameter byte 5 */
839 style_id = (dtvcc_buffer[index + 5] & 0x38) >> 3;
840 pen_style_id = dtvcc_buffer[index + 5] & 0x7;
842 window->screen_vertical = anchor_vertical;
843 window->screen_horizontal = anchor_horizontal;
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;
857 window->screen_vertical /= 100;
858 window->screen_horizontal /= 100;
860 window->screen_vertical *= 100;
861 window->screen_horizontal *= 100;
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;
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;
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;
887 window->style_id = style_id;
890 if (pen_style_id != 0) {
891 window->pen_style_id = pen_style_id;
894 gst_cea708dec_set_window_style (decoder, window->style_id);
895 gst_cea708dec_set_pen_style (decoder, window->pen_style_id);
898 GST_LOG ("priority=%d anchor=%d relative_pos=%d anchor_v=%d anchor_h=%d",
900 window->anchor_point,
901 window->relative_position,
902 window->anchor_vertical, window->anchor_horizontal);
904 GST_LOG ("row_count=%d col_count=%d row_lock=%d col_lock=%d visible=%d",
906 window->column_count,
907 window->row_lock, window->column_lock, window->visible);
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",
912 window->pen_style_id,
913 window->screen_horizontal,
914 window->screen_vertical,
915 window->v_offset, window->h_offset, v_anchor, h_anchor);
919 pango_span_markup_init (cea708PangoSpanControl * span_control)
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;
930 pango_span_markup_start (cea708PangoSpanControl * span_control,
931 gchar * line_buffer, guint16 * index)
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;
942 GST_WARNING ("warning span start !!!");
947 pango_span_markup_txt (cea708PangoSpanControl * span_control,
948 gchar * line_buffer, guint16 * index)
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] = '>';
956 span_control->span_txt_flag = TRUE;
958 GST_WARNING ("warning span txt !!!");
963 pango_span_markup_end (cea708PangoSpanControl * span_control,
964 gchar * line_buffer, guint16 * index)
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;
976 GST_WARNING ("line_buffer=%s", line_buffer);
977 GST_WARNING ("warning span end !!!");
981 /* FIXME: Convert to GString ! */
983 gst_cea708dec_show_pango_window (Cea708Dec * decoder, guint window_id)
985 cea708Window *window = decoder->cc_windows[window_id];
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;
992 gint16 right_index; /* within a single line of window text, the
993 * index of the rightmost non-blank character */
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;
1003 GST_DEBUG ("window #%02d (visible:%d)", window_id, window->visible);
1005 window->updated = TRUE;
1007 if (!window->visible) {
1008 GST_DEBUG ("Window is not visible, skipping rendering");
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 */
1024 GST_DEBUG ("No visible text, skipping rendering");
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 != ' ') {
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 != ' ') {
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);
1051 GST_MEMDUMP ("line_buffer", (guint8 *) line_buffer, index);
1053 ("text[%d][%d] '%c' underline:%d , italics:%d , font_style:%d , pen_size : %d",
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);
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);
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)
1077 GST_LOG ("Markup changed");
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 !!!");
1090 pango_span_markup_start (&span_control, line_buffer, &index);
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;
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;
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];
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;
1125 /* Check for transitions to/from pen size */
1126 pen_size = pen_size_names[current->pen_attributes.pen_size];
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;
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;
1139 /* Check for transitions to/from foreground 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);
1155 ("span_control.fg_color was NOT updated (still 0x%02x)",
1156 span_control.fg_color);
1158 /* Check for transitions to/from background 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);
1174 ("span_control.bg_color was NOT updated (still 0x%02x)",
1175 span_control.bg_color);
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);
1182 span_control.span_next_flag = FALSE;
1183 } while (span_control.span_next_flag);
1186 /* Finally write the character */
1191 switch (current->c) {
1193 g_snprintf (&(line_buffer[index]),
1194 sizeof (line_buffer) - index - 1, "&");
1199 g_snprintf (&(line_buffer[index]),
1200 sizeof (line_buffer) - index - 1, "<");
1205 g_snprintf (&(line_buffer[index]),
1206 sizeof (line_buffer) - index - 1, ">");
1211 g_snprintf (&(line_buffer[index]),
1212 sizeof (line_buffer) - index - 1, "'");
1217 g_snprintf (&(line_buffer[index]),
1218 sizeof (line_buffer) - index - 1, """);
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++];
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)
1240 pango_span_markup_end (&span_control, line_buffer, &index);
1241 pango_span_markup_init (&span_control);
1244 GST_LOG ("adding row[%d]: %s\nlength:%d", row, line_buffer, index);
1246 if (row != window->row_count - 1) {
1247 line_buffer[index++] = '\n';
1251 gst_cea708dec_text_list_add (&decoder->text_list, index + 1, "%s",
1257 if (col == window->column_count && row != window->row_count - 1) {
1259 gst_cea708dec_text_list_add (&decoder->text_list, strlen ("\n") + 1,
1265 GST_LOG ("window %d had no text", window_id);
1267 /* send text to output pad */
1268 gst_cea708dec_render_text (decoder, &decoder->text_list, len, window_id);
1273 gst_cea708dec_clear_window_text (Cea708Dec * decoder, guint window_id)
1275 cea708Window *window = decoder->cc_windows[window_id];
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;
1289 gst_cea708dec_scroll_window_up (Cea708Dec * decoder, guint window_id)
1291 cea708Window *window = decoder->cc_windows[window_id];
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
1297 GST_LOG_OBJECT (decoder, "called for window: %d", window_id);
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];
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;
1317 gst_cea708dec_clear_window (Cea708Dec * decoder, cea708Window * window)
1319 g_free (window->text_image);
1320 memset (window, 0, sizeof (cea708Window));
1324 gst_cea708dec_init_window (Cea708Dec * decoder, guint window_id)
1326 cea708Window *window = decoder->cc_windows[window_id];
1328 if (window_id >= MAX_708_WINDOWS) {
1329 GST_ERROR ("window_id outside of range %d", window_id);
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;
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;
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;
1363 /* Init pen position */
1364 window->pen_row = 0;
1365 window->pen_col = 0;
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);
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;
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;
1396 gst_cea708dec_set_pen_attributes (Cea708Dec * decoder,
1397 guint8 * dtvcc_buffer, int index)
1399 cea708Window *window = decoder->cc_windows[decoder->current_window];
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;
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);
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);
1425 gst_cea708dec_for_each_window (Cea708Dec * decoder,
1426 guint8 window_list, VisibilityControl visibility_control,
1427 const gchar * log_message, void (*function) (Cea708Dec * decoder,
1432 GST_LOG ("window_list: %02x", window_list);
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) {
1445 case SWITCH_TO_HIDE:
1446 decoder->cc_windows[i]->visible = FALSE;
1449 case SWITCH_TO_SHOW:
1450 decoder->cc_windows[i]->visible = TRUE;
1454 decoder->cc_windows[i]->visible = !decoder->cc_windows[i]->visible;
1458 if (NULL != function) {
1459 function (decoder, i);
1468 gst_cea708dec_process_command (Cea708Dec * decoder,
1469 guint8 * dtvcc_buffer, int index)
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) */
1475 /* Process command codes */
1476 gst_cea708dec_print_command_name (decoder, c);
1478 case CC_COMMAND_ETX: /* End of text */
1479 window->visible = TRUE;
1480 gst_cea708dec_show_pango_window (decoder, decoder->current_window);
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);
1495 case CC_COMMAND_CLW: /* Clear windows */
1496 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1498 /* Clear window data */
1499 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1500 "clear_window", gst_cea708dec_clear_window_text);
1503 case CC_COMMAND_DSW: /* Display windows */
1504 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1507 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1508 "display_window", gst_cea708dec_show_pango_window);
1511 case CC_COMMAND_HDW: /* Hide windows */
1512 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1515 gst_cea708dec_for_each_window (decoder, window_list, SWITCH_TO_HIDE,
1516 "hide_window", NULL);
1519 case CC_COMMAND_TGW: /* Toggle windows */
1520 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
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);
1527 case CC_COMMAND_DLW: /* Delete windows */
1528 decoder->output_ignore = 1; /* 1 byte parameter = windowmap */
1531 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1532 "delete_window", gst_cea708dec_init_window);
1535 case CC_COMMAND_DLY: /* Delay */
1536 decoder->output_ignore = 1; /* 1 byte parameter = delay in 1/10 sec */
1537 /* TODO: - process this command. */
1540 case CC_COMMAND_DLC: /* Delay cancel */
1541 /* TODO: - process this command. */
1545 case CC_COMMAND_RST:
1546 /* Reset - cancel any delay, delete all windows */
1547 window_list = 0xFF; /* all windows... */
1550 gst_cea708dec_for_each_window (decoder, window_list, NO_CHANGE,
1551 "reset_window", gst_cea708dec_init_window);
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);
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);
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);
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);
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:
1587 window_list = 0xFF; /* all windows... */
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);
1599 get_cea708dec_bufcat (gpointer data, gpointer whole_buf)
1601 gchar *buf = whole_buf;
1602 strcat ((gchar *) buf, data);
1607 gst_cea708dec_render_text (Cea708Dec * decoder, GSList ** text_list,
1608 gint length, guint window_id)
1610 gchar *out_str = NULL;
1611 PangoAlignment align_mode;
1612 PangoFontDescription *desc;
1614 cea708Window *window = decoder->cc_windows[window_id];
1617 out_str = g_malloc0 (length + 1);
1618 memset (out_str, 0, length + 1);
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]);
1630 font_desc = g_strdup (decoder->default_font_desc);
1631 desc = pango_font_description_from_string (font_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);
1639 GST_ERROR ("font description parse failed: %s", font_desc);
1643 /* data freed in slist loop!
1644 *g_slist_free_full (*text_list, g_free); */
1653 gst_cea708dec_window_add_char (Cea708Dec * decoder, gunichar c)
1655 cea708Window *window = decoder->cc_windows[decoder->current_window];
1659 /* Add one character to the current window, using current pen location.
1660 * Wrap pen location if necessary */
1661 if (c == 0) /* NULL */
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 = ' ';
1668 window->pen_col = 0;
1672 if (c == 0x08) { /* BS */
1673 switch (window->print_direction) {
1674 case PRINT_DIR_LEFT_TO_RIGHT:
1675 if (window->pen_col) {
1680 case PRINT_DIR_RIGHT_TO_LEFT:
1684 case PRINT_DIR_TOP_TO_BOTTOM:
1685 if (window->pen_row) {
1690 case PRINT_DIR_BOTTOM_TO_TOP:
1694 pen_row = window->pen_row;
1695 pen_col = window->pen_col;
1696 window->text[pen_row][pen_col].c = ' ';
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);
1709 ("carriage return, window->word_wrap=%d,window->scroll_direction=%d",
1710 window->word_wrap, window->scroll_direction);
1711 window->pen_col = 0;
1715 if (window->pen_col >= window->column_count) {
1716 window->pen_col = 0;
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);
1724 window->pen_row = window->row_count - 1;
1725 GST_WARNING ("pen row exceed window row count,scroll up");
1728 if ((c != '\r') && (c != '\n')) {
1729 pen_row = window->pen_row;
1730 pen_col = window->pen_col;
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);
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;
1743 switch (window->print_direction) {
1744 case PRINT_DIR_LEFT_TO_RIGHT:
1748 case PRINT_DIR_RIGHT_TO_LEFT:
1749 if (window->pen_col) {
1754 case PRINT_DIR_TOP_TO_BOTTOM:
1758 case PRINT_DIR_BOTTOM_TO_TOP:
1759 if (window->pen_row) {
1763 } /* switch (print_direction) */
1768 gst_cea708dec_process_c2 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
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;
1783 gst_cea708dec_process_g2 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
1785 guint8 c = dtvcc_buffer[index];
1786 gst_cea708dec_window_add_char (decoder, g2_table[c - 0x20]);
1787 decoder->output_ignore = 1;
1791 gst_cea708dec_process_c3 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
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;
1806 gst_cea708dec_process_g3 (Cea708Dec * decoder, guint8 * dtvcc_buffer, int index)
1808 gst_cea708dec_window_add_char (decoder, 0x5F);
1809 decoder->output_ignore = 1;
1813 gst_cea708dec_set_video_width_height (Cea708Dec * decoder, gint width,
1816 decoder->width = width;
1817 decoder->height = height;