From patchwork Mon Nov 12 18:37:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Oerlemans X-Patchwork-Id: 11003 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 D34E344DF79 for ; Mon, 12 Nov 2018 20:37:45 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 4FE6168A441; Mon, 12 Nov 2018 20:37:17 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 272E968A353 for ; Mon, 12 Nov 2018 20:37:10 +0200 (EET) Received: by mail-pl1-f201.google.com with SMTP id w19-v6so7789329plq.1 for ; Mon, 12 Nov 2018 10:37:47 -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=PlJISG9RYcZmTmxvNQj6f64+XO8/AviHjWwk1d+6dqw=; b=lw6CIM9vfgMWX8C/r19cWbwW2Ch6SsSfeIkHtcwAEc3EzWqaDtTYjzc71tWMQ4+UQh m7SoHiNaAIBhAU57pp5Dls4XRQo9gu19W6pWmoJM1QUBKo8QvghJU6Mj7Ou90dgwM7iH /rAdy+nXse7j/37ZU/9w2Mzhh2yJ9eZ7eyjQionVpKgEvVhw3UyqpCDyG/Zgfw2Bd0dZ KR0x2utZqtmT9JvfucUC6/5LO8FPI8McqDja2BrDqYOJNfJUcC+zFBL7hYJ571eiMe5g 0+gF9Nf772g6U7f4E14LuP7x7VftIOJzTnaZxDetWe5tooRX5CS5xvDEV2KaMa07x8Ur 4alQ== 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=PlJISG9RYcZmTmxvNQj6f64+XO8/AviHjWwk1d+6dqw=; b=Mqv+HZFxCCPDkCWQ+29OMb7r0HS1hPKjFRBuCsz+n5T0t/CgH+Gf4FaV1c0MHkRLoI 2fn9gF+xJAv1KnBGcrUMGkT7fOCrsL/22Ak9NwoPYZWEnIW088TeXbtJYq/UILPO8Vnd KQAL33wJZUvLHWg3sEsCZOVNKn84UV7x0KM2lJFoMYf5jKXCyf3DKpDAMJacBgzTEscA j00gglh1OQiNGTaE4zN8KecByjQZzkX683KsIAaL7ooCb3boLWU+/LM1XdMjFHYZmOyA nlBVYgOxj58SXe6DyFP0H8PttJZ3xj4d1CtVVJJL0V66FFLu3MITYD/Fqjbn3ChtTSGA Zgsg== X-Gm-Message-State: AGRZ1gIJ9nQ03sB3DSw1uzAREgowVMHEJhOu1NQJoS0BYhTVgwOosBax nVRGqyCHxbFwpHX2OcFzOPja1NbIUcAWPu+q37jbWsTITU2pIylFv4+cruut2J+tIrcqqqL1wzD Gx7AYGukfnxy/JASUF8sxhZI4F3im99CTuful9uDhcVk/L/TqGgaqk+EFkGKIOyuykMz9gSD/w9 C/DWk= X-Google-Smtp-Source: AJdET5dXfpkwIuGft1WQO7d9kAoD/NdJwuMs3ObCMr+XaoWr9ioGgSIZR4fVgn7KQTO7ieVfgbdrP2nDeTh0fTz8uiM= X-Received: by 2002:a17:902:903:: with SMTP id 3-v6mr343951plm.32.1542047865742; Mon, 12 Nov 2018 10:37:45 -0800 (PST) Date: Mon, 12 Nov 2018 10:37:41 -0800 In-Reply-To: Message-Id: <20181112183741.17407-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" Thanks for the suggestion. I have updated the patch. Ard --- doc/encoders.texi | 28 +++++++++++++++ libavcodec/libvpxenc.c | 79 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/doc/encoders.texi b/doc/encoders.texi index 899faac49b..6ecd572ea3 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -1654,6 +1654,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 09f7a88452..161f952d65 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; @@ -165,6 +168,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" @@ -204,6 +208,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, @@ -321,6 +344,39 @@ 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 *value_end = value + value_len; + while (value < value_end && dest_idx < max_entries) { + dest[dest_idx] = strtoul(value, &value, 10); + while (value < value_end && (*value == ',' || av_isspace(*value))) + value++; + dest_idx++; + } +} + +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, @@ -635,6 +691,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); @@ -1021,6 +1093,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, @@ -1113,6 +1191,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, static const AVOption vp8_options[] = { COMMON_OPTIONS { "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 } };