3 * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
4 * Copyright (c) 2020 Anthony Violo <anthony.violo@ubicast.eu>
5 * Copyright (c) 2020 Thibault Saunier <tsaunier@igalia.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
34 #include <gst/base/gstbasetransform.h>
35 #include <json-glib/json-glib.h>
42 #include "gstbaseqroverlay.h"
44 GST_DEBUG_CATEGORY_STATIC (gst_base_qr_overlay_debug);
45 #define GST_CAT_DEFAULT gst_base_qr_overlay_debug
53 PROP_QRCODE_ERROR_CORRECTION,
57 typedef struct _GstBaseQROverlayPrivate GstBaseQROverlayPrivate;
58 struct _GstBaseQROverlayPrivate
66 GstElement *overlaycomposition;
69 gboolean case_sensitive;
71 GstPad *sinkpad, *srcpad;
72 GstVideoOverlayComposition *prev_overlay;
75 #define PRIV(s) gst_base_qr_overlay_get_instance_private (GST_BASE_QR_OVERLAY (s))
77 #define OVERLAY_COMPOSITION_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS)
79 #define ALL_CAPS OVERLAY_COMPOSITION_CAPS ";" \
80 GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
82 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
85 GST_STATIC_CAPS (ALL_CAPS)
88 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
91 GST_STATIC_CAPS (ALL_CAPS)
94 #define DEFAULT_PROP_QUALITY 1
95 #define DEFAULT_PROP_PIXEL_SIZE 3
96 #define DEFAULT_PROP_CASE_SENSITIVE FALSE
98 #define GST_TYPE_QRCODE_QUALITY (gst_qrcode_quality_get_type())
100 gst_qrcode_quality_get_type (void)
102 static GType qrcode_quality_type = 0;
104 static const GEnumValue qrcode_quality[] = {
105 {0, "Level L", "Approx 7%"},
106 {1, "Level M", "Approx 15%"},
107 {2, "Level Q", "Approx 25%"},
108 {3, "Level H", "Approx 30%"},
112 if (!qrcode_quality_type) {
113 qrcode_quality_type =
114 g_enum_register_static ("GstQrcodeOverlayCorrection", qrcode_quality);
116 return qrcode_quality_type;
119 #define gst_base_qr_overlay_parent_class parent_class
120 G_DEFINE_TYPE_WITH_PRIVATE (GstBaseQROverlay, gst_base_qr_overlay,
123 static void gst_base_qr_overlay_set_property (GObject * object, guint prop_id,
124 const GValue * value, GParamSpec * pspec);
125 static void gst_base_qr_overlay_get_property (GObject * object, guint prop_id,
126 GValue * value, GParamSpec * pspec);
129 gst_base_qr_overlay_caps_changed_cb (GstBaseQROverlay * self, GstCaps * caps,
130 gint window_width, gint window_height, GstElement * overlay)
132 GstBaseQROverlayPrivate *priv = PRIV (self);
134 if (gst_video_info_from_caps (&priv->info, caps))
140 static GstVideoOverlayComposition *
141 draw_overlay (GstBaseQROverlay * self, QRcode * qrcode)
143 guint8 *qr_data, *pixels;
144 gint stride, pstride, y, x, yy, square_size;
145 gsize offset, line_offset;
147 GstVideoOverlayRectangle *rect;
148 GstVideoOverlayComposition *comp;
150 GstBaseQROverlayPrivate *priv = PRIV (self);
152 gst_video_info_init (&info);
154 square_size = (qrcode->width + 4 * 2) * priv->qrcode_size;
155 gst_video_info_set_format (&info, GST_VIDEO_FORMAT_ARGB, square_size,
158 pixels = g_malloc ((size_t) info.size);
159 stride = info.stride[0];
160 pstride = info.finfo->pixel_stride[0];
162 /* White background */
163 for (y = 0; y < info.height; y++)
164 memset (&pixels[y * stride], 0xff, stride);
166 /* Draw the black QR code blocks with 4px white space around it
168 line_offset = 4 * priv->qrcode_size * stride;
169 qr_data = qrcode->data;
170 for (y = 0; y < qrcode->width; y++) {
171 for (x = 0; x < (qrcode->width); x++) {
172 for (yy = 0; yy < priv->qrcode_size * pstride; yy += pstride) {
177 (((line_offset + (stride * (yy / pstride))) +
178 x * priv->qrcode_size * pstride)) +
179 (priv->qrcode_size * pstride) + (4 * priv->qrcode_size * pstride);
181 for (gint i = 0; i < priv->qrcode_size * pstride; i += pstride) {
182 pixels[offset + i] = 0x00;
183 pixels[offset + i + 1] = 0x00;
184 pixels[offset + i + 2] = 0x00;
189 line_offset += (stride * priv->qrcode_size);
192 buf = gst_buffer_new_wrapped (pixels, info.size);
193 gst_buffer_add_video_meta (buf, GST_VIDEO_FRAME_FLAG_NONE,
194 GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB, info.width, info.height);
196 x = (int) (priv->info.width - square_size) * (priv->x_percent / 100);
197 x = GST_ROUND_DOWN_2 (x);
198 y = (int) (priv->info.height - square_size) * (priv->y_percent / 100);
199 y = GST_ROUND_DOWN_4 (y);
201 rect = gst_video_overlay_rectangle_new_raw (buf, x, y,
202 info.width, info.height, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
203 comp = gst_video_overlay_composition_new (rect);
204 gst_video_overlay_rectangle_unref (rect);
209 static GstVideoOverlayComposition *
210 gst_base_qr_overlay_draw_cb (GstBaseQROverlay * self, GstSample * sample,
213 GstBaseQROverlayPrivate *priv = PRIV (self);
216 gboolean reuse_previous = FALSE;
217 GstVideoOverlayComposition *overlay = NULL;
218 GstBuffer *buffer = gst_sample_get_buffer (sample);
219 GstSegment *segment = gst_sample_get_segment (sample);
220 GstClockTime rtime = gst_segment_to_running_time (segment, GST_FORMAT_TIME,
221 GST_BUFFER_PTS (buffer));
224 GST_ERROR_OBJECT (self, "Trying to draw before negotiation?");
229 if (GST_CLOCK_TIME_IS_VALID (rtime))
230 gst_object_sync_values (GST_OBJECT (self), rtime);
233 GST_BASE_QR_OVERLAY_GET_CLASS (self)->get_content (GST_BASE_QR_OVERLAY
234 (self), buffer, &priv->info, &reuse_previous);
235 if (reuse_previous && priv->prev_overlay) {
236 overlay = gst_video_overlay_composition_ref (priv->prev_overlay);
237 } else if (content) {
238 GST_INFO_OBJECT (self, "String will be encoded : %s", content);
240 QRcode_encodeString (content, 0, priv->qrcode_quality, QR_MODE_8,
241 priv->case_sensitive);
244 GST_DEBUG_OBJECT (self, "String encoded");
245 overlay = draw_overlay (GST_BASE_QR_OVERLAY (self), qrcode);
246 gst_mini_object_replace (((GstMiniObject **) & priv->prev_overlay),
247 (GstMiniObject *) overlay);
249 GST_WARNING_OBJECT (self, "Could not encode content: %s", content);
257 /* GObject vmethod implementations */
260 gst_base_qr_overlay_dispose (GObject * object)
262 GstBaseQROverlayPrivate *priv = PRIV (object);
264 gst_mini_object_replace (((GstMiniObject **) & priv->prev_overlay), NULL);
267 /* initialize the qroverlay's class */
269 gst_base_qr_overlay_class_init (GstBaseQROverlayClass * klass)
271 GObjectClass *gobject_class;
272 GstElementClass *gstelement_class;
274 gobject_class = (GObjectClass *) klass;
275 gstelement_class = (GstElementClass *) klass;
277 gobject_class->set_property = gst_base_qr_overlay_set_property;
278 gobject_class->get_property = gst_base_qr_overlay_get_property;
279 gobject_class->dispose = gst_base_qr_overlay_dispose;
281 GST_DEBUG_CATEGORY_INIT (gst_base_qr_overlay_debug, "qroverlay", 0,
282 "Qrcode overlay base class");
284 g_object_class_install_property (gobject_class,
285 PROP_X_AXIS, g_param_spec_float ("x",
286 "X position (in percent of the width)",
287 "X position (in percent of the width)",
288 0.0, 100.0, 50.0, G_PARAM_READWRITE));
290 g_object_class_install_property (gobject_class,
291 PROP_Y_AXIS, g_param_spec_float ("y",
292 "Y position (in percent of the height)",
293 "Y position (in percent of the height)",
294 0.0, 100.0, 50.0, G_PARAM_READWRITE));
296 g_object_class_install_property (gobject_class,
297 PROP_PIXEL_SIZE, g_param_spec_float ("pixel-size",
298 "pixel-size", "Pixel size of each Qrcode pixel",
299 1, 100.0, DEFAULT_PROP_PIXEL_SIZE, G_PARAM_READWRITE));
301 g_object_class_install_property (gobject_class, PROP_QRCODE_ERROR_CORRECTION,
302 g_param_spec_enum ("qrcode-error-correction", "qrcode-error-correction",
303 "qrcode-error-correction", GST_TYPE_QRCODE_QUALITY,
304 DEFAULT_PROP_QUALITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
307 * GstBaseQROverlay::case-sensitive:
309 * Strings to encode are case sensitive (e.g. passwords or SSIDs).
313 g_object_class_install_property (gobject_class, PROP_CASE_SENSITIVE,
314 g_param_spec_boolean ("case-sensitive", "Case Sensitive",
315 "Strings to encode are case sensitive (e.g. passwords or SSIDs)",
316 DEFAULT_PROP_CASE_SENSITIVE,
317 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
319 gst_element_class_add_pad_template (gstelement_class,
320 gst_static_pad_template_get (&src_template));
321 gst_element_class_add_pad_template (gstelement_class,
322 gst_static_pad_template_get (&sink_template));
324 gst_type_mark_as_plugin_api (GST_TYPE_QRCODE_QUALITY, 0);
325 gst_type_mark_as_plugin_api (GST_TYPE_QRCODE_QUALITY, 0);
328 /* initialize the new element
329 * initialize instance structure
332 gst_base_qr_overlay_init (GstBaseQROverlay * self)
334 GstBaseQROverlayPrivate *priv = PRIV (self);
336 priv->x_percent = 50.0;
337 priv->y_percent = 50.0;
338 priv->qrcode_quality = DEFAULT_PROP_QUALITY;
339 priv->case_sensitive = DEFAULT_PROP_CASE_SENSITIVE;
340 priv->span_frame = 0;
341 priv->qrcode_size = DEFAULT_PROP_PIXEL_SIZE;
342 priv->overlaycomposition =
343 gst_element_factory_make ("overlaycomposition", NULL);
344 gst_video_info_init (&priv->info);
346 if (priv->overlaycomposition) {
347 GstPadTemplate *sink_tmpl = gst_static_pad_template_get (&sink_template);
348 GstPadTemplate *src_tmpl = gst_static_pad_template_get (&src_template);
350 gst_bin_add (GST_BIN (self), priv->overlaycomposition);
352 gst_element_add_pad (GST_ELEMENT_CAST (self),
353 gst_ghost_pad_new_from_template ("sink",
354 priv->overlaycomposition->sinkpads->data, sink_tmpl));
355 gst_element_add_pad (GST_ELEMENT_CAST (self),
356 gst_ghost_pad_new_from_template ("src",
357 priv->overlaycomposition->srcpads->data, src_tmpl));
358 gst_object_unref (sink_tmpl);
359 gst_object_unref (src_tmpl);
361 g_signal_connect_swapped (priv->overlaycomposition, "draw",
362 G_CALLBACK (gst_base_qr_overlay_draw_cb), self);
363 g_signal_connect_swapped (priv->overlaycomposition, "caps-changed",
364 G_CALLBACK (gst_base_qr_overlay_caps_changed_cb), self);
369 gst_base_qr_overlay_set_property (GObject * object, guint prop_id,
370 const GValue * value, GParamSpec * pspec)
372 GstBaseQROverlayPrivate *priv = PRIV (object);
376 priv->x_percent = g_value_get_float (value);
379 priv->y_percent = g_value_get_float (value);
381 case PROP_PIXEL_SIZE:
382 priv->qrcode_size = g_value_get_float (value);
384 case PROP_QRCODE_ERROR_CORRECTION:
385 priv->qrcode_quality = g_value_get_enum (value);
387 case PROP_CASE_SENSITIVE:
388 priv->case_sensitive = g_value_get_boolean (value);
391 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397 gst_base_qr_overlay_get_property (GObject * object, guint prop_id,
398 GValue * value, GParamSpec * pspec)
400 GstBaseQROverlayPrivate *priv = PRIV (object);
404 g_value_set_float (value, priv->x_percent);
407 g_value_set_float (value, priv->y_percent);
409 case PROP_PIXEL_SIZE:
410 g_value_set_float (value, priv->qrcode_size);
412 case PROP_QRCODE_ERROR_CORRECTION:
413 g_value_set_enum (value, priv->qrcode_quality);
415 case PROP_CASE_SENSITIVE:
416 g_value_set_boolean (value, priv->case_sensitive);
419 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);