Unified · Split
| @@ -0,0 +1,36 @@ | ||
| 1 | +#include <memory> | |
| 2 | +#include <vector> | |
| 3 | +#include "third_party/googletest/src/include/gtest/gtest.h" | |
| 4 | +#include "vp9/simple_encode.h" | |
| 5 | + | |
| 6 | +namespace { | |
| 7 | + | |
| 8 | +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, | |
| 18 | + target_bitrate, num_frames, file); | |
| 19 | + simple_encode.ComputeFirstPassStats(); | |
| 20 | + std::vector<std::vector<double>> frame_stats = | |
| 21 | + simple_encode.ObserveFirstPassStats(); | |
| 22 | + EXPECT_EQ(frame_stats.size(), static_cast<size_t>(num_frames)); | |
| 23 | + size_t data_num = frame_stats[0].size(); | |
| 24 | + // Read ObserveFirstPassStats before changing FIRSTPASS_STATS. | |
| 25 | + EXPECT_EQ(data_num, static_cast<size_t>(25)); | |
| 26 | + for (size_t i = 0; i < frame_stats.size(); ++i) { | |
| 27 | + EXPECT_EQ(frame_stats[i].size(), data_num); | |
| 28 | + // FIRSTPASS_STATS's first element is frame | |
| 29 | + EXPECT_EQ(frame_stats[i][0], i); | |
| 30 | + // FIRSTPASS_STATS's last element is count, and the count is 1 for single | |
| 31 | + // frame stats | |
| 32 | + EXPECT_EQ(frame_stats[i][data_num - 1], 1); | |
| 33 | + } | |
| 34 | +} | |
| 35 | + | |
| 36 | +} // namespace | |
| @@ -26,6 +26,7 @@ LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += noisy_clip_640_360.y4m · 25 unmodified lines | ||
| 26 | 26 | LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += rush_hour_444.y4m |
| 27 | 27 | LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += screendata.y4m |
| 28 | 28 | LIBVPX_TEST_DATA-$(CONFIG_VP9_ENCODER) += niklas_640_480_30.yuv |
| 29 | +LIBVPX_TEST_DATA-$(CONFIG_RATE_CTRL) += bus_352x288_420_f20_b8.yuv | |
| 29 | 30 | |
| 30 | 31 | # Test vectors |
| 31 | 32 | LIBVPX_TEST_DATA-$(CONFIG_VP8_DECODER) += vp80-00-comprehensive-001.ivf |
| @@ -1,3 +1,4 @@ | ||
| 1 | +3eaf216d9fc8b4b9bb8c3956311f49a85974806c *bus_352x288_420_f20_b8.yuv | |
| 1 | 2 | d5dfb0151c9051f8c85999255645d7a23916d3c0 *hantro_collage_w352h288.yuv |
| 2 | 3 | b87815bf86020c592ccc7a846ba2e28ec8043902 *hantro_odd.yuv |
| 3 | 4 | 76024eb753cdac6a5e5703aaea189d35c3c30ac7 *invalid-vp90-2-00-quantizer-00.webm.ivf.s5861_r01-05_b6-.v2.ivf |
| @@ -58,6 +58,7 @@ LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += svc_test.cc · 57 unmodified lines | ||
| 58 | 58 | LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += svc_test.h |
| 59 | 59 | LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += svc_end_to_end_test.cc |
| 60 | 60 | LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += timestamp_test.cc |
| 61 | +LIBVPX_TEST_SRCS-$(CONFIG_RATE_CTRL) += simple_encode_test.cc | |
| 61 | 62 | |
| 62 | 63 | LIBVPX_TEST_SRCS-yes += decode_test_driver.cc |
| 63 | 64 | LIBVPX_TEST_SRCS-yes += decode_test_driver.h |
| @@ -3685,3 +3685,10 @@ int vp9_get_coding_frame_num(const struct VP9EncoderConfig *oxcf, · 3684 unmodified lines | ||
| 3685 | 3685 | return coding_frame_num; |
| 3686 | 3686 | } |
| 3687 | 3687 | #endif |
| 3688 | + | |
| 3689 | +FIRSTPASS_STATS vp9_get_frame_stats(const TWO_PASS *twopass) { | |
| 3690 | + return twopass->this_frame_stats; | |
| 3691 | +} | |
| 3692 | +FIRSTPASS_STATS vp9_get_total_stats(const TWO_PASS *twopass) { | |
| 3693 | + return twopass->total_stats; | |
| 3694 | +} | |
| @@ -256,6 +256,9 @@ int vp9_get_coding_frame_num(const struct VP9EncoderConfig *oxcf, · 255 unmodified lines | ||
| 256 | 256 | int multi_layer_arf, int allow_alt_ref); |
| 257 | 257 | #endif |
| 258 | 258 | |
| 259 | +FIRSTPASS_STATS vp9_get_frame_stats(const TWO_PASS *two_pass); | |
| 260 | +FIRSTPASS_STATS vp9_get_total_stats(const TWO_PASS *two_pass); | |
| 261 | + | |
| 259 | 262 | #ifdef __cplusplus |
| 260 | 263 | } // extern "C" |
| 261 | 264 | #endif |
| @@ -1,27 +1,173 @@ | ||
| 1 | +#include <vector> | |
| 1 | 2 | #include "vp9/common/vp9_onyxc_int.h" |
| 3 | +#include "vp9/vp9_iface_common.h" | |
| 2 | 4 | #include "vp9/encoder/vp9_encoder.h" |
| 5 | +#include "vp9/encoder/vp9_firstpass.h" | |
| 3 | 6 | #include "vp9/simple_encode.h" |
| 4 | 7 | #include "vp9/vp9_cx_iface.h" |
| 5 | 8 | |
| 9 | +// TODO(angiebird): Merge this function with vpx_img_plane_width() | |
| 10 | +static int img_plane_width(const vpx_image_t *img, int plane) { | |
| 11 | + if (plane > 0 && img->x_chroma_shift > 0) | |
| 12 | + return (img->d_w + 1) >> img->x_chroma_shift; | |
| 13 | + else | |
| 14 | + return img->d_w; | |
| 15 | +} | |
| 16 | + | |
| 17 | +// TODO(angiebird): Merge this function with vpx_img_plane_height() | |
| 18 | +static int img_plane_height(const vpx_image_t *img, int plane) { | |
| 19 | + if (plane > 0 && img->y_chroma_shift > 0) | |
| 20 | + return (img->d_h + 1) >> img->y_chroma_shift; | |
| 21 | + else | |
| 22 | + return img->d_h; | |
| 23 | +} | |
| 24 | + | |
| 25 | +// TODO(angiebird): Merge this function with vpx_img_read() | |
| 26 | +static int img_read(vpx_image_t *img, FILE *file) { | |
| 27 | + int plane; | |
| 28 | + | |
| 29 | + for (plane = 0; plane < 3; ++plane) { | |
| 30 | + unsigned char *buf = img->planes[plane]; | |
| 31 | + const int stride = img->stride[plane]; | |
| 32 | + const int w = img_plane_width(img, plane) * | |
| 33 | + ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1); | |
| 34 | + const int h = img_plane_height(img, plane); | |
| 35 | + int y; | |
| 36 | + | |
| 37 | + for (y = 0; y < h; ++y) { | |
| 38 | + if (fread(buf, 1, w, file) != (size_t)w) return 0; | |
| 39 | + buf += stride; | |
| 40 | + } | |
| 41 | + } | |
| 42 | + | |
| 43 | + return 1; | |
| 44 | +} | |
| 45 | + | |
| 6 | 46 | class SimpleEncode::impl { |
| 7 | 47 | public: |
| 8 | 48 | VP9_COMP *cpi; |
| 9 | − BufferPool *buffer_pool; | |
| 49 | + std::vector<FIRSTPASS_STATS> frame_stats; | |
| 10 | 50 | }; |
| 11 | 51 | |
| 52 | +static VP9_COMP *init_encoder(const VP9EncoderConfig *oxcf, | |
| 53 | + vpx_img_fmt_t img_fmt) { | |
| 54 | + VP9_COMP *cpi; | |
| 55 | + BufferPool *buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(*buffer_pool)); | |
| 56 | + vp9_initialize_enc(); | |
| 57 | + cpi = vp9_create_compressor(oxcf, buffer_pool); | |
| 58 | + vp9_update_compressor_with_img_fmt(cpi, img_fmt); | |
| 59 | + return cpi; | |
| 60 | +} | |
| 61 | + | |
| 62 | +static void free_encoder(VP9_COMP *cpi) { | |
| 63 | + vpx_free(cpi->common.buffer_pool); | |
| 64 | + vp9_remove_compressor(cpi); | |
| 65 | +} | |
| 66 | + | |
| 67 | +static INLINE vpx_rational_t make_vpx_rational(int num, int den) { | |
| 68 | + vpx_rational_t v; | |
| 69 | + v.num = num; | |
| 70 | + v.den = den; | |
| 71 | + return v; | |
| 72 | +} | |
| 73 | + | |
| 12 | 74 | SimpleEncode::SimpleEncode(int frame_width, int frame_height, |
| 13 | − vpx_rational_t frame_rate, int target_bitrate) | |
| 75 | + int frame_rate_num, int frame_rate_den, | |
| 76 | + int target_bitrate, int num_frames, FILE *file) | |
| 14 | 77 | : pimpl{ std::unique_ptr<impl>(new impl()) } { |
| 78 | + this->frame_width = frame_width; | |
| 79 | + this->frame_height = frame_height; | |
| 80 | + this->frame_rate_num = frame_rate_num; | |
| 81 | + this->frame_rate_den = frame_rate_den; | |
| 82 | + this->target_bitrate = target_bitrate; | |
| 83 | + this->num_frames = num_frames; | |
| 84 | + this->file = file; | |
| 85 | + pimpl->cpi = NULL; | |
| 86 | +} | |
| 87 | + | |
| 88 | +void SimpleEncode::ComputeFirstPassStats() { | |
| 89 | + const vpx_img_fmt_t img_fmt = VPX_IMG_FMT_I420; | |
| 90 | + vpx_rational_t frame_rate = make_vpx_rational(frame_rate_num, frame_rate_den); | |
| 91 | + const VP9EncoderConfig oxcf = vp9_get_encoder_config( | |
| 92 | + frame_width, frame_height, frame_rate, target_bitrate, VPX_RC_FIRST_PASS); | |
| 93 | + VP9_COMP *cpi = init_encoder(&oxcf, img_fmt); | |
| 94 | + struct lookahead_ctx *lookahead = cpi->lookahead; | |
| 95 | + int i; | |
| 96 | + int use_highbitdepth = 0; | |
| 97 | +#if CONFIG_VP9_HIGHBITDEPTH | |
| 98 | + use_highbitdepth = cpi->common.use_highbitdepth; | |
| 99 | +#endif | |
| 100 | + vpx_image_t img; | |
| 101 | + vpx_img_alloc(&img, img_fmt, frame_width, frame_height, 1); | |
| 102 | + rewind(file); | |
| 103 | + pimpl->frame_stats.clear(); | |
| 104 | + for (i = 0; i < num_frames; ++i) { | |
| 105 | + assert(!vp9_lookahead_full(lookahead)); | |
| 106 | + if (img_read(&img, file)) { | |
| 107 | + int next_show_idx = vp9_lookahead_next_show_idx(lookahead); | |
| 108 | + int64_t ts_start = | |
| 109 | + timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx); | |
| 110 | + int64_t ts_end = | |
| 111 | + timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx + 1); | |
| 112 | + YV12_BUFFER_CONFIG sd; | |
| 113 | + image2yuvconfig(&img, &sd); | |
| 114 | + vp9_lookahead_push(lookahead, &sd, ts_start, ts_end, use_highbitdepth, 0); | |
| 115 | + { | |
| 116 | + int64_t time_stamp; | |
| 117 | + int64_t time_end; | |
| 118 | + int flush = 1; // Make vp9_get_compressed_data process a frame | |
| 119 | + size_t size; | |
| 120 | + unsigned int frame_flags = 0; | |
| 121 | + // TODO(angiebird): Call vp9_first_pass directly | |
| 122 | + vp9_get_compressed_data(cpi, &frame_flags, &size, NULL, &time_stamp, | |
| 123 | + &time_end, flush); | |
| 124 | + // vp9_get_compressed_data only generates first pass stats not | |
| 125 | + // compresses data | |
| 126 | + assert(size == 0); | |
| 127 | + } | |
| 128 | + pimpl->frame_stats.push_back(vp9_get_frame_stats(&cpi->twopass)); | |
| 129 | + } | |
| 130 | + } | |
| 131 | + vp9_end_first_pass(cpi); | |
| 132 | + // TODO(angiebird): Store the total_stats apart form frame_stats | |
| 133 | + pimpl->frame_stats.push_back(vp9_get_total_stats(&cpi->twopass)); | |
| 134 | + free_encoder(cpi); | |
| 135 | + rewind(file); | |
| 136 | + vpx_img_free(&img); | |
| 137 | +} | |
| 138 | + | |
| 139 | +std::vector<std::vector<double>> SimpleEncode::ObserveFirstPassStats() { | |
| 140 | + std::vector<std::vector<double>> output_stats; | |
| 141 | + // TODO(angiebird): This function make several assumptions of | |
| 142 | + // FIRSTPASS_STATS. 1) All elements in FIRSTPASS_STATS are double except the | |
| 143 | + // last one. 2) The last entry of frame_stats is the total_stats. | |
| 144 | + // Change the code structure, so that we don't have to make these assumptions | |
| 145 | + | |
| 146 | + // Note the last entry of frame_stats is the total_stats, we don't need it. | |
| 147 | + for (size_t i = 0; i < pimpl->frame_stats.size() - 1; ++i) { | |
| 148 | + double *buf_start = reinterpret_cast<double *>(&pimpl->frame_stats[i]); | |
| 149 | + // We use - 1 here because the last member in FIRSTPASS_STATS is not double | |
| 150 | + double *buf_end = | |
| 151 | + buf_start + sizeof(pimpl->frame_stats[i]) / sizeof(*buf_end) - 1; | |
| 152 | + std::vector<double> this_stats(buf_start, buf_end); | |
| 153 | + output_stats.push_back(this_stats); | |
| 154 | + } | |
| 155 | + return output_stats; | |
| 156 | +} | |
| 157 | + | |
| 158 | +void SimpleEncode::StartEncode() { | |
| 159 | + vpx_rational_t frame_rate = make_vpx_rational(frame_rate_num, frame_rate_den); | |
| 15 | 160 | VP9EncoderConfig oxcf = vp9_get_encoder_config( |
| 16 | 161 | frame_width, frame_height, frame_rate, target_bitrate, VPX_RC_LAST_PASS); |
| 17 | − pimpl->buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(*pimpl->buffer_pool)); | |
| 18 | − vp9_initialize_enc(); | |
| 19 | − pimpl->cpi = vp9_create_compressor(&oxcf, pimpl->buffer_pool); | |
| 20 | − vp9_update_compressor_with_img_fmt(pimpl->cpi, VPX_IMG_FMT_I420); | |
| 162 | + assert(pimpl->cpi == NULL); | |
| 163 | + pimpl->cpi = init_encoder(&oxcf, VPX_IMG_FMT_I420); | |
| 164 | + rewind(file); | |
| 21 | 165 | } |
| 22 | 166 | |
| 23 | −SimpleEncode::~SimpleEncode() { | |
| 24 | − vpx_free(pimpl->buffer_pool); | |
| 25 | − vp9_remove_compressor(pimpl->cpi); | |
| 167 | +void SimpleEncode::EndEncode() { | |
| 168 | + free_encoder(pimpl->cpi); | |
| 26 | 169 | pimpl->cpi = nullptr; |
| 170 | + rewind(file); | |
| 27 | 171 | } |
| 172 | + | |
| 173 | +SimpleEncode::~SimpleEncode() {} | |
| @@ -1,13 +1,35 @@ | ||
| 1 | 1 | #include <memory> |
| 2 | +#include <vector> | |
| 2 | 3 | class SimpleEncode { |
| 3 | 4 | public: |
| 4 | − SimpleEncode(int frame_width, int frame_height, vpx_rational_t frame_rate, | |
| 5 | − int target_bitrate); | |
| 5 | + SimpleEncode(int frame_width, int frame_height, int frame_rate_num, | |
| 6 | + int frame_rate_den, int target_bitrate, int num_frames, | |
| 7 | + FILE *file); | |
| 6 | 8 | ~SimpleEncode(); |
| 7 | 9 | SimpleEncode(SimpleEncode &&) = delete; |
| 8 | 10 | SimpleEncode &operator=(SimpleEncode &&) = delete; |
| 9 | 11 | |
| 12 | + // Make encoder compute the first pass stats and store it internally for | |
| 13 | + // future encode | |
| 14 | + void ComputeFirstPassStats(); | |
| 15 | + | |
| 16 | + // Output the first pass stats | |
| 17 | + std::vector<std::vector<double>> ObserveFirstPassStats(); | |
| 18 | + | |
| 19 | + // Initialize the encoder for actual encoding | |
| 20 | + void StartEncode(); | |
| 21 | + | |
| 22 | + // Free the encoder | |
| 23 | + void EndEncode(); | |
| 24 | + | |
| 10 | 25 | private: |
| 11 | 26 | class impl; |
| 27 | + int frame_width; | |
| 28 | + int frame_height; | |
| 29 | + int frame_rate_num; | |
| 30 | + int frame_rate_den; | |
| 31 | + int target_bitrate; | |
| 32 | + int num_frames; | |
| 33 | + FILE *file; | |
| 12 | 34 | std::unique_ptr<impl> pimpl; |
| 13 | 35 | }; |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | #include "vp9/encoder/vp9_encoder.h" |
| 23 | 23 | #include "vpx/vp8cx.h" |
| 24 | 24 | #include "vp9/common/vp9_alloccommon.h" |
| 25 | +#include "vp9/vp9_cx_iface.h" | |
| 25 | 26 | #include "vp9/encoder/vp9_firstpass.h" |
| 26 | 27 | #include "vp9/encoder/vp9_lookahead.h" |
| 27 | 28 | #include "vp9/vp9_cx_iface.h" |
| @@ -1086,18 +1087,6 @@ static int write_superframe_index(vpx_codec_alg_priv_t *ctx) { · 1058 unmodified lines | ||
| 1086 | 1087 | return index_sz; |
| 1087 | 1088 | } |
| 1088 | 1089 | |
| 1089 | −static int64_t timebase_units_to_ticks(const vpx_rational64_t *timestamp_ratio, | |
| 1090 | − int64_t n) { | |
| 1091 | − return n * timestamp_ratio->num / timestamp_ratio->den; | |
| 1092 | −} | |
| 1093 | − | |
| 1094 | −static int64_t ticks_to_timebase_units(const vpx_rational64_t *timestamp_ratio, | |
| 1095 | − int64_t n) { | |
| 1096 | − int64_t round = timestamp_ratio->num / 2; | |
| 1097 | − if (round > 0) --round; | |
| 1098 | − return (n * timestamp_ratio->den + round) / timestamp_ratio->num; | |
| 1099 | −} | |
| 1100 | − | |
| 1101 | 1090 | static vpx_codec_frame_flags_t get_frame_pkt_flags(const VP9_COMP *cpi, |
| 1102 | 1091 | unsigned int lib_flags) { |
| 1103 | 1092 | vpx_codec_frame_flags_t flags = lib_flags << 16; |
| @@ -23,6 +23,18 @@ VP9EncoderConfig vp9_get_encoder_config(int frame_width, int frame_height, · 22 unmodified lines | ||
| 23 | 23 | vpx_enc_pass enc_pass); |
| 24 | 24 | FRAME_INFO vp9_get_frame_info(const VP9EncoderConfig *oxcf); |
| 25 | 25 | |
| 26 | +static INLINE int64_t | |
| 27 | +timebase_units_to_ticks(const vpx_rational64_t *timestamp_ratio, int64_t n) { | |
| 28 | + return n * timestamp_ratio->num / timestamp_ratio->den; | |
| 29 | +} | |
| 30 | + | |
| 31 | +static INLINE int64_t | |
| 32 | +ticks_to_timebase_units(const vpx_rational64_t *timestamp_ratio, int64_t n) { | |
| 33 | + int64_t round = timestamp_ratio->num / 2; | |
| 34 | + if (round > 0) --round; | |
| 35 | + return (n * timestamp_ratio->den + round) / timestamp_ratio->num; | |
| 36 | +} | |
| 37 | + | |
| 26 | 38 | #ifdef __cplusplus |
| 27 | 39 | } // extern "C" |
| 28 | 40 | #endif |