1 /* GStreamer gdkpixbufoverlay test app
2  * Copyright (C) 2014 Tim-Philipp Müller <tim centricular 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 St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <gio/gio.h>
25 #include <gst/gst.h>
26 #include <gst/video/video.h>
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28 
29 #include <math.h>
30 
31 #define VIDEO_WIDTH 720
32 #define VIDEO_HEIGHT 480
33 #define VIDEO_FPS 50
34 
35 /* GdkPixbuf RGBA C-Source image dump from gdk-pixbuf-csource --raw,
36  * gzipped and then base64 encoded */
37 const gchar gzipped_pixdata_base64[] =
38     "H4sICPX/Z1QAA2xvZ28ucGl4AO2dsZHrNhCG+ewK2II64ClyrhmnTtSBh4kLUOLQAUuwEhSgFtiA"
39     "A7agwA2wBT5AXJ5w4P5LgKLEO97ezDf2SCAIAftjFwuQ7/c///ojy/77+8eP7Jcs+/WfLMv+t/zW"
40     "dV32pSmK3HK6sXZbFOXV9PZfWipLbWksHdHQZxVpZP+Ee7u62/d7rt0fivIqimJnOX+w/zhauq68"
41     "aWjevfdUR1h3vXq/KMqzufueFN1J1FE+stf8KfCzobZ3q/ePojyT3v8gDSyB09GFND7g/N014rpl"
42     "41xF+Wz0/i817nwFjepP+Rb0PnDwOUOehePyQq1eurlrSkXZOkVx7OblbGK43upf+zcqyldhOT22"
43     "5GvV9ynKXIriQDpKye242Ldcve2KskX6deaRWVeebnpdu32Koryc4t/iaKksmm9Vvg3W3neWk+Vs"
44     "qS2tpSOu9NmZtLG4f7R15paS7jXct1q7XxTl2ZDd157dp1A/6q9I+1Wg+QH1g8pmcb7M0szUHseV"
45     "/KTTtJg39XyudH/NASmbhfzOUtqTNHmhew1cglhT9ad8O16kv7m4eFT3/pVNQ77IX8shLgW/RnsW"
46     "Li7V5y4UJaDo9wnOT9Sjq1fzn4oSQXHft4tZ00Vpr5jI3yiKwmO1sy/63GZKzNpS+dVyLsaYneVk"
47     "Ud+rbIqi32M/kC7DtaXzn6vt9Vm95ZbS0lg64rx2nynKdyHQ3oC+A06ZxdtbkztWundpqS1fKo5j"
48     "9OfQ8+jKJNbW987eye47QGO5WE6u/BPakJP2rt4927X7JhanNaBBzQUpEMbmU2hJk66O2ftt7lrS"
49     "f8vc4yl+0P7llspyWapOl3th9Hdde4yVz4m17eMD2pP8ZBXjI0l3J7oG1Rf1DlH7dyBOpKuB2+dB"
50     "2ZzKXS3ugw9+lnKZB49oH2bL1owGH9K4vX5P7Yg+0+O1/Uj/XSRm8ep9Ws6LclrRfe+N1+xYIxjz"
51     "49Jrh6D+m68inSypPeQja7pXRZp3/z1P6M7XM+xX+7eznElHc3E63Hv2xWmoo89H4yLEnpDg+orq"
52     "DsnJFq7oWqYtLhd7sbTg3i3dL2VO2Xlt5Oo7DzY1Uc8VtOng9eMZtP0ctpnmJa787Tcm/jbUtmHc"
53     "xfmG+p0bwz0R9t1hYr33WTgj/ZEfe1R7HfnJ3NNCjIbKoP8f1SBn2w2Nq3htYI9o7uBopnRI9nmO"
54     "rM/ZveijhWulee+DFrx2xZSHe0Gmn99ix5sd96A+1E97w88pB8bX1ABubfYK4DxG+msYPbWWksqU"
55     "gu5a0u/O68PQ3t99hWF8kW9vC2gQaWTyWtD2sB5UVzOhGeRLJR2y/pB0k9RHgCqxXaP2kP5Qn9TU"
56     "n+He7rumwO/j5oSr0FanQZf/jIp33+650qXXjSj2FP2+y58AbYVrPs5PjuyOxsTvq5H+yYb8Mofg"
57     "u8rgmKb2vq/C+lPtMLj2KOjBbyPSqTS3N9T2Y/D5SbAtdt1r4uapoZ9ife+VroHzlQn2ZCf0dw7K"
58     "7rk2Cn2VMo6z15pP1KPzt5N72LT+4/Q36hv7d+R8INN/oR2zdunZEcyvANuEPj3SNlkNmvHcIY6x"
59     "4eeHWbkiSfugvBT3jeLYifKdCdaHggbC+Q7Vi+YOriznW5PG8BENAj3G5FYQTsslWvcxuqqABke6"
60     "oXzoqGyEfcC1kunnXRRvcfOm2N9mOo4szX1df/DnB4P9BbsOMmANNXf8F9TgaB1p5LlppBehfr+/"
61     "0PiwuhJ+Y+hbpXpbGsNbLtTcc66LPzP31u+rDznPesJPDnnS5Ny2oMGRnSdoEOVERjpE2qTvTmAc"
62     "pGuQ7Yg5EyP7QDY3gjQozREzNIhiNXTv1Ni1BeOCyvvxOJqzUJtzUD6MWaX1wKd4P9HbgmfdhFwL"
63     "p0FOr9x6EOngmtKHpt8TGNUxcQ2yTXGeNLL/lHz4Qxo09/0tNN8gH4zainIcyK7ZuF7Q4D6iDal1"
64     "1kE5ZD+bfD+DsB48BuXy7L7//mEvgulrKWd3iyUi7ZPzS+J6C1wz+XyFkfMWB0D02tHrl2FfMDYX"
65     "yfUvitXg2QvBrlHMyM4JEW3oqC+5/orymwbEUWtr5ck65PKidVCGy4leh71AZgwlv9JN6VDQsZSP"
66     "QddM+iUw7nM5BHWn7jWKbRf6VsrJcjEFtGug2dr7HvntOYQa5OanTT8jY//2tMcXauxCuVBOo+9n"
67     "YYRxnNIhvN7g2AnGI+iamD4w6bnwqN810QctaUM6V8LlDJFPk9a7qfllzmdVEW2Yg18vmkc/xTrw"
68     "yTqU9uDD/fgK+b9EHcK1nTDGcF0Hrok6Gyu00c33KBZlifztYY6fPbcG2poUqwl2LeWXuXv4OVEp"
69     "95XUX/6Yoj5bWx8v0F8OfN3g72rS3TFWewk6RHuHXOwkjoXhfVnsOcdFc5xUJ/Jto7gKlEX5xUlN"
70     "x/T/RNvFvhA0GDXnCfedPY9+ZUhjvu4Wfy5P0CHKoXGaEMci1TYj7sfqJbI+uBfwSNuFelPPXkp+"
71     "k823BGXQWuGh3AmYRzf9zpKMOfvyYB8uEiuC8ZXO00zuZ020G83rs8Y/xU8gmwcaRPVKa+uk/LLh"
72     "8y1NUEbKfc+aww3eP/xS73xIBez5XbKPzwuW3H4h6Ec3j6E9ba6PkR9MGgvBNmPbLeXa58TfKRqM"
73     "brtJPJuTom/vGm4dwJ2lQXmsWe/cM4l7mFtBOCcjPR+B9iMGW4LnGWLHa4bdzNqbj6wj2QYEXYX+"
74     "JPWMKlcOvpdEaIcUu0blUA1eX7Bnbx7os02/N4iLRSMY7QuacWwSc24YjhXQgxQ/sT4s0QbQs2kO"
75     "dl1o+hiYs09p/8x/7kPaqw/3GZGfkPbmka9C5wiQ30RzJdp3R2cTh3MKozk6ta1bgfYGUzU42sMX"
76     "xsP1q4ttuLxfci7Ps18/RwfPI6f2x4QOG087/m8arRlN2vN9yP+G5yiTcpETbUBxCpo7kGZzof3D"
77     "OwGG56ca7/NwDkdrwc1qUNiXT4Hrx9hn1WLOj0n7wDF7Vcka9Gw39lyL9Gz51P78ebAv1G9BfWiv"
78     "A2kQ+mKhzbP2aEz8s8DsmWGDfbxjc/+OENDfib4b3uNUejmZqGd8g/5EY3k1CWt2cz9nONR380XM"
79     "+HPvHlnivU/ce19aA96FA/TsP2984q6jcpeg/f75kTyoxwfFiSW6ZkJLHDHvt8np93Hv3xliCLT2"
80     "OIEx3OTeILMf2EztCYKYNcYG2fMQivKdYbQUpY1s/M6ZTcbpivJMMv5Zpdhn7mdpV1GUO1l/NjTU"
81     "Usx7Z8Iz3ZuM0xXlFWT8s4CsDkmzJZPD0ThUUWaSye8THZ6RqOj/uf2L1f79T0XZCpn8vJJ0Pkb9"
82     "n6IsCOVoBp/HvS+moe83+T4dRVEUZRmyn2F9swl9yAAA";
83 
84 static GdkPixbuf *logo_pixbuf;
85 
86 static GMainLoop *main_loop;
87 static gint count;
88 
89 static GdkPixbuf *
create_overlay_pixbuf(void)90 create_overlay_pixbuf (void)
91 {
92   GZlibDecompressor *decompress;
93   GConverterResult decomp_res;
94   guchar *gzipped_pixdata, *pixdata, *pixels_copy;
95   gsize gzipped_size, bytes_read, pixdata_size;
96   guint stride, width, height;
97   GdkPixbuf *pixbuf;
98 
99   gzipped_pixdata = g_base64_decode (gzipped_pixdata_base64, &gzipped_size);
100   g_assert (gzipped_pixdata != NULL);
101 
102   pixdata = g_malloc (64 * 1024);
103 
104   decompress = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
105   decomp_res = g_converter_convert (G_CONVERTER (decompress),
106       gzipped_pixdata, gzipped_size, pixdata, 64 * 1024,
107       G_CONVERTER_INPUT_AT_END, &bytes_read, &pixdata_size, NULL);
108   g_assert (decomp_res == G_CONVERTER_FINISHED);
109   g_assert (bytes_read == gzipped_size);
110   g_free (gzipped_pixdata);
111   g_object_unref (decompress);
112 
113   /* 0: Pixbuf magic (0x47646b50) */
114   g_assert (GST_READ_UINT32_BE (pixdata) == 0x47646b50);
115 
116   /* pixdata length */
117   pixdata_size = GST_READ_UINT32_BE (pixdata + 4);
118   g_assert (pixdata_size > 4 + 4 + 4 + 4 + 4 + 4);
119 
120   /* raw, 8-bit depth, RGBA */
121   g_assert (GST_READ_UINT32_BE (pixdata + 8) == 0x01010002);
122 
123   stride = GST_READ_UINT32_BE (pixdata + 12);
124   width = GST_READ_UINT32_BE (pixdata + 16);
125   height = GST_READ_UINT32_BE (pixdata + 20);
126 
127   g_assert (pixdata_size == 24 + height * stride);
128 
129   pixels_copy = g_memdup (pixdata + 24, height * stride);
130 
131   pixbuf =
132       gdk_pixbuf_new_from_data (pixels_copy, GDK_COLORSPACE_RGB, TRUE, 8,
133       width, height, stride, (GdkPixbufDestroyNotify) g_free, pixels_copy);
134 
135   g_assert (pixbuf != NULL);
136 
137   g_free (pixdata);
138 
139   return pixbuf;
140 }
141 
142 static gboolean
bus_cb(GstBus * bus,GstMessage * msg,gpointer user_data)143 bus_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
144 {
145   GMainLoop *loop = user_data;
146 
147   switch (GST_MESSAGE_TYPE (msg)) {
148     case GST_MESSAGE_ERROR:{
149       GError *err = NULL;
150       gchar *dbg;
151 
152       gst_message_parse_error (msg, &err, &dbg);
153       gst_object_default_error (msg->src, err, dbg);
154       g_clear_error (&err);
155       g_free (dbg);
156       g_main_loop_quit (loop);
157       break;
158     }
159     default:
160       break;
161   }
162   return TRUE;
163 }
164 
165 #define SPEED_SCALE_FACTOR (VIDEO_FPS * 4)
166 
167 /* nicked from videotestsrc's ball pattern renderer */
168 static void
calculate_position(gint * x,gint * y,guint logo_w,guint logo_h,guint n)169 calculate_position (gint * x, gint * y, guint logo_w, guint logo_h, guint n)
170 {
171   guint r_x = logo_w / 2;
172   guint r_y = logo_h / 2;
173   guint w = VIDEO_WIDTH + logo_w;
174   guint h = VIDEO_HEIGHT + logo_h;
175 
176   *x = r_x + (0.5 + 0.5 * sin (2 * G_PI * n / SPEED_SCALE_FACTOR))
177       * (w - 2 * r_x);
178   *y = r_y + (0.5 + 0.5 * sin (2 * G_PI * sqrt (2) * n / SPEED_SCALE_FACTOR))
179       * (h - 2 * r_y);
180 
181   *x -= logo_w;
182   *y -= logo_h;
183 }
184 
185 static GstPadProbeReturn
buffer_cb(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)186 buffer_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
187 {
188   GstElement *overlay = GST_PAD_PARENT (pad);
189   gint x, y, w, h;
190 
191   w = gdk_pixbuf_get_width (logo_pixbuf);
192   h = gdk_pixbuf_get_height (logo_pixbuf);
193 
194   calculate_position (&x, &y, w, h, ++count);
195 
196   GST_LOG ("%3d, %3d", x, y);
197 
198   g_object_set (overlay, "offset-x", x, "offset-y", y, NULL);
199 
200   return GST_PAD_PROBE_OK;
201 }
202 
203 int
main(int argc,char ** argv)204 main (int argc, char **argv)
205 {
206   GOptionEntry options[] = {
207     {NULL}
208   };
209   GOptionContext *ctx;
210   GError *err = NULL;
211   GstElement *src, *q, *capsfilter, *overlay, *sink;
212   GstElement *pipeline;
213   GstPad *sink_pad;
214   GstCaps *filter_caps;
215 
216   ctx = g_option_context_new ("");
217   g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
218   g_option_context_add_group (ctx, gst_init_get_option_group ());
219   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
220     g_print ("Error initializing: %s\n", err->message);
221     g_option_context_free (ctx);
222     g_clear_error (&err);
223     return 1;
224   }
225   g_option_context_free (ctx);
226 
227   logo_pixbuf = create_overlay_pixbuf ();
228 
229   main_loop = g_main_loop_new (NULL, FALSE);
230 
231   pipeline = gst_pipeline_new ("pipeline");
232 
233   src = gst_element_factory_make ("videotestsrc", NULL);
234   gst_util_set_object_arg (G_OBJECT (src), "pattern", "white");
235 
236   overlay = gst_element_factory_make ("gdkpixbufoverlay", NULL);
237 
238   /* set positioning-mode to absolute so we can set negative positions */
239   g_object_set (overlay, "pixbuf", logo_pixbuf, "positioning-mode", 1, NULL);
240 
241   sink_pad = gst_element_get_static_pad (overlay, "sink");
242   gst_pad_add_probe (sink_pad, GST_PAD_PROBE_TYPE_BUFFER, buffer_cb, NULL,
243       NULL);
244   gst_object_unref (sink_pad);
245 
246   q = gst_element_factory_make ("queue", NULL);
247 
248   capsfilter = gst_element_factory_make ("capsfilter", NULL);
249   filter_caps = gst_caps_from_string ("video/x-raw, format = "
250       GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS);
251   gst_caps_set_simple (filter_caps,
252       "width", G_TYPE_INT, VIDEO_WIDTH,
253       "height", G_TYPE_INT, VIDEO_HEIGHT,
254       "framerate", GST_TYPE_FRACTION, VIDEO_FPS, 1, NULL);
255   g_object_set (capsfilter, "caps", filter_caps, NULL);
256   gst_caps_unref (filter_caps);
257 
258   sink = gst_element_factory_make ("ximagesink", NULL);
259 
260   gst_bin_add_many (GST_BIN (pipeline), src, q, overlay, capsfilter, sink,
261       NULL);
262 
263   gst_element_link_many (src, q, overlay, capsfilter, sink, NULL);
264 
265   count = 0;
266 
267   gst_element_set_state (pipeline, GST_STATE_PLAYING);
268 
269   gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_cb, main_loop);
270 
271   g_main_loop_run (main_loop);
272 
273   gst_element_set_state (pipeline, GST_STATE_NULL);
274   gst_object_unref (pipeline);
275   g_object_unref (logo_pixbuf);
276 
277   return 0;
278 }
279