]> git.sesse.net Git - vlc/blob - modules/video_filter/dynamicoverlay/dynamicoverlay.c
LGPL
[vlc] / modules / video_filter / dynamicoverlay / dynamicoverlay.c
1 /*****************************************************************************
2  * dynamicoverlay.c : dynamic overlay plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Author: Søren Bøg <avacore@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
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>
38
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <unistd.h>
43
44 #include "dynamicoverlay.h"
45
46 /*****************************************************************************
47  * Local prototypes
48  *****************************************************************************/
49 static int Create( vlc_object_t * );
50 static void Destroy( vlc_object_t * );
51 static subpicture_t *Filter( filter_t *, mtime_t );
52
53 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
54                            vlc_value_t oldval, vlc_value_t newval,
55                            void *p_data );
56
57 /*****************************************************************************
58  * Module descriptor
59  *****************************************************************************/
60
61 #define INPUT_TEXT N_("Input FIFO")
62 #define INPUT_LONGTEXT N_("FIFO which will be read for commands")
63
64 #define OUTPUT_TEXT N_("Output FIFO")
65 #define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
66
67 vlc_module_begin ()
68     set_description( N_("Dynamic video overlay") )
69     set_shortname( N_("Overlay" ))
70     set_category( CAT_VIDEO )
71     set_subcategory( SUBCAT_VIDEO_VFILTER )
72     set_capability( "sub source", 0 )
73
74     add_loadfile( "overlay-input", NULL, INPUT_TEXT, INPUT_LONGTEXT,
75                   false )
76     /* Note: add_loadfile as O_WRONLY w/o O_CREAT, i.e. FIFO must exist */
77     add_loadfile( "overlay-output", NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
78                   false )
79
80     add_shortcut( "overlay" )
81     set_callbacks( Create, Destroy )
82 vlc_module_end ()
83
84 static const char *const ppsz_filter_options[] = {
85     "input", "output", NULL
86 };
87
88 /*****************************************************************************
89  * Create: allocates adjust video thread output method
90  *****************************************************************************
91  * This function allocates and initializes a adjust vout method.
92  *****************************************************************************/
93 static int Create( vlc_object_t *p_this )
94 {
95     filter_t *p_filter = (filter_t *)p_this;
96     filter_sys_t *p_sys;
97
98     /* Allocate structure */
99     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
100     if( p_filter->p_sys == NULL )
101         return VLC_ENOMEM;
102     p_sys = p_filter->p_sys;
103
104     BufferInit( &p_sys->input );
105     BufferInit( &p_sys->output );
106     QueueInit( &p_sys->atomic );
107     QueueInit( &p_sys->pending );
108     QueueInit( &p_sys->processed );
109     do_ListInit( &p_sys->overlays );
110
111     p_sys->i_inputfd = -1;
112     p_sys->i_outputfd = -1;
113     p_sys->b_updated = true;
114     p_sys->b_atomic = false;
115     vlc_mutex_init( &p_sys->lock );
116
117     p_filter->pf_sub_source = Filter;
118
119     config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
120                        p_filter->p_cfg );
121
122     p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
123                                                        "overlay-input" );
124     p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
125                                                         "overlay-output" );
126
127     var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
128     var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
129
130     RegisterCommand( p_filter );
131     return VLC_SUCCESS;
132 }
133
134 /*****************************************************************************
135  * Destroy: destroy adjust video thread output method
136  *****************************************************************************
137  * Terminate an output method created by adjustCreateOutputMethod
138  *****************************************************************************/
139 static void Destroy( vlc_object_t *p_this )
140 {
141     filter_t *p_filter = (filter_t *)p_this;
142     filter_sys_t *p_sys = p_filter->p_sys;
143
144     BufferDestroy( &p_sys->input );
145     BufferDestroy( &p_sys->output );
146     QueueDestroy( &p_sys->atomic );
147     QueueDestroy( &p_sys->pending );
148     QueueDestroy( &p_sys->processed );
149     do_ListDestroy( &p_sys->overlays );
150     UnregisterCommand( p_filter );
151
152     var_DelCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
153     var_DelCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
154
155     vlc_mutex_destroy( &p_sys->lock );
156     free( p_sys->psz_inputfile );
157     free( p_sys->psz_outputfile );
158     free( p_sys );
159 }
160
161 /*****************************************************************************
162  * Render: displays previously rendered output
163  *****************************************************************************
164  * This function send the currently rendered image to adjust modified image,
165  * waits until it is displayed and switch the two rendering buffers, preparing
166  * next frame.
167  *****************************************************************************/
168 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
169 {
170     filter_sys_t *p_sys = p_filter->p_sys;
171
172     /* We might need to open these at any time. */
173     vlc_mutex_lock( &p_sys->lock );
174     if( p_sys->i_inputfd == -1 )
175     {
176         p_sys->i_inputfd = vlc_open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
177         if( p_sys->i_inputfd == -1 )
178         {
179             msg_Warn( p_filter, "Failed to grab input file: %s (%m)",
180                       p_sys->psz_inputfile );
181         }
182         else
183         {
184             msg_Info( p_filter, "Grabbed input file: %s",
185                       p_sys->psz_inputfile );
186         }
187     }
188
189     if( p_sys->i_outputfd == -1 )
190     {
191         p_sys->i_outputfd = vlc_open( p_sys->psz_outputfile,
192                                   O_WRONLY | O_NONBLOCK );
193         if( p_sys->i_outputfd == -1 )
194         {
195             if( errno != ENXIO )
196             {
197                 msg_Warn( p_filter, "Failed to grab output file: %s (%m)",
198                           p_sys->psz_outputfile );
199             }
200         }
201         else
202         {
203             msg_Info( p_filter, "Grabbed output file: %s",
204                       p_sys->psz_outputfile );
205         }
206     }
207     vlc_mutex_unlock( &p_sys->lock );
208
209     /* Read any waiting commands */
210     if( p_sys->i_inputfd != -1 )
211     {
212         char p_buffer[1024];
213         ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
214         if( i_len == -1 )
215         {
216             /* We hit an error */
217             if( errno != EAGAIN )
218             {
219                 msg_Warn( p_filter, "Error on input file: %m" );
220                 close( p_sys->i_inputfd );
221                 p_sys->i_inputfd = -1;
222             }
223         }
224         else if( i_len == 0 )
225         {
226             /* We hit the end-of-file */
227         }
228         else
229         {
230             BufferAdd( &p_sys->input, p_buffer, i_len );
231         }
232     }
233
234     /* Parse any complete commands */
235     char *p_end, *p_cmd;
236     while( ( p_end = memchr( p_sys->input.p_begin, '\n',
237                              p_sys->input.i_length ) ) )
238     {
239         commanddesc_t *p_cur = NULL;
240         bool b_found = false;
241         size_t i_index = 0;
242
243         *p_end = '\0';
244         p_cmd = BufferGetToken( &p_sys->input );
245
246         msg_Info( p_filter, "Search command: %s", p_cmd );
247         for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
248         {
249             p_cur = p_sys->pp_commands[i_index];
250             if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
251             {
252                 p_cmd[strlen(p_cur->psz_command)] = '\0';
253                 b_found = true;
254                 break;
255             }
256         }
257
258         if( !b_found )
259         {
260             /* No matching command */
261             msg_Err( p_filter, "Got invalid command: %s", p_cmd );
262             BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
263         }
264         else
265         {
266             msg_Info( p_filter, "Got valid command: %s", p_cmd );
267
268             command_t *p_cmddesc = malloc( sizeof( command_t ) );
269             if( !p_cmddesc )
270                 return NULL;
271
272             p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
273             p_cmddesc->p_command = p_cur;
274             p_cmddesc->p_command->pf_parser( p_cmd, p_end,
275                                              &p_cmddesc->params );
276
277             if( p_cmddesc->p_command->b_atomic && p_sys->b_atomic )
278                 QueueEnqueue( &p_sys->atomic, p_cmddesc );
279             else
280                 QueueEnqueue( &p_sys->pending, p_cmddesc );
281         }
282
283         BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
284     }
285
286     /* Process any pending commands */
287     command_t *p_command = NULL;
288     while( (p_command = QueueDequeue( &p_sys->pending )) )
289     {
290         p_command->i_status =
291             p_command->p_command->pf_execute( p_filter, &p_command->params,
292                                               &p_command->results );
293         QueueEnqueue( &p_sys->processed, p_command );
294     }
295
296     /* Output any processed commands */
297     while( (p_command = QueueDequeue( &p_sys->processed )) )
298     {
299         if( p_command->i_status == VLC_SUCCESS )
300         {
301             const char *psz_success = "SUCCESS:";
302             const char *psz_nl = "\n";
303             BufferAdd( &p_sys->output, psz_success, 8 );
304             p_command->p_command->pf_unparse( &p_command->results,
305                                               &p_sys->output );
306             BufferAdd( &p_sys->output, psz_nl, 1 );
307         }
308         else
309         {
310             BufferPrintf( &p_sys->output, "FAILURE: %d\n",
311                           p_command->i_status );
312         }
313     }
314
315     /* Try emptying the output buffer */
316     if( p_sys->i_outputfd != -1 )
317     {
318         ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
319                               p_sys->output.i_length );
320         if( i_len == -1 )
321         {
322             /* We hit an error */
323             if( errno != EAGAIN )
324             {
325                 msg_Warn( p_filter, "Error on output file: %m" );
326                 close( p_sys->i_outputfd );
327                 p_sys->i_outputfd = -1;
328             }
329         }
330         else
331         {
332             BufferDel( &p_sys->output, i_len );
333         }
334     }
335
336     if( !p_sys->b_updated )
337         return NULL;
338
339     subpicture_t *p_spu = NULL;
340     overlay_t *p_overlay = NULL;
341
342     p_spu = p_filter->pf_sub_buffer_new( p_filter );
343     if( !p_spu )
344     {
345         msg_Err( p_filter, "cannot allocate subpicture" );
346         return NULL;
347     }
348
349     p_spu->b_absolute = true;
350     p_spu->i_start = date;
351     p_spu->i_stop = 0;
352     p_spu->b_ephemer = true;
353
354     subpicture_region_t **pp_region = &p_spu->p_region;
355     while( (p_overlay = ListWalk( &p_sys->overlays )) )
356     {
357         subpicture_region_t *p_region;
358
359         *pp_region = p_region = subpicture_region_New( &p_overlay->format );
360         if( !p_region )
361             break;
362
363         msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
364                  (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
365                  p_overlay->i_alpha );
366
367         if( p_overlay->format.i_chroma == VLC_CODEC_TEXT )
368         {
369             p_region->psz_text = strdup( p_overlay->data.p_text );
370             p_region->p_style = text_style_Duplicate( p_overlay->p_fontstyle );
371         }
372         else
373         {
374             /* FIXME the copy is probably not needed anymore */
375             picture_Copy( p_region->p_picture, p_overlay->data.p_pic );
376         }
377         p_region->i_x = p_overlay->i_x;
378         p_region->i_y = p_overlay->i_y;
379         p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP;
380         p_region->i_alpha = p_overlay->i_alpha;
381         pp_region = &p_region->p_next;
382     }
383
384     p_sys->b_updated = false;
385     return p_spu;
386 }
387
388 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
389                            vlc_value_t oldval, vlc_value_t newval,
390                            void *p_data )
391 {
392     filter_sys_t *p_sys = (filter_sys_t *)p_data;
393     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
394
395     vlc_mutex_lock( &p_sys->lock );
396     if( !strncmp( psz_var, "overlay-input", 13 ) )
397     {
398         free( p_sys->psz_inputfile );
399         p_sys->psz_inputfile = strdup( newval.psz_string );
400     }
401     else if( !strncmp( psz_var, "overlay-output", 14 ) )
402     {
403         free( p_sys->psz_outputfile );
404         p_sys->psz_outputfile = strdup( newval.psz_string );
405     }
406     vlc_mutex_unlock( &p_sys->lock );
407
408     return VLC_EGENERIC;
409 }