X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fmatroska%2Fmatroska-demux.c;h=824110477c4c46c937a6325174ba217182a83966;hb=93ddea2a70991f24f2b4de81d2d9beb0136c9a5e;hp=489c00a0420ae3909242f5e5a096d7628ba45503;hpb=0fad053497d46bba8b9785ae9c33fa29368ed7a1;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 489c00a..8241104 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -80,13 +80,14 @@ GST_DEBUG_CATEGORY_STATIC (matroskademux_debug); enum { - ARG_0, - ARG_METADATA, - ARG_STREAMINFO, - ARG_MAX_GAP_TIME + PROP_0, + PROP_METADATA, + PROP_STREAMINFO, + PROP_MAX_GAP_TIME }; #define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND) +#define INVALID_DATA_THRESHOLD (2 * 1024 * 1024) static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -172,7 +173,7 @@ static GstCaps /* stream methods */ static void gst_matroska_demux_reset (GstElement * element); static gboolean perform_seek_to_offset (GstMatroskaDemux * demux, - gdouble rate, guint64 offset, guint32 seqnum); + gdouble rate, guint64 offset, guint32 seqnum, GstSeekFlags flags); /* gobject functions */ static void gst_matroska_demux_set_property (GObject * object, @@ -208,7 +209,7 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass) gobject_class->get_property = gst_matroska_demux_get_property; gobject_class->set_property = gst_matroska_demux_set_property; - g_object_class_install_property (gobject_class, ARG_MAX_GAP_TIME, + g_object_class_install_property (gobject_class, PROP_MAX_GAP_TIME, g_param_spec_uint64 ("max-gap-time", "Maximum gap time", "The demuxer sends out segment events for skipping " "gaps longer than this (0 = disabled).", 0, G_MAXUINT64, @@ -227,19 +228,18 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass) GST_DEBUG_FUNCPTR (gst_matroska_demux_get_index); #endif - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&video_src_templ)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&audio_src_templ)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&subtitle_src_templ)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&sink_templ)); + gst_element_class_add_static_pad_template (gstelement_class, + &video_src_templ); + gst_element_class_add_static_pad_template (gstelement_class, + &audio_src_templ); + gst_element_class_add_static_pad_template (gstelement_class, + &subtitle_src_templ); + gst_element_class_add_static_pad_template (gstelement_class, &sink_templ); gst_element_class_set_static_metadata (gstelement_class, "Matroska demuxer", "Codec/Demuxer", "Demuxes Matroska/WebM streams into video/audio/subtitles", - "GStreamer maintainers "); + "GStreamer maintainers "); } static void @@ -306,7 +306,11 @@ gst_matroska_demux_reset (GstElement * element) demux->to_time = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = 0; + demux->cluster_prevsize = 0; + demux->seen_cluster_prevsize = FALSE; demux->next_cluster_offset = 0; + demux->stream_last_time = GST_CLOCK_TIME_NONE; + demux->last_cluster_offset = 0; demux->index_offset = 0; demux->seekable = FALSE; demux->need_segment = FALSE; @@ -390,6 +394,189 @@ gst_matroska_demux_add_stream_headers_to_caps (GstMatroskaDemux * demux, } static GstFlowReturn +gst_matroska_demux_parse_colour (GstMatroskaDemux * demux, GstEbmlRead * ebml, + GstMatroskaTrackVideoContext * video_context) +{ + GstFlowReturn ret; + GstVideoColorimetry colorimetry; + guint32 id; + guint64 num; + + colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN; + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN; + colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN; + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN; + + DEBUG_ELEMENT_START (demux, ebml, "TrackVideoColour"); + + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + goto beach; + + while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) { + if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK) + goto beach; + + switch (id) { + case GST_MATROSKA_ID_VIDEOMATRIXCOEFFICIENTS:{ + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + goto beach; + + switch (num) { + case 0: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_RGB; + break; + case 1: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT709; + break; + case 2: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN; + break; + case 4: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_FCC; + break; + /* FIXME: "5: BT470BG" is undefined in GstVideoColorMatrix + * but it's functionally same as "6: BT601" */ + case 5: + case 6: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT601; + break; + case 7: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_SMPTE240M; + break; + case 9: + colorimetry.matrix = GST_VIDEO_COLOR_MATRIX_BT2020; + break; + default: + GST_FIXME_OBJECT (demux, "Unsupported color matrix coefficients %" + G_GUINT64_FORMAT, num); + break; + } + break; + } + + case GST_MATROSKA_ID_VIDEORANGE:{ + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + goto beach; + + switch (num) { + case 0: + colorimetry.range = GST_VIDEO_COLOR_RANGE_UNKNOWN; + break; + case 1: + colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235; + break; + case 2: + colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255; + break; + default: + GST_FIXME_OBJECT (demux, "Unsupported color range %" + G_GUINT64_FORMAT, num); + break; + } + break; + } + + case GST_MATROSKA_ID_VIDEOTRANSFERCHARACTERISTICS:{ + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + goto beach; + + switch (num) { + /* FIXME: "6: BT601" and "14: BT2020_10" are undefined in + * GstVideoTransferFunction, but functionally same as "1: BT709" */ + case 1: + case 6: + case 14: + colorimetry.transfer = GST_VIDEO_TRANSFER_BT709; + break; + case 2: + colorimetry.transfer = GST_VIDEO_TRANSFER_UNKNOWN; + break; + case 4: + colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA22; + break; + case 5: + colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA28; + break; + case 7: + colorimetry.transfer = GST_VIDEO_TRANSFER_SMPTE240M; + break; + case 8: + colorimetry.transfer = GST_VIDEO_TRANSFER_GAMMA10; + break; + case 9: + colorimetry.transfer = GST_VIDEO_TRANSFER_LOG100; + break; + case 10: + colorimetry.transfer = GST_VIDEO_TRANSFER_LOG316; + break; + case 13: + colorimetry.transfer = GST_VIDEO_TRANSFER_SRGB; + break; + case 15: + colorimetry.transfer = GST_VIDEO_TRANSFER_BT2020_12; + break; + default: + GST_FIXME_OBJECT (demux, + "Unsupported color transfer characteristics %" + G_GUINT64_FORMAT, num); + break; + } + break; + } + + case GST_MATROSKA_ID_VIDEOPRIMARIES:{ + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + goto beach; + + switch (num) { + case 1: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709; + break; + case 2: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_UNKNOWN; + break; + case 4: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470M; + break; + case 5: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT470BG; + break; + case 6: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M; + break; + case 7: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE240M; + break; + case 8: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_FILM; + break; + case 9: + colorimetry.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020; + break; + default: + GST_FIXME_OBJECT (demux, "Unsupported color primaries %" + G_GUINT64_FORMAT, num); + break; + } + break; + } + + default: + GST_FIXME_OBJECT (demux, "Unsupported subelement 0x%x in Colour", id); + ret = gst_ebml_read_skip (ebml); + break; + } + } + + memcpy (&video_context->colorimetry, &colorimetry, + sizeof (GstVideoColorimetry)); + +beach: + DEBUG_ELEMENT_STOP (demux, ebml, "TrackVideoColour", ret); + return ret; +} + +static GstFlowReturn gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) { GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux); @@ -397,11 +584,11 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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; - GstTagList *list = NULL; GstEvent *stream_start; gchar *codec = NULL; gchar *stream_id; @@ -433,6 +620,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) context->to_offset = G_MAXINT64; context->alignment = 1; 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); @@ -675,13 +864,18 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) break; - if (num) - context->flags |= GST_MATROSKA_VIDEOTRACK_INTERLACED; + if (num == 1) + videocontext->interlace_mode = + GST_MATROSKA_INTERLACE_MODE_INTERLACED; + else if (num == 2) + videocontext->interlace_mode = + GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE; else - context->flags &= ~GST_MATROSKA_VIDEOTRACK_INTERLACED; - GST_DEBUG_OBJECT (demux, "TrackVideoInterlaced: %d", - (context->flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) ? 1 : - 0); + videocontext->interlace_mode = + GST_MATROSKA_INTERLACE_MODE_UNKNOWN; + + GST_DEBUG_OBJECT (demux, "video track interlacing mode: %d", + videocontext->interlace_mode); break; } @@ -731,11 +925,67 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) break; } + /* color info */ + case GST_MATROSKA_ID_VIDEOCOLOUR:{ + ret = gst_matroska_demux_parse_colour (demux, ebml, videocontext); + break; + } + + case GST_MATROSKA_ID_VIDEOSTEREOMODE: + { + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + GST_DEBUG_OBJECT (demux, "StereoMode: %" G_GUINT64_FORMAT, num); + + switch (num) { + case GST_MATROSKA_STEREO_MODE_SBS_RL: + videocontext->multiview_flags = + GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST; + /* fall through */ + case GST_MATROSKA_STEREO_MODE_SBS_LR: + videocontext->multiview_mode = + GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE; + break; + case GST_MATROSKA_STEREO_MODE_TB_RL: + videocontext->multiview_flags = + GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST; + /* fall through */ + case GST_MATROSKA_STEREO_MODE_TB_LR: + videocontext->multiview_mode = + GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM; + break; + case GST_MATROSKA_STEREO_MODE_CHECKER_RL: + videocontext->multiview_flags = + GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST; + /* fall through */ + case GST_MATROSKA_STEREO_MODE_CHECKER_LR: + videocontext->multiview_mode = + GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD; + break; + case GST_MATROSKA_STEREO_MODE_FBF_RL: + videocontext->multiview_flags = + GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST; + /* fall through */ + case GST_MATROSKA_STEREO_MODE_FBF_LR: + videocontext->multiview_mode = + GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME; + /* FIXME: In frame-by-frame mode, left/right frame buffers are + * laced within one block, and we'll need to apply FIRST_IN_BUNDLE + * accordingly. See http://www.matroska.org/technical/specs/index.html#StereoMode */ + GST_FIXME_OBJECT (demux, + "Frame-by-frame stereoscopic mode not fully implemented"); + break; + } + break; + } + default: GST_WARNING_OBJECT (demux, "Unknown TrackVideo subelement 0x%x - ignoring", id); /* fall through */ - case GST_MATROSKA_ID_VIDEOSTEREOMODE: case GST_MATROSKA_ID_VIDEODISPLAYUNIT: case GST_MATROSKA_ID_VIDEOPIXELCROPBOTTOM: case GST_MATROSKA_ID_VIDEOPIXELCROPTOP: @@ -889,6 +1139,34 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) break; } + /* codec delay */ + case GST_MATROSKA_ID_CODECDELAY:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + context->codec_delay = num; + + GST_DEBUG_OBJECT (demux, "CodecDelay: %" GST_TIME_FORMAT, + GST_TIME_ARGS (num)); + break; + } + + /* codec delay */ + case GST_MATROSKA_ID_SEEKPREROLL:{ + guint64 num; + + if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK) + break; + + context->seek_preroll = num; + + GST_DEBUG_OBJECT (demux, "SeekPreroll: %" GST_TIME_FORMAT, + GST_TIME_ARGS (num)); + break; + } + /* name of this track */ case GST_MATROSKA_ID_TRACKNAME:{ gchar *text; @@ -1073,13 +1351,18 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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); - if (context) { - gst_matroska_track_free (context); - } + gst_matroska_track_free (context); return ret; } + /* check for a cached track taglist */ + cached_taglist = + (GstTagList *) g_hash_table_lookup (demux->common.cached_track_taglists, + GUINT_TO_POINTER (context->uid)); + if (cached_taglist) + gst_tag_list_insert (context->tags, cached_taglist, GST_TAG_MERGE_APPEND); + /* now create the GStreamer connectivity */ switch (context->type) { case GST_MATROSKA_TRACK_TYPE_VIDEO:{ @@ -1093,7 +1376,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) context->codec_priv_size, &codec, &riff_fourcc); if (codec) { - list = gst_tag_list_new (GST_TAG_VIDEO_CODEC, codec, NULL); + gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE, + GST_TAG_VIDEO_CODEC, codec, NULL); + context->tags_changed = TRUE; g_free (codec); } break; @@ -1110,7 +1395,9 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) &codec, &riff_audio_fmt); if (codec) { - list = gst_tag_list_new (GST_TAG_AUDIO_CODEC, codec, NULL); + gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE, + GST_TAG_AUDIO_CODEC, codec, NULL); + context->tags_changed = TRUE; g_free (codec); } break; @@ -1146,13 +1433,16 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) if (context->language) { const gchar *lang; - if (!list) - list = gst_tag_list_new_empty (); - /* Matroska contains ISO 639-2B codes, we want ISO 639-1 */ lang = gst_tag_get_language_code (context->language); - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE, GST_TAG_LANGUAGE_CODE, (lang) ? lang : context->language, NULL); + + if (context->name) { + gst_tag_list_add (context->tags, GST_TAG_MERGE_REPLACE, + GST_TAG_TITLE, context->name, NULL); + } + context->tags_changed = TRUE; } if (caps == NULL) { @@ -1202,8 +1492,6 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) GST_INFO_OBJECT (demux, "Adding pad '%s' with caps %" GST_PTR_FORMAT, padname, caps); - context->pending_tags = list; - gst_pad_set_element_private (context->pad, context); gst_pad_use_fixed_caps (context->pad); @@ -1211,7 +1499,8 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) stream_id = gst_pad_create_stream_id_printf (context->pad, GST_ELEMENT_CAST (demux), - "%03" G_GUINT64_FORMAT, context->uid); + "%03" G_GUINT64_FORMAT ":%03" G_GUINT64_FORMAT, + context->num, context->uid); stream_start = gst_pad_get_sticky_event (demux->common.sinkpad, GST_EVENT_STREAM_START, 0); @@ -1235,10 +1524,36 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) stream_flags |= GST_STREAM_FLAG_SPARSE; if (context->flags & GST_MATROSKA_TRACK_DEFAULT) stream_flags |= GST_STREAM_FLAG_SELECT; + else if (!(context->flags & GST_MATROSKA_TRACK_ENABLED)) + stream_flags |= GST_STREAM_FLAG_UNSELECT; + gst_event_set_stream_flags (stream_start, stream_flags); gst_pad_push_event (context->pad, stream_start); gst_pad_set_caps (context->pad, context->caps); + + if (demux->common.global_tags) { + GstEvent *tag_event; + + gst_tag_list_add (demux->common.global_tags, GST_TAG_MERGE_REPLACE, + GST_TAG_CONTAINER_FORMAT, "Matroska", NULL); + GST_DEBUG_OBJECT (context->pad, "Sending global_tags %p: %" GST_PTR_FORMAT, + demux->common.global_tags, demux->common.global_tags); + + tag_event = + gst_event_new_tag (gst_tag_list_copy (demux->common.global_tags)); + + gst_pad_push_event (context->pad, tag_event); + } + + if (G_UNLIKELY (context->tags_changed)) { + GST_DEBUG_OBJECT (context->pad, "Sending tags %p: %" + GST_PTR_FORMAT, context->tags, context->tags); + gst_pad_push_event (context->pad, + gst_event_new_tag (gst_tag_list_copy (context->tags))); + context->tags_changed = FALSE; + } + gst_element_add_pad (GST_ELEMENT (demux), context->pad); gst_flow_combiner_add_pad (demux->flowcombiner, context->pad); @@ -1423,14 +1738,15 @@ gst_matroska_demux_send_tags (GstMatroskaDemux * demux) { gint i; - if (G_UNLIKELY (demux->common.global_tags != NULL)) { + if (G_UNLIKELY (demux->common.global_tags_changed)) { GstEvent *tag_event; gst_tag_list_add (demux->common.global_tags, GST_TAG_MERGE_REPLACE, GST_TAG_CONTAINER_FORMAT, "Matroska", NULL); GST_DEBUG_OBJECT (demux, "Sending global_tags %p : %" GST_PTR_FORMAT, demux->common.global_tags, demux->common.global_tags); - tag_event = gst_event_new_tag (demux->common.global_tags); + tag_event = + gst_event_new_tag (gst_tag_list_copy (demux->common.global_tags)); for (i = 0; i < demux->common.src->len; i++) { GstMatroskaTrackContext *stream; @@ -1440,7 +1756,7 @@ gst_matroska_demux_send_tags (GstMatroskaDemux * demux) } gst_event_unref (tag_event); - demux->common.global_tags = NULL; + demux->common.global_tags_changed = FALSE; } g_assert (demux->common.src->len == demux->common.num_streams); @@ -1449,13 +1765,13 @@ gst_matroska_demux_send_tags (GstMatroskaDemux * demux) stream = g_ptr_array_index (demux->common.src, i); - if (G_UNLIKELY (stream->pending_tags != NULL)) { - GST_DEBUG_OBJECT (demux, "Sending pending_tags %p for pad %s:%s : %" - GST_PTR_FORMAT, stream->pending_tags, - GST_DEBUG_PAD_NAME (stream->pad), stream->pending_tags); + if (G_UNLIKELY (stream->tags_changed)) { + GST_DEBUG_OBJECT (demux, "Sending tags %p for pad %s:%s : %" + GST_PTR_FORMAT, stream->tags, + GST_DEBUG_PAD_NAME (stream->pad), stream->tags); gst_pad_push_event (stream->pad, - gst_event_new_tag (stream->pending_tags)); - stream->pending_tags = NULL; + gst_event_new_tag (gst_tag_list_copy (stream->tags))); + stream->tags_changed = FALSE; } } } @@ -1469,6 +1785,12 @@ gst_matroska_demux_element_send_event (GstElement * element, GstEvent * event) g_return_val_if_fail (event != NULL, FALSE); 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; + } res = gst_matroska_demux_handle_seek_event (demux, NULL, event); } else { GST_WARNING_OBJECT (demux, "Unhandled event of type %s", @@ -1500,6 +1822,7 @@ gst_matroska_demux_move_to_entry (GstMatroskaDemux * demux, /* update the time */ gst_matroska_read_common_reset_streams (&demux->common, entry->time, TRUE); + gst_flow_combiner_reset (demux->flowcombiner); demux->common.segment.position = entry->time; demux->seek_block = entry->block; demux->seek_first = TRUE; @@ -1538,12 +1861,13 @@ gst_matroska_cluster_compare (gint64 * i1, gint64 * i2) /* searches for a cluster start from @pos, * return GST_FLOW_OK and cluster position in @pos if found */ static GstFlowReturn -gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) +gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos, + gboolean forward) { gint64 newpos = *pos; gint64 orig_offset; GstFlowReturn ret = GST_FLOW_OK; - const guint chunk = 64 * 1024; + const guint chunk = 128 * 1024; GstBuffer *buf = NULL; GstMapInfo map; gpointer data = NULL; @@ -1555,8 +1879,8 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) orig_offset = demux->common.offset; - GST_LOG_OBJECT (demux, "searching cluster following offset %" G_GINT64_FORMAT, - *pos); + GST_LOG_OBJECT (demux, "searching cluster %s offset %" G_GINT64_FORMAT, + forward ? "following" : "preceding", *pos); if (demux->clusters) { gint64 *cpos; @@ -1564,7 +1888,7 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) cpos = gst_util_array_binary_search (demux->clusters->data, demux->clusters->len, sizeof (gint64), (GCompareDataFunc) gst_matroska_cluster_compare, - GST_SEARCH_MODE_AFTER, pos, NULL); + forward ? GST_SEARCH_MODE_AFTER : GST_SEARCH_MODE_BEFORE, pos, NULL); /* sanity check */ if (cpos) { GST_DEBUG_OBJECT (demux, @@ -1584,13 +1908,23 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) while (1) { GstByteReader reader; gint cluster_pos; + guint toread = chunk; + if (!forward) { + /* never read beyond the requested target */ + if (G_UNLIKELY (newpos < chunk)) { + toread = newpos; + newpos = 0; + } else { + newpos -= chunk; + } + } if (buf != NULL) { gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); buf = NULL; } - ret = gst_pad_pull_range (demux->common.sinkpad, newpos, chunk, &buf); + ret = gst_pad_pull_range (demux->common.sinkpad, newpos, toread, &buf); if (ret != GST_FLOW_OK) break; GST_DEBUG_OBJECT (demux, @@ -1609,16 +1943,25 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) } gst_byte_reader_init (&reader, data, size); - resume: - cluster_pos = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, - GST_MATROSKA_ID_CLUSTER, 0, gst_byte_reader_get_remaining (&reader)); + cluster_pos = -1; + while (1) { + gint found = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff, + GST_MATROSKA_ID_CLUSTER, 0, gst_byte_reader_get_remaining (&reader)); + if (forward) { + cluster_pos = found; + break; + } + /* need last occurrence when searching backwards */ + if (found >= 0) { + cluster_pos = gst_byte_reader_get_pos (&reader) + found; + gst_byte_reader_skip (&reader, found + 4); + } else { + break; + } + } + if (cluster_pos >= 0) { newpos += cluster_pos; - /* prepare resuming at next byte */ - if (!gst_byte_reader_skip (&reader, cluster_pos + 1)) { - GST_DEBUG_OBJECT (demux, "Need more data -> continue"); - continue; - } GST_DEBUG_OBJECT (demux, "found cluster ebml id at offset %" G_GINT64_FORMAT, newpos); /* extra checks whether we really sync'ed to a cluster: @@ -1636,7 +1979,7 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) GST_ELEMENT_CAST (demux), &id, &length, &needed); if (ret != GST_FLOW_OK) { GST_DEBUG_OBJECT (demux, "need more data -> continue"); - continue; + goto next; } g_assert (id == GST_MATROSKA_ID_CLUSTER); GST_DEBUG_OBJECT (demux, "cluster size %" G_GUINT64_FORMAT ", prefix %d", @@ -1651,16 +1994,18 @@ gst_matroska_demux_search_cluster (GstMatroskaDemux * demux, gint64 * pos) ret = gst_matroska_read_common_peek_id_length_pull (&demux->common, GST_ELEMENT_CAST (demux), &id, &length, &needed); if (ret != GST_FLOW_OK) - goto resume; + goto next; GST_DEBUG_OBJECT (demux, "next element is %scluster", id == GST_MATROSKA_ID_CLUSTER ? "" : "not "); if (id == GST_MATROSKA_ID_CLUSTER) break; - /* not ok, resume */ - goto resume; + next: + if (forward) + newpos += 1; } else { /* partial cluster id may have been in tail of buffer */ - newpos += MAX (gst_byte_reader_get_remaining (&reader), 4) - 3; + newpos += + forward ? MAX (gst_byte_reader_get_remaining (&reader), 4) - 3 : 3; } } @@ -1684,19 +2029,18 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time) GstMatroskaIndex *entry = NULL; GstMatroskaReadState current_state; GstClockTime otime, prev_cluster_time, current_cluster_time, cluster_time; - gint64 opos, newpos, startpos = 0, current_offset; + GstClockTime atime; + gint64 opos, newpos, current_offset; gint64 prev_cluster_offset = -1, current_cluster_offset, cluster_offset; - const guint chunk = 64 * 1024; + gint64 apos, maxpos; + guint64 cluster_size = 0; GstFlowReturn ret; guint64 length; guint32 id; guint needed; - /* (under)estimate new position, resync using cluster ebml id, - * and scan forward to appropriate cluster - * (and re-estimate if need to go backward) */ - - prev_cluster_time = GST_CLOCK_TIME_NONE; + /* estimate new position, resync using cluster ebml id, + * and bisect further or scan forward to appropriate cluster */ /* store some current state */ current_state = demux->common.state; @@ -1708,77 +2052,78 @@ gst_matroska_demux_search_pos (GstMatroskaDemux * demux, GstClockTime time) demux->common.state = GST_MATROSKA_READ_STATE_SCANNING; - /* estimate using start and current position */ + /* estimate using start and last known cluster */ GST_OBJECT_LOCK (demux); - opos = demux->common.offset - demux->common.ebml_segment_start; - otime = demux->common.segment.position; + apos = demux->first_cluster_offset; + atime = demux->stream_start_time; + opos = demux->last_cluster_offset; + otime = demux->stream_last_time; GST_OBJECT_UNLOCK (demux); /* sanitize */ - time = MAX (time, demux->stream_start_time); + time = MAX (time, atime); + otime = MAX (otime, atime); + opos = MAX (opos, apos); + + maxpos = gst_matroska_read_common_get_length (&demux->common); - /* avoid division by zero in first estimation below */ - if (otime <= demux->stream_start_time) - otime = time; + /* invariants; + * apos <= opos + * atime <= otime + * apos always refer to a cluster before target time; + * opos may or may not be after target time, but if it is once so, + * then also in next iteration + * */ retry: GST_LOG_OBJECT (demux, + "apos: %" G_GUINT64_FORMAT ", atime: %" GST_TIME_FORMAT ", %" + GST_TIME_FORMAT " in stream time, " "opos: %" G_GUINT64_FORMAT ", otime: %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT " in stream time (start %" GST_TIME_FORMAT "), time %" - GST_TIME_FORMAT, opos, GST_TIME_ARGS (otime), - GST_TIME_ARGS (otime - demux->stream_start_time), + GST_TIME_FORMAT, apos, GST_TIME_ARGS (atime), + GST_TIME_ARGS (atime - demux->stream_start_time), opos, + GST_TIME_ARGS (otime), GST_TIME_ARGS (otime - demux->stream_start_time), GST_TIME_ARGS (demux->stream_start_time), GST_TIME_ARGS (time)); - if (otime <= demux->stream_start_time) { - newpos = 0; + g_assert (atime <= otime); + g_assert (apos <= opos); + if (time == GST_CLOCK_TIME_NONE) { + GST_DEBUG_OBJECT (demux, "searching last cluster"); + newpos = maxpos; + if (newpos == -1) { + GST_DEBUG_OBJECT (demux, "unknown file size; bailing out"); + goto exit; + } + } else if (otime <= atime) { + newpos = apos; } else { - newpos = - gst_util_uint64_scale (opos - demux->common.ebml_segment_start, - time - demux->stream_start_time, - otime - demux->stream_start_time) - chunk; - if (newpos < 0) - newpos = 0; + newpos = apos + + gst_util_uint64_scale (opos - apos, time - atime, otime - atime); + if (maxpos != -1 && newpos > maxpos) + newpos = maxpos; } - /* favour undershoot */ - newpos = newpos * 90 / 100; - newpos += demux->common.ebml_segment_start; GST_DEBUG_OBJECT (demux, "estimated offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT, GST_TIME_ARGS (time), newpos); - /* and at least start scanning before previous scan start to avoid looping */ - startpos = startpos * 90 / 100; - if (startpos && startpos < newpos) - newpos = startpos; - - /* read in at newpos and scan for ebml cluster id */ - startpos = newpos; - while (1) { - - ret = gst_matroska_demux_search_cluster (demux, &newpos); - if (ret == GST_FLOW_EOS) { - /* heuristic HACK */ - newpos = startpos * 80 / 100; - GST_DEBUG_OBJECT (demux, "EOS; " - "new estimated offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT, - GST_TIME_ARGS (time), newpos); - startpos = newpos; - continue; - } else if (ret != GST_FLOW_OK) { + /* search backwards */ + if (newpos > apos) { + ret = gst_matroska_demux_search_cluster (demux, &newpos, FALSE); + if (ret != GST_FLOW_OK) goto exit; - } else { - break; - } } /* then start scanning and parsing for cluster time, - * re-estimate if overshoot, otherwise next cluster and so on */ + * re-estimate if possible, otherwise next cluster and so on */ + /* note that each re-estimate is entered with a change in apos or opos, + * avoiding infinite loop */ demux->common.offset = newpos; demux->cluster_time = cluster_time = GST_CLOCK_TIME_NONE; + cluster_size = 0; + prev_cluster_time = GST_CLOCK_TIME_NONE; while (1) { - guint64 cluster_size = 0; - /* peek and parse some elements */ ret = gst_matroska_read_common_peek_id_length_pull (&demux->common, GST_ELEMENT_CAST (demux), &id, &length, &needed); @@ -1805,6 +2150,12 @@ retry: GST_DEBUG_OBJECT (demux, "found cluster at offset %" G_GINT64_FORMAT " with time %" GST_TIME_FORMAT, cluster_offset, GST_TIME_ARGS (cluster_time)); + if (time == GST_CLOCK_TIME_NONE) { + GST_DEBUG_OBJECT (demux, "found last cluster"); + prev_cluster_time = cluster_time; + prev_cluster_offset = cluster_offset; + break; + } if (cluster_time > time) { GST_DEBUG_OBJECT (demux, "overshot target"); /* cluster overshoots */ @@ -1825,6 +2176,30 @@ retry: goto retry; } } else { + /* cluster undershoots */ + GST_DEBUG_OBJECT (demux, "undershot target"); + /* ok if close enough */ + if (GST_CLOCK_DIFF (cluster_time, time) < 5 * GST_SECOND) { + GST_DEBUG_OBJECT (demux, "target close enough"); + prev_cluster_time = cluster_time; + prev_cluster_offset = cluster_offset; + break; + } + if (otime > time) { + /* we are in between atime and otime => can bisect if worthwhile */ + if (prev_cluster_time != GST_CLOCK_TIME_NONE && + cluster_time > prev_cluster_time && + (GST_CLOCK_DIFF (prev_cluster_time, cluster_time) * 10 < + GST_CLOCK_DIFF (cluster_time, time))) { + /* we moved at least one cluster forward, + * and it looks like target is still far away, + * let's estimate again */ + GST_DEBUG_OBJECT (demux, "bisecting with new apos"); + apos = cluster_offset; + atime = cluster_time; + goto retry; + } + } /* cluster undershoots, goto next one */ prev_cluster_time = cluster_time; prev_cluster_offset = cluster_offset; @@ -2049,13 +2424,19 @@ next: } finish: - if (keyunit) { + if (keyunit && seeksegment.rate > 0) { GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment start from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (entry->time)); seeksegment.start = MAX (entry->time, demux->stream_start_time); seeksegment.position = seeksegment.start; seeksegment.time = seeksegment.start - demux->stream_start_time; + } else if (keyunit) { + GST_DEBUG_OBJECT (demux, "seek to key unit, adjusting segment stop from %" + GST_TIME_FORMAT " to %" GST_TIME_FORMAT, + GST_TIME_ARGS (seeksegment.stop), GST_TIME_ARGS (entry->time)); + seeksegment.stop = MAX (entry->time, demux->stream_start_time); + seeksegment.position = seeksegment.stop; } if (demux->streaming) { @@ -2069,7 +2450,7 @@ finish: /* upstream takes care of flushing and all that * ... and newsegment event handling takes care of the rest */ return perform_seek_to_offset (demux, rate, - entry->pos + demux->common.ebml_segment_start, seqnum); + entry->pos + demux->common.ebml_segment_start, seqnum, flags); } exit: @@ -2112,6 +2493,7 @@ exit: demux->to_time = demux->common.segment.position; else demux->to_time = GST_CLOCK_TIME_NONE; + demux->segment_seqnum = seqnum; GST_OBJECT_UNLOCK (demux); /* restart our task since it might have been stopped when we did the @@ -2210,7 +2592,7 @@ gst_matroska_demux_handle_seek_push (GstMatroskaDemux * demux, GstPad * pad, /* seek to the first subindex or legacy index */ GST_INFO_OBJECT (demux, "Seeking to Cues at %" G_GUINT64_FORMAT, offset); return perform_seek_to_offset (demux, rate, offset, - gst_event_get_seqnum (event)); + gst_event_get_seqnum (event), GST_SEEK_FLAG_NONE); } /* well, we are handling it already */ @@ -2233,8 +2615,20 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstObject * parent, /* 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; } + + { + guint32 seqnum = gst_event_get_seqnum (event); + if (seqnum == demux->segment_seqnum) { + GST_LOG_OBJECT (pad, + "Drop duplicated SEEK event seqnum %" G_GUINT32_FORMAT, seqnum); + gst_event_unref (event); + return TRUE; + } + } + if (!demux->streaming) res = gst_matroska_demux_handle_seek_event (demux, pad, event); else @@ -2475,6 +2869,7 @@ gst_matroska_ebmlnum_sint (guint8 * data, guint size, gint64 * num) static void gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) { + GstClockTime gap_threshold; gint stream_nr; GST_OBJECT_LOCK (demux); @@ -2492,21 +2887,22 @@ gst_matroska_demux_sync_streams (GstMatroskaDemux * demux) "Checking for resync on stream %d (%" GST_TIME_FORMAT ")", stream_nr, GST_TIME_ARGS (context->pos)); - if (G_LIKELY (context->type != GST_MATROSKA_TRACK_TYPE_SUBTITLE)) { - GST_LOG_OBJECT (demux, "Skipping sync on non-subtitle stream"); - continue; - } + /* Only send gap events on non-subtitle streams if lagging way behind. + * The 0.5 second threshold for subtitle streams is also quite random. */ + if (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE) + gap_threshold = GST_SECOND / 2; + else + gap_threshold = 3 * GST_SECOND; - /* does it lag? 0.5 seconds is a random threshold... - * lag need only be considered if we have advanced into requested segment */ + /* 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->common.segment.position) && demux->common.segment.position > demux->common.segment.start && - context->pos + (GST_SECOND / 2) < demux->common.segment.position) { + context->pos + gap_threshold < demux->common.segment.position) { GstEvent *event; guint64 start = context->pos; - guint64 stop = demux->common.segment.position - (GST_SECOND / 2); + guint64 stop = demux->common.segment.position - gap_threshold; GST_DEBUG_OBJECT (demux, "Synchronizing stream %d with other by advancing time from %" @@ -2634,8 +3030,6 @@ gst_matroska_demux_push_codec_data_all (GstMatroskaDemux * demux) { gint stream_nr; - GST_OBJECT_LOCK (demux); - g_assert (demux->common.num_streams == demux->common.src->len); for (stream_nr = 0; stream_nr < demux->common.src->len; stream_nr++) { GstMatroskaTrackContext *stream; @@ -2660,7 +3054,6 @@ gst_matroska_demux_push_codec_data_all (GstMatroskaDemux * demux) } } - GST_OBJECT_UNLOCK (demux); } static GstFlowReturn @@ -2761,6 +3154,7 @@ gst_matroska_demux_add_wvpk_header (GstElement * element, GST_WRITE_UINT8 (data + 11, wvh.index_no); GST_WRITE_UINT32_LE (data + 12, wvh.total_samples); GST_WRITE_UINT32_LE (data + 16, wvh.block_index); + gst_buffer_unmap (newbuf, &outmap); /* Append data from buf: */ gst_buffer_copy_into (newbuf, *buf, GST_BUFFER_COPY_TIMESTAMPS | @@ -2862,6 +3256,33 @@ gst_matroska_demux_add_wvpk_header (GstElement * element, return GST_FLOW_OK; } +static GstFlowReturn +gst_matroska_demux_add_prores_header (GstElement * element, + GstMatroskaTrackContext * stream, GstBuffer ** buf) +{ + GstBuffer *newbuf = gst_buffer_new_allocate (NULL, 8, NULL); + GstMapInfo map; + guint32 frame_size; + + if (!gst_buffer_map (newbuf, &map, GST_MAP_WRITE)) { + GST_ERROR ("Failed to map newly allocated buffer"); + return GST_FLOW_ERROR; + } + + frame_size = gst_buffer_get_size (*buf); + + GST_WRITE_UINT32_BE (map.data, frame_size); + map.data[4] = 'i'; + map.data[5] = 'c'; + map.data[6] = 'p'; + map.data[7] = 'f'; + + gst_buffer_unmap (newbuf, &map); + *buf = gst_buffer_append (newbuf, *buf); + + return GST_FLOW_OK; +} + /* @text must be null-terminated */ static gboolean gst_matroska_demux_subtitle_chunk_has_tag (GstElement * element, @@ -2913,25 +3334,16 @@ gst_matroska_demux_check_subtitle_buffer (GstElement * element, if (!gst_buffer_get_size (*buf) || !gst_buffer_map (*buf, &map, GST_MAP_READ)) return GST_FLOW_OK; - /* Need \0-terminator at the end */ - if (map.data[map.size - 1] != '\0') { - newbuf = gst_buffer_new_and_alloc (map.size + 1); - - /* Copy old buffer and add a 0 at the end */ - gst_buffer_fill (newbuf, 0, map.data, map.size); - gst_buffer_memset (newbuf, map.size, 0, 1); + /* The subtitle buffer we push out should not include a NUL terminator as + * part of the data. */ + if (map.data[map.size - 1] == '\0') { + gst_buffer_set_size (*buf, map.size - 1); gst_buffer_unmap (*buf, &map); - - gst_buffer_copy_into (newbuf, *buf, - GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_FLAGS | - GST_BUFFER_COPY_META, 0, -1); - gst_buffer_unref (*buf); - *buf = newbuf; gst_buffer_map (*buf, &map, GST_MAP_READ); } if (!sub_stream->invalid_utf8) { - if (g_utf8_validate ((gchar *) map.data, map.size - 1, NULL)) { + if (g_utf8_validate ((gchar *) map.data, map.size, NULL)) { goto next; } GST_WARNING_OBJECT (element, "subtitle stream %" G_GUINT64_FORMAT @@ -3090,6 +3502,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, gboolean readblock = FALSE; guint32 id; guint64 block_duration = -1; + gint64 block_discardpadding = 0; GstBuffer *buf = NULL; GstMapInfo map; gint stream_num = -1, n, laces = 0; @@ -3254,6 +3667,13 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, break; } + case GST_MATROSKA_ID_DISCARDPADDING:{ + ret = gst_ebml_read_sint (ebml, &id, &block_discardpadding); + GST_DEBUG_OBJECT (demux, "DiscardPadding: %" GST_STIME_FORMAT, + GST_STIME_ARGS (block_discardpadding)); + break; + } + case GST_MATROSKA_ID_REFERENCEBLOCK:{ ret = gst_ebml_read_sint (ebml, &id, &referenceblock); GST_DEBUG_OBJECT (demux, "ReferenceBlock: %" G_GINT64_FORMAT, @@ -3362,7 +3782,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, GST_DEBUG_OBJECT (demux, "using stored seek position %" GST_TIME_FORMAT, GST_TIME_ARGS (demux->common.segment.position)); - clace_time = demux->common.segment.position + demux->stream_start_time; + clace_time = demux->common.segment.position; segment->position = GST_CLOCK_TIME_NONE; } segment->start = clace_time; @@ -3399,15 +3819,27 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, } /* else duration is diff between timecode of this and next block */ - /* For SimpleBlock, look at the keyframe bit in flags. Otherwise, - a ReferenceBlock implies that this is not a keyframe. In either - case, it only makes sense for video streams. */ if (stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO) { + /* For SimpleBlock, look at the keyframe bit in flags. Otherwise, + a ReferenceBlock implies that this is not a keyframe. In either + case, it only makes sense for video streams. */ if ((is_simpleblock && !(flags & 0x80)) || referenceblock) { delta_unit = TRUE; invisible_frame = ((flags & 0x08)) && (!strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP8) || - !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9)); + !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9) || + !strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_VIDEO_AV1)); + } + + /* 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) { + GST_LOG_OBJECT (demux, "Skipping non-keyframe on stream %d", + stream->index); + ret = GST_FLOW_OK; + goto done; } } @@ -3433,7 +3865,8 @@ 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->common.segment, + earliest_stream_time = + gst_segment_position_from_running_time (&demux->common.segment, GST_FORMAT_TIME, earliest_time); if (GST_CLOCK_TIME_IS_VALID (lace_time) && @@ -3478,12 +3911,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, goto next_lace; } - buffer_timestamp = gst_matroska_track_get_buffer_timestamp (stream, sub); - - if (!stream->dts_only) + if (!stream->dts_only) { GST_BUFFER_PTS (sub) = lace_time; - else + } else { GST_BUFFER_DTS (sub) = lace_time; + if (stream->intra_only) + GST_BUFFER_PTS (sub) = lace_time; + } + + buffer_timestamp = gst_matroska_track_get_buffer_timestamp (stream, sub); if (GST_CLOCK_TIME_IS_VALID (lace_time)) { GstClockTime last_stop_end; @@ -3625,9 +4061,49 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, for 32 bit samples, etc), or bad things will happen downstream as elements typically assume minimal alignment. Therefore, create an aligned copy if necessary. */ - g_assert (stream->alignment <= G_MEM_ALIGN); sub = gst_matroska_demux_align_buffer (demux, sub, stream->alignment); + if (!strcmp (stream->codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) { + guint64 start_clip = 0, end_clip = 0; + + /* Codec delay is part of the timestamps */ + if (GST_BUFFER_PTS_IS_VALID (sub) && stream->codec_delay) { + if (GST_BUFFER_PTS (sub) > stream->codec_delay) { + GST_BUFFER_PTS (sub) -= stream->codec_delay; + } else { + GST_BUFFER_PTS (sub) = 0; + + /* Opus GstAudioClippingMeta units are scaled by 48000/sample_rate. + That is, if a Opus track has audio encoded at 24000 Hz and 132 + samples need to be clipped, GstAudioClippingMeta.start will be + set to 264. (This is also the case for buffer offsets.) + Opus sample rates are always divisors of 48000 Hz, which is the + maximum allowed sample rate. */ + start_clip = + gst_util_uint64_scale_round (stream->codec_delay, 48000, + GST_SECOND); + + if (GST_BUFFER_DURATION_IS_VALID (sub)) { + if (GST_BUFFER_DURATION (sub) > stream->codec_delay) + GST_BUFFER_DURATION (sub) -= stream->codec_delay; + else + GST_BUFFER_DURATION (sub) = 0; + } + } + } + + if (block_discardpadding) { + end_clip = + gst_util_uint64_scale_round (block_discardpadding, 48000, + GST_SECOND); + } + + if (start_clip || end_clip) { + gst_buffer_add_audio_clipping_meta (sub, GST_FORMAT_DEFAULT, + start_clip, end_clip); + } + } + if (GST_BUFFER_PTS_IS_VALID (sub)) { stream->pos = GST_BUFFER_PTS (sub); if (GST_BUFFER_DURATION_IS_VALID (sub)) @@ -3650,7 +4126,8 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, } } /* combine flows */ - ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); + ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, + stream->pad, ret); next_lace: size -= lace_size[n]; @@ -3676,7 +4153,8 @@ eos: stream->eos = TRUE; ret = GST_FLOW_OK; /* combine flows */ - ret = gst_flow_combiner_update_flow (demux->flowcombiner, ret); + ret = gst_flow_combiner_update_pad_flow (demux->flowcombiner, stream->pad, + ret); goto done; } invalid_lacing: @@ -3956,7 +4434,8 @@ gst_matroska_demux_check_parse_error (GstMatroskaDemux * demux) * search for cluster mark following current pos */ pos = demux->common.offset; GST_WARNING_OBJECT (demux, "parse error, looking for next cluster"); - if ((ret = gst_matroska_demux_search_cluster (demux, &pos)) != GST_FLOW_OK) { + if ((ret = gst_matroska_demux_search_cluster (demux, &pos, TRUE)) != + GST_FLOW_OK) { /* did not work, give up */ return ret; } else { @@ -4182,8 +4661,22 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, break; case GST_MATROSKA_READ_STATE_SCANNING: if (id != GST_MATROSKA_ID_CLUSTER && - id != GST_MATROSKA_ID_CLUSTERTIMECODE) + id != GST_MATROSKA_ID_PREVSIZE && + id != GST_MATROSKA_ID_CLUSTERTIMECODE) { + if (demux->common.start_resync_offset != -1) { + /* we need to skip byte per byte if we are scanning for a new cluster + * after invalid data is found + */ + read = 1; + } goto skip; + } else { + if (demux->common.start_resync_offset != -1) { + GST_LOG_OBJECT (demux, "Resync done, new cluster found!"); + demux->common.start_resync_offset = -1; + demux->common.state = demux->common.state_to_restore; + } + } /* fall-through */ case GST_MATROSKA_READ_STATE_HEADER: case GST_MATROSKA_READ_STATE_DATA: @@ -4223,6 +4716,26 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, == GST_MATROSKA_READ_STATE_HEADER)) { demux->common.state = GST_MATROSKA_READ_STATE_DATA; demux->first_cluster_offset = demux->common.offset; + if (!demux->streaming && + !GST_CLOCK_TIME_IS_VALID (demux->common.segment.duration)) { + GstMatroskaIndex *last = NULL; + + GST_DEBUG_OBJECT (demux, + "estimating duration using last cluster"); + if ((last = gst_matroska_demux_search_pos (demux, + GST_CLOCK_TIME_NONE)) != NULL) { + demux->last_cluster_offset = + last->pos + demux->common.ebml_segment_start; + demux->stream_last_time = last->time; + demux->common.segment.duration = + demux->stream_last_time - demux->stream_start_time; + /* above estimate should not be taken all too strongly */ + demux->invalid_duration = TRUE; + GST_DEBUG_OBJECT (demux, + "estimated duration as %" GST_TIME_FORMAT, + GST_TIME_ARGS (demux->common.segment.duration)); + } + } GST_DEBUG_OBJECT (demux, "signaling no more pads"); gst_element_no_more_pads (GST_ELEMENT (demux)); /* send initial segment - we wait till we know the first @@ -4232,6 +4745,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, } demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = demux->common.offset; + demux->cluster_prevsize = 0; if (G_UNLIKELY (!demux->seek_first && demux->seek_block)) { GST_DEBUG_OBJECT (demux, "seek target block %" G_GUINT64_FORMAT " not found in Cluster, trying next Cluster's first block instead", @@ -4254,6 +4768,12 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, goto parse_failed; GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num); demux->cluster_time = num; + /* track last cluster */ + if (demux->cluster_offset > demux->last_cluster_offset) { + demux->last_cluster_offset = demux->cluster_offset; + demux->stream_last_time = + demux->cluster_time * demux->common.time_scale; + } #if 0 if (demux->common.element_index) { if (demux->common.element_index_writer_id == -1) @@ -4346,16 +4866,29 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, g_assert (event); /* unlikely to fail, since we managed to seek to this point */ - if (!gst_matroska_demux_handle_seek_event (demux, NULL, event)) + if (!gst_matroska_demux_handle_seek_event (demux, NULL, event)) { + gst_event_unref (event); goto seek_failed; + } + gst_event_unref (event); /* resume data handling, main thread clear to seek again */ GST_OBJECT_LOCK (demux); demux->common.state = GST_MATROSKA_READ_STATE_DATA; GST_OBJECT_UNLOCK (demux); } break; + case GST_MATROSKA_ID_PREVSIZE:{ + guint64 num; + + GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml)); + if ((ret = gst_ebml_read_uint (&ebml, &id, &num)) != GST_FLOW_OK) + goto parse_failed; + GST_LOG_OBJECT (demux, "ClusterPrevSize: %" G_GUINT64_FORMAT, num); + demux->cluster_prevsize = num; + demux->seen_cluster_prevsize = TRUE; + break; + } case GST_MATROSKA_ID_POSITION: - case GST_MATROSKA_ID_PREVSIZE: case GST_MATROSKA_ID_ENCRYPTEDBLOCK: case GST_MATROSKA_ID_SILENTTRACKS: GST_DEBUG_OBJECT (demux, @@ -4521,7 +5054,7 @@ pause: /* perform EOS logic */ /* If we were in the headers, make sure we send no-more-pads. - This will ensure decodebin2 does not get stuck thinking + This will ensure decodebin does not get stuck thinking the chain is not complete yet, and waiting indefinitely. */ if (G_UNLIKELY (demux->common.state == GST_MATROSKA_READ_STATE_HEADER)) { if (demux->common.src->len == 0) { @@ -4535,6 +5068,8 @@ pause: } if (demux->common.segment.flags & GST_SEEK_FLAG_SEGMENT) { + GstEvent *event; + GstMessage *msg; gint64 stop; /* for segment playback we need to post when (in stream time) @@ -4543,24 +5078,33 @@ pause: stop = demux->last_stop_end; GST_LOG_OBJECT (demux, "Sending segment done, at end of segment"); - gst_element_post_message (GST_ELEMENT (demux), - gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME, - stop)); - gst_matroska_demux_send_event (demux, - gst_event_new_segment_done (GST_FORMAT_TIME, stop)); + msg = gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME, + stop); + if (demux->segment_seqnum) + gst_message_set_seqnum (msg, demux->segment_seqnum); + gst_element_post_message (GST_ELEMENT (demux), msg); + + event = gst_event_new_segment_done (GST_FORMAT_TIME, stop); + if (demux->segment_seqnum) + gst_event_set_seqnum (event, demux->segment_seqnum); + gst_matroska_demux_send_event (demux, event); } else { push_eos = TRUE; } } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) { /* for fatal errors we post an error message */ - GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), - ("stream stopped, reason %s", reason)); + GST_ELEMENT_FLOW_ERROR (demux, ret); push_eos = TRUE; } if (push_eos) { + GstEvent *event; + /* send EOS, and prevent hanging if no streams yet */ GST_LOG_OBJECT (demux, "Sending EOS, at end of stream"); - if (!gst_matroska_demux_send_event (demux, gst_event_new_eos ()) && + event = gst_event_new_eos (); + if (demux->segment_seqnum) + gst_event_set_seqnum (event, demux->segment_seqnum); + if (!gst_matroska_demux_send_event (demux, event) && (ret == GST_FLOW_EOS)) { GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("got eos but no streams (yet)")); @@ -4575,7 +5119,7 @@ pause: */ static gboolean perform_seek_to_offset (GstMatroskaDemux * demux, gdouble rate, guint64 offset, - guint32 seqnum) + guint32 seqnum, GstSeekFlags flags) { GstEvent *event; gboolean res = 0; @@ -4584,8 +5128,8 @@ perform_seek_to_offset (GstMatroskaDemux * demux, gdouble rate, guint64 offset, event = gst_event_new_seek (rate, GST_FORMAT_BYTES, - GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset, - GST_SEEK_TYPE_NONE, -1); + flags | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, + GST_SEEK_TYPE_SET, offset, GST_SEEK_TYPE_NONE, -1); gst_event_set_seqnum (event, seqnum); res = gst_pad_push_event (demux->common.sinkpad, event); @@ -4624,9 +5168,29 @@ next: if (G_UNLIKELY (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)) { if (demux->common.ebml_segment_length != G_MAXUINT64 && demux->common.offset >= - demux->common.ebml_segment_start + demux->common.ebml_segment_length) - ret = GST_FLOW_EOS; - return ret; + demux->common.ebml_segment_start + demux->common.ebml_segment_length) { + return GST_FLOW_OK; + } else { + gint64 bytes_scanned; + if (demux->common.start_resync_offset == -1) { + demux->common.start_resync_offset = demux->common.offset; + demux->common.state_to_restore = demux->common.state; + } + bytes_scanned = demux->common.offset - demux->common.start_resync_offset; + if (bytes_scanned <= INVALID_DATA_THRESHOLD) { + GST_WARNING_OBJECT (demux, + "parse error, looking for next cluster, actual offset %" + G_GUINT64_FORMAT ", start resync offset %" G_GUINT64_FORMAT, + demux->common.offset, demux->common.start_resync_offset); + demux->common.state = GST_MATROSKA_READ_STATE_SCANNING; + ret = GST_FLOW_OK; + } else { + GST_WARNING_OBJECT (demux, + "unrecoverable parse error, next cluster not found and threshold " + "exceeded, bytes scanned %" G_GINT64_FORMAT, bytes_scanned); + return ret; + } + } } GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, " @@ -4695,10 +5259,12 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent, demux->common.segment.position = GST_CLOCK_TIME_NONE; demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = 0; + demux->cluster_prevsize = 0; demux->need_segment = TRUE; demux->segment_seqnum = gst_event_get_seqnum (event); /* but keep some of the upstream segment */ demux->common.segment.rate = segment->rate; + demux->common.segment.flags = segment->flags; /* also check if need to keep some of the requested seek position */ if (demux->seek_offset == segment->start) { GST_DEBUG_OBJECT (demux, "position matches requested seek"); @@ -4719,7 +5285,8 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent, } case GST_EVENT_EOS: { - if (demux->common.state != GST_MATROSKA_READ_STATE_DATA) { + if (demux->common.state != GST_MATROSKA_READ_STATE_DATA + && demux->common.state != GST_MATROSKA_READ_STATE_SCANNING) { gst_event_unref (event); GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("got eos and didn't receive a complete header object")); @@ -4739,11 +5306,13 @@ gst_matroska_demux_handle_sink_event (GstPad * pad, GstObject * parent, GST_OBJECT_LOCK (demux); gst_matroska_read_common_reset_streams (&demux->common, GST_CLOCK_TIME_NONE, TRUE); + gst_flow_combiner_reset (demux->flowcombiner); dur = demux->common.segment.duration; gst_segment_init (&demux->common.segment, GST_FORMAT_TIME); demux->common.segment.duration = dur; demux->cluster_time = GST_CLOCK_TIME_NONE; demux->cluster_offset = 0; + demux->cluster_prevsize = 0; GST_OBJECT_UNLOCK (demux); /* fall-through */ } @@ -4874,6 +5443,12 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * if (caps == NULL) { GST_WARNING ("Unhandled RIFF fourcc %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (vids->compression)); + } else { + 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"); + context->intra_only = + gst_caps_can_intersect (gst_static_caps_get (&intra_caps), caps); } if (buf) @@ -4919,10 +5494,13 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * return NULL; } + context->intra_only = TRUE; + gst_video_info_set_format (&info, format, videocontext->pixel_width, videocontext->pixel_height); caps = gst_video_info_to_caps (&info); *codec_name = gst_pb_utils_get_codec_description (caps); + context->alignment = 32; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_SP)) { caps = gst_caps_new_simple ("video/x-divx", "divxversion", G_TYPE_INT, 4, NULL); @@ -4972,6 +5550,7 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MJPEG)) { caps = gst_caps_new_empty_simple ("image/jpeg"); *codec_name = g_strdup ("Motion-JPEG"); + context->intra_only = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_AVC)) { caps = gst_caps_new_empty_simple ("video/x-h264"); if (data) { @@ -5067,6 +5646,62 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_VP9)) { caps = gst_caps_new_empty_simple ("video/x-vp9"); *codec_name = g_strdup_printf ("On2 VP9"); + } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_AV1)) { + caps = gst_caps_new_empty_simple ("video/x-av1"); + if (data) { + GstBuffer *priv; + + priv = gst_buffer_new_wrapped (g_memdup (data, size), size); + 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); + } + *codec_name = g_strdup_printf ("AOM AV1"); + } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_PRORES)) { + guint32 fourcc; + const gchar *variant, *variant_descr = ""; + + /* Expect a fourcc in the codec private data */ + if (!data || size < 4) { + GST_WARNING ("No or too small PRORESS fourcc (%d bytes)", size); + return NULL; + } + + fourcc = GST_STR_FOURCC (data); + switch (fourcc) { + case GST_MAKE_FOURCC ('a', 'p', 'c', 's'): + variant_descr = " 4:2:2 LT"; + variant = "lt"; + break; + case GST_MAKE_FOURCC ('a', 'p', 'c', 'h'): + variant = "hq"; + variant_descr = " 4:2:2 HQ"; + break; + case GST_MAKE_FOURCC ('a', 'p', '4', 'h'): + variant = "4444"; + variant_descr = " 4:4:4:4"; + break; + case GST_MAKE_FOURCC ('a', 'p', 'c', 'o'): + variant = "proxy"; + variant_descr = " 4:2:2 Proxy"; + break; + case GST_MAKE_FOURCC ('a', 'p', 'c', 'n'): + default: + variant = "standard"; + variant_descr = " 4:2:2 SD"; + break; + } + + GST_LOG ("Prores video, codec fourcc %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (fourcc)); + + caps = gst_caps_new_simple ("video/x-prores", + "format", G_TYPE_STRING, variant, NULL); + *codec_name = g_strdup_printf ("Apple ProRes%s", variant_descr); + context->postprocess_frame = gst_matroska_demux_add_prores_header; } else { GST_WARNING ("Unknown codec '%s', cannot build Caps", codec_id); return NULL; @@ -5136,9 +5771,45 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * 0, 1, NULL); } - if (videocontext->parent.flags & GST_MATROSKA_VIDEOTRACK_INTERLACED) - gst_structure_set (structure, "interlace-mode", G_TYPE_STRING, - "mixed", NULL); + switch (videocontext->interlace_mode) { + case GST_MATROSKA_INTERLACE_MODE_PROGRESSIVE: + gst_structure_set (structure, + "interlace-mode", G_TYPE_STRING, "progressive", NULL); + break; + case GST_MATROSKA_INTERLACE_MODE_INTERLACED: + gst_structure_set (structure, + "interlace-mode", G_TYPE_STRING, "mixed", NULL); + break; + default: + break; + } + } + if (videocontext->multiview_mode != GST_VIDEO_MULTIVIEW_MODE_NONE) { + if (gst_video_multiview_guess_half_aspect (videocontext->multiview_mode, + videocontext->pixel_width, videocontext->pixel_height, + videocontext->display_width * videocontext->pixel_height, + videocontext->display_height * videocontext->pixel_width)) { + videocontext->multiview_flags |= GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT; + } + gst_caps_set_simple (caps, + "multiview-mode", G_TYPE_STRING, + gst_video_multiview_mode_to_caps_string + (videocontext->multiview_mode), "multiview-flags", + GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, videocontext->multiview_flags, + GST_FLAG_SET_MASK_EXACT, NULL); + } + + if (videocontext->colorimetry.range != GST_VIDEO_COLOR_RANGE_UNKNOWN || + videocontext->colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN || + videocontext->colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN || + videocontext->colorimetry.primaries != + GST_VIDEO_COLOR_PRIMARIES_UNKNOWN) { + gchar *colorimetry = + gst_video_colorimetry_to_string (&videocontext->colorimetry); + gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, colorimetry, + NULL); + GST_DEBUG ("setting colorimetry to %s", colorimetry); + g_free (colorimetry); } caps = gst_caps_simplify (caps); @@ -5273,7 +5944,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * /* FIXME: Channel mask and reordering */ caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, gst_audio_format_to_string (format), - "layout", G_TYPE_STRING, "interleaved", NULL); + "layout", G_TYPE_STRING, "interleaved", + "channel-mask", GST_TYPE_BITMASK, + gst_audio_channel_get_fallback_mask (audiocontext->channels), NULL); *codec_name = g_strdup_printf ("Raw %d-bit PCM audio", audiocontext->bitdepth); @@ -5288,7 +5961,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * /* FIXME: Channel mask and reordering */ caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, format, - "layout", G_TYPE_STRING, "interleaved", NULL); + "layout", G_TYPE_STRING, "interleaved", + "channel-mask", GST_TYPE_BITMASK, + gst_audio_channel_get_fallback_mask (audiocontext->channels), NULL); *codec_name = g_strdup_printf ("Raw %d-bit floating-point audio", audiocontext->bitdepth); context->alignment = audiocontext->bitdepth / 8; @@ -5332,20 +6007,57 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * /* FIXME: mark stream as broken and skip if there are no stream headers */ context->send_stream_headers = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_OPUS)) { - caps = gst_caps_new_empty_simple ("audio/x-opus"); - *codec_name = g_strdup ("Opus"); - context->stream_headers = - gst_matroska_parse_opus_stream_headers (context->codec_priv, - context->codec_priv_size); - if (context->stream_headers) { - /* There was a valid header. Multistream headers are more than - * 19 bytes, as they include an extra channel mapping table. */ - gboolean multistream = (context->codec_priv_size > 19); - gst_caps_set_simple (caps, "multistream", G_TYPE_BOOLEAN, multistream, - NULL); + GstBuffer *tmp; + + if (context->codec_priv_size >= 19) { + if (audiocontext->samplerate) + GST_WRITE_UINT32_LE ((guint8 *) context->codec_priv + 12, + audiocontext->samplerate); + if (context->codec_delay) { + guint64 delay = + gst_util_uint64_scale_round (context->codec_delay, 48000, + GST_SECOND); + GST_WRITE_UINT16_LE ((guint8 *) context->codec_priv + 10, delay); + } + + tmp = + gst_buffer_new_wrapped (g_memdup (context->codec_priv, + context->codec_priv_size), context->codec_priv_size); + caps = gst_codec_utils_opus_create_caps_from_header (tmp, NULL); + gst_buffer_unref (tmp); + *codec_name = g_strdup ("Opus"); + } else if (context->codec_priv_size == 0) { + GST_WARNING ("No Opus codec data found, trying to create one"); + if (audiocontext->channels <= 2) { + guint8 streams, coupled, channels; + guint32 samplerate; + + samplerate = + audiocontext->samplerate == 0 ? 48000 : audiocontext->samplerate; + channels = audiocontext->channels == 0 ? 2 : audiocontext->channels; + if (channels == 1) { + streams = 1; + coupled = 0; + } else { + streams = 1; + coupled = 1; + } + + caps = + gst_codec_utils_opus_create_caps (samplerate, channels, 0, streams, + coupled, NULL); + if (caps) { + *codec_name = g_strdup ("Opus"); + } else { + GST_WARNING ("Failed to create Opus caps from audio context"); + } + } else { + GST_WARNING ("No Opus codec data, and not enough info to create one"); + } + } else { + GST_WARNING ("Invalid Opus codec data size (got %" G_GSIZE_FORMAT + ", expected 19)", context->codec_priv_size); } - /* FIXME: mark stream as broken and skip if there are no stream headers */ - context->send_stream_headers = TRUE; } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_ACM)) { gst_riff_strf_auds auds; @@ -5507,7 +6219,7 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * guint sample_width; guint extra_data_size; - GST_ERROR ("real audio raversion:%d", raversion); + GST_DEBUG ("real audio raversion:%d", raversion); if (raversion == 8) { /* COOK */ flavor = GST_READ_UINT16_BE (data + 22); @@ -5517,7 +6229,7 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext * sample_width = GST_READ_UINT16_BE (data + 58); extra_data_size = GST_READ_UINT32_BE (data + 74); - GST_ERROR + GST_DEBUG ("flavor:%d, packet_size:%d, height:%d, leaf_size:%d, sample_width:%d, extra_data_size:%d", flavor, packet_size, height, leaf_size, sample_width, extra_data_size); @@ -5701,7 +6413,7 @@ gst_matroska_demux_set_property (GObject * object, demux = GST_MATROSKA_DEMUX (object); switch (prop_id) { - case ARG_MAX_GAP_TIME: + case PROP_MAX_GAP_TIME: GST_OBJECT_LOCK (demux); demux->max_gap_time = g_value_get_uint64 (value); GST_OBJECT_UNLOCK (demux); @@ -5722,7 +6434,7 @@ gst_matroska_demux_get_property (GObject * object, demux = GST_MATROSKA_DEMUX (object); switch (prop_id) { - case ARG_MAX_GAP_TIME: + case PROP_MAX_GAP_TIME: GST_OBJECT_LOCK (demux); g_value_set_uint64 (value, demux->max_gap_time); GST_OBJECT_UNLOCK (demux);