PROP_0,
PROP_METADATA,
PROP_STREAMINFO,
- PROP_MAX_GAP_TIME
+ PROP_MAX_GAP_TIME,
+ PROP_MAX_BACKTRACK_DISTANCE
};
-#define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND)
-#define INVALID_DATA_THRESHOLD (2 * 1024 * 1024)
+#define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND)
+#define DEFAULT_MAX_BACKTRACK_DISTANCE 30
+#define INVALID_DATA_THRESHOLD (2 * 1024 * 1024)
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
static gboolean gst_matroska_demux_handle_sink_event (GstPad * pad,
GstObject * parent, GstEvent * event);
+static gboolean gst_matroska_demux_handle_sink_query (GstPad * pad,
+ GstObject * parent, GstQuery * query);
static GstFlowReturn gst_matroska_demux_chain (GstPad * pad,
GstObject * object, GstBuffer * buffer);
static GstCaps
* gst_matroska_demux_subtitle_caps (GstMatroskaTrackSubtitleContext *
subtitlecontext, const gchar * codec_id, gpointer data, guint size);
+static const gchar *gst_matroska_track_encryption_algorithm_name (gint val);
+static const gchar *gst_matroska_track_encryption_cipher_mode_name (gint val);
+static const gchar *gst_matroska_track_encoding_scope_name (gint val);
/* stream methods */
static void gst_matroska_demux_reset (GstElement * element);
"gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_MAX_BACKTRACK_DISTANCE,
+ g_param_spec_uint ("max-backtrack-distance",
+ "Maximum backtrack distance",
+ "Maximum backtrack distance in seconds when seeking without "
+ "and index in pull mode and search for a keyframe "
+ "(0 = disable backtracking).",
+ 0, G_MAXUINT, DEFAULT_MAX_BACKTRACK_DISTANCE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
gstelement_class->send_event =
GST_DEBUG_FUNCPTR (gst_matroska_demux_chain));
gst_pad_set_event_function (demux->common.sinkpad,
GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_event));
+ gst_pad_set_query_function (demux->common.sinkpad,
+ GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_query));
gst_element_add_pad (GST_ELEMENT (demux), demux->common.sinkpad);
/* init defaults for common read context */
/* property defaults */
demux->max_gap_time = DEFAULT_MAX_GAP_TIME;
+ demux->max_backtrack_distance = DEFAULT_MAX_BACKTRACK_DISTANCE;
GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);
demux->cached_length = G_MAXUINT64;
+ if (demux->deferred_seek_event)
+ gst_event_unref (demux->deferred_seek_event);
+ demux->deferred_seek_event = NULL;
+ demux->deferred_seek_pad = NULL;
+
gst_flow_combiner_clear (demux->flowcombiner);
}
GstMapInfo map;
gpointer data;
gsize size;
+ GstBuffer *out_buf = buf;
g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);
GST_DEBUG ("decoding buffer %p", buf);
- gst_buffer_map (buf, &map, GST_MAP_READ);
+ gst_buffer_map (out_buf, &map, GST_MAP_READ);
data = map.data;
size = map.size;
if (gst_matroska_decode_data (context->encodings, &data, &size,
GST_MATROSKA_TRACK_ENCODING_SCOPE_FRAME, FALSE)) {
- gst_buffer_unmap (buf, &map);
- gst_buffer_unref (buf);
- return gst_buffer_new_wrapped (data, size);
+ if (data != map.data) {
+ gst_buffer_unmap (out_buf, &map);
+ gst_buffer_unref (out_buf);
+ out_buf = gst_buffer_new_wrapped (data, size);
+ } else {
+ gst_buffer_unmap (out_buf, &map);
+ }
} else {
GST_DEBUG ("decode data failed");
- gst_buffer_unmap (buf, &map);
- gst_buffer_unref (buf);
+ gst_buffer_unmap (out_buf, &map);
+ gst_buffer_unref (out_buf);
return NULL;
}
+ /* Encrypted stream */
+ if (context->protection_info) {
+
+ GstStructure *info_protect = gst_structure_copy (context->protection_info);
+ gboolean encrypted = FALSE;
+
+ gst_buffer_map (out_buf, &map, GST_MAP_READ);
+ data = map.data;
+ size = map.size;
+
+ if (gst_matroska_parse_protection_meta (&data, &size, info_protect,
+ &encrypted)) {
+ if (data != map.data) {
+ GstBuffer *tmp_buf;
+
+ gst_buffer_unmap (out_buf, &map);
+ tmp_buf = out_buf;
+ out_buf = gst_buffer_copy_region (tmp_buf, GST_BUFFER_COPY_ALL,
+ gst_buffer_get_size (tmp_buf) - size, size);
+ gst_buffer_unref (tmp_buf);
+ if (encrypted)
+ gst_buffer_add_protection_meta (out_buf, info_protect);
+ else
+ gst_structure_free (info_protect);
+ } else {
+ gst_buffer_unmap (out_buf, &map);
+ gst_structure_free (info_protect);
+ }
+ } else {
+ GST_WARNING ("Adding protection metadata failed");
+ gst_buffer_unmap (out_buf, &map);
+ gst_buffer_unref (out_buf);
+ gst_structure_free (info_protect);
+ return NULL;
+ }
+ }
+
+ return out_buf;
}
static void
}
static GstFlowReturn
-gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml)
+gst_matroska_demux_parse_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml,
+ GstMatroskaTrackContext ** dest_context)
{
- GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
GstMatroskaTrackContext *context;
- GstPadTemplate *templ = NULL;
- GstStreamFlags stream_flags;
GstCaps *caps = NULL;
GstTagList *cached_taglist;
- gchar *padname = NULL;
GstFlowReturn ret;
guint32 id, riff_fourcc = 0;
guint16 riff_audio_fmt = 0;
- GstEvent *stream_start;
gchar *codec = NULL;
- gchar *stream_id;
DEBUG_ELEMENT_START (demux, ebml, "TrackEntry");
/* allocate generic... if we know the type, we'll g_renew()
* with the precise type */
context = g_new0 (GstMatroskaTrackContext, 1);
- g_ptr_array_add (demux->common.src, context);
- context->index = demux->common.num_streams;
context->index_writer_id = -1;
context->type = 0; /* no type yet */
context->default_duration = 0;
context->dts_only = FALSE;
context->intra_only = FALSE;
context->tags = gst_tag_list_new_empty ();
- demux->common.num_streams++;
- g_assert (demux->common.src->len == demux->common.num_streams);
+ g_queue_init (&context->protection_event_queue);
+ context->protection_info = NULL;
- GST_DEBUG_OBJECT (demux, "Stream number %d", context->index);
+ GST_DEBUG_OBJECT (demux, "Parsing a TrackEntry (%d tracks parsed so far)",
+ demux->common.num_streams);
/* try reading the trackentry headers */
while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
GST_ERROR_OBJECT (demux, "Invalid TrackNumber 0");
ret = GST_FLOW_ERROR;
break;
- } else if (!gst_matroska_read_common_tracknumber_unique (&demux->common,
- num)) {
- GST_ERROR_OBJECT (demux, "TrackNumber %" G_GUINT64_FORMAT
- " is not unique", num);
- ret = GST_FLOW_ERROR;
- break;
}
GST_DEBUG_OBJECT (demux, "TrackNumber: %" G_GUINT64_FORMAT, num);
context->type = 0;
break;
}
- g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
- = context;
break;
}
break;
}
videocontext = (GstMatroskaTrackVideoContext *) context;
- g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
- = context;
while (ret == GST_FLOW_OK &&
gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
break;
audiocontext = (GstMatroskaTrackAudioContext *) context;
- g_ptr_array_index (demux->common.src, demux->common.num_streams - 1)
- = context;
while (ret == GST_FLOW_OK &&
gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
if (ret == GST_FLOW_OK || ret == GST_FLOW_EOS)
GST_WARNING_OBJECT (ebml, "Unknown stream/codec in track entry header");
- demux->common.num_streams--;
- g_ptr_array_remove_index (demux->common.src, demux->common.num_streams);
- g_assert (demux->common.src->len == demux->common.num_streams);
gst_matroska_track_free (context);
-
+ context = NULL;
+ *dest_context = NULL;
return ret;
}
if (cached_taglist)
gst_tag_list_insert (context->tags, cached_taglist, GST_TAG_MERGE_APPEND);
- /* now create the GStreamer connectivity */
+ /* compute caps */
switch (context->type) {
case GST_MATROSKA_TRACK_TYPE_VIDEO:{
GstMatroskaTrackVideoContext *videocontext =
(GstMatroskaTrackVideoContext *) context;
- padname = g_strdup_printf ("video_%u", demux->num_v_streams++);
- templ = gst_element_class_get_pad_template (klass, "video_%u");
caps = gst_matroska_demux_video_caps (videocontext,
context->codec_id, context->codec_priv,
context->codec_priv_size, &codec, &riff_fourcc);
- if (!context->intra_only)
- demux->have_nonintraonly_v_streams = TRUE;
-
if (codec) {
gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE,
GST_TAG_VIDEO_CODEC, codec, NULL);
GstMatroskaTrackAudioContext *audiocontext =
(GstMatroskaTrackAudioContext *) context;
- padname = g_strdup_printf ("audio_%u", demux->num_a_streams++);
- templ = gst_element_class_get_pad_template (klass, "audio_%u");
caps = gst_matroska_demux_audio_caps (audiocontext,
context->codec_id, context->codec_priv, context->codec_priv_size,
&codec, &riff_audio_fmt);
GstMatroskaTrackSubtitleContext *subtitlecontext =
(GstMatroskaTrackSubtitleContext *) context;
- padname = g_strdup_printf ("subtitle_%u", demux->num_t_streams++);
- templ = gst_element_class_get_pad_template (klass, "subtitle_%u");
caps = gst_matroska_demux_subtitle_caps (subtitlecontext,
context->codec_id, context->codec_priv, context->codec_priv_size);
break;
context->stream_headers, caps);
}
+ if (context->encodings) {
+ GstMatroskaTrackEncoding *enc;
+ guint i;
+
+ for (i = 0; i < context->encodings->len; i++) {
+ enc = &g_array_index (context->encodings, GstMatroskaTrackEncoding, i);
+ if (enc->type == GST_MATROSKA_ENCODING_ENCRYPTION /* encryption */ ) {
+ GstStructure *s = gst_caps_get_structure (caps, 0);
+ if (!gst_structure_has_name (s, "application/x-webm-enc")) {
+ gst_structure_set (s, "original-media-type", G_TYPE_STRING,
+ gst_structure_get_name (s), NULL);
+ gst_structure_set (s, "encryption-algorithm", G_TYPE_STRING,
+ gst_matroska_track_encryption_algorithm_name (enc->enc_algo),
+ NULL);
+ gst_structure_set (s, "encoding-scope", G_TYPE_STRING,
+ gst_matroska_track_encoding_scope_name (enc->scope), NULL);
+ gst_structure_set (s, "cipher-mode", G_TYPE_STRING,
+ gst_matroska_track_encryption_cipher_mode_name
+ (enc->enc_cipher_mode), NULL);
+ gst_structure_set_name (s, "application/x-webm-enc");
+ }
+ }
+ }
+ }
+
+ context->caps = caps;
+
+ /* tadaah! */
+ *dest_context = context;
+ return ret;
+}
+
+static void
+gst_matroska_demux_add_stream (GstMatroskaDemux * demux,
+ GstMatroskaTrackContext * context)
+{
+ GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
+ gchar *padname = NULL;
+ GstPadTemplate *templ = NULL;
+ GstStreamFlags stream_flags;
+
+ GstEvent *stream_start;
+
+ gchar *stream_id;
+
+ g_ptr_array_add (demux->common.src, context);
+ context->index = demux->common.num_streams++;
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ g_ptr_array_index (demux->common.src, demux->common.num_streams - 1) =
+ context;
+
+ /* now create the GStreamer connectivity */
+ switch (context->type) {
+ case GST_MATROSKA_TRACK_TYPE_VIDEO:
+ padname = g_strdup_printf ("video_%u", demux->num_v_streams++);
+ templ = gst_element_class_get_pad_template (klass, "video_%u");
+
+ if (!context->intra_only)
+ demux->have_nonintraonly_v_streams = TRUE;
+ break;
+
+ case GST_MATROSKA_TRACK_TYPE_AUDIO:
+ padname = g_strdup_printf ("audio_%u", demux->num_a_streams++);
+ templ = gst_element_class_get_pad_template (klass, "audio_%u");
+ break;
+
+ case GST_MATROSKA_TRACK_TYPE_SUBTITLE:
+ padname = g_strdup_printf ("subtitle_%u", demux->num_t_streams++);
+ templ = gst_element_class_get_pad_template (klass, "subtitle_%u");
+ break;
+
+ default:
+ /* we should already have quit by now */
+ g_assert_not_reached ();
+ }
+
/* the pad in here */
context->pad = gst_pad_new_from_template (templ, padname);
- context->caps = caps;
gst_pad_set_event_function (context->pad,
GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_src_event));
GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_src_query));
GST_INFO_OBJECT (demux, "Adding pad '%s' with caps %" GST_PTR_FORMAT,
- padname, caps);
+ padname, context->caps);
gst_pad_set_element_private (context->pad, context);
gst_flow_combiner_add_pad (demux->flowcombiner, context->pad);
g_free (padname);
-
- /* tadaah! */
- return ret;
}
static gboolean
if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
/* no seeking until we are (safely) ready */
if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
- GST_DEBUG_OBJECT (demux, "not ready for seeking yet");
- gst_event_unref (event);
- return FALSE;
+ GST_DEBUG_OBJECT (demux,
+ "not ready for seeking yet, deferring seek: %" GST_PTR_FORMAT, event);
+ if (demux->deferred_seek_event)
+ gst_event_unref (demux->deferred_seek_event);
+ demux->deferred_seek_event = event;
+ demux->deferred_seek_pad = NULL;
+ return TRUE;
}
res = gst_matroska_demux_handle_seek_event (demux, NULL, event);
} else {
GST_DEBUG_OBJECT (demux, "Cluster starts with delta frame, backtracking");
- if (cluster.prev_size == 0 || cluster.prev_size > off) {
- GST_LOG_OBJECT (demux, "Cluster has no or invalid prev size, stopping");
- break;
+ /* Don't scan back more than this much in time from the cluster we
+ * originally landed on. This is mostly a sanity check in case a file
+ * always has keyframes in the middle of clusters and never at the
+ * beginning. Without this we would always scan back to the beginning
+ * of the file in that case. */
+ if (cluster.time != GST_CLOCK_TIME_NONE) {
+ GstClockTimeDiff distance = GST_CLOCK_DIFF (cluster.time, *cluster_time);
+
+ if (distance < 0 || distance > demux->max_backtrack_distance * GST_SECOND) {
+ GST_DEBUG_OBJECT (demux, "Haven't found cluster with keyframe within "
+ "%u secs of original seek target cluster, stopping",
+ demux->max_backtrack_distance);
+ break;
+ }
+ }
+
+ /* If we have cluster prev_size we can skip back efficiently. If not,
+ * we'll just do a brute force search for a cluster identifier */
+ if (cluster.prev_size > 0 && off >= cluster.prev_size) {
+ off -= cluster.prev_size;
+ } else {
+ GstFlowReturn flow;
+
+ GST_LOG_OBJECT (demux, "Cluster has no or invalid prev size, searching "
+ "for previous cluster instead then");
+
+ flow = gst_matroska_demux_search_cluster (demux, &off, FALSE);
+ if (flow != GST_FLOW_OK) {
+ GST_DEBUG_OBJECT (demux, "cluster search yielded flow %s, stopping",
+ gst_flow_get_name (flow));
+ break;
+ }
}
- off -= cluster.prev_size;
if (off <= first_cluster_offset) {
GST_LOG_OBJECT (demux, "Reached first cluster, stopping");
*cluster_offset = first_cluster_offset;
/* If we have video and can easily backtrack, check if we landed on a cluster
* that starts with a keyframe - and if not backtrack until we find one that
* does. */
- if (demux->have_nonintraonly_v_streams && demux->seen_cluster_prevsize) {
+ if (demux->have_nonintraonly_v_streams && demux->max_backtrack_distance > 0) {
if (gst_matroska_demux_scan_back_for_keyframe_cluster (demux,
&cluster_offset, &cluster_time)) {
GST_INFO_OBJECT (demux, "Adjusted cluster to %" GST_TIME_FORMAT " @ "
"%" G_GUINT64_FORMAT, GST_TIME_ARGS (cluster_time), cluster_offset);
}
- } else if (demux->have_nonintraonly_v_streams) {
- GST_FIXME_OBJECT (demux, "implement scanning back to prev cluster without "
- "cluster prev size field");
}
entry = g_new0 (GstMatroskaIndex, 1);
case GST_EVENT_SEEK:
/* no seeking until we are (safely) ready */
if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) {
- GST_DEBUG_OBJECT (demux, "not ready for seeking yet");
- gst_event_unref (event);
- return FALSE;
+ GST_DEBUG_OBJECT (demux,
+ "not ready for seeking yet, deferring seek event: %" GST_PTR_FORMAT,
+ event);
+ if (demux->deferred_seek_event)
+ gst_event_unref (demux->deferred_seek_event);
+ demux->deferred_seek_event = event;
+ demux->deferred_seek_pad = pad;
+ return TRUE;
}
{
return res;
}
+static gboolean
+gst_matroska_demux_handle_sink_query (GstPad * pad, GstObject * parent,
+ GstQuery * query)
+{
+ GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (parent);
+ gboolean res = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_BITRATE:
+ {
+ if (G_UNLIKELY (demux->cached_length == G_MAXUINT64 ||
+ demux->common.offset >= demux->cached_length)) {
+ demux->cached_length =
+ gst_matroska_read_common_get_length (&demux->common);
+ }
+
+ if (demux->cached_length < G_MAXUINT64
+ && demux->common.segment.duration > 0) {
+ /* TODO: better results based on ranges/index tables */
+ guint bitrate =
+ gst_util_uint64_scale (8 * demux->cached_length, GST_SECOND,
+ demux->common.segment.duration);
+
+ GST_LOG_OBJECT (demux, "bitrate query byte length: %" G_GUINT64_FORMAT
+ " duration %" GST_TIME_FORMAT " resulting in a bitrate of %u",
+ demux->cached_length,
+ GST_TIME_ARGS (demux->common.segment.duration), bitrate);
+
+ gst_query_set_bitrate (query, bitrate);
+ res = TRUE;
+ }
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, (GstObject *) demux, query);
+ break;
+ }
+
+ return res;
+}
+
static GstFlowReturn
gst_matroska_demux_seek_to_previous_keyframe (GstMatroskaDemux * demux)
{
switch (id) {
/* one track within the "all-tracks" header */
- case GST_MATROSKA_ID_TRACKENTRY:
- ret = gst_matroska_demux_add_stream (demux, ebml);
+ case GST_MATROSKA_ID_TRACKENTRY:{
+ GstMatroskaTrackContext *track;
+ ret = gst_matroska_demux_parse_stream (demux, ebml, &track);
+ if (track != NULL) {
+ if (gst_matroska_read_common_tracknumber_unique (&demux->common,
+ track->num)) {
+ gst_matroska_demux_add_stream (demux, track);
+ } else {
+ GST_ERROR_OBJECT (demux,
+ "TrackNumber %" G_GUINT64_FORMAT " is not unique", track->num);
+ ret = GST_FLOW_ERROR;
+ gst_matroska_track_free (track);
+ track = NULL;
+ }
+ }
break;
+ }
default:
ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", ret);
demux->tracks_parsed = TRUE;
+ GST_DEBUG_OBJECT (demux, "signaling no more pads");
+ gst_element_no_more_pads (GST_ELEMENT (demux));
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_matroska_demux_update_tracks (GstMatroskaDemux * demux, GstEbmlRead * ebml)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint num_tracks_found = 0;
+ guint32 id;
+
+ GST_INFO_OBJECT (demux, "Reparsing Tracks element");
+
+ DEBUG_ELEMENT_START (demux, ebml, "Tracks");
+
+ if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) {
+ DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", 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) {
+ /* one track within the "all-tracks" header */
+ case GST_MATROSKA_ID_TRACKENTRY:{
+ GstMatroskaTrackContext *new_track;
+ gint old_track_index;
+ GstMatroskaTrackContext *old_track;
+ ret = gst_matroska_demux_parse_stream (demux, ebml, &new_track);
+ if (new_track == NULL)
+ break;
+ num_tracks_found++;
+
+ if (gst_matroska_read_common_tracknumber_unique (&demux->common,
+ new_track->num)) {
+ GST_ERROR_OBJECT (demux,
+ "Unexpected new TrackNumber: %" G_GUINT64_FORMAT, new_track->num);
+ goto track_mismatch_error;
+ }
+
+ old_track_index =
+ gst_matroska_read_common_stream_from_num (&demux->common,
+ new_track->num);
+ g_assert (old_track_index != -1);
+ old_track = g_ptr_array_index (demux->common.src, old_track_index);
+
+ if (old_track->type != new_track->type) {
+ GST_ERROR_OBJECT (demux,
+ "Mismatch reparsing track %" G_GUINT64_FORMAT
+ " on track type. Expected %d, found %d", new_track->num,
+ old_track->type, new_track->type);
+ goto track_mismatch_error;
+ }
+
+ if (g_strcmp0 (old_track->codec_id, new_track->codec_id) != 0) {
+ GST_ERROR_OBJECT (demux,
+ "Mismatch reparsing track %" G_GUINT64_FORMAT
+ " on codec id. Expected '%s', found '%s'", new_track->num,
+ old_track->codec_id, new_track->codec_id);
+ goto track_mismatch_error;
+ }
+
+ /* The new track matches the old track. No problems on our side.
+ * Let's make it replace the old track. */
+ new_track->pad = old_track->pad;
+ new_track->index = old_track->index;
+ new_track->pos = old_track->pos;
+ g_ptr_array_index (demux->common.src, old_track_index) = new_track;
+ gst_pad_set_element_private (new_track->pad, new_track);
+
+ if (!gst_caps_is_equal (old_track->caps, new_track->caps)) {
+ gst_pad_set_caps (new_track->pad, new_track->caps);
+ }
+ gst_caps_replace (&old_track->caps, NULL);
+
+ if (!gst_tag_list_is_equal (old_track->tags, new_track->tags)) {
+ GST_DEBUG_OBJECT (old_track->pad, "Sending tags %p: %"
+ GST_PTR_FORMAT, new_track->tags, new_track->tags);
+ gst_pad_push_event (new_track->pad,
+ gst_event_new_tag (gst_tag_list_copy (new_track->tags)));
+ }
+
+ gst_matroska_track_free (old_track);
+ break;
+
+ track_mismatch_error:
+ gst_matroska_track_free (new_track);
+ new_track = NULL;
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+
+ default:
+ ret = gst_matroska_read_common_parse_skip (&demux->common, ebml,
+ "Track", id);
+ break;
+ }
+ }
+ DEBUG_ELEMENT_STOP (demux, ebml, "Tracks", ret);
+
+ if (ret != GST_FLOW_ERROR && demux->common.num_streams != num_tracks_found) {
+ GST_ERROR_OBJECT (demux,
+ "Mismatch on the number of tracks. Expected %du tracks, found %du",
+ demux->common.num_streams, num_tracks_found);
+ ret = GST_FLOW_ERROR;
+ }
return ret;
}
gboolean delta_unit = FALSE;
guint64 duration = 0;
gint64 lace_time = 0;
+ GstEvent *protect_event;
stream = g_ptr_array_index (demux->common.src, stream_num);
} else {
lace_time = GST_CLOCK_TIME_NONE;
}
+ /* Send the GST_PROTECTION event */
+ while ((protect_event = g_queue_pop_head (&stream->protection_event_queue))) {
+ GST_TRACE_OBJECT (demux, "pushing protection event for stream %d:%s",
+ stream->index, GST_STR_NULL (stream->name));
+ gst_pad_push_event (stream->pad, protect_event);
+ }
/* need to refresh segment info ASAP */
if (GST_CLOCK_TIME_IS_VALID (lace_time) && demux->need_segment) {
/* If we're doing a keyframe-only trickmode, only push keyframes on video
* streams */
if (delta_unit
- && demux->common.
- segment.flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) {
+ && demux->common.segment.
+ flags & GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS) {
GST_LOG_OBJECT (demux, "Skipping non-keyframe on stream %d",
stream->index);
ret = GST_FLOW_OK;
case GST_MATROSKA_READ_STATE_DATA:
case GST_MATROSKA_READ_STATE_SEEK:
switch (id) {
+ case GST_EBML_ID_HEADER:
+ GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ demux->common.state = GST_MATROSKA_READ_STATE_SEGMENT;
+ gst_matroska_demux_check_seekability (demux);
+ break;
case GST_MATROSKA_ID_SEGMENTINFO:
if (!demux->common.segmentinfo_parsed) {
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
}
break;
case GST_MATROSKA_ID_TRACKS:
+ GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
if (!demux->tracks_parsed) {
- GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
ret = gst_matroska_demux_parse_tracks (demux, &ebml);
} else {
- GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
+ ret = gst_matroska_demux_update_tracks (demux, &ebml);
}
break;
case GST_MATROSKA_ID_CLUSTER:
GST_DEBUG_OBJECT (demux,
"estimated duration as %" GST_TIME_FORMAT,
GST_TIME_ARGS (demux->common.segment.duration));
+
+ g_free (last);
}
}
demux->common.offset = demux->first_cluster_offset;
}
- GST_DEBUG_OBJECT (demux, "signaling no more pads");
- gst_element_no_more_pads (GST_ELEMENT (demux));
+ if (demux->deferred_seek_event) {
+ GstEvent *seek_event;
+ GstPad *seek_pad;
+ seek_event = demux->deferred_seek_event;
+ seek_pad = demux->deferred_seek_pad;
+ demux->deferred_seek_event = NULL;
+ demux->deferred_seek_pad = NULL;
+ GST_DEBUG_OBJECT (demux,
+ "Handling deferred seek event: %" GST_PTR_FORMAT, seek_event);
+ gst_matroska_demux_handle_seek_event (demux, seek_pad,
+ seek_event);
+ gst_event_unref (seek_event);
+ }
+
/* send initial segment - we wait till we know the first
incoming timestamp, so we can properly set the start of
the segment. */
}
case GST_MATROSKA_ID_POSITION:
case GST_MATROSKA_ID_ENCRYPTEDBLOCK:
+ /* The WebM doesn't support the EncryptedBlock element.
+ * The Matroska spec doesn't give us more detail, how to parse this element,
+ * for example the field TransformID isn't specified yet.*/
case GST_MATROSKA_ID_SILENTTRACKS:
GST_DEBUG_OBJECT (demux,
"Skipping Cluster subelement 0x%x - ignoring", id);
static GstStaticCaps intra_caps = GST_STATIC_CAPS ("image/jpeg; "
"video/x-raw; image/png; video/x-dv; video/x-huffyuv; video/x-ffv; "
"video/x-compressed-yuv");
+ GstCaps *tmp = gst_static_caps_get (&intra_caps);
+
context->intra_only =
- gst_caps_can_intersect (gst_static_caps_get (&intra_caps), caps);
+ gst_caps_can_intersect (tmp, caps);
+ gst_caps_unref(tmp);
}
if (buf)
gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, priv, NULL);
gst_buffer_unref (priv);
} else {
- GST_WARNING ("No codec data found, assuming output is byte-stream");
- gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, "byte-stream",
- NULL);
+ GST_WARNING ("No AV1 codec data found!");
}
*codec_name = g_strdup_printf ("AOM AV1");
} else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) {
*riff_audio_fmt = auds.format;
/* FIXME: Handle reorder map */
- caps = gst_riff_create_audio_caps (auds.format, NULL, &auds, NULL,
- codec_data, codec_name, NULL);
+ caps = gst_riff_create_audio_caps (auds.format, NULL, &auds, codec_data,
+ NULL, codec_name, NULL);
if (codec_data)
gst_buffer_unref (codec_data);
demux->max_gap_time = g_value_get_uint64 (value);
GST_OBJECT_UNLOCK (demux);
break;
+ case PROP_MAX_BACKTRACK_DISTANCE:
+ GST_OBJECT_LOCK (demux);
+ demux->max_backtrack_distance = g_value_get_uint (value);
+ GST_OBJECT_UNLOCK (demux);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_uint64 (value, demux->max_gap_time);
GST_OBJECT_UNLOCK (demux);
break;
+ case PROP_MAX_BACKTRACK_DISTANCE:
+ GST_OBJECT_LOCK (demux);
+ g_value_set_uint (value, demux->max_backtrack_distance);
+ GST_OBJECT_UNLOCK (demux);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
+static const gchar *
+gst_matroska_track_encryption_algorithm_name (gint val)
+{
+ GEnumValue *en;
+ GEnumClass *enum_class =
+ g_type_class_ref (MATROSKA_TRACK_ENCRYPTION_ALGORITHM_TYPE);
+ en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
+ return en ? en->value_nick : NULL;
+}
+
+static const gchar *
+gst_matroska_track_encryption_cipher_mode_name (gint val)
+{
+ GEnumValue *en;
+ GEnumClass *enum_class =
+ g_type_class_ref (MATROSKA_TRACK_ENCRYPTION_CIPHER_MODE_TYPE);
+ en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
+ return en ? en->value_nick : NULL;
+}
+
+static const gchar *
+gst_matroska_track_encoding_scope_name (gint val)
+{
+ GEnumValue *en;
+ GEnumClass *enum_class =
+ g_type_class_ref (MATROSKA_TRACK_ENCODING_SCOPE_TYPE);
+
+ en = g_enum_get_value (G_ENUM_CLASS (enum_class), val);
+ return en ? en->value_nick : NULL;
+}
+
gboolean
gst_matroska_demux_plugin_init (GstPlugin * plugin)
{