]> git.sesse.net Git - x264/blob - filters/video/resize.c
Resize filter updates
[x264] / filters / video / resize.c
1 /*****************************************************************************
2  * resize.c: resize video filter
3  *****************************************************************************
4  * Copyright (C) 2010-2011 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  * This program is also available under a commercial proprietary license.
23  * For more information, contact us at licensing@x264.com.
24  *****************************************************************************/
25
26 #include "video.h"
27 #define NAME "resize"
28 #define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
29
30 cli_vid_filter_t resize_filter;
31
32 static int full_check( video_info_t *info, x264_param_t *param )
33 {
34     int required = 0;
35     required |= info->csp       != param->i_csp;
36     required |= info->width     != param->i_width;
37     required |= info->height    != param->i_height;
38     required |= info->fullrange != param->vui.b_fullrange;
39     return required;
40 }
41
42 #if HAVE_SWSCALE
43 #undef DECLARE_ALIGNED
44 #include <libswscale/swscale.h>
45 #include <libavutil/opt.h>
46 #include <libavutil/pixdesc.h>
47
48 #ifndef PIX_FMT_BGRA64
49 #define PIX_FMT_BGRA64 PIX_FMT_NONE
50 #endif
51
52 typedef struct
53 {
54     int width;
55     int height;
56     int pix_fmt;
57     int range;
58 } frame_prop_t;
59
60 typedef struct
61 {
62     hnd_t prev_hnd;
63     cli_vid_filter_t prev_filter;
64
65     cli_pic_t buffer;
66     int buffer_allocated;
67     int dst_csp;
68     int input_range;
69     struct SwsContext *ctx;
70     uint32_t ctx_flags;
71     /* state of swapping chroma planes pre and post resize */
72     int pre_swap_chroma;
73     int post_swap_chroma;
74     int variable_input; /* input is capable of changing properties */
75     int working;        /* we have already started working with frames */
76     frame_prop_t dst;   /* desired output properties */
77     frame_prop_t scale; /* properties of the SwsContext input */
78 } resizer_hnd_t;
79
80 static void help( int longhelp )
81 {
82     printf( "      "NAME":[width,height][,sar][,fittobox][,csp][,method]\n" );
83     if( !longhelp )
84         return;
85     printf( "            resizes frames based on the given criteria:\n"
86             "            - resolution only: resizes and adapts sar to avoid stretching\n"
87             "            - sar only: sets the sar and resizes to avoid stretching\n"
88             "            - resolution and sar: resizes to given resolution and sets the sar\n"
89             "            - fittobox: resizes the video based on the desired constraints\n"
90             "               - width, height, both\n"
91             "            - fittobox and sar: same as above except with specified sar\n"
92             "            - csp: convert to the given csp. syntax: [name][:depth]\n"
93             "               - valid csp names [keep current]: " );
94
95     for( int i = X264_CSP_NONE+1; i < X264_CSP_CLI_MAX; i++ )
96     {
97         printf( "%s", x264_cli_csps[i].name );
98         if( i+1 < X264_CSP_CLI_MAX )
99             printf( ", " );
100     }
101     printf( "\n"
102             "               - depth: 8 or 16 bits per pixel [keep current]\n"
103             "            note: not all depths are supported by all csps.\n"
104             "            - method: use resizer method [\"bicubic\"]\n"
105             "               - fastbilinear, bilinear, bicubic, experimental, point,\n"
106             "               - area, bicublin, gauss, sinc, lanczos, spline\n" );
107 }
108
109 static uint32_t convert_method_to_flag( const char *name )
110 {
111     uint32_t flag = 0;
112     if( !strcasecmp( name, "fastbilinear" ) )
113         flag = SWS_FAST_BILINEAR;
114     else if( !strcasecmp( name, "bilinear" ) )
115         flag = SWS_BILINEAR;
116     else if( !strcasecmp( name, "bicubic" ) )
117         flag = SWS_BICUBIC;
118     else if( !strcasecmp( name, "experimental" ) )
119         flag = SWS_X;
120     else if( !strcasecmp( name, "point" ) )
121         flag = SWS_POINT;
122     else if( !strcasecmp( name, "area" ) )
123         flag = SWS_AREA;
124     else if( !strcasecmp( name, "bicublin" ) )
125         flag = SWS_BICUBLIN;
126     else if( !strcasecmp( name, "guass" ) )
127         flag = SWS_GAUSS;
128     else if( !strcasecmp( name, "sinc" ) )
129         flag = SWS_SINC;
130     else if( !strcasecmp( name, "lanczos" ) )
131         flag = SWS_LANCZOS;
132     else if( !strcasecmp( name, "spline" ) )
133         flag = SWS_SPLINE;
134     else // default
135         flag = SWS_BICUBIC;
136     return flag;
137 }
138
139 static int convert_csp_to_pix_fmt( int csp )
140 {
141     if( csp&X264_CSP_OTHER )
142         return csp&X264_CSP_MASK;
143     switch( csp&X264_CSP_MASK )
144     {
145         case X264_CSP_YV12: /* specially handled via swapping chroma */
146         case X264_CSP_I420: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV420P16 : PIX_FMT_YUV420P;
147         case X264_CSP_YV16: /* specially handled via swapping chroma */
148         case X264_CSP_I422: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV422P16 : PIX_FMT_YUV422P;
149         case X264_CSP_YV24: /* specially handled via swapping chroma */
150         case X264_CSP_I444: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV444P16 : PIX_FMT_YUV444P;
151         case X264_CSP_RGB:  return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_RGB48     : PIX_FMT_RGB24;
152         case X264_CSP_BGR:  return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_BGR48     : PIX_FMT_BGR24;
153         case X264_CSP_BGRA: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_BGRA64    : PIX_FMT_BGRA;
154         /* the next csp has no equivalent 16bit depth in swscale */
155         case X264_CSP_NV12: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_NONE      : PIX_FMT_NV12;
156         default:            return PIX_FMT_NONE;
157     }
158 }
159
160 static int pix_number_of_planes( const AVPixFmtDescriptor *pix_desc )
161 {
162     int num_planes = 0;
163     for( int i = 0; i < pix_desc->nb_components; i++ )
164     {
165         int plane_plus1 = pix_desc->comp[i].plane + 1;
166         num_planes = X264_MAX( plane_plus1, num_planes );
167     }
168     return num_planes;
169 }
170
171 static int pick_closest_supported_csp( int csp )
172 {
173     int pix_fmt = convert_csp_to_pix_fmt( csp );
174     // first determine the base csp
175     int ret = X264_CSP_NONE;
176     const AVPixFmtDescriptor *pix_desc = av_pix_fmt_descriptors+pix_fmt;
177     if( (unsigned)pix_fmt >= PIX_FMT_NB || !pix_desc->name )
178         return ret;
179
180     const char *pix_fmt_name = pix_desc->name;
181     int is_rgb = pix_desc->flags & (PIX_FMT_RGB | PIX_FMT_PAL);
182     int is_bgr = !!strstr( pix_fmt_name, "bgr" );
183     if( is_bgr || is_rgb )
184     {
185         if( pix_desc->nb_components == 4 ) // has alpha
186             ret = X264_CSP_BGRA;
187         else if( is_bgr )
188             ret = X264_CSP_BGR;
189         else
190             ret = X264_CSP_RGB;
191     }
192     else
193     {
194         // yuv-based
195         if( pix_desc->nb_components == 1 || pix_desc->nb_components == 2 ) // no chroma
196             ret = X264_CSP_I420;
197         else if( pix_desc->log2_chroma_w && pix_desc->log2_chroma_h ) // reduced chroma width & height
198             ret = (pix_desc->nb_components == pix_number_of_planes( pix_desc )) ? X264_CSP_I420 : X264_CSP_NV12;
199         else if( pix_desc->log2_chroma_w ) // reduced chroma width only
200             ret = (pix_desc->nb_components == pix_number_of_planes( pix_desc )) ? X264_CSP_I422 : X264_CSP_NV16;
201         else
202             ret = X264_CSP_I444;
203     }
204     // now determine high depth
205     for( int i = 0; i < pix_desc->nb_components; i++ )
206         if( pix_desc->comp[i].depth_minus1 >= 8 )
207             ret |= X264_CSP_HIGH_DEPTH;
208     return ret;
209 }
210
211 static int handle_opts( const char **optlist, char **opts, video_info_t *info, resizer_hnd_t *h )
212 {
213     uint32_t out_sar_w, out_sar_h;
214
215     char *str_width  = x264_get_option( optlist[0], opts );
216     char *str_height = x264_get_option( optlist[1], opts );
217     char *str_sar    = x264_get_option( optlist[2], opts );
218     char *fittobox   = x264_get_option( optlist[3], opts );
219     char *str_csp    = x264_get_option( optlist[4], opts );
220     int width        = x264_otoi( str_width, -1 );
221     int height       = x264_otoi( str_height, -1 );
222
223     int csp_only = 0;
224     uint32_t in_sar_w = info->sar_width;
225     uint32_t in_sar_h = info->sar_height;
226
227     if( str_csp )
228     {
229         /* output csp was specified, first check if optional depth was provided */
230         char *str_depth = strchr( str_csp, ':' );
231         int depth = x264_cli_csp_depth_factor( info->csp ) * 8;
232         if( str_depth )
233         {
234             /* csp bit depth was specified */
235             *str_depth++ = '\0';
236             depth = x264_otoi( str_depth, -1 );
237             FAIL_IF_ERROR( depth != 8 && depth != 16, "unsupported bit depth %d\n", depth );
238         }
239         /* now lookup against the list of valid csps */
240         int csp;
241         if( strlen( str_csp ) == 0 )
242             csp = info->csp & X264_CSP_MASK;
243         else
244             for( csp = X264_CSP_CLI_MAX-1; x264_cli_csps[csp].name && strcasecmp( x264_cli_csps[csp].name, str_csp ); )
245                 csp--;
246         FAIL_IF_ERROR( csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", str_csp );
247         h->dst_csp = csp;
248         if( depth == 16 )
249             h->dst_csp |= X264_CSP_HIGH_DEPTH;
250     }
251
252     /* if the input sar is currently invalid, set it to 1:1 so it can be used in math */
253     if( !in_sar_w || !in_sar_h )
254         in_sar_w = in_sar_h = 1;
255     if( str_sar )
256     {
257         FAIL_IF_ERROR( 2 != sscanf( str_sar, "%u:%u", &out_sar_w, &out_sar_h ) &&
258                        2 != sscanf( str_sar, "%u/%u", &out_sar_w, &out_sar_h ),
259                        "invalid sar `%s'\n", str_sar )
260     }
261     else
262         out_sar_w = out_sar_h = 1;
263     if( fittobox )
264     {
265         /* resize the video to fit the box as much as possible */
266         if( !strcasecmp( fittobox, "both" ) )
267         {
268             FAIL_IF_ERROR( width <= 0 || height <= 0, "invalid box resolution %sx%s\n",
269                            x264_otos( str_width, "<unset>" ), x264_otos( str_height, "<unset>" ) )
270         }
271         else if( !strcasecmp( fittobox, "width" ) )
272         {
273             FAIL_IF_ERROR( width <= 0, "invalid box width `%s'\n", x264_otos( str_width, "<unset>" ) )
274             height = INT_MAX;
275         }
276         else if( !strcasecmp( fittobox, "height" ) )
277         {
278             FAIL_IF_ERROR( height <= 0, "invalid box height `%s'\n", x264_otos( str_height, "<unset>" ) )
279             width = INT_MAX;
280         }
281         else FAIL_IF_ERROR( 1, "invalid fittobox mode `%s'\n", fittobox )
282
283         /* maximally fit the new coded resolution to the box */
284         const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
285         double width_units = (double)info->height * in_sar_h * out_sar_w;
286         double height_units = (double)info->width * in_sar_w * out_sar_h;
287         width = width / csp->mod_width * csp->mod_width;
288         height = height / csp->mod_height * csp->mod_height;
289         if( width * width_units > height * height_units )
290         {
291             int new_width = round( height * height_units / (width_units * csp->mod_width) );
292             new_width *= csp->mod_width;
293             width = X264_MIN( new_width, width );
294         }
295         else
296         {
297             int new_height = round( width * width_units / (height_units * csp->mod_height) );
298             new_height *= csp->mod_height;
299             height = X264_MIN( new_height, height );
300         }
301     }
302     else
303     {
304         if( str_width || str_height )
305         {
306             FAIL_IF_ERROR( width <= 0 || height <= 0, "invalid resolution %sx%s\n",
307                            x264_otos( str_width, "<unset>" ), x264_otos( str_height, "<unset>" ) )
308             if( !str_sar ) /* res only -> adjust sar */
309             {
310                 /* new_sar = (new_h * old_w * old_sar_w) / (old_h * new_w * old_sar_h) */
311                 uint64_t num = (uint64_t)info->width  * height;
312                 uint64_t den = (uint64_t)info->height * width;
313                 x264_reduce_fraction64( &num, &den );
314                 out_sar_w = num * in_sar_w;
315                 out_sar_h = den * in_sar_h;
316                 x264_reduce_fraction( &out_sar_w, &out_sar_h );
317             }
318         }
319         else if( str_sar ) /* sar only -> adjust res */
320         {
321              const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
322              double width_units = (double)in_sar_h * out_sar_w;
323              double height_units = (double)in_sar_w * out_sar_h;
324              width  = info->width;
325              height = info->height;
326              if( width_units > height_units ) // SAR got wider, decrease width
327              {
328                  width = round( info->width * height_units / (width_units * csp->mod_width) );
329                  width *= csp->mod_width;
330              }
331              else // SAR got thinner, decrease height
332              {
333                  height = round( info->height * width_units / (height_units * csp->mod_height) );
334                  height *= csp->mod_height;
335              }
336         }
337         else /* csp only */
338         {
339             h->dst.width  = info->width;
340             h->dst.height = info->height;
341             csp_only = 1;
342         }
343     }
344     if( !csp_only )
345     {
346         info->sar_width  = out_sar_w;
347         info->sar_height = out_sar_h;
348         h->dst.width  = width;
349         h->dst.height = height;
350     }
351     return 0;
352 }
353
354 static int x264_init_sws_context( resizer_hnd_t *h )
355 {
356     if( h->ctx )
357         sws_freeContext( h->ctx );
358     h->ctx = sws_alloc_context();
359     if( !h->ctx )
360         return -1;
361
362     av_opt_set_int( h->ctx, "sws_flags",  h->ctx_flags,   0 );
363     av_opt_set_int( h->ctx, "dstw",       h->dst.width,   0 );
364     av_opt_set_int( h->ctx, "dsth",       h->dst.height,  0 );
365     av_opt_set_int( h->ctx, "dst_format", h->dst.pix_fmt, 0 );
366     av_opt_set_int( h->ctx, "dst_range",  h->dst.range,   0 );
367
368     av_opt_set_int( h->ctx, "srcw",       h->scale.width,   0 );
369     av_opt_set_int( h->ctx, "srch",       h->scale.height,  0 );
370     av_opt_set_int( h->ctx, "src_format", h->scale.pix_fmt, 0 );
371     av_opt_set_int( h->ctx, "src_range",  h->scale.range,   0 );
372
373     /* FIXME: use the correct matrix coefficients (only YUV -> RGB conversions are supported) */
374     sws_setColorspaceDetails( h->ctx,
375                               sws_getCoefficients( SWS_CS_DEFAULT ), h->scale.range,
376                               sws_getCoefficients( SWS_CS_DEFAULT ), h->dst.range,
377                               0, 1<<16, 1<<16 );
378
379     return sws_init_context( h->ctx, NULL, NULL ) < 0;
380 }
381
382 static int check_resizer( resizer_hnd_t *h, cli_pic_t *in )
383 {
384     frame_prop_t input_prop = { in->img.width, in->img.height, convert_csp_to_pix_fmt( in->img.csp ), h->input_range };
385     if( !memcmp( &input_prop, &h->scale, sizeof(frame_prop_t) ) )
386         return 0;
387     /* also warn if the resizer was initialized after the first frame */
388     if( h->ctx || h->working )
389         x264_cli_log( NAME, X264_LOG_WARNING, "stream properties changed at pts %"PRId64"\n", in->pts );
390     h->scale = input_prop;
391     if( !h->buffer_allocated )
392     {
393         if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, h->dst.width, h->dst.height ) )
394             return -1;
395         h->buffer_allocated = 1;
396     }
397     FAIL_IF_ERROR( x264_init_sws_context( h ), "swscale init failed\n" )
398     return 0;
399 }
400
401 static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
402 {
403     /* if called for normalizing the csp to known formats and the format is not unknown, exit */
404     if( opt_string && !strcmp( opt_string, "normcsp" ) && !(info->csp&X264_CSP_OTHER) )
405         return 0;
406     /* if called by x264cli and nothing needs to be done, exit */
407     if( !opt_string && !full_check( info, param ) )
408         return 0;
409
410     static const char *optlist[] = { "width", "height", "sar", "fittobox", "csp", "method", NULL };
411     char **opts = x264_split_options( opt_string, optlist );
412     if( !opts && opt_string )
413         return -1;
414
415     resizer_hnd_t *h = calloc( 1, sizeof(resizer_hnd_t) );
416     if( !h )
417         return -1;
418     if( opts )
419     {
420         h->dst_csp    = info->csp;
421         h->dst.width  = info->width;
422         h->dst.height = info->height;
423         h->dst.range  = info->fullrange; // maintain input range
424         if( !strcmp( opt_string, "normcsp" ) )
425         {
426             /* only in normalization scenarios is the input capable of changing properties */
427             h->variable_input = 1;
428             h->dst_csp = pick_closest_supported_csp( info->csp );
429             FAIL_IF_ERROR( h->dst_csp == X264_CSP_NONE,
430                            "filter get invalid input pixel format %d (colorspace %d)\n", convert_csp_to_pix_fmt( info->csp ), info->csp )
431         }
432         else if( handle_opts( optlist, opts, info, h ) )
433             return -1;
434     }
435     else
436     {
437         h->dst_csp    = param->i_csp;
438         h->dst.width  = param->i_width;
439         h->dst.height = param->i_height;
440         h->dst.range  = param->vui.b_fullrange; // change to libx264's range
441     }
442     h->ctx_flags = convert_method_to_flag( x264_otos( x264_get_option( optlist[5], opts ), "" ) );
443     x264_free_string_array( opts );
444
445     if( h->ctx_flags != SWS_FAST_BILINEAR )
446         h->ctx_flags |= SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND;
447     h->dst.pix_fmt = convert_csp_to_pix_fmt( h->dst_csp );
448     h->scale = h->dst;
449     h->input_range = info->fullrange;
450
451     /* swap chroma planes if YV12/YV16/YV24 is involved, as libswscale works with I420/I422/I444 */
452     int src_csp = info->csp & (X264_CSP_MASK | X264_CSP_OTHER);
453     int dst_csp = h->dst_csp & (X264_CSP_MASK | X264_CSP_OTHER);
454     h->pre_swap_chroma  = src_csp == X264_CSP_YV12 || src_csp == X264_CSP_YV16 || src_csp == X264_CSP_YV24;
455     h->post_swap_chroma = dst_csp == X264_CSP_YV12 || dst_csp == X264_CSP_YV16 || dst_csp == X264_CSP_YV24;
456
457     int src_pix_fmt = convert_csp_to_pix_fmt( info->csp );
458
459     int src_pix_fmt_inv = convert_csp_to_pix_fmt( info->csp ^ X264_CSP_HIGH_DEPTH );
460     int dst_pix_fmt_inv = convert_csp_to_pix_fmt( h->dst_csp ^ X264_CSP_HIGH_DEPTH );
461
462     /* confirm swscale can support this conversion */
463     FAIL_IF_ERROR( src_pix_fmt == PIX_FMT_NONE && src_pix_fmt_inv != PIX_FMT_NONE,
464                    "input colorspace %s with bit depth %d is not supported\n", av_get_pix_fmt_name( src_pix_fmt_inv ),
465                    info->csp & X264_CSP_HIGH_DEPTH ? 16 : 8 );
466     FAIL_IF_ERROR( !sws_isSupportedInput( src_pix_fmt ), "input colorspace %s is not supported\n", av_get_pix_fmt_name( src_pix_fmt ) )
467     FAIL_IF_ERROR( h->dst.pix_fmt == PIX_FMT_NONE && dst_pix_fmt_inv != PIX_FMT_NONE,
468                    "input colorspace %s with bit depth %d is not supported\n", av_get_pix_fmt_name( dst_pix_fmt_inv ),
469                    h->dst_csp & X264_CSP_HIGH_DEPTH ? 16 : 8 );
470     FAIL_IF_ERROR( !sws_isSupportedOutput( h->dst.pix_fmt ), "output colorspace %s is not supported\n", av_get_pix_fmt_name( h->dst.pix_fmt ) )
471     FAIL_IF_ERROR( h->dst.height != info->height && info->interlaced,
472                    "swscale is not compatible with interlaced vertical resizing\n" )
473     /* confirm that the desired resolution meets the colorspace requirements */
474     const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
475     FAIL_IF_ERROR( h->dst.width % csp->mod_width || h->dst.height % csp->mod_height,
476                    "resolution %dx%d is not compliant with colorspace %s\n", h->dst.width, h->dst.height, csp->name )
477
478     if( h->dst.width != info->width || h->dst.height != info->height )
479         x264_cli_log( NAME, X264_LOG_INFO, "resizing to %dx%d\n", h->dst.width, h->dst.height );
480     if( h->dst.pix_fmt != src_pix_fmt )
481         x264_cli_log( NAME, X264_LOG_WARNING, "converting from %s to %s\n",
482                       av_get_pix_fmt_name( src_pix_fmt ), av_get_pix_fmt_name( h->dst.pix_fmt ) );
483     else if( h->dst.range != h->input_range )
484         x264_cli_log( NAME, X264_LOG_WARNING, "converting range from %s to %s\n",
485                       h->input_range ? "PC" : "TV", h->dst.range ? "PC" : "TV" );
486     h->dst_csp |= info->csp & X264_CSP_VFLIP; // preserve vflip
487
488     /* if the input is not variable, initialize the context */
489     if( !h->variable_input )
490     {
491         cli_pic_t input_pic = {{info->csp, info->width, info->height, 0}, 0};
492         if( check_resizer( h, &input_pic ) )
493             return -1;
494     }
495
496     /* finished initing, overwrite values */
497     info->csp       = h->dst_csp;
498     info->width     = h->dst.width;
499     info->height    = h->dst.height;
500     info->fullrange = h->dst.range;
501
502     h->prev_filter = *filter;
503     h->prev_hnd = *handle;
504     *handle = h;
505     *filter = resize_filter;
506
507     return 0;
508 }
509
510 static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
511 {
512     resizer_hnd_t *h = handle;
513     if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
514         return -1;
515     if( h->variable_input && check_resizer( h, output ) )
516         return -1;
517     h->working = 1;
518     if( h->pre_swap_chroma )
519         XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
520     if( h->ctx )
521     {
522         sws_scale( h->ctx, (const uint8_t* const*)output->img.plane, output->img.stride,
523                    0, output->img.height, h->buffer.img.plane, h->buffer.img.stride );
524         output->img = h->buffer.img; /* copy img data */
525     }
526     else
527         output->img.csp = h->dst_csp;
528     if( h->post_swap_chroma )
529         XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
530
531     return 0;
532 }
533
534 static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
535 {
536     resizer_hnd_t *h = handle;
537     return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
538 }
539
540 static void free_filter( hnd_t handle )
541 {
542     resizer_hnd_t *h = handle;
543     h->prev_filter.free( h->prev_hnd );
544     if( h->ctx )
545         sws_freeContext( h->ctx );
546     if( h->buffer_allocated )
547         x264_cli_pic_clean( &h->buffer );
548     free( h );
549 }
550
551 #else /* no swscale */
552 static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
553 {
554     int ret = 0;
555
556     if( !opt_string )
557         ret = full_check( info, param );
558     else
559     {
560         if( !strcmp( opt_string, "normcsp" ) )
561             ret = info->csp & X264_CSP_OTHER;
562         else
563             ret = -1;
564     }
565
566     /* pass if nothing needs to be done, otherwise fail */
567     FAIL_IF_ERROR( ret, "not compiled with swscale support\n" )
568     return 0;
569 }
570
571 #define help NULL
572 #define get_frame NULL
573 #define release_frame NULL
574 #define free_filter NULL
575 #define convert_csp_to_pix_fmt(x) (x & X264_CSP_MASK)
576
577 #endif
578
579 cli_vid_filter_t resize_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };