1 /* GStreamer GdkPixbuf sink
2 * Copyright (C) 2006-2008 Tim-Philipp Müller <tim centricular net>
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.
9 * This library is free software; you can redistribute it and/or
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.
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-gdkpixbufsink
24 * This sink element takes RGB or RGBA images as input and wraps them into
25 * #GdkPixbuf objects, for easy saving to file via the
26 * GdkPixbuf library API or displaying in Gtk+ applications (e.g. using
27 * the #GtkImage widget).
29 * There are two ways to use this element and obtain the #GdkPixbuf objects
33 * Watching for element messages named <classname>"preroll-pixbuf"
34 * </classname> or <classname>"pixbuf"</classname> on the bus, which
35 * will be posted whenever an image would usually be rendered. See below for
36 * more details on these messages and how to extract the pixbuf object
40 * Retrieving the current pixbuf via the #GstGdkPixbufSink:last-pixbuf property
45 * The primary purpose of this element is to abstract away the #GstBuffer to
46 * #GdkPixbuf conversion. Other than that it's very similar to the fakesink
49 * This element is meant for easy no-hassle video snapshotting. It is not
50 * suitable for video playback or video display at high framerates. Use
51 * ximagesink, xvimagesink or some other suitable video sink in connection
52 * with the #GstXOverlay interface instead if you want to do video playback.
55 * <title>Message details</title>
56 * As mentioned above, this element will by default post element messages
57 * containing structures named <classname>"preroll-pixbuf"
58 * </classname> or <classname>"pixbuf"</classname> on the bus (this
59 * can be disabled by setting the #GstGdkPixbufSink:post-messages property
60 * to #FALSE though). The element message structure has the following fields:
63 * <classname>"pixbuf"</classname>: the #GdkPixbuf object
66 * <classname>"pixel-aspect-ratio"</classname>: the pixel aspect
67 * ratio (PAR) of the input image (this field contains a #GstFraction); the
68 * PAR is usually 1:1 for images, but is often something non-1:1 in the case
69 * of video input. In this case the image may be distorted and you may need
70 * to rescale it accordingly before saving it to file or displaying it. This
71 * can easily be done using gdk_pixbuf_scale() (the reason this is not done
72 * automatically is that the application will often scale the image anyway
73 * according to the size of the output window, in which case it is much more
74 * efficient to only scale once rather than twice). You can put a videoscale
75 * element and a capsfilter element with
76 * <literal>video/x-raw-rgb,pixel-aspect-ratio=(fraction)1/1</literal> caps
77 * in front of this element to make sure the pixbufs always have a 1:1 PAR.
83 * <title>Example pipeline</title>
85 * gst-launch -m -v videotestsrc num-buffers=1 ! gdkpixbufsink
86 * ]| Process one single test image as pixbuf (note that the output you see will
87 * be slightly misleading. The message structure does contain a valid pixbuf
88 * object even if the structure string says '(NULL)').
96 #include "gstgdkpixbufsink.h"
98 #include <gst/video/video.h>
100 #define DEFAULT_SEND_MESSAGES TRUE
101 #define DEFAULT_POST_MESSAGES TRUE
113 GST_BOILERPLATE (GstGdkPixbufSink, gst_gdk_pixbuf_sink, GstVideoSink,
114 GST_TYPE_VIDEO_SINK);
116 static void gst_gdk_pixbuf_sink_set_property (GObject * object, guint prop_id,
117 const GValue * value, GParamSpec * pspec);
118 static void gst_gdk_pixbuf_sink_get_property (GObject * object, guint prop_id,
119 GValue * value, GParamSpec * pspec);
121 static gboolean gst_gdk_pixbuf_sink_start (GstBaseSink * basesink);
122 static gboolean gst_gdk_pixbuf_sink_stop (GstBaseSink * basesink);
123 static gboolean gst_gdk_pixbuf_sink_set_caps (GstBaseSink * basesink,
125 static GstFlowReturn gst_gdk_pixbuf_sink_render (GstBaseSink * bsink,
127 static GstFlowReturn gst_gdk_pixbuf_sink_preroll (GstBaseSink * bsink,
129 static GdkPixbuf *gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (GstGdkPixbufSink *
130 sink, GstBuffer * buf);
132 #define WxH ", width = (int) [ 16, 4096 ], height = (int) [ 16, 4096 ]"
134 static GstStaticPadTemplate pixbufsink_sink_factory =
135 GST_STATIC_PAD_TEMPLATE ("sink",
138 GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB WxH ";" GST_VIDEO_CAPS_RGBA WxH));
141 gst_gdk_pixbuf_sink_base_init (gpointer g_class)
143 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
145 gst_element_class_set_details_simple (element_class, "GdkPixbuf sink",
146 "Sink/Video", "Output images as GdkPixbuf objects in bus messages",
147 "Tim-Philipp Müller <tim centricular net>");
149 gst_element_class_add_static_pad_template (element_class,
150 &pixbufsink_sink_factory);
154 gst_gdk_pixbuf_sink_class_init (GstGdkPixbufSinkClass * klass)
156 GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
157 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
159 gobject_class->set_property = gst_gdk_pixbuf_sink_set_property;
160 gobject_class->get_property = gst_gdk_pixbuf_sink_get_property;
162 /* FIXME 0.11, remove in favour of post-messages */
163 g_object_class_install_property (gobject_class, PROP_SEND_MESSAGES,
164 g_param_spec_boolean ("send-messages", "Send Messages",
165 "Whether to post messages containing pixbufs on the bus "
166 " (deprecated, use post-messages)",
167 DEFAULT_SEND_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
169 * GstGdkPixbuf:post-messages:
171 * Post messages on the bus containing pixbufs.
175 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
176 g_param_spec_boolean ("post-messages", "Post Messages",
177 "Whether to post messages containing pixbufs on the bus",
178 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180 g_object_class_install_property (gobject_class, PROP_LAST_PIXBUF,
181 g_param_spec_object ("last-pixbuf", "Last Pixbuf",
182 "Last GdkPixbuf object rendered", GDK_TYPE_PIXBUF,
183 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
185 basesink_class->start = GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_start);
186 basesink_class->stop = GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_stop);
187 basesink_class->render = GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_render);
188 basesink_class->preroll = GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_preroll);
189 basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_gdk_pixbuf_sink_set_caps);
193 gst_gdk_pixbuf_sink_init (GstGdkPixbufSink * sink,
194 GstGdkPixbufSinkClass * klass)
198 sink->has_alpha = FALSE;
199 sink->last_pixbuf = NULL;
200 sink->post_messages = DEFAULT_POST_MESSAGES;
202 /* we're not a real video sink, we just derive from GstVideoSink in case
203 * anything interesting is added to it in future */
204 gst_base_sink_set_max_lateness (GST_BASE_SINK (sink), -1);
205 gst_base_sink_set_qos_enabled (GST_BASE_SINK (sink), FALSE);
210 gst_gdk_pixbuf_sink_start (GstBaseSink * basesink)
212 GST_LOG_OBJECT (basesink, "start");
218 gst_gdk_pixbuf_sink_stop (GstBaseSink * basesink)
220 GstGdkPixbufSink *sink = GST_GDK_PIXBUF_SINK (basesink);
222 GST_VIDEO_SINK_WIDTH (sink) = 0;
223 GST_VIDEO_SINK_HEIGHT (sink) = 0;
227 sink->has_alpha = FALSE;
229 if (sink->last_pixbuf) {
230 g_object_unref (sink->last_pixbuf);
231 sink->last_pixbuf = NULL;
234 GST_LOG_OBJECT (sink, "stop");
240 gst_gdk_pixbuf_sink_set_caps (GstBaseSink * basesink, GstCaps * caps)
242 GstGdkPixbufSink *sink = GST_GDK_PIXBUF_SINK (basesink);
244 gint w, h, par_n, par_d;
246 GST_LOG_OBJECT (sink, "caps: %" GST_PTR_FORMAT, caps);
248 if (!gst_video_format_parse_caps (caps, &fmt, &w, &h)) {
249 GST_WARNING_OBJECT (sink, "parse_caps failed");
253 if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) {
254 GST_LOG_OBJECT (sink, "no pixel aspect ratio");
258 g_assert ((fmt == GST_VIDEO_FORMAT_RGB &&
259 gst_video_format_get_pixel_stride (fmt, 0) == 3) ||
260 (fmt == GST_VIDEO_FORMAT_RGBA &&
261 gst_video_format_get_pixel_stride (fmt, 0) == 4));
263 GST_VIDEO_SINK_WIDTH (sink) = w;
264 GST_VIDEO_SINK_HEIGHT (sink) = h;
266 sink->rowstride = gst_video_format_get_row_stride (fmt, 0, w);
267 sink->has_alpha = (fmt == GST_VIDEO_FORMAT_RGBA);
272 GST_INFO_OBJECT (sink, "format : %d", fmt);
273 GST_INFO_OBJECT (sink, "width x height : %d x %d", w, h);
274 GST_INFO_OBJECT (sink, "pixel-aspect-ratio : %d/%d", par_n, par_d);
280 gst_gdk_pixbuf_sink_pixbuf_destroy_notify (guchar * pixels, GstBuffer * buf)
282 gst_buffer_unref (buf);
286 gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (GstGdkPixbufSink * sink,
289 GdkPixbuf *pix = NULL;
290 gint minsize, bytes_per_pixel;
292 g_return_val_if_fail (GST_VIDEO_SINK_WIDTH (sink) > 0, NULL);
293 g_return_val_if_fail (GST_VIDEO_SINK_HEIGHT (sink) > 0, NULL);
295 bytes_per_pixel = (sink->has_alpha) ? 4 : 3;
297 /* last row needn't have row padding */
298 minsize = (sink->rowstride * (GST_VIDEO_SINK_HEIGHT (sink) - 1)) +
299 (bytes_per_pixel * GST_VIDEO_SINK_WIDTH (sink));
301 g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= minsize, NULL);
303 pix = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buf),
304 GDK_COLORSPACE_RGB, sink->has_alpha, 8, GST_VIDEO_SINK_WIDTH (sink),
305 GST_VIDEO_SINK_HEIGHT (sink), sink->rowstride,
306 (GdkPixbufDestroyNotify) gst_gdk_pixbuf_sink_pixbuf_destroy_notify,
307 gst_buffer_ref (buf));
313 gst_gdk_pixbuf_sink_handle_buffer (GstBaseSink * basesink, GstBuffer * buf,
314 const gchar * msg_name)
316 GstGdkPixbufSink *sink;
320 sink = GST_GDK_PIXBUF_SINK (basesink);
322 pixbuf = gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (sink, buf);
324 GST_OBJECT_LOCK (sink);
326 do_post = sink->post_messages;
328 if (sink->last_pixbuf)
329 g_object_unref (sink->last_pixbuf);
331 sink->last_pixbuf = pixbuf; /* take ownership */
333 GST_OBJECT_UNLOCK (sink);
335 if (G_UNLIKELY (pixbuf == NULL))
342 /* it's okay to keep using pixbuf here, we can be sure no one is going to
343 * unref or change sink->last_pixbuf before we return from this function.
344 * The structure will take its own ref to the pixbuf. */
345 s = gst_structure_new (msg_name,
346 "pixbuf", GDK_TYPE_PIXBUF, pixbuf,
347 "pixel-aspect-ratio", GST_TYPE_FRACTION, sink->par_n, sink->par_d,
350 msg = gst_message_new_element (GST_OBJECT_CAST (sink), s);
351 gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
354 g_object_notify (G_OBJECT (sink), "last-pixbuf");
361 /* This shouldn't really happen */
362 GST_ELEMENT_ERROR (sink, LIBRARY, FAILED,
363 ("Couldn't create pixbuf from RGB image."),
364 ("Probably not enough free memory"));
365 return GST_FLOW_ERROR;
370 gst_gdk_pixbuf_sink_preroll (GstBaseSink * basesink, GstBuffer * buf)
372 return gst_gdk_pixbuf_sink_handle_buffer (basesink, buf, "preroll-pixbuf");
376 gst_gdk_pixbuf_sink_render (GstBaseSink * basesink, GstBuffer * buf)
378 return gst_gdk_pixbuf_sink_handle_buffer (basesink, buf, "pixbuf");
382 gst_gdk_pixbuf_sink_set_property (GObject * object, guint prop_id,
383 const GValue * value, GParamSpec * pspec)
385 GstGdkPixbufSink *sink;
387 sink = GST_GDK_PIXBUF_SINK (object);
390 case PROP_SEND_MESSAGES:
391 case PROP_POST_MESSAGES:
392 GST_OBJECT_LOCK (sink);
393 sink->post_messages = g_value_get_boolean (value);
394 GST_OBJECT_UNLOCK (sink);
397 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
403 gst_gdk_pixbuf_sink_get_property (GObject * object, guint prop_id,
404 GValue * value, GParamSpec * pspec)
406 GstGdkPixbufSink *sink;
408 sink = GST_GDK_PIXBUF_SINK (object);
411 case PROP_SEND_MESSAGES:
412 case PROP_POST_MESSAGES:
413 GST_OBJECT_LOCK (sink);
414 g_value_set_boolean (value, sink->post_messages);
415 GST_OBJECT_UNLOCK (sink);
417 case PROP_LAST_PIXBUF:
418 GST_OBJECT_LOCK (sink);
419 g_value_set_object (value, sink->last_pixbuf);
420 GST_OBJECT_UNLOCK (sink);
423 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);