upload tizen1.0 source
[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 #define MAX_HEIGHT              4096
34
35 GST_DEBUG_CATEGORY_STATIC (pngenc_debug);
36 #define GST_CAT_DEFAULT pngenc_debug
37
38 /* Filter signals and args */
39 enum
40 {
41   /* FILL ME */
42   LAST_SIGNAL
43 };
44
45 #define DEFAULT_SNAPSHOT                TRUE
46 /* #define DEFAULT_NEWMEDIA             FALSE */
47 #define DEFAULT_COMPRESSION_LEVEL       6
48
49 enum
50 {
51   ARG_0,
52   ARG_SNAPSHOT,
53 /*   ARG_NEWMEDIA, */
54   ARG_COMPRESSION_LEVEL
55 };
56
57 static GstStaticPadTemplate pngenc_src_template =
58 GST_STATIC_PAD_TEMPLATE ("src",
59     GST_PAD_SRC,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS ("image/png, "
62         "width = (int) [ 16, 4096 ], "
63         "height = (int) [ 16, 4096 ], " "framerate = " GST_VIDEO_FPS_RANGE)
64     );
65
66 static GstStaticPadTemplate pngenc_sink_template =
67     GST_STATIC_PAD_TEMPLATE ("sink",
68     GST_PAD_SINK,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_RGB ";"
71         GST_VIDEO_CAPS_GRAY8)
72     );
73
74 /* static GstElementClass *parent_class = NULL; */
75
76 GST_BOILERPLATE (GstPngEnc, gst_pngenc, GstElement, GST_TYPE_ELEMENT);
77
78 static void gst_pngenc_set_property (GObject * object,
79     guint prop_id, const GValue * value, GParamSpec * pspec);
80 static void gst_pngenc_get_property (GObject * object,
81     guint prop_id, GValue * value, GParamSpec * pspec);
82
83 static GstFlowReturn gst_pngenc_chain (GstPad * pad, GstBuffer * data);
84
85 static void
86 user_error_fn (png_structp png_ptr, png_const_charp error_msg)
87 {
88   g_warning ("%s", error_msg);
89 }
90
91 static void
92 user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
93 {
94   g_warning ("%s", warning_msg);
95 }
96
97 static void
98 gst_pngenc_base_init (gpointer g_class)
99 {
100   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
101
102   gst_element_class_add_pad_template
103       (element_class, gst_static_pad_template_get (&pngenc_sink_template));
104   gst_element_class_add_pad_template
105       (element_class, gst_static_pad_template_get (&pngenc_src_template));
106   gst_element_class_set_details_simple (element_class, "PNG image encoder",
107       "Codec/Encoder/Image",
108       "Encode a video frame to a .png image",
109       "Jeremy SIMON <jsimon13@yahoo.fr>");
110 }
111
112 static void
113 gst_pngenc_class_init (GstPngEncClass * klass)
114 {
115   GObjectClass *gobject_class;
116
117   gobject_class = (GObjectClass *) klass;
118
119   parent_class = g_type_class_peek_parent (klass);
120
121   gobject_class->get_property = gst_pngenc_get_property;
122   gobject_class->set_property = gst_pngenc_set_property;
123
124   g_object_class_install_property (gobject_class, ARG_SNAPSHOT,
125       g_param_spec_boolean ("snapshot", "Snapshot",
126           "Send EOS after encoding a frame, useful for snapshots",
127           DEFAULT_SNAPSHOT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
128
129 /*   g_object_class_install_property (gobject_class, ARG_NEWMEDIA, */
130 /*       g_param_spec_boolean ("newmedia", "newmedia", */
131 /*           "Send new media discontinuity after encoding each frame", */
132 /*           DEFAULT_NEWMEDIA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); */
133
134   g_object_class_install_property (gobject_class, ARG_COMPRESSION_LEVEL,
135       g_param_spec_uint ("compression-level", "compression-level",
136           "PNG compression level",
137           Z_NO_COMPRESSION, Z_BEST_COMPRESSION,
138           DEFAULT_COMPRESSION_LEVEL,
139           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
140
141   GST_DEBUG_CATEGORY_INIT (pngenc_debug, "pngenc", 0, "PNG image encoder");
142 }
143
144
145 static gboolean
146 gst_pngenc_setcaps (GstPad * pad, GstCaps * caps)
147 {
148   GstPngEnc *pngenc;
149   const GValue *fps;
150   GstStructure *structure;
151   GstCaps *pcaps;
152   gboolean ret = TRUE;
153
154   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
155
156   structure = gst_caps_get_structure (caps, 0);
157   gst_structure_get_int (structure, "width", &pngenc->width);
158   gst_structure_get_int (structure, "height", &pngenc->height);
159   fps = gst_structure_get_value (structure, "framerate");
160   gst_structure_get_int (structure, "bpp", &pngenc->bpp);
161
162   if (pngenc->bpp == 32)
163     pngenc->stride = pngenc->width * 4;
164   else if (pngenc->bpp == 8)
165     pngenc->stride = GST_ROUND_UP_4 (pngenc->width);
166   else
167     pngenc->stride = GST_ROUND_UP_4 (pngenc->width * 3);
168
169   pcaps = gst_caps_new_simple ("image/png",
170       "width", G_TYPE_INT, pngenc->width,
171       "height", G_TYPE_INT, pngenc->height, NULL);
172   structure = gst_caps_get_structure (pcaps, 0);
173   gst_structure_set_value (structure, "framerate", fps);
174
175   ret = gst_pad_set_caps (pngenc->srcpad, pcaps);
176
177   gst_caps_unref (pcaps);
178   gst_object_unref (pngenc);
179
180   return ret;
181 }
182
183 static void
184 gst_pngenc_init (GstPngEnc * pngenc, GstPngEncClass * g_class)
185 {
186   /* sinkpad */
187   pngenc->sinkpad = gst_pad_new_from_static_template
188       (&pngenc_sink_template, "sink");
189   gst_pad_set_chain_function (pngenc->sinkpad, gst_pngenc_chain);
190   /*   gst_pad_set_link_function (pngenc->sinkpad, gst_pngenc_sinklink); */
191   /*   gst_pad_set_getcaps_function (pngenc->sinkpad, gst_pngenc_sink_getcaps); */
192   gst_pad_set_setcaps_function (pngenc->sinkpad, gst_pngenc_setcaps);
193   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->sinkpad);
194
195   /* srcpad */
196   pngenc->srcpad = gst_pad_new_from_static_template
197       (&pngenc_src_template, "src");
198   /*   pngenc->srcpad = gst_pad_new ("src", GST_PAD_SRC); */
199   /*   gst_pad_set_getcaps_function (pngenc->srcpad, gst_pngenc_src_getcaps); */
200   /*   gst_pad_set_setcaps_function (pngenc->srcpad, gst_pngenc_setcaps); */
201   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->srcpad);
202
203   /* init settings */
204   pngenc->png_struct_ptr = NULL;
205   pngenc->png_info_ptr = NULL;
206
207   pngenc->snapshot = DEFAULT_SNAPSHOT;
208 /*   pngenc->newmedia = FALSE; */
209   pngenc->compression_level = DEFAULT_COMPRESSION_LEVEL;
210 }
211
212 static void
213 user_flush_data (png_structp png_ptr G_GNUC_UNUSED)
214 {
215 }
216
217 static void
218 user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length)
219 {
220   GstPngEnc *pngenc;
221
222   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
223
224   if (pngenc->written + length >= GST_BUFFER_SIZE (pngenc->buffer_out)) {
225     GST_ERROR_OBJECT (pngenc, "output buffer bigger than the input buffer!?");
226     png_error (png_ptr, "output buffer bigger than the input buffer!?");
227
228     /* never reached */
229     return;
230   }
231
232   memcpy (GST_BUFFER_DATA (pngenc->buffer_out) + pngenc->written, data, length);
233   pngenc->written += length;
234 }
235
236 static GstFlowReturn
237 gst_pngenc_chain (GstPad * pad, GstBuffer * buf)
238 {
239   GstPngEnc *pngenc;
240   gint row_index;
241   gint color_type;
242   png_byte *row_pointers[MAX_HEIGHT];
243   GstFlowReturn ret = GST_FLOW_OK;
244   GstBuffer *encoded_buf = NULL;
245
246   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
247
248   GST_DEBUG_OBJECT (pngenc, "BEGINNING");
249
250   /* initialize png struct stuff */
251   pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
252       (png_voidp) NULL, user_error_fn, user_warning_fn);
253   if (pngenc->png_struct_ptr == NULL) {
254     gst_buffer_unref (buf);
255     GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
256         ("Failed to initialize png structure"));
257     ret = GST_FLOW_ERROR;
258     goto done;
259   }
260
261   pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr);
262   if (!pngenc->png_info_ptr) {
263     gst_buffer_unref (buf);
264     png_destroy_write_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL);
265     GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL),
266         ("Failed to initialize the png info structure"));
267     ret = GST_FLOW_ERROR;
268     goto done;
269   }
270
271   /* non-0 return is from a longjmp inside of libpng */
272   if (setjmp (png_jmpbuf (pngenc->png_struct_ptr)) != 0) {
273     gst_buffer_unref (buf);
274     png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
275     GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL),
276         ("returning from longjmp"));
277     ret = GST_FLOW_ERROR;
278     goto done;
279   }
280
281   png_set_filter (pngenc->png_struct_ptr, 0,
282       PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
283   png_set_compression_level (pngenc->png_struct_ptr, pngenc->compression_level);
284
285   if (pngenc->bpp == 32)
286     color_type = PNG_COLOR_TYPE_RGBA;
287   else if (pngenc->bpp == 8)
288     color_type = PNG_COLOR_TYPE_GRAY;
289   else
290     color_type = PNG_COLOR_TYPE_RGB;
291
292   png_set_IHDR (pngenc->png_struct_ptr,
293       pngenc->png_info_ptr,
294       pngenc->width,
295       pngenc->height,
296       8,
297       color_type,
298       PNG_INTERLACE_NONE,
299       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
300
301   png_set_write_fn (pngenc->png_struct_ptr, pngenc,
302       (png_rw_ptr) user_write_data, user_flush_data);
303
304   for (row_index = 0; row_index < pngenc->height; row_index++) {
305     row_pointers[row_index] = GST_BUFFER_DATA (buf) +
306         (row_index * pngenc->stride);
307   }
308
309   /* allocate the output buffer */
310   pngenc->buffer_out =
311       gst_buffer_new_and_alloc (pngenc->height * pngenc->stride);
312   pngenc->written = 0;
313
314   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
315   png_write_image (pngenc->png_struct_ptr, row_pointers);
316   png_write_end (pngenc->png_struct_ptr, NULL);
317
318   encoded_buf = gst_buffer_create_sub (pngenc->buffer_out, 0, pngenc->written);
319
320   png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr);
321   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
322   gst_buffer_copy_metadata (encoded_buf, buf, GST_BUFFER_COPY_TIMESTAMPS);
323   gst_buffer_unref (buf);
324   gst_buffer_set_caps (encoded_buf, GST_PAD_CAPS (pngenc->srcpad));
325
326   if ((ret = gst_pad_push (pngenc->srcpad, encoded_buf)) != GST_FLOW_OK)
327     goto done;
328
329   if (pngenc->snapshot) {
330     GstEvent *event;
331
332     GST_DEBUG_OBJECT (pngenc, "snapshot mode, sending EOS");
333     /* send EOS event, since a frame has been pushed out */
334     event = gst_event_new_eos ();
335
336     gst_pad_push_event (pngenc->srcpad, event);
337     ret = GST_FLOW_UNEXPECTED;
338   }
339
340 done:
341   GST_DEBUG_OBJECT (pngenc, "END, ret:%d", ret);
342
343   if (pngenc->buffer_out != NULL) {
344     gst_buffer_unref (pngenc->buffer_out);
345     pngenc->buffer_out = NULL;
346   }
347
348   gst_object_unref (pngenc);
349   return ret;
350 }
351
352
353 static void
354 gst_pngenc_get_property (GObject * object,
355     guint prop_id, GValue * value, GParamSpec * pspec)
356 {
357   GstPngEnc *pngenc;
358
359   pngenc = GST_PNGENC (object);
360
361   switch (prop_id) {
362     case ARG_SNAPSHOT:
363       g_value_set_boolean (value, pngenc->snapshot);
364       break;
365 /*     case ARG_NEWMEDIA: */
366 /*       g_value_set_boolean (value, pngenc->newmedia); */
367 /*       break; */
368     case ARG_COMPRESSION_LEVEL:
369       g_value_set_uint (value, pngenc->compression_level);
370       break;
371     default:
372       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
373       break;
374   }
375 }
376
377
378 static void
379 gst_pngenc_set_property (GObject * object,
380     guint prop_id, const GValue * value, GParamSpec * pspec)
381 {
382   GstPngEnc *pngenc;
383
384   pngenc = GST_PNGENC (object);
385
386   switch (prop_id) {
387     case ARG_SNAPSHOT:
388       pngenc->snapshot = g_value_get_boolean (value);
389       break;
390 /*     case ARG_NEWMEDIA: */
391 /*       pngenc->newmedia = g_value_get_boolean (value); */
392 /*       break; */
393     case ARG_COMPRESSION_LEVEL:
394       pngenc->compression_level = g_value_get_uint (value);
395       break;
396     default:
397       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
398       break;
399   }
400 }