Refactor: rename "sub-filter" to "sub-source" this includes capability, functions...
[vlc.git] / modules / video_filter / dynamicoverlay / dynamicoverlay.c
blob52dba04e8f3dbab1980aed66e9366fce09d77ef9
1 /*****************************************************************************
2 * dynamicoverlay.c : dynamic overlay plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2007 the VideoLAN team
5 * $Id$
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 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_sout.h>
34 #include <vlc_vout.h>
35 #include <vlc_filter.h>
36 #include <vlc_osd.h>
37 #include <vlc_fs.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <errno.h>
43 #include "dynamicoverlay.h"
45 /*****************************************************************************
46 * Local prototypes
47 *****************************************************************************/
48 static int Create( vlc_object_t * );
49 static void Destroy( vlc_object_t * );
50 static subpicture_t *Filter( filter_t *, mtime_t );
52 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
53 vlc_value_t oldval, vlc_value_t newval,
54 void *p_data );
56 /*****************************************************************************
57 * Module descriptor
58 *****************************************************************************/
60 #define INPUT_TEXT N_("Input FIFO")
61 #define INPUT_LONGTEXT N_("FIFO which will be read for commands")
63 #define OUTPUT_TEXT N_("Output FIFO")
64 #define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
66 vlc_module_begin ()
67 set_description( N_("Dynamic video overlay") )
68 set_shortname( N_("Overlay" ))
69 set_category( CAT_VIDEO )
70 set_subcategory( SUBCAT_VIDEO_VFILTER )
71 set_capability( "sub source", 0 )
73 add_loadfile( "overlay-input", NULL, INPUT_TEXT, INPUT_LONGTEXT,
74 false )
75 /* Note: add_loadfile as O_WRONLY w/o O_CREAT, i.e. FIFO must exist */
76 add_loadfile( "overlay-output", NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
77 false )
79 add_shortcut( "overlay" )
80 set_callbacks( Create, Destroy )
81 vlc_module_end ()
83 static const char *const ppsz_filter_options[] = {
84 "input", "output", NULL
87 /*****************************************************************************
88 * Create: allocates adjust video thread output method
89 *****************************************************************************
90 * This function allocates and initializes a adjust vout method.
91 *****************************************************************************/
92 static int Create( vlc_object_t *p_this )
94 filter_t *p_filter = (filter_t *)p_this;
95 filter_sys_t *p_sys;
97 /* Allocate structure */
98 p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
99 if( p_filter->p_sys == NULL )
100 return VLC_ENOMEM;
101 p_sys = p_filter->p_sys;
103 BufferInit( &p_sys->input );
104 BufferInit( &p_sys->output );
105 QueueInit( &p_sys->atomic );
106 QueueInit( &p_sys->pending );
107 QueueInit( &p_sys->processed );
108 ListInit( &p_sys->overlays );
110 p_sys->i_inputfd = -1;
111 p_sys->i_outputfd = -1;
112 p_sys->b_updated = true;
113 p_sys->b_atomic = false;
114 vlc_mutex_init( &p_sys->lock );
116 p_filter->pf_sub_source = Filter;
118 config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
119 p_filter->p_cfg );
121 p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
122 "overlay-input" );
123 p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
124 "overlay-output" );
126 var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
127 var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
129 RegisterCommand( p_filter );
130 return VLC_SUCCESS;
133 /*****************************************************************************
134 * Destroy: destroy adjust video thread output method
135 *****************************************************************************
136 * Terminate an output method created by adjustCreateOutputMethod
137 *****************************************************************************/
138 static void Destroy( vlc_object_t *p_this )
140 filter_t *p_filter = (filter_t *)p_this;
141 filter_sys_t *p_sys = p_filter->p_sys;
143 BufferDestroy( &p_sys->input );
144 BufferDestroy( &p_sys->output );
145 QueueDestroy( &p_sys->atomic );
146 QueueDestroy( &p_sys->pending );
147 QueueDestroy( &p_sys->processed );
148 ListDestroy( &p_sys->overlays );
149 UnregisterCommand( p_filter );
151 var_DelCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
152 var_DelCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
154 vlc_mutex_destroy( &p_sys->lock );
155 free( p_sys->psz_inputfile );
156 free( p_sys->psz_outputfile );
157 free( p_sys );
160 /*****************************************************************************
161 * Render: displays previously rendered output
162 *****************************************************************************
163 * This function send the currently rendered image to adjust modified image,
164 * waits until it is displayed and switch the two rendering buffers, preparing
165 * next frame.
166 *****************************************************************************/
167 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
169 filter_sys_t *p_sys = p_filter->p_sys;
171 /* We might need to open these at any time. */
172 vlc_mutex_lock( &p_sys->lock );
173 if( p_sys->i_inputfd == -1 )
175 p_sys->i_inputfd = vlc_open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
176 if( p_sys->i_inputfd == -1 )
178 msg_Warn( p_filter, "Failed to grab input file: %s (%m)",
179 p_sys->psz_inputfile );
181 else
183 msg_Info( p_filter, "Grabbed input file: %s",
184 p_sys->psz_inputfile );
188 if( p_sys->i_outputfd == -1 )
190 p_sys->i_outputfd = vlc_open( p_sys->psz_outputfile,
191 O_WRONLY | O_NONBLOCK );
192 if( p_sys->i_outputfd == -1 )
194 if( errno != ENXIO )
196 msg_Warn( p_filter, "Failed to grab output file: %s (%m)",
197 p_sys->psz_outputfile );
200 else
202 msg_Info( p_filter, "Grabbed output file: %s",
203 p_sys->psz_outputfile );
206 vlc_mutex_unlock( &p_sys->lock );
208 /* Read any waiting commands */
209 if( p_sys->i_inputfd != -1 )
211 char p_buffer[1024];
212 ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
213 if( i_len == -1 )
215 /* We hit an error */
216 if( errno != EAGAIN )
218 msg_Warn( p_filter, "Error on input file: %m" );
219 close( p_sys->i_inputfd );
220 p_sys->i_inputfd = -1;
223 else if( i_len == 0 )
225 /* We hit the end-of-file */
227 else
229 BufferAdd( &p_sys->input, p_buffer, i_len );
233 /* Parse any complete commands */
234 char *p_end, *p_cmd;
235 while( ( p_end = memchr( p_sys->input.p_begin, '\n',
236 p_sys->input.i_length ) ) )
238 commanddesc_t *p_cur = NULL;
239 bool b_found = false;
240 size_t i_index = 0;
242 *p_end = '\0';
243 p_cmd = BufferGetToken( &p_sys->input );
245 msg_Info( p_filter, "Search command: %s", p_cmd );
246 for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
248 p_cur = p_sys->pp_commands[i_index];
249 if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
251 p_cmd[strlen(p_cur->psz_command)] = '\0';
252 b_found = true;
253 break;
257 if( !b_found )
259 /* No matching command */
260 msg_Err( p_filter, "Got invalid command: %s", p_cmd );
261 BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
263 else
265 msg_Info( p_filter, "Got valid command: %s", p_cmd );
267 command_t *p_cmddesc = malloc( sizeof( command_t ) );
268 if( !p_cmddesc )
269 return NULL;
271 p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
272 p_cmddesc->p_command = p_cur;
273 p_cmddesc->p_command->pf_parser( p_cmd, p_end,
274 &p_cmddesc->params );
276 if( p_cmddesc->p_command->b_atomic && p_sys->b_atomic )
277 QueueEnqueue( &p_sys->atomic, p_cmddesc );
278 else
279 QueueEnqueue( &p_sys->pending, p_cmddesc );
282 BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
285 /* Process any pending commands */
286 command_t *p_command = NULL;
287 while( (p_command = QueueDequeue( &p_sys->pending )) )
289 p_command->i_status =
290 p_command->p_command->pf_execute( p_filter, &p_command->params,
291 &p_command->results );
292 QueueEnqueue( &p_sys->processed, p_command );
295 /* Output any processed commands */
296 while( (p_command = QueueDequeue( &p_sys->processed )) )
298 if( p_command->i_status == VLC_SUCCESS )
300 const char *psz_success = "SUCCESS:";
301 const char *psz_nl = "\n";
302 BufferAdd( &p_sys->output, psz_success, 8 );
303 p_command->p_command->pf_unparse( &p_command->results,
304 &p_sys->output );
305 BufferAdd( &p_sys->output, psz_nl, 1 );
307 else
309 BufferPrintf( &p_sys->output, "FAILURE: %d\n",
310 p_command->i_status );
314 /* Try emptying the output buffer */
315 if( p_sys->i_outputfd != -1 )
317 ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
318 p_sys->output.i_length );
319 if( i_len == -1 )
321 /* We hit an error */
322 if( errno != EAGAIN )
324 msg_Warn( p_filter, "Error on output file: %m" );
325 close( p_sys->i_outputfd );
326 p_sys->i_outputfd = -1;
329 else
331 BufferDel( &p_sys->output, i_len );
335 if( !p_sys->b_updated )
336 return NULL;
338 subpicture_t *p_spu = NULL;
339 overlay_t *p_overlay = NULL;
341 p_spu = p_filter->pf_sub_buffer_new( p_filter );
342 if( !p_spu )
344 msg_Err( p_filter, "cannot allocate subpicture" );
345 return NULL;
348 p_spu->b_absolute = true;
349 p_spu->i_start = date;
350 p_spu->i_stop = 0;
351 p_spu->b_ephemer = true;
353 subpicture_region_t **pp_region = &p_spu->p_region;
354 while( (p_overlay = ListWalk( &p_sys->overlays )) )
356 subpicture_region_t *p_region;
358 *pp_region = p_region = subpicture_region_New( &p_overlay->format );
359 if( !p_region )
360 break;
362 msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
363 (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
364 p_overlay->i_alpha );
366 if( p_overlay->format.i_chroma == VLC_CODEC_TEXT )
368 p_region->psz_text = strdup( p_overlay->data.p_text );
369 p_region->p_style = text_style_Duplicate( p_overlay->p_fontstyle );
371 else
373 /* FIXME the copy is probably not needed anymore */
374 picture_Copy( p_region->p_picture, p_overlay->data.p_pic );
376 p_region->i_x = p_overlay->i_x;
377 p_region->i_y = p_overlay->i_y;
378 p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP;
379 p_region->i_alpha = p_overlay->i_alpha;
380 pp_region = &p_region->p_next;
383 p_sys->b_updated = false;
384 return p_spu;
387 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
388 vlc_value_t oldval, vlc_value_t newval,
389 void *p_data )
391 filter_sys_t *p_sys = (filter_sys_t *)p_data;
392 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
394 vlc_mutex_lock( &p_sys->lock );
395 if( !strncmp( psz_var, "overlay-input", 13 ) )
397 free( p_sys->psz_inputfile );
398 p_sys->psz_inputfile = strdup( newval.psz_string );
400 else if( !strncmp( psz_var, "overlay-output", 14 ) )
402 free( p_sys->psz_outputfile );
403 p_sys->psz_outputfile = strdup( newval.psz_string );
405 vlc_mutex_unlock( &p_sys->lock );
407 return VLC_EGENERIC;