mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 01:30:38 +00:00
parent
e4183c6904
commit
900ff7247e
4 changed files with 470 additions and 8 deletions
|
@ -170,6 +170,29 @@ gst_ebml_read_change_state (GstElement * element, GstStateChange transition)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used in push mode.
|
||||||
|
* Provided buffer is used as cache, based on offset 0, and no further reads
|
||||||
|
* will be issued.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_ebml_read_reset_cache (GstEbmlRead * ebml, GstBuffer * buffer,
|
||||||
|
guint64 offset)
|
||||||
|
{
|
||||||
|
if (ebml->cached_buffer)
|
||||||
|
gst_buffer_unref (ebml->cached_buffer);
|
||||||
|
|
||||||
|
ebml->cached_buffer = buffer;
|
||||||
|
ebml->push_cache = TRUE;
|
||||||
|
buffer = gst_buffer_make_metadata_writable (buffer);
|
||||||
|
GST_BUFFER_OFFSET (buffer) = offset;
|
||||||
|
ebml->offset = offset;
|
||||||
|
g_list_foreach (ebml->level, (GFunc) gst_ebml_level_free, NULL);
|
||||||
|
g_list_free (ebml->level);
|
||||||
|
ebml->level = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return: the amount of levels in the hierarchy that the
|
* Return: the amount of levels in the hierarchy that the
|
||||||
* current element lies higher than the previous one.
|
* current element lies higher than the previous one.
|
||||||
|
@ -224,6 +247,13 @@ gst_ebml_read_peek_bytes (GstEbmlRead * ebml, guint size, GstBuffer ** p_buf,
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
/* not enough data in the cache, free cache and get a new one */
|
/* not enough data in the cache, free cache and get a new one */
|
||||||
|
/* never drop pushed cache */
|
||||||
|
if (ebml->push_cache) {
|
||||||
|
if (ebml->offset == cache_offset + cache_size)
|
||||||
|
return GST_FLOW_END;
|
||||||
|
else
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
}
|
||||||
gst_buffer_unref (ebml->cached_buffer);
|
gst_buffer_unref (ebml->cached_buffer);
|
||||||
ebml->cached_buffer = NULL;
|
ebml->cached_buffer = NULL;
|
||||||
}
|
}
|
||||||
|
@ -437,8 +467,17 @@ gst_ebml_peek_id (GstEbmlRead * ebml, guint * level_up, guint32 * id)
|
||||||
next:
|
next:
|
||||||
off = ebml->offset; /* save offset */
|
off = ebml->offset; /* save offset */
|
||||||
|
|
||||||
if ((ret = gst_ebml_read_element_id (ebml, id, &level_up_tmp)) != GST_FLOW_OK)
|
if ((ret = gst_ebml_read_element_id (ebml, id, &level_up_tmp)) != GST_FLOW_OK) {
|
||||||
return ret;
|
if (ret != GST_FLOW_END)
|
||||||
|
return ret;
|
||||||
|
else {
|
||||||
|
/* simulate dummy VOID element,
|
||||||
|
* and have the call stack bail out all the way */
|
||||||
|
*id = GST_EBML_ID_VOID;
|
||||||
|
*level_up = G_MAXUINT32 >> 2;
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ebml->offset = off; /* restore offset */
|
ebml->offset = off; /* restore offset */
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,9 @@ G_BEGIN_DECLS
|
||||||
#define GST_EBML_READ_GET_CLASS(obj) \
|
#define GST_EBML_READ_GET_CLASS(obj) \
|
||||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_EBML_READ, GstEbmlReadClass))
|
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_EBML_READ, GstEbmlReadClass))
|
||||||
|
|
||||||
|
/* custom flow return code */
|
||||||
|
#define GST_FLOW_END GST_FLOW_CUSTOM_SUCCESS
|
||||||
|
|
||||||
typedef struct _GstEbmlLevel {
|
typedef struct _GstEbmlLevel {
|
||||||
guint64 start;
|
guint64 start;
|
||||||
guint64 length;
|
guint64 length;
|
||||||
|
@ -48,6 +51,7 @@ typedef struct _GstEbmlRead {
|
||||||
GstElement parent;
|
GstElement parent;
|
||||||
|
|
||||||
GstBuffer *cached_buffer;
|
GstBuffer *cached_buffer;
|
||||||
|
gboolean push_cache;
|
||||||
|
|
||||||
GstPad *sinkpad;
|
GstPad *sinkpad;
|
||||||
guint64 offset;
|
guint64 offset;
|
||||||
|
@ -63,6 +67,10 @@ GType gst_ebml_read_get_type (void);
|
||||||
|
|
||||||
void gst_ebml_level_free (GstEbmlLevel *level);
|
void gst_ebml_level_free (GstEbmlLevel *level);
|
||||||
|
|
||||||
|
void gst_ebml_read_reset_cache (GstEbmlRead * ebml,
|
||||||
|
GstBuffer * buffer,
|
||||||
|
guint64 offset);
|
||||||
|
|
||||||
GstFlowReturn gst_ebml_peek_id (GstEbmlRead *ebml,
|
GstFlowReturn gst_ebml_peek_id (GstEbmlRead *ebml,
|
||||||
guint *level_up,
|
guint *level_up,
|
||||||
guint32 *id);
|
guint32 *id);
|
||||||
|
|
|
@ -149,6 +149,11 @@ static const GstQueryType *gst_matroska_demux_get_src_query_types (GstPad *
|
||||||
static gboolean gst_matroska_demux_handle_src_query (GstPad * pad,
|
static gboolean gst_matroska_demux_handle_src_query (GstPad * pad,
|
||||||
GstQuery * query);
|
GstQuery * query);
|
||||||
|
|
||||||
|
static gboolean gst_matroska_demux_handle_sink_event (GstPad * pad,
|
||||||
|
GstEvent * event);
|
||||||
|
static GstFlowReturn gst_matroska_demux_chain (GstPad * pad,
|
||||||
|
GstBuffer * buffer);
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
gst_matroska_demux_change_state (GstElement * element,
|
gst_matroska_demux_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
@ -208,6 +213,8 @@ gst_matroska_demux_finalize (GObject * object)
|
||||||
demux->global_tags = NULL;
|
demux->global_tags = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_object_unref (demux->adapter);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +251,10 @@ gst_matroska_demux_init (GstMatroskaDemux * demux,
|
||||||
GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate));
|
GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate));
|
||||||
gst_pad_set_activatepull_function (demux->sinkpad,
|
gst_pad_set_activatepull_function (demux->sinkpad,
|
||||||
GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate_pull));
|
GST_DEBUG_FUNCPTR (gst_matroska_demux_sink_activate_pull));
|
||||||
|
gst_pad_set_chain_function (demux->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_matroska_demux_chain));
|
||||||
|
gst_pad_set_event_function (demux->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_matroska_demux_handle_sink_event));
|
||||||
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
|
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
|
||||||
GST_EBML_READ (demux)->sinkpad = demux->sinkpad;
|
GST_EBML_READ (demux)->sinkpad = demux->sinkpad;
|
||||||
|
|
||||||
|
@ -255,6 +266,8 @@ gst_matroska_demux_init (GstMatroskaDemux * demux,
|
||||||
demux->index = NULL;
|
demux->index = NULL;
|
||||||
demux->global_tags = NULL;
|
demux->global_tags = NULL;
|
||||||
|
|
||||||
|
demux->adapter = gst_adapter_new ();
|
||||||
|
|
||||||
/* finish off */
|
/* finish off */
|
||||||
gst_matroska_demux_reset (GST_ELEMENT (demux));
|
gst_matroska_demux_reset (GST_ELEMENT (demux));
|
||||||
}
|
}
|
||||||
|
@ -390,6 +403,10 @@ gst_matroska_demux_reset (GstElement * element)
|
||||||
demux->duration = -1;
|
demux->duration = -1;
|
||||||
demux->last_stop_end = GST_CLOCK_TIME_NONE;
|
demux->last_stop_end = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
demux->offset = 0;
|
||||||
|
demux->cluster_time = GST_CLOCK_TIME_NONE;
|
||||||
|
demux->cluster_offset = 0;
|
||||||
|
|
||||||
if (demux->close_segment) {
|
if (demux->close_segment) {
|
||||||
gst_event_unref (demux->close_segment);
|
gst_event_unref (demux->close_segment);
|
||||||
demux->close_segment = NULL;
|
demux->close_segment = NULL;
|
||||||
|
@ -2339,15 +2356,12 @@ gst_matroska_demux_handle_src_event (GstPad * pad, GstEvent * event)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_matroska_demux_init_stream (GstMatroskaDemux * demux)
|
gst_matroska_demux_parse_header (GstMatroskaDemux * demux)
|
||||||
{
|
{
|
||||||
GstEbmlRead *ebml = GST_EBML_READ (demux);
|
GstEbmlRead *ebml = GST_EBML_READ (demux);
|
||||||
guint32 id;
|
GstFlowReturn ret;
|
||||||
gchar *doctype;
|
gchar *doctype;
|
||||||
guint version;
|
guint version;
|
||||||
GstFlowReturn ret;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (demux, "Init stream");
|
|
||||||
|
|
||||||
if ((ret = gst_ebml_read_header (ebml, &doctype, &version)) != GST_FLOW_OK)
|
if ((ret = gst_ebml_read_header (ebml, &doctype, &version)) != GST_FLOW_OK)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2366,6 +2380,21 @@ gst_matroska_demux_init_stream (GstMatroskaDemux * demux)
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_matroska_demux_init_stream (GstMatroskaDemux * demux)
|
||||||
|
{
|
||||||
|
GstEbmlRead *ebml = GST_EBML_READ (demux);
|
||||||
|
guint32 id;
|
||||||
|
GstFlowReturn ret;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "Init stream");
|
||||||
|
|
||||||
|
if ((ret = gst_matroska_demux_parse_header (demux)) != GST_FLOW_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* find segment, must be the next element but search as long as
|
/* find segment, must be the next element but search as long as
|
||||||
* we find it anyway */
|
* we find it anyway */
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
|
@ -5168,11 +5197,389 @@ pause:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
gst_matroska_demux_take (GstMatroskaDemux * demux, guint bytes)
|
||||||
|
{
|
||||||
|
GstBuffer *buffer;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (demux, "caching %d bytes for parsing", bytes);
|
||||||
|
buffer = gst_adapter_take_buffer (demux->adapter, bytes);
|
||||||
|
gst_ebml_read_reset_cache (GST_EBML_READ (demux), buffer, demux->offset);
|
||||||
|
demux->offset += bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
gst_matroska_demux_flush (GstMatroskaDemux * demux, guint flush)
|
||||||
|
{
|
||||||
|
GST_LOG_OBJECT (demux, "skipping %d bytes", flush);
|
||||||
|
gst_adapter_flush (demux->adapter, flush);
|
||||||
|
demux->offset += flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_matroska_demux_peek_id_length (GstMatroskaDemux * demux, guint32 * _id,
|
||||||
|
guint64 * _length, guint * _needed)
|
||||||
|
{
|
||||||
|
guint avail, needed;
|
||||||
|
const guint8 *buf;
|
||||||
|
gint len_mask = 0x80, read = 1, n = 1, num_ffs = 0;
|
||||||
|
guint64 total;
|
||||||
|
guint8 b;
|
||||||
|
|
||||||
|
g_return_val_if_fail (_id != NULL, GST_FLOW_ERROR);
|
||||||
|
g_return_val_if_fail (_length != NULL, GST_FLOW_ERROR);
|
||||||
|
g_return_val_if_fail (_needed != NULL, GST_FLOW_ERROR);
|
||||||
|
|
||||||
|
/* well ... */
|
||||||
|
*_id = (guint32) GST_EBML_SIZE_UNKNOWN;
|
||||||
|
*_length = GST_EBML_SIZE_UNKNOWN;
|
||||||
|
|
||||||
|
/* read element id */
|
||||||
|
needed = 2;
|
||||||
|
avail = gst_adapter_available (demux->adapter);
|
||||||
|
if (avail < needed)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
buf = gst_adapter_peek (demux->adapter, 1);
|
||||||
|
b = GST_READ_UINT8 (buf);
|
||||||
|
|
||||||
|
total = (guint64) b;
|
||||||
|
while (read <= 4 && !(total & len_mask)) {
|
||||||
|
read++;
|
||||||
|
len_mask >>= 1;
|
||||||
|
}
|
||||||
|
if (G_UNLIKELY (read > 4))
|
||||||
|
goto invalid_id;
|
||||||
|
/* need id and at least something for subsequent length */
|
||||||
|
if ((needed = read + 1) > avail)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
buf = gst_adapter_peek (demux->adapter, needed);
|
||||||
|
while (n < read) {
|
||||||
|
b = GST_READ_UINT8 (buf + n);
|
||||||
|
total = (total << 8) | b;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
*_id = (guint32) total;
|
||||||
|
|
||||||
|
/* read element length */
|
||||||
|
b = GST_READ_UINT8 (buf + n);
|
||||||
|
total = (guint64) b;
|
||||||
|
len_mask = 0x80;
|
||||||
|
read = 1;
|
||||||
|
while (read <= 8 && !(total & len_mask)) {
|
||||||
|
read++;
|
||||||
|
len_mask >>= 1;
|
||||||
|
}
|
||||||
|
if (G_UNLIKELY (read > 8))
|
||||||
|
goto invalid_length;
|
||||||
|
if ((needed += read - 1) > avail)
|
||||||
|
goto exit;
|
||||||
|
if ((total &= (len_mask - 1)) == len_mask - 1)
|
||||||
|
num_ffs++;
|
||||||
|
|
||||||
|
buf = gst_adapter_peek (demux->adapter, needed);
|
||||||
|
buf += (needed - read);
|
||||||
|
n = 1;
|
||||||
|
while (n < read) {
|
||||||
|
guint8 b = GST_READ_UINT8 (buf + n);
|
||||||
|
|
||||||
|
if (G_UNLIKELY (b == 0xff))
|
||||||
|
num_ffs++;
|
||||||
|
total = (total << 8) | b;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (read == num_ffs))
|
||||||
|
*_length = G_MAXUINT64;
|
||||||
|
else
|
||||||
|
*_length = total;
|
||||||
|
*_length = total;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
*_needed = needed;
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
invalid_id:
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT (demux,
|
||||||
|
"Invalid EBML ID size tag (0x%x) at position %" G_GUINT64_FORMAT " (0x%"
|
||||||
|
G_GINT64_MODIFIER "x)", (guint) b, demux->offset, demux->offset);
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
invalid_length:
|
||||||
|
{
|
||||||
|
GST_ERROR_OBJECT (demux,
|
||||||
|
"Invalid EBML length size tag (0x%x) at position %" G_GUINT64_FORMAT
|
||||||
|
" (0x%" G_GINT64_MODIFIER "x)", (guint) b, demux->offset,
|
||||||
|
demux->offset);
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_matroska_demux_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
|
{
|
||||||
|
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (GST_PAD_PARENT (pad));
|
||||||
|
GstEbmlRead *ebml = GST_EBML_READ (demux);
|
||||||
|
guint available;
|
||||||
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
guint needed;
|
||||||
|
guint32 id;
|
||||||
|
guint64 length;
|
||||||
|
gchar *name;
|
||||||
|
|
||||||
|
gst_adapter_push (demux->adapter, buffer);
|
||||||
|
buffer = NULL;
|
||||||
|
|
||||||
|
next:
|
||||||
|
available = gst_adapter_available (demux->adapter);
|
||||||
|
|
||||||
|
ret = gst_matroska_demux_peek_id_length (demux, &id, &length, &needed);
|
||||||
|
if (G_UNLIKELY (ret != GST_FLOW_OK))
|
||||||
|
goto parse_failed;
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
|
||||||
|
"size %" G_GUINT64_FORMAT ", needed %d, available %d", demux->offset, id,
|
||||||
|
length, needed, available);
|
||||||
|
|
||||||
|
if (needed > available)
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* only a few blocks are expected/allowed to be large,
|
||||||
|
* and will be recursed into, whereas others must fit */
|
||||||
|
if (G_LIKELY (id != GST_MATROSKA_ID_SEGMENT && id != GST_MATROSKA_ID_CLUSTER)) {
|
||||||
|
if (needed + length > available)
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
/* probably happens with 'large pieces' at the end, so consider it EOS */
|
||||||
|
if (G_UNLIKELY (length > 10 * 1024 * 1024)) {
|
||||||
|
GST_WARNING_OBJECT (demux, "forcing EOS due to size %" G_GUINT64_FORMAT,
|
||||||
|
length);
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (demux->state) {
|
||||||
|
case GST_MATROSKA_DEMUX_STATE_START:
|
||||||
|
switch (id) {
|
||||||
|
case GST_EBML_ID_HEADER:
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
ret = gst_matroska_demux_parse_header (demux);
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
goto parse_failed;
|
||||||
|
demux->state = GST_MATROSKA_DEMUX_STATE_HEADER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto invalid_header;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_DEMUX_STATE_HEADER:
|
||||||
|
case GST_MATROSKA_DEMUX_STATE_DATA:
|
||||||
|
switch (id) {
|
||||||
|
case GST_MATROSKA_ID_SEGMENT:
|
||||||
|
/* eat segment prefix */
|
||||||
|
gst_matroska_demux_flush (demux, needed);
|
||||||
|
GST_DEBUG_OBJECT (demux,
|
||||||
|
"Found Segment start at offset %" G_GUINT64_FORMAT, ebml->offset);
|
||||||
|
/* seeks are from the beginning of the segment,
|
||||||
|
* after the segment ID/length */
|
||||||
|
demux->ebml_segment_start = demux->offset;
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_SEGMENTINFO:
|
||||||
|
if (!demux->segmentinfo_parsed) {
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
ret = gst_matroska_demux_parse_info (demux);
|
||||||
|
} else {
|
||||||
|
gst_matroska_demux_flush (demux, needed + length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_TRACKS:
|
||||||
|
if (!demux->tracks_parsed) {
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
ret = gst_matroska_demux_parse_tracks (demux);
|
||||||
|
} else {
|
||||||
|
gst_matroska_demux_flush (demux, needed + length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_CLUSTER:
|
||||||
|
if (G_UNLIKELY (!demux->tracks_parsed)) {
|
||||||
|
GST_DEBUG_OBJECT (demux, "Cluster before Track");
|
||||||
|
goto not_streamable;
|
||||||
|
} else if (demux->state == GST_MATROSKA_DEMUX_STATE_HEADER) {
|
||||||
|
demux->state = GST_MATROSKA_DEMUX_STATE_DATA;
|
||||||
|
GST_DEBUG_OBJECT (demux, "signaling no more pads");
|
||||||
|
gst_element_no_more_pads (GST_ELEMENT (demux));
|
||||||
|
/* send initial newsegment */
|
||||||
|
gst_matroska_demux_send_event (demux,
|
||||||
|
gst_event_new_new_segment (FALSE, 1.0,
|
||||||
|
GST_FORMAT_TIME, 0,
|
||||||
|
(demux->segment.duration >
|
||||||
|
0) ? demux->segment.duration : -1, 0));
|
||||||
|
}
|
||||||
|
demux->cluster_time = GST_CLOCK_TIME_NONE;
|
||||||
|
demux->cluster_offset = demux->parent.offset;
|
||||||
|
/* eat cluster prefix */
|
||||||
|
gst_matroska_demux_flush (demux, needed);
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_CLUSTERTIMECODE:
|
||||||
|
{
|
||||||
|
guint64 num;
|
||||||
|
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
if ((ret = gst_ebml_read_uint (ebml, &id, &num)) != GST_FLOW_OK)
|
||||||
|
goto parse_failed;
|
||||||
|
GST_DEBUG_OBJECT (demux, "ClusterTimeCode: %" G_GUINT64_FORMAT, num);
|
||||||
|
demux->cluster_time = num;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MATROSKA_ID_BLOCKGROUP:
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
DEBUG_ELEMENT_START (demux, ebml, "BlockGroup");
|
||||||
|
if ((ret = gst_ebml_read_master (ebml, &id)) == GST_FLOW_OK) {
|
||||||
|
ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
|
||||||
|
demux->cluster_time, demux->cluster_offset, FALSE);
|
||||||
|
}
|
||||||
|
DEBUG_ELEMENT_STOP (demux, ebml, "BlockGroup", ret);
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
goto exit;
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_SIMPLEBLOCK:
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
DEBUG_ELEMENT_START (demux, ebml, "SimpleBlock");
|
||||||
|
ret = gst_matroska_demux_parse_blockgroup_or_simpleblock (demux,
|
||||||
|
demux->cluster_time, demux->cluster_offset, TRUE);
|
||||||
|
DEBUG_ELEMENT_STOP (demux, ebml, "SimpleBlock", ret);
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
goto exit;
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_ATTACHMENTS:
|
||||||
|
if (!demux->attachments_parsed) {
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
ret = gst_matroska_demux_parse_attachments (demux);
|
||||||
|
} else {
|
||||||
|
gst_matroska_demux_flush (demux, needed + length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_TAGS:
|
||||||
|
gst_matroska_demux_take (demux, length + needed);
|
||||||
|
ret = gst_matroska_demux_parse_metadata (demux);
|
||||||
|
break;
|
||||||
|
case GST_MATROSKA_ID_CHAPTERS:
|
||||||
|
name = "Cues";
|
||||||
|
goto skip;
|
||||||
|
case GST_MATROSKA_ID_SEEKHEAD:
|
||||||
|
name = "SeekHead";
|
||||||
|
goto skip;
|
||||||
|
case GST_MATROSKA_ID_CUES:
|
||||||
|
name = "Cues";
|
||||||
|
goto skip;
|
||||||
|
default:
|
||||||
|
name = "Unknown";
|
||||||
|
skip:
|
||||||
|
GST_DEBUG_OBJECT (demux, "skipping Element 0x%x (%s)", id, name);
|
||||||
|
gst_matroska_demux_flush (demux, needed + length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
goto parse_failed;
|
||||||
|
else
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
parse_failed:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
|
||||||
|
("Failed to parse Element 0x%x", id));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
not_streamable:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
|
||||||
|
("File layout does not permit streaming"));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
invalid_header:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL), ("Invalid header"));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_matroska_demux_handle_sink_event (GstPad * pad, GstEvent * event)
|
||||||
|
{
|
||||||
|
gboolean res = TRUE;
|
||||||
|
GstMatroskaDemux *demux = GST_MATROSKA_DEMUX (GST_PAD_PARENT (pad));
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux,
|
||||||
|
"have event type %s: %p on sink pad", GST_EVENT_TYPE_NAME (event), event);
|
||||||
|
|
||||||
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_NEWSEGMENT:
|
||||||
|
{
|
||||||
|
GstFormat format;
|
||||||
|
gdouble rate, arate;
|
||||||
|
gint64 start, stop, time = 0;
|
||||||
|
gboolean update;
|
||||||
|
GstSegment segment;
|
||||||
|
|
||||||
|
/* some debug output */
|
||||||
|
gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
|
||||||
|
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
|
||||||
|
&start, &stop, &time);
|
||||||
|
gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
|
||||||
|
start, stop, time);
|
||||||
|
GST_DEBUG_OBJECT (demux,
|
||||||
|
"received format %d newsegment %" GST_SEGMENT_FORMAT, format,
|
||||||
|
&segment);
|
||||||
|
|
||||||
|
/* chain will send initial newsegment after pads have been added */
|
||||||
|
GST_DEBUG_OBJECT (demux, "eating event");
|
||||||
|
gst_event_unref (event);
|
||||||
|
res = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_EVENT_EOS:
|
||||||
|
{
|
||||||
|
if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) {
|
||||||
|
gst_event_unref (event);
|
||||||
|
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
|
||||||
|
(NULL), ("got eos and didn't receive a complete header object"));
|
||||||
|
} else if (demux->num_streams == 0) {
|
||||||
|
GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
|
||||||
|
(NULL), ("got eos but no streams (yet)"));
|
||||||
|
} else {
|
||||||
|
gst_matroska_demux_send_event (demux, event);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
res = gst_pad_event_default (pad, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_matroska_demux_sink_activate (GstPad * sinkpad)
|
gst_matroska_demux_sink_activate (GstPad * sinkpad)
|
||||||
{
|
{
|
||||||
if (gst_pad_check_pull_range (sinkpad))
|
if (gst_pad_check_pull_range (sinkpad)) {
|
||||||
|
GST_DEBUG ("going to pull mode");
|
||||||
return gst_pad_activate_pull (sinkpad, TRUE);
|
return gst_pad_activate_pull (sinkpad, TRUE);
|
||||||
|
} else {
|
||||||
|
GST_DEBUG ("going to push (streaming) mode");
|
||||||
|
return gst_pad_activate_push (sinkpad, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#define __GST_MATROSKA_DEMUX_H__
|
#define __GST_MATROSKA_DEMUX_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
#include "ebml-read.h"
|
#include "ebml-read.h"
|
||||||
#include "matroska-ids.h"
|
#include "matroska-ids.h"
|
||||||
|
@ -97,6 +98,13 @@ typedef struct _GstMatroskaDemux {
|
||||||
GstEvent *close_segment;
|
GstEvent *close_segment;
|
||||||
GstEvent *new_segment;
|
GstEvent *new_segment;
|
||||||
GstTagList *global_tags;
|
GstTagList *global_tags;
|
||||||
|
|
||||||
|
/* push based mode usual suspects */
|
||||||
|
guint64 offset;
|
||||||
|
GstAdapter *adapter;
|
||||||
|
/* some state saving */
|
||||||
|
GstClockTime cluster_time;
|
||||||
|
guint64 cluster_offset;
|
||||||
} GstMatroskaDemux;
|
} GstMatroskaDemux;
|
||||||
|
|
||||||
typedef struct _GstMatroskaDemuxClass {
|
typedef struct _GstMatroskaDemuxClass {
|
||||||
|
|
Loading…
Reference in a new issue