dynamicoverlay.c 13.1 KB
Newer Older
1
2
3
4
5
6
/*****************************************************************************
 * dynamicoverlay.c : dynamic overlay plugin for vlc
 *****************************************************************************
 * Copyright (C) 2007 the VideoLAN team
 * $Id$
 *
ivoire's avatar
ivoire committed
7
 * Author: Soren Bog <avacore@videolan.org>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc_common.h>
32
#include <vlc_plugin.h>
33
34
35
36
37
#include <vlc_sout.h>
#include <vlc_vout.h>
#include <vlc_filter.h>
#include <vlc_osd.h>

38
39
40
#include <ctype.h>
#include <fcntl.h>

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "dynamicoverlay.h"

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int Create( vlc_object_t * );
static void Destroy( vlc_object_t * );
static subpicture_t *Filter( filter_t *, mtime_t );

static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
                           vlc_value_t oldval, vlc_value_t newval,
                           void *p_data );

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/

#define INPUT_TEXT N_("Input FIFO")
#define INPUT_LONGTEXT N_("FIFO which will be read for commands")

#define OUTPUT_TEXT N_("Output FIFO")
#define OUTPUT_LONGTEXT N_("FIFO which will be written to for responses")

64
65
66
67
68
69
vlc_module_begin ()
    set_description( N_("Dynamic video overlay") )
    set_shortname( N_("Overlay" ))
    set_category( CAT_VIDEO )
    set_subcategory( SUBCAT_VIDEO_VFILTER )
    set_capability( "sub filter", 0 )
70
71

    add_file( "overlay-input", NULL, NULL, INPUT_TEXT, INPUT_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
72
              false )
73
    add_file( "overlay-output", NULL, NULL, OUTPUT_TEXT, OUTPUT_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
74
              false )
75

76
77
78
    add_shortcut( "overlay" )
    set_callbacks( Create, Destroy )
vlc_module_end ()
79

80
static const char *const ppsz_filter_options[] = {
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    "input", "output", NULL
};

/*****************************************************************************
 * Create: allocates adjust video thread output method
 *****************************************************************************
 * This function allocates and initializes a adjust vout method.
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;

    /* Allocate structure */
    p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
    if( p_filter->p_sys == NULL )
        return VLC_ENOMEM;
    p_sys = p_filter->p_sys;

    BufferInit( &p_sys->input );
    BufferInit( &p_sys->output );
    QueueInit( &p_sys->atomic );
    QueueInit( &p_sys->pending );
    QueueInit( &p_sys->processed );
    ListInit( &p_sys->overlays );

    p_sys->i_inputfd = -1;
    p_sys->i_outputfd = -1;
109
110
    p_sys->b_updated = true;
    p_sys->b_atomic = false;
111
112
113
114
115
116
117
118
119
120
121
122
123
124

    p_filter->pf_sub_filter = Filter;

    config_ChainParse( p_filter, "overlay-", ppsz_filter_options,
                       p_filter->p_cfg );

    p_sys->psz_inputfile = var_CreateGetStringCommand( p_filter,
                                                       "overlay-input" );
    p_sys->psz_outputfile = var_CreateGetStringCommand( p_filter,
                                                        "overlay-output" );

    var_AddCallback( p_filter, "overlay-input", AdjustCallback, p_sys );
    var_AddCallback( p_filter, "overlay-output", AdjustCallback, p_sys );

125
    RegisterCommand( p_filter );
126
127
128
129
130
131
132
133
134
135
136
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Destroy: destroy adjust video thread output method
 *****************************************************************************
 * Terminate an output method created by adjustCreateOutputMethod
 *****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
ivoire's avatar
ivoire committed
137
    filter_sys_t *p_sys = p_filter->p_sys;
138

ivoire's avatar
ivoire committed
139
140
141
142
143
144
    BufferDestroy( &p_sys->input );
    BufferDestroy( &p_sys->output );
    QueueDestroy( &p_sys->atomic );
    QueueDestroy( &p_sys->pending );
    QueueDestroy( &p_sys->processed );
    ListDestroy( &p_sys->overlays );
145
    UnregisterCommand( p_filter );
146

ivoire's avatar
ivoire committed
147
148
149
    free( p_sys->psz_inputfile );
    free( p_sys->psz_outputfile );
    free( p_sys );
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
}

/*****************************************************************************
 * Render: displays previously rendered output
 *****************************************************************************
 * This function send the currently rendered image to adjust modified image,
 * waits until it is displayed and switch the two rendering buffers, preparing
 * next frame.
 *****************************************************************************/
static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
{
    filter_sys_t *p_sys = p_filter->p_sys;

    /* We might need to open these at any time. */
    if( p_sys->i_inputfd == -1 )
    {
        p_sys->i_inputfd = open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK );
        if( p_sys->i_inputfd == -1 )
        {
            msg_Warn( p_filter, "Failed to grab input file: %s (%s)",
                      p_sys->psz_inputfile, strerror( errno ) );
        }
        else
        {
            msg_Info( p_filter, "Grabbed input file: %s",
                      p_sys->psz_inputfile );
        }
    }

    if( p_sys->i_outputfd == -1 )
    {
        p_sys->i_outputfd = open( p_sys->psz_outputfile,
                                  O_WRONLY | O_NONBLOCK );
        if( p_sys->i_outputfd == -1 )
        {
            if( errno != ENXIO )
            {
                msg_Warn( p_filter, "Failed to grab output file: %s (%s)",
                          p_sys->psz_outputfile, strerror( errno ) );
            }
        }
        else
        {
            msg_Info( p_filter, "Grabbed output file: %s",
                      p_sys->psz_outputfile );
        }
    }

    /* Read any waiting commands */
    if( p_sys->i_inputfd != -1 )
    {
        char p_buffer[1024];
        ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 );
        if( i_len == -1 )
        {
            /* We hit an error */
            if( errno != EAGAIN )
            {
                msg_Warn( p_filter, "Error on input file: %s",
                          strerror( errno ) );
                close( p_sys->i_inputfd );
                p_sys->i_inputfd = -1;
            }
        }
        else if( i_len == 0 )
        {
            /* We hit the end-of-file */
        }
        else
        {
            BufferAdd( &p_sys->input, p_buffer, i_len );
        }
    }

    /* Parse any complete commands */
    char *p_end, *p_cmd;
    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
232
        size_t i_index = 0;

233
        *p_end = '\0';
234
        p_cmd = BufferGetToken( &p_sys->input );
235

236
237
        msg_Info( p_filter, "Search command: %s", p_cmd );
        for( i_index = 0; i_index < p_sys->i_commands; i_index++ )
238
        {
239
240
            p_cur = p_sys->pp_commands[i_index];
            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
245
246
                break;
            }
        }
247
248

        if( !b_found )
249
250
251
252
253
254
255
        {
            /* No matching command */
            msg_Err( p_filter, "Got invalid command: %s", p_cmd );
            BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC );
        }
        else
        {
256
257
258
259
            msg_Info( p_filter, "Got valid command: %s", p_cmd );

            command_t *p_cmddesc = malloc( sizeof( command_t ) );
            if( !p_cmddesc )
260
261
                return NULL;

262
263
264
265
            p_cmd = p_cmd + strlen(p_cur->psz_command) +1;
            p_cmddesc->p_command = p_cur;
            p_cmddesc->p_command->pf_parser( p_cmd, p_end,
                                             &p_cmddesc->params );
266

267
268
            if( ( p_cmddesc->p_command->b_atomic == true ) &&
                ( p_sys->b_atomic == true ) )
269
                QueueEnqueue( &p_sys->atomic, p_cmddesc );
270
            else
271
                QueueEnqueue( &p_sys->pending, p_cmddesc );
272
273
274
275
276
277
278
279
280
281
282
        }

        BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 );
    }

    /* Process any pending commands */
    command_t *p_command = NULL;
    while( (p_command = QueueDequeue( &p_sys->pending )) )
    {
        p_command->i_status =
            p_command->p_command->pf_execute( p_filter, &p_command->params,
283
                                              &p_command->results );
284
285
286
287
288
289
290
291
292
293
294
        QueueEnqueue( &p_sys->processed, p_command );
    }

    /* Output any processed commands */
    while( (p_command = QueueDequeue( &p_sys->processed )) )
    {
        if( p_command->i_status == VLC_SUCCESS )
        {
            const char *psz_success = "SUCCESS:";
            const char *psz_nl = "\n";
            BufferAdd( &p_sys->output, psz_success, 8 );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
295
296
            p_command->p_command->pf_unparse( &p_command->results,
                                              &p_sys->output );
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
            BufferAdd( &p_sys->output, psz_nl, 1 );
        }
        else
        {
            BufferPrintf( &p_sys->output, "FAILURE: %d\n",
                          p_command->i_status );
        }
    }

    /* Try emptying the output buffer */
    if( p_sys->i_outputfd != -1 )
    {
        ssize_t i_len = write( p_sys->i_outputfd, p_sys->output.p_begin,
                              p_sys->output.i_length );
        if( i_len == -1 )
        {
            /* We hit an error */
            if( errno != EAGAIN )
            {
                msg_Warn( p_filter, "Error on output file: %s",
                          strerror( errno ) );
                close( p_sys->i_outputfd );
                p_sys->i_outputfd = -1;
            }
        }
        else
        {
            BufferDel( &p_sys->output, i_len );
        }
    }

328
    if( p_sys->b_updated == false )
329
330
331
332
333
334
335
336
337
338
339
340
        return NULL;

    subpicture_t *p_spu = NULL;
    overlay_t *p_overlay = NULL;

    p_spu = p_filter->pf_sub_buffer_new( p_filter );
    if( !p_spu )
    {
        msg_Err( p_filter, "cannot allocate subpicture" );
        return NULL;
    }

341
    p_spu->b_absolute = true;
342
343
    p_spu->i_start = date;
    p_spu->i_stop = 0;
344
    p_spu->b_ephemer = true;
345
346
347
348

    subpicture_region_t **pp_region = &p_spu->p_region;
    while( (p_overlay = ListWalk( &p_sys->overlays )) )
    {
349
350
        subpicture_region_t *p_region;

351
        *pp_region = p_region = subpicture_region_New( &p_overlay->format );
352
353
354
        if( !p_region )
            break;

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 );
basOS G's avatar
basOS G committed
362
            p_region->p_style = text_style_Duplicate( p_overlay->p_fontstyle );
363
364
365
        }
        else
        {
366
            /* FIXME the copy is probably not needed anymore */
367
            picture_Copy( p_region->p_picture, p_overlay->data.p_pic );
368
        }
369
370
371
372
373
        p_region->i_x = p_overlay->i_x;
        p_region->i_y = p_overlay->i_y;
        p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
        p_region->i_alpha = p_overlay->i_alpha;
        pp_region = &p_region->p_next;
374
375
    }

376
    p_sys->b_updated = false;
377
378
379
380
381
382
383
384
    return p_spu;
}

static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
                           vlc_value_t oldval, vlc_value_t newval,
                           void *p_data )
{
    filter_sys_t *p_sys = (filter_sys_t *)p_data;
385
386
387
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);

    if( !strncmp( psz_var, "overlay-input", 13 ) )
388
389
390
391
    {
        free( p_sys->psz_inputfile );
        p_sys->psz_inputfile = strdup( newval.psz_string );
    }
392
    else if( !strncmp( psz_var, "overlay-output", 14 ) )
393
394
395
396
    {
        free( p_sys->psz_outputfile );
        p_sys->psz_outputfile = strdup( newval.psz_string );
    }
397

398
399
    return VLC_EGENERIC;
}