1 /*****************************************************************************
2 * dynamicoverlay.c : dynamic overlay plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2007 the VideoLAN team
7 * Author: Søren Bøg <avacore@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_filter.h>
40 #include "dynamicoverlay.h"
42 /*****************************************************************************
44 *****************************************************************************/
45 static int Create( vlc_object_t * );
46 static void Destroy( vlc_object_t * );
47 static subpicture_t *Filter( filter_t *, mtime_t );
49 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
50 vlc_value_t oldval, vlc_value_t newval,
53 /*****************************************************************************
55 *****************************************************************************/
57 #define INPUT_TEXT N_("Input FIFO")
58 #define INPUT_LONGTEXT N_("FIFO which will be read for commands")
60 #define OUTPUT_TEXT N_("Output FIFO")
61 #define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
64 set_description( _("Dynamic video overlay") );
65 set_shortname( _("Overlay" ));
66 set_category( CAT_VIDEO );
67 set_subcategory( SUBCAT_VIDEO_VFILTER );
68 set_capability( "sub filter", 0 );
70 add_file( "overlay-input", NULL, NULL, INPUT_TEXT, INPUT_LONGTEXT,
72 add_file( "overlay-output", NULL, NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
75 add_shortcut( "overlay" );
76 set_callbacks( Create, Destroy );
79 static const char *ppsz_filter_options[] = {
80 "input", "output", NULL
83 /*****************************************************************************
84 * Create: allocates adjust video thread output method
85 *****************************************************************************
86 * This function allocates and initializes a adjust vout method.
87 *****************************************************************************/
88 static int Create( vlc_object_t *p_this )
90 filter_t *p_filter = (filter_t *)p_this;
93 /* Allocate structure */
94 p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
95 if( p_filter->p_sys == NULL )
97 msg_Err( p_filter, "out of memory" );
100 p_sys = p_filter->p_sys;
102 BufferInit( &p_sys->input );
103 BufferInit( &p_sys->output );
104 QueueInit( &p_sys->atomic );
105 QueueInit( &p_sys->pending );
106 QueueInit( &p_sys->processed );
107 ListInit( &p_sys->overlays );
109 p_sys->i_inputfd = -1;
110 p_sys->i_outputfd = -1;
111 p_sys->b_updated = VLC_TRUE;
112 p_sys->b_atomic = VLC_FALSE;
114 p_filter->pf_sub_filter = Filter;
116 config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
119 p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
121 p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
124 var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
125 var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
127 RegisterCommand( p_filter );
131 /*****************************************************************************
132 * Destroy: destroy adjust video thread output method
133 *****************************************************************************
134 * Terminate an output method created by adjustCreateOutputMethod
135 *****************************************************************************/
136 static void Destroy( vlc_object_t *p_this )
138 filter_t *p_filter = (filter_t *)p_this;
140 BufferDestroy( &p_filter->p_sys->input );
141 BufferDestroy( &p_filter->p_sys->output );
142 QueueDestroy( &p_filter->p_sys->atomic );
143 QueueDestroy( &p_filter->p_sys->pending );
144 QueueDestroy( &p_filter->p_sys->processed );
145 ListDestroy( &p_filter->p_sys->overlays );
146 UnregisterCommand( p_filter );
148 free( p_filter->p_sys->psz_inputfile );
149 free( p_filter->p_sys->psz_outputfile );
150 free( p_filter->p_sys );
153 /*****************************************************************************
154 * Render: displays previously rendered output
155 *****************************************************************************
156 * This function send the currently rendered image to adjust modified image,
157 * waits until it is displayed and switch the two rendering buffers, preparing
159 *****************************************************************************/
160 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
162 filter_sys_t *p_sys = p_filter->p_sys;
164 /* We might need to open these at any time. */
165 if( p_sys->i_inputfd == -1 )
167 p_sys->i_inputfd = open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
168 if( p_sys->i_inputfd == -1 )
170 msg_Warn( p_filter, "Failed to grab input file: %s (%s)",
171 p_sys->psz_inputfile, strerror( errno ) );
175 msg_Info( p_filter, "Grabbed input file: %s",
176 p_sys->psz_inputfile );
180 if( p_sys->i_outputfd == -1 )
182 p_sys->i_outputfd = open( p_sys->psz_outputfile,
183 O_WRONLY | O_NONBLOCK );
184 if( p_sys->i_outputfd == -1 )
188 msg_Warn( p_filter, "Failed to grab output file: %s (%s)",
189 p_sys->psz_outputfile, strerror( errno ) );
194 msg_Info( p_filter, "Grabbed output file: %s",
195 p_sys->psz_outputfile );
199 /* Read any waiting commands */
200 if( p_sys->i_inputfd != -1 )
203 ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
206 /* We hit an error */
207 if( errno != EAGAIN )
209 msg_Warn( p_filter, "Error on input file: %s",
211 close( p_sys->i_inputfd );
212 p_sys->i_inputfd = -1;
215 else if( i_len == 0 )
217 /* We hit the end-of-file */
221 BufferAdd( &p_sys->input, p_buffer, i_len );
225 /* Parse any complete commands */
227 while( ( p_end = memchr( p_sys->input.p_begin, '\n',
228 p_sys->input.i_length ) ) )
230 commanddesc_t *p_cur = NULL;
231 vlc_bool_t b_found = VLC_FALSE;
235 p_cmd = BufferGetToken( &p_sys->input );
237 msg_Info( p_filter, "Search command: %s", p_cmd );
238 for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
240 p_cur = p_sys->pp_commands[i_index];
241 if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
243 p_cmd[strlen(p_cur->psz_command)] = '\0';
251 /* No matching command */
252 msg_Err( p_filter, "Got invalid command: %s", p_cmd );
253 BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
257 msg_Info( p_filter, "Got valid command: %s", p_cmd );
259 command_t *p_cmddesc = malloc( sizeof( command_t ) );
263 p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
264 p_cmddesc->p_command = p_cur;
265 p_cmddesc->p_command->pf_parser( p_cmd, p_end,
266 &p_cmddesc->params );
268 if( ( p_cmddesc->p_command->b_atomic == VLC_TRUE ) &&
269 ( p_sys->b_atomic == VLC_TRUE ) )
270 QueueEnqueue( &p_sys->atomic, p_cmddesc );
272 QueueEnqueue( &p_sys->pending, p_cmddesc );
275 BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
278 /* Process any pending commands */
279 command_t *p_command = NULL;
280 while( (p_command = QueueDequeue( &p_sys->pending )) )
282 p_command->i_status =
283 p_command->p_command->pf_execute( p_filter, &p_command->params,
284 &p_command->results );
285 QueueEnqueue( &p_sys->processed, p_command );
288 /* Output any processed commands */
289 while( (p_command = QueueDequeue( &p_sys->processed )) )
291 if( p_command->i_status == VLC_SUCCESS )
293 const char *psz_success = "SUCCESS:";
294 const char *psz_nl = "\n";
295 BufferAdd( &p_sys->output, psz_success, 8 );
296 p_command->p_command->pf_unparse( &p_command->results,
298 BufferAdd( &p_sys->output, psz_nl, 1 );
302 BufferPrintf( &p_sys->output, "FAILURE: %d\n",
303 p_command->i_status );
307 /* Try emptying the output buffer */
308 if( p_sys->i_outputfd != -1 )
310 ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
311 p_sys->output.i_length );
314 /* We hit an error */
315 if( errno != EAGAIN )
317 msg_Warn( p_filter, "Error on output file: %s",
319 close( p_sys->i_outputfd );
320 p_sys->i_outputfd = -1;
325 BufferDel( &p_sys->output, i_len );
329 if( p_sys->b_updated == VLC_FALSE )
332 subpicture_t *p_spu = NULL;
333 overlay_t *p_overlay = NULL;
335 p_spu = p_filter->pf_sub_buffer_new( p_filter );
338 msg_Err( p_filter, "cannot allocate subpicture" );
342 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
345 p_spu->b_absolute = VLC_TRUE;
346 p_spu->i_start = date;
348 p_spu->b_ephemer = VLC_TRUE;
350 subpicture_region_t **pp_region = &p_spu->p_region;
351 while( (p_overlay = ListWalk( &p_sys->overlays )) )
353 msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
354 (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
355 p_overlay->i_alpha );
357 if( p_overlay->format.i_chroma == VLC_FOURCC('T','E','X','T') )
359 *pp_region = p_spu->pf_create_region( VLC_OBJECT(p_filter),
360 &p_overlay->format );
363 (*pp_region)->psz_text = strdup( p_overlay->data.p_text );
364 (*pp_region)->p_style = malloc( sizeof(struct text_style_t) );
365 if( !(*pp_region)->p_style )
367 p_spu->pf_destroy_region( VLC_OBJECT(p_filter), (*pp_region) );
371 p_filter->p_libvlc->pf_memcpy( (*pp_region)->p_style,
372 &p_overlay->fontstyle,
373 sizeof(struct text_style_t) );
378 if( vout_AllocatePicture( p_filter, &clone,
379 p_overlay->format.i_chroma,
380 p_overlay->format.i_width,
381 p_overlay->format.i_height,
382 p_overlay->format.i_aspect ) )
384 msg_Err( p_filter, "cannot allocate picture" );
387 vout_CopyPicture( p_filter, &clone, p_overlay->data.p_pic );
388 *pp_region = p_spu->pf_make_region( VLC_OBJECT(p_filter),
393 msg_Err( p_filter, "cannot allocate subpicture region" );
397 (*pp_region)->i_x = p_overlay->i_x;
398 (*pp_region)->i_y = p_overlay->i_y;
399 (*pp_region)->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
400 (*pp_region)->i_alpha = p_overlay->i_alpha;
401 pp_region = &(*pp_region)->p_next;
404 p_sys->b_updated = VLC_FALSE;
408 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
409 vlc_value_t oldval, vlc_value_t newval,
412 filter_sys_t *p_sys = (filter_sys_t *)p_data;
413 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
415 if( !strncmp( psz_var, "overlay-input", 13 ) )
416 p_sys->psz_inputfile = newval.psz_string;
417 else if( !strncmp( psz_var, "overlay-output", 14 ) )
418 p_sys->psz_outputfile = newval.psz_string;