/* Gstreamer
 * Copyright (C) <2011> Intel
 * Copyright (C) <2011> Collabora Ltd.
 * Copyright (C) <2011> Thibault Saunier <thibault.saunier@collabora.com>
 *
 * 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.
 */
/**
 * SECTION:gstmpeg4parser
 * @title: GstMpeg4Parser
 * @short_description: Convenience library for parsing mpeg4 part 2 video
 * bitstream.
 *
 * For more details about the structures, you can refer to the
 * specifications: ISO-IEC-14496-2_2004_MPEG4_VISUAL.pdf
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <gst/base/gstbitreader.h>
#include <gst/base/gstbytereader.h>


#include "gstmpeg4parser.h"
#include "parserutils.h"

#ifndef GST_DISABLE_GST_DEBUG

#define GST_CAT_DEFAULT ensure_debug_category()

static GstDebugCategory *
ensure_debug_category (void)
{
  static gsize cat_gonce = 0;

  if (g_once_init_enter (&cat_gonce)) {
    gsize cat_done;

    cat_done = (gsize) _gst_debug_category_new ("codecparsers_mpeg4", 0,
        "GstMpeg4 codec parsing library");

    g_once_init_leave (&cat_gonce, cat_done);
  }

  return (GstDebugCategory *) cat_gonce;
}

#else

#define ensure_debug_category() /* NOOP */

#endif /* GST_DISABLE_GST_DEBUG */

#define CHECK_MARKER(br) G_STMT_START { \
  guint8 marker;\
  if (!gst_bit_reader_get_bits_uint8 (br, &marker, 1)) { \
    GST_WARNING ("failed to read marker bit"); \
    goto failed; \
  } else if (!marker) {\
    GST_WARNING ("Wrong marker bit"); \
    goto failed;\
  }\
} G_STMT_END

#define MARKER_UNCHECKED(br) G_STMT_START { \
  if (!gst_bit_reader_get_bits_uint8_unchecked (br, 1)) { \
    GST_WARNING ("Wrong marker bit"); \
    goto failed; \
  } \
} G_STMT_END

#define CHECK_REMAINING(br, needed) G_STMT_START { \
  if (gst_bit_reader_get_remaining (br) < needed) \
    goto failed; \
} G_STMT_END

static const guint8 default_intra_quant_mat[64] = {
  8, 17, 18, 19, 21, 23, 25, 27,
  17, 18, 19, 21, 23, 25, 27, 28,
  20, 21, 22, 23, 24, 26, 28, 30,
  21, 22, 23, 24, 26, 28, 30, 32,
  22, 23, 24, 26, 28, 30, 32, 35,
  23, 24, 26, 28, 30, 32, 35, 38,
  25, 26, 28, 30, 32, 35, 38, 41,
  27, 28, 30, 32, 35, 38, 41, 45
};

static const guint8 default_non_intra_quant_mat[64] = {
  16, 17, 18, 19, 20, 21, 22, 23,
  17, 18, 19, 20, 21, 22, 23, 24,
  18, 19, 20, 21, 22, 23, 24, 25,
  19, 20, 21, 22, 23, 24, 26, 27,
  20, 21, 22, 23, 25, 26, 27, 28,
  21, 22, 23, 24, 26, 27, 28, 30,
  22, 23, 24, 26, 27, 28, 30, 31,
  23, 24, 25, 27, 28, 30, 31, 33,
};

static const guint8 mpeg4_zigzag_8x8[64] = {
  0, 1, 8, 16, 9, 2, 3, 10,
  17, 24, 32, 25, 18, 11, 4, 5,
  12, 19, 26, 33, 40, 48, 41, 34,
  27, 20, 13, 6, 7, 14, 21, 28,
  35, 42, 49, 56, 57, 50, 43, 36,
  29, 22, 15, 23, 30, 37, 44, 51,
  58, 59, 52, 45, 38, 31, 39, 46,
  53, 60, 61, 54, 47, 55, 62, 63
};

static const VLCTable mpeg4_dmv_size_vlc_table[] = {
  {0, 0x00, 2},
  {1, 0x02, 3},
  {2, 0x03, 3},
  {3, 0x04, 3},
  {4, 0x05, 3},
  {5, 0x06, 3},
  {6, 0x0e, 4},
  {7, 0x1e, 5},
  {8, 0x3e, 6},
  {9, 0x7e, 7},
  {10, 0xfe, 8},
  {11, 0x1fe, 9},
  {12, 0x3fe, 10},
  {13, 0x7fe, 11},
  {14, 0xffe, 12}
};

static void
mpeg4_util_par_from_info (guint8 aspect_ratio_info, guint8 * par_width,
    guint8 * par_height)
{
  switch (aspect_ratio_info) {
    case 0x02:
      *par_width = 12;
      *par_height = 11;
      break;
    case 0x03:
      *par_width = 10;
      *par_height = 11;
      break;
    case 0x04:
      *par_width = 16;
      *par_height = 11;
      break;
    case 0x05:
      *par_width = 40;
      *par_height = 33;
      break;

    case 0x01:
    default:
      *par_width = 1;
      *par_height = 1;
  }
}

static gboolean
parse_quant (GstBitReader * br, guint8 quant_mat[64],
    const guint8 default_quant_mat[64], guint8 * load_quant_mat)
{
  READ_UINT8 (br, *load_quant_mat, 1);
  if (*load_quant_mat) {
    guint i;
    guint8 val;

    val = 1;
    for (i = 0; i < 64; i++) {

      if (val != 0)
        READ_UINT8 (br, val, 8);

      if (val == 0) {
        if (i == 0)
          goto invalid_quant_mat;
        quant_mat[mpeg4_zigzag_8x8[i]] = quant_mat[mpeg4_zigzag_8x8[i - 1]];
      } else
        quant_mat[mpeg4_zigzag_8x8[i]] = val;
    }
  } else
    memcpy (quant_mat, default_quant_mat, 64);

  return TRUE;

failed:
  GST_WARNING ("failed parsing quant matrix");
  return FALSE;

invalid_quant_mat:
  GST_WARNING ("the first value should be non zero");
  goto failed;
}

static gboolean
parse_signal_type (GstBitReader * br, GstMpeg4VideoSignalType * signal_type)
{
  READ_UINT8 (br, signal_type->type, 1);

  if (signal_type->type) {

    READ_UINT8 (br, signal_type->format, 3);
    READ_UINT8 (br, signal_type->range, 1);
    READ_UINT8 (br, signal_type->color_description, 1);

    if (signal_type->color_description) {
      READ_UINT8 (br, signal_type->color_primaries, 8);
      READ_UINT8 (br, signal_type->transfer_characteristics, 8);
      READ_UINT8 (br, signal_type->matrix_coefficients, 8);
    }
  }

  return TRUE;

failed:
  GST_WARNING ("failed parsing \"Video Signal Type\"");

  return FALSE;
}

static gboolean
parse_sprite_trajectory (GstBitReader * br,
    GstMpeg4SpriteTrajectory * sprite_traj, guint no_of_sprite_warping_points)
{
  guint i, length;

  for (i = 0; i < no_of_sprite_warping_points; i++) {

    if (!decode_vlc (br, &length, mpeg4_dmv_size_vlc_table,
            G_N_ELEMENTS (mpeg4_dmv_size_vlc_table)))
      goto failed;

    if (length)
      READ_UINT16 (br, sprite_traj->vop_ref_points[i], length);
    CHECK_MARKER (br);

    if (!decode_vlc (br, &length, mpeg4_dmv_size_vlc_table,
            G_N_ELEMENTS (mpeg4_dmv_size_vlc_table)))
      goto failed;

    if (length)
      READ_UINT16 (br, sprite_traj->sprite_ref_points[i], length);
    CHECK_MARKER (br);
  }

  return TRUE;

failed:
  GST_WARNING ("Could not parse the sprite trajectory");
  return FALSE;
}

static guint
find_psc (GstByteReader * br)
{
  guint psc_pos = -1, psc;

  if (!gst_byte_reader_peek_uint24_be (br, &psc))
    goto failed;

  /* Scan for the picture start code (22 bits - 0x0020) */
  while ((gst_byte_reader_get_remaining (br) >= 3)) {
    if (gst_byte_reader_peek_uint24_be (br, &psc) &&
        ((psc & 0xfffffc) == 0x000080)) {
      psc_pos = gst_byte_reader_get_pos (br);
      break;
    } else
      gst_byte_reader_skip_unchecked (br, 1);
  }

failed:

  return psc_pos;
}

static inline guint8
compute_resync_marker_size (const GstMpeg4VideoObjectPlane * vop,
    guint32 * pattern, guint32 * mask)
{
  guint8 off;

  /* FIXME handle the binary only shape case */
  switch (vop->coding_type) {
    case (GST_MPEG4_I_VOP):
      off = 16;
      break;
    case (GST_MPEG4_S_VOP):
    case (GST_MPEG4_P_VOP):
      off = 15 + vop->fcode_forward;

      break;
    case (GST_MPEG4_B_VOP):
      off = MAX (15 + MAX (vop->fcode_forward, vop->fcode_backward), 17);

      break;
    default:
      return -1;
  }

  if (mask && pattern) {
    switch (off) {
      case 16:
        *pattern = 0x00008000;
        *mask = 0xffff8000;
        break;
      case 17:
        *pattern = 0x00004000;
        *mask = 0xffffc000;
        break;
      case 18:
        *pattern = 0x00002000;
        *mask = 0xffffe000;
        break;
      case 19:
        *pattern = 0x00001000;
        *mask = 0xfffff000;
        break;
      case 20:
        *pattern = 0x00000800;
        *mask = 0xfffff800;
        break;
      case 21:
        *pattern = 0x00000400;
        *mask = 0xfffffc00;
        break;
      case 22:
        *pattern = 0x00000200;
        *mask = 0xfffffe00;
        break;
      case 23:
        *pattern = 0x00000100;
        *mask = 0xffffff00;
        break;
    }
  }

  return off + 1;               /* Take the following 1 into account */
}

/**
 * gst_mpeg4_next_resync:
 * @packet: The #GstMpeg4Packet to fill
 * @vop: The previously parsed #GstMpeg4VideoObjectPlane
 * @offset: offset from which to start the parsing
 * @data: The data to parse
 * @size: The size of the @data to parse
 *
 * Parses @data and fills @packet with the information of the next resync packet
 * found.
 *
 * Returns: a #GstMpeg4ParseResult
 */
static GstMpeg4ParseResult
gst_mpeg4_next_resync (GstMpeg4Packet * packet,
    const GstMpeg4VideoObjectPlane * vop, const guint8 * data, gsize size,
    gboolean first_resync_marker)
{
  guint markersize = 0, off1, off2;
  guint32 mask = 0xff, pattern = 0xff;
  GstByteReader br;

  gst_byte_reader_init (&br, data, size);

  g_return_val_if_fail (packet != NULL, GST_MPEG4_PARSER_ERROR);
  g_return_val_if_fail (vop != NULL, GST_MPEG4_PARSER_ERROR);

  markersize = compute_resync_marker_size (vop, &pattern, &mask);

  if (first_resync_marker) {
    off1 = 0;
  } else {
    off1 = gst_byte_reader_masked_scan_uint32 (&br, mask, pattern, 0, size);
  }

  if (off1 == -1)
    return GST_MPEG4_PARSER_NO_PACKET;

  GST_DEBUG ("Resync code found at %i", off1);

  packet->offset = off1;
  packet->type = GST_MPEG4_RESYNC;
  packet->marker_size = markersize;

  off2 = gst_byte_reader_masked_scan_uint32 (&br, mask, pattern,
      off1 + 2, size - off1 - 2);

  if (off2 == -1)
    return GST_MPEG4_PARSER_NO_PACKET_END;

  packet->size = off2 - off1;

  return GST_MPEG4_PARSER_OK;
}


/********** API **********/

/**
 * gst_mpeg4_parse:
 * @packet: The #GstMpeg4Packet to fill
 * @skip_user_data: %TRUE to skip user data packet %FALSE otherwise
 * @vop: The last parsed #GstMpeg4VideoObjectPlane or %NULL if you do
 * not need to detect the resync codes.
 * @offset: offset from which to start the parsing
 * @data: The data to parse
 * @size: The size of the @data to parse
 *
 * Parses @data and fills @packet with the information of the next packet
 * found.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_mpeg4_parse (GstMpeg4Packet * packet, gboolean skip_user_data,
    GstMpeg4VideoObjectPlane * vop, const guint8 * data, guint offset,
    gsize size)
{
  gint off1, off2;
  GstByteReader br;
  GstMpeg4ParseResult resync_res;
  static guint first_resync_marker = TRUE;

  gst_byte_reader_init (&br, data, size);

  g_return_val_if_fail (packet != NULL, GST_MPEG4_PARSER_ERROR);

  if (size - offset <= 4) {
    GST_DEBUG ("Can't parse, buffer is to small size %" G_GSIZE_FORMAT
        " at offset %d", size, offset);
    return GST_MPEG4_PARSER_ERROR;
  }

  if (vop) {
    resync_res =
        gst_mpeg4_next_resync (packet, vop, data + offset, size - offset,
        first_resync_marker);
    first_resync_marker = FALSE;

    /*  We found a complet slice */
    if (resync_res == GST_MPEG4_PARSER_OK)
      return resync_res;
    else if (resync_res == GST_MPEG4_PARSER_NO_PACKET_END) {
      /* It doesn't mean there is no standard packet end, look for it */
      off1 = packet->offset;
      goto find_end;
    } else if (resync_res == GST_MPEG4_PARSER_NO_PACKET)
      return resync_res;
  } else {
    first_resync_marker = TRUE;
  }

  off1 = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100,
      offset, size - offset);

  if (off1 == -1) {
    GST_DEBUG ("No start code prefix in this buffer");
    return GST_MPEG4_PARSER_NO_PACKET;
  }

  /* Recursively skip user data if needed */
  if (skip_user_data && data[off1 + 3] == GST_MPEG4_USER_DATA)
    /* If we are here, we know no resync code has been found the first time, so we
     * don't look for it this time */
    return gst_mpeg4_parse (packet, skip_user_data, NULL, data, off1 + 3, size);

  packet->offset = off1 + 3;
  packet->data = data;
  packet->type = (GstMpeg4StartCode) (data[off1 + 3]);

find_end:
  if (off1 < size - 4)
    off2 = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100,
        off1 + 4, size - off1 - 4);
  else
    off2 = -1;

  if (off2 == -1) {
    GST_DEBUG ("Packet start %d, No end found", off1 + 4);

    packet->size = G_MAXUINT;
    return GST_MPEG4_PARSER_NO_PACKET_END;
  }

  if (packet->type == GST_MPEG4_RESYNC) {
    packet->size = (gsize) off2 - off1;
  } else {
    packet->size = (gsize) off2 - off1 - 3;
  }

  GST_DEBUG ("Complete packet of type %x found at: %d, Size: %" G_GSIZE_FORMAT,
      packet->type, packet->offset, packet->size);
  return GST_MPEG4_PARSER_OK;

}

/**
 * gst_h263_parse:
 * @packet: The #GstMpeg4Packet to fill
 * @offset: offset from which to start the parsing
 * @data: The data to parse
 * @size: The size of the @data to parse
 *
 * Parses @data and fills @packet with the information of the next packet
 * found.
 *
 * Note that the type of the packet is meaningless in this case.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_h263_parse (GstMpeg4Packet * packet,
    const guint8 * data, guint offset, gsize size)
{
  gint off1, off2;
  GstByteReader br;

  gst_byte_reader_init (&br, data + offset, size - offset);

  g_return_val_if_fail (packet != NULL, GST_MPEG4_PARSER_ERROR);

  if (size - offset < 3) {
    GST_DEBUG ("Can't parse, buffer is to small size %" G_GSIZE_FORMAT
        " at offset %d", size, offset);
    return GST_MPEG4_PARSER_ERROR;
  }

  off1 = find_psc (&br);

  if (off1 == -1) {
    GST_DEBUG ("No start code prefix in this buffer");
    return GST_MPEG4_PARSER_NO_PACKET;
  }

  packet->offset = off1 + offset;
  packet->data = data;

  gst_byte_reader_skip_unchecked (&br, 3);
  off2 = find_psc (&br);

  if (off2 == -1) {
    GST_DEBUG ("Packet start %d, No end found", off1);

    packet->size = G_MAXUINT;
    return GST_MPEG4_PARSER_NO_PACKET_END;
  }

  packet->size = (gsize) off2 - off1;

  GST_DEBUG ("Complete packet found at: %d, Size: %" G_GSIZE_FORMAT,
      packet->offset, packet->size);

  return GST_MPEG4_PARSER_OK;
}

/**
 * gst_mpeg4_parse_visual_object_sequence:
 * @vos: The #GstMpeg4VisualObjectSequence structure to fill
 * @data: The data to parse, should contain the visual_object_sequence_start_code
 * but not the start code prefix
 * @size: The size of the @data to parse
 *
 * Parses @data containing the visual object sequence packet, and fills
 * the @vos structure.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_mpeg4_parse_visual_object_sequence (GstMpeg4VisualObjectSequence * vos,
    const guint8 * data, gsize size)
{
  guint8 vos_start_code;
  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (vos != NULL, GST_MPEG4_PARSER_ERROR);

  READ_UINT8 (&br, vos_start_code, 8);
  if (vos_start_code != GST_MPEG4_VISUAL_OBJ_SEQ_START)
    goto wrong_start_code;

  READ_UINT8 (&br, vos->profile_and_level_indication, 8);

  switch (vos->profile_and_level_indication) {
    case 0x01:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x02:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x03:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0x08:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE;
      vos->level = GST_MPEG4_LEVEL0;
      break;
    case 0x10:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_SCALABLE;
      vos->level = GST_MPEG4_LEVEL0;
      break;
    case 0x11:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_SCALABLE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x12:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_SCALABLE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x21:
      vos->profile = GST_MPEG4_PROFILE_CORE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x22:
      vos->profile = GST_MPEG4_PROFILE_CORE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x32:
      vos->profile = GST_MPEG4_PROFILE_MAIN;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x33:
      vos->profile = GST_MPEG4_PROFILE_MAIN;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0x34:
      vos->profile = GST_MPEG4_PROFILE_MAIN;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0x42:
      vos->profile = GST_MPEG4_PROFILE_N_BIT;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x51:
      vos->profile = GST_MPEG4_PROFILE_SCALABLE_TEXTURE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x61:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_FACE_ANIMATION;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x62:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_FACE_ANIMATION;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x63:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_FBA;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x64:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_FBA;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x71:
      vos->profile = GST_MPEG4_PROFILE_BASIC_ANIMATED_TEXTURE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x72:
      vos->profile = GST_MPEG4_PROFILE_BASIC_ANIMATED_TEXTURE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x81:
      vos->profile = GST_MPEG4_PROFILE_HYBRID;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x82:
      vos->profile = GST_MPEG4_PROFILE_HYBRID;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x91:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_REALTIME_SIMPLE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0x92:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_REALTIME_SIMPLE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0x93:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_REALTIME_SIMPLE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0x94:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_REALTIME_SIMPLE;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0xa1:
      vos->profile = GST_MPEG4_PROFILE_CORE_SCALABLE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xa2:
      vos->profile = GST_MPEG4_PROFILE_CORE_SCALABLE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xa3:
      vos->profile = GST_MPEG4_PROFILE_CORE_SCALABLE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xb1:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xb2:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xb3:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xb4:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0xc1:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CORE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xc2:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CORE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xc3:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_CORE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xd1:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SCALABLE_TEXTURE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xd2:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SCALABLE_TEXTURE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xd3:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SCALABLE_TEXTURE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xe1:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_STUDIO;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xe2:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_STUDIO;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xe3:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_STUDIO;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xe4:
      vos->profile = GST_MPEG4_PROFILE_SIMPLE_STUDIO;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0xe5:
      vos->profile = GST_MPEG4_PROFILE_CORE_STUDIO;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xe6:
      vos->profile = GST_MPEG4_PROFILE_CORE_STUDIO;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xe7:
      vos->profile = GST_MPEG4_PROFILE_CORE_STUDIO;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xe8:
      vos->profile = GST_MPEG4_PROFILE_CORE_STUDIO;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0xf0:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL0;
      break;
    case 0xf1:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xf2:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xf3:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xf4:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0xf5:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL5;
      break;
    case 0xf7:
      vos->profile = GST_MPEG4_PROFILE_ADVANCED_SIMPLE;
      vos->level = GST_MPEG4_LEVEL3b;
      break;
    case 0xf8:
      vos->profile = GST_MPEG4_PROFILE_FINE_GRANULARITY_SCALABLE;
      vos->level = GST_MPEG4_LEVEL0;
      break;
    case 0xf9:
      vos->profile = GST_MPEG4_PROFILE_FINE_GRANULARITY_SCALABLE;
      vos->level = GST_MPEG4_LEVEL1;
      break;
    case 0xfa:
      vos->profile = GST_MPEG4_PROFILE_FINE_GRANULARITY_SCALABLE;
      vos->level = GST_MPEG4_LEVEL2;
      break;
    case 0xfb:
      vos->profile = GST_MPEG4_PROFILE_FINE_GRANULARITY_SCALABLE;
      vos->level = GST_MPEG4_LEVEL3;
      break;
    case 0xfc:
      vos->profile = GST_MPEG4_PROFILE_FINE_GRANULARITY_SCALABLE;
      vos->level = GST_MPEG4_LEVEL4;
      break;
    case 0xfd:
      vos->profile = GST_MPEG4_PROFILE_FINE_GRANULARITY_SCALABLE;
      vos->level = GST_MPEG4_LEVEL5;
      break;
    default:
      vos->profile = GST_MPEG4_PROFILE_RESERVED;
      vos->level = GST_MPEG4_LEVEL_RESERVED;
      break;
  }

  return GST_MPEG4_PARSER_OK;

wrong_start_code:
  GST_WARNING ("got buffer with wrong start code");
  return GST_MPEG4_PARSER_ERROR;

failed:
  GST_WARNING ("failed parsing \"Visual Object\"");
  return GST_MPEG4_PARSER_ERROR;
}

/**
 * gst_mpeg4_parse_visual_object:
 * @vo: The #GstMpeg4VisualObject structure to fill
 * @signal_type: The #GstMpeg4VideoSignalType to fill or %NULL
 * @data: The data to parse, should contain the vo_start_code
 * but not the start code prefix
 * @size: The size of the @data to parse
 *
 * Parses @data containing the visual object packet, and fills
 * the @vo structure.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_mpeg4_parse_visual_object (GstMpeg4VisualObject * vo,
    GstMpeg4VideoSignalType * signal_type, const guint8 * data, gsize size)
{
  guint8 vo_start_code, type;
  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (vo != NULL, GST_MPEG4_PARSER_ERROR);

  GST_DEBUG ("Parsing visual object");

  READ_UINT8 (&br, vo_start_code, 8);
  if (vo_start_code != GST_MPEG4_VISUAL_OBJ)
    goto wrong_start_code;

  /* set default values */
  vo->verid = 0x1;
  vo->priority = 1;

  READ_UINT8 (&br, vo->is_identifier, 1);
  if (vo->is_identifier) {
    READ_UINT8 (&br, vo->verid, 4);
    READ_UINT8 (&br, vo->priority, 3);
  }

  READ_UINT8 (&br, type, 4);
  vo->type = type;

  if ((type == GST_MPEG4_VIDEO_ID ||
          type == GST_MPEG4_STILL_TEXTURE_ID) && signal_type) {

    if (!parse_signal_type (&br, signal_type))
      goto failed;

  } else if (signal_type) {
    signal_type->type = 0;
  }

  return GST_MPEG4_PARSER_OK;

wrong_start_code:
  GST_WARNING ("got buffer with wrong start code");
  return GST_MPEG4_PARSER_ERROR;

failed:
  GST_WARNING ("failed parsing \"Visual Object\"");
  return GST_MPEG4_PARSER_ERROR;
}

/**
 * gst_mpeg4_parse_video_object_layer:
 * @vol: The #GstMpeg4VideoObjectLayer structure to fill
 * @vo: The #GstMpeg4VisualObject currently being parsed or %NULL
 * @data: The data to parse
 * @size: The size of the @data to parse
 *
 * Parses @data containing the video object layer packet, and fills
 * the @vol structure.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_mpeg4_parse_video_object_layer (GstMpeg4VideoObjectLayer * vol,
    GstMpeg4VisualObject * vo, const guint8 * data, gsize size)
{
  guint8 video_object_layer_start_code;

  /* Used for enums types */
  guint8 tmp;
  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (vol != NULL, GST_MPEG4_PARSER_ERROR);

  GST_DEBUG ("Parsing video object layer");

  READ_UINT8 (&br, video_object_layer_start_code, 8);
  if (!(video_object_layer_start_code >= GST_MPEG4_VIDEO_LAYER_FIRST &&
          video_object_layer_start_code <= GST_MPEG4_VIDEO_LAYER_LAST))
    goto wrong_start_code;

  /* set default values */
  if (vo) {
    vol->verid = vo->verid;
    vol->priority = vo->priority;
  } else {
    vol->verid = 1;
    vol->priority = 0;
  }

  vol->low_delay = FALSE;
  vol->chroma_format = 1;
  vol->vbv_parameters = FALSE;
  vol->quant_precision = 5;
  vol->bits_per_pixel = 8;
  vol->quarter_sample = FALSE;
  vol->newpred_enable = FALSE;
  vol->interlaced = 0;
  vol->width = 0;
  vol->height = 0;

  READ_UINT8 (&br, vol->random_accessible_vol, 1);
  READ_UINT8 (&br, vol->video_object_type_indication, 8);

  READ_UINT8 (&br, vol->is_object_layer_identifier, 1);
  if (vol->is_object_layer_identifier) {
    READ_UINT8 (&br, vol->verid, 4);
    READ_UINT8 (&br, vol->priority, 3);
  }

  READ_UINT8 (&br, tmp, 4);
  vol->aspect_ratio_info = tmp;
  if (vol->aspect_ratio_info != GST_MPEG4_EXTENDED_PAR) {
    mpeg4_util_par_from_info (vol->aspect_ratio_info, &vol->par_width,
        &vol->par_height);

  } else {
    gint v;

    READ_UINT8 (&br, vol->par_width, 8);
    v = vol->par_width;
    CHECK_ALLOWED (v, 1, 255);

    READ_UINT8 (&br, vol->par_height, 8);
    v = vol->par_height;
    CHECK_ALLOWED (v, 1, 255);
  }
  GST_DEBUG ("Pixel aspect ratio %d/%d", vol->par_width, vol->par_width);

  READ_UINT8 (&br, vol->control_parameters, 1);
  if (vol->control_parameters) {
    guint8 chroma_format;

    READ_UINT8 (&br, chroma_format, 2);
    vol->chroma_format = chroma_format;
    READ_UINT8 (&br, vol->low_delay, 1);

    READ_UINT8 (&br, vol->vbv_parameters, 1);
    if (vol->vbv_parameters) {
      CHECK_REMAINING (&br, 79);

      vol->first_half_bitrate =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
      MARKER_UNCHECKED (&br);

      vol->latter_half_bitrate =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
      MARKER_UNCHECKED (&br);

      vol->bit_rate =
          (vol->first_half_bitrate << 15) | vol->latter_half_bitrate;

      vol->first_half_vbv_buffer_size =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
      MARKER_UNCHECKED (&br);

      vol->latter_half_vbv_buffer_size =
          gst_bit_reader_get_bits_uint8_unchecked (&br, 3);

      vol->vbv_buffer_size = (vol->first_half_vbv_buffer_size << 15) |
          vol->latter_half_vbv_buffer_size;

      vol->first_half_vbv_occupancy =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 11);
      MARKER_UNCHECKED (&br);

      vol->latter_half_vbv_occupancy =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
      MARKER_UNCHECKED (&br);
    }
  }

  READ_UINT8 (&br, tmp, 2);
  vol->shape = tmp;

  if (vol->shape == GST_MPEG4_GRAYSCALE) {
    /* TODO support grayscale shapes, for now we just pass */

    /* Something the standard starts to define... */
    GST_WARNING ("Grayscale shaped not supported");
    goto failed;
  }

  if (vol->shape == GST_MPEG4_GRAYSCALE && vol->verid != 0x01)
    READ_UINT8 (&br, vol->shape_extension, 4);

  CHECK_REMAINING (&br, 19);

  MARKER_UNCHECKED (&br);
  vol->vop_time_increment_resolution =
      gst_bit_reader_get_bits_uint16_unchecked (&br, 16);
  if (vol->vop_time_increment_resolution < 1) {
    GST_WARNING ("value not in allowed range. value: %d, range %d-%d",
        vol->vop_time_increment_resolution, 1, G_MAXUINT16);
    goto failed;
  }
  vol->vop_time_increment_bits =
      g_bit_storage (vol->vop_time_increment_resolution);

  MARKER_UNCHECKED (&br);
  vol->fixed_vop_rate = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  if (vol->fixed_vop_rate)
    READ_UINT16 (&br, vol->fixed_vop_time_increment,
        vol->vop_time_increment_bits);

  if (vol->shape != GST_MPEG4_BINARY_ONLY) {
    if (vol->shape == GST_MPEG4_RECTANGULAR) {
      CHECK_REMAINING (&br, 29);

      MARKER_UNCHECKED (&br);
      vol->width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
      MARKER_UNCHECKED (&br);
      vol->height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
      MARKER_UNCHECKED (&br);
    }

    READ_UINT8 (&br, vol->interlaced, 1);
    READ_UINT8 (&br, vol->obmc_disable, 1);

    if (vol->verid == 0x1)
      READ_UINT8 (&br, tmp, 1);
    else
      READ_UINT8 (&br, tmp, 2);
    vol->sprite_enable = tmp;

    if (vol->sprite_enable == GST_MPEG4_SPRITE_STATIC ||
        vol->sprite_enable == GST_MPEG4_SPRITE_GMG) {

      if (vol->sprite_enable == GST_MPEG4_SPRITE_GMG)
        CHECK_REMAINING (&br, 9);
      else {
        CHECK_REMAINING (&br, 65);

        vol->sprite_width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
        MARKER_UNCHECKED (&br);

        vol->sprite_height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
        MARKER_UNCHECKED (&br);

        vol->sprite_left_coordinate =
            gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
        MARKER_UNCHECKED (&br);

        vol->sprite_top_coordinate =
            gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
        MARKER_UNCHECKED (&br);
      }
      vol->no_of_sprite_warping_points =
          gst_bit_reader_get_bits_uint8_unchecked (&br, 6);
      vol->sprite_warping_accuracy =
          gst_bit_reader_get_bits_uint8_unchecked (&br, 2);
      vol->sprite_brightness_change =
          gst_bit_reader_get_bits_uint8_unchecked (&br, 1);

      if (vol->sprite_enable != GST_MPEG4_SPRITE_GMG)
        vol->low_latency_sprite_enable =
            gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
    }

    if (vol->verid != 0x1 && vol->shape != GST_MPEG4_RECTANGULAR)
      READ_UINT8 (&br, vol->sadct_disable, 1);

    READ_UINT8 (&br, vol->not_8_bit, 1);
    if (vol->not_8_bit) {
      READ_UINT8 (&br, vol->quant_precision, 4);
      CHECK_ALLOWED (vol->quant_precision, 3, 9);

      READ_UINT8 (&br, vol->bits_per_pixel, 4);
      CHECK_ALLOWED (vol->bits_per_pixel, 4, 12);
    }

    if (vol->shape == GST_MPEG4_GRAYSCALE) {
      /* We don't actually support it */
      READ_UINT8 (&br, vol->no_gray_quant_update, 1);
      READ_UINT8 (&br, vol->composition_method, 1);
      READ_UINT8 (&br, vol->linear_composition, 1);
    }

    READ_UINT8 (&br, vol->quant_type, 1);
    if (vol->quant_type) {
      if (!parse_quant (&br, vol->intra_quant_mat, default_intra_quant_mat,
              &vol->load_intra_quant_mat))
        goto failed;

      if (!parse_quant (&br, vol->non_intra_quant_mat,
              default_non_intra_quant_mat, &vol->load_non_intra_quant_mat))
        goto failed;

      if (vol->shape == GST_MPEG4_GRAYSCALE) {
        /* Something the standard starts to define... */
        GST_WARNING ("Grayscale shaped not supported");
        goto failed;
      }

    } else {
      memset (&vol->intra_quant_mat, 0, 64);
      memset (&vol->non_intra_quant_mat, 0, 64);
    }

    if (vol->verid != 0x1)
      READ_UINT8 (&br, vol->quarter_sample, 1);

    READ_UINT8 (&br, vol->complexity_estimation_disable, 1);
    if (!vol->complexity_estimation_disable) {
      guint8 estimation_method;
      guint8 estimation_disable;

      /* skip unneeded properties */
      READ_UINT8 (&br, estimation_method, 2);
      if (estimation_method < 2) {
        READ_UINT8 (&br, estimation_disable, 1);
        if (!estimation_disable)
          SKIP (&br, 6);
        READ_UINT8 (&br, estimation_disable, 1);
        if (!estimation_disable)
          SKIP (&br, 4);
        CHECK_MARKER (&br);
        READ_UINT8 (&br, estimation_disable, 1);
        if (!estimation_disable)
          SKIP (&br, 4);
        READ_UINT8 (&br, estimation_disable, 1);
        if (!estimation_disable)
          SKIP (&br, 6);
        CHECK_MARKER (&br);

        if (estimation_method == 1) {
          READ_UINT8 (&br, estimation_disable, 1);
          if (!estimation_disable)
            SKIP (&br, 2);
        }
      }
    }

    READ_UINT8 (&br, vol->resync_marker_disable, 1);
    READ_UINT8 (&br, vol->data_partitioned, 1);

    if (vol->data_partitioned)
      READ_UINT8 (&br, vol->reversible_vlc, 1);

    if (vol->verid != 0x01) {
      READ_UINT8 (&br, vol->newpred_enable, 1);
      if (vol->newpred_enable)
        /* requested_upstream_message_type and newpred_segment_type */
        SKIP (&br, 3);

      READ_UINT8 (&br, vol->reduced_resolution_vop_enable, 1);
    }

    READ_UINT8 (&br, vol->scalability, 1);
    if (vol->scalability) {
      SKIP (&br, 26);           /* Few not needed props */
      READ_UINT8 (&br, vol->enhancement_type, 1);
    }

    /* More unused infos */
  } else if (vol->verid != 0x01) {
    GST_WARNING ("Binary only shapes not fully supported");
    goto failed;
  }
  /* ... */

  return GST_MPEG4_PARSER_OK;

failed:
  GST_WARNING ("failed parsing \"Video Object Layer\"");
  return GST_MPEG4_PARSER_ERROR;

wrong_start_code:
  GST_WARNING ("got buffer with wrong start code");
  goto failed;
}

/**
 * gst_mpeg4_parse_group_of_vop:
 * @gov: The #GstMpeg4GroupOfVOP structure to fill
 * @data: The data to parse
 * @size: The size of the @data to parse
 *
 * Parses @data containing the group of video object plane packet, and fills
 * the @gov structure.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_mpeg4_parse_group_of_vop (GstMpeg4GroupOfVOP *
    gov, const guint8 * data, gsize size)
{
  guint8 gov_start_code;
  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (gov != NULL, GST_MPEG4_PARSER_ERROR);

  READ_UINT8 (&br, gov_start_code, 8);
  if (gov_start_code != GST_MPEG4_GROUP_OF_VOP)
    goto wrong_start_code;

  CHECK_REMAINING (&br, 65);

  gov->hours = gst_bit_reader_get_bits_uint8_unchecked (&br, 5);
  gov->minutes = gst_bit_reader_get_bits_uint8_unchecked (&br, 6);
  /* marker bit */
  MARKER_UNCHECKED (&br);
  gov->seconds = gst_bit_reader_get_bits_uint8_unchecked (&br, 6);

  gov->closed = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  gov->broken_link = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);

  return GST_MPEG4_PARSER_OK;

failed:
  GST_WARNING ("failed parsing \"Group of Video Object Plane\"");
  return GST_MPEG4_PARSER_ERROR;

wrong_start_code:
  GST_WARNING ("got buffer with wrong start code");
  goto failed;
}

/**
 * gst_mpeg4_parse_video_object_plane:
 * @vop: The #GstMpeg4VideoObjectPlane currently being parsed
 * @sprite_trajectory: A #GstMpeg4SpriteTrajectory to fill or %NULL
 * @vol: The #GstMpeg4VideoObjectLayer structure to fill
 * @data: The data to parse
 * @size: The size of the @data to parse
 *
 * Parses @data containing the video object plane packet, and fills the @vol
 * structure.
 *
 * Returns: a #GstMpeg4ParseResult
 */
GstMpeg4ParseResult
gst_mpeg4_parse_video_object_plane (GstMpeg4VideoObjectPlane * vop,
    GstMpeg4SpriteTrajectory * sprite_trajectory,
    GstMpeg4VideoObjectLayer * vol, const guint8 * data, gsize size)
{
  guint8 vop_start_code, coding_type, modulo_time_base;
  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (vop != NULL, GST_MPEG4_PARSER_ERROR);

  if (vol->shape == GST_MPEG4_BINARY_ONLY) {
    /* TODO: implement binary only shapes */
    GST_WARNING ("Binary only shapes not supported");
    goto failed;
  }

  READ_UINT8 (&br, vop_start_code, 8);
  if (vop_start_code != GST_MPEG4_VIDEO_OBJ_PLANE)
    goto wrong_start_code;


  /* set default values */
  vop->modulo_time_base = 0;
  vop->rounding_type = 0;
  vop->top_field_first = 1;
  vop->alternate_vertical_scan_flag = 0;
  vop->fcode_forward = 1;
  vop->fcode_backward = 1;

  /*  Compute macroblock informations */
  if (vol->interlaced)
    vop->mb_height = (2 * (vol->height + 31) / 32);
  else
    vop->mb_height = (vol->height + 15) / 16;

  vop->mb_width = (vol->width + 15) / 16;
  vop->mb_num = vop->mb_height * vop->mb_width;

  READ_UINT8 (&br, coding_type, 2);
  vop->coding_type = coding_type;

  READ_UINT8 (&br, modulo_time_base, 1);
  while (modulo_time_base) {
    vop->modulo_time_base++;

    READ_UINT8 (&br, modulo_time_base, 1);
  }

  CHECK_REMAINING (&br, vol->vop_time_increment_bits + 3);

  MARKER_UNCHECKED (&br);
  vop->time_increment =
      gst_bit_reader_get_bits_uint16_unchecked (&br,
      vol->vop_time_increment_bits);
  MARKER_UNCHECKED (&br);

  vop->coded = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  if (!vop->coded)
    return GST_MPEG4_PARSER_OK;

  if (vol->newpred_enable) {
    guint16 nbbits =
        vop->time_increment + 3 < 15 ? vop->time_increment + 3 : 15;

    READ_UINT16 (&br, vop->id, nbbits);
    READ_UINT8 (&br, vop->id_for_prediction_indication, 1);
    if (vop->id_for_prediction_indication) {
      /* Would be nice if the standard actually told us... */
      READ_UINT16 (&br, vop->id, nbbits);
      CHECK_MARKER (&br);
    }
  }

  if (vol->shape != GST_MPEG4_BINARY_ONLY &&
      (vop->coding_type == GST_MPEG4_P_VOP ||
          (vop->coding_type == GST_MPEG4_S_VOP &&
              vol->sprite_enable == GST_MPEG4_SPRITE_GMG)))
    READ_UINT8 (&br, vop->rounding_type, 1);

  if ((vol->reduced_resolution_vop_enable) &&
      (vol->shape == GST_MPEG4_RECTANGULAR ||
          (vop->coding_type = GST_MPEG4_P_VOP ||
              vop->coding_type == GST_MPEG4_I_VOP)))
    READ_UINT8 (&br, vop->reduced_resolution, 1);

  if (vol->shape != GST_MPEG4_RECTANGULAR) {
    if (vol->sprite_enable == GST_MPEG4_SPRITE_STATIC &&
        vop->coding_type == GST_MPEG4_I_VOP) {
      CHECK_REMAINING (&br, 55);

      vop->width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
      MARKER_UNCHECKED (&br);

      vop->height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
      MARKER_UNCHECKED (&br);

      vop->horizontal_mc_spatial_ref =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
      MARKER_UNCHECKED (&br);

      vop->vertical_mc_spatial_ref =
          gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
      MARKER_UNCHECKED (&br);

      /* Recompute the Macroblock informations
       * accordingly to the new values */
      if (vol->interlaced)
        vop->mb_height = (2 * (vol->height + 31) / 32);
      else
        vop->mb_height = (vol->height + 15) / 16;

      vop->mb_width = (vol->width + 15) / 16;
      vop->mb_num = vop->mb_height * vop->mb_width;
    }

    if ((vol->shape != GST_MPEG4_BINARY_ONLY) &&
        vol->scalability && vol->enhancement_type)
      READ_UINT8 (&br, vop->background_composition, 1);

    READ_UINT8 (&br, vop->change_conv_ratio_disable, 1);

    READ_UINT8 (&br, vop->constant_alpha, 1);
    if (vop->constant_alpha)
      READ_UINT8 (&br, vop->constant_alpha_value, 1);
  }

  if (vol->shape != GST_MPEG4_BINARY_ONLY) {
    if (!vol->complexity_estimation_disable) {
      GST_WARNING ("Complexity estimation not supported");
      goto failed;
    }

    READ_UINT8 (&br, vop->intra_dc_vlc_thr, 3);

    if (vol->interlaced) {
      READ_UINT8 (&br, vop->top_field_first, 1);
      READ_UINT8 (&br, vop->alternate_vertical_scan_flag, 1);
    }
  }

  if ((vol->sprite_enable == GST_MPEG4_SPRITE_STATIC ||
          vol->sprite_enable == GST_MPEG4_SPRITE_GMG) &&
      vop->coding_type == GST_MPEG4_S_VOP) {

    /* only if @sprite_trajectory is not NULL we parse it */
    if (sprite_trajectory && vol->no_of_sprite_warping_points)
      parse_sprite_trajectory (&br, sprite_trajectory,
          vol->no_of_sprite_warping_points);

    if (vol->sprite_brightness_change) {
      GST_WARNING ("sprite_brightness_change not supported");
      goto failed;
    }

    if (vol->sprite_enable == GST_MPEG4_SPRITE_STATIC) {
      GST_WARNING ("sprite enable static not supported");
      goto failed;
    }
  }

  if (vol->shape != GST_MPEG4_BINARY_ONLY) {
    READ_UINT16 (&br, vop->quant, vol->quant_precision);

    if (vol->shape == GST_MPEG4_GRAYSCALE) {
      /* TODO implement grayscale support */
      GST_WARNING ("Grayscale shapes no supported");

      /* TODO implement me */
      goto failed;
    }

    if (vop->coding_type != GST_MPEG4_I_VOP) {
      READ_UINT8 (&br, vop->fcode_forward, 3);
      CHECK_ALLOWED (vop->fcode_forward, 1, 7);
    }

    if (vop->coding_type == GST_MPEG4_B_VOP) {
      READ_UINT8 (&br, vop->fcode_backward, 3);
      CHECK_ALLOWED (vop->fcode_backward, 1, 7);
    }
  }

  if (!vol->scalability) {
    if (vol->shape != GST_MPEG4_RECTANGULAR)
      READ_UINT8 (&br, vop->shape_coding_type, 1);

  } else {
    if (vol->enhancement_type) {
      READ_UINT8 (&br, vop->load_backward_shape, 1);

      if (vop->load_backward_shape) {
        GST_WARNING ("Load backward shape not supported");
        goto failed;
      }

      READ_UINT8 (&br, vop->ref_select_code, 2);
    }
  }

  vop->size = gst_bit_reader_get_pos (&br);
  /* More things to possibly parse ... */

  return GST_MPEG4_PARSER_OK;

failed:
  GST_WARNING ("failed parsing \"Video Object Plane\"");
  return GST_MPEG4_PARSER_ERROR;

wrong_start_code:
  GST_WARNING ("got buffer with wrong start code");
  goto failed;
}

/**
 * gst_mpeg4_parse_video_plane_with_short_header:
 * @shorthdr: The #GstMpeg4VideoPlaneShortHdr to parse
 * @data: The data to parse
 * @size: The size of the @data to parse
 */
GstMpeg4ParseResult
gst_mpeg4_parse_video_plane_short_header (GstMpeg4VideoPlaneShortHdr *
    shorthdr, const guint8 * data, gsize size)
{
  guint8 zero_bits;

  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (shorthdr != NULL, GST_MPEG4_PARSER_ERROR);

  if (gst_bit_reader_get_remaining (&br) < 48)
    goto failed;

  if (gst_bit_reader_get_bits_uint32_unchecked (&br, 22) != 0x20)
    goto failed;

  shorthdr->temporal_reference =
      gst_bit_reader_get_bits_uint8_unchecked (&br, 8);
  CHECK_MARKER (&br);
  zero_bits = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  if (zero_bits != 0x00)
    goto failed;

  shorthdr->split_screen_indicator =
      gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  shorthdr->document_camera_indicator =
      gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  shorthdr->full_picture_freeze_release =
      gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  shorthdr->source_format = gst_bit_reader_get_bits_uint8_unchecked (&br, 3);

  /* Set parameters/Table 6-25 */
  switch (shorthdr->source_format) {
    case 0x01:
      shorthdr->vop_width = 128;
      shorthdr->vop_height = 96;
      shorthdr->num_macroblocks_in_gob = 8;
      shorthdr->num_gobs_in_vop = 6;
      break;
    case 0x02:
      shorthdr->vop_width = 176;
      shorthdr->vop_height = 144;
      shorthdr->num_macroblocks_in_gob = 11;
      shorthdr->num_gobs_in_vop = 9;
      break;
    case 0x03:
      shorthdr->vop_width = 352;
      shorthdr->vop_height = 288;
      shorthdr->num_macroblocks_in_gob = 22;
      shorthdr->num_gobs_in_vop = 18;
      break;
    case 0x04:
      shorthdr->vop_width = 704;
      shorthdr->vop_height = 576;
      shorthdr->num_macroblocks_in_gob = 88;
      shorthdr->num_gobs_in_vop = 18;
      break;
    case 0x05:
      shorthdr->vop_width = 1408;
      shorthdr->vop_height = 1152;
      shorthdr->num_macroblocks_in_gob = 352;
      shorthdr->num_gobs_in_vop = 18;
      break;
    default:
      shorthdr->vop_width = 0;
      shorthdr->vop_height = 0;
      shorthdr->num_macroblocks_in_gob = 0;
      shorthdr->num_gobs_in_vop = 0;
  }

  shorthdr->picture_coding_type =
      gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
  zero_bits = gst_bit_reader_get_bits_uint8_unchecked (&br, 4);

  if (zero_bits != 0x00)
    goto failed;

  shorthdr->vop_quant = gst_bit_reader_get_bits_uint8_unchecked (&br, 5);
  zero_bits = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);

  if (zero_bits != 0x00)
    goto failed;

  do {
    READ_UINT8 (&br, shorthdr->pei, 1);

    if (shorthdr->pei == 1)
      READ_UINT8 (&br, shorthdr->psupp, 8);

  } while (shorthdr->pei == 1);

  shorthdr->size = gst_bit_reader_get_pos (&br);

  return GST_MPEG4_PARSER_OK;

failed:
  GST_WARNING ("Could not parse the Plane short header");

  return GST_MPEG4_PARSER_ERROR;
}

/**
 * gst_mpeg4_parse_video_packet_header:
 * @videopackethdr: The #GstMpeg4VideoPacketHdr structure to fill
 * @vol: The last parsed #GstMpeg4VideoObjectLayer, will be updated
 * with the informations found during the parsing
 * @vop: The last parsed #GstMpeg4VideoObjectPlane, will be updated
 * with the informations found during the parsing
 * @sprite_trajectory: A #GstMpeg4SpriteTrajectory to fill or %NULL
 * with the informations found during the parsing
 * @data: The data to parse, should be set after the resync marker.
 * @size: The size of the data to parse
 *
 * Parsers @data containing the video packet header
 * and fills the @videopackethdr structure
 */
GstMpeg4ParseResult
gst_mpeg4_parse_video_packet_header (GstMpeg4VideoPacketHdr * videopackethdr,
    GstMpeg4VideoObjectLayer * vol, GstMpeg4VideoObjectPlane * vop,
    GstMpeg4SpriteTrajectory * sprite_trajectory, const guint8 * data,
    gsize size)
{
  guint8 markersize;
  GstBitReader br = GST_BIT_READER_INIT (data, size);

  g_return_val_if_fail (videopackethdr != NULL, GST_MPEG4_PARSER_ERROR);
  g_return_val_if_fail (vol != NULL, GST_MPEG4_PARSER_ERROR);

  markersize = compute_resync_marker_size (vop, NULL, NULL);

  CHECK_REMAINING (&br, markersize);

  if (gst_bit_reader_get_bits_uint32_unchecked (&br, markersize) != 0x01)
    goto failed;

  if (vol->shape != GST_MPEG4_RECTANGULAR) {
    READ_UINT8 (&br, videopackethdr->header_extension_code, 1);
    if (vol->sprite_enable == GST_MPEG4_SPRITE_STATIC &&
        vop->coding_type == GST_MPEG4_I_VOP) {

      CHECK_REMAINING (&br, 56);

      U_READ_UINT16 (&br, vop->width, 13);
      CHECK_MARKER (&br);
      U_READ_UINT16 (&br, vop->height, 13);
      CHECK_MARKER (&br);
      U_READ_UINT16 (&br, vop->horizontal_mc_spatial_ref, 13);
      CHECK_MARKER (&br);
      U_READ_UINT16 (&br, vop->vertical_mc_spatial_ref, 13);
      CHECK_MARKER (&br);

      /* Update macroblock infirmations */
      vop->mb_height = (vop->height + 15) / 16;
      vop->mb_width = (vop->width + 15) / 16;
      vop->mb_num = vop->mb_height * vop->mb_width;
    }
  }

  READ_UINT16 (&br, videopackethdr->macroblock_number,
      g_bit_storage (vop->mb_num - 1));

  if (vol->shape != GST_MPEG4_BINARY_ONLY)
    READ_UINT16 (&br, videopackethdr->quant_scale, vol->quant_precision);

  if (vol->shape == GST_MPEG4_RECTANGULAR)
    READ_UINT8 (&br, videopackethdr->header_extension_code, 1);

  if (videopackethdr->header_extension_code) {
    guint timeincr = 0;
    guint8 bit = 0, coding_type;

    do {
      READ_UINT8 (&br, bit, 1);
      timeincr++;
    } while (bit);

    vol->vop_time_increment_bits = timeincr;

    CHECK_MARKER (&br);
    READ_UINT16 (&br, vop->time_increment, timeincr);
    CHECK_MARKER (&br);
    READ_UINT8 (&br, coding_type, 2);
    vop->coding_type = coding_type;

    if (vol->shape != GST_MPEG4_RECTANGULAR) {
      READ_UINT8 (&br, vop->change_conv_ratio_disable, 1);
      if (vop->coding_type != GST_MPEG4_I_VOP)
        READ_UINT8 (&br, vop->shape_coding_type, 1);
    }

    if (vol->shape != GST_MPEG4_BINARY_ONLY) {
      READ_UINT8 (&br, vop->intra_dc_vlc_thr, 3);

      if (sprite_trajectory && vol->sprite_enable == GST_MPEG4_SPRITE_GMG &&
          vop->coding_type == GST_MPEG4_S_VOP &&
          vol->no_of_sprite_warping_points > 0) {

        parse_sprite_trajectory (&br, sprite_trajectory,
            vol->no_of_sprite_warping_points);
      }

      if (vol->reduced_resolution_vop_enable &&
          vol->shape == GST_MPEG4_RECTANGULAR &&
          (vop->coding_type == GST_MPEG4_P_VOP ||
              vop->coding_type == GST_MPEG4_I_VOP))
        READ_UINT8 (&br, vop->reduced_resolution, 1);

      if (vop->coding_type != GST_MPEG4_I_VOP) {
        READ_UINT8 (&br, vop->fcode_forward, 3);
        CHECK_ALLOWED (vop->fcode_forward, 1, 7);
      }

      if (vop->coding_type == GST_MPEG4_B_VOP) {
        READ_UINT8 (&br, vop->fcode_backward, 3);
        CHECK_ALLOWED (vop->fcode_backward, 1, 7);
      }
    }
  }

  if (vol->newpred_enable) {
    guint16 nbbits =
        vol->vop_time_increment_bits + 3 < 15 ? vop->time_increment + 3 : 15;

    READ_UINT16 (&br, vop->id, nbbits);
    READ_UINT8 (&br, vop->id_for_prediction_indication, 1);
    if (vop->id_for_prediction_indication) {
      /* Would be nice if the standard actually told us... */
      READ_UINT16 (&br, vop->id, nbbits);
      CHECK_MARKER (&br);
    }
  }

  videopackethdr->size = gst_bit_reader_get_pos (&br);

failed:
  GST_DEBUG ("Failed to parse video packet header");

  return GST_MPEG4_PARSER_NO_PACKET;
}
