mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-02 16:52:42 +00:00
ext/ogg/gstogmparse.c: Refactor ogm parse, do better input checking, misc. clean-ups.
Original commit message from CVS: * ext/ogg/gstogmparse.c: (gst_ogm_audio_parse_base_init), (gst_ogm_video_parse_base_init), (gst_ogm_text_parse_base_init), (gst_ogm_parse_class_init), (gst_ogm_parse_dispose), (gst_ogm_parse_init), (gst_ogm_audio_parse_init), (gst_ogm_video_parse_init), (gst_ogm_text_parse_init), (gst_ogm_parse_stream_header), (gst_ogm_parse_comment_packet), (gst_ogm_text_parse_strip_trailing_zeroes), (gst_ogm_parse_data_packet), (gst_ogm_parse_chain), (gst_ogm_parse_sink_event), (gst_ogm_parse_change_state): Refactor ogm parse, do better input checking, misc. clean-ups. Cache incoming events and push them once the source pad has been created. Don't pass unterminated strings to sscanf(). Strip trailing zeroes from subtitle text output, since they are not valid UTF-8. Don't push vorbiscomment packets on the subtitle text pad. Output perfect streams if possible.
This commit is contained in:
parent
df70110d58
commit
e7221135cd
2 changed files with 403 additions and 202 deletions
18
ChangeLog
18
ChangeLog
|
@ -1,3 +1,21 @@
|
|||
2006-08-23 Tim-Philipp Müller <tim at centricular dot net>
|
||||
|
||||
* ext/ogg/gstogmparse.c: (gst_ogm_audio_parse_base_init),
|
||||
(gst_ogm_video_parse_base_init), (gst_ogm_text_parse_base_init),
|
||||
(gst_ogm_parse_class_init), (gst_ogm_parse_dispose),
|
||||
(gst_ogm_parse_init), (gst_ogm_audio_parse_init),
|
||||
(gst_ogm_video_parse_init), (gst_ogm_text_parse_init),
|
||||
(gst_ogm_parse_stream_header), (gst_ogm_parse_comment_packet),
|
||||
(gst_ogm_text_parse_strip_trailing_zeroes),
|
||||
(gst_ogm_parse_data_packet), (gst_ogm_parse_chain),
|
||||
(gst_ogm_parse_sink_event), (gst_ogm_parse_change_state):
|
||||
Refactor ogm parse, do better input checking, misc. clean-ups.
|
||||
Cache incoming events and push them once the source pad has
|
||||
been created. Don't pass unterminated strings to sscanf().
|
||||
Strip trailing zeroes from subtitle text output, since they
|
||||
are not valid UTF-8. Don't push vorbiscomment packets on
|
||||
the subtitle text pad. Output perfect streams if possible.
|
||||
|
||||
2006-08-23 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* tests/check/libs/cddabasesrc.c: (GST_START_TEST):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/* GStreamer
|
||||
/* GStreamer OGM parsing
|
||||
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
*
|
||||
* gstogmparse.c: OGM stream header parsing (and data passthrough)
|
||||
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -94,7 +93,7 @@ typedef struct _stream_header_audio
|
|||
typedef struct _stream_header
|
||||
{
|
||||
gchar streamtype[8];
|
||||
gchar subtype[4];
|
||||
gchar subtype[4 + 1];
|
||||
|
||||
/* size of the structure */
|
||||
gint32 size;
|
||||
|
@ -126,6 +125,9 @@ typedef struct _GstOgmParse
|
|||
GstPad *srcpad, *sinkpad;
|
||||
GstPadTemplate *srcpadtempl;
|
||||
|
||||
/* we need to cache events that we receive before creating the source pad */
|
||||
GList *cached_events;
|
||||
|
||||
/* audio or video */
|
||||
stream_header hdr;
|
||||
|
||||
|
@ -138,13 +140,13 @@ typedef struct _GstOgmParseClass
|
|||
GstElementClass parent_class;
|
||||
} GstOgmParseClass;
|
||||
|
||||
static GstStaticPadTemplate ogm_video_parse_sink_template_factory =
|
||||
static GstStaticPadTemplate sink_factory_video =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-ogm-video"));
|
||||
static GstStaticPadTemplate ogm_audio_parse_sink_template_factory =
|
||||
static GstStaticPadTemplate sink_factory_audio =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-ogm-audio"));
|
||||
static GstStaticPadTemplate ogm_text_parse_sink_template_factory =
|
||||
static GstStaticPadTemplate sink_factory_text =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-ogm-text"));
|
||||
static GstPadTemplate *video_src_templ, *audio_src_templ, *text_src_templ;
|
||||
|
@ -164,6 +166,7 @@ static void gst_ogm_audio_parse_init (GstOgmParse * ogm);
|
|||
static void gst_ogm_text_parse_init (GstOgmParse * ogm);
|
||||
|
||||
static const GstQueryType *gst_ogm_parse_get_sink_querytypes (GstPad * pad);
|
||||
static gboolean gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event);
|
||||
static gboolean gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query);
|
||||
static gboolean gst_ogm_parse_sink_convert (GstPad * pad, GstFormat src_format,
|
||||
gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
|
||||
|
@ -288,7 +291,7 @@ gst_ogm_audio_parse_base_init (GstOgmParseClass * klass)
|
|||
gst_element_class_set_details (element_class, &gst_ogm_audio_parse_details);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&ogm_audio_parse_sink_template_factory));
|
||||
gst_static_pad_template_get (&sink_factory_audio));
|
||||
audio_src_templ = gst_pad_template_new ("src",
|
||||
GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
|
||||
gst_element_class_add_pad_template (element_class, audio_src_templ);
|
||||
|
@ -303,7 +306,7 @@ gst_ogm_video_parse_base_init (GstOgmParseClass * klass)
|
|||
gst_element_class_set_details (element_class, &gst_ogm_video_parse_details);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&ogm_video_parse_sink_template_factory));
|
||||
gst_static_pad_template_get (&sink_factory_video));
|
||||
video_src_templ = gst_pad_template_new ("src",
|
||||
GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
|
||||
gst_element_class_add_pad_template (element_class, video_src_templ);
|
||||
|
@ -318,7 +321,7 @@ gst_ogm_text_parse_base_init (GstOgmParseClass * klass)
|
|||
gst_element_class_set_details (element_class, &gst_ogm_text_parse_details);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&ogm_text_parse_sink_template_factory));
|
||||
gst_static_pad_template_get (&sink_factory_text));
|
||||
text_src_templ = gst_pad_template_new ("src",
|
||||
GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
|
||||
gst_element_class_add_pad_template (element_class, text_src_templ);
|
||||
|
@ -338,23 +341,22 @@ gst_ogm_parse_class_init (GstOgmParseClass * klass)
|
|||
static void
|
||||
gst_ogm_parse_init (GstOgmParse * ogm)
|
||||
{
|
||||
/* initalize */
|
||||
memset (&ogm->hdr, 0, sizeof (ogm->hdr));
|
||||
ogm->next_granulepos = 0;
|
||||
ogm->srcpad = NULL;
|
||||
ogm->cached_events = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ogm_audio_parse_init (GstOgmParse * ogm)
|
||||
{
|
||||
/* create the pads */
|
||||
ogm->sinkpad =
|
||||
gst_pad_new_from_static_template (&ogm_audio_parse_sink_template_factory,
|
||||
"sink");
|
||||
ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_audio, "sink");
|
||||
gst_pad_set_query_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
|
||||
gst_pad_set_chain_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
|
||||
gst_pad_set_event_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
|
||||
gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
|
||||
|
||||
ogm->srcpad = NULL;
|
||||
|
@ -364,14 +366,13 @@ gst_ogm_audio_parse_init (GstOgmParse * ogm)
|
|||
static void
|
||||
gst_ogm_video_parse_init (GstOgmParse * ogm)
|
||||
{
|
||||
/* create the pads */
|
||||
ogm->sinkpad =
|
||||
gst_pad_new_from_static_template (&ogm_video_parse_sink_template_factory,
|
||||
"sink");
|
||||
ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_video, "sink");
|
||||
gst_pad_set_query_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
|
||||
gst_pad_set_chain_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
|
||||
gst_pad_set_event_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
|
||||
gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
|
||||
|
||||
ogm->srcpad = NULL;
|
||||
|
@ -381,16 +382,15 @@ gst_ogm_video_parse_init (GstOgmParse * ogm)
|
|||
static void
|
||||
gst_ogm_text_parse_init (GstOgmParse * ogm)
|
||||
{
|
||||
/* create the pads */
|
||||
ogm->sinkpad =
|
||||
gst_pad_new_from_static_template (&ogm_text_parse_sink_template_factory,
|
||||
"sink");
|
||||
ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_text, "sink");
|
||||
gst_pad_set_query_type_function (ogm->sinkpad,
|
||||
gst_ogm_parse_get_sink_querytypes);
|
||||
gst_pad_set_query_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
|
||||
gst_pad_set_chain_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
|
||||
gst_pad_set_event_function (ogm->sinkpad,
|
||||
GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
|
||||
gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
|
||||
|
||||
ogm->srcpad = NULL;
|
||||
|
@ -516,107 +516,114 @@ gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query)
|
|||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer)
|
||||
gst_ogm_parse_stream_header (GstOgmParse * ogm, const guint8 * data, guint size)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstOgmParse *ogm = GST_OGM_PARSE (GST_PAD_PARENT (pad));
|
||||
GstBuffer *buf = GST_BUFFER (buffer);
|
||||
guint8 *data = GST_BUFFER_DATA (buf);
|
||||
guint size = GST_BUFFER_SIZE (buf);
|
||||
|
||||
GST_DEBUG_OBJECT (ogm, "New packet with packet start code 0x%02x", data[0]);
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x01:{
|
||||
GstCaps *caps = NULL;
|
||||
|
||||
/* stream header */
|
||||
if (size < (1 + OGM_STREAM_HEADER_SIZE)) {
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE,
|
||||
("Buffer too small"), (NULL));
|
||||
break;
|
||||
}
|
||||
if (size < OGM_STREAM_HEADER_SIZE)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (!memcmp (&data[1], "video\000\000\000", 8)) {
|
||||
ogm->hdr.s.video.width = GST_READ_UINT32_LE (&data[45]);
|
||||
ogm->hdr.s.video.height = GST_READ_UINT32_LE (&data[49]);
|
||||
} else if (!memcmp (&data[1], "audio\000\000\000", 8)) {
|
||||
ogm->hdr.s.audio.channels = GST_READ_UINT32_LE (&data[45]);
|
||||
ogm->hdr.s.audio.blockalign = GST_READ_UINT32_LE (&data[47]);
|
||||
ogm->hdr.s.audio.avgbytespersec = GST_READ_UINT32_LE (&data[49]);
|
||||
} else if (!memcmp (&data[1], "text\000\000\000\000", 8)) {
|
||||
if (!memcmp (data, "video\000\000\000", 8)) {
|
||||
ogm->hdr.s.video.width = GST_READ_UINT32_LE (&data[44]);
|
||||
ogm->hdr.s.video.height = GST_READ_UINT32_LE (&data[48]);
|
||||
} else if (!memcmp (data, "audio\000\000\000", 8)) {
|
||||
ogm->hdr.s.audio.channels = GST_READ_UINT32_LE (&data[44]);
|
||||
ogm->hdr.s.audio.blockalign = GST_READ_UINT32_LE (&data[46]);
|
||||
ogm->hdr.s.audio.avgbytespersec = GST_READ_UINT32_LE (&data[48]);
|
||||
} else if (!memcmp (data, "text\000\000\000\000", 8)) {
|
||||
/* nothing here */
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE,
|
||||
("Unknown stream type"), (NULL));
|
||||
break;
|
||||
goto cannot_decode;
|
||||
}
|
||||
memcpy (ogm->hdr.streamtype, &data[1], 8);
|
||||
memcpy (ogm->hdr.subtype, &data[9], 4);
|
||||
ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
|
||||
ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
|
||||
ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
|
||||
ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
|
||||
ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
|
||||
ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
|
||||
memcpy (ogm->hdr.streamtype, &data[0], 8);
|
||||
memcpy (ogm->hdr.subtype, &data[8], 4);
|
||||
ogm->hdr.subtype[4] = '\0';
|
||||
ogm->hdr.size = GST_READ_UINT32_LE (&data[12]);
|
||||
ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[16]);
|
||||
ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[24]);
|
||||
ogm->hdr.default_len = GST_READ_UINT32_LE (&data[32]);
|
||||
ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[36]);
|
||||
ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[40]);
|
||||
|
||||
switch (ogm->hdr.streamtype[0]) {
|
||||
case 'a':{
|
||||
guint codec_id;
|
||||
guint codec_id = 0;
|
||||
|
||||
if (!sscanf (ogm->hdr.subtype, "%04x", &codec_id)) {
|
||||
caps = NULL;
|
||||
break;
|
||||
if (sscanf (ogm->hdr.subtype, "%04x", &codec_id) != 1) {
|
||||
GST_WARNING_OBJECT (ogm, "cannot parse subtype %s", ogm->hdr.subtype);
|
||||
}
|
||||
caps = gst_riff_create_audio_caps (codec_id,
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
caps =
|
||||
gst_riff_create_audio_caps (codec_id, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (caps == NULL) {
|
||||
GST_WARNING_OBJECT (ogm, "no audio caps for codec %u found", codec_id);
|
||||
caps = gst_caps_new_simple ("audio/x-ogm-unknown", "codec_id",
|
||||
G_TYPE_INT, (gint) codec_id, NULL);
|
||||
}
|
||||
|
||||
gst_caps_set_simple (caps,
|
||||
"channels", G_TYPE_INT, ogm->hdr.s.audio.channels,
|
||||
"rate", G_TYPE_INT, ogm->hdr.samples_per_unit, NULL);
|
||||
GST_LOG_OBJECT (ogm, "Type: %s, subtype: 0x%04x, "
|
||||
"channels: %d, samplerate: %d, blockalign: %d, bps: %d",
|
||||
|
||||
GST_LOG_OBJECT (ogm, "Type: %s, subtype: 0x%04x, channels: %d, "
|
||||
"samplerate: %d, blockalign: %d, bps: %d, caps = %" GST_PTR_FORMAT,
|
||||
ogm->hdr.streamtype, codec_id, ogm->hdr.s.audio.channels,
|
||||
ogm->hdr.samples_per_unit,
|
||||
ogm->hdr.s.audio.blockalign, ogm->hdr.s.audio.avgbytespersec);
|
||||
ogm->hdr.samples_per_unit, ogm->hdr.s.audio.blockalign,
|
||||
ogm->hdr.s.audio.avgbytespersec, caps);
|
||||
break;
|
||||
}
|
||||
case 'v':{
|
||||
guint32 fcc;
|
||||
guint32 fourcc;
|
||||
|
||||
fcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0],
|
||||
fourcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0],
|
||||
ogm->hdr.subtype[1], ogm->hdr.subtype[2], ogm->hdr.subtype[3]);
|
||||
|
||||
caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (caps == NULL) {
|
||||
GST_WARNING_OBJECT (ogm, "could not find video caps for fourcc %"
|
||||
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
|
||||
caps = gst_caps_new_simple ("video/x-ogm-unknown", "fourcc",
|
||||
GST_TYPE_FOURCC, fourcc, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (ogm, "Type: %s, subtype: %" GST_FOURCC_FORMAT
|
||||
", size: %dx%d, timeunit: %" G_GINT64_FORMAT
|
||||
" (fps: %lf), s/u: %" G_GINT64_FORMAT ", "
|
||||
"def.len: %d, bufsize: %d, bps: %d",
|
||||
ogm->hdr.streamtype, GST_FOURCC_ARGS (fcc),
|
||||
"def.len: %d, bufsize: %d, bps: %d, caps = %" GST_PTR_FORMAT,
|
||||
ogm->hdr.streamtype, GST_FOURCC_ARGS (fourcc),
|
||||
ogm->hdr.s.video.width, ogm->hdr.s.video.height,
|
||||
ogm->hdr.time_unit, 10000000. / ogm->hdr.time_unit,
|
||||
ogm->hdr.samples_per_unit, ogm->hdr.default_len,
|
||||
ogm->hdr.buffersize, ogm->hdr.bits_per_sample);
|
||||
caps = gst_riff_create_video_caps (fcc, NULL, NULL, NULL, NULL, NULL);
|
||||
ogm->hdr.buffersize, ogm->hdr.bits_per_sample, caps);
|
||||
|
||||
gst_caps_set_simple (caps,
|
||||
"width", G_TYPE_INT, ogm->hdr.s.video.width,
|
||||
"height", G_TYPE_INT, ogm->hdr.s.video.height,
|
||||
"framerate", GST_TYPE_FRACTION, 10000000, ogm->hdr.time_unit,
|
||||
NULL);
|
||||
"framerate", GST_TYPE_FRACTION, 10000000, ogm->hdr.time_unit, NULL);
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
case 't':{
|
||||
GST_LOG_OBJECT (ogm, "Type: %s, s/u: %" G_GINT64_FORMAT
|
||||
", timeunit=%" G_GINT64_FORMAT,
|
||||
ogm->hdr.streamtype, ogm->hdr.samples_per_unit,
|
||||
ogm->hdr.time_unit);
|
||||
ogm->hdr.streamtype, ogm->hdr.samples_per_unit, ogm->hdr.time_unit);
|
||||
caps = gst_caps_new_simple ("text/plain", NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (caps == NULL)
|
||||
goto cannot_decode;
|
||||
|
||||
if (ogm->srcpad) {
|
||||
GstCaps *current_caps = GST_PAD_CAPS (ogm->srcpad);
|
||||
|
||||
if (current_caps && !gst_caps_is_equal (current_caps, caps)) {
|
||||
if (current_caps && caps && !gst_caps_is_equal (current_caps, caps)) {
|
||||
GST_WARNING_OBJECT (ogm, "Already an existing pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (ogm->srcpad));
|
||||
gst_element_remove_pad (GST_ELEMENT (ogm), ogm->srcpad);
|
||||
|
@ -625,27 +632,65 @@ gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer)
|
|||
GST_DEBUG_OBJECT (ogm, "Existing pad has the same caps, do nothing");
|
||||
}
|
||||
}
|
||||
|
||||
if (ogm->srcpad == NULL) {
|
||||
if (caps) {
|
||||
ogm->srcpad = gst_pad_new ("src", GST_PAD_SRC);
|
||||
gst_pad_set_caps (ogm->srcpad, caps);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (ogm,
|
||||
"No fixed caps were found, carrying on with template");
|
||||
GList *l, *cached_events;
|
||||
|
||||
ogm->srcpad = gst_pad_new_from_template (ogm->srcpadtempl, "src");
|
||||
}
|
||||
gst_pad_use_fixed_caps (ogm->srcpad);
|
||||
gst_pad_set_caps (ogm->srcpad, caps);
|
||||
gst_element_add_pad (GST_ELEMENT (ogm), ogm->srcpad);
|
||||
GST_INFO_OBJECT (ogm, "Added pad %s:%s with caps %" GST_PTR_FORMAT,
|
||||
GST_DEBUG_PAD_NAME (ogm->srcpad), caps);
|
||||
|
||||
GST_OBJECT_LOCK (ogm);
|
||||
cached_events = ogm->cached_events;
|
||||
ogm->cached_events = NULL;
|
||||
GST_OBJECT_UNLOCK (ogm);
|
||||
|
||||
for (l = cached_events; l; l = l->next) {
|
||||
GstEvent *event = GST_EVENT_CAST (l->data);
|
||||
|
||||
GST_DEBUG_OBJECT (ogm, "Pushing cached event %" GST_PTR_FORMAT, event);
|
||||
gst_pad_push_event (ogm->srcpad, event);
|
||||
}
|
||||
break;
|
||||
g_list_free (cached_events);
|
||||
}
|
||||
case 0x03:{
|
||||
/* vorbis comment - need to extract the tags ourself for subtitle
|
||||
* streams since we want to know the language and there won't be a
|
||||
* decoder to do this for us like with vorbis */
|
||||
|
||||
gst_caps_unref (caps);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
|
||||
/* ERRORS */
|
||||
buffer_too_small:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE, ("Buffer too small"), (NULL));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
cannot_decode:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("unknown ogm format"));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ogm_parse_comment_packet (GstOgmParse * ogm, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn ret;
|
||||
|
||||
if (ogm->srcpad == NULL) {
|
||||
GST_DEBUG ("no source pad");
|
||||
return GST_FLOW_WRONG_STATE;
|
||||
}
|
||||
|
||||
/* if this is not a subtitle stream, push the vorbiscomment packet
|
||||
* on downstream, the respective decoder will handle it; if it is
|
||||
* a subtitle stream, we will have to handle the comment ourself */
|
||||
if (ogm->hdr.streamtype[0] == 't') {
|
||||
GstTagList *tags;
|
||||
|
||||
tags = gst_tag_list_from_vorbiscomment_buffer (buffer,
|
||||
tags = gst_tag_list_from_vorbiscomment_buffer (buf,
|
||||
(guint8 *) "\003vorbis", 7, NULL);
|
||||
|
||||
if (tags) {
|
||||
|
@ -654,77 +699,212 @@ gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer)
|
|||
} else {
|
||||
GST_DEBUG_OBJECT (ogm, "failed to extract tags from vorbis comment");
|
||||
}
|
||||
/* do not push packet downstream, just let parent unref it */
|
||||
ret = GST_FLOW_OK;
|
||||
} else {
|
||||
buf = gst_buffer_copy (buf);
|
||||
gst_buffer_set_caps (buf, GST_PAD_CAPS (ogm->srcpad));
|
||||
ret = gst_pad_push (ogm->srcpad, buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if ((data[0] & 0x01) == 0) {
|
||||
/* data - push on */
|
||||
guint len = ((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1);
|
||||
guint xsize = 0, n;
|
||||
GstBuffer *sbuf;
|
||||
gboolean keyframe = (data[0] & 0x08) >> 3;
|
||||
|
||||
if (size < len + 1) {
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE,
|
||||
("Buffer too small"), (NULL));
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ogm_text_parse_strip_trailing_zeroes (GstOgmParse * ogm, GstBuffer * buf)
|
||||
{
|
||||
const guint8 *data;
|
||||
guint size;
|
||||
|
||||
g_assert (gst_buffer_is_metadata_writable (buf));
|
||||
|
||||
/* zeroes are not valid UTF-8 characters, so strip them from output */
|
||||
data = GST_BUFFER_DATA (buf);
|
||||
size = GST_BUFFER_SIZE (buf);
|
||||
while (size > 0 && data[size - 1] == '\0') {
|
||||
--size;
|
||||
}
|
||||
|
||||
GST_BUFFER_SIZE (buf) = size;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ogm_parse_data_packet (GstOgmParse * ogm, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn ret;
|
||||
const guint8 *data;
|
||||
GstBuffer *sbuf;
|
||||
gboolean keyframe;
|
||||
guint size, len, n, xsize = 0;
|
||||
|
||||
data = GST_BUFFER_DATA (buf);
|
||||
size = GST_BUFFER_SIZE (buf);
|
||||
|
||||
if ((data[0] & 0x01) != 0)
|
||||
goto invalid_startcode;
|
||||
|
||||
/* data - push on */
|
||||
len = ((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1);
|
||||
keyframe = (((data[0] & 0x08) >> 3) != 0);
|
||||
|
||||
if ((1 + len) > size)
|
||||
goto buffer_too_small;
|
||||
|
||||
for (n = len; n > 0; n--) {
|
||||
xsize = (xsize << 8) | data[n];
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (ogm,
|
||||
"[0x%02x] samples: %d, hdrbytes: %d, datasize: %d",
|
||||
GST_LOG_OBJECT (ogm, "[0x%02x] samples: %d, hdrbytes: %d, datasize: %d",
|
||||
data[0], xsize, len, size - len - 1);
|
||||
|
||||
sbuf = gst_buffer_create_sub (buf, len + 1, size - len - 1);
|
||||
if (GST_BUFFER_OFFSET_END_IS_VALID (buf)) {
|
||||
|
||||
if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
|
||||
ogm->next_granulepos = GST_BUFFER_OFFSET_END (buf);
|
||||
}
|
||||
|
||||
switch (ogm->hdr.streamtype[0]) {
|
||||
case 't':
|
||||
case 'v':{
|
||||
gint samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize;
|
||||
GstClockTime ts, next_ts;
|
||||
guint samples;
|
||||
|
||||
if (!keyframe)
|
||||
samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize;
|
||||
|
||||
if (!keyframe) {
|
||||
GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||
}
|
||||
|
||||
/* shouldn't this be granulepos - samples? (tpm) */
|
||||
ts = gst_util_uint64_scale (ogm->next_granulepos,
|
||||
ogm->hdr.time_unit * GST_SECOND, 10000000);
|
||||
next_ts = gst_util_uint64_scale (ogm->next_granulepos + samples,
|
||||
ogm->hdr.time_unit * GST_SECOND, 10000000);
|
||||
|
||||
GST_BUFFER_TIMESTAMP (sbuf) = ts;
|
||||
GST_BUFFER_DURATION (sbuf) = next_ts - ts;
|
||||
|
||||
GST_BUFFER_TIMESTAMP (sbuf) = (GST_SECOND / 10000000) *
|
||||
ogm->next_granulepos * ogm->hdr.time_unit;
|
||||
GST_BUFFER_DURATION (sbuf) = (GST_SECOND / 10000000) *
|
||||
ogm->hdr.time_unit * samples;
|
||||
ogm->next_granulepos += samples;
|
||||
|
||||
if (ogm->hdr.streamtype[0] == 't') {
|
||||
gst_ogm_text_parse_strip_trailing_zeroes (ogm, sbuf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'a':
|
||||
GST_BUFFER_TIMESTAMP (sbuf) = GST_SECOND *
|
||||
ogm->next_granulepos / ogm->hdr.samples_per_unit;
|
||||
GST_BUFFER_DURATION (sbuf) = GST_SECOND * xsize /
|
||||
ogm->hdr.samples_per_unit;
|
||||
case 'a':{
|
||||
GstClockTime ts, next_ts;
|
||||
|
||||
/* shouldn't this be granulepos - samples? (tpm) */
|
||||
ts = gst_util_uint64_scale_int (ogm->next_granulepos,
|
||||
GST_SECOND, ogm->hdr.samples_per_unit);
|
||||
next_ts = gst_util_uint64_scale_int (ogm->next_granulepos + xsize,
|
||||
GST_SECOND, ogm->hdr.samples_per_unit);
|
||||
|
||||
GST_BUFFER_TIMESTAMP (sbuf) = ts;
|
||||
GST_BUFFER_DURATION (sbuf) = next_ts - ts;
|
||||
|
||||
ogm->next_granulepos += xsize;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gst_buffer_unref (sbuf);
|
||||
sbuf = NULL;
|
||||
GST_ELEMENT_ERROR (ogm, RESOURCE, SYNC, (NULL), (NULL));
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ogm->srcpad) {
|
||||
gst_buffer_set_caps (sbuf, GST_PAD_CAPS (ogm->srcpad));
|
||||
GST_LOG_OBJECT (ogm, "Pushing buffer with ts=%" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sbuf)));
|
||||
ret = gst_pad_push (ogm->srcpad, sbuf);
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_DEBUG_OBJECT (ogm, "Flow return: %s", gst_flow_get_name (ret));
|
||||
GST_DEBUG_OBJECT (ogm, "Flow on %s:%s = %s",
|
||||
GST_DEBUG_PAD_NAME (ogm->srcpad), gst_flow_get_name (ret));
|
||||
}
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE,
|
||||
("Wrong packet startcode 0x%02x", data[0]), (NULL));
|
||||
ret = GST_FLOW_WRONG_STATE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
invalid_startcode:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
|
||||
("unexpected packet startcode 0x%02x", data[0]));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
buffer_too_small:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
|
||||
("buffer too small, len+1=%u, size=%u", len + 1, size));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_ogm_parse_chain (GstPad * pad, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstOgmParse *ogm = GST_OGM_PARSE (GST_PAD_PARENT (pad));
|
||||
guint8 *data = GST_BUFFER_DATA (buf);
|
||||
guint size = GST_BUFFER_SIZE (buf);
|
||||
|
||||
if (size < 1)
|
||||
goto buffer_too_small;
|
||||
|
||||
GST_LOG_OBJECT (ogm, "Packet with start code 0x%02x", data[0]);
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x01:{
|
||||
ret = gst_ogm_parse_stream_header (ogm, data + 1, size - 1);
|
||||
break;
|
||||
}
|
||||
case 0x03:{
|
||||
ret = gst_ogm_parse_comment_packet (ogm, buf);
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
ret = gst_ogm_parse_data_packet (ogm, buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
if (ret != GST_FLOW_OK) {
|
||||
GST_DEBUG_OBJECT (ogm, "Flow: %s", gst_flow_get_name (ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
buffer_too_small:
|
||||
{
|
||||
GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("buffer too small"));
|
||||
gst_buffer_unref (buf);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event)
|
||||
{
|
||||
GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
|
||||
gboolean res;
|
||||
|
||||
GST_LOG_OBJECT (ogm, "processing %s event", GST_EVENT_TYPE_NAME (event));
|
||||
|
||||
GST_OBJECT_LOCK (ogm);
|
||||
if (ogm->srcpad == NULL) {
|
||||
ogm->cached_events = g_list_append (ogm->cached_events, event);
|
||||
GST_OBJECT_UNLOCK (ogm);
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_OBJECT_UNLOCK (ogm);
|
||||
res = gst_pad_event_default (pad, event);
|
||||
}
|
||||
|
||||
gst_object_unref (ogm);
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
|
@ -745,6 +925,9 @@ gst_ogm_parse_change_state (GstElement * element, GstStateChange transition)
|
|||
}
|
||||
memset (&ogm->hdr, 0, sizeof (ogm->hdr));
|
||||
ogm->next_granulepos = 0;
|
||||
g_list_foreach (ogm->cached_events, (GFunc) gst_mini_object_unref, NULL);
|
||||
g_list_free (ogm->cached_events);
|
||||
ogm->cached_events = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue