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