mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
413a60959d
the calculations for drawing the videomark is being repeated in for loop unnecessarily. Moving this outside of for loop such that the code need not be executed evertime the loop is executed. https://bugzilla.gnome.org/show_bug.cgi?id=744371
418 lines
14 KiB
C
418 lines
14 KiB
C
/* GStreamer
|
|
* Copyright (C) <2007> Wim Taymans <wim@fluendo.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 Street, Suite 500,
|
|
* Boston, MA 02110-1335, USA.
|
|
*/
|
|
/**
|
|
* SECTION:element-simplevideomark
|
|
* @see_also: #GstVideoDetect
|
|
*
|
|
* This plugin produces #GstSimpleVideoMark::pattern-count squares in the bottom left
|
|
* corner of the video frames. The squares have a width and height of
|
|
* respectively #GstSimpleVideoMark:pattern-width and #GstSimpleVideoMark:pattern-height.
|
|
* Even squares will be black and odd squares will be white.
|
|
*
|
|
* After writing the pattern, #GstSimpleVideoMark:pattern-data-count squares after the
|
|
* pattern squares are produced as the bitarray given in
|
|
* #GstSimpleVideoMark:pattern-data. 1 bits will produce white squares and 0 bits will
|
|
* produce black squares.
|
|
*
|
|
* The element can be enabled with the #GstSimpleVideoMark:enabled property. It is
|
|
* mostly used together with the #GstVideoDetect plugin.
|
|
*
|
|
* <refsect2>
|
|
* <title>Example launch line</title>
|
|
* |[
|
|
* gst-launch videotestsrc ! simplevideomark ! ximagesink
|
|
* ]| Add the default black/white squares at the bottom left of the video frames.
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/gst.h>
|
|
#include <gst/video/video.h>
|
|
#include <gst/video/gstvideofilter.h>
|
|
#include "gstsimplevideomark.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_video_mark_debug_category);
|
|
#define GST_CAT_DEFAULT gst_video_mark_debug_category
|
|
|
|
/* prototypes */
|
|
|
|
|
|
static void gst_video_mark_set_property (GObject * object,
|
|
guint property_id, const GValue * value, GParamSpec * pspec);
|
|
static void gst_video_mark_get_property (GObject * object,
|
|
guint property_id, GValue * value, GParamSpec * pspec);
|
|
static void gst_video_mark_dispose (GObject * object);
|
|
static void gst_video_mark_finalize (GObject * object);
|
|
|
|
static gboolean gst_video_mark_start (GstBaseTransform * trans);
|
|
static gboolean gst_video_mark_stop (GstBaseTransform * trans);
|
|
static gboolean gst_video_mark_set_info (GstVideoFilter * filter,
|
|
GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
|
|
GstVideoInfo * out_info);
|
|
static GstFlowReturn gst_video_mark_transform_frame_ip (GstVideoFilter * filter,
|
|
GstVideoFrame * frame);
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_PATTERN_WIDTH,
|
|
PROP_PATTERN_HEIGHT,
|
|
PROP_PATTERN_COUNT,
|
|
PROP_PATTERN_DATA_COUNT,
|
|
PROP_PATTERN_DATA,
|
|
PROP_ENABLED,
|
|
PROP_LEFT_OFFSET,
|
|
PROP_BOTTOM_OFFSET
|
|
};
|
|
|
|
#define DEFAULT_PATTERN_WIDTH 4
|
|
#define DEFAULT_PATTERN_HEIGHT 16
|
|
#define DEFAULT_PATTERN_COUNT 4
|
|
#define DEFAULT_PATTERN_DATA_COUNT 5
|
|
#define DEFAULT_PATTERN_DATA 10
|
|
#define DEFAULT_ENABLED TRUE
|
|
#define DEFAULT_LEFT_OFFSET 0
|
|
#define DEFAULT_BOTTOM_OFFSET 0
|
|
|
|
/* pad templates */
|
|
|
|
#define VIDEO_CAPS \
|
|
GST_VIDEO_CAPS_MAKE( \
|
|
"{ I420, YV12, Y41B, Y42B, Y444, YUY2, UYVY, AYUV, YVYU }")
|
|
|
|
|
|
/* class initialization */
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstSimpleVideoMark, gst_video_mark,
|
|
GST_TYPE_VIDEO_FILTER,
|
|
GST_DEBUG_CATEGORY_INIT (gst_video_mark_debug_category, "simplevideomark",
|
|
0, "debug category for simplevideomark element"));
|
|
|
|
static void
|
|
gst_video_mark_class_init (GstSimpleVideoMarkClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GstBaseTransformClass *base_transform_class =
|
|
GST_BASE_TRANSFORM_CLASS (klass);
|
|
GstVideoFilterClass *video_filter_class = GST_VIDEO_FILTER_CLASS (klass);
|
|
|
|
gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
|
|
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
|
|
gst_caps_from_string (VIDEO_CAPS)));
|
|
gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
|
|
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
|
gst_caps_from_string (VIDEO_CAPS)));
|
|
|
|
gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
|
|
"Video marker", "Filter/Effect/Video",
|
|
"Marks a video signal with a pattern", "Wim Taymans <wim@fluendo.com>");
|
|
|
|
gobject_class->set_property = gst_video_mark_set_property;
|
|
gobject_class->get_property = gst_video_mark_get_property;
|
|
gobject_class->dispose = gst_video_mark_dispose;
|
|
gobject_class->finalize = gst_video_mark_finalize;
|
|
base_transform_class->start = GST_DEBUG_FUNCPTR (gst_video_mark_start);
|
|
base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_video_mark_stop);
|
|
video_filter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_mark_set_info);
|
|
video_filter_class->transform_frame_ip =
|
|
GST_DEBUG_FUNCPTR (gst_video_mark_transform_frame_ip);
|
|
|
|
g_object_class_install_property (gobject_class, PROP_PATTERN_WIDTH,
|
|
g_param_spec_int ("pattern-width", "Pattern width",
|
|
"The width of the pattern markers", 1, G_MAXINT,
|
|
DEFAULT_PATTERN_WIDTH,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_PATTERN_HEIGHT,
|
|
g_param_spec_int ("pattern-height", "Pattern height",
|
|
"The height of the pattern markers", 1, G_MAXINT,
|
|
DEFAULT_PATTERN_HEIGHT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_PATTERN_COUNT,
|
|
g_param_spec_int ("pattern-count", "Pattern count",
|
|
"The number of pattern markers", 0, G_MAXINT,
|
|
DEFAULT_PATTERN_COUNT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_PATTERN_DATA_COUNT,
|
|
g_param_spec_int ("pattern-data-count", "Pattern data count",
|
|
"The number of extra data pattern markers", 0, 64,
|
|
DEFAULT_PATTERN_DATA_COUNT,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_PATTERN_DATA,
|
|
g_param_spec_uint64 ("pattern-data", "Pattern data",
|
|
"The extra data pattern markers", 0, G_MAXUINT64,
|
|
DEFAULT_PATTERN_DATA,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_ENABLED,
|
|
g_param_spec_boolean ("enabled", "Enabled",
|
|
"Enable or disable the filter",
|
|
DEFAULT_ENABLED,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_LEFT_OFFSET,
|
|
g_param_spec_int ("left-offset", "Left Offset",
|
|
"The offset from the left border where the pattern starts", 0,
|
|
G_MAXINT, DEFAULT_LEFT_OFFSET,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_BOTTOM_OFFSET,
|
|
g_param_spec_int ("bottom-offset", "Bottom Offset",
|
|
"The offset from the bottom border where the pattern starts", 0,
|
|
G_MAXINT, DEFAULT_BOTTOM_OFFSET,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
|
|
|
}
|
|
|
|
static void
|
|
gst_video_mark_init (GstSimpleVideoMark * simplevideomark)
|
|
{
|
|
}
|
|
|
|
void
|
|
gst_video_mark_set_property (GObject * object, guint property_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "set_property");
|
|
|
|
switch (property_id) {
|
|
case PROP_PATTERN_WIDTH:
|
|
simplevideomark->pattern_width = g_value_get_int (value);
|
|
break;
|
|
case PROP_PATTERN_HEIGHT:
|
|
simplevideomark->pattern_height = g_value_get_int (value);
|
|
break;
|
|
case PROP_PATTERN_COUNT:
|
|
simplevideomark->pattern_count = g_value_get_int (value);
|
|
break;
|
|
case PROP_PATTERN_DATA_COUNT:
|
|
simplevideomark->pattern_data_count = g_value_get_int (value);
|
|
break;
|
|
case PROP_PATTERN_DATA:
|
|
simplevideomark->pattern_data = g_value_get_uint64 (value);
|
|
break;
|
|
case PROP_ENABLED:
|
|
simplevideomark->enabled = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_LEFT_OFFSET:
|
|
simplevideomark->left_offset = g_value_get_int (value);
|
|
break;
|
|
case PROP_BOTTOM_OFFSET:
|
|
simplevideomark->bottom_offset = g_value_get_int (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_video_mark_get_property (GObject * object, guint property_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "get_property");
|
|
|
|
switch (property_id) {
|
|
case PROP_PATTERN_WIDTH:
|
|
g_value_set_int (value, simplevideomark->pattern_width);
|
|
break;
|
|
case PROP_PATTERN_HEIGHT:
|
|
g_value_set_int (value, simplevideomark->pattern_height);
|
|
break;
|
|
case PROP_PATTERN_COUNT:
|
|
g_value_set_int (value, simplevideomark->pattern_count);
|
|
break;
|
|
case PROP_PATTERN_DATA_COUNT:
|
|
g_value_set_int (value, simplevideomark->pattern_data_count);
|
|
break;
|
|
case PROP_PATTERN_DATA:
|
|
g_value_set_uint64 (value, simplevideomark->pattern_data);
|
|
break;
|
|
case PROP_ENABLED:
|
|
g_value_set_boolean (value, simplevideomark->enabled);
|
|
break;
|
|
case PROP_LEFT_OFFSET:
|
|
g_value_set_int (value, simplevideomark->left_offset);
|
|
break;
|
|
case PROP_BOTTOM_OFFSET:
|
|
g_value_set_int (value, simplevideomark->bottom_offset);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
gst_video_mark_dispose (GObject * object)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "dispose");
|
|
|
|
/* clean up as possible. may be called multiple times */
|
|
|
|
G_OBJECT_CLASS (gst_video_mark_parent_class)->dispose (object);
|
|
}
|
|
|
|
void
|
|
gst_video_mark_finalize (GObject * object)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "finalize");
|
|
|
|
/* clean up object here */
|
|
|
|
G_OBJECT_CLASS (gst_video_mark_parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
gst_video_mark_start (GstBaseTransform * trans)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (trans);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "start");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_video_mark_stop (GstBaseTransform * trans)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (trans);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "stop");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_video_mark_set_info (GstVideoFilter * filter, GstCaps * incaps,
|
|
GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (filter);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "set_info");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_video_mark_draw_box (GstSimpleVideoMark * simplevideomark, guint8 * data,
|
|
gint width, gint height, gint row_stride, gint pixel_stride, guint8 color)
|
|
{
|
|
gint i, j;
|
|
|
|
for (i = 0; i < height; i++) {
|
|
for (j = 0; j < width; j++) {
|
|
data[pixel_stride * j] = color;
|
|
}
|
|
data += row_stride;
|
|
}
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_video_mark_yuv (GstSimpleVideoMark * simplevideomark, GstVideoFrame * frame)
|
|
{
|
|
gint i, pw, ph, row_stride, pixel_stride;
|
|
gint width, height, req_width, req_height;
|
|
guint8 *d;
|
|
guint64 pattern_shift;
|
|
guint8 color;
|
|
|
|
width = frame->info.width;
|
|
height = frame->info.height;
|
|
|
|
pw = simplevideomark->pattern_width;
|
|
ph = simplevideomark->pattern_height;
|
|
row_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
|
|
pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
|
|
|
|
req_width =
|
|
(simplevideomark->pattern_count +
|
|
simplevideomark->pattern_data_count) * pw + simplevideomark->left_offset;
|
|
req_height = simplevideomark->bottom_offset + ph;
|
|
if (req_width > width || req_height > height) {
|
|
GST_ELEMENT_ERROR (simplevideomark, STREAM, WRONG_TYPE, (NULL),
|
|
("simplevideomark pattern doesn't fit video, need at least %ix%i (stream has %ix%i)",
|
|
req_width, req_height, width, height));
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
d = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
|
|
/* move to start of bottom left */
|
|
d += row_stride * (height - ph - simplevideomark->bottom_offset) +
|
|
pixel_stride * simplevideomark->left_offset;
|
|
|
|
/* draw the bottom left pixels */
|
|
for (i = 0; i < simplevideomark->pattern_count; i++) {
|
|
if (i & 1)
|
|
/* odd pixels must be white */
|
|
color = 255;
|
|
else
|
|
color = 0;
|
|
|
|
/* draw box of width * height */
|
|
gst_video_mark_draw_box (simplevideomark, d, pw, ph, row_stride,
|
|
pixel_stride, color);
|
|
|
|
/* move to i-th pattern */
|
|
d += pixel_stride * pw;
|
|
}
|
|
|
|
pattern_shift =
|
|
G_GUINT64_CONSTANT (1) << (simplevideomark->pattern_data_count - 1);
|
|
|
|
/* get the data of the pattern */
|
|
for (i = 0; i < simplevideomark->pattern_data_count; i++) {
|
|
if (simplevideomark->pattern_data & pattern_shift)
|
|
color = 255;
|
|
else
|
|
color = 0;
|
|
|
|
gst_video_mark_draw_box (simplevideomark, d, pw, ph, row_stride,
|
|
pixel_stride, color);
|
|
|
|
pattern_shift >>= 1;
|
|
|
|
/* move to i-th pattern data */
|
|
d += pixel_stride * pw;
|
|
}
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
|
|
static GstFlowReturn
|
|
gst_video_mark_transform_frame_ip (GstVideoFilter * filter,
|
|
GstVideoFrame * frame)
|
|
{
|
|
GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (filter);
|
|
|
|
GST_DEBUG_OBJECT (simplevideomark, "transform_frame_ip");
|
|
|
|
if (simplevideomark->enabled)
|
|
return gst_video_mark_yuv (simplevideomark, frame);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|