| /* 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 <gst/gstutils.h> |
| #include <gst/tag/tag.h> |
| |
| #include "riff-read.h" |
| |
| GST_DEBUG_CATEGORY_EXTERN (riff_debug); |
| #define GST_CAT_DEFAULT riff_debug |
| |
| /** |
| * 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. Since 0.10.8 '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; |
| 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; |
| else if (gst_buffer_get_size (buf) < size) |
| goto too_small; |
| |
| 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; |
| 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; |
| 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; |
| |
| bufsize = gst_buffer_get_size (buf); |
| |
| if (bufsize == offset) |
| goto end_offset; |
| |
| if (bufsize < offset + 8) |
| goto too_small; |
| |
| /* read header */ |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| ptr = info.data + offset; |
| fourcc = GST_READ_UINT32_LE (ptr); |
| size = GST_READ_UINT32_LE (ptr + 4); |
| 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) |
| *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) |
| { |
| GstMapInfo info; |
| guint32 tag; |
| |
| g_return_val_if_fail (buf != NULL, FALSE); |
| g_return_val_if_fail (doctype != NULL, FALSE); |
| |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| if (info.size < 12) |
| goto too_small; |
| |
| tag = GST_READ_UINT32_LE (info.data); |
| if (tag != GST_RIFF_TAG_RIFF && tag != GST_RIFF_TAG_AVF0) |
| goto not_riff; |
| |
| *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," |
| " %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: %" GST_FOURCC_FORMAT, |
| GST_FOURCC_ARGS (tag))); |
| 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; |
| GstMapInfo info; |
| |
| g_return_val_if_fail (buf != NULL, FALSE); |
| g_return_val_if_fail (_strh != NULL, FALSE); |
| |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| if (info.size < sizeof (gst_riff_strh)) |
| goto too_small; |
| |
| strh = g_memdup (info.data, info.size); |
| gst_buffer_unmap (buf, &info); |
| |
| 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)", |
| 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; |
| 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); |
| |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| if (info.size < sizeof (gst_riff_strf_vids)) |
| goto too_small; |
| |
| 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; |
| if (strf->size > info.size) { |
| GST_WARNING_OBJECT (element, |
| "strf_vids header gave %d bytes data, only %" G_GSIZE_FORMAT |
| " available", strf->size, info.size); |
| strf->size = info.size; |
| } |
| if (sizeof (gst_riff_strf_vids) < info.size) { |
| *data = |
| gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, |
| sizeof (gst_riff_strf_vids), info.size - sizeof (gst_riff_strf_vids)); |
| } |
| 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", |
| 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)", |
| 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. |
| */ |
| gboolean |
| gst_riff_parse_strf_auds (GstElement * element, |
| GstBuffer * buf, gst_riff_strf_auds ** _strf, GstBuffer ** data) |
| { |
| gst_riff_strf_auds *strf; |
| 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); |
| |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| if (info.size < sizeof (gst_riff_strf_auds)) |
| goto too_small; |
| |
| 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); |
| strf->size = GUINT16_FROM_LE (strf->size); |
| #endif |
| |
| /* size checking */ |
| *data = NULL; |
| if (info.size > sizeof (gst_riff_strf_auds) + 2) { |
| gint len; |
| |
| len = GST_READ_UINT16_LE (&data[16]); |
| if (len + 2 + sizeof (gst_riff_strf_auds) > info.size) { |
| GST_WARNING_OBJECT (element, |
| "Extradata indicated %d bytes, but only %" G_GSSIZE_FORMAT |
| " available", len, info.size - 2 - sizeof (gst_riff_strf_auds)); |
| len = info.size - 2 - sizeof (gst_riff_strf_auds); |
| } |
| if (len) |
| *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, " size %d", strf->size); |
| if (*data) |
| GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata", |
| gst_buffer_get_size (*data)); |
| |
| 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_GSSIZE_FORMAT " needed)", info.size, |
| 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; |
| 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); |
| |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| if (info.size < sizeof (gst_riff_strf_iavs)) |
| goto too_small; |
| |
| strf = g_memdup (info.data, info.size); |
| gst_buffer_unmap (buf, &info); |
| |
| 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_GSSIZE_FORMAT " needed)", info.size, |
| sizeof (gst_riff_strf_iavs)); |
| gst_buffer_unmap (buf, &info); |
| gst_buffer_unref (buf); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * 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) |
| { |
| GstMapInfo info; |
| guint8 *ptr; |
| gsize left; |
| guint tsize; |
| guint32 tag; |
| const gchar *type; |
| GstTagList *taglist; |
| |
| g_return_if_fail (_taglist != NULL); |
| |
| if (!buf) { |
| *_taglist = NULL; |
| return; |
| } |
| gst_buffer_map (buf, &info, GST_MAP_READ); |
| |
| taglist = gst_tag_list_new_empty (); |
| |
| ptr = info.data; |
| left = info.size; |
| |
| 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)); |
| |
| left -= 8; |
| ptr += 8; |
| |
| GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u", |
| GST_FOURCC_ARGS (tag), tsize); |
| |
| if (tsize > left) { |
| GST_WARNING_OBJECT (element, |
| "Tagsize %d is larger than available data %" G_GSIZE_FORMAT, |
| tsize, left); |
| 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; |
| 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; |
| } |
| |
| if (type != NULL && ptr[0] != '\0') { |
| static const gchar *env_vars[] = { "GST_AVI_TAG_ENCODING", |
| "GST_RIFF_TAG_ENCODING", "GST_TAG_ENCODING", NULL |
| }; |
| GType tag_type; |
| gchar *val; |
| |
| GST_DEBUG_OBJECT (element, "mapped tag %" GST_FOURCC_FORMAT " to tag %s", |
| GST_FOURCC_ARGS (tag), type); |
| |
| 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); |
| } |
| } |
| |
| if (tsize & 1) { |
| tsize++; |
| if (tsize > left) |
| tsize = left; |
| } |
| |
| 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_free (taglist); |
| } |
| gst_buffer_unmap (buf, &info); |
| |
| return; |
| } |