diff --git a/ChangeLog b/ChangeLog index b1cebbd6e8..ed2986ffe1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2007-05-30 Wim Taymans + + * configure.ac: + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-bad-plugins-docs.sgml: + * docs/plugins/gst-plugins-bad-plugins-sections.txt: + * docs/plugins/inspect/plugin-videosignal.xml: + * gst/videosignal/Makefile.am: + * gst/videosignal/gstvideoanalyse.c: (gst_video_analyse_set_caps), + (gst_video_analyse_post_message), (gst_video_analyse_420), + (gst_video_analyse_transform_ip), (gst_video_analyse_set_property), + (gst_video_analyse_get_property), (gst_video_analyse_base_init), + (gst_video_analyse_class_init), (gst_video_analyse_init), + (gst_video_analyse_get_type): + * gst/videosignal/gstvideoanalyse.h: + * gst/videosignal/gstvideodetect.c: (gst_video_detect_set_caps), + (gst_video_detect_post_message), + (gst_video_detect_calc_brightness), (gst_video_detect_420), + (gst_video_detect_transform_ip), (gst_video_detect_set_property), + (gst_video_detect_get_property), (gst_video_detect_base_init), + (gst_video_detect_class_init), (gst_video_detect_init), + (gst_video_detect_get_type): + * gst/videosignal/gstvideodetect.h: + * gst/videosignal/gstvideosignal.c: (plugin_init): + * gst/videosignal/gstvideosignal.h: + Added videosignal plugin with two plugins to analyse video frames. + Added videoanalyse to report about brightness and variance in video + frames. + Added videodetect to detect predefined patterns in a video signal. + 2007-05-30 Tim-Philipp Müller * docs/plugins/gst-plugins-bad-plugins.hierarchy: diff --git a/configure.ac b/configure.ac index eaa379b44a..ad4def80a8 100644 --- a/configure.ac +++ b/configure.ac @@ -105,6 +105,7 @@ GST_PLUGINS_ALL="\ tta \ videocrop \ videoparse \ + videosignal \ vmnc \ xingheader \ y4m \ @@ -1128,6 +1129,7 @@ gst/qtdemux/Makefile gst/tta/Makefile gst/videocrop/Makefile gst/videoparse/Makefile +gst/videosignal/Makefile gst/vmnc/Makefile gst/xingheader/Makefile gst/real/Makefile diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 80c71c1b4e..380ef7729a 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -111,7 +111,9 @@ EXTRA_HFILES = \ $(top_srcdir)/gst/rtpmanager/gstrtpptdemux.h \ $(top_srcdir)/gst/rtpmanager/gstrtpsession.h \ $(top_srcdir)/gst/rtpmanager/gstrtpssrcdemux.h \ - $(top_srcdir)/gst/videocrop/gstvideocrop.h + $(top_srcdir)/gst/videocrop/gstvideocrop.h \ + $(top_srcdir)/gst/videosignal/gstvideoanalyse.h \ + $(top_srcdir)/gst/videosignal/gstvideodetect.h # Images to copy into HTML directory. HTML_IMAGES = diff --git a/docs/plugins/gst-plugins-bad-plugins-docs.sgml b/docs/plugins/gst-plugins-bad-plugins-docs.sgml index 92bdc83f4b..9bc19312d7 100644 --- a/docs/plugins/gst-plugins-bad-plugins-docs.sgml +++ b/docs/plugins/gst-plugins-bad-plugins-docs.sgml @@ -31,7 +31,9 @@ - + + + @@ -64,6 +66,7 @@ + diff --git a/docs/plugins/gst-plugins-bad-plugins-sections.txt b/docs/plugins/gst-plugins-bad-plugins-sections.txt index 0a4a25e7c1..5a14817068 100644 --- a/docs/plugins/gst-plugins-bad-plugins-sections.txt +++ b/docs/plugins/gst-plugins-bad-plugins-sections.txt @@ -201,6 +201,35 @@ GstVideoCrop GstVideoCropClass +
+element-videoanalyse +videoanalyse +GstVideoAnalyse + +GstVideoAnalyseClass +GST_VIDEO_ANALYSE +GST_IS_VIDEO_ANALYSE +GST_TYPE_VIDEO_ANALYSE +gst_video_analyse_get_type +GST_VIDEO_ANALYSE_CLASS +GST_IS_VIDEO_ANALYSE_CLASS +
+ +
+element-videodetect +videodetect +GstVideoDetect + +GstVideoDetectClass +GST_VIDEO_DETECT +GST_IS_VIDEO_DETECT +GST_TYPE_VIDEO_DETECT +gst_video_detect_get_type +GST_VIDEO_DETECT_CLASS +GST_IS_VIDEO_DETECT_CLASS +
+ +
element-waveformsink GstWaveFormSink diff --git a/docs/plugins/inspect/plugin-videosignal.xml b/docs/plugins/inspect/plugin-videosignal.xml new file mode 100644 index 0000000000..f06805c31e --- /dev/null +++ b/docs/plugins/inspect/plugin-videosignal.xml @@ -0,0 +1,27 @@ + + videosignal + Various video signal analysers + ../../gst/videosignal/.libs/libgstvideosignal.so + libgstvideosignal.so + 0.10.4.1 + LGPL + gst-plugins-bad + GStreamer Bad Plug-ins CVS/prerelease + Unknown package origin + + + videoanalyse + Video analyser + Filter/Effect/Video + Analyse video signal + Wim Taymans <wim@fluendo.com> + + + videodetect + Video detecter + Filter/Effect/Video + Detect patterns in a video signal + Wim Taymans <wim@fluendo.com> + + + \ No newline at end of file diff --git a/gst/videosignal/Makefile.am b/gst/videosignal/Makefile.am new file mode 100644 index 0000000000..1877c4c026 --- /dev/null +++ b/gst/videosignal/Makefile.am @@ -0,0 +1,12 @@ +plugin_LTLIBRARIES = libgstvideosignal.la + +noinst_HEADERS = gstvideoanalyse.h gstvideodetect.h + +libgstvideosignal_la_SOURCES = gstvideosignal.c \ + gstvideoanalyse.c \ + gstvideodetect.c + +libgstvideosignal_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) +libgstvideosignal_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ $(GST_BASE_LIBS) $(GST_LIBS) +libgstvideosignal_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/gst/videosignal/gstvideoanalyse.c b/gst/videosignal/gstvideoanalyse.c new file mode 100644 index 0000000000..d0cdf70c61 --- /dev/null +++ b/gst/videosignal/gstvideoanalyse.c @@ -0,0 +1,353 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-videoanalyse + * @short_description: Analyse and report about the video frame + * + * + * + * This plugin analyses every video frame and if the message property is #TRUE, posts an element + * message with video statistics called "GstVideoAnalyse". + * The message's structure contains these fields: + * + * + * + * #GstClockTime + * "timestamp": + * the timestamp of the buffer that triggered the message. + * + * + * + * + * #GstClockTime + * "stream-time": + * the stream time of the buffer. + * + * + * + * + * #GstClockTime + * "running-time": + * the running_time of the buffer. + * + * + * + * + * #GstClockTime + * "duration": + * the duration of the buffer. + * + * + * + * + * #gdouble + * "brightness": + * the average brightness of the frame. + * + * + * + * + * #gdouble + * "brightness-variance": + * the brightness variance of the frame. + * + * + * + * + * Example launch line + * + * + * gst-launch -m videotestsrc ! videoanalyse ! ffmpegcolorspace ! ximagesink + * + * This pipeline emits messages to the console for each frame that has been analysed. + * + * + * + * Last reviewed on 2007-05-30 (0.10.5) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstvideoanalyse.h" + +#include +#include + +#include + +/* GstVideoAnalyse signals and args */ + +#define DEFAULT_MESSAGE TRUE + +enum +{ + PROP_0, + PROP_MESSAGE +}; + +GST_DEBUG_CATEGORY_STATIC (video_analyse_debug); +#define GST_CAT_DEFAULT video_analyse_debug + +static const GstElementDetails video_analyse_details = +GST_ELEMENT_DETAILS ("Video analyser", + "Filter/Effect/Video", + "Analyse video signal", + "Wim Taymans "); + +static GstStaticPadTemplate gst_video_analyse_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, YV12 }")) + ); + +static GstStaticPadTemplate gst_video_analyse_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, YV12 }")) + ); + +static GstVideoFilterClass *parent_class = NULL; + +static gboolean +gst_video_analyse_set_caps (GstBaseTransform * btrans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstVideoAnalyse *vf; + GstStructure *in_s; + gboolean ret; + + vf = GST_VIDEO_ANALYSE (btrans); + + in_s = gst_caps_get_structure (incaps, 0); + + ret = gst_structure_get_int (in_s, "width", &vf->width); + ret &= gst_structure_get_int (in_s, "height", &vf->height); + + return ret; +} + +/* Useful macros */ +#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width)) +#define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2) +#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2) + +#define GST_VIDEO_I420_Y_OFFSET(w,h) (0) +#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h))) +#define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2)) + +#define GST_VIDEO_I420_SIZE(w,h) (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2)) + +static void +gst_video_analyse_post_message (GstVideoAnalyse * videoanalyse, + GstBuffer * buffer) +{ + GstBaseTransform *trans; + GstMessage *m; + guint64 duration, timestamp, running_time, stream_time; + + trans = GST_BASE_TRANSFORM_CAST (videoanalyse); + + /* get timestamps */ + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, + timestamp); + stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, + timestamp); + + m = gst_message_new_element (GST_OBJECT_CAST (videoanalyse), + gst_structure_new ("GstVideoAnalyse", + "timestamp", G_TYPE_UINT64, timestamp, + "stream-time", G_TYPE_UINT64, stream_time, + "running-time", G_TYPE_UINT64, running_time, + "duration", G_TYPE_UINT64, duration, + "brightness", G_TYPE_DOUBLE, videoanalyse->brightness, + "brightness-variance", G_TYPE_DOUBLE, videoanalyse->brightness_var, + NULL)); + + gst_element_post_message (GST_ELEMENT_CAST (videoanalyse), m); +} + +static void +gst_video_analyse_420 (GstVideoAnalyse * videoanalyse, guint8 * data, + gint width, gint height) +{ + guint64 sum; + gint avg, diff; + gint i, j; + guint8 *d; + + d = data; + sum = 0; + /* do brightness as average of pixel brightness in 0.0 to 1.0 */ + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + sum += data[j]; + } + d += GST_VIDEO_I420_Y_ROWSTRIDE (width); + } + avg = sum / (width * height); + videoanalyse->brightness = sum / (255.0 * width * height); + + d = data; + sum = 0; + /* do variance */ + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + diff = (avg - d[j]); + sum += diff * diff; + } + d += GST_VIDEO_I420_Y_ROWSTRIDE (width); + } + videoanalyse->brightness_var = sum / (255.0 * 255.0 * width * height); +} + +static GstFlowReturn +gst_video_analyse_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstVideoAnalyse *videoanalyse; + GstFlowReturn ret = GST_FLOW_OK; + guint8 *data; + + videoanalyse = GST_VIDEO_ANALYSE (trans); + + data = GST_BUFFER_DATA (buf); + + gst_video_analyse_420 (videoanalyse, data, videoanalyse->width, + videoanalyse->height); + + if (videoanalyse->message) + gst_video_analyse_post_message (videoanalyse, buf); + + return ret; +} + +static void +gst_video_analyse_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVideoAnalyse *videoanalyse; + + videoanalyse = GST_VIDEO_ANALYSE (object); + + switch (prop_id) { + case PROP_MESSAGE: + videoanalyse->message = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_analyse_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstVideoAnalyse *videoanalyse; + + videoanalyse = GST_VIDEO_ANALYSE (object); + + switch (prop_id) { + case PROP_MESSAGE: + g_value_set_boolean (value, videoanalyse->message); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_analyse_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &video_analyse_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_analyse_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_analyse_src_template)); +} + +static void +gst_video_analyse_class_init (gpointer klass, gpointer class_data) +{ + GObjectClass *gobject_class; + GstBaseTransformClass *trans_class; + + gobject_class = (GObjectClass *) klass; + trans_class = (GstBaseTransformClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_video_analyse_set_property; + gobject_class->get_property = gst_video_analyse_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MESSAGE, + g_param_spec_boolean ("message", "Message", + "Post statics messages", + DEFAULT_MESSAGE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_analyse_set_caps); + trans_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_video_analyse_transform_ip); + trans_class->passthrough_on_same_caps = TRUE; +} + +static void +gst_video_analyse_init (GTypeInstance * instance, gpointer g_class) +{ + GstVideoAnalyse *videoanalyse; + + videoanalyse = GST_VIDEO_ANALYSE (instance); + + GST_DEBUG_OBJECT (videoanalyse, "gst_video_analyse_init"); +} + +GType +gst_video_analyse_get_type (void) +{ + static GType video_analyse_type = 0; + + if (!video_analyse_type) { + static const GTypeInfo video_analyse_info = { + sizeof (GstVideoAnalyseClass), + gst_video_analyse_base_init, + NULL, + gst_video_analyse_class_init, + NULL, + NULL, + sizeof (GstVideoAnalyse), + 0, + gst_video_analyse_init, + }; + + video_analyse_type = g_type_register_static (GST_TYPE_VIDEO_FILTER, + "GstVideoAnalyse", &video_analyse_info, 0); + } + return video_analyse_type; +} diff --git a/gst/videosignal/gstvideoanalyse.h b/gst/videosignal/gstvideoanalyse.h new file mode 100644 index 0000000000..221f63ba93 --- /dev/null +++ b/gst/videosignal/gstvideoanalyse.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_ANALYSE_H__ +#define __GST_VIDEO_ANALYSE_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_ANALYSE \ + (gst_video_analyse_get_type()) +#define GST_VIDEO_ANALYSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_ANALYSE,GstVideoAnalyse)) +#define GST_VIDEO_ANALYSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_ANALYSE,GstVideoAnalyseClass)) +#define GST_IS_VIDEO_ANALYSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_ANALYSE)) +#define GST_IS_VIDEO_ANALYSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_ANALYSE)) + +typedef struct _GstVideoAnalyse GstVideoAnalyse; +typedef struct _GstVideoAnalyseClass GstVideoAnalyseClass; + +/** + * GstVideoAnalyse: + * + * Opaque datastructure. + */ +struct _GstVideoAnalyse { + GstVideoFilter videofilter; + + gint width, height; + + gboolean message; + + gdouble brightness; + gdouble brightness_var; + + guint64 interval; +}; + +struct _GstVideoAnalyseClass { + GstVideoFilterClass parent_class; +}; + +GType gst_video_analyse_get_type (void); + +G_END_DECLS + +#endif /* __GST_VIDEO_ANALYSE_H__ */ diff --git a/gst/videosignal/gstvideodetect.c b/gst/videosignal/gstvideodetect.c new file mode 100644 index 0000000000..54d0dd77ad --- /dev/null +++ b/gst/videosignal/gstvideodetect.c @@ -0,0 +1,493 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-videodetect + * @short_description: Detect a pattern in a video signal + * + * + * + * This plugin detects ::pattern-count squares in the bottom left corner of + * the video frames. The squares have a width and height of respectively + * ::pattern-width and ::patern-height. Even squares must be black and odd + * squares must be white. + * + * + * When the pattern has been found, ::pattern-data-count squares after the + * pattern squares are read as a bitarray. White squares represent a 1 bit and + * black squares a 0 bit. The bitarray will will included in the element message + * that is posted (see below). + * + * + * After the pattern has been found and the data pattern has been read, an + * element message called "GstVideoDetect" will + * be posted on the bus. If the pattern is no longer found in the frame, the + * same element message is posted with the have-pattern field set to #FALSE. + * The message is only posted if the message + * property is #TRUE. + * + * + * The message's structure contains these fields: + * + * + * + * #gboolean + * "have-pattern": + * if the pattern was found. This field will be set to #TRUE for as long as + * the pattern was found in the frame and set to FALSE for the first frame + * that does not contain the pattern anymore. + * + * + * + * + * #GstClockTime + * "timestamp": + * the timestamp of the buffer that triggered the message. + * + * + * + * + * #GstClockTime + * "stream-time": + * the stream time of the buffer. + * + * + * + * + * #GstClockTime + * "running-time": + * the running_time of the buffer. + * + * + * + * + * #GstClockTime + * "duration": + * the duration of the buffer. + * + * + * + * + * #guint + * "data": + * the data-pattern found after the pattern or 0 when have-signal is #FALSE. + * + * + * + * + * Example launch line + * + * + * gst-launch videotestsrc ! videodetect ! ffmpegcolorspace ! ximagesink + * + * + * + * + * Last reviewed on 2007-05-30 (0.10.5) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstvideodetect.h" + +#include +#include + +#include + +/* GstVideoDetect signals and args */ + +#define DEFAULT_MESSAGE TRUE +#define DEFAULT_PATTERN_WIDTH 4 +#define DEFAULT_PATTERN_HEIGHT 16 +#define DEFAULT_PATTERN_COUNT 4 +#define DEFAULT_PATTERN_DATA_COUNT 5 +#define DEFAULT_PATTERN_SENSITIVITY 0.2 + +enum +{ + PROP_0, + PROP_MESSAGE, + PROP_PATTERN_WIDTH, + PROP_PATTERN_HEIGHT, + PROP_PATTERN_COUNT, + PROP_PATTERN_DATA_COUNT, + PROP_PATTERN_SENSITIVITY +}; + +GST_DEBUG_CATEGORY_STATIC (video_detect_debug); +#define GST_CAT_DEFAULT video_detect_debug + +static const GstElementDetails video_detect_details = +GST_ELEMENT_DETAILS ("Video detecter", + "Filter/Effect/Video", + "Detect patterns in a video signal", + "Wim Taymans "); + +static GstStaticPadTemplate gst_video_detect_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, YV12 }")) + ); + +static GstStaticPadTemplate gst_video_detect_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, YV12 }")) + ); + +static GstVideoFilterClass *parent_class = NULL; + +static gboolean +gst_video_detect_set_caps (GstBaseTransform * btrans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstVideoDetect *vf; + GstStructure *in_s; + gboolean ret; + + vf = GST_VIDEO_DETECT (btrans); + + in_s = gst_caps_get_structure (incaps, 0); + + ret = gst_structure_get_int (in_s, "width", &vf->width); + ret &= gst_structure_get_int (in_s, "height", &vf->height); + ret &= gst_structure_get_fourcc (in_s, "format", &vf->format); + + return ret; +} + +/* Useful macros */ +#define GST_VIDEO_I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width)) +#define GST_VIDEO_I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2) +#define GST_VIDEO_I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2) + +#define GST_VIDEO_I420_Y_OFFSET(w,h) (0) +#define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h))) +#define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2)) + +#define GST_VIDEO_I420_SIZE(w,h) (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2)) + +static void +gst_video_detect_post_message (GstVideoDetect * videodetect, GstBuffer * buffer, + guint data) +{ + GstBaseTransform *trans; + GstMessage *m; + guint64 duration, timestamp, running_time, stream_time; + + trans = GST_BASE_TRANSFORM_CAST (videodetect); + + /* get timestamps */ + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, + timestamp); + stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, + timestamp); + + /* post message */ + m = gst_message_new_element (GST_OBJECT_CAST (videodetect), + gst_structure_new ("GstVideoDetect", + "have-pattern", G_TYPE_BOOLEAN, videodetect->in_pattern, + "timestamp", G_TYPE_UINT64, timestamp, + "stream-time", G_TYPE_UINT64, stream_time, + "running-time", G_TYPE_UINT64, running_time, + "duration", G_TYPE_UINT64, duration, + "data", G_TYPE_UINT, data, NULL)); + gst_element_post_message (GST_ELEMENT_CAST (videodetect), m); +} + +static gdouble +gst_video_detect_calc_brightness (GstVideoDetect * videodetect, guint8 * data, + gint width, gint height, gint stride) +{ + gint i, j; + guint64 sum; + + sum = 0; + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + sum += data[j]; + } + /* move to next line */ + data += stride; + } + return sum / (255.0 * width * height); +} + +static void +gst_video_detect_420 (GstVideoDetect * videodetect, GstBuffer * buffer) +{ + gdouble brightness; + gint i, pw, ph, stride, width, height; + guint8 *d, *data; + guint pattern_data; + + data = GST_BUFFER_DATA (buffer); + + width = videodetect->width; + height = videodetect->height; + + pw = videodetect->pattern_width; + ph = videodetect->pattern_height; + stride = GST_VIDEO_I420_Y_ROWSTRIDE (width); + + /* analyse the bottom left pixels */ + for (i = 0; i < videodetect->pattern_count; i++) { + d = data; + /* move to start of bottom left */ + d += stride * (height - ph); + /* move to i-th pattern */ + d += pw * i; + + /* calc brightness of width * height box */ + brightness = + gst_video_detect_calc_brightness (videodetect, d, pw, ph, stride); + + GST_DEBUG_OBJECT (videodetect, "brightness %f", brightness); + + if (i & 1) { + /* odd pixels must be white */ + if (brightness < (1.0 - videodetect->pattern_sensitivity)) + goto no_pattern; + } else { + /* even pixels must be black */ + if (brightness > videodetect->pattern_sensitivity) + goto no_pattern; + } + } + GST_DEBUG_OBJECT (videodetect, "found pattern"); + + pattern_data = 0; + + /* get the data of the pattern */ + for (i = 0; i < videodetect->pattern_data_count; i++) { + d = data; + /* move to start of bottom left, after the pattern */ + d += stride * (height - ph) + (videodetect->pattern_count * pw); + /* move to i-th pattern data */ + d += pw * i; + + /* calc brightness of width * height box */ + brightness = + gst_video_detect_calc_brightness (videodetect, d, pw, ph, stride); + /* update pattern */ + pattern_data <<= 1; + if (brightness > 0.5) + pattern_data |= 1; + } + + GST_DEBUG_OBJECT (videodetect, "have data %u", pattern_data); + + videodetect->in_pattern = TRUE; + gst_video_detect_post_message (videodetect, buffer, pattern_data); + + return; + +no_pattern: + { + GST_DEBUG_OBJECT (videodetect, "no pattern found"); + if (videodetect->in_pattern) { + videodetect->in_pattern = FALSE; + gst_video_detect_post_message (videodetect, buffer, 0); + } + return; + } +} + +static GstFlowReturn +gst_video_detect_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstVideoDetect *videodetect; + GstFlowReturn ret = GST_FLOW_OK; + + videodetect = GST_VIDEO_DETECT (trans); + + gst_video_detect_420 (videodetect, buf); + + return ret; +} + +static void +gst_video_detect_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVideoDetect *videodetect; + + videodetect = GST_VIDEO_DETECT (object); + + switch (prop_id) { + case PROP_MESSAGE: + videodetect->message = g_value_get_boolean (value); + break; + case PROP_PATTERN_WIDTH: + videodetect->pattern_width = g_value_get_int (value); + break; + case PROP_PATTERN_HEIGHT: + videodetect->pattern_height = g_value_get_int (value); + break; + case PROP_PATTERN_COUNT: + videodetect->pattern_count = g_value_get_int (value); + break; + case PROP_PATTERN_DATA_COUNT: + videodetect->pattern_data_count = g_value_get_int (value); + break; + case PROP_PATTERN_SENSITIVITY: + videodetect->pattern_sensitivity = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_detect_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstVideoDetect *videodetect; + + videodetect = GST_VIDEO_DETECT (object); + + switch (prop_id) { + case PROP_MESSAGE: + g_value_set_boolean (value, videodetect->message); + break; + case PROP_PATTERN_WIDTH: + g_value_set_int (value, videodetect->pattern_width); + break; + case PROP_PATTERN_HEIGHT: + g_value_set_int (value, videodetect->pattern_height); + break; + case PROP_PATTERN_COUNT: + g_value_set_int (value, videodetect->pattern_count); + break; + case PROP_PATTERN_DATA_COUNT: + g_value_set_int (value, videodetect->pattern_data_count); + break; + case PROP_PATTERN_SENSITIVITY: + g_value_set_double (value, videodetect->pattern_sensitivity); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_detect_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &video_detect_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_detect_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_detect_src_template)); +} + +static void +gst_video_detect_class_init (gpointer klass, gpointer class_data) +{ + GObjectClass *gobject_class; + GstBaseTransformClass *trans_class; + + gobject_class = (GObjectClass *) klass; + trans_class = (GstBaseTransformClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->set_property = gst_video_detect_set_property; + gobject_class->get_property = gst_video_detect_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MESSAGE, + g_param_spec_boolean ("message", "Message", + "Post statics messages", + DEFAULT_MESSAGE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + 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_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_object_class_install_property (gobject_class, PROP_PATTERN_COUNT, + g_param_spec_int ("pattern-count", "Pattern count", + "The number of pattern markers", 1, G_MAXINT, + DEFAULT_PATTERN_COUNT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + 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, G_MAXINT, + DEFAULT_PATTERN_DATA_COUNT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_PATTERN_SENSITIVITY, + g_param_spec_double ("pattern-sensitivity", "Pattern sensitivity", + "The sensitivity for detecting the markers (0.0 = highest, 0.5 lowest)", + 0.0, 0.5, DEFAULT_PATTERN_SENSITIVITY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_detect_set_caps); + trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_detect_transform_ip); + trans_class->passthrough_on_same_caps = TRUE; + + GST_DEBUG_CATEGORY_INIT (video_detect_debug, "videodetect", 0, + "Video detect"); +} + +static void +gst_video_detect_init (GTypeInstance * instance, gpointer g_class) +{ + GstVideoDetect *videodetect; + + videodetect = GST_VIDEO_DETECT (instance); + + GST_DEBUG_OBJECT (videodetect, "gst_video_detect_init"); + + videodetect->in_pattern = FALSE; +} + +GType +gst_video_detect_get_type (void) +{ + static GType video_detect_type = 0; + + if (!video_detect_type) { + static const GTypeInfo video_detect_info = { + sizeof (GstVideoDetectClass), + gst_video_detect_base_init, + NULL, + gst_video_detect_class_init, + NULL, + NULL, + sizeof (GstVideoDetect), + 0, + gst_video_detect_init, + }; + + video_detect_type = g_type_register_static (GST_TYPE_VIDEO_FILTER, + "GstVideoDetect", &video_detect_info, 0); + } + return video_detect_type; +} diff --git a/gst/videosignal/gstvideodetect.h b/gst/videosignal/gstvideodetect.h new file mode 100644 index 0000000000..8fa5c05fe8 --- /dev/null +++ b/gst/videosignal/gstvideodetect.h @@ -0,0 +1,70 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_DETECT_H__ +#define __GST_VIDEO_DETECT_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_DETECT \ + (gst_video_detect_get_type()) +#define GST_VIDEO_DETECT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_DETECT,GstVideoDetect)) +#define GST_VIDEO_DETECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_DETECT,GstVideoDetectClass)) +#define GST_IS_VIDEO_DETECT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_DETECT)) +#define GST_IS_VIDEO_DETECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_DETECT)) + +typedef struct _GstVideoDetect GstVideoDetect; +typedef struct _GstVideoDetectClass GstVideoDetectClass; + +/** + * GstVideoDetect: + * + * Opaque datastructure. + */ +struct _GstVideoDetect { + GstVideoFilter videofilter; + + gint width, height; + guint32 format; + + gboolean message; + gint pattern_width; + gint pattern_height; + gint pattern_count; + gint pattern_data_count; + gdouble pattern_sensitivity; + + gboolean in_pattern; +}; + +struct _GstVideoDetectClass { + GstVideoFilterClass parent_class; +}; + +GType gst_video_detect_get_type (void); + +G_END_DECLS + +#endif /* __GST_VIDEO_DETECT_H__ */ diff --git a/gst/videosignal/gstvideosignal.c b/gst/videosignal/gstvideosignal.c new file mode 100644 index 0000000000..55b81cc2cc --- /dev/null +++ b/gst/videosignal/gstvideosignal.c @@ -0,0 +1,45 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstvideoanalyse.h" +#include "gstvideodetect.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean res; + + res = gst_element_register (plugin, "videoanalyse", GST_RANK_NONE, + GST_TYPE_VIDEO_ANALYSE); + + res &= gst_element_register (plugin, "videodetect", GST_RANK_NONE, + GST_TYPE_VIDEO_DETECT); + + return res; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "videosignal", + "Various video signal analysers", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/videosignal/gstvideosignal.h b/gst/videosignal/gstvideosignal.h new file mode 100644 index 0000000000..ffbc913c5c --- /dev/null +++ b/gst/videosignal/gstvideosignal.h @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_SIGNAL_H__ +#define __GST_VIDEO_SIGNAL_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_SIGNAL \ + (gst_video_signal_get_type()) +#define GST_VIDEO_SIGNAL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_SIGNAL,GstVideoSignal)) +#define GST_VIDEO_SIGNAL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_SIGNAL,GstVideoSignalClass)) +#define GST_IS_VIDEO_SIGNAL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_SIGNAL)) +#define GST_IS_VIDEO_SIGNAL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_SIGNAL)) + +typedef struct _GstVideoSignal GstVideoSignal; +typedef struct _GstVideoSignalClass GstVideoSignalClass; + +/** + * GstVideoSignal: + * + * Opaque datastructure. + */ +struct _GstVideoSignal { + GstVideoFilter videofilter; + + gint width, height; + + gdouble brightness; + gdouble brightness_var; + + guint64 interval; +}; + +struct _GstVideoSignalClass { + GstVideoFilterClass parent_class; +}; + +GType gst_video_signal_get_type (void); + +G_END_DECLS + +#endif /* __GST_VIDEO_SIGNAL_H__ */