Commit 30d76a5e authored by Kieran Kunhya's avatar Kieran Kunhya Committed by Fiona Glaser

LAVF/FFMS input support, native VFR timestamp handling

libx264 now takes three new API parameters.
b_vfr_input tells x264 whether or not the input is VFR, and is 1 by default.
i_timebase_num and i_timebase_den pass the timebase to x264.

x264_picture_t now returns the DTS of each frame: the calling app need not calculate it anymore.

Add libavformat and FFMS2 input support: requires libav* and ffms2 libraries respectively.
FFMS2 is _STRONGLY_ preferred over libavformat: we encourage all distributions to compile with FFMS2 support if at all possible.
FFMS2 can be found at http://code.google.com/p/ffmpegsource/.
--index, a new x264cli option, allows the user to store (or load) an FFMS2 index file for future use, to avoid re-indexing in the future.

Overhaul the muxers to pass through timestamps instead of assuming CFR.
Also overhaul muxers to correctly use b_annexb and b_repeat_headers to simplify the code.
Remove VFW input support, since it's now pretty much redundant with native AVS support and LAVF support.
Finally, overhaul a large part of the x264cli internals.

--force-cfr, a new x264cli option, allows the user to force the old method of timestamp handling.  May be useful in case of a source with broken timestamps.
Avisynth, YUV, and Y4M input are all still CFR.  LAVF or FFMS2 must be used for VFR support.

Do note that this patch does *not* add VFR ratecontrol yet.
Support for telecined input is also somewhat dubious at the moment.

Large parts of this patch by Mike Gurlitz <mike.gurlitz@gmail.com>, Steven Walters <kemuri9@gmail.com>, and Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>.
parent 8c8bfe19
......@@ -19,10 +19,6 @@ SRCCLI = x264.c input/yuv.c input/y4m.c output/raw.c \
MUXERS := $(shell grep -E "(IN|OUT)PUT" config.h)
# Optional muxer module sources
ifneq ($(findstring VFW_INPUT, $(MUXERS)),)
SRCCLI += input/vfw.c
endif
ifneq ($(findstring AVS_INPUT, $(MUXERS)),)
SRCCLI += input/avs.c
endif
......@@ -31,6 +27,14 @@ ifneq ($(findstring HAVE_PTHREAD, $(CFLAGS)),)
SRCCLI += input/thread.c
endif
ifneq ($(findstring LAVF_INPUT, $(MUXERS)),)
SRCCLI += input/lavf.c
endif
ifneq ($(findstring FFMS_INPUT, $(MUXERS)),)
SRCCLI += input/ffms.c
endif
ifneq ($(findstring MP4_OUTPUT, $(MUXERS)),)
SRCCLI += output/mp4.c
endif
......@@ -110,8 +114,8 @@ libx264.a: .depend $(OBJS) $(OBJASM)
$(SONAME): .depend $(OBJS) $(OBJASM)
$(CC) -shared -o $@ $(OBJS) $(OBJASM) $(SOFLAGS) $(LDFLAGS)
x264$(EXE): $(OBJCLI) libx264.a
$(CC) -o $@ $+ $(LDFLAGS)
x264$(EXE): $(OBJCLI) libx264.a
$(CC) -o $@ $+ $(LDFLAGS) $(LDFLAGSCLI)
checkasm: tools/checkasm.o libx264.a
$(CC) -o $@ $+ $(LDFLAGS)
......
......@@ -156,6 +156,7 @@ void x264_param_default( x264_param_t *param )
param->b_repeat_headers = 1;
param->b_annexb = 1;
param->b_aud = 0;
param->b_vfr_input = 1;
}
static int parse_enum( const char *arg, const char * const *names, int *dst )
......@@ -615,6 +616,8 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value )
p->b_repeat_headers = atobool(value);
OPT("annexb")
p->b_annexb = atobool(value);
OPT("force-cfr")
p->b_vfr_input = !atobool(value);
else
return X264_PARAM_BAD_NAME;
#undef OPT
......
......@@ -431,6 +431,8 @@ struct x264_t
int i_max_ref0;
int i_max_ref1;
int i_delay; /* Number of frames buffered for B reordering */
int i_bframe_delay;
int64_t i_bframe_delay_time;
int b_have_lowres; /* Whether 1/2 resolution luma planes are being used */
int b_have_sub8x8_esa;
} frames;
......
......@@ -223,7 +223,7 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
dst->i_type = src->i_type;
dst->i_qpplus1 = src->i_qpplus1;
dst->i_pts = src->i_pts;
dst->i_pts = dst->i_dts = src->i_pts;
dst->param = src->param;
for( i=0; i<3; i++ )
......
......@@ -35,10 +35,11 @@ typedef struct x264_frame
int i_type;
int i_qpplus1;
int64_t i_pts;
int64_t i_dts;
x264_param_t *param;
int i_frame; /* Presentation frame number */
int i_dts; /* Coded frame number */
int i_coded; /* Coded frame number */
int i_frame_num; /* 7.4.3 frame_num */
int b_kept_as_ref;
uint8_t b_fdec;
......
......@@ -8,6 +8,8 @@ echo "available options:"
echo ""
echo " --help print this message"
echo " --disable-avs-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"
......@@ -29,7 +31,7 @@ cc_check() {
rm -f conftest.c
[ -n "$1" ] && echo "#include <$1>" > conftest.c
echo "int main () { $3 return 0; }" >> conftest.c
$CC conftest.c $CFLAGS $LDFLAGS $2 -o conftest 2>$DEVNULL
$CC conftest.c $CFLAGS $LDFLAGS $LDFLAGSCLI $2 -o conftest 2>$DEVNULL
}
as_check() {
......@@ -52,6 +54,8 @@ includedir='${prefix}/include'
DEVNULL='/dev/null'
avs_input="auto"
lavf_input="auto"
ffms_input="auto"
mp4_output="auto"
pthread="auto"
asm="yes"
......@@ -63,6 +67,7 @@ shared="no"
CFLAGS="$CFLAGS -Wall -I."
LDFLAGS="$LDFLAGS"
LDFLAGSCLI="$LDFLAGSCLI"
ASFLAGS="$ASFLAGS"
HAVE_GETOPT_LONG=1
cross_prefix=""
......@@ -95,17 +100,24 @@ for opt do
--disable-asm)
asm="no"
;;
--enable-avs-input=*)
avs_input="$optarg"
if [ "$avs_input" != "auto" -a "$avs_input" != "vfw" -a "$avs_input" != "avs" ] ; then
echo "unrecognized enable-avis-input option '$avs_input'"
echo "available options are 'auto', 'avs', or 'vfw'"
avs_input="auto"
fi
--enable-avs-input)
avs_input="auto"
;;
--disable-avs-input)
avs_input="no"
;;
--enable-lavf-input)
lavf_input="auto"
;;
--disable-lavf-input)
lavf_input="no"
;;
--enable-ffms-input)
ffms_input="auto"
;;
--disable-ffms-input)
ffms_input="no"
;;
--enable-mp4-output)
mp4_output="yes"
;;
......@@ -402,6 +414,48 @@ if test "$pthread" = "yes" ; then
LDFLAGS="$LDFLAGS $libpthread"
fi
if [ "$lavf_input" = "auto" ] ; then
lavf_input="no"
if [ `pkg-config --exists libavformat libavcodec libswscale 2>$DEVNULL` ] ; then
LAVF_LDFLAGS="$LAVF_LDFLAGS $(pkg-config --libs libavformat libavcodec libswscale)"
LAVF_CDFLAGS="$LAVF_CFLAGS $(pkg-config --cflags libavformat libavcodec libswscale)"
fi
if [ -z "$LAVF_LDFLAGS" -a -z "$LAVF_CFLAGS" ]; then
LAVF_LDFLAGS="-lavformat -lswscale"
for lib in -lpostproc -lavcodec -lavutil -lm -lz -lbz2 $libpthread -lavifil32; do
cc_check "" $lib && LAVF_LDFLAGS="$LAVF_LDFLAGS $lib"
done
fi
LAVF_LDFLAGS="-L. $LAVF_LDFLAGS"
if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LDFLAGS" && \
cc_check libswscale/swscale.h "$LAVF_CFLAGS $LAVF_LDFLAGS" ; then
# avcodec_decode_video2 is currently the most recently added function that we use; it was added in r18351
if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LDFLAGS" "avcodec_decode_video2( NULL, NULL, NULL, NULL );" ; then
lavf_input="yes"
echo "#define LAVF_INPUT" >> config.h
LDFLAGSCLI="$LDFLAGSCLI $LAVF_LDFLAGS"
[ -n "$LAVF_CFLAGS" ] && CFLAGS="$CFLAGS $LAVF_CFLAGS"
else
echo "Warning: libavformat is too old, update to ffmpeg r18351+"
fi
fi
fi
if [ "$ffms_input" = "auto" ] ; then
ffms_input="no"
if [ "$lavf_input" = "yes" ] ; then
if cc_check ffms.h -lFFMS2 "FFMS_DestroyVideoSource(0);" ; then
ffms_input="yes"
echo "#define FFMS_INPUT" >> config.h
LDFLAGSCLI="$LDFLAGSCLI -lFFMS2"
elif cc_check ffms.h "-lFFMS2 $LAVF_LDFLAGS -lstdc++" "FFMS_DestroyVideoSource(0);" ; then
ffms_input="yes"
echo "#define FFMS_INPUT" >> config.h
LDFLAGSCLI="-lFFMS2 $LDFLAGSCLI -lstdc++"
fi
fi
fi
MP4_LDFLAGS="-lgpac_static"
if [ $SYS = MINGW ]; then
MP4_LDFLAGS="$MP4_LDFLAGS -lwinmm"
......@@ -412,32 +466,18 @@ if [ "$mp4_output" = "auto" ] ; then
fi
if [ "$mp4_output" = "yes" ] ; then
echo "#define MP4_OUTPUT" >> config.h
LDFLAGS="$LDFLAGS $MP4_LDFLAGS"
LDFLAGSCLI="$LDFLAGSCLI $MP4_LDFLAGS"
fi
if [ "$avs_input" = "auto" -o "$avs_input" = "avs" ] ; then
if [ "$avs_input" = "auto" ] ; then
avs_input=no
if [ $SYS = MINGW ] && cc_check avisynth_c.h ; then
avs_input="avs"
avs_input="yes"
echo "#define AVS_INPUT" >> config.h
echo "#define HAVE_AVISYNTH_C_H" >> config.h
elif [ $SYS = MINGW ] && cc_check extras/avisynth_c.h ; then
avs_input="avs"
avs_input="yes"
echo "#define AVS_INPUT" >> config.h
else
avs_input="auto"
fi
fi
if [ "$avs_input" = "auto" -o "$avs_input" = "vfw" ] ; then
if [ $SYS = MINGW ] && cc_check "stdlib.h" -lvfw32 ; then
echo "#define VFW_INPUT" >> config.h
LDFLAGS="$LDFLAGS -lvfw32"
avs_input="vfw"
elif [ $SYS = MINGW ] && cc_check "stdlib.h" -lavifil32 ; then
echo "#define VFW_INPUT" >> config.h
LDFLAGS="$LDFLAGS -lavifil32"
avs_input="vfw"
else
avs_input="no";
fi
fi
......@@ -486,6 +526,7 @@ SYS=$SYS
CC=$CC
CFLAGS=$CFLAGS
LDFLAGS=$LDFLAGS
LDFLAGSCLI=$LDFLAGSCLI
AR=$AR
RANLIB=$RANLIB
STRIP=$STRIP
......@@ -541,6 +582,8 @@ echo "Platform: $ARCH"
echo "System: $SYS"
echo "asm: $asm"
echo "avs input: $avs_input"
echo "lavf input: $lavf_input"
echo "ffms input: $ffms_input"
echo "mp4 output: $mp4_output"
echo "pthread: $pthread"
echo "debug: $debug"
......
......@@ -395,7 +395,8 @@ static int x264_validate_parameters( x264_t *h )
h->param.i_width, h->param.i_height );
return -1;
}
if( h->param.i_csp != X264_CSP_I420 && h->param.i_csp != X264_CSP_YV12 )
int i_csp = h->param.i_csp & X264_CSP_MASK;
if( i_csp != X264_CSP_I420 && i_csp != X264_CSP_YV12 )
{
x264_log( h, X264_LOG_ERROR, "invalid CSP (only I420/YV12 supported)\n" );
return -1;
......@@ -560,6 +561,12 @@ static int x264_validate_parameters( x264_t *h )
h->param.rc.i_lookahead = X264_MIN( h->param.rc.i_lookahead, X264_MAX( h->param.i_keyint_max, bufsize*fps ) );
}
if( !h->param.i_timebase_num || !h->param.i_timebase_den )
{
h->param.i_timebase_num = h->param.i_fps_den;
h->param.i_timebase_den = h->param.i_fps_num;
}
h->param.rc.f_qcompress = x264_clip3f( h->param.rc.f_qcompress, 0.0, 1.0 );
if( !h->param.rc.i_lookahead || h->param.i_keyint_max == 1 || h->param.rc.f_qcompress == 1 )
h->param.rc.b_mb_tree = 0;
......@@ -833,6 +840,7 @@ x264_t *x264_encoder_open( x264_param_t *param )
x264_set_aspect_ratio( h, param, 1 );
x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den );
x264_reduce_fraction( &h->param.i_timebase_num, &h->param.i_timebase_den );
/* Init x264_t */
h->i_frame = -1;
......@@ -866,6 +874,7 @@ x264_t *x264_encoder_open( x264_param_t *param )
h->frames.i_delay += h->param.i_threads - 1;
h->frames.i_delay = X264_MIN( h->frames.i_delay, X264_LOOKAHEAD_MAX );
h->frames.i_delay += h->param.i_sync_lookahead;
h->frames.i_bframe_delay = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 2 : 1) : 0;
h->frames.i_max_ref0 = h->param.i_frame_reference;
h->frames.i_max_ref1 = h->sps->vui.i_num_reorder_frames;
......@@ -1542,7 +1551,7 @@ static inline void x264_reference_hierarchy_reset( x264_t *h )
/* look for delay frames -- chain must only contain frames that are disposable */
for( i = 0; h->frames.current[i] && IS_DISPOSABLE( h->frames.current[i]->i_type ); i++ )
b_hasdelayframe |= h->frames.current[i]->i_dts
b_hasdelayframe |= h->frames.current[i]->i_coded
!= h->frames.current[i]->i_frame + h->sps->vui.i_num_reorder_frames;
if( h->param.i_bframe_pyramid != X264_B_PYRAMID_STRICT && !b_hasdelayframe )
......@@ -2040,6 +2049,9 @@ int x264_encoder_encode( x264_t *h,
fenc->i_frame = h->frames.i_input++;
if( h->frames.i_bframe_delay && fenc->i_frame == h->frames.i_bframe_delay )
h->frames.i_bframe_delay_time = fenc->i_pts;
if( h->frames.b_have_lowres )
{
if( h->param.analyse.i_weighted_pred == X264_WEIGHTP_FAKE || h->param.analyse.i_weighted_pred == X264_WEIGHTP_SMART )
......@@ -2308,7 +2320,9 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,
pic_out->i_type = X264_TYPE_P;
else
pic_out->i_type = X264_TYPE_B;
pic_out->i_pts = h->fenc->i_pts;
pic_out->i_dts = h->fenc->i_dts - h->frames.i_bframe_delay_time;
pic_out->img.i_plane = h->fdec->i_plane;
for(i = 0; i < 3; i++)
......
......@@ -180,12 +180,12 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
}
sps->vui.b_timing_info_present = 0;
if( param->i_fps_num > 0 && param->i_fps_den > 0)
if( param->i_timebase_num > 0 && param->i_timebase_den > 0 )
{
sps->vui.b_timing_info_present = 1;
sps->vui.i_num_units_in_tick = param->i_fps_den;
sps->vui.i_time_scale = param->i_fps_num * 2;
sps->vui.b_fixed_frame_rate = 1;
sps->vui.i_num_units_in_tick = param->i_timebase_num;
sps->vui.i_time_scale = param->i_timebase_den * 2;
sps->vui.b_fixed_frame_rate = !param->b_vfr_input;
}
sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
......
......@@ -1327,17 +1327,22 @@ void x264_slicetype_decide( x264_t *h )
/* shift sequence to coded order.
use a small temporary list to avoid shifting the entire next buffer around */
int i_dts = h->lookahead->next.list[0]->i_frame;
int i_coded = h->lookahead->next.list[0]->i_frame;
if( bframes )
{
int index[] = { brefs+1, 1 };
for( i = 0; i < bframes; i++ )
frames[ index[h->lookahead->next.list[i]->i_type == X264_TYPE_BREF]++ ] = h->lookahead->next.list[i];
{
int idx = index[h->lookahead->next.list[i]->i_type == X264_TYPE_BREF]++;
frames[idx] = h->lookahead->next.list[i];
frames[idx]->i_dts = h->lookahead->next.list[idx]->i_pts;
}
frames[0] = h->lookahead->next.list[bframes];
frames[0]->i_dts = h->lookahead->next.list[0]->i_pts;
memcpy( h->lookahead->next.list, frames, (bframes+1) * sizeof(x264_frame_t*) );
}
for( i = 0; i <= bframes; i++ )
h->lookahead->next.list[i]->i_dts = i_dts++;
h->lookahead->next.list[i]->i_coded = i_coded++;
}
int x264_rc_analyse_slice( x264_t *h )
......
......@@ -117,7 +117,7 @@ static void avs_build_filter_sequence( char *filename_ext, const char *filter[AV
filter[i++] = all_purpose[j];
}
static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
FILE *fh = fopen( psz_filename, "r" );
if( !fh )
......@@ -175,7 +175,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
int i;
for( i = 0; filter[i]; i++ )
{
fprintf( stderr, "avs [info]: Trying %s... ", filter[i] );
fprintf( stderr, "avs [info]: trying %s... ", filter[i] );
if( !h->func.avs_function_exists( h->env, filter[i] ) )
{
fprintf( stderr, "not found\n" );
......@@ -183,7 +183,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
}
if( !strncasecmp( filter[i], "FFmpegSource", 12 ) )
{
fprintf( stderr, "Indexing... " );
fprintf( stderr, "indexing... " );
fflush( stderr );
}
res = h->func.avs_invoke( h->env, filter[i], arg, NULL );
......@@ -225,13 +225,13 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
{
h->func.avs_release_clip( h->clip );
fprintf( stderr, "avs %s\n", !avs_is_yv12( vi ) ? "[warning]: converting input clip to YV12"
: "[info]: Avisynth 2.6+ detected, forcing conversion to YV12" );
: "[info]: avisynth 2.6+ detected, forcing conversion to YV12" );
const char *arg_name[2] = { NULL, "interlaced" };
AVS_Value arg_arr[2] = { res, avs_new_value_bool( p_param->b_interlaced ) };
AVS_Value arg_arr[2] = { res, avs_new_value_bool( info->interlaced ) };
AVS_Value res2 = h->func.avs_invoke( h->env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name );
if( avs_is_error( res2 ) )
{
fprintf( stderr, "avs [error]: Couldn't convert input clip to YV12\n" );
fprintf( stderr, "avs [error]: couldn't convert input clip to YV12\n" );
return -1;
}
h->clip = h->func.avs_take_clip( res2, h->env );
......@@ -240,17 +240,13 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
}
h->func.avs_release_value( res );
p_param->i_width = vi->width;
p_param->i_height = vi->height;
p_param->i_fps_num = vi->fps_numerator;
p_param->i_fps_den = vi->fps_denominator;
info->width = vi->width;
info->height = vi->height;
info->fps_num = vi->fps_numerator;
info->fps_den = vi->fps_denominator;
h->num_frames = vi->num_frames;
p_param->i_csp = X264_CSP_YV12;
fprintf( stderr, "avs [info]: %dx%d @ %.2f fps (%d frames)\n",
p_param->i_width, p_param->i_height,
(double)p_param->i_fps_num / p_param->i_fps_den,
h->num_frames );
info->csp = X264_CSP_YV12;
info->vfr = 0;
*p_handle = h;
return 0;
......
/*****************************************************************************
* ffms.c: x264 ffmpegsource input module
*****************************************************************************
* Copyright (C) 2009 x264 project
*
* Authors: Mike Gurlitz <mike.gurlitz@gmail.com>
* Steven Walters <kemuri9@gmail.com>
*
* 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 <ffms.h>
#undef DECLARE_ALIGNED
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#ifdef _WIN32
#include <windows.h>
#else
#define SetConsoleTitle(t)
#endif
typedef struct
{
FFMS_VideoSource *video_source;
FFMS_Track *track;
int total_frames;
struct SwsContext *scaler;
int pts_offset_flag;
int64_t pts_offset;
int reduce_pts;
int vfr_input;
int init_width;
int init_height;
int cur_width;
int cur_height;
int cur_pix_fmt;
} ffms_hnd_t;
static int FFMS_CC update_progress( int64_t current, int64_t total, void *private )
{
if( current % 10 )
return 0;
char buf[200];
sprintf( buf, "ffms [info]: 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, video_info_t *info, cli_input_opt_t *opt )
{
ffms_hnd_t *h = calloc( 1, sizeof(ffms_hnd_t) );
if( !h )
return -1;
FFMS_Init( 0 );
FFMS_ErrorInfo e;
e.BufferSize = 0;
int seekmode = opt->seek ? FFMS_SEEK_NORMAL : FFMS_SEEK_LINEAR_NO_RW;
FFMS_Index *index = NULL;
if( opt->index )
{
struct stat index_s, input_s;
if( !stat( opt->index, &index_s ) && !stat( psz_filename, &input_s ) &&
input_s.st_mtime < index_s.st_mtime )
index = FFMS_ReadIndex( opt->index, &e );
}
if( !index )
{
index = FFMS_MakeIndex( psz_filename, 0, 0, NULL, NULL, 0, update_progress, NULL, &e );
fprintf( stderr, " \r" );
if( !index )
{
fprintf( stderr, "ffms [error]: could not create index\n" );
return -1;
}
if( opt->index && FFMS_WriteIndex( opt->index, index, &e ) )
fprintf( stderr, "ffms [warning]: could not write index file\n" );
}
int trackno = FFMS_GetFirstTrackOfType( index, FFMS_TYPE_VIDEO, &e );
if( trackno < 0 )
{
fprintf( stderr, "ffms [error]: could not find video track\n" );
return -1;
}
h->video_source = FFMS_CreateVideoSource( psz_filename, trackno, index, 1, seekmode, &e );
if( !h->video_source )
{
fprintf( stderr, "ffms [error]: could not create video source\n" );
return -1;
}
h->track = FFMS_GetTrackFromVideo( h->video_source );
const FFMS_TrackTimeBase *timebase = FFMS_GetTimeBase( h->track );
FFMS_DestroyIndex( index );
const FFMS_VideoProperties *videop = FFMS_GetVideoProperties( h->video_source );
h->total_frames = videop->NumFrames;
info->sar_height = videop->SARDen;
info->sar_width = videop->SARNum;
info->fps_den = videop->FPSDenominator;
info->fps_num = videop->FPSNumerator;
info->timebase_num = (int)timebase->Num;
h->vfr_input = info->vfr;
const FFMS_Frame *frame = FFMS_GetFrame( h->video_source, 0, &e );
if( !frame )
{
fprintf( stderr, "ffms [error]: could not read frame 0\n" );
return -1;
}
h->init_width = h->cur_width = info->width = frame->EncodedWidth;
h->init_height = h->cur_height = info->height = frame->EncodedHeight;
h->cur_pix_fmt = frame->EncodedPixelFormat;
info->interlaced = frame->InterlacedFrame;
if( h->cur_pix_fmt != PIX_FMT_YUV420P )
fprintf( stderr, "ffms [warning]: converting from %s to YV12\n",
avcodec_get_pix_fmt_name( h->cur_pix_fmt ) );
/* ffms timestamps are in milliseconds. Increasing timebase denominator could cause integer overflow.
* Conversely, reducing PTS may lose too much accuracy */
if( h->vfr_input )
{
int64_t timebase_den = (int64_t)timebase->Den * 1000;
if( timebase_den > INT_MAX )
{
info->timebase_den = (int)timebase->Den;
h->reduce_pts = 1;
}
else
{
info->timebase_den = (int)timebase->Den * 1000;
h->reduce_pts = 0;
}
}
*p_handle = h;
return 0;
}
static int get_frame_total( hnd_t handle )
{
return ((ffms_hnd_t*)handle)->total_frames;
}
static int check_swscale( ffms_hnd_t *h, const FFMS_Frame *frame, int i_frame )
{
if( h->scaler && h->cur_width == frame->EncodedWidth && h->cur_height == frame->EncodedHeight &&
h->cur_pix_fmt == frame->EncodedPixelFormat )
return 0;
if( h->scaler )
{
sws_freeContext( h->scaler );
fprintf( stderr, "ffms [warning]: stream properties changed to %dx%d, %s at frame %d \n", frame->EncodedWidth,
frame->EncodedHeight, avcodec_get_pix_fmt_name( frame->EncodedPixelFormat ), i_frame );
h->cur_width = frame->EncodedWidth;
h->cur_height = frame->EncodedHeight;
h->cur_pix_fmt = frame->EncodedPixelFormat;
}
h->scaler = sws_getContext( h->cur_width, h->cur_height, h->cur_pix_fmt, h->init_width, h->init_height,
PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL );
if( !h->scaler )
{
fprintf( stderr, "ffms [error]: could not open swscale context\n" );
return -1;
}
return 0;
}
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 );
if( !frame )
{
fprintf( stderr, "ffms [error]: could not read frame %d\n", i_frame );
return -1;
}
if( check_swscale( h, frame, i_frame ) )
return -1;
/* 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.
* FIXME: don't do redundant sws_scales for singlethreaded input, or fix FFMS to allow
* multiple FFMS_Frame buffers. */
sws_scale( h->scaler, (uint8_t**)frame->Data, (int*)frame->Linesize, 0,
frame->EncodedHeight, p_pic->img.plane, p_pic->img.i_stride );
const FFMS_FrameInfo *info = FFMS_GetFrameInfo( h->track, i_frame );
if( h->vfr_input )
{
if( info->PTS == AV_NOPTS_VALUE )
{
fprintf( stderr, "ffms [error]: invalid timestamp. "
"Use --force-cfr and specify a framerate with --fps\n" );
return -1;
}
if( !h->pts_offset_flag )
{
h->pts_offset = info->PTS;
h->pts_offset_flag = 1;
}
if( h->reduce_pts )
p_pic->i_pts = (int64_t)(((info->PTS - h->pts_offset) / 1000) + 0.5);
else
p_pic->i_pts = info->PTS - h->pts_offset;
}
return 0;
}
static int close_file( hnd_t handle )
{
ffms_hnd_t *h = handle;
sws_freeContext( h->scaler );
FFMS_DestroyVideoSource( h->video_source );
free( h );
return 0;
}
cli_input_t ffms_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
......@@ -5,6 +5,7 @@
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Loren Merritt <lorenm@u.washington.edu>
* Steven Walters <kemuri9@gmail.com>
*
* 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
......@@ -24,9 +25,33 @@
#ifndef X264_INPUT_H
#define X264_INPUT_H
/* options that are used by only some demuxers */
typedef struct
{
int (*open_file)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param );
char *index;
char *resolution; /* resolution string parsed by raw yuv input */
int seek;
} cli_input_opt_t;
/* properties of the source given by the demuxer */
typedef struct
{
int csp; /* X264_CSP_YV12 or X264_CSP_I420 */
int fps_num;
int fps_den;
int height;
int interlaced;
int sar_width;
int sar_height;
int timebase_num;
int timebase_den;
int vfr;
int width;
} video_info_t;
typedef struct
{
int (*open_file)( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt );
int (*get_frame_total)( hnd_t handle );
int (*picture_alloc)( x264_picture_t *pic, int i_csp, int i_width, int i_height );
int (*read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame );
......@@ -39,5 +64,7 @@ extern cli_input_t yuv_input;
extern cli_input_t y4m_input;
extern cli_input_t avs_input;
extern cli_input_t thread_input;
extern cli_input_t lavf_input;
extern cli_input_t ffms_input;