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