summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Hervey <edward@centricular.com>2018-02-13 08:36:30 +0100
committerEdward Hervey <bilboed@bilboed.com>2018-02-13 09:02:46 +0100
commit865209d3afeff82f629bf4bf293fce9cc40df53b (patch)
tree7465d02fde36a8849ba34a765c493af80a01b15f
parentfc4187977b26b80127ce07a36bf838a97b228284 (diff)
downloadgstreamer-plugins-base-865209d3afeff82f629bf4bf293fce9cc40df53b.tar.gz
vorbisdec: Improve "new headers while initialized" handling
If new headers arrive after we are initialized, we need to make sure that they are indeed valid. A vorbis bitstream always begins with three header packets and must be in order. Also some streams have unframed (invalid?) headers that might confuse and disrupt the decoding process. Therefore if ever we see new headers, we accumulate them and once we get a non-header packet we check them to make sure that: * We have at least 3 headers * They are the expected ones (identification, comments and setup) * They are in order * Any other "header" is ignored If those conditions are met, we reset and reconfigure the decoder https://bugzilla.gnome.org/show_bug.cgi?id=784530
-rw-r--r--ext/vorbis/gstvorbisdec.c111
-rw-r--r--ext/vorbis/gstvorbisdec.h2
2 files changed, 106 insertions, 7 deletions
diff --git a/ext/vorbis/gstvorbisdec.c b/ext/vorbis/gstvorbisdec.c
index c4b92e35a..f4e563c13 100644
--- a/ext/vorbis/gstvorbisdec.c
+++ b/ext/vorbis/gstvorbisdec.c
@@ -159,6 +159,10 @@ vorbis_dec_stop (GstAudioDecoder * dec)
vorbis_dsp_clear (&vd->vd);
vorbis_comment_clear (&vd->vc);
vorbis_info_clear (&vd->vi);
+ if (vd->pending_headers) {
+ g_list_free_full (vd->pending_headers, (GDestroyNotify) gst_buffer_unref);
+ vd->pending_headers = NULL;
+ }
return TRUE;
}
@@ -547,6 +551,92 @@ wrong_samples:
}
static GstFlowReturn
+check_pending_headers (GstVorbisDec * vd)
+{
+ GstBuffer *buffer1, *buffer3, *buffer5;
+ GstMapInfo map;
+ gboolean isvalid;
+ GList *tmp = vd->pending_headers;
+ GstFlowReturn result = GST_FLOW_OK;
+
+ if (g_list_length (vd->pending_headers) < MIN_NUM_HEADERS)
+ goto not_enough;
+
+ buffer1 = (GstBuffer *) tmp->data;
+ tmp = tmp->next;
+ buffer3 = (GstBuffer *) tmp->data;
+ tmp = tmp->next;
+ buffer5 = (GstBuffer *) tmp->data;
+
+ /* Start checking the headers */
+ gst_buffer_map (buffer1, &map, GST_MAP_READ);
+ isvalid = map.size >= 1 && map.data[0] == 0x01;
+ gst_buffer_unmap (buffer1, &map);
+ if (!isvalid) {
+ GST_WARNING_OBJECT (vd, "Pending first header was invalid");
+ goto cleanup;
+ }
+
+ gst_buffer_map (buffer3, &map, GST_MAP_READ);
+ isvalid = map.size >= 1 && map.data[0] == 0x03;
+ gst_buffer_unmap (buffer3, &map);
+ if (!isvalid) {
+ GST_WARNING_OBJECT (vd, "Pending second header was invalid");
+ goto cleanup;
+ }
+
+ gst_buffer_map (buffer5, &map, GST_MAP_READ);
+ isvalid = map.size >= 1 && map.data[0] == 0x05;
+ gst_buffer_unmap (buffer5, &map);
+ if (!isvalid) {
+ GST_WARNING_OBJECT (vd, "Pending third header was invalid");
+ goto cleanup;
+ }
+
+ /* Discard any other pending headers */
+ if (tmp->next) {
+ GST_DEBUG_OBJECT (vd, "Discarding extra headers");
+ g_list_free_full (tmp->next, (GDestroyNotify) gst_buffer_unref);
+ tmp->next = NULL;
+ }
+ g_list_free (vd->pending_headers);
+ vd->pending_headers = NULL;
+
+ GST_DEBUG_OBJECT (vd, "Resetting and processing new headers");
+
+ /* All good, let's reset ourselves and process the headers */
+ vorbis_dec_reset ((GstAudioDecoder *) vd);
+ result = vorbis_dec_handle_header_buffer (vd, buffer1);
+ if (result != GST_FLOW_OK) {
+ gst_buffer_unref (buffer3);
+ gst_buffer_unref (buffer5);
+ return result;
+ }
+ result = vorbis_dec_handle_header_buffer (vd, buffer3);
+ if (result != GST_FLOW_OK) {
+ gst_buffer_unref (buffer5);
+ return result;
+ }
+ result = vorbis_dec_handle_header_buffer (vd, buffer5);
+
+ return result;
+
+ /* ERRORS */
+cleanup:
+ {
+ g_list_free_full (vd->pending_headers, (GDestroyNotify) gst_buffer_unref);
+ vd->pending_headers = NULL;
+ return result;
+ }
+not_enough:
+ {
+ GST_LOG_OBJECT (vd,
+ "Not enough pending headers to properly reset, ignoring them");
+ goto cleanup;
+ }
+}
+
+static GstFlowReturn
vorbis_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
{
ogg_packet *packet;
@@ -582,13 +672,15 @@ vorbis_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
/* switch depending on packet type */
if ((gst_ogg_packet_data (packet))[0] & 1) {
- /* If we get a new initialization packet, reset the decoder.
- * The vorbis_info struct should have a rate of 0 if it hasn't been
- * initialized yet. */
- if ((vd->initialized || (vd->vi.rate != 0)) &&
- (gst_ogg_packet_data (packet))[0] == 0x01) {
- GST_INFO_OBJECT (vd, "already initialized, re-init");
- vorbis_dec_reset (dec);
+ /* If we get a new initialization packet after being initialized,
+ * store it.
+ * When the next non-header buffer comes in, we will check whether
+ * those pending headers are correct and if so reset ourselves */
+ if (vd->initialized) {
+ GST_LOG_OBJECT (vd, "storing header for later analyzis");
+ vd->pending_headers =
+ g_list_append (vd->pending_headers, gst_buffer_ref (buffer));
+ goto done;
}
result = vorbis_handle_header_packet (vd, packet);
if (result != GST_FLOW_OK)
@@ -598,6 +690,11 @@ vorbis_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
} else {
GstClockTime timestamp, duration;
+ if (vd->pending_headers)
+ result = check_pending_headers (vd);
+ if (G_UNLIKELY (result != GST_FLOW_OK))
+ goto done;
+
timestamp = GST_BUFFER_TIMESTAMP (buffer);
duration = GST_BUFFER_DURATION (buffer);
diff --git a/ext/vorbis/gstvorbisdec.h b/ext/vorbis/gstvorbisdec.h
index 78d275fda..917d9046c 100644
--- a/ext/vorbis/gstvorbisdec.h
+++ b/ext/vorbis/gstvorbisdec.h
@@ -65,6 +65,8 @@ struct _GstVorbisDec {
GstAudioInfo info;
CopySampleFunc copy_samples;
+
+ GList *pending_headers;
};
struct _GstVorbisDecClass {