gstreamer/gst-libs/gst/riff/riff-read.c

826 lines
25 KiB
C
Raw Normal View History

/* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
configure.ac: bump required gstreamer version to 0.8.1.1 because of following changes [--ds] Original commit message from CVS: reviewed by David Schleef * configure.ac: bump required gstreamer version to 0.8.1.1 because of following changes [--ds] * gst-libs/gst/riff/riff-read.c: Include gst/gstutils.h. (gst_riff_peek_head, gst_riff_peek_list, gst_riff_read_list) (gst_riff_read_header): Use GST_READ_UINT* macros to access possibly unaligned memory. * gst/typefind/gsttypefindfunctions.c: Include gst/gstutils.h. (mp3_type_find): Use GST_READ_UINT* macros to access possibly unaligned memory. (mp3_type_find, mpeg1_parse_header, qt_type_find) (speex_type_find): Likewise * gst/tags/gstvorbistag.c: (ADVANCE): Likewise * gst/qtdemux/qtdemux.c: Include stdlib.h (needed by realloc). (QTDEMUX_GUINT32_GET, QTDEMUX_GUINT16_GET, QTDEMUX_FP32_GET) (QTDEMUX_FP16_GET, QTDEMUX_FOURCC_GET) (gst_qtdemux_loop_header, gst_qtdemux_loop_header) (qtdemux_node_dump_foreach, qtdemux_tree_get_child_by_type) (qtdemux_tree_get_sibling_by_type): Use GST_READ_UINT* macros to access possibly unaligned memory. * gst/mpegstream/gstmpegpacketize.c: (parse_generic, parse_chunk): Likewise. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_parse_syshead) (gst_mpeg_demux_parse_packet, gst_mpeg_demux_parse_pes): Likewise. * gst/mpegaudioparse/gstmpegaudioparse.c: (gst_mp3parse_chain): Likewise. * gst/mpeg2sub/gstmpeg2subt.c: (GST_BUFFER_DATA) (gst_mpeg2subt_chain_subtitle): Likewise. * gst/mpeg1videoparse/gstmp1videoparse.c: (mp1videoparse_parse_seq) (gst_mp1videoparse_time_code, gst_mp1videoparse_real_chain): Likewise. * gst/mpeg1sys/buffer.c: (mpeg1mux_buffer_update_audio_info): Likewise. * gst/cdxaparse/gstcdxaparse.c: (gst_bytestream_peek_bytes): Likewise. * gst/asfdemux/gstasfdemux.c: (_read_var_length, _read_uint): Likewise.
2004-04-20 21:04:21 +00:00
#include <gst/gstutils.h>
#include <gst/tag/tag.h>
#include "riff-read.h"
GST_DEBUG_CATEGORY_EXTERN (riff_debug);
#define GST_CAT_DEFAULT riff_debug
ext/flac/gstflacdec.c: Only return true if we actually filled something in. Prevents player applications from showing... Original commit message from CVS: * ext/flac/gstflacdec.c: (gst_flacdec_src_query): Only return true if we actually filled something in. Prevents player applications from showing a random length for flac files. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init), (gst_riff_read_use_event), (gst_riff_read_handle_event), (gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh), (gst_riff_read_strf_vids_with_data), (gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs): OK, ok, so I implemented event handling. Apparently it's normal that we receive random events at random points without asking for it. * gst/avi/gstavidemux.c: (gst_avi_demux_reset), (gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query), (gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index), (gst_avi_demux_sync), (gst_avi_demux_stream_scan), (gst_avi_demux_massage_index), (gst_avi_demux_stream_header), (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry), (gst_avi_demux_stream_data), (gst_avi_demux_loop): * gst/avi/gstavidemux.h: Implement non-lineair chunk handling and subchunk processing. The first solves playback of AVI files where the audio and video data of individual buffers that we read are not synchronized. This should not happen according to the wonderful AVI specs, but of course it does happen in reality. It is also a prerequisite for the second. Subchunk processing allows us to cut chunks in small pieces and process each of these pieces separately. This is required because I've seen several AVI files with incredibly large audio chunks, even some files with only one audio chunk for the whole file. This allows for proper playback including seeking. This patch is supposed to fix all AVI A/V sync issues. * gst/flx/gstflxdec.c: (gst_flxdec_class_init), (flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop): Work. * gst/modplug/gstmodplug.cc: Proper return value setting for the query() function. * gst/playback/gstplaybasebin.c: (setup_source): Being in non-playing state (after, e.g., EOS) is not necessarily a bad thing. Allow for that. This fixes playback of short files. They don't actually playback fully now, because the clock already runs. This means that small files (<500kB) with a small length (<2sec) will still not or barely play. Other files, such as mod or flx, will work correctly, however.
2004-09-29 09:45:40 +00:00
/**
* gst_riff_read_chunk:
* @element: caller element (used for debugging).
* @pad: pad to pull data from.
* @offset: offset to pull from, incremented by this function.
* @tag: fourcc of the chunk (returned by this function).
* @chunk_data: buffer (returned by this function).
*
* Reads a single chunk of data. 'JUNK' chunks are skipped
* automatically.
*
* Returns: flow status.
*/
GstFlowReturn
gst_riff_read_chunk (GstElement * element,
GstPad * pad, guint64 * _offset, guint32 * tag, GstBuffer ** _chunk_data)
{
GstBuffer *buf;
GstFlowReturn res;
2012-01-20 15:11:54 +00:00
GstMapInfo info;
guint size;
guint64 offset = *_offset;
g_return_val_if_fail (element != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (_offset != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (tag != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (_chunk_data != NULL, GST_FLOW_ERROR);
skip_junk:
size = 8;
buf = NULL;
if ((res = gst_pad_pull_range (pad, offset, size, &buf)) != GST_FLOW_OK)
return res;
2011-03-27 11:55:15 +00:00
else if (gst_buffer_get_size (buf) < size)
goto too_small;
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
*tag = GST_READ_UINT32_LE (info.data);
size = GST_READ_UINT32_LE (info.data + 4);
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
GST_DEBUG_OBJECT (element, "fourcc=%" GST_FOURCC_FORMAT ", size=%u",
GST_FOURCC_ARGS (*tag), size);
/* skip 'JUNK' chunks */
if (*tag == GST_RIFF_TAG_JUNK || *tag == GST_RIFF_TAG_JUNQ) {
size = GST_ROUND_UP_2 (size);
*_offset += 8 + size;
offset += 8 + size;
GST_DEBUG_OBJECT (element, "skipping JUNK chunk");
goto skip_junk;
}
buf = NULL;
if ((res = gst_pad_pull_range (pad, offset + 8, size, &buf)) != GST_FLOW_OK)
return res;
2011-03-27 11:55:15 +00:00
else if (gst_buffer_get_size (buf) < size)
goto too_small;
*_chunk_data = buf;
*_offset += 8 + GST_ROUND_UP_2 (size);
return GST_FLOW_OK;
/* ERRORS */
too_small:
{
/* short read, we return EOS to mark the EOS case */
GST_DEBUG_OBJECT (element, "not enough data (available=%" G_GSIZE_FORMAT
", needed=%u)", gst_buffer_get_size (buf), size);
gst_buffer_unref (buf);
return GST_FLOW_EOS;
}
}
/**
* gst_riff_parse_chunk:
* @element: caller element (used for debugging).
* @buf: input buffer.
* @offset: offset in the buffer in the caller. Is incremented
* by the read size by this function.
* @fourcc: fourcc (returned by this function0 of the chunk.
* @chunk_data: buffer (returned by the function) containing the
* chunk data, which may be NULL if chunksize == 0
*
* Reads a single chunk.
*
* Returns: FALSE on error, TRUE otherwise
*/
gboolean
gst_riff_parse_chunk (GstElement * element, GstBuffer * buf,
guint * _offset, guint32 * _fourcc, GstBuffer ** chunk_data)
{
guint size, bufsize;
guint32 fourcc;
2012-01-20 15:11:54 +00:00
guint8 *ptr;
GstMapInfo info;
guint offset = *_offset;
g_return_val_if_fail (element != NULL, FALSE);
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (_offset != NULL, FALSE);
g_return_val_if_fail (_fourcc != NULL, FALSE);
g_return_val_if_fail (chunk_data != NULL, FALSE);
*chunk_data = NULL;
*_fourcc = 0;
2011-03-27 11:55:15 +00:00
bufsize = gst_buffer_get_size (buf);
if (bufsize == offset)
goto end_offset;
if (bufsize < offset + 8)
goto too_small;
/* read header */
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
ptr = info.data + offset;
2011-03-27 11:55:15 +00:00
fourcc = GST_READ_UINT32_LE (ptr);
size = GST_READ_UINT32_LE (ptr + 4);
2012-01-20 15:11:54 +00:00
gst_buffer_unmap (buf, &info);
GST_DEBUG_OBJECT (element, "fourcc=%" GST_FOURCC_FORMAT ", size=%u",
GST_FOURCC_ARGS (fourcc), size);
/* be paranoid: size may be nonsensical value here, such as (guint) -1 */
if (G_UNLIKELY (size > G_MAXINT))
goto bogus_size;
if (bufsize < size + 8 + offset) {
GST_DEBUG_OBJECT (element,
"Needed chunk data (%d) is more than available (%d), shortcutting",
size, bufsize - 8 - offset);
size = bufsize - 8 - offset;
}
if (size)
2011-03-30 14:50:45 +00:00
*chunk_data =
gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset + 8, size);
else
*chunk_data = NULL;
*_fourcc = fourcc;
*_offset += 8 + GST_ROUND_UP_2 (size);
return TRUE;
/* ERRORS */
end_offset:
{
GST_DEBUG_OBJECT (element, "End of chunk (offset %d)", offset);
return FALSE;
}
too_small:
{
GST_DEBUG_OBJECT (element,
"Failed to parse chunk header (offset %d, %d available, %d needed)",
offset, bufsize, 8);
return FALSE;
}
bogus_size:
{
GST_ERROR_OBJECT (element, "Broken file: bogus chunk size %u", size);
return FALSE;
}
}
/**
* gst_riff_parse_file_header:
* @element: caller element (used for debugging/error).
* @buf: input buffer from which the file header will be parsed,
* should be at least 12 bytes long.
* @doctype: a fourcc (returned by this function) to indicate the
* type of document (according to the header).
*
* Reads the first few bytes from the provided buffer, checks
* if this stream is a RIFF stream, and determines document type.
* This function takes ownership of @buf so it should not be used anymore
* after calling this function.
*
* Returns: FALSE if this is not a RIFF stream (in which case the
* caller should error out; we already throw an error), or TRUE
* if it is.
*/
gboolean
gst_riff_parse_file_header (GstElement * element,
GstBuffer * buf, guint32 * doctype)
{
2012-01-20 15:11:54 +00:00
GstMapInfo info;
guint32 tag;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (doctype != NULL, FALSE);
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
if (info.size < 12)
goto too_small;
2012-01-20 15:11:54 +00:00
tag = GST_READ_UINT32_LE (info.data);
if (tag != GST_RIFF_TAG_RIFF && tag != GST_RIFF_TAG_AVF0
&& tag != GST_RIFF_TAG_RF64)
goto not_riff;
2012-01-20 15:11:54 +00:00
*doctype = GST_READ_UINT32_LE (info.data + 8);
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return TRUE;
/* ERRORS */
too_small:
{
GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
("Not enough data to parse RIFF header (%" G_GSIZE_FORMAT " available,"
2012-01-20 15:11:54 +00:00
" %d needed)", info.size, 12));
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return FALSE;
}
not_riff:
{
GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
("Stream is no RIFF stream: 0x%" G_GINT32_MODIFIER "x", tag));
2012-01-20 15:11:54 +00:00
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return FALSE;
}
}
/**
* gst_riff_parse_strh:
* @element: caller element (used for debugging/error).
* @buf: input data to be used for parsing, stripped from header.
* @strh: a pointer (returned by this function) to a filled-in
* strh structure. Caller should free it.
*
* Parses a strh structure from input data. Takes ownership of @buf.
*
* Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
* should be skipped on error, but it is not fatal.
*/
gboolean
gst_riff_parse_strh (GstElement * element,
GstBuffer * buf, gst_riff_strh ** _strh)
{
gst_riff_strh *strh;
2012-01-20 15:11:54 +00:00
GstMapInfo info;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (_strh != NULL, FALSE);
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
if (info.size < sizeof (gst_riff_strh))
goto too_small;
2012-01-20 15:11:54 +00:00
strh = g_memdup (info.data, info.size);
gst_buffer_unmap (buf, &info);
2011-03-27 11:55:15 +00:00
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_OBJECT (element, "strh tag found:");
GST_INFO_OBJECT (element, " type %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (strh->type));
GST_INFO_OBJECT (element, " fcc_handler %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (strh->fcc_handler));
GST_INFO_OBJECT (element, " flags 0x%08x", strh->flags);
GST_INFO_OBJECT (element, " priority %d", strh->priority);
GST_INFO_OBJECT (element, " init_frames %d", strh->init_frames);
GST_INFO_OBJECT (element, " scale %d", strh->scale);
GST_INFO_OBJECT (element, " rate %d", strh->rate);
GST_INFO_OBJECT (element, " start %d", strh->start);
GST_INFO_OBJECT (element, " length %d", strh->length);
GST_INFO_OBJECT (element, " bufsize %d", strh->bufsize);
GST_INFO_OBJECT (element, " quality %d", strh->quality);
GST_INFO_OBJECT (element, " samplesize %d", strh->samplesize);
*_strh = strh;
return TRUE;
/* ERRORS */
too_small:
{
GST_ERROR_OBJECT (element,
"Too small strh (%" G_GSIZE_FORMAT " available, %d needed)",
2012-01-20 15:11:54 +00:00
info.size, (int) sizeof (gst_riff_strh));
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return FALSE;
}
}
/**
* gst_riff_parse_strf_vids:
* @element: caller element (used for debugging/error).
* @buf: input data to be used for parsing, stripped from header.
* @strf: a pointer (returned by this function) to a filled-in
* strf/vids structure. Caller should free it.
* @data: a pointer (returned by this function) to a buffer
* containing extradata for this particular stream (e.g.
* palette, codec initialization data).
*
* Parses a video stream's strf structure plus optionally some
* extradata from input data. This function takes ownership of @buf.
*
* Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
* should be skipped on error, but it is not fatal.
*/
gboolean
gst_riff_parse_strf_vids (GstElement * element,
GstBuffer * buf, gst_riff_strf_vids ** _strf, GstBuffer ** data)
{
gst_riff_strf_vids *strf;
2012-01-20 15:11:54 +00:00
GstMapInfo info;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (_strf != NULL, FALSE);
g_return_val_if_fail (data != NULL, FALSE);
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
if (info.size < sizeof (gst_riff_strf_vids))
goto too_small;
2012-01-20 15:11:54 +00:00
strf = g_memdup (info.data, info.size);
gst_buffer_unmap (buf, &info);
#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 */
*data = NULL;
2012-01-20 15:11:54 +00:00
if (strf->size > info.size) {
GST_WARNING_OBJECT (element,
"strf_vids header gave %d bytes data, only %" G_GSIZE_FORMAT
2012-01-20 15:11:54 +00:00
" available", strf->size, info.size);
strf->size = info.size;
}
2012-01-20 15:11:54 +00:00
if (sizeof (gst_riff_strf_vids) < info.size) {
2011-03-30 14:50:45 +00:00
*data =
gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
2012-01-20 15:11:54 +00:00
sizeof (gst_riff_strf_vids), info.size - sizeof (gst_riff_strf_vids));
}
2011-03-27 11:55:15 +00:00
gst_buffer_unref (buf);
/* debug */
GST_INFO_OBJECT (element, "strf tag found in context vids:");
GST_INFO_OBJECT (element, " size %d", strf->size);
GST_INFO_OBJECT (element, " width %d", strf->width);
GST_INFO_OBJECT (element, " height %d", strf->height);
GST_INFO_OBJECT (element, " planes %d", strf->planes);
GST_INFO_OBJECT (element, " bit_cnt %d", strf->bit_cnt);
GST_INFO_OBJECT (element, " compression %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (strf->compression));
GST_INFO_OBJECT (element, " image_size %d", strf->image_size);
GST_INFO_OBJECT (element, " xpels_meter %d", strf->xpels_meter);
GST_INFO_OBJECT (element, " ypels_meter %d", strf->ypels_meter);
GST_INFO_OBJECT (element, " num_colors %d", strf->num_colors);
GST_INFO_OBJECT (element, " imp_colors %d", strf->imp_colors);
if (*data)
GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
2011-03-27 11:55:15 +00:00
gst_buffer_get_size (*data));
*_strf = strf;
return TRUE;
/* ERRORS */
too_small:
{
GST_ERROR_OBJECT (element,
"Too small strf_vids (%" G_GSIZE_FORMAT " available, %d needed)",
2012-01-20 15:11:54 +00:00
info.size, (int) sizeof (gst_riff_strf_vids));
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return FALSE;
}
}
/**
* gst_riff_parse_strf_auds:
* @element: caller element (used for debugging/error).
* @buf: input data to be used for parsing, stripped from header.
* @strf: a pointer (returned by this function) to a filled-in
* strf/auds structure. Caller should free it.
* @data: a pointer (returned by this function) to a buffer
* containing extradata for this particular stream (e.g.
* codec initialization data).
*
* Parses an audio stream's strf structure plus optionally some
* extradata from input data. This function takes ownership of @buf.
* use.
*
* Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
* should be skipped on error, but it is not fatal.
gst/: Add MS RLE support. I added some functions to read out strf chunks into strf chunks and the data behind it. Thi... Original commit message from CVS: reviewed by: <delete if not using a buddy> * gst-libs/gst/riff/riff-media.c: (gst_riff_create_video_caps_with_data), (gst_riff_create_video_caps), (gst_riff_create_audio_caps), (gst_riff_create_video_template_caps), (gst_riff_create_audio_template_caps): * gst-libs/gst/riff/riff-media.h: * gst-libs/gst/riff/riff-read.c: (gst_riff_read_strf_vids_with_data), (gst_riff_read_strf_vids): * gst-libs/gst/riff/riff-read.h: * gst/avi/gstavidemux.c: (gst_avi_demux_add_stream): Add MS RLE support. I added some functions to read out strf chunks into strf chunks and the data behind it. This is usually color palettes (as in RLE, but also in 8-bit RGB). Also use those during caps creation. Lastly, add ADPCM (similar to wavparse - which should eventually be rifflib based). * gst/matroska/matroska-demux.c: (gst_matroska_demux_class_init), (gst_matroska_demux_init), (gst_matroska_demux_reset): * gst/matroska/matroska-demux.h: Remove placeholders for some prehistoric tagging system. Didn't add support for any tag system really anyway. * gst/qtdemux/qtdemux.c: Add support for audio/x-m4a (MPEG-4) through spider. * gst/wavparse/gstwavparse.c: (gst_wavparse_parse_fmt), (gst_wavparse_loop): ADPCM support (#135862). Increase max. buffer size because we cannot split buffers for ADPCM (screws references) and I've seen files with 2048 byte chunks. 4096 seems safe for now.
2004-04-16 01:20:43 +00:00
*/
gboolean
gst_riff_parse_strf_auds (GstElement * element,
GstBuffer * buf, gst_riff_strf_auds ** _strf, GstBuffer ** data)
{
gst_riff_strf_auds *strf;
2012-01-20 15:11:54 +00:00
GstMapInfo info;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (_strf != NULL, FALSE);
g_return_val_if_fail (data != NULL, FALSE);
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
if (info.size < sizeof (gst_riff_strf_auds))
goto too_small;
2012-01-20 15:11:54 +00:00
strf = g_memdup (info.data, info.size);
#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);
2012-08-04 18:33:32 +00:00
strf->bits_per_sample = GUINT16_FROM_LE (strf->bits_per_sample);
#endif
/* size checking */
*data = NULL;
2012-01-20 15:11:54 +00:00
if (info.size > sizeof (gst_riff_strf_auds) + 2) {
gint len;
len = GST_READ_UINT16_LE (&info.data[16]);
2012-01-20 15:11:54 +00:00
if (len + 2 + sizeof (gst_riff_strf_auds) > info.size) {
GST_WARNING_OBJECT (element,
"Extradata indicated %d bytes, but only %" G_GSIZE_FORMAT
2012-01-20 15:11:54 +00:00
" available", len, info.size - 2 - sizeof (gst_riff_strf_auds));
len = info.size - 2 - sizeof (gst_riff_strf_auds);
}
if (len)
2011-03-30 14:50:45 +00:00
*data = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
sizeof (gst_riff_strf_auds) + 2, len);
}
/* debug */
GST_INFO_OBJECT (element, "strf tag found in context auds:");
GST_INFO_OBJECT (element, " format %d", strf->format);
GST_INFO_OBJECT (element, " channels %d", strf->channels);
GST_INFO_OBJECT (element, " rate %d", strf->rate);
GST_INFO_OBJECT (element, " av_bps %d", strf->av_bps);
GST_INFO_OBJECT (element, " blockalign %d", strf->blockalign);
GST_INFO_OBJECT (element, " bits/sample %d", strf->bits_per_sample);
if (*data)
GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
2011-03-27 11:55:15 +00:00
gst_buffer_get_size (*data));
2012-01-20 15:11:54 +00:00
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
*_strf = strf;
return TRUE;
/* ERROR */
too_small:
{
GST_ERROR_OBJECT (element,
"Too small strf_auds (%" G_GSIZE_FORMAT " available"
", %" G_GSIZE_FORMAT " needed)", info.size,
2012-01-20 15:11:54 +00:00
sizeof (gst_riff_strf_auds));
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return FALSE;
}
}
/**
* gst_riff_parse_strf_iavs:
* @element: caller element (used for debugging/error).
* @buf: input data to be used for parsing, stripped from header.
* @strf: a pointer (returned by this function) to a filled-in
* strf/iavs structure. Caller should free it.
* @data: a pointer (returned by this function) to a buffer
* containing extradata for this particular stream (e.g.
* codec initialization data).
*
* Parses a interleaved (also known as "complex") stream´s strf
* structure plus optionally some extradata from input data. This
* function takes ownership of @buf.
*
* Returns: TRUE if parsing succeeded, otherwise FALSE.
*/
gboolean
gst_riff_parse_strf_iavs (GstElement * element,
GstBuffer * buf, gst_riff_strf_iavs ** _strf, GstBuffer ** data)
{
gst_riff_strf_iavs *strf;
2012-01-20 15:11:54 +00:00
GstMapInfo info;
g_return_val_if_fail (buf != NULL, FALSE);
g_return_val_if_fail (_strf != NULL, FALSE);
g_return_val_if_fail (data != NULL, FALSE);
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
if (info.size < sizeof (gst_riff_strf_iavs))
goto too_small;
2012-01-20 15:11:54 +00:00
strf = g_memdup (info.data, info.size);
gst_buffer_unmap (buf, &info);
2011-03-27 11:55:15 +00:00
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_OBJECT (element, "strf tag found in context iavs:");
GST_INFO_OBJECT (element, " DVAAuxSrc %08x", strf->DVAAuxSrc);
GST_INFO_OBJECT (element, " DVAAuxCtl %08x", strf->DVAAuxCtl);
GST_INFO_OBJECT (element, " DVAAuxSrc1 %08x", strf->DVAAuxSrc1);
GST_INFO_OBJECT (element, " DVAAuxCtl1 %08x", strf->DVAAuxCtl1);
GST_INFO_OBJECT (element, " DVVAuxSrc %08x", strf->DVVAuxSrc);
GST_INFO_OBJECT (element, " DVVAuxCtl %08x", strf->DVVAuxCtl);
GST_INFO_OBJECT (element, " DVReserved1 %08x", strf->DVReserved1);
GST_INFO_OBJECT (element, " DVReserved2 %08x", strf->DVReserved2);
*_strf = strf;
*data = NULL;
return TRUE;
/* ERRORS */
too_small:
{
GST_ERROR_OBJECT (element,
"Too small strf_iavs (%" G_GSIZE_FORMAT "available"
", %" G_GSIZE_FORMAT " needed)", info.size,
2012-01-20 15:11:54 +00:00
sizeof (gst_riff_strf_iavs));
gst_buffer_unmap (buf, &info);
gst_buffer_unref (buf);
return FALSE;
}
}
static void
parse_tag_value (GstElement * element, GstTagList * taglist, const gchar * type,
guint8 * ptr, guint tsize)
{
static const gchar *env_vars[] = { "GST_AVI_TAG_ENCODING",
"GST_RIFF_TAG_ENCODING", "GST_TAG_ENCODING", NULL
};
GType tag_type;
gchar *val;
tag_type = gst_tag_get_type (type);
val = gst_tag_freeform_string_to_utf8 ((gchar *) ptr, tsize, env_vars);
if (val != NULL) {
if (tag_type == G_TYPE_STRING) {
gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, type, val, NULL);
} else {
GValue tag_val = { 0, };
g_value_init (&tag_val, tag_type);
if (gst_value_deserialize (&tag_val, val)) {
gst_tag_list_add_value (taglist, GST_TAG_MERGE_APPEND, type, &tag_val);
} else {
GST_WARNING_OBJECT (element, "could not deserialize '%s' into a "
"tag %s of type %s", val, type, g_type_name (tag_type));
}
g_value_unset (&tag_val);
}
g_free (val);
} else {
GST_WARNING_OBJECT (element, "could not extract %s tag", type);
}
}
/**
* gst_riff_parse_info:
* @element: caller element (used for debugging/error).
* @buf: input data to be used for parsing, stripped from header.
* @taglist: a pointer to a taglist (returned by this function)
* containing information about this stream. May be
* NULL if no supported tags were found.
*
* Parses stream metadata from input data.
*/
void
gst_riff_parse_info (GstElement * element,
GstBuffer * buf, GstTagList ** _taglist)
{
2012-01-20 15:11:54 +00:00
GstMapInfo info;
guint8 *ptr;
gsize left;
2011-03-27 11:55:15 +00:00
guint tsize;
guint32 tag;
const gchar *type;
GstTagList *taglist;
g_return_if_fail (_taglist != NULL);
if (!buf) {
*_taglist = NULL;
return;
}
2012-01-20 15:11:54 +00:00
gst_buffer_map (buf, &info, GST_MAP_READ);
2011-03-27 11:55:15 +00:00
taglist = gst_tag_list_new_empty ();
2012-01-20 15:11:54 +00:00
ptr = info.data;
left = info.size;
2011-03-27 11:55:15 +00:00
while (left > 8) {
tag = GST_READ_UINT32_LE (ptr);
tsize = GST_READ_UINT32_LE (ptr + 4);
GST_MEMDUMP_OBJECT (element, "tag chunk", ptr, MIN (tsize + 8, left));
2011-03-27 11:55:15 +00:00
left -= 8;
ptr += 8;
GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
GST_FOURCC_ARGS (tag), tsize);
2011-03-27 11:55:15 +00:00
if (tsize > left) {
GST_WARNING_OBJECT (element,
"Tagsize %d is larger than available data %" G_GSIZE_FORMAT,
tsize, left);
2011-03-27 11:55:15 +00:00
tsize = left;
}
/* make uppercase */
tag = tag & 0xDFDFDFDF;
/* find out the type of metadata */
switch (tag) {
case GST_RIFF_INFO_IARL:
type = GST_TAG_LOCATION;
break;
case GST_RIFF_INFO_IAAR:
type = GST_TAG_ALBUM_ARTIST;
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_TIME;
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 = GST_TAG_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 = GST_TAG_ALBUM;
break;
case GST_RIFF_INFO_ISBJ:
type = GST_TAG_ALBUM_ARTIST;
break;
case GST_RIFF_INFO_ISFT:
type = GST_TAG_ENCODER;
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;
case GST_RIFF_INFO_ITRK:
type = GST_TAG_TRACK_NUMBER;
break;
default:
type = NULL;
GST_WARNING_OBJECT (element,
"Unknown INFO (metadata) tag entry %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (tag));
break;
}
2011-03-27 11:55:15 +00:00
if (type != NULL && ptr[0] != '\0') {
GST_DEBUG_OBJECT (element, "mapped tag %" GST_FOURCC_FORMAT " to tag %s",
GST_FOURCC_ARGS (tag), type);
parse_tag_value (element, taglist, type, ptr, tsize);
}
if (tsize & 1) {
tsize++;
2011-03-27 11:55:15 +00:00
if (tsize > left)
tsize = left;
}
2011-03-27 11:55:15 +00:00
ptr += tsize;
left -= tsize;
}
if (!gst_tag_list_is_empty (taglist)) {
GST_INFO_OBJECT (element, "extracted tags: %" GST_PTR_FORMAT, taglist);
*_taglist = taglist;
} else {
*_taglist = NULL;
gst_tag_list_unref (taglist);
}
2012-01-20 15:11:54 +00:00
gst_buffer_unmap (buf, &info);
return;
}