diff --git a/configure b/configure index 2708b458b..32b70f18e 100755 --- a/configure +++ b/configure @@ -334,6 +334,7 @@ CONFIG_LIST=" multi_res_encoding temporal_denoising coefficient_range_checking + vp9_highbitdepth experimental size_limit ${EXPERIMENT_LIST} @@ -392,6 +393,7 @@ CMDLINE_SELECT=" multi_res_encoding temporal_denoising coefficient_range_checking + vp9_highbitdepth experimental " diff --git a/test/md5_helper.h b/test/md5_helper.h index dc9558267..1db712b4e 100644 --- a/test/md5_helper.h +++ b/test/md5_helper.h @@ -28,7 +28,8 @@ class MD5 { // 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) >> diff --git a/test/y4m_test.cc b/test/y4m_test.cc index 17cd78207..58a6fe3fa 100644 --- a/test/y4m_test.cc +++ b/test/y4m_test.cc @@ -57,7 +57,7 @@ static void write_image_file(const vpx_image_t *img, FILE *file) { 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) >> diff --git a/tools_common.c b/tools_common.c index 7cfd066ec..2ec17117c 100644 --- a/tools_common.c +++ b/tools_common.c @@ -83,7 +83,7 @@ int read_yuv_frame(struct VpxInputContext *input_ctx, vpx_image_t *yuv_frame) { 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 @@ int vpx_img_read(vpx_image_t *img, FILE *file) { 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; diff --git a/vp8/vp8_cx_iface.c b/vp8/vp8_cx_iface.c index 5be645c71..4237efc7e 100644 --- a/vp8/vp8_cx_iface.c +++ b/vp8/vp8_cx_iface.c @@ -1244,6 +1244,9 @@ static vpx_codec_enc_cfg_map_t vp8e_usage_cfg_map[] = 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 */ diff --git a/vp9/common/vp9_alloccommon.c b/vp9/common/vp9_alloccommon.c index c65e008c9..21ae8d575 100644 --- a/vp9/common/vp9_alloccommon.c +++ b/vp9/common/vp9_alloccommon.c @@ -177,7 +177,11 @@ int vp9_alloc_ref_frame_buffers(VP9_COMMON *cm, int width, int height) { 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 @@ int vp9_alloc_ref_frame_buffers(VP9_COMMON *cm, int width, int height) { #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 diff --git a/vp9/common/vp9_common.h b/vp9/common/vp9_common.h index 2788e66f4..5587192e8 100644 --- a/vp9/common/vp9_common.h +++ b/vp9/common/vp9_common.h @@ -64,6 +64,11 @@ static INLINE int get_unsigned_bits(unsigned int num_values) { 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); \ diff --git a/vp9/common/vp9_enums.h b/vp9/common/vp9_enums.h index d77631341..8817fdbb9 100644 --- a/vp9/common/vp9_enums.h +++ b/vp9/common/vp9_enums.h @@ -40,12 +40,6 @@ typedef enum BITSTREAM_PROFILE { MAX_PROFILES } BITSTREAM_PROFILE; -typedef enum BIT_DEPTH { - BITS_8, - BITS_10, - BITS_12 -} BIT_DEPTH; - typedef enum BLOCK_SIZE { BLOCK_4X4, BLOCK_4X8, diff --git a/vp9/common/vp9_onyxc_int.h b/vp9/common/vp9_onyxc_int.h index 47aa563ba..637867aa1 100644 --- a/vp9/common/vp9_onyxc_int.h +++ b/vp9/common/vp9_onyxc_int.h @@ -84,6 +84,10 @@ typedef struct VP9Common { 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 @@ typedef struct VP9Common { 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; diff --git a/vp9/decoder/vp9_decodeframe.c b/vp9/decoder/vp9_decodeframe.c index aae56c178..372dc83c8 100644 --- a/vp9/decoder/vp9_decodeframe.c +++ b/vp9/decoder/vp9_decodeframe.c @@ -653,7 +653,11 @@ static void setup_frame_size(VP9_COMMON *cm, struct vp9_read_bit_buffer *rb) { 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 @@ static void setup_frame_size_with_refs(VP9_COMMON *cm, 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 @@ BITSTREAM_PROFILE vp9_read_profile(struct vp9_read_bit_buffer *rb) { 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 @@ static size_t read_uncompressed_header(VP9Decoder *pbi, "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 @@ void vp9_decode_frame(VP9Decoder *pbi, if (!first_partition_size) { // showing a frame directly - *p_data_end = data + 1; + *p_data_end = data + (cm->profile <= PROFILE_2 ? 1 : 2); return; } diff --git a/vp9/decoder/vp9_decoder.c b/vp9/decoder/vp9_decoder.c index ae0da796e..f461af53e 100644 --- a/vp9/decoder/vp9_decoder.c +++ b/vp9/decoder/vp9_decoder.c @@ -66,6 +66,7 @@ VP9Decoder *vp9_decoder_create() { 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 diff --git a/vp9/encoder/vp9_bitstream.c b/vp9/encoder/vp9_bitstream.c index 5c304462a..a23d4b762 100644 --- a/vp9/encoder/vp9_bitstream.c +++ b/vp9/encoder/vp9_bitstream.c @@ -1046,8 +1046,8 @@ static void write_profile(BITSTREAM_PROFILE profile, 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) { diff --git a/vp9/encoder/vp9_denoiser.c b/vp9/encoder/vp9_denoiser.c index 793a9da07..e9fbf1bbf 100644 --- a/vp9/encoder/vp9_denoiser.c +++ b/vp9/encoder/vp9_denoiser.c @@ -388,13 +388,21 @@ void vp9_denoiser_update_frame_stats(MB_MODE_INFO *mbmi, unsigned int sse, } 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 @@ int vp9_denoiser_alloc(VP9_DENOISER *denoiser, int width, int height, } 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; diff --git a/vp9/encoder/vp9_denoiser.h b/vp9/encoder/vp9_denoiser.h index 8a91492b7..1c827b622 100644 --- a/vp9/encoder/vp9_denoiser.h +++ b/vp9/encoder/vp9_denoiser.h @@ -47,7 +47,11 @@ void vp9_denoiser_update_frame_stats(MB_MODE_INFO *mbmi, 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); diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index 88b67cf5e..4d970530e 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c @@ -443,6 +443,9 @@ static void alloc_raw_frame_buffers(VP9_COMP *cpi) { 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 @@ static void alloc_raw_frame_buffers(VP9_COMP *cpi) { 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 @@ static void alloc_util_frame_buffers(VP9_COMP *cpi) { 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 @@ static void alloc_util_frame_buffers(VP9_COMP *cpi) { 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 @@ static void alloc_util_frame_buffers(VP9_COMP *cpi) { 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 @@ static void update_frame_size(VP9_COMP *cpi) { 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 @@ void vp9_change_config(struct VP9_COMP *cpi, const VP9EncoderConfig *oxcf) { 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 @@ void vp9_change_config(struct VP9_COMP *cpi, const VP9EncoderConfig *oxcf) { 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 @@ void vp9_scale_references(VP9_COMP *cpi) { 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 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, 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); diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h index b26c8c788..b152f08db 100644 --- a/vp9/encoder/vp9_encoder.h +++ b/vp9/encoder/vp9_encoder.h @@ -114,9 +114,10 @@ typedef enum { 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 diff --git a/vp9/encoder/vp9_lookahead.c b/vp9/encoder/vp9_lookahead.c index e7435170e..823e7a162 100644 --- a/vp9/encoder/vp9_lookahead.c +++ b/vp9/encoder/vp9_lookahead.c @@ -50,6 +50,9 @@ struct lookahead_ctx *vp9_lookahead_init(unsigned int width, 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 @@ struct lookahead_ctx *vp9_lookahead_init(unsigned int width, 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; } diff --git a/vp9/encoder/vp9_lookahead.h b/vp9/encoder/vp9_lookahead.h index 678c51a1b..27861933d 100644 --- a/vp9/encoder/vp9_lookahead.h +++ b/vp9/encoder/vp9_lookahead.h @@ -56,6 +56,9 @@ struct lookahead_ctx *vp9_lookahead_init(unsigned int width, unsigned int height, unsigned int subsampling_x, unsigned int subsampling_y, +#if CONFIG_VP9_HIGHBITDEPTH + int use_highbitdepth, +#endif unsigned int depth); diff --git a/vp9/encoder/vp9_temporal_filter.c b/vp9/encoder/vp9_temporal_filter.c index 045e3590a..0d95ed16f 100644 --- a/vp9/encoder/vp9_temporal_filter.c +++ b/vp9/encoder/vp9_temporal_filter.c @@ -465,6 +465,9 @@ void vp9_temporal_filter(VP9_COMP *cpi, int distance) { 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, diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index 3dbf0017d..2230db3a5 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -37,7 +37,7 @@ struct vp9_extracfg { 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 @@ static struct vp9_extracfg default_extra_cfg = { 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 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, 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 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, } } +#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 @@ static int get_image_bps(const vpx_image_t *img) { 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 @@ static vpx_codec_err_t set_encoder_config( 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 @@ static vpx_codec_enc_cfg_map_t encoder_usage_cfg_map[] = { 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 diff --git a/vp9/vp9_dx_iface.c b/vp9/vp9_dx_iface.c index 7909f5392..bcc64a5d6 100644 --- a/vp9/vp9_dx_iface.c +++ b/vp9/vp9_dx_iface.c @@ -440,6 +440,7 @@ static vpx_image_t *decoder_get_frame(vpx_codec_alg_priv_t *ctx, // 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_display_size(vpx_codec_alg_priv_t *ctx, } } +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 @@ static vpx_codec_ctrl_fn_map_t decoder_ctrl_maps[] = { {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}, }; diff --git a/vpx/src/vpx_image.c b/vpx/src/vpx_image.c index e20703a5e..e58b61ea3 100644 --- a/vpx/src/vpx_image.c +++ b/vpx/src/vpx_image.c @@ -154,7 +154,7 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, 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; diff --git a/vpx/vp8dx.h b/vpx/vp8dx.h index bd7f19c43..379b30620 100644 --- a/vpx/vp8dx.h +++ b/vpx/vp8dx.h @@ -75,6 +75,9 @@ enum vp8_dec_control_id { /** 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(VP8D_GET_LAST_REF_USED, int *) 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 */ diff --git a/vpx/vpx_codec.h b/vpx/vpx_codec.h index 91fc532b5..b25308ed9 100644 --- a/vpx/vpx_codec.h +++ b/vpx/vpx_codec.h @@ -217,9 +217,9 @@ extern "C" { * 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; /* diff --git a/vpx/vpx_encoder.h b/vpx/vpx_encoder.h index 75d3a47b1..fdabed121 100644 --- a/vpx/vpx_encoder.h +++ b/vpx/vpx_encoder.h @@ -80,6 +80,9 @@ extern "C" { */ #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 @@ extern "C" { #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 @@ extern "C" { */ 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 * diff --git a/vpx/vpx_image.h b/vpx/vpx_image.h index 7b04b70a1..0b7bb9057 100644 --- a/vpx/vpx_image.h +++ b/vpx/vpx_image.h @@ -31,10 +31,10 @@ extern "C" { #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 @@ extern "C" { 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 diff --git a/vpx_mem/vpx_mem.c b/vpx_mem/vpx_mem.c index 059248bab..da616425c 100644 --- a/vpx_mem/vpx_mem.c +++ b/vpx_mem/vpx_mem.c @@ -16,6 +16,7 @@ #include #include #include "include/vpx_mem_intrnl.h" +#include "vpx/vpx_integer.h" #if CONFIG_MEM_TRACKER #ifndef VPX_NO_GLOBALS @@ -452,6 +453,29 @@ void *vpx_memset(void *dest, int val, size_t length) { 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 diff --git a/vpx_mem/vpx_mem.h b/vpx_mem/vpx_mem.h index 33686b276..e2391f496 100644 --- a/vpx_mem/vpx_mem.h +++ b/vpx_mem/vpx_mem.h @@ -73,6 +73,9 @@ extern "C" { 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 */ diff --git a/vpx_scale/generic/yv12config.c b/vpx_scale/generic/yv12config.c index 827bce789..70d7ac0c8 100644 --- 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_free_frame_buffer(YV12_BUFFER_CONFIG *ybf) { 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 @@ int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, 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 @@ int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, 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_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, 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; } diff --git a/vpx_scale/generic/yv12extend.c b/vpx_scale/generic/yv12extend.c index 036a50537..0485452ae 100644 --- 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 @@ static void extend_plane(uint8_t *const src, int src_stride, } } +#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 @@ void vp8_yv12_extend_frame_borders_c(YV12_BUFFER_CONFIG *ybf) { 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 @@ static void extend_frame(YV12_BUFFER_CONFIG *const ybf, int ext_size) { 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 @@ void vp9_extend_frame_inner_borders_c(YV12_BUFFER_CONFIG *ybf) { 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 @@ void vp8_yv12_copy_frame_c(const YV12_BUFFER_CONFIG *src_ybc, 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; @@ -173,6 +301,19 @@ void vpx_yv12_copy_y_c(const YV12_BUFFER_CONFIG *src_ybc, 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); src += src_ybc->y_stride; diff --git a/vpx_scale/yv12config.h b/vpx_scale/yv12config.h index cdde75cc5..eb0a8d68f 100644 --- a/vpx_scale/yv12config.h +++ b/vpx_scale/yv12config.h @@ -55,6 +55,8 @@ typedef struct yv12_buffer_config { 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 vp8_yv12_de_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf); 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 @@ int vp9_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf, // 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, diff --git a/vpxdec.c b/vpxdec.c index 412504409..647008162 100644 --- a/vpxdec.c +++ b/vpxdec.c @@ -90,12 +90,20 @@ static const arg_def_t fb_arg = 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, &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 @@ static const arg_def_t *vp8_pp_args[] = { #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 update_image_md5(const vpx_image_t *img, const int planes[3], 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 @@ static void write_image_file(const vpx_image_t *img, const int planes[3], 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 @@ static FILE *open_outfile(const char *name) { } } +#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 main_loop(int argc, const char **argv_) { 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 main_loop(int argc, const char **argv_) { 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 @@ int main_loop(int argc, const char **argv_) { 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 @@ int main_loop(int argc, const char **argv_) { 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 @@ int main_loop(int argc, const char **argv_) { #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 @@ fail: 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); diff --git a/vpxenc.c b/vpxenc.c index ab91d5080..b1156e1de 100644 --- a/vpxenc.c +++ b/vpxenc.c @@ -200,6 +200,10 @@ static const arg_def_t experimental_bitstream = 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 @@ static const arg_def_t *global_args[] = { #endif &timebase, &framerate, &error_resilient, +#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH + &test16bitinternalarg, +#endif &lag_in_frames, NULL }; @@ -381,6 +388,23 @@ static const arg_def_t frame_periodic_boost = ARG_DEF( 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 @@ static const arg_def_t *vp9_args[] = { &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 @@ void usage_exit() { } #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 void find_mismatch(const vpx_image_t *const img1, 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 @@ static int compare_img(const vpx_image_t *const img1, 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 @@ struct stream_config { 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 int parse_stream_params(struct VpxEncoderConfig *global, 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 @@ static int parse_stream_params(struct VpxEncoderConfig *global, 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 @@ static int parse_stream_params(struct VpxEncoderConfig *global, 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 @@ static int parse_stream_params(struct VpxEncoderConfig *global, 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 @@ static void validate_stream_config(const struct stream_state *stream, 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 @@ static void show_stream_config(struct stream_state *stream, 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 @@ static void initialize_encoder(struct stream_state *stream, 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 @@ static void encode_frame(struct stream_state *stream, / 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,6 +1716,131 @@ static float usec_to_fps(uint64_t usec, unsigned int frames) { 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, @@ -1534,20 +1867,44 @@ static void test_decode(struct stream_state *stream, 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"); 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 @@ static void print_time(const char *label, int64_t etl) { 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 @@ int main(int argc, const char **argv_) { 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 @@ int main(int argc, const char **argv_) { 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 @@ int main(int argc, const char **argv_) { 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 @@ int main(int argc, const char **argv_) { }); #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);