jp2kdecimator: Add a JPEG2000 decimator element
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 15 Nov 2010 19:03:01 +0000 (20:03 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 30 Nov 2010 11:39:46 +0000 (12:39 +0100)
This element drops information from JPEG2000 images without reencoding.

configure.ac
gst/jp2kdecimator/Makefile.am [new file with mode: 0644]
gst/jp2kdecimator/gstjp2kdecimator.c [new file with mode: 0644]
gst/jp2kdecimator/gstjp2kdecimator.h [new file with mode: 0644]
gst/jp2kdecimator/jp2kcodestream.c [new file with mode: 0644]
gst/jp2kdecimator/jp2kcodestream.h [new file with mode: 0644]

index 396b427..e728fcd 100644 (file)
@@ -312,6 +312,7 @@ AG_GST_CHECK_PLUGIN(id3tag)
 AG_GST_CHECK_PLUGIN(interlace)
 AG_GST_CHECK_PLUGIN(invtelecine)
 AG_GST_CHECK_PLUGIN(ivfparse)
+AG_GST_CHECK_PLUGIN(jp2kdecimator)
 AG_GST_CHECK_PLUGIN(jpegformat)
 AG_GST_CHECK_PLUGIN(legacyresample)
 AG_GST_CHECK_PLUGIN(librfb)
@@ -1736,6 +1737,7 @@ gst/id3tag/Makefile
 gst/interlace/Makefile
 gst/invtelecine/Makefile
 gst/ivfparse/Makefile
+gst/jp2kdecimator/Makefile
 gst/jpegformat/Makefile
 gst/legacyresample/Makefile
 gst/librfb/Makefile
diff --git a/gst/jp2kdecimator/Makefile.am b/gst/jp2kdecimator/Makefile.am
new file mode 100644 (file)
index 0000000..f967a65
--- /dev/null
@@ -0,0 +1,14 @@
+plugin_LTLIBRARIES = libgstjp2kdecimator.la
+
+libgstjp2kdecimator_la_SOURCES = \
+       gstjp2kdecimator.c \
+       jp2kcodestream.c
+
+libgstjp2kdecimator_la_CFLAGS = \
+    $(GST_CFLAGS)
+libgstjp2kdecimator_la_LIBADD = \
+    $(GST_LIBS) $(GST_BASE_LIBS)
+libgstjp2kdecimator_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstjp2kdecimator_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = gstjp2kdecimator.h jp2kcodestream.h
diff --git a/gst/jp2kdecimator/gstjp2kdecimator.c b/gst/jp2kdecimator/gstjp2kdecimator.c
new file mode 100644 (file)
index 0000000..98b50bb
--- /dev/null
@@ -0,0 +1,387 @@
+/* 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)
diff --git a/gst/jp2kdecimator/gstjp2kdecimator.h b/gst/jp2kdecimator/gstjp2kdecimator.h
new file mode 100644 (file)
index 0000000..6c2d26c
--- /dev/null
@@ -0,0 +1,58 @@
+/* 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.
+ */
+
+#ifndef _GST_JP2K_DECIMATOR_H_
+#define _GST_JP2K_DECIMATOR_H_
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_JP2K_DECIMATOR   (gst_jp2k_decimator_get_type())
+#define GST_JP2K_DECIMATOR(obj)   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_JP2K_DECIMATOR,GstJP2kDecimator))
+#define GST_JP2K_DECIMATOR_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_JP2K_DECIMATOR,GstJP2kDecimatorClass))
+#define GST_IS_JP2K_DECIMATOR(obj)   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JP2K_DECIMATOR))
+#define GST_IS_JP2K_DECIMATOR_CLASS(obj)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JP2K_DECIMATOR))
+
+typedef struct _GstJP2kDecimator GstJP2kDecimator;
+typedef struct _GstJP2kDecimatorClass GstJP2kDecimatorClass;
+
+struct _GstJP2kDecimator
+{
+  GstElement parent;
+
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  gint max_layers;
+  gint max_decomposition_levels;
+};
+
+struct _GstJP2kDecimatorClass
+{
+  GstElementClass parent;
+};
+
+GType gst_jp2k_decimator_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gst/jp2kdecimator/jp2kcodestream.c b/gst/jp2kdecimator/jp2kcodestream.c
new file mode 100644 (file)
index 0000000..e5c67e0
--- /dev/null
@@ -0,0 +1,1817 @@
+/* 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;
+}
diff --git a/gst/jp2kdecimator/jp2kcodestream.h b/gst/jp2kdecimator/jp2kcodestream.h
new file mode 100644 (file)
index 0000000..d116e44
--- /dev/null
@@ -0,0 +1,193 @@
+/* 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.
+ */
+
+#ifndef __JP2K_CODESTREAM_H__
+#define __JP2K_CODESTREAM_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbytereader.h>
+#include <gst/base/gstbitreader.h>
+#include <gst/base/gstbytewriter.h>
+
+#include "gstjp2kdecimator.h"
+
+/* Used to represent codestream packets */
+typedef struct
+{
+  gboolean sop;
+  gboolean eph;
+  guint16 seqno;
+
+  const guint8 *data;
+  guint length;
+} Packet;
+
+/* Used to represent unparsed markers for passthrough */
+typedef struct
+{
+  const guint8 *data;
+  guint length;
+} Buffer;
+
+typedef struct
+{
+  guint8 s;                     /* sample precision */
+  guint8 xr, yr;                /* resolution */
+} ComponentSize;
+
+/* SIZ */
+typedef struct
+{
+  guint16 caps;                 /* capabilities */
+  guint32 x, y;                 /* reference grid size */
+  guint32 xo, yo;               /* origin */
+  ComponentSize *components;
+  guint16 n_components;
+  guint32 xt, yt;               /* tile sizes */
+  guint32 xto, yto;             /* tile origin */
+} ImageSize;
+
+/* Progression orders
+ * L - layer
+ * R - resolution/decomposition level
+ * C - component
+ * P - position/precinct
+ */
+typedef enum
+{
+  PROGRESSION_ORDER_LRCP = 0,
+  PROGRESSION_ORDER_RLCP,
+  PROGRESSION_ORDER_RPCL,
+  PROGRESSION_ORDER_PCRL,
+  PROGRESSION_ORDER_CPRL,
+  PROGRESSION_ORDER_MAX
+} ProgressionOrder;
+
+/* COD */
+typedef struct
+{
+  /* Scod */
+  gboolean sop, eph;
+  /* SGcod */
+  ProgressionOrder progression_order;
+  guint16 n_layers;
+  guint8 multi_component_transform;
+  /* SPcod */
+  guint8 n_decompositions;
+  guint8 xcb, ycb;              /* code block dimensions */
+  guint8 code_block_style;
+  guint8 transformation;
+  guint8 *PPx, *PPy;            /* precinct sizes (default:15,
+                                 * otherwise n_decompositions+1 elements) */
+} CodingStyleDefault;
+
+/* SOT */
+typedef struct
+{
+  guint16 tile_index;
+  guint32 tile_part_size;
+  guint8 tile_part_index, n_tile_parts;
+} StartOfTile;
+
+/* PLT */
+typedef struct
+{
+  guint8 index;
+  GArray *packet_lengths;       /* array of guint32 */
+} PacketLengthTilePart;
+
+typedef struct
+{
+  StartOfTile sot;
+  CodingStyleDefault *cod;
+
+  Buffer *qcd;
+  GList *qcc;                   /* list of Buffer */
+
+  GList *plt;                   /* list of PacketLengthTilePart */
+
+  GList *com;                   /* list of Buffer */
+
+  GList *packets;               /* list of Packet, codestream */
+
+  /* TODO: COC, PPT */
+
+  /* Calculated value */
+  gint tile_x, tile_y;
+  gint tx0, tx1, ty0, ty1;      /* tile dimensions */
+} Tile;
+
+typedef struct
+{
+  /* Parsed values */
+  ImageSize siz;
+  CodingStyleDefault cod;
+
+  Buffer qcd;
+  GList *qcc;                   /* list of Buffer */
+  GList *crg, *com;             /* lists of Buffer */
+
+  /* TODO: COC, PPM, TLM, PLM */
+
+  guint n_tiles_x, n_tiles_y, n_tiles;  /* calculated */
+  Tile *tiles;
+} MainHeader;
+
+typedef struct _PacketIterator PacketIterator;
+struct _PacketIterator
+{
+  gboolean (*next) (PacketIterator * it);
+  const MainHeader *header;
+  const Tile *tile;
+
+  gboolean first;
+
+  gint cur_layer;
+  gint cur_resolution;
+  gint cur_component;
+  gint cur_precinct;
+  gint cur_x, cur_y;
+
+  gint n_layers;
+  gint n_resolutions;
+  gint n_components;
+  gint n_precincts, n_precincts_w, n_precincts_h;
+
+  gint tx0, tx1, ty0, ty1;
+  gint x_step, y_step;
+
+  /* cached calculated values */
+  /* depends on resolution and component */
+  gint tcx0, tcx1, tcy0, tcy1;
+  gint trx0, trx1, try0, try1;
+  gint tpx0, tpx1, tpy0, tpy1;
+  gint yr, xr;
+  gint two_nl_r, two_ppx, two_ppy;
+
+  gint cur_packet;
+};
+
+GstFlowReturn parse_main_header (GstJP2kDecimator * self, GstByteReader * reader, MainHeader * header);
+guint sizeof_main_header (GstJP2kDecimator * self, const MainHeader * header);
+void reset_main_header (GstJP2kDecimator * self, MainHeader * header);
+GstFlowReturn write_main_header (GstJP2kDecimator * self, GstByteWriter * writer, const MainHeader * header);
+GstFlowReturn decimate_main_header (GstJP2kDecimator * self, MainHeader * header);
+
+#endif /* __JP2K_CODESTREAM_H__ */