• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2014 Tim-Philipp Müller <tim centricular com>
3  * Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <gst/gst.h>
25 #include <gst/video/video.h>
26 
27 #include <math.h>
28 
29 #include <gio/gio.h>
30 #include <stdlib.h>
31 
32 #define VIDEO_WIDTH 720
33 #define VIDEO_HEIGHT 480
34 #define VIDEO_FPS 50
35 
36 /* GdkPixbuf RGBA C-Source image dump from gdk-pixbuf-csource --raw,
37  * gzipped and then base64 encoded */
38 const gchar gzipped_pixdata_base64[] =
39     "H4sICPX/Z1QAA2xvZ28ucGl4AO2dsZHrNhCG+ewK2II64ClyrhmnTtSBh4kLUOLQAUuwEhSgFtiA"
40     "A7agwA2wBT5AXJ5w4P5LgKLEO97ezDf2SCAIAftjFwuQ7/c///ojy/77+8eP7Jcs+/WfLMv+t/zW"
41     "dV32pSmK3HK6sXZbFOXV9PZfWipLbWksHdHQZxVpZP+Ee7u62/d7rt0fivIqimJnOX+w/zhauq68"
42     "aWjevfdUR1h3vXq/KMqzufueFN1J1FE+stf8KfCzobZ3q/ePojyT3v8gDSyB09GFND7g/N014rpl"
43     "41xF+Wz0/i817nwFjepP+Rb0PnDwOUOehePyQq1eurlrSkXZOkVx7OblbGK43upf+zcqyldhOT22"
44     "5GvV9ynKXIriQDpKye242Ldcve2KskX6deaRWVeebnpdu32Koryc4t/iaKksmm9Vvg3W3neWk+Vs"
45     "qS2tpSOu9NmZtLG4f7R15paS7jXct1q7XxTl2ZDd157dp1A/6q9I+1Wg+QH1g8pmcb7M0szUHseV"
46     "/KTTtJg39XyudH/NASmbhfzOUtqTNHmhew1cglhT9ad8O16kv7m4eFT3/pVNQ77IX8shLgW/RnsW"
47     "Li7V5y4UJaDo9wnOT9Sjq1fzn4oSQXHft4tZ00Vpr5jI3yiKwmO1sy/63GZKzNpS+dVyLsaYneVk"
48     "Ud+rbIqi32M/kC7DtaXzn6vt9Vm95ZbS0lg64rx2nynKdyHQ3oC+A06ZxdtbkztWundpqS1fKo5j"
49     "9OfQ8+jKJNbW987eye47QGO5WE6u/BPakJP2rt4927X7JhanNaBBzQUpEMbmU2hJk66O2ftt7lrS"
50     "f8vc4yl+0P7llspyWapOl3th9Hdde4yVz4m17eMD2pP8ZBXjI0l3J7oG1Rf1DlH7dyBOpKuB2+dB"
51     "2ZzKXS3ugw9+lnKZB49oH2bL1owGH9K4vX5P7Yg+0+O1/Uj/XSRm8ep9Ws6LclrRfe+N1+xYIxjz"
52     "49Jrh6D+m68inSypPeQja7pXRZp3/z1P6M7XM+xX+7eznElHc3E63Hv2xWmoo89H4yLEnpDg+orq"
53     "DsnJFq7oWqYtLhd7sbTg3i3dL2VO2Xlt5Oo7DzY1Uc8VtOng9eMZtP0ctpnmJa787Tcm/jbUtmHc"
54     "xfmG+p0bwz0R9t1hYr33WTgj/ZEfe1R7HfnJ3NNCjIbKoP8f1SBn2w2Nq3htYI9o7uBopnRI9nmO"
55     "rM/ZveijhWulee+DFrx2xZSHe0Gmn99ix5sd96A+1E97w88pB8bX1ABubfYK4DxG+msYPbWWksqU"
56     "gu5a0u/O68PQ3t99hWF8kW9vC2gQaWTyWtD2sB5UVzOhGeRLJR2y/pB0k9RHgCqxXaP2kP5Qn9TU"
57     "n+He7rumwO/j5oSr0FanQZf/jIp33+650qXXjSj2FP2+y58AbYVrPs5PjuyOxsTvq5H+yYb8Mofg"
58     "u8rgmKb2vq/C+lPtMLj2KOjBbyPSqTS3N9T2Y/D5SbAtdt1r4uapoZ9ife+VroHzlQn2ZCf0dw7K"
59     "7rk2Cn2VMo6z15pP1KPzt5N72LT+4/Q36hv7d+R8INN/oR2zdunZEcyvANuEPj3SNlkNmvHcIY6x"
60     "4eeHWbkiSfugvBT3jeLYifKdCdaHggbC+Q7Vi+YOriznW5PG8BENAj3G5FYQTsslWvcxuqqABke6"
61     "oXzoqGyEfcC1kunnXRRvcfOm2N9mOo4szX1df/DnB4P9BbsOMmANNXf8F9TgaB1p5LlppBehfr+/"
62     "0PiwuhJ+Y+hbpXpbGsNbLtTcc66LPzP31u+rDznPesJPDnnS5Ny2oMGRnSdoEOVERjpE2qTvTmAc"
63     "pGuQ7Yg5EyP7QDY3gjQozREzNIhiNXTv1Ni1BeOCyvvxOJqzUJtzUD6MWaX1wKd4P9HbgmfdhFwL"
64     "p0FOr9x6EOngmtKHpt8TGNUxcQ2yTXGeNLL/lHz4Qxo09/0tNN8gH4zainIcyK7ZuF7Q4D6iDal1"
65     "1kE5ZD+bfD+DsB48BuXy7L7//mEvgulrKWd3iyUi7ZPzS+J6C1wz+XyFkfMWB0D02tHrl2FfMDYX"
66     "yfUvitXg2QvBrlHMyM4JEW3oqC+5/orymwbEUWtr5ck65PKidVCGy4leh71AZgwlv9JN6VDQsZSP"
67     "QddM+iUw7nM5BHWn7jWKbRf6VsrJcjEFtGug2dr7HvntOYQa5OanTT8jY//2tMcXauxCuVBOo+9n"
68     "YYRxnNIhvN7g2AnGI+iamD4w6bnwqN810QctaUM6V8LlDJFPk9a7qfllzmdVEW2Yg18vmkc/xTrw"
69     "yTqU9uDD/fgK+b9EHcK1nTDGcF0Hrok6Gyu00c33KBZlifztYY6fPbcG2poUqwl2LeWXuXv4OVEp"
70     "95XUX/6Yoj5bWx8v0F8OfN3g72rS3TFWewk6RHuHXOwkjoXhfVnsOcdFc5xUJ/Jto7gKlEX5xUlN"
71     "x/T/RNvFvhA0GDXnCfedPY9+ZUhjvu4Wfy5P0CHKoXGaEMci1TYj7sfqJbI+uBfwSNuFelPPXkp+"
72     "k823BGXQWuGh3AmYRzf9zpKMOfvyYB8uEiuC8ZXO00zuZ020G83rs8Y/xU8gmwcaRPVKa+uk/LLh"
73     "8y1NUEbKfc+aww3eP/xS73xIBez5XbKPzwuW3H4h6Ec3j6E9ba6PkR9MGgvBNmPbLeXa58TfKRqM"
74     "brtJPJuTom/vGm4dwJ2lQXmsWe/cM4l7mFtBOCcjPR+B9iMGW4LnGWLHa4bdzNqbj6wj2QYEXYX+"
75     "JPWMKlcOvpdEaIcUu0blUA1eX7Bnbx7os02/N4iLRSMY7QuacWwSc24YjhXQgxQ/sT4s0QbQs2kO"
76     "dl1o+hiYs09p/8x/7kPaqw/3GZGfkPbmka9C5wiQ30RzJdp3R2cTh3MKozk6ta1bgfYGUzU42sMX"
77     "xsP1q4ttuLxfci7Ps18/RwfPI6f2x4QOG087/m8arRlN2vN9yP+G5yiTcpETbUBxCpo7kGZzof3D"
78     "OwGG56ca7/NwDkdrwc1qUNiXT4Hrx9hn1WLOj0n7wDF7Vcka9Gw39lyL9Gz51P78ebAv1G9BfWiv"
79     "A2kQ+mKhzbP2aEz8s8DsmWGDfbxjc/+OENDfib4b3uNUejmZqGd8g/5EY3k1CWt2cz9nONR380XM"
80     "+HPvHlnivU/ce19aA96FA/TsP2984q6jcpeg/f75kTyoxwfFiSW6ZkJLHDHvt8np93Hv3xliCLT2"
81     "OIEx3OTeILMf2EztCYKYNcYG2fMQivKdYbQUpY1s/M6ZTcbpivJMMv5Zpdhn7mdpV1GUO1l/NjTU"
82     "Usx7Z8Iz3ZuM0xXlFWT8s4CsDkmzJZPD0ThUUWaSye8THZ6RqOj/uf2L1f79T0XZCpn8vJJ0Pkb9"
83     "n6IsCOVoBp/HvS+moe83+T4dRVEUZRmyn2F9swl9yAAA";
84 
85 static GstBuffer *logo_buf;
86 
87 static GMainLoop *main_loop;
88 static gint count;
89 
90 static GstBuffer *
create_overlay_buffer(void)91 create_overlay_buffer (void)
92 {
93   GZlibDecompressor *decompress;
94   GConverterResult decomp_res;
95   guchar *gzipped_pixdata, *pixdata;
96   gsize gzipped_size, bytes_read, pixdata_size;
97   GstBuffer *logo_pixels;
98   guint w, h, stride;
99 
100   gzipped_pixdata = g_base64_decode (gzipped_pixdata_base64, &gzipped_size);
101   g_assert (gzipped_pixdata != NULL);
102 
103   pixdata = g_malloc (64 * 1024);
104 
105   decompress = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
106   decomp_res = g_converter_convert (G_CONVERTER (decompress),
107       gzipped_pixdata, gzipped_size, pixdata, 64 * 1024,
108       G_CONVERTER_INPUT_AT_END, &bytes_read, &pixdata_size, NULL);
109   g_assert (decomp_res == G_CONVERTER_FINISHED);
110   g_assert (bytes_read == gzipped_size);
111   g_free (gzipped_pixdata);
112   g_object_unref (decompress);
113 
114   /* 0: Pixbuf magic (0x47646b50) */
115   g_assert (GST_READ_UINT32_BE (pixdata) == 0x47646b50);
116 
117   /* 4: length incl. header */
118   /* 8: pixdata_type */
119   /* 12: rowstride (900) */
120   stride = GST_READ_UINT32_BE (pixdata + 12);
121   /* 16: width (225) */
122   w = GST_READ_UINT32_BE (pixdata + 16);
123   /* 20: height (57) */
124   h = GST_READ_UINT32_BE (pixdata + 20);
125   /* 24: pixel_data */
126   GST_LOG ("%dx%d @ %d", w, h, stride);
127   /* we assume that the last line also has padding at the end */
128   g_assert (pixdata_size - 24 >= h * stride);
129 
130   logo_pixels = gst_buffer_new_and_alloc (h * stride);
131   gst_buffer_fill (logo_pixels, 0, pixdata + 24, h * stride);
132   gst_buffer_add_video_meta (logo_pixels, GST_VIDEO_FRAME_FLAG_NONE,
133       GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB, w, h);
134 
135   g_free (pixdata);
136 
137   return logo_pixels;
138 }
139 
140 static gboolean
message_cb(GstBus * bus,GstMessage * message,gpointer user_data)141 message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
142 {
143   switch (GST_MESSAGE_TYPE (message)) {
144     case GST_MESSAGE_ERROR:{
145       GError *err = NULL;
146       gchar *name, *debug = NULL;
147 
148       name = gst_object_get_path_string (message->src);
149       gst_message_parse_error (message, &err, &debug);
150 
151       g_printerr ("ERROR: from element %s: %s\n", name, err->message);
152       if (debug != NULL)
153         g_printerr ("Additional debug info:\n%s\n", debug);
154 
155       g_error_free (err);
156       g_free (debug);
157       g_free (name);
158 
159       g_main_loop_quit (main_loop);
160       break;
161     }
162     case GST_MESSAGE_WARNING:{
163       GError *err = NULL;
164       gchar *name, *debug = NULL;
165 
166       name = gst_object_get_path_string (message->src);
167       gst_message_parse_warning (message, &err, &debug);
168 
169       g_printerr ("ERROR: from element %s: %s\n", name, err->message);
170       if (debug != NULL)
171         g_printerr ("Additional debug info:\n%s\n", debug);
172 
173       g_error_free (err);
174       g_free (debug);
175       g_free (name);
176       break;
177     }
178     case GST_MESSAGE_EOS:
179       g_print ("Got EOS\n");
180       g_main_loop_quit (main_loop);
181       break;
182     default:
183       break;
184   }
185 
186   return TRUE;
187 }
188 
189 typedef struct
190 {
191   gboolean valid;
192   GstVideoInfo info;
193 } OverlayState;
194 
195 static void
prepare_overlay(GstElement * overlay,GstCaps * caps,gint window_width,gint window_height,gpointer user_data)196 prepare_overlay (GstElement * overlay, GstCaps * caps, gint window_width,
197     gint window_height, gpointer user_data)
198 {
199   OverlayState *s = (OverlayState *) user_data;
200 
201   if (gst_video_info_from_caps (&s->info, caps))
202     s->valid = TRUE;
203   else
204     s->valid = FALSE;
205 }
206 
207 #define SPEED_SCALE_FACTOR (VIDEO_FPS * 4)
208 
209 /* nicked from videotestsrc's ball pattern renderer */
210 static void
calculate_position(gint * x,gint * y,guint logo_w,guint logo_h,guint n)211 calculate_position (gint * x, gint * y, guint logo_w, guint logo_h, guint n)
212 {
213   guint r_x = logo_w / 2;
214   guint r_y = logo_h / 2;
215   guint w = VIDEO_WIDTH + logo_w;
216   guint h = VIDEO_HEIGHT + logo_h;
217 
218   *x = r_x + (0.5 + 0.5 * sin (2 * G_PI * n / SPEED_SCALE_FACTOR))
219       * (w - 2 * r_x);
220   *y = r_y + (0.5 + 0.5 * sin (2 * G_PI * sqrt (2) * n / SPEED_SCALE_FACTOR))
221       * (h - 2 * r_y);
222 
223   *x -= logo_w;
224   *y -= logo_h;
225 }
226 
227 static GstVideoOverlayComposition *
draw_overlay(GstElement * overlay,GstSample * sample,gpointer user_data)228 draw_overlay (GstElement * overlay, GstSample * sample, gpointer user_data)
229 {
230   OverlayState *s = (OverlayState *) user_data;
231   GstVideoOverlayRectangle *rect;
232   GstVideoOverlayComposition *comp;
233   GstVideoMeta *vmeta;
234   gint x, y;
235 
236   if (!s->valid)
237     return NULL;
238 
239   vmeta = gst_buffer_get_video_meta (logo_buf);
240   calculate_position (&x, &y, vmeta->width, vmeta->height, ++count);
241 
242   rect = gst_video_overlay_rectangle_new_raw (logo_buf, x, y,
243       vmeta->width, vmeta->height, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
244   comp = gst_video_overlay_composition_new (rect);
245   gst_video_overlay_rectangle_unref (rect);
246 
247   return comp;
248 }
249 
250 int
main(int argc,char ** argv)251 main (int argc, char **argv)
252 {
253   GstElement *pipeline;
254   GstElement *src, *capsfilter, *overlay, *conv, *sink;
255   GstBus *bus;
256   GstCaps *filter_caps;
257   OverlayState overlay_state = { 0, };
258   GOptionContext *option_ctx;
259   GError *error = NULL;
260   gchar *video_sink = NULL;
261   gboolean ret;
262   GOptionEntry options[] = {
263     {"videosink", 0, 0, G_OPTION_ARG_STRING, &video_sink,
264         "Video sink element to use (default is autovideosink)", NULL},
265     {NULL}
266   };
267 
268   option_ctx = g_option_context_new ("- test overlaycomposition");
269   g_option_context_add_main_entries (option_ctx, options, NULL);
270   g_option_context_add_group (option_ctx, gst_init_get_option_group ());
271   ret = g_option_context_parse (option_ctx, &argc, &argv, &error);
272   g_option_context_free (option_ctx);
273 
274   if (!ret) {
275     g_printerr ("option parsing failed: %s\n", error->message);
276     g_clear_error (&error);
277     exit (1);
278   }
279 
280   pipeline = gst_pipeline_new (NULL);
281   src = gst_element_factory_make ("videotestsrc", NULL);
282   capsfilter = gst_element_factory_make ("capsfilter", NULL);
283   overlay = gst_element_factory_make ("overlaycomposition", NULL);
284   conv = gst_element_factory_make ("videoconvert", NULL);
285 
286   if (!video_sink)
287     video_sink = g_strdup ("autovideosink");
288 
289   sink = gst_element_factory_make (video_sink, NULL);
290   g_free (video_sink);
291 
292   if (!pipeline || !src || !capsfilter || !overlay || !conv || !sink) {
293     g_error ("Failed to create elements");
294     return -1;
295   }
296 
297   gst_bin_add_many (GST_BIN (pipeline), src, capsfilter, overlay, conv, sink,
298       NULL);
299   if (!gst_element_link_many (src, capsfilter, overlay, conv, sink, NULL)) {
300     g_error ("Failed to link elements");
301     return -2;
302   }
303 
304   filter_caps = gst_caps_from_string ("video/x-raw, format = "
305       GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS);
306   gst_caps_set_simple (filter_caps,
307       "width", G_TYPE_INT, VIDEO_WIDTH,
308       "height", G_TYPE_INT, VIDEO_HEIGHT,
309       "framerate", GST_TYPE_FRACTION, VIDEO_FPS, 1, NULL);
310   g_object_set (capsfilter, "caps", filter_caps, NULL);
311   gst_caps_unref (filter_caps);
312 
313   g_signal_connect (overlay, "draw", G_CALLBACK (draw_overlay), &overlay_state);
314   g_signal_connect (overlay, "caps-changed",
315       G_CALLBACK (prepare_overlay), &overlay_state);
316 
317   count = 0;
318   logo_buf = create_overlay_buffer ();
319 
320   main_loop = g_main_loop_new (NULL, FALSE);
321 
322   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
323   gst_bus_add_signal_watch (bus);
324   g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (message_cb), NULL);
325   gst_object_unref (GST_OBJECT (bus));
326 
327   if (gst_element_set_state (pipeline,
328           GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
329     g_error ("Failed to go into PLAYING state");
330     return -3;
331   }
332 
333   g_main_loop_run (main_loop);
334 
335   gst_element_set_state (pipeline, GST_STATE_NULL);
336 
337   g_main_loop_unref (main_loop);
338   gst_object_unref (pipeline);
339 
340   return 0;
341 }
342