plugin_LTLIBRARIES = libgstvorbis.la
-libgstvorbis_la_SOURCES = vorbis.c \
- vorbisdec.c \
- vorbisenc.c \
- vorbisparse.c \
- vorbistag.c \
+libgstvorbis_la_SOURCES = gstvorbis.c \
+ gstvorbisdec.c \
+ gstvorbisenc.c \
+ gstvorbisparse.c \
+ gstvorbistag.c \
gstvorbiscommon.c
libgstvorbis_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(VORBIS_CFLAGS)
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gst/tag/tag.h"
+
+#include "gstvorbisenc.h"
+#include "gstvorbisdec.h"
+#include "gstvorbisparse.h"
+#include "gstvorbistag.h"
+
+GST_DEBUG_CATEGORY (vorbisenc_debug);
+GST_DEBUG_CATEGORY (vorbisdec_debug);
+GST_DEBUG_CATEGORY (vorbisparse_debug);
+GST_DEBUG_CATEGORY (vorbistag_debug);
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "vorbisenc", GST_RANK_PRIMARY,
+ GST_TYPE_VORBISENC))
+ return FALSE;
+
+ if (!gst_element_register (plugin, "vorbisdec", GST_RANK_PRIMARY,
+ gst_vorbis_dec_get_type ()))
+ return FALSE;
+
+ if (!gst_element_register (plugin, "vorbisparse", GST_RANK_NONE,
+ gst_vorbis_parse_get_type ()))
+ return FALSE;
+
+ if (!gst_element_register (plugin, "vorbistag", GST_RANK_NONE,
+ gst_vorbis_tag_get_type ()))
+ return FALSE;
+
+ GST_DEBUG_CATEGORY_INIT (vorbisenc_debug, "vorbisenc", 0,
+ "vorbis encoding element");
+ GST_DEBUG_CATEGORY_INIT (vorbisdec_debug, "vorbisdec", 0,
+ "vorbis decoding element");
+ GST_DEBUG_CATEGORY_INIT (vorbisparse_debug, "vorbisparse", 0,
+ "vorbis parsing element");
+ GST_DEBUG_CATEGORY_INIT (vorbistag_debug, "vorbistag", 0,
+ "vorbis tagging element");
+
+ gst_tag_register_musicbrainz_tags ();
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "vorbis",
+ "Vorbis plugin library",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
--- /dev/null
+/* 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-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>.
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
+ * ]| Decode an Ogg/Vorbis. To create an Ogg/Vorbis file refer to the documentation of vorbisenc.
+ * </refsect2>
+ *
+ * Last reviewed on 2006-03-01 (0.10.4)
+ */
+
+#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 <gst/audio/multichannel.h>
+
+#include "gstvorbiscommon.h"
+
+GST_DEBUG_CATEGORY_EXTERN (vorbisdec_debug);
+#define GST_CAT_DEFAULT vorbisdec_debug
+
+static const GstElementDetails vorbis_dec_details =
+GST_ELEMENT_DETAILS ("Vorbis audio decoder",
+ "Codec/Decoder/Audio",
+ "decode raw vorbis streams to float audio",
+ "Benjamin Otte <in7y118@public.uni-hamburg.de>");
+
+static GstStaticPadTemplate vorbis_dec_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw-float, "
+ "rate = (int) [ 1, MAX ], "
+ "channels = (int) [ 1, 256 ], " "endianness = (int) BYTE_ORDER, "
+/* no ifdef in macros, please
+#ifdef GST_VORBIS_DEC_SEQUENTIAL
+ "layout = \"sequential\", "
+#endif
+*/
+ "width = (int) 32")
+ );
+
+static GstStaticPadTemplate vorbis_dec_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-vorbis")
+ );
+
+GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstElement, GST_TYPE_ELEMENT);
+
+static void vorbis_dec_finalize (GObject * object);
+static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer);
+static GstFlowReturn vorbis_dec_chain_forward (GstVorbisDec * vd,
+ gboolean discont, GstBuffer * buffer);
+static GstStateChangeReturn vorbis_dec_change_state (GstElement * element,
+ GstStateChange transition);
+
+static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event);
+static gboolean vorbis_dec_src_query (GstPad * pad, GstQuery * query);
+static gboolean vorbis_dec_convert (GstPad * pad,
+ GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value);
+
+static gboolean vorbis_dec_sink_query (GstPad * pad, GstQuery * query);
+
+static void
+gst_vorbis_dec_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GstPadTemplate *src_template, *sink_template;
+
+ src_template = gst_static_pad_template_get (&vorbis_dec_src_factory);
+ gst_element_class_add_pad_template (element_class, src_template);
+
+ sink_template = gst_static_pad_template_get (&vorbis_dec_sink_factory);
+ gst_element_class_add_pad_template (element_class, sink_template);
+
+ gst_element_class_set_details (element_class, &vorbis_dec_details);
+}
+
+static void
+gst_vorbis_dec_class_init (GstVorbisDecClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ gobject_class->finalize = vorbis_dec_finalize;
+
+ gstelement_class->change_state = GST_DEBUG_FUNCPTR (vorbis_dec_change_state);
+}
+
+static const GstQueryType *
+vorbis_get_query_types (GstPad * pad)
+{
+ static const GstQueryType vorbis_dec_src_query_types[] = {
+ GST_QUERY_POSITION,
+ GST_QUERY_DURATION,
+ GST_QUERY_CONVERT,
+ 0
+ };
+
+ return vorbis_dec_src_query_types;
+}
+
+static void
+gst_vorbis_dec_init (GstVorbisDec * dec, GstVorbisDecClass * g_class)
+{
+ dec->sinkpad = gst_pad_new_from_static_template (&vorbis_dec_sink_factory,
+ "sink");
+
+ gst_pad_set_event_function (dec->sinkpad,
+ GST_DEBUG_FUNCPTR (vorbis_dec_sink_event));
+ gst_pad_set_chain_function (dec->sinkpad,
+ GST_DEBUG_FUNCPTR (vorbis_dec_chain));
+ gst_pad_set_query_function (dec->sinkpad,
+ GST_DEBUG_FUNCPTR (vorbis_dec_sink_query));
+ gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
+
+ dec->srcpad = gst_pad_new_from_static_template (&vorbis_dec_src_factory,
+ "src");
+
+ gst_pad_set_event_function (dec->srcpad,
+ GST_DEBUG_FUNCPTR (vorbis_dec_src_event));
+ gst_pad_set_query_type_function (dec->srcpad,
+ GST_DEBUG_FUNCPTR (vorbis_get_query_types));
+ gst_pad_set_query_function (dec->srcpad,
+ GST_DEBUG_FUNCPTR (vorbis_dec_src_query));
+ gst_pad_use_fixed_caps (dec->srcpad);
+ gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
+
+ dec->queued = NULL;
+ dec->pendingevents = NULL;
+ dec->taglist = NULL;
+}
+
+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);
+
+ vorbis_block_clear (&vd->vb);
+ vorbis_dsp_clear (&vd->vd);
+ vorbis_comment_clear (&vd->vc);
+ vorbis_info_clear (&vd->vi);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_vorbis_dec_reset (GstVorbisDec * dec)
+{
+ dec->last_timestamp = GST_CLOCK_TIME_NONE;
+ dec->discont = TRUE;
+ dec->seqnum = gst_util_seqnum_next ();
+ gst_segment_init (&dec->segment, GST_FORMAT_TIME);
+
+ g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (dec->queued);
+ dec->queued = NULL;
+ g_list_foreach (dec->gather, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (dec->gather);
+ dec->gather = NULL;
+ g_list_foreach (dec->decode, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (dec->decode);
+ dec->decode = NULL;
+ g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (dec->pendingevents);
+ dec->pendingevents = NULL;
+
+ if (dec->taglist)
+ gst_tag_list_free (dec->taglist);
+ dec->taglist = NULL;
+}
+
+
+static gboolean
+vorbis_dec_convert (GstPad * pad,
+ GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ GstVorbisDec *dec;
+ guint64 scale = 1;
+
+ if (src_format == *dest_format) {
+ *dest_value = src_value;
+ return TRUE;
+ }
+
+ dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
+
+ if (!dec->initialized)
+ goto no_header;
+
+ if (dec->sinkpad == pad &&
+ (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
+ goto no_format;
+
+ switch (src_format) {
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ scale = sizeof (float) * dec->vi.channels;
+ case GST_FORMAT_DEFAULT:
+ *dest_value =
+ scale * gst_util_uint64_scale_int (src_value, dec->vi.rate,
+ GST_SECOND);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * sizeof (float) * dec->vi.channels;
+ break;
+ case GST_FORMAT_TIME:
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND, dec->vi.rate);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_DEFAULT:
+ *dest_value = src_value / (sizeof (float) * dec->vi.channels);
+ break;
+ case GST_FORMAT_TIME:
+ *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
+ dec->vi.rate * sizeof (float) * dec->vi.channels);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+done:
+ gst_object_unref (dec);
+
+ return res;
+
+ /* ERRORS */
+no_header:
+ {
+ GST_DEBUG_OBJECT (dec, "no header packets received");
+ res = FALSE;
+ goto done;
+ }
+no_format:
+ {
+ GST_DEBUG_OBJECT (dec, "formats unsupported");
+ res = FALSE;
+ goto done;
+ }
+}
+
+static gboolean
+vorbis_dec_src_query (GstPad * pad, GstQuery * query)
+{
+ GstVorbisDec *dec;
+ gboolean res = FALSE;
+
+ dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ gint64 value;
+ GstFormat format;
+ gint64 time;
+
+ gst_query_parse_position (query, &format, NULL);
+
+ /* we start from the last seen time */
+ time = dec->last_timestamp;
+ /* correct for the segment values */
+ time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
+
+ GST_LOG_OBJECT (dec,
+ "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));
+
+ /* and convert to the final format */
+ if (!(res =
+ vorbis_dec_convert (pad, GST_FORMAT_TIME, time, &format, &value)))
+ goto error;
+
+ gst_query_set_position (query, format, value);
+
+ GST_LOG_OBJECT (dec,
+ "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
+ format);
+
+ break;
+ }
+ case GST_QUERY_DURATION:
+ {
+ res = gst_pad_peer_query (dec->sinkpad, query);
+ if (!res)
+ goto error;
+
+ break;
+ }
+ 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);
+ if (!(res =
+ vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val)))
+ goto error;
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+done:
+ gst_object_unref (dec);
+
+ return res;
+
+ /* ERRORS */
+error:
+ {
+ GST_WARNING_OBJECT (dec, "error handling query");
+ goto done;
+ }
+}
+
+static gboolean
+vorbis_dec_sink_query (GstPad * pad, GstQuery * query)
+{
+ GstVorbisDec *dec;
+ gboolean res;
+
+ dec = GST_VORBIS_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);
+ if (!(res =
+ vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val)))
+ goto error;
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+
+done:
+ gst_object_unref (dec);
+
+ return res;
+
+ /* ERRORS */
+error:
+ {
+ GST_DEBUG_OBJECT (dec, "error converting value");
+ goto done;
+ }
+}
+
+static gboolean
+vorbis_dec_src_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = TRUE;
+ GstVorbisDec *dec;
+
+ dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
+
+ 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;
+ guint32 seqnum;
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
+ &stop_type, &stop);
+ seqnum = gst_event_get_seqnum (event);
+ gst_event_unref (event);
+
+ /* First bring the requested format to time */
+ tformat = GST_FORMAT_TIME;
+ if (!(res = vorbis_dec_convert (pad, format, cur, &tformat, &tcur)))
+ goto convert_error;
+ if (!(res = vorbis_dec_convert (pad, format, stop, &tformat, &tstop)))
+ goto convert_error;
+
+ /* 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_event_set_seqnum (real_seek, seqnum);
+
+ res = gst_pad_push_event (dec->sinkpad, real_seek);
+ break;
+ }
+ default:
+ res = gst_pad_push_event (dec->sinkpad, event);
+ break;
+ }
+done:
+ gst_object_unref (dec);
+
+ return res;
+
+ /* ERRORS */
+convert_error:
+ {
+ GST_DEBUG_OBJECT (dec, "cannot convert start/stop for seek");
+ goto done;
+ }
+}
+
+static gboolean
+vorbis_dec_sink_event (GstPad * pad, GstEvent * event)
+{
+ gboolean ret = FALSE;
+ GstVorbisDec *dec;
+
+ dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
+
+ GST_LOG_OBJECT (dec, "handling event");
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ ret = gst_pad_push_event (dec->srcpad, event);
+ break;
+ case GST_EVENT_FLUSH_START:
+ ret = gst_pad_push_event (dec->srcpad, event);
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ /* here we must clean any state in the decoder */
+#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
+ vorbis_synthesis_restart (&dec->vd);
+#endif
+ gst_vorbis_dec_reset (dec);
+ ret = gst_pad_push_event (dec->srcpad, event);
+ break;
+ 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);
+
+ /* we need time for now */
+ if (format != GST_FORMAT_TIME)
+ goto newseg_wrong_format;
+
+ GST_DEBUG_OBJECT (dec,
+ "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
+ ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
+ update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
+ GST_TIME_ARGS (time));
+
+ /* now configure the values */
+ gst_segment_set_newsegment_full (&dec->segment, update,
+ rate, arate, format, start, stop, time);
+ dec->seqnum = gst_event_get_seqnum (event);
+
+ if (dec->initialized)
+ /* and forward */
+ ret = gst_pad_push_event (dec->srcpad, event);
+ else {
+ /* store it to send once we're initialized */
+ dec->pendingevents = g_list_append (dec->pendingevents, event);
+ ret = TRUE;
+ }
+ break;
+ }
+ case GST_EVENT_TAG:
+ {
+ if (dec->initialized)
+ /* and forward */
+ ret = gst_pad_push_event (dec->srcpad, event);
+ else {
+ /* store it to send once we're initialized */
+ dec->pendingevents = g_list_append (dec->pendingevents, event);
+ ret = TRUE;
+ }
+ break;
+ }
+ default:
+ ret = gst_pad_push_event (dec->srcpad, event);
+ break;
+ }
+done:
+ gst_object_unref (dec);
+
+ return ret;
+
+ /* ERRORS */
+newseg_wrong_format:
+ {
+ GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
+ goto done;
+ }
+}
+
+static GstFlowReturn
+vorbis_handle_identification_packet (GstVorbisDec * vd)
+{
+ GstCaps *caps;
+ const GstAudioChannelPosition *pos = NULL;
+
+ switch (vd->vi.channels) {
+ case 1:
+ case 2:
+ /* nothing */
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ pos = gst_vorbis_channel_positions[vd->vi.channels - 1];
+ break;
+ default:{
+ gint i;
+ GstAudioChannelPosition *posn =
+ g_new (GstAudioChannelPosition, vd->vi.channels);
+
+ GST_ELEMENT_WARNING (GST_ELEMENT (vd), STREAM, DECODE,
+ (NULL), ("Using NONE channel layout for more than 8 channels"));
+
+ for (i = 0; i < vd->vi.channels; i++)
+ posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
+
+ pos = posn;
+ }
+ }
+
+ caps = gst_caps_new_simple ("audio/x-raw-float",
+ "rate", G_TYPE_INT, vd->vi.rate,
+ "channels", G_TYPE_INT, vd->vi.channels,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
+
+ if (pos) {
+ gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos);
+ }
+
+ if (vd->vi.channels > 8) {
+ g_free ((GstAudioChannelPosition *) pos);
+ }
+
+ gst_pad_set_caps (vd->srcpad, caps);
+ gst_caps_unref (caps);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet)
+{
+ guint bitrate = 0;
+ gchar *encoder = NULL;
+ GstTagList *list, *old_list;
+ GstBuffer *buf;
+
+ GST_DEBUG_OBJECT (vd, "parsing comment packet");
+
+ buf = gst_buffer_new ();
+ GST_BUFFER_DATA (buf) = packet->packet;
+ GST_BUFFER_SIZE (buf) = packet->bytes;
+
+ list =
+ gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\003vorbis", 7,
+ &encoder);
+
+ old_list = vd->taglist;
+ vd->taglist = gst_tag_list_merge (vd->taglist, list, GST_TAG_MERGE_REPLACE);
+
+ if (old_list)
+ gst_tag_list_free (old_list);
+ gst_tag_list_free (list);
+ gst_buffer_unref (buf);
+
+ if (!vd->taglist) {
+ GST_ERROR_OBJECT (vd, "couldn't decode comments");
+ vd->taglist = gst_tag_list_new ();
+ }
+ if (encoder) {
+ if (encoder[0])
+ gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE,
+ GST_TAG_ENCODER, encoder, NULL);
+ g_free (encoder);
+ }
+ gst_tag_list_add (vd->taglist, 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 (vd->taglist, 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 (vd->taglist, 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 (vd->taglist, 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 (vd->taglist, GST_TAG_MERGE_REPLACE,
+ GST_TAG_BITRATE, (guint) bitrate, NULL);
+ }
+
+ if (vd->initialized) {
+ gst_element_found_tags_for_pad (GST_ELEMENT_CAST (vd), vd->srcpad,
+ vd->taglist);
+ vd->taglist = NULL;
+ } else {
+ /* Only post them as messages for the time being. *
+ * They will be pushed on the pad once the decoder is initialized */
+ gst_element_post_message (GST_ELEMENT_CAST (vd),
+ gst_message_new_tag (GST_OBJECT (vd), gst_tag_list_copy (vd->taglist)));
+ }
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+vorbis_handle_type_packet (GstVorbisDec * vd)
+{
+ GList *walk;
+ gint res;
+
+ g_assert (vd->initialized == FALSE);
+
+ 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;
+
+ vd->initialized = TRUE;
+
+ if (vd->pendingevents) {
+ for (walk = vd->pendingevents; walk; walk = g_list_next (walk))
+ gst_pad_push_event (vd->srcpad, GST_EVENT_CAST (walk->data));
+ g_list_free (vd->pendingevents);
+ vd->pendingevents = NULL;
+ }
+
+ if (vd->taglist) {
+ /* The tags have already been sent on the bus as messages. */
+ gst_pad_push_event (vd->srcpad, gst_event_new_tag (vd->taglist));
+ vd->taglist = NULL;
+ }
+ 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 = (packet->packet[0] == 0x1) ? 1 : 0;
+
+ if ((ret = vorbis_synthesis_headerin (&vd->vi, &vd->vc, packet)))
+ goto header_read_error;
+
+ switch (packet->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;
+ }
+}
+
+/* These samples can be outside of the float -1.0 -- 1.0 range, this
+ * is allowed, downstream elements are supposed to clip */
+static void
+copy_samples (float *out, float **in, guint samples, gint channels)
+{
+ gint i, j;
+
+#ifdef GST_VORBIS_DEC_SEQUENTIAL
+ for (i = 0; i < channels; i++) {
+ memcpy (out, in[i], samples * sizeof (float));
+ out += samples;
+ }
+#else
+ for (j = 0; j < samples; j++) {
+ for (i = 0; i < channels; i++) {
+ *out++ = in[i][j];
+ }
+ }
+#endif
+}
+
+static GstFlowReturn
+vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf)
+{
+ GstFlowReturn result;
+
+ /* clip */
+ if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate,
+ dec->vi.channels * sizeof (float)))) {
+ GST_LOG_OBJECT (dec, "clipped buffer");
+ return GST_FLOW_OK;
+ }
+
+ if (dec->discont) {
+ GST_LOG_OBJECT (dec, "setting DISCONT");
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+ dec->discont = FALSE;
+ }
+
+ GST_DEBUG_OBJECT (dec,
+ "pushing time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
+
+ result = gst_pad_push (dec->srcpad, buf);
+
+ return result;
+}
+
+static GstFlowReturn
+vorbis_dec_push_reverse (GstVorbisDec * dec, GstBuffer * buf)
+{
+ GstFlowReturn result = GST_FLOW_OK;
+
+ dec->queued = g_list_prepend (dec->queued, buf);
+
+ return result;
+}
+
+static void
+vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse,
+ GstClockTime timestamp, GstClockTime duration)
+{
+ /* interpolate reverse */
+ if (vd->last_timestamp != -1 && reverse)
+ vd->last_timestamp -= duration;
+
+ /* take buffer timestamp, use interpolated timestamp otherwise */
+ if (timestamp != -1)
+ vd->last_timestamp = timestamp;
+ else
+ timestamp = vd->last_timestamp;
+
+ /* interpolate forwards */
+ if (vd->last_timestamp != -1 && !reverse)
+ vd->last_timestamp += duration;
+
+ GST_BUFFER_TIMESTAMP (buf) = timestamp;
+ GST_BUFFER_DURATION (buf) = duration;
+}
+
+static GstFlowReturn
+vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet,
+ GstClockTime timestamp, GstClockTime duration)
+{
+ float **pcm;
+ guint sample_count;
+ GstBuffer *out;
+ GstFlowReturn result;
+ gint size;
+
+ if (G_UNLIKELY (!vd->initialized))
+ 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 carefull not to
+ * throw away too much. For now we decode everything and clip right
+ * before pushing data. */
+ 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;
+
+ /* assume all goes well here */
+ result = GST_FLOW_OK;
+
+ /* count samples ready for reading */
+ if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0)
+ goto done;
+
+ size = sample_count * vd->vi.channels * sizeof (float);
+ GST_LOG_OBJECT (vd, "%d samples ready for reading, size %d", sample_count,
+ size);
+
+ /* alloc buffer for it */
+ result =
+ gst_pad_alloc_buffer_and_set_caps (vd->srcpad, GST_BUFFER_OFFSET_NONE,
+ size, GST_PAD_CAPS (vd->srcpad), &out);
+ if (G_UNLIKELY (result != GST_FLOW_OK))
+ goto done;
+
+ /* get samples ready for reading now, should be sample_count */
+ if (G_UNLIKELY ((vorbis_synthesis_pcmout (&vd->vd, &pcm)) != sample_count))
+ goto wrong_samples;
+
+ /* copy samples in buffer */
+ copy_samples ((float *) GST_BUFFER_DATA (out), pcm, sample_count,
+ vd->vi.channels);
+
+ GST_LOG_OBJECT (vd, "setting output size to %d", size);
+ GST_BUFFER_SIZE (out) = size;
+
+ /* this should not overflow */
+ if (duration == -1)
+ duration = sample_count * GST_SECOND / vd->vi.rate;
+
+ vorbis_do_timestamps (vd, out, FALSE, timestamp, duration);
+
+ if (vd->segment.rate >= 0.0)
+ result = vorbis_dec_push_forward (vd, out);
+ else
+ result = vorbis_dec_push_reverse (vd, out);
+
+done:
+ vorbis_synthesis_read (&vd->vd, sample_count);
+
+ return result;
+
+ /* ERRORS */
+not_initialized:
+ {
+ GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
+ (NULL), ("no header sent yet"));
+ return GST_FLOW_ERROR;
+ }
+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
+vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer)
+{
+ ogg_packet packet;
+ GstFlowReturn result = GST_FLOW_OK;
+
+ /* make ogg_packet out of the buffer */
+ packet.packet = GST_BUFFER_DATA (buffer);
+ packet.bytes = GST_BUFFER_SIZE (buffer);
+ 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 (packet.packet[0] & 1) {
+ if (vd->initialized) {
+ GST_WARNING_OBJECT (vd, "Already initialized, so ignoring header packet");
+ goto done;
+ }
+ result = vorbis_handle_header_packet (vd, &packet);
+ } else {
+ GstClockTime timestamp, duration;
+
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ duration = GST_BUFFER_DURATION (buffer);
+
+ result = vorbis_handle_data_packet (vd, &packet, timestamp, duration);
+ }
+
+done:
+ 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;
+ vd->discont = TRUE;
+ goto done;
+ }
+}
+
+/*
+ * Input:
+ * Buffer decoding order: 7 8 9 4 5 6 3 1 2 EOS
+ * Discont flag: D D D D
+ *
+ * - Each Discont marks a discont in the decoding order.
+ *
+ * for vorbis, each buffer is a keyframe when we have the previous
+ * buffer. This means that to decode buffer 7, we need buffer 6, which
+ * arrives out of order.
+ *
+ * we first gather buffers in the gather queue until we get a DISCONT. We
+ * prepend each incomming buffer so that they are in reversed order.
+ *
+ * gather queue: 9 8 7
+ * decode queue:
+ * output queue:
+ *
+ * When a DISCONT is received (buffer 4), we move the gather queue to the
+ * decode queue. This is simply done be taking the head of the gather queue
+ * and prepending it to the decode queue. This yields:
+ *
+ * gather queue:
+ * decode queue: 7 8 9
+ * output queue:
+ *
+ * Then we decode each buffer in the decode queue in order and put the output
+ * buffer in the output queue. The first buffer (7) will not produce any output
+ * because it needs the previous buffer (6) which did not arrive yet. This
+ * yields:
+ *
+ * gather queue:
+ * decode queue: 7 8 9
+ * output queue: 9 8
+ *
+ * Then we remove the consumed buffers from the decode queue. Buffer 7 is not
+ * completely consumed, we need to keep it around for when we receive buffer
+ * 6. This yields:
+ *
+ * gather queue:
+ * decode queue: 7
+ * output queue: 9 8
+ *
+ * Then we accumulate more buffers:
+ *
+ * gather queue: 6 5 4
+ * decode queue: 7
+ * output queue:
+ *
+ * prepending to the decode queue on DISCONT yields:
+ *
+ * gather queue:
+ * decode queue: 4 5 6 7
+ * output queue:
+ *
+ * after decoding and keeping buffer 4:
+ *
+ * gather queue:
+ * decode queue: 4
+ * output queue: 7 6 5
+ *
+ * Etc..
+ */
+static GstFlowReturn
+vorbis_dec_flush_decode (GstVorbisDec * dec)
+{
+ GstFlowReturn res = GST_FLOW_OK;
+ GList *walk;
+
+ walk = dec->decode;
+
+ GST_DEBUG_OBJECT (dec, "flushing buffers to decoder");
+
+ while (walk) {
+ GList *next;
+ GstBuffer *buf = GST_BUFFER_CAST (walk->data);
+
+ GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT,
+ buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+ next = g_list_next (walk);
+
+ /* decode buffer, prepend to output queue */
+ res = vorbis_dec_decode_buffer (dec, buf);
+
+ /* if we generated output, we can discard the buffer, else we
+ * keep it in the queue */
+ if (dec->queued) {
+ GST_DEBUG_OBJECT (dec, "decoded buffer to %p", dec->queued->data);
+ dec->decode = g_list_delete_link (dec->decode, walk);
+ gst_buffer_unref (buf);
+ } else {
+ GST_DEBUG_OBJECT (dec, "buffer did not decode, keeping");
+ }
+ walk = next;
+ }
+ while (dec->queued) {
+ GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data);
+ GstClockTime timestamp, duration;
+
+ timestamp = GST_BUFFER_TIMESTAMP (buf);
+ duration = GST_BUFFER_DURATION (buf);
+
+ vorbis_do_timestamps (dec, buf, TRUE, timestamp, duration);
+ res = vorbis_dec_push_forward (dec, buf);
+
+ dec->queued = g_list_delete_link (dec->queued, dec->queued);
+ }
+ return res;
+}
+
+static GstFlowReturn
+vorbis_dec_chain_reverse (GstVorbisDec * vd, gboolean discont, GstBuffer * buf)
+{
+ GstFlowReturn result = GST_FLOW_OK;
+
+ /* if we have a discont, move buffers to the decode list */
+ if (G_UNLIKELY (discont)) {
+ GST_DEBUG_OBJECT (vd, "received discont");
+ while (vd->gather) {
+ GstBuffer *gbuf;
+
+ gbuf = GST_BUFFER_CAST (vd->gather->data);
+ /* remove from the gather list */
+ vd->gather = g_list_delete_link (vd->gather, vd->gather);
+ /* copy to decode queue */
+ vd->decode = g_list_prepend (vd->decode, gbuf);
+ }
+ /* flush and decode the decode queue */
+ result = vorbis_dec_flush_decode (vd);
+ }
+
+ GST_DEBUG_OBJECT (vd, "gathering buffer %p of size %u, time %" GST_TIME_FORMAT
+ ", dur %" GST_TIME_FORMAT, buf, GST_BUFFER_SIZE (buf),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
+
+ /* add buffer to gather queue */
+ vd->gather = g_list_prepend (vd->gather, buf);
+
+ return result;
+}
+
+static GstFlowReturn
+vorbis_dec_chain_forward (GstVorbisDec * vd, gboolean discont,
+ GstBuffer * buffer)
+{
+ GstFlowReturn result;
+
+ result = vorbis_dec_decode_buffer (vd, buffer);
+
+ gst_buffer_unref (buffer);
+
+ return result;
+}
+
+static GstFlowReturn
+vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstVorbisDec *vd;
+ GstFlowReturn result = GST_FLOW_OK;
+ gboolean discont;
+
+ vd = GST_VORBIS_DEC (gst_pad_get_parent (pad));
+
+ discont = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+
+ /* resync on DISCONT */
+ if (G_UNLIKELY (discont)) {
+ GST_DEBUG_OBJECT (vd, "received DISCONT buffer");
+ vd->last_timestamp = GST_CLOCK_TIME_NONE;
+#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
+ vorbis_synthesis_restart (&vd->vd);
+#endif
+ vd->discont = TRUE;
+ }
+
+ if (vd->segment.rate >= 0.0)
+ result = vorbis_dec_chain_forward (vd, discont, buffer);
+ else
+ result = vorbis_dec_chain_reverse (vd, discont, buffer);
+
+ gst_object_unref (vd);
+
+ return result;
+}
+
+static GstStateChangeReturn
+vorbis_dec_change_state (GstElement * element, GstStateChange transition)
+{
+ GstVorbisDec *vd = GST_VORBIS_DEC (element);
+ GstStateChangeReturn res;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ vorbis_info_init (&vd->vi);
+ vorbis_comment_init (&vd->vc);
+ vd->initialized = FALSE;
+ gst_vorbis_dec_reset (vd);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ res = parent_class->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_DEBUG_OBJECT (vd, "PAUSED -> READY, clearing vorbis structures");
+ vd->initialized = FALSE;
+ vorbis_block_clear (&vd->vb);
+ vorbis_dsp_clear (&vd->vd);
+ vorbis_comment_clear (&vd->vc);
+ vorbis_info_clear (&vd->vi);
+ gst_vorbis_dec_reset (vd);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ return res;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-vorbisenc
+ * @see_also: vorbisdec, oggmux
+ *
+ * This element encodes raw float audio into a Vorbis stream.
+ * <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>.
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! vorbisenc ! oggmux ! filesink location=sine.ogg
+ * ]| Encode a test sine signal to Ogg/Vorbis. Note that the resulting file
+ * will be really small because a sine signal compresses very well.
+ * |[
+ * gst-launch -v alsasrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg
+ * ]| Record from a sound card using ALSA and encode to Ogg/Vorbis.
+ * </refsect2>
+ *
+ * Last reviewed on 2006-03-01 (0.10.4)
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <vorbis/vorbisenc.h>
+
+#include <gst/gsttagsetter.h>
+#include <gst/tag/tag.h>
+#include <gst/audio/multichannel.h>
+#include <gst/audio/audio.h>
+#include "gstvorbisenc.h"
+
+#include "gstvorbiscommon.h"
+
+GST_DEBUG_CATEGORY_EXTERN (vorbisenc_debug);
+#define GST_CAT_DEFAULT vorbisenc_debug
+
+static GstStaticPadTemplate vorbis_enc_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw-float, "
+ "rate = (int) [ 1, 200000 ], "
+ "channels = (int) [ 1, 256 ], " "endianness = (int) BYTE_ORDER, "
+ "width = (int) 32")
+ );
+
+static GstStaticPadTemplate vorbis_enc_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-vorbis")
+ );
+
+
+/* elementfactory information */
+static const GstElementDetails vorbisenc_details =
+GST_ELEMENT_DETAILS ("Vorbis audio encoder",
+ "Codec/Encoder/Audio",
+ "Encodes audio in Vorbis format",
+ "Monty <monty@xiph.org>, " "Wim Taymans <wim@fluendo.com>");
+
+enum
+{
+ ARG_0,
+ ARG_MAX_BITRATE,
+ ARG_BITRATE,
+ ARG_MIN_BITRATE,
+ ARG_QUALITY,
+ ARG_MANAGED,
+ ARG_LAST_MESSAGE
+};
+
+static GstFlowReturn gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc);
+
+/* this function takes into account the granulepos_offset and the subgranule
+ * time offset */
+static GstClockTime
+granulepos_to_timestamp_offset (GstVorbisEnc * vorbisenc,
+ ogg_int64_t granulepos)
+{
+ if (granulepos >= 0)
+ return gst_util_uint64_scale ((guint64) granulepos
+ + vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency)
+ + vorbisenc->subgranule_offset;
+ return GST_CLOCK_TIME_NONE;
+}
+
+/* this function does a straight granulepos -> timestamp conversion */
+static GstClockTime
+granulepos_to_timestamp (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
+{
+ if (granulepos >= 0)
+ return gst_util_uint64_scale ((guint64) granulepos,
+ GST_SECOND, vorbisenc->frequency);
+ return GST_CLOCK_TIME_NONE;
+}
+
+#define MAX_BITRATE_DEFAULT -1
+#define BITRATE_DEFAULT -1
+#define MIN_BITRATE_DEFAULT -1
+#define QUALITY_DEFAULT 0.3
+#define LOWEST_BITRATE 6000 /* lowest allowed for a 8 kHz stream */
+#define HIGHEST_BITRATE 250001 /* highest allowed for a 44 kHz stream */
+
+static gboolean gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer);
+static gboolean gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc);
+
+static void gst_vorbis_enc_dispose (GObject * object);
+static void gst_vorbis_enc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_vorbis_enc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static GstStateChangeReturn gst_vorbis_enc_change_state (GstElement * element,
+ GstStateChange transition);
+static void gst_vorbis_enc_add_interfaces (GType vorbisenc_type);
+
+GST_BOILERPLATE_FULL (GstVorbisEnc, gst_vorbis_enc, GstElement,
+ GST_TYPE_ELEMENT, gst_vorbis_enc_add_interfaces);
+
+static void
+gst_vorbis_enc_add_interfaces (GType vorbisenc_type)
+{
+ static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
+ static const GInterfaceInfo preset_info = { NULL, NULL, NULL };
+
+ g_type_add_interface_static (vorbisenc_type, GST_TYPE_TAG_SETTER,
+ &tag_setter_info);
+ g_type_add_interface_static (vorbisenc_type, GST_TYPE_PRESET, &preset_info);
+}
+
+static void
+gst_vorbis_enc_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GstPadTemplate *src_template, *sink_template;
+
+
+ src_template = gst_static_pad_template_get (&vorbis_enc_src_factory);
+ gst_element_class_add_pad_template (element_class, src_template);
+
+ sink_template = gst_static_pad_template_get (&vorbis_enc_sink_factory);
+ gst_element_class_add_pad_template (element_class, sink_template);
+ gst_element_class_set_details (element_class, &vorbisenc_details);
+}
+
+static void
+gst_vorbis_enc_class_init (GstVorbisEncClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = gst_vorbis_enc_set_property;
+ gobject_class->get_property = gst_vorbis_enc_get_property;
+ gobject_class->dispose = gst_vorbis_enc_dispose;
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BITRATE,
+ g_param_spec_int ("max-bitrate", "Maximum Bitrate",
+ "Specify a maximum bitrate (in bps). Useful for streaming "
+ "applications. (-1 == disabled)",
+ -1, HIGHEST_BITRATE, MAX_BITRATE_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
+ g_param_spec_int ("bitrate", "Target Bitrate",
+ "Attempt to encode at a bitrate averaging this (in bps). "
+ "This uses the bitrate management engine, and is not recommended for most users. "
+ "Quality is a better alternative. (-1 == disabled)", -1,
+ HIGHEST_BITRATE, BITRATE_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MIN_BITRATE,
+ g_param_spec_int ("min-bitrate", "Minimum Bitrate",
+ "Specify a minimum bitrate (in bps). Useful for encoding for a "
+ "fixed-size channel. (-1 == disabled)", -1, HIGHEST_BITRATE,
+ MIN_BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
+ g_param_spec_float ("quality", "Quality",
+ "Specify quality instead of specifying a particular bitrate.", -0.1,
+ 1.0, QUALITY_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MANAGED,
+ g_param_spec_boolean ("managed", "Managed",
+ "Enable bitrate management engine", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
+ g_param_spec_string ("last-message", "last-message",
+ "The last status message", NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_change_state);
+}
+
+static void
+gst_vorbis_enc_dispose (GObject * object)
+{
+ GstVorbisEnc *vorbisenc = GST_VORBISENC (object);
+
+ if (vorbisenc->sinkcaps) {
+ gst_caps_unref (vorbisenc->sinkcaps);
+ vorbisenc->sinkcaps = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static GstCaps *
+gst_vorbis_enc_generate_sink_caps (void)
+{
+ GstCaps *caps = gst_caps_new_empty ();
+ int i, c;
+
+ gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float",
+ "rate", GST_TYPE_INT_RANGE, 1, 200000,
+ "channels", G_TYPE_INT, 1,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32,
+ NULL));
+
+ gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float",
+ "rate", GST_TYPE_INT_RANGE, 1, 200000,
+ "channels", G_TYPE_INT, 2,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32,
+ NULL));
+
+ for (i = 3; i <= 8; i++) {
+ GValue chanpos = { 0 };
+ GValue pos = { 0 };
+ GstStructure *structure;
+
+ g_value_init (&chanpos, GST_TYPE_ARRAY);
+ g_value_init (&pos, GST_TYPE_AUDIO_CHANNEL_POSITION);
+
+ for (c = 0; c < i; c++) {
+ g_value_set_enum (&pos, gst_vorbis_channel_positions[i - 1][c]);
+ gst_value_array_append_value (&chanpos, &pos);
+ }
+ g_value_unset (&pos);
+
+ structure = gst_structure_new ("audio/x-raw-float",
+ "rate", GST_TYPE_INT_RANGE, 1, 200000,
+ "channels", G_TYPE_INT, i,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
+ gst_structure_set_value (structure, "channel-positions", &chanpos);
+ g_value_unset (&chanpos);
+
+ gst_caps_append_structure (caps, structure);
+ }
+
+ gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float",
+ "rate", GST_TYPE_INT_RANGE, 1, 200000,
+ "channels", GST_TYPE_INT_RANGE, 9, 256,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32,
+ NULL));
+
+ return caps;
+}
+
+static GstCaps *
+gst_vorbis_enc_sink_getcaps (GstPad * pad)
+{
+ GstVorbisEnc *vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
+
+ if (vorbisenc->sinkcaps == NULL)
+ vorbisenc->sinkcaps = gst_vorbis_enc_generate_sink_caps ();
+
+ return gst_caps_ref (vorbisenc->sinkcaps);
+}
+
+static gboolean
+gst_vorbis_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+ GstVorbisEnc *vorbisenc;
+ GstStructure *structure;
+
+ vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
+ vorbisenc->setup = FALSE;
+
+ structure = gst_caps_get_structure (caps, 0);
+ gst_structure_get_int (structure, "channels", &vorbisenc->channels);
+ gst_structure_get_int (structure, "rate", &vorbisenc->frequency);
+
+ gst_vorbis_enc_setup (vorbisenc);
+
+ if (vorbisenc->setup)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+gst_vorbis_enc_convert_src (GstPad * pad, GstFormat src_format,
+ gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ GstVorbisEnc *vorbisenc;
+ gint64 avg;
+
+ vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
+
+ if (vorbisenc->samples_in == 0 ||
+ vorbisenc->bytes_out == 0 || vorbisenc->frequency == 0) {
+ gst_object_unref (vorbisenc);
+ return FALSE;
+ }
+
+ avg = (vorbisenc->bytes_out * vorbisenc->frequency) / (vorbisenc->samples_in);
+
+ switch (src_format) {
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_TIME:
+ *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, avg);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = gst_util_uint64_scale_int (src_value, avg, GST_SECOND);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+ gst_object_unref (vorbisenc);
+ return res;
+}
+
+static gboolean
+gst_vorbis_enc_convert_sink (GstPad * pad, GstFormat src_format,
+ gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ guint scale = 1;
+ gint bytes_per_sample;
+ GstVorbisEnc *vorbisenc;
+
+ vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
+
+ bytes_per_sample = vorbisenc->channels * 2;
+
+ switch (src_format) {
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_DEFAULT:
+ if (bytes_per_sample == 0)
+ return FALSE;
+ *dest_value = src_value / bytes_per_sample;
+ break;
+ case GST_FORMAT_TIME:
+ {
+ gint byterate = bytes_per_sample * vorbisenc->frequency;
+
+ if (byterate == 0)
+ return FALSE;
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
+ break;
+ }
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * bytes_per_sample;
+ break;
+ case GST_FORMAT_TIME:
+ if (vorbisenc->frequency == 0)
+ return FALSE;
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND,
+ vorbisenc->frequency);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ scale = bytes_per_sample;
+ /* fallthrough */
+ case GST_FORMAT_DEFAULT:
+ *dest_value =
+ gst_util_uint64_scale_int (src_value,
+ scale * vorbisenc->frequency, GST_SECOND);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+ gst_object_unref (vorbisenc);
+ return res;
+}
+
+static gint64
+gst_vorbis_enc_get_latency (GstVorbisEnc * vorbisenc)
+{
+ /* FIXME, this probably depends on the bitrate and other setting but for now
+ * we return this value, which was obtained by totally unscientific
+ * measurements */
+ return 58 * GST_MSECOND;
+}
+
+static const GstQueryType *
+gst_vorbis_enc_get_query_types (GstPad * pad)
+{
+ static const GstQueryType gst_vorbis_enc_src_query_types[] = {
+ GST_QUERY_POSITION,
+ GST_QUERY_DURATION,
+ GST_QUERY_CONVERT,
+ 0
+ };
+
+ return gst_vorbis_enc_src_query_types;
+}
+
+static gboolean
+gst_vorbis_enc_src_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = TRUE;
+ GstVorbisEnc *vorbisenc;
+ GstPad *peerpad;
+
+ vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
+ peerpad = gst_pad_get_peer (GST_PAD (vorbisenc->sinkpad));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ GstFormat fmt, req_fmt;
+ gint64 pos, val;
+
+ gst_query_parse_position (query, &req_fmt, NULL);
+ if ((res = gst_pad_query_position (peerpad, &req_fmt, &val))) {
+ gst_query_set_position (query, req_fmt, val);
+ break;
+ }
+
+ fmt = GST_FORMAT_TIME;
+ if (!(res = gst_pad_query_position (peerpad, &fmt, &pos)))
+ break;
+
+ if ((res = gst_pad_query_convert (peerpad, fmt, pos, &req_fmt, &val))) {
+ gst_query_set_position (query, req_fmt, val);
+ }
+ break;
+ }
+ case GST_QUERY_DURATION:
+ {
+ GstFormat fmt, req_fmt;
+ gint64 dur, val;
+
+ gst_query_parse_duration (query, &req_fmt, NULL);
+ if ((res = gst_pad_query_duration (peerpad, &req_fmt, &val))) {
+ gst_query_set_duration (query, req_fmt, val);
+ break;
+ }
+
+ fmt = GST_FORMAT_TIME;
+ if (!(res = gst_pad_query_duration (peerpad, &fmt, &dur)))
+ break;
+
+ if ((res = gst_pad_query_convert (peerpad, fmt, dur, &req_fmt, &val))) {
+ gst_query_set_duration (query, req_fmt, val);
+ }
+ break;
+ }
+ 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);
+ if (!(res =
+ gst_vorbis_enc_convert_src (pad, src_fmt, src_val, &dest_fmt,
+ &dest_val)))
+ goto error;
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ break;
+ }
+ case GST_QUERY_LATENCY:
+ {
+ gboolean live;
+ GstClockTime min_latency, max_latency;
+ gint64 latency;
+
+ if ((res = gst_pad_query (peerpad, query))) {
+ gst_query_parse_latency (query, &live, &min_latency, &max_latency);
+
+ latency = gst_vorbis_enc_get_latency (vorbisenc);
+
+ /* add our latency */
+ min_latency += latency;
+ if (max_latency != -1)
+ max_latency += latency;
+
+ gst_query_set_latency (query, live, min_latency, max_latency);
+ }
+ break;
+ }
+ default:
+ res = gst_pad_query (peerpad, query);
+ break;
+ }
+
+error:
+ gst_object_unref (peerpad);
+ gst_object_unref (vorbisenc);
+ return res;
+}
+
+static gboolean
+gst_vorbis_enc_sink_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = TRUE;
+
+ 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);
+ if (!(res =
+ gst_vorbis_enc_convert_sink (pad, src_fmt, src_val, &dest_fmt,
+ &dest_val)))
+ goto error;
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+
+error:
+ return res;
+}
+
+static void
+gst_vorbis_enc_init (GstVorbisEnc * vorbisenc, GstVorbisEncClass * klass)
+{
+ vorbisenc->sinkpad =
+ gst_pad_new_from_static_template (&vorbis_enc_sink_factory, "sink");
+ gst_pad_set_event_function (vorbisenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_event));
+ gst_pad_set_chain_function (vorbisenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_chain));
+ gst_pad_set_setcaps_function (vorbisenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_setcaps));
+ gst_pad_set_getcaps_function (vorbisenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_getcaps));
+ gst_pad_set_query_function (vorbisenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_query));
+ gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad);
+
+ vorbisenc->srcpad =
+ gst_pad_new_from_static_template (&vorbis_enc_src_factory, "src");
+ gst_pad_set_query_function (vorbisenc->srcpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_src_query));
+ gst_pad_set_query_type_function (vorbisenc->srcpad,
+ GST_DEBUG_FUNCPTR (gst_vorbis_enc_get_query_types));
+ gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->srcpad);
+
+ vorbisenc->channels = -1;
+ vorbisenc->frequency = -1;
+
+ vorbisenc->managed = FALSE;
+ vorbisenc->max_bitrate = MAX_BITRATE_DEFAULT;
+ vorbisenc->bitrate = BITRATE_DEFAULT;
+ vorbisenc->min_bitrate = MIN_BITRATE_DEFAULT;
+ vorbisenc->quality = QUALITY_DEFAULT;
+ vorbisenc->quality_set = FALSE;
+ vorbisenc->last_message = NULL;
+}
+
+static void
+gst_vorbis_enc_metadata_set1 (const GstTagList * list, const gchar * tag,
+ gpointer vorbisenc)
+{
+ GstVorbisEnc *enc = GST_VORBISENC (vorbisenc);
+ GList *vc_list, *l;
+
+ vc_list = gst_tag_to_vorbis_comments (list, tag);
+
+ for (l = vc_list; l != NULL; l = l->next) {
+ const gchar *vc_string = (const gchar *) l->data;
+ gchar *key = NULL, *val = NULL;
+
+ GST_LOG_OBJECT (vorbisenc, "vorbis comment: %s", vc_string);
+ if (gst_tag_parse_extended_comment (vc_string, &key, NULL, &val, TRUE)) {
+ vorbis_comment_add_tag (&enc->vc, key, val);
+ g_free (key);
+ g_free (val);
+ }
+ }
+
+ g_list_foreach (vc_list, (GFunc) g_free, NULL);
+ g_list_free (vc_list);
+}
+
+static void
+gst_vorbis_enc_set_metadata (GstVorbisEnc * enc)
+{
+ GstTagList *merged_tags;
+ const GstTagList *user_tags;
+
+ vorbis_comment_init (&enc->vc);
+
+ user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc));
+
+ GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags);
+ GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags);
+
+ /* gst_tag_list_merge() will handle NULL for either or both lists fine */
+ merged_tags = gst_tag_list_merge (user_tags, enc->tags,
+ gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc)));
+
+ if (merged_tags) {
+ GST_DEBUG_OBJECT (enc, "merged tags = %" GST_PTR_FORMAT, merged_tags);
+ gst_tag_list_foreach (merged_tags, gst_vorbis_enc_metadata_set1, enc);
+ gst_tag_list_free (merged_tags);
+ }
+}
+
+static gchar *
+get_constraints_string (GstVorbisEnc * vorbisenc)
+{
+ gint min = vorbisenc->min_bitrate;
+ gint max = vorbisenc->max_bitrate;
+ gchar *result;
+
+ if (min > 0 && max > 0)
+ result = g_strdup_printf ("(min %d bps, max %d bps)", min, max);
+ else if (min > 0)
+ result = g_strdup_printf ("(min %d bps, no max)", min);
+ else if (max > 0)
+ result = g_strdup_printf ("(no min, max %d bps)", max);
+ else
+ result = g_strdup_printf ("(no min or max)");
+
+ return result;
+}
+
+static void
+update_start_message (GstVorbisEnc * vorbisenc)
+{
+ gchar *constraints;
+
+ g_free (vorbisenc->last_message);
+
+ if (vorbisenc->bitrate > 0) {
+ if (vorbisenc->managed) {
+ constraints = get_constraints_string (vorbisenc);
+ vorbisenc->last_message =
+ g_strdup_printf ("encoding at average bitrate %d bps %s",
+ vorbisenc->bitrate, constraints);
+ g_free (constraints);
+ } else {
+ vorbisenc->last_message =
+ g_strdup_printf
+ ("encoding at approximate bitrate %d bps (VBR encoding enabled)",
+ vorbisenc->bitrate);
+ }
+ } else {
+ if (vorbisenc->quality_set) {
+ if (vorbisenc->managed) {
+ constraints = get_constraints_string (vorbisenc);
+ vorbisenc->last_message =
+ g_strdup_printf
+ ("encoding at quality level %2.2f using constrained VBR %s",
+ vorbisenc->quality, constraints);
+ g_free (constraints);
+ } else {
+ vorbisenc->last_message =
+ g_strdup_printf ("encoding at quality level %2.2f",
+ vorbisenc->quality);
+ }
+ } else {
+ constraints = get_constraints_string (vorbisenc);
+ vorbisenc->last_message =
+ g_strdup_printf ("encoding using bitrate management %s", constraints);
+ g_free (constraints);
+ }
+ }
+
+ g_object_notify (G_OBJECT (vorbisenc), "last_message");
+}
+
+static gboolean
+gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc)
+{
+ vorbisenc->setup = FALSE;
+
+ if (vorbisenc->bitrate < 0 && vorbisenc->min_bitrate < 0
+ && vorbisenc->max_bitrate < 0) {
+ vorbisenc->quality_set = TRUE;
+ }
+
+ update_start_message (vorbisenc);
+
+ /* choose an encoding mode */
+ /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
+ vorbis_info_init (&vorbisenc->vi);
+
+ if (vorbisenc->quality_set) {
+ if (vorbis_encode_setup_vbr (&vorbisenc->vi,
+ vorbisenc->channels, vorbisenc->frequency,
+ vorbisenc->quality) != 0) {
+ GST_ERROR_OBJECT (vorbisenc,
+ "vorbisenc: initialisation failed: invalid parameters for quality");
+ vorbis_info_clear (&vorbisenc->vi);
+ return FALSE;
+ }
+
+ /* do we have optional hard quality restrictions? */
+ if (vorbisenc->max_bitrate > 0 || vorbisenc->min_bitrate > 0) {
+ struct ovectl_ratemanage_arg ai;
+
+ vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_GET, &ai);
+
+ ai.bitrate_hard_min = vorbisenc->min_bitrate;
+ ai.bitrate_hard_max = vorbisenc->max_bitrate;
+ ai.management_active = 1;
+
+ vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, &ai);
+ }
+ } else {
+ long min_bitrate, max_bitrate;
+
+ min_bitrate = vorbisenc->min_bitrate > 0 ? vorbisenc->min_bitrate : -1;
+ max_bitrate = vorbisenc->max_bitrate > 0 ? vorbisenc->max_bitrate : -1;
+
+ if (vorbis_encode_setup_managed (&vorbisenc->vi,
+ vorbisenc->channels,
+ vorbisenc->frequency,
+ max_bitrate, vorbisenc->bitrate, min_bitrate) != 0) {
+ GST_ERROR_OBJECT (vorbisenc,
+ "vorbis_encode_setup_managed "
+ "(c %d, rate %d, max br %ld, br %d, min br %ld) failed",
+ vorbisenc->channels, vorbisenc->frequency, max_bitrate,
+ vorbisenc->bitrate, min_bitrate);
+ vorbis_info_clear (&vorbisenc->vi);
+ return FALSE;
+ }
+ }
+
+ if (vorbisenc->managed && vorbisenc->bitrate < 0) {
+ vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_AVG, NULL);
+ } else if (!vorbisenc->managed) {
+ /* Turn off management entirely (if it was turned on). */
+ vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, NULL);
+ }
+ vorbis_encode_setup_init (&vorbisenc->vi);
+
+ /* set up the analysis state and auxiliary encoding storage */
+ vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi);
+ vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb);
+
+ vorbisenc->next_ts = 0;
+
+ vorbisenc->setup = TRUE;
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_vorbis_enc_clear (GstVorbisEnc * vorbisenc)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (vorbisenc->setup) {
+ vorbis_analysis_wrote (&vorbisenc->vd, 0);
+ ret = gst_vorbis_enc_output_buffers (vorbisenc);
+
+ vorbisenc->setup = FALSE;
+ }
+
+ /* clean up and exit. vorbis_info_clear() must be called last */
+ vorbis_block_clear (&vorbisenc->vb);
+ vorbis_dsp_clear (&vorbisenc->vd);
+ vorbis_info_clear (&vorbisenc->vi);
+
+ vorbisenc->header_sent = FALSE;
+
+ return ret;
+}
+
+/* prepare a buffer for transmission by passing data through libvorbis */
+static GstBuffer *
+gst_vorbis_enc_buffer_from_packet (GstVorbisEnc * vorbisenc,
+ ogg_packet * packet)
+{
+ GstBuffer *outbuf;
+
+ outbuf = gst_buffer_new_and_alloc (packet->bytes);
+ memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
+ /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
+ * time representation */
+ GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos +
+ vorbisenc->granulepos_offset;
+ GST_BUFFER_OFFSET (outbuf) = granulepos_to_timestamp (vorbisenc,
+ GST_BUFFER_OFFSET_END (outbuf));
+ GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts;
+
+ /* update the next timestamp, taking granulepos_offset and subgranule offset
+ * into account */
+ vorbisenc->next_ts =
+ granulepos_to_timestamp_offset (vorbisenc, packet->granulepos) +
+ vorbisenc->initial_ts;
+ GST_BUFFER_DURATION (outbuf) =
+ vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
+
+ if (vorbisenc->next_discont) {
+ GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+ vorbisenc->next_discont = FALSE;
+ }
+
+ gst_buffer_set_caps (outbuf, vorbisenc->srccaps);
+
+ GST_LOG_OBJECT (vorbisenc, "encoded buffer of %d bytes",
+ GST_BUFFER_SIZE (outbuf));
+ return outbuf;
+}
+
+/* the same as above, but different logic for setting timestamp and granulepos
+ * */
+static GstBuffer *
+gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
+ ogg_packet * packet)
+{
+ GstBuffer *outbuf;
+
+ outbuf = gst_buffer_new_and_alloc (packet->bytes);
+ memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
+ GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
+ GST_BUFFER_OFFSET_END (outbuf) = 0;
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
+
+ gst_buffer_set_caps (outbuf, vorbisenc->srccaps);
+
+ GST_DEBUG ("created header packet buffer, %d bytes",
+ GST_BUFFER_SIZE (outbuf));
+ return outbuf;
+}
+
+/* push out the buffer and do internal bookkeeping */
+static GstFlowReturn
+gst_vorbis_enc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer)
+{
+ vorbisenc->bytes_out += GST_BUFFER_SIZE (buffer);
+
+ GST_DEBUG_OBJECT (vorbisenc,
+ "Pushing buffer with GP %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT,
+ GST_BUFFER_OFFSET_END (buffer),
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+ return gst_pad_push (vorbisenc->srcpad, buffer);
+}
+
+static GstFlowReturn
+gst_vorbis_enc_push_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet)
+{
+ GstBuffer *outbuf;
+
+ outbuf = gst_vorbis_enc_buffer_from_packet (vorbisenc, packet);
+ return gst_vorbis_enc_push_buffer (vorbisenc, outbuf);
+}
+
+/* Set a copy of these buffers as 'streamheader' on the caps.
+ * We need a copy to avoid these buffers ending up with (indirect) refs on
+ * themselves
+ */
+static GstCaps *
+gst_vorbis_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
+ GstBuffer * buf2, GstBuffer * buf3)
+{
+ GstBuffer *buf;
+ GstStructure *structure;
+ GValue array = { 0 };
+ GValue value = { 0 };
+
+ caps = gst_caps_make_writable (caps);
+ structure = gst_caps_get_structure (caps, 0);
+
+ /* mark buffers */
+ GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
+ GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
+ GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS);
+
+ /* put buffers in a fixed list */
+ g_value_init (&array, GST_TYPE_ARRAY);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ buf = gst_buffer_copy (buf1);
+ gst_value_set_buffer (&value, buf);
+ gst_buffer_unref (buf);
+ gst_value_array_append_value (&array, &value);
+ g_value_unset (&value);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ buf = gst_buffer_copy (buf2);
+ gst_value_set_buffer (&value, buf);
+ gst_buffer_unref (buf);
+ gst_value_array_append_value (&array, &value);
+ g_value_unset (&value);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ buf = gst_buffer_copy (buf3);
+ gst_value_set_buffer (&value, buf);
+ gst_buffer_unref (buf);
+ gst_value_array_append_value (&array, &value);
+ gst_structure_set_value (structure, "streamheader", &array);
+ g_value_unset (&value);
+ g_value_unset (&array);
+
+ return caps;
+}
+
+static gboolean
+gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = TRUE;
+ GstVorbisEnc *vorbisenc;
+
+ vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ /* Tell the library we're at end of stream so that it can handle
+ * the last frame and mark end of stream in the output properly */
+ GST_DEBUG_OBJECT (vorbisenc, "EOS, clearing state and sending event on");
+ gst_vorbis_enc_clear (vorbisenc);
+
+ res = gst_pad_push_event (vorbisenc->srcpad, event);
+ break;
+ case GST_EVENT_TAG:
+ if (vorbisenc->tags) {
+ GstTagList *list;
+
+ gst_event_parse_tag (event, &list);
+ gst_tag_list_insert (vorbisenc->tags, list,
+ gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (vorbisenc)));
+ } else {
+ g_assert_not_reached ();
+ }
+ res = gst_pad_push_event (vorbisenc->srcpad, event);
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ {
+ gboolean update;
+ gdouble rate, applied_rate;
+ GstFormat format;
+ gint64 start, stop, position;
+
+ gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+ &format, &start, &stop, &position);
+ if (format == GST_FORMAT_TIME) {
+ gst_segment_set_newsegment (&vorbisenc->segment, update, rate, format,
+ start, stop, position);
+ if (vorbisenc->initial_ts == GST_CLOCK_TIME_NONE) {
+ GST_DEBUG_OBJECT (vorbisenc, "Initial segment %" GST_SEGMENT_FORMAT,
+ &vorbisenc->segment);
+ vorbisenc->initial_ts = start;
+ }
+ }
+ }
+ /* fall through */
+ default:
+ res = gst_pad_push_event (vorbisenc->srcpad, event);
+ break;
+ }
+ return res;
+}
+
+static gboolean
+gst_vorbis_enc_buffer_check_discontinuous (GstVorbisEnc * vorbisenc,
+ GstClockTime timestamp, GstClockTime duration)
+{
+ gboolean ret = FALSE;
+
+ if (timestamp != GST_CLOCK_TIME_NONE &&
+ vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
+ timestamp + duration != vorbisenc->expected_ts) {
+ /* It turns out that a lot of elements don't generate perfect streams due
+ * to rounding errors. So, we permit small errors (< 1/2 a sample) without
+ * causing a discont.
+ */
+ int halfsample = GST_SECOND / vorbisenc->frequency / 2;
+
+ if ((GstClockTimeDiff) (timestamp - vorbisenc->expected_ts) > halfsample) {
+ GST_DEBUG_OBJECT (vorbisenc, "Expected TS %" GST_TIME_FORMAT
+ ", buffer TS %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (vorbisenc->expected_ts), GST_TIME_ARGS (timestamp));
+ ret = TRUE;
+ }
+ }
+
+ if (timestamp != GST_CLOCK_TIME_NONE && duration != GST_CLOCK_TIME_NONE) {
+ vorbisenc->expected_ts = timestamp + duration;
+ } else
+ vorbisenc->expected_ts = GST_CLOCK_TIME_NONE;
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstVorbisEnc *vorbisenc;
+ GstFlowReturn ret = GST_FLOW_OK;
+ gfloat *data;
+ gulong size;
+ gulong i, j;
+ float **vorbis_buffer;
+ GstBuffer *buf1, *buf2, *buf3;
+ gboolean first = FALSE;
+ GstClockTime timestamp = GST_CLOCK_TIME_NONE;
+ GstClockTime running_time = GST_CLOCK_TIME_NONE;
+
+ vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
+
+ if (!vorbisenc->setup)
+ goto not_setup;
+
+ buffer = gst_audio_buffer_clip (buffer, &vorbisenc->segment,
+ vorbisenc->frequency, 4 * vorbisenc->channels);
+ if (buffer == NULL) {
+ GST_DEBUG_OBJECT (vorbisenc, "Dropping buffer, out of segment");
+ return GST_FLOW_OK;
+ }
+ running_time =
+ gst_segment_to_running_time (&vorbisenc->segment, GST_FORMAT_TIME,
+ GST_BUFFER_TIMESTAMP (buffer));
+ timestamp = running_time + vorbisenc->initial_ts;
+ GST_DEBUG_OBJECT (vorbisenc, "Initial ts is %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (vorbisenc->initial_ts));
+ if (!vorbisenc->header_sent) {
+ /* Vorbis streams begin with three headers; the initial header (with
+ most of the codec setup parameters) which is mandated by the Ogg
+ bitstream spec. The second header holds any comment fields. The
+ third header holds the bitstream codebook. We merely need to
+ make the headers, then pass them to libvorbis one at a time;
+ libvorbis handles the additional Ogg bitstream constraints */
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+ GstCaps *caps;
+
+ /* first, make sure header buffers get timestamp == 0 */
+ vorbisenc->next_ts = 0;
+ vorbisenc->granulepos_offset = 0;
+ vorbisenc->subgranule_offset = 0;
+
+ GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets");
+ gst_vorbis_enc_set_metadata (vorbisenc);
+ vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header,
+ &header_comm, &header_code);
+ vorbis_comment_clear (&vorbisenc->vc);
+
+ /* create header buffers */
+ buf1 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header);
+ buf2 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_comm);
+ buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code);
+
+ /* mark and put on caps */
+ vorbisenc->srccaps = gst_caps_new_simple ("audio/x-vorbis", NULL);
+ caps = vorbisenc->srccaps;
+ caps = gst_vorbis_enc_set_header_on_caps (caps, buf1, buf2, buf3);
+
+ /* negotiate with these caps */
+ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
+ gst_pad_set_caps (vorbisenc->srcpad, caps);
+
+ gst_buffer_set_caps (buf1, caps);
+ gst_buffer_set_caps (buf2, caps);
+ gst_buffer_set_caps (buf3, caps);
+
+ /* push out buffers */
+ /* push_buffer takes the reference even for failure */
+ if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK)
+ goto failed_header_push;
+ if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) {
+ buf2 = NULL;
+ goto failed_header_push;
+ }
+ if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) {
+ buf3 = NULL;
+ goto failed_header_push;
+ }
+
+ /* now adjust starting granulepos accordingly if the buffer's timestamp is
+ nonzero */
+ vorbisenc->next_ts = timestamp;
+ vorbisenc->expected_ts = timestamp;
+ vorbisenc->granulepos_offset = gst_util_uint64_scale
+ (running_time, vorbisenc->frequency, GST_SECOND);
+ vorbisenc->subgranule_offset = 0;
+ vorbisenc->subgranule_offset =
+ (vorbisenc->next_ts - vorbisenc->initial_ts) -
+ granulepos_to_timestamp_offset (vorbisenc, 0);
+
+ vorbisenc->header_sent = TRUE;
+ first = TRUE;
+ }
+
+ if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
+ timestamp < vorbisenc->expected_ts) {
+ guint64 diff = vorbisenc->expected_ts - timestamp;
+ guint64 diff_bytes;
+
+ GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous "
+ "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
+ "), cannot handle. Clipping buffer.",
+ GST_TIME_ARGS (timestamp), GST_TIME_ARGS (vorbisenc->expected_ts));
+
+ diff_bytes =
+ GST_CLOCK_TIME_TO_FRAMES (diff,
+ vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat);
+ if (diff_bytes >= GST_BUFFER_SIZE (buffer)) {
+ gst_buffer_unref (buffer);
+ return GST_FLOW_OK;
+ }
+ buffer = gst_buffer_make_metadata_writable (buffer);
+ GST_BUFFER_DATA (buffer) += diff_bytes;
+ GST_BUFFER_SIZE (buffer) -= diff_bytes;
+
+ GST_BUFFER_TIMESTAMP (buffer) += diff;
+ if (GST_BUFFER_DURATION_IS_VALID (buffer))
+ GST_BUFFER_DURATION (buffer) -= diff;
+ }
+
+ if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, timestamp,
+ GST_BUFFER_DURATION (buffer)) && !first) {
+ GST_WARNING_OBJECT (vorbisenc,
+ "Buffer is discontinuous, flushing encoder "
+ "and restarting (Discont from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
+ ")", GST_TIME_ARGS (vorbisenc->next_ts), GST_TIME_ARGS (timestamp));
+ /* Re-initialise encoder (there's unfortunately no API to flush it) */
+ if ((ret = gst_vorbis_enc_clear (vorbisenc)) != GST_FLOW_OK)
+ return ret;
+ if (!gst_vorbis_enc_setup (vorbisenc))
+ return GST_FLOW_ERROR; /* Should be impossible, we can only get here if
+ we successfully initialised earlier */
+
+ /* Now, set our granulepos offset appropriately. */
+ vorbisenc->next_ts = timestamp;
+ /* We need to round to the nearest whole number of samples, not just do
+ * a truncating division here */
+ vorbisenc->granulepos_offset = gst_util_uint64_scale
+ (running_time + GST_SECOND / vorbisenc->frequency / 2
+ - vorbisenc->subgranule_offset, vorbisenc->frequency, GST_SECOND);
+
+ vorbisenc->header_sent = TRUE;
+
+ /* And our next output buffer must have DISCONT set on it */
+ vorbisenc->next_discont = TRUE;
+ }
+
+ /* Sending zero samples to libvorbis marks EOS, so we mustn't do that */
+ if (GST_BUFFER_SIZE (buffer) == 0) {
+ gst_buffer_unref (buffer);
+ return GST_FLOW_OK;
+ }
+
+ /* data to encode */
+ data = (gfloat *) GST_BUFFER_DATA (buffer);
+ size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float));
+
+ /* expose the buffer to submit data */
+ vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
+
+ /* deinterleave samples, write the buffer data */
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < vorbisenc->channels; j++) {
+ vorbis_buffer[j][i] = *data++;
+ }
+ }
+
+ /* tell the library how much we actually submitted */
+ vorbis_analysis_wrote (&vorbisenc->vd, size);
+
+ GST_LOG_OBJECT (vorbisenc, "wrote %lu samples to vorbis", size);
+
+ vorbisenc->samples_in += size;
+
+ gst_buffer_unref (buffer);
+
+ ret = gst_vorbis_enc_output_buffers (vorbisenc);
+
+ return ret;
+
+ /* error cases */
+not_setup:
+ {
+ gst_buffer_unref (buffer);
+ GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL),
+ ("encoder not initialized (input is not audio?)"));
+ return GST_FLOW_UNEXPECTED;
+ }
+failed_header_push:
+ {
+ GST_WARNING_OBJECT (vorbisenc, "Failed to push headers");
+ /* buf1 is always already unreffed */
+ if (buf2)
+ gst_buffer_unref (buf2);
+ if (buf3)
+ gst_buffer_unref (buf3);
+ gst_buffer_unref (buffer);
+ return ret;
+ }
+}
+
+static GstFlowReturn
+gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc)
+{
+ GstFlowReturn ret;
+
+ /* vorbis does some data preanalysis, then divides up blocks for
+ more involved (potentially parallel) processing. Get a single
+ block for encoding now */
+ while (vorbis_analysis_blockout (&vorbisenc->vd, &vorbisenc->vb) == 1) {
+ ogg_packet op;
+
+ GST_LOG_OBJECT (vorbisenc, "analysed to a block");
+
+ /* analysis */
+ vorbis_analysis (&vorbisenc->vb, NULL);
+ vorbis_bitrate_addblock (&vorbisenc->vb);
+
+ while (vorbis_bitrate_flushpacket (&vorbisenc->vd, &op)) {
+ GST_LOG_OBJECT (vorbisenc, "pushing out a data packet");
+ ret = gst_vorbis_enc_push_packet (vorbisenc, &op);
+
+ if (ret != GST_FLOW_OK)
+ return ret;
+ }
+ }
+
+ return GST_FLOW_OK;
+}
+
+static void
+gst_vorbis_enc_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstVorbisEnc *vorbisenc;
+
+ g_return_if_fail (GST_IS_VORBISENC (object));
+
+ vorbisenc = GST_VORBISENC (object);
+
+ switch (prop_id) {
+ case ARG_MAX_BITRATE:
+ g_value_set_int (value, vorbisenc->max_bitrate);
+ break;
+ case ARG_BITRATE:
+ g_value_set_int (value, vorbisenc->bitrate);
+ break;
+ case ARG_MIN_BITRATE:
+ g_value_set_int (value, vorbisenc->min_bitrate);
+ break;
+ case ARG_QUALITY:
+ g_value_set_float (value, vorbisenc->quality);
+ break;
+ case ARG_MANAGED:
+ g_value_set_boolean (value, vorbisenc->managed);
+ break;
+ case ARG_LAST_MESSAGE:
+ g_value_set_string (value, vorbisenc->last_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_vorbis_enc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVorbisEnc *vorbisenc;
+
+ g_return_if_fail (GST_IS_VORBISENC (object));
+
+ vorbisenc = GST_VORBISENC (object);
+
+ switch (prop_id) {
+ case ARG_MAX_BITRATE:
+ {
+ gboolean old_value = vorbisenc->managed;
+
+ vorbisenc->max_bitrate = g_value_get_int (value);
+ if (vorbisenc->max_bitrate >= 0
+ && vorbisenc->max_bitrate < LOWEST_BITRATE) {
+ g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE);
+ vorbisenc->max_bitrate = LOWEST_BITRATE;
+ }
+ if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0)
+ vorbisenc->managed = TRUE;
+ else
+ vorbisenc->managed = FALSE;
+
+ if (old_value != vorbisenc->managed)
+ g_object_notify (object, "managed");
+ break;
+ }
+ case ARG_BITRATE:
+ vorbisenc->bitrate = g_value_get_int (value);
+ if (vorbisenc->bitrate >= 0 && vorbisenc->bitrate < LOWEST_BITRATE) {
+ g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE);
+ vorbisenc->bitrate = LOWEST_BITRATE;
+ }
+ break;
+ case ARG_MIN_BITRATE:
+ {
+ gboolean old_value = vorbisenc->managed;
+
+ vorbisenc->min_bitrate = g_value_get_int (value);
+ if (vorbisenc->min_bitrate >= 0
+ && vorbisenc->min_bitrate < LOWEST_BITRATE) {
+ g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE);
+ vorbisenc->min_bitrate = LOWEST_BITRATE;
+ }
+ if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0)
+ vorbisenc->managed = TRUE;
+ else
+ vorbisenc->managed = FALSE;
+
+ if (old_value != vorbisenc->managed)
+ g_object_notify (object, "managed");
+ break;
+ }
+ case ARG_QUALITY:
+ vorbisenc->quality = g_value_get_float (value);
+ if (vorbisenc->quality >= 0.0)
+ vorbisenc->quality_set = TRUE;
+ else
+ vorbisenc->quality_set = FALSE;
+ break;
+ case ARG_MANAGED:
+ vorbisenc->managed = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+gst_vorbis_enc_change_state (GstElement * element, GstStateChange transition)
+{
+ GstVorbisEnc *vorbisenc = GST_VORBISENC (element);
+ GstStateChangeReturn res;
+
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ vorbisenc->tags = gst_tag_list_new ();
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ vorbisenc->setup = FALSE;
+ vorbisenc->next_discont = FALSE;
+ vorbisenc->header_sent = FALSE;
+ gst_segment_init (&vorbisenc->segment, GST_FORMAT_TIME);
+ vorbisenc->initial_ts = GST_CLOCK_TIME_NONE;
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ vorbis_block_clear (&vorbisenc->vb);
+ vorbis_dsp_clear (&vorbisenc->vd);
+ vorbis_info_clear (&vorbisenc->vi);
+ g_free (vorbisenc->last_message);
+ vorbisenc->last_message = NULL;
+ if (vorbisenc->srccaps) {
+ gst_caps_unref (vorbisenc->srccaps);
+ vorbisenc->srccaps = NULL;
+ }
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ gst_tag_list_free (vorbisenc->tags);
+ vorbisenc->tags = NULL;
+ default:
+ break;
+ }
+
+ return res;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
+ * Copyright (C) 2006 Andy Wingo <wingo@pobox.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-vorbisparse
+ * @see_also: vorbisdec, oggdemux, theoraparse
+ *
+ * The vorbisparse element will parse the header packets of the Vorbis
+ * stream and put them as the streamheader in the caps. This is used in the
+ * multifdsink case where you want to stream live vorbis streams to multiple
+ * clients, each client has to receive the streamheaders first before they can
+ * consume the vorbis packets.
+ *
+ * This element also makes sure that the buffers that it pushes out are properly
+ * timestamped and that their offset and offset_end are set. The buffers that
+ * vorbisparse outputs have all of the metadata that oggmux expects to receive,
+ * which allows you to (for example) remux an ogg/vorbis file.
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisparse ! fakesink
+ * ]| This pipeline shows that the streamheader is set in the caps, and that each
+ * buffer has the timestamp, duration, offset, and offset_end set.
+ * |[
+ * gst-launch filesrc location=sine.ogg ! oggdemux ! vorbisparse \
+ * ! oggmux ! filesink location=sine-remuxed.ogg
+ * ]| This pipeline shows remuxing. sine-remuxed.ogg might not be exactly the same
+ * as sine.ogg, but they should produce exactly the same decoded data.
+ * </refsect2>
+ *
+ * Last reviewed on 2006-04-01 (0.10.4.1)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstvorbisparse.h"
+
+GST_DEBUG_CATEGORY_EXTERN (vorbisparse_debug);
+#define GST_CAT_DEFAULT vorbisparse_debug
+
+static const GstElementDetails vorbis_parse_details = {
+ "VorbisParse",
+ "Codec/Parser/Audio",
+ "parse raw vorbis streams",
+ "Thomas Vander Stichele <thomas at apestaart dot org>"
+};
+
+static GstStaticPadTemplate vorbis_parse_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-vorbis")
+ );
+
+static GstStaticPadTemplate vorbis_parse_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-vorbis")
+ );
+
+GST_BOILERPLATE (GstVorbisParse, gst_vorbis_parse, GstElement,
+ GST_TYPE_ELEMENT);
+
+static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstBuffer * buffer);
+static GstStateChangeReturn vorbis_parse_change_state (GstElement * element,
+ GstStateChange transition);
+static gboolean vorbis_parse_sink_event (GstPad * pad, GstEvent * event);
+static gboolean vorbis_parse_src_query (GstPad * pad, GstQuery * query);
+static gboolean vorbis_parse_convert (GstPad * pad,
+ GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value);
+static GstFlowReturn vorbis_parse_parse_packet (GstVorbisParse * parse,
+ GstBuffer * buf);
+
+static void
+gst_vorbis_parse_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 (&vorbis_parse_src_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&vorbis_parse_sink_factory));
+ gst_element_class_set_details (element_class, &vorbis_parse_details);
+}
+
+static void
+gst_vorbis_parse_class_init (GstVorbisParseClass * klass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ gstelement_class->change_state = vorbis_parse_change_state;
+
+ klass->parse_packet = GST_DEBUG_FUNCPTR (vorbis_parse_parse_packet);
+}
+
+static void
+gst_vorbis_parse_init (GstVorbisParse * parse, GstVorbisParseClass * g_class)
+{
+ parse->sinkpad =
+ gst_pad_new_from_static_template (&vorbis_parse_sink_factory, "sink");
+ gst_pad_set_chain_function (parse->sinkpad,
+ GST_DEBUG_FUNCPTR (vorbis_parse_chain));
+ gst_pad_set_event_function (parse->sinkpad,
+ GST_DEBUG_FUNCPTR (vorbis_parse_sink_event));
+ gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
+
+ parse->srcpad =
+ gst_pad_new_from_static_template (&vorbis_parse_src_factory, "src");
+ gst_pad_set_query_function (parse->srcpad,
+ GST_DEBUG_FUNCPTR (vorbis_parse_src_query));
+ gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
+}
+
+static void
+vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps)
+{
+ GstBuffer *buf1, *buf2, *buf3;
+ GstStructure *structure;
+ GValue array = { 0 };
+ GValue value = { 0 };
+
+ g_assert (parse);
+ g_assert (parse->streamheader);
+ g_assert (parse->streamheader->next);
+ g_assert (parse->streamheader->next->next);
+ buf1 = parse->streamheader->data;
+ g_assert (buf1);
+ buf2 = parse->streamheader->next->data;
+ g_assert (buf2);
+ buf3 = parse->streamheader->next->next->data;
+ g_assert (buf3);
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ /* mark buffers */
+ GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
+ GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
+ GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS);
+
+ /* put buffers in a fixed list */
+ g_value_init (&array, GST_TYPE_ARRAY);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ gst_value_set_buffer (&value, buf1);
+ gst_value_array_append_value (&array, &value);
+ g_value_unset (&value);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ gst_value_set_buffer (&value, buf2);
+ gst_value_array_append_value (&array, &value);
+ g_value_unset (&value);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ gst_value_set_buffer (&value, buf3);
+ gst_value_array_append_value (&array, &value);
+ gst_structure_set_value (structure, "streamheader", &array);
+ g_value_unset (&value);
+ g_value_unset (&array);
+}
+
+static void
+vorbis_parse_drain_event_queue (GstVorbisParse * parse)
+{
+ while (parse->event_queue->length) {
+ GstEvent *event;
+
+ event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
+ gst_pad_event_default (parse->sinkpad, event);
+ }
+}
+
+static void
+vorbis_parse_push_headers (GstVorbisParse * parse)
+{
+ /* mark and put on caps */
+ GstCaps *caps;
+ GstBuffer *outbuf, *outbuf1, *outbuf2, *outbuf3;
+ ogg_packet packet;
+
+ /* get the headers into the caps, passing them to vorbis as we go */
+ caps = gst_caps_make_writable (gst_pad_get_caps (parse->srcpad));
+ vorbis_parse_set_header_on_caps (parse, caps);
+ GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps);
+ gst_pad_set_caps (parse->srcpad, caps);
+ gst_caps_unref (caps);
+
+ outbuf = GST_BUFFER_CAST (parse->streamheader->data);
+ packet.packet = GST_BUFFER_DATA (outbuf);
+ packet.bytes = GST_BUFFER_SIZE (outbuf);
+ packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
+ packet.packetno = 1;
+ packet.e_o_s = 0;
+ packet.b_o_s = 1;
+ vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
+ parse->sample_rate = parse->vi.rate;
+ outbuf1 = outbuf;
+
+ outbuf = GST_BUFFER_CAST (parse->streamheader->next->data);
+ packet.packet = GST_BUFFER_DATA (outbuf);
+ packet.bytes = GST_BUFFER_SIZE (outbuf);
+ packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
+ packet.packetno = 2;
+ packet.e_o_s = 0;
+ packet.b_o_s = 0;
+ vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
+ outbuf2 = outbuf;
+
+ outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data);
+ packet.packet = GST_BUFFER_DATA (outbuf);
+ packet.bytes = GST_BUFFER_SIZE (outbuf);
+ packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
+ packet.packetno = 3;
+ packet.e_o_s = 0;
+ packet.b_o_s = 0;
+ vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
+ outbuf3 = outbuf;
+
+ /* first process queued events */
+ vorbis_parse_drain_event_queue (parse);
+
+ /* push out buffers, ignoring return value... */
+ gst_buffer_set_caps (outbuf1, GST_PAD_CAPS (parse->srcpad));
+ gst_pad_push (parse->srcpad, outbuf1);
+ gst_buffer_set_caps (outbuf2, GST_PAD_CAPS (parse->srcpad));
+ gst_pad_push (parse->srcpad, outbuf2);
+ gst_buffer_set_caps (outbuf3, GST_PAD_CAPS (parse->srcpad));
+ gst_pad_push (parse->srcpad, outbuf3);
+
+ g_list_free (parse->streamheader);
+ parse->streamheader = NULL;
+}
+
+static void
+vorbis_parse_clear_queue (GstVorbisParse * parse)
+{
+ while (parse->buffer_queue->length) {
+ GstBuffer *buf;
+
+ buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
+ gst_buffer_unref (buf);
+ }
+ while (parse->event_queue->length) {
+ GstEvent *event;
+
+ event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
+ gst_event_unref (event);
+ }
+}
+
+static GstFlowReturn
+vorbis_parse_push_buffer (GstVorbisParse * parse, GstBuffer * buf,
+ gint64 granulepos)
+{
+ guint64 samples;
+
+ /* our hack as noted below */
+ samples = GST_BUFFER_OFFSET (buf);
+
+ GST_BUFFER_OFFSET_END (buf) = granulepos;
+ GST_BUFFER_DURATION (buf) = samples * GST_SECOND / parse->sample_rate;
+ GST_BUFFER_OFFSET (buf) = granulepos * GST_SECOND / parse->sample_rate;
+ GST_BUFFER_TIMESTAMP (buf) =
+ GST_BUFFER_OFFSET (buf) - GST_BUFFER_DURATION (buf);
+
+ gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad));
+
+ return gst_pad_push (parse->srcpad, buf);
+}
+
+static GstFlowReturn
+vorbis_parse_drain_queue_prematurely (GstVorbisParse * parse)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ gint64 granulepos = MAX (parse->prev_granulepos, 0);
+
+ /* got an EOS event, make sure to push out any buffers that were in the queue
+ * -- won't normally be the case, but this catches the
+ * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous
+ * stream. */
+
+ /* if we got EOS before any buffers came, go ahead and push the other events
+ * first */
+ vorbis_parse_drain_event_queue (parse);
+
+ while (!g_queue_is_empty (parse->buffer_queue)) {
+ GstBuffer *buf;
+
+ buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
+
+ granulepos += GST_BUFFER_OFFSET (buf);
+ ret = vorbis_parse_push_buffer (parse, buf, granulepos);
+
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ parse->prev_granulepos = granulepos;
+
+done:
+ return ret;
+}
+
+static GstFlowReturn
+vorbis_parse_drain_queue (GstVorbisParse * parse, gint64 granulepos)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ GList *walk;
+ gint64 cur = granulepos;
+ gint64 gp;
+
+ for (walk = parse->buffer_queue->head; walk; walk = walk->next)
+ cur -= GST_BUFFER_OFFSET (walk->data);
+
+ if (parse->prev_granulepos != -1)
+ cur = MAX (cur, parse->prev_granulepos);
+
+ while (!g_queue_is_empty (parse->buffer_queue)) {
+ GstBuffer *buf;
+
+ buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
+
+ cur += GST_BUFFER_OFFSET (buf);
+ gp = CLAMP (cur, 0, granulepos);
+
+ ret = vorbis_parse_push_buffer (parse, buf, gp);
+
+ if (ret != GST_FLOW_OK)
+ goto done;
+ }
+
+ parse->prev_granulepos = granulepos;
+
+done:
+ return ret;
+}
+
+static GstFlowReturn
+vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ long blocksize;
+ ogg_packet packet;
+
+ buf = gst_buffer_make_metadata_writable (buf);
+
+ packet.packet = GST_BUFFER_DATA (buf);
+ packet.bytes = GST_BUFFER_SIZE (buf);
+ packet.granulepos = GST_BUFFER_OFFSET_END (buf);
+ packet.packetno = parse->packetno + parse->buffer_queue->length;
+ packet.e_o_s = 0;
+
+ blocksize = vorbis_packet_blocksize (&parse->vi, &packet);
+
+ /* temporarily store the sample count in OFFSET -- we overwrite this later */
+
+ if (parse->prev_blocksize < 0)
+ GST_BUFFER_OFFSET (buf) = 0;
+ else
+ GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4;
+
+ parse->prev_blocksize = blocksize;
+
+ g_queue_push_tail (parse->buffer_queue, buf);
+
+ if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
+ ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf));
+
+ return ret;
+}
+
+static GstFlowReturn
+vorbis_parse_parse_packet (GstVorbisParse * parse, GstBuffer * buf)
+{
+ GstFlowReturn ret;
+ guint8 *data;
+ guint size;
+ gboolean have_header;
+
+ data = GST_BUFFER_DATA (buf);
+ size = GST_BUFFER_SIZE (buf);
+
+ parse->packetno++;
+
+ have_header = FALSE;
+ if (size >= 1) {
+ if (data[0] >= 0x01 && data[0] <= 0x05)
+ have_header = TRUE;
+ }
+
+ if (have_header) {
+ if (!parse->streamheader_sent) {
+ /* we need to collect the headers still */
+ /* so put it on the streamheader list and return */
+ parse->streamheader = g_list_append (parse->streamheader, buf);
+ }
+ ret = GST_FLOW_OK;
+ } else {
+ /* data packet, push the headers we collected before */
+ if (!parse->streamheader_sent) {
+ vorbis_parse_push_headers (parse);
+ parse->streamheader_sent = TRUE;
+ }
+ ret = vorbis_parse_queue_buffer (parse, buf);
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+vorbis_parse_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstVorbisParseClass *klass;
+ GstVorbisParse *parse;
+
+ parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
+ klass = GST_VORBIS_PARSE_CLASS (G_OBJECT_GET_CLASS (parse));
+
+ g_assert (klass->parse_packet != NULL);
+
+ return klass->parse_packet (parse, buffer);
+}
+
+static gboolean
+vorbis_parse_queue_event (GstVorbisParse * parse, GstEvent * event)
+{
+ GstFlowReturn ret = TRUE;
+
+ g_queue_push_tail (parse->event_queue, event);
+
+ return ret;
+}
+
+static gboolean
+vorbis_parse_sink_event (GstPad * pad, GstEvent * event)
+{
+ gboolean ret;
+ GstVorbisParse *parse;
+
+ parse = GST_VORBIS_PARSE (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ vorbis_parse_clear_queue (parse);
+ parse->prev_granulepos = -1;
+ parse->prev_blocksize = -1;
+ ret = gst_pad_event_default (pad, event);
+ break;
+ case GST_EVENT_EOS:
+ vorbis_parse_drain_queue_prematurely (parse);
+ ret = gst_pad_event_default (pad, event);
+ break;
+ default:
+ if (!parse->streamheader_sent && GST_EVENT_IS_SERIALIZED (event))
+ ret = vorbis_parse_queue_event (parse, event);
+ else
+ ret = gst_pad_event_default (pad, event);
+ break;
+ }
+
+ gst_object_unref (parse);
+
+ return ret;
+}
+
+static gboolean
+vorbis_parse_convert (GstPad * pad,
+ GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ GstVorbisParse *parse;
+ guint64 scale = 1;
+
+ parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
+
+ /* fixme: assumes atomic access to lots of instance variables modified from
+ * the streaming thread, including 64-bit variables */
+
+ if (parse->packetno < 4)
+ return FALSE;
+
+ if (src_format == *dest_format) {
+ *dest_value = src_value;
+ return TRUE;
+ }
+
+ if (parse->sinkpad == pad &&
+ (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
+ return FALSE;
+
+ switch (src_format) {
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ scale = sizeof (float) * parse->vi.channels;
+ case GST_FORMAT_DEFAULT:
+ *dest_value =
+ scale * gst_util_uint64_scale_int (src_value, parse->vi.rate,
+ GST_SECOND);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * sizeof (float) * parse->vi.channels;
+ break;
+ case GST_FORMAT_TIME:
+ *dest_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND, parse->vi.rate);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_DEFAULT:
+ *dest_value = src_value / (sizeof (float) * parse->vi.channels);
+ break;
+ case GST_FORMAT_TIME:
+ *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
+ parse->vi.rate * sizeof (float) * parse->vi.channels);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+
+ return res;
+}
+
+static gboolean
+vorbis_parse_src_query (GstPad * pad, GstQuery * query)
+{
+ gint64 granulepos;
+ GstVorbisParse *parse;
+ gboolean res = FALSE;
+
+ parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ GstFormat format;
+ gint64 value;
+
+ granulepos = parse->prev_granulepos;
+
+ gst_query_parse_position (query, &format, NULL);
+
+ /* and convert to the final format */
+ if (!(res =
+ vorbis_parse_convert (pad, GST_FORMAT_DEFAULT, granulepos,
+ &format, &value)))
+ goto error;
+
+ /* fixme: support segments
+ value = (value - parse->segment_start) + parse->segment_time;
+ */
+
+ gst_query_set_position (query, format, value);
+
+ GST_LOG_OBJECT (parse, "query %p: peer returned granulepos: %"
+ G_GUINT64_FORMAT " - we return %" G_GUINT64_FORMAT " (format %u)",
+ query, granulepos, value, format);
+
+ break;
+ }
+ case GST_QUERY_DURATION:
+ {
+ /* fixme: not threadsafe */
+ /* query peer for total length */
+ if (!gst_pad_is_linked (parse->sinkpad)) {
+ GST_WARNING_OBJECT (parse, "sink pad %" GST_PTR_FORMAT " is not linked",
+ parse->sinkpad);
+ goto error;
+ }
+ if (!(res = gst_pad_query (GST_PAD_PEER (parse->sinkpad), query)))
+ goto error;
+ break;
+ }
+ 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);
+ if (!(res =
+ vorbis_parse_convert (pad, src_fmt, src_val, &dest_fmt,
+ &dest_val)))
+ goto error;
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+ return res;
+
+error:
+ {
+ GST_WARNING_OBJECT (parse, "error handling query");
+ return res;
+ }
+}
+
+static GstStateChangeReturn
+vorbis_parse_change_state (GstElement * element, GstStateChange transition)
+{
+ GstVorbisParse *parse = GST_VORBIS_PARSE (element);
+ GstStateChangeReturn ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ vorbis_info_init (&parse->vi);
+ vorbis_comment_init (&parse->vc);
+ parse->prev_granulepos = -1;
+ parse->prev_blocksize = -1;
+ parse->packetno = 0;
+ parse->streamheader_sent = FALSE;
+ parse->buffer_queue = g_queue_new ();
+ parse->event_queue = g_queue_new ();
+ break;
+ default:
+ break;
+ }
+
+ ret = parent_class->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ vorbis_info_clear (&parse->vi);
+ vorbis_comment_clear (&parse->vc);
+ vorbis_parse_clear_queue (parse);
+ g_queue_free (parse->buffer_queue);
+ parse->buffer_queue = NULL;
+ g_queue_free (parse->event_queue);
+ parse->event_queue = NULL;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 James Livingston <doclivingston@gmail.com>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-vorbistag
+ * @see_also: #oggdemux, #oggmux, #vorbisparse, #GstTagSetter
+ *
+ * The vorbistags element can change the tag contained within a raw
+ * vorbis stream. Specifically, it modifies the comments header packet
+ * of the vorbis stream.
+ *
+ * The element will also process the stream as the #vorbisparse element does
+ * so it can be used when remuxing an Ogg Vorbis stream, without additional
+ * elements.
+ *
+ * Applications can set the tags to write using the #GstTagSetter interface.
+ * Tags contained withing the vorbis bitstream will be picked up
+ * automatically (and merged according to the merge mode set via the tag
+ * setter interface).
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch -v filesrc location=foo.ogg ! oggdemux ! vorbistag ! oggmux ! filesink location=bar.ogg
+ * ]| This element is not useful with gst-launch, because it does not support
+ * setting the tags on a #GstTagSetter interface. Conceptually, the element
+ * will usually be used in this order though.
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <gst/tag/tag.h>
+#include <gst/gsttagsetter.h>
+
+#include <vorbis/codec.h>
+
+#include "gstvorbistag.h"
+
+
+GST_DEBUG_CATEGORY_EXTERN (vorbisparse_debug);
+#define GST_CAT_DEFAULT vorbisparse_debug
+
+static void gst_vorbis_tag_base_init (gpointer g_class);
+static void gst_vorbis_tag_class_init (GstVorbisTagClass * klass);
+static void gst_vorbis_tag_init (GstVorbisTag * tagger,
+ GstVorbisTagClass * g_class);
+static GstFlowReturn gst_vorbis_tag_parse_packet (GstVorbisParse * parse,
+ GstBuffer * buffer);
+
+#define _do_init(type) \
+ G_STMT_START{ \
+ static const GInterfaceInfo tag_setter_info = { \
+ NULL, \
+ NULL, \
+ NULL \
+ }; \
+ g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, \
+ &tag_setter_info); \
+ }G_STMT_END
+
+GST_BOILERPLATE_FULL (GstVorbisTag, gst_vorbis_tag, GstVorbisParse,
+ GST_TYPE_VORBIS_PARSE, _do_init);
+
+static GstElementDetails vorbis_tag_details = {
+ "VorbisTag",
+ "Formatter/Metadata",
+ "Retags vorbis streams",
+ "James Livingston <doclivingston@gmail.com>"
+};
+
+
+static void
+gst_vorbis_tag_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (element_class, &vorbis_tag_details);
+}
+
+static void
+gst_vorbis_tag_class_init (GstVorbisTagClass * klass)
+{
+ GstVorbisParseClass *vorbisparse_class = GST_VORBIS_PARSE_CLASS (klass);
+
+ vorbisparse_class->parse_packet = gst_vorbis_tag_parse_packet;
+}
+
+static void
+gst_vorbis_tag_init (GstVorbisTag * tagger, GstVorbisTagClass * g_class)
+{
+ /* nothing to do */
+}
+
+
+static GstFlowReturn
+gst_vorbis_tag_parse_packet (GstVorbisParse * parse, GstBuffer * buffer)
+{
+ GstTagList *old_tags, *new_tags;
+ const GstTagList *user_tags;
+ GstVorbisTag *tagger;
+ gchar *encoder = NULL;
+ GstBuffer *new_buf;
+
+ /* just pass everything except the comments packet */
+ if (GST_BUFFER_SIZE (buffer) >= 1 && GST_BUFFER_DATA (buffer)[0] != 0x03) {
+ return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, buffer);
+ }
+
+ tagger = GST_VORBIS_TAG (parse);
+
+ old_tags =
+ gst_tag_list_from_vorbiscomment_buffer (buffer, (guint8 *) "\003vorbis",
+ 7, &encoder);
+ user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tagger));
+
+ /* build new tag list */
+ new_tags = gst_tag_list_merge (user_tags, old_tags,
+ gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tagger)));
+ gst_tag_list_free (old_tags);
+
+ new_buf =
+ gst_tag_list_to_vorbiscomment_buffer (new_tags, (guint8 *) "\003vorbis",
+ 7, encoder);
+ gst_buffer_copy_metadata (new_buf, buffer, GST_BUFFER_COPY_TIMESTAMPS);
+
+ gst_tag_list_free (new_tags);
+ g_free (encoder);
+ gst_buffer_unref (buffer);
+
+ return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, new_buf);
+}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- *
- * 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.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "gst/tag/tag.h"
-
-#include "gstvorbisenc.h"
-#include "gstvorbisdec.h"
-#include "gstvorbisparse.h"
-#include "gstvorbistag.h"
-
-GST_DEBUG_CATEGORY (vorbisenc_debug);
-GST_DEBUG_CATEGORY (vorbisdec_debug);
-GST_DEBUG_CATEGORY (vorbisparse_debug);
-GST_DEBUG_CATEGORY (vorbistag_debug);
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- if (!gst_element_register (plugin, "vorbisenc", GST_RANK_PRIMARY,
- GST_TYPE_VORBISENC))
- return FALSE;
-
- if (!gst_element_register (plugin, "vorbisdec", GST_RANK_PRIMARY,
- gst_vorbis_dec_get_type ()))
- return FALSE;
-
- if (!gst_element_register (plugin, "vorbisparse", GST_RANK_NONE,
- gst_vorbis_parse_get_type ()))
- return FALSE;
-
- if (!gst_element_register (plugin, "vorbistag", GST_RANK_NONE,
- gst_vorbis_tag_get_type ()))
- return FALSE;
-
- GST_DEBUG_CATEGORY_INIT (vorbisenc_debug, "vorbisenc", 0,
- "vorbis encoding element");
- GST_DEBUG_CATEGORY_INIT (vorbisdec_debug, "vorbisdec", 0,
- "vorbis decoding element");
- GST_DEBUG_CATEGORY_INIT (vorbisparse_debug, "vorbisparse", 0,
- "vorbis parsing element");
- GST_DEBUG_CATEGORY_INIT (vorbistag_debug, "vorbistag", 0,
- "vorbis tagging element");
-
- gst_tag_register_musicbrainz_tags ();
-
- return TRUE;
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "vorbis",
- "Vorbis plugin library",
- plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
+++ /dev/null
-/* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-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>.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
- * ]| Decode an Ogg/Vorbis. To create an Ogg/Vorbis file refer to the documentation of vorbisenc.
- * </refsect2>
- *
- * Last reviewed on 2006-03-01 (0.10.4)
- */
-
-#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 <gst/audio/multichannel.h>
-
-#include "gstvorbiscommon.h"
-
-GST_DEBUG_CATEGORY_EXTERN (vorbisdec_debug);
-#define GST_CAT_DEFAULT vorbisdec_debug
-
-static const GstElementDetails vorbis_dec_details =
-GST_ELEMENT_DETAILS ("Vorbis audio decoder",
- "Codec/Decoder/Audio",
- "decode raw vorbis streams to float audio",
- "Benjamin Otte <in7y118@public.uni-hamburg.de>");
-
-static GstStaticPadTemplate vorbis_dec_src_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-float, "
- "rate = (int) [ 1, MAX ], "
- "channels = (int) [ 1, 256 ], " "endianness = (int) BYTE_ORDER, "
-/* no ifdef in macros, please
-#ifdef GST_VORBIS_DEC_SEQUENTIAL
- "layout = \"sequential\", "
-#endif
-*/
- "width = (int) 32")
- );
-
-static GstStaticPadTemplate vorbis_dec_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-vorbis")
- );
-
-GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstElement, GST_TYPE_ELEMENT);
-
-static void vorbis_dec_finalize (GObject * object);
-static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event);
-static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer);
-static GstFlowReturn vorbis_dec_chain_forward (GstVorbisDec * vd,
- gboolean discont, GstBuffer * buffer);
-static GstStateChangeReturn vorbis_dec_change_state (GstElement * element,
- GstStateChange transition);
-
-static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event);
-static gboolean vorbis_dec_src_query (GstPad * pad, GstQuery * query);
-static gboolean vorbis_dec_convert (GstPad * pad,
- GstFormat src_format, gint64 src_value,
- GstFormat * dest_format, gint64 * dest_value);
-
-static gboolean vorbis_dec_sink_query (GstPad * pad, GstQuery * query);
-
-static void
-gst_vorbis_dec_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- GstPadTemplate *src_template, *sink_template;
-
- src_template = gst_static_pad_template_get (&vorbis_dec_src_factory);
- gst_element_class_add_pad_template (element_class, src_template);
-
- sink_template = gst_static_pad_template_get (&vorbis_dec_sink_factory);
- gst_element_class_add_pad_template (element_class, sink_template);
-
- gst_element_class_set_details (element_class, &vorbis_dec_details);
-}
-
-static void
-gst_vorbis_dec_class_init (GstVorbisDecClass * klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
-
- gobject_class->finalize = vorbis_dec_finalize;
-
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (vorbis_dec_change_state);
-}
-
-static const GstQueryType *
-vorbis_get_query_types (GstPad * pad)
-{
- static const GstQueryType vorbis_dec_src_query_types[] = {
- GST_QUERY_POSITION,
- GST_QUERY_DURATION,
- GST_QUERY_CONVERT,
- 0
- };
-
- return vorbis_dec_src_query_types;
-}
-
-static void
-gst_vorbis_dec_init (GstVorbisDec * dec, GstVorbisDecClass * g_class)
-{
- dec->sinkpad = gst_pad_new_from_static_template (&vorbis_dec_sink_factory,
- "sink");
-
- gst_pad_set_event_function (dec->sinkpad,
- GST_DEBUG_FUNCPTR (vorbis_dec_sink_event));
- gst_pad_set_chain_function (dec->sinkpad,
- GST_DEBUG_FUNCPTR (vorbis_dec_chain));
- gst_pad_set_query_function (dec->sinkpad,
- GST_DEBUG_FUNCPTR (vorbis_dec_sink_query));
- gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
-
- dec->srcpad = gst_pad_new_from_static_template (&vorbis_dec_src_factory,
- "src");
-
- gst_pad_set_event_function (dec->srcpad,
- GST_DEBUG_FUNCPTR (vorbis_dec_src_event));
- gst_pad_set_query_type_function (dec->srcpad,
- GST_DEBUG_FUNCPTR (vorbis_get_query_types));
- gst_pad_set_query_function (dec->srcpad,
- GST_DEBUG_FUNCPTR (vorbis_dec_src_query));
- gst_pad_use_fixed_caps (dec->srcpad);
- gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
-
- dec->queued = NULL;
- dec->pendingevents = NULL;
- dec->taglist = NULL;
-}
-
-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);
-
- vorbis_block_clear (&vd->vb);
- vorbis_dsp_clear (&vd->vd);
- vorbis_comment_clear (&vd->vc);
- vorbis_info_clear (&vd->vi);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_vorbis_dec_reset (GstVorbisDec * dec)
-{
- dec->last_timestamp = GST_CLOCK_TIME_NONE;
- dec->discont = TRUE;
- dec->seqnum = gst_util_seqnum_next ();
- gst_segment_init (&dec->segment, GST_FORMAT_TIME);
-
- g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
- g_list_free (dec->queued);
- dec->queued = NULL;
- g_list_foreach (dec->gather, (GFunc) gst_mini_object_unref, NULL);
- g_list_free (dec->gather);
- dec->gather = NULL;
- g_list_foreach (dec->decode, (GFunc) gst_mini_object_unref, NULL);
- g_list_free (dec->decode);
- dec->decode = NULL;
- g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL);
- g_list_free (dec->pendingevents);
- dec->pendingevents = NULL;
-
- if (dec->taglist)
- gst_tag_list_free (dec->taglist);
- dec->taglist = NULL;
-}
-
-
-static gboolean
-vorbis_dec_convert (GstPad * pad,
- GstFormat src_format, gint64 src_value,
- GstFormat * dest_format, gint64 * dest_value)
-{
- gboolean res = TRUE;
- GstVorbisDec *dec;
- guint64 scale = 1;
-
- if (src_format == *dest_format) {
- *dest_value = src_value;
- return TRUE;
- }
-
- dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
-
- if (!dec->initialized)
- goto no_header;
-
- if (dec->sinkpad == pad &&
- (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
- goto no_format;
-
- switch (src_format) {
- case GST_FORMAT_TIME:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- scale = sizeof (float) * dec->vi.channels;
- case GST_FORMAT_DEFAULT:
- *dest_value =
- scale * gst_util_uint64_scale_int (src_value, dec->vi.rate,
- GST_SECOND);
- break;
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_DEFAULT:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- *dest_value = src_value * sizeof (float) * dec->vi.channels;
- break;
- case GST_FORMAT_TIME:
- *dest_value =
- gst_util_uint64_scale_int (src_value, GST_SECOND, dec->vi.rate);
- break;
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_BYTES:
- switch (*dest_format) {
- case GST_FORMAT_DEFAULT:
- *dest_value = src_value / (sizeof (float) * dec->vi.channels);
- break;
- case GST_FORMAT_TIME:
- *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
- dec->vi.rate * sizeof (float) * dec->vi.channels);
- break;
- default:
- res = FALSE;
- }
- break;
- default:
- res = FALSE;
- }
-done:
- gst_object_unref (dec);
-
- return res;
-
- /* ERRORS */
-no_header:
- {
- GST_DEBUG_OBJECT (dec, "no header packets received");
- res = FALSE;
- goto done;
- }
-no_format:
- {
- GST_DEBUG_OBJECT (dec, "formats unsupported");
- res = FALSE;
- goto done;
- }
-}
-
-static gboolean
-vorbis_dec_src_query (GstPad * pad, GstQuery * query)
-{
- GstVorbisDec *dec;
- gboolean res = FALSE;
-
- dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_POSITION:
- {
- gint64 value;
- GstFormat format;
- gint64 time;
-
- gst_query_parse_position (query, &format, NULL);
-
- /* we start from the last seen time */
- time = dec->last_timestamp;
- /* correct for the segment values */
- time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
-
- GST_LOG_OBJECT (dec,
- "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));
-
- /* and convert to the final format */
- if (!(res =
- vorbis_dec_convert (pad, GST_FORMAT_TIME, time, &format, &value)))
- goto error;
-
- gst_query_set_position (query, format, value);
-
- GST_LOG_OBJECT (dec,
- "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value,
- format);
-
- break;
- }
- case GST_QUERY_DURATION:
- {
- res = gst_pad_peer_query (dec->sinkpad, query);
- if (!res)
- goto error;
-
- break;
- }
- 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);
- if (!(res =
- vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val)))
- goto error;
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
-done:
- gst_object_unref (dec);
-
- return res;
-
- /* ERRORS */
-error:
- {
- GST_WARNING_OBJECT (dec, "error handling query");
- goto done;
- }
-}
-
-static gboolean
-vorbis_dec_sink_query (GstPad * pad, GstQuery * query)
-{
- GstVorbisDec *dec;
- gboolean res;
-
- dec = GST_VORBIS_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);
- if (!(res =
- vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val)))
- goto error;
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
-
-done:
- gst_object_unref (dec);
-
- return res;
-
- /* ERRORS */
-error:
- {
- GST_DEBUG_OBJECT (dec, "error converting value");
- goto done;
- }
-}
-
-static gboolean
-vorbis_dec_src_event (GstPad * pad, GstEvent * event)
-{
- gboolean res = TRUE;
- GstVorbisDec *dec;
-
- dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
-
- 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;
- guint32 seqnum;
-
- gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
- &stop_type, &stop);
- seqnum = gst_event_get_seqnum (event);
- gst_event_unref (event);
-
- /* First bring the requested format to time */
- tformat = GST_FORMAT_TIME;
- if (!(res = vorbis_dec_convert (pad, format, cur, &tformat, &tcur)))
- goto convert_error;
- if (!(res = vorbis_dec_convert (pad, format, stop, &tformat, &tstop)))
- goto convert_error;
-
- /* 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_event_set_seqnum (real_seek, seqnum);
-
- res = gst_pad_push_event (dec->sinkpad, real_seek);
- break;
- }
- default:
- res = gst_pad_push_event (dec->sinkpad, event);
- break;
- }
-done:
- gst_object_unref (dec);
-
- return res;
-
- /* ERRORS */
-convert_error:
- {
- GST_DEBUG_OBJECT (dec, "cannot convert start/stop for seek");
- goto done;
- }
-}
-
-static gboolean
-vorbis_dec_sink_event (GstPad * pad, GstEvent * event)
-{
- gboolean ret = FALSE;
- GstVorbisDec *dec;
-
- dec = GST_VORBIS_DEC (gst_pad_get_parent (pad));
-
- GST_LOG_OBJECT (dec, "handling event");
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- ret = gst_pad_push_event (dec->srcpad, event);
- break;
- case GST_EVENT_FLUSH_START:
- ret = gst_pad_push_event (dec->srcpad, event);
- break;
- case GST_EVENT_FLUSH_STOP:
- /* here we must clean any state in the decoder */
-#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
- vorbis_synthesis_restart (&dec->vd);
-#endif
- gst_vorbis_dec_reset (dec);
- ret = gst_pad_push_event (dec->srcpad, event);
- break;
- 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);
-
- /* we need time for now */
- if (format != GST_FORMAT_TIME)
- goto newseg_wrong_format;
-
- GST_DEBUG_OBJECT (dec,
- "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
- ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
- update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
- GST_TIME_ARGS (time));
-
- /* now configure the values */
- gst_segment_set_newsegment_full (&dec->segment, update,
- rate, arate, format, start, stop, time);
- dec->seqnum = gst_event_get_seqnum (event);
-
- if (dec->initialized)
- /* and forward */
- ret = gst_pad_push_event (dec->srcpad, event);
- else {
- /* store it to send once we're initialized */
- dec->pendingevents = g_list_append (dec->pendingevents, event);
- ret = TRUE;
- }
- break;
- }
- case GST_EVENT_TAG:
- {
- if (dec->initialized)
- /* and forward */
- ret = gst_pad_push_event (dec->srcpad, event);
- else {
- /* store it to send once we're initialized */
- dec->pendingevents = g_list_append (dec->pendingevents, event);
- ret = TRUE;
- }
- break;
- }
- default:
- ret = gst_pad_push_event (dec->srcpad, event);
- break;
- }
-done:
- gst_object_unref (dec);
-
- return ret;
-
- /* ERRORS */
-newseg_wrong_format:
- {
- GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
- goto done;
- }
-}
-
-static GstFlowReturn
-vorbis_handle_identification_packet (GstVorbisDec * vd)
-{
- GstCaps *caps;
- const GstAudioChannelPosition *pos = NULL;
-
- switch (vd->vi.channels) {
- case 1:
- case 2:
- /* nothing */
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- pos = gst_vorbis_channel_positions[vd->vi.channels - 1];
- break;
- default:{
- gint i;
- GstAudioChannelPosition *posn =
- g_new (GstAudioChannelPosition, vd->vi.channels);
-
- GST_ELEMENT_WARNING (GST_ELEMENT (vd), STREAM, DECODE,
- (NULL), ("Using NONE channel layout for more than 8 channels"));
-
- for (i = 0; i < vd->vi.channels; i++)
- posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
-
- pos = posn;
- }
- }
-
- caps = gst_caps_new_simple ("audio/x-raw-float",
- "rate", G_TYPE_INT, vd->vi.rate,
- "channels", G_TYPE_INT, vd->vi.channels,
- "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
-
- if (pos) {
- gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos);
- }
-
- if (vd->vi.channels > 8) {
- g_free ((GstAudioChannelPosition *) pos);
- }
-
- gst_pad_set_caps (vd->srcpad, caps);
- gst_caps_unref (caps);
-
- return GST_FLOW_OK;
-}
-
-static GstFlowReturn
-vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet)
-{
- guint bitrate = 0;
- gchar *encoder = NULL;
- GstTagList *list, *old_list;
- GstBuffer *buf;
-
- GST_DEBUG_OBJECT (vd, "parsing comment packet");
-
- buf = gst_buffer_new ();
- GST_BUFFER_DATA (buf) = packet->packet;
- GST_BUFFER_SIZE (buf) = packet->bytes;
-
- list =
- gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\003vorbis", 7,
- &encoder);
-
- old_list = vd->taglist;
- vd->taglist = gst_tag_list_merge (vd->taglist, list, GST_TAG_MERGE_REPLACE);
-
- if (old_list)
- gst_tag_list_free (old_list);
- gst_tag_list_free (list);
- gst_buffer_unref (buf);
-
- if (!vd->taglist) {
- GST_ERROR_OBJECT (vd, "couldn't decode comments");
- vd->taglist = gst_tag_list_new ();
- }
- if (encoder) {
- if (encoder[0])
- gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_ENCODER, encoder, NULL);
- g_free (encoder);
- }
- gst_tag_list_add (vd->taglist, 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 (vd->taglist, 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 (vd->taglist, 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 (vd->taglist, 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 (vd->taglist, GST_TAG_MERGE_REPLACE,
- GST_TAG_BITRATE, (guint) bitrate, NULL);
- }
-
- if (vd->initialized) {
- gst_element_found_tags_for_pad (GST_ELEMENT_CAST (vd), vd->srcpad,
- vd->taglist);
- vd->taglist = NULL;
- } else {
- /* Only post them as messages for the time being. *
- * They will be pushed on the pad once the decoder is initialized */
- gst_element_post_message (GST_ELEMENT_CAST (vd),
- gst_message_new_tag (GST_OBJECT (vd), gst_tag_list_copy (vd->taglist)));
- }
-
- return GST_FLOW_OK;
-}
-
-static GstFlowReturn
-vorbis_handle_type_packet (GstVorbisDec * vd)
-{
- GList *walk;
- gint res;
-
- g_assert (vd->initialized == FALSE);
-
- 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;
-
- vd->initialized = TRUE;
-
- if (vd->pendingevents) {
- for (walk = vd->pendingevents; walk; walk = g_list_next (walk))
- gst_pad_push_event (vd->srcpad, GST_EVENT_CAST (walk->data));
- g_list_free (vd->pendingevents);
- vd->pendingevents = NULL;
- }
-
- if (vd->taglist) {
- /* The tags have already been sent on the bus as messages. */
- gst_pad_push_event (vd->srcpad, gst_event_new_tag (vd->taglist));
- vd->taglist = NULL;
- }
- 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 = (packet->packet[0] == 0x1) ? 1 : 0;
-
- if ((ret = vorbis_synthesis_headerin (&vd->vi, &vd->vc, packet)))
- goto header_read_error;
-
- switch (packet->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;
- }
-}
-
-/* These samples can be outside of the float -1.0 -- 1.0 range, this
- * is allowed, downstream elements are supposed to clip */
-static void
-copy_samples (float *out, float **in, guint samples, gint channels)
-{
- gint i, j;
-
-#ifdef GST_VORBIS_DEC_SEQUENTIAL
- for (i = 0; i < channels; i++) {
- memcpy (out, in[i], samples * sizeof (float));
- out += samples;
- }
-#else
- for (j = 0; j < samples; j++) {
- for (i = 0; i < channels; i++) {
- *out++ = in[i][j];
- }
- }
-#endif
-}
-
-static GstFlowReturn
-vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf)
-{
- GstFlowReturn result;
-
- /* clip */
- if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate,
- dec->vi.channels * sizeof (float)))) {
- GST_LOG_OBJECT (dec, "clipped buffer");
- return GST_FLOW_OK;
- }
-
- if (dec->discont) {
- GST_LOG_OBJECT (dec, "setting DISCONT");
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
- dec->discont = FALSE;
- }
-
- GST_DEBUG_OBJECT (dec,
- "pushing time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
- result = gst_pad_push (dec->srcpad, buf);
-
- return result;
-}
-
-static GstFlowReturn
-vorbis_dec_push_reverse (GstVorbisDec * dec, GstBuffer * buf)
-{
- GstFlowReturn result = GST_FLOW_OK;
-
- dec->queued = g_list_prepend (dec->queued, buf);
-
- return result;
-}
-
-static void
-vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse,
- GstClockTime timestamp, GstClockTime duration)
-{
- /* interpolate reverse */
- if (vd->last_timestamp != -1 && reverse)
- vd->last_timestamp -= duration;
-
- /* take buffer timestamp, use interpolated timestamp otherwise */
- if (timestamp != -1)
- vd->last_timestamp = timestamp;
- else
- timestamp = vd->last_timestamp;
-
- /* interpolate forwards */
- if (vd->last_timestamp != -1 && !reverse)
- vd->last_timestamp += duration;
-
- GST_BUFFER_TIMESTAMP (buf) = timestamp;
- GST_BUFFER_DURATION (buf) = duration;
-}
-
-static GstFlowReturn
-vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet,
- GstClockTime timestamp, GstClockTime duration)
-{
- float **pcm;
- guint sample_count;
- GstBuffer *out;
- GstFlowReturn result;
- gint size;
-
- if (G_UNLIKELY (!vd->initialized))
- 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 carefull not to
- * throw away too much. For now we decode everything and clip right
- * before pushing data. */
- 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;
-
- /* assume all goes well here */
- result = GST_FLOW_OK;
-
- /* count samples ready for reading */
- if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0)
- goto done;
-
- size = sample_count * vd->vi.channels * sizeof (float);
- GST_LOG_OBJECT (vd, "%d samples ready for reading, size %d", sample_count,
- size);
-
- /* alloc buffer for it */
- result =
- gst_pad_alloc_buffer_and_set_caps (vd->srcpad, GST_BUFFER_OFFSET_NONE,
- size, GST_PAD_CAPS (vd->srcpad), &out);
- if (G_UNLIKELY (result != GST_FLOW_OK))
- goto done;
-
- /* get samples ready for reading now, should be sample_count */
- if (G_UNLIKELY ((vorbis_synthesis_pcmout (&vd->vd, &pcm)) != sample_count))
- goto wrong_samples;
-
- /* copy samples in buffer */
- copy_samples ((float *) GST_BUFFER_DATA (out), pcm, sample_count,
- vd->vi.channels);
-
- GST_LOG_OBJECT (vd, "setting output size to %d", size);
- GST_BUFFER_SIZE (out) = size;
-
- /* this should not overflow */
- if (duration == -1)
- duration = sample_count * GST_SECOND / vd->vi.rate;
-
- vorbis_do_timestamps (vd, out, FALSE, timestamp, duration);
-
- if (vd->segment.rate >= 0.0)
- result = vorbis_dec_push_forward (vd, out);
- else
- result = vorbis_dec_push_reverse (vd, out);
-
-done:
- vorbis_synthesis_read (&vd->vd, sample_count);
-
- return result;
-
- /* ERRORS */
-not_initialized:
- {
- GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE,
- (NULL), ("no header sent yet"));
- return GST_FLOW_ERROR;
- }
-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
-vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer)
-{
- ogg_packet packet;
- GstFlowReturn result = GST_FLOW_OK;
-
- /* make ogg_packet out of the buffer */
- packet.packet = GST_BUFFER_DATA (buffer);
- packet.bytes = GST_BUFFER_SIZE (buffer);
- 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 (packet.packet[0] & 1) {
- if (vd->initialized) {
- GST_WARNING_OBJECT (vd, "Already initialized, so ignoring header packet");
- goto done;
- }
- result = vorbis_handle_header_packet (vd, &packet);
- } else {
- GstClockTime timestamp, duration;
-
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- duration = GST_BUFFER_DURATION (buffer);
-
- result = vorbis_handle_data_packet (vd, &packet, timestamp, duration);
- }
-
-done:
- 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;
- vd->discont = TRUE;
- goto done;
- }
-}
-
-/*
- * Input:
- * Buffer decoding order: 7 8 9 4 5 6 3 1 2 EOS
- * Discont flag: D D D D
- *
- * - Each Discont marks a discont in the decoding order.
- *
- * for vorbis, each buffer is a keyframe when we have the previous
- * buffer. This means that to decode buffer 7, we need buffer 6, which
- * arrives out of order.
- *
- * we first gather buffers in the gather queue until we get a DISCONT. We
- * prepend each incomming buffer so that they are in reversed order.
- *
- * gather queue: 9 8 7
- * decode queue:
- * output queue:
- *
- * When a DISCONT is received (buffer 4), we move the gather queue to the
- * decode queue. This is simply done be taking the head of the gather queue
- * and prepending it to the decode queue. This yields:
- *
- * gather queue:
- * decode queue: 7 8 9
- * output queue:
- *
- * Then we decode each buffer in the decode queue in order and put the output
- * buffer in the output queue. The first buffer (7) will not produce any output
- * because it needs the previous buffer (6) which did not arrive yet. This
- * yields:
- *
- * gather queue:
- * decode queue: 7 8 9
- * output queue: 9 8
- *
- * Then we remove the consumed buffers from the decode queue. Buffer 7 is not
- * completely consumed, we need to keep it around for when we receive buffer
- * 6. This yields:
- *
- * gather queue:
- * decode queue: 7
- * output queue: 9 8
- *
- * Then we accumulate more buffers:
- *
- * gather queue: 6 5 4
- * decode queue: 7
- * output queue:
- *
- * prepending to the decode queue on DISCONT yields:
- *
- * gather queue:
- * decode queue: 4 5 6 7
- * output queue:
- *
- * after decoding and keeping buffer 4:
- *
- * gather queue:
- * decode queue: 4
- * output queue: 7 6 5
- *
- * Etc..
- */
-static GstFlowReturn
-vorbis_dec_flush_decode (GstVorbisDec * dec)
-{
- GstFlowReturn res = GST_FLOW_OK;
- GList *walk;
-
- walk = dec->decode;
-
- GST_DEBUG_OBJECT (dec, "flushing buffers to decoder");
-
- while (walk) {
- GList *next;
- GstBuffer *buf = GST_BUFFER_CAST (walk->data);
-
- GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT,
- buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
-
- next = g_list_next (walk);
-
- /* decode buffer, prepend to output queue */
- res = vorbis_dec_decode_buffer (dec, buf);
-
- /* if we generated output, we can discard the buffer, else we
- * keep it in the queue */
- if (dec->queued) {
- GST_DEBUG_OBJECT (dec, "decoded buffer to %p", dec->queued->data);
- dec->decode = g_list_delete_link (dec->decode, walk);
- gst_buffer_unref (buf);
- } else {
- GST_DEBUG_OBJECT (dec, "buffer did not decode, keeping");
- }
- walk = next;
- }
- while (dec->queued) {
- GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data);
- GstClockTime timestamp, duration;
-
- timestamp = GST_BUFFER_TIMESTAMP (buf);
- duration = GST_BUFFER_DURATION (buf);
-
- vorbis_do_timestamps (dec, buf, TRUE, timestamp, duration);
- res = vorbis_dec_push_forward (dec, buf);
-
- dec->queued = g_list_delete_link (dec->queued, dec->queued);
- }
- return res;
-}
-
-static GstFlowReturn
-vorbis_dec_chain_reverse (GstVorbisDec * vd, gboolean discont, GstBuffer * buf)
-{
- GstFlowReturn result = GST_FLOW_OK;
-
- /* if we have a discont, move buffers to the decode list */
- if (G_UNLIKELY (discont)) {
- GST_DEBUG_OBJECT (vd, "received discont");
- while (vd->gather) {
- GstBuffer *gbuf;
-
- gbuf = GST_BUFFER_CAST (vd->gather->data);
- /* remove from the gather list */
- vd->gather = g_list_delete_link (vd->gather, vd->gather);
- /* copy to decode queue */
- vd->decode = g_list_prepend (vd->decode, gbuf);
- }
- /* flush and decode the decode queue */
- result = vorbis_dec_flush_decode (vd);
- }
-
- GST_DEBUG_OBJECT (vd, "gathering buffer %p of size %u, time %" GST_TIME_FORMAT
- ", dur %" GST_TIME_FORMAT, buf, GST_BUFFER_SIZE (buf),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
- /* add buffer to gather queue */
- vd->gather = g_list_prepend (vd->gather, buf);
-
- return result;
-}
-
-static GstFlowReturn
-vorbis_dec_chain_forward (GstVorbisDec * vd, gboolean discont,
- GstBuffer * buffer)
-{
- GstFlowReturn result;
-
- result = vorbis_dec_decode_buffer (vd, buffer);
-
- gst_buffer_unref (buffer);
-
- return result;
-}
-
-static GstFlowReturn
-vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstVorbisDec *vd;
- GstFlowReturn result = GST_FLOW_OK;
- gboolean discont;
-
- vd = GST_VORBIS_DEC (gst_pad_get_parent (pad));
-
- discont = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT);
-
- /* resync on DISCONT */
- if (G_UNLIKELY (discont)) {
- GST_DEBUG_OBJECT (vd, "received DISCONT buffer");
- vd->last_timestamp = GST_CLOCK_TIME_NONE;
-#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
- vorbis_synthesis_restart (&vd->vd);
-#endif
- vd->discont = TRUE;
- }
-
- if (vd->segment.rate >= 0.0)
- result = vorbis_dec_chain_forward (vd, discont, buffer);
- else
- result = vorbis_dec_chain_reverse (vd, discont, buffer);
-
- gst_object_unref (vd);
-
- return result;
-}
-
-static GstStateChangeReturn
-vorbis_dec_change_state (GstElement * element, GstStateChange transition)
-{
- GstVorbisDec *vd = GST_VORBIS_DEC (element);
- GstStateChangeReturn res;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- vorbis_info_init (&vd->vi);
- vorbis_comment_init (&vd->vc);
- vd->initialized = FALSE;
- gst_vorbis_dec_reset (vd);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- default:
- break;
- }
-
- res = parent_class->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_DEBUG_OBJECT (vd, "PAUSED -> READY, clearing vorbis structures");
- vd->initialized = FALSE;
- vorbis_block_clear (&vd->vb);
- vorbis_dsp_clear (&vd->vd);
- vorbis_comment_clear (&vd->vc);
- vorbis_info_clear (&vd->vi);
- gst_vorbis_dec_reset (vd);
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
-
- return res;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- *
- * 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.
- */
-
-/**
- * SECTION:element-vorbisenc
- * @see_also: vorbisdec, oggmux
- *
- * This element encodes raw float audio into a Vorbis stream.
- * <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>.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! vorbisenc ! oggmux ! filesink location=sine.ogg
- * ]| Encode a test sine signal to Ogg/Vorbis. Note that the resulting file
- * will be really small because a sine signal compresses very well.
- * |[
- * gst-launch -v alsasrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg
- * ]| Record from a sound card using ALSA and encode to Ogg/Vorbis.
- * </refsect2>
- *
- * Last reviewed on 2006-03-01 (0.10.4)
- */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <vorbis/vorbisenc.h>
-
-#include <gst/gsttagsetter.h>
-#include <gst/tag/tag.h>
-#include <gst/audio/multichannel.h>
-#include <gst/audio/audio.h>
-#include "gstvorbisenc.h"
-
-#include "gstvorbiscommon.h"
-
-GST_DEBUG_CATEGORY_EXTERN (vorbisenc_debug);
-#define GST_CAT_DEFAULT vorbisenc_debug
-
-static GstStaticPadTemplate vorbis_enc_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-float, "
- "rate = (int) [ 1, 200000 ], "
- "channels = (int) [ 1, 256 ], " "endianness = (int) BYTE_ORDER, "
- "width = (int) 32")
- );
-
-static GstStaticPadTemplate vorbis_enc_src_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-vorbis")
- );
-
-
-/* elementfactory information */
-static const GstElementDetails vorbisenc_details =
-GST_ELEMENT_DETAILS ("Vorbis audio encoder",
- "Codec/Encoder/Audio",
- "Encodes audio in Vorbis format",
- "Monty <monty@xiph.org>, " "Wim Taymans <wim@fluendo.com>");
-
-enum
-{
- ARG_0,
- ARG_MAX_BITRATE,
- ARG_BITRATE,
- ARG_MIN_BITRATE,
- ARG_QUALITY,
- ARG_MANAGED,
- ARG_LAST_MESSAGE
-};
-
-static GstFlowReturn gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc);
-
-/* this function takes into account the granulepos_offset and the subgranule
- * time offset */
-static GstClockTime
-granulepos_to_timestamp_offset (GstVorbisEnc * vorbisenc,
- ogg_int64_t granulepos)
-{
- if (granulepos >= 0)
- return gst_util_uint64_scale ((guint64) granulepos
- + vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency)
- + vorbisenc->subgranule_offset;
- return GST_CLOCK_TIME_NONE;
-}
-
-/* this function does a straight granulepos -> timestamp conversion */
-static GstClockTime
-granulepos_to_timestamp (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
-{
- if (granulepos >= 0)
- return gst_util_uint64_scale ((guint64) granulepos,
- GST_SECOND, vorbisenc->frequency);
- return GST_CLOCK_TIME_NONE;
-}
-
-#define MAX_BITRATE_DEFAULT -1
-#define BITRATE_DEFAULT -1
-#define MIN_BITRATE_DEFAULT -1
-#define QUALITY_DEFAULT 0.3
-#define LOWEST_BITRATE 6000 /* lowest allowed for a 8 kHz stream */
-#define HIGHEST_BITRATE 250001 /* highest allowed for a 44 kHz stream */
-
-static gboolean gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event);
-static GstFlowReturn gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer);
-static gboolean gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc);
-
-static void gst_vorbis_enc_dispose (GObject * object);
-static void gst_vorbis_enc_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-static void gst_vorbis_enc_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static GstStateChangeReturn gst_vorbis_enc_change_state (GstElement * element,
- GstStateChange transition);
-static void gst_vorbis_enc_add_interfaces (GType vorbisenc_type);
-
-GST_BOILERPLATE_FULL (GstVorbisEnc, gst_vorbis_enc, GstElement,
- GST_TYPE_ELEMENT, gst_vorbis_enc_add_interfaces);
-
-static void
-gst_vorbis_enc_add_interfaces (GType vorbisenc_type)
-{
- static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
- static const GInterfaceInfo preset_info = { NULL, NULL, NULL };
-
- g_type_add_interface_static (vorbisenc_type, GST_TYPE_TAG_SETTER,
- &tag_setter_info);
- g_type_add_interface_static (vorbisenc_type, GST_TYPE_PRESET, &preset_info);
-}
-
-static void
-gst_vorbis_enc_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- GstPadTemplate *src_template, *sink_template;
-
-
- src_template = gst_static_pad_template_get (&vorbis_enc_src_factory);
- gst_element_class_add_pad_template (element_class, src_template);
-
- sink_template = gst_static_pad_template_get (&vorbis_enc_sink_factory);
- gst_element_class_add_pad_template (element_class, sink_template);
- gst_element_class_set_details (element_class, &vorbisenc_details);
-}
-
-static void
-gst_vorbis_enc_class_init (GstVorbisEncClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- gobject_class->set_property = gst_vorbis_enc_set_property;
- gobject_class->get_property = gst_vorbis_enc_get_property;
- gobject_class->dispose = gst_vorbis_enc_dispose;
-
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BITRATE,
- g_param_spec_int ("max-bitrate", "Maximum Bitrate",
- "Specify a maximum bitrate (in bps). Useful for streaming "
- "applications. (-1 == disabled)",
- -1, HIGHEST_BITRATE, MAX_BITRATE_DEFAULT,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
- g_param_spec_int ("bitrate", "Target Bitrate",
- "Attempt to encode at a bitrate averaging this (in bps). "
- "This uses the bitrate management engine, and is not recommended for most users. "
- "Quality is a better alternative. (-1 == disabled)", -1,
- HIGHEST_BITRATE, BITRATE_DEFAULT,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MIN_BITRATE,
- g_param_spec_int ("min-bitrate", "Minimum Bitrate",
- "Specify a minimum bitrate (in bps). Useful for encoding for a "
- "fixed-size channel. (-1 == disabled)", -1, HIGHEST_BITRATE,
- MIN_BITRATE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
- g_param_spec_float ("quality", "Quality",
- "Specify quality instead of specifying a particular bitrate.", -0.1,
- 1.0, QUALITY_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MANAGED,
- g_param_spec_boolean ("managed", "Managed",
- "Enable bitrate management engine", FALSE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
- g_param_spec_string ("last-message", "last-message",
- "The last status message", NULL,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_change_state);
-}
-
-static void
-gst_vorbis_enc_dispose (GObject * object)
-{
- GstVorbisEnc *vorbisenc = GST_VORBISENC (object);
-
- if (vorbisenc->sinkcaps) {
- gst_caps_unref (vorbisenc->sinkcaps);
- vorbisenc->sinkcaps = NULL;
- }
-
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static GstCaps *
-gst_vorbis_enc_generate_sink_caps (void)
-{
- GstCaps *caps = gst_caps_new_empty ();
- int i, c;
-
- gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float",
- "rate", GST_TYPE_INT_RANGE, 1, 200000,
- "channels", G_TYPE_INT, 1,
- "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32,
- NULL));
-
- gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float",
- "rate", GST_TYPE_INT_RANGE, 1, 200000,
- "channels", G_TYPE_INT, 2,
- "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32,
- NULL));
-
- for (i = 3; i <= 8; i++) {
- GValue chanpos = { 0 };
- GValue pos = { 0 };
- GstStructure *structure;
-
- g_value_init (&chanpos, GST_TYPE_ARRAY);
- g_value_init (&pos, GST_TYPE_AUDIO_CHANNEL_POSITION);
-
- for (c = 0; c < i; c++) {
- g_value_set_enum (&pos, gst_vorbis_channel_positions[i - 1][c]);
- gst_value_array_append_value (&chanpos, &pos);
- }
- g_value_unset (&pos);
-
- structure = gst_structure_new ("audio/x-raw-float",
- "rate", GST_TYPE_INT_RANGE, 1, 200000,
- "channels", G_TYPE_INT, i,
- "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32, NULL);
- gst_structure_set_value (structure, "channel-positions", &chanpos);
- g_value_unset (&chanpos);
-
- gst_caps_append_structure (caps, structure);
- }
-
- gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw-float",
- "rate", GST_TYPE_INT_RANGE, 1, 200000,
- "channels", GST_TYPE_INT_RANGE, 9, 256,
- "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 32,
- NULL));
-
- return caps;
-}
-
-static GstCaps *
-gst_vorbis_enc_sink_getcaps (GstPad * pad)
-{
- GstVorbisEnc *vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
-
- if (vorbisenc->sinkcaps == NULL)
- vorbisenc->sinkcaps = gst_vorbis_enc_generate_sink_caps ();
-
- return gst_caps_ref (vorbisenc->sinkcaps);
-}
-
-static gboolean
-gst_vorbis_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstVorbisEnc *vorbisenc;
- GstStructure *structure;
-
- vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
- vorbisenc->setup = FALSE;
-
- structure = gst_caps_get_structure (caps, 0);
- gst_structure_get_int (structure, "channels", &vorbisenc->channels);
- gst_structure_get_int (structure, "rate", &vorbisenc->frequency);
-
- gst_vorbis_enc_setup (vorbisenc);
-
- if (vorbisenc->setup)
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-gst_vorbis_enc_convert_src (GstPad * pad, GstFormat src_format,
- gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
-{
- gboolean res = TRUE;
- GstVorbisEnc *vorbisenc;
- gint64 avg;
-
- vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
-
- if (vorbisenc->samples_in == 0 ||
- vorbisenc->bytes_out == 0 || vorbisenc->frequency == 0) {
- gst_object_unref (vorbisenc);
- return FALSE;
- }
-
- avg = (vorbisenc->bytes_out * vorbisenc->frequency) / (vorbisenc->samples_in);
-
- switch (src_format) {
- case GST_FORMAT_BYTES:
- switch (*dest_format) {
- case GST_FORMAT_TIME:
- *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, avg);
- break;
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_TIME:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- *dest_value = gst_util_uint64_scale_int (src_value, avg, GST_SECOND);
- break;
- default:
- res = FALSE;
- }
- break;
- default:
- res = FALSE;
- }
- gst_object_unref (vorbisenc);
- return res;
-}
-
-static gboolean
-gst_vorbis_enc_convert_sink (GstPad * pad, GstFormat src_format,
- gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
-{
- gboolean res = TRUE;
- guint scale = 1;
- gint bytes_per_sample;
- GstVorbisEnc *vorbisenc;
-
- vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
-
- bytes_per_sample = vorbisenc->channels * 2;
-
- switch (src_format) {
- case GST_FORMAT_BYTES:
- switch (*dest_format) {
- case GST_FORMAT_DEFAULT:
- if (bytes_per_sample == 0)
- return FALSE;
- *dest_value = src_value / bytes_per_sample;
- break;
- case GST_FORMAT_TIME:
- {
- gint byterate = bytes_per_sample * vorbisenc->frequency;
-
- if (byterate == 0)
- return FALSE;
- *dest_value =
- gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
- break;
- }
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_DEFAULT:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- *dest_value = src_value * bytes_per_sample;
- break;
- case GST_FORMAT_TIME:
- if (vorbisenc->frequency == 0)
- return FALSE;
- *dest_value =
- gst_util_uint64_scale_int (src_value, GST_SECOND,
- vorbisenc->frequency);
- break;
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_TIME:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- scale = bytes_per_sample;
- /* fallthrough */
- case GST_FORMAT_DEFAULT:
- *dest_value =
- gst_util_uint64_scale_int (src_value,
- scale * vorbisenc->frequency, GST_SECOND);
- break;
- default:
- res = FALSE;
- }
- break;
- default:
- res = FALSE;
- }
- gst_object_unref (vorbisenc);
- return res;
-}
-
-static gint64
-gst_vorbis_enc_get_latency (GstVorbisEnc * vorbisenc)
-{
- /* FIXME, this probably depends on the bitrate and other setting but for now
- * we return this value, which was obtained by totally unscientific
- * measurements */
- return 58 * GST_MSECOND;
-}
-
-static const GstQueryType *
-gst_vorbis_enc_get_query_types (GstPad * pad)
-{
- static const GstQueryType gst_vorbis_enc_src_query_types[] = {
- GST_QUERY_POSITION,
- GST_QUERY_DURATION,
- GST_QUERY_CONVERT,
- 0
- };
-
- return gst_vorbis_enc_src_query_types;
-}
-
-static gboolean
-gst_vorbis_enc_src_query (GstPad * pad, GstQuery * query)
-{
- gboolean res = TRUE;
- GstVorbisEnc *vorbisenc;
- GstPad *peerpad;
-
- vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad));
- peerpad = gst_pad_get_peer (GST_PAD (vorbisenc->sinkpad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_POSITION:
- {
- GstFormat fmt, req_fmt;
- gint64 pos, val;
-
- gst_query_parse_position (query, &req_fmt, NULL);
- if ((res = gst_pad_query_position (peerpad, &req_fmt, &val))) {
- gst_query_set_position (query, req_fmt, val);
- break;
- }
-
- fmt = GST_FORMAT_TIME;
- if (!(res = gst_pad_query_position (peerpad, &fmt, &pos)))
- break;
-
- if ((res = gst_pad_query_convert (peerpad, fmt, pos, &req_fmt, &val))) {
- gst_query_set_position (query, req_fmt, val);
- }
- break;
- }
- case GST_QUERY_DURATION:
- {
- GstFormat fmt, req_fmt;
- gint64 dur, val;
-
- gst_query_parse_duration (query, &req_fmt, NULL);
- if ((res = gst_pad_query_duration (peerpad, &req_fmt, &val))) {
- gst_query_set_duration (query, req_fmt, val);
- break;
- }
-
- fmt = GST_FORMAT_TIME;
- if (!(res = gst_pad_query_duration (peerpad, &fmt, &dur)))
- break;
-
- if ((res = gst_pad_query_convert (peerpad, fmt, dur, &req_fmt, &val))) {
- gst_query_set_duration (query, req_fmt, val);
- }
- break;
- }
- 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);
- if (!(res =
- gst_vorbis_enc_convert_src (pad, src_fmt, src_val, &dest_fmt,
- &dest_val)))
- goto error;
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- break;
- }
- case GST_QUERY_LATENCY:
- {
- gboolean live;
- GstClockTime min_latency, max_latency;
- gint64 latency;
-
- if ((res = gst_pad_query (peerpad, query))) {
- gst_query_parse_latency (query, &live, &min_latency, &max_latency);
-
- latency = gst_vorbis_enc_get_latency (vorbisenc);
-
- /* add our latency */
- min_latency += latency;
- if (max_latency != -1)
- max_latency += latency;
-
- gst_query_set_latency (query, live, min_latency, max_latency);
- }
- break;
- }
- default:
- res = gst_pad_query (peerpad, query);
- break;
- }
-
-error:
- gst_object_unref (peerpad);
- gst_object_unref (vorbisenc);
- return res;
-}
-
-static gboolean
-gst_vorbis_enc_sink_query (GstPad * pad, GstQuery * query)
-{
- gboolean res = TRUE;
-
- 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);
- if (!(res =
- gst_vorbis_enc_convert_sink (pad, src_fmt, src_val, &dest_fmt,
- &dest_val)))
- goto error;
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
-
-error:
- return res;
-}
-
-static void
-gst_vorbis_enc_init (GstVorbisEnc * vorbisenc, GstVorbisEncClass * klass)
-{
- vorbisenc->sinkpad =
- gst_pad_new_from_static_template (&vorbis_enc_sink_factory, "sink");
- gst_pad_set_event_function (vorbisenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_event));
- gst_pad_set_chain_function (vorbisenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_chain));
- gst_pad_set_setcaps_function (vorbisenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_setcaps));
- gst_pad_set_getcaps_function (vorbisenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_getcaps));
- gst_pad_set_query_function (vorbisenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_query));
- gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad);
-
- vorbisenc->srcpad =
- gst_pad_new_from_static_template (&vorbis_enc_src_factory, "src");
- gst_pad_set_query_function (vorbisenc->srcpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_src_query));
- gst_pad_set_query_type_function (vorbisenc->srcpad,
- GST_DEBUG_FUNCPTR (gst_vorbis_enc_get_query_types));
- gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->srcpad);
-
- vorbisenc->channels = -1;
- vorbisenc->frequency = -1;
-
- vorbisenc->managed = FALSE;
- vorbisenc->max_bitrate = MAX_BITRATE_DEFAULT;
- vorbisenc->bitrate = BITRATE_DEFAULT;
- vorbisenc->min_bitrate = MIN_BITRATE_DEFAULT;
- vorbisenc->quality = QUALITY_DEFAULT;
- vorbisenc->quality_set = FALSE;
- vorbisenc->last_message = NULL;
-}
-
-static void
-gst_vorbis_enc_metadata_set1 (const GstTagList * list, const gchar * tag,
- gpointer vorbisenc)
-{
- GstVorbisEnc *enc = GST_VORBISENC (vorbisenc);
- GList *vc_list, *l;
-
- vc_list = gst_tag_to_vorbis_comments (list, tag);
-
- for (l = vc_list; l != NULL; l = l->next) {
- const gchar *vc_string = (const gchar *) l->data;
- gchar *key = NULL, *val = NULL;
-
- GST_LOG_OBJECT (vorbisenc, "vorbis comment: %s", vc_string);
- if (gst_tag_parse_extended_comment (vc_string, &key, NULL, &val, TRUE)) {
- vorbis_comment_add_tag (&enc->vc, key, val);
- g_free (key);
- g_free (val);
- }
- }
-
- g_list_foreach (vc_list, (GFunc) g_free, NULL);
- g_list_free (vc_list);
-}
-
-static void
-gst_vorbis_enc_set_metadata (GstVorbisEnc * enc)
-{
- GstTagList *merged_tags;
- const GstTagList *user_tags;
-
- vorbis_comment_init (&enc->vc);
-
- user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc));
-
- GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags);
- GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags);
-
- /* gst_tag_list_merge() will handle NULL for either or both lists fine */
- merged_tags = gst_tag_list_merge (user_tags, enc->tags,
- gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc)));
-
- if (merged_tags) {
- GST_DEBUG_OBJECT (enc, "merged tags = %" GST_PTR_FORMAT, merged_tags);
- gst_tag_list_foreach (merged_tags, gst_vorbis_enc_metadata_set1, enc);
- gst_tag_list_free (merged_tags);
- }
-}
-
-static gchar *
-get_constraints_string (GstVorbisEnc * vorbisenc)
-{
- gint min = vorbisenc->min_bitrate;
- gint max = vorbisenc->max_bitrate;
- gchar *result;
-
- if (min > 0 && max > 0)
- result = g_strdup_printf ("(min %d bps, max %d bps)", min, max);
- else if (min > 0)
- result = g_strdup_printf ("(min %d bps, no max)", min);
- else if (max > 0)
- result = g_strdup_printf ("(no min, max %d bps)", max);
- else
- result = g_strdup_printf ("(no min or max)");
-
- return result;
-}
-
-static void
-update_start_message (GstVorbisEnc * vorbisenc)
-{
- gchar *constraints;
-
- g_free (vorbisenc->last_message);
-
- if (vorbisenc->bitrate > 0) {
- if (vorbisenc->managed) {
- constraints = get_constraints_string (vorbisenc);
- vorbisenc->last_message =
- g_strdup_printf ("encoding at average bitrate %d bps %s",
- vorbisenc->bitrate, constraints);
- g_free (constraints);
- } else {
- vorbisenc->last_message =
- g_strdup_printf
- ("encoding at approximate bitrate %d bps (VBR encoding enabled)",
- vorbisenc->bitrate);
- }
- } else {
- if (vorbisenc->quality_set) {
- if (vorbisenc->managed) {
- constraints = get_constraints_string (vorbisenc);
- vorbisenc->last_message =
- g_strdup_printf
- ("encoding at quality level %2.2f using constrained VBR %s",
- vorbisenc->quality, constraints);
- g_free (constraints);
- } else {
- vorbisenc->last_message =
- g_strdup_printf ("encoding at quality level %2.2f",
- vorbisenc->quality);
- }
- } else {
- constraints = get_constraints_string (vorbisenc);
- vorbisenc->last_message =
- g_strdup_printf ("encoding using bitrate management %s", constraints);
- g_free (constraints);
- }
- }
-
- g_object_notify (G_OBJECT (vorbisenc), "last_message");
-}
-
-static gboolean
-gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc)
-{
- vorbisenc->setup = FALSE;
-
- if (vorbisenc->bitrate < 0 && vorbisenc->min_bitrate < 0
- && vorbisenc->max_bitrate < 0) {
- vorbisenc->quality_set = TRUE;
- }
-
- update_start_message (vorbisenc);
-
- /* choose an encoding mode */
- /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
- vorbis_info_init (&vorbisenc->vi);
-
- if (vorbisenc->quality_set) {
- if (vorbis_encode_setup_vbr (&vorbisenc->vi,
- vorbisenc->channels, vorbisenc->frequency,
- vorbisenc->quality) != 0) {
- GST_ERROR_OBJECT (vorbisenc,
- "vorbisenc: initialisation failed: invalid parameters for quality");
- vorbis_info_clear (&vorbisenc->vi);
- return FALSE;
- }
-
- /* do we have optional hard quality restrictions? */
- if (vorbisenc->max_bitrate > 0 || vorbisenc->min_bitrate > 0) {
- struct ovectl_ratemanage_arg ai;
-
- vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_GET, &ai);
-
- ai.bitrate_hard_min = vorbisenc->min_bitrate;
- ai.bitrate_hard_max = vorbisenc->max_bitrate;
- ai.management_active = 1;
-
- vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, &ai);
- }
- } else {
- long min_bitrate, max_bitrate;
-
- min_bitrate = vorbisenc->min_bitrate > 0 ? vorbisenc->min_bitrate : -1;
- max_bitrate = vorbisenc->max_bitrate > 0 ? vorbisenc->max_bitrate : -1;
-
- if (vorbis_encode_setup_managed (&vorbisenc->vi,
- vorbisenc->channels,
- vorbisenc->frequency,
- max_bitrate, vorbisenc->bitrate, min_bitrate) != 0) {
- GST_ERROR_OBJECT (vorbisenc,
- "vorbis_encode_setup_managed "
- "(c %d, rate %d, max br %ld, br %d, min br %ld) failed",
- vorbisenc->channels, vorbisenc->frequency, max_bitrate,
- vorbisenc->bitrate, min_bitrate);
- vorbis_info_clear (&vorbisenc->vi);
- return FALSE;
- }
- }
-
- if (vorbisenc->managed && vorbisenc->bitrate < 0) {
- vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_AVG, NULL);
- } else if (!vorbisenc->managed) {
- /* Turn off management entirely (if it was turned on). */
- vorbis_encode_ctl (&vorbisenc->vi, OV_ECTL_RATEMANAGE_SET, NULL);
- }
- vorbis_encode_setup_init (&vorbisenc->vi);
-
- /* set up the analysis state and auxiliary encoding storage */
- vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi);
- vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb);
-
- vorbisenc->next_ts = 0;
-
- vorbisenc->setup = TRUE;
-
- return TRUE;
-}
-
-static GstFlowReturn
-gst_vorbis_enc_clear (GstVorbisEnc * vorbisenc)
-{
- GstFlowReturn ret = GST_FLOW_OK;
-
- if (vorbisenc->setup) {
- vorbis_analysis_wrote (&vorbisenc->vd, 0);
- ret = gst_vorbis_enc_output_buffers (vorbisenc);
-
- vorbisenc->setup = FALSE;
- }
-
- /* clean up and exit. vorbis_info_clear() must be called last */
- vorbis_block_clear (&vorbisenc->vb);
- vorbis_dsp_clear (&vorbisenc->vd);
- vorbis_info_clear (&vorbisenc->vi);
-
- vorbisenc->header_sent = FALSE;
-
- return ret;
-}
-
-/* prepare a buffer for transmission by passing data through libvorbis */
-static GstBuffer *
-gst_vorbis_enc_buffer_from_packet (GstVorbisEnc * vorbisenc,
- ogg_packet * packet)
-{
- GstBuffer *outbuf;
-
- outbuf = gst_buffer_new_and_alloc (packet->bytes);
- memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
- /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
- * time representation */
- GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos +
- vorbisenc->granulepos_offset;
- GST_BUFFER_OFFSET (outbuf) = granulepos_to_timestamp (vorbisenc,
- GST_BUFFER_OFFSET_END (outbuf));
- GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts;
-
- /* update the next timestamp, taking granulepos_offset and subgranule offset
- * into account */
- vorbisenc->next_ts =
- granulepos_to_timestamp_offset (vorbisenc, packet->granulepos) +
- vorbisenc->initial_ts;
- GST_BUFFER_DURATION (outbuf) =
- vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
-
- if (vorbisenc->next_discont) {
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
- vorbisenc->next_discont = FALSE;
- }
-
- gst_buffer_set_caps (outbuf, vorbisenc->srccaps);
-
- GST_LOG_OBJECT (vorbisenc, "encoded buffer of %d bytes",
- GST_BUFFER_SIZE (outbuf));
- return outbuf;
-}
-
-/* the same as above, but different logic for setting timestamp and granulepos
- * */
-static GstBuffer *
-gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
- ogg_packet * packet)
-{
- GstBuffer *outbuf;
-
- outbuf = gst_buffer_new_and_alloc (packet->bytes);
- memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
- GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
- GST_BUFFER_OFFSET_END (outbuf) = 0;
- GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
-
- gst_buffer_set_caps (outbuf, vorbisenc->srccaps);
-
- GST_DEBUG ("created header packet buffer, %d bytes",
- GST_BUFFER_SIZE (outbuf));
- return outbuf;
-}
-
-/* push out the buffer and do internal bookkeeping */
-static GstFlowReturn
-gst_vorbis_enc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer)
-{
- vorbisenc->bytes_out += GST_BUFFER_SIZE (buffer);
-
- GST_DEBUG_OBJECT (vorbisenc,
- "Pushing buffer with GP %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT,
- GST_BUFFER_OFFSET_END (buffer),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
- return gst_pad_push (vorbisenc->srcpad, buffer);
-}
-
-static GstFlowReturn
-gst_vorbis_enc_push_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet)
-{
- GstBuffer *outbuf;
-
- outbuf = gst_vorbis_enc_buffer_from_packet (vorbisenc, packet);
- return gst_vorbis_enc_push_buffer (vorbisenc, outbuf);
-}
-
-/* Set a copy of these buffers as 'streamheader' on the caps.
- * We need a copy to avoid these buffers ending up with (indirect) refs on
- * themselves
- */
-static GstCaps *
-gst_vorbis_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
- GstBuffer * buf2, GstBuffer * buf3)
-{
- GstBuffer *buf;
- GstStructure *structure;
- GValue array = { 0 };
- GValue value = { 0 };
-
- caps = gst_caps_make_writable (caps);
- structure = gst_caps_get_structure (caps, 0);
-
- /* mark buffers */
- GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
- GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
- GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS);
-
- /* put buffers in a fixed list */
- g_value_init (&array, GST_TYPE_ARRAY);
- g_value_init (&value, GST_TYPE_BUFFER);
- buf = gst_buffer_copy (buf1);
- gst_value_set_buffer (&value, buf);
- gst_buffer_unref (buf);
- gst_value_array_append_value (&array, &value);
- g_value_unset (&value);
- g_value_init (&value, GST_TYPE_BUFFER);
- buf = gst_buffer_copy (buf2);
- gst_value_set_buffer (&value, buf);
- gst_buffer_unref (buf);
- gst_value_array_append_value (&array, &value);
- g_value_unset (&value);
- g_value_init (&value, GST_TYPE_BUFFER);
- buf = gst_buffer_copy (buf3);
- gst_value_set_buffer (&value, buf);
- gst_buffer_unref (buf);
- gst_value_array_append_value (&array, &value);
- gst_structure_set_value (structure, "streamheader", &array);
- g_value_unset (&value);
- g_value_unset (&array);
-
- return caps;
-}
-
-static gboolean
-gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event)
-{
- gboolean res = TRUE;
- GstVorbisEnc *vorbisenc;
-
- vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- /* Tell the library we're at end of stream so that it can handle
- * the last frame and mark end of stream in the output properly */
- GST_DEBUG_OBJECT (vorbisenc, "EOS, clearing state and sending event on");
- gst_vorbis_enc_clear (vorbisenc);
-
- res = gst_pad_push_event (vorbisenc->srcpad, event);
- break;
- case GST_EVENT_TAG:
- if (vorbisenc->tags) {
- GstTagList *list;
-
- gst_event_parse_tag (event, &list);
- gst_tag_list_insert (vorbisenc->tags, list,
- gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (vorbisenc)));
- } else {
- g_assert_not_reached ();
- }
- res = gst_pad_push_event (vorbisenc->srcpad, event);
- break;
- case GST_EVENT_NEWSEGMENT:
- {
- gboolean update;
- gdouble rate, applied_rate;
- GstFormat format;
- gint64 start, stop, position;
-
- gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
- &format, &start, &stop, &position);
- if (format == GST_FORMAT_TIME) {
- gst_segment_set_newsegment (&vorbisenc->segment, update, rate, format,
- start, stop, position);
- if (vorbisenc->initial_ts == GST_CLOCK_TIME_NONE) {
- GST_DEBUG_OBJECT (vorbisenc, "Initial segment %" GST_SEGMENT_FORMAT,
- &vorbisenc->segment);
- vorbisenc->initial_ts = start;
- }
- }
- }
- /* fall through */
- default:
- res = gst_pad_push_event (vorbisenc->srcpad, event);
- break;
- }
- return res;
-}
-
-static gboolean
-gst_vorbis_enc_buffer_check_discontinuous (GstVorbisEnc * vorbisenc,
- GstClockTime timestamp, GstClockTime duration)
-{
- gboolean ret = FALSE;
-
- if (timestamp != GST_CLOCK_TIME_NONE &&
- vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
- timestamp + duration != vorbisenc->expected_ts) {
- /* It turns out that a lot of elements don't generate perfect streams due
- * to rounding errors. So, we permit small errors (< 1/2 a sample) without
- * causing a discont.
- */
- int halfsample = GST_SECOND / vorbisenc->frequency / 2;
-
- if ((GstClockTimeDiff) (timestamp - vorbisenc->expected_ts) > halfsample) {
- GST_DEBUG_OBJECT (vorbisenc, "Expected TS %" GST_TIME_FORMAT
- ", buffer TS %" GST_TIME_FORMAT,
- GST_TIME_ARGS (vorbisenc->expected_ts), GST_TIME_ARGS (timestamp));
- ret = TRUE;
- }
- }
-
- if (timestamp != GST_CLOCK_TIME_NONE && duration != GST_CLOCK_TIME_NONE) {
- vorbisenc->expected_ts = timestamp + duration;
- } else
- vorbisenc->expected_ts = GST_CLOCK_TIME_NONE;
-
- return ret;
-}
-
-static GstFlowReturn
-gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstVorbisEnc *vorbisenc;
- GstFlowReturn ret = GST_FLOW_OK;
- gfloat *data;
- gulong size;
- gulong i, j;
- float **vorbis_buffer;
- GstBuffer *buf1, *buf2, *buf3;
- gboolean first = FALSE;
- GstClockTime timestamp = GST_CLOCK_TIME_NONE;
- GstClockTime running_time = GST_CLOCK_TIME_NONE;
-
- vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
-
- if (!vorbisenc->setup)
- goto not_setup;
-
- buffer = gst_audio_buffer_clip (buffer, &vorbisenc->segment,
- vorbisenc->frequency, 4 * vorbisenc->channels);
- if (buffer == NULL) {
- GST_DEBUG_OBJECT (vorbisenc, "Dropping buffer, out of segment");
- return GST_FLOW_OK;
- }
- running_time =
- gst_segment_to_running_time (&vorbisenc->segment, GST_FORMAT_TIME,
- GST_BUFFER_TIMESTAMP (buffer));
- timestamp = running_time + vorbisenc->initial_ts;
- GST_DEBUG_OBJECT (vorbisenc, "Initial ts is %" GST_TIME_FORMAT,
- GST_TIME_ARGS (vorbisenc->initial_ts));
- if (!vorbisenc->header_sent) {
- /* Vorbis streams begin with three headers; the initial header (with
- most of the codec setup parameters) which is mandated by the Ogg
- bitstream spec. The second header holds any comment fields. The
- third header holds the bitstream codebook. We merely need to
- make the headers, then pass them to libvorbis one at a time;
- libvorbis handles the additional Ogg bitstream constraints */
- ogg_packet header;
- ogg_packet header_comm;
- ogg_packet header_code;
- GstCaps *caps;
-
- /* first, make sure header buffers get timestamp == 0 */
- vorbisenc->next_ts = 0;
- vorbisenc->granulepos_offset = 0;
- vorbisenc->subgranule_offset = 0;
-
- GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets");
- gst_vorbis_enc_set_metadata (vorbisenc);
- vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header,
- &header_comm, &header_code);
- vorbis_comment_clear (&vorbisenc->vc);
-
- /* create header buffers */
- buf1 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header);
- buf2 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_comm);
- buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code);
-
- /* mark and put on caps */
- vorbisenc->srccaps = gst_caps_new_simple ("audio/x-vorbis", NULL);
- caps = vorbisenc->srccaps;
- caps = gst_vorbis_enc_set_header_on_caps (caps, buf1, buf2, buf3);
-
- /* negotiate with these caps */
- GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
- gst_pad_set_caps (vorbisenc->srcpad, caps);
-
- gst_buffer_set_caps (buf1, caps);
- gst_buffer_set_caps (buf2, caps);
- gst_buffer_set_caps (buf3, caps);
-
- /* push out buffers */
- /* push_buffer takes the reference even for failure */
- if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK)
- goto failed_header_push;
- if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) {
- buf2 = NULL;
- goto failed_header_push;
- }
- if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) {
- buf3 = NULL;
- goto failed_header_push;
- }
-
- /* now adjust starting granulepos accordingly if the buffer's timestamp is
- nonzero */
- vorbisenc->next_ts = timestamp;
- vorbisenc->expected_ts = timestamp;
- vorbisenc->granulepos_offset = gst_util_uint64_scale
- (running_time, vorbisenc->frequency, GST_SECOND);
- vorbisenc->subgranule_offset = 0;
- vorbisenc->subgranule_offset =
- (vorbisenc->next_ts - vorbisenc->initial_ts) -
- granulepos_to_timestamp_offset (vorbisenc, 0);
-
- vorbisenc->header_sent = TRUE;
- first = TRUE;
- }
-
- if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
- timestamp < vorbisenc->expected_ts) {
- guint64 diff = vorbisenc->expected_ts - timestamp;
- guint64 diff_bytes;
-
- GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous "
- "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
- "), cannot handle. Clipping buffer.",
- GST_TIME_ARGS (timestamp), GST_TIME_ARGS (vorbisenc->expected_ts));
-
- diff_bytes =
- GST_CLOCK_TIME_TO_FRAMES (diff,
- vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat);
- if (diff_bytes >= GST_BUFFER_SIZE (buffer)) {
- gst_buffer_unref (buffer);
- return GST_FLOW_OK;
- }
- buffer = gst_buffer_make_metadata_writable (buffer);
- GST_BUFFER_DATA (buffer) += diff_bytes;
- GST_BUFFER_SIZE (buffer) -= diff_bytes;
-
- GST_BUFFER_TIMESTAMP (buffer) += diff;
- if (GST_BUFFER_DURATION_IS_VALID (buffer))
- GST_BUFFER_DURATION (buffer) -= diff;
- }
-
- if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, timestamp,
- GST_BUFFER_DURATION (buffer)) && !first) {
- GST_WARNING_OBJECT (vorbisenc,
- "Buffer is discontinuous, flushing encoder "
- "and restarting (Discont from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
- ")", GST_TIME_ARGS (vorbisenc->next_ts), GST_TIME_ARGS (timestamp));
- /* Re-initialise encoder (there's unfortunately no API to flush it) */
- if ((ret = gst_vorbis_enc_clear (vorbisenc)) != GST_FLOW_OK)
- return ret;
- if (!gst_vorbis_enc_setup (vorbisenc))
- return GST_FLOW_ERROR; /* Should be impossible, we can only get here if
- we successfully initialised earlier */
-
- /* Now, set our granulepos offset appropriately. */
- vorbisenc->next_ts = timestamp;
- /* We need to round to the nearest whole number of samples, not just do
- * a truncating division here */
- vorbisenc->granulepos_offset = gst_util_uint64_scale
- (running_time + GST_SECOND / vorbisenc->frequency / 2
- - vorbisenc->subgranule_offset, vorbisenc->frequency, GST_SECOND);
-
- vorbisenc->header_sent = TRUE;
-
- /* And our next output buffer must have DISCONT set on it */
- vorbisenc->next_discont = TRUE;
- }
-
- /* Sending zero samples to libvorbis marks EOS, so we mustn't do that */
- if (GST_BUFFER_SIZE (buffer) == 0) {
- gst_buffer_unref (buffer);
- return GST_FLOW_OK;
- }
-
- /* data to encode */
- data = (gfloat *) GST_BUFFER_DATA (buffer);
- size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float));
-
- /* expose the buffer to submit data */
- vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
-
- /* deinterleave samples, write the buffer data */
- for (i = 0; i < size; i++) {
- for (j = 0; j < vorbisenc->channels; j++) {
- vorbis_buffer[j][i] = *data++;
- }
- }
-
- /* tell the library how much we actually submitted */
- vorbis_analysis_wrote (&vorbisenc->vd, size);
-
- GST_LOG_OBJECT (vorbisenc, "wrote %lu samples to vorbis", size);
-
- vorbisenc->samples_in += size;
-
- gst_buffer_unref (buffer);
-
- ret = gst_vorbis_enc_output_buffers (vorbisenc);
-
- return ret;
-
- /* error cases */
-not_setup:
- {
- gst_buffer_unref (buffer);
- GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL),
- ("encoder not initialized (input is not audio?)"));
- return GST_FLOW_UNEXPECTED;
- }
-failed_header_push:
- {
- GST_WARNING_OBJECT (vorbisenc, "Failed to push headers");
- /* buf1 is always already unreffed */
- if (buf2)
- gst_buffer_unref (buf2);
- if (buf3)
- gst_buffer_unref (buf3);
- gst_buffer_unref (buffer);
- return ret;
- }
-}
-
-static GstFlowReturn
-gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc)
-{
- GstFlowReturn ret;
-
- /* vorbis does some data preanalysis, then divides up blocks for
- more involved (potentially parallel) processing. Get a single
- block for encoding now */
- while (vorbis_analysis_blockout (&vorbisenc->vd, &vorbisenc->vb) == 1) {
- ogg_packet op;
-
- GST_LOG_OBJECT (vorbisenc, "analysed to a block");
-
- /* analysis */
- vorbis_analysis (&vorbisenc->vb, NULL);
- vorbis_bitrate_addblock (&vorbisenc->vb);
-
- while (vorbis_bitrate_flushpacket (&vorbisenc->vd, &op)) {
- GST_LOG_OBJECT (vorbisenc, "pushing out a data packet");
- ret = gst_vorbis_enc_push_packet (vorbisenc, &op);
-
- if (ret != GST_FLOW_OK)
- return ret;
- }
- }
-
- return GST_FLOW_OK;
-}
-
-static void
-gst_vorbis_enc_get_property (GObject * object, guint prop_id, GValue * value,
- GParamSpec * pspec)
-{
- GstVorbisEnc *vorbisenc;
-
- g_return_if_fail (GST_IS_VORBISENC (object));
-
- vorbisenc = GST_VORBISENC (object);
-
- switch (prop_id) {
- case ARG_MAX_BITRATE:
- g_value_set_int (value, vorbisenc->max_bitrate);
- break;
- case ARG_BITRATE:
- g_value_set_int (value, vorbisenc->bitrate);
- break;
- case ARG_MIN_BITRATE:
- g_value_set_int (value, vorbisenc->min_bitrate);
- break;
- case ARG_QUALITY:
- g_value_set_float (value, vorbisenc->quality);
- break;
- case ARG_MANAGED:
- g_value_set_boolean (value, vorbisenc->managed);
- break;
- case ARG_LAST_MESSAGE:
- g_value_set_string (value, vorbisenc->last_message);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_vorbis_enc_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstVorbisEnc *vorbisenc;
-
- g_return_if_fail (GST_IS_VORBISENC (object));
-
- vorbisenc = GST_VORBISENC (object);
-
- switch (prop_id) {
- case ARG_MAX_BITRATE:
- {
- gboolean old_value = vorbisenc->managed;
-
- vorbisenc->max_bitrate = g_value_get_int (value);
- if (vorbisenc->max_bitrate >= 0
- && vorbisenc->max_bitrate < LOWEST_BITRATE) {
- g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE);
- vorbisenc->max_bitrate = LOWEST_BITRATE;
- }
- if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0)
- vorbisenc->managed = TRUE;
- else
- vorbisenc->managed = FALSE;
-
- if (old_value != vorbisenc->managed)
- g_object_notify (object, "managed");
- break;
- }
- case ARG_BITRATE:
- vorbisenc->bitrate = g_value_get_int (value);
- if (vorbisenc->bitrate >= 0 && vorbisenc->bitrate < LOWEST_BITRATE) {
- g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE);
- vorbisenc->bitrate = LOWEST_BITRATE;
- }
- break;
- case ARG_MIN_BITRATE:
- {
- gboolean old_value = vorbisenc->managed;
-
- vorbisenc->min_bitrate = g_value_get_int (value);
- if (vorbisenc->min_bitrate >= 0
- && vorbisenc->min_bitrate < LOWEST_BITRATE) {
- g_warning ("Lowest allowed bitrate is %d", LOWEST_BITRATE);
- vorbisenc->min_bitrate = LOWEST_BITRATE;
- }
- if (vorbisenc->min_bitrate > 0 && vorbisenc->max_bitrate > 0)
- vorbisenc->managed = TRUE;
- else
- vorbisenc->managed = FALSE;
-
- if (old_value != vorbisenc->managed)
- g_object_notify (object, "managed");
- break;
- }
- case ARG_QUALITY:
- vorbisenc->quality = g_value_get_float (value);
- if (vorbisenc->quality >= 0.0)
- vorbisenc->quality_set = TRUE;
- else
- vorbisenc->quality_set = FALSE;
- break;
- case ARG_MANAGED:
- vorbisenc->managed = g_value_get_boolean (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static GstStateChangeReturn
-gst_vorbis_enc_change_state (GstElement * element, GstStateChange transition)
-{
- GstVorbisEnc *vorbisenc = GST_VORBISENC (element);
- GstStateChangeReturn res;
-
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- vorbisenc->tags = gst_tag_list_new ();
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- vorbisenc->setup = FALSE;
- vorbisenc->next_discont = FALSE;
- vorbisenc->header_sent = FALSE;
- gst_segment_init (&vorbisenc->segment, GST_FORMAT_TIME);
- vorbisenc->initial_ts = GST_CLOCK_TIME_NONE;
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- default:
- break;
- }
-
- res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- vorbis_block_clear (&vorbisenc->vb);
- vorbis_dsp_clear (&vorbisenc->vd);
- vorbis_info_clear (&vorbisenc->vi);
- g_free (vorbisenc->last_message);
- vorbisenc->last_message = NULL;
- if (vorbisenc->srccaps) {
- gst_caps_unref (vorbisenc->srccaps);
- vorbisenc->srccaps = NULL;
- }
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- gst_tag_list_free (vorbisenc->tags);
- vorbisenc->tags = NULL;
- default:
- break;
- }
-
- return res;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
- * Copyright (C) 2006 Andy Wingo <wingo@pobox.com>
- *
- * 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.
- */
-
-/**
- * SECTION:element-vorbisparse
- * @see_also: vorbisdec, oggdemux, theoraparse
- *
- * The vorbisparse element will parse the header packets of the Vorbis
- * stream and put them as the streamheader in the caps. This is used in the
- * multifdsink case where you want to stream live vorbis streams to multiple
- * clients, each client has to receive the streamheaders first before they can
- * consume the vorbis packets.
- *
- * This element also makes sure that the buffers that it pushes out are properly
- * timestamped and that their offset and offset_end are set. The buffers that
- * vorbisparse outputs have all of the metadata that oggmux expects to receive,
- * which allows you to (for example) remux an ogg/vorbis file.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisparse ! fakesink
- * ]| This pipeline shows that the streamheader is set in the caps, and that each
- * buffer has the timestamp, duration, offset, and offset_end set.
- * |[
- * gst-launch filesrc location=sine.ogg ! oggdemux ! vorbisparse \
- * ! oggmux ! filesink location=sine-remuxed.ogg
- * ]| This pipeline shows remuxing. sine-remuxed.ogg might not be exactly the same
- * as sine.ogg, but they should produce exactly the same decoded data.
- * </refsect2>
- *
- * Last reviewed on 2006-04-01 (0.10.4.1)
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "gstvorbisparse.h"
-
-GST_DEBUG_CATEGORY_EXTERN (vorbisparse_debug);
-#define GST_CAT_DEFAULT vorbisparse_debug
-
-static const GstElementDetails vorbis_parse_details = {
- "VorbisParse",
- "Codec/Parser/Audio",
- "parse raw vorbis streams",
- "Thomas Vander Stichele <thomas at apestaart dot org>"
-};
-
-static GstStaticPadTemplate vorbis_parse_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-vorbis")
- );
-
-static GstStaticPadTemplate vorbis_parse_src_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-vorbis")
- );
-
-GST_BOILERPLATE (GstVorbisParse, gst_vorbis_parse, GstElement,
- GST_TYPE_ELEMENT);
-
-static GstFlowReturn vorbis_parse_chain (GstPad * pad, GstBuffer * buffer);
-static GstStateChangeReturn vorbis_parse_change_state (GstElement * element,
- GstStateChange transition);
-static gboolean vorbis_parse_sink_event (GstPad * pad, GstEvent * event);
-static gboolean vorbis_parse_src_query (GstPad * pad, GstQuery * query);
-static gboolean vorbis_parse_convert (GstPad * pad,
- GstFormat src_format, gint64 src_value,
- GstFormat * dest_format, gint64 * dest_value);
-static GstFlowReturn vorbis_parse_parse_packet (GstVorbisParse * parse,
- GstBuffer * buf);
-
-static void
-gst_vorbis_parse_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 (&vorbis_parse_src_factory));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&vorbis_parse_sink_factory));
- gst_element_class_set_details (element_class, &vorbis_parse_details);
-}
-
-static void
-gst_vorbis_parse_class_init (GstVorbisParseClass * klass)
-{
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
-
- gstelement_class->change_state = vorbis_parse_change_state;
-
- klass->parse_packet = GST_DEBUG_FUNCPTR (vorbis_parse_parse_packet);
-}
-
-static void
-gst_vorbis_parse_init (GstVorbisParse * parse, GstVorbisParseClass * g_class)
-{
- parse->sinkpad =
- gst_pad_new_from_static_template (&vorbis_parse_sink_factory, "sink");
- gst_pad_set_chain_function (parse->sinkpad,
- GST_DEBUG_FUNCPTR (vorbis_parse_chain));
- gst_pad_set_event_function (parse->sinkpad,
- GST_DEBUG_FUNCPTR (vorbis_parse_sink_event));
- gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
-
- parse->srcpad =
- gst_pad_new_from_static_template (&vorbis_parse_src_factory, "src");
- gst_pad_set_query_function (parse->srcpad,
- GST_DEBUG_FUNCPTR (vorbis_parse_src_query));
- gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
-}
-
-static void
-vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps)
-{
- GstBuffer *buf1, *buf2, *buf3;
- GstStructure *structure;
- GValue array = { 0 };
- GValue value = { 0 };
-
- g_assert (parse);
- g_assert (parse->streamheader);
- g_assert (parse->streamheader->next);
- g_assert (parse->streamheader->next->next);
- buf1 = parse->streamheader->data;
- g_assert (buf1);
- buf2 = parse->streamheader->next->data;
- g_assert (buf2);
- buf3 = parse->streamheader->next->next->data;
- g_assert (buf3);
-
- structure = gst_caps_get_structure (caps, 0);
-
- /* mark buffers */
- GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
- GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
- GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS);
-
- /* put buffers in a fixed list */
- g_value_init (&array, GST_TYPE_ARRAY);
- g_value_init (&value, GST_TYPE_BUFFER);
- gst_value_set_buffer (&value, buf1);
- gst_value_array_append_value (&array, &value);
- g_value_unset (&value);
- g_value_init (&value, GST_TYPE_BUFFER);
- gst_value_set_buffer (&value, buf2);
- gst_value_array_append_value (&array, &value);
- g_value_unset (&value);
- g_value_init (&value, GST_TYPE_BUFFER);
- gst_value_set_buffer (&value, buf3);
- gst_value_array_append_value (&array, &value);
- gst_structure_set_value (structure, "streamheader", &array);
- g_value_unset (&value);
- g_value_unset (&array);
-}
-
-static void
-vorbis_parse_drain_event_queue (GstVorbisParse * parse)
-{
- while (parse->event_queue->length) {
- GstEvent *event;
-
- event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
- gst_pad_event_default (parse->sinkpad, event);
- }
-}
-
-static void
-vorbis_parse_push_headers (GstVorbisParse * parse)
-{
- /* mark and put on caps */
- GstCaps *caps;
- GstBuffer *outbuf, *outbuf1, *outbuf2, *outbuf3;
- ogg_packet packet;
-
- /* get the headers into the caps, passing them to vorbis as we go */
- caps = gst_caps_make_writable (gst_pad_get_caps (parse->srcpad));
- vorbis_parse_set_header_on_caps (parse, caps);
- GST_DEBUG_OBJECT (parse, "here are the caps: %" GST_PTR_FORMAT, caps);
- gst_pad_set_caps (parse->srcpad, caps);
- gst_caps_unref (caps);
-
- outbuf = GST_BUFFER_CAST (parse->streamheader->data);
- packet.packet = GST_BUFFER_DATA (outbuf);
- packet.bytes = GST_BUFFER_SIZE (outbuf);
- packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
- packet.packetno = 1;
- packet.e_o_s = 0;
- packet.b_o_s = 1;
- vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
- parse->sample_rate = parse->vi.rate;
- outbuf1 = outbuf;
-
- outbuf = GST_BUFFER_CAST (parse->streamheader->next->data);
- packet.packet = GST_BUFFER_DATA (outbuf);
- packet.bytes = GST_BUFFER_SIZE (outbuf);
- packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
- packet.packetno = 2;
- packet.e_o_s = 0;
- packet.b_o_s = 0;
- vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
- outbuf2 = outbuf;
-
- outbuf = GST_BUFFER_CAST (parse->streamheader->next->next->data);
- packet.packet = GST_BUFFER_DATA (outbuf);
- packet.bytes = GST_BUFFER_SIZE (outbuf);
- packet.granulepos = GST_BUFFER_OFFSET_END (outbuf);
- packet.packetno = 3;
- packet.e_o_s = 0;
- packet.b_o_s = 0;
- vorbis_synthesis_headerin (&parse->vi, &parse->vc, &packet);
- outbuf3 = outbuf;
-
- /* first process queued events */
- vorbis_parse_drain_event_queue (parse);
-
- /* push out buffers, ignoring return value... */
- gst_buffer_set_caps (outbuf1, GST_PAD_CAPS (parse->srcpad));
- gst_pad_push (parse->srcpad, outbuf1);
- gst_buffer_set_caps (outbuf2, GST_PAD_CAPS (parse->srcpad));
- gst_pad_push (parse->srcpad, outbuf2);
- gst_buffer_set_caps (outbuf3, GST_PAD_CAPS (parse->srcpad));
- gst_pad_push (parse->srcpad, outbuf3);
-
- g_list_free (parse->streamheader);
- parse->streamheader = NULL;
-}
-
-static void
-vorbis_parse_clear_queue (GstVorbisParse * parse)
-{
- while (parse->buffer_queue->length) {
- GstBuffer *buf;
-
- buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
- gst_buffer_unref (buf);
- }
- while (parse->event_queue->length) {
- GstEvent *event;
-
- event = GST_EVENT_CAST (g_queue_pop_head (parse->event_queue));
- gst_event_unref (event);
- }
-}
-
-static GstFlowReturn
-vorbis_parse_push_buffer (GstVorbisParse * parse, GstBuffer * buf,
- gint64 granulepos)
-{
- guint64 samples;
-
- /* our hack as noted below */
- samples = GST_BUFFER_OFFSET (buf);
-
- GST_BUFFER_OFFSET_END (buf) = granulepos;
- GST_BUFFER_DURATION (buf) = samples * GST_SECOND / parse->sample_rate;
- GST_BUFFER_OFFSET (buf) = granulepos * GST_SECOND / parse->sample_rate;
- GST_BUFFER_TIMESTAMP (buf) =
- GST_BUFFER_OFFSET (buf) - GST_BUFFER_DURATION (buf);
-
- gst_buffer_set_caps (buf, GST_PAD_CAPS (parse->srcpad));
-
- return gst_pad_push (parse->srcpad, buf);
-}
-
-static GstFlowReturn
-vorbis_parse_drain_queue_prematurely (GstVorbisParse * parse)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- gint64 granulepos = MAX (parse->prev_granulepos, 0);
-
- /* got an EOS event, make sure to push out any buffers that were in the queue
- * -- won't normally be the case, but this catches the
- * didn't-get-a-granulepos-on-the-last-packet case. Assuming a continuous
- * stream. */
-
- /* if we got EOS before any buffers came, go ahead and push the other events
- * first */
- vorbis_parse_drain_event_queue (parse);
-
- while (!g_queue_is_empty (parse->buffer_queue)) {
- GstBuffer *buf;
-
- buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
-
- granulepos += GST_BUFFER_OFFSET (buf);
- ret = vorbis_parse_push_buffer (parse, buf, granulepos);
-
- if (ret != GST_FLOW_OK)
- goto done;
- }
-
- parse->prev_granulepos = granulepos;
-
-done:
- return ret;
-}
-
-static GstFlowReturn
-vorbis_parse_drain_queue (GstVorbisParse * parse, gint64 granulepos)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- GList *walk;
- gint64 cur = granulepos;
- gint64 gp;
-
- for (walk = parse->buffer_queue->head; walk; walk = walk->next)
- cur -= GST_BUFFER_OFFSET (walk->data);
-
- if (parse->prev_granulepos != -1)
- cur = MAX (cur, parse->prev_granulepos);
-
- while (!g_queue_is_empty (parse->buffer_queue)) {
- GstBuffer *buf;
-
- buf = GST_BUFFER_CAST (g_queue_pop_head (parse->buffer_queue));
-
- cur += GST_BUFFER_OFFSET (buf);
- gp = CLAMP (cur, 0, granulepos);
-
- ret = vorbis_parse_push_buffer (parse, buf, gp);
-
- if (ret != GST_FLOW_OK)
- goto done;
- }
-
- parse->prev_granulepos = granulepos;
-
-done:
- return ret;
-}
-
-static GstFlowReturn
-vorbis_parse_queue_buffer (GstVorbisParse * parse, GstBuffer * buf)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- long blocksize;
- ogg_packet packet;
-
- buf = gst_buffer_make_metadata_writable (buf);
-
- packet.packet = GST_BUFFER_DATA (buf);
- packet.bytes = GST_BUFFER_SIZE (buf);
- packet.granulepos = GST_BUFFER_OFFSET_END (buf);
- packet.packetno = parse->packetno + parse->buffer_queue->length;
- packet.e_o_s = 0;
-
- blocksize = vorbis_packet_blocksize (&parse->vi, &packet);
-
- /* temporarily store the sample count in OFFSET -- we overwrite this later */
-
- if (parse->prev_blocksize < 0)
- GST_BUFFER_OFFSET (buf) = 0;
- else
- GST_BUFFER_OFFSET (buf) = (blocksize + parse->prev_blocksize) / 4;
-
- parse->prev_blocksize = blocksize;
-
- g_queue_push_tail (parse->buffer_queue, buf);
-
- if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
- ret = vorbis_parse_drain_queue (parse, GST_BUFFER_OFFSET_END (buf));
-
- return ret;
-}
-
-static GstFlowReturn
-vorbis_parse_parse_packet (GstVorbisParse * parse, GstBuffer * buf)
-{
- GstFlowReturn ret;
- guint8 *data;
- guint size;
- gboolean have_header;
-
- data = GST_BUFFER_DATA (buf);
- size = GST_BUFFER_SIZE (buf);
-
- parse->packetno++;
-
- have_header = FALSE;
- if (size >= 1) {
- if (data[0] >= 0x01 && data[0] <= 0x05)
- have_header = TRUE;
- }
-
- if (have_header) {
- if (!parse->streamheader_sent) {
- /* we need to collect the headers still */
- /* so put it on the streamheader list and return */
- parse->streamheader = g_list_append (parse->streamheader, buf);
- }
- ret = GST_FLOW_OK;
- } else {
- /* data packet, push the headers we collected before */
- if (!parse->streamheader_sent) {
- vorbis_parse_push_headers (parse);
- parse->streamheader_sent = TRUE;
- }
- ret = vorbis_parse_queue_buffer (parse, buf);
- }
-
- return ret;
-}
-
-static GstFlowReturn
-vorbis_parse_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstVorbisParseClass *klass;
- GstVorbisParse *parse;
-
- parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
- klass = GST_VORBIS_PARSE_CLASS (G_OBJECT_GET_CLASS (parse));
-
- g_assert (klass->parse_packet != NULL);
-
- return klass->parse_packet (parse, buffer);
-}
-
-static gboolean
-vorbis_parse_queue_event (GstVorbisParse * parse, GstEvent * event)
-{
- GstFlowReturn ret = TRUE;
-
- g_queue_push_tail (parse->event_queue, event);
-
- return ret;
-}
-
-static gboolean
-vorbis_parse_sink_event (GstPad * pad, GstEvent * event)
-{
- gboolean ret;
- GstVorbisParse *parse;
-
- parse = GST_VORBIS_PARSE (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_START:
- vorbis_parse_clear_queue (parse);
- parse->prev_granulepos = -1;
- parse->prev_blocksize = -1;
- ret = gst_pad_event_default (pad, event);
- break;
- case GST_EVENT_EOS:
- vorbis_parse_drain_queue_prematurely (parse);
- ret = gst_pad_event_default (pad, event);
- break;
- default:
- if (!parse->streamheader_sent && GST_EVENT_IS_SERIALIZED (event))
- ret = vorbis_parse_queue_event (parse, event);
- else
- ret = gst_pad_event_default (pad, event);
- break;
- }
-
- gst_object_unref (parse);
-
- return ret;
-}
-
-static gboolean
-vorbis_parse_convert (GstPad * pad,
- GstFormat src_format, gint64 src_value,
- GstFormat * dest_format, gint64 * dest_value)
-{
- gboolean res = TRUE;
- GstVorbisParse *parse;
- guint64 scale = 1;
-
- parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
-
- /* fixme: assumes atomic access to lots of instance variables modified from
- * the streaming thread, including 64-bit variables */
-
- if (parse->packetno < 4)
- return FALSE;
-
- if (src_format == *dest_format) {
- *dest_value = src_value;
- return TRUE;
- }
-
- if (parse->sinkpad == pad &&
- (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
- return FALSE;
-
- switch (src_format) {
- case GST_FORMAT_TIME:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- scale = sizeof (float) * parse->vi.channels;
- case GST_FORMAT_DEFAULT:
- *dest_value =
- scale * gst_util_uint64_scale_int (src_value, parse->vi.rate,
- GST_SECOND);
- break;
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_DEFAULT:
- switch (*dest_format) {
- case GST_FORMAT_BYTES:
- *dest_value = src_value * sizeof (float) * parse->vi.channels;
- break;
- case GST_FORMAT_TIME:
- *dest_value =
- gst_util_uint64_scale_int (src_value, GST_SECOND, parse->vi.rate);
- break;
- default:
- res = FALSE;
- }
- break;
- case GST_FORMAT_BYTES:
- switch (*dest_format) {
- case GST_FORMAT_DEFAULT:
- *dest_value = src_value / (sizeof (float) * parse->vi.channels);
- break;
- case GST_FORMAT_TIME:
- *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
- parse->vi.rate * sizeof (float) * parse->vi.channels);
- break;
- default:
- res = FALSE;
- }
- break;
- default:
- res = FALSE;
- }
-
- return res;
-}
-
-static gboolean
-vorbis_parse_src_query (GstPad * pad, GstQuery * query)
-{
- gint64 granulepos;
- GstVorbisParse *parse;
- gboolean res = FALSE;
-
- parse = GST_VORBIS_PARSE (GST_PAD_PARENT (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_POSITION:
- {
- GstFormat format;
- gint64 value;
-
- granulepos = parse->prev_granulepos;
-
- gst_query_parse_position (query, &format, NULL);
-
- /* and convert to the final format */
- if (!(res =
- vorbis_parse_convert (pad, GST_FORMAT_DEFAULT, granulepos,
- &format, &value)))
- goto error;
-
- /* fixme: support segments
- value = (value - parse->segment_start) + parse->segment_time;
- */
-
- gst_query_set_position (query, format, value);
-
- GST_LOG_OBJECT (parse, "query %p: peer returned granulepos: %"
- G_GUINT64_FORMAT " - we return %" G_GUINT64_FORMAT " (format %u)",
- query, granulepos, value, format);
-
- break;
- }
- case GST_QUERY_DURATION:
- {
- /* fixme: not threadsafe */
- /* query peer for total length */
- if (!gst_pad_is_linked (parse->sinkpad)) {
- GST_WARNING_OBJECT (parse, "sink pad %" GST_PTR_FORMAT " is not linked",
- parse->sinkpad);
- goto error;
- }
- if (!(res = gst_pad_query (GST_PAD_PEER (parse->sinkpad), query)))
- goto error;
- break;
- }
- 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);
- if (!(res =
- vorbis_parse_convert (pad, src_fmt, src_val, &dest_fmt,
- &dest_val)))
- goto error;
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
- return res;
-
-error:
- {
- GST_WARNING_OBJECT (parse, "error handling query");
- return res;
- }
-}
-
-static GstStateChangeReturn
-vorbis_parse_change_state (GstElement * element, GstStateChange transition)
-{
- GstVorbisParse *parse = GST_VORBIS_PARSE (element);
- GstStateChangeReturn ret;
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- vorbis_info_init (&parse->vi);
- vorbis_comment_init (&parse->vc);
- parse->prev_granulepos = -1;
- parse->prev_blocksize = -1;
- parse->packetno = 0;
- parse->streamheader_sent = FALSE;
- parse->buffer_queue = g_queue_new ();
- parse->event_queue = g_queue_new ();
- break;
- default:
- break;
- }
-
- ret = parent_class->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- vorbis_info_clear (&parse->vi);
- vorbis_comment_clear (&parse->vc);
- vorbis_parse_clear_queue (parse);
- g_queue_free (parse->buffer_queue);
- parse->buffer_queue = NULL;
- g_queue_free (parse->event_queue);
- parse->event_queue = NULL;
- break;
- default:
- break;
- }
-
- return ret;
-}
+++ /dev/null
-/*
- * Copyright (C) 2006 James Livingston <doclivingston@gmail.com>
- *
- * 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.
- */
-
-/**
- * SECTION:element-vorbistag
- * @see_also: #oggdemux, #oggmux, #vorbisparse, #GstTagSetter
- *
- * The vorbistags element can change the tag contained within a raw
- * vorbis stream. Specifically, it modifies the comments header packet
- * of the vorbis stream.
- *
- * The element will also process the stream as the #vorbisparse element does
- * so it can be used when remuxing an Ogg Vorbis stream, without additional
- * elements.
- *
- * Applications can set the tags to write using the #GstTagSetter interface.
- * Tags contained withing the vorbis bitstream will be picked up
- * automatically (and merged according to the merge mode set via the tag
- * setter interface).
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch -v filesrc location=foo.ogg ! oggdemux ! vorbistag ! oggmux ! filesink location=bar.ogg
- * ]| This element is not useful with gst-launch, because it does not support
- * setting the tags on a #GstTagSetter interface. Conceptually, the element
- * will usually be used in this order though.
- * </refsect2>
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <glib.h>
-#include <gst/tag/tag.h>
-#include <gst/gsttagsetter.h>
-
-#include <vorbis/codec.h>
-
-#include "gstvorbistag.h"
-
-
-GST_DEBUG_CATEGORY_EXTERN (vorbisparse_debug);
-#define GST_CAT_DEFAULT vorbisparse_debug
-
-static void gst_vorbis_tag_base_init (gpointer g_class);
-static void gst_vorbis_tag_class_init (GstVorbisTagClass * klass);
-static void gst_vorbis_tag_init (GstVorbisTag * tagger,
- GstVorbisTagClass * g_class);
-static GstFlowReturn gst_vorbis_tag_parse_packet (GstVorbisParse * parse,
- GstBuffer * buffer);
-
-#define _do_init(type) \
- G_STMT_START{ \
- static const GInterfaceInfo tag_setter_info = { \
- NULL, \
- NULL, \
- NULL \
- }; \
- g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, \
- &tag_setter_info); \
- }G_STMT_END
-
-GST_BOILERPLATE_FULL (GstVorbisTag, gst_vorbis_tag, GstVorbisParse,
- GST_TYPE_VORBIS_PARSE, _do_init);
-
-static GstElementDetails vorbis_tag_details = {
- "VorbisTag",
- "Formatter/Metadata",
- "Retags vorbis streams",
- "James Livingston <doclivingston@gmail.com>"
-};
-
-
-static void
-gst_vorbis_tag_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_details (element_class, &vorbis_tag_details);
-}
-
-static void
-gst_vorbis_tag_class_init (GstVorbisTagClass * klass)
-{
- GstVorbisParseClass *vorbisparse_class = GST_VORBIS_PARSE_CLASS (klass);
-
- vorbisparse_class->parse_packet = gst_vorbis_tag_parse_packet;
-}
-
-static void
-gst_vorbis_tag_init (GstVorbisTag * tagger, GstVorbisTagClass * g_class)
-{
- /* nothing to do */
-}
-
-
-static GstFlowReturn
-gst_vorbis_tag_parse_packet (GstVorbisParse * parse, GstBuffer * buffer)
-{
- GstTagList *old_tags, *new_tags;
- const GstTagList *user_tags;
- GstVorbisTag *tagger;
- gchar *encoder = NULL;
- GstBuffer *new_buf;
-
- /* just pass everything except the comments packet */
- if (GST_BUFFER_SIZE (buffer) >= 1 && GST_BUFFER_DATA (buffer)[0] != 0x03) {
- return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, buffer);
- }
-
- tagger = GST_VORBIS_TAG (parse);
-
- old_tags =
- gst_tag_list_from_vorbiscomment_buffer (buffer, (guint8 *) "\003vorbis",
- 7, &encoder);
- user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tagger));
-
- /* build new tag list */
- new_tags = gst_tag_list_merge (user_tags, old_tags,
- gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tagger)));
- gst_tag_list_free (old_tags);
-
- new_buf =
- gst_tag_list_to_vorbiscomment_buffer (new_tags, (guint8 *) "\003vorbis",
- 7, encoder);
- gst_buffer_copy_metadata (new_buf, buffer, GST_BUFFER_COPY_TIMESTAMPS);
-
- gst_tag_list_free (new_tags);
- g_free (encoder);
- gst_buffer_unref (buffer);
-
- return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, new_buf);
-}