From b0c146eea9cdd3f43dbf82574ab037e08f1da55c Mon Sep 17 00:00:00 2001 From: Minghai Shang Date: Fri, 28 Feb 2014 14:22:57 -0800 Subject: [svc] 1. Add two pass RC options in vp9_spatial_scalable_encoder. 2. Add read/write for RC stats file The two pass RC for svc does not work yet. This is just the first step. We need further development to make it working. Change-Id: I8ef0e177dff0b5ed3c97a916beea5123717cc6f2 --- examples.mk | 1 + examples/vp9_spatial_scalable_encoder.c | 95 +++++++++++++++++++++++++++++---- vpx/exports_enc | 2 + vpx/src/svc_encodeframe.c | 42 ++++++++++++++- vpx/svc_context.h | 11 ++++ 5 files changed, 139 insertions(+), 12 deletions(-) diff --git a/examples.mk b/examples.mk index 85b84577c..870ddaad7 100644 --- a/examples.mk +++ b/examples.mk @@ -62,6 +62,7 @@ vp9_spatial_scalable_encoder.SRCS += ivfenc.c ivfenc.h vp9_spatial_scalable_encoder.SRCS += tools_common.c tools_common.h vp9_spatial_scalable_encoder.SRCS += video_common.h vp9_spatial_scalable_encoder.SRCS += video_writer.h video_writer.c +vp9_spatial_scalable_encoder.SRCS += vpxstats.c vpxstats.h vp9_spatial_scalable_encoder.GUID = 4A38598D-627D-4505-9C7B-D4020C84100D vp9_spatial_scalable_encoder.DESCRIPTION = Spatial Scalable Encoder diff --git a/examples/vp9_spatial_scalable_encoder.c b/examples/vp9_spatial_scalable_encoder.c index 98dc3f5f9..5333b11f1 100644 --- a/examples/vp9_spatial_scalable_encoder.c +++ b/examples/vp9_spatial_scalable_encoder.c @@ -26,6 +26,7 @@ #include "vpx/svc_context.h" #include "vpx/vp8cx.h" #include "vpx/vpx_encoder.h" +#include "./vpxstats.h" static const struct arg_enum_list encoding_mode_enum[] = { {"i", INTER_LAYER_PREDICTION_I}, @@ -60,12 +61,19 @@ static const arg_def_t quantizers_arg = static const arg_def_t quantizers_keyframe_arg = ARG_DEF("qn", "quantizers-keyframe", 1, "quantizers for key frames (lowest " "to highest layer)"); +static const arg_def_t passes_arg = + ARG_DEF("p", "passes", 1, "Number of passes (1/2)"); +static const arg_def_t pass_arg = + ARG_DEF(NULL, "pass", 1, "Pass to execute (1/2)"); +static const arg_def_t fpf_name_arg = + ARG_DEF(NULL, "fpf", 1, "First pass statistics file name"); static const arg_def_t *svc_args[] = { &encoding_mode_arg, &frames_arg, &width_arg, &height_arg, &timebase_arg, &bitrate_arg, &skip_frames_arg, &layers_arg, &kf_dist_arg, &scale_factors_arg, &quantizers_arg, - &quantizers_keyframe_arg, NULL + &quantizers_keyframe_arg, &passes_arg, &pass_arg, + &fpf_name_arg, NULL }; static const SVC_ENCODING_MODE default_encoding_mode = @@ -85,6 +93,10 @@ typedef struct { const char *output_filename; uint32_t frames_to_code; uint32_t frames_to_skip; + struct VpxInputContext input_ctx; + stats_io_t rc_stats; + int passes; + int pass; } AppInput; static const char *exec_name; @@ -105,6 +117,9 @@ static void parse_command_line(int argc, const char **argv_, char **argi = NULL; char **argj = NULL; vpx_codec_err_t res; + int passes = 0; + int pass = 0; + const char *fpf_file_name = NULL; // initialize SvcContext with parameters that will be passed to vpx_svc_init svc_ctx->log_level = SVC_LOG_DEBUG; @@ -159,11 +174,53 @@ static void parse_command_line(int argc, const char **argv_, vpx_svc_set_quantizers(svc_ctx, arg.val, 0); } else if (arg_match(&arg, &quantizers_keyframe_arg, argi)) { vpx_svc_set_quantizers(svc_ctx, arg.val, 1); + } else if (arg_match(&arg, &passes_arg, argi)) { + passes = arg_parse_uint(&arg); + if (passes < 1 || passes > 2) { + die("Error: Invalid number of passes (%d)\n", passes); + } + } else if (arg_match(&arg, &pass_arg, argi)) { + pass = arg_parse_uint(&arg); + if (pass < 1 || pass > 2) { + die("Error: Invalid pass selected (%d)\n", pass); + } + } else if (arg_match(&arg, &fpf_name_arg, argi)) { + fpf_file_name = arg.val; } else { ++argj; } } + if (passes == 0 || passes == 1) { + if (pass) { + fprintf(stderr, "pass is ignored since there's only one pass\n"); + } + enc_cfg->g_pass = VPX_RC_ONE_PASS; + } else { + if (pass == 0) { + die("pass must be specified when passes is 2\n"); + } + + if (fpf_file_name == NULL) { + die("fpf must be specified when passes is 2\n"); + } + + if (pass == 1) { + enc_cfg->g_pass = VPX_RC_FIRST_PASS; + if (!stats_open_file(&app_input->rc_stats, fpf_file_name, 0)) { + fatal("Failed to open statistics store"); + } + } else { + enc_cfg->g_pass = VPX_RC_LAST_PASS; + if (!stats_open_file(&app_input->rc_stats, fpf_file_name, 1)) { + fatal("Failed to open statistics store"); + } + enc_cfg->rc_twopass_stats_in = stats_get(&app_input->rc_stats); + } + app_input->passes = passes; + app_input->pass = pass; + } + // Check for unrecognized options for (argi = argv; *argi; ++argi) if (argi[0][0] == '-' && strlen(argi[0]) > 1) @@ -234,10 +291,14 @@ int main(int argc, const char **argv) { VPX_CODEC_OK) { die("Failed to get output resolution"); } - writer = vpx_video_writer_open(app_input.output_filename, kContainerIVF, - &info); - if (!writer) - die("Failed to open %s for writing\n", app_input.output_filename); + + if (!(app_input.passes == 2 && app_input.pass == 1)) { + // We don't save the bitstream for the 1st pass on two pass rate control + writer = vpx_video_writer_open(app_input.output_filename, kContainerIVF, + &info); + if (!writer) + die("Failed to open %s for writing\n", app_input.output_filename); + } // skip initial frames for (i = 0; i < app_input.frames_to_skip; ++i) @@ -254,11 +315,18 @@ int main(int argc, const char **argv) { if (res != VPX_CODEC_OK) { die_codec(&codec, "Failed to encode frame"); } - if (vpx_svc_get_frame_size(&svc_ctx) > 0) { - vpx_video_writer_write_frame(writer, - vpx_svc_get_buffer(&svc_ctx), - vpx_svc_get_frame_size(&svc_ctx), - pts); + if (!(app_input.passes == 2 && app_input.pass == 1)) { + if (vpx_svc_get_frame_size(&svc_ctx) > 0) { + vpx_video_writer_write_frame(writer, + vpx_svc_get_buffer(&svc_ctx), + vpx_svc_get_frame_size(&svc_ctx), + pts); + } + } + if (vpx_svc_get_rc_stats_buffer_size(&svc_ctx) > 0) { + stats_write(&app_input.rc_stats, + vpx_svc_get_rc_stats_buffer(&svc_ctx), + vpx_svc_get_rc_stats_buffer_size(&svc_ctx)); } ++frame_cnt; pts += frame_duration; @@ -269,7 +337,12 @@ int main(int argc, const char **argv) { fclose(infile); if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec"); - vpx_video_writer_close(writer); + if (app_input.passes == 2) + stats_close(&app_input.rc_stats, 1); + + if (writer) { + vpx_video_writer_close(writer); + } vpx_img_free(&raw); diff --git a/vpx/exports_enc b/vpx/exports_enc index 99b1bfacc..155faf6f6 100644 --- a/vpx/exports_enc +++ b/vpx/exports_enc @@ -21,3 +21,5 @@ text vpx_svc_set_options text vpx_svc_set_quantizers text vpx_svc_set_scale_factors text vpx_svc_get_layer_resolution +text vpx_svc_get_rc_stats_buffer_size +text vpx_svc_get_rc_stats_buffer \ No newline at end of file diff --git a/vpx/src/svc_encodeframe.c b/vpx/src/svc_encodeframe.c index c7837244f..3e22fdf38 100644 --- a/vpx/src/svc_encodeframe.c +++ b/vpx/src/svc_encodeframe.c @@ -81,6 +81,10 @@ typedef struct SvcInternal { size_t buffer_size; void *buffer; + char *rc_stats_buf; + size_t rc_stats_buf_size; + size_t rc_stats_buf_used; + char message_buffer[2048]; vpx_codec_ctx_t *codec_ctx; } SvcInternal; @@ -569,7 +573,6 @@ vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, enc_cfg->ss_number_layers = si->layers; enc_cfg->ts_number_layers = 1; // Temporal layers not used in this encoder. enc_cfg->kf_mode = VPX_KF_DISABLED; - enc_cfg->g_pass = VPX_RC_ONE_PASS; // Lag in frames not currently supported enc_cfg->g_lag_in_frames = 0; @@ -851,6 +854,7 @@ vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, memset(&superframe, 0, sizeof(superframe)); svc_log_reset(svc_ctx); + si->rc_stats_buf_used = 0; si->layers = svc_ctx->spatial_layers; if (si->frame_within_gop >= si->kf_dist || @@ -923,6 +927,25 @@ vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, } break; } + case VPX_CODEC_STATS_PKT: { + size_t new_size = si->rc_stats_buf_used + + cx_pkt->data.twopass_stats.sz; + + if (new_size > si->rc_stats_buf_size) { + char *p = (char*)realloc(si->rc_stats_buf, new_size); + if (p == NULL) { + svc_log(svc_ctx, SVC_LOG_ERROR, "Error allocating stats buf\n"); + break; + } + si->rc_stats_buf = p; + si->rc_stats_buf_size = new_size; + } + + memcpy(si->rc_stats_buf + si->rc_stats_buf_used, + cx_pkt->data.twopass_stats.buf, cx_pkt->data.twopass_stats.sz); + si->rc_stats_buf_used += cx_pkt->data.twopass_stats.sz; + break; + } default: { break; } @@ -1077,7 +1100,24 @@ void vpx_svc_release(SvcContext *svc_ctx) { si = (SvcInternal *)svc_ctx->internal; if (si != NULL) { free(si->buffer); + if (si->rc_stats_buf) { + free(si->rc_stats_buf); + } free(si); svc_ctx->internal = NULL; } } + +size_t vpx_svc_get_rc_stats_buffer_size(const SvcContext *svc_ctx) { + const SvcInternal *const si = get_const_svc_internal(svc_ctx); + if (svc_ctx == NULL || si == NULL) return 0; + return si->rc_stats_buf_used; +} + +char *vpx_svc_get_rc_stats_buffer(const SvcContext *svc_ctx) { + const SvcInternal *const si = get_const_svc_internal(svc_ctx); + if (svc_ctx == NULL || si == NULL) return NULL; + return si->rc_stats_buf; +} + + diff --git a/vpx/svc_context.h b/vpx/svc_context.h index 98474ca91..5d0fbbd77 100644 --- a/vpx/svc_context.h +++ b/vpx/svc_context.h @@ -113,6 +113,17 @@ size_t vpx_svc_get_frame_size(const SvcContext *svc_ctx); */ void *vpx_svc_get_buffer(const SvcContext *svc_ctx); +/** + * return size of two pass rate control stats data to be returned by + * vpx_svc_get_rc_stats_buffer + */ +size_t vpx_svc_get_rc_stats_buffer_size(const SvcContext *svc_ctx); + +/** + * return buffer two pass of rate control stats data + */ +char *vpx_svc_get_rc_stats_buffer(const SvcContext *svc_ctx); + /** * return spatial resolution of the specified layer */ -- cgit v1.2.3