]> git.sesse.net Git - vlc/blob - modules/codec/zvbi.c
dtv: fix ISDB-S tuning
[vlc] / modules / codec / zvbi.c
1 /*****************************************************************************
2  * zvbi.c : VBI and Teletext PES demux and decoder using libzvbi
3  *****************************************************************************
4  * Copyright (C) 2007, M2X
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <djhartman at m2x dot nl>
8  *          Jean-Paul Saman <jpsaman at m2x dot nl>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  *
27  * information on teletext format can be found here :
28  * http://pdc.ro.nu/teletext.html
29  *
30  *****************************************************************************/
31
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)
39  */
40
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
44
45 #include <vlc_common.h>
46 #include <vlc_plugin.h>
47 #include <assert.h>
48 #include <libzvbi.h>
49
50 #include <vlc_codec.h>
51
52 /*****************************************************************************
53  * Module descriptor.
54  *****************************************************************************/
55 static int  Open ( vlc_object_t * );
56 static void Close( vlc_object_t * );
57
58 #define PAGE_TEXT N_("Teletext page")
59 #define PAGE_LONGTEXT N_("Open the indicated Teletext page." \
60         "Default page is index 100")
61
62 #define OPAQUE_TEXT N_("Teletext transparency")
63 #define OPAQUE_LONGTEXT N_("Setting vbi-opaque to false " \
64         "makes the boxed text transparent." )
65
66 #define POS_TEXT N_("Teletext alignment")
67 #define POS_LONGTEXT N_( \
68   "You can enforce the teletext position on the video " \
69   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
70   "also use combinations of these values, eg. 6 = top-right).")
71
72 #define TELX_TEXT N_("Teletext text subtitles")
73 #define TELX_LONGTEXT N_( "Output teletext subtitles as text " \
74   "instead of as RGBA" )
75
76 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
77 static const char *const ppsz_pos_descriptions[] =
78 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
79   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
80
81 vlc_module_begin ()
82     set_description( N_("VBI and Teletext decoder") )
83     set_shortname( N_("VBI & Teletext") )
84     set_capability( "decoder", 51 )
85     set_category( CAT_INPUT )
86     set_subcategory( SUBCAT_INPUT_SCODEC )
87     set_callbacks( Open, Close )
88
89     add_integer( "vbi-page", 100,
90                  PAGE_TEXT, PAGE_LONGTEXT, false )
91     add_bool( "vbi-opaque", true,
92                  OPAQUE_TEXT, OPAQUE_LONGTEXT, false )
93     add_integer( "vbi-position", 4, POS_TEXT, POS_LONGTEXT, false )
94         change_integer_list( pi_pos_values, ppsz_pos_descriptions );
95     add_bool( "vbi-text", false,
96               TELX_TEXT, TELX_LONGTEXT, false )
97 vlc_module_end ()
98
99 /****************************************************************************
100  * Local structures
101  ****************************************************************************/
102
103 // #define ZVBI_DEBUG
104
105 //Guessing table for missing "default region triplet"
106 static const int pi_default_triplet[] = {
107  0, 0, 0, 0,     // slo slk cze ces
108  8,              // pol
109  24,24,24,24,24, //scc scr srp hrv slv
110  24,24,          //rum ron
111  32,32,32,32,32, //est lit rus bul ukr
112  48,48,          //gre ell
113  64,             //ara
114  88,             //heb
115  16 };           //default
116 static const char *const ppsz_default_triplet[] = {
117  "slo", "slk", "cze", "ces",
118  "pol",
119  "scc", "scr", "srp", "hrv", "slv",
120  "rum", "ron",
121  "est", "lit", "rus", "bul", "ukr",
122  "gre", "ell",
123  "ara",
124  "heb",
125  NULL
126 };
127
128 typedef enum {
129     ZVBI_KEY_RED    = 'r' << 16,
130     ZVBI_KEY_GREEN  = 'g' << 16,
131     ZVBI_KEY_YELLOW = 'y' << 16,
132     ZVBI_KEY_BLUE   = 'b' << 16,
133     ZVBI_KEY_INDEX  = 'i' << 16,
134 } ttxt_key_id;
135
136 typedef enum {
137     DATA_UNIT_EBU_TELETEXT_NON_SUBTITLE     = 0x02,
138     DATA_UNIT_EBU_TELETEXT_SUBTITLE         = 0x03,
139     DATA_UNIT_EBU_TELETEXT_INVERTED         = 0x0C,
140
141     DATA_UNIT_ZVBI_WSS_CPR1204              = 0xB4,
142     DATA_UNIT_ZVBI_CLOSED_CAPTION_525       = 0xB5,
143     DATA_UNIT_ZVBI_MONOCHROME_SAMPLES_525   = 0xB6,
144
145     DATA_UNIT_VPS                           = 0xC3,
146     DATA_UNIT_WSS                           = 0xC4,
147     DATA_UNIT_CLOSED_CAPTION                = 0xC5,
148     DATA_UNIT_MONOCHROME_SAMPLES            = 0xC6,
149
150     DATA_UNIT_STUFFING                      = 0xFF,
151 } data_unit_id;
152
153 #define MAX_SLICES 32
154
155 struct decoder_sys_t
156 {
157     vbi_decoder *     p_vbi_dec;
158     vbi_sliced        p_vbi_sliced[MAX_SLICES];
159     unsigned int      i_last_page;
160     bool              b_update;
161     bool              b_text;   /* Subtitles as text */
162
163     vlc_mutex_t       lock; /* Lock to protect the following variables */
164     /* Positioning of Teletext images */
165     int               i_align;
166     /* */
167     unsigned int      i_wanted_page;
168     unsigned int      i_wanted_subpage;
169     /* */
170     bool              b_opaque;
171     struct {
172         int pgno, subno;
173     }                 nav_link[6];
174     int               i_key[3];
175 };
176
177 static subpicture_t *Decode( decoder_t *, block_t ** );
178
179 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
180                                  bool b_text,
181                                  int i_columns, int i_rows,
182                                  int i_align, mtime_t i_pts );
183
184 static void EventHandler( vbi_event *ev, void *user_data );
185 static int OpaquePage( picture_t *p_src, const vbi_page p_page,
186                        const video_format_t fmt, bool b_opaque );
187
188 /* Properties callbacks */
189 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
190                         vlc_value_t oldval, vlc_value_t newval, void *p_data );
191 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
192                    vlc_value_t oldval, vlc_value_t newval, void *p_data );
193 static int Position( vlc_object_t *p_this, char const *psz_cmd,
194                      vlc_value_t oldval, vlc_value_t newval, void *p_data );
195 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
196                      vlc_value_t oldval, vlc_value_t newval, void *p_data );
197
198 /*****************************************************************************
199  * Open: probe the decoder and return score
200  *****************************************************************************
201  * Tries to launch a decoder and return score so that the interface is able
202  * to chose.
203  *****************************************************************************/
204 static int Open( vlc_object_t *p_this )
205 {
206     decoder_t     *p_dec = (decoder_t *) p_this;
207     decoder_sys_t *p_sys = NULL;
208
209     if( p_dec->fmt_in.i_codec != VLC_CODEC_TELETEXT )
210         return VLC_EGENERIC;
211
212     p_dec->pf_decode_sub = Decode;
213     p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) );
214     if( p_sys == NULL )
215         return VLC_ENOMEM;
216
217     p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
218     p_sys->b_update = false;
219     p_sys->p_vbi_dec = vbi_decoder_new();
220     vlc_mutex_init( &p_sys->lock );
221
222     if( p_sys->p_vbi_dec == NULL )
223     {
224         msg_Err( p_dec, "VBI decoder could not be created." );
225         Close( p_this );
226         return VLC_ENOMEM;
227     }
228
229     /* Some broadcasters in countries with level 1 and level 1.5 still not send a G0 to do 
230      * matches against table 32 of ETSI 300 706. We try to do some best effort guessing
231      * This is not perfect, but might handle some cases where we know the vbi language 
232      * is known. It would be better if people started sending G0 */
233     for( int i = 0; ppsz_default_triplet[i] != NULL; i++ )
234     {
235         if( p_dec->fmt_in.psz_language && !strcasecmp( p_dec->fmt_in.psz_language, ppsz_default_triplet[i] ) )
236         {
237             vbi_teletext_set_default_region( p_sys->p_vbi_dec, pi_default_triplet[i]);
238             msg_Dbg( p_dec, "overwriting default zvbi region: %d", pi_default_triplet[i] );
239         }
240     }
241
242     vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE | VBI_EVENT_NETWORK |
243 #ifdef ZVBI_DEBUG
244                                 VBI_EVENT_CAPTION | VBI_EVENT_TRIGGER |
245                                 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_NETWORK_ID |
246 #endif
247                                 0 , EventHandler, p_dec );
248
249     /* Create the var on vlc_global. */
250     p_sys->i_wanted_page = var_CreateGetInteger( p_dec, "vbi-page" );
251     var_AddCallback( p_dec, "vbi-page", RequestPage, p_sys );
252
253     /* Check if the Teletext track has a known "initial page". */
254     if( p_sys->i_wanted_page == 100 && p_dec->fmt_in.subs.teletext.i_magazine != -1 )
255     {
256         p_sys->i_wanted_page = 100 * p_dec->fmt_in.subs.teletext.i_magazine +
257                                vbi_bcd2dec( p_dec->fmt_in.subs.teletext.i_page );
258         var_SetInteger( p_dec, "vbi-page", p_sys->i_wanted_page );
259     }
260     p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
261
262     p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
263     var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
264
265     p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
266     var_AddCallback( p_dec, "vbi-position", Position, p_sys );
267
268     p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
269 //    var_AddCallback( p_dec, "vbi-text", Text, p_sys );
270
271     /* Listen for keys */
272     var_AddCallback( p_dec->p_libvlc, "key-pressed", EventKey, p_dec );
273
274     es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_CODEC_SPU );
275     if( p_sys->b_text )
276         p_dec->fmt_out.video.i_chroma = VLC_CODEC_TEXT;
277     else
278         p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBA;
279     return VLC_SUCCESS;
280 }
281
282 /*****************************************************************************
283  * Close:
284  *****************************************************************************/
285 static void Close( vlc_object_t *p_this )
286 {
287     decoder_t     *p_dec = (decoder_t*) p_this;
288     decoder_sys_t *p_sys = p_dec->p_sys;
289
290     var_DelCallback( p_dec, "vbi-position", Position, p_sys );
291     var_DelCallback( p_dec, "vbi-opaque", Opaque, p_sys );
292     var_DelCallback( p_dec, "vbi-page", RequestPage, p_sys );
293     var_DelCallback( p_dec->p_libvlc, "key-pressed", EventKey, p_dec );
294
295     vlc_mutex_destroy( &p_sys->lock );
296
297     if( p_sys->p_vbi_dec )
298         vbi_decoder_delete( p_sys->p_vbi_dec );
299     free( p_sys );
300 }
301
302 #ifdef WORDS_BIGENDIAN
303 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
304 #else
305 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
306 #endif
307
308 /*****************************************************************************
309  * Decode:
310  *****************************************************************************/
311 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
312 {
313     decoder_sys_t   *p_sys = p_dec->p_sys;
314     block_t         *p_block;
315     subpicture_t    *p_spu = NULL;
316     video_format_t  fmt;
317     bool            b_cached = false;
318     vbi_page        p_page;
319
320     if( (pp_block == NULL) || (*pp_block == NULL) )
321         return NULL;
322
323     p_block = *pp_block;
324     *pp_block = NULL;
325
326     if( p_block->i_buffer > 0 &&
327         ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) ||
328           ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) )
329     {
330         vbi_sliced   *p_sliced = p_sys->p_vbi_sliced;
331         unsigned int i_lines = 0;
332
333         p_block->i_buffer--;
334         p_block->p_buffer++;
335         while( p_block->i_buffer >= 2 )
336         {
337             int      i_id   = p_block->p_buffer[0];
338             unsigned i_size = p_block->p_buffer[1];
339
340             if( 2 + i_size > p_block->i_buffer )
341                 break;
342
343             if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES )
344             {
345                 unsigned line_offset  = p_block->p_buffer[2] & 0x1f;
346                 unsigned field_parity = p_block->p_buffer[2] & 0x20;
347
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++;
356             }
357
358             p_block->i_buffer -= 2 + i_size;
359             p_block->p_buffer += 2 + i_size;
360         }
361
362         if( i_lines > 0 )
363             vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, (double)p_block->i_pts / 1000000 );
364     }
365
366     /* */
367     vlc_mutex_lock( &p_sys->lock );
368     const int i_align = p_sys->i_align;
369     const unsigned int i_wanted_page = p_sys->i_wanted_page;
370     const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage;
371     const bool b_opaque = p_sys->b_opaque;
372     vlc_mutex_unlock( &p_sys->lock );
373
374     /* Try to see if the page we want is in the cache yet */
375     memset( &p_page, 0, sizeof(vbi_page) );
376     b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
377                                   vbi_dec2bcd( i_wanted_page ),
378                                   i_wanted_subpage, VBI_WST_LEVEL_3p5,
379                                   25, true );
380
381     if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update )
382         goto error;
383
384     if( !b_cached )
385     {
386         if( p_sys->i_last_page != i_wanted_page )
387         {
388             /* We need to reset the subtitle */
389             p_spu = Subpicture( p_dec, &fmt, true,
390                                 p_page.columns, p_page.rows,
391                                 i_align, p_block->i_pts );
392             if( !p_spu )
393                 goto error;
394             p_spu->p_region->psz_text = strdup("");
395
396             p_sys->b_update = true;
397             p_sys->i_last_page = i_wanted_page;
398             goto exit;
399         }
400         goto error;
401     }
402
403     p_sys->b_update = false;
404     p_sys->i_last_page = i_wanted_page;
405 #ifdef ZVBI_DEBUG
406     msg_Dbg( p_dec, "we now have page: %d ready for display",
407              i_wanted_page );
408 #endif
409     /* If there is a page or sub to render, then we do that here */
410     /* Create the subpicture unit */
411     p_spu = Subpicture( p_dec, &fmt, p_sys->b_text,
412                         p_page.columns, p_page.rows,
413                         i_align, p_block->i_pts );
414     if( !p_spu )
415         goto error;
416
417     if( p_sys->b_text )
418     {
419         unsigned int i_textsize = 7000;
420         int i_total;
421         char p_text[i_textsize+1];
422
423         i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
424                         "UTF-8", 0, 0, 0, 0, p_page.columns, p_page.rows );
425         p_text[i_total] = '\0';
426         /* Strip off the pagenumber */
427         if( i_total <= 40 )
428             goto error;
429         p_spu->p_region->psz_text = strdup( &p_text[8] );
430
431 #ifdef ZVBI_DEBUG
432         msg_Info( p_dec, "page %x-%x(%d)\n%s", p_page.pgno, p_page.subno, i_total, p_text );
433 #endif
434     }
435     else
436     {
437         picture_t *p_pic = p_spu->p_region->p_picture;
438
439         /* ZVBI is stupid enough to assume pitch == width */
440         p_pic->p->i_pitch = 4 * fmt.i_width;
441         vbi_draw_vt_page( &p_page, ZVBI_PIXFMT_RGBA32,
442                           p_spu->p_region->p_picture->p->p_pixels, 1, 1 );
443
444         vlc_mutex_lock( &p_sys->lock );
445         memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ;
446         vlc_mutex_unlock( &p_sys->lock );
447
448         OpaquePage( p_pic, p_page, fmt, b_opaque );
449     }
450
451 exit:
452     vbi_unref_page( &p_page );
453     block_Release( p_block );
454     return p_spu;
455
456 error:
457     vbi_unref_page( &p_page );
458     if( p_spu != NULL )
459     {
460         decoder_DeleteSubpicture( p_dec, p_spu );
461         p_spu = NULL;
462     }
463
464     block_Release( p_block );
465     return NULL;
466 }
467
468 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
469                                  bool b_text,
470                                  int i_columns, int i_rows, int i_align,
471                                  mtime_t i_pts )
472 {
473     video_format_t fmt;
474     subpicture_t *p_spu;
475
476     /* If there is a page or sub to render, then we do that here */
477     /* Create the subpicture unit */
478     p_spu = decoder_NewSubpicture( p_dec, NULL );
479     if( !p_spu )
480     {
481         msg_Warn( p_dec, "can't get spu buffer" );
482         return NULL;
483     }
484
485     memset( &fmt, 0, sizeof(video_format_t) );
486     fmt.i_chroma = b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA;
487     fmt.i_sar_num = 0;
488     fmt.i_sar_den = 1;
489     if( b_text )
490     {
491         fmt.i_bits_per_pixel = 0;
492     }
493     else
494     {
495         fmt.i_width = fmt.i_visible_width = i_columns * 12;
496         fmt.i_height = fmt.i_visible_height = i_rows * 10;
497         fmt.i_bits_per_pixel = 32;
498     }
499     fmt.i_x_offset = fmt.i_y_offset = 0;
500
501     p_spu->p_region = subpicture_region_New( &fmt );
502     if( p_spu->p_region == NULL )
503     {
504         msg_Err( p_dec, "cannot allocate SPU region" );
505         decoder_DeleteSubpicture( p_dec, p_spu );
506         return NULL;
507     }
508
509     p_spu->p_region->i_x = 0;
510     p_spu->p_region->i_y = 0;
511     p_spu->p_region->i_align = i_align;
512
513     p_spu->i_start = i_pts;
514     p_spu->i_stop = 0;
515     p_spu->b_ephemer = true;
516     p_spu->b_absolute = false;
517
518     if( !b_text )
519     {
520         p_spu->i_original_picture_width = fmt.i_width;
521         p_spu->i_original_picture_height = fmt.i_height;
522     }
523
524     /* */
525     *p_fmt = fmt;
526     return p_spu;
527 }
528
529 static void EventHandler( vbi_event *ev, void *user_data )
530 {
531     decoder_t *p_dec        = (decoder_t *)user_data;
532     decoder_sys_t *p_sys    = p_dec->p_sys;
533
534     if( ev->type == VBI_EVENT_TTX_PAGE )
535     {
536 #ifdef ZVBI_DEBUG
537         msg_Info( p_dec, "Page %03x.%02x ",
538                     ev->ev.ttx_page.pgno,
539                     ev->ev.ttx_page.subno & 0xFF);
540 #endif
541         if( p_sys->i_last_page == vbi_bcd2dec( ev->ev.ttx_page.pgno ) )
542             p_sys->b_update = true;
543 #ifdef ZVBI_DEBUG
544         if( ev->ev.ttx_page.clock_update )
545             msg_Dbg( p_dec, "clock" );
546         if( ev->ev.ttx_page.header_update )
547             msg_Dbg( p_dec, "header" );
548 #endif
549     }
550     else if( ev->type == VBI_EVENT_CLOSE )
551         msg_Dbg( p_dec, "Close event" );
552     else if( ev->type == VBI_EVENT_CAPTION )
553         msg_Dbg( p_dec, "Caption line: %x", ev->ev.caption.pgno );
554     else if( ev->type == VBI_EVENT_NETWORK )
555     {
556         msg_Dbg( p_dec, "Network change");
557         vbi_network n = ev->ev.network;
558         msg_Dbg( p_dec, "Network id:%d name: %s, call: %s ", n.nuid, n.name, n.call );
559     }
560     else if( ev->type == VBI_EVENT_TRIGGER )
561         msg_Dbg( p_dec, "Trigger event" );
562     else if( ev->type == VBI_EVENT_ASPECT )
563         msg_Dbg( p_dec, "Aspect update" );
564     else if( ev->type == VBI_EVENT_PROG_INFO )
565         msg_Dbg( p_dec, "Program info received" );
566     else if( ev->type == VBI_EVENT_NETWORK_ID )
567         msg_Dbg( p_dec, "Network ID changed" );
568 }
569
570 static int OpaquePage( picture_t *p_src, const vbi_page p_page,
571                        const video_format_t fmt, bool b_opaque )
572 {
573     unsigned int    x, y;
574
575     assert( fmt.i_chroma == VLC_CODEC_RGBA );
576
577     /* Kludge since zvbi doesn't provide an option to specify opacity. */
578     for( y = 0; y < fmt.i_height; y++ )
579     {
580         for( x = 0; x < fmt.i_width; x++ )
581         {
582             const vbi_opacity opacity = p_page.text[ y/10 * p_page.columns + x/12 ].opacity;
583             const int background = p_page.text[ y/10 * p_page.columns + x/12 ].background;
584             uint32_t *p_pixel = (uint32_t*)&p_src->p->p_pixels[y * p_src->p->i_pitch + 4*x];
585
586             switch( opacity )
587             {
588             /* Show video instead of this character */
589             case VBI_TRANSPARENT_SPACE:
590                 *p_pixel = 0;
591                 break;
592             /* Display foreground and background color */
593             /* To make the boxed text "closed captioning" transparent
594              * change true to false.
595              */
596             case VBI_OPAQUE:
597             /* alpha blend video into background color */
598             case VBI_SEMI_TRANSPARENT:
599                 if( b_opaque )
600                     break;
601             /* Full text transparency. only foreground color is show */
602             case VBI_TRANSPARENT_FULL:
603                 if( (*p_pixel) == (0xff000000 | p_page.color_map[background] ) )
604                     *p_pixel = 0;
605                 break;
606             }
607         }
608     }
609     /* end of kludge */
610     return VLC_SUCCESS;
611 }
612
613 /* Callbacks */
614 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
615                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
616 {
617     decoder_sys_t *p_sys = p_data;
618     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
619
620     vlc_mutex_lock( &p_sys->lock );
621     switch( newval.i_int )
622     {
623         case ZVBI_KEY_RED:
624             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[0].pgno );
625             p_sys->i_wanted_subpage = p_sys->nav_link[0].subno;
626             break;
627         case ZVBI_KEY_GREEN:
628             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[1].pgno );
629             p_sys->i_wanted_subpage = p_sys->nav_link[1].subno;
630             break;
631         case ZVBI_KEY_YELLOW:
632             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[2].pgno );
633             p_sys->i_wanted_subpage = p_sys->nav_link[2].subno;
634             break;
635         case ZVBI_KEY_BLUE:
636             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[3].pgno );
637             p_sys->i_wanted_subpage = p_sys->nav_link[3].subno;
638             break;
639         case ZVBI_KEY_INDEX:
640             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[5].pgno ); /* #4 is SKIPPED */
641             p_sys->i_wanted_subpage = p_sys->nav_link[5].subno;
642             break;
643     }
644     if( newval.i_int > 0 && newval.i_int < 999 )
645     {
646         p_sys->i_wanted_page = newval.i_int;
647         p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
648     }
649     vlc_mutex_unlock( &p_sys->lock );
650
651     return VLC_SUCCESS;
652 }
653
654 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
655                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
656 {
657     decoder_sys_t *p_sys = p_data;
658     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
659
660     vlc_mutex_lock( &p_sys->lock );
661     p_sys->b_opaque = newval.b_bool;
662     p_sys->b_update = true;
663     vlc_mutex_unlock( &p_sys->lock );
664
665     return VLC_SUCCESS;
666 }
667
668 static int Position( vlc_object_t *p_this, char const *psz_cmd,
669                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
670 {
671     decoder_sys_t *p_sys = p_data;
672     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
673
674     vlc_mutex_lock( &p_sys->lock );
675     p_sys->i_align = newval.i_int;
676     vlc_mutex_unlock( &p_sys->lock );
677
678     return VLC_SUCCESS;
679 }
680
681 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
682                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
683 {
684     decoder_t *p_dec = p_data;
685     decoder_sys_t *p_sys = p_dec->p_sys;
686
687     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED( p_this );
688
689     /* FIXME: Capture + and - key for subpage browsing */
690     if( newval.i_int == '-' || newval.i_int == '+' )
691     {
692         vlc_mutex_lock( &p_sys->lock );
693         if( p_sys->i_wanted_subpage == VBI_ANY_SUBNO && newval.i_int == '+' )
694             p_sys->i_wanted_subpage = vbi_dec2bcd(1);
695         else if ( newval.i_int == '+' )
696             p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 1);
697         else if( newval.i_int == '-')
698             p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 0xF9999999); /* BCD complement - 1 */
699
700         if ( !vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x00 ) || vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x99 ) )
701                 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
702         else
703             msg_Info( p_dec, "subpage: %d",
704                       vbi_bcd2dec( p_sys->i_wanted_subpage) );
705
706         p_sys->b_update = true;
707         vlc_mutex_unlock( &p_sys->lock );
708     }
709
710     /* Capture 0-9 for page selection */
711     if( newval.i_int < '0' || newval.i_int > '9' )
712         return VLC_SUCCESS;
713
714     vlc_mutex_lock( &p_sys->lock );
715     p_sys->i_key[0] = p_sys->i_key[1];
716     p_sys->i_key[1] = p_sys->i_key[2];
717     p_sys->i_key[2] = (int)(newval.i_int - '0');
718     msg_Info( p_dec, "page: %c%c%c", (char)(p_sys->i_key[0]+'0'),
719               (char)(p_sys->i_key[1]+'0'), (char)(p_sys->i_key[2]+'0') );
720
721     int i_new_page = 0;
722
723     if( p_sys->i_key[0] > 0 && p_sys->i_key[0] <= 8 &&
724         p_sys->i_key[1] >= 0 && p_sys->i_key[1] <= 9 &&
725         p_sys->i_key[2] >= 0 && p_sys->i_key[2] <= 9 )
726     {
727         i_new_page = p_sys->i_key[0]*100 + p_sys->i_key[1]*10 + p_sys->i_key[2];
728         p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
729     }
730     vlc_mutex_unlock( &p_sys->lock );
731
732     if( i_new_page > 0 )
733         var_SetInteger( p_dec, "vbi-page", i_new_page );
734
735     return VLC_SUCCESS;
736 }