diff mbox

[FFmpeg-devel,v12] - Added Turing codec interface for ffmpeg

Message ID 1499180071-21040-1-git-send-email-saverio.blasi@bbc.co.uk
State New
Headers show

Commit Message

Saverio Blasi July 4, 2017, 2:54 p.m. UTC
- This patch contains the changes to interface the Turing codec (http://turingcodec.org/) with ffmpeg. The patch was modified to address the comments in the review as follows:
  - Added a pkg-config file to list all dependencies required by libturing. This should address the issue pointed out by Hendrik Leppkes on Fri 18/11/2016
  - As per suggestions of wm4, two functions (add_option and finalise_options) have been created. The former appends new options while the latter sets up the argv array of pointers to char* accordingly. add_option re-allocates the buffer for options using av_realloc
  - Additionally, both these functions handle the errors in case the memory wasn't allocated correctly
  - malloc|free|realloc have been substituted with their corresponding av_{malloc|free|realloc} version
  - Check on bit-depth has been removed since the ffmpeg already casts the right pix_fmt and bit depth
  - pix_fmts is now set in ff_libturing_encoder as in h264dec.c.
  - Changed usage of av_free with av_freep and fixed calls to free arrays
  - Added brackets to all if and for statements
  - Avoid repetition of code to free arrays in case of failure to initialise the libturing encoder
  - Some fixes to address the review from wm4 and Mark Thompson received on Wed 08/02/2017
  - Fixed indentation
  - Version bump, removed strcpy() and excluding bool use in headers
---
 LICENSE.md             |   1 +
 configure              |   6 +
 libavcodec/Makefile    |   1 +
 libavcodec/allcodecs.c |   1 +
 libavcodec/libturing.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 327 insertions(+)
 create mode 100755 libavcodec/libturing.c

Comments

wm4 July 5, 2017, 2:10 p.m. UTC | #1
On Tue,  4 Jul 2017 14:54:31 +0000
Saverio Blasi <saverio.blasi@bbc.co.uk> wrote:

> - This patch contains the changes to interface the Turing codec (http://turingcodec.org/) with ffmpeg. The patch was modified to address the comments in the review as follows:
>   - Added a pkg-config file to list all dependencies required by libturing. This should address the issue pointed out by Hendrik Leppkes on Fri 18/11/2016
>   - As per suggestions of wm4, two functions (add_option and finalise_options) have been created. The former appends new options while the latter sets up the argv array of pointers to char* accordingly. add_option re-allocates the buffer for options using av_realloc
>   - Additionally, both these functions handle the errors in case the memory wasn't allocated correctly
>   - malloc|free|realloc have been substituted with their corresponding av_{malloc|free|realloc} version
>   - Check on bit-depth has been removed since the ffmpeg already casts the right pix_fmt and bit depth
>   - pix_fmts is now set in ff_libturing_encoder as in h264dec.c.
>   - Changed usage of av_free with av_freep and fixed calls to free arrays
>   - Added brackets to all if and for statements
>   - Avoid repetition of code to free arrays in case of failure to initialise the libturing encoder
>   - Some fixes to address the review from wm4 and Mark Thompson received on Wed 08/02/2017
>   - Fixed indentation
>   - Version bump, removed strcpy() and excluding bool use in headers
> ---
>  LICENSE.md             |   1 +
>  configure              |   6 +
>  libavcodec/Makefile    |   1 +
>  libavcodec/allcodecs.c |   1 +
>  libavcodec/libturing.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 327 insertions(+)
>  create mode 100755 libavcodec/libturing.c
> 
> diff --git a/LICENSE.md b/LICENSE.md
> index ba65b05..03787c0 100644
> --- a/LICENSE.md
> +++ b/LICENSE.md
> @@ -84,6 +84,7 @@ The following libraries are under GPL:
>  - frei0r
>  - libcdio
>  - librubberband
> +- libturing
>  - libvidstab
>  - libx264
>  - libx265
> diff --git a/configure b/configure
> index 282114d..d450f2f 100755
> --- a/configure
> +++ b/configure
> @@ -253,6 +253,7 @@ External library support:
>    --enable-libssh          enable SFTP protocol via libssh [no]
>    --enable-libtesseract    enable Tesseract, needed for ocr filter [no]
>    --enable-libtheora       enable Theora encoding via libtheora [no]
> +  --enable-libturing       enable H.265/HEVC encoding via libturing [no]
>    --enable-libtwolame      enable MP2 encoding via libtwolame [no]
>    --enable-libv4l2         enable libv4l2/v4l-utils [no]
>    --enable-libvidstab      enable video stabilization using vid.stab [no]
> @@ -1497,6 +1498,7 @@ EXTERNAL_LIBRARY_GPL_LIST="
>      frei0r
>      libcdio
>      librubberband
> +    libturing
>      libvidstab
>      libx264
>      libx265
> @@ -2893,6 +2895,7 @@ libspeex_decoder_deps="libspeex"
>  libspeex_encoder_deps="libspeex"
>  libspeex_encoder_select="audio_frame_queue"
>  libtheora_encoder_deps="libtheora"
> +libturing_encoder_deps="libturing"
>  libtwolame_encoder_deps="libtwolame"
>  libvo_amrwbenc_encoder_deps="libvo_amrwbenc"
>  libvorbis_decoder_deps="libvorbis"
> @@ -5896,6 +5899,9 @@ enabled libssh            && require_pkg_config libssh libssh/sftp.h sftp_init
>  enabled libspeex          && require_pkg_config speex speex/speex.h speex_decoder_init -lspeex
>  enabled libtesseract      && require_pkg_config tesseract tesseract/capi.h TessBaseAPICreate
>  enabled libtheora         && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg
> +enabled libturing         && require_pkg_config libturing turing.h turing_version &&
> +                             { check_cpp_condition turing.h "TURING_API_VERSION > 1" ||
> +                             die "ERROR: libturing requires turing api version 2 or greater."; }
>  enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame &&
>                               { check_lib libtwolame twolame.h twolame_encode_buffer_float32_interleaved -ltwolame ||
>                                 die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; }
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index b440a00..13a19ff 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -910,6 +910,7 @@ OBJS-$(CONFIG_LIBSHINE_ENCODER)           += libshine.o
>  OBJS-$(CONFIG_LIBSPEEX_DECODER)           += libspeexdec.o
>  OBJS-$(CONFIG_LIBSPEEX_ENCODER)           += libspeexenc.o
>  OBJS-$(CONFIG_LIBTHEORA_ENCODER)          += libtheoraenc.o
> +OBJS-$(CONFIG_LIBTURING_ENCODER)          += libturing.o
>  OBJS-$(CONFIG_LIBTWOLAME_ENCODER)         += libtwolame.o
>  OBJS-$(CONFIG_LIBVO_AMRWBENC_ENCODER)     += libvo-amrwbenc.o
>  OBJS-$(CONFIG_LIBVORBIS_DECODER)          += libvorbisdec.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 0243f47..c08f94b 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -630,6 +630,7 @@ static void register_all(void)
>      REGISTER_ENCODER(LIBSHINE,          libshine);
>      REGISTER_ENCDEC (LIBSPEEX,          libspeex);
>      REGISTER_ENCODER(LIBTHEORA,         libtheora);
> +    REGISTER_ENCODER(LIBTURING,         libturing);
>      REGISTER_ENCODER(LIBTWOLAME,        libtwolame);
>      REGISTER_ENCODER(LIBVO_AMRWBENC,    libvo_amrwbenc);
>      REGISTER_ENCDEC (LIBVORBIS,         libvorbis);
> diff --git a/libavcodec/libturing.c b/libavcodec/libturing.c
> new file mode 100755
> index 0000000..c368dcd
> --- /dev/null
> +++ b/libavcodec/libturing.c
> @@ -0,0 +1,318 @@
> +/*
> + * libturing encoder
> + *
> + * Copyright (c) 2017 Turing Codec contributors
> + *
> + * 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
> + */
> +
> + #if defined(_MSC_VER)
> +#define TURING_API_IMPORTS 1
> +#endif
> +
> +
> +#include <turing.h>
> +
> +#include "libavutil/internal.h"
> +#include "libavutil/common.h"
> +#include "libavutil/avstring.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "avcodec.h"
> +#include "internal.h"
> +
> +#define MAX_OPTION_LENGTH 256
> +
> +typedef struct libturingEncodeContext {
> +    const AVClass *class;
> +    turing_encoder *encoder;
> +    const char *options;
> +} libturingEncodeContext;
> +
> +typedef struct optionContext {
> +    char **argv;
> +    char *options;
> +    char *s;
> +    int options_buffer_size;
> +    int buffer_filled;
> +    int options_added;
> +} optionContext;
> +
> +static av_cold int libturing_encode_close(AVCodecContext *avctx)
> +{
> +    libturingEncodeContext *ctx = avctx->priv_data;
> +    turing_destroy_encoder(ctx->encoder);
> +    return 0;
> +}
> +
> +static av_cold int add_option(const char *current_option, optionContext *option_ctx)
> +{
> +    int option_length = strlen(current_option);
> +    char *temp_ptr;
> +
> +
> +    if (option_ctx->buffer_filled + option_length + 1 > option_ctx->options_buffer_size) {
> +        if (!(option_ctx->options)) {
> +            option_ctx->options = av_malloc(option_length + 1);
> +            if (!(option_ctx->options)) {
> +                return AVERROR(ENOMEM);
> +            }
> +        } else {
> +            temp_ptr = av_realloc(option_ctx->options, option_ctx->options_buffer_size + option_length + 1);
> +            if (!(temp_ptr)) {
> +                return AVERROR(ENOMEM);
> +            }
> +            option_ctx->options = temp_ptr;
> +        }
> +        option_ctx->options_buffer_size += option_length + 1;
> +        option_ctx->s = option_ctx->options + option_ctx->buffer_filled;
> +    }
> +    av_strlcpy(option_ctx->s, current_option, (option_length + 1));
> +    option_ctx->s += 1 + option_length;
> +    option_ctx->options_added++;
> +    option_ctx->buffer_filled += option_length + 1;
> +    return 0;
> +}
> +
> +static av_cold int finalise_options(optionContext *option_ctx)
> +{
> +    int option_idx = 0;
> +    if (option_ctx->options_added) {
> +        char *p;
> +        option_ctx->argv = av_malloc(option_ctx->options_added * sizeof(char*));
> +        if (!(option_ctx->argv)) {
> +            return AVERROR(ENOMEM);
> +        }
> +        p = option_ctx->options;
> +        for (option_idx=0; option_idx<option_ctx->options_added; option_idx++) {
> +            option_ctx->argv[option_idx] = p;
> +            p += strlen(p) + 1;
> +        }
> +    }
> +    return 0;
> +}
> +
> +static av_cold int libturing_encode_init(AVCodecContext *avctx)
> +{
> +    libturingEncodeContext *ctx = avctx->priv_data;
> +    const int bit_depth = av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth;
> +    int error_code = 0;
> +    int i = 0;
> +
> +    optionContext encoder_options = {0};
> +    turing_encoder_settings settings;
> +    char option_string[MAX_OPTION_LENGTH];
> +    double frame_rate;
> +
> +    frame_rate = (double)avctx->time_base.den / (avctx->time_base.num * avctx->ticks_per_frame);
> +
> +    encoder_options.buffer_filled = 0;
> +    encoder_options.options_added = 0;
> +    encoder_options.options_buffer_size = 0;
> +    encoder_options.options = NULL;
> +    encoder_options.s = encoder_options.options;
> +    encoder_options.argv = NULL;
> +
> +    if (error_code = add_option("turing", &encoder_options)) {
> +        goto fail;
> +    }
> +
> +    if (error_code = add_option("--frames=0", &encoder_options)) {
> +        goto fail;
> +    }
> +
> +    snprintf(option_string, MAX_OPTION_LENGTH, "--input-res=%dx%d", avctx->width, avctx->height);
> +    if (error_code = add_option(option_string, &encoder_options)) {
> +        goto fail;
> +    }
> +
> +    snprintf(option_string, MAX_OPTION_LENGTH, "--frame-rate=%f", frame_rate);
> +    if (error_code = add_option(option_string, &encoder_options)) {
> +        goto fail;
> +    }
> +
> +    snprintf(option_string, MAX_OPTION_LENGTH, "--bit-depth=%d", bit_depth);
> +    if (error_code = add_option(option_string, &encoder_options)) {
> +        goto fail;
> +    }
> +
> +    if (avctx->sample_aspect_ratio.num > 0 && avctx->sample_aspect_ratio.den > 0) {
> +        int sar_num, sar_den;
> +
> +        av_reduce(&sar_num, &sar_den,
> +            avctx->sample_aspect_ratio.num,
> +            avctx->sample_aspect_ratio.den, 65535);
> +        snprintf(option_string, MAX_OPTION_LENGTH, "--sar=%d:%d", sar_num, sar_den);
> +        if (error_code = add_option(option_string, &encoder_options)) {
> +            goto fail;
> +        }
> +    }
> +
> +    if (ctx->options) {
> +        AVDictionary *dict = NULL;
> +        AVDictionaryEntry *en = NULL;
> +
> +        if (!av_dict_parse_string(&dict, ctx->options, "=", ":", 0)) {
> +            while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) {
> +                int const illegal_option = av_match_name(en->key, "input-res,frame-rate,f,frames,sar,bit-depth,internal-bit-depth");
> +                if (illegal_option) {
> +                    av_log(avctx, AV_LOG_WARNING, "%s=%s ignored - this parameter is inferred from ffmpeg.\n", en->key, en->value);
> +                } else {
> +                    if (turing_check_binary_option(en->key)) {
> +                        snprintf(option_string, MAX_OPTION_LENGTH, "--%s", en->key);
> +                    } else {
> +                        snprintf(option_string, MAX_OPTION_LENGTH, "--%s=%s", en->key, en->value);
> +                    }
> +                    if (error_code = add_option(option_string, &encoder_options)) {
> +                        goto fail;
> +                    }
> +                }
> +            }
> +            av_dict_free(&dict);
> +        }
> +    }
> +
> +    if (error_code = add_option("dummy-input-filename", &encoder_options)) {
> +        goto fail;
> +    }
> +
> +    if (error_code = finalise_options(&encoder_options)) {
> +        goto fail;
> +    }
> +
> +    settings.argv = (char const**)encoder_options.argv;
> +    settings.argc = encoder_options.options_added;
> +
> +    for (i = 0; i < settings.argc; i++) {
> +        av_log(avctx, AV_LOG_VERBOSE, "arg %d: %s\n", i, settings.argv[i]);
> +    }
> +
> +    ctx->encoder = turing_create_encoder(settings);
> +
> +    if (!ctx->encoder) {
> +        av_log(avctx, AV_LOG_ERROR, "Failed to create libturing encoder.\n");
> +        error_code = AVERROR_INVALIDDATA;
> +        goto fail;
> +    }
> +
> +    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
> +        turing_bitstream const *bitstream;
> +        bitstream = turing_encode_headers(ctx->encoder);
> +        if (bitstream->size <= 0) {
> +            av_log(avctx, AV_LOG_ERROR, "Failed to encode headers.\n");
> +            turing_destroy_encoder(ctx->encoder);
> +            error_code = AVERROR_INVALIDDATA;
> +            goto fail;
> +        }
> +
> +        avctx->extradata_size = bitstream->size;
> +
> +        avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
> +        if (!avctx->extradata) {
> +            av_log(avctx, AV_LOG_ERROR, "Failed to allocate HEVC extradata %d bytes\n", avctx->extradata_size);
> +            turing_destroy_encoder(ctx->encoder);
> +            error_code =  AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +
> +        memcpy(avctx->extradata, bitstream->p, bitstream->size);
> +    }
> +
> +    av_freep(&encoder_options.argv);
> +    av_freep(&encoder_options.options);
> +    return 0;
> +
> +fail:
> +    av_log(avctx, AV_LOG_ERROR, "Error while initialising the Turing codec.\n");
> +    av_freep(&encoder_options.argv);
> +    av_freep(&encoder_options.options);
> +    return error_code;
> +}
> +
> +static int libturing_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pic, int *got_packet)
> +{
> +    libturingEncodeContext *ctx = avctx->priv_data;
> +    turing_encoder_output const *output;
> +    int ret = 0;
> +
> +    if (pic) {
> +        turing_picture picture;
> +
> +        picture.image[0].p = pic->data[0];
> +        picture.image[1].p = pic->data[1];
> +        picture.image[2].p = pic->data[2];
> +        picture.image[0].stride = pic->linesize[0];
> +        picture.image[1].stride = pic->linesize[1];
> +        picture.image[2].stride = pic->linesize[2];
> +        picture.pts = pic->pts;
> +        output = turing_encode_picture(ctx->encoder, &picture);
> +    } else {
> +        output = turing_encode_picture(ctx->encoder, NULL);
> +    }
> +
> +    if (output->bitstream.size < 0) {
> +        return AVERROR_EXTERNAL;
> +    }
> +
> +    if (!(output->bitstream.size)) {
> +        return 0;
> +    }
> +
> +    ret = ff_alloc_packet2(avctx, pkt, output->bitstream.size, 0);
> +    if (ret < 0) {
> +        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
> +        return ret;
> +    }
> +
> +    memcpy(pkt->data, output->bitstream.p, output->bitstream.size);
> +
> +    pkt->pts = output->pts;
> +    pkt->dts = output->dts;
> +    if (output->keyframe) {
> +        pkt->flags |= AV_PKT_FLAG_KEY;
> +    }
> +
> +    *got_packet = 1;
> +    return 0;
> +}
> +
> +static const AVOption options[] = {
> +    { "turing-params", "configure additional turing encoder parameters", offsetof(libturingEncodeContext, options), 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 = "libturing",
> +    .item_name  = av_default_item_name,
> +    .option     = options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +AVCodec ff_libturing_encoder = {
> +    .name           = "libturing",
> +    .long_name      = NULL_IF_CONFIG_SMALL("libturing HEVC"),
> +    .type           = AVMEDIA_TYPE_VIDEO,
> +    .id             = AV_CODEC_ID_HEVC,
> +    .init           = libturing_encode_init,
> +    .encode2        = libturing_encode_frame,
> +    .close          = libturing_encode_close,
> +    .priv_data_size = sizeof(libturingEncodeContext),
> +    .priv_class     = &class,
> +    .capabilities   = AV_CODEC_CAP_DELAY,
> +    .pix_fmts       = (const enum AVPixelFormat[]){AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE},
> +};

So I did try to build this, but the ffmpeg configure check apparently
fails at picking it up and dumps 3.5MB of C++ linker errors into the
config.log. And I have no idea why it does that. configure actually
prints "ERROR: libturing not found using pkg-config", but the .pc part
seems to be ok, but maybe the check_cpp_condition call tries to link.

The first error is:

lib/libturing.a(encode.cpp.o): In function `operator<<(std::ostream&, res
idual_coding)':                                                                                   encode.cpp:(.text+0xf6e): undefined reference to `std::basic_ostream<char, std::char_traits<char> 
>& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'               

The .pc file actually contains -lstdc++. Going by the linker error,
this is probably what's needed?

I have no idea why this error happens, but it seems a deal breaker. I'm
using gcc on Debian, and built libturing with a standard cmake
invocation.

Also, the .pc file contains a git hash as version "number", which makes
using this for actual version checks impossible. This should probably
be fixed too.

Sorry, can't justify spending more time on this, maybe someone else can
pick it up.
Saverio Blasi July 5, 2017, 2:56 p.m. UTC | #2
>>So I did try to build this, but the ffmpeg configure check apparently fails at picking it up and dumps 3.5MB of C++ linker errors into the config.log. And I have no idea why it does that. configure actually prints "ERROR: libturing not found using pkg-config", but the .pc part seems to be ok, but maybe the check_cpp_condition call tries to link.

>>

>>The first error is:

>>

>>lib/libturing.a(encode.cpp.o): In function `operator<<(std::ostream&, res

idual_coding)':                                                                                   encode.cpp:(.text+0xf6e): undefined reference to `std::basic_ostream<char, std::char_traits<char> 
>>& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'               

>>

>>The .pc file actually contains -lstdc++. Going by the linker error, this is probably what's needed?

>>

>>I have no idea why this error happens, but it seems a deal breaker. I'm using gcc on Debian, and built libturing with a standard cmake invocation.


Thanks a lot for trying this out. I am struggling a bit to replicate this error. I am using Ubuntu in my local machine but I have also just tried with a clean Debian installation in a Docker container. The Dockerfile clones both ffmpeg and Turing repositories, applies the patch as from the email, builds and installs the Turing library in a specific folder, and finally configures and builds ffmpeg succesfully. I am not quite sure what the difference could be with your workflow. Which version of gcc are you using? I have tested this with gcc 6. .I have also tried with an old Linux machine with 4.8.2 and that works as well. If I understand correctly, you are able to successfully build Turing and the problem happens later when configuring ffmpeg, is this correct? 

Below is the Dockerfile I have used for your reference, maybe I am doing something wrong in there which prevents me from catching this error. 

Any help in resolving this would be very much appreciated. Thanks a lot for your help, and sorry for using up your time. 

Saverio

FROM debian:latest
RUN apt-get -y update && \
    apt-get -y install cmake && \
    apt-get -y install git && \
    apt-get -y install build-essential && \
    apt-get -y install pkg-config && \
    apt-get -y install yasm
RUN mkdir source
WORKDIR /source/
RUN mkdir /source/ffmpeg && git clone https://github.com/FFmpeg/FFmpeg.git /source/ffmpeg && \
     mkdir /source/turingcodec && git clone https://github.com/bbc/turingcodec.git /source/turingcodec && \
     mkdir /source/ffmpeg/ffmpeg_build && \
     mkdir /source/turingcodec/build
WORKDIR /source/turingcodec/build
RUN cmake -DCMAKE_INSTALL_PREFIX="/source/ffmpeg/ffmpeg_build" .. && \
    make && \
    make install
WORKDIR /source/ffmpeg
COPY 0001-Added-Turing-codec-interface-for-ffmpeg.patch /source/ffmpeg
RUN git apply 0001-Added-Turing-codec-interface-for-ffmpeg.patch && \
    PKG_CONFIG_PATH="/source/ffmpeg/ffmpeg_build/lib/pkgconfig" ./configure   --prefix="/source/ffmpeg/ffmpeg_build"  \
    --pkg-config-flags="--static"   --extra-cflags="-I/source/ffmpeg/ffmpeg_build/include"  \
    --extra-ldflags="-L/source/ffmpeg/ffmpeg_build/lib"   --bindir="/source/ffmpeg" --enable-gpl --enable-libturing && \
    make
Derek Buitenhuis July 5, 2017, 3:08 p.m. UTC | #3
On 7/5/2017 3:56 PM, Saverio Blasi wrote:
> RUN cmake -DCMAKE_INSTALL_PREFIX="/source/ffmpeg/ffmpeg_build" .. && \
>     make && \
>     make install
> WORKDIR /source/ffmpeg
> COPY 0001-Added-Turing-codec-interface-for-ffmpeg.patch /source/ffmpeg
> RUN git apply 0001-Added-Turing-codec-interface-for-ffmpeg.patch && \
>     PKG_CONFIG_PATH="/source/ffmpeg/ffmpeg_build/lib/pkgconfig" ./configure   --prefix="/source/ffmpeg/ffmpeg_build"  \
>     --pkg-config-flags="--static"   --extra-cflags="-I/source/ffmpeg/ffmpeg_build/include"  \
>     --extra-ldflags="-L/source/ffmpeg/ffmpeg_build/lib"   --bindir="/source/ffmpeg" --enable-gpl --enable-libturing && \
>     make

It's possible he did two things. He may have

1. Built libturing as a shared library.

or

2. Not used --pkg-config-flags--static and thus missed including the (unfortunately non-namespaced)
embedded boost libraries.

- Derek
Saverio Blasi July 5, 2017, 3:16 p.m. UTC | #4
2. Not used --pkg-config-flags--static and thus missed including the (unfortunately non-namespaced) embedded boost libraries.

I followed the example instructions in https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu#ffmpeg (at the top of the page it says "Compile FFmpeg on Ubuntu, Debian, or Mint") for the build.

Saverio


-----------------------------
http://www.bbc.co.uk
This e-mail (and any attachments) is confidential and
may contain personal views which are not the views of the BBC unless specifically stated.
If you have received it in
error, please delete it from your system.
Do not use, copy or disclose the
information in any way nor act in reliance on it and notify the sender
immediately.
Please note that the BBC monitors e-mails
sent or received.
Further communication will signify your consent to
this.
-----------------------------
Saverio Blasi July 5, 2017, 3:49 p.m. UTC | #5
>>2. Not used --pkg-config-flags--static and thus missed including the (unfortunately non-namespaced) embedded boost libraries.

>>

>>I followed the example instructions in https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu#ffmpeg (at the top of the page it says "Compile FFmpeg on Ubuntu, Debian, or Mint") for the build.


I have in fact tried the same steps removing the --pkg-config-flags="--static" and I could replicate that behaviour. I believe though that anyone trying to build this following the official instructions would not incur in this error? 

Any help would be appreciated, I am not sure how to proceed with this.
Thank you very much for your time,
Saverio
wm4 July 5, 2017, 4:36 p.m. UTC | #6
On Wed, 5 Jul 2017 15:49:53 +0000
Saverio Blasi <Saverio.Blasi@bbc.co.uk> wrote:

> >>2. Not used --pkg-config-flags--static and thus missed including the (unfortunately non-namespaced) embedded boost libraries.
> >>
> >>I followed the example instructions in https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu#ffmpeg (at the top of the page it says "Compile FFmpeg on Ubuntu, Debian, or Mint") for the build.  
> 
> I have in fact tried the same steps removing the --pkg-config-flags="--static" and I could replicate that behaviour. I believe though that anyone trying to build this following the official instructions would not incur in this error? 
> 

These pkg-config flag additions in the Ubuntu instructions are pretty
unusual.
Derek Buitenhuis July 5, 2017, 4:42 p.m. UTC | #7
On 7/5/2017 5:36 PM, wm4 wrote:
> These pkg-config flag additions in the Ubuntu instructions are pretty
> unusual.

Passing --static to pkg-config is the de-facto way to build FFmpeg
with static libraries.

- Derek
Moritz Barsnick July 5, 2017, 11:16 p.m. UTC | #8
On Wed, Jul 05, 2017 at 17:42:31 +0100, Derek Buitenhuis wrote:
> Passing --static to pkg-config is the de-facto way to build FFmpeg
> with static libraries.

In other words, it's just an example in the wiki. It's one way to do
it, but that doesn't mean it's *the* way to do it. Building against
dynamic libs is certainly much more common if those are (at least for
the most part) provided by the "system" (distribution or what have
you).

Both options must work.

Moritz
Saverio Blasi July 6, 2017, 9:28 a.m. UTC | #9
>>In other words, it's just an example in the wiki. It's one way to do it, but that doesn't mean it's *the* way to do it. Building against dynamic libs is certainly much more common if those are (at least for the most part) provided by the "system" (distribution or what have you).

>>

>>Both options must work.


Thanks a lot, this makes sense. I thought that flag was compulsory and therefore we only included linking against libraries in "Libs.private" in the pc file. I have just pushed a fix to the Turing repo to include that in the Libs as well.

I have tested this and it seems to work fine in my machine and in the Docker with Ubuntu and debian with and without the flag now. No changes are needed to the actual patch, it should work as long as you update Turing to the latest commit.

Thanks a lot for your help with this,

Saverio
Saverio Blasi July 17, 2017, 11:19 a.m. UTC | #10
>> Thanks a lot, this makes sense. I thought that flag was compulsory and therefore we only included linking against libraries in "Libs.private" in the pc file. I have just pushed a fix to the Turing repo to include that in the Libs as well.

>> 

>> I have tested this and it seems to work fine in my machine and in the Docker with Ubuntu and debian with and without the flag now. No changes are needed to the actual patch, it should work as long as you update Turing to the latest commit.



So we have now further tested this extensively in different machines. I believe the problems with linking against dynamic libraries are solved in v 12 of the patch. In all our tests the patch works correctly with either settings of the pkg-config-flags. Could you please help us and confirm that this is the case? I think this was the only blocker with this latest version of the patch?

Thanks,
----------------------------------------------------
Saverio Blasi
Senior Research Engineer 
BBC Research & Development
Centre House|56 Wood Lane|London|W12 7SB
Ali KIZIL Aug. 8, 2017, 10:33 a.m. UTC | #11
2017-07-17 14:19 GMT+03:00 Saverio Blasi <Saverio.Blasi@bbc.co.uk>:

> >> Thanks a lot, this makes sense. I thought that flag was compulsory and
> therefore we only included linking against libraries in "Libs.private" in
> the pc file. I have just pushed a fix to the Turing repo to include that in
> the Libs as well.
> >>
> >> I have tested this and it seems to work fine in my machine and in the
> Docker with Ubuntu and debian with and without the flag now. No changes are
> needed to the actual patch, it should work as long as you update Turing to
> the latest commit.
>
>
> So we have now further tested this extensively in different machines. I
> believe the problems with linking against dynamic libraries are solved in v
> 12 of the patch. In all our tests the patch works correctly with either
> settings of the pkg-config-flags. Could you please help us and confirm that
> this is the case? I think this was the only blocker with this latest
> version of the patch?
>
> Thanks,
> ----------------------------------------------------
> Saverio Blasi
> Senior Research Engineer
> BBC Research & Development
> Centre House|56 Wood Lane|London|W12 7SB
>
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Is the Turing Codec interface patch going to be pushed after the fix of
flag problem ?
Saverio Blasi Aug. 8, 2017, 2:50 p.m. UTC | #12
Dear Ali,

> Is the Turing Codec interface patch going to be pushed after the fix of flag problem ?


The problem with the static flag is already fixed in the current version of the patch (v12). If you want to test it, you can manually copy the patch and apply it on top of the latest ffmpeg HEAD.

Thanks,
Saverio
diff mbox

Patch

diff --git a/LICENSE.md b/LICENSE.md
index ba65b05..03787c0 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -84,6 +84,7 @@  The following libraries are under GPL:
 - frei0r
 - libcdio
 - librubberband
+- libturing
 - libvidstab
 - libx264
 - libx265
diff --git a/configure b/configure
index 282114d..d450f2f 100755
--- a/configure
+++ b/configure
@@ -253,6 +253,7 @@  External library support:
   --enable-libssh          enable SFTP protocol via libssh [no]
   --enable-libtesseract    enable Tesseract, needed for ocr filter [no]
   --enable-libtheora       enable Theora encoding via libtheora [no]
+  --enable-libturing       enable H.265/HEVC encoding via libturing [no]
   --enable-libtwolame      enable MP2 encoding via libtwolame [no]
   --enable-libv4l2         enable libv4l2/v4l-utils [no]
   --enable-libvidstab      enable video stabilization using vid.stab [no]
@@ -1497,6 +1498,7 @@  EXTERNAL_LIBRARY_GPL_LIST="
     frei0r
     libcdio
     librubberband
+    libturing
     libvidstab
     libx264
     libx265
@@ -2893,6 +2895,7 @@  libspeex_decoder_deps="libspeex"
 libspeex_encoder_deps="libspeex"
 libspeex_encoder_select="audio_frame_queue"
 libtheora_encoder_deps="libtheora"
+libturing_encoder_deps="libturing"
 libtwolame_encoder_deps="libtwolame"
 libvo_amrwbenc_encoder_deps="libvo_amrwbenc"
 libvorbis_decoder_deps="libvorbis"
@@ -5896,6 +5899,9 @@  enabled libssh            && require_pkg_config libssh libssh/sftp.h sftp_init
 enabled libspeex          && require_pkg_config speex speex/speex.h speex_decoder_init -lspeex
 enabled libtesseract      && require_pkg_config tesseract tesseract/capi.h TessBaseAPICreate
 enabled libtheora         && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg
+enabled libturing         && require_pkg_config libturing turing.h turing_version &&
+                             { check_cpp_condition turing.h "TURING_API_VERSION > 1" ||
+                             die "ERROR: libturing requires turing api version 2 or greater."; }
 enabled libtwolame        && require libtwolame twolame.h twolame_init -ltwolame &&
                              { check_lib libtwolame twolame.h twolame_encode_buffer_float32_interleaved -ltwolame ||
                                die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; }
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index b440a00..13a19ff 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -910,6 +910,7 @@  OBJS-$(CONFIG_LIBSHINE_ENCODER)           += libshine.o
 OBJS-$(CONFIG_LIBSPEEX_DECODER)           += libspeexdec.o
 OBJS-$(CONFIG_LIBSPEEX_ENCODER)           += libspeexenc.o
 OBJS-$(CONFIG_LIBTHEORA_ENCODER)          += libtheoraenc.o
+OBJS-$(CONFIG_LIBTURING_ENCODER)          += libturing.o
 OBJS-$(CONFIG_LIBTWOLAME_ENCODER)         += libtwolame.o
 OBJS-$(CONFIG_LIBVO_AMRWBENC_ENCODER)     += libvo-amrwbenc.o
 OBJS-$(CONFIG_LIBVORBIS_DECODER)          += libvorbisdec.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 0243f47..c08f94b 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -630,6 +630,7 @@  static void register_all(void)
     REGISTER_ENCODER(LIBSHINE,          libshine);
     REGISTER_ENCDEC (LIBSPEEX,          libspeex);
     REGISTER_ENCODER(LIBTHEORA,         libtheora);
+    REGISTER_ENCODER(LIBTURING,         libturing);
     REGISTER_ENCODER(LIBTWOLAME,        libtwolame);
     REGISTER_ENCODER(LIBVO_AMRWBENC,    libvo_amrwbenc);
     REGISTER_ENCDEC (LIBVORBIS,         libvorbis);
diff --git a/libavcodec/libturing.c b/libavcodec/libturing.c
new file mode 100755
index 0000000..c368dcd
--- /dev/null
+++ b/libavcodec/libturing.c
@@ -0,0 +1,318 @@ 
+/*
+ * libturing encoder
+ *
+ * Copyright (c) 2017 Turing Codec contributors
+ *
+ * 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
+ */
+
+ #if defined(_MSC_VER)
+#define TURING_API_IMPORTS 1
+#endif
+
+
+#include <turing.h>
+
+#include "libavutil/internal.h"
+#include "libavutil/common.h"
+#include "libavutil/avstring.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "avcodec.h"
+#include "internal.h"
+
+#define MAX_OPTION_LENGTH 256
+
+typedef struct libturingEncodeContext {
+    const AVClass *class;
+    turing_encoder *encoder;
+    const char *options;
+} libturingEncodeContext;
+
+typedef struct optionContext {
+    char **argv;
+    char *options;
+    char *s;
+    int options_buffer_size;
+    int buffer_filled;
+    int options_added;
+} optionContext;
+
+static av_cold int libturing_encode_close(AVCodecContext *avctx)
+{
+    libturingEncodeContext *ctx = avctx->priv_data;
+    turing_destroy_encoder(ctx->encoder);
+    return 0;
+}
+
+static av_cold int add_option(const char *current_option, optionContext *option_ctx)
+{
+    int option_length = strlen(current_option);
+    char *temp_ptr;
+
+
+    if (option_ctx->buffer_filled + option_length + 1 > option_ctx->options_buffer_size) {
+        if (!(option_ctx->options)) {
+            option_ctx->options = av_malloc(option_length + 1);
+            if (!(option_ctx->options)) {
+                return AVERROR(ENOMEM);
+            }
+        } else {
+            temp_ptr = av_realloc(option_ctx->options, option_ctx->options_buffer_size + option_length + 1);
+            if (!(temp_ptr)) {
+                return AVERROR(ENOMEM);
+            }
+            option_ctx->options = temp_ptr;
+        }
+        option_ctx->options_buffer_size += option_length + 1;
+        option_ctx->s = option_ctx->options + option_ctx->buffer_filled;
+    }
+    av_strlcpy(option_ctx->s, current_option, (option_length + 1));
+    option_ctx->s += 1 + option_length;
+    option_ctx->options_added++;
+    option_ctx->buffer_filled += option_length + 1;
+    return 0;
+}
+
+static av_cold int finalise_options(optionContext *option_ctx)
+{
+    int option_idx = 0;
+    if (option_ctx->options_added) {
+        char *p;
+        option_ctx->argv = av_malloc(option_ctx->options_added * sizeof(char*));
+        if (!(option_ctx->argv)) {
+            return AVERROR(ENOMEM);
+        }
+        p = option_ctx->options;
+        for (option_idx=0; option_idx<option_ctx->options_added; option_idx++) {
+            option_ctx->argv[option_idx] = p;
+            p += strlen(p) + 1;
+        }
+    }
+    return 0;
+}
+
+static av_cold int libturing_encode_init(AVCodecContext *avctx)
+{
+    libturingEncodeContext *ctx = avctx->priv_data;
+    const int bit_depth = av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth;
+    int error_code = 0;
+    int i = 0;
+
+    optionContext encoder_options = {0};
+    turing_encoder_settings settings;
+    char option_string[MAX_OPTION_LENGTH];
+    double frame_rate;
+
+    frame_rate = (double)avctx->time_base.den / (avctx->time_base.num * avctx->ticks_per_frame);
+
+    encoder_options.buffer_filled = 0;
+    encoder_options.options_added = 0;
+    encoder_options.options_buffer_size = 0;
+    encoder_options.options = NULL;
+    encoder_options.s = encoder_options.options;
+    encoder_options.argv = NULL;
+
+    if (error_code = add_option("turing", &encoder_options)) {
+        goto fail;
+    }
+
+    if (error_code = add_option("--frames=0", &encoder_options)) {
+        goto fail;
+    }
+
+    snprintf(option_string, MAX_OPTION_LENGTH, "--input-res=%dx%d", avctx->width, avctx->height);
+    if (error_code = add_option(option_string, &encoder_options)) {
+        goto fail;
+    }
+
+    snprintf(option_string, MAX_OPTION_LENGTH, "--frame-rate=%f", frame_rate);
+    if (error_code = add_option(option_string, &encoder_options)) {
+        goto fail;
+    }
+
+    snprintf(option_string, MAX_OPTION_LENGTH, "--bit-depth=%d", bit_depth);
+    if (error_code = add_option(option_string, &encoder_options)) {
+        goto fail;
+    }
+
+    if (avctx->sample_aspect_ratio.num > 0 && avctx->sample_aspect_ratio.den > 0) {
+        int sar_num, sar_den;
+
+        av_reduce(&sar_num, &sar_den,
+            avctx->sample_aspect_ratio.num,
+            avctx->sample_aspect_ratio.den, 65535);
+        snprintf(option_string, MAX_OPTION_LENGTH, "--sar=%d:%d", sar_num, sar_den);
+        if (error_code = add_option(option_string, &encoder_options)) {
+            goto fail;
+        }
+    }
+
+    if (ctx->options) {
+        AVDictionary *dict = NULL;
+        AVDictionaryEntry *en = NULL;
+
+        if (!av_dict_parse_string(&dict, ctx->options, "=", ":", 0)) {
+            while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) {
+                int const illegal_option = av_match_name(en->key, "input-res,frame-rate,f,frames,sar,bit-depth,internal-bit-depth");
+                if (illegal_option) {
+                    av_log(avctx, AV_LOG_WARNING, "%s=%s ignored - this parameter is inferred from ffmpeg.\n", en->key, en->value);
+                } else {
+                    if (turing_check_binary_option(en->key)) {
+                        snprintf(option_string, MAX_OPTION_LENGTH, "--%s", en->key);
+                    } else {
+                        snprintf(option_string, MAX_OPTION_LENGTH, "--%s=%s", en->key, en->value);
+                    }
+                    if (error_code = add_option(option_string, &encoder_options)) {
+                        goto fail;
+                    }
+                }
+            }
+            av_dict_free(&dict);
+        }
+    }
+
+    if (error_code = add_option("dummy-input-filename", &encoder_options)) {
+        goto fail;
+    }
+
+    if (error_code = finalise_options(&encoder_options)) {
+        goto fail;
+    }
+
+    settings.argv = (char const**)encoder_options.argv;
+    settings.argc = encoder_options.options_added;
+
+    for (i = 0; i < settings.argc; i++) {
+        av_log(avctx, AV_LOG_VERBOSE, "arg %d: %s\n", i, settings.argv[i]);
+    }
+
+    ctx->encoder = turing_create_encoder(settings);
+
+    if (!ctx->encoder) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to create libturing encoder.\n");
+        error_code = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+
+    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
+        turing_bitstream const *bitstream;
+        bitstream = turing_encode_headers(ctx->encoder);
+        if (bitstream->size <= 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to encode headers.\n");
+            turing_destroy_encoder(ctx->encoder);
+            error_code = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+
+        avctx->extradata_size = bitstream->size;
+
+        avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
+        if (!avctx->extradata) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to allocate HEVC extradata %d bytes\n", avctx->extradata_size);
+            turing_destroy_encoder(ctx->encoder);
+            error_code =  AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        memcpy(avctx->extradata, bitstream->p, bitstream->size);
+    }
+
+    av_freep(&encoder_options.argv);
+    av_freep(&encoder_options.options);
+    return 0;
+
+fail:
+    av_log(avctx, AV_LOG_ERROR, "Error while initialising the Turing codec.\n");
+    av_freep(&encoder_options.argv);
+    av_freep(&encoder_options.options);
+    return error_code;
+}
+
+static int libturing_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pic, int *got_packet)
+{
+    libturingEncodeContext *ctx = avctx->priv_data;
+    turing_encoder_output const *output;
+    int ret = 0;
+
+    if (pic) {
+        turing_picture picture;
+
+        picture.image[0].p = pic->data[0];
+        picture.image[1].p = pic->data[1];
+        picture.image[2].p = pic->data[2];
+        picture.image[0].stride = pic->linesize[0];
+        picture.image[1].stride = pic->linesize[1];
+        picture.image[2].stride = pic->linesize[2];
+        picture.pts = pic->pts;
+        output = turing_encode_picture(ctx->encoder, &picture);
+    } else {
+        output = turing_encode_picture(ctx->encoder, NULL);
+    }
+
+    if (output->bitstream.size < 0) {
+        return AVERROR_EXTERNAL;
+    }
+
+    if (!(output->bitstream.size)) {
+        return 0;
+    }
+
+    ret = ff_alloc_packet2(avctx, pkt, output->bitstream.size, 0);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+        return ret;
+    }
+
+    memcpy(pkt->data, output->bitstream.p, output->bitstream.size);
+
+    pkt->pts = output->pts;
+    pkt->dts = output->dts;
+    if (output->keyframe) {
+        pkt->flags |= AV_PKT_FLAG_KEY;
+    }
+
+    *got_packet = 1;
+    return 0;
+}
+
+static const AVOption options[] = {
+    { "turing-params", "configure additional turing encoder parameters", offsetof(libturingEncodeContext, options), 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 = "libturing",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVCodec ff_libturing_encoder = {
+    .name           = "libturing",
+    .long_name      = NULL_IF_CONFIG_SMALL("libturing HEVC"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_HEVC,
+    .init           = libturing_encode_init,
+    .encode2        = libturing_encode_frame,
+    .close          = libturing_encode_close,
+    .priv_data_size = sizeof(libturingEncodeContext),
+    .priv_class     = &class,
+    .capabilities   = AV_CODEC_CAP_DELAY,
+    .pix_fmts       = (const enum AVPixelFormat[]){AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE},
+};