Use p_block->i_dts as spu->i_start instead of p_block->i_pts. This fixes timing issue...
[vlc.git] / 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
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 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 General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 /*****************************************************************************
25  *
26  * information on teletext format can be found here :
27  * http://pdc.ro.nu/teletext.html
28  *
29  *****************************************************************************/
30
31 /* This module implements:
32  * ETSI EN 301 775: VBI data in PES
33  * ETSI EN 300 472: EBU Teletext data in PES
34  * ETSI EN 300 706: Enhanced Teletext (libzvbi)
35  * ETSI EN 300 231: Video Programme System [VPS] (libzvbi)
36  * ETSI EN 300 294: 625-line Wide Screen Signaling [WSS] (libzvbi)
37  * EIA-608 Revision A: Closed Captioning [CC] (libzvbi)
38  */
39
40 #include <vlc/vlc.h>
41 #include <assert.h>
42 #include <stdint.h>
43 #include <libzvbi.h>
44
45 #include "vlc_vout.h"
46 #include "vlc_bits.h"
47 #include "vlc_codec.h"
48 #include "vlc_image.h"
49
50 typedef enum {
51     DATA_UNIT_EBU_TELETEXT_NON_SUBTITLE     = 0x02,
52     DATA_UNIT_EBU_TELETEXT_SUBTITLE         = 0x03,
53     DATA_UNIT_EBU_TELETEXT_INVERTED         = 0x0C,
54
55     DATA_UNIT_ZVBI_WSS_CPR1204              = 0xB4,
56     DATA_UNIT_ZVBI_CLOSED_CAPTION_525       = 0xB5,
57     DATA_UNIT_ZVBI_MONOCHROME_SAMPLES_525   = 0xB6,
58
59     DATA_UNIT_VPS                           = 0xC3,
60     DATA_UNIT_WSS                           = 0xC4,
61     DATA_UNIT_CLOSED_CAPTION                = 0xC5,
62     DATA_UNIT_MONOCHROME_SAMPLES            = 0xC6,
63
64     DATA_UNIT_STUFFING                      = 0xFF,
65 } data_unit_id;
66
67 /*****************************************************************************
68  * Module descriptor.
69  *****************************************************************************/
70 static int  Open ( vlc_object_t * );
71 static void Close( vlc_object_t * );
72 static subpicture_t *Decode( decoder_t *, block_t ** );
73
74 #define PAGE_TEXT N_("Teletext page")
75 #define PAGE_LONGTEXT N_("Open the indicated Teletext page." \
76         "Default page is index 100")
77
78 #define OPAQUE_TEXT N_("Text is always opaque")
79 #define OPAQUE_LONGTEXT N_("Setting vbi-opaque to false " \
80         "makes the boxed text transparent." )
81
82 #define POS_TEXT N_("Teletext alignment")
83 #define POS_LONGTEXT N_( \
84   "You can enforce the teletext position on the video " \
85   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
86   "also use combinations of these values, eg. 6 = top-right).")
87
88 #define TELX_TEXT N_("Teletext text subtitles")
89 #define TELX_LONGTEXT N_( "Output teletext subtitles as text " \
90   "instead of as RGBA" )
91
92 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
93 static const char *ppsz_pos_descriptions[] =
94 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
95   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
96
97 vlc_module_begin();
98     set_description( _("VBI and Teletext decoder") );
99     set_shortname( "VBI & Teletext" );
100     set_capability( "decoder", 51 );
101     set_category( CAT_INPUT );
102     set_subcategory( SUBCAT_INPUT_SCODEC );
103     set_callbacks( Open, Close );
104
105     add_integer( "vbi-page", 100, NULL,
106                  PAGE_TEXT, PAGE_LONGTEXT, VLC_FALSE );
107     add_bool( "vbi-opaque", VLC_TRUE, NULL,
108                  OPAQUE_TEXT, OPAQUE_LONGTEXT, VLC_FALSE );
109     add_integer( "vbi-position", 4, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
110         change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
111     add_bool( "vbi-text", VLC_FALSE, NULL,
112               TELX_TEXT, TELX_LONGTEXT, VLC_FALSE );
113 vlc_module_end();
114
115 /****************************************************************************
116  * Local structures
117  ****************************************************************************/
118
119 struct decoder_sys_t
120 {
121     vbi_decoder *           p_vbi_dec;
122     vbi_dvb_demux *         p_dvb_demux;
123     unsigned int            i_wanted_page;
124     unsigned int            i_last_page;
125     vlc_bool_t              b_update;
126     vlc_bool_t              b_opaque;
127
128     /* Subtitles as text */
129     vlc_bool_t              b_text;
130
131     /* Positioning of Teletext images */
132     int                     i_align;
133
134     /* Misc */
135 #ifdef HAVE_FFMPEG_SWSCALE_H
136     image_handler_t         *p_image;
137 #endif
138 };
139
140 static void event_handler( vbi_event *ev, void *user_data );
141 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
142                         vlc_value_t oldval, vlc_value_t newval, void *p_data );
143 static int OpaquePage( decoder_t *p_dec, vbi_page p_page, video_format_t fmt,
144                         picture_t **p_src );
145
146 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
147                    vlc_value_t oldval, vlc_value_t newval, void *p_data );
148 static int Position( vlc_object_t *p_this, char const *psz_cmd,
149                      vlc_value_t oldval, vlc_value_t newval, void *p_data );
150
151 /*****************************************************************************
152  * Open: probe the decoder and return score
153  *****************************************************************************
154  * Tries to launch a decoder and return score so that the interface is able
155  * to chose.
156  *****************************************************************************/
157 static int Open( vlc_object_t *p_this )
158 {
159     decoder_t     *p_dec = (decoder_t *) p_this;
160     decoder_sys_t *p_sys = NULL;
161
162     if( p_dec->fmt_in.i_codec != VLC_FOURCC('t','e','l','x') )
163     {
164         return VLC_EGENERIC;
165     }
166
167     p_dec->pf_decode_sub = Decode;
168     p_sys = p_dec->p_sys = malloc( sizeof(decoder_sys_t) );
169     if( p_sys == NULL )
170     {
171         msg_Err( p_dec, "out of memory" );
172         return VLC_ENOMEM;
173     }
174     memset( p_sys, 0, sizeof(decoder_sys_t) );
175
176 #ifdef HAVE_FFMPEG_SWSCALE_H
177     p_sys->p_image = image_HandlerCreate( VLC_OBJECT(p_dec) );
178     if( !p_sys->p_image )
179     {
180         free( p_sys );
181         msg_Err( p_dec, "out of memory" );
182         return VLC_ENOMEM;
183     }
184 #endif
185
186     p_sys->b_update = VLC_FALSE;
187     p_sys->p_vbi_dec = vbi_decoder_new();
188     p_sys->p_dvb_demux = vbi_dvb_pes_demux_new( NULL, NULL );
189
190     if( (p_sys->p_vbi_dec == NULL) || (p_sys->p_dvb_demux == NULL) )
191     {
192         msg_Err( p_dec, "VBI decoder/demux could not be created." );
193         Close( p_this );
194         return VLC_ENOMEM;
195     }
196
197     vbi_event_handler_register( p_sys->p_vbi_dec, VBI_EVENT_TTX_PAGE |
198                                 VBI_EVENT_CAPTION | VBI_EVENT_NETWORK |
199                                 VBI_EVENT_ASPECT | VBI_EVENT_PROG_INFO,
200                                 event_handler, p_dec );
201
202     /* Create the var on vlc_global. */
203     p_sys->i_wanted_page = var_CreateGetInteger( p_dec->p_libvlc, "vbi-page" );
204     var_AddCallback( p_dec->p_libvlc, "vbi-page",
205                      RequestPage, p_sys );
206
207     p_sys->b_opaque = var_CreateGetBool( p_dec, "vbi-opaque" );
208     var_AddCallback( p_dec, "vbi-opaque", Opaque, p_sys );
209
210     p_sys->i_align = var_CreateGetInteger( p_dec, "vbi-position" );
211     var_AddCallback( p_dec, "vbi-position", Position, p_sys );
212
213     p_sys->b_text = var_CreateGetBool( p_dec, "vbi-text" );
214 //    var_AddCallback( p_dec, "vbi-text", Text, p_sys );
215
216     es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 's','p','u',' ' ) );
217     if( p_sys->b_text )
218         p_dec->fmt_out.video.i_chroma = VLC_FOURCC('T','E','X','T');
219     else
220 #ifdef HAVE_FFMPEG_SWSCALE_H
221         p_dec->fmt_out.video.i_chroma = VLC_FOURCC('Y','U','V','A');
222 #else
223         p_dec->fmt_out.video.i_chroma = VLC_FOURCC('R','G','B','A');
224 #endif
225     return VLC_SUCCESS;
226 }
227
228 /*****************************************************************************
229  * Close:
230  *****************************************************************************/
231 static void Close( vlc_object_t *p_this )
232 {
233     decoder_t     *p_dec = (decoder_t*) p_this;
234     decoder_sys_t *p_sys = p_dec->p_sys;
235
236 #ifdef HAVE_FFMPEG_SWSCALE_H
237     if( p_sys->p_image ) image_HandlerDelete( p_sys->p_image );
238 #endif
239     if( p_sys->p_vbi_dec ) vbi_decoder_delete( p_sys->p_vbi_dec );
240     if( p_sys->p_dvb_demux ) vbi_dvb_demux_delete( p_sys->p_dvb_demux );
241     free( p_sys );
242 }
243
244 #define MAX_SLICES 32
245
246 /*****************************************************************************
247  * Decode:
248  *****************************************************************************/
249 static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
250 {
251     decoder_sys_t   *p_sys = (decoder_sys_t *) p_dec->p_sys;
252     block_t         *p_block;
253     subpicture_t    *p_spu = NULL;
254     video_format_t  fmt;
255     vlc_bool_t      b_cached = VLC_FALSE;
256     vbi_page        p_page;
257     const uint8_t   *p_pos;
258     unsigned int    i_left;
259
260     if( (pp_block == NULL) || (*pp_block == NULL) )
261         return NULL;
262
263     p_block = *pp_block;
264     *pp_block = NULL;
265
266     p_pos = p_block->p_buffer;
267     i_left = p_block->i_buffer;
268
269     while( i_left > 0 )
270     {
271         vbi_sliced      p_sliced[MAX_SLICES];
272         unsigned int    i_lines = 0;
273         int64_t         i_pts;
274
275         i_lines = vbi_dvb_demux_cor( p_sys->p_dvb_demux, p_sliced,
276                                      MAX_SLICES, &i_pts, &p_pos, &i_left );
277
278         if( i_lines > 0 )
279             vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, i_pts / 90000.0 );
280     }
281
282     /* Try to see if the page we want is in the cache yet */
283     b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page,
284                                   vbi_dec2bcd( p_sys->i_wanted_page ),
285                                   VBI_ANY_SUBNO, VBI_WST_LEVEL_3p5,
286                                   25, FALSE );
287
288     if( !b_cached )
289         goto error;
290
291     if( ( p_sys->i_wanted_page == p_sys->i_last_page ) &&
292         ( p_sys->b_update != VLC_TRUE ) )
293         goto error;
294
295     p_sys->b_update = VLC_FALSE;
296     p_sys->i_last_page = p_sys->i_wanted_page;
297 #if 0
298     msg_Dbg( p_dec, "we now have page: %d ready for display",
299              p_sys->i_wanted_page );
300 #endif
301     /* If there is a page or sub to render, then we do that here */
302     /* Create the subpicture unit */
303     p_spu = p_dec->pf_spu_buffer_new( p_dec );
304     if( !p_spu )
305     {
306         msg_Warn( p_dec, "can't get spu buffer" );
307         goto error;
308     }
309
310     /* Create a new subpicture region */
311     memset( &fmt, 0, sizeof(video_format_t) );
312     fmt.i_chroma = p_sys->b_text ? VLC_FOURCC('T','E','X','T') :
313 #ifdef HAVE_FFMPEG_SWSCALE_H
314                                    VLC_FOURCC('Y','U','V','A');
315 #else
316                                    VLC_FOURCC('R','G','B','A');
317 #endif
318     fmt.i_aspect = p_sys->b_text ? 0 : VOUT_ASPECT_FACTOR;
319     fmt.i_sar_num = fmt.i_sar_den = 1;
320     fmt.i_width = fmt.i_visible_width = p_page.columns * 12;
321     fmt.i_height = fmt.i_visible_height = p_page.rows * 10;
322     fmt.i_bits_per_pixel = p_sys->b_text ? 0 : 32;
323     fmt.i_x_offset = fmt.i_y_offset = 0;
324
325     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
326     if( p_spu->p_region == NULL )
327     {
328         msg_Err( p_dec, "cannot allocate SPU region" );
329         goto error;
330     }
331
332     p_spu->p_region->i_x = 0;
333     p_spu->p_region->i_y = 0;
334     p_spu->p_region->i_align = SUBPICTURE_ALIGN_TOP;
335
336     /* Normal text subs, easy markup */
337     p_spu->i_flags = SUBPICTURE_ALIGN_TOP;
338
339     p_spu->i_start = (mtime_t) p_block->i_dts;
340     p_spu->i_stop = (mtime_t) 0;
341     p_spu->b_ephemer = VLC_TRUE;
342     p_spu->b_absolute = VLC_FALSE;
343     p_spu->b_pausable = VLC_TRUE;
344     p_spu->i_width = fmt.i_width;
345     p_spu->i_height = fmt.i_height;
346     p_spu->i_original_picture_width = p_page.columns * 12;
347     p_spu->i_original_picture_height = p_page.rows * 10;
348
349 #ifdef WORDS_BIGENDIAN
350 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_BE
351 #else
352 # define ZVBI_PIXFMT_RGBA32 VBI_PIXFMT_RGBA32_LE
353 #endif
354
355     if( p_sys->b_text )
356     {
357         unsigned int i_textsize = 7000;
358         int i_total;
359         char p_text[7000];
360
361         i_total = vbi_print_page_region( &p_page, p_text, i_textsize,
362                         "UTF-8", 0, 0, 0, 0, p_page.columns, p_page.rows );
363         p_text[i_total] = '\0';
364         /* Strip off the pagenumber */
365         if( i_total <= 40 ) goto error;
366         p_spu->p_region->psz_text = strdup( &p_text[8] );
367
368         p_spu->p_region->fmt.i_height = p_spu->p_region->fmt.i_visible_height = p_page.rows + 1;
369         msg_Dbg( p_dec, "page %x-%x(%d)\n%s", p_page.pgno, p_page.subno, i_total, p_text );
370     }
371     else
372     {
373 #ifdef HAVE_FFMPEG_SWSCALE_H
374         video_format_t fmt_in;
375         picture_t *p_pic, *p_dest;
376
377         p_pic = ( picture_t * ) malloc( sizeof( picture_t ) );
378         if( !p_pic )
379         {
380             msg_Err( p_dec, "out of memory" );
381             goto error;
382         }
383
384         memset( &fmt_in, 0, sizeof( video_format_t ) );
385         memset( p_pic, 0, sizeof( picture_t ) );
386
387         fmt_in = p_spu->p_region->fmt;
388         fmt_in.i_chroma = VLC_FOURCC('R','G','B','A');
389
390         vout_AllocatePicture( VLC_OBJECT(p_dec), p_pic, fmt_in.i_chroma,
391                         fmt_in.i_width, fmt_in.i_height, fmt_in.i_aspect );
392         if( !p_pic->i_planes )
393         {
394             free( p_pic->p_data_orig );
395             free( p_pic );
396             goto error;
397         }
398
399         vbi_draw_vt_page( &p_page, ZVBI_PIXFMT_RGBA32,
400                           p_pic->p->p_pixels, 1, 1 );
401
402         p_pic->p->i_lines = p_page.rows * 10;
403         p_pic->p->i_pitch = p_page.columns * 12 * 4;
404
405         OpaquePage( p_dec, p_page, fmt_in, &p_pic );
406
407 #if 0
408         msg_Dbg( p_dec, "page %x-%x(%d,%d)",
409                  p_page.pgno, p_page.subno,
410                  p_page.rows, p_page.columns );
411 #endif
412         p_dest = image_Convert( p_sys->p_image, p_pic, &fmt_in,
413                                 &p_spu->p_region->fmt );
414         if( !p_dest )
415         {
416             free( p_pic->p_data_orig );
417             free( p_pic );
418             msg_Err( p_dec, "chroma conversion failed" );
419             goto error;
420         }
421
422         vout_CopyPicture( VLC_OBJECT(p_dec), &p_spu->p_region->picture,
423                           p_dest );
424
425         free( p_pic->p_data_orig );
426         free( p_pic );
427
428         free( p_dest->p_data_orig );
429         free( p_dest );
430 #else
431         picture_t *p_pic;
432
433         vbi_draw_vt_page( &p_page, ZVBI_PIXFMT_RGBA32,
434                           p_spu->p_region->picture.p->p_pixels, 1, 1 );
435
436         p_spu->p_region->picture.p->i_lines = p_page.rows * 10;
437         p_spu->p_region->picture.p->i_pitch = p_page.columns * 12 * 4;
438
439         p_pic = &(p_spu->p_region->picture);
440         OpaquePage( p_dec, p_page, fmt, &p_pic );
441 #endif
442     }
443
444 #undef PIXFMT_RGBA32
445
446     vbi_unref_page( &p_page );
447     block_Release( p_block );
448     return p_spu;
449
450 error:
451     vbi_unref_page( &p_page );
452     if( p_spu != NULL )
453     {
454         p_dec->pf_spu_buffer_del( p_dec, p_spu );
455         p_spu = NULL;
456     }
457
458     block_Release( p_block );
459     return NULL;
460 }
461
462 static void event_handler( vbi_event *ev, void *user_data )
463 {
464     decoder_t *p_dec        = (decoder_t *)user_data;
465     decoder_sys_t *p_sys    = p_dec->p_sys;
466
467     if( ev->type == VBI_EVENT_TTX_PAGE )
468     {
469         /* msg_Dbg( p_dec, "Page %03x.%02x ",
470                     ev->ev.ttx_page.pgno,
471                     ev->ev.ttx_page.subno & 0xFF);
472         */
473         if( p_sys->i_last_page == vbi_bcd2dec( ev->ev.ttx_page.pgno ) )
474             p_sys->b_update = VLC_TRUE;
475
476         if( ev->ev.ttx_page.clock_update )
477             msg_Dbg( p_dec, "clock" );
478         if( ev->ev.ttx_page.header_update )
479             msg_Dbg( p_dec, "header" );
480     }
481     else if( ev->type == VBI_EVENT_CAPTION )
482         msg_Dbg( p_dec, "Caption line: %x", ev->ev.caption.pgno );
483     else if( ev->type == VBI_EVENT_NETWORK )
484         msg_Dbg( p_dec, "Network change" );
485     else if( ev->type == VBI_EVENT_ASPECT )
486         msg_Dbg( p_dec, "Aspect update" );
487     else if( ev->type == VBI_EVENT_NETWORK )
488         msg_Dbg( p_dec, "Program info received" );
489 }
490
491 static int OpaquePage( decoder_t *p_dec, vbi_page p_page,
492                        video_format_t fmt, picture_t **p_src )
493 {
494     decoder_sys_t   *p_sys = (decoder_sys_t *) p_dec->p_sys;
495     uint32_t        *p_begin, *p_end;
496     unsigned int    x = 0, y = 0;
497     vbi_opacity     opacity;
498
499     /* Kludge since zvbi doesn't provide an option to specify opacity. */
500     if( ( fmt.i_chroma != VLC_FOURCC('R','G','B','A' ) ) &&
501         ( fmt.i_chroma != VLC_FOURCC('Y','U','V','A' ) ) )
502     {
503         msg_Err( p_dec, "chroma not supported %4.4s", (char *)&fmt.i_chroma );
504         return VLC_EGENERIC;
505     }
506
507     switch( fmt.i_chroma )
508     {
509         case VLC_FOURCC('R','G','B','A' ):
510             p_begin = (uint32_t *)(*p_src)->p->p_pixels;
511             p_end = (uint32_t *)(*p_src)->p->p_pixels +
512                     ( fmt.i_width * fmt.i_height );
513             break;
514         case VLC_FOURCC('Y','U','V','A' ):
515             p_begin = (uint32_t *)(*p_src)->p[A_PLANE].p_pixels;
516             p_end = (uint32_t *)(*p_src)->p[A_PLANE].p_pixels +
517                 ( (*p_src)->p[A_PLANE].i_lines * (*p_src)->p[A_PLANE].i_pitch );
518             break;
519         default:
520             msg_Err( p_dec, "chroma not supported %4.4s", (char *)&fmt.i_chroma );
521             return VLC_EGENERIC;
522     }
523
524     for( ; p_begin < p_end; p_begin++ )
525     {
526         opacity = p_page.text[ y / 10 * p_page.columns + x / 12 ].opacity;
527         switch( opacity )
528         {
529         /* Show video instead of this character */
530         case VBI_TRANSPARENT_SPACE:
531             *p_begin = 0;
532             break;
533         /* To make the boxed text "closed captioning" transparent
534          * change VLC_TRUE to VLC_FALSE.
535          */
536         case VBI_OPAQUE:
537             if( p_sys->b_opaque )
538                 break;
539         /* Full text transparency. only foreground color is show */
540         case VBI_TRANSPARENT_FULL:
541             *p_begin = 0;
542             break;
543         /* Transparency for boxed text */
544         case VBI_SEMI_TRANSPARENT:
545             if( (*p_begin & 0xffffff00) == 0xff )
546                 *p_begin = 0;
547             break;
548         }
549         x++;
550         if( x >= fmt.i_width )
551         {
552             x = 0;
553             y++;
554         }
555     }
556     /* end of kludge */
557     return VLC_SUCCESS;
558 }
559
560 static int RequestPage( vlc_object_t *p_this, char const *psz_cmd,
561                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
562 {
563     decoder_sys_t   *p_sys = p_data;
564
565     if( (newval.i_int > 0) && (newval.i_int < 999) )
566         p_sys->i_wanted_page = newval.i_int;
567
568     return VLC_SUCCESS;
569 }
570
571 static int Opaque( vlc_object_t *p_this, char const *psz_cmd,
572                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
573 {
574     decoder_sys_t *p_sys = p_data;
575
576     if( p_sys )
577         p_sys->b_opaque = newval.b_bool;
578     return VLC_SUCCESS;
579 }
580
581 static int Position( vlc_object_t *p_this, char const *psz_cmd,
582                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
583 {
584     decoder_sys_t *p_sys = p_data;
585
586     if( p_sys )
587         p_sys->i_align = newval.i_int;
588     return VLC_SUCCESS;
589 }