[FFmpeg-devel,V2,3/4] Add codec wrapper for librv11

Submitted by Thilo Borgmann on Feb. 2, 2018, 2:44 a.m.

Details

Message ID 72019de1-6da0-eafe-a0ea-2c4684b5424a@mail.de
State New
Headers show

Commit Message

Thilo Borgmann Feb. 2, 2018, 2:44 a.m.
Moved to non-free.
Symbols handled at compile time, no more dlsym fun.
Merged into one ENCDEC.

-Thilo
From f27957382ffb729a2454914bcd3a99554d6dff0d Mon Sep 17 00:00:00 2001
From: Thilo Borgmann <thilo.borgmann@mail.de>
Date: Fri, 2 Feb 2018 02:51:14 +0100
Subject: [PATCH 3/4] Add codec wrapper for librv11

---
 MAINTAINERS             |   1 +
 configure               |   5 +
 fftools/ffmpeg_opt.c    |   2 +-
 libavcodec/Makefile     |   2 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/avcodec.h    |   1 +
 libavcodec/codec_desc.c |   7 +
 libavcodec/librv11.h    |  55 ++++
 libavcodec/librv11dec.c | 296 ++++++++++++++++++++++
 libavcodec/librv11enc.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h    |   2 +-
 11 files changed, 1019 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/librv11.h
 create mode 100644 libavcodec/librv11dec.c
 create mode 100644 libavcodec/librv11enc.c

Patch hide | download patch | download mbox

diff --git a/MAINTAINERS b/MAINTAINERS
index ba7a728..d07fa75 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -192,6 +192,7 @@  Codecs:
   libkvazaar.c                          Arttu Ylä-Outinen
   libopenjpeg.c                         Jaikrishnan Menon
   libopenjpegenc.c                      Michael Bradshaw
+  librv11*                              Thilo Borgmann, Qiang Luo
   libtheoraenc.c                        David Conrad
   libvorbis.c                           David Conrad
   libvpx*                               James Zern
diff --git a/configure b/configure
index 0b01a22..9cc78da 100755
--- a/configure
+++ b/configure
@@ -250,6 +250,7 @@  External library support:
   --enable-librsvg         enable SVG rasterization via librsvg [no]
   --enable-librubberband   enable rubberband needed for rubberband filter [no]
   --enable-librtmp         enable RTMP[E] support via librtmp [no]
+  --enable-librv11         enable RV11 support via librv11 [no]
   --enable-libshine        enable fixed-point MP3 encoding via libshine [no]
   --enable-libsmbclient    enable Samba protocol via libsmbclient [no]
   --enable-libsnappy       enable Snappy compression, needed for hap encoding [no]
@@ -1565,6 +1566,7 @@  EXTERNAL_LIBRARY_NONFREE_LIST="
     libndi_newtek
     libfdk_aac
     openssl
+    librv11
     libtls
 "
 
@@ -2959,6 +2961,8 @@  libopus_decoder_deps="libopus"
 libopus_encoder_deps="libopus"
 libopus_encoder_select="audio_frame_queue"
 librsvg_decoder_deps="librsvg"
+librv11_decoder_deps="librv11"
+librv11_encoder_deps="librv11"
 libshine_encoder_deps="libshine"
 libshine_encoder_select="audio_frame_queue"
 libspeex_decoder_deps="libspeex"
@@ -5893,6 +5897,7 @@  enabled libpulse          && require_pkg_config libpulse libpulse pulse/pulseaud
 enabled librsvg           && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_render_cairo
 enabled librtmp           && require_pkg_config librtmp librtmp librtmp/rtmp.h RTMP_Socket
 enabled librubberband     && require_pkg_config librubberband "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new -lstdc++ && append librubberband_extralibs "-lstdc++"
+enabled librv11           && require_pkg_config librv11 librv11 librv11_sdk.h RV60toYUV420Init
 enabled libshine          && require_pkg_config libshine shine shine/layer3.h shine_encode_buffer
 enabled libsmbclient      && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init ||
                                require libsmbclient libsmbclient.h smbc_init -lsmbclient; }
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 997d538..0f88786 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1747,7 +1747,7 @@  static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in
                      ost->logfile_prefix ? ost->logfile_prefix :
                                            DEFAULT_PASS_LOGFILENAME_PREFIX,
                      i);
-            if (!strcmp(ost->enc->name, "libx264")) {
+            if (!strcmp(ost->enc->name, "libx264") || !strcmp(ost->enc->name, "librv11enc")) {
                 av_dict_set(&ost->encoder_opts, "stats", logfilename, AV_DICT_DONT_OVERWRITE);
             } else {
                 if (video_enc->flags & AV_CODEC_FLAG_PASS2) {
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 68777d9..ad7b9cd 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -948,6 +948,8 @@  OBJS-$(CONFIG_LIBOPUS_DECODER)            += libopusdec.o libopus.o     \
                                              vorbis_data.o
 OBJS-$(CONFIG_LIBOPUS_ENCODER)            += libopusenc.o libopus.o     \
                                              vorbis_data.o
+OBJS-$(CONFIG_LIBRV11_DECODER)            += librv11dec.o
+OBJS-$(CONFIG_LIBRV11_ENCODER)            += librv11enc.o
 OBJS-$(CONFIG_LIBSHINE_ENCODER)           += libshine.o
 OBJS-$(CONFIG_LIBSPEEX_DECODER)           += libspeexdec.o
 OBJS-$(CONFIG_LIBSPEEX_ENCODER)           += libspeexenc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index ed1e7ab..64bb723 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -548,6 +548,7 @@  static void register_all(void)
     REGISTER_ENCDEC (LIBOPENJPEG,       libopenjpeg);
     REGISTER_ENCDEC (LIBOPUS,           libopus);
     REGISTER_DECODER(LIBRSVG,           librsvg);
+    REGISTER_ENCDEC (LIBRV11,           librv11);
     REGISTER_ENCODER(LIBSHINE,          libshine);
     REGISTER_ENCDEC (LIBSPEEX,          libspeex);
     REGISTER_ENCODER(LIBTHEORA,         libtheora);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 8fbbc79..b7c1fa1 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -409,6 +409,7 @@  enum AVCodecID {
     AV_CODEC_ID_DXV,
     AV_CODEC_ID_SCREENPRESSO,
     AV_CODEC_ID_RSCC,
+    AV_CODEC_ID_RV60,
 
     AV_CODEC_ID_Y41P = 0x8000,
     AV_CODEC_ID_AVRP,
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index c3688de..1ad2c9c 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -471,6 +471,13 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .props     = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER,
     },
     {
+        .id        = AV_CODEC_ID_RV60,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "rv60",
+        .long_name = NULL_IF_CONFIG_SMALL("RealVideo 11"),
+        .props     = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER,
+    },
+    {
         .id        = AV_CODEC_ID_VC1,
         .type      = AVMEDIA_TYPE_VIDEO,
         .name      = "vc1",
diff --git a/libavcodec/librv11.h b/libavcodec/librv11.h
new file mode 100644
index 0000000..ec0d64c
--- /dev/null
+++ b/libavcodec/librv11.h
@@ -0,0 +1,55 @@ 
+/*
+ * This copyright notice applies to this file only
+ * This Software is distributed under MIT License
+ *
+ * API software for using RealVideo 11 (RV60) Codec
+ *
+ *      * Copyright (c) 2017 Qiang Luo, RealNetworks, Inc. <qluo _at_ realnetworks.com>
+ *      * Copyright (c) 2017 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef AVCODEC_LIBRV11_H
+#define AVCODEC_LIBRV11_H
+
+#include <librv11/librv11_sdk.h>
+
+#include "avcodec.h"
+#include "libavutil/intreadwrite.h"
+
+#ifdef _WIN32
+#include "compat/w32dlfcn.h"
+#else
+#include <dlfcn.h>
+#endif
+
+#define RV_MAX_INPUT_FRAME_RATE 120
+#define RV_NUM_OUT_FRAMES         6
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define LIBRV11DEC_FILE XSTR(RV_DEC_LIB_FILE)
+#define LIBRV11ENC_FILE XSTR(RV_ENC_LIB_FILE)
+
+
+#endif // AVCODEC_LIBRV11_H
diff --git a/libavcodec/librv11dec.c b/libavcodec/librv11dec.c
new file mode 100644
index 0000000..f3ed688
--- /dev/null
+++ b/libavcodec/librv11dec.c
@@ -0,0 +1,296 @@ 
+/*
+ * This copyright notice applies to this file only
+ * This Software is distributed under MIT License
+ *
+ * API software for using RealVideo 11 (RV60) Decoder
+ *
+ *      * Copyright (c) 2017 Qiang Luo, RealNetworks, Inc. <qluo _at_ realnetworks.com>
+ *      * Copyright (c) 2017 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "libavutil/imgutils.h"
+#include "internal.h"
+#include "get_bits.h"
+
+#include "librv11.h"
+
+typedef struct LIBRV11DecContext {
+    AVCodecContext *avctx;
+    AVFrame        out_frame;
+    void           *codec_status;
+    uint32_t       input_buf_size;
+    uint32_t       input_buf_num;
+    uint32_t       output_buf_size;
+    uint32_t       output_buf_num;
+    uint8_t        *output_buf[RV_NUM_OUT_FRAMES];
+    int            frame_index;
+    uint32_t       last_frame;
+    uint32_t       more_frames;
+} LIBRV11DecContext;
+
+static av_cold int librv11dec_init(AVCodecContext *avctx)
+{
+    LIBRV11DecContext *ctx = avctx->priv_data;
+    RVInitParams init_params;
+    RVMsgSimple msg;
+    HX_RESULT res;
+    uint32_t SPO_extra_flags;
+    uint32_t version;
+    int i;
+    ctx->avctx = avctx;
+
+    if (!avctx->extradata || !avctx->extradata_size) {
+        return AVERROR_INVALIDDATA;
+    }
+
+    SPO_extra_flags = AV_RL32(&avctx->extradata[0]);
+    version         = AV_RL32(&avctx->extradata[5]);
+
+    init_params.out_type   = 0;
+    init_params.width      = avctx->width;
+    init_params.height     = avctx->height;
+    init_params.pad_width  = avctx->width;
+    init_params.pad_height = avctx->height;
+    init_params.pad_to_32  = 0;
+    init_params.invariants = SPO_extra_flags;
+    init_params.packetize  = 1;
+    init_params.version    = version;
+
+    res = RV60toYUV420Init(&init_params, &ctx->codec_status);
+    if (res) {
+        return res;
+    }
+
+    msg.id     = RV_MSG_ID_SMOOTHING_POSTFILTER;
+    msg.value1 = RV_MSG_DISABLE;
+    msg.value2 = 0;
+
+    res = RV60toYUV420CustomMessage(&msg.id, ctx->codec_status);
+    if (res) {
+        return res;
+    }
+
+    avctx->pix_fmt   = AV_PIX_FMT_YUV420P;
+    ctx->last_frame  = 0;
+    ctx->more_frames = 0;
+    ctx->frame_index = 0;
+
+    ctx->output_buf_size = (avctx->width * avctx->height * 12) >> 3;
+    for(i = 0; i < RV_NUM_OUT_FRAMES; i++) {
+        ctx->output_buf[i] = av_mallocz(ctx->output_buf_size);
+    }
+    av_image_fill_arrays(ctx->out_frame.data, ctx->out_frame.linesize, ctx->output_buf[0], AV_PIX_FMT_YUV420P, avctx->width, avctx->height, 1);
+
+    return 0;
+}
+
+static int librv11dec_decode_last(AVCodecContext *avctx, void *data,
+                                    int *got_frame, AVPacket *avpkt)
+{
+    LIBRV11DecContext* ctx = avctx->priv_data;
+    AVFrame *pict    = data;
+    const int stride = avctx->width * avctx->height;
+    RVInParams inParams;
+    RVOutParams outParams;
+    int32_t ret;
+
+    inParams.length       = 0;
+    inParams.interpolate  = 0;
+    inParams.num_segments = 0;
+    inParams.segments     = NULL;
+    inParams.timestamp    = 0;
+    inParams.flags        = RV_DECODE_MORE_FRAMES;
+
+    pict->data[0] = ctx->output_buf[ctx->frame_index];
+    pict->data[1] = ctx->output_buf[ctx->frame_index] + stride;
+    pict->data[2] = ctx->output_buf[ctx->frame_index] + stride + (stride >> 2);
+
+    pict->linesize[0] = avctx->width;
+    pict->linesize[1] = avctx->width >> 1;
+    pict->linesize[2] = avctx->width >> 1;
+
+    ret = RV60toYUV420Transform(NULL, ctx->output_buf[ctx->frame_index], &inParams, &outParams, ctx->codec_status);
+    if (ret) {
+        *got_frame = 0;
+        return ret;
+    }
+
+    if(outParams.notes & RV_DECODE_LAST_FRAME) {
+        ctx->last_frame = 1;
+    }
+
+    pict->pts       = outParams.timestamp;
+    pict->pkt_dts   = avpkt->dts;
+    pict->width     = avctx->width;
+    pict->height    = avctx->height;
+    pict->format    = AV_PIX_FMT_YUV420P;
+    pict->key_frame = 0;
+    pict->pict_type = AV_PICTURE_TYPE_P;
+
+    if (outParams.notes & RV_DECODE_KEY_FRAME) {
+        pict->key_frame = 1;
+        pict->pict_type = AV_PICTURE_TYPE_I;
+    } else if (outParams.notes & RV_DECODE_B_FRAME ||
+               outParams.notes & RV_DECODE_FRU_FRAME) {
+        pict->key_frame = 0;
+        pict->pict_type = AV_PICTURE_TYPE_B;
+    }
+
+    *got_frame = !(outParams.notes & RV_DECODE_DONT_DRAW);
+
+    return 0;
+
+}
+
+static int librv11dec_decode_frame(AVCodecContext *avctx, void *data,
+                                    int *got_frame, AVPacket *avpkt)
+{
+    LIBRV11DecContext* ctx = avctx->priv_data;
+    const uint8_t *buf = avpkt->data;
+    int buf_size       = avpkt->size;
+    const int stride   = avctx->width * avctx->height;
+    AVFrame *pict      = data;
+    int slice_count    = 0;
+    int consumed_bytes = 0;
+    RVSegment *segment = NULL;
+    RVInParams inParams;
+    RVOutParams outParams;
+    int32_t ret;
+    int i;
+
+    ret = ff_get_buffer(avctx, pict, 0);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ctx->frame_index++;
+    ctx->frame_index %= RV_NUM_OUT_FRAMES;
+
+    if (!buf_size) { // maybe last frame
+        if (ctx->last_frame) {
+            *got_frame = 0;
+            return 0;
+        } else {
+            return librv11dec_decode_last(avctx, data, got_frame, avpkt);
+        }
+    }
+
+    if (!avctx->slice_count) {
+        slice_count = (*buf++) + 1;
+        segment = av_mallocz(slice_count * sizeof(RVSegment));
+        for (i = 0; i < slice_count; i++) {
+            segment[i].is_valid = *buf;
+            segment[i].offset   = *(buf + 4);
+            buf += 8;
+        }
+        buf_size -= 1 + 8 * slice_count;
+    } else {
+        slice_count = avctx->slice_count;
+    }
+
+    inParams.length       = buf_size;
+    inParams.interpolate  = 0;
+    inParams.num_segments = slice_count - 1;
+    inParams.segments     = segment;
+    inParams.timestamp    = avpkt->pts;
+    inParams.flags        = 0;
+
+    pict->data[0] = ctx->output_buf[ctx->frame_index];
+    pict->data[1] = ctx->output_buf[ctx->frame_index] + stride;
+    pict->data[2] = ctx->output_buf[ctx->frame_index] + stride + (stride >> 2);
+    pict->linesize[0] = avctx->width;
+    pict->linesize[1] = avctx->width >> 1;
+    pict->linesize[2] = avctx->width >> 1;
+
+    ret = RV60toYUV420Transform((UCHAR*)buf, ctx->output_buf[ctx->frame_index], &inParams, &outParams, ctx->codec_status);
+    if (ret) {
+        consumed_bytes = 0;
+        *got_frame     = 0;
+        return ret;
+    }
+
+    if (slice_count) {
+        av_freep(&segment);
+    }
+
+    consumed_bytes  = avpkt->size;
+    *got_frame      = 1;
+    pict->key_frame = 0;
+    pict->pict_type = AV_PICTURE_TYPE_P;
+
+    if (outParams.notes & RV_DECODE_KEY_FRAME) {
+        pict->pict_type = AV_PICTURE_TYPE_I;
+        pict->key_frame = 1;
+    } else if (outParams.notes & RV_DECODE_B_FRAME ||
+               outParams.notes & RV_DECODE_FRU_FRAME) {
+        pict->pict_type = AV_PICTURE_TYPE_B;
+    } else if (outParams.notes & RV_DECODE_DONT_DRAW) {
+        consumed_bytes  = 0;
+        *got_frame      = 0;
+    }
+
+    pict->pts     = outParams.timestamp;
+    pict->pkt_dts = avpkt->dts;
+    pict->width   = avctx->width;
+    pict->height  = avctx->height;
+    pict->format  = AV_PIX_FMT_YUV420P;
+
+    return consumed_bytes;
+}
+
+static av_cold int librv11dec_close(AVCodecContext *avctx)
+{
+    LIBRV11DecContext* ctx = avctx->priv_data;
+    int i;
+
+    RV60toYUV420Free(ctx->codec_status);
+
+    for(i = 0; i < RV_NUM_OUT_FRAMES; i++) {
+        if (ctx->output_buf[i]) {
+            av_freep(&ctx->output_buf[i]);
+        }
+    }
+
+    return 0;
+}
+
+static void librv11dec_flush(AVCodecContext *avctx)
+{
+    LIBRV11DecContext* ctx = avctx->priv_data;
+    uint32_t id = RV_MSG_ID_FLUSH;
+    RV60toYUV420HiveMessage(&id, ctx->codec_status);
+}
+
+AVCodec ff_librv11_decoder = {
+    .name           = "librv11",
+    .long_name      = NULL_IF_CONFIG_SMALL("librv11 RealVideo 11 (RV60)"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_RV60,
+    .priv_data_size = sizeof(LIBRV11DecContext),
+    .init           = librv11dec_init,
+    .decode         = librv11dec_decode_frame,
+    .close          = librv11dec_close,
+    .flush          = librv11dec_flush,
+    .capabilities   = AV_CODEC_CAP_DELAY,
+};
diff --git a/libavcodec/librv11enc.c b/libavcodec/librv11enc.c
new file mode 100644
index 0000000..bdf2ceb
--- /dev/null
+++ b/libavcodec/librv11enc.c
@@ -0,0 +1,649 @@ 
+/*
+ * This copyright notice applies to this file only
+ * This Software is distributed under MIT License
+ *
+ * API software for using RealVideo 11 (RV60) Encoder
+ *
+ *      * Copyright (c) 2017 Qiang Luo, RealNetworks, Inc. <qluo _at_ realnetworks.com>
+ *      * Copyright (c) 2017 Thilo Borgmann <thilo.borgmann _at_ mail.de>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+
+#include "librv11.h"
+
+typedef struct CodedFrameList {
+    AVPacket pkt;
+    struct CodedFrameList* pNext;
+} CodedFrameList;
+
+typedef struct FrameRatioConverter {
+    uint32_t    count;
+    double      framerate;
+    uint8_t*    selected_map;
+    uint32_t    framerate_in;
+} FrameRatioConverter;
+
+typedef struct LIBRV11EncContext
+{
+    AVClass    *class;
+    void       *codec_ref;
+    void       *stream_ref;
+    RVEncodeParam rvenc_param;
+    uint32_t   codec_4cc;
+    uint32_t   frame_size;
+    uint8_t*   frame_buf;
+    CodedFrameList* coded_frame_list;
+    int        bitrate_vbr;           ///< 0:VBRQuality, 1:VBRBitrate
+    int        eos;                   ///< set to TURE when input frame was set to NULL
+    int        last_packet;           ///< set to 1 when the last codec frame was popped out
+    int        seen_packet;
+    int64_t    prev_dts;
+    FrameRatioConverter fr_convert;
+} LIBRV11EncContext;
+
+static int32_t interval_search(uint8_t *buf, const uint32_t buf_size, const uint32_t num_frames)
+{
+    if (buf_size < num_frames || !num_frames) {
+        return -1;
+    }
+
+    if (!(buf_size % num_frames)) {
+        uint32_t i;
+        const uint32_t den = buf_size / num_frames;
+
+        for(i = 0; i < buf_size; i++) {
+            if (!((i+1) % den)) {
+                buf[i] = 1;
+            }
+        }
+    } else {
+        const uint32_t half  = buf_size >> 1;
+        const uint32_t left  = num_frames >> 1;
+        const uint32_t right = left + (num_frames & 1);
+
+        interval_search(buf, half, left);
+        interval_search(buf + half, buf_size - half, right);
+    }
+
+    return 0;
+}
+
+static av_cold int32_t fr_convert_init(FrameRatioConverter* fr_convert, double fr_in, double fr_max)
+{
+    if (fr_in < fr_max || fr_in > RV_MAX_INPUT_FRAME_RATE) {
+        return -1;
+    }
+
+    fr_convert->count        = 0;
+    fr_convert->framerate    = fr_max;
+    fr_convert->framerate_in = (int32_t)(fr_in + 0.5);
+    fr_convert->selected_map = av_mallocz(fr_convert->framerate_in);
+
+    return interval_search(fr_convert->selected_map, fr_convert->framerate_in, (int32_t)(fr_max + 0.5));
+}
+
+static av_always_inline int32_t use_frame(FrameRatioConverter *fr_convert)
+{
+    return fr_convert->selected_map[fr_convert->count];
+}
+
+static void next_frame(FrameRatioConverter* fr_convert)
+{
+    fr_convert->count++;
+    if (fr_convert->count >= fr_convert->framerate_in) {
+        fr_convert->count = 0;
+    }
+}
+
+static void coded_frame_add(void *list, CodedFrameList *cx_frame)
+{
+    CodedFrameList **p = list;
+
+    while(*p) {
+        p = &(*p)->pNext;
+    }
+
+    *p = cx_frame;
+    cx_frame->pNext = NULL;
+}
+
+static CodedFrameList* coded_frame_remove_header(CodedFrameList** list)
+{
+    CodedFrameList *p = *list;
+
+    if (p) {
+        *list = p->pNext;
+    }
+
+    return p;
+}
+
+static av_cold void free_coded_frame(CodedFrameList *cx_frame)
+{
+    av_packet_unref(&cx_frame->pkt);
+    av_freep(&cx_frame);
+}
+
+static av_cold void free_frame_list(CodedFrameList *list)
+{
+    CodedFrameList *p = list;
+
+    while(p) {
+        list = list->pNext;
+        free_coded_frame(p);
+        p = list;
+    }
+}
+
+static int librv11_num_segments(uint32_t data_length)
+{
+    int num_packet;
+
+    if (data_length < 1) {
+        return -1;
+    }
+
+    num_packet = data_length / RV_MAX_PACKET_SIZE;
+    if (data_length % RV_MAX_PACKET_SIZE) {
+        num_packet++;
+    }
+
+    return num_packet;
+}
+
+static int librv11_write_frame_header(uint8_t *data, int num_packet)
+{
+    int i;
+
+    if (num_packet < 1 || !data) {
+        return -1;
+    }
+
+    data[0] = num_packet -1;
+
+    for (i = 0; i < num_packet; i++) {
+        // the value of next 4 byte is the segment index
+        AV_WL32(data + 1 + 8 * i, i + 1);
+        // continuous 4 byte is the segment offset
+        AV_WL32(data + 5 + 8 * i, RV_MAX_PACKET_SIZE * i);
+    }
+
+    return 0;
+}
+
+static av_cold int CC librv11enc_data_callback(void *stream_ctx, void *stream_ref, HXCodecData* data)
+{
+    int ret = 0;
+    int pict_type;
+    int64_t cur_pts;
+    int64_t cur_dts;
+    AVCodecContext *avctx = (AVCodecContext*) stream_ctx;
+    LIBRV11EncContext *enctx = avctx->priv_data;
+
+    if (data && data->length) {
+        int numPackets = 0;
+        int bufLength;
+
+        CodedFrameList* node = av_mallocz(sizeof(CodedFrameList));
+        if (!node) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory.\n");
+            return AVERROR(ENOMEM);
+        }
+
+        // for rv11 num_segments should be 1
+        if (data->num_segments == 1) {
+            numPackets = librv11_num_segments(data->length);
+            if (numPackets > 127 || numPackets < 0) {
+                av_log(avctx, AV_LOG_WARNING, "Too many packets per frame (%d).\n", numPackets);
+            }
+        } else {
+            av_log(avctx, AV_LOG_WARNING, "Invalid number of segments (%u), should be 1.\n", data->num_segments);
+        }
+
+        // allocate extra numPackets*8+1  byte for real video head saving segment information
+        bufLength = data->length + numPackets * 8 + 1;
+        ret = av_new_packet(&node->pkt, bufLength);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet.\n");
+            return ret;
+        }
+        librv11_write_frame_header(node->pkt.data, numPackets);
+        memcpy((node->pkt.data + numPackets * 8 + 1), data->data, data->length);
+
+        if (data->flags & HX_KEYFRAME_FLAG) {
+            node->pkt.flags |= AV_PKT_FLAG_KEY;
+        }
+
+        if (data->last_packet) {
+            node->pkt.flags |= RV_PKT_FLAG_END;
+        }
+
+        pict_type = ((*(data->data + 1)) >> 6) & 0x03;
+        cur_pts = data->timestamp;
+        if(!enctx->seen_packet) {
+            enctx->seen_packet = 1;
+            cur_dts = cur_pts - 1;             // first frame
+        } else {
+            if (pict_type == 2) {
+                cur_dts = cur_pts;             // B-frame
+            } else {
+                cur_dts = enctx->prev_dts + 1; // ref-frame
+            }
+        }
+
+        node->pkt.pts   = cur_pts;
+        node->pkt.dts   = cur_dts;
+        enctx->prev_dts = cur_dts;
+
+        coded_frame_add((void*)(&enctx->coded_frame_list), node);
+    }
+
+    return 0;
+}
+
+static av_cold int librv11enc_init(AVCodecContext *avctx)
+{
+    LIBRV11EncContext *enctx = avctx->priv_data;
+    HX_RESULT res;
+    HXMof hxInput, hxOutput;
+    HXFormatVideo mofin, mofout;
+    HXCodecInit ci;
+    double framerate;
+    uint32_t dummy_ui32;
+    float dummy_f;
+
+    // Width and height of the video are required to be multiples of 4
+    if (avctx->width % 4 || avctx->height % 4) {
+        av_log(avctx, AV_LOG_ERROR,
+        "Video dimensions have to be multiples of 4 (%dx%d).\n",
+        avctx->width, avctx->height);
+        return AVERROR(EINVAL);
+    }
+
+    res = PNCodec_QueryMediaFormat(NULL, &hxInput, &hxOutput, 0);
+    if (FAILED(res)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to query in/out formats.\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    res = PNCodec_Open(hxOutput.submoftag, &enctx->codec_ref);
+    if (FAILED(res)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to open the codec library.\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    enctx->codec_4cc = hxOutput.submoftag;
+
+    // init the librv11 encoder using default/user parameters
+    enctx->rvenc_param.in_width  = avctx->width;
+    enctx->rvenc_param.in_height = avctx->height;
+
+    if (!enctx->rvenc_param.out_width) {
+        enctx->rvenc_param.out_width = avctx->width;
+    }
+    if (!enctx->rvenc_param.out_height) {
+        enctx->rvenc_param.out_height = avctx->height;
+    }
+
+    if (enctx->bitrate_vbr) {
+        enctx->rvenc_param.encode_type = ENCODE_VBR_BITRATE;
+        if (avctx->flags & AV_CODEC_FLAG_PASS1) {
+            enctx->rvenc_param.vbr_quality = 25;
+        }
+    } else {
+        enctx->rvenc_param.encode_type = ENCODE_VBR_QUALITY;
+    }
+
+    if (avctx->flags & AV_CODEC_FLAG_PASS1) {
+        enctx->rvenc_param.encode_mode = ENCODE_CREATE_ANALYSIS;
+    } else if (avctx->flags & AV_CODEC_FLAG_PASS2){
+        enctx->rvenc_param.encode_mode = ENCODE_USING_ANALYSIS;
+    } else {
+        enctx->rvenc_param.encode_mode = ENCODE_SINGLE_PASS;
+    }
+
+    enctx->rvenc_param.avg_bitrate = avctx->bit_rate;
+    enctx->rvenc_param.max_bitrate = enctx->rvenc_param.avg_bitrate;
+
+    enctx->rvenc_param.max_packet_size = RV_MAX_PACKET_SIZE;
+
+    if (!avctx->framerate.den) {
+        return AVERROR(EINVAL);
+    }
+    framerate = av_q2d(avctx->framerate);
+
+    res = fr_convert_init(&enctx->fr_convert, framerate, enctx->rvenc_param.max_framerate);
+    if (res) {
+        fr_convert_init(&enctx->fr_convert, framerate, framerate);
+    }
+
+    // set input parameters for I420
+    memset(&mofin, 0, sizeof(mofin));
+    mofin.length     = sizeof(HXFormatVideo);
+    mofin.moftag     = HX_MEDIA_VIDEO;
+    mofin.submoftag  = HX_YUV420_ID;
+    mofin.width      = enctx->rvenc_param.in_width;
+    mofin.height     = enctx->rvenc_param.in_height;
+    mofin.bit_count  = 12;
+    mofin.pad_width  = 0;
+    mofin.pad_height = 0;
+    mofin.moftag     = HX_MEDIA_VIDEO;
+
+    mofin.fps = (int)(enctx->fr_convert.framerate * (1L << 16) + 0.5);
+
+    // set output parameters
+    memset(&mofout, 0, sizeof(mofout));
+    mofout.length     = sizeof(mofout);
+    mofout.moftag     = HX_MEDIA_VIDEO;
+    mofout.submoftag  = enctx->codec_4cc;
+    mofout.width      = enctx->rvenc_param.out_width;
+    mofout.height     = enctx->rvenc_param.out_height;
+    mofout.bit_count  = mofin.bit_count;
+    mofout.pad_width  = 0;
+    mofout.pad_height = 0;
+    mofout.moftag     = HX_MEDIA_VIDEO;
+    mofout.fps        = mofin.fps;
+
+    ci.in_mof  = (HXMof*)&mofin;
+    ci.out_mof = (HXMof*)&mofout;
+    ci.mem_ptr = NULL;
+
+#define SET_RV11_PROP(prop, value) {                                        \
+    res = PNStream_SetProperty(enctx->stream_ref, prop, value);             \
+    if (FAILED(res)) {                                                      \
+        av_log(avctx, AV_LOG_ERROR, "Failed to set property %s.\n", #prop); \
+        return AVERROR_EXTERNAL;                                            \
+    }                                                                       \
+}
+
+    res = PNCodec_StreamOpen(enctx->codec_ref, &enctx->stream_ref, &ci);
+    if (FAILED(res)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to open the stream.\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    if (enctx->bitrate_vbr && !(avctx->flags & AV_CODEC_FLAG_PASS1)) {
+        SET_RV11_PROP(SP_BITRATE, &enctx->rvenc_param.avg_bitrate);
+    } else {
+        SET_RV11_PROP(SP_QUALITY, &enctx->rvenc_param.vbr_quality);
+    }
+
+    SET_RV11_PROP(SP_MAXBITRATE, &enctx->rvenc_param.max_bitrate);
+
+    dummy_f = (float)(enctx->rvenc_param.max_framerate);
+    SET_RV11_PROP(SP_MAX_FRAMERATE, &dummy_f);
+
+    dummy_ui32 = 0;
+    SET_RV11_PROP(SP_CHECK_FRAME_SKIP, &dummy_ui32);
+
+    dummy_ui32 = 0;
+    SET_RV11_PROP(SP_LIVE, &dummy_ui32);
+
+    dummy_ui32 = enctx->rvenc_param.max_keyint * 1000;
+    SET_RV11_PROP(SP_KEYFRAMERATE, &dummy_ui32);
+
+    SET_RV11_PROP(SP_ECC, &enctx->rvenc_param.loss_protect);
+    SET_RV11_PROP(SP_SET_FAST_LEVEL, &enctx->rvenc_param.enc_complexity);
+
+    dummy_ui32 = (uint32_t) enctx->rvenc_param.max_start_latency * 1000;
+    SET_RV11_PROP(SP_TARGET_LATENCY, &dummy_ui32);
+
+    if (enctx->rvenc_param.pon > 0) {
+        SET_RV11_PROP(SP_SET_PON_START, &enctx->rvenc_param.pon);
+    }
+
+    res = PNStream_SetDataCallback(enctx->stream_ref, avctx, NULL, librv11enc_data_callback);
+    if (FAILED(res)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to set data callback.\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    if (avctx->flags & AV_CODEC_FLAG_PASS1) {
+        dummy_ui32 = SPCODINGMODE_ANALYSIS;
+    } else if (avctx->flags & AV_CODEC_FLAG_PASS2) {
+        dummy_ui32 = SPCODINGMODE_FROMFILE;
+    } else {
+        dummy_ui32 = SPCODINGMODE_ENCODE;
+    }
+    SET_RV11_PROP(SP_CODING_MODE, &dummy_ui32);
+
+    // if vbr_opt is set to true the encoder can override bitrate settings to remain subjective quality.
+    // default is false
+    SET_RV11_PROP(SP_RATECONTROL_PLUS, &enctx->rvenc_param.vbr_opt);
+
+    if (avctx->flags & AV_CODEC_FLAG_PASS1 ||
+        avctx->flags & AV_CODEC_FLAG_PASS2) {
+        if (!enctx->rvenc_param.passlog_file) {
+            av_log(avctx, AV_LOG_ERROR, "Invalid passlogfile.\n");
+            return AVERROR(EINVAL);
+        }
+        SET_RV11_PROP(SP_ANALYSIS_FILENAME, (void*)enctx->rvenc_param.passlog_file);
+    }
+
+    dummy_ui32 = 1;
+    SET_RV11_PROP(SP_CODEC_SETUP_DONE, &dummy_ui32);
+
+    dummy_ui32 = enctx->rvenc_param.max_packet_size;
+    res = PNStream_SetOutputPacketSize(enctx->stream_ref, dummy_ui32, dummy_ui32, &dummy_ui32);
+    if (FAILED(res)) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to set output packet size (%u).\n", dummy_ui32);
+        return AVERROR(EINVAL);
+    }
+
+    enctx->frame_size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width, avctx->height, 1);
+
+    enctx->frame_buf = av_malloc(enctx->frame_size);
+    if (!enctx->frame_buf) {
+        av_log(avctx, AV_LOG_ERROR, "Cannot allocate frame buffer.");
+        return AVERROR(ENOMEM);
+    }
+
+    if (!avctx->extradata_size) {
+        uint8_t *q;
+
+        avctx->extradata_size = 14;
+        avctx->extradata = av_mallocz(avctx->extradata_size);
+        if (!avctx->extradata) {
+            avctx->extradata_size = 0;
+            return AVERROR(ENOMEM);
+        }
+
+        q = avctx->extradata;
+        *q++ = 0x01; //spo_extra_flags
+        *q++ = 0x08;
+        *q++ = 0x10;
+        *q++ = 0x00;
+        *q++ = 0x40; //codec frontend version (NOT codec version!)
+        *q++ = 0x00;
+        *q++ = 0x00;
+        *q++ = 0x00;
+        PNCodec_GetHyperProperty(enctx->codec_ref, q);
+    }
+
+    return 0;
+}
+
+static int librv11enc_encode(AVCodecContext *avctx, AVPacket*pkt, const AVFrame* frame, int* got_packet)
+{
+    int ret = 0;
+    int nb;
+    HXCodecData cd;
+    HX_RESULT res;
+    LIBRV11EncContext *enctx = avctx->priv_data;
+    CodedFrameList* pCodedFrame;
+
+#define RETURN_PACKET(got_p, r) { \
+    *got_packet = got_p;          \
+    return r;                     \
+}
+
+    if (!frame && enctx->last_packet) {
+        RETURN_PACKET(0, 0);
+    }
+
+    if (frame && enctx->frame_buf && enctx->frame_size > 0) {
+        if (use_frame(&enctx->fr_convert)) {
+            nb = av_image_copy_to_buffer(enctx->frame_buf, enctx->frame_size,
+                                         (const uint8_t * const *)frame->data,
+                                         frame->linesize, avctx->pix_fmt,
+                                         avctx->width, avctx->height, 1);
+            if (nb < 0) {
+                RETURN_PACKET(0, nb);
+            }
+
+            cd.data         = enctx->frame_buf;
+            cd.length       = enctx->frame_size;
+            cd.timestamp    = frame->pts;//XXX * 1000 * av_q2d(avctx->time_base);
+            cd.flags        = (frame->pict_type == AV_PICTURE_TYPE_I) ? HX_KEYFRAME_FLAG : 0;
+            cd.last_packet  = enctx->eos;
+            cd.num_segments = 1;
+            cd.segments[0].is_valid = 1;
+            cd.segments[0].segment_offset = 0;
+
+            res = PNCodec_Input(enctx->codec_ref, &cd);
+            if (FAILED(res)) {
+                av_log(avctx, AV_LOG_ERROR, "Failed to set input data.\n");
+                RETURN_PACKET(0, AVERROR_EXTERNAL);
+            }
+        }
+        next_frame(&enctx->fr_convert);
+    } else if (!frame && !enctx->eos) {
+        enctx->eos = 1;
+
+        cd.data         = NULL;
+        cd.length       = 0;
+        cd.timestamp    = 0;
+        cd.flags        = 0;
+        cd.last_packet  = 1;
+        cd.num_segments = 1;
+        cd.segments[0].is_valid = 1;
+        cd.segments[0].segment_offset = 0;
+
+        res = PNCodec_Input(enctx->codec_ref, &cd);
+        if (FAILED(res)) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to set input data.\n");
+            RETURN_PACKET(0, AVERROR_EXTERNAL);
+        }
+    }
+
+    pCodedFrame = coded_frame_remove_header(&enctx->coded_frame_list);
+    if (pCodedFrame) {
+        if (pCodedFrame->pkt.flags & RV_PKT_FLAG_END) {
+            pCodedFrame->pkt.flags &= ~RV_PKT_FLAG_END;
+            enctx->last_packet = 1;
+        }
+
+        ret = av_packet_ref(pkt, &pCodedFrame->pkt);
+        if (ret < 0) {
+            av_log(enctx, AV_LOG_INFO, "Failed to recieve frame reference.\n");
+            free_coded_frame(pCodedFrame);
+            RETURN_PACKET(0, ret);
+        } else {
+            pkt->pts = pCodedFrame->pkt.pts;
+            pkt->dts = pCodedFrame->pkt.dts;
+
+            if (pCodedFrame->pkt.flags&AV_PKT_FLAG_KEY) {
+                pkt->flags |= AV_PKT_FLAG_KEY;
+            }
+            free_coded_frame(pCodedFrame);
+            RETURN_PACKET(1, 0);
+        }
+    }
+
+    *got_packet = 0;
+    return ret;
+}
+
+static av_cold int librv11enc_close(AVCodecContext *avctx)
+{
+    LIBRV11EncContext *enctx = avctx->priv_data;
+
+    PNStream_Close(enctx->stream_ref);
+    PNCodec_Close(enctx->codec_ref);
+    enctx->stream_ref = NULL;
+    enctx->codec_ref  = NULL;
+
+    free_frame_list(enctx->coded_frame_list);
+
+    av_freep(&enctx->frame_buf);
+    av_freep(&enctx->fr_convert.selected_map);
+    av_freep(&avctx->extradata);
+
+    return 0;
+}
+
+#define OFFSET(x) offsetof(LIBRV11EncContext, x)
+#define OFFSETP(x) offsetof(RVEncodeParam, x)
+#define OFFSETBASE OFFSET(rvenc_param)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+
+static const AVOption options[] = {
+    { "is_lossprotect", "enable loss protection feature",     OFFSETBASE+OFFSETP(loss_protect),      AV_OPT_TYPE_INT,    { .i64 = 0 }, 0,    1, VE },
+    { "output_width", "video encoded frame output width",     OFFSETBASE+OFFSETP(out_width),         AV_OPT_TYPE_INT,    { .i64 = 0 }, 0, 4096, VE },
+    { "output_height", "video encoded frame output height",   OFFSETBASE+OFFSETP(out_height),        AV_OPT_TYPE_INT,    { .i64 = 0 }, 0, 4096, VE },
+    { "rc_strategy", "which ratecontrol method to be used",   OFFSET(bitrate_vbr),                   AV_OPT_TYPE_INT,    { .i64 = 1 }, 0,    1, VE, "rc_strategy" },
+    { "bitrate", "", 0,    AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "rc_strategy" },
+    { "quality", "", 0,    AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "rc_strategy" },
+    { "complexity", "encoding complexity",                    OFFSETBASE+OFFSETP(enc_complexity),    AV_OPT_TYPE_INT,    { .i64 = 75 }, 55, 85, VE, "complexity" },
+    { "verylow",  "", 0,  AV_OPT_TYPE_CONST, {.i64=55}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" },
+    { "low",      "", 0,  AV_OPT_TYPE_CONST, {.i64=65}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" },
+    { "medium",   "", 0,  AV_OPT_TYPE_CONST, {.i64=75}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" },
+    { "high",     "", 0,  AV_OPT_TYPE_CONST, {.i64=85}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" },
+    { "framerate", "max frame rate value",                    OFFSETBASE+OFFSETP(max_framerate),     AV_OPT_TYPE_INT,    { .i64 = RV_MAX_INPUT_FRAME_RATE }, 0, RV_MAX_INPUT_FRAME_RATE, VE },
+    { "max_keyint", "max keyframe interval",                  OFFSETBASE+OFFSETP(max_keyint),        AV_OPT_TYPE_INT,    { .i64 = 5   },   0,   10, VE },
+    { "max_latency", "max video latency on start",            OFFSETBASE+OFFSETP(max_start_latency), AV_OPT_TYPE_FLOAT,  { .dbl = 4.0 }, 0.5, 60.0, VE },
+    { "vbrquality", "vbr quality value",                      OFFSETBASE+OFFSETP(vbr_quality),       AV_OPT_TYPE_INT,    { .i64 = 60  },   0,  100, VE },
+    { "passlogfile", "filename for 2 pass encoding stats",    OFFSETBASE+OFFSETP(passlog_file),      AV_OPT_TYPE_STRING, { .str = "rv11passstats.log" }, 0, 0, VE },
+    { "pon", "picture order number",                          OFFSETBASE+OFFSETP(pon),               AV_OPT_TYPE_INT,    { .i64 = 0   },   0, INT_MAX, VE },
+    { "vbr_opt", "vbr enabled",                               OFFSETBASE+OFFSETP(vbr_opt),            AV_OPT_TYPE_INT,    { .i64 = 0   },   0,       1, VE, "vbr_opt" },
+    { "true",  "", 0,      AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "vbr_opt" },
+    { "false", "", 0,      AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "vbr_opt" },
+    { NULL },
+};
+
+static const AVClass librv11_class = {
+    .class_name = "librv11",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVCodec ff_librv11_encoder = {
+    .name           = "librv11",
+    .long_name      = NULL_IF_CONFIG_SMALL("librv11 RealVideo 11 (RV60)"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_RV60,
+    .priv_data_size = sizeof(LIBRV11EncContext),
+    .init           = librv11enc_init,
+    .encode2        = librv11enc_encode,
+    .close          = librv11enc_close,
+    .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS,
+    .pix_fmts       = (const enum AVPixelFormat[]) {
+        AV_PIX_FMT_YUV420P,
+        AV_PIX_FMT_NONE
+    },
+    .priv_class     = &librv11_class,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 47a15d5..1050f60 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@ 
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  58
-#define LIBAVCODEC_VERSION_MINOR   9
+#define LIBAVCODEC_VERSION_MINOR  10
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \