]> git.sesse.net Git - x264/blob - input/avs.c
Mark cli_input/output_t variables as const when possible
[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     int num_frames;
61     /* declare function pointers for the utilized functions to be loaded without __declspec,
62        as the avisynth header does not compensate for this type of usage */
63     struct
64     {
65         const char *(__stdcall *avs_clip_get_error)( AVS_Clip *clip );
66         AVS_ScriptEnvironment *(__stdcall *avs_create_script_environment)( int version );
67         void (__stdcall *avs_delete_script_environment)( AVS_ScriptEnvironment *env );
68         AVS_VideoFrame *(__stdcall *avs_get_frame)( AVS_Clip *clip, int n );
69         int (__stdcall *avs_get_version)( AVS_Clip *clip );
70         const AVS_VideoInfo *(__stdcall *avs_get_video_info)( AVS_Clip *clip );
71         int (__stdcall *avs_function_exists)( AVS_ScriptEnvironment *env, const char *name );
72         AVS_Value (__stdcall *avs_invoke)( AVS_ScriptEnvironment *env, const char *name,
73             AVS_Value args, const char **arg_names );
74         void (__stdcall *avs_release_clip)( AVS_Clip *clip );
75         void (__stdcall *avs_release_value)( AVS_Value value );
76         void (__stdcall *avs_release_video_frame)( AVS_VideoFrame *frame );
77         AVS_Clip *(__stdcall *avs_take_clip)( AVS_Value, AVS_ScriptEnvironment *env );
78     } func;
79 } avs_hnd_t;
80
81 /* load the library and functions we require from it */
82 static int avs_load_library( avs_hnd_t *h )
83 {
84     h->library = LoadLibrary( "avisynth" );
85     if( !h->library )
86         return -1;
87     LOAD_AVS_FUNC( avs_clip_get_error, 0 );
88     LOAD_AVS_FUNC( avs_create_script_environment, 0 );
89     LOAD_AVS_FUNC( avs_delete_script_environment, 1 );
90     LOAD_AVS_FUNC( avs_get_frame, 0 );
91     LOAD_AVS_FUNC( avs_get_version, 0 );
92     LOAD_AVS_FUNC( avs_get_video_info, 0 );
93     LOAD_AVS_FUNC( avs_function_exists, 0 );
94     LOAD_AVS_FUNC( avs_invoke, 0 );
95     LOAD_AVS_FUNC( avs_release_clip, 0 );
96     LOAD_AVS_FUNC( avs_release_value, 0 );
97     LOAD_AVS_FUNC( avs_release_video_frame, 0 );
98     LOAD_AVS_FUNC( avs_take_clip, 0 );
99     return 0;
100 fail:
101     FreeLibrary( h->library );
102     return -1;
103 }
104
105 /* generate a filter sequence to try based on the filename extension */
106 static void avs_build_filter_sequence( char *filename_ext, const char *filter[AVS_MAX_SEQUENCE+1] )
107 {
108     int i=0, j;
109     const char *all_purpose[] = { "FFmpegSource2", "DSS2", "DirectShowSource", 0 };
110     if( !strcasecmp( filename_ext, "avi" ) )
111         filter[i++] = "AVISource";
112     if( !strcasecmp( filename_ext, "d2v" ) )
113         filter[i++] = "MPEG2Source";
114     if( !strcasecmp( filename_ext, "dga" ) )
115         filter[i++] = "AVCSource";
116     for( j = 0; all_purpose[j] && i < AVS_MAX_SEQUENCE; j++ )
117         filter[i++] = all_purpose[j];
118 }
119
120 static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
121 {
122     FILE *fh = fopen( psz_filename, "r" );
123     if( !fh )
124         return -1;
125     else if( !x264_is_regular_file( fh ) )
126     {
127         fprintf( stderr, "avs [error]: AVS input is incompatible with non-regular file `%s'\n", psz_filename );
128         return -1;
129     }
130     fclose( fh );
131
132     avs_hnd_t *h = malloc( sizeof(avs_hnd_t) );
133     if( !h )
134         return -1;
135     if( avs_load_library( h ) )
136     {
137         fprintf( stderr, "avs [error]: failed to load avisynth\n" );
138         return -1;
139     }
140     h->env = h->func.avs_create_script_environment( AVS_INTERFACE_YV12 );
141     if( !h->env )
142     {
143         fprintf( stderr, "avs [error]: failed to initiate avisynth\n" );
144         return -1;
145     }
146     AVS_Value arg = avs_new_value_string( psz_filename );
147     AVS_Value res;
148     char *filename_ext = get_filename_extension( psz_filename );
149
150     if( !strcasecmp( filename_ext, "avs" ) )
151     {
152         res = h->func.avs_invoke( h->env, "Import", arg, NULL );
153         if( avs_is_error( res ) )
154         {
155             fprintf( stderr, "avs [error]: %s\n", avs_as_string( res ) );
156             return -1;
157         }
158         /* check if the user is using a multi-threaded script and apply distributor if necessary.
159            adapted from avisynth's vfw interface */
160         AVS_Value mt_test = h->func.avs_invoke( h->env, "GetMTMode", avs_new_value_bool( 0 ), NULL );
161         int mt_mode = avs_is_int( mt_test ) ? avs_as_int( mt_test ) : 0;
162         h->func.avs_release_value( mt_test );
163         if( mt_mode > 0 && mt_mode < 5 )
164         {
165             AVS_Value temp = h->func.avs_invoke( h->env, "Distributor", res, NULL );
166             h->func.avs_release_value( res );
167             res = temp;
168         }
169     }
170     else /* non script file */
171     {
172         /* cycle through known source filters to find one that works */
173         const char *filter[AVS_MAX_SEQUENCE+1] = { 0 };
174         avs_build_filter_sequence( filename_ext, filter );
175         int i;
176         for( i = 0; filter[i]; i++ )
177         {
178             fprintf( stderr, "avs [info]: trying %s... ", filter[i] );
179             if( !h->func.avs_function_exists( h->env, filter[i] ) )
180             {
181                 fprintf( stderr, "not found\n" );
182                 continue;
183             }
184             if( !strncasecmp( filter[i], "FFmpegSource", 12 ) )
185             {
186                 fprintf( stderr, "indexing... " );
187                 fflush( stderr );
188             }
189             res = h->func.avs_invoke( h->env, filter[i], arg, NULL );
190             if( !avs_is_error( res ) )
191             {
192                 fprintf( stderr, "succeeded\n" );
193                 break;
194             }
195             fprintf( stderr, "failed\n" );
196         }
197         if( !filter[i] )
198         {
199             fprintf( stderr, "avs [error]: unable to find source filter to open `%s'\n", psz_filename );
200             return -1;
201         }
202     }
203     if( !avs_is_clip( res ) )
204     {
205         fprintf( stderr, "avs [error]: `%s' didn't return a video clip\n", psz_filename );
206         return -1;
207     }
208     h->clip = h->func.avs_take_clip( res, h->env );
209     int avs_version = h->func.avs_get_version( h->clip );
210     const AVS_VideoInfo *vi = h->func.avs_get_video_info( h->clip );
211     if( !avs_has_video( vi ) )
212     {
213         fprintf( stderr, "avs [error]: `%s' has no video data\n", psz_filename );
214         return -1;
215     }
216     if( vi->width&1 || vi->height&1 )
217     {
218         fprintf( stderr, "avs [error]: input clip width or height not divisible by 2 (%dx%d)\n",
219                  vi->width, vi->height );
220         return -1;
221     }
222     /* always call ConvertToYV12 to convert non YV12 planar colorspaces to YV12 when user's AVS supports them,
223        as all planar colorspaces are flagged as YV12. If it is already YV12 in this case, the call does nothing */
224     if( !avs_is_yv12( vi ) || avs_version >= AVS_INTERFACE_OTHER_PLANAR )
225     {
226         h->func.avs_release_clip( h->clip );
227         fprintf( stderr, "avs %s\n", !avs_is_yv12( vi ) ? "[warning]: converting input clip to YV12"
228                : "[info]: avisynth 2.6+ detected, forcing conversion to YV12" );
229         const char *arg_name[2] = { NULL, "interlaced" };
230         AVS_Value arg_arr[2] = { res, avs_new_value_bool( info->interlaced ) };
231         AVS_Value res2 = h->func.avs_invoke( h->env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name );
232         if( avs_is_error( res2 ) )
233         {
234             fprintf( stderr, "avs [error]: couldn't convert input clip to YV12\n" );
235             return -1;
236         }
237         h->clip = h->func.avs_take_clip( res2, h->env );
238         h->func.avs_release_value( res2 );
239         vi = h->func.avs_get_video_info( h->clip );
240     }
241     h->func.avs_release_value( res );
242
243     info->width = vi->width;
244     info->height = vi->height;
245     info->fps_num = vi->fps_numerator;
246     info->fps_den = vi->fps_denominator;
247     h->num_frames = vi->num_frames;
248     info->csp = X264_CSP_YV12;
249     info->vfr = 0;
250
251     *p_handle = h;
252     return 0;
253 }
254
255 static int get_frame_total( hnd_t handle )
256 {
257     avs_hnd_t *h = handle;
258     return h->num_frames;
259 }
260
261 static int picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
262 {
263     pic->img.i_csp = i_csp;
264     pic->img.i_plane = 3;
265     pic->param = NULL;
266     return 0;
267 }
268
269 static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
270 {
271     static int plane[3] = { AVS_PLANAR_Y, AVS_PLANAR_V, AVS_PLANAR_U };
272     avs_hnd_t *h = handle;
273     if( i_frame >= h->num_frames )
274         return -1;
275     AVS_VideoFrame *frm =
276     p_pic->opaque = h->func.avs_get_frame( h->clip, i_frame );
277     int i;
278     const char *err = h->func.avs_clip_get_error( h->clip );
279     if( err )
280     {
281         fprintf( stderr, "avs [error]: %s occurred while reading frame %d\n", err, i_frame );
282         return -1;
283     }
284     for( i = 0; i < 3; i++ )
285     {
286         /* explicitly cast away the const attribute to avoid a warning */
287         p_pic->img.plane[i] = (uint8_t*)avs_get_read_ptr_p( frm, plane[i] );
288         p_pic->img.i_stride[i] = avs_get_pitch_p( frm, plane[i] );
289     }
290     return 0;
291 }
292
293 static int release_frame( x264_picture_t *pic, hnd_t handle )
294 {
295     avs_hnd_t *h = handle;
296     h->func.avs_release_video_frame( pic->opaque );
297     return 0;
298 }
299
300 static void picture_clean( x264_picture_t *pic )
301 {
302     memset( pic, 0, sizeof(x264_picture_t) );
303 }
304
305 static int close_file( hnd_t handle )
306 {
307     avs_hnd_t *h = handle;
308     h->func.avs_release_clip( h->clip );
309     if( h->func.avs_delete_script_environment )
310         h->func.avs_delete_script_environment( h->env );
311     FreeLibrary( h->library );
312     free( h );
313     return 0;
314 }
315
316 const cli_input_t avs_input = { open_file, get_frame_total, picture_alloc, read_frame, release_frame, picture_clean, close_file };