From a84899e0b4fd16d49cb7085a275cd7bb1ce9f67c Mon Sep 17 00:00:00 2001 From: Loren Merritt Date: Sun, 26 Mar 2006 20:40:20 +0000 Subject: [PATCH] cli: support yuv4mpeg input. patch by anonymous. git-svn-id: svn://svn.videolan.org/x264/trunk@484 df754926-b1dd-0310-bc7b-ec298dee348c --- common/common.c | 21 +++++ common/common.h | 2 + encoder/encoder.c | 12 +-- muxers.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++ muxers.h | 5 ++ x264.c | 15 +++- 6 files changed, 243 insertions(+), 12 deletions(-) diff --git a/common/common.c b/common/common.c index 5f560d78..e4d5ef69 100644 --- a/common/common.c +++ b/common/common.c @@ -395,6 +395,27 @@ void *x264_realloc( void *p, int i_size ) #endif } +/**************************************************************************** + * x264_reduce_fraction: + ****************************************************************************/ +void x264_reduce_fraction( int *n, int *d ) +{ + int a = *n; + int b = *d; + int c; + if( !a || !b ) + return; + c = a % b; + while(c) + { + a = b; + b = c; + c = a % b; + } + *n /= b; + *d /= b; +} + /**************************************************************************** * x264_slurp_file: ****************************************************************************/ diff --git a/common/common.h b/common/common.h index 3465cb65..b9fbae44 100644 --- a/common/common.h +++ b/common/common.h @@ -120,6 +120,8 @@ char *x264_param2string( x264_param_t *p, int b_res ); /* log */ void x264_log( x264_t *h, int i_level, const char *psz_fmt, ... ); +void x264_reduce_fraction( int *n, int *d ); + static inline int x264_clip3( int v, int i_min, int i_max ) { return ( (v < i_min) ? i_min : (v > i_max) ? i_max : v ); diff --git a/encoder/encoder.c b/encoder/encoder.c index bdfd74b9..88f1bf0f 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -489,18 +489,9 @@ x264_t *x264_encoder_open ( x264_param_t *param ) { int i_w = param->vui.i_sar_width; int i_h = param->vui.i_sar_height; - int a = i_w, b = i_h; - while( b != 0 ) - { - int t = a; - - a = b; - b = t % b; - } + x264_reduce_fraction( &i_w, &i_h ); - i_w /= a; - i_h /= a; while( i_w > 65535 || i_h > 65535 ) { i_w /= 2; @@ -525,6 +516,7 @@ x264_t *x264_encoder_open ( x264_param_t *param ) } } + x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); /* Init x264_t */ h->out.i_nal = 0; diff --git a/muxers.c b/muxers.c index f8526974..97a513bb 100644 --- a/muxers.c +++ b/muxers.c @@ -108,6 +108,206 @@ int close_file_yuv(hnd_t handle) return fclose(h->fh); } +/* YUV4MPEG2 raw 420 yuv file operation */ +typedef struct { + FILE *fh; + int width, height; + int next_frame; + int seq_header_len, frame_header_len; + int frame_size; +} y4m_input_t; + +#define Y4M_MAGIC "YUV4MPEG2" +#define MAX_YUV4_HEADER 80 +#define Y4M_FRAME_MAGIC "FRAME" +#define MAX_FRAME_HEADER 80 + +int open_file_y4m( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + int i, n, d; + int interlaced; + char header[MAX_YUV4_HEADER+10]; + char *tokstart, *tokend, *header_end; + y4m_input_t *h = malloc(sizeof(y4m_input_t)); + + h->next_frame = 0; + + if( !strcmp(psz_filename, "-") ) + h->fh = stdin; + else + h->fh = fopen(psz_filename, "rb"); + if( h->fh == NULL ) + return -1; + + h->frame_header_len = strlen(Y4M_FRAME_MAGIC)+1; + + /* Read header */ + for( i=0; ifh); + if( header[i] == '\n' ) + { + /* Add a space after last option. Makes parsing "444" vs + "444alpha" easier. */ + header[i+1] = 0x20; + header[i+2] = 0; + break; + } + } + if( i == MAX_YUV4_HEADER || strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC)) ) + return -1; + + /* Scan properties */ + header_end = &header[i+1]; /* Include space */ + h->seq_header_len = i+1; + for( tokstart = &header[strlen(Y4M_MAGIC)+1]; tokstart < header_end; tokstart++ ) + { + if(*tokstart==0x20) continue; + switch(*tokstart++) + { + case 'W': /* Width. Required. */ + h->width = p_param->i_width = strtol(tokstart, &tokend, 10); + tokstart=tokend; + break; + case 'H': /* Height. Required. */ + h->height = p_param->i_height = strtol(tokstart, &tokend, 10); + tokstart=tokend; + break; + case 'C': /* Color space */ + if( strncmp("420", tokstart, 3) ) + { + fprintf(stderr, "Colorspace unhandled\n"); + return -1; + } + tokstart = strchr(tokstart, 0x20); + break; + case 'I': /* Interlace type */ + switch(*tokstart++) + { + case 'p': interlaced = 0; break; + case '?': + case 't': + case 'b': + case 'm': + default: interlaced = 1; + fprintf(stderr, "Warning, this sequence might be interlaced\n"); + } + break; + case 'F': /* Frame rate - 0:0 if unknown */ + if( sscanf(tokstart, "%d:%d", &n, &d) == 2 && n && d ) + { + x264_reduce_fraction( &n, &d ); + p_param->i_fps_num = n; + p_param->i_fps_den = d; + } + tokstart = strchr(tokstart, 0x20); + break; + case 'A': /* Pixel aspect - 0:0 if unknown */ + if( sscanf(tokstart, "%d:%d", &n, &d) == 2 && n && d ) + { + x264_reduce_fraction( &n, &d ); + p_param->vui.i_sar_width = n; + p_param->vui.i_sar_height = d; + } + tokstart = strchr(tokstart, 0x20); + break; + case 'X': /* Vendor extensions */ + if( !strncmp("YSCSS=",tokstart,6) ) + { + /* Older nonstandard pixel format representation */ + tokstart += 6; + if( strncmp("420JPEG",tokstart,7) && + strncmp("420MPEG2",tokstart,8) && + strncmp("420PALDV",tokstart,8) ) + { + fprintf(stderr, "Unsupported extended colorspace\n"); + return -1; + } + } + tokstart = strchr(tokstart, 0x20); + break; + } + } + + fprintf(stderr, "yuv4mpeg: %ix%i@%i/%ifps, %i:%i\n", + h->width, h->height, p_param->i_fps_num, p_param->i_fps_den, + p_param->vui.i_sar_width, p_param->vui.i_sar_height); + + *p_handle = (hnd_t)h; + return 0; +} + +/* Most common case: frame_header = "FRAME" */ +int get_frame_total_y4m( hnd_t handle ) +{ + y4m_input_t *h = handle; + int i_frame_total = 0; + off_t init_pos = ftell(h->fh); + + if( !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) / + (3*(h->width*h->height)/2+h->frame_header_len)); + } + + return i_frame_total; +} + +int read_frame_y4m( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + int slen = strlen(Y4M_FRAME_MAGIC); + int i = 0; + char header[16]; + y4m_input_t *h = handle; + + 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; + + header[slen] = 0; + if (strncmp(header, Y4M_FRAME_MAGIC, slen)) + { + fprintf(stderr, "Bad header magic (%08X <=> %s)\n", + *((uint32_t*)header), header); + return -1; + } + + /* Skip most of it */ + while (ifh) != '\n') + i++; + if (i == MAX_FRAME_HEADER) + { + fprintf(stderr, "Bad frame header!\n"); + return -1; + } + 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) + return -1; + + h->next_frame = i_frame+1; + + return 0; +} + +int close_file_y4m(hnd_t handle) +{ + y4m_input_t *h = handle; + if( !h || !h->fh ) + return 0; + return fclose(h->fh); +} /* avs/avi input file support under cygwin */ diff --git a/muxers.h b/muxers.h index e6d3d2ea..7ce84afb 100644 --- a/muxers.h +++ b/muxers.h @@ -8,6 +8,11 @@ int get_frame_total_yuv( hnd_t handle ); int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame ); int close_file_yuv( hnd_t handle ); +int open_file_y4m( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); +int get_frame_total_y4m( hnd_t handle ); +int read_frame_y4m( x264_picture_t *p_pic, hnd_t handle, int i_frame ); +int close_file_y4m( hnd_t handle ); + #ifdef AVIS_INPUT int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); int get_frame_total_avis( hnd_t handle ); diff --git a/x264.c b/x264.c index 2ffcdfcb..6f9a4858 100644 --- a/x264.c +++ b/x264.c @@ -133,6 +133,7 @@ static void Help( x264_param_t *defaults ) "Syntax: x264 [options] -o outfile infile [widthxheight]\n" "\n" "Infile can be raw YUV 4:2:0 (in which case resolution is required),\n" + " or YUV4MPEG 4:2:0 (*.y4m),\n" " or AVI or Avisynth if compiled with AVIS support (%s).\n" "Outfile type is selected by filename:\n" " .264 -> Raw bytestream\n" @@ -360,6 +361,7 @@ static int Parse( int argc, char **argv, x264_param_t defaults = *param; char *psz; int b_avis = 0; + int b_y4m = 0; int b_thread_input = 0; memset( opt, 0, sizeof(cli_opt_t) ); @@ -947,8 +949,9 @@ static int Parse( int argc, char **argv, if( !strncasecmp( psz, ".avi", 4 ) || !strncasecmp( psz, ".avs", 4 ) ) b_avis = 1; - - if( !b_avis && ( !param->i_width || !param->i_height ) ) + if( !strncasecmp( psz, ".y4m", 4 ) ) + b_y4m = 1; + if( !(b_avis || b_y4m) && ( !param->i_width || !param->i_height ) ) { Help( &defaults ); return -1; @@ -969,6 +972,14 @@ static int Parse( int argc, char **argv, return -1; #endif } + if ( b_y4m ) + { + p_open_infile = open_file_y4m; + p_get_frame_total = get_frame_total_y4m; + p_read_frame = read_frame_y4m; + p_close_infile = close_file_y4m; + } + if( p_open_infile( psz_filename, &opt->hin, param ) ) { fprintf( stderr, "could not open input file '%s'\n", psz_filename ); -- 2.49.0