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