gst/matroska/: Parse Attachments and post them as GST_TAG_IMAGE if we detect it as...
authorSebastian Dröge <slomo@circular-chaos.org>
Mon, 16 Jun 2008 10:59:39 +0000 (10:59 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Mon, 16 Jun 2008 10:59:39 +0000 (10:59 +0000)
Original commit message from CVS:
* gst/matroska/Makefile.am:
* gst/matroska/matroska-demux.c: (gst_matroska_demux_reset),
(gst_matroska_demux_parse_attached_file),
(gst_matroska_demux_parse_attachments),
(gst_matroska_demux_parse_contents_seekentry),
(gst_matroska_demux_loop_stream_parse_id):
* gst/matroska/matroska-demux.h:
* gst/matroska/matroska-ids.c: (gst_matroska_register_tags):
* gst/matroska/matroska-ids.h:
* gst/matroska/matroska.c: (plugin_init):
Parse Attachments and post them as GST_TAG_IMAGE if we detect
it as image and otherwise as GST_TAG_ATTACHMENT. Include filename
and description of the attachments in the caps. Fixes bug #537622.

ChangeLog
gst/matroska/Makefile.am
gst/matroska/matroska-demux.c
gst/matroska/matroska-demux.h
gst/matroska/matroska-ids.c
gst/matroska/matroska-ids.h
gst/matroska/matroska.c

index 5e20d87..f9a2990 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2008-06-16  Sebastian Dröge  <slomo@circular-chaos.org>
+
+       * gst/matroska/Makefile.am:
+       * gst/matroska/matroska-demux.c: (gst_matroska_demux_reset),
+       (gst_matroska_demux_parse_attached_file),
+       (gst_matroska_demux_parse_attachments),
+       (gst_matroska_demux_parse_contents_seekentry),
+       (gst_matroska_demux_loop_stream_parse_id):
+       * gst/matroska/matroska-demux.h:
+       * gst/matroska/matroska-ids.c: (gst_matroska_register_tags):
+       * gst/matroska/matroska-ids.h:
+       * gst/matroska/matroska.c: (plugin_init):
+       Parse Attachments and post them as GST_TAG_IMAGE if we detect
+       it as image and otherwise as GST_TAG_ATTACHMENT. Include filename
+       and description of the attachments in the caps. Fixes bug #537622.
+
 2008-06-16  Wim Taymans  <wim.taymans@collabora.co.uk>
 
        * ext/speex/gstspeexenc.c: (gst_speex_enc_mode_get_type),
index 1fa4f4b..6c3f227 100644 (file)
@@ -26,6 +26,7 @@ libgstmatroska_la_LIBADD = \
        $(GST_PLUGINS_BASE_LIBS) \
        $(GST_LIBS) \
        -lgstriff-@GST_MAJORMINOR@ \
+       -lgsttag-@GST_MAJORMINOR@ \
        $(ZLIB_LIBS) \
        $(LIBM)
 libgstmatroska_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
index d6908e9..2a0d0f8 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer Matroska muxer/demuxer
  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
  * (c) 2006 Tim-Philipp Müller <tim centricular net>
+ * (c) 2008 Sebastian Dröge <slomo@circular-chaos.org>
  *
  * matroska-demux.c: matroska file/stream demuxer
  *
 #include <gst/riff/riff-ids.h>
 #include <gst/riff/riff-media.h>
 
+#include <gst/tag/tag.h>
+
+#include <gst/base/gsttypefindhelper.h>
+
 #ifdef HAVE_ZLIB
 #include <zlib.h>
 #endif
@@ -312,6 +317,7 @@ gst_matroska_demux_reset (GstElement * element)
   demux->index_parsed = FALSE;
   demux->tracks_parsed = FALSE;
   demux->segmentinfo_parsed = FALSE;
+  demux->attachments_parsed = FALSE;
 
   g_list_foreach (demux->tags_parsed, (GFunc) gst_ebml_level_free, NULL);
   g_list_free (demux->tags_parsed);
@@ -2468,6 +2474,173 @@ gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux,
 }
 
 static GstFlowReturn
+gst_matroska_demux_parse_attached_file (GstMatroskaDemux * demux,
+    gboolean prevent_eos, guint64 length, GstTagList * taglist)
+{
+  GstEbmlRead *ebml = GST_EBML_READ (demux);
+
+  guint32 id;
+
+  GstFlowReturn ret;
+
+  gchar *description = NULL;
+
+  gchar *filename = NULL;
+
+  gchar *mimetype = NULL;
+
+  guint8 *data = NULL;
+
+  guint64 datalen = 0;
+
+  GST_DEBUG_OBJECT (demux, "Parsing AttachedFile at offset %" G_GUINT64_FORMAT,
+      ebml->offset);
+
+  if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
+    return ret;
+
+  while (ret == GST_FLOW_OK) {
+    /* read all sub-entries */
+    if (prevent_eos && length == ebml->offset)
+      break;
+
+    if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK)
+      return ret;
+
+    if (demux->level_up) {
+      demux->level_up--;
+      break;
+    }
+
+    switch (id) {
+      case GST_MATROSKA_ID_FILEDESCRIPTION:
+        if (description) {
+          GST_WARNING_OBJECT (demux, "FileDescription can only appear once");
+          break;
+        }
+
+        if ((ret = gst_ebml_read_utf8 (ebml, &id, &description)) != GST_FLOW_OK)
+          return ret;
+        break;
+      case GST_MATROSKA_ID_FILENAME:
+        if (filename) {
+          GST_WARNING_OBJECT (demux, "FileName can only appear once");
+          break;
+        }
+
+        if ((ret = gst_ebml_read_utf8 (ebml, &id, &filename)) != GST_FLOW_OK)
+          return ret;
+        break;
+      case GST_MATROSKA_ID_FILEMIMETYPE:
+        if (mimetype) {
+          GST_WARNING_OBJECT (demux, "FileMimeType can only appear once");
+          break;
+        }
+
+        if ((ret = gst_ebml_read_ascii (ebml, &id, &mimetype)) != GST_FLOW_OK)
+          return ret;
+        break;
+      case GST_MATROSKA_ID_FILEDATA:
+        if (data) {
+          GST_WARNING_OBJECT (demux, "FileData can only appear once");
+          break;
+        }
+
+        if ((ret =
+                gst_ebml_read_binary (ebml, &id, &data,
+                    &datalen)) != GST_FLOW_OK)
+          return ret;
+        break;
+
+      default:
+        GST_WARNING ("Unknown entry 0x%x in AttachedFile", id);
+        /* fall through */
+      case GST_MATROSKA_ID_FILEUID:
+        ret = gst_ebml_read_skip (ebml);
+        break;
+    }
+
+    if (demux->level_up) {
+      demux->level_up--;
+      break;
+    }
+  }
+
+  if (filename && mimetype && data && datalen > 0) {
+    GstBuffer *tagbuffer;
+
+    GstCaps *caps;
+
+    GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE;
+
+    gchar *filename_lc = g_utf8_strdown (filename, -1);
+
+    GST_DEBUG_OBJECT (demux, "Creating tag for attachment with filename '%s', "
+        "mimetype '%s', description '%s', size %" G_GUINT64_FORMAT, filename,
+        mimetype, GST_STR_NULL (description), datalen);
+
+    /* TODO: better heuristics for different image types */
+    if (strstr (filename_lc, "cover")) {
+      if (strstr (filename_lc, "back"))
+        image_type = GST_TAG_IMAGE_TYPE_BACK_COVER;
+      else
+        image_type = GST_TAG_IMAGE_TYPE_FRONT_COVER;
+    } else if (g_str_has_prefix (mimetype, "image/")) {
+      image_type = GST_TAG_IMAGE_TYPE_UNDEFINED;
+    }
+    g_free (filename_lc);
+
+    /* First try to create an image tag buffer from this */
+    if (image_type != GST_TAG_IMAGE_TYPE_NONE) {
+      tagbuffer =
+          gst_tag_image_data_to_image_buffer (data, datalen, image_type);
+
+      if (!tagbuffer)
+        image_type = GST_TAG_IMAGE_TYPE_NONE;
+    }
+
+    /* if this failed create an attachment buffer */
+    if (!tagbuffer) {
+      tagbuffer = gst_buffer_new_and_alloc (datalen);
+
+      memcpy (GST_BUFFER_DATA (tagbuffer), data, datalen);
+      GST_BUFFER_SIZE (tagbuffer) = datalen;
+
+      caps = gst_type_find_helper_for_buffer (NULL, tagbuffer, NULL);
+      if (caps == NULL)
+        caps = gst_caps_new_simple (mimetype, NULL);
+      gst_buffer_set_caps (tagbuffer, caps);
+      gst_caps_unref (caps);
+    }
+
+    /* Set filename and description on the caps */
+    caps = GST_BUFFER_CAPS (tagbuffer);
+    gst_caps_set_simple (caps, "filename", G_TYPE_STRING, filename, NULL);
+    if (description)
+      gst_caps_set_simple (caps, "description", G_TYPE_STRING, description,
+          NULL);
+
+    GST_DEBUG_OBJECT (demux, "Created tag buffer with caps: %" GST_PTR_FORMAT,
+        caps);
+
+    /* and append to the tag list */
+    if (image_type != GST_TAG_IMAGE_TYPE_NONE)
+      gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_IMAGE, tagbuffer,
+          NULL);
+    else
+      gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_ATTACHMENT,
+          tagbuffer, NULL);
+  }
+
+  g_free (filename);
+  g_free (mimetype);
+  g_free (data);
+  g_free (description);
+
+  return ret;
+}
+
+static GstFlowReturn
 gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux,
     gboolean prevent_eos)
 {
@@ -2479,14 +2652,17 @@ gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux,
 
   GstFlowReturn ret = GST_FLOW_OK;
 
-  GST_WARNING_OBJECT (demux, "Parsing of attachments not implemented yet");
-
-  /* TODO: implement parsing of attachments */
+  GstTagList *taglist;
 
   if (prevent_eos) {
     length = gst_ebml_read_get_length (ebml);
   }
 
+  GST_DEBUG_OBJECT (demux, "Parsing Attachments at offset %" G_GUINT64_FORMAT,
+      ebml->offset);
+
+  taglist = gst_tag_list_new ();
+
   while (ret == GST_FLOW_OK) {
     /* We're an element that can be seeked to. If we are, then
      * we want to prevent EOS, since that'll kill us. So we cache
@@ -2503,7 +2679,14 @@ gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux,
     }
 
     switch (id) {
+      case GST_MATROSKA_ID_ATTACHEDFILE:
+        ret =
+            gst_matroska_demux_parse_attached_file (demux, prevent_eos, length,
+            taglist);
+        break;
+
       default:
+        GST_WARNING ("Unknown entry 0x%x in Attachments", id);
         ret = gst_ebml_read_skip (ebml);
         break;
     }
@@ -2514,6 +2697,16 @@ gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux,
     }
   }
 
+  if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) {
+    GST_DEBUG_OBJECT (demux, "Posting attachment tags");
+    gst_element_found_tags (GST_ELEMENT (ebml), taglist);
+  } else {
+    GST_DEBUG_OBJECT (demux, "No valid attachments found");
+    gst_tag_list_free (taglist);
+  }
+
+  demux->attachments_parsed = TRUE;
+
   return ret;
 }
 
@@ -3730,12 +3923,14 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux,
           break;
         }
         case GST_MATROSKA_ID_ATTACHMENTS:
-          if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
-            return ret;
-          if ((ret =
-                  gst_matroska_demux_parse_attachments (demux,
-                      TRUE)) != GST_FLOW_OK)
-            return ret;
+          if (!demux->attachments_parsed) {
+            if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
+              return ret;
+            if ((ret =
+                    gst_matroska_demux_parse_attachments (demux,
+                        TRUE)) != GST_FLOW_OK)
+              return ret;
+          }
           if (gst_ebml_read_get_length (ebml) == ebml->offset)
             *p_run_loop = FALSE;
           break;
@@ -3944,12 +4139,18 @@ gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux,
       /* attachments - contains files attached to the mkv container
        * like album art, etc */
     case GST_MATROSKA_ID_ATTACHMENTS:{
-      if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
-        return ret;
-      if ((ret =
-              gst_matroska_demux_parse_attachments (demux,
-                  FALSE)) != GST_FLOW_OK)
-        return ret;
+
+      if (!demux->attachments_parsed) {
+        if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK)
+          return ret;
+        if ((ret =
+                gst_matroska_demux_parse_attachments (demux,
+                    FALSE)) != GST_FLOW_OK)
+          return ret;
+      } else {
+        if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK)
+          return ret;
+      }
       break;
     }
 
index 1a65286..c510e49 100644 (file)
@@ -81,6 +81,7 @@ typedef struct _GstMatroskaDemux {
   gboolean                 index_parsed;
   gboolean                 tracks_parsed;
   gboolean                 segmentinfo_parsed;
+  gboolean                 attachments_parsed;
   GList                   *tags_parsed;
 
   /* start-of-segment */
index 81cfd04..056dbf4 100644 (file)
@@ -109,3 +109,13 @@ gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_context)
   subtitle_context->invalid_utf8 = FALSE;
   return TRUE;
 }
+
+void
+gst_matroska_register_tags (void)
+{
+  /* FIXME: Remove this when we depend on core 0.10.21 */
+  if (!gst_tag_exists (GST_TAG_ATTACHMENT))
+    gst_tag_register (GST_TAG_ATTACHMENT, GST_TAG_FLAG_META, GST_TYPE_BUFFER,
+        "attachment", "file attached to this stream", gst_tag_merge_use_first);
+  /* TODO: register other custom tags */
+}
index 8c83d1a..987cbbb 100644 (file)
@@ -563,4 +563,12 @@ gboolean gst_matroska_track_init_video_context    (GstMatroskaTrackContext ** p_
 gboolean gst_matroska_track_init_audio_context    (GstMatroskaTrackContext ** p_context);
 gboolean gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_context);
 
+
+/* FIXME: remove when we depend on core 0.10.21 */
+#ifndef GST_TAG_ATTACHMENT
+#define GST_TAG_ATTACHMENT "attachment"
+#endif
+
+void gst_matroska_register_tags (void);
+
 #endif /* __GST_MATROSKA_IDS_H__ */
index 5156ca5..5a74c6b 100644 (file)
 
 #include "matroska-demux.h"
 #include "matroska-mux.h"
+#include "matroska-ids.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
+  gst_matroska_register_tags ();
   return gst_matroska_demux_plugin_init (plugin) &&
       gst_matroska_mux_plugin_init (plugin);
 }