speexdec: Get and use streamheader from the caps if possible

This allows playback of streams where the streamheader buffers
were dropped from the stream for some reason.
This commit is contained in:
Sebastian Dröge 2011-03-23 16:34:16 +01:00
parent 87e1b06cac
commit 85ace6d413
2 changed files with 82 additions and 10 deletions

View file

@ -86,6 +86,7 @@ static GstStateChangeReturn speex_dec_change_state (GstElement * element,
static gboolean speex_dec_src_event (GstPad * pad, GstEvent * event); static gboolean speex_dec_src_event (GstPad * pad, GstEvent * event);
static gboolean speex_dec_src_query (GstPad * pad, GstQuery * query); static gboolean speex_dec_src_query (GstPad * pad, GstQuery * query);
static gboolean speex_dec_sink_query (GstPad * pad, GstQuery * query); static gboolean speex_dec_sink_query (GstPad * pad, GstQuery * query);
static gboolean speex_dec_sink_setcaps (GstPad * pad, GstCaps * caps);
static const GstQueryType *speex_get_src_query_types (GstPad * pad); static const GstQueryType *speex_get_src_query_types (GstPad * pad);
static const GstQueryType *speex_get_sink_query_types (GstPad * pad); static const GstQueryType *speex_get_sink_query_types (GstPad * pad);
static gboolean speex_dec_convert (GstPad * pad, static gboolean speex_dec_convert (GstPad * pad,
@ -100,6 +101,11 @@ static void gst_speex_dec_set_property (GObject * object, guint prop_id,
static GstFlowReturn speex_dec_chain_parse_data (GstSpeexDec * dec, static GstFlowReturn speex_dec_chain_parse_data (GstSpeexDec * dec,
GstBuffer * buf, GstClockTime timestamp, GstClockTime duration); GstBuffer * buf, GstClockTime timestamp, GstClockTime duration);
static GstFlowReturn speex_dec_chain_parse_header (GstSpeexDec * dec,
GstBuffer * buf);
static GstFlowReturn speex_dec_chain_parse_comments (GstSpeexDec * dec,
GstBuffer * buf);
static void static void
gst_speex_dec_base_init (gpointer g_class) gst_speex_dec_base_init (gpointer g_class)
{ {
@ -148,6 +154,9 @@ gst_speex_dec_reset (GstSpeexDec * dec)
dec->header = NULL; dec->header = NULL;
speex_bits_destroy (&dec->bits); speex_bits_destroy (&dec->bits);
gst_buffer_replace (&dec->streamheader, NULL);
gst_buffer_replace (&dec->vorbiscomment, NULL);
if (dec->stereo) { if (dec->stereo) {
speex_stereo_state_destroy (dec->stereo); speex_stereo_state_destroy (dec->stereo);
dec->stereo = NULL; dec->stereo = NULL;
@ -172,6 +181,8 @@ gst_speex_dec_init (GstSpeexDec * dec, GstSpeexDecClass * g_class)
GST_DEBUG_FUNCPTR (speex_get_sink_query_types)); GST_DEBUG_FUNCPTR (speex_get_sink_query_types));
gst_pad_set_query_function (dec->sinkpad, gst_pad_set_query_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (speex_dec_sink_query)); GST_DEBUG_FUNCPTR (speex_dec_sink_query));
gst_pad_set_setcaps_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (speex_dec_sink_setcaps));
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
dec->srcpad = dec->srcpad =
@ -190,6 +201,46 @@ gst_speex_dec_init (GstSpeexDec * dec, GstSpeexDecClass * g_class)
gst_speex_dec_reset (dec); gst_speex_dec_reset (dec);
} }
static gboolean
speex_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstSpeexDec *dec = GST_SPEEX_DEC (gst_pad_get_parent (pad));
gboolean ret = TRUE;
GstStructure *s;
const GValue *streamheader;
s = gst_caps_get_structure (caps, 0);
if ((streamheader = gst_structure_get_value (s, "streamheader")) &&
G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) &&
gst_value_array_get_size (streamheader) >= 2) {
const GValue *header, *vorbiscomment;
GstBuffer *buf;
GstFlowReturn res = GST_FLOW_OK;
header = gst_value_array_get_value (streamheader, 0);
if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) {
buf = gst_value_get_buffer (header);
res = speex_dec_chain_parse_header (dec, buf);
if (res != GST_FLOW_OK)
goto done;
gst_buffer_replace (&dec->streamheader, buf);
}
vorbiscomment = gst_value_array_get_value (streamheader, 1);
if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) {
buf = gst_value_get_buffer (vorbiscomment);
res = speex_dec_chain_parse_comments (dec, buf);
if (res != GST_FLOW_OK)
goto done;
gst_buffer_replace (&dec->vorbiscomment, buf);
}
}
done:
gst_object_unref (dec);
return ret;
}
static gboolean static gboolean
speex_dec_convert (GstPad * pad, speex_dec_convert (GstPad * pad,
GstFormat src_format, gint64 src_value, GstFormat src_format, gint64 src_value,
@ -760,19 +811,37 @@ speex_dec_chain (GstPad * pad, GstBuffer * buf)
dec = GST_SPEEX_DEC (gst_pad_get_parent (pad)); dec = GST_SPEEX_DEC (gst_pad_get_parent (pad));
switch (dec->packetno) { /* If we have the streamheader and vorbiscomment from the caps already
case 0: * ignore them here */
res = speex_dec_chain_parse_header (dec, buf); if (dec->streamheader && dec->vorbiscomment) {
break; if (GST_BUFFER_SIZE (dec->streamheader) == GST_BUFFER_SIZE (buf)
case 1: && memcmp (GST_BUFFER_DATA (dec->streamheader), GST_BUFFER_DATA (buf),
res = speex_dec_chain_parse_comments (dec, buf); GST_BUFFER_SIZE (buf)) == 0) {
break; res = GST_FLOW_OK;
default: } else if (GST_BUFFER_SIZE (dec->vorbiscomment) == GST_BUFFER_SIZE (buf)
{ && memcmp (GST_BUFFER_DATA (dec->vorbiscomment), GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf)) == 0) {
res = GST_FLOW_OK;
} else {
res = res =
speex_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), speex_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf)); GST_BUFFER_DURATION (buf));
break; }
} else {
/* Otherwise fall back to packet counting and assume that the
* first two packets are the headers. */
switch (dec->packetno) {
case 0:
res = speex_dec_chain_parse_header (dec, buf);
break;
case 1:
res = speex_dec_chain_parse_comments (dec, buf);
break;
default:
res =
speex_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf),
GST_BUFFER_DURATION (buf));
break;
} }
} }

View file

@ -68,6 +68,9 @@ struct _GstSpeexDec {
guint64 packetno; guint64 packetno;
GstSegment segment; /* STREAM LOCK */ GstSegment segment; /* STREAM LOCK */
GstBuffer *streamheader;
GstBuffer *vorbiscomment;
}; };
struct _GstSpeexDecClass { struct _GstSpeexDecClass {