* 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 | #include <memory> | 2 | #include <memory> |
| 2 | #include <vector> | 3 | #include <vector> |
| 3 | #include "third_party/googletest/src/include/gtest/gtest.h" | 4 | #include "third_party/googletest/src/include/gtest/gtest.h" |
| 4 | #include "vp9/simple_encode.h" | 5 | #include "vp9/simple_encode.h" |
| 5 | 6 | ||
| 6 | namespace { | 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 | TEST(SimpleEncode, ComputeFirstPassStats) { | 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 | SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, | 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 | simple_encode.ComputeFirstPassStats(); | 26 | simple_encode.ComputeFirstPassStats(); |
| 20 | std::vector<std::vector<double>> frame_stats = | 27 | std::vector<std::vector<double>> frame_stats = |
| 21 | simple_encode.ObserveFirstPassStats(); | 28 | simple_encode.ObserveFirstPassStats(); |
| @@ -34,32 +41,16 @@ TEST(SimpleEncode, ComputeFirstPassStats) { · 12 unmodified lines | |||
| 34 | } | 41 | } |
| 35 | 42 | ||
| 36 | TEST(SimpleEncode, GetCodingFrameNum) { | 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 | SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, | 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 | simple_encode.ComputeFirstPassStats(); | 46 | simple_encode.ComputeFirstPassStats(); |
| 48 | int num_coding_frames = simple_encode.GetCodingFrameNum(); | 47 | int num_coding_frames = simple_encode.GetCodingFrameNum(); |
| 49 | EXPECT_EQ(num_coding_frames, 19); | 48 | EXPECT_EQ(num_coding_frames, 19); |
| 50 | } | 49 | } |
| 51 | 50 | ||
| 52 | TEST(SimpleEncode, EncodeFrame) { | 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 | SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den, | 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 | simple_encode.ComputeFirstPassStats(); | 54 | simple_encode.ComputeFirstPassStats(); |
| 64 | int num_coding_frames = simple_encode.GetCodingFrameNum(); | 55 | int num_coding_frames = simple_encode.GetCodingFrameNum(); |
| 65 | EXPECT_GE(num_coding_frames, num_frames); | 56 | EXPECT_GE(num_coding_frames, num_frames); |
| @@ -68,6 +59,7 @@ TEST(SimpleEncode, EncodeFrame) { · 2 unmodified lines | |||
| 68 | int ref_num_alternate_refereces = num_coding_frames - num_frames; | 59 | int ref_num_alternate_refereces = num_coding_frames - num_frames; |
| 69 | int num_alternate_refereces = 0; | 60 | int num_alternate_refereces = 0; |
| 70 | simple_encode.StartEncode(); | 61 | simple_encode.StartEncode(); |
| 62 | size_t total_data_bit_size = 0; | ||
| 71 | for (int i = 0; i < num_coding_frames; ++i) { | 63 | for (int i = 0; i < num_coding_frames; ++i) { |
| 72 | EncodeFrameResult encode_frame_result; | 64 | EncodeFrameResult encode_frame_result; |
| 73 | if (i == 0) { | 65 | if (i == 0) { |
| @@ -88,8 +80,28 @@ TEST(SimpleEncode, EncodeFrame) { · 14 unmodified lines | |||
| 88 | EXPECT_GE(encode_frame_result.psnr, 34) | 80 | EXPECT_GE(encode_frame_result.psnr, 34) |
| 89 | << "The psnr is supposed to be greater than 34 given the " | 81 | << "The psnr is supposed to be greater than 34 given the " |
| 90 | "target_bitrate 1000 kbps"; | 82 | "target_bitrate 1000 kbps"; |
| 83 | total_data_bit_size += encode_frame_result.coding_data_bit_size; | ||
| 91 | } | 84 | } |
| 92 | EXPECT_EQ(num_alternate_refereces, ref_num_alternate_refereces); | 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 | simple_encode.EndEncode(); | 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 | cm->error.setjmp = 0; | 2648 | cm->error.setjmp = 0; |
| 2649 | 2649 | ||
| 2650 | #if CONFIG_RATE_CTRL | ||
| 2651 | encode_command_init(&cpi->encode_command); | ||
| 2652 | #endif | ||
| 2653 | |||
| 2650 | return cpi; | 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 | vp9_scale_references(cpi); | 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 | vp9_set_quantizer(cm, q); | 4297 | vp9_set_quantizer(cm, q); |
| 4286 | 4298 | ||
| 4287 | if (loop_count == 0) setup_frame(cpi); | 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 | // update_base_skip_probs(cpi); | 4319 | // update_base_skip_probs(cpi); |
| 4308 | 4320 | ||
| 4309 | vpx_clear_system_state(); | 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 | // Dummy pack of the bitstream using up to date stats to get an | 4330 | // Dummy pack of the bitstream using up to date stats to get an |
| 4312 | // accurate estimate of output frame size to determine if we need | 4331 | // accurate estimate of output frame size to determine if we need |
| @@ -516,6 +516,31 @@ typedef struct KMEANS_DATA { · 515 unmodified lines | |||
| 516 | int group_idx; | 516 | int group_idx; |
| 517 | } KMEANS_DATA; | 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 | typedef struct VP9_COMP { | 544 | typedef struct VP9_COMP { |
| 520 | FRAME_INFO frame_info; | 545 | FRAME_INFO frame_info; |
| 521 | QUANTS quants; | 546 | QUANTS quants; |
| @@ -820,6 +845,9 @@ typedef struct VP9_COMP { · 298 unmodified lines | |||
| 820 | 845 | ||
| 821 | int multi_layer_arf; | 846 | int multi_layer_arf; |
| 822 | vpx_roi_map_t roi; | 847 | vpx_roi_map_t roi; |
| 848 | #if CONFIG_RATE_CTRL | ||
| 849 | ENCODE_COMMAND encode_command; | ||
| 850 | #endif | ||
| 823 | } VP9_COMP; | 851 | } VP9_COMP; |
| 824 | 852 | ||
| 825 | typedef struct ENCODE_FRAME_RESULT { | 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 | static void update_encode_frame_result( | 87 | static void update_encode_frame_result( |
| 88 | EncodeFrameResult *encode_frame_result, | 88 | EncodeFrameResult *encode_frame_result, |
| 89 | const ENCODE_FRAME_RESULT *encode_frame_info) { | 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 | encode_frame_result->show_idx = encode_frame_info->show_idx; | 92 | encode_frame_result->show_idx = encode_frame_info->show_idx; |
| 91 | encode_frame_result->frame_type = | 93 | encode_frame_result->frame_type = |
| 92 | get_frame_type_from_update_type(encode_frame_info->update_type); | 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 | SimpleEncode::SimpleEncode(int frame_width, int frame_height, | 100 | SimpleEncode::SimpleEncode(int frame_width, int frame_height, |
| 99 | int frame_rate_num, int frame_rate_den, | 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 | : pimpl{ std::unique_ptr<impl>(new impl()) } { | 104 | : pimpl{ std::unique_ptr<impl>(new impl()) } { |
| 102 | this->frame_width = frame_width; | 105 | this->frame_width = frame_width; |
| 103 | this->frame_height = frame_height; | 106 | this->frame_height = frame_height; |
| @@ -105,7 +108,8 @@ SimpleEncode::SimpleEncode(int frame_width, int frame_height, · 1 unmodified line | |||
| 105 | this->frame_rate_den = frame_rate_den; | 108 | this->frame_rate_den = frame_rate_den; |
| 106 | this->target_bitrate = target_bitrate; | 109 | this->target_bitrate = target_bitrate; |
| 107 | this->num_frames = num_frames; | 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 | pimpl->cpi = NULL; | 113 | pimpl->cpi = NULL; |
| 110 | pimpl->img_fmt = VPX_IMG_FMT_I420; | 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 | assert(encode_frame_result->coding_data.get() == nullptr); | 240 | assert(encode_frame_result->coding_data.get() == nullptr); |
| 237 | const size_t max_coding_data_size = frame_width * frame_height * 3; | 241 | const size_t max_coding_data_byte_size = frame_width * frame_height * 3; |
| 238 | encode_frame_result->coding_data = | 242 | encode_frame_result->coding_data = std::move( |
| 239 | std::move(std::unique_ptr<uint8_t[]>(new uint8_t[max_coding_data_size])); | 243 | std::unique_ptr<uint8_t[]>(new uint8_t[max_coding_data_byte_size])); |
| 240 | int64_t time_stamp; | 244 | int64_t time_stamp; |
| 241 | int64_t time_end; | 245 | int64_t time_end; |
| 242 | int flush = 1; // Make vp9_get_compressed_data encode a frame | 246 | int flush = 1; // Make vp9_get_compressed_data encode a frame |
| 243 | unsigned int frame_flags = 0; | 247 | unsigned int frame_flags = 0; |
| 244 | ENCODE_FRAME_RESULT encode_frame_info; | 248 | ENCODE_FRAME_RESULT encode_frame_info; |
| 245 | vp9_get_compressed_data(cpi, &frame_flags, | 249 | vp9_get_compressed_data(cpi, &frame_flags, |
| 246 | &encode_frame_result->coding_data_size, | 250 | &encode_frame_result->coding_data_byte_size, |
| 247 | encode_frame_result->coding_data.get(), &time_stamp, | 251 | encode_frame_result->coding_data.get(), &time_stamp, |
| 248 | &time_end, flush, &encode_frame_info); | 252 | &time_end, flush, &encode_frame_info); |
| 249 | // vp9_get_compressed_data is expected to encode a frame every time, so the | 253 | // vp9_get_compressed_data is expected to encode a frame every time, so the |
| 250 | // data size should be greater than zero. | 254 | // data size should be greater than zero. |
| 251 | assert(encode_frame_result->coding_data_size > 0); | 255 | assert(encode_frame_result->coding_data_byte_size > 0); |
| 252 | assert(encode_frame_result->coding_data_size < max_coding_data_size); | 256 | assert(encode_frame_result->coding_data_byte_size < |
| 257 | max_coding_data_byte_size); | ||
| 253 | 258 | ||
| 254 | update_encode_frame_result(encode_frame_result, &encode_frame_info); | 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 | int SimpleEncode::GetCodingFrameNum() { | 270 | int SimpleEncode::GetCodingFrameNum() { |
| 258 | assert(pimpl->first_pass_stats.size() - 1 > 0); | 271 | assert(pimpl->first_pass_stats.size() - 1 > 0); |
| 259 | // These are the default settings for now. | 272 | // These are the default settings for now. |
| @@ -10,7 +10,8 @@ enum FrameType { · 9 unmodified lines | |||
| 10 | struct EncodeFrameResult { | 10 | struct EncodeFrameResult { |
| 11 | int show_idx; | 11 | int show_idx; |
| 12 | FrameType frame_type; | 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 | // The EncodeFrame will allocate a buffer, write the coding data into the | 15 | // The EncodeFrame will allocate a buffer, write the coding data into the |
| 15 | // buffer and give the ownership of the buffer to coding_data | 16 | // buffer and give the ownership of the buffer to coding_data |
| 16 | std::unique_ptr<unsigned char[]> coding_data; | 17 | std::unique_ptr<unsigned char[]> coding_data; |
| @@ -23,7 +24,7 @@ class SimpleEncode { · 6 unmodified lines | |||
| 23 | public: | 24 | public: |
| 24 | SimpleEncode(int frame_width, int frame_height, int frame_rate_num, | 25 | SimpleEncode(int frame_width, int frame_height, int frame_rate_num, |
| 25 | int frame_rate_den, int target_bitrate, int num_frames, | 26 | int frame_rate_den, int target_bitrate, int num_frames, |
| 26 | FILE *file); | 27 | const char *infile_path); |
| 27 | ~SimpleEncode(); | 28 | ~SimpleEncode(); |
| 28 | SimpleEncode(SimpleEncode &&) = delete; | 29 | SimpleEncode(SimpleEncode &&) = delete; |
| 29 | SimpleEncode &operator=(SimpleEncode &&) = delete; | 30 | SimpleEncode &operator=(SimpleEncode &&) = delete; |
| @@ -47,6 +48,11 @@ class SimpleEncode { · 17 unmodified lines | |||
| 47 | // This funtion should be called after StartEncode() before EndEncode() | 48 | // This funtion should be called after StartEncode() before EndEncode() |
| 48 | void EncodeFrame(EncodeFrameResult *encode_frame_result); | 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 | // Get the number of coding frames for the video. The coding frames include | 56 | // Get the number of coding frames for the video. The coding frames include |
| 51 | // show frame and no show frame. | 57 | // show frame and no show frame. |
| 52 | // This funtion should be called after ComputeFirstPassStats() | 58 | // This funtion should be called after ComputeFirstPassStats() |