tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.git] / ext / libpng / gstpngenc.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * Filter:
5  * Copyright (C) 2000 Donald A. Graft
6  *
7  * This library is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  * Library General Public License for more details.
11  *
12  * You should have received a copy of the GNU Library General Public
13  * License along with this library; if not, write to the
14  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
15  * Boston, MA 02111-1307, USA.
16  *
17  */
18 /**
19  * SECTION:element-pngenc
20  *
21  * Encodes png images.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include <string.h>
28 #include <gst/gst.h>
29 #include "gstpngenc.h"
30 #include <gst/video/video.h>
31 #include <zlib.h>
32
33 GST_DEBUG_CATEGORY_STATIC (pngenc_debug);
34 #define GST_CAT_DEFAULT pngenc_debug
35
36 /* Filter signals and args */
37 enum
38 {
39   /* FILL ME */
40   LAST_SIGNAL
41 };
42
43 #define DEFAULT_SNAPSHOT                TRUE
44 /* #define DEFAULT_NEWMEDIA             FALSE */
45 #define DEFAULT_COMPRESSION_LEVEL       6
46
47 enum
48 {
49   ARG_0,
50   ARG_SNAPSHOT,
51 /*   ARG_NEWMEDIA, */
52   ARG_COMPRESSION_LEVEL
53 };
54
55 static GstStaticPadTemplate pngenc_src_template =
56 GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("image/png, "
60         "width = (int) [ 16, 1000000 ], "
61         "height = (int) [ 16, 1000000 ], " "framerate = " GST_VIDEO_FPS_RANGE)
62     );
63
64 static GstStaticPadTemplate pngenc_sink_template =
65     GST_STATIC_PAD_TEMPLATE ("sink",
66     GST_PAD_SINK,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB ";"
69         GST_VIDEO_CAPS_GRAY8)
70     );
71
72 /* static GstElementClass *parent_class = NULL; */
73
74 GST_BOILERPLATE (GstPngEnc, gst_pngenc, GstElement, GST_TYPE_ELEMENT);
75
76 static void gst_pngenc_set_property (GObject * object,
77     guint prop_id, const GValue * value, GParamSpec * pspec);
78 static void gst_pngenc_get_property (GObject * object,
79     guint prop_id, GValue * value, GParamSpec * pspec);
80
81 static GstFlowReturn gst_pngenc_chain (GstPad * pad, GstBuffer * data);
82
83 static void
84 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
85 {
86   g_warning ("%s", error_msg);
87 }
88
89 static void
90 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
91 {
92   g_warning ("%s", warning_msg);
93 }
94
95 static void
96 gst_pngenc_base_init (gpointer g_class)
97 {
98   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
99
100   gst_element_class_add_static_pad_template
101       (element_class, &pngenc_sink_template);
102   gst_element_class_add_static_pad_template
103       (element_class, &pngenc_src_template);
104   gst_element_class_set_details_simple (element_class, "PNG image encoder",
105       "Codec/Encoder/Image",
106       "Encode a video frame to a .png image",
107       "Jeremy SIMON <jsimon13@yahoo.fr>");
108 }
109
110 static void
111 gst_pngenc_class_init (GstPngEncClass * klass)
112 {
113   GObjectClass *gobject_class;
114
115   gobject_class = (GObjectClass *) klass;
116
117   parent_class = g_type_class_peek_parent (klass);
118
119   gobject_class->get_property = gst_pngenc_get_property;
120   gobject_class->set_property = gst_pngenc_set_property;
121
122   g_object_class_install_property (gobject_class, ARG_SNAPSHOT,
123       g_param_spec_boolean ("snapshot", "Snapshot",
124           "Send EOS after encoding a frame, useful for snapshots",
125           DEFAULT_SNAPSHOT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
126
127 /*   g_object_class_install_property (gobject_class, ARG_NEWMEDIA, */
128 /*       g_param_spec_boolean ("newmedia", "newmedia", */
129 /*           "Send new media discontinuity after encoding each frame", */
130 /*           DEFAULT_NEWMEDIA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); */
131
132   g_object_class_install_property (gobject_class, ARG_COMPRESSION_LEVEL,
133       g_param_spec_uint ("compression-level", "compression-level",
134           "PNG compression level",
135           Z_NO_COMPRESSION, Z_BEST_COMPRESSION,
136           DEFAULT_COMPRESSION_LEVEL,
137           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
138
139   GST_DEBUG_CATEGORY_INIT (pngenc_debug, "pngenc", 0, "PNG image encoder");
140 }
141
142
143 static gboolean
144 gst_pngenc_setcaps (GstPad * pad, GstCaps * caps)
145 {
146   GstPngEnc *pngenc;
147   GstVideoFormat format;
148   int fps_n, fps_d;
149   GstCaps *pcaps;
150   gboolean ret;
151
152   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
153
154   ret = gst_video_format_parse_caps (caps, &format,
155       &pngenc->width, &pngenc->height);
156   if (G_LIKELY (ret))
157     ret = gst_video_parse_caps_framerate (caps, &fps_n, &fps_d);
158
159   if (G_UNLIKELY (!ret))
160     goto done;
161
162   switch (format) {
163     case GST_VIDEO_FORMAT_RGBA:
164       pngenc->png_color_type = PNG_COLOR_TYPE_RGBA;
165       break;
166     case GST_VIDEO_FORMAT_RGB:
167       pngenc->png_color_type = PNG_COLOR_TYPE_RGB;
168       break;
169     case GST_VIDEO_FORMAT_GRAY8:
170       pngenc->png_color_type = PNG_COLOR_TYPE_GRAY;
171       break;
172     default:
173       ret = FALSE;
174       goto done;
175   }
176
177   if (G_UNLIKELY (pngenc->width < 16 || pngenc->width > 1000000 ||
178           pngenc->height < 16 || pngenc->height > 1000000)) {
179     ret = FALSE;
180     goto done;
181   }
182
183   pngenc->stride = gst_video_format_get_row_stride (format, 0, pngenc->width);
184
185   pcaps = gst_caps_new_simple ("image/png",
186       "width", G_TYPE_INT, pngenc->width,
187       "height", G_TYPE_INT, pngenc->height,
188       "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
189
190   ret = gst_pad_set_caps (pngenc->srcpad, pcaps);
191
192   gst_caps_unref (pcaps);
193
194   /* Fall-through. */
195 done:
196   if (G_UNLIKELY (!ret)) {
197     pngenc->width = 0;
198     pngenc->height = 0;
199   }
200
201   gst_object_unref (pngenc);
202
203   return ret;
204 }
205
206 static void
207 gst_pngenc_init (GstPngEnc * pngenc, GstPngEncClass * g_class)
208 {
209   /* sinkpad */
210   pngenc->sinkpad = gst_pad_new_from_static_template
211       (&pngenc_sink_template, "sink");
212   gst_pad_set_chain_function (pngenc->sinkpad, gst_pngenc_chain);
213   /*   gst_pad_set_link_function (pngenc->sinkpad, gst_pngenc_sinklink); */
214   /*   gst_pad_set_getcaps_function (pngenc->sinkpad, gst_pngenc_sink_getcaps); */
215   gst_pad_set_setcaps_function (pngenc->sinkpad, gst_pngenc_setcaps);
216   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->sinkpad);
217
218   /* srcpad */
219   pngenc->srcpad = gst_pad_new_from_static_template
220       (&pngenc_src_template, "src");
221   /*   pngenc->srcpad = gst_pad_new ("src", GST_PAD_SRC); */
222   /*   gst_pad_set_getcaps_function (pngenc->srcpad, gst_pngenc_src_getcaps); */
223   /*   gst_pad_set_setcaps_function (pngenc->srcpad, gst_pngenc_setcaps); */
224   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->srcpad);
225
226   /* init settings */
227   pngenc->png_struct_ptr = NULL;
228   pngenc->png_info_ptr = NULL;
229
230   pngenc->snapshot = DEFAULT_SNAPSHOT;
231 /*   pngenc->newmedia = FALSE; */
232   pngenc->compression_level = DEFAULT_COMPRESSION_LEVEL;
233 }
234
235 static void
236 user_flush_data (png_structp png_ptr G_GNUC_UNUSED)
237 {
238 }
239
240 static void
241 user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length)
242 {
243   GstPngEnc *pngenc;
244
245   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
246
247   if (pngenc->written + length >= GST_BUFFER_SIZE (pngenc->buffer_out)) {
248     GST_ERROR_OBJECT (pngenc, "output buffer bigger than the input buffer!?");
249     png_error (png_ptr, "output buffer bigger than the input buffer!?");
250
251     /* never reached */
252     return;
253   }
254
255   memcpy (GST_BUFFER_DATA (pngenc->buffer_out) + pngenc->written, data, length);
256   pngenc->written += length;
257 }
258
259 static GstFlowReturn
260 gst_pngenc_chain (GstPad * pad, GstBuffer * buf)
261 {
262   GstPngEnc *pngenc;
263   gint row_index;
264   png_byte **row_pointers;
265   GstFlowReturn ret = GST_FLOW_OK;
266   GstBuffer *encoded_buf = NULL;
267
268   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
269
270   GST_DEBUG_OBJECT (pngenc, "BEGINNING");
271
272   if (G_UNLIKELY (pngenc->width <= 0 || pngenc->height <= 0)) {
273     ret = GST_FLOW_NOT_NEGOTIATED;
274     goto done;
275   }
276
277   if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < pngenc->height * pngenc->stride)) {
278     gst_buffer_unref (buf);
279     GST_ELEMENT_ERROR (pngenc, STREAM, FORMAT, (NULL),
280         ("Provided input buffer is too small, caps problem?"));
281     ret = GST_FLOW_ERROR;
282     goto done;
283   }
284
285   /* initialize png struct stuff */
286   pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
287       (png_voidp) NULL, user_error_fn, user_warning_fn);
288   if (pngenc->png_struct_ptr == NULL) {
289     gst_buffer_unref (buf);
290     GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
291         ("Failed to initialize png structure"));
292     ret = GST_FLOW_ERROR;
293     goto done;
294   }
295
296   pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr);
297   if (!pngenc->png_info_ptr) {
298     gst_buffer_unref (buf);
299     png_destroy_write_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL);
300     GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
301         ("Failed to initialize the png info structure"));
302     ret = GST_FLOW_ERROR;
303     goto done;
304   }
305
306   /* non-0 return is from a longjmp inside of libpng */
307   if (setjmp (png_jmpbuf (pngenc->png_struct_ptr)) != 0) {
308     gst_buffer_unref (buf);
309     png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
310     GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL),
311         ("returning from longjmp"));
312     ret = GST_FLOW_ERROR;
313     goto done;
314   }
315
316   png_set_filter (pngenc->png_struct_ptr, 0,
317       PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
318   png_set_compression_level (pngenc->png_struct_ptr, pngenc->compression_level);
319
320   png_set_IHDR (pngenc->png_struct_ptr,
321       pngenc->png_info_ptr,
322       pngenc->width,
323       pngenc->height,
324       8,
325       pngenc->png_color_type,
326       PNG_INTERLACE_NONE,
327       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
328
329   png_set_write_fn (pngenc->png_struct_ptr, pngenc,
330       (png_rw_ptr) user_write_data, user_flush_data);
331
332   row_pointers = g_new (png_byte *, pngenc->height);
333
334   for (row_index = 0; row_index < pngenc->height; row_index++) {
335     row_pointers[row_index] = GST_BUFFER_DATA (buf) +
336         (row_index * pngenc->stride);
337   }
338
339   /* allocate the output buffer */
340   pngenc->buffer_out =
341       gst_buffer_new_and_alloc (pngenc->height * pngenc->stride);
342   pngenc->written = 0;
343
344   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
345   png_write_image (pngenc->png_struct_ptr, row_pointers);
346   png_write_end (pngenc->png_struct_ptr, NULL);
347
348   g_free (row_pointers);
349
350   encoded_buf = gst_buffer_create_sub (pngenc->buffer_out, 0, pngenc->written);
351
352   png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr);
353   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
354   gst_buffer_copy_metadata (encoded_buf, buf, GST_BUFFER_COPY_TIMESTAMPS);
355   gst_buffer_unref (buf);
356   gst_buffer_set_caps (encoded_buf, GST_PAD_CAPS (pngenc->srcpad));
357
358   if ((ret = gst_pad_push (pngenc->srcpad, encoded_buf)) != GST_FLOW_OK)
359     goto done;
360
361   if (pngenc->snapshot) {
362     GstEvent *event;
363
364     GST_DEBUG_OBJECT (pngenc, "snapshot mode, sending EOS");
365     /* send EOS event, since a frame has been pushed out */
366     event = gst_event_new_eos ();
367
368     gst_pad_push_event (pngenc->srcpad, event);
369     ret = GST_FLOW_UNEXPECTED;
370   }
371
372 done:
373   GST_DEBUG_OBJECT (pngenc, "END, ret:%d", ret);
374
375   if (pngenc->buffer_out != NULL) {
376     gst_buffer_unref (pngenc->buffer_out);
377     pngenc->buffer_out = NULL;
378   }
379
380   gst_object_unref (pngenc);
381   return ret;
382 }
383
384
385 static void
386 gst_pngenc_get_property (GObject * object,
387     guint prop_id, GValue * value, GParamSpec * pspec)
388 {
389   GstPngEnc *pngenc;
390
391   pngenc = GST_PNGENC (object);
392
393   switch (prop_id) {
394     case ARG_SNAPSHOT:
395       g_value_set_boolean (value, pngenc->snapshot);
396       break;
397 /*     case ARG_NEWMEDIA: */
398 /*       g_value_set_boolean (value, pngenc->newmedia); */
399 /*       break; */
400     case ARG_COMPRESSION_LEVEL:
401       g_value_set_uint (value, pngenc->compression_level);
402       break;
403     default:
404       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
405       break;
406   }
407 }
408
409
410 static void
411 gst_pngenc_set_property (GObject * object,
412     guint prop_id, const GValue * value, GParamSpec * pspec)
413 {
414   GstPngEnc *pngenc;
415
416   pngenc = GST_PNGENC (object);
417
418   switch (prop_id) {
419     case ARG_SNAPSHOT:
420       pngenc->snapshot = g_value_get_boolean (value);
421       break;
422 /*     case ARG_NEWMEDIA: */
423 /*       pngenc->newmedia = g_value_get_boolean (value); */
424 /*       break; */
425     case ARG_COMPRESSION_LEVEL:
426       pngenc->compression_level = g_value_get_uint (value);
427       break;
428     default:
429       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
430       break;
431   }
432 }