* changes: Add coding_data_bit_size to EncodeFrameResult Pass in infile_path to SimpleEncode() Add SimpleEncode::EncodeFrameWithQuantizeIndex()
Unified · Split
| @@ -1,21 +1,28 @@ | ||
| 1 | +#include <math.h> | |
| 1 | 2 | #include <memory> |
| 2 | 3 | #include <vector> |
| 3 | 4 | #include "third_party/googletest/src/include/gtest/gtest.h" |
| 4 | 5 | #include "vp9/simple_encode.h" |
| 5 | 6 | |
| 6 | 7 | namespace { |
| 8 | +const int w = 352; | |
| 9 | +const int h = 288; | |
| 10 | +const int frame_rate_num = 30; | |
| 11 | +const int frame_rate_den = 1; | |
| 12 | +const int target_bitrate = 1000; | |
| 13 | +const int num_frames = 17; | |
| 14 | +// TODO(angiebird): Figure out how to upload test video to our codebase | |
| 15 | +const char infile_path[] = "bus_352x288_420_f20_b8.yuv"; | |
| 16 | + | |
| 17 | +static double get_bit_rate_in_kpbs(size_t bit_size, int num_frames, | |
| 18 | + int frame_rate_num, int frame_rate_den) { | |
| 19 | + return static_cast<double>(bit_size) / num_frames * frame_rate_num / | |
| 20 | + frame_rate_den / 1000.; | |
| 21 | +} | |
| 7 | 22 | |
| 8 | 23 | TEST(SimpleEncode, ComputeFirstPassStats) { |
| 9 | − int w = 352; | |
| 10 | − int h = 288; | |
| 11 | − int frame_rate_num = 30; | |
| 12 | − int frame_rate_den = 1; | |
| 13 | − int target_bitrate = 200; | |
| 14 | − int num_frames = 17; | |
| 15 | − // TODO(angiebird): Figure out how to upload test video to our codebase | |
| 16 | − FILE *file = fopen("bus_352x288_420_f20_b8.yuv", "r"); | |
| 17 | 24 | SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, |
| 18 | − target_bitrate, num_frames, file); | |
| 25 | + target_bitrate, num_frames, infile_path); | |
| 19 | 26 | simple_encode.ComputeFirstPassStats(); |
| 20 | 27 | std::vector<std::vector<double>> frame_stats = |
| 21 | 28 | simple_encode.ObserveFirstPassStats(); |
| @@ -34,32 +41,16 @@ TEST(SimpleEncode, ComputeFirstPassStats) { · 12 unmodified lines | ||
| 34 | 41 | } |
| 35 | 42 | |
| 36 | 43 | TEST(SimpleEncode, GetCodingFrameNum) { |
| 37 | − int w = 352; | |
| 38 | − int h = 288; | |
| 39 | − int frame_rate_num = 30; | |
| 40 | − int frame_rate_den = 1; | |
| 41 | − int target_bitrate = 200; | |
| 42 | − int num_frames = 17; | |
| 43 | − // TODO(angiebird): Figure out how to upload test video to our codebase | |
| 44 | − FILE *file = fopen("bus_352x288_420_f20_b8.yuv", "r"); | |
| 45 | 44 | SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, |
| 46 | − target_bitrate, num_frames, file); | |
| 45 | + target_bitrate, num_frames, infile_path); | |
| 47 | 46 | simple_encode.ComputeFirstPassStats(); |
| 48 | 47 | int num_coding_frames = simple_encode.GetCodingFrameNum(); |
| 49 | 48 | EXPECT_EQ(num_coding_frames, 19); |
| 50 | 49 | } |
| 51 | 50 | |
| 52 | 51 | TEST(SimpleEncode, EncodeFrame) { |
| 53 | − int w = 352; | |
| 54 | − int h = 288; | |
| 55 | − int frame_rate_num = 30; | |
| 56 | − int frame_rate_den = 1; | |
| 57 | − int target_bitrate = 1000; | |
| 58 | − int num_frames = 17; | |
| 59 | − // TODO(angiebird): Figure out how to upload test video to our codebase | |
| 60 | − FILE *file = fopen("bus_352x288_420_f20_b8.yuv", "r"); | |
| 61 | 52 | SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, |
| 62 | − target_bitrate, num_frames, file); | |
| 53 | + target_bitrate, num_frames, infile_path); | |
| 63 | 54 | simple_encode.ComputeFirstPassStats(); |
| 64 | 55 | int num_coding_frames = simple_encode.GetCodingFrameNum(); |
| 65 | 56 | EXPECT_GE(num_coding_frames, num_frames); |
| @@ -68,6 +59,7 @@ TEST(SimpleEncode, EncodeFrame) { · 2 unmodified lines | ||
| 68 | 59 | int ref_num_alternate_refereces = num_coding_frames - num_frames; |
| 69 | 60 | int num_alternate_refereces = 0; |
| 70 | 61 | simple_encode.StartEncode(); |
| 62 | + size_t total_data_bit_size = 0; | |
| 71 | 63 | for (int i = 0; i < num_coding_frames; ++i) { |
| 72 | 64 | EncodeFrameResult encode_frame_result; |
| 73 | 65 | if (i == 0) { |
| @@ -88,8 +80,28 @@ TEST(SimpleEncode, EncodeFrame) { · 14 unmodified lines | ||
| 88 | 80 | EXPECT_GE(encode_frame_result.psnr, 34) |
| 89 | 81 | << "The psnr is supposed to be greater than 34 given the " |
| 90 | 82 | "target_bitrate 1000 kbps"; |
| 83 | + total_data_bit_size += encode_frame_result.coding_data_bit_size; | |
| 91 | 84 | } |
| 92 | 85 | EXPECT_EQ(num_alternate_refereces, ref_num_alternate_refereces); |
| 86 | + double bitrate = get_bit_rate_in_kpbs(total_data_bit_size, num_frames, | |
| 87 | + frame_rate_num, frame_rate_den); | |
| 88 | + EXPECT_LE(fabs(target_bitrate - bitrate), 150); | |
| 89 | + simple_encode.EndEncode(); | |
| 90 | +} | |
| 91 | + | |
| 92 | +TEST(SimpleEncode, EncodeFrameWithQuantizeIndex) { | |
| 93 | + SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, | |
| 94 | + target_bitrate, num_frames, infile_path); | |
| 95 | + simple_encode.ComputeFirstPassStats(); | |
| 96 | + int num_coding_frames = simple_encode.GetCodingFrameNum(); | |
| 97 | + simple_encode.StartEncode(); | |
| 98 | + for (int i = 0; i < num_coding_frames; ++i) { | |
| 99 | + int assigned_quantize_index = 100 + i; | |
| 100 | + EncodeFrameResult encode_frame_result; | |
| 101 | + simple_encode.EncodeFrameWithQuantizeIndex(&encode_frame_result, | |
| 102 | + assigned_quantize_index); | |
| 103 | + EXPECT_EQ(encode_frame_result.quantize_index, assigned_quantize_index); | |
| 104 | + } | |
| 93 | 105 | simple_encode.EndEncode(); |
| 94 | 106 | } |
| 95 | 107 | |
| @@ -2647,6 +2647,10 @@ VP9_COMP *vp9_create_compressor(const VP9EncoderConfig *oxcf, · 2646 unmodified lines | ||
| 2647 | 2647 | |
| 2648 | 2648 | cm->error.setjmp = 0; |
| 2649 | 2649 | |
| 2650 | +#if CONFIG_RATE_CTRL | |
| 2651 | + encode_command_init(&cpi->encode_command); | |
| 2652 | +#endif | |
| 2653 | + | |
| 2650 | 2654 | return cpi; |
| 2651 | 2655 | } |
| 2652 | 2656 | |
| @@ -4282,6 +4286,14 @@ static void encode_with_recode_loop(VP9_COMP *cpi, size_t *size, · 1629 unmodified lines | ||
| 4282 | 4286 | vp9_scale_references(cpi); |
| 4283 | 4287 | } |
| 4284 | 4288 | |
| 4289 | +#if CONFIG_RATE_CTRL | |
| 4290 | + // TODO(angiebird): This is a hack for making sure the encoder use the | |
| 4291 | + // external_quantize_index exactly. Avoid this kind of hack later. | |
| 4292 | + if (cpi->encode_command.use_external_quantize_index) { | |
| 4293 | + q = cpi->encode_command.external_quantize_index; | |
| 4294 | + } | |
| 4295 | +#endif | |
| 4296 | + | |
| 4285 | 4297 | vp9_set_quantizer(cm, q); |
| 4286 | 4298 | |
| 4287 | 4299 | if (loop_count == 0) setup_frame(cpi); |
| @@ -4307,6 +4319,13 @@ static void encode_with_recode_loop(VP9_COMP *cpi, size_t *size, · 19 unmodified lines | ||
| 4307 | 4319 | // update_base_skip_probs(cpi); |
| 4308 | 4320 | |
| 4309 | 4321 | vpx_clear_system_state(); |
| 4322 | +#if CONFIG_RATE_CTRL | |
| 4323 | + // TODO(angiebird): This is a hack for making sure the encoder use the | |
| 4324 | + // external_quantize_index exactly. Avoid this kind of hack later. | |
| 4325 | + if (cpi->encode_command.use_external_quantize_index) { | |
| 4326 | + break; | |
| 4327 | + } | |
| 4328 | +#endif | |
| 4310 | 4329 | |
| 4311 | 4330 | // Dummy pack of the bitstream using up to date stats to get an |
| 4312 | 4331 | // accurate estimate of output frame size to determine if we need |
| @@ -516,6 +516,31 @@ typedef struct KMEANS_DATA { · 515 unmodified lines | ||
| 516 | 516 | int group_idx; |
| 517 | 517 | } KMEANS_DATA; |
| 518 | 518 | |
| 519 | +#if CONFIG_RATE_CTRL | |
| 520 | +typedef struct ENCODE_COMMAND { | |
| 521 | + int use_external_quantize_index; | |
| 522 | + int external_quantize_index; | |
| 523 | +} ENCODE_COMMAND; | |
| 524 | + | |
| 525 | +static INLINE void encode_command_init(ENCODE_COMMAND *encode_command) { | |
| 526 | + vp9_zero(*encode_command); | |
| 527 | + encode_command->use_external_quantize_index = 0; | |
| 528 | + encode_command->external_quantize_index = -1; | |
| 529 | +} | |
| 530 | + | |
| 531 | +static INLINE void encode_command_set_external_quantize_index( | |
| 532 | + ENCODE_COMMAND *encode_command, int quantize_index) { | |
| 533 | + encode_command->use_external_quantize_index = 1; | |
| 534 | + encode_command->external_quantize_index = quantize_index; | |
| 535 | +} | |
| 536 | + | |
| 537 | +static INLINE void encode_command_reset_external_quantize_index( | |
| 538 | + ENCODE_COMMAND *encode_command) { | |
| 539 | + encode_command->use_external_quantize_index = 0; | |
| 540 | + encode_command->external_quantize_index = -1; | |
| 541 | +} | |
| 542 | +#endif | |
| 543 | + | |
| 519 | 544 | typedef struct VP9_COMP { |
| 520 | 545 | FRAME_INFO frame_info; |
| 521 | 546 | QUANTS quants; |
| @@ -820,6 +845,9 @@ typedef struct VP9_COMP { · 298 unmodified lines | ||
| 820 | 845 | |
| 821 | 846 | int multi_layer_arf; |
| 822 | 847 | vpx_roi_map_t roi; |
| 848 | +#if CONFIG_RATE_CTRL | |
| 849 | + ENCODE_COMMAND encode_command; | |
| 850 | +#endif | |
| 823 | 851 | } VP9_COMP; |
| 824 | 852 | |
| 825 | 853 | typedef struct ENCODE_FRAME_RESULT { |
| @@ -87,6 +87,8 @@ get_frame_type_from_update_type(FRAME_UPDATE_TYPE update_type) { · 86 unmodified lines | ||
| 87 | 87 | static void update_encode_frame_result( |
| 88 | 88 | EncodeFrameResult *encode_frame_result, |
| 89 | 89 | const ENCODE_FRAME_RESULT *encode_frame_info) { |
| 90 | + encode_frame_result->coding_data_bit_size = | |
| 91 | + encode_frame_result->coding_data_byte_size * 8; | |
| 90 | 92 | encode_frame_result->show_idx = encode_frame_info->show_idx; |
| 91 | 93 | encode_frame_result->frame_type = |
| 92 | 94 | get_frame_type_from_update_type(encode_frame_info->update_type); |
| @@ -97,7 +99,8 @@ static void update_encode_frame_result( · 4 unmodified lines | ||
| 97 | 99 | |
| 98 | 100 | SimpleEncode::SimpleEncode(int frame_width, int frame_height, |
| 99 | 101 | int frame_rate_num, int frame_rate_den, |
| 100 | − int target_bitrate, int num_frames, FILE *file) | |
| 102 | + int target_bitrate, int num_frames, | |
| 103 | + const char *infile_path) | |
| 101 | 104 | : pimpl{ std::unique_ptr<impl>(new impl()) } { |
| 102 | 105 | this->frame_width = frame_width; |
| 103 | 106 | this->frame_height = frame_height; |
| @@ -105,7 +108,8 @@ SimpleEncode::SimpleEncode(int frame_width, int frame_height, · 1 unmodified line | ||
| 105 | 108 | this->frame_rate_den = frame_rate_den; |
| 106 | 109 | this->target_bitrate = target_bitrate; |
| 107 | 110 | this->num_frames = num_frames; |
| 108 | − this->file = file; | |
| 111 | + // TODO(angirbid): Should we keep a file pointer here or keep the file_path? | |
| 112 | + this->file = fopen(infile_path, "r"); | |
| 109 | 113 | pimpl->cpi = NULL; |
| 110 | 114 | pimpl->img_fmt = VPX_IMG_FMT_I420; |
| 111 | 115 | } |
| @@ -234,26 +238,35 @@ void SimpleEncode::EncodeFrame(EncodeFrameResult *encode_frame_result) { · 122 unmodified lines | ||
| 234 | 238 | } |
| 235 | 239 | } |
| 236 | 240 | assert(encode_frame_result->coding_data.get() == nullptr); |
| 237 | − const size_t max_coding_data_size = frame_width * frame_height * 3; | |
| 238 | − encode_frame_result->coding_data = | |
| 239 | − std::move(std::unique_ptr<uint8_t[]>(new uint8_t[max_coding_data_size])); | |
| 241 | + const size_t max_coding_data_byte_size = frame_width * frame_height * 3; | |
| 242 | + encode_frame_result->coding_data = std::move( | |
| 243 | + std::unique_ptr<uint8_t[]>(new uint8_t[max_coding_data_byte_size])); | |
| 240 | 244 | int64_t time_stamp; |
| 241 | 245 | int64_t time_end; |
| 242 | 246 | int flush = 1; // Make vp9_get_compressed_data encode a frame |
| 243 | 247 | unsigned int frame_flags = 0; |
| 244 | 248 | ENCODE_FRAME_RESULT encode_frame_info; |
| 245 | 249 | vp9_get_compressed_data(cpi, &frame_flags, |
| 246 | − &encode_frame_result->coding_data_size, | |
| 250 | + &encode_frame_result->coding_data_byte_size, | |
| 247 | 251 | encode_frame_result->coding_data.get(), &time_stamp, |
| 248 | 252 | &time_end, flush, &encode_frame_info); |
| 249 | 253 | // vp9_get_compressed_data is expected to encode a frame every time, so the |
| 250 | 254 | // data size should be greater than zero. |
| 251 | − assert(encode_frame_result->coding_data_size > 0); | |
| 252 | − assert(encode_frame_result->coding_data_size < max_coding_data_size); | |
| 255 | + assert(encode_frame_result->coding_data_byte_size > 0); | |
| 256 | + assert(encode_frame_result->coding_data_byte_size < | |
| 257 | + max_coding_data_byte_size); | |
| 253 | 258 | |
| 254 | 259 | update_encode_frame_result(encode_frame_result, &encode_frame_info); |
| 255 | 260 | } |
| 256 | 261 | |
| 262 | +void SimpleEncode::EncodeFrameWithQuantizeIndex( | |
| 263 | + EncodeFrameResult *encode_frame_result, int quantize_index) { | |
| 264 | + encode_command_set_external_quantize_index(&pimpl->cpi->encode_command, | |
| 265 | + quantize_index); | |
| 266 | + EncodeFrame(encode_frame_result); | |
| 267 | + encode_command_reset_external_quantize_index(&pimpl->cpi->encode_command); | |
| 268 | +} | |
| 269 | + | |
| 257 | 270 | int SimpleEncode::GetCodingFrameNum() { |
| 258 | 271 | assert(pimpl->first_pass_stats.size() - 1 > 0); |
| 259 | 272 | // These are the default settings for now. |
| @@ -10,7 +10,8 @@ enum FrameType { · 9 unmodified lines | ||
| 10 | 10 | struct EncodeFrameResult { |
| 11 | 11 | int show_idx; |
| 12 | 12 | FrameType frame_type; |
| 13 | − size_t coding_data_size; | |
| 13 | + size_t coding_data_bit_size; | |
| 14 | + size_t coding_data_byte_size; | |
| 14 | 15 | // The EncodeFrame will allocate a buffer, write the coding data into the |
| 15 | 16 | // buffer and give the ownership of the buffer to coding_data |
| 16 | 17 | std::unique_ptr<unsigned char[]> coding_data; |
| @@ -23,7 +24,7 @@ class SimpleEncode { · 6 unmodified lines | ||
| 23 | 24 | public: |
| 24 | 25 | SimpleEncode(int frame_width, int frame_height, int frame_rate_num, |
| 25 | 26 | int frame_rate_den, int target_bitrate, int num_frames, |
| 26 | − FILE *file); | |
| 27 | + const char *infile_path); | |
| 27 | 28 | ~SimpleEncode(); |
| 28 | 29 | SimpleEncode(SimpleEncode &&) = delete; |
| 29 | 30 | SimpleEncode &operator=(SimpleEncode &&) = delete; |
| @@ -47,6 +48,11 @@ class SimpleEncode { · 17 unmodified lines | ||
| 47 | 48 | // This funtion should be called after StartEncode() before EndEncode() |
| 48 | 49 | void EncodeFrame(EncodeFrameResult *encode_frame_result); |
| 49 | 50 | |
| 51 | + // Encode a frame with a specific quantize index | |
| 52 | + // This funtion should be called after StartEncode() before EndEncode() | |
| 53 | + void EncodeFrameWithQuantizeIndex(EncodeFrameResult *encode_frame_result, | |
| 54 | + int quantize_index); | |
| 55 | + | |
| 50 | 56 | // Get the number of coding frames for the video. The coding frames include |
| 51 | 57 | // show frame and no show frame. |
| 52 | 58 | // This funtion should be called after ComputeFirstPassStats() |