+2005-10-28 Michal Benes <michal dot benes at xeris dot cz>
+
+ Reviewed by: Tim-Philipp Müller <tim at centricular dot net>
+
+ * gst/matroska/matroska-demux.c: (gst_matroska_demux_init_stream),
+ (gst_matroska_demux_parse_info),
+ (gst_matroska_demux_parse_blockgroup_or_simpleblock),
+ (gst_matroska_demux_parse_cluster):
+ * gst/matroska/matroska-ids.h:
+ * gst/matroska/matroska-mux.c: (gst_matroska_mux_class_init),
+ (gst_matroska_mux_init), (gst_matroska_mux_start),
+ (gst_matroska_mux_create_buffer_header),
+ (gst_matroska_mux_write_data), (gst_matroska_mux_set_property),
+ (gst_matroska_mux_get_property):
+ * gst/matroska/matroska-mux.h:
+ Add SimpleBlock support to matroska demuxer and muxer (part of
+ Matroska v2). (#319731)
+
2005-10-28 Wim Taymans <wim@fluendo.com>
* ext/jpeg/gstjpegdec.c: (gst_jpeg_dec_init), (gst_jpeg_dec_chain),
return FALSE;
}
g_free (doctype);
- if (version > 1) {
+ if (version > 2) {
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
- ("Demuxer version (1) is too old to read stream version %d", version));
+ ("Demuxer version (2) is too old to read stream version %d", version));
return FALSE;
}
break;
}
+ case GST_MATROSKA_ID_SEGMENTUID:{
+ /* TODO not yet implemented. */
+ if (!gst_ebml_read_skip (ebml))
+ res = FALSE;
+ break;
+ }
+
default:
GST_WARNING ("Unknown entry 0x%x in info header", id);
/* fall-through */
}
static gboolean
-gst_matroska_demux_parse_blockgroup (GstMatroskaDemux * demux,
- guint64 cluster_time)
+gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
+ guint64 cluster_time, gboolean is_simpleblock)
{
GstMatroskaTrackContext *stream = NULL;
GstEbmlRead *ebml = GST_EBML_READ (demux);
guint size = 0;
gint *lace_size = NULL;
gint64 time = 0;
+ gint flags = 0;
while (!got_error) {
- if (!gst_ebml_peek_id (ebml, &demux->level_up, &id))
- goto error;
+ if (!is_simpleblock) {
+ if (!gst_ebml_peek_id (ebml, &demux->level_up, &id))
+ goto error;
- if (demux->level_up) {
- demux->level_up--;
- break;
+ if (demux->level_up) {
+ demux->level_up--;
+ break;
+ }
+ } else {
+ id = GST_MATROSKA_ID_SIMPLEBLOCK;
}
switch (id) {
/* one block inside the group. Note, block parsing is one
* of the harder things, so this code is a bit complicated.
* See http://www.matroska.org/ for documentation. */
+ case GST_MATROSKA_ID_SIMPLEBLOCK:
case GST_MATROSKA_ID_BLOCK:
{
guint64 num;
guint8 *data;
- gint flags = 0;
if (!gst_ebml_read_buffer (ebml, &id, &buf)) {
got_error = TRUE;
break;
}
+ if (is_simpleblock)
+ break;
+
if (demux->level_up) {
demux->level_up--;
break;
stream->pos += GST_BUFFER_DURATION (sub);
}
+ if (is_simpleblock) {
+ if (flags & 0x80)
+ GST_BUFFER_FLAG_UNSET (sub, GST_BUFFER_FLAG_DELTA_UNIT);
+ else
+ GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DELTA_UNIT);
+ }
+
GST_DEBUG ("Pushing data of size %d for stream %d, time=%"
GST_TIME_FORMAT " and duration=%" GST_TIME_FORMAT,
GST_BUFFER_SIZE (sub), stream_num,
if (!gst_ebml_read_master (ebml, &id)) {
got_error = TRUE;
} else {
- if (!gst_matroska_demux_parse_blockgroup (demux, cluster_time))
+ if (!gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
+ cluster_time, FALSE))
got_error = TRUE;
}
break;
+ case GST_MATROSKA_ID_SIMPLEBLOCK:
+ {
+ if (!gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
+ cluster_time, TRUE))
+ got_error = TRUE;
+ break;
+ }
+
default:
GST_WARNING ("Unknown entry 0x%x in cluster data", id);
/* fall-through */
enum
{
ARG_0,
- ARG_WRITING_APP
+ ARG_WRITING_APP,
+ ARG_MATROSKA_VERSION
/* FILL ME */
};
g_param_spec_string ("writing-app", "Writing application.",
"The name the application that creates the matroska file.",
NULL, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, ARG_MATROSKA_VERSION,
+ g_param_spec_int ("version", "Matroska version",
+ "This parameter determines what matroska features can be used.",
+ 1, 2, 1, G_PARAM_READWRITE));
gstelement_class->change_state = gst_matroska_mux_change_state;
gstelement_class->request_new_pad = gst_matroska_mux_request_new_pad;
/* initialize internal variables */
mux->index = NULL;
+ mux->matroska_version = 1;
/* Initialize all variables */
gst_matroska_mux_reset (GST_ELEMENT (mux));
GTimeVal time = { 0, 0 };
/* we start with a EBML header */
- gst_ebml_write_header (ebml, "matroska", 1);
+ gst_ebml_write_header (ebml, "matroska", mux->matroska_version);
/* start a segment */
mux->segment_pos =
return best;
}
+
+/**
+ * gst_matroska_mux_buffer_header:
+ * @track: Track context.
+ * @relative_timestamp: relative timestamp of the buffer
+ * @flags: Buffer flags.
+ *
+ * Create a buffer containing buffer header.
+ *
+ * Returns: New buffer.
+ */
+GstBuffer *
+gst_matroska_mux_create_buffer_header (GstMatroskaTrackContext * track,
+ guint16 relative_timestamp, int flags)
+{
+ GstBuffer *hdr;
+
+ hdr = gst_buffer_new_and_alloc (4);
+ /* track num - FIXME: what if num >= 0x80 (unlikely)? */
+ GST_BUFFER_DATA (hdr)[0] = track->num | 0x80;
+ /* time relative to clustertime */
+ GST_WRITE_UINT16_BE (GST_BUFFER_DATA (hdr) + 1, relative_timestamp);
+
+ /* flags */
+ GST_BUFFER_DATA (hdr)[3] = flags;
+
+ return hdr;
+}
+
/**
* gst_matroska_mux_write_data:
* @mux: #GstMatroskaMux
GstEbmlWrite *ebml = mux->ebml_write;
GstBuffer *buf, *hdr;
guint64 cluster, blockgroup;
+ gboolean write_duration;
+ guint16 relative_timestamp;
/* which stream to write from? */
best = gst_matroska_mux_best_pad (mux);
idx->track = best->track->num;
}
- /* write one blockgroup with one block with
- * one slice (*breath*).
- * FIXME: lacing, etc. */
- blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
- gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
- GST_BUFFER_SIZE (buf) + 4);
- hdr = gst_buffer_new_and_alloc (4);
- /* track num - FIXME: what if num >= 0x80 (unlikely)? */
- GST_BUFFER_DATA (hdr)[0] = best->track->num | 0x80;
- /* time relative to clustertime */
- *(guint16 *) & GST_BUFFER_DATA (hdr)[1] = GUINT16_TO_BE (
- (GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time) / mux->time_scale);
- /* flags - no lacing (yet) */
- GST_BUFFER_DATA (hdr)[3] = 0;
- gst_ebml_write_buffer (ebml, hdr);
- gst_ebml_write_buffer (ebml, buf);
+ /* Check if the duration differs from the default duration. */
+ write_duration = FALSE;
if (GST_BUFFER_DURATION_IS_VALID (buf)) {
guint64 block_duration = GST_BUFFER_DURATION (buf);
if (block_duration != best->track->default_duration) {
+ write_duration = TRUE;
+ }
+ }
+
+ /* write the block, for matroska v2 use SimpleBlock if possible
+ * one slice (*breath*).
+ * FIXME: lacing, etc. */
+ relative_timestamp =
+ (GST_BUFFER_TIMESTAMP (buf) - mux->cluster_time) / mux->time_scale;
+ if (mux->matroska_version > 1 && !write_duration) {
+ int flags =
+ GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ? 0 : 0x80;
+
+ hdr =
+ gst_matroska_mux_create_buffer_header (best->track, relative_timestamp,
+ flags);
+ gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_SIMPLEBLOCK,
+ GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
+ gst_ebml_write_buffer (ebml, hdr);
+ gst_ebml_write_buffer (ebml, buf);
+
+ return gst_ebml_last_write_result (ebml);
+ } else {
+ blockgroup = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_BLOCKGROUP);
+ hdr =
+ gst_matroska_mux_create_buffer_header (best->track, relative_timestamp,
+ 0);
+ gst_ebml_write_buffer_header (ebml, GST_MATROSKA_ID_BLOCK,
+ GST_BUFFER_SIZE (buf) + GST_BUFFER_SIZE (hdr));
+ gst_ebml_write_buffer (ebml, hdr);
+ gst_ebml_write_buffer (ebml, buf);
+ if (write_duration) {
+ guint64 block_duration = GST_BUFFER_DURATION (buf);
+
gst_ebml_write_uint (ebml, GST_MATROSKA_ID_BLOCKDURATION,
block_duration / mux->time_scale);
}
+ gst_ebml_write_master_finish (ebml, blockgroup);
+ return gst_ebml_last_write_result (ebml);
}
- gst_ebml_write_master_finish (ebml, blockgroup);
- return gst_ebml_last_write_result (ebml);
}
g_free (mux->writing_app);
mux->writing_app = g_strdup (g_value_get_string (value));
break;
+ case ARG_MATROSKA_VERSION:
+ mux->matroska_version = g_value_get_int (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case ARG_WRITING_APP:
g_value_set_string (value, mux->writing_app);
break;
+ case ARG_MATROSKA_VERSION:
+ g_value_set_int (value, mux->matroska_version);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;