diff --git a/configure b/configure index d26ace1..f451f33 100755 --- a/configure +++ b/configure @@ -8,6 +8,7 @@ echo "available options:" echo "" echo " --help print this message" echo " --disable-avis-input disables avisynth input (win32 only)" +echo " --disable-lavc-input disables libavcodec input" echo " --disable-mp4-output disables mp4 output (using gpac)" echo " --disable-pthread disables multithreaded encoding" echo " --disable-asm disables assembly optimizations on x86 and arm" @@ -52,6 +53,7 @@ includedir='${prefix}/include' DEVNULL='/dev/null' avis_input="auto" +lavc_input="auto" mp4_output="auto" pthread="auto" asm="yes" @@ -101,6 +103,12 @@ for opt do --disable-avis-input) avis_input="no" ;; + --enable-lavc-input) + lavc_input="yes" + ;; + --disable-lavc-input) + lavc_input="no" + ;; --enable-mp4-output) mp4_output="yes" ;; @@ -423,6 +431,14 @@ if [ "$avis_input" = "yes" ] ; then fi fi +if [ "$lavc_input" = "auto" ] ; then + cc_check libavcodec/avcodec.h "-lavcodec" && cc_check libavformat/avformat.h "-lavformat" && lavc_input="yes" || lavc_input="no" +fi +if [ "$lavc_input" = "yes" ] ; then + echo "#define LAVC_INPUT" >> config.h + LDFLAGS="$LDFLAGS -lavcodec -lavformat -lavutil" +fi + if [ "$pic" = "yes" ] ; then CFLAGS="$CFLAGS -fPIC" ASFLAGS="$ASFLAGS -DPIC" @@ -524,6 +540,7 @@ echo "Platform: $ARCH" echo "System: $SYS" echo "asm: $asm" echo "avis input: $avis_input" +echo "lavc input: $lavc_input" echo "mp4 output: $mp4_output" echo "pthread: $pthread" echo "debug: $debug" diff --git a/muxers.c b/muxers.c index c9b8dfa..a5cd8f8 100644 --- a/muxers.c +++ b/muxers.c @@ -38,6 +38,12 @@ #include #endif +#ifdef LAVC_INPUT +#include +#include +#include +#endif + static int64_t gcd( int64_t a, int64_t b ) { while( 1 ) @@ -423,6 +429,159 @@ int close_file_avis( hnd_t handle ) } #endif +/* libavcodec file input */ + +#ifdef LAVC_INPUT +typedef struct +{ + AVFormatContext *context; + const char *file; + int stream_id, next_frame, frame_count; + int64_t pts; +} lavc_input_t; + +int decode_frame_lavc( lavc_input_t *h, AVFrame *frame ) +{ + int finished = 0; + AVPacket pkt; + av_init_packet( &pkt ); + while( !finished && av_read_frame( h->context, &pkt ) >= 0 ) + { + if( pkt.stream_index == h->stream_id ) + avcodec_decode_video2( h->context->streams[h->stream_id]->codec, frame, &finished, &pkt ); + h->pts = pkt.pts; + av_free_packet( &pkt ); + } + return finished; +} + +int open_file_lavc( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + int is_pipe = 0; + struct stat pipe_s; + lavc_input_t *h = malloc( sizeof(lavc_input_t) ); + av_register_all(); + h->context = avformat_alloc_context(); + h->next_frame = 0; + h->file = psz_filename; + if( strcmp( h->file, "-" ) ) + { + if( stat(h->file, &pipe_s) != 0 ) + return -1; + if( pipe_s.st_mode & S_IFIFO ) + is_pipe = 1; + } + else + { + h->file = "pipe:"; + is_pipe = 1; + } + if( is_pipe && !p_param->b_user_fps ) + { + fprintf( stderr, "x264 [error]: Input file is not seekable but requires seeking to determine fps.\n" ); + fprintf( stderr, "x264 [error]: Specify an fps.\n" ); + return -1; + } + + if( av_open_input_file( &h->context, h->file, NULL, 0, NULL ) != 0 || av_find_stream_info( h->context ) < 0 ) + return -1; + int i = 0; + while( i < h->context->nb_streams && h->context->streams[i]->codec->codec_type != CODEC_TYPE_VIDEO ) + i++; + if( i == h->context->nb_streams ) + return -1; + h->stream_id = i; + AVCodecContext *c = h->context->streams[i]->codec; + if( avcodec_open( c, avcodec_find_decoder( c->codec_id ) ) != 0 ) + return -1; + p_param->i_width = c->width; + p_param->i_height = c->height; + h->frame_count = -1; + *p_handle = (hnd_t)h; + + if( !p_param->b_user_fps ) + { + char index_fn[ strlen( psz_filename ) + 7 ]; + sprintf( index_fn, ".%s.index", psz_filename ); + FILE *f = fopen( index_fn, "r" ); + + if( f ) + { + struct stat index_s, input_s; + stat( index_fn, &index_s ); + stat( psz_filename, &input_s ); + float fps; + if( difftime( index_s.st_mtime, input_s.st_mtime ) > 0 && fscanf( f, "%d %f\n", &h->frame_count, &fps) > 0 ) + { + p_param->i_fps_num = (int)(fps + .5); + p_param->i_fps_den = 1000; + fclose( f ); + return 0; + } + fclose( f ); + } + + AVFrame *frame = avcodec_alloc_frame(); + i = 0; + char buf[200]; + int64_t start = x264_mdate(); + while( decode_frame_lavc( h, frame ) ) + { + if( i ++ % 10 == 0 ) + { + sprintf( buf, "Analyzing: %d frames: %.2f fps", i, i * 1000000. / (x264_mdate() - start) ); + fprintf( stderr, "%s \r", buf ); + fflush( stderr ); // needed in windows + } + } + av_free( frame ); + if( av_seek_frame( h->context, h->stream_id, 0, 0 ) < 0 ) + return -1; + h->frame_count = i; + double fps = (i - 1.)/h->pts * AV_TIME_BASE / 100; + p_param->i_fps_num = (int)(fps * 1000 + .5); + p_param->i_fps_den = 1000; + + f = fopen( index_fn, "w" ); + fprintf( f, "%d %f\n", h->frame_count, fps * 1000 ); + fclose( f ); + } + return 0; +} + +int get_frame_total_lavc( hnd_t handle ) +{ + return ((lavc_input_t *)handle)->frame_count; +} + +int read_frame_lavc( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + lavc_input_t *h = handle; + AVFrame *frame = avcodec_alloc_frame(); + while( i_frame >= h->next_frame ) + { + if( decode_frame_lavc( h, frame ) == 0 ) + return -1; + h->next_frame ++; + } + int i; + for( i = 0; i < 3; i ++ ) + { + p_pic->img.plane[i] = frame->data[i]; + p_pic->img.i_stride[i] = frame->linesize[i]; + } + av_free( frame ); + return 0; +} + +int close_file_lavc( hnd_t handle ) +{ + lavc_input_t *h = handle; + av_free( h->context ); + free( h ); + return 0; +} +#endif #ifdef HAVE_PTHREAD typedef struct @@ -523,7 +682,10 @@ int close_file_thread( hnd_t handle ) if( h->in_progress ) x264_pthread_join( h->tid, NULL ); h->p_close_infile( h->p_handle ); - x264_picture_clean( &h->pic ); + #ifdef LAVC_INPUT + if( h->p_read_frame != read_frame_lavc ) + #endif + x264_picture_clean( &h->pic ); free( h->next_args ); free( h ); return 0; diff --git a/muxers.h b/muxers.h index aaede1c..62872bd 100644 --- a/muxers.h +++ b/muxers.h @@ -41,6 +41,11 @@ int get_frame_total_avis( hnd_t handle ); int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame ); int close_file_avis( hnd_t handle ); +int open_file_lavc( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); +int get_frame_total_lavc( hnd_t handle ); +int read_frame_lavc( x264_picture_t *p_pic, hnd_t handle, int i_frame ); +int close_file_lavc( hnd_t handle ); + int open_file_thread( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); int get_frame_total_thread( hnd_t handle ); int read_frame_thread( x264_picture_t *p_pic, hnd_t handle, int i_frame ); diff --git a/x264.c b/x264.c index a440ab7..56a1d97 100644 --- a/x264.c +++ b/x264.c @@ -399,6 +399,7 @@ static void Help( x264_param_t *defaults, int longhelp ) #define OPT_SLOWFIRSTPASS 267 #define OPT_FULLHELP 268 #define OPT_FPS 269 +#define OPT_LAVC_INPUT 270 static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw"; static struct option long_options[] = @@ -522,6 +523,7 @@ static struct option long_options[] = { "transfer", required_argument, NULL, 0 }, { "colormatrix", required_argument, NULL, 0 }, { "chromaloc", required_argument, NULL, 0 }, + { "lavc-input", no_argument, NULL, OPT_LAVC_INPUT}, {0, 0, 0, 0} }; @@ -537,6 +539,7 @@ static int Parse( int argc, char **argv, char *profile = NULL; int b_avis = 0; int b_y4m = 0; + int b_lavc = 0; int b_thread_input = 0; int b_turbo = 1; int b_pass1 = 0; @@ -863,6 +866,9 @@ static int Parse( int argc, char **argv, case OPT_FPS: b_user_fps = 1; goto generic_option; + case OPT_LAVC_INPUT: + b_lavc = 1; + break; default: generic_option: { @@ -960,7 +966,7 @@ generic_option: if( !strncasecmp( psz, ".y4m", 4 ) ) b_y4m = 1; - if( !(b_avis || b_y4m) ) // raw yuv + if( !(b_avis || b_y4m || b_lavc) ) // raw yuv { if( optind > argc - 1 ) { @@ -984,7 +990,7 @@ generic_option: } } - if( !(b_avis || b_y4m) && ( !param->i_width || !param->i_height ) ) + if( !(b_avis || b_y4m || b_lavc ) && ( !param->i_width || !param->i_height ) ) { fprintf( stderr, "x264 [error]: Rawyuv input requires a resolution.\n" ); return -1; @@ -1013,6 +1019,19 @@ generic_option: p_read_frame = read_frame_y4m; p_close_infile = close_file_y4m; } + if( b_lavc ) + { +#ifdef LAVC_INPUT + param->b_user_fps = b_user_fps; + p_open_infile = open_file_lavc; + p_get_frame_total = get_frame_total_lavc; + p_read_frame = read_frame_lavc; + p_close_infile = close_file_lavc; +#else + fprintf( stderr, "x264 [error]: not compiled with libavcodec input support\n" ); + return -1; +#endif + } if( p_open_infile( psz_filename, &opt->hin, param ) ) { @@ -1168,9 +1187,14 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) int64_t i_file; int i_frame_size; int i_update_interval; + int b_lavc; opt->b_progress &= param->i_log_level < X264_LOG_DEBUG; i_frame_total = p_get_frame_total( opt->hin ); + if( i_frame_total == -1 ) { + b_lavc = 1; + i_frame_total = opt->i_seek; + } i_frame_total -= opt->i_seek; if( ( i_frame_total == 0 || param->i_frame_total < i_frame_total ) && param->i_frame_total > 0 ) @@ -1246,7 +1270,8 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) } i_end = x264_mdate(); - x264_picture_clean( &pic ); + if( !b_lavc ) + x264_picture_clean( &pic ); /* Erase progress indicator before printing encoding stats. */ if( opt->b_progress ) fprintf( stderr, " \r" ); diff --git a/x264.h b/x264.h index 7bc2d36..d96477d 100644 --- a/x264.h +++ b/x264.h @@ -187,6 +187,7 @@ typedef struct x264_param_t int i_fps_num; int i_fps_den; + int b_user_fps; /* Bitstream parameters */ int i_frame_reference; /* Maximum number of reference frames */