[FFmpeg-devel] [PATCH] avcodec: add libkvazaar HECV encoder

Hendrik Leppkes h.leppkes at gmail.com
Tue Jul 14 09:29:11 CEST 2015


On Tue, Jul 14, 2015 at 7:53 AM, Arttu Ylä-Outinen
<arttu.yla-outinen at tut.fi> wrote:
> Signed-off-by: Arttu Ylä-Outinen <arttu.yla-outinen at tut.fi>
> ---
>  Changelog               |    1 +
>  configure               |    4 +
>  doc/encoders.texi       |   24 +++++
>  doc/general.texi        |   10 +-
>  libavcodec/Makefile     |    1 +
>  libavcodec/allcodecs.c  |    1 +
>  libavcodec/libkvazaar.c |  248 +++++++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/version.h    |    2 +-
>  8 files changed, 289 insertions(+), 2 deletions(-)
>  create mode 100644 libavcodec/libkvazaar.c
>
> diff --git a/Changelog b/Changelog
> index ccd8d4a..a4451f9 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -15,6 +15,7 @@ version <next>:
>  - adrawgraph audio and drawgraph video filter
>  - removegrain video filter
>  - Intel QSV-accelerated MPEG-2 video and HEVC encoding
> +- libkvazaar HEVC encoder
>
>
>  version 2.7:
> diff --git a/configure b/configure
> index 88ee936..5616b59 100755
> --- a/configure
> +++ b/configure
> @@ -222,6 +222,7 @@ External library support:
>    --enable-libgsm          enable GSM de/encoding via libgsm [no]
>    --enable-libiec61883     enable iec61883 via libiec61883 [no]
>    --enable-libilbc         enable iLBC de/encoding via libilbc [no]
> +  --enable-libkvazaar      enable HEVC encoding via libkvazaar [no]
>    --enable-libmfx          enable HW acceleration through libmfx
>    --enable-libmodplug      enable ModPlug via libmodplug [no]
>    --enable-libmp3lame      enable MP3 encoding via libmp3lame [no]
> @@ -1386,6 +1387,7 @@ EXTERNAL_LIBRARY_LIST="
>      libgsm
>      libiec61883
>      libilbc
> +    libkvazaar
>      libmfx
>      libmodplug
>      libmp3lame
> @@ -2464,6 +2466,7 @@ libgsm_ms_decoder_deps="libgsm"
>  libgsm_ms_encoder_deps="libgsm"
>  libilbc_decoder_deps="libilbc"
>  libilbc_encoder_deps="libilbc"
> +libkvazaar_encoder_deps="libkvazaar"
>  libmodplug_demuxer_deps="libmodplug"
>  libmp3lame_encoder_deps="libmp3lame"
>  libmp3lame_encoder_select="audio_frame_queue"
> @@ -5152,6 +5155,7 @@ enabled libgsm            && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do
>                                     check_lib "${gsm_hdr}" gsm_create -lgsm && break;
>                                 done || die "ERROR: libgsm not found"; }
>  enabled libilbc           && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -lilbc
> +enabled libkvazaar        && require2 libkvazaar kvazaar.h kvz_api_get -lkvazaar
>  enabled libmfx            && require_pkg_config libmfx "mfx/mfxvideo.h" MFXInit
>  enabled libmodplug        && require_pkg_config libmodplug libmodplug/modplug.h ModPlug_Load
>  enabled libmp3lame        && require "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame
> diff --git a/doc/encoders.texi b/doc/encoders.texi
> index 5946644..6e50a90 100644
> --- a/doc/encoders.texi
> +++ b/doc/encoders.texi
> @@ -2315,6 +2315,30 @@ Setting a higher @option{bits_per_mb} limit will improve the speed.
>  For the fastest encoding speed set the @option{qscale} parameter (4 is the
>  recommended value) and do not set a size constraint.
>
> + at section libkvazaar
> +
> +Kvazaar H.265/HEVC encoder.
> +
> +Requires the presence of the libkvazaar headers and library during
> +configuration. You need to explicitly configure the build with
> + at option{--enable-libkvazaar}.
> +
> + at subsection Options
> +
> + at table @option
> +
> + at item b
> +Set target video bitrate in bit/s and enable rate control.
> +
> + at item threads
> +Set number of encoding threads.
> +
> + at item kvazaar-params
> +Set kvazaar parameters as a list of @var{name}=@var{value} pairs separated
> +by commas (,). See kvazaar documentation for a list of options.
> +
> + at end table
> +
>  @c man end VIDEO ENCODERS
>
>  @chapter Subtitles Encoders
> diff --git a/doc/general.texi b/doc/general.texi
> index 5089c36..dc22d90 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -145,6 +145,14 @@ x265 is under the GNU Public License Version 2 or later
>  details), you must upgrade FFmpeg's license to GPL in order to use it.
>  @end float
>
> + at section kvazaar
> +
> +FFmpeg can make use of the kvazaar library for HEVC encoding.
> +
> +Go to @url{https://github.com/ultravideo/kvazaar} and follow the
> +instructions for installing the library. Then pass
> + at code{--enable-libkvazaar} to configure to enable it.
> +
>  @section libilbc
>
>  iLBC is a narrowband speech codec that has been made freely available
> @@ -688,7 +696,7 @@ following image formats are supported:
>  @item H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10  @tab  E  @tab  X
>      @tab encoding supported through external library libx264 and OpenH264
>  @item HEVC                   @tab  X  @tab  X
> -    @tab encoding supported through the external library libx265
> +    @tab encoding supported through external library libx265 and libkvazaar
>  @item HNM version 4          @tab     @tab  X
>  @item HuffYUV                @tab  X  @tab  X
>  @item HuffYUV FFmpeg variant @tab  X  @tab  X
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 70755f6..b7fe1c9 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -768,6 +768,7 @@ OBJS-$(CONFIG_LIBGSM_MS_DECODER)          += libgsmdec.o
>  OBJS-$(CONFIG_LIBGSM_MS_ENCODER)          += libgsmenc.o
>  OBJS-$(CONFIG_LIBILBC_DECODER)            += libilbc.o
>  OBJS-$(CONFIG_LIBILBC_ENCODER)            += libilbc.o
> +OBJS-$(CONFIG_LIBKVAZAAR_ENCODER)         += libkvazaar.o
>  OBJS-$(CONFIG_LIBMP3LAME_ENCODER)         += libmp3lame.o mpegaudiodecheader.o
>  OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER)  += libopencore-amr.o
>  OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER)  += libopencore-amr.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 70c81c7..a352a47 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -530,6 +530,7 @@ void avcodec_register_all(void)
>      REGISTER_ENCDEC (LIBGSM,            libgsm);
>      REGISTER_ENCDEC (LIBGSM_MS,         libgsm_ms);
>      REGISTER_ENCDEC (LIBILBC,           libilbc);
> +    REGISTER_ENCODER(LIBKVAZAAR,        libkvazaar);

This entry should be added further down in the file, in the section of
"external libraries that shouldn't be used by default", so that
libx265 remains the default if both are available.
Unless you can convince us how this one is much better and more complete? :)

>      REGISTER_ENCODER(LIBMP3LAME,        libmp3lame);
>      REGISTER_ENCDEC (LIBOPENCORE_AMRNB, libopencore_amrnb);
>      REGISTER_DECODER(LIBOPENCORE_AMRWB, libopencore_amrwb);
> diff --git a/libavcodec/libkvazaar.c b/libavcodec/libkvazaar.c
> new file mode 100644
> index 0000000..6742090
> --- /dev/null
> +++ b/libavcodec/libkvazaar.c
> @@ -0,0 +1,248 @@
> +/*
> + * libkvazaar encoder
> + *
> + * Copyright (c) 2015 Tampere University of Technology
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <kvazaar.h>
> +#include <string.h>
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/dict.h"
> +#include "libavutil/opt.h"
> +#include "avcodec.h"
> +#include "internal.h"
> +
> +typedef struct LibkvazaarContext {
> +    const AVClass *class;
> +
> +    const kvz_api *api;
> +    kvz_encoder *encoder;
> +    kvz_config *config;
> +
> +    char *kvz_params;
> +} LibkvazaarContext;
> +
> +static av_cold int libkvazaar_init(AVCodecContext *avctx)
> +{
> +    int retval = 0;
> +    kvz_config *cfg = NULL;
> +    kvz_encoder *enc = NULL;
> +    const kvz_api *const api = kvz_api_get(8);
> +
> +    LibkvazaarContext *const ctx = avctx->priv_data;
> +
> +    // Kvazaar requires width and height to be multiples of eight.
> +    if (avctx->width % 8 || avctx->height % 8) {
> +        av_log(avctx, AV_LOG_ERROR, "Video dimensions are not a multiple of 8.\n");
> +        retval = AVERROR_INVALIDDATA;
> +        goto done;
> +    }
> +
> +    cfg = api->config_alloc();
> +    if (!cfg) {
> +        av_log(avctx, AV_LOG_ERROR, "Could not allocate kvazaar config structure.\n");
> +        retval = AVERROR(ENOMEM);
> +        goto done;
> +    }
> +
> +    if (!api->config_init(cfg)) {
> +        av_log(avctx, AV_LOG_ERROR, "Could not initialize kvazaar config structure.\n");
> +        retval = AVERROR_EXTERNAL;
> +        goto done;
> +    }
> +
> +    cfg->width = avctx->width;
> +    cfg->height = avctx->height;
> +    cfg->framerate =
> +      (double)(avctx->time_base.num * avctx->ticks_per_frame) / avctx->time_base.den;
> +    cfg->threads = avctx->thread_count;
> +    cfg->target_bitrate = avctx->bit_rate;
> +    cfg->vui.sar_width = avctx->sample_aspect_ratio.num;
> +    cfg->vui.sar_height = avctx->sample_aspect_ratio.den;
> +
> +    if (ctx->kvz_params) {
> +        AVDictionary *dict = NULL;
> +        if (!av_dict_parse_string(&dict, ctx->kvz_params, "=", ",", 0)) {
> +            AVDictionaryEntry *entry = NULL;
> +            while ((entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX))) {
> +                if (!api->config_parse(cfg, entry->key, entry->value)) {
> +                    av_log(avctx, AV_LOG_WARNING,
> +                           "Invalid option: %s=%s.\n",
> +                           entry->key, entry->value);
> +                }
> +            }
> +            av_dict_free(&dict);
> +        }
> +    }
> +
> +    enc = api->encoder_open(cfg);
> +    if (!enc) {
> +        av_log(avctx, AV_LOG_ERROR, "Could not open kvazaar encoder.\n");
> +        retval = AVERROR_EXTERNAL;
> +        goto done;
> +    }
> +
> +    ctx->api = api;
> +    ctx->encoder = enc;
> +    ctx->config = cfg;
> +    enc = NULL;
> +    cfg = NULL;
> +
> +done:
> +    if (cfg) api->config_destroy(cfg);
> +    if (enc) api->encoder_close(enc);
> +
> +    return retval;
> +}
> +
> +static av_cold int libkvazaar_close(AVCodecContext *avctx)
> +{
> +    LibkvazaarContext *ctx = avctx->priv_data;
> +    if (!ctx->api) return 0;
> +
> +    if (ctx->encoder) {
> +        ctx->api->encoder_close(ctx->encoder);
> +        ctx->encoder = NULL;
> +    }
> +
> +    if (ctx->config) {
> +        ctx->api->config_destroy(ctx->config);
> +        ctx->config = NULL;
> +    }
> +
> +    return 0;
> +}
> +
> +static int libkvazaar_encode(AVCodecContext *avctx,
> +                             AVPacket *avpkt,
> +                             const AVFrame *frame,
> +                             int *got_packet_ptr)
> +{
> +    int retval = 0;
> +    kvz_picture *img_in = NULL;
> +    kvz_data_chunk *data_out = NULL;
> +    uint32_t len_out = 0;
> +    LibkvazaarContext *ctx = avctx->priv_data;
> +
> +    *got_packet_ptr = 0;
> +
> +    if (frame) {
> +        int i = 0;
> +
> +        av_assert0(frame->width == ctx->config->width);
> +        av_assert0(frame->height == ctx->config->height);
> +        av_assert0(frame->format == avctx->pix_fmt);
> +
> +        // Allocate input picture for kvazaar.
> +        img_in = ctx->api->picture_alloc(frame->width, frame->height);
> +        if (!img_in) {
> +            av_log(avctx, AV_LOG_ERROR, "Failed to allocate picture.\n");
> +            retval = AVERROR(ENOMEM);
> +            goto done;
> +        }
> +
> +        // Copy pixels from frame to img_in.
> +        for (i = 0; i < 3; ++i) {
> +            uint8_t *dst = img_in->data[i];
> +            uint8_t *src = frame->data[i];
> +            int width = (i == 0) ? frame->width : (frame->width / 2);
> +            int height = (i == 0) ? frame->height : (frame->height / 2);
> +            int y = 0;
> +            for (y = 0; y < height; ++y) {
> +                memcpy(dst, src, width);
> +                src += frame->linesize[i];
> +                dst += width;
> +            }
> +        }
> +    }
> +
> +    if (!ctx->api->encoder_encode(ctx->encoder, img_in, &data_out, &len_out, NULL)) {
> +        av_log(avctx, AV_LOG_ERROR, "Failed to encode frame.\n");
> +        retval = AVERROR_EXTERNAL;
> +        goto done;
> +    }
> +
> +    if (data_out) {
> +        kvz_data_chunk *chunk = NULL;
> +        uint64_t written = 0;
> +
> +        retval = ff_alloc_packet(avpkt, len_out);
> +        if (retval < 0) {
> +            av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");
> +            goto done;
> +        }
> +
> +        for (chunk = data_out; chunk != NULL; chunk = chunk->next) {
> +            av_assert0(written + chunk->len <= len_out);
> +            memcpy(avpkt->data + written, chunk->data, chunk->len);
> +            written += chunk->len;
> +        }
> +        *got_packet_ptr = 1;
> +
> +        ctx->api->chunk_free(data_out);
> +        data_out = NULL;
> +    }
> +
> +done:
> +    if (img_in) ctx->api->picture_free(img_in);
> +    if (data_out) ctx->api->chunk_free(data_out);
> +    return retval;
> +}
> +
> +static const enum AVPixelFormat pix_fmts[] = {
> +    AV_PIX_FMT_YUV420P,
> +    AV_PIX_FMT_NONE
> +};
> +
> +static const AVOption options[] = {
> +    { "kvazaar-params", "Set kvazaar parameters as a comma-separated list of name=value pairs.",
> +      offsetof(LibkvazaarContext, kvz_params), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0,
> +      AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
> +    { NULL },
> +};
> +
> +static const AVClass class = {
> +    .class_name = "libkvazaar",
> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +static const AVCodecDefault defaults[] = {
> +    { "b", "0" },
> +    { NULL },
> +};
> +
> +AVCodec ff_libkvazaar_encoder = {
> +    .name             = "libkvazaar",
> +    .long_name        = NULL_IF_CONFIG_SMALL("libkvazaar H.265 / HEVC"),
> +    .type             = AVMEDIA_TYPE_VIDEO,
> +    .id               = AV_CODEC_ID_HEVC,
> +    .capabilities     = CODEC_CAP_DELAY,
> +    .pix_fmts         = pix_fmts,
> +
> +    .priv_class       = &class,
> +    .priv_data_size   = sizeof(LibkvazaarContext),
> +    .defaults         = defaults,
> +
> +    .init             = libkvazaar_init,
> +    .encode2          = libkvazaar_encode,
> +    .close            = libkvazaar_close,
> +};
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index ee21f5d..df58e0a 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -29,7 +29,7 @@
>  #include "libavutil/version.h"
>
>  #define LIBAVCODEC_VERSION_MAJOR 56
> -#define LIBAVCODEC_VERSION_MINOR  47
> +#define LIBAVCODEC_VERSION_MINOR  48
>  #define LIBAVCODEC_VERSION_MICRO 100
>
>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> --
> 1.7.9.5
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list