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?)"); }
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; }
/** * 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; } }
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; }
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); }
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, °rees_n) || !gst_byte_reader_get_uint32_le (&fractions_reader, °rees_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, °rees_n) || !gst_byte_reader_get_uint32_be (&fractions_reader, °rees_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, °rees); 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; }
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"); } } }
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; }
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; }