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: #GstVideoDetect
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 #GstVideoDetect 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
110 static void
gst_video_mark_class_init(GstSimpleVideoMarkClass * klass)111 gst_video_mark_class_init (GstSimpleVideoMarkClass * klass)
112 {
113 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
114 GstBaseTransformClass *base_transform_class =
115 GST_BASE_TRANSFORM_CLASS (klass);
116 GstVideoFilterClass *video_filter_class = GST_VIDEO_FILTER_CLASS (klass);
117
118 gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
119 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
120 gst_caps_from_string (VIDEO_CAPS)));
121 gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass),
122 gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
123 gst_caps_from_string (VIDEO_CAPS)));
124
125 gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
126 "Video marker", "Filter/Effect/Video",
127 "Marks a video signal with a pattern", "Wim Taymans <wim@fluendo.com>");
128
129 gobject_class->set_property = gst_video_mark_set_property;
130 gobject_class->get_property = gst_video_mark_get_property;
131 gobject_class->dispose = gst_video_mark_dispose;
132 gobject_class->finalize = gst_video_mark_finalize;
133 base_transform_class->start = GST_DEBUG_FUNCPTR (gst_video_mark_start);
134 base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_video_mark_stop);
135 video_filter_class->set_info = GST_DEBUG_FUNCPTR (gst_video_mark_set_info);
136 video_filter_class->transform_frame_ip =
137 GST_DEBUG_FUNCPTR (gst_video_mark_transform_frame_ip);
138
139 g_object_class_install_property (gobject_class, PROP_PATTERN_WIDTH,
140 g_param_spec_int ("pattern-width", "Pattern width",
141 "The width of the pattern markers", 1, G_MAXINT,
142 DEFAULT_PATTERN_WIDTH,
143 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
144 g_object_class_install_property (gobject_class, PROP_PATTERN_HEIGHT,
145 g_param_spec_int ("pattern-height", "Pattern height",
146 "The height of the pattern markers", 1, G_MAXINT,
147 DEFAULT_PATTERN_HEIGHT,
148 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
149 g_object_class_install_property (gobject_class, PROP_PATTERN_COUNT,
150 g_param_spec_int ("pattern-count", "Pattern count",
151 "The number of pattern markers", 0, G_MAXINT,
152 DEFAULT_PATTERN_COUNT,
153 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
154 g_object_class_install_property (gobject_class, PROP_PATTERN_DATA_COUNT,
155 g_param_spec_int ("pattern-data-count", "Pattern data count",
156 "The number of extra data pattern markers", 0, 64,
157 DEFAULT_PATTERN_DATA_COUNT,
158 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
159 g_object_class_install_property (gobject_class, PROP_PATTERN_DATA,
160 g_param_spec_uint64 ("pattern-data", "Pattern data",
161 "The extra data pattern markers", 0, G_MAXUINT64,
162 DEFAULT_PATTERN_DATA,
163 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
164 g_object_class_install_property (gobject_class, PROP_ENABLED,
165 g_param_spec_boolean ("enabled", "Enabled",
166 "Enable or disable the filter",
167 DEFAULT_ENABLED,
168 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
169 g_object_class_install_property (gobject_class, PROP_LEFT_OFFSET,
170 g_param_spec_int ("left-offset", "Left Offset",
171 "The offset from the left border where the pattern starts", 0,
172 G_MAXINT, DEFAULT_LEFT_OFFSET,
173 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
174 g_object_class_install_property (gobject_class, PROP_BOTTOM_OFFSET,
175 g_param_spec_int ("bottom-offset", "Bottom Offset",
176 "The offset from the bottom border where the pattern starts", 0,
177 G_MAXINT, DEFAULT_BOTTOM_OFFSET,
178 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
179
180 }
181
182 static void
gst_video_mark_init(GstSimpleVideoMark * simplevideomark)183 gst_video_mark_init (GstSimpleVideoMark * simplevideomark)
184 {
185 }
186
187 void
gst_video_mark_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)188 gst_video_mark_set_property (GObject * object, guint property_id,
189 const GValue * value, GParamSpec * pspec)
190 {
191 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
192
193 GST_DEBUG_OBJECT (simplevideomark, "set_property");
194
195 switch (property_id) {
196 case PROP_PATTERN_WIDTH:
197 simplevideomark->pattern_width = g_value_get_int (value);
198 break;
199 case PROP_PATTERN_HEIGHT:
200 simplevideomark->pattern_height = g_value_get_int (value);
201 break;
202 case PROP_PATTERN_COUNT:
203 simplevideomark->pattern_count = g_value_get_int (value);
204 break;
205 case PROP_PATTERN_DATA_COUNT:
206 simplevideomark->pattern_data_count = g_value_get_int (value);
207 break;
208 case PROP_PATTERN_DATA:
209 simplevideomark->pattern_data = g_value_get_uint64 (value);
210 break;
211 case PROP_ENABLED:
212 simplevideomark->enabled = g_value_get_boolean (value);
213 break;
214 case PROP_LEFT_OFFSET:
215 simplevideomark->left_offset = g_value_get_int (value);
216 break;
217 case PROP_BOTTOM_OFFSET:
218 simplevideomark->bottom_offset = g_value_get_int (value);
219 break;
220 default:
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
222 break;
223 }
224 }
225
226 void
gst_video_mark_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)227 gst_video_mark_get_property (GObject * object, guint property_id,
228 GValue * value, GParamSpec * pspec)
229 {
230 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
231
232 GST_DEBUG_OBJECT (simplevideomark, "get_property");
233
234 switch (property_id) {
235 case PROP_PATTERN_WIDTH:
236 g_value_set_int (value, simplevideomark->pattern_width);
237 break;
238 case PROP_PATTERN_HEIGHT:
239 g_value_set_int (value, simplevideomark->pattern_height);
240 break;
241 case PROP_PATTERN_COUNT:
242 g_value_set_int (value, simplevideomark->pattern_count);
243 break;
244 case PROP_PATTERN_DATA_COUNT:
245 g_value_set_int (value, simplevideomark->pattern_data_count);
246 break;
247 case PROP_PATTERN_DATA:
248 g_value_set_uint64 (value, simplevideomark->pattern_data);
249 break;
250 case PROP_ENABLED:
251 g_value_set_boolean (value, simplevideomark->enabled);
252 break;
253 case PROP_LEFT_OFFSET:
254 g_value_set_int (value, simplevideomark->left_offset);
255 break;
256 case PROP_BOTTOM_OFFSET:
257 g_value_set_int (value, simplevideomark->bottom_offset);
258 break;
259 default:
260 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
261 break;
262 }
263 }
264
265 void
gst_video_mark_dispose(GObject * object)266 gst_video_mark_dispose (GObject * object)
267 {
268 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
269
270 GST_DEBUG_OBJECT (simplevideomark, "dispose");
271
272 /* clean up as possible. may be called multiple times */
273
274 G_OBJECT_CLASS (gst_video_mark_parent_class)->dispose (object);
275 }
276
277 void
gst_video_mark_finalize(GObject * object)278 gst_video_mark_finalize (GObject * object)
279 {
280 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (object);
281
282 GST_DEBUG_OBJECT (simplevideomark, "finalize");
283
284 /* clean up object here */
285
286 G_OBJECT_CLASS (gst_video_mark_parent_class)->finalize (object);
287 }
288
289 static gboolean
gst_video_mark_start(GstBaseTransform * trans)290 gst_video_mark_start (GstBaseTransform * trans)
291 {
292 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (trans);
293
294 GST_DEBUG_OBJECT (simplevideomark, "start");
295
296 return TRUE;
297 }
298
299 static gboolean
gst_video_mark_stop(GstBaseTransform * trans)300 gst_video_mark_stop (GstBaseTransform * trans)
301 {
302 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (trans);
303
304 GST_DEBUG_OBJECT (simplevideomark, "stop");
305
306 return TRUE;
307 }
308
309 static gboolean
gst_video_mark_set_info(GstVideoFilter * filter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)310 gst_video_mark_set_info (GstVideoFilter * filter, GstCaps * incaps,
311 GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
312 {
313 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (filter);
314
315 GST_DEBUG_OBJECT (simplevideomark, "set_info");
316
317 return TRUE;
318 }
319
320 static void
gst_video_mark_draw_box(GstSimpleVideoMark * simplevideomark,guint8 * data,gint width,gint height,gint row_stride,gint pixel_stride,guint8 color)321 gst_video_mark_draw_box (GstSimpleVideoMark * simplevideomark, guint8 * data,
322 gint width, gint height, gint row_stride, gint pixel_stride, guint8 color)
323 {
324 gint i, j;
325
326 for (i = 0; i < height; i++) {
327 for (j = 0; j < width; j++) {
328 data[pixel_stride * j] = color;
329 }
330 data += row_stride;
331 }
332 }
333
334 static gint
calculate_pw(gint pw,gint x,gint width)335 calculate_pw (gint pw, gint x, gint width)
336 {
337 if (x < 0)
338 pw += x;
339 else if ((x + pw) > width)
340 pw = width - x;
341
342 return pw;
343 }
344
345 static GstFlowReturn
gst_video_mark_yuv(GstSimpleVideoMark * simplevideomark,GstVideoFrame * frame)346 gst_video_mark_yuv (GstSimpleVideoMark * simplevideomark, GstVideoFrame * frame)
347 {
348 gint i, pw, ph, row_stride, pixel_stride;
349 gint width, height, offset_calc, x, y;
350 guint8 *d;
351 guint64 pattern_shift;
352 guint8 color;
353 gint total_pattern;
354
355 width = frame->info.width;
356 height = frame->info.height;
357
358 pw = simplevideomark->pattern_width;
359 ph = simplevideomark->pattern_height;
360 row_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
361 pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (frame, 0);
362
363 d = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
364 offset_calc =
365 row_stride * (height - ph - simplevideomark->bottom_offset) +
366 pixel_stride * simplevideomark->left_offset;
367 x = simplevideomark->left_offset;
368 y = height - ph - simplevideomark->bottom_offset;
369
370 total_pattern =
371 simplevideomark->pattern_count + simplevideomark->pattern_data_count;
372 /* If x and y offset values are outside the video, no need to draw */
373 if ((x + (pw * total_pattern)) < 0 || x > width || (y + height) < 0
374 || y > height) {
375 GST_ERROR_OBJECT (simplevideomark,
376 "simplevideomark pattern is outside the video. Not drawing.");
377 return GST_FLOW_OK;
378 }
379
380 /* Offset calculation less than 0, then reset to 0 */
381 if (offset_calc < 0)
382 offset_calc = 0;
383 /* Y position of mark is negative or pattern exceeds the video height,
384 then recalculate pattern height for partial display */
385 if (y < 0)
386 ph += y;
387 else if ((y + ph) > height)
388 ph = height - y;
389 /* If pattern height is less than 0, need not draw anything */
390 if (ph < 0)
391 return GST_FLOW_OK;
392
393 /* move to start of bottom left */
394 d += offset_calc;
395
396 /* draw the bottom left pixels */
397 for (i = 0; i < simplevideomark->pattern_count; i++) {
398 gint draw_pw;
399
400 if (i & 1)
401 /* odd pixels must be white */
402 color = 255;
403 else
404 color = 0;
405
406 /* X position of mark is negative or pattern exceeds the video width,
407 then recalculate pattern width for partial display */
408 draw_pw = calculate_pw (pw, x, width);
409 /* If pattern width is less than 0, continue with the next pattern */
410 if (draw_pw < 0)
411 continue;
412
413 /* draw box of width * height */
414 gst_video_mark_draw_box (simplevideomark, d, draw_pw, ph, row_stride,
415 pixel_stride, color);
416
417 /* move to i-th pattern */
418 d += pixel_stride * draw_pw;
419 x += draw_pw;
420
421 if ((x + (pw * (total_pattern - i - 1))) < 0 || x >= width)
422 return GST_FLOW_OK;
423 }
424
425 pattern_shift =
426 G_GUINT64_CONSTANT (1) << (simplevideomark->pattern_data_count - 1);
427
428 /* get the data of the pattern */
429 for (i = 0; i < simplevideomark->pattern_data_count; i++) {
430 gint draw_pw;
431 if (simplevideomark->pattern_data & pattern_shift)
432 color = 255;
433 else
434 color = 0;
435
436 /* X position of mark is negative or pattern exceeds the video width,
437 then recalculate pattern width for partial display */
438 draw_pw = calculate_pw (pw, x, width);
439 /* If pattern width is less than 0, continue with the next pattern */
440 if (draw_pw < 0)
441 continue;
442
443 gst_video_mark_draw_box (simplevideomark, d, draw_pw, ph, row_stride,
444 pixel_stride, color);
445
446 pattern_shift >>= 1;
447
448 /* move to i-th pattern data */
449 d += pixel_stride * draw_pw;
450 x += draw_pw;
451
452 if ((x + (pw * (simplevideomark->pattern_data_count - i - 1))) < 0
453 || x >= width)
454 return GST_FLOW_OK;
455 }
456
457 return GST_FLOW_OK;
458 }
459
460
461 static GstFlowReturn
gst_video_mark_transform_frame_ip(GstVideoFilter * filter,GstVideoFrame * frame)462 gst_video_mark_transform_frame_ip (GstVideoFilter * filter,
463 GstVideoFrame * frame)
464 {
465 GstSimpleVideoMark *simplevideomark = GST_SIMPLE_VIDEO_MARK (filter);
466
467 GST_DEBUG_OBJECT (simplevideomark, "transform_frame_ip");
468
469 if (simplevideomark->enabled)
470 return gst_video_mark_yuv (simplevideomark, frame);
471
472 return GST_FLOW_OK;
473 }
474