示例#1
0
static void
parse_exif_rational_tag (GstExifReader * exif_reader,
    const gchar * gst_tag, guint32 count, guint32 offset, gdouble multiplier)
{
  GstByteReader data_reader;
  guint32 real_offset;
  guint32 frac_n = 0;
  guint32 frac_d = 1;
  gdouble value;

  if (count > 1) {
    GST_WARNING ("Rationals with multiple entries are not supported");
  }
  if (offset < exif_reader->base_offset) {
    GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset,
        exif_reader->base_offset);
    return;
  }

  real_offset = offset - exif_reader->base_offset;
  if (real_offset >= GST_BUFFER_SIZE (exif_reader->buffer)) {
    GST_WARNING ("Invalid offset %u for buffer of size %u, not adding tag %s",
        real_offset, GST_BUFFER_SIZE (exif_reader->buffer), gst_tag);
    return;
  }

  gst_byte_reader_init_from_buffer (&data_reader, exif_reader->buffer);
  if (!gst_byte_reader_set_pos (&data_reader, real_offset))
    goto reader_fail;

  if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
    if (!gst_byte_reader_get_uint32_le (&data_reader, &frac_n) ||
        !gst_byte_reader_get_uint32_le (&data_reader, &frac_d))
      goto reader_fail;
  } else {
    if (!gst_byte_reader_get_uint32_be (&data_reader, &frac_n) ||
        !gst_byte_reader_get_uint32_be (&data_reader, &frac_d))
      goto reader_fail;
  }

  GST_DEBUG ("Read fraction for tag %s: %u/%u", gst_tag, frac_n, frac_d);

  gst_util_fraction_to_double (frac_n, frac_d, &value);

  value *= multiplier;
  GST_DEBUG ("Adding %s tag: %lf", gst_tag, value);
  gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE, gst_tag, value,
      NULL);

  return;

reader_fail:
  GST_WARNING ("Failed to read from byte reader. (Buffer too short?)");
}
示例#2
0
static gboolean
parse_exif_tag_header (GstByteReader * reader, gint byte_order,
    GstExifTagData * _tagdata)
{
  g_assert (_tagdata);

  /* read the fields */
  if (byte_order == G_LITTLE_ENDIAN) {
    if (!gst_byte_reader_get_uint16_le (reader, &_tagdata->tag) ||
        !gst_byte_reader_get_uint16_le (reader, &_tagdata->tag_type) ||
        !gst_byte_reader_get_uint32_le (reader, &_tagdata->count) ||
        !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) {
      return FALSE;
    }
    _tagdata->offset = GST_READ_UINT32_LE (_tagdata->offset_as_data);
  } else {
    if (!gst_byte_reader_get_uint16_be (reader, &_tagdata->tag) ||
        !gst_byte_reader_get_uint16_be (reader, &_tagdata->tag_type) ||
        !gst_byte_reader_get_uint32_be (reader, &_tagdata->count) ||
        !gst_byte_reader_get_data (reader, 4, &_tagdata->offset_as_data)) {
      return FALSE;
    }
    _tagdata->offset = GST_READ_UINT32_BE (_tagdata->offset_as_data);
  }

  return TRUE;
}
示例#3
0
/**
 * gst_tag_list_from_exif_buffer_with_tiff_header:
 * @buffer: The exif buffer
 *
 * Parses the exif tags starting with a tiff header structure.
 *
 * Returns: The taglist
 *
 * Since: 0.10.30
 */
GstTagList *
gst_tag_list_from_exif_buffer_with_tiff_header (const GstBuffer * buffer)
{
  GstByteReader reader;
  guint16 fortytwo = 42;
  guint16 endianness = 0;
  guint32 offset;
  GstTagList *taglist = NULL;
  GstBuffer *subbuffer;

  GST_LOG ("Parsing exif tags with tiff header of size %u",
      GST_BUFFER_SIZE (buffer));

  gst_byte_reader_init_from_buffer (&reader, buffer);

  GST_LOG ("Parsing the tiff header");
  if (!gst_byte_reader_get_uint16_be (&reader, &endianness)) {
    goto byte_reader_fail;
  }

  if (endianness == TIFF_LITTLE_ENDIAN) {
    if (!gst_byte_reader_get_uint16_le (&reader, &fortytwo) ||
        !gst_byte_reader_get_uint32_le (&reader, &offset))
      goto byte_reader_fail;
  } else if (endianness == TIFF_BIG_ENDIAN) {
    if (!gst_byte_reader_get_uint16_be (&reader, &fortytwo) ||
        !gst_byte_reader_get_uint32_be (&reader, &offset))
      goto byte_reader_fail;
  } else {
    GST_WARNING ("Invalid endianness number %u", endianness);
    return NULL;
  }

  if (fortytwo != 42) {
    GST_WARNING ("Invalid magic number %u, should be 42", fortytwo);
    return NULL;
  }

  subbuffer = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buffer) -
      (TIFF_HEADER_SIZE - 2));
  memcpy (GST_BUFFER_DATA (subbuffer),
      GST_BUFFER_DATA (buffer) + TIFF_HEADER_SIZE,
      GST_BUFFER_SIZE (buffer) - TIFF_HEADER_SIZE);

  taglist = gst_tag_list_from_exif_buffer (subbuffer,
      endianness == TIFF_LITTLE_ENDIAN ? G_LITTLE_ENDIAN : G_BIG_ENDIAN, 8);

  gst_buffer_unref (subbuffer);
  return taglist;

byte_reader_fail:
  {
    GST_WARNING ("Failed to read values from buffer");
    return NULL;
  }
}
示例#4
0
static GstFlowReturn
gst_png_parse_handle_frame (GstBaseParse * parse,
    GstBaseParseFrame * frame, gint * skipsize)
{
  GstPngParse *pngparse = GST_PNG_PARSE (parse);
  GstMapInfo map;
  GstByteReader reader;
  GstFlowReturn ret = GST_FLOW_OK;
  guint64 signature;
  guint width = 0, height = 0;

  gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
  gst_byte_reader_init (&reader, map.data, map.size);

  if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
    goto beach;

  if (signature != PNG_SIGNATURE) {
    for (;;) {
      guint offset;

      offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
          0x89504E47, 0, gst_byte_reader_get_remaining (&reader));

      if (offset == -1) {
        *skipsize = gst_byte_reader_get_remaining (&reader) - 4;
        goto beach;
      }

      gst_byte_reader_skip (&reader, offset);

      if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
        goto beach;

      if (signature == PNG_SIGNATURE) {
        /* We're skipping, go out, we'll be back */
        *skipsize = gst_byte_reader_get_pos (&reader);
        goto beach;
      }
      gst_byte_reader_skip (&reader, 4);
    }
  }

  gst_byte_reader_skip (&reader, 8);

  for (;;) {
    guint32 length;
    guint32 code;

    if (!gst_byte_reader_get_uint32_be (&reader, &length))
      goto beach;
    if (!gst_byte_reader_get_uint32_le (&reader, &code))
      goto beach;

    GST_TRACE_OBJECT (parse, "%" GST_FOURCC_FORMAT " chunk, %u bytes",
        GST_FOURCC_ARGS (code), length);

    if (code == GST_MAKE_FOURCC ('I', 'H', 'D', 'R')) {
      if (!gst_byte_reader_get_uint32_be (&reader, &width))
        goto beach;
      if (!gst_byte_reader_get_uint32_be (&reader, &height))
        goto beach;
      length -= 8;
    } else if (code == GST_MAKE_FOURCC ('I', 'D', 'A', 'T')) {
      gst_base_parse_set_min_frame_size (parse,
          gst_byte_reader_get_pos (&reader) + 4 + length + 12);
    }

    if (!gst_byte_reader_skip (&reader, length + 4))
      goto beach;

    if (code == GST_MAKE_FOURCC ('I', 'E', 'N', 'D')) {
      /* the start code and at least 2 empty frames (IHDR and IEND) */
      gst_base_parse_set_min_frame_size (parse, 8 + 12 + 12);

      if (pngparse->width != width || pngparse->height != height) {
        GstCaps *caps, *sink_caps;

        pngparse->height = height;
        pngparse->width = width;

        caps = gst_caps_new_simple ("image/png",
            "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);

        sink_caps =
            gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (pngparse));

        if (sink_caps) {
          GstStructure *st;
          gint fr_num, fr_denom;

          st = gst_caps_get_structure (sink_caps, 0);
          if (st
              && gst_structure_get_fraction (st, "framerate", &fr_num,
                  &fr_denom)) {
            gst_caps_set_simple (caps,
                "framerate", GST_TYPE_FRACTION, fr_num, fr_denom, NULL);
          } else {
            GST_WARNING_OBJECT (pngparse, "No framerate set");
          }

          gst_caps_unref (sink_caps);
        }

        if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps))
          ret = GST_FLOW_NOT_NEGOTIATED;

        gst_caps_unref (caps);

        if (ret != GST_FLOW_OK)
          goto beach;
      }


      gst_buffer_unmap (frame->buffer, &map);
      return gst_base_parse_finish_frame (parse, frame,
          gst_byte_reader_get_pos (&reader));
    }
  }

beach:

  gst_buffer_unmap (frame->buffer, &map);

  return ret;
}
示例#5
0
static GstFlowReturn
gst_pngdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
    GstAdapter * adapter, gboolean at_eos)
{
  gsize toadd = 0;
  GstByteReader reader;
  gconstpointer data;
  guint64 signature;
  gsize size;
  GstPngDec *pngdec = (GstPngDec *) decoder;

  GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);

  /* FIXME : The overhead of using scan_uint32 is massive */

  size = gst_adapter_available (adapter);
  GST_DEBUG ("Parsing PNG image data (%" G_GSIZE_FORMAT " bytes)", size);

  if (size < 8)
    goto need_more_data;

  data = gst_adapter_map (adapter, size);
  gst_byte_reader_init (&reader, data, size);

  if (pngdec->read_data == 0) {
    if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
      goto need_more_data;

    if (signature != PNG_SIGNATURE) {
      for (;;) {
        guint offset;

        offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
            0x89504E47, 0, gst_byte_reader_get_remaining (&reader));

        if (offset == -1) {
          gst_adapter_flush (adapter,
              gst_byte_reader_get_remaining (&reader) - 4);
          goto need_more_data;
        }

        if (!gst_byte_reader_skip (&reader, offset))
          goto need_more_data;

        if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
          goto need_more_data;

        if (signature == PNG_SIGNATURE) {
          /* We're skipping, go out, we'll be back */
          gst_adapter_flush (adapter, gst_byte_reader_get_pos (&reader));
          goto need_more_data;
        }
        if (!gst_byte_reader_skip (&reader, 4))
          goto need_more_data;
      }
    }
    pngdec->read_data = 8;
  }

  if (!gst_byte_reader_skip (&reader, pngdec->read_data))
    goto need_more_data;

  for (;;) {
    guint32 length;
    guint32 code;

    if (!gst_byte_reader_get_uint32_be (&reader, &length))
      goto need_more_data;
    if (!gst_byte_reader_get_uint32_le (&reader, &code))
      goto need_more_data;

    if (!gst_byte_reader_skip (&reader, length + 4))
      goto need_more_data;

    if (code == GST_MAKE_FOURCC ('I', 'E', 'N', 'D')) {
      /* Have complete frame */
      toadd = gst_byte_reader_get_pos (&reader);
      GST_DEBUG_OBJECT (decoder, "Have complete frame of size %" G_GSIZE_FORMAT,
          toadd);
      pngdec->read_data = 0;
      goto have_full_frame;
    } else
      pngdec->read_data += length + 12;
  }

  g_assert_not_reached ();
  return GST_FLOW_ERROR;

need_more_data:
  return GST_VIDEO_DECODER_FLOW_NEED_DATA;

have_full_frame:
  if (toadd)
    gst_video_decoder_add_to_frame (decoder, toadd);
  return gst_video_decoder_have_frame (decoder);
}
示例#6
0
static gint
deserialize_geo_coordinate (GstExifReader * exif_reader,
    GstByteReader * reader, const GstExifTagMatch * exiftag,
    GstExifTagData * tagdata)
{
  GstByteReader fractions_reader;
  gint multiplier;
  GstExifTagData next_tagdata;
  gint ret = 0;
  /* for the conversion */
  guint32 degrees_n = 0;
  guint32 degrees_d = 1;
  guint32 minutes_n = 0;
  guint32 minutes_d = 1;
  guint32 seconds_n = 0;
  guint32 seconds_d = 1;
  gdouble degrees;
  gdouble minutes;
  gdouble seconds;

  GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag,
      exiftag->exif_tag);

  if (exiftag->complementary_tag != tagdata->tag) {
    /* First should come the 'Ref' tags */
    GST_WARNING ("Tag %d is not the 'Ref' tag for latitude nor longitude",
        tagdata->tag);
    return ret;
  }

  if (tagdata->offset_as_data[0] == 'N' || tagdata->offset_as_data[0] == 'E') {
    multiplier = 1;
  } else if (tagdata->offset_as_data[0] == 'S'
      || tagdata->offset_as_data[0] == 'W') {
    multiplier = -1;
  } else {
    GST_WARNING ("Invalid LatitudeRef or LongitudeRef %c",
        tagdata->offset_as_data[0]);
    return ret;
  }

  /* now read the following tag that must be the latitude or longitude */
  if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
    if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag))
      goto reader_fail;
  } else {
    if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag))
      goto reader_fail;
  }

  if (exiftag->exif_tag != next_tagdata.tag) {
    GST_WARNING ("This is not a geo cordinate tag");
    return ret;
  }

  /* read the remaining tag entry data */
  if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) {
    ret = -1;
    goto reader_fail;
  }

  ret = 1;

  /* some checking */
  if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) {
    GST_WARNING ("Invalid type %d for geo coordinate (latitude/longitude)",
        next_tagdata.tag_type);
    return ret;
  }
  if (next_tagdata.count != 3) {
    GST_WARNING ("Geo coordinate should use 3 fractions, we have %u",
        next_tagdata.count);
    return ret;
  }

  /* now parse the fractions */
  gst_byte_reader_init_from_buffer (&fractions_reader, exif_reader->buffer);
  if (!gst_byte_reader_set_pos (&fractions_reader,
          next_tagdata.offset - exif_reader->base_offset))
    goto reader_fail;

  if (exif_reader->byte_order == G_LITTLE_ENDIAN) {
    if (!gst_byte_reader_get_uint32_le (&fractions_reader, &degrees_n) ||
        !gst_byte_reader_get_uint32_le (&fractions_reader, &degrees_d) ||
        !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_n) ||
        !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_d) ||
        !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_n) ||
        !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_d))
      goto reader_fail;
  } else {
    if (!gst_byte_reader_get_uint32_be (&fractions_reader, &degrees_n) ||
        !gst_byte_reader_get_uint32_be (&fractions_reader, &degrees_d) ||
        !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_n) ||
        !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_d) ||
        !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_n) ||
        !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_d))
      goto reader_fail;
  }

  GST_DEBUG ("Read degrees fraction for tag %s: %u/%u %u/%u %u/%u",
      exiftag->gst_tag, degrees_n, degrees_d, minutes_n, minutes_d,
      seconds_n, seconds_d);

  gst_util_fraction_to_double (degrees_n, degrees_d, &degrees);
  gst_util_fraction_to_double (minutes_n, minutes_d, &minutes);
  gst_util_fraction_to_double (seconds_n, seconds_d, &seconds);

  minutes += seconds / 60;
  degrees += minutes / 60;
  degrees *= multiplier;

  GST_DEBUG ("Adding %s tag: %lf", exiftag->gst_tag, degrees);
  gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE,
      exiftag->gst_tag, degrees, NULL);

  return ret;

reader_fail:
  GST_WARNING ("Failed to read fields from buffer (too short?)");
  return ret;
}
示例#7
0
static void
gst_exif_tag_rewrite_offsets (GstExifWriter * writer, guint32 base_offset)
{
  guint32 offset;

  GST_LOG ("Rewriting tag entries offsets");

  offset = gst_byte_writer_get_size (&writer->tagwriter);
  while (gst_byte_writer_get_pos (&writer->tagwriter) <
      gst_byte_writer_get_size (&writer->tagwriter)) {
    guint16 type = 0;
    guint32 cur_offset = 0;
    GstByteReader *reader;
    gint byte_size = 0;
    guint32 count = 0;
    guint16 tag_id = 0;

    reader = (GstByteReader *) & writer->tagwriter;

    /* read the type */
    if (writer->byte_order == G_LITTLE_ENDIAN) {
      if (!gst_byte_reader_get_uint16_le (reader, &tag_id))
        break;
      if (!gst_byte_reader_get_uint16_le (reader, &type))
        break;
      if (!gst_byte_reader_get_uint32_le (reader, &count))
        break;
    } else {
      if (!gst_byte_reader_get_uint16_be (reader, &tag_id))
        break;
      if (!gst_byte_reader_get_uint16_be (reader, &type))
        break;
      if (!gst_byte_reader_get_uint32_be (reader, &count))
        break;
    }

    switch (type) {
      case EXIF_TYPE_BYTE:
      case EXIF_TYPE_ASCII:
      case EXIF_TYPE_UNDEFINED:
        byte_size = count;
        break;
      case EXIF_TYPE_SHORT:
        byte_size = count * 2;  /* 2 bytes */
        break;
      case EXIF_TYPE_LONG:
      case EXIF_TYPE_SLONG:
        byte_size = count * 4;  /* 4 bytes */
        break;
      case EXIF_TYPE_RATIONAL:
      case EXIF_TYPE_SRATIONAL:
        byte_size = count * 8;  /* 8 bytes */
        break;
      default:
        g_assert_not_reached ();
        break;
    }

    /* adjust the offset if needed */
    if (byte_size > 4 || tag_id == EXIF_GPS_IFD_TAG) {
      if (writer->byte_order == G_LITTLE_ENDIAN) {
        if (gst_byte_reader_peek_uint32_le (reader, &cur_offset)) {
          gst_byte_writer_put_uint32_le (&writer->tagwriter,
              cur_offset + offset + base_offset);
        }
      } else {
        if (gst_byte_reader_peek_uint32_be (reader, &cur_offset)) {
          gst_byte_writer_put_uint32_be (&writer->tagwriter,
              cur_offset + offset + base_offset);
        }
      }
      GST_DEBUG ("Rewriting tag offset from %u to (%u + %u + %u) %u",
          cur_offset, cur_offset, offset, base_offset,
          cur_offset + offset + base_offset);
    } else {
      gst_byte_reader_skip (reader, 4);
      GST_DEBUG ("No need to rewrite tag offset");
    }
  }
}
示例#8
0
static GstFlowReturn
gst_png_parse_handle_frame (GstBaseParse * parse,
                            GstBaseParseFrame * frame, gint * skipsize)
{
    GstPngParse *pngparse = GST_PNG_PARSE (parse);
    GstMapInfo map;
    GstByteReader reader;
    GstFlowReturn ret = GST_FLOW_OK;
    guint64 signature;
    guint width = 0, height = 0;

    gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
    gst_byte_reader_init (&reader, map.data, map.size);

    if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
        goto beach;

    if (signature != PNG_SIGNATURE) {
        for (;;) {
            guint offset;

            offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
                     0x89504E47, 0, gst_byte_reader_get_remaining (&reader));

            if (offset == -1) {
                *skipsize = gst_byte_reader_get_remaining (&reader) - 4;
                goto beach;
            }

            gst_byte_reader_skip (&reader, offset);

            if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
                goto beach;

            if (signature == PNG_SIGNATURE) {
                /* We're skipping, go out, we'll be back */
                *skipsize = gst_byte_reader_get_pos (&reader);
                goto beach;
            }
            gst_byte_reader_skip (&reader, 4);
        }
    }

    gst_byte_reader_skip (&reader, 8);

    for (;;) {
        guint32 length;
        guint32 code;

        if (!gst_byte_reader_get_uint32_be (&reader, &length))
            goto beach;
        if (!gst_byte_reader_get_uint32_le (&reader, &code))
            goto beach;

        if (code == GST_MAKE_FOURCC ('I', 'H', 'D', 'R')) {
            if (!gst_byte_reader_get_uint32_be (&reader, &width))
                goto beach;
            if (!gst_byte_reader_get_uint32_be (&reader, &height))
                goto beach;
            length -= 8;
        }

        if (!gst_byte_reader_skip (&reader, length + 4))
            goto beach;

        if (code == GST_MAKE_FOURCC ('I', 'E', 'N', 'D')) {
            if (pngparse->width != width || pngparse->height != height) {
                GstCaps *caps;

                pngparse->height = height;
                pngparse->width = width;

                caps = gst_caps_new_simple ("image/png",
                                            "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
                if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps)) {
                    ret = GST_FLOW_NOT_NEGOTIATED;
                }
                gst_caps_unref (caps);

                if (ret != GST_FLOW_OK)
                    goto beach;
            }


            gst_buffer_unmap (frame->buffer, &map);
            return gst_base_parse_finish_frame (parse, frame,
                                                gst_byte_reader_get_pos (&reader));
        }
    }

beach:

    gst_buffer_unmap (frame->buffer, &map);

    return ret;
}
示例#9
0
static GstFlowReturn
gst_png_parse_handle_frame (GstBaseParse * parse,
    GstBaseParseFrame * frame, gint * skipsize)
{
  GstPngParse *pngparse = GST_PNG_PARSE (parse);
  GstMapInfo map;
  GstByteReader reader;
  GstFlowReturn ret = GST_FLOW_OK;
  guint64 signature;
  guint width = 0, height = 0;

  gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
  gst_byte_reader_init (&reader, map.data, map.size);

  if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
    goto beach;

  if (signature != PNG_SIGNATURE) {
    for (;;) {
      guint offset;

      offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
          0x89504E47, 0, gst_byte_reader_get_remaining (&reader));

      if (offset == -1) {
        *skipsize = gst_byte_reader_get_remaining (&reader) - 4;
        goto beach;
      }

      gst_byte_reader_skip (&reader, offset);

      if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
        goto beach;

      if (signature == PNG_SIGNATURE) {
        /* We're skipping, go out, we'll be back */
        *skipsize = gst_byte_reader_get_pos (&reader);
        goto beach;
      }
      gst_byte_reader_skip (&reader, 4);
    }
  }

  gst_byte_reader_skip (&reader, 8);

  for (;;) {
    guint32 length;
    guint32 code;

    if (!gst_byte_reader_get_uint32_be (&reader, &length))
      goto beach;
    if (!gst_byte_reader_get_uint32_le (&reader, &code))
      goto beach;

    if (code == GST_MAKE_FOURCC ('I', 'H', 'D', 'R')) {
      if (!gst_byte_reader_get_uint32_be (&reader, &width))
        goto beach;
      if (!gst_byte_reader_get_uint32_be (&reader, &height))
        goto beach;
      length -= 8;
    }

    if (!gst_byte_reader_skip (&reader, length + 4))
      goto beach;

    if (code == GST_MAKE_FOURCC ('I', 'E', 'N', 'D')) {
      if (pngparse->width != width || pngparse->height != height) {
        GstStructure *st = NULL;
        GstCaps *caps, *sink_caps;
        gint fr_num, fr_denom;

        pngparse->height = height;
        pngparse->width = width;

        sink_caps =
            gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (pngparse));
        if (sink_caps && (st = gst_caps_get_structure (sink_caps, 0))
            && gst_structure_get_fraction (st, "framerate", &fr_num, &fr_denom)) {
          /* Got it in caps - nothing more to do */
          GST_DEBUG_OBJECT (pngparse,
              "sink caps override framerate from headers");
        } else {
          GST_INFO_OBJECT (pngparse, "No framerate set");
        }

        caps = gst_caps_new_simple ("image/png",
            "width", G_TYPE_INT, width, "height", G_TYPE_INT, height,
            "framerate", GST_TYPE_FRACTION, fr_num, fr_denom, NULL);

        if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps)) {
          ret = GST_FLOW_NOT_NEGOTIATED;
        }
        gst_caps_unref (caps);

        if (ret != GST_FLOW_OK)
          goto beach;
      }


      gst_buffer_unmap (frame->buffer, &map);
      return gst_base_parse_finish_frame (parse, frame,
          gst_byte_reader_get_pos (&reader));
    }
  }

beach:

  gst_buffer_unmap (frame->buffer, &map);

  return ret;
}
static GstFlowReturn
gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstByteReader reader;
  GstBuffer *input;
  GstMapInfo map_info;
  GstCaps *caps;
  guint available;
  GstFlowReturn res = GST_FLOW_OK;

  GstFlxDec *flxdec;
  FlxHeader *flxh;

  g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
  flxdec = (GstFlxDec *) parent;
  g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR);

  gst_adapter_push (flxdec->adapter, buf);
  available = gst_adapter_available (flxdec->adapter);
  input = gst_adapter_get_buffer (flxdec->adapter, available);
  if (!gst_buffer_map (input, &map_info, GST_MAP_READ)) {
    GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
        ("%s", "Failed to map buffer"), (NULL));
    goto error;
  }
  gst_byte_reader_init (&reader, map_info.data, map_info.size);

  if (flxdec->state == GST_FLXDEC_READ_HEADER) {
    if (available >= FlxHeaderSize) {
      GstByteReader header;
      GstCaps *templ;

      if (!gst_byte_reader_get_sub_reader (&reader, &header, FlxHeaderSize)) {
        GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
            ("%s", "Could not read header"), (NULL));
        goto unmap_input_error;
      }
      gst_adapter_flush (flxdec->adapter, FlxHeaderSize);
      available -= FlxHeaderSize;

      if (!_read_flx_header (flxdec, &header, &flxdec->hdr)) {
        GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
            ("%s", "Failed to parse header"), (NULL));
        goto unmap_input_error;
      }

      flxh = &flxdec->hdr;

      /* check header */
      if (flxh->type != FLX_MAGICHDR_FLI &&
          flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX) {
        GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
            ("not a flx file (type %x)", flxh->type));
        goto unmap_input_error;
      }

      GST_INFO_OBJECT (flxdec, "size      :  %d", flxh->size);
      GST_INFO_OBJECT (flxdec, "frames    :  %d", flxh->frames);
      GST_INFO_OBJECT (flxdec, "width     :  %d", flxh->width);
      GST_INFO_OBJECT (flxdec, "height    :  %d", flxh->height);
      GST_INFO_OBJECT (flxdec, "depth     :  %d", flxh->depth);
      GST_INFO_OBJECT (flxdec, "speed     :  %d", flxh->speed);

      flxdec->next_time = 0;

      if (flxh->type == FLX_MAGICHDR_FLI) {
        flxdec->frame_time = JIFFIE * flxh->speed;
      } else if (flxh->speed == 0) {
        flxdec->frame_time = GST_SECOND / 70;
      } else {
        flxdec->frame_time = flxh->speed * GST_MSECOND;
      }

      flxdec->duration = flxh->frames * flxdec->frame_time;
      GST_LOG ("duration   :  %" GST_TIME_FORMAT,
          GST_TIME_ARGS (flxdec->duration));

      templ = gst_pad_get_pad_template_caps (flxdec->srcpad);
      caps = gst_caps_copy (templ);
      gst_caps_unref (templ);
      gst_caps_set_simple (caps,
          "width", G_TYPE_INT, flxh->width,
          "height", G_TYPE_INT, flxh->height,
          "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
          (gint) flxdec->frame_time / 1000, NULL);

      gst_pad_set_caps (flxdec->srcpad, caps);
      gst_caps_unref (caps);

      if (flxdec->need_segment) {
        gst_pad_push_event (flxdec->srcpad,
            gst_event_new_segment (&flxdec->segment));
        flxdec->need_segment = FALSE;
      }

      /* zero means 8 */
      if (flxh->depth == 0)
        flxh->depth = 8;

      if (flxh->depth != 8) {
        GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE,
            ("%s", "Don't know how to decode non 8 bit depth streams"), (NULL));
        goto unmap_input_error;
      }

      flxdec->converter =
          flx_colorspace_converter_new (flxh->width, flxh->height);

      if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
        GST_INFO_OBJECT (flxdec, "(FLC) aspect_dx :  %d", flxh->aspect_dx);
        GST_INFO_OBJECT (flxdec, "(FLC) aspect_dy :  %d", flxh->aspect_dy);
        GST_INFO_OBJECT (flxdec, "(FLC) oframe1   :  0x%08x", flxh->oframe1);
        GST_INFO_OBJECT (flxdec, "(FLC) oframe2   :  0x%08x", flxh->oframe2);
      }

      flxdec->size = ((guint) flxh->width * (guint) flxh->height);
      if (flxdec->size >= G_MAXSIZE / 4) {
        GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
            ("%s", "Cannot allocate required memory"), (NULL));
        goto unmap_input_error;
      }

      /* create delta and output frame */
      flxdec->frame_data = g_malloc0 (flxdec->size);
      flxdec->delta_data = g_malloc0 (flxdec->size);

      flxdec->state = GST_FLXDEC_PLAYING;
    }
  } else if (flxdec->state == GST_FLXDEC_PLAYING) {
    GstBuffer *out;

    /* while we have enough data in the adapter */
    while (available >= FlxFrameChunkSize && res == GST_FLOW_OK) {
      guint32 size;
      guint16 type;

      if (!gst_byte_reader_get_uint32_le (&reader, &size))
        goto parse_error;
      if (available < size)
        goto need_more_data;

      available -= size;
      gst_adapter_flush (flxdec->adapter, size);

      if (!gst_byte_reader_get_uint16_le (&reader, &type))
        goto parse_error;

      switch (type) {
        case FLX_FRAME_TYPE:{
          GstByteReader chunks;
          GstByteWriter writer;
          guint16 n_chunks;
          GstMapInfo map;

          GST_LOG_OBJECT (flxdec, "Have frame type 0x%02x of size %d", type,
              size);

          if (!gst_byte_reader_get_sub_reader (&reader, &chunks,
                  size - FlxFrameChunkSize))
            goto parse_error;

          if (!gst_byte_reader_get_uint16_le (&chunks, &n_chunks))
            goto parse_error;
          GST_LOG_OBJECT (flxdec, "Have %d chunks", n_chunks);

          if (n_chunks == 0)
            break;
          if (!gst_byte_reader_skip (&chunks, 8))       /* reserved */
            goto parse_error;

          gst_byte_writer_init_with_data (&writer, flxdec->frame_data,
              flxdec->size, TRUE);

          /* decode chunks */
          if (!flx_decode_chunks (flxdec, n_chunks, &chunks, &writer)) {
            GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
                ("%s", "Could not decode chunk"), NULL);
            goto unmap_input_error;
          }
          gst_byte_writer_reset (&writer);

          /* save copy of the current frame for possible delta. */
          memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size);

          out = gst_buffer_new_and_alloc (flxdec->size * 4);
          if (!gst_buffer_map (out, &map, GST_MAP_WRITE)) {
            GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
                ("%s", "Could not map output buffer"), NULL);
            gst_buffer_unref (out);
            goto unmap_input_error;
          }

          /* convert current frame. */
          flx_colorspace_convert (flxdec->converter, flxdec->frame_data,
              map.data);
          gst_buffer_unmap (out, &map);

          GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
          flxdec->next_time += flxdec->frame_time;

          res = gst_pad_push (flxdec->srcpad, out);
          break;
        }
        default:
          GST_DEBUG_OBJECT (flxdec, "Unknown frame type 0x%02x, skipping %d",
              type, size);
          if (!gst_byte_reader_skip (&reader, size - FlxFrameChunkSize))
            goto parse_error;
          break;
      }
    }
  }

need_more_data:
  gst_buffer_unmap (input, &map_info);
  gst_buffer_unref (input);
  return res;

  /* ERRORS */
parse_error:
  GST_ELEMENT_ERROR (flxdec, STREAM, DECODE,
      ("%s", "Failed to parse stream"), (NULL));
unmap_input_error:
  gst_buffer_unmap (input, &map_info);
error:
  gst_buffer_unref (input);
  return GST_FLOW_ERROR;
}
static gboolean
_read_flx_header (GstFlxDec * flxdec, GstByteReader * reader, FlxHeader * flxh)
{
  memset (flxh, 0, sizeof (*flxh));

  if (!gst_byte_reader_get_uint32_le (reader, &flxh->size))
    goto error;
  if (flxh->size < FlxHeaderSize) {
    GST_ERROR_OBJECT (flxdec, "Invalid file size in the header");
    return FALSE;
  }

  if (!gst_byte_reader_get_uint16_le (reader, &flxh->type))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->frames))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->width))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->height))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->depth))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->flags))
    goto error;
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->speed))
    goto error;
  if (!gst_byte_reader_skip (reader, 2))        /* reserved */
    goto error;
  /* FLC */
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->created))
    goto error;
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->creator))
    goto error;
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->updated))
    goto error;
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->updater))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->aspect_dx))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->aspect_dy))
    goto error;
  /* EGI */
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->ext_flags))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->keyframes))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->totalframes))
    goto error;
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->req_memory))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->max_regions))
    goto error;
  if (!gst_byte_reader_get_uint16_le (reader, &flxh->transp_num))
    goto error;
  if (!gst_byte_reader_skip (reader, 24))       /* reserved */
    goto error;
  /* FLC */
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->oframe1))
    goto error;
  if (!gst_byte_reader_get_uint32_le (reader, &flxh->oframe2))
    goto error;
  if (!gst_byte_reader_skip (reader, 40))       /* reserved */
    goto error;

  return TRUE;

error:
  GST_ERROR_OBJECT (flxdec, "Error reading file header");
  return FALSE;
}
static gboolean
flx_decode_chunks (GstFlxDec * flxdec, gulong n_chunks, GstByteReader * reader,
    GstByteWriter * writer)
{
  gboolean ret = TRUE;

  while (n_chunks--) {
    GstByteReader chunk;
    guint32 size;
    guint16 type;

    if (!gst_byte_reader_get_uint32_le (reader, &size))
      goto parse_error;
    if (!gst_byte_reader_get_uint16_le (reader, &type))
      goto parse_error;
    GST_LOG_OBJECT (flxdec, "chunk has type 0x%02x size %d", type, size);

    if (!gst_byte_reader_get_sub_reader (reader, &chunk,
            size - FlxFrameChunkSize)) {
      GST_ERROR_OBJECT (flxdec, "Incorrect size in the chunk header");
      goto error;
    }

    switch (type) {
      case FLX_COLOR64:
        ret = flx_decode_color (flxdec, &chunk, writer, 2);
        break;

      case FLX_COLOR256:
        ret = flx_decode_color (flxdec, &chunk, writer, 0);
        break;

      case FLX_BRUN:
        ret = flx_decode_brun (flxdec, &chunk, writer);
        break;

      case FLX_LC:
        ret = flx_decode_delta_fli (flxdec, &chunk, writer);
        break;

      case FLX_SS2:
        ret = flx_decode_delta_flc (flxdec, &chunk, writer);
        break;

      case FLX_BLACK:
        ret = gst_byte_writer_fill (writer, 0, flxdec->size);
        break;

      case FLX_MINI:
        break;

      default:
        GST_WARNING ("Unimplemented chunk type: 0x%02x size: %d - skipping",
            type, size);
        break;
    }

    if (!ret)
      break;
  }

  return ret;

parse_error:
  GST_ERROR_OBJECT (flxdec, "Failed to decode chunk");
error:
  return FALSE;
}