/* 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, 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_ppx * 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_ppx * 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_ppx * 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); if (!gst_byte_writer_put_uint16_be (writer, plt_end_pos - plt_start_pos)) { GST_ERROR_OBJECT (self, "Not enough space to write plt size"); return GST_FLOW_ERROR; } 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 = 0, length; guint16 seqno = 0; 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; g_slice_free (Packet, p); 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; g_slice_free (Packet, p); goto done; } if (!gst_byte_reader_get_uint16_be (reader, &seqno)) { GST_ERROR_OBJECT (self, "Truncated file"); ret = GST_FLOW_ERROR; g_slice_free (Packet, p); 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_EOS; 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 = 0, 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) { g_slice_free (PacketLengthTilePart, plt); 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_unchecked (writer, MARKER_SOP); gst_byte_writer_put_uint16_be_unchecked (writer, 4); gst_byte_writer_put_uint16_be_unchecked (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 = 0, length = 0; /* 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_EOS; 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_unchecked (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; g_array_free (plt->packet_lengths, TRUE); g_slice_free (PacketLengthTilePart, plt); 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; }