From patchwork Thu Nov 8 20:44:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Oerlemans X-Patchwork-Id: 10956 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 8920144D7FF for ; Thu, 8 Nov 2018 22:45:04 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id F412E68A02A; Thu, 8 Nov 2018 22:44:35 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-io1-f74.google.com (mail-io1-f74.google.com [209.85.166.74]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 23237689FE1 for ; Thu, 8 Nov 2018 22:44:29 +0200 (EET) Received: by mail-io1-f74.google.com with SMTP id o8-v6so25007955iob.19 for ; Thu, 08 Nov 2018 12:45:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:message-id:mime-version:subject:from:to:cc; bh=iMsOEPLfnVElJUvr0bGN+UZsCusYurBpFLMT1Nh5orY=; b=iYE/SBnIqjMMwnNfOPQeSiFAQiwxrXvqDpNatbEr2iEueOh36a35LMbScIYiwBLiU4 e1atF5y86YH0rr9UAjPo7+51sj4B1SzPVX0+FtHde9REIbg04vZNmmz22beGia6Ixbo6 YQBMyqZ18hBrCNpxT8ufF8A1cFcM3QU4kj+4G9GW0xTiLPLdTJyBZMPJTx8oVHJC/qnI 66rH04/kV1RFpncc+sl3bA4Gdh/3Sz13CL+7hv1hTFDr2Btktk8kcS57Q07e548opTem LuHxEdz71sXTPp5RkC4uuikd+opVwsPxMnCDmnwlxiy1H3JxrDQjU8BXXdyfyiRucKpv YgRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:mime-version:subject:from:to:cc; bh=iMsOEPLfnVElJUvr0bGN+UZsCusYurBpFLMT1Nh5orY=; b=Joxt2PpkduLMIkZPBjwdJkQW0NOdF1rzZ8xxgrwvHbhPJI7Ybwg8oXAxMnzuIqa1BW nl1oiWmI5ZdBii1NWJVd4g8ugnF/NgG7iLsNbzn3v2uyLuU3x50i3KepHykFjD34TIYx jpCN/9ZpPQlAAtzjeq+HP6Uf/DVwklNebWzhBZ8a2s4zMnjS3giROeUVIU6tb7vV2fZU WAybzvuai7GJROAh9AAuewHR4Hc93sWceV0E3pc5+luQVbhhlRHeYrzYY4fK+AeJKA+O UqSE7gErMmpMYYd8Pg3mA2mCDtI2eCHxqB5dchRiHMpQLcwWmb7jZPNpGfltT4kMqAwg dc/g== X-Gm-Message-State: AGRZ1gKVN5ebALdEjN2PSPlxetu1vcr8IQzDvVqv7ddoLjua7X2slN/z LAc7Fjs/iTcGELZvML+ha+bNsxPS1EqqtlRzi33UGi+aRM99PFm+8WrCc/065CGRkpIP5CQxoXL SANUCXX0FOfqdeeT6aAn17obJdhZfw/2yXDCGUNr92ojNRrB6Sppk/+TdGmuAHwlaXKBZ5DglMu 6YjxQ= X-Google-Smtp-Source: AJdET5dl8yZ9r/Bp8ZkyC0MJ6FpZhb5zNe4tta7o15cQ+sIcFzZDRODg99xKwhGfvwdbtcB3R/EmMbYqsENEUlaqqlc= X-Received: by 2002:a24:1882:: with SMTP id 124mr2250345itr.0.1541709903048; Thu, 08 Nov 2018 12:45:03 -0800 (PST) Date: Thu, 8 Nov 2018 12:44:42 -0800 Message-Id: <20181108204442.133420-1-ardoerlemans@google.com> Mime-Version: 1.0 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 | 85 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 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..206f17f4cd 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,41 @@ static av_cold int vpx_free(AVCodecContext *avctx) return 0; } +#if CONFIG_LIBVPX_VP8_ENCODER +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; +} +#endif + #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 +693,24 @@ FF_ENABLE_DEPRECATION_WARNINGS enccfg.g_error_resilient = ctx->error_resilient || ctx->flags & VP8F_ERROR_RESILIENT; +#if CONFIG_LIBVPX_VP8_ENCODER + if (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); + } + } +#endif + dump_enc_cfg(avctx, &enccfg); /* Construct Encoder Context */ res = vpx_codec_enc_init(&ctx->encoder, iface, &enccfg, flags); @@ -1021,6 +1097,14 @@ 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 + if (frame->metadata) { + AVDictionaryEntry* en = av_dict_get(frame->metadata, "vp8-flags", NULL, 0); + if (en) { + flags |= strtoul(en->value, NULL, 10); + } + } +#endif } res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp, @@ -1113,6 +1197,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 } };