contrib: ncurses: explicitly set PKG_CONFIG_LIBDIR
[vlc.git] / modules / codec / zvbi.c
blobbd5599fb04b6f8d688316ddad347c7b16e021a2d
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_("Teletext transparency")
66 #define OPAQUE_LONGTEXT N_("Setting vbi-opaque 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 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
80 static const char *const ppsz_pos_descriptions[] =
81 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
82 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
84 vlc_module_begin ()
85 set_description( N_("VBI and Teletext decoder") )
86 set_shortname( N_("VBI & Teletext") )
87 set_capability( "decoder", 51 )
88 set_category( CAT_INPUT )
89 set_subcategory( SUBCAT_INPUT_SCODEC )
90 set_callbacks( Open, Close )
92 add_integer( "vbi-page", 100,
93 PAGE_TEXT, PAGE_LONGTEXT, false )
94 add_bool( "vbi-opaque", false,
95 OPAQUE_TEXT, OPAQUE_LONGTEXT, false )
96 add_integer( "vbi-position", 8, POS_TEXT, POS_LONGTEXT, false )
97 change_integer_list( pi_pos_values, ppsz_pos_descriptions );
98 add_bool( "vbi-text", false,
99 TELX_TEXT, TELX_LONGTEXT, false )
100 vlc_module_end ()
102 /****************************************************************************
103 * Local structures
104 ****************************************************************************/
106 // #define ZVBI_DEBUG
108 //Guessing table for missing "default region triplet"
109 static const int pi_default_triplet[] = {
110 0, 0, 0, 0, // slo slk cze ces
111 8, // pol
112 24,24,24,24,24, //scc scr srp hrv slv
113 24,24, //rum ron
114 32,32,32,32,32, //est lit rus bul ukr
115 48,48, //gre ell
116 64, //ara
117 88, //heb
118 16 }; //default
119 static const char *const ppsz_default_triplet[] = {
120 "slo", "slk", "cze", "ces",
121 "pol",
122 "scc", "scr", "srp", "hrv", "slv",
123 "rum", "ron",
124 "est", "lit", "rus", "bul", "ukr",
125 "gre", "ell",
126 "ara",
127 "heb",
128 NULL
131 typedef enum {
132 ZVBI_KEY_RED = 'r' << 16,
133 ZVBI_KEY_GREEN = 'g' << 16,
134 ZVBI_KEY_YELLOW = 'y' << 16,
135 ZVBI_KEY_BLUE = 'b' << 16,
136 ZVBI_KEY_INDEX = 'i' << 16,
137 } ttxt_key_id;
139 #define MAX_SLICES 32
141 struct decoder_sys_t
143 vbi_decoder * p_vbi_dec;
144 vbi_sliced p_vbi_sliced[MAX_SLICES];
145 unsigned int i_last_page;
146 bool b_update;
147 bool b_text; /* Subtitles as text */
149 vlc_mutex_t lock; /* Lock to protect the following variables */
150 /* Positioning of Teletext images */
151 int i_align;
152 /* */
153 unsigned int i_wanted_page;
154 unsigned int i_wanted_subpage;
155 /* */
156 bool b_opaque;
157 struct {
158 int pgno, subno;
159 } nav_link[6];
160 int i_key[3];
163 static subpicture_t *Decode( decoder_t *, block_t ** );
165 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
166 bool b_text,
167 int i_columns, int i_rows,
168 int i_align, mtime_t i_pts );
170 static void EventHandler( vbi_event *ev, void *user_data );
171 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
172 const video_format_t fmt, bool b_opaque, const int text_offset );
173 static int get_first_visible_row( vbi_char *p_text, int rows, int columns);
174 static int get_last_visible_row( vbi_char *p_text, int rows, int columns);
176 /* Properties callbacks */
177 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
178 vlc_value_t oldval, vlc_value_t newval, void *p_data );
179 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
180 vlc_value_t oldval, vlc_value_t newval, void *p_data );
181 static int Position( vlc_object_t *p_this, char const *psz_cmd,
182 vlc_value_t oldval, vlc_value_t newval, void *p_data );
183 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
184 vlc_value_t oldval, vlc_value_t newval, void *p_data );
186 /*****************************************************************************
187 * Open: probe the decoder and return score
188 *****************************************************************************
189 * Tries to launch a decoder and return score so that the interface is able
190 * to chose.
191 *****************************************************************************/
192 static int Open( vlc_object_t *p_this )
194 decoder_t *p_dec = (decoder_t *) p_this;
195 decoder_sys_t *p_sys = NULL;
197 if( p_dec->fmt_in.i_codec != VLC_CODEC_TELETEXT )
198 return VLC_EGENERIC;
200 p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) );
201 if( p_sys == NULL )
202 return VLC_ENOMEM;
204 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
205 p_sys->b_update = false;
206 p_sys->p_vbi_dec = vbi_decoder_new();
207 vlc_mutex_init( &p_sys->lock );
209 if( p_sys->p_vbi_dec == NULL )
211 msg_Err( p_dec, "VBI decoder could not be created." );
212 Close( p_this );
213 return VLC_ENOMEM;
216 /* Some broadcasters in countries with level 1 and level 1.5 still not send a G0 to do
217 * matches against table 32 of ETSI 300 706. We try to do some best effort guessing
218 * This is not perfect, but might handle some cases where we know the vbi language
219 * is known. It would be better if people started sending G0 */
220 for( int i = 0; ppsz_default_triplet[i] != NULL; i++ )
222 if( p_dec->fmt_in.psz_language && !strcasecmp( p_dec->fmt_in.psz_language, ppsz_default_triplet[i] ) )
224 vbi_teletext_set_default_region( p_sys->p_vbi_dec, pi_default_triplet[i]);
225 msg_Dbg( p_dec, "overwriting default zvbi region: %d", pi_default_triplet[i] );
229 vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE | VBI_EVENT_NETWORK |
230 #ifdef ZVBI_DEBUG
231 VBI_EVENT_CAPTION | VBI_EVENT_TRIGGER |
232 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_NETWORK_ID |
233 #endif
234 0 , EventHandler, p_dec );
236 /* Create the var on vlc_global. */
237 p_sys->i_wanted_page = var_CreateGetInteger( p_dec, "vbi-page" );
238 var_AddCallback( p_dec, "vbi-page", RequestPage, p_sys );
240 /* Check if the Teletext track has a known "initial page". */
241 if( p_sys->i_wanted_page == 100 && p_dec->fmt_in.subs.teletext.i_magazine != -1 )
243 p_sys->i_wanted_page = 100 * p_dec->fmt_in.subs.teletext.i_magazine +
244 vbi_bcd2dec( p_dec->fmt_in.subs.teletext.i_page );
245 var_SetInteger( p_dec, "vbi-page", p_sys->i_wanted_page );
247 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
249 p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
250 var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
252 p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
253 var_AddCallback( p_dec, "vbi-position", Position, p_sys );
255 p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
256 // var_AddCallback( p_dec, "vbi-text", Text, p_sys );
258 /* Listen for keys */
259 var_AddCallback( p_dec->p_libvlc, "key-pressed", EventKey, p_dec );
261 es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_CODEC_SPU );
262 if( p_sys->b_text )
263 p_dec->fmt_out.video.i_chroma = VLC_CODEC_TEXT;
264 else
265 p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBA;
267 p_dec->pf_decode_sub = Decode;
268 return VLC_SUCCESS;
271 /*****************************************************************************
272 * Close:
273 *****************************************************************************/
274 static void Close( vlc_object_t *p_this )
276 decoder_t *p_dec = (decoder_t*) p_this;
277 decoder_sys_t *p_sys = p_dec->p_sys;
279 var_DelCallback( p_dec, "vbi-position", Position, p_sys );
280 var_DelCallback( p_dec, "vbi-opaque", Opaque, p_sys );
281 var_DelCallback( p_dec, "vbi-page", RequestPage, p_sys );
282 var_DelCallback( p_dec->p_libvlc, "key-pressed", EventKey, p_dec );
284 vlc_mutex_destroy( &p_sys->lock );
286 if( p_sys->p_vbi_dec )
287 vbi_decoder_delete( p_sys->p_vbi_dec );
288 free( p_sys );
291 #ifdef WORDS_BIGENDIAN
292 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
293 #else
294 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
295 #endif
297 /*****************************************************************************
298 * Decode:
299 *****************************************************************************/
300 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
302 decoder_sys_t *p_sys = p_dec->p_sys;
303 block_t *p_block;
304 subpicture_t *p_spu = NULL;
305 video_format_t fmt;
306 bool b_cached = false;
307 vbi_page p_page;
309 if( (pp_block == NULL) || (*pp_block == NULL) )
310 return NULL;
312 p_block = *pp_block;
313 *pp_block = NULL;
315 if( p_block->i_buffer > 0 &&
316 ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) ||
317 ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) )
319 vbi_sliced *p_sliced = p_sys->p_vbi_sliced;
320 unsigned int i_lines = 0;
322 p_block->i_buffer--;
323 p_block->p_buffer++;
324 while( p_block->i_buffer >= 2 )
326 int i_id = p_block->p_buffer[0];
327 unsigned i_size = p_block->p_buffer[1];
329 if( 2 + i_size > p_block->i_buffer )
330 break;
332 if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES )
334 unsigned line_offset = p_block->p_buffer[2] & 0x1f;
335 unsigned field_parity = p_block->p_buffer[2] & 0x20;
337 p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B;
338 if( line_offset > 0 )
339 p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313);
340 else
341 p_sliced[i_lines].line = 0;
342 for( int i = 0; i < 42; i++ )
343 p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] );
344 i_lines++;
347 p_block->i_buffer -= 2 + i_size;
348 p_block->p_buffer += 2 + i_size;
351 if( i_lines > 0 )
352 vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 );
355 /* */
356 vlc_mutex_lock( &p_sys->lock );
357 const int i_align = p_sys->i_align;
358 const unsigned int i_wanted_page = p_sys->i_wanted_page;
359 const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage;
360 const bool b_opaque = p_sys->b_opaque;
361 vlc_mutex_unlock( &p_sys->lock );
363 /* Try to see if the page we want is in the cache yet */
364 memset( &p_page, 0, sizeof(vbi_page) );
365 b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
366 vbi_dec2bcd( i_wanted_page ),
367 i_wanted_subpage, VBI_WST_LEVEL_3p5,
368 25, true );
370 if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update )
371 goto error;
373 if( !b_cached )
375 if( p_sys->i_last_page != i_wanted_page )
377 /* We need to reset the subtitle */
378 p_spu = Subpicture( p_dec, &fmt, true,
379 p_page.columns, p_page.rows,
380 i_align, p_block->i_pts );
381 if( !p_spu )
382 goto error;
383 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
384 p_spu_sys->text = strdup("");
386 p_sys->b_update = true;
387 p_sys->i_last_page = i_wanted_page;
388 goto exit;
390 goto error;
393 p_sys->b_update = false;
394 p_sys->i_last_page = i_wanted_page;
395 #ifdef ZVBI_DEBUG
396 msg_Dbg( p_dec, "we now have page: %d ready for display",
397 i_wanted_page );
398 #endif
400 /* Ignore transparent rows at the beginning and end */
401 int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns );
402 int i_num_rows;
403 if ( i_first_row < 0 ) {
404 i_first_row = p_page.rows - 1;
405 i_num_rows = 0;
406 } else {
407 i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1;
409 #ifdef ZVBI_DEBUG
410 msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i",
411 i_first_row + 1, i_first_row + i_num_rows, p_page.rows );
412 #endif
414 /* If there is a page or sub to render, then we do that here */
415 /* Create the subpicture unit */
416 p_spu = Subpicture( p_dec, &fmt, p_sys->b_text,
417 p_page.columns, i_num_rows,
418 i_align, p_block->i_pts );
419 if( !p_spu )
420 goto error;
422 if( p_sys->b_text )
424 unsigned int i_textsize = 7000;
425 int i_total,offset;
426 char p_text[i_textsize+1];
428 i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
429 "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows );
431 for( offset=1; offset<i_total && isspace( p_text[i_total-offset ] ); offset++)
432 p_text[i_total-offset] = '\0';
434 i_total -= offset;
436 offset=0;
437 while( offset < i_total && isspace( p_text[offset] ) )
438 offset++;
440 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
441 p_spu_sys->text = strdup( &p_text[offset] );
443 p_spu_sys->align = i_align;
444 p_spu_sys->i_font_height_percent = 5;
445 p_spu_sys->renderbg = b_opaque;
447 #ifdef ZVBI_DEBUG
448 msg_Info( p_dec, "page %x-%x(%d)\n\"%s\"", p_page.pgno, p_page.subno, i_total, &p_text[offset] );
449 #endif
451 else
453 picture_t *p_pic = p_spu->p_region->p_picture;
455 /* ZVBI is stupid enough to assume pitch == width */
456 p_pic->p->i_pitch = 4 * fmt.i_width;
458 /* Maintain subtitle postion */
459 p_spu->p_region->i_y = i_first_row*10;
460 p_spu->i_original_picture_width = p_page.columns*12;
461 p_spu->i_original_picture_height = p_page.rows*10;
463 vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32,
464 p_spu->p_region->p_picture->p->p_pixels, -1,
465 0, i_first_row, p_page.columns, i_num_rows,
466 1, 1);
468 vlc_mutex_lock( &p_sys->lock );
469 memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ;
470 vlc_mutex_unlock( &p_sys->lock );
472 OpaquePage( p_pic, &p_page, fmt, b_opaque, i_first_row * p_page.columns );
475 exit:
476 vbi_unref_page( &p_page );
477 block_Release( p_block );
478 return p_spu;
480 error:
481 vbi_unref_page( &p_page );
482 if( p_spu != NULL )
484 decoder_DeleteSubpicture( p_dec, p_spu );
485 p_spu = NULL;
488 block_Release( p_block );
489 return NULL;
492 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
493 bool b_text,
494 int i_columns, int i_rows, int i_align,
495 mtime_t i_pts )
497 video_format_t fmt;
498 subpicture_t *p_spu=NULL;
500 /* If there is a page or sub to render, then we do that here */
501 /* Create the subpicture unit */
502 if( b_text )
503 p_spu = decoder_NewSubpictureText( p_dec );
504 else
505 p_spu = decoder_NewSubpicture( p_dec, NULL );
506 if( !p_spu )
508 msg_Warn( p_dec, "can't get spu buffer" );
509 return NULL;
512 memset( &fmt, 0, sizeof(video_format_t) );
513 fmt.i_chroma = b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA;
514 fmt.i_sar_num = 0;
515 fmt.i_sar_den = 1;
516 if( b_text )
518 fmt.i_bits_per_pixel = 0;
520 else
522 fmt.i_width = fmt.i_visible_width = i_columns * 12;
523 fmt.i_height = fmt.i_visible_height = i_rows * 10;
524 fmt.i_bits_per_pixel = 32;
526 fmt.i_x_offset = fmt.i_y_offset = 0;
528 p_spu->p_region = subpicture_region_New( &fmt );
529 if( p_spu->p_region == NULL )
531 msg_Err( p_dec, "cannot allocate SPU region" );
532 decoder_DeleteSubpicture( p_dec, p_spu );
533 return NULL;
536 p_spu->p_region->i_x = 0;
537 p_spu->p_region->i_y = 0;
539 p_spu->i_start = i_pts;
540 p_spu->i_stop = b_text ? i_pts + (10*CLOCK_FREQ): 0;
541 p_spu->b_ephemer = true;
542 p_spu->b_absolute = b_text ? false : true;
544 if( !b_text )
545 p_spu->p_region->i_align = i_align;
546 p_spu->i_original_picture_width = fmt.i_width;
547 p_spu->i_original_picture_height = fmt.i_height;
549 /* */
550 *p_fmt = fmt;
551 return p_spu;
554 static void EventHandler( vbi_event *ev, void *user_data )
556 decoder_t *p_dec = (decoder_t *)user_data;
557 decoder_sys_t *p_sys = p_dec->p_sys;
559 if( ev->type == VBI_EVENT_TTX_PAGE )
561 #ifdef ZVBI_DEBUG
562 msg_Info( p_dec, "Page %03x.%02x ",
563 ev->ev.ttx_page.pgno,
564 ev->ev.ttx_page.subno & 0xFF);
565 #endif
566 if( p_sys->i_last_page == vbi_bcd2dec( ev->ev.ttx_page.pgno ) )
567 p_sys->b_update = true;
568 #ifdef ZVBI_DEBUG
569 if( ev->ev.ttx_page.clock_update )
570 msg_Dbg( p_dec, "clock" );
571 if( ev->ev.ttx_page.header_update )
572 msg_Dbg( p_dec, "header" );
573 #endif
575 else if( ev->type == VBI_EVENT_CLOSE )
576 msg_Dbg( p_dec, "Close event" );
577 else if( ev->type == VBI_EVENT_CAPTION )
578 msg_Dbg( p_dec, "Caption line: %x", ev->ev.caption.pgno );
579 else if( ev->type == VBI_EVENT_NETWORK )
581 msg_Dbg( p_dec, "Network change");
582 vbi_network n = ev->ev.network;
583 msg_Dbg( p_dec, "Network id:%d name: %s, call: %s ", n.nuid, n.name, n.call );
585 else if( ev->type == VBI_EVENT_TRIGGER )
586 msg_Dbg( p_dec, "Trigger event" );
587 else if( ev->type == VBI_EVENT_ASPECT )
588 msg_Dbg( p_dec, "Aspect update" );
589 else if( ev->type == VBI_EVENT_PROG_INFO )
590 msg_Dbg( p_dec, "Program info received" );
591 else if( ev->type == VBI_EVENT_NETWORK_ID )
592 msg_Dbg( p_dec, "Network ID changed" );
595 static int get_first_visible_row( vbi_char *p_text, int rows, int columns)
597 for ( int i = 0; i < rows * columns; i++ )
599 if ( p_text[i].opacity != VBI_TRANSPARENT_SPACE )
601 return i / columns;
605 return -1;
608 static int get_last_visible_row( vbi_char *p_text, int rows, int columns)
610 for ( int i = rows * columns - 1; i >= 0; i-- )
612 if (p_text[i].opacity != VBI_TRANSPARENT_SPACE)
614 return ( i + columns - 1) / columns;
618 return -1;
621 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
622 const video_format_t fmt, bool b_opaque, const int text_offset )
624 unsigned int x, y;
626 assert( fmt.i_chroma == VLC_CODEC_RGBA );
628 /* Kludge since zvbi doesn't provide an option to specify opacity. */
629 for( y = 0; y < fmt.i_height; y++ )
631 for( x = 0; x < fmt.i_width; x++ )
633 const vbi_opacity opacity = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].opacity;
634 const int background = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].background;
635 uint32_t *p_pixel = (uint32_t*)&p_src->p->p_pixels[y * p_src->p->i_pitch + 4*x];
637 switch( opacity )
639 /* Show video instead of this character */
640 case VBI_TRANSPARENT_SPACE:
641 *p_pixel = 0;
642 break;
643 /* Display foreground and background color */
644 /* To make the boxed text "closed captioning" transparent
645 * change true to false.
647 case VBI_OPAQUE:
648 /* alpha blend video into background color */
649 case VBI_SEMI_TRANSPARENT:
650 if( b_opaque )
651 break;
652 /* Full text transparency. only foreground color is show */
653 case VBI_TRANSPARENT_FULL:
654 if( (*p_pixel) == (0xff000000 | p_page->color_map[background] ) )
655 *p_pixel = 0;
656 break;
660 /* end of kludge */
661 return VLC_SUCCESS;
664 /* Callbacks */
665 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
666 vlc_value_t oldval, vlc_value_t newval, void *p_data )
668 decoder_sys_t *p_sys = p_data;
669 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
671 vlc_mutex_lock( &p_sys->lock );
672 switch( newval.i_int )
674 case ZVBI_KEY_RED:
675 p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[0].pgno );
676 p_sys->i_wanted_subpage = p_sys->nav_link[0].subno;
677 break;
678 case ZVBI_KEY_GREEN:
679 p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[1].pgno );
680 p_sys->i_wanted_subpage = p_sys->nav_link[1].subno;
681 break;
682 case ZVBI_KEY_YELLOW:
683 p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[2].pgno );
684 p_sys->i_wanted_subpage = p_sys->nav_link[2].subno;
685 break;
686 case ZVBI_KEY_BLUE:
687 p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[3].pgno );
688 p_sys->i_wanted_subpage = p_sys->nav_link[3].subno;
689 break;
690 case ZVBI_KEY_INDEX:
691 p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[5].pgno ); /* #4 is SKIPPED */
692 p_sys->i_wanted_subpage = p_sys->nav_link[5].subno;
693 break;
695 if( newval.i_int > 0 && newval.i_int < 999 )
697 p_sys->i_wanted_page = newval.i_int;
698 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
700 vlc_mutex_unlock( &p_sys->lock );
702 return VLC_SUCCESS;
705 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
706 vlc_value_t oldval, vlc_value_t newval, void *p_data )
708 decoder_sys_t *p_sys = p_data;
709 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
711 vlc_mutex_lock( &p_sys->lock );
712 p_sys->b_opaque = newval.b_bool;
713 p_sys->b_update = true;
714 vlc_mutex_unlock( &p_sys->lock );
716 return VLC_SUCCESS;
719 static int Position( vlc_object_t *p_this, char const *psz_cmd,
720 vlc_value_t oldval, vlc_value_t newval, void *p_data )
722 decoder_sys_t *p_sys = p_data;
723 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
725 vlc_mutex_lock( &p_sys->lock );
726 p_sys->i_align = newval.i_int;
727 vlc_mutex_unlock( &p_sys->lock );
729 return VLC_SUCCESS;
732 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
733 vlc_value_t oldval, vlc_value_t newval, void *p_data )
735 decoder_t *p_dec = p_data;
736 decoder_sys_t *p_sys = p_dec->p_sys;
738 VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED( p_this );
740 /* FIXME: Capture + and - key for subpage browsing */
741 if( newval.i_int == '-' || newval.i_int == '+' )
743 vlc_mutex_lock( &p_sys->lock );
744 if( p_sys->i_wanted_subpage == VBI_ANY_SUBNO && newval.i_int == '+' )
745 p_sys->i_wanted_subpage = vbi_dec2bcd(1);
746 else if ( newval.i_int == '+' )
747 p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 1);
748 else if( newval.i_int == '-')
749 p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 0xF9999999); /* BCD complement - 1 */
751 if ( !vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x00 ) || vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x99 ) )
752 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
753 else
754 msg_Info( p_dec, "subpage: %d",
755 vbi_bcd2dec( p_sys->i_wanted_subpage) );
757 p_sys->b_update = true;
758 vlc_mutex_unlock( &p_sys->lock );
761 /* Capture 0-9 for page selection */
762 if( newval.i_int < '0' || newval.i_int > '9' )
763 return VLC_SUCCESS;
765 vlc_mutex_lock( &p_sys->lock );
766 p_sys->i_key[0] = p_sys->i_key[1];
767 p_sys->i_key[1] = p_sys->i_key[2];
768 p_sys->i_key[2] = (int)(newval.i_int - '0');
769 msg_Info( p_dec, "page: %c%c%c", (char)(p_sys->i_key[0]+'0'),
770 (char)(p_sys->i_key[1]+'0'), (char)(p_sys->i_key[2]+'0') );
772 int i_new_page = 0;
774 if( p_sys->i_key[0] > 0 && p_sys->i_key[0] <= 8 &&
775 p_sys->i_key[1] >= 0 && p_sys->i_key[1] <= 9 &&
776 p_sys->i_key[2] >= 0 && p_sys->i_key[2] <= 9 )
778 i_new_page = p_sys->i_key[0]*100 + p_sys->i_key[1]*10 + p_sys->i_key[2];
779 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
781 vlc_mutex_unlock( &p_sys->lock );
783 if( i_new_page > 0 )
784 var_SetInteger( p_dec, "vbi-page", i_new_page );
786 return VLC_SUCCESS;