From f783ef73b7d56aa049717fc5fcaff8add912f550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 2 Aug 2008 17:29:44 +0000 Subject: Add CELT encoder and decoder elements based on the Speex elements. Original commit message from CVS: * configure.ac: * ext/Makefile.am: * ext/celt/Makefile.am: * ext/celt/gstcelt.c: (plugin_init): * ext/celt/gstceltdec.c: (gst_celt_dec_base_init), (gst_celt_dec_class_init), (gst_celt_dec_reset), (gst_celt_dec_init), (celt_dec_convert), (celt_get_sink_query_types), (celt_dec_sink_query), (celt_get_src_query_types), (celt_dec_src_query), (celt_dec_src_event), (celt_dec_sink_event), (celt_dec_chain_parse_header), (celt_dec_chain_parse_comments), (celt_dec_chain_parse_data), (celt_dec_chain), (celt_dec_change_state): * ext/celt/gstceltdec.h: * ext/celt/gstceltenc.c: (gst_celt_enc_setup_interfaces), (gst_celt_enc_base_init), (gst_celt_enc_class_init), (gst_celt_enc_finalize), (gst_celt_enc_sink_setcaps), (gst_celt_enc_sink_getcaps), (gst_celt_enc_convert_src), (gst_celt_enc_convert_sink), (gst_celt_enc_get_latency), (gst_celt_enc_get_query_types), (gst_celt_enc_src_query), (gst_celt_enc_sink_query), (gst_celt_enc_init), (gst_celt_enc_create_metadata_buffer), (gst_celt_enc_setup), (gst_celt_enc_buffer_from_data), (gst_celt_enc_push_buffer), (gst_celt_enc_set_header_on_caps), (gst_celt_enc_sinkevent), (gst_celt_enc_chain), (gst_celt_enc_get_property), (gst_celt_enc_set_property), (gst_celt_enc_change_state): * ext/celt/gstceltenc.h: Add CELT encoder and decoder elements based on the Speex elements. --- ext/celt/gstceltdec.c | 744 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 744 insertions(+) create mode 100644 ext/celt/gstceltdec.c (limited to 'ext/celt/gstceltdec.c') diff --git a/ext/celt/gstceltdec.c b/ext/celt/gstceltdec.c new file mode 100644 index 00000000..25f99b19 --- /dev/null +++ b/ext/celt/gstceltdec.c @@ -0,0 +1,744 @@ +/* GStreamer + * Copyright (C) 2004 Wim Taymans + * Copyright (C) 2006 Tim-Philipp Müller + * Copyright (C) 2008 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Based on the speexdec element. + */ + +/** + * SECTION:element-celtdec + * @short_description: a decoder that decodes CELT to raw audio + * @see_also: celtenc, oggdemux + * + * + * + * This element decodes a CELT stream to raw integer audio. + * + * Example pipelines + * + * + * gst-launch -v filesrc location=celt.ogg ! oggdemux ! celtdec ! audioconvert ! audioresample ! alsasink + * + * Decode an Ogg/Celt file. To create an Ogg/Celt file refer to the documentation of celtenc. + * + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstceltdec.h" +#include +#include + +GST_DEBUG_CATEGORY_STATIC (celtdec_debug); +#define GST_CAT_DEFAULT celtdec_debug + +static GstStaticPadTemplate celt_dec_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "rate = (int) [ 32000, 64000 ], " + "channels = (int) [ 1, 2 ], " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") + ); + +static GstStaticPadTemplate celt_dec_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-celt") + ); + +GST_BOILERPLATE (GstCeltDec, gst_celt_dec, GstElement, GST_TYPE_ELEMENT); + +static gboolean celt_dec_sink_event (GstPad * pad, GstEvent * event); +static GstFlowReturn celt_dec_chain (GstPad * pad, GstBuffer * buf); +static GstStateChangeReturn celt_dec_change_state (GstElement * element, + GstStateChange transition); + +static gboolean celt_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean celt_dec_src_query (GstPad * pad, GstQuery * query); +static gboolean celt_dec_sink_query (GstPad * pad, GstQuery * query); +static const GstQueryType *celt_get_src_query_types (GstPad * pad); +static const GstQueryType *celt_get_sink_query_types (GstPad * pad); +static gboolean celt_dec_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value); + +static GstFlowReturn celt_dec_chain_parse_data (GstCeltDec * dec, + GstBuffer * buf, GstClockTime timestamp, GstClockTime duration); + +static void +gst_celt_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&celt_dec_src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&celt_dec_sink_factory)); + gst_element_class_set_details_simple (element_class, "Celt audio decoder", + "Codec/Decoder/Audio", + "decode celt streams to audio", + "Sebastian Dröge "); +} + +static void +gst_celt_dec_class_init (GstCeltDecClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass *) klass; + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (celt_dec_change_state); + + GST_DEBUG_CATEGORY_INIT (celtdec_debug, "celtdec", 0, + "celt decoding element"); +} + +static void +gst_celt_dec_reset (GstCeltDec * dec) +{ + gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED); + dec->granulepos = -1; + dec->packetno = 0; + dec->frame_size = 0; + dec->frame_duration = 0; + if (dec->state) { + celt_decoder_destroy (dec->state); + dec->state = NULL; + } + + if (dec->mode) { + celt_mode_destroy (dec->mode); + dec->mode = NULL; + } + + memset (&dec->header, 0, sizeof (dec->header)); +} + +static void +gst_celt_dec_init (GstCeltDec * dec, GstCeltDecClass * g_class) +{ + dec->sinkpad = + gst_pad_new_from_static_template (&celt_dec_sink_factory, "sink"); + gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (celt_dec_chain)); + gst_pad_set_event_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (celt_dec_sink_event)); + gst_pad_set_query_type_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (celt_get_sink_query_types)); + gst_pad_set_query_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (celt_dec_sink_query)); + gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); + + dec->srcpad = gst_pad_new_from_static_template (&celt_dec_src_factory, "src"); + gst_pad_use_fixed_caps (dec->srcpad); + gst_pad_set_event_function (dec->srcpad, + GST_DEBUG_FUNCPTR (celt_dec_src_event)); + gst_pad_set_query_type_function (dec->srcpad, + GST_DEBUG_FUNCPTR (celt_get_src_query_types)); + gst_pad_set_query_function (dec->srcpad, + GST_DEBUG_FUNCPTR (celt_dec_src_query)); + gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); + + gst_celt_dec_reset (dec); +} + +static gboolean +celt_dec_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstCeltDec *dec; + guint64 scale = 1; + + dec = GST_CELT_DEC (gst_pad_get_parent (pad)); + + if (dec->packetno < 1) { + res = FALSE; + goto cleanup; + } + + if (src_format == *dest_format) { + *dest_value = src_value; + res = TRUE; + goto cleanup; + } + + if (pad == dec->sinkpad && + (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) { + res = FALSE; + goto cleanup; + } + + switch (src_format) { + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_BYTES: + scale = sizeof (gint16) * dec->header.nb_channels; + case GST_FORMAT_DEFAULT: + *dest_value = + gst_util_uint64_scale_int (scale * src_value, + dec->header.sample_rate, GST_SECOND); + break; + default: + res = FALSE; + break; + } + break; + case GST_FORMAT_DEFAULT: + switch (*dest_format) { + case GST_FORMAT_BYTES: + *dest_value = src_value * sizeof (gint16) * dec->header.nb_channels; + break; + case GST_FORMAT_TIME: + *dest_value = + gst_util_uint64_scale_int (src_value, GST_SECOND, + dec->header.sample_rate); + break; + default: + res = FALSE; + break; + } + break; + case GST_FORMAT_BYTES: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + *dest_value = src_value / (sizeof (gint16) * dec->header.nb_channels); + break; + case GST_FORMAT_TIME: + *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, + dec->header.sample_rate * sizeof (gint16) * + dec->header.nb_channels); + break; + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } + +cleanup: + gst_object_unref (dec); + return res; +} + +static const GstQueryType * +celt_get_sink_query_types (GstPad * pad) +{ + static const GstQueryType celt_dec_sink_query_types[] = { + GST_QUERY_CONVERT, + 0 + }; + + return celt_dec_sink_query_types; +} + +static gboolean +celt_dec_sink_query (GstPad * pad, GstQuery * query) +{ + GstCeltDec *dec; + gboolean res; + + dec = GST_CELT_DEC (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = celt_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val); + if (res) { + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (dec); + return res; +} + +static const GstQueryType * +celt_get_src_query_types (GstPad * pad) +{ + static const GstQueryType celt_dec_src_query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + 0 + }; + + return celt_dec_src_query_types; +} + +static gboolean +celt_dec_src_query (GstPad * pad, GstQuery * query) +{ + GstCeltDec *dec; + gboolean res = FALSE; + + dec = GST_CELT_DEC (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION:{ + GstSegment segment; + GstFormat format; + gint64 cur; + + gst_query_parse_position (query, &format, NULL); + + GST_PAD_STREAM_LOCK (dec->sinkpad); + segment = dec->segment; + GST_PAD_STREAM_UNLOCK (dec->sinkpad); + + if (segment.format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (dec, "segment not initialised yet"); + break; + } + + if ((res = celt_dec_convert (dec->srcpad, GST_FORMAT_TIME, + segment.last_stop, &format, &cur))) { + gst_query_set_position (query, format, cur); + } + break; + } + case GST_QUERY_DURATION:{ + GstFormat format = GST_FORMAT_TIME; + gint64 dur; + + /* get duration from demuxer */ + if (!gst_pad_query_peer_duration (dec->sinkpad, &format, &dur)) + break; + + gst_query_parse_duration (query, &format, NULL); + + /* and convert it into the requested format */ + if ((res = celt_dec_convert (dec->srcpad, GST_FORMAT_TIME, + dur, &format, &dur))) { + gst_query_set_duration (query, format, dur); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (dec); + return res; +} + +static gboolean +celt_dec_src_event (GstPad * pad, GstEvent * event) +{ + gboolean res = FALSE; + GstCeltDec *dec = GST_CELT_DEC (gst_pad_get_parent (pad)); + + GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK:{ + GstFormat format, tformat; + gdouble rate; + GstEvent *real_seek; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gint64 tcur, tstop; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + /* we have to ask our peer to seek to time here as we know + * nothing about how to generate a granulepos from the src + * formats or anything. + * + * First bring the requested format to time + */ + tformat = GST_FORMAT_TIME; + if (!(res = celt_dec_convert (pad, format, cur, &tformat, &tcur))) + break; + if (!(res = celt_dec_convert (pad, format, stop, &tformat, &tstop))) + break; + + /* then seek with time on the peer */ + real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, + flags, cur_type, tcur, stop_type, tstop); + + GST_LOG_OBJECT (dec, "seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (tcur)); + + res = gst_pad_push_event (dec->sinkpad, real_seek); + gst_event_unref (event); + break; + } + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (dec); + return res; +} + +static gboolean +celt_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstCeltDec *dec; + gboolean ret = FALSE; + + dec = GST_CELT_DEC (gst_pad_get_parent (pad)); + + GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT:{ + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (rate <= 0.0) + goto newseg_wrong_rate; + + if (update) { + /* time progressed without data, see if we can fill the gap with + * some concealment data */ + if (dec->segment.last_stop < start) { + GstClockTime duration; + + duration = start - dec->segment.last_stop; + celt_dec_chain_parse_data (dec, NULL, dec->segment.last_stop, + duration); + } + } + + /* now configure the values */ + gst_segment_set_newsegment_full (&dec->segment, update, + rate, arate, GST_FORMAT_TIME, start, stop, time); + + dec->granulepos = -1; + + GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%" + GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]", + GST_TIME_ARGS (dec->segment.last_stop), + GST_TIME_ARGS (dec->segment.start), + GST_TIME_ARGS (dec->segment.stop)); + + ret = gst_pad_push_event (dec->srcpad, event); + break; + } + default: + ret = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (dec); + return ret; + + /* ERRORS */ +newseg_wrong_format: + { + GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); + gst_object_unref (dec); + return FALSE; + } +newseg_wrong_rate: + { + GST_DEBUG_OBJECT (dec, "negative rates not supported yet"); + gst_object_unref (dec); + return FALSE; + } +} + +static GstFlowReturn +celt_dec_chain_parse_header (GstCeltDec * dec, GstBuffer * buf) +{ + GstCaps *caps; + gint error = CELT_OK; + + /* get the header */ + celt_header_from_packet ((const unsigned char *) GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf), &dec->header); + + if (memcmp (dec->header.codec_id, "CELT ", 8) != 0) + goto invalid_header; + + dec->mode = + celt_mode_create (dec->header.sample_rate, dec->header.nb_channels, + dec->header.frame_size, &error); + if (!dec->mode) + goto mode_init_failed; + + /* initialize the decoder */ + dec->state = celt_decoder_create (dec->mode); + if (!dec->state) + goto init_failed; + + celt_mode_info (dec->mode, CELT_GET_FRAME_SIZE, &dec->frame_size); + + dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, + GST_SECOND, dec->header.sample_rate); + + /* set caps */ + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->header.sample_rate, + "channels", G_TYPE_INT, dec->header.nb_channels, + "signed", G_TYPE_BOOLEAN, TRUE, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); + + if (!gst_pad_set_caps (dec->srcpad, caps)) + goto nego_failed; + + gst_caps_unref (caps); + return GST_FLOW_OK; + + /* ERRORS */ +invalid_header: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Invalid header")); + return GST_FLOW_ERROR; + } +mode_init_failed: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Mode initialization failed: %d", error)); + return GST_FLOW_ERROR; + } +init_failed: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("couldn't initialize decoder")); + return GST_FLOW_ERROR; + } +nego_failed: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("couldn't negotiate format")); + gst_caps_unref (caps); + return GST_FLOW_NOT_NEGOTIATED; + } +} + +static GstFlowReturn +celt_dec_chain_parse_comments (GstCeltDec * dec, GstBuffer * buf) +{ + GstTagList *list; + gchar *ver, *encoder = NULL; + + list = gst_tag_list_from_vorbiscomment_buffer (buf, NULL, 0, &encoder); + + if (!list) { + GST_WARNING_OBJECT (dec, "couldn't decode comments"); + list = gst_tag_list_new (); + } + + if (encoder) { + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_ENCODER, encoder, NULL); + } + + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_AUDIO_CODEC, "Celt", NULL); + + ver = g_strndup (dec->header.codec_version, 20); + g_strstrip (ver); + + if (ver != NULL && *ver != '\0') { + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_ENCODER_VERSION, ver, NULL); + } + + if (dec->header.bytes_per_packet > 0) { + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, (guint) dec->header.bytes_per_packet * 8, NULL); + } + + GST_INFO_OBJECT (dec, "tags: %" GST_PTR_FORMAT, list); + + gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, list); + + g_free (encoder); + g_free (ver); + + return GST_FLOW_OK; +} + +static GstFlowReturn +celt_dec_chain_parse_data (GstCeltDec * dec, GstBuffer * buf, + GstClockTime timestamp, GstClockTime duration) +{ + GstFlowReturn res = GST_FLOW_OK; + gint size; + guint8 *data; + GstBuffer *outbuf; + gint16 *out_data; + gint error = CELT_OK; + + if (timestamp != -1) { + dec->segment.last_stop = timestamp; + dec->granulepos = -1; + } + + if (buf) { + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + GST_DEBUG_OBJECT (dec, "received buffer of size %u", size); + + /* copy timestamp */ + } else { + /* concealment data, pass NULL as the bits parameters */ + GST_DEBUG_OBJECT (dec, "creating concealment data"); + data = NULL; + size = 0; + } + + res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, + GST_BUFFER_OFFSET_NONE, dec->frame_size * dec->header.nb_channels * 2, + GST_PAD_CAPS (dec->srcpad), &outbuf); + + if (res != GST_FLOW_OK) { + GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); + return res; + } + + out_data = (gint16 *) GST_BUFFER_DATA (outbuf); + + GST_LOG_OBJECT (dec, "decoding frame"); + + error = celt_decode (dec->state, data, size, out_data); + if (error != CELT_OK) { + GST_WARNING_OBJECT (dec, "Decoding error: %d", error); + return GST_FLOW_ERROR; + } + + if (dec->granulepos == -1) { + if (dec->segment.format != GST_FORMAT_TIME) { + GST_WARNING_OBJECT (dec, "segment not initialized or not TIME format"); + dec->granulepos = 0; + } else { + dec->granulepos = gst_util_uint64_scale_int (dec->segment.last_stop, + dec->header.sample_rate, GST_SECOND); + } + GST_DEBUG_OBJECT (dec, "granulepos=%" G_GINT64_FORMAT, dec->granulepos); + } + + GST_BUFFER_OFFSET (outbuf) = dec->granulepos; + GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos + dec->frame_size; + GST_BUFFER_TIMESTAMP (outbuf) = gst_util_uint64_scale_int (dec->granulepos, + GST_SECOND, dec->header.sample_rate); + GST_BUFFER_DURATION (outbuf) = dec->frame_duration; + + dec->granulepos += dec->frame_size; + dec->segment.last_stop += dec->frame_duration; + + GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_TIME_ARGS (dec->frame_duration)); + + res = gst_pad_push (dec->srcpad, outbuf); + + if (res != GST_FLOW_OK) + GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); + + return res; +} + +static GstFlowReturn +celt_dec_chain (GstPad * pad, GstBuffer * buf) +{ + GstFlowReturn res; + GstCeltDec *dec; + + dec = GST_CELT_DEC (gst_pad_get_parent (pad)); + + if (dec->packetno == 0) + res = celt_dec_chain_parse_header (dec, buf); + else if (dec->packetno == 1) + res = celt_dec_chain_parse_comments (dec, buf); + else if (dec->packetno <= 1 + dec->header.extra_headers) + res = GST_FLOW_OK; + else + res = + celt_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + + dec->packetno++; + + gst_buffer_unref (buf); + gst_object_unref (dec); + + return res; +} + +static GstStateChangeReturn +celt_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstCeltDec *dec = GST_CELT_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + default: + break; + } + + ret = parent_class->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_celt_dec_reset (dec); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} -- cgit v1.2.1