]> git.sesse.net Git - vlc/blob - modules/video_filter/dynamicoverlay/dynamicoverlay.c
DynamicOverlay: cosmetics.
[vlc] / modules / video_filter / dynamicoverlay / dynamicoverlay.c
1 /*****************************************************************************
2  * dynamicoverlay.c : dynamic overlay plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  * $Id$
6  *
7  * Author: Soren Bog <avacore@videolan.org>
8  *
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.
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 General Public License for more details.
18  *
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  *****************************************************************************/
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
38 #include <ctype.h>
39 #include <fcntl.h>
40
41 #include "dynamicoverlay.h"
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static int Create( vlc_object_t * );
47 static void Destroy( vlc_object_t * );
48 static subpicture_t *Filter( filter_t *, mtime_t );
49
50 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
51                            vlc_value_t oldval, vlc_value_t newval,
52                            void *p_data );
53
54 /*****************************************************************************
55  * Module descriptor
56  *****************************************************************************/
57
58 #define INPUT_TEXT N_("Input FIFO")
59 #define INPUT_LONGTEXT N_("FIFO which will be read for commands")
60
61 #define OUTPUT_TEXT N_("Output FIFO")
62 #define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
63
64 vlc_module_begin ()
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 )
70
71     add_file( "overlay-input", NULL, NULL, INPUT_TEXT, INPUT_LONGTEXT,
72               false )
73     add_file( "overlay-output", NULL, NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
74               false )
75
76     add_shortcut( "overlay" )
77     set_callbacks( Create, Destroy )
78 vlc_module_end ()
79
80 static const char *const ppsz_filter_options[] = {
81     "input", "output", NULL
82 };
83
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 )
90 {
91     filter_t *p_filter = (filter_t *)p_this;
92     filter_sys_t *p_sys;
93
94     /* Allocate structure */
95     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
96     if( p_filter->p_sys == NULL )
97         return VLC_ENOMEM;
98     p_sys = p_filter->p_sys;
99
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 );
106
107     p_sys->i_inputfd = -1;
108     p_sys->i_outputfd = -1;
109     p_sys->b_updated = true;
110     p_sys->b_atomic = false;
111
112     p_filter->pf_sub_filter = Filter;
113
114     config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
115                        p_filter->p_cfg );
116
117     p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
118                                                        "overlay-input" );
119     p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
120                                                         "overlay-output" );
121
122     var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
123     var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
124
125     RegisterCommand( p_filter );
126     return VLC_SUCCESS;
127 }
128
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 )
135 {
136     filter_t *p_filter = (filter_t *)p_this;
137     filter_sys_t *p_sys = p_filter->p_sys;
138
139     BufferDestroy( &p_sys->input );
140     BufferDestroy( &p_sys->output );
141     QueueDestroy( &p_sys->atomic );
142     QueueDestroy( &p_sys->pending );
143     QueueDestroy( &p_sys->processed );
144     ListDestroy( &p_sys->overlays );
145     UnregisterCommand( p_filter );
146
147     free( p_sys->psz_inputfile );
148     free( p_sys->psz_outputfile );
149     free( p_sys );
150 }
151
152 /*****************************************************************************
153  * Render: displays previously rendered output
154  *****************************************************************************
155  * This function send the currently rendered image to adjust modified image,
156  * waits until it is displayed and switch the two rendering buffers, preparing
157  * next frame.
158  *****************************************************************************/
159 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
160 {
161     filter_sys_t *p_sys = p_filter->p_sys;
162
163     /* We might need to open these at any time. */
164     if( p_sys->i_inputfd == -1 )
165     {
166         p_sys->i_inputfd = open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
167         if( p_sys->i_inputfd == -1 )
168         {
169             msg_Warn( p_filter, "Failed to grab input file: %s (%s)",
170                       p_sys->psz_inputfile, strerror( errno ) );
171         }
172         else
173         {
174             msg_Info( p_filter, "Grabbed input file: %s",
175                       p_sys->psz_inputfile );
176         }
177     }
178
179     if( p_sys->i_outputfd == -1 )
180     {
181         p_sys->i_outputfd = open( p_sys->psz_outputfile,
182                                   O_WRONLY | O_NONBLOCK );
183         if( p_sys->i_outputfd == -1 )
184         {
185             if( errno != ENXIO )
186             {
187                 msg_Warn( p_filter, "Failed to grab output file: %s (%s)",
188                           p_sys->psz_outputfile, strerror( errno ) );
189             }
190         }
191         else
192         {
193             msg_Info( p_filter, "Grabbed output file: %s",
194                       p_sys->psz_outputfile );
195         }
196     }
197
198     /* Read any waiting commands */
199     if( p_sys->i_inputfd != -1 )
200     {
201         char p_buffer[1024];
202         ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
203         if( i_len == -1 )
204         {
205             /* We hit an error */
206             if( errno != EAGAIN )
207             {
208                 msg_Warn( p_filter, "Error on input file: %s",
209                           strerror( errno ) );
210                 close( p_sys->i_inputfd );
211                 p_sys->i_inputfd = -1;
212             }
213         }
214         else if( i_len == 0 )
215         {
216             /* We hit the end-of-file */
217         }
218         else
219         {
220             BufferAdd( &p_sys->input, p_buffer, i_len );
221         }
222     }
223
224     /* Parse any complete commands */
225     char *p_end, *p_cmd;
226     while( ( p_end = memchr( p_sys->input.p_begin, '\n',
227                              p_sys->input.i_length ) ) )
228     {
229         commanddesc_t *p_cur = NULL;
230         bool b_found = false;
231         size_t i_index = 0;
232
233         *p_end = '\0';
234         p_cmd = BufferGetToken( &p_sys->input );
235
236         msg_Info( p_filter, "Search command: %s", p_cmd );
237         for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
238         {
239             p_cur = p_sys->pp_commands[i_index];
240             if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
241             {
242                 p_cmd[strlen(p_cur->psz_command)] = '\0';
243                 b_found = true;
244                 break;
245             }
246         }
247
248         if( !b_found )
249         {
250             /* No matching command */
251             msg_Err( p_filter, "Got invalid command: %s", p_cmd );
252             BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
253         }
254         else
255         {
256             msg_Info( p_filter, "Got valid command: %s", p_cmd );
257
258             command_t *p_cmddesc = malloc( sizeof( command_t ) );
259             if( !p_cmddesc )
260                 return NULL;
261
262             p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
263             p_cmddesc->p_command = p_cur;
264             p_cmddesc->p_command->pf_parser( p_cmd, p_end,
265                                              &p_cmddesc->params );
266
267             if( ( p_cmddesc->p_command->b_atomic == true ) &&
268                 ( p_sys->b_atomic == true ) )
269                 QueueEnqueue( &p_sys->atomic, p_cmddesc );
270             else
271                 QueueEnqueue( &p_sys->pending, p_cmddesc );
272         }
273
274         BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
275     }
276
277     /* Process any pending commands */
278     command_t *p_command = NULL;
279     while( (p_command = QueueDequeue( &p_sys->pending )) )
280     {
281         p_command->i_status =
282             p_command->p_command->pf_execute( p_filter, &p_command->params,
283                                               &p_command->results );
284         QueueEnqueue( &p_sys->processed, p_command );
285     }
286
287     /* Output any processed commands */
288     while( (p_command = QueueDequeue( &p_sys->processed )) )
289     {
290         if( p_command->i_status == VLC_SUCCESS )
291         {
292             const char *psz_success = "SUCCESS:";
293             const char *psz_nl = "\n";
294             BufferAdd( &p_sys->output, psz_success, 8 );
295             p_command->p_command->pf_unparse( &p_command->results,
296                                               &p_sys->output );
297             BufferAdd( &p_sys->output, psz_nl, 1 );
298         }
299         else
300         {
301             BufferPrintf( &p_sys->output, "FAILURE: %d\n",
302                           p_command->i_status );
303         }
304     }
305
306     /* Try emptying the output buffer */
307     if( p_sys->i_outputfd != -1 )
308     {
309         ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
310                               p_sys->output.i_length );
311         if( i_len == -1 )
312         {
313             /* We hit an error */
314             if( errno != EAGAIN )
315             {
316                 msg_Warn( p_filter, "Error on output file: %s",
317                           strerror( errno ) );
318                 close( p_sys->i_outputfd );
319                 p_sys->i_outputfd = -1;
320             }
321         }
322         else
323         {
324             BufferDel( &p_sys->output, i_len );
325         }
326     }
327
328     if( p_sys->b_updated == false )
329         return NULL;
330
331     subpicture_t *p_spu = NULL;
332     overlay_t *p_overlay = NULL;
333
334     p_spu = p_filter->pf_sub_buffer_new( p_filter );
335     if( !p_spu )
336     {
337         msg_Err( p_filter, "cannot allocate subpicture" );
338         return NULL;
339     }
340
341     p_spu->b_absolute = true;
342     p_spu->i_start = date;
343     p_spu->i_stop = 0;
344     p_spu->b_ephemer = true;
345
346     subpicture_region_t **pp_region = &p_spu->p_region;
347     while( (p_overlay = ListWalk( &p_sys->overlays )) )
348     {
349         subpicture_region_t *p_region;
350
351         *pp_region = p_region = subpicture_region_New( &p_overlay->format );
352         if( !p_region )
353             break;
354
355         msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
356                  (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
357                  p_overlay->i_alpha );
358
359         if( p_overlay->format.i_chroma == VLC_CODEC_TEXT )
360         {
361             p_region->psz_text = strdup( p_overlay->data.p_text );
362             p_region->p_style = text_style_Duplicate( p_overlay->p_fontstyle );
363         }
364         else
365         {
366             /* FIXME the copy is probably not needed anymore */
367             picture_Copy( p_region->p_picture, p_overlay->data.p_pic );
368         }
369         p_region->i_x = p_overlay->i_x;
370         p_region->i_y = p_overlay->i_y;
371         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
372         p_region->i_alpha = p_overlay->i_alpha;
373         pp_region = &p_region->p_next;
374     }
375
376     p_sys->b_updated = false;
377     return p_spu;
378 }
379
380 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
381                            vlc_value_t oldval, vlc_value_t newval,
382                            void *p_data )
383 {
384     filter_sys_t *p_sys = (filter_sys_t *)p_data;
385     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
386
387     if( !strncmp( psz_var, "overlay-input", 13 ) )
388         p_sys->psz_inputfile = newval.psz_string;
389     else if( !strncmp( psz_var, "overlay-output", 14 ) )
390         p_sys->psz_outputfile = newval.psz_string;
391
392     return VLC_EGENERIC;
393 }