diff options
Diffstat (limited to 'gst-libs/gst/riff/riff-read.c')
-rw-r--r-- | gst-libs/gst/riff/riff-read.c | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/gst-libs/gst/riff/riff-read.c b/gst-libs/gst/riff/riff-read.c new file mode 100644 index 00000000..c264cced --- /dev/null +++ b/gst-libs/gst/riff/riff-read.c @@ -0,0 +1,877 @@ +/* GStreamer RIFF I/O + * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * riff-read.c: RIFF input file parsing + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "riff-ids.h" +#include "riff-read.h" + +enum { + ARG_0, + ARG_METADATA + /* FILL ME */ +}; + +static void gst_riff_read_class_init (GstRiffReadClass *klass); +static void gst_riff_read_init (GstRiffRead *riff); +static void gst_riff_read_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static GstElementStateReturn + gst_riff_read_change_state (GstElement *element); + +static GstElementClass *parent_class = NULL; + +GType +gst_riff_read_get_type (void) +{ + static GType gst_riff_read_type = 0; + + if (!gst_riff_read_type) { + static const GTypeInfo gst_riff_read_info = { + sizeof (GstRiffReadClass), + NULL, + NULL, + (GClassInitFunc) gst_riff_read_class_init, + NULL, + NULL, + sizeof (GstRiffRead), + 0, + (GInstanceInitFunc) gst_riff_read_init, + }; + + gst_riff_read_type = + g_type_register_static (GST_TYPE_ELEMENT, "GstRiffRead", + &gst_riff_read_info, 0); + } + + return gst_riff_read_type; +} + +static void +gst_riff_read_class_init (GstRiffReadClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + g_object_class_install_property (gobject_class, ARG_METADATA, + g_param_spec_boxed ("metadata", "Metadata", "Metadata", + GST_TYPE_CAPS, G_PARAM_READABLE)); + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + gobject_class->get_property = gst_riff_read_get_property; + + gstelement_class->change_state = gst_riff_read_change_state; +} + +static void +gst_riff_read_init (GstRiffRead *riff) +{ + riff->sinkpad = NULL; + riff->bs = NULL; + riff->level = NULL; + riff->metadata = NULL; +} + +static void +gst_riff_read_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GstRiffRead *riff = GST_RIFF_READ (object); + + switch (prop_id) { + case ARG_METADATA: + g_value_set_boxed (value, riff->metadata); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstElementStateReturn +gst_riff_read_change_state (GstElement *element) +{ + GstRiffRead *riff = GST_RIFF_READ (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + if (!riff->sinkpad) + return GST_STATE_FAILURE; + riff->bs = gst_bytestream_new (riff->sinkpad); + break; + case GST_STATE_PAUSED_TO_READY: + gst_caps_replace (&riff->metadata, NULL); + gst_bytestream_destroy (riff->bs); + while (riff->level) { + GstRiffLevel *level = riff->level->data; + + riff->level = g_list_remove (riff->level, level); + g_free (level); + } + break; + default: + break; + } + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + +/* + * Return: the amount of levels in the hierarchy that the + * current element lies higher than the previous one. + * The opposite isn't done - that's auto-done using list + * element reading. + */ + +static guint +gst_riff_read_element_level_up (GstRiffRead *riff) +{ + guint num = 0; + guint64 pos = gst_bytestream_tell (riff->bs); + + while (riff->level != NULL) { + GList *last = g_list_last (riff->level); + GstRiffLevel *level = last->data; + + if (pos >= level->start + level->length) { + riff->level = g_list_remove (riff->level, level); + g_free (level); + num++; + } else + break; + } + + return num; +} + +/* + * Read the next tag plus length (may be NULL). Return + * TRUE on success or FALSE on failure. + */ + +static gboolean +gst_riff_peek_head (GstRiffRead *riff, + guint32 *tag, + guint32 *length, + guint *level_up) +{ + guint8 *data; + + /* read */ + if (gst_bytestream_peek_bytes (riff->bs, &data, 8) != 8) { + GstEvent *event = NULL; + guint32 remaining; + + /* Here, we might encounter EOS */ + gst_bytestream_get_status (riff->bs, &remaining, &event); + if (event && GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + gst_pad_event_default (riff->sinkpad, event); + } else { + gst_event_unref (event); + gst_element_error (GST_ELEMENT (riff), "Read error"); + } + return FALSE; + } + + /* parse tag + length (if wanted) */ + *tag = GUINT32_FROM_LE (((guint32 *) data)[0]); + if (length) + *length = GUINT32_FROM_LE (((guint32 *) data)[1]); + + /* level */ + if (level_up) + *level_up = gst_riff_read_element_level_up (riff); + + return TRUE; +} + +/* + * Read: the actual data (plus alignment and flush). + * Return: the data, as a GstBuffer. + */ + +static GstBuffer * +gst_riff_read_element_data (GstRiffRead *riff, + guint length) +{ + GstBuffer *buf = NULL; + + if (gst_bytestream_peek (riff->bs, &buf, length) != length) { + gst_element_error (GST_ELEMENT (riff), "Read error"); + if (buf) + gst_buffer_unref (buf); + return NULL; + } + + /* we need 16-bit alignment */ + if (length & 1) + length++; + + gst_bytestream_flush (riff->bs, length); + + return buf; +} + +/* + * Seek. + */ + +GstEvent * +gst_riff_read_seek (GstRiffRead *riff, + guint64 offset) +{ + guint64 length = gst_bytestream_length (riff->bs); + guint32 remaining; + GstEvent *event; + guchar *data; + + /* hack for AVI files with broken idx1 size chunk markers */ + if (offset > length) + offset = length; + + /* first, flush remaining buffers */ + gst_bytestream_get_status (riff->bs, &remaining, &event); + if (event) { + g_warning ("Unexpected event before seek"); + gst_event_unref (event); + } + if (remaining) + gst_bytestream_flush_fast (riff->bs, remaining); + + /* now seek */ + if (!gst_bytestream_seek (riff->bs, offset, GST_SEEK_METHOD_SET)) { + gst_element_error (GST_ELEMENT (riff), "Seek failed"); + return NULL; + } + + /* and now, peek a new byte. This will fail because there's a + * pending event. Then, take the event and return it. */ + if (gst_bytestream_peek_bytes (riff->bs, &data, 1)) + g_warning ("Unexpected data after seek"); + + /* get the discont event and return */ + gst_bytestream_get_status (riff->bs, &remaining, &event); + if (!event || GST_EVENT_TYPE (event) != GST_EVENT_DISCONTINUOUS) { + gst_element_error (GST_ELEMENT (riff), + "No discontinuity event after seek"); + if (event) + gst_event_unref (event); + return NULL; + } + + return event; +} + +/* + * Gives the tag of the next RIFF element. + */ + +guint32 +gst_riff_peek_tag (GstRiffRead *riff, + guint *level_up) +{ + guint32 tag; + + if (!gst_riff_peek_head (riff, &tag, NULL, level_up)) + return 0; + + return tag; +} + +/* + * Gives the tag of the next LIST/RIFF element. + */ + +guint32 +gst_riff_peek_list (GstRiffRead *riff) +{ + guint32 lst; + guint8 *data; + + if (!gst_riff_peek_head (riff, &lst, NULL, NULL)) + return FALSE; + if (lst != GST_RIFF_TAG_LIST) { + g_warning ("Not a LIST object"); + return 0; + } + + if (gst_bytestream_peek_bytes (riff->bs, &data, 12) != 12) { + gst_element_error (GST_ELEMENT (riff), "Read error"); + return 0; + } + + return GUINT32_FROM_LE (((guint32 *) data)[2]); +} + +/* + * Don't read data. + */ + +gboolean +gst_riff_read_skip (GstRiffRead *riff) +{ + guint32 tag, length; + GstEvent *event; + guint32 remaining; + + if (!gst_riff_peek_head (riff, &tag, &length, NULL)) + return FALSE; + + /* 16-bit alignment */ + if (length & 1) + length++; + + /* header itself */ + length += 8; + + /* see if we have that much data available */ + gst_bytestream_get_status (riff->bs, &remaining, &event); + if (event) { + g_warning ("Unexpected event in skip"); + gst_event_unref (event); + } + + /* yes */ + if (remaining >= length) { + gst_bytestream_flush_fast (riff->bs, length); + return TRUE; + } + + /* no */ + if (!(event = gst_riff_read_seek (riff, + gst_bytestream_tell (riff->bs) + length))) + return FALSE; + + gst_event_unref (event); + + return TRUE; +} + +/* + * Read any type of data. + */ + +gboolean +gst_riff_read_data (GstRiffRead *riff, + guint32 *tag, + GstBuffer **buf) +{ + guint32 length; + + if (!gst_riff_peek_head (riff, tag, &length, NULL)) + return FALSE; + gst_bytestream_flush_fast (riff->bs, 8); + + return ((*buf = gst_riff_read_element_data (riff, length)) != NULL); +} + +/* + * Read a string. + */ + +gboolean +gst_riff_read_ascii (GstRiffRead *riff, + guint32 *tag, + gchar **str) +{ + GstBuffer *buf; + + if (!gst_riff_read_data (riff, tag, &buf)) + return FALSE; + + *str = g_malloc (GST_BUFFER_SIZE (buf) + 1); + memcpy (*str, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + (*str)[GST_BUFFER_SIZE (buf)] = '\0'; + + gst_buffer_unref (buf); + + return TRUE; +} + +/* + * Read media structs. + */ + +gboolean +gst_riff_read_strh (GstRiffRead *riff, + gst_riff_strh **header) +{ + guint32 tag; + GstBuffer *buf; + gst_riff_strh *strh; + + if (!gst_riff_read_data (riff, &tag, &buf)) + return FALSE; + + if (tag != GST_RIFF_TAG_strh) { + g_warning ("Not a strh chunk"); + gst_buffer_unref (buf); + return FALSE; + } + if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strh)) { + g_warning ("Too small strh (%d available, %d needed)", + GST_BUFFER_SIZE (buf), sizeof (gst_riff_strh)); + gst_buffer_unref (buf); + return FALSE; + } + + strh = g_memdup (GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + gst_buffer_unref (buf); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + strh->type = GUINT32_FROM_LE (strh->type); + strh->fcc_handler = GUINT32_FROM_LE (strh->fcc_handler); + strh->flags = GUINT32_FROM_LE (strh->flags); + strh->priority = GUINT32_FROM_LE (strh->priority); + strh->init_frames = GUINT32_FROM_LE (strh->init_frames); + strh->scale = GUINT32_FROM_LE (strh->scale); + strh->rate = GUINT32_FROM_LE (strh->rate); + strh->start = GUINT32_FROM_LE (strh->start); + strh->length = GUINT32_FROM_LE (strh->length); + strh->bufsize = GUINT32_FROM_LE (strh->bufsize); + strh->quality = GUINT32_FROM_LE (strh->quality); + strh->samplesize = GUINT32_FROM_LE (strh->samplesize); +#endif + + /* avoid divisions by zero */ + if (!strh->scale) + strh->scale = 1; + if (!strh->rate) + strh->rate = 1; + + /* debug */ + GST_INFO ("strh tag found"); + GST_INFO (" type " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (strh->type)); + GST_INFO (" fcc_handler " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (strh->fcc_handler)); + GST_INFO (" flags 0x%08x", strh->flags); + GST_INFO (" priority %d", strh->priority); + GST_INFO (" init_frames %d", strh->init_frames); + GST_INFO (" scale %d", strh->scale); + GST_INFO (" rate %d", strh->rate); + GST_INFO (" start %d", strh->start); + GST_INFO (" length %d", strh->length); + GST_INFO (" bufsize %d", strh->bufsize); + GST_INFO (" quality %d", strh->quality); + GST_INFO (" samplesize %d", strh->samplesize); + + *header = strh; + + return TRUE; +} + +gboolean +gst_riff_read_strf_vids (GstRiffRead *riff, + gst_riff_strf_vids **header) +{ + guint32 tag; + GstBuffer *buf; + gst_riff_strf_vids *strf; + + if (!gst_riff_read_data (riff, &tag, &buf)) + return FALSE; + + if (tag != GST_RIFF_TAG_strf) { + g_warning ("Not a strf chunk"); + gst_buffer_unref (buf); + return FALSE; + } + if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_vids)) { + g_warning ("Too small strf_vids (%d available, %d needed)", + GST_BUFFER_SIZE (buf), sizeof (gst_riff_strf_vids)); + gst_buffer_unref (buf); + return FALSE; + } + + strf = g_memdup (GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + strf->size = GUINT32_FROM_LE (strf->size); + strf->width = GUINT32_FROM_LE (strf->width); + strf->height = GUINT32_FROM_LE (strf->height); + strf->planes = GUINT16_FROM_LE (strf->planes); + strf->bit_cnt = GUINT16_FROM_LE (strf->bit_cnt); + strf->compression = GUINT32_FROM_LE (strf->compression); + strf->image_size = GUINT32_FROM_LE (strf->image_size); + strf->xpels_meter = GUINT32_FROM_LE (strf->xpels_meter); + strf->ypels_meter = GUINT32_FROM_LE (strf->ypels_meter); + strf->num_colors = GUINT32_FROM_LE (strf->num_colors); + strf->imp_colors = GUINT32_FROM_LE (strf->imp_colors); +#endif + + /* size checking */ + if (strf->size > GST_BUFFER_SIZE (buf)) { + g_warning ("strf_vids header gave %d bytes data, only %d available", + strf->size, GST_BUFFER_SIZE (buf)); + strf->size = GST_BUFFER_SIZE (buf); + } + + /* debug */ + GST_INFO ("strf tag found in context vids:"); + GST_INFO (" size %d", strf->size); + GST_INFO (" width %d", strf->width); + GST_INFO (" height %d", strf->height); + GST_INFO (" planes %d", strf->planes); + GST_INFO (" bit_cnt %d", strf->bit_cnt); + GST_INFO (" compression " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (strf->compression)); + GST_INFO (" image_size %d", strf->image_size); + GST_INFO (" xpels_meter %d", strf->xpels_meter); + GST_INFO (" ypels_meter %d", strf->ypels_meter); + GST_INFO (" num_colors %d", strf->num_colors); + GST_INFO (" imp_colors %d", strf->imp_colors); + + gst_buffer_unref (buf); + + *header = strf; + + return TRUE; +} + +gboolean +gst_riff_read_strf_auds (GstRiffRead *riff, + gst_riff_strf_auds **header) +{ + guint32 tag; + GstBuffer *buf; + gst_riff_strf_auds *strf; + + if (!gst_riff_read_data (riff, &tag, &buf)) + return FALSE; + + if (tag != GST_RIFF_TAG_strf) { + g_warning ("Not a strf chunk"); + gst_buffer_unref (buf); + return FALSE; + } + if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_auds)) { + g_warning ("Too small strf_auds (%d available, %d needed)", + GST_BUFFER_SIZE (buf), sizeof (gst_riff_strf_auds)); + gst_buffer_unref (buf); + return FALSE; + } + + strf = g_memdup (GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + strf->format = GUINT16_FROM_LE (strf->format); + strf->channels = GUINT16_FROM_LE (strf->channels); + strf->rate = GUINT32_FROM_LE (strf->rate); + strf->av_bps = GUINT32_FROM_LE (strf->av_bps); + strf->blockalign = GUINT16_FROM_LE (strf->blockalign); + strf->size = GUINT16_FROM_LE (strf->size); +#endif + + /* debug */ + GST_INFO ("strf tag found in context auds:"); + GST_INFO (" format %d", strf->format); + GST_INFO (" channels %d", strf->channels); + GST_INFO (" rate %d", strf->rate); + GST_INFO (" av_bps %d", strf->av_bps); + GST_INFO (" blockalign %d", strf->blockalign); + GST_INFO (" size %d", strf->size); /* wordsize, not extrasize! */ + + gst_buffer_unref (buf); + + *header = strf; + + return TRUE; +} + +gboolean +gst_riff_read_strf_iavs (GstRiffRead *riff, + gst_riff_strf_iavs **header) +{ + guint32 tag; + GstBuffer *buf; + gst_riff_strf_iavs *strf; + + if (!gst_riff_read_data (riff, &tag, &buf)) + return FALSE; + + if (tag != GST_RIFF_TAG_strf) { + g_warning ("Not a strf chunk"); + gst_buffer_unref (buf); + return FALSE; + } + if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_strf_iavs)) { + g_warning ("Too small strf_iavs (%d available, %d needed)", + GST_BUFFER_SIZE (buf), sizeof (gst_riff_strf_iavs)); + gst_buffer_unref (buf); + return FALSE; + } + + strf = g_memdup (GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + gst_buffer_unref (buf); + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + strf->DVAAuxSrc = GUINT32_FROM_LE (strf->DVAAuxSrc); + strf->DVAAuxCtl = GUINT32_FROM_LE (strf->DVAAuxCtl); + strf->DVAAuxSrc1 = GUINT32_FROM_LE (strf->DVAAuxSrc1); + strf->DVAAuxCtl1 = GUINT32_FROM_LE (strf->DVAAuxCtl1); + strf->DVVAuxSrc = GUINT32_FROM_LE (strf->DVVAuxSrc); + strf->DVVAuxCtl = GUINT32_FROM_LE (strf->DVVAuxCtl); + strf->DVReserved1 = GUINT32_FROM_LE (strf->DVReserved1); + strf->DVReserved2 = GUINT32_FROM_LE (strf->DVReserved2); +#endif + + /* debug */ + GST_INFO ("strf tag found in context iavs"); + GST_INFO (" DVAAuxSrc %08x", strf->DVAAuxSrc); + GST_INFO (" DVAAuxCtl %08x", strf->DVAAuxCtl); + GST_INFO (" DVAAuxSrc1 %08x", strf->DVAAuxSrc1); + GST_INFO (" DVAAuxCtl1 %08x", strf->DVAAuxCtl1); + GST_INFO (" DVVAuxSrc %08x", strf->DVVAuxSrc); + GST_INFO (" DVVAuxCtl %08x", strf->DVVAuxCtl); + GST_INFO (" DVReserved1 %08x", strf->DVReserved1); + GST_INFO (" DVReserved2 %08x", strf->DVReserved2); + + *header = strf; + + return TRUE; +} + +/* + * Read a list. + */ + +gboolean +gst_riff_read_list (GstRiffRead *riff, + guint32 *tag) +{ + guint32 length, lst; + GstRiffLevel *level; + guint8 *data; + + if (!gst_riff_peek_head (riff, &lst, &length, NULL)) + return FALSE; + if (lst != GST_RIFF_TAG_LIST) { + g_warning ("Not a LIST object"); + return FALSE; + } + gst_bytestream_flush_fast (riff->bs, 8); + if (gst_bytestream_peek_bytes (riff->bs, &data, 4) != 4) { + gst_element_error (GST_ELEMENT (riff), "Read error"); + return FALSE; + } + gst_bytestream_flush_fast (riff->bs, 4); + *tag = GUINT32_FROM_LE (* (guint32 *) data); + + /* remember level */ + level = g_new (GstRiffLevel, 1); + level->start = gst_bytestream_tell (riff->bs); + level->length = length - 4; + riff->level = g_list_append (riff->level, level); + + return TRUE; +} + +/* + * Utility function for reading metadata in a RIFF file. + */ + +gboolean +gst_riff_read_info (GstRiffRead *riff) +{ + guint32 tag; + guint64 end; + GstRiffLevel *level; + GList *last; + gchar *name, *type; + GstProps *props; + + /* What we're doing here is ugly (oh no!); we look + * at our LIST tag size and assure that we do not + * cross boundaries. This is to maintain the level + * counter for the client app. */ + last = g_list_last (riff->level); + level = last->data; + riff->level = g_list_remove (riff->level, level); + end = level->start + level->length; + g_free (level); + + props = gst_props_empty_new (); + + while (gst_bytestream_tell (riff->bs) < end) { + if (!gst_riff_peek_head (riff, &tag, NULL, NULL)) { + gst_props_unref (props); + return FALSE; + } + + /* find out the type of metadata */ + switch (tag) { + case GST_RIFF_INFO_IARL: + type = "Location"; + break; + case GST_RIFF_INFO_IART: + type = "Artist"; + break; + case GST_RIFF_INFO_ICMS: + type = "Commissioner"; + break; + case GST_RIFF_INFO_ICMT: + type = "Comment"; + break; + case GST_RIFF_INFO_ICOP: + type = "Copyright"; + break; + case GST_RIFF_INFO_ICRD: + type = "Creation Date"; + break; + case GST_RIFF_INFO_ICRP: + type = "Cropped"; + break; + case GST_RIFF_INFO_IDIM: + type = "Dimensions"; + break; + case GST_RIFF_INFO_IDPI: + type = "Dots per Inch"; + break; + case GST_RIFF_INFO_IENG: + type = "Engineer"; + break; + case GST_RIFF_INFO_IGNR: + type = "Genre"; + break; + case GST_RIFF_INFO_IKEY: + type = "Keywords"; + break; + case GST_RIFF_INFO_ILGT: + type = "Lightness"; + break; + case GST_RIFF_INFO_IMED: + type = "Medium"; + break; + case GST_RIFF_INFO_INAM: + type = "Title"; /* "Name" */ + break; + case GST_RIFF_INFO_IPLT: + type = "Palette"; + break; + case GST_RIFF_INFO_IPRD: + type = "Product"; + break; + case GST_RIFF_INFO_ISBJ: + type = "Subject"; + break; + case GST_RIFF_INFO_ISFT: + type = "Encoder"; /* "Software" */ + break; + case GST_RIFF_INFO_ISHP: + type = "Sharpness"; + break; + case GST_RIFF_INFO_ISRC: + type = "Source"; + break; + case GST_RIFF_INFO_ISRF: + type = "Source Form"; + break; + case GST_RIFF_INFO_ITCH: + type = "Technician"; + break; + default: + type = NULL; + GST_WARNING ("Unknown INFO (metadata) tag entry " GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (tag)); + break; + } + + if (type) { + GstPropsEntry *entry; + + if (!gst_riff_read_ascii (riff, &tag, &name)) { + gst_props_unref (props); + return FALSE; + } + + entry = gst_props_entry_new (type, GST_PROPS_STRING (name)); + gst_props_add_entry (props, entry); + } else { + gst_riff_read_skip (riff); + } + } + + /* let the world know about this wonderful thing */ + gst_props_debug (props); + gst_caps_replace_sink (&riff->metadata, + gst_caps_new ("riff_metadata", + "application/x-gst-metadata", + props)); + g_object_notify (G_OBJECT (riff), "metadata"); + + return TRUE; +} + +/* + * Read RIFF header and document type. + */ + +gboolean +gst_riff_read_header (GstRiffRead *riff, + guint32 *doctype) +{ + GstRiffLevel *level; + guint32 tag, length; + guint8 *data; + + /* We ignore size for openDML-2.0 support */ + if (!gst_riff_peek_head (riff, &tag, &length, NULL)) + return FALSE; + if (tag != GST_RIFF_TAG_RIFF) { + GST_WARNING ("Not a RIFF file"); + return FALSE; + } + gst_bytestream_flush_fast (riff->bs, 8); + + /* doctype */ + if (gst_bytestream_peek_bytes (riff->bs, &data, 4) != 4) { + gst_element_error (GST_ELEMENT (riff), "Read error"); + return FALSE; + } + gst_bytestream_flush_fast (riff->bs, 4); + *doctype = GUINT32_FROM_LE (* (guint32 *) data); + + /* remember level */ + level = g_new (GstRiffLevel, 1); + level->start = gst_bytestream_tell (riff->bs); + level->length = length - 4; + riff->level = g_list_append (riff->level, level); + + return TRUE; +} |