X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=input%2Favs.c;h=6eab41f91d29a9253c728bc00ea58aa3124f4cff;hb=d23d18655249944c1ca894b451e2c82c7a584c62;hp=5489a5e58ae8d3b2917a646225f432cbd85fa4ff;hpb=54e784fdf410bf6dd7dd2312251fbe576a0d03fd;p=x264 diff --git a/input/avs.c b/input/avs.c index 5489a5e5..6eab41f9 100644 --- a/input/avs.c +++ b/input/avs.c @@ -1,7 +1,7 @@ /***************************************************************************** - * avs.c: x264 avisynth input module + * avs.c: avisynth input ***************************************************************************** - * Copyright (C) 2009 x264 project + * Copyright (C) 2009-2016 x264 project * * Authors: Steven Walters * @@ -18,36 +18,60 @@ * 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. + * + * This program is also available under a commercial proprietary license. + * For more information, contact us at licensing@x264.com. *****************************************************************************/ -#include "muxers.h" +#include "input.h" +#if USE_AVXSYNTH +#include +#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 +#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__ ) -/* 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) +#define AVSC_NO_DECLSPEC #undef EXTERN_C - -#ifdef HAVE_AVISYNTH_C_H -#include +#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 */ -/* 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 +/* YV12 support is required, which was added in 2.5 */ +#define AVS_INTERFACE_25 2 + +#if HAVE_SWSCALE +#include +#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;\ } @@ -56,39 +80,36 @@ typedef struct { AVS_Clip *clip; AVS_ScriptEnvironment *env; - HMODULE library; + void *library; int num_frames; - /* 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 ); + AVSC_DECLARE_FUNC( avs_clip_get_error ); + AVSC_DECLARE_FUNC( avs_create_script_environment ); + AVSC_DECLARE_FUNC( avs_delete_script_environment ); + AVSC_DECLARE_FUNC( avs_get_error ); + AVSC_DECLARE_FUNC( avs_get_frame ); + AVSC_DECLARE_FUNC( avs_get_video_info ); + AVSC_DECLARE_FUNC( avs_function_exists ); + AVSC_DECLARE_FUNC( avs_invoke ); + AVSC_DECLARE_FUNC( avs_release_clip ); + AVSC_DECLARE_FUNC( avs_release_value ); + AVSC_DECLARE_FUNC( avs_release_video_frame ); + AVSC_DECLARE_FUNC( avs_take_clip ); } func; } avs_hnd_t; /* load the library and functions we require from it */ -static int avs_load_library( avs_hnd_t *h ) +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_create_script_environment, 0 ); LOAD_AVS_FUNC( avs_delete_script_environment, 1 ); + LOAD_AVS_FUNC( avs_get_error, 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 ); @@ -98,7 +119,7 @@ static int avs_load_library( avs_hnd_t *h ) LOAD_AVS_FUNC( avs_take_clip, 0 ); return 0; fail: - FreeLibrary( h->library ); + avs_close( h->library ); return -1; } @@ -106,6 +127,9 @@ fail: 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"; @@ -113,6 +137,7 @@ static void avs_build_filter_sequence( char *filename_ext, const char *filter[AV 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]; } @@ -126,44 +151,65 @@ static AVS_Value update_clip( avs_hnd_t *h, const AVS_VideoInfo **vi, AVS_Value return res; } +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 ) ) + FAIL_IF_ERROR( !avs_is_float( ver ), "VersionNumber did not return a float value\n" ); + 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; - 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; - } + 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 ) return -1; - if( avs_load_library( h ) ) + FAIL_IF_ERROR( x264_avs_load_library( h ), "failed to load avisynth\n" ) + h->env = h->func.avs_create_script_environment( AVS_INTERFACE_25 ); + if( h->func.avs_get_error ) { - fprintf( stderr, "avs [error]: failed to load avisynth\n" ); - return -1; + const char *error = h->func.avs_get_error( h->env ); + FAIL_IF_ERROR( error, "%s\n", error ); } - h->env = h->func.avs_create_script_environment( AVS_INTERFACE_YV12 ); - if( !h->env ) - { - fprintf( stderr, "avs [error]: failed to initiate avisynth\n" ); + float avs_version = get_avs_version( 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( !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; - } + FAIL_IF_ERROR( avs_is_error( res ), "%s\n", avs_as_string( res ) ) /* check if the user is using a multi-threaded script and apply distributor if necessary. adapted from avisynth's vfw interface */ AVS_Value mt_test = h->func.avs_invoke( h->env, "GetMTMode", avs_new_value_bool( 0 ), NULL ); @@ -184,141 +230,178 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c int i; for( i = 0; filter[i]; i++ ) { - fprintf( stderr, "avs [info]: trying %s... ", filter[i] ); + x264_cli_log( "avs", X264_LOG_INFO, "trying %s... ", filter[i] ); if( !h->func.avs_function_exists( h->env, filter[i] ) ) { - fprintf( stderr, "not found\n" ); + x264_cli_printf( X264_LOG_INFO, "not found\n" ); continue; } if( !strncasecmp( filter[i], "FFmpegSource", 12 ) ) { - fprintf( stderr, "indexing... " ); + x264_cli_printf( X264_LOG_INFO, "indexing... " ); fflush( stderr ); } res = h->func.avs_invoke( h->env, filter[i], arg, NULL ); if( !avs_is_error( res ) ) { - fprintf( stderr, "succeeded\n" ); + x264_cli_printf( X264_LOG_INFO, "succeeded\n" ); break; } - fprintf( stderr, "failed\n" ); + x264_cli_printf( X264_LOG_INFO, "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; + FAIL_IF_ERROR( !filter[i], "unable to find source filter to open `%s'\n", psz_filename ) } + FAIL_IF_ERROR( !avs_is_clip( res ), "`%s' didn't return a video clip\n", psz_filename ) 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; - } + FAIL_IF_ERROR( !avs_has_video( vi ), "`%s' has no video data\n", psz_filename ) /* if the clip is made of fields instead of frames, call weave to make them frames */ if( avs_is_field_based( vi ) ) { - fprintf( stderr, "avs [warning]: detected fieldbased (separated) input, weaving to frames\n" ); + x264_cli_log( "avs", X264_LOG_WARNING, "detected fieldbased (separated) input, weaving to frames\n" ); AVS_Value tmp = h->func.avs_invoke( h->env, "Weave", res, NULL ); - if( avs_is_error( tmp ) ) - { - fprintf( stderr, "avs [error]: couldn't weave fields into frames\n" ); - return -1; - } + FAIL_IF_ERROR( avs_is_error( tmp ), "couldn't weave fields into frames\n" ) res = update_clip( h, &vi, tmp, res ); info->interlaced = 1; info->tff = avs_is_tff( vi ); } - if( vi->width&1 || vi->height&1 ) +#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 )) ) { - 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 ) - { - fprintf( stderr, "avs %s\n", !avs_is_yv12( vi ) ? "[warning]: converting input clip to YV12" - : "[info]: avisynth 2.6+ detected, forcing conversion to YV12" ); - const char *arg_name[2] = { NULL, "interlaced" }; - AVS_Value arg_arr[2] = { res, avs_new_value_bool( info->interlaced ) }; - AVS_Value res2 = h->func.avs_invoke( h->env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name ); - if( avs_is_error( res2 ) ) + + const char *csp = opt->output_csp == X264_CSP_I420 ? "YV12" : + opt->output_csp == X264_CSP_I422 ? "YV16" : + opt->output_csp == X264_CSP_I444 ? "YV24" : "RGB"; + x264_cli_log( "avs", X264_LOG_WARNING, "converting input clip to %s\n", csp ); + FAIL_IF_ERROR( opt->output_csp < X264_CSP_I444 && (vi->width&1), + "input clip width not divisible by 2 (%dx%d)\n", vi->width, vi->height ) + FAIL_IF_ERROR( opt->output_csp == X264_CSP_I420 && info->interlaced && (vi->height&3), + "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 ) + 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 ))) ) { - fprintf( stderr, "avs [error]: couldn't convert input clip to YV12\n" ); - return -1; + // 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->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( 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_yv24( vi ) ) + info->csp = X264_CSP_I444; + else if( avs_is_yv16( vi ) ) + info->csp = X264_CSP_I422; + else if( avs_is_yv12( vi ) ) + info->csp = X264_CSP_I420; +#if HAVE_SWSCALE + else if( avs_is_yuy2( vi ) ) + info->csp = AV_PIX_FMT_YUYV422 | X264_CSP_OTHER; + else if( avs_is_yv411( vi ) ) + info->csp = AV_PIX_FMT_YUV411P | X264_CSP_OTHER; + else if( avs_is_y8( vi ) ) + info->csp = AV_PIX_FMT_GRAY8 | X264_CSP_OTHER; +#endif + else + info->csp = X264_CSP_NONE; info->vfr = 0; *p_handle = h; return 0; } -static int get_frame_total( hnd_t handle ) -{ - 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 ) +static int picture_alloc( cli_pic_t *pic, hnd_t handle, int csp, int width, int height ) { - pic->img.i_csp = i_csp; - pic->img.i_plane = 3; - pic->param = NULL; - pic->i_pic_struct = PIC_STRUCT_AUTO; + 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 ); + if( cli_csp ) + pic->img.planes = cli_csp->planes; +#if HAVE_SWSCALE + 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 +#endif 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 ); - if( err ) - { - fprintf( stderr, "avs [error]: %s occurred while reading frame %d\n", err, i_frame ); - return -1; - } - for( int i = 0; i < 3; i++ ) + FAIL_IF_ERROR( err, "%s occurred while reading frame %d\n", err, i_frame ) + 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, hnd_t handle ) { - memset( pic, 0, sizeof(x264_picture_t) ); + memset( pic, 0, sizeof(cli_pic_t) ); } static int close_file( hnd_t handle ) @@ -327,9 +410,9 @@ static int close_file( hnd_t 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 ); + avs_close( h->library ); free( h ); 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 };