Add sami extension for auto-loading of subs
[vlc.git] / modules / codec / zvbi.c
blobffef4dcc7731233c1cd93b49d15799b5aec6ee22
1 /*****************************************************************************
2 * zvbi.c : VBI and Teletext PES demux and decoder using libzvbi
3 *****************************************************************************
4 * Copyright (C) 2007, M2X
5 * $Id$
7 * Authors: Derk-Jan Hartman <djhartman at m2x dot nl>
8 * Jean-Paul Saman <jpsaman at m2x dot nl>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 * information on teletext format can be found here :
28 * http://pdc.ro.nu/teletext.html
30 *****************************************************************************/
32 /* This module implements:
33 * ETSI EN 301 775: VBI data in PES
34 * ETSI EN 300 472: EBU Teletext data in PES
35 * ETSI EN 300 706: Enhanced Teletext (libzvbi)
36 * ETSI EN 300 231: Video Programme System [VPS] (libzvbi)
37 * ETSI EN 300 294: 625-line Wide Screen Signaling [WSS] (libzvbi)
38 * EIA-608 Revision A: Closed Captioning [CC] (libzvbi)
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
45 #include <ctype.h>
47 #include <vlc_common.h>
48 #include <vlc_plugin.h>
49 #include <assert.h>
50 #include <libzvbi.h>
52 #include <vlc_codec.h>
53 #include "substext.h"
55 /*****************************************************************************
56 * Module descriptor.
57 *****************************************************************************/
58 static int Open ( vlc_object_t * );
59 static void Close( vlc_object_t * );
61 #define PAGE_TEXT N_("Teletext page")
62 #define PAGE_LONGTEXT N_("Open the indicated Teletext page. " \
63 "Default page is index 100.")
65 #define OPAQUE_TEXT N_("Opacity")
66 #define OPAQUE_LONGTEXT N_("Setting to true " \
67 "makes the text to be boxed and maybe easier to read." )
69 #define POS_TEXT N_("Teletext alignment")
70 #define POS_LONGTEXT N_( \
71 "You can enforce the teletext position on the video " \
72 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
73 "also use combinations of these values, eg. 6 = top-right).")
75 #define TELX_TEXT N_("Teletext text subtitles")
76 #define TELX_LONGTEXT N_( "Output teletext subtitles as text " \
77 "instead of as RGBA." )
79 #define LEVEL_TEXT N_("Presentation Level")
81 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
82 static const char *const ppsz_pos_descriptions[] =
83 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
84 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
86 static const int level_values[] = { 0, 1, 2, 3 };
87 static const char *const level_descriptions[] =
88 { N_("1"), N_("1.5"), N_("2.5"), N_("3.5") };
90 /* separate internal and zvbi values, as the latter could change */
91 static const int level_zvbi_values[] =
92 { VBI_WST_LEVEL_1, VBI_WST_LEVEL_1p5, VBI_WST_LEVEL_2p5, VBI_WST_LEVEL_3p5 };
94 vlc_module_begin ()
95 set_description( N_("VBI and Teletext decoder") )
96 set_shortname( N_("VBI & Teletext") )
97 set_capability( "spu decoder", 51 )
98 set_category( CAT_INPUT )
99 set_subcategory( SUBCAT_INPUT_SCODEC )
100 set_callbacks( Open, Close )
102 add_integer_with_range( "vbi-page", 100, 0, 'z' << 16,
103 PAGE_TEXT, PAGE_LONGTEXT, false )
104 add_bool( "vbi-opaque", false,
105 OPAQUE_TEXT, OPAQUE_LONGTEXT, false )
106 add_integer( "vbi-position", 8, POS_TEXT, POS_LONGTEXT, false )
107 change_integer_list( pi_pos_values, ppsz_pos_descriptions );
108 add_bool( "vbi-text", false,
109 TELX_TEXT, TELX_LONGTEXT, false )
110 add_integer( "vbi-level", 3, LEVEL_TEXT, NULL, false )
111 change_integer_list( level_values, level_descriptions );
112 vlc_module_end ()
114 /****************************************************************************
115 * Local structures
116 ****************************************************************************/
118 // #define ZVBI_DEBUG
120 //Guessing table for missing "default region triplet"
121 static const int pi_default_triplet[] = {
122 0, 0, 0, 0, // slo slk cze ces
123 8, // pol
124 24,24,24,24,24, //scc scr srp hrv slv
125 24,24, //rum ron
126 32,32,32,32,32, //est lit rus bul ukr
127 48,48, //gre ell
128 64, //ara
129 88, //heb
130 16 }; //default
131 static const char *const ppsz_default_triplet[] = {
132 "slo", "slk", "cze", "ces",
133 "pol",
134 "scc", "scr", "srp", "hrv", "slv",
135 "rum", "ron",
136 "est", "lit", "rus", "bul", "ukr",
137 "gre", "ell",
138 "ara",
139 "heb",
140 NULL
143 typedef enum {
144 ZVBI_KEY_RED = 'r' << 16,
145 ZVBI_KEY_GREEN = 'g' << 16,
146 ZVBI_KEY_YELLOW = 'y' << 16,
147 ZVBI_KEY_BLUE = 'b' << 16,
148 ZVBI_KEY_INDEX = 'i' << 16,
149 } ttxt_key_id;
151 #define MAX_SLICES 32
153 struct decoder_sys_t
155 vbi_decoder * p_vbi_dec;
156 vbi_sliced p_vbi_sliced[MAX_SLICES];
157 unsigned int i_last_page;
158 bool b_update;
159 bool b_text; /* Subtitles as text */
161 vlc_mutex_t lock; /* Lock to protect the following variables */
162 /* Positioning of Teletext images */
163 int i_align;
164 /* */
165 unsigned int i_wanted_page;
166 unsigned int i_wanted_subpage;
167 /* */
168 bool b_opaque;
169 unsigned int i_level;
170 struct {
171 int pgno, subno;
172 } nav_link[6];
173 int i_key[3];
176 static int Decode( decoder_t *, block_t * );
178 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
179 bool b_text,
180 int i_columns, int i_rows,
181 int i_align, mtime_t i_pts );
183 static void EventHandler( vbi_event *ev, void *user_data );
184 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
185 const video_format_t *p_fmt, bool b_opaque, const int text_offset );
186 static int get_first_visible_row( vbi_char *p_text, int rows, int columns);
187 static int get_last_visible_row( vbi_char *p_text, int rows, int columns);
189 /* Properties callbacks */
190 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
191 vlc_value_t oldval, vlc_value_t newval, void *p_data );
192 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
193 vlc_value_t oldval, vlc_value_t newval, void *p_data );
194 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
195 vlc_value_t oldval, vlc_value_t newval, void *p_data );
197 /*****************************************************************************
198 * Open: probe the decoder and return score
199 *****************************************************************************
200 * Tries to launch a decoder and return score so that the interface is able
201 * to chose.
202 *****************************************************************************/
203 static int Open( vlc_object_t *p_this )
205 decoder_t *p_dec = (decoder_t *) p_this;
206 decoder_sys_t *p_sys = NULL;
208 if( p_dec->fmt_in.i_codec != VLC_CODEC_TELETEXT )
209 return VLC_EGENERIC;
211 int i_page = var_CreateGetInteger( p_dec, "vbi-page" );
212 if( i_page > 999 )
214 msg_Warn( p_dec, "invalid vbi-page requested");
215 i_page = 0;
218 p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) );
219 if( p_sys == NULL )
220 return VLC_ENOMEM;
222 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
223 p_sys->b_update = false;
224 p_sys->p_vbi_dec = vbi_decoder_new();
225 vlc_mutex_init( &p_sys->lock );
227 if( p_sys->p_vbi_dec == NULL )
229 msg_Err( p_dec, "VBI decoder could not be created." );
230 Close( p_this );
231 return VLC_ENOMEM;
234 /* Some broadcasters in countries with level 1 and level 1.5 still not send a G0 to do
235 * matches against table 32 of ETSI 300 706. We try to do some best effort guessing
236 * This is not perfect, but might handle some cases where we know the vbi language
237 * is known. It would be better if people started sending G0 */
238 for( int i = 0; ppsz_default_triplet[i] != NULL; i++ )
240 if( p_dec->fmt_in.psz_language && !strcasecmp( p_dec->fmt_in.psz_language, ppsz_default_triplet[i] ) )
242 vbi_teletext_set_default_region( p_sys->p_vbi_dec, pi_default_triplet[i]);
243 msg_Dbg( p_dec, "overwriting default zvbi region: %d", pi_default_triplet[i] );
247 vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE | VBI_EVENT_NETWORK |
248 #ifdef ZVBI_DEBUG
249 VBI_EVENT_CAPTION | VBI_EVENT_TRIGGER |
250 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_NETWORK_ID |
251 #endif
252 0 , EventHandler, p_dec );
254 /* Create the var on vlc_global. */
255 p_sys->i_wanted_page = i_page;
256 var_AddCallback( p_dec, "vbi-page", RequestPage, p_sys );
258 /* Check if the Teletext track has a known "initial page". */
259 if( p_sys->i_wanted_page == 100 && p_dec->fmt_in.subs.teletext.i_magazine != -1 )
261 p_sys->i_wanted_page = 100 * p_dec->fmt_in.subs.teletext.i_magazine +
262 vbi_bcd2dec( p_dec->fmt_in.subs.teletext.i_page );
263 var_SetInteger( p_dec, "vbi-page", p_sys->i_wanted_page );
265 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
267 p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
268 var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
270 p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
272 p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
274 p_sys->i_level = var_CreateGetInteger( p_dec, "vbi-level" );
276 /* Listen for keys */
277 var_AddCallback( p_dec->obj.libvlc, "key-pressed", EventKey, p_dec );
279 p_dec->fmt_out.i_codec = p_sys->b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA;
281 p_dec->pf_decode = Decode;
282 return VLC_SUCCESS;
285 /*****************************************************************************
286 * Close:
287 *****************************************************************************/
288 static void Close( vlc_object_t *p_this )
290 decoder_t *p_dec = (decoder_t*) p_this;
291 decoder_sys_t *p_sys = p_dec->p_sys;
293 var_DelCallback( p_dec, "vbi-opaque", Opaque, p_sys );
294 var_DelCallback( p_dec, "vbi-page", RequestPage, p_sys );
295 var_DelCallback( p_dec->obj.libvlc, "key-pressed", EventKey, p_dec );
297 vlc_mutex_destroy( &p_sys->lock );
299 if( p_sys->p_vbi_dec )
300 vbi_decoder_delete( p_sys->p_vbi_dec );
301 free( p_sys );
304 #ifdef WORDS_BIGENDIAN
305 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
306 #else
307 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
308 #endif
310 /*****************************************************************************
311 * Decode:
312 *****************************************************************************/
313 static int Decode( decoder_t *p_dec, block_t *p_block )
315 decoder_sys_t *p_sys = p_dec->p_sys;
316 subpicture_t *p_spu = NULL;
317 video_format_t fmt;
318 bool b_cached = false;
319 vbi_page p_page;
321 if( p_block == NULL ) /* No Drain */
322 return VLCDEC_SUCCESS;
324 if( p_block->i_buffer > 0 &&
325 ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) ||
326 ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) )
328 vbi_sliced *p_sliced = p_sys->p_vbi_sliced;
329 unsigned int i_lines = 0;
331 p_block->i_buffer--;
332 p_block->p_buffer++;
333 while( p_block->i_buffer >= 2 )
335 int i_id = p_block->p_buffer[0];
336 unsigned i_size = p_block->p_buffer[1];
338 if( 2 + i_size > p_block->i_buffer )
339 break;
341 if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES )
343 if(p_block->p_buffer[3] == 0xE4 ) /* framing_code */
345 unsigned line_offset = p_block->p_buffer[2] & 0x1f;
346 unsigned field_parity = p_block->p_buffer[2] & 0x20;
348 p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B;
349 if( line_offset > 0 )
350 p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313);
351 else
352 p_sliced[i_lines].line = 0;
353 for( int i = 0; i < 42; i++ )
354 p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] );
355 i_lines++;
359 p_block->i_buffer -= 2 + i_size;
360 p_block->p_buffer += 2 + i_size;
363 if( i_lines > 0 )
364 vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 );
367 /* */
368 vlc_mutex_lock( &p_sys->lock );
369 if( p_sys->i_wanted_page == 0 )
371 vlc_mutex_unlock( &p_sys->lock );
372 block_Release( p_block );
373 return VLCDEC_SUCCESS;
375 const int i_align = p_sys->i_align;
376 const unsigned int i_wanted_page = p_sys->i_wanted_page;
377 const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage;
378 const bool b_opaque = p_sys->b_opaque;
379 const unsigned int i_level = p_sys->i_level > 3 ? 3 : p_sys->i_level;
380 vlc_mutex_unlock( &p_sys->lock );
382 /* Try to see if the page we want is in the cache yet */
383 memset( &p_page, 0, sizeof(vbi_page) );
384 b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
385 vbi_dec2bcd( i_wanted_page ),
386 i_wanted_subpage, level_zvbi_values[i_level],
387 25, true );
389 if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update )
390 goto error;
392 if( !b_cached )
394 if( p_sys->b_text && p_sys->i_last_page != i_wanted_page )
396 /* We need to reset the subtitle */
397 p_spu = Subpicture( p_dec, &fmt, true,
398 p_page.columns, p_page.rows,
399 i_align, p_block->i_pts );
400 if( !p_spu )
401 goto error;
402 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
403 p_spu_sys->region.p_segments = text_segment_New("");
405 p_sys->b_update = true;
406 p_sys->i_last_page = i_wanted_page;
407 goto exit;
409 goto error;
412 p_sys->b_update = false;
413 p_sys->i_last_page = i_wanted_page;
414 #ifdef ZVBI_DEBUG
415 msg_Dbg( p_dec, "we now have page: %d ready for display",
416 i_wanted_page );
417 #endif
419 /* Ignore transparent rows at the beginning and end */
420 int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns );
421 int i_num_rows;
422 if ( i_first_row < 0 ) {
423 i_first_row = p_page.rows - 1;
424 i_num_rows = 0;
425 } else {
426 i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1;
428 #ifdef ZVBI_DEBUG
429 msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i",
430 i_first_row + 1, i_first_row + i_num_rows, p_page.rows );
431 #endif
433 /* If there is a page or sub to render, then we do that here */
434 /* Create the subpicture unit */
435 p_spu = Subpicture( p_dec, &fmt, p_sys->b_text,
436 p_page.columns, i_num_rows,
437 i_align, p_block->i_pts );
438 if( !p_spu )
439 goto error;
441 if( p_sys->b_text )
443 unsigned int i_textsize = 7000;
444 int i_total,offset;
445 char p_text[i_textsize+1];
447 i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
448 "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows );
450 for( offset=1; offset<i_total && isspace( p_text[i_total-offset ] ); offset++)
451 p_text[i_total-offset] = '\0';
453 i_total -= offset;
455 offset=0;
456 while( offset < i_total && isspace( p_text[offset] ) )
457 offset++;
459 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
460 p_spu_sys->region.p_segments = text_segment_New( &p_text[offset] );
461 if( p_spu_sys->region.p_segments && b_opaque )
463 p_spu_sys->region.p_segments->style = text_style_Create( STYLE_NO_DEFAULTS );
464 if( p_spu_sys->region.p_segments->style )
466 /* Set text background */
467 p_spu_sys->region.p_segments->style->i_style_flags = STYLE_BACKGROUND;
468 p_spu_sys->region.p_segments->style->i_features |= STYLE_HAS_FLAGS;
472 p_spu_sys->region.inner_align = i_align;
473 p_spu_sys->region.flags = UPDT_REGION_IGNORE_BACKGROUND;
475 #ifdef ZVBI_DEBUG
476 msg_Info( p_dec, "page %x-%x(%d)\n\"%s\"", p_page.pgno, p_page.subno, i_total, &p_text[offset] );
477 #endif
479 else
481 picture_t *p_pic = p_spu->p_region->p_picture;
483 /* ZVBI is stupid enough to assume pitch == width */
484 p_pic->p->i_pitch = 4 * fmt.i_width;
486 /* Maintain subtitle postion */
487 p_spu->p_region->i_y = i_first_row*10;
488 p_spu->i_original_picture_width = p_page.columns*12;
489 p_spu->i_original_picture_height = p_page.rows*10;
491 vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32,
492 p_spu->p_region->p_picture->p->p_pixels, -1,
493 0, i_first_row, p_page.columns, i_num_rows,
494 1, 1);
496 vlc_mutex_lock( &p_sys->lock );
497 memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ;
498 vlc_mutex_unlock( &p_sys->lock );
500 OpaquePage( p_pic, &p_page, &fmt, b_opaque, i_first_row * p_page.columns );
503 exit:
504 vbi_unref_page( &p_page );
505 block_Release( p_block );
506 if( p_spu )
507 decoder_QueueSub( p_dec, p_spu );
508 return VLCDEC_SUCCESS;
510 error:
511 vbi_unref_page( &p_page );
512 block_Release( p_block );
513 return VLCDEC_SUCCESS;
516 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
517 bool b_text,
518 int i_columns, int i_rows, int i_align,
519 mtime_t i_pts )
521 video_format_t fmt;
522 subpicture_t *p_spu=NULL;
524 /* If there is a page or sub to render, then we do that here */
525 /* Create the subpicture unit */
526 if( b_text )
527 p_spu = decoder_NewSubpictureText( p_dec );
528 else
529 p_spu = decoder_NewSubpicture( p_dec, NULL );
530 if( !p_spu )
532 msg_Warn( p_dec, "can't get spu buffer" );
533 return NULL;
536 video_format_Init(&fmt, b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA);
537 if( b_text )
539 fmt.i_bits_per_pixel = 0;
541 else
543 fmt.i_width = fmt.i_visible_width = i_columns * 12;
544 fmt.i_height = fmt.i_visible_height = i_rows * 10;
545 fmt.i_bits_per_pixel = 32;
546 fmt.i_sar_num = fmt.i_sar_den = 0; /* let the vout set the correct AR */
548 fmt.i_x_offset = fmt.i_y_offset = 0;
550 p_spu->p_region = subpicture_region_New( &fmt );
551 if( p_spu->p_region == NULL )
553 msg_Err( p_dec, "cannot allocate SPU region" );
554 subpicture_Delete( p_spu );
555 return NULL;
558 p_spu->p_region->i_x = 0;
559 p_spu->p_region->i_y = 0;
561 p_spu->i_start = i_pts;
562 p_spu->i_stop = b_text ? i_pts + (10*CLOCK_FREQ): 0;
563 p_spu->b_ephemer = true;
564 p_spu->b_absolute = b_text ? false : true;
566 if( !b_text )
567 p_spu->p_region->i_align = i_align;
568 p_spu->i_original_picture_width = fmt.i_width;
569 p_spu->i_original_picture_height = fmt.i_height;
571 /* */
572 *p_fmt = fmt;
573 return p_spu;
576 static void EventHandler( vbi_event *ev, void *user_data )
578 decoder_t *p_dec = (decoder_t *)user_data;
579 decoder_sys_t *p_sys = p_dec->p_sys;
581 if( ev->type == VBI_EVENT_TTX_PAGE )
583 #ifdef ZVBI_DEBUG
584 msg_Info( p_dec, "Page %03x.%02x ",
585 ev->ev.ttx_page.pgno,
586 ev->ev.ttx_page.subno & 0xFF);
587 #endif
588 if( p_sys->i_last_page == vbi_bcd2dec( ev->ev.ttx_page.pgno ) )
589 p_sys->b_update = true;
590 #ifdef ZVBI_DEBUG
591 if( ev->ev.ttx_page.clock_update )
592 msg_Dbg( p_dec, "clock" );
593 if( ev->ev.ttx_page.header_update )
594 msg_Dbg( p_dec, "header" );
595 #endif
597 else if( ev->type == VBI_EVENT_CLOSE )
598 msg_Dbg( p_dec, "Close event" );
599 else if( ev->type == VBI_EVENT_CAPTION )
600 msg_Dbg( p_dec, "Caption line: %x", ev->ev.caption.pgno );
601 else if( ev->type == VBI_EVENT_NETWORK )
603 msg_Dbg( p_dec, "Network change");
604 vbi_network n = ev->ev.network;
605 msg_Dbg( p_dec, "Network id:%d name: %s, call: %s ", n.nuid, n.name, n.call );
607 else if( ev->type == VBI_EVENT_TRIGGER )
608 msg_Dbg( p_dec, "Trigger event" );
609 else if( ev->type == VBI_EVENT_ASPECT )
610 msg_Dbg( p_dec, "Aspect update" );
611 else if( ev->type == VBI_EVENT_PROG_INFO )
612 msg_Dbg( p_dec, "Program info received" );
613 else if( ev->type == VBI_EVENT_NETWORK_ID )
614 msg_Dbg( p_dec, "Network ID changed" );
617 static int get_first_visible_row( vbi_char *p_text, int rows, int columns)
619 for ( int i = 0; i < rows * columns; i++ )
621 if ( p_text[i].opacity != VBI_TRANSPARENT_SPACE )
623 return i / columns;
627 return -1;
630 static int get_last_visible_row( vbi_char *p_text, int rows, int columns)
632 for ( int i = rows * columns - 1; i >= 0; i-- )
634 if (p_text[i].opacity != VBI_TRANSPARENT_SPACE)
636 return i / columns;
640 return -1;
643 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
644 const video_format_t *p_fmt, bool b_opaque, const int text_offset )
646 unsigned int x, y;
648 assert( p_fmt->i_chroma == VLC_CODEC_RGBA );
650 /* Kludge since zvbi doesn't provide an option to specify opacity. */
651 for( y = 0; y < p_fmt->i_height; y++ )
653 for( x = 0; x < p_fmt->i_width; x++ )
655 const vbi_opacity opacity = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].opacity;
656 const int background = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].background;
657 uint32_t *p_pixel = (uint32_t*)&p_src->p->p_pixels[y * p_src->p->i_pitch + 4*x];
659 switch( opacity )
661 /* Show video instead of this character */
662 case VBI_TRANSPARENT_SPACE:
663 *p_pixel = 0;
664 break;
665 /* Display foreground and background color */
666 /* To make the boxed text "closed captioning" transparent
667 * change true to false.
669 case VBI_OPAQUE:
670 /* alpha blend video into background color */
671 case VBI_SEMI_TRANSPARENT:
672 if( b_opaque )
673 break;
674 /* Full text transparency. only foreground color is show */
675 case VBI_TRANSPARENT_FULL:
676 if( (*p_pixel) == (0xff000000 | p_page->color_map[background] ) )
677 *p_pixel = 0;
678 break;
682 /* end of kludge */
683 return VLC_SUCCESS;
686 /* Callbacks */
687 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
688 vlc_value_t oldval, vlc_value_t newval, void *p_data )
690 decoder_sys_t *p_sys = p_data;
691 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
692 int want_navlink = -1;
694 vlc_mutex_lock( &p_sys->lock );
695 switch( newval.i_int )
697 case ZVBI_KEY_RED:
698 want_navlink = 0;
699 break;
700 case ZVBI_KEY_GREEN:
701 want_navlink = 1;
702 break;
703 case ZVBI_KEY_YELLOW:
704 want_navlink = 2;
705 break;
706 case ZVBI_KEY_BLUE:
707 want_navlink = 3;
708 break;
709 case ZVBI_KEY_INDEX:
710 want_navlink = 5; /* #4 is SKIPPED */
711 break;
714 if (want_navlink > -1)
716 int page = vbi_bcd2dec( p_sys->nav_link[want_navlink].pgno );
717 if (page > 0 && page < 999) {
718 p_sys->i_wanted_page = page;
719 p_sys->i_wanted_subpage = p_sys->nav_link[want_navlink].subno;
722 else if( newval.i_int >= 0 && newval.i_int < 999 )
724 p_sys->i_wanted_page = newval.i_int;
725 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
727 vlc_mutex_unlock( &p_sys->lock );
729 return VLC_SUCCESS;
732 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
733 vlc_value_t oldval, vlc_value_t newval, void *p_data )
735 decoder_sys_t *p_sys = p_data;
736 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
738 vlc_mutex_lock( &p_sys->lock );
739 p_sys->b_opaque = newval.b_bool;
740 p_sys->b_update = true;
741 vlc_mutex_unlock( &p_sys->lock );
743 return VLC_SUCCESS;
746 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
747 vlc_value_t oldval, vlc_value_t newval, void *p_data )
749 decoder_t *p_dec = p_data;
750 decoder_sys_t *p_sys = p_dec->p_sys;
752 VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED( p_this );
754 /* FIXME: Capture + and - key for subpage browsing */
755 if( newval.i_int == '-' || newval.i_int == '+' )
757 vlc_mutex_lock( &p_sys->lock );
758 if( p_sys->i_wanted_subpage == VBI_ANY_SUBNO && newval.i_int == '+' )
759 p_sys->i_wanted_subpage = vbi_dec2bcd(1);
760 else if ( newval.i_int == '+' )
761 p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 1);
762 else if( newval.i_int == '-')
763 p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 0xF9999999); /* BCD complement - 1 */
765 if ( !vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x00 ) || vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x99 ) )
766 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
767 else
768 msg_Info( p_dec, "subpage: %d",
769 vbi_bcd2dec( p_sys->i_wanted_subpage) );
771 p_sys->b_update = true;
772 vlc_mutex_unlock( &p_sys->lock );
775 /* Capture 0-9 for page selection */
776 if( newval.i_int < '0' || newval.i_int > '9' )
777 return VLC_SUCCESS;
779 vlc_mutex_lock( &p_sys->lock );
780 p_sys->i_key[0] = p_sys->i_key[1];
781 p_sys->i_key[1] = p_sys->i_key[2];
782 p_sys->i_key[2] = (int)(newval.i_int - '0');
783 msg_Info( p_dec, "page: %c%c%c", (char)(p_sys->i_key[0]+'0'),
784 (char)(p_sys->i_key[1]+'0'), (char)(p_sys->i_key[2]+'0') );
786 int i_new_page = 0;
788 if( p_sys->i_key[0] > 0 && p_sys->i_key[0] <= 8 &&
789 p_sys->i_key[1] >= 0 && p_sys->i_key[1] <= 9 &&
790 p_sys->i_key[2] >= 0 && p_sys->i_key[2] <= 9 )
792 i_new_page = p_sys->i_key[0]*100 + p_sys->i_key[1]*10 + p_sys->i_key[2];
793 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
795 vlc_mutex_unlock( &p_sys->lock );
797 if( i_new_page > 0 )
798 var_SetInteger( p_dec, "vbi-page", i_new_page );
800 return VLC_SUCCESS;