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,
/* 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,
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,
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-devel@lists.sourceforge.net>");
+ "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
}
static void
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;
}
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);
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;
context->from_offset = -1;
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);
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;
}
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:
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;
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:{
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;
&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;
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) {
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);
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);
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);
static gboolean
gst_matroska_demux_send_event (GstMatroskaDemux * demux, GstEvent * event)
{
- gboolean is_segment;
gboolean ret = FALSE;
gint i;
GST_DEBUG_OBJECT (demux, "Sending event of type %s to all source pads",
GST_EVENT_TYPE_NAME (event));
- is_segment = (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT);
-
g_assert (demux->common.src->len == demux->common.num_streams);
for (i = 0; i < demux->common.src->len; i++) {
GstMatroskaTrackContext *stream;
gst_event_ref (event);
gst_pad_push_event (stream->pad, event);
ret = TRUE;
-
- /* FIXME: send global tags before stream tags */
- if (G_UNLIKELY (is_segment && 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);
- gst_pad_push_event (stream->pad,
- gst_event_new_tag (stream->pending_tags));
- stream->pending_tags = NULL;
- }
}
- if (G_UNLIKELY (is_segment && demux->common.global_tags != NULL)) {
+ gst_event_unref (event);
+ return ret;
+}
+
+static void
+gst_matroska_demux_send_tags (GstMatroskaDemux * demux)
+{
+ gint i;
+
+ 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;
}
gst_event_unref (tag_event);
- demux->common.global_tags = NULL;
+ demux->common.global_tags_changed = FALSE;
}
- gst_event_unref (event);
- return ret;
+ g_assert (demux->common.src->len == demux->common.num_streams);
+ for (i = 0; i < demux->common.src->len; i++) {
+ GstMatroskaTrackContext *stream;
+
+ stream = g_ptr_array_index (demux->common.src, i);
+
+ 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 (gst_tag_list_copy (stream->tags)));
+ stream->tags_changed = FALSE;
+ }
+ }
}
static gboolean
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",
/* 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;
/* 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;
guint64 length;
guint32 id;
guint needed;
+ gint64 oldpos, oldlength;
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;
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,
}
/* read in at newpos and scan for ebml cluster id */
+ oldpos = oldlength = -1;
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,
gst_buffer_map (buf, &map, GST_MAP_READ);
data = map.data;
size = map.size;
+ if (oldpos == newpos && oldlength == map.size) {
+ GST_ERROR_OBJECT (demux, "Stuck at same position");
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ } else {
+ oldpos = newpos;
+ oldlength = map.size;
+ }
+
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:
GST_DEBUG_OBJECT (demux, "cluster is first cluster -> OK");
break;
}
- if (newpos == demux->common.offset) {
- GST_ERROR_OBJECT (demux, "Stuck at the same offset");
- ret = GST_FLOW_ERROR;
- goto exit;
- }
demux->common.offset = newpos;
ret = gst_matroska_read_common_peek_id_length_pull (&demux->common,
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",
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;
}
}
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;
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);
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 */
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;
gboolean update = TRUE;
gboolean pad_locked = FALSE;
guint32 seqnum;
+ GstSearchMode snap_dir;
+
+ g_return_val_if_fail (event != NULL, FALSE);
if (pad)
track = gst_pad_get_element_private (pad);
+ GST_DEBUG_OBJECT (demux, "Have seek %" GST_PTR_FORMAT, event);
+
gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
&stop_type, &stop);
seqnum = gst_event_get_seqnum (event);
seeksegment.duration = GST_CLOCK_TIME_NONE;
}
- if (event) {
- GST_DEBUG_OBJECT (demux, "configuring seek");
- gst_segment_do_seek (&seeksegment, rate, format, flags,
- cur_type, cur, stop_type, stop, &update);
- /* compensate for clip start time, but only for SET seeks,
- * otherwise it is already part of the segments */
- if (GST_CLOCK_TIME_IS_VALID (demux->stream_start_time)) {
- if (cur_type == GST_SEEK_TYPE_SET) {
- if (rate > 0.0)
- seeksegment.position += demux->stream_start_time;
- seeksegment.start += demux->stream_start_time;
- }
- if (stop_type == GST_SEEK_TYPE_SET
- && GST_CLOCK_TIME_IS_VALID (seeksegment.stop)) {
- if (rate < 0.0)
- seeksegment.position += demux->stream_start_time;
- seeksegment.stop += demux->stream_start_time;
- }
- }
+ GST_DEBUG_OBJECT (demux, "configuring seek");
+ /* Subtract stream_start_time so we always seek on a segment
+ * in stream time */
+ if (GST_CLOCK_TIME_IS_VALID (demux->stream_start_time)) {
+ seeksegment.start -= demux->stream_start_time;
+ seeksegment.position -= demux->stream_start_time;
+ if (GST_CLOCK_TIME_IS_VALID (seeksegment.stop))
+ seeksegment.stop -= demux->stream_start_time;
+ else
+ seeksegment.stop = seeksegment.duration;
+ }
+
+ gst_segment_do_seek (&seeksegment, rate, format, flags,
+ cur_type, cur, stop_type, stop, &update);
+
+ /* Restore the clip timestamp offset */
+ if (GST_CLOCK_TIME_IS_VALID (demux->stream_start_time)) {
+ seeksegment.position += demux->stream_start_time;
+ seeksegment.start += demux->stream_start_time;
+ if (!GST_CLOCK_TIME_IS_VALID (seeksegment.stop))
+ seeksegment.stop = seeksegment.duration;
+ if (GST_CLOCK_TIME_IS_VALID (seeksegment.stop))
+ seeksegment.stop += demux->stream_start_time;
}
/* restore segment duration (if any effect),
/* check sanity before we start flushing and all that */
snap_next = after && !before;
if (seeksegment.rate < 0)
- snap_next = !snap_next;
+ snap_dir = snap_next ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER;
+ else
+ snap_dir = snap_next ? GST_SEARCH_MODE_AFTER : GST_SEARCH_MODE_BEFORE;
+
GST_OBJECT_LOCK (demux);
track = gst_matroska_read_common_get_seek_track (&demux->common, track);
if ((entry = gst_matroska_read_common_do_index_seek (&demux->common, track,
seeksegment.position, &demux->seek_index, &demux->seek_entry,
- snap_next)) == NULL) {
+ snap_dir)) == NULL) {
/* pull mode without index can scan later on */
if (demux->streaming) {
GST_DEBUG_OBJECT (demux, "No matching seek entry in index");
}
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) {
/* 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:
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
/* 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 */
/* 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
static void
gst_matroska_demux_sync_streams (GstMatroskaDemux * demux)
{
+ GstClockTime gap_threshold;
gint stream_nr;
GST_OBJECT_LOCK (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 %"
{
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;
}
}
- GST_OBJECT_UNLOCK (demux);
}
static GstFlowReturn
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 |
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,
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
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;
gint flags = 0;
gint64 referenceblock = 0;
gint64 offset;
+ GstClockTime buffer_timestamp;
offset = gst_ebml_read_get_offset (ebml);
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,
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;
}
/* 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;
}
}
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) &&
goto next_lace;
}
- GST_BUFFER_TIMESTAMP (sub) = lace_time;
+ if (!stream->dts_only) {
+ GST_BUFFER_PTS (sub) = lace_time;
+ } 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;
"Pushing lace %d, data of size %" G_GSIZE_FORMAT
" for stream %d, time=%" GST_TIME_FORMAT " and duration=%"
GST_TIME_FORMAT, n, gst_buffer_get_size (sub), stream_num,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)),
+ GST_TIME_ARGS (buffer_timestamp),
GST_TIME_ARGS (GST_BUFFER_DURATION (sub)));
#if 0
GST_LOG_OBJECT (demux, "adding association %" GST_TIME_FORMAT "-> %"
G_GUINT64_FORMAT " for writer id %d",
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sub)), cluster_offset,
+ GST_TIME_ARGS (buffer_timestamp), cluster_offset,
stream->index_writer_id);
gst_index_add_association (demux->common.element_index,
stream->index_writer_id, GST_BUFFER_FLAG_IS_SET (sub,
GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : GST_ASSOCIATION_FLAG_KEY_UNIT,
- GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (sub), GST_FORMAT_BYTES,
- cluster_offset, NULL);
+ GST_FORMAT_TIME, buffer_timestamp, GST_FORMAT_BYTES, cluster_offset,
+ NULL);
}
#endif
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))
stream->pos += GST_BUFFER_DURATION (sub);
+ } else if (GST_BUFFER_DTS_IS_VALID (sub)) {
+ stream->pos = GST_BUFFER_DTS (sub);
+ if (GST_BUFFER_DURATION_IS_VALID (sub))
+ stream->pos += GST_BUFFER_DURATION (sub);
}
ret = gst_pad_push (stream->pad, sub);
}
}
/* 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];
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:
* 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 {
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:
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
ret = gst_matroska_read_common_parse_info (&demux->common,
GST_ELEMENT_CAST (demux), &ebml);
+ if (ret == GST_FLOW_OK)
+ gst_matroska_demux_send_tags (demux);
} else {
GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
}
== 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
}
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",
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)
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
ret = gst_matroska_read_common_parse_attachments (&demux->common,
GST_ELEMENT_CAST (demux), &ebml);
+ if (ret == GST_FLOW_OK)
+ gst_matroska_demux_send_tags (demux);
} else {
GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
}
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
ret = gst_matroska_read_common_parse_metadata (&demux->common,
GST_ELEMENT_CAST (demux), &ebml);
+ if (ret == GST_FLOW_OK)
+ gst_matroska_demux_send_tags (demux);
break;
case GST_MATROSKA_ID_CHAPTERS:
if (!demux->common.chapters_parsed) {
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,
/* 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) {
}
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)
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)"));
*/
static gboolean
perform_seek_to_offset (GstMatroskaDemux * demux, gdouble rate, guint64 offset,
- guint32 seqnum)
+ guint32 seqnum, GstSeekFlags flags)
{
GstEvent *event;
gboolean res = 0;
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);
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, "
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");
}
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"));
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 */
}
memcpy (vids, data, size);
}
+ context->dts_only = TRUE; /* VFW files only store DTS */
+
/* little-endian -> byte-order */
vids->size = GUINT32_FROM_LE (vids->size);
vids->width = GUINT32_FROM_LE (vids->width);
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)
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);
} 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) {
} 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;
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);
/* 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);
/* 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;
/* 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;
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);
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);
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);
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);