]> git.sesse.net Git - vlc/blob - modules/video_filter/dynamicoverlay/dynamicoverlay.c
update module LIST file.
[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: Søren Bøg <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/vlc.h>
32 #include <vlc_sout.h>
33 #include <vlc_vout.h>
34 #include <vlc_filter.h>
35 #include <vlc_osd.h>
36
37 #include <ctype.h>
38 #include <fcntl.h>
39
40 #include "dynamicoverlay.h"
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int Create( vlc_object_t * );
46 static void Destroy( vlc_object_t * );
47 static subpicture_t *Filter( filter_t *, mtime_t );
48
49 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
50                            vlc_value_t oldval, vlc_value_t newval,
51                            void *p_data );
52
53 /*****************************************************************************
54  * Module descriptor
55  *****************************************************************************/
56
57 #define INPUT_TEXT N_("Input FIFO")
58 #define INPUT_LONGTEXT N_("FIFO which will be read for commands")
59
60 #define OUTPUT_TEXT N_("Output FIFO")
61 #define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")
62
63 vlc_module_begin();
64     set_description( _("Dynamic video overlay") );
65     set_shortname( _("Overlay" ));
66     set_category( CAT_VIDEO );
67     set_subcategory( SUBCAT_VIDEO_VFILTER );
68     set_capability( "sub filter", 0 );
69
70     add_file( "overlay-input", NULL, NULL, INPUT_TEXT, INPUT_LONGTEXT,
71               VLC_FALSE );
72     add_file( "overlay-output", NULL, NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
73               VLC_FALSE );
74
75     add_shortcut( "overlay" );
76     set_callbacks( Create, Destroy );
77 vlc_module_end();
78
79 static const char *ppsz_filter_options[] = {
80     "input", "output", NULL
81 };
82
83 /*****************************************************************************
84  * Create: allocates adjust video thread output method
85  *****************************************************************************
86  * This function allocates and initializes a adjust vout method.
87  *****************************************************************************/
88 static int Create( vlc_object_t *p_this )
89 {
90     filter_t *p_filter = (filter_t *)p_this;
91     filter_sys_t *p_sys;
92
93     /* Allocate structure */
94     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
95     if( p_filter->p_sys == NULL )
96     {
97         msg_Err( p_filter, "out of memory" );
98         return VLC_ENOMEM;
99     }
100     p_sys = p_filter->p_sys;
101
102     BufferInit( &p_sys->input );
103     BufferInit( &p_sys->output );
104     QueueInit( &p_sys->atomic );
105     QueueInit( &p_sys->pending );
106     QueueInit( &p_sys->processed );
107     ListInit( &p_sys->overlays );
108
109     p_sys->i_inputfd = -1;
110     p_sys->i_outputfd = -1;
111     p_sys->b_updated = VLC_TRUE;
112     p_sys->b_atomic = VLC_FALSE;
113
114     p_filter->pf_sub_filter = Filter;
115
116     config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
117                        p_filter->p_cfg );
118
119     p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
120                                                        "overlay-input" );
121     p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
122                                                         "overlay-output" );
123
124     var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
125     var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );
126
127     RegisterCommand( p_filter );
128     return VLC_SUCCESS;
129 }
130
131 /*****************************************************************************
132  * Destroy: destroy adjust video thread output method
133  *****************************************************************************
134  * Terminate an output method created by adjustCreateOutputMethod
135  *****************************************************************************/
136 static void Destroy( vlc_object_t *p_this )
137 {
138     filter_t *p_filter = (filter_t *)p_this;
139
140     BufferDestroy( &p_filter->p_sys->input );
141     BufferDestroy( &p_filter->p_sys->output );
142     QueueDestroy( &p_filter->p_sys->atomic );
143     QueueDestroy( &p_filter->p_sys->pending );
144     QueueDestroy( &p_filter->p_sys->processed );
145     ListDestroy( &p_filter->p_sys->overlays );
146     UnregisterCommand( p_filter );
147
148     free( p_filter->p_sys->psz_inputfile );
149     free( p_filter->p_sys->psz_outputfile );
150     free( p_filter->p_sys );
151 }
152
153 /*****************************************************************************
154  * Render: displays previously rendered output
155  *****************************************************************************
156  * This function send the currently rendered image to adjust modified image,
157  * waits until it is displayed and switch the two rendering buffers, preparing
158  * next frame.
159  *****************************************************************************/
160 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
161 {
162     filter_sys_t *p_sys = p_filter->p_sys;
163
164     /* We might need to open these at any time. */
165     if( p_sys->i_inputfd == -1 )
166     {
167         p_sys->i_inputfd = open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
168         if( p_sys->i_inputfd == -1 )
169         {
170             msg_Warn( p_filter, "Failed to grab input file: %s (%s)",
171                       p_sys->psz_inputfile, strerror( errno ) );
172         }
173         else
174         {
175             msg_Info( p_filter, "Grabbed input file: %s",
176                       p_sys->psz_inputfile );
177         }
178     }
179
180     if( p_sys->i_outputfd == -1 )
181     {
182         p_sys->i_outputfd = open( p_sys->psz_outputfile,
183                                   O_WRONLY | O_NONBLOCK );
184         if( p_sys->i_outputfd == -1 )
185         {
186             if( errno != ENXIO )
187             {
188                 msg_Warn( p_filter, "Failed to grab output file: %s (%s)",
189                           p_sys->psz_outputfile, strerror( errno ) );
190             }
191         }
192         else
193         {
194             msg_Info( p_filter, "Grabbed output file: %s",
195                       p_sys->psz_outputfile );
196         }
197     }
198
199     /* Read any waiting commands */
200     if( p_sys->i_inputfd != -1 )
201     {
202         char p_buffer[1024];
203         ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
204         if( i_len == -1 )
205         {
206             /* We hit an error */
207             if( errno != EAGAIN )
208             {
209                 msg_Warn( p_filter, "Error on input file: %s",
210                           strerror( errno ) );
211                 close( p_sys->i_inputfd );
212                 p_sys->i_inputfd = -1;
213             }
214         }
215         else if( i_len == 0 )
216         {
217             /* We hit the end-of-file */
218         }
219         else
220         {
221             BufferAdd( &p_sys->input, p_buffer, i_len );
222         }
223     }
224
225     /* Parse any complete commands */
226     char *p_end, *p_cmd;
227     while( ( p_end = memchr( p_sys->input.p_begin, '\n',
228                              p_sys->input.i_length ) ) )
229     {
230         commanddesc_t *p_cur = NULL;
231         vlc_bool_t b_found = VLC_FALSE;
232         size_t i_index = 0;
233
234         *p_end = '\0';
235         p_cmd = BufferGetToken( &p_sys->input );
236
237         msg_Info( p_filter, "Search command: %s", p_cmd );
238         for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
239         {
240             p_cur = p_sys->pp_commands[i_index];
241             if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) )
242             {
243                 p_cmd[strlen(p_cur->psz_command)] = '\0';
244                 b_found = VLC_TRUE;
245                 break;
246             }
247         }
248
249         if( !b_found )
250         {
251             /* No matching command */
252             msg_Err( p_filter, "Got invalid command: %s", p_cmd );
253             BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
254         }
255         else
256         {
257             msg_Info( p_filter, "Got valid command: %s", p_cmd );
258
259             command_t *p_cmddesc = malloc( sizeof( command_t ) );
260             if( !p_cmddesc )
261                 return NULL;
262
263             p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
264             p_cmddesc->p_command = p_cur;
265             p_cmddesc->p_command->pf_parser( p_cmd, p_end,
266                                              &p_cmddesc->params );
267
268             if( ( p_cmddesc->p_command->b_atomic == VLC_TRUE ) &&
269                 ( p_sys->b_atomic == VLC_TRUE ) )
270                 QueueEnqueue( &p_sys->atomic, p_cmddesc );
271             else
272                 QueueEnqueue( &p_sys->pending, p_cmddesc );
273         }
274
275         BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
276     }
277
278     /* Process any pending commands */
279     command_t *p_command = NULL;
280     while( (p_command = QueueDequeue( &p_sys->pending )) )
281     {
282         p_command->i_status =
283             p_command->p_command->pf_execute( p_filter, &p_command->params,
284                                               &p_command->results );
285         QueueEnqueue( &p_sys->processed, p_command );
286     }
287
288     /* Output any processed commands */
289     while( (p_command = QueueDequeue( &p_sys->processed )) )
290     {
291         if( p_command->i_status == VLC_SUCCESS )
292         {
293             const char *psz_success = "SUCCESS:";
294             const char *psz_nl = "\n";
295             BufferAdd( &p_sys->output, psz_success, 8 );
296             p_command->p_command->pf_unparse( &p_command->results,
297                                               &p_sys->output );
298             BufferAdd( &p_sys->output, psz_nl, 1 );
299         }
300         else
301         {
302             BufferPrintf( &p_sys->output, "FAILURE: %d\n",
303                           p_command->i_status );
304         }
305     }
306
307     /* Try emptying the output buffer */
308     if( p_sys->i_outputfd != -1 )
309     {
310         ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
311                               p_sys->output.i_length );
312         if( i_len == -1 )
313         {
314             /* We hit an error */
315             if( errno != EAGAIN )
316             {
317                 msg_Warn( p_filter, "Error on output file: %s",
318                           strerror( errno ) );
319                 close( p_sys->i_outputfd );
320                 p_sys->i_outputfd = -1;
321             }
322         }
323         else
324         {
325             BufferDel( &p_sys->output, i_len );
326         }
327     }
328
329     if( p_sys->b_updated == VLC_FALSE )
330         return NULL;
331
332     subpicture_t *p_spu = NULL;
333     overlay_t *p_overlay = NULL;
334
335     p_spu = p_filter->pf_sub_buffer_new( p_filter );
336     if( !p_spu )
337     {
338         msg_Err( p_filter, "cannot allocate subpicture" );
339         return NULL;
340     }
341
342     p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
343     p_spu->i_x = 0;
344     p_spu->i_y = 0;
345     p_spu->b_absolute = VLC_TRUE;
346     p_spu->i_start = date;
347     p_spu->i_stop = 0;
348     p_spu->b_ephemer = VLC_TRUE;
349
350     subpicture_region_t **pp_region = &p_spu->p_region;
351     while( (p_overlay = ListWalk( &p_sys->overlays )) )
352     {
353         msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d",
354                  (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y,
355                  p_overlay->i_alpha );
356
357         if( p_overlay->format.i_chroma == VLC_FOURCC('T','E','X','T') )
358         {
359             *pp_region = p_spu->pf_create_region( VLC_OBJECT(p_filter),
360                                                   &p_overlay->format );
361             if( !*pp_region )
362                 break;
363             (*pp_region)->psz_text = strdup( p_overlay->data.p_text );
364             (*pp_region)->p_style = malloc( sizeof(struct text_style_t) );
365             if( !(*pp_region)->p_style )
366             {
367                 p_spu->pf_destroy_region( VLC_OBJECT(p_filter), (*pp_region) );
368                 *pp_region = NULL;
369                 break;
370             }
371             p_filter->p_libvlc->pf_memcpy( (*pp_region)->p_style,
372                                             &p_overlay->fontstyle,
373                                             sizeof(struct text_style_t) );
374         }
375         else
376         {
377             picture_t clone;
378             if( vout_AllocatePicture( p_filter, &clone,
379                                       p_overlay->format.i_chroma,
380                                       p_overlay->format.i_width,
381                                       p_overlay->format.i_height,
382                                       p_overlay->format.i_aspect ) )
383             {
384                 msg_Err( p_filter, "cannot allocate picture" );
385                 continue;
386             }
387             vout_CopyPicture( p_filter, &clone, p_overlay->data.p_pic );
388             *pp_region = p_spu->pf_make_region( VLC_OBJECT(p_filter),
389                                                 &p_overlay->format,
390                                                 &clone );
391             if( !*pp_region )
392             {
393                 msg_Err( p_filter, "cannot allocate subpicture region" );
394                 continue;
395             }
396         }
397         (*pp_region)->i_x = p_overlay->i_x;
398         (*pp_region)->i_y = p_overlay->i_y;
399         (*pp_region)->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
400         (*pp_region)->i_alpha = p_overlay->i_alpha;
401         pp_region = &(*pp_region)->p_next;
402     }
403
404     p_sys->b_updated = VLC_FALSE;
405     return p_spu;
406 }
407
408 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
409                            vlc_value_t oldval, vlc_value_t newval,
410                            void *p_data )
411 {
412     filter_sys_t *p_sys = (filter_sys_t *)p_data;
413     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
414
415     if( !strncmp( psz_var, "overlay-input", 13 ) )
416         p_sys->psz_inputfile = newval.psz_string;
417     else if( !strncmp( psz_var, "overlay-output", 14 ) )
418         p_sys->psz_outputfile = newval.psz_string;
419
420     return VLC_EGENERIC;
421 }