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