| /* 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; |
| } |