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 "libavutil/avstring.h" 29#include "libavformat/framehook.h" 30#include "libavformat/avformat.h" 31#include "libswscale/swscale.h" 32#undef fprintf 33 34static int sws_flags = SWS_BICUBIC; 35 36/** Bi-directional pipe structure. 37*/ 38 39typedef struct rwpipe 40{ 41 int pid; 42 FILE *reader; 43 FILE *writer; 44} 45rwpipe; 46 47/** Create a bidirectional pipe for the given command. 48*/ 49 50static rwpipe *rwpipe_open( int argc, char *argv[] ) 51{ 52 rwpipe *this = av_mallocz( sizeof( rwpipe ) ); 53 54 if ( this != NULL ) 55 { 56 int input[ 2 ]; 57 int output[ 2 ]; 58 59 if (!pipe( input )) 60 return NULL; 61 62 if (!pipe( output )) 63 return NULL; 64 65 this->pid = fork(); 66 67 if ( this->pid == 0 ) 68 { 69#define COMMAND_SIZE 10240 70 char *command = av_mallocz( COMMAND_SIZE ); 71 int i; 72 73 strcpy( command, "" ); 74 for ( i = 0; i < argc; i ++ ) 75 { 76 av_strlcat( command, argv[ i ], COMMAND_SIZE ); 77 av_strlcat( command, " ", COMMAND_SIZE ); 78 } 79 80 dup2( output[ 0 ], STDIN_FILENO ); 81 dup2( input[ 1 ], STDOUT_FILENO ); 82 83 close( input[ 0 ] ); 84 close( input[ 1 ] ); 85 close( output[ 0 ] ); 86 close( output[ 1 ] ); 87 88 execl("/bin/sh", "sh", "-c", command, (char*)NULL ); 89 _exit( 255 ); 90 } 91 else 92 { 93 close( input[ 1 ] ); 94 close( output[ 0 ] ); 95 96 this->reader = fdopen( input[ 0 ], "r" ); 97 this->writer = fdopen( output[ 1 ], "w" ); 98 } 99 } 100 101 return this; 102} 103 104/** Read data from the pipe. 105*/ 106 107static FILE *rwpipe_reader( rwpipe *this ) 108{ 109 if ( this != NULL ) 110 return this->reader; 111 else 112 return NULL; 113} 114 115/** Write data to the pipe. 116*/ 117 118static FILE *rwpipe_writer( rwpipe *this ) 119{ 120 if ( this != NULL ) 121 return this->writer; 122 else 123 return NULL; 124} 125 126/* Read a number from the pipe - assumes PNM style headers. 127*/ 128 129static int rwpipe_read_number( rwpipe *rw ) 130{ 131 int value = 0; 132 int c = 0; 133 FILE *in = rwpipe_reader( rw ); 134 135 do 136 { 137 c = fgetc( in ); 138 139 while( c != EOF && !isdigit( c ) && c != '#' ) 140 c = fgetc( in ); 141 142 if ( c == '#' ) 143 while( c != EOF && c != '\n' ) 144 c = fgetc( in ); 145 } 146 while ( c != EOF && !isdigit( c ) ); 147 148 while( c != EOF && isdigit( c ) ) 149 { 150 value = value * 10 + ( c - '0' ); 151 c = fgetc( in ); 152 } 153 154 return value; 155} 156 157/** Read a PPM P6 header. 158*/ 159 160static int rwpipe_read_ppm_header( rwpipe *rw, int *width, int *height ) 161{ 162 char line[ 3 ]; 163 FILE *in = rwpipe_reader( rw ); 164 int max; 165 166 if (!fgets( line, 3, in )) 167 return -1; 168 169 if ( !strncmp( line, "P6", 2 ) ) 170 { 171 *width = rwpipe_read_number( rw ); 172 *height = rwpipe_read_number( rw ); 173 max = rwpipe_read_number( rw ); 174 return max != 255 || *width <= 0 || *height <= 0; 175 } 176 return 1; 177} 178 179/** Close the pipe and process. 180*/ 181 182static void rwpipe_close( rwpipe *this ) 183{ 184 if ( this != NULL ) 185 { 186 fclose( this->reader ); 187 fclose( this->writer ); 188 waitpid( this->pid, NULL, 0 ); 189 av_free( this ); 190 } 191} 192 193/** Context info for this vhook - stores the pipe and image buffers. 194*/ 195 196typedef struct 197{ 198 rwpipe *rw; 199 int size1; 200 char *buf1; 201 int size2; 202 char *buf2; 203 204 // This vhook first converts frame to RGB ... 205 struct SwsContext *toRGB_convert_ctx; 206 // ... then processes it via a PPM command pipe ... 207 // ... and finally converts back frame from RGB to initial format 208 struct SwsContext *fromRGB_convert_ctx; 209} 210ContextInfo; 211 212/** Initialise the context info for this vhook. 213*/ 214 215int Configure(void **ctxp, int argc, char *argv[]) 216{ 217 if ( argc > 1 ) 218 { 219 *ctxp = av_mallocz(sizeof(ContextInfo)); 220 if ( *ctxp != NULL && argc > 1 ) 221 { 222 ContextInfo *info = (ContextInfo *)*ctxp; 223 info->rw = rwpipe_open( argc - 1, &argv[ 1 ] ); 224 return 0; 225 } 226 } 227 return 1; 228} 229 230/** Process a frame. 231*/ 232 233void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts) 234{ 235 int err = 0; 236 ContextInfo *ci = (ContextInfo *) ctx; 237 AVPicture picture1; 238 AVPicture picture2; 239 AVPicture *pict = picture; 240 int out_width; 241 int out_height; 242 int i; 243 uint8_t *ptr = NULL; 244 FILE *in = rwpipe_reader( ci->rw ); 245 FILE *out = rwpipe_writer( ci->rw ); 246 247 /* Check that we have a pipe to talk to. */ 248 if ( in == NULL || out == NULL ) 249 err = 1; 250 251 /* Convert to RGB24 if necessary */ 252 if ( !err && pix_fmt != PIX_FMT_RGB24 ) 253 { 254 int size = avpicture_get_size(PIX_FMT_RGB24, width, height); 255 256 if ( size != ci->size1 ) 257 { 258 av_free( ci->buf1 ); 259 ci->buf1 = av_malloc(size); 260 ci->size1 = size; 261 err = ci->buf1 == NULL; 262 } 263 264 if ( !err ) 265 { 266 avpicture_fill(&picture1, ci->buf1, PIX_FMT_RGB24, width, height); 267 268 // if we already got a SWS context, let's realloc if is not re-useable 269 ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx, 270 width, height, pix_fmt, 271 width, height, PIX_FMT_RGB24, 272 sws_flags, NULL, NULL, NULL); 273 if (ci->toRGB_convert_ctx == NULL) { 274 av_log(NULL, AV_LOG_ERROR, 275 "Cannot initialize the toRGB conversion context\n"); 276 return; 277 } 278 279// img_convert parameters are 2 first destination, then 4 source 280// sws_scale parameters are context, 4 first source, then 2 destination 281 sws_scale(ci->toRGB_convert_ctx, 282 picture->data, picture->linesize, 0, height, 283 picture1.data, picture1.linesize); 284 285 pict = &picture1; 286 } 287 } 288 289 /* Write out the PPM */ 290 if ( !err ) 291 { 292 ptr = pict->data[ 0 ]; 293 fprintf( out, "P6\n%d %d\n255\n", width, height ); 294 for ( i = 0; !err && i < height; i ++ ) 295 { 296 err = !fwrite( ptr, width * 3, 1, out ); 297 ptr += pict->linesize[ 0 ]; 298 } 299 if ( !err ) 300 err = fflush( out ); 301 } 302 303 /* Read the PPM returned. */ 304 if ( !err && !rwpipe_read_ppm_header( ci->rw, &out_width, &out_height ) ) 305 { 306 int size = avpicture_get_size(PIX_FMT_RGB24, out_width, out_height); 307 308 if ( size != ci->size2 ) 309 { 310 av_free( ci->buf2 ); 311 ci->buf2 = av_malloc(size); 312 ci->size2 = size; 313 err = ci->buf2 == NULL; 314 } 315 316 if ( !err ) 317 { 318 avpicture_fill(&picture2, ci->buf2, PIX_FMT_RGB24, out_width, out_height); 319 ptr = picture2.data[ 0 ]; 320 for ( i = 0; !err && i < out_height; i ++ ) 321 { 322 err = !fread( ptr, out_width * 3, 1, in ); 323 ptr += picture2.linesize[ 0 ]; 324 } 325 } 326 } 327 328 /* Convert the returned PPM back to the input format */ 329 if ( !err ) 330 { 331 /* The out_width/out_height returned from the PPM 332 * filter won't necessarily be the same as width and height 333 * but it will be scaled anyway to width/height. 334 */ 335 av_log(NULL, AV_LOG_DEBUG, 336 "PPM vhook: Input dimensions: %d x %d Output dimensions: %d x %d\n", 337 width, height, out_width, out_height); 338 ci->fromRGB_convert_ctx = sws_getCachedContext(ci->fromRGB_convert_ctx, 339 out_width, out_height, PIX_FMT_RGB24, 340 width, height, pix_fmt, 341 sws_flags, NULL, NULL, NULL); 342 if (ci->fromRGB_convert_ctx == NULL) { 343 av_log(NULL, AV_LOG_ERROR, 344 "Cannot initialize the fromRGB conversion context\n"); 345 return; 346 } 347 348// img_convert parameters are 2 first destination, then 4 source 349// sws_scale parameters are context, 4 first source, then 2 destination 350 sws_scale(ci->fromRGB_convert_ctx, 351 picture2.data, picture2.linesize, 0, out_height, 352 picture->data, picture->linesize); 353 } 354} 355 356/** Clean up the effect. 357*/ 358 359void Release(void *ctx) 360{ 361 ContextInfo *ci; 362 ci = (ContextInfo *) ctx; 363 364 if (ctx) 365 { 366 rwpipe_close( ci->rw ); 367 av_free( ci->buf1 ); 368 av_free( ci->buf2 ); 369 sws_freeContext(ci->toRGB_convert_ctx); 370 sws_freeContext(ci->fromRGB_convert_ctx); 371 av_free(ctx); 372 } 373} 374 375