From 8c6d2c506a102109339347e039cb38a2047fef40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 16 Jun 2008 10:59:39 +0000 Subject: [PATCH] gst/matroska/: Parse Attachments and post them as GST_TAG_IMAGE if we detect it as image and otherwise as GST_TAG_ATT... 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 | 16 +++ gst/matroska/Makefile.am | 1 + gst/matroska/matroska-demux.c | 231 +++++++++++++++++++++++++++++++--- gst/matroska/matroska-demux.h | 1 + gst/matroska/matroska-ids.c | 10 ++ gst/matroska/matroska-ids.h | 8 ++ gst/matroska/matroska.c | 2 + 7 files changed, 254 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5e20d878cd..f9a2990e11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2008-06-16 Sebastian Dröge + + * 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 * ext/speex/gstspeexenc.c: (gst_speex_enc_mode_get_type), diff --git a/gst/matroska/Makefile.am b/gst/matroska/Makefile.am index 1fa4f4bc4d..6c3f2275e8 100644 --- a/gst/matroska/Makefile.am +++ b/gst/matroska/Makefile.am @@ -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) diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index d6908e916b..2a0d0f8b54 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -1,6 +1,7 @@ /* GStreamer Matroska muxer/demuxer * (c) 2003 Ronald Bultje * (c) 2006 Tim-Philipp Müller + * (c) 2008 Sebastian Dröge * * matroska-demux.c: matroska file/stream demuxer * @@ -50,6 +51,10 @@ #include #include +#include + +#include + #ifdef HAVE_ZLIB #include #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); @@ -2467,6 +2473,173 @@ gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, return ret; } +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; } diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index 1a65286c17..c510e498b7 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -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 */ diff --git a/gst/matroska/matroska-ids.c b/gst/matroska/matroska-ids.c index 81cfd04ace..056dbf44b5 100644 --- a/gst/matroska/matroska-ids.c +++ b/gst/matroska/matroska-ids.c @@ -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 */ +} diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index 8c83d1aa20..987cbbbafb 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -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__ */ diff --git a/gst/matroska/matroska.c b/gst/matroska/matroska.c index 5156ca5766..5a74c6b521 100644 --- a/gst/matroska/matroska.c +++ b/gst/matroska/matroska.c @@ -25,10 +25,12 @@ #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); }