393e642cb69f4134654b9fc441816dbeff49214c
[platform/upstream/gst-plugins-good.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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include <string.h>
23 #include <gst/gst.h>
24 #include "gstpngenc.h"
25 #include <gst/video/video.h>
26
27 #define MAX_HEIGHT              4096
28
29
30 GstElementDetails gst_pngenc_details = {
31   "PNG encoder",
32   "Codec/Encoder/Image",
33   "Encode a video frame to a .png image",
34   "Jeremy SIMON <jsimon13@yahoo.fr>",
35 };
36
37
38 /* Filter signals and args */
39 enum
40 {
41   /* FILL ME */
42   LAST_SIGNAL
43 };
44
45 enum
46 {
47   ARG_0
48 };
49
50 static void     gst_pngenc_base_init    (gpointer g_class);
51 static void     gst_pngenc_class_init   (GstPngEncClass *klass);
52 static void     gst_pngenc_init         (GstPngEnc *pngenc);
53
54 static void     gst_pngenc_chain        (GstPad *pad, GstData *_data);
55
56 GstPadTemplate *pngenc_src_template, *pngenc_sink_template;
57
58 static GstElementClass *parent_class = NULL;
59
60
61 static void user_error_fn (png_structp png_ptr, png_const_charp error_msg)
62 {
63   g_warning("%s", error_msg);
64 }
65
66 static void user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
67 {
68   g_warning("%s", warning_msg);
69 }
70
71
72 GType gst_pngenc_get_type (void)
73 {
74   static GType pngenc_type = 0;
75
76   if (!pngenc_type) {
77     static const GTypeInfo pngenc_info = {
78       sizeof (GstPngEncClass),
79       gst_pngenc_base_init,
80       NULL,
81       (GClassInitFunc) gst_pngenc_class_init,
82       NULL,
83       NULL,
84       sizeof (GstPngEnc),
85       0,
86       (GInstanceInitFunc) gst_pngenc_init,
87     };
88
89     pngenc_type = g_type_register_static (GST_TYPE_ELEMENT, "GstPngEnc",
90                                           &pngenc_info, 0);
91   }
92   return pngenc_type;
93 }
94
95 static GstCaps*
96 png_caps_factory (void)
97 {
98   return gst_caps_new_simple ("video/x-png",
99       "width",     GST_TYPE_INT_RANGE, 16, 4096,
100       "height",    GST_TYPE_INT_RANGE, 16, 4096,
101       "framerate", GST_TYPE_DOUBLE_RANGE, 0.0, G_MAXDOUBLE,
102       NULL);
103 }
104
105
106 static GstCaps*
107 raw_caps_factory (void)
108
109   return gst_caps_from_string (GST_VIDEO_CAPS_RGB);
110 }
111
112 static void
113 gst_pngenc_base_init (gpointer g_class)
114 {
115   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
116   GstCaps *raw_caps, *png_caps;
117   
118   raw_caps = raw_caps_factory ();
119   png_caps = png_caps_factory ();
120
121   pngenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
122                                                GST_PAD_ALWAYS,
123                                                raw_caps);
124   
125   pngenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
126                                               GST_PAD_ALWAYS,
127                                               png_caps);
128   
129   gst_element_class_add_pad_template (element_class, pngenc_sink_template);
130   gst_element_class_add_pad_template (element_class, pngenc_src_template);
131   gst_element_class_set_details (element_class, &gst_pngenc_details);
132 }
133
134 static void
135 gst_pngenc_class_init (GstPngEncClass *klass)
136 {
137   GObjectClass *gobject_class;
138   GstElementClass *gstelement_class;
139
140   gobject_class = (GObjectClass *) klass;
141   gstelement_class = (GstElementClass *) klass;
142
143   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
144 }
145
146
147 static GstPadLinkReturn
148 gst_pngenc_sinklink (GstPad *pad, const GstCaps *caps)
149 {
150   GstPngEnc *pngenc;
151   gdouble fps;
152   GstStructure *structure;
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   gst_structure_get_double (structure, "framerate", &fps);
160   gst_structure_get_int (structure, "bpp", &pngenc->bpp);
161
162   caps = gst_caps_new_simple ("video/x-png",
163       "framerate", G_TYPE_DOUBLE, fps,
164       "width",     G_TYPE_INT, pngenc->width,
165       "height",    G_TYPE_INT, pngenc->height, NULL);
166
167   return gst_pad_try_set_caps (pngenc->srcpad, caps);
168 }
169
170 static void
171 gst_pngenc_init (GstPngEnc * pngenc)
172 {
173   pngenc->sinkpad = gst_pad_new_from_template (pngenc_sink_template, "sink");
174   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->sinkpad);
175
176   pngenc->srcpad = gst_pad_new ("src", GST_PAD_SRC);
177   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->srcpad);
178
179   gst_pad_set_chain_function (pngenc->sinkpad, gst_pngenc_chain);
180   gst_pad_set_link_function (pngenc->sinkpad, gst_pngenc_sinklink);
181
182   pngenc->png_struct_ptr = NULL;
183   pngenc->png_info_ptr = NULL;
184
185 }
186
187 void user_flush_data (png_structp png_ptr)
188 {
189 GstPngEnc *pngenc;
190
191   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
192
193   gst_pad_push (pngenc->srcpad, GST_DATA (gst_event_new (GST_EVENT_FLUSH)));
194 }
195
196
197 void user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length)
198 {
199   GstBuffer *buffer;
200   GstPngEnc *pngenc;
201
202   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
203
204   buffer = gst_buffer_new();
205   GST_BUFFER_DATA (buffer) = g_memdup (data, length);
206   GST_BUFFER_SIZE (buffer) = length;
207
208   if (pngenc->buffer_out)
209   {
210     GstBuffer *merge;
211     merge = gst_buffer_merge (pngenc->buffer_out, buffer);
212     gst_buffer_unref (buffer);
213     gst_buffer_unref (pngenc->buffer_out);
214     pngenc->buffer_out = merge;
215   }
216   else
217     pngenc->buffer_out = buffer;
218 }
219
220 static void
221 gst_pngenc_chain (GstPad *pad, GstData *_data)
222 {
223   GstBuffer *buf = GST_BUFFER (_data);
224   GstPngEnc *pngenc;
225   gint row_index;
226   png_byte *row_pointers[MAX_HEIGHT];
227   GstEvent *event;
228
229   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
230
231   pngenc->buffer_out = NULL;
232   if (!GST_PAD_IS_USABLE (pngenc->srcpad))
233   {
234     gst_buffer_unref (buf);
235     return;
236   }
237
238   /* initialize png struct stuff */
239   pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
240                            (png_voidp) NULL, user_error_fn, user_warning_fn);
241   /* FIXME: better error handling */
242   if (pngenc->png_struct_ptr == NULL)
243      g_warning ("Failed to initialize png structure");
244
245   pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr);
246   if (!pngenc->png_info_ptr)
247   {
248     png_destroy_read_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL,
249                              (png_infopp) NULL);
250   }
251
252   /* non-0 return is from a longjmp inside of libpng */
253   if (setjmp (pngenc->png_struct_ptr->jmpbuf) != 0)
254   {
255     GST_DEBUG ("returning from longjmp");
256     png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
257     return;
258   }
259
260   png_set_filter (pngenc->png_struct_ptr, 0,
261                   PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
262   png_set_compression_level (pngenc->png_struct_ptr, 9);
263
264   png_set_IHDR(
265     pngenc->png_struct_ptr,
266     pngenc->png_info_ptr,
267     pngenc->width,
268     pngenc->height,
269     pngenc->bpp / 3,
270     PNG_COLOR_TYPE_RGB,
271     PNG_INTERLACE_NONE,
272     PNG_COMPRESSION_TYPE_DEFAULT,
273     PNG_FILTER_TYPE_DEFAULT
274   );
275
276   png_set_write_fn (pngenc->png_struct_ptr, pngenc,
277                     (png_rw_ptr) user_write_data, user_flush_data);
278
279   for (row_index = 0; row_index < pngenc->height; row_index++)
280     row_pointers[row_index] = GST_BUFFER_DATA (buf) +
281                               (pngenc->width * row_index * pngenc->bpp / 8);
282
283   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
284   png_write_image (pngenc->png_struct_ptr, row_pointers);
285   png_write_end (pngenc->png_struct_ptr, NULL);
286
287   user_flush_data (pngenc->png_struct_ptr);
288
289   png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr);
290   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
291
292   gst_pad_push (pngenc->srcpad, GST_DATA (pngenc->buffer_out));
293
294   /* send EOS event, since a frame has been pushed out */
295   event = gst_event_new (GST_EVENT_EOS);
296   gst_pad_push (pngenc->srcpad, GST_DATA (event));
297
298   gst_buffer_unref (buf);
299 }