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