2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 * SECTION:element-jpegenc
22 * Encodes jpeg images.
25 * <title>Example launch line</title>
27 * gst-launch videotestsrc num-buffers=50 ! video/x-raw-yuv, framerate='(fraction)'5/1 ! jpegenc ! avimux ! filesink location=mjpeg.avi
28 * ]| a pipeline to mux 5 JPEG frames per second into a 10 sec. long motion jpeg
38 #include "gstjpegenc.h"
39 #include <gst/video/video.h>
42 /* setting smoothig seems to have no effect in libjepeg
43 #define ENABLE_SMOOTHING 1
45 /*#define ENABLE_COLORSPACE_RGB 1 */
47 /* elementfactory information */
48 static const GstElementDetails gst_jpegenc_details =
49 GST_ELEMENT_DETAILS ("JPEG image encoder",
50 "Codec/Encoder/Image",
51 "Encode images in JPEG format",
52 "Wim Taymans <wim.taymans@tvd.be>");
54 GST_DEBUG_CATEGORY_STATIC (jpegenc_debug);
55 #define GST_CAT_DEFAULT jpegenc_debug
57 #define JPEG_DEFAULT_QUALITY 85
58 #define JPEG_DEFAULT_SMOOTHING 0
59 #define JPEG_DEFAULT_IDCT_METHOD JDCT_FASTEST
61 /* These macros are adapted from videotestsrc.c
62 * and/or gst-plugins/gst/games/gstvideoimage.c */
65 #define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
66 #define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
67 #define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
69 #define I420_Y_OFFSET(w,h) (0)
70 #define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
71 #define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
73 #define I420_SIZE(w,h) (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
75 /* JpegEnc signals and args */
91 extern GType gst_idct_method_get_type (void);
92 #define GST_TYPE_IDCT_METHOD (gst_idct_method_get_type())
94 static void gst_jpegenc_base_init (gpointer g_class);
95 static void gst_jpegenc_class_init (GstJpegEnc * klass);
96 static void gst_jpegenc_init (GstJpegEnc * jpegenc);
97 static void gst_jpegenc_finalize (GObject * object);
99 static GstFlowReturn gst_jpegenc_chain (GstPad * pad, GstBuffer * buf);
100 static gboolean gst_jpegenc_setcaps (GstPad * pad, GstCaps * caps);
101 static GstCaps *gst_jpegenc_getcaps (GstPad * pad);
103 static void gst_jpegenc_resync (GstJpegEnc * jpegenc);
104 static void gst_jpegenc_set_property (GObject * object, guint prop_id,
105 const GValue * value, GParamSpec * pspec);
106 static void gst_jpegenc_get_property (GObject * object, guint prop_id,
107 GValue * value, GParamSpec * pspec);
108 static GstStateChangeReturn gst_jpegenc_change_state (GstElement * element,
109 GstStateChange transition);
112 static GstElementClass *parent_class = NULL;
113 static guint gst_jpegenc_signals[LAST_SIGNAL] = { 0 };
116 gst_jpegenc_get_type (void)
118 static GType jpegenc_type = 0;
121 static const GTypeInfo jpegenc_info = {
123 (GBaseInitFunc) gst_jpegenc_base_init,
125 (GClassInitFunc) gst_jpegenc_class_init,
130 (GInstanceInitFunc) gst_jpegenc_init,
134 g_type_register_static (GST_TYPE_ELEMENT, "GstJpegEnc", &jpegenc_info,
140 static GstStaticPadTemplate gst_jpegenc_sink_pad_template =
141 GST_STATIC_PAD_TEMPLATE ("sink",
144 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
147 static GstStaticPadTemplate gst_jpegenc_src_pad_template =
148 GST_STATIC_PAD_TEMPLATE ("src",
151 GST_STATIC_CAPS ("image/jpeg, "
152 "width = (int) [ 16, 4096 ], "
153 "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0/1, MAX ]")
157 gst_jpegenc_base_init (gpointer g_class)
159 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
161 gst_element_class_add_pad_template (element_class,
162 gst_static_pad_template_get (&gst_jpegenc_sink_pad_template));
163 gst_element_class_add_pad_template (element_class,
164 gst_static_pad_template_get (&gst_jpegenc_src_pad_template));
165 gst_element_class_set_details (element_class, &gst_jpegenc_details);
169 gst_jpegenc_class_init (GstJpegEnc * klass)
171 GObjectClass *gobject_class;
172 GstElementClass *gstelement_class;
174 gobject_class = (GObjectClass *) klass;
175 gstelement_class = (GstElementClass *) klass;
177 parent_class = g_type_class_peek_parent (klass);
179 gst_jpegenc_signals[FRAME_ENCODED] =
180 g_signal_new ("frame-encoded", G_TYPE_FROM_CLASS (klass),
181 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstJpegEncClass, frame_encoded), NULL,
182 NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
184 gobject_class->set_property = gst_jpegenc_set_property;
185 gobject_class->get_property = gst_jpegenc_get_property;
188 g_object_class_install_property (gobject_class, PROP_QUALITY,
189 g_param_spec_int ("quality", "Quality", "Quality of encoding",
190 0, 100, JPEG_DEFAULT_QUALITY, G_PARAM_READWRITE));
193 /* disabled, since it doesn't seem to work */
194 g_object_class_install_property (gobject_class, PROP_SMOOTHING,
195 g_param_spec_int ("smoothing", "Smoothing", "Smoothing factor",
196 0, 100, JPEG_DEFAULT_SMOOTHING, G_PARAM_READWRITE));
199 g_object_class_install_property (gobject_class, PROP_IDCT_METHOD,
200 g_param_spec_enum ("idct-method", "IDCT Method",
201 "The IDCT algorithm to use", GST_TYPE_IDCT_METHOD,
202 JPEG_DEFAULT_IDCT_METHOD, G_PARAM_READWRITE));
204 gstelement_class->change_state = gst_jpegenc_change_state;
206 gobject_class->finalize = gst_jpegenc_finalize;
208 GST_DEBUG_CATEGORY_INIT (jpegenc_debug, "jpegenc", 0,
209 "JPEG encoding element");
213 gst_jpegenc_init_destination (j_compress_ptr cinfo)
215 GST_DEBUG ("gst_jpegenc_chain: init_destination");
219 gst_jpegenc_flush_destination (j_compress_ptr cinfo)
221 GstBuffer *overflow_buffer;
222 guint32 old_buffer_size;
223 GstJpegEnc *jpegenc = (GstJpegEnc *) (cinfo->client_data);
224 GST_DEBUG_OBJECT (jpegenc,
225 "gst_jpegenc_chain: flush_destination: buffer too small");
227 /* Our output buffer wasn't big enough.
228 * Make a new buffer that's twice the size, */
229 old_buffer_size = GST_BUFFER_SIZE (jpegenc->output_buffer);
230 gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad,
231 GST_BUFFER_OFFSET_NONE, old_buffer_size * 2,
232 GST_PAD_CAPS (jpegenc->srcpad), &overflow_buffer);
233 memcpy (GST_BUFFER_DATA (overflow_buffer),
234 GST_BUFFER_DATA (jpegenc->output_buffer), old_buffer_size);
236 gst_buffer_copy_metadata (overflow_buffer, jpegenc->output_buffer,
237 GST_BUFFER_COPY_TIMESTAMPS);
239 /* drop it into place, */
240 gst_buffer_unref (jpegenc->output_buffer);
241 jpegenc->output_buffer = overflow_buffer;
243 /* and last, update libjpeg on where to work. */
244 jpegenc->jdest.next_output_byte =
245 GST_BUFFER_DATA (jpegenc->output_buffer) + old_buffer_size;
246 jpegenc->jdest.free_in_buffer =
247 GST_BUFFER_SIZE (jpegenc->output_buffer) - old_buffer_size;
253 gst_jpegenc_term_destination (j_compress_ptr cinfo)
255 GstJpegEnc *jpegenc = (GstJpegEnc *) (cinfo->client_data);
256 GST_DEBUG_OBJECT (jpegenc, "gst_jpegenc_chain: term_source");
258 /* Trim the buffer size and push it. */
259 GST_BUFFER_SIZE (jpegenc->output_buffer) =
260 GST_ROUND_UP_4 (GST_BUFFER_SIZE (jpegenc->output_buffer) -
261 jpegenc->jdest.free_in_buffer);
263 g_signal_emit (G_OBJECT (jpegenc), gst_jpegenc_signals[FRAME_ENCODED], 0);
265 jpegenc->last_ret = gst_pad_push (jpegenc->srcpad, jpegenc->output_buffer);
266 jpegenc->output_buffer = NULL;
270 gst_jpegenc_init (GstJpegEnc * jpegenc)
272 /* create the sink and src pads */
274 gst_pad_new_from_static_template (&gst_jpegenc_sink_pad_template, "sink");
275 gst_pad_set_chain_function (jpegenc->sinkpad,
276 GST_DEBUG_FUNCPTR (gst_jpegenc_chain));
277 gst_pad_set_getcaps_function (jpegenc->sinkpad,
278 GST_DEBUG_FUNCPTR (gst_jpegenc_getcaps));
279 gst_pad_set_setcaps_function (jpegenc->sinkpad,
280 GST_DEBUG_FUNCPTR (gst_jpegenc_setcaps));
281 gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->sinkpad);
284 gst_pad_new_from_static_template (&gst_jpegenc_src_pad_template, "src");
285 gst_pad_set_getcaps_function (jpegenc->sinkpad,
286 GST_DEBUG_FUNCPTR (gst_jpegenc_getcaps));
287 /*gst_pad_set_setcaps_function (jpegenc->sinkpad, gst_jpegenc_setcaps); */
288 gst_pad_use_fixed_caps (jpegenc->sinkpad);
289 gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->srcpad);
291 /* reset the initial video state */
293 jpegenc->height = -1;
296 memset (&jpegenc->cinfo, 0, sizeof (jpegenc->cinfo));
297 memset (&jpegenc->jerr, 0, sizeof (jpegenc->jerr));
298 jpegenc->cinfo.err = jpeg_std_error (&jpegenc->jerr);
299 jpeg_create_compress (&jpegenc->cinfo);
301 jpegenc->jdest.init_destination = gst_jpegenc_init_destination;
302 jpegenc->jdest.empty_output_buffer = gst_jpegenc_flush_destination;
303 jpegenc->jdest.term_destination = gst_jpegenc_term_destination;
304 jpegenc->cinfo.dest = &jpegenc->jdest;
305 jpegenc->cinfo.client_data = jpegenc;
308 /* init properties */
309 jpegenc->quality = JPEG_DEFAULT_QUALITY;
310 jpegenc->smoothing = JPEG_DEFAULT_SMOOTHING;
311 jpegenc->idct_method = JPEG_DEFAULT_IDCT_METHOD;
315 gst_jpegenc_finalize (GObject * object)
317 GstJpegEnc *filter = GST_JPEGENC (object);
319 jpeg_destroy_compress (&filter->cinfo);
321 G_OBJECT_CLASS (parent_class)->finalize (object);
325 gst_jpegenc_getcaps (GstPad * pad)
327 GstJpegEnc *jpegenc = GST_JPEGENC (gst_pad_get_parent (pad));
332 GstStructure *structure = NULL;
334 /* we want to proxy properties like width, height and framerate from the
335 other end of the element */
336 otherpad = (pad == jpegenc->srcpad) ? jpegenc->sinkpad : jpegenc->srcpad;
338 caps = gst_pad_peer_get_caps (otherpad);
340 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
342 caps = gst_caps_make_writable (caps);
344 if (pad == jpegenc->srcpad) {
347 name = "video/x-raw-yuv";
350 for (i = 0; i < gst_caps_get_size (caps); i++) {
351 structure = gst_caps_get_structure (caps, i);
353 gst_structure_set_name (structure, name);
354 gst_structure_remove_field (structure, "format");
355 /* ... but for the sink pad, we only do I420 anyway, so add that */
356 if (pad == jpegenc->sinkpad) {
357 gst_structure_set (structure, "format", GST_TYPE_FOURCC,
358 GST_STR_FOURCC ("I420"), NULL);
361 gst_object_unref (jpegenc);
367 gst_jpegenc_setcaps (GstPad * pad, GstCaps * caps)
369 GstJpegEnc *jpegenc = GST_JPEGENC (gst_pad_get_parent (pad));
370 GstStructure *structure;
374 const GValue *framerate;
376 otherpad = (pad == jpegenc->srcpad) ? jpegenc->sinkpad : jpegenc->srcpad;
378 structure = gst_caps_get_structure (caps, 0);
379 framerate = gst_structure_get_value (structure, "framerate");
380 gst_structure_get_int (structure, "width", &jpegenc->width);
381 gst_structure_get_int (structure, "height", &jpegenc->height);
383 othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (otherpad));
385 gst_caps_set_simple (othercaps,
386 "width", G_TYPE_INT, jpegenc->width,
387 "height", G_TYPE_INT, jpegenc->height,
388 "framerate", GST_TYPE_FRACTION,
389 gst_value_get_fraction_numerator (framerate),
390 gst_value_get_fraction_denominator (framerate), NULL);
392 gst_caps_set_simple (othercaps,
393 "width", G_TYPE_INT, jpegenc->width,
394 "height", G_TYPE_INT, jpegenc->height, NULL);
397 ret = gst_pad_set_caps (jpegenc->srcpad, othercaps);
398 gst_caps_unref (othercaps);
401 gst_jpegenc_resync (jpegenc);
403 gst_object_unref (jpegenc);
409 gst_jpegenc_resync (GstJpegEnc * jpegenc)
413 GST_DEBUG_OBJECT (jpegenc, "resync");
415 jpegenc->cinfo.image_width = width = jpegenc->width;
416 jpegenc->cinfo.image_height = height = jpegenc->height;
417 jpegenc->cinfo.input_components = 3;
419 GST_DEBUG_OBJECT (jpegenc, "width %d, height %d", width, height);
421 #if ENABLE_COLORSPACE_RGB
422 switch (jpegenc->format) {
423 case GST_COLORSPACE_RGB24:
424 jpegenc->bufsize = jpegenc->width * jpegenc->height * 3;
425 GST_DEBUG ("gst_jpegenc_resync: setting format to RGB24");
426 jpegenc->cinfo.in_color_space = JCS_RGB;
427 jpegenc->cinfo.raw_data_in = FALSE;
429 case GST_COLORSPACE_YUV420P:
431 GST_DEBUG_OBJECT (jpegenc, "setting format to YUV420P");
433 jpegenc->bufsize = I420_SIZE (jpegenc->width, jpegenc->height);
434 jpegenc->cinfo.in_color_space = JCS_YCbCr;
436 jpeg_set_defaults (&jpegenc->cinfo);
437 /* these are set in _chain()
438 jpeg_set_quality (&jpegenc->cinfo, jpegenc->quality, TRUE);
439 jpegenc->cinfo.smoothing_factor = jpegenc->smoothing;
440 jpegenc->cinfo.dct_method = jpegenc->idct_method;
443 jpegenc->cinfo.raw_data_in = TRUE;
447 g_realloc (jpegenc->line[0], height * sizeof (char *));
449 g_realloc (jpegenc->line[1], height * sizeof (char *) / 2);
451 g_realloc (jpegenc->line[2], height * sizeof (char *) / 2);
454 GST_DEBUG_OBJECT (jpegenc, "setting format done");
455 #if ENABLE_COLORSPACE_RGB
458 printf ("gst_jpegenc_resync: unsupported colorspace, using RGB\n");
459 jpegenc->bufsize = jpegenc->width * jpegenc->height * 3;
460 jpegenc->cinfo.in_color_space = JCS_RGB;
465 /* guard against a potential error in gst_jpegenc_term_destination
466 which occurs iff bufsize % 4 < free_space_remaining */
467 jpegenc->bufsize = GST_ROUND_UP_4 (jpegenc->bufsize);
469 jpeg_suppress_tables (&jpegenc->cinfo, TRUE);
471 GST_DEBUG_OBJECT (jpegenc, "resync done");
475 gst_jpegenc_chain (GstPad * pad, GstBuffer * buf)
482 guchar *base[3], *end[3];
485 jpegenc = GST_JPEGENC (GST_OBJECT_PARENT (pad));
487 if (G_UNLIKELY (jpegenc->width <= 0 || jpegenc->height <= 0))
490 data = GST_BUFFER_DATA (buf);
491 size = GST_BUFFER_SIZE (buf);
493 GST_LOG_OBJECT (jpegenc, "got buffer of %lu bytes", size);
496 gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad,
497 GST_BUFFER_OFFSET_NONE, jpegenc->bufsize, GST_PAD_CAPS (jpegenc->srcpad),
498 &jpegenc->output_buffer);
500 if (ret != GST_FLOW_OK)
503 gst_buffer_copy_metadata (jpegenc->output_buffer, buf,
504 GST_BUFFER_COPY_TIMESTAMPS);
506 width = jpegenc->width;
507 height = jpegenc->height;
509 base[0] = data + I420_Y_OFFSET (width, height);
510 base[1] = data + I420_U_OFFSET (width, height);
511 base[2] = data + I420_V_OFFSET (width, height);
513 end[0] = base[0] + height * I420_Y_ROWSTRIDE (width);
514 end[1] = base[1] + (height / 2) * I420_U_ROWSTRIDE (width);
515 end[2] = base[2] + (height / 2) * I420_V_ROWSTRIDE (width);
517 jpegenc->jdest.next_output_byte = GST_BUFFER_DATA (jpegenc->output_buffer);
518 jpegenc->jdest.free_in_buffer = GST_BUFFER_SIZE (jpegenc->output_buffer);
520 /* prepare for raw input */
521 #if JPEG_LIB_VERSION >= 70
522 jpegenc->cinfo.do_fancy_downsampling = FALSE;
524 jpegenc->cinfo.smoothing_factor = jpegenc->smoothing;
525 jpegenc->cinfo.dct_method = jpegenc->idct_method;
526 jpeg_set_quality (&jpegenc->cinfo, jpegenc->quality, TRUE);
527 jpeg_start_compress (&jpegenc->cinfo, TRUE);
529 GST_LOG_OBJECT (jpegenc, "compressing");
531 for (i = 0; i < height; i += 2 * DCTSIZE) {
532 /*g_print ("next scanline: %d\n", jpegenc->cinfo.next_scanline); */
533 for (j = 0, k = 0; j < (2 * DCTSIZE); j += 2, k++) {
534 jpegenc->line[0][j] = base[0];
535 if (base[0] + I420_Y_ROWSTRIDE (width) < end[0])
536 base[0] += I420_Y_ROWSTRIDE (width);
537 jpegenc->line[0][j + 1] = base[0];
538 if (base[0] + I420_Y_ROWSTRIDE (width) < end[0])
539 base[0] += I420_Y_ROWSTRIDE (width);
540 jpegenc->line[1][k] = base[1];
541 if (base[1] + I420_U_ROWSTRIDE (width) < end[1])
542 base[1] += I420_U_ROWSTRIDE (width);
543 jpegenc->line[2][k] = base[2];
544 if (base[2] + I420_V_ROWSTRIDE (width) < end[2])
545 base[2] += I420_V_ROWSTRIDE (width);
547 jpeg_write_raw_data (&jpegenc->cinfo, jpegenc->line, 2 * DCTSIZE);
550 /* This will ensure that gst_jpegenc_term_destination is called; we push
551 the final output buffer from there */
552 jpeg_finish_compress (&jpegenc->cinfo);
553 GST_LOG_OBJECT (jpegenc, "compressing done");
556 gst_buffer_unref (buf);
563 GST_WARNING_OBJECT (jpegenc, "no input format set (no caps on buffer)");
564 ret = GST_FLOW_NOT_NEGOTIATED;
570 gst_jpegenc_set_property (GObject * object, guint prop_id,
571 const GValue * value, GParamSpec * pspec)
573 GstJpegEnc *jpegenc = GST_JPEGENC (object);
575 GST_OBJECT_LOCK (jpegenc);
579 jpegenc->quality = g_value_get_int (value);
583 jpegenc->smoothing = g_value_get_int (value);
586 case PROP_IDCT_METHOD:
587 jpegenc->idct_method = g_value_get_enum (value);
590 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
594 GST_OBJECT_UNLOCK (jpegenc);
598 gst_jpegenc_get_property (GObject * object, guint prop_id, GValue * value,
601 GstJpegEnc *jpegenc = GST_JPEGENC (object);
603 GST_OBJECT_LOCK (jpegenc);
607 g_value_set_int (value, jpegenc->quality);
611 g_value_set_int (value, jpegenc->smoothing);
614 case PROP_IDCT_METHOD:
615 g_value_set_enum (value, jpegenc->idct_method);
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
622 GST_OBJECT_UNLOCK (jpegenc);
625 static GstStateChangeReturn
626 gst_jpegenc_change_state (GstElement * element, GstStateChange transition)
628 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
629 GstJpegEnc *filter = GST_JPEGENC (element);
631 switch (transition) {
632 case GST_STATE_CHANGE_NULL_TO_READY:
633 GST_DEBUG_OBJECT (element, "setting line buffers");
634 filter->line[0] = NULL;
635 filter->line[1] = NULL;
636 filter->line[2] = NULL;
642 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
643 if (ret == GST_STATE_CHANGE_FAILURE)
646 switch (transition) {
647 case GST_STATE_CHANGE_READY_TO_NULL:
648 g_free (filter->line[0]);
649 g_free (filter->line[1]);
650 g_free (filter->line[2]);
651 filter->line[0] = NULL;
652 filter->line[1] = NULL;
653 filter->line[2] = NULL;