/*****************************************************************************
* avs.c: avisynth input
*****************************************************************************
- * Copyright (C) 2009-2011 x264 project
+ * Copyright (C) 2009-2016 x264 project
*
* Authors: Steven Walters <kemuri9@gmail.com>
*
*****************************************************************************/
#include "input.h"
+#if USE_AVXSYNTH
+#include <dlfcn.h>
+#if SYS_MACOSX
+#define avs_open() dlopen( "libavxsynth.dylib", RTLD_NOW )
+#else
+#define avs_open() dlopen( "libavxsynth.so", RTLD_NOW )
+#endif
+#define avs_close dlclose
+#define avs_address dlsym
+#else
#include <windows.h>
+#define avs_open() LoadLibraryW( L"avisynth" )
+#define avs_close FreeLibrary
+#define avs_address GetProcAddress
+#endif
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "avs", __VA_ARGS__ )
#define AVSC_NO_DECLSPEC
#undef EXTERN_C
+#if USE_AVXSYNTH
+#include "extras/avxsynth_c.h"
+#else
#include "extras/avisynth_c.h"
+#endif
#define AVSC_DECLARE_FUNC(name) name##_func name
/* AVS uses a versioned interface to control backwards compatibility */
#include <libavutil/pixfmt.h>
#endif
+/* AvxSynth doesn't have yv24, yv16, yv411, or y8, so disable them. */
+#if USE_AVXSYNTH
+#define avs_is_yv24( vi ) 0
+#define avs_is_yv16( vi ) 0
+#define avs_is_yv411( vi ) 0
+#define avs_is_y8( vi ) 0
+#endif
+
/* 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 );\
+ h->func.name = (void*)avs_address( h->library, #name );\
if( !continue_on_fail && !h->func.name )\
goto fail;\
}
{
AVS_Clip *clip;
AVS_ScriptEnvironment *env;
- HMODULE library;
+ void *library;
int num_frames;
struct
{
/* load the library and functions we require from it */
static int x264_avs_load_library( avs_hnd_t *h )
{
- h->library = LoadLibrary( "avisynth" );
+ h->library = avs_open();
if( !h->library )
return -1;
LOAD_AVS_FUNC( avs_clip_get_error, 0 );
LOAD_AVS_FUNC( avs_take_clip, 0 );
return 0;
fail:
- FreeLibrary( h->library );
+ avs_close( h->library );
return -1;
}
static void avs_build_filter_sequence( char *filename_ext, const char *filter[AVS_MAX_SEQUENCE+1] )
{
int i = 0;
+#if USE_AVXSYNTH
+ const char *all_purpose[] = { "FFVideoSource", 0 };
+#else
const char *all_purpose[] = { "FFmpegSource2", "DSS2", "DirectShowSource", 0 };
if( !strcasecmp( filename_ext, "avi" ) )
filter[i++] = "AVISource";
filter[i++] = "MPEG2Source";
if( !strcasecmp( filename_ext, "dga" ) )
filter[i++] = "AVCSource";
+#endif
for( int j = 0; all_purpose[j] && i < AVS_MAX_SEQUENCE; j++ )
filter[i++] = all_purpose[j];
}
static float get_avs_version( avs_hnd_t *h )
{
+/* AvxSynth has its version defined starting at 4.0, even though it's based on
+ AviSynth 2.5.8. This is troublesome for get_avs_version and working around
+ the new colorspaces in 2.6. So if AvxSynth is detected, explicitly define
+ the version as 2.58. */
+#if USE_AVXSYNTH
+ return 2.58f;
+#else
FAIL_IF_ERROR( !h->func.avs_function_exists( h->env, "VersionNumber" ), "VersionNumber does not exist\n" )
AVS_Value ver = h->func.avs_invoke( h->env, "VersionNumber", avs_new_value_array( NULL, 0 ), NULL );
FAIL_IF_ERROR( avs_is_error( ver ), "unable to determine avisynth version: %s\n", avs_as_error( ver ) )
float ret = avs_as_float( ver );
h->func.avs_release_value( ver );
return ret;
+#endif
}
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
- FILE *fh = fopen( psz_filename, "r" );
+ FILE *fh = x264_fopen( psz_filename, "r" );
if( !fh )
return -1;
- FAIL_IF_ERROR( !x264_is_regular_file( fh ), "AVS input is incompatible with non-regular file `%s'\n", psz_filename );
+ int b_regular = x264_is_regular_file( fh );
fclose( fh );
+ FAIL_IF_ERROR( !b_regular, "AVS input is incompatible with non-regular file `%s'\n", psz_filename );
avs_hnd_t *h = malloc( sizeof(avs_hnd_t) );
if( !h )
if( avs_version <= 0 )
return -1;
x264_cli_log( "avs", X264_LOG_DEBUG, "using avisynth version %.2f\n", avs_version );
+
+#ifdef _WIN32
+ /* Avisynth doesn't support Unicode filenames. */
+ char ansi_filename[MAX_PATH];
+ FAIL_IF_ERROR( !x264_ansi_filename( psz_filename, ansi_filename, MAX_PATH, 0 ), "invalid ansi filename\n" );
+ AVS_Value arg = avs_new_value_string( ansi_filename );
+#else
AVS_Value arg = avs_new_value_string( psz_filename );
+#endif
+
AVS_Value res;
char *filename_ext = get_filename_extension( psz_filename );
}
#if !HAVE_SWSCALE
/* if swscale is not available, convert the CSP if necessary */
+ FAIL_IF_ERROR( avs_version < 2.6f && (opt->output_csp == X264_CSP_I422 || opt->output_csp == X264_CSP_I444),
+ "avisynth >= 2.6 is required for i422/i444 output\n" )
if( (opt->output_csp == X264_CSP_I420 && !avs_is_yv12( vi )) || (opt->output_csp == X264_CSP_I422 && !avs_is_yv16( vi )) ||
(opt->output_csp == X264_CSP_I444 && !avs_is_yv24( vi )) || (opt->output_csp == X264_CSP_RGB && !avs_is_rgb( vi )) )
{
- FAIL_IF_ERROR( avs_version < 2.6f && (opt->output_csp == X264_CSP_I422 || opt->output_csp == X264_CSP_I444),
- "avisynth >= 2.6 is required for i422/i444 output\n" )
const char *csp = opt->output_csp == X264_CSP_I420 ? "YV12" :
opt->output_csp == X264_CSP_I422 ? "YV16" :
"input clip height not divisible by 4 (%dx%d)\n", vi->width, vi->height )
FAIL_IF_ERROR( (opt->output_csp == X264_CSP_I420 || info->interlaced) && (vi->height&1),
"input clip 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 ) };
- char conv_func[14] = { "ConvertTo" };
- strcat( conv_func, csp );
- AVS_Value res2 = h->func.avs_invoke( h->env, conv_func, avs_new_value_array( arg_arr, 2 ), arg_name );
+ char conv_func[14];
+ snprintf( conv_func, sizeof(conv_func), "ConvertTo%s", csp );
+ char matrix[7] = "";
+ int arg_count = 2;
+ /* if doing a rgb <-> yuv conversion then range is handled via 'matrix'. though it's only supported in 2.56+ */
+ if( avs_version >= 2.56f && ((opt->output_csp == X264_CSP_RGB && avs_is_yuv( vi )) || (opt->output_csp != X264_CSP_RGB && avs_is_rgb( vi ))) )
+ {
+ // if converting from yuv, then we specify the matrix for the input, otherwise use the output's.
+ int use_pc_matrix = avs_is_yuv( vi ) ? opt->input_range == RANGE_PC : opt->output_range == RANGE_PC;
+ snprintf( matrix, sizeof(matrix), "%s601", use_pc_matrix ? "PC." : "Rec" ); /* FIXME: use correct coefficients */
+ arg_count++;
+ // notification that the input range has changed to the desired one
+ opt->input_range = opt->output_range;
+ }
+ const char *arg_name[] = { NULL, "interlaced", "matrix" };
+ AVS_Value arg_arr[3];
+ arg_arr[0] = res;
+ arg_arr[1] = avs_new_value_bool( info->interlaced );
+ arg_arr[2] = avs_new_value_string( matrix );
+ AVS_Value res2 = h->func.avs_invoke( h->env, conv_func, avs_new_value_array( arg_arr, arg_count ), arg_name );
FAIL_IF_ERROR( avs_is_error( res2 ), "couldn't convert input clip to %s\n", csp )
res = update_clip( h, &vi, res2, res );
}
+ /* if swscale is not available, change the range if necessary. This only applies to YUV-based CSPs however */
+ if( avs_is_yuv( vi ) && opt->output_range != RANGE_AUTO && ((opt->input_range == RANGE_PC) != opt->output_range) )
+ {
+ const char *levels = opt->output_range ? "TV->PC" : "PC->TV";
+ x264_cli_log( "avs", X264_LOG_WARNING, "performing %s conversion\n", levels );
+ AVS_Value arg_arr[2];
+ arg_arr[0] = res;
+ arg_arr[1] = avs_new_value_string( levels );
+ const char *arg_name[] = { NULL, "levels" };
+ AVS_Value res2 = h->func.avs_invoke( h->env, "ColorYUV", avs_new_value_array( arg_arr, 2 ), arg_name );
+ FAIL_IF_ERROR( avs_is_error( res2 ), "couldn't convert range: %s\n", avs_as_error( res2 ) )
+ res = update_clip( h, &vi, res2, res );
+ // notification that the input range has changed to the desired one
+ opt->input_range = opt->output_range;
+ }
#endif
+
h->func.avs_release_value( res );
info->width = vi->width;
info->csp = X264_CSP_I420;
#if HAVE_SWSCALE
else if( avs_is_yuy2( vi ) )
- info->csp = PIX_FMT_YUYV422 | X264_CSP_OTHER;
+ info->csp = AV_PIX_FMT_YUYV422 | X264_CSP_OTHER;
else if( avs_is_yv411( vi ) )
- info->csp = PIX_FMT_YUV411P | X264_CSP_OTHER;
+ info->csp = AV_PIX_FMT_YUV411P | X264_CSP_OTHER;
else if( avs_is_y8( vi ) )
- info->csp = PIX_FMT_GRAY8 | X264_CSP_OTHER;
+ info->csp = AV_PIX_FMT_GRAY8 | X264_CSP_OTHER;
#endif
else
info->csp = X264_CSP_NONE;
return 0;
}
-static int picture_alloc( cli_pic_t *pic, int csp, int width, int height )
+static int picture_alloc( cli_pic_t *pic, hnd_t handle, int csp, int width, int height )
{
if( x264_cli_pic_alloc( pic, X264_CSP_NONE, width, height ) )
return -1;
if( cli_csp )
pic->img.planes = cli_csp->planes;
#if HAVE_SWSCALE
- else if( csp == (PIX_FMT_YUV411P | X264_CSP_OTHER) )
+ else if( csp == (AV_PIX_FMT_YUV411P | X264_CSP_OTHER) )
pic->img.planes = 3;
else
pic->img.planes = 1; //y8 and yuy2 are one plane
return 0;
}
-static void picture_clean( cli_pic_t *pic )
+static void picture_clean( cli_pic_t *pic, hnd_t handle )
{
memset( pic, 0, sizeof(cli_pic_t) );
}
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 );
+ avs_close( h->library );
free( h );
return 0;
}