Statistics
| Branch: | Revision:

ffmpeg / vhook / ppm.c @ f321635a

History | View | Annotate | Download (9.33 KB)

1
/*
2
 * PPM Video Hook
3
 * Copyright (c) 2003 Charles Yates
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21

    
22
#include <stdio.h>
23
#include <unistd.h>
24
#include <fcntl.h>
25
#include <sys/types.h>
26
#include <sys/wait.h>
27
#include <ctype.h>
28
#include "framehook.h"
29
#include "avformat.h"
30
#include "swscale.h"
31

    
32
static int sws_flags = SWS_BICUBIC;
33

    
34
/** Bi-directional pipe structure.
35
*/
36

    
37
typedef struct rwpipe
38
{
39
    int pid;
40
    FILE *reader;
41
    FILE *writer;
42
}
43
rwpipe;
44

    
45
/** Create a bidirectional pipe for the given command.
46
*/
47

    
48
static rwpipe *rwpipe_open( int argc, char *argv[] )
49
{
50
    rwpipe *this = av_mallocz( sizeof( rwpipe ) );
51

    
52
    if ( this != NULL )
53
    {
54
        int input[ 2 ];
55
        int output[ 2 ];
56

    
57
        pipe( input );
58
        pipe( output );
59

    
60
        this->pid = fork();
61

    
62
        if ( this->pid == 0 )
63
        {
64
#define COMMAND_SIZE 10240
65
            char *command = av_mallocz( COMMAND_SIZE );
66
            int i;
67

    
68
            strcpy( command, "" );
69
            for ( i = 0; i < argc; i ++ )
70
            {
71
                pstrcat( command, COMMAND_SIZE, argv[ i ] );
72
                pstrcat( command, COMMAND_SIZE, " " );
73
            }
74

    
75
            dup2( output[ 0 ], STDIN_FILENO );
76
            dup2( input[ 1 ], STDOUT_FILENO );
77

    
78
            close( input[ 0 ] );
79
            close( input[ 1 ] );
80
            close( output[ 0 ] );
81
            close( output[ 1 ] );
82

    
83
            execl("/bin/sh", "sh", "-c", command, (char*)NULL );
84
            _exit( 255 );
85
        }
86
        else
87
        {
88
            close( input[ 1 ] );
89
            close( output[ 0 ] );
90

    
91
            this->reader = fdopen( input[ 0 ], "r" );
92
            this->writer = fdopen( output[ 1 ], "w" );
93
        }
94
    }
95

    
96
    return this;
97
}
98

    
99
/** Read data from the pipe.
100
*/
101

    
102
static FILE *rwpipe_reader( rwpipe *this )
103
{
104
    if ( this != NULL )
105
        return this->reader;
106
    else
107
        return NULL;
108
}
109

    
110
/** Write data to the pipe.
111
*/
112

    
113
static FILE *rwpipe_writer( rwpipe *this )
114
{
115
    if ( this != NULL )
116
        return this->writer;
117
    else
118
        return NULL;
119
}
120

    
121
/* Read a number from the pipe - assumes PNM style headers.
122
*/
123

    
124
static int rwpipe_read_number( rwpipe *rw )
125
{
126
    int value = 0;
127
    int c = 0;
128
    FILE *in = rwpipe_reader( rw );
129

    
130
    do
131
    {
132
        c = fgetc( in );
133

    
134
        while( c != EOF && !isdigit( c ) && c != '#' )
135
            c = fgetc( in );
136

    
137
        if ( c == '#' )
138
            while( c != EOF && c != '\n' )
139
                c = fgetc( in );
140
    }
141
    while ( c != EOF && !isdigit( c ) );
142

    
143
    while( c != EOF && isdigit( c ) )
144
    {
145
        value = value * 10 + ( c - '0' );
146
        c = fgetc( in );
147
    }
148

    
149
    return value;
150
}
151

    
152
/** Read a PPM P6 header.
153
*/
154

    
155
static int rwpipe_read_ppm_header( rwpipe *rw, int *width, int *height )
156
{
157
    char line[ 3 ];
158
    FILE *in = rwpipe_reader( rw );
159
    int max;
160

    
161
    fgets( line, 3, in );
162
    if ( !strncmp( line, "P6", 2 ) )
163
    {
164
        *width = rwpipe_read_number( rw );
165
        *height = rwpipe_read_number( rw );
166
        max = rwpipe_read_number( rw );
167
        return max != 255 || *width <= 0 || *height <= 0;
168
    }
169
    return 1;
170
}
171

    
172
/** Close the pipe and process.
173
*/
174

    
175
static void rwpipe_close( rwpipe *this )
176
{
177
    if ( this != NULL )
178
    {
179
        fclose( this->reader );
180
        fclose( this->writer );
181
        waitpid( this->pid, NULL, 0 );
182
        av_free( this );
183
    }
184
}
185

    
186
/** Context info for this vhook - stores the pipe and image buffers.
187
*/
188

    
189
typedef struct
190
{
191
    rwpipe *rw;
192
    int size1;
193
    char *buf1;
194
    int size2;
195
    char *buf2;
196

    
197
    // This vhook first converts frame to RGB ...
198
    struct SwsContext *toRGB_convert_ctx;
199
    // ... then processes it via a PPM command pipe ...
200
    // ... and finally converts back frame from RGB to initial format
201
    struct SwsContext *fromRGB_convert_ctx;
202
}
203
ContextInfo;
204

    
205
/** Initialise the context info for this vhook.
206
*/
207

    
208
int Configure(void **ctxp, int argc, char *argv[])
209
{
210
    if ( argc > 1 )
211
    {
212
        *ctxp = av_mallocz(sizeof(ContextInfo));
213
        if ( ctxp != NULL && argc > 1 )
214
        {
215
            ContextInfo *info = (ContextInfo *)*ctxp;
216
            info->rw = rwpipe_open( argc - 1, &argv[ 1 ] );
217
            return 0;
218
        }
219
    }
220
    return 1;
221
}
222

    
223
/** Process a frame.
224
*/
225

    
226
void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts)
227
{
228
    int err = 0;
229
    ContextInfo *ci = (ContextInfo *) ctx;
230
    AVPicture picture1;
231
    AVPicture picture2;
232
    AVPicture *pict = picture;
233
    int out_width;
234
    int out_height;
235
    int i;
236
    uint8_t *ptr = NULL;
237
    FILE *in = rwpipe_reader( ci->rw );
238
    FILE *out = rwpipe_writer( ci->rw );
239

    
240
    /* Check that we have a pipe to talk to. */
241
    if ( in == NULL || out == NULL )
242
        err = 1;
243

    
244
    /* Convert to RGB24 if necessary */
245
    if ( !err && pix_fmt != PIX_FMT_RGB24 )
246
    {
247
        int size = avpicture_get_size(PIX_FMT_RGB24, width, height);
248

    
249
        if ( size != ci->size1 )
250
        {
251
            av_free( ci->buf1 );
252
            ci->buf1 = av_malloc(size);
253
            ci->size1 = size;
254
            err = ci->buf1 == NULL;
255
        }
256

    
257
        if ( !err )
258
        {
259
            avpicture_fill(&picture1, ci->buf1, PIX_FMT_RGB24, width, height);
260

    
261
            // if we already got a SWS context, let's realloc if is not re-useable
262
            ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx,
263
                                        width, height, pix_fmt,
264
                                        width, height, PIX_FMT_RGB24,
265
                                        sws_flags, NULL, NULL, NULL);
266
            if (ci->toRGB_convert_ctx == NULL) {
267
                av_log(NULL, AV_LOG_ERROR,
268
                       "Cannot initialize the toRGB conversion context\n");
269
                exit(1);
270
            }
271

    
272
// img_convert parameters are          2 first destination, then 4 source
273
// sws_scale   parameters are context, 4 first source,      then 2 destination
274
            sws_scale(ci->toRGB_convert_ctx,
275
                     picture->data, picture->linesize, 0, height,
276
                     picture1.data, picture1.linesize);
277

    
278
            pict = &picture1;
279
        }
280
    }
281

    
282
    /* Write out the PPM */
283
    if ( !err )
284
    {
285
        ptr = pict->data[ 0 ];
286
        fprintf( out, "P6\n%d %d\n255\n", width, height );
287
        for ( i = 0; !err && i < height; i ++ )
288
        {
289
            err = !fwrite( ptr, width * 3, 1, out );
290
            ptr += pict->linesize[ 0 ];
291
        }
292
        if ( !err )
293
            err = fflush( out );
294
    }
295

    
296
    /* Read the PPM returned. */
297
    if ( !err && !rwpipe_read_ppm_header( ci->rw, &out_width, &out_height ) )
298
    {
299
        int size = avpicture_get_size(PIX_FMT_RGB24, out_width, out_height);
300

    
301
        if ( size != ci->size2 )
302
        {
303
            av_free( ci->buf2 );
304
            ci->buf2 = av_malloc(size);
305
            ci->size2 = size;
306
            err = ci->buf2 == NULL;
307
        }
308

    
309
        if ( !err )
310
        {
311
            avpicture_fill(&picture2, ci->buf2, PIX_FMT_RGB24, out_width, out_height);
312
            ptr = picture2.data[ 0 ];
313
            for ( i = 0; !err && i < out_height; i ++ )
314
            {
315
                err = !fread( ptr, out_width * 3, 1, in );
316
                ptr += picture2.linesize[ 0 ];
317
            }
318
        }
319
    }
320

    
321
    /* Convert the returned PPM back to the input format */
322
    if ( !err )
323
    {
324
        /* The out_width/out_height returned from the PPM
325
         * filter won't necessarily be the same as width and height
326
         * but it will be scaled anyway to width/height.
327
         */
328
        av_log(NULL, AV_LOG_DEBUG,
329
                  "PPM vhook: Input dimensions: %d x %d Output dimensions: %d x %d\n",
330
                  width, height, out_width, out_height);
331
        ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx,
332
                                        out_width, out_height, PIX_FMT_RGB24,
333
                                        width,     height,     pix_fmt,
334
                                        sws_flags, NULL, NULL, NULL);
335
        if (ci->fromRGB_convert_ctx == NULL) {
336
            av_log(NULL, AV_LOG_ERROR,
337
                   "Cannot initialize the fromRGB conversion context\n");
338
            exit(1);
339
        }
340

    
341
// img_convert parameters are          2 first destination, then 4 source
342
// sws_scale   parameters are context, 4 first source,      then 2 destination
343
        sws_scale(ci->fromRGB_convert_ctx,
344
                 picture2.data, picture2.linesize, 0, out_height,
345
                 picture->data, picture->linesize);
346
    }
347
}
348

    
349
/** Clean up the effect.
350
*/
351

    
352
void Release(void *ctx)
353
{
354
    ContextInfo *ci;
355
    ci = (ContextInfo *) ctx;
356

    
357
    if (ctx)
358
    {
359
        rwpipe_close( ci->rw );
360
        av_free( ci->buf1 );
361
        av_free( ci->buf2 );
362
        sws_freeContext(ci->toRGB_convert_ctx);
363
        sws_freeContext(ci->fromRGB_convert_ctx);
364
        av_free(ctx);
365
    }
366
}
367