mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-30 12:49:40 +00:00
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
This commit is contained in:
parent
eacb7a77d2
commit
aab5cccc34
2 changed files with 106 additions and 7 deletions
|
@ -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;
|
||||
}
|
||||
|
@ -546,6 +550,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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ struct _GstVorbisDec {
|
|||
GstAudioInfo info;
|
||||
|
||||
CopySampleFunc copy_samples;
|
||||
|
||||
GList *pending_headers;
|
||||
};
|
||||
|
||||
struct _GstVorbisDecClass {
|
||||
|
|
Loading…
Reference in a new issue