[vlc-devel] [PATCHv4 10/12] codec: add rav1e encoder module

Thomas Guillem thomas at gllm.fr
Tue Sep 8 14:13:39 CEST 2020


From: Kartik Ohri <kartikohri13 at gmail.com>

Co-Authored-By: Thomas Guillem <thomas at gllm.fr>
---
 configure.ac              |   5 +
 modules/codec/Makefile.am |   8 +
 modules/codec/rav1e.c     | 303 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 316 insertions(+)
 create mode 100644 modules/codec/rav1e.c

diff --git a/configure.ac b/configure.ac
index 7b40650216d..99e46699424 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2882,6 +2882,11 @@ dnl  AOM encoder/decoder plugin
 dnl
 PKG_ENABLE_MODULES_VLC([AOM], [], [aom], [libaom AV1 encoder and decoder (default auto)])
 
+dnl
+dnl  Rav1e encoder plugin
+dnl
+PKG_ENABLE_MODULES_VLC([RAV1E], [], [rav1e], [rav1e encoder module codec (default auto)])
+
 dnl
 dnl  Dav1d decoder plugin
 dnl
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index 0aede611336..ec6447ddf9c 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -544,6 +544,14 @@ libaom_plugin_la_LIBADD = $(AOM_LIBS)
 EXTRA_LTLIBRARIES += libaom_plugin.la
 codec_LTLIBRARIES += $(LTLIBaom)
 
+librav1e_plugin_la_SOURCES = codec/rav1e.c
+librav1e_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
+librav1e_plugin_la_CFLAGS = $(AM_CFLAGS) $(RAV1E_CFLAGS)
+librav1e_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(codecdir)'
+librav1e_plugin_la_LIBADD = $(RAV1E_LIBS)
+EXTRA_LTLIBRARIES += librav1e_plugin.la
+codec_LTLIBRARIES += $(LTLIBrav1e)
+
 libtwolame_plugin_la_SOURCES = codec/twolame.c
 libtwolame_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -DLIBTWOLAME_STATIC
 libtwolame_plugin_la_CFLAGS = $(AM_CFLAGS) $(TWOLAME_CFLAGS)
diff --git a/modules/codec/rav1e.c b/modules/codec/rav1e.c
new file mode 100644
index 00000000000..fe6098d7047
--- /dev/null
+++ b/modules/codec/rav1e.c
@@ -0,0 +1,303 @@
+/*****************************************************************************
+ * rav1e.c : rav1e encoder (AV1) module
+ *****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * Authors: Kartik Ohri <kartikohri13 at gmail.com>
+ *          Thomas Guillem <thomas at gllm.fr>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <rav1e/rav1e.h>
+
+#define SOUT_CFG_PREFIX "sout-rav1e-"
+
+typedef struct
+{
+    struct RaContext *ra_context;
+    date_t date;
+    bool date_set;
+} encoder_sys_t;
+
+static block_t *Encode(encoder_t *enc, picture_t *p_pict)
+{
+    encoder_sys_t *sys = enc->p_sys;
+    RaContext *ctx = sys->ra_context;
+    block_t *p_out = NULL;
+
+    RaFrame *frame;
+    if (p_pict != NULL)
+    {
+        if (!sys->date_set && p_pict->date != VLC_TICK_INVALID)
+        {
+            date_Set(&sys->date, p_pict->date);
+            sys->date_set = true;
+        }
+
+        frame = rav1e_frame_new(ctx);
+        if (frame == NULL) {
+            msg_Err(enc, "Unable to create new frame\n");
+            goto error;
+        }
+
+        for (int idx = 0; idx < p_pict->i_planes; idx++)
+            rav1e_frame_fill_plane(frame, idx,
+                                   p_pict->p[idx].p_pixels,
+                                   p_pict->p[idx].i_pitch * p_pict->p[idx].i_visible_lines,
+                                   p_pict->p[idx].i_pitch,
+                                   p_pict->p[idx].i_pixel_pitch);
+    }
+    else
+        frame = NULL; /* Drain with a NULL frame */
+
+    int ret = rav1e_send_frame(ctx, frame);
+    rav1e_frame_unref(frame);
+    if (ret != 0)
+    {
+        msg_Err(enc, "rav1e_send_frame failed: %d: %s", ret,
+                rav1e_status_to_str(ret));
+        goto error;
+    }
+
+    bool again;
+    do
+    {
+        RaPacket *pkt = NULL;
+        ret = rav1e_receive_packet(ctx, &pkt);
+
+        switch (ret)
+        {
+            case RA_ENCODER_STATUS_SUCCESS:
+            {
+                block_t *p_block = block_Alloc(pkt->len);
+                if (unlikely(p_block == NULL))
+                {
+                    block_ChainRelease(p_out);
+                    p_out = NULL;
+                    break;
+                }
+
+                memcpy(p_block->p_buffer, pkt->data, pkt->len);
+                p_block->i_dts = p_block->i_pts = date_Get(&sys->date);
+
+                if (pkt->frame_type == RA_FRAME_TYPE_KEY)
+                    p_block->i_flags |= BLOCK_FLAG_TYPE_I;
+                block_ChainAppend(&p_out, p_block);
+                rav1e_packet_unref(pkt);
+            }
+            /* fall-through */
+            case RA_ENCODER_STATUS_ENCODED:
+                again = true;
+                break;
+            case RA_ENCODER_STATUS_LIMIT_REACHED:
+            case RA_ENCODER_STATUS_NEED_MORE_DATA:
+                again = false;
+                break;
+            default:
+                msg_Err(enc, "rav1e_receive_packet() failed: %d: %s", ret,
+                        rav1e_status_to_str(ret));
+                goto error;
+        }
+    } while (again);
+
+    return p_out;
+
+error:
+    free(p_out);
+    return NULL;
+}
+
+static int OpenEncoder(vlc_object_t *this)
+{
+    encoder_t *enc = (encoder_t *) this;
+    encoder_sys_t *sys;
+
+    if (enc->fmt_out.i_codec != VLC_CODEC_AV1)
+        return VLC_EGENERIC;
+
+    static const char *const ppsz_rav1e_options[] = {
+        "bitdepth", "tile-rows", "tile-columns", NULL
+    };
+
+    config_ChainParse(enc, SOUT_CFG_PREFIX, ppsz_rav1e_options, enc->p_cfg);
+
+    sys = malloc(sizeof(*sys));
+    if (sys == NULL)
+        return VLC_ENOMEM;
+
+    enc->p_sys = sys;
+
+    struct RaConfig *ra_config = rav1e_config_default();
+    if (ra_config == NULL)
+    {
+        msg_Err(enc, "Unable to initialize configuration\n");
+        free(sys);
+        return VLC_EGENERIC;
+    }
+
+    int ret;
+
+    ret = rav1e_config_parse_int(ra_config, "height", enc->fmt_in.video.i_visible_height);
+    if (ret < 0)
+    {
+        msg_Err(enc, "Unable to set height\n");
+        goto error;
+    }
+
+    ret = rav1e_config_parse_int(ra_config, "width", enc->fmt_in.video.i_visible_width);
+    if (ret < 0) {
+        msg_Err(enc, "Unable to set width\n");
+        goto error;
+    }
+
+    RaRational *timebase = malloc(sizeof(RaRational));
+    if (timebase == NULL)
+    {
+        msg_Err(enc, "%s", "Unable to set width\n");
+        goto error;
+    }
+
+    timebase->num = enc->fmt_in.video.i_frame_rate_base;
+    timebase->den = enc->fmt_in.video.i_frame_rate;
+    rav1e_config_set_time_base(ra_config, *timebase);
+
+    int tile_rows = var_InheritInteger(enc, SOUT_CFG_PREFIX "tile-rows");
+    int tile_columns = var_InheritInteger(enc, SOUT_CFG_PREFIX "tile-columns");
+    tile_rows = 1 << tile_rows;
+    tile_columns = 1 << tile_columns;
+
+    ret = rav1e_config_parse_int(ra_config, "tile_rows", tile_rows);
+    if (ret < 0)
+    {
+        msg_Err(enc, "Unable to set tile rows\n");
+        goto error;
+    }
+
+    ret = rav1e_config_parse_int(ra_config, "tile_cols", tile_columns);
+    if (ret < 0)
+    {
+        msg_Err(enc, "Unable to set tile columns\n");
+        goto error;
+    }
+
+    int bitdepth = var_InheritInteger(enc, SOUT_CFG_PREFIX "bitdepth");
+    int profile = var_InheritInteger(enc, SOUT_CFG_PREFIX "profile");
+
+    RaChromaSampling chroma_sampling;
+    switch (profile)
+    {
+        case 2:
+            chroma_sampling = RA_CHROMA_SAMPLING_CS422;
+            enc->fmt_in.i_codec = bitdepth == 8 ? VLC_CODEC_I422 : VLC_CODEC_I422_10L;
+            break;
+        case 1:
+            chroma_sampling = RA_CHROMA_SAMPLING_CS444;
+            enc->fmt_in.i_codec = bitdepth == 8 ? VLC_CODEC_I444 : VLC_CODEC_I444_10L;
+            break;
+        default:
+        case 0:
+            chroma_sampling = RA_CHROMA_SAMPLING_CS420;
+            enc->fmt_in.i_codec = bitdepth == 8 ? VLC_CODEC_I420 : VLC_CODEC_I420_10L;
+            break;
+    }
+
+    RaChromaSamplePosition sample_pos;
+    switch (enc->fmt_in.video.chroma_location)
+    {
+        case CHROMA_LOCATION_LEFT:
+            sample_pos = RA_CHROMA_SAMPLE_POSITION_VERTICAL;
+            break;
+        case CHROMA_LOCATION_TOP_LEFT:
+            sample_pos = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
+            break;
+        default:
+            sample_pos = RA_CHROMA_SAMPLE_POSITION_UNKNOWN;
+            break;
+    }
+
+    RaPixelRange pixel_range;
+    switch (enc->fmt_in.video.color_range)
+    {
+        case COLOR_RANGE_FULL:
+            pixel_range = RA_PIXEL_RANGE_FULL;
+            break;
+        case COLOR_RANGE_LIMITED:
+        default:
+            pixel_range = RA_PIXEL_RANGE_LIMITED;
+            break;
+    }
+    ret = rav1e_config_set_pixel_format(ra_config, bitdepth, chroma_sampling,
+                                        sample_pos, pixel_range);
+    if (ret < 0)
+    {
+        msg_Err(enc, "Unable to set pixel format\n");
+        goto error;
+    }
+
+
+    sys->ra_context = rav1e_context_new(ra_config);
+    if (!sys->ra_context)
+    {
+        msg_Err(enc, "Unable to allocate a new context\n");
+        goto error;
+    }
+    rav1e_config_unref(ra_config);
+
+    date_Init(&sys->date, enc->fmt_out.video.i_frame_rate,
+              enc->fmt_out.video.i_frame_rate_base);
+    sys->date_set = false;
+
+    enc->pf_encode_video = Encode;
+    return VLC_SUCCESS;
+
+error:
+    rav1e_config_unref(ra_config);
+    free(sys);
+    return VLC_EGENERIC;
+}
+
+static void CloseEncoder(vlc_object_t* this)
+{
+    encoder_t *enc = (encoder_t *) this;
+    encoder_sys_t *sys = enc->p_sys;
+    rav1e_context_unref(sys->ra_context);
+    free(sys);
+}
+
+static const int bitdepth_values_list[] = {8, 10};
+static const char *bitdepth_values_name_list[] = {N_("8 bpp"), N_("10 bpp")};
+
+vlc_module_begin()
+    set_shortname("rav1e")
+    set_description(N_("rav1e video encoder"))
+    set_capability("encoder", 101)
+    set_callbacks(OpenEncoder, CloseEncoder)
+    add_integer(SOUT_CFG_PREFIX "profile", 0, "Profile", NULL, true)
+        change_integer_range(0, 3)
+    add_integer(SOUT_CFG_PREFIX "bitdepth", 8, "Bit Depth", NULL, true)
+        change_integer_list(bitdepth_values_list, bitdepth_values_name_list)
+    add_integer(SOUT_CFG_PREFIX "tile-rows", 0, "Tile Rows (in log2 units)", NULL, true)
+        change_integer_range(0, 6)
+    add_integer(SOUT_CFG_PREFIX "tile-columns", 0, "Tile Columns (in log2 units)", NULL, true)
+        change_integer_range(0, 6)
+vlc_module_end()
-- 
2.28.0



More information about the vlc-devel mailing list