codec: subsdec: fix variable shadowing
[vlc.git] / modules / codec / zvbi.c
bloba81d668a0fd70600c27f2d1412abcca51ffde702
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 typedef struct
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];
174 } decoder_sys_t;
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, vlc_tick_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 );
195 /*****************************************************************************
196 * Open: probe the decoder and return score
197 *****************************************************************************
198 * Tries to launch a decoder and return score so that the interface is able
199 * to chose.
200 *****************************************************************************/
201 static int Open( vlc_object_t *p_this )
203 decoder_t *p_dec = (decoder_t *) p_this;
204 decoder_sys_t *p_sys = NULL;
206 if( p_dec->fmt_in.i_codec != VLC_CODEC_TELETEXT )
207 return VLC_EGENERIC;
209 int i_page = var_CreateGetInteger( p_dec, "vbi-page" );
210 if( i_page > 999 )
212 msg_Warn( p_dec, "invalid vbi-page requested");
213 i_page = 0;
216 p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) );
217 if( p_sys == NULL )
218 return VLC_ENOMEM;
220 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
221 p_sys->b_update = false;
222 p_sys->p_vbi_dec = vbi_decoder_new();
223 vlc_mutex_init( &p_sys->lock );
225 if( p_sys->p_vbi_dec == NULL )
227 msg_Err( p_dec, "VBI decoder could not be created." );
228 Close( p_this );
229 return VLC_ENOMEM;
232 /* Some broadcasters in countries with level 1 and level 1.5 still not send a G0 to do
233 * matches against table 32 of ETSI 300 706. We try to do some best effort guessing
234 * This is not perfect, but might handle some cases where we know the vbi language
235 * is known. It would be better if people started sending G0 */
236 for( int i = 0; ppsz_default_triplet[i] != NULL; i++ )
238 if( p_dec->fmt_in.psz_language && !strcasecmp( p_dec->fmt_in.psz_language, ppsz_default_triplet[i] ) )
240 vbi_teletext_set_default_region( p_sys->p_vbi_dec, pi_default_triplet[i]);
241 msg_Dbg( p_dec, "overwriting default zvbi region: %d", pi_default_triplet[i] );
245 vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE | VBI_EVENT_NETWORK |
246 #ifdef ZVBI_DEBUG
247 VBI_EVENT_CAPTION | VBI_EVENT_TRIGGER |
248 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_NETWORK_ID |
249 #endif
250 0 , EventHandler, p_dec );
252 /* Create the var on vlc_global. */
253 p_sys->i_wanted_page = i_page;
254 var_AddCallback( p_dec, "vbi-page", RequestPage, p_sys );
256 /* Check if the Teletext track has a known "initial page". */
257 if( p_sys->i_wanted_page == 100 && p_dec->fmt_in.subs.teletext.i_magazine != -1 )
259 p_sys->i_wanted_page = 100 * p_dec->fmt_in.subs.teletext.i_magazine +
260 vbi_bcd2dec( p_dec->fmt_in.subs.teletext.i_page );
261 var_SetInteger( p_dec, "vbi-page", p_sys->i_wanted_page );
263 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
265 p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
266 var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
268 p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
270 p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
272 p_sys->i_level = var_CreateGetInteger( p_dec, "vbi-level" );
274 p_dec->fmt_out.i_codec = p_sys->b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA;
276 p_dec->pf_decode = Decode;
277 return VLC_SUCCESS;
280 /*****************************************************************************
281 * Close:
282 *****************************************************************************/
283 static void Close( vlc_object_t *p_this )
285 decoder_t *p_dec = (decoder_t*) p_this;
286 decoder_sys_t *p_sys = p_dec->p_sys;
288 var_DelCallback( p_dec, "vbi-opaque", Opaque, p_sys );
289 var_DelCallback( p_dec, "vbi-page", RequestPage, p_sys );
291 vlc_mutex_destroy( &p_sys->lock );
293 if( p_sys->p_vbi_dec )
294 vbi_decoder_delete( p_sys->p_vbi_dec );
295 free( p_sys );
298 #ifdef WORDS_BIGENDIAN
299 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
300 #else
301 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
302 #endif
304 /*****************************************************************************
305 * Decode:
306 *****************************************************************************/
307 static int Decode( decoder_t *p_dec, block_t *p_block )
309 decoder_sys_t *p_sys = p_dec->p_sys;
310 subpicture_t *p_spu = NULL;
311 video_format_t fmt;
312 bool b_cached = false;
313 vbi_page p_page;
315 if( p_block == NULL ) /* No Drain */
316 return VLCDEC_SUCCESS;
318 if( p_block->i_buffer > 0 &&
319 ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) ||
320 ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) )
322 vbi_sliced *p_sliced = p_sys->p_vbi_sliced;
323 unsigned int i_lines = 0;
325 p_block->i_buffer--;
326 p_block->p_buffer++;
327 while( p_block->i_buffer >= 2 )
329 int i_id = p_block->p_buffer[0];
330 unsigned i_size = p_block->p_buffer[1];
332 if( 2 + i_size > p_block->i_buffer )
333 break;
335 if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES )
337 if(p_block->p_buffer[3] == 0xE4 ) /* framing_code */
339 unsigned line_offset = p_block->p_buffer[2] & 0x1f;
340 unsigned field_parity = p_block->p_buffer[2] & 0x20;
342 p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B;
343 if( line_offset > 0 )
344 p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313);
345 else
346 p_sliced[i_lines].line = 0;
347 for( int i = 0; i < 42; i++ )
348 p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] );
349 i_lines++;
353 p_block->i_buffer -= 2 + i_size;
354 p_block->p_buffer += 2 + i_size;
357 if( i_lines > 0 )
358 vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 );
361 /* */
362 vlc_mutex_lock( &p_sys->lock );
363 if( p_sys->i_wanted_page == 0 )
365 vlc_mutex_unlock( &p_sys->lock );
366 block_Release( p_block );
367 return VLCDEC_SUCCESS;
369 const int i_align = p_sys->i_align;
370 const unsigned int i_wanted_page = p_sys->i_wanted_page;
371 const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage;
372 const bool b_opaque = p_sys->b_opaque;
373 const unsigned int i_level = p_sys->i_level > 3 ? 3 : p_sys->i_level;
374 vlc_mutex_unlock( &p_sys->lock );
376 /* Try to see if the page we want is in the cache yet */
377 memset( &p_page, 0, sizeof(vbi_page) );
378 b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
379 vbi_dec2bcd( i_wanted_page ),
380 i_wanted_subpage, level_zvbi_values[i_level],
381 25, true );
383 if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update )
384 goto error;
386 if( !b_cached )
388 if( p_sys->b_text && p_sys->i_last_page != i_wanted_page )
390 /* We need to reset the subtitle */
391 p_spu = Subpicture( p_dec, &fmt, true,
392 p_page.columns, p_page.rows,
393 i_align, p_block->i_pts );
394 if( !p_spu )
395 goto error;
396 subtext_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
397 p_spu_sys->region.p_segments = text_segment_New("");
399 p_sys->b_update = true;
400 p_sys->i_last_page = i_wanted_page;
401 goto exit;
403 goto error;
406 p_sys->b_update = false;
407 p_sys->i_last_page = i_wanted_page;
408 #ifdef ZVBI_DEBUG
409 msg_Dbg( p_dec, "we now have page: %d ready for display",
410 i_wanted_page );
411 #endif
413 /* Ignore transparent rows at the beginning and end */
414 int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns );
415 int i_num_rows;
416 if ( i_first_row < 0 ) {
417 i_first_row = p_page.rows - 1;
418 i_num_rows = 0;
419 } else {
420 i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1;
422 #ifdef ZVBI_DEBUG
423 msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i",
424 i_first_row + 1, i_first_row + i_num_rows, p_page.rows );
425 #endif
427 /* If there is a page or sub to render, then we do that here */
428 /* Create the subpicture unit */
429 p_spu = Subpicture( p_dec, &fmt, p_sys->b_text,
430 p_page.columns, i_num_rows,
431 i_align, p_block->i_pts );
432 if( !p_spu )
433 goto error;
435 if( p_sys->b_text )
437 unsigned int i_textsize = 7000;
438 int i_total,offset;
439 char p_text[i_textsize+1];
441 i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
442 "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows );
444 for( offset=1; offset<i_total && isspace( p_text[i_total-offset ] ); offset++)
445 p_text[i_total-offset] = '\0';
447 i_total -= offset;
449 offset=0;
450 while( offset < i_total && isspace( p_text[offset] ) )
451 offset++;
453 subtext_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
454 p_spu_sys->region.p_segments = text_segment_New( &p_text[offset] );
455 if( p_spu_sys->region.p_segments && b_opaque )
457 p_spu_sys->region.p_segments->style = text_style_Create( STYLE_NO_DEFAULTS );
458 if( p_spu_sys->region.p_segments->style )
460 /* Set text background */
461 p_spu_sys->region.p_segments->style->i_style_flags = STYLE_BACKGROUND;
462 p_spu_sys->region.p_segments->style->i_features |= STYLE_HAS_FLAGS;
466 p_spu_sys->region.inner_align = i_align;
467 p_spu_sys->region.flags = UPDT_REGION_IGNORE_BACKGROUND;
469 #ifdef ZVBI_DEBUG
470 msg_Info( p_dec, "page %x-%x(%d)\n\"%s\"", p_page.pgno, p_page.subno, i_total, &p_text[offset] );
471 #endif
473 else
475 picture_t *p_pic = p_spu->p_region->p_picture;
477 /* ZVBI is stupid enough to assume pitch == width */
478 p_pic->p->i_pitch = 4 * fmt.i_width;
480 /* Maintain subtitle postion */
481 p_spu->p_region->i_y = i_first_row*10;
482 p_spu->i_original_picture_width = p_page.columns*12;
483 p_spu->i_original_picture_height = p_page.rows*10;
485 vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32,
486 p_spu->p_region->p_picture->p->p_pixels, -1,
487 0, i_first_row, p_page.columns, i_num_rows,
488 1, 1);
490 vlc_mutex_lock( &p_sys->lock );
491 memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ;
492 vlc_mutex_unlock( &p_sys->lock );
494 OpaquePage( p_pic, &p_page, &fmt, b_opaque, i_first_row * p_page.columns );
497 exit:
498 vbi_unref_page( &p_page );
499 block_Release( p_block );
500 if( p_spu )
501 decoder_QueueSub( p_dec, p_spu );
502 return VLCDEC_SUCCESS;
504 error:
505 vbi_unref_page( &p_page );
506 block_Release( p_block );
507 return VLCDEC_SUCCESS;
510 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
511 bool b_text,
512 int i_columns, int i_rows, int i_align,
513 vlc_tick_t i_pts )
515 video_format_t fmt;
516 subpicture_t *p_spu=NULL;
518 /* If there is a page or sub to render, then we do that here */
519 /* Create the subpicture unit */
520 if( b_text )
521 p_spu = decoder_NewSubpictureText( p_dec );
522 else
523 p_spu = decoder_NewSubpicture( p_dec, NULL );
524 if( !p_spu )
526 msg_Warn( p_dec, "can't get spu buffer" );
527 return NULL;
530 video_format_Init(&fmt, b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA);
531 if( b_text )
533 fmt.i_bits_per_pixel = 0;
535 else
537 fmt.i_width = fmt.i_visible_width = i_columns * 12;
538 fmt.i_height = fmt.i_visible_height = i_rows * 10;
539 fmt.i_bits_per_pixel = 32;
540 fmt.i_sar_num = fmt.i_sar_den = 0; /* let the vout set the correct AR */
542 fmt.i_x_offset = fmt.i_y_offset = 0;
544 p_spu->p_region = subpicture_region_New( &fmt );
545 if( p_spu->p_region == NULL )
547 msg_Err( p_dec, "cannot allocate SPU region" );
548 subpicture_Delete( p_spu );
549 return NULL;
552 p_spu->p_region->i_x = 0;
553 p_spu->p_region->i_y = 0;
555 p_spu->i_start = i_pts;
556 p_spu->i_stop = b_text ? i_pts + VLC_TICK_FROM_SEC(10): 0;
557 p_spu->b_ephemer = true;
558 p_spu->b_absolute = b_text ? false : true;
560 if( !b_text )
561 p_spu->p_region->i_align = i_align;
562 p_spu->i_original_picture_width = fmt.i_width;
563 p_spu->i_original_picture_height = fmt.i_height;
565 /* */
566 *p_fmt = fmt;
567 return p_spu;
570 static void EventHandler( vbi_event *ev, void *user_data )
572 decoder_t *p_dec = (decoder_t *)user_data;
573 decoder_sys_t *p_sys = p_dec->p_sys;
575 if( ev->type == VBI_EVENT_TTX_PAGE )
577 #ifdef ZVBI_DEBUG
578 msg_Info( p_dec, "Page %03x.%02x ",
579 ev->ev.ttx_page.pgno,
580 ev->ev.ttx_page.subno & 0xFF);
581 #endif
582 if( p_sys->i_last_page == vbi_bcd2dec( ev->ev.ttx_page.pgno ) )
583 p_sys->b_update = true;
584 #ifdef ZVBI_DEBUG
585 if( ev->ev.ttx_page.clock_update )
586 msg_Dbg( p_dec, "clock" );
587 if( ev->ev.ttx_page.header_update )
588 msg_Dbg( p_dec, "header" );
589 #endif
591 else if( ev->type == VBI_EVENT_CLOSE )
592 msg_Dbg( p_dec, "Close event" );
593 else if( ev->type == VBI_EVENT_CAPTION )
594 msg_Dbg( p_dec, "Caption line: %x", ev->ev.caption.pgno );
595 else if( ev->type == VBI_EVENT_NETWORK )
597 msg_Dbg( p_dec, "Network change");
598 vbi_network n = ev->ev.network;
599 msg_Dbg( p_dec, "Network id:%d name: %s, call: %s ", n.nuid, n.name, n.call );
601 else if( ev->type == VBI_EVENT_TRIGGER )
602 msg_Dbg( p_dec, "Trigger event" );
603 else if( ev->type == VBI_EVENT_ASPECT )
604 msg_Dbg( p_dec, "Aspect update" );
605 else if( ev->type == VBI_EVENT_PROG_INFO )
606 msg_Dbg( p_dec, "Program info received" );
607 else if( ev->type == VBI_EVENT_NETWORK_ID )
608 msg_Dbg( p_dec, "Network ID changed" );
611 static int get_first_visible_row( vbi_char *p_text, int rows, int columns)
613 for ( int i = 0; i < rows * columns; i++ )
615 if ( p_text[i].opacity != VBI_TRANSPARENT_SPACE )
617 return i / columns;
621 return -1;
624 static int get_last_visible_row( vbi_char *p_text, int rows, int columns)
626 for ( int i = rows * columns - 1; i >= 0; i-- )
628 if (p_text[i].opacity != VBI_TRANSPARENT_SPACE)
630 return i / columns;
634 return -1;
637 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
638 const video_format_t *p_fmt, bool b_opaque, const int text_offset )
640 unsigned int x, y;
642 assert( p_fmt->i_chroma == VLC_CODEC_RGBA );
644 /* Kludge since zvbi doesn't provide an option to specify opacity. */
645 for( y = 0; y < p_fmt->i_height; y++ )
647 for( x = 0; x < p_fmt->i_width; x++ )
649 const vbi_opacity opacity = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].opacity;
650 const int background = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].background;
651 uint32_t *p_pixel = (uint32_t*)&p_src->p->p_pixels[y * p_src->p->i_pitch + 4*x];
653 switch( opacity )
655 /* Show video instead of this character */
656 case VBI_TRANSPARENT_SPACE:
657 *p_pixel = 0;
658 break;
659 /* Display foreground and background color */
660 /* To make the boxed text "closed captioning" transparent
661 * change true to false.
663 case VBI_OPAQUE:
664 /* alpha blend video into background color */
665 case VBI_SEMI_TRANSPARENT:
666 if( b_opaque )
667 break;
668 /* Full text transparency. only foreground color is show */
669 case VBI_TRANSPARENT_FULL:
670 if( (*p_pixel) == (0xff000000 | p_page->color_map[background] ) )
671 *p_pixel = 0;
672 break;
676 /* end of kludge */
677 return VLC_SUCCESS;
680 /* Callbacks */
681 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
682 vlc_value_t oldval, vlc_value_t newval, void *p_data )
684 decoder_sys_t *p_sys = p_data;
685 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
686 int want_navlink = -1;
688 vlc_mutex_lock( &p_sys->lock );
689 switch( newval.i_int )
691 case ZVBI_KEY_RED:
692 want_navlink = 0;
693 break;
694 case ZVBI_KEY_GREEN:
695 want_navlink = 1;
696 break;
697 case ZVBI_KEY_YELLOW:
698 want_navlink = 2;
699 break;
700 case ZVBI_KEY_BLUE:
701 want_navlink = 3;
702 break;
703 case ZVBI_KEY_INDEX:
704 want_navlink = 5; /* #4 is SKIPPED */
705 break;
708 if (want_navlink > -1)
710 int page = vbi_bcd2dec( p_sys->nav_link[want_navlink].pgno );
711 if (page > 0 && page < 999) {
712 p_sys->i_wanted_page = page;
713 p_sys->i_wanted_subpage = p_sys->nav_link[want_navlink].subno;
716 else if( newval.i_int >= 0 && newval.i_int < 999 )
718 p_sys->i_wanted_page = newval.i_int;
719 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
721 vlc_mutex_unlock( &p_sys->lock );
723 return VLC_SUCCESS;
726 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
727 vlc_value_t oldval, vlc_value_t newval, void *p_data )
729 decoder_sys_t *p_sys = p_data;
730 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
732 vlc_mutex_lock( &p_sys->lock );
733 p_sys->b_opaque = newval.b_bool;
734 p_sys->b_update = true;
735 vlc_mutex_unlock( &p_sys->lock );
737 return VLC_SUCCESS;