]> git.sesse.net Git - x264/commitdiff
Enhanced Avisynth input support
authorSteven Walters <kemuri9@gmail.com>
Fri, 27 Nov 2009 06:37:18 +0000 (22:37 -0800)
committerFiona Glaser <fiona@x264.com>
Mon, 30 Nov 2009 05:02:10 +0000 (21:02 -0800)
Requires avisynth_c.h from the Avisynth API headers.
Reports errors properly from Avisynth script input.
Automatically construct input scripts for almost any input file.
Tries ffmpegsource2, DSS2, directshowsource, and many other sourcing methods, based on the input file extension.
Automatically converts to YV12.

13 files changed:
Makefile
configure
encoder/encoder.c
extras/avisynth_c.h [new file with mode: 0644]
input/avs.c [new file with mode: 0644]
input/input.h
input/thread.c
input/vfw.c [moved from input/avis.c with 80% similarity]
input/y4m.c
input/yuv.c
muxers.h
x264.c
x264.h

index 5a6d358dc1885b80f29310dab033e3e5506bb87e..ea6edd496e86ac21dae24d7068886448ffcf3816 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,8 +19,12 @@ SRCCLI = x264.c input/yuv.c input/y4m.c output/raw.c \
 MUXERS := $(shell grep -E "(IN|OUT)PUT" config.h)
 
 # Optional muxer module sources
-ifneq ($(findstring AVIS_INPUT, $(MUXERS)),)
-SRCCLI += input/avis.c
+ifneq ($(findstring VFW_INPUT, $(MUXERS)),)
+SRCCLI += input/vfw.c
+endif
+
+ifneq ($(findstring AVS_INPUT, $(MUXERS)),)
+SRCCLI += input/avs.c
 endif
 
 ifneq ($(findstring HAVE_PTHREAD, $(CFLAGS)),)
index 6326166a11b020c246a5dd42fba9e184d3c092fe..0cc246dc444901655e2792eedc87c90682597aee 100755 (executable)
--- a/configure
+++ b/configure
@@ -7,7 +7,7 @@ echo ""
 echo "available options:"
 echo ""
 echo "  --help                   print this message"
-echo "  --disable-avis-input     disables avisynth input (win32 only)"
+echo "  --disable-avs-input      disables avisynth input (win32 only)"
 echo "  --disable-mp4-output     disables mp4 output (using gpac)"
 echo "  --disable-pthread        disables multithreaded encoding"
 echo "  --disable-asm            disables assembly optimizations on x86 and arm"
@@ -51,7 +51,7 @@ libdir='${exec_prefix}/lib'
 includedir='${prefix}/include'
 DEVNULL='/dev/null'
 
-avis_input="auto"
+avs_input="auto"
 mp4_output="auto"
 pthread="auto"
 asm="yes"
@@ -95,11 +95,16 @@ for opt do
         --disable-asm)
             asm="no"
             ;;
-        --enable-avis-input)
-            avis_input="yes"
+        --enable-avs-input=*)
+            avs_input="$optarg"
+            if [ "$avs_input" != "auto" -a "$avs_input" != "vfw" -a "$avs_input" != "avs" ] ; then
+                echo "unrecognized enable-avis-input option '$avs_input'"
+                echo "available options are 'auto', 'avs', or 'vfw'"
+                avs_input="auto"
+            fi
             ;;
-        --disable-avis-input)
-            avis_input="no"
+        --disable-avs-input)
+            avs_input="no"
             ;;
         --enable-mp4-output)
             mp4_output="yes"
@@ -410,22 +415,29 @@ if [ "$mp4_output" = "yes" ] ; then
     LDFLAGS="$LDFLAGS $MP4_LDFLAGS"
 fi
 
-if [ "$avis_input" = "auto" ] ; then
-    if [ $SYS = MINGW ]; then
-        avis_input="yes"
+if [ "$avs_input" = "auto" -o "$avs_input" = "avs" ] ; then
+    if [ $SYS = MINGW ] && cc_check avisynth_c.h ; then
+        avs_input="avs"
+        echo "#define AVS_INPUT" >> config.h
+        echo "#define HAVE_AVISYNTH_C_H" >> config.h
+    elif [ $SYS = MINGW ] && cc_check extras/avisynth_c.h ; then
+        avs_input="avs"
+        echo "#define AVS_INPUT" >> config.h
     else
-        avis_input="no";
+        avs_input="auto"
     fi
 fi
-if [ "$avis_input" = "yes" ] ; then
-    if cc_check "stdlib.h" -lvfw32 ; then
-        echo "#define AVIS_INPUT" >> config.h
+if [ "$avs_input" = "auto" -o "$avs_input" = "vfw" ] ; then
+    if [ $SYS = MINGW ] && cc_check "stdlib.h" -lvfw32 ; then
+        echo "#define VFW_INPUT" >> config.h
         LDFLAGS="$LDFLAGS -lvfw32"
-    elif cc_check "stdlib.h" -lavifil32 ; then
-        echo "#define AVIS_INPUT" >> config.h
+        avs_input="vfw"
+    elif [ $SYS = MINGW ] && cc_check "stdlib.h" -lavifil32 ; then
+        echo "#define VFW_INPUT" >> config.h
         LDFLAGS="$LDFLAGS -lavifil32"
+        avs_input="vfw"
     else
-        avis_input="no";
+        avs_input="no";
     fi
 fi
 
@@ -528,7 +540,7 @@ EOF
 echo "Platform:   $ARCH"
 echo "System:     $SYS"
 echo "asm:        $asm"
-echo "avis input: $avis_input"
+echo "avs input:  $avs_input"
 echo "mp4 output: $mp4_output"
 echo "pthread:    $pthread"
 echo "debug:      $debug"
index 08ae3372e3e82c28a16ff3e0866564e2c68b4f6c..8f72e3f6b0adc96728d3401136959296d2f28059 100644 (file)
@@ -394,9 +394,9 @@ static int x264_validate_parameters( x264_t *h )
                   h->param.i_width, h->param.i_height );
         return -1;
     }
-    if( h->param.i_csp != X264_CSP_I420 )
+    if( h->param.i_csp != X264_CSP_I420 && h->param.i_csp != X264_CSP_YV12 )
     {
-        x264_log( h, X264_LOG_ERROR, "invalid CSP (only I420 supported)\n" );
+        x264_log( h, X264_LOG_ERROR, "invalid CSP (only I420/YV12 supported)\n" );
         return -1;
     }
 
diff --git a/extras/avisynth_c.h b/extras/avisynth_c.h
new file mode 100644 (file)
index 0000000..27e8270
--- /dev/null
@@ -0,0 +1,661 @@
+// Avisynth C Interface Version 0.20
+// Copyright 2003 Kevin Atkinson
+
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
+// http://www.gnu.org/copyleft/gpl.html .
+//
+// As a special exception, I give you permission to link to the
+// Avisynth C interface with independent modules that communicate with
+// the Avisynth C interface solely through the interfaces defined in
+// avisynth_c.h, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting combined work
+// under terms of your choice, provided that every copy of the
+// combined work is accompanied by a complete copy of the source code
+// of the Avisynth C interface and Avisynth itself (with the version
+// used to produce the combined work), being distributed under the
+// terms of the GNU General Public License plus this exception.  An
+// independent module is a module which is not derived from or based
+// on Avisynth C Interface, such as 3rd-party filters, import and
+// export plugins, or graphical user interfaces.
+
+#ifndef __AVISYNTH_C__
+#define __AVISYNTH_C__
+
+#ifdef __cplusplus
+#  define EXTERN_C extern "C"
+#else
+#  define EXTERN_C
+#endif
+
+#define AVSC_USE_STDCALL 1
+
+#ifndef AVSC_USE_STDCALL
+#  define AVSC_CC __cdecl
+#else
+#  define AVSC_CC __stdcall
+#endif
+
+#define AVSC_EXPORT EXTERN_C __declspec(dllexport)
+#define AVSC_INLINE static __inline
+#ifdef AVISYNTH_C_EXPORTS
+#  define AVSC_API(ret) EXTERN_C __declspec(dllexport) ret AVSC_CC
+#else
+#  define AVSC_API(ret) EXTERN_C __declspec(dllimport) ret AVSC_CC
+#endif
+
+typedef unsigned char BYTE;
+#ifdef __GNUC__
+typedef long long int INT64;
+#else
+typedef __int64 INT64;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Constants
+//
+
+#ifndef __AVISYNTH_H__
+enum { AVISYNTH_INTERFACE_VERSION = 2 };
+#endif
+
+enum {AVS_SAMPLE_INT8  = 1<<0,
+      AVS_SAMPLE_INT16 = 1<<1, 
+      AVS_SAMPLE_INT24 = 1<<2,
+      AVS_SAMPLE_INT32 = 1<<3,
+      AVS_SAMPLE_FLOAT = 1<<4};
+
+enum {AVS_PLANAR_Y=1<<0,
+      AVS_PLANAR_U=1<<1,
+      AVS_PLANAR_V=1<<2,
+      AVS_PLANAR_ALIGNED=1<<3,
+      AVS_PLANAR_Y_ALIGNED=AVS_PLANAR_Y|AVS_PLANAR_ALIGNED,
+      AVS_PLANAR_U_ALIGNED=AVS_PLANAR_U|AVS_PLANAR_ALIGNED,
+      AVS_PLANAR_V_ALIGNED=AVS_PLANAR_V|AVS_PLANAR_ALIGNED};
+
+  // Colorspace properties.
+enum {AVS_CS_BGR = 1<<28,  
+      AVS_CS_YUV = 1<<29,
+      AVS_CS_INTERLEAVED = 1<<30,
+      AVS_CS_PLANAR = 1<<31};
+
+  // Specific colorformats
+enum {
+  AVS_CS_UNKNOWN = 0,
+  AVS_CS_BGR24 = 1<<0 | AVS_CS_BGR | AVS_CS_INTERLEAVED,
+  AVS_CS_BGR32 = 1<<1 | AVS_CS_BGR | AVS_CS_INTERLEAVED,
+  AVS_CS_YUY2 = 1<<2 | AVS_CS_YUV | AVS_CS_INTERLEAVED,
+  AVS_CS_YV12 = 1<<3 | AVS_CS_YUV | AVS_CS_PLANAR,  // y-v-u, planar
+  AVS_CS_I420 = 1<<4 | AVS_CS_YUV | AVS_CS_PLANAR,  // y-u-v, planar
+  AVS_CS_IYUV = 1<<4 | AVS_CS_YUV | AVS_CS_PLANAR  // same as above
+};
+
+enum {
+  AVS_IT_BFF = 1<<0,
+  AVS_IT_TFF = 1<<1,
+  AVS_IT_FIELDBASED = 1<<2};
+
+enum {
+  AVS_FILTER_TYPE=1,
+  AVS_FILTER_INPUT_COLORSPACE=2,
+  AVS_FILTER_OUTPUT_TYPE=9,
+  AVS_FILTER_NAME=4,
+  AVS_FILTER_AUTHOR=5,
+  AVS_FILTER_VERSION=6,
+  AVS_FILTER_ARGS=7,
+  AVS_FILTER_ARGS_INFO=8,
+  AVS_FILTER_ARGS_DESCRIPTION=10,
+  AVS_FILTER_DESCRIPTION=11};
+
+enum {  //SUBTYPES
+  AVS_FILTER_TYPE_AUDIO=1,
+  AVS_FILTER_TYPE_VIDEO=2,
+  AVS_FILTER_OUTPUT_TYPE_SAME=3,
+  AVS_FILTER_OUTPUT_TYPE_DIFFERENT=4};
+
+enum {
+  AVS_CACHE_NOTHING=0,
+  AVS_CACHE_RANGE=1 };
+
+#define AVS_FRAME_ALIGN 16 
+
+typedef struct AVS_Clip AVS_Clip;
+typedef struct AVS_ScriptEnvironment AVS_ScriptEnvironment;
+
+/////////////////////////////////////////////////////////////////////
+//
+// AVS_VideoInfo
+//
+
+// AVS_VideoInfo is layed out identicly to VideoInfo
+typedef struct AVS_VideoInfo {
+  int width, height;    // width=0 means no video
+  unsigned fps_numerator, fps_denominator;
+  int num_frames;
+
+  int pixel_type;
+  
+  int audio_samples_per_second;   // 0 means no audio
+  int sample_type;
+  INT64 num_audio_samples;
+  int nchannels;
+
+  // Imagetype properties
+
+  int image_type;
+} AVS_VideoInfo;
+
+// useful functions of the above
+AVSC_INLINE int avs_has_video(const AVS_VideoInfo * p) 
+        { return (p->width!=0); }
+
+AVSC_INLINE int avs_has_audio(const AVS_VideoInfo * p) 
+        { return (p->audio_samples_per_second!=0); }
+
+AVSC_INLINE int avs_is_rgb(const AVS_VideoInfo * p) 
+        { return !!(p->pixel_type&AVS_CS_BGR); }
+
+AVSC_INLINE int avs_is_rgb24(const AVS_VideoInfo * p) 
+        { return (p->pixel_type&AVS_CS_BGR24)==AVS_CS_BGR24; } // Clear out additional properties
+
+AVSC_INLINE int avs_is_rgb32(const AVS_VideoInfo * p) 
+        { return (p->pixel_type & AVS_CS_BGR32) == AVS_CS_BGR32 ; }
+
+AVSC_INLINE int avs_is_yuv(const AVS_VideoInfo * p) 
+        { return !!(p->pixel_type&AVS_CS_YUV ); }
+
+AVSC_INLINE int avs_is_yuy2(const AVS_VideoInfo * p) 
+        { return (p->pixel_type & AVS_CS_YUY2) == AVS_CS_YUY2; }  
+
+AVSC_INLINE int avs_is_yv12(const AVS_VideoInfo * p) 
+        { return ((p->pixel_type & AVS_CS_YV12) == AVS_CS_YV12)||((p->pixel_type & AVS_CS_I420) == AVS_CS_I420); }
+
+AVSC_INLINE int avs_is_color_space(const AVS_VideoInfo * p, int c_space) 
+        { return ((p->pixel_type & c_space) == c_space); }
+
+AVSC_INLINE int avs_is_property(const AVS_VideoInfo * p, int property) 
+        { return ((p->pixel_type & property)==property ); }
+
+AVSC_INLINE int avs_is_planar(const AVS_VideoInfo * p) 
+        { return !!(p->pixel_type & AVS_CS_PLANAR); }
+        
+AVSC_INLINE int avs_is_field_based(const AVS_VideoInfo * p) 
+        { return !!(p->image_type & AVS_IT_FIELDBASED); }
+
+AVSC_INLINE int avs_is_parity_known(const AVS_VideoInfo * p) 
+        { return ((p->image_type & AVS_IT_FIELDBASED)&&(p->image_type & (AVS_IT_BFF | AVS_IT_TFF))); }
+
+AVSC_INLINE int avs_is_bff(const AVS_VideoInfo * p) 
+        { return !!(p->image_type & AVS_IT_BFF); }
+
+AVSC_INLINE int avs_is_tff(const AVS_VideoInfo * p) 
+        { return !!(p->image_type & AVS_IT_TFF); }
+
+AVSC_INLINE int avs_bits_per_pixel(const AVS_VideoInfo * p) 
+{ 
+  switch (p->pixel_type) {
+      case AVS_CS_BGR24: return 24;
+      case AVS_CS_BGR32: return 32;
+      case AVS_CS_YUY2:  return 16;
+      case AVS_CS_YV12:
+      case AVS_CS_I420:  return 12;
+      default:           return 0;
+    }
+}
+AVSC_INLINE int avs_bytes_from_pixels(const AVS_VideoInfo * p, int pixels) 
+        { return pixels * (avs_bits_per_pixel(p)>>3); }   // Will work on planar images, but will return only luma planes
+
+AVSC_INLINE int avs_row_size(const AVS_VideoInfo * p) 
+        { return avs_bytes_from_pixels(p,p->width); }  // Also only returns first plane on planar images
+
+AVSC_INLINE int avs_bmp_size(const AVS_VideoInfo * vi)                
+        { if (avs_is_planar(vi)) {int p = vi->height * ((avs_row_size(vi)+3) & ~3); p+=p>>1; return p;  } return vi->height * ((avs_row_size(vi)+3) & ~3); }
+
+AVSC_INLINE int avs_samples_per_second(const AVS_VideoInfo * p) 
+        { return p->audio_samples_per_second; }
+
+
+AVSC_INLINE int avs_bytes_per_channel_sample(const AVS_VideoInfo * p) 
+{
+    switch (p->sample_type) {
+      case AVS_SAMPLE_INT8:  return sizeof(signed char);
+      case AVS_SAMPLE_INT16: return sizeof(signed short);
+      case AVS_SAMPLE_INT24: return 3;
+      case AVS_SAMPLE_INT32: return sizeof(signed int);
+      case AVS_SAMPLE_FLOAT: return sizeof(float);
+      default: return 0;
+    }
+}
+AVSC_INLINE int avs_bytes_per_audio_sample(const AVS_VideoInfo * p)   
+        { return p->nchannels*avs_bytes_per_channel_sample(p);}
+
+AVSC_INLINE INT64 avs_audio_samples_from_frames(const AVS_VideoInfo * p, INT64 frames) 
+        { return ((INT64)(frames) * p->audio_samples_per_second * p->fps_denominator / p->fps_numerator); }
+
+AVSC_INLINE int avs_frames_from_audio_samples(const AVS_VideoInfo * p, INT64 samples) 
+        { return (int)(samples * (INT64)p->fps_numerator / (INT64)p->fps_denominator / (INT64)p->audio_samples_per_second); }
+
+AVSC_INLINE INT64 avs_audio_samples_from_bytes(const AVS_VideoInfo * p, INT64 bytes) 
+        { return bytes / avs_bytes_per_audio_sample(p); }
+
+AVSC_INLINE INT64 avs_bytes_from_audio_samples(const AVS_VideoInfo * p, INT64 samples) 
+        { return samples * avs_bytes_per_audio_sample(p); }
+
+AVSC_INLINE int avs_audio_channels(const AVS_VideoInfo * p) 
+        { return p->nchannels; }
+
+AVSC_INLINE int avs_sample_type(const AVS_VideoInfo * p)
+        { return p->sample_type;}
+
+// useful mutator
+AVSC_INLINE void avs_set_property(AVS_VideoInfo * p, int property)  
+        { p->image_type|=property; }
+
+AVSC_INLINE void avs_clear_property(AVS_VideoInfo * p, int property)  
+        { p->image_type&=~property; }
+
+AVSC_INLINE void avs_set_field_based(AVS_VideoInfo * p, int isfieldbased)  
+        { if (isfieldbased) p->image_type|=AVS_IT_FIELDBASED; else p->image_type&=~AVS_IT_FIELDBASED; }
+
+AVSC_INLINE void avs_set_fps(AVS_VideoInfo * p, unsigned numerator, unsigned denominator) 
+{
+    unsigned x=numerator, y=denominator;
+    while (y) {   // find gcd
+      unsigned t = x%y; x = y; y = t;
+    }
+    p->fps_numerator = numerator/x;
+    p->fps_denominator = denominator/x;
+}
+
+AVSC_INLINE int avs_is_same_colorspace(AVS_VideoInfo * x, AVS_VideoInfo * y)
+{
+        return (x->pixel_type == y->pixel_type)
+                || (avs_is_yv12(x) && avs_is_yv12(y));
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// AVS_VideoFrame
+//
+
+// VideoFrameBuffer holds information about a memory block which is used
+// for video data.  For efficiency, instances of this class are not deleted
+// when the refcount reaches zero; instead they're stored in a linked list
+// to be reused.  The instances are deleted when the corresponding AVS
+// file is closed.
+
+// AVS_VideoFrameBuffer is layed out identicly to VideoFrameBuffer
+// DO NOT USE THIS STRUCTURE DIRECTLY
+typedef struct AVS_VideoFrameBuffer {
+  BYTE * data;
+  int data_size;
+  // sequence_number is incremented every time the buffer is changed, so
+  // that stale views can tell they're no longer valid.
+  long sequence_number;
+
+  long refcount;
+} AVS_VideoFrameBuffer;
+
+// VideoFrame holds a "window" into a VideoFrameBuffer.
+
+// AVS_VideoFrame is layed out identicly to IVideoFrame
+// DO NOT USE THIS STRUCTURE DIRECTLY
+typedef struct AVS_VideoFrame {
+  int refcount;
+  AVS_VideoFrameBuffer * vfb;
+  int offset, pitch, row_size, height, offsetU, offsetV, pitchUV;  // U&V offsets are from top of picture.
+} AVS_VideoFrame;
+
+// Access functions for AVS_VideoFrame
+AVSC_INLINE int avs_get_pitch(const AVS_VideoFrame * p) {
+        return p->pitch;}
+
+AVSC_INLINE int avs_get_pitch_p(const AVS_VideoFrame * p, int plane) { 
+  switch (plane) {
+  case AVS_PLANAR_U: case AVS_PLANAR_V: return p->pitchUV;}
+  return p->pitch;}
+
+AVSC_INLINE int avs_get_row_size(const AVS_VideoFrame * p) {
+        return p->row_size; }
+
+AVSC_INLINE int avs_get_row_size_p(const AVS_VideoFrame * p, int plane) { 
+        int r;
+    switch (plane) {
+    case AVS_PLANAR_U: case AVS_PLANAR_V: 
+                if (p->pitchUV) return p->row_size>>1; 
+                else            return 0;
+    case AVS_PLANAR_U_ALIGNED: case AVS_PLANAR_V_ALIGNED: 
+                if (p->pitchUV) { 
+                        int r = ((p->row_size+AVS_FRAME_ALIGN-1)&(~(AVS_FRAME_ALIGN-1)) )>>1; // Aligned rowsize
+                        if (r < p->pitchUV) 
+                                return r; 
+                        return p->row_size>>1; 
+                } else return 0;
+    case AVS_PLANAR_Y_ALIGNED:
+                r = (p->row_size+AVS_FRAME_ALIGN-1)&(~(AVS_FRAME_ALIGN-1)); // Aligned rowsize
+                if (r <= p->pitch) 
+                        return r; 
+                return p->row_size;
+    }
+    return p->row_size;
+}
+
+AVSC_INLINE int avs_get_height(const AVS_VideoFrame * p) {
+        return p->height;}
+
+AVSC_INLINE int avs_get_height_p(const AVS_VideoFrame * p, int plane) {
+        switch (plane) {
+                case AVS_PLANAR_U: case AVS_PLANAR_V: 
+                        if (p->pitchUV) return p->height>>1;
+                        return 0;
+        }
+        return p->height;}
+
+AVSC_INLINE const BYTE* avs_get_read_ptr(const AVS_VideoFrame * p) {
+        return p->vfb->data + p->offset;}
+
+AVSC_INLINE const BYTE* avs_get_read_ptr_p(const AVS_VideoFrame * p, int plane) 
+{
+        switch (plane) {
+                case AVS_PLANAR_U: return p->vfb->data + p->offsetU;
+                case AVS_PLANAR_V: return p->vfb->data + p->offsetV;
+                default:           return p->vfb->data + p->offset;}
+}
+
+AVSC_INLINE int avs_is_writable(const AVS_VideoFrame * p) {
+        return (p->refcount == 1 && p->vfb->refcount == 1);}
+
+AVSC_INLINE BYTE* avs_get_write_ptr(const AVS_VideoFrame * p) 
+{
+        if (avs_is_writable(p)) {
+                ++p->vfb->sequence_number;
+                return p->vfb->data + p->offset;
+        } else
+                return 0;
+}
+
+AVSC_INLINE BYTE* avs_get_write_ptr_p(const AVS_VideoFrame * p, int plane) 
+{
+        if (plane==AVS_PLANAR_Y && avs_is_writable(p)) {
+                ++p->vfb->sequence_number;
+                return p->vfb->data + p->offset;
+        } else if (plane==AVS_PLANAR_Y) {
+                return 0;
+        } else {
+                switch (plane) {
+                        case AVS_PLANAR_U: return p->vfb->data + p->offsetU;
+                        case AVS_PLANAR_V: return p->vfb->data + p->offsetV;
+                        default:       return p->vfb->data + p->offset;
+                }
+        }
+}
+
+
+AVSC_API(void) avs_release_video_frame(AVS_VideoFrame *);
+// makes a shallow copy of a video frame
+AVSC_API(AVS_VideoFrame *) avs_copy_video_frame(AVS_VideoFrame *);
+
+AVSC_INLINE void avs_release_frame(AVS_VideoFrame * f)
+  {avs_release_video_frame(f);}
+AVSC_INLINE AVS_VideoFrame * avs_copy_frame(AVS_VideoFrame * f)
+  {return avs_copy_video_frame(f);}
+
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// AVS_Value
+//
+
+// Treat AVS_Value as a fat pointer.  That is use avs_copy_value
+// and avs_release_value appropiaty as you would if AVS_Value was
+// a pointer.
+
+// To maintain source code compatibility with future versions of the
+// avisynth_c API don't use the AVS_Value directly.  Use the helper
+// functions below.
+
+// AVS_Value is layed out identicly to AVSValue
+typedef struct AVS_Value AVS_Value;
+struct AVS_Value {
+  short type;  // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong
+               // for some function e'rror
+  short array_size;
+  union {
+    void * clip; // do not use directly, use avs_take_clip
+    char boolean;
+    int integer;
+    float floating_pt;
+    const char * string;
+    const AVS_Value * array;
+  } d;
+};
+
+// AVS_Value should be initilized with avs_void.
+// Should also set to avs_void after the value is released
+// with avs_copy_value.  Consider it the equalvent of setting
+// a pointer to NULL
+static const AVS_Value avs_void = {'v'};
+
+AVSC_API(void) avs_copy_value(AVS_Value * dest, AVS_Value src);
+AVSC_API(void) avs_release_value(AVS_Value);
+
+AVSC_INLINE int avs_defined(AVS_Value v) { return v.type != 'v'; }
+AVSC_INLINE int avs_is_clip(AVS_Value v) { return v.type == 'c'; }
+AVSC_INLINE int avs_is_bool(AVS_Value v) { return v.type == 'b'; }
+AVSC_INLINE int avs_is_int(AVS_Value v) { return v.type == 'i'; }
+AVSC_INLINE int avs_is_float(AVS_Value v) { return v.type == 'f' || v.type == 'i'; }
+AVSC_INLINE int avs_is_string(AVS_Value v) { return v.type == 's'; }
+AVSC_INLINE int avs_is_array(AVS_Value v) { return v.type == 'a'; }
+AVSC_INLINE int avs_is_error(AVS_Value v) { return v.type == 'e'; }
+
+AVSC_API(AVS_Clip *) avs_take_clip(AVS_Value, AVS_ScriptEnvironment *);
+AVSC_API(void) avs_set_to_clip(AVS_Value *, AVS_Clip *);
+
+AVSC_INLINE int avs_as_bool(AVS_Value v) 
+        { return v.d.boolean; }   
+AVSC_INLINE int avs_as_int(AVS_Value v) 
+        { return v.d.integer; }   
+AVSC_INLINE const char * avs_as_string(AVS_Value v) 
+        { return avs_is_error(v) || avs_is_string(v) ? v.d.string : 0; }
+AVSC_INLINE double avs_as_float(AVS_Value v) 
+        { return avs_is_int(v) ? v.d.integer : v.d.floating_pt; }
+AVSC_INLINE const char * avs_as_error(AVS_Value v) 
+        { return avs_is_error(v) ? v.d.string : 0; }
+AVSC_INLINE const AVS_Value * avs_as_array(AVS_Value v)
+        { return v.d.array; }
+AVSC_INLINE int avs_array_size(AVS_Value v) 
+        { return avs_is_array(v) ? v.array_size : 1; }
+AVSC_INLINE AVS_Value avs_array_elt(AVS_Value v, int index) 
+        { return avs_is_array(v) ? v.d.array[index] : v; }
+
+// only use these functions on am AVS_Value that does not already have
+// an active value.  Remember, treat AVS_Value as a fat pointer.
+AVSC_INLINE AVS_Value avs_new_value_bool(int v0) 
+        { AVS_Value v; v.type = 'b'; v.d.boolean = v0 == 0 ? 0 : 1; return v; }   
+AVSC_INLINE AVS_Value avs_new_value_int(int v0) 
+        { AVS_Value v; v.type = 'i'; v.d.integer = v0; return v; }   
+AVSC_INLINE AVS_Value avs_new_value_string(const char * v0) 
+        { AVS_Value v; v.type = 's'; v.d.string = v0; return v; }
+AVSC_INLINE AVS_Value avs_new_value_float(float v0) 
+        { AVS_Value v; v.type = 'f'; v.d.floating_pt = v0; return v;}
+AVSC_INLINE AVS_Value avs_new_value_error(const char * v0) 
+        { AVS_Value v; v.type = 'e'; v.d.string = v0; return v; }
+AVSC_INLINE AVS_Value avs_new_value_clip(AVS_Clip * v0)
+        { AVS_Value v; avs_set_to_clip(&v, v0); return v; }
+AVSC_INLINE AVS_Value avs_new_value_array(AVS_Value * v0, int size)
+        { AVS_Value v; v.type = 'a'; v.d.array = v0; v.array_size = size; return v; }
+
+/////////////////////////////////////////////////////////////////////
+//
+// AVS_Clip
+//
+
+AVSC_API(void) avs_release_clip(AVS_Clip *);
+AVSC_API(AVS_Clip *) avs_copy_clip(AVS_Clip *);
+
+AVSC_API(const char *) avs_clip_get_error(AVS_Clip *); // return 0 if no error
+
+AVSC_API(const AVS_VideoInfo *) avs_get_video_info(AVS_Clip *);
+
+AVSC_API(int) avs_get_version(AVS_Clip *);
+AVSC_API(AVS_VideoFrame *) avs_get_frame(AVS_Clip *, int n);
+// The returned video frame must be released with avs_release_video_frame
+
+AVSC_API(int) avs_get_parity(AVS_Clip *, int n); 
+// return field parity if field_based, else parity of first field in frame
+
+AVSC_API(int) avs_get_audio(AVS_Clip *, void * buf, 
+                                  INT64 start, INT64 count); 
+// start and count are in samples
+
+AVSC_API(int) avs_set_cache_hints(AVS_Clip *, 
+                                        int cachehints, int frame_range);
+
+// This is the callback type used by avs_add_function
+typedef AVS_Value (AVSC_CC * AVS_ApplyFunc)
+                        (AVS_ScriptEnvironment *, AVS_Value args, void * user_data);
+
+typedef struct AVS_FilterInfo AVS_FilterInfo;
+struct AVS_FilterInfo
+{
+  // these members should not be modified outside of the AVS_ApplyFunc callback
+  AVS_Clip * child;
+  AVS_VideoInfo vi;
+  AVS_ScriptEnvironment * env;
+  AVS_VideoFrame * (AVSC_CC * get_frame)(AVS_FilterInfo *, int n);
+  int (AVSC_CC * get_parity)(AVS_FilterInfo *, int n);
+  int (AVSC_CC * get_audio)(AVS_FilterInfo *, void * buf, 
+                                 INT64 start, INT64 count);
+  int (AVSC_CC * set_cache_hints)(AVS_FilterInfo *, int cachehints, 
+                                       int frame_range);
+  void (AVSC_CC * free_filter)(AVS_FilterInfo *);
+  
+  // Should be set when ever there is an error to report.
+  // It is cleared before any of the above methods are called
+  const char * error;
+  // this is to store whatever and may be modified at will
+  void * user_data;
+};
+
+// Create a new filter
+// fi is set to point to the AVS_FilterInfo so that you can
+//   modify it once it is initilized.
+// store_child should generally be set to true.  If it is not
+//    set than ALL methods (the function pointers) must be defined
+// If it is set than you do not need to worry about freeing the child
+//    clip.
+AVSC_API(AVS_Clip *) avs_new_c_filter(AVS_ScriptEnvironment * e,
+                                            AVS_FilterInfo * * fi,
+                                            AVS_Value child, int store_child);
+
+/////////////////////////////////////////////////////////////////////
+//
+// AVS_ScriptEnvironment
+//
+
+// For GetCPUFlags.  These are backwards-compatible with those in VirtualDub.
+enum {                    
+                                /* slowest CPU to support extension */
+  AVS_CPU_FORCE        = 0x01,   // N/A
+  AVS_CPU_FPU          = 0x02,   // 386/486DX
+  AVS_CPU_MMX          = 0x04,   // P55C, K6, PII
+  AVS_CPU_INTEGER_SSE  = 0x08,   // PIII, Athlon
+  AVS_CPU_SSE          = 0x10,   // PIII, Athlon XP/MP
+  AVS_CPU_SSE2         = 0x20,   // PIV, Hammer
+  AVS_CPU_3DNOW        = 0x40,   // K6-2
+  AVS_CPU_3DNOW_EXT    = 0x80,   // Athlon
+  AVS_CPU_X86_64       = 0xA0,   // Hammer (note: equiv. to 3DNow + SSE2, 
+                                 // which only Hammer will have anyway)
+};
+
+
+AVSC_API(long) avs_get_cpu_flags(AVS_ScriptEnvironment *);
+AVSC_API(int) avs_check_version(AVS_ScriptEnvironment *, int version);
+
+AVSC_API(char *) avs_save_string(AVS_ScriptEnvironment *, const char* s, int length);
+AVSC_API(char *) avs_sprintf(AVS_ScriptEnvironment *, const char * fmt, ...);
+
+AVSC_API(char *) avs_vsprintf(AVS_ScriptEnvironment *, const char * fmt, void* val);
+ // note: val is really a va_list; I hope everyone typedefs va_list to a pointer
+
+AVSC_API(int) avs_add_function(AVS_ScriptEnvironment *, 
+                                    const char * name, const char * params, 
+                                    AVS_ApplyFunc apply, void * user_data);
+
+AVSC_API(int) avs_function_exists(AVS_ScriptEnvironment *, const char * name);
+
+AVSC_API(AVS_Value) avs_invoke(AVS_ScriptEnvironment *, const char * name, 
+                               AVS_Value args, const char** arg_names);
+// The returned value must be be released with avs_release_value
+
+AVSC_API(AVS_Value) avs_get_var(AVS_ScriptEnvironment *, const char* name);
+// The returned value must be be released with avs_release_value
+
+AVSC_API(int) avs_set_var(AVS_ScriptEnvironment *, const char* name, AVS_Value val);
+
+AVSC_API(int) avs_set_global_var(AVS_ScriptEnvironment *, const char* name, const AVS_Value val);
+
+//void avs_push_context(AVS_ScriptEnvironment *, int level=0);
+//void avs_pop_context(AVS_ScriptEnvironment *);
+
+AVSC_API(AVS_VideoFrame *) avs_new_video_frame_a(AVS_ScriptEnvironment *, 
+                                          const AVS_VideoInfo * vi, int align);
+// align should be at least 16
+
+AVSC_INLINE 
+AVS_VideoFrame * avs_new_video_frame(AVS_ScriptEnvironment * env, 
+                                     const AVS_VideoInfo * vi)
+  {return avs_new_video_frame_a(env,vi,AVS_FRAME_ALIGN);}
+
+AVSC_INLINE 
+AVS_VideoFrame * avs_new_frame(AVS_ScriptEnvironment * env, 
+                               const AVS_VideoInfo * vi)
+  {return avs_new_video_frame_a(env,vi,AVS_FRAME_ALIGN);}
+
+
+AVSC_API(int) avs_make_writable(AVS_ScriptEnvironment *, AVS_VideoFrame * * pvf);
+
+AVSC_API(void) avs_bit_blt(AVS_ScriptEnvironment *, BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height);
+
+typedef void (AVSC_CC *AVS_ShutdownFunc)(void* user_data, AVS_ScriptEnvironment * env);
+AVSC_API(void) avs_at_exit(AVS_ScriptEnvironment *, AVS_ShutdownFunc function, void * user_data);
+
+AVSC_API(AVS_VideoFrame *) avs_subframe(AVS_ScriptEnvironment *, AVS_VideoFrame * src, int rel_offset, int new_pitch, int new_row_size, int new_height);
+// The returned video frame must be be released
+
+AVSC_API(int) avs_set_memory_max(AVS_ScriptEnvironment *, int mem);
+
+AVSC_API(int) avs_set_working_dir(AVS_ScriptEnvironment *, const char * newdir);
+
+// avisynth.dll exports this; it's a way to use it as a library, without
+// writing an AVS script or without going through AVIFile.
+AVSC_API(AVS_ScriptEnvironment *) avs_create_script_environment(int version);
+
+// this symbol is the entry point for the plugin and must
+// be defined
+AVSC_EXPORT
+const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment* env);
+
+
+AVSC_API(void) avs_delete_script_environment(AVS_ScriptEnvironment *);
+
+
+AVSC_API(AVS_VideoFrame *) avs_subframe_planar(AVS_ScriptEnvironment *, AVS_VideoFrame * src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV);
+// The returned video frame must be be released
+
+#endif
diff --git a/input/avs.c b/input/avs.c
new file mode 100644 (file)
index 0000000..0cd99e2
--- /dev/null
@@ -0,0 +1,305 @@
+/*****************************************************************************
+ * avs.c: x264 avisynth input module
+ *****************************************************************************
+ * Copyright (C) 2009 x264 project
+ *
+ * Authors: Steven Walters <kemuri9@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+#include "muxers.h"
+#include <windows.h>
+
+/* the AVS interface currently uses __declspec to link function declarations to their definitions in the dll.
+   this has a side effect of preventing program execution if the avisynth dll is not found,
+   so define __declspec(dllimport) to nothing and work around this */
+#undef __declspec
+#define __declspec(i)
+#undef EXTERN_C
+
+#ifdef HAVE_AVISYNTH_C_H
+#include <avisynth_c.h>
+#else
+#include "extras/avisynth_c.h"
+#endif
+
+/* AVS uses a versioned interface to control backwards compatibility */
+/* YV12 support is required */
+#define AVS_INTERFACE_YV12 2
+/* when AVS supports other planar colorspaces, a workaround is required */
+#define AVS_INTERFACE_OTHER_PLANAR 5
+
+/* maximum size of the sequence of filters to try on non script files */
+#define AVS_MAX_SEQUENCE 5
+
+#define LOAD_AVS_FUNC(name, continue_on_fail) \
+{\
+    h->func.name = (void*)GetProcAddress( h->library, #name );\
+    if( !continue_on_fail && !h->func.name )\
+        goto fail;\
+}
+
+typedef struct
+{
+    AVS_Clip *clip;
+    AVS_ScriptEnvironment *env;
+    HMODULE library;
+    /* declare function pointers for the utilized functions to be loaded without __declspec,
+       as the avisynth header does not compensate for this type of usage */
+    struct
+    {
+        const char *(__stdcall *avs_clip_get_error)( AVS_Clip *clip );
+        AVS_ScriptEnvironment *(__stdcall *avs_create_script_environment)( int version );
+        void (__stdcall *avs_delete_script_environment)( AVS_ScriptEnvironment *env );
+        AVS_VideoFrame *(__stdcall *avs_get_frame)( AVS_Clip *clip, int n );
+        int (__stdcall *avs_get_version)( AVS_Clip *clip );
+        const AVS_VideoInfo *(__stdcall *avs_get_video_info)( AVS_Clip *clip );
+        int (__stdcall *avs_function_exists)( AVS_ScriptEnvironment *env, const char *name );
+        AVS_Value (__stdcall *avs_invoke)( AVS_ScriptEnvironment *env, const char *name,
+            AVS_Value args, const char **arg_names );
+        void (__stdcall *avs_release_clip)( AVS_Clip *clip );
+        void (__stdcall *avs_release_value)( AVS_Value value );
+        void (__stdcall *avs_release_video_frame)( AVS_VideoFrame *frame );
+        AVS_Clip *(__stdcall *avs_take_clip)( AVS_Value, AVS_ScriptEnvironment *env );
+    } func;
+} avs_hnd_t;
+
+/* load the library and functions we require from it */
+static int avs_load_library( avs_hnd_t *h )
+{
+    h->library = LoadLibrary( "avisynth" );
+    if( !h->library )
+        return -1;
+    LOAD_AVS_FUNC( avs_clip_get_error, 0 );
+    LOAD_AVS_FUNC( avs_create_script_environment, 0 );
+    LOAD_AVS_FUNC( avs_delete_script_environment, 1 );
+    LOAD_AVS_FUNC( avs_get_frame, 0 );
+    LOAD_AVS_FUNC( avs_get_version, 0 );
+    LOAD_AVS_FUNC( avs_get_video_info, 0 );
+    LOAD_AVS_FUNC( avs_function_exists, 0 );
+    LOAD_AVS_FUNC( avs_invoke, 0 );
+    LOAD_AVS_FUNC( avs_release_clip, 0 );
+    LOAD_AVS_FUNC( avs_release_value, 0 );
+    LOAD_AVS_FUNC( avs_release_video_frame, 0 );
+    LOAD_AVS_FUNC( avs_take_clip, 0 );
+    return 0;
+fail:
+    FreeLibrary( h->library );
+    return -1;
+}
+
+/* generate a filter sequence to try based on the filename extension */
+static void avs_build_filter_sequence( char *filename_ext, const char *filter[AVS_MAX_SEQUENCE+1] )
+{
+    int i=0, j;
+    const char *all_purpose[] = { "FFmpegSource2", "DSS2", "DirectShowSource", 0 };
+    if( !strcasecmp( filename_ext, "avi" ) )
+        filter[i++] = "AVISource";
+    if( !strcasecmp( filename_ext, "d2v" ) )
+        filter[i++] = "MPEG2Source";
+    if( !strcasecmp( filename_ext, "dga" ) )
+        filter[i++] = "AVCSource";
+    for( j = 0; all_purpose[j] && i < AVS_MAX_SEQUENCE; j++ )
+        filter[i++] = all_purpose[j];
+}
+
+static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
+{
+    FILE *fh = fopen( psz_filename, "r" );
+    if( !fh )
+        return -1;
+    else if( !x264_is_regular_file( fh ) )
+    {
+        fprintf( stderr, "avs [error]: AVS input is incompatible with non-regular file `%s'\n", psz_filename );
+        return -1;
+    }
+    fclose( fh );
+
+    avs_hnd_t *h = malloc( sizeof(avs_hnd_t) );
+    if( !h )
+        return -1;
+    if( avs_load_library( h ) )
+    {
+        fprintf( stderr, "avs [error]: failed to load avisynth\n" );
+        return -1;
+    }
+    h->env = h->func.avs_create_script_environment( AVS_INTERFACE_YV12 );
+    if( !h->env )
+    {
+        fprintf( stderr, "avs [error]: failed to initiate avisynth\n" );
+        return -1;
+    }
+    AVS_Value arg = avs_new_value_string( psz_filename );
+    AVS_Value res;
+    char *filename_ext = get_filename_extension( psz_filename );
+
+    if( !strcasecmp( filename_ext, "avs" ) )
+    {
+        res = h->func.avs_invoke( h->env, "Import", arg, NULL );
+        if( avs_is_error( res ) )
+        {
+            fprintf( stderr, "avs [error]: %s\n", avs_as_string( res ) );
+            return -1;
+        }
+    }
+    else /* non script file */
+    {
+        /* cycle through known source filters to find one that works */
+        const char *filter[AVS_MAX_SEQUENCE+1] = { 0 };
+        avs_build_filter_sequence( filename_ext, filter );
+        int i;
+        for( i = 0; filter[i]; i++ )
+        {
+            fprintf( stderr, "avs [info]: Trying %s... ", filter[i] );
+            if( !h->func.avs_function_exists( h->env, filter[i] ) )
+            {
+                fprintf( stderr, "not found\n" );
+                continue;
+            }
+            if( !strncasecmp( filter[i], "FFmpegSource", 12 ) )
+            {
+                fprintf( stderr, "Indexing... " );
+                fflush( stderr );
+            }
+            res = h->func.avs_invoke( h->env, filter[i], arg, NULL );
+            if( !avs_is_error( res ) )
+            {
+                fprintf( stderr, "succeeded\n" );
+                break;
+            }
+            fprintf( stderr, "failed\n" );
+        }
+        if( !filter[i] )
+        {
+            fprintf( stderr, "avs [error]: unable to find source filter to open `%s'\n", psz_filename );
+            return -1;
+        }
+    }
+    if( !avs_is_clip( res ) )
+    {
+        fprintf( stderr, "avs [error]: `%s' didn't return a video clip\n", psz_filename );
+        return -1;
+    }
+    h->clip = h->func.avs_take_clip( res, h->env );
+    int avs_version = h->func.avs_get_version( h->clip );
+    const AVS_VideoInfo *vi = h->func.avs_get_video_info( h->clip );
+    if( !avs_has_video( vi ) )
+    {
+        fprintf( stderr, "avs [error]: `%s' has no video data\n", psz_filename );
+        return -1;
+    }
+    if( vi->width&1 || vi->height&1 )
+    {
+        fprintf( stderr, "avs [error]: input clip width or height not divisible by 2 (%dx%d)\n",
+                 vi->width, vi->height );
+        return -1;
+    }
+    /* 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 )
+    {
+        h->func.avs_release_clip( h->clip );
+        fprintf( stderr, "avs [warning]: converting input clip to YV12\n" );
+        const char *arg_name[2] = { NULL, "interlaced" };
+        AVS_Value arg_arr[2] = { res, avs_new_value_bool( p_param->b_interlaced ) };
+        AVS_Value res2 = h->func.avs_invoke( h->env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name );
+        if( avs_is_error( res2 ) )
+        {
+            fprintf( stderr, "avs [error]: Couldn't convert input clip to YV12\n" );
+            return -1;
+        }
+        h->clip = h->func.avs_take_clip( res2, h->env );
+        h->func.avs_release_value( res2 );
+        vi = h->func.avs_get_video_info( h->clip );
+    }
+    h->func.avs_release_value( res );
+
+    p_param->i_width = vi->width;
+    p_param->i_height = vi->height;
+    p_param->i_fps_num = vi->fps_numerator;
+    p_param->i_fps_den = vi->fps_denominator;
+    p_param->i_csp = X264_CSP_YV12;
+
+    fprintf( stderr, "avs [info]: %dx%d @ %.2f fps (%d frames)\n",
+             p_param->i_width, p_param->i_height,
+             (double)p_param->i_fps_num / p_param->i_fps_den,
+             vi->num_frames );
+
+    *p_handle = h;
+    return 0;
+}
+
+static int get_frame_total( hnd_t handle )
+{
+    avs_hnd_t *h = handle;
+    const AVS_VideoInfo *vi = h->func.avs_get_video_info( h->clip );
+    return vi->num_frames;
+}
+
+static int picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
+{
+    pic->img.i_csp = i_csp;
+    pic->img.i_plane = 3;
+    pic->param = NULL;
+    return 0;
+}
+
+static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
+{
+    static int plane[3] = { AVS_PLANAR_Y, AVS_PLANAR_V, AVS_PLANAR_U };
+    avs_hnd_t *h = handle;
+    AVS_VideoFrame *frm =
+    p_pic->opaque = h->func.avs_get_frame( h->clip, i_frame );
+    int i;
+    const char *err = h->func.avs_clip_get_error( h->clip );
+    if( err )
+    {
+        fprintf( stderr, "avs [error]: %s occurred while reading frame %d\n", err, i_frame );
+        return -1;
+    }
+    for( i = 0; i < 3; 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] );
+    }
+    return 0;
+}
+
+static int release_frame( x264_picture_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 )
+{
+    memset( pic, 0, sizeof(x264_picture_t) );
+}
+
+static int close_file( hnd_t handle )
+{
+    avs_hnd_t *h = handle;
+    h->func.avs_release_clip( h->clip );
+    if( h->func.avs_delete_script_environment )
+        h->func.avs_delete_script_environment( h->env );
+    FreeLibrary( h->library );
+    free( h );
+    return 0;
+}
+
+cli_input_t avs_input = { open_file, get_frame_total, picture_alloc, read_frame, release_frame, picture_clean, close_file };
index 87d5386f1263c60efa041b0b253d06f167c438bd..220a414e0cb813c9df98a45d32464c91e7d2ac84 100644 (file)
@@ -28,13 +28,16 @@ typedef struct
 {
     int (*open_file)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param );
     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 (*close_file)( hnd_t handle );
 } cli_input_t;
 
 extern cli_input_t yuv_input;
 extern cli_input_t y4m_input;
-extern cli_input_t avis_input;
+extern cli_input_t avs_input;
 extern cli_input_t thread_input;
 
 #endif
index a9330dcc05d19e4e741a3eacafe8a4840ad7229a..4aa25e7250ad3e27aa9b8b1b5fe4b8fc2245979b 100644 (file)
@@ -48,7 +48,7 @@ typedef struct thread_input_arg_t
 static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
 {
     thread_hnd_t *h = malloc( sizeof(thread_hnd_t) );
-    if( !h || x264_picture_alloc( &h->pic, X264_CSP_I420, p_param->i_width, p_param->i_height ) < 0 )
+    if( !h || input.picture_alloc( &h->pic, p_param->i_csp, p_param->i_width, p_param->i_height ) )
     {
         fprintf( stderr, "x264 [error]: malloc failed\n" );
         return -1;
@@ -63,6 +63,8 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
     h->next_args->h = h;
     h->next_args->status = 0;
     h->frame_total = input.get_frame_total( h->p_handle );
+    thread_input.picture_alloc = h->input.picture_alloc;
+    thread_input.picture_clean = h->input.picture_clean;
 
     *p_handle = h;
     return 0;
@@ -111,16 +113,24 @@ 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 )
+{
+    thread_hnd_t *h = handle;
+    if( h->input.release_frame )
+        return h->input.release_frame( pic, h->p_handle );
+    return 0;
+}
+
 static int close_file( hnd_t handle )
 {
     thread_hnd_t *h = handle;
     if( h->in_progress )
         x264_pthread_join( h->tid, NULL );
     h->input.close_file( h->p_handle );
-    x264_picture_clean( &h->pic );
+    h->input.picture_clean( &h->pic );
     free( h->next_args );
     free( h );
     return 0;
 }
 
-cli_input_t thread_input = { open_file, get_frame_total, read_frame, close_file };
+cli_input_t thread_input = { open_file, get_frame_total, NULL, read_frame, release_frame, NULL, close_file };
similarity index 80%
rename from input/avis.c
rename to input/vfw.c
index f9517e34ffe2418a3496bef804d88c130c8aa2f6..479d610d893421d5f20ee726c91924b2f5f6f242 100644 (file)
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * avis.c: x264 avi/avs input module
+ * vfw.c: x264 avisynth input via VFW module
  *****************************************************************************
  * Copyright (C) 2003-2009 x264 project
  *
@@ -28,8 +28,9 @@
 typedef struct
 {
     PAVISTREAM p_avi;
-    int width, height;
-} avis_hnd_t;
+    int width;
+    int height;
+} vfw_hnd_t;
 
 static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
 {
@@ -38,12 +39,12 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
         return -1;
     else if( !x264_is_regular_file( fh ) )
     {
-        fprintf( stderr, "avis [error]: AVIS input is incompatible with non-regular file `%s'\n", psz_filename );
+        fprintf( stderr, "vfw [error]: VFW input is incompatible with non-regular file `%s'\n", psz_filename );
         return -1;
     }
     fclose( fh );
 
-    avis_hnd_t *h = malloc( sizeof(avis_hnd_t) );
+    vfw_hnd_t *h = malloc( sizeof(vfw_hnd_t) );
     if( !h )
         return -1;
     AVISTREAMINFO info;
@@ -68,7 +69,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
     // check input format
     if( info.fccHandler != MAKEFOURCC('Y', 'V', '1', '2') )
     {
-        fprintf( stderr, "avis [error]: unsupported input format (%c%c%c%c)\n",
+        fprintf( stderr, "vfw [error]: unsupported input format (%c%c%c%c)\n",
             (char)(info.fccHandler & 0xff), (char)((info.fccHandler >> 8) & 0xff),
             (char)((info.fccHandler >> 16) & 0xff), (char)((info.fccHandler >> 24)) );
 
@@ -85,8 +86,9 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
     i = gcd( info.dwRate, info.dwScale );
     p_param->i_fps_den = info.dwScale / i;
     p_param->i_fps_num = info.dwRate / i;
+    p_param->i_csp = X264_CSP_YV12;
 
-    fprintf( stderr, "avis [info]: %dx%d @ %.2f fps (%d frames)\n",
+    fprintf( stderr, "vfw [info]: %dx%d @ %.2f fps (%d frames)\n",
              p_param->i_width, p_param->i_height,
              (double)p_param->i_fps_num / (double)p_param->i_fps_den,
              (int)info.dwLength );
@@ -96,7 +98,7 @@ static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param
 
 static int get_frame_total( hnd_t handle )
 {
-    avis_hnd_t *h = handle;
+    vfw_hnd_t *h = handle;
     AVISTREAMINFO info;
 
     if( AVIStreamInfo( h->p_avi, &info, sizeof(AVISTREAMINFO) ) )
@@ -107,23 +109,17 @@ static int get_frame_total( hnd_t handle )
 
 static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
 {
-    avis_hnd_t *h = handle;
-
-    p_pic->img.i_csp = X264_CSP_YV12;
-
-    if( AVIStreamRead( h->p_avi, i_frame, 1, p_pic->img.plane[0], h->width * h->height * 3 / 2, NULL, NULL ) )
-        return -1;
-
-    return 0;
+    vfw_hnd_t *h = handle;
+    return AVIStreamRead( h->p_avi, i_frame, 1, p_pic->img.plane[0], h->width * h->height * 3 / 2, NULL, NULL );
 }
 
 static int close_file( hnd_t handle )
 {
-    avis_hnd_t *h = handle;
+    vfw_hnd_t *h = handle;
     AVIStreamRelease( h->p_avi );
     AVIFileExit();
     free( h );
     return 0;
 }
 
-cli_input_t avis_input = { open_file, get_frame_total, read_frame, close_file };
+cli_input_t avs_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
index d5d85c846c0fd7b13bc195f45b1e6c08a8aadfa1..6092b92126857e22a29e9394f03b2b74cc6735e5 100644 (file)
@@ -245,4 +245,4 @@ static int close_file( hnd_t handle )
     return 0;
 }
 
-cli_input_t y4m_input = { open_file, get_frame_total, read_frame, close_file };
+cli_input_t y4m_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
index 6fe3d5edef95646e75b3746f59b3e47808f2a4d5..ca4b082ec392909242b445487939d16f5e55c5ab 100644 (file)
@@ -107,4 +107,4 @@ static int close_file( hnd_t handle )
     return 0;
 }
 
-cli_input_t yuv_input = { open_file, get_frame_total, read_frame, close_file };
+cli_input_t yuv_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };
index 44ea9662263cd36bdcf0cad1efbc081b352a05d5..041dbbc3230463c2050bfa6f281b79dbab4d5d0f 100644 (file)
--- a/muxers.h
+++ b/muxers.h
@@ -41,6 +41,15 @@ static inline int64_t gcd( int64_t a, int64_t b )
     }
 }
 
+static inline char *get_filename_extension( char *filename )
+{
+    char *ext = filename + strlen( filename );
+    while( *ext != '.' && ext > filename )
+        ext--;
+    ext += *ext == '.';
+    return ext;
+}
+
 #include "input/input.h"
 #include "output/output.h"
 
diff --git a/x264.c b/x264.c
index 6bf75e05fe1227642f4d4cf98509c2fb54d700fe..c857f58d3775e9ab46e194464332dc8b241dd576 100644 (file)
--- a/x264.c
+++ b/x264.c
@@ -130,7 +130,7 @@ static void Help( x264_param_t *defaults, int longhelp )
         "\n"
         "Infile can be raw YUV 4:2:0 (in which case resolution is required),\n"
         "  or YUV4MPEG 4:2:0 (*.y4m),\n"
-        "  or AVI or Avisynth if compiled with AVIS support (%s).\n"
+        "  or Avisynth if compiled with support (%s).\n"
         "Outfile type is selected by filename:\n"
         " .264 -> Raw bytestream\n"
         " .mkv -> Matroska\n"
@@ -144,8 +144,10 @@ static void Help( x264_param_t *defaults, int longhelp )
         "      --fullhelp              List all options\n"
         "\n",
         X264_BUILD, X264_VERSION,
-#ifdef AVIS_INPUT
-        "yes",
+#ifdef AVS_INPUT
+        "native",
+#elif defined(VFW_INPUT)
+        "vfw (fallback)",
 #else
         "no",
 #endif
@@ -539,9 +541,9 @@ static struct option long_options[] =
 
 static int select_output( char *filename, const char *pipe_format, x264_param_t *param )
 {
-    char *ext = filename + strlen( filename ) - 1;
-    while( *ext != '.' && ext > filename )
-        ext--;
+    const char *ext = get_filename_extension( filename );
+    if( !strcmp( filename, "-" ) )
+        ext = pipe_format;
 
     if( !strcasecmp( ext, ".mp4" ) )
     {
@@ -552,9 +554,9 @@ static int select_output( char *filename, const char *pipe_format, x264_param_t
         return -1;
 #endif
     }
-    else if( !strcasecmp( ext, ".mkv" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "mkv" )) )
+    else if( !strcasecmp( ext, "mkv" ) )
         output = mkv_output; // FIXME use b_annexb=0
-    else if( !strcasecmp( ext, ".flv" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "flv" )) )
+    else if( !strcasecmp( ext, "flv" ) )
     {
         output = flv_output;
         param->b_annexb = 0;
@@ -566,22 +568,22 @@ static int select_output( char *filename, const char *pipe_format, x264_param_t
 
 static int select_input( char *filename, char *resolution, const char *pipe_format, x264_param_t *param )
 {
-    char *ext = filename + strlen( filename ) - 1;
-    while( ext > filename && *ext != '.' )
-        ext--;
+    const char *ext = get_filename_extension( filename );
+    if( !strcmp( filename, "-" ) )
+        ext = pipe_format;
 
-    if( !strcasecmp( ext, ".avi" ) || !strcasecmp( ext, ".avs" ) )
+    if( !strcasecmp( ext, "avi" ) || !strcasecmp( ext, "avs" ) )
     {
-#ifdef AVIS_INPUT
-        input = avis_input;
+#if defined(AVS_INPUT) || defined(VFW_INPUT)
+        input = avs_input;
 #else
-        fprintf( stderr, "x264 [error]: not compiled with AVIS input support\n" );
+        fprintf( stderr, "x264 [error]: not compiled with AVS input support\n" );
         return -1;
 #endif
     }
-    else if( !strcasecmp( ext, ".y4m" ) || (!strcmp( filename, "-" ) && !strcasecmp( pipe_format, "y4m" )) )
+    else if( !strcasecmp( ext, "y4m" ) )
         input = y4m_input;
-    else // yuv
+    else if( !strcasecmp( ext, "yuv" ) )
     {
         if( !resolution )
         {
@@ -611,6 +613,14 @@ static int select_input( char *filename, char *resolution, const char *pipe_form
         }
         input = yuv_input;
     }
+    else
+    {
+#ifdef AVS_INPUT
+        input = avs_input;
+#else
+        input = yuv_input;
+#endif
+    }
 
     return 0;
 }
@@ -1223,7 +1233,7 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
     }
 
     /* Create a new pic */
-    if( x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height ) < 0 )
+    if( input.picture_alloc( &pic, param->i_csp, param->i_width, param->i_height ) )
     {
         fprintf( stderr, "x264 [error]: malloc failed\n" );
         return -1;
@@ -1257,6 +1267,9 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
 
         i_frame++;
 
+        if( input.release_frame && input.release_frame( &pic, opt->hin ) )
+            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 );
@@ -1275,7 +1288,7 @@ static int  Encode( x264_param_t *param, cli_opt_t *opt )
     }
 
     i_end = x264_mdate();
-    x264_picture_clean( &pic );
+    input.picture_clean( &pic );
     /* Erase progress indicator before printing encoding stats. */
     if( opt->b_progress )
         fprintf( stderr, "                                                                               \r" );
diff --git a/x264.h b/x264.h
index 99cabbae3469663f44440a874956bf3db9ad2205..b8e31136cdf2f4bb505459df77098843b9077d58 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -388,9 +388,11 @@ typedef struct
            of H.264 itself; in this case, the caller must force an IDR frame
            if it needs the changed parameter to apply immediately. */
     x264_param_t *param;
-
     /* In: raw data */
     x264_image_t img;
+    /* private user data. libx264 doesn't touch this,
+       not even copy it from input to output frames. */
+    void *opaque;
 } x264_picture_t;
 
 /* x264_picture_alloc: