1 /* GStreamer
2 * Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
17 * Boston, MA 02110-1335, USA.
18 */
19 /**
20 * SECTION:element-videoanalyse
21 * @title: videoanalyse
22 *
23 * This plugin analyses every video frame and if the #GstVideoAnalyse:message
24 * property is %TRUE, posts an element message with video statistics called
25 * `GstVideoAnalyse`.
26 *
27 * The message's structure contains these fields:
28 *
29 * * #GstClockTime `timestamp`: the timestamp of the buffer that triggered the message.
30 *
31 * * #GstClockTime `stream-time`: the stream time of the buffer.
32 *
33 * * #GstClockTime `running-time`: the running_time of the buffer.
34 *
35 * * #GstClockTime`duration`:the duration of the buffer.
36 *
37 * * #gdouble`luma-average`: the average brightness of the frame. Range: 0.0-1.0
38 *
39 * * #gdouble`luma-variance`: the brightness variance of the frame.
40 *
41 * ## Example launch line
42 * |[
43 * gst-launch-1.0 -m videotestsrc ! videoanalyse ! videoconvert ! ximagesink
44 * ]| This pipeline emits messages to the console for each frame that has been analysed.
45 *
46 */
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51
52 #include <gst/gst.h>
53 #include <gst/video/video.h>
54 #include <gst/video/gstvideofilter.h>
55 #include "gstvideoanalyse.h"
56
57 GST_DEBUG_CATEGORY_STATIC (gst_video_analyse_debug_category);
58 #define GST_CAT_DEFAULT gst_video_analyse_debug_category
59
60 /* prototypes */
61
62
63 static void gst_video_analyse_set_property (GObject * object,
64 guint property_id, const GValue * value, GParamSpec * pspec);
65 static void gst_video_analyse_get_property (GObject * object,
66 guint property_id, GValue * value, GParamSpec * pspec);
67 static void gst_video_analyse_finalize (GObject * object);
68
69 static GstFlowReturn gst_video_analyse_transform_frame_ip (GstVideoFilter *
70 filter, GstVideoFrame * frame);
71
72 enum
73 {
74 PROP_0,
75 PROP_MESSAGE
76 };
77
78 #define DEFAULT_MESSAGE TRUE
79
80 #define VIDEO_CAPS \
81 GST_VIDEO_CAPS_MAKE("{ I420, YV12, Y444, Y42B, Y41B }")
82
83
84 /* class initialization */
85
86 G_DEFINE_TYPE_WITH_CODE (GstVideoAnalyse, gst_video_analyse,
87 GST_TYPE_VIDEO_FILTER,
88 GST_DEBUG_CATEGORY_INIT (gst_video_analyse_debug_category, "videoanalyse",
89 0, "debug category for videoanalyse element"));
90
91 static void
gst_video_analyse_class_init(GstVideoAnalyseClass * klass)92 gst_video_analyse_class_init (GstVideoAnalyseClass * klass)
93 {
94 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
95 GstVideoFilterClass *video_filter_class = GST_VIDEO_FILTER_CLASS (klass);
96
97 gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
98 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
99 gst_caps_from_string (VIDEO_CAPS)));
100 gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
101 gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
102 gst_caps_from_string (VIDEO_CAPS)));
103
104 gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
105 "Video analyser", "Filter/Analyzer/Video",
106 "Analyse video signal", "Wim Taymans <wim@fluendo.com>");
107
108 gobject_class->set_property = gst_video_analyse_set_property;
109 gobject_class->get_property = gst_video_analyse_get_property;
110 gobject_class->finalize = gst_video_analyse_finalize;
111 video_filter_class->transform_frame_ip =
112 GST_DEBUG_FUNCPTR (gst_video_analyse_transform_frame_ip);
113
114 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MESSAGE,
115 g_param_spec_boolean ("message", "Message",
116 "Post statics messages",
117 DEFAULT_MESSAGE,
118 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
119 //trans_class->passthrough_on_same_caps = TRUE;
120 }
121
122 static void
gst_video_analyse_init(GstVideoAnalyse * videoanalyse)123 gst_video_analyse_init (GstVideoAnalyse * videoanalyse)
124 {
125 }
126
127 void
gst_video_analyse_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)128 gst_video_analyse_set_property (GObject * object, guint property_id,
129 const GValue * value, GParamSpec * pspec)
130 {
131 GstVideoAnalyse *videoanalyse = GST_VIDEO_ANALYSE (object);
132
133 GST_DEBUG_OBJECT (videoanalyse, "set_property");
134
135 switch (property_id) {
136 case PROP_MESSAGE:
137 videoanalyse->message = g_value_get_boolean (value);
138 break;
139 default:
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
141 break;
142 }
143 }
144
145 void
gst_video_analyse_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)146 gst_video_analyse_get_property (GObject * object, guint property_id,
147 GValue * value, GParamSpec * pspec)
148 {
149 GstVideoAnalyse *videoanalyse = GST_VIDEO_ANALYSE (object);
150
151 GST_DEBUG_OBJECT (videoanalyse, "get_property");
152
153 switch (property_id) {
154 case PROP_MESSAGE:
155 g_value_set_boolean (value, videoanalyse->message);
156 break;
157 default:
158 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
159 break;
160 }
161 }
162
163 void
gst_video_analyse_finalize(GObject * object)164 gst_video_analyse_finalize (GObject * object)
165 {
166 GstVideoAnalyse *videoanalyse = GST_VIDEO_ANALYSE (object);
167
168 GST_DEBUG_OBJECT (videoanalyse, "finalize");
169
170 /* clean up object here */
171
172 G_OBJECT_CLASS (gst_video_analyse_parent_class)->finalize (object);
173 }
174
175 static void
gst_video_analyse_post_message(GstVideoAnalyse * videoanalyse,GstVideoFrame * frame)176 gst_video_analyse_post_message (GstVideoAnalyse * videoanalyse,
177 GstVideoFrame * frame)
178 {
179 GstBaseTransform *trans;
180 GstMessage *m;
181 guint64 duration, timestamp, running_time, stream_time;
182
183 trans = GST_BASE_TRANSFORM_CAST (videoanalyse);
184
185 /* get timestamps */
186 timestamp = GST_BUFFER_TIMESTAMP (frame->buffer);
187 duration = GST_BUFFER_DURATION (frame->buffer);
188 running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
189 timestamp);
190 stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
191 timestamp);
192
193 m = gst_message_new_element (GST_OBJECT_CAST (videoanalyse),
194 gst_structure_new ("GstVideoAnalyse",
195 "timestamp", G_TYPE_UINT64, timestamp,
196 "stream-time", G_TYPE_UINT64, stream_time,
197 "running-time", G_TYPE_UINT64, running_time,
198 "duration", G_TYPE_UINT64, duration,
199 "luma-average", G_TYPE_DOUBLE, videoanalyse->luma_average,
200 "luma-variance", G_TYPE_DOUBLE, videoanalyse->luma_variance, NULL));
201
202 gst_element_post_message (GST_ELEMENT_CAST (videoanalyse), m);
203 }
204
205 static void
gst_video_analyse_planar(GstVideoAnalyse * videoanalyse,GstVideoFrame * frame)206 gst_video_analyse_planar (GstVideoAnalyse * videoanalyse, GstVideoFrame * frame)
207 {
208 guint64 sum;
209 gint avg, diff;
210 gint i, j;
211 guint8 *d;
212 gint width = frame->info.width;
213 gint height = frame->info.height;
214 gint stride;
215
216 d = frame->data[0];
217 stride = frame->info.stride[0];
218 sum = 0;
219 /* do brightness as average of pixel brightness in 0.0 to 1.0 */
220 for (i = 0; i < height; i++) {
221 for (j = 0; j < width; j++) {
222 sum += d[j];
223 }
224 d += stride;
225 }
226 avg = sum / (width * height);
227 videoanalyse->luma_average = sum / (255.0 * width * height);
228
229 d = frame->data[0];
230 stride = frame->info.stride[0];
231 sum = 0;
232 /* do variance */
233 for (i = 0; i < height; i++) {
234 for (j = 0; j < width; j++) {
235 diff = (avg - d[j]);
236 sum += diff * diff;
237 }
238 d += stride;
239 }
240 videoanalyse->luma_variance = sum / (255.0 * 255.0 * width * height);
241 }
242
243 static GstFlowReturn
gst_video_analyse_transform_frame_ip(GstVideoFilter * filter,GstVideoFrame * frame)244 gst_video_analyse_transform_frame_ip (GstVideoFilter * filter,
245 GstVideoFrame * frame)
246 {
247 GstVideoAnalyse *videoanalyse = GST_VIDEO_ANALYSE (filter);
248
249 GST_DEBUG_OBJECT (videoanalyse, "transform_frame_ip");
250
251 gst_video_analyse_planar (videoanalyse, frame);
252
253 if (videoanalyse->message)
254 gst_video_analyse_post_message (videoanalyse, frame);
255
256 return GST_FLOW_OK;
257 }
258