]> git.sesse.net Git - x264/blobdiff - filters/video/resize.c
Fix a few static analyzer performance hints
[x264] / filters / video / resize.c
index b68db59a5cf1f9bf53ab207909d90271a485bf80..f348c5bb956e77014f86b76cda217b17d98ac282 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * resize.c: resize video filter
  *****************************************************************************
- * Copyright (C) 2010 x264 project
+ * Copyright (C) 2010-2015 x264 project
  *
  * Authors: Steven Walters <kemuri9@gmail.com>
  *
@@ -32,24 +32,29 @@ cli_vid_filter_t resize_filter;
 static int full_check( video_info_t *info, x264_param_t *param )
 {
     int required = 0;
-    required |= info->csp    != param->i_csp;
-    required |= info->width  != param->i_width;
-    required |= info->height != param->i_height;
+    required |= info->csp       != param->i_csp;
+    required |= info->width     != param->i_width;
+    required |= info->height    != param->i_height;
+    required |= info->fullrange != param->vui.b_fullrange;
     return required;
 }
 
 #if HAVE_SWSCALE
 #undef DECLARE_ALIGNED
 #include <libswscale/swscale.h>
+#include <libavutil/opt.h>
+#include <libavutil/pixdesc.h>
 
-/* this function is not a part of the swscale API but is defined in swscale_internal.h */
-const char *sws_format_name( enum PixelFormat format );
+#ifndef AV_PIX_FMT_BGRA64
+#define AV_PIX_FMT_BGRA64 AV_PIX_FMT_NONE
+#endif
 
 typedef struct
 {
     int width;
     int height;
     int pix_fmt;
+    int range;
 } frame_prop_t;
 
 typedef struct
@@ -60,9 +65,14 @@ typedef struct
     cli_pic_t buffer;
     int buffer_allocated;
     int dst_csp;
+    int input_range;
     struct SwsContext *ctx;
-    int ctx_flags;
-    int swap_chroma;    /* state of swapping chroma planes */
+    uint32_t ctx_flags;
+    /* state of swapping chroma planes pre and post resize */
+    int pre_swap_chroma;
+    int post_swap_chroma;
+    int variable_input; /* input is capable of changing properties */
+    int working;        /* we have already started working with frames */
     frame_prop_t dst;   /* desired output properties */
     frame_prop_t scale; /* properties of the SwsContext input */
 } resizer_hnd_t;
@@ -76,23 +86,27 @@ static void help( int longhelp )
             "            - resolution only: resizes and adapts sar to avoid stretching\n"
             "            - sar only: sets the sar and resizes to avoid stretching\n"
             "            - resolution and sar: resizes to given resolution and sets the sar\n"
-            "            - fittobox: resizes the video based on the desired contraints\n"
+            "            - fittobox: resizes the video based on the desired constraints\n"
             "               - width, height, both\n"
             "            - fittobox and sar: same as above except with specified sar\n"
-            "            simultaneously converting to the given colorspace\n"
-            "            using resizer method [\"bicubic\"]\n"
-            "             - fastbilinear, bilinear, bicubic, experimental, point,\n"
-            "             - area, bicublin, gauss, sinc, lanczos, spline\n" );
-}
+            "            - csp: convert to the given csp. syntax: [name][:depth]\n"
+            "               - valid csp names [keep current]: " );
 
-static uint32_t convert_cpu_to_flag( uint32_t cpu )
-{
-    uint32_t swscale_cpu = 0;
-    if( cpu & X264_CPU_ALTIVEC )
-        swscale_cpu |= SWS_CPU_CAPS_ALTIVEC;
-    if( cpu & X264_CPU_MMXEXT )
-        swscale_cpu |= SWS_CPU_CAPS_MMX | SWS_CPU_CAPS_MMX2;
-    return swscale_cpu;
+    for( int i = X264_CSP_NONE+1; i < X264_CSP_CLI_MAX; i++ )
+    {
+        if( x264_cli_csps[i].name )
+        {
+            printf( "%s", x264_cli_csps[i].name );
+            if( i+1 < X264_CSP_CLI_MAX )
+                printf( ", " );
+        }
+    }
+    printf( "\n"
+            "               - depth: 8 or 16 bits per pixel [keep current]\n"
+            "            note: not all depths are supported by all csps.\n"
+            "            - method: use resizer method [\"bicubic\"]\n"
+            "               - fastbilinear, bilinear, bicubic, experimental, point,\n"
+            "               - area, bicublin, gauss, sinc, lanczos, spline\n" );
 }
 
 static uint32_t convert_method_to_flag( const char *name )
@@ -131,63 +145,72 @@ static int convert_csp_to_pix_fmt( int csp )
         return csp&X264_CSP_MASK;
     switch( csp&X264_CSP_MASK )
     {
-        case X264_CSP_I420: return PIX_FMT_YUV420P;
-        case X264_CSP_I422: return PIX_FMT_YUV422P;
-        case X264_CSP_I444: return PIX_FMT_YUV444P;
-        case X264_CSP_NV12: return PIX_FMT_NV12;
-        case X264_CSP_YV12: return PIX_FMT_YUV420P; /* specially handled via swapping chroma */
-        case X264_CSP_BGR:  return PIX_FMT_BGR24;
-        case X264_CSP_BGRA: return PIX_FMT_BGRA;
-        default:            return PIX_FMT_NONE;
+        case X264_CSP_YV12: /* specially handled via swapping chroma */
+        case X264_CSP_I420: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_YUV420P16 : AV_PIX_FMT_YUV420P;
+        case X264_CSP_YV16: /* specially handled via swapping chroma */
+        case X264_CSP_I422: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_YUV422P16 : AV_PIX_FMT_YUV422P;
+        case X264_CSP_YV24: /* specially handled via swapping chroma */
+        case X264_CSP_I444: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_YUV444P16 : AV_PIX_FMT_YUV444P;
+        case X264_CSP_RGB:  return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_RGB48     : AV_PIX_FMT_RGB24;
+        case X264_CSP_BGR:  return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_BGR48     : AV_PIX_FMT_BGR24;
+        case X264_CSP_BGRA: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_BGRA64    : AV_PIX_FMT_BGRA;
+        /* the next csp has no equivalent 16bit depth in swscale */
+        case X264_CSP_NV12: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_NONE      : AV_PIX_FMT_NV12;
+        case X264_CSP_NV21: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_NONE      : AV_PIX_FMT_NV21;
+        /* the next csp is no supported by swscale at all */
+        case X264_CSP_NV16:
+        default:            return AV_PIX_FMT_NONE;
     }
 }
 
-static int pick_closest_supported_csp( int csp )
+static int pix_number_of_planes( const AVPixFmtDescriptor *pix_desc )
 {
-    int pix_fmt = convert_csp_to_pix_fmt( csp );
-    switch( pix_fmt )
+    int num_planes = 0;
+    for( int i = 0; i < pix_desc->nb_components; i++ )
     {
-        case PIX_FMT_YUV422P:
-        case PIX_FMT_YUV422P16LE:
-        case PIX_FMT_YUV422P16BE:
-        case PIX_FMT_YUYV422:
-        case PIX_FMT_UYVY422:
-            return X264_CSP_I422;
-        case PIX_FMT_YUV444P:
-        case PIX_FMT_YUV444P16LE:
-        case PIX_FMT_YUV444P16BE:
-            return X264_CSP_I444;
-        case PIX_FMT_RGB24:    // convert rgb to bgr
-        case PIX_FMT_RGB48BE:
-        case PIX_FMT_RGB48LE:
-        case PIX_FMT_RGB565BE:
-        case PIX_FMT_RGB565LE:
-        case PIX_FMT_RGB555BE:
-        case PIX_FMT_RGB555LE:
-        case PIX_FMT_BGR24:
-        case PIX_FMT_BGR565BE:
-        case PIX_FMT_BGR565LE:
-        case PIX_FMT_BGR555BE:
-        case PIX_FMT_BGR555LE:
-            return X264_CSP_BGR;
-        case PIX_FMT_ARGB:
-        case PIX_FMT_RGBA:
-        case PIX_FMT_ABGR:
-        case PIX_FMT_BGRA:
-            return X264_CSP_BGRA;
-        case PIX_FMT_NV12:
-        case PIX_FMT_NV21:
-             return X264_CSP_NV12;
-        default:
-            return X264_CSP_I420;
+        int plane_plus1 = pix_desc->comp[i].plane + 1;
+        num_planes = X264_MAX( plane_plus1, num_planes );
     }
+    return num_planes;
 }
 
-static int round_dbl( double val, int precision, int b_truncate )
+static int pick_closest_supported_csp( int csp )
 {
-    int ret = (int)(val / precision) * precision;
-    if( !b_truncate && (val - ret) >= (precision/2) ) // use the remainder if we're not truncating it
-        ret += precision;
+    int pix_fmt = convert_csp_to_pix_fmt( csp );
+    // first determine the base csp
+    int ret = X264_CSP_NONE;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get( pix_fmt );
+    if( !pix_desc || !pix_desc->name )
+        return ret;
+
+    const char *pix_fmt_name = pix_desc->name;
+    int is_rgb = pix_desc->flags & (AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_PAL);
+    int is_bgr = !!strstr( pix_fmt_name, "bgr" );
+    if( is_bgr || is_rgb )
+    {
+        if( pix_desc->nb_components == 4 ) // has alpha
+            ret = X264_CSP_BGRA;
+        else if( is_bgr )
+            ret = X264_CSP_BGR;
+        else
+            ret = X264_CSP_RGB;
+    }
+    else
+    {
+        // yuv-based
+        if( pix_desc->nb_components == 1 || pix_desc->nb_components == 2 ) // no chroma
+            ret = X264_CSP_I420;
+        else if( pix_desc->log2_chroma_w && pix_desc->log2_chroma_h ) // reduced chroma width & height
+            ret = (pix_number_of_planes( pix_desc ) == 2) ? X264_CSP_NV12 : X264_CSP_I420;
+        else if( pix_desc->log2_chroma_w ) // reduced chroma width only
+            ret = X264_CSP_I422; // X264_CSP_NV16 is not supported by swscale so don't use it
+        else
+            ret = X264_CSP_I444;
+    }
+    // now determine high depth
+    for( int i = 0; i < pix_desc->nb_components; i++ )
+        if( pix_desc->comp[i].depth_minus1 >= 8 )
+            ret |= X264_CSP_HIGH_DEPTH;
     return ret;
 }
 
@@ -209,12 +232,30 @@ static int handle_opts( const char **optlist, char **opts, video_info_t *info, r
 
     if( str_csp )
     {
-        /* output csp was specified, lookup against valid values */
+        /* output csp was specified, first check if optional depth was provided */
+        char *str_depth = strchr( str_csp, ':' );
+        int depth = x264_cli_csp_depth_factor( info->csp ) * 8;
+        if( str_depth )
+        {
+            /* csp bit depth was specified */
+            *str_depth++ = '\0';
+            depth = x264_otoi( str_depth, -1 );
+            FAIL_IF_ERROR( depth != 8 && depth != 16, "unsupported bit depth %d\n", depth );
+        }
+        /* now lookup against the list of valid csps */
         int csp;
-        for( csp = X264_CSP_CLI_MAX-1; x264_cli_csps[csp].name && strcasecmp( x264_cli_csps[csp].name, str_csp ); )
-            csp--;
+        if( strlen( str_csp ) == 0 )
+            csp = info->csp & X264_CSP_MASK;
+        else
+            for( csp = X264_CSP_CLI_MAX-1; csp > X264_CSP_NONE; csp-- )
+            {
+                if( x264_cli_csps[csp].name && !strcasecmp( x264_cli_csps[csp].name, str_csp ) )
+                    break;
+            }
         FAIL_IF_ERROR( csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", str_csp );
         h->dst_csp = csp;
+        if( depth == 16 )
+            h->dst_csp |= X264_CSP_HIGH_DEPTH;
     }
 
     /* if the input sar is currently invalid, set it to 1:1 so it can be used in math */
@@ -231,56 +272,48 @@ static int handle_opts( const char **optlist, char **opts, video_info_t *info, r
     if( fittobox )
     {
         /* resize the video to fit the box as much as possible */
-        double box_width = width;
-        double box_height = height;
         if( !strcasecmp( fittobox, "both" ) )
         {
-            FAIL_IF_ERROR( box_width <= 0 || box_height <= 0, "invalid box resolution %sx%s\n",
-                           x264_otos( str_width, "unset" ), x264_otos( str_height, "unset" ) )
+            FAIL_IF_ERROR( width <= 0 || height <= 0, "invalid box resolution %sx%s\n",
+                           x264_otos( str_width, "<unset>" ), x264_otos( str_height, "<unset>" ) )
         }
         else if( !strcasecmp( fittobox, "width" ) )
         {
-            FAIL_IF_ERROR( box_width <= 0, "invalid box width `%s'\n", x264_otos( str_width, "unset" ) )
-            box_height = INT_MAX;
+            FAIL_IF_ERROR( width <= 0, "invalid box width `%s'\n", x264_otos( str_width, "<unset>" ) )
+            height = INT_MAX;
         }
         else if( !strcasecmp( fittobox, "height" ) )
         {
-            FAIL_IF_ERROR( box_height <= 0, "invalid box height `%s'\n", x264_otos( str_height, "unset" ) )
-            box_width = INT_MAX;
+            FAIL_IF_ERROR( height <= 0, "invalid box height `%s'\n", x264_otos( str_height, "<unset>" ) )
+            width = INT_MAX;
         }
         else FAIL_IF_ERROR( 1, "invalid fittobox mode `%s'\n", fittobox )
 
-        /* we now have the requested bounding box display dimensions, now adjust them for output sar */
-        if( out_sar_w > out_sar_h ) // SAR is wide, decrease width
-            box_width  *= (double)out_sar_h / out_sar_w;
-        else // SAR is thin, decrease height
-            box_height *= (double)out_sar_w  / out_sar_h;
-
-        /* get the display resolution of the clip as it is now */
-        double d_width  = info->width;
-        double d_height = info->height;
-        if( in_sar_w > in_sar_h )
-            d_width  *= (double)in_sar_w / in_sar_h;
-        else
-            d_height *= (double)in_sar_h / in_sar_w;
-        /* now convert it to the coded resolution in accordance with the output sar */
-        if( out_sar_w > out_sar_h )
-            d_width  *= (double)out_sar_h / out_sar_w;
-        else
-            d_height *= (double)out_sar_w / out_sar_h;
-
         /* maximally fit the new coded resolution to the box */
-        double scale = X264_MIN( box_width / d_width, box_height / d_height );
         const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
-        width  = round_dbl( scale * d_width,  csp->mod_width,  1 );
-        height = round_dbl( scale * d_height, csp->mod_height, 1 );
+        double width_units = (double)info->height * in_sar_h * out_sar_w;
+        double height_units = (double)info->width * in_sar_w * out_sar_h;
+        width = width / csp->mod_width * csp->mod_width;
+        height = height / csp->mod_height * csp->mod_height;
+        if( width * width_units > height * height_units )
+        {
+            int new_width = round( height * height_units / (width_units * csp->mod_width) );
+            new_width *= csp->mod_width;
+            width = X264_MIN( new_width, width );
+        }
+        else
+        {
+            int new_height = round( width * width_units / (height_units * csp->mod_height) );
+            new_height *= csp->mod_height;
+            height = X264_MIN( new_height, height );
+        }
     }
     else
     {
         if( str_width || str_height )
         {
             FAIL_IF_ERROR( width <= 0 || height <= 0, "invalid resolution %sx%s\n",
-                           x264_otos( str_width, "unset" ), x264_otos( str_height, "unset" ) )
+                           x264_otos( str_width, "<unset>" ), x264_otos( str_height, "<unset>" ) )
             if( !str_sar ) /* res only -> adjust sar */
             {
                 /* new_sar = (new_h * old_w * old_sar_w) / (old_h * new_w * old_sar_h) */
@@ -295,14 +328,20 @@ static int handle_opts( const char **optlist, char **opts, video_info_t *info, r
         else if( str_sar ) /* sar only -> adjust res */
         {
              const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
+             double width_units = (double)in_sar_h * out_sar_w;
+             double height_units = (double)in_sar_w * out_sar_h;
              width  = info->width;
              height = info->height;
-             if( (out_sar_w * in_sar_h) > (out_sar_h * in_sar_w) ) // SAR got wider, decrease width
-                 width = round_dbl( (double)info->width * in_sar_w * out_sar_h
-                                  / in_sar_h / out_sar_w, csp->mod_width, 0 );
+             if( width_units > height_units ) // SAR got wider, decrease width
+             {
+                 width = round( info->width * height_units / (width_units * csp->mod_width) );
+                 width *= csp->mod_width;
+             }
              else // SAR got thinner, decrease height
-                 height = round_dbl( (double)info->height * in_sar_h * out_sar_w
-                                   / in_sar_w / out_sar_h, csp->mod_height, 0 );
+             {
+                 height = round( info->height * width_units / (height_units * csp->mod_height) );
+                 height *= csp->mod_height;
+             }
         }
         else /* csp only */
         {
@@ -321,6 +360,53 @@ static int handle_opts( const char **optlist, char **opts, video_info_t *info, r
     return 0;
 }
 
+static int x264_init_sws_context( resizer_hnd_t *h )
+{
+    if( h->ctx )
+        sws_freeContext( h->ctx );
+    h->ctx = sws_alloc_context();
+    if( !h->ctx )
+        return -1;
+
+    av_opt_set_int( h->ctx, "sws_flags",  h->ctx_flags,   0 );
+    av_opt_set_int( h->ctx, "dstw",       h->dst.width,   0 );
+    av_opt_set_int( h->ctx, "dsth",       h->dst.height,  0 );
+    av_opt_set_int( h->ctx, "dst_format", h->dst.pix_fmt, 0 );
+    av_opt_set_int( h->ctx, "dst_range",  h->dst.range,   0 );
+
+    av_opt_set_int( h->ctx, "srcw",       h->scale.width,   0 );
+    av_opt_set_int( h->ctx, "srch",       h->scale.height,  0 );
+    av_opt_set_int( h->ctx, "src_format", h->scale.pix_fmt, 0 );
+    av_opt_set_int( h->ctx, "src_range",  h->scale.range,   0 );
+
+    /* FIXME: use the correct matrix coefficients (only YUV -> RGB conversions are supported) */
+    sws_setColorspaceDetails( h->ctx,
+                              sws_getCoefficients( SWS_CS_DEFAULT ), h->scale.range,
+                              sws_getCoefficients( SWS_CS_DEFAULT ), h->dst.range,
+                              0, 1<<16, 1<<16 );
+
+    return sws_init_context( h->ctx, NULL, NULL ) < 0;
+}
+
+static int check_resizer( resizer_hnd_t *h, cli_pic_t *in )
+{
+    frame_prop_t input_prop = { in->img.width, in->img.height, convert_csp_to_pix_fmt( in->img.csp ), h->input_range };
+    if( !memcmp( &input_prop, &h->scale, sizeof(frame_prop_t) ) )
+        return 0;
+    /* also warn if the resizer was initialized after the first frame */
+    if( h->ctx || h->working )
+        x264_cli_log( NAME, X264_LOG_WARNING, "stream properties changed at pts %"PRId64"\n", in->pts );
+    h->scale = input_prop;
+    if( !h->buffer_allocated )
+    {
+        if( x264_cli_pic_alloc_aligned( &h->buffer, h->dst_csp, h->dst.width, h->dst.height ) )
+            return -1;
+        h->buffer_allocated = 1;
+    }
+    FAIL_IF_ERROR( x264_init_sws_context( h ), "swscale init failed\n" )
+    return 0;
+}
+
 static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
 {
     /* if called for normalizing the csp to known formats and the format is not unknown, exit */
@@ -343,8 +429,15 @@ static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x2
         h->dst_csp    = info->csp;
         h->dst.width  = info->width;
         h->dst.height = info->height;
+        h->dst.range  = info->fullrange; // maintain input range
         if( !strcmp( opt_string, "normcsp" ) )
+        {
+            /* only in normalization scenarios is the input capable of changing properties */
+            h->variable_input = 1;
             h->dst_csp = pick_closest_supported_csp( info->csp );
+            FAIL_IF_ERROR( h->dst_csp == X264_CSP_NONE,
+                           "filter get invalid input pixel format %d (colorspace %d)\n", convert_csp_to_pix_fmt( info->csp ), info->csp )
+        }
         else if( handle_opts( optlist, opts, info, h ) )
             return -1;
     }
@@ -353,22 +446,37 @@ static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x2
         h->dst_csp    = param->i_csp;
         h->dst.width  = param->i_width;
         h->dst.height = param->i_height;
+        h->dst.range  = param->vui.b_fullrange; // change to libx264's range
     }
-    uint32_t method = convert_method_to_flag( x264_otos( x264_get_option( optlist[5], opts ), "" ) );
+    h->ctx_flags = convert_method_to_flag( x264_otos( x264_get_option( optlist[5], opts ), "" ) );
     x264_free_string_array( opts );
 
-    h->ctx_flags = convert_cpu_to_flag( param->cpu ) | method;
-    if( method != SWS_FAST_BILINEAR )
-        h->ctx_flags |= SWS_FULL_CHR_H_INT | SWS_ACCURATE_RND;
+    if( h->ctx_flags != SWS_FAST_BILINEAR )
+        h->ctx_flags |= SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND;
     h->dst.pix_fmt = convert_csp_to_pix_fmt( h->dst_csp );
     h->scale = h->dst;
-    /* swap chroma planes for yv12 to have it become i420 */
-    h->swap_chroma = (info->csp & X264_CSP_MASK) == X264_CSP_YV12;
+    h->input_range = info->fullrange;
+
+    /* swap chroma planes if YV12/YV16/YV24 is involved, as libswscale works with I420/I422/I444 */
+    int src_csp = info->csp & (X264_CSP_MASK | X264_CSP_OTHER);
+    int dst_csp = h->dst_csp & (X264_CSP_MASK | X264_CSP_OTHER);
+    h->pre_swap_chroma  = src_csp == X264_CSP_YV12 || src_csp == X264_CSP_YV16 || src_csp == X264_CSP_YV24;
+    h->post_swap_chroma = dst_csp == X264_CSP_YV12 || dst_csp == X264_CSP_YV16 || dst_csp == X264_CSP_YV24;
+
     int src_pix_fmt = convert_csp_to_pix_fmt( info->csp );
 
+    int src_pix_fmt_inv = convert_csp_to_pix_fmt( info->csp ^ X264_CSP_HIGH_DEPTH );
+    int dst_pix_fmt_inv = convert_csp_to_pix_fmt( h->dst_csp ^ X264_CSP_HIGH_DEPTH );
+
     /* confirm swscale can support this conversion */
-    FAIL_IF_ERROR( !sws_isSupportedInput( src_pix_fmt ), "input colorspace %s is not supported\n", sws_format_name( src_pix_fmt ) )
-    FAIL_IF_ERROR( !sws_isSupportedOutput( h->dst.pix_fmt ), "output colorspace %s is not supported\n", sws_format_name( h->dst.pix_fmt ) )
+    FAIL_IF_ERROR( src_pix_fmt == AV_PIX_FMT_NONE && src_pix_fmt_inv != AV_PIX_FMT_NONE,
+                   "input colorspace %s with bit depth %d is not supported\n", av_get_pix_fmt_name( src_pix_fmt_inv ),
+                   info->csp & X264_CSP_HIGH_DEPTH ? 16 : 8 );
+    FAIL_IF_ERROR( !sws_isSupportedInput( src_pix_fmt ), "input colorspace %s is not supported\n", av_get_pix_fmt_name( src_pix_fmt ) )
+    FAIL_IF_ERROR( h->dst.pix_fmt == AV_PIX_FMT_NONE && dst_pix_fmt_inv != AV_PIX_FMT_NONE,
+                   "input colorspace %s with bit depth %d is not supported\n", av_get_pix_fmt_name( dst_pix_fmt_inv ),
+                   h->dst_csp & X264_CSP_HIGH_DEPTH ? 16 : 8 );
+    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 ) )
     FAIL_IF_ERROR( h->dst.height != info->height && info->interlaced,
                    "swscale is not compatible with interlaced vertical resizing\n" )
     /* confirm that the desired resolution meets the colorspace requirements */
@@ -380,12 +488,25 @@ static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x2
         x264_cli_log( NAME, X264_LOG_INFO, "resizing to %dx%d\n", h->dst.width, h->dst.height );
     if( h->dst.pix_fmt != src_pix_fmt )
         x264_cli_log( NAME, X264_LOG_WARNING, "converting from %s to %s\n",
-                      sws_format_name( src_pix_fmt ), sws_format_name( h->dst.pix_fmt ) );
+                      av_get_pix_fmt_name( src_pix_fmt ), av_get_pix_fmt_name( h->dst.pix_fmt ) );
+    else if( h->dst.range != h->input_range )
+        x264_cli_log( NAME, X264_LOG_WARNING, "converting range from %s to %s\n",
+                      h->input_range ? "PC" : "TV", h->dst.range ? "PC" : "TV" );
     h->dst_csp |= info->csp & X264_CSP_VFLIP; // preserve vflip
+
+    /* if the input is not variable, initialize the context */
+    if( !h->variable_input )
+    {
+        cli_pic_t input_pic = {{info->csp, info->width, info->height, 0}, 0};
+        if( check_resizer( h, &input_pic ) )
+            return -1;
+    }
+
     /* finished initing, overwrite values */
-    info->csp    = h->dst_csp;
-    info->width  = h->dst.width;
-    info->height = h->dst.height;
+    info->csp       = h->dst_csp;
+    info->width     = h->dst.width;
+    info->height    = h->dst.height;
+    info->fullrange = h->dst.range;
 
     h->prev_filter = *filter;
     h->prev_hnd = *handle;
@@ -395,36 +516,16 @@ static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x2
     return 0;
 }
 
-static int check_resizer( resizer_hnd_t *h, cli_pic_t *in )
-{
-    frame_prop_t input_prop = { in->img.width, in->img.height, convert_csp_to_pix_fmt( in->img.csp ) };
-    if( !memcmp( &input_prop, &h->scale, sizeof(frame_prop_t) ) )
-        return 0;
-    if( h->ctx )
-    {
-        sws_freeContext( h->ctx );
-        x264_cli_log( NAME, X264_LOG_WARNING, "stream properties changed at pts %"PRId64"\n", in->pts );
-    }
-    h->scale = input_prop;
-    if( !h->buffer_allocated )
-    {
-        if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, h->dst.width, h->dst.height ) )
-            return -1;
-        h->buffer_allocated = 1;
-    }
-    h->ctx = sws_getContext( h->scale.width, h->scale.height, h->scale.pix_fmt, h->dst.width,
-                             h->dst.height, h->dst.pix_fmt, h->ctx_flags, NULL, NULL, NULL );
-    FAIL_IF_ERROR( !h->ctx, "swscale init failed\n" )
-    return 0;
-}
-
 static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
 {
     resizer_hnd_t *h = handle;
     if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
         return -1;
-    if( check_resizer( h, output ) )
+    if( h->variable_input && check_resizer( h, output ) )
         return -1;
+    h->working = 1;
+    if( h->pre_swap_chroma )
+        XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
     if( h->ctx )
     {
         sws_scale( h->ctx, (const uint8_t* const*)output->img.plane, output->img.stride,
@@ -433,7 +534,7 @@ static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
     }
     else
         output->img.csp = h->dst_csp;
-    if( h->swap_chroma )
+    if( h->post_swap_chroma )
         XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
 
     return 0;