]> git.sesse.net Git - vlc/blob - modules/video_filter/marq.c
Some more format strings
[vlc] / modules / video_filter / marq.c
1 /*****************************************************************************
2  * marq.c : marquee display video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Mark Moriarty
8  *          Sigmund Augdal Helberg <dnumgis@videolan.org>
9  *          Antoine Cellerier <dionoea . videolan \ org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <string.h>
31
32 #include <time.h>
33
34 #include <vlc/vlc.h>
35 #include <vlc/vout.h>
36
37 #include "vlc_filter.h"
38 #include "vlc_block.h"
39 #include "vlc_osd.h"
40 #include "vlc_playlist.h"
41 #include "vlc_meta.h"
42 #include "vlc_input.h"
43 #include <vlc/aout.h>
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 static int  CreateFilter ( vlc_object_t * );
49 static void DestroyFilter( vlc_object_t * );
50 static subpicture_t *Filter( filter_t *, mtime_t );
51
52
53 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
54                             vlc_value_t oldval, vlc_value_t newval,
55                             void *p_data );
56 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
57                0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
58                0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
59                0x00000080, 0x000000FF, 0x0000FFFF};
60 static char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
61                N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
62                N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
63                N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
64                N_("Aqua") };
65
66 /*****************************************************************************
67  * filter_sys_t: marquee filter descriptor
68  *****************************************************************************/
69 struct filter_sys_t
70 {
71     int i_xoff, i_yoff;  /* offsets for the display string in the video window */
72     int i_pos; /* permit relative positioning (top, bottom, left, right, center) */
73     int i_timeout;
74
75     char *psz_marquee;    /* marquee string */
76
77     text_style_t *p_style; /* font control */
78
79     time_t last_time;
80
81     vlc_bool_t b_need_update;
82 };
83
84 #define MSG_TEXT N_("Text")
85 #define MSG_LONGTEXT N_( \
86     "Marquee text to display. " \
87     "(Available format strings: " \
88     "Time related: %Y = year, %m = month, %d = day, %H = hour, " \
89     "%M = minute, %S = second, ... " \
90     "Meta data related: $a = artist, $b = album, $c = copyright, " \
91     "$d = description, $e = encoded by, $g = genre, " \
92     "$l = language, $n = track num, $p = now playing, " \
93     "$r = rating, $s = subtitles language, $t = title, "\
94     "$u = url, $A = date, " \
95     "$B = audio bitrate (in kb/s), $C = chapter," \
96     "$D = duration, $F = full name with path, $I = title, "\
97     "$L = time left, " \
98     "$N = name, $O = audio language, $P = position (in %), $R = rate, " \
99     "$S = audio sample rate (in kHz), " \
100     "$T = time, $U = publisher, $V = volume, $_ = new line) ")
101 #define POSX_TEXT N_("X offset")
102 #define POSX_LONGTEXT N_("X offset, from the left screen edge." )
103 #define POSY_TEXT N_("Y offset")
104 #define POSY_LONGTEXT N_("Y offset, down from the top." )
105 #define TIMEOUT_TEXT N_("Timeout")
106 #define TIMEOUT_LONGTEXT N_("Number of milliseconds the marquee must remain " \
107                             "displayed. Default value is " \
108                             "0 (remains forever).")
109 #define OPACITY_TEXT N_("Opacity")
110 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
111     "overlayed text. 0 = transparent, 255 = totally opaque. " )
112 #define SIZE_TEXT N_("Font size, pixels")
113 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
114     "font size)." )
115
116 #define COLOR_TEXT N_("Color")
117 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
118     "the video. This must be an hexadecimal (like HTML colors). The first two "\
119     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
120     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
121
122 #define POS_TEXT N_("Marquee position")
123 #define POS_LONGTEXT N_( \
124   "You can enforce the marquee position on the video " \
125   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
126   "also use combinations of these values, eg 6 = top-right).")
127
128 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
129 static char *ppsz_pos_descriptions[] =
130      { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
131      N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
132
133 /*****************************************************************************
134  * Module descriptor
135  *****************************************************************************/
136 vlc_module_begin();
137     set_capability( "sub filter", 0 );
138     set_shortname( N_("Marquee" ));
139     set_callbacks( CreateFilter, DestroyFilter );
140     set_category( CAT_VIDEO );
141     set_subcategory( SUBCAT_VIDEO_SUBPIC );
142     add_string( "marq-marquee", "VLC", NULL, MSG_TEXT, MSG_LONGTEXT,
143                 VLC_FALSE );
144
145     set_section( N_("Position"), NULL );
146     add_integer( "marq-x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
147     add_integer( "marq-y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
148     add_integer( "marq-position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
149
150     set_section( N_("Font"), NULL );
151     /* 5 sets the default to top [1] left [4] */
152     change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
153     add_integer_with_range( "marq-opacity", 255, 0, 255, NULL,
154         OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
155     add_integer( "marq-color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
156                   VLC_FALSE );
157         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
158     add_integer( "marq-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
159
160     set_section( N_("Misc"), NULL );
161     add_integer( "marq-timeout", 0, NULL, TIMEOUT_TEXT, TIMEOUT_LONGTEXT,
162                  VLC_FALSE );
163
164     set_description( _("Marquee display") );
165     add_shortcut( "marq" );
166     add_shortcut( "time" );
167 vlc_module_end();
168
169 /*****************************************************************************
170  * CreateFilter: allocates marquee video filter
171  *****************************************************************************/
172 static int CreateFilter( vlc_object_t *p_this )
173 {
174     filter_t *p_filter = (filter_t *)p_this;
175     filter_sys_t *p_sys;
176
177     /* Allocate structure */
178     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
179     if( p_sys == NULL )
180     {
181         msg_Err( p_filter, "out of memory" );
182         return VLC_ENOMEM;
183     }
184
185     p_sys->p_style = malloc( sizeof( text_style_t ) );
186     memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
187
188 #define CREATE_VAR( stor, type, var ) \
189     p_sys->stor = var_CreateGet##type( p_filter->p_libvlc, var ); \
190     var_AddCallback( p_filter->p_libvlc, var, MarqueeCallback, p_sys );
191
192     CREATE_VAR( i_xoff, Integer, "marq-x" );
193     CREATE_VAR( i_yoff, Integer, "marq-y" );
194     CREATE_VAR( i_timeout,Integer, "marq-timeout" );
195     CREATE_VAR( i_pos, Integer, "marq-position" );
196     CREATE_VAR( psz_marquee, String, "marq-marquee" );
197     CREATE_VAR( p_style->i_font_alpha, Integer, "marq-opacity" );
198     CREATE_VAR( p_style->i_font_color, Integer, "marq-color" );
199     CREATE_VAR( p_style->i_font_size, Integer, "marq-size" );
200
201     p_sys->p_style->i_font_alpha = 255 - p_sys->p_style->i_font_alpha ;
202
203     /* Misc init */
204     p_filter->pf_sub_filter = Filter;
205     p_sys->last_time = ((time_t)-1);
206     p_sys->b_need_update = VLC_TRUE;
207
208     return VLC_SUCCESS;
209 }
210 /*****************************************************************************
211  * DestroyFilter: destroy marquee video filter
212  *****************************************************************************/
213 static void DestroyFilter( vlc_object_t *p_this )
214 {
215     filter_t *p_filter = (filter_t *)p_this;
216     filter_sys_t *p_sys = p_filter->p_sys;
217
218     if( p_sys->p_style ) free( p_sys->p_style );
219     if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
220     free( p_sys );
221
222     /* Delete the marquee variables */
223 #define DEL_VAR(var) \
224     var_DelCallback( p_filter->p_libvlc, var, MarqueeCallback, p_sys ); \
225     var_Destroy( p_filter->p_libvlc, var );
226     DEL_VAR( "marq-x" );
227     DEL_VAR( "marq-y" );
228     DEL_VAR( "marq-marquee" );
229     DEL_VAR( "marq-timeout" );
230     DEL_VAR( "marq-position" );
231     DEL_VAR( "marq-color" );
232     DEL_VAR( "marq-opacity" );
233     DEL_VAR( "marq-size" );
234 }
235 /****************************************************************************
236  * String formating functions
237  ****************************************************************************/
238
239 static char *FormatTime(char *tformat )
240 {
241     char buffer[255];
242     time_t curtime;
243 #if defined(HAVE_LOCALTIME_R)
244     struct tm loctime;
245 #else
246     struct tm *loctime;
247 #endif
248
249     /* Get the current time.  */
250     curtime = time( NULL );
251
252     /* Convert it to local time representation.  */
253 #if defined(HAVE_LOCALTIME_R)
254     localtime_r( &curtime, &loctime );
255     strftime( buffer, 255, tformat, &loctime );
256 #else
257     loctime = localtime( &curtime );
258     strftime( buffer, 255, tformat, loctime );
259 #endif
260     return strdup( buffer );
261 }
262
263 #define INSERT_STRING( check, string )                              \
264                     if( check && string )                           \
265                     {                                               \
266                         int len = strlen( string );                 \
267                         dst = realloc( dst,                         \
268                                        i_size = i_size + len + 1 ); \
269                         strncpy( d, string, len+1 );                \
270                         d += len;                                   \
271                     }                                               \
272                     else                                            \
273                     {                                               \
274                         *d = '-';                                   \
275                         d++;                                        \
276                     }
277 char *FormatMeta( vlc_object_t *p_object, char *string )
278 {
279     char *s = string;
280     char *dst = malloc( 1000 );
281     char *d = dst;
282     int b_is_format = 0;
283     char buf[10];
284     int i_size = strlen( string );
285
286     playlist_t *p_playlist = pl_Yield( p_object );
287     input_thread_t *p_input = p_playlist->p_input;
288     input_item_t *p_item = NULL;
289     pl_Release( p_object );
290     if( p_input )
291     {
292         vlc_object_yield( p_input );
293         p_item = p_input->input.p_item;
294         if( p_item )
295             vlc_mutex_lock( &p_item->lock );
296     }
297
298     sprintf( dst, string );
299
300     while( *s )
301     {
302         if( b_is_format )
303         {
304             switch( *s )
305             {
306                 case 'a':
307                     INSERT_STRING( p_item && p_item->p_meta,
308                                    p_item->p_meta->psz_artist );
309                     break;
310                 case 'b':
311                     INSERT_STRING( p_item && p_item->p_meta,
312                                    p_item->p_meta->psz_album );
313                     break;
314                 case 'c':
315                     INSERT_STRING( p_item && p_item->p_meta,
316                                    p_item->p_meta->psz_copyright );
317                     break;
318                 case 'd':
319                     INSERT_STRING( p_item && p_item->p_meta,
320                                    p_item->p_meta->psz_description );
321                     break;
322                 case 'e':
323                     INSERT_STRING( p_item && p_item->p_meta,
324                                    p_item->p_meta->psz_encodedby );
325                     break;
326                 case 'g':
327                     INSERT_STRING( p_item && p_item->p_meta,
328                                    p_item->p_meta->psz_genre );
329                     break;
330                 case 'l':
331                     INSERT_STRING( p_item && p_item->p_meta,
332                                    p_item->p_meta->psz_language );
333                     break;
334                 case 'n':
335                     INSERT_STRING( p_item && p_item->p_meta,
336                                    p_item->p_meta->psz_tracknum );
337                     break;
338                 case 'p':
339                     INSERT_STRING( p_item && p_item->p_meta,
340                                    p_item->p_meta->psz_nowplaying );
341                     break;
342                 case 'r':
343                     INSERT_STRING( p_item && p_item->p_meta,
344                                    p_item->p_meta->psz_rating );
345                     break;
346                 case 's':
347                 {
348                     char *lang;
349                     if( p_input )
350                     {
351                         lang = var_GetString( p_input, "sub-language" );
352                     }
353                     else
354                     {
355                         lang = strdup( "-" );
356                     }
357                     INSERT_STRING( 1, lang );
358                     break;
359                 }
360                 case 't':
361                     INSERT_STRING( p_item && p_item->p_meta,
362                                    p_item->p_meta->psz_title );
363                     break;
364                 case 'u':
365                     INSERT_STRING( p_item && p_item->p_meta,
366                                    p_item->p_meta->psz_url );
367                     break;
368                 case 'A':
369                     INSERT_STRING( p_item && p_item->p_meta,
370                                    p_item->p_meta->psz_date );
371                     break;
372                 case 'B':
373                     if( p_input )
374                     {
375                         snprintf( buf, 10, "%d",
376                                   var_GetInteger( p_input, "bit-rate" )/1000 );
377                     }
378                     else
379                     {
380                         sprintf( buf, "-" );
381                     }
382                     INSERT_STRING( 1, buf );
383                     break;
384                 case 'C':
385                     if( p_input )
386                     {
387                         snprintf( buf, 10, "%d",
388                                   var_GetInteger( p_input, "chapter" ) );
389                     }
390                     else
391                     {
392                         sprintf( buf, "-" );
393                     }
394                     INSERT_STRING( 1, buf );
395                     break;
396                 case 'D':
397                     if( p_item )
398                     {
399                         sprintf( buf, "%02d:%02d:%02d",
400                                  (int)(p_item->i_duration/(3600000000)),
401                                  (int)((p_item->i_duration/(60000000))%60),
402                                  (int)((p_item->i_duration/1000000)%60) );
403                     }
404                     else
405                     {
406                         sprintf( buf, "--:--:--" );
407                     }
408                     INSERT_STRING( 1, buf );
409                     break;
410                 case 'F':
411                     INSERT_STRING( p_item, p_item->psz_uri );
412                     break;
413                 case 'I':
414                     if( p_input )
415                     {
416                         snprintf( buf, 10, "%d",
417                                   var_GetInteger( p_input, "title" ) );
418                     }
419                     else
420                     {
421                         sprintf( buf, "-" );
422                     }
423                     INSERT_STRING( 1, buf );
424                     break;
425                 case 'L':
426                     if( p_item && p_input )
427                     {
428                         sprintf( buf, "%02d:%02d:%02d",
429                      (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
430                      (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
431                      (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
432                     }
433                     else
434                     {
435                         sprintf( buf, "--:--:--" );
436                     }
437                     INSERT_STRING( 1, buf );
438                     break;
439                 case 'N':
440                     INSERT_STRING( p_item, p_item->psz_name );
441                     break;
442                 case 'O':
443                 {
444                     char *lang;
445                     if( p_input )
446                     {
447                         lang = var_GetString( p_input, "audio-language" );
448                     }
449                     else
450                     {
451                         lang = strdup( "-" );
452                     }
453                     INSERT_STRING( 1, lang );
454                     break;
455                 }
456                 case 'P':
457                     if( p_input )
458                     {
459                         snprintf( buf, 10, "%2.1lf",
460                                   var_GetFloat( p_input, "position" ) * 100. );
461                     }
462                     else
463                     {
464                         sprintf( buf, "--.-%%" );
465                     }
466                     INSERT_STRING( 1, buf );
467                     break;
468                 case 'R':
469                     if( p_input )
470                     {
471                         int r = var_GetInteger( p_input, "rate" );
472                         snprintf( buf, 10, "%d.%d", r/1000, r%1000 );
473                     }
474                     else
475                     {
476                         sprintf( buf, "-" );
477                     }
478                     INSERT_STRING( 1, buf );
479                     break;
480                 case 'S':
481                     if( p_input )
482                     {
483                         int r = var_GetInteger( p_input, "sample-rate" );
484                         snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
485                     }
486                     else
487                     {
488                         sprintf( buf, "-" );
489                     }
490                     INSERT_STRING( 1, buf );
491                     break;
492                 case 'T':
493                     if( p_input )
494                     {
495                         sprintf( buf, "%02d:%02d:%02d",
496                                  (int)(p_input->i_time/(3600000000)),
497                                  (int)((p_input->i_time/(60000000))%60),
498                                  (int)((p_input->i_time/1000000)%60) );
499                     }
500                     else
501                     {
502                         sprintf( buf, "--:--:--" );
503                     }
504                     INSERT_STRING( 1, buf );
505                     break;
506                 case 'U':
507                     INSERT_STRING( p_item && p_item->p_meta,
508                                    p_item->p_meta->psz_publisher );
509                     break;
510                 case 'V':
511                 {
512                     audio_volume_t volume;
513                     aout_VolumeGet( p_object, &volume );
514                     snprintf( buf, 10, "%d", volume );
515                     INSERT_STRING( 1, buf );
516                     break;
517                 }
518                 case '_':
519                     *d = '\n';
520                     d++;
521                     break;
522
523                 default:
524                     *d = *s;
525                     d++;
526                     break;
527             }
528             b_is_format = 0;
529         }
530         else if( *s == '$' )
531         {
532             b_is_format = 1;
533         }
534         else
535         {
536             *d = *s;
537             d++;
538         }
539         s++;
540     }
541     *d = '\0';
542
543     if( p_input )
544     {
545         vlc_object_release( p_input );
546         if( p_item )
547             vlc_mutex_unlock( &p_item->lock );
548     }
549
550     return dst;
551 }
552
553 /****************************************************************************
554  * Filter: the whole thing
555  ****************************************************************************
556  * This function outputs subpictures at regular time intervals.
557  ****************************************************************************/
558 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
559 {
560     filter_sys_t *p_sys = p_filter->p_sys;
561     subpicture_t *p_spu;
562     video_format_t fmt;
563     time_t t;
564     char *buf;
565
566     if( p_sys->last_time == time( NULL ) )
567     {
568         return NULL;
569     }
570
571 /*    if( p_sys->b_need_update == VLC_FALSE )
572     {
573         return NULL;
574     }*/
575
576     p_spu = p_filter->pf_sub_buffer_new( p_filter );
577     if( !p_spu ) return NULL;
578
579     memset( &fmt, 0, sizeof(video_format_t) );
580     fmt.i_chroma = VLC_FOURCC('T','E','X','T');
581     fmt.i_aspect = 0;
582     fmt.i_width = fmt.i_height = 0;
583     fmt.i_x_offset = 0;
584     fmt.i_y_offset = 0;
585     p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
586     if( !p_spu->p_region )
587     {
588         p_filter->pf_sub_buffer_del( p_filter, p_spu );
589         return NULL;
590     }
591
592     t = p_sys->last_time = time( NULL );
593
594     buf = FormatTime( p_sys->psz_marquee );
595     p_spu->p_region->psz_text = FormatMeta( VLC_OBJECT( p_filter ), buf );
596     free( buf );
597     p_spu->i_start = date;
598     p_spu->i_stop  = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000;
599     p_spu->b_ephemer = VLC_TRUE;
600
601     /*  where to locate the string: */
602     if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
603     {   /* set to one of the 9 relative locations */
604         p_spu->i_flags = p_sys->i_pos;
605         p_spu->i_x = 0;
606         p_spu->i_y = 0;
607         p_spu->b_absolute = VLC_FALSE;
608     }
609     else
610     {   /*  set to an absolute xy, referenced to upper left corner */
611         p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
612         p_spu->i_x = p_sys->i_xoff;
613         p_spu->i_y = p_sys->i_yoff;
614         p_spu->b_absolute = VLC_TRUE;
615     }
616     p_spu->p_region->p_style = p_sys->p_style;
617
618     p_sys->b_need_update = VLC_FALSE;
619     return p_spu;
620 }
621
622 /**********************************************************************
623  * Callback to update params on the fly
624  **********************************************************************/
625 static int MarqueeCallback( vlc_object_t *p_this, char const *psz_var,
626                             vlc_value_t oldval, vlc_value_t newval,
627                             void *p_data )
628 {
629     filter_sys_t *p_sys = (filter_sys_t *) p_data;
630
631     if( !strncmp( psz_var, "marq-marquee", 7 ) )
632     {
633         if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
634         p_sys->psz_marquee = strdup( newval.psz_string );
635     }
636     else if ( !strncmp( psz_var, "marq-x", 6 ) )
637     {
638         p_sys->i_xoff = newval.i_int;
639     }
640     else if ( !strncmp( psz_var, "marq-y", 6 ) )
641     {
642         p_sys->i_yoff = newval.i_int;
643     }
644     else if ( !strncmp( psz_var, "marq-color", 8 ) )  /* "marq-col" */
645     {
646         p_sys->p_style->i_font_color = newval.i_int;
647     }
648     else if ( !strncmp( psz_var, "marq-opacity", 8 ) ) /* "marq-opa" */
649     {
650         p_sys->p_style->i_font_alpha = 255 - newval.i_int;
651     }
652     else if ( !strncmp( psz_var, "marq-size", 6 ) )
653     {
654         p_sys->p_style->i_font_size = newval.i_int;
655     }
656     else if ( !strncmp( psz_var, "marq-timeout", 12 ) )
657     {
658         p_sys->i_timeout = newval.i_int;
659     }
660     else if ( !strncmp( psz_var, "marq-position", 8 ) )
661     /* willing to accept a match against marq-pos */
662     {
663         p_sys->i_pos = newval.i_int;
664         p_sys->i_xoff = -1;       /* force to relative positioning */
665     }
666     p_sys->b_need_update = VLC_TRUE;
667     return VLC_SUCCESS;
668 }