[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