From 82dc1332af4b16d3e4ad3c4358498820637b7add Mon Sep 17 00:00:00 2001 From: Deb Mukherjee Date: Thu, 12 Jun 2014 16:53:13 -0700 Subject: [PATCH] Adds support for reading and writing 10/12-bit y4m The y4m extension used is the same as the one used in ffmpeg/x264. The patch is adapted from the highbitdepth branch. Also adds unit tests for y4m header parsing and md5 check of the raw frame data, as well as y4m writing. Change-Id: Ie2794daf6dbafd2f128464f9b9da520fc54c0dd6 --- test/md5_helper.h | 5 +- test/test-data.sha1 | 9 ++ test/test.mk | 16 +++- test/video_source.h | 9 ++ test/y4m_test.cc | 193 ++++++++++++++++++++++++++++++++++++++++ test/y4m_video_source.h | 18 ++-- tools_common.h | 1 + vp9/vp9_iface_common.h | 1 + vpx/src/vpx_image.c | 33 +++++-- vpx/vpx_codec.h | 9 ++ vpx/vpx_image.h | 5 +- vpxdec.c | 3 +- vpxenc.c | 2 + y4menc.c | 44 +++++++-- y4menc.h | 2 +- y4minput.c | 125 +++++++++++++++++++++++--- y4minput.h | 3 +- 17 files changed, 441 insertions(+), 37 deletions(-) create mode 100644 test/y4m_test.cc diff --git a/test/md5_helper.h b/test/md5_helper.h index dd446f4f6..dc9558267 100644 --- a/test/md5_helper.h +++ b/test/md5_helper.h @@ -28,10 +28,11 @@ 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 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) >> - img->x_chroma_shift : img->d_w; + const int w = (plane ? (img->d_w + img->x_chroma_shift) >> + img->x_chroma_shift : img->d_w) * bytes_per_sample; for (int y = 0; y < h; ++y) { MD5Update(&md5_, buf, w); diff --git a/test/test-data.sha1 b/test/test-data.sha1 index af1815c4b..f9c09c641 100644 --- a/test/test-data.sha1 +++ b/test/test-data.sha1 @@ -6,6 +6,15 @@ d78e2fceba5ac942246503ec8366f879c4775ca5 invalid-vp90-02.webm 2dadee5306245fa5eeb0f99652d0e17afbcba96d invalid-vp90-02.webm.res df1a1453feb3c00d7d89746c7003b4163523bff3 invalid-vp90-03.webm 8fe6fd82bf537340f586f97a7ae31fb37ccda302 invalid-vp90-03.webm.res +a432f96ff0a787268e2f94a8092ab161a18d1b06 park_joy_90p_10_420.y4m +0b194cc312c3a2e84d156a221b0a5eb615dfddc5 park_joy_90p_10_422.y4m +ff0e0a21dc2adc95b8c1b37902713700655ced17 park_joy_90p_10_444.y4m +614c32ae1eca391e867c70d19974f0d62664dd99 park_joy_90p_12_420.y4m +c92825f1ea25c5c37855083a69faac6ac4641a9e park_joy_90p_12_422.y4m +b592189b885b6cc85db55cc98512a197d73d3b34 park_joy_90p_12_444.y4m +4e0eb61e76f0684188d9bc9f3ce61f6b6b77bb2c park_joy_90p_8_420.y4m +7a193ff7dfeb96ba5f82b2afd7afa9e1fe83d947 park_joy_90p_8_422.y4m +bdb7856e6bc93599bdda05c2e773a9f22b6c6d03 park_joy_90p_8_444.y4m b1f1c3ec79114b9a0651af24ce634afb44a9a419 rush_hour_444.y4m 5184c46ddca8b1fadd16742e8500115bc8f749da vp80-00-comprehensive-001.ivf 65bf1bbbced81b97bd030f376d1b7f61a224793f vp80-00-comprehensive-002.ivf diff --git a/test/test.mk b/test/test.mk index f06e28e5f..85212d96e 100644 --- a/test/test.mk +++ b/test/test.mk @@ -15,7 +15,7 @@ LIBVPX_TEST_SRCS-yes += video_source.h ## ## Black box tests only use the public API. ## -LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ../md5_utils.h ../md5_utils.c +LIBVPX_TEST_SRCS-yes += ../md5_utils.h ../md5_utils.c LIBVPX_TEST_SRCS-$(CONFIG_DECODERS) += ivf_video_source.h LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += ../y4minput.h ../y4minput.c LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += aq_segment_test.cc @@ -42,6 +42,9 @@ LIBVPX_TEST_SRCS-yes += decode_test_driver.h LIBVPX_TEST_SRCS-yes += encode_test_driver.cc LIBVPX_TEST_SRCS-yes += encode_test_driver.h +## Y4m parsing. +LIBVPX_TEST_SRCS-$(CONFIG_ENCODERS) += y4m_test.cc ../y4menc.c ../y4menc.h + ## WebM Parsing ifeq ($(CONFIG_WEBM_IO), yes) LIBWEBM_PARSER_SRCS += ../third_party/libwebm/mkvparser.cpp @@ -134,6 +137,17 @@ endif # CONFIG_SHARED ## LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += hantro_collage_w352h288.yuv LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += hantro_odd.yuv + +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_10_420.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_10_422.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_10_444.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_12_420.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_12_422.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_12_444.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_8_420.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_8_422.y4m +LIBVPX_TEST_DATA-$(CONFIG_ENCODERS) += park_joy_90p_8_444.y4m + LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rush_hour_444.y4m LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += screendata.y4m diff --git a/test/video_source.h b/test/video_source.h index 6d1855ae3..4250cb74b 100644 --- a/test/video_source.h +++ b/test/video_source.h @@ -50,6 +50,15 @@ static FILE *OpenTestDataFile(const std::string& file_name) { return fopen(path_to_source.c_str(), "rb"); } +static FILE *OpenTestOutFile(const std::string& file_name) { + const std::string path_to_source = GetDataPath() + "/" + file_name; + return fopen(path_to_source.c_str(), "wb"); +} + +static FILE *OpenTempOutFile() { + return tmpfile(); +} + // Abstract base class for test video sources, which provide a stream of // vpx_image_t images with associated timestamps and duration. class VideoSource { diff --git a/test/y4m_test.cc b/test/y4m_test.cc new file mode 100644 index 000000000..cfa30e82a --- /dev/null +++ b/test/y4m_test.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "test/md5_helper.h" +#include "test/util.h" +#include "test/y4m_video_source.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "./vpx_config.h" +#include "./y4menc.h" + +namespace { + +using std::string; +using std::tr1::make_tuple; + +static const unsigned int kWidth = 160; +static const unsigned int kHeight = 90; +static const unsigned int kFrames = 10; + +typedef std::tr1::tuple test_entry_type; + +static const test_entry_type kY4mTestVectors[] = { + make_tuple("park_joy_90p_8_420.y4m", 8, VPX_IMG_FMT_I420, + "e5406275b9fc6bb3436c31d4a05c1cab"), + make_tuple("park_joy_90p_8_422.y4m", 8, VPX_IMG_FMT_I422, + "284a47a47133b12884ec3a14e959a0b6"), + make_tuple("park_joy_90p_8_444.y4m", 8, VPX_IMG_FMT_I444, + "90517ff33843d85de712fd4fe60dbed0"), + make_tuple("park_joy_90p_10_420.y4m", 10, VPX_IMG_FMT_I42016, + "63f21f9f717d8b8631bd2288ee87137b"), + make_tuple("park_joy_90p_10_422.y4m", 10, VPX_IMG_FMT_I42216, + "48ab51fb540aed07f7ff5af130c9b605"), + make_tuple("park_joy_90p_10_444.y4m", 10, VPX_IMG_FMT_I44416, + "067bfd75aa85ff9bae91fa3e0edd1e3e"), + make_tuple("park_joy_90p_12_420.y4m", 12, VPX_IMG_FMT_I42016, + "9e6d8f6508c6e55625f6b697bc461cef"), + make_tuple("park_joy_90p_12_422.y4m", 12, VPX_IMG_FMT_I42216, + "b239c6b301c0b835485be349ca83a7e3"), + make_tuple("park_joy_90p_12_444.y4m", 12, VPX_IMG_FMT_I44416, + "5a6481a550821dab6d0192f5c63845e9") +}; + +static void write_image_file(const vpx_image_t *img, FILE *file) { + int plane, y; + 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 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) >> + img->x_chroma_shift : img->d_w); + for (y = 0; y < h; ++y) { + fwrite(buf, bytes_per_sample, w, file); + buf += stride; + } + } +} + +class Y4mVideoSourceTest + : public ::testing::TestWithParam, + public ::libvpx_test::Y4mVideoSource { + protected: + Y4mVideoSourceTest() : Y4mVideoSource("", 0, 0) {} + + virtual ~Y4mVideoSourceTest() { + CloseSource(); + } + + virtual void Init(const std::string &file_name, int limit) { + file_name_ = file_name; + start_ = 0; + limit_ = limit; + frame_ = 0; + Begin(); + } + + // Checks y4m header information + void HeaderChecks(unsigned int bit_depth, vpx_img_fmt_t fmt) { + ASSERT_TRUE(input_file_ != NULL); + ASSERT_EQ(y4m_.pic_w, (int)kWidth); + ASSERT_EQ(y4m_.pic_h, (int)kHeight); + ASSERT_EQ(img()->d_w, kWidth); + ASSERT_EQ(img()->d_h, kHeight); + ASSERT_EQ(y4m_.bit_depth, bit_depth); + ASSERT_EQ(y4m_.vpx_fmt, fmt); + if (fmt == VPX_IMG_FMT_I420 || fmt == VPX_IMG_FMT_I42016) { + ASSERT_EQ(y4m_.bps, (int)y4m_.bit_depth * 3 / 2); + ASSERT_EQ(img()->x_chroma_shift, 1U); + ASSERT_EQ(img()->y_chroma_shift, 1U); + } + if (fmt == VPX_IMG_FMT_I422 || fmt == VPX_IMG_FMT_I42216) { + ASSERT_EQ(y4m_.bps, (int)y4m_.bit_depth * 2); + ASSERT_EQ(img()->x_chroma_shift, 1U); + ASSERT_EQ(img()->y_chroma_shift, 0U); + } + if (fmt == VPX_IMG_FMT_I444 || fmt == VPX_IMG_FMT_I44416) { + ASSERT_EQ(y4m_.bps, (int)y4m_.bit_depth * 3); + ASSERT_EQ(img()->x_chroma_shift, 0U); + ASSERT_EQ(img()->y_chroma_shift, 0U); + } + } + + // Checks MD5 of the raw frame data + void Md5Check(const string &expected_md5) { + ASSERT_TRUE(input_file_ != NULL); + libvpx_test::MD5 md5; + for (unsigned int i = start_; i < limit_; i++) { + md5.Add(img()); + Next(); + } + ASSERT_EQ(string(md5.Get()), expected_md5); + } +}; + +TEST_P(Y4mVideoSourceTest, SourceTest) { + const char *filename = GET_PARAM(0); + const unsigned int bit_depth = GET_PARAM(1); + const vpx_img_fmt format = GET_PARAM(2); + const char *md5raw = GET_PARAM(3); + + Init(filename, kFrames); + HeaderChecks(bit_depth, format); + Md5Check(md5raw); +} + +INSTANTIATE_TEST_CASE_P(C, Y4mVideoSourceTest, + ::testing::ValuesIn(kY4mTestVectors)); + +class Y4mVideoWriteTest + : public Y4mVideoSourceTest { + protected: + Y4mVideoWriteTest() : Y4mVideoSourceTest() {} + + virtual void ReplaceInputFp(FILE *input_file) { + CloseSource(); + frame_ = 0; + input_file_ = input_file; + rewind(input_file_); + ReadSourceToStart(); + } + + // Writes out a y4m file and then reads it back + void WriteY4mAndReadBack() { + ASSERT_TRUE(input_file_ != NULL); + char buf[Y4M_BUFFER_SIZE] = {0}; + const struct VpxRational framerate = {y4m_.fps_n, y4m_.fps_d}; + FILE *out_file = libvpx_test::OpenTempOutFile(); + ASSERT_TRUE(out_file != NULL); + y4m_write_file_header(buf, sizeof(buf), + kWidth, kHeight, + &framerate, y4m_.vpx_fmt, + y4m_.bit_depth); + fputs(buf, out_file); + for (unsigned int i = start_; i < limit_; i++) { + y4m_write_frame_header(buf, sizeof(buf)); + fputs(buf, out_file); + write_image_file(img(), out_file); + Next(); + } + ReplaceInputFp(out_file); + } + + virtual void Init(const std::string &file_name, int limit) { + Y4mVideoSourceTest::Init(file_name, limit); + WriteY4mAndReadBack(); + } +}; + +TEST_P(Y4mVideoWriteTest, WriteTest) { + const char *filename = GET_PARAM(0); + const unsigned int bit_depth = GET_PARAM(1); + const vpx_img_fmt format = GET_PARAM(2); + const char *md5raw = GET_PARAM(3); + + Init(filename, kFrames); + HeaderChecks(bit_depth, format); + Md5Check(md5raw); +} + +INSTANTIATE_TEST_CASE_P(C, Y4mVideoWriteTest, + ::testing::ValuesIn(kY4mTestVectors)); + +} // namespace diff --git a/test/y4m_video_source.h b/test/y4m_video_source.h index 74190432d..378e75bf8 100644 --- a/test/y4m_video_source.h +++ b/test/y4m_video_source.h @@ -38,24 +38,30 @@ class Y4mVideoSource : public VideoSource { CloseSource(); } - virtual void Begin() { + virtual void OpenSource() { CloseSource(); input_file_ = OpenTestDataFile(file_name_); ASSERT_TRUE(input_file_ != NULL) << "Input file open failed. Filename: " - << file_name_; + << file_name_; + } - y4m_input_open(&y4m_, input_file_, NULL, 0, 0); + virtual void ReadSourceToStart() { + ASSERT_TRUE(input_file_ != NULL); + ASSERT_FALSE(y4m_input_open(&y4m_, input_file_, NULL, 0, 0)); framerate_numerator_ = y4m_.fps_n; framerate_denominator_ = y4m_.fps_d; - frame_ = 0; for (unsigned int i = 0; i < start_; i++) { - Next(); + Next(); } - FillFrame(); } + virtual void Begin() { + OpenSource(); + ReadSourceToStart(); + } + virtual void Next() { ++frame_; FillFrame(); diff --git a/tools_common.h b/tools_common.h index e033de23d..6a9f4f7f0 100644 --- a/tools_common.h +++ b/tools_common.h @@ -90,6 +90,7 @@ struct VpxInputContext { uint32_t width; uint32_t height; vpx_img_fmt_t fmt; + vpx_bit_depth_t bit_depth; int only_i420; uint32_t fourcc; struct VpxRational framerate; diff --git a/vp9/vp9_iface_common.h b/vp9/vp9_iface_common.h index d60883cc2..b90c37b91 100644 --- a/vp9/vp9_iface_common.h +++ b/vp9/vp9_iface_common.h @@ -31,6 +31,7 @@ static void yuvconfig2image(vpx_image_t *img, const YV12_BUFFER_CONFIG *yv12, img->fmt = VPX_IMG_FMT_I420; bps = 12; } + img->bit_depth = 8; img->w = yv12->y_stride; img->h = ALIGN_POWER_OF_TWO(yv12->y_height + 2 * VP9_ENC_BORDER_IN_PIXELS, 3); img->d_w = yv12->y_crop_width; diff --git a/vpx/src/vpx_image.c b/vpx/src/vpx_image.c index 36eda958e..dc8fcbc3b 100644 --- a/vpx/src/vpx_image.c +++ b/vpx/src/vpx_image.c @@ -40,13 +40,13 @@ static void img_buf_free(void *memblk) { } } -static vpx_image_t *img_alloc_helper(vpx_image_t *img, - vpx_img_fmt_t fmt, - unsigned int d_w, - unsigned int d_h, - unsigned int buf_align, - unsigned int stride_align, - unsigned char *img_data) { +static vpx_image_t *img_alloc_helper(vpx_image_t *img, + vpx_img_fmt_t fmt, + unsigned int d_w, + unsigned int d_h, + unsigned int buf_align, + unsigned int stride_align, + unsigned char *img_data) { unsigned int h, w, s, xcs, ycs, bps; int align; @@ -94,6 +94,21 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, case VPX_IMG_FMT_VPXYV12: bps = 12; break; + case VPX_IMG_FMT_I422: + bps = 16; + break; + case VPX_IMG_FMT_I444: + bps = 24; + break; + case VPX_IMG_FMT_I42016: + bps = 24; + break; + case VPX_IMG_FMT_I42216: + bps = 32; + break; + case VPX_IMG_FMT_I44416: + bps = 48; + break; default: bps = 16; break; @@ -105,6 +120,9 @@ static vpx_image_t *img_alloc_helper(vpx_image_t *img, case VPX_IMG_FMT_YV12: case VPX_IMG_FMT_VPXI420: case VPX_IMG_FMT_VPXYV12: + case VPX_IMG_FMT_I422: + case VPX_IMG_FMT_I42016: + case VPX_IMG_FMT_I42216: xcs = 1; break; default: @@ -156,6 +174,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->w = w; img->h = h; img->x_chroma_shift = xcs; diff --git a/vpx/vpx_codec.h b/vpx/vpx_codec.h index 03d2dec92..45e702354 100644 --- a/vpx/vpx_codec.h +++ b/vpx/vpx_codec.h @@ -212,6 +212,15 @@ extern "C" { vpx_codec_priv_t *priv; /**< Algorithm private storage */ } vpx_codec_ctx_t; + /*!\brief Bit depth for codec + * * + * 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_bit_depth_t; /* * Library Version Number Interface diff --git a/vpx/vpx_image.h b/vpx/vpx_image.h index d45b003c6..7b04b70a1 100644 --- a/vpx/vpx_image.h +++ b/vpx/vpx_image.h @@ -103,8 +103,9 @@ extern "C" { vpx_img_fmt_t fmt; /**< Image Format */ /* Image storage dimensions */ - unsigned int w; /**< Stored image width */ - unsigned int h; /**< Stored image height */ + unsigned int w; /**< Stored image width */ + unsigned int h; /**< Stored image height */ + unsigned int bit_depth; /**< Stored image bit-depth */ /* Image display dimensions */ unsigned int d_w; /**< Displayed image width */ diff --git a/vpxdec.c b/vpxdec.c index 127e65f89..33d3b69da 100644 --- a/vpxdec.c +++ b/vpxdec.c @@ -895,7 +895,8 @@ int main_loop(int argc, const char **argv_) { len = y4m_write_file_header(buf, sizeof(buf), vpx_input_ctx.width, vpx_input_ctx.height, - &vpx_input_ctx.framerate, img->fmt); + &vpx_input_ctx.framerate, + img->fmt, 8); if (do_md5) { MD5Update(&md5_ctx, (md5byte *)buf, (unsigned int)len); } else { diff --git a/vpxenc.c b/vpxenc.c index d46a83eb0..fce68075d 100644 --- a/vpxenc.c +++ b/vpxenc.c @@ -756,6 +756,7 @@ void open_input_file(struct VpxInputContext *input) { input->framerate.numerator = input->y4m.fps_n; input->framerate.denominator = input->y4m.fps_d; input->fmt = input->y4m.vpx_fmt; + input->bit_depth = input->y4m.bit_depth; } else fatal("Unsupported Y4M stream."); } else if (input->detect.buf_read == 4 && fourcc_is_ivf(input->detect.buf)) { @@ -1533,6 +1534,7 @@ int main(int argc, const char **argv_) { input.framerate.numerator = 30; input.framerate.denominator = 1; input.only_i420 = 1; + input.bit_depth = 0; /* First parse the global configuration values, because we want to apply * other parameters on top of the default configuration provided by the diff --git a/y4menc.c b/y4menc.c index 8b1c95e2b..9211452a4 100644 --- a/y4menc.c +++ b/y4menc.c @@ -8,16 +8,48 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include "./y4menc.h" int y4m_write_file_header(char *buf, size_t len, int width, int height, const struct VpxRational *framerate, - vpx_img_fmt_t fmt) { - const char *const color = fmt == VPX_IMG_FMT_444A ? "C444alpha\n" : - fmt == VPX_IMG_FMT_I444 ? "C444\n" : - fmt == VPX_IMG_FMT_I422 ? "C422\n" : - "C420jpeg\n"; - + vpx_img_fmt_t fmt, unsigned int bit_depth) { + const char *color; + switch (bit_depth) { + case 8: + color = fmt == VPX_IMG_FMT_444A ? "C444alpha\n" : + fmt == VPX_IMG_FMT_I444 ? "C444\n" : + fmt == VPX_IMG_FMT_I422 ? "C422\n" : + "C420jpeg\n"; + break; + case 9: + color = fmt == VPX_IMG_FMT_I44416 ? "C444p9 XYSCSS=444P9\n" : + fmt == VPX_IMG_FMT_I42216 ? "C422p9 XYSCSS=422P9\n" : + "C420p9 XYSCSS=420P9\n"; + break; + case 10: + color = fmt == VPX_IMG_FMT_I44416 ? "C444p10 XYSCSS=444P10\n" : + fmt == VPX_IMG_FMT_I42216 ? "C422p10 XYSCSS=422P10\n" : + "C420p10 XYSCSS=420P10\n"; + break; + case 12: + color = fmt == VPX_IMG_FMT_I44416 ? "C444p12 XYSCSS=444P12\n" : + fmt == VPX_IMG_FMT_I42216 ? "C422p12 XYSCSS=422P12\n" : + "C420p12 XYSCSS=420P12\n"; + break; + case 14: + color = fmt == VPX_IMG_FMT_I44416 ? "C444p14 XYSCSS=444P14\n" : + fmt == VPX_IMG_FMT_I42216 ? "C422p14 XYSCSS=422P14\n" : + "C420p14 XYSCSS=420P14\n"; + break; + case 16: + color = fmt == VPX_IMG_FMT_I44416 ? "C444p16 XYSCSS=444P16\n" : + fmt == VPX_IMG_FMT_I42216 ? "C422p16 XYSCSS=422P16\n" : + "C420p16 XYSCSS=420P16\n"; + break; + default: + assert(0); + } return snprintf(buf, len, "YUV4MPEG2 W%u H%u F%u:%u I%c %s", width, height, framerate->numerator, framerate->denominator, 'p', color); } diff --git a/y4menc.h b/y4menc.h index 0fabf56eb..69d590413 100644 --- a/y4menc.h +++ b/y4menc.h @@ -23,7 +23,7 @@ extern "C" { int y4m_write_file_header(char *buf, size_t len, int width, int height, const struct VpxRational *framerate, - vpx_img_fmt_t fmt); + vpx_img_fmt_t fmt, unsigned int bit_depth); int y4m_write_frame_header(char *buf, size_t len); #ifdef __cplusplus diff --git a/y4minput.c b/y4minput.c index 90c5310a1..b005b71d3 100644 --- a/y4minput.c +++ b/y4minput.c @@ -737,15 +737,52 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, return -1; } _y4m->vpx_fmt = VPX_IMG_FMT_I420; - _y4m->vpx_bps = 12; + _y4m->bps = 12; + _y4m->bit_depth = 8; if (strcmp(_y4m->chroma_type, "420") == 0 || strcmp(_y4m->chroma_type, "420jpeg") == 0) { _y4m->src_c_dec_h = _y4m->dst_c_dec_h = _y4m->src_c_dec_v = _y4m->dst_c_dec_v = 2; _y4m->dst_buf_read_sz = _y4m->pic_w * _y4m->pic_h + 2 * ((_y4m->pic_w + 1) / 2) * ((_y4m->pic_h + 1) / 2); - /*Natively supported: no conversion required.*/ + /* Natively supported: no conversion required. */ _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; _y4m->convert = y4m_convert_null; + } else if (strcmp(_y4m->chroma_type, "420p10") == 0) { + _y4m->src_c_dec_h = 2; + _y4m->dst_c_dec_h = 2; + _y4m->src_c_dec_v = 2; + _y4m->dst_c_dec_v = 2; + _y4m->dst_buf_read_sz = 2 * (_y4m->pic_w * _y4m->pic_h + + 2 * ((_y4m->pic_w + 1) / 2) * + ((_y4m->pic_h + 1) / 2)); + /* Natively supported: no conversion required. */ + _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; + _y4m->convert = y4m_convert_null; + _y4m->bit_depth = 10; + _y4m->bps = 15; + _y4m->vpx_fmt = VPX_IMG_FMT_I42016; + if (only_420) { + fprintf(stderr, "Unsupported conversion from 420p10 to 420jpeg\n"); + return -1; + } + } else if (strcmp(_y4m->chroma_type, "420p12") == 0) { + _y4m->src_c_dec_h = 2; + _y4m->dst_c_dec_h = 2; + _y4m->src_c_dec_v = 2; + _y4m->dst_c_dec_v = 2; + _y4m->dst_buf_read_sz = 2 * (_y4m->pic_w * _y4m->pic_h + + 2 * ((_y4m->pic_w + 1) / 2) * + ((_y4m->pic_h + 1) / 2)); + /* Natively supported: no conversion required. */ + _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; + _y4m->convert = y4m_convert_null; + _y4m->bit_depth = 12; + _y4m->bps = 18; + _y4m->vpx_fmt = VPX_IMG_FMT_I42016; + if (only_420) { + fprintf(stderr, "Unsupported conversion from 420p12 to 420jpeg\n"); + return -1; + } } else if (strcmp(_y4m->chroma_type, "420mpeg2") == 0) { _y4m->src_c_dec_h = _y4m->dst_c_dec_h = _y4m->src_c_dec_v = _y4m->dst_c_dec_v = 2; _y4m->dst_buf_read_sz = _y4m->pic_w * _y4m->pic_h; @@ -786,7 +823,7 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, _y4m->convert = y4m_convert_422_420jpeg; } else { _y4m->vpx_fmt = VPX_IMG_FMT_I422; - _y4m->vpx_bps = 16; + _y4m->bps = 16; _y4m->dst_c_dec_h = _y4m->src_c_dec_h; _y4m->dst_c_dec_v = _y4m->src_c_dec_v; _y4m->dst_buf_read_sz = _y4m->pic_w * _y4m->pic_h @@ -794,7 +831,39 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, /*Natively supported: no conversion required.*/ _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; _y4m->convert = y4m_convert_null; - } + } + } else if (strcmp(_y4m->chroma_type, "422p10") == 0) { + _y4m->src_c_dec_h = 2; + _y4m->src_c_dec_v = 1; + _y4m->vpx_fmt = VPX_IMG_FMT_I42216; + _y4m->bps = 20; + _y4m->bit_depth = 10; + _y4m->dst_c_dec_h = _y4m->src_c_dec_h; + _y4m->dst_c_dec_v = _y4m->src_c_dec_v; + _y4m->dst_buf_read_sz = 2 * (_y4m->pic_w * _y4m->pic_h + + 2 * ((_y4m->pic_w + 1) / 2) * _y4m->pic_h); + _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; + _y4m->convert = y4m_convert_null; + if (only_420) { + fprintf(stderr, "Unsupported conversion from 422p10 to 420jpeg\n"); + return -1; + } + } else if (strcmp(_y4m->chroma_type, "422p12") == 0) { + _y4m->src_c_dec_h = 2; + _y4m->src_c_dec_v = 1; + _y4m->vpx_fmt = VPX_IMG_FMT_I42216; + _y4m->bps = 24; + _y4m->bit_depth = 12; + _y4m->dst_c_dec_h = _y4m->src_c_dec_h; + _y4m->dst_c_dec_v = _y4m->src_c_dec_v; + _y4m->dst_buf_read_sz = 2 * (_y4m->pic_w * _y4m->pic_h + + 2 * ((_y4m->pic_w + 1) / 2) * _y4m->pic_h); + _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; + _y4m->convert = y4m_convert_null; + if (only_420) { + fprintf(stderr, "Unsupported conversion from 422p12 to 420jpeg\n"); + return -1; + } } else if (strcmp(_y4m->chroma_type, "411") == 0) { _y4m->src_c_dec_h = 4; _y4m->dst_c_dec_h = 2; @@ -823,7 +892,7 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, _y4m->convert = y4m_convert_444_420jpeg; } else { _y4m->vpx_fmt = VPX_IMG_FMT_I444; - _y4m->vpx_bps = 24; + _y4m->bps = 24; _y4m->dst_c_dec_h = _y4m->src_c_dec_h; _y4m->dst_c_dec_v = _y4m->src_c_dec_v; _y4m->dst_buf_read_sz = 3 * _y4m->pic_w * _y4m->pic_h; @@ -831,6 +900,36 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; _y4m->convert = y4m_convert_null; } + } else if (strcmp(_y4m->chroma_type, "444p10") == 0) { + _y4m->src_c_dec_h = 1; + _y4m->src_c_dec_v = 1; + _y4m->vpx_fmt = VPX_IMG_FMT_I44416; + _y4m->bps = 30; + _y4m->bit_depth = 10; + _y4m->dst_c_dec_h = _y4m->src_c_dec_h; + _y4m->dst_c_dec_v = _y4m->src_c_dec_v; + _y4m->dst_buf_read_sz = 2 * 3 * _y4m->pic_w * _y4m->pic_h; + _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; + _y4m->convert = y4m_convert_null; + if (only_420) { + fprintf(stderr, "Unsupported conversion from 444p10 to 420jpeg\n"); + return -1; + } + } else if (strcmp(_y4m->chroma_type, "444p12") == 0) { + _y4m->src_c_dec_h = 1; + _y4m->src_c_dec_v = 1; + _y4m->vpx_fmt = VPX_IMG_FMT_I44416; + _y4m->bps = 36; + _y4m->bit_depth = 12; + _y4m->dst_c_dec_h = _y4m->src_c_dec_h; + _y4m->dst_c_dec_v = _y4m->src_c_dec_v; + _y4m->dst_buf_read_sz = 2 * 3 * _y4m->pic_w * _y4m->pic_h; + _y4m->aux_buf_sz = _y4m->aux_buf_read_sz = 0; + _y4m->convert = y4m_convert_null; + if (only_420) { + fprintf(stderr, "Unsupported conversion from 444p12 to 420jpeg\n"); + return -1; + } } else if (strcmp(_y4m->chroma_type, "444alpha") == 0) { _y4m->src_c_dec_h = 1; _y4m->src_c_dec_v = 1; @@ -847,7 +946,7 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, _y4m->convert = y4m_convert_444_420jpeg; } else { _y4m->vpx_fmt = VPX_IMG_FMT_444A; - _y4m->vpx_bps = 32; + _y4m->bps = 32; _y4m->dst_c_dec_h = _y4m->src_c_dec_h; _y4m->dst_c_dec_v = _y4m->src_c_dec_v; _y4m->dst_buf_read_sz = 4 * _y4m->pic_w * _y4m->pic_h; @@ -871,7 +970,10 @@ int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, _y4m->dst_buf_sz = _y4m->pic_w * _y4m->pic_h + 2 * ((_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h) * ((_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v); - _y4m->dst_buf = (unsigned char *)malloc(_y4m->dst_buf_sz); + if (_y4m->bit_depth == 8) + _y4m->dst_buf = (unsigned char *)malloc(_y4m->dst_buf_sz); + else + _y4m->dst_buf = (unsigned char *)malloc(2 * _y4m->dst_buf_sz); _y4m->aux_buf = (unsigned char *)malloc(_y4m->aux_buf_sz); return 0; } @@ -887,6 +989,7 @@ int y4m_input_fetch_frame(y4m_input *_y4m, FILE *_fin, vpx_image_t *_img) { int c_w; int c_h; int c_sz; + int bytes_per_sample = _y4m->bit_depth > 8 ? 2 : 1; /*Read and skip the frame header.*/ if (!file_read(frame, 6, _fin)) return 0; if (memcmp(frame, "FRAME", 5)) { @@ -924,14 +1027,16 @@ int y4m_input_fetch_frame(y4m_input *_y4m, FILE *_fin, vpx_image_t *_img) { _img->h = _img->d_h = _y4m->pic_h; _img->x_chroma_shift = _y4m->dst_c_dec_h >> 1; _img->y_chroma_shift = _y4m->dst_c_dec_v >> 1; - _img->bps = _y4m->vpx_bps; + _img->bps = _y4m->bps; /*Set up the buffer pointers.*/ - pic_sz = _y4m->pic_w * _y4m->pic_h; + pic_sz = _y4m->pic_w * _y4m->pic_h * bytes_per_sample; c_w = (_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h; + c_w *= bytes_per_sample; c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v; c_sz = c_w * c_h; - _img->stride[PLANE_Y] = _img->stride[PLANE_ALPHA] = _y4m->pic_w; + _img->stride[PLANE_Y] = _img->stride[PLANE_ALPHA] = + _y4m->pic_w * bytes_per_sample; _img->stride[PLANE_U] = _img->stride[PLANE_V] = c_w; _img->planes[PLANE_Y] = _y4m->dst_buf; _img->planes[PLANE_U] = _y4m->dst_buf + pic_sz; diff --git a/y4minput.h b/y4minput.h index d53eb651b..356cebbcf 100644 --- a/y4minput.h +++ b/y4minput.h @@ -58,7 +58,8 @@ struct y4m_input { unsigned char *dst_buf; unsigned char *aux_buf; enum vpx_img_fmt vpx_fmt; - int vpx_bps; + int bps; + unsigned int bit_depth; }; int y4m_input_open(y4m_input *_y4m, FILE *_fin, char *_skip, int _nskip, -- 2.40.0