2003-12-07 22:49:04 +00:00
|
|
|
/* 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 */
|
|
|
|
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);
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, RESOURCE, READ, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
}
|
|
|
|
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) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, RESOURCE, READ, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
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)) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, RESOURCE, SEEK, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
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) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, CORE, EVENT, NULL,
|
|
|
|
("No discontinuity event after seek"));
|
2003-12-07 22:49:04 +00:00
|
|
|
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) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, RESOURCE, READ, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
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) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, RESOURCE, READ, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
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;
|
2004-01-23 13:22:16 +00:00
|
|
|
GstTagList *taglist;
|
|
|
|
gboolean have_tags = FALSE;
|
2003-12-07 22:49:04 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2004-01-23 13:22:16 +00:00
|
|
|
taglist = gst_tag_list_new ();
|
2003-12-07 22:49:04 +00:00
|
|
|
|
|
|
|
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:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_LOCATION;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IART:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_ARTIST;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ICMS:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Commissioner";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ICMT:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_COMMENT;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ICOP:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_COPYRIGHT;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ICRD:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_DATE;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ICRP:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Cropped";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IDIM:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Dimensions";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IDPI:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Dots per Inch";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IENG:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Engineer";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IGNR:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_GENRE;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IKEY:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Keywords";*/;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ILGT:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Lightness";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IMED:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Medium";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_INAM:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_TITLE;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IPLT:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Palette";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_IPRD:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Product";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ISBJ:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Subject";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ISFT:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_APPLICATION;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ISHP:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Sharpness";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ISRC:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = GST_TAG_ISRC;
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ISRF:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Source Form";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
case GST_RIFF_INFO_ITCH:
|
2004-01-23 13:22:16 +00:00
|
|
|
type = NULL; /*"Technician";*/
|
2003-12-07 22:49:04 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
type = NULL;
|
|
|
|
GST_WARNING ("Unknown INFO (metadata) tag entry " GST_FOURCC_FORMAT,
|
|
|
|
GST_FOURCC_ARGS (tag));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type) {
|
2004-01-23 13:22:16 +00:00
|
|
|
name = NULL;
|
2003-12-07 22:49:04 +00:00
|
|
|
if (!gst_riff_read_ascii (riff, &tag, &name)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2004-01-23 13:22:16 +00:00
|
|
|
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);
|
2003-12-07 22:49:04 +00:00
|
|
|
} else {
|
|
|
|
gst_riff_read_skip (riff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-01-23 13:22:16 +00:00
|
|
|
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) {
|
gst-libs/gst/riff/riff-read.c: Additional pad usability check.
Original commit message from CVS:
2004-01-25 Ronald Bultje <rbultje@ronald.bitfreak.net>
* gst-libs/gst/riff/riff-read.c: (gst_riff_read_info):
Additional pad usability check.
* gst/mpeg1videoparse/gstmp1videoparse.c: (gst_mp1videoparse_init),
(mp1videoparse_find_next_gop), (gst_mp1videoparse_time_code),
(gst_mp1videoparse_real_chain):
Fix MPEG video stream parsing. The original plugin had several
issues, including not timestamping streams where the source was
not timestamped (this happens with PTS values in mpeg system
streams, but MPEG video is also a valid stream on its own so
that needs timestamps too). We use the display time code for that
for now. Also, if one incoming buffer contains multiple valid
frames, we push them all on correctly now, including proper EOS
handling. Lastly, several potential segfaults were fixed, and we
properly sync on new sequence/gop headers to include them in next,
not previous frames (since they're header for the next frame, not
the previous). Also see #119206.
* gst/mpegaudioparse/gstmpegaudioparse.c: (gst_mp3parse_chain),
(bpf_from_header):
Move caps setting so we only do it after finding several valid
MPEG-1 fraes sequentially, not right after the first one (which
might be coincidental).
* gst/typefind/gsttypefindfunctions.c: (mpeg1_sys_type_find),
(mpeg_video_type_find), (mpeg_video_stream_type_find),
(plugin_init):
Add unsynced MPEG video stream typefinding, and change some
probability values so we detect streams rightly. The idea is as
follows: I can have an unsynced system stream which contains
video. In the current code, I would randomly get a type for either
system or video stream type found, because the probabilities are
being calculated rather randomly. I now use fixed values, so we
always prefer system stream if that was found (and that is how it
should be). If no system stream was found, we can still identity
the stream as video-only.
2004-01-25 00:25:16 +00:00
|
|
|
if (GST_PAD_IS_SRC (padlist->data) && GST_PAD_IS_USABLE(padlist->data)) {
|
2004-01-23 13:22:16 +00:00
|
|
|
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);
|
2003-12-07 22:49:04 +00:00
|
|
|
|
|
|
|
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) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, STREAM, WRONG_TYPE, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
gst_bytestream_flush_fast (riff->bs, 8);
|
|
|
|
|
|
|
|
/* doctype */
|
|
|
|
if (gst_bytestream_peek_bytes (riff->bs, &data, 4) != 4) {
|
2004-01-18 21:46:58 +00:00
|
|
|
gst_element_error (riff, RESOURCE, READ, NULL, NULL);
|
2003-12-07 22:49:04 +00:00
|
|
|
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;
|
|
|
|
}
|