codec: ass: Don't force fonts that aren't shipped anymore in the winstore app
[vlc.git] / modules / codec / zvbi.c
blob1004fd102c7b2f19d0cfcdabfc50b5bbc8bc2466
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( "decoder", 51 )
98 set_category( CAT_INPUT )
99 set_subcategory( SUBCAT_INPUT_SCODEC )
100 set_callbacks( Open, Close )
102 add_integer( "vbi-page", 100,
103 PAGE_TEXT, PAGE_LONGTEXT, false )
104 add_bool( "vbi-opaque", false,
105 OPAQUE_TEXT, OPAQUE_LONGTEXT, false )
106 add_integer( "vbi-position", 8, POS_TEXT, POS_LONGTEXT, false )
107 change_integer_list( pi_pos_values, ppsz_pos_descriptions );
108 add_bool( "vbi-text", false,
109 TELX_TEXT, TELX_LONGTEXT, false )
110 add_integer( "vbi-level", 3, LEVEL_TEXT, NULL, false )
111 change_integer_list( level_values, level_descriptions );
112 vlc_module_end ()
114 /****************************************************************************
115 * Local structures
116 ****************************************************************************/
118 // #define ZVBI_DEBUG
120 //Guessing table for missing "default region triplet"
121 static const int pi_default_triplet[] = {
122 0, 0, 0, 0, // slo slk cze ces
123 8, // pol
124 24,24,24,24,24, //scc scr srp hrv slv
125 24,24, //rum ron
126 32,32,32,32,32, //est lit rus bul ukr
127 48,48, //gre ell
128 64, //ara
129 88, //heb
130 16 }; //default
131 static const char *const ppsz_default_triplet[] = {
132 "slo", "slk", "cze", "ces",
133 "pol",
134 "scc", "scr", "srp", "hrv", "slv",
135 "rum", "ron",
136 "est", "lit", "rus", "bul", "ukr",
137 "gre", "ell",
138 "ara",
139 "heb",
140 NULL
143 typedef enum {
144 ZVBI_KEY_RED = 'r' << 16,
145 ZVBI_KEY_GREEN = 'g' << 16,
146 ZVBI_KEY_YELLOW = 'y' << 16,
147 ZVBI_KEY_BLUE = 'b' << 16,
148 ZVBI_KEY_INDEX = 'i' << 16,
149 } ttxt_key_id;
151 #define MAX_SLICES 32
153 struct decoder_sys_t
155 vbi_decoder * p_vbi_dec;
156 vbi_sliced p_vbi_sliced[MAX_SLICES];
157 unsigned int i_last_page;
158 bool b_update;
159 bool b_text; /* Subtitles as text */
161 vlc_mutex_t lock; /* Lock to protect the following variables */
162 /* Positioning of Teletext images */
163 int i_align;
164 /* */
165 unsigned int i_wanted_page;
166 unsigned int i_wanted_subpage;
167 /* */
168 bool b_opaque;
169 unsigned int i_level;
170 struct {
171 int pgno, subno;
172 } nav_link[6];
173 int i_key[3];
176 static subpicture_t *Decode( decoder_t *, block_t ** );
178 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
179 bool b_text,
180 int i_columns, int i_rows,
181 int i_align, mtime_t i_pts );
183 static void EventHandler( vbi_event *ev, void *user_data );
184 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
185 const video_format_t fmt, bool b_opaque, const int text_offset );
186 static int get_first_visible_row( vbi_char *p_text, int rows, int columns);
187 static int get_last_visible_row( vbi_char *p_text, int rows, int columns);
189 /* Properties callbacks */
190 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
191 vlc_value_t oldval, vlc_value_t newval, void *p_data );
192 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
193 vlc_value_t oldval, vlc_value_t newval, void *p_data );
194 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
195 vlc_value_t oldval, vlc_value_t newval, void *p_data );
197 /*****************************************************************************
198 * Open: probe the decoder and return score
199 *****************************************************************************
200 * Tries to launch a decoder and return score so that the interface is able
201 * to chose.
202 *****************************************************************************/
203 static int Open( vlc_object_t *p_this )
205 decoder_t *p_dec = (decoder_t *) p_this;
206 decoder_sys_t *p_sys = NULL;
208 if( p_dec->fmt_in.i_codec != VLC_CODEC_TELETEXT )
209 return VLC_EGENERIC;
211 p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) );
212 if( p_sys == NULL )
213 return VLC_ENOMEM;
215 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
216 p_sys->b_update = false;
217 p_sys->p_vbi_dec = vbi_decoder_new();
218 vlc_mutex_init( &p_sys->lock );
220 if( p_sys->p_vbi_dec == NULL )
222 msg_Err( p_dec, "VBI decoder could not be created." );
223 Close( p_this );
224 return VLC_ENOMEM;
227 /* Some broadcasters in countries with level 1 and level 1.5 still not send a G0 to do
228 * matches against table 32 of ETSI 300 706. We try to do some best effort guessing
229 * This is not perfect, but might handle some cases where we know the vbi language
230 * is known. It would be better if people started sending G0 */
231 for( int i = 0; ppsz_default_triplet[i] != NULL; i++ )
233 if( p_dec->fmt_in.psz_language && !strcasecmp( p_dec->fmt_in.psz_language, ppsz_default_triplet[i] ) )
235 vbi_teletext_set_default_region( p_sys->p_vbi_dec, pi_default_triplet[i]);
236 msg_Dbg( p_dec, "overwriting default zvbi region: %d", pi_default_triplet[i] );
240 vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE | VBI_EVENT_NETWORK |
241 #ifdef ZVBI_DEBUG
242 VBI_EVENT_CAPTION | VBI_EVENT_TRIGGER |
243 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO | VBI_EVENT_NETWORK_ID |
244 #endif
245 0 , EventHandler, p_dec );
247 /* Create the var on vlc_global. */
248 p_sys->i_wanted_page = var_CreateGetInteger( p_dec, "vbi-page" );
249 var_AddCallback( p_dec, "vbi-page", RequestPage, p_sys );
251 /* Check if the Teletext track has a known "initial page". */
252 if( p_sys->i_wanted_page == 100 && p_dec->fmt_in.subs.teletext.i_magazine != -1 )
254 p_sys->i_wanted_page = 100 * p_dec->fmt_in.subs.teletext.i_magazine +
255 vbi_bcd2dec( p_dec->fmt_in.subs.teletext.i_page );
256 var_SetInteger( p_dec, "vbi-page", p_sys->i_wanted_page );
258 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
260 p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
261 var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
263 p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
265 p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
267 p_sys->i_level = var_CreateGetInteger( p_dec, "vbi-level" );
269 /* Listen for keys */
270 var_AddCallback( p_dec->obj.libvlc, "key-pressed", EventKey, p_dec );
272 es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_CODEC_SPU );
273 if( p_sys->b_text )
274 p_dec->fmt_out.video.i_chroma = VLC_CODEC_TEXT;
275 else
276 p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBA;
278 p_dec->pf_decode_sub = Decode;
279 return VLC_SUCCESS;
282 /*****************************************************************************
283 * Close:
284 *****************************************************************************/
285 static void Close( vlc_object_t *p_this )
287 decoder_t *p_dec = (decoder_t*) p_this;
288 decoder_sys_t *p_sys = p_dec->p_sys;
290 var_DelCallback( p_dec, "vbi-opaque", Opaque, p_sys );
291 var_DelCallback( p_dec, "vbi-page", RequestPage, p_sys );
292 var_DelCallback( p_dec->obj.libvlc, "key-pressed", EventKey, p_dec );
294 vlc_mutex_destroy( &p_sys->lock );
296 if( p_sys->p_vbi_dec )
297 vbi_decoder_delete( p_sys->p_vbi_dec );
298 free( p_sys );
301 #ifdef WORDS_BIGENDIAN
302 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
303 #else
304 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
305 #endif
307 /*****************************************************************************
308 * Decode:
309 *****************************************************************************/
310 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
312 decoder_sys_t *p_sys = p_dec->p_sys;
313 block_t *p_block;
314 subpicture_t *p_spu = NULL;
315 video_format_t fmt;
316 bool b_cached = false;
317 vbi_page p_page;
319 if( (pp_block == NULL) || (*pp_block == NULL) )
320 return NULL;
322 p_block = *pp_block;
323 *pp_block = NULL;
325 if( p_block->i_buffer > 0 &&
326 ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) ||
327 ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) )
329 vbi_sliced *p_sliced = p_sys->p_vbi_sliced;
330 unsigned int i_lines = 0;
332 p_block->i_buffer--;
333 p_block->p_buffer++;
334 while( p_block->i_buffer >= 2 )
336 int i_id = p_block->p_buffer[0];
337 unsigned i_size = p_block->p_buffer[1];
339 if( 2 + i_size > p_block->i_buffer )
340 break;
342 if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES )
344 if(p_block->p_buffer[3] == 0xE4 ) /* framing_code */
346 unsigned line_offset = p_block->p_buffer[2] & 0x1f;
347 unsigned field_parity = p_block->p_buffer[2] & 0x20;
349 p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B;
350 if( line_offset > 0 )
351 p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313);
352 else
353 p_sliced[i_lines].line = 0;
354 for( int i = 0; i < 42; i++ )
355 p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] );
356 i_lines++;
360 p_block->i_buffer -= 2 + i_size;
361 p_block->p_buffer += 2 + i_size;
364 if( i_lines > 0 )
365 vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 );
368 /* */
369 vlc_mutex_lock( &p_sys->lock );
370 const int i_align = p_sys->i_align;
371 const unsigned int i_wanted_page = p_sys->i_wanted_page;
372 const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage;
373 const bool b_opaque = p_sys->b_opaque;
374 const unsigned int i_level = p_sys->i_level > 3 ? 3 : p_sys->i_level;
375 vlc_mutex_unlock( &p_sys->lock );
377 /* Try to see if the page we want is in the cache yet */
378 memset( &p_page, 0, sizeof(vbi_page) );
379 b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
380 vbi_dec2bcd( i_wanted_page ),
381 i_wanted_subpage, level_zvbi_values[i_level],
382 25, true );
384 if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update )
385 goto error;
387 if( !b_cached )
389 if( p_sys->b_text && p_sys->i_last_page != i_wanted_page )
391 /* We need to reset the subtitle */
392 p_spu = Subpicture( p_dec, &fmt, true,
393 p_page.columns, p_page.rows,
394 i_align, p_block->i_pts );
395 if( !p_spu )
396 goto error;
397 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
398 p_spu_sys->p_segments = text_segment_New("");
400 p_sys->b_update = true;
401 p_sys->i_last_page = i_wanted_page;
402 goto exit;
404 goto error;
407 p_sys->b_update = false;
408 p_sys->i_last_page = i_wanted_page;
409 #ifdef ZVBI_DEBUG
410 msg_Dbg( p_dec, "we now have page: %d ready for display",
411 i_wanted_page );
412 #endif
414 /* Ignore transparent rows at the beginning and end */
415 int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns );
416 int i_num_rows;
417 if ( i_first_row < 0 ) {
418 i_first_row = p_page.rows - 1;
419 i_num_rows = 0;
420 } else {
421 i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1;
423 #ifdef ZVBI_DEBUG
424 msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i",
425 i_first_row + 1, i_first_row + i_num_rows, p_page.rows );
426 #endif
428 /* If there is a page or sub to render, then we do that here */
429 /* Create the subpicture unit */
430 p_spu = Subpicture( p_dec, &fmt, p_sys->b_text,
431 p_page.columns, i_num_rows,
432 i_align, p_block->i_pts );
433 if( !p_spu )
434 goto error;
436 if( p_sys->b_text )
438 unsigned int i_textsize = 7000;
439 int i_total,offset;
440 char p_text[i_textsize+1];
442 i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
443 "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows );
445 for( offset=1; offset<i_total && isspace( p_text[i_total-offset ] ); offset++)
446 p_text[i_total-offset] = '\0';
448 i_total -= offset;
450 offset=0;
451 while( offset < i_total && isspace( p_text[offset] ) )
452 offset++;
454 subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys;
455 p_spu_sys->p_segments = text_segment_New( &p_text[offset] );
456 if( p_spu_sys->p_segments && b_opaque )
458 p_spu_sys->p_segments->style = text_style_Create( STYLE_NO_DEFAULTS );
459 if( p_spu_sys->p_segments->style )
461 /* Set text background */
462 p_spu_sys->p_segments->style->i_style_flags = STYLE_BACKGROUND;
463 p_spu_sys->p_segments->style->i_features |= STYLE_HAS_FLAGS;
467 p_spu_sys->align = i_align;
468 p_spu_sys->noregionbg = true;
470 #ifdef ZVBI_DEBUG
471 msg_Info( p_dec, "page %x-%x(%d)\n\"%s\"", p_page.pgno, p_page.subno, i_total, &p_text[offset] );
472 #endif
474 else
476 picture_t *p_pic = p_spu->p_region->p_picture;
478 /* ZVBI is stupid enough to assume pitch == width */
479 p_pic->p->i_pitch = 4 * fmt.i_width;
481 /* Maintain subtitle postion */
482 p_spu->p_region->i_y = i_first_row*10;
483 p_spu->i_original_picture_width = p_page.columns*12;
484 p_spu->i_original_picture_height = p_page.rows*10;
486 vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32,
487 p_spu->p_region->p_picture->p->p_pixels, -1,
488 0, i_first_row, p_page.columns, i_num_rows,
489 1, 1);
491 vlc_mutex_lock( &p_sys->lock );
492 memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ;
493 vlc_mutex_unlock( &p_sys->lock );
495 OpaquePage( p_pic, &p_page, fmt, b_opaque, i_first_row * p_page.columns );
498 exit:
499 vbi_unref_page( &p_page );
500 block_Release( p_block );
501 return p_spu;
503 error:
504 vbi_unref_page( &p_page );
505 block_Release( p_block );
506 return NULL;
509 static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt,
510 bool b_text,
511 int i_columns, int i_rows, int i_align,
512 mtime_t i_pts )
514 video_format_t fmt;
515 subpicture_t *p_spu=NULL;
517 /* If there is a page or sub to render, then we do that here */
518 /* Create the subpicture unit */
519 if( b_text )
520 p_spu = decoder_NewSubpictureText( p_dec );
521 else
522 p_spu = decoder_NewSubpicture( p_dec, NULL );
523 if( !p_spu )
525 msg_Warn( p_dec, "can't get spu buffer" );
526 return NULL;
529 video_format_Init(&fmt, b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA);
530 if( b_text )
532 fmt.i_bits_per_pixel = 0;
534 else
536 fmt.i_width = fmt.i_visible_width = i_columns * 12;
537 fmt.i_height = fmt.i_visible_height = i_rows * 10;
538 fmt.i_bits_per_pixel = 32;
539 fmt.i_sar_num = fmt.i_sar_den = 0; /* let the vout set the correct AR */
541 fmt.i_x_offset = fmt.i_y_offset = 0;
543 p_spu->p_region = subpicture_region_New( &fmt );
544 if( p_spu->p_region == NULL )
546 msg_Err( p_dec, "cannot allocate SPU region" );
547 subpicture_Delete( p_spu );
548 return NULL;
551 p_spu->p_region->i_x = 0;
552 p_spu->p_region->i_y = 0;
554 p_spu->i_start = i_pts;
555 p_spu->i_stop = b_text ? i_pts + (10*CLOCK_FREQ): 0;
556 p_spu->b_ephemer = true;
557 p_spu->b_absolute = b_text ? false : true;
559 if( !b_text )
560 p_spu->p_region->i_align = i_align;
561 p_spu->i_original_picture_width = fmt.i_width;
562 p_spu->i_original_picture_height = fmt.i_height;
564 /* */
565 *p_fmt = fmt;
566 return p_spu;
569 static void EventHandler( vbi_event *ev, void *user_data )
571 decoder_t *p_dec = (decoder_t *)user_data;
572 decoder_sys_t *p_sys = p_dec->p_sys;
574 if( ev->type == VBI_EVENT_TTX_PAGE )
576 #ifdef ZVBI_DEBUG
577 msg_Info( p_dec, "Page %03x.%02x ",
578 ev->ev.ttx_page.pgno,
579 ev->ev.ttx_page.subno & 0xFF);
580 #endif
581 if( p_sys->i_last_page == vbi_bcd2dec( ev->ev.ttx_page.pgno ) )
582 p_sys->b_update = true;
583 #ifdef ZVBI_DEBUG
584 if( ev->ev.ttx_page.clock_update )
585 msg_Dbg( p_dec, "clock" );
586 if( ev->ev.ttx_page.header_update )
587 msg_Dbg( p_dec, "header" );
588 #endif
590 else if( ev->type == VBI_EVENT_CLOSE )
591 msg_Dbg( p_dec, "Close event" );
592 else if( ev->type == VBI_EVENT_CAPTION )
593 msg_Dbg( p_dec, "Caption line: %x", ev->ev.caption.pgno );
594 else if( ev->type == VBI_EVENT_NETWORK )
596 msg_Dbg( p_dec, "Network change");
597 vbi_network n = ev->ev.network;
598 msg_Dbg( p_dec, "Network id:%d name: %s, call: %s ", n.nuid, n.name, n.call );
600 else if( ev->type == VBI_EVENT_TRIGGER )
601 msg_Dbg( p_dec, "Trigger event" );
602 else if( ev->type == VBI_EVENT_ASPECT )
603 msg_Dbg( p_dec, "Aspect update" );
604 else if( ev->type == VBI_EVENT_PROG_INFO )
605 msg_Dbg( p_dec, "Program info received" );
606 else if( ev->type == VBI_EVENT_NETWORK_ID )
607 msg_Dbg( p_dec, "Network ID changed" );
610 static int get_first_visible_row( vbi_char *p_text, int rows, int columns)
612 for ( int i = 0; i < rows * columns; i++ )
614 if ( p_text[i].opacity != VBI_TRANSPARENT_SPACE )
616 return i / columns;
620 return -1;
623 static int get_last_visible_row( vbi_char *p_text, int rows, int columns)
625 for ( int i = rows * columns - 1; i >= 0; i-- )
627 if (p_text[i].opacity != VBI_TRANSPARENT_SPACE)
629 return i / columns;
633 return -1;
636 static int OpaquePage( picture_t *p_src, const vbi_page *p_page,
637 const video_format_t fmt, bool b_opaque, const int text_offset )
639 unsigned int x, y;
641 assert( fmt.i_chroma == VLC_CODEC_RGBA );
643 /* Kludge since zvbi doesn't provide an option to specify opacity. */
644 for( y = 0; y < fmt.i_height; y++ )
646 for( x = 0; x < fmt.i_width; x++ )
648 const vbi_opacity opacity = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].opacity;
649 const int background = p_page->text[ text_offset + y/10 * p_page->columns + x/12 ].background;
650 uint32_t *p_pixel = (uint32_t*)&p_src->p->p_pixels[y * p_src->p->i_pitch + 4*x];
652 switch( opacity )
654 /* Show video instead of this character */
655 case VBI_TRANSPARENT_SPACE:
656 *p_pixel = 0;
657 break;
658 /* Display foreground and background color */
659 /* To make the boxed text "closed captioning" transparent
660 * change true to false.
662 case VBI_OPAQUE:
663 /* alpha blend video into background color */
664 case VBI_SEMI_TRANSPARENT:
665 if( b_opaque )
666 break;
667 /* Full text transparency. only foreground color is show */
668 case VBI_TRANSPARENT_FULL:
669 if( (*p_pixel) == (0xff000000 | p_page->color_map[background] ) )
670 *p_pixel = 0;
671 break;
675 /* end of kludge */
676 return VLC_SUCCESS;
679 /* Callbacks */
680 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
681 vlc_value_t oldval, vlc_value_t newval, void *p_data )
683 decoder_sys_t *p_sys = p_data;
684 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
685 int want_navlink = -1;
687 vlc_mutex_lock( &p_sys->lock );
688 switch( newval.i_int )
690 case ZVBI_KEY_RED:
691 want_navlink = 0;
692 break;
693 case ZVBI_KEY_GREEN:
694 want_navlink = 1;
695 break;
696 case ZVBI_KEY_YELLOW:
697 want_navlink = 2;
698 break;
699 case ZVBI_KEY_BLUE:
700 want_navlink = 3;
701 break;
702 case ZVBI_KEY_INDEX:
703 want_navlink = 5; /* #4 is SKIPPED */
704 break;
707 if (want_navlink > -1)
709 int page = vbi_bcd2dec( p_sys->nav_link[want_navlink].pgno );
710 if (page > 0 && page < 999) {
711 p_sys->i_wanted_page = page;
712 p_sys->i_wanted_subpage = p_sys->nav_link[want_navlink].subno;
715 else if( newval.i_int > 0 && newval.i_int < 999 )
717 p_sys->i_wanted_page = newval.i_int;
718 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
720 vlc_mutex_unlock( &p_sys->lock );
722 return VLC_SUCCESS;
725 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
726 vlc_value_t oldval, vlc_value_t newval, void *p_data )
728 decoder_sys_t *p_sys = p_data;
729 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
731 vlc_mutex_lock( &p_sys->lock );
732 p_sys->b_opaque = newval.b_bool;
733 p_sys->b_update = true;
734 vlc_mutex_unlock( &p_sys->lock );
736 return VLC_SUCCESS;
739 static int EventKey( vlc_object_t *p_this, char const *psz_cmd,
740 vlc_value_t oldval, vlc_value_t newval, void *p_data )
742 decoder_t *p_dec = p_data;
743 decoder_sys_t *p_sys = p_dec->p_sys;
745 VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED( p_this );
747 /* FIXME: Capture + and - key for subpage browsing */
748 if( newval.i_int == '-' || newval.i_int == '+' )
750 vlc_mutex_lock( &p_sys->lock );
751 if( p_sys->i_wanted_subpage == VBI_ANY_SUBNO && newval.i_int == '+' )
752 p_sys->i_wanted_subpage = vbi_dec2bcd(1);
753 else if ( newval.i_int == '+' )
754 p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 1);
755 else if( newval.i_int == '-')
756 p_sys->i_wanted_subpage = vbi_add_bcd( p_sys->i_wanted_subpage, 0xF9999999); /* BCD complement - 1 */
758 if ( !vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x00 ) || vbi_bcd_digits_greater( p_sys->i_wanted_subpage, 0x99 ) )
759 p_sys->i_wanted_subpage = VBI_ANY_SUBNO;
760 else
761 msg_Info( p_dec, "subpage: %d",
762 vbi_bcd2dec( p_sys->i_wanted_subpage) );
764 p_sys->b_update = true;
765 vlc_mutex_unlock( &p_sys->lock );
768 /* Capture 0-9 for page selection */
769 if( newval.i_int < '0' || newval.i_int > '9' )
770 return VLC_SUCCESS;
772 vlc_mutex_lock( &p_sys->lock );
773 p_sys->i_key[0] = p_sys->i_key[1];
774 p_sys->i_key[1] = p_sys->i_key[2];
775 p_sys->i_key[2] = (int)(newval.i_int - '0');
776 msg_Info( p_dec, "page: %c%c%c", (char)(p_sys->i_key[0]+'0'),
777 (char)(p_sys->i_key[1]+'0'), (char)(p_sys->i_key[2]+'0') );
779 int i_new_page = 0;
781 if( p_sys->i_key[0] > 0 && p_sys->i_key[0] <= 8 &&
782 p_sys->i_key[1] >= 0 && p_sys->i_key[1] <= 9 &&
783 p_sys->i_key[2] >= 0 && p_sys->i_key[2] <= 9 )
785 i_new_page = p_sys->i_key[0]*100 + p_sys->i_key[1]*10 + p_sys->i_key[2];
786 p_sys->i_key[0] = p_sys->i_key[1] = p_sys->i_key[2] = '*' - '0';
788 vlc_mutex_unlock( &p_sys->lock );
790 if( i_new_page > 0 )
791 var_SetInteger( p_dec, "vbi-page", i_new_page );
793 return VLC_SUCCESS;