mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
327 lines
9.9 KiB
C
327 lines
9.9 KiB
C
|
/* GStreamer
|
||
|
* Copyright 2023 Igalia S.L.
|
||
|
* @author: Thibault Saunier <tsaunier@igalia.com>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library 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
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
/**
|
||
|
* SECTION:element-autovideoflip
|
||
|
* @title: autovideoflip
|
||
|
*
|
||
|
* The #autovideoflip element is used to flip the video plugging the right
|
||
|
* element depending on caps and underlying buffer memory.
|
||
|
*
|
||
|
* Since: 1.24
|
||
|
*/
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <gst/video/video.h>
|
||
|
|
||
|
#include "gstautovideo.h"
|
||
|
#include "gstautovideoflip.h"
|
||
|
|
||
|
GST_DEBUG_CATEGORY (autovideoflip_debug);
|
||
|
#define GST_CAT_DEFAULT (autovideoflip_debug)
|
||
|
|
||
|
#define PROP_DIRECTION_DEFAULT GST_VIDEO_ORIENTATION_IDENTITY
|
||
|
|
||
|
/* GstVideoFlip properties */
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
PROP_VIDEO_DIRECTION
|
||
|
};
|
||
|
|
||
|
struct _GstAutoVideoFlip
|
||
|
{
|
||
|
GstBaseAutoConvert parent;
|
||
|
|
||
|
GstVideoOrientationMethod direction;
|
||
|
GList *bindings;
|
||
|
};
|
||
|
|
||
|
G_DEFINE_TYPE (GstAutoVideoFlip, gst_auto_video_flip,
|
||
|
GST_TYPE_BASE_AUTO_CONVERT);
|
||
|
|
||
|
GST_ELEMENT_REGISTER_DEFINE (autovideoflip, "autovideoflip",
|
||
|
GST_RANK_NONE, gst_auto_video_flip_get_type ());
|
||
|
|
||
|
static void
|
||
|
gst_auto_video_flip_set_property (GObject * object, guint prop_id,
|
||
|
const GValue * value, GParamSpec * pspec)
|
||
|
{
|
||
|
GstAutoVideoFlip *self = GST_AUTO_VIDEO_FLIP (object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_VIDEO_DIRECTION:
|
||
|
self->direction = g_value_get_enum (value);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_auto_video_flip_get_property (GObject * object, guint prop_id,
|
||
|
GValue * value, GParamSpec * pspec)
|
||
|
{
|
||
|
GstAutoVideoFlip *self = GST_AUTO_VIDEO_FLIP (object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_VIDEO_DIRECTION:
|
||
|
g_value_set_enum (value, self->direction);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if !GLIB_CHECK_VERSION(2, 68, 0)
|
||
|
static GObject *
|
||
|
g_binding_dup_target (GBinding * binding)
|
||
|
{
|
||
|
return g_object_ref (g_binding_get_target (binding));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static gboolean
|
||
|
element_is_handled_video_flip (GstElement * element)
|
||
|
{
|
||
|
GstElementFactory *factory = gst_element_get_factory (element);
|
||
|
|
||
|
return !g_strcmp0 (GST_OBJECT_NAME (factory), "glvideoflip") ||
|
||
|
!g_strcmp0 (GST_OBJECT_NAME (factory), "videoflip");
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
gst_auto_video_flip_transform_to (GBinding * binding, const GValue * from,
|
||
|
GValue * to_value, gpointer _udata)
|
||
|
{
|
||
|
g_value_set_enum (to_value, g_value_get_enum (from));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_auto_video_flip_deep_element_added (GstBin * bin, GstBin * sub_bin,
|
||
|
GstElement * element)
|
||
|
{
|
||
|
GstAutoVideoFlip *self = GST_AUTO_VIDEO_FLIP (bin);
|
||
|
GList *new_bindings = NULL;
|
||
|
|
||
|
if (!element_is_handled_video_flip (element))
|
||
|
goto done;
|
||
|
|
||
|
GST_OBJECT_LOCK (bin);
|
||
|
for (GList * tmp = self->bindings; tmp; tmp = tmp->next) {
|
||
|
GBinding *binding = tmp->data;
|
||
|
GObject *target = g_binding_dup_target (binding);
|
||
|
|
||
|
if (GST_ELEMENT (target) == element) {
|
||
|
GST_INFO_OBJECT (self, "Newly added element %s already bound",
|
||
|
GST_OBJECT_NAME (gst_element_get_factory (element)));
|
||
|
GST_OBJECT_UNLOCK (bin);
|
||
|
gst_object_unref (target);
|
||
|
goto done;
|
||
|
}
|
||
|
gst_object_unref (target);
|
||
|
}
|
||
|
GST_OBJECT_UNLOCK (bin);
|
||
|
|
||
|
new_bindings = g_list_prepend (new_bindings,
|
||
|
g_object_bind_property_full (bin, "video-direction",
|
||
|
element, "video-direction",
|
||
|
G_BINDING_SYNC_CREATE | G_BINDING_DEFAULT,
|
||
|
gst_auto_video_flip_transform_to, NULL, NULL, NULL)
|
||
|
);
|
||
|
|
||
|
GST_OBJECT_LOCK (bin);
|
||
|
self->bindings = g_list_concat (self->bindings, new_bindings);
|
||
|
GST_OBJECT_UNLOCK (bin);
|
||
|
|
||
|
done:
|
||
|
GST_BIN_CLASS (gst_auto_video_flip_parent_class)->deep_element_added (bin,
|
||
|
sub_bin, element);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_auto_video_flip_deep_element_removed (GstBin * bin, GstBin * sub_bin,
|
||
|
GstElement * element)
|
||
|
{
|
||
|
GList *bindings = NULL;
|
||
|
GstAutoVideoFlip *self = GST_AUTO_VIDEO_FLIP (bin);
|
||
|
|
||
|
if (!element_is_handled_video_flip (element))
|
||
|
goto done;
|
||
|
|
||
|
GST_OBJECT_LOCK (bin);
|
||
|
for (GList * tmp = self->bindings; tmp; tmp = tmp->next) {
|
||
|
GBinding *binding = tmp->data;
|
||
|
GObject *target = g_binding_dup_target (binding);
|
||
|
|
||
|
if (GST_ELEMENT (target) == element) {
|
||
|
GList *node = tmp;
|
||
|
|
||
|
bindings = g_list_prepend (bindings, binding);
|
||
|
tmp = tmp->prev;
|
||
|
|
||
|
self->bindings = g_list_delete_link (self->bindings, node);
|
||
|
if (!tmp)
|
||
|
break;
|
||
|
}
|
||
|
gst_object_unref (target);
|
||
|
}
|
||
|
GST_OBJECT_UNLOCK (bin);
|
||
|
|
||
|
done:
|
||
|
GST_BIN_CLASS (gst_auto_video_flip_parent_class)->deep_element_removed (bin,
|
||
|
sub_bin, element);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
gst_auto_video_flip_class_init (GstAutoVideoFlipClass * klass)
|
||
|
{
|
||
|
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||
|
GstBinClass *gstbin_class = (GstBinClass *) klass;
|
||
|
|
||
|
GST_DEBUG_CATEGORY_INIT (autovideoflip_debug, "autovideoflip", 0,
|
||
|
"Auto video flipper");
|
||
|
|
||
|
gobject_class->set_property = gst_auto_video_flip_set_property;
|
||
|
gobject_class->get_property = gst_auto_video_flip_get_property;
|
||
|
g_object_class_install_property (gobject_class, PROP_VIDEO_DIRECTION,
|
||
|
g_param_spec_enum ("video-direction", "video-direction",
|
||
|
"Video direction: rotation and flipping",
|
||
|
GST_TYPE_VIDEO_ORIENTATION_METHOD, PROP_DIRECTION_DEFAULT,
|
||
|
GST_PARAM_CONTROLLABLE | GST_PARAM_MUTABLE_PLAYING |
|
||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
gst_element_class_set_static_metadata (gstelement_class,
|
||
|
"Flip the video plugging the right element depending on caps",
|
||
|
"Bin/Filter/Effect/Video",
|
||
|
"Selects the right video flip element based on the caps",
|
||
|
"Thibault Saunier <tsaunier@igalia.com>");
|
||
|
|
||
|
gstbin_class->deep_element_added = gst_auto_video_flip_deep_element_added;
|
||
|
gstbin_class->deep_element_removed = gst_auto_video_flip_deep_element_removed;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gst_auto_video_flip_init (GstAutoVideoFlip * self)
|
||
|
{
|
||
|
self->direction = PROP_DIRECTION_DEFAULT;
|
||
|
|
||
|
/* *INDENT-OFF* */
|
||
|
static const GstAutoVideoFilterGenerator gen[] = {
|
||
|
{
|
||
|
.first_elements = { "bayer2rgb", NULL},
|
||
|
.colorspace_converters = { "videoconvertscale", NULL },
|
||
|
.last_elements = { NULL } ,
|
||
|
.filters = { "videoflip", NULL },
|
||
|
.rank = GST_RANK_MARGINAL,
|
||
|
},
|
||
|
{
|
||
|
.first_elements = { NULL, },
|
||
|
.colorspace_converters = { "videoconvertscale", NULL },
|
||
|
.last_elements = { NULL },
|
||
|
.filters = { "videoflip" },
|
||
|
.rank = GST_RANK_SECONDARY,
|
||
|
},
|
||
|
{
|
||
|
.first_elements = { NULL, },
|
||
|
.colorspace_converters = { "videoconvertscale", NULL },
|
||
|
.last_elements = { "rgb2bayer", NULL },
|
||
|
.filters = { "videoflip" },
|
||
|
.rank = GST_RANK_MARGINAL,
|
||
|
},
|
||
|
{
|
||
|
.first_elements = { "glupload" },
|
||
|
.colorspace_converters = { "glcolorconvert", NULL },
|
||
|
.last_elements = { NULL, },
|
||
|
.filters = { "glvideoflip" },
|
||
|
.rank = GST_RANK_PRIMARY,
|
||
|
},
|
||
|
{
|
||
|
.first_elements = { NULL },
|
||
|
.colorspace_converters = { "glcolorconvert", NULL },
|
||
|
.last_elements = { NULL, },
|
||
|
.filters = { "glvideoflip" },
|
||
|
.rank = GST_RANK_PRIMARY,
|
||
|
},
|
||
|
{
|
||
|
.first_elements = { "videoconvertscale", "glupload", NULL },
|
||
|
.colorspace_converters = { NULL },
|
||
|
.last_elements = { NULL },
|
||
|
.filters = { "glvideoflip", NULL },
|
||
|
.rank = GST_RANK_MARGINAL + 1,
|
||
|
},
|
||
|
{
|
||
|
.first_elements = { NULL },
|
||
|
.colorspace_converters = { NULL },
|
||
|
.last_elements = { "gldownload", NULL },
|
||
|
.filters = { "glvideoflip", NULL },
|
||
|
.rank = GST_RANK_MARGINAL + 1,
|
||
|
},
|
||
|
{ /* CUDA -> GL */
|
||
|
.first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL },
|
||
|
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
|
||
|
.last_elements = { NULL },
|
||
|
.filters = { "glvideoflip", NULL },
|
||
|
.rank = GST_RANK_PRIMARY - 1,
|
||
|
},
|
||
|
{ /* CUDA -> CUDA */
|
||
|
.first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL },
|
||
|
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
|
||
|
.last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL },
|
||
|
.filters = { "glvideoflip", NULL },
|
||
|
.rank = GST_RANK_SECONDARY - 1,
|
||
|
},
|
||
|
{ /* Software -> CUDA (uploading as soon as possible) */
|
||
|
.first_elements = { "glupload", NULL },
|
||
|
.colorspace_converters = { "glcoloconvert", NULL },
|
||
|
.last_elements = { "cudaupload", "capsfilter caps=video/x-raw(memory:CUDAMemory)", NULL },
|
||
|
.filters = { "glvideoflip", NULL },
|
||
|
.rank = GST_RANK_MARGINAL,
|
||
|
},
|
||
|
{ /* CUDA -> Software */
|
||
|
.first_elements = { "capsfilter caps=video/x-raw(memory:CUDAMemory)", "cudadownload", NULL },
|
||
|
.colorspace_converters = { "glcolorconvert", "glcolorscale", "glcolorconvert", NULL },
|
||
|
.last_elements = { "gldownload", NULL },
|
||
|
.filters = { "glvideoflip", NULL },
|
||
|
.rank = GST_RANK_MARGINAL,
|
||
|
},
|
||
|
{ /* Worst case we upload/download as required */
|
||
|
.first_elements = { NULL},
|
||
|
.colorspace_converters = { NULL },
|
||
|
.last_elements = { NULL },
|
||
|
.filters = { NULL },
|
||
|
.rank = 0,
|
||
|
},
|
||
|
};
|
||
|
/* *INDENT-ON* */
|
||
|
|
||
|
|
||
|
gst_auto_video_register_well_known_bins (GST_BASE_AUTO_CONVERT (self), gen);
|
||
|
}
|