diff --git a/configure b/configure index d26ace1..c4a0e6e 100755 --- a/configure +++ b/configure @@ -8,6 +8,8 @@ echo "available options:" echo "" echo " --help print this message" echo " --disable-avis-input disables avisynth input (win32 only)" +echo " --disable-lavf-input disables libavformat input" +echo " --disable-ffms-input disables ffmpegsource 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 +54,8 @@ includedir='${prefix}/include' DEVNULL='/dev/null' avis_input="auto" +lavf_input="auto" +ffms_input="auto" mp4_output="auto" pthread="auto" asm="yes" @@ -101,6 +105,18 @@ for opt do --disable-avis-input) avis_input="no" ;; + --enable-lavf-input) + lavf_input="yes" + ;; + --disable-lavf-input) + lavf_input="no" + ;; + --enable-ffms-input) + ffms_input="yes" + ;; + --disable-ffms-input) + ffms_input="no" + ;; --enable-mp4-output) mp4_output="yes" ;; @@ -423,6 +439,24 @@ if [ "$avis_input" = "yes" ] ; then fi fi +if [ "$lavf_input" = "auto" ] ; then + lavf_input="no" + cc_check "libavformat/avformat.h" "-lavformat" && lavf_input="yes" +fi +if [ "$lavf_input" = "yes" ] ; then + echo "#define LAVF_INPUT" >> config.h + LDFLAGS="$LDFLAGS -lavformat -lavcodec -lavutil" +fi + +if [ "$ffms_input" = "auto" ] ; then + ffms_input="no" + cc_check "ffms.h" "-lFFMS2" && [ "$lavf_input" = "yes" ] && ffms_input="yes" +fi +if [ "$ffms_input" = "yes" ] ; then + echo "#define FFMS_INPUT" >> config.h + LDFLAGS="$LDFLAGS -lFFMS2" +fi + if [ "$pic" = "yes" ] ; then CFLAGS="$CFLAGS -fPIC" ASFLAGS="$ASFLAGS -DPIC" @@ -524,6 +558,8 @@ echo "Platform: $ARCH" echo "System: $SYS" echo "asm: $asm" echo "avis input: $avis_input" +echo "lavf input: $lavf_input" +echo "ffms input: $ffms_input" echo "mp4 output: $mp4_output" echo "pthread: $pthread" echo "debug: $debug" diff --git a/muxers.c b/muxers.c index c9b8dfa..7fbb006 100644 --- a/muxers.c +++ b/muxers.c @@ -38,6 +38,14 @@ #include #endif +#ifdef FFMS_INPUT +#include +#endif + +#ifdef LAVF_INPUT +#include +#endif + static int64_t gcd( int64_t a, int64_t b ) { while( 1 ) @@ -328,6 +336,142 @@ int close_file_y4m(hnd_t handle) return 0; } +/* libavformat input file support */ + +#ifdef LAVF_INPUT +typedef struct +{ + AVFormatContext *context; + int stream_id, next_frame; +} lavf_input_t; + +int open_file_lavf( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + lavf_input_t *h = malloc( sizeof(lavf_input_t) ); + av_register_all(); + if( av_open_input_file( &h->context, psz_filename, 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; + h->next_frame = 0; + *p_handle = (hnd_t)h; + AVCodecContext *c = h->context->streams[i]->codec; + p_param->i_width = c->width; + p_param->i_height = c->height; + if( avcodec_open( c, avcodec_find_decoder( c->codec_id ) ) != 0 ) + return -1; + return 0; +} + +int get_frame_total_lavf( hnd_t handle ) +{ + return 0; +} + +int read_frame_lavf( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + lavf_input_t *h = handle; + AVFrame *frame = avcodec_alloc_frame(); + while( i_frame >= h->next_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 ); + av_free_packet( &pkt ); + } + if( finished == 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_lavf( hnd_t handle ) +{ + free( handle ); + return 0; +} +#endif + +/* ffmpegsource input file support */ + +#ifdef FFMS_INPUT +typedef struct +{ + FFMS_VideoSource *video_source; + int total_frames; +} ffms_input_t; + +int open_file_ffms( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + ffms_input_t *h = malloc( sizeof(ffms_input_t) ); + *p_handle = (hnd_t)h; + FFMS_Init( 0 ); + FFMS_ErrorInfo e; + FFMS_Index *index = FFMS_MakeIndex( psz_filename, 0, 0, NULL, NULL, 0, NULL, NULL, &e ); + int trackno = FFMS_GetFirstTrackOfType( index, FFMS_TYPE_VIDEO, &e ); + h->video_source = FFMS_CreateVideoSource( psz_filename, trackno, index, 1, FFMS_SEEK_NORMAL, &e ); + FFMS_DestroyIndex( index ); + FFMS_VideoProperties *videop = FFMS_GetVideoProperties( h->video_source ); + h->total_frames = videop->NumFrames; + // int64_t pixfmt = (int64_t)1 << FFMS_GetPixFmt("yuv420p"); + const FFMS_Frame *frame = FFMS_GetFrame( h->video_source, 0, &e ); + // FFMS_SetOutputFormatV( h->video_source, pixfmt, frame->EncodedWidth, frame->EncodedHeight, FFMS_RESIZER_BICUBIC, &e ); + p_param->i_width = frame->EncodedWidth; + p_param->i_height = frame->EncodedHeight; + p_param->i_fps_den = videop->FPSDenominator; + p_param->i_fps_num = videop->FPSNumerator; + p_param->vui.i_sar_height = videop->SARDen; + p_param->vui.i_sar_width = videop->SARNum; + return 0; +} + +int get_frame_total_ffms( hnd_t handle ) +{ + return ((ffms_input_t *)handle)->total_frames; +} + +int read_frame_ffms( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + ffms_input_t *h = handle; + FFMS_ErrorInfo e; + e.BufferSize = 0; + FFMS_Frame *frame = FFMS_GetFrame( h->video_source, i_frame, &e ); + FFMS_FrameInfo *z = FFMS_GetFrameInfo(FFMS_GetTrackFromVideo( h->video_source), i_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]; + } + return 0; +} + +int close_file_ffms( hnd_t handle ) +{ + ffms_input_t *h = handle; + FFMS_DestroyVideoSource( h->video_source ); + free( h ); + return 0; +} +#endif + /* avs/avi input file support under cygwin */ #ifdef AVIS_INPUT @@ -502,7 +646,7 @@ int read_frame_thread( x264_picture_t *p_pic, hnd_t handle, int i_frame ) ret |= h->p_read_frame( p_pic, h->p_handle, i_frame ); } - if( !h->frame_total || i_frame+1 < h->frame_total ) + if( 0 && (!h->frame_total || i_frame+1 < h->frame_total )) { h->next_frame = h->next_args->i_frame = i_frame+1; @@ -523,7 +667,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 FFMS_INPUT + if( h->p_read_frame != read_frame_ffms && h->p_read_frame != read_frame_lavf ) +#endif + x264_picture_clean( &h->pic ); free( h->next_args ); free( h ); return 0; @@ -1045,4 +1192,3 @@ int close_file_mkv( hnd_t handle ) return ret; } - diff --git a/muxers.h b/muxers.h index aaede1c..000e086 100644 --- a/muxers.h +++ b/muxers.h @@ -36,6 +36,16 @@ 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 ); +int open_file_lavf( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); +int get_frame_total_lavf( hnd_t handle ); +int read_frame_lavf( x264_picture_t *p_pic, hnd_t handle, int i_frame ); +int close_file_lavf( hnd_t handle ); + +int open_file_ffms( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); +int get_frame_total_ffms( hnd_t handle ); +int read_frame_ffms( x264_picture_t *p_pic, hnd_t handle, int i_frame ); +int close_file_ffms( hnd_t handle ); + int open_file_avis( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); int get_frame_total_avis( hnd_t handle ); int read_frame_avis( x264_picture_t *p_pic, hnd_t handle, int i_frame ); diff --git a/x264.c b/x264.c index a440ab7..d71c8fc 100644 --- a/x264.c +++ b/x264.c @@ -40,6 +40,10 @@ #define SetConsoleTitle(t) #endif +#ifdef LAVF_INPUT +#include +#endif + uint8_t *mux_buffer = NULL; int mux_buffer_size = 0; @@ -59,6 +63,7 @@ typedef struct { hnd_t hin; hnd_t hout; FILE *qpfile; + int b_lavf; } cli_opt_t; /* input file operation function pointers */ @@ -399,6 +404,7 @@ static void Help( x264_param_t *defaults, int longhelp ) #define OPT_SLOWFIRSTPASS 267 #define OPT_FULLHELP 268 #define OPT_FPS 269 +#define OPT_LAVF_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 +528,7 @@ static struct option long_options[] = { "transfer", required_argument, NULL, 0 }, { "colormatrix", required_argument, NULL, 0 }, { "chromaloc", required_argument, NULL, 0 }, + { "lavf-input", no_argument, NULL, OPT_LAVF_INPUT }, {0, 0, 0, 0} }; @@ -845,6 +852,9 @@ static int Parse( int argc, char **argv, fprintf( stderr, "x264 [warning]: not compiled with visualization support\n" ); #endif break; + case OPT_LAVF_INPUT: + opt->b_lavf = 1; + break; case OPT_TUNE: case OPT_PRESET: break; @@ -960,7 +970,7 @@ generic_option: if( !strncasecmp( psz, ".y4m", 4 ) ) b_y4m = 1; - if( !(b_avis || b_y4m) ) // raw yuv + if( !(b_avis || b_y4m || opt->b_lavf) ) // raw yuv { if( optind > argc - 1 ) { @@ -984,7 +994,7 @@ generic_option: } } - if( !(b_avis || b_y4m) && ( !param->i_width || !param->i_height ) ) + if( !(b_avis || b_y4m || opt->b_lavf) && ( !param->i_width || !param->i_height ) ) { fprintf( stderr, "x264 [error]: Rawyuv input requires a resolution.\n" ); return -1; @@ -1013,6 +1023,55 @@ generic_option: p_read_frame = read_frame_y4m; p_close_infile = close_file_y4m; } + if( opt->b_lavf ) + { +#ifdef LAVF_INPUT +#ifdef FFMS_INPUT + int b_use_pipe = 0; + if( strcmp( psz_filename, "-") ) + { + struct stat pipe_s; + if( stat( psz_filename, &pipe_s ) != 0 ) + return -1; + b_use_pipe = (pipe_s.st_mode & S_IFIFO) != 0; + } + else + { + b_use_pipe = 1; + psz_filename = "pipe:"; + } + if( !b_use_pipe ) + { + opt->b_lavf = 2; + p_open_infile = open_file_ffms; + p_get_frame_total = get_frame_total_ffms; + p_read_frame = read_frame_ffms; + p_close_infile = close_file_ffms; + } + else + { +#endif + if( !b_user_fps ) + { +#ifdef FFMS_INPUT + fprintf( stderr, "x264 [error]: Input file is a pipe. Specify an fps.\n" ); +#else + fprintf( stderr, "x264 [error]: Libavformat cannot determine fps. Specify an fps.\n" ); +#endif + return -1; + } + p_open_infile = open_file_lavf; + p_get_frame_total = get_frame_total_lavf; + p_read_frame = read_frame_lavf; + p_close_infile = close_file_lavf; +#ifdef FFMS_INPUT + } +#endif +#else + fprintf( stderr, "x264 [error]: not compiled with libavformat input support\n" ); + return -1; +#endif + } if( p_open_infile( psz_filename, &opt->hin, param ) ) { @@ -1171,6 +1230,8 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) opt->b_progress &= param->i_log_level < X264_LOG_DEBUG; i_frame_total = p_get_frame_total( opt->hin ); + if( opt->b_lavf == 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 +1307,8 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) } i_end = x264_mdate(); - x264_picture_clean( &pic ); + if( !opt->b_lavf ) + x264_picture_clean( &pic ); /* Erase progress indicator before printing encoding stats. */ if( opt->b_progress ) fprintf( stderr, " \r" );