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.
output/flv.c output/flv_bytestream.c filters/filters.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/select_every.c filters/video/crop.c
+ filters/video/select_every.c filters/video/crop.c filters/video/depth.c
SRCSO =
#include <malloc.h>
#endif
+const int x264_bit_depth = BIT_DEPTH;
+
static void x264_log_default( void *, int, const char *, va_list );
/****************************************************************************
x264_picture_init( pic );
pic->img.i_csp = i_csp;
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] )
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 )
- pic->img.plane[2] = pic->img.plane[1] + i_width * i_height / 4;
- pic->img.i_stride[0] = i_width;
+ pic->img.plane[2] = pic->img.plane[1] + i_width * i_height / 4 * depth_factor;
+ pic->img.i_stride[0] = i_width * depth_factor;
if( csp == X264_CSP_NV12 )
- pic->img.i_stride[1] = i_width;
+ pic->img.i_stride[1] = i_width * depth_factor;
else
{
- pic->img.i_stride[1] = i_width / 2;
- pic->img.i_stride[2] = i_width / 2;
+ pic->img.i_stride[1] = i_width / 2 * depth_factor;
+ pic->img.i_stride[2] = i_width / 2 * depth_factor;
}
return 0;
}
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_qpplus1 = src->i_qpplus1;
dst->i_pts = dst->i_reordered_pts = src->i_pts;
{
while( h-- )
{
-#if X264_HIGH_BIT_DEPTH
- for( int i = 0; i < w; i++ )
- dst[i] = src[i] << (BIT_DEPTH-8);
-#else
- memcpy( dst, src, w );
-#endif
+ memcpy( dst, src, w * sizeof(pixel) );
dst += i_dst;
src += i_src;
}
for( int y=0; y<h; y++, dst+=i_dst, srcu+=i_srcu, srcv+=i_srcv )
for( int x=0; x<w; x++ )
{
- dst[2*x] = srcu[x] << (BIT_DEPTH-8);
- dst[2*x+1] = srcv[x] << (BIT_DEPTH-8);
+ dst[2*x] = ((pixel*)srcu)[x];
+ dst[2*x+1] = ((pixel*)srcv)[x];
}
}
x264_log( h, X264_LOG_WARNING, "invalid DTS: PTS is less than DTS\n" );
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;
for( int i = 0; i < 2; i++ )
{
- pic_out->img.i_stride[i] = h->fdec->i_stride[i];
- // FIXME This breaks the API when pixel != uint8_t.
- pic_out->img.plane[i] = h->fdec->plane[i];
+ pic_out->img.i_stride[i] = h->fdec->i_stride[i] * sizeof(pixel);
+ pic_out->img.plane[i] = (uint8_t*)h->fdec->plane[i];
}
x264_frame_push_unused( thread_current, h->fenc );
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++ )
- 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;
}
--- /dev/null
+/*****************************************************************************
+ * 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 };
{
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 );
}
" - fittobox: resizes the video based on the desired contraints\n"
" - width, height, both\n"
" - fittobox and sar: same as above except with specified sar\n"
- " simultaneously converting to the given colorspace\n"
- " using resizer method [\"bicubic\"]\n"
- " - fastbilinear, bilinear, bicubic, experimental, point,\n"
- " - area, bicublin, gauss, sinc, lanczos, spline\n" );
+ " - csp: convert to the given csp. syntax: [name][:depth]\n"
+ " - valid csp names [keep current]: " );
+
+ 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 )
return csp&X264_CSP_MASK;
switch( csp&X264_CSP_MASK )
{
- case X264_CSP_I420: return PIX_FMT_YUV420P;
- case X264_CSP_I422: return PIX_FMT_YUV422P;
- case X264_CSP_I444: return PIX_FMT_YUV444P;
- case X264_CSP_NV12: return PIX_FMT_NV12;
- case X264_CSP_YV12: return PIX_FMT_YUV420P; /* specially handled via swapping chroma */
- case X264_CSP_BGR: return PIX_FMT_BGR24;
- case X264_CSP_BGRA: return PIX_FMT_BGRA;
+ case X264_CSP_YV12: /* specially handled via swapping chroma */
+ case X264_CSP_I420: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV420P16 : PIX_FMT_YUV420P;
+ case X264_CSP_I422: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV422P16 : PIX_FMT_YUV422P;
+ case X264_CSP_I444: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_YUV444P16 : PIX_FMT_YUV444P;
+ case X264_CSP_RGB: return csp&X264_CSP_HIGH_DEPTH ? PIX_FMT_RGB48 : PIX_FMT_RGB24;
+ /* the next 3 csps have no equivalent 16bit depth in swscale */
+ 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;
}
}
int pix_fmt = convert_csp_to_pix_fmt( csp );
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_YUV422P16LE:
- case PIX_FMT_YUV422P16BE:
case PIX_FMT_YUYV422:
case PIX_FMT_UYVY422:
return X264_CSP_I422;
+ case PIX_FMT_YUV422P16LE:
+ case PIX_FMT_YUV422P16BE:
+ return X264_CSP_I422 | X264_CSP_HIGH_DEPTH;
case PIX_FMT_YUV444P:
+ return X264_CSP_I444;
case PIX_FMT_YUV444P16LE:
case PIX_FMT_YUV444P16BE:
- return X264_CSP_I444;
- case PIX_FMT_RGB24: // convert rgb to bgr
- case PIX_FMT_RGB48BE:
- case PIX_FMT_RGB48LE:
+ return X264_CSP_I444 | X264_CSP_HIGH_DEPTH;
+ case PIX_FMT_RGB24:
case PIX_FMT_RGB565BE:
case PIX_FMT_RGB565LE:
case PIX_FMT_RGB555BE:
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_BGR565BE:
case PIX_FMT_BGR565LE:
if( str_csp )
{
- /* output csp was specified, lookup against valid values */
+ /* output csp was specified, first check if optional depth was provided */
+ char *str_depth = strchr( str_csp, ':' );
+ int depth = x264_cli_csp_depth_factor( info->csp ) * 8;
+ if( str_depth )
+ {
+ /* csp bit depth was specified */
+ *str_depth++ = '\0';
+ depth = x264_otoi( str_depth, -1 );
+ FAIL_IF_ERROR( depth != 8 && depth != 16, "unsupported bit depth %d\n", depth );
+ }
+ /* now lookup against the list of valid csps */
int csp;
- for( csp = X264_CSP_CLI_MAX-1; x264_cli_csps[csp].name && strcasecmp( x264_cli_csps[csp].name, str_csp ); )
- csp--;
+ if( strlen( str_csp ) == 0 )
+ csp = info->csp & X264_CSP_MASK;
+ else
+ for( csp = X264_CSP_CLI_MAX-1; 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 );
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 */
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_inv = convert_csp_to_pix_fmt( info->csp ^ X264_CSP_HIGH_DEPTH );
+ int dst_pix_fmt_inv = convert_csp_to_pix_fmt( h->dst_csp ^ X264_CSP_HIGH_DEPTH );
+
/* confirm swscale can support this conversion */
+ FAIL_IF_ERROR( 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( 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( h->dst.height != info->height && info->interlaced,
"swscale is not compatible with interlaced vertical resizing\n" )
REGISTER_VFILTER( fix_vfr_pts );
REGISTER_VFILTER( resize );
REGISTER_VFILTER( select_every );
+ REGISTER_VFILTER( depth );
#if HAVE_GPL
#endif
}
[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_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 )
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 )
{
int csp_mask = csp & X264_CSP_MASK;
return 0;
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_csp_depth_factor( csp );
return size;
}
pic->img.plane[i] = x264_malloc( x264_cli_pic_plane_size( csp, width, height, i ) );
if( !pic->img.plane[i] )
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;
char *index_file;
char *resolution;
char *colorspace;
+ int bit_depth;
char *timebase;
int seek;
} cli_input_opt_t;
#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_BGRA (X264_CSP_MAX+3) /* packed bgr 32bits */
-#define X264_CSP_CLI_MAX (X264_CSP_MAX+4) /* end of list */
-#define X264_CSP_OTHER 0x2000 /* non x264 colorspace */
+#define X264_CSP_RGB (X264_CSP_MAX+4) /* packed rgb 24bits */
+#define X264_CSP_CLI_MAX (X264_CSP_MAX+5) /* end of list */
+#define X264_CSP_OTHER 0x4000 /* non x264 colorspace */
typedef struct
{
extern const x264_cli_csp_t x264_cli_csps[];
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 );
void x264_cli_pic_clean( cli_pic_t *pic );
uint64_t x264_cli_pic_plane_size( int csp, int width, int height, int plane );
int next_frame;
uint64_t plane_size[4];
uint64_t frame_size;
+ int bit_depth;
} raw_hnd_t;
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 )
return -1;
else /* default */
info->csp = X264_CSP_I420;
- h->next_frame = 0;
- info->vfr = 0;
+ h->bit_depth = opt->bit_depth;
+ FAIL_IF_ERROR( h->bit_depth < 8 || h->bit_depth > 16, "unsupported bit depth `%d'\n", h->bit_depth );
+ if( h->bit_depth > 8 )
+ info->csp |= X264_CSP_HIGH_DEPTH;
if( !strcmp( psz_filename, "-" ) )
h->fh = stdin;
info->thread_safe = 1;
info->num_frames = 0;
- h->frame_size = 0;
- for( int i = 0; i < x264_cli_csps[info->csp].planes; i++ )
+ info->vfr = 0;
+
+ const x264_cli_csp_t *csp = x264_cli_get_csp( info->csp );
+ for( int i = 0; i < csp->planes; i++ )
{
h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
h->frame_size += h->plane_size[i];
+ /* x264_cli_pic_plane_size returns the size in bytes, we need the value in pixels from here on */
+ h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp );
}
if( x264_is_regular_file( h->fh ) )
static int read_frame_internal( cli_pic_t *pic, raw_hnd_t *h )
{
int error = 0;
+ int pixel_depth = x264_cli_csp_depth_factor( pic->img.csp );
for( int i = 0; i < pic->img.planes && !error; i++ )
- error |= fread( pic->img.plane[i], h->plane_size[i], 1, h->fh ) <= 0;
+ {
+ error |= fread( pic->img.plane[i], pixel_depth, h->plane_size[i], h->fh ) != h->plane_size[i];
+ if( h->bit_depth & 7 )
+ {
+ /* upconvert non 16bit high depth planes to 16bit using the same
+ * algorithm as used in the depth filter. */
+ uint16_t *plane = (uint16_t*)pic->img.plane[i];
+ uint64_t pixel_count = h->plane_size[i];
+ int lshift = 16 - h->bit_depth;
+ int rshift = 2*h->bit_depth - 16;
+ for( uint64_t j = 0; j < pixel_count; j++ )
+ plane[j] = (plane[j] << lshift) + (plane[j] >> rshift);
+ }
+ }
return error;
}
#else
printf( "using a non-gcc compiler\n" );
#endif
- printf( "configuration: --bit-depth=%d\n", BIT_DEPTH );
+ printf( "configuration: --bit-depth=%d\n", x264_bit_depth );
printf( "x264 license: " );
#if HAVE_GPL
printf( "GPL version 2 or later\n" );
#else
"no",
#endif
- BIT_DEPTH
+ x264_bit_depth
);
H0( "Example usage:\n" );
H0( "\n" );
" - %s\n", demuxer_names[0], stringify_names( buf, demuxer_names ) );
H1( " --input-csp <string> Specify input colorspace format for raw input\n" );
print_csp_names( longhelp );
+ H1( " --input-depth <integer> Specify input bit depth for raw input\n" );
H1( " --input-res <intxint> Specify input resolution (width x height)\n" );
H1( " --index <string> Filename for input index file\n" );
H0( " --sar width:height Specify Sample Aspect Ratio\n" );
OPT_LOG_LEVEL,
OPT_VIDEO_FILTER,
OPT_INPUT_RES,
- OPT_INPUT_CSP
+ OPT_INPUT_CSP,
+ OPT_INPUT_DEPTH
} OptionsOPT;
static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw";
{ "video-filter", required_argument, NULL, OPT_VIDEO_FILTER },
{ "input-res", required_argument, NULL, OPT_INPUT_RES },
{ "input-csp", required_argument, NULL, OPT_INPUT_CSP },
+ { "input-depth", required_argument, NULL, OPT_INPUT_DEPTH },
{0, 0, 0, 0}
};
if( csp > X264_CSP_NONE && csp < X264_CSP_MAX )
param->i_csp = info->csp;
else
- param->i_csp = X264_CSP_I420;
+ param->i_csp = X264_CSP_I420 | ( info->csp & X264_CSP_HIGH_DEPTH );
if( x264_init_vid_filter( "resize", handle, &filter, info, param, NULL ) )
return -1;
+ char args[20];
+ sprintf( args, "bit_depth=%d", x264_bit_depth );
+
+ if( x264_init_vid_filter( "depth", handle, &filter, info, param, args ) )
+ return -1;
+
return 0;
}
memset( opt, 0, sizeof(cli_opt_t) );
memset( &input_opt, 0, sizeof(cli_input_opt_t) );
+ input_opt.bit_depth = 8;
opt->b_progress = 1;
/* Presets are applied before all other options. */
case OPT_INPUT_CSP:
input_opt.colorspace = optarg;
break;
+ case OPT_INPUT_DEPTH:
+ input_opt.bit_depth = atoi( optarg );
+ break;
default:
generic_option:
{
#define X264_CSP_YV12 0x0002 /* yvu 4:2:0 planar */
#define X264_CSP_NV12 0x0003 /* yuv 4:2:0, with one y plane and one packed u+v */
#define X264_CSP_MAX 0x0004 /* end of list */
-#define X264_CSP_VFLIP 0x1000 /* */
+#define X264_CSP_VFLIP 0x1000 /* the csp is vertically flipped */
+#define X264_CSP_HIGH_DEPTH 0x2000 /* the csp has a depth of 16 bits per pixel component */
/* Slice type */
#define X264_TYPE_AUTO 0x0000 /* Let x264 choose the right type */
{
int i_rc_method; /* X264_RC_* */
- int i_qp_constant; /* 0 to (51 + 6*(BIT_DEPTH-8)) */
+ int i_qp_constant; /* 0 to (51 + 6*(x264_bit_depth-8)) */
int i_qp_min; /* min allowed QP value */
int i_qp_max; /* max allowed QP value */
int i_qp_step; /* max QP step between frames */
* Picture structures and functions
****************************************************************************/
+/* x264_bit_depth:
+ * Specifies the number of bits per pixel that x264 uses. This is also the
+ * bit depth that x264 encodes in. If this value is > 8, x264 will read
+ * two bytes of input data for each pixel sample, and expect the upper
+ * (16-x264_bit_depth) bits to be zero.
+ * Note: The flag X264_CSP_HIGH_DEPTH must be used to specify the
+ * colorspace depth as well. */
+extern const int x264_bit_depth;
+
enum pic_struct_e
{
PIC_STRUCT_AUTO = 0, // automatically decide (default)