Similar to mplayer's -vf system.
Supports some basic operations like resizing and cropping. Will support more in the future.
See the help for more details.
encoder/set.c encoder/macroblock.c encoder/cabac.c \
encoder/cavlc.c encoder/encoder.c encoder/lookahead.c
-SRCCLI = x264.c input/timecode.c \
- input/yuv.c input/y4m.c output/raw.c \
- output/matroska.c output/matroska_ebml.c \
- output/flv.c output/flv_bytestream.c
+SRCCLI = x264.c input/input.c input/timecode.c input/raw.c input/y4m.c \
+ output/raw.c output/matroska.c output/matroska_ebml.c \
+ output/flv.c output/flv_bytestream.c filters/filters.c \
+ filters/video/video.c filters/video/source.c filters/video/internal.c \
+ filters/video/resize.c filters/video/cache.c filters/video/fix_vfr_pts.c \
+ filters/video/select_every.c filters/video/crop.c
SRCSO =
$(CC) -shared -o $@ $(OBJS) $(OBJASM) $(OBJSO) $(SOFLAGS) $(LDFLAGS)
x264$(EXE): $(OBJCLI) libx264.a
- $(CC) -o $@ $+ $(LDFLAGS) $(LDFLAGSCLI)
+ $(CC) -o $@ $+ $(LDFLAGSCLI) $(LDFLAGS)
checkasm: tools/checkasm.o libx264.a
$(CC) -o $@ $+ $(LDFLAGS)
int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
{
int i_csp = src->img.i_csp & X264_CSP_MASK;
- if( i_csp != X264_CSP_I420 && i_csp != X264_CSP_YV12 )
+ if( i_csp <= X264_CSP_NONE || i_csp >= X264_CSP_MAX )
{
x264_log( h, X264_LOG_ERROR, "Invalid input colorspace\n" );
return -1;
echo " --disable-ffms disables ffmpegsource support"
echo " --disable-gpac disables gpac support"
echo " --disable-pthread disables multithreaded encoding"
+echo " --disable-swscale disables swscale support"
echo " --disable-asm disables platform-specific assembly optimizations"
echo " --enable-debug adds -g, doesn't strip"
echo " --enable-gprof adds -pg, doesn't strip"
rm -f conftest.c
[ -n "$1" ] && echo "#include <$1>" > conftest.c
echo "int main () { $3 return 0; }" >> conftest.c
- if $CC conftest.c $CFLAGS $LDFLAGS $LDFLAGSCLI $2 -o conftest >conftest.log 2>&1; then
+ if $CC conftest.c $CFLAGS $2 $LDFLAGSCLI $LDFLAGS -o conftest >conftest.log 2>&1; then
res=$?
log_ok
else
log_fail
log_msg "Failed commandline was:"
log_msg "--------------------------------------------------"
- log_msg "$CC conftest.c $CFLAGS $LDFLAGS $LDFLAGSCLI $2"
+ log_msg "$CC conftest.c $CFLAGS $2 $LDFLAGSCLI $LDFLAGS"
+ cat conftest.log >> config.log
+ log_msg "--------------------------------------------------"
+ fi
+ return $res
+}
+
+cpp_check() {
+ log_check "whether $3 is true"
+ rm -f conftest.c
+ [ -n "$1" ] && echo "#include <$1>" > conftest.c
+ echo -e "#if !($3) \n#error $4 \n#endif " >> conftest.c
+
+ if $CC conftest.c $CFLAGS $2 -E -o conftest >conftest.log 2>&1; then
+ res=$?
+ log_ok
+ else
+ res=$?
+ log_fail
+ log_msg "--------------------------------------------------"
cat conftest.log >> config.log
log_msg "--------------------------------------------------"
fi
ffms="auto"
gpac="auto"
pthread="auto"
+swscale="auto"
asm="auto"
debug="no"
gprof="no"
--disable-pthread)
pthread="no"
;;
+ --disable-swscale)
+ swscale="no"
+ ;;
--enable-debug)
debug="yes"
;;
vis="no"
fi
+if [ "$swscale" = "auto" ] ; then
+ swscale="no"
+ if ${cross_prefix}pkg-config --exists libswscale 2>$DEVNULL; then
+ SWSCALE_LIBS="$SWSCALE_LIBS $(${cross_prefix}pkg-config --libs libswscale)"
+ SWSCALE_CFLAGS="$SWSCALE_CFLAGS $(${cross_prefix}pkg-config --cflags libswscale)"
+ fi
+ [ -z "$SWSCALE_LIBS" ] && SWSCALE_LIBS="-lswscale -lavutil"
+
+ error="swscale must be at least version 0.9.0"
+ if cc_check "libswscale/swscale.h" "$SWSCALE_CFLAGS $SWSCALE_LIBS" "sws_getContext(0,0,0,0,0,0,0,0,0,0);" ; then
+ if cpp_check "libswscale/swscale.h" "$SWSCALE_CFLAGS" "LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0,9,0)" "$error"; then
+ define HAVE_SWSCALE
+ swscale="yes"
+ else
+ echo "Warning: ${error}"
+ fi
+ fi
+fi
+
if [ "$lavf" = "auto" ] ; then
lavf="no"
if ${cross_prefix}pkg-config --exists libavformat libavcodec libswscale 2>$DEVNULL; then
LAVF_CFLAGS="$LAVF_CFLAGS $(${cross_prefix}pkg-config --cflags libavformat libavcodec libswscale)"
fi
if [ -z "$LAVF_LIBS" -a -z "$LAVF_CFLAGS" ]; then
- LAVF_LIBS="-lavformat -lswscale"
- for lib in -lpostproc -lavcodec -lavutil -lm -lz -lbz2 $libpthread -lavifil32; do
+ LAVF_LIBS="-lavformat"
+ for lib in -lpostproc -lavcodec -lswscale -lavutil -lm -lz -lbz2 $libpthread -lavifil32; do
cc_check "" $lib && LAVF_LIBS="$LAVF_LIBS $lib"
done
fi
LAVF_LIBS="-L. $LAVF_LIBS"
- if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LIBS" && \
- cc_check libswscale/swscale.h "$LAVF_CFLAGS $LAVF_LIBS" ; 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_LIBS" "avcodec_decode_video2( NULL, NULL, NULL, NULL );" ; then
- lavf="yes"
- define HAVE_LAVF
+ if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LIBS" "avcodec_decode_video2(0,0,0,0);" ; then
+ # libvautil/pixdesc.h included the private header intreadwrite.h until r21854
+ if cc_check libavutil/pixdesc.h "$LAVF_CFLAGS $LAVF_LIBS" ; then
+ if [ "$swscale" = "yes" ]; then
+ lavf="yes"
+ define HAVE_LAVF
+ else
+ echo "Warning: libavformat is not supported without swscale support"
+ fi
else
- echo "Warning: libavformat is too old, update to ffmpeg r18351+"
+ echo "Warning: libavutil is too old, update to ffmpeg r21854+"
fi
fi
fi
if [ "$ffms" = "auto" ] ; then
ffms_major="2"; ffms_minor="13"; ffms_micro="1"; ffms_bump="0"
-
ffms="no"
- [ $ffms_micro -gt 0 -o $ffms_bump -gt 0 ] && vmicro=".$ffms_micro"
- [ $ffms_bump -gt 0 ] && vbump=".$ffms_bump"
- if ${cross_prefix}pkg-config --atleast-version="$ffms_major.$ffms_minor$vmicro$vbump" ffms2 2>$DEVNULL; then
+
+ if ${cross_prefix}pkg-config --exists ffms2 2>$DEVNULL; then
FFMS2_LIBS="$FFMS2_LIBS $(${cross_prefix}pkg-config --libs ffms2)"
FFMS2_CFLAGS="$FFMS2_CFLAGS $(${cross_prefix}pkg-config --cflags ffms2)"
- api_check="no"
- else
- api_check="yes"
fi
[ -z "$FFMS2_LIBS" ] && FFMS2_LIBS="-lffms2"
FFMS2_LIBS="$FFMS2_LIBS -lstdc++ $LAVF_LIBS"
fi
- if [ $api_check = "yes" -a $ffms = "yes" ]; then
- log_check "whether ffms2 version is at least $ffms_major.$ffms_minor$vmicro$vbump"
- $CC $CFLAGS $FFMS2_CFLAGS -c -o conftest -x c - >$DEVNULL 2>&1 <<EOF
-#include <ffms.h>
-#if FFMS_VERSION < (($ffms_major << 24) | ($ffms_minor << 16) | ($ffms_micro << 8) | $ffms_bump)
-#error Requires ffms2 version 2.13.1
-#endif
-EOF
- [ $? = 0 ] && log_ok || { ffms="no"; log_fail; }
+ error="ffms must be at least version $ffms_major.$ffms_minor.$ffms_micro.$ffms_bump"
+ if [ $ffms = "yes" ] && ! cpp_check "ffms.h" "$FFMS2_CFLAGS" "FFMS_VERSION >= (($ffms_major << 24) | ($ffms_minor << 16) | ($ffms_micro << 8) | $ffms_bump)" "$error"; then
+ ffms="no"
+ echo "Warning: $error"
+ fi
+ if [ "$ffms" = "yes" -a "$swscale" = "no" ]; then
+ echo "Warning: ffms is not supported without swscale support"
+ ffms="no"
fi
fi
if [ "$ffms" = "yes" ]; then
LDFLAGSCLI="$FFMS2_LIBS $LDFLAGSCLI"
- [ -n "$FFMS2_CFLAGS" ] && CFLAGS="$CFLAGS $FFMS2_CFLAGS"
+ CFLAGS="$CFLAGS $FFMS2_CFLAGS"
define HAVE_FFMS
elif [ "$lavf" = "yes" ]; then
LDFLAGSCLI="$LAVF_LIBS $LDFLAGSCLI"
- [ -n "$LAVF_CFLAGS" ] && CFLAGS="$CFLAGS $LAVF_CFLAGS"
+ CFLAGS="$CFLAGS $LAVF_CFLAGS"
+elif [ "$swscale" = "yes" ]; then
+ LDFLAGSCLI="$SWSCALE_LIBS $LDFLAGSCLI"
+ CFLAGS="$CFLAGS $SWSCALE_CFLAGS"
fi
GPAC_LIBS="-lgpac_static"
if cc_check gpac/isomedia.h "-Werror $GPAC_LIBS" "gf_malloc(1); gf_free(NULL);" ; then
define HAVE_GF_MALLOC
fi
- LDFLAGSCLI="$LDFLAGSCLI $GPAC_LIBS"
+ LDFLAGSCLI="$GPAC_LIBS $LDFLAGSCLI"
fi
if [ "$avs" = "auto" ] ; then
Cflags: -I$includedir
EOF
+filters="crop select_every"
+[ $swscale = yes ] && filters="resize $filters"
+
cat > conftest.log <<EOF
Platform: $ARCH
System: $SYS
ffms: $ffms
gpac: $gpac
pthread: $pthread
+filters: $filters
debug: $debug
gprof: $gprof
PIC: $pic
return -1;
}
int i_csp = h->param.i_csp & X264_CSP_MASK;
- if( i_csp != X264_CSP_I420 && i_csp != X264_CSP_YV12 )
+ if( i_csp <= X264_CSP_NONE || i_csp >= X264_CSP_MAX )
{
- x264_log( h, X264_LOG_ERROR, "invalid CSP (only I420/YV12 supported)\n" );
+ x264_log( h, X264_LOG_ERROR, "invalid CSP\n" );
return -1;
}
--- /dev/null
+/*****************************************************************************
+ * filters.c: x264 filter common
+ *****************************************************************************
+ * Copyright (C) 2010 x264 project
+ *
+ * Authors: Diogo Franco <diogomfranco@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 "filters.h"
+#define RETURN_IF_ERROR( cond, ... ) RETURN_IF_ERR( cond, "options", NULL, __VA_ARGS__ )
+
+char **x264_split_string( char *string, char *sep, uint32_t limit )
+{
+ if( !string )
+ return NULL;
+ int sep_count = 0;
+ char *tmp = string;
+ while( ( tmp = ( tmp = strstr( tmp, sep ) ) ? tmp + strlen( sep ) : 0 ) )
+ ++sep_count;
+ if( sep_count == 0 )
+ {
+ if( string[0] == '\0' )
+ return calloc( 1, sizeof( char** ) );
+ char **ret = calloc( 2, sizeof( char** ) );
+ ret[0] = strdup( string );
+ return ret;
+ }
+
+ char **split = calloc( ( limit > 0 ? limit : sep_count ) + 2, sizeof(char**) );
+ int i = 0;
+ char *str = strdup( string );
+ assert( str );
+ char *esc = NULL;
+ char *tok = str, *nexttok = str;
+ do
+ {
+ nexttok = strstr( nexttok, sep );
+ if( nexttok )
+ *nexttok++ = '\0';
+ if( ( limit > 0 && i >= limit ) ||
+ ( i > 0 && ( ( esc = strrchr( split[i-1], '\\' ) ) ? esc[1] == '\0' : 0 ) ) ) // Allow escaping
+ {
+ int j = i-1;
+ if( esc )
+ esc[0] = '\0';
+ split[j] = realloc( split[j], strlen( split[j] ) + strlen( sep ) + strlen( tok ) + 1 );
+ assert( split[j] );
+ strcat( split[j], sep );
+ strcat( split[j], tok );
+ esc = NULL;
+ }
+ else
+ assert( ( split[i++] = strdup( tok ) ) );
+ tok = nexttok;
+ } while ( tok );
+ free( str );
+ assert( !split[i] );
+
+ return split;
+}
+
+void x264_free_string_array( char **array )
+{
+ if( !array )
+ return;
+ for( int i = 0; array[i] != NULL; i++ )
+ free( array[i] );
+ free( array );
+}
+
+char **x264_split_options( const char *opt_str, const char *options[] )
+{
+ if( !opt_str )
+ return NULL;
+ char *opt_str_dup = strdup( opt_str );
+ char **split = x264_split_string( opt_str_dup, ",", 0 );
+ free( opt_str_dup );
+ int split_count = 0;
+ while( split[split_count] != NULL )
+ ++split_count;
+
+ int options_count = 0;
+ while( options[options_count] != NULL )
+ ++options_count;
+
+ char **opts = calloc( split_count * 2 + 2, sizeof( char ** ) );
+ char **arg = NULL;
+ int opt = 0, found_named = 0, invalid = 0;
+ for( int i = 0; split[i] != NULL; i++, invalid = 0 )
+ {
+ arg = x264_split_string( split[i], "=", 2 );
+ if( arg == NULL )
+ {
+ if( found_named )
+ invalid = 1;
+ else RETURN_IF_ERROR( i > options_count || options[i] == NULL, "Too many options given\n" )
+ else
+ {
+ opts[opt++] = strdup( options[i] );
+ opts[opt++] = strdup( "" );
+ }
+ }
+ else if( arg[0] == NULL || arg[1] == NULL )
+ {
+ if( found_named )
+ invalid = 1;
+ else RETURN_IF_ERROR( i > options_count || options[i] == NULL, "Too many options given\n" )
+ else
+ {
+ opts[opt++] = strdup( options[i] );
+ if( arg[0] )
+ opts[opt++] = strdup( arg[0] );
+ else
+ opts[opt++] = strdup( "" );
+ }
+ }
+ else
+ {
+ found_named = 1;
+ int j = 0;
+ while( options[j] != NULL && strcmp( arg[0], options[j] ) )
+ ++j;
+ RETURN_IF_ERROR( options[j] == NULL, "Invalid option '%s'\n", arg[0] )
+ else
+ {
+ opts[opt++] = strdup( arg[0] );
+ opts[opt++] = strdup( arg[1] );
+ }
+ }
+ RETURN_IF_ERROR( invalid, "Ordered option given after named\n" )
+ x264_free_string_array( arg );
+ }
+ x264_free_string_array( split );
+ return opts;
+}
+
+char *x264_get_option( const char *name, char **split_options )
+{
+ if( !split_options )
+ return NULL;
+ int last_i = -1;
+ for( int i = 0; split_options[i] != NULL; i += 2 )
+ if( !strcmp( split_options[i], name ) )
+ last_i = i;
+ if( last_i >= 0 )
+ return split_options[last_i+1][0] ? split_options[last_i+1] : NULL;
+ return NULL;
+}
+
+int x264_otob( char *str, int def )
+{
+ int ret = def;
+ if( str )
+ ret = !strcasecmp( str, "true" ) ||
+ !strcmp( str, "1" ) ||
+ !strcasecmp( str, "yes" );
+ return ret;
+}
+
+double x264_otof( char *str, double def )
+{
+ double ret = def;
+ if( str )
+ {
+ char *end;
+ ret = strtod( str, &end );
+ if( end == str || *end != '\0' )
+ ret = def;
+ }
+ return ret;
+}
+
+int x264_otoi( char *str, int def )
+{
+ int ret = def;
+ if( str )
+ {
+ char *end;
+ ret = strtol( str, &end, 0 );
+ if( end == str || *end != '\0' )
+ ret = def;
+ }
+ return ret;
+}
+
+char *x264_otos( char *str, char *def )
+{
+ return str ? str : def;
+}
--- /dev/null
+/*****************************************************************************
+ * filters.h: x264 filter common
+ *****************************************************************************
+ * Copyright (C) 2010 x264 project
+ *
+ * Authors: Diogo Franco <diogomfranco@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.
+ *****************************************************************************/
+
+#ifndef X264_FILTERS_H
+#define X264_FILTERS_H
+
+#include "x264cli.h"
+#include "filters/video/video.h"
+
+char **x264_split_string( char *string, char *sep, uint32_t limit );
+void x264_free_string_array( char **array );
+
+char **x264_split_options( const char *opt_str, const char *options[] );
+char *x264_get_option( const char *name, char **split_options );
+int x264_otob( char *str, int def ); // option to bool
+double x264_otof( char *str, double def ); // option to float/double
+int x264_otoi( char *str, int def ); // option to int
+char *x264_otos( char *str, char *def ); // option to string
+
+#endif
--- /dev/null
+/*****************************************************************************
+ * cache.c: x264 video cache filter
+ *****************************************************************************
+ * Copyright (C) 2010 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 "video.h"
+#include "internal.h"
+#define NAME "cache"
+#define LAST_FRAME (h->first_frame + h->cur_size - 1)
+
+typedef struct
+{
+ hnd_t prev_hnd;
+ cli_vid_filter_t prev_filter;
+
+ int max_size;
+ int first_frame; /* first cached frame */
+ cli_pic_t **cache;
+ int cur_size;
+ int eof; /* frame beyond end of the file */
+} cache_hnd_t;
+
+cli_vid_filter_t cache_filter;
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ intptr_t size = (intptr_t)opt_string;
+ /* upon a <= 0 cache request, do nothing */
+ if( size <= 0 )
+ return 0;
+ cache_hnd_t *h = calloc( 1, sizeof(cache_hnd_t) );
+ if( !h )
+ return -1;
+
+ h->max_size = size;
+ h->cache = malloc( (h->max_size+1) * sizeof(cli_pic_t*) );
+ if( !h->cache )
+ return -1;
+
+ for( int i = 0; i < h->max_size; i++ )
+ {
+ h->cache[i] = malloc( sizeof(cli_pic_t) );
+ if( !h->cache[i] || x264_cli_pic_alloc( h->cache[i], info->csp, info->width, info->height ) )
+ return -1;
+ }
+ h->cache[h->max_size] = NULL; /* require null terminator for list methods */
+
+ h->prev_filter = *filter;
+ h->prev_hnd = *handle;
+ *handle = h;
+ *filter = cache_filter;
+
+ return 0;
+}
+
+static void fill_cache( cache_hnd_t *h, int frame )
+{
+ /* shift frames out of the cache as the frame request is beyond the filled cache */
+ int shift = frame - LAST_FRAME;
+ /* no frames to shift or no frames left to read */
+ if( shift <= 0 || h->eof )
+ return;
+ /* the next frames to read are either
+ * A) starting at the end of the current cache, or
+ * B) starting at a new frame that has the end of the cache at the desired frame
+ * and proceeding to fill the entire cache */
+ int cur_frame = X264_MAX( h->first_frame + h->cur_size, frame - h->max_size + 1 );
+ /* the new starting point is either
+ * A) the current one shifted the number of frames entering/leaving the cache, or
+ * B) at a new frame that has the end of the cache at the desired frame. */
+ h->first_frame = X264_MIN( h->first_frame + shift, cur_frame );
+ h->cur_size = X264_MAX( h->cur_size - shift, 0 );
+ while( h->cur_size < h->max_size )
+ {
+ cli_pic_t temp;
+ /* the old front frame is going to shift off, overwrite it with the new frame */
+ cli_pic_t *cache = h->cache[0];
+ if( h->prev_filter.get_frame( h->prev_hnd, &temp, cur_frame ) ||
+ x264_cli_pic_copy( cache, &temp ) ||
+ h->prev_filter.release_frame( h->prev_hnd, &temp, cur_frame ) )
+ {
+ h->eof = cur_frame;
+ return;
+ }
+ /* the read was successful, shift the frame off the front to the end */
+ x264_frame_push( (void*)h->cache, x264_frame_shift( (void*)h->cache ) );
+ cur_frame++;
+ h->cur_size++;
+ }
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ cache_hnd_t *h = handle;
+ FAIL_IF_ERR( frame < h->first_frame, NAME, "frame %d is before first cached frame %d \n", frame, h->first_frame );
+ fill_cache( h, frame );
+ if( frame > LAST_FRAME ) /* eof */
+ return -1;
+ int idx = frame - (h->eof ? h->eof - h->max_size : h->first_frame);
+ *output = *h->cache[idx];
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ /* the parent filter's frame has already been released so do nothing here */
+ return 0;
+}
+
+static void free_filter( hnd_t handle )
+{
+ cache_hnd_t *h = handle;
+ h->prev_filter.free( h->prev_hnd );
+ for( int i = 0; i < h->max_size; i++ )
+ {
+ x264_cli_pic_clean( h->cache[i] );
+ free( h->cache[i] );
+ }
+ free( h->cache );
+ free( h );
+}
+
+cli_vid_filter_t cache_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };
--- /dev/null
+/*****************************************************************************
+ * crop.c: x264 crop video filter
+ *****************************************************************************
+ * Copyright (C) 2010 x264 project
+ *
+ * Authors: Steven Walters <kemuri9@gmail.com>
+ * James Darnley <james.darnley@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 "video.h"
+#define NAME "crop"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
+
+cli_vid_filter_t crop_filter;
+
+typedef struct
+{
+ hnd_t prev_hnd;
+ cli_vid_filter_t prev_filter;
+
+ int dims[4]; /* left, top, width, height */
+ const x264_cli_csp_t *csp;
+} crop_hnd_t;
+
+static void help( int longhelp )
+{
+ printf( " "NAME":left,top,right,bottom\n" );
+ if( !longhelp )
+ return;
+ printf( " removes pixels from the edges of the frame\n" );
+}
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ FAIL_IF_ERROR( x264_cli_csp_is_invalid( info->csp ), "invalid csp %d\n", info->csp )
+ crop_hnd_t *h = calloc( 1, sizeof(crop_hnd_t) );
+ if( !h )
+ return -1;
+
+ h->csp = x264_cli_get_csp( info->csp );
+ static const char *optlist[] = { "left", "top", "right", "bottom", NULL };
+ char **opts = x264_split_options( opt_string, optlist );
+ if( !opts )
+ return -1;
+
+ for( int i = 0; i < 4; i++ )
+ {
+ char *opt = x264_get_option( optlist[i], opts );
+ FAIL_IF_ERROR( !opt, "%s crop value not specified\n", optlist[i] )
+ h->dims[i] = x264_otoi( opt, -1 );
+ FAIL_IF_ERROR( h->dims[i] < 0, "%s crop value `%s' is less than 0\n", optlist[i], opt )
+ int dim_mod = i&1 ? (h->csp->mod_height << info->interlaced) : h->csp->mod_width;
+ FAIL_IF_ERROR( h->dims[i] % dim_mod, "%s crop value `%s' is not a multiple of %d\n", optlist[i], opt, dim_mod )
+ }
+ x264_free_string_array( opts );
+ h->dims[2] = info->width - h->dims[0] - h->dims[2];
+ h->dims[3] = info->height - h->dims[1] - h->dims[3];
+ FAIL_IF_ERROR( h->dims[2] <= 0 || h->dims[3] <= 0, "invalid output resolution %dx%d\n", h->dims[2], h->dims[3] )
+
+ if( info->width != h->dims[2] || info->height != h->dims[3] )
+ x264_cli_log( NAME, X264_LOG_INFO, "cropping to %dx%d\n", h->dims[2], h->dims[3] );
+ else
+ {
+ /* do nothing as the user supplied 0s for all the values */
+ free( h );
+ return 0;
+ }
+ /* done initializing, overwrite values */
+ info->width = h->dims[2];
+ info->height = h->dims[3];
+
+ h->prev_filter = *filter;
+ h->prev_hnd = *handle;
+ *handle = h;
+ *filter = crop_filter;
+
+ return 0;
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ crop_hnd_t *h = handle;
+ if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
+ return -1;
+ output->img.width = h->dims[2];
+ output->img.height = h->dims[3];
+ /* shift the plane pointers down 'top' rows and right 'left' columns. */
+ for( int i = 0; i < output->img.planes; i++ )
+ output->img.plane[i] += (int)(output->img.stride[i] * h->dims[1] * h->csp->height[i]
+ + h->dims[0] * h->csp->width[i]);
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ crop_hnd_t *h = handle;
+ /* NO filter should ever have a dependent release based on the plane pointers,
+ * so avoid unnecessary unshifting */
+ return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
+}
+
+static void free_filter( hnd_t handle )
+{
+ crop_hnd_t *h = handle;
+ h->prev_filter.free( h->prev_hnd );
+ free( h );
+}
+
+cli_vid_filter_t crop_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
--- /dev/null
+/*****************************************************************************
+ * fix_vfr_pts.c: x264 video vfr pts fixing filter
+ *****************************************************************************
+ * Copyright (C) 2010 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 "video.h"
+#include "internal.h"
+
+/* This filter calculates and store the frame's duration to the frame data
+ * (if it is not already calculated when the frame arrives to this point)
+ * so it can be used by filters that will need to reconstruct pts due to
+ * out-of-order frame requests */
+
+typedef struct
+{
+ hnd_t prev_hnd;
+ cli_vid_filter_t prev_filter;
+
+ /* we need 1 buffer picture and 1 place holder */
+ cli_pic_t buffer;
+ cli_pic_t holder;
+ int buffer_allocated;
+ int holder_frame;
+ int holder_ret;
+ int64_t pts;
+ int64_t last_duration;
+} fix_vfr_pts_hnd_t;
+
+cli_vid_filter_t fix_vfr_pts_filter;
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ /* if the input is not vfr, we don't do anything */
+ if( !info->vfr )
+ return 0;
+ fix_vfr_pts_hnd_t *h = calloc( 1, sizeof(fix_vfr_pts_hnd_t) );
+ if( !h )
+ return -1;
+
+ h->holder_frame = -1;
+ h->prev_hnd = *handle;
+ h->prev_filter = *filter;
+ *handle = h;
+ *filter = fix_vfr_pts_filter;
+
+ return 0;
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ fix_vfr_pts_hnd_t *h = handle;
+ /* if we want the holder picture and it errored, return the error. */
+ if( frame == h->holder_frame )
+ {
+ if( h->holder_ret )
+ return h->holder_ret;
+ }
+ else
+ {
+ /* if we have a holder frame and we don't want it, release the frame */
+ if( h->holder_frame > 0 && h->holder_frame < frame && h->prev_filter.release_frame( h->prev_hnd, &h->holder, h->holder_frame ) )
+ return -1;
+ h->holder_frame = -1;
+ if( h->prev_filter.get_frame( h->prev_hnd, &h->holder, frame ) )
+ return -1;
+ }
+
+ /* if the frame's duration is not set already, read the next frame to set it. */
+ if( !h->holder.duration )
+ {
+ /* allocate a buffer picture if we didn't already */
+ if( !h->buffer_allocated )
+ {
+ if( x264_cli_pic_alloc( &h->buffer, h->holder.img.csp, h->holder.img.width, h->holder.img.height ) )
+ return -1;
+ h->buffer_allocated = 1;
+ }
+ h->holder_frame = frame+1;
+ /* copy the current frame to the buffer, release it, and then read in the next frame to the placeholder */
+ if( x264_cli_pic_copy( &h->buffer, &h->holder ) || h->prev_filter.release_frame( h->prev_hnd, &h->holder, frame ) )
+ return -1;
+ h->holder_ret = h->prev_filter.get_frame( h->prev_hnd, &h->holder, h->holder_frame );
+ /* suppress non-monotonic pts warnings by setting the duration to be at least 1 */
+ if( !h->holder_ret )
+ h->last_duration = X264_MAX( h->holder.pts - h->buffer.pts, 1 );
+ h->buffer.duration = h->last_duration;
+ *output = h->buffer;
+ }
+ else
+ *output = h->holder;
+
+ output->pts = h->pts;
+ h->pts += output->duration;
+
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ fix_vfr_pts_hnd_t *h = handle;
+ /* if the frame is the buffered one, it's already been released */
+ if( frame == (h->holder_frame - 1) )
+ return 0;
+ return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
+}
+
+static void free_filter( hnd_t handle )
+{
+ fix_vfr_pts_hnd_t *h = handle;
+ h->prev_filter.free( h->prev_hnd );
+ if( h->buffer_allocated )
+ x264_cli_pic_clean( &h->buffer );
+ free( h );
+}
+
+cli_vid_filter_t fix_vfr_pts_filter = { "fix_vfr_pts", NULL, init, get_frame, release_frame, free_filter, NULL };
--- /dev/null
+/*****************************************************************************
+ * internal.c: x264 video filter internal utilities
+ *****************************************************************************
+ * Copyright (C) 2010 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 "internal.h"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "x264", __VA_ARGS__ )
+
+void x264_cli_plane_copy( pixel *dst, int i_dst, uint8_t *src, int i_src, int w, int h )
+{
+ while( h-- )
+ {
+ memcpy( dst, src, w );
+ dst += i_dst;
+ src += i_src;
+ }
+}
+
+int x264_cli_pic_copy( cli_pic_t *out, cli_pic_t *in )
+{
+ int csp = in->img.csp & X264_CSP_MASK;
+ FAIL_IF_ERROR( x264_cli_csp_is_invalid( in->img.csp ), "invalid colorspace arg %d\n", in->img.csp )
+ FAIL_IF_ERROR( in->img.csp != out->img.csp || in->img.height != out->img.height
+ || in->img.width != out->img.width, "incompatible frame properties\n" );
+ /* copy data */
+ out->duration = in->duration;
+ out->pts = in->pts;
+ out->opaque = in->opaque;
+
+ for( int i = 0; i < out->img.planes; i++ )
+ {
+ int height = in->img.height * x264_cli_csps[csp].height[i];
+ int width = in->img.width * x264_cli_csps[csp].width[i];
+ x264_cli_plane_copy( out->img.plane[i], out->img.stride[i], in->img.plane[i],
+ in->img.stride[i], width, height );
+ }
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+ * internal.h: x264 video filter internal utilities
+ *****************************************************************************
+ * Copyright (C) 2010 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.
+ *****************************************************************************/
+
+#ifndef X264_FILTER_VIDEO_INTERNAL_H
+#define X264_FILTER_VIDEO_INTERNAL_H
+#include "video.h"
+
+void x264_cli_plane_copy( uint8_t *dst, int i_dst, uint8_t *src, int i_src, int w, int h );
+int x264_cli_pic_copy( cli_pic_t *out, cli_pic_t *in );
+
+#endif
--- /dev/null
+/*****************************************************************************
+ * resize.c: x264 video resize filter
+ *****************************************************************************
+ * Copyright (C) 2010 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 "video.h"
+#define NAME "resize"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
+
+cli_vid_filter_t resize_filter;
+
+static int full_check( video_info_t *info, x264_param_t *param )
+{
+ int required = 0;
+ required |= info->csp != param->i_csp;
+ required |= info->width != param->i_width;
+ required |= info->height != param->i_height;
+ return required;
+}
+
+#if HAVE_SWSCALE
+#undef DECLARE_ALIGNED
+#include <libswscale/swscale.h>
+
+/* this function is not a part of the swscale API but is defined in swscale_internal.h */
+const char *sws_format_name( enum PixelFormat format );
+
+typedef struct
+{
+ int width;
+ int height;
+ int pix_fmt;
+} frame_prop_t;
+
+typedef struct
+{
+ hnd_t prev_hnd;
+ cli_vid_filter_t prev_filter;
+
+ cli_pic_t buffer;
+ int buffer_allocated;
+ int dst_csp;
+ struct SwsContext *ctx;
+ int ctx_flags;
+ int swap_chroma; /* state of swapping chroma planes */
+ frame_prop_t dst; /* desired output properties */
+ frame_prop_t scale; /* properties of the SwsContext input */
+} resizer_hnd_t;
+
+static void help( int longhelp )
+{
+ printf( " "NAME":[width,height][,sar][,fittobox][,csp][,method]\n" );
+ if( !longhelp )
+ return;
+ printf( " resizes frames based on the given criteria:\n"
+ " - resolution only: resizes and adapts sar to avoid stretching\n"
+ " - sar only: sets the sar and resizes to avoid stretching\n"
+ " - resolution and sar: resizes to given resolution and sets the sar\n"
+ " - fittobox: resizes the video based on the desired contraints\n"
+ " - width, height, both\n"
+ " - fittobox and sar: same as above except with specified sar\n"
+ " simultaneously converting to the given colorspace\n"
+ " using resizer method [\"bicubic\"]\n"
+ " - fastbilinear, bilinear, bicubic, experimental, point,\n"
+ " - area, bicublin, gauss, sinc, lanczos, spline\n" );
+}
+
+static uint32_t convert_cpu_to_flag( uint32_t cpu )
+{
+ uint32_t swscale_cpu = 0;
+ if( cpu & X264_CPU_ALTIVEC )
+ swscale_cpu |= SWS_CPU_CAPS_ALTIVEC;
+ if( cpu & X264_CPU_MMXEXT )
+ swscale_cpu |= SWS_CPU_CAPS_MMX | SWS_CPU_CAPS_MMX2;
+ return swscale_cpu;
+}
+
+static uint32_t convert_method_to_flag( const char *name )
+{
+ uint32_t flag = 0;
+ if( !strcasecmp( name, "fastbilinear" ) )
+ flag = SWS_FAST_BILINEAR;
+ else if( !strcasecmp( name, "bilinear" ) )
+ flag = SWS_BILINEAR;
+ else if( !strcasecmp( name, "bicubic" ) )
+ flag = SWS_BICUBIC;
+ else if( !strcasecmp( name, "experimental" ) )
+ flag = SWS_X;
+ else if( !strcasecmp( name, "point" ) )
+ flag = SWS_POINT;
+ else if( !strcasecmp( name, "area" ) )
+ flag = SWS_AREA;
+ else if( !strcasecmp( name, "bicublin" ) )
+ flag = SWS_BICUBLIN;
+ else if( !strcasecmp( name, "guass" ) )
+ flag = SWS_GAUSS;
+ else if( !strcasecmp( name, "sinc" ) )
+ flag = SWS_SINC;
+ else if( !strcasecmp( name, "lanczos" ) )
+ flag = SWS_LANCZOS;
+ else if( !strcasecmp( name, "spline" ) )
+ flag = SWS_SPLINE;
+ else // default
+ flag = SWS_BICUBIC;
+ return flag;
+}
+
+static int convert_csp_to_pix_fmt( int csp )
+{
+ if( csp&X264_CSP_OTHER )
+ return csp&X264_CSP_MASK;
+ switch( csp&X264_CSP_MASK )
+ {
+ case X264_CSP_I420: return PIX_FMT_YUV420P;
+ case X264_CSP_I422: return PIX_FMT_YUV422P;
+ case X264_CSP_I444: return PIX_FMT_YUV444P;
+ case X264_CSP_YV12: return PIX_FMT_YUV420P; /* specially handled via swapping chroma */
+ case X264_CSP_BGR: return PIX_FMT_BGR24;
+ case X264_CSP_BGRA: return PIX_FMT_BGRA;
+ default: return PIX_FMT_NONE;
+ }
+}
+
+static int pick_closest_supported_csp( int csp )
+{
+ int pix_fmt = convert_csp_to_pix_fmt( csp );
+ switch( pix_fmt )
+ {
+ case PIX_FMT_YUV422P:
+ case PIX_FMT_YUV422P16LE:
+ case PIX_FMT_YUV422P16BE:
+ case PIX_FMT_YUYV422:
+ case PIX_FMT_UYVY422:
+ return X264_CSP_I422;
+ case PIX_FMT_YUV444P:
+ case PIX_FMT_YUV444P16LE:
+ case PIX_FMT_YUV444P16BE:
+ return X264_CSP_I444;
+ case PIX_FMT_RGB24: // convert rgb to bgr
+ case PIX_FMT_RGB48BE:
+ case PIX_FMT_RGB48LE:
+ case PIX_FMT_RGB565BE:
+ case PIX_FMT_RGB565LE:
+ case PIX_FMT_RGB555BE:
+ case PIX_FMT_RGB555LE:
+ case PIX_FMT_BGR24:
+ case PIX_FMT_BGR565BE:
+ case PIX_FMT_BGR565LE:
+ case PIX_FMT_BGR555BE:
+ case PIX_FMT_BGR555LE:
+ return X264_CSP_BGR;
+ case PIX_FMT_ARGB:
+ case PIX_FMT_RGBA:
+ case PIX_FMT_ABGR:
+ case PIX_FMT_BGRA:
+ return X264_CSP_BGRA;
+ default:
+ return X264_CSP_I420;
+ }
+}
+
+static int round_dbl( double val, int precision, int truncate )
+{
+ int ret = (int)(val / precision) * precision;
+ if( !truncate && (val - ret) >= (precision/2) ) // use the remainder if we're not truncating it
+ ret += precision;
+ return ret;
+}
+
+static int handle_opts( const char **optlist, char **opts, video_info_t *info, resizer_hnd_t *h )
+{
+ uint32_t out_sar_w, out_sar_h;
+
+ char *str_width = x264_get_option( optlist[0], opts );
+ char *str_height = x264_get_option( optlist[1], opts );
+ char *str_sar = x264_get_option( optlist[2], opts );
+ char *fittobox = x264_get_option( optlist[3], opts );
+ char *str_csp = x264_get_option( optlist[4], opts );
+ int width = x264_otoi( str_width, -1 );
+ int height = x264_otoi( str_height, -1 );
+
+ int csp_only = 0;
+ uint32_t in_sar_w = info->sar_width;
+ uint32_t in_sar_h = info->sar_height;
+
+ if( str_csp )
+ {
+ /* output csp was specified, lookup against valid values */
+ int csp;
+ for( csp = X264_CSP_CLI_MAX-1; x264_cli_csps[csp].name && strcasecmp( x264_cli_csps[csp].name, str_csp ); )
+ csp--;
+ FAIL_IF_ERROR( csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", str_csp );
+ h->dst_csp = csp;
+ }
+
+ /* if the input sar is currently invalid, set it to 1:1 so it can be used in math */
+ if( !in_sar_w || !in_sar_h )
+ in_sar_w = in_sar_h = 1;
+ if( str_sar )
+ {
+ FAIL_IF_ERROR( 2 != sscanf( str_sar, "%u:%u", &out_sar_w, &out_sar_h ) &&
+ 2 != sscanf( str_sar, "%u/%u", &out_sar_w, &out_sar_h ),
+ "invalid sar `%s'\n", str_sar )
+ }
+ else
+ out_sar_w = out_sar_h = 1;
+ if( fittobox )
+ {
+ /* resize the video to fit the box as much as possible */
+ double box_width = width;
+ double box_height = height;
+ if( !strcasecmp( fittobox, "both" ) )
+ {
+ FAIL_IF_ERROR( box_width <= 0 || box_height <= 0, "invalid box resolution %sx%s\n",
+ x264_otos( str_width, "unset" ), x264_otos( str_height, "unset" ) )
+ }
+ else if( !strcasecmp( fittobox, "width" ) )
+ {
+ FAIL_IF_ERROR( box_width <= 0, "invalid box width `%s'\n", x264_otos( str_width, "unset" ) )
+ box_height = INT_MAX;
+ }
+ else if( !strcasecmp( fittobox, "height" ) )
+ {
+ FAIL_IF_ERROR( box_height <= 0, "invalid box height `%s'\n", x264_otos( str_height, "unset" ) )
+ box_width = INT_MAX;
+ }
+ else FAIL_IF_ERROR( 1, "invalid fittobox mode `%s'\n", fittobox )
+
+ /* we now have the requested bounding box display dimensions, now adjust them for output sar */
+ if( out_sar_w > out_sar_h ) // SAR is wide, decrease width
+ box_width *= (double)out_sar_h / out_sar_w;
+ else // SAR is thin, decrease height
+ box_height *= (double)out_sar_w / out_sar_h;
+
+ /* get the display resolution of the clip as it is now */
+ double d_width = info->width;
+ double d_height = info->height;
+ if( in_sar_w > in_sar_h )
+ d_width *= (double)in_sar_w / in_sar_h;
+ else
+ d_height *= (double)in_sar_h / in_sar_w;
+ /* now convert it to the coded resolution in accordance with the output sar */
+ if( out_sar_w > out_sar_h )
+ d_width *= (double)out_sar_h / out_sar_w;
+ else
+ d_height *= (double)out_sar_w / out_sar_h;
+
+ /* maximally fit the new coded resolution to the box */
+ double scale = X264_MIN( box_width / d_width, box_height / d_height );
+ const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
+ width = round_dbl( scale * d_width, csp->mod_width, 1 );
+ height = round_dbl( scale * d_height, csp->mod_height, 1 );
+ }
+ else
+ {
+ if( str_width || str_height )
+ {
+ FAIL_IF_ERROR( width <= 0 || height <= 0, "invalid resolution %sx%s\n",
+ x264_otos( str_width, "unset" ), x264_otos( str_height, "unset" ) )
+ if( !str_sar ) /* res only -> adjust sar */
+ {
+ /* new_sar = (new_h * old_w * old_sar_w) / (old_h * new_w * old_sar_h) */
+ uint64_t num = (uint64_t)info->width * height;
+ uint64_t den = (uint64_t)info->height * width;
+ x264_reduce_fraction64( &num, &den );
+ out_sar_w = num * in_sar_w;
+ out_sar_h = den * in_sar_h;
+ x264_reduce_fraction( &out_sar_w, &out_sar_h );
+ }
+ }
+ else if( str_sar ) /* sar only -> adjust res */
+ {
+ const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
+ width = info->width;
+ height = info->height;
+ if( (out_sar_w * in_sar_h) > (out_sar_h * in_sar_w) ) // SAR got wider, decrease width
+ width = round_dbl( (double)info->width * in_sar_w * out_sar_h
+ / in_sar_h / out_sar_w, csp->mod_width, 0 );
+ else // SAR got thinner, decrease height
+ height = round_dbl( (double)info->height * in_sar_h * out_sar_w
+ / in_sar_w / out_sar_h, csp->mod_height, 0 );
+ }
+ else /* csp only */
+ {
+ h->dst.width = info->width;
+ h->dst.height = info->height;
+ csp_only = 1;
+ }
+ }
+ if( !csp_only )
+ {
+ info->sar_width = out_sar_w;
+ info->sar_height = out_sar_h;
+ h->dst.width = width;
+ h->dst.height = height;
+ }
+ return 0;
+}
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ /* if called for normalizing the csp to known formats and the format is not unknown, exit */
+ if( opt_string && !strcmp( opt_string, "normcsp" ) && !(info->csp&X264_CSP_OTHER) )
+ return 0;
+ /* if called by x264cli and nothing needs to be done, exit */
+ if( !opt_string && !full_check( info, param ) )
+ return 0;
+
+ static const char *optlist[] = { "width", "height", "sar", "fittobox", "csp", "method", NULL };
+ char **opts = x264_split_options( opt_string, optlist );
+ if( !opts && opt_string )
+ return -1;
+
+ resizer_hnd_t *h = calloc( 1, sizeof(resizer_hnd_t) );
+ if( !h )
+ return -1;
+ if( opts )
+ {
+ h->dst_csp = info->csp;
+ h->dst.width = info->width;
+ h->dst.height = info->height;
+ if( !strcmp( opt_string, "normcsp" ) )
+ h->dst_csp = pick_closest_supported_csp( info->csp );
+ else if( handle_opts( optlist, opts, info, h ) )
+ return -1;
+ }
+ else
+ {
+ h->dst_csp = param->i_csp;
+ h->dst.width = param->i_width;
+ h->dst.height = param->i_height;
+ }
+ uint32_t method = convert_method_to_flag( x264_otos( x264_get_option( optlist[5], opts ), "" ) );
+ x264_free_string_array( opts );
+
+ h->ctx_flags = convert_cpu_to_flag( param->cpu ) | method;
+ if( method != SWS_FAST_BILINEAR )
+ h->ctx_flags |= SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND;
+ h->dst.pix_fmt = convert_csp_to_pix_fmt( h->dst_csp );
+ h->scale = h->dst;
+ /* swap chroma planes for yv12 to have it become i420 */
+ h->swap_chroma = (info->csp & X264_CSP_MASK) == X264_CSP_YV12;
+ int src_pix_fmt = convert_csp_to_pix_fmt( info->csp );
+
+ /* confirm swscale can support this conversion */
+ FAIL_IF_ERROR( !sws_isSupportedInput( src_pix_fmt ), "input colorspace %s is not supported\n", sws_format_name( src_pix_fmt ) )
+ FAIL_IF_ERROR( !sws_isSupportedOutput( h->dst.pix_fmt ), "output colorspace %s is not supported\n", sws_format_name( h->dst.pix_fmt ) )
+ FAIL_IF_ERROR( h->dst.height != info->height && info->interlaced,
+ "swscale is not compatible with interlaced vertical resizing\n" )
+ /* confirm that the desired resolution meets the colorspace requirements */
+ const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
+ FAIL_IF_ERROR( h->dst.width % csp->mod_width || h->dst.height % csp->mod_height,
+ "resolution %dx%d is not compliant with colorspace %s\n", h->dst.width, h->dst.height, csp->name )
+
+ if( h->dst.width != info->width || h->dst.height != info->height )
+ x264_cli_log( NAME, X264_LOG_INFO, "resizing to %dx%d\n", h->dst.width, h->dst.height );
+ if( h->dst.pix_fmt != src_pix_fmt )
+ x264_cli_log( NAME, X264_LOG_WARNING, "converting from %s to %s\n",
+ sws_format_name( src_pix_fmt ), sws_format_name( h->dst.pix_fmt ) );
+ h->dst_csp |= info->csp & X264_CSP_VFLIP; // preserve vflip
+ /* finished initing, overwrite values */
+ info->csp = h->dst_csp;
+ info->width = h->dst.width;
+ info->height = h->dst.height;
+
+ h->prev_filter = *filter;
+ h->prev_hnd = *handle;
+ *handle = h;
+ *filter = resize_filter;
+
+ return 0;
+}
+
+static int check_resizer( resizer_hnd_t *h, cli_pic_t *in )
+{
+ frame_prop_t input_prop = { in->img.width, in->img.height, convert_csp_to_pix_fmt( in->img.csp ) };
+ if( !memcmp( &input_prop, &h->scale, sizeof(frame_prop_t) ) )
+ return 0;
+ if( h->ctx )
+ {
+ sws_freeContext( h->ctx );
+ x264_cli_log( NAME, X264_LOG_WARNING, "stream properties changed at pts %"PRId64"\n", in->pts );
+ }
+ h->scale = input_prop;
+ if( !h->buffer_allocated )
+ {
+ if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, h->dst.width, h->dst.height ) )
+ return -1;
+ h->buffer_allocated = 1;
+ }
+ h->ctx = sws_getContext( h->scale.width, h->scale.height, h->scale.pix_fmt, h->dst.width,
+ h->dst.height, h->dst.pix_fmt, h->ctx_flags, NULL, NULL, NULL );
+ FAIL_IF_ERROR( !h->ctx, "swscale init failed\n" )
+ return 0;
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ resizer_hnd_t *h = handle;
+ if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
+ return -1;
+ if( check_resizer( h, output ) )
+ return -1;
+ if( h->ctx )
+ {
+ sws_scale( h->ctx, (const uint8_t* const*)output->img.plane, output->img.stride,
+ 0, output->img.height, h->buffer.img.plane, h->buffer.img.stride );
+ output->img = h->buffer.img; /* copy img data */
+ }
+ else
+ output->img.csp = h->dst_csp;
+ if( h->swap_chroma )
+ XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
+
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ resizer_hnd_t *h = handle;
+ return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
+}
+
+static void free_filter( hnd_t handle )
+{
+ resizer_hnd_t *h = handle;
+ h->prev_filter.free( h->prev_hnd );
+ if( h->ctx )
+ sws_freeContext( h->ctx );
+ if( h->buffer_allocated )
+ x264_cli_pic_clean( &h->buffer );
+ free( h );
+}
+
+#else /* no swscale */
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ int ret = 0;
+
+ if( !opt_string )
+ ret = full_check( info, param );
+ else
+ {
+ if( !strcmp( opt_string, "normcsp" ) )
+ ret = info->csp & X264_CSP_OTHER;
+ else
+ ret = -1;
+ }
+
+ /* pass if nothing needs to be done, otherwise fail */
+ FAIL_IF_ERROR( ret, "not compiled with swscale support\n" )
+ return 0;
+}
+
+#define help NULL
+#define get_frame NULL
+#define release_frame NULL
+#define free_filter NULL
+#define convert_csp_to_pix_fmt(x) (x & X264_CSP_MASK)
+
+#endif
+
+cli_vid_filter_t resize_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
--- /dev/null
+/*****************************************************************************
+ * select_every.c: x264 video select every filter
+ *****************************************************************************
+ * Copyright (C) 2010 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 "video.h"
+#define NAME "select_every"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
+
+#define MAX_PATTERN_SIZE 100 /* arbitrary */
+
+typedef struct
+{
+ hnd_t prev_hnd;
+ cli_vid_filter_t prev_filter;
+
+ int *pattern;
+ int pattern_len;
+ int step_size;
+ int vfr;
+ int64_t pts;
+} selvry_hnd_t;
+
+cli_vid_filter_t select_every_filter;
+
+static void help( int longhelp )
+{
+ printf( " "NAME":step,offset1[,...]\n" );
+ if( !longhelp )
+ return;
+ printf( " apply a selection pattern to input frames\n"
+ " step: the number of frames in the pattern\n"
+ " offsets: the offset into the step to select a frame\n"
+ " see: http://avisynth.org/mediawiki/Select#SelectEvery\n" );
+}
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ selvry_hnd_t *h = malloc( sizeof(selvry_hnd_t) );
+ if( !h )
+ return -1;
+ h->pattern_len = 0;
+ h->step_size = 0;
+ int offsets[MAX_PATTERN_SIZE];
+ for( char *tok, *p = opt_string; (tok = strtok( p, "," )); p = NULL )
+ {
+ int val = x264_otoi( tok, -1 );
+ if( p )
+ {
+ FAIL_IF_ERROR( val <= 0, "invalid step `%s'\n", tok )
+ h->step_size = val;
+ continue;
+ }
+ FAIL_IF_ERROR( val < 0 || val >= h->step_size, "invalid offset `%s'\n", tok )
+ FAIL_IF_ERROR( h->pattern_len >= MAX_PATTERN_SIZE, "max pattern size %d reached\n", MAX_PATTERN_SIZE )
+ offsets[h->pattern_len++] = val;
+ }
+ FAIL_IF_ERROR( !h->step_size, "no step size provided\n" )
+ FAIL_IF_ERROR( !h->pattern_len, "no offsets supplied\n" )
+
+ h->pattern = malloc( h->pattern_len * sizeof(int) );
+ if( !h->pattern )
+ return -1;
+ memcpy( h->pattern, offsets, h->pattern_len * sizeof(int) );
+
+ /* determine required cache size to maintain pattern. */
+ intptr_t max_rewind = 0;
+ int min = h->step_size;
+ for( int i = h->pattern_len-1; i >= 0; i-- )
+ {
+ min = X264_MIN( min, offsets[i] );
+ if( i )
+ max_rewind = X264_MAX( max_rewind, offsets[i-1] - min + 1 );
+ /* reached maximum rewind size */
+ if( max_rewind == h->step_size )
+ break;
+ }
+ if( x264_init_vid_filter( "cache", handle, filter, info, param, (void*)max_rewind ) )
+ return -1;
+
+ /* done initing, overwrite properties */
+ if( h->step_size != h->pattern_len )
+ {
+ info->num_frames = (uint64_t)info->num_frames * h->pattern_len / h->step_size;
+ info->fps_den *= h->step_size;
+ info->fps_num *= h->pattern_len;
+ x264_reduce_fraction( &info->fps_num, &info->fps_den );
+ if( info->vfr )
+ {
+ info->timebase_den *= h->pattern_len;
+ info->timebase_num *= h->step_size;
+ x264_reduce_fraction( &info->timebase_num, &info->timebase_den );
+ }
+ }
+
+ h->pts = 0;
+ h->vfr = info->vfr;
+ h->prev_filter = *filter;
+ h->prev_hnd = *handle;
+ *filter = select_every_filter;
+ *handle = h;
+
+ return 0;
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ selvry_hnd_t *h = handle;
+ int pat_frame = h->pattern[frame % h->pattern_len] + frame / h->pattern_len * h->step_size;
+ if( h->prev_filter.get_frame( h->prev_hnd, output, pat_frame ) )
+ return -1;
+ if( h->vfr )
+ {
+ output->pts = h->pts;
+ h->pts += output->duration;
+ }
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ selvry_hnd_t *h = handle;
+ int pat_frame = h->pattern[frame % h->pattern_len] + frame / h->pattern_len * h->step_size;
+ return h->prev_filter.release_frame( h->prev_hnd, pic, pat_frame );
+}
+
+static void free_filter( hnd_t handle )
+{
+ selvry_hnd_t *h = handle;
+ h->prev_filter.free( h->prev_hnd );
+ free( h->pattern );
+ free( h );
+}
+
+cli_vid_filter_t select_every_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
--- /dev/null
+/*****************************************************************************
+ * source.c: x264 video source filter
+ *****************************************************************************
+ * Copyright (C) 2010 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 "video.h"
+
+/* This filter converts the demuxer API into the filtering API for video frames.
+ * Backseeking is prohibited here as not all demuxers are capable of doing so. */
+
+typedef struct
+{
+ cli_pic_t pic;
+ hnd_t hin;
+ int cur_frame;
+} source_hnd_t;
+
+cli_vid_filter_t source_filter;
+
+static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ source_hnd_t *h = calloc( 1, sizeof(source_hnd_t) );
+ if( !h )
+ return -1;
+ h->cur_frame = -1;
+
+ if( input.picture_alloc( &h->pic, info->csp, info->width, info->height ) )
+ return -1;
+
+ h->hin = *handle;
+ *handle = h;
+ *filter = source_filter;
+
+ return 0;
+}
+
+static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
+{
+ source_hnd_t *h = handle;
+ /* do not allow requesting of frames from before the current position */
+ if( frame <= h->cur_frame || input.read_frame( &h->pic, h->hin, frame ) )
+ return -1;
+ h->cur_frame = frame;
+ *output = h->pic;
+ return 0;
+}
+
+static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
+{
+ source_hnd_t *h = handle;
+ if( input.release_frame && input.release_frame( &h->pic, h->hin ) )
+ return -1;
+ return 0;
+}
+
+static void free_filter( hnd_t handle )
+{
+ source_hnd_t *h = handle;
+ input.picture_clean( &h->pic );
+ input.close_file( h->hin );
+ free( h );
+}
+
+cli_vid_filter_t source_filter = { "source", NULL, init, get_frame, release_frame, free_filter, NULL };
--- /dev/null
+/*****************************************************************************
+ * video.c: x264 video filter driver
+ *****************************************************************************
+ * Copyright (C) 2010 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 "video.h"
+
+static cli_vid_filter_t *first_filter = NULL;
+
+static void register_vid_filter( cli_vid_filter_t *new_filter )
+{
+ cli_vid_filter_t *filter_i = first_filter;
+ while( filter_i->next )
+ filter_i = filter_i->next;
+ filter_i->next = new_filter;
+ new_filter->next = NULL;
+}
+
+#define REGISTER_VFILTER(name)\
+{\
+ extern cli_vid_filter_t name##_filter;\
+ register_vid_filter( &name##_filter );\
+}
+
+void x264_register_vid_filters()
+{
+ extern cli_vid_filter_t source_filter;
+ first_filter = &source_filter;
+ REGISTER_VFILTER( cache );
+ REGISTER_VFILTER( crop );
+ REGISTER_VFILTER( fix_vfr_pts );
+ REGISTER_VFILTER( resize );
+ REGISTER_VFILTER( select_every );
+}
+
+int x264_init_vid_filter( const char *name, hnd_t *handle, cli_vid_filter_t *filter,
+ video_info_t *info, x264_param_t *param, char *opt_string )
+{
+ cli_vid_filter_t *filter_i = first_filter;
+ while( filter_i && strcasecmp( name, filter_i->name ) )
+ filter_i = filter_i->next;
+ FAIL_IF_ERR( !filter_i, "x264", "invalid filter `%s'\n", name );
+ if( filter_i->init( handle, filter, info, param, opt_string ) )
+ return -1;
+
+ return 0;
+}
+
+void x264_vid_filter_help( int longhelp )
+{
+ for( cli_vid_filter_t *filter_i = first_filter; filter_i; filter_i = filter_i->next )
+ if( filter_i->help )
+ filter_i->help( longhelp );
+}
--- /dev/null
+/*****************************************************************************
+ * video.h: x264 video filter modules
+ *****************************************************************************
+ * Copyright (C) 2010 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.
+ *****************************************************************************/
+
+#ifndef X264_FILTER_VIDEO_H
+#define X264_FILTER_VIDEO_H
+
+#include "input/input.h"
+#include "filters/filters.h"
+
+typedef struct cli_vid_filter_t cli_vid_filter_t;
+
+struct cli_vid_filter_t
+{
+ /* name of the filter */
+ const char *name;
+ /* help: a short message on what the filter does and how to use it.
+ * this should only be implemented by filters directly accessible by the user */
+ void (*help)( int longhelp );
+ /* init: initializes the filter given the input clip properties and parameter to adjust them as necessary
+ * with the given options provided by the user.
+ * returns 0 on success, nonzero on error. */
+ int (*init)( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string );
+ /* get_frame: given the storage for the output frame and desired frame number, generate the frame accordingly.
+ * the image data returned by get_frame should be treated as const and not be altered.
+ * returns 0 on success, nonzero on error. */
+ int (*get_frame)( hnd_t handle, cli_pic_t *output, int frame );
+ /* release_frame: frame is done being used and is signaled for cleanup.
+ * returns 0 on succeess, nonzero on error. */
+ int (*release_frame)( hnd_t handle, cli_pic_t *pic, int frame );
+ /* free: run filter cleanup procedures. */
+ void (*free)( hnd_t handle );
+ /* next registered filter, unused by filters themselves */
+ cli_vid_filter_t *next;
+};
+
+void x264_register_vid_filters();
+void x264_vid_filter_help( int longhelp );
+int x264_init_vid_filter( const char *name, hnd_t *handle, cli_vid_filter_t *filter,
+ video_info_t *info, x264_param_t *param, char *opt_string );
+
+#endif
/* when AVS supports other planar colorspaces, a workaround is required */
#define AVS_INTERFACE_OTHER_PLANAR 5
+#if HAVE_SWSCALE
+#include <libavutil/pixfmt.h>
+#endif
+
/* maximum size of the sequence of filters to try on non script files */
#define AVS_MAX_SEQUENCE 5
info->interlaced = 1;
info->tff = avs_is_tff( vi );
}
- FAIL_IF_ERROR( vi->width&1 || vi->height&1, "input clip width or height not divisible by 2 (%dx%d)\n", vi->width, vi->height )
+ /* if swscale is available, convert CSPs with it rather than with avisynth. */
+#if HAVE_SWSCALE
+ int convert_to_yv12 = 0;
+#else
+ int convert_to_yv12 = !avs_is_yv12( vi );
+#endif
/* always call ConvertToYV12 to convert non YV12 planar colorspaces to YV12 when user's AVS supports them,
as all planar colorspaces are flagged as YV12. If it is already YV12 in this case, the call does nothing */
- if( !avs_is_yv12( vi ) || avs_version >= AVS_INTERFACE_OTHER_PLANAR )
+ if( convert_to_yv12 || (avs_version >= AVS_INTERFACE_OTHER_PLANAR && avs_is_yv12( vi )) )
{
- if( !avs_is_yv12( vi ) )
+ if( convert_to_yv12 )
x264_cli_log( "avs", X264_LOG_WARNING, "converting input clip to YV12" );
else
x264_cli_log( "avs", X264_LOG_INFO, "avisynth 2.6+ detected, forcing conversion to YV12" );
+ FAIL_IF_ERROR( vi->width&1 || vi->height&1, "input clip width or height not divisible by 2 (%dx%d)\n", vi->width, vi->height )
const char *arg_name[2] = { NULL, "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 );
}
h->func.avs_release_value( res );
- info->width = vi->width;
- info->height = vi->height;
+ 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;
- info->csp = X264_CSP_YV12;
+ h->num_frames = info->num_frames = vi->num_frames;
+ info->thread_safe = 1;
+#if HAVE_SWSCALE
+ if( avs_is_rgb32( vi ) )
+ info->csp = X264_CSP_BGRA | X264_CSP_VFLIP;
+ else if( avs_is_rgb24( vi ) )
+ info->csp = X264_CSP_BGR | X264_CSP_VFLIP;
+ else if( avs_is_yuy2( vi ) )
+ info->csp = PIX_FMT_YUYV422 | X264_CSP_OTHER;
+ else /* yv12 */
+ info->csp = X264_CSP_I420;
+#else
+ info->csp = X264_CSP_I420;
+#endif
info->vfr = 0;
*p_handle = h;
return 0;
}
-static int get_frame_total( hnd_t handle )
+static int picture_alloc( cli_pic_t *pic, int csp, int width, int height )
{
- avs_hnd_t *h = handle;
- return h->num_frames;
-}
-
-static int picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
-{
- x264_picture_init( pic );
- pic->img.i_csp = i_csp;
- pic->img.i_plane = 3;
+ if( x264_cli_pic_alloc( pic, X264_CSP_NONE, width, height ) )
+ return -1;
+ pic->img.csp = csp;
+ const x264_cli_csp_t *cli_csp = x264_cli_get_csp( csp );
+ pic->img.planes = cli_csp ? cli_csp->planes : 1;
return 0;
}
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
- static int plane[3] = { AVS_PLANAR_Y, AVS_PLANAR_V, AVS_PLANAR_U };
+ static const int plane[3] = { AVS_PLANAR_Y, AVS_PLANAR_U, AVS_PLANAR_V };
avs_hnd_t *h = handle;
if( i_frame >= h->num_frames )
return -1;
- AVS_VideoFrame *frm = p_pic->opaque = h->func.avs_get_frame( h->clip, i_frame );
+ AVS_VideoFrame *frm = pic->opaque = h->func.avs_get_frame( h->clip, i_frame );
const char *err = h->func.avs_clip_get_error( h->clip );
FAIL_IF_ERROR( err, "%s occurred while reading frame %d\n", err, i_frame )
- for( int i = 0; i < 3; i++ )
+ for( int i = 0; i < pic->img.planes; i++ )
{
/* explicitly cast away the const attribute to avoid a warning */
- p_pic->img.plane[i] = (uint8_t*)avs_get_read_ptr_p( frm, plane[i] );
- p_pic->img.i_stride[i] = avs_get_pitch_p( frm, plane[i] );
+ pic->img.plane[i] = (uint8_t*)avs_get_read_ptr_p( frm, plane[i] );
+ pic->img.stride[i] = avs_get_pitch_p( frm, plane[i] );
}
return 0;
}
-static int release_frame( x264_picture_t *pic, hnd_t handle )
+static int release_frame( cli_pic_t *pic, hnd_t handle )
{
avs_hnd_t *h = handle;
h->func.avs_release_video_frame( pic->opaque );
return 0;
}
-static void picture_clean( x264_picture_t *pic )
+static void picture_clean( cli_pic_t *pic )
{
- memset( pic, 0, sizeof(x264_picture_t) );
+ memset( pic, 0, sizeof(cli_pic_t) );
}
static int close_file( hnd_t handle )
return 0;
}
-const cli_input_t avs_input = { open_file, get_frame_total, picture_alloc, read_frame, release_frame, picture_clean, close_file };
+const cli_input_t avs_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };
{
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 )
FFMS_DestroyIndex( idx );
const FFMS_VideoProperties *videop = FFMS_GetVideoProperties( h->video_source );
- h->total_frames = videop->NumFrames;
+ info->num_frames = videop->NumFrames;
info->sar_height = videop->SARDen;
info->sar_width = videop->SARNum;
info->fps_den = videop->FPSDenominator;
info->fps_num = videop->FPSNumerator;
h->vfr_input = info->vfr;
+ /* ffms is thread unsafe as it uses a single frame buffer for all frame requests */
+ info->thread_safe = 0;
const FFMS_Frame *frame = FFMS_GetFrame( h->video_source, 0, &e );
FAIL_IF_ERROR( !frame, "could not read frame 0\n" )
- 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->width = frame->EncodedWidth;
+ info->height = frame->EncodedHeight;
+ info->csp = frame->EncodedPixelFormat | X264_CSP_OTHER;
info->interlaced = frame->InterlacedFrame;
info->tff = frame->TopFieldFirst;
- if( h->cur_pix_fmt != PIX_FMT_YUV420P )
- x264_cli_log( "ffms", X264_LOG_WARNING, "converting from %s to YV12\n",
- avcodec_get_pix_fmt_name( h->cur_pix_fmt ) );
-
/* ffms timestamps are in milliseconds. ffms also uses int64_ts for timebase,
* so we need to reduce large timebases to prevent overflow */
if( h->vfr_input )
return 0;
}
-static int get_frame_total( hnd_t handle )
+static int picture_alloc( cli_pic_t *pic, int csp, int width, int height )
{
- 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 );
- x264_cli_log( "ffms", X264_LOG_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 );
- FAIL_IF_ERROR( !h->scaler, "could not open swscale context\n" )
+ if( x264_cli_pic_alloc( pic, csp, width, height ) )
+ return -1;
+ pic->img.planes = 4;
return 0;
}
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
ffms_hnd_t *h = handle;
FFMS_ErrorInfo e;
const FFMS_Frame *frame = FFMS_GetFrame( h->video_source, i_frame, &e );
FAIL_IF_ERROR( !frame, "could not read frame %d\n", i_frame )
- 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 );
+ memcpy( pic->img.stride, frame->Linesize, sizeof(pic->img.stride) );
+ memcpy( pic->img.plane, frame->Data, sizeof(pic->img.plane) );
if( h->vfr_input )
{
+ const FFMS_FrameInfo *info = FFMS_GetFrameInfo( h->track, i_frame );
FAIL_IF_ERROR( info->PTS == AV_NOPTS_VALUE, "invalid timestamp. "
"Use --force-cfr and specify a framerate with --fps\n" )
- if( !h->pts_offset_flag )
- {
- h->pts_offset = info->PTS;
- h->pts_offset_flag = 1;
- }
-
- p_pic->i_pts = (info->PTS - h->pts_offset) >> h->reduce_pts;
+ pic->pts = info->PTS >> h->reduce_pts;
+ pic->duration = 0;
}
return 0;
}
+static void picture_clean( cli_pic_t *pic )
+{
+ memset( pic, 0, sizeof(cli_pic_t) );
+}
+
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;
}
-const cli_input_t ffms_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
+const cli_input_t ffms_input = { open_file, picture_alloc, read_frame, NULL, picture_clean, close_file };
--- /dev/null
+/*****************************************************************************
+ * input.c: x264 input module common
+ *****************************************************************************
+ * Copyright (C) 2010 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 "input.h"
+
+const x264_cli_csp_t x264_cli_csps[] = {
+ [X264_CSP_I420] = { "i420", 3, { 1, .5, .5 }, { 1, .5, .5 }, 2, 2 },
+ [X264_CSP_I422] = { "i422", 3, { 1, .5, .5 }, { 1, 1, 1 }, 2, 1 },
+ [X264_CSP_I444] = { "i444", 3, { 1, 1, 1 }, { 1, 1, 1 }, 1, 1 },
+ [X264_CSP_YV12] = { "yv12", 3, { 1, .5, .5 }, { 1, .5, .5 }, 2, 2 },
+ [X264_CSP_BGR] = { "bgr", 1, { 3 }, { 1 }, 1, 1 },
+ [X264_CSP_BGRA] = { "bgra", 1, { 4 }, { 1 }, 1, 1 }
+};
+
+int x264_cli_csp_is_invalid( int csp )
+{
+ int csp_mask = csp & X264_CSP_MASK;
+ return csp_mask <= X264_CSP_NONE || csp_mask >= X264_CSP_CLI_MAX || csp & X264_CSP_OTHER;
+}
+
+uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane )
+{
+ int csp_mask = csp & X264_CSP_MASK;
+ if( x264_cli_csp_is_invalid( csp ) || plane < 0 || plane >= x264_cli_csps[csp_mask].planes )
+ return 0;
+ uint64_t size = (uint64_t)width * height;
+ size *= x264_cli_csps[csp_mask].width[plane] * x264_cli_csps[csp_mask].height[plane];
+ return size;
+}
+
+uint64_t x264_cli_pic_size( int csp, int width, int height )
+{
+ if( x264_cli_csp_is_invalid( csp ) )
+ return 0;
+ uint64_t size = 0;
+ int csp_mask = csp & X264_CSP_MASK;
+ for( int i = 0; i < x264_cli_csps[csp_mask].planes; i++ )
+ size += x264_cli_pic_plane_size( csp, width, height, i );
+ return size;
+}
+
+int x264_cli_pic_alloc( cli_pic_t *pic, int csp, int width, int height )
+{
+ memset( pic, 0, sizeof(cli_pic_t) );
+ int csp_mask = csp & X264_CSP_MASK;
+ if( x264_cli_csp_is_invalid( csp ) )
+ pic->img.planes = 0;
+ else
+ pic->img.planes = x264_cli_csps[csp_mask].planes;
+ pic->img.csp = csp;
+ pic->img.width = width;
+ pic->img.height = height;
+ for( int i = 0; i < pic->img.planes; i++ )
+ {
+ pic->img.plane[i] = x264_malloc( x264_cli_pic_plane_size( csp, width, height, i ) );
+ if( !pic->img.plane[i] )
+ return -1;
+ pic->img.stride[i] = width * x264_cli_csps[csp_mask].width[i];
+ }
+
+ return 0;
+}
+
+void x264_cli_pic_clean( cli_pic_t *pic )
+{
+ for( int i = 0; i < pic->img.planes; i++ )
+ x264_free( pic->img.plane[i] );
+ memset( pic, 0, sizeof(cli_pic_t) );
+}
+
+const x264_cli_csp_t *x264_cli_get_csp( int csp )
+{
+ if( x264_cli_csp_is_invalid( csp ) )
+ return NULL;
+ return x264_cli_csps + (csp&X264_CSP_MASK);
+}
typedef struct
{
char *index_file;
- char *resolution; /* resolution string parsed by raw yuv input */
+ char *resolution;
+ char *colorspace;
char *timebase;
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 csp; /* colorspace of the input */
uint32_t fps_num;
uint32_t fps_den;
int height;
int interlaced;
+ int num_frames;
uint32_t sar_width;
uint32_t sar_height;
int tff;
+ int thread_safe; /* demuxer is thread_input safe */
uint32_t timebase_num;
uint32_t timebase_den;
int vfr;
int width;
} video_info_t;
+/* image data type used by x264cli */
+typedef struct
+{
+ int csp; /* colorspace */
+ int width; /* width of the picture */
+ int height; /* height of the picture */
+ int planes; /* number of planes */
+ uint8_t *plane[4]; /* pointers for each plane */
+ int stride[4]; /* strides for each plane */
+} cli_image_t;
+
+typedef struct
+{
+ cli_image_t img;
+ int64_t pts; /* input pts */
+ int64_t duration; /* frame duration - used for vfr */
+ void *opaque; /* opaque handle */
+} cli_pic_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 );
- int (*release_frame)( x264_picture_t *pic, hnd_t handle );
- void (*picture_clean)( x264_picture_t *pic );
+ int (*picture_alloc)( cli_pic_t *pic, int csp, int width, int height );
+ int (*read_frame)( cli_pic_t *pic, hnd_t handle, int i_frame );
+ int (*release_frame)( cli_pic_t *pic, hnd_t handle );
+ void (*picture_clean)( cli_pic_t *pic );
int (*close_file)( hnd_t handle );
} cli_input_t;
-extern const cli_input_t yuv_input;
+extern const cli_input_t raw_input;
extern const cli_input_t y4m_input;
extern const cli_input_t avs_input;
extern cli_input_t thread_input;
extern const cli_input_t ffms_input;
extern cli_input_t timecode_input;
+extern cli_input_t input;
+
+/* extended colorspace list that isn't supported by libx264 but by the cli */
+#define X264_CSP_I422 X264_CSP_MAX /* yuv 4:2:2 planar */
+#define X264_CSP_I444 (X264_CSP_MAX+1) /* yuv 4:4:4 planar */
+#define X264_CSP_BGR (X264_CSP_MAX+2) /* packed bgr 24bits */
+#define X264_CSP_BGRA (X264_CSP_MAX+3) /* packed bgr 32bits */
+#define X264_CSP_CLI_MAX (X264_CSP_MAX+4) /* end of list */
+#define X264_CSP_OTHER 0x2000 /* non x264 colorspace */
+
+typedef struct
+{
+ const char *name;
+ int planes;
+ float width[4];
+ float height[4];
+ int mod_width;
+ int mod_height;
+} x264_cli_csp_t;
+
+extern const x264_cli_csp_t x264_cli_csps[];
+
+int x264_cli_csp_is_invalid( int csp );
+int x264_cli_pic_alloc( cli_pic_t *pic, int csp, int width, int height );
+void x264_cli_pic_clean( cli_pic_t *pic );
+uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane );
+uint64_t x264_cli_pic_size( int csp, int width, int height );
+const x264_cli_csp_t *x264_cli_get_csp( int csp );
+
#endif
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "lavf", __VA_ARGS__ )
#undef DECLARE_ALIGNED
#include <libavformat/avformat.h>
-#include <libswscale/swscale.h>
+#include <libavutil/pixdesc.h>
typedef struct
{
int stream_id;
int next_frame;
int vfr_input;
- int vertical_flip;
- struct SwsContext *scaler;
- int pts_offset_flag;
- int64_t pts_offset;
- x264_picture_t *first_pic;
-
- int init_width;
- int init_height;
-
- int cur_width;
- int cur_height;
- enum PixelFormat cur_pix_fmt;
+ cli_pic_t *first_pic;
} lavf_hnd_t;
-typedef struct
-{
- AVFrame frame;
- AVPacket packet;
-} lavf_pic_t;
-
-static int check_swscale( lavf_hnd_t *h, AVCodecContext *c, int i_frame )
-{
- if( h->scaler && (h->cur_width == c->width) && (h->cur_height == c->height) && (h->cur_pix_fmt == c->pix_fmt) )
- return 0;
- if( h->scaler )
- {
- sws_freeContext( h->scaler );
- x264_cli_log( "lavf", X264_LOG_WARNING, "stream properties changed to %dx%d, %s at frame %d \n",
- c->width, c->height, avcodec_get_pix_fmt_name( c->pix_fmt ), i_frame );
- h->cur_width = c->width;
- h->cur_height = c->height;
- h->cur_pix_fmt = c->pix_fmt;
- }
- 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 );
- FAIL_IF_ERROR( !h->scaler, "could not open swscale context\n" )
- return 0;
-}
-
-static int read_frame_internal( x264_picture_t *p_pic, lavf_hnd_t *h, int i_frame, video_info_t *info )
+static int read_frame_internal( cli_pic_t *p_pic, lavf_hnd_t *h, int i_frame, video_info_t *info )
{
if( h->first_pic && !info )
{
* if so, retrieve the pts and image data before freeing it. */
if( !i_frame )
{
- XCHG( x264_image_t, p_pic->img, h->first_pic->img );
- p_pic->i_pts = h->first_pic->i_pts;
+ XCHG( cli_image_t, p_pic->img, h->first_pic->img );
+ p_pic->pts = h->first_pic->pts;
+ XCHG( void*, p_pic->opaque, h->first_pic->opaque );
}
+ lavf_input.release_frame( h->first_pic, NULL );
lavf_input.picture_clean( h->first_pic );
free( h->first_pic );
h->first_pic = NULL;
}
AVCodecContext *c = h->lavf->streams[h->stream_id]->codec;
- lavf_pic_t *pic_h = p_pic->opaque;
- AVPacket *pkt = &pic_h->packet;
- AVFrame *frame = &pic_h->frame;
+ AVPacket *pkt = p_pic->opaque;
+ AVFrame frame;
+ avcodec_get_frame_defaults( &frame );
while( i_frame >= h->next_frame )
{
if( pkt->stream_index == h->stream_id )
{
c->reordered_opaque = pkt->pts;
- if( avcodec_decode_video2( c, frame, &finished, pkt ) < 0 )
+ if( avcodec_decode_video2( c, &frame, &finished, pkt ) < 0 )
x264_cli_log( "lavf", X264_LOG_WARNING, "video decoding failed on frame %d\n", h->next_frame );
}
if( !finished )
{
- if( avcodec_decode_video2( c, frame, &finished, pkt ) < 0 )
+ if( avcodec_decode_video2( c, &frame, &finished, pkt ) < 0 )
x264_cli_log( "lavf", X264_LOG_WARNING, "video decoding failed on frame %d\n", h->next_frame );
if( !finished )
return -1;
h->next_frame++;
}
- if( check_swscale( h, c, i_frame ) )
- return -1;
- /* FIXME: avoid sws_scale where possible (no colorspace conversion). */
- sws_scale( h->scaler, frame->data, frame->linesize, 0, c->height, p_pic->img.plane, p_pic->img.i_stride );
+ memcpy( p_pic->img.stride, frame.linesize, sizeof(p_pic->img.stride) );
+ memcpy( p_pic->img.plane, frame.data, sizeof(p_pic->img.plane) );
+ p_pic->img.height = c->height;
+ p_pic->img.csp = c->pix_fmt | X264_CSP_OTHER;
+ p_pic->img.width = c->width;
if( info )
{
- info->interlaced = frame->interlaced_frame;
- info->tff = frame->top_field_first;
+ info->interlaced = frame.interlaced_frame;
+ info->tff = frame.top_field_first;
}
if( h->vfr_input )
{
- p_pic->i_pts = 0;
- if( frame->reordered_opaque != AV_NOPTS_VALUE )
- p_pic->i_pts = frame->reordered_opaque;
+ p_pic->pts = p_pic->duration = 0;
+ if( frame.reordered_opaque != AV_NOPTS_VALUE )
+ p_pic->pts = frame.reordered_opaque;
else if( pkt->dts != AV_NOPTS_VALUE )
- p_pic->i_pts = pkt->dts; // for AVI files
+ p_pic->pts = pkt->dts; // for AVI files
else if( info )
{
h->vfr_input = info->vfr = 0;
- goto exit;
- }
- if( !h->pts_offset_flag )
- {
- h->pts_offset = p_pic->i_pts;
- h->pts_offset_flag = 1;
+ return 0;
}
- p_pic->i_pts -= h->pts_offset;
}
-exit:
- if( pkt->destruct )
- pkt->destruct( pkt );
- avcodec_get_frame_defaults( frame );
return 0;
}
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
- lavf_hnd_t *h = malloc( sizeof(lavf_hnd_t) );
+ lavf_hnd_t *h = calloc( 1, sizeof(lavf_hnd_t) );
if( !h )
return -1;
av_register_all();
- h->scaler = NULL;
if( !strcmp( psz_filename, "-" ) )
psz_filename = "pipe:";
- FAIL_IF_ERROR( av_open_input_file( &h->lavf, psz_filename, NULL, 0, NULL ), "could not open input file\n" )
+ /* if resolution was passed in, parse it and colorspace into parameters. this allows raw video support */
+ AVFormatParameters *param = NULL;
+ if( opt->resolution )
+ {
+ param = calloc( 1, sizeof(AVFormatParameters) );
+ if( !param )
+ return -1;
+ sscanf( opt->resolution, "%dx%d", ¶m->width, ¶m->height );
+ param->pix_fmt = opt->colorspace ? av_get_pix_fmt( opt->colorspace ) : PIX_FMT_YUV420P;
+ }
+
+ FAIL_IF_ERROR( av_open_input_file( &h->lavf, psz_filename, NULL, 0, param ), "could not open input file\n" )
+ if( param )
+ free( param );
FAIL_IF_ERROR( av_find_stream_info( h->lavf ) < 0, "could not find input stream info\n" )
int i = 0;
FAIL_IF_ERROR( i == h->lavf->nb_streams, "could not find video stream\n" )
h->stream_id = i;
h->next_frame = 0;
- h->pts_offset_flag = 0;
- h->pts_offset = 0;
AVCodecContext *c = h->lavf->streams[i]->codec;
- h->init_width = h->cur_width = info->width = c->width;
- h->init_height = h->cur_height = info->height = c->height;
- h->cur_pix_fmt = c->pix_fmt;
info->fps_num = h->lavf->streams[i]->r_frame_rate.num;
info->fps_den = h->lavf->streams[i]->r_frame_rate.den;
info->timebase_num = h->lavf->streams[i]->time_base.num;
info->timebase_den = h->lavf->streams[i]->time_base.den;
+ /* lavf is thread unsafe as calling av_read_frame invalidates previously read AVPackets */
+ info->thread_safe = 0;
h->vfr_input = info->vfr;
- h->vertical_flip = 0;
-
- /* avisynth stores rgb data vertically flipped. */
- if( !strcasecmp( get_filename_extension( psz_filename ), "avs" ) &&
- (h->cur_pix_fmt == PIX_FMT_BGRA || h->cur_pix_fmt == PIX_FMT_BGR24) )
- info->csp |= X264_CSP_VFLIP;
-
- if( h->cur_pix_fmt != PIX_FMT_YUV420P )
- x264_cli_log( "lavf", X264_LOG_WARNING, "converting from %s to YV12\n",
- avcodec_get_pix_fmt_name( h->cur_pix_fmt ) );
FAIL_IF_ERROR( avcodec_open( c, avcodec_find_decoder( c->codec_id ) ),
"could not find decoder for video stream\n" )
/* prefetch the first frame and set/confirm flags */
- h->first_pic = malloc( sizeof(x264_picture_t) );
- FAIL_IF_ERROR( !h->first_pic || lavf_input.picture_alloc( h->first_pic, info->csp, info->width, info->height ),
+ h->first_pic = malloc( sizeof(cli_pic_t) );
+ FAIL_IF_ERROR( !h->first_pic || lavf_input.picture_alloc( h->first_pic, X264_CSP_OTHER, info->width, info->height ),
"malloc failed\n" )
else if( read_frame_internal( h->first_pic, h, 0, info ) )
return -1;
+ info->width = c->width;
+ info->height = c->height;
+ info->csp = h->first_pic->img.csp;
+ info->num_frames = 0; /* FIXME */
info->sar_height = c->sample_aspect_ratio.den;
info->sar_width = c->sample_aspect_ratio.num;
+
+ /* avisynth stores rgb data vertically flipped. */
+ if( !strcasecmp( get_filename_extension( psz_filename ), "avs" ) &&
+ (c->pix_fmt == PIX_FMT_BGRA || c->pix_fmt == PIX_FMT_BGR24) )
+ info->csp |= X264_CSP_VFLIP;
+
*p_handle = h;
return 0;
}
-static int picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
+static int picture_alloc( cli_pic_t *pic, int csp, int width, int height )
{
- if( x264_picture_alloc( pic, i_csp, i_width, i_height ) )
+ if( x264_cli_pic_alloc( pic, csp, width, height ) )
return -1;
- lavf_pic_t *pic_h = pic->opaque = malloc( sizeof(lavf_pic_t) );
- if( !pic_h )
+ pic->img.planes = 4;
+ pic->opaque = malloc( sizeof(AVPacket) );
+ if( !pic->opaque )
return -1;
- avcodec_get_frame_defaults( &pic_h->frame );
- av_init_packet( &pic_h->packet );
+ av_init_packet( pic->opaque );
return 0;
}
-/* FIXME */
-static int get_frame_total( hnd_t handle )
+static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
- return 0;
+ return read_frame_internal( pic, handle, i_frame, NULL );
}
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+static int release_frame( cli_pic_t *pic, hnd_t handle )
{
- return read_frame_internal( p_pic, handle, i_frame, NULL );
+ av_free_packet( pic->opaque );
+ av_init_packet( pic->opaque );
+ return 0;
}
-static void picture_clean( x264_picture_t *pic )
+static void picture_clean( cli_pic_t *pic )
{
free( pic->opaque );
- x264_picture_clean( pic );
+ memset( pic, 0, sizeof(cli_pic_t) );
}
static int close_file( hnd_t handle )
{
lavf_hnd_t *h = handle;
- sws_freeContext( h->scaler );
avcodec_close( h->lavf->streams[h->stream_id]->codec );
av_close_input_file( h->lavf );
free( h );
return 0;
}
-const cli_input_t lavf_input = { open_file, get_frame_total, picture_alloc, read_frame, NULL, picture_clean, close_file };
+const cli_input_t lavf_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };
/*****************************************************************************
- * yuv.c: x264 yuv input module
+ * raw.c: x264 raw input module
*****************************************************************************
- * Copyright (C) 2003-2009 x264 project
+ * Copyright (C) 2003-2010 x264 project
*
* 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
*****************************************************************************/
#include "input.h"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "raw", __VA_ARGS__ )
typedef struct
{
FILE *fh;
- int width, height;
int next_frame;
-} yuv_hnd_t;
+ uint64_t plane_size[4];
+ uint64_t frame_size;
+} raw_hnd_t;
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
- yuv_hnd_t *h = malloc( sizeof(yuv_hnd_t) );
+ raw_hnd_t *h = malloc( sizeof(raw_hnd_t) );
if( !h )
return -1;
}
else
sscanf( opt->resolution, "%ux%u", &info->width, &info->height );
- FAIL_IF_ERR( !info->width || !info->height, "yuv", "rawyuv input requires a resolution.\n" )
+ FAIL_IF_ERROR( !info->width || !info->height, "raw input requires a resolution.\n" )
+ if( opt->colorspace )
+ {
+ for( info->csp = X264_CSP_CLI_MAX-1; x264_cli_csps[info->csp].name && strcasecmp( x264_cli_csps[info->csp].name, opt->colorspace ); )
+ info->csp--;
+ FAIL_IF_ERROR( info->csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", opt->colorspace );
+ }
+ else /* default */
+ info->csp = X264_CSP_I420;
h->next_frame = 0;
info->vfr = 0;
- h->width = info->width;
- h->height = info->height;
if( !strcmp( psz_filename, "-" ) )
h->fh = stdin;
if( h->fh == NULL )
return -1;
- *p_handle = h;
- return 0;
-}
-
-static int get_frame_total( hnd_t handle )
-{
- yuv_hnd_t *h = handle;
- int i_frame_total = 0;
+ info->thread_safe = 1;
+ info->num_frames = 0;
+ h->frame_size = 0;
+ for( int i = 0; i < x264_cli_csps[info->csp].planes; i++ )
+ {
+ h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
+ h->frame_size += h->plane_size[i];
+ }
if( x264_is_regular_file( h->fh ) )
{
fseek( h->fh, 0, SEEK_END );
- uint64_t i_size = ftell( h->fh );
+ uint64_t size = ftell( h->fh );
fseek( h->fh, 0, SEEK_SET );
- i_frame_total = (int)(i_size / ( h->width * h->height * 3 / 2 ));
+ info->num_frames = size / h->frame_size;
}
- return i_frame_total;
+ *p_handle = h;
+ return 0;
}
-static int read_frame_internal( x264_picture_t *p_pic, yuv_hnd_t *h )
+static int read_frame_internal( cli_pic_t *pic, raw_hnd_t *h )
{
- return fread( p_pic->img.plane[0], h->width * h->height, 1, h->fh ) <= 0
- || fread( p_pic->img.plane[1], h->width * h->height / 4, 1, h->fh ) <= 0
- || fread( p_pic->img.plane[2], h->width * h->height / 4, 1, h->fh ) <= 0;
+ int error = 0;
+ for( int i = 0; i < pic->img.planes && !error; i++ )
+ error |= fread( pic->img.plane[i], h->plane_size[i], 1, h->fh ) <= 0;
+ return error;
}
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
- yuv_hnd_t *h = handle;
+ raw_hnd_t *h = handle;
if( i_frame > h->next_frame )
{
if( x264_is_regular_file( h->fh ) )
- fseek( h->fh, (uint64_t)i_frame * h->width * h->height * 3 / 2, SEEK_SET );
+ fseek( h->fh, i_frame * h->frame_size, SEEK_SET );
else
while( i_frame > h->next_frame )
{
- if( read_frame_internal( p_pic, h ) )
+ if( read_frame_internal( pic, h ) )
return -1;
h->next_frame++;
}
}
- if( read_frame_internal( p_pic, h ) )
+ if( read_frame_internal( pic, h ) )
return -1;
h->next_frame = i_frame+1;
static int close_file( hnd_t handle )
{
- yuv_hnd_t *h = handle;
+ raw_hnd_t *h = handle;
if( !h || !h->fh )
return 0;
fclose( h->fh );
return 0;
}
-const cli_input_t yuv_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
+const cli_input_t raw_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };
#include "input.h"
-extern cli_input_t input;
-
typedef struct
{
cli_input_t input;
hnd_t p_handle;
- x264_picture_t pic;
+ cli_pic_t pic;
x264_threadpool_t *pool;
int next_frame;
int frame_total;
typedef struct thread_input_arg_t
{
thread_hnd_t *h;
- x264_picture_t *pic;
+ cli_pic_t *pic;
int i_frame;
int status;
} thread_input_arg_t;
return -1;
h->next_args->h = h;
h->next_args->status = 0;
- h->frame_total = input.get_frame_total( h->p_handle );
+ h->frame_total = info->num_frames;
thread_input.picture_alloc = h->input.picture_alloc;
thread_input.picture_clean = h->input.picture_clean;
return 0;
}
-static int get_frame_total( hnd_t handle )
-{
- thread_hnd_t *h = handle;
- return h->frame_total;
-}
-
static void read_frame_thread_int( thread_input_arg_t *i )
{
i->status = i->h->input.read_frame( i->pic, i->h->p_handle, i->i_frame );
}
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+static int read_frame( cli_pic_t *p_pic, hnd_t handle, int i_frame )
{
thread_hnd_t *h = handle;
int ret = 0;
}
if( h->next_frame == i_frame )
- XCHG( x264_picture_t, *p_pic, h->pic );
+ XCHG( cli_pic_t, *p_pic, h->pic );
else
ret |= h->input.read_frame( p_pic, h->p_handle, i_frame );
return ret;
}
-static int release_frame( x264_picture_t *pic, hnd_t handle )
+static int release_frame( cli_pic_t *pic, hnd_t handle )
{
thread_hnd_t *h = handle;
if( h->input.release_frame )
return 0;
}
-cli_input_t thread_input = { open_file, get_frame_total, NULL, read_frame, release_frame, NULL, close_file };
+cli_input_t thread_input = { open_file, NULL, read_frame, release_frame, NULL, close_file };
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "timecode", __VA_ARGS__ )
#include <math.h>
-extern cli_input_t input;
-
typedef struct
{
cli_input_t input;
hnd_t p_handle;
- int frame_total;
int auto_timebase_num;
int auto_timebase_den;
uint64_t timebase_num;
uint64_t timebase_den;
- int seek;
int stored_pts_num;
int64_t *pts;
double assume_fps;
{
char buff[256];
int ret, tcfv, num, seq_num, timecodes_num;
- int64_t pts_seek_offset;
double *timecodes = NULL;
double *fpss = NULL;
{
uint64_t file_pos;
double assume_fps, seq_fps;
- int start, end = h->seek;
+ int start, end;
int prev_start = -1, prev_end = -1;
h->assume_fps = 0;
if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' )
{
if( sscanf( buff, "# TDecimate Mode 3: Last Frame = %d", &end ) == 1 )
- h->stored_pts_num = end + 1 - h->seek;
+ h->stored_pts_num = end + 1;
continue;
}
ret = sscanf( buff, "%d,%d,%lf", &start, &end, &seq_fps );
++seq_num;
}
if( !h->stored_pts_num )
- h->stored_pts_num = end + 1 - h->seek;
- timecodes_num = h->stored_pts_num + h->seek;
+ h->stored_pts_num = end + 1;
+ timecodes_num = h->stored_pts_num;
fseek( tcfile_in, file_pos, SEEK_SET );
timecodes = malloc( timecodes_num * sizeof(double) );
{
uint64_t file_pos = ftell( tcfile_in );
- num = h->stored_pts_num = 0;
+ h->stored_pts_num = 0;
while( fgets( buff, sizeof(buff), tcfile_in ) != NULL )
{
if( buff[0] == '#' || buff[0] == '\n' || buff[0] == '\r' )
{
- if( !num )
+ if( !h->stored_pts_num )
file_pos = ftell( tcfile_in );
continue;
}
- if( num >= h->seek )
- ++h->stored_pts_num;
- ++num;
+ h->stored_pts_num++;
}
- timecodes_num = h->stored_pts_num + h->seek;
+ timecodes_num = h->stored_pts_num;
FAIL_IF_ERROR( !timecodes_num, "input tcfile doesn't have any timecodes!\n" )
fseek( tcfile_in, file_pos, SEEK_SET );
h->pts = malloc( h->stored_pts_num * sizeof(int64_t) );
if( !h->pts )
goto fail;
- pts_seek_offset = (int64_t)( timecodes[h->seek] * ((double)h->timebase_den / h->timebase_num) + 0.5 );
h->pts[0] = 0;
for( num = 1; num < h->stored_pts_num; num++ )
{
- h->pts[num] = (int64_t)( timecodes[h->seek + num] * ((double)h->timebase_den / h->timebase_num) + 0.5 );
- h->pts[num] -= pts_seek_offset;
+ h->pts[num] = timecodes[num] * ((double)h->timebase_den / h->timebase_num) + 0.5;
FAIL_IF_ERROR( h->pts[num] <= h->pts[num - 1], "invalid timebase or timecode for frame %d\n", num )
}
FAIL_IF_ERROR( !h, "malloc failed\n" )
h->input = input;
h->p_handle = *p_handle;
- h->frame_total = input.get_frame_total( h->p_handle );
- h->seek = opt->seek;
if( opt->timebase )
{
ret = sscanf( opt->timebase, "%"SCNu64"/%"SCNu64, &h->timebase_num, &h->timebase_den );
return 0;
}
-static int get_frame_total( hnd_t handle )
+static int64_t get_frame_pts( timecode_hnd_t *h, int frame, int real_frame )
{
- timecode_hnd_t *h = handle;
- return h->frame_total;
-}
-
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
-{
- timecode_hnd_t *h = handle;
- int ret = h->input.read_frame( p_pic, h->p_handle, i_frame );
-
- if( i_frame - h->seek < h->stored_pts_num )
- {
- assert( i_frame >= h->seek );
- p_pic->i_pts = h->pts[i_frame - h->seek];
- }
+ if( frame < h->stored_pts_num )
+ return h->pts[frame];
else
{
- if( h->pts )
+ if( h->pts && real_frame )
{
x264_cli_log( "timecode", X264_LOG_INFO, "input timecode file missing data for frame %d and later\n"
- " assuming constant fps %.6f\n", i_frame, h->assume_fps );
+ " assuming constant fps %.6f\n", frame, h->assume_fps );
free( h->pts );
h->pts = NULL;
}
- h->last_timecode += 1 / h->assume_fps;
- p_pic->i_pts = (int64_t)( h->last_timecode * ((double)h->timebase_den / h->timebase_num) + 0.5 );
+ double timecode = h->last_timecode + 1 / h->assume_fps;
+ if( real_frame )
+ h->last_timecode = timecode;
+ return timecode * ((double)h->timebase_den / h->timebase_num) + 0.5;
}
+}
+
+static int read_frame( cli_pic_t *pic, hnd_t handle, int frame )
+{
+ timecode_hnd_t *h = handle;
+ int ret = h->input.read_frame( pic, h->p_handle, frame );
+
+ pic->pts = get_frame_pts( h, frame, 1 );
+ pic->duration = get_frame_pts( h, frame + 1, 0 ) - pic->pts;
return ret;
}
-static int release_frame( x264_picture_t *pic, hnd_t handle )
+static int release_frame( cli_pic_t *pic, hnd_t handle )
{
timecode_hnd_t *h = handle;
if( h->input.release_frame )
return 0;
}
-cli_input_t timecode_input = { open_file, get_frame_total, NULL, read_frame, release_frame, NULL, close_file };
+cli_input_t timecode_input = { open_file, NULL, read_frame, release_frame, NULL, close_file };
typedef struct
{
FILE *fh;
- int width, height;
int next_frame;
- int seq_header_len, frame_header_len;
- int frame_size;
+ int seq_header_len;
+ int frame_header_len;
+ uint64_t frame_size;
+ uint64_t plane_size[3];
} y4m_hnd_t;
#define Y4M_MAGIC "YUV4MPEG2"
#define Y4M_FRAME_MAGIC "FRAME"
#define MAX_FRAME_HEADER 80
+static int csp_string_to_int( char *csp_name )
+{
+ int csp = X264_CSP_MAX;
+ if( !strncmp( "420", csp_name, 3 ) )
+ csp = X264_CSP_I420;
+ else if( !strncmp( "422", csp_name, 3 ) )
+ csp = X264_CSP_I422;
+ else if( !strncmp( "444", csp_name, 3 ) && strncmp( "444alpha", csp_name, 8 ) ) // only accept alphaless 4:4:4
+ csp = X264_CSP_I444;
+ return csp;
+}
+
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
y4m_hnd_t *h = malloc( sizeof(y4m_hnd_t) );
switch( *tokstart++ )
{
case 'W': /* Width. Required. */
- h->width = info->width = strtol( tokstart, &tokend, 10 );
+ info->width = strtol( tokstart, &tokend, 10 );
tokstart=tokend;
break;
case 'H': /* Height. Required. */
- h->height = info->height = strtol( tokstart, &tokend, 10 );
+ info->height = strtol( tokstart, &tokend, 10 );
tokstart=tokend;
break;
case 'C': /* Color space */
- if( !strncmp( "420", tokstart, 3 ) )
- colorspace = X264_CSP_I420;
- else
- colorspace = X264_CSP_MAX; ///< anything other than 420 since we don't handle it
+ colorspace = csp_string_to_int( tokstart );
tokstart = strchr( tokstart, 0x20 );
break;
case 'I': /* Interlace type */
{
/* Older nonstandard pixel format representation */
tokstart += 6;
- if( !strncmp( "420",tokstart, 3 ) )
- alt_colorspace = X264_CSP_I420;
- else
- alt_colorspace = X264_CSP_MAX;
+ alt_colorspace = csp_string_to_int( tokstart );
}
tokstart = strchr( tokstart, 0x20 );
break;
if( colorspace == X264_CSP_NONE )
colorspace = X264_CSP_I420;
- FAIL_IF_ERROR( colorspace != X264_CSP_I420, "colorspace unhandled\n" )
-
- *p_handle = h;
- return 0;
-}
+ FAIL_IF_ERROR( colorspace <= X264_CSP_NONE && colorspace >= X264_CSP_MAX, "colorspace unhandled\n" )
-/* Most common case: frame_header = "FRAME" */
-static int get_frame_total( hnd_t handle )
-{
- y4m_hnd_t *h = handle;
- int i_frame_total = 0;
+ info->thread_safe = 1;
+ info->num_frames = 0;
+ info->csp = colorspace;
+ h->frame_size = h->frame_header_len;
+ for( i = 0; i < x264_cli_csps[info->csp].planes; i++ )
+ {
+ h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
+ h->frame_size += h->plane_size[i];
+ }
+ /* Most common case: frame_header = "FRAME" */
if( x264_is_regular_file( h->fh ) )
{
uint64_t init_pos = ftell( h->fh );
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));
+ info->num_frames = (i_size - h->seq_header_len) / h->frame_size;
}
- return i_frame_total;
+ *p_handle = h;
+ return 0;
}
-static int read_frame_internal( x264_picture_t *p_pic, y4m_hnd_t *h )
+static int read_frame_internal( cli_pic_t *pic, y4m_hnd_t *h )
{
int slen = strlen( Y4M_FRAME_MAGIC );
int i = 0;
while( i < MAX_FRAME_HEADER && fgetc( h->fh ) != '\n' )
i++;
FAIL_IF_ERROR( i == MAX_FRAME_HEADER, "bad frame header!\n" )
+ h->frame_size = h->frame_size - h->frame_header_len + i+slen+1;
h->frame_header_len = i+slen+1;
- if( fread( p_pic->img.plane[0], h->width * h->height, 1, h->fh ) <= 0
- || fread( p_pic->img.plane[1], h->width * h->height / 4, 1, h->fh ) <= 0
- || fread( p_pic->img.plane[2], h->width * h->height / 4, 1, h->fh ) <= 0 )
- return -1;
-
- return 0;
+ int error = 0;
+ for( i = 0; i < pic->img.planes && !error; i++ )
+ error |= fread( pic->img.plane[i], h->plane_size[i], 1, h->fh ) <= 0;
+ return error;
}
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
y4m_hnd_t *h = handle;
if( i_frame > h->next_frame )
{
if( x264_is_regular_file( h->fh ) )
- fseek( h->fh, (uint64_t)i_frame*(3*(h->width*h->height)/2+h->frame_header_len)
- + h->seq_header_len, SEEK_SET );
+ fseek( h->fh, h->frame_size * i_frame + h->seq_header_len, SEEK_SET );
else
while( i_frame > h->next_frame )
{
- if( read_frame_internal( p_pic, h ) )
+ if( read_frame_internal( pic, h ) )
return -1;
h->next_frame++;
}
}
- if( read_frame_internal( p_pic, h ) )
+ if( read_frame_internal( pic, h ) )
return -1;
h->next_frame = i_frame+1;
return 0;
}
-const cli_input_t y4m_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
+const cli_input_t y4m_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };
#include "x264cli.h"
#include "input/input.h"
#include "output/output.h"
+#include "filters/filters.h"
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "x264", __VA_ARGS__ )
#define SetConsoleTitle(t)
#endif
+#if HAVE_LAVF
+#include <libavutil/pixfmt.h>
+#include <libavutil/pixdesc.h>
+#endif
+
/* Ctrl-C handler */
static int b_ctrl_c = 0;
static int b_exit_on_ctrl_c = 0;
int i_pulldown;
} cli_opt_t;
-/* i/o file operation function pointer structs */
+/* file i/o operation structs */
cli_input_t input;
static cli_output_t output;
+/* video filter operation struct */
+static cli_vid_filter_t filter;
+
static const char * const demuxer_names[] =
{
"auto",
- "yuv",
+ "raw",
"y4m",
#if HAVE_AVS
"avs",
return buf;
}
+static void print_csp_names( int longhelp )
+{
+ if( longhelp < 2 )
+ return;
+# define INDENT " "
+ printf( " - valid csps for `raw' demuxer:\n" );
+ printf( INDENT );
+ for( int i = X264_CSP_NONE+1; i < X264_CSP_CLI_MAX; i++ )
+ {
+ printf( "%s", x264_cli_csps[i].name );
+ if( i+1 < X264_CSP_CLI_MAX )
+ printf( ", " );
+ }
+#if HAVE_LAVF
+ printf( "\n" );
+ printf( " - valid csps for `lavf' demuxer:\n" );
+ printf( INDENT );
+ int line_len = strlen( INDENT );
+ for( enum PixelFormat i = PIX_FMT_NONE+1; i < PIX_FMT_NB; i++ )
+ {
+ const char *pfname = av_pix_fmt_descriptors[i].name;
+ int name_len = strlen( pfname );
+ if( line_len + name_len > (80 - strlen( ", " )) )
+ {
+ printf( "\n" INDENT );
+ line_len = strlen( INDENT );
+ }
+ printf( "%s", pfname );
+ line_len += name_len;
+ if( i+1 < PIX_FMT_NB )
+ {
+ printf( ", " );
+ line_len += 2;
+ }
+ }
+#endif
+ printf( "\n" );
+}
+
/*****************************************************************************
* Help:
*****************************************************************************/
#define H1 if(longhelp>=1) printf
#define H2 if(longhelp==2) printf
H0( "x264 core:%d%s\n"
- "Syntax: x264 [options] -o outfile infile [widthxheight]\n"
+ "Syntax: x264 [options] -o outfile infile\n"
"\n"
- "Infile can be raw YUV 4:2:0 (in which case resolution is required),\n"
- " or YUV4MPEG 4:2:0 (*.y4m),\n"
+ "Infile can be raw (in which case resolution is required),\n"
+ " or YUV4MPEG (*.y4m),\n"
" or Avisynth if compiled with support (%s).\n"
" or libav* formats if compiled with lavf support (%s) or ffms support (%s).\n"
"Outfile type is selected by filename:\n"
" - %s\n", muxer_names[0], stringify_names( buf, muxer_names ) );
H1( " --demuxer <string> Specify input container format [\"%s\"]\n"
" - %s\n", demuxer_names[0], stringify_names( buf, demuxer_names ) );
+ H1( " --input-csp <string> Specify input colorspace format for raw input\n" );
+ print_csp_names( longhelp );
+ H1( " --input-res <intxint> Specify input resolution (width x height)\n" );
H1( " --index <string> Filename for input index file\n" );
H0( " --sar width:height Specify Sample Aspect Ratio\n" );
H0( " --fps <float|rational> Specify framerate\n" );
" <integer> Specify timebase numerator for input timecode file\n"
" or specify timebase denominator for other input\n" );
H0( "\n" );
+ H0( "Filtering:\n" );
+ H0( "\n" );
+ H0( "--vf, --video-filter <filter0>/<filter1>/... Apply video filtering to the input file\n" );
+ H0( " Available filters:\n" );
+ x264_register_vid_filters();
+ x264_vid_filter_help( longhelp );
+ H0( "\n" );
}
enum {
OPT_TCFILE_OUT,
OPT_TIMEBASE,
OPT_PULLDOWN,
- OPT_LOG_LEVEL
+ OPT_LOG_LEVEL,
+ OPT_VIDEO_FILTER,
+ OPT_INPUT_RES,
+ OPT_INPUT_CSP
} OptionsOPT;
static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw";
{ "nal-hrd", required_argument, NULL, 0 },
{ "pulldown", required_argument, NULL, OPT_PULLDOWN },
{ "fake-interlaced", no_argument, NULL, 0 },
+ { "vf", required_argument, NULL, OPT_VIDEO_FILTER },
+ { "video-filter", required_argument, NULL, OPT_VIDEO_FILTER },
+ { "input-res", required_argument, NULL, OPT_INPUT_RES },
+ { "input-csp", required_argument, NULL, OPT_INPUT_CSP },
{0, 0, 0, 0}
};
int b_regular = strcmp( filename, "-" );
int b_auto = !strcasecmp( demuxer, "auto" );
if( !b_regular && b_auto )
- ext = "yuv";
+ ext = "raw";
b_regular = b_regular && x264_is_regular_file_path( filename );
if( b_regular )
{
}
else if( !strcasecmp( module, "y4m" ) )
input = y4m_input;
- else if( !strcasecmp( module, "yuv" ) )
- input = yuv_input;
+ else if( !strcasecmp( module, "raw" ) || !strcasecmp( ext, "yuv" ) )
+ input = raw_input;
else
{
#if HAVE_FFMS
input = avs_input;
}
#endif
- if( b_auto && !yuv_input.open_file( filename, p_handle, info, opt ) )
+ if( b_auto && !raw_input.open_file( filename, p_handle, info, opt ) )
{
- module = "yuv";
+ module = "raw";
b_auto = 0;
- input = yuv_input;
+ input = raw_input;
}
FAIL_IF_ERROR( !(*p_handle), "could not open input file `%s' via any method!\n", filename )
return 0;
}
+static int init_vid_filters( char *sequence, hnd_t *handle, video_info_t *info, x264_param_t *param )
+{
+ x264_register_vid_filters();
+
+ /* intialize baseline filters */
+ if( x264_init_vid_filter( "source", handle, &filter, info, param, NULL ) ) /* wrap demuxer into a filter */
+ return -1;
+ if( x264_init_vid_filter( "resize", handle, &filter, info, param, "normcsp" ) ) /* normalize csps to be of a known/supported format */
+ return -1;
+ if( x264_init_vid_filter( "fix_vfr_pts", handle, &filter, info, param, NULL ) ) /* fix vfr pts */
+ return -1;
+
+ /* parse filter chain */
+ for( char *p = sequence; p && *p; )
+ {
+ int tok_len = strcspn( p, "/" );
+ int p_len = strlen( p );
+ p[tok_len] = 0;
+ int name_len = strcspn( p, ":" );
+ p[name_len] = 0;
+ name_len += name_len != tok_len;
+ if( x264_init_vid_filter( p, handle, &filter, info, param, p + name_len ) )
+ return -1;
+ p += X264_MIN( tok_len+1, p_len );
+ }
+
+ /* force end result resolution */
+ if( !param->i_width && !param->i_height )
+ {
+ param->i_height = info->height;
+ param->i_width = info->width;
+ }
+ /* if the current csp is supported by libx264, have libx264 use this csp.
+ * otherwise change the csp to I420 and have libx264 use this.
+ * when more colorspaces are supported, this decision will need to be updated. */
+ int csp = info->csp & X264_CSP_MASK;
+ if( csp > X264_CSP_NONE && csp < X264_CSP_MAX )
+ param->i_csp = info->csp;
+ else
+ param->i_csp = X264_CSP_I420;
+ if( x264_init_vid_filter( "resize", handle, &filter, info, param, NULL ) )
+ return -1;
+
+ return 0;
+}
+
static int parse_enum_name( const char *arg, const char * const *names, const char **dst )
{
for( int i = 0; names[i]; i++ )
char *tcfile_name = NULL;
x264_param_t defaults;
char *profile = NULL;
+ char *vid_filters = NULL;
int b_thread_input = 0;
int b_turbo = 1;
int b_user_ref = 0;
case OPT_PULLDOWN:
FAIL_IF_ERROR( parse_enum_value( optarg, pulldown_names, &opt->i_pulldown ), "Unknown pulldown `%s'\n", optarg )
break;
+ case OPT_VIDEO_FILTER:
+ vid_filters = optarg;
+ break;
+ case OPT_INPUT_RES:
+ input_opt.resolution = optarg;
+ break;
+ case OPT_INPUT_CSP:
+ input_opt.colorspace = optarg;
+ break;
default:
generic_option:
{
FAIL_IF_ERROR( output.open_file( output_filename, &opt->hout ), "could not open output file `%s'\n", output_filename )
input_filename = argv[optind++];
- input_opt.resolution = optind < argc ? argv[optind++] : NULL;
video_info_t info = {0};
char demuxername[5];
}
else FAIL_IF_ERROR( !info.vfr && input_opt.timebase, "--timebase is incompatible with cfr input\n" )
- /* set param flags from the info flags as necessary */
- param->i_csp = info.csp;
- param->i_height = info.height;
- param->b_vfr_input = info.vfr;
- param->i_width = info.width;
- if( !b_user_interlaced && info.interlaced )
+ /* init threaded input while the information about the input video is unaltered by filtering */
+#if HAVE_PTHREAD
+ if( info.thread_safe && (b_thread_input || param->i_threads > 1
+ || (param->i_threads == X264_THREADS_AUTO && x264_cpu_num_processors() > 1)) )
{
- x264_cli_log( "x264", X264_LOG_WARNING, "input appears to be interlaced, enabling %cff interlaced mode.\n"
- " If you want otherwise, use --no-interlaced or --%cff\n",
- info.tff ? 't' : 'b', info.tff ? 'b' : 't' );
- param->b_interlaced = 1;
- param->b_tff = !!info.tff;
+ if( thread_input.open_file( NULL, &opt->hin, &info, NULL ) )
+ {
+ fprintf( stderr, "x264 [error]: threaded input failed\n" );
+ return -1;
+ }
+ input = thread_input;
}
- if( !b_user_fps )
+#endif
+
+ /* override detected values by those specified by the user */
+ if( param->vui.i_sar_width && param->vui.i_sar_height )
{
- param->i_fps_num = info.fps_num;
- param->i_fps_den = info.fps_den;
+ info.sar_width = param->vui.i_sar_width;
+ info.sar_height = param->vui.i_sar_height;
}
- if( param->b_vfr_input )
+ if( b_user_fps )
{
- param->i_timebase_num = info.timebase_num;
- param->i_timebase_den = info.timebase_den;
+ info.fps_num = param->i_fps_num;
+ info.fps_den = param->i_fps_den;
}
- else
+ if( !info.vfr )
{
- param->i_timebase_num = param->i_fps_den;
- param->i_timebase_den = param->i_fps_num;
+ info.timebase_num = info.fps_den;
+ info.timebase_den = info.fps_num;
}
if( !tcfile_name && input_opt.timebase )
{
FAIL_IF_ERROR( !ret, "invalid argument: timebase = %s\n", input_opt.timebase )
else if( ret == 1 )
{
- i_user_timebase_num = param->i_timebase_num;
+ i_user_timebase_num = info.timebase_num;
i_user_timebase_den = strtoul( input_opt.timebase, NULL, 10 );
}
FAIL_IF_ERROR( i_user_timebase_num > UINT32_MAX || i_user_timebase_den > UINT32_MAX,
"timebase you specified exceeds H.264 maximum\n" )
- opt->timebase_convert_multiplier = ((double)i_user_timebase_den / param->i_timebase_den)
- * ((double)param->i_timebase_num / i_user_timebase_num);
- param->i_timebase_num = i_user_timebase_num;
- param->i_timebase_den = i_user_timebase_den;
- param->b_vfr_input = 1;
+ opt->timebase_convert_multiplier = ((double)i_user_timebase_den / info.timebase_den)
+ * ((double)info.timebase_num / i_user_timebase_num);
+ info.timebase_num = i_user_timebase_num;
+ info.timebase_den = i_user_timebase_den;
+ info.vfr = 1;
}
- if( !param->vui.i_sar_width || !param->vui.i_sar_height )
+ if( b_user_interlaced )
{
- param->vui.i_sar_width = info.sar_width;
- param->vui.i_sar_height = info.sar_height;
+ info.interlaced = param->b_interlaced;
+ info.tff = param->b_tff;
}
-#if HAVE_PTHREAD
- if( b_thread_input || param->i_threads > 1
- || (param->i_threads == X264_THREADS_AUTO && x264_cpu_num_processors() > 1) )
+ if( init_vid_filters( vid_filters, &opt->hin, &info, param ) )
+ return -1;
+
+ /* set param flags from the post-filtered video */
+ param->b_vfr_input = info.vfr;
+ param->i_fps_num = info.fps_num;
+ param->i_fps_den = info.fps_den;
+ param->i_timebase_num = info.timebase_num;
+ param->i_timebase_den = info.timebase_den;
+ param->vui.i_sar_width = info.sar_width;
+ param->vui.i_sar_height = info.sar_height;
+
+ info.num_frames = X264_MAX( info.num_frames - opt->i_seek, 0 );
+ if( (!info.num_frames || param->i_frame_total < info.num_frames)
+ && param->i_frame_total > 0 )
+ info.num_frames = param->i_frame_total;
+ param->i_frame_total = info.num_frames;
+
+ if( !b_user_interlaced && info.interlaced )
{
- FAIL_IF_ERROR( thread_input.open_file( NULL, &opt->hin, &info, NULL ), "threaded input failed\n" )
- input = thread_input;
+ x264_cli_log( "x264", X264_LOG_WARNING, "input appears to be interlaced, enabling %cff interlaced mode.\n"
+ " If you want otherwise, use --no-interlaced or --%cff\n",
+ info.tff ? 't' : 'b', info.tff ? 'b' : 't' );
+ param->b_interlaced = 1;
+ param->b_tff = !!info.tff;
}
-#endif
-
/* Automatically reduce reference frame count to match the user's target level
* if the user didn't explicitly set a reference frame count. */
fflush( stderr ); // needed in windows
}
+static void Convert_cli_to_lib_pic( x264_picture_t *lib, cli_pic_t *cli )
+{
+ memcpy( lib->img.i_stride, cli->img.stride, sizeof(cli->img.stride) );
+ memcpy( lib->img.plane, cli->img.plane, sizeof(cli->img.plane) );
+ lib->img.i_plane = cli->img.planes;
+ lib->img.i_csp = cli->img.csp;
+ lib->i_pts = cli->pts;
+}
+
static int Encode( x264_param_t *param, cli_opt_t *opt )
{
x264_t *h;
x264_picture_t pic;
+ cli_pic_t cli_pic;
const cli_pulldown_t *pulldown = NULL; // shut up gcc
- int i_frame, i_frame_total, i_frame_output;
+ int i_frame, i_frame_output;
int64_t i_start, i_end;
int64_t i_file = 0;
int i_frame_size;
double pulldown_pts = 0;
opt->b_progress &= param->i_log_level < X264_LOG_DEBUG;
- i_frame_total = input.get_frame_total( opt->hin );
- i_frame_total = X264_MAX( i_frame_total - opt->i_seek, 0 );
- if( ( i_frame_total == 0 || param->i_frame_total < i_frame_total )
- && param->i_frame_total > 0 )
- i_frame_total = param->i_frame_total;
- param->i_frame_total = i_frame_total;
- i_update_interval = i_frame_total ? x264_clip3( i_frame_total / 1000, 1, 10 ) : 10;
+ i_update_interval = param->i_frame_total ? x264_clip3( param->i_frame_total / 1000, 1, 10 ) : 10;
+ x264_picture_init( &pic );
/* set up pulldown */
if( opt->i_pulldown && !param->b_vfr_input )
if( ( h = x264_encoder_open( param ) ) == NULL )
{
x264_cli_log( "x264", X264_LOG_ERROR, "x264_encoder_open failed\n" );
- input.close_file( opt->hin );
+ filter.free( opt->hin );
return -1;
}
if( output.set_param( opt->hout, param ) )
{
x264_cli_log( "x264", X264_LOG_ERROR, "can't set outfile param\n" );
- input.close_file( opt->hin );
+ filter.free( opt->hin );
output.close_file( opt->hout, largest_pts, second_largest_pts );
return -1;
}
- /* Create a new pic */
- FAIL_IF_ERROR( input.picture_alloc( &pic, param->i_csp, param->i_width, param->i_height ), "malloc failed\n" )
-
i_start = x264_mdate();
/* ticks/frame = ticks/second / frames/second */
ticks_per_frame = (int64_t)param->i_timebase_den * param->i_fps_den / param->i_timebase_num / param->i_fps_num;
fprintf( opt->tcfile_out, "# timecode format v2\n" );
/* Encode frames */
- for( i_frame = 0, i_frame_output = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); )
+ for( i_frame = 0, i_frame_output = 0; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ )
{
- if( input.read_frame( &pic, opt->hin, i_frame + opt->i_seek ) )
+ if( filter.get_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
break;
+ Convert_cli_to_lib_pic( &pic, &cli_pic );
if( !param->b_vfr_input )
pic.i_pts = i_frame;
first_dts = prev_dts = last_dts;
}
- i_frame++;
-
- if( input.release_frame && input.release_frame( &pic, opt->hin ) )
+ if( filter.release_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
break;
/* update status line (up to 1000 times per input file) */
if( opt->b_progress && i_frame_output % i_update_interval == 0 && i_frame_output )
- Print_status( i_start, i_frame_output, i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
+ Print_status( i_start, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
}
/* Flush delayed frames */
while( !b_ctrl_c && x264_encoder_delayed_frames( h ) )
first_dts = prev_dts = last_dts;
}
if( opt->b_progress && i_frame_output % i_update_interval == 0 && i_frame_output )
- Print_status( i_start, i_frame_output, i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
+ Print_status( i_start, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
}
if( pts_warning_cnt >= MAX_PTS_WARNING && cli_log_level < X264_LOG_DEBUG )
x264_cli_log( "x264", X264_LOG_WARNING, "%d suppressed nonmonotonic pts warnings\n", pts_warning_cnt-MAX_PTS_WARNING );
duration *= dts_compress_multiplier;
i_end = x264_mdate();
- input.picture_clean( &pic );
/* Erase progress indicator before printing encoding stats. */
if( opt->b_progress )
fprintf( stderr, " \r" );
opt->tcfile_out = NULL;
}
- input.close_file( opt->hin );
+ filter.free( opt->hin );
output.close_file( opt->hout, largest_pts, second_largest_pts );
if( i_frame_output > 0 )
#include <stdarg.h>
-#define X264_BUILD 102
+#define X264_BUILD 103
/* x264_t:
* opaque handler for encoder */
#define X264_CSP_MASK 0x00ff /* */
#define X264_CSP_NONE 0x0000 /* Invalid mode */
#define X264_CSP_I420 0x0001 /* yuv 4:2:0 planar */
-#define X264_CSP_I422 0x0002 /* yuv 4:2:2 planar */
-#define X264_CSP_I444 0x0003 /* yuv 4:4:4 planar */
-#define X264_CSP_YV12 0x0004 /* yuv 4:2:0 planar */
-#define X264_CSP_YUYV 0x0005 /* yuv 4:2:2 packed */
-#define X264_CSP_RGB 0x0006 /* rgb 24bits */
-#define X264_CSP_BGR 0x0007 /* bgr 24bits */
-#define X264_CSP_BGRA 0x0008 /* bgr 32bits */
-#define X264_CSP_MAX 0x0009 /* end of list */
+#define X264_CSP_YV12 0x0002 /* yvu 4:2:0 planar */
+#define X264_CSP_MAX 0x0003 /* end of list */
#define X264_CSP_VFLIP 0x1000 /* */
/* Slice type */
void x264_cli_log( const char *name, int i_level, const char *fmt, ... );
void x264_cli_printf( int i_level, const char *fmt, ... );
-#define FAIL_IF_ERR( cond, name, ... )\
+#define RETURN_IF_ERR( cond, name, ret, ... )\
if( cond )\
{\
x264_cli_log( name, X264_LOG_ERROR, __VA_ARGS__ );\
- return -1;\
+ return ret;\
}
+#define FAIL_IF_ERR( cond, name, ... ) RETURN_IF_ERR( cond, name, -1, __VA_ARGS__ )
+
#endif