diff options
author | Edward Hervey <edward@centricular.com> | 2018-02-13 08:36:30 +0100 |
---|---|---|
committer | Edward Hervey <bilboed@bilboed.com> | 2018-02-13 08:41:29 +0100 |
commit | aab5cccc340c5562429869612f2e788cfcd579f7 (patch) | |
tree | 44f52b374baff007d23795d791072184327d4a64 | |
parent | eacb7a77d2f00304a4af884d2cfc4966da861acf (diff) |
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.c | 111 | ||||
-rw-r--r-- | ext/vorbis/gstvorbisdec.h | 2 |
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 { |