• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2007> 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-simplevideomark
21  * @title: simplevideomark
22  * @see_also: #GstSimpleVideoMarkDetect
23  *
24  * This plugin produces #GstSimpleVideoMark:pattern-count squares in the bottom left
25  * corner of the video frames. The squares have a width and height of
26  * respectively #GstSimpleVideoMark:pattern-width and #GstSimpleVideoMark:pattern-height.
27  * Even squares will be black and odd squares will be white.
28  *
29  * After writing the pattern, #GstSimpleVideoMark:pattern-data-count squares after the
30  * pattern squares are produced as the bitarray given in
31  * #GstSimpleVideoMark:pattern-data. 1 bits will produce white squares and 0 bits will
32  * produce black squares.
33  *
34  * The element can be enabled with the #GstSimpleVideoMark:enabled property. It is
35  * mostly used together with the #GstSimpleVideoMarkDetect plugin.
36  *
37  * ## Example launch line
38  * |[
39  * gst-launch-1.0 videotestsrc ! simplevideomark ! videoconvert ! ximagesink
40  * ]| Add the default black/white squares at the bottom left of the video frames.
41  *
42  */
43 
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47 
48 #include <gst/gst.h>
49 #include <gst/video/video.h>
50 #include <gst/video/gstvideofilter.h>
51 #include "gstsimplevideomark.h"
52 
53 GST_DEBUG_CATEGORY_STATIC (gst_video_mark_debug_category);
54 #define GST_CAT_DEFAULT gst_video_mark_debug_category
55 
56 /* prototypes */
57 
58 
59 static void gst_video_mark_set_property (GObject * object,
60     guint property_id, const GValue * value, GParamSpec * pspec);
61 static void gst_video_mark_get_property (GObject * object,
62     guint property_id, GValue * value, GParamSpec * pspec);
63 static void gst_video_mark_dispose (GObject * object);
64 static void gst_video_mark_finalize (GObject * object);
65 
66 static gboolean gst_video_mark_start (GstBaseTransform * trans);
67 static gboolean gst_video_mark_stop (GstBaseTransform * trans);
68 static gboolean gst_video_mark_set_info (GstVideoFilter * filter,
69     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
70     GstVideoInfo * out_info);
71 static GstFlowReturn gst_video_mark_transform_frame_ip (GstVideoFilter * filter,
72     GstVideoFrame * frame);
73 
74 enum
75 {
76   PROP_0,
77   PROP_PATTERN_WIDTH,
78   PROP_PATTERN_HEIGHT,
79   PROP_PATTERN_COUNT,
80   PROP_PATTERN_DATA_COUNT,
81   PROP_PATTERN_DATA,
82   PROP_ENABLED,
83   PROP_LEFT_OFFSET,
84   PROP_BOTTOM_OFFSET
85 };
86 
87 #define DEFAULT_PATTERN_WIDTH        4
88 #define DEFAULT_PATTERN_HEIGHT       16
89 #define DEFAULT_PATTERN_COUNT        4
90 #define DEFAULT_PATTERN_DATA_COUNT   5
91 #define DEFAULT_PATTERN_DATA         10
92 #define DEFAULT_ENABLED              TRUE
93 #define DEFAULT_LEFT_OFFSET          0
94 #define DEFAULT_BOTTOM_OFFSET        0
95 
96 /* pad templates */
97 
98 #define VIDEO_CAPS \
99     GST_VIDEO_CAPS_MAKE( \
100         "{ I420, YV12, Y41B, Y42B, Y444, YUY2, UYVY, AYUV, YVYU }")
101 
102 
103 /* class initialization */
104 
105 G_DEFINE_TYPE_WITH_CODE (GstSimpleVideoMark, gst_video_mark,
106     GST_TYPE_VIDEO_FILTER,
107     GST_DEBUG_CATEGORY_INIT (gst_video_mark_debug_category, "simplevideomark",
108         0, "debug category for simplevideomark element"));
109 GST_ELEMENT_REGISTER_DEFINE (simplevideomark, "simplevideomark",
110     GST_RANK_NONE, GST_TYPE_SIMPLE_VIDEO_MARK);
111 
112 static void
gst_video_mark_class_init(GstSimpleVideoMarkClass * klass)113 gst_video_mark_class_init (GstSimpleVideoMarkClass * klass)
114 {
115   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
116   GstBaseTransformClass *base_transform_class =
117       GST_BASE_TRANSFORM_CLASS (klass);
118   GstVideoFilterClass *video_filter_class = GST_VIDEO_FILTER_CLASS (klass);
119 
120   gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
121       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
122           gst_caps_from_string (VIDEO_CAPS)));
123   gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
124       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
125           gst_caps_from_string (VIDEO_CAPS)));
126 
127   gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
128       "Video marker", "Filter/Effect/Video",
129       "Marks a video signal with a pattern", "Wim Taymans <wim@fluendo.com>");
130 
131   gobject_class->set_property = gst_video_mark_set_property;
132   gobject_class->get_property = gst_video_mark_get_property;
133   gobject_class->dispose = gst_video_mark_dispose;
134   gobject_class->finalize = gst_video_mark_finalize;
135   base_transform_class->start = GST_DEBUG_FUNCPTR (gst_video_mark_start);
136   base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_video_mark_stop);
137   video_filter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_mark_set_info);
138   video_filter_class->transform_frame_ip =
139       GST_DEBUG_FUNCPTR (gst_video_mark_transform_frame_ip);
140 
141   g_object_class_install_property (gobject_class, PROP_PATTERN_WIDTH,
142       g_param_spec_int ("pattern-width", "Pattern width",
143           "The width of the pattern markers", 1, G_MAXINT,
144           DEFAULT_PATTERN_WIDTH,
145           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
146   g_object_class_install_property (gobject_class, PROP_PATTERN_HEIGHT,
147       g_param_spec_int ("pattern-height", "Pattern height",
148           "The height of the pattern markers", 1, G_MAXINT,
149           DEFAULT_PATTERN_HEIGHT,
150           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
151   g_object_class_install_property (gobject_class, PROP_PATTERN_COUNT,
152       g_param_spec_int ("pattern-count", "Pattern count",
153           "The number of pattern markers", 0, G_MAXINT,
154           DEFAULT_PATTERN_COUNT,
155           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
156   g_object_class_install_property (gobject_class, PROP_PATTERN_DATA_COUNT,
157       g_param_spec_int ("pattern-data-count", "Pattern data count",
158           "The number of extra data pattern markers", 0, 64,
159           DEFAULT_PATTERN_DATA_COUNT,
160           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
161   g_object_class_install_property (gobject_class, PROP_PATTERN_DATA,
162       g_param_spec_uint64 ("pattern-data", "Pattern data",
163           "The extra data pattern markers", 0, G_MAXUINT64,
164           DEFAULT_PATTERN_DATA,
165           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
166   g_object_class_install_property (gobject_class, PROP_ENABLED,
167       g_param_spec_boolean ("enabled", "Enabled",
168           "Enable or disable the filter",
169           DEFAULT_ENABLED,
170           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
171   g_object_class_install_property (gobject_class, PROP_LEFT_OFFSET,
172       g_param_spec_int ("left-offset", "Left Offset",
173           "The offset from the left border where the pattern starts", 0,
174           G_MAXINT, DEFAULT_LEFT_OFFSET,
175           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
176   g_object_class_install_property (gobject_class, PROP_BOTTOM_OFFSET,
177       g_param_spec_int ("bottom-offset", "Bottom Offset",
178           "The offset from the bottom border where the pattern starts", 0,
179           G_MAXINT, DEFAULT_BOTTOM_OFFSET,
180           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
181 
182 }
183 
184 static void
gst_video_mark_init(GstSimpleVideoMark * simplevideomark)185 gst_video_mark_init (GstSimpleVideoMark * simplevideomark)
186 {
187 }
188 
189 void
gst_video_mark_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)190 gst_video_mark_set_property (GObject * object, guint property_id,
191     const GValue * value, GParamSpec * pspec)
192 {
193   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
194 
195   GST_DEBUG_OBJECT (simplevideomark, "set_property");
196 
197   switch (property_id) {
198     case PROP_PATTERN_WIDTH:
199       simplevideomark->pattern_width = g_value_get_int (value);
200       break;
201     case PROP_PATTERN_HEIGHT:
202       simplevideomark->pattern_height = g_value_get_int (value);
203       break;
204     case PROP_PATTERN_COUNT:
205       simplevideomark->pattern_count = g_value_get_int (value);
206       break;
207     case PROP_PATTERN_DATA_COUNT:
208       simplevideomark->pattern_data_count = g_value_get_int (value);
209       break;
210     case PROP_PATTERN_DATA:
211       simplevideomark->pattern_data = g_value_get_uint64 (value);
212       break;
213     case PROP_ENABLED:
214       simplevideomark->enabled = g_value_get_boolean (value);
215       break;
216     case PROP_LEFT_OFFSET:
217       simplevideomark->left_offset = g_value_get_int (value);
218       break;
219     case PROP_BOTTOM_OFFSET:
220       simplevideomark->bottom_offset = g_value_get_int (value);
221       break;
222     default:
223       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
224       break;
225   }
226 }
227 
228 void
gst_video_mark_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)229 gst_video_mark_get_property (GObject * object, guint property_id,
230     GValue * value, GParamSpec * pspec)
231 {
232   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
233 
234   GST_DEBUG_OBJECT (simplevideomark, "get_property");
235 
236   switch (property_id) {
237     case PROP_PATTERN_WIDTH:
238       g_value_set_int (value, simplevideomark->pattern_width);
239       break;
240     case PROP_PATTERN_HEIGHT:
241       g_value_set_int (value, simplevideomark->pattern_height);
242       break;
243     case PROP_PATTERN_COUNT:
244       g_value_set_int (value, simplevideomark->pattern_count);
245       break;
246     case PROP_PATTERN_DATA_COUNT:
247       g_value_set_int (value, simplevideomark->pattern_data_count);
248       break;
249     case PROP_PATTERN_DATA:
250       g_value_set_uint64 (value, simplevideomark->pattern_data);
251       break;
252     case PROP_ENABLED:
253       g_value_set_boolean (value, simplevideomark->enabled);
254       break;
255     case PROP_LEFT_OFFSET:
256       g_value_set_int (value, simplevideomark->left_offset);
257       break;
258     case PROP_BOTTOM_OFFSET:
259       g_value_set_int (value, simplevideomark->bottom_offset);
260       break;
261     default:
262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
263       break;
264   }
265 }
266 
267 void
gst_video_mark_dispose(GObject * object)268 gst_video_mark_dispose (GObject * object)
269 {
270   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
271 
272   GST_DEBUG_OBJECT (simplevideomark, "dispose");
273 
274   /* clean up as possible.  may be called multiple times */
275 
276   G_OBJECT_CLASS (gst_video_mark_parent_class)->dispose (object);
277 }
278 
279 void
gst_video_mark_finalize(GObject * object)280 gst_video_mark_finalize (GObject * object)
281 {
282   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
283 
284   GST_DEBUG_OBJECT (simplevideomark, "finalize");
285 
286   /* clean up object here */
287 
288   G_OBJECT_CLASS (gst_video_mark_parent_class)->finalize (object);
289 }
290 
291 static gboolean
gst_video_mark_start(GstBaseTransform * trans)292 gst_video_mark_start (GstBaseTransform * trans)
293 {
294   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (trans);
295 
296   GST_DEBUG_OBJECT (simplevideomark, "start");
297 
298   return TRUE;
299 }
300 
301 static gboolean
gst_video_mark_stop(GstBaseTransform * trans)302 gst_video_mark_stop (GstBaseTransform * trans)
303 {
304   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (trans);
305 
306   GST_DEBUG_OBJECT (simplevideomark, "stop");
307 
308   return TRUE;
309 }
310 
311 static gboolean
gst_video_mark_set_info(GstVideoFilter * filter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)312 gst_video_mark_set_info (GstVideoFilter * filter, GstCaps * incaps,
313     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
314 {
315   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (filter);
316 
317   GST_DEBUG_OBJECT (simplevideomark, "set_info");
318 
319   return TRUE;
320 }
321 
322 static void
gst_video_mark_draw_box(GstSimpleVideoMark * simplevideomark,guint8 * data,gint width,gint height,gint row_stride,gint pixel_stride,guint8 color)323 gst_video_mark_draw_box (GstSimpleVideoMark * simplevideomark, guint8 * data,
324     gint width, gint height, gint row_stride, gint pixel_stride, guint8 color)
325 {
326   gint i, j;
327 
328   for (i = 0; i < height; i++) {
329     for (j = 0; j < width; j++) {
330       data[pixel_stride * j] = color;
331     }
332     data += row_stride;
333   }
334 }
335 
336 static gint
calculate_pw(gint pw,gint x,gint width)337 calculate_pw (gint pw, gint x, gint width)
338 {
339   if (x < 0)
340     pw += x;
341   else if ((x + pw) > width)
342     pw = width - x;
343 
344   return pw;
345 }
346 
347 static GstFlowReturn
gst_video_mark_yuv(GstSimpleVideoMark * simplevideomark,GstVideoFrame * frame)348 gst_video_mark_yuv (GstSimpleVideoMark * simplevideomark, GstVideoFrame * frame)
349 {
350   gint i, pw, ph, row_stride, pixel_stride;
351   gint width, height, offset_calc, x, y;
352   guint8 *d;
353   guint64 pattern_shift;
354   guint8 color;
355   gint total_pattern;
356 
357   width = frame->info.width;
358   height = frame->info.height;
359 
360   pw = simplevideomark->pattern_width;
361   ph = simplevideomark->pattern_height;
362   row_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
363   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
364 
365   d = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
366   offset_calc =
367       row_stride * (height - ph - simplevideomark->bottom_offset) +
368       pixel_stride * simplevideomark->left_offset;
369   x = simplevideomark->left_offset;
370   y = height - ph - simplevideomark->bottom_offset;
371 
372   total_pattern =
373       simplevideomark->pattern_count + simplevideomark->pattern_data_count;
374   /* If x and y offset values are outside the video, no need to draw */
375   if ((x + (pw * total_pattern)) < 0 || x > width || (y + height) < 0
376       || y > height) {
377     GST_ERROR_OBJECT (simplevideomark,
378         "simplevideomark pattern is outside the video. Not drawing.");
379     return GST_FLOW_OK;
380   }
381 
382   /* Offset calculation less than 0, then reset to 0 */
383   if (offset_calc < 0)
384     offset_calc = 0;
385   /* Y position of mark is negative or pattern exceeds the video height,
386      then recalculate pattern height for partial display */
387   if (y < 0)
388     ph += y;
389   else if ((y + ph) > height)
390     ph = height - y;
391   /* If pattern height is less than 0, need not draw anything */
392   if (ph < 0)
393     return GST_FLOW_OK;
394 
395   /* move to start of bottom left */
396   d += offset_calc;
397 
398   /* draw the bottom left pixels */
399   for (i = 0; i < simplevideomark->pattern_count; i++) {
400     gint draw_pw;
401 
402     if (i & 1)
403       /* odd pixels must be white */
404       color = 255;
405     else
406       color = 0;
407 
408     /* X position of mark is negative or pattern exceeds the video width,
409        then recalculate pattern width for partial display */
410     draw_pw = calculate_pw (pw, x, width);
411     /* If pattern width is less than 0, continue with the next pattern */
412     if (draw_pw < 0)
413       continue;
414 
415     /* draw box of width * height */
416     gst_video_mark_draw_box (simplevideomark, d, draw_pw, ph, row_stride,
417         pixel_stride, color);
418 
419     /* move to i-th pattern */
420     d += pixel_stride * draw_pw;
421     x += draw_pw;
422 
423     if ((x + (pw * (total_pattern - i - 1))) < 0 || x >= width)
424       return GST_FLOW_OK;
425   }
426 
427   pattern_shift =
428       G_GUINT64_CONSTANT (1) << (simplevideomark->pattern_data_count - 1);
429 
430   /* get the data of the pattern */
431   for (i = 0; i < simplevideomark->pattern_data_count; i++) {
432     gint draw_pw;
433     if (simplevideomark->pattern_data & pattern_shift)
434       color = 255;
435     else
436       color = 0;
437 
438     /* X position of mark is negative or pattern exceeds the video width,
439        then recalculate pattern width for partial display */
440     draw_pw = calculate_pw (pw, x, width);
441     /* If pattern width is less than 0, continue with the next pattern */
442     if (draw_pw < 0)
443       continue;
444 
445     gst_video_mark_draw_box (simplevideomark, d, draw_pw, ph, row_stride,
446         pixel_stride, color);
447 
448     pattern_shift >>= 1;
449 
450     /* move to i-th pattern data */
451     d += pixel_stride * draw_pw;
452     x += draw_pw;
453 
454     if ((x + (pw * (simplevideomark->pattern_data_count - i - 1))) < 0
455         || x >= width)
456       return GST_FLOW_OK;
457   }
458 
459   return GST_FLOW_OK;
460 }
461 
462 
463 static GstFlowReturn
gst_video_mark_transform_frame_ip(GstVideoFilter * filter,GstVideoFrame * frame)464 gst_video_mark_transform_frame_ip (GstVideoFilter * filter,
465     GstVideoFrame * frame)
466 {
467   GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (filter);
468 
469   GST_DEBUG_OBJECT (simplevideomark, "transform_frame_ip");
470 
471   if (simplevideomark->enabled)
472     return gst_video_mark_yuv (simplevideomark, frame);
473 
474   return GST_FLOW_OK;
475 }
476