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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
35 #include <vlc_filter.h>
43 #include "dynamicoverlay.h"
45 /*****************************************************************************
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
,
56 /*****************************************************************************
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")
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
,
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
,
79 add_shortcut( "overlay" )
80 set_callbacks( Create
, Destroy
)
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
;
97 /* Allocate structure */
98 p_filter
->p_sys
= malloc( sizeof( filter_sys_t
) );
99 if( p_filter
->p_sys
== NULL
)
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
,
121 p_sys
->psz_inputfile
= var_CreateGetStringCommand( p_filter
,
123 p_sys
->psz_outputfile
= var_CreateGetStringCommand( p_filter
,
126 var_AddCallback( p_filter
, "overlay-input", AdjustCallback
, p_sys
);
127 var_AddCallback( p_filter
, "overlay-output", AdjustCallback
, p_sys
);
129 RegisterCommand( p_filter
);
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
);
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
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
);
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 )
196 msg_Warn( p_filter
, "Failed to grab output file: %s (%m)",
197 p_sys
->psz_outputfile
);
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 )
212 ssize_t i_len
= read( p_sys
->i_inputfd
, p_buffer
, 1024 );
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 */
229 BufferAdd( &p_sys
->input
, p_buffer
, i_len
);
233 /* Parse any complete commands */
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;
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';
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
);
265 msg_Info( p_filter
, "Got valid command: %s", p_cmd
);
267 command_t
*p_cmddesc
= malloc( sizeof( command_t
) );
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
);
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
,
305 BufferAdd( &p_sys
->output
, psz_nl
, 1 );
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
);
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;
331 BufferDel( &p_sys
->output
, i_len
);
335 if( !p_sys
->b_updated
)
338 subpicture_t
*p_spu
= NULL
;
339 overlay_t
*p_overlay
= NULL
;
341 p_spu
= p_filter
->pf_sub_buffer_new( p_filter
);
344 msg_Err( p_filter
, "cannot allocate subpicture" );
348 p_spu
->b_absolute
= true;
349 p_spu
->i_start
= date
;
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
);
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
);
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;
387 static int AdjustCallback( vlc_object_t
*p_this
, char const *psz_var
,
388 vlc_value_t oldval
, vlc_value_t newval
,
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
);