Commit 0467589e authored by Oskar Arvidsson's avatar Oskar Arvidsson Committed by Fiona Glaser

Finish support for high-depth video throughout x264

Add support for high depth input in libx264.
Add support for 16-bit colorspaces in the filtering system.
Add support for input bit depths in the interval [9,16] with the raw demuxer.
Add a depth filter to dither input to x264.
parent b6b8aea6
...@@ -18,7 +18,7 @@ SRCCLI = x264.c input/input.c input/timecode.c input/raw.c input/y4m.c \ ...@@ -18,7 +18,7 @@ SRCCLI = x264.c input/input.c input/timecode.c input/raw.c input/y4m.c \
output/flv.c output/flv_bytestream.c filters/filters.c \ output/flv.c output/flv_bytestream.c filters/filters.c \
filters/video/video.c filters/video/source.c filters/video/internal.c \ filters/video/video.c filters/video/source.c filters/video/internal.c \
filters/video/resize.c filters/video/cache.c filters/video/fix_vfr_pts.c \ filters/video/resize.c filters/video/cache.c filters/video/fix_vfr_pts.c \
filters/video/select_every.c filters/video/crop.c filters/video/select_every.c filters/video/crop.c filters/video/depth.c
SRCSO = SRCSO =
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <malloc.h> #include <malloc.h>
#endif #endif
const int x264_bit_depth = BIT_DEPTH;
static void x264_log_default( void *, int, const char *, va_list ); static void x264_log_default( void *, int, const char *, va_list );
/**************************************************************************** /****************************************************************************
...@@ -1047,19 +1049,20 @@ int x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_heigh ...@@ -1047,19 +1049,20 @@ int x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_heigh
x264_picture_init( pic ); x264_picture_init( pic );
pic->img.i_csp = i_csp; pic->img.i_csp = i_csp;
pic->img.i_plane = csp == X264_CSP_NV12 ? 2 : 3; pic->img.i_plane = csp == X264_CSP_NV12 ? 2 : 3;
pic->img.plane[0] = x264_malloc( 3 * i_width * i_height / 2 ); int depth_factor = i_csp & X264_CSP_HIGH_DEPTH ? 2 : 1;
pic->img.plane[0] = x264_malloc( 3 * i_width * i_height / 2 * depth_factor );
if( !pic->img.plane[0] ) if( !pic->img.plane[0] )
return -1; return -1;
pic->img.plane[1] = pic->img.plane[0] + i_width * i_height; pic->img.plane[1] = pic->img.plane[0] + i_width * i_height * depth_factor;
if( csp != X264_CSP_NV12 ) if( csp != X264_CSP_NV12 )
pic->img.plane[2] = pic->img.plane[1] + i_width * i_height / 4; pic->img.plane[2] = pic->img.plane[1] + i_width * i_height / 4 * depth_factor;
pic->img.i_stride[0] = i_width; pic->img.i_stride[0] = i_width * depth_factor;
if( csp == X264_CSP_NV12 ) if( csp == X264_CSP_NV12 )
pic->img.i_stride[1] = i_width; pic->img.i_stride[1] = i_width * depth_factor;
else else
{ {
pic->img.i_stride[1] = i_width / 2; pic->img.i_stride[1] = i_width / 2 * depth_factor;
pic->img.i_stride[2] = i_width / 2; pic->img.i_stride[2] = i_width / 2 * depth_factor;
} }
return 0; return 0;
} }
......
...@@ -263,6 +263,20 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src ) ...@@ -263,6 +263,20 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
return -1; return -1;
} }
#if X264_HIGH_BIT_DEPTH
if( !(src->img.i_csp & X264_CSP_HIGH_DEPTH) )
{
x264_log( h, X264_LOG_ERROR, "This build of x264 requires high depth input. Rebuild to support 8-bit input.\n" );
return -1;
}
#else
if( src->img.i_csp & X264_CSP_HIGH_DEPTH )
{
x264_log( h, X264_LOG_ERROR, "This build of x264 requires 8-bit input. Rebuild to support high depth input.\n" );
return -1;
}
#endif
dst->i_type = src->i_type; dst->i_type = src->i_type;
dst->i_qpplus1 = src->i_qpplus1; dst->i_qpplus1 = src->i_qpplus1;
dst->i_pts = dst->i_reordered_pts = src->i_pts; dst->i_pts = dst->i_reordered_pts = src->i_pts;
......
...@@ -302,12 +302,7 @@ void x264_plane_copy_c( pixel *dst, int i_dst, ...@@ -302,12 +302,7 @@ void x264_plane_copy_c( pixel *dst, int i_dst,
{ {
while( h-- ) while( h-- )
{ {
#if X264_HIGH_BIT_DEPTH memcpy( dst, src, w * sizeof(pixel) );
for( int i = 0; i < w; i++ )
dst[i] = src[i] << (BIT_DEPTH-8);
#else
memcpy( dst, src, w );
#endif
dst += i_dst; dst += i_dst;
src += i_src; src += i_src;
} }
...@@ -320,8 +315,8 @@ void x264_plane_copy_interleave_c( pixel *dst, int i_dst, ...@@ -320,8 +315,8 @@ void x264_plane_copy_interleave_c( pixel *dst, int i_dst,
for( int y=0; y<h; y++, dst+=i_dst, srcu+=i_srcu, srcv+=i_srcv ) for( int y=0; y<h; y++, dst+=i_dst, srcu+=i_srcu, srcv+=i_srcv )
for( int x=0; x<w; x++ ) for( int x=0; x<w; x++ )
{ {
dst[2*x] = srcu[x] << (BIT_DEPTH-8); dst[2*x] = ((pixel*)srcu)[x];
dst[2*x+1] = srcv[x] << (BIT_DEPTH-8); dst[2*x+1] = ((pixel*)srcv)[x];
} }
} }
......
...@@ -2777,12 +2777,14 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current, ...@@ -2777,12 +2777,14 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,
x264_log( h, X264_LOG_WARNING, "invalid DTS: PTS is less than DTS\n" ); x264_log( h, X264_LOG_WARNING, "invalid DTS: PTS is less than DTS\n" );
pic_out->img.i_csp = X264_CSP_NV12; pic_out->img.i_csp = X264_CSP_NV12;
#if X264_HIGH_BIT_DEPTH
pic_out->img.i_csp |= X264_CSP_HIGH_DEPTH;
#endif
pic_out->img.i_plane = h->fdec->i_plane; pic_out->img.i_plane = h->fdec->i_plane;
for( int i = 0; i < 2; i++ ) for( int i = 0; i < 2; i++ )
{ {
pic_out->img.i_stride[i] = h->fdec->i_stride[i]; pic_out->img.i_stride[i] = h->fdec->i_stride[i] * sizeof(pixel);
// FIXME This breaks the API when pixel != uint8_t. pic_out->img.plane[i] = (uint8_t*)h->fdec->plane[i];
pic_out->img.plane[i] = h->fdec->plane[i];
} }
x264_frame_push_unused( thread_current, h->fenc ); x264_frame_push_unused( thread_current, h->fenc );
......
...@@ -103,8 +103,12 @@ static int get_frame( hnd_t handle, cli_pic_t *output, int frame ) ...@@ -103,8 +103,12 @@ static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
output->img.height = h->dims[3]; output->img.height = h->dims[3];
/* shift the plane pointers down 'top' rows and right 'left' columns. */ /* shift the plane pointers down 'top' rows and right 'left' columns. */
for( int i = 0; i < output->img.planes; i++ ) for( int i = 0; i < output->img.planes; i++ )
output->img.plane[i] += (int)(output->img.stride[i] * h->dims[1] * h->csp->height[i] {
+ h->dims[0] * h->csp->width[i]); intptr_t offset = output->img.stride[i] * h->dims[1] * h->csp->height[i];
offset += h->dims[0] * h->csp->width[i];
offset *= x264_cli_csp_depth_factor( output->img.csp );
output->img.plane[i] += offset;
}
return 0; return 0;
} }
......
/*****************************************************************************
* depth.c: x264 video depth filter
*****************************************************************************
* Copyright (C) 2010 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.
*****************************************************************************/
#include "video.h"
#define NAME "depth"
#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_I420 ||
csp_mask == X264_CSP_I422 ||
csp_mask == X264_CSP_I444 ||
csp_mask == X264_CSP_YV12 ||
csp_mask == X264_CSP_NV12;
}
static int csp_num_interleaved( int csp, int plane )
{
int csp_mask = csp & X264_CSP_MASK;
return ( csp_mask == X264_CSP_NV12 && plane == 1 ) ? 2 : 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 = 2*BIT_DEPTH-16; \
const int pixel_max = (1 << BIT_DEPTH)-1; \
const int half = 1 << (16-BIT_DEPTH); \
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]+half)<<2)+err)*pixel_max >> 18, 0, pixel_max ); \
errors[x] = err = src[x*pitch] - (dst[x*pitch] << lshift) - (dst[x*pitch] >> rshift); \
} \
} \
}
DITHER_PLANE( 1 )
DITHER_PLANE( 2 )
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 == 1 )
{
CALL_DITHER_PLANE( 1, 0 );
}
else
{
CALL_DITHER_PLANE( 2, 0 );
CALL_DITHER_PLANE( 2, 1 );
}
}
}
static void scale_image( cli_image_t *output, cli_image_t *img )
{
/* this function mimics how swscale does upconversion. 8-bit is converted
* to 16-bit through left shifting the orginal value with 8 and then adding
* the original value to that. This effectively keeps the full color range
* while also being fast. for n-bit we basically do the same thing, but we
* discard the lower 16-n bits. */
int csp_mask = img->csp & X264_CSP_MASK;
const int shift = 16-BIT_DEPTH;
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] << 8) + 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 *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;
x264_free_string_array( opts );
}
else
ret = 1;
}
FAIL_IF_ERROR( bit_depth != BIT_DEPTH, "this build 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 };
...@@ -51,6 +51,7 @@ int x264_cli_pic_copy( cli_pic_t *out, cli_pic_t *in ) ...@@ -51,6 +51,7 @@ int x264_cli_pic_copy( cli_pic_t *out, cli_pic_t *in )
{ {
int height = in->img.height * x264_cli_csps[csp].height[i]; int height = in->img.height * x264_cli_csps[csp].height[i];
int width = in->img.width * x264_cli_csps[csp].width[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], x264_cli_plane_copy( out->img.plane[i], out->img.stride[i], in->img.plane[i],
in->img.stride[i], width, height ); in->img.stride[i], width, height );
} }
......
...@@ -79,10 +79,21 @@ static void help( int longhelp ) ...@@ -79,10 +79,21 @@ static void help( int longhelp )
" - fittobox: resizes the video based on the desired contraints\n" " - fittobox: resizes the video based on the desired contraints\n"
" - width, height, both\n" " - width, height, both\n"
" - fittobox and sar: same as above except with specified sar\n" " - fittobox and sar: same as above except with specified sar\n"
" simultaneously converting to the given colorspace\n" " - csp: convert to the given csp. syntax: [name][:depth]\n"
" using resizer method [\"bicubic\"]\n" " - valid csp names [keep current]: " );
" - fastbilinear, bilinear, bicubic, experimental, point,\n"
" - area, bicublin, gauss, sinc, lanczos, spline\n" ); for( int i = X264_CSP_NONE+1; i < X264_CSP_CLI_MAX; i++ )
{
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_cpu_to_flag( uint32_t cpu ) static uint32_t convert_cpu_to_flag( uint32_t cpu )
...@@ -131,13 +142,15 @@ static int convert_csp_to_pix_fmt( int csp ) ...@@ -131,13 +142,15 @@ static int convert_csp_to_pix_fmt( int csp )
return csp&X264_CSP_MASK; return csp&X264_CSP_MASK;
switch( csp&X264_CSP_MASK ) switch( csp&X264_CSP_MASK )
{ {
case X264_CSP_I420: return PIX_FMT_YUV420P; case X264_CSP_YV12: /* specially handled via swapping chroma */
case X264_CSP_I422: return PIX_FMT_YUV422P; case X264_CSP_I420: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV420P16 : PIX_FMT_YUV420P;
case X264_CSP_I444: return PIX_FMT_YUV444P; case X264_CSP_I422: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV422P16 : PIX_FMT_YUV422P;
case X264_CSP_NV12: return PIX_FMT_NV12; case X264_CSP_I444: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV444P16 : PIX_FMT_YUV444P;
case X264_CSP_YV12: return PIX_FMT_YUV420P; /* specially handled via swapping chroma */ case X264_CSP_RGB: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_RGB48 : PIX_FMT_RGB24;
case X264_CSP_BGR: return PIX_FMT_BGR24; /* the next 3 csps have no equivalent 16bit depth in swscale */
case X264_CSP_BGRA: return PIX_FMT_BGRA; case X264_CSP_NV12: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_NONE : PIX_FMT_NV12;
case X264_CSP_BGR: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_NONE : PIX_FMT_BGR24;
case X264_CSP_BGRA: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_NONE : PIX_FMT_BGRA;
default: return PIX_FMT_NONE; default: return PIX_FMT_NONE;
} }
} }
...@@ -147,23 +160,30 @@ static int pick_closest_supported_csp( int csp ) ...@@ -147,23 +160,30 @@ static int pick_closest_supported_csp( int csp )
int pix_fmt = convert_csp_to_pix_fmt( csp ); int pix_fmt = convert_csp_to_pix_fmt( csp );
switch( pix_fmt ) switch( pix_fmt )
{ {
case PIX_FMT_YUV420P16LE:
case PIX_FMT_YUV420P16BE:
return X264_CSP_I420 | X264_CSP_HIGH_DEPTH;
case PIX_FMT_YUV422P: case PIX_FMT_YUV422P:
case PIX_FMT_YUV422P16LE:
case PIX_FMT_YUV422P16BE:
case PIX_FMT_YUYV422: case PIX_FMT_YUYV422:
case PIX_FMT_UYVY422: case PIX_FMT_UYVY422:
return X264_CSP_I422; return X264_CSP_I422;
case PIX_FMT_YUV422P16LE:
case PIX_FMT_YUV422P16BE:
return X264_CSP_I422 | X264_CSP_HIGH_DEPTH;
case PIX_FMT_YUV444P: case PIX_FMT_YUV444P:
return X264_CSP_I444;
case PIX_FMT_YUV444P16LE: case PIX_FMT_YUV444P16LE:
case PIX_FMT_YUV444P16BE: case PIX_FMT_YUV444P16BE:
return X264_CSP_I444; return X264_CSP_I444 | X264_CSP_HIGH_DEPTH;
case PIX_FMT_RGB24: // convert rgb to bgr case PIX_FMT_RGB24:
case PIX_FMT_RGB48BE:
case PIX_FMT_RGB48LE:
case PIX_FMT_RGB565BE: case PIX_FMT_RGB565BE:
case PIX_FMT_RGB565LE: case PIX_FMT_RGB565LE:
case PIX_FMT_RGB555BE: case PIX_FMT_RGB555BE:
case PIX_FMT_RGB555LE: case PIX_FMT_RGB555LE:
return X264_CSP_RGB;
case PIX_FMT_RGB48BE:
case PIX_FMT_RGB48LE:
return X264_CSP_RGB | X264_CSP_HIGH_DEPTH;
case PIX_FMT_BGR24: case PIX_FMT_BGR24:
case PIX_FMT_BGR565BE: case PIX_FMT_BGR565BE:
case PIX_FMT_BGR565LE: case PIX_FMT_BGR565LE:
...@@ -209,12 +229,27 @@ static int handle_opts( const char **optlist, char **opts, video_info_t *info, r ...@@ -209,12 +229,27 @@ static int handle_opts( const char **optlist, char **opts, video_info_t *info, r
if( str_csp ) if( str_csp )
{ {
/* output csp was specified, lookup against valid values */ /* output csp was specified, first check if optional depth was provided */
char *str_depth = strchr( str_csp, ':' );
int depth = x264_cli_csp_depth_factor( info->csp ) * 8;
if( str_depth )
{
/* csp bit depth was specified */
*str_depth++ = '\0';
depth = x264_otoi( str_depth, -1 );
FAIL_IF_ERROR( depth != 8 && depth != 16, "unsupported bit depth %d\n", depth );
}
/* now lookup against the list of valid csps */
int csp; int csp;
for( csp = X264_CSP_CLI_MAX-1; x264_cli_csps[csp].name && strcasecmp( x264_cli_csps[csp].name, str_csp ); ) if( strlen( str_csp ) == 0 )
csp--; csp = info->csp & X264_CSP_MASK;
else
for( csp = X264_CSP_CLI_MAX-1; x264_cli_csps[csp].name && strcasecmp( x264_cli_csps[csp].name, str_csp ); )
csp--;
FAIL_IF_ERROR( csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", str_csp ); FAIL_IF_ERROR( csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", str_csp );
h->dst_csp = 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 the input sar is currently invalid, set it to 1:1 so it can be used in math */
...@@ -366,8 +401,17 @@ static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x2 ...@@ -366,8 +401,17 @@ static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x2
h->swap_chroma = (info->csp & X264_CSP_MASK) == X264_CSP_YV12; h->swap_chroma = (info->csp & X264_CSP_MASK) == X264_CSP_YV12;
int src_pix_fmt = convert_csp_to_pix_fmt( info->csp ); int src_pix_fmt = convert_csp_to_pix_fmt( info->csp );
int src_pix_fmt_inv = convert_csp_to_pix_fmt( info->csp ^ X264_CSP_HIGH_DEPTH );
int dst_pix_fmt_inv = convert_csp_to_pix_fmt( h->dst_csp ^ X264_CSP_HIGH_DEPTH );
/* confirm swscale can support this conversion */ /* confirm swscale can support this conversion */
FAIL_IF_ERROR( src_pix_fmt == PIX_FMT_NONE && src_pix_fmt_inv != PIX_FMT_NONE,
"input colorspace %s with bit depth %d is not supported\n", sws_format_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", sws_format_name( src_pix_fmt ) ) FAIL_IF_ERROR( !sws_isSupportedInput( src_pix_fmt ), "input colorspace %s is not supported\n", sws_format_name( src_pix_fmt ) )
FAIL_IF_ERROR( h->dst.pix_fmt == PIX_FMT_NONE && dst_pix_fmt_inv != PIX_FMT_NONE,
"input colorspace %s with bit depth %d is not supported\n", sws_format_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", sws_format_name( h->dst.pix_fmt ) ) FAIL_IF_ERROR( !sws_isSupportedOutput( h->dst.pix_fmt ), "output colorspace %s is not supported\n", sws_format_name( h->dst.pix_fmt ) )
FAIL_IF_ERROR( h->dst.height != info->height && info->interlaced, FAIL_IF_ERROR( h->dst.height != info->height && info->interlaced,
"swscale is not compatible with interlaced vertical resizing\n" ) "swscale is not compatible with interlaced vertical resizing\n" )
......
...@@ -51,6 +51,7 @@ void x264_register_vid_filters() ...@@ -51,6 +51,7 @@ void x264_register_vid_filters()
REGISTER_VFILTER( fix_vfr_pts ); REGISTER_VFILTER( fix_vfr_pts );
REGISTER_VFILTER( resize ); REGISTER_VFILTER( resize );
REGISTER_VFILTER( select_every ); REGISTER_VFILTER( select_every );
REGISTER_VFILTER( depth );
#if HAVE_GPL #if HAVE_GPL
#endif #endif
} }
......
...@@ -32,7 +32,8 @@ const x264_cli_csp_t x264_cli_csps[] = { ...@@ -32,7 +32,8 @@ const x264_cli_csp_t x264_cli_csps[] = {
[X264_CSP_YV12] = { "yv12", 3, { 1, .5, .5 }, { 1, .5, .5 }, 2, 2 }, [X264_CSP_YV12] = { "yv12", 3, { 1, .5, .5 }, { 1, .5, .5 }, 2, 2 },
[X264_CSP_NV12] = { "nv12", 2, { 1, 1 }, { 1, .5 }, 2, 2 }, [X264_CSP_NV12] = { "nv12", 2, { 1, 1 }, { 1, .5 }, 2, 2 },
[X264_CSP_BGR] = { "bgr", 1, { 3 }, { 1 }, 1, 1 }, [X264_CSP_BGR] = { "bgr", 1, { 3 }, { 1 }, 1, 1 },
[X264_CSP_BGRA] = { "bgra", 1, { 4 }, { 1 }, 1, 1 } [X264_CSP_BGRA] = { "bgra", 1, { 4 }, { 1 }, 1, 1 },
[X264_CSP_RGB] = { "rgb", 1, { 3 }, { 1 }, 1, 1 },
}; };
int x264_cli_csp_is_invalid( int csp ) int x264_cli_csp_is_invalid( int csp )
...@@ -41,6 +42,13 @@ int x264_cli_csp_is_invalid( int csp ) ...@@ -41,6 +42,13 @@ int x264_cli_csp_is_invalid( int csp )
return csp_mask <= X264_CSP_NONE || csp_mask >= X264_CSP_CLI_MAX || csp & X264_CSP_OTHER; return csp_mask <= X264_CSP_NONE || csp_mask >= X264_CSP_CLI_MAX || csp & X264_CSP_OTHER;
} }
int x264_cli_csp_depth_factor( int csp )
{
if( x264_cli_csp_is_invalid( csp ) )
return 0;
return (csp & X264_CSP_HIGH_DEPTH) ? 2 : 1;
}
uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane ) uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane )
{ {
int csp_mask = csp & X264_CSP_MASK; int csp_mask = csp & X264_CSP_MASK;
...@@ -48,6 +56,7 @@ uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane ) ...@@ -48,6 +56,7 @@ uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane )
return 0; return 0;
uint64_t size = (uint64_t)width * height; uint64_t size = (uint64_t)width * height;
size *= x264_cli_csps[csp_mask].width[plane] * x264_cli_csps[csp_mask].height[plane]; size *= x264_cli_csps[csp_mask].width[plane] * x264_cli_csps[csp_mask].height[plane];
size *= x264_cli_csp_depth_factor( csp );
return size; return size;
} }
...@@ -78,7 +87,7 @@ int x264_cli_pic_alloc( cli_pic_t *pic, int csp, int width, int height ) ...@@ -78,7 +87,7 @@ int x264_cli_pic_alloc( cli_pic_t *pic, int csp, int width, int height )
pic->img.plane[i] = x264_malloc( x264_cli_pic_plane_size( csp, width, height, i ) ); pic->img.plane[i] = x264_malloc( x264_cli_pic_plane_size( csp, width, height, i ) );
if( !pic->img.plane[i] ) if( !pic->img.plane[i] )
return -1; return -1;
pic->img.stride[i] = width * x264_cli_csps[csp_mask].width[i]; pic->img.stride[i] = width * x264_cli_csps[csp_mask].width[i] * x264_cli_csp_depth_factor( csp );
} }
return 0; return 0;
......
...@@ -36,6 +36,7 @@ typedef struct ...@@ -36,6 +36,7 @@ typedef struct
char *index_file; char *index_file;
char *resolution; char *resolution;
char *colorspace; char *colorspace;
int bit_depth;
char *timebase; char *timebase;
int seek; int seek;
} cli_input_opt_t; } cli_input_opt_t;
...@@ -103,8 +104,9 @@ extern cli_input_t input; ...@@ -103,8 +104,9 @@ extern cli_input_t input;
#define X264_CSP_I444 (X264_CSP_MAX+1) /* yuv 4:4:4 planar */ #define X264_CSP_I444 (X264_CSP_MAX+1) /* yuv 4:4:4 planar */
#define X264_CSP_BGR (X264_CSP_MAX+2) /* packed bgr 24bits */ #define X264_CSP_BGR (X264_CSP_MAX+2) /* packed bgr 24bits */
#define X264_CSP_BGRA (X264_CSP_MAX+3) /* packed bgr 32bits */ #define X264_CSP_BGRA (X264_CSP_MAX+3) /* packed bgr 32bits */
#define X264_CSP_CLI_MAX (X264_CSP_MAX+4) /* end of list */ #define X264_CSP_RGB (X264_CSP_MAX+4) /* packed rgb 24bits */
#define X264_CSP_OTHER 0x2000 /* non x264 colorspace */ #define X264_CSP_CLI_MAX (X264_CSP_MAX+5) /* end of list */
#define X264_CSP_OTHER 0x4000 /* non x264 colorspace */
typedef struct typedef struct
{ {
...@@ -119,6 +121,7 @@ typedef struct ...@@ -119,6 +121,7 @@ typedef struct
extern const x264_cli_csp_t x264_cli_csps[]; extern const x264_cli_csp_t x264_cli_csps[];
int x264_cli_csp_is_invalid( int csp ); int x264_cli_csp_is_invalid( int csp );
int x264_cli_csp_depth_factor( int csp );
int x264_cli_pic_alloc( cli_pic_t *pic, int csp, int width, int height ); int x264_cli_pic_alloc( cli_pic_t *pic, int csp, int width, int height );
void x264_cli_pic_clean( cli_pic_t *pic ); void x264_cli_pic_clean( cli_pic_t *pic );
uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane ); uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane );
......
...@@ -34,11 +34,12 @@ typedef struct ...@@ -34,11 +34,12 @@ typedef struct
int next_frame; int next_frame;
uint64_t plane_size[4]; uint64_t plane_size[4];
uint64_t frame_size; uint64_t frame_size;
int bit_depth;
} raw_hnd_t; } raw_hnd_t;
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt ) static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{ {
raw_hnd_t *h = malloc( sizeof(raw_hnd_t) ); raw_hnd_t *h = calloc( 1, sizeof(raw_hnd_t) );
if( !h ) if( !h )
return -1; return -1;
...@@ -61,8 +62,10 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c ...@@ -61,8 +62,10 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c
else /* default */ else /* default */
info->csp = X264_CSP_I420; info->csp = X264_CSP_I420;
h->next_frame = 0; h->bit_depth = opt->bit_depth;