--- /dev/null
+/* 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)
--- /dev/null
+/* 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "jp2kcodestream.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_jp2k_decimator_debug);
+#define GST_CAT_DEFAULT gst_jp2k_decimator_debug
+
+/* Delimiting markers and marker segments */
+#define MARKER_SOC 0xFF4F
+#define MARKER_SOT 0xFF90
+#define MARKER_SOD 0xFF93
+#define MARKER_EOC 0xFFD9
+
+/* Fixed information marker segments */
+#define MARKER_SIZ 0xFF51
+
+/* Functional marker segments */
+#define MARKER_COD 0xFF52
+#define MARKER_COC 0xFF53
+#define MARKER_RGN 0xFF5E
+#define MARKER_QCD 0xFF5C
+#define MARKER_QCC 0xFF5D
+#define MARKER_POC 0xFF5F
+
+/* Pointer marker segments */
+#define MARKER_PLM 0xFF57
+#define MARKER_PLT 0xFF58
+#define MARKER_PPM 0xFF60
+#define MARKER_PPT 0xFF61
+#define MARKER_TLM 0xFF55
+
+/* In-bit-stream markers and marker segments */
+#define MARKER_SOP 0xFF91
+#define MARKER_EPH 0xFF92
+
+/* Informational marker segments */
+#define MARKER_CRG 0xFF63
+#define MARKER_COM 0xFF64
+
+static void
+packet_iterator_changed_resolution_or_component (PacketIterator * it)
+{
+ gint tx0, tx1, ty0, ty1;
+ gint tcx0, tcx1, tcy0, tcy1;
+ gint trx0, trx1, try0, try1;
+ gint tpx0, tpx1, tpy0, tpy1;
+ gint two_nl_r;
+ gint two_ppx, two_ppy;
+ gint xr, yr;
+ guint8 *PPx, *PPy;
+
+ tx0 = it->tile->tx0;
+ tx1 = it->tile->tx1;
+ ty0 = it->tile->ty0;
+ ty1 = it->tile->ty1;
+
+ it->two_nl_r = two_nl_r = (1 << (it->n_resolutions - it->cur_resolution - 1));
+
+ PPx = it->tile->cod ? it->tile->cod->PPx : it->header->cod.PPx;
+ PPy = it->tile->cod ? it->tile->cod->PPy : it->header->cod.PPy;
+ it->two_ppx = two_ppx = (1 << (PPx ? PPx[it->cur_resolution] : 15));
+ it->two_ppy = two_ppy = (1 << (PPy ? PPy[it->cur_resolution] : 15));
+
+ it->xr = xr = it->header->siz.components[it->cur_component].xr;
+ it->yr = yr = it->header->siz.components[it->cur_component].yr;
+
+ it->tcx0 = tcx0 = (tx0 + xr - 1) / xr;
+ it->tcx1 = tcx1 = (tx1 + xr - 1) / xr;
+ it->tcy0 = tcy0 = (ty0 + yr - 1) / yr;
+ it->tcy1 = tcy1 = (ty1 + yr - 1) / yr;
+
+ it->trx0 = trx0 = (tcx0 + two_nl_r - 1) / two_nl_r;
+ it->trx1 = trx1 = (tcx1 + two_nl_r - 1) / two_nl_r;
+ it->try0 = try0 = (tcy0 + two_nl_r - 1) / two_nl_r;
+ it->try1 = try1 = (tcy1 + two_nl_r - 1) / two_nl_r;
+
+ it->tpx0 = tpx0 = two_ppx * (trx0 / two_ppx);
+ it->tpx1 = tpx1 = two_ppx * ((trx1 + two_ppx - 1) / two_ppx);
+ it->tpy0 = tpy0 = two_ppy * (try0 / two_ppy);
+ it->tpy1 = tpy1 = two_ppy * ((try1 + two_ppy - 1) / two_ppy);
+
+ it->n_precincts_w = (trx0 == trx1) ? 0 : (tpx1 - tpx0) / two_ppx;
+ it->n_precincts_h = (try0 == try1) ? 0 : (tpy1 - tpy0) / two_ppy;
+ it->n_precincts = it->n_precincts_w * it->n_precincts_h;
+}
+
+static gboolean
+packet_iterator_next_lrcp (PacketIterator * it)
+{
+ g_return_val_if_fail (it->cur_layer < it->n_layers, FALSE);
+
+ if (it->first) {
+ packet_iterator_changed_resolution_or_component (it);
+ it->first = FALSE;
+ return TRUE;
+ }
+
+ it->cur_precinct += 1;
+ if (it->cur_precinct >= it->n_precincts) {
+ it->cur_precinct = 0;
+
+ it->cur_component += 1;
+ if (it->cur_component >= it->n_components) {
+ it->cur_component = 0;
+
+ it->cur_resolution += 1;
+ if (it->cur_resolution >= it->n_resolutions) {
+ it->cur_resolution = 0;
+ it->cur_layer += 1;
+ if (it->cur_layer >= it->n_layers) {
+ it->cur_packet++;
+ return FALSE;
+ }
+ }
+ }
+ packet_iterator_changed_resolution_or_component (it);
+ }
+
+ it->cur_packet++;
+
+ return TRUE;
+}
+
+static gboolean
+packet_iterator_next_rlcp (PacketIterator * it)
+{
+ g_return_val_if_fail (it->cur_resolution < it->n_resolutions, FALSE);
+
+ if (it->first) {
+ packet_iterator_changed_resolution_or_component (it);
+ it->first = FALSE;
+ return TRUE;
+ }
+
+ it->cur_precinct += 1;
+ if (it->cur_precinct >= it->n_precincts) {
+ it->cur_precinct = 0;
+
+ it->cur_component += 1;
+ if (it->cur_component >= it->n_components) {
+ it->cur_component = 0;
+
+ it->cur_layer += 1;
+ if (it->cur_layer >= it->n_layers) {
+ it->cur_layer = 0;
+ it->cur_resolution += 1;
+ if (it->cur_resolution >= it->n_resolutions) {
+ it->cur_packet++;
+ return FALSE;
+ }
+ }
+ }
+ packet_iterator_changed_resolution_or_component (it);
+ }
+
+ it->cur_packet++;
+
+ return TRUE;
+}
+
+static gboolean
+packet_iterator_next_rpcl (PacketIterator * it)
+{
+ g_return_val_if_fail (it->cur_resolution < it->n_resolutions, FALSE);
+
+ if (it->first) {
+ packet_iterator_changed_resolution_or_component (it);
+ it->first = FALSE;
+ return TRUE;
+ }
+
+ it->cur_layer += 1;
+ if (it->cur_layer >= it->n_layers) {
+ it->cur_layer = 0;
+
+ /* Loop and advance the position and resolution until
+ * we find the next precinct
+ */
+ while (TRUE) {
+ it->cur_component += 1;
+ if (it->cur_component >= it->n_components) {
+ it->cur_component = 0;
+
+ it->cur_x += it->x_step - (it->cur_x % it->x_step);
+ if (it->cur_x >= it->tx1) {
+ it->cur_x = it->tx0;
+
+ it->cur_y += it->y_step - (it->cur_y % it->y_step);
+ if (it->cur_y >= it->ty1) {
+ it->cur_y = it->ty0;
+
+ it->cur_resolution += 1;
+
+ if (it->cur_resolution >= it->n_resolutions) {
+ it->cur_packet++;
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ packet_iterator_changed_resolution_or_component (it);
+
+ if (((it->cur_y % (it->yr * it->two_ppy * it->two_nl_r) == 0)
+ || (it->cur_y == it->ty0
+ && ((it->try0 * it->two_nl_r) %
+ (it->two_ppy * it->two_nl_r) != 0)))
+ && ((it->cur_x % (it->xr * it->two_ppy * it->two_nl_r) == 0)
+ || (it->cur_x == it->tx0
+ && ((it->trx0 * it->two_nl_r) %
+ (it->two_ppx * it->two_nl_r) != 0)))) {
+ gint k;
+
+ k = (((it->cur_x + it->xr * it->two_nl_r - 1) /
+ (it->xr * it->two_nl_r)) / it->two_ppx) -
+ (it->trx0 / it->two_ppx) +
+ it->n_precincts_w *
+ (((it->cur_y + it->yr * it->two_nl_r - 1) /
+ (it->yr * it->two_nl_r)) / it->two_ppy);
+
+ g_assert (k < it->n_precincts);
+
+ it->cur_precinct = k;
+ break;
+ }
+ }
+ }
+
+ it->cur_packet++;
+
+ return TRUE;
+}
+
+static gboolean
+packet_iterator_next_pcrl (PacketIterator * it)
+{
+ g_return_val_if_fail (it->cur_resolution < it->n_resolutions, FALSE);
+
+ if (it->first) {
+ it->first = FALSE;
+ return TRUE;
+ }
+
+ it->cur_layer += 1;
+ if (it->cur_layer >= it->n_layers) {
+ it->cur_layer = 0;
+
+ /* Loop and advance the position and resolution until
+ * we find the next precinct
+ */
+ while (TRUE) {
+ it->cur_resolution += 1;
+ if (it->cur_resolution >= it->n_resolutions) {
+ it->cur_resolution = 0;
+
+ it->cur_component += 1;
+ if (it->cur_component >= it->n_components) {
+
+ it->cur_x += it->x_step - (it->cur_x % it->x_step);
+ if (it->cur_x >= it->tx1) {
+ it->cur_x = it->tx0;
+
+ it->cur_y += it->y_step - (it->cur_y % it->y_step);
+ if (it->cur_y >= it->ty1) {
+ it->cur_packet++;
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ packet_iterator_changed_resolution_or_component (it);
+
+ if (((it->cur_y % (it->yr * it->two_ppy * it->two_nl_r) == 0)
+ || (it->cur_y == it->ty0
+ && ((it->try0 * it->two_nl_r) %
+ (it->two_ppy * it->two_nl_r) != 0)))
+ && ((it->cur_x % (it->xr * it->two_ppy * it->two_nl_r) == 0)
+ || (it->cur_x == it->tx0
+ && ((it->trx0 * it->two_nl_r) %
+ (it->two_ppx * it->two_nl_r) != 0)))) {
+ gint k;
+
+ k = (((it->cur_x + it->xr * it->two_nl_r - 1) /
+ (it->xr * it->two_nl_r)) / it->two_ppx) -
+ (it->trx0 / it->two_ppx) +
+ it->n_precincts_w *
+ (((it->cur_y + it->yr * it->two_nl_r - 1) /
+ (it->yr * it->two_nl_r)) / it->two_ppy);
+
+ g_assert (k < it->n_precincts);
+
+ it->cur_precinct = k;
+ break;
+ }
+ }
+ }
+
+ it->cur_packet++;
+
+ return TRUE;
+}
+
+static gboolean
+packet_iterator_next_cprl (PacketIterator * it)
+{
+ g_return_val_if_fail (it->cur_resolution < it->n_resolutions, FALSE);
+
+ if (it->first) {
+ packet_iterator_changed_resolution_or_component (it);
+ it->first = FALSE;
+ return TRUE;
+ }
+
+ it->cur_layer += 1;
+ if (it->cur_layer >= it->n_layers) {
+ it->cur_layer = 0;
+
+ /* Loop and advance the position and resolution until
+ * we find the next precinct
+ */
+ while (TRUE) {
+ it->cur_resolution += 1;
+ if (it->cur_resolution >= it->n_resolutions) {
+ it->cur_resolution = 0;
+
+ it->cur_x += it->x_step - (it->cur_x % it->x_step);
+ if (it->cur_x >= it->tx1) {
+ it->cur_x = it->tx0;
+
+ it->cur_y += it->y_step - (it->cur_y % it->y_step);
+ if (it->cur_y >= it->ty1) {
+ it->cur_y = it->ty0;
+
+ it->cur_component += 1;
+
+ if (it->cur_component >= it->n_components) {
+ it->cur_packet++;
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ packet_iterator_changed_resolution_or_component (it);
+
+ if (((it->cur_y % (it->yr * it->two_ppy * it->two_nl_r) == 0)
+ || (it->cur_y == it->ty0
+ && ((it->try0 * it->two_nl_r) %
+ (it->two_ppy * it->two_nl_r) != 0)))
+ && ((it->cur_x % (it->xr * it->two_ppy * it->two_nl_r) == 0)
+ || (it->cur_x == it->tx0
+ && ((it->trx0 * it->two_nl_r) %
+ (it->two_ppx * it->two_nl_r) != 0)))) {
+ gint k;
+
+ k = (((it->cur_x + it->xr * it->two_nl_r - 1) /
+ (it->xr * it->two_nl_r)) / it->two_ppx) -
+ (it->trx0 / it->two_ppx) +
+ it->n_precincts_w *
+ (((it->cur_y + it->yr * it->two_nl_r - 1) /
+ (it->yr * it->two_nl_r)) / it->two_ppy);
+
+ g_assert (k < it->n_precincts);
+
+ it->cur_precinct = k;
+ break;
+ }
+ }
+ }
+
+ it->cur_packet++;
+
+ return TRUE;
+}
+
+static GstFlowReturn
+init_packet_iterator (GstJP2kDecimator * self, PacketIterator * it,
+ const MainHeader * header, const Tile * tile)
+{
+ ProgressionOrder order;
+ gint i, j;
+
+ memset (it, 0, sizeof (PacketIterator));
+
+ it->header = header;
+ it->tile = tile;
+
+ it->first = TRUE;
+
+ it->n_layers = (tile->cod) ? tile->cod->n_layers : header->cod.n_layers;
+ it->n_resolutions =
+ 1 +
+ ((tile->cod) ? tile->cod->n_decompositions : header->
+ cod.n_decompositions);
+ it->n_components = header->siz.n_components;
+
+ it->tx0 = tile->tx0;
+ it->tx1 = tile->tx1;
+ it->ty0 = tile->ty0;
+ it->ty1 = tile->ty1;
+
+ it->cur_x = it->tx0;
+ it->cur_y = it->ty0;
+
+ /* Calculate the step sizes for the position-dependent progression orders */
+ it->x_step = it->y_step = 0;
+ for (i = 0; i < it->n_components; i++) {
+ gint xr, yr;
+
+ xr = header->siz.components[i].xr;
+ yr = header->siz.components[i].yr;
+
+
+ for (j = 0; j < it->n_resolutions; j++) {
+ gint xs, ys;
+ guint8 PPx, PPy;
+
+ if (tile->cod) {
+ PPx = (tile->cod->PPx) ? tile->cod->PPx[j] : 15;
+ PPy = (tile->cod->PPy) ? tile->cod->PPy[j] : 15;
+ } else {
+ PPx = (header->cod.PPx) ? header->cod.PPx[j] : 15;
+ PPy = (header->cod.PPy) ? header->cod.PPy[j] : 15;
+ }
+
+ xs = xr * (1 << (PPx + it->n_resolutions - j - 1));
+ ys = yr * (1 << (PPy + it->n_resolutions - j - 1));
+
+ if (it->x_step == 0 || it->x_step > xs)
+ it->x_step = xs;
+ if (it->y_step == 0 || it->y_step > ys)
+ it->y_step = ys;
+ }
+ }
+
+ order =
+ (tile->cod) ? tile->cod->progression_order : header->
+ cod.progression_order;
+ if (order == PROGRESSION_ORDER_LRCP) {
+ it->next = packet_iterator_next_lrcp;
+ } else if (order == PROGRESSION_ORDER_RLCP) {
+ it->next = packet_iterator_next_rlcp;
+ } else if (order == PROGRESSION_ORDER_RPCL) {
+ it->next = packet_iterator_next_rpcl;
+ } else if (order == PROGRESSION_ORDER_PCRL) {
+ it->next = packet_iterator_next_pcrl;
+ } else if (order == PROGRESSION_ORDER_CPRL) {
+ it->next = packet_iterator_next_cprl;
+ } else {
+ GST_ERROR_OBJECT (self, "Progression order %d not supported", order);
+ return GST_FLOW_ERROR;
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+parse_siz (GstJP2kDecimator * self, GstByteReader * reader,
+ ImageSize * siz, guint16 length)
+{
+ gint i;
+
+ if (length < 38) {
+ GST_ERROR_OBJECT (self, "Invalid SIZ marker");
+ return GST_FLOW_ERROR;
+ }
+
+ siz->caps = gst_byte_reader_get_uint16_be_unchecked (reader);
+ siz->x = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->y = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->xo = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->yo = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->xt = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->yt = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->xto = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->yto = gst_byte_reader_get_uint32_be_unchecked (reader);
+ siz->n_components = gst_byte_reader_get_uint16_be_unchecked (reader);
+
+ if (length < 38 + 3 * siz->n_components) {
+ GST_ERROR_OBJECT (self, "Invalid SIZ marker");
+ return GST_FLOW_ERROR;
+ }
+
+ siz->components = g_slice_alloc (sizeof (ComponentSize) * siz->n_components);
+ for (i = 0; i < siz->n_components; i++) {
+ siz->components[i].s = gst_byte_reader_get_uint8_unchecked (reader);
+ siz->components[i].xr = gst_byte_reader_get_uint8_unchecked (reader);
+ siz->components[i].yr = gst_byte_reader_get_uint8_unchecked (reader);
+ }
+
+ return GST_FLOW_OK;
+}
+
+static guint
+sizeof_siz (GstJP2kDecimator * self, const ImageSize * siz)
+{
+ return 2 + 38 + 3 * siz->n_components;
+}
+
+static void
+reset_siz (GstJP2kDecimator * self, ImageSize * siz)
+{
+ if (siz->components)
+ g_slice_free1 (sizeof (ComponentSize) * siz->n_components, siz->components);
+ memset (siz, 0, sizeof (ImageSize));
+}
+
+static GstFlowReturn
+write_siz (GstJP2kDecimator * self, GstByteWriter * writer,
+ const ImageSize * siz)
+{
+ gint i;
+
+ if (!gst_byte_writer_ensure_free_space (writer,
+ 2 + 38 + 3 * siz->n_components)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_byte_writer_put_uint16_be_unchecked (writer, MARKER_SIZ);
+ gst_byte_writer_put_uint16_be_unchecked (writer, 38 + 3 * siz->n_components);
+ gst_byte_writer_put_uint16_be_unchecked (writer, siz->caps);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->x);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->y);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->xo);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->yo);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->xt);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->yt);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->xto);
+ gst_byte_writer_put_uint32_be_unchecked (writer, siz->yto);
+ gst_byte_writer_put_uint16_be_unchecked (writer, siz->n_components);
+
+ for (i = 0; i < siz->n_components; i++) {
+ gst_byte_writer_put_uint8_unchecked (writer, siz->components[i].s);
+ gst_byte_writer_put_uint8_unchecked (writer, siz->components[i].xr);
+ gst_byte_writer_put_uint8_unchecked (writer, siz->components[i].yr);
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+parse_cod (GstJP2kDecimator * self, GstByteReader * reader,
+ CodingStyleDefault * cod, guint16 length)
+{
+ guint8 Scod;
+
+ if (length < 12) {
+ GST_ERROR_OBJECT (self, "Invalid COD marker");
+ return GST_FLOW_ERROR;
+ }
+
+ Scod = gst_byte_reader_get_uint8_unchecked (reader);
+ cod->sop = ! !(Scod & 0x02);
+ cod->eph = ! !(Scod & 0x04);
+
+ /* SGcod */
+ cod->progression_order = gst_byte_reader_get_uint8_unchecked (reader);
+ cod->n_layers = gst_byte_reader_get_uint16_be_unchecked (reader);
+ cod->multi_component_transform = gst_byte_reader_get_uint8_unchecked (reader);
+
+ /* SPcod */
+ cod->n_decompositions = gst_byte_reader_get_uint8_unchecked (reader);
+ cod->xcb = gst_byte_reader_get_uint8_unchecked (reader) + 2;
+ cod->ycb = gst_byte_reader_get_uint8_unchecked (reader) + 2;
+ cod->code_block_style = gst_byte_reader_get_uint8_unchecked (reader);
+ cod->transformation = gst_byte_reader_get_uint8_unchecked (reader);
+
+ if ((Scod & 0x01)) {
+ gint i;
+ /* User defined precincts */
+
+ if (length < 12 + (Scod & 0x01) * (cod->n_decompositions + 1)) {
+ GST_ERROR_OBJECT (self, "Invalid COD marker");
+ return GST_FLOW_ERROR;
+ }
+
+ cod->PPx = g_slice_alloc (sizeof (guint8) * (cod->n_decompositions + 1));
+ for (i = 0; i < cod->n_decompositions + 1; i++) {
+ guint8 v = gst_byte_reader_get_uint8_unchecked (reader);
+ cod->PPx[i] = (v & 0x0f);
+ cod->PPy[i] = (v >> 4);
+ }
+ }
+
+ return GST_FLOW_OK;
+}
+
+static guint
+sizeof_cod (GstJP2kDecimator * self, const CodingStyleDefault * cod)
+{
+ return 2 + 12 + (cod->PPx ? (cod->n_decompositions + 1) : 0);
+}
+
+static void
+reset_cod (GstJP2kDecimator * self, CodingStyleDefault * cod)
+{
+ if (cod->PPx)
+ g_slice_free1 (sizeof (guint8) * (cod->n_decompositions + 1), cod->PPx);
+ if (cod->PPy)
+ g_slice_free1 (sizeof (guint8) * (cod->n_decompositions + 1), cod->PPy);
+ memset (cod, 0, sizeof (CodingStyleDefault));
+}
+
+static GstFlowReturn
+write_cod (GstJP2kDecimator * self, GstByteWriter * writer,
+ const CodingStyleDefault * cod)
+{
+ guint tmp;
+
+ tmp = 12 + (cod->PPx ? (1 + cod->n_decompositions) : 0);
+ if (!gst_byte_writer_ensure_free_space (writer, tmp)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_byte_writer_put_uint16_be_unchecked (writer, MARKER_COD);
+ gst_byte_writer_put_uint16_be_unchecked (writer, tmp);
+
+ /* Scod */
+ tmp =
+ (cod->PPx ? 0x01 : 0x00) | (cod->
+ sop ? 0x02 : 0x00) | (cod->eph ? 0x04 : 0x00);
+ gst_byte_writer_put_uint8_unchecked (writer, tmp);
+
+ /* SGcod */
+ gst_byte_writer_put_uint8_unchecked (writer, cod->progression_order);
+ gst_byte_writer_put_uint16_be_unchecked (writer, cod->n_layers);
+ gst_byte_writer_put_uint8_unchecked (writer, cod->multi_component_transform);
+
+ /* SPcod */
+ gst_byte_writer_put_uint8_unchecked (writer, cod->n_decompositions);
+ gst_byte_writer_put_uint8_unchecked (writer, cod->xcb - 2);
+ gst_byte_writer_put_uint8_unchecked (writer, cod->ycb - 2);
+ gst_byte_writer_put_uint8_unchecked (writer, cod->code_block_style);
+ gst_byte_writer_put_uint8_unchecked (writer, cod->transformation);
+
+ if (cod->PPx) {
+ gint i;
+
+ for (i = 0; i < cod->n_decompositions + 1; i++) {
+ tmp = (cod->PPx[i]) | (cod->PPy[i] << 4);
+ gst_byte_writer_put_uint8_unchecked (writer, tmp);
+ }
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+parse_plt (GstJP2kDecimator * self, GstByteReader * reader,
+ PacketLengthTilePart * plt, guint length)
+{
+ guint32 n;
+ guint8 b = 0;
+ gint i;
+
+ if (length < 3) {
+ GST_ERROR_OBJECT (self, "Invalid PLT");
+ return GST_FLOW_ERROR;
+ }
+
+ plt->index = gst_byte_reader_get_uint8_unchecked (reader);
+ plt->packet_lengths = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+ length -= 3;
+
+ n = 0;
+ for (i = 0; i < length; i++) {
+ b = gst_byte_reader_get_uint8_unchecked (reader);
+
+ if ((n & 0xfe000000)) {
+ GST_ERROR_OBJECT (self, "PLT element overflow");
+ return GST_FLOW_ERROR;
+ }
+
+ n = (n << 7) | (b & 0x7f);
+ if ((b & 0x80) == 0x00) {
+ g_array_append_val (plt->packet_lengths, n);
+ n = 0;
+ }
+ }
+
+ if ((b & 0x80) != 0x00) {
+ GST_ERROR_OBJECT (self, "Truncated PLT");
+ return GST_FLOW_ERROR;
+ }
+ return GST_FLOW_OK;
+}
+
+static guint
+sizeof_plt (GstJP2kDecimator * self, const PacketLengthTilePart * plt)
+{
+ guint size = 2 + 3;
+ gint i, n;
+
+ n = plt->packet_lengths->len;
+ for (i = 0; i < n; i++) {
+ guint32 len = g_array_index (plt->packet_lengths, guint32, i);
+
+ if (len < (1 << 7)) {
+ size += 1;
+ } else if (len < (1 << 14)) {
+ size += 2;
+ } else if (len < (1 << 21)) {
+ size += 3;
+ } else if (len < (1 << 28)) {
+ size += 4;
+ } else {
+ size += 5;
+ }
+ }
+
+ return size;
+}
+
+static void
+reset_plt (GstJP2kDecimator * self, PacketLengthTilePart * plt)
+{
+ if (plt->packet_lengths)
+ g_array_free (plt->packet_lengths, TRUE);
+ memset (plt, 0, sizeof (PacketLengthTilePart));
+}
+
+static GstFlowReturn
+write_plt (GstJP2kDecimator * self, GstByteWriter * writer,
+ const PacketLengthTilePart * plt)
+{
+ gint i, n;
+ guint plt_start_pos, plt_end_pos;
+
+ if (!gst_byte_writer_ensure_free_space (writer, 2 + 2 + 1)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_byte_writer_put_uint16_be_unchecked (writer, MARKER_PLT);
+ plt_start_pos = gst_byte_writer_get_pos (writer);
+ gst_byte_writer_put_uint16_be_unchecked (writer, 0);
+
+ gst_byte_writer_put_uint8_unchecked (writer, plt->index);
+
+ n = plt->packet_lengths->len;
+ for (i = 0; i < n; i++) {
+ guint32 len = g_array_index (plt->packet_lengths, guint32, i);
+
+ /* FIXME: Write multiple plt here */
+ if (gst_byte_writer_get_pos (writer) - plt_start_pos > 65535 - 5) {
+ GST_ERROR_OBJECT (self, "Too big PLT");
+ return GST_FLOW_ERROR;
+ }
+
+ if (len < (1 << 7)) {
+ if (!gst_byte_writer_ensure_free_space (writer, 1)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+ gst_byte_writer_put_uint8_unchecked (writer, (0x00 | (len & 0x7f)));
+ } else if (len < (1 << 14)) {
+ if (!gst_byte_writer_ensure_free_space (writer, 2)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 7) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer, (0x00 | (len & 0x7f)));
+ } else if (len < (1 << 21)) {
+ if (!gst_byte_writer_ensure_free_space (writer, 3)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 14) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 7) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer, (0x00 | (len & 0x7f)));
+ } else if (len < (1 << 28)) {
+ if (!gst_byte_writer_ensure_free_space (writer, 4)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 21) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 14) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 7) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer, (0x00 | (len & 0x7f)));
+ } else {
+ if (!gst_byte_writer_ensure_free_space (writer, 5)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 28) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 21) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 14) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer,
+ (0x80 | ((len >> 7) & 0x7f)));
+ gst_byte_writer_put_uint8_unchecked (writer, (0x00 | (len & 0x7f)));
+ }
+ }
+
+ plt_end_pos = gst_byte_writer_get_pos (writer);
+ gst_byte_writer_set_pos (writer, plt_start_pos);
+ gst_byte_writer_put_uint16_be (writer, plt_end_pos - plt_start_pos);
+ gst_byte_writer_set_pos (writer, plt_end_pos);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+parse_packet (GstJP2kDecimator * self, GstByteReader * reader,
+ const MainHeader * header, Tile * tile, const PacketIterator * it)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint16 marker, length;
+ guint16 seqno;
+ guint packet_start_pos;
+ const guint8 *packet_start_data;
+ gboolean sop, eph;
+ PacketLengthTilePart *plt = NULL;
+
+ sop = (tile->cod) ? tile->cod->sop : header->cod.sop;
+ eph = (tile->cod) ? tile->cod->eph : header->cod.eph;
+ if (tile->plt) {
+ if (g_list_length (tile->plt) > 1) {
+ GST_ERROR_OBJECT (self,
+ "Only a single PLT per tile is supported currently");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ plt = tile->plt->data;
+ }
+
+ if (plt) {
+ guint32 length;
+ Packet *p;
+
+ if (plt->packet_lengths->len <= it->cur_packet) {
+ GST_ERROR_OBJECT (self, "Truncated PLT");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ length = g_array_index (plt->packet_lengths, guint32, it->cur_packet);
+
+ if (gst_byte_reader_get_remaining (reader) < length) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ p = g_slice_new0 (Packet);
+
+ /* If there is a SOP keep the seqno */
+ if (sop && length > 6) {
+ if (!gst_byte_reader_peek_uint16_be (reader, &marker)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (marker == MARKER_SOP) {
+ guint16 dummy;
+
+ gst_byte_reader_skip_unchecked (reader, 2);
+
+ if (!gst_byte_reader_get_uint16_be (reader, &dummy)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (!gst_byte_reader_get_uint16_be (reader, &seqno)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length - 6;
+ p->sop = TRUE;
+ p->eph = eph;
+ p->seqno = seqno;
+ gst_byte_reader_skip_unchecked (reader, length - 6);
+ }
+ }
+
+ if (!p->data) {
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length;
+ p->sop = FALSE;
+ p->eph = eph;
+ gst_byte_reader_skip_unchecked (reader, length);
+ }
+
+ tile->packets = g_list_prepend (tile->packets, p);
+ } else if (sop) {
+ if (!gst_byte_reader_peek_uint16_be (reader, &marker)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (marker != MARKER_SOP) {
+ GST_ERROR_OBJECT (self, "No SOP marker");
+ ret = GST_FLOW_UNEXPECTED;
+ goto done;
+ }
+
+ gst_byte_reader_skip_unchecked (reader, 2);
+
+ if (!gst_byte_reader_get_uint16_be (reader, &length)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (!gst_byte_reader_get_uint16_be (reader, &seqno)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ packet_start_data = reader->data + reader->byte;
+ packet_start_pos = gst_byte_reader_get_pos (reader);
+
+ /* Find end of packet */
+ while (TRUE) {
+ if (!gst_byte_reader_peek_uint16_be (reader, &marker)) {
+ GST_ERROR_OBJECT (self, "Truncated file");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (marker == MARKER_SOP || marker == MARKER_EOC || marker == MARKER_SOT) {
+ Packet *p = g_slice_new (Packet);
+
+ p->sop = TRUE;
+ p->eph = eph;
+ p->seqno = seqno;
+ p->data = packet_start_data;
+ p->length = reader->byte - packet_start_pos;
+ tile->packets = g_list_prepend (tile->packets, p);
+
+ if (marker == MARKER_EOC || marker == MARKER_SOT)
+ goto done;
+ else
+ break;
+ }
+
+ gst_byte_reader_skip_unchecked (reader, 1);
+ }
+ } else {
+ GST_ERROR_OBJECT (self, "Either PLT or SOP are required");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+done:
+
+ return ret;
+}
+
+static guint
+sizeof_packet (GstJP2kDecimator * self, const Packet * packet)
+{
+ return packet->length + (packet->sop ? 6 : 0) + ((packet->eph
+ && !packet->data) ? 2 : 0);
+}
+
+static GstFlowReturn
+parse_packets (GstJP2kDecimator * self, GstByteReader * reader,
+ const MainHeader * header, Tile * tile)
+{
+ guint16 marker = 0;
+ GstFlowReturn ret = GST_FLOW_OK;
+ PacketIterator it;
+
+ /* Start of data here */
+ if (!gst_byte_reader_get_uint16_be (reader, &marker)
+ && marker != MARKER_SOD) {
+ GST_ERROR_OBJECT (self, "No SOD in tile");
+ return GST_FLOW_ERROR;
+ }
+
+ ret = init_packet_iterator (self, &it, header, tile);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ while ((it.next (&it))) {
+ ret = parse_packet (self, reader, header, tile, &it);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ tile->packets = g_list_reverse (tile->packets);
+
+done:
+
+ return ret;
+}
+
+static GstFlowReturn
+parse_tile (GstJP2kDecimator * self, GstByteReader * reader,
+ const MainHeader * header, Tile * tile)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint16 marker, length;
+
+ if (!gst_byte_reader_peek_uint16_be (reader, &marker)) {
+ GST_ERROR_OBJECT (self, "Could not read marker");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (marker != MARKER_SOT) {
+ GST_ERROR_OBJECT (self, "Unexpected marker 0x%04x", marker);
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ /* Skip marker */
+ gst_byte_reader_skip_unchecked (reader, 2);
+
+ if (gst_byte_reader_get_remaining (reader) < 10) {
+ GST_ERROR_OBJECT (self, "Invalid SOT marker");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ length = gst_byte_reader_get_uint16_be_unchecked (reader);
+ if (length != 10) {
+ GST_ERROR_OBJECT (self, "Invalid SOT length");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ /* FIXME: handle multiple tile parts per tile */
+ tile->sot.tile_index = gst_byte_reader_get_uint16_be_unchecked (reader);
+ tile->sot.tile_part_size = gst_byte_reader_get_uint32_be_unchecked (reader);
+ tile->sot.tile_part_index = gst_byte_reader_get_uint8_unchecked (reader);
+ tile->sot.n_tile_parts = gst_byte_reader_get_uint8_unchecked (reader);
+
+ if (tile->sot.tile_part_size >
+ 2 + 10 + gst_byte_reader_get_remaining (reader)) {
+ GST_ERROR_OBJECT (self, "Truncated tile part");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ tile->tile_x = tile->sot.tile_index % header->n_tiles_x;
+ tile->tile_y = tile->sot.tile_index / header->n_tiles_x;
+
+ tile->tx0 =
+ MAX (header->siz.xto + tile->tile_x * header->siz.xt, header->siz.xo);
+ tile->ty0 =
+ MAX (header->siz.yto + tile->tile_y * header->siz.yt, header->siz.yo);
+ tile->tx1 =
+ MIN (header->siz.xto + (tile->tile_x + 1) * header->siz.xt,
+ header->siz.x);
+ tile->ty1 =
+ MIN (header->siz.yto + (tile->tile_y + 1) * header->siz.yt,
+ header->siz.y);
+
+ /* tile part header */
+ while (TRUE) {
+ if (!gst_byte_reader_peek_uint16_be (reader, &marker)) {
+ GST_ERROR_OBJECT (self, "Could not read marker");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ /* SOD starts the data */
+ if (marker == MARKER_SOD) {
+ break;
+ }
+
+ if ((marker >> 8) != 0xff) {
+ GST_ERROR_OBJECT (self, "Lost synchronization (0x%04x)", marker);
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ /* Skip the marker */
+ gst_byte_reader_skip_unchecked (reader, 2);
+
+ /* All markers here have a length */
+ if (!gst_byte_reader_get_uint16_be (reader, &length)) {
+ GST_ERROR_OBJECT (self, "Could not read marker length");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (length < 2 || gst_byte_reader_get_remaining (reader) < length - 2) {
+ GST_ERROR_OBJECT (self, "Invalid marker length %u (available %u)",
+ length, gst_byte_reader_get_remaining (reader));
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ GST_LOG_OBJECT (self,
+ "Tile header Marker 0x%04x at offset %u with length %u", marker,
+ gst_byte_reader_get_pos (reader), length);
+
+ switch (marker) {
+ case MARKER_COD:
+ if (tile->cod) {
+ GST_ERROR_OBJECT (self, "Only one COD allowed");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ tile->cod = g_slice_new0 (CodingStyleDefault);
+ ret = parse_cod (self, reader, tile->cod, length);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ break;
+ case MARKER_COC:
+ GST_ERROR_OBJECT (self, "COC marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_POC:
+ GST_ERROR_OBJECT (self, "POC marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_RGN:
+ GST_ERROR_OBJECT (self, "RGN marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_PPT:
+ GST_ERROR_OBJECT (self, "PPT marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_PLT:{
+ PacketLengthTilePart *plt = g_slice_new (PacketLengthTilePart);
+
+ ret = parse_plt (self, reader, plt, length);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ tile->plt = g_list_append (tile->plt, plt);
+ break;
+ }
+ case MARKER_QCD:
+ if (tile->qcd != NULL) {
+ GST_ERROR_OBJECT (self, "Multiple QCD markers");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ tile->qcd = g_slice_new (Buffer);
+ tile->qcd->data = gst_byte_reader_peek_data_unchecked (reader);
+ tile->qcd->length = length - 2;
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ case MARKER_QCC:{
+ Buffer *p = g_slice_new (Buffer);
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length - 2;
+ tile->qcc = g_list_append (tile->qcc, p);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ case MARKER_COM:{
+ Buffer *p = g_slice_new (Buffer);
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length - 2;
+ tile->com = g_list_append (tile->com, p);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ default:
+ GST_DEBUG_OBJECT (self, "Skipping unknown marker 0x%04x", marker);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ }
+
+ ret = parse_packets (self, reader, header, tile);
+
+done:
+
+ return ret;
+}
+
+static guint
+sizeof_tile (GstJP2kDecimator * self, const Tile * tile)
+{
+ guint size = 0;
+ GList *l;
+
+ /* SOT */
+ size += 2 + 2 + 2 + 4 + 1 + 1;
+
+ if (tile->cod)
+ size += sizeof_cod (self, tile->cod);
+
+ if (tile->qcd)
+ size += 2 + 2 + tile->qcd->length;
+
+ for (l = tile->qcc; l; l = l->next) {
+ Buffer *b = l->data;
+ size += 2 + 2 + b->length;
+ }
+
+ for (l = tile->plt; l; l = l->next) {
+ PacketLengthTilePart *plt = l->data;
+ size += sizeof_plt (self, plt);
+ }
+
+ for (l = tile->com; l; l = l->next) {
+ Buffer *b = l->data;
+ size += 2 + 2 + b->length;
+ }
+
+ /* SOD */
+ size += 2;
+
+ for (l = tile->packets; l; l = l->next) {
+ Packet *p = l->data;
+ size += sizeof_packet (self, p);
+ }
+
+ return size;
+}
+
+static void
+reset_tile (GstJP2kDecimator * self, const MainHeader * header, Tile * tile)
+{
+ GList *l;
+
+ if (tile->cod) {
+ reset_cod (self, tile->cod);
+ g_slice_free (CodingStyleDefault, tile->cod);
+ }
+
+ for (l = tile->plt; l; l = l->next) {
+ PacketLengthTilePart *plt = l->data;
+
+ reset_plt (self, plt);
+
+ g_slice_free (PacketLengthTilePart, plt);
+ }
+ g_list_free (tile->plt);
+
+ if (tile->qcd)
+ g_slice_free (Buffer, tile->qcd);
+
+ for (l = tile->qcc; l; l = l->next) {
+ g_slice_free (Buffer, l->data);
+ }
+ g_list_free (tile->qcc);
+
+ for (l = tile->com; l; l = l->next) {
+ g_slice_free (Buffer, l->data);
+ }
+ g_list_free (tile->com);
+
+ for (l = tile->packets; l; l = l->next) {
+ Packet *p = l->data;
+
+ g_slice_free (Packet, p);
+ }
+ g_list_free (tile->packets);
+
+ memset (tile, 0, sizeof (Tile));
+}
+
+static GstFlowReturn
+write_marker_buffer (GstJP2kDecimator * self, GstByteWriter * writer,
+ guint16 marker, const Buffer * buffer)
+{
+ if (!gst_byte_writer_ensure_free_space (writer, 2 + 2 + buffer->length)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_byte_writer_put_uint16_be_unchecked (writer, marker);
+ gst_byte_writer_put_uint16_be_unchecked (writer, buffer->length + 2);
+ gst_byte_writer_put_data_unchecked (writer, buffer->data, buffer->length);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+write_packet (GstJP2kDecimator * self, GstByteWriter * writer,
+ const Packet * packet)
+{
+ guint size = packet->length;
+
+ if (packet->sop)
+ size += 6;
+ if (packet->eph && !packet->data)
+ size += 2;
+
+ if (!gst_byte_writer_ensure_free_space (writer, size)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ if (packet->sop) {
+ gst_byte_writer_put_uint16_be (writer, MARKER_SOP);
+ gst_byte_writer_put_uint16_be (writer, 4);
+ gst_byte_writer_put_uint16_be (writer, packet->seqno);
+ }
+
+ if (packet->data) {
+ gst_byte_writer_put_data_unchecked (writer, packet->data, packet->length);
+ } else {
+ gst_byte_writer_put_uint8_unchecked (writer, 0);
+ if (packet->eph) {
+ gst_byte_writer_put_uint16_be_unchecked (writer, MARKER_EPH);
+ }
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+write_tile (GstJP2kDecimator * self, GstByteWriter * writer,
+ const MainHeader * header, Tile * tile)
+{
+ GList *l;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (!gst_byte_writer_ensure_free_space (writer, 12)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_byte_writer_put_uint16_be_unchecked (writer, MARKER_SOT);
+ gst_byte_writer_put_uint16_be_unchecked (writer, 10);
+
+ gst_byte_writer_put_uint16_be_unchecked (writer, tile->sot.tile_index);
+ gst_byte_writer_put_uint32_be_unchecked (writer, tile->sot.tile_part_size);
+ gst_byte_writer_put_uint8_unchecked (writer, tile->sot.tile_part_index);
+ gst_byte_writer_put_uint8_unchecked (writer, tile->sot.n_tile_parts);
+
+ if (tile->cod) {
+ ret = write_cod (self, writer, tile->cod);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ if (tile->qcd) {
+ ret = write_marker_buffer (self, writer, MARKER_QCD, tile->qcd);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ for (l = tile->qcc; l; l = l->next) {
+ Buffer *p = l->data;
+
+ ret = write_marker_buffer (self, writer, MARKER_QCC, p);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ for (l = tile->plt; l; l = l->next) {
+ PacketLengthTilePart *plt = l->data;
+
+ ret = write_plt (self, writer, plt);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ for (l = tile->com; l; l = l->next) {
+ Buffer *p = l->data;
+
+ ret = write_marker_buffer (self, writer, MARKER_COM, p);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ if (!gst_byte_writer_put_uint16_be (writer, MARKER_SOD)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ for (l = tile->packets; l; l = l->next) {
+ Packet *p = l->data;
+
+ ret = write_packet (self, writer, p);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+done:
+
+ return ret;
+}
+
+GstFlowReturn
+parse_main_header (GstJP2kDecimator * self, GstByteReader * reader,
+ MainHeader * header)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint16 marker, length;
+
+ /* First SOC */
+ if (!gst_byte_reader_get_uint16_be (reader, &marker)
+ || marker != MARKER_SOC) {
+ GST_ERROR_OBJECT (self, "Frame does not start with SOC");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ while (TRUE) {
+ if (!gst_byte_reader_peek_uint16_be (reader, &marker)) {
+ GST_ERROR_OBJECT (self, "Could not read marker");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ /* SOT starts the tiles */
+ if (marker == MARKER_SOT) {
+ ret = GST_FLOW_OK;
+ break;
+ } else if (marker == MARKER_EOC) {
+ GST_WARNING_OBJECT (self, "EOC marker before SOT");
+ ret = GST_FLOW_UNEXPECTED;
+ goto done;
+ }
+
+ if ((marker >> 8) != 0xff) {
+ GST_ERROR_OBJECT (self, "Lost synchronization (0x%04x)", marker);
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ /* Now skip the marker */
+ gst_byte_reader_skip_unchecked (reader, 2);
+
+ /* All markers here have a length */
+ if (!gst_byte_reader_get_uint16_be (reader, &length)) {
+ GST_ERROR_OBJECT (self, "Could not read marker length");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (length < 2 || gst_byte_reader_get_remaining (reader) < length - 2) {
+ GST_ERROR_OBJECT (self, "Invalid marker length %u (available %u)",
+ length, gst_byte_reader_get_remaining (reader));
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ GST_LOG_OBJECT (self, "Marker 0x%04x at offset %u with length %u", marker,
+ gst_byte_reader_get_pos (reader), length);
+
+ switch (marker) {
+ case MARKER_SIZ:
+
+ if (header->siz.n_components != 0) {
+ GST_ERROR_OBJECT (self, "Multiple SIZ marker");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ ret = parse_siz (self, reader, &header->siz, length);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ break;
+ case MARKER_COD:
+ if (header->siz.n_components == 0) {
+ GST_ERROR_OBJECT (self, "Require SIZ before COD");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ if (header->cod.n_layers != 0) {
+ GST_ERROR_OBJECT (self, "Multiple COD");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ ret = parse_cod (self, reader, &header->cod, length);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ break;
+ case MARKER_POC:
+ GST_ERROR_OBJECT (self, "POC marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_COC:
+ GST_ERROR_OBJECT (self, "COC marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_RGN:
+ GST_ERROR_OBJECT (self, "RGN marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_TLM:
+ GST_ERROR_OBJECT (self, "TLM marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_PLM:
+ GST_ERROR_OBJECT (self, "PLM marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_PPM:
+ GST_ERROR_OBJECT (self, "PPM marker not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ break;
+ case MARKER_QCD:
+ if (header->qcd.data != NULL) {
+ GST_ERROR_OBJECT (self, "Multiple QCD markers");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ header->qcd.data = gst_byte_reader_peek_data_unchecked (reader);
+ header->qcd.length = length - 2;
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ case MARKER_QCC:{
+ Buffer *p = g_slice_new (Buffer);
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length - 2;
+ header->qcc = g_list_append (header->qcc, p);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ case MARKER_COM:{
+ Buffer *p = g_slice_new (Buffer);
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length - 2;
+ header->com = g_list_append (header->com, p);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ case MARKER_CRG:{
+ Buffer *p = g_slice_new (Buffer);
+ p->data = gst_byte_reader_peek_data_unchecked (reader);
+ p->length = length - 2;
+ header->crg = g_list_append (header->crg, p);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ default:
+ GST_DEBUG_OBJECT (self, "Skipping unknown marker 0x%04x", marker);
+ gst_byte_reader_skip_unchecked (reader, length - 2);
+ break;
+ }
+ }
+
+ if (header->siz.n_components == 0 || header->cod.n_layers == 0) {
+ GST_ERROR_OBJECT (self, "No SIZ or COD before SOT");
+ return GST_FLOW_ERROR;
+ }
+
+ header->n_tiles_x =
+ (header->siz.x - header->siz.xto + header->siz.xt - 1) / header->siz.xt;
+ header->n_tiles_y =
+ (header->siz.y - header->siz.yto + header->siz.yt - 1) / header->siz.yt;
+ header->n_tiles = header->n_tiles_x * header->n_tiles_y;
+
+ header->tiles = g_slice_alloc0 (sizeof (Tile) * header->n_tiles);
+
+ /* now at SOT marker, read the tiles */
+ {
+ gint i;
+
+ for (i = 0; i < header->n_tiles; i++) {
+ ret = parse_tile (self, reader, header, &header->tiles[i]);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+ }
+
+ /* now there must be the EOC marker */
+ if (!gst_byte_reader_get_uint16_be (reader, &marker)
+ || marker != MARKER_EOC) {
+ GST_ERROR_OBJECT (self, "Frame does not end with EOC");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+done:
+
+ return ret;
+}
+
+guint
+sizeof_main_header (GstJP2kDecimator * self, const MainHeader * header)
+{
+ guint size = 2;
+ GList *l;
+ gint i;
+
+ size += sizeof_siz (self, &header->siz);
+ size += sizeof_cod (self, &header->cod);
+ size += 2 + 2 + header->qcd.length;
+
+ for (l = header->qcc; l; l = l->next) {
+ Buffer *b = l->data;
+ size += 2 + 2 + b->length;
+ }
+
+ for (l = header->crg; l; l = l->next) {
+ Buffer *b = l->data;
+ size += 2 + 2 + b->length;
+ }
+
+ for (l = header->com; l; l = l->next) {
+ Buffer *b = l->data;
+ size += 2 + 2 + b->length;
+ }
+
+ for (i = 0; i < header->n_tiles; i++) {
+ size += sizeof_tile (self, &header->tiles[i]);
+ }
+
+ /* EOC */
+ size += 2;
+
+ return size;
+}
+
+void
+reset_main_header (GstJP2kDecimator * self, MainHeader * header)
+{
+ gint i;
+ GList *l;
+
+ if (header->tiles) {
+ for (i = 0; i < header->n_tiles; i++) {
+ reset_tile (self, header, &header->tiles[i]);
+ }
+ g_slice_free1 (sizeof (Tile) * header->n_tiles, header->tiles);
+ }
+
+ for (l = header->qcc; l; l = l->next)
+ g_slice_free (Buffer, l->data);
+ g_list_free (header->qcc);
+
+ for (l = header->com; l; l = l->next)
+ g_slice_free (Buffer, l->data);
+ g_list_free (header->com);
+
+ for (l = header->crg; l; l = l->next)
+ g_slice_free (Buffer, l->data);
+ g_list_free (header->crg);
+
+ reset_cod (self, &header->cod);
+ reset_siz (self, &header->siz);
+
+ memset (header, 0, sizeof (MainHeader));
+}
+
+GstFlowReturn
+write_main_header (GstJP2kDecimator * self, GstByteWriter * writer,
+ const MainHeader * header)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ GList *l;
+ gint i;
+
+ if (!gst_byte_writer_ensure_free_space (writer, 2)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ return GST_FLOW_ERROR;
+ }
+
+ gst_byte_writer_put_uint16_be (writer, MARKER_SOC);
+
+ ret = write_siz (self, writer, &header->siz);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ ret = write_cod (self, writer, &header->cod);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ ret = write_marker_buffer (self, writer, MARKER_QCD, &header->qcd);
+ if (ret != GST_FLOW_OK)
+ goto done;
+
+ for (l = header->qcc; l; l = l->next) {
+ Buffer *p = l->data;
+
+ ret = write_marker_buffer (self, writer, MARKER_QCC, p);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ for (l = header->crg; l; l = l->next) {
+ Buffer *p = l->data;
+
+ ret = write_marker_buffer (self, writer, MARKER_CRG, p);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ for (l = header->com; l; l = l->next) {
+ Buffer *p = l->data;
+
+ ret = write_marker_buffer (self, writer, MARKER_COM, p);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ for (i = 0; i < header->n_tiles; i++) {
+ ret = write_tile (self, writer, header, &header->tiles[i]);
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ if (!gst_byte_writer_ensure_free_space (writer, 2)) {
+ GST_ERROR_OBJECT (self, "Could not ensure free space");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ gst_byte_writer_put_uint16_be_unchecked (writer, MARKER_EOC);
+
+done:
+ return ret;
+}
+
+GstFlowReturn
+decimate_main_header (GstJP2kDecimator * self, MainHeader * header)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ gint i;
+
+ for (i = 0; i < header->n_tiles; i++) {
+ Tile *tile = &header->tiles[i];
+ GList *l;
+ PacketIterator it;
+ PacketLengthTilePart *plt = NULL;
+
+ if (tile->plt) {
+ if (g_list_length (tile->plt) > 1) {
+ GST_ERROR_OBJECT (self, "Multiple PLT per tile not supported yet");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+ plt = g_slice_new (PacketLengthTilePart);
+ plt->index = 0;
+ plt->packet_lengths = g_array_new (FALSE, FALSE, sizeof (guint32));
+ }
+
+ init_packet_iterator (self, &it, header, tile);
+
+ l = tile->packets;
+ while ((it.next (&it))) {
+ Packet *p;
+
+ if (l == NULL) {
+ GST_ERROR_OBJECT (self, "Not enough packets");
+ ret = GST_FLOW_ERROR;
+ goto done;
+ }
+
+ p = l->data;
+
+ if ((self->max_layers != 0 && it.cur_layer >= self->max_layers) ||
+ (self->max_decomposition_levels != -1
+ && it.cur_resolution > self->max_decomposition_levels)) {
+ p->data = NULL;
+ p->length = 1;
+ }
+
+ if (plt) {
+ guint32 len = sizeof_packet (self, p);
+ g_array_append_val (plt->packet_lengths, len);
+ }
+
+ l = l->next;
+ }
+
+ if (plt) {
+ reset_plt (self, tile->plt->data);
+ g_slice_free (PacketLengthTilePart, tile->plt->data);
+ tile->plt->data = plt;
+ }
+
+ tile->sot.tile_part_size = sizeof_tile (self, tile);
+ }
+
+done:
+ return ret;
+}