Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / ext / gdk_pixbuf / gstgdkpixbufsink.c
1 /* GStreamer GdkPixbuf sink
2  * Copyright (C) 2006-2008 Tim-Philipp Müller <tim centricular net>
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 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.
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-gdkpixbufsink
22  * @Since: 0.10.8
23  *
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).
28  *
29  * There are two ways to use this element and obtain the #GdkPixbuf objects
30  * created:
31  * <itemizedlist>
32  * <listitem>
33  * Watching for element messages named <classname>&quot;preroll-pixbuf&quot;
34  * </classname> or <classname>&quot;pixbuf&quot;</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
37  * contained in them.
38  * </listitem>
39  * <listitem>
40  * Retrieving the current pixbuf via the #GstGdkPixbufSink:last-pixbuf property
41  * when needed.
42  * </listitem>
43  * </itemizedlist>
44  *
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
47  * element.
48  *
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.
53  *
54  * <refsect2>
55  * <title>Message details</title>
56  * As mentioned above, this element will by default post element messages
57  * containing structures named <classname>&quot;preroll-pixbuf&quot;
58  * </classname> or <classname>&quot;pixbuf&quot;</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:
61  * <itemizedlist>
62  * <listitem>
63  *   <classname>&quot;pixbuf&quot;</classname>: the #GdkPixbuf object
64  * </listitem>
65  * <listitem>
66  *   <classname>&quot;pixel-aspect-ratio&quot;</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.
78  * </listitem>
79  * </itemizedlist>
80  * </refsect2>
81  *
82  * <refsect2>
83  * <title>Example pipeline</title>
84  * |[
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 &apos;(NULL)&apos;).
89  * </refsect2>
90  */
91
92 #ifdef HAVE_CONFIG_H
93 #include "config.h"
94 #endif
95
96 #include "gstgdkpixbufsink.h"
97
98 #include <gst/video/video.h>
99
100 #define DEFAULT_SEND_MESSAGES TRUE
101 #define DEFAULT_POST_MESSAGES TRUE
102
103 enum
104 {
105   PROP_0,
106   PROP_SEND_MESSAGES,
107   PROP_POST_MESSAGES,
108   PROP_LAST_PIXBUF,
109   PROP_LAST
110 };
111
112
113 GST_BOILERPLATE (GstGdkPixbufSink, gst_gdk_pixbuf_sink, GstVideoSink,
114     GST_TYPE_VIDEO_SINK);
115
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);
120
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,
124     GstCaps * caps);
125 static GstFlowReturn gst_gdk_pixbuf_sink_render (GstBaseSink * bsink,
126     GstBuffer * buf);
127 static GstFlowReturn gst_gdk_pixbuf_sink_preroll (GstBaseSink * bsink,
128     GstBuffer * buf);
129 static GdkPixbuf *gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (GstGdkPixbufSink *
130     sink, GstBuffer * buf);
131
132 #define WxH ", width = (int) [ 16, 4096 ], height = (int) [ 16, 4096 ]"
133
134 static GstStaticPadTemplate pixbufsink_sink_factory =
135     GST_STATIC_PAD_TEMPLATE ("sink",
136     GST_PAD_SINK,
137     GST_PAD_ALWAYS,
138     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB WxH ";" GST_VIDEO_CAPS_RGBA WxH));
139
140 static void
141 gst_gdk_pixbuf_sink_base_init (gpointer g_class)
142 {
143   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
144
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>");
148
149   gst_element_class_add_static_pad_template (element_class,
150       &pixbufsink_sink_factory);
151 }
152
153 static void
154 gst_gdk_pixbuf_sink_class_init (GstGdkPixbufSinkClass * klass)
155 {
156   GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
157   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
158
159   gobject_class->set_property = gst_gdk_pixbuf_sink_set_property;
160   gobject_class->get_property = gst_gdk_pixbuf_sink_get_property;
161
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));
168   /**
169    * GstGdkPixbuf:post-messages:
170    *
171    * Post messages on the bus containing pixbufs.
172    *
173    * Since: 0.10.17
174    */
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));
179
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));
184
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);
190 }
191
192 static void
193 gst_gdk_pixbuf_sink_init (GstGdkPixbufSink * sink,
194     GstGdkPixbufSinkClass * klass)
195 {
196   sink->par_n = 0;
197   sink->par_d = 0;
198   sink->has_alpha = FALSE;
199   sink->last_pixbuf = NULL;
200   sink->post_messages = DEFAULT_POST_MESSAGES;
201
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);
206 }
207
208
209 static gboolean
210 gst_gdk_pixbuf_sink_start (GstBaseSink * basesink)
211 {
212   GST_LOG_OBJECT (basesink, "start");
213
214   return TRUE;
215 }
216
217 static gboolean
218 gst_gdk_pixbuf_sink_stop (GstBaseSink * basesink)
219 {
220   GstGdkPixbufSink *sink = GST_GDK_PIXBUF_SINK (basesink);
221
222   GST_VIDEO_SINK_WIDTH (sink) = 0;
223   GST_VIDEO_SINK_HEIGHT (sink) = 0;
224
225   sink->par_n = 0;
226   sink->par_d = 0;
227   sink->has_alpha = FALSE;
228
229   if (sink->last_pixbuf) {
230     g_object_unref (sink->last_pixbuf);
231     sink->last_pixbuf = NULL;
232   }
233
234   GST_LOG_OBJECT (sink, "stop");
235
236   return TRUE;
237 }
238
239 static gboolean
240 gst_gdk_pixbuf_sink_set_caps (GstBaseSink * basesink, GstCaps * caps)
241 {
242   GstGdkPixbufSink *sink = GST_GDK_PIXBUF_SINK (basesink);
243   GstVideoFormat fmt;
244   gint w, h, par_n, par_d;
245
246   GST_LOG_OBJECT (sink, "caps: %" GST_PTR_FORMAT, caps);
247
248   if (!gst_video_format_parse_caps (caps, &fmt, &w, &h)) {
249     GST_WARNING_OBJECT (sink, "parse_caps failed");
250     return FALSE;
251   }
252
253   if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) {
254     GST_LOG_OBJECT (sink, "no pixel aspect ratio");
255     return FALSE;
256   }
257
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));
262
263   GST_VIDEO_SINK_WIDTH (sink) = w;
264   GST_VIDEO_SINK_HEIGHT (sink) = h;
265
266   sink->rowstride = gst_video_format_get_row_stride (fmt, 0, w);
267   sink->has_alpha = (fmt == GST_VIDEO_FORMAT_RGBA);
268
269   sink->par_n = par_n;
270   sink->par_d = par_d;
271
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);
275
276   return TRUE;
277 }
278
279 static void
280 gst_gdk_pixbuf_sink_pixbuf_destroy_notify (guchar * pixels, GstBuffer * buf)
281 {
282   gst_buffer_unref (buf);
283 }
284
285 static GdkPixbuf *
286 gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (GstGdkPixbufSink * sink,
287     GstBuffer * buf)
288 {
289   GdkPixbuf *pix = NULL;
290   gint minsize, bytes_per_pixel;
291
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);
294
295   bytes_per_pixel = (sink->has_alpha) ? 4 : 3;
296
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));
300
301   g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= minsize, NULL);
302
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));
308
309   return pix;
310 }
311
312 static GstFlowReturn
313 gst_gdk_pixbuf_sink_handle_buffer (GstBaseSink * basesink, GstBuffer * buf,
314     const gchar * msg_name)
315 {
316   GstGdkPixbufSink *sink;
317   GdkPixbuf *pixbuf;
318   gboolean do_post;
319
320   sink = GST_GDK_PIXBUF_SINK (basesink);
321
322   pixbuf = gst_gdk_pixbuf_sink_get_pixbuf_from_buffer (sink, buf);
323
324   GST_OBJECT_LOCK (sink);
325
326   do_post = sink->post_messages;
327
328   if (sink->last_pixbuf)
329     g_object_unref (sink->last_pixbuf);
330
331   sink->last_pixbuf = pixbuf;   /* take ownership */
332
333   GST_OBJECT_UNLOCK (sink);
334
335   if (G_UNLIKELY (pixbuf == NULL))
336     goto error;
337
338   if (do_post) {
339     GstStructure *s;
340     GstMessage *msg;
341
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,
348         NULL);
349
350     msg = gst_message_new_element (GST_OBJECT_CAST (sink), s);
351     gst_element_post_message (GST_ELEMENT_CAST (sink), msg);
352   }
353
354   g_object_notify (G_OBJECT (sink), "last-pixbuf");
355
356   return GST_FLOW_OK;
357
358 /* ERRORS */
359 error:
360   {
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;
366   }
367 }
368
369 static GstFlowReturn
370 gst_gdk_pixbuf_sink_preroll (GstBaseSink * basesink, GstBuffer * buf)
371 {
372   return gst_gdk_pixbuf_sink_handle_buffer (basesink, buf, "preroll-pixbuf");
373 }
374
375 static GstFlowReturn
376 gst_gdk_pixbuf_sink_render (GstBaseSink * basesink, GstBuffer * buf)
377 {
378   return gst_gdk_pixbuf_sink_handle_buffer (basesink, buf, "pixbuf");
379 }
380
381 static void
382 gst_gdk_pixbuf_sink_set_property (GObject * object, guint prop_id,
383     const GValue * value, GParamSpec * pspec)
384 {
385   GstGdkPixbufSink *sink;
386
387   sink = GST_GDK_PIXBUF_SINK (object);
388
389   switch (prop_id) {
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);
395       break;
396     default:
397       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
398       break;
399   }
400 }
401
402 static void
403 gst_gdk_pixbuf_sink_get_property (GObject * object, guint prop_id,
404     GValue * value, GParamSpec * pspec)
405 {
406   GstGdkPixbufSink *sink;
407
408   sink = GST_GDK_PIXBUF_SINK (object);
409
410   switch (prop_id) {
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);
416       break;
417     case PROP_LAST_PIXBUF:
418       GST_OBJECT_LOCK (sink);
419       g_value_set_object (value, sink->last_pixbuf);
420       GST_OBJECT_UNLOCK (sink);
421       break;
422     default:
423       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
424       break;
425   }
426 }