Add video filtering system to x264cli
authorSteven Walters <kemuri9@gmail.com>
Mon, 5 Jul 2010 21:37:47 +0000 (17:37 -0400)
committerFiona Glaser <fiona@x264.com>
Thu, 15 Jul 2010 01:50:34 +0000 (18:50 -0700)
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.

29 files changed:
Makefile
common/frame.c
configure
encoder/encoder.c
filters/filters.c [new file with mode: 0644]
filters/filters.h [new file with mode: 0644]
filters/video/cache.c [new file with mode: 0644]
filters/video/crop.c [new file with mode: 0644]
filters/video/fix_vfr_pts.c [new file with mode: 0644]
filters/video/internal.c [new file with mode: 0644]
filters/video/internal.h [new file with mode: 0644]
filters/video/resize.c [new file with mode: 0644]
filters/video/select_every.c [new file with mode: 0644]
filters/video/source.c [new file with mode: 0644]
filters/video/video.c [new file with mode: 0644]
filters/video/video.h [new file with mode: 0644]
input/avs.c
input/ffms.c
input/input.c [new file with mode: 0644]
input/input.h
input/lavf.c
input/raw.c [new file with mode: 0644]
input/thread.c
input/timecode.c
input/y4m.c
input/yuv.c [deleted file]
x264.c
x264.h
x264cli.h

index 9837821..0f4d0cf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,10 +13,12 @@ SRCS = common/mc.c common/predict.c common/pixel.c common/macroblock.c \
        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 =
 
@@ -129,7 +131,7 @@ $(SONAME): .depend $(OBJS) $(OBJASM) $(OBJSO)
        $(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)
index 1caaf50..69291c8 100644 (file)
@@ -220,7 +220,7 @@ void x264_frame_delete( x264_frame_t *frame )
 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;
index 43fbe39..b6382f0 100755 (executable)
--- a/configure
+++ b/configure
@@ -12,6 +12,7 @@ echo "  --disable-lavf           disables libavformat support"
 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"
@@ -62,7 +63,7 @@ cc_check() {
     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
@@ -70,7 +71,26 @@ cc_check() {
         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
@@ -119,6 +139,7 @@ lavf="auto"
 ffms="auto"
 gpac="auto"
 pthread="auto"
+swscale="auto"
 asm="auto"
 debug="no"
 gprof="no"
@@ -183,6 +204,9 @@ for opt do
         --disable-pthread)
             pthread="no"
             ;;
+        --disable-swscale)
+            swscale="no"
+            ;;
         --enable-debug)
             debug="yes"
             ;;
@@ -514,6 +538,25 @@ else
     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
@@ -521,36 +564,34 @@ if [ "$lavf" = "auto" ] ; 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"
 
@@ -561,25 +602,27 @@ if [ "$ffms" = "auto" ] ; then
         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"
@@ -601,7 +644,7 @@ if [ "$gpac" = "yes" ] ; then
     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
@@ -726,6 +769,9 @@ Libs: $pclibs
 Cflags: -I$includedir
 EOF
 
+filters="crop select_every"
+[ $swscale = yes ] && filters="resize $filters"
+
 cat > conftest.log <<EOF
 Platform:   $ARCH
 System:     $SYS
@@ -735,6 +781,7 @@ lavf:       $lavf
 ffms:       $ffms
 gpac:       $gpac
 pthread:    $pthread
+filters:    $filters
 debug:      $debug
 gprof:      $gprof
 PIC:        $pic
index fe8fec3..10b4f1d 100644 (file)
@@ -403,9 +403,9 @@ static int x264_validate_parameters( x264_t *h )
         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;
     }
 
diff --git a/filters/filters.c b/filters/filters.c
new file mode 100644 (file)
index 0000000..ecc7b30
--- /dev/null
@@ -0,0 +1,204 @@
+/*****************************************************************************
+ * 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;
+}
diff --git a/filters/filters.h b/filters/filters.h
new file mode 100644 (file)
index 0000000..8bbd08e
--- /dev/null
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * 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
diff --git a/filters/video/cache.c b/filters/video/cache.c
new file mode 100644 (file)
index 0000000..89ba2c1
--- /dev/null
@@ -0,0 +1,138 @@
+/*****************************************************************************
+ * 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 };
diff --git a/filters/video/crop.c b/filters/video/crop.c
new file mode 100644 (file)
index 0000000..abff0e8
--- /dev/null
@@ -0,0 +1,123 @@
+/*****************************************************************************
+ * 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 };
diff --git a/filters/video/fix_vfr_pts.c b/filters/video/fix_vfr_pts.c
new file mode 100644 (file)
index 0000000..fe99a42
--- /dev/null
@@ -0,0 +1,131 @@
+/*****************************************************************************
+ * 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 };
diff --git a/filters/video/internal.c b/filters/video/internal.c
new file mode 100644 (file)
index 0000000..3789433
--- /dev/null
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ * 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;
+}
diff --git a/filters/video/internal.h b/filters/video/internal.h
new file mode 100644 (file)
index 0000000..b676d5a
--- /dev/null
@@ -0,0 +1,28 @@
+/*****************************************************************************
+ * 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
diff --git a/filters/video/resize.c b/filters/video/resize.c
new file mode 100644 (file)
index 0000000..7adf8c0
--- /dev/null
@@ -0,0 +1,478 @@
+/*****************************************************************************
+ * 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 };
diff --git a/filters/video/select_every.c b/filters/video/select_every.c
new file mode 100644 (file)
index 0000000..f5941f4
--- /dev/null
@@ -0,0 +1,150 @@
+/*****************************************************************************
+ * 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 };
diff --git a/filters/video/source.c b/filters/video/source.c
new file mode 100644 (file)
index 0000000..72da584
--- /dev/null
@@ -0,0 +1,79 @@
+/*****************************************************************************
+ * 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 };
diff --git a/filters/video/video.c b/filters/video/video.c
new file mode 100644 (file)
index 0000000..8da89e3
--- /dev/null
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ * 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 );
+}
diff --git a/filters/video/video.h b/filters/video/video.h
new file mode 100644 (file)
index 0000000..08f427b
--- /dev/null
@@ -0,0 +1,58 @@
+/*****************************************************************************
+ * 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
index b83f715..0737b27 100644 (file)
 /* 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
 
@@ -205,15 +209,21 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
         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 );
@@ -222,60 +232,68 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     }
     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 )
@@ -289,4 +307,4 @@ 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 };
index fe8bf7e..84118d5 100644 (file)
@@ -39,19 +39,8 @@ typedef struct
 {
     FFMS_VideoSource *video_source;
     FFMS_Track *track;
-    int total_frames;
-    struct SwsContext *scaler;
-    int pts_offset_flag;
-    int64_t pts_offset;
     int reduce_pts;
     int vfr_input;
-
-    int init_width;
-    int init_height;
-
-    int cur_width;
-    int cur_height;
-    int cur_pix_fmt;
 } ffms_hnd_t;
 
 static int FFMS_CC update_progress( int64_t current, int64_t total, void *private )
@@ -103,26 +92,24 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
 
     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 )
@@ -146,32 +133,15 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     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;
@@ -179,40 +149,32 @@ static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
     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 };
diff --git a/input/input.c b/input/input.c
new file mode 100644 (file)
index 0000000..21828cf
--- /dev/null
@@ -0,0 +1,93 @@
+/*****************************************************************************
+ * 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);
+}
index f588f3c..bb1bfb7 100644 (file)
@@ -31,7 +31,8 @@
 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;
@@ -39,32 +40,52 @@ typedef struct
 /* 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;
@@ -72,4 +93,33 @@ extern const cli_input_t lavf_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
index 54a275f..e1dd352 100644 (file)
@@ -25,7 +25,7 @@
 #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
 {
@@ -33,46 +33,10 @@ 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 )
     {
@@ -80,9 +44,11 @@ static int read_frame_internal( x264_picture_t *p_pic, lavf_hnd_t *h, int i_fram
          * 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;
@@ -91,9 +57,9 @@ static int read_frame_internal( x264_picture_t *p_pic, lavf_hnd_t *h, int i_fram
     }
 
     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 )
     {
@@ -102,12 +68,12 @@ static int read_frame_internal( x264_picture_t *p_pic, lavf_hnd_t *h, int i_fram
             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;
@@ -115,55 +81,58 @@ static int read_frame_internal( x264_picture_t *p_pic, lavf_hnd_t *h, int i_fram
         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", &param->width, &param->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;
@@ -172,81 +141,78 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     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 };
diff --git a/input/raw.c b/input/raw.c
new file mode 100644 (file)
index 0000000..9a2d5ae
--- /dev/null
@@ -0,0 +1,134 @@
+/*****************************************************************************
+ * raw.c: x264 raw input module
+ *****************************************************************************
+ * 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
+ * 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"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "raw", __VA_ARGS__ )
+
+typedef struct
+{
+    FILE *fh;
+    int next_frame;
+    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 )
+{
+    raw_hnd_t *h = malloc( sizeof(raw_hnd_t) );
+    if( !h )
+        return -1;
+
+    if( !opt->resolution )
+    {
+        /* try to parse the file name */
+        for( char *p = psz_filename; *p; p++ )
+            if( *p >= '0' && *p <= '9' && sscanf( p, "%ux%u", &info->width, &info->height ) == 2 )
+                break;
+    }
+    else
+        sscanf( opt->resolution, "%ux%u", &info->width, &info->height );
+    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;
+
+    if( !strcmp( psz_filename, "-" ) )
+        h->fh = stdin;
+    else
+        h->fh = fopen( psz_filename, "rb" );
+    if( h->fh == NULL )
+        return -1;
+
+    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 size = ftell( h->fh );
+        fseek( h->fh, 0, SEEK_SET );
+        info->num_frames = size / h->frame_size;
+    }
+
+    *p_handle = h;
+    return 0;
+}
+
+static int read_frame_internal( cli_pic_t *pic, raw_hnd_t *h )
+{
+    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( cli_pic_t *pic, hnd_t handle, int i_frame )
+{
+    raw_hnd_t *h = handle;
+
+    if( i_frame > h->next_frame )
+    {
+        if( x264_is_regular_file( h->fh ) )
+            fseek( h->fh, i_frame * h->frame_size, SEEK_SET );
+        else
+            while( i_frame > h->next_frame )
+            {
+                if( read_frame_internal( pic, h ) )
+                    return -1;
+                h->next_frame++;
+            }
+    }
+
+    if( read_frame_internal( pic, h ) )
+        return -1;
+
+    h->next_frame = i_frame+1;
+    return 0;
+}
+
+static int close_file( hnd_t handle )
+{
+    raw_hnd_t *h = handle;
+    if( !h || !h->fh )
+        return 0;
+    fclose( h->fh );
+    free( h );
+    return 0;
+}
+
+const cli_input_t raw_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };
index 98af22b..a89c8a8 100644 (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;
@@ -39,7 +37,7 @@ typedef struct
 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;
@@ -57,7 +55,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
         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;
 
@@ -68,18 +66,12 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     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;
@@ -91,7 +83,7 @@ static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
     }
 
     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 );
 
@@ -108,7 +100,7 @@ static int read_frame( x264_picture_t *p_pic, hnd_t handle, int 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 )
@@ -127,4 +119,4 @@ static int close_file( hnd_t handle )
     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 };
index 7821e76..69e1aad 100644 (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;
@@ -98,7 +94,6 @@ static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info
 {
     char buff[256];
     int ret, tcfv, num, seq_num, timecodes_num;
-    int64_t pts_seek_offset;
     double *timecodes = NULL;
     double *fpss = NULL;
 
@@ -109,7 +104,7 @@ static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info
     {
         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;
@@ -130,7 +125,7 @@ static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info
             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 );
@@ -143,8 +138,8 @@ static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info
                 ++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) );
@@ -220,20 +215,18 @@ static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info
     {
         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 );
 
@@ -314,12 +307,10 @@ static int parse_tcfile( FILE *tcfile_in, timecode_hnd_t *h, video_info_t *info
     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 )
     }
 
@@ -345,8 +336,6 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     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 );
@@ -391,39 +380,38 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     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 )
@@ -441,4 +429,4 @@ static int close_file( hnd_t handle )
     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 };
index 9b39d2f..40a1c55 100644 (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"
@@ -38,6 +39,18 @@ typedef struct
 #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) );
@@ -88,18 +101,15 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
         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 */
@@ -146,10 +156,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
                 {
                     /* 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;
@@ -163,32 +170,33 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
     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;
@@ -206,35 +214,33 @@ static int read_frame_internal( x264_picture_t *p_pic, y4m_hnd_t *h )
     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;
@@ -251,4 +257,4 @@ static int close_file( hnd_t handle )
     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 };
diff --git a/input/yuv.c b/input/yuv.c
deleted file mode 100644 (file)
index 613662c..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*****************************************************************************
- * yuv.c: x264 yuv input module
- *****************************************************************************
- * Copyright (C) 2003-2009 x264 project
- *
- * Authors: Laurent Aimar <fenrir@via.ecp.fr>
- *          Loren Merritt <lorenm@u.washington.edu>
- *
- * 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"
-
-typedef struct
-{
-    FILE *fh;
-    int width, height;
-    int next_frame;
-} yuv_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) );
-    if( !h )
-        return -1;
-
-    if( !opt->resolution )
-    {
-        /* try to parse the file name */
-        for( char *p = psz_filename; *p; p++ )
-            if( *p >= '0' && *p <= '9' && sscanf( p, "%ux%u", &info->width, &info->height ) == 2 )
-                break;
-    }
-    else
-        sscanf( opt->resolution, "%ux%u", &info->width, &info->height );
-    FAIL_IF_ERR( !info->width || !info->height, "yuv", "rawyuv input requires a resolution.\n" )
-
-    h->next_frame = 0;
-    info->vfr     = 0;
-    h->width      = info->width;
-    h->height     = info->height;
-
-    if( !strcmp( psz_filename, "-" ) )
-        h->fh = stdin;
-    else
-        h->fh = fopen( psz_filename, "rb" );
-    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;
-
-    if( x264_is_regular_file( h->fh ) )
-    {
-        fseek( h->fh, 0, SEEK_END );
-        uint64_t i_size = ftell( h->fh );
-        fseek( h->fh, 0, SEEK_SET );
-        i_frame_total = (int)(i_size / ( h->width * h->height * 3 / 2 ));
-    }
-
-    return i_frame_total;
-}
-
-static int read_frame_internal( x264_picture_t *p_pic, yuv_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;
-}
-
-static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
-{
-    yuv_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 );
-        else
-            while( i_frame > h->next_frame )
-            {
-                if( read_frame_internal( p_pic, h ) )
-                    return -1;
-                h->next_frame++;
-            }
-    }
-
-    if( read_frame_internal( p_pic, h ) )
-        return -1;
-
-    h->next_frame = i_frame+1;
-    return 0;
-}
-
-static int close_file( hnd_t handle )
-{
-    yuv_hnd_t *h = handle;
-    if( !h || !h->fh )
-        return 0;
-    fclose( h->fh );
-    free( h );
-    return 0;
-}
-
-const cli_input_t yuv_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
diff --git a/x264.c b/x264.c
index 52af0c7..2f4263e 100644 (file)
--- a/x264.c
+++ b/x264.c
@@ -34,6 +34,7 @@
 #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;
@@ -64,14 +70,17 @@ typedef struct {
     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",
@@ -241,6 +250,45 @@ static char *stringify_names( char *buf, const char * const names[] )
     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:
  *****************************************************************************/
@@ -251,10 +299,10 @@ static void Help( x264_param_t *defaults, int longhelp )
 #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"
@@ -611,6 +659,9 @@ static void Help( x264_param_t *defaults, int longhelp )
         "                                  - %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" );
@@ -644,6 +695,13 @@ static void Help( x264_param_t *defaults, int longhelp )
         "                 <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 {
@@ -669,7 +727,10 @@ 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";
@@ -817,6 +878,10 @@ static struct option long_options[] =
     { "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}
 };
 
@@ -869,7 +934,7 @@ static int select_input( const char *demuxer, char *used_demuxer, char *filename
     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 )
     {
@@ -894,8 +959,8 @@ static int select_input( const char *demuxer, char *used_demuxer, char *filename
     }
     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
@@ -925,11 +990,11 @@ static int select_input( const char *demuxer, char *used_demuxer, char *filename
             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 )
@@ -939,6 +1004,52 @@ static int select_input( const char *demuxer, char *used_demuxer, char *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++ )
@@ -973,6 +1084,7 @@ static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )
     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;
@@ -1134,6 +1246,15 @@ static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )
             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:
             {
@@ -1181,7 +1302,6 @@ 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];
 
@@ -1215,33 +1335,35 @@ generic_option:
     }
     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 )
     {
@@ -1251,32 +1373,49 @@ generic_option:
         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. */
@@ -1387,13 +1526,23 @@ static void Print_status( int64_t i_start, int i_frame, int i_frame_total, int64
     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;
@@ -1412,13 +1561,8 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
     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 )
@@ -1434,7 +1578,7 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
     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;
     }
 
@@ -1445,14 +1589,11 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
     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;
@@ -1473,10 +1614,11 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
         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;
@@ -1530,14 +1672,12 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
                 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 ) )
@@ -1554,7 +1694,7 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
                 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 );
@@ -1570,7 +1710,6 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
         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" );
@@ -1586,7 +1725,7 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
         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 )
diff --git a/x264.h b/x264.h
index 4d9b9ca..ea5309c 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -35,7 +35,7 @@
 
 #include <stdarg.h>
 
-#define X264_BUILD 102
+#define X264_BUILD 103
 
 /* x264_t:
  *      opaque handler for encoder */
@@ -174,14 +174,8 @@ static const char * const x264_open_gop_names[] = { "none", "normal", "bluray",
 #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 */
index 1acca56..36e0786 100644 (file)
--- a/x264cli.h
+++ b/x264cli.h
@@ -57,11 +57,13 @@ static inline char *get_filename_extension( char *filename )
 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