mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
b1984b72bb
When creating the caps allowed to upstream using downstream restrictions, use gst_pad_get_allowed_caps as that has the usable formats and puts into it the width, height and framerate fields. This avoids getting errors about getcaps returning non subset caps of its pad template. This error showed up on the metadata plugin unit test in -bad.
640 lines
19 KiB
C
640 lines
19 KiB
C
/* GStreamer
|
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
/**
|
|
* SECTION:element-jpegenc
|
|
*
|
|
* Encodes jpeg images.
|
|
*
|
|
* <refsect2>
|
|
* <title>Example launch line</title>
|
|
* |[
|
|
* gst-launch videotestsrc num-buffers=50 ! video/x-raw-yuv, framerate='(fraction)'5/1 ! jpegenc ! avimux ! filesink location=mjpeg.avi
|
|
* ]| a pipeline to mux 5 JPEG frames per second into a 10 sec. long motion jpeg
|
|
* avi.
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <string.h>
|
|
|
|
#include "gstjpegenc.h"
|
|
#include "gstjpeg.h"
|
|
#include <gst/video/video.h>
|
|
|
|
/* experimental */
|
|
/* setting smoothig seems to have no effect in libjepeg
|
|
#define ENABLE_SMOOTHING 1
|
|
*/
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (jpegenc_debug);
|
|
#define GST_CAT_DEFAULT jpegenc_debug
|
|
|
|
#define JPEG_DEFAULT_QUALITY 85
|
|
#define JPEG_DEFAULT_SMOOTHING 0
|
|
#define JPEG_DEFAULT_IDCT_METHOD JDCT_FASTEST
|
|
|
|
/* These macros are adapted from videotestsrc.c
|
|
* and/or gst-plugins/gst/games/gstvideoimage.c */
|
|
|
|
/* I420 */
|
|
#define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
|
|
#define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
|
|
#define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
|
|
|
|
#define I420_Y_OFFSET(w,h) (0)
|
|
#define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
|
|
#define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
|
|
|
|
#define I420_SIZE(w,h) (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
|
|
|
|
/* JpegEnc signals and args */
|
|
enum
|
|
{
|
|
FRAME_ENCODED,
|
|
/* FILL ME */
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_QUALITY,
|
|
PROP_SMOOTHING,
|
|
PROP_IDCT_METHOD
|
|
};
|
|
|
|
static void gst_jpegenc_base_init (gpointer g_class);
|
|
static void gst_jpegenc_class_init (GstJpegEnc * klass);
|
|
static void gst_jpegenc_init (GstJpegEnc * jpegenc);
|
|
static void gst_jpegenc_finalize (GObject * object);
|
|
|
|
static GstFlowReturn gst_jpegenc_chain (GstPad * pad, GstBuffer * buf);
|
|
static gboolean gst_jpegenc_setcaps (GstPad * pad, GstCaps * caps);
|
|
static GstCaps *gst_jpegenc_getcaps (GstPad * pad);
|
|
|
|
static void gst_jpegenc_resync (GstJpegEnc * jpegenc);
|
|
static void gst_jpegenc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_jpegenc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static GstStateChangeReturn gst_jpegenc_change_state (GstElement * element,
|
|
GstStateChange transition);
|
|
|
|
|
|
static GstElementClass *parent_class = NULL;
|
|
static guint gst_jpegenc_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
GType
|
|
gst_jpegenc_get_type (void)
|
|
{
|
|
static GType jpegenc_type = 0;
|
|
|
|
if (!jpegenc_type) {
|
|
static const GTypeInfo jpegenc_info = {
|
|
sizeof (GstJpegEnc),
|
|
(GBaseInitFunc) gst_jpegenc_base_init,
|
|
NULL,
|
|
(GClassInitFunc) gst_jpegenc_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof (GstJpegEnc),
|
|
0,
|
|
(GInstanceInitFunc) gst_jpegenc_init,
|
|
};
|
|
|
|
jpegenc_type =
|
|
g_type_register_static (GST_TYPE_ELEMENT, "GstJpegEnc", &jpegenc_info,
|
|
0);
|
|
}
|
|
return jpegenc_type;
|
|
}
|
|
|
|
static GstStaticPadTemplate gst_jpegenc_sink_pad_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
|
|
);
|
|
|
|
static GstStaticPadTemplate gst_jpegenc_src_pad_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("image/jpeg, "
|
|
"width = (int) [ 16, 4096 ], "
|
|
"height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0/1, MAX ]")
|
|
);
|
|
|
|
static void
|
|
gst_jpegenc_base_init (gpointer g_class)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
|
|
|
gst_element_class_add_pad_template (element_class,
|
|
gst_static_pad_template_get (&gst_jpegenc_sink_pad_template));
|
|
gst_element_class_add_pad_template (element_class,
|
|
gst_static_pad_template_get (&gst_jpegenc_src_pad_template));
|
|
gst_element_class_set_details_simple (element_class, "JPEG image encoder",
|
|
"Codec/Encoder/Image",
|
|
"Encode images in JPEG format", "Wim Taymans <wim.taymans@tvd.be>");
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_class_init (GstJpegEnc * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gstelement_class = (GstElementClass *) klass;
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
gst_jpegenc_signals[FRAME_ENCODED] =
|
|
g_signal_new ("frame-encoded", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstJpegEncClass, frame_encoded), NULL,
|
|
NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
|
|
|
|
gobject_class->set_property = gst_jpegenc_set_property;
|
|
gobject_class->get_property = gst_jpegenc_get_property;
|
|
|
|
|
|
g_object_class_install_property (gobject_class, PROP_QUALITY,
|
|
g_param_spec_int ("quality", "Quality", "Quality of encoding",
|
|
0, 100, JPEG_DEFAULT_QUALITY, G_PARAM_READWRITE));
|
|
|
|
#ifdef ENABLE_SMOOTHING
|
|
/* disabled, since it doesn't seem to work */
|
|
g_object_class_install_property (gobject_class, PROP_SMOOTHING,
|
|
g_param_spec_int ("smoothing", "Smoothing", "Smoothing factor",
|
|
0, 100, JPEG_DEFAULT_SMOOTHING, G_PARAM_READWRITE));
|
|
#endif
|
|
|
|
g_object_class_install_property (gobject_class, PROP_IDCT_METHOD,
|
|
g_param_spec_enum ("idct-method", "IDCT Method",
|
|
"The IDCT algorithm to use", GST_TYPE_IDCT_METHOD,
|
|
JPEG_DEFAULT_IDCT_METHOD, G_PARAM_READWRITE));
|
|
|
|
gstelement_class->change_state = gst_jpegenc_change_state;
|
|
|
|
gobject_class->finalize = gst_jpegenc_finalize;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (jpegenc_debug, "jpegenc", 0,
|
|
"JPEG encoding element");
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_init_destination (j_compress_ptr cinfo)
|
|
{
|
|
GST_DEBUG ("gst_jpegenc_chain: init_destination");
|
|
}
|
|
|
|
static boolean
|
|
gst_jpegenc_flush_destination (j_compress_ptr cinfo)
|
|
{
|
|
GstBuffer *overflow_buffer;
|
|
guint32 old_buffer_size;
|
|
GstJpegEnc *jpegenc = (GstJpegEnc *) (cinfo->client_data);
|
|
GST_DEBUG_OBJECT (jpegenc,
|
|
"gst_jpegenc_chain: flush_destination: buffer too small");
|
|
|
|
/* Our output buffer wasn't big enough.
|
|
* Make a new buffer that's twice the size, */
|
|
old_buffer_size = GST_BUFFER_SIZE (jpegenc->output_buffer);
|
|
gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad,
|
|
GST_BUFFER_OFFSET_NONE, old_buffer_size * 2,
|
|
GST_PAD_CAPS (jpegenc->srcpad), &overflow_buffer);
|
|
memcpy (GST_BUFFER_DATA (overflow_buffer),
|
|
GST_BUFFER_DATA (jpegenc->output_buffer), old_buffer_size);
|
|
|
|
gst_buffer_copy_metadata (overflow_buffer, jpegenc->output_buffer,
|
|
GST_BUFFER_COPY_TIMESTAMPS);
|
|
|
|
/* drop it into place, */
|
|
gst_buffer_unref (jpegenc->output_buffer);
|
|
jpegenc->output_buffer = overflow_buffer;
|
|
|
|
/* and last, update libjpeg on where to work. */
|
|
jpegenc->jdest.next_output_byte =
|
|
GST_BUFFER_DATA (jpegenc->output_buffer) + old_buffer_size;
|
|
jpegenc->jdest.free_in_buffer =
|
|
GST_BUFFER_SIZE (jpegenc->output_buffer) - old_buffer_size;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_term_destination (j_compress_ptr cinfo)
|
|
{
|
|
GstJpegEnc *jpegenc = (GstJpegEnc *) (cinfo->client_data);
|
|
GST_DEBUG_OBJECT (jpegenc, "gst_jpegenc_chain: term_source");
|
|
|
|
/* Trim the buffer size and push it. */
|
|
GST_BUFFER_SIZE (jpegenc->output_buffer) =
|
|
GST_ROUND_UP_4 (GST_BUFFER_SIZE (jpegenc->output_buffer) -
|
|
jpegenc->jdest.free_in_buffer);
|
|
|
|
g_signal_emit (G_OBJECT (jpegenc), gst_jpegenc_signals[FRAME_ENCODED], 0);
|
|
|
|
jpegenc->last_ret = gst_pad_push (jpegenc->srcpad, jpegenc->output_buffer);
|
|
jpegenc->output_buffer = NULL;
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_init (GstJpegEnc * jpegenc)
|
|
{
|
|
/* create the sink and src pads */
|
|
jpegenc->sinkpad =
|
|
gst_pad_new_from_static_template (&gst_jpegenc_sink_pad_template, "sink");
|
|
gst_pad_set_chain_function (jpegenc->sinkpad,
|
|
GST_DEBUG_FUNCPTR (gst_jpegenc_chain));
|
|
gst_pad_set_getcaps_function (jpegenc->sinkpad,
|
|
GST_DEBUG_FUNCPTR (gst_jpegenc_getcaps));
|
|
gst_pad_set_setcaps_function (jpegenc->sinkpad,
|
|
GST_DEBUG_FUNCPTR (gst_jpegenc_setcaps));
|
|
gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->sinkpad);
|
|
|
|
jpegenc->srcpad =
|
|
gst_pad_new_from_static_template (&gst_jpegenc_src_pad_template, "src");
|
|
gst_pad_use_fixed_caps (jpegenc->srcpad);
|
|
gst_element_add_pad (GST_ELEMENT (jpegenc), jpegenc->srcpad);
|
|
|
|
/* reset the initial video state */
|
|
jpegenc->width = -1;
|
|
jpegenc->height = -1;
|
|
|
|
/* setup jpeglib */
|
|
memset (&jpegenc->cinfo, 0, sizeof (jpegenc->cinfo));
|
|
memset (&jpegenc->jerr, 0, sizeof (jpegenc->jerr));
|
|
jpegenc->cinfo.err = jpeg_std_error (&jpegenc->jerr);
|
|
jpeg_create_compress (&jpegenc->cinfo);
|
|
|
|
jpegenc->jdest.init_destination = gst_jpegenc_init_destination;
|
|
jpegenc->jdest.empty_output_buffer = gst_jpegenc_flush_destination;
|
|
jpegenc->jdest.term_destination = gst_jpegenc_term_destination;
|
|
jpegenc->cinfo.dest = &jpegenc->jdest;
|
|
jpegenc->cinfo.client_data = jpegenc;
|
|
|
|
|
|
/* init properties */
|
|
jpegenc->quality = JPEG_DEFAULT_QUALITY;
|
|
jpegenc->smoothing = JPEG_DEFAULT_SMOOTHING;
|
|
jpegenc->idct_method = JPEG_DEFAULT_IDCT_METHOD;
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_finalize (GObject * object)
|
|
{
|
|
GstJpegEnc *filter = GST_JPEGENC (object);
|
|
|
|
jpeg_destroy_compress (&filter->cinfo);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_jpegenc_getcaps (GstPad * pad)
|
|
{
|
|
GstJpegEnc *jpegenc = GST_JPEGENC (gst_pad_get_parent (pad));
|
|
GstCaps *caps;
|
|
int i;
|
|
GstStructure *structure = NULL;
|
|
|
|
/* we want to proxy properties like width, height and framerate from the
|
|
other end of the element */
|
|
|
|
caps = gst_pad_get_allowed_caps (jpegenc->srcpad);
|
|
|
|
if (caps == NULL) {
|
|
caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
|
|
} else if (gst_caps_is_any (caps)) {
|
|
gst_caps_unref (caps);
|
|
caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
|
|
} else {
|
|
caps = gst_caps_make_writable (caps);
|
|
}
|
|
|
|
for (i = 0; i < gst_caps_get_size (caps); i++) {
|
|
structure = gst_caps_get_structure (caps, i);
|
|
|
|
gst_structure_set_name (structure, "video/x-raw-yuv");
|
|
gst_structure_set (structure, "format", GST_TYPE_FOURCC,
|
|
GST_STR_FOURCC ("I420"), NULL);
|
|
}
|
|
gst_object_unref (jpegenc);
|
|
|
|
return caps;
|
|
}
|
|
|
|
static gboolean
|
|
gst_jpegenc_setcaps (GstPad * pad, GstCaps * caps)
|
|
{
|
|
GstJpegEnc *jpegenc;
|
|
const GValue *framerate;
|
|
GstStructure *structure;
|
|
GstCaps *pcaps;
|
|
gboolean ret;
|
|
|
|
jpegenc = GST_JPEGENC (gst_pad_get_parent (pad));
|
|
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
framerate = gst_structure_get_value (structure, "framerate");
|
|
gst_structure_get_int (structure, "width", &jpegenc->width);
|
|
gst_structure_get_int (structure, "height", &jpegenc->height);
|
|
|
|
pcaps = gst_caps_new_simple ("image/jpeg",
|
|
"width", G_TYPE_INT, jpegenc->width,
|
|
"height", G_TYPE_INT, jpegenc->height, NULL);
|
|
structure = gst_caps_get_structure (pcaps, 0);
|
|
if (framerate)
|
|
gst_structure_set_value (structure, "framerate", framerate);
|
|
|
|
ret = gst_pad_set_caps (jpegenc->srcpad, pcaps);
|
|
|
|
if (ret)
|
|
gst_jpegenc_resync (jpegenc);
|
|
|
|
gst_caps_unref (pcaps);
|
|
gst_object_unref (jpegenc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_resync (GstJpegEnc * jpegenc)
|
|
{
|
|
gint width, height;
|
|
|
|
GST_DEBUG_OBJECT (jpegenc, "resync");
|
|
|
|
jpegenc->cinfo.image_width = width = jpegenc->width;
|
|
jpegenc->cinfo.image_height = height = jpegenc->height;
|
|
jpegenc->cinfo.input_components = 3;
|
|
|
|
GST_DEBUG_OBJECT (jpegenc, "width %d, height %d", width, height);
|
|
|
|
#ifdef ENABLE_COLORSPACE_RGB
|
|
switch (jpegenc->format) {
|
|
case GST_COLORSPACE_RGB24:
|
|
jpegenc->bufsize = jpegenc->width * jpegenc->height * 3;
|
|
GST_DEBUG ("gst_jpegenc_resync: setting format to RGB24");
|
|
jpegenc->cinfo.in_color_space = JCS_RGB;
|
|
jpegenc->cinfo.raw_data_in = FALSE;
|
|
break;
|
|
case GST_COLORSPACE_YUV420P:
|
|
#endif
|
|
GST_DEBUG_OBJECT (jpegenc, "setting format to YUV420P");
|
|
|
|
jpegenc->bufsize = I420_SIZE (jpegenc->width, jpegenc->height);
|
|
jpegenc->cinfo.in_color_space = JCS_YCbCr;
|
|
|
|
jpeg_set_defaults (&jpegenc->cinfo);
|
|
/* these are set in _chain()
|
|
jpeg_set_quality (&jpegenc->cinfo, jpegenc->quality, TRUE);
|
|
jpegenc->cinfo.smoothing_factor = jpegenc->smoothing;
|
|
jpegenc->cinfo.dct_method = jpegenc->idct_method;
|
|
*/
|
|
|
|
jpegenc->cinfo.raw_data_in = TRUE;
|
|
|
|
if (height != -1) {
|
|
jpegenc->line[0] =
|
|
g_realloc (jpegenc->line[0], height * sizeof (char *));
|
|
jpegenc->line[1] =
|
|
g_realloc (jpegenc->line[1], height * sizeof (char *) / 2);
|
|
jpegenc->line[2] =
|
|
g_realloc (jpegenc->line[2], height * sizeof (char *) / 2);
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (jpegenc, "setting format done");
|
|
#ifdef ENABLE_COLORSPACE_RGB
|
|
break;
|
|
default:
|
|
printf ("gst_jpegenc_resync: unsupported colorspace, using RGB\n");
|
|
jpegenc->bufsize = jpegenc->width * jpegenc->height * 3;
|
|
jpegenc->cinfo.in_color_space = JCS_RGB;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/* guard against a potential error in gst_jpegenc_term_destination
|
|
which occurs iff bufsize % 4 < free_space_remaining */
|
|
jpegenc->bufsize = GST_ROUND_UP_4 (jpegenc->bufsize);
|
|
|
|
jpeg_suppress_tables (&jpegenc->cinfo, TRUE);
|
|
|
|
GST_DEBUG_OBJECT (jpegenc, "resync done");
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_jpegenc_chain (GstPad * pad, GstBuffer * buf)
|
|
{
|
|
GstFlowReturn ret;
|
|
GstJpegEnc *jpegenc;
|
|
guchar *data;
|
|
gulong size;
|
|
guint height, width;
|
|
guchar *base[3], *end[3];
|
|
gint i, j, k;
|
|
|
|
jpegenc = GST_JPEGENC (GST_OBJECT_PARENT (pad));
|
|
|
|
if (G_UNLIKELY (jpegenc->width <= 0 || jpegenc->height <= 0))
|
|
goto not_negotiated;
|
|
|
|
data = GST_BUFFER_DATA (buf);
|
|
size = GST_BUFFER_SIZE (buf);
|
|
|
|
GST_LOG_OBJECT (jpegenc, "got buffer of %lu bytes", size);
|
|
|
|
ret =
|
|
gst_pad_alloc_buffer_and_set_caps (jpegenc->srcpad,
|
|
GST_BUFFER_OFFSET_NONE, jpegenc->bufsize, GST_PAD_CAPS (jpegenc->srcpad),
|
|
&jpegenc->output_buffer);
|
|
|
|
if (ret != GST_FLOW_OK)
|
|
goto done;
|
|
|
|
gst_buffer_copy_metadata (jpegenc->output_buffer, buf,
|
|
GST_BUFFER_COPY_TIMESTAMPS);
|
|
|
|
width = jpegenc->width;
|
|
height = jpegenc->height;
|
|
|
|
base[0] = data + I420_Y_OFFSET (width, height);
|
|
base[1] = data + I420_U_OFFSET (width, height);
|
|
base[2] = data + I420_V_OFFSET (width, height);
|
|
|
|
end[0] = base[0] + height * I420_Y_ROWSTRIDE (width);
|
|
end[1] = base[1] + (height / 2) * I420_U_ROWSTRIDE (width);
|
|
end[2] = base[2] + (height / 2) * I420_V_ROWSTRIDE (width);
|
|
|
|
/* FIXME: shouldn't we also set
|
|
* - jpegenc->cinfo.max_{v,h}_samp_factor
|
|
* - jpegenc->cinfo.comp_info[0,1,2].{v,h}_samp_factor
|
|
* accordingly?
|
|
*/
|
|
jpegenc->jdest.next_output_byte = GST_BUFFER_DATA (jpegenc->output_buffer);
|
|
jpegenc->jdest.free_in_buffer = GST_BUFFER_SIZE (jpegenc->output_buffer);
|
|
|
|
/* prepare for raw input */
|
|
#if JPEG_LIB_VERSION >= 70
|
|
jpegenc->cinfo.do_fancy_downsampling = FALSE;
|
|
#endif
|
|
jpegenc->cinfo.smoothing_factor = jpegenc->smoothing;
|
|
jpegenc->cinfo.dct_method = jpegenc->idct_method;
|
|
jpeg_set_quality (&jpegenc->cinfo, jpegenc->quality, TRUE);
|
|
jpeg_start_compress (&jpegenc->cinfo, TRUE);
|
|
|
|
GST_LOG_OBJECT (jpegenc, "compressing");
|
|
|
|
for (i = 0; i < height; i += 2 * DCTSIZE) {
|
|
/*g_print ("next scanline: %d\n", jpegenc->cinfo.next_scanline); */
|
|
for (j = 0, k = 0; j < (2 * DCTSIZE); j += 2, k++) {
|
|
jpegenc->line[0][j] = base[0];
|
|
if (base[0] + I420_Y_ROWSTRIDE (width) < end[0])
|
|
base[0] += I420_Y_ROWSTRIDE (width);
|
|
jpegenc->line[0][j + 1] = base[0];
|
|
if (base[0] + I420_Y_ROWSTRIDE (width) < end[0])
|
|
base[0] += I420_Y_ROWSTRIDE (width);
|
|
jpegenc->line[1][k] = base[1];
|
|
if (base[1] + I420_U_ROWSTRIDE (width) < end[1])
|
|
base[1] += I420_U_ROWSTRIDE (width);
|
|
jpegenc->line[2][k] = base[2];
|
|
if (base[2] + I420_V_ROWSTRIDE (width) < end[2])
|
|
base[2] += I420_V_ROWSTRIDE (width);
|
|
}
|
|
jpeg_write_raw_data (&jpegenc->cinfo, jpegenc->line, 2 * DCTSIZE);
|
|
}
|
|
|
|
/* This will ensure that gst_jpegenc_term_destination is called; we push
|
|
the final output buffer from there */
|
|
jpeg_finish_compress (&jpegenc->cinfo);
|
|
GST_LOG_OBJECT (jpegenc, "compressing done");
|
|
|
|
done:
|
|
gst_buffer_unref (buf);
|
|
|
|
return ret;
|
|
|
|
/* ERRORS */
|
|
not_negotiated:
|
|
{
|
|
GST_WARNING_OBJECT (jpegenc, "no input format set (no caps on buffer)");
|
|
ret = GST_FLOW_NOT_NEGOTIATED;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstJpegEnc *jpegenc = GST_JPEGENC (object);
|
|
|
|
GST_OBJECT_LOCK (jpegenc);
|
|
|
|
switch (prop_id) {
|
|
case PROP_QUALITY:
|
|
jpegenc->quality = g_value_get_int (value);
|
|
break;
|
|
#ifdef ENABLE_SMOOTHING
|
|
case PROP_SMOOTHING:
|
|
jpegenc->smoothing = g_value_get_int (value);
|
|
break;
|
|
#endif
|
|
case PROP_IDCT_METHOD:
|
|
jpegenc->idct_method = g_value_get_enum (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
|
|
GST_OBJECT_UNLOCK (jpegenc);
|
|
}
|
|
|
|
static void
|
|
gst_jpegenc_get_property (GObject * object, guint prop_id, GValue * value,
|
|
GParamSpec * pspec)
|
|
{
|
|
GstJpegEnc *jpegenc = GST_JPEGENC (object);
|
|
|
|
GST_OBJECT_LOCK (jpegenc);
|
|
|
|
switch (prop_id) {
|
|
case PROP_QUALITY:
|
|
g_value_set_int (value, jpegenc->quality);
|
|
break;
|
|
#ifdef ENABLE_SMOOTHING
|
|
case PROP_SMOOTHING:
|
|
g_value_set_int (value, jpegenc->smoothing);
|
|
break;
|
|
#endif
|
|
case PROP_IDCT_METHOD:
|
|
g_value_set_enum (value, jpegenc->idct_method);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
|
|
GST_OBJECT_UNLOCK (jpegenc);
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_jpegenc_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
|
GstJpegEnc *filter = GST_JPEGENC (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
GST_DEBUG_OBJECT (element, "setting line buffers");
|
|
filter->line[0] = NULL;
|
|
filter->line[1] = NULL;
|
|
filter->line[2] = NULL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
|
return ret;
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
g_free (filter->line[0]);
|
|
g_free (filter->line[1]);
|
|
g_free (filter->line[2]);
|
|
filter->line[0] = NULL;
|
|
filter->line[1] = NULL;
|
|
filter->line[2] = NULL;
|
|
filter->width = -1;
|
|
filter->height = -1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|