static inline gboolean gst_jpeg_parse_remove_marker (GstJpegParse * parse, GstByteReader * reader, guint8 marker, GstBuffer * buffer) { guint16 size = 0; guint pos = gst_byte_reader_get_pos (reader); guint8 *data; gsize bsize; if (!gst_byte_reader_peek_uint16_be (reader, &size)) return FALSE; if (gst_byte_reader_get_remaining (reader) < size) return FALSE; GST_LOG_OBJECT (parse, "unhandled marker %x removing %u bytes", marker, size); data = gst_buffer_map (buffer, &bsize, NULL, GST_MAP_READWRITE); memmove (&data[pos], &data[pos + size], bsize - (pos + size)); gst_buffer_unmap (buffer, data, bsize - size); if (!gst_byte_reader_set_pos (reader, pos - size)) return FALSE; return TRUE; }
static guint find_psc (GstBuffer * buffer, guint skip) { GstByteReader br; guint psc_pos = -1, psc; gst_byte_reader_init_from_buffer (&br, buffer); if (!gst_byte_reader_set_pos (&br, skip)) goto out; gst_byte_reader_peek_uint24_be (&br, &psc); /* 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 & 0xffffc0) == 0x000080)) { psc_pos = gst_byte_reader_get_pos (&br); break; } else gst_byte_reader_skip (&br, 1); } out: return psc_pos; }
static GstBuffer * gst_vdp_mpeg_packetizer_get_next_packet (GstVdpMpegPacketizer * packetizer) { guint offset, size; GstBuffer *buf; if (packetizer->start == -1) return NULL; if (!gst_byte_reader_set_pos (&packetizer->reader, packetizer->start + 3)) return NULL; offset = gst_byte_reader_masked_scan_uint32 (&packetizer->reader, 0xffffff00, 0x00000100, 0, gst_byte_reader_get_remaining (&packetizer->reader)); if (offset != -1) { offset = gst_byte_reader_get_pos (&packetizer->reader) + offset; size = offset - packetizer->start; } else size = gst_byte_reader_get_remaining (&packetizer->reader) + 3; buf = gst_buffer_create_sub (packetizer->buffer, packetizer->start, size); packetizer->start = offset; return buf; }
static guint find_psc (GstBuffer * buffer, guint skip) { GstMapInfo map; GstByteReader br; guint psc_pos = -1, psc; gst_buffer_map (buffer, &map, GST_MAP_READ); gst_byte_reader_init (&br, map.data, map.size); if (!gst_byte_reader_set_pos (&br, skip)) goto out; if (gst_byte_reader_peek_uint24_be (&br, &psc) == FALSE) goto out; /* 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 & 0xffffc0) == 0x000080)) { psc_pos = gst_byte_reader_get_pos (&br); break; } else if (gst_byte_reader_skip (&br, 1) == FALSE) break; } out: gst_buffer_unmap (buffer, &map); return psc_pos; }
GstIsoffParserResult gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buffer, guint * consumed) { GstIsoffParserResult res = GST_ISOFF_PARSER_OK; GstByteReader reader; GstMapInfo info; guint32 fourcc; if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) { *consumed = 0; return GST_ISOFF_PARSER_ERROR; } gst_byte_reader_init (&reader, info.data, info.size); if (parser->status == GST_ISOFF_SIDX_PARSER_INIT) { if (!gst_isoff_parse_box_header (&reader, &fourcc, NULL, NULL, &parser->size)) goto done; if (fourcc != GST_ISOFF_FOURCC_SIDX) { res = GST_ISOFF_PARSER_UNEXPECTED; gst_byte_reader_set_pos (&reader, 0); goto done; } if (parser->size == 0) { res = GST_ISOFF_PARSER_ERROR; gst_byte_reader_set_pos (&reader, 0); goto done; } /* Try again once we have enough data for the FullBox header */ if (gst_byte_reader_get_remaining (&reader) < 4) { gst_byte_reader_set_pos (&reader, 0); goto done; } } res = gst_isoff_sidx_parser_parse (parser, &reader, consumed); done: gst_buffer_unmap (buffer, &info); return res; }
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?)"); }
/* gst_isoff_parse_box: * @reader: * @type: type that was found at the current position * @extended_type: (allow-none): extended type if type=='uuid' * @header_size: (allow-none): size of the box header (type, extended type and size) * @size: size of the complete box including type, extended type and size * * Advances the byte reader to the start of the box content. To skip * over the complete box, skip size - header_size bytes. * * Returns: TRUE if a box header could be parsed, FALSE if more data is needed */ gboolean gst_isoff_parse_box_header (GstByteReader * reader, guint32 * type, guint8 extended_type[16], guint * header_size, guint64 * size) { guint header_start_offset; guint32 size_field; header_start_offset = gst_byte_reader_get_pos (reader); if (gst_byte_reader_get_remaining (reader) < 8) goto not_enough_data; size_field = gst_byte_reader_get_uint32_be_unchecked (reader); *type = gst_byte_reader_get_uint32_le_unchecked (reader); if (size_field == 1) { if (gst_byte_reader_get_remaining (reader) < 8) goto not_enough_data; *size = gst_byte_reader_get_uint64_be_unchecked (reader); } else { *size = size_field; } if (*type == GST_ISOFF_FOURCC_UUID) { if (gst_byte_reader_get_remaining (reader) < 16) goto not_enough_data; if (extended_type) memcpy (extended_type, gst_byte_reader_get_data_unchecked (reader, 16), 16); } if (header_size) *header_size = gst_byte_reader_get_pos (reader) - header_start_offset; return TRUE; not_enough_data: gst_byte_reader_set_pos (reader, header_start_offset); return FALSE; }
static inline gboolean gst_jpeg_parse_remove_marker (GstJpegParse * parse, GstByteReader * reader, guint8 marker, GstBuffer * buffer) { guint16 size = 0; guint pos = gst_byte_reader_get_pos (reader); guint8 *data = GST_BUFFER_DATA (buffer); if (!gst_byte_reader_peek_uint16_be (reader, &size)) return FALSE; if (gst_byte_reader_get_remaining (reader) < size) return FALSE; GST_LOG_OBJECT (parse, "unhandled marker %x removing %u bytes", marker, size); memmove (&data[pos], &data[pos + size], GST_BUFFER_SIZE (buffer) - (pos + size)); GST_BUFFER_SIZE (buffer) -= size; if (!gst_byte_reader_set_pos (reader, pos - size)) return FALSE; return TRUE; }
static gboolean parse_exif_ifd (GstExifReader * exif_reader, gint buf_offset, const GstExifTagMatch * tag_map) { GstByteReader reader; guint16 entries = 0; guint16 i; g_return_val_if_fail (exif_reader->byte_order == G_LITTLE_ENDIAN || exif_reader->byte_order == G_BIG_ENDIAN, FALSE); gst_byte_reader_init_from_buffer (&reader, exif_reader->buffer); if (!gst_byte_reader_set_pos (&reader, buf_offset)) { GST_WARNING ("Buffer offset invalid when parsing exif ifd"); return FALSE; } /* read the IFD entries number */ if (exif_reader->byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint16_le (&reader, &entries)) goto read_error; } else { if (!gst_byte_reader_get_uint16_be (&reader, &entries)) goto read_error; } GST_DEBUG ("Read number of entries: %u", entries); /* iterate over the buffer and find the tags and stuff */ for (i = 0; i < entries; i++) { GstExifTagData tagdata; gint map_index; GST_LOG ("Reading entry: %u", i); if (!parse_exif_tag_header (&reader, exif_reader->byte_order, &tagdata)) goto read_error; GST_DEBUG ("Parsed tag: id 0x%x, type %u, count %u, offset %u (0x%x)", tagdata.tag, tagdata.tag_type, tagdata.count, tagdata.offset, tagdata.offset); map_index = exif_tag_map_find_reverse (tagdata.tag, tag_map, TRUE); if (map_index == -1) { GST_WARNING ("Unmapped exif tag: 0x%x", tagdata.tag); continue; } /* inner ifd tags handling */ if (tagdata.tag == EXIF_GPS_IFD_TAG) { i += parse_exif_ifd (exif_reader, tagdata.offset - exif_reader->base_offset, tag_map_gps); continue; } /* tags that need specialized deserialization */ if (tag_map[map_index].deserialize) { i += tag_map[map_index].deserialize (exif_reader, &reader, &tag_map[map_index], &tagdata); continue; } switch (tagdata.tag_type) { case EXIF_TYPE_ASCII: parse_exif_ascii_tag (exif_reader, tag_map[map_index].gst_tag, tagdata.count, tagdata.offset, tagdata.offset_as_data); break; case EXIF_TYPE_RATIONAL: parse_exif_rational_tag (exif_reader, tag_map[map_index].gst_tag, tagdata.count, tagdata.offset, 1); break; default: GST_WARNING ("Unhandled tag type: %u", tagdata.tag_type); break; } } return TRUE; read_error: { GST_WARNING ("Failed to parse the exif ifd"); return FALSE; } }
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; }
GstIsoffParserResult gst_isoff_sidx_parser_parse (GstSidxParser * parser, GstByteReader * reader, guint * consumed) { GstIsoffParserResult res = GST_ISOFF_PARSER_OK; gsize remaining; switch (parser->status) { case GST_ISOFF_SIDX_PARSER_INIT: /* Try again once we have enough data for the FullBox header */ if (gst_byte_reader_get_remaining (reader) < 4) { gst_byte_reader_set_pos (reader, 0); break; } parser->sidx.version = gst_byte_reader_get_uint8_unchecked (reader); parser->sidx.flags = gst_byte_reader_get_uint24_le_unchecked (reader); parser->status = GST_ISOFF_SIDX_PARSER_HEADER; case GST_ISOFF_SIDX_PARSER_HEADER: remaining = gst_byte_reader_get_remaining (reader); if (remaining < 12 + (parser->sidx.version == 0 ? 8 : 16)) { break; } parser->sidx.ref_id = gst_byte_reader_get_uint32_be_unchecked (reader); parser->sidx.timescale = gst_byte_reader_get_uint32_be_unchecked (reader); if (parser->sidx.version == 0) { parser->sidx.earliest_pts = gst_byte_reader_get_uint32_be_unchecked (reader); parser->sidx.first_offset = gst_byte_reader_get_uint32_be_unchecked (reader); } else { parser->sidx.earliest_pts = gst_byte_reader_get_uint64_be_unchecked (reader); parser->sidx.first_offset = gst_byte_reader_get_uint64_be_unchecked (reader); } /* skip 2 reserved bytes */ gst_byte_reader_skip_unchecked (reader, 2); parser->sidx.entries_count = gst_byte_reader_get_uint16_be_unchecked (reader); GST_LOG ("Timescale: %" G_GUINT32_FORMAT, parser->sidx.timescale); GST_LOG ("Earliest pts: %" G_GUINT64_FORMAT, parser->sidx.earliest_pts); GST_LOG ("First offset: %" G_GUINT64_FORMAT, parser->sidx.first_offset); parser->cumulative_pts = gst_util_uint64_scale_int_round (parser->sidx.earliest_pts, GST_SECOND, parser->sidx.timescale); if (parser->sidx.entries_count) { parser->sidx.entries = g_malloc (sizeof (GstSidxBoxEntry) * parser->sidx.entries_count); } parser->sidx.entry_index = 0; parser->status = GST_ISOFF_SIDX_PARSER_DATA; case GST_ISOFF_SIDX_PARSER_DATA: while (parser->sidx.entry_index < parser->sidx.entries_count) { GstSidxBoxEntry *entry = &parser->sidx.entries[parser->sidx.entry_index]; remaining = gst_byte_reader_get_remaining (reader); if (remaining < 12) break; entry->offset = parser->cumulative_entry_size; entry->pts = parser->cumulative_pts; gst_isoff_parse_sidx_entry (entry, reader); entry->duration = gst_util_uint64_scale_int_round (entry->duration, GST_SECOND, parser->sidx.timescale); parser->cumulative_entry_size += entry->size; parser->cumulative_pts += entry->duration; GST_LOG ("Sidx entry %d) offset: %" G_GUINT64_FORMAT ", pts: %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " - size %" G_GUINT32_FORMAT, parser->sidx.entry_index, entry->offset, GST_TIME_ARGS (entry->pts), GST_TIME_ARGS (entry->duration), entry->size); parser->sidx.entry_index++; } if (parser->sidx.entry_index == parser->sidx.entries_count) parser->status = GST_ISOFF_SIDX_PARSER_FINISHED; else break; case GST_ISOFF_SIDX_PARSER_FINISHED: parser->sidx.entry_index = 0; res = GST_ISOFF_PARSER_DONE; break; } *consumed = gst_byte_reader_get_pos (reader); return res; }
GstIsoffParserResult gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buffer, guint * consumed) { GstIsoffParserResult res = GST_ISOFF_PARSER_OK; GstByteReader reader; GstMapInfo info; gsize remaining; guint32 fourcc; if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) { *consumed = 0; return GST_ISOFF_PARSER_ERROR; } gst_byte_reader_init (&reader, info.data, info.size); switch (parser->status) { case GST_ISOFF_SIDX_PARSER_INIT: if (gst_byte_reader_get_remaining (&reader) < GST_ISOFF_FULL_BOX_SIZE) { break; } parser->size = gst_byte_reader_get_uint32_be_unchecked (&reader); fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader); if (fourcc != GST_ISOFF_FOURCC_SIDX) { res = GST_ISOFF_PARSER_UNEXPECTED; gst_byte_reader_set_pos (&reader, 0); break; } if (parser->size == 1) { if (gst_byte_reader_get_remaining (&reader) < 12) { gst_byte_reader_set_pos (&reader, 0); break; } parser->size = gst_byte_reader_get_uint64_be_unchecked (&reader); } if (parser->size == 0) { res = GST_ISOFF_PARSER_ERROR; gst_byte_reader_set_pos (&reader, 0); break; } parser->sidx.version = gst_byte_reader_get_uint8_unchecked (&reader); parser->sidx.flags = gst_byte_reader_get_uint24_le_unchecked (&reader); parser->status = GST_ISOFF_SIDX_PARSER_HEADER; case GST_ISOFF_SIDX_PARSER_HEADER: remaining = gst_byte_reader_get_remaining (&reader); if (remaining < 12 + (parser->sidx.version == 0 ? 8 : 16)) { break; } parser->sidx.ref_id = gst_byte_reader_get_uint32_be_unchecked (&reader); parser->sidx.timescale = gst_byte_reader_get_uint32_be_unchecked (&reader); if (parser->sidx.version == 0) { parser->sidx.earliest_pts = gst_byte_reader_get_uint32_be_unchecked (&reader); parser->sidx.first_offset = parser->sidx.earliest_pts = gst_byte_reader_get_uint32_be_unchecked (&reader); } else { parser->sidx.earliest_pts = gst_byte_reader_get_uint64_be_unchecked (&reader); parser->sidx.first_offset = gst_byte_reader_get_uint64_be_unchecked (&reader); } /* skip 2 reserved bytes */ gst_byte_reader_skip_unchecked (&reader, 2); parser->sidx.entries_count = gst_byte_reader_get_uint16_be_unchecked (&reader); GST_LOG ("Timescale: %" G_GUINT32_FORMAT, parser->sidx.timescale); GST_LOG ("Earliest pts: %" G_GUINT64_FORMAT, parser->sidx.earliest_pts); GST_LOG ("First offset: %" G_GUINT64_FORMAT, parser->sidx.first_offset); parser->cumulative_pts = gst_util_uint64_scale_int_round (parser->sidx.earliest_pts, GST_SECOND, parser->sidx.timescale); if (parser->sidx.entries_count) { parser->sidx.entries = g_malloc (sizeof (GstSidxBoxEntry) * parser->sidx.entries_count); } parser->sidx.entry_index = 0; parser->status = GST_ISOFF_SIDX_PARSER_DATA; case GST_ISOFF_SIDX_PARSER_DATA: while (parser->sidx.entry_index < parser->sidx.entries_count) { GstSidxBoxEntry *entry = &parser->sidx.entries[parser->sidx.entry_index]; remaining = gst_byte_reader_get_remaining (&reader);; if (remaining < 12) break; entry->offset = parser->cumulative_entry_size; entry->pts = parser->cumulative_pts; gst_isoff_parse_sidx_entry (entry, &reader); entry->duration = gst_util_uint64_scale_int_round (entry->duration, GST_SECOND, parser->sidx.timescale); parser->cumulative_entry_size += entry->size; parser->cumulative_pts += entry->duration; GST_LOG ("Sidx entry %d) offset: %" G_GUINT64_FORMAT ", pts: %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " - size %" G_GUINT32_FORMAT, parser->sidx.entry_index, entry->offset, GST_TIME_ARGS (entry->pts), GST_TIME_ARGS (entry->duration), entry->size); parser->sidx.entry_index++; } if (parser->sidx.entry_index == parser->sidx.entries_count) parser->status = GST_ISOFF_SIDX_PARSER_FINISHED; else break; case GST_ISOFF_SIDX_PARSER_FINISHED: parser->sidx.entry_index = 0; res = GST_ISOFF_PARSER_DONE; break; } *consumed = gst_byte_reader_get_pos (&reader); gst_buffer_unmap (buffer, &info); return res; }