Merge branch 'master' into 0.11

This commit is contained in:
Wim Taymans 2011-06-08 18:06:56 +02:00
commit 710fa239d5
10 changed files with 832 additions and 1342 deletions

View file

@ -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);

View file

@ -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;
};

View file

@ -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,

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -61,8 +61,6 @@
#include <gst/tag/tag.h>
#include <gst/base/gsttypefindhelper.h>
#include <gst/pbutils/pbutils.h>
#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 */

View file

@ -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;

View file

@ -36,6 +36,9 @@
#include <bzlib.h>
#endif
#include <gst/tag/tag.h>
#include <gst/base/gsttypefindhelper.h>
#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)

View file

@ -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 *

View file

@ -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);
}