diff --git a/Makefile b/Makefile index 3de0b61..966a867 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,14 @@ ifneq ($(findstring HAVE_PTHREAD, $(CFLAGS)),) SRCCLI += input/thread.c endif +ifneq ($(findstring FFMS_INPUT, $(MUXERS)),) +SRCCLI += input/ffms.c +endif + +ifneq ($(findstring LAVF_INPUT, $(MUXERS)),) +SRCCLI += input/lavf.c +endif + ifneq ($(findstring MP4_OUTPUT, $(MUXERS)),) SRCCLI += output/mp4.c endif diff --git a/configure b/configure index d26ace1..32d91f8 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-ffms-input disables ffmpegsource input" +echo " --disable-lavf-input disables libavformat 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" +ffms_input="auto" +lavf_input="auto" mp4_output="auto" pthread="auto" asm="yes" @@ -101,6 +105,18 @@ for opt do --disable-avis-input) avis_input="no" ;; + --enable-ffms-input) + ffms_input="yes" + ;; + --disable-ffms-input) + ffms_input="no" + ;; + --enable-lavf-input) + lavf_input="yes" + ;; + --disable-lavf-input) + lavf_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 && cc_check libswscale/swscale.h -lswscale && lavf_input="yes" +fi +if [ "$lavf_input" = "yes" ] ; then + echo "#define LAVF_INPUT" >> config.h + LDFLAGS="$LDFLAGS -lavformat -lavcodec -lavutil -lswscale" +fi + +if [ "$ffms_input" = "auto" ] ; then + ffms_input="no" + [ "$lavf_input" = "yes" ] && cc_check ffms.h -lFFMS2 && 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 "ffms input: $ffms_input" +echo "lavf input: $lavf_input" echo "mp4 output: $mp4_output" echo "pthread: $pthread" echo "debug: $debug" diff --git a/input/ffms.c b/input/ffms.c new file mode 100644 index 0000000..f5f9587 --- /dev/null +++ b/input/ffms.c @@ -0,0 +1,128 @@ +/***************************************************************************** + * ffms.c: x264 ffmpegsource input module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * Dummy copyright name ;) Will fix later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#define SetConsoleTitle(t) +#endif + +typedef struct +{ + FFMS_VideoSource *video_source; + int total_frames; + struct SwsContext *ctx; +} ffms_hnd_t; + +static int FFMS_CC update_progress( int64_t current, int64_t total, void *private ) +{ + if( current % 10 != 0 ) return 0; + char buf[200]; + sprintf( buf, "x264 indexing input file [%.1f%%]", 100.0 * current / total ); + fprintf( stderr, "%s \r", buf+5 ); + SetConsoleTitle( buf ); + fflush( stderr ); + return 0; +} + +static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + ffms_hnd_t *h = malloc( sizeof(ffms_hnd_t) ); + if( !h ) + return -1; + FFMS_Init( 0 ); + FFMS_ErrorInfo e; + e.BufferSize = 0; + + FFMS_Index *index = NULL; + if( ffms_input.psz_index ) + { + struct stat index_s, input_s; + if( stat( ffms_input.psz_index, &index_s ) == 0 ) + { + stat( psz_filename, &input_s ); + if( input_s.st_mtime < index_s.st_mtime ) + index = FFMS_ReadIndex( ffms_input.psz_index, &e ); + } + } + if( !index ) + { + index = FFMS_MakeIndex( psz_filename, 0, 0, NULL, NULL, 0, update_progress, NULL, &e ); + if( ffms_input.psz_index ) + FFMS_WriteIndex( ffms_input.psz_index, index, &e ); + } + x264_free( ffms_input.psz_index ); + + 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 ); + const FFMS_VideoProperties *videop = FFMS_GetVideoProperties( h->video_source ); + h->total_frames = videop->NumFrames; + + const FFMS_Frame *frame = FFMS_GetFrame( h->video_source, 0, &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; + h->ctx = sws_getContext( frame->EncodedWidth, frame->EncodedHeight, frame->EncodedPixelFormat, frame->EncodedWidth, frame->EncodedHeight, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL ); + *p_handle = h; + return 0; +} + +static int get_frame_total( hnd_t handle ) +{ + return ((ffms_hnd_t *)handle)->total_frames; +} + +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + ffms_hnd_t *h = handle; + FFMS_ErrorInfo e; + e.BufferSize = 0; + const FFMS_Frame *frame = FFMS_GetFrame( h->video_source, i_frame, &e ); + // FFMS_VideoSource has a single FFMS_Frame buffer for all calls to GetFrame. + // With threaded input, copying the pointers would result in the data changing during encoding. + // Further, i_stride is not guaranteed to equal the Linesize of the frame returned by FFMS. + // sws_scale will copy the data correctly for differently strided memory. + sws_scale( h->ctx, frame->Data, frame->Linesize, 0, frame->EncodedHeight, p_pic->img.plane, p_pic->img.i_stride ); + return 0; +} + +static int close_file( hnd_t handle ) +{ + ffms_hnd_t *h = handle; + sws_freeContext( h->ctx ); + FFMS_DestroyVideoSource( h->video_source ); + free( h ); + return -1; // Disable x264_picture_clean +} + +cli_input_t ffms_input = { open_file, get_frame_total, read_frame, close_file, NULL }; diff --git a/input/input.h b/input/input.h index 87d5386..eac9eec 100644 --- a/input/input.h +++ b/input/input.h @@ -30,11 +30,14 @@ typedef struct int (*get_frame_total)( hnd_t handle ); int (*read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); int (*close_file)( hnd_t handle ); + char *psz_index; // Incorporate into open_file arguments? } cli_input_t; extern cli_input_t yuv_input; extern cli_input_t y4m_input; extern cli_input_t avis_input; extern cli_input_t thread_input; +extern cli_input_t ffms_input; +extern cli_input_t lavf_input; #endif diff --git a/input/lavf.c b/input/lavf.c new file mode 100644 index 0000000..edd3cab --- /dev/null +++ b/input/lavf.c @@ -0,0 +1,100 @@ +/***************************************************************************** + * lavf.c: x264 libavformat input module + ***************************************************************************** + * Copyright (C) 2003-2009 x264 project + * + * Authors: Laurent Aimar + * Loren Merritt + * Dummy copyright name ;) Will fix later + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. + *****************************************************************************/ + +#include "muxers.h" +#include +#include + +typedef struct +{ + AVFormatContext *context; + int stream_id, next_frame; + struct SwsContext *ctx; +} lavf_hnd_t; + +static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ) +{ + lavf_hnd_t *h = malloc( sizeof(lavf_hnd_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; + h->ctx = sws_getContext( c->width, c->height, c->pix_fmt, c->width, c->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL ); + if( avcodec_open( c, avcodec_find_decoder( c->codec_id ) ) != 0 ) + return -1; + return 0; +} + +static int get_frame_total( hnd_t handle ) +{ + // Transformed to opt->i_seek in x264.c. Allows for seeking. + return -2; +} + +static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame ) +{ + lavf_hnd_t *h = handle; + AVFrame *frame = avcodec_alloc_frame(); + AVPacket pkt; + av_init_packet( &pkt ); + while( i_frame >= h->next_frame ) + { + int finished = 0; + 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 ); + } + if( !finished ) + return -1; + h->next_frame ++; + } + AVCodecContext *c = h->context->streams[h->stream_id]->codec; + // i_stride is not guaranteed to equal the linesize of the frame returned by libavcodec. + // sws_scale will copy the data correctly for differently strided memory. + sws_scale( h->ctx, frame->data, frame->linesize, 0, c->height, p_pic->img.plane, p_pic->img.i_stride ); + av_freep( &frame ); + return 0; +} + +static int close_file( hnd_t handle ) +{ + lavf_hnd_t *h = handle; + sws_freeContext( h->ctx ); + av_freep( &h->context ); + free( handle ); + return -1; // Disable x264_picture_clean +} + +cli_input_t lavf_input = { open_file, get_frame_total, read_frame, close_file }; diff --git a/input/thread.c b/input/thread.c index a9330dc..54f9631 100644 --- a/input/thread.c +++ b/input/thread.c @@ -116,8 +116,8 @@ static int close_file( hnd_t handle ) thread_hnd_t *h = handle; if( h->in_progress ) x264_pthread_join( h->tid, NULL ); - h->input.close_file( h->p_handle ); - x264_picture_clean( &h->pic ); + if( h->input.close_file( h->p_handle ) == 0 ) + x264_picture_clean( &h->pic ); free( h->next_args ); free( h ); return 0; diff --git a/x264.c b/x264.c index c1728f1..72dd4e4 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; @@ -56,6 +60,7 @@ static void SigIntHandler( int a ) typedef struct { int b_progress; int i_seek; + int b_lavf; hnd_t hin; hnd_t hout; FILE *qpfile; @@ -128,6 +133,7 @@ static void Help( x264_param_t *defaults, int longhelp ) "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" + " or any libavformat supported file if compiled with libavformat support (%s).\n" "Outfile type is selected by filename:\n" " .264 -> Raw bytestream\n" " .mkv -> Matroska\n" @@ -146,6 +152,11 @@ static void Help( x264_param_t *defaults, int longhelp ) "no", #endif #ifdef MP4_OUTPUT + "yes", +#else + "no", +#endif +#ifdef FFMS_INPUT "yes" #else "no" @@ -377,6 +388,8 @@ static void Help( x264_param_t *defaults, int longhelp ) H2( " --dump-yuv Save reconstructed frames\n" ); H2( " --sps-id Set SPS and PPS id numbers [%d]\n", defaults->i_sps_id ); H2( " --aud Use access unit delimiters\n" ); + H1( " --lavf-input Use libavformat to open the input file\n" ); + H2( " --ffms-index Filename for FFmpegSource index file\n" ); H0( "\n" ); } @@ -394,6 +407,8 @@ 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 +#define OPT_INDEX 271 static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw"; static struct option long_options[] = @@ -517,6 +532,8 @@ 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 }, + { "index", required_argument, NULL, OPT_INDEX }, {0, 0, 0, 0} }; @@ -530,8 +547,11 @@ static int Parse( int argc, char **argv, x264_param_t defaults = *param; char *psz; char *profile = NULL; + char *psz_index = NULL; int b_avis = 0; + int b_y4m = 0; + int b_thread_input = 0; int b_turbo = 1; int b_pass1 = 0; @@ -830,6 +850,12 @@ static int Parse( int argc, char **argv, case OPT_SLOWFIRSTPASS: b_turbo = 0; break; + case OPT_LAVF_INPUT: + opt->b_lavf = 1; + break; + case OPT_INDEX: + psz_index = strdup(optarg); + break; case 'r': b_user_ref = 1; goto generic_option; @@ -935,8 +961,9 @@ generic_option: b_avis = 1; if( !strncasecmp( psz, ".y4m", 4 ) ) b_y4m = 1; + opt->b_lavf |= !strncasecmp( psz, ".mkv", 4 ) || !strncasecmp( psz, ".mp4", 4 ) || !strncasecmp( psz, ".h264", 4 ); - if( !(b_avis || b_y4m) ) // raw yuv + if( !(b_avis || b_y4m || opt->b_lavf) ) // raw yuv { if( optind > argc - 1 ) { @@ -960,7 +987,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; @@ -981,6 +1008,30 @@ generic_option: } if( b_y4m ) input = y4m_input; + if( opt->b_lavf ) + { +#ifdef LAVF_INPUT + int b_use_pipe = 1; + if( strcmp( psz_filename, "-") ) + { + struct stat pipe_s; + stat( psz_filename, &pipe_s ); + b_use_pipe = !S_ISREG( pipe_s.st_mode ); + } + else + psz_filename = "pipe:"; + input = lavf_input; +#ifdef FFMS_INPUT + if( !b_use_pipe ) { + ffms_input.psz_index = psz_index; + input = ffms_input; + } +#endif +#else + fprintf( stderr, "x264 [error]: not compiled with libavformat input support\n" ); + return -1; +#endif + } if( input.open_file( psz_filename, &opt->hin, param ) ) { @@ -1134,6 +1185,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 = input.get_frame_total( opt->hin ); + if( i_frame_total == -2 ) + 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 ) @@ -1209,7 +1262,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" );