]> git.sesse.net Git - vlc/blob - modules/codec/zvbi.c
zvbi: default alignment to bottom
[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", 8, 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 #define MAX_SLICES 32
137
138 struct decoder_sys_t
139 {
140     vbi_decoder *     p_vbi_dec;
141     vbi_sliced        p_vbi_sliced[MAX_SLICES];
142     unsigned int      i_last_page;
143     bool              b_update;
144     bool              b_text;   /* Subtitles as text */
145
146     vlc_mutex_t       lock; /* Lock to protect the following variables */
147     /* Positioning of Teletext images */
148     int               i_align;
149     /* */
150     unsigned int      i_wanted_page;
151     unsigned int      i_wanted_subpage;
152     /* */
153     bool              b_opaque;
154     struct {
155         int pgno, subno;
156     }                 nav_link[6];
157     int               i_key[3];
158 };
159
160 static subpicture_t *Decode( decoder_t *, block_t ** );
161
162 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
163                                  bool b_text,
164                                  int i_columns, int i_rows,
165                                  int i_align, mtime_t i_pts );
166
167 static void EventHandler( vbi_event *ev, void *user_data );
168 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
169                        const video_format_t fmt, bool b_opaque, const int text_offset );
170 static int get_first_visible_row( vbi_char *p_text, int rows, int columns);
171 static int get_last_visible_row( vbi_char *p_text, int rows, int columns);
172
173 /* Properties callbacks */
174 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
175                         vlc_value_t oldval, vlc_value_t newval, void *p_data );
176 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
177                    vlc_value_t oldval, vlc_value_t newval, void *p_data );
178 static int Position( vlc_object_t *p_this, char const *psz_cmd,
179                      vlc_value_t oldval, vlc_value_t newval, void *p_data );
180 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
181                      vlc_value_t oldval, vlc_value_t newval, void *p_data );
182
183 /*****************************************************************************
184  * Open: probe the decoder and return score
185  *****************************************************************************
186  * Tries to launch a decoder and return score so that the interface is able
187  * to chose.
188  *****************************************************************************/
189 static int Open( vlc_object_t *p_this )
190 {
191     decoder_t     *p_dec = (decoder_t *) p_this;
192     decoder_sys_t *p_sys = NULL;
193
194     if( p_dec->fmt_in.i_codec != VLC_CODEC_TELETEXT )
195         return VLC_EGENERIC;
196
197     p_dec->pf_decode_sub = Decode;
198     p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) );
199     if( p_sys == NULL )
200         return VLC_ENOMEM;
201
202     p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
203     p_sys->b_update = false;
204     p_sys->p_vbi_dec = vbi_decoder_new();
205     vlc_mutex_init( &p_sys->lock );
206
207     if( p_sys->p_vbi_dec == NULL )
208     {
209         msg_Err( p_dec, "VBI decoder could not be created." );
210         Close( p_this );
211         return VLC_ENOMEM;
212     }
213
214     /* Some broadcasters in countries with level 1 and level 1.5 still not send a G0 to do 
215      * matches against table 32 of ETSI 300 706. We try to do some best effort guessing
216      * This is not perfect, but might handle some cases where we know the vbi language 
217      * is known. It would be better if people started sending G0 */
218     for( int i = 0; ppsz_default_triplet[i] != NULL; i++ )
219     {
220         if( p_dec->fmt_in.psz_language && !strcasecmp( p_dec->fmt_in.psz_language, ppsz_default_triplet[i] ) )
221         {
222             vbi_teletext_set_default_region( p_sys->p_vbi_dec, pi_default_triplet[i]);
223             msg_Dbg( p_dec, "overwriting default zvbi region: %d", pi_default_triplet[i] );
224         }
225     }
226
227     vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE | VBI_EVENT_NETWORK |
228 #ifdef ZVBI_DEBUG
229                                 VBI_EVENT_CAPTION | VBI_EVENT_TRIGGER |
230                                 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_NETWORK_ID |
231 #endif
232                                 0 , EventHandler, p_dec );
233
234     /* Create the var on vlc_global. */
235     p_sys->i_wanted_page = var_CreateGetInteger( p_dec, "vbi-page" );
236     var_AddCallback( p_dec, "vbi-page", RequestPage, p_sys );
237
238     /* Check if the Teletext track has a known "initial page". */
239     if( p_sys->i_wanted_page == 100 && p_dec->fmt_in.subs.teletext.i_magazine != -1 )
240     {
241         p_sys->i_wanted_page = 100 * p_dec->fmt_in.subs.teletext.i_magazine +
242                                vbi_bcd2dec( p_dec->fmt_in.subs.teletext.i_page );
243         var_SetInteger( p_dec, "vbi-page", p_sys->i_wanted_page );
244     }
245     p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
246
247     p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
248     var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
249
250     p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
251     var_AddCallback( p_dec, "vbi-position", Position, p_sys );
252
253     p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
254 //    var_AddCallback( p_dec, "vbi-text", Text, p_sys );
255
256     /* Listen for keys */
257     var_AddCallback( p_dec->p_libvlc, "key-pressed", EventKey, p_dec );
258
259     es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_CODEC_SPU );
260     if( p_sys->b_text )
261         p_dec->fmt_out.video.i_chroma = VLC_CODEC_TEXT;
262     else
263         p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBA;
264     return VLC_SUCCESS;
265 }
266
267 /*****************************************************************************
268  * Close:
269  *****************************************************************************/
270 static void Close( vlc_object_t *p_this )
271 {
272     decoder_t     *p_dec = (decoder_t*) p_this;
273     decoder_sys_t *p_sys = p_dec->p_sys;
274
275     var_DelCallback( p_dec, "vbi-position", Position, p_sys );
276     var_DelCallback( p_dec, "vbi-opaque", Opaque, p_sys );
277     var_DelCallback( p_dec, "vbi-page", RequestPage, p_sys );
278     var_DelCallback( p_dec->p_libvlc, "key-pressed", EventKey, p_dec );
279
280     vlc_mutex_destroy( &p_sys->lock );
281
282     if( p_sys->p_vbi_dec )
283         vbi_decoder_delete( p_sys->p_vbi_dec );
284     free( p_sys );
285 }
286
287 #ifdef WORDS_BIGENDIAN
288 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
289 #else
290 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
291 #endif
292
293 /*****************************************************************************
294  * Decode:
295  *****************************************************************************/
296 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
297 {
298     decoder_sys_t   *p_sys = p_dec->p_sys;
299     block_t         *p_block;
300     subpicture_t    *p_spu = NULL;
301     video_format_t  fmt;
302     bool            b_cached = false;
303     vbi_page        p_page;
304
305     if( (pp_block == NULL) || (*pp_block == NULL) )
306         return NULL;
307
308     p_block = *pp_block;
309     *pp_block = NULL;
310
311     if( p_block->i_buffer > 0 &&
312         ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) ||
313           ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) )
314     {
315         vbi_sliced   *p_sliced = p_sys->p_vbi_sliced;
316         unsigned int i_lines = 0;
317
318         p_block->i_buffer--;
319         p_block->p_buffer++;
320         while( p_block->i_buffer >= 2 )
321         {
322             int      i_id   = p_block->p_buffer[0];
323             unsigned i_size = p_block->p_buffer[1];
324
325             if( 2 + i_size > p_block->i_buffer )
326                 break;
327
328             if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES )
329             {
330                 unsigned line_offset  = p_block->p_buffer[2] & 0x1f;
331                 unsigned field_parity = p_block->p_buffer[2] & 0x20;
332
333                 p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B;
334                 if( line_offset > 0 )
335                     p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313);
336                 else
337                     p_sliced[i_lines].line = 0;
338                 for( int i = 0; i < 42; i++ )
339                     p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] );
340                 i_lines++;
341             }
342
343             p_block->i_buffer -= 2 + i_size;
344             p_block->p_buffer += 2 + i_size;
345         }
346
347         if( i_lines > 0 )
348             vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 );
349     }
350
351     /* */
352     vlc_mutex_lock( &p_sys->lock );
353     const int i_align = p_sys->i_align;
354     const unsigned int i_wanted_page = p_sys->i_wanted_page;
355     const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage;
356     const bool b_opaque = p_sys->b_opaque;
357     vlc_mutex_unlock( &p_sys->lock );
358
359     /* Try to see if the page we want is in the cache yet */
360     memset( &p_page, 0, sizeof(vbi_page) );
361     b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
362                                   vbi_dec2bcd( i_wanted_page ),
363                                   i_wanted_subpage, VBI_WST_LEVEL_3p5,
364                                   25, true );
365
366     if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update )
367         goto error;
368
369     if( !b_cached )
370     {
371         if( p_sys->i_last_page != i_wanted_page )
372         {
373             /* We need to reset the subtitle */
374             p_spu = Subpicture( p_dec, &fmt, true,
375                                 p_page.columns, p_page.rows,
376                                 i_align, p_block->i_pts );
377             if( !p_spu )
378                 goto error;
379             p_spu->p_region->psz_text = strdup("");
380
381             p_sys->b_update = true;
382             p_sys->i_last_page = i_wanted_page;
383             goto exit;
384         }
385         goto error;
386     }
387
388     p_sys->b_update = false;
389     p_sys->i_last_page = i_wanted_page;
390 #ifdef ZVBI_DEBUG
391     msg_Dbg( p_dec, "we now have page: %d ready for display",
392              i_wanted_page );
393 #endif
394
395     /* Ignore transparent rows at the beginning and end */
396     int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns );
397     if ( i_first_row < 0 )
398         goto error;
399     int i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1;
400 #ifdef ZVBI_DEBUG
401     msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i",
402              i_first_row + 1, i_first_row + i_num_rows, p_page.rows );
403 #endif
404
405     /* If there is a page or sub to render, then we do that here */
406     /* Create the subpicture unit */
407     p_spu = Subpicture( p_dec, &fmt, p_sys->b_text,
408                         p_page.columns, i_num_rows,
409                         i_align, p_block->i_pts );
410     if( !p_spu )
411         goto error;
412
413     if( p_sys->b_text )
414     {
415         unsigned int i_textsize = 7000;
416         int i_total;
417         char p_text[i_textsize+1];
418
419         i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
420                         "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows );
421         p_text[i_total] = '\0';
422         p_spu->p_region->psz_text = strdup( p_text );
423 #ifdef ZVBI_DEBUG
424         msg_Info( p_dec, "page %x-%x(%d)\n%s", p_page.pgno, p_page.subno, i_total, p_text );
425 #endif
426     }
427     else
428     {
429         picture_t *p_pic = p_spu->p_region->p_picture;
430
431         /* ZVBI is stupid enough to assume pitch == width */
432         p_pic->p->i_pitch = 4 * fmt.i_width;
433
434         /* Maintain subtitle postion */
435         p_spu->p_region->i_y = i_first_row*10;
436         p_spu->i_original_picture_width = p_page.columns*12;
437         p_spu->i_original_picture_height = p_page.rows*10;
438
439         vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32,
440                           p_spu->p_region->p_picture->p->p_pixels, -1,
441                           0, i_first_row, p_page.columns, i_num_rows,
442                           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, i_first_row * p_page.columns );
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 = true;
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 get_first_visible_row( vbi_char *p_text, int rows, int columns)
571 {
572     for ( int i = 0; i < rows * columns; i++ )
573     {
574         if ( p_text[i].opacity != VBI_TRANSPARENT_SPACE )
575         {
576             return i / columns;
577         }
578     }
579
580     return rows;
581 }
582
583 static int get_last_visible_row( vbi_char *p_text, int rows, int columns)
584 {
585     for ( int i = rows * columns - 1; i >= 0; i-- )
586     {
587         if (p_text[i].opacity != VBI_TRANSPARENT_SPACE)
588         {
589             return ( i + columns - 1) / columns;
590         }
591     }
592
593     return 0;
594 }
595
596 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
597                        const video_format_t fmt, bool b_opaque, const int text_offset )
598 {
599     unsigned int    x, y;
600
601     assert( fmt.i_chroma == VLC_CODEC_RGBA );
602
603     /* Kludge since zvbi doesn't provide an option to specify opacity. */
604     for( y = 0; y < fmt.i_height; y++ )
605     {
606         for( x = 0; x < fmt.i_width; x++ )
607         {
608             const vbi_opacity opacity = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].opacity;
609             const int background = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].background;
610             uint32_t *p_pixel = (uint32_t*)&p_src->p->p_pixels[y * p_src->p->i_pitch + 4*x];
611
612             switch( opacity )
613             {
614             /* Show video instead of this character */
615             case VBI_TRANSPARENT_SPACE:
616                 *p_pixel = 0;
617                 break;
618             /* Display foreground and background color */
619             /* To make the boxed text "closed captioning" transparent
620              * change true to false.
621              */
622             case VBI_OPAQUE:
623             /* alpha blend video into background color */
624             case VBI_SEMI_TRANSPARENT:
625                 if( b_opaque )
626                     break;
627             /* Full text transparency. only foreground color is show */
628             case VBI_TRANSPARENT_FULL:
629                 if( (*p_pixel) == (0xff000000 | p_page->color_map[background] ) )
630                     *p_pixel = 0;
631                 break;
632             }
633         }
634     }
635     /* end of kludge */
636     return VLC_SUCCESS;
637 }
638
639 /* Callbacks */
640 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
641                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
642 {
643     decoder_sys_t *p_sys = p_data;
644     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
645
646     vlc_mutex_lock( &p_sys->lock );
647     switch( newval.i_int )
648     {
649         case ZVBI_KEY_RED:
650             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[0].pgno );
651             p_sys->i_wanted_subpage = p_sys->nav_link[0].subno;
652             break;
653         case ZVBI_KEY_GREEN:
654             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[1].pgno );
655             p_sys->i_wanted_subpage = p_sys->nav_link[1].subno;
656             break;
657         case ZVBI_KEY_YELLOW:
658             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[2].pgno );
659             p_sys->i_wanted_subpage = p_sys->nav_link[2].subno;
660             break;
661         case ZVBI_KEY_BLUE:
662             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[3].pgno );
663             p_sys->i_wanted_subpage = p_sys->nav_link[3].subno;
664             break;
665         case ZVBI_KEY_INDEX:
666             p_sys->i_wanted_page = vbi_bcd2dec( p_sys->nav_link[5].pgno ); /* #4 is SKIPPED */
667             p_sys->i_wanted_subpage = p_sys->nav_link[5].subno;
668             break;
669     }
670     if( newval.i_int > 0 && newval.i_int < 999 )
671     {
672         p_sys->i_wanted_page = newval.i_int;
673         p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
674     }
675     vlc_mutex_unlock( &p_sys->lock );
676
677     return VLC_SUCCESS;
678 }
679
680 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
681                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
682 {
683     decoder_sys_t *p_sys = p_data;
684     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
685
686     vlc_mutex_lock( &p_sys->lock );
687     p_sys->b_opaque = newval.b_bool;
688     p_sys->b_update = true;
689     vlc_mutex_unlock( &p_sys->lock );
690
691     return VLC_SUCCESS;
692 }
693
694 static int Position( vlc_object_t *p_this, char const *psz_cmd,
695                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
696 {
697     decoder_sys_t *p_sys = p_data;
698     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
699
700     vlc_mutex_lock( &p_sys->lock );
701     p_sys->i_align = newval.i_int;
702     vlc_mutex_unlock( &p_sys->lock );
703
704     return VLC_SUCCESS;
705 }
706
707 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
708                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
709 {
710     decoder_t *p_dec = p_data;
711     decoder_sys_t *p_sys = p_dec->p_sys;
712
713     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED( p_this );
714
715     /* FIXME: Capture + and - key for subpage browsing */
716     if( newval.i_int == '-' || newval.i_int == '+' )
717     {
718         vlc_mutex_lock( &p_sys->lock );
719         if( p_sys->i_wanted_subpage == VBI_ANY_SUBNO && newval.i_int == '+' )
720             p_sys->i_wanted_subpage = vbi_dec2bcd(1);
721         else if ( newval.i_int == '+' )
722             p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 1);
723         else if( newval.i_int == '-')
724             p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 0xF9999999); /* BCD complement - 1 */
725
726         if ( !vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x00 ) || vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x99 ) )
727                 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
728         else
729             msg_Info( p_dec, "subpage: %d",
730                       vbi_bcd2dec( p_sys->i_wanted_subpage) );
731
732         p_sys->b_update = true;
733         vlc_mutex_unlock( &p_sys->lock );
734     }
735
736     /* Capture 0-9 for page selection */
737     if( newval.i_int < '0' || newval.i_int > '9' )
738         return VLC_SUCCESS;
739
740     vlc_mutex_lock( &p_sys->lock );
741     p_sys->i_key[0] = p_sys->i_key[1];
742     p_sys->i_key[1] = p_sys->i_key[2];
743     p_sys->i_key[2] = (int)(newval.i_int - '0');
744     msg_Info( p_dec, "page: %c%c%c", (char)(p_sys->i_key[0]+'0'),
745               (char)(p_sys->i_key[1]+'0'), (char)(p_sys->i_key[2]+'0') );
746
747     int i_new_page = 0;
748
749     if( p_sys->i_key[0] > 0 && p_sys->i_key[0] <= 8 &&
750         p_sys->i_key[1] >= 0 && p_sys->i_key[1] <= 9 &&
751         p_sys->i_key[2] >= 0 && p_sys->i_key[2] <= 9 )
752     {
753         i_new_page = p_sys->i_key[0]*100 + p_sys->i_key[1]*10 + p_sys->i_key[2];
754         p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
755     }
756     vlc_mutex_unlock( &p_sys->lock );
757
758     if( i_new_page > 0 )
759         var_SetInteger( p_dec, "vbi-page", i_new_page );
760
761     return VLC_SUCCESS;
762 }