--- /dev/null
+#include <memory>
+#include <vector>
+#include "third_party/googletest/src/include/gtest/gtest.h"
+#include "vp9/simple_encode.h"
+
+namespace {
+
+TEST(SimpleEncode, ComputeFirstPassStats) {
+ int w = 352;
+ int h = 288;
+ int frame_rate_num = 30;
+ int frame_rate_den = 1;
+ int target_bitrate = 200;
+ int num_frames = 17;
+ // TODO(angiebird): Figure out how to upload test video to our codebase
+ FILE *file = fopen("bus_352x288_420_f20_b8.yuv", "r");
+ SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den,
+ target_bitrate, num_frames, file);
+ simple_encode.ComputeFirstPassStats();
+ std::vector<std::vector<double>> frame_stats =
+ simple_encode.ObserveFirstPassStats();
+ EXPECT_EQ(frame_stats.size(), static_cast<size_t>(num_frames));
+ size_t data_num = frame_stats[0].size();
+ // Read ObserveFirstPassStats before changing FIRSTPASS_STATS.
+ EXPECT_EQ(data_num, static_cast<size_t>(25));
+ for (size_t i = 0; i < frame_stats.size(); ++i) {
+ EXPECT_EQ(frame_stats[i].size(), data_num);
+ // FIRSTPASS_STATS's first element is frame
+ EXPECT_EQ(frame_stats[i][0], i);
+ // FIRSTPASS_STATS's last element is count, and the count is 1 for single
+ // frame stats
+ EXPECT_EQ(frame_stats[i][data_num - 1], 1);
+ }
+}
+
+} // namespace
+#include <vector>
#include "vp9/common/vp9_onyxc_int.h"
+#include "vp9/vp9_iface_common.h"
#include "vp9/encoder/vp9_encoder.h"
+#include "vp9/encoder/vp9_firstpass.h"
#include "vp9/simple_encode.h"
#include "vp9/vp9_cx_iface.h"
+// TODO(angiebird): Merge this function with vpx_img_plane_width()
+static int img_plane_width(const vpx_image_t *img, int plane) {
+ if (plane > 0 && img->x_chroma_shift > 0)
+ return (img->d_w + 1) >> img->x_chroma_shift;
+ else
+ return img->d_w;
+}
+
+// TODO(angiebird): Merge this function with vpx_img_plane_height()
+static int img_plane_height(const vpx_image_t *img, int plane) {
+ if (plane > 0 && img->y_chroma_shift > 0)
+ return (img->d_h + 1) >> img->y_chroma_shift;
+ else
+ return img->d_h;
+}
+
+// TODO(angiebird): Merge this function with vpx_img_read()
+static int img_read(vpx_image_t *img, FILE *file) {
+ int plane;
+
+ for (plane = 0; plane < 3; ++plane) {
+ unsigned char *buf = img->planes[plane];
+ const int stride = img->stride[plane];
+ const int w = img_plane_width(img, plane) *
+ ((img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+ const int h = img_plane_height(img, plane);
+ int y;
+
+ for (y = 0; y < h; ++y) {
+ if (fread(buf, 1, w, file) != (size_t)w) return 0;
+ buf += stride;
+ }
+ }
+
+ return 1;
+}
+
class SimpleEncode::impl {
public:
VP9_COMP *cpi;
- BufferPool *buffer_pool;
+ std::vector<FIRSTPASS_STATS> frame_stats;
};
+static VP9_COMP *init_encoder(const VP9EncoderConfig *oxcf,
+ vpx_img_fmt_t img_fmt) {
+ VP9_COMP *cpi;
+ BufferPool *buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(*buffer_pool));
+ vp9_initialize_enc();
+ cpi = vp9_create_compressor(oxcf, buffer_pool);
+ vp9_update_compressor_with_img_fmt(cpi, img_fmt);
+ return cpi;
+}
+
+static void free_encoder(VP9_COMP *cpi) {
+ vpx_free(cpi->common.buffer_pool);
+ vp9_remove_compressor(cpi);
+}
+
+static INLINE vpx_rational_t make_vpx_rational(int num, int den) {
+ vpx_rational_t v;
+ v.num = num;
+ v.den = den;
+ return v;
+}
+
SimpleEncode::SimpleEncode(int frame_width, int frame_height,
- vpx_rational_t frame_rate, int target_bitrate)
+ int frame_rate_num, int frame_rate_den,
+ int target_bitrate, int num_frames, FILE *file)
: pimpl{ std::unique_ptr<impl>(new impl()) } {
+ this->frame_width = frame_width;
+ this->frame_height = frame_height;
+ this->frame_rate_num = frame_rate_num;
+ this->frame_rate_den = frame_rate_den;
+ this->target_bitrate = target_bitrate;
+ this->num_frames = num_frames;
+ this->file = file;
+ pimpl->cpi = NULL;
+}
+
+void SimpleEncode::ComputeFirstPassStats() {
+ const vpx_img_fmt_t img_fmt = VPX_IMG_FMT_I420;
+ vpx_rational_t frame_rate = make_vpx_rational(frame_rate_num, frame_rate_den);
+ const VP9EncoderConfig oxcf = vp9_get_encoder_config(
+ frame_width, frame_height, frame_rate, target_bitrate, VPX_RC_FIRST_PASS);
+ VP9_COMP *cpi = init_encoder(&oxcf, img_fmt);
+ struct lookahead_ctx *lookahead = cpi->lookahead;
+ int i;
+ int use_highbitdepth = 0;
+#if CONFIG_VP9_HIGHBITDEPTH
+ use_highbitdepth = cpi->common.use_highbitdepth;
+#endif
+ vpx_image_t img;
+ vpx_img_alloc(&img, img_fmt, frame_width, frame_height, 1);
+ rewind(file);
+ pimpl->frame_stats.clear();
+ for (i = 0; i < num_frames; ++i) {
+ assert(!vp9_lookahead_full(lookahead));
+ if (img_read(&img, file)) {
+ int next_show_idx = vp9_lookahead_next_show_idx(lookahead);
+ int64_t ts_start =
+ timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx);
+ int64_t ts_end =
+ timebase_units_to_ticks(&oxcf.g_timebase_in_ts, next_show_idx + 1);
+ YV12_BUFFER_CONFIG sd;
+ image2yuvconfig(&img, &sd);
+ vp9_lookahead_push(lookahead, &sd, ts_start, ts_end, use_highbitdepth, 0);
+ {
+ int64_t time_stamp;
+ int64_t time_end;
+ int flush = 1; // Make vp9_get_compressed_data process a frame
+ size_t size;
+ unsigned int frame_flags = 0;
+ // TODO(angiebird): Call vp9_first_pass directly
+ vp9_get_compressed_data(cpi, &frame_flags, &size, NULL, &time_stamp,
+ &time_end, flush);
+ // vp9_get_compressed_data only generates first pass stats not
+ // compresses data
+ assert(size == 0);
+ }
+ pimpl->frame_stats.push_back(vp9_get_frame_stats(&cpi->twopass));
+ }
+ }
+ vp9_end_first_pass(cpi);
+ // TODO(angiebird): Store the total_stats apart form frame_stats
+ pimpl->frame_stats.push_back(vp9_get_total_stats(&cpi->twopass));
+ free_encoder(cpi);
+ rewind(file);
+ vpx_img_free(&img);
+}
+
+std::vector<std::vector<double>> SimpleEncode::ObserveFirstPassStats() {
+ std::vector<std::vector<double>> output_stats;
+ // TODO(angiebird): This function make several assumptions of
+ // FIRSTPASS_STATS. 1) All elements in FIRSTPASS_STATS are double except the
+ // last one. 2) The last entry of frame_stats is the total_stats.
+ // Change the code structure, so that we don't have to make these assumptions
+
+ // Note the last entry of frame_stats is the total_stats, we don't need it.
+ for (size_t i = 0; i < pimpl->frame_stats.size() - 1; ++i) {
+ double *buf_start = reinterpret_cast<double *>(&pimpl->frame_stats[i]);
+ // We use - 1 here because the last member in FIRSTPASS_STATS is not double
+ double *buf_end =
+ buf_start + sizeof(pimpl->frame_stats[i]) / sizeof(*buf_end) - 1;
+ std::vector<double> this_stats(buf_start, buf_end);
+ output_stats.push_back(this_stats);
+ }
+ return output_stats;
+}
+
+void SimpleEncode::StartEncode() {
+ vpx_rational_t frame_rate = make_vpx_rational(frame_rate_num, frame_rate_den);
VP9EncoderConfig oxcf = vp9_get_encoder_config(
frame_width, frame_height, frame_rate, target_bitrate, VPX_RC_LAST_PASS);
- pimpl->buffer_pool = (BufferPool *)vpx_calloc(1, sizeof(*pimpl->buffer_pool));
- vp9_initialize_enc();
- pimpl->cpi = vp9_create_compressor(&oxcf, pimpl->buffer_pool);
- vp9_update_compressor_with_img_fmt(pimpl->cpi, VPX_IMG_FMT_I420);
+ assert(pimpl->cpi == NULL);
+ pimpl->cpi = init_encoder(&oxcf, VPX_IMG_FMT_I420);
+ rewind(file);
}
-SimpleEncode::~SimpleEncode() {
- vpx_free(pimpl->buffer_pool);
- vp9_remove_compressor(pimpl->cpi);
+void SimpleEncode::EndEncode() {
+ free_encoder(pimpl->cpi);
pimpl->cpi = nullptr;
+ rewind(file);
}
+
+SimpleEncode::~SimpleEncode() {}