coding_path_sync.cc (5741B)
1 /* 2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved. 3 * 4 * This source code is subject to the terms of the BSD 2 Clause License and 5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 6 * was not distributed with this source code in the LICENSE file, you can 7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open 8 * Media Patent License 1.0 was not distributed with this source code in the 9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent. 10 */ 11 12 #include <vector> 13 #include "gtest/gtest.h" 14 #include "test/acm_random.h" 15 16 #include "config/aom_config.h" 17 18 #include "aom/aomcx.h" 19 #include "aom/aomdx.h" 20 #include "aom/aom_encoder.h" 21 #include "aom/aom_decoder.h" 22 23 #define NELEMENTS(x) static_cast<int>(sizeof(x) / sizeof(x[0])) 24 25 using libaom_test::ACMRandom; 26 namespace { 27 28 class CompressedSource { 29 public: 30 explicit CompressedSource(int seed) : rnd_(seed), frame_count_(0) { 31 aom_codec_iface_t *algo = aom_codec_av1_cx(); 32 33 aom_codec_enc_cfg_t cfg; 34 #if CONFIG_REALTIME_ONLY 35 aom_codec_enc_config_default(algo, &cfg, 1); 36 #else 37 aom_codec_enc_config_default(algo, &cfg, 0); 38 #endif 39 40 // force the quantizer, to reduce the sensitivity on encoding choices. 41 // e.g, we don't want this test to break when the rate control is modified. 42 { 43 const int max_q = cfg.rc_max_quantizer; 44 const int min_q = cfg.rc_min_quantizer; 45 const int q = rnd_.PseudoUniform(max_q - min_q + 1) + min_q; 46 47 cfg.rc_end_usage = AOM_Q; 48 cfg.rc_max_quantizer = q; 49 cfg.rc_min_quantizer = q; 50 } 51 52 // choose the picture size 53 { 54 width_ = rnd_.PseudoUniform(kWidth - 8) + 8; 55 height_ = rnd_.PseudoUniform(kHeight - 8) + 8; 56 } 57 58 // choose the chroma subsampling 59 { 60 const aom_img_fmt_t fmts[] = { 61 AOM_IMG_FMT_I420, 62 AOM_IMG_FMT_I422, 63 AOM_IMG_FMT_I444, 64 }; 65 66 format_ = fmts[rnd_.PseudoUniform(NELEMENTS(fmts))]; 67 } 68 69 cfg.g_w = width_; 70 cfg.g_h = height_; 71 cfg.g_lag_in_frames = 0; 72 if (format_ == AOM_IMG_FMT_I420) 73 cfg.g_profile = 0; 74 else if (format_ == AOM_IMG_FMT_I444) 75 cfg.g_profile = 1; 76 else if (format_ == AOM_IMG_FMT_I422) 77 cfg.g_profile = 2; 78 79 aom_codec_enc_init(&enc_, algo, &cfg, 0); 80 } 81 82 ~CompressedSource() { aom_codec_destroy(&enc_); } 83 84 const aom_codec_cx_pkt_t *ReadFrame() { 85 uint8_t buf[kWidth * kHeight * 3] = { 0 }; 86 87 // render regular pattern 88 const int period = rnd_.Rand8() % 32 + 1; 89 const int phase = rnd_.Rand8() % period; 90 91 const int val_a = rnd_.Rand8(); 92 const int val_b = rnd_.Rand8(); 93 94 for (int i = 0; i < (int)sizeof buf; ++i) 95 buf[i] = (i + phase) % period < period / 2 ? val_a : val_b; 96 97 aom_image_t img; 98 aom_img_wrap(&img, format_, width_, height_, 0, buf); 99 aom_codec_encode(&enc_, &img, frame_count_++, 1, 0); 100 101 aom_codec_iter_t iter = nullptr; 102 103 const aom_codec_cx_pkt_t *pkt = nullptr; 104 105 do { 106 pkt = aom_codec_get_cx_data(&enc_, &iter); 107 } while (pkt && pkt->kind != AOM_CODEC_CX_FRAME_PKT); 108 109 return pkt; 110 } 111 112 private: 113 static const int kWidth = 128; 114 static const int kHeight = 128; 115 116 ACMRandom rnd_; 117 aom_img_fmt_t format_; 118 aom_codec_ctx_t enc_; 119 int frame_count_; 120 int width_, height_; 121 }; 122 123 // lowers an aom_image_t to an easily comparable/printable form 124 std::vector<uint16_t> Serialize(const aom_image_t *img) { 125 std::vector<uint16_t> bytes; 126 bytes.reserve(img->d_w * img->d_h * 3); 127 for (int plane = 0; plane < 3; ++plane) { 128 const int w = aom_img_plane_width(img, plane); 129 const int h = aom_img_plane_height(img, plane); 130 131 for (int r = 0; r < h; ++r) { 132 for (int c = 0; c < w; ++c) { 133 const unsigned char *row = img->planes[plane] + r * img->stride[plane]; 134 if (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) { 135 const uint16_t *row16 = reinterpret_cast<const uint16_t *>(row); 136 bytes.push_back(row16[c]); 137 } else { 138 bytes.push_back(row[c]); 139 } 140 } 141 } 142 } 143 144 return bytes; 145 } 146 147 class Decoder { 148 public: 149 explicit Decoder(int allowLowbitdepth) { 150 aom_codec_iface_t *algo = aom_codec_av1_dx(); 151 152 aom_codec_dec_cfg_t cfg = aom_codec_dec_cfg_t(); 153 cfg.allow_lowbitdepth = allowLowbitdepth; 154 155 aom_codec_dec_init(&dec_, algo, &cfg, 0); 156 } 157 158 ~Decoder() { aom_codec_destroy(&dec_); } 159 160 std::vector<uint16_t> decode(const aom_codec_cx_pkt_t *pkt) { 161 aom_codec_decode(&dec_, static_cast<uint8_t *>(pkt->data.frame.buf), 162 pkt->data.frame.sz, nullptr); 163 164 aom_codec_iter_t iter = nullptr; 165 return Serialize(aom_codec_get_frame(&dec_, &iter)); 166 } 167 168 private: 169 aom_codec_ctx_t dec_; 170 }; 171 172 // Try to reveal a mismatch between LBD and HBD coding paths. 173 TEST(CodingPathSync, SearchForHbdLbdMismatch) { 174 const int count_tests = 10; 175 for (int i = 0; i < count_tests; ++i) { 176 Decoder dec_hbd(0); 177 Decoder dec_lbd(1); 178 179 CompressedSource enc(i); 180 181 for (int k = 0; k < 3; ++k) { 182 const aom_codec_cx_pkt_t *frame = enc.ReadFrame(); 183 184 std::vector<uint16_t> lbd_yuv = dec_lbd.decode(frame); 185 std::vector<uint16_t> hbd_yuv = dec_hbd.decode(frame); 186 187 ASSERT_EQ(lbd_yuv, hbd_yuv); 188 } 189 } 190 } 191 192 TEST(CodingPathSyncLarge, SearchForHbdLbdMismatchLarge) { 193 const int count_tests = 100; 194 const int seed = 1234; 195 for (int i = 0; i < count_tests; ++i) { 196 Decoder dec_hbd(0); 197 Decoder dec_lbd(1); 198 199 CompressedSource enc(seed + i); 200 201 for (int k = 0; k < 5; ++k) { 202 const aom_codec_cx_pkt_t *frame = enc.ReadFrame(); 203 204 std::vector<uint16_t> lbd_yuv = dec_lbd.decode(frame); 205 std::vector<uint16_t> hbd_yuv = dec_hbd.decode(frame); 206 207 ASSERT_EQ(lbd_yuv, hbd_yuv); 208 } 209 } 210 } 211 212 } // namespace