| /* 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 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) |
| { |
| GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); |
| |
| parent_class = g_type_class_ref (GST_TYPE_ELEMENT); |
| |
| 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; |
| } |
| |
| 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_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 */ |
| while (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_pad_event_default (riff->sinkpad, event); |
| } else { |
| gst_event_unref (event); |
| GST_ELEMENT_ERROR (riff, RESOURCE, READ, NULL, NULL); |
| 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 (riff, RESOURCE, READ, NULL, NULL); |
| 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 = NULL; |
| 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 (riff, RESOURCE, SEEK, NULL, NULL); |
| return NULL; |
| } |
| |
| /* and now, peek a new byte. This will fail because there's a |
| * pending event. Then, take the event and return it. */ |
| while (!event) { |
| if (gst_bytestream_peek_bytes (riff->bs, &data, 1)) { |
| GST_WARNING ("Unexpected data after seek - this means seek failed"); |
| break; |
| } |
| |
| /* get the discont event and return */ |
| gst_bytestream_get_status (riff->bs, &remaining, &event); |
| if (!event) { |
| GST_WARNING ("No discontinuity event after seek - seek failed"); |
| break; |
| } else if (GST_EVENT_TYPE (event) != GST_EVENT_DISCONTINUOUS) { |
| gst_pad_event_default (riff->sinkpad, event); |
| event = 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 (riff, RESOURCE, READ, NULL, NULL); |
| 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 (riff, RESOURCE, READ, NULL, NULL); |
| 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; |
| GstTagList *taglist; |
| gboolean have_tags = FALSE; |
| |
| /* 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); |
| |
| taglist = gst_tag_list_new (); |
| |
| while (gst_bytestream_tell (riff->bs) < end) { |
| if (!gst_riff_peek_head (riff, &tag, NULL, NULL)) { |
| return FALSE; |
| } |
| |
| /* find out the type of metadata */ |
| switch (tag) { |
| case GST_RIFF_INFO_IARL: |
| type = GST_TAG_LOCATION; |
| break; |
| case GST_RIFF_INFO_IART: |
| type = GST_TAG_ARTIST; |
| break; |
| case GST_RIFF_INFO_ICMS: |
| type = NULL; /*"Commissioner";*/ |
| break; |
| case GST_RIFF_INFO_ICMT: |
| type = GST_TAG_COMMENT; |
| break; |
| case GST_RIFF_INFO_ICOP: |
| type = GST_TAG_COPYRIGHT; |
| break; |
| case GST_RIFF_INFO_ICRD: |
| type = GST_TAG_DATE; |
| break; |
| case GST_RIFF_INFO_ICRP: |
| type = NULL; /*"Cropped";*/ |
| break; |
| case GST_RIFF_INFO_IDIM: |
| type = NULL; /*"Dimensions";*/ |
| break; |
| case GST_RIFF_INFO_IDPI: |
| type = NULL; /*"Dots per Inch";*/ |
| break; |
| case GST_RIFF_INFO_IENG: |
| type = NULL; /*"Engineer";*/ |
| break; |
| case GST_RIFF_INFO_IGNR: |
| type = GST_TAG_GENRE; |
| break; |
| case GST_RIFF_INFO_IKEY: |
| type = NULL; /*"Keywords";*/; |
| break; |
| case GST_RIFF_INFO_ILGT: |
| type = NULL; /*"Lightness";*/ |
| break; |
| case GST_RIFF_INFO_IMED: |
| type = NULL; /*"Medium";*/ |
| break; |
| case GST_RIFF_INFO_INAM: |
| type = GST_TAG_TITLE; |
| break; |
| case GST_RIFF_INFO_IPLT: |
| type = NULL; /*"Palette";*/ |
| break; |
| case GST_RIFF_INFO_IPRD: |
| type = NULL; /*"Product";*/ |
| break; |
| case GST_RIFF_INFO_ISBJ: |
| type = NULL; /*"Subject";*/ |
| break; |
| case GST_RIFF_INFO_ISFT: |
| type = GST_TAG_APPLICATION; |
| break; |
| case GST_RIFF_INFO_ISHP: |
| type = NULL; /*"Sharpness";*/ |
| break; |
| case GST_RIFF_INFO_ISRC: |
| type = GST_TAG_ISRC; |
| break; |
| case GST_RIFF_INFO_ISRF: |
| type = NULL; /*"Source Form";*/ |
| break; |
| case GST_RIFF_INFO_ITCH: |
| type = NULL; /*"Technician";*/ |
| break; |
| default: |
| type = NULL; |
| GST_WARNING ("Unknown INFO (metadata) tag entry " GST_FOURCC_FORMAT, |
| GST_FOURCC_ARGS (tag)); |
| break; |
| } |
| |
| if (type) { |
| name = NULL; |
| if (!gst_riff_read_ascii (riff, &tag, &name)) { |
| return FALSE; |
| } |
| |
| if (name && name[0] != '\0') { |
| GValue src = { 0 }, dest = { 0 }; |
| GType dest_type = gst_tag_get_type (type); |
| |
| have_tags = TRUE; |
| g_value_init (&src, G_TYPE_STRING); |
| g_value_set_string (&src, name); |
| g_value_init (&dest, dest_type); |
| g_value_transform (&src, &dest); |
| g_value_unset (&src); |
| gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND, |
| type, &dest, NULL); |
| g_value_unset (&dest); |
| } |
| g_free (name); |
| } else { |
| gst_riff_read_skip (riff); |
| } |
| } |
| |
| if (have_tags) { |
| GstElement *element = GST_ELEMENT (riff); |
| GstEvent *event = gst_event_new_tag (taglist); |
| const GList *padlist; |
| |
| /* let the world know about this wonderful thing */ |
| for (padlist = gst_element_get_pad_list (element); |
| padlist != NULL; padlist = padlist->next) { |
| if (GST_PAD_IS_SRC (padlist->data) && GST_PAD_IS_USABLE(padlist->data)) { |
| gst_event_ref (event); |
| gst_pad_push (GST_PAD (padlist->data), GST_DATA (event)); |
| } |
| } |
| gst_event_unref (event); |
| gst_element_found_tags (GST_ELEMENT (riff), taglist); |
| } |
| gst_tag_list_free (taglist); |
| |
| 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_ELEMENT_ERROR (riff, STREAM, WRONG_TYPE, NULL, NULL); |
| return FALSE; |
| } |
| gst_bytestream_flush_fast (riff->bs, 8); |
| |
| /* doctype */ |
| if (gst_bytestream_peek_bytes (riff->bs, &data, 4) != 4) { |
| GST_ELEMENT_ERROR (riff, RESOURCE, READ, NULL, NULL); |
| 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; |
| } |