1 /*****************************************************************************
2 * dynamicoverlay.c : dynamic overlay plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2007 the VideoLAN team
7 * Author: Soren Bog <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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
41 #include "dynamicoverlay.h"
43 /*****************************************************************************
45 *****************************************************************************/
46 static int Create( vlc_object_t * );
47 static void Destroy( vlc_object_t * );
48 static subpicture_t *Filter( filter_t *, mtime_t );
50 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
51 vlc_value_t oldval, vlc_value_t newval,
54 /*****************************************************************************
56 *****************************************************************************/
58 #define INPUT_TEXT N_("Input FIFO")
59 #define INPUT_LONGTEXT N_("FIFO which will be read for commands")
61 #define OUTPUT_TEXT N_("Output FIFO")
62 #define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
65 set_description( N_("Dynamic video overlay") )
66 set_shortname( N_("Overlay" ))
67 set_category( CAT_VIDEO )
68 set_subcategory( SUBCAT_VIDEO_VFILTER )
69 set_capability( "sub filter", 0 )
71 add_file( "overlay-input", NULL, NULL, INPUT_TEXT, INPUT_LONGTEXT,
73 add_file( "overlay-output", NULL, NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
76 add_shortcut( "overlay" )
77 set_callbacks( Create, Destroy )
80 static const char *const ppsz_filter_options[] = {
81 "input", "output", NULL
84 /*****************************************************************************
85 * Create: allocates adjust video thread output method
86 *****************************************************************************
87 * This function allocates and initializes a adjust vout method.
88 *****************************************************************************/
89 static int Create( vlc_object_t *p_this )
91 filter_t *p_filter = (filter_t *)p_this;
94 /* Allocate structure */
95 p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
96 if( p_filter->p_sys == NULL )
98 p_sys = p_filter->p_sys;
100 BufferInit( &p_sys->input );
101 BufferInit( &p_sys->output );
102 QueueInit( &p_sys->atomic );
103 QueueInit( &p_sys->pending );
104 QueueInit( &p_sys->processed );
105 ListInit( &p_sys->overlays );
107 p_sys->i_inputfd = -1;
108 p_sys->i_outputfd = -1;
109 p_sys->b_updated = true;
110 p_sys->b_atomic = false;
112 p_filter->pf_sub_filter = Filter;
114 config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
117 p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
119 p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
122 var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
123 var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
125 RegisterCommand( p_filter );
129 /*****************************************************************************
130 * Destroy: destroy adjust video thread output method
131 *****************************************************************************
132 * Terminate an output method created by adjustCreateOutputMethod
133 *****************************************************************************/
134 static void Destroy( vlc_object_t *p_this )
136 filter_t *p_filter = (filter_t *)p_this;
138 BufferDestroy( &p_filter->p_sys->input );
139 BufferDestroy( &p_filter->p_sys->output );
140 QueueDestroy( &p_filter->p_sys->atomic );
141 QueueDestroy( &p_filter->p_sys->pending );
142 QueueDestroy( &p_filter->p_sys->processed );
143 ListDestroy( &p_filter->p_sys->overlays );
144 UnregisterCommand( p_filter );
146 free( p_filter->p_sys->psz_inputfile );
147 free( p_filter->p_sys->psz_outputfile );
148 free( p_filter->p_sys );
151 /*****************************************************************************
152 * Render: displays previously rendered output
153 *****************************************************************************
154 * This function send the currently rendered image to adjust modified image,
155 * waits until it is displayed and switch the two rendering buffers, preparing
157 *****************************************************************************/
158 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
160 filter_sys_t *p_sys = p_filter->p_sys;
162 /* We might need to open these at any time. */
163 if( p_sys->i_inputfd == -1 )
165 p_sys->i_inputfd = open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
166 if( p_sys->i_inputfd == -1 )
168 msg_Warn( p_filter, "Failed to grab input file: %s (%s)",
169 p_sys->psz_inputfile, strerror( errno ) );
173 msg_Info( p_filter, "Grabbed input file: %s",
174 p_sys->psz_inputfile );
178 if( p_sys->i_outputfd == -1 )
180 p_sys->i_outputfd = open( p_sys->psz_outputfile,
181 O_WRONLY | O_NONBLOCK );
182 if( p_sys->i_outputfd == -1 )
186 msg_Warn( p_filter, "Failed to grab output file: %s (%s)",
187 p_sys->psz_outputfile, strerror( errno ) );
192 msg_Info( p_filter, "Grabbed output file: %s",
193 p_sys->psz_outputfile );
197 /* Read any waiting commands */
198 if( p_sys->i_inputfd != -1 )
201 ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
204 /* We hit an error */
205 if( errno != EAGAIN )
207 msg_Warn( p_filter, "Error on input file: %s",
209 close( p_sys->i_inputfd );
210 p_sys->i_inputfd = -1;
213 else if( i_len == 0 )
215 /* We hit the end-of-file */
219 BufferAdd( &p_sys->input, p_buffer, i_len );
223 /* Parse any complete commands */
225 while( ( p_end = memchr( p_sys->input.p_begin, '\n',
226 p_sys->input.i_length ) ) )
228 commanddesc_t *p_cur = NULL;
229 bool b_found = false;
233 p_cmd = BufferGetToken( &p_sys->input );
235 msg_Info( p_filter, "Search command: %s", p_cmd );
236 for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
238 p_cur = p_sys->pp_commands[i_index];
239 if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
241 p_cmd[strlen(p_cur->psz_command)] = '\0';
249 /* No matching command */
250 msg_Err( p_filter, "Got invalid command: %s", p_cmd );
251 BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
255 msg_Info( p_filter, "Got valid command: %s", p_cmd );
257 command_t *p_cmddesc = malloc( sizeof( command_t ) );
261 p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
262 p_cmddesc->p_command = p_cur;
263 p_cmddesc->p_command->pf_parser( p_cmd, p_end,
264 &p_cmddesc->params );
266 if( ( p_cmddesc->p_command->b_atomic == true ) &&
267 ( p_sys->b_atomic == true ) )
268 QueueEnqueue( &p_sys->atomic, p_cmddesc );
270 QueueEnqueue( &p_sys->pending, p_cmddesc );
273 BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
276 /* Process any pending commands */
277 command_t *p_command = NULL;
278 while( (p_command = QueueDequeue( &p_sys->pending )) )
280 p_command->i_status =
281 p_command->p_command->pf_execute( p_filter, &p_command->params,
282 &p_command->results );
283 QueueEnqueue( &p_sys->processed, p_command );
286 /* Output any processed commands */
287 while( (p_command = QueueDequeue( &p_sys->processed )) )
289 if( p_command->i_status == VLC_SUCCESS )
291 const char *psz_success = "SUCCESS:";
292 const char *psz_nl = "\n";
293 BufferAdd( &p_sys->output, psz_success, 8 );
294 p_command->p_command->pf_unparse( &p_command->results,
296 BufferAdd( &p_sys->output, psz_nl, 1 );
300 BufferPrintf( &p_sys->output, "FAILURE: %d\n",
301 p_command->i_status );
305 /* Try emptying the output buffer */
306 if( p_sys->i_outputfd != -1 )
308 ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
309 p_sys->output.i_length );
312 /* We hit an error */
313 if( errno != EAGAIN )
315 msg_Warn( p_filter, "Error on output file: %s",
317 close( p_sys->i_outputfd );
318 p_sys->i_outputfd = -1;
323 BufferDel( &p_sys->output, i_len );
327 if( p_sys->b_updated == false )
330 subpicture_t *p_spu = NULL;
331 overlay_t *p_overlay = NULL;
333 p_spu = p_filter->pf_sub_buffer_new( p_filter );
336 msg_Err( p_filter, "cannot allocate subpicture" );
340 p_spu->b_absolute = true;
341 p_spu->i_start = date;
343 p_spu->b_ephemer = true;
345 subpicture_region_t **pp_region = &p_spu->p_region;
346 while( (p_overlay = ListWalk( &p_sys->overlays )) )
348 subpicture_region_t *p_region;
350 *pp_region = p_region = subpicture_region_New( &p_overlay->format );
354 msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
355 (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
356 p_overlay->i_alpha );
358 if( p_overlay->format.i_chroma == VLC_FOURCC('T','E','X','T') )
360 p_region->psz_text = strdup( p_overlay->data.p_text );
361 p_region->p_style = text_style_Duplicate( p_overlay->p_fontstyle );
365 /* FIXME the copy is probably not needed anymore */
366 picture_Copy( p_region->p_picture, p_overlay->data.p_pic );
368 p_region->i_x = p_overlay->i_x;
369 p_region->i_y = p_overlay->i_y;
370 p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
371 p_region->i_alpha = p_overlay->i_alpha;
372 pp_region = &p_region->p_next;
375 p_sys->b_updated = false;
379 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
380 vlc_value_t oldval, vlc_value_t newval,
383 filter_sys_t *p_sys = (filter_sys_t *)p_data;
384 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
386 if( !strncmp( psz_var, "overlay-input", 13 ) )
387 p_sys->psz_inputfile = newval.psz_string;
388 else if( !strncmp( psz_var, "overlay-output", 14 ) )
389 p_sys->psz_outputfile = newval.psz_string;