From 64485398d86a4b81ce19c35f791cbf0478bde0ee Mon Sep 17 00:00:00 2001 From: Jerome Jiang Date: Wed, 27 May 2020 13:57:50 -0700 Subject: Add NV12 support Change-Id: Ia2a8221a156e0882079c5a252f59bc84d8f516b1 --- test/test-data.mk | 1 + test/test-data.sha1 | 1 + test/vp8_datarate_test.cc | 22 +++++++++++++++++++ test/vp9_end_to_end_test.cc | 30 +++++++++++++++++++++++++ test/yuv_video_source.h | 1 + tools_common.c | 7 ++++-- vp8/common/extend.c | 53 +++++++++++++++++++++++++++++---------------- vp8/vp8_cx_iface.c | 7 ++++-- vp9/encoder/vp9_extend.c | 32 ++++++++++++++++++--------- vp9/vp9_cx_iface.c | 6 +++-- vp9/vp9_iface_common.c | 9 ++++++-- vpx/src/vpx_image.c | 13 +++++++++-- vpx/vpx_image.h | 1 + vpxenc.c | 8 ++++++- vpxenc.h | 1 + 15 files changed, 152 insertions(+), 40 deletions(-) diff --git a/test/test-data.mk b/test/test-data.mk index 81f035d83..ca2e11442 100644 --- a/test/test-data.mk +++ b/test/test-data.mk @@ -2,6 +2,7 @@ LIBVPX_TEST_SRCS-yes += test-data.mk # Encoder test source LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += hantro_collage_w352h288.yuv +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += hantro_collage_w352h288_nv12.yuv LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += hantro_odd.yuv LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += desktop_office1.1280_720-020.yuv LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += slides_code_term_web_plot.1920_1080.yuv diff --git a/test/test-data.sha1 b/test/test-data.sha1 index dcaea2866..668992fba 100644 --- a/test/test-data.sha1 +++ b/test/test-data.sha1 @@ -868,3 +868,4 @@ bac455906360b45338a16dd626ac5f19bc36a307 *desktop_office1.1280_720-020.yuv 094be4b80fa30bd227149ea16ab6476d549ea092 *slides_code_term_web_plot.1920_1080.yuv 518a0be998afece76d3df76047d51e256c591ff2 *invalid-bug-148271109.ivf d3964f9dad9f60363c81b688324d95b4ec7c8038 *invalid-bug-148271109.ivf.res +ad18ca16f0a249fb3b7c38de0d9b327fed273f96 *hantro_collage_w352h288_nv12.yuv diff --git a/test/vp8_datarate_test.cc b/test/vp8_datarate_test.cc index 95a1157f6..2ae99ee49 100644 --- a/test/vp8_datarate_test.cc +++ b/test/vp8_datarate_test.cc @@ -408,6 +408,28 @@ TEST_P(DatarateTestRealTime, GFBoost) { << " The datarate for the file missed the target!"; } +TEST_P(DatarateTestRealTime, NV12) { + denoiser_on_ = 0; + cfg_.rc_buf_initial_sz = 500; + cfg_.rc_dropframe_thresh = 0; + cfg_.rc_max_quantizer = 56; + cfg_.rc_end_usage = VPX_CBR; + cfg_.g_error_resilient = 0; + ::libvpx_test::YUVVideoSource video("hantro_collage_w352h288_nv12.yuv", + VPX_IMG_FMT_NV12, 352, 288, 30, 1, 0, + 100); + + cfg_.rc_target_bitrate = 200; + ResetModel(); + + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_GE(cfg_.rc_target_bitrate, effective_datarate_ * 0.95) + << " The datarate for the file exceeds the target!"; + + ASSERT_LE(cfg_.rc_target_bitrate, file_datarate_ * 1.4) + << " The datarate for the file missed the target!"; +} + VP8_INSTANTIATE_TEST_CASE(DatarateTestLarge, ALL_TEST_MODES, ::testing::Values(0)); VP8_INSTANTIATE_TEST_CASE(DatarateTestRealTime, diff --git a/test/vp9_end_to_end_test.cc b/test/vp9_end_to_end_test.cc index 7cb716f22..0165aa26e 100644 --- a/test/vp9_end_to_end_test.cc +++ b/test/vp9_end_to_end_test.cc @@ -59,6 +59,10 @@ const TestVideoParam kTestVectors[] = { #endif // CONFIG_VP9_HIGHBITDEPTH }; +const TestVideoParam kTestVectorsNv12[] = { + { "hantro_collage_w352h288_nv12.yuv", 8, VPX_IMG_FMT_NV12, VPX_BITS_8, 0 }, +}; + // Encoding modes tested const libvpx_test::TestMode kEncodingModeVectors[] = { ::libvpx_test::kTwoPassGood, ::libvpx_test::kOnePassGood, @@ -237,6 +241,27 @@ class EndToEndTestLoopFilterThreading }; #endif // CONFIG_VP9_DECODER +class EndToEndNV12 : public EndToEndTestLarge {}; + +TEST_P(EndToEndNV12, EndtoEndNV12Test) { + cfg_.rc_target_bitrate = kBitrate; + cfg_.g_error_resilient = 0; + cfg_.g_profile = test_video_param_.profile; + cfg_.g_input_bit_depth = test_video_param_.input_bit_depth; + cfg_.g_bit_depth = test_video_param_.bit_depth; + init_flags_ = VPX_CODEC_USE_PSNR; + if (cfg_.g_bit_depth > 8) init_flags_ |= VPX_CODEC_USE_HIGHBITDEPTH; + + std::unique_ptr video; + + video.reset(new libvpx_test::YUVVideoSource(test_video_param_.filename, + test_video_param_.fmt, 352, 288, + 30, 1, 0, 100)); + ASSERT_TRUE(video.get() != NULL); + + ASSERT_NO_FATAL_FAILURE(RunLoop(video.get())); +} + TEST_P(EndToEndTestLarge, EndtoEndPSNRTest) { cfg_.rc_target_bitrate = kBitrate; cfg_.g_error_resilient = 0; @@ -314,6 +339,11 @@ VP9_INSTANTIATE_TEST_CASE(EndToEndTestLarge, ::testing::ValuesIn(kTestVectors), ::testing::ValuesIn(kCpuUsedVectors)); +VP9_INSTANTIATE_TEST_CASE(EndToEndNV12, + ::testing::Values(::libvpx_test::kRealTime), + ::testing::ValuesIn(kTestVectorsNv12), + ::testing::ValuesIn({ 6, 7, 8 })); + VP9_INSTANTIATE_TEST_CASE(EndToEndTestAdaptiveRDThresh, ::testing::Values(5, 6, 7), ::testing::Values(8, 9)); diff --git a/test/yuv_video_source.h b/test/yuv_video_source.h index 020ce801d..383ab8f1b 100644 --- a/test/yuv_video_source.h +++ b/test/yuv_video_source.h @@ -84,6 +84,7 @@ class YUVVideoSource : public VideoSource { height_ = height; format_ = format; switch (format) { + case VPX_IMG_FMT_NV12: case VPX_IMG_FMT_I420: raw_size_ = width * height * 3 / 2; break; case VPX_IMG_FMT_I422: raw_size_ = width * height * 2; break; case VPX_IMG_FMT_I440: raw_size_ = width * height * 2; break; diff --git a/tools_common.c b/tools_common.c index 59978b7f9..cbecfbb41 100644 --- a/tools_common.c +++ b/tools_common.c @@ -91,10 +91,13 @@ int read_yuv_frame(struct VpxInputContext *input_ctx, vpx_image_t *yuv_frame) { for (plane = 0; plane < 3; ++plane) { uint8_t *ptr; - const int w = vpx_img_plane_width(yuv_frame, plane); + int w = vpx_img_plane_width(yuv_frame, plane); const int h = vpx_img_plane_height(yuv_frame, plane); int r; - + // Assuming that for nv12 we read all chroma data at one time + if (yuv_frame->fmt == VPX_IMG_FMT_NV12 && plane > 1) break; + // Fixing NV12 chroma width it is odd + if (yuv_frame->fmt == VPX_IMG_FMT_NV12 && plane == 1) w = (w + 1) & ~1; /* Determine the correct plane based on the image format. The for-loop * always counts in Y,U,V order, but this may not match the order of * the data on disk. diff --git a/vp8/common/extend.c b/vp8/common/extend.c index f4dbce2cd..b52e9fe93 100644 --- a/vp8/common/extend.c +++ b/vp8/common/extend.c @@ -11,30 +11,40 @@ #include "extend.h" #include "vpx_mem/vpx_mem.h" -static void copy_and_extend_plane(unsigned char *s, /* source */ - int sp, /* source pitch */ - unsigned char *d, /* destination */ - int dp, /* destination pitch */ - int h, /* height */ - int w, /* width */ - int et, /* extend top border */ - int el, /* extend left border */ - int eb, /* extend bottom border */ - int er) { /* extend right border */ - int i; +static void copy_and_extend_plane( + unsigned char *s, /* source */ + int sp, /* source pitch */ + unsigned char *d, /* destination */ + int dp, /* destination pitch */ + int h, /* height */ + int w, /* width */ + int et, /* extend top border */ + int el, /* extend left border */ + int eb, /* extend bottom border */ + int er, /* extend right border */ + int interleave_step) { /* step between pixels of the current plane */ + int i, j; unsigned char *src_ptr1, *src_ptr2; unsigned char *dest_ptr1, *dest_ptr2; int linesize; + if (interleave_step < 1) interleave_step = 1; + /* copy the left and right most columns out */ src_ptr1 = s; - src_ptr2 = s + w - 1; + src_ptr2 = s + (w - 1) * interleave_step; dest_ptr1 = d - el; dest_ptr2 = d + w; for (i = 0; i < h; ++i) { memset(dest_ptr1, src_ptr1[0], el); - memcpy(dest_ptr1 + el, src_ptr1, w); + if (interleave_step == 1) { + memcpy(dest_ptr1 + el, src_ptr1, w); + } else { + for (j = 0; j < w; j++) { + dest_ptr1[el + j] = src_ptr1[interleave_step * j]; + } + } memset(dest_ptr2, src_ptr2[0], er); src_ptr1 += sp; src_ptr2 += sp; @@ -69,9 +79,12 @@ void vp8_copy_and_extend_frame(YV12_BUFFER_CONFIG *src, int eb = dst->border + dst->y_height - src->y_height; int er = dst->border + dst->y_width - src->y_width; + // detect nv12 colorspace + int chroma_step = src->v_buffer - src->u_buffer == 1 ? 2 : 1; + copy_and_extend_plane(src->y_buffer, src->y_stride, dst->y_buffer, dst->y_stride, src->y_height, src->y_width, et, el, eb, - er); + er, 1); et = dst->border >> 1; el = dst->border >> 1; @@ -80,11 +93,11 @@ void vp8_copy_and_extend_frame(YV12_BUFFER_CONFIG *src, copy_and_extend_plane(src->u_buffer, src->uv_stride, dst->u_buffer, dst->uv_stride, src->uv_height, src->uv_width, et, el, - eb, er); + eb, er, chroma_step); copy_and_extend_plane(src->v_buffer, src->uv_stride, dst->v_buffer, dst->uv_stride, src->uv_height, src->uv_width, et, el, - eb, er); + eb, er, chroma_step); } void vp8_copy_and_extend_frame_with_rect(YV12_BUFFER_CONFIG *src, @@ -98,6 +111,8 @@ void vp8_copy_and_extend_frame_with_rect(YV12_BUFFER_CONFIG *src, int dst_y_offset = srcy * dst->y_stride + srcx; int src_uv_offset = ((srcy * src->uv_stride) >> 1) + (srcx >> 1); int dst_uv_offset = ((srcy * dst->uv_stride) >> 1) + (srcx >> 1); + // detect nv12 colorspace + int chroma_step = src->v_buffer - src->u_buffer == 1 ? 2 : 1; /* If the side is not touching the bounder then don't extend. */ if (srcy) et = 0; @@ -107,7 +122,7 @@ void vp8_copy_and_extend_frame_with_rect(YV12_BUFFER_CONFIG *src, copy_and_extend_plane(src->y_buffer + src_y_offset, src->y_stride, dst->y_buffer + dst_y_offset, dst->y_stride, srch, srcw, - et, el, eb, er); + et, el, eb, er, 1); et = (et + 1) >> 1; el = (el + 1) >> 1; @@ -118,11 +133,11 @@ void vp8_copy_and_extend_frame_with_rect(YV12_BUFFER_CONFIG *src, copy_and_extend_plane(src->u_buffer + src_uv_offset, src->uv_stride, dst->u_buffer + dst_uv_offset, dst->uv_stride, srch, - srcw, et, el, eb, er); + srcw, et, el, eb, er, chroma_step); copy_and_extend_plane(src->v_buffer + src_uv_offset, src->uv_stride, dst->v_buffer + dst_uv_offset, dst->uv_stride, srch, - srcw, et, el, eb, er); + srcw, et, el, eb, er, chroma_step); } /* note the extension is only for the last row, for intra prediction purpose */ diff --git a/vp8/vp8_cx_iface.c b/vp8/vp8_cx_iface.c index 8f7617abf..1160f51d6 100644 --- a/vp8/vp8_cx_iface.c +++ b/vp8/vp8_cx_iface.c @@ -264,9 +264,12 @@ static vpx_codec_err_t validate_img(vpx_codec_alg_priv_t *ctx, const vpx_image_t *img) { switch (img->fmt) { case VPX_IMG_FMT_YV12: - case VPX_IMG_FMT_I420: break; + case VPX_IMG_FMT_I420: + case VPX_IMG_FMT_NV12: break; default: - ERROR("Invalid image format. Only YV12 and I420 images are supported"); + ERROR( + "Invalid image format. Only YV12, I420 and NV12 images are " + "supported"); } if ((img->d_w != ctx->cfg.g_w) || (img->d_h != ctx->cfg.g_h)) diff --git a/vp9/encoder/vp9_extend.c b/vp9/encoder/vp9_extend.c index f8e24610a..dcb62e876 100644 --- a/vp9/encoder/vp9_extend.c +++ b/vp9/encoder/vp9_extend.c @@ -18,18 +18,26 @@ static void copy_and_extend_plane(const uint8_t *src, int src_pitch, uint8_t *dst, int dst_pitch, int w, int h, int extend_top, int extend_left, - int extend_bottom, int extend_right) { - int i, linesize; + int extend_bottom, int extend_right, + int interleave_step) { + int i, j, linesize; + const int step = interleave_step < 1 ? 1 : interleave_step; // copy the left and right most columns out const uint8_t *src_ptr1 = src; - const uint8_t *src_ptr2 = src + w - 1; + const uint8_t *src_ptr2 = src + (w - 1) * step; uint8_t *dst_ptr1 = dst - extend_left; uint8_t *dst_ptr2 = dst + w; for (i = 0; i < h; i++) { memset(dst_ptr1, src_ptr1[0], extend_left); - memcpy(dst_ptr1 + extend_left, src_ptr1, w); + if (step == 1) { + memcpy(dst_ptr1 + extend_left, src_ptr1, w); + } else { + for (j = 0; j < w; j++) { + dst_ptr1[extend_left + j] = src_ptr1[step * j]; + } + } memset(dst_ptr2, src_ptr2[0], extend_right); src_ptr1 += src_pitch; src_ptr2 += src_pitch; @@ -122,6 +130,8 @@ void vp9_copy_and_extend_frame(const YV12_BUFFER_CONFIG *src, const int el_uv = el_y >> uv_width_subsampling; const int eb_uv = eb_y >> uv_height_subsampling; const int er_uv = er_y >> uv_width_subsampling; + // detect nv12 colorspace + const int chroma_step = src->v_buffer - src->u_buffer == 1 ? 2 : 1; #if CONFIG_VP9_HIGHBITDEPTH if (src->flags & YV12_FLAG_HIGHBITDEPTH) { @@ -142,15 +152,15 @@ void vp9_copy_and_extend_frame(const YV12_BUFFER_CONFIG *src, copy_and_extend_plane(src->y_buffer, src->y_stride, dst->y_buffer, dst->y_stride, src->y_crop_width, src->y_crop_height, - et_y, el_y, eb_y, er_y); + et_y, el_y, eb_y, er_y, 1); copy_and_extend_plane(src->u_buffer, src->uv_stride, dst->u_buffer, dst->uv_stride, src->uv_crop_width, src->uv_crop_height, - et_uv, el_uv, eb_uv, er_uv); + et_uv, el_uv, eb_uv, er_uv, chroma_step); copy_and_extend_plane(src->v_buffer, src->uv_stride, dst->v_buffer, dst->uv_stride, src->uv_crop_width, src->uv_crop_height, - et_uv, el_uv, eb_uv, er_uv); + et_uv, el_uv, eb_uv, er_uv, chroma_step); } void vp9_copy_and_extend_frame_with_rect(const YV12_BUFFER_CONFIG *src, @@ -176,16 +186,18 @@ void vp9_copy_and_extend_frame_with_rect(const YV12_BUFFER_CONFIG *src, const int dst_uv_offset = ((srcy * dst->uv_stride) >> 1) + (srcx >> 1); const int srch_uv = ROUND_POWER_OF_TWO(srch, 1); const int srcw_uv = ROUND_POWER_OF_TWO(srcw, 1); + // detect nv12 colorspace + const int chroma_step = src->v_buffer - src->u_buffer == 1 ? 2 : 1; copy_and_extend_plane(src->y_buffer + src_y_offset, src->y_stride, dst->y_buffer + dst_y_offset, dst->y_stride, srcw, srch, - et_y, el_y, eb_y, er_y); + et_y, el_y, eb_y, er_y, 1); copy_and_extend_plane(src->u_buffer + src_uv_offset, src->uv_stride, dst->u_buffer + dst_uv_offset, dst->uv_stride, srcw_uv, - srch_uv, et_uv, el_uv, eb_uv, er_uv); + srch_uv, et_uv, el_uv, eb_uv, er_uv, chroma_step); copy_and_extend_plane(src->v_buffer + src_uv_offset, src->uv_stride, dst->v_buffer + dst_uv_offset, dst->uv_stride, srcw_uv, - srch_uv, et_uv, el_uv, eb_uv, er_uv); + srch_uv, et_uv, el_uv, eb_uv, er_uv, chroma_step); } diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index bd6b07a29..15aa7e60f 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -355,13 +355,14 @@ static vpx_codec_err_t validate_img(vpx_codec_alg_priv_t *ctx, switch (img->fmt) { case VPX_IMG_FMT_YV12: case VPX_IMG_FMT_I420: - case VPX_IMG_FMT_I42016: break; + case VPX_IMG_FMT_I42016: + case VPX_IMG_FMT_NV12: break; case VPX_IMG_FMT_I422: case VPX_IMG_FMT_I444: case VPX_IMG_FMT_I440: if (ctx->cfg.g_profile != (unsigned int)PROFILE_1) { ERROR( - "Invalid image format. I422, I444, I440 images are " + "Invalid image format. I422, I444, I440, NV12 images are " "not supported in profile."); } break; @@ -391,6 +392,7 @@ static vpx_codec_err_t validate_img(vpx_codec_alg_priv_t *ctx, static int get_image_bps(const vpx_image_t *img) { switch (img->fmt) { case VPX_IMG_FMT_YV12: + case VPX_IMG_FMT_NV12: case VPX_IMG_FMT_I420: return 12; case VPX_IMG_FMT_I422: return 16; case VPX_IMG_FMT_I444: return 24; diff --git a/vp9/vp9_iface_common.c b/vp9/vp9_iface_common.c index 74d08a587..8d031694d 100644 --- a/vp9/vp9_iface_common.c +++ b/vp9/vp9_iface_common.c @@ -88,8 +88,9 @@ vpx_codec_err_t image2yuvconfig(const vpx_image_t *img, yv12->y_width = img->d_w; yv12->y_height = img->d_h; - yv12->uv_width = - img->x_chroma_shift == 1 ? (1 + yv12->y_width) / 2 : yv12->y_width; + yv12->uv_width = img->x_chroma_shift == 1 || img->fmt == VPX_IMG_FMT_NV12 + ? (1 + yv12->y_width) / 2 + : yv12->y_width; yv12->uv_height = img->y_chroma_shift == 1 ? (1 + yv12->y_height) / 2 : yv12->y_height; yv12->uv_crop_width = yv12->uv_width; @@ -127,5 +128,9 @@ vpx_codec_err_t image2yuvconfig(const vpx_image_t *img, #endif // CONFIG_VP9_HIGHBITDEPTH yv12->subsampling_x = img->x_chroma_shift; yv12->subsampling_y = img->y_chroma_shift; + // When reading the data, UV are in one plane for NV12 format, thus + // x_chroma_shift is 0. After converting, UV are in separate planes, and + // subsampling_x should be set to 1. + if (img->fmt == VPX_IMG_FMT_NV12) yv12->subsampling_x = 1; return VPX_CODEC_OK; } diff --git a/vpx/src/vpx_image.c b/vpx/src/vpx_image.c index a7c6ec0ce..ff496b5d3 100644 --- a/vpx/src/vpx_image.c +++ b/vpx/src/vpx_image.c @@ -39,7 +39,8 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, /* Get sample size for this format */ switch (fmt) { case VPX_IMG_FMT_I420: - case VPX_IMG_FMT_YV12: bps = 12; break; + case VPX_IMG_FMT_YV12: + case VPX_IMG_FMT_NV12: bps = 12; break; case VPX_IMG_FMT_I422: case VPX_IMG_FMT_I440: bps = 16; break; case VPX_IMG_FMT_I444: bps = 24; break; @@ -51,6 +52,8 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, } /* Get chroma shift values for this format */ + // For VPX_IMG_FMT_NV12, xcs needs to be 0 such that UV data is all read at + // one time. switch (fmt) { case VPX_IMG_FMT_I420: case VPX_IMG_FMT_YV12: @@ -62,6 +65,7 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, switch (fmt) { case VPX_IMG_FMT_I420: + case VPX_IMG_FMT_NV12: case VPX_IMG_FMT_I440: case VPX_IMG_FMT_YV12: case VPX_IMG_FMT_I42016: @@ -173,7 +177,12 @@ int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y, data + x * bytes_per_sample + y * img->stride[VPX_PLANE_Y]; data += img->h * img->stride[VPX_PLANE_Y]; - if (!(img->fmt & VPX_IMG_FMT_UV_FLIP)) { + if (img->fmt == VPX_IMG_FMT_NV12) { + img->planes[VPX_PLANE_U] = + data + (x >> img->x_chroma_shift) + + (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; + img->planes[VPX_PLANE_V] = img->planes[VPX_PLANE_U] + 1; + } else if (!(img->fmt & VPX_IMG_FMT_UV_FLIP)) { img->planes[VPX_PLANE_U] = data + (x >> img->x_chroma_shift) * bytes_per_sample + (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; diff --git a/vpx/vpx_image.h b/vpx/vpx_image.h index 98be5966a..bc23be50c 100644 --- a/vpx/vpx_image.h +++ b/vpx/vpx_image.h @@ -43,6 +43,7 @@ typedef enum vpx_img_fmt { VPX_IMG_FMT_I422 = VPX_IMG_FMT_PLANAR | 5, VPX_IMG_FMT_I444 = VPX_IMG_FMT_PLANAR | 6, VPX_IMG_FMT_I440 = VPX_IMG_FMT_PLANAR | 7, + VPX_IMG_FMT_NV12 = VPX_IMG_FMT_PLANAR | 9, 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, diff --git a/vpxenc.c b/vpxenc.c index 50c36bedd..64288e83d 100644 --- a/vpxenc.c +++ b/vpxenc.c @@ -95,6 +95,8 @@ static const arg_def_t debugmode = ARG_DEF("D", "debug", 0, "Debug mode (makes output deterministic)"); static const arg_def_t outputfile = ARG_DEF("o", "output", 1, "Output filename"); +static const arg_def_t use_nv12 = + ARG_DEF(NULL, "nv12", 0, "Input file is NV12 "); static const arg_def_t use_yv12 = ARG_DEF(NULL, "yv12", 0, "Input file is YV12 "); static const arg_def_t use_i420 = @@ -220,7 +222,8 @@ static const arg_def_t error_resilient = static const arg_def_t lag_in_frames = ARG_DEF(NULL, "lag-in-frames", 1, "Max number of frames to lag"); -static const arg_def_t *global_args[] = { &use_yv12, +static const arg_def_t *global_args[] = { &use_nv12, + &use_yv12, &use_i420, &use_i422, &use_i444, @@ -696,6 +699,8 @@ static void parse_global_config(struct VpxEncoderConfig *global, char **argv) { global->deadline = VPX_DL_REALTIME; else if (arg_match(&arg, &use_yv12, argi)) global->color_type = YV12; + else if (arg_match(&arg, &use_nv12, argi)) + global->color_type = NV12; else if (arg_match(&arg, &use_i420, argi)) global->color_type = I420; else if (arg_match(&arg, &use_i422, argi)) @@ -1642,6 +1647,7 @@ int main(int argc, const char **argv_) { case I444: input.fmt = VPX_IMG_FMT_I444; break; case I440: input.fmt = VPX_IMG_FMT_I440; break; case YV12: input.fmt = VPX_IMG_FMT_YV12; break; + case NV12: input.fmt = VPX_IMG_FMT_NV12; break; } { diff --git a/vpxenc.h b/vpxenc.h index b780aedca..be54840f7 100644 --- a/vpxenc.h +++ b/vpxenc.h @@ -28,6 +28,7 @@ typedef enum { I444, // 4:4:4 8+ bit-depth I440, // 4:4:0 8+ bit-depth YV12, // 4:2:0 with uv flipped, only 8-bit depth + NV12, // 4:2:0 with uv interleaved } ColorInputType; struct VpxInterface; -- cgit v1.2.3