]> git.sesse.net Git - x264/blob - input/avs.c
Add video filtering system to x264cli
[x264] / input / avs.c
1 /*****************************************************************************
2  * avs.c: x264 avisynth input module
3  *****************************************************************************
4  * Copyright (C) 2009 x264 project
5  *
6  * Authors: Steven Walters <kemuri9@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 #include "input.h"
24 #include <windows.h>
25 #define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "avs", __VA_ARGS__ )
26
27 /* the AVS interface currently uses __declspec to link function declarations to their definitions in the dll.
28    this has a side effect of preventing program execution if the avisynth dll is not found,
29    so define __declspec(dllimport) to nothing and work around this */
30 #undef __declspec
31 #define __declspec(i)
32 #undef EXTERN_C
33
34 #if HAVE_AVISYNTH_C_H
35 #include <avisynth_c.h>
36 #else
37 #include "extras/avisynth_c.h"
38 #endif
39
40 /* AVS uses a versioned interface to control backwards compatibility */
41 /* YV12 support is required */
42 #define AVS_INTERFACE_YV12 2
43 /* when AVS supports other planar colorspaces, a workaround is required */
44 #define AVS_INTERFACE_OTHER_PLANAR 5
45
46 #if HAVE_SWSCALE
47 #include <libavutil/pixfmt.h>
48 #endif
49
50 /* maximum size of the sequence of filters to try on non script files */
51 #define AVS_MAX_SEQUENCE 5
52
53 #define LOAD_AVS_FUNC(name, continue_on_fail)\
54 {\
55     h->func.name = (void*)GetProcAddress( h->library, #name );\
56     if( !continue_on_fail && !h->func.name )\
57         goto fail;\
58 }
59
60 typedef struct
61 {
62     AVS_Clip *clip;
63     AVS_ScriptEnvironment *env;
64     HMODULE library;
65     int num_frames;
66     /* declare function pointers for the utilized functions to be loaded without __declspec,
67        as the avisynth header does not compensate for this type of usage */
68     struct
69     {
70         const char *(__stdcall *avs_clip_get_error)( AVS_Clip *clip );
71         AVS_ScriptEnvironment *(__stdcall *avs_create_script_environment)( int version );
72         void (__stdcall *avs_delete_script_environment)( AVS_ScriptEnvironment *env );
73         AVS_VideoFrame *(__stdcall *avs_get_frame)( AVS_Clip *clip, int n );
74         int (__stdcall *avs_get_version)( AVS_Clip *clip );
75         const AVS_VideoInfo *(__stdcall *avs_get_video_info)( AVS_Clip *clip );
76         int (__stdcall *avs_function_exists)( AVS_ScriptEnvironment *env, const char *name );
77         AVS_Value (__stdcall *avs_invoke)( AVS_ScriptEnvironment *env, const char *name,
78             AVS_Value args, const char **arg_names );
79         void (__stdcall *avs_release_clip)( AVS_Clip *clip );
80         void (__stdcall *avs_release_value)( AVS_Value value );
81         void (__stdcall *avs_release_video_frame)( AVS_VideoFrame *frame );
82         AVS_Clip *(__stdcall *avs_take_clip)( AVS_Value, AVS_ScriptEnvironment *env );
83     } func;
84 } avs_hnd_t;
85
86 /* load the library and functions we require from it */
87 static int avs_load_library( avs_hnd_t *h )
88 {
89     h->library = LoadLibrary( "avisynth" );
90     if( !h->library )
91         return -1;
92     LOAD_AVS_FUNC( avs_clip_get_error, 0 );
93     LOAD_AVS_FUNC( avs_create_script_environment, 0 );
94     LOAD_AVS_FUNC( avs_delete_script_environment, 1 );
95     LOAD_AVS_FUNC( avs_get_frame, 0 );
96     LOAD_AVS_FUNC( avs_get_version, 0 );
97     LOAD_AVS_FUNC( avs_get_video_info, 0 );
98     LOAD_AVS_FUNC( avs_function_exists, 0 );
99     LOAD_AVS_FUNC( avs_invoke, 0 );
100     LOAD_AVS_FUNC( avs_release_clip, 0 );
101     LOAD_AVS_FUNC( avs_release_value, 0 );
102     LOAD_AVS_FUNC( avs_release_video_frame, 0 );
103     LOAD_AVS_FUNC( avs_take_clip, 0 );
104     return 0;
105 fail:
106     FreeLibrary( h->library );
107     return -1;
108 }
109
110 /* generate a filter sequence to try based on the filename extension */
111 static void avs_build_filter_sequence( char *filename_ext, const char *filter[AVS_MAX_SEQUENCE+1] )
112 {
113     int i = 0;
114     const char *all_purpose[] = { "FFmpegSource2", "DSS2", "DirectShowSource", 0 };
115     if( !strcasecmp( filename_ext, "avi" ) )
116         filter[i++] = "AVISource";
117     if( !strcasecmp( filename_ext, "d2v" ) )
118         filter[i++] = "MPEG2Source";
119     if( !strcasecmp( filename_ext, "dga" ) )
120         filter[i++] = "AVCSource";
121     for( int j = 0; all_purpose[j] && i < AVS_MAX_SEQUENCE; j++ )
122         filter[i++] = all_purpose[j];
123 }
124
125 static AVS_Value update_clip( avs_hnd_t *h, const AVS_VideoInfo **vi, AVS_Value res, AVS_Value release )
126 {
127     h->func.avs_release_clip( h->clip );
128     h->clip = h->func.avs_take_clip( res, h->env );
129     h->func.avs_release_value( release );
130     *vi = h->func.avs_get_video_info( h->clip );
131     return res;
132 }
133
134 static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
135 {
136     FILE *fh = fopen( psz_filename, "r" );
137     if( !fh )
138         return -1;
139     FAIL_IF_ERROR( !x264_is_regular_file( fh ), "AVS input is incompatible with non-regular file `%s'\n", psz_filename );
140     fclose( fh );
141
142     avs_hnd_t *h = malloc( sizeof(avs_hnd_t) );
143     if( !h )
144         return -1;
145     FAIL_IF_ERROR( avs_load_library( h ), "failed to load avisynth\n" )
146     h->env = h->func.avs_create_script_environment( AVS_INTERFACE_YV12 );
147     FAIL_IF_ERROR( !h->env, "failed to initiate avisynth\n" )
148     AVS_Value arg = avs_new_value_string( psz_filename );
149     AVS_Value res;
150     char *filename_ext = get_filename_extension( psz_filename );
151
152     if( !strcasecmp( filename_ext, "avs" ) )
153     {
154         res = h->func.avs_invoke( h->env, "Import", arg, NULL );
155         FAIL_IF_ERROR( avs_is_error( res ), "%s\n", avs_as_string( res ) )
156         /* check if the user is using a multi-threaded script and apply distributor if necessary.
157            adapted from avisynth's vfw interface */
158         AVS_Value mt_test = h->func.avs_invoke( h->env, "GetMTMode", avs_new_value_bool( 0 ), NULL );
159         int mt_mode = avs_is_int( mt_test ) ? avs_as_int( mt_test ) : 0;
160         h->func.avs_release_value( mt_test );
161         if( mt_mode > 0 && mt_mode < 5 )
162         {
163             AVS_Value temp = h->func.avs_invoke( h->env, "Distributor", res, NULL );
164             h->func.avs_release_value( res );
165             res = temp;
166         }
167     }
168     else /* non script file */
169     {
170         /* cycle through known source filters to find one that works */
171         const char *filter[AVS_MAX_SEQUENCE+1] = { 0 };
172         avs_build_filter_sequence( filename_ext, filter );
173         int i;
174         for( i = 0; filter[i]; i++ )
175         {
176             x264_cli_log( "avs", X264_LOG_INFO, "trying %s... ", filter[i] );
177             if( !h->func.avs_function_exists( h->env, filter[i] ) )
178             {
179                 x264_cli_printf( X264_LOG_INFO, "not found\n" );
180                 continue;
181             }
182             if( !strncasecmp( filter[i], "FFmpegSource", 12 ) )
183             {
184                 x264_cli_printf( X264_LOG_INFO, "indexing... " );
185                 fflush( stderr );
186             }
187             res = h->func.avs_invoke( h->env, filter[i], arg, NULL );
188             if( !avs_is_error( res ) )
189             {
190                 x264_cli_printf( X264_LOG_INFO, "succeeded\n" );
191                 break;
192             }
193             x264_cli_printf( X264_LOG_INFO, "failed\n" );
194         }
195         FAIL_IF_ERROR( !filter[i], "unable to find source filter to open `%s'\n", psz_filename )
196     }
197     FAIL_IF_ERROR( !avs_is_clip( res ), "`%s' didn't return a video clip\n", psz_filename )
198     h->clip = h->func.avs_take_clip( res, h->env );
199     int avs_version = h->func.avs_get_version( h->clip );
200     const AVS_VideoInfo *vi = h->func.avs_get_video_info( h->clip );
201     FAIL_IF_ERROR( !avs_has_video( vi ), "`%s' has no video data\n", psz_filename )
202     /* if the clip is made of fields instead of frames, call weave to make them frames */
203     if( avs_is_field_based( vi ) )
204     {
205         x264_cli_log( "avs", X264_LOG_WARNING, "detected fieldbased (separated) input, weaving to frames\n" );
206         AVS_Value tmp = h->func.avs_invoke( h->env, "Weave", res, NULL );
207         FAIL_IF_ERROR( avs_is_error( tmp ), "couldn't weave fields into frames\n" )
208         res = update_clip( h, &vi, tmp, res );
209         info->interlaced = 1;
210         info->tff = avs_is_tff( vi );
211     }
212     /* if swscale is available, convert CSPs with it rather than with avisynth. */
213 #if HAVE_SWSCALE
214     int convert_to_yv12 = 0;
215 #else
216     int convert_to_yv12 = !avs_is_yv12( vi );
217 #endif
218     /* always call ConvertToYV12 to convert non YV12 planar colorspaces to YV12 when user's AVS supports them,
219        as all planar colorspaces are flagged as YV12. If it is already YV12 in this case, the call does nothing */
220     if( convert_to_yv12 || (avs_version >= AVS_INTERFACE_OTHER_PLANAR && avs_is_yv12( vi )) )
221     {
222         if( convert_to_yv12 )
223             x264_cli_log( "avs", X264_LOG_WARNING, "converting input clip to YV12" );
224         else
225             x264_cli_log( "avs", X264_LOG_INFO, "avisynth 2.6+ detected, forcing conversion to YV12" );
226         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 )
227         const char *arg_name[2] = { NULL, "interlaced" };
228         AVS_Value arg_arr[2] = { res, avs_new_value_bool( info->interlaced ) };
229         AVS_Value res2 = h->func.avs_invoke( h->env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name );
230         FAIL_IF_ERROR( avs_is_error( res2 ), "couldn't convert input clip to YV12\n" )
231         res = update_clip( h, &vi, res2, res );
232     }
233     h->func.avs_release_value( res );
234
235     info->width   = vi->width;
236     info->height  = vi->height;
237     info->fps_num = vi->fps_numerator;
238     info->fps_den = vi->fps_denominator;
239     h->num_frames = info->num_frames = vi->num_frames;
240     info->thread_safe = 1;
241 #if HAVE_SWSCALE
242     if( avs_is_rgb32( vi ) )
243         info->csp = X264_CSP_BGRA | X264_CSP_VFLIP;
244     else if( avs_is_rgb24( vi ) )
245         info->csp = X264_CSP_BGR | X264_CSP_VFLIP;
246     else if( avs_is_yuy2( vi ) )
247         info->csp = PIX_FMT_YUYV422 | X264_CSP_OTHER;
248     else /* yv12 */
249         info->csp = X264_CSP_I420;
250 #else
251     info->csp = X264_CSP_I420;
252 #endif
253     info->vfr = 0;
254
255     *p_handle = h;
256     return 0;
257 }
258
259 static int picture_alloc( cli_pic_t *pic, int csp, int width, int height )
260 {
261     if( x264_cli_pic_alloc( pic, X264_CSP_NONE, width, height ) )
262         return -1;
263     pic->img.csp = csp;
264     const x264_cli_csp_t *cli_csp = x264_cli_get_csp( csp );
265     pic->img.planes = cli_csp ? cli_csp->planes : 1;
266     return 0;
267 }
268
269 static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
270 {
271     static const int plane[3] = { AVS_PLANAR_Y, AVS_PLANAR_U, AVS_PLANAR_V };
272     avs_hnd_t *h = handle;
273     if( i_frame >= h->num_frames )
274         return -1;
275     AVS_VideoFrame *frm = pic->opaque = h->func.avs_get_frame( h->clip, i_frame );
276     const char *err = h->func.avs_clip_get_error( h->clip );
277     FAIL_IF_ERROR( err, "%s occurred while reading frame %d\n", err, i_frame )
278     for( int i = 0; i < pic->img.planes; i++ )
279     {
280         /* explicitly cast away the const attribute to avoid a warning */
281         pic->img.plane[i] = (uint8_t*)avs_get_read_ptr_p( frm, plane[i] );
282         pic->img.stride[i] = avs_get_pitch_p( frm, plane[i] );
283     }
284     return 0;
285 }
286
287 static int release_frame( cli_pic_t *pic, hnd_t handle )
288 {
289     avs_hnd_t *h = handle;
290     h->func.avs_release_video_frame( pic->opaque );
291     return 0;
292 }
293
294 static void picture_clean( cli_pic_t *pic )
295 {
296     memset( pic, 0, sizeof(cli_pic_t) );
297 }
298
299 static int close_file( hnd_t handle )
300 {
301     avs_hnd_t *h = handle;
302     h->func.avs_release_clip( h->clip );
303     if( h->func.avs_delete_script_environment )
304         h->func.avs_delete_script_environment( h->env );
305     FreeLibrary( h->library );
306     free( h );
307     return 0;
308 }
309
310 const cli_input_t avs_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };