]> git.sesse.net Git - x264/blob - input/avs.c
Enhanced Avisynth input support
[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 "muxers.h"
24 #include <windows.h>
25
26 /* the AVS interface currently uses __declspec to link function declarations to their definitions in the dll.
27    this has a side effect of preventing program execution if the avisynth dll is not found,
28    so define __declspec(dllimport) to nothing and work around this */
29 #undef __declspec
30 #define __declspec(i)
31 #undef EXTERN_C
32
33 #ifdef HAVE_AVISYNTH_C_H
34 #include <avisynth_c.h>
35 #else
36 #include "extras/avisynth_c.h"
37 #endif
38
39 /* AVS uses a versioned interface to control backwards compatibility */
40 /* YV12 support is required */
41 #define AVS_INTERFACE_YV12 2
42 /* when AVS supports other planar colorspaces, a workaround is required */
43 #define AVS_INTERFACE_OTHER_PLANAR 5
44
45 /* maximum size of the sequence of filters to try on non script files */
46 #define AVS_MAX_SEQUENCE 5
47
48 #define LOAD_AVS_FUNC(name, continue_on_fail) \
49 {\
50     h->func.name = (void*)GetProcAddress( h->library, #name );\
51     if( !continue_on_fail && !h->func.name )\
52         goto fail;\
53 }
54
55 typedef struct
56 {
57     AVS_Clip *clip;
58     AVS_ScriptEnvironment *env;
59     HMODULE library;
60     /* declare function pointers for the utilized functions to be loaded without __declspec,
61        as the avisynth header does not compensate for this type of usage */
62     struct
63     {
64         const char *(__stdcall *avs_clip_get_error)( AVS_Clip *clip );
65         AVS_ScriptEnvironment *(__stdcall *avs_create_script_environment)( int version );
66         void (__stdcall *avs_delete_script_environment)( AVS_ScriptEnvironment *env );
67         AVS_VideoFrame *(__stdcall *avs_get_frame)( AVS_Clip *clip, int n );
68         int (__stdcall *avs_get_version)( AVS_Clip *clip );
69         const AVS_VideoInfo *(__stdcall *avs_get_video_info)( AVS_Clip *clip );
70         int (__stdcall *avs_function_exists)( AVS_ScriptEnvironment *env, const char *name );
71         AVS_Value (__stdcall *avs_invoke)( AVS_ScriptEnvironment *env, const char *name,
72             AVS_Value args, const char **arg_names );
73         void (__stdcall *avs_release_clip)( AVS_Clip *clip );
74         void (__stdcall *avs_release_value)( AVS_Value value );
75         void (__stdcall *avs_release_video_frame)( AVS_VideoFrame *frame );
76         AVS_Clip *(__stdcall *avs_take_clip)( AVS_Value, AVS_ScriptEnvironment *env );
77     } func;
78 } avs_hnd_t;
79
80 /* load the library and functions we require from it */
81 static int avs_load_library( avs_hnd_t *h )
82 {
83     h->library = LoadLibrary( "avisynth" );
84     if( !h->library )
85         return -1;
86     LOAD_AVS_FUNC( avs_clip_get_error, 0 );
87     LOAD_AVS_FUNC( avs_create_script_environment, 0 );
88     LOAD_AVS_FUNC( avs_delete_script_environment, 1 );
89     LOAD_AVS_FUNC( avs_get_frame, 0 );
90     LOAD_AVS_FUNC( avs_get_version, 0 );
91     LOAD_AVS_FUNC( avs_get_video_info, 0 );
92     LOAD_AVS_FUNC( avs_function_exists, 0 );
93     LOAD_AVS_FUNC( avs_invoke, 0 );
94     LOAD_AVS_FUNC( avs_release_clip, 0 );
95     LOAD_AVS_FUNC( avs_release_value, 0 );
96     LOAD_AVS_FUNC( avs_release_video_frame, 0 );
97     LOAD_AVS_FUNC( avs_take_clip, 0 );
98     return 0;
99 fail:
100     FreeLibrary( h->library );
101     return -1;
102 }
103
104 /* generate a filter sequence to try based on the filename extension */
105 static void avs_build_filter_sequence( char *filename_ext, const char *filter[AVS_MAX_SEQUENCE+1] )
106 {
107     int i=0, j;
108     const char *all_purpose[] = { "FFmpegSource2", "DSS2", "DirectShowSource", 0 };
109     if( !strcasecmp( filename_ext, "avi" ) )
110         filter[i++] = "AVISource";
111     if( !strcasecmp( filename_ext, "d2v" ) )
112         filter[i++] = "MPEG2Source";
113     if( !strcasecmp( filename_ext, "dga" ) )
114         filter[i++] = "AVCSource";
115     for( j = 0; all_purpose[j] && i < AVS_MAX_SEQUENCE; j++ )
116         filter[i++] = all_purpose[j];
117 }
118
119 static int open_file( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param )
120 {
121     FILE *fh = fopen( psz_filename, "r" );
122     if( !fh )
123         return -1;
124     else if( !x264_is_regular_file( fh ) )
125     {
126         fprintf( stderr, "avs [error]: AVS input is incompatible with non-regular file `%s'\n", psz_filename );
127         return -1;
128     }
129     fclose( fh );
130
131     avs_hnd_t *h = malloc( sizeof(avs_hnd_t) );
132     if( !h )
133         return -1;
134     if( avs_load_library( h ) )
135     {
136         fprintf( stderr, "avs [error]: failed to load avisynth\n" );
137         return -1;
138     }
139     h->env = h->func.avs_create_script_environment( AVS_INTERFACE_YV12 );
140     if( !h->env )
141     {
142         fprintf( stderr, "avs [error]: failed to initiate avisynth\n" );
143         return -1;
144     }
145     AVS_Value arg = avs_new_value_string( psz_filename );
146     AVS_Value res;
147     char *filename_ext = get_filename_extension( psz_filename );
148
149     if( !strcasecmp( filename_ext, "avs" ) )
150     {
151         res = h->func.avs_invoke( h->env, "Import", arg, NULL );
152         if( avs_is_error( res ) )
153         {
154             fprintf( stderr, "avs [error]: %s\n", avs_as_string( res ) );
155             return -1;
156         }
157     }
158     else /* non script file */
159     {
160         /* cycle through known source filters to find one that works */
161         const char *filter[AVS_MAX_SEQUENCE+1] = { 0 };
162         avs_build_filter_sequence( filename_ext, filter );
163         int i;
164         for( i = 0; filter[i]; i++ )
165         {
166             fprintf( stderr, "avs [info]: Trying %s... ", filter[i] );
167             if( !h->func.avs_function_exists( h->env, filter[i] ) )
168             {
169                 fprintf( stderr, "not found\n" );
170                 continue;
171             }
172             if( !strncasecmp( filter[i], "FFmpegSource", 12 ) )
173             {
174                 fprintf( stderr, "Indexing... " );
175                 fflush( stderr );
176             }
177             res = h->func.avs_invoke( h->env, filter[i], arg, NULL );
178             if( !avs_is_error( res ) )
179             {
180                 fprintf( stderr, "succeeded\n" );
181                 break;
182             }
183             fprintf( stderr, "failed\n" );
184         }
185         if( !filter[i] )
186         {
187             fprintf( stderr, "avs [error]: unable to find source filter to open `%s'\n", psz_filename );
188             return -1;
189         }
190     }
191     if( !avs_is_clip( res ) )
192     {
193         fprintf( stderr, "avs [error]: `%s' didn't return a video clip\n", psz_filename );
194         return -1;
195     }
196     h->clip = h->func.avs_take_clip( res, h->env );
197     int avs_version = h->func.avs_get_version( h->clip );
198     const AVS_VideoInfo *vi = h->func.avs_get_video_info( h->clip );
199     if( !avs_has_video( vi ) )
200     {
201         fprintf( stderr, "avs [error]: `%s' has no video data\n", psz_filename );
202         return -1;
203     }
204     if( vi->width&1 || vi->height&1 )
205     {
206         fprintf( stderr, "avs [error]: input clip width or height not divisible by 2 (%dx%d)\n",
207                  vi->width, vi->height );
208         return -1;
209     }
210     /* always call ConvertToYV12 to convert non YV12 planar colorspaces to YV12 when user's AVS supports them,
211        as all planar colorspaces are flagged as YV12. If it is already YV12 in this case, the call does nothing */
212     if( !avs_is_yv12( vi ) || avs_version >= AVS_INTERFACE_OTHER_PLANAR )
213     {
214         h->func.avs_release_clip( h->clip );
215         fprintf( stderr, "avs [warning]: converting input clip to YV12\n" );
216         const char *arg_name[2] = { NULL, "interlaced" };
217         AVS_Value arg_arr[2] = { res, avs_new_value_bool( p_param->b_interlaced ) };
218         AVS_Value res2 = h->func.avs_invoke( h->env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name );
219         if( avs_is_error( res2 ) )
220         {
221             fprintf( stderr, "avs [error]: Couldn't convert input clip to YV12\n" );
222             return -1;
223         }
224         h->clip = h->func.avs_take_clip( res2, h->env );
225         h->func.avs_release_value( res2 );
226         vi = h->func.avs_get_video_info( h->clip );
227     }
228     h->func.avs_release_value( res );
229
230     p_param->i_width = vi->width;
231     p_param->i_height = vi->height;
232     p_param->i_fps_num = vi->fps_numerator;
233     p_param->i_fps_den = vi->fps_denominator;
234     p_param->i_csp = X264_CSP_YV12;
235
236     fprintf( stderr, "avs [info]: %dx%d @ %.2f fps (%d frames)\n",
237              p_param->i_width, p_param->i_height,
238              (double)p_param->i_fps_num / p_param->i_fps_den,
239              vi->num_frames );
240
241     *p_handle = h;
242     return 0;
243 }
244
245 static int get_frame_total( hnd_t handle )
246 {
247     avs_hnd_t *h = handle;
248     const AVS_VideoInfo *vi = h->func.avs_get_video_info( h->clip );
249     return vi->num_frames;
250 }
251
252 static int picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
253 {
254     pic->img.i_csp = i_csp;
255     pic->img.i_plane = 3;
256     pic->param = NULL;
257     return 0;
258 }
259
260 static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
261 {
262     static int plane[3] = { AVS_PLANAR_Y, AVS_PLANAR_V, AVS_PLANAR_U };
263     avs_hnd_t *h = handle;
264     AVS_VideoFrame *frm =
265     p_pic->opaque = h->func.avs_get_frame( h->clip, i_frame );
266     int i;
267     const char *err = h->func.avs_clip_get_error( h->clip );
268     if( err )
269     {
270         fprintf( stderr, "avs [error]: %s occurred while reading frame %d\n", err, i_frame );
271         return -1;
272     }
273     for( i = 0; i < 3; i++ )
274     {
275         /* explicitly cast away the const attribute to avoid a warning */
276         p_pic->img.plane[i] = (uint8_t*)avs_get_read_ptr_p( frm, plane[i] );
277         p_pic->img.i_stride[i] = avs_get_pitch_p( frm, plane[i] );
278     }
279     return 0;
280 }
281
282 static int release_frame( x264_picture_t *pic, hnd_t handle )
283 {
284     avs_hnd_t *h = handle;
285     h->func.avs_release_video_frame( pic->opaque );
286     return 0;
287 }
288
289 static void picture_clean( x264_picture_t *pic )
290 {
291     memset( pic, 0, sizeof(x264_picture_t) );
292 }
293
294 static int close_file( hnd_t handle )
295 {
296     avs_hnd_t *h = handle;
297     h->func.avs_release_clip( h->clip );
298     if( h->func.avs_delete_script_environment )
299         h->func.avs_delete_script_environment( h->env );
300     FreeLibrary( h->library );
301     free( h );
302     return 0;
303 }
304
305 cli_input_t avs_input = { open_file, get_frame_total, picture_alloc, read_frame, release_frame, picture_clean, close_file };