| /* GStreamer |
| * Copyright (C) 2010 Oblong Industries, Inc. |
| * Copyright (C) 2010 Collabora Multimedia |
| * |
| * 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-gstjp2kdecimator |
| * |
| * The jp2kdecimator element removes information from JPEG2000 images without reencoding. |
| * |
| * <refsect2> |
| * <title>Example launch line</title> |
| * |[ |
| * gst-launch -v videotestsrc num-buffers=1 ! jp2kenc ! \ |
| * gstjp2kdecimator max-decomposition-levels=2 ! jp2kdec ! \ |
| * ffmpegcolorspace ! autovideosink |
| * ]| |
| * This pipelines encodes a test image to JPEG2000, only keeps 3 decomposition levels |
| * decodes the decimated image again and shows it on the screen. |
| * </refsect2> |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/gst.h> |
| |
| #include "gstjp2kdecimator.h" |
| |
| #include "jp2kcodestream.h" |
| |
| #include <gst/base/gstbytereader.h> |
| #include <gst/base/gstbitreader.h> |
| #include <gst/base/gstbytewriter.h> |
| |
| #include <string.h> |
| |
| static GstStaticPadTemplate sink_pad_template = |
| GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("image/x-jpc")); |
| |
| static GstStaticPadTemplate src_pad_template = |
| GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("image/x-jpc")); |
| |
| enum |
| { |
| PROP_0, |
| PROP_MAX_LAYERS, |
| PROP_MAX_DECOMPOSITION_LEVELS |
| }; |
| |
| #define DEFAULT_MAX_LAYERS (0) |
| #define DEFAULT_MAX_DECOMPOSITION_LEVELS (-1) |
| |
| static void gst_jp2k_decimator_finalize (GObject * obj); |
| static void gst_jp2k_decimator_set_property (GObject * object, |
| guint prop_id, const GValue * value, GParamSpec * pspec); |
| static void gst_jp2k_decimator_get_property (GObject * object, |
| guint prop_id, GValue * value, GParamSpec * pspec); |
| |
| static GstFlowReturn gst_jp2k_decimator_sink_chain (GstPad * pad, |
| GstBuffer * inbuf); |
| static gboolean gst_jp2k_decimator_sink_setcaps (GstPad * pad, GstCaps * caps); |
| |
| static gboolean gst_jp2k_decimator_event (GstPad * pad, GstEvent * event); |
| static GstCaps *gst_jp2k_decimator_getcaps (GstPad * pad); |
| static gboolean gst_jp2k_decimator_query (GstPad * pad, GstQuery * query); |
| static const GstQueryType *gst_jp2k_decimator_query_type (GstPad * pad); |
| |
| GST_DEBUG_CATEGORY (gst_jp2k_decimator_debug); |
| #define GST_CAT_DEFAULT gst_jp2k_decimator_debug |
| |
| GST_BOILERPLATE (GstJP2kDecimator, gst_jp2k_decimator, GstElement, |
| GST_TYPE_ELEMENT); |
| |
| static void |
| gst_jp2k_decimator_base_init (gpointer g_class) |
| { |
| GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); |
| |
| gst_element_class_set_details_simple (gstelement_class, |
| "JPEG2000 decimator", |
| "Filter/Image", |
| "Removes information from JPEG2000 streams without recompression", |
| "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); |
| |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&sink_pad_template)); |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&src_pad_template)); |
| } |
| |
| static void |
| gst_jp2k_decimator_class_init (GstJP2kDecimatorClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| gobject_class->set_property = gst_jp2k_decimator_set_property; |
| gobject_class->get_property = gst_jp2k_decimator_get_property; |
| gobject_class->finalize = gst_jp2k_decimator_finalize; |
| |
| g_object_class_install_property (gobject_class, PROP_MAX_LAYERS, |
| g_param_spec_int ("max-layers", "Maximum Number of Layers", |
| "Maximum number of layers to keep (0 == all)", 0, 65535, |
| DEFAULT_MAX_LAYERS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| |
| g_object_class_install_property (gobject_class, PROP_MAX_DECOMPOSITION_LEVELS, |
| g_param_spec_int ("max-decomposition-levels", |
| "Maximum Number of Decomposition Levels", |
| "Maximum number of decomposition levels to keep (-1 == all)", -1, 32, |
| DEFAULT_MAX_DECOMPOSITION_LEVELS, |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
| } |
| |
| static void |
| gst_jp2k_decimator_init (GstJP2kDecimator * self, |
| GstJP2kDecimatorClass * g_class) |
| { |
| self->max_layers = DEFAULT_MAX_LAYERS; |
| self->max_decomposition_levels = DEFAULT_MAX_DECOMPOSITION_LEVELS; |
| |
| self->sinkpad = gst_pad_new_from_static_template (&sink_pad_template, "sink"); |
| gst_pad_set_chain_function (self->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_sink_chain)); |
| gst_pad_set_event_function (self->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_event)); |
| gst_pad_set_setcaps_function (self->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_sink_setcaps)); |
| gst_pad_set_getcaps_function (self->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_getcaps)); |
| gst_pad_set_query_function (self->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_query)); |
| gst_pad_set_query_type_function (self->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_query_type)); |
| gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); |
| |
| self->srcpad = gst_pad_new_from_static_template (&src_pad_template, "src"); |
| gst_pad_set_event_function (self->srcpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_event)); |
| gst_pad_set_getcaps_function (self->srcpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_getcaps)); |
| gst_pad_set_query_function (self->srcpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_query)); |
| gst_pad_set_query_type_function (self->srcpad, |
| GST_DEBUG_FUNCPTR (gst_jp2k_decimator_query_type)); |
| gst_element_add_pad (GST_ELEMENT (self), self->srcpad); |
| } |
| |
| static void |
| gst_jp2k_decimator_finalize (GObject * obj) |
| { |
| G_OBJECT_CLASS (parent_class)->finalize (obj); |
| } |
| |
| static void |
| gst_jp2k_decimator_set_property (GObject * object, |
| guint prop_id, const GValue * value, GParamSpec * pspec) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (object); |
| |
| switch (prop_id) { |
| case PROP_MAX_LAYERS: |
| self->max_layers = g_value_get_int (value); |
| break; |
| case PROP_MAX_DECOMPOSITION_LEVELS: |
| self->max_decomposition_levels = g_value_get_int (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gst_jp2k_decimator_get_property (GObject * object, |
| guint prop_id, GValue * value, GParamSpec * pspec) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (object); |
| |
| switch (prop_id) { |
| case PROP_MAX_LAYERS: |
| g_value_set_int (value, self->max_layers); |
| break; |
| case PROP_MAX_DECOMPOSITION_LEVELS: |
| g_value_set_int (value, self->max_decomposition_levels); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static gboolean |
| gst_jp2k_decimator_sink_setcaps (GstPad * pad, GstCaps * caps) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (gst_pad_get_parent (pad)); |
| gboolean ret; |
| |
| GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps); |
| |
| ret = gst_pad_set_caps (self->srcpad, caps); |
| |
| gst_object_unref (self); |
| |
| return ret; |
| } |
| |
| static GstCaps * |
| gst_jp2k_decimator_getcaps (GstPad * pad) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (gst_pad_get_parent (pad)); |
| GstPad *otherpad; |
| GstCaps *tmp, *ret; |
| |
| otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; |
| |
| tmp = gst_pad_peer_get_caps_reffed (otherpad); |
| if (tmp) { |
| ret = gst_caps_intersect (tmp, gst_pad_get_pad_template_caps (pad)); |
| gst_caps_unref (tmp); |
| } else { |
| ret = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); |
| } |
| |
| gst_object_unref (self); |
| |
| GST_LOG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, ret); |
| |
| return ret; |
| } |
| |
| static gboolean |
| gst_jp2k_decimator_query (GstPad * pad, GstQuery * query) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (gst_pad_get_parent (pad)); |
| gboolean ret; |
| GstPad *otherpad; |
| |
| otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; |
| |
| GST_LOG_OBJECT (pad, "Handling query of type '%s'", |
| gst_query_type_get_name (GST_QUERY_TYPE (query))); |
| |
| ret = gst_pad_peer_query (otherpad, query); |
| |
| gst_object_unref (self); |
| return ret; |
| } |
| |
| static const GstQueryType * |
| gst_jp2k_decimator_query_type (GstPad * pad) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (gst_pad_get_parent (pad)); |
| GstPad *otherpad, *peer; |
| const GstQueryType *types = NULL; |
| |
| otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; |
| peer = gst_pad_get_peer (otherpad); |
| if (peer) { |
| types = gst_pad_get_query_types (peer); |
| gst_object_unref (peer); |
| } |
| |
| gst_object_unref (self); |
| |
| return types; |
| } |
| |
| static gboolean |
| gst_jp2k_decimator_event (GstPad * pad, GstEvent * event) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (gst_pad_get_parent (pad)); |
| GstPad *otherpad; |
| gboolean ret; |
| |
| GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); |
| |
| otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; |
| ret = gst_pad_push_event (otherpad, event); |
| |
| gst_object_unref (self); |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_jp2k_decimator_decimate_jpc (GstJP2kDecimator * self, GstBuffer * inbuf, |
| GstBuffer ** outbuf_) |
| { |
| GstBuffer *outbuf = NULL; |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (inbuf); |
| GstByteWriter writer; |
| MainHeader main_header; |
| |
| gst_byte_writer_init_with_size (&writer, GST_BUFFER_SIZE (inbuf), FALSE); |
| |
| /* main header */ |
| memset (&main_header, 0, sizeof (MainHeader)); |
| ret = parse_main_header (self, &reader, &main_header); |
| if (ret != GST_FLOW_OK) |
| goto done; |
| |
| ret = decimate_main_header (self, &main_header); |
| if (ret != GST_FLOW_OK) |
| goto done; |
| |
| ret = write_main_header (self, &writer, &main_header); |
| if (ret != GST_FLOW_OK) |
| goto done; |
| |
| outbuf = gst_byte_writer_reset_and_get_buffer (&writer); |
| gst_buffer_copy_metadata (outbuf, inbuf, GST_BUFFER_COPY_ALL); |
| |
| GST_DEBUG_OBJECT (self, |
| "Decimated buffer from %u bytes to %u bytes (%.2lf%%)", |
| GST_BUFFER_SIZE (inbuf), GST_BUFFER_SIZE (outbuf), |
| (100 * GST_BUFFER_SIZE (outbuf)) / ((gdouble) GST_BUFFER_SIZE (inbuf))); |
| |
| done: |
| *outbuf_ = outbuf; |
| reset_main_header (self, &main_header); |
| gst_buffer_unref (inbuf); |
| |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_jp2k_decimator_sink_chain (GstPad * pad, GstBuffer * inbuf) |
| { |
| GstJP2kDecimator *self = GST_JP2K_DECIMATOR (GST_PAD_PARENT (pad)); |
| GstFlowReturn ret; |
| GstBuffer *outbuf; |
| |
| GST_LOG_OBJECT (pad, |
| "Handling inbuf with timestamp %" GST_TIME_FORMAT " and duration %" |
| GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)), |
| GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf))); |
| |
| if (self->max_layers == 0 && self->max_decomposition_levels == -1) { |
| outbuf = inbuf; |
| inbuf = NULL; |
| ret = GST_FLOW_OK; |
| } else { |
| ret = gst_jp2k_decimator_decimate_jpc (self, inbuf, &outbuf); |
| } |
| |
| if (G_UNLIKELY (ret != GST_FLOW_OK)) |
| return ret; |
| |
| ret = gst_pad_push (self->srcpad, outbuf); |
| |
| return ret; |
| } |
| |
| static gboolean |
| plugin_init (GstPlugin * plugin) |
| { |
| GST_DEBUG_CATEGORY_INIT (gst_jp2k_decimator_debug, "jp2kdecimator", 0, |
| "JPEG2000 decimator"); |
| |
| gst_element_register (plugin, "jp2kdecimator", GST_RANK_NONE, |
| gst_jp2k_decimator_get_type ()); |
| |
| return TRUE; |
| } |
| |
| GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
| GST_VERSION_MINOR, |
| jp2kdecimator, |
| "JPEG2000 decimator", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, |
| GST_PACKAGE_ORIGIN) |