From patchwork Wed Nov 14 20:56:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Oerlemans X-Patchwork-Id: 11025 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 71AB444CC16 for ; Wed, 14 Nov 2018 22:56:43 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 50A00688338; Wed, 14 Nov 2018 22:56:05 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf1-f202.google.com (mail-pf1-f202.google.com [209.85.210.202]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id AF3D1680D20 for ; Wed, 14 Nov 2018 22:55:58 +0200 (EET) Received: by mail-pf1-f202.google.com with SMTP id b88-v6so14154701pfj.4 for ; Wed, 14 Nov 2018 12:56:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=O2g/7I2+qrJ5Hj8u6DdC7A8BFUUIODMDKqi6EURDUT8=; b=oa6GHYvqOfRt+C8BlEhnT6pBIJ211+clU5mS7QwxyxcbLA9mp7+dTFQBBj49HQQbNG /lUHr7nH864QItpBO3t0cffcwcX/SH5PZ0AbV+sZeljvaICyW8UcYKl6Nq6Yajk6NMCz wqfm+DPMf0Niqik4a8++2pkeuHs2lHZj999tGVvhsXng5E/K7wLGD600PIFLl9+WJ3e2 YlqtC8tcDr+Gsdwc169HW/KBB83LnPJ2zj3F40UPrG8MUxP0yyAjALTKnUhNcwPBWNOs pKMFz+OQD05yT77zGM2wRddXAhYR7Fe0V5hUqYRKcQsKr83mbVLIsyYcB7RSBebt2oTp iU3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=O2g/7I2+qrJ5Hj8u6DdC7A8BFUUIODMDKqi6EURDUT8=; b=PenNA/NT9nwhkh/3IpYOXpdKfw3pT5e0IkVxgVnBHjXH8Yez5aJftmbg6Okyy+d77n YxrDOAipntNSTKsddZsxELe+GVwXoDXcJrBg9D36QufvG6xwoA9tyJlQuvFfsXTRtMFq 50HnyiIsRXnI52kiWTtOvrDbhoNfAIyMbtvNQo8GAr6hii25oGojZFsWajA5uC+5brVS 1gTW4q8J5dC2AQlWuGFowHoDf1ByykN0u1Mp1KX77lkP5s3y1tEG6XXsCdhiuLAr0LFy 4IXQlWbf5dtvj2nIRXsur3M6GeOjdI6fyku/6i+8Wl+Can7L9+IL1YW0+IMMKwNOxTcW hk0Q== X-Gm-Message-State: AGRZ1gLCKNOOq+kb9KKS6YaJufx8KPwUiqGca8TQD5LD9yqFx4RXInZt veuY8859HwDHBgbTeGLIXQProaFH+fULwUgcXMb6V8c/KUe3+L2Ygv5Xd+axHztFhJ+h7fNJMnj vioMWXtzQ6o9fqQMTmxnsktiK9Ih2A8l+kTcL4UBBfJ3YFlIfSZ8ExUo/BvQdqtklQhjethijQ0 YtC7s= X-Google-Smtp-Source: AJdET5cjKX0jD6E9UfO4Cby4cnG9r9t38Op1n9hGgPQrYNphwmelyZmR+9iGL87iBXHzJW+fyfcQPIFDE8mc60PeQXo= X-Received: by 2002:a17:902:7803:: with SMTP id p3-v6mr791359pll.74.1542228995214; Wed, 14 Nov 2018 12:56:35 -0800 (PST) Date: Wed, 14 Nov 2018 12:56:31 -0800 In-Reply-To: Message-Id: <20181114205631.69593-1-ardoerlemans@google.com> Mime-Version: 1.0 References: X-Mailer: git-send-email 2.19.1.930.g4563a0d9d0-goog From: Ard Oerlemans To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH] avcodec/libvpxenc: add VP8 temporal scalability configuration options X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Ard Oerlemans Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This commit adds configuration options to libvpxenc.c that can be used to enable VP8 temporal scalability. It also adds a way to programmatically set the per-frame encoding flags which can be used to control usage and updates of reference frames while encoding with temporal scalability enabled. --- doc/encoders.texi | 28 +++++++++++++++ libavcodec/libvpxenc.c | 81 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/doc/encoders.texi b/doc/encoders.texi index c9464ca7b3..39c3830dd5 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -1655,6 +1655,34 @@ Set number of frames to look ahead for frametype and ratecontrol. @item error-resilient Enable error resiliency features. +@item VP8-specific options +@table @option +@item ts-parameters +Sets the temporal scalability configuration using a :-separated list of +key=value pairs. For example, to specify temporal scalability parameters +with @code{ffmpeg}: +@example +ffmpeg -i INPUT -c:v libvpx -ts-parameters ts_number_layers=3:\ +ts_target_bitrate=250000,500000,1000000:ts_rate_decimator=4,2,1:\ +ts_periodicity=4:ts_layer_id=0,2,1,2 OUTPUT +@end example +Below is a brief explanation of each of the parameters, please +refer to @code{struct vpx_codec_enc_cfg} in @code{vpx/vpx_encoder.h} for more +details. +@table @option +@item ts_number_layers +Number of temporal coding layers. +@item ts_target_bitrate +Target bitrate for each temporal layer. +@item ts_rate_decimator +Frame rate decimation factor for each temporal layer. +@item ts_periodicity +Length of the sequence defining frame temporal layer membership. +@item ts_layer_id +Template defining the membership of frames to temporal layers. +@end table +@end table + @item VP9-specific options @table @option @item lossless diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index ad440a9c21..774fda457e 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -33,6 +33,7 @@ #include "libavutil/avassert.h" #include "libvpx.h" #include "profiles.h" +#include "libavutil/avstring.h" #include "libavutil/base64.h" #include "libavutil/common.h" #include "libavutil/internal.h" @@ -98,6 +99,8 @@ typedef struct VPxEncoderContext { int rc_undershoot_pct; int rc_overshoot_pct; + char *vp8_ts_parameters; + // VP9-only int lossless; int tile_columns; @@ -169,6 +172,7 @@ static av_cold void dump_enc_cfg(AVCodecContext *avctx, { int width = -30; int level = AV_LOG_DEBUG; + int i; av_log(avctx, level, "vpx_codec_enc_cfg\n"); av_log(avctx, level, "generic settings\n" @@ -208,6 +212,25 @@ static av_cold void dump_enc_cfg(AVCodecContext *avctx, " %*s%u\n %*s%u\n", width, "rc_undershoot_pct:", cfg->rc_undershoot_pct, width, "rc_overshoot_pct:", cfg->rc_overshoot_pct); + av_log(avctx, level, "temporal layering settings\n" + " %*s%u\n", width, "ts_number_layers:", cfg->ts_number_layers); + av_log(avctx, level, + "\n %*s", width, "ts_target_bitrate:"); + for (i = 0; i < VPX_TS_MAX_LAYERS; i++) + av_log(avctx, level, "%u ", cfg->ts_target_bitrate[i]); + av_log(avctx, level, "\n"); + av_log(avctx, level, + "\n %*s", width, "ts_rate_decimator:"); + for (i = 0; i < VPX_TS_MAX_LAYERS; i++) + av_log(avctx, level, "%u ", cfg->ts_rate_decimator[i]); + av_log(avctx, level, "\n"); + av_log(avctx, level, + "\n %*s%u\n", width, "ts_periodicity:", cfg->ts_periodicity); + av_log(avctx, level, + "\n %*s", width, "ts_layer_id:"); + for (i = 0; i < VPX_TS_MAX_PERIODICITY; i++) + av_log(avctx, level, "%u ", cfg->ts_layer_id[i]); + av_log(avctx, level, "\n"); av_log(avctx, level, "decoder buffer model\n" " %*s%u\n %*s%u\n %*s%u\n", width, "rc_buf_sz:", cfg->rc_buf_sz, @@ -325,6 +348,40 @@ static av_cold int vpx_free(AVCodecContext *avctx) return 0; } +static void vp8_ts_parse_int_array(int *dest, char *value, size_t value_len, int max_entries) +{ + int dest_idx = 0; + char *saveptr = NULL; + char *token = av_strtok(value, ",", &saveptr); + + while (token && dest_idx < max_entries) + { + dest[dest_idx++] = strtoul(token, NULL, 10); + token = av_strtok(NULL, ",", &saveptr); + } +} + +static int vp8_ts_param_parse(struct vpx_codec_enc_cfg *enccfg, char *key, char *value) +{ + size_t value_len = strlen(value); + + if (!value_len) + return -1; + + if (!strcmp(key, "ts_number_layers")) + enccfg->ts_number_layers = strtoul(value, &value, 10); + else if (!strcmp(key, "ts_target_bitrate")) + vp8_ts_parse_int_array(enccfg->ts_target_bitrate, value, value_len, VPX_TS_MAX_LAYERS); + else if (!strcmp(key, "ts_rate_decimator")) + vp8_ts_parse_int_array(enccfg->ts_rate_decimator, value, value_len, VPX_TS_MAX_LAYERS); + else if (!strcmp(key, "ts_periodicity")) + enccfg->ts_periodicity = strtoul(value, &value, 10); + else if (!strcmp(key, "ts_layer_id")) + vp8_ts_parse_int_array(enccfg->ts_layer_id, value, value_len, VPX_TS_MAX_PERIODICITY); + + return 0; +} + #if CONFIG_LIBVPX_VP9_ENCODER static int set_pix_fmt(AVCodecContext *avctx, vpx_codec_caps_t codec_caps, struct vpx_codec_enc_cfg *enccfg, vpx_codec_flags_t *flags, @@ -639,6 +696,22 @@ FF_ENABLE_DEPRECATION_WARNINGS enccfg.g_error_resilient = ctx->error_resilient || ctx->flags & VP8F_ERROR_RESILIENT; + if (CONFIG_LIBVPX_VP8_ENCODER && avctx->codec_id == AV_CODEC_ID_VP8 && ctx->vp8_ts_parameters) { + AVDictionary *dict = NULL; + AVDictionaryEntry* en = NULL; + + if (!av_dict_parse_string(&dict, ctx->vp8_ts_parameters, "=", ":", 0)) { + while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { + if (vp8_ts_param_parse(&enccfg, en->key, en->value) < 0) + av_log(avctx, AV_LOG_WARNING, + "Error parsing option '%s = %s'.\n", + en->key, en->value); + } + + av_dict_free(&dict); + } + } + dump_enc_cfg(avctx, &enccfg); /* Construct Encoder Context */ res = vpx_codec_enc_init(&ctx->encoder, iface, &enccfg, flags); @@ -1029,6 +1102,12 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, #endif if (frame->pict_type == AV_PICTURE_TYPE_I) flags |= VPX_EFLAG_FORCE_KF; + if (CONFIG_LIBVPX_VP8_ENCODER && avctx->codec_id == AV_CODEC_ID_VP8 && frame->metadata) { + AVDictionaryEntry* en = av_dict_get(frame->metadata, "vp8-flags", NULL, 0); + if (en) { + flags |= strtoul(en->value, NULL, 10); + } + } } res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp, @@ -1121,6 +1200,8 @@ static const AVOption vp8_options[] = { { "auto-alt-ref", "Enable use of alternate reference " "frames (2-pass only)", OFFSET(auto_alt_ref), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE}, { "cpu-used", "Quality/Speed ratio modifier", OFFSET(cpu_used), AV_OPT_TYPE_INT, {.i64 = 1}, -16, 16, VE}, + { "ts-parameters", "Temporal scaling configuration using a " + ":-separated list of key=value parameters", OFFSET(vp8_ts_parameters), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, VE}, LEGACY_OPTIONS { NULL } };