x264 source for verification 2026-05-22
This commit is contained in:
151
filters/video/cache.c
Normal file
151
filters/video/cache.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*****************************************************************************
|
||||
* cache.c: cache video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
#include "internal.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#define cache_filter x264_glue3(cache, BIT_DEPTH, filter)
|
||||
#if BIT_DEPTH == 8
|
||||
#define NAME "cache_8"
|
||||
#else
|
||||
#define NAME "cache_10"
|
||||
#endif
|
||||
|
||||
#define LAST_FRAME (h->first_frame + h->cur_size - 1)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hnd_t prev_hnd;
|
||||
cli_vid_filter_t prev_filter;
|
||||
|
||||
int max_size;
|
||||
int first_frame; /* first cached frame */
|
||||
cli_pic_t **cache;
|
||||
int cur_size;
|
||||
int eof; /* frame beyond end of the file */
|
||||
} cache_hnd_t;
|
||||
|
||||
cli_vid_filter_t cache_filter;
|
||||
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
intptr_t size = (intptr_t)opt_string;
|
||||
/* upon a <= 0 cache request, do nothing */
|
||||
if( size <= 0 )
|
||||
return 0;
|
||||
cache_hnd_t *h = calloc( 1, sizeof(cache_hnd_t) );
|
||||
if( !h )
|
||||
return -1;
|
||||
|
||||
h->max_size = size;
|
||||
h->cache = malloc( (h->max_size+1) * sizeof(cli_pic_t*) );
|
||||
if( !h->cache )
|
||||
return -1;
|
||||
|
||||
for( int i = 0; i < h->max_size; i++ )
|
||||
{
|
||||
h->cache[i] = malloc( sizeof(cli_pic_t) );
|
||||
if( !h->cache[i] || x264_cli_pic_alloc( h->cache[i], info->csp, info->width, info->height ) )
|
||||
return -1;
|
||||
}
|
||||
h->cache[h->max_size] = NULL; /* require null terminator for list methods */
|
||||
|
||||
h->prev_filter = *filter;
|
||||
h->prev_hnd = *handle;
|
||||
*handle = h;
|
||||
*filter = cache_filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fill_cache( cache_hnd_t *h, int frame )
|
||||
{
|
||||
/* shift frames out of the cache as the frame request is beyond the filled cache */
|
||||
int shift = frame - LAST_FRAME;
|
||||
/* no frames to shift or no frames left to read */
|
||||
if( shift <= 0 || h->eof )
|
||||
return;
|
||||
/* the next frames to read are either
|
||||
* A) starting at the end of the current cache, or
|
||||
* B) starting at a new frame that has the end of the cache at the desired frame
|
||||
* and proceeding to fill the entire cache */
|
||||
int cur_frame = X264_MAX( h->first_frame + h->cur_size, frame - h->max_size + 1 );
|
||||
/* the new starting point is either
|
||||
* A) the current one shifted the number of frames entering/leaving the cache, or
|
||||
* B) at a new frame that has the end of the cache at the desired frame. */
|
||||
h->first_frame = X264_MIN( h->first_frame + shift, cur_frame );
|
||||
h->cur_size = X264_MAX( h->cur_size - shift, 0 );
|
||||
while( h->cur_size < h->max_size )
|
||||
{
|
||||
cli_pic_t temp;
|
||||
/* the old front frame is going to shift off, overwrite it with the new frame */
|
||||
cli_pic_t *cache = h->cache[0];
|
||||
if( h->prev_filter.get_frame( h->prev_hnd, &temp, cur_frame ) ||
|
||||
x264_cli_pic_copy( cache, &temp ) ||
|
||||
h->prev_filter.release_frame( h->prev_hnd, &temp, cur_frame ) )
|
||||
{
|
||||
h->eof = cur_frame;
|
||||
return;
|
||||
}
|
||||
/* the read was successful, shift the frame off the front to the end */
|
||||
x264_frame_push( (void*)h->cache, x264_frame_shift( (void*)h->cache ) );
|
||||
cur_frame++;
|
||||
h->cur_size++;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
|
||||
{
|
||||
cache_hnd_t *h = handle;
|
||||
FAIL_IF_ERR( frame < h->first_frame, NAME, "frame %d is before first cached frame %d \n", frame, h->first_frame );
|
||||
fill_cache( h, frame );
|
||||
if( frame > LAST_FRAME ) /* eof */
|
||||
return -1;
|
||||
int idx = frame - (h->eof ? h->eof - h->max_size : h->first_frame);
|
||||
*output = *h->cache[idx];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
/* the parent filter's frame has already been released so do nothing here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
cache_hnd_t *h = handle;
|
||||
h->prev_filter.free( h->prev_hnd );
|
||||
for( int i = 0; i < h->max_size; i++ )
|
||||
{
|
||||
x264_cli_pic_clean( h->cache[i] );
|
||||
free( h->cache[i] );
|
||||
}
|
||||
free( h->cache );
|
||||
free( h );
|
||||
}
|
||||
|
||||
cli_vid_filter_t cache_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };
|
||||
139
filters/video/crop.c
Normal file
139
filters/video/crop.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*****************************************************************************
|
||||
* crop.c: crop video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
* James Darnley <james.darnley@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
|
||||
#define NAME "crop"
|
||||
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
|
||||
|
||||
cli_vid_filter_t crop_filter;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hnd_t prev_hnd;
|
||||
cli_vid_filter_t prev_filter;
|
||||
|
||||
int dims[4]; /* left, top, width, height */
|
||||
const x264_cli_csp_t *csp;
|
||||
} crop_hnd_t;
|
||||
|
||||
static void help( int longhelp )
|
||||
{
|
||||
printf( " "NAME":left,top,right,bottom\n" );
|
||||
if( !longhelp )
|
||||
return;
|
||||
printf( " removes pixels from the edges of the frame\n" );
|
||||
}
|
||||
|
||||
static int handle_opts( crop_hnd_t *h, video_info_t *info, char **opts, const char * const *optlist )
|
||||
{
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
char *opt = x264_get_option( optlist[i], opts );
|
||||
FAIL_IF_ERROR( !opt, "%s crop value not specified\n", optlist[i] );
|
||||
h->dims[i] = x264_otoi( opt, -1 );
|
||||
FAIL_IF_ERROR( h->dims[i] < 0, "%s crop value `%s' is less than 0\n", optlist[i], opt );
|
||||
int dim_mod = i&1 ? (h->csp->mod_height << info->interlaced) : h->csp->mod_width;
|
||||
FAIL_IF_ERROR( h->dims[i] % dim_mod, "%s crop value `%s' is not a multiple of %d\n", optlist[i], opt, dim_mod );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
FAIL_IF_ERROR( x264_cli_csp_is_invalid( info->csp ), "invalid csp %d\n", info->csp );
|
||||
crop_hnd_t *h = calloc( 1, sizeof(crop_hnd_t) );
|
||||
if( !h )
|
||||
return -1;
|
||||
|
||||
h->csp = x264_cli_get_csp( info->csp );
|
||||
static const char * const optlist[] = { "left", "top", "right", "bottom", NULL };
|
||||
char **opts = x264_split_options( opt_string, optlist );
|
||||
if( !opts )
|
||||
return -1;
|
||||
|
||||
int err = handle_opts( h, info, opts, optlist );
|
||||
free( opts );
|
||||
if( err )
|
||||
return -1;
|
||||
|
||||
h->dims[2] = info->width - h->dims[0] - h->dims[2];
|
||||
h->dims[3] = info->height - h->dims[1] - h->dims[3];
|
||||
FAIL_IF_ERROR( h->dims[2] <= 0 || h->dims[3] <= 0, "invalid output resolution %dx%d\n", h->dims[2], h->dims[3] );
|
||||
|
||||
if( info->width != h->dims[2] || info->height != h->dims[3] )
|
||||
x264_cli_log( NAME, X264_LOG_INFO, "cropping to %dx%d\n", h->dims[2], h->dims[3] );
|
||||
else
|
||||
{
|
||||
/* do nothing as the user supplied 0s for all the values */
|
||||
free( h );
|
||||
return 0;
|
||||
}
|
||||
/* done initializing, overwrite values */
|
||||
info->width = h->dims[2];
|
||||
info->height = h->dims[3];
|
||||
|
||||
h->prev_filter = *filter;
|
||||
h->prev_hnd = *handle;
|
||||
*handle = h;
|
||||
*filter = crop_filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
|
||||
{
|
||||
crop_hnd_t *h = handle;
|
||||
if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
|
||||
return -1;
|
||||
output->img.width = h->dims[2];
|
||||
output->img.height = h->dims[3];
|
||||
/* shift the plane pointers down 'top' rows and right 'left' columns. */
|
||||
for( int i = 0; i < output->img.planes; i++ )
|
||||
{
|
||||
intptr_t offset = output->img.stride[i] * h->dims[1] * h->csp->height[i];
|
||||
offset += h->dims[0] * h->csp->width[i] * x264_cli_csp_depth_factor( output->img.csp );
|
||||
output->img.plane[i] += offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
crop_hnd_t *h = handle;
|
||||
/* NO filter should ever have a dependent release based on the plane pointers,
|
||||
* so avoid unnecessary unshifting */
|
||||
return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
crop_hnd_t *h = handle;
|
||||
h->prev_filter.free( h->prev_hnd );
|
||||
free( h );
|
||||
}
|
||||
|
||||
cli_vid_filter_t crop_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
|
||||
261
filters/video/depth.c
Normal file
261
filters/video/depth.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*****************************************************************************
|
||||
* depth.c: bit-depth conversion video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Oskar Arvidsson <oskar@irock.se>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#define depth_filter x264_glue3(depth, BIT_DEPTH, filter)
|
||||
#if BIT_DEPTH == 8
|
||||
#define NAME "depth_8"
|
||||
#else
|
||||
#define NAME "depth_10"
|
||||
#endif
|
||||
|
||||
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
|
||||
|
||||
cli_vid_filter_t depth_filter;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hnd_t prev_hnd;
|
||||
cli_vid_filter_t prev_filter;
|
||||
|
||||
int bit_depth;
|
||||
int dst_csp;
|
||||
cli_pic_t buffer;
|
||||
int16_t *error_buf;
|
||||
} depth_hnd_t;
|
||||
|
||||
static int depth_filter_csp_is_supported( int csp )
|
||||
{
|
||||
int csp_mask = csp & X264_CSP_MASK;
|
||||
return csp_mask == X264_CSP_I400 ||
|
||||
csp_mask == X264_CSP_I420 ||
|
||||
csp_mask == X264_CSP_I422 ||
|
||||
csp_mask == X264_CSP_I444 ||
|
||||
csp_mask == X264_CSP_YV12 ||
|
||||
csp_mask == X264_CSP_YV16 ||
|
||||
csp_mask == X264_CSP_YV24 ||
|
||||
csp_mask == X264_CSP_NV12 ||
|
||||
csp_mask == X264_CSP_NV21 ||
|
||||
csp_mask == X264_CSP_NV16 ||
|
||||
csp_mask == X264_CSP_BGR ||
|
||||
csp_mask == X264_CSP_RGB ||
|
||||
csp_mask == X264_CSP_BGRA;
|
||||
}
|
||||
|
||||
static int csp_num_interleaved( int csp, int plane )
|
||||
{
|
||||
int csp_mask = csp & X264_CSP_MASK;
|
||||
return (csp_mask == X264_CSP_NV12 || csp_mask == X264_CSP_NV21 || csp_mask == X264_CSP_NV16) && plane == 1 ? 2 :
|
||||
csp_mask == X264_CSP_BGR || csp_mask == X264_CSP_RGB ? 3 :
|
||||
csp_mask == X264_CSP_BGRA ? 4 :
|
||||
1;
|
||||
}
|
||||
|
||||
/* The dithering algorithm is based on Sierra-2-4A error diffusion. It has been
|
||||
* written in such a way so that if the source has been upconverted using the
|
||||
* same algorithm as used in scale_image, dithering down to the source bit
|
||||
* depth again is lossless. */
|
||||
#define DITHER_PLANE( pitch ) \
|
||||
static void dither_plane_##pitch( pixel *dst, int dst_stride, uint16_t *src, int src_stride, \
|
||||
int width, int height, int16_t *errors ) \
|
||||
{ \
|
||||
const int lshift = 16-BIT_DEPTH; \
|
||||
const int rshift = 16-BIT_DEPTH+2; \
|
||||
const int half = 1 << (16-BIT_DEPTH+1); \
|
||||
const int pixel_max = (1 << BIT_DEPTH)-1; \
|
||||
memset( errors, 0, (width+1) * sizeof(int16_t) ); \
|
||||
for( int y = 0; y < height; y++, src += src_stride, dst += dst_stride ) \
|
||||
{ \
|
||||
int err = 0; \
|
||||
for( int x = 0; x < width; x++ ) \
|
||||
{ \
|
||||
err = err*2 + errors[x] + errors[x+1]; \
|
||||
dst[x*pitch] = x264_clip3( ((src[x*pitch]<<2)+err+half) >> rshift, 0, pixel_max ); \
|
||||
errors[x] = err = src[x*pitch] - (dst[x*pitch] << lshift); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
DITHER_PLANE( 1 )
|
||||
DITHER_PLANE( 2 )
|
||||
DITHER_PLANE( 3 )
|
||||
DITHER_PLANE( 4 )
|
||||
|
||||
static void dither_image( cli_image_t *out, cli_image_t *img, int16_t *error_buf )
|
||||
{
|
||||
int csp_mask = img->csp & X264_CSP_MASK;
|
||||
for( int i = 0; i < img->planes; i++ )
|
||||
{
|
||||
int num_interleaved = csp_num_interleaved( img->csp, i );
|
||||
int height = x264_cli_csps[csp_mask].height[i] * img->height;
|
||||
int width = x264_cli_csps[csp_mask].width[i] * img->width / num_interleaved;
|
||||
|
||||
#define CALL_DITHER_PLANE( pitch, off ) \
|
||||
dither_plane_##pitch( ((pixel*)out->plane[i])+off, out->stride[i]/SIZEOF_PIXEL, \
|
||||
((uint16_t*)img->plane[i])+off, img->stride[i]/2, width, height, error_buf )
|
||||
|
||||
if( num_interleaved == 4 )
|
||||
{
|
||||
CALL_DITHER_PLANE( 4, 0 );
|
||||
CALL_DITHER_PLANE( 4, 1 );
|
||||
CALL_DITHER_PLANE( 4, 2 );
|
||||
CALL_DITHER_PLANE( 4, 3 ); //we probably can skip this one
|
||||
}
|
||||
else if( num_interleaved == 3 )
|
||||
{
|
||||
CALL_DITHER_PLANE( 3, 0 );
|
||||
CALL_DITHER_PLANE( 3, 1 );
|
||||
CALL_DITHER_PLANE( 3, 2 );
|
||||
}
|
||||
else if( num_interleaved == 2 )
|
||||
{
|
||||
CALL_DITHER_PLANE( 2, 0 );
|
||||
CALL_DITHER_PLANE( 2, 1 );
|
||||
}
|
||||
else //if( num_interleaved == 1 )
|
||||
{
|
||||
CALL_DITHER_PLANE( 1, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scale_image( cli_image_t *output, cli_image_t *img )
|
||||
{
|
||||
int csp_mask = img->csp & X264_CSP_MASK;
|
||||
const int shift = BIT_DEPTH - 8;
|
||||
for( int i = 0; i < img->planes; i++ )
|
||||
{
|
||||
uint8_t *src = img->plane[i];
|
||||
uint16_t *dst = (uint16_t*)output->plane[i];
|
||||
int height = x264_cli_csps[csp_mask].height[i] * img->height;
|
||||
int width = x264_cli_csps[csp_mask].width[i] * img->width;
|
||||
|
||||
for( int j = 0; j < height; j++ )
|
||||
{
|
||||
for( int k = 0; k < width; k++ )
|
||||
dst[k] = src[k] << shift;
|
||||
|
||||
src += img->stride[i];
|
||||
dst += output->stride[i]/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
|
||||
{
|
||||
depth_hnd_t *h = handle;
|
||||
|
||||
if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
|
||||
return -1;
|
||||
|
||||
if( h->bit_depth < 16 && output->img.csp & X264_CSP_HIGH_DEPTH )
|
||||
{
|
||||
dither_image( &h->buffer.img, &output->img, h->error_buf );
|
||||
output->img = h->buffer.img;
|
||||
}
|
||||
else if( h->bit_depth > 8 && !(output->img.csp & X264_CSP_HIGH_DEPTH) )
|
||||
{
|
||||
scale_image( &h->buffer.img, &output->img );
|
||||
output->img = h->buffer.img;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
depth_hnd_t *h = handle;
|
||||
return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
depth_hnd_t *h = handle;
|
||||
h->prev_filter.free( h->prev_hnd );
|
||||
x264_cli_pic_clean( &h->buffer );
|
||||
x264_free( h );
|
||||
}
|
||||
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info,
|
||||
x264_param_t *param, char *opt_string )
|
||||
{
|
||||
int ret = 0;
|
||||
int change_fmt = (info->csp ^ param->i_csp) & X264_CSP_HIGH_DEPTH;
|
||||
int csp = ~(~info->csp ^ change_fmt);
|
||||
int bit_depth = 8*x264_cli_csp_depth_factor( csp );
|
||||
|
||||
if( opt_string )
|
||||
{
|
||||
static const char * const optlist[] = { "bit_depth", NULL };
|
||||
char **opts = x264_split_options( opt_string, optlist );
|
||||
|
||||
if( opts )
|
||||
{
|
||||
char *str_bit_depth = x264_get_option( "bit_depth", opts );
|
||||
bit_depth = x264_otoi( str_bit_depth, -1 );
|
||||
|
||||
ret = bit_depth < 8 || bit_depth > 16;
|
||||
csp = bit_depth > 8 ? csp | X264_CSP_HIGH_DEPTH : csp & ~X264_CSP_HIGH_DEPTH;
|
||||
change_fmt = (info->csp ^ csp) & X264_CSP_HIGH_DEPTH;
|
||||
free( opts );
|
||||
}
|
||||
else
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
FAIL_IF_ERROR( bit_depth != BIT_DEPTH, "this filter supports only bit depth %d\n", BIT_DEPTH );
|
||||
FAIL_IF_ERROR( ret, "unsupported bit depth conversion.\n" );
|
||||
|
||||
/* only add the filter to the chain if it's needed */
|
||||
if( change_fmt || bit_depth != 8 * x264_cli_csp_depth_factor( csp ) )
|
||||
{
|
||||
FAIL_IF_ERROR( !depth_filter_csp_is_supported(csp), "unsupported colorspace.\n" );
|
||||
depth_hnd_t *h = x264_malloc( sizeof(depth_hnd_t) + (info->width+1)*sizeof(int16_t) );
|
||||
|
||||
if( !h )
|
||||
return -1;
|
||||
|
||||
h->error_buf = (int16_t*)(h + 1);
|
||||
h->dst_csp = csp;
|
||||
h->bit_depth = bit_depth;
|
||||
h->prev_hnd = *handle;
|
||||
h->prev_filter = *filter;
|
||||
|
||||
if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, info->width, info->height ) )
|
||||
{
|
||||
x264_free( h );
|
||||
return -1;
|
||||
}
|
||||
|
||||
*handle = h;
|
||||
*filter = depth_filter;
|
||||
info->csp = h->dst_csp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cli_vid_filter_t depth_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };
|
||||
136
filters/video/fix_vfr_pts.c
Normal file
136
filters/video/fix_vfr_pts.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*****************************************************************************
|
||||
* fix_vfr_pts.c: vfr pts fixing video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
#include "internal.h"
|
||||
|
||||
/* This filter calculates and store the frame's duration to the frame data
|
||||
* (if it is not already calculated when the frame arrives to this point)
|
||||
* so it can be used by filters that will need to reconstruct pts due to
|
||||
* out-of-order frame requests */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hnd_t prev_hnd;
|
||||
cli_vid_filter_t prev_filter;
|
||||
|
||||
/* we need 1 buffer picture and 1 place holder */
|
||||
cli_pic_t buffer;
|
||||
cli_pic_t holder;
|
||||
int buffer_allocated;
|
||||
int holder_frame;
|
||||
int holder_ret;
|
||||
int64_t pts;
|
||||
int64_t last_duration;
|
||||
} fix_vfr_pts_hnd_t;
|
||||
|
||||
cli_vid_filter_t fix_vfr_pts_filter;
|
||||
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
/* if the input is not vfr, we don't do anything */
|
||||
if( !info->vfr )
|
||||
return 0;
|
||||
fix_vfr_pts_hnd_t *h = calloc( 1, sizeof(fix_vfr_pts_hnd_t) );
|
||||
if( !h )
|
||||
return -1;
|
||||
|
||||
h->holder_frame = -1;
|
||||
h->prev_hnd = *handle;
|
||||
h->prev_filter = *filter;
|
||||
*handle = h;
|
||||
*filter = fix_vfr_pts_filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
|
||||
{
|
||||
fix_vfr_pts_hnd_t *h = handle;
|
||||
/* if we want the holder picture and it errored, return the error. */
|
||||
if( frame == h->holder_frame )
|
||||
{
|
||||
if( h->holder_ret )
|
||||
return h->holder_ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if we have a holder frame and we don't want it, release the frame */
|
||||
if( h->holder_frame > 0 && h->holder_frame < frame && h->prev_filter.release_frame( h->prev_hnd, &h->holder, h->holder_frame ) )
|
||||
return -1;
|
||||
h->holder_frame = -1;
|
||||
if( h->prev_filter.get_frame( h->prev_hnd, &h->holder, frame ) )
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if the frame's duration is not set already, read the next frame to set it. */
|
||||
if( !h->holder.duration )
|
||||
{
|
||||
/* allocate a buffer picture if we didn't already */
|
||||
if( !h->buffer_allocated )
|
||||
{
|
||||
if( x264_cli_pic_alloc( &h->buffer, h->holder.img.csp, h->holder.img.width, h->holder.img.height ) )
|
||||
return -1;
|
||||
h->buffer_allocated = 1;
|
||||
}
|
||||
h->holder_frame = frame+1;
|
||||
/* copy the current frame to the buffer, release it, and then read in the next frame to the placeholder */
|
||||
if( x264_cli_pic_copy( &h->buffer, &h->holder ) || h->prev_filter.release_frame( h->prev_hnd, &h->holder, frame ) )
|
||||
return -1;
|
||||
h->holder_ret = h->prev_filter.get_frame( h->prev_hnd, &h->holder, h->holder_frame );
|
||||
/* suppress non-monotonic pts warnings by setting the duration to be at least 1 */
|
||||
if( !h->holder_ret )
|
||||
h->last_duration = X264_MAX( h->holder.pts - h->buffer.pts, 1 );
|
||||
h->buffer.duration = h->last_duration;
|
||||
*output = h->buffer;
|
||||
}
|
||||
else
|
||||
*output = h->holder;
|
||||
|
||||
output->pts = h->pts;
|
||||
h->pts += output->duration;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
fix_vfr_pts_hnd_t *h = handle;
|
||||
/* if the frame is the buffered one, it's already been released */
|
||||
if( frame == (h->holder_frame - 1) )
|
||||
return 0;
|
||||
return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
fix_vfr_pts_hnd_t *h = handle;
|
||||
h->prev_filter.free( h->prev_hnd );
|
||||
if( h->buffer_allocated )
|
||||
x264_cli_pic_clean( &h->buffer );
|
||||
free( h );
|
||||
}
|
||||
|
||||
cli_vid_filter_t fix_vfr_pts_filter = { "fix_vfr_pts", NULL, init, get_frame, release_frame, free_filter, NULL };
|
||||
60
filters/video/internal.c
Normal file
60
filters/video/internal.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/*****************************************************************************
|
||||
* internal.c: video filter utilities
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "x264", __VA_ARGS__ )
|
||||
|
||||
void x264_cli_plane_copy( uint8_t *dst, int i_dst, uint8_t *src, int i_src, int w, int h )
|
||||
{
|
||||
while( h-- )
|
||||
{
|
||||
memcpy( dst, src, w );
|
||||
dst += i_dst;
|
||||
src += i_src;
|
||||
}
|
||||
}
|
||||
|
||||
int x264_cli_pic_copy( cli_pic_t *out, cli_pic_t *in )
|
||||
{
|
||||
int csp = in->img.csp & X264_CSP_MASK;
|
||||
FAIL_IF_ERROR( x264_cli_csp_is_invalid( in->img.csp ), "invalid colorspace arg %d\n", in->img.csp );
|
||||
FAIL_IF_ERROR( in->img.csp != out->img.csp || in->img.height != out->img.height
|
||||
|| in->img.width != out->img.width, "incompatible frame properties\n" );
|
||||
/* copy data */
|
||||
out->duration = in->duration;
|
||||
out->pts = in->pts;
|
||||
out->opaque = in->opaque;
|
||||
|
||||
for( int i = 0; i < out->img.planes; i++ )
|
||||
{
|
||||
int height = in->img.height * x264_cli_csps[csp].height[i];
|
||||
int width = in->img.width * x264_cli_csps[csp].width[i];
|
||||
width *= x264_cli_csp_depth_factor( in->img.csp );
|
||||
x264_cli_plane_copy( out->img.plane[i], out->img.stride[i], in->img.plane[i],
|
||||
in->img.stride[i], width, height );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
34
filters/video/internal.h
Normal file
34
filters/video/internal.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*****************************************************************************
|
||||
* internal.h: video filter utilities
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef X264_FILTER_VIDEO_INTERNAL_H
|
||||
#define X264_FILTER_VIDEO_INTERNAL_H
|
||||
|
||||
#include "video.h"
|
||||
|
||||
void x264_cli_plane_copy( uint8_t *dst, int i_dst, uint8_t *src, int i_src, int w, int h );
|
||||
int x264_cli_pic_copy( cli_pic_t *out, cli_pic_t *in );
|
||||
|
||||
#endif
|
||||
612
filters/video/resize.c
Normal file
612
filters/video/resize.c
Normal file
@@ -0,0 +1,612 @@
|
||||
/*****************************************************************************
|
||||
* resize.c: resize video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
|
||||
#define NAME "resize"
|
||||
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
|
||||
|
||||
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->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>
|
||||
|
||||
#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
|
||||
{
|
||||
hnd_t prev_hnd;
|
||||
cli_vid_filter_t prev_filter;
|
||||
|
||||
cli_pic_t buffer;
|
||||
int buffer_allocated;
|
||||
int dst_csp;
|
||||
int input_range;
|
||||
struct SwsContext *ctx;
|
||||
uint32_t ctx_flags;
|
||||
/* state of swapping chroma planes pre and post resize */
|
||||
int pre_swap_chroma;
|
||||
int post_swap_chroma;
|
||||
int fast_mono; /* yuv with planar luma can be "converted" to monochrome by simply ignoring 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;
|
||||
|
||||
static void help( int longhelp )
|
||||
{
|
||||
printf( " "NAME":[width,height][,sar][,fittobox][,csp][,method]\n" );
|
||||
if( !longhelp )
|
||||
return;
|
||||
printf( " resizes frames based on the given criteria:\n"
|
||||
" - 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 constraints\n"
|
||||
" - width, height, both\n"
|
||||
" - fittobox and sar: same as above except with specified sar\n"
|
||||
" - csp: convert to the given csp. syntax: [name][:depth]\n"
|
||||
" - valid csp names [keep current]: " );
|
||||
|
||||
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 )
|
||||
{
|
||||
uint32_t flag = 0;
|
||||
if( !strcasecmp( name, "fastbilinear" ) )
|
||||
flag = SWS_FAST_BILINEAR;
|
||||
else if( !strcasecmp( name, "bilinear" ) )
|
||||
flag = SWS_BILINEAR;
|
||||
else if( !strcasecmp( name, "bicubic" ) )
|
||||
flag = SWS_BICUBIC;
|
||||
else if( !strcasecmp( name, "experimental" ) )
|
||||
flag = SWS_X;
|
||||
else if( !strcasecmp( name, "point" ) )
|
||||
flag = SWS_POINT;
|
||||
else if( !strcasecmp( name, "area" ) )
|
||||
flag = SWS_AREA;
|
||||
else if( !strcasecmp( name, "bicublin" ) )
|
||||
flag = SWS_BICUBLIN;
|
||||
else if( !strcasecmp( name, "gauss" ) )
|
||||
flag = SWS_GAUSS;
|
||||
else if( !strcasecmp( name, "sinc" ) )
|
||||
flag = SWS_SINC;
|
||||
else if( !strcasecmp( name, "lanczos" ) )
|
||||
flag = SWS_LANCZOS;
|
||||
else if( !strcasecmp( name, "spline" ) )
|
||||
flag = SWS_SPLINE;
|
||||
else // default
|
||||
flag = SWS_BICUBIC;
|
||||
return flag;
|
||||
}
|
||||
|
||||
static int convert_csp_to_pix_fmt( int csp )
|
||||
{
|
||||
if( csp&X264_CSP_OTHER )
|
||||
return csp&X264_CSP_MASK;
|
||||
switch( csp&X264_CSP_MASK )
|
||||
{
|
||||
case X264_CSP_I400: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_GRAY16 : AV_PIX_FMT_GRAY8;
|
||||
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 following has no equivalent 16-bit 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;
|
||||
case X264_CSP_YUYV: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_NONE : AV_PIX_FMT_YUYV422;
|
||||
case X264_CSP_UYVY: return csp&X264_CSP_HIGH_DEPTH ? AV_PIX_FMT_NONE : AV_PIX_FMT_UYVY422;
|
||||
/* the following is not supported by swscale at all */
|
||||
case X264_CSP_NV16:
|
||||
default: return AV_PIX_FMT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static int pix_number_of_planes( const AVPixFmtDescriptor *pix_desc )
|
||||
{
|
||||
int num_planes = 0;
|
||||
for( int i = 0; i < pix_desc->nb_components; i++ )
|
||||
{
|
||||
int plane_plus1 = pix_desc->comp[i].plane + 1;
|
||||
num_planes = X264_MAX( plane_plus1, num_planes );
|
||||
}
|
||||
return num_planes;
|
||||
}
|
||||
|
||||
static int pick_closest_supported_csp( int csp )
|
||||
{
|
||||
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_I400;
|
||||
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 > 8 )
|
||||
ret |= X264_CSP_HIGH_DEPTH;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_opts( const char * const *optlist, char **opts, video_info_t *info, resizer_hnd_t *h )
|
||||
{
|
||||
uint32_t out_sar_w, out_sar_h;
|
||||
|
||||
char *str_width = x264_get_option( optlist[0], opts );
|
||||
char *str_height = x264_get_option( optlist[1], opts );
|
||||
char *str_sar = x264_get_option( optlist[2], opts );
|
||||
char *fittobox = x264_get_option( optlist[3], opts );
|
||||
char *str_csp = x264_get_option( optlist[4], opts );
|
||||
int width = x264_otoi( str_width, -1 );
|
||||
int height = x264_otoi( str_height, -1 );
|
||||
|
||||
int csp_only = 0;
|
||||
uint32_t in_sar_w = info->sar_width;
|
||||
uint32_t in_sar_h = info->sar_height;
|
||||
|
||||
if( str_csp )
|
||||
{
|
||||
/* 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;
|
||||
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 */
|
||||
if( !in_sar_w || !in_sar_h )
|
||||
in_sar_w = in_sar_h = 1;
|
||||
if( str_sar )
|
||||
{
|
||||
FAIL_IF_ERROR( 2 != sscanf( str_sar, "%u:%u", &out_sar_w, &out_sar_h ) &&
|
||||
2 != sscanf( str_sar, "%u/%u", &out_sar_w, &out_sar_h ),
|
||||
"invalid sar `%s'\n", str_sar );
|
||||
}
|
||||
else
|
||||
out_sar_w = out_sar_h = 1;
|
||||
if( fittobox )
|
||||
{
|
||||
/* resize the video to fit the box as much as possible */
|
||||
if( !strcasecmp( fittobox, "both" ) )
|
||||
{
|
||||
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( width <= 0, "invalid box width `%s'\n", x264_otos( str_width, "<unset>" ) );
|
||||
height = INT_MAX;
|
||||
}
|
||||
else if( !strcasecmp( fittobox, "height" ) )
|
||||
{
|
||||
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 );
|
||||
|
||||
/* maximally fit the new coded resolution to the box */
|
||||
const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
|
||||
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>" ) );
|
||||
if( !str_sar ) /* res only -> adjust sar */
|
||||
{
|
||||
/* new_sar = (new_h * old_w * old_sar_w) / (old_h * new_w * old_sar_h) */
|
||||
uint64_t num = (uint64_t)info->width * height;
|
||||
uint64_t den = (uint64_t)info->height * width;
|
||||
x264_reduce_fraction64( &num, &den );
|
||||
out_sar_w = num * in_sar_w;
|
||||
out_sar_h = den * in_sar_h;
|
||||
x264_reduce_fraction( &out_sar_w, &out_sar_h );
|
||||
}
|
||||
}
|
||||
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( 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( info->height * width_units / (height_units * csp->mod_height) );
|
||||
height *= csp->mod_height;
|
||||
}
|
||||
}
|
||||
else /* csp only */
|
||||
{
|
||||
h->dst.width = info->width;
|
||||
h->dst.height = info->height;
|
||||
csp_only = 1;
|
||||
}
|
||||
}
|
||||
if( !csp_only )
|
||||
{
|
||||
info->sar_width = out_sar_w;
|
||||
info->sar_height = out_sar_h;
|
||||
h->dst.width = width;
|
||||
h->dst.height = height;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int 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->fast_mono = 0;
|
||||
}
|
||||
h->scale = input_prop;
|
||||
if( !h->buffer_allocated && !h->fast_mono )
|
||||
{
|
||||
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( 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 */
|
||||
if( opt_string && !strcmp( opt_string, "normcsp" ) && !(info->csp&X264_CSP_OTHER) )
|
||||
return 0;
|
||||
/* if called by x264cli and nothing needs to be done, exit */
|
||||
if( !opt_string && !full_check( info, param ) )
|
||||
return 0;
|
||||
|
||||
static const char * const optlist[] = { "width", "height", "sar", "fittobox", "csp", "method", NULL };
|
||||
char **opts = x264_split_options( opt_string, optlist );
|
||||
if( !opts && opt_string )
|
||||
return -1;
|
||||
|
||||
resizer_hnd_t *h = calloc( 1, sizeof(resizer_hnd_t) );
|
||||
if( !h )
|
||||
return -1;
|
||||
|
||||
h->ctx_flags = convert_method_to_flag( x264_otos( x264_get_option( optlist[5], opts ), "" ) );
|
||||
|
||||
if( opts )
|
||||
{
|
||||
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" ) )
|
||||
{
|
||||
free( opts );
|
||||
/* 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
|
||||
{
|
||||
int err = handle_opts( optlist, opts, info, h );
|
||||
free( opts );
|
||||
if( err )
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
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;
|
||||
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 );
|
||||
|
||||
FAIL_IF_ERROR( h->dst.width <= 0 || h->dst.height <= 0 ||
|
||||
h->dst.width > MAX_RESOLUTION || h->dst.height > MAX_RESOLUTION,
|
||||
"invalid width x height (%dx%d)\n", h->dst.width, h->dst.height );
|
||||
|
||||
/* confirm swscale can support this conversion */
|
||||
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 */
|
||||
const x264_cli_csp_t *csp = x264_cli_get_csp( h->dst_csp );
|
||||
FAIL_IF_ERROR( h->dst.width % csp->mod_width || h->dst.height % csp->mod_height,
|
||||
"resolution %dx%d is not compliant with colorspace %s\n", h->dst.width, h->dst.height, csp->name );
|
||||
|
||||
if( h->dst.width != info->width || h->dst.height != info->height )
|
||||
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",
|
||||
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( dst_csp == X264_CSP_I400 &&
|
||||
((src_csp >= X264_CSP_I420 && src_csp <= X264_CSP_NV16) || src_csp == X264_CSP_I444 || src_csp == X264_CSP_YV24) &&
|
||||
h->dst.width == info->width && h->dst.height == info->height && h->dst.range == h->input_range )
|
||||
h->fast_mono = 1; /* use the input luma plane as is */
|
||||
|
||||
/* 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->fullrange = h->dst.range;
|
||||
|
||||
h->prev_filter = *filter;
|
||||
h->prev_hnd = *handle;
|
||||
*handle = h;
|
||||
*filter = resize_filter;
|
||||
|
||||
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( 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 && !h->fast_mono )
|
||||
{
|
||||
sws_scale( h->ctx, (const uint8_t* const*)output->img.plane, output->img.stride,
|
||||
0, output->img.height, h->buffer.img.plane, h->buffer.img.stride );
|
||||
output->img = h->buffer.img; /* copy img data */
|
||||
}
|
||||
else
|
||||
output->img.csp = h->dst_csp;
|
||||
if( h->post_swap_chroma )
|
||||
XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
resizer_hnd_t *h = handle;
|
||||
return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
resizer_hnd_t *h = handle;
|
||||
h->prev_filter.free( h->prev_hnd );
|
||||
if( h->ctx )
|
||||
sws_freeContext( h->ctx );
|
||||
if( h->buffer_allocated )
|
||||
x264_cli_pic_clean( &h->buffer );
|
||||
free( h );
|
||||
}
|
||||
|
||||
#else /* no swscale */
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if( !opt_string )
|
||||
ret = full_check( info, param );
|
||||
else
|
||||
{
|
||||
if( !strcmp( opt_string, "normcsp" ) )
|
||||
ret = info->csp & X264_CSP_OTHER;
|
||||
else
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
/* pass if nothing needs to be done, otherwise fail */
|
||||
FAIL_IF_ERROR( ret, "not compiled with swscale support\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define help NULL
|
||||
#define get_frame NULL
|
||||
#define release_frame NULL
|
||||
#define free_filter NULL
|
||||
#define convert_csp_to_pix_fmt(x) (x & X264_CSP_MASK)
|
||||
|
||||
#endif
|
||||
|
||||
cli_vid_filter_t resize_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
|
||||
158
filters/video/select_every.c
Normal file
158
filters/video/select_every.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*****************************************************************************
|
||||
* select_every.c: select-every video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
|
||||
#define NAME "select_every"
|
||||
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
|
||||
|
||||
#define MAX_PATTERN_SIZE 100 /* arbitrary */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
hnd_t prev_hnd;
|
||||
cli_vid_filter_t prev_filter;
|
||||
|
||||
int *pattern;
|
||||
int pattern_len;
|
||||
int step_size;
|
||||
int vfr;
|
||||
int64_t pts;
|
||||
} selvry_hnd_t;
|
||||
|
||||
cli_vid_filter_t select_every_filter;
|
||||
|
||||
static void help( int longhelp )
|
||||
{
|
||||
printf( " "NAME":step,offset1[,...]\n" );
|
||||
if( !longhelp )
|
||||
return;
|
||||
printf( " apply a selection pattern to input frames\n"
|
||||
" step: the number of frames in the pattern\n"
|
||||
" offsets: the offset into the step to select a frame\n"
|
||||
" see: http://avisynth.nl/index.php/Select#SelectEvery\n" );
|
||||
}
|
||||
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
selvry_hnd_t *h = malloc( sizeof(selvry_hnd_t) );
|
||||
if( !h )
|
||||
return -1;
|
||||
h->pattern_len = 0;
|
||||
h->step_size = 0;
|
||||
int offsets[MAX_PATTERN_SIZE];
|
||||
for( char *tok, *p = opt_string, UNUSED *saveptr = NULL; (tok = strtok_r( p, ",", &saveptr )); p = NULL )
|
||||
{
|
||||
int val = x264_otoi( tok, -1 );
|
||||
if( p )
|
||||
{
|
||||
FAIL_IF_ERROR( val <= 0, "invalid step `%s'\n", tok );
|
||||
h->step_size = val;
|
||||
continue;
|
||||
}
|
||||
FAIL_IF_ERROR( val < 0 || val >= h->step_size, "invalid offset `%s'\n", tok );
|
||||
FAIL_IF_ERROR( h->pattern_len >= MAX_PATTERN_SIZE, "max pattern size %d reached\n", MAX_PATTERN_SIZE );
|
||||
offsets[h->pattern_len++] = val;
|
||||
}
|
||||
FAIL_IF_ERROR( !h->step_size, "no step size provided\n" );
|
||||
FAIL_IF_ERROR( !h->pattern_len, "no offsets supplied\n" );
|
||||
|
||||
h->pattern = malloc( h->pattern_len * sizeof(int) );
|
||||
if( !h->pattern )
|
||||
return -1;
|
||||
memcpy( h->pattern, offsets, h->pattern_len * sizeof(int) );
|
||||
|
||||
/* determine required cache size to maintain pattern. */
|
||||
intptr_t max_rewind = 0;
|
||||
int min = h->step_size;
|
||||
for( int i = h->pattern_len-1; i >= 0; i-- )
|
||||
{
|
||||
min = X264_MIN( min, offsets[i] );
|
||||
if( i )
|
||||
max_rewind = X264_MAX( max_rewind, offsets[i-1] - min + 1 );
|
||||
/* reached maximum rewind size */
|
||||
if( max_rewind == h->step_size )
|
||||
break;
|
||||
}
|
||||
char name[20];
|
||||
sprintf( name, "cache_%d", param->i_bitdepth );
|
||||
if( x264_init_vid_filter( name, handle, filter, info, param, (void*)max_rewind ) )
|
||||
return -1;
|
||||
|
||||
/* done initing, overwrite properties */
|
||||
if( h->step_size != h->pattern_len )
|
||||
{
|
||||
info->num_frames = (uint64_t)info->num_frames * h->pattern_len / h->step_size;
|
||||
info->fps_den *= h->step_size;
|
||||
info->fps_num *= h->pattern_len;
|
||||
x264_reduce_fraction( &info->fps_num, &info->fps_den );
|
||||
if( info->vfr )
|
||||
{
|
||||
info->timebase_den *= h->pattern_len;
|
||||
info->timebase_num *= h->step_size;
|
||||
x264_reduce_fraction( &info->timebase_num, &info->timebase_den );
|
||||
}
|
||||
}
|
||||
|
||||
h->pts = 0;
|
||||
h->vfr = info->vfr;
|
||||
h->prev_filter = *filter;
|
||||
h->prev_hnd = *handle;
|
||||
*filter = select_every_filter;
|
||||
*handle = h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
|
||||
{
|
||||
selvry_hnd_t *h = handle;
|
||||
int pat_frame = h->pattern[frame % h->pattern_len] + frame / h->pattern_len * h->step_size;
|
||||
if( h->prev_filter.get_frame( h->prev_hnd, output, pat_frame ) )
|
||||
return -1;
|
||||
if( h->vfr )
|
||||
{
|
||||
output->pts = h->pts;
|
||||
h->pts += output->duration;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
selvry_hnd_t *h = handle;
|
||||
int pat_frame = h->pattern[frame % h->pattern_len] + frame / h->pattern_len * h->step_size;
|
||||
return h->prev_filter.release_frame( h->prev_hnd, pic, pat_frame );
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
selvry_hnd_t *h = handle;
|
||||
h->prev_filter.free( h->prev_hnd );
|
||||
free( h->pattern );
|
||||
free( h );
|
||||
}
|
||||
|
||||
cli_vid_filter_t select_every_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
|
||||
84
filters/video/source.c
Normal file
84
filters/video/source.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*****************************************************************************
|
||||
* source.c: source video filter
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
|
||||
/* This filter converts the demuxer API into the filtering API for video frames.
|
||||
* Backseeking is prohibited here as not all demuxers are capable of doing so. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cli_pic_t pic;
|
||||
hnd_t hin;
|
||||
int cur_frame;
|
||||
} source_hnd_t;
|
||||
|
||||
cli_vid_filter_t source_filter;
|
||||
|
||||
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
source_hnd_t *h = calloc( 1, sizeof(source_hnd_t) );
|
||||
if( !h )
|
||||
return -1;
|
||||
h->cur_frame = -1;
|
||||
|
||||
if( cli_input.picture_alloc( &h->pic, *handle, info->csp, info->width, info->height ) )
|
||||
return -1;
|
||||
|
||||
h->hin = *handle;
|
||||
*handle = h;
|
||||
*filter = source_filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
|
||||
{
|
||||
source_hnd_t *h = handle;
|
||||
/* do not allow requesting of frames from before the current position */
|
||||
if( frame <= h->cur_frame || cli_input.read_frame( &h->pic, h->hin, frame ) )
|
||||
return -1;
|
||||
h->cur_frame = frame;
|
||||
*output = h->pic;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
|
||||
{
|
||||
source_hnd_t *h = handle;
|
||||
if( cli_input.release_frame && cli_input.release_frame( &h->pic, h->hin ) )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_filter( hnd_t handle )
|
||||
{
|
||||
source_hnd_t *h = handle;
|
||||
cli_input.picture_clean( &h->pic, h->hin );
|
||||
cli_input.close_file( h->hin );
|
||||
free( h );
|
||||
}
|
||||
|
||||
cli_vid_filter_t source_filter = { "source", NULL, init, get_frame, release_frame, free_filter, NULL };
|
||||
83
filters/video/video.c
Normal file
83
filters/video/video.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*****************************************************************************
|
||||
* video.c: video filters
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "video.h"
|
||||
|
||||
static cli_vid_filter_t *first_filter = NULL;
|
||||
|
||||
static void register_vid_filter( cli_vid_filter_t *new_filter )
|
||||
{
|
||||
cli_vid_filter_t *filter_i = first_filter;
|
||||
while( filter_i->next )
|
||||
filter_i = filter_i->next;
|
||||
filter_i->next = new_filter;
|
||||
new_filter->next = NULL;
|
||||
}
|
||||
|
||||
#define REGISTER_VFILTER(name)\
|
||||
{\
|
||||
extern cli_vid_filter_t name##_filter;\
|
||||
register_vid_filter( &name##_filter );\
|
||||
}
|
||||
|
||||
void x264_register_vid_filters( void )
|
||||
{
|
||||
extern cli_vid_filter_t source_filter;
|
||||
first_filter = &source_filter;
|
||||
#if HAVE_BITDEPTH8
|
||||
REGISTER_VFILTER( cache_8 );
|
||||
REGISTER_VFILTER( depth_8 );
|
||||
#endif
|
||||
#if HAVE_BITDEPTH10
|
||||
REGISTER_VFILTER( cache_10 );
|
||||
REGISTER_VFILTER( depth_10 );
|
||||
#endif
|
||||
REGISTER_VFILTER( crop );
|
||||
REGISTER_VFILTER( fix_vfr_pts );
|
||||
REGISTER_VFILTER( resize );
|
||||
REGISTER_VFILTER( select_every );
|
||||
#if HAVE_GPL
|
||||
#endif
|
||||
}
|
||||
|
||||
int x264_init_vid_filter( const char *name, hnd_t *handle, cli_vid_filter_t *filter,
|
||||
video_info_t *info, x264_param_t *param, char *opt_string )
|
||||
{
|
||||
cli_vid_filter_t *filter_i = first_filter;
|
||||
while( filter_i && strcasecmp( name, filter_i->name ) )
|
||||
filter_i = filter_i->next;
|
||||
FAIL_IF_ERR( !filter_i, "x264", "invalid filter `%s'\n", name );
|
||||
if( filter_i->init( handle, filter, info, param, opt_string ) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x264_vid_filter_help( int longhelp )
|
||||
{
|
||||
for( cli_vid_filter_t *filter_i = first_filter; filter_i; filter_i = filter_i->next )
|
||||
if( filter_i->help )
|
||||
filter_i->help( longhelp );
|
||||
}
|
||||
63
filters/video/video.h
Normal file
63
filters/video/video.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*****************************************************************************
|
||||
* video.h: video filters
|
||||
*****************************************************************************
|
||||
* Copyright (C) 2010-2025 x264 project
|
||||
*
|
||||
* Authors: Steven Walters <kemuri9@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
|
||||
*
|
||||
* This program is also available under a commercial proprietary license.
|
||||
* For more information, contact us at licensing@x264.com.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef X264_FILTER_VIDEO_H
|
||||
#define X264_FILTER_VIDEO_H
|
||||
|
||||
#include "input/input.h"
|
||||
#include "filters/filters.h"
|
||||
|
||||
typedef struct cli_vid_filter_t cli_vid_filter_t;
|
||||
|
||||
struct cli_vid_filter_t
|
||||
{
|
||||
/* name of the filter */
|
||||
const char *name;
|
||||
/* help: a short message on what the filter does and how to use it.
|
||||
* this should only be implemented by filters directly accessible by the user */
|
||||
void (*help)( int longhelp );
|
||||
/* init: initializes the filter given the input clip properties and parameter to adjust them as necessary
|
||||
* with the given options provided by the user.
|
||||
* returns 0 on success, nonzero on error. */
|
||||
int (*init)( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string );
|
||||
/* get_frame: given the storage for the output frame and desired frame number, generate the frame accordingly.
|
||||
* the image data returned by get_frame should be treated as const and not be altered.
|
||||
* returns 0 on success, nonzero on error. */
|
||||
int (*get_frame)( hnd_t handle, cli_pic_t *output, int frame );
|
||||
/* release_frame: frame is done being used and is signaled for cleanup.
|
||||
* returns 0 on succeess, nonzero on error. */
|
||||
int (*release_frame)( hnd_t handle, cli_pic_t *pic, int frame );
|
||||
/* free: run filter cleanup procedures. */
|
||||
void (*free)( hnd_t handle );
|
||||
/* next registered filter, unused by filters themselves */
|
||||
cli_vid_filter_t *next;
|
||||
};
|
||||
|
||||
void x264_register_vid_filters( void );
|
||||
void x264_vid_filter_help( int longhelp );
|
||||
int x264_init_vid_filter( const char *name, hnd_t *handle, cli_vid_filter_t *filter,
|
||||
video_info_t *info, x264_param_t *param, char *opt_string );
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user