[vlc-devel] [PATCH 4/9] video_filter: add a video filter converting between different 3D stereo formats

Felix Abecassis felix.abecassis at gmail.com
Fri Aug 8 20:13:29 CEST 2014


---
 modules/video_filter/Modules.am         |   2 +
 modules/video_filter/stereo3d_convert.c | 360 ++++++++++++++++++++++++++++++++
 2 files changed, 362 insertions(+)
 create mode 100644 modules/video_filter/stereo3d_convert.c

diff --git a/modules/video_filter/Modules.am b/modules/video_filter/Modules.am
index 3bb8cdb..e1eb973 100644
--- a/modules/video_filter/Modules.am
+++ b/modules/video_filter/Modules.am
@@ -89,6 +89,7 @@ SOURCES_blendbench = blendbench.c
 SOURCES_postproc = postproc.c
 SOURCES_scene = scene.c
 SOURCES_sepia = sepia.c
+SOURCES_stereo3d_convert = stereo3d_convert.c
 SOURCES_yuvp = yuvp.c
 SOURCES_antiflicker = antiflicker.c
 SOURCES_atmo = atmo/atmo.cpp \
@@ -154,6 +155,7 @@ video_filter_LTLIBRARIES += \
 	libsepia_plugin.la \
 	libsharpen_plugin.la \
 	libsubsdelay_plugin.la \
+	libstereo3d_convert_plugin.la \
 	libtransform_plugin.la \
 	libwave_plugin.la \
 	libgradfun_plugin.la \
diff --git a/modules/video_filter/stereo3d_convert.c b/modules/video_filter/stereo3d_convert.c
new file mode 100644
index 0000000..150c31c
--- /dev/null
+++ b/modules/video_filter/stereo3d_convert.c
@@ -0,0 +1,360 @@
+/*****************************************************************************
+ * stereo3d_convert.c : conversion filter between different 3D stereo types
+ *****************************************************************************
+ * Copyright (C) 2014 VLC authors and VideoLAN
+ *
+ * Authors: Felix Abecassis <felix.abecassis at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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_filter.h>
+#include <vlc_modules.h>
+#include <vlc_plugin.h>
+#include <vlc_stereo3d.h>
+
+#include <math.h>
+
+static int Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+static picture_t *Filter(filter_t *, picture_t *);
+
+struct filter_sys_t
+{
+    filter_t *scale_filter;
+};
+
+vlc_module_begin()
+    set_description(N_("3D Stereo conversion filter"))
+    set_shortname(N_( "3D Stereo conversion"))
+    set_capability("video filter2", 100)
+    set_category(CAT_VIDEO)
+    set_subcategory(SUBCAT_VIDEO_VFILTER)
+    add_shortcut("stereo3d_convert")
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+static picture_t *BufferNew(filter_t *filter)
+{
+    picture_t *pic = (picture_t*)filter->owner.sys;
+    return pic;
+}
+
+static void BufferDelete(filter_t *filter, picture_t *p_picture)
+{
+    VLC_UNUSED(filter);
+    picture_Release(p_picture);
+}
+
+static int Open(vlc_object_t *p_this)
+{
+    filter_t *filter = (filter_t *)p_this;
+
+    const video_format_t *fmt_in = &filter->fmt_in.video;
+    video_format_t *fmt_out = &filter->fmt_out.video;
+    if (fmt_in->stereo_format.mode == fmt_out->stereo_format.mode)
+        return VLC_EGENERIC;
+
+    filter_sys_t *sys = calloc(1, sizeof(*sys));
+    if (!sys)
+        return VLC_ENOMEM;
+    filter->p_sys = sys;
+
+    const vlc_chroma_description_t *chroma_desc = vlc_fourcc_GetChromaDescription(fmt_in->i_chroma);
+    if (chroma_desc->plane_count == 0)
+        goto error;
+
+    /*
+     * A video filter is needed to convert from the source
+     * stereoscopic type to the target stereoscopic type.
+     *
+     * For instance when converting from a SBS_LR picture of dimension
+     * (w1, h1) to a TB_LR picture of dimension (w2, h2). The left
+     * rectangle of size (w1/2, h1) of the input picture is stretched
+     * to the top rectangle of size (w2, h2/2) of the output picture.
+     */
+    filter_t *scale_filter = (filter_t *)vlc_object_create(filter, sizeof(filter_t));
+    if (!scale_filter)
+        goto error;
+    sys->scale_filter = scale_filter;
+
+    es_format_Init(&scale_filter->fmt_in, VIDEO_ES, 0);
+    scale_filter->fmt_in.video = filter->fmt_in.video;
+    es_format_Init(&scale_filter->fmt_out, VIDEO_ES, 0);
+    scale_filter->fmt_out.video = filter->fmt_out.video;
+    scale_filter->fmt_in.video.stereo_format.mode = VLC_STEREO3D_2D;
+    scale_filter->fmt_out.video.stereo_format.mode = VLC_STEREO3D_2D;
+
+    /*
+     * Overload the picture allocation function of the internal filter
+     * in order to pass the picture to render to.
+     */
+    scale_filter->owner.video.buffer_new = BufferNew;
+    scale_filter->owner.video.buffer_del = BufferDelete;
+    scale_filter->p_module = module_need(sys->scale_filter, "video filter2", NULL, false);
+    if (!scale_filter->p_module)
+        goto error;
+
+    filter->pf_video_filter = Filter;
+
+    return VLC_SUCCESS;
+
+error:
+    Close(p_this);
+    return VLC_EGENERIC;
+}
+
+static void Close(vlc_object_t *p_this)
+{
+    filter_t *filter = (filter_t *)p_this;
+    filter_sys_t *sys = filter->p_sys;
+
+    if (sys)
+    {
+        if (sys->scale_filter)
+        {
+            if (sys->scale_filter->p_module)
+                module_unneed(sys->scale_filter, sys->scale_filter->p_module);
+            vlc_object_release(sys->scale_filter);
+        }
+    }
+
+    free(sys);
+}
+
+/*
+ * Create two pictures storing the left and right eyes of src
+ * according to the stereoscopic type. The created pictures simply
+ * wrap the original pictures therefore they should not be freed.
+ *
+ * For instance, with an TB_LR picture, *res_left will point to the
+ * top half of src and *res_right will point to the bottom half of
+ * src. The height of each picture is half the height of the original
+ * picture.
+ */
+static void ExtractPictures(picture_t *src, const video_format_t *src_format,
+                            picture_t **res_left, picture_t **res_right)
+{
+    video_format_t res_format = src->format;
+    res_format.stereo_format.mode = VLC_STEREO3D_2D;
+    res_format.stereo_format.flags = 0;
+
+    *res_left = NULL;
+    *res_right = NULL;
+
+    const stereo3d_format_t *stereo_format = &src_format->stereo_format;
+
+    if (stereo_format->mode == VLC_STEREO3D_2D)
+    {
+        *res_left = src;
+        return;
+    }
+
+    picture_resource_t src_resource = { .p_sys = src->p_sys };
+    for (int i = 0; i < src->i_planes; ++i)
+    {
+        src_resource.p[i].p_pixels = src->p[i].p_pixels;
+        src_resource.p[i].i_lines  = src->p[i].i_lines;
+        src_resource.p[i].i_pitch  = src->p[i].i_pitch;
+    }
+    picture_resource_t left_resource = src_resource;
+    picture_resource_t right_resource = src_resource;
+
+    /*
+     * Depending on the stereoscopic type, we have to tweak the pitch
+     * and/or the pointer in order to wrap the original picture.
+     *
+     * Computations are made assuming left-right (LR) ordering,
+     * right-left ordering is supported by swapping pictures at the
+     * end.
+     */
+    if (stereo_format->mode == VLC_STEREO3D_SBS)
+    {
+        res_format.i_visible_width /= 2;
+        res_format.i_width = res_format.i_visible_width;
+
+        /* Advance to the start of the right picture. */
+        for (int i = 0; i < src->i_planes; ++i)
+            right_resource.p[i].p_pixels += src->p[i].i_visible_pitch / 2;
+    }
+    else if (stereo_format->mode == VLC_STEREO3D_TB)
+    {
+        res_format.i_visible_height /= 2;
+        res_format.i_height = res_format.i_visible_height;
+
+        for (int i = 0; i < src->i_planes; ++i)
+        {
+            left_resource.p[i].i_lines = src->p[i].i_visible_lines / 2;
+            right_resource.p[i].i_lines = src->p[i].i_visible_lines / 2;
+
+            /* Skip the left picture (i.e. the first half of the buffer) */
+            right_resource.p[i].p_pixels += src->p[i].i_pitch * left_resource.p[i].i_lines;
+        }
+    }
+    else if (stereo_format->mode == VLC_STEREO3D_ROW)
+    {
+        res_format.i_visible_height /= 2;
+        res_format.i_height = res_format.i_visible_height;
+
+        for (int i = 0; i < src->i_planes; ++i)
+        {
+            left_resource.p[i].i_lines = src->p[i].i_visible_lines / 2;
+            right_resource.p[i].i_lines = src->p[i].i_visible_lines / 2;
+
+            /*
+             * Double the pitch of left and right pictures so only one
+             * line over two is used.
+             */
+            right_resource.p[i].i_pitch *= 2;
+            left_resource.p[i].i_pitch *= 2;
+
+            /* Advance to second line. */
+            right_resource.p[i].p_pixels += src->p[i].i_pitch;
+        }
+    }
+    else if (stereo_format->mode == VLC_STEREO3D_COL)
+    {
+        res_format.i_visible_width /= 2;
+        res_format.i_width = res_format.i_visible_width;
+
+        /* Advance to the start of the right picture. */
+        for (int i = 0; i < src->i_planes; ++i)
+        {
+            right_resource.p[i].p_pixels += src->p[i].i_pixel_pitch;
+        }
+    }
+
+    picture_t *left = picture_NewFromResource(&res_format, &left_resource);
+    if (!left)
+        return;
+    picture_t *right = picture_NewFromResource(&res_format, &right_resource);
+    if (!right)
+    {
+        picture_Release(left);
+        return;
+    }
+
+    if (stereo_format->mode == VLC_STEREO3D_COL)
+    {
+        for (int i = 0; i < left->i_planes; ++i)
+        {
+            left->p[i].i_pixel_pitch *= 2;
+            right->p[i].i_pixel_pitch *= 2;
+        }
+    }
+
+    /* Swap pictures pointers in case of right-left ordering */
+    bool is_swapped = stereo_format->flags & VLC_STEREO3D_SWAP_EYES;
+    if (is_swapped)
+    {
+        *res_left = right;
+        *res_right = left;
+    }
+    else
+    {
+        *res_left = left;
+        *res_right = right;
+    }
+}
+
+static void GenericRescale(picture_t *dst, const picture_t *src)
+{
+    for (int i = 0; i < src->i_planes; ++i)
+    {
+        const uint8_t *s = src->p[i].p_pixels;
+        uint8_t *d = dst->p[i].p_pixels;
+
+        uint32_t src_pitch = src->p[i].i_pitch;
+        uint32_t dst_pitch = dst->p[i].i_pitch;
+        uint32_t src_pixel_pitch = src->p[i].i_pixel_pitch;
+        uint32_t dst_pixel_pitch = dst->p[i].i_pixel_pitch;
+
+        float step_r = src->p[i].i_visible_lines / (float)dst->p[i].i_visible_lines;
+        float step_c = src->p[i].i_visible_pitch / (float)dst->p[i].i_visible_pitch;
+        float pos_r = 0.f;
+        for (int r = 0; r < dst->p[i].i_visible_lines; r++, pos_r += step_r)
+        {
+            float pos_c = 0.f;
+            for (int c = 0; c < dst->p[i].i_visible_pitch; ++c, pos_c += step_c)
+            {
+                int src_r = __MIN(roundf(pos_r), src->p[i].i_visible_lines);
+                int src_c = __MIN(roundf(pos_c), src->p[i].i_visible_pitch);
+
+                d[r * dst_pitch + c * dst_pixel_pitch] = s[src_r * src_pitch + src_c * src_pixel_pitch];
+            }
+        }
+    }
+}
+
+static void Rescale(filter_t *filter, picture_t *dst, picture_t *src)
+{
+    filter_sys_t *sys = filter->p_sys;
+    filter_t *scale_filter = sys->scale_filter;
+
+    /* Increase refcount to avoid picture src being released in the internal filter */
+    picture_Hold(src);
+    scale_filter->fmt_in.video = src->format;
+    scale_filter->fmt_out.video = dst->format;
+
+    if (filter->fmt_in.video.stereo_format.mode == VLC_STEREO3D_COL
+        || filter->fmt_out.video.stereo_format.mode == VLC_STEREO3D_COL)
+    {
+        GenericRescale(dst, src);
+    }
+    else
+    {
+        scale_filter->owner.sys = dst;
+        scale_filter->pf_video_filter(scale_filter, src);
+    }
+}
+
+static picture_t *Filter(filter_t *filter, picture_t *src)
+{
+    const video_format_t *fmt_in = &filter->fmt_in.video;
+    video_format_t *fmt_out = &filter->fmt_out.video;
+
+    if (fmt_in->stereo_format.mode == VLC_STEREO3D_2D
+        || fmt_in->stereo_format.mode == fmt_out->stereo_format.mode)
+        return src;
+
+    /*
+     * Extract left/right pictures from src and dst and use the
+     * internal filter to copy src_left to dst_left and src_right to
+     * dst_right
+     */
+    picture_t *src_left;
+    picture_t *src_right;
+    ExtractPictures(src, fmt_in, &src_left, &src_right);
+
+    picture_t *dst = filter_NewPicture(filter);
+    picture_t *dst_left;
+    picture_t *dst_right;
+    ExtractPictures(dst, fmt_out, &dst_left, &dst_right);
+
+    if (src_left && dst_left)
+        Rescale(filter, dst_left, src_left);
+    if (src_right && dst_right)
+        Rescale(filter, dst_right, src_right);
+
+    dst->date = src->date;
+    picture_Release(src);
+
+    return dst;
+}
-- 
1.9.1




More information about the vlc-devel mailing list