From 133ee69dff02b7db62dc99aca3f60c534c90eb34 Mon Sep 17 00:00:00 2001 From: Steven Walters Date: Sat, 7 Nov 2009 17:07:28 -0800 Subject: [PATCH] Fix issues relating to input/output files being pipes/FIFOs --- common/common.h | 1 + common/osdep.h | 8 ++ encoder/encoder.c | 10 ++- encoder/ratecontrol.c | 7 +- input/avis.c | 10 +++ input/y4m.c | 46 ++++++---- input/yuv.c | 30 +++++-- output/matroska_ebml.c | 11 +-- output/mp4.c | 9 ++ output/raw.c | 4 +- x264.c | 194 ++++++++++++++++++++++++++--------------- 11 files changed, 224 insertions(+), 106 deletions(-) diff --git a/common/common.h b/common/common.h index d7c51d5e..2496d0f2 100644 --- a/common/common.h +++ b/common/common.h @@ -63,6 +63,7 @@ do {\ /**************************************************************************** * Includes ****************************************************************************/ +#include #include "osdep.h" #include #include diff --git a/common/osdep.h b/common/osdep.h index 194990b8..2f9e1af0 100644 --- a/common/osdep.h +++ b/common/osdep.h @@ -226,4 +226,12 @@ static int ALWAYS_INLINE x264_clz( uint32_t x ) #define x264_lower_thread_priority(p) #endif +static inline uint8_t x264_is_regular_file( FILE *filehandle ) +{ + struct stat file_stat; + if( fstat( fileno( filehandle ), &file_stat ) ) + return 0; + return S_ISREG( file_stat.st_mode ); +} + #endif /* X264_OSDEP_H */ diff --git a/encoder/encoder.c b/encoder/encoder.c index 7cf6a963..32fab040 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -920,13 +920,17 @@ x264_t *x264_encoder_open( x264_param_t *param ) { /* create or truncate the reconstructed video file */ FILE *f = fopen( h->param.psz_dump_yuv, "w" ); - if( f ) - fclose( f ); - else + if( !f ) { x264_log( h, X264_LOG_ERROR, "dump_yuv: can't write to %s\n", h->param.psz_dump_yuv ); goto fail; } + else if( !x264_is_regular_file( f ) ) + { + x264_log( h, X264_LOG_ERROR, "dump_yuv: incompatible with non-regular file %s\n", h->param.psz_dump_yuv ); + goto fail; + } + fclose( f ); } x264_log( h, X264_LOG_INFO, "profile %s, level %d.%d\n", diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 49e88f70..1d848a8c 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -880,11 +880,13 @@ void x264_ratecontrol_delete( x264_t *h ) { x264_ratecontrol_t *rc = h->rc; int i; + int b_regular_file; if( rc->p_stat_file_out ) { + b_regular_file = x264_is_regular_file( rc->p_stat_file_out ); fclose( rc->p_stat_file_out ); - if( h->i_frame >= rc->num_entries ) + if( h->i_frame >= rc->num_entries && b_regular_file ) if( rename( rc->psz_stat_file_tmpname, h->param.rc.psz_stat_out ) != 0 ) { x264_log( h, X264_LOG_ERROR, "failed to rename \"%s\" to \"%s\"\n", @@ -894,8 +896,9 @@ void x264_ratecontrol_delete( x264_t *h ) } if( rc->p_mbtree_stat_file_out ) { + b_regular_file = x264_is_regular_file( rc->p_mbtree_stat_file_out ); fclose( rc->p_mbtree_stat_file_out ); - if( h->i_frame >= rc->num_entries ) + if( h->i_frame >= rc->num_entries && b_regular_file ) if( rename( rc->psz_mbtree_stat_file_tmpname, rc->psz_mbtree_stat_file_name ) != 0 ) { x264_log( h, X264_LOG_ERROR, "failed to rename \"%s\" to \"%s\"\n", diff --git a/input/avis.c b/input/avis.c index 2ca16340..f9517e34 100644 --- a/input/avis.c +++ b/input/avis.c @@ -33,6 +33,16 @@ typedef struct static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) { + FILE *fh = fopen( psz_filename, "r" ); + if( !fh ) + return -1; + else if( !x264_is_regular_file( fh ) ) + { + fprintf( stderr, "avis [error]: AVIS input is incompatible with non-regular file `%s'\n", psz_filename ); + return -1; + } + fclose( fh ); + avis_hnd_t *h = malloc( sizeof(avis_hnd_t) ); if( !h ) return -1; diff --git a/input/y4m.c b/input/y4m.c index a04cd211..5dba38a5 100644 --- a/input/y4m.c +++ b/input/y4m.c @@ -160,10 +160,11 @@ static int get_frame_total( hnd_t handle ) { y4m_hnd_t *h = handle; int i_frame_total = 0; - uint64_t init_pos = ftell( h->fh ); - if( !fseek( h->fh, 0, SEEK_END ) ) + if( x264_is_regular_file( h->fh ) ) { + uint64_t init_pos = ftell( h->fh ); + fseek( h->fh, 0, SEEK_END ); uint64_t i_size = ftell( h->fh ); fseek( h->fh, init_pos, SEEK_SET ); i_frame_total = (int)((i_size - h->seq_header_len) / @@ -173,20 +174,12 @@ static int get_frame_total( hnd_t handle ) return i_frame_total; } -static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +static int read_frame_internal( x264_picture_t *p_pic, y4m_hnd_t *h ) { - y4m_hnd_t *h = handle; int slen = strlen( Y4M_FRAME_MAGIC ); int i = 0; char header[16]; - if( i_frame != h->next_frame ) - { - if( fseek( h->fh, (uint64_t)i_frame*(3*(h->width*h->height)/2+h->frame_header_len) - + h->seq_header_len, SEEK_SET ) ) - return -1; - } - /* Read frame header - without terminating '\n' */ if( fread( header, 1, slen, h->fh ) != slen ) return -1; @@ -209,13 +202,36 @@ static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) } h->frame_header_len = i+slen+1; - if( fread( p_pic->img.plane[0], 1, h->width*h->height, h->fh ) <= 0 - || fread( p_pic->img.plane[1], 1, h->width * h->height / 4, h->fh ) <= 0 - || fread( p_pic->img.plane[2], 1, h->width * h->height / 4, h->fh ) <= 0 ) + if( fread( p_pic->img.plane[0], h->width * h->height, 1, h->fh ) <= 0 + || fread( p_pic->img.plane[1], h->width * h->height / 4, 1, h->fh ) <= 0 + || fread( p_pic->img.plane[2], h->width * h->height / 4, 1, h->fh ) <= 0 ) return -1; - h->next_frame = i_frame+1; + return 0; +} +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + y4m_hnd_t *h = handle; + + if( i_frame > h->next_frame ) + { + if( x264_is_regular_file( h->fh ) ) + fseek( h->fh, (uint64_t)i_frame*(3*(h->width*h->height)/2+h->frame_header_len) + + h->seq_header_len, SEEK_SET ); + else + while( i_frame > h->next_frame ) + { + if( read_frame_internal( p_pic, h ) ) + return -1; + h->next_frame++; + } + } + + if( read_frame_internal( p_pic, h ) ) + return -1; + + h->next_frame = i_frame+1; return 0; } diff --git a/input/yuv.c b/input/yuv.c index 98dc46c2..6fe3d5ed 100644 --- a/input/yuv.c +++ b/input/yuv.c @@ -55,8 +55,9 @@ static int get_frame_total( hnd_t handle ) yuv_hnd_t *h = handle; int i_frame_total = 0; - if( !fseek( h->fh, 0, SEEK_END ) ) + if( x264_is_regular_file( h->fh ) ) { + fseek( h->fh, 0, SEEK_END ); uint64_t i_size = ftell( h->fh ); fseek( h->fh, 0, SEEK_SET ); i_frame_total = (int)(i_size / ( h->width * h->height * 3 / 2 )); @@ -65,21 +66,34 @@ static int get_frame_total( hnd_t handle ) return i_frame_total; } +static int read_frame_internal( x264_picture_t *p_pic, yuv_hnd_t *h ) +{ + return fread( p_pic->img.plane[0], h->width * h->height, 1, h->fh ) <= 0 + || fread( p_pic->img.plane[1], h->width * h->height / 4, 1, h->fh ) <= 0 + || fread( p_pic->img.plane[2], h->width * h->height / 4, 1, h->fh ) <= 0; +} + static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) { yuv_hnd_t *h = handle; - if( i_frame != h->next_frame ) - if( fseek( h->fh, (uint64_t)i_frame * h->width * h->height * 3 / 2, SEEK_SET ) ) - return -1; + if( i_frame > h->next_frame ) + { + if( x264_is_regular_file( h->fh ) ) + fseek( h->fh, (uint64_t)i_frame * h->width * h->height * 3 / 2, SEEK_SET ); + else + while( i_frame > h->next_frame ) + { + if( read_frame_internal( p_pic, h ) ) + return -1; + h->next_frame++; + } + } - if( fread( p_pic->img.plane[0], 1, h->width * h->height, h->fh ) <= 0 - || fread( p_pic->img.plane[1], 1, h->width * h->height / 4, h->fh ) <= 0 - || fread( p_pic->img.plane[2], 1, h->width * h->height / 4, h->fh ) <= 0 ) + if( read_frame_internal( p_pic, h ) ) return -1; h->next_frame = i_frame+1; - return 0; } diff --git a/output/matroska_ebml.c b/output/matroska_ebml.c index 4e6bf0c7..c374d098 100644 --- a/output/matroska_ebml.c +++ b/output/matroska_ebml.c @@ -18,9 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. *****************************************************************************/ -#include -#include -#include "common/osdep.h" +#include "muxers.h" #include "matroska_ebml.h" #define CLSIZE 1048576 @@ -346,7 +344,10 @@ mk_writer *mk_create_writer( const char *filename ) return NULL; } - w->fp = fopen( filename, "wb" ); + if( !strcmp( filename, "-" ) ) + w->fp = stdout; + else + w->fp = fopen( filename, "wb" ); if( !w->fp ) { mk_destroy_contexts( w ); @@ -545,7 +546,7 @@ int mk_close( mk_writer *w ) int ret = 0; if( mk_flush_frame( w ) < 0 || mk_close_cluster( w ) < 0 ) ret = -1; - if( w->wrote_header ) + if( w->wrote_header && x264_is_regular_file( w->fp ) ) { fseek( w->fp, w->duration_ptr, SEEK_SET ); if( mk_write_float_raw( w->root, (float)((double)(w->max_frame_tc+w->def_duration) / w->timescale) ) < 0 || diff --git a/output/mp4.c b/output/mp4.c index c361b2be..dcac72e9 100644 --- a/output/mp4.c +++ b/output/mp4.c @@ -126,6 +126,15 @@ static int open_file( char *psz_filename, hnd_t *p_handle ) mp4_hnd_t *p_mp4; *p_handle = NULL; + FILE *fh = fopen( psz_filename, "w" ); + if( !fh ) + return -1; + else if( !x264_is_regular_file( fh ) ) + { + fprintf( stderr, "mp4 [error]: MP4 output is incompatible with non-regular file `%s'\n", psz_filename ); + return -1; + } + fclose( fh ); if( !(p_mp4 = malloc( sizeof(mp4_hnd_t) )) ) return -1; diff --git a/output/raw.c b/output/raw.c index bc0b0daf..1bfc3562 100644 --- a/output/raw.c +++ b/output/raw.c @@ -25,7 +25,9 @@ static int open_file( char *psz_filename, hnd_t *p_handle ) { - if( !(*p_handle = fopen( psz_filename, "w+b" )) ) + if( !strcmp( psz_filename, "-" ) ) + *p_handle = stdout; + else if( !(*p_handle = fopen( psz_filename, "w+b" )) ) return -1; return 0; diff --git a/x264.c b/x264.c index d845a478..48c5ccaf 100644 --- a/x264.c +++ b/x264.c @@ -64,6 +64,10 @@ typedef struct { cli_input_t input; static cli_output_t output; +/* i/o modules that work with pipes (and fifos) */ +static const char * const stdin_format_names[] = { "yuv", "y4m", 0 }; +static const char * const stdout_format_names[] = { "raw", "mkv", 0 }; + static void Help( x264_param_t *defaults, int longhelp ); static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ); static int Encode( x264_param_t *param, cli_opt_t *opt ); @@ -355,6 +359,10 @@ static void Help( x264_param_t *defaults, int longhelp ) H0( "Input/Output:\n" ); H0( "\n" ); H0( " -o, --output Specify output file\n" ); + H1( " --stdout Specify stdout format [\"%s\"]\n" + " - raw, mkv\n", stdout_format_names[0] ); + H1( " --stdin Specify stdin format [\"%s\"]\n" + " - yuv, y4m\n", stdin_format_names[0] ); H0( " --sar width:height Specify Sample Aspect Ratio\n" ); H0( " --fps Specify framerate\n" ); H0( " --seek First frame to encode\n" ); @@ -393,6 +401,8 @@ static void Help( x264_param_t *defaults, int longhelp ) #define OPT_SLOWFIRSTPASS 267 #define OPT_FULLHELP 268 #define OPT_FPS 269 +#define OPT_STDOUT_FORMAT 270 +#define OPT_STDIN_FORMAT 271 static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw"; static struct option long_options[] = @@ -437,6 +447,8 @@ static struct option long_options[] = { "frames", required_argument, NULL, OPT_FRAMES }, { "seek", required_argument, NULL, OPT_SEEK }, { "output", required_argument, NULL, 'o' }, + { "stdout", required_argument, NULL, OPT_STDOUT_FORMAT }, + { "stdin", required_argument, NULL, OPT_STDIN_FORMAT }, { "analyse", required_argument, NULL, 0 }, { "partitions", required_argument, NULL, 'A' }, { "direct", required_argument, NULL, 0 }, @@ -519,31 +531,100 @@ static struct option long_options[] = {0, 0, 0, 0} }; +static int select_output( char *filename, const char *pipe_format ) +{ + char *ext = filename + strlen( filename ) - 1; + while( *ext != '.' && ext > filename ) + ext--; + + if( !strcasecmp( ext, ".mp4" ) ) + { +#ifdef MP4_OUTPUT + output = mp4_output; +#else + fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" ); + return -1; +#endif + } + else if( !strcasecmp( ext, ".mkv" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "mkv" )) ) + output = mkv_output; + else + output = raw_output; + return 0; +} + +static int select_input( char *filename, char *resolution, const char *pipe_format, x264_param_t *param ) +{ + char *psz = filename + strlen( filename ) - 1; + while( psz > filename && *psz != '.' ) + psz--; + + if( !strcasecmp( psz, ".avi" ) || !strcasecmp( psz, ".avs" ) ) + { +#ifdef AVIS_INPUT + input = avis_input; +#else + fprintf( stderr, "x264 [error]: not compiled with AVIS input support\n" ); + return -1; +#endif + } + else if( !strcasecmp( psz, ".y4m" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "y4m" )) ) + input = y4m_input; + else // yuv + { + if( !resolution ) + { + /* try to parse the file name */ + for( psz = filename; *psz; psz++ ) + if( *psz >= '0' && *psz <= '9' && + sscanf( psz, "%ux%u", ¶m->i_width, ¶m->i_height ) == 2 ) + { + if( param->i_log_level >= X264_LOG_INFO ) + fprintf( stderr, "x264 [info]: %dx%d (given by file name) @ %.2f fps\n", param->i_width, + param->i_height, (double)param->i_fps_num / param->i_fps_den ); + break; + } + } + else + { + sscanf( resolution, "%ux%u", ¶m->i_width, ¶m->i_height ); + if( param->i_log_level >= X264_LOG_INFO ) + fprintf( stderr, "x264 [info]: %dx%d @ %.2f fps\n", param->i_width, param->i_height, + (double)param->i_fps_num / param->i_fps_den ); + } + if( !param->i_width || !param->i_height ) + { + fprintf( stderr, "x264 [error]: Rawyuv input requires a resolution.\n" ); + return -1; + } + input = yuv_input; + } + + return 0; +} + /***************************************************************************** * Parse: *****************************************************************************/ static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) { - char *psz_filename = NULL; + char *input_filename = NULL; + const char *stdin_format = stdin_format_names[0]; + char *output_filename = NULL; + const char *stdout_format = stdout_format_names[0]; x264_param_t defaults = *param; - char *psz; char *profile = NULL; - int b_avis = 0; - int b_y4m = 0; int b_thread_input = 0; int b_turbo = 1; int b_pass1 = 0; int b_user_ref = 0; int b_user_fps = 0; + int i; memset( opt, 0, sizeof(cli_opt_t) ); opt->b_progress = 1; - /* Default i/o modules */ - input = yuv_input; - output = raw_output; - /* Presets are applied before all other options. */ for( optind = 0;; ) { @@ -773,24 +854,27 @@ static int Parse( int argc, char **argv, opt->i_seek = atoi( optarg ); break; case 'o': - if( !strncasecmp(optarg + strlen(optarg) - 4, ".mp4", 4) ) + output_filename = optarg; + break; + case OPT_STDOUT_FORMAT: + for( i = 0; stdout_format_names[i] && strcasecmp( stdout_format_names[i], optarg ); ) + i++; + if( !stdout_format_names[i] ) { -#ifdef MP4_OUTPUT - output = mp4_output; -#else - fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" ); + fprintf( stderr, "x264 [error]: invalid stdout format `%s'\n", optarg ); return -1; -#endif } - else if( !strncasecmp(optarg + strlen(optarg) - 4, ".mkv", 4) ) - output = mkv_output; - if( !strcmp(optarg, "-") ) - opt->hout = stdout; - else if( output.open_file( optarg, &opt->hout ) ) + stdout_format = optarg; + break; + case OPT_STDIN_FORMAT: + for( i = 0; stdin_format_names[i] && strcasecmp( stdin_format_names[i], optarg ); ) + i++; + if( !stdin_format_names[i] ) { - fprintf( stderr, "x264 [error]: can't open output file `%s'\n", optarg ); + fprintf( stderr, "x264 [error]: invalid stdin format `%s'\n", optarg ); return -1; } + stdin_format = optarg; break; case OPT_QPFILE: opt->qpfile = fopen( optarg, "rb" ); @@ -799,6 +883,12 @@ static int Parse( int argc, char **argv, fprintf( stderr, "x264 [error]: can't open `%s'\n", optarg ); return -1; } + else if( !x264_is_regular_file( opt->qpfile ) ) + { + fprintf( stderr, "x264 [error]: qpfile incompatible with non-regular file `%s'\n", optarg ); + fclose( opt->qpfile ); + return -1; + } break; case OPT_THREAD_INPUT: b_thread_input = 1; @@ -918,72 +1008,32 @@ generic_option: } /* Get the file name */ - if( optind > argc - 1 || !opt->hout ) + if( optind > argc - 1 || !output_filename ) { fprintf( stderr, "x264 [error]: No %s file. Run x264 --help for a list of options.\n", optind > argc - 1 ? "input" : "output" ); return -1; } - psz_filename = argv[optind++]; - - /* check demuxer type */ - psz = psz_filename + strlen(psz_filename) - 1; - while( psz > psz_filename && *psz != '.' ) - psz--; - if( !strncasecmp( psz, ".avi", 4 ) || !strncasecmp( psz, ".avs", 4 ) ) - b_avis = 1; - if( !strncasecmp( psz, ".y4m", 4 ) ) - b_y4m = 1; + input_filename = argv[optind++]; - if( !(b_avis || b_y4m) ) // raw yuv + if( select_output( output_filename, stdout_format ) ) + return -1; + if( output.open_file( output_filename, &opt->hout ) ) { - if( optind > argc - 1 ) - { - /* try to parse the file name */ - for( psz = psz_filename; *psz; psz++ ) - { - if( *psz >= '0' && *psz <= '9' - && sscanf( psz, "%ux%u", ¶m->i_width, ¶m->i_height ) == 2 ) - { - if( param->i_log_level >= X264_LOG_INFO ) - fprintf( stderr, "x264 [info]: %dx%d (given by file name) @ %.2f fps\n", param->i_width, param->i_height, (double)param->i_fps_num / (double)param->i_fps_den); - break; - } - } - } - else - { - sscanf( argv[optind++], "%ux%u", ¶m->i_width, ¶m->i_height ); - if( param->i_log_level >= X264_LOG_INFO ) - fprintf( stderr, "x264 [info]: %dx%d @ %.2f fps\n", param->i_width, param->i_height, (double)param->i_fps_num / (double)param->i_fps_den); - } + fprintf( stderr, "x264 [error]: could not open output file `%s'\n", output_filename ); + return -1; } - if( !(b_avis || b_y4m) && ( !param->i_width || !param->i_height ) ) - { - fprintf( stderr, "x264 [error]: Rawyuv input requires a resolution.\n" ); + if( select_input( input_filename, optind < argc ? argv[optind++] : NULL, stdin_format, param ) ) return -1; - } - /* open the input */ { int i_fps_num = param->i_fps_num; int i_fps_den = param->i_fps_den; - if( b_avis ) - { -#ifdef AVIS_INPUT - input = avis_input; -#else - fprintf( stderr, "x264 [error]: not compiled with AVIS input support\n" ); - return -1; -#endif - } - if( b_y4m ) - input = y4m_input; - if( input.open_file( psz_filename, &opt->hin, param ) ) + if( input.open_file( input_filename, &opt->hin, param ) ) { - fprintf( stderr, "x264 [error]: could not open input file '%s'\n", psz_filename ); + fprintf( stderr, "x264 [error]: could not open input file `%s'\n", input_filename ); return -1; } /* Restore the user's frame rate if fps has been explicitly set on the commandline. */ @@ -1044,7 +1094,7 @@ static void parse_qpfile( cli_opt_t *opt, x264_picture_t *pic, int i_frame ) { pic->i_type = X264_TYPE_AUTO; pic->i_qpplus1 = 0; - fseek( opt->qpfile , file_pos , SEEK_SET ); + fseek( opt->qpfile, file_pos, SEEK_SET ); break; } if( num < i_frame && ret == 3 ) @@ -1133,7 +1183,7 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) opt->b_progress &= param->i_log_level < X264_LOG_DEBUG; i_frame_total = input.get_frame_total( opt->hin ); - i_frame_total -= opt->i_seek; + i_frame_total = X264_MAX( i_frame_total - opt->i_seek, 0 ); if( ( i_frame_total == 0 || param->i_frame_total < i_frame_total ) && param->i_frame_total > 0 ) i_frame_total = param->i_frame_total; -- 2.39.2