Use g_memdup2() where available and add fallback for older GLib versions
[platform/upstream/gstreamer.git] / gst-libs / gst / riff / riff-read.c
index b3ac371..5e9f331 100644 (file)
@@ -15,8 +15,8 @@
  *
  * 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.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -40,8 +40,8 @@ GST_DEBUG_CATEGORY_EXTERN (riff_debug);
  * @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.
+ * Reads a single chunk of data. 'JUNK' chunks are skipped
+ * automatically.
  *
  * Returns: flow status.
  */
@@ -52,10 +52,9 @@ gst_riff_read_chunk (GstElement * element,
 {
   GstBuffer *buf;
   GstFlowReturn res;
-  guint8 *data;
+  GstMapInfo info;
   guint size;
   guint64 offset = *_offset;
-  gsize bsize;
 
   g_return_val_if_fail (element != NULL, GST_FLOW_ERROR);
   g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
@@ -65,15 +64,16 @@ gst_riff_read_chunk (GstElement * element,
 
 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;
 
-  data = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-  *tag = GST_READ_UINT32_LE (data);
-  size = GST_READ_UINT32_LE (data + 4);
-  gst_buffer_unmap (buf, data, bsize);
+  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",
@@ -88,6 +88,7 @@ skip_junk:
     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)
@@ -129,8 +130,8 @@ gst_riff_parse_chunk (GstElement * element, GstBuffer * buf,
 {
   guint size, bufsize;
   guint32 fourcc;
-  guint8 *data, *ptr;
-  gsize bsize;
+  guint8 *ptr;
+  GstMapInfo info;
   guint offset = *_offset;
 
   g_return_val_if_fail (element != NULL, FALSE);
@@ -151,11 +152,11 @@ gst_riff_parse_chunk (GstElement * element, GstBuffer * buf,
     goto too_small;
 
   /* read header */
-  data = ptr = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-  ptr += offset;
+  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, data, bsize);
+  gst_buffer_unmap (buf, &info);
 
   GST_DEBUG_OBJECT (element, "fourcc=%" GST_FOURCC_FORMAT ", size=%u",
       GST_FOURCC_ARGS (fourcc), size);
@@ -223,23 +224,23 @@ gboolean
 gst_riff_parse_file_header (GstElement * element,
     GstBuffer * buf, guint32 * doctype)
 {
-  guint8 *data;
+  GstMapInfo info;
   guint32 tag;
-  gsize size;
 
   g_return_val_if_fail (buf != NULL, FALSE);
   g_return_val_if_fail (doctype != NULL, FALSE);
 
-  data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
-  if (size < 12)
+  gst_buffer_map (buf, &info, GST_MAP_READ);
+  if (info.size < 12)
     goto too_small;
 
-  tag = GST_READ_UINT32_LE (data);
-  if (tag != GST_RIFF_TAG_RIFF && tag != GST_RIFF_TAG_AVF0)
+  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;
 
-  *doctype = GST_READ_UINT32_LE (data + 8);
-  gst_buffer_unmap (buf, data, size);
+  *doctype = GST_READ_UINT32_LE (info.data + 8);
+  gst_buffer_unmap (buf, &info);
 
   gst_buffer_unref (buf);
 
@@ -250,17 +251,16 @@ too_small:
   {
     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
         ("Not enough data to parse RIFF header (%" G_GSIZE_FORMAT " available,"
-            " %d needed)", size, 12));
-    gst_buffer_unmap (buf, data, size);
+            " %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, data, size);
+        ("Stream is no RIFF stream: 0x%" G_GINT32_MODIFIER "x", tag));
+    gst_buffer_unmap (buf, &info);
     gst_buffer_unref (buf);
     return FALSE;
   }
@@ -284,18 +284,17 @@ gst_riff_parse_strh (GstElement * element,
     GstBuffer * buf, gst_riff_strh ** _strh)
 {
   gst_riff_strh *strh;
-  guint8 *data;
-  gsize size;
+  GstMapInfo info;
 
   g_return_val_if_fail (buf != NULL, FALSE);
   g_return_val_if_fail (_strh != NULL, FALSE);
 
-  data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
-  if (size < sizeof (gst_riff_strh))
+  gst_buffer_map (buf, &info, GST_MAP_READ);
+  if (info.size < sizeof (gst_riff_strh))
     goto too_small;
 
-  strh = g_memdup (data, size);
-  gst_buffer_unmap (buf, data, size);
+  strh = g_memdup2 (info.data, info.size);
+  gst_buffer_unmap (buf, &info);
 
   gst_buffer_unref (buf);
 
@@ -346,8 +345,8 @@ too_small:
   {
     GST_ERROR_OBJECT (element,
         "Too small strh (%" G_GSIZE_FORMAT " available, %d needed)",
-        size, (int) sizeof (gst_riff_strh));
-    gst_buffer_unmap (buf, data, size);
+        info.size, (int) sizeof (gst_riff_strh));
+    gst_buffer_unmap (buf, &info);
     gst_buffer_unref (buf);
     return FALSE;
   }
@@ -363,7 +362,7 @@ too_small:
  *        containing extradata for this particular stream (e.g.
  *        palette, codec initialization data).
  *
- * Parses a video stream´s strf structure plus optionally some
+ * 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
@@ -375,19 +374,18 @@ gst_riff_parse_strf_vids (GstElement * element,
     GstBuffer * buf, gst_riff_strf_vids ** _strf, GstBuffer ** data)
 {
   gst_riff_strf_vids *strf;
-  guint8 *bdata;
-  gsize size;
+  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);
 
-  bdata = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
-  if (size < sizeof (gst_riff_strf_vids))
+  gst_buffer_map (buf, &info, GST_MAP_READ);
+  if (info.size < sizeof (gst_riff_strf_vids))
     goto too_small;
 
-  strf = g_memdup (bdata, size);
-  gst_buffer_unmap (buf, bdata, size);
+  strf = g_memdup2 (info.data, info.size);
+  gst_buffer_unmap (buf, &info);
 
 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
   strf->size = GUINT32_FROM_LE (strf->size);
@@ -405,16 +403,16 @@ gst_riff_parse_strf_vids (GstElement * element,
 
   /* size checking */
   *data = NULL;
-  if (strf->size > size) {
+  if (strf->size > info.size) {
     GST_WARNING_OBJECT (element,
         "strf_vids header gave %d bytes data, only %" G_GSIZE_FORMAT
-        " available", strf->size, size);
-    strf->size = size;
+        " available", strf->size, info.size);
+    strf->size = info.size;
   }
-  if (sizeof (gst_riff_strf_vids) < size) {
+  if (sizeof (gst_riff_strf_vids) < info.size) {
     *data =
         gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
-        sizeof (gst_riff_strf_vids), size - sizeof (gst_riff_strf_vids));
+        sizeof (gst_riff_strf_vids), info.size - sizeof (gst_riff_strf_vids));
   }
   gst_buffer_unref (buf);
 
@@ -445,8 +443,8 @@ too_small:
   {
     GST_ERROR_OBJECT (element,
         "Too small strf_vids (%" G_GSIZE_FORMAT " available, %d needed)",
-        size, (int) sizeof (gst_riff_strf_vids));
-    gst_buffer_unmap (buf, data, size);
+        info.size, (int) sizeof (gst_riff_strf_vids));
+    gst_buffer_unmap (buf, &info);
     gst_buffer_unref (buf);
     return FALSE;
   }
@@ -462,7 +460,7 @@ too_small:
  *        containing extradata for this particular stream (e.g.
  *        codec initialization data).
  *
- * Parses an audio stream´s strf structure plus optionally some
+ * Parses an audio stream's strf structure plus optionally some
  * extradata from input data. This function takes ownership of @buf.
  * use.
  *
@@ -474,18 +472,17 @@ gst_riff_parse_strf_auds (GstElement * element,
     GstBuffer * buf, gst_riff_strf_auds ** _strf, GstBuffer ** data)
 {
   gst_riff_strf_auds *strf;
-  gsize bsize;
-  guint8 *bdata;
+  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);
 
-  bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-  if (bsize < sizeof (gst_riff_strf_auds))
+  gst_buffer_map (buf, &info, GST_MAP_READ);
+  if (info.size < sizeof (gst_riff_strf_auds))
     goto too_small;
 
-  strf = g_memdup (bdata, bsize);
+  strf = g_memdup2 (info.data, info.size);
 
 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
   strf->format = GUINT16_FROM_LE (strf->format);
@@ -493,20 +490,20 @@ gst_riff_parse_strf_auds (GstElement * element,
   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);
+  strf->bits_per_sample = GUINT16_FROM_LE (strf->bits_per_sample);
 #endif
 
   /* size checking */
   *data = NULL;
-  if (bsize > sizeof (gst_riff_strf_auds) + 2) {
+  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) > bsize) {
+    len = GST_READ_UINT16_LE (&info.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, bsize - 2 - sizeof (gst_riff_strf_auds));
-      len = bsize - 2 - sizeof (gst_riff_strf_auds);
+          "Extradata indicated %d bytes, but only %" G_GSIZE_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,
@@ -520,12 +517,12 @@ gst_riff_parse_strf_auds (GstElement * element,
   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);
+  GST_INFO_OBJECT (element, " bits/sample %d", strf->bits_per_sample);
   if (*data)
     GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
         gst_buffer_get_size (*data));
 
-  gst_buffer_unmap (buf, bdata, bsize);
+  gst_buffer_unmap (buf, &info);
   gst_buffer_unref (buf);
 
   *_strf = strf;
@@ -537,8 +534,9 @@ too_small:
   {
     GST_ERROR_OBJECT (element,
         "Too small strf_auds (%" G_GSIZE_FORMAT " available"
-        ", %" G_GSSIZE_FORMAT " needed)", bsize, sizeof (gst_riff_strf_auds));
-    gst_buffer_unmap (buf, bdata, bsize);
+        ", %" G_GSIZE_FORMAT " needed)", info.size,
+        sizeof (gst_riff_strf_auds));
+    gst_buffer_unmap (buf, &info);
     gst_buffer_unref (buf);
     return FALSE;
   }
@@ -566,19 +564,18 @@ gst_riff_parse_strf_iavs (GstElement * element,
     GstBuffer * buf, gst_riff_strf_iavs ** _strf, GstBuffer ** data)
 {
   gst_riff_strf_iavs *strf;
-  gsize bsize;
-  guint8 *bdata;
+  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);
 
-  bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ);
-  if (bsize < sizeof (gst_riff_strf_iavs))
+  gst_buffer_map (buf, &info, GST_MAP_READ);
+  if (info.size < sizeof (gst_riff_strf_iavs))
     goto too_small;
 
-  strf = g_memdup (bdata, bsize);
-  gst_buffer_unmap (buf, bdata, bsize);
+  strf = g_memdup2 (info.data, info.size);
+  gst_buffer_unmap (buf, &info);
 
   gst_buffer_unref (buf);
 
@@ -614,13 +611,48 @@ too_small:
   {
     GST_ERROR_OBJECT (element,
         "Too small strf_iavs (%" G_GSIZE_FORMAT "available"
-        ", %" G_GSSIZE_FORMAT " needed)", bsize, sizeof (gst_riff_strf_iavs));
-    gst_buffer_unmap (buf, bdata, bsize);
+        ", %" G_GSIZE_FORMAT " needed)", info.size,
+        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).
@@ -635,8 +667,9 @@ void
 gst_riff_parse_info (GstElement * element,
     GstBuffer * buf, GstTagList ** _taglist)
 {
-  guint8 *data, *ptr;
-  gsize size, left;
+  GstMapInfo info;
+  guint8 *ptr;
+  gsize left;
   guint tsize;
   guint32 tag;
   const gchar *type;
@@ -648,16 +681,19 @@ gst_riff_parse_info (GstElement * element,
     *_taglist = NULL;
     return;
   }
-  data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (buf, &info, GST_MAP_READ);
 
   taglist = gst_tag_list_new_empty ();
 
-  ptr = data;
-  left = size;
+  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;
 
@@ -671,11 +707,17 @@ gst_riff_parse_info (GstElement * element,
       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;
@@ -689,7 +731,7 @@ gst_riff_parse_info (GstElement * element,
         type = GST_TAG_COPYRIGHT;
         break;
       case GST_RIFF_INFO_ICRD:
-        type = GST_TAG_DATE;
+        type = GST_TAG_DATE_TIME;
         break;
       case GST_RIFF_INFO_ICRP:
         type = NULL;            /*"Cropped"; */
@@ -722,10 +764,10 @@ gst_riff_parse_info (GstElement * element,
         type = NULL;            /*"Palette"; */
         break;
       case GST_RIFF_INFO_IPRD:
-        type = NULL;            /*"Product"; */
+        type = GST_TAG_ALBUM;
         break;
       case GST_RIFF_INFO_ISBJ:
-        type = NULL;            /*"Subject"; */
+        type = GST_TAG_ALBUM_ARTIST;
         break;
       case GST_RIFF_INFO_ISFT:
         type = GST_TAG_ENCODER;
@@ -742,6 +784,9 @@ gst_riff_parse_info (GstElement * element,
       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,
@@ -751,19 +796,10 @@ gst_riff_parse_info (GstElement * element,
     }
 
     if (type != NULL && ptr[0] != '\0') {
-      static const gchar *env_vars[] = { "GST_AVI_TAG_ENCODING",
-        "GST_RIFF_TAG_ENCODING", "GST_TAG_ENCODING", NULL
-      };
-      gchar *val;
-
-      val = gst_tag_freeform_string_to_utf8 ((gchar *) ptr, tsize, env_vars);
+      GST_DEBUG_OBJECT (element, "mapped tag %" GST_FOURCC_FORMAT " to tag %s",
+          GST_FOURCC_ARGS (tag), type);
 
-      if (val) {
-        gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, type, val, NULL);
-        g_free (val);
-      } else {
-        GST_WARNING_OBJECT (element, "could not extract %s tag", type);
-      }
+      parse_tag_value (element, taglist, type, ptr, tsize);
     }
 
     if (tsize & 1) {
@@ -777,12 +813,13 @@ gst_riff_parse_info (GstElement * element,
   }
 
   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_tag_list_unref (taglist);
   }
-  gst_buffer_unmap (buf, data, size);
+  gst_buffer_unmap (buf, &info);
 
   return;
 }