debugqroverlay: fix string leak
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / qroverlay / gstbaseqroverlay.c
1 /*
2  * GStreamer
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>
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /**
24  * plugin-qroverlay
25  *
26  * Since: 1.20
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <gst/gst.h>
34 #include <gst/base/gstbasetransform.h>
35 #include <json-glib/json-glib.h>
36
37 #include <qrencode.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #include "gstbaseqroverlay.h"
43
44 GST_DEBUG_CATEGORY_STATIC (gst_base_qr_overlay_debug);
45 #define GST_CAT_DEFAULT gst_base_qr_overlay_debug
46
47 enum
48 {
49   PROP_0,
50   PROP_X_AXIS,
51   PROP_Y_AXIS,
52   PROP_PIXEL_SIZE,
53   PROP_QRCODE_ERROR_CORRECTION,
54   PROP_CASE_SENSITIVE,
55 };
56
57 typedef struct _GstBaseQROverlayPrivate GstBaseQROverlayPrivate;
58 struct _GstBaseQROverlayPrivate
59 {
60   gfloat qrcode_size;
61   guint qrcode_quality;
62   guint span_frame;
63   QRecLevel level;
64   gfloat x_percent;
65   gfloat y_percent;
66   GstElement *overlaycomposition;
67   GstVideoInfo info;
68   gboolean valid;
69   gboolean case_sensitive;
70
71   GstPad *sinkpad, *srcpad;
72   GstVideoOverlayComposition *prev_overlay;
73 };
74
75 #define PRIV(s) gst_base_qr_overlay_get_instance_private (GST_BASE_QR_OVERLAY (s))
76
77 #define OVERLAY_COMPOSITION_CAPS GST_VIDEO_CAPS_MAKE (GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS)
78
79 #define ALL_CAPS OVERLAY_COMPOSITION_CAPS ";" \
80     GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
81
82 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
83     GST_PAD_SINK,
84     GST_PAD_ALWAYS,
85     GST_STATIC_CAPS (ALL_CAPS)
86     );
87
88 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
89     GST_PAD_SRC,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS (ALL_CAPS)
92     );
93
94 #define DEFAULT_PROP_QUALITY    1
95 #define DEFAULT_PROP_PIXEL_SIZE    3
96 #define DEFAULT_PROP_CASE_SENSITIVE FALSE
97
98 #define GST_TYPE_QRCODE_QUALITY (gst_qrcode_quality_get_type())
99 static GType
100 gst_qrcode_quality_get_type (void)
101 {
102   static GType qrcode_quality_type = 0;
103
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%"},
109     {0, NULL, NULL},
110   };
111
112   if (!qrcode_quality_type) {
113     qrcode_quality_type =
114         g_enum_register_static ("GstQrcodeOverlayCorrection", qrcode_quality);
115   }
116   return qrcode_quality_type;
117 }
118
119 #define gst_base_qr_overlay_parent_class parent_class
120 G_DEFINE_TYPE_WITH_PRIVATE (GstBaseQROverlay, gst_base_qr_overlay,
121     GST_TYPE_BIN);
122
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);
127
128 static void
129 gst_base_qr_overlay_caps_changed_cb (GstBaseQROverlay * self, GstCaps * caps,
130     gint window_width, gint window_height, GstElement * overlay)
131 {
132   GstBaseQROverlayPrivate *priv = PRIV (self);
133
134   if (gst_video_info_from_caps (&priv->info, caps))
135     priv->valid = TRUE;
136   else
137     priv->valid = FALSE;
138 }
139
140 static GstVideoOverlayComposition *
141 draw_overlay (GstBaseQROverlay * self, QRcode * qrcode)
142 {
143   guint8 *qr_data, *pixels;
144   gint stride, pstride, y, x, yy, square_size;
145   gsize offset, line_offset;
146   GstVideoInfo info;
147   GstVideoOverlayRectangle *rect;
148   GstVideoOverlayComposition *comp;
149   GstBuffer *buf;
150   GstBaseQROverlayPrivate *priv = PRIV (self);
151
152   gst_video_info_init (&info);
153
154   square_size = (qrcode->width + 4 * 2) * priv->qrcode_size;
155   gst_video_info_set_format (&info, GST_VIDEO_FORMAT_ARGB, square_size,
156       square_size);
157
158   pixels = g_malloc ((size_t) info.size);
159   stride = info.stride[0];
160   pstride = info.finfo->pixel_stride[0];
161
162   /* White background */
163   for (y = 0; y < info.height; y++)
164     memset (&pixels[y * stride], 0xff, stride);
165
166   /* Draw the black QR code blocks with 4px white space around it
167    * on top */
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) {
173         if (!(*qr_data & 1))
174           continue;
175
176         offset =
177             (((line_offset + (stride * (yy / pstride))) +
178                 x * priv->qrcode_size * pstride)) +
179             (priv->qrcode_size * pstride) + (4 * priv->qrcode_size * pstride);
180
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;
185         }
186       }
187       qr_data++;
188     }
189     line_offset += (stride * priv->qrcode_size);
190   }
191
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);
195
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);
200
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);
205
206   return comp;
207 }
208
209 static GstVideoOverlayComposition *
210 gst_base_qr_overlay_draw_cb (GstBaseQROverlay * self, GstSample * sample,
211     GstElement * _)
212 {
213   GstBaseQROverlayPrivate *priv = PRIV (self);
214   QRcode *qrcode;
215   gchar *content;
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));
222
223   if (!priv->valid) {
224     GST_ERROR_OBJECT (self, "Trying to draw before negotiation?");
225
226     return NULL;
227   }
228
229   if (GST_CLOCK_TIME_IS_VALID (rtime))
230     gst_object_sync_values (GST_OBJECT (self), rtime);
231
232   content =
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);
239     qrcode =
240         QRcode_encodeString (content, 0, priv->qrcode_quality, QR_MODE_8,
241         priv->case_sensitive);
242
243     if (qrcode) {
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);
248     } else {
249       GST_WARNING_OBJECT (self, "Could not encode content: %s", content);
250     }
251   }
252   g_free (content);
253
254   return overlay;
255 }
256
257 /* GObject vmethod implementations */
258
259 static void
260 gst_base_qr_overlay_dispose (GObject * object)
261 {
262   GstBaseQROverlayPrivate *priv = PRIV (object);
263
264   gst_mini_object_replace (((GstMiniObject **) & priv->prev_overlay), NULL);
265 }
266
267 /* initialize the qroverlay's class */
268 static void
269 gst_base_qr_overlay_class_init (GstBaseQROverlayClass * klass)
270 {
271   GObjectClass *gobject_class;
272   GstElementClass *gstelement_class;
273
274   gobject_class = (GObjectClass *) klass;
275   gstelement_class = (GstElementClass *) klass;
276
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;
280
281   GST_DEBUG_CATEGORY_INIT (gst_base_qr_overlay_debug, "qroverlay", 0,
282       "Qrcode overlay base class");
283
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));
289
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));
295
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));
300
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));
305
306   /**
307    * GstBaseQROverlay::case-sensitive:
308    *
309    * Strings to encode are case sensitive (e.g. passwords or SSIDs).
310    *
311    * Since: 1.22
312    */
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));
318
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));
323
324   gst_type_mark_as_plugin_api (GST_TYPE_QRCODE_QUALITY, 0);
325   gst_type_mark_as_plugin_api (GST_TYPE_QRCODE_QUALITY, 0);
326 }
327
328 /* initialize the new element
329  * initialize instance structure
330  */
331 static void
332 gst_base_qr_overlay_init (GstBaseQROverlay * self)
333 {
334   GstBaseQROverlayPrivate *priv = PRIV (self);
335
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);
345
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);
349
350     gst_bin_add (GST_BIN (self), priv->overlaycomposition);
351
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);
360
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);
365   }
366 }
367
368 static void
369 gst_base_qr_overlay_set_property (GObject * object, guint prop_id,
370     const GValue * value, GParamSpec * pspec)
371 {
372   GstBaseQROverlayPrivate *priv = PRIV (object);
373
374   switch (prop_id) {
375     case PROP_X_AXIS:
376       priv->x_percent = g_value_get_float (value);
377       break;
378     case PROP_Y_AXIS:
379       priv->y_percent = g_value_get_float (value);
380       break;
381     case PROP_PIXEL_SIZE:
382       priv->qrcode_size = g_value_get_float (value);
383       break;
384     case PROP_QRCODE_ERROR_CORRECTION:
385       priv->qrcode_quality = g_value_get_enum (value);
386       break;
387     case PROP_CASE_SENSITIVE:
388       priv->case_sensitive = g_value_get_boolean (value);
389       break;
390     default:
391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392       break;
393   }
394 }
395
396 static void
397 gst_base_qr_overlay_get_property (GObject * object, guint prop_id,
398     GValue * value, GParamSpec * pspec)
399 {
400   GstBaseQROverlayPrivate *priv = PRIV (object);
401
402   switch (prop_id) {
403     case PROP_X_AXIS:
404       g_value_set_float (value, priv->x_percent);
405       break;
406     case PROP_Y_AXIS:
407       g_value_set_float (value, priv->y_percent);
408       break;
409     case PROP_PIXEL_SIZE:
410       g_value_set_float (value, priv->qrcode_size);
411       break;
412     case PROP_QRCODE_ERROR_CORRECTION:
413       g_value_set_enum (value, priv->qrcode_quality);
414       break;
415     case PROP_CASE_SENSITIVE:
416       g_value_set_boolean (value, priv->case_sensitive);
417       break;
418     default:
419       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
420       break;
421   }
422 }