9384505061564651f52ad721a25c52a64cbf25b0
[platform/upstream/gstreamer.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 #include <string.h>
20 #include <gst/gst.h>
21 #include "gstpngenc.h"
22
23 #define MAX_HEIGHT              4096
24
25
26 GstElementDetails gst_pngenc_details = {
27   "",
28   "Filter/Video/Effect",
29   "LGPL",
30   "Encode a video frame to a .png image"
31   VERSION,
32   "Jeremy SIMON <jsimon13@yahoo.fr>",
33   "(C) 2000 Donald Graft",
34 };
35
36
37 /* Filter signals and args */
38 enum
39 {
40   /* FILL ME */
41   LAST_SIGNAL
42 };
43
44 enum
45 {
46   ARG_0
47 };
48
49 static void     gst_pngenc_class_init   (GstPngEncClass *klass);
50 static void     gst_pngenc_init         (GstPngEnc *pngenc);
51
52 static void     gst_pngenc_chain        (GstPad *pad, GstBuffer *buf);
53
54 static GstElementClass *parent_class = NULL;
55
56
57 static void user_error_fn (png_structp png_ptr, png_const_charp error_msg)
58 {
59   g_warning("%s", error_msg);
60 }
61
62 static void user_warning_fn (png_structp png_ptr, png_const_charp warning_msg)
63 {
64   g_warning("%s", warning_msg);
65 }
66
67
68 GType gst_pngenc_get_type (void)
69 {
70   static GType pngenc_type = 0;
71
72   if (!pngenc_type) {
73     static const GTypeInfo pngenc_info = {
74       sizeof (GstPngEncClass), NULL,
75       NULL,
76       (GClassInitFunc) gst_pngenc_class_init,
77       NULL,
78       NULL,
79       sizeof (GstPngEnc),
80       0,
81       (GInstanceInitFunc) gst_pngenc_init,
82     };
83
84     pngenc_type = g_type_register_static (GST_TYPE_ELEMENT, "GstPngEnc",
85                                           &pngenc_info, 0);
86   }
87   return pngenc_type;
88 }
89
90 static void
91 gst_pngenc_class_init (GstPngEncClass *klass)
92 {
93   GObjectClass *gobject_class;
94   GstElementClass *gstelement_class;
95
96   gobject_class = (GObjectClass *) klass;
97   gstelement_class = (GstElementClass *) klass;
98
99   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
100 }
101
102
103 static GstPadLinkReturn
104 gst_pngenc_sinklink (GstPad *pad, GstCaps *caps)
105 {
106   GstPngEnc *pngenc;
107
108   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
109
110   if (!GST_CAPS_IS_FIXED (caps))
111     return GST_PAD_LINK_DELAYED;
112
113   gst_caps_get_int (caps, "width", &pngenc->width);
114   gst_caps_get_int (caps, "height", &pngenc->height);
115   gst_caps_get_int (caps, "bpp", &pngenc->bpp);
116
117   return gst_pad_try_set_caps (pngenc->srcpad, caps);
118 }
119
120 static void
121 gst_pngenc_init (GstPngEnc * pngenc)
122 {
123   pngenc->sinkpad = gst_pad_new_from_template (pngenc_sink_template, "sink");
124   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->sinkpad);
125
126   pngenc->srcpad = gst_pad_new ("src", GST_PAD_SRC);
127   gst_element_add_pad (GST_ELEMENT (pngenc), pngenc->srcpad);
128
129   gst_pad_set_chain_function (pngenc->sinkpad, gst_pngenc_chain);
130   gst_pad_set_link_function (pngenc->sinkpad, gst_pngenc_sinklink);
131
132   pngenc->png_struct_ptr = NULL;
133   pngenc->png_info_ptr = NULL;
134
135 }
136
137 void user_flush_data (png_structp png_ptr)
138 {
139 GstPngEnc *pngenc;
140
141   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
142
143   gst_pad_push (pngenc->srcpad, GST_BUFFER (gst_event_new (GST_EVENT_FLUSH)));
144 }
145
146
147 void user_write_data (png_structp png_ptr, png_bytep data, png_uint_32 length)
148 {
149   GstBuffer *buffer;
150   GstPngEnc *pngenc;
151
152   pngenc = (GstPngEnc *) png_get_io_ptr (png_ptr);
153
154   buffer = gst_buffer_new();
155   GST_BUFFER_DATA (buffer) = g_memdup (data, length);
156   GST_BUFFER_SIZE (buffer) = length;
157
158   if (pngenc->buffer_out)
159   {
160     pngenc->buffer_out = gst_buffer_merge (pngenc->buffer_out, buffer);
161     gst_buffer_unref (buffer);
162   }
163   else
164     pngenc->buffer_out = buffer;
165 }
166
167 static void
168 gst_pngenc_chain (GstPad *pad, GstBuffer *buf)
169 {
170   GstPngEnc *pngenc;
171   gint row_index;
172   png_byte *row_pointers[MAX_HEIGHT];
173   GstEvent *event;
174
175   pngenc = GST_PNGENC (gst_pad_get_parent (pad));
176
177   pngenc->buffer_out = NULL;
178   if (!GST_PAD_IS_USABLE (pngenc->srcpad))
179   {
180     gst_buffer_unref (buf);
181     return;
182   }
183
184   /* initialize png struct stuff */
185   pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
186                            (png_voidp) NULL, user_error_fn, user_warning_fn);
187   /* FIXME: better error handling */
188   if (pngenc->png_struct_ptr == NULL)
189      g_warning ("Failed to initialize png structure");
190
191   pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr);
192   if (!pngenc->png_info_ptr)
193   {
194     png_destroy_read_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL,
195                              (png_infopp) NULL);
196   }
197
198   /* non-0 return is from a longjmp inside of libpng */
199   if (setjmp (pngenc->png_struct_ptr->jmpbuf) != 0)
200   {
201     GST_DEBUG (GST_CAT_PLUGIN_INFO, "returning from longjmp");
202     png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr);
203     return;
204   }
205
206   png_set_filter (pngenc->png_struct_ptr, 0,
207                   PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE);
208   png_set_compression_level (pngenc->png_struct_ptr, 9);
209
210   png_set_IHDR(
211     pngenc->png_struct_ptr,
212     pngenc->png_info_ptr,
213     pngenc->width,
214     pngenc->height,
215     pngenc->bpp / 3,
216     PNG_COLOR_TYPE_RGB,
217     PNG_INTERLACE_NONE,
218     PNG_COMPRESSION_TYPE_DEFAULT,
219     PNG_FILTER_TYPE_DEFAULT
220   );
221
222   png_set_write_fn (pngenc->png_struct_ptr, pngenc,
223                     (png_rw_ptr) user_write_data, user_flush_data);
224
225   for (row_index = 0; row_index < pngenc->height; row_index++)
226     row_pointers[row_index] = GST_BUFFER_DATA (buf) +
227                               (pngenc->width * row_index * pngenc->bpp / 8);
228
229   png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr);
230   png_write_image (pngenc->png_struct_ptr, row_pointers);
231   png_write_end (pngenc->png_struct_ptr, NULL);
232
233   user_flush_data (pngenc->png_struct_ptr);
234
235   png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr);
236   png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL);
237
238   gst_pad_push (pngenc->srcpad, pngenc->buffer_out);
239
240   /* send EOS event, since a frame has been pushed out */
241   event = gst_event_new (GST_EVENT_EOS);
242   gst_pad_push (pngenc->srcpad, GST_BUFFER (event));
243
244   gst_buffer_unref (buf);
245 }