ref: 5acfafb18e1720abfc0dc5baafce8e1a1b292b14
parent: 72037944df611abccac02c225e20adc9479200db
author: Deb Mukherjee <[email protected]>
date: Tue Aug 26 08:35:15 EDT 2014
Adds config opt for highbitdepth + misc. vpx Adds config parameter vp9_highbitdepth, to support highbitdepth profiles. Also includes most vpx level high bit-depth functions. However encode/decode in the highbitdepth profiles will not work until the rest of the code is in place. Change-Id: I34c53b253c38873611057a6cbc89a1361b8985a6
--- a/configure
+++ b/configure
@@ -334,6 +334,7 @@
multi_res_encoding
temporal_denoising
coefficient_range_checking
+ vp9_highbitdepth
experimental
size_limit
${EXPERIMENT_LIST}
@@ -392,6 +393,7 @@
multi_res_encoding
temporal_denoising
coefficient_range_checking
+ vp9_highbitdepth
experimental
"
--- a/test/md5_helper.h
+++ b/test/md5_helper.h
@@ -28,7 +28,8 @@
// plane, we never want to round down and thus skip a pixel so if
// we are shifting by 1 (chroma_shift) we add 1 before doing the shift.
// This works only for chroma_shift of 0 and 1.
- const int bytes_per_sample = (img->fmt & VPX_IMG_FMT_HIGH) ? 2 : 1;
+ const int bytes_per_sample =
+ (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
const int h = plane ? (img->d_h + img->y_chroma_shift) >>
img->y_chroma_shift : img->d_h;
const int w = (plane ? (img->d_w + img->x_chroma_shift) >>
--- a/test/y4m_test.cc
+++ b/test/y4m_test.cc
@@ -57,7 +57,7 @@
for (plane = 0; plane < 3; ++plane) {
const unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
- const int bytes_per_sample = (img->fmt & VPX_IMG_FMT_HIGH) ? 2 : 1;
+ const int bytes_per_sample = (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
const int h = (plane ? (img->d_h + img->y_chroma_shift) >>
img->y_chroma_shift : img->d_h);
const int w = (plane ? (img->d_w + img->x_chroma_shift) >>
--- a/tools_common.c
+++ b/tools_common.c
@@ -83,7 +83,7 @@
struct FileTypeDetectionBuffer *detect = &input_ctx->detect;
int plane = 0;
int shortread = 0;
- const int bytespp = (yuv_frame->fmt & VPX_IMG_FMT_HIGH) ? 2 : 1;
+ const int bytespp = (yuv_frame->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
for (plane = 0; plane < 3; ++plane) {
uint8_t *ptr;
@@ -241,7 +241,8 @@
for (plane = 0; plane < 3; ++plane) {
unsigned char *buf = img->planes[plane];
const int stride = img->stride[plane];
- const int w = vpx_img_plane_width(img, plane);
+ const int w = vpx_img_plane_width(img, plane) *
+ ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
const int h = vpx_img_plane_height(img, plane);
int y;
--- a/vp8/vp8_cx_iface.c
+++ b/vp8/vp8_cx_iface.c
@@ -1244,6 +1244,9 @@
320, /* g_width */
240, /* g_height */
+ VPX_BITS_8, /* g_bit_depth */
+ 8, /* g_input_bit_depth */
+
{1, 30}, /* g_timebase */
0, /* g_error_resilient */
--- a/vp9/common/vp9_alloccommon.c
+++ b/vp9/common/vp9_alloccommon.c
@@ -177,7 +177,11 @@
for (i = 0; i < FRAME_BUFFERS; ++i) {
cm->frame_bufs[i].ref_count = 0;
if (vp9_alloc_frame_buffer(&cm->frame_bufs[i].buf, width, height,
- ss_x, ss_y, VP9_ENC_BORDER_IN_PIXELS) < 0)
+ ss_x, ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
+ VP9_ENC_BORDER_IN_PIXELS) < 0)
goto fail;
}
@@ -185,6 +189,9 @@
#if CONFIG_INTERNAL_STATS || CONFIG_VP9_POSTPROC
if (vp9_alloc_frame_buffer(&cm->post_proc_buffer, width, height, ss_x, ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS) < 0)
goto fail;
#endif
--- a/vp9/common/vp9_common.h
+++ b/vp9/common/vp9_common.h
@@ -64,6 +64,11 @@
return num_values > 0 ? get_msb(num_values) + 1 : 0;
}
+#if CONFIG_VP9_HIGHBITDEPTH
+#define CONVERT_TO_SHORTPTR(x) ((uint16_t*)(((uintptr_t)x) << 1))
+#define CONVERT_TO_BYTEPTR(x) ((uint8_t*)(((uintptr_t)x) >> 1 ))
+#endif // CONFIG_VP9_HIGHBITDEPTH
+
#if CONFIG_DEBUG
#define CHECK_MEM_ERROR(cm, lval, expr) do { \
lval = (expr); \
--- a/vp9/common/vp9_enums.h
+++ b/vp9/common/vp9_enums.h
@@ -40,12 +40,6 @@
MAX_PROFILES
} BITSTREAM_PROFILE;
-typedef enum BIT_DEPTH {
- BITS_8,
- BITS_10,
- BITS_12
-} BIT_DEPTH;
-
typedef enum BLOCK_SIZE {
BLOCK_4X4,
BLOCK_4X8,
--- a/vp9/common/vp9_onyxc_int.h
+++ b/vp9/common/vp9_onyxc_int.h
@@ -84,6 +84,10 @@
int subsampling_x;
int subsampling_y;
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth; // Marks if we need to use 16bit frame buffers.
+#endif
+
YV12_BUFFER_CONFIG *frame_to_show;
RefCntBuffer frame_bufs[FRAME_BUFFERS];
@@ -179,8 +183,8 @@
unsigned int current_video_frame;
BITSTREAM_PROFILE profile;
- // BITS_8 in versions 0 and 1, BITS_10 or BITS_12 in version 2
- BIT_DEPTH bit_depth;
+ // VPX_BITS_8 in profile 0 or 1, VPX_BITS_10 or VPX_BITS_12 in profile 2 or 3.
+ vpx_bit_depth_t bit_depth;
#if CONFIG_VP9_POSTPROC
struct postproc_state postproc_state;
--- a/vp9/decoder/vp9_decodeframe.c
+++ b/vp9/decoder/vp9_decodeframe.c
@@ -653,7 +653,11 @@
if (vp9_realloc_frame_buffer(
get_frame_new_buffer(cm), cm->width, cm->height,
- cm->subsampling_x, cm->subsampling_y, VP9_DEC_BORDER_IN_PIXELS,
+ cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
+ VP9_DEC_BORDER_IN_PIXELS,
&cm->frame_bufs[cm->new_fb_idx].raw_frame_buffer, cm->get_fb_cb,
cm->cb_priv)) {
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
@@ -700,7 +704,11 @@
if (vp9_realloc_frame_buffer(
get_frame_new_buffer(cm), cm->width, cm->height,
- cm->subsampling_x, cm->subsampling_y, VP9_DEC_BORDER_IN_PIXELS,
+ cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
+ VP9_DEC_BORDER_IN_PIXELS,
&cm->frame_bufs[cm->new_fb_idx].raw_frame_buffer, cm->get_fb_cb,
cm->cb_priv)) {
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
@@ -1100,7 +1108,7 @@
static void read_bitdepth_colorspace_sampling(
VP9_COMMON *cm, struct vp9_read_bit_buffer *rb) {
if (cm->profile >= PROFILE_2)
- cm->bit_depth = vp9_rb_read_bit(rb) ? BITS_12 : BITS_10;
+ cm->bit_depth = vp9_rb_read_bit(rb) ? VPX_BITS_12 : VPX_BITS_10;
cm->color_space = (COLOR_SPACE)vp9_rb_read_literal(rb, 3);
if (cm->color_space != SRGB) {
vp9_rb_read_bit(rb); // [16,235] (including xvycc) vs [0,255] range
@@ -1144,6 +1152,7 @@
"Invalid frame marker");
cm->profile = vp9_read_profile(rb);
+
if (cm->profile >= MAX_PROFILES)
vpx_internal_error(&cm->error, VPX_CODEC_UNSUP_BITSTREAM,
"Unsupported bitstream profile");
@@ -1402,7 +1411,7 @@
if (!first_partition_size) {
// showing a frame directly
- *p_data_end = data + 1;
+ *p_data_end = data + (cm->profile <= PROFILE_2 ? 1 : 2);
return;
}
--- a/vp9/decoder/vp9_decoder.c
+++ b/vp9/decoder/vp9_decoder.c
@@ -66,6 +66,7 @@
cm->current_video_frame = 0;
pbi->ready_for_new_data = 1;
+ cm->bit_depth = VPX_BITS_8;
// vp9_init_dequantizer() is first called here. Add check in
// frame_init_dequantizer() to avoid unnecessary calling of
--- a/vp9/encoder/vp9_bitstream.c
+++ b/vp9/encoder/vp9_bitstream.c
@@ -1046,8 +1046,8 @@
static void write_bitdepth_colorspace_sampling(
VP9_COMMON *const cm, struct vp9_write_bit_buffer *wb) {
if (cm->profile >= PROFILE_2) {
- assert(cm->bit_depth > BITS_8);
- vp9_wb_write_bit(wb, cm->bit_depth - BITS_10);
+ assert(cm->bit_depth > VPX_BITS_8);
+ vp9_wb_write_bit(wb, cm->bit_depth == VPX_BITS_10 ? 0 : 1);
}
vp9_wb_write_literal(wb, cm->color_space, 3);
if (cm->color_space != SRGB) {
--- a/vp9/encoder/vp9_denoiser.c
+++ b/vp9/encoder/vp9_denoiser.c
@@ -388,13 +388,21 @@
}
int vp9_denoiser_alloc(VP9_DENOISER *denoiser, int width, int height,
- int ssx, int ssy, int border) {
+ int ssx, int ssy,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
+ int border) {
int i, fail;
assert(denoiser != NULL);
for (i = 0; i < MAX_REF_FRAMES; ++i) {
fail = vp9_alloc_frame_buffer(&denoiser->running_avg_y[i], width, height,
- ssx, ssy, border);
+ ssx, ssy,
+#if CONFIG_VP9_HIGHBITDEPTH
+ use_highbitdepth,
+#endif
+ border);
if (fail) {
vp9_denoiser_free(denoiser);
return 1;
@@ -405,7 +413,11 @@
}
fail = vp9_alloc_frame_buffer(&denoiser->mc_running_avg_y, width, height,
- ssx, ssy, border);
+ ssx, ssy,
+#if CONFIG_VP9_HIGHBITDEPTH
+ use_highbitdepth,
+#endif
+ border);
if (fail) {
vp9_denoiser_free(denoiser);
return 1;
--- a/vp9/encoder/vp9_denoiser.h
+++ b/vp9/encoder/vp9_denoiser.h
@@ -47,7 +47,11 @@
PICK_MODE_CONTEXT *ctx);
int vp9_denoiser_alloc(VP9_DENOISER *denoiser, int width, int height,
- int ssx, int ssy, int border);
+ int ssx, int ssy,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
+ int border);
void vp9_denoiser_free(VP9_DENOISER *denoiser);
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -443,6 +443,9 @@
cpi->lookahead = vp9_lookahead_init(oxcf->width, oxcf->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
oxcf->lag_in_frames);
if (!cpi->lookahead)
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
@@ -451,6 +454,9 @@
if (vp9_realloc_frame_buffer(&cpi->alt_ref_buffer,
oxcf->width, oxcf->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate altref buffer");
@@ -468,6 +474,9 @@
if (vp9_realloc_frame_buffer(&cpi->last_frame_uf,
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate last frame buffer");
@@ -475,6 +484,9 @@
if (vp9_realloc_frame_buffer(&cpi->scaled_source,
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate scaled source buffer");
@@ -482,6 +494,9 @@
if (vp9_realloc_frame_buffer(&cpi->scaled_last_source,
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate scaled last source buffer");
@@ -514,6 +529,9 @@
if (vp9_realloc_frame_buffer(&cpi->alt_ref_buffer,
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to reallocate alt_ref_buffer");
@@ -600,9 +618,9 @@
cm->bit_depth = oxcf->bit_depth;
if (cm->profile <= PROFILE_1)
- assert(cm->bit_depth == BITS_8);
+ assert(cm->bit_depth == VPX_BITS_8);
else
- assert(cm->bit_depth > BITS_8);
+ assert(cm->bit_depth > VPX_BITS_8);
cpi->oxcf = *oxcf;
@@ -677,6 +695,9 @@
if (cpi->oxcf.noise_sensitivity > 0) {
vp9_denoiser_alloc(&(cpi->denoiser), cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS);
}
#endif
@@ -1604,6 +1625,9 @@
vp9_realloc_frame_buffer(&cm->frame_bufs[new_fb].buf,
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL);
scale_and_extend_frame(ref, &cm->frame_bufs[new_fb].buf);
cpi->scaled_ref_idx[ref_frame - 1] = new_fb;
@@ -2699,6 +2723,9 @@
vp9_realloc_frame_buffer(get_frame_new_buffer(cm),
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL, NULL);
alloc_util_frame_buffers(cpi);
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -114,9 +114,10 @@
typedef struct VP9EncoderConfig {
BITSTREAM_PROFILE profile;
- BIT_DEPTH bit_depth;
+ vpx_bit_depth_t bit_depth; // Codec bit-depth.
int width; // width of data passed to the compressor
int height; // height of data passed to the compressor
+ unsigned int input_bit_depth; // Input bit depth.
double init_framerate; // set to passed in framerate
int64_t target_bandwidth; // bandwidth to be used in kilobits per second
--- a/vp9/encoder/vp9_lookahead.c
+++ b/vp9/encoder/vp9_lookahead.c
@@ -50,6 +50,9 @@
unsigned int height,
unsigned int subsampling_x,
unsigned int subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
unsigned int depth) {
struct lookahead_ctx *ctx = NULL;
@@ -70,6 +73,9 @@
for (i = 0; i < depth; i++)
if (vp9_alloc_frame_buffer(&ctx->buf[i].img,
width, height, subsampling_x, subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS))
goto bail;
}
--- a/vp9/encoder/vp9_lookahead.h
+++ b/vp9/encoder/vp9_lookahead.h
@@ -56,6 +56,9 @@
unsigned int height,
unsigned int subsampling_x,
unsigned int subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
unsigned int depth);
--- a/vp9/encoder/vp9_temporal_filter.c
+++ b/vp9/encoder/vp9_temporal_filter.c
@@ -465,6 +465,9 @@
if (vp9_realloc_frame_buffer(&cpi->svc.scaled_frames[frame_used],
cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
VP9_ENC_BORDER_IN_PIXELS, NULL, NULL,
NULL))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -37,7 +37,7 @@
unsigned int frame_parallel_decoding_mode;
AQ_MODE aq_mode;
unsigned int frame_periodic_boost;
- BIT_DEPTH bit_depth;
+ vpx_bit_depth_t bit_depth;
vp9e_tune_content content;
};
@@ -58,7 +58,7 @@
0, // frame_parallel_decoding_mode
NO_AQ, // aq_mode
0, // frame_periodic_delta_q
- BITS_8, // Bit depth
+ VPX_BITS_8, // Bit depth
VP9E_CONTENT_DEFAULT // content
};
@@ -208,6 +208,8 @@
RANGE_CHECK(extra_cfg, arnr_max_frames, 0, 15);
RANGE_CHECK_HI(extra_cfg, arnr_strength, 6);
RANGE_CHECK(extra_cfg, cq_level, 0, 63);
+ RANGE_CHECK(cfg, g_bit_depth, VPX_BITS_8, VPX_BITS_12);
+ RANGE_CHECK(cfg, g_input_bit_depth, 8, 12);
RANGE_CHECK(extra_cfg, content,
VP9E_CONTENT_DEFAULT, VP9E_CONTENT_INVALID - 1);
@@ -266,12 +268,16 @@
}
}
+#if !CONFIG_VP9_HIGHBITDEPTH
+ if (cfg->g_profile > (unsigned int)PROFILE_1)
+ ERROR("Profile > 1 not supported in this build configuration");
+#endif
if (cfg->g_profile <= (unsigned int)PROFILE_1 &&
- extra_cfg->bit_depth > BITS_8)
- ERROR("High bit-depth not supported in profile < 2");
+ extra_cfg->bit_depth > VPX_BITS_8)
+ ERROR("Codec high bit-depth not supported in profile < 2");
if (cfg->g_profile > (unsigned int)PROFILE_1 &&
- extra_cfg->bit_depth == BITS_8)
- ERROR("Bit-depth 8 not supported in profile > 1");
+ extra_cfg->bit_depth == VPX_BITS_8)
+ ERROR("Codec bit-depth 8 not supported in profile > 1");
return VPX_CODEC_OK;
}
@@ -303,6 +309,9 @@
case VPX_IMG_FMT_I420: return 12;
case VPX_IMG_FMT_I422: return 16;
case VPX_IMG_FMT_I444: return 24;
+ case VPX_IMG_FMT_I42016: return 24;
+ case VPX_IMG_FMT_I42216: return 32;
+ case VPX_IMG_FMT_I44416: return 48;
default: assert(0 && "Invalid image format"); break;
}
return 0;
@@ -317,6 +326,7 @@
oxcf->width = cfg->g_w;
oxcf->height = cfg->g_h;
oxcf->bit_depth = extra_cfg->bit_depth;
+ oxcf->input_bit_depth = cfg->g_input_bit_depth;
// guess a frame rate if out of whack, use 30
oxcf->init_framerate = (double)cfg->g_timebase.den / cfg->g_timebase.num;
if (oxcf->init_framerate > 180)
@@ -1246,6 +1256,9 @@
320, // g_width
240, // g_height
+ VPX_BITS_8, // g_bit_depth
+ 8, // g_input_bit_depth
+
{1, 30}, // g_timebase
0, // g_error_resilient
--- a/vp9/vp9_dx_iface.c
+++ b/vp9/vp9_dx_iface.c
@@ -440,6 +440,7 @@
// call to get_frame.
if (!(*iter)) {
img = &ctx->img;
+ img->bit_depth = (int)ctx->pbi->common.bit_depth;
*iter = img;
}
}
@@ -588,6 +589,23 @@
}
}
+static vpx_codec_err_t ctrl_get_bit_depth(vpx_codec_alg_priv_t *ctx,
+ va_list args) {
+ unsigned int *const bit_depth = va_arg(args, unsigned int *);
+
+ if (bit_depth) {
+ if (ctx->pbi) {
+ const VP9_COMMON *const cm = &ctx->pbi->common;
+ *bit_depth = cm->bit_depth;
+ return VPX_CODEC_OK;
+ } else {
+ return VPX_CODEC_ERROR;
+ }
+ } else {
+ return VPX_CODEC_INVALID_PARAM;
+ }
+}
+
static vpx_codec_err_t ctrl_set_invert_tile_order(vpx_codec_alg_priv_t *ctx,
va_list args) {
ctx->invert_tile_order = va_arg(args, int);
@@ -620,6 +638,7 @@
{VP8D_GET_FRAME_CORRUPTED, ctrl_get_frame_corrupted},
{VP9_GET_REFERENCE, ctrl_get_reference},
{VP9D_GET_DISPLAY_SIZE, ctrl_get_display_size},
+ {VP9D_GET_BIT_DEPTH, ctrl_get_bit_depth},
{ -1, NULL},
};
--- a/vpx/src/vpx_image.c
+++ b/vpx/src/vpx_image.c
@@ -154,7 +154,7 @@
goto fail;
img->fmt = fmt;
- img->bit_depth = (fmt & VPX_IMG_FMT_HIGH) ? 16 : 8;
+ img->bit_depth = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
img->w = w;
img->h = h;
img->x_chroma_shift = xcs;
--- a/vpx/vp8dx.h
+++ b/vpx/vp8dx.h
@@ -75,6 +75,9 @@
/** control function to get the display dimensions for the current frame. */
VP9D_GET_DISPLAY_SIZE,
+ /** control function to get the bit depth of the stream. */
+ VP9D_GET_BIT_DEPTH,
+
/** For testing. */
VP9_INVERT_TILE_DECODE_ORDER,
@@ -118,6 +121,7 @@
VPX_CTRL_USE_TYPE(VPXD_SET_DECRYPTOR, vpx_decrypt_init *)
VPX_CTRL_USE_TYPE(VP8D_SET_DECRYPTOR, vpx_decrypt_init *)
VPX_CTRL_USE_TYPE(VP9D_GET_DISPLAY_SIZE, int *)
+VPX_CTRL_USE_TYPE(VP9D_GET_BIT_DEPTH, unsigned int *)
VPX_CTRL_USE_TYPE(VP9_INVERT_TILE_DECODE_ORDER, int)
/*! @} - end defgroup vp8_decoder */
--- a/vpx/vpx_codec.h
+++ b/vpx/vpx_codec.h
@@ -217,9 +217,9 @@
* This enumeration determines the bit depth of the codec.
*/
typedef enum vpx_bit_depth {
- VPX_BITS_8, /**< 8 bits */
- VPX_BITS_10, /**< 10 bits */
- VPX_BITS_12 /**< 12 bits */
+ VPX_BITS_8 = 8, /**< 8 bits */
+ VPX_BITS_10 = 10, /**< 10 bits */
+ VPX_BITS_12 = 12, /**< 12 bits */
} vpx_bit_depth_t;
/*
--- a/vpx/vpx_encoder.h
+++ b/vpx/vpx_encoder.h
@@ -80,6 +80,9 @@
*/
#define VPX_CODEC_CAP_OUTPUT_PARTITION 0x20000
+/*! Can support input images at greater than 8 bitdepth.
+ */
+#define VPX_CODEC_CAP_HIGHBITDEPTH 0x40000
/*! \brief Initialization-time Feature Enabling
*
@@ -91,6 +94,7 @@
#define VPX_CODEC_USE_PSNR 0x10000 /**< Calculate PSNR on each frame */
#define VPX_CODEC_USE_OUTPUT_PARTITION 0x20000 /**< Make the encoder output one
partition at a time. */
+#define VPX_CODEC_USE_HIGHBITDEPTH 0x40000 /**< Use high bitdepth */
/*!\brief Generic fixed size buffer structure
@@ -324,6 +328,21 @@
*/
unsigned int g_h;
+ /*!\brief Bit-depth of the codec
+ *
+ * This value identifies the bit_depth of the codec,
+ * Only certain bit-depths are supported as identified in the
+ * vpx_bit_depth_t enum.
+ */
+ vpx_bit_depth_t g_bit_depth;
+
+ /*!\brief Bit-depth of the input frames
+ *
+ * This value identifies the bit_depth of the input frames in bits.
+ * Note that the frames passed as input to the encoder must have
+ * this bit-depth.
+ */
+ unsigned int g_input_bit_depth;
/*!\brief Stream timebase units
*
--- a/vpx/vpx_image.h
+++ b/vpx/vpx_image.h
@@ -31,10 +31,10 @@
#define VPX_IMAGE_ABI_VERSION (2) /**<\hideinitializer*/
-#define VPX_IMG_FMT_PLANAR 0x100 /**< Image is a planar format */
-#define VPX_IMG_FMT_UV_FLIP 0x200 /**< V plane precedes U plane in memory */
-#define VPX_IMG_FMT_HAS_ALPHA 0x400 /**< Image has an alpha channel component */
-#define VPX_IMG_FMT_HIGH 0x800 /**< Image uses 16bit framebuffer */
+#define VPX_IMG_FMT_PLANAR 0x100 /**< Image is a planar format. */
+#define VPX_IMG_FMT_UV_FLIP 0x200 /**< V plane precedes U in memory. */
+#define VPX_IMG_FMT_HAS_ALPHA 0x400 /**< Image has an alpha channel. */
+#define VPX_IMG_FMT_HIGHBITDEPTH 0x800 /**< Image uses 16bit framebuffer. */
/*!\brief List of supported image formats */
typedef enum vpx_img_fmt {
@@ -59,9 +59,9 @@
VPX_IMG_FMT_I422 = VPX_IMG_FMT_PLANAR | 5,
VPX_IMG_FMT_I444 = VPX_IMG_FMT_PLANAR | 6,
VPX_IMG_FMT_444A = VPX_IMG_FMT_PLANAR | VPX_IMG_FMT_HAS_ALPHA | 7,
- VPX_IMG_FMT_I42016 = VPX_IMG_FMT_I420 | VPX_IMG_FMT_HIGH,
- VPX_IMG_FMT_I42216 = VPX_IMG_FMT_I422 | VPX_IMG_FMT_HIGH,
- VPX_IMG_FMT_I44416 = VPX_IMG_FMT_I444 | VPX_IMG_FMT_HIGH
+ VPX_IMG_FMT_I42016 = VPX_IMG_FMT_I420 | VPX_IMG_FMT_HIGHBITDEPTH,
+ VPX_IMG_FMT_I42216 = VPX_IMG_FMT_I422 | VPX_IMG_FMT_HIGHBITDEPTH,
+ VPX_IMG_FMT_I44416 = VPX_IMG_FMT_I444 | VPX_IMG_FMT_HIGHBITDEPTH
} vpx_img_fmt_t; /**< alias for enum vpx_img_fmt */
#if !defined(VPX_CODEC_DISABLE_COMPAT) || !VPX_CODEC_DISABLE_COMPAT
--- a/vpx_mem/vpx_mem.c
+++ b/vpx_mem/vpx_mem.c
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <string.h>
#include "include/vpx_mem_intrnl.h"
+#include "vpx/vpx_integer.h"
#if CONFIG_MEM_TRACKER
#ifndef VPX_NO_GLOBALS
@@ -451,6 +452,29 @@
return VPX_MEMSET_L(dest, val, length);
}
+
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+void *vpx_memset16(void *dest, int val, size_t length) {
+#if CONFIG_MEM_CHECKS
+ if ((int)dest < 0x4000) {
+ _P(printf("WARNING: vpx_memset dest:0x%x val:%d len:%d\n",
+ (int)dest, val, length);)
+
+#if defined(VXWORKS)
+ sp(get_my_tt, task_id_self(), 0, 0, 0, 0, 0, 0, 0, 0);
+
+ vx_sleep(10000);
+#endif
+ }
+#endif
+ int i;
+ void *orig = dest;
+ uint16_t *dest16 = dest;
+ for (i = 0; i < length; i++)
+ *dest16++ = val;
+ return orig;
+}
+#endif // CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
void *vpx_memmove(void *dest, const void *src, size_t count) {
#if CONFIG_MEM_CHECKS
--- a/vpx_mem/vpx_mem.h
+++ b/vpx_mem/vpx_mem.h
@@ -73,6 +73,9 @@
void *vpx_memcpy(void *dest, const void *src, size_t length);
void *vpx_memset(void *dest, int val, size_t length);
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ void *vpx_memset16(void *dest, int val, size_t length);
+#endif
void *vpx_memmove(void *dest, const void *src, size_t count);
/* special memory functions */
--- a/vpx_scale/generic/yv12config.c
+++ b/vpx_scale/generic/yv12config.c
@@ -13,6 +13,9 @@
#include "./vpx_config.h"
#include "vpx_scale/yv12config.h"
#include "vpx_mem/vpx_mem.h"
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+#include "vp9/common/vp9_common.h"
+#endif
/****************************************************************************
* Exports
@@ -136,7 +139,11 @@
int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
int width, int height,
- int ss_x, int ss_y, int border,
+ int ss_x, int ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
+ int border,
vpx_codec_frame_buffer_t *fb,
vpx_get_frame_buffer_cb_fn_t cb,
void *cb_priv) {
@@ -161,11 +168,21 @@
const int alpha_border_h = border;
const uint64_t alpha_plane_size = (alpha_height + 2 * alpha_border_h) *
(uint64_t)alpha_stride;
+#if CONFIG_VP9_HIGHBITDEPTH
+ const uint64_t frame_size = (1 + use_highbitdepth) *
+ (yplane_size + 2 * uvplane_size + alpha_plane_size);
+#else
const uint64_t frame_size = yplane_size + 2 * uvplane_size +
alpha_plane_size;
+#endif // CONFIG_VP9_HIGHBITDEPTH
#else
+#if CONFIG_VP9_HIGHBITDEPTH
+ const uint64_t frame_size =
+ (1 + use_highbitdepth) * (yplane_size + 2 * uvplane_size);
+#else
const uint64_t frame_size = yplane_size + 2 * uvplane_size;
-#endif
+#endif // CONFIG_VP9_HIGHBITDEPTH
+#endif // CONFIG_ALPHA
if (cb != NULL) {
const int align_addr_extra_size = 31;
const uint64_t external_frame_size = frame_size + align_addr_extra_size;
@@ -231,11 +248,31 @@
ybf->border = border;
ybf->frame_size = (int)frame_size;
+#if CONFIG_VP9_HIGHBITDEPTH
+ if (use_highbitdepth) {
+ // Store uint16 addresses when using 16bit framebuffers
+ uint8_t *p = CONVERT_TO_BYTEPTR(ybf->buffer_alloc);
+ ybf->y_buffer = p + (border * y_stride) + border;
+ ybf->u_buffer = p + yplane_size +
+ (uv_border_h * uv_stride) + uv_border_w;
+ ybf->v_buffer = p + yplane_size + uvplane_size +
+ (uv_border_h * uv_stride) + uv_border_w;
+ ybf->flags = YV12_FLAG_HIGHBITDEPTH;
+ } else {
+ ybf->y_buffer = ybf->buffer_alloc + (border * y_stride) + border;
+ ybf->u_buffer = ybf->buffer_alloc + yplane_size +
+ (uv_border_h * uv_stride) + uv_border_w;
+ ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size +
+ (uv_border_h * uv_stride) + uv_border_w;
+ ybf->flags = 0;
+ }
+#else
ybf->y_buffer = ybf->buffer_alloc + (border * y_stride) + border;
ybf->u_buffer = ybf->buffer_alloc + yplane_size +
(uv_border_h * uv_stride) + uv_border_w;
ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size +
(uv_border_h * uv_stride) + uv_border_w;
+#endif // CONFIG_VP9_HIGHBITDEPTH
#if CONFIG_ALPHA
ybf->alpha_width = alpha_width;
@@ -252,11 +289,18 @@
int vp9_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
int width, int height,
- int ss_x, int ss_y, int border) {
+ int ss_x, int ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
+ int border) {
if (ybf) {
vp9_free_frame_buffer(ybf);
- return vp9_realloc_frame_buffer(ybf, width, height, ss_x, ss_y, border,
- NULL, NULL, NULL);
+ return vp9_realloc_frame_buffer(ybf, width, height, ss_x, ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ use_highbitdepth,
+#endif
+ border, NULL, NULL, NULL);
}
return -2;
}
--- a/vpx_scale/generic/yv12extend.c
+++ b/vpx_scale/generic/yv12extend.c
@@ -13,6 +13,9 @@
#include "vpx/vpx_integer.h"
#include "vpx_mem/vpx_mem.h"
#include "vpx_scale/yv12config.h"
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+#include "vp9/common/vp9_common.h"
+#endif
static void extend_plane(uint8_t *const src, int src_stride,
int width, int height,
@@ -55,6 +58,50 @@
}
}
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static void extend_plane_high(uint8_t *const src8, int src_stride,
+ int width, int height,
+ int extend_top, int extend_left,
+ int extend_bottom, int extend_right) {
+ int i;
+ const int linesize = extend_left + extend_right + width;
+ uint16_t *src = CONVERT_TO_SHORTPTR(src8);
+
+ /* copy the left and right most columns out */
+ uint16_t *src_ptr1 = src;
+ uint16_t *src_ptr2 = src + width - 1;
+ uint16_t *dst_ptr1 = src - extend_left;
+ uint16_t *dst_ptr2 = src + width;
+
+ for (i = 0; i < height; ++i) {
+ vpx_memset16(dst_ptr1, src_ptr1[0], extend_left);
+ vpx_memset16(dst_ptr2, src_ptr2[0], extend_right);
+ src_ptr1 += src_stride;
+ src_ptr2 += src_stride;
+ dst_ptr1 += src_stride;
+ dst_ptr2 += src_stride;
+ }
+
+ /* Now copy the top and bottom lines into each line of the respective
+ * borders
+ */
+ src_ptr1 = src - extend_left;
+ src_ptr2 = src + src_stride * (height - 1) - extend_left;
+ dst_ptr1 = src + src_stride * -extend_top - extend_left;
+ dst_ptr2 = src + src_stride * height - extend_left;
+
+ for (i = 0; i < extend_top; ++i) {
+ vpx_memcpy(dst_ptr1, src_ptr1, linesize * sizeof(uint16_t));
+ dst_ptr1 += src_stride;
+ }
+
+ for (i = 0; i < extend_bottom; ++i) {
+ vpx_memcpy(dst_ptr2, src_ptr2, linesize * sizeof(uint16_t));
+ dst_ptr2 += src_stride;
+ }
+}
+#endif
+
void vp8_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) {
const int uv_border = ybf->border / 2;
@@ -64,6 +111,31 @@
assert(ybf->y_height - ybf->y_crop_height >= 0);
assert(ybf->y_width - ybf->y_crop_width >= 0);
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (ybf->flags & YV12_FLAG_HIGHBITDEPTH) {
+ extend_plane_high(
+ ybf->y_buffer, ybf->y_stride,
+ ybf->y_crop_width, ybf->y_crop_height,
+ ybf->border, ybf->border,
+ ybf->border + ybf->y_height - ybf->y_crop_height,
+ ybf->border + ybf->y_width - ybf->y_crop_width);
+
+ extend_plane_high(
+ ybf->u_buffer, ybf->uv_stride,
+ (ybf->y_crop_width + 1) / 2, (ybf->y_crop_height + 1) / 2,
+ ybf->border / 2, ybf->border / 2,
+ (ybf->border + ybf->y_height - ybf->y_crop_height + 1) / 2,
+ (ybf->border + ybf->y_width - ybf->y_crop_width + 1) / 2);
+
+ extend_plane_high(
+ ybf->v_buffer, ybf->uv_stride,
+ (ybf->y_crop_width + 1) / 2, (ybf->y_crop_height + 1) / 2,
+ ybf->border / 2, ybf->border / 2,
+ (ybf->border + ybf->y_height - ybf->y_crop_height + 1) / 2,
+ (ybf->border + ybf->y_width - ybf->y_crop_width + 1) / 2);
+ return;
+ }
+#endif
extend_plane(ybf->y_buffer, ybf->y_stride,
ybf->y_crop_width, ybf->y_crop_height,
ybf->border, ybf->border,
@@ -99,6 +171,20 @@
assert(ybf->y_height - ybf->y_crop_height >= 0);
assert(ybf->y_width - ybf->y_crop_width >= 0);
+#if CONFIG_VP9_HIGHBITDEPTH
+ if (ybf->flags & YV12_FLAG_HIGHBITDEPTH) {
+ extend_plane_high(ybf->y_buffer, ybf->y_stride,
+ ybf->y_crop_width, ybf->y_crop_height,
+ ext_size, ext_size,
+ ext_size + ybf->y_height - ybf->y_crop_height,
+ ext_size + ybf->y_width - ybf->y_crop_width);
+ extend_plane_high(ybf->u_buffer, ybf->uv_stride,
+ c_w, c_h, c_et, c_el, c_eb, c_er);
+ extend_plane_high(ybf->v_buffer, ybf->uv_stride,
+ c_w, c_h, c_et, c_el, c_eb, c_er);
+ return;
+ }
+#endif
extend_plane(ybf->y_buffer, ybf->y_stride,
ybf->y_crop_width, ybf->y_crop_height,
ext_size, ext_size,
@@ -121,6 +207,14 @@
VP9INNERBORDERINPIXELS : ybf->border;
extend_frame(ybf, inner_bw);
}
+
+#if CONFIG_VP9_HIGHBITDEPTH
+void memcpy_short_addr(uint8_t *dst8, const uint8_t *src8, int num) {
+ uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
+ uint16_t *src = CONVERT_TO_SHORTPTR(src8);
+ vpx_memcpy(dst, src, num * sizeof(uint16_t));
+}
+#endif // CONFIG_VP9_HIGHBITDEPTH
#endif // CONFIG_VP9
// Copies the source image into the destination image and updates the
@@ -140,6 +234,40 @@
assert(src_ybc->y_height == dst_ybc->y_height);
#endif
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (src_ybc->flags & YV12_FLAG_HIGHBITDEPTH) {
+ assert(dst_ybc->flags & YV12_FLAG_HIGHBITDEPTH);
+ for (row = 0; row < src_ybc->y_height; ++row) {
+ memcpy_short_addr(dst, src, src_ybc->y_width);
+ src += src_ybc->y_stride;
+ dst += dst_ybc->y_stride;
+ }
+
+ src = src_ybc->u_buffer;
+ dst = dst_ybc->u_buffer;
+
+ for (row = 0; row < src_ybc->uv_height; ++row) {
+ memcpy_short_addr(dst, src, src_ybc->uv_width);
+ src += src_ybc->uv_stride;
+ dst += dst_ybc->uv_stride;
+ }
+
+ src = src_ybc->v_buffer;
+ dst = dst_ybc->v_buffer;
+
+ for (row = 0; row < src_ybc->uv_height; ++row) {
+ memcpy_short_addr(dst, src, src_ybc->uv_width);
+ src += src_ybc->uv_stride;
+ dst += dst_ybc->uv_stride;
+ }
+
+ vp8_yv12_extend_frame_borders_c(dst_ybc);
+ return;
+ } else {
+ assert(!(dst_ybc->flags & YV12_FLAG_HIGHBITDEPTH));
+ }
+#endif
+
for (row = 0; row < src_ybc->y_height; ++row) {
vpx_memcpy(dst, src, src_ybc->y_width);
src += src_ybc->y_stride;
@@ -172,6 +300,19 @@
int row;
const uint8_t *src = src_ybc->y_buffer;
uint8_t *dst = dst_ybc->y_buffer;
+
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (src_ybc->flags & YV12_FLAG_HIGHBITDEPTH) {
+ const uint16_t *src16 = CONVERT_TO_SHORTPTR(src);
+ uint16_t *dst16 = CONVERT_TO_SHORTPTR(dst);
+ for (row = 0; row < src_ybc->y_height; ++row) {
+ vpx_memcpy(dst16, src16, src_ybc->y_width * sizeof(uint16_t));
+ src16 += src_ybc->y_stride;
+ dst16 += dst_ybc->y_stride;
+ }
+ return;
+ }
+#endif
for (row = 0; row < src_ybc->y_height; ++row) {
vpx_memcpy(dst, src, src_ybc->y_width);
--- a/vpx_scale/yv12config.h
+++ b/vpx_scale/yv12config.h
@@ -55,6 +55,8 @@
int flags;
} YV12_BUFFER_CONFIG;
+#define YV12_FLAG_HIGHBITDEPTH 1
+
int vp8_yv12_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
int width, int height, int border);
int vp8_yv12_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
@@ -63,6 +65,9 @@
int vp9_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
int width, int height, int ss_x, int ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
int border);
// Updates the yv12 buffer config with the frame buffer. If cb is not
@@ -73,6 +78,9 @@
// on failure.
int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
int width, int height, int ss_x, int ss_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ int use_highbitdepth,
+#endif
int border,
vpx_codec_frame_buffer_t *fb,
vpx_get_frame_buffer_cb_fn_t cb,
--- a/vpxdec.c
+++ b/vpxdec.c
@@ -90,6 +90,11 @@
static const arg_def_t md5arg = ARG_DEF(NULL, "md5", 0,
"Compute the MD5 sum of the decoded frame");
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static const arg_def_t outbitdeptharg = ARG_DEF(
+ NULL, "output-bit-depth", 1,
+ "Output bit-depth for decoded frames");
+#endif
static const arg_def_t *all_args[] = {
&codecarg, &use_yv12, &use_i420, &flipuvarg, &rawvideo, &noblitarg,
@@ -96,6 +101,9 @@
&progressarg, &limitarg, &skiparg, &postprocarg, &summaryarg, &outputfile,
&threadsarg, &verbosearg, &scalearg, &fb_arg,
&md5arg, &error_concealment, &continuearg,
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ &outbitdeptharg,
+#endif
NULL
};
@@ -129,6 +137,26 @@
#if CONFIG_LIBYUV
static INLINE int vpx_image_scale(vpx_image_t *src, vpx_image_t *dst,
FilterModeEnum mode) {
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (src->fmt == VPX_IMG_FMT_I42016) {
+ assert(dst->fmt == VPX_IMG_FMT_I42016);
+ return I420Scale_16((uint16_t*)src->planes[VPX_PLANE_Y],
+ src->stride[VPX_PLANE_Y]/2,
+ (uint16_t*)src->planes[VPX_PLANE_U],
+ src->stride[VPX_PLANE_U]/2,
+ (uint16_t*)src->planes[VPX_PLANE_V],
+ src->stride[VPX_PLANE_V]/2,
+ src->d_w, src->d_h,
+ (uint16_t*)dst->planes[VPX_PLANE_Y],
+ dst->stride[VPX_PLANE_Y]/2,
+ (uint16_t*)dst->planes[VPX_PLANE_U],
+ dst->stride[VPX_PLANE_U]/2,
+ (uint16_t*)dst->planes[VPX_PLANE_V],
+ dst->stride[VPX_PLANE_V]/2,
+ dst->d_w, dst->d_h,
+ mode);
+ }
+#endif
assert(src->fmt == VPX_IMG_FMT_I420);
assert(dst->fmt == VPX_IMG_FMT_I420);
return I420Scale(src->planes[VPX_PLANE_Y], src->stride[VPX_PLANE_Y],
@@ -265,6 +293,11 @@
static void write_image_file(const vpx_image_t *img, const int planes[3],
FILE *file) {
int i, y;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ const int bytes_per_sample = ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+#else
+ const int bytes_per_sample = 1;
+#endif
for (i = 0; i < 3; ++i) {
const int plane = planes[i];
@@ -274,7 +307,7 @@
const int h = vpx_img_plane_height(img, plane);
for (y = 0; y < h; ++y) {
- fwrite(buf, 1, w, file);
+ fwrite(buf, bytes_per_sample, w, file);
buf += stride;
}
}
@@ -494,6 +527,178 @@
}
}
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static void high_img_upshift(vpx_image_t *dst, vpx_image_t *src,
+ int input_shift) {
+ const int offset = input_shift > 0 ? (1 << (input_shift - 1)) : 0;
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ dst->fmt != src->fmt || input_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case VPX_IMG_FMT_I42016:
+ case VPX_IMG_FMT_I42216:
+ case VPX_IMG_FMT_I44416:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint16_t *p_src = (uint16_t *)(src->planes[plane] +
+ y * src->stride[plane]);
+ uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
+ y * dst->stride[plane]);
+ for (x = 0; x < w; x++)
+ *p_dst++ = (*p_src++ << input_shift) + offset;
+ }
+ }
+}
+
+static void low_img_upshift(vpx_image_t *dst, vpx_image_t *src,
+ int input_shift) {
+ const int offset = input_shift > 0 ? (1 << (input_shift - 1)) : 0;
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ dst->fmt != src->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
+ input_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case VPX_IMG_FMT_I420:
+ case VPX_IMG_FMT_I422:
+ case VPX_IMG_FMT_I444:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
+ uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
+ y * dst->stride[plane]);
+ for (x = 0; x < w; x++) {
+ *p_dst++ = (*p_src++ << input_shift) + offset;
+ }
+ }
+ }
+}
+
+static void img_upshift(vpx_image_t *dst, vpx_image_t *src,
+ int input_shift) {
+ if (src->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ high_img_upshift(dst, src, input_shift);
+ } else {
+ low_img_upshift(dst, src, input_shift);
+ }
+}
+
+static void high_img_downshift(vpx_image_t *dst, vpx_image_t *src,
+ int down_shift) {
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ dst->fmt != src->fmt || down_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case VPX_IMG_FMT_I42016:
+ case VPX_IMG_FMT_I42216:
+ case VPX_IMG_FMT_I44416:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint16_t *p_src = (uint16_t *)(src->planes[plane] +
+ y * src->stride[plane]);
+ uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
+ y * dst->stride[plane]);
+ for (x = 0; x < w; x++)
+ *p_dst++ = *p_src++ >> down_shift;
+ }
+ }
+}
+
+static void low_img_downshift(vpx_image_t *dst, vpx_image_t *src,
+ int down_shift) {
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ src->fmt != dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
+ down_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (dst->fmt) {
+ case VPX_IMG_FMT_I420:
+ case VPX_IMG_FMT_I422:
+ case VPX_IMG_FMT_I444:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint16_t *p_src = (uint16_t *)(src->planes[plane] +
+ y * src->stride[plane]);
+ uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
+ for (x = 0; x < w; x++) {
+ *p_dst++ = *p_src++ >> down_shift;
+ }
+ }
+ }
+}
+
+static void img_downshift(vpx_image_t *dst, vpx_image_t *src,
+ int down_shift) {
+ if (dst->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ high_img_downshift(dst, src, down_shift);
+ } else {
+ low_img_downshift(dst, src, down_shift);
+ }
+}
+#endif
+
int main_loop(int argc, const char **argv_) {
vpx_codec_ctx_t decoder;
char *fn = NULL;
@@ -518,6 +723,9 @@
int opt_yv12 = 0;
int opt_i420 = 0;
vpx_codec_dec_cfg_t cfg = {0, 0, 0};
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ int output_bit_depth = 0;
+#endif
#if CONFIG_VP8_DECODER
vp8_postproc_cfg_t vp8_pp_cfg = {0};
int vp8_dbg_color_ref_frame = 0;
@@ -529,6 +737,9 @@
int dec_flags = 0;
int do_scale = 0;
vpx_image_t *scaled_img = NULL;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ vpx_image_t *img_shifted = NULL;
+#endif
int frame_avail, got_data;
int num_external_frame_buffers = 0;
struct ExternalFrameBufferList ext_fb_list = {0, NULL};
@@ -569,6 +780,9 @@
use_y4m = 0;
flipuv = 1;
opt_yv12 = 1;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ output_bit_depth = 8; // For yv12 8-bit depth output is assumed
+#endif
} else if (arg_match(&arg, &use_i420, argi)) {
use_y4m = 0;
flipuv = 0;
@@ -601,6 +815,11 @@
num_external_frame_buffers = arg_parse_uint(&arg);
else if (arg_match(&arg, &continuearg, argi))
keep_going = 1;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ else if (arg_match(&arg, &outbitdeptharg, argi)) {
+ output_bit_depth = arg_parse_uint(&arg);
+ }
+#endif
#if CONFIG_VP8_DECODER
else if (arg_match(&arg, &addnoise_level, argi)) {
postproc = 1;
@@ -905,6 +1124,33 @@
#endif
}
}
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ // Default to codec bit depth if output bit depth not set
+ if (!output_bit_depth) {
+ output_bit_depth = img->bit_depth;
+ }
+ // Shift up or down if necessary
+ if (output_bit_depth != img->bit_depth) {
+ if (!img_shifted) {
+ if (output_bit_depth == 8) {
+ img_shifted = vpx_img_alloc(
+ NULL, img->fmt - VPX_IMG_FMT_HIGHBITDEPTH,
+ img->d_w, img->d_h, 16);
+ } else {
+ img_shifted = vpx_img_alloc(
+ NULL, img->fmt | VPX_IMG_FMT_HIGHBITDEPTH,
+ img->d_w, img->d_h, 16);
+ }
+ img_shifted->bit_depth = output_bit_depth;
+ }
+ if (output_bit_depth > img->bit_depth) {
+ img_upshift(img_shifted, img, output_bit_depth - img->bit_depth);
+ } else {
+ img_downshift(img_shifted, img, img->bit_depth - output_bit_depth);
+ }
+ img = img_shifted;
+ }
+#endif
if (single_file) {
if (use_y4m) {
@@ -1011,6 +1257,9 @@
free(buf);
if (scaled_img) vpx_img_free(scaled_img);
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (img_shifted) vpx_img_free(img_shifted);
+#endif
for (i = 0; i < ext_fb_list.num_external_frame_buffers; ++i) {
free(ext_fb_list.ext_fb[i].data);
--- a/vpxenc.c
+++ b/vpxenc.c
@@ -200,6 +200,10 @@
ARG_DEF(NULL, "experimental-bitstream", 0,
"Allow experimental bitstream features.");
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static const arg_def_t test16bitinternalarg = ARG_DEF(
+ NULL, "test-16bit-internal", 0, "Force use of 16 bit internal buffer");
+#endif
static const arg_def_t *main_args[] = {
&debugmode,
@@ -248,6 +252,9 @@
#endif
&timebase, &framerate,
&error_resilient,
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ &test16bitinternalarg,
+#endif
&lag_in_frames, NULL
};
@@ -381,6 +388,23 @@
NULL, "frame-boost", 1,
"Enable frame periodic boost (0: off (default), 1: on)");
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static const struct arg_enum_list bitdepth_enum[] = {
+ {"8", VPX_BITS_8},
+ {"10", VPX_BITS_10},
+ {"12", VPX_BITS_12},
+ {NULL, 0}
+};
+
+static const arg_def_t bitdeptharg = ARG_DEF_ENUM("b", "bit-depth", 1,
+ "Bit depth for codec "
+ "(8 for version <=1, "
+ "10 or 12 for version 2)",
+ bitdepth_enum);
+static const arg_def_t inbitdeptharg = ARG_DEF(NULL, "input-bit-depth", 1,
+ "Bit depth of input");
+#endif
+
static const struct arg_enum_list tune_content_enum[] = {
{"default", VP9E_CONTENT_DEFAULT},
{"screen", VP9E_CONTENT_SCREEN},
@@ -395,6 +419,9 @@
&tile_cols, &tile_rows, &arnr_maxframes, &arnr_strength, &arnr_type,
&tune_ssim, &cq_level, &max_intra_rate_pct, &lossless,
&frame_parallel_decoding, &aq_mode, &frame_periodic_boost, &tune_content,
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ &bitdeptharg, &inbitdeptharg,
+#endif
NULL
};
static const int vp9_arg_ctrl_map[] = {
@@ -450,6 +477,102 @@
}
#define mmin(a, b) ((a) < (b) ? (a) : (b))
+
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static void find_mismatch_high(const vpx_image_t *const img1,
+ const vpx_image_t *const img2,
+ int yloc[4], int uloc[4], int vloc[4]) {
+ uint16_t *plane1, *plane2;
+ uint32_t stride1, stride2;
+ const uint32_t bsize = 64;
+ const uint32_t bsizey = bsize >> img1->y_chroma_shift;
+ const uint32_t bsizex = bsize >> img1->x_chroma_shift;
+ const uint32_t c_w =
+ (img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
+ const uint32_t c_h =
+ (img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
+ int match = 1;
+ uint32_t i, j;
+ yloc[0] = yloc[1] = yloc[2] = yloc[3] = -1;
+ plane1 = (uint16_t*)img1->planes[VPX_PLANE_Y];
+ plane2 = (uint16_t*)img2->planes[VPX_PLANE_Y];
+ stride1 = img1->stride[VPX_PLANE_Y]/2;
+ stride2 = img2->stride[VPX_PLANE_Y]/2;
+ for (i = 0, match = 1; match && i < img1->d_h; i += bsize) {
+ for (j = 0; match && j < img1->d_w; j += bsize) {
+ int k, l;
+ const int si = mmin(i + bsize, img1->d_h) - i;
+ const int sj = mmin(j + bsize, img1->d_w) - j;
+ for (k = 0; match && k < si; ++k) {
+ for (l = 0; match && l < sj; ++l) {
+ if (*(plane1 + (i + k) * stride1 + j + l) !=
+ *(plane2 + (i + k) * stride2 + j + l)) {
+ yloc[0] = i + k;
+ yloc[1] = j + l;
+ yloc[2] = *(plane1 + (i + k) * stride1 + j + l);
+ yloc[3] = *(plane2 + (i + k) * stride2 + j + l);
+ match = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ uloc[0] = uloc[1] = uloc[2] = uloc[3] = -1;
+ plane1 = (uint16_t*)img1->planes[VPX_PLANE_U];
+ plane2 = (uint16_t*)img2->planes[VPX_PLANE_U];
+ stride1 = img1->stride[VPX_PLANE_U]/2;
+ stride2 = img2->stride[VPX_PLANE_U]/2;
+ for (i = 0, match = 1; match && i < c_h; i += bsizey) {
+ for (j = 0; match && j < c_w; j += bsizex) {
+ int k, l;
+ const int si = mmin(i + bsizey, c_h - i);
+ const int sj = mmin(j + bsizex, c_w - j);
+ for (k = 0; match && k < si; ++k) {
+ for (l = 0; match && l < sj; ++l) {
+ if (*(plane1 + (i + k) * stride1 + j + l) !=
+ *(plane2 + (i + k) * stride2 + j + l)) {
+ uloc[0] = i + k;
+ uloc[1] = j + l;
+ uloc[2] = *(plane1 + (i + k) * stride1 + j + l);
+ uloc[3] = *(plane2 + (i + k) * stride2 + j + l);
+ match = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ vloc[0] = vloc[1] = vloc[2] = vloc[3] = -1;
+ plane1 = (uint16_t*)img1->planes[VPX_PLANE_V];
+ plane2 = (uint16_t*)img2->planes[VPX_PLANE_V];
+ stride1 = img1->stride[VPX_PLANE_V]/2;
+ stride2 = img2->stride[VPX_PLANE_V]/2;
+ for (i = 0, match = 1; match && i < c_h; i += bsizey) {
+ for (j = 0; match && j < c_w; j += bsizex) {
+ int k, l;
+ const int si = mmin(i + bsizey, c_h - i);
+ const int sj = mmin(j + bsizex, c_w - j);
+ for (k = 0; match && k < si; ++k) {
+ for (l = 0; match && l < sj; ++l) {
+ if (*(plane1 + (i + k) * stride1 + j + l) !=
+ *(plane2 + (i + k) * stride2 + j + l)) {
+ vloc[0] = i + k;
+ vloc[1] = j + l;
+ vloc[2] = *(plane1 + (i + k) * stride1 + j + l);
+ vloc[3] = *(plane2 + (i + k) * stride2 + j + l);
+ match = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
static void find_mismatch(const vpx_image_t *const img1,
const vpx_image_t *const img2,
int yloc[4], int uloc[4], int vloc[4]) {
@@ -542,7 +665,8 @@
static int compare_img(const vpx_image_t *const img1,
const vpx_image_t *const img2) {
- const uint32_t c_w =
+ uint32_t l_w = img1->d_w;
+ uint32_t c_w =
(img1->d_w + img1->x_chroma_shift) >> img1->x_chroma_shift;
const uint32_t c_h =
(img1->d_h + img1->y_chroma_shift) >> img1->y_chroma_shift;
@@ -552,11 +676,17 @@
match &= (img1->fmt == img2->fmt);
match &= (img1->d_w == img2->d_w);
match &= (img1->d_h == img2->d_h);
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (img1->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ l_w *= 2;
+ c_w *= 2;
+ }
+#endif
for (i = 0; i < img1->d_h; ++i)
match &= (memcmp(img1->planes[VPX_PLANE_Y] + i * img1->stride[VPX_PLANE_Y],
img2->planes[VPX_PLANE_Y] + i * img2->stride[VPX_PLANE_Y],
- img1->d_w) == 0);
+ l_w) == 0);
for (i = 0; i < c_h; ++i)
match &= (memcmp(img1->planes[VPX_PLANE_U] + i * img1->stride[VPX_PLANE_U],
@@ -601,6 +731,10 @@
int arg_ctrl_cnt;
int write_webm;
int have_kf_max_dist;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ // whether to use 16bit internal buffers
+ int use_16bit_internal;
+#endif
};
@@ -873,6 +1007,9 @@
static const int *ctrl_args_map = NULL;
struct stream_config *config = &stream->config;
int eos_mark_found = 0;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ int test_16bit_internal = 0;
+#endif
// Handle codec specific options
if (0) {
@@ -921,6 +1058,12 @@
config->cfg.g_w = arg_parse_uint(&arg);
} else if (arg_match(&arg, &height, argi)) {
config->cfg.g_h = arg_parse_uint(&arg);
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ } else if (arg_match(&arg, &bitdeptharg, argi)) {
+ config->cfg.g_bit_depth = arg_parse_enum_or_int(&arg);
+ } else if (arg_match(&arg, &inbitdeptharg, argi)) {
+ config->cfg.g_input_bit_depth = arg_parse_uint(&arg);
+#endif
#if CONFIG_WEBM_IO
} else if (arg_match(&arg, &stereo_mode, argi)) {
config->stereo_fmt = arg_parse_enum_or_int(&arg);
@@ -988,6 +1131,12 @@
config->have_kf_max_dist = 1;
} else if (arg_match(&arg, &kf_disabled, argi)) {
config->cfg.kf_mode = VPX_KF_DISABLED;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ } else if (arg_match(&arg, &test16bitinternalarg, argi)) {
+ if (strcmp(global->codec->name, "vp9") == 0) {
+ test_16bit_internal = 1;
+ }
+#endif
} else {
int i, match = 0;
for (i = 0; ctrl_args[i]; i++) {
@@ -1018,6 +1167,12 @@
argj++;
}
}
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (strcmp(global->codec->name, "vp9") == 0) {
+ config->use_16bit_internal = test_16bit_internal |
+ (config->cfg.g_profile > 1);
+ }
+#endif
return eos_mark_found;
}
@@ -1045,6 +1200,14 @@
experimental_bitstream.long_name);
}
+ // Check that the codec bit depth is greater than the input bit depth.
+ if (stream->config.cfg.g_input_bit_depth >
+ (int)stream->config.cfg.g_bit_depth) {
+ fatal("Stream %d: codec bit depth (%d) less than input bit depth (%d)",
+ stream->index, (int)stream->config.cfg.g_bit_depth,
+ stream->config.cfg.g_input_bit_depth);
+ }
+
for (streami = stream; streami; streami = streami->next) {
/* All streams require output files */
if (!streami->config.out_fn)
@@ -1153,6 +1316,8 @@
SHOW(g_profile);
SHOW(g_w);
SHOW(g_h);
+ SHOW(g_bit_depth);
+ SHOW(g_input_bit_depth);
SHOW(g_timebase.num);
SHOW(g_timebase.den);
SHOW(g_error_resilient);
@@ -1285,6 +1450,9 @@
flags |= global->show_psnr ? VPX_CODEC_USE_PSNR : 0;
flags |= global->out_part ? VPX_CODEC_USE_OUTPUT_PARTITION : 0;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ flags |= stream->config.use_16bit_internal ? VPX_CODEC_USE_HIGHBITDEPTH : 0;
+#endif
/* Construct Encoder Context */
vpx_codec_enc_init(&stream->encoder, global->codec->codec_interface(),
@@ -1330,6 +1498,46 @@
/ cfg->g_timebase.num / global->framerate.num;
/* Scale if necessary */
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (img) {
+ if ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) &&
+ (img->d_w != cfg->g_w || img->d_h != cfg->g_h)) {
+ if (img->fmt != VPX_IMG_FMT_I42016) {
+ fprintf(stderr, "%s can only scale 4:2:0 inputs\n", exec_name);
+ exit(EXIT_FAILURE);
+ }
+#if CONFIG_LIBYUV
+ if (!stream->img) {
+ stream->img = vpx_img_alloc(NULL, VPX_IMG_FMT_I42016,
+ cfg->g_w, cfg->g_h, 16);
+ }
+ I420Scale_16((uint16*)img->planes[VPX_PLANE_Y],
+ img->stride[VPX_PLANE_Y]/2,
+ (uint16*)img->planes[VPX_PLANE_U],
+ img->stride[VPX_PLANE_U]/2,
+ (uint16*)img->planes[VPX_PLANE_V],
+ img->stride[VPX_PLANE_V]/2,
+ img->d_w, img->d_h,
+ (uint16*)stream->img->planes[VPX_PLANE_Y],
+ stream->img->stride[VPX_PLANE_Y]/2,
+ (uint16*)stream->img->planes[VPX_PLANE_U],
+ stream->img->stride[VPX_PLANE_U]/2,
+ (uint16*)stream->img->planes[VPX_PLANE_V],
+ stream->img->stride[VPX_PLANE_V]/2,
+ stream->img->d_w, stream->img->d_h,
+ kFilterBox);
+ img = stream->img;
+#else
+ stream->encoder.err = 1;
+ ctx_exit_on_error(&stream->encoder,
+ "Stream %d: Failed to encode frame.\n"
+ "Scaling disabled in this configuration. \n"
+ "To enable, configure with --enable-libyuv\n",
+ stream->index);
+#endif
+ }
+ }
+#endif
if (img && (img->d_w != cfg->g_w || img->d_h != cfg->g_h)) {
if (img->fmt != VPX_IMG_FMT_I420 && img->fmt != VPX_IMG_FMT_YV12) {
fprintf(stderr, "%s can only scale 4:2:0 8bpp inputs\n", exec_name);
@@ -1508,7 +1716,132 @@
return (float)(usec > 0 ? frames * 1000000.0 / (float)usec : 0);
}
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+static void high_img_upshift(vpx_image_t *dst, vpx_image_t *src,
+ int input_shift) {
+ // Note the offset is 1 less than half
+ const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
+ int plane;
+ if (dst->w != src->w || dst->h != src->h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ dst->fmt != src->fmt || input_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case VPX_IMG_FMT_I42016:
+ case VPX_IMG_FMT_I42216:
+ case VPX_IMG_FMT_I44416:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->w;
+ int h = src->h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint16_t *p_src = (uint16_t *)(src->planes[plane] +
+ y * src->stride[plane]);
+ uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
+ y * dst->stride[plane]);
+ for (x = 0; x < w; x++)
+ *p_dst++ = (*p_src++ << input_shift) + offset;
+ }
+ }
+}
+static void low_img_upshift(vpx_image_t *dst, vpx_image_t *src,
+ int input_shift) {
+ // Note the offset is 1 less than half
+ const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
+ int plane;
+ if (dst->w != src->w || dst->h != src->h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ dst->fmt != src->fmt + VPX_IMG_FMT_HIGHBITDEPTH ||
+ input_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case VPX_IMG_FMT_I420:
+ case VPX_IMG_FMT_I422:
+ case VPX_IMG_FMT_I444:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->w;
+ int h = src->h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
+ uint16_t *p_dst = (uint16_t *)(dst->planes[plane] +
+ y * dst->stride[plane]);
+ for (x = 0; x < w; x++) {
+ *p_dst++ = (*p_src++ << input_shift) + offset;
+ }
+ }
+ }
+}
+
+static void img_upshift(vpx_image_t *dst, vpx_image_t *src,
+ int input_shift) {
+ if (src->fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ high_img_upshift(dst, src, input_shift);
+ } else {
+ low_img_upshift(dst, src, input_shift);
+ }
+}
+
+static void img_cast_16_to_8(vpx_image_t *dst, vpx_image_t *src) {
+ int plane;
+ if (dst->fmt + VPX_IMG_FMT_HIGHBITDEPTH != src->fmt ||
+ dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift) {
+ fatal("Unsupported image conversion");
+ }
+ switch (dst->fmt) {
+ case VPX_IMG_FMT_I420:
+ case VPX_IMG_FMT_I422:
+ case VPX_IMG_FMT_I444:
+ break;
+ default:
+ fatal("Unsupported image conversion");
+ break;
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w >>= src->x_chroma_shift;
+ h >>= src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ uint16_t *p_src = (uint16_t *)(src->planes[plane] +
+ y * src->stride[plane]);
+ uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
+ for (x = 0; x < w; x++) {
+ *p_dst++ = *p_src++;
+ }
+ }
+ }
+}
+#endif
+
static void test_decode(struct stream_state *stream,
enum TestDecodeFatality fatal,
const VpxInterface *codec) {
@@ -1534,13 +1867,29 @@
vpx_codec_control(&stream->encoder, VP8_COPY_REFERENCE, &ref_enc);
vpx_codec_control(&stream->decoder, VP8_COPY_REFERENCE, &ref_dec);
} else {
- struct vp9_ref_frame ref;
+ struct vp9_ref_frame ref_enc, ref_dec;
- ref.idx = 0;
- vpx_codec_control(&stream->encoder, VP9_GET_REFERENCE, &ref);
- enc_img = ref.img;
- vpx_codec_control(&stream->decoder, VP9_GET_REFERENCE, &ref);
- dec_img = ref.img;
+ ref_enc.idx = 0;
+ ref_dec.idx = 0;
+ vpx_codec_control(&stream->encoder, VP9_GET_REFERENCE, &ref_enc);
+ enc_img = ref_enc.img;
+ vpx_codec_control(&stream->decoder, VP9_GET_REFERENCE, &ref_dec);
+ dec_img = ref_dec.img;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if ((enc_img.fmt & VPX_IMG_FMT_HIGHBITDEPTH) !=
+ (dec_img.fmt & VPX_IMG_FMT_HIGHBITDEPTH)) {
+ if (enc_img.fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ vpx_img_alloc(&enc_img, enc_img.fmt - VPX_IMG_FMT_HIGHBITDEPTH,
+ enc_img.d_w, enc_img.d_h, 16);
+ img_cast_16_to_8(&enc_img, &ref_enc.img);
+ }
+ if (dec_img.fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ vpx_img_alloc(&dec_img, dec_img.fmt - VPX_IMG_FMT_HIGHBITDEPTH,
+ dec_img.d_w, dec_img.d_h, 16);
+ img_cast_16_to_8(&dec_img, &ref_dec.img);
+ }
+ }
+#endif
}
ctx_exit_on_error(&stream->encoder, "Failed to get encoder reference frame");
ctx_exit_on_error(&stream->decoder, "Failed to get decoder reference frame");
@@ -1547,7 +1896,15 @@
if (!compare_img(&enc_img, &dec_img)) {
int y[4], u[4], v[4];
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (enc_img.fmt & VPX_IMG_FMT_HIGHBITDEPTH) {
+ find_mismatch_high(&enc_img, &dec_img, y, u, v);
+ } else {
+ find_mismatch(&enc_img, &dec_img, y, u, v);
+ }
+#else
find_mismatch(&enc_img, &dec_img, y, u, v);
+#endif
stream->decoder.err = 1;
warn_or_exit_on_error(&stream->decoder, fatal == TEST_DECODE_FATAL,
"Stream %d: Encode/decode mismatch on frame %d at"
@@ -1589,6 +1946,12 @@
int main(int argc, const char **argv_) {
int pass;
vpx_image_t raw;
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ vpx_image_t raw_shift;
+ int allocated_raw_shift = 0;
+ int use_16bit_internal = 0;
+ int input_shift = 0;
+#endif
int frame_avail, got_data;
struct VpxInputContext input;
@@ -1690,6 +2053,27 @@
if (!input.width || !input.height)
fatal("Specify stream dimensions with --width (-w) "
" and --height (-h)");
+
+ /* If input file does not specify bit-depth but input-bit-depth parameter
+ * exists, assume that to be the input bit-depth. However, if the
+ * input-bit-depth paramter does not exist, assume the input bit-depth
+ * to be the same as the codec bit-depth.
+ */
+ if (!input.bit_depth) {
+ FOREACH_STREAM({
+ if (stream->config.cfg.g_input_bit_depth)
+ input.bit_depth = stream->config.cfg.g_input_bit_depth;
+ else
+ input.bit_depth = stream->config.cfg.g_input_bit_depth =
+ (int)stream->config.cfg.g_bit_depth;
+ });
+ if (input.bit_depth > 8) input.fmt |= VPX_IMG_FMT_HIGHBITDEPTH;
+ } else {
+ FOREACH_STREAM({
+ stream->config.cfg.g_input_bit_depth = input.bit_depth;
+ });
+ }
+
FOREACH_STREAM(set_stream_dimensions(stream, input.width, input.height));
FOREACH_STREAM(validate_stream_config(stream, &global));
@@ -1743,6 +2127,25 @@
FOREACH_STREAM(open_output_file(stream, &global));
FOREACH_STREAM(initialize_encoder(stream, &global));
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (strcmp(global.codec->name, "vp9") == 0) {
+ // Check to see if at least one stream uses 16 bit internal.
+ // Currently assume that the bit_depths for all streams using
+ // highbitdepth are the same.
+ FOREACH_STREAM({
+ if (stream->config.use_16bit_internal) {
+ use_16bit_internal = 1;
+ }
+ if (stream->config.cfg.g_profile == 0) {
+ input_shift = 0;
+ } else {
+ input_shift = (int)stream->config.cfg.g_bit_depth -
+ stream->config.cfg.g_input_bit_depth;
+ }
+ });
+ }
+#endif
+
frame_avail = 1;
got_data = 0;
@@ -1780,10 +2183,45 @@
frame_avail = 0;
if (frames_in > global.skip_frames) {
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ vpx_image_t *frame_to_encode;
+ if (input_shift || (use_16bit_internal && input.bit_depth == 8)) {
+ assert(use_16bit_internal);
+ // Input bit depth and stream bit depth do not match, so up
+ // shift frame to stream bit depth
+ if (!allocated_raw_shift) {
+ vpx_img_alloc(&raw_shift, raw.fmt | VPX_IMG_FMT_HIGHBITDEPTH,
+ input.width, input.height, 32);
+ allocated_raw_shift = 1;
+ }
+ img_upshift(&raw_shift, &raw, input_shift);
+ frame_to_encode = &raw_shift;
+ } else {
+ frame_to_encode = &raw;
+ }
vpx_usec_timer_start(&timer);
+ if (use_16bit_internal) {
+ assert(frame_to_encode->fmt & VPX_IMG_FMT_HIGHBITDEPTH);
+ FOREACH_STREAM({
+ if (stream->config.use_16bit_internal)
+ encode_frame(stream, &global,
+ frame_avail ? frame_to_encode : NULL,
+ frames_in);
+ else
+ assert(0);
+ });
+ } else {
+ assert((frame_to_encode->fmt & VPX_IMG_FMT_HIGHBITDEPTH) == 0);
+ FOREACH_STREAM(encode_frame(stream, &global,
+ frame_avail ? frame_to_encode : NULL,
+ frames_in));
+ }
+#else
+ vpx_usec_timer_start(&timer);
FOREACH_STREAM(encode_frame(stream, &global,
frame_avail ? &raw : NULL,
frames_in));
+#endif
vpx_usec_timer_mark(&timer);
cx_time += vpx_usec_timer_elapsed(&timer);
@@ -1901,6 +2339,10 @@
});
#endif
+#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
+ if (allocated_raw_shift)
+ vpx_img_free(&raw_shift);
+#endif
vpx_img_free(&raw);
free(argv);
free(streams);