Instead of using g_array_free which is not thread safe use g_array_unref instead Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1203>
		
			
				
	
	
		
			683 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			683 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GStreamer EBML I/O
 | |
|  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
 | |
|  *
 | |
|  * ebml-read.c: read EBML data from file/stream
 | |
|  *
 | |
|  * 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>
 | |
| 
 | |
| #include "ebml-read.h"
 | |
| #include "ebml-ids.h"
 | |
| 
 | |
| #include <gst/math-compat.h>
 | |
| 
 | |
| GST_DEBUG_CATEGORY (ebmlread_debug);
 | |
| #define GST_CAT_DEFAULT ebmlread_debug
 | |
| 
 | |
| /* Peeks following element id and element length in datastream provided
 | |
|  * by @peek with @ctx as user data.
 | |
|  * Returns GST_FLOW_EOS if not enough data to read id and length.
 | |
|  * Otherwise, @needed provides the prefix length (id + length), and
 | |
|  * @length provides element length.
 | |
|  *
 | |
|  * @object and @offset are provided for informative messaging/debug purposes.
 | |
|  */
 | |
| GstFlowReturn
 | |
| gst_ebml_peek_id_length (guint32 * _id, guint64 * _length, guint * _needed,
 | |
|     GstPeekData peek, gpointer * ctx, GstElement * el, guint64 offset)
 | |
| {
 | |
|   guint needed;
 | |
|   const guint8 *buf;
 | |
|   gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
 | |
|   guint64 total;
 | |
|   guint8 b;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
 | |
|   g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR);
 | |
|   g_return_val_if_fail (_needed != NULL, GST_FLOW_ERROR);
 | |
| 
 | |
|   /* well ... */
 | |
|   *_id = (guint32) GST_EBML_SIZE_UNKNOWN;
 | |
|   *_length = GST_EBML_SIZE_UNKNOWN;
 | |
| 
 | |
|   /* read element id */
 | |
|   needed = 2;
 | |
|   ret = peek (ctx, needed, &buf);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     goto peek_error;
 | |
|   b = GST_READ_UINT8 (buf);
 | |
|   total = (guint64) b;
 | |
|   while (read <= 4 && !(total & len_mask)) {
 | |
|     read++;
 | |
|     len_mask >>= 1;
 | |
|   }
 | |
|   if (G_UNLIKELY (read > 4))
 | |
|     goto invalid_id;
 | |
| 
 | |
|   /* need id and at least something for subsequent length */
 | |
|   needed = read + 1;
 | |
|   ret = peek (ctx, needed, &buf);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     goto peek_error;
 | |
|   while (n < read) {
 | |
|     b = GST_READ_UINT8 (buf + n);
 | |
|     total = (total << 8) | b;
 | |
|     ++n;
 | |
|   }
 | |
|   *_id = (guint32) total;
 | |
| 
 | |
|   /* read element length */
 | |
|   b = GST_READ_UINT8 (buf + n);
 | |
|   total = (guint64) b;
 | |
|   len_mask = 0x80;
 | |
|   read = 1;
 | |
|   while (read <= 8 && !(total & len_mask)) {
 | |
|     read++;
 | |
|     len_mask >>= 1;
 | |
|   }
 | |
|   if (G_UNLIKELY (read > 8))
 | |
|     goto invalid_length;
 | |
|   if ((total &= (len_mask - 1)) == len_mask - 1)
 | |
|     num_ffs++;
 | |
| 
 | |
|   needed += read - 1;
 | |
|   ret = peek (ctx, needed, &buf);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     goto peek_error;
 | |
|   buf += (needed - read);
 | |
|   n = 1;
 | |
|   while (n < read) {
 | |
|     guint8 b = GST_READ_UINT8 (buf + n);
 | |
| 
 | |
|     if (G_UNLIKELY (b == 0xff))
 | |
|       num_ffs++;
 | |
|     total = (total << 8) | b;
 | |
|     ++n;
 | |
|   }
 | |
| 
 | |
|   if (G_UNLIKELY (read == num_ffs))
 | |
|     *_length = G_MAXUINT64;
 | |
|   else
 | |
|     *_length = total;
 | |
| 
 | |
|   *_needed = needed;
 | |
| 
 | |
|   return GST_FLOW_OK;
 | |
| 
 | |
|   /* ERRORS */
 | |
| peek_error:
 | |
|   {
 | |
|     if (ret != GST_FLOW_FLUSHING && ret != GST_FLOW_EOS)
 | |
|       GST_WARNING_OBJECT (el, "peek failed, ret = %s", gst_flow_get_name (ret));
 | |
|     else
 | |
|       GST_DEBUG_OBJECT (el, "peek failed, ret = %s", gst_flow_get_name (ret));
 | |
|     *_needed = needed;
 | |
|     return ret;
 | |
|   }
 | |
| invalid_id:
 | |
|   {
 | |
|     GST_ERROR_OBJECT (el,
 | |
|         "Invalid EBML ID size tag (0x%x) at position %" G_GUINT64_FORMAT " (0x%"
 | |
|         G_GINT64_MODIFIER "x)", (guint) b, offset, offset);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| invalid_length:
 | |
|   {
 | |
|     GST_ERROR_OBJECT (el,
 | |
|         "Invalid EBML length size tag (0x%x) at position %" G_GUINT64_FORMAT
 | |
|         " (0x%" G_GINT64_MODIFIER "x)", (guint) b, offset, offset);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* setup for parsing @buf at position @offset on behalf of @el.
 | |
|  * Takes ownership of @buf. */
 | |
| void
 | |
| gst_ebml_read_init (GstEbmlRead * ebml, GstElement * el, GstBuffer * buf,
 | |
|     guint64 offset)
 | |
| {
 | |
|   GstEbmlMaster m;
 | |
| 
 | |
|   g_return_if_fail (el);
 | |
|   g_return_if_fail (buf);
 | |
| 
 | |
|   ebml->el = el;
 | |
|   ebml->offset = offset;
 | |
|   ebml->buf = buf;
 | |
|   gst_buffer_map (buf, &ebml->map, GST_MAP_READ);
 | |
|   ebml->readers = g_array_sized_new (FALSE, FALSE, sizeof (GstEbmlMaster), 10);
 | |
|   m.offset = ebml->offset;
 | |
|   gst_byte_reader_init (&m.br, ebml->map.data, ebml->map.size);
 | |
|   g_array_append_val (ebml->readers, m);
 | |
| }
 | |
| 
 | |
| void
 | |
| gst_ebml_read_clear (GstEbmlRead * ebml)
 | |
| {
 | |
|   if (ebml->readers)
 | |
|     g_array_unref (ebml->readers);
 | |
|   ebml->readers = NULL;
 | |
|   if (ebml->buf) {
 | |
|     gst_buffer_unmap (ebml->buf, &ebml->map);
 | |
|     gst_buffer_unref (ebml->buf);
 | |
|   }
 | |
|   ebml->buf = NULL;
 | |
|   ebml->el = NULL;
 | |
| }
 | |
| 
 | |
| static GstFlowReturn
 | |
| gst_ebml_read_peek (GstByteReader * br, guint peek, const guint8 ** data)
 | |
| {
 | |
|   if (G_LIKELY (gst_byte_reader_peek_data (br, peek, data)))
 | |
|     return GST_FLOW_OK;
 | |
|   else
 | |
|     return GST_FLOW_EOS;
 | |
| }
 | |
| 
 | |
| static GstFlowReturn
 | |
| gst_ebml_peek_id_full (GstEbmlRead * ebml, guint32 * id, guint64 * length,
 | |
|     guint * prefix)
 | |
| {
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_peek_id_length (id, length, prefix,
 | |
|       (GstPeekData) gst_ebml_read_peek, (gpointer) gst_ebml_read_br (ebml),
 | |
|       ebml->el, gst_ebml_read_get_pos (ebml));
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   GST_LOG_OBJECT (ebml->el, "id 0x%x at offset 0x%" G_GINT64_MODIFIER "x"
 | |
|       " of length %" G_GUINT64_FORMAT ", prefix %d", *id,
 | |
|       gst_ebml_read_get_pos (ebml), *length, *prefix);
 | |
| 
 | |
| #ifndef GST_DISABLE_GST_DEBUG
 | |
|   if (ebmlread_debug->threshold >= GST_LEVEL_LOG) {
 | |
|     const guint8 *data = NULL;
 | |
|     GstByteReader *br = gst_ebml_read_br (ebml);
 | |
|     guint size = gst_byte_reader_get_remaining (br);
 | |
| 
 | |
|     if (gst_byte_reader_peek_data (br, size, &data)) {
 | |
| 
 | |
|       GST_LOG_OBJECT (ebml->el, "current br %p; remaining %d", br, size);
 | |
|       if (data)
 | |
|         GST_MEMDUMP_OBJECT (ebml->el, "element", data, MIN (size, *length));
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_peek_id (GstEbmlRead * ebml, guint32 * id)
 | |
| {
 | |
|   guint64 length;
 | |
|   guint needed;
 | |
| 
 | |
|   return gst_ebml_peek_id_full (ebml, id, &length, &needed);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element, the contents are supposed to be sub-elements which
 | |
|  * can be read separately.  A new bytereader is setup for doing so.
 | |
|  */
 | |
| GstFlowReturn
 | |
| gst_ebml_read_master (GstEbmlRead * ebml, guint32 * id)
 | |
| {
 | |
|   guint64 length;
 | |
|   guint prefix;
 | |
|   const guint8 *data = NULL;
 | |
|   GstFlowReturn ret;
 | |
|   GstEbmlMaster m;
 | |
| 
 | |
|   ret = gst_ebml_peek_id_full (ebml, id, &length, &prefix);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   /* we just at least peeked the id */
 | |
|   if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix))
 | |
|     return GST_FLOW_ERROR;      /* FIXME: do proper error handling */
 | |
| 
 | |
|   m.offset = gst_ebml_read_get_pos (ebml);
 | |
|   if (!gst_byte_reader_get_data (gst_ebml_read_br (ebml), length, &data))
 | |
|     return GST_FLOW_PARSE;
 | |
| 
 | |
|   GST_LOG_OBJECT (ebml->el, "pushing level %d at offset %" G_GUINT64_FORMAT,
 | |
|       ebml->readers->len, m.offset);
 | |
|   gst_byte_reader_init (&m.br, data, length);
 | |
|   g_array_append_val (ebml->readers, m);
 | |
| 
 | |
|   return GST_FLOW_OK;
 | |
| }
 | |
| 
 | |
| /* explicitly pop a bytereader from stack.  Usually invoked automagically. */
 | |
| GstFlowReturn
 | |
| gst_ebml_read_pop_master (GstEbmlRead * ebml)
 | |
| {
 | |
|   g_return_val_if_fail (ebml->readers, GST_FLOW_ERROR);
 | |
| 
 | |
|   /* never remove initial bytereader */
 | |
|   if (ebml->readers->len > 1) {
 | |
|     GST_LOG_OBJECT (ebml->el, "popping level %d", ebml->readers->len - 1);
 | |
|     g_array_remove_index (ebml->readers, ebml->readers->len - 1);
 | |
|   }
 | |
| 
 | |
|   return GST_FLOW_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Skip the next element.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_skip (GstEbmlRead * ebml)
 | |
| {
 | |
|   guint64 length;
 | |
|   guint32 id;
 | |
|   guint prefix;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_peek_id_full (ebml, &id, &length, &prefix);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), length + prefix))
 | |
|     return GST_FLOW_PARSE;
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as a GstBuffer (binary).
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_buffer (GstEbmlRead * ebml, guint32 * id, GstBuffer ** buf)
 | |
| {
 | |
|   guint64 length;
 | |
|   guint prefix;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_peek_id_full (ebml, id, &length, &prefix);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   /* we just at least peeked the id */
 | |
|   if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix))
 | |
|     return GST_FLOW_ERROR;      /* FIXME: do proper error handling */
 | |
| 
 | |
|   if (G_LIKELY (length > 0)) {
 | |
|     guint offset;
 | |
| 
 | |
|     offset = gst_ebml_read_get_pos (ebml) - ebml->offset;
 | |
|     if (G_LIKELY (gst_byte_reader_skip (gst_ebml_read_br (ebml), length))) {
 | |
|       *buf = gst_buffer_copy_region (ebml->buf, GST_BUFFER_COPY_ALL,
 | |
|           offset, length);
 | |
|     } else {
 | |
|       *buf = NULL;
 | |
|       return GST_FLOW_PARSE;
 | |
|     }
 | |
|   } else {
 | |
|     *buf = gst_buffer_new ();
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element, return a pointer to it and its size.
 | |
|  */
 | |
| 
 | |
| static GstFlowReturn
 | |
| gst_ebml_read_bytes (GstEbmlRead * ebml, guint32 * id, const guint8 ** data,
 | |
|     guint * size)
 | |
| {
 | |
|   guint64 length;
 | |
|   guint prefix;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   *size = 0;
 | |
| 
 | |
|   ret = gst_ebml_peek_id_full (ebml, id, &length, &prefix);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   /* we just at least peeked the id */
 | |
|   if (!gst_byte_reader_skip (gst_ebml_read_br (ebml), prefix))
 | |
|     return GST_FLOW_ERROR;      /* FIXME: do proper error handling */
 | |
| 
 | |
|   /* This shouldn't happen here with the elements read through this function */
 | |
|   if (length == GST_EBML_SIZE_UNKNOWN || length == G_MAXUINT64) {
 | |
|     GST_ERROR_OBJECT (ebml->el, "element 0x%x has undefined length!", *id);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| 
 | |
|   /* Sanity check since we're downcasting a 64-bit len to possibly 32-bit here */
 | |
|   if (length >= G_MAXUINT) {
 | |
|     GST_ERROR_OBJECT (ebml->el, "element 0x%x too large, "
 | |
|         "size %" G_GUINT64_FORMAT, *id, length);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| 
 | |
|   *data = NULL;
 | |
|   if (G_LIKELY (length > 0)) {
 | |
|     if (!gst_byte_reader_get_data (gst_ebml_read_br (ebml), length, data))
 | |
|       return GST_FLOW_PARSE;
 | |
|   }
 | |
| 
 | |
|   *size = length;
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as an unsigned int.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_uint (GstEbmlRead * ebml, guint32 * id, guint64 * num)
 | |
| {
 | |
|   const guint8 *data;
 | |
|   guint size;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_read_bytes (ebml, id, &data, &size);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   if (size > 8) {
 | |
|     GST_ERROR_OBJECT (ebml->el,
 | |
|         "Invalid integer element size %d at position %" G_GUINT64_FORMAT " (0x%"
 | |
|         G_GINT64_MODIFIER "x)", size, gst_ebml_read_get_pos (ebml) - size,
 | |
|         gst_ebml_read_get_pos (ebml) - size);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (size == 0) {
 | |
|     *num = 0;
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   *num = 0;
 | |
|   while (size > 0) {
 | |
|     *num = (*num << 8) | *data;
 | |
|     size--;
 | |
|     data++;
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as a signed int.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_sint (GstEbmlRead * ebml, guint32 * id, gint64 * num)
 | |
| {
 | |
|   const guint8 *data;
 | |
|   guint size;
 | |
|   gboolean negative = 0;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_read_bytes (ebml, id, &data, &size);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   if (size > 8) {
 | |
|     GST_ERROR_OBJECT (ebml->el,
 | |
|         "Invalid integer element size %d at position %" G_GUINT64_FORMAT " (0x%"
 | |
|         G_GINT64_MODIFIER "x)", size, gst_ebml_read_get_pos (ebml) - size,
 | |
|         gst_ebml_read_get_pos (ebml) - size);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (size == 0) {
 | |
|     *num = 0;
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   *num = 0;
 | |
|   if (*data & 0x80) {
 | |
|     negative = 1;
 | |
|     *num = *data & ~0x80;
 | |
|     size--;
 | |
|     data++;
 | |
|   }
 | |
| 
 | |
|   while (size > 0) {
 | |
|     *num = (*num << 8) | *data;
 | |
|     size--;
 | |
|     data++;
 | |
|   }
 | |
| 
 | |
|   /* make signed */
 | |
|   if (negative) {
 | |
|     *num = 0 - *num;
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /* Convert 80 bit extended precision float in big endian format to double.
 | |
|  * Code taken from libavutil/intfloat_readwrite.c from ffmpeg,
 | |
|  * licensed under LGPL */
 | |
| 
 | |
| struct _ext_float
 | |
| {
 | |
|   guint8 exponent[2];
 | |
|   guint8 mantissa[8];
 | |
| };
 | |
| 
 | |
| static gdouble
 | |
| _ext2dbl (const guint8 * data)
 | |
| {
 | |
|   struct _ext_float ext;
 | |
|   guint64 m = 0;
 | |
|   gint e, i;
 | |
| 
 | |
|   memcpy (&ext.exponent, data, 2);
 | |
|   memcpy (&ext.mantissa, data + 2, 8);
 | |
| 
 | |
|   for (i = 0; i < 8; i++)
 | |
|     m = (m << 8) + ext.mantissa[i];
 | |
|   e = (((gint) ext.exponent[0] & 0x7f) << 8) | ext.exponent[1];
 | |
|   if (e == 0x7fff && m)
 | |
|     return NAN;
 | |
|   e -= 16383 + 63;              /* In IEEE 80 bits, the whole (i.e. 1.xxxx)
 | |
|                                  * mantissa bit is written as opposed to the
 | |
|                                  * single and double precision formats */
 | |
|   if (ext.exponent[0] & 0x80)
 | |
|     m = -m;
 | |
|   return ldexp (m, e);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as a float.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_float (GstEbmlRead * ebml, guint32 * id, gdouble * num)
 | |
| {
 | |
|   const guint8 *data;
 | |
|   guint size;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_read_bytes (ebml, id, &data, &size);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   if (size != 0 && size != 4 && size != 8 && size != 10) {
 | |
|     GST_ERROR_OBJECT (ebml->el,
 | |
|         "Invalid float element size %d at position %" G_GUINT64_FORMAT " (0x%"
 | |
|         G_GINT64_MODIFIER "x)", size, gst_ebml_read_get_pos (ebml) - size,
 | |
|         gst_ebml_read_get_pos (ebml) - size);
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (size == 4) {
 | |
|     gfloat f;
 | |
| 
 | |
|     memcpy (&f, data, 4);
 | |
|     f = GFLOAT_FROM_BE (f);
 | |
| 
 | |
|     *num = f;
 | |
|   } else if (size == 8) {
 | |
|     gdouble d;
 | |
| 
 | |
|     memcpy (&d, data, 8);
 | |
|     d = GDOUBLE_FROM_BE (d);
 | |
| 
 | |
|     *num = d;
 | |
|   } else if (size == 10) {
 | |
|     *num = _ext2dbl (data);
 | |
|   } else {
 | |
|     /* size == 0 means a value of 0.0 */
 | |
|     *num = 0.0;
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as a C string.
 | |
|  */
 | |
| 
 | |
| static GstFlowReturn
 | |
| gst_ebml_read_string (GstEbmlRead * ebml, guint32 * id, gchar ** str)
 | |
| {
 | |
|   const guint8 *data;
 | |
|   guint size;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_read_bytes (ebml, id, &data, &size);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   *str = g_malloc (size + 1);
 | |
|   memcpy (*str, data, size);
 | |
|   (*str)[size] = '\0';
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as an ASCII string.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_ascii (GstEbmlRead * ebml, guint32 * id, gchar ** str_out)
 | |
| {
 | |
|   GstFlowReturn ret;
 | |
|   gchar *str;
 | |
|   gchar *iter;
 | |
| 
 | |
| #ifndef GST_DISABLE_GST_DEBUG
 | |
|   guint64 oldoff = ebml->offset;
 | |
| #endif
 | |
| 
 | |
|   ret = gst_ebml_read_string (ebml, id, &str);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   for (iter = str; *iter != '\0'; iter++) {
 | |
|     if (G_UNLIKELY (*iter & 0x80)) {
 | |
|       GST_ERROR_OBJECT (ebml,
 | |
|           "Invalid ASCII string at offset %" G_GUINT64_FORMAT, oldoff);
 | |
|       g_free (str);
 | |
|       return GST_FLOW_ERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   *str_out = str;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as a UTF-8 string.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_utf8 (GstEbmlRead * ebml, guint32 * id, gchar ** str)
 | |
| {
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
| #ifndef GST_DISABLE_GST_DEBUG
 | |
|   guint64 oldoff = gst_ebml_read_get_pos (ebml);
 | |
| #endif
 | |
| 
 | |
|   ret = gst_ebml_read_string (ebml, id, str);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   if (str != NULL && *str != NULL && **str != '\0' &&
 | |
|       !g_utf8_validate (*str, -1, NULL)) {
 | |
|     GST_WARNING_OBJECT (ebml->el,
 | |
|         "Invalid UTF-8 string at offset %" G_GUINT64_FORMAT, oldoff);
 | |
|   }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as a date.
 | |
|  * Returns the nanoseconds since the unix epoch.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_date (GstEbmlRead * ebml, guint32 * id, gint64 * date)
 | |
| {
 | |
|   gint64 ebml_date;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_read_sint (ebml, id, &ebml_date);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   *date = ebml_date + GST_EBML_DATE_OFFSET;
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read the next element as binary data.
 | |
|  */
 | |
| 
 | |
| GstFlowReturn
 | |
| gst_ebml_read_binary (GstEbmlRead * ebml,
 | |
|     guint32 * id, guint8 ** binary, guint64 * length)
 | |
| {
 | |
|   const guint8 *data;
 | |
|   guint size;
 | |
|   GstFlowReturn ret;
 | |
| 
 | |
|   ret = gst_ebml_read_bytes (ebml, id, &data, &size);
 | |
|   if (ret != GST_FLOW_OK)
 | |
|     return ret;
 | |
| 
 | |
|   *length = size;
 | |
|   *binary = g_memdup2 (data, size);
 | |
| 
 | |
|   return GST_FLOW_OK;
 | |
| }
 |