mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-19 10:30:30 +00:00
4bb9548026
This is the default basetransform behaviour, being more strict than that is not really useful.
400 lines
11 KiB
C
400 lines
11 KiB
C
/* GStreamer
|
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
|
* Copyright (C) <2003> David Schleef <ds@schleef.org>
|
|
*
|
|
* 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:gstvideofilter
|
|
* @short_description: Base class for video filters
|
|
*
|
|
* <refsect2>
|
|
* <para>
|
|
* Provides useful functions and a base class for video filters.
|
|
* </para>
|
|
* <para>
|
|
* The videofilter will by default enable QoS on the parent GstBaseTransform
|
|
* to implement frame dropping.
|
|
* </para>
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstvideofilter.h"
|
|
|
|
#include <gst/video/video.h>
|
|
#include <gst/video/gstvideometa.h>
|
|
#include <gst/video/gstvideopool.h>
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_video_filter_debug);
|
|
#define GST_CAT_DEFAULT gst_video_filter_debug
|
|
|
|
#define gst_video_filter_parent_class parent_class
|
|
G_DEFINE_ABSTRACT_TYPE (GstVideoFilter, gst_video_filter,
|
|
GST_TYPE_BASE_TRANSFORM);
|
|
|
|
/* Answer the allocation query downstream. */
|
|
static gboolean
|
|
gst_video_filter_propose_allocation (GstBaseTransform * trans,
|
|
GstQuery * decide_query, GstQuery * query)
|
|
{
|
|
GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
|
|
GstVideoInfo info;
|
|
GstBufferPool *pool;
|
|
GstCaps *caps;
|
|
guint size;
|
|
|
|
if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
|
|
decide_query, query))
|
|
return FALSE;
|
|
|
|
/* passthrough, we're done */
|
|
if (decide_query == NULL)
|
|
return TRUE;
|
|
|
|
gst_query_parse_allocation (query, &caps, NULL);
|
|
|
|
if (caps == NULL)
|
|
return FALSE;
|
|
|
|
if (!gst_video_info_from_caps (&info, caps))
|
|
return FALSE;
|
|
|
|
size = GST_VIDEO_INFO_SIZE (&info);
|
|
|
|
if (gst_query_get_n_allocation_pools (query) == 0) {
|
|
GstStructure *structure;
|
|
GstAllocator *allocator = NULL;
|
|
GstAllocationParams params = { 0, 15, 0, 0, };
|
|
|
|
if (gst_query_get_n_allocation_params (query) > 0)
|
|
gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
|
|
else
|
|
gst_query_add_allocation_param (query, allocator, ¶ms);
|
|
|
|
pool = gst_video_buffer_pool_new ();
|
|
|
|
structure = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
|
|
gst_buffer_pool_config_set_allocator (structure, allocator, ¶ms);
|
|
|
|
if (allocator)
|
|
gst_object_unref (allocator);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, structure))
|
|
goto config_failed;
|
|
|
|
gst_query_add_allocation_pool (query, pool, size, 0, 0);
|
|
gst_object_unref (pool);
|
|
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
config_failed:
|
|
{
|
|
GST_ERROR_OBJECT (filter, "failed to set config");
|
|
gst_object_unref (pool);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* configure the allocation query that was answered downstream, we can configure
|
|
* some properties on it. Only called when not in passthrough mode. */
|
|
static gboolean
|
|
gst_video_filter_decide_allocation (GstBaseTransform * trans, GstQuery * query)
|
|
{
|
|
GstBufferPool *pool = NULL;
|
|
GstStructure *config;
|
|
guint min, max, size;
|
|
gboolean update_pool;
|
|
GstCaps *outcaps = NULL;
|
|
|
|
if (gst_query_get_n_allocation_pools (query) > 0) {
|
|
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
|
|
|
update_pool = TRUE;
|
|
} else {
|
|
GstVideoInfo vinfo;
|
|
|
|
gst_query_parse_allocation (query, &outcaps, NULL);
|
|
gst_video_info_init (&vinfo);
|
|
gst_video_info_from_caps (&vinfo, outcaps);
|
|
size = vinfo.size;
|
|
min = max = 0;
|
|
update_pool = FALSE;
|
|
}
|
|
|
|
if (!pool)
|
|
pool = gst_video_buffer_pool_new ();
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
if (outcaps)
|
|
gst_buffer_pool_config_set_params (config, outcaps, size, 0, 0);
|
|
gst_buffer_pool_set_config (pool, config);
|
|
|
|
if (update_pool)
|
|
gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
|
|
else
|
|
gst_query_add_allocation_pool (query, pool, size, min, max);
|
|
|
|
gst_object_unref (pool);
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
|
|
query);
|
|
}
|
|
|
|
|
|
/* our output size only depends on the caps, not on the input caps */
|
|
static gboolean
|
|
gst_video_filter_transform_size (GstBaseTransform * btrans,
|
|
GstPadDirection direction, GstCaps * caps, gsize size,
|
|
GstCaps * othercaps, gsize * othersize)
|
|
{
|
|
gboolean ret = TRUE;
|
|
GstVideoInfo info;
|
|
|
|
g_assert (size);
|
|
|
|
ret = gst_video_info_from_caps (&info, othercaps);
|
|
if (ret)
|
|
*othersize = info.size;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
gst_video_filter_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
|
|
gsize * size)
|
|
{
|
|
GstVideoInfo info;
|
|
|
|
if (!gst_video_info_from_caps (&info, caps)) {
|
|
GST_WARNING_OBJECT (btrans, "Failed to parse caps %" GST_PTR_FORMAT, caps);
|
|
return FALSE;
|
|
}
|
|
|
|
*size = info.size;
|
|
|
|
GST_DEBUG_OBJECT (btrans, "Returning size %" G_GSIZE_FORMAT " bytes"
|
|
"for caps %" GST_PTR_FORMAT, *size, caps);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_video_filter_set_caps (GstBaseTransform * trans, GstCaps * incaps,
|
|
GstCaps * outcaps)
|
|
{
|
|
GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
|
|
GstVideoFilterClass *fclass;
|
|
GstVideoInfo in_info, out_info;
|
|
gboolean res;
|
|
|
|
/* input caps */
|
|
if (!gst_video_info_from_caps (&in_info, incaps))
|
|
goto invalid_caps;
|
|
|
|
/* output caps */
|
|
if (!gst_video_info_from_caps (&out_info, outcaps))
|
|
goto invalid_caps;
|
|
|
|
fclass = GST_VIDEO_FILTER_GET_CLASS (filter);
|
|
if (fclass->set_info)
|
|
res = fclass->set_info (filter, incaps, &in_info, outcaps, &out_info);
|
|
else
|
|
res = TRUE;
|
|
|
|
if (res) {
|
|
filter->in_info = in_info;
|
|
filter->out_info = out_info;
|
|
if (fclass->transform_frame == NULL)
|
|
gst_base_transform_set_in_place (trans, TRUE);
|
|
if (fclass->transform_frame_ip == NULL)
|
|
GST_BASE_TRANSFORM_CLASS (fclass)->transform_ip_on_passthrough = FALSE;
|
|
}
|
|
filter->negotiated = res;
|
|
|
|
return res;
|
|
|
|
/* ERRORS */
|
|
invalid_caps:
|
|
{
|
|
GST_ERROR_OBJECT (filter, "invalid caps");
|
|
filter->negotiated = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_video_filter_transform (GstBaseTransform * trans, GstBuffer * inbuf,
|
|
GstBuffer * outbuf)
|
|
{
|
|
GstFlowReturn res;
|
|
GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
|
|
GstVideoFilterClass *fclass;
|
|
|
|
if (G_UNLIKELY (!filter->negotiated))
|
|
goto unknown_format;
|
|
|
|
fclass = GST_VIDEO_FILTER_GET_CLASS (filter);
|
|
if (fclass->transform_frame) {
|
|
GstVideoFrame in_frame, out_frame;
|
|
|
|
if (!gst_video_frame_map (&in_frame, &filter->in_info, inbuf,
|
|
GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))
|
|
goto invalid_buffer;
|
|
|
|
if (!gst_video_frame_map (&out_frame, &filter->out_info, outbuf,
|
|
GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))
|
|
goto invalid_buffer;
|
|
|
|
res = fclass->transform_frame (filter, &in_frame, &out_frame);
|
|
|
|
gst_video_frame_unmap (&out_frame);
|
|
gst_video_frame_unmap (&in_frame);
|
|
} else {
|
|
GST_DEBUG_OBJECT (trans, "no transform_frame vmethod");
|
|
res = GST_FLOW_OK;
|
|
}
|
|
|
|
return res;
|
|
|
|
/* ERRORS */
|
|
unknown_format:
|
|
{
|
|
GST_ELEMENT_ERROR (filter, CORE, NOT_IMPLEMENTED, (NULL),
|
|
("unknown format"));
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
invalid_buffer:
|
|
{
|
|
GST_ELEMENT_WARNING (filter, CORE, NOT_IMPLEMENTED, (NULL),
|
|
("invalid video buffer received"));
|
|
return GST_FLOW_OK;
|
|
}
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_video_filter_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
|
|
{
|
|
GstFlowReturn res;
|
|
GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
|
|
GstVideoFilterClass *fclass;
|
|
|
|
if (G_UNLIKELY (!filter->negotiated))
|
|
goto unknown_format;
|
|
|
|
fclass = GST_VIDEO_FILTER_GET_CLASS (filter);
|
|
if (fclass->transform_frame_ip) {
|
|
GstVideoFrame frame;
|
|
GstMapFlags flags;
|
|
|
|
flags = GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF;
|
|
|
|
if (!gst_base_transform_is_passthrough (trans))
|
|
flags |= GST_MAP_WRITE;
|
|
|
|
if (!gst_video_frame_map (&frame, &filter->in_info, buf, flags))
|
|
goto invalid_buffer;
|
|
|
|
res = fclass->transform_frame_ip (filter, &frame);
|
|
|
|
gst_video_frame_unmap (&frame);
|
|
} else {
|
|
GST_DEBUG_OBJECT (trans, "no transform_frame_ip vmethod");
|
|
res = GST_FLOW_OK;
|
|
}
|
|
|
|
return res;
|
|
|
|
/* ERRORS */
|
|
unknown_format:
|
|
{
|
|
GST_ELEMENT_ERROR (filter, CORE, NOT_IMPLEMENTED, (NULL),
|
|
("unknown format"));
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
invalid_buffer:
|
|
{
|
|
GST_ELEMENT_WARNING (filter, CORE, NOT_IMPLEMENTED, (NULL),
|
|
("invalid video buffer received"));
|
|
return GST_FLOW_OK;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_video_filter_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
|
|
GstMeta * meta, GstBuffer * outbuf)
|
|
{
|
|
const GstMetaInfo *info = meta->info;
|
|
const gchar *const *tags;
|
|
|
|
tags = gst_meta_api_type_get_tags (info->api);
|
|
|
|
if (!tags || (g_strv_length ((gchar **) tags) == 1
|
|
&& gst_meta_api_type_has_tag (info->api,
|
|
g_quark_from_string (GST_META_TAG_VIDEO_STR))))
|
|
return TRUE;
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (trans, inbuf,
|
|
meta, outbuf);
|
|
}
|
|
|
|
static void
|
|
gst_video_filter_class_init (GstVideoFilterClass * g_class)
|
|
{
|
|
GstBaseTransformClass *trans_class;
|
|
GstVideoFilterClass *klass;
|
|
|
|
klass = (GstVideoFilterClass *) g_class;
|
|
trans_class = (GstBaseTransformClass *) klass;
|
|
|
|
trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_filter_set_caps);
|
|
trans_class->propose_allocation =
|
|
GST_DEBUG_FUNCPTR (gst_video_filter_propose_allocation);
|
|
trans_class->decide_allocation =
|
|
GST_DEBUG_FUNCPTR (gst_video_filter_decide_allocation);
|
|
trans_class->transform_size =
|
|
GST_DEBUG_FUNCPTR (gst_video_filter_transform_size);
|
|
trans_class->get_unit_size =
|
|
GST_DEBUG_FUNCPTR (gst_video_filter_get_unit_size);
|
|
trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_filter_transform);
|
|
trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_filter_transform_ip);
|
|
trans_class->transform_meta =
|
|
GST_DEBUG_FUNCPTR (gst_video_filter_transform_meta);
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_video_filter_debug, "videofilter", 0,
|
|
"videofilter");
|
|
}
|
|
|
|
static void
|
|
gst_video_filter_init (GstVideoFilter * instance)
|
|
{
|
|
GstVideoFilter *videofilter = GST_VIDEO_FILTER (instance);
|
|
|
|
GST_DEBUG_OBJECT (videofilter, "gst_video_filter_init");
|
|
|
|
videofilter->negotiated = FALSE;
|
|
/* enable QoS */
|
|
gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (videofilter), TRUE);
|
|
}
|