diff --git a/ext/soup/gstsouphttpsink.c b/ext/soup/gstsouphttpsink.c index 04af372bf9..2de51d867f 100644 --- a/ext/soup/gstsouphttpsink.c +++ b/ext/soup/gstsouphttpsink.c @@ -74,6 +74,9 @@ static void authenticate (SoupSession * session, SoupMessage * msg, SoupAuth * auth, gboolean retrying, gpointer user_data); static void callback (SoupSession * session, SoupMessage * msg, gpointer user_data); +static gboolean +gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, + const gchar * uri); enum { @@ -87,7 +90,7 @@ enum PROP_PROXY_ID, PROP_PROXY_PW, PROP_COOKIES, - PROP_SESSION, + PROP_SESSION }; #define DEFAULT_USER_AGENT "GStreamer souphttpsink " @@ -159,13 +162,11 @@ gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass) g_param_spec_boolean ("automatic-redirect", "automatic-redirect", "Automatically follow HTTP redirects (HTTP Status Code 3xx)", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -#if 0 g_object_class_install_property (gobject_class, PROP_PROXY, g_param_spec_string ("proxy", "Proxy", "HTTP proxy server URI", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -#endif g_object_class_install_property (gobject_class, PROP_USER_ID, g_param_spec_string ("user-id", "user-id", @@ -187,7 +188,9 @@ gst_soup_http_sink_class_init (GstSoupHttpSinkClass * klass) g_param_spec_object ("session", "session", "SoupSession object to use for communication", SOUP_TYPE_SESSION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - + g_object_class_install_property (gobject_class, PROP_COOKIES, + g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies", + G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } @@ -195,9 +198,7 @@ static void gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink, GstSoupHttpSinkClass * souphttpsink_class) { -#if 0 const char *proxy; -#endif souphttpsink->sinkpad = gst_pad_new_from_static_template (&gst_soup_http_sink_sink_template, @@ -215,14 +216,12 @@ gst_soup_http_sink_init (GstSoupHttpSink * souphttpsink, souphttpsink->proxy_pw = NULL; souphttpsink->prop_session = NULL; souphttpsink->timeout = 1; -#if 0 proxy = g_getenv ("http_proxy"); if (proxy && !gst_soup_http_sink_set_proxy (souphttpsink, proxy)) { GST_WARNING_OBJECT (souphttpsink, "The proxy in the http_proxy env var (\"%s\") cannot be parsed.", proxy); } -#endif gst_soup_http_sink_reset (souphttpsink); } @@ -237,6 +236,25 @@ gst_soup_http_sink_reset (GstSoupHttpSink * souphttpsink) } +static gboolean +gst_soup_http_sink_set_proxy (GstSoupHttpSink * souphttpsink, const gchar * uri) +{ + if (souphttpsink->proxy) { + soup_uri_free (souphttpsink->proxy); + souphttpsink->proxy = NULL; + } + if (g_str_has_prefix (uri, "http://")) { + souphttpsink->proxy = soup_uri_new (uri); + } else { + gchar *new_uri = g_strconcat ("http://", uri, NULL); + + souphttpsink->proxy = soup_uri_new (new_uri); + g_free (new_uri); + } + + return TRUE; +} + void gst_soup_http_sink_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) @@ -279,10 +297,31 @@ gst_soup_http_sink_set_property (GObject * object, guint property_id, g_free (souphttpsink->proxy_pw); souphttpsink->proxy_pw = g_value_dup_string (value); break; + case PROP_PROXY: + { + const gchar *proxy; + + proxy = g_value_get_string (value); + + if (proxy == NULL) { + GST_WARNING ("proxy property cannot be NULL"); + goto done; + } + if (!gst_soup_http_sink_set_proxy (souphttpsink, proxy)) { + GST_WARNING ("badly formatted proxy URI"); + goto done; + } + break; + } + case PROP_COOKIES: + g_strfreev (souphttpsink->cookies); + souphttpsink->cookies = g_strdupv (g_value_get_boxed (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } +done: g_mutex_unlock (souphttpsink->mutex); } @@ -317,7 +356,19 @@ gst_soup_http_sink_get_property (GObject * object, guint property_id, case PROP_PROXY_PW: g_value_set_string (value, souphttpsink->proxy_pw); break; + case PROP_PROXY: + if (souphttpsink->proxy == NULL) + g_value_set_static_string (value, ""); + else { + char *proxy = soup_uri_to_string (souphttpsink->proxy, FALSE); + g_value_set_string (value, proxy); + g_free (proxy); + } + break; + case PROP_COOKIES: + g_value_set_boxed (value, g_strdupv (souphttpsink->cookies)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -349,6 +400,8 @@ gst_soup_http_sink_finalize (GObject * object) g_free (souphttpsink->user_pw); g_free (souphttpsink->proxy_id); g_free (souphttpsink->proxy_pw); + if (souphttpsink->proxy) + soup_uri_free (souphttpsink->proxy); g_free (souphttpsink->location); g_cond_free (souphttpsink->cond); @@ -482,17 +535,17 @@ gst_soup_http_sink_event (GstBaseSink * sink, GstEvent * event) { GstSoupHttpSink *souphttpsink = GST_SOUP_HTTP_SINK (sink); - GST_DEBUG ("event"); + GST_DEBUG_OBJECT (souphttpsink, "event"); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - GST_DEBUG ("got eos"); + GST_DEBUG_OBJECT (souphttpsink, "got eos"); g_mutex_lock (souphttpsink->mutex); while (souphttpsink->message) { - GST_DEBUG ("waiting"); + GST_DEBUG_OBJECT (souphttpsink, "waiting"); g_cond_wait (souphttpsink->cond, souphttpsink->mutex); } g_mutex_unlock (souphttpsink->mutex); - GST_DEBUG ("finished eos"); + GST_DEBUG_OBJECT (souphttpsink, "finished eos"); } return TRUE; @@ -536,8 +589,6 @@ send_message_locked (GstSoupHttpSink * souphttpsink) souphttpsink->message = soup_message_new ("PUT", souphttpsink->location); - //soup_message_body_set_accumulate (souphttpsink->message->request_body, TRUE); - n = 0; if (souphttpsink->offset == 0) { for (g = souphttpsink->streamheader_buffers; g; g = g_list_next (g)) { @@ -579,10 +630,11 @@ send_message_locked (GstSoupHttpSink * souphttpsink) souphttpsink->sent_buffers = souphttpsink->queued_buffers; souphttpsink->queued_buffers = NULL; - GST_DEBUG ("queue message %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, + GST_DEBUG_OBJECT (souphttpsink, + "queue message %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, souphttpsink->offset, n); - soup_session_queue_message (souphttpsink->session, - souphttpsink->message, callback, souphttpsink); + soup_session_queue_message (souphttpsink->session, souphttpsink->message, + callback, souphttpsink); souphttpsink->offset += n; } @@ -631,6 +683,7 @@ gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer) gboolean wake; if (souphttpsink->status_code != 0) { + /* FIXME we should allow a moderate amount of retries. */ GST_ELEMENT_ERROR (souphttpsink, RESOURCE, WRITE, ("Could not write to HTTP URI"), ("error: %d %s", souphttpsink->status_code, @@ -646,7 +699,6 @@ gst_soup_http_sink_render (GstBaseSink * sink, GstBuffer * buffer) if (wake) { source = g_idle_source_new (); - //g_source_set_priority (source, G_PRIORITY_DEFAULT); g_source_set_callback (source, (GSourceFunc) (send_message), souphttpsink, NULL); g_source_attach (source, souphttpsink->context); diff --git a/ext/soup/gstsouphttpsink.h b/ext/soup/gstsouphttpsink.h index c7d4e49b82..f78272946e 100644 --- a/ext/soup/gstsouphttpsink.h +++ b/ext/soup/gstsouphttpsink.h @@ -62,10 +62,12 @@ struct _GstSoupHttpSink char *location; char *user_id; char *user_pw; + SoupURI *proxy; char *proxy_id; char *proxy_pw; char *user_agent; gboolean automatic_redirect; + gchar **cookies; }; diff --git a/gst/interleave/interleave.c b/gst/interleave/interleave.c index 96a43c8d61..f8e4392889 100644 --- a/gst/interleave/interleave.c +++ b/gst/interleave/interleave.c @@ -479,8 +479,13 @@ gst_interleave_request_new_pad (GstElement * element, GstPadTemplate * templ, if (templ->direction != GST_PAD_SINK) goto not_sink_pad; +#if GLIB_CHECK_VERSION(2,29,5) + channels = g_atomic_int_add (&self->channels, 1); + padnumber = g_atomic_int_add (&self->padcounter, 1); +#else channels = g_atomic_int_exchange_and_add (&self->channels, 1); padnumber = g_atomic_int_exchange_and_add (&self->padcounter, 1); +#endif pad_name = g_strdup_printf ("sink%d", padnumber); new_pad = GST_PAD_CAST (g_object_new (GST_TYPE_INTERLEAVE_PAD, diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 865127d279..129b66ce4f 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -61,8 +61,6 @@ #include -#include - #include #include "matroska-demux.h" @@ -257,8 +255,8 @@ gst_matroska_demux_init (GstMatroskaDemux * demux, /* initial stream no. */ demux->common.src = NULL; - demux->writing_app = NULL; - demux->muxing_app = NULL; + demux->common.writing_app = NULL; + demux->common.muxing_app = NULL; demux->common.index = NULL; demux->common.global_tags = NULL; @@ -378,10 +376,10 @@ gst_matroska_demux_reset (GstElement * element) demux->num_v_streams = 0; /* reset media info */ - g_free (demux->writing_app); - demux->writing_app = NULL; - g_free (demux->muxing_app); - demux->muxing_app = NULL; + g_free (demux->common.writing_app); + demux->common.writing_app = NULL; + g_free (demux->common.muxing_app); + demux->common.muxing_app = NULL; /* reset indexes */ if (demux->common.index) { @@ -397,24 +395,24 @@ gst_matroska_demux_reset (GstElement * element) /* reset timers */ demux->clock = NULL; demux->common.time_scale = 1000000; - demux->created = G_MININT64; + demux->common.created = G_MININT64; demux->common.index_parsed = FALSE; demux->tracks_parsed = FALSE; - demux->segmentinfo_parsed = FALSE; - demux->attachments_parsed = FALSE; + demux->common.segmentinfo_parsed = FALSE; + demux->common.attachments_parsed = FALSE; - g_list_foreach (demux->tags_parsed, + g_list_foreach (demux->common.tags_parsed, (GFunc) gst_matroska_demux_free_parsed_el, NULL); - g_list_free (demux->tags_parsed); - demux->tags_parsed = NULL; + g_list_free (demux->common.tags_parsed); + demux->common.tags_parsed = NULL; g_list_foreach (demux->seek_parsed, (GFunc) gst_matroska_demux_free_parsed_el, NULL); g_list_free (demux->seek_parsed); demux->seek_parsed = NULL; - gst_segment_init (&demux->segment, GST_FORMAT_TIME); + gst_segment_init (&demux->common.segment, GST_FORMAT_TIME); demux->last_stop_end = GST_CLOCK_TIME_NONE; demux->seek_block = 0; @@ -1353,7 +1351,7 @@ gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad, gst_query_set_position (query, GST_FORMAT_TIME, context->pos); else gst_query_set_position (query, GST_FORMAT_TIME, - demux->segment.last_stop); + demux->common.segment.last_stop); GST_OBJECT_UNLOCK (demux); } else if (format == GST_FORMAT_DEFAULT && context && context->default_duration) { @@ -1378,13 +1376,13 @@ gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad, if (format == GST_FORMAT_TIME) { GST_OBJECT_LOCK (demux); gst_query_set_duration (query, GST_FORMAT_TIME, - demux->segment.duration); + demux->common.segment.duration); GST_OBJECT_UNLOCK (demux); } else if (format == GST_FORMAT_DEFAULT && context && context->default_duration) { GST_OBJECT_LOCK (demux); gst_query_set_duration (query, GST_FORMAT_DEFAULT, - demux->segment.duration / context->default_duration); + demux->common.segment.duration / context->default_duration); GST_OBJECT_UNLOCK (demux); } else { GST_DEBUG_OBJECT (demux, @@ -1412,7 +1410,7 @@ gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad, } gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, - 0, demux->segment.duration); + 0, demux->common.segment.duration); res = TRUE; } GST_OBJECT_UNLOCK (demux); @@ -1532,7 +1530,7 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux, /* update the time */ gst_matroska_read_common_reset_streams (&demux->common, entry->time, TRUE); - demux->segment.last_stop = entry->time; + demux->common.segment.last_stop = entry->time; demux->seek_block = entry->block; demux->seek_first = TRUE; demux->last_stop_end = GST_CLOCK_TIME_NONE; @@ -1715,7 +1713,7 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time) /* estimate using start and current position */ GST_OBJECT_LOCK (demux); opos = demux->common.offset - demux->common.ebml_segment_start; - otime = demux->segment.last_stop; + otime = demux->common.segment.last_stop; GST_OBJECT_UNLOCK (demux); retry: @@ -1884,7 +1882,7 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, /* copy segment, we need this because we still need the old * segment when we close the current segment. */ - memcpy (&seeksegment, &demux->segment, sizeof (GstSegment)); + memcpy (&seeksegment, &demux->common.segment, sizeof (GstSegment)); if (event) { GST_DEBUG_OBJECT (demux, "configuring seek"); @@ -1975,15 +1973,16 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, gst_event_unref (demux->close_segment); demux->close_segment = gst_event_new_new_segment (TRUE, - demux->segment.rate, GST_FORMAT_TIME, demux->segment.start, - demux->segment.last_stop, demux->segment.time); + demux->common.segment.rate, GST_FORMAT_TIME, + demux->common.segment.start, demux->common.segment.last_stop, + demux->common.segment.time); GST_OBJECT_UNLOCK (demux); } GST_OBJECT_LOCK (demux); /* now update the real segment info */ GST_DEBUG_OBJECT (demux, "Committing new seek segment"); - memcpy (&demux->segment, &seeksegment, sizeof (GstSegment)); + memcpy (&demux->common.segment, &seeksegment, sizeof (GstSegment)); GST_OBJECT_UNLOCK (demux); /* update some (segment) state */ @@ -1991,11 +1990,11 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, goto seek_error; /* notify start of new segment */ - if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + if (demux->common.segment.flags & GST_SEEK_FLAG_SEGMENT) { GstMessage *msg; msg = gst_message_new_segment_start (GST_OBJECT (demux), - GST_FORMAT_TIME, demux->segment.start); + GST_FORMAT_TIME, demux->common.segment.start); gst_element_post_message (GST_ELEMENT (demux), msg); } @@ -2003,8 +2002,9 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, if (demux->new_segment) gst_event_unref (demux->new_segment); demux->new_segment = gst_event_new_new_segment_full (FALSE, - demux->segment.rate, demux->segment.applied_rate, demux->segment.format, - demux->segment.start, demux->segment.stop, demux->segment.time); + demux->common.segment.rate, demux->common.segment.applied_rate, + demux->common.segment.format, demux->common.segment.start, + demux->common.segment.stop, demux->common.segment.time); GST_OBJECT_UNLOCK (demux); /* restart our task since it might have been stopped when we did the @@ -2191,10 +2191,10 @@ gst_matroska_demux_seek_to_previous_keyframe (GstMatroskaDemux * demux) GST_DEBUG_OBJECT (demux, "segment start %" GST_TIME_FORMAT ", stream %d at %" GST_TIME_FORMAT, - GST_TIME_ARGS (demux->segment.start), stream->index, + GST_TIME_ARGS (demux->common.segment.start), stream->index, GST_TIME_ARGS (stream->from_time)); if (GST_CLOCK_TIME_IS_VALID (stream->from_time)) { - if (stream->from_time > demux->segment.start) { + if (stream->from_time > demux->common.segment.start) { GST_DEBUG_OBJECT (demux, "stream %d not finished yet", stream->index); done = FALSE; } @@ -2257,595 +2257,6 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml) return ret; } -static GstFlowReturn -gst_matroska_demux_parse_info (GstMatroskaDemux * demux, GstEbmlRead * ebml) -{ - GstFlowReturn ret = GST_FLOW_OK; - gdouble dur_f = -1.0; - guint32 id; - - DEBUG_ELEMENT_START (demux, ebml, "SegmentInfo"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "SegmentInfo", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - /* cluster timecode */ - case GST_MATROSKA_ID_TIMECODESCALE:{ - guint64 num; - - if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) - break; - - - GST_DEBUG_OBJECT (demux, "TimeCodeScale: %" G_GUINT64_FORMAT, num); - demux->common.time_scale = num; - break; - } - - case GST_MATROSKA_ID_DURATION:{ - if ((ret = gst_ebml_read_float (ebml, &id, &dur_f)) != GST_FLOW_OK) - break; - - if (dur_f <= 0.0) { - GST_WARNING_OBJECT (demux, "Invalid duration %lf", dur_f); - break; - } - - GST_DEBUG_OBJECT (demux, "Duration: %lf", dur_f); - break; - } - - case GST_MATROSKA_ID_WRITINGAPP:{ - gchar *text; - - if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (demux, "WritingApp: %s", GST_STR_NULL (text)); - demux->writing_app = text; - break; - } - - case GST_MATROSKA_ID_MUXINGAPP:{ - gchar *text; - - if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (demux, "MuxingApp: %s", GST_STR_NULL (text)); - demux->muxing_app = text; - break; - } - - case GST_MATROSKA_ID_DATEUTC:{ - gint64 time; - - if ((ret = gst_ebml_read_date (ebml, &id, &time)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (demux, "DateUTC: %" G_GINT64_FORMAT, time); - demux->created = time; - break; - } - - case GST_MATROSKA_ID_TITLE:{ - gchar *text; - GstTagList *taglist; - - if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (demux, "Title: %s", GST_STR_NULL (text)); - taglist = gst_tag_list_new (); - gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, text, - NULL); - gst_matroska_read_common_found_global_tag (&demux->common, - GST_ELEMENT_CAST (demux), taglist); - g_free (text); - break; - } - - default: - ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, - "SegmentInfo", id); - break; - - /* fall through */ - case GST_MATROSKA_ID_SEGMENTUID: - case GST_MATROSKA_ID_SEGMENTFILENAME: - case GST_MATROSKA_ID_PREVUID: - case GST_MATROSKA_ID_PREVFILENAME: - case GST_MATROSKA_ID_NEXTUID: - case GST_MATROSKA_ID_NEXTFILENAME: - case GST_MATROSKA_ID_SEGMENTFAMILY: - case GST_MATROSKA_ID_CHAPTERTRANSLATE: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - if (dur_f > 0.0) { - GstClockTime dur_u; - - dur_u = gst_gdouble_to_guint64 (dur_f * - gst_guint64_to_gdouble (demux->common.time_scale)); - if (GST_CLOCK_TIME_IS_VALID (dur_u) && dur_u <= G_MAXINT64) - gst_segment_set_duration (&demux->segment, GST_FORMAT_TIME, dur_u); - } - - DEBUG_ELEMENT_STOP (demux, ebml, "SegmentInfo", ret); - - demux->segmentinfo_parsed = TRUE; - - return ret; -} - -static GstFlowReturn -gst_matroska_demux_parse_metadata_id_simple_tag (GstMatroskaDemux * demux, - GstEbmlRead * ebml, GstTagList ** p_taglist) -{ - /* FIXME: check if there are more useful mappings */ - static const struct - { - const gchar *matroska_tagname; - const gchar *gstreamer_tagname; - } - tag_conv[] = { - { - GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, { - GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, { - GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, { - GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, { - GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, { - GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, { - GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, { - GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, { - GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, { - GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, { - GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, { - GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, { - GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, { - GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, { - GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, { - GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE} - }; - GstFlowReturn ret; - guint32 id; - gchar *value = NULL; - gchar *tag = NULL; - - DEBUG_ELEMENT_START (demux, ebml, "SimpleTag"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "SimpleTag", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - /* read all sub-entries */ - - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_TAGNAME: - g_free (tag); - tag = NULL; - ret = gst_ebml_read_ascii (ebml, &id, &tag); - GST_DEBUG_OBJECT (demux, "TagName: %s", GST_STR_NULL (tag)); - break; - - case GST_MATROSKA_ID_TAGSTRING: - g_free (value); - value = NULL; - ret = gst_ebml_read_utf8 (ebml, &id, &value); - GST_DEBUG_OBJECT (demux, "TagString: %s", GST_STR_NULL (value)); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, - "SimpleTag", id); - break; - /* fall-through */ - - case GST_MATROSKA_ID_TAGLANGUAGE: - case GST_MATROSKA_ID_TAGDEFAULT: - case GST_MATROSKA_ID_TAGBINARY: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (demux, ebml, "SimpleTag", ret); - - if (tag && value) { - guint i; - - for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) { - const gchar *tagname_gst = tag_conv[i].gstreamer_tagname; - - const gchar *tagname_mkv = tag_conv[i].matroska_tagname; - - if (strcmp (tagname_mkv, tag) == 0) { - GValue dest = { 0, }; - GType dest_type = gst_tag_get_type (tagname_gst); - - /* Ensure that any date string is complete */ - if (dest_type == GST_TYPE_DATE) { - guint year = 1901, month = 1, day = 1; - - /* Dates can be yyyy-MM-dd, yyyy-MM or yyyy, but we need - * the first type */ - if (sscanf (value, "%04u-%02u-%02u", &year, &month, &day) != 0) { - g_free (value); - value = g_strdup_printf ("%04u-%02u-%02u", year, month, day); - } - } - - g_value_init (&dest, dest_type); - if (gst_value_deserialize (&dest, value)) { - gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND, - tagname_gst, &dest, NULL); - } else { - GST_WARNING_OBJECT (demux, "Can't transform tag '%s' with " - "value '%s' to target type '%s'", tag, value, - g_type_name (dest_type)); - } - g_value_unset (&dest); - break; - } - } - } - - g_free (tag); - g_free (value); - - return ret; -} - -static GstFlowReturn -gst_matroska_demux_parse_metadata_id_tag (GstMatroskaDemux * demux, - GstEbmlRead * ebml, GstTagList ** p_taglist) -{ - guint32 id; - GstFlowReturn ret; - - DEBUG_ELEMENT_START (demux, ebml, "Tag"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "Tag", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - /* read all sub-entries */ - - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_SIMPLETAG: - ret = gst_matroska_demux_parse_metadata_id_simple_tag (demux, ebml, - p_taglist); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, - "Tag", id); - break; - } - } - - DEBUG_ELEMENT_STOP (demux, ebml, "Tag", ret); - - return ret; -} - -static GstFlowReturn -gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, GstEbmlRead * ebml) -{ - GstTagList *taglist; - GstFlowReturn ret = GST_FLOW_OK; - guint32 id; - GList *l; - guint64 curpos; - - curpos = gst_ebml_read_get_pos (ebml); - - /* Make sure we don't parse a tags element twice and - * post it's tags twice */ - curpos = gst_ebml_read_get_pos (ebml); - for (l = demux->tags_parsed; l; l = l->next) { - guint64 *pos = l->data; - - if (*pos == curpos) { - GST_DEBUG_OBJECT (demux, "Skipping already parsed Tags at offset %" - G_GUINT64_FORMAT, curpos); - return GST_FLOW_OK; - } - } - - demux->tags_parsed = - g_list_prepend (demux->tags_parsed, g_slice_new (guint64)); - *((guint64 *) demux->tags_parsed->data) = curpos; - /* fall-through */ - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "Tags", ret); - return ret; - } - - taglist = gst_tag_list_new (); - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_TAG: - ret = gst_matroska_demux_parse_metadata_id_tag (demux, ebml, &taglist); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, - "Tags", id); - break; - /* FIXME: Use to limit the tags to specific pads */ - case GST_MATROSKA_ID_TARGETS: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (demux, ebml, "Tags", ret); - - gst_matroska_read_common_found_global_tag (&demux->common, - GST_ELEMENT_CAST (demux), taglist); - - return ret; -} - -static GstFlowReturn -gst_matroska_demux_parse_attached_file (GstMatroskaDemux * demux, - GstEbmlRead * ebml, GstTagList * taglist) -{ - guint32 id; - GstFlowReturn ret; - gchar *description = NULL; - gchar *filename = NULL; - gchar *mimetype = NULL; - guint8 *data = NULL; - guint64 datalen = 0; - - DEBUG_ELEMENT_START (demux, ebml, "AttachedFile"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "AttachedFile", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - /* read all sub-entries */ - - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_FILEDESCRIPTION: - if (description) { - GST_WARNING_OBJECT (demux, "FileDescription can only appear once"); - break; - } - - ret = gst_ebml_read_utf8 (ebml, &id, &description); - GST_DEBUG_OBJECT (demux, "FileDescription: %s", - GST_STR_NULL (description)); - break; - case GST_MATROSKA_ID_FILENAME: - if (filename) { - GST_WARNING_OBJECT (demux, "FileName can only appear once"); - break; - } - - ret = gst_ebml_read_utf8 (ebml, &id, &filename); - - GST_DEBUG_OBJECT (demux, "FileName: %s", GST_STR_NULL (filename)); - break; - case GST_MATROSKA_ID_FILEMIMETYPE: - if (mimetype) { - GST_WARNING_OBJECT (demux, "FileMimeType can only appear once"); - break; - } - - ret = gst_ebml_read_ascii (ebml, &id, &mimetype); - GST_DEBUG_OBJECT (demux, "FileMimeType: %s", GST_STR_NULL (mimetype)); - break; - case GST_MATROSKA_ID_FILEDATA: - if (data) { - GST_WARNING_OBJECT (demux, "FileData can only appear once"); - break; - } - - ret = gst_ebml_read_binary (ebml, &id, &data, &datalen); - GST_DEBUG_OBJECT (demux, "FileData of size %" G_GUINT64_FORMAT, - datalen); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, - "AttachedFile", id); - break; - case GST_MATROSKA_ID_FILEUID: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (demux, ebml, "AttachedFile", ret); - - if (filename && mimetype && data && datalen > 0) { - GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE; - GstBuffer *tagbuffer = NULL; - GstCaps *caps; - 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/") || - g_str_has_suffix (filename_lc, "png") || - g_str_has_suffix (filename_lc, "jpg") || - g_str_has_suffix (filename_lc, "jpeg") || - g_str_has_suffix (filename_lc, "gif") || - g_str_has_suffix (filename_lc, "bmp")) { - 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 attachment 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, - GstEbmlRead * ebml) -{ - guint32 id; - GstFlowReturn ret = GST_FLOW_OK; - GstTagList *taglist; - - DEBUG_ELEMENT_START (demux, ebml, "Attachments"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "Attachments", ret); - return ret; - } - - taglist = gst_tag_list_new (); - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_ATTACHEDFILE: - ret = gst_matroska_demux_parse_attached_file (demux, ebml, taglist); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&demux->common, ebml, - "Attachments", id); - break; - } - } - DEBUG_ELEMENT_STOP (demux, ebml, "Attachments", ret); - - if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) { - GST_DEBUG_OBJECT (demux, "Storing attachment tags"); - gst_matroska_read_common_found_global_tag (&demux->common, - GST_ELEMENT_CAST (demux), taglist); - } else { - GST_DEBUG_OBJECT (demux, "No valid attachments found"); - gst_tag_list_free (taglist); - } - - demux->attachments_parsed = TRUE; - - return ret; -} - -static GstFlowReturn -gst_matroska_demux_parse_chapters (GstMatroskaDemux * demux, GstEbmlRead * ebml) -{ - guint32 id; - GstFlowReturn ret = GST_FLOW_OK; - - GST_WARNING_OBJECT (demux, "Parsing of chapters not implemented yet"); - - /* TODO: implement parsing of chapters */ - - DEBUG_ELEMENT_START (demux, ebml, "Chapters"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (demux, ebml, "Chapters", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - default: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (demux, ebml, "Chapters", ret); - return ret; -} - /* * Read signed/unsigned "EBML" numbers. * Return: number of bytes processed. @@ -2920,7 +2331,7 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) GST_OBJECT_LOCK (demux); GST_LOG_OBJECT (demux, "Sync to %" GST_TIME_FORMAT, - GST_TIME_ARGS (demux->segment.last_stop)); + GST_TIME_ARGS (demux->common.segment.last_stop)); g_assert (demux->common.num_streams == demux->common.src->len); for (stream_nr = 0; stream_nr < demux->common.src->len; stream_nr++) { @@ -2940,15 +2351,15 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) /* does it lag? 0.5 seconds is a random threshold... * lag need only be considered if we have advanced into requested segment */ if (GST_CLOCK_TIME_IS_VALID (context->pos) && - GST_CLOCK_TIME_IS_VALID (demux->segment.last_stop) && - demux->segment.last_stop > demux->segment.start && - context->pos + (GST_SECOND / 2) < demux->segment.last_stop) { + GST_CLOCK_TIME_IS_VALID (demux->common.segment.last_stop) && + demux->common.segment.last_stop > demux->common.segment.start && + context->pos + (GST_SECOND / 2) < demux->common.segment.last_stop) { gint64 new_start; GstEvent *event; - new_start = demux->segment.last_stop - (GST_SECOND / 2); - if (GST_CLOCK_TIME_IS_VALID (demux->segment.stop)) - new_start = MIN (new_start, demux->segment.stop); + new_start = demux->common.segment.last_stop - (GST_SECOND / 2); + if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)) + new_start = MIN (new_start, demux->common.segment.stop); GST_DEBUG_OBJECT (demux, "Synchronizing stream %d with others by advancing time " "from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, stream_nr, @@ -2957,8 +2368,9 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) context->pos = new_start; /* advance stream time */ - event = gst_event_new_new_segment (TRUE, demux->segment.rate, - demux->segment.format, new_start, demux->segment.stop, new_start); + event = gst_event_new_new_segment (TRUE, demux->common.segment.rate, + demux->common.segment.format, new_start, demux->common.segment.stop, + new_start); GST_OBJECT_UNLOCK (demux); gst_pad_push_event (context->pad, event); GST_OBJECT_LOCK (demux); @@ -3818,13 +3230,14 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, "generating segment starting at %" GST_TIME_FORMAT, GST_TIME_ARGS (lace_time)); /* pretend we seeked here */ - gst_segment_set_seek (&demux->segment, demux->segment.rate, + gst_segment_set_seek (&demux->common.segment, demux->common.segment.rate, GST_FORMAT_TIME, 0, GST_SEEK_TYPE_SET, lace_time, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE, NULL); /* now convey our segment notion downstream */ gst_matroska_demux_send_event (demux, gst_event_new_new_segment (FALSE, - demux->segment.rate, demux->segment.format, demux->segment.start, - demux->segment.stop, demux->segment.start)); + demux->common.segment.rate, demux->common.segment.format, + demux->common.segment.start, demux->common.segment.stop, + demux->common.segment.start)); demux->need_newsegment = FALSE; } @@ -3868,7 +3281,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, will instad skip until the next keyframe. */ if (GST_CLOCK_TIME_IS_VALID (lace_time) && stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && - stream->index_table && demux->segment.rate > 0.0) { + stream->index_table && demux->common.segment.rate > 0.0) { GstMatroskaTrackVideoContext *videocontext = (GstMatroskaTrackVideoContext *) stream; GstClockTime earliest_time; @@ -3877,7 +3290,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, GST_OBJECT_LOCK (demux); earliest_time = videocontext->earliest_time; GST_OBJECT_UNLOCK (demux); - earliest_stream_time = gst_segment_to_position (&demux->segment, + earliest_stream_time = gst_segment_to_position (&demux->common.segment, GST_FORMAT_TIME, earliest_time); if (GST_CLOCK_TIME_IS_VALID (lace_time) && @@ -3925,11 +3338,11 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, GstClockTime last_stop_end; /* Check if this stream is after segment stop */ - if (GST_CLOCK_TIME_IS_VALID (demux->segment.stop) && - lace_time >= demux->segment.stop) { + if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) && + lace_time >= demux->common.segment.stop) { GST_DEBUG_OBJECT (demux, "Stream %d after segment stop %" GST_TIME_FORMAT, stream->index, - GST_TIME_ARGS (demux->segment.stop)); + GST_TIME_ARGS (demux->common.segment.stop)); gst_buffer_unref (sub); goto eos; } @@ -3943,18 +3356,18 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, /* handle gaps, e.g. non-zero start-time, or an cue index entry * that landed us with timestamps not quite intended */ GST_OBJECT_LOCK (demux); - if (GST_CLOCK_TIME_IS_VALID (demux->segment.last_stop) && - demux->segment.rate > 0.0) { + if (GST_CLOCK_TIME_IS_VALID (demux->common.segment.last_stop) && + demux->common.segment.rate > 0.0) { GstClockTimeDiff diff; GstEvent *event1, *event2; /* only send newsegments with increasing start times, * otherwise if these go back and forth downstream (sinks) increase * accumulated time and running_time */ - diff = GST_CLOCK_DIFF (demux->segment.last_stop, lace_time); - if (diff > 2 * GST_SECOND && lace_time > demux->segment.start && - (!GST_CLOCK_TIME_IS_VALID (demux->segment.stop) || - lace_time < demux->segment.stop)) { + diff = GST_CLOCK_DIFF (demux->common.segment.last_stop, lace_time); + if (diff > 2 * GST_SECOND && lace_time > demux->common.segment.start + && (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop) + || lace_time < demux->common.segment.stop)) { GST_DEBUG_OBJECT (demux, "Gap of %" G_GINT64_FORMAT " ns detected in" "stream %d (%" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT "). " @@ -3964,29 +3377,33 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, /* send newsegment events such that the gap is not accounted in * accum time, hence running_time */ /* close ahead of gap */ - event1 = gst_event_new_new_segment (TRUE, demux->segment.rate, - demux->segment.format, demux->segment.last_stop, - demux->segment.last_stop, demux->segment.last_stop); + event1 = gst_event_new_new_segment (TRUE, + demux->common.segment.rate, + demux->common.segment.format, + demux->common.segment.last_stop, + demux->common.segment.last_stop, + demux->common.segment.last_stop); /* skip gap */ - event2 = gst_event_new_new_segment (FALSE, demux->segment.rate, - demux->segment.format, lace_time, demux->segment.stop, - lace_time); + event2 = gst_event_new_new_segment (FALSE, + demux->common.segment.rate, + demux->common.segment.format, lace_time, + demux->common.segment.stop, lace_time); GST_OBJECT_UNLOCK (demux); gst_matroska_demux_send_event (demux, event1); gst_matroska_demux_send_event (demux, event2); GST_OBJECT_LOCK (demux); /* align segment view with downstream, * prevents double-counting accum when closing segment */ - gst_segment_set_newsegment (&demux->segment, FALSE, - demux->segment.rate, demux->segment.format, lace_time, - demux->segment.stop, lace_time); - demux->segment.last_stop = lace_time; + gst_segment_set_newsegment (&demux->common.segment, FALSE, + demux->common.segment.rate, demux->common.segment.format, + lace_time, demux->common.segment.stop, lace_time); + demux->common.segment.last_stop = lace_time; } } - if (!GST_CLOCK_TIME_IS_VALID (demux->segment.last_stop) - || demux->segment.last_stop < lace_time) { - demux->segment.last_stop = lace_time; + if (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.last_stop) + || demux->common.segment.last_stop < lace_time) { + demux->common.segment.last_stop = lace_time; } GST_OBJECT_UNLOCK (demux); @@ -4001,9 +3418,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, demux->last_stop_end = last_stop_end; GST_OBJECT_LOCK (demux); - if (demux->segment.duration == -1 || - demux->segment.duration < lace_time) { - gst_segment_set_duration (&demux->segment, GST_FORMAT_TIME, + if (demux->common.segment.duration == -1 || + demux->common.segment.duration < lace_time) { + gst_segment_set_duration (&demux->common.segment, GST_FORMAT_TIME, last_stop_end); GST_OBJECT_UNLOCK (demux); gst_element_post_message (GST_ELEMENT_CAST (demux), @@ -4062,8 +3479,9 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, } ret = gst_pad_push (stream->pad, sub); - if (demux->segment.rate < 0) { - if (lace_time > demux->segment.stop && ret == GST_FLOW_UNEXPECTED) { + if (demux->common.segment.rate < 0) { + if (lace_time > demux->common.segment.stop + && ret == GST_FLOW_UNEXPECTED) { /* In reverse playback we can get a GST_FLOW_UNEXPECTED when * we are at the end of the segment, so we just need to jump * back to the previous section. */ @@ -4607,9 +4025,10 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, case GST_MATROSKA_READ_STATE_SEEK: switch (id) { case GST_MATROSKA_ID_SEGMENTINFO: - if (!demux->segmentinfo_parsed) { + if (!demux->common.segmentinfo_parsed) { GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); - ret = gst_matroska_demux_parse_info (demux, &ebml); + ret = gst_matroska_read_common_parse_info (&demux->common, + GST_ELEMENT_CAST (demux), &ebml); } else { GST_READ_CHECK (gst_matroska_demux_flush (demux, read)); } @@ -4643,8 +4062,8 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, gst_matroska_demux_send_event (demux, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, - (demux->segment.duration > - 0) ? demux->segment.duration : -1, 0)); + (demux->common.segment.duration > + 0) ? demux->common.segment.duration : -1, 0)); } demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = demux->common.offset; @@ -4707,20 +4126,22 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, DEBUG_ELEMENT_STOP (demux, &ebml, "SimpleBlock", ret); break; case GST_MATROSKA_ID_ATTACHMENTS: - if (!demux->attachments_parsed) { + if (!demux->common.attachments_parsed) { GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); - ret = gst_matroska_demux_parse_attachments (demux, &ebml); + ret = gst_matroska_read_common_parse_attachments (&demux->common, + GST_ELEMENT_CAST (demux), &ebml); } else { GST_READ_CHECK (gst_matroska_demux_flush (demux, read)); } break; case GST_MATROSKA_ID_TAGS: GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); - ret = gst_matroska_demux_parse_metadata (demux, &ebml); + ret = gst_matroska_read_common_parse_metadata (&demux->common, + GST_ELEMENT_CAST (demux), &ebml); break; case GST_MATROSKA_ID_CHAPTERS: GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); - ret = gst_matroska_demux_parse_chapters (demux, &ebml); + ret = gst_matroska_read_common_parse_chapters (&demux->common, &ebml); break; case GST_MATROSKA_ID_SEEKHEAD: GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); @@ -4892,7 +4313,7 @@ next: /* ERRORS */ eos: { - if (demux->segment.rate < 0.0) { + if (demux->common.segment.rate < 0.0) { ret = gst_matroska_demux_seek_to_previous_keyframe (demux); if (ret == GST_FLOW_OK) return; @@ -4914,22 +4335,22 @@ pause: /* Close the segment, i.e. update segment stop with the duration * if no stop was set */ if (GST_CLOCK_TIME_IS_VALID (demux->last_stop_end) && - !GST_CLOCK_TIME_IS_VALID (demux->segment.stop)) { + !GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)) { GstEvent *event = - gst_event_new_new_segment_full (TRUE, demux->segment.rate, - demux->segment.applied_rate, demux->segment.format, - demux->segment.start, - MAX (demux->last_stop_end, demux->segment.start), - demux->segment.time); + gst_event_new_new_segment_full (TRUE, demux->common.segment.rate, + demux->common.segment.applied_rate, demux->common.segment.format, + demux->common.segment.start, + MAX (demux->last_stop_end, demux->common.segment.start), + demux->common.segment.time); gst_matroska_demux_send_event (demux, event); } - if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) { + if (demux->common.segment.flags & GST_SEEK_FLAG_SEGMENT) { gint64 stop; /* for segment playback we need to post when (in stream time) * we stopped, this is either stop (when set) or the duration. */ - if ((stop = demux->segment.stop) == -1) + if ((stop = demux->common.segment.stop) == -1) stop = demux->last_stop_end; GST_LOG_OBJECT (demux, "Sending segment done, at end of segment"); @@ -5074,12 +4495,12 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstEvent * event) demux->common.offset = start; /* do not know where we are; * need to come across a cluster and generate newsegment */ - demux->segment.last_stop = GST_CLOCK_TIME_NONE; + demux->common.segment.last_stop = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = 0; demux->need_newsegment = TRUE; /* but keep some of the upstream segment */ - demux->segment.rate = rate; + demux->common.segment.rate = rate; GST_OBJECT_UNLOCK (demux); exit: /* chain will send initial newsegment after pads have been added, @@ -5109,7 +4530,7 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstEvent * event) GST_OBJECT_LOCK (demux); gst_matroska_read_common_reset_streams (&demux->common, GST_CLOCK_TIME_NONE, TRUE); - demux->segment.last_stop = GST_CLOCK_TIME_NONE; + demux->common.segment.last_stop = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = 0; GST_OBJECT_UNLOCK (demux); diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index 109f301736..c9ab2fc8e1 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -55,11 +55,6 @@ typedef struct _GstMatroskaDemux { guint num_a_streams; guint num_t_streams; - /* metadata */ - gchar *muxing_app; - gchar *writing_app; - gint64 created; - /* state */ gboolean streaming; guint level_up; @@ -68,16 +63,12 @@ typedef struct _GstMatroskaDemux { /* did we parse cues/tracks/segmentinfo already? */ gboolean tracks_parsed; - gboolean segmentinfo_parsed; - gboolean attachments_parsed; - GList *tags_parsed; GList *seek_parsed; /* cluster positions (optional) */ GArray *clusters; /* keeping track of playback position */ - GstSegment segment; gboolean segment_running; GstClockTime last_stop_end; diff --git a/gst/matroska/matroska-parse.c b/gst/matroska/matroska-parse.c index 177a619434..8f8da64fcc 100644 --- a/gst/matroska/matroska-parse.c +++ b/gst/matroska/matroska-parse.c @@ -61,8 +61,6 @@ #include -#include - #include #include "matroska-parse.h" @@ -226,8 +224,8 @@ gst_matroska_parse_init (GstMatroskaParse * parse, /* initial stream no. */ parse->common.src = NULL; - parse->writing_app = NULL; - parse->muxing_app = NULL; + parse->common.writing_app = NULL; + parse->common.muxing_app = NULL; parse->common.index = NULL; parse->common.global_tags = NULL; @@ -306,10 +304,10 @@ gst_matroska_parse_reset (GstElement * element) parse->num_v_streams = 0; /* reset media info */ - g_free (parse->writing_app); - parse->writing_app = NULL; - g_free (parse->muxing_app); - parse->muxing_app = NULL; + g_free (parse->common.writing_app); + parse->common.writing_app = NULL; + g_free (parse->common.muxing_app); + parse->common.muxing_app = NULL; /* reset indexes */ if (parse->common.index) { @@ -320,24 +318,24 @@ gst_matroska_parse_reset (GstElement * element) /* reset timers */ parse->clock = NULL; parse->common.time_scale = 1000000; - parse->created = G_MININT64; + parse->common.created = G_MININT64; parse->common.index_parsed = FALSE; parse->tracks_parsed = FALSE; - parse->segmentinfo_parsed = FALSE; - parse->attachments_parsed = FALSE; + parse->common.segmentinfo_parsed = FALSE; + parse->common.attachments_parsed = FALSE; - g_list_foreach (parse->tags_parsed, + g_list_foreach (parse->common.tags_parsed, (GFunc) gst_matroska_parse_free_parsed_el, NULL); - g_list_free (parse->tags_parsed); - parse->tags_parsed = NULL; + g_list_free (parse->common.tags_parsed); + parse->common.tags_parsed = NULL; g_list_foreach (parse->seek_parsed, (GFunc) gst_matroska_parse_free_parsed_el, NULL); g_list_free (parse->seek_parsed); parse->seek_parsed = NULL; - gst_segment_init (&parse->segment, GST_FORMAT_TIME); + gst_segment_init (&parse->common.segment, GST_FORMAT_TIME); parse->last_stop_end = GST_CLOCK_TIME_NONE; parse->seek_block = 0; @@ -1111,7 +1109,7 @@ gst_matroska_parse_query (GstMatroskaParse * parse, GstPad * pad, gst_query_set_position (query, GST_FORMAT_TIME, context->pos); else gst_query_set_position (query, GST_FORMAT_TIME, - parse->segment.last_stop); + parse->common.segment.last_stop); GST_OBJECT_UNLOCK (parse); } else if (format == GST_FORMAT_DEFAULT && context && context->default_duration) { @@ -1136,13 +1134,13 @@ gst_matroska_parse_query (GstMatroskaParse * parse, GstPad * pad, if (format == GST_FORMAT_TIME) { GST_OBJECT_LOCK (parse); gst_query_set_duration (query, GST_FORMAT_TIME, - parse->segment.duration); + parse->common.segment.duration); GST_OBJECT_UNLOCK (parse); } else if (format == GST_FORMAT_DEFAULT && context && context->default_duration) { GST_OBJECT_LOCK (parse); gst_query_set_duration (query, GST_FORMAT_DEFAULT, - parse->segment.duration / context->default_duration); + parse->common.segment.duration / context->default_duration); GST_OBJECT_UNLOCK (parse); } else { GST_DEBUG_OBJECT (parse, @@ -1165,7 +1163,7 @@ gst_matroska_parse_query (GstMatroskaParse * parse, GstPad * pad, seekable = parse->seekable; gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, - 0, parse->segment.duration); + 0, parse->common.segment.duration); res = TRUE; } break; @@ -1354,7 +1352,7 @@ gst_matroska_parse_handle_seek_event (GstMatroskaParse * parse, /* copy segment, we need this because we still need the old * segment when we close the current segment. */ - memcpy (&seeksegment, &parse->segment, sizeof (GstSegment)); + memcpy (&seeksegment, &parse->common.segment, sizeof (GstSegment)); if (event) { GST_DEBUG_OBJECT (parse, "configuring seek"); @@ -1559,594 +1557,6 @@ gst_matroska_parse_parse_tracks (GstMatroskaParse * parse, GstEbmlRead * ebml) return ret; } -static GstFlowReturn -gst_matroska_parse_parse_info (GstMatroskaParse * parse, GstEbmlRead * ebml) -{ - GstFlowReturn ret = GST_FLOW_OK; - gdouble dur_f = -1.0; - guint32 id; - - DEBUG_ELEMENT_START (parse, ebml, "SegmentInfo"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "SegmentInfo", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - /* cluster timecode */ - case GST_MATROSKA_ID_TIMECODESCALE:{ - guint64 num; - - if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) - break; - - - GST_DEBUG_OBJECT (parse, "TimeCodeScale: %" G_GUINT64_FORMAT, num); - parse->common.time_scale = num; - break; - } - - case GST_MATROSKA_ID_DURATION:{ - if ((ret = gst_ebml_read_float (ebml, &id, &dur_f)) != GST_FLOW_OK) - break; - - if (dur_f <= 0.0) { - GST_WARNING_OBJECT (parse, "Invalid duration %lf", dur_f); - break; - } - - GST_DEBUG_OBJECT (parse, "Duration: %lf", dur_f); - break; - } - - case GST_MATROSKA_ID_WRITINGAPP:{ - gchar *text; - - if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (parse, "WritingApp: %s", GST_STR_NULL (text)); - parse->writing_app = text; - break; - } - - case GST_MATROSKA_ID_MUXINGAPP:{ - gchar *text; - - if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (parse, "MuxingApp: %s", GST_STR_NULL (text)); - parse->muxing_app = text; - break; - } - - case GST_MATROSKA_ID_DATEUTC:{ - gint64 time; - - if ((ret = gst_ebml_read_date (ebml, &id, &time)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (parse, "DateUTC: %" G_GINT64_FORMAT, time); - parse->created = time; - break; - } - - case GST_MATROSKA_ID_TITLE:{ - gchar *text; - GstTagList *taglist; - - if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) - break; - - GST_DEBUG_OBJECT (parse, "Title: %s", GST_STR_NULL (text)); - taglist = gst_tag_list_new (); - gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, text, - NULL); - gst_matroska_read_common_found_global_tag (&parse->common, - GST_ELEMENT_CAST (parse), taglist); - g_free (text); - break; - } - - default: - ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, - "SegmentInfo", id); - break; - - /* fall through */ - case GST_MATROSKA_ID_SEGMENTUID: - case GST_MATROSKA_ID_SEGMENTFILENAME: - case GST_MATROSKA_ID_PREVUID: - case GST_MATROSKA_ID_PREVFILENAME: - case GST_MATROSKA_ID_NEXTUID: - case GST_MATROSKA_ID_NEXTFILENAME: - case GST_MATROSKA_ID_SEGMENTFAMILY: - case GST_MATROSKA_ID_CHAPTERTRANSLATE: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - if (dur_f > 0.0) { - GstClockTime dur_u; - - dur_u = gst_gdouble_to_guint64 (dur_f * - gst_guint64_to_gdouble (parse->common.time_scale)); - if (GST_CLOCK_TIME_IS_VALID (dur_u) && dur_u <= G_MAXINT64) - gst_segment_set_duration (&parse->segment, GST_FORMAT_TIME, dur_u); - } - - DEBUG_ELEMENT_STOP (parse, ebml, "SegmentInfo", ret); - - parse->segmentinfo_parsed = TRUE; - - return ret; -} - -static GstFlowReturn -gst_matroska_parse_parse_metadata_id_simple_tag (GstMatroskaParse * parse, - GstEbmlRead * ebml, GstTagList ** p_taglist) -{ - /* FIXME: check if there are more useful mappings */ - struct - { - const gchar *matroska_tagname; - const gchar *gstreamer_tagname; - } - tag_conv[] = { - { - GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, { - GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, { - GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, { - GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, { - GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, { - GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, { - GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, { - GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, { - GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, { - GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, { - GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, { - GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, { - GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, { - GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, { - GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE} - }; - GstFlowReturn ret; - guint32 id; - gchar *value = NULL; - gchar *tag = NULL; - - DEBUG_ELEMENT_START (parse, ebml, "SimpleTag"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "SimpleTag", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - /* read all sub-entries */ - - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_TAGNAME: - g_free (tag); - tag = NULL; - ret = gst_ebml_read_ascii (ebml, &id, &tag); - GST_DEBUG_OBJECT (parse, "TagName: %s", GST_STR_NULL (tag)); - break; - - case GST_MATROSKA_ID_TAGSTRING: - g_free (value); - value = NULL; - ret = gst_ebml_read_utf8 (ebml, &id, &value); - GST_DEBUG_OBJECT (parse, "TagString: %s", GST_STR_NULL (value)); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, - "SimpleTag", id); - break; - /* fall-through */ - - case GST_MATROSKA_ID_TAGLANGUAGE: - case GST_MATROSKA_ID_TAGDEFAULT: - case GST_MATROSKA_ID_TAGBINARY: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "SimpleTag", ret); - - if (tag && value) { - guint i; - - for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) { - const gchar *tagname_gst = tag_conv[i].gstreamer_tagname; - - const gchar *tagname_mkv = tag_conv[i].matroska_tagname; - - if (strcmp (tagname_mkv, tag) == 0) { - GValue dest = { 0, }; - GType dest_type = gst_tag_get_type (tagname_gst); - - /* Ensure that any date string is complete */ - if (dest_type == GST_TYPE_DATE) { - guint year = 1901, month = 1, day = 1; - - /* Dates can be yyyy-MM-dd, yyyy-MM or yyyy, but we need - * the first type */ - if (sscanf (value, "%04u-%02u-%02u", &year, &month, &day) != 0) { - g_free (value); - value = g_strdup_printf ("%04u-%02u-%02u", year, month, day); - } - } - - g_value_init (&dest, dest_type); - if (gst_value_deserialize (&dest, value)) { - gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND, - tagname_gst, &dest, NULL); - } else { - GST_WARNING_OBJECT (parse, "Can't transform tag '%s' with " - "value '%s' to target type '%s'", tag, value, - g_type_name (dest_type)); - } - g_value_unset (&dest); - break; - } - } - } - - g_free (tag); - g_free (value); - - return ret; -} - -static GstFlowReturn -gst_matroska_parse_parse_metadata_id_tag (GstMatroskaParse * parse, - GstEbmlRead * ebml, GstTagList ** p_taglist) -{ - guint32 id; - GstFlowReturn ret; - - DEBUG_ELEMENT_START (parse, ebml, "Tag"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "Tag", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - /* read all sub-entries */ - - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_SIMPLETAG: - ret = gst_matroska_parse_parse_metadata_id_simple_tag (parse, ebml, - p_taglist); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, - "Tag", id); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "Tag", ret); - - return ret; -} - -static GstFlowReturn -gst_matroska_parse_parse_metadata (GstMatroskaParse * parse, GstEbmlRead * ebml) -{ - GstTagList *taglist; - GstFlowReturn ret = GST_FLOW_OK; - guint32 id; - GList *l; - guint64 curpos; - - curpos = gst_ebml_read_get_pos (ebml); - - /* Make sure we don't parse a tags element twice and - * post it's tags twice */ - curpos = gst_ebml_read_get_pos (ebml); - for (l = parse->tags_parsed; l; l = l->next) { - guint64 *pos = l->data; - - if (*pos == curpos) { - GST_DEBUG_OBJECT (parse, "Skipping already parsed Tags at offset %" - G_GUINT64_FORMAT, curpos); - return GST_FLOW_OK; - } - } - - parse->tags_parsed = - g_list_prepend (parse->tags_parsed, g_slice_new (guint64)); - *((guint64 *) parse->tags_parsed->data) = curpos; - /* fall-through */ - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "Tags", ret); - return ret; - } - - taglist = gst_tag_list_new (); - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_TAG: - ret = gst_matroska_parse_parse_metadata_id_tag (parse, ebml, &taglist); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, - "Tags", id); - break; - /* FIXME: Use to limit the tags to specific pads */ - case GST_MATROSKA_ID_TARGETS: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "Tags", ret); - - gst_matroska_read_common_found_global_tag (&parse->common, - GST_ELEMENT_CAST (parse), taglist); - - return ret; -} - -static GstFlowReturn -gst_matroska_parse_parse_attached_file (GstMatroskaParse * parse, - GstEbmlRead * ebml, GstTagList * taglist) -{ - guint32 id; - GstFlowReturn ret; - gchar *description = NULL; - gchar *filename = NULL; - gchar *mimetype = NULL; - guint8 *data = NULL; - guint64 datalen = 0; - - DEBUG_ELEMENT_START (parse, ebml, "AttachedFile"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "AttachedFile", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - /* read all sub-entries */ - - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_FILEDESCRIPTION: - if (description) { - GST_WARNING_OBJECT (parse, "FileDescription can only appear once"); - break; - } - - ret = gst_ebml_read_utf8 (ebml, &id, &description); - GST_DEBUG_OBJECT (parse, "FileDescription: %s", - GST_STR_NULL (description)); - break; - case GST_MATROSKA_ID_FILENAME: - if (filename) { - GST_WARNING_OBJECT (parse, "FileName can only appear once"); - break; - } - - ret = gst_ebml_read_utf8 (ebml, &id, &filename); - - GST_DEBUG_OBJECT (parse, "FileName: %s", GST_STR_NULL (filename)); - break; - case GST_MATROSKA_ID_FILEMIMETYPE: - if (mimetype) { - GST_WARNING_OBJECT (parse, "FileMimeType can only appear once"); - break; - } - - ret = gst_ebml_read_ascii (ebml, &id, &mimetype); - GST_DEBUG_OBJECT (parse, "FileMimeType: %s", GST_STR_NULL (mimetype)); - break; - case GST_MATROSKA_ID_FILEDATA: - if (data) { - GST_WARNING_OBJECT (parse, "FileData can only appear once"); - break; - } - - ret = gst_ebml_read_binary (ebml, &id, &data, &datalen); - GST_DEBUG_OBJECT (parse, "FileData of size %" G_GUINT64_FORMAT, - datalen); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, - "AttachedFile", id); - break; - case GST_MATROSKA_ID_FILEUID: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "AttachedFile", ret); - - if (filename && mimetype && data && datalen > 0) { - GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE; - GstBuffer *tagbuffer = NULL; - GstCaps *caps; - gchar *filename_lc = g_utf8_strdown (filename, -1); - - GST_DEBUG_OBJECT (parse, "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/") || - g_str_has_suffix (filename_lc, "png") || - g_str_has_suffix (filename_lc, "jpg") || - g_str_has_suffix (filename_lc, "jpeg") || - g_str_has_suffix (filename_lc, "gif") || - g_str_has_suffix (filename_lc, "bmp")) { - 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 (parse, - "Created attachment 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_parse_parse_attachments (GstMatroskaParse * parse, - GstEbmlRead * ebml) -{ - guint32 id; - GstFlowReturn ret = GST_FLOW_OK; - GstTagList *taglist; - - DEBUG_ELEMENT_START (parse, ebml, "Attachments"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "Attachments", ret); - return ret; - } - - taglist = gst_tag_list_new (); - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - case GST_MATROSKA_ID_ATTACHEDFILE: - ret = gst_matroska_parse_parse_attached_file (parse, ebml, taglist); - break; - - default: - ret = gst_matroska_read_common_parse_skip (&parse->common, ebml, - "Attachments", id); - break; - } - } - DEBUG_ELEMENT_STOP (parse, ebml, "Attachments", ret); - - if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) { - GST_DEBUG_OBJECT (parse, "Storing attachment tags"); - gst_matroska_read_common_found_global_tag (&parse->common, - GST_ELEMENT_CAST (parse), taglist); - } else { - GST_DEBUG_OBJECT (parse, "No valid attachments found"); - gst_tag_list_free (taglist); - } - - parse->attachments_parsed = TRUE; - - return ret; -} - -static GstFlowReturn -gst_matroska_parse_parse_chapters (GstMatroskaParse * parse, GstEbmlRead * ebml) -{ - guint32 id; - GstFlowReturn ret = GST_FLOW_OK; - - GST_WARNING_OBJECT (parse, "Parsing of chapters not implemented yet"); - - /* TODO: implement parsing of chapters */ - - DEBUG_ELEMENT_START (parse, ebml, "Chapters"); - - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { - DEBUG_ELEMENT_STOP (parse, ebml, "Chapters", ret); - return ret; - } - - while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { - if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) - break; - - switch (id) { - default: - ret = gst_ebml_read_skip (ebml); - break; - } - } - - DEBUG_ELEMENT_STOP (parse, ebml, "Chapters", ret); - return ret; -} - /* * Read signed/unsigned "EBML" numbers. * Return: number of bytes processed. @@ -2461,13 +1871,14 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, "generating segment starting at %" GST_TIME_FORMAT, GST_TIME_ARGS (lace_time)); /* pretend we seeked here */ - gst_segment_set_seek (&parse->segment, parse->segment.rate, + gst_segment_set_seek (&parse->common.segment, parse->common.segment.rate, GST_FORMAT_TIME, 0, GST_SEEK_TYPE_SET, lace_time, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE, NULL); /* now convey our segment notion downstream */ gst_matroska_parse_send_event (parse, gst_event_new_new_segment (FALSE, - parse->segment.rate, parse->segment.format, parse->segment.start, - parse->segment.stop, parse->segment.start)); + parse->common.segment.rate, parse->common.segment.format, + parse->common.segment.start, parse->common.segment.stop, + parse->common.segment.start)); parse->need_newsegment = FALSE; } @@ -2509,7 +1920,7 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, will instad skip until the next keyframe. */ if (GST_CLOCK_TIME_IS_VALID (lace_time) && stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && - stream->index_table && parse->segment.rate > 0.0) { + stream->index_table && parse->common.segment.rate > 0.0) { GstMatroskaTrackVideoContext *videocontext = (GstMatroskaTrackVideoContext *) stream; GstClockTime earliest_time; @@ -2518,7 +1929,7 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, GST_OBJECT_LOCK (parse); earliest_time = videocontext->earliest_time; GST_OBJECT_UNLOCK (parse); - earliest_stream_time = gst_segment_to_position (&parse->segment, + earliest_stream_time = gst_segment_to_position (&parse->common.segment, GST_FORMAT_TIME, earliest_time); if (GST_CLOCK_TIME_IS_VALID (lace_time) && @@ -2566,11 +1977,11 @@ gst_matroska_parse_parse_blockgroup_or_simpleblock (GstMatroskaParse * parse, GstClockTime last_stop_end; /* Check if this stream is after segment stop */ - if (GST_CLOCK_TIME_IS_VALID (parse->segment.stop) && - lace_time >= parse->segment.stop) { + if (GST_CLOCK_TIME_IS_VALID (parse->common.segment.stop) && + lace_time >= parse->common.segment.stop) { GST_DEBUG_OBJECT (parse, "Stream %d after segment stop %" GST_TIME_FORMAT, stream->index, - GST_TIME_ARGS (parse->segment.stop)); + GST_TIME_ARGS (parse->common.segment.stop)); gst_buffer_unref (sub); goto eos; } @@ -3238,8 +2649,9 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, switch (id) { case GST_MATROSKA_ID_SEGMENTINFO: GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); - if (!parse->segmentinfo_parsed) { - ret = gst_matroska_parse_parse_info (parse, &ebml); + if (!parse->common.segmentinfo_parsed) { + ret = gst_matroska_read_common_parse_info (&parse->common, + GST_ELEMENT_CAST (parse), &ebml); } gst_matroska_parse_accumulate_streamheader (parse, ebml.buf); break; @@ -3328,19 +2740,21 @@ gst_matroska_parse_parse_id (GstMatroskaParse * parse, guint32 id, break; case GST_MATROSKA_ID_ATTACHMENTS: GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); - if (!parse->attachments_parsed) { - ret = gst_matroska_parse_parse_attachments (parse, &ebml); + if (!parse->common.attachments_parsed) { + ret = gst_matroska_read_common_parse_attachments (&parse->common, + GST_ELEMENT_CAST (parse), &ebml); } gst_matroska_parse_output (parse, ebml.buf, FALSE); break; case GST_MATROSKA_ID_TAGS: GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); - ret = gst_matroska_parse_parse_metadata (parse, &ebml); + ret = gst_matroska_read_common_parse_metadata (&parse->common, + GST_ELEMENT_CAST (parse), &ebml); gst_matroska_parse_output (parse, ebml.buf, FALSE); break; case GST_MATROSKA_ID_CHAPTERS: GST_READ_CHECK (gst_matroska_parse_take (parse, read, &ebml)); - ret = gst_matroska_parse_parse_chapters (parse, &ebml); + ret = gst_matroska_read_common_parse_chapters (&parse->common, &ebml); gst_matroska_parse_output (parse, ebml.buf, FALSE); break; case GST_MATROSKA_ID_SEEKHEAD: @@ -3698,12 +3112,12 @@ gst_matroska_parse_handle_sink_event (GstPad * pad, GstEvent * event) parse->common.offset = start; /* do not know where we are; * need to come across a cluster and generate newsegment */ - parse->segment.last_stop = GST_CLOCK_TIME_NONE; + parse->common.segment.last_stop = GST_CLOCK_TIME_NONE; parse->cluster_time = GST_CLOCK_TIME_NONE; parse->cluster_offset = 0; parse->need_newsegment = TRUE; /* but keep some of the upstream segment */ - parse->segment.rate = rate; + parse->common.segment.rate = rate; exit: /* chain will send initial newsegment after pads have been added, * or otherwise come up with one */ @@ -3733,7 +3147,7 @@ gst_matroska_parse_handle_sink_event (GstPad * pad, GstEvent * event) gst_matroska_read_common_reset_streams (&parse->common, GST_CLOCK_TIME_NONE, TRUE); GST_OBJECT_UNLOCK (parse); - parse->segment.last_stop = GST_CLOCK_TIME_NONE; + parse->common.segment.last_stop = GST_CLOCK_TIME_NONE; parse->cluster_time = GST_CLOCK_TIME_NONE; parse->cluster_offset = 0; /* fall-through */ diff --git a/gst/matroska/matroska-parse.h b/gst/matroska/matroska-parse.h index 14c9129525..a447e776cd 100644 --- a/gst/matroska/matroska-parse.h +++ b/gst/matroska/matroska-parse.h @@ -60,11 +60,6 @@ typedef struct _GstMatroskaParse { gboolean pushed_headers; GstClockTime last_timestamp; - /* metadata */ - gchar *muxing_app; - gchar *writing_app; - gint64 created; - /* state */ //gboolean streaming; guint level_up; @@ -73,13 +68,9 @@ typedef struct _GstMatroskaParse { /* did we parse cues/tracks/segmentinfo already? */ gboolean tracks_parsed; - gboolean segmentinfo_parsed; - gboolean attachments_parsed; - GList *tags_parsed; GList *seek_parsed; /* keeping track of playback position */ - GstSegment segment; gboolean segment_running; GstClockTime last_stop_end; diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index bc4200b593..fa678fdb0e 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -36,6 +36,9 @@ #include #endif +#include +#include + #include "lzo.h" #include "ebml-read.h" @@ -484,6 +487,242 @@ gst_matroska_read_common_parse_skip (GstMatroskaReadCommon * common, return gst_ebml_read_skip (ebml); } +static GstFlowReturn +gst_matroska_read_common_parse_attached_file (GstMatroskaReadCommon * common, + GstEbmlRead * ebml, GstTagList * taglist) +{ + guint32 id; + GstFlowReturn ret; + gchar *description = NULL; + gchar *filename = NULL; + gchar *mimetype = NULL; + guint8 *data = NULL; + guint64 datalen = 0; + + DEBUG_ELEMENT_START (common, ebml, "AttachedFile"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "AttachedFile", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + /* read all sub-entries */ + + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_FILEDESCRIPTION: + if (description) { + GST_WARNING_OBJECT (common, "FileDescription can only appear once"); + break; + } + + ret = gst_ebml_read_utf8 (ebml, &id, &description); + GST_DEBUG_OBJECT (common, "FileDescription: %s", + GST_STR_NULL (description)); + break; + case GST_MATROSKA_ID_FILENAME: + if (filename) { + GST_WARNING_OBJECT (common, "FileName can only appear once"); + break; + } + + ret = gst_ebml_read_utf8 (ebml, &id, &filename); + + GST_DEBUG_OBJECT (common, "FileName: %s", GST_STR_NULL (filename)); + break; + case GST_MATROSKA_ID_FILEMIMETYPE: + if (mimetype) { + GST_WARNING_OBJECT (common, "FileMimeType can only appear once"); + break; + } + + ret = gst_ebml_read_ascii (ebml, &id, &mimetype); + GST_DEBUG_OBJECT (common, "FileMimeType: %s", GST_STR_NULL (mimetype)); + break; + case GST_MATROSKA_ID_FILEDATA: + if (data) { + GST_WARNING_OBJECT (common, "FileData can only appear once"); + break; + } + + ret = gst_ebml_read_binary (ebml, &id, &data, &datalen); + GST_DEBUG_OBJECT (common, "FileData of size %" G_GUINT64_FORMAT, + datalen); + break; + + default: + ret = gst_matroska_read_common_parse_skip (common, ebml, + "AttachedFile", id); + break; + case GST_MATROSKA_ID_FILEUID: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (common, ebml, "AttachedFile", ret); + + if (filename && mimetype && data && datalen > 0) { + GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE; + GstBuffer *tagbuffer = NULL; + GstCaps *caps; + gchar *filename_lc = g_utf8_strdown (filename, -1); + + GST_DEBUG_OBJECT (common, "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/") || + g_str_has_suffix (filename_lc, "png") || + g_str_has_suffix (filename_lc, "jpg") || + g_str_has_suffix (filename_lc, "jpeg") || + g_str_has_suffix (filename_lc, "gif") || + g_str_has_suffix (filename_lc, "bmp")) { + 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 (common, + "Created attachment 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; +} + +GstFlowReturn +gst_matroska_read_common_parse_attachments (GstMatroskaReadCommon * common, + GstElement * el, GstEbmlRead * ebml) +{ + guint32 id; + GstFlowReturn ret = GST_FLOW_OK; + GstTagList *taglist; + + DEBUG_ELEMENT_START (common, ebml, "Attachments"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "Attachments", ret); + return ret; + } + + taglist = gst_tag_list_new (); + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_ATTACHEDFILE: + ret = gst_matroska_read_common_parse_attached_file (common, ebml, + taglist); + break; + + default: + ret = gst_matroska_read_common_parse_skip (common, ebml, + "Attachments", id); + break; + } + } + DEBUG_ELEMENT_STOP (common, ebml, "Attachments", ret); + + if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) { + GST_DEBUG_OBJECT (common, "Storing attachment tags"); + gst_matroska_read_common_found_global_tag (common, el, taglist); + } else { + GST_DEBUG_OBJECT (common, "No valid attachments found"); + gst_tag_list_free (taglist); + } + + common->attachments_parsed = TRUE; + + return ret; +} + +GstFlowReturn +gst_matroska_read_common_parse_chapters (GstMatroskaReadCommon * common, + GstEbmlRead * ebml) +{ + guint32 id; + GstFlowReturn ret = GST_FLOW_OK; + + GST_WARNING_OBJECT (common, "Parsing of chapters not implemented yet"); + + /* TODO: implement parsing of chapters */ + + DEBUG_ELEMENT_START (common, ebml, "Chapters"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "Chapters", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + default: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (common, ebml, "Chapters", ret); + return ret; +} + GstFlowReturn gst_matroska_read_common_parse_header (GstMatroskaReadCommon * common, GstEbmlRead * ebml) @@ -953,6 +1192,360 @@ gst_matroska_read_common_parse_index (GstMatroskaReadCommon * common, return ret; } +GstFlowReturn +gst_matroska_read_common_parse_info (GstMatroskaReadCommon * common, + GstElement * el, GstEbmlRead * ebml) +{ + GstFlowReturn ret = GST_FLOW_OK; + gdouble dur_f = -1.0; + guint32 id; + + DEBUG_ELEMENT_START (common, ebml, "SegmentInfo"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "SegmentInfo", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + /* cluster timecode */ + case GST_MATROSKA_ID_TIMECODESCALE:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + + GST_DEBUG_OBJECT (common, "TimeCodeScale: %" G_GUINT64_FORMAT, num); + common->time_scale = num; + break; + } + + case GST_MATROSKA_ID_DURATION:{ + if ((ret = gst_ebml_read_float (ebml, &id, &dur_f)) != GST_FLOW_OK) + break; + + if (dur_f <= 0.0) { + GST_WARNING_OBJECT (common, "Invalid duration %lf", dur_f); + break; + } + + GST_DEBUG_OBJECT (common, "Duration: %lf", dur_f); + break; + } + + case GST_MATROSKA_ID_WRITINGAPP:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (common, "WritingApp: %s", GST_STR_NULL (text)); + common->writing_app = text; + break; + } + + case GST_MATROSKA_ID_MUXINGAPP:{ + gchar *text; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (common, "MuxingApp: %s", GST_STR_NULL (text)); + common->muxing_app = text; + break; + } + + case GST_MATROSKA_ID_DATEUTC:{ + gint64 time; + + if ((ret = gst_ebml_read_date (ebml, &id, &time)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (common, "DateUTC: %" G_GINT64_FORMAT, time); + common->created = time; + break; + } + + case GST_MATROSKA_ID_TITLE:{ + gchar *text; + GstTagList *taglist; + + if ((ret = gst_ebml_read_utf8 (ebml, &id, &text)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (common, "Title: %s", GST_STR_NULL (text)); + taglist = gst_tag_list_new (); + gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_TITLE, text, + NULL); + gst_matroska_read_common_found_global_tag (common, el, taglist); + g_free (text); + break; + } + + default: + ret = gst_matroska_read_common_parse_skip (common, ebml, + "SegmentInfo", id); + break; + + /* fall through */ + case GST_MATROSKA_ID_SEGMENTUID: + case GST_MATROSKA_ID_SEGMENTFILENAME: + case GST_MATROSKA_ID_PREVUID: + case GST_MATROSKA_ID_PREVFILENAME: + case GST_MATROSKA_ID_NEXTUID: + case GST_MATROSKA_ID_NEXTFILENAME: + case GST_MATROSKA_ID_SEGMENTFAMILY: + case GST_MATROSKA_ID_CHAPTERTRANSLATE: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + if (dur_f > 0.0) { + GstClockTime dur_u; + + dur_u = gst_gdouble_to_guint64 (dur_f * + gst_guint64_to_gdouble (common->time_scale)); + if (GST_CLOCK_TIME_IS_VALID (dur_u) && dur_u <= G_MAXINT64) + gst_segment_set_duration (&common->segment, GST_FORMAT_TIME, dur_u); + } + + DEBUG_ELEMENT_STOP (common, ebml, "SegmentInfo", ret); + + common->segmentinfo_parsed = TRUE; + + return ret; +} + +static GstFlowReturn +gst_matroska_read_common_parse_metadata_id_simple_tag (GstMatroskaReadCommon * + common, GstEbmlRead * ebml, GstTagList ** p_taglist) +{ + /* FIXME: check if there are more useful mappings */ + static const struct + { + const gchar *matroska_tagname; + const gchar *gstreamer_tagname; + } + tag_conv[] = { + { + GST_MATROSKA_TAG_ID_TITLE, GST_TAG_TITLE}, { + GST_MATROSKA_TAG_ID_ARTIST, GST_TAG_ARTIST}, { + GST_MATROSKA_TAG_ID_AUTHOR, GST_TAG_ARTIST}, { + GST_MATROSKA_TAG_ID_ALBUM, GST_TAG_ALBUM}, { + GST_MATROSKA_TAG_ID_COMMENTS, GST_TAG_COMMENT}, { + GST_MATROSKA_TAG_ID_BITSPS, GST_TAG_BITRATE}, { + GST_MATROSKA_TAG_ID_BPS, GST_TAG_BITRATE}, { + GST_MATROSKA_TAG_ID_ENCODER, GST_TAG_ENCODER}, { + GST_MATROSKA_TAG_ID_DATE, GST_TAG_DATE}, { + GST_MATROSKA_TAG_ID_ISRC, GST_TAG_ISRC}, { + GST_MATROSKA_TAG_ID_COPYRIGHT, GST_TAG_COPYRIGHT}, { + GST_MATROSKA_TAG_ID_BPM, GST_TAG_BEATS_PER_MINUTE}, { + GST_MATROSKA_TAG_ID_TERMS_OF_USE, GST_TAG_LICENSE}, { + GST_MATROSKA_TAG_ID_COMPOSER, GST_TAG_COMPOSER}, { + GST_MATROSKA_TAG_ID_LEAD_PERFORMER, GST_TAG_PERFORMER}, { + GST_MATROSKA_TAG_ID_GENRE, GST_TAG_GENRE} + }; + GstFlowReturn ret; + guint32 id; + gchar *value = NULL; + gchar *tag = NULL; + + DEBUG_ELEMENT_START (common, ebml, "SimpleTag"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "SimpleTag", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + /* read all sub-entries */ + + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_TAGNAME: + g_free (tag); + tag = NULL; + ret = gst_ebml_read_ascii (ebml, &id, &tag); + GST_DEBUG_OBJECT (common, "TagName: %s", GST_STR_NULL (tag)); + break; + + case GST_MATROSKA_ID_TAGSTRING: + g_free (value); + value = NULL; + ret = gst_ebml_read_utf8 (ebml, &id, &value); + GST_DEBUG_OBJECT (common, "TagString: %s", GST_STR_NULL (value)); + break; + + default: + ret = gst_matroska_read_common_parse_skip (common, ebml, "SimpleTag", + id); + break; + /* fall-through */ + + case GST_MATROSKA_ID_TAGLANGUAGE: + case GST_MATROSKA_ID_TAGDEFAULT: + case GST_MATROSKA_ID_TAGBINARY: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (common, ebml, "SimpleTag", ret); + + if (tag && value) { + guint i; + + for (i = 0; i < G_N_ELEMENTS (tag_conv); i++) { + const gchar *tagname_gst = tag_conv[i].gstreamer_tagname; + + const gchar *tagname_mkv = tag_conv[i].matroska_tagname; + + if (strcmp (tagname_mkv, tag) == 0) { + GValue dest = { 0, }; + GType dest_type = gst_tag_get_type (tagname_gst); + + /* Ensure that any date string is complete */ + if (dest_type == GST_TYPE_DATE) { + guint year = 1901, month = 1, day = 1; + + /* Dates can be yyyy-MM-dd, yyyy-MM or yyyy, but we need + * the first type */ + if (sscanf (value, "%04u-%02u-%02u", &year, &month, &day) != 0) { + g_free (value); + value = g_strdup_printf ("%04u-%02u-%02u", year, month, day); + } + } + + g_value_init (&dest, dest_type); + if (gst_value_deserialize (&dest, value)) { + gst_tag_list_add_values (*p_taglist, GST_TAG_MERGE_APPEND, + tagname_gst, &dest, NULL); + } else { + GST_WARNING_OBJECT (common, "Can't transform tag '%s' with " + "value '%s' to target type '%s'", tag, value, + g_type_name (dest_type)); + } + g_value_unset (&dest); + break; + } + } + } + + g_free (tag); + g_free (value); + + return ret; +} + +static GstFlowReturn +gst_matroska_read_common_parse_metadata_id_tag (GstMatroskaReadCommon * common, + GstEbmlRead * ebml, GstTagList ** p_taglist) +{ + guint32 id; + GstFlowReturn ret; + + DEBUG_ELEMENT_START (common, ebml, "Tag"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "Tag", ret); + return ret; + } + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + /* read all sub-entries */ + + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_SIMPLETAG: + ret = gst_matroska_read_common_parse_metadata_id_simple_tag (common, + ebml, p_taglist); + break; + + default: + ret = gst_matroska_read_common_parse_skip (common, ebml, "Tag", id); + break; + } + } + + DEBUG_ELEMENT_STOP (common, ebml, "Tag", ret); + + return ret; +} + +GstFlowReturn +gst_matroska_read_common_parse_metadata (GstMatroskaReadCommon * common, + GstElement * el, GstEbmlRead * ebml) +{ + GstTagList *taglist; + GstFlowReturn ret = GST_FLOW_OK; + guint32 id; + GList *l; + guint64 curpos; + + curpos = gst_ebml_read_get_pos (ebml); + + /* Make sure we don't parse a tags element twice and + * post it's tags twice */ + curpos = gst_ebml_read_get_pos (ebml); + for (l = common->tags_parsed; l; l = l->next) { + guint64 *pos = l->data; + + if (*pos == curpos) { + GST_DEBUG_OBJECT (common, "Skipping already parsed Tags at offset %" + G_GUINT64_FORMAT, curpos); + return GST_FLOW_OK; + } + } + + common->tags_parsed = + g_list_prepend (common->tags_parsed, g_slice_new (guint64)); + *((guint64 *) common->tags_parsed->data) = curpos; + /* fall-through */ + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) { + DEBUG_ELEMENT_STOP (common, ebml, "Tags", ret); + return ret; + } + + taglist = gst_tag_list_new (); + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + break; + + switch (id) { + case GST_MATROSKA_ID_TAG: + ret = gst_matroska_read_common_parse_metadata_id_tag (common, ebml, + &taglist); + break; + + default: + ret = gst_matroska_read_common_parse_skip (common, ebml, "Tags", id); + break; + /* FIXME: Use to limit the tags to specific pads */ + case GST_MATROSKA_ID_TARGETS: + ret = gst_ebml_read_skip (ebml); + break; + } + } + + DEBUG_ELEMENT_STOP (common, ebml, "Tags", ret); + + gst_matroska_read_common_found_global_tag (common, el, taglist); + + return ret; +} + static const guint8 * gst_matroska_read_common_peek_adapter (GstMatroskaReadCommon * common, guint peek) diff --git a/gst/matroska/matroska-read-common.h b/gst/matroska/matroska-read-common.h index 7525772d94..68b0f6c34f 100644 --- a/gst/matroska/matroska-read-common.h +++ b/gst/matroska/matroska-read-common.h @@ -49,11 +49,19 @@ typedef struct _GstMatroskaReadCommon { GPtrArray *src; guint num_streams; + /* metadata */ + gchar *muxing_app; + gchar *writing_app; + gint64 created; + /* state */ GstMatroskaReadState state; /* did we parse cues/tracks/segmentinfo already? */ gboolean index_parsed; + gboolean segmentinfo_parsed; + gboolean attachments_parsed; + GList *tags_parsed; /* start-of-segment */ guint64 ebml_segment_start; @@ -64,6 +72,9 @@ typedef struct _GstMatroskaReadCommon { /* timescale in the file */ guint64 time_scale; + /* keeping track of playback position */ + GstSegment segment; + GstTagList *global_tags; /* pull mode caching */ @@ -91,8 +102,16 @@ GstMatroskaTrackContext * gst_matroska_read_common_get_seek_track ( GstMatroskaReadCommon * common, GstMatroskaTrackContext * track); GstFlowReturn gst_matroska_read_common_parse_index (GstMatroskaReadCommon * common, GstEbmlRead * ebml); +GstFlowReturn gst_matroska_read_common_parse_info (GstMatroskaReadCommon * + common, GstElement * el, GstEbmlRead * ebml); +GstFlowReturn gst_matroska_read_common_parse_attachments ( + GstMatroskaReadCommon * common, GstElement * el, GstEbmlRead * ebml); +GstFlowReturn gst_matroska_read_common_parse_chapters (GstMatroskaReadCommon * + common, GstEbmlRead * ebml); GstFlowReturn gst_matroska_read_common_parse_header (GstMatroskaReadCommon * common, GstEbmlRead * ebml); +GstFlowReturn gst_matroska_read_common_parse_metadata (GstMatroskaReadCommon * + common, GstElement * el, GstEbmlRead * ebml); GstFlowReturn gst_matroska_read_common_parse_skip (GstMatroskaReadCommon * common, GstEbmlRead * ebml, const gchar * parent_name, guint id); GstFlowReturn gst_matroska_read_common_peek_bytes (GstMatroskaReadCommon * diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 92b9ae002f..ecb420e007 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -6434,6 +6434,8 @@ gst_rtspsrc_thread (GstRTSPSrc * src) else if (src->task) gst_task_pause (src->task); } + /* reset waiting */ + src->waiting = FALSE; GST_OBJECT_UNLOCK (src); }