gstreamer/ext/vorbis/gstvorbisdec.c
Sebastian Dröge 0619168e2a vorbisdec: Always handle in-band header packets once the first non-header packet arrives
And clean up any old pending headers if we receive a new identification
header, or if we receive a new set of headers via caps.

Otherwise it might happen that we receive one or more header but not
all, and then afterwards all headers again, and libvorbis does not like
getting headers passed multiple times and would error out.

It only makes sense to pass the very latest headers to the decoder at
the time we can actually make use of them.

https://bugzilla.gnome.org/show_bug.cgi?id=796980
2018-08-28 17:47:09 +03:00

776 lines
21 KiB
C

/* GStreamer
* Copyright (C) 2004 Benjamin Otte <in7y118@public.uni-hamburg.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-vorbisdec
* @title: vorbisdec
* @see_also: vorbisenc, oggdemux
*
* This element decodes a Vorbis stream to raw float audio.
* <ulink url="http://www.vorbis.com/">Vorbis</ulink> is a royalty-free
* audio codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org
* Foundation</ulink>. As it outputs raw float audio you will often need to
* put an audioconvert element after it.
*
* ## Example pipelines
* |[
* gst-launch-1.0 -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink
* ]|
* Decode an Ogg/Vorbis. To create an Ogg/Vorbis file refer to the documentation of vorbisenc.
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstvorbisdec.h"
#include <string.h>
#include <gst/audio/audio.h>
#include <gst/tag/tag.h>
#include "gstvorbiscommon.h"
#ifndef TREMOR
GST_DEBUG_CATEGORY_EXTERN (vorbisdec_debug);
#define GST_CAT_DEFAULT vorbisdec_debug
#else
GST_DEBUG_CATEGORY_EXTERN (ivorbisdec_debug);
#define GST_CAT_DEFAULT ivorbisdec_debug
#endif
static GstStaticPadTemplate vorbis_dec_src_factory =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_VORBIS_DEC_SRC_CAPS);
static GstStaticPadTemplate vorbis_dec_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-vorbis")
);
#define gst_vorbis_dec_parent_class parent_class
G_DEFINE_TYPE (GstVorbisDec, gst_vorbis_dec, GST_TYPE_AUDIO_DECODER);
static void vorbis_dec_finalize (GObject * object);
static gboolean vorbis_dec_start (GstAudioDecoder * dec);
static gboolean vorbis_dec_stop (GstAudioDecoder * dec);
static GstFlowReturn vorbis_dec_handle_frame (GstAudioDecoder * dec,
GstBuffer * buffer);
static void vorbis_dec_flush (GstAudioDecoder * dec, gboolean hard);
static gboolean vorbis_dec_set_format (GstAudioDecoder * dec, GstCaps * caps);
static void vorbis_dec_reset (GstAudioDecoder * dec);
static void
gst_vorbis_dec_class_init (GstVorbisDecClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
gobject_class->finalize = vorbis_dec_finalize;
gst_element_class_add_static_pad_template (element_class,
&vorbis_dec_src_factory);
gst_element_class_add_static_pad_template (element_class,
&vorbis_dec_sink_factory);
gst_element_class_set_static_metadata (element_class,
"Vorbis audio decoder", "Codec/Decoder/Audio",
GST_VORBIS_DEC_DESCRIPTION,
"Benjamin Otte <otte@gnome.org>, Chris Lord <chris@openedhand.com>");
base_class->start = GST_DEBUG_FUNCPTR (vorbis_dec_start);
base_class->stop = GST_DEBUG_FUNCPTR (vorbis_dec_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (vorbis_dec_set_format);
base_class->handle_frame = GST_DEBUG_FUNCPTR (vorbis_dec_handle_frame);
base_class->flush = GST_DEBUG_FUNCPTR (vorbis_dec_flush);
}
static void
gst_vorbis_dec_init (GstVorbisDec * dec)
{
gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST
(dec), TRUE);
GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (dec));
}
static void
vorbis_dec_finalize (GObject * object)
{
/* Release any possibly allocated libvorbis data.
* _clear functions can safely be called multiple times
*/
GstVorbisDec *vd = GST_VORBIS_DEC (object);
#ifndef USE_TREMOLO
vorbis_block_clear (&vd->vb);
#endif
vorbis_dsp_clear (&vd->vd);
vorbis_comment_clear (&vd->vc);
vorbis_info_clear (&vd->vi);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
vorbis_dec_start (GstAudioDecoder * dec)
{
GstVorbisDec *vd = GST_VORBIS_DEC (dec);
GST_DEBUG_OBJECT (dec, "start");
vorbis_info_init (&vd->vi);
vorbis_comment_init (&vd->vc);
vd->initialized = FALSE;
return TRUE;
}
static gboolean
vorbis_dec_stop (GstAudioDecoder * dec)
{
GstVorbisDec *vd = GST_VORBIS_DEC (dec);
GST_DEBUG_OBJECT (dec, "stop");
vd->initialized = FALSE;
#ifndef USE_TREMOLO
vorbis_block_clear (&vd->vb);
#endif
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;
}
static GstFlowReturn
vorbis_handle_identification_packet (GstVorbisDec * vd)
{
GstAudioInfo info;
switch (vd->vi.channels) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
{
const GstAudioChannelPosition *pos;
pos = gst_vorbis_default_channel_positions[vd->vi.channels - 1];
gst_audio_info_set_format (&info, GST_VORBIS_AUDIO_FORMAT, vd->vi.rate,
vd->vi.channels, pos);
break;
}
default:{
GstAudioChannelPosition position[64];
gint i, max_pos = MAX (vd->vi.channels, 64);
GST_ELEMENT_WARNING (vd, STREAM, DECODE,
(NULL), ("Using NONE channel layout for more than 8 channels"));
for (i = 0; i < max_pos; i++)
position[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
gst_audio_info_set_format (&info, GST_VORBIS_AUDIO_FORMAT, vd->vi.rate,
vd->vi.channels, position);
break;
}
}
gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (vd), &info);
vd->info = info;
/* select a copy_samples function, this way we can have specialized versions
* for mono/stereo and avoid the depth switch in tremor case */
vd->copy_samples = gst_vorbis_get_copy_sample_func (info.channels);
return GST_FLOW_OK;
}
/* FIXME 0.11: remove tag handling and let container take care of that? */
static GstFlowReturn
vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet)
{
guint bitrate = 0;
gchar *encoder = NULL;
GstTagList *list;
guint8 *data;
gsize size;
GST_DEBUG_OBJECT (vd, "parsing comment packet");
data = gst_ogg_packet_data (packet);
size = gst_ogg_packet_size (packet);
list =
gst_tag_list_from_vorbiscomment (data, size, (guint8 *) "\003vorbis", 7,
&encoder);
if (!list) {
GST_ERROR_OBJECT (vd, "couldn't decode comments");
list = gst_tag_list_new_empty ();
}
if (encoder) {
if (encoder[0])
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_ENCODER, encoder, NULL);
g_free (encoder);
}
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_ENCODER_VERSION, vd->vi.version,
GST_TAG_AUDIO_CODEC, "Vorbis", NULL);
if (vd->vi.bitrate_nominal > 0 && vd->vi.bitrate_nominal <= 0x7FFFFFFF) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_NOMINAL_BITRATE, (guint) vd->vi.bitrate_nominal, NULL);
bitrate = vd->vi.bitrate_nominal;
}
if (vd->vi.bitrate_upper > 0 && vd->vi.bitrate_upper <= 0x7FFFFFFF) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_MAXIMUM_BITRATE, (guint) vd->vi.bitrate_upper, NULL);
if (!bitrate)
bitrate = vd->vi.bitrate_upper;
}
if (vd->vi.bitrate_lower > 0 && vd->vi.bitrate_lower <= 0x7FFFFFFF) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL);
if (!bitrate)
bitrate = vd->vi.bitrate_lower;
}
if (bitrate) {
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_BITRATE, (guint) bitrate, NULL);
}
gst_audio_decoder_merge_tags (GST_AUDIO_DECODER_CAST (vd), list,
GST_TAG_MERGE_REPLACE);
gst_tag_list_unref (list);
return GST_FLOW_OK;
}
static GstFlowReturn
vorbis_handle_type_packet (GstVorbisDec * vd)
{
gint res;
g_assert (!vd->initialized);
#ifdef USE_TREMOLO
if (G_UNLIKELY ((res = vorbis_dsp_init (&vd->vd, &vd->vi))))
goto synthesis_init_error;
#else
if (G_UNLIKELY ((res = vorbis_synthesis_init (&vd->vd, &vd->vi))))
goto synthesis_init_error;
if (G_UNLIKELY ((res = vorbis_block_init (&vd->vd, &vd->vb))))
goto block_init_error;
#endif
vd->initialized = TRUE;
return GST_FLOW_OK;
/* ERRORS */
synthesis_init_error:
{
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("couldn't initialize synthesis (%d)", res));
return GST_FLOW_ERROR;
}
block_init_error:
{
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("couldn't initialize block (%d)", res));
return GST_FLOW_ERROR;
}
}
static GstFlowReturn
vorbis_handle_header_packet (GstVorbisDec * vd, ogg_packet * packet)
{
GstFlowReturn res;
gint ret;
GST_DEBUG_OBJECT (vd, "parsing header packet");
/* Packetno = 0 if the first byte is exactly 0x01 */
packet->b_o_s = ((gst_ogg_packet_data (packet))[0] == 0x1) ? 1 : 0;
#ifdef USE_TREMOLO
if ((ret = vorbis_dsp_headerin (&vd->vi, &vd->vc, packet)))
#else
if ((ret = vorbis_synthesis_headerin (&vd->vi, &vd->vc, packet)))
#endif
goto header_read_error;
switch ((gst_ogg_packet_data (packet))[0]) {
case 0x01:
res = vorbis_handle_identification_packet (vd);
break;
case 0x03:
res = vorbis_handle_comment_packet (vd, packet);
break;
case 0x05:
res = vorbis_handle_type_packet (vd);
break;
default:
/* ignore */
g_warning ("unknown vorbis header packet found");
res = GST_FLOW_OK;
break;
}
return res;
/* ERRORS */
header_read_error:
{
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("couldn't read header packet (%d)", ret));
return GST_FLOW_ERROR;
}
}
static GstFlowReturn
vorbis_dec_handle_header_buffer (GstVorbisDec * vd, GstBuffer * buffer)
{
ogg_packet *packet;
ogg_packet_wrapper packet_wrapper;
GstFlowReturn ret;
GstMapInfo map;
gst_ogg_packet_wrapper_map (&packet_wrapper, buffer, &map);
packet = gst_ogg_packet_from_wrapper (&packet_wrapper);
ret = vorbis_handle_header_packet (vd, packet);
gst_ogg_packet_wrapper_unmap (&packet_wrapper, buffer, &map);
return ret;
}
#define MIN_NUM_HEADERS 3
static GstFlowReturn
vorbis_dec_handle_header_caps (GstVorbisDec * vd)
{
GstFlowReturn result = GST_FLOW_OK;
GstCaps *caps;
GstStructure *s = NULL;
const GValue *array = NULL;
caps = gst_pad_get_current_caps (GST_AUDIO_DECODER_SINK_PAD (vd));
if (caps)
s = gst_caps_get_structure (caps, 0);
if (s)
array = gst_structure_get_value (s, "streamheader");
if (caps)
gst_caps_unref (caps);
if (array && (gst_value_array_get_size (array) >= MIN_NUM_HEADERS)) {
const GValue *value = NULL;
GstBuffer *buf = NULL;
gint i = 0;
if (vd->pending_headers) {
GST_DEBUG_OBJECT (vd,
"got new headers from caps, discarding old pending headers");
g_list_free_full (vd->pending_headers, (GDestroyNotify) gst_buffer_unref);
vd->pending_headers = NULL;
}
while (result == GST_FLOW_OK && i < gst_value_array_get_size (array)) {
value = gst_value_array_get_value (array, i);
buf = gst_value_get_buffer (value);
if (!buf)
goto null_buffer;
result = vorbis_dec_handle_header_buffer (vd, buf);
i++;
}
} else
goto array_error;
done:
return (result != GST_FLOW_OK ? GST_FLOW_NOT_NEGOTIATED : GST_FLOW_OK);
/* ERRORS */
array_error:
{
GST_WARNING_OBJECT (vd, "streamheader array not found");
result = GST_FLOW_ERROR;
goto done;
}
null_buffer:
{
GST_WARNING_OBJECT (vd, "streamheader with null buffer received");
result = GST_FLOW_ERROR;
goto done;
}
}
static GstFlowReturn
vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet,
GstClockTime timestamp, GstClockTime duration)
{
#ifdef USE_TREMOLO
vorbis_sample_t *pcm;
#else
vorbis_sample_t **pcm;
#endif
guint sample_count;
GstBuffer *out = NULL;
GstFlowReturn result;
GstMapInfo map;
gsize size;
if (G_UNLIKELY (!vd->initialized)) {
result = vorbis_dec_handle_header_caps (vd);
if (result != GST_FLOW_OK)
goto not_initialized;
}
/* normal data packet */
/* FIXME, we can skip decoding if the packet is outside of the
* segment, this is however not very trivial as we need a previous
* packet to decode the current one so we must be careful not to
* throw away too much. For now we decode everything and clip right
* before pushing data. */
#ifdef USE_TREMOLO
if (G_UNLIKELY (vorbis_dsp_synthesis (&vd->vd, packet, 1)))
goto could_not_read;
#else
if (G_UNLIKELY (vorbis_synthesis (&vd->vb, packet)))
goto could_not_read;
if (G_UNLIKELY (vorbis_synthesis_blockin (&vd->vd, &vd->vb) < 0))
goto not_accepted;
#endif
/* assume all goes well here */
result = GST_FLOW_OK;
/* count samples ready for reading */
#ifdef USE_TREMOLO
if ((sample_count = vorbis_dsp_pcmout (&vd->vd, NULL, 0)) == 0)
#else
if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0)
goto done;
#endif
size = sample_count * vd->info.bpf;
GST_LOG_OBJECT (vd, "%d samples ready for reading, size %" G_GSIZE_FORMAT,
sample_count, size);
/* alloc buffer for it */
out = gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (vd), size);
gst_buffer_map (out, &map, GST_MAP_WRITE);
/* get samples ready for reading now, should be sample_count */
#ifdef USE_TREMOLO
if (G_UNLIKELY (vorbis_dsp_pcmout (&vd->vd, map.data, sample_count) !=
sample_count))
#else
if (G_UNLIKELY (vorbis_synthesis_pcmout (&vd->vd, &pcm) != sample_count))
#endif
goto wrong_samples;
#ifdef USE_TREMOLO
if (vd->info.channels < 9)
gst_audio_reorder_channels (map.data, map.size, GST_VORBIS_AUDIO_FORMAT,
vd->info.channels, gst_vorbis_channel_positions[vd->info.channels - 1],
gst_vorbis_default_channel_positions[vd->info.channels - 1]);
#else
/* copy samples in buffer */
vd->copy_samples ((vorbis_sample_t *) map.data, pcm,
sample_count, vd->info.channels);
#endif
GST_LOG_OBJECT (vd, "have output size of %" G_GSIZE_FORMAT, size);
gst_buffer_unmap (out, &map);
done:
/* whether or not data produced, consume one frame and advance time */
result = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (vd), out, 1);
#ifdef USE_TREMOLO
vorbis_dsp_read (&vd->vd, sample_count);
#else
vorbis_synthesis_read (&vd->vd, sample_count);
#endif
return result;
/* ERRORS */
not_initialized:
{
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("no header sent yet"));
return GST_FLOW_NOT_NEGOTIATED;
}
could_not_read:
{
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("couldn't read data packet"));
return GST_FLOW_ERROR;
}
not_accepted:
{
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("vorbis decoder did not accept data packet"));
return GST_FLOW_ERROR;
}
wrong_samples:
{
gst_buffer_unref (out);
GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
(NULL), ("vorbis decoder reported wrong number of samples"));
return GST_FLOW_ERROR;
}
}
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;
ogg_packet_wrapper packet_wrapper;
GstFlowReturn result = GST_FLOW_OK;
GstMapInfo map;
GstVorbisDec *vd = GST_VORBIS_DEC (dec);
/* no draining etc */
if (G_UNLIKELY (!buffer))
return GST_FLOW_OK;
GST_LOG_OBJECT (vd, "got buffer %p", buffer);
/* make ogg_packet out of the buffer */
gst_ogg_packet_wrapper_map (&packet_wrapper, buffer, &map);
packet = gst_ogg_packet_from_wrapper (&packet_wrapper);
/* set some more stuff */
packet->granulepos = -1;
packet->packetno = 0; /* we don't care */
/* EOS does not matter, it is used in vorbis to implement clipping the last
* block of samples based on the granulepos. We clip based on segments. */
packet->e_o_s = 0;
GST_LOG_OBJECT (vd, "decode buffer of size %ld", packet->bytes);
/* error out on empty header packets, but just skip empty data packets */
if (G_UNLIKELY (packet->bytes == 0)) {
if (vd->initialized)
goto empty_buffer;
else
goto empty_header;
}
/* switch depending on packet type */
if ((gst_ogg_packet_data (packet))[0] & 1) {
GST_LOG_OBJECT (vd, "storing header for later analyzis");
if (vd->pending_headers && (gst_ogg_packet_data (packet))[0] == 0x01) {
GST_DEBUG_OBJECT (vd,
"got new identification header packet, discarding old pending headers");
g_list_free_full (vd->pending_headers, (GDestroyNotify) gst_buffer_unref);
vd->pending_headers = NULL;
}
vd->pending_headers =
g_list_append (vd->pending_headers, gst_buffer_ref (buffer));
result = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (vd), NULL, 1);
} 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);
result = vorbis_handle_data_packet (vd, packet, timestamp, duration);
}
done:
GST_LOG_OBJECT (vd, "unmap buffer %p", buffer);
gst_ogg_packet_wrapper_unmap (&packet_wrapper, buffer, &map);
return result;
empty_buffer:
{
/* don't error out here, just ignore the buffer, it's invalid for vorbis
* but not fatal. */
GST_WARNING_OBJECT (vd, "empty buffer received, ignoring");
result = GST_FLOW_OK;
goto done;
}
/* ERRORS */
empty_header:
{
GST_ELEMENT_ERROR (vd, STREAM, DECODE, (NULL), ("empty header received"));
result = GST_FLOW_ERROR;
goto done;
}
}
static void
vorbis_dec_flush (GstAudioDecoder * dec, gboolean hard)
{
#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
GstVorbisDec *vd = GST_VORBIS_DEC (dec);
vorbis_synthesis_restart (&vd->vd);
#endif
}
static void
vorbis_dec_reset (GstAudioDecoder * dec)
{
GstVorbisDec *vd = GST_VORBIS_DEC (dec);
vd->initialized = FALSE;
#ifndef USE_TREMOLO
vorbis_block_clear (&vd->vb);
#endif
vorbis_dsp_clear (&vd->vd);
vorbis_comment_clear (&vd->vc);
vorbis_info_clear (&vd->vi);
vorbis_info_init (&vd->vi);
vorbis_comment_init (&vd->vc);
}
static gboolean
vorbis_dec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{
GstVorbisDec *vd = GST_VORBIS_DEC (dec);
GST_DEBUG_OBJECT (vd, "New caps %" GST_PTR_FORMAT " - resetting", caps);
/* A set_format call implies new data with new header packets */
if (!vd->initialized)
return TRUE;
/* We need to free and re-init libvorbis,
* or it chokes */
vorbis_dec_reset (dec);
return TRUE;
}