Statistics
| Branch: | Revision:

ffmpeg / vhook / ppm.c @ a4dd5fb7

History | View | Annotate | Download (9.31 KB)

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

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

    
30
static int sws_flags = SWS_BICUBIC;
31

    
32
/** Bi-directional pipe structure.
33
*/
34

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

    
43
/** Create a bidirectional pipe for the given command.
44
*/
45

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

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

    
55
        pipe( input );
56
        pipe( output );
57

    
58
        this->pid = fork();
59

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

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

    
73
            dup2( output[ 0 ], STDIN_FILENO );
74
            dup2( input[ 1 ], STDOUT_FILENO );
75

    
76
            close( input[ 0 ] );
77
            close( input[ 1 ] );
78
            close( output[ 0 ] );
79
            close( output[ 1 ] );
80

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

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

    
94
    return this;
95
}
96

    
97
/** Read data from the pipe.
98
*/
99

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

    
108
/** Write data to the pipe.
109
*/
110

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

    
119
/* Read a number from the pipe - assumes PNM style headers.
120
*/
121

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

    
128
    do
129
    {
130
        c = fgetc( in );
131

    
132
        while( c != EOF && !isdigit( c ) && c != '#' )
133
            c = fgetc( in );
134

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

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

    
147
    return value;
148
}
149

    
150
/** Read a PPM P6 header.
151
*/
152

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

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

    
170
/** Close the pipe and process.
171
*/
172

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

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

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

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

    
203
/** Initialise the context info for this vhook.
204
*/
205

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

    
221
/** Process a frame.
222
*/
223

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

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

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

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

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

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

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

    
276
            pict = &picture1;
277
        }
278
    }
279

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

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

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

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

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

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

    
347
/** Clean up the effect.
348
*/
349

    
350
void Release(void *ctx)
351
{
352
    ContextInfo *ci;
353
    ci = (ContextInfo *) ctx;
354

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