From ca6758350ad0fbb0e1398f84f17adc886167a7e3 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Mon, 17 Dec 2001 18:37:01 +0000 Subject: [PATCH 001/197] building up speed Original commit message from CVS: building up speed From 1d33a3f18edcba8654856bbb817e23806dcec253 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sun, 5 Jun 2011 00:54:19 -0700 Subject: [PATCH 002/197] opus: duplicate from CELT Copy the celt plugin and convert it to Opus. Mostly works. --- ext/opus/Makefile.am | 16 + ext/opus/gstopus.c | 50 ++ ext/opus/gstopusdec.c | 865 +++++++++++++++++++++++++++++ ext/opus/gstopusdec.h | 77 +++ ext/opus/gstopusenc.c | 1198 +++++++++++++++++++++++++++++++++++++++++ ext/opus/gstopusenc.h | 105 ++++ 6 files changed, 2311 insertions(+) create mode 100644 ext/opus/Makefile.am create mode 100644 ext/opus/gstopus.c create mode 100644 ext/opus/gstopusdec.c create mode 100644 ext/opus/gstopusdec.h create mode 100644 ext/opus/gstopusenc.c create mode 100644 ext/opus/gstopusenc.h diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am new file mode 100644 index 0000000000..aa50ba96ef --- /dev/null +++ b/ext/opus/Makefile.am @@ -0,0 +1,16 @@ +plugin_LTLIBRARIES = libgstopus.la + +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c +libgstopus_la_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(OPUS_CFLAGS) +libgstopus_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) \ + $(GST_LIBS) \ + $(OPUS_LIBS) +libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) +libgstopus_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstopusenc.h gstopusdec.h diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c new file mode 100644 index 0000000000..65e9dcdc58 --- /dev/null +++ b/ext/opus/gstopus.c @@ -0,0 +1,50 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2008> Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstopusdec.h" +#include "gstopusenc.h" + +#include + +static gboolean +plugin_init (GstPlugin * plugin) +{ + + if (!gst_element_register (plugin, "opusenc", GST_RANK_NONE, + GST_TYPE_OPUS_ENC)) + return FALSE; + + if (!gst_element_register (plugin, "opusdec", GST_RANK_PRIMARY, + GST_TYPE_OPUS_DEC)) + return FALSE; + + gst_tag_register_musicbrainz_tags (); + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "opus", + "OPUS plugin library", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c new file mode 100644 index 0000000000..47c06cec0a --- /dev/null +++ b/ext/opus/gstopusdec.c @@ -0,0 +1,865 @@ +/* GStreamer + * Copyright (C) 2004 Wim Taymans + * Copyright (C) 2006 Tim-Philipp Müller + * Copyright (C) 2008 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Based on the speexdec element. + */ + +/** + * SECTION:element-opusdec + * @see_also: opusenc, oggdemux + * + * This element decodes a OPUS stream to raw integer audio. + * + * + * Example pipelines + * |[ + * gst-launch -v filesrc location=opus.ogg ! oggdemux ! opusdec ! audioconvert ! audioresample ! alsasink + * ]| Decode an Ogg/Opus file. To create an Ogg/Opus file refer to the documentation of opusenc. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstopusdec.h" +#include +#include + +GST_DEBUG_CATEGORY_STATIC (opusdec_debug); +#define GST_CAT_DEFAULT opusdec_debug + +#define DEC_MAX_FRAME_SIZE 2000 + +static GstStaticPadTemplate opus_dec_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "rate = (int) [ 32000, 64000 ], " + "channels = (int) [ 1, 2 ], " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") + ); + +static GstStaticPadTemplate opus_dec_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-opus") + ); + +GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstElement, GST_TYPE_ELEMENT); + +static gboolean opus_dec_sink_event (GstPad * pad, GstEvent * event); +static GstFlowReturn opus_dec_chain (GstPad * pad, GstBuffer * buf); +static gboolean opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps); +static GstStateChangeReturn opus_dec_change_state (GstElement * element, + GstStateChange transition); + +static gboolean opus_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean opus_dec_src_query (GstPad * pad, GstQuery * query); +static gboolean opus_dec_sink_query (GstPad * pad, GstQuery * query); +static const GstQueryType *opus_get_src_query_types (GstPad * pad); +static const GstQueryType *opus_get_sink_query_types (GstPad * pad); +static gboolean opus_dec_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value); + +static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, + GstBuffer * buf, GstClockTime timestamp, GstClockTime duration); +static GstFlowReturn opus_dec_chain_parse_header (GstOpusDec * dec, + GstBuffer * buf); +#if 0 +static GstFlowReturn opus_dec_chain_parse_comments (GstOpusDec * dec, + GstBuffer * buf); +#endif + +static void +gst_opus_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&opus_dec_src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&opus_dec_sink_factory)); + gst_element_class_set_details_simple (element_class, "Opus audio decoder", + "Codec/Decoder/Audio", + "decode opus streams to audio", + "Sebastian Dröge "); +} + +static void +gst_opus_dec_class_init (GstOpusDecClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass *) klass; + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (opus_dec_change_state); + + GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, + "opus decoding element"); +} + +static void +gst_opus_dec_reset (GstOpusDec * dec) +{ + gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED); + dec->granulepos = -1; + dec->packetno = 0; + dec->frame_size = 0; + dec->frame_samples = 960; + dec->frame_duration = 0; + if (dec->state) { + opus_decoder_destroy (dec->state); + dec->state = NULL; + } +#if 0 + if (dec->mode) { + opus_mode_destroy (dec->mode); + dec->mode = NULL; + } +#endif + + gst_buffer_replace (&dec->streamheader, NULL); + gst_buffer_replace (&dec->vorbiscomment, NULL); + g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (dec->extra_headers); + dec->extra_headers = NULL; + +#if 0 + memset (&dec->header, 0, sizeof (dec->header)); +#endif +} + +static void +gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) +{ + dec->sinkpad = + gst_pad_new_from_static_template (&opus_dec_sink_factory, "sink"); + gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (opus_dec_chain)); + gst_pad_set_event_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (opus_dec_sink_event)); + gst_pad_set_query_type_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (opus_get_sink_query_types)); + gst_pad_set_query_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (opus_dec_sink_query)); + gst_pad_set_setcaps_function (dec->sinkpad, + GST_DEBUG_FUNCPTR (opus_dec_sink_setcaps)); + gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); + + dec->srcpad = gst_pad_new_from_static_template (&opus_dec_src_factory, "src"); + gst_pad_use_fixed_caps (dec->srcpad); + gst_pad_set_event_function (dec->srcpad, + GST_DEBUG_FUNCPTR (opus_dec_src_event)); + gst_pad_set_query_type_function (dec->srcpad, + GST_DEBUG_FUNCPTR (opus_get_src_query_types)); + gst_pad_set_query_function (dec->srcpad, + GST_DEBUG_FUNCPTR (opus_dec_src_query)); + gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); + + dec->sample_rate = 48000; + dec->n_channels = 2; + + gst_opus_dec_reset (dec); +} + +static gboolean +opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstOpusDec *dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + GstStructure *s; + const GValue *streamheader; + + s = gst_caps_get_structure (caps, 0); + if ((streamheader = gst_structure_get_value (s, "streamheader")) && + G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && + gst_value_array_get_size (streamheader) >= 2) { + const GValue *header; + GstBuffer *buf; + GstFlowReturn res = GST_FLOW_OK; + + header = gst_value_array_get_value (streamheader, 0); + if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { + buf = gst_value_get_buffer (header); + res = opus_dec_chain_parse_header (dec, buf); + if (res != GST_FLOW_OK) + goto done; + gst_buffer_replace (&dec->streamheader, buf); + } +#if 0 + vorbiscomment = gst_value_array_get_value (streamheader, 1); + if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { + buf = gst_value_get_buffer (vorbiscomment); + res = opus_dec_chain_parse_comments (dec, buf); + if (res != GST_FLOW_OK) + goto done; + gst_buffer_replace (&dec->vorbiscomment, buf); + } +#endif + + g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (dec->extra_headers); + dec->extra_headers = NULL; + + if (gst_value_array_get_size (streamheader) > 2) { + gint i, n; + + n = gst_value_array_get_size (streamheader); + for (i = 2; i < n; i++) { + header = gst_value_array_get_value (streamheader, i); + buf = gst_value_get_buffer (header); + dec->extra_headers = + g_list_prepend (dec->extra_headers, gst_buffer_ref (buf)); + } + } + } + +done: + gst_object_unref (dec); + return ret; +} + +static gboolean +opus_dec_convert (GstPad * pad, + GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstOpusDec *dec; + guint64 scale = 1; + + dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + + if (dec->packetno < 1) { + res = FALSE; + goto cleanup; + } + + if (src_format == *dest_format) { + *dest_value = src_value; + res = TRUE; + goto cleanup; + } + + if (pad == dec->sinkpad && + (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) { + res = FALSE; + goto cleanup; + } + + switch (src_format) { + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_BYTES: + scale = sizeof (gint16) * dec->n_channels; + case GST_FORMAT_DEFAULT: + *dest_value = + gst_util_uint64_scale_int (scale * src_value, + dec->sample_rate, GST_SECOND); + break; + default: + res = FALSE; + break; + } + break; + case GST_FORMAT_DEFAULT: + switch (*dest_format) { + case GST_FORMAT_BYTES: + *dest_value = src_value * sizeof (gint16) * dec->n_channels; + break; + case GST_FORMAT_TIME: + *dest_value = + gst_util_uint64_scale_int (src_value, GST_SECOND, + dec->sample_rate); + break; + default: + res = FALSE; + break; + } + break; + case GST_FORMAT_BYTES: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + *dest_value = src_value / (sizeof (gint16) * dec->n_channels); + break; + case GST_FORMAT_TIME: + *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, + dec->sample_rate * sizeof (gint16) * dec->n_channels); + break; + default: + res = FALSE; + break; + } + break; + default: + res = FALSE; + break; + } + +cleanup: + gst_object_unref (dec); + return res; +} + +static const GstQueryType * +opus_get_sink_query_types (GstPad * pad) +{ + static const GstQueryType opus_dec_sink_query_types[] = { + GST_QUERY_CONVERT, + 0 + }; + + return opus_dec_sink_query_types; +} + +static gboolean +opus_dec_sink_query (GstPad * pad, GstQuery * query) +{ + GstOpusDec *dec; + gboolean res; + + dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CONVERT: + { + GstFormat src_fmt, dest_fmt; + gint64 src_val, dest_val; + + gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); + res = opus_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val); + if (res) { + gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (dec); + return res; +} + +static const GstQueryType * +opus_get_src_query_types (GstPad * pad) +{ + static const GstQueryType opus_dec_src_query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + 0 + }; + + return opus_dec_src_query_types; +} + +static gboolean +opus_dec_src_query (GstPad * pad, GstQuery * query) +{ + GstOpusDec *dec; + gboolean res = FALSE; + + dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION:{ + GstSegment segment; + GstFormat format; + gint64 cur; + + gst_query_parse_position (query, &format, NULL); + + GST_PAD_STREAM_LOCK (dec->sinkpad); + segment = dec->segment; + GST_PAD_STREAM_UNLOCK (dec->sinkpad); + + if (segment.format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (dec, "segment not initialised yet"); + break; + } + + if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, + segment.last_stop, &format, &cur))) { + gst_query_set_position (query, format, cur); + } + break; + } + case GST_QUERY_DURATION:{ + GstFormat format = GST_FORMAT_TIME; + gint64 dur; + + /* get duration from demuxer */ + if (!gst_pad_query_peer_duration (dec->sinkpad, &format, &dur)) + break; + + gst_query_parse_duration (query, &format, NULL); + + /* and convert it into the requested format */ + if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, + dur, &format, &dur))) { + gst_query_set_duration (query, format, dur); + } + break; + } + default: + res = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (dec); + return res; +} + +static gboolean +opus_dec_src_event (GstPad * pad, GstEvent * event) +{ + gboolean res = FALSE; + GstOpusDec *dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + + GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK:{ + GstFormat format, tformat; + gdouble rate; + GstEvent *real_seek; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + gint64 tcur, tstop; + + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + + /* we have to ask our peer to seek to time here as we know + * nothing about how to generate a granulepos from the src + * formats or anything. + * + * First bring the requested format to time + */ + tformat = GST_FORMAT_TIME; + if (!(res = opus_dec_convert (pad, format, cur, &tformat, &tcur))) + break; + if (!(res = opus_dec_convert (pad, format, stop, &tformat, &tstop))) + break; + + /* then seek with time on the peer */ + real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, + flags, cur_type, tcur, stop_type, tstop); + + GST_LOG_OBJECT (dec, "seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (tcur)); + + res = gst_pad_push_event (dec->sinkpad, real_seek); + gst_event_unref (event); + break; + } + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (dec); + return res; +} + +static gboolean +opus_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstOpusDec *dec; + gboolean ret = FALSE; + + dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + + GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT:{ + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + gboolean update; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + if (format != GST_FORMAT_TIME) + goto newseg_wrong_format; + + if (rate <= 0.0) + goto newseg_wrong_rate; + + if (update) { + /* time progressed without data, see if we can fill the gap with + * some concealment data */ + if (dec->segment.last_stop < start) { + GstClockTime duration; + + duration = start - dec->segment.last_stop; + opus_dec_chain_parse_data (dec, NULL, dec->segment.last_stop, + duration); + } + } + + /* now configure the values */ + gst_segment_set_newsegment_full (&dec->segment, update, + rate, arate, GST_FORMAT_TIME, start, stop, time); + + dec->granulepos = -1; + + GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%" + GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]", + GST_TIME_ARGS (dec->segment.last_stop), + GST_TIME_ARGS (dec->segment.start), + GST_TIME_ARGS (dec->segment.stop)); + + ret = gst_pad_push_event (dec->srcpad, event); + break; + } + default: + ret = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (dec); + return ret; + + /* ERRORS */ +newseg_wrong_format: + { + GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); + gst_object_unref (dec); + return FALSE; + } +newseg_wrong_rate: + { + GST_DEBUG_OBJECT (dec, "negative rates not supported yet"); + gst_object_unref (dec); + return FALSE; + } +} + +static GstFlowReturn +opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) +{ + GstCaps *caps; + //gint error = OPUS_OK; + +#if 0 + dec->samples_per_frame = opus_packet_get_samples_per_frame ( + (const unsigned char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); +#endif + +#if 0 + if (memcmp (dec->header.codec_id, "OPUS ", 8) != 0) + goto invalid_header; +#endif + +#if 0 +#ifdef HAVE_OPUS_0_7 + dec->mode = + opus_mode_create (dec->sample_rate, dec->header.frame_size, &error); +#else + dec->mode = + opus_mode_create (dec->sample_rate, dec->header.nb_channels, + dec->header.frame_size, &error); +#endif + if (!dec->mode) + goto mode_init_failed; + + /* initialize the decoder */ +#ifdef HAVE_OPUS_0_11 + dec->state = + opus_decoder_create_custom (dec->mode, dec->header.nb_channels, &error); +#else +#ifdef HAVE_OPUS_0_7 + dec->state = opus_decoder_create (dec->mode, dec->header.nb_channels, &error); +#else + dec->state = opus_decoder_create (dec->mode); +#endif +#endif +#endif + dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels); + if (!dec->state) + goto init_failed; + +#if 0 +#ifdef HAVE_OPUS_0_8 + dec->frame_size = dec->header.frame_size; +#else + opus_mode_info (dec->mode, OPUS_GET_FRAME_SIZE, &dec->frame_size); +#endif +#endif + + dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, + GST_SECOND, dec->sample_rate); + + /* set caps */ + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sample_rate, + "channels", G_TYPE_INT, dec->n_channels, + "signed", G_TYPE_BOOLEAN, TRUE, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); + + GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", + dec->sample_rate, dec->n_channels, dec->frame_size); + + if (!gst_pad_set_caps (dec->srcpad, caps)) + goto nego_failed; + + gst_caps_unref (caps); + return GST_FLOW_OK; + + /* ERRORS */ +#if 0 +invalid_header: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Invalid header")); + return GST_FLOW_ERROR; + } +mode_init_failed: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Mode initialization failed: %d", error)); + return GST_FLOW_ERROR; + } +#endif +init_failed: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("couldn't initialize decoder")); + return GST_FLOW_ERROR; + } +nego_failed: + { + GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("couldn't negotiate format")); + gst_caps_unref (caps); + return GST_FLOW_NOT_NEGOTIATED; + } +} + +#if 0 +static GstFlowReturn +opus_dec_chain_parse_comments (GstOpusDec * dec, GstBuffer * buf) +{ + GstTagList *list; + gchar *encoder = NULL; + + list = gst_tag_list_from_vorbiscomment_buffer (buf, NULL, 0, &encoder); + + if (!list) { + GST_WARNING_OBJECT (dec, "couldn't decode comments"); + list = gst_tag_list_new (); + } + + if (encoder) { + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_ENCODER, encoder, NULL); + } + + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_AUDIO_CODEC, "Opus", NULL); + + if (dec->header.bytes_per_packet > 0) { + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, (guint) dec->header.bytes_per_packet * 8, NULL); + } + + GST_INFO_OBJECT (dec, "tags: %" GST_PTR_FORMAT, list); + + gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, list); + + g_free (encoder); + g_free (ver); + + return GST_FLOW_OK; +} +#endif + +static GstFlowReturn +opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, + GstClockTime timestamp, GstClockTime duration) +{ + GstFlowReturn res = GST_FLOW_OK; + gint size; + guint8 *data; + GstBuffer *outbuf; + gint16 *out_data; + int n; + + if (timestamp != -1) { + dec->segment.last_stop = timestamp; + dec->granulepos = -1; + } + + if (dec->state == NULL) { + GstCaps *caps; + + dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels); + + /* set caps */ + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sample_rate, + "channels", G_TYPE_INT, dec->n_channels, + "signed", G_TYPE_BOOLEAN, TRUE, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); + + GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", + dec->sample_rate, dec->n_channels, dec->frame_size); + + if (!gst_pad_set_caps (dec->srcpad, caps)) + GST_ERROR ("nego failure"); + + gst_caps_unref (caps); + } + + if (buf) { + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + GST_DEBUG_OBJECT (dec, "received buffer of size %u", size); + + /* copy timestamp */ + } else { + /* concealment data, pass NULL as the bits parameters */ + GST_DEBUG_OBJECT (dec, "creating concealment data"); + data = NULL; + size = 0; + } + + GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data)); + GST_DEBUG ("samples_per_frame %d", opus_packet_get_samples_per_frame (data, + 48000)); + GST_DEBUG ("channels %d", opus_packet_get_nb_channels (data)); + + res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, + GST_BUFFER_OFFSET_NONE, dec->frame_samples * dec->n_channels * 2, + GST_PAD_CAPS (dec->srcpad), &outbuf); + + if (res != GST_FLOW_OK) { + GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); + return res; + } + + out_data = (gint16 *) GST_BUFFER_DATA (outbuf); + + GST_LOG_OBJECT (dec, "decoding frame"); + + n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, TRUE); + if (n < 0) { + GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); + return GST_FLOW_ERROR; + } + + if (!GST_CLOCK_TIME_IS_VALID (timestamp)) { + timestamp = gst_util_uint64_scale_int (dec->granulepos - dec->frame_size, + GST_SECOND, dec->sample_rate); + } + + GST_DEBUG_OBJECT (dec, "timestamp=%" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); + GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf); + if (dec->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + dec->discont = 0; + } + + dec->segment.last_stop += dec->frame_duration; + + GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_TIME_ARGS (dec->frame_duration)); + + res = gst_pad_push (dec->srcpad, outbuf); + + if (res != GST_FLOW_OK) + GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); + + return res; +} + +static GstFlowReturn +opus_dec_chain (GstPad * pad, GstBuffer * buf) +{ + GstFlowReturn res; + GstOpusDec *dec; + + dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + + if (GST_BUFFER_IS_DISCONT (buf)) { + dec->discont = TRUE; + } + + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + +//done: + dec->packetno++; + + gst_buffer_unref (buf); + gst_object_unref (dec); + + return res; +} + +static GstStateChangeReturn +opus_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstOpusDec *dec = GST_OPUS_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + default: + break; + } + + ret = parent_class->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_opus_dec_reset (dec); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h new file mode 100644 index 0000000000..886a907532 --- /dev/null +++ b/ext/opus/gstopusdec.h @@ -0,0 +1,77 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2008> Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_OPUS_DEC_H__ +#define __GST_OPUS_DEC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_OPUS_DEC \ + (gst_opus_dec_get_type()) +#define GST_OPUS_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPUS_DEC,GstOpusDec)) +#define GST_OPUS_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPUS_DEC,GstOpusDecClass)) +#define GST_IS_OPUS_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPUS_DEC)) +#define GST_IS_OPUS_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPUS_DEC)) + +typedef struct _GstOpusDec GstOpusDec; +typedef struct _GstOpusDecClass GstOpusDecClass; + +struct _GstOpusDec { + GstElement element; + + /* pads */ + GstPad *sinkpad; + GstPad *srcpad; + + OpusDecoder *state; + int frame_samples; + + gint frame_size; + GstClockTime frame_duration; + guint64 packetno; + + GstSegment segment; /* STREAM LOCK */ + gint64 granulepos; /* -1 = needs to be set from current time */ + gboolean discont; + + GstBuffer *streamheader; + GstBuffer *vorbiscomment; + GList *extra_headers; + + int sample_rate; + int n_channels; +}; + +struct _GstOpusDecClass { + GstElementClass parent_class; +}; + +GType gst_opus_dec_get_type (void); + +G_END_DECLS + +#endif /* __GST_OPUS_DEC_H__ */ diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c new file mode 100644 index 0000000000..db57ff75d1 --- /dev/null +++ b/ext/opus/gstopusenc.c @@ -0,0 +1,1198 @@ +/* GStreamer Opus Encoder + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2008> Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Based on the speexenc element + */ + +/** + * SECTION:element-opusenc + * @see_also: opusdec, oggmux + * + * This element encodes raw audio to OPUS. + * + * + * Example pipelines + * |[ + * gst-launch -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! opusenc ! oggmux ! filesink location=sine.ogg + * ]| Encode a test sine signal to Ogg/OPUS. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include "gstopusenc.h" + +GST_DEBUG_CATEGORY_STATIC (opusenc_debug); +#define GST_CAT_DEFAULT opusenc_debug + +#define GST_OPUS_ENC_TYPE_BANDWIDTH (gst_opus_enc_bandwidth_get_type()) +static GType +gst_opus_enc_bandwidth_get_type (void) +{ + static const GEnumValue values[] = { + {OPUS_BANDWIDTH_NARROWBAND, "Narrow band", "narrowband"}, + {OPUS_BANDWIDTH_MEDIUMBAND, "Medium band", "mediumband"}, + {OPUS_BANDWIDTH_WIDEBAND, "Wide band", "wideband"}, + {OPUS_BANDWIDTH_SUPERWIDEBAND, "Super wide band", "superwideband"}, + {OPUS_BANDWIDTH_FULLBAND, "Full band", "fullband"}, + {OPUS_BANDWIDTH_AUTO, "Auto", "auto"}, + {0, NULL, NULL} + }; + static volatile GType id = 0; + + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + + _id = g_enum_register_static ("GstOpusEncBandwidth", values); + + g_once_init_leave ((gsize *) & id, _id); + } + + return id; +} + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " + "channels = (int) [ 1, 2 ], " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16") + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-opus, " + "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " + "channels = (int) [ 1, 2 ], " "frame-size = (int) [ 2, 60 ]") + ); + +#define DEFAULT_AUDIO TRUE +#define DEFAULT_BITRATE 64000 +#define DEFAULT_BANDWIDTH OPUS_BANDWIDTH_FULLBAND +#define DEFAULT_FRAMESIZE 20 +#define DEFAULT_CBR TRUE +#define DEFAULT_CONSTRAINED_VBR TRUE +#define DEFAULT_COMPLEXITY 10 +#define DEFAULT_INBAND_FEC FALSE +#define DEFAULT_DTX FALSE +#define DEFAULT_PACKET_LOSS_PERCENT 0 + +enum +{ + PROP_0, + PROP_AUDIO, + PROP_BITRATE, + PROP_BANDWIDTH, + PROP_FRAME_SIZE, + PROP_CBR, + PROP_CONSTRAINED_VBR, + PROP_COMPLEXITY, + PROP_INBAND_FEC, + PROP_DTX, + PROP_PACKET_LOSS_PERCENT +}; + +static void gst_opus_enc_finalize (GObject * object); + +static gboolean gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_opus_enc_chain (GstPad * pad, GstBuffer * buf); +static gboolean gst_opus_enc_setup (GstOpusEnc * enc); + +static void gst_opus_enc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_opus_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_opus_enc_change_state (GstElement * element, + GstStateChange transition); + +static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush); + +static void +gst_opus_enc_setup_interfaces (GType opusenc_type) +{ + static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; + const GInterfaceInfo preset_interface_info = { + NULL, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + g_type_add_interface_static (opusenc_type, GST_TYPE_TAG_SETTER, + &tag_setter_info); + g_type_add_interface_static (opusenc_type, GST_TYPE_PRESET, + &preset_interface_info); + + GST_DEBUG_CATEGORY_INIT (opusenc_debug, "opusenc", 0, "Opus encoder"); +} + +GST_BOILERPLATE_FULL (GstOpusEnc, gst_opus_enc, GstElement, GST_TYPE_ELEMENT, + gst_opus_enc_setup_interfaces); + +static void +gst_opus_enc_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 (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_set_details_simple (element_class, "Opus audio encoder", + "Codec/Encoder/Audio", + "Encodes audio in Opus format", + "Sebastian Dröge "); +} + +static void +gst_opus_enc_class_init (GstOpusEncClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->set_property = gst_opus_enc_set_property; + gobject_class->get_property = gst_opus_enc_get_property; + + g_object_class_install_property (gobject_class, PROP_AUDIO, + g_param_spec_boolean ("audio", "Audio or voice", + "Audio or voice", DEFAULT_AUDIO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE, + g_param_spec_int ("bitrate", "Encoding Bit-rate", + "Specify an encoding bit-rate (in bps).", + 1, 320000, DEFAULT_BITRATE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BANDWIDTH, + g_param_spec_enum ("bandwidth", "Band Width", + "Audio Band Width", GST_OPUS_ENC_TYPE_BANDWIDTH, DEFAULT_BANDWIDTH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_FRAME_SIZE, + g_param_spec_int ("frame-size", "Frame Size", + "The duration of an audio frame, in ms", 2, 60, DEFAULT_FRAMESIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CBR, + g_param_spec_boolean ("cbr", "Constant bit rate", + "Constant bit rate", DEFAULT_CBR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR, + g_param_spec_boolean ("constrained-cbr", "Constrained VBR", + "Constrained VBR", DEFAULT_CONSTRAINED_VBR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_COMPLEXITY, + g_param_spec_int ("complexity", "Complexity", + "Complexity", 0, 10, DEFAULT_COMPLEXITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_INBAND_FEC, + g_param_spec_boolean ("inband-fec", "In-band FEC", + "Enable forward error correction", DEFAULT_INBAND_FEC, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DTX, + g_param_spec_boolean ("dtx", "DTX", + "DTX", DEFAULT_DTX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_PACKET_LOSS_PERCENT, g_param_spec_int ("packet-loss-percentage", + "Loss percentage", "Packet loss percentage", 0, 100, + DEFAULT_PACKET_LOSS_PERCENT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_opus_enc_change_state); +} + +static void +gst_opus_enc_finalize (GObject * object) +{ + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (object); + + g_object_unref (enc->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstOpusEnc *enc; + GstStructure *structure; + GstCaps *otherpadcaps; + + enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); + enc->setup = FALSE; + enc->frame_size = DEFAULT_FRAMESIZE; + otherpadcaps = gst_pad_get_allowed_caps (pad); + + structure = gst_caps_get_structure (caps, 0); + gst_structure_get_int (structure, "channels", &enc->n_channels); + gst_structure_get_int (structure, "rate", &enc->sample_rate); + + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *ps = gst_caps_get_structure (otherpadcaps, 0); + gst_structure_get_int (ps, "frame-size", &enc->frame_size); + } + gst_caps_unref (otherpadcaps); + } + + GST_ERROR_OBJECT (pad, "channels=%d rate=%d frame-size=%d", + enc->n_channels, enc->sample_rate, enc->frame_size); + switch (enc->frame_size) { + case 2: + enc->frame_samples = enc->sample_rate / 400; + break; + case 5: + enc->frame_samples = enc->sample_rate / 200; + break; + case 10: + enc->frame_samples = enc->sample_rate / 100; + break; + case 20: + enc->frame_samples = enc->sample_rate / 50; + break; + case 40: + enc->frame_samples = enc->sample_rate / 20; + break; + case 60: + enc->frame_samples = 3 * enc->sample_rate / 50; + break; + default: + return FALSE; + break; + } + GST_ERROR ("frame_samples %d", enc->frame_samples); + + gst_opus_enc_setup (enc); + + return TRUE; +} + + +static GstCaps * +gst_opus_enc_sink_getcaps (GstPad * pad) +{ + GstCaps *caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + GstCaps *peercaps = NULL; + GstOpusEnc *enc = GST_OPUS_ENC (gst_pad_get_parent_element (pad)); + + peercaps = gst_pad_peer_get_caps (enc->srcpad); + + if (peercaps) { + if (!gst_caps_is_empty (peercaps) && !gst_caps_is_any (peercaps)) { + GstStructure *ps = gst_caps_get_structure (peercaps, 0); + GstStructure *s = gst_caps_get_structure (caps, 0); + gint rate, channels; + + if (gst_structure_get_int (ps, "rate", &rate)) { + gst_structure_fixate_field_nearest_int (s, "rate", rate); + } + + if (gst_structure_get_int (ps, "channels", &channels)) { + gst_structure_fixate_field_nearest_int (s, "channels", channels); + } + } + gst_caps_unref (peercaps); + } + + gst_object_unref (enc); + + return caps; +} + + +static gboolean +gst_opus_enc_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value, + GstFormat * dest_format, gint64 * dest_value) +{ + gboolean res = TRUE; + GstOpusEnc *enc; + gint64 avg; + + enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); + + if (enc->samples_in == 0 || enc->bytes_out == 0 || enc->sample_rate == 0) + return FALSE; + + avg = (enc->bytes_out * enc->sample_rate) / (enc->samples_in); + + switch (src_format) { + case GST_FORMAT_BYTES: + switch (*dest_format) { + case GST_FORMAT_TIME: + *dest_value = src_value * GST_SECOND / avg; + break; + default: + res = FALSE; + } + break; + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_BYTES: + *dest_value = src_value * avg / GST_SECOND; + break; + default: + res = FALSE; + } + break; + default: + res = FALSE; + } + return res; +} + +static gboolean +gst_opus_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; + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); + + bytes_per_sample = enc->n_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 * enc->sample_rate; + + if (byterate == 0) + return FALSE; + *dest_value = 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 (enc->sample_rate == 0) + return FALSE; + *dest_value = src_value * GST_SECOND / enc->sample_rate; + 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 = src_value * scale * enc->sample_rate / GST_SECOND; + break; + default: + res = FALSE; + } + break; + default: + res = FALSE; + } + return res; +} + +static gint64 +gst_opus_enc_get_latency (GstOpusEnc * enc) +{ + return gst_util_uint64_scale (enc->frame_samples, GST_SECOND, + enc->sample_rate); +} + +static const GstQueryType * +gst_opus_enc_get_query_types (GstPad * pad) +{ + static const GstQueryType gst_opus_enc_src_query_types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_CONVERT, + GST_QUERY_LATENCY, + 0 + }; + + return gst_opus_enc_src_query_types; +} + +static gboolean +gst_opus_enc_src_query (GstPad * pad, GstQuery * query) +{ + gboolean res = TRUE; + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (gst_pad_get_parent (pad)); + + 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_peer_position (enc->sinkpad, &req_fmt, &val))) { + gst_query_set_position (query, req_fmt, val); + break; + } + + fmt = GST_FORMAT_TIME; + if (!(res = gst_pad_query_peer_position (enc->sinkpad, &fmt, &pos))) + break; + + if ((res = + gst_pad_query_peer_convert (enc->sinkpad, 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_peer_duration (enc->sinkpad, &req_fmt, &val))) { + gst_query_set_duration (query, req_fmt, val); + break; + } + + fmt = GST_FORMAT_TIME; + if (!(res = gst_pad_query_peer_duration (enc->sinkpad, &fmt, &dur))) + break; + + if ((res = + gst_pad_query_peer_convert (enc->sinkpad, 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_opus_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_peer_query (pad, query))) { + gst_query_parse_latency (query, &live, &min_latency, &max_latency); + + latency = gst_opus_enc_get_latency (enc); + + /* 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_peer_query (pad, query); + break; + } + +error: + + gst_object_unref (enc); + + return res; +} + +static gboolean +gst_opus_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_opus_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_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) +{ + enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad); + gst_pad_set_event_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_sinkevent)); + gst_pad_set_chain_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_chain)); + gst_pad_set_setcaps_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_sink_setcaps)); + gst_pad_set_getcaps_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps)); + gst_pad_set_query_function (enc->sinkpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_sink_query)); + + enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_query_function (enc->srcpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_src_query)); + gst_pad_set_query_type_function (enc->srcpad, + GST_DEBUG_FUNCPTR (gst_opus_enc_get_query_types)); + gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); + + enc->n_channels = -1; + enc->sample_rate = -1; + enc->frame_samples = 0; + + enc->bitrate = DEFAULT_BITRATE; + enc->bandwidth = DEFAULT_BANDWIDTH; + enc->frame_size = DEFAULT_FRAMESIZE; + enc->cbr = DEFAULT_CBR; + enc->constrained_vbr = DEFAULT_CONSTRAINED_VBR; + enc->complexity = DEFAULT_COMPLEXITY; + enc->inband_fec = DEFAULT_INBAND_FEC; + enc->dtx = DEFAULT_DTX; + enc->packet_loss_percentage = DEFAULT_PACKET_LOSS_PERCENT; + + enc->setup = FALSE; + enc->header_sent = FALSE; + + enc->adapter = gst_adapter_new (); +} + +#if 0 +static GstBuffer * +gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) +{ + const GstTagList *tags; + GstTagList *empty_tags = NULL; + GstBuffer *comments = NULL; + + tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); + + GST_DEBUG_OBJECT (enc, "tags = %" GST_PTR_FORMAT, tags); + + if (tags == NULL) { + /* FIXME: better fix chain of callers to not write metadata at all, + * if there is none */ + empty_tags = gst_tag_list_new (); + tags = empty_tags; + } + comments = gst_tag_list_to_vorbiscomment_buffer (tags, NULL, + 0, "Encoded with GStreamer Opusenc"); + + GST_BUFFER_OFFSET (comments) = enc->bytes_out; + GST_BUFFER_OFFSET_END (comments) = 0; + + if (empty_tags) + gst_tag_list_free (empty_tags); + + return comments; +} +#endif + +static gboolean +gst_opus_enc_setup (GstOpusEnc * enc) +{ + //gint error = OPUS_OK; + + enc->setup = FALSE; + +#if 0 +#ifdef HAVE_OPUS_0_7 + enc->mode = opus_mode_create (enc->rate, enc->frame_size, &error); +#else + enc->mode = + opus_mode_create (enc->rate, enc->n_channels, enc->frame_size, &error); +#endif + if (!enc->mode) + goto mode_initialization_failed; + +#ifdef HAVE_OPUS_0_11 + opus_header_init (&enc->header, enc->mode, enc->frame_size, enc->n_channels); +#else +#ifdef HAVE_OPUS_0_7 + opus_header_init (&enc->header, enc->mode, enc->n_channels); +#else + opus_header_init (&enc->header, enc->mode); +#endif +#endif + enc->header.nb_channels = enc->n_channels; + +#ifdef HAVE_OPUS_0_8 + enc->frame_size = enc->header.frame_size; +#else + opus_mode_info (enc->mode, OPUS_GET_FRAME_SIZE, &enc->frame_size); +#endif +#endif + +#if 0 +#ifdef HAVE_OPUS_0_11 + enc->state = opus_encoder_create_custom (enc->mode, enc->n_channels, &error); +#else +#ifdef HAVE_OPUS_0_7 + enc->state = opus_encoder_create (enc->mode, enc->n_channels, &error); +#else + enc->state = opus_encoder_create (enc->mode); +#endif +#endif +#endif + enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, + enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP); + if (!enc->state) + goto encoder_creation_failed; + + opus_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); + opus_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); + opus_encoder_ctl (enc->state, OPUS_SET_VBR_FLAG (!enc->cbr), 0); + opus_encoder_ctl (enc->state, OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), + 0); + opus_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0); + opus_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC_FLAG (enc->inband_fec), 0); + opus_encoder_ctl (enc->state, OPUS_SET_DTX_FLAG (enc->dtx), 0); + opus_encoder_ctl (enc->state, + OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0); + + GST_LOG_OBJECT (enc, "we have frame size %d", enc->frame_size); + + enc->setup = TRUE; + + return TRUE; + +#if 0 +mode_initialization_failed: + GST_ERROR_OBJECT (enc, "Mode initialization failed: %d", error); + return FALSE; +#endif + +encoder_creation_failed: + GST_ERROR_OBJECT (enc, "Encoder creation failed"); + return FALSE; +} + + +/* push out the buffer and do internal bookkeeping */ +static GstFlowReturn +gst_opus_enc_push_buffer (GstOpusEnc * enc, GstBuffer * buffer) +{ + guint size; + + size = GST_BUFFER_SIZE (buffer); + + enc->bytes_out += size; + + GST_DEBUG_OBJECT (enc, "pushing output buffer of size %u", size); + + return gst_pad_push (enc->srcpad, buffer); +} + +#if 0 +static GstCaps * +gst_opus_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, + GstBuffer * buf2) +{ + GstStructure *structure = NULL; + GstBuffer *buf; + GValue array = { 0 }; + GValue value = { 0 }; + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_assert (gst_buffer_is_metadata_writable (buf1)); + g_assert (gst_buffer_is_metadata_writable (buf2)); + + /* mark buffers */ + GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); + GST_BUFFER_FLAG_SET (buf2, 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); + gst_structure_set_value (structure, "streamheader", &array); + g_value_unset (&value); + g_value_unset (&array); + + return caps; +} +#endif + + +static gboolean +gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event) +{ + gboolean res = TRUE; + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + gst_opus_enc_encode (enc, TRUE); + res = gst_pad_event_default (pad, event); + break; + case GST_EVENT_TAG: + { + GstTagList *list; + GstTagSetter *setter = GST_TAG_SETTER (enc); + const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter); + + gst_event_parse_tag (event, &list); + gst_tag_setter_merge_tags (setter, list, mode); + res = gst_pad_event_default (pad, event); + break; + } + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (enc); + + return res; +} + +static GstFlowReturn +gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) +{ + + GstFlowReturn ret = GST_FLOW_OK; + gint bytes = enc->frame_samples * 2 * enc->n_channels; + gint bytes_per_packet; + + bytes_per_packet = + (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8; + + if (flush && gst_adapter_available (enc->adapter) % bytes != 0) { + guint diff = gst_adapter_available (enc->adapter) % bytes; + GstBuffer *buf = gst_buffer_new_and_alloc (diff); + + memset (GST_BUFFER_DATA (buf), 0, diff); + gst_adapter_push (enc->adapter, buf); + } + + + while (gst_adapter_available (enc->adapter) >= bytes) { + gint16 *data; + gint outsize; + GstBuffer *outbuf; + + ret = gst_pad_alloc_buffer_and_set_caps (enc->srcpad, + GST_BUFFER_OFFSET_NONE, bytes_per_packet, GST_PAD_CAPS (enc->srcpad), + &outbuf); + + if (GST_FLOW_OK != ret) + goto done; + + data = (gint16 *) gst_adapter_take (enc->adapter, bytes); + enc->samples_in += enc->frame_samples; + + GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", + enc->frame_samples, bytes); + + outsize = opus_encode (enc->state, data, enc->frame_samples, + GST_BUFFER_DATA (outbuf), bytes_per_packet); + + g_free (data); + + if (outsize < 0) { + GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); + ret = GST_FLOW_ERROR; + goto done; + } + + GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts + + gst_util_uint64_scale_int (enc->frameno_out * enc->frame_samples, + GST_SECOND, enc->sample_rate); + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale_int (enc->frame_samples, GST_SECOND, + enc->sample_rate); + GST_BUFFER_OFFSET (outbuf) = + gst_util_uint64_scale_int (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND, + enc->sample_rate); + + enc->frameno++; + enc->frameno_out++; + + ret = gst_opus_enc_push_buffer (enc, outbuf); + + if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) + goto done; + } + +done: + + return ret; +} + +static GstFlowReturn +gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) +{ + GstOpusEnc *enc; + GstFlowReturn ret = GST_FLOW_OK; + + enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); + + if (!enc->setup) + goto not_setup; + +#if 0 + if (!enc->header_sent) { + /* Opus streams begin with two 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. + We merely need to make the headers, then pass them to libopus + one at a time; libopus handles the additional Ogg bitstream + constraints */ + GstBuffer *buf1, *buf2; + GstCaps *caps; + guchar data[100]; + + /* create header buffer */ + opus_header_to_packet (&enc->header, data, 100); + buf1 = gst_opus_enc_buffer_from_data (enc, data, 100, 0); + + /* create comment buffer */ + buf2 = gst_opus_enc_create_metadata_buffer (enc); + + /* mark and put on caps */ + caps = gst_pad_get_caps (enc->srcpad); + caps = gst_opus_enc_set_header_on_caps (caps, buf1, buf2); + + gst_caps_set_simple (caps, + "rate", G_TYPE_INT, enc->sample_rate, + "channels", G_TYPE_INT, enc->n_channels, + "frame-size", G_TYPE_INT, enc->frame_size, NULL); + + /* negotiate with these caps */ + GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); + GST_LOG_OBJECT (enc, "rate=%d channels=%d frame-size=%d", + enc->sample_rate, enc->n_channels, enc->frame_size); + gst_pad_set_caps (enc->srcpad, caps); + + gst_buffer_set_caps (buf1, caps); + gst_buffer_set_caps (buf2, caps); + gst_caps_unref (caps); + + /* push out buffers */ + ret = gst_opus_enc_push_buffer (enc, buf1); + + if (ret != GST_FLOW_OK) { + gst_buffer_unref (buf2); + goto done; + } + + ret = gst_opus_enc_push_buffer (enc, buf2); + + if (ret != GST_FLOW_OK) + goto done; + + enc->header_sent = TRUE; + } +#endif + + GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf)); + + /* Save the timestamp of the first buffer. This will be later + * used as offset for all following buffers */ + if (enc->start_ts == GST_CLOCK_TIME_NONE) { + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + enc->start_ts = GST_BUFFER_TIMESTAMP (buf); + } else { + enc->start_ts = 0; + } + } + + + /* Check if we have a continous stream, if not drop some samples or the buffer or + * insert some silence samples */ + if (enc->next_ts != GST_CLOCK_TIME_NONE && + GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) { + guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf); + guint64 diff_bytes; + + GST_WARNING_OBJECT (enc, "Buffer is older than previous " + "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT + "), cannot handle. Clipping buffer.", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_TIME_ARGS (enc->next_ts)); + + diff_bytes = + GST_CLOCK_TIME_TO_FRAMES (diff, enc->sample_rate) * enc->n_channels * 2; + if (diff_bytes >= GST_BUFFER_SIZE (buf)) { + gst_buffer_unref (buf); + return GST_FLOW_OK; + } + buf = gst_buffer_make_metadata_writable (buf); + GST_BUFFER_DATA (buf) += diff_bytes; + GST_BUFFER_SIZE (buf) -= diff_bytes; + + GST_BUFFER_TIMESTAMP (buf) += diff; + if (GST_BUFFER_DURATION_IS_VALID (buf)) + GST_BUFFER_DURATION (buf) -= diff; + } + + if (enc->next_ts != GST_CLOCK_TIME_NONE + && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + guint64 max_diff = + gst_util_uint64_scale (enc->frame_size, GST_SECOND, enc->sample_rate); + + if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts && + GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > max_diff) { + GST_WARNING_OBJECT (enc, + "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT, + GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, max_diff); + + gst_opus_enc_encode (enc, TRUE); + + enc->frameno_out = 0; + enc->start_ts = GST_BUFFER_TIMESTAMP (buf); + } + } + + if (GST_BUFFER_TIMESTAMP_IS_VALID (buf) + && GST_BUFFER_DURATION_IS_VALID (buf)) + enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); + else + enc->next_ts = GST_CLOCK_TIME_NONE; + + /* push buffer to adapter */ + gst_adapter_push (enc->adapter, buf); + buf = NULL; + + ret = gst_opus_enc_encode (enc, FALSE); + +done: + + if (buf) + gst_buffer_unref (buf); + + return ret; + + /* ERRORS */ +not_setup: + { + GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL), + ("encoder not initialized (input is not audio?)")); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } + +} + + +static void +gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (object); + + switch (prop_id) { + case PROP_AUDIO: + g_value_set_boolean (value, enc->audio_or_voip); + break; + case PROP_BITRATE: + g_value_set_int (value, enc->bitrate); + break; + case PROP_BANDWIDTH: + g_value_set_int (value, enc->bandwidth); + break; + case PROP_FRAME_SIZE: + g_value_set_int (value, enc->frame_size); + break; + case PROP_CBR: + g_value_set_boolean (value, enc->cbr); + break; + case PROP_CONSTRAINED_VBR: + g_value_set_boolean (value, enc->constrained_vbr); + break; + case PROP_COMPLEXITY: + g_value_set_int (value, enc->complexity); + break; + case PROP_INBAND_FEC: + g_value_set_boolean (value, enc->inband_fec); + break; + case PROP_DTX: + g_value_set_boolean (value, enc->dtx); + break; + case PROP_PACKET_LOSS_PERCENT: + g_value_set_int (value, enc->packet_loss_percentage); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_opus_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (object); + + switch (prop_id) { + case PROP_AUDIO: + enc->audio_or_voip = g_value_get_boolean (value); + break; + case PROP_BITRATE: + enc->bitrate = g_value_get_int (value); + break; + case PROP_BANDWIDTH: + enc->bandwidth = g_value_get_int (value); + break; + case PROP_FRAME_SIZE: + enc->frame_size = g_value_get_int (value); + break; + case PROP_CBR: + enc->cbr = g_value_get_boolean (value); + break; + case PROP_CONSTRAINED_VBR: + enc->constrained_vbr = g_value_get_boolean (value); + break; + case PROP_COMPLEXITY: + enc->complexity = g_value_get_int (value); + break; + case PROP_INBAND_FEC: + enc->inband_fec = g_value_get_boolean (value); + break; + case PROP_DTX: + enc->dtx = g_value_get_boolean (value); + break; + case PROP_PACKET_LOSS_PERCENT: + enc->packet_loss_percentage = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_opus_enc_change_state (GstElement * element, GstStateChange transition) +{ + GstOpusEnc *enc = GST_OPUS_ENC (element); + GstStateChangeReturn res; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + enc->frameno = 0; + enc->samples_in = 0; + enc->frameno_out = 0; + enc->start_ts = GST_CLOCK_TIME_NONE; + enc->next_ts = GST_CLOCK_TIME_NONE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + /* fall through */ + default: + break; + } + + res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (res == GST_STATE_CHANGE_FAILURE) + return res; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + enc->setup = FALSE; + enc->header_sent = FALSE; + if (enc->state) { + opus_encoder_destroy (enc->state); + enc->state = NULL; + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); + default: + break; + } + + return res; +} diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h new file mode 100644 index 0000000000..5cb54598af --- /dev/null +++ b/ext/opus/gstopusenc.h @@ -0,0 +1,105 @@ +/* GStreamer Opus Encoder + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2008> Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_OPUS_ENC_H__ +#define __GST_OPUS_ENC_H__ + + +#include +#include + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_OPUS_ENC \ + (gst_opus_enc_get_type()) +#define GST_OPUS_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPUS_ENC,GstOpusEnc)) +#define GST_OPUS_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPUS_ENC,GstOpusEncClass)) +#define GST_IS_OPUS_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPUS_ENC)) +#define GST_IS_OPUS_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPUS_ENC)) + +#define MAX_FRAME_SIZE 2000*2 +#define MAX_FRAME_BYTES 2000 + +typedef struct _GstOpusEnc GstOpusEnc; +typedef struct _GstOpusEncClass GstOpusEncClass; + +struct _GstOpusEnc { + GstElement element; + + /* pads */ + GstPad *sinkpad; + GstPad *srcpad; + + //OpusHeader header; + //OpusMode *mode; + OpusEncoder *state; + GstAdapter *adapter; + + /* properties */ + gboolean audio_or_voip; + gint bitrate; + gint bandwidth; + gint frame_size; + gboolean cbr; + gboolean constrained_vbr; + gint complexity; + gboolean inband_fec; + gboolean dtx; + gint packet_loss_percentage; + + int frame_samples; + + gint n_channels; + gint sample_rate; + + gboolean setup; + gboolean header_sent; + gboolean eos; + + guint64 samples_in; + guint64 bytes_out; + + guint64 frameno; + guint64 frameno_out; + + GstClockTime start_ts; + GstClockTime next_ts; + guint64 granulepos_offset; +}; + +struct _GstOpusEncClass { + GstElementClass parent_class; + + /* signals */ + void (*frame_encoded) (GstElement *element); +}; + +GType gst_opus_enc_get_type (void); + +G_END_DECLS + +#endif /* __GST_OPUS_ENC_H__ */ From f2a86068a963d4ef51fe422988d842b7fb92ada6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 4 Aug 2011 09:36:07 +0200 Subject: [PATCH 003/197] Merge branch 'master' into 0.11 Conflicts: common configure.ac gst/colorspace/colorspace.c gst/colorspace/colorspace.h gst/colorspace/gstcolorspace.c From cbb3c6e0193de78307a4c5645e51020bf6c7136d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 4 Aug 2011 09:40:46 +0200 Subject: [PATCH 004/197] Merge branch 'master' into 0.11 From df00cae0be545210b2dbd7eeb39d9aaa0d741cb9 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 17 Aug 2011 19:01:39 +0200 Subject: [PATCH 005/197] Merge branch 'master' into 0.11 From dd6de226a817f20ac22875d337835b538c12a281 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 25 Aug 2011 12:49:26 +0200 Subject: [PATCH 006/197] Merge branch 'master' into 0.11 Conflicts: ext/resindvd/rsnwrappedbuffer.c From 33180df8ba73957fcfec6579a7dee6dbf27cc2de Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 6 Sep 2011 16:13:28 +0200 Subject: [PATCH 007/197] Merge branch 'master' into 0.11 From 1e68f2d312b70cfd4fe663509d91c55a6d62bb0f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 26 Sep 2011 22:31:17 +0200 Subject: [PATCH 008/197] Merge branch 'master' into 0.11 From 00a6c9ac643dc1561fbb55cd5a1a4f7e14d5d848 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Sep 2011 13:24:21 +0100 Subject: [PATCH 009/197] opus: make it build against current, and remove cruft https://bugzilla.gnome.org/show_bug.cgi?id=660364 --- ext/opus/gstopusdec.c | 48 +++++++++----------------------------- ext/opus/gstopusenc.c | 54 +++++++------------------------------------ 2 files changed, 19 insertions(+), 83 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 47c06cec0a..aff4eb9346 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -566,7 +566,7 @@ static GstFlowReturn opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) { GstCaps *caps; - //gint error = OPUS_OK; + int err; #if 0 dec->samples_per_frame = opus_packet_get_samples_per_frame ( @@ -578,42 +578,10 @@ opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) goto invalid_header; #endif -#if 0 -#ifdef HAVE_OPUS_0_7 - dec->mode = - opus_mode_create (dec->sample_rate, dec->header.frame_size, &error); -#else - dec->mode = - opus_mode_create (dec->sample_rate, dec->header.nb_channels, - dec->header.frame_size, &error); -#endif - if (!dec->mode) - goto mode_init_failed; - - /* initialize the decoder */ -#ifdef HAVE_OPUS_0_11 - dec->state = - opus_decoder_create_custom (dec->mode, dec->header.nb_channels, &error); -#else -#ifdef HAVE_OPUS_0_7 - dec->state = opus_decoder_create (dec->mode, dec->header.nb_channels, &error); -#else - dec->state = opus_decoder_create (dec->mode); -#endif -#endif -#endif - dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels); - if (!dec->state) + dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); + if (!dec->state || err != OPUS_OK) goto init_failed; -#if 0 -#ifdef HAVE_OPUS_0_8 - dec->frame_size = dec->header.frame_size; -#else - opus_mode_info (dec->mode, OPUS_GET_FRAME_SIZE, &dec->frame_size); -#endif -#endif - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, GST_SECOND, dec->sample_rate); @@ -711,7 +679,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, guint8 *data; GstBuffer *outbuf; gint16 *out_data; - int n; + int n, err; if (timestamp != -1) { dec->segment.last_stop = timestamp; @@ -721,7 +689,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, if (dec->state == NULL) { GstCaps *caps; - dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels); + dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); + if (!dec->state || err != OPUS_OK) + goto creation_failed; /* set caps */ caps = gst_caps_new_simple ("audio/x-raw-int", @@ -805,6 +775,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); return res; + +creation_failed: + GST_ERROR_OBJECT (dec, "Failed to create Opus decoder: %d", err); + return GST_FLOW_ERROR; } static GstFlowReturn diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index db57ff75d1..9d4fe12862 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -63,7 +63,7 @@ gst_opus_enc_bandwidth_get_type (void) {OPUS_BANDWIDTH_WIDEBAND, "Wide band", "wideband"}, {OPUS_BANDWIDTH_SUPERWIDEBAND, "Super wide band", "superwideband"}, {OPUS_BANDWIDTH_FULLBAND, "Full band", "fullband"}, - {OPUS_BANDWIDTH_AUTO, "Auto", "auto"}, + {OPUS_AUTO, "Auto", "auto"}, {0, NULL, NULL} }; static volatile GType id = 0; @@ -664,62 +664,24 @@ gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { - //gint error = OPUS_OK; + int error = OPUS_OK; enc->setup = FALSE; -#if 0 -#ifdef HAVE_OPUS_0_7 - enc->mode = opus_mode_create (enc->rate, enc->frame_size, &error); -#else - enc->mode = - opus_mode_create (enc->rate, enc->n_channels, enc->frame_size, &error); -#endif - if (!enc->mode) - goto mode_initialization_failed; - -#ifdef HAVE_OPUS_0_11 - opus_header_init (&enc->header, enc->mode, enc->frame_size, enc->n_channels); -#else -#ifdef HAVE_OPUS_0_7 - opus_header_init (&enc->header, enc->mode, enc->n_channels); -#else - opus_header_init (&enc->header, enc->mode); -#endif -#endif - enc->header.nb_channels = enc->n_channels; - -#ifdef HAVE_OPUS_0_8 - enc->frame_size = enc->header.frame_size; -#else - opus_mode_info (enc->mode, OPUS_GET_FRAME_SIZE, &enc->frame_size); -#endif -#endif - -#if 0 -#ifdef HAVE_OPUS_0_11 - enc->state = opus_encoder_create_custom (enc->mode, enc->n_channels, &error); -#else -#ifdef HAVE_OPUS_0_7 - enc->state = opus_encoder_create (enc->mode, enc->n_channels, &error); -#else - enc->state = opus_encoder_create (enc->mode); -#endif -#endif -#endif enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, - enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP); - if (!enc->state) + enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, + &error); + if (!enc->state || error != OPUS_OK) goto encoder_creation_failed; opus_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); opus_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); - opus_encoder_ctl (enc->state, OPUS_SET_VBR_FLAG (!enc->cbr), 0); + opus_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr), 0); opus_encoder_ctl (enc->state, OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), 0); opus_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0); - opus_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC_FLAG (enc->inband_fec), 0); - opus_encoder_ctl (enc->state, OPUS_SET_DTX_FLAG (enc->dtx), 0); + opus_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC (enc->inband_fec), 0); + opus_encoder_ctl (enc->state, OPUS_SET_DTX (enc->dtx), 0); opus_encoder_ctl (enc->state, OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0); From 24577fcf6dfe24f46d532fa0fbe098078a233d5e Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Sep 2011 13:24:52 +0100 Subject: [PATCH 010/197] opusdec: opus supports a select set of sampling rates https://bugzilla.gnome.org/show_bug.cgi?id=660364 --- ext/opus/gstopusdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index aff4eb9346..9cdc144570 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -55,7 +55,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " - "rate = (int) [ 32000, 64000 ], " + "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " "channels = (int) [ 1, 2 ], " "endianness = (int) BYTE_ORDER, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") From 495de129ff12e6c5645f194760a923fc3d86287c Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Sep 2011 13:25:21 +0100 Subject: [PATCH 011/197] opusenc: use the same frame size setup as the opus test code https://bugzilla.gnome.org/show_bug.cgi?id=660364 --- ext/opus/gstopusenc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 9d4fe12862..10c1747d67 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -286,12 +286,13 @@ gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) enc->frame_samples = enc->sample_rate / 50; break; case 40: - enc->frame_samples = enc->sample_rate / 20; + enc->frame_samples = enc->sample_rate / 25; break; case 60: enc->frame_samples = 3 * enc->sample_rate / 50; break; default: + GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size); return FALSE; break; } From f9d4dd5215136d86706b93a0e67555f3000b3829 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Sep 2011 14:22:02 +0100 Subject: [PATCH 012/197] opus: properly setup caps and init state from caps https://bugzilla.gnome.org/show_bug.cgi?id=660364 --- ext/opus/gstopusdec.c | 47 +++++++++++++++++++++++++++++++++++++++++++ ext/opus/gstopusenc.c | 37 ---------------------------------- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 9cdc144570..ae0b0bc689 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -193,6 +193,8 @@ opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps) GstStructure *s; const GValue *streamheader; + GST_DEBUG_OBJECT (pad, "Setting sink caps to %" GST_PTR_FORMAT, caps); + s = gst_caps_get_structure (caps, 0); if ((streamheader = gst_structure_get_value (s, "streamheader")) && G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && @@ -237,6 +239,47 @@ opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps) } } + if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { + GST_WARNING_OBJECT (dec, "Frame size not included in caps"); + } + if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { + GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); + } + if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { + GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); + } + switch (dec->frame_size) { + case 2: + dec->frame_samples = dec->sample_rate / 400; + break; + case 5: + dec->frame_samples = dec->sample_rate / 200; + break; + case 10: + dec->frame_samples = dec->sample_rate / 100; + break; + case 20: + dec->frame_samples = dec->sample_rate / 50; + break; + case 40: + dec->frame_samples = dec->sample_rate / 25; + break; + case 60: + dec->frame_samples = 3 * dec->sample_rate / 50; + break; + default: + GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); + break; + } + + dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, + GST_SECOND, dec->sample_rate); + + GST_INFO_OBJECT (dec, + "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" + GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, + dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); + done: gst_object_unref (dec); return ret; @@ -788,6 +831,10 @@ opus_dec_chain (GstPad * pad, GstBuffer * buf) GstOpusDec *dec; dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + GST_LOG_OBJECT (pad, + "Got buffer ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); if (GST_BUFFER_IS_DISCONT (buf)) { dec->discont = TRUE; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 10c1747d67..db0d4d43c3 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -879,29 +879,10 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) if (!enc->setup) goto not_setup; -#if 0 if (!enc->header_sent) { - /* Opus streams begin with two 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. - We merely need to make the headers, then pass them to libopus - one at a time; libopus handles the additional Ogg bitstream - constraints */ - GstBuffer *buf1, *buf2; GstCaps *caps; - guchar data[100]; - /* create header buffer */ - opus_header_to_packet (&enc->header, data, 100); - buf1 = gst_opus_enc_buffer_from_data (enc, data, 100, 0); - - /* create comment buffer */ - buf2 = gst_opus_enc_create_metadata_buffer (enc); - - /* mark and put on caps */ caps = gst_pad_get_caps (enc->srcpad); - caps = gst_opus_enc_set_header_on_caps (caps, buf1, buf2); - gst_caps_set_simple (caps, "rate", G_TYPE_INT, enc->sample_rate, "channels", G_TYPE_INT, enc->n_channels, @@ -913,26 +894,8 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) enc->sample_rate, enc->n_channels, enc->frame_size); gst_pad_set_caps (enc->srcpad, caps); - gst_buffer_set_caps (buf1, caps); - gst_buffer_set_caps (buf2, caps); - gst_caps_unref (caps); - - /* push out buffers */ - ret = gst_opus_enc_push_buffer (enc, buf1); - - if (ret != GST_FLOW_OK) { - gst_buffer_unref (buf2); - goto done; - } - - ret = gst_opus_enc_push_buffer (enc, buf2); - - if (ret != GST_FLOW_OK) - goto done; - enc->header_sent = TRUE; } -#endif GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf)); From f70c921ff97755f7cc2e239af7b15a9c4bac88ae Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Sep 2011 14:56:18 +0100 Subject: [PATCH 013/197] opusenc: moan if we get an unexpected amount of data https://bugzilla.gnome.org/show_bug.cgi?id=660364 --- ext/opus/gstopusenc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index db0d4d43c3..75ccaaa573 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -842,6 +842,12 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; goto done; + } else if (outsize != bytes_per_packet) { + GST_WARNING_OBJECT (enc, + "Encoded size %d is different from %d bytes per packet", outsize, + bytes_per_packet); + ret = GST_FLOW_ERROR; + goto done; } GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts + From ab1d420f8891c54d6ec6627f697c67dca9a6fba5 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Sep 2011 14:57:02 +0100 Subject: [PATCH 014/197] opusdec: fix decoding A simple ... opusenc ! opusdec ... pipeline now works. https://bugzilla.gnome.org/show_bug.cgi?id=660364 --- ext/opus/gstopusdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index ae0b0bc689..35f501a8ab 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -785,7 +785,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GST_LOG_OBJECT (dec, "decoding frame"); - n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, TRUE); + n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, 0); if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); return GST_FLOW_ERROR; From 0d0257cfd4e5e172cbf6661028c888fb4accca20 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 3 Oct 2011 11:24:04 +0200 Subject: [PATCH 015/197] Merge branch 'master' into 0.11 From 9edd94d793ba637679bab221fe48c0d02d46c776 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 6 Oct 2011 14:05:42 +0200 Subject: [PATCH 016/197] Merge branch 'master' into 0.11 From 139cb43b14a4779b165fed915364f36aa80c21d8 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sat, 8 Oct 2011 11:17:11 +0200 Subject: [PATCH 017/197] Merge branch 'master' into 0.11 From e1e5cda8dcd96b22d14e4b0d2138a8875043b429 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sun, 16 Oct 2011 15:28:31 +0200 Subject: [PATCH 018/197] Merge branch 'master' into 0.11 From de91ed89cfea575509ffd6816c4147e778465c2e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 18 Oct 2011 14:32:05 +0200 Subject: [PATCH 019/197] Merge branch 'master' into 0.11 From b22b0ce72d18024ff5ef4a920bf5f1562a60d0e4 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 27 Oct 2011 16:13:56 +0200 Subject: [PATCH 020/197] Merge branch 'master' into 0.11 From 9a478090a2190f1ca89bd37db01567857d17aff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 31 Oct 2011 14:51:32 +0000 Subject: [PATCH 021/197] Merge remote-tracking branch 'origin/master' into 0.11 From 4e5da3c213451d4dbf282480d24733d476aa69aa Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 2 Nov 2011 10:31:24 +0100 Subject: [PATCH 022/197] Merge branch 'master' into 0.11 From 03dc5e7119b8cb7e19b40b16f5a588b6d0bf7895 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 4 Nov 2011 11:01:42 +0100 Subject: [PATCH 023/197] Merge branch 'master' into 0.11 From 5e8fd1b875a5740a22c110a2172bdabf1987403d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 7 Nov 2011 10:02:00 +0100 Subject: [PATCH 024/197] Merge branch 'master' into 0.11 From 10a1f8395fb72ef4c7096c4d8c4949a477b2ef14 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 29 Sep 2011 14:22:33 +0100 Subject: [PATCH 025/197] opusenc: fix calculation of filler data size https://bugzilla.gnome.org/show_bug.cgi?id=660469 --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 75ccaaa573..9fc30757fe 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -807,7 +807,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8; if (flush && gst_adapter_available (enc->adapter) % bytes != 0) { - guint diff = gst_adapter_available (enc->adapter) % bytes; + guint diff = bytes - gst_adapter_available (enc->adapter) % bytes; GstBuffer *buf = gst_buffer_new_and_alloc (diff); memset (GST_BUFFER_DATA (buf), 0, diff); From ca14e2e0617f466aef588adb5b3cebec80a2d7a2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 5 Oct 2011 15:47:06 +0100 Subject: [PATCH 026/197] opusenc: use debug level for debug info, not error https://bugzilla.gnome.org/show_bug.cgi?id=660999 --- ext/opus/gstopusenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 9fc30757fe..f25cbca43d 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -270,7 +270,7 @@ gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) gst_caps_unref (otherpadcaps); } - GST_ERROR_OBJECT (pad, "channels=%d rate=%d frame-size=%d", + GST_DEBUG_OBJECT (pad, "channels=%d rate=%d frame-size=%d", enc->n_channels, enc->sample_rate, enc->frame_size); switch (enc->frame_size) { case 2: @@ -296,7 +296,7 @@ gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) return FALSE; break; } - GST_ERROR ("frame_samples %d", enc->frame_samples); + GST_DEBUG_OBJECT (pad, "frame_samples %d", enc->frame_samples); gst_opus_enc_setup (enc); From 2488107279285be1dde759aeaf20857a529e0f8d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 5 Oct 2011 18:25:58 +0100 Subject: [PATCH 027/197] opusenc: fix latency query This makes live 'audiosrc ! opusenc ! opusdec ! audiosink' pipelines actually work without all audio being dumped. https://bugzilla.gnome.org/show_bug.cgi?id=660999 --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f25cbca43d..51ae5c2a37 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -534,7 +534,7 @@ gst_opus_enc_src_query (GstPad * pad, GstQuery * query) GstClockTime min_latency, max_latency; gint64 latency; - if ((res = gst_pad_peer_query (pad, query))) { + if ((res = gst_pad_peer_query (enc->sinkpad, query))) { gst_query_parse_latency (query, &live, &min_latency, &max_latency); latency = gst_opus_enc_get_latency (enc); From ea30e91443f5062299e201e7ab5b0671d9054dc6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 9 Nov 2011 12:24:37 +0100 Subject: [PATCH 028/197] Merge branch 'master' into 0.11 From 15e5c673b5ce1c114e10fde42b1cf7f09add347c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 10 Nov 2011 12:14:19 +0100 Subject: [PATCH 029/197] Merge branch 'master' into 0.11 From 545b87e14c5d33c8186f7206a669a646eb16cc15 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 10 Nov 2011 17:13:40 +0000 Subject: [PATCH 030/197] opusenc: fix bandwidth property type mismatch --- ext/opus/gstopusenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 51ae5c2a37..8d40cdf812 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1009,7 +1009,7 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_int (value, enc->bitrate); break; case PROP_BANDWIDTH: - g_value_set_int (value, enc->bandwidth); + g_value_set_enum (value, enc->bandwidth); break; case PROP_FRAME_SIZE: g_value_set_int (value, enc->frame_size); @@ -1054,7 +1054,7 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, enc->bitrate = g_value_get_int (value); break; case PROP_BANDWIDTH: - enc->bandwidth = g_value_get_int (value); + enc->bandwidth = g_value_get_enum (value); break; case PROP_FRAME_SIZE: enc->frame_size = g_value_get_int (value); From fbbbe8a3aa771835bd0f2f2e48775010902bf9cb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 11 Nov 2011 10:39:17 +0100 Subject: [PATCH 031/197] Merge branch 'master' into 0.11 From 73d0c7516f36c7af7852e38a75628b66cdda4287 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 11 Nov 2011 17:46:41 +0000 Subject: [PATCH 032/197] opus: port to 0.11 --- ext/opus/gstopusdec.c | 211 +++++++++++++++++++++++++----------------- ext/opus/gstopusdec.h | 2 + ext/opus/gstopusenc.c | 152 +++++++++++++----------------- 3 files changed, 193 insertions(+), 172 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 35f501a8ab..199731efb9 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -54,11 +54,10 @@ static GstStaticPadTemplate opus_dec_src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) { S16LE }, " "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " - "channels = (int) [ 1, 2 ], " - "endianness = (int) BYTE_ORDER, " - "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") + "channels = (int) [ 1, 2 ] ") ); static GstStaticPadTemplate opus_dec_sink_factory = @@ -68,7 +67,7 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-opus") ); -GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstElement, GST_TYPE_ELEMENT); +G_DEFINE_TYPE (GstOpusDec, gst_opus_dec, GST_TYPE_ELEMENT); static gboolean opus_dec_sink_event (GstPad * pad, GstEvent * event); static GstFlowReturn opus_dec_chain (GstPad * pad, GstBuffer * buf); @@ -79,8 +78,6 @@ static GstStateChangeReturn opus_dec_change_state (GstElement * element, static gboolean opus_dec_src_event (GstPad * pad, GstEvent * event); static gboolean opus_dec_src_query (GstPad * pad, GstQuery * query); static gboolean opus_dec_sink_query (GstPad * pad, GstQuery * query); -static const GstQueryType *opus_get_src_query_types (GstPad * pad); -static const GstQueryType *opus_get_sink_query_types (GstPad * pad); static gboolean opus_dec_convert (GstPad * pad, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value); @@ -95,9 +92,11 @@ static GstFlowReturn opus_dec_chain_parse_comments (GstOpusDec * dec, #endif static void -gst_opus_dec_base_init (gpointer g_class) +gst_opus_dec_class_init (GstOpusDecClass * klass) { - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstElementClass *element_class; + + element_class = (GstElementClass *) klass; gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&opus_dec_src_factory)); @@ -107,16 +106,8 @@ gst_opus_dec_base_init (gpointer g_class) "Codec/Decoder/Audio", "decode opus streams to audio", "Sebastian Dröge "); -} -static void -gst_opus_dec_class_init (GstOpusDecClass * klass) -{ - GstElementClass *gstelement_class; - - gstelement_class = (GstElementClass *) klass; - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (opus_dec_change_state); + element_class->change_state = GST_DEBUG_FUNCPTR (opus_dec_change_state); GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, "opus decoding element"); @@ -154,27 +145,21 @@ gst_opus_dec_reset (GstOpusDec * dec) } static void -gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) +gst_opus_dec_init (GstOpusDec * dec) { dec->sinkpad = gst_pad_new_from_static_template (&opus_dec_sink_factory, "sink"); gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (opus_dec_chain)); gst_pad_set_event_function (dec->sinkpad, GST_DEBUG_FUNCPTR (opus_dec_sink_event)); - gst_pad_set_query_type_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_get_sink_query_types)); gst_pad_set_query_function (dec->sinkpad, GST_DEBUG_FUNCPTR (opus_dec_sink_query)); - gst_pad_set_setcaps_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_dec_sink_setcaps)); gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); dec->srcpad = gst_pad_new_from_static_template (&opus_dec_src_factory, "src"); gst_pad_use_fixed_caps (dec->srcpad); gst_pad_set_event_function (dec->srcpad, GST_DEBUG_FUNCPTR (opus_dec_src_event)); - gst_pad_set_query_type_function (dec->srcpad, - GST_DEBUG_FUNCPTR (opus_get_src_query_types)); gst_pad_set_query_function (dec->srcpad, GST_DEBUG_FUNCPTR (opus_dec_src_query)); gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); @@ -185,6 +170,52 @@ gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) gst_opus_dec_reset (dec); } +static GstFlowReturn +gst_opus_dec_negotiate_pool (GstOpusDec * dec, GstCaps * caps, gsize bytes) +{ + GstQuery *query; + GstBufferPool *pool = NULL; + guint size, min, max, prefix, alignment; + GstStructure *config; + + /* find a pool for the negotiated caps now */ + query = gst_query_new_allocation (caps, TRUE); + + if (gst_pad_peer_query (dec->srcpad, query)) { + GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints"); + /* we got configuration from our peer, parse them */ + gst_query_parse_allocation_params (query, &size, &min, &max, &prefix, + &alignment, &pool); + size = MAX (size, bytes); + } else { + GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints"); + size = bytes; + min = max = 0; + prefix = 0; + alignment = 0; + } + + if (pool == NULL) { + /* we did not get a pool, make one ourselves then */ + pool = gst_buffer_pool_new (); + } + + if (dec->pool) + gst_object_unref (dec->pool); + dec->pool = pool; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set (config, caps, size, min, max, prefix, alignment); + + gst_buffer_pool_set_config (pool, config); + /* and activate */ + gst_buffer_pool_set_active (pool, TRUE); + + gst_query_unref (query); + + return GST_FLOW_OK; +} + static gboolean opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps) { @@ -367,17 +398,6 @@ cleanup: return res; } -static const GstQueryType * -opus_get_sink_query_types (GstPad * pad) -{ - static const GstQueryType opus_dec_sink_query_types[] = { - GST_QUERY_CONVERT, - 0 - }; - - return opus_dec_sink_query_types; -} - static gboolean opus_dec_sink_query (GstPad * pad, GstQuery * query) { @@ -408,18 +428,6 @@ opus_dec_sink_query (GstPad * pad, GstQuery * query) return res; } -static const GstQueryType * -opus_get_src_query_types (GstPad * pad) -{ - static const GstQueryType opus_dec_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - 0 - }; - - return opus_dec_src_query_types; -} - static gboolean opus_dec_src_query (GstPad * pad, GstQuery * query) { @@ -446,7 +454,7 @@ opus_dec_src_query (GstPad * pad, GstQuery * query) } if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, - segment.last_stop, &format, &cur))) { + segment.position, &format, &cur))) { gst_query_set_position (query, format, cur); } break; @@ -456,7 +464,7 @@ opus_dec_src_query (GstPad * pad, GstQuery * query) gint64 dur; /* get duration from demuxer */ - if (!gst_pad_query_peer_duration (dec->sinkpad, &format, &dur)) + if (!gst_pad_query_peer_duration (dec->sinkpad, format, &dur)) break; gst_query_parse_duration (query, &format, NULL); @@ -540,48 +548,57 @@ opus_dec_sink_event (GstPad * pad, GstEvent * event) GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT:{ - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; + case GST_EVENT_SEGMENT:{ + const GstSegment *segment = NULL; - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); + gst_event_parse_segment (event, &segment); - if (format != GST_FORMAT_TIME) + if (segment->format != GST_FORMAT_TIME) goto newseg_wrong_format; - if (rate <= 0.0) + if (segment->rate <= 0.0) goto newseg_wrong_rate; - if (update) { +#if 0 + /* TODO: update is gone */ + if (segment->update) { /* time progressed without data, see if we can fill the gap with * some concealment data */ - if (dec->segment.last_stop < start) { + if (dec->segment.position < segment->start) { GstClockTime duration; - duration = start - dec->segment.last_stop; - opus_dec_chain_parse_data (dec, NULL, dec->segment.last_stop, + duration = segment->start - dec->segment.position; + opus_dec_chain_parse_data (dec, NULL, dec->segment.position, duration); } } +#endif /* now configure the values */ - gst_segment_set_newsegment_full (&dec->segment, update, - rate, arate, GST_FORMAT_TIME, start, stop, time); + gst_segment_copy_into (segment, &dec->segment); dec->granulepos = -1; GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]", - GST_TIME_ARGS (dec->segment.last_stop), + GST_TIME_ARGS (dec->segment.position), GST_TIME_ARGS (dec->segment.start), GST_TIME_ARGS (dec->segment.stop)); ret = gst_pad_push_event (dec->srcpad, event); break; } + + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + ret = opus_dec_sink_setcaps (pad, caps); + gst_event_unref (event); + break; + } + default: ret = gst_pad_event_default (pad, event); break; @@ -629,12 +646,10 @@ opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) GST_SECOND, dec->sample_rate); /* set caps */ - caps = gst_caps_new_simple ("audio/x-raw-int", + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, - "signed", G_TYPE_BOOLEAN, TRUE, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); + "channels", G_TYPE_INT, dec->n_channels, NULL); GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", dec->sample_rate, dec->n_channels, dec->frame_size); @@ -718,14 +733,14 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstClockTime timestamp, GstClockTime duration) { GstFlowReturn res = GST_FLOW_OK; - gint size; + gsize size, out_size; guint8 *data; GstBuffer *outbuf; gint16 *out_data; int n, err; if (timestamp != -1) { - dec->segment.last_stop = timestamp; + dec->segment.position = timestamp; dec->granulepos = -1; } @@ -737,12 +752,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, goto creation_failed; /* set caps */ - caps = gst_caps_new_simple ("audio/x-raw-int", + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, - "signed", G_TYPE_BOOLEAN, TRUE, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); + "channels", G_TYPE_INT, dec->n_channels, NULL); GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", dec->sample_rate, dec->n_channels, dec->frame_size); @@ -750,12 +763,19 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, if (!gst_pad_set_caps (dec->srcpad, caps)) GST_ERROR ("nego failure"); + /* negotiate a bufferpool */ + if ((res = + gst_opus_dec_negotiate_pool (dec, caps, + dec->frame_size * dec->n_channels * 2)) != GST_FLOW_OK) { + gst_caps_unref (caps); + goto no_bufferpool; + } + gst_caps_unref (caps); } if (buf) { - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); + data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ); GST_DEBUG_OBJECT (dec, "received buffer of size %u", size); @@ -772,21 +792,27 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, 48000)); GST_DEBUG ("channels %d", opus_packet_get_nb_channels (data)); - res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, - GST_BUFFER_OFFSET_NONE, dec->frame_samples * dec->n_channels * 2, - GST_PAD_CAPS (dec->srcpad), &outbuf); + if (gst_pad_check_reconfigure (dec->srcpad)) { + GstCaps *caps = gst_pad_get_current_caps (dec->srcpad); + gst_opus_dec_negotiate_pool (dec, caps, + dec->frame_samples * dec->n_channels * 2); + gst_caps_unref (caps); + } + res = gst_buffer_pool_acquire_buffer (dec->pool, &outbuf, NULL); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); return res; } - out_data = (gint16 *) GST_BUFFER_DATA (outbuf); + out_data = (gint16 *) gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); GST_LOG_OBJECT (dec, "decoding frame"); n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, 0); + gst_buffer_unmap (buf, data, size); if (n < 0) { + gst_buffer_unmap (outbuf, out_data, out_size); GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); return GST_FLOW_ERROR; } @@ -806,7 +832,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, dec->discont = 0; } - dec->segment.last_stop += dec->frame_duration; + dec->segment.position += dec->frame_duration; GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), @@ -814,6 +840,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, res = gst_pad_push (dec->srcpad, outbuf); + gst_buffer_unmap (outbuf, out_data, out_size); + if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); @@ -822,6 +850,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, creation_failed: GST_ERROR_OBJECT (dec, "Failed to create Opus decoder: %d", err); return GST_FLOW_ERROR; + +no_bufferpool: + GST_ERROR_OBJECT (dec, "Failed to negotiate buffer pool: %d", res); + return GST_FLOW_ERROR; } static GstFlowReturn @@ -866,7 +898,9 @@ opus_dec_change_state (GstElement * element, GstStateChange transition) break; } - ret = parent_class->change_state (element, transition); + ret = + GST_ELEMENT_CLASS (gst_opus_dec_parent_class)->change_state (element, + transition); if (ret != GST_STATE_CHANGE_SUCCESS) return ret; @@ -875,6 +909,11 @@ opus_dec_change_state (GstElement * element, GstStateChange transition) break; case GST_STATE_CHANGE_PAUSED_TO_READY: gst_opus_dec_reset (dec); + if (dec->pool) { + gst_buffer_pool_set_active (dec->pool, FALSE); + gst_object_unref (dec->pool); + dec->pool = NULL; + } break; case GST_STATE_CHANGE_READY_TO_NULL: break; diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 886a907532..57db8847e3 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -64,6 +64,8 @@ struct _GstOpusDec { int sample_rate; int n_channels; + + GstBufferPool *pool; }; struct _GstOpusDecClass { diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 8d40cdf812..484a84b7d5 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -82,11 +82,10 @@ gst_opus_enc_bandwidth_get_type (void) static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-int, " + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) { S16LE }, " "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " - "channels = (int) [ 1, 2 ], " - "endianness = (int) BYTE_ORDER, " - "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16") + "channels = (int) [ 1, 2 ] ") ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", @@ -138,31 +137,19 @@ static GstStateChangeReturn gst_opus_enc_change_state (GstElement * element, static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush); -static void -gst_opus_enc_setup_interfaces (GType opusenc_type) -{ - static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; - const GInterfaceInfo preset_interface_info = { - NULL, /* interface_init */ - NULL, /* interface_finalize */ - NULL /* interface_data */ - }; - - g_type_add_interface_static (opusenc_type, GST_TYPE_TAG_SETTER, - &tag_setter_info); - g_type_add_interface_static (opusenc_type, GST_TYPE_PRESET, - &preset_interface_info); - - GST_DEBUG_CATEGORY_INIT (opusenc_debug, "opusenc", 0, "Opus encoder"); -} - -GST_BOILERPLATE_FULL (GstOpusEnc, gst_opus_enc, GstElement, GST_TYPE_ELEMENT, - gst_opus_enc_setup_interfaces); +#define gst_opus_enc_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstOpusEnc, gst_opus_enc, GST_TYPE_ELEMENT, + G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL); + G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL)); static void -gst_opus_enc_base_init (gpointer g_class) +gst_opus_enc_class_init (GstOpusEncClass * klass) { - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GObjectClass *gobject_class; + GstElementClass *element_class; + + gobject_class = (GObjectClass *) klass; + element_class = (GstElementClass *) klass; gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_factory)); @@ -172,16 +159,6 @@ gst_opus_enc_base_init (gpointer g_class) "Codec/Encoder/Audio", "Encodes audio in Opus format", "Sebastian Dröge "); -} - -static void -gst_opus_enc_class_init (GstOpusEncClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; gobject_class->set_property = gst_opus_enc_set_property; gobject_class->get_property = gst_opus_enc_get_property; @@ -230,8 +207,9 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize); - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_opus_enc_change_state); + element_class->change_state = GST_DEBUG_FUNCPTR (gst_opus_enc_change_state); + + GST_DEBUG_CATEGORY_INIT (opusenc_debug, "opusenc", 0, "Opus encoder"); } static void @@ -305,13 +283,13 @@ gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) static GstCaps * -gst_opus_enc_sink_getcaps (GstPad * pad) +gst_opus_enc_sink_getcaps (GstPad * pad, GstCaps * filter) { GstCaps *caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); GstCaps *peercaps = NULL; GstOpusEnc *enc = GST_OPUS_ENC (gst_pad_get_parent_element (pad)); - peercaps = gst_pad_peer_get_caps (enc->srcpad); + peercaps = gst_pad_peer_get_caps (enc->srcpad, NULL); if (peercaps) { if (!gst_caps_is_empty (peercaps) && !gst_caps_is_any (peercaps)) { @@ -332,6 +310,13 @@ gst_opus_enc_sink_getcaps (GstPad * pad) gst_object_unref (enc); + if (filter) { + GstCaps *new_caps = + gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (caps); + caps = new_caps; + } + return caps; } @@ -449,20 +434,6 @@ gst_opus_enc_get_latency (GstOpusEnc * enc) enc->sample_rate); } -static const GstQueryType * -gst_opus_enc_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_opus_enc_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - GST_QUERY_LATENCY, - 0 - }; - - return gst_opus_enc_src_query_types; -} - static gboolean gst_opus_enc_src_query (GstPad * pad, GstQuery * query) { @@ -478,17 +449,17 @@ gst_opus_enc_src_query (GstPad * pad, GstQuery * query) gint64 pos, val; gst_query_parse_position (query, &req_fmt, NULL); - if ((res = gst_pad_query_peer_position (enc->sinkpad, &req_fmt, &val))) { + if ((res = gst_pad_query_peer_position (enc->sinkpad, req_fmt, &val))) { gst_query_set_position (query, req_fmt, val); break; } fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_peer_position (enc->sinkpad, &fmt, &pos))) + if (!(res = gst_pad_query_peer_position (enc->sinkpad, fmt, &pos))) break; if ((res = - gst_pad_query_peer_convert (enc->sinkpad, fmt, pos, &req_fmt, + gst_pad_query_peer_convert (enc->sinkpad, fmt, pos, req_fmt, &val))) gst_query_set_position (query, req_fmt, val); @@ -500,17 +471,17 @@ gst_opus_enc_src_query (GstPad * pad, GstQuery * query) gint64 dur, val; gst_query_parse_duration (query, &req_fmt, NULL); - if ((res = gst_pad_query_peer_duration (enc->sinkpad, &req_fmt, &val))) { + if ((res = gst_pad_query_peer_duration (enc->sinkpad, req_fmt, &val))) { gst_query_set_duration (query, req_fmt, val); break; } fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_peer_duration (enc->sinkpad, &fmt, &dur))) + if (!(res = gst_pad_query_peer_duration (enc->sinkpad, fmt, &dur))) break; if ((res = - gst_pad_query_peer_convert (enc->sinkpad, fmt, dur, &req_fmt, + gst_pad_query_peer_convert (enc->sinkpad, fmt, dur, req_fmt, &val))) { gst_query_set_duration (query, req_fmt, val); } @@ -589,7 +560,7 @@ error: } static void -gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) +gst_opus_enc_init (GstOpusEnc * enc) { enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad); @@ -597,8 +568,6 @@ gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) GST_DEBUG_FUNCPTR (gst_opus_enc_sinkevent)); gst_pad_set_chain_function (enc->sinkpad, GST_DEBUG_FUNCPTR (gst_opus_enc_chain)); - gst_pad_set_setcaps_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sink_setcaps)); gst_pad_set_getcaps_function (enc->sinkpad, GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps)); gst_pad_set_query_function (enc->sinkpad, @@ -607,8 +576,6 @@ gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); gst_pad_set_query_function (enc->srcpad, GST_DEBUG_FUNCPTR (gst_opus_enc_src_query)); - gst_pad_set_query_type_function (enc->srcpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_get_query_types)); gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); enc->n_channels = -1; @@ -708,9 +675,9 @@ encoder_creation_failed: static GstFlowReturn gst_opus_enc_push_buffer (GstOpusEnc * enc, GstBuffer * buffer) { - guint size; + gsize size; - size = GST_BUFFER_SIZE (buffer); + size = gst_buffer_get_size (buffer); enc->bytes_out += size; @@ -785,6 +752,16 @@ gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event) res = gst_pad_event_default (pad, event); break; } + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + res = gst_opus_enc_sink_setcaps (pad, caps); + gst_event_unref (event); + break; + } + default: res = gst_pad_event_default (pad, event); break; @@ -810,21 +787,20 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) guint diff = bytes - gst_adapter_available (enc->adapter) % bytes; GstBuffer *buf = gst_buffer_new_and_alloc (diff); - memset (GST_BUFFER_DATA (buf), 0, diff); + gst_buffer_memset (buf, 0, 0, diff); gst_adapter_push (enc->adapter, buf); } while (gst_adapter_available (enc->adapter) >= bytes) { gint16 *data; - gint outsize; + gint encoded_size; + unsigned char *out_data; + gsize out_size; GstBuffer *outbuf; - ret = gst_pad_alloc_buffer_and_set_caps (enc->srcpad, - GST_BUFFER_OFFSET_NONE, bytes_per_packet, GST_PAD_CAPS (enc->srcpad), - &outbuf); - - if (GST_FLOW_OK != ret) + outbuf = gst_buffer_new_and_alloc (bytes_per_packet); + if (!outbuf) goto done; data = (gint16 *) gst_adapter_take (enc->adapter, bytes); @@ -833,18 +809,20 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", enc->frame_samples, bytes); - outsize = opus_encode (enc->state, data, enc->frame_samples, - GST_BUFFER_DATA (outbuf), bytes_per_packet); + out_data = gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); + encoded_size = opus_encode (enc->state, data, enc->frame_samples, + out_data, bytes_per_packet); + gst_buffer_unmap (outbuf, out_data, out_size); g_free (data); - if (outsize < 0) { - GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); + if (encoded_size < 0) { + GST_ERROR_OBJECT (enc, "Encoding failed: %d", encoded_size); ret = GST_FLOW_ERROR; goto done; - } else if (outsize != bytes_per_packet) { + } else if (encoded_size != bytes_per_packet) { GST_WARNING_OBJECT (enc, - "Encoded size %d is different from %d bytes per packet", outsize, + "Encoded size %d is different from %d bytes per packet", encoded_size, bytes_per_packet); ret = GST_FLOW_ERROR; goto done; @@ -888,7 +866,8 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) if (!enc->header_sent) { GstCaps *caps; - caps = gst_pad_get_caps (enc->srcpad); + caps = gst_pad_get_caps (enc->srcpad, NULL); + caps = gst_caps_copy (caps); gst_caps_set_simple (caps, "rate", G_TYPE_INT, enc->sample_rate, "channels", G_TYPE_INT, enc->n_channels, @@ -899,11 +878,13 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) GST_LOG_OBJECT (enc, "rate=%d channels=%d frame-size=%d", enc->sample_rate, enc->n_channels, enc->frame_size); gst_pad_set_caps (enc->srcpad, caps); + gst_caps_unref (caps); enc->header_sent = TRUE; } - GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf)); + GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", + gst_buffer_get_size (buf)); /* Save the timestamp of the first buffer. This will be later * used as offset for all following buffers */ @@ -931,13 +912,12 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) diff_bytes = GST_CLOCK_TIME_TO_FRAMES (diff, enc->sample_rate) * enc->n_channels * 2; - if (diff_bytes >= GST_BUFFER_SIZE (buf)) { + if (diff_bytes >= gst_buffer_get_size (buf)) { gst_buffer_unref (buf); return GST_FLOW_OK; } - buf = gst_buffer_make_metadata_writable (buf); - GST_BUFFER_DATA (buf) += diff_bytes; - GST_BUFFER_SIZE (buf) -= diff_bytes; + buf = gst_buffer_make_writable (buf); + gst_buffer_resize (buf, diff_bytes, gst_buffer_get_size (buf) - diff_bytes); GST_BUFFER_TIMESTAMP (buf) += diff; if (GST_BUFFER_DURATION_IS_VALID (buf)) From dc1e8fef486de438c9614a494a4f2f4343851caf Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 14 Nov 2011 13:41:58 +0000 Subject: [PATCH 033/197] opus: port to encoder/decoder base classes --- ext/opus/Makefile.am | 2 + ext/opus/gstopusdec.c | 831 +++++++++--------------------------- ext/opus/gstopusdec.h | 14 +- ext/opus/gstopusenc.c | 964 +++++++++++++++--------------------------- ext/opus/gstopusenc.h | 24 +- 5 files changed, 548 insertions(+), 1287 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index aa50ba96ef..57e1692645 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -2,10 +2,12 @@ plugin_LTLIBRARIES = libgstopus.la libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c libgstopus_la_CFLAGS = \ + -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_CFLAGS) \ $(OPUS_CFLAGS) libgstopus_la_LIBADD = \ + -lgstaudio-$(GST_MAJORMINOR) \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 199731efb9..8aa99466b2 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -67,37 +67,31 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-opus") ); -G_DEFINE_TYPE (GstOpusDec, gst_opus_dec, GST_TYPE_ELEMENT); - -static gboolean opus_dec_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn opus_dec_chain (GstPad * pad, GstBuffer * buf); -static gboolean opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps); -static GstStateChangeReturn opus_dec_change_state (GstElement * element, - GstStateChange transition); - -static gboolean opus_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean opus_dec_src_query (GstPad * pad, GstQuery * query); -static gboolean opus_dec_sink_query (GstPad * pad, GstQuery * query); -static gboolean opus_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); +G_DEFINE_TYPE (GstOpusDec, gst_opus_dec, GST_TYPE_AUDIO_DECODER); +static gboolean gst_opus_dec_start (GstAudioDecoder * dec); +static gboolean gst_opus_dec_stop (GstAudioDecoder * dec); +static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec, + GstBuffer * buffer); +static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, + GstCaps * caps); static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstClockTime timestamp, GstClockTime duration); -static GstFlowReturn opus_dec_chain_parse_header (GstOpusDec * dec, - GstBuffer * buf); -#if 0 -static GstFlowReturn opus_dec_chain_parse_comments (GstOpusDec * dec, - GstBuffer * buf); -#endif static void gst_opus_dec_class_init (GstOpusDecClass * klass) { + GstAudioDecoderClass *adclass; GstElementClass *element_class; + adclass = (GstAudioDecoderClass *) klass; element_class = (GstElementClass *) klass; + adclass->start = GST_DEBUG_FUNCPTR (gst_opus_dec_start); + adclass->stop = GST_DEBUG_FUNCPTR (gst_opus_dec_stop); + adclass->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_dec_handle_frame); + adclass->set_format = GST_DEBUG_FUNCPTR (gst_opus_dec_set_format); + gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&opus_dec_src_factory)); gst_element_class_add_pad_template (element_class, @@ -107,8 +101,6 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) "decode opus streams to audio", "Sebastian Dröge "); - element_class->change_state = GST_DEBUG_FUNCPTR (opus_dec_change_state); - GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, "opus decoding element"); } @@ -116,8 +108,6 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) static void gst_opus_dec_reset (GstOpusDec * dec) { - gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED); - dec->granulepos = -1; dec->packetno = 0; dec->frame_size = 0; dec->frame_samples = 960; @@ -126,50 +116,43 @@ gst_opus_dec_reset (GstOpusDec * dec) opus_decoder_destroy (dec->state); dec->state = NULL; } -#if 0 - if (dec->mode) { - opus_mode_destroy (dec->mode); - dec->mode = NULL; - } -#endif gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); - g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->extra_headers); - dec->extra_headers = NULL; - -#if 0 - memset (&dec->header, 0, sizeof (dec->header)); -#endif } static void gst_opus_dec_init (GstOpusDec * dec) { - dec->sinkpad = - gst_pad_new_from_static_template (&opus_dec_sink_factory, "sink"); - gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (opus_dec_chain)); - gst_pad_set_event_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_dec_sink_event)); - gst_pad_set_query_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_dec_sink_query)); - gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - - dec->srcpad = gst_pad_new_from_static_template (&opus_dec_src_factory, "src"); - gst_pad_use_fixed_caps (dec->srcpad); - gst_pad_set_event_function (dec->srcpad, - GST_DEBUG_FUNCPTR (opus_dec_src_event)); - gst_pad_set_query_function (dec->srcpad, - GST_DEBUG_FUNCPTR (opus_dec_src_query)); - gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - dec->sample_rate = 48000; dec->n_channels = 2; gst_opus_dec_reset (dec); } +static gboolean +gst_opus_dec_start (GstAudioDecoder * dec) +{ + GstOpusDec *odec = GST_OPUS_DEC (dec); + + gst_opus_dec_reset (odec); + + /* we know about concealment */ + gst_audio_decoder_set_plc_aware (dec, TRUE); + + return TRUE; +} + +static gboolean +gst_opus_dec_stop (GstAudioDecoder * dec) +{ + GstOpusDec *odec = GST_OPUS_DEC (dec); + + gst_opus_dec_reset (odec); + + return TRUE; +} + static GstFlowReturn gst_opus_dec_negotiate_pool (GstOpusDec * dec, GstCaps * caps, gsize bytes) { @@ -181,7 +164,7 @@ gst_opus_dec_negotiate_pool (GstOpusDec * dec, GstCaps * caps, gsize bytes) /* find a pool for the negotiated caps now */ query = gst_query_new_allocation (caps, TRUE); - if (gst_pad_peer_query (dec->srcpad, query)) { + if (gst_pad_peer_query (GST_AUDIO_DECODER_SRC_PAD (dec), query)) { GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints"); /* we got configuration from our peer, parse them */ gst_query_parse_allocation_params (query, &size, &min, &max, &prefix, @@ -216,517 +199,17 @@ gst_opus_dec_negotiate_pool (GstOpusDec * dec, GstCaps * caps, gsize bytes) return GST_FLOW_OK; } -static gboolean -opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps) +static GstFlowReturn +gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { - GstOpusDec *dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - gboolean ret = TRUE; - GstStructure *s; - const GValue *streamheader; - - GST_DEBUG_OBJECT (pad, "Setting sink caps to %" GST_PTR_FORMAT, caps); - - s = gst_caps_get_structure (caps, 0); - if ((streamheader = gst_structure_get_value (s, "streamheader")) && - G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && - gst_value_array_get_size (streamheader) >= 2) { - const GValue *header; - GstBuffer *buf; - GstFlowReturn res = GST_FLOW_OK; - - header = gst_value_array_get_value (streamheader, 0); - if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { - buf = gst_value_get_buffer (header); - res = opus_dec_chain_parse_header (dec, buf); - if (res != GST_FLOW_OK) - goto done; - gst_buffer_replace (&dec->streamheader, buf); - } -#if 0 - vorbiscomment = gst_value_array_get_value (streamheader, 1); - if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { - buf = gst_value_get_buffer (vorbiscomment); - res = opus_dec_chain_parse_comments (dec, buf); - if (res != GST_FLOW_OK) - goto done; - gst_buffer_replace (&dec->vorbiscomment, buf); - } -#endif - - g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->extra_headers); - dec->extra_headers = NULL; - - if (gst_value_array_get_size (streamheader) > 2) { - gint i, n; - - n = gst_value_array_get_size (streamheader); - for (i = 2; i < n; i++) { - header = gst_value_array_get_value (streamheader, i); - buf = gst_value_get_buffer (header); - dec->extra_headers = - g_list_prepend (dec->extra_headers, gst_buffer_ref (buf)); - } - } - } - - if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { - GST_WARNING_OBJECT (dec, "Frame size not included in caps"); - } - if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { - GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); - } - if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { - GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); - } - switch (dec->frame_size) { - case 2: - dec->frame_samples = dec->sample_rate / 400; - break; - case 5: - dec->frame_samples = dec->sample_rate / 200; - break; - case 10: - dec->frame_samples = dec->sample_rate / 100; - break; - case 20: - dec->frame_samples = dec->sample_rate / 50; - break; - case 40: - dec->frame_samples = dec->sample_rate / 25; - break; - case 60: - dec->frame_samples = 3 * dec->sample_rate / 50; - break; - default: - GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); - break; - } - - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, - GST_SECOND, dec->sample_rate); - - GST_INFO_OBJECT (dec, - "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" - GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, - dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); - -done: - gst_object_unref (dec); - return ret; -} - -static gboolean -opus_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstOpusDec *dec; - guint64 scale = 1; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - if (dec->packetno < 1) { - res = FALSE; - goto cleanup; - } - - if (src_format == *dest_format) { - *dest_value = src_value; - res = TRUE; - goto cleanup; - } - - if (pad == dec->sinkpad && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) { - res = FALSE; - goto cleanup; - } - - switch (src_format) { - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = sizeof (gint16) * dec->n_channels; - case GST_FORMAT_DEFAULT: - *dest_value = - gst_util_uint64_scale_int (scale * src_value, - dec->sample_rate, GST_SECOND); - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * sizeof (gint16) * dec->n_channels; - break; - case GST_FORMAT_TIME: - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, - dec->sample_rate); - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = src_value / (sizeof (gint16) * dec->n_channels); - break; - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, - dec->sample_rate * sizeof (gint16) * dec->n_channels); - break; - default: - res = FALSE; - break; - } - break; - default: - res = FALSE; - break; - } - -cleanup: - gst_object_unref (dec); - return res; -} - -static gboolean -opus_dec_sink_query (GstPad * pad, GstQuery * query) -{ - GstOpusDec *dec; - gboolean res; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - res = opus_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val); - if (res) { - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (dec); - return res; -} - -static gboolean -opus_dec_src_query (GstPad * pad, GstQuery * query) -{ - GstOpusDec *dec; - gboolean res = FALSE; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION:{ - GstSegment segment; - GstFormat format; - gint64 cur; - - gst_query_parse_position (query, &format, NULL); - - GST_PAD_STREAM_LOCK (dec->sinkpad); - segment = dec->segment; - GST_PAD_STREAM_UNLOCK (dec->sinkpad); - - if (segment.format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (dec, "segment not initialised yet"); - break; - } - - if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, - segment.position, &format, &cur))) { - gst_query_set_position (query, format, cur); - } - break; - } - case GST_QUERY_DURATION:{ - GstFormat format = GST_FORMAT_TIME; - gint64 dur; - - /* get duration from demuxer */ - if (!gst_pad_query_peer_duration (dec->sinkpad, format, &dur)) - break; - - gst_query_parse_duration (query, &format, NULL); - - /* and convert it into the requested format */ - if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, - dur, &format, &dur))) { - gst_query_set_duration (query, format, dur); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (dec); - return res; -} - -static gboolean -opus_dec_src_event (GstPad * pad, GstEvent * event) -{ - gboolean res = FALSE; - GstOpusDec *dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK:{ - GstFormat format, tformat; - gdouble rate; - GstEvent *real_seek; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 tcur, tstop; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - - /* we have to ask our peer to seek to time here as we know - * nothing about how to generate a granulepos from the src - * formats or anything. - * - * First bring the requested format to time - */ - tformat = GST_FORMAT_TIME; - if (!(res = opus_dec_convert (pad, format, cur, &tformat, &tcur))) - break; - if (!(res = opus_dec_convert (pad, format, stop, &tformat, &tstop))) - break; - - /* then seek with time on the peer */ - real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, - flags, cur_type, tcur, stop_type, tstop); - - GST_LOG_OBJECT (dec, "seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (tcur)); - - res = gst_pad_push_event (dec->sinkpad, real_seek); - gst_event_unref (event); - break; - } - default: - res = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (dec); - return res; -} - -static gboolean -opus_dec_sink_event (GstPad * pad, GstEvent * event) -{ - GstOpusDec *dec; - gboolean ret = FALSE; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEGMENT:{ - const GstSegment *segment = NULL; - - gst_event_parse_segment (event, &segment); - - if (segment->format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - if (segment->rate <= 0.0) - goto newseg_wrong_rate; - -#if 0 - /* TODO: update is gone */ - if (segment->update) { - /* time progressed without data, see if we can fill the gap with - * some concealment data */ - if (dec->segment.position < segment->start) { - GstClockTime duration; - - duration = segment->start - dec->segment.position; - opus_dec_chain_parse_data (dec, NULL, dec->segment.position, - duration); - } - } -#endif - - /* now configure the values */ - gst_segment_copy_into (segment, &dec->segment); - - dec->granulepos = -1; - - GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%" - GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]", - GST_TIME_ARGS (dec->segment.position), - GST_TIME_ARGS (dec->segment.start), - GST_TIME_ARGS (dec->segment.stop)); - - ret = gst_pad_push_event (dec->srcpad, event); - break; - } - - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - ret = opus_dec_sink_setcaps (pad, caps); - gst_event_unref (event); - break; - } - - default: - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (dec); - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); - gst_object_unref (dec); - return FALSE; - } -newseg_wrong_rate: - { - GST_DEBUG_OBJECT (dec, "negative rates not supported yet"); - gst_object_unref (dec); - return FALSE; - } + return GST_FLOW_OK; } static GstFlowReturn -opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) +gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) { - GstCaps *caps; - int err; - -#if 0 - dec->samples_per_frame = opus_packet_get_samples_per_frame ( - (const unsigned char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); -#endif - -#if 0 - if (memcmp (dec->header.codec_id, "OPUS ", 8) != 0) - goto invalid_header; -#endif - - dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); - if (!dec->state || err != OPUS_OK) - goto init_failed; - - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, - GST_SECOND, dec->sample_rate); - - /* set caps */ - caps = gst_caps_new_simple ("audio/x-raw", - "format", G_TYPE_STRING, "S16LE", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, NULL); - - GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", - dec->sample_rate, dec->n_channels, dec->frame_size); - - if (!gst_pad_set_caps (dec->srcpad, caps)) - goto nego_failed; - - gst_caps_unref (caps); - return GST_FLOW_OK; - - /* ERRORS */ -#if 0 -invalid_header: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("Invalid header")); - return GST_FLOW_ERROR; - } -mode_init_failed: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("Mode initialization failed: %d", error)); - return GST_FLOW_ERROR; - } -#endif -init_failed: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("couldn't initialize decoder")); - return GST_FLOW_ERROR; - } -nego_failed: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("couldn't negotiate format")); - gst_caps_unref (caps); - return GST_FLOW_NOT_NEGOTIATED; - } -} - -#if 0 -static GstFlowReturn -opus_dec_chain_parse_comments (GstOpusDec * dec, GstBuffer * buf) -{ - GstTagList *list; - gchar *encoder = NULL; - - list = gst_tag_list_from_vorbiscomment_buffer (buf, NULL, 0, &encoder); - - if (!list) { - GST_WARNING_OBJECT (dec, "couldn't decode comments"); - list = gst_tag_list_new (); - } - - if (encoder) { - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER, encoder, NULL); - } - - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_AUDIO_CODEC, "Opus", NULL); - - if (dec->header.bytes_per_packet > 0) { - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_BITRATE, (guint) dec->header.bytes_per_packet * 8, NULL); - } - - GST_INFO_OBJECT (dec, "tags: %" GST_PTR_FORMAT, list); - - gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, list); - - g_free (encoder); - g_free (ver); - return GST_FLOW_OK; } -#endif static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, @@ -739,11 +222,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, gint16 *out_data; int n, err; - if (timestamp != -1) { - dec->segment.position = timestamp; - dec->granulepos = -1; - } - if (dec->state == NULL) { GstCaps *caps; @@ -760,7 +238,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", dec->sample_rate, dec->n_channels, dec->frame_size); - if (!gst_pad_set_caps (dec->srcpad, caps)) + if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) GST_ERROR ("nego failure"); /* negotiate a bufferpool */ @@ -792,8 +270,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, 48000)); GST_DEBUG ("channels %d", opus_packet_get_nb_channels (data)); - if (gst_pad_check_reconfigure (dec->srcpad)) { - GstCaps *caps = gst_pad_get_current_caps (dec->srcpad); + if (gst_pad_check_reconfigure (GST_AUDIO_DECODER_SRC_PAD (dec))) { + GstCaps *caps = gst_pad_get_current_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); gst_opus_dec_negotiate_pool (dec, caps, dec->frame_samples * dec->n_channels * 2); gst_caps_unref (caps); @@ -818,8 +296,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, } if (!GST_CLOCK_TIME_IS_VALID (timestamp)) { - timestamp = gst_util_uint64_scale_int (dec->granulepos - dec->frame_size, - GST_SECOND, dec->sample_rate); + GST_WARNING_OBJECT (dec, "No timestamp in -> no timestamp out"); } GST_DEBUG_OBJECT (dec, "timestamp=%" GST_TIME_FORMAT, @@ -827,18 +304,12 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf); - if (dec->discont) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - dec->discont = 0; - } - - dec->segment.position += dec->frame_duration; GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (dec->frame_duration)); - res = gst_pad_push (dec->srcpad, outbuf); + res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); gst_buffer_unmap (outbuf, out_data, out_size); @@ -856,70 +327,182 @@ no_bufferpool: return GST_FLOW_ERROR; } -static GstFlowReturn -opus_dec_chain (GstPad * pad, GstBuffer * buf) +static gint +gst_opus_dec_get_frame_samples (GstOpusDec * dec) { - GstFlowReturn res; - GstOpusDec *dec; + gint frame_samples = 0; + switch (dec->frame_size) { + case 2: + frame_samples = dec->sample_rate / 400; + break; + case 5: + frame_samples = dec->sample_rate / 200; + break; + case 10: + frame_samples = dec->sample_rate / 100; + break; + case 20: + frame_samples = dec->sample_rate / 50; + break; + case 40: + frame_samples = dec->sample_rate / 25; + break; + case 60: + frame_samples = 3 * dec->sample_rate / 50; + break; + default: + GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); + frame_samples = 0; + break; + } + return frame_samples; +} - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - GST_LOG_OBJECT (pad, - "Got buffer ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); +static gboolean +gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) +{ + GstOpusDec *dec = GST_OPUS_DEC (bdec); + gboolean ret = TRUE; + GstStructure *s; + const GValue *streamheader; - if (GST_BUFFER_IS_DISCONT (buf)) { - dec->discont = TRUE; + GST_DEBUG_OBJECT (dec, "set_format: %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + if ((streamheader = gst_structure_get_value (s, "streamheader")) && + G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && + gst_value_array_get_size (streamheader) >= 2) { + const GValue *header, *vorbiscomment; + GstBuffer *buf; + GstFlowReturn res = GST_FLOW_OK; + + header = gst_value_array_get_value (streamheader, 0); + if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { + buf = gst_value_get_buffer (header); + res = gst_opus_dec_parse_header (dec, buf); + if (res != GST_FLOW_OK) + goto done; + gst_buffer_replace (&dec->streamheader, buf); + } + + vorbiscomment = gst_value_array_get_value (streamheader, 1); + if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { + buf = gst_value_get_buffer (vorbiscomment); + res = gst_opus_dec_parse_comments (dec, buf); + if (res != GST_FLOW_OK) + goto done; + gst_buffer_replace (&dec->vorbiscomment, buf); + } } - res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), - GST_BUFFER_DURATION (buf)); + if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { + GST_WARNING_OBJECT (dec, "Frame size not included in caps"); + } + if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { + GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); + } + if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { + GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); + } -//done: - dec->packetno++; + dec->frame_samples = gst_opus_dec_get_frame_samples (dec); + dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, + GST_SECOND, dec->sample_rate); - gst_buffer_unref (buf); - gst_object_unref (dec); + GST_INFO_OBJECT (dec, + "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" + GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, + dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); + + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", + "rate", G_TYPE_INT, dec->sample_rate, + "channels", G_TYPE_INT, dec->n_channels, NULL); + gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (dec), caps); + gst_caps_unref (caps); + +done: + return ret; +} + +static gboolean +memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) +{ + gsize size1, size2; + gpointer data1; + gboolean res; + + size1 = gst_buffer_get_size (buf1); + size2 = gst_buffer_get_size (buf2); + + if (size1 != size2) + return FALSE; + + data1 = gst_buffer_map (buf1, NULL, NULL, GST_MAP_READ); + res = gst_buffer_memcmp (buf2, 0, data1, size1) == 0; + gst_buffer_unmap (buf1, data1, size1); return res; } -static GstStateChangeReturn -opus_dec_change_state (GstElement * element, GstStateChange transition) + +static GstFlowReturn +gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) { - GstStateChangeReturn ret; - GstOpusDec *dec = GST_OPUS_DEC (element); + GstFlowReturn res; + GstOpusDec *dec; - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - case GST_STATE_CHANGE_READY_TO_PAUSED: - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - default: - break; - } + /* no fancy draining */ + if (G_UNLIKELY (!buf)) + return GST_FLOW_OK; - ret = - GST_ELEMENT_CLASS (gst_opus_dec_parent_class)->change_state (element, - transition); - if (ret != GST_STATE_CHANGE_SUCCESS) - return ret; + dec = GST_OPUS_DEC (adec); + GST_LOG_OBJECT (dec, + "Got buffer ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_opus_dec_reset (dec); - if (dec->pool) { - gst_buffer_pool_set_active (dec->pool, FALSE); - gst_object_unref (dec->pool); - dec->pool = NULL; + /* If we have the streamheader and vorbiscomment from the caps already + * ignore them here */ + if (dec->streamheader && dec->vorbiscomment) { + if (memcmp_buffers (dec->streamheader, buf)) { + GST_DEBUG_OBJECT (dec, "found streamheader"); + gst_audio_decoder_finish_frame (adec, NULL, 1); + res = GST_FLOW_OK; + } else if (memcmp_buffers (dec->vorbiscomment, buf)) { + GST_DEBUG_OBJECT (dec, "found vorbiscomments"); + gst_audio_decoder_finish_frame (adec, NULL, 1); + res = GST_FLOW_OK; + } else { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + } + } else { + /* Otherwise fall back to packet counting and assume that the + * first two packets are the headers. */ + switch (dec->packetno) { + case 0: + GST_DEBUG_OBJECT (dec, "counted streamheader"); + res = GST_FLOW_OK; + res = gst_opus_dec_parse_header (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + break; + case 1: + GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); + res = GST_FLOW_OK; + res = gst_opus_dec_parse_comments (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + break; + default: + { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + break; } - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; + } } - return ret; + dec->packetno++; + + return res; } diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 57db8847e3..c29b6b1f1f 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -22,6 +22,7 @@ #define __GST_OPUS_DEC_H__ #include +#include #include G_BEGIN_DECLS @@ -41,11 +42,7 @@ typedef struct _GstOpusDec GstOpusDec; typedef struct _GstOpusDecClass GstOpusDecClass; struct _GstOpusDec { - GstElement element; - - /* pads */ - GstPad *sinkpad; - GstPad *srcpad; + GstAudioDecoder element; OpusDecoder *state; int frame_samples; @@ -54,13 +51,8 @@ struct _GstOpusDec { GstClockTime frame_duration; guint64 packetno; - GstSegment segment; /* STREAM LOCK */ - gint64 granulepos; /* -1 = needs to be set from current time */ - gboolean discont; - GstBuffer *streamheader; GstBuffer *vorbiscomment; - GList *extra_headers; int sample_rate; int n_channels; @@ -69,7 +61,7 @@ struct _GstOpusDec { }; struct _GstOpusDecClass { - GstElementClass parent_class; + GstAudioDecoderClass parent_class; }; GType gst_opus_dec_get_type (void); diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 484a84b7d5..363eb6976e 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -47,6 +47,7 @@ #include #include +#include #include #include "gstopusenc.h" @@ -79,11 +80,13 @@ gst_opus_enc_bandwidth_get_type (void) return id; } +#define FORMAT_STR GST_AUDIO_NE(S16) + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " - "format = (string) { S16LE }, " + "format = (string) " FORMAT_STR ", " "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " "channels = (int) [ 1, 2 ] ") ); @@ -124,21 +127,29 @@ enum static void gst_opus_enc_finalize (GObject * object); -static gboolean gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_opus_enc_chain (GstPad * pad, GstBuffer * buf); +static gboolean gst_opus_enc_sink_event (GstAudioEncoder * benc, + GstEvent * event); static gboolean gst_opus_enc_setup (GstOpusEnc * enc); static void gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_opus_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_opus_enc_change_state (GstElement * element, - GstStateChange transition); -static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush); +static gboolean gst_opus_enc_start (GstAudioEncoder * benc); +static gboolean gst_opus_enc_stop (GstAudioEncoder * benc); +static gboolean gst_opus_enc_set_format (GstAudioEncoder * benc, + GstAudioInfo * info); +static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc, + GstBuffer * buf); +static GstFlowReturn gst_opus_enc_pre_push (GstAudioEncoder * benc, + GstBuffer ** buffer); + +static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf); +static gint64 gst_opus_enc_get_latency (GstOpusEnc * enc); #define gst_opus_enc_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstOpusEnc, gst_opus_enc, GST_TYPE_ELEMENT, +G_DEFINE_TYPE_WITH_CODE (GstOpusEnc, gst_opus_enc, GST_TYPE_AUDIO_ENCODER, G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL); G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL)); @@ -147,9 +158,11 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) { GObjectClass *gobject_class; GstElementClass *element_class; + GstAudioEncoderClass *base_class; gobject_class = (GObjectClass *) klass; element_class = (GstElementClass *) klass; + base_class = (GstAudioEncoderClass *) klass; gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_factory)); @@ -160,6 +173,13 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) "Encodes audio in Opus format", "Sebastian Dröge "); + base_class->start = GST_DEBUG_FUNCPTR (gst_opus_enc_start); + base_class->stop = GST_DEBUG_FUNCPTR (gst_opus_enc_stop); + base_class->set_format = GST_DEBUG_FUNCPTR (gst_opus_enc_set_format); + base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_enc_handle_frame); + base_class->pre_push = GST_DEBUG_FUNCPTR (gst_opus_enc_pre_push); + base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); + gobject_class->set_property = gst_opus_enc_set_property; gobject_class->get_property = gst_opus_enc_get_property; @@ -207,8 +227,6 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize); - element_class->change_state = GST_DEBUG_FUNCPTR (gst_opus_enc_change_state); - GST_DEBUG_CATEGORY_INIT (opusenc_debug, "opusenc", 0, "Opus encoder"); } @@ -219,364 +237,16 @@ gst_opus_enc_finalize (GObject * object) enc = GST_OPUS_ENC (object); - g_object_unref (enc->adapter); - + GST_DEBUG_OBJECT (enc, "finalize"); G_OBJECT_CLASS (parent_class)->finalize (object); } -static gboolean -gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstOpusEnc *enc; - GstStructure *structure; - GstCaps *otherpadcaps; - - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - enc->setup = FALSE; - enc->frame_size = DEFAULT_FRAMESIZE; - otherpadcaps = gst_pad_get_allowed_caps (pad); - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "channels", &enc->n_channels); - gst_structure_get_int (structure, "rate", &enc->sample_rate); - - if (otherpadcaps) { - if (!gst_caps_is_empty (otherpadcaps)) { - GstStructure *ps = gst_caps_get_structure (otherpadcaps, 0); - gst_structure_get_int (ps, "frame-size", &enc->frame_size); - } - gst_caps_unref (otherpadcaps); - } - - GST_DEBUG_OBJECT (pad, "channels=%d rate=%d frame-size=%d", - enc->n_channels, enc->sample_rate, enc->frame_size); - switch (enc->frame_size) { - case 2: - enc->frame_samples = enc->sample_rate / 400; - break; - case 5: - enc->frame_samples = enc->sample_rate / 200; - break; - case 10: - enc->frame_samples = enc->sample_rate / 100; - break; - case 20: - enc->frame_samples = enc->sample_rate / 50; - break; - case 40: - enc->frame_samples = enc->sample_rate / 25; - break; - case 60: - enc->frame_samples = 3 * enc->sample_rate / 50; - break; - default: - GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size); - return FALSE; - break; - } - GST_DEBUG_OBJECT (pad, "frame_samples %d", enc->frame_samples); - - gst_opus_enc_setup (enc); - - return TRUE; -} - - -static GstCaps * -gst_opus_enc_sink_getcaps (GstPad * pad, GstCaps * filter) -{ - GstCaps *caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); - GstCaps *peercaps = NULL; - GstOpusEnc *enc = GST_OPUS_ENC (gst_pad_get_parent_element (pad)); - - peercaps = gst_pad_peer_get_caps (enc->srcpad, NULL); - - if (peercaps) { - if (!gst_caps_is_empty (peercaps) && !gst_caps_is_any (peercaps)) { - GstStructure *ps = gst_caps_get_structure (peercaps, 0); - GstStructure *s = gst_caps_get_structure (caps, 0); - gint rate, channels; - - if (gst_structure_get_int (ps, "rate", &rate)) { - gst_structure_fixate_field_nearest_int (s, "rate", rate); - } - - if (gst_structure_get_int (ps, "channels", &channels)) { - gst_structure_fixate_field_nearest_int (s, "channels", channels); - } - } - gst_caps_unref (peercaps); - } - - gst_object_unref (enc); - - if (filter) { - GstCaps *new_caps = - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - caps = new_caps; - } - - return caps; -} - - -static gboolean -gst_opus_enc_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstOpusEnc *enc; - gint64 avg; - - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - - if (enc->samples_in == 0 || enc->bytes_out == 0 || enc->sample_rate == 0) - return FALSE; - - avg = (enc->bytes_out * enc->sample_rate) / (enc->samples_in); - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = src_value * GST_SECOND / avg; - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * avg / GST_SECOND; - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - return res; -} - -static gboolean -gst_opus_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; - GstOpusEnc *enc; - - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - - bytes_per_sample = enc->n_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 * enc->sample_rate; - - if (byterate == 0) - return FALSE; - *dest_value = 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 (enc->sample_rate == 0) - return FALSE; - *dest_value = src_value * GST_SECOND / enc->sample_rate; - 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 = src_value * scale * enc->sample_rate / GST_SECOND; - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - return res; -} - -static gint64 -gst_opus_enc_get_latency (GstOpusEnc * enc) -{ - return gst_util_uint64_scale (enc->frame_samples, GST_SECOND, - enc->sample_rate); -} - -static gboolean -gst_opus_enc_src_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - GstOpusEnc *enc; - - enc = GST_OPUS_ENC (gst_pad_get_parent (pad)); - - 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_peer_position (enc->sinkpad, req_fmt, &val))) { - gst_query_set_position (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_peer_position (enc->sinkpad, fmt, &pos))) - break; - - if ((res = - gst_pad_query_peer_convert (enc->sinkpad, 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_peer_duration (enc->sinkpad, req_fmt, &val))) { - gst_query_set_duration (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_peer_duration (enc->sinkpad, fmt, &dur))) - break; - - if ((res = - gst_pad_query_peer_convert (enc->sinkpad, 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_opus_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_peer_query (enc->sinkpad, query))) { - gst_query_parse_latency (query, &live, &min_latency, &max_latency); - - latency = gst_opus_enc_get_latency (enc); - - /* 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_peer_query (pad, query); - break; - } - -error: - - gst_object_unref (enc); - - return res; -} - -static gboolean -gst_opus_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_opus_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_opus_enc_init (GstOpusEnc * enc) { - enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); - gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad); - gst_pad_set_event_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sinkevent)); - gst_pad_set_chain_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_chain)); - gst_pad_set_getcaps_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps)); - gst_pad_set_query_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sink_query)); + GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc); - enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_set_query_function (enc->srcpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_src_query)); - gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); + GST_DEBUG_OBJECT (enc, "init"); enc->n_channels = -1; enc->sample_rate = -1; @@ -592,13 +262,142 @@ gst_opus_enc_init (GstOpusEnc * enc) enc->dtx = DEFAULT_DTX; enc->packet_loss_percentage = DEFAULT_PACKET_LOSS_PERCENT; - enc->setup = FALSE; - enc->header_sent = FALSE; - - enc->adapter = gst_adapter_new (); + /* arrange granulepos marking (and required perfect ts) */ + gst_audio_encoder_set_mark_granule (benc, TRUE); + gst_audio_encoder_set_perfect_timestamp (benc, TRUE); +} + +static gboolean +gst_opus_enc_start (GstAudioEncoder * benc) +{ + GstOpusEnc *enc = GST_OPUS_ENC (benc); + + GST_DEBUG_OBJECT (enc, "start"); + enc->tags = gst_tag_list_new_empty (); + enc->header_sent = FALSE; + return TRUE; +} + +static gboolean +gst_opus_enc_stop (GstAudioEncoder * benc) +{ + GstOpusEnc *enc = GST_OPUS_ENC (benc); + + GST_DEBUG_OBJECT (enc, "stop"); + enc->header_sent = FALSE; + if (enc->state) { + opus_encoder_destroy (enc->state); + enc->state = NULL; + } + gst_tag_list_free (enc->tags); + enc->tags = NULL; + g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + enc->headers = NULL; + + return TRUE; +} + +static gint +gst_opus_enc_get_frame_samples (GstOpusEnc * enc) +{ + gint frame_samples = 0; + switch (enc->frame_size) { + case 2: + frame_samples = enc->sample_rate / 400; + break; + case 5: + frame_samples = enc->sample_rate / 200; + break; + case 10: + frame_samples = enc->sample_rate / 100; + break; + case 20: + frame_samples = enc->sample_rate / 50; + break; + case 40: + frame_samples = enc->sample_rate / 25; + break; + case 60: + frame_samples = 3 * enc->sample_rate / 50; + break; + default: + GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size); + frame_samples = 0; + break; + } + return frame_samples; +} + +static gboolean +gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) +{ + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (benc); + + enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); + enc->sample_rate = GST_AUDIO_INFO_RATE (info); + GST_DEBUG_OBJECT (benc, "Setup with %d channels, %d Hz", enc->n_channels, + enc->sample_rate); + + /* handle reconfigure */ + if (enc->state) { + opus_encoder_destroy (enc->state); + enc->state = NULL; + } + + if (!gst_opus_enc_setup (enc)) + return FALSE; + + enc->frame_samples = gst_opus_enc_get_frame_samples (enc); + + /* feedback to base class */ + gst_audio_encoder_set_latency (benc, + gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc)); + + gst_audio_encoder_set_frame_samples_min (benc, + enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_samples_max (benc, + enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_max (benc, 0); + + return TRUE; +} + +static gint64 +gst_opus_enc_get_latency (GstOpusEnc * enc) +{ + gint64 latency = gst_util_uint64_scale (enc->frame_samples, GST_SECOND, + enc->sample_rate); + GST_DEBUG_OBJECT (enc, "Latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency)); + return latency; +} + +static GstBuffer * +gst_opus_enc_create_id_buffer (GstOpusEnc * enc) +{ + GstBuffer *buffer; + GstByteWriter bw; + + gst_byte_writer_init (&bw); + + /* See http://wiki.xiph.org/OggOpus */ + gst_byte_writer_put_string_utf8 (&bw, "OpusHead"); + gst_byte_writer_put_uint8 (&bw, 0); /* version number */ + gst_byte_writer_put_uint8 (&bw, enc->n_channels); + gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ + gst_byte_writer_put_uint32_le (&bw, enc->sample_rate); + gst_byte_writer_put_uint16_le (&bw, 0); /* output gain *//* TODO: endianness ? */ + gst_byte_writer_put_uint8 (&bw, 0); /* channel mapping *//* TODO: what is this ? */ + + buffer = gst_byte_writer_reset_and_get_buffer (&bw); + + GST_BUFFER_OFFSET (buffer) = 0; + GST_BUFFER_OFFSET_END (buffer) = 0; + + return buffer; } -#if 0 static GstBuffer * gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) { @@ -613,13 +412,14 @@ gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) if (tags == NULL) { /* FIXME: better fix chain of callers to not write metadata at all, * if there is none */ - empty_tags = gst_tag_list_new (); + empty_tags = gst_tag_list_new_empty (); tags = empty_tags; } - comments = gst_tag_list_to_vorbiscomment_buffer (tags, NULL, - 0, "Encoded with GStreamer Opusenc"); + comments = + gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", + 8, "Encoded with GStreamer Opusenc"); - GST_BUFFER_OFFSET (comments) = enc->bytes_out; + GST_BUFFER_OFFSET (comments) = 0; GST_BUFFER_OFFSET_END (comments) = 0; if (empty_tags) @@ -627,13 +427,14 @@ gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) return comments; } -#endif static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; + GST_DEBUG_OBJECT (enc, "setup"); + enc->setup = FALSE; enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, @@ -659,88 +460,20 @@ gst_opus_enc_setup (GstOpusEnc * enc) return TRUE; -#if 0 -mode_initialization_failed: - GST_ERROR_OBJECT (enc, "Mode initialization failed: %d", error); - return FALSE; -#endif - encoder_creation_failed: GST_ERROR_OBJECT (enc, "Encoder creation failed"); return FALSE; } - -/* push out the buffer and do internal bookkeeping */ -static GstFlowReturn -gst_opus_enc_push_buffer (GstOpusEnc * enc, GstBuffer * buffer) -{ - gsize size; - - size = gst_buffer_get_size (buffer); - - enc->bytes_out += size; - - GST_DEBUG_OBJECT (enc, "pushing output buffer of size %u", size); - - return gst_pad_push (enc->srcpad, buffer); -} - -#if 0 -static GstCaps * -gst_opus_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, - GstBuffer * buf2) -{ - GstStructure *structure = NULL; - GstBuffer *buf; - GValue array = { 0 }; - GValue value = { 0 }; - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - g_assert (gst_buffer_is_metadata_writable (buf1)); - g_assert (gst_buffer_is_metadata_writable (buf2)); - - /* mark buffers */ - GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf2, 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); - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&value); - g_value_unset (&array); - - return caps; -} -#endif - - static gboolean -gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event) +gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) { - gboolean res = TRUE; GstOpusEnc *enc; - enc = GST_OPUS_ENC (gst_pad_get_parent (pad)); + enc = GST_OPUS_ENC (benc); + GST_DEBUG_OBJECT (enc, "sink event: %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - gst_opus_enc_encode (enc, TRUE); - res = gst_pad_event_default (pad, event); - break; case GST_EVENT_TAG: { GstTagList *list; @@ -749,51 +482,78 @@ gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event) gst_event_parse_tag (event, &list); gst_tag_setter_merge_tags (setter, list, mode); - res = gst_pad_event_default (pad, event); - break; - } - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - res = gst_opus_enc_sink_setcaps (pad, caps); - gst_event_unref (event); break; } default: - res = gst_pad_event_default (pad, event); break; } - gst_object_unref (enc); - - return res; + return FALSE; } static GstFlowReturn -gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) +gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer) { - + GstOpusEnc *enc; GstFlowReturn ret = GST_FLOW_OK; - gint bytes = enc->frame_samples * 2 * enc->n_channels; - gint bytes_per_packet; - bytes_per_packet = - (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8; + enc = GST_OPUS_ENC (benc); - if (flush && gst_adapter_available (enc->adapter) % bytes != 0) { - guint diff = bytes - gst_adapter_available (enc->adapter) % bytes; - GstBuffer *buf = gst_buffer_new_and_alloc (diff); + /* FIXME 0.11 ? get rid of this special ogg stuff and have it + * put and use 'codec data' in caps like anything else, + * with all the usual out-of-band advantage etc */ + if (G_UNLIKELY (enc->headers)) { + GSList *header = enc->headers; - gst_buffer_memset (buf, 0, 0, diff); - gst_adapter_push (enc->adapter, buf); + /* try to push all of these, if we lose one, might as well lose all */ + while (header) { + if (ret == GST_FLOW_OK) + ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), header->data); + else + gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), header->data); + header = g_slist_next (header); + } + + g_slist_free (enc->headers); + enc->headers = NULL; } + return ret; +} - while (gst_adapter_available (enc->adapter) >= bytes) { - gint16 *data; +static GstFlowReturn +gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) +{ + guint8 *bdata, *data, *mdata = NULL; + gsize bsize, size; + gsize bytes = enc->frame_samples * enc->n_channels * 2; + gsize bytes_per_packet = + (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8; + gint ret = GST_FLOW_OK; + + if (G_LIKELY (buf)) { + bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ); + + if (G_UNLIKELY (bsize % bytes)) { + GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); + + size = ((bsize / bytes) + 1) * bytes; + mdata = g_malloc0 (size); + memcpy (data, bdata, bsize); + gst_buffer_unmap (buf, bdata, bsize); + bdata = NULL; + data = mdata; + } else { + data = bdata; + size = bsize; + } + } else { + GST_DEBUG_OBJECT (enc, "nothing to drain"); + goto done; + } + + while (size) { gint encoded_size; unsigned char *out_data; gsize out_size; @@ -803,19 +563,15 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) if (!outbuf) goto done; - data = (gint16 *) gst_adapter_take (enc->adapter, bytes); - enc->samples_in += enc->frame_samples; - - GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - enc->frame_samples, bytes); + GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes) to %d bytes", + enc->frame_samples, bytes, bytes_per_packet); out_data = gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); - encoded_size = opus_encode (enc->state, data, enc->frame_samples, + encoded_size = + opus_encode (enc->state, (const gint16 *) data, enc->frame_samples, out_data, bytes_per_packet); gst_buffer_unmap (outbuf, out_data, out_size); - g_free (data); - if (encoded_size < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", encoded_size); ret = GST_FLOW_ERROR; @@ -828,151 +584,137 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) goto done; } - GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts + - gst_util_uint64_scale_int (enc->frameno_out * enc->frame_samples, - GST_SECOND, enc->sample_rate); - GST_BUFFER_DURATION (outbuf) = - gst_util_uint64_scale_int (enc->frame_samples, GST_SECOND, - enc->sample_rate); - GST_BUFFER_OFFSET (outbuf) = - gst_util_uint64_scale_int (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND, - enc->sample_rate); - - enc->frameno++; - enc->frameno_out++; - - ret = gst_opus_enc_push_buffer (enc, outbuf); + ret = + gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, + enc->frame_samples); if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) goto done; + + data += bytes; + size -= bytes; } done: + if (bdata) + gst_buffer_unmap (buf, bdata, bsize); + if (mdata) + g_free (mdata); + return ret; } +/* + * (really really) FIXME: move into core (dixit tpm) + */ +/** + * _gst_caps_set_buffer_array: + * @caps: a #GstCaps + * @field: field in caps to set + * @buf: header buffers + * + * Adds given buffers to an array of buffers set as the given @field + * on the given @caps. List of buffer arguments must be NULL-terminated. + * + * Returns: input caps with a streamheader field added, or NULL if some error + */ +static GstCaps * +_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, + GstBuffer * buf, ...) +{ + GstStructure *structure = NULL; + va_list va; + GValue array = { 0 }; + GValue value = { 0 }; + + g_return_val_if_fail (caps != NULL, NULL); + g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); + g_return_val_if_fail (field != NULL, NULL); + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&array, GST_TYPE_ARRAY); + + va_start (va, buf); + /* put buffers in a fixed list */ + while (buf) { + g_assert (gst_buffer_is_writable (buf)); + + /* mark buffer */ + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + + g_value_init (&value, GST_TYPE_BUFFER); + buf = gst_buffer_copy (buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + gst_value_set_buffer (&value, buf); + gst_buffer_unref (buf); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + + buf = va_arg (va, GstBuffer *); + } + + gst_structure_set_value (structure, field, &array); + g_value_unset (&array); + + return caps; +} + static GstFlowReturn -gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) +gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) { GstOpusEnc *enc; GstFlowReturn ret = GST_FLOW_OK; - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); + enc = GST_OPUS_ENC (benc); - if (!enc->setup) - goto not_setup; + GST_DEBUG_OBJECT (enc, "handle_frame"); if (!enc->header_sent) { + /* Opus streams in Ogg begin with two 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. */ + GstBuffer *buf1, *buf2; GstCaps *caps; - caps = gst_pad_get_caps (enc->srcpad, NULL); - caps = gst_caps_copy (caps); - gst_caps_set_simple (caps, - "rate", G_TYPE_INT, enc->sample_rate, - "channels", G_TYPE_INT, enc->n_channels, - "frame-size", G_TYPE_INT, enc->frame_size, NULL); + /* create header buffers */ + buf1 = gst_opus_enc_create_id_buffer (enc); + buf2 = gst_opus_enc_create_metadata_buffer (enc); + + /* mark and put on caps */ + caps = + gst_caps_new_simple ("audio/x-opus", "rate", G_TYPE_INT, + enc->sample_rate, "channels", G_TYPE_INT, enc->n_channels, "frame-size", + G_TYPE_INT, enc->frame_size, NULL); + caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL); /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); - GST_LOG_OBJECT (enc, "rate=%d channels=%d frame-size=%d", - enc->sample_rate, enc->n_channels, enc->frame_size); - gst_pad_set_caps (enc->srcpad, caps); + + gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps); gst_caps_unref (caps); + /* push out buffers */ + /* store buffers for later pre_push sending */ + g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + enc->headers = NULL; + GST_DEBUG_OBJECT (enc, "storing header buffers"); + enc->headers = g_slist_prepend (enc->headers, buf2); + enc->headers = g_slist_prepend (enc->headers, buf1); + enc->header_sent = TRUE; } - GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", - gst_buffer_get_size (buf)); + GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf, + buf ? gst_buffer_get_size (buf) : 0); - /* Save the timestamp of the first buffer. This will be later - * used as offset for all following buffers */ - if (enc->start_ts == GST_CLOCK_TIME_NONE) { - if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { - enc->start_ts = GST_BUFFER_TIMESTAMP (buf); - } else { - enc->start_ts = 0; - } - } - - - /* Check if we have a continous stream, if not drop some samples or the buffer or - * insert some silence samples */ - if (enc->next_ts != GST_CLOCK_TIME_NONE && - GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) { - guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf); - guint64 diff_bytes; - - GST_WARNING_OBJECT (enc, "Buffer is older than previous " - "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT - "), cannot handle. Clipping buffer.", - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (enc->next_ts)); - - diff_bytes = - GST_CLOCK_TIME_TO_FRAMES (diff, enc->sample_rate) * enc->n_channels * 2; - if (diff_bytes >= gst_buffer_get_size (buf)) { - gst_buffer_unref (buf); - return GST_FLOW_OK; - } - buf = gst_buffer_make_writable (buf); - gst_buffer_resize (buf, diff_bytes, gst_buffer_get_size (buf) - diff_bytes); - - GST_BUFFER_TIMESTAMP (buf) += diff; - if (GST_BUFFER_DURATION_IS_VALID (buf)) - GST_BUFFER_DURATION (buf) -= diff; - } - - if (enc->next_ts != GST_CLOCK_TIME_NONE - && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { - guint64 max_diff = - gst_util_uint64_scale (enc->frame_size, GST_SECOND, enc->sample_rate); - - if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts && - GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > max_diff) { - GST_WARNING_OBJECT (enc, - "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT, - GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, max_diff); - - gst_opus_enc_encode (enc, TRUE); - - enc->frameno_out = 0; - enc->start_ts = GST_BUFFER_TIMESTAMP (buf); - } - } - - if (GST_BUFFER_TIMESTAMP_IS_VALID (buf) - && GST_BUFFER_DURATION_IS_VALID (buf)) - enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); - else - enc->next_ts = GST_CLOCK_TIME_NONE; - - /* push buffer to adapter */ - gst_adapter_push (enc->adapter, buf); - buf = NULL; - - ret = gst_opus_enc_encode (enc, FALSE); - -done: - - if (buf) - gst_buffer_unref (buf); + ret = gst_opus_enc_encode (enc, buf); return ret; - - /* ERRORS */ -not_setup: - { - GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL), - ("encoder not initialized (input is not audio?)")); - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - } - static void gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -1062,49 +804,3 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, break; } } - -static GstStateChangeReturn -gst_opus_enc_change_state (GstElement * element, GstStateChange transition) -{ - GstOpusEnc *enc = GST_OPUS_ENC (element); - GstStateChangeReturn res; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - enc->frameno = 0; - enc->samples_in = 0; - enc->frameno_out = 0; - enc->start_ts = GST_CLOCK_TIME_NONE; - enc->next_ts = GST_CLOCK_TIME_NONE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - /* fall through */ - default: - break; - } - - res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (res == GST_STATE_CHANGE_FAILURE) - return res; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - enc->setup = FALSE; - enc->header_sent = FALSE; - if (enc->state) { - opus_encoder_destroy (enc->state); - enc->state = NULL; - } - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); - default: - break; - } - - return res; -} diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 5cb54598af..5d4e3c4008 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -24,7 +24,7 @@ #include -#include +#include #include @@ -48,16 +48,13 @@ typedef struct _GstOpusEnc GstOpusEnc; typedef struct _GstOpusEncClass GstOpusEncClass; struct _GstOpusEnc { - GstElement element; + GstAudioEncoder element; /* pads */ GstPad *sinkpad; GstPad *srcpad; - //OpusHeader header; - //OpusMode *mode; OpusEncoder *state; - GstAdapter *adapter; /* properties */ gboolean audio_or_voip; @@ -71,28 +68,19 @@ struct _GstOpusEnc { gboolean dtx; gint packet_loss_percentage; - int frame_samples; - + gint frame_samples; gint n_channels; gint sample_rate; gboolean setup; gboolean header_sent; - gboolean eos; + GSList *headers; - guint64 samples_in; - guint64 bytes_out; - - guint64 frameno; - guint64 frameno_out; - - GstClockTime start_ts; - GstClockTime next_ts; - guint64 granulepos_offset; + GstTagList *tags; }; struct _GstOpusEncClass { - GstElementClass parent_class; + GstAudioEncoderClass parent_class; /* signals */ void (*frame_encoded) (GstElement *element); From a4a837dd39fa2a6b562472ba12ff57e1f1e7c548 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 15 Nov 2011 17:49:48 +0000 Subject: [PATCH 034/197] opusenc: fix pointer mismatch in memcpy on drain --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 363eb6976e..4ef0ffdbe8 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -540,7 +540,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); - memcpy (data, bdata, bsize); + memcpy (mdata, bdata, bsize); gst_buffer_unmap (buf, bdata, bsize); bdata = NULL; data = mdata; From 1c99cb3ab1fc32ee5b1f414f91a32b3055ca57a7 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 15 Nov 2011 19:53:33 +0000 Subject: [PATCH 035/197] opusparse: add opusparse element A very simple element that parses Opus streams from the ad hoc framing used by the Opus test vectors. --- ext/opus/Makefile.am | 4 ++-- ext/opus/gstopus.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 57e1692645..6fe723ecce 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstopus.la -libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -15,4 +15,4 @@ libgstopus_la_LIBADD = \ libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) libgstopus_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstopusenc.h gstopusdec.h +noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index 65e9dcdc58..c5f68a131c 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -23,6 +23,7 @@ #include "gstopusdec.h" #include "gstopusenc.h" +#include "gstopusparse.h" #include @@ -38,6 +39,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_OPUS_DEC)) return FALSE; + if (!gst_element_register (plugin, "opusparse", GST_RANK_NONE, + GST_TYPE_OPUS_PARSE)) + return FALSE; + gst_tag_register_musicbrainz_tags (); return TRUE; From 9690361d04a2a91ed508aa895031b58fd52cd5e4 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 15 Nov 2011 23:00:32 +0000 Subject: [PATCH 036/197] opusdec: remove buffer pool, buffers are not constant size --- ext/opus/gstopusdec.c | 82 +++++++------------------------------------ ext/opus/gstopusdec.h | 2 -- 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 8aa99466b2..0b30c38d7a 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -49,6 +49,7 @@ GST_DEBUG_CATEGORY_STATIC (opusdec_debug); #define GST_CAT_DEFAULT opusdec_debug #define DEC_MAX_FRAME_SIZE 2000 +#define DEC_MAX_OUTPUT_BUFFER_SIZE (5760) static GstStaticPadTemplate opus_dec_src_factory = GST_STATIC_PAD_TEMPLATE ("src", @@ -153,52 +154,6 @@ gst_opus_dec_stop (GstAudioDecoder * dec) return TRUE; } -static GstFlowReturn -gst_opus_dec_negotiate_pool (GstOpusDec * dec, GstCaps * caps, gsize bytes) -{ - GstQuery *query; - GstBufferPool *pool = NULL; - guint size, min, max, prefix, alignment; - GstStructure *config; - - /* find a pool for the negotiated caps now */ - query = gst_query_new_allocation (caps, TRUE); - - if (gst_pad_peer_query (GST_AUDIO_DECODER_SRC_PAD (dec), query)) { - GST_DEBUG_OBJECT (dec, "got downstream ALLOCATION hints"); - /* we got configuration from our peer, parse them */ - gst_query_parse_allocation_params (query, &size, &min, &max, &prefix, - &alignment, &pool); - size = MAX (size, bytes); - } else { - GST_DEBUG_OBJECT (dec, "didn't get downstream ALLOCATION hints"); - size = bytes; - min = max = 0; - prefix = 0; - alignment = 0; - } - - if (pool == NULL) { - /* we did not get a pool, make one ourselves then */ - pool = gst_buffer_pool_new (); - } - - if (dec->pool) - gst_object_unref (dec->pool); - dec->pool = pool; - - config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set (config, caps, size, min, max, prefix, alignment); - - gst_buffer_pool_set_config (pool, config); - /* and activate */ - gst_buffer_pool_set_active (pool, TRUE); - - gst_query_unref (query); - - return GST_FLOW_OK; -} - static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { @@ -221,6 +176,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstBuffer *outbuf; gint16 *out_data; int n, err; + int samples_per_frame; + unsigned int packet_size; if (dec->state == NULL) { GstCaps *caps; @@ -241,14 +198,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) GST_ERROR ("nego failure"); - /* negotiate a bufferpool */ - if ((res = - gst_opus_dec_negotiate_pool (dec, caps, - dec->frame_size * dec->n_channels * 2)) != GST_FLOW_OK) { - gst_caps_unref (caps); - goto no_bufferpool; - } - gst_caps_unref (caps); } @@ -265,22 +214,15 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, size = 0; } + samples_per_frame = + opus_packet_get_samples_per_frame (data, dec->sample_rate); GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data)); - GST_DEBUG ("samples_per_frame %d", opus_packet_get_samples_per_frame (data, - 48000)); - GST_DEBUG ("channels %d", opus_packet_get_nb_channels (data)); + GST_DEBUG ("samples_per_frame %d", samples_per_frame); - if (gst_pad_check_reconfigure (GST_AUDIO_DECODER_SRC_PAD (dec))) { - GstCaps *caps = gst_pad_get_current_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); - gst_opus_dec_negotiate_pool (dec, caps, - dec->frame_samples * dec->n_channels * 2); - gst_caps_unref (caps); - } - - res = gst_buffer_pool_acquire_buffer (dec->pool, &outbuf, NULL); - if (res != GST_FLOW_OK) { - GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); - return res; + packet_size = samples_per_frame * dec->n_channels * 2; + outbuf = gst_buffer_new_and_alloc (packet_size); + if (!outbuf) { + goto buffer_failed; } out_data = (gint16 *) gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); @@ -322,8 +264,8 @@ creation_failed: GST_ERROR_OBJECT (dec, "Failed to create Opus decoder: %d", err); return GST_FLOW_ERROR; -no_bufferpool: - GST_ERROR_OBJECT (dec, "Failed to negotiate buffer pool: %d", res); +buffer_failed: + GST_ERROR_OBJECT (dec, "Failed to create %u byte buffer", packet_size); return GST_FLOW_ERROR; } diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index c29b6b1f1f..8389580e3b 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -56,8 +56,6 @@ struct _GstOpusDec { int sample_rate; int n_channels; - - GstBufferPool *pool; }; struct _GstOpusDecClass { From afbc4fdd713a939b4f1d893b4048438617f6cdce Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 01:14:32 +0000 Subject: [PATCH 037/197] opusdec: rewrite logic Parameters such as frame size, etc, are variable. Pretty much everything can change within a stream, so be prepared about it, and do not cache parameters in the decoder. --- ext/opus/gstopusdec.c | 136 ++++++++++++++++-------------------------- ext/opus/gstopusdec.h | 4 +- 2 files changed, 53 insertions(+), 87 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 0b30c38d7a..d3528c85a9 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -48,9 +48,6 @@ GST_DEBUG_CATEGORY_STATIC (opusdec_debug); #define GST_CAT_DEFAULT opusdec_debug -#define DEC_MAX_FRAME_SIZE 2000 -#define DEC_MAX_OUTPUT_BUFFER_SIZE (5760) - static GstStaticPadTemplate opus_dec_src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -110,14 +107,13 @@ static void gst_opus_dec_reset (GstOpusDec * dec) { dec->packetno = 0; - dec->frame_size = 0; - dec->frame_samples = 960; - dec->frame_duration = 0; if (dec->state) { opus_decoder_destroy (dec->state); dec->state = NULL; } + dec->next_ts = 0; + gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); } @@ -176,7 +172,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstBuffer *outbuf; gint16 *out_data; int n, err; - int samples_per_frame; + int samples; unsigned int packet_size; if (dec->state == NULL) { @@ -192,8 +188,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, "rate", G_TYPE_INT, dec->sample_rate, "channels", G_TYPE_INT, dec->n_channels, NULL); - GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", - dec->sample_rate, dec->n_channels, dec->frame_size); + GST_DEBUG_OBJECT (dec, "rate=%d channels=%d", + dec->sample_rate, dec->n_channels); if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) GST_ERROR ("nego failure"); @@ -214,12 +210,13 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, size = 0; } - samples_per_frame = - opus_packet_get_samples_per_frame (data, dec->sample_rate); + samples = + opus_packet_get_samples_per_frame (data, + dec->sample_rate) * opus_packet_get_nb_frames (data, size); GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data)); - GST_DEBUG ("samples_per_frame %d", samples_per_frame); + GST_DEBUG ("samples %d", samples); - packet_size = samples_per_frame * dec->n_channels * 2; + packet_size = samples * dec->n_channels * 2; outbuf = gst_buffer_new_and_alloc (packet_size); if (!outbuf) { goto buffer_failed; @@ -227,34 +224,33 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, out_data = (gint16 *) gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); - GST_LOG_OBJECT (dec, "decoding frame"); + GST_LOG_OBJECT (dec, "decoding %d samples, in size %u", samples, size); - n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, 0); + n = opus_decode (dec->state, data, size, out_data, samples, 0); gst_buffer_unmap (buf, data, size); + gst_buffer_unmap (outbuf, out_data, out_size); if (n < 0) { - gst_buffer_unmap (outbuf, out_data, out_size); GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); return GST_FLOW_ERROR; } + GST_DEBUG_OBJECT (dec, "decoded %d samples", n); - if (!GST_CLOCK_TIME_IS_VALID (timestamp)) { - GST_WARNING_OBJECT (dec, "No timestamp in -> no timestamp out"); + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + } else { + GST_BUFFER_TIMESTAMP (outbuf) = dec->next_ts; } - GST_DEBUG_OBJECT (dec, "timestamp=%" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - - GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); - GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf); + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale (n, GST_SECOND, dec->sample_rate); + dec->next_ts = GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf); GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_TIME_ARGS (dec->frame_duration)); + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); - gst_buffer_unmap (outbuf, out_data, out_size); - if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); @@ -269,37 +265,6 @@ buffer_failed: return GST_FLOW_ERROR; } -static gint -gst_opus_dec_get_frame_samples (GstOpusDec * dec) -{ - gint frame_samples = 0; - switch (dec->frame_size) { - case 2: - frame_samples = dec->sample_rate / 400; - break; - case 5: - frame_samples = dec->sample_rate / 200; - break; - case 10: - frame_samples = dec->sample_rate / 100; - break; - case 20: - frame_samples = dec->sample_rate / 50; - break; - case 40: - frame_samples = dec->sample_rate / 25; - break; - case 60: - frame_samples = 3 * dec->sample_rate / 50; - break; - default: - GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); - frame_samples = 0; - break; - } - return frame_samples; -} - static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) { @@ -337,25 +302,6 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) } } - if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { - GST_WARNING_OBJECT (dec, "Frame size not included in caps"); - } - if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { - GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); - } - if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { - GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); - } - - dec->frame_samples = gst_opus_dec_get_frame_samples (dec); - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, - GST_SECOND, dec->sample_rate); - - GST_INFO_OBJECT (dec, - "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" - GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, - dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); - caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, "S16LE", "rate", G_TYPE_INT, dec->sample_rate, @@ -387,6 +333,20 @@ memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) return res; } +static gboolean +gst_opus_dec_is_header (GstBuffer * buf, const char *magic, guint magic_size) +{ + guint8 *data; + gsize size; + gboolean ret; + + data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ); + if (!data) + return FALSE; + ret = (size >= magic_size && !memcmp (magic, data, magic_size)); + gst_buffer_unmap (buf, data, size); + return ret; +} static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) @@ -424,16 +384,24 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) * first two packets are the headers. */ switch (dec->packetno) { case 0: - GST_DEBUG_OBJECT (dec, "counted streamheader"); - res = GST_FLOW_OK; - res = gst_opus_dec_parse_header (dec, buf); - gst_audio_decoder_finish_frame (adec, NULL, 1); + if (gst_opus_dec_is_header (buf, "OpusHead", 8)) { + GST_DEBUG_OBJECT (dec, "found streamheader"); + res = gst_opus_dec_parse_header (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + } else { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + } break; case 1: - GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); - res = GST_FLOW_OK; - res = gst_opus_dec_parse_comments (dec, buf); - gst_audio_decoder_finish_frame (adec, NULL, 1); + if (gst_opus_dec_is_header (buf, "OpusTags", 8)) { + GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); + res = gst_opus_dec_parse_comments (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + } else { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + } break; default: { diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 8389580e3b..38dd2799ad 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -45,11 +45,9 @@ struct _GstOpusDec { GstAudioDecoder element; OpusDecoder *state; - int frame_samples; - gint frame_size; - GstClockTime frame_duration; guint64 packetno; + GstClockTime next_ts; GstBuffer *streamheader; GstBuffer *vorbiscomment; From acf18e34bedb68dbe927a32183b29eeac2023e4c Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 13:26:35 +0000 Subject: [PATCH 038/197] opusdec: allow negotiation of rate/channels with downstream Since an opus stream may be decoded to any (sensible) rate, and either stereo or mono, we try to accomodate downstream. --- ext/opus/gstopusdec.c | 67 +++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index d3528c85a9..5af378487f 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -121,8 +121,8 @@ gst_opus_dec_reset (GstOpusDec * dec) static void gst_opus_dec_init (GstOpusDec * dec) { - dec->sample_rate = 48000; - dec->n_channels = 2; + dec->sample_rate = 0; + dec->n_channels = 0; gst_opus_dec_reset (dec); } @@ -162,6 +162,46 @@ gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) return GST_FLOW_OK; } +static void +gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec) +{ + GstPad *srcpad, *peer; + GstStructure *s; + GstCaps *caps, *template_caps, *peer_caps; + + srcpad = GST_AUDIO_DECODER_SRC_PAD (dec); + peer = gst_pad_get_peer (srcpad); + + if (peer) { + template_caps = gst_pad_get_pad_template_caps (srcpad); + peer_caps = gst_pad_get_caps (peer, NULL); + GST_DEBUG_OBJECT (dec, "Peer caps: %" GST_PTR_FORMAT, peer_caps); + caps = gst_caps_intersect (template_caps, peer_caps); + gst_caps_fixate (caps); + GST_DEBUG_OBJECT (dec, "Fixated caps: %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { + dec->n_channels = 2; + GST_WARNING_OBJECT (dec, "Failed to get channels, using default %d", + dec->n_channels); + } else { + GST_DEBUG_OBJECT (dec, "Got channels %d", dec->n_channels); + } + if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { + dec->sample_rate = 48000; + GST_WARNING_OBJECT (dec, "Failed to get rate, using default %d", + dec->sample_rate); + } else { + GST_DEBUG_OBJECT (dec, "Got sample rate %d", dec->sample_rate); + } + + gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (dec), caps); + } else { + GST_WARNING_OBJECT (dec, "Failed to get src pad peer"); + } +} + static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstClockTime timestamp, GstClockTime duration) @@ -176,25 +216,11 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, unsigned int packet_size; if (dec->state == NULL) { - GstCaps *caps; + gst_opus_dec_setup_from_peer_caps (dec); dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; - - /* set caps */ - caps = gst_caps_new_simple ("audio/x-raw", - "format", G_TYPE_STRING, "S16LE", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, NULL); - - GST_DEBUG_OBJECT (dec, "rate=%d channels=%d", - dec->sample_rate, dec->n_channels); - - if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) - GST_ERROR ("nego failure"); - - gst_caps_unref (caps); } if (buf) { @@ -302,13 +328,6 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) } } - caps = gst_caps_new_simple ("audio/x-raw", - "format", G_TYPE_STRING, "S16LE", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, NULL); - gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (dec), caps); - gst_caps_unref (caps); - done: return ret; } From f978a60f38ad536efb688df574cf2fadc4d96db2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 16:56:43 +0000 Subject: [PATCH 039/197] opus: port to base audio encoder/decoder --- ext/opus/Makefile.am | 2 + ext/opus/gstopusdec.c | 819 +++++++++-------------------------- ext/opus/gstopusdec.h | 14 +- ext/opus/gstopusenc.c | 961 ++++++++++++++---------------------------- ext/opus/gstopusenc.h | 27 +- 5 files changed, 523 insertions(+), 1300 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index aa50ba96ef..57e1692645 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -2,10 +2,12 @@ plugin_LTLIBRARIES = libgstopus.la libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c libgstopus_la_CFLAGS = \ + -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_CFLAGS) \ $(OPUS_CFLAGS) libgstopus_la_LIBADD = \ + -lgstaudio-$(GST_MAJORMINOR) \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 35f501a8ab..1abe3201cb 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -68,31 +68,17 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-opus") ); -GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstElement, GST_TYPE_ELEMENT); +GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstAudioDecoder, + GST_TYPE_AUDIO_DECODER); -static gboolean opus_dec_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn opus_dec_chain (GstPad * pad, GstBuffer * buf); -static gboolean opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps); -static GstStateChangeReturn opus_dec_change_state (GstElement * element, - GstStateChange transition); - -static gboolean opus_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean opus_dec_src_query (GstPad * pad, GstQuery * query); -static gboolean opus_dec_sink_query (GstPad * pad, GstQuery * query); -static const GstQueryType *opus_get_src_query_types (GstPad * pad); -static const GstQueryType *opus_get_sink_query_types (GstPad * pad); -static gboolean opus_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); - -static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, - GstBuffer * buf, GstClockTime timestamp, GstClockTime duration); -static GstFlowReturn opus_dec_chain_parse_header (GstOpusDec * dec, +static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf); -#if 0 -static GstFlowReturn opus_dec_chain_parse_comments (GstOpusDec * dec, - GstBuffer * buf); -#endif +static gboolean gst_opus_dec_start (GstAudioDecoder * dec); +static gboolean gst_opus_dec_stop (GstAudioDecoder * dec); +static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec, + GstBuffer * buffer); +static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, + GstCaps * caps); static void gst_opus_dec_base_init (gpointer g_class) @@ -112,11 +98,16 @@ gst_opus_dec_base_init (gpointer g_class) static void gst_opus_dec_class_init (GstOpusDecClass * klass) { + GstAudioDecoderClass *adclass; GstElementClass *gstelement_class; + adclass = (GstAudioDecoderClass *) klass; gstelement_class = (GstElementClass *) klass; - gstelement_class->change_state = GST_DEBUG_FUNCPTR (opus_dec_change_state); + adclass->start = GST_DEBUG_FUNCPTR (gst_opus_dec_start); + adclass->stop = GST_DEBUG_FUNCPTR (gst_opus_dec_stop); + adclass->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_dec_handle_frame); + adclass->set_format = GST_DEBUG_FUNCPTR (gst_opus_dec_set_format); GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, "opus decoding element"); @@ -125,8 +116,6 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) static void gst_opus_dec_reset (GstOpusDec * dec) { - gst_segment_init (&dec->segment, GST_FORMAT_UNDEFINED); - dec->granulepos = -1; dec->packetno = 0; dec->frame_size = 0; dec->frame_samples = 960; @@ -135,50 +124,14 @@ gst_opus_dec_reset (GstOpusDec * dec) opus_decoder_destroy (dec->state); dec->state = NULL; } -#if 0 - if (dec->mode) { - opus_mode_destroy (dec->mode); - dec->mode = NULL; - } -#endif gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); - g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->extra_headers); - dec->extra_headers = NULL; - -#if 0 - memset (&dec->header, 0, sizeof (dec->header)); -#endif } static void gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) { - dec->sinkpad = - gst_pad_new_from_static_template (&opus_dec_sink_factory, "sink"); - gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (opus_dec_chain)); - gst_pad_set_event_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_dec_sink_event)); - gst_pad_set_query_type_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_get_sink_query_types)); - gst_pad_set_query_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_dec_sink_query)); - gst_pad_set_setcaps_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (opus_dec_sink_setcaps)); - gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - - dec->srcpad = gst_pad_new_from_static_template (&opus_dec_src_factory, "src"); - gst_pad_use_fixed_caps (dec->srcpad); - gst_pad_set_event_function (dec->srcpad, - GST_DEBUG_FUNCPTR (opus_dec_src_event)); - gst_pad_set_query_type_function (dec->srcpad, - GST_DEBUG_FUNCPTR (opus_get_src_query_types)); - gst_pad_set_query_function (dec->srcpad, - GST_DEBUG_FUNCPTR (opus_dec_src_query)); - gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - dec->sample_rate = 48000; dec->n_channels = 2; @@ -186,532 +139,39 @@ gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) } static gboolean -opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps) +gst_opus_dec_start (GstAudioDecoder * dec) { - GstOpusDec *dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - gboolean ret = TRUE; - GstStructure *s; - const GValue *streamheader; + GstOpusDec *odec = GST_OPUS_DEC (dec); - GST_DEBUG_OBJECT (pad, "Setting sink caps to %" GST_PTR_FORMAT, caps); + gst_opus_dec_reset (odec); - s = gst_caps_get_structure (caps, 0); - if ((streamheader = gst_structure_get_value (s, "streamheader")) && - G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && - gst_value_array_get_size (streamheader) >= 2) { - const GValue *header; - GstBuffer *buf; - GstFlowReturn res = GST_FLOW_OK; + /* we know about concealment */ + gst_audio_decoder_set_plc_aware (dec, TRUE); - header = gst_value_array_get_value (streamheader, 0); - if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { - buf = gst_value_get_buffer (header); - res = opus_dec_chain_parse_header (dec, buf); - if (res != GST_FLOW_OK) - goto done; - gst_buffer_replace (&dec->streamheader, buf); - } -#if 0 - vorbiscomment = gst_value_array_get_value (streamheader, 1); - if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { - buf = gst_value_get_buffer (vorbiscomment); - res = opus_dec_chain_parse_comments (dec, buf); - if (res != GST_FLOW_OK) - goto done; - gst_buffer_replace (&dec->vorbiscomment, buf); - } -#endif - - g_list_foreach (dec->extra_headers, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->extra_headers); - dec->extra_headers = NULL; - - if (gst_value_array_get_size (streamheader) > 2) { - gint i, n; - - n = gst_value_array_get_size (streamheader); - for (i = 2; i < n; i++) { - header = gst_value_array_get_value (streamheader, i); - buf = gst_value_get_buffer (header); - dec->extra_headers = - g_list_prepend (dec->extra_headers, gst_buffer_ref (buf)); - } - } - } - - if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { - GST_WARNING_OBJECT (dec, "Frame size not included in caps"); - } - if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { - GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); - } - if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { - GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); - } - switch (dec->frame_size) { - case 2: - dec->frame_samples = dec->sample_rate / 400; - break; - case 5: - dec->frame_samples = dec->sample_rate / 200; - break; - case 10: - dec->frame_samples = dec->sample_rate / 100; - break; - case 20: - dec->frame_samples = dec->sample_rate / 50; - break; - case 40: - dec->frame_samples = dec->sample_rate / 25; - break; - case 60: - dec->frame_samples = 3 * dec->sample_rate / 50; - break; - default: - GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); - break; - } - - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, - GST_SECOND, dec->sample_rate); - - GST_INFO_OBJECT (dec, - "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" - GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, - dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); - -done: - gst_object_unref (dec); - return ret; + return TRUE; } static gboolean -opus_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) +gst_opus_dec_stop (GstAudioDecoder * dec) { - gboolean res = TRUE; - GstOpusDec *dec; - guint64 scale = 1; + GstOpusDec *odec = GST_OPUS_DEC (dec); - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); + gst_opus_dec_reset (odec); - if (dec->packetno < 1) { - res = FALSE; - goto cleanup; - } - - if (src_format == *dest_format) { - *dest_value = src_value; - res = TRUE; - goto cleanup; - } - - if (pad == dec->sinkpad && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) { - res = FALSE; - goto cleanup; - } - - switch (src_format) { - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = sizeof (gint16) * dec->n_channels; - case GST_FORMAT_DEFAULT: - *dest_value = - gst_util_uint64_scale_int (scale * src_value, - dec->sample_rate, GST_SECOND); - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * sizeof (gint16) * dec->n_channels; - break; - case GST_FORMAT_TIME: - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, - dec->sample_rate); - break; - default: - res = FALSE; - break; - } - break; - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = src_value / (sizeof (gint16) * dec->n_channels); - break; - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, - dec->sample_rate * sizeof (gint16) * dec->n_channels); - break; - default: - res = FALSE; - break; - } - break; - default: - res = FALSE; - break; - } - -cleanup: - gst_object_unref (dec); - return res; -} - -static const GstQueryType * -opus_get_sink_query_types (GstPad * pad) -{ - static const GstQueryType opus_dec_sink_query_types[] = { - GST_QUERY_CONVERT, - 0 - }; - - return opus_dec_sink_query_types; -} - -static gboolean -opus_dec_sink_query (GstPad * pad, GstQuery * query) -{ - GstOpusDec *dec; - gboolean res; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - res = opus_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val); - if (res) { - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (dec); - return res; -} - -static const GstQueryType * -opus_get_src_query_types (GstPad * pad) -{ - static const GstQueryType opus_dec_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - 0 - }; - - return opus_dec_src_query_types; -} - -static gboolean -opus_dec_src_query (GstPad * pad, GstQuery * query) -{ - GstOpusDec *dec; - gboolean res = FALSE; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION:{ - GstSegment segment; - GstFormat format; - gint64 cur; - - gst_query_parse_position (query, &format, NULL); - - GST_PAD_STREAM_LOCK (dec->sinkpad); - segment = dec->segment; - GST_PAD_STREAM_UNLOCK (dec->sinkpad); - - if (segment.format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (dec, "segment not initialised yet"); - break; - } - - if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, - segment.last_stop, &format, &cur))) { - gst_query_set_position (query, format, cur); - } - break; - } - case GST_QUERY_DURATION:{ - GstFormat format = GST_FORMAT_TIME; - gint64 dur; - - /* get duration from demuxer */ - if (!gst_pad_query_peer_duration (dec->sinkpad, &format, &dur)) - break; - - gst_query_parse_duration (query, &format, NULL); - - /* and convert it into the requested format */ - if ((res = opus_dec_convert (dec->srcpad, GST_FORMAT_TIME, - dur, &format, &dur))) { - gst_query_set_duration (query, format, dur); - } - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (dec); - return res; -} - -static gboolean -opus_dec_src_event (GstPad * pad, GstEvent * event) -{ - gboolean res = FALSE; - GstOpusDec *dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK:{ - GstFormat format, tformat; - gdouble rate; - GstEvent *real_seek; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - gint64 tcur, tstop; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, - &stop_type, &stop); - - /* we have to ask our peer to seek to time here as we know - * nothing about how to generate a granulepos from the src - * formats or anything. - * - * First bring the requested format to time - */ - tformat = GST_FORMAT_TIME; - if (!(res = opus_dec_convert (pad, format, cur, &tformat, &tcur))) - break; - if (!(res = opus_dec_convert (pad, format, stop, &tformat, &tstop))) - break; - - /* then seek with time on the peer */ - real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, - flags, cur_type, tcur, stop_type, tstop); - - GST_LOG_OBJECT (dec, "seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (tcur)); - - res = gst_pad_push_event (dec->sinkpad, real_seek); - gst_event_unref (event); - break; - } - default: - res = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (dec); - return res; -} - -static gboolean -opus_dec_sink_event (GstPad * pad, GstEvent * event) -{ - GstOpusDec *dec; - gboolean ret = FALSE; - - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling %s event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT:{ - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - if (rate <= 0.0) - goto newseg_wrong_rate; - - if (update) { - /* time progressed without data, see if we can fill the gap with - * some concealment data */ - if (dec->segment.last_stop < start) { - GstClockTime duration; - - duration = start - dec->segment.last_stop; - opus_dec_chain_parse_data (dec, NULL, dec->segment.last_stop, - duration); - } - } - - /* now configure the values */ - gst_segment_set_newsegment_full (&dec->segment, update, - rate, arate, GST_FORMAT_TIME, start, stop, time); - - dec->granulepos = -1; - - GST_DEBUG_OBJECT (dec, "segment now: cur = %" GST_TIME_FORMAT " [%" - GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]", - GST_TIME_ARGS (dec->segment.last_stop), - GST_TIME_ARGS (dec->segment.start), - GST_TIME_ARGS (dec->segment.stop)); - - ret = gst_pad_push_event (dec->srcpad, event); - break; - } - default: - ret = gst_pad_event_default (pad, event); - break; - } - - gst_object_unref (dec); - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); - gst_object_unref (dec); - return FALSE; - } -newseg_wrong_rate: - { - GST_DEBUG_OBJECT (dec, "negative rates not supported yet"); - gst_object_unref (dec); - return FALSE; - } + return TRUE; } static GstFlowReturn -opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) +gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { - GstCaps *caps; - int err; - -#if 0 - dec->samples_per_frame = opus_packet_get_samples_per_frame ( - (const unsigned char *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); -#endif - -#if 0 - if (memcmp (dec->header.codec_id, "OPUS ", 8) != 0) - goto invalid_header; -#endif - - dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); - if (!dec->state || err != OPUS_OK) - goto init_failed; - - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, - GST_SECOND, dec->sample_rate); - - /* set caps */ - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, - "signed", G_TYPE_BOOLEAN, TRUE, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); - - GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", - dec->sample_rate, dec->n_channels, dec->frame_size); - - if (!gst_pad_set_caps (dec->srcpad, caps)) - goto nego_failed; - - gst_caps_unref (caps); return GST_FLOW_OK; - - /* ERRORS */ -#if 0 -invalid_header: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("Invalid header")); - return GST_FLOW_ERROR; - } -mode_init_failed: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("Mode initialization failed: %d", error)); - return GST_FLOW_ERROR; - } -#endif -init_failed: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("couldn't initialize decoder")); - return GST_FLOW_ERROR; - } -nego_failed: - { - GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("couldn't negotiate format")); - gst_caps_unref (caps); - return GST_FLOW_NOT_NEGOTIATED; - } } -#if 0 static GstFlowReturn -opus_dec_chain_parse_comments (GstOpusDec * dec, GstBuffer * buf) +gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) { - GstTagList *list; - gchar *encoder = NULL; - - list = gst_tag_list_from_vorbiscomment_buffer (buf, NULL, 0, &encoder); - - if (!list) { - GST_WARNING_OBJECT (dec, "couldn't decode comments"); - list = gst_tag_list_new (); - } - - if (encoder) { - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_ENCODER, encoder, NULL); - } - - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_AUDIO_CODEC, "Opus", NULL); - - if (dec->header.bytes_per_packet > 0) { - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_BITRATE, (guint) dec->header.bytes_per_packet * 8, NULL); - } - - GST_INFO_OBJECT (dec, "tags: %" GST_PTR_FORMAT, list); - - gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, list); - - g_free (encoder); - g_free (ver); - return GST_FLOW_OK; } -#endif static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, @@ -724,11 +184,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, gint16 *out_data; int n, err; - if (timestamp != -1) { - dec->segment.last_stop = timestamp; - dec->granulepos = -1; - } - if (dec->state == NULL) { GstCaps *caps; @@ -747,7 +202,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", dec->sample_rate, dec->n_channels, dec->frame_size); - if (!gst_pad_set_caps (dec->srcpad, caps)) + if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) GST_ERROR ("nego failure"); gst_caps_unref (caps); @@ -767,14 +222,15 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, size = 0; } + GST_DEBUG ("frames %d", opus_packet_get_nb_frames (data, size)); GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data)); GST_DEBUG ("samples_per_frame %d", opus_packet_get_samples_per_frame (data, 48000)); GST_DEBUG ("channels %d", opus_packet_get_nb_channels (data)); - res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, + res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), GST_BUFFER_OFFSET_NONE, dec->frame_samples * dec->n_channels * 2, - GST_PAD_CAPS (dec->srcpad), &outbuf); + GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf); if (res != GST_FLOW_OK) { GST_DEBUG_OBJECT (dec, "buf alloc flow: %s", gst_flow_get_name (res)); @@ -783,7 +239,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, out_data = (gint16 *) GST_BUFFER_DATA (outbuf); - GST_LOG_OBJECT (dec, "decoding frame"); + GST_LOG_OBJECT (dec, "decoding %d sample frame", dec->frame_samples); n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, 0); if (n < 0) { @@ -792,8 +248,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, } if (!GST_CLOCK_TIME_IS_VALID (timestamp)) { - timestamp = gst_util_uint64_scale_int (dec->granulepos - dec->frame_size, - GST_SECOND, dec->sample_rate); + GST_WARNING_OBJECT (dec, "No timestamp in -> no timestamp out"); } GST_DEBUG_OBJECT (dec, "timestamp=%" GST_TIME_FORMAT, @@ -801,18 +256,12 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf); - if (dec->discont) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - dec->discont = 0; - } - - dec->segment.last_stop += dec->frame_duration; GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (dec->frame_duration)); - res = gst_pad_push (dec->srcpad, outbuf); + res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); @@ -824,63 +273,173 @@ creation_failed: return GST_FLOW_ERROR; } +static gint +gst_opus_dec_get_frame_samples (GstOpusDec * dec) +{ + gint frame_samples = 0; + switch (dec->frame_size) { + case 2: + frame_samples = dec->sample_rate / 400; + break; + case 5: + frame_samples = dec->sample_rate / 200; + break; + case 10: + frame_samples = dec->sample_rate / 100; + break; + case 20: + frame_samples = dec->sample_rate / 50; + break; + case 40: + frame_samples = dec->sample_rate / 25; + break; + case 60: + frame_samples = 3 * dec->sample_rate / 50; + break; + default: + GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); + frame_samples = 0; + break; + } + return frame_samples; +} + +static gboolean +gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) +{ + GstOpusDec *dec = GST_OPUS_DEC (bdec); + gboolean ret = TRUE; + GstStructure *s; + const GValue *streamheader; + + GST_DEBUG_OBJECT (dec, "set_format: %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + if ((streamheader = gst_structure_get_value (s, "streamheader")) && + G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && + gst_value_array_get_size (streamheader) >= 2) { + const GValue *header, *vorbiscomment; + GstBuffer *buf; + GstFlowReturn res = GST_FLOW_OK; + + header = gst_value_array_get_value (streamheader, 0); + if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { + buf = gst_value_get_buffer (header); + res = gst_opus_dec_parse_header (dec, buf); + if (res != GST_FLOW_OK) + goto done; + gst_buffer_replace (&dec->streamheader, buf); + } + + vorbiscomment = gst_value_array_get_value (streamheader, 1); + if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { + buf = gst_value_get_buffer (vorbiscomment); + res = gst_opus_dec_parse_comments (dec, buf); + if (res != GST_FLOW_OK) + goto done; + gst_buffer_replace (&dec->vorbiscomment, buf); + } + } + if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { + GST_WARNING_OBJECT (dec, "Frame size not included in caps"); + } + if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { + GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); + } + if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { + GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); + } + + dec->frame_samples = gst_opus_dec_get_frame_samples (dec); + dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, + GST_SECOND, dec->sample_rate); + GST_INFO_OBJECT (dec, + "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" + GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, + dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sample_rate, + "channels", G_TYPE_INT, dec->n_channels, + "signed", G_TYPE_BOOLEAN, TRUE, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); + gst_caps_unref (caps); + +done: + return ret; +} + +static gboolean +memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) +{ + gsize size1, size2; + + size1 = GST_BUFFER_SIZE (buf1); + size2 = GST_BUFFER_SIZE (buf2); + + if (size1 != size2) + return FALSE; + + return !memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2), size1); +} + static GstFlowReturn -opus_dec_chain (GstPad * pad, GstBuffer * buf) +gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) { GstFlowReturn res; GstOpusDec *dec; - dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); - GST_LOG_OBJECT (pad, + /* no fancy draining */ + if (G_UNLIKELY (!buf)) + return GST_FLOW_OK; + + dec = GST_OPUS_DEC (adec); + GST_LOG_OBJECT (dec, "Got buffer ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - if (GST_BUFFER_IS_DISCONT (buf)) { - dec->discont = TRUE; + /* If we have the streamheader and vorbiscomment from the caps already + * ignore them here */ + if (dec->streamheader && dec->vorbiscomment) { + if (memcmp_buffers (dec->streamheader, buf)) { + GST_DEBUG_OBJECT (dec, "found streamheader"); + gst_audio_decoder_finish_frame (adec, NULL, 1); + res = GST_FLOW_OK; + } else if (memcmp_buffers (dec->vorbiscomment, buf)) { + GST_DEBUG_OBJECT (dec, "found vorbiscomments"); + gst_audio_decoder_finish_frame (adec, NULL, 1); + res = GST_FLOW_OK; + } else { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + } + } else { + /* Otherwise fall back to packet counting and assume that the + * first two packets are the headers. */ + switch (dec->packetno) { + case 0: + GST_DEBUG_OBJECT (dec, "counted streamheader"); + res = gst_opus_dec_parse_header (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + break; + case 1: + GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); + res = gst_opus_dec_parse_comments (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + break; + default: + { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + break; + } + } } - res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), - GST_BUFFER_DURATION (buf)); - -//done: dec->packetno++; - gst_buffer_unref (buf); - gst_object_unref (dec); - return res; } - -static GstStateChangeReturn -opus_dec_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstOpusDec *dec = GST_OPUS_DEC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - case GST_STATE_CHANGE_READY_TO_PAUSED: - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - default: - break; - } - - ret = parent_class->change_state (element, transition); - if (ret != GST_STATE_CHANGE_SUCCESS) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_opus_dec_reset (dec); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 886a907532..8389580e3b 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -22,6 +22,7 @@ #define __GST_OPUS_DEC_H__ #include +#include #include G_BEGIN_DECLS @@ -41,11 +42,7 @@ typedef struct _GstOpusDec GstOpusDec; typedef struct _GstOpusDecClass GstOpusDecClass; struct _GstOpusDec { - GstElement element; - - /* pads */ - GstPad *sinkpad; - GstPad *srcpad; + GstAudioDecoder element; OpusDecoder *state; int frame_samples; @@ -54,20 +51,15 @@ struct _GstOpusDec { GstClockTime frame_duration; guint64 packetno; - GstSegment segment; /* STREAM LOCK */ - gint64 granulepos; /* -1 = needs to be set from current time */ - gboolean discont; - GstBuffer *streamheader; GstBuffer *vorbiscomment; - GList *extra_headers; int sample_rate; int n_channels; }; struct _GstOpusDecClass { - GstElementClass parent_class; + GstAudioDecoderClass parent_class; }; GType gst_opus_dec_get_type (void); diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 8d40cdf812..4be63cb882 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -47,6 +47,7 @@ #include #include +#include #include #include "gstopusenc.h" @@ -125,18 +126,26 @@ enum static void gst_opus_enc_finalize (GObject * object); -static gboolean gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_opus_enc_chain (GstPad * pad, GstBuffer * buf); +static gboolean gst_opus_enc_sink_event (GstAudioEncoder * benc, + GstEvent * event); static gboolean gst_opus_enc_setup (GstOpusEnc * enc); static void gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_opus_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_opus_enc_change_state (GstElement * element, - GstStateChange transition); -static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush); +static gboolean gst_opus_enc_start (GstAudioEncoder * benc); +static gboolean gst_opus_enc_stop (GstAudioEncoder * benc); +static gboolean gst_opus_enc_set_format (GstAudioEncoder * benc, + GstAudioInfo * info); +static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc, + GstBuffer * buf); +static GstFlowReturn gst_opus_enc_pre_push (GstAudioEncoder * benc, + GstBuffer ** buffer); +static gint64 gst_opus_enc_get_latency (GstOpusEnc * enc); + +static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buffer); static void gst_opus_enc_setup_interfaces (GType opusenc_type) @@ -156,8 +165,8 @@ gst_opus_enc_setup_interfaces (GType opusenc_type) GST_DEBUG_CATEGORY_INIT (opusenc_debug, "opusenc", 0, "Opus encoder"); } -GST_BOILERPLATE_FULL (GstOpusEnc, gst_opus_enc, GstElement, GST_TYPE_ELEMENT, - gst_opus_enc_setup_interfaces); +GST_BOILERPLATE_FULL (GstOpusEnc, gst_opus_enc, GstAudioEncoder, + GST_TYPE_AUDIO_ENCODER, gst_opus_enc_setup_interfaces); static void gst_opus_enc_base_init (gpointer g_class) @@ -179,13 +188,22 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; + GstAudioEncoderClass *base_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + base_class = (GstAudioEncoderClass *) klass; gobject_class->set_property = gst_opus_enc_set_property; gobject_class->get_property = gst_opus_enc_get_property; + base_class->start = GST_DEBUG_FUNCPTR (gst_opus_enc_start); + base_class->stop = GST_DEBUG_FUNCPTR (gst_opus_enc_stop); + base_class->set_format = GST_DEBUG_FUNCPTR (gst_opus_enc_set_format); + base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_enc_handle_frame); + base_class->pre_push = GST_DEBUG_FUNCPTR (gst_opus_enc_pre_push); + base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); + g_object_class_install_property (gobject_class, PROP_AUDIO, g_param_spec_boolean ("audio", "Audio or voice", "Audio or voice", DEFAULT_AUDIO, @@ -229,9 +247,6 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_opus_enc_change_state); } static void @@ -241,375 +256,15 @@ gst_opus_enc_finalize (GObject * object) enc = GST_OPUS_ENC (object); - g_object_unref (enc->adapter); - G_OBJECT_CLASS (parent_class)->finalize (object); } -static gboolean -gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstOpusEnc *enc; - GstStructure *structure; - GstCaps *otherpadcaps; - - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - enc->setup = FALSE; - enc->frame_size = DEFAULT_FRAMESIZE; - otherpadcaps = gst_pad_get_allowed_caps (pad); - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "channels", &enc->n_channels); - gst_structure_get_int (structure, "rate", &enc->sample_rate); - - if (otherpadcaps) { - if (!gst_caps_is_empty (otherpadcaps)) { - GstStructure *ps = gst_caps_get_structure (otherpadcaps, 0); - gst_structure_get_int (ps, "frame-size", &enc->frame_size); - } - gst_caps_unref (otherpadcaps); - } - - GST_DEBUG_OBJECT (pad, "channels=%d rate=%d frame-size=%d", - enc->n_channels, enc->sample_rate, enc->frame_size); - switch (enc->frame_size) { - case 2: - enc->frame_samples = enc->sample_rate / 400; - break; - case 5: - enc->frame_samples = enc->sample_rate / 200; - break; - case 10: - enc->frame_samples = enc->sample_rate / 100; - break; - case 20: - enc->frame_samples = enc->sample_rate / 50; - break; - case 40: - enc->frame_samples = enc->sample_rate / 25; - break; - case 60: - enc->frame_samples = 3 * enc->sample_rate / 50; - break; - default: - GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size); - return FALSE; - break; - } - GST_DEBUG_OBJECT (pad, "frame_samples %d", enc->frame_samples); - - gst_opus_enc_setup (enc); - - return TRUE; -} - - -static GstCaps * -gst_opus_enc_sink_getcaps (GstPad * pad) -{ - GstCaps *caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); - GstCaps *peercaps = NULL; - GstOpusEnc *enc = GST_OPUS_ENC (gst_pad_get_parent_element (pad)); - - peercaps = gst_pad_peer_get_caps (enc->srcpad); - - if (peercaps) { - if (!gst_caps_is_empty (peercaps) && !gst_caps_is_any (peercaps)) { - GstStructure *ps = gst_caps_get_structure (peercaps, 0); - GstStructure *s = gst_caps_get_structure (caps, 0); - gint rate, channels; - - if (gst_structure_get_int (ps, "rate", &rate)) { - gst_structure_fixate_field_nearest_int (s, "rate", rate); - } - - if (gst_structure_get_int (ps, "channels", &channels)) { - gst_structure_fixate_field_nearest_int (s, "channels", channels); - } - } - gst_caps_unref (peercaps); - } - - gst_object_unref (enc); - - return caps; -} - - -static gboolean -gst_opus_enc_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstOpusEnc *enc; - gint64 avg; - - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - - if (enc->samples_in == 0 || enc->bytes_out == 0 || enc->sample_rate == 0) - return FALSE; - - avg = (enc->bytes_out * enc->sample_rate) / (enc->samples_in); - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = src_value * GST_SECOND / avg; - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * avg / GST_SECOND; - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - return res; -} - -static gboolean -gst_opus_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; - GstOpusEnc *enc; - - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - - bytes_per_sample = enc->n_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 * enc->sample_rate; - - if (byterate == 0) - return FALSE; - *dest_value = 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 (enc->sample_rate == 0) - return FALSE; - *dest_value = src_value * GST_SECOND / enc->sample_rate; - 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 = src_value * scale * enc->sample_rate / GST_SECOND; - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - return res; -} - -static gint64 -gst_opus_enc_get_latency (GstOpusEnc * enc) -{ - return gst_util_uint64_scale (enc->frame_samples, GST_SECOND, - enc->sample_rate); -} - -static const GstQueryType * -gst_opus_enc_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_opus_enc_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - GST_QUERY_LATENCY, - 0 - }; - - return gst_opus_enc_src_query_types; -} - -static gboolean -gst_opus_enc_src_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - GstOpusEnc *enc; - - enc = GST_OPUS_ENC (gst_pad_get_parent (pad)); - - 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_peer_position (enc->sinkpad, &req_fmt, &val))) { - gst_query_set_position (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_peer_position (enc->sinkpad, &fmt, &pos))) - break; - - if ((res = - gst_pad_query_peer_convert (enc->sinkpad, 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_peer_duration (enc->sinkpad, &req_fmt, &val))) { - gst_query_set_duration (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_peer_duration (enc->sinkpad, &fmt, &dur))) - break; - - if ((res = - gst_pad_query_peer_convert (enc->sinkpad, 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_opus_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_peer_query (enc->sinkpad, query))) { - gst_query_parse_latency (query, &live, &min_latency, &max_latency); - - latency = gst_opus_enc_get_latency (enc); - - /* 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_peer_query (pad, query); - break; - } - -error: - - gst_object_unref (enc); - - return res; -} - -static gboolean -gst_opus_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_opus_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_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) { - enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); - gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad); - gst_pad_set_event_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sinkevent)); - gst_pad_set_chain_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_chain)); - gst_pad_set_setcaps_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sink_setcaps)); - gst_pad_set_getcaps_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps)); - gst_pad_set_query_function (enc->sinkpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_sink_query)); + GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc); - enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_set_query_function (enc->srcpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_src_query)); - gst_pad_set_query_type_function (enc->srcpad, - GST_DEBUG_FUNCPTR (gst_opus_enc_get_query_types)); - gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); + GST_DEBUG_OBJECT (enc, "init"); enc->n_channels = -1; enc->sample_rate = -1; @@ -625,13 +280,140 @@ gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) enc->dtx = DEFAULT_DTX; enc->packet_loss_percentage = DEFAULT_PACKET_LOSS_PERCENT; - enc->setup = FALSE; - enc->header_sent = FALSE; - - enc->adapter = gst_adapter_new (); + /* arrange granulepos marking (and required perfect ts) */ + gst_audio_encoder_set_mark_granule (benc, TRUE); + gst_audio_encoder_set_perfect_timestamp (benc, TRUE); +} + +static gboolean +gst_opus_enc_start (GstAudioEncoder * benc) +{ + GstOpusEnc *enc = GST_OPUS_ENC (benc); + + GST_DEBUG_OBJECT (enc, "start"); + enc->tags = gst_tag_list_new (); + enc->header_sent = FALSE; + return TRUE; +} + +static gboolean +gst_opus_enc_stop (GstAudioEncoder * benc) +{ + GstOpusEnc *enc = GST_OPUS_ENC (benc); + + GST_DEBUG_OBJECT (enc, "stop"); + enc->header_sent = FALSE; + if (enc->state) { + opus_encoder_destroy (enc->state); + enc->state = NULL; + } + gst_tag_list_free (enc->tags); + enc->tags = NULL; + g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + enc->headers = NULL; + + return TRUE; +} + +static gint64 +gst_opus_enc_get_latency (GstOpusEnc * enc) +{ + gint64 latency = gst_util_uint64_scale (enc->frame_samples, GST_SECOND, + enc->sample_rate); + GST_DEBUG_OBJECT (enc, "Latency: %" GST_TIME_FORMAT, GST_TIME_ARGS (latency)); + return latency; +} + +static gint +gst_opus_enc_get_frame_samples (GstOpusEnc * enc) +{ + gint frame_samples = 0; + switch (enc->frame_size) { + case 2: + frame_samples = enc->sample_rate / 400; + break; + case 5: + frame_samples = enc->sample_rate / 200; + break; + case 10: + frame_samples = enc->sample_rate / 100; + break; + case 20: + frame_samples = enc->sample_rate / 50; + break; + case 40: + frame_samples = enc->sample_rate / 25; + break; + case 60: + frame_samples = 3 * enc->sample_rate / 50; + break; + default: + GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size); + frame_samples = 0; + break; + } + return frame_samples; +} + +static gboolean +gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) +{ + GstOpusEnc *enc; + + enc = GST_OPUS_ENC (benc); + + enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); + enc->sample_rate = GST_AUDIO_INFO_RATE (info); + GST_DEBUG_OBJECT (benc, "Setup with %d channels, %d Hz", enc->n_channels, + enc->sample_rate); + + /* handle reconfigure */ + if (enc->state) { + opus_encoder_destroy (enc->state); + enc->state = NULL; + } + if (!gst_opus_enc_setup (enc)) + return FALSE; + + enc->frame_samples = gst_opus_enc_get_frame_samples (enc); + + /* feedback to base class */ + gst_audio_encoder_set_latency (benc, + gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc)); + gst_audio_encoder_set_frame_samples_min (benc, + enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_samples_max (benc, + enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_max (benc, 0); + + return TRUE; +} + +static GstBuffer * +gst_opus_enc_create_id_buffer (GstOpusEnc * enc) +{ + GstBuffer *buffer; + GstByteWriter bw; + + gst_byte_writer_init (&bw); + + /* See http://wiki.xiph.org/OggOpus */ + gst_byte_writer_put_string_utf8 (&bw, "OpusHead"); + gst_byte_writer_put_uint8 (&bw, 0); /* version number */ + gst_byte_writer_put_uint8 (&bw, enc->n_channels); + gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ + gst_byte_writer_put_uint32_le (&bw, enc->sample_rate); + gst_byte_writer_put_uint16_le (&bw, 0); /* output gain *//* TODO: endianness ? */ + gst_byte_writer_put_uint8 (&bw, 0); /* channel mapping *//* TODO: what is this ? */ + + buffer = gst_byte_writer_reset_and_get_buffer (&bw); + + GST_BUFFER_OFFSET (buffer) = 0; + GST_BUFFER_OFFSET_END (buffer) = 0; + + return buffer; } -#if 0 static GstBuffer * gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) { @@ -649,10 +431,11 @@ gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) empty_tags = gst_tag_list_new (); tags = empty_tags; } - comments = gst_tag_list_to_vorbiscomment_buffer (tags, NULL, - 0, "Encoded with GStreamer Opusenc"); + comments = + gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", + 8, "Encoded with GStreamer Opusenc"); - GST_BUFFER_OFFSET (comments) = enc->bytes_out; + GST_BUFFER_OFFSET (comments) = 0; GST_BUFFER_OFFSET_END (comments) = 0; if (empty_tags) @@ -660,13 +443,14 @@ gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) return comments; } -#endif static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; + GST_DEBUG_OBJECT (enc, "setup"); + enc->setup = FALSE; enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, @@ -692,88 +476,20 @@ gst_opus_enc_setup (GstOpusEnc * enc) return TRUE; -#if 0 -mode_initialization_failed: - GST_ERROR_OBJECT (enc, "Mode initialization failed: %d", error); - return FALSE; -#endif - encoder_creation_failed: GST_ERROR_OBJECT (enc, "Encoder creation failed"); return FALSE; } - -/* push out the buffer and do internal bookkeeping */ -static GstFlowReturn -gst_opus_enc_push_buffer (GstOpusEnc * enc, GstBuffer * buffer) -{ - guint size; - - size = GST_BUFFER_SIZE (buffer); - - enc->bytes_out += size; - - GST_DEBUG_OBJECT (enc, "pushing output buffer of size %u", size); - - return gst_pad_push (enc->srcpad, buffer); -} - -#if 0 -static GstCaps * -gst_opus_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, - GstBuffer * buf2) -{ - GstStructure *structure = NULL; - GstBuffer *buf; - GValue array = { 0 }; - GValue value = { 0 }; - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - g_assert (gst_buffer_is_metadata_writable (buf1)); - g_assert (gst_buffer_is_metadata_writable (buf2)); - - /* mark buffers */ - GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf2, 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); - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&value); - g_value_unset (&array); - - return caps; -} -#endif - - static gboolean -gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event) +gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) { - gboolean res = TRUE; GstOpusEnc *enc; - enc = GST_OPUS_ENC (gst_pad_get_parent (pad)); + enc = GST_OPUS_ENC (benc); + GST_DEBUG_OBJECT (enc, "sink event: %s", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - gst_opus_enc_encode (enc, TRUE); - res = gst_pad_event_default (pad, event); - break; case GST_EVENT_TAG: { GstTagList *list; @@ -782,62 +498,94 @@ gst_opus_enc_sinkevent (GstPad * pad, GstEvent * event) gst_event_parse_tag (event, &list); gst_tag_setter_merge_tags (setter, list, mode); - res = gst_pad_event_default (pad, event); break; } default: - res = gst_pad_event_default (pad, event); break; } - gst_object_unref (enc); - - return res; + return FALSE; } static GstFlowReturn -gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) +gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer) { - GstFlowReturn ret = GST_FLOW_OK; - gint bytes = enc->frame_samples * 2 * enc->n_channels; - gint bytes_per_packet; + GstOpusEnc *enc; - bytes_per_packet = + enc = GST_OPUS_ENC (benc); + + /* FIXME 0.11 ? get rid of this special ogg stuff and have it + * put and use 'codec data' in caps like anything else, + * with all the usual out-of-band advantage etc */ + if (G_UNLIKELY (enc->headers)) { + GSList *header = enc->headers; + + /* try to push all of these, if we lose one, might as well lose all */ + while (header) { + if (ret == GST_FLOW_OK) + ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), header->data); + else + gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), header->data); + header = g_slist_next (header); + } + + g_slist_free (enc->headers); + enc->headers = NULL; + } + + return ret; +} + +static GstFlowReturn +gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) +{ + guint8 *bdata, *data, *mdata = NULL; + gsize bsize, size; + gsize bytes = enc->frame_samples * enc->n_channels * 2; + gsize bytes_per_packet = (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8; + gint ret = GST_FLOW_OK; - if (flush && gst_adapter_available (enc->adapter) % bytes != 0) { - guint diff = bytes - gst_adapter_available (enc->adapter) % bytes; - GstBuffer *buf = gst_buffer_new_and_alloc (diff); + if (G_LIKELY (buf)) { + bdata = GST_BUFFER_DATA (buf); + bsize = GST_BUFFER_SIZE (buf); + if (G_UNLIKELY (bsize % bytes)) { + GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); - memset (GST_BUFFER_DATA (buf), 0, diff); - gst_adapter_push (enc->adapter, buf); + size = ((bsize / bytes) + 1) * bytes; + mdata = g_malloc0 (size); + memcpy (mdata, bdata, bsize); + bdata = NULL; + data = mdata; + } else { + data = bdata; + size = bsize; + } + } else { + GST_DEBUG_OBJECT (enc, "nothing to drain"); + goto done; } - while (gst_adapter_available (enc->adapter) >= bytes) { - gint16 *data; + while (size) { gint outsize; GstBuffer *outbuf; - ret = gst_pad_alloc_buffer_and_set_caps (enc->srcpad, - GST_BUFFER_OFFSET_NONE, bytes_per_packet, GST_PAD_CAPS (enc->srcpad), - &outbuf); + ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), + GST_BUFFER_OFFSET_NONE, bytes_per_packet, + GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf); if (GST_FLOW_OK != ret) goto done; - data = (gint16 *) gst_adapter_take (enc->adapter, bytes); - enc->samples_in += enc->frame_samples; + GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes) to %d bytes", + enc->frame_samples, bytes, bytes_per_packet); - GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - enc->frame_samples, bytes); - - outsize = opus_encode (enc->state, data, enc->frame_samples, + outsize = + opus_encode (enc->state, (const gint16 *) data, enc->frame_samples, GST_BUFFER_DATA (outbuf), bytes_per_packet); - g_free (data); - if (outsize < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; @@ -850,149 +598,132 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush) goto done; } - GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts + - gst_util_uint64_scale_int (enc->frameno_out * enc->frame_samples, - GST_SECOND, enc->sample_rate); - GST_BUFFER_DURATION (outbuf) = - gst_util_uint64_scale_int (enc->frame_samples, GST_SECOND, - enc->sample_rate); - GST_BUFFER_OFFSET (outbuf) = - gst_util_uint64_scale_int (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND, - enc->sample_rate); - - enc->frameno++; - enc->frameno_out++; - - ret = gst_opus_enc_push_buffer (enc, outbuf); + ret = + gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, + enc->frame_samples); if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) goto done; + + data += bytes; + size -= bytes; } done: + if (mdata) + g_free (mdata); + return ret; } +/* + * (really really) FIXME: move into core (dixit tpm) + */ +/** + * _gst_caps_set_buffer_array: + * @caps: a #GstCaps + * @field: field in caps to set + * @buf: header buffers + * + * Adds given buffers to an array of buffers set as the given @field + * on the given @caps. List of buffer arguments must be NULL-terminated. + * + * Returns: input caps with a streamheader field added, or NULL if some error + */ +static GstCaps * +_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, + GstBuffer * buf, ...) +{ + GstStructure *structure = NULL; + va_list va; + GValue array = { 0 }; + GValue value = { 0 }; + + g_return_val_if_fail (caps != NULL, NULL); + g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); + g_return_val_if_fail (field != NULL, NULL); + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&array, GST_TYPE_ARRAY); + + va_start (va, buf); + /* put buffers in a fixed list */ + while (buf) { + g_assert (gst_buffer_is_writable (buf)); + + /* mark buffer */ + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + + g_value_init (&value, GST_TYPE_BUFFER); + buf = gst_buffer_copy (buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + gst_value_set_buffer (&value, buf); + gst_buffer_unref (buf); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + + buf = va_arg (va, GstBuffer *); + } + + gst_structure_set_value (structure, field, &array); + g_value_unset (&array); + + return caps; +} + static GstFlowReturn -gst_opus_enc_chain (GstPad * pad, GstBuffer * buf) +gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) { GstOpusEnc *enc; GstFlowReturn ret = GST_FLOW_OK; - enc = GST_OPUS_ENC (GST_PAD_PARENT (pad)); - - if (!enc->setup) - goto not_setup; + enc = GST_OPUS_ENC (benc); + GST_DEBUG_OBJECT (enc, "handle_frame"); if (!enc->header_sent) { + /* Opus streams in Ogg begin with two 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. */ + GstBuffer *buf1, *buf2; GstCaps *caps; - caps = gst_pad_get_caps (enc->srcpad); - gst_caps_set_simple (caps, - "rate", G_TYPE_INT, enc->sample_rate, - "channels", G_TYPE_INT, enc->n_channels, - "frame-size", G_TYPE_INT, enc->frame_size, NULL); + /* create header buffers */ + buf1 = gst_opus_enc_create_id_buffer (enc); + buf2 = gst_opus_enc_create_metadata_buffer (enc); + + /* mark and put on caps */ + caps = + gst_caps_new_simple ("audio/x-opus", "rate", G_TYPE_INT, + enc->sample_rate, "channels", G_TYPE_INT, enc->n_channels, "frame-size", + G_TYPE_INT, enc->frame_size, NULL); + caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL); /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); - GST_LOG_OBJECT (enc, "rate=%d channels=%d frame-size=%d", - enc->sample_rate, enc->n_channels, enc->frame_size); - gst_pad_set_caps (enc->srcpad, caps); + gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps); + + /* push out buffers */ + /* store buffers for later pre_push sending */ + g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + enc->headers = NULL; + GST_DEBUG_OBJECT (enc, "storing header buffers"); + enc->headers = g_slist_prepend (enc->headers, buf2); + enc->headers = g_slist_prepend (enc->headers, buf1); enc->header_sent = TRUE; } - GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf)); + GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf, + buf ? GST_BUFFER_SIZE (buf) : 0); - /* Save the timestamp of the first buffer. This will be later - * used as offset for all following buffers */ - if (enc->start_ts == GST_CLOCK_TIME_NONE) { - if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { - enc->start_ts = GST_BUFFER_TIMESTAMP (buf); - } else { - enc->start_ts = 0; - } - } - - - /* Check if we have a continous stream, if not drop some samples or the buffer or - * insert some silence samples */ - if (enc->next_ts != GST_CLOCK_TIME_NONE && - GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) { - guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf); - guint64 diff_bytes; - - GST_WARNING_OBJECT (enc, "Buffer is older than previous " - "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT - "), cannot handle. Clipping buffer.", - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (enc->next_ts)); - - diff_bytes = - GST_CLOCK_TIME_TO_FRAMES (diff, enc->sample_rate) * enc->n_channels * 2; - if (diff_bytes >= GST_BUFFER_SIZE (buf)) { - gst_buffer_unref (buf); - return GST_FLOW_OK; - } - buf = gst_buffer_make_metadata_writable (buf); - GST_BUFFER_DATA (buf) += diff_bytes; - GST_BUFFER_SIZE (buf) -= diff_bytes; - - GST_BUFFER_TIMESTAMP (buf) += diff; - if (GST_BUFFER_DURATION_IS_VALID (buf)) - GST_BUFFER_DURATION (buf) -= diff; - } - - if (enc->next_ts != GST_CLOCK_TIME_NONE - && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { - guint64 max_diff = - gst_util_uint64_scale (enc->frame_size, GST_SECOND, enc->sample_rate); - - if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts && - GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > max_diff) { - GST_WARNING_OBJECT (enc, - "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT, - GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, max_diff); - - gst_opus_enc_encode (enc, TRUE); - - enc->frameno_out = 0; - enc->start_ts = GST_BUFFER_TIMESTAMP (buf); - } - } - - if (GST_BUFFER_TIMESTAMP_IS_VALID (buf) - && GST_BUFFER_DURATION_IS_VALID (buf)) - enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); - else - enc->next_ts = GST_CLOCK_TIME_NONE; - - /* push buffer to adapter */ - gst_adapter_push (enc->adapter, buf); - buf = NULL; - - ret = gst_opus_enc_encode (enc, FALSE); - -done: - - if (buf) - gst_buffer_unref (buf); + ret = gst_opus_enc_encode (enc, buf); return ret; - - /* ERRORS */ -not_setup: - { - GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL), - ("encoder not initialized (input is not audio?)")); - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - } - static void gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -1082,49 +813,3 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, break; } } - -static GstStateChangeReturn -gst_opus_enc_change_state (GstElement * element, GstStateChange transition) -{ - GstOpusEnc *enc = GST_OPUS_ENC (element); - GstStateChangeReturn res; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - enc->frameno = 0; - enc->samples_in = 0; - enc->frameno_out = 0; - enc->start_ts = GST_CLOCK_TIME_NONE; - enc->next_ts = GST_CLOCK_TIME_NONE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - /* fall through */ - default: - break; - } - - res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (res == GST_STATE_CHANGE_FAILURE) - return res; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - enc->setup = FALSE; - enc->header_sent = FALSE; - if (enc->state) { - opus_encoder_destroy (enc->state); - enc->state = NULL; - } - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); - default: - break; - } - - return res; -} diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 5cb54598af..fe7b94e68d 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -24,7 +24,7 @@ #include -#include +#include #include @@ -48,16 +48,9 @@ typedef struct _GstOpusEnc GstOpusEnc; typedef struct _GstOpusEncClass GstOpusEncClass; struct _GstOpusEnc { - GstElement element; + GstAudioEncoder element; - /* pads */ - GstPad *sinkpad; - GstPad *srcpad; - - //OpusHeader header; - //OpusMode *mode; OpusEncoder *state; - GstAdapter *adapter; /* properties */ gboolean audio_or_voip; @@ -71,28 +64,20 @@ struct _GstOpusEnc { gboolean dtx; gint packet_loss_percentage; - int frame_samples; - + gint frame_samples; gint n_channels; gint sample_rate; gboolean setup; gboolean header_sent; - gboolean eos; - guint64 samples_in; - guint64 bytes_out; + GSList *headers; - guint64 frameno; - guint64 frameno_out; - - GstClockTime start_ts; - GstClockTime next_ts; - guint64 granulepos_offset; + GstTagList *tags; }; struct _GstOpusEncClass { - GstElementClass parent_class; + GstAudioEncoderClass parent_class; /* signals */ void (*frame_encoded) (GstElement *element); From ffc4408d539541ff50080e1ae2665bf7851f4a77 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 17:05:17 +0000 Subject: [PATCH 040/197] opusdec: rewrite logic Parameters such as frame size, etc, are variable. Pretty much everything can change within a stream, so be prepared about it, and do not cache parameters in the decoder. --- ext/opus/gstopusdec.c | 123 ++++++++++++++++-------------------------- ext/opus/gstopusdec.h | 4 +- 2 files changed, 47 insertions(+), 80 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 1abe3201cb..f14e4e0ded 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -48,8 +48,6 @@ GST_DEBUG_CATEGORY_STATIC (opusdec_debug); #define GST_CAT_DEFAULT opusdec_debug -#define DEC_MAX_FRAME_SIZE 2000 - static GstStaticPadTemplate opus_dec_src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -117,14 +115,13 @@ static void gst_opus_dec_reset (GstOpusDec * dec) { dec->packetno = 0; - dec->frame_size = 0; - dec->frame_samples = 960; - dec->frame_duration = 0; if (dec->state) { opus_decoder_destroy (dec->state); dec->state = NULL; } + dec->next_ts = 0; + gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); } @@ -183,6 +180,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstBuffer *outbuf; gint16 *out_data; int n, err; + int samples; + unsigned int packet_size; if (dec->state == NULL) { GstCaps *caps; @@ -199,8 +198,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, "endianness", G_TYPE_INT, G_BYTE_ORDER, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); - GST_DEBUG_OBJECT (dec, "rate=%d channels=%d frame-size=%d", - dec->sample_rate, dec->n_channels, dec->frame_size); + GST_DEBUG_OBJECT (dec, "rate=%d channels=%d", + dec->sample_rate, dec->n_channels); if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) GST_ERROR ("nego failure"); @@ -222,14 +221,15 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, size = 0; } - GST_DEBUG ("frames %d", opus_packet_get_nb_frames (data, size)); + samples = + opus_packet_get_samples_per_frame (data, + dec->sample_rate) * opus_packet_get_nb_frames (data, size); + packet_size = samples * dec->n_channels * 2; GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data)); - GST_DEBUG ("samples_per_frame %d", opus_packet_get_samples_per_frame (data, - 48000)); - GST_DEBUG ("channels %d", opus_packet_get_nb_channels (data)); + GST_DEBUG ("samples %d", samples); res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), - GST_BUFFER_OFFSET_NONE, dec->frame_samples * dec->n_channels * 2, + GST_BUFFER_OFFSET_NONE, packet_size, GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf); if (res != GST_FLOW_OK) { @@ -239,27 +239,28 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, out_data = (gint16 *) GST_BUFFER_DATA (outbuf); - GST_LOG_OBJECT (dec, "decoding %d sample frame", dec->frame_samples); + GST_LOG_OBJECT (dec, "decoding %d samples, in size %u", samples, size); - n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, 0); + n = opus_decode (dec->state, data, size, out_data, samples, 0); if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); return GST_FLOW_ERROR; } + GST_DEBUG_OBJECT (dec, "decoded %d samples", n); - if (!GST_CLOCK_TIME_IS_VALID (timestamp)) { - GST_WARNING_OBJECT (dec, "No timestamp in -> no timestamp out"); + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + } else { + GST_BUFFER_TIMESTAMP (outbuf) = dec->next_ts; } - GST_DEBUG_OBJECT (dec, "timestamp=%" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - - GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); - GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf); + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale (n, GST_SECOND, dec->sample_rate); + dec->next_ts = GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf); GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_TIME_ARGS (dec->frame_duration)); + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); @@ -273,37 +274,6 @@ creation_failed: return GST_FLOW_ERROR; } -static gint -gst_opus_dec_get_frame_samples (GstOpusDec * dec) -{ - gint frame_samples = 0; - switch (dec->frame_size) { - case 2: - frame_samples = dec->sample_rate / 400; - break; - case 5: - frame_samples = dec->sample_rate / 200; - break; - case 10: - frame_samples = dec->sample_rate / 100; - break; - case 20: - frame_samples = dec->sample_rate / 50; - break; - case 40: - frame_samples = dec->sample_rate / 25; - break; - case 60: - frame_samples = 3 * dec->sample_rate / 50; - break; - default: - GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size); - frame_samples = 0; - break; - } - return frame_samples; -} - static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) { @@ -340,24 +310,6 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) gst_buffer_replace (&dec->vorbiscomment, buf); } } - if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) { - GST_WARNING_OBJECT (dec, "Frame size not included in caps"); - } - if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { - GST_WARNING_OBJECT (dec, "Number of channels not included in caps"); - } - if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { - GST_WARNING_OBJECT (dec, "Sample rate not included in caps"); - } - - dec->frame_samples = gst_opus_dec_get_frame_samples (dec); - dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples, - GST_SECOND, dec->sample_rate); - GST_INFO_OBJECT (dec, - "Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %" - GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate, - dec->frame_samples, GST_TIME_ARGS (dec->frame_duration)); - caps = gst_caps_new_simple ("audio/x-raw-int", "rate", G_TYPE_INT, dec->sample_rate, "channels", G_TYPE_INT, dec->n_channels, @@ -385,6 +337,13 @@ memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) return !memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2), size1); } +static gboolean +gst_opus_dec_is_header (GstBuffer * buf, const char *magic, guint magic_size) +{ + return (GST_BUFFER_SIZE (buf) >= magic_size + && !memcmp (magic, GST_BUFFER_DATA (buf), magic_size)); +} + static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) { @@ -421,14 +380,24 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) * first two packets are the headers. */ switch (dec->packetno) { case 0: - GST_DEBUG_OBJECT (dec, "counted streamheader"); - res = gst_opus_dec_parse_header (dec, buf); - gst_audio_decoder_finish_frame (adec, NULL, 1); + if (gst_opus_dec_is_header (buf, "OpusHead", 8)) { + GST_DEBUG_OBJECT (dec, "found streamheader"); + res = gst_opus_dec_parse_header (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + } else { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + } break; case 1: - GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); - res = gst_opus_dec_parse_comments (dec, buf); - gst_audio_decoder_finish_frame (adec, NULL, 1); + if (gst_opus_dec_is_header (buf, "OpusTags", 8)) { + GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); + res = gst_opus_dec_parse_comments (dec, buf); + gst_audio_decoder_finish_frame (adec, NULL, 1); + } else { + res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), + GST_BUFFER_DURATION (buf)); + } break; default: { diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 8389580e3b..38dd2799ad 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -45,11 +45,9 @@ struct _GstOpusDec { GstAudioDecoder element; OpusDecoder *state; - int frame_samples; - gint frame_size; - GstClockTime frame_duration; guint64 packetno; + GstClockTime next_ts; GstBuffer *streamheader; GstBuffer *vorbiscomment; From 310daa509638f808b60047e49d2339580b9cb49d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 17:24:20 +0000 Subject: [PATCH 041/197] opusdec: allow negotiation of rate/channels with downstream Since an opus stream may be decoded to any (sensible) rate, and either stereo or mono, we try to accomodate downstream. --- ext/opus/gstopusdec.c | 74 +++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index f14e4e0ded..d4cdabdded 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -129,8 +129,8 @@ gst_opus_dec_reset (GstOpusDec * dec) static void gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) { - dec->sample_rate = 48000; - dec->n_channels = 2; + dec->sample_rate = 0; + dec->n_channels = 0; gst_opus_dec_reset (dec); } @@ -170,6 +170,48 @@ gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) return GST_FLOW_OK; } +static void +gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec) +{ + GstPad *srcpad, *peer; + GstStructure *s; + GstCaps *caps; + const GstCaps *template_caps; + const GstCaps *peer_caps; + + srcpad = GST_AUDIO_DECODER_SRC_PAD (dec); + peer = gst_pad_get_peer (srcpad); + + if (peer) { + template_caps = gst_pad_get_pad_template_caps (srcpad); + peer_caps = gst_pad_get_caps (peer); + GST_DEBUG_OBJECT (dec, "Peer caps: %" GST_PTR_FORMAT, peer_caps); + caps = gst_caps_intersect (template_caps, peer_caps); + gst_pad_fixate_caps (peer, caps); + GST_DEBUG_OBJECT (dec, "Fixated caps: %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { + dec->n_channels = 2; + GST_WARNING_OBJECT (dec, "Failed to get channels, using default %d", + dec->n_channels); + } else { + GST_DEBUG_OBJECT (dec, "Got channels %d", dec->n_channels); + } + if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { + dec->sample_rate = 48000; + GST_WARNING_OBJECT (dec, "Failed to get rate, using default %d", + dec->sample_rate); + } else { + GST_DEBUG_OBJECT (dec, "Got sample rate %d", dec->sample_rate); + } + + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); + } else { + GST_WARNING_OBJECT (dec, "Failed to get src pad peer"); + } +} + static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, GstClockTime timestamp, GstClockTime duration) @@ -184,27 +226,13 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, unsigned int packet_size; if (dec->state == NULL) { - GstCaps *caps; + gst_opus_dec_setup_from_peer_caps (dec); + GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", + dec->n_channels, dec->sample_rate); dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; - - /* set caps */ - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, - "signed", G_TYPE_BOOLEAN, TRUE, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); - - GST_DEBUG_OBJECT (dec, "rate=%d channels=%d", - dec->sample_rate, dec->n_channels); - - if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps)) - GST_ERROR ("nego failure"); - - gst_caps_unref (caps); } if (buf) { @@ -310,14 +338,6 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) gst_buffer_replace (&dec->vorbiscomment, buf); } } - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->n_channels, - "signed", G_TYPE_BOOLEAN, TRUE, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); - gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); - gst_caps_unref (caps); done: return ret; From 8bb91f923f8382dea7da2c52d7b58dc709026edf Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 15 Nov 2011 19:53:33 +0000 Subject: [PATCH 042/197] opusparse: add opusparse element A very simple element that parses Opus streams from the ad hoc framing used by the Opus test vectors. --- ext/opus/Makefile.am | 4 ++-- ext/opus/gstopus.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 57e1692645..6fe723ecce 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstopus.la -libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -15,4 +15,4 @@ libgstopus_la_LIBADD = \ libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) libgstopus_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstopusenc.h gstopusdec.h +noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index 65e9dcdc58..c5f68a131c 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -23,6 +23,7 @@ #include "gstopusdec.h" #include "gstopusenc.h" +#include "gstopusparse.h" #include @@ -38,6 +39,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_OPUS_DEC)) return FALSE; + if (!gst_element_register (plugin, "opusparse", GST_RANK_NONE, + GST_TYPE_OPUS_PARSE)) + return FALSE; + gst_tag_register_musicbrainz_tags (); return TRUE; From 656e1f8d9f3906d61dc212ed9b15fb18e545ba2e Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 18:35:29 +0000 Subject: [PATCH 043/197] opusdec: let the base class handle all timing --- ext/opus/gstopusdec.c | 33 +++++---------------------------- ext/opus/gstopusdec.h | 1 - 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index d4cdabdded..624ac81e42 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -120,8 +120,6 @@ gst_opus_dec_reset (GstOpusDec * dec) dec->state = NULL; } - dec->next_ts = 0; - gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); } @@ -213,8 +211,7 @@ gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec) } static GstFlowReturn -opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, - GstClockTime timestamp, GstClockTime duration) +opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) { GstFlowReturn res = GST_FLOW_OK; gint size; @@ -240,8 +237,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, size = GST_BUFFER_SIZE (buf); GST_DEBUG_OBJECT (dec, "received buffer of size %u", size); - - /* copy timestamp */ } else { /* concealment data, pass NULL as the bits parameters */ GST_DEBUG_OBJECT (dec, "creating concealment data"); @@ -276,20 +271,6 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf, } GST_DEBUG_OBJECT (dec, "decoded %d samples", n); - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - GST_BUFFER_TIMESTAMP (outbuf) = timestamp; - } else { - GST_BUFFER_TIMESTAMP (outbuf) = dec->next_ts; - } - - GST_BUFFER_DURATION (outbuf) = - gst_util_uint64_scale (n, GST_SECOND, dec->sample_rate); - dec->next_ts = GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf); - - GST_LOG_OBJECT (dec, "pushing buffer with ts=%" GST_TIME_FORMAT ", dur=%" - GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); - res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) @@ -392,8 +373,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) gst_audio_decoder_finish_frame (adec, NULL, 1); res = GST_FLOW_OK; } else { - res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), - GST_BUFFER_DURATION (buf)); + res = opus_dec_chain_parse_data (dec, buf); } } else { /* Otherwise fall back to packet counting and assume that the @@ -405,8 +385,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) res = gst_opus_dec_parse_header (dec, buf); gst_audio_decoder_finish_frame (adec, NULL, 1); } else { - res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), - GST_BUFFER_DURATION (buf)); + res = opus_dec_chain_parse_data (dec, buf); } break; case 1: @@ -415,14 +394,12 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) res = gst_opus_dec_parse_comments (dec, buf); gst_audio_decoder_finish_frame (adec, NULL, 1); } else { - res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), - GST_BUFFER_DURATION (buf)); + res = opus_dec_chain_parse_data (dec, buf); } break; default: { - res = opus_dec_chain_parse_data (dec, buf, GST_BUFFER_TIMESTAMP (buf), - GST_BUFFER_DURATION (buf)); + res = opus_dec_chain_parse_data (dec, buf); break; } } diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 38dd2799ad..9e78330e1f 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -47,7 +47,6 @@ struct _GstOpusDec { OpusDecoder *state; guint64 packetno; - GstClockTime next_ts; GstBuffer *streamheader; GstBuffer *vorbiscomment; From 0031b486848464651b18bb1c53ca23c179417381 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 18:43:53 +0000 Subject: [PATCH 044/197] opusenc: fix constrained-vbr property name typo --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 4be63cb882..838e7826f6 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -226,7 +226,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) "Constant bit rate", DEFAULT_CBR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR, - g_param_spec_boolean ("constrained-cbr", "Constrained VBR", + g_param_spec_boolean ("constrained-vbr", "Constrained VBR", "Constrained VBR", DEFAULT_CONSTRAINED_VBR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_COMPLEXITY, From ccc04a1529e89da251383f8988f7c129265d20e3 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 18:49:03 +0000 Subject: [PATCH 045/197] opusenc: do not include variable fields in caps Those can vary from one packet to the next, so have no reason to be in the caps. --- ext/opus/gstopusenc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 838e7826f6..f632a72056 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -93,9 +93,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-opus, " - "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " - "channels = (int) [ 1, 2 ], " "frame-size = (int) [ 2, 60 ]") + GST_STATIC_CAPS ("audio/x-opus") ); #define DEFAULT_AUDIO TRUE @@ -695,10 +693,7 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) buf2 = gst_opus_enc_create_metadata_buffer (enc); /* mark and put on caps */ - caps = - gst_caps_new_simple ("audio/x-opus", "rate", G_TYPE_INT, - enc->sample_rate, "channels", G_TYPE_INT, enc->n_channels, "frame-size", - G_TYPE_INT, enc->frame_size, NULL); + caps = gst_caps_from_string ("audio/x-opus"); caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL); /* negotiate with these caps */ From d2aba01428a5a0c4aa49dee1816a22dfd312523a Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 19:22:44 +0000 Subject: [PATCH 046/197] opusenc: the encoder might not make use of all the bytes --- ext/opus/gstopusenc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f632a72056..327c5fae80 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -588,7 +588,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; goto done; - } else if (outsize != bytes_per_packet) { + } else if (outsize > bytes_per_packet) { GST_WARNING_OBJECT (enc, "Encoded size %d is different from %d bytes per packet", outsize, bytes_per_packet); @@ -596,6 +596,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) goto done; } + GST_BUFFER_SIZE (outbuf) = outsize; + ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, enc->frame_samples); From 7c0c221fa4f015671743fc5706ab7cb0283d0081 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 16 Nov 2011 19:40:20 +0000 Subject: [PATCH 047/197] opusenc: make frame-size an enum It only supports a set number of specific values (including a non integer one). --- ext/opus/gstopusenc.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 327c5fae80..ed3e82ebd6 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -80,6 +80,32 @@ gst_opus_enc_bandwidth_get_type (void) return id; } +#define GST_OPUS_ENC_TYPE_FRAME_SIZE (gst_opus_enc_frame_size_get_type()) +static GType +gst_opus_enc_frame_size_get_type (void) +{ + static const GEnumValue values[] = { + {2, "2.5", "2.5"}, + {5, "5", "5"}, + {10, "10", "10"}, + {20, "20", "20"}, + {40, "40", "40"}, + {60, "60", "60"}, + {0, NULL, NULL} + }; + static volatile GType id = 0; + + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + + _id = g_enum_register_static ("GstOpusEncFrameSize", values); + + g_once_init_leave ((gsize *) & id, _id); + } + + return id; +} + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -216,8 +242,9 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) "Audio Band Width", GST_OPUS_ENC_TYPE_BANDWIDTH, DEFAULT_BANDWIDTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_FRAME_SIZE, - g_param_spec_int ("frame-size", "Frame Size", - "The duration of an audio frame, in ms", 2, 60, DEFAULT_FRAMESIZE, + g_param_spec_enum ("frame-size", "Frame Size", + "The duration of an audio frame, in ms", + GST_OPUS_ENC_TYPE_FRAME_SIZE, DEFAULT_FRAMESIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_CBR, g_param_spec_boolean ("cbr", "Constant bit rate", @@ -740,7 +767,7 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_enum (value, enc->bandwidth); break; case PROP_FRAME_SIZE: - g_value_set_int (value, enc->frame_size); + g_value_set_enum (value, enc->frame_size); break; case PROP_CBR: g_value_set_boolean (value, enc->cbr); @@ -785,7 +812,7 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, enc->bandwidth = g_value_get_enum (value); break; case PROP_FRAME_SIZE: - enc->frame_size = g_value_get_int (value); + enc->frame_size = g_value_get_enum (value); break; case PROP_CBR: enc->cbr = g_value_get_boolean (value); From efcd0383460a3bcee9d6f83a12888468148c2e70 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sat, 19 Nov 2011 15:58:09 +0000 Subject: [PATCH 048/197] opusenc: fix terminating NUL being written in signature --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index ed3e82ebd6..2f8dc5bfbe 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -423,7 +423,7 @@ gst_opus_enc_create_id_buffer (GstOpusEnc * enc) gst_byte_writer_init (&bw); /* See http://wiki.xiph.org/OggOpus */ - gst_byte_writer_put_string_utf8 (&bw, "OpusHead"); + gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); gst_byte_writer_put_uint8 (&bw, 0); /* version number */ gst_byte_writer_put_uint8 (&bw, enc->n_channels); gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ From 7030b04f5e1af896f25aa7de974089de25351172 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sun, 20 Nov 2011 09:52:46 +0000 Subject: [PATCH 049/197] opus: make opusparse set headers on caps Header-on-caps code moved to a new shared location to avoid duplicating the code. --- ext/opus/Makefile.am | 4 +- ext/opus/gstopusenc.c | 138 ++------------------------------- ext/opus/gstopusheader.c | 163 +++++++++++++++++++++++++++++++++++++++ ext/opus/gstopusheader.h | 32 ++++++++ 4 files changed, 204 insertions(+), 133 deletions(-) create mode 100644 ext/opus/gstopusheader.c create mode 100644 ext/opus/gstopusheader.h diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 6fe723ecce..88845a3cab 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstopus.la -libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -15,4 +15,4 @@ libgstopus_la_LIBADD = \ libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) libgstopus_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h +noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 2f8dc5bfbe..f13490ee74 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1,6 +1,7 @@ /* GStreamer Opus Encoder * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2008> Sebastian Dröge + * Copyright (C) <2011> Vincent Penquerc'h * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -46,9 +47,8 @@ #include #include -#include -#include #include +#include "gstopusheader.h" #include "gstopusenc.h" GST_DEBUG_CATEGORY_STATIC (opusenc_debug); @@ -414,61 +414,6 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) return TRUE; } -static GstBuffer * -gst_opus_enc_create_id_buffer (GstOpusEnc * enc) -{ - GstBuffer *buffer; - GstByteWriter bw; - - gst_byte_writer_init (&bw); - - /* See http://wiki.xiph.org/OggOpus */ - gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); - gst_byte_writer_put_uint8 (&bw, 0); /* version number */ - gst_byte_writer_put_uint8 (&bw, enc->n_channels); - gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ - gst_byte_writer_put_uint32_le (&bw, enc->sample_rate); - gst_byte_writer_put_uint16_le (&bw, 0); /* output gain *//* TODO: endianness ? */ - gst_byte_writer_put_uint8 (&bw, 0); /* channel mapping *//* TODO: what is this ? */ - - buffer = gst_byte_writer_reset_and_get_buffer (&bw); - - GST_BUFFER_OFFSET (buffer) = 0; - GST_BUFFER_OFFSET_END (buffer) = 0; - - return buffer; -} - -static GstBuffer * -gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc) -{ - const GstTagList *tags; - GstTagList *empty_tags = NULL; - GstBuffer *comments = NULL; - - tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); - - GST_DEBUG_OBJECT (enc, "tags = %" GST_PTR_FORMAT, tags); - - if (tags == NULL) { - /* FIXME: better fix chain of callers to not write metadata at all, - * if there is none */ - empty_tags = gst_tag_list_new (); - tags = empty_tags; - } - comments = - gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", - 8, "Encoded with GStreamer Opusenc"); - - GST_BUFFER_OFFSET (comments) = 0; - GST_BUFFER_OFFSET_END (comments) = 0; - - if (empty_tags) - gst_tag_list_free (empty_tags); - - return comments; -} - static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { @@ -644,63 +589,6 @@ done: return ret; } -/* - * (really really) FIXME: move into core (dixit tpm) - */ -/** - * _gst_caps_set_buffer_array: - * @caps: a #GstCaps - * @field: field in caps to set - * @buf: header buffers - * - * Adds given buffers to an array of buffers set as the given @field - * on the given @caps. List of buffer arguments must be NULL-terminated. - * - * Returns: input caps with a streamheader field added, or NULL if some error - */ -static GstCaps * -_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, - GstBuffer * buf, ...) -{ - GstStructure *structure = NULL; - va_list va; - GValue array = { 0 }; - GValue value = { 0 }; - - g_return_val_if_fail (caps != NULL, NULL); - g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); - g_return_val_if_fail (field != NULL, NULL); - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - g_value_init (&array, GST_TYPE_ARRAY); - - va_start (va, buf); - /* put buffers in a fixed list */ - while (buf) { - g_assert (gst_buffer_is_writable (buf)); - - /* mark buffer */ - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - - buf = va_arg (va, GstBuffer *); - } - - gst_structure_set_value (structure, field, &array); - g_value_unset (&array); - - return caps; -} - static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) { @@ -711,32 +599,20 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) GST_DEBUG_OBJECT (enc, "handle_frame"); if (!enc->header_sent) { - /* Opus streams in Ogg begin with two 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. */ - GstBuffer *buf1, *buf2; GstCaps *caps; - /* create header buffers */ - buf1 = gst_opus_enc_create_id_buffer (enc); - buf2 = gst_opus_enc_create_metadata_buffer (enc); + g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + enc->headers = NULL; + + gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, + enc->sample_rate, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); - /* mark and put on caps */ - caps = gst_caps_from_string ("audio/x-opus"); - caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL); /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps); - /* push out buffers */ - /* store buffers for later pre_push sending */ - g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); - enc->headers = NULL; - GST_DEBUG_OBJECT (enc, "storing header buffers"); - enc->headers = g_slist_prepend (enc->headers, buf2); - enc->headers = g_slist_prepend (enc->headers, buf1); enc->header_sent = TRUE; } diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c new file mode 100644 index 0000000000..b430b7df47 --- /dev/null +++ b/ext/opus/gstopusheader.c @@ -0,0 +1,163 @@ +/* GStreamer Opus Encoder + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2008> Sebastian Dröge + * Copyright (C) <2011> Vincent Penquerc'h + * + * 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 +#include +#include "gstopusheader.h" + +static GstBuffer * +gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate) +{ + GstBuffer *buffer; + GstByteWriter bw; + + gst_byte_writer_init (&bw); + + /* See http://wiki.xiph.org/OggOpus */ + gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); + gst_byte_writer_put_uint8 (&bw, 0); /* version number */ + gst_byte_writer_put_uint8 (&bw, nchannels); + gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ + gst_byte_writer_put_uint32_le (&bw, sample_rate); + gst_byte_writer_put_uint16_le (&bw, 0); /* output gain *//* TODO: endianness ? */ + gst_byte_writer_put_uint8 (&bw, 0); /* channel mapping *//* TODO: what is this ? */ + + buffer = gst_byte_writer_reset_and_get_buffer (&bw); + + GST_BUFFER_OFFSET (buffer) = 0; + GST_BUFFER_OFFSET_END (buffer) = 0; + + return buffer; +} + +static GstBuffer * +gst_opus_enc_create_metadata_buffer (const GstTagList * tags) +{ + GstTagList *empty_tags = NULL; + GstBuffer *comments = NULL; + + GST_DEBUG ("tags = %" GST_PTR_FORMAT, tags); + + if (tags == NULL) { + /* FIXME: better fix chain of callers to not write metadata at all, + * if there is none */ + empty_tags = gst_tag_list_new (); + tags = empty_tags; + } + comments = + gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", + 8, "Encoded with GStreamer Opusenc"); + + GST_BUFFER_OFFSET (comments) = 0; + GST_BUFFER_OFFSET_END (comments) = 0; + + if (empty_tags) + gst_tag_list_free (empty_tags); + + return comments; +} + +/* + * (really really) FIXME: move into core (dixit tpm) + */ +/** + * _gst_caps_set_buffer_array: + * @caps: a #GstCaps + * @field: field in caps to set + * @buf: header buffers + * + * Adds given buffers to an array of buffers set as the given @field + * on the given @caps. List of buffer arguments must be NULL-terminated. + * + * Returns: input caps with a streamheader field added, or NULL if some error + */ +static GstCaps * +_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, + GstBuffer * buf, ...) +{ + GstStructure *structure = NULL; + va_list va; + GValue array = { 0 }; + GValue value = { 0 }; + + g_return_val_if_fail (caps != NULL, NULL); + g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); + g_return_val_if_fail (field != NULL, NULL); + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&array, GST_TYPE_ARRAY); + + va_start (va, buf); + /* put buffers in a fixed list */ + while (buf) { + g_assert (gst_buffer_is_writable (buf)); + + /* mark buffer */ + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + + g_value_init (&value, GST_TYPE_BUFFER); + buf = gst_buffer_copy (buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + gst_value_set_buffer (&value, buf); + gst_buffer_unref (buf); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + + buf = va_arg (va, GstBuffer *); + } + + gst_structure_set_value (structure, field, &array); + g_value_unset (&array); + + return caps; +} + +void +gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, + gint sample_rate, const GstTagList * tags) +{ + GstBuffer *buf1, *buf2; + + g_return_if_fail (caps); + g_return_if_fail (headers && !*headers); + g_return_if_fail (nchannels > 0); + g_return_if_fail (sample_rate >= 0); /* 0 -> unset */ + + /* Opus streams in Ogg begin with two 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. */ + + /* create header buffers */ + buf1 = gst_opus_enc_create_id_buffer (nchannels, sample_rate); + buf2 = gst_opus_enc_create_metadata_buffer (tags); + + /* mark and put on caps */ + *caps = gst_caps_from_string ("audio/x-opus"); + *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); + + *headers = g_slist_prepend (*headers, buf2); + *headers = g_slist_prepend (*headers, buf1); +} diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h new file mode 100644 index 0000000000..4679083661 --- /dev/null +++ b/ext/opus/gstopusheader.h @@ -0,0 +1,32 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) <2008> Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_OPUS_HEADER_H__ +#define __GST_OPUS_HEADER_H__ + +#include + +G_BEGIN_DECLS + +extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, gint nchannels, gint sample_rate, const GstTagList *tags); + +G_END_DECLS + +#endif /* __GST_OPUS_HEADER_H__ */ From 95ae14f8b453e6f9fab2954629565094e65c17fb Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sun, 20 Nov 2011 09:58:06 +0000 Subject: [PATCH 050/197] opusenc: do not push header buffers Opus headers appear only when muxed in Ogg, so only place them on the caps, where oggmux will find them, but other elements will be blithely unaware of them. --- ext/opus/gstopusenc.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f13490ee74..c343968c5b 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -165,8 +165,6 @@ static gboolean gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info); static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf); -static GstFlowReturn gst_opus_enc_pre_push (GstAudioEncoder * benc, - GstBuffer ** buffer); static gint64 gst_opus_enc_get_latency (GstOpusEnc * enc); static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buffer); @@ -225,7 +223,6 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) base_class->stop = GST_DEBUG_FUNCPTR (gst_opus_enc_stop); base_class->set_format = GST_DEBUG_FUNCPTR (gst_opus_enc_set_format); base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_enc_handle_frame); - base_class->pre_push = GST_DEBUG_FUNCPTR (gst_opus_enc_pre_push); base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); g_object_class_install_property (gobject_class, PROP_AUDIO, @@ -477,36 +474,6 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) return FALSE; } -static GstFlowReturn -gst_opus_enc_pre_push (GstAudioEncoder * benc, GstBuffer ** buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstOpusEnc *enc; - - enc = GST_OPUS_ENC (benc); - - /* FIXME 0.11 ? get rid of this special ogg stuff and have it - * put and use 'codec data' in caps like anything else, - * with all the usual out-of-band advantage etc */ - if (G_UNLIKELY (enc->headers)) { - GSList *header = enc->headers; - - /* try to push all of these, if we lose one, might as well lose all */ - while (header) { - if (ret == GST_FLOW_OK) - ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), header->data); - else - gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), header->data); - header = g_slist_next (header); - } - - g_slist_free (enc->headers); - enc->headers = NULL; - } - - return ret; -} - static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { From de87d061fcde9d59a3acc0f00c73e41a124a63e2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 11:28:10 +0000 Subject: [PATCH 051/197] opusdec: light cleanup --- ext/opus/gstopusdec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 624ac81e42..c924e80171 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -248,8 +248,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) opus_packet_get_samples_per_frame (data, dec->sample_rate) * opus_packet_get_nb_frames (data, size); packet_size = samples * dec->n_channels * 2; - GST_DEBUG ("bandwidth %d", opus_packet_get_bandwidth (data)); - GST_DEBUG ("samples %d", samples); + GST_DEBUG_OBJECT (dec, "bandwidth %d", opus_packet_get_bandwidth (data)); + GST_DEBUG_OBJECT (dec, "samples %d", samples); res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), GST_BUFFER_OFFSET_NONE, packet_size, @@ -377,7 +377,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) } } else { /* Otherwise fall back to packet counting and assume that the - * first two packets are the headers. */ + * first two packets might be the headers, checking magic. */ switch (dec->packetno) { case 0: if (gst_opus_dec_is_header (buf, "OpusHead", 8)) { From a6ca3673b4ff427890534083e75225f5b00a3c7e Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 11:44:01 +0000 Subject: [PATCH 052/197] opusdec: handle NULL packets (used for PLC) --- ext/opus/gstopusdec.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index c924e80171..54d25d56cd 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -244,12 +244,19 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) size = 0; } - samples = - opus_packet_get_samples_per_frame (data, - dec->sample_rate) * opus_packet_get_nb_frames (data, size); + if (data) { + samples = + opus_packet_get_samples_per_frame (data, + dec->sample_rate) * opus_packet_get_nb_frames (data, size); + packet_size = samples * dec->n_channels * 2; + GST_DEBUG_OBJECT (dec, "bandwidth %d", opus_packet_get_bandwidth (data)); + GST_DEBUG_OBJECT (dec, "samples %d", samples); + } else { + /* use maximum size (120 ms) as we do now know in advance how many samples + will be returned */ + samples = 120 * dec->sample_rate / 1000; + } packet_size = samples * dec->n_channels * 2; - GST_DEBUG_OBJECT (dec, "bandwidth %d", opus_packet_get_bandwidth (data)); - GST_DEBUG_OBJECT (dec, "samples %d", samples); res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), GST_BUFFER_OFFSET_NONE, packet_size, @@ -262,14 +269,13 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) out_data = (gint16 *) GST_BUFFER_DATA (outbuf); - GST_LOG_OBJECT (dec, "decoding %d samples, in size %u", samples, size); - n = opus_decode (dec->state, data, size, out_data, samples, 0); if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (dec, "decoded %d samples", n); + GST_BUFFER_SIZE (outbuf) = n * 2 * dec->n_channels; res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); From fbfc870343e9993c9f1b256edcbbf77268149fde Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 12:02:28 +0000 Subject: [PATCH 053/197] opusenc: reset tagsetter interface on stop --- ext/opus/gstopusenc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index c343968c5b..276e1ddaa1 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -333,6 +333,7 @@ gst_opus_enc_stop (GstAudioEncoder * benc) enc->tags = NULL; g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); enc->headers = NULL; + gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; } From 1e81bc4b64a9d383e5396869d14da6c2dff31f4d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 12:50:22 +0000 Subject: [PATCH 054/197] opusdec: read pre-skip from first header if available --- ext/opus/gstopusdec.c | 12 ++++++++++++ ext/opus/gstopusdec.h | 1 + 2 files changed, 13 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 54d25d56cd..f0a9a5144e 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -77,6 +77,8 @@ static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer); static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps); +static gboolean gst_opus_dec_is_header (GstBuffer * buf, const char *magic, + guint magic_size); static void gst_opus_dec_base_init (gpointer g_class) @@ -122,6 +124,8 @@ gst_opus_dec_reset (GstOpusDec * dec) gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); + + dec->pre_skip = 0; } static void @@ -159,9 +163,17 @@ gst_opus_dec_stop (GstAudioDecoder * dec) static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { + g_return_val_if_fail (gst_opus_dec_is_header (buf, "OpusHead", 8), + GST_FLOW_ERROR); + g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 19, GST_FLOW_ERROR); + + dec->pre_skip = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 10); + GST_DEBUG_OBJECT (dec, "Found pre-skip of %u samples", dec->pre_skip); + return GST_FLOW_OK; } + static GstFlowReturn gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) { diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 9e78330e1f..eee27dc554 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -53,6 +53,7 @@ struct _GstOpusDec { int sample_rate; int n_channels; + guint32 pre_skip; }; struct _GstOpusDecClass { From 354c7824aab009896f5894f787ee89bd903afcdf Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 17:01:49 +0000 Subject: [PATCH 055/197] opusdec: skip pre-skip samples --- ext/opus/gstopusdec.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index f0a9a5144e..8bb3a047c9 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -168,7 +168,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 19, GST_FLOW_ERROR); dec->pre_skip = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 10); - GST_DEBUG_OBJECT (dec, "Found pre-skip of %u samples", dec->pre_skip); + GST_INFO_OBJECT (dec, "Found pre-skip of %u samples", dec->pre_skip); return GST_FLOW_OK; } @@ -289,6 +289,24 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) GST_DEBUG_OBJECT (dec, "decoded %d samples", n); GST_BUFFER_SIZE (outbuf) = n * 2 * dec->n_channels; + /* Skip any samples that need skipping */ + if (dec->pre_skip > 0) { + guint scaled_pre_skip = dec->pre_skip * dec->sample_rate / 48000; + guint skip = scaled_pre_skip > n ? n : scaled_pre_skip; + guint scaled_skip = skip * 48000 / dec->sample_rate; + GST_BUFFER_SIZE (outbuf) -= skip * 2 * dec->n_channels; + GST_BUFFER_DATA (outbuf) += skip * 2 * dec->n_channels; + dec->pre_skip -= scaled_skip; + GST_INFO_OBJECT (dec, + "Skipping %u samples (%u at 48000 Hz, %u left to skip)", skip, + scaled_skip, dec->pre_skip); + + if (GST_BUFFER_SIZE (outbuf) == 0) { + gst_buffer_unref (outbuf); + outbuf = NULL; + } + } + res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) From 5be77031ca2f105df78bef18f4bd29abb588bdfe Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 17:48:54 +0000 Subject: [PATCH 056/197] opus: move header magic testing to gstopusheader --- ext/opus/gstopusdec.c | 18 +++++------------- ext/opus/gstopusheader.c | 7 +++++++ ext/opus/gstopusheader.h | 2 ++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 8bb3a047c9..89eec6941e 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -41,9 +41,10 @@ # include "config.h" #endif -#include "gstopusdec.h" #include #include +#include "gstopusheader.h" +#include "gstopusdec.h" GST_DEBUG_CATEGORY_STATIC (opusdec_debug); #define GST_CAT_DEFAULT opusdec_debug @@ -77,8 +78,6 @@ static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer); static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps); -static gboolean gst_opus_dec_is_header (GstBuffer * buf, const char *magic, - guint magic_size); static void gst_opus_dec_base_init (gpointer g_class) @@ -163,7 +162,7 @@ gst_opus_dec_stop (GstAudioDecoder * dec) static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { - g_return_val_if_fail (gst_opus_dec_is_header (buf, "OpusHead", 8), + g_return_val_if_fail (gst_opus_header_is_header (buf, "OpusHead", 8), GST_FLOW_ERROR); g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 19, GST_FLOW_ERROR); @@ -374,13 +373,6 @@ memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) return !memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2), size1); } -static gboolean -gst_opus_dec_is_header (GstBuffer * buf, const char *magic, guint magic_size) -{ - return (GST_BUFFER_SIZE (buf) >= magic_size - && !memcmp (magic, GST_BUFFER_DATA (buf), magic_size)); -} - static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) { @@ -416,7 +408,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) * first two packets might be the headers, checking magic. */ switch (dec->packetno) { case 0: - if (gst_opus_dec_is_header (buf, "OpusHead", 8)) { + if (gst_opus_header_is_header (buf, "OpusHead", 8)) { GST_DEBUG_OBJECT (dec, "found streamheader"); res = gst_opus_dec_parse_header (dec, buf); gst_audio_decoder_finish_frame (adec, NULL, 1); @@ -425,7 +417,7 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) } break; case 1: - if (gst_opus_dec_is_header (buf, "OpusTags", 8)) { + if (gst_opus_header_is_header (buf, "OpusTags", 8)) { GST_DEBUG_OBJECT (dec, "counted vorbiscomments"); res = gst_opus_dec_parse_comments (dec, buf); gst_audio_decoder_finish_frame (adec, NULL, 1); diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index b430b7df47..3551055840 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -161,3 +161,10 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, *headers = g_slist_prepend (*headers, buf2); *headers = g_slist_prepend (*headers, buf1); } + +gboolean +gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size) +{ + return (GST_BUFFER_SIZE (buf) >= magic_size + && !memcmp (magic, GST_BUFFER_DATA (buf), magic_size)); +} diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 4679083661..4594264ccd 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -26,6 +26,8 @@ G_BEGIN_DECLS extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, gint nchannels, gint sample_rate, const GstTagList *tags); +extern gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size); + G_END_DECLS From bb39c86fdfff4674a023de3ae193db68141a80e4 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 15:33:20 +0000 Subject: [PATCH 057/197] opusenc: fix crash on pathological parameters Asking for 1 bit/s would select a 0 byte buffer, leading to a crash. Buffer size is now controlled by a max-payload-size property, which can't be less than 2. --- ext/opus/gstopusenc.c | 32 ++++++++++++++++++++++---------- ext/opus/gstopusenc.h | 1 + 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 276e1ddaa1..6a7d549bd6 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -132,6 +132,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", #define DEFAULT_INBAND_FEC FALSE #define DEFAULT_DTX FALSE #define DEFAULT_PACKET_LOSS_PERCENT 0 +#define DEFAULT_MAX_PAYLOAD_SIZE 1024 enum { @@ -145,7 +146,8 @@ enum PROP_COMPLEXITY, PROP_INBAND_FEC, PROP_DTX, - PROP_PACKET_LOSS_PERCENT + PROP_PACKET_LOSS_PERCENT, + PROP_MAX_PAYLOAD_SIZE }; static void gst_opus_enc_finalize (GObject * object); @@ -267,6 +269,11 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) "Loss percentage", "Packet loss percentage", 0, 100, DEFAULT_PACKET_LOSS_PERCENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_MAX_PAYLOAD_SIZE, g_param_spec_uint ("max-payload-size", + "Max payload size", "Maximum payload size in bytes", 2, 1275, + DEFAULT_MAX_PAYLOAD_SIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize); } @@ -301,6 +308,7 @@ gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) enc->inband_fec = DEFAULT_INBAND_FEC; enc->dtx = DEFAULT_DTX; enc->packet_loss_percentage = DEFAULT_PACKET_LOSS_PERCENT; + enc->max_payload_size = DEFAULT_MAX_PAYLOAD_SIZE; /* arrange granulepos marking (and required perfect ts) */ gst_audio_encoder_set_mark_granule (benc, TRUE); @@ -481,8 +489,6 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) guint8 *bdata, *data, *mdata = NULL; gsize bsize, size; gsize bytes = enc->frame_samples * enc->n_channels * 2; - gsize bytes_per_packet = - (enc->bitrate * enc->frame_samples / enc->sample_rate + 4) / 8; gint ret = GST_FLOW_OK; if (G_LIKELY (buf)) { @@ -511,27 +517,27 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GstBuffer *outbuf; ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), - GST_BUFFER_OFFSET_NONE, bytes_per_packet, + GST_BUFFER_OFFSET_NONE, enc->max_payload_size, GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf); if (GST_FLOW_OK != ret) goto done; - GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes) to %d bytes", - enc->frame_samples, bytes, bytes_per_packet); + GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", + enc->frame_samples); outsize = opus_encode (enc->state, (const gint16 *) data, enc->frame_samples, - GST_BUFFER_DATA (outbuf), bytes_per_packet); + GST_BUFFER_DATA (outbuf), enc->max_payload_size); if (outsize < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; goto done; - } else if (outsize > bytes_per_packet) { + } else if (outsize > enc->max_payload_size) { GST_WARNING_OBJECT (enc, - "Encoded size %d is different from %d bytes per packet", outsize, - bytes_per_packet); + "Encoded size %d is higher than max payload size (%d bytes)", + outsize, enc->max_payload_size); ret = GST_FLOW_ERROR; goto done; } @@ -631,6 +637,9 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, case PROP_PACKET_LOSS_PERCENT: g_value_set_int (value, enc->packet_loss_percentage); break; + case PROP_MAX_PAYLOAD_SIZE: + g_value_set_uint (value, enc->max_payload_size); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -676,6 +685,9 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, case PROP_PACKET_LOSS_PERCENT: enc->packet_loss_percentage = g_value_get_int (value); break; + case PROP_MAX_PAYLOAD_SIZE: + enc->max_payload_size = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index fe7b94e68d..d645ce8d07 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -63,6 +63,7 @@ struct _GstOpusEnc { gboolean inband_fec; gboolean dtx; gint packet_loss_percentage; + guint max_payload_size; gint frame_samples; gint n_channels; From ee723996dbac3f5f310cbfe12dad9f09951ea67b Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 16:14:06 +0000 Subject: [PATCH 058/197] opusenc: bound the bitrate to more sensible values Go from the bounds mentioned in the spec, and allow some more variation. In particular, don't allow silly low bitrates, and allow reaching the maximum useful bitrate. --- ext/opus/gstopusenc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 6a7d549bd6..df11c5ff77 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -54,6 +54,12 @@ GST_DEBUG_CATEGORY_STATIC (opusenc_debug); #define GST_CAT_DEFAULT opusenc_debug +/* Some arbitrary bounds beyond which it really doesn't make sense. + The spec mentions 6 kb/s to 510 kb/s, so 4000 and 650000 ought to be + safe as property bounds. */ +#define LOWEST_BITRATE 4000 +#define HIGHEST_BITRATE 650000 + #define GST_OPUS_ENC_TYPE_BANDWIDTH (gst_opus_enc_bandwidth_get_type()) static GType gst_opus_enc_bandwidth_get_type (void) @@ -234,7 +240,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE, g_param_spec_int ("bitrate", "Encoding Bit-rate", "Specify an encoding bit-rate (in bps).", - 1, 320000, DEFAULT_BITRATE, + LOWEST_BITRATE, HIGHEST_BITRATE, DEFAULT_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_BANDWIDTH, g_param_spec_enum ("bandwidth", "Band Width", From b226e8a0857dd7a947565a375a0610ef2a6838c7 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 17:04:09 +0000 Subject: [PATCH 059/197] opusenc: allow setting most properties at PLAYING time Opus allows these to be changed during encoding, transparently to the decoder. --- ext/opus/gstopusenc.c | 76 ++++++++++++++++++++++++++++++++++--------- ext/opus/gstopusenc.h | 3 ++ 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index df11c5ff77..cdcb298629 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -291,6 +291,8 @@ gst_opus_enc_finalize (GObject * object) enc = GST_OPUS_ENC (object); + g_mutex_free (enc->property_lock); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -301,6 +303,8 @@ gst_opus_enc_init (GstOpusEnc * enc, GstOpusEncClass * klass) GST_DEBUG_OBJECT (enc, "init"); + enc->property_lock = g_mutex_new (); + enc->n_channels = -1; enc->sample_rate = -1; enc->frame_samples = 0; @@ -329,6 +333,7 @@ gst_opus_enc_start (GstAudioEncoder * benc) GST_DEBUG_OBJECT (enc, "start"); enc->tags = gst_tag_list_new (); enc->header_sent = FALSE; + return TRUE; } @@ -361,6 +366,18 @@ gst_opus_enc_get_latency (GstOpusEnc * enc) return latency; } +static void +gst_opus_enc_setup_base_class (GstOpusEnc * enc, GstAudioEncoder * benc) +{ + gst_audio_encoder_set_latency (benc, + gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc)); + gst_audio_encoder_set_frame_samples_min (benc, + enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_samples_max (benc, + enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_max (benc, 0); +} + static gint gst_opus_enc_get_frame_samples (GstOpusEnc * enc) { @@ -399,6 +416,8 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc = GST_OPUS_ENC (benc); + g_mutex_lock (enc->property_lock); + enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); enc->sample_rate = GST_AUDIO_INFO_RATE (info); GST_DEBUG_OBJECT (benc, "Setup with %d channels, %d Hz", enc->n_channels, @@ -415,13 +434,9 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc->frame_samples = gst_opus_enc_get_frame_samples (enc); /* feedback to base class */ - gst_audio_encoder_set_latency (benc, - gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc)); - gst_audio_encoder_set_frame_samples_min (benc, - enc->frame_samples * enc->n_channels * 2); - gst_audio_encoder_set_frame_samples_max (benc, - enc->frame_samples * enc->n_channels * 2); - gst_audio_encoder_set_frame_max (benc, 0); + gst_opus_enc_setup_base_class (enc, benc); + + g_mutex_unlock (enc->property_lock); return TRUE; } @@ -494,9 +509,12 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { guint8 *bdata, *data, *mdata = NULL; gsize bsize, size; - gsize bytes = enc->frame_samples * enc->n_channels * 2; + gsize bytes; gint ret = GST_FLOW_OK; + g_mutex_lock (enc->property_lock); + + bytes = enc->frame_samples * enc->n_channels * 2; if (G_LIKELY (buf)) { bdata = GST_BUFFER_DATA (buf); bsize = GST_BUFFER_SIZE (buf); @@ -563,6 +581,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) done: + g_mutex_unlock (enc->property_lock); + if (mdata) g_free (mdata); @@ -612,6 +632,8 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, enc = GST_OPUS_ENC (object); + g_mutex_lock (enc->property_lock); + switch (prop_id) { case PROP_AUDIO: g_value_set_boolean (value, enc->audio_or_voip); @@ -650,6 +672,8 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + + g_mutex_unlock (enc->property_lock); } static void @@ -660,42 +684,64 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, enc = GST_OPUS_ENC (object); +#define GST_OPUS_UPDATE_PROPERTY(prop,type,ctl) do { \ + g_mutex_lock (enc->property_lock); \ + enc->prop = g_value_get_##type (value); \ + if (enc->state) { \ + opus_encoder_ctl (enc->state, OPUS_SET_##ctl (enc->prop)); \ + } \ + g_mutex_unlock (enc->property_lock); \ +} while(0) + switch (prop_id) { case PROP_AUDIO: enc->audio_or_voip = g_value_get_boolean (value); break; case PROP_BITRATE: - enc->bitrate = g_value_get_int (value); + GST_OPUS_UPDATE_PROPERTY (bitrate, int, BITRATE); break; case PROP_BANDWIDTH: - enc->bandwidth = g_value_get_enum (value); + GST_OPUS_UPDATE_PROPERTY (bandwidth, enum, BANDWIDTH); break; case PROP_FRAME_SIZE: + g_mutex_lock (enc->property_lock); enc->frame_size = g_value_get_enum (value); + enc->frame_samples = gst_opus_enc_get_frame_samples (enc); + gst_opus_enc_setup_base_class (enc, GST_AUDIO_ENCODER (enc)); + g_mutex_unlock (enc->property_lock); break; case PROP_CBR: + /* this one has an opposite meaning to the opus ctl... */ + g_mutex_lock (enc->property_lock); enc->cbr = g_value_get_boolean (value); + opus_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); + g_mutex_unlock (enc->property_lock); break; case PROP_CONSTRAINED_VBR: - enc->constrained_vbr = g_value_get_boolean (value); + GST_OPUS_UPDATE_PROPERTY (constrained_vbr, boolean, VBR_CONSTRAINT); break; case PROP_COMPLEXITY: - enc->complexity = g_value_get_int (value); + GST_OPUS_UPDATE_PROPERTY (complexity, int, COMPLEXITY); break; case PROP_INBAND_FEC: - enc->inband_fec = g_value_get_boolean (value); + GST_OPUS_UPDATE_PROPERTY (inband_fec, boolean, INBAND_FEC); break; case PROP_DTX: - enc->dtx = g_value_get_boolean (value); + GST_OPUS_UPDATE_PROPERTY (dtx, boolean, DTX); break; case PROP_PACKET_LOSS_PERCENT: - enc->packet_loss_percentage = g_value_get_int (value); + GST_OPUS_UPDATE_PROPERTY (packet_loss_percentage, int, PACKET_LOSS_PERC); break; case PROP_MAX_PAYLOAD_SIZE: + g_mutex_lock (enc->property_lock); enc->max_payload_size = g_value_get_uint (value); + g_mutex_unlock (enc->property_lock); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + +#undef GST_OPUS_UPDATE_PROPERTY + } diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index d645ce8d07..772f6f4cc7 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -52,6 +52,9 @@ struct _GstOpusEnc { OpusEncoder *state; + /* Locks those properties which may be changed at play time */ + GMutex *property_lock; + /* properties */ gboolean audio_or_voip; gint bitrate; From e7228fc0b344edf5c412ea2cf7f8f806abe0a215 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 18:33:17 +0000 Subject: [PATCH 060/197] opus: add test --- tests/check/elements/opus.c | 383 ++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 tests/check/elements/opus.c diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c new file mode 100644 index 0000000000..66c12a3237 --- /dev/null +++ b/tests/check/elements/opus.c @@ -0,0 +1,383 @@ +/* GStreamer + * + * unit test for opus + * + * Copyright (C) <2011> Vincent Penquerc'h + * + * 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. + */ + +#include + +#include + +static const guint8 opus_ogg_id_header[19] = { + 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const guint8 opus_ogg_comments_header[] = { + 0x4f, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73, 0x1e, 0x00, 0x00, 0x00, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x72, 0x20, 0x4f, 0x70, 0x75, 0x73, + 0x65, 0x6e, 0x63, 0x00, 0x00, 0x00, 0x00 +}; + +/* A lot of these taken from the vorbisdec test */ + +/* For ease of programming we use globals to keep refs for our floating + * src and sink pads we create; otherwise we always have to do get_pad, + * get_peer, and then remove references in every test function */ +static GstPad *mydecsrcpad, *mydecsinkpad; +static GstPad *myencsrcpad, *myencsinkpad; + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstElement * +setup_opusdec (void) +{ + GstElement *opusdec; + + GST_DEBUG ("setup_opusdec"); + opusdec = gst_check_setup_element ("opusdec"); + mydecsrcpad = gst_check_setup_src_pad (opusdec, &srctemplate, NULL); + mydecsinkpad = gst_check_setup_sink_pad (opusdec, &sinktemplate, NULL); + gst_pad_set_active (mydecsrcpad, TRUE); + gst_pad_set_active (mydecsinkpad, TRUE); + + return opusdec; +} + +static void +cleanup_opusdec (GstElement * opusdec) +{ + GST_DEBUG ("cleanup_opusdec"); + gst_element_set_state (opusdec, GST_STATE_NULL); + + gst_pad_set_active (mydecsrcpad, FALSE); + gst_pad_set_active (mydecsinkpad, FALSE); + gst_check_teardown_src_pad (opusdec); + gst_check_teardown_sink_pad (opusdec); + gst_check_teardown_element (opusdec); +} + +static GstElement * +setup_opusenc (void) +{ + GstElement *opusenc; + + GST_DEBUG ("setup_opusenc"); + opusenc = gst_check_setup_element ("opusenc"); + myencsrcpad = gst_check_setup_src_pad (opusenc, &srctemplate, NULL); + myencsinkpad = gst_check_setup_sink_pad (opusenc, &sinktemplate, NULL); + gst_pad_set_active (myencsrcpad, TRUE); + gst_pad_set_active (myencsinkpad, TRUE); + + return opusenc; +} + +static void +cleanup_opusenc (GstElement * opusenc) +{ + GST_DEBUG ("cleanup_opusenc"); + gst_element_set_state (opusenc, GST_STATE_NULL); + + gst_pad_set_active (myencsrcpad, FALSE); + gst_pad_set_active (myencsinkpad, FALSE); + gst_check_teardown_src_pad (opusenc); + gst_check_teardown_sink_pad (opusenc); + gst_check_teardown_element (opusenc); +} + +static void +check_buffers (guint expected, gboolean headers_in_caps) +{ + GstBuffer *outbuffer; + guint i, num_buffers; + + /* check buffers are the type we expect */ + num_buffers = g_list_length (buffers); + fail_unless (num_buffers >= expected); + for (i = 0; i < num_buffers; ++i) { + outbuffer = GST_BUFFER (buffers->data); + fail_if (outbuffer == NULL); + fail_if (GST_BUFFER_SIZE (outbuffer) == 0); + + buffers = g_list_remove (buffers, outbuffer); + + ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1); + gst_buffer_unref (outbuffer); + outbuffer = NULL; + } +} + +GST_START_TEST (test_opus_id_header) +{ + GstElement *opusdec; + GstBuffer *inbuffer; + + opusdec = setup_opusdec (); + fail_unless (gst_element_set_state (opusdec, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (sizeof (opus_ogg_id_header)); + memcpy (GST_BUFFER_DATA (inbuffer), opus_ogg_id_header, + sizeof (opus_ogg_id_header)); + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + gst_buffer_ref (inbuffer); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mydecsrcpad, inbuffer) == GST_FLOW_OK); + /* ... and nothing ends up on the global buffer list */ + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + gst_buffer_unref (inbuffer); + fail_unless (g_list_length (buffers) == 0); + + /* cleanup */ + cleanup_opusdec (opusdec); +} + +GST_END_TEST; + +GST_START_TEST (test_opus_encode_nothing) +{ + GstElement *opusenc; + + opusenc = setup_opusenc (); + fail_unless (gst_element_set_state (opusenc, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE); + + fail_unless (gst_element_set_state (opusenc, + GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS, + "could not set to ready"); + + /* cleanup */ + cleanup_opusenc (opusenc); +} + +GST_END_TEST; + +GST_START_TEST (test_opus_decode_nothing) +{ + GstElement *opusdec; + + opusdec = setup_opusdec (); + fail_unless (gst_element_set_state (opusdec, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + fail_unless (gst_pad_push_event (mydecsrcpad, gst_event_new_eos ()) == TRUE); + + fail_unless (gst_element_set_state (opusdec, + GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS, + "could not set to ready"); + + /* cleanup */ + cleanup_opusdec (opusdec); +} + +GST_END_TEST; + +GST_START_TEST (test_opus_encode_samples) +{ + const unsigned int nsamples = 4096; + GstElement *opusenc; + GstBuffer *inbuffer; + GstCaps *caps; + guint16 *samples; + unsigned int n; + + opusenc = setup_opusenc (); + + fail_unless (gst_element_set_state (opusenc, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (nsamples * 2); + samples = (guint16 *) GST_BUFFER_DATA (inbuffer); + for (n = 0; n < nsamples; ++n) { + samples[n] = 0; + } + + GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) = 0; + GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE; + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + + caps = + gst_caps_from_string + ("audio/x-raw-int,rate=48000,channels=1,signed=true,width=16,depth=16,endianness=1234"); + fail_unless (caps != NULL); + gst_buffer_set_caps (inbuffer, caps); + gst_caps_unref (caps); + gst_buffer_ref (inbuffer); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (myencsrcpad, inbuffer) == GST_FLOW_OK); + /* ... and nothing ends up on the global buffer list */ + fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE); + + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + gst_buffer_unref (inbuffer); + + fail_unless (gst_element_set_state (opusenc, + GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS, + "could not set to ready"); + + /* default frame size is 20 ms, at 48000 Hz that's 960 samples */ + check_buffers ((nsamples + 959) / 960, FALSE); + + /* cleanup */ + cleanup_opusenc (opusenc); + g_list_free (buffers); +} + +GST_END_TEST; + +GST_START_TEST (test_opus_encode_properties) +{ + const unsigned int nsamples = 4096; + enum + { steps = 20 }; + GstElement *opusenc; + GstBuffer *inbuffer; + GstCaps *caps; + guint16 *samples; + unsigned int n, step; + static const struct + { + const char *param; + int value; + } param_changes[steps] = { + { + "frame-size", 40}, { + "inband-fec", 1}, { + "complexity", 5}, { + "bandwidth", 1104}, { + "frame-size", 2}, { + "max-payload-size", 80}, { + "frame-size", 60}, { + "max-payload-size", 900}, { + "complexity", 1}, { + "bitrate", 30000}, { + "frame-size", 10}, { + "bitrate", 300000}, { + "inband-fec", 0}, { + "frame-size", 5}, { + "bandwidth", 1101}, { + "frame-size", 10}, { + "bitrate", 500000}, { + "frame-size", 5}, { + "bitrate", 80000}, { + "complexity", 8},}; + + opusenc = setup_opusenc (); + + fail_unless (gst_element_set_state (opusenc, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + caps = + gst_caps_from_string + ("audio/x-raw-int,rate=48000,channels=1,signed=true,width=16,depth=16,endianness=1234"); + fail_unless (caps != NULL); + + for (step = 0; step < steps; ++step) { + inbuffer = gst_buffer_new_and_alloc (nsamples * 2); + samples = (guint16 *) GST_BUFFER_DATA (inbuffer); + for (n = 0; n < nsamples; ++n) { + samples[n] = 0; + } + + GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) = 0; + GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE; + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + + gst_buffer_set_caps (inbuffer, caps); + gst_buffer_ref (inbuffer); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (myencsrcpad, inbuffer) == GST_FLOW_OK); + /* ... and nothing ends up on the global buffer list */ + fail_unless (gst_pad_push_event (myencsrcpad, + gst_event_new_eos ()) == TRUE); + + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + gst_buffer_unref (inbuffer); + + /* change random parameters */ + g_object_set (opusenc, param_changes[step].param, param_changes[step].value, + NULL); + } + + gst_caps_unref (caps); + + fail_unless (gst_element_set_state (opusenc, + GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS, + "could not set to ready"); + + /* cleanup */ + cleanup_opusenc (opusenc); + g_list_free (buffers); +} + +GST_END_TEST; + +static Suite * +opus_suite (void) +{ + Suite *s = suite_create ("opus"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + +#define X if (0) + tcase_add_test (tc_chain, test_opus_id_header); + tcase_add_test (tc_chain, test_opus_encode_nothing); + tcase_add_test (tc_chain, test_opus_decode_nothing); + tcase_add_test (tc_chain, test_opus_encode_samples); + tcase_add_test (tc_chain, test_opus_encode_properties); +#undef X + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = opus_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} From 2befc00dea12f103024bdd4ee8bc3fdc825f1e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 22 Nov 2011 20:27:50 +0000 Subject: [PATCH 061/197] opusenc: mark properties changeable at runtime with GST_PARAM_MUTABLE_PLAYING --- ext/opus/gstopusenc.c | 48 ++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index cdcb298629..29b9faf756 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -241,45 +241,55 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) g_param_spec_int ("bitrate", "Encoding Bit-rate", "Specify an encoding bit-rate (in bps).", LOWEST_BITRATE, HIGHEST_BITRATE, DEFAULT_BITRATE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_BANDWIDTH, - g_param_spec_enum ("bandwidth", "Band Width", - "Audio Band Width", GST_OPUS_ENC_TYPE_BANDWIDTH, DEFAULT_BANDWIDTH, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_enum ("bandwidth", "Band Width", "Audio Band Width", + GST_OPUS_ENC_TYPE_BANDWIDTH, DEFAULT_BANDWIDTH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_FRAME_SIZE, g_param_spec_enum ("frame-size", "Frame Size", - "The duration of an audio frame, in ms", - GST_OPUS_ENC_TYPE_FRAME_SIZE, DEFAULT_FRAMESIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "The duration of an audio frame, in ms", GST_OPUS_ENC_TYPE_FRAME_SIZE, + DEFAULT_FRAMESIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_CBR, - g_param_spec_boolean ("cbr", "Constant bit rate", - "Constant bit rate", DEFAULT_CBR, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("cbr", "Constant bit rate", "Constant bit rate", + DEFAULT_CBR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR, g_param_spec_boolean ("constrained-vbr", "Constrained VBR", "Constrained VBR", DEFAULT_CONSTRAINED_VBR, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_COMPLEXITY, - g_param_spec_int ("complexity", "Complexity", - "Complexity", 0, 10, DEFAULT_COMPLEXITY, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_int ("complexity", "Complexity", "Complexity", 0, 10, + DEFAULT_COMPLEXITY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_INBAND_FEC, g_param_spec_boolean ("inband-fec", "In-band FEC", "Enable forward error correction", DEFAULT_INBAND_FEC, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_DTX, - g_param_spec_boolean ("dtx", "DTX", - "DTX", DEFAULT_DTX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("dtx", "DTX", "DTX", DEFAULT_DTX, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PACKET_LOSS_PERCENT, g_param_spec_int ("packet-loss-percentage", "Loss percentage", "Packet loss percentage", 0, 100, DEFAULT_PACKET_LOSS_PERCENT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_PAYLOAD_SIZE, g_param_spec_uint ("max-payload-size", "Max payload size", "Maximum payload size in bytes", 2, 1275, DEFAULT_MAX_PAYLOAD_SIZE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opus_enc_finalize); } From bfdda8e09a11f565ff935f4f5c4e0267e72b7554 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 23 Nov 2011 11:58:54 +0000 Subject: [PATCH 062/197] opusdec: add in-band FEC support This allows reconstruction of lost packets if FEC info is included in the next packet, at the cost of extra latency. Since we do not know if the stream has FEC (and this can change at runtime), we always incur the latency, even if we never lose any frame, or see any FEC information. Off by default. --- ext/opus/gstopusdec.c | 123 ++++++++++++++++++++++++++++++++++++------ ext/opus/gstopusdec.h | 4 ++ 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 89eec6941e..7d70fea184 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -67,6 +67,14 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-opus") ); +#define DEFAULT_USE_INBAND_FEC FALSE + +enum +{ + PROP_0, + PROP_USE_INBAND_FEC +}; + GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstAudioDecoder, GST_TYPE_AUDIO_DECODER); @@ -78,6 +86,11 @@ static GstFlowReturn gst_opus_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer); static gboolean gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps); +static void gst_opus_dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_opus_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + static void gst_opus_dec_base_init (gpointer g_class) @@ -97,17 +110,27 @@ gst_opus_dec_base_init (gpointer g_class) static void gst_opus_dec_class_init (GstOpusDecClass * klass) { + GObjectClass *gobject_class; GstAudioDecoderClass *adclass; GstElementClass *gstelement_class; + gobject_class = (GObjectClass *) klass; adclass = (GstAudioDecoderClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->set_property = gst_opus_dec_set_property; + gobject_class->get_property = gst_opus_dec_get_property; + adclass->start = GST_DEBUG_FUNCPTR (gst_opus_dec_start); adclass->stop = GST_DEBUG_FUNCPTR (gst_opus_dec_stop); adclass->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_dec_handle_frame); adclass->set_format = GST_DEBUG_FUNCPTR (gst_opus_dec_set_format); + g_object_class_install_property (gobject_class, PROP_USE_INBAND_FEC, + g_param_spec_boolean ("use-inband-fec", "Use in-band FEC", + "Use forward error correction if available", DEFAULT_USE_INBAND_FEC, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, "opus decoding element"); } @@ -123,6 +146,8 @@ gst_opus_dec_reset (GstOpusDec * dec) gst_buffer_replace (&dec->streamheader, NULL); gst_buffer_replace (&dec->vorbiscomment, NULL); + gst_buffer_replace (&dec->last_buffer, NULL); + dec->primed = FALSE; dec->pre_skip = 0; } @@ -132,6 +157,7 @@ gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) { dec->sample_rate = 0; dec->n_channels = 0; + dec->use_inband_fec = FALSE; gst_opus_dec_reset (dec); } @@ -146,6 +172,11 @@ gst_opus_dec_start (GstAudioDecoder * dec) /* we know about concealment */ gst_audio_decoder_set_plc_aware (dec, TRUE); + if (odec->use_inband_fec) { + gst_audio_decoder_set_latency (dec, 2 * GST_MSECOND + GST_MSECOND / 2, + 120 * GST_MSECOND); + } + return TRUE; } @@ -222,7 +253,7 @@ gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec) } static GstFlowReturn -opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) +opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) { GstFlowReturn res = GST_FLOW_OK; gint size; @@ -232,6 +263,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) int n, err; int samples; unsigned int packet_size; + GstBuffer *buf; if (dec->state == NULL) { gst_opus_dec_setup_from_peer_caps (dec); @@ -243,30 +275,37 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) goto creation_failed; } + if (buffer) { + GST_DEBUG_OBJECT (dec, "Received buffer of size %u", + GST_BUFFER_SIZE (buffer)); + } else { + GST_DEBUG_OBJECT (dec, "Received missing buffer"); + } + + /* if using in-band FEC, we introdude one extra frame's delay as we need + to potentially wait for next buffer to decode a missing buffer */ + if (dec->use_inband_fec && !dec->primed) { + GST_DEBUG_OBJECT (dec, "First buffer received in FEC mode, early out"); + goto done; + } + + /* That's the buffer we'll be sending to the opus decoder. */ + buf = dec->use_inband_fec && dec->last_buffer ? dec->last_buffer : buffer; + if (buf) { data = GST_BUFFER_DATA (buf); size = GST_BUFFER_SIZE (buf); - - GST_DEBUG_OBJECT (dec, "received buffer of size %u", size); + GST_DEBUG_OBJECT (dec, "Using buffer of size %u", size); } else { /* concealment data, pass NULL as the bits parameters */ - GST_DEBUG_OBJECT (dec, "creating concealment data"); + GST_DEBUG_OBJECT (dec, "Using NULL buffer"); data = NULL; size = 0; } - if (data) { - samples = - opus_packet_get_samples_per_frame (data, - dec->sample_rate) * opus_packet_get_nb_frames (data, size); - packet_size = samples * dec->n_channels * 2; - GST_DEBUG_OBJECT (dec, "bandwidth %d", opus_packet_get_bandwidth (data)); - GST_DEBUG_OBJECT (dec, "samples %d", samples); - } else { - /* use maximum size (120 ms) as we do now know in advance how many samples - will be returned */ - samples = 120 * dec->sample_rate / 1000; - } + /* use maximum size (120 ms) as the number of returned samples is + not constant over the stream. */ + samples = 120 * dec->sample_rate / 1000; packet_size = samples * dec->n_channels * 2; res = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), @@ -280,7 +319,19 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) out_data = (gint16 *) GST_BUFFER_DATA (outbuf); - n = opus_decode (dec->state, data, size, out_data, samples, 0); + if (dec->use_inband_fec) { + if (dec->last_buffer) { + /* normal delayed decode */ + n = opus_decode (dec->state, data, size, out_data, samples, 0); + } else { + /* FEC reconstruction decode */ + n = opus_decode (dec->state, data, size, out_data, samples, 1); + } + } else { + /* normal decode */ + n = opus_decode (dec->state, data, size, out_data, samples, 0); + } + if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); return GST_FLOW_ERROR; @@ -311,6 +362,12 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf) if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); +done: + if (dec->use_inband_fec) { + gst_buffer_replace (&dec->last_buffer, buffer); + dec->primed = TRUE; + } + return res; creation_failed: @@ -437,3 +494,35 @@ gst_opus_dec_handle_frame (GstAudioDecoder * adec, GstBuffer * buf) return res; } + +static void +gst_opus_dec_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstOpusDec *dec = GST_OPUS_DEC (object); + + switch (prop_id) { + case PROP_USE_INBAND_FEC: + g_value_set_boolean (value, dec->use_inband_fec); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_opus_dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOpusDec *dec = GST_OPUS_DEC (object); + + switch (prop_id) { + case PROP_USE_INBAND_FEC: + dec->use_inband_fec = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index eee27dc554..081e575247 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -54,6 +54,10 @@ struct _GstOpusDec { int sample_rate; int n_channels; guint32 pre_skip; + + gboolean use_inband_fec; + GstBuffer *last_buffer; + gboolean primed; }; struct _GstOpusDecClass { From 5c8812f58c5d0f41d29e4ec4bb647a1151050a74 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 23 Nov 2011 13:22:12 +0000 Subject: [PATCH 063/197] opusdec: implement replay gain It would ideally be better to leave this to a rgvolume element, but we don't control the pipeline. So do it by default, and allow disabling it via a property, so the correct volume should always be output. --- ext/opus/gstopusdec.c | 55 +++++++++++++++++++++++++++++++++++++++++-- ext/opus/gstopusdec.h | 4 ++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 7d70fea184..e85a6b7471 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -41,6 +41,7 @@ # include "config.h" #endif +#include #include #include #include "gstopusheader.h" @@ -67,12 +68,16 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-opus") ); +#define DB_TO_LINEAR(x) pow (10., (x) / 20.) + #define DEFAULT_USE_INBAND_FEC FALSE +#define DEFAULT_APPLY_GAIN TRUE enum { PROP_0, - PROP_USE_INBAND_FEC + PROP_USE_INBAND_FEC, + PROP_APPLY_GAIN }; GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstAudioDecoder, @@ -131,6 +136,11 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) "Use forward error correction if available", DEFAULT_USE_INBAND_FEC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_APPLY_GAIN, + g_param_spec_boolean ("apply-gain", "Apply gain", + "Apply gain if any is specified in the header", DEFAULT_APPLY_GAIN, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0, "opus decoding element"); } @@ -150,6 +160,7 @@ gst_opus_dec_reset (GstOpusDec * dec) dec->primed = FALSE; dec->pre_skip = 0; + dec->r128_gain = 0; } static void @@ -158,6 +169,7 @@ gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class) dec->sample_rate = 0; dec->n_channels = 0; dec->use_inband_fec = FALSE; + dec->apply_gain = DEFAULT_APPLY_GAIN; gst_opus_dec_reset (dec); } @@ -190,6 +202,18 @@ gst_opus_dec_stop (GstAudioDecoder * dec) return TRUE; } +static double +gst_opus_dec_get_r128_gain (gint16 r128_gain) +{ + return r128_gain / (double) (1 << 8); +} + +static double +gst_opus_dec_get_r128_volume (gint16 r128_gain) +{ + return DB_TO_LINEAR (gst_opus_dec_get_r128_gain (r128_gain)); +} + static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { @@ -198,7 +222,11 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 19, GST_FLOW_ERROR); dec->pre_skip = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 10); - GST_INFO_OBJECT (dec, "Found pre-skip of %u samples", dec->pre_skip); + dec->r128_gain = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 14); + dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); + GST_INFO_OBJECT (dec, + "Found pre-skip of %u samples, R128 gain %d (volume %f)", + dec->pre_skip, dec->r128_gain, dec->r128_gain_volume); return GST_FLOW_OK; } @@ -357,6 +385,23 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) } } + /* Apply gain */ + /* Would be better off leaving this to a volume element, as this is + a naive conversion that does too many int/float conversions. + However, we don't have control over the pipeline... + So make it optional if the user program wants to use a volume, + but do it by default so the correct volume goes out by default */ + if (dec->apply_gain && outbuf && dec->r128_gain) { + unsigned int i, nsamples = GST_BUFFER_SIZE (outbuf) / 2; + double volume = dec->r128_gain_volume; + gint16 *samples = (gint16 *) GST_BUFFER_DATA (outbuf); + GST_DEBUG_OBJECT (dec, "Applying gain: volume %f", volume); + for (i = 0; i < nsamples; ++i) { + int sample = (int) (samples[i] * volume + 0.5); + samples[i] = sample < -32768 ? -32768 : sample > 32767 ? 32767 : sample; + } + } + res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) @@ -505,6 +550,9 @@ gst_opus_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_USE_INBAND_FEC: g_value_set_boolean (value, dec->use_inband_fec); break; + case PROP_APPLY_GAIN: + g_value_set_boolean (value, dec->apply_gain); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -521,6 +569,9 @@ gst_opus_dec_set_property (GObject * object, guint prop_id, case PROP_USE_INBAND_FEC: dec->use_inband_fec = g_value_get_boolean (value); break; + case PROP_APPLY_GAIN: + dec->apply_gain = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 081e575247..b074787d7f 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -54,6 +54,10 @@ struct _GstOpusDec { int sample_rate; int n_channels; guint32 pre_skip; + gint16 r128_gain; + + gboolean apply_gain; + double r128_gain_volume; gboolean use_inband_fec; GstBuffer *last_buffer; From d1d8cbad95611203718b5d93c1f929528e978cb5 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 23 Nov 2011 16:36:54 +0000 Subject: [PATCH 064/197] opusenc: remove useless setup field --- ext/opus/gstopusenc.c | 4 ---- ext/opus/gstopusenc.h | 1 - 2 files changed, 5 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 29b9faf756..71e19b045a 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -458,8 +458,6 @@ gst_opus_enc_setup (GstOpusEnc * enc) GST_DEBUG_OBJECT (enc, "setup"); - enc->setup = FALSE; - enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); @@ -479,8 +477,6 @@ gst_opus_enc_setup (GstOpusEnc * enc) GST_LOG_OBJECT (enc, "we have frame size %d", enc->frame_size); - enc->setup = TRUE; - return TRUE; encoder_creation_failed: diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 772f6f4cc7..58c617c5a1 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -72,7 +72,6 @@ struct _GstOpusEnc { gint n_channels; gint sample_rate; - gboolean setup; gboolean header_sent; GSList *headers; From e4ae3e89faa4b5fe09319ffdee1e696b5c8e8448 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 23 Nov 2011 17:32:03 +0000 Subject: [PATCH 065/197] opusdec: shuffle supported sample rates to favor 48000 --- ext/opus/gstopusdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index e85a6b7471..7ac2bda279 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -55,7 +55,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " - "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " + "rate = (int) { 48000, 24000, 16000, 12000, 8000 }, " "channels = (int) [ 1, 2 ], " "endianness = (int) BYTE_ORDER, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") From 670c365400b4babd4b907f807bbc52de0b7232a4 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 23 Nov 2011 17:49:58 +0000 Subject: [PATCH 066/197] opus: switch to multistream API It's very similar to the basic API, and is a superset ot it, which will allow encoding and decoding more than 2 channels. --- ext/opus/gstopusdec.c | 23 ++++++++++++++++++----- ext/opus/gstopusdec.h | 6 ++++-- ext/opus/gstopusenc.c | 40 +++++++++++++++++++++++++--------------- ext/opus/gstopusenc.h | 4 ++-- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 7ac2bda279..f8b39ba08c 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -150,7 +150,7 @@ gst_opus_dec_reset (GstOpusDec * dec) { dec->packetno = 0; if (dec->state) { - opus_decoder_destroy (dec->state); + opus_multistream_decoder_destroy (dec->state); dec->state = NULL; } @@ -228,6 +228,16 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) "Found pre-skip of %u samples, R128 gain %d (volume %f)", dec->pre_skip, dec->r128_gain, dec->r128_gain_volume); + dec->channel_mapping_family = GST_BUFFER_DATA (buf)[18]; + if (dec->channel_mapping_family != 0) { + GST_ELEMENT_ERROR (dec, STREAM, DECODE, + ("Decoding error: unsupported channel nmapping family %d", + dec->channel_mapping_family), (NULL)); + return GST_FLOW_ERROR; + } + dec->channel_mapping[0] = 0; + dec->channel_mapping[1] = 1; + return GST_FLOW_OK; } @@ -298,7 +308,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); - dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err); + dec->state = opus_multistream_decoder_create (dec->sample_rate, + dec->n_channels, 1, 1, dec->channel_mapping, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; } @@ -350,14 +361,16 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) if (dec->use_inband_fec) { if (dec->last_buffer) { /* normal delayed decode */ - n = opus_decode (dec->state, data, size, out_data, samples, 0); + n = opus_multistream_decode (dec->state, data, size, out_data, samples, + 0); } else { /* FEC reconstruction decode */ - n = opus_decode (dec->state, data, size, out_data, samples, 1); + n = opus_multistream_decode (dec->state, data, size, out_data, samples, + 1); } } else { /* normal decode */ - n = opus_decode (dec->state, data, size, out_data, samples, 0); + n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } if (n < 0) { diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index b074787d7f..aa04b814d9 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -23,7 +23,7 @@ #include #include -#include +#include G_BEGIN_DECLS @@ -44,7 +44,7 @@ typedef struct _GstOpusDecClass GstOpusDecClass; struct _GstOpusDec { GstAudioDecoder element; - OpusDecoder *state; + OpusMSDecoder *state; guint64 packetno; @@ -55,6 +55,8 @@ struct _GstOpusDec { int n_channels; guint32 pre_skip; gint16 r128_gain; + guint8 channel_mapping_family; + guint8 channel_mapping[256]; gboolean apply_gain; double r128_gain_volume; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 71e19b045a..93e00aa38f 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -355,7 +355,7 @@ gst_opus_enc_stop (GstAudioEncoder * benc) GST_DEBUG_OBJECT (enc, "stop"); enc->header_sent = FALSE; if (enc->state) { - opus_encoder_destroy (enc->state); + opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } gst_tag_list_free (enc->tags); @@ -435,7 +435,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) /* handle reconfigure */ if (enc->state) { - opus_encoder_destroy (enc->state); + opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } if (!gst_opus_enc_setup (enc)) @@ -455,24 +455,34 @@ static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; + unsigned char mapping[256]; + int n; GST_DEBUG_OBJECT (enc, "setup"); - enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, + for (n = 0; n < enc->n_channels; ++n) + mapping[n] = n; + + enc->state = + opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, + (enc->n_channels + 1) / 2, enc->n_channels / 2, mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) goto encoder_creation_failed; - opus_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); - opus_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); - opus_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr), 0); - opus_encoder_ctl (enc->state, OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), + opus_multistream_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); + opus_multistream_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); - opus_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0); - opus_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC (enc->inband_fec), 0); - opus_encoder_ctl (enc->state, OPUS_SET_DTX (enc->dtx), 0); - opus_encoder_ctl (enc->state, + opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr), 0); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), 0); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_COMPLEXITY (enc->complexity), 0); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_INBAND_FEC (enc->inband_fec), 0); + opus_multistream_encoder_ctl (enc->state, OPUS_SET_DTX (enc->dtx), 0); + opus_multistream_encoder_ctl (enc->state, OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0); GST_LOG_OBJECT (enc, "we have frame size %d", enc->frame_size); @@ -557,8 +567,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) enc->frame_samples); outsize = - opus_encode (enc->state, (const gint16 *) data, enc->frame_samples, - GST_BUFFER_DATA (outbuf), enc->max_payload_size); + opus_multistream_encode (enc->state, (const gint16 *) data, + enc->frame_samples, GST_BUFFER_DATA (outbuf), enc->max_payload_size); if (outsize < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); @@ -694,7 +704,7 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, g_mutex_lock (enc->property_lock); \ enc->prop = g_value_get_##type (value); \ if (enc->state) { \ - opus_encoder_ctl (enc->state, OPUS_SET_##ctl (enc->prop)); \ + opus_multistream_encoder_ctl (enc->state, OPUS_SET_##ctl (enc->prop)); \ } \ g_mutex_unlock (enc->property_lock); \ } while(0) @@ -720,7 +730,7 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, /* this one has an opposite meaning to the opus ctl... */ g_mutex_lock (enc->property_lock); enc->cbr = g_value_get_boolean (value); - opus_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); + opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); g_mutex_unlock (enc->property_lock); break; case PROP_CONSTRAINED_VBR: diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 58c617c5a1..8304e82556 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -26,7 +26,7 @@ #include #include -#include +#include G_BEGIN_DECLS @@ -50,7 +50,7 @@ typedef struct _GstOpusEncClass GstOpusEncClass; struct _GstOpusEnc { GstAudioEncoder element; - OpusEncoder *state; + OpusMSEncoder *state; /* Locks those properties which may be changed at play time */ GMutex *property_lock; From d38f4b8a09e83db60264d22f827e957b357ef74e Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 24 Nov 2011 13:29:56 +0000 Subject: [PATCH 067/197] opus: multichannel support --- ext/opus/Makefile.am | 4 +- ext/opus/gstopuscommon.c | 72 ++++++++++++++++++++ ext/opus/gstopuscommon.h | 33 +++++++++ ext/opus/gstopusdec.c | 141 +++++++++++++++++++++++---------------- ext/opus/gstopusdec.h | 3 + ext/opus/gstopusenc.c | 100 +++++++++++++++++++++++---- ext/opus/gstopusenc.h | 3 + ext/opus/gstopusheader.c | 92 ++++++++++++++++++++----- ext/opus/gstopusheader.h | 12 +++- 9 files changed, 369 insertions(+), 91 deletions(-) create mode 100644 ext/opus/gstopuscommon.c create mode 100644 ext/opus/gstopuscommon.h diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 88845a3cab..cb0a9b338a 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstopus.la -libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c gstopuscommon.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -15,4 +15,4 @@ libgstopus_la_LIBADD = \ libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) libgstopus_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h +noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h gstopuscommon.h diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c new file mode 100644 index 0000000000..fc3e0376b9 --- /dev/null +++ b/ext/opus/gstopuscommon.c @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstopuscommon.h" + +/* http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 */ +/* copy of the same structure in the vorbis plugin */ +const GstAudioChannelPosition gst_opus_channel_positions[][8] = { + { /* Mono */ + GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}, + { /* Stereo */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + { /* Stereo + Centre */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + { /* Quadraphonic */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + }, + { /* Stereo + Centre + rear stereo */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + }, + { /* Full 5.1 Surround */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE, + }, + { /* 6.1 Surround, in Vorbis spec since 2010-01-13 */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE}, + { /* 7.1 Surround, in Vorbis spec since 2010-01-13 */ + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE}, +}; diff --git a/ext/opus/gstopuscommon.h b/ext/opus/gstopuscommon.h new file mode 100644 index 0000000000..96a303e303 --- /dev/null +++ b/ext/opus/gstopuscommon.h @@ -0,0 +1,33 @@ +/* GStreamer Opus Encoder + * Copyright (C) 2009 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_OPUS_COMMON_H__ +#define __GST_OPUS_COMMON_H__ + +#include +#include + +G_BEGIN_DECLS + +extern const GstAudioChannelPosition gst_opus_channel_positions[][8]; + +G_END_DECLS + +#endif /* __GST_OPUS_COMMON_H__ */ diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index f8b39ba08c..58283973a2 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -45,6 +45,7 @@ #include #include #include "gstopusheader.h" +#include "gstopuscommon.h" #include "gstopusdec.h" GST_DEBUG_CATEGORY_STATIC (opusdec_debug); @@ -56,7 +57,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " "rate = (int) { 48000, 24000, 16000, 12000, 8000 }, " - "channels = (int) [ 1, 2 ], " + "channels = (int) [ 1, 8 ], " "endianness = (int) BYTE_ORDER, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") ); @@ -217,26 +218,91 @@ gst_opus_dec_get_r128_volume (gint16 r128_gain) static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { - g_return_val_if_fail (gst_opus_header_is_header (buf, "OpusHead", 8), - GST_FLOW_ERROR); - g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 19, GST_FLOW_ERROR); + const guint8 *data = GST_BUFFER_DATA (buf); + GstCaps *caps; + GstStructure *s; + const GstAudioChannelPosition *pos = NULL; - dec->pre_skip = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 10); - dec->r128_gain = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 14); + g_return_val_if_fail (gst_opus_header_is_id_header (buf), GST_FLOW_ERROR); + g_return_val_if_fail (dec->n_channels != data[9], GST_FLOW_ERROR); + + dec->n_channels = data[9]; + dec->pre_skip = GST_READ_UINT16_LE (data + 10); + dec->r128_gain = GST_READ_UINT16_LE (data + 14); dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); GST_INFO_OBJECT (dec, "Found pre-skip of %u samples, R128 gain %d (volume %f)", dec->pre_skip, dec->r128_gain, dec->r128_gain_volume); - dec->channel_mapping_family = GST_BUFFER_DATA (buf)[18]; - if (dec->channel_mapping_family != 0) { - GST_ELEMENT_ERROR (dec, STREAM, DECODE, - ("Decoding error: unsupported channel nmapping family %d", - dec->channel_mapping_family), (NULL)); - return GST_FLOW_ERROR; + dec->channel_mapping_family = data[18]; + if (dec->channel_mapping_family == 0) { + /* implicit mapping */ + GST_INFO_OBJECT (dec, "Channel mapping family 0, implicit mapping"); + dec->n_streams = dec->n_stereo_streams = 1; + dec->channel_mapping[0] = 0; + dec->channel_mapping[1] = 1; + } else { + dec->n_streams = data[19]; + dec->n_stereo_streams = data[20]; + memcpy (dec->channel_mapping, data + 21, dec->n_channels); + + if (dec->channel_mapping_family == 1) { + GST_INFO_OBJECT (dec, "Channel mapping family 1, Vorbis mapping"); + switch (dec->n_channels) { + case 1: + case 2: + /* nothing */ + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + pos = gst_opus_channel_positions[dec->n_channels - 1]; + break; + default:{ + gint i; + GstAudioChannelPosition *posn = + g_new (GstAudioChannelPosition, dec->n_channels); + + GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Using NONE channel layout for more than 8 channels")); + + for (i = 0; i < dec->n_channels; i++) + posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + + pos = posn; + } + } + } else { + GST_INFO_OBJECT (dec, "Channel mapping family %d", + dec->channel_mapping_family); + } } - dec->channel_mapping[0] = 0; - dec->channel_mapping[1] = 1; + + /* negotiate width with downstream */ + caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); + s = gst_caps_get_structure (caps, 0); + gst_structure_fixate_field_nearest_int (s, "rate", 48000); + gst_structure_get_int (s, "rate", &dec->sample_rate); + gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); + gst_structure_get_int (s, "channels", &dec->n_channels); + + GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, + dec->sample_rate); + + if (pos) { + gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); + } + + if (dec->n_channels > 8) { + g_free ((GstAudioChannelPosition *) pos); + } + + GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); + gst_caps_unref (caps); return GST_FLOW_OK; } @@ -248,48 +314,6 @@ gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) return GST_FLOW_OK; } -static void -gst_opus_dec_setup_from_peer_caps (GstOpusDec * dec) -{ - GstPad *srcpad, *peer; - GstStructure *s; - GstCaps *caps; - const GstCaps *template_caps; - const GstCaps *peer_caps; - - srcpad = GST_AUDIO_DECODER_SRC_PAD (dec); - peer = gst_pad_get_peer (srcpad); - - if (peer) { - template_caps = gst_pad_get_pad_template_caps (srcpad); - peer_caps = gst_pad_get_caps (peer); - GST_DEBUG_OBJECT (dec, "Peer caps: %" GST_PTR_FORMAT, peer_caps); - caps = gst_caps_intersect (template_caps, peer_caps); - gst_pad_fixate_caps (peer, caps); - GST_DEBUG_OBJECT (dec, "Fixated caps: %" GST_PTR_FORMAT, caps); - - s = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (s, "channels", &dec->n_channels)) { - dec->n_channels = 2; - GST_WARNING_OBJECT (dec, "Failed to get channels, using default %d", - dec->n_channels); - } else { - GST_DEBUG_OBJECT (dec, "Got channels %d", dec->n_channels); - } - if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) { - dec->sample_rate = 48000; - GST_WARNING_OBJECT (dec, "Failed to get rate, using default %d", - dec->sample_rate); - } else { - GST_DEBUG_OBJECT (dec, "Got sample rate %d", dec->sample_rate); - } - - gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); - } else { - GST_WARNING_OBJECT (dec, "Failed to get src pad peer"); - } -} - static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) { @@ -304,12 +328,11 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GstBuffer *buf; if (dec->state == NULL) { - gst_opus_dec_setup_from_peer_caps (dec); - GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); dec->state = opus_multistream_decoder_create (dec->sample_rate, - dec->n_channels, 1, 1, dec->channel_mapping, &err); + dec->n_channels, dec->n_streams, dec->n_stereo_streams, + dec->channel_mapping, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; } diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index aa04b814d9..3ccfa26969 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -55,6 +55,9 @@ struct _GstOpusDec { int n_channels; guint32 pre_skip; gint16 r128_gain; + + guint8 n_streams; + guint8 n_stereo_streams; guint8 channel_mapping_family; guint8 channel_mapping[256]; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 93e00aa38f..6dad531156 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -49,6 +49,7 @@ #include #include #include "gstopusheader.h" +#include "gstopuscommon.h" #include "gstopusenc.h" GST_DEBUG_CATEGORY_STATIC (opusenc_debug); @@ -116,8 +117,8 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw-int, " - "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " - "channels = (int) [ 1, 2 ], " + "rate = (int) { 48000, 24000, 16000, 12000, 8000 }, " + "channels = (int) [ 1, 8 ], " "endianness = (int) BYTE_ORDER, " "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16") ); @@ -419,6 +420,82 @@ gst_opus_enc_get_frame_samples (GstOpusEnc * enc) return frame_samples; } +static void +gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) +{ +#define MAPS(idx,pos) (GST_AUDIO_INFO_POSITION (info, (idx)) == GST_AUDIO_CHANNEL_POSITION_##pos) + + int n; + + GST_DEBUG_OBJECT (enc, "Setting up channel mapping for %d channels", + enc->n_channels); + + /* Start by setting up a default trivial mapping */ + for (n = 0; n < 255; ++n) + enc->channel_mapping[n] = n; + + /* For one channel, use the basic RTP mapping */ + if (enc->n_channels == 1) { + GST_INFO_OBJECT (enc, "Mono, trivial RTP mapping"); + enc->channel_mapping_family = 0; + enc->channel_mapping[0] = 0; + return; + } + + /* For two channels, use the basic RTP mapping if the channels are + mapped as left/right. */ + if (enc->n_channels == 2) { + if (MAPS (0, FRONT_LEFT) && MAPS (1, FRONT_RIGHT)) { + GST_INFO_OBJECT (enc, "Stereo, canonical mapping"); + enc->channel_mapping_family = 0; + /* The channel mapping is implicit for family 0, that's why we do not + attempt to create one for right/left - this will be mapped to the + Vorbis mapping below. */ + } else { + GST_DEBUG_OBJECT (enc, "Stereo, but not canonical mapping, continuing"); + } + } + + /* For channels between 1 and 8, we use the Vorbis mapping if we can + find a permutation that matches it. Mono will have been taken care + of earlier, but this code also handles it. */ + if (enc->n_channels >= 1 && enc->n_channels <= 8) { + GST_DEBUG_OBJECT (enc, + "In range for the Vorbis mapping, checking channel positions"); + for (n = 0; n < enc->n_channels; ++n) { + GstAudioChannelPosition pos = GST_AUDIO_INFO_POSITION (info, n); + int c; + + GST_DEBUG_OBJECT (enc, "Channel %d has position %d", n, pos); + for (c = 0; c < enc->n_channels; ++c) { + if (gst_opus_channel_positions[enc->n_channels - 1][c] == pos) { + GST_DEBUG_OBJECT (enc, "Found in Vorbis mapping as channel %d", c); + break; + } + } + if (c == enc->n_channels) { + /* We did not find that position, so use undefined */ + GST_WARNING_OBJECT (enc, + "Position %d not found in Vorbis mapping, using unknown mapping", + pos); + enc->channel_mapping_family = 255; + return; + } + GST_DEBUG_OBJECT (enc, "Mapping output channel %d to %d", c, n); + enc->channel_mapping[c] = n; + } + GST_INFO_OBJECT (enc, "Permutation found, using Vorbis mapping"); + enc->channel_mapping_family = 1; + return; + } + + /* For other cases, we use undefined, with the default trivial mapping */ + GST_WARNING_OBJECT (enc, "Unknown mapping"); + enc->channel_mapping_family = 255; + +#undef MAPS +} + static gboolean gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) { @@ -430,6 +507,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); enc->sample_rate = GST_AUDIO_INFO_RATE (info); + gst_opus_enc_setup_channel_mapping (enc, info); GST_DEBUG_OBJECT (benc, "Setup with %d channels, %d Hz", enc->n_channels, enc->sample_rate); @@ -455,17 +533,12 @@ static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; - unsigned char mapping[256]; - int n; GST_DEBUG_OBJECT (enc, "setup"); - for (n = 0; n < enc->n_channels; ++n) - mapping[n] = n; - enc->state = opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, - (enc->n_channels + 1) / 2, enc->n_channels / 2, mapping, + (enc->n_channels + 1) / 2, enc->n_channels / 2, enc->channel_mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) @@ -557,18 +630,19 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GstBuffer *outbuf; ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), - GST_BUFFER_OFFSET_NONE, enc->max_payload_size, + GST_BUFFER_OFFSET_NONE, enc->max_payload_size * enc->n_channels, GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf); if (GST_FLOW_OK != ret) goto done; GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - enc->frame_samples); + enc->frame_samples, (int) bytes); outsize = opus_multistream_encode (enc->state, (const gint16 *) data, - enc->frame_samples, GST_BUFFER_DATA (outbuf), enc->max_payload_size); + enc->frame_samples, GST_BUFFER_DATA (outbuf), + enc->max_payload_size * enc->n_channels); if (outsize < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); @@ -582,6 +656,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) goto done; } + GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); GST_BUFFER_SIZE (outbuf) = outsize; ret = @@ -621,7 +696,8 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) enc->headers = NULL; gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, - enc->sample_rate, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); + enc->sample_rate, enc->channel_mapping_family, enc->channel_mapping, + gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); /* negotiate with these caps */ diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 8304e82556..8c2c3c6e82 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -77,6 +77,9 @@ struct _GstOpusEnc { GSList *headers; GstTagList *tags; + + guint8 channel_mapping_family; + guint8 channel_mapping[256]; }; struct _GstOpusEncClass { diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 3551055840..0379c909f4 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -27,7 +27,8 @@ #include "gstopusheader.h" static GstBuffer * -gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate) +gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, + guint8 channel_mapping_family, const guint8 * channel_mapping) { GstBuffer *buffer; GstByteWriter bw; @@ -41,7 +42,12 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate) gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ gst_byte_writer_put_uint32_le (&bw, sample_rate); gst_byte_writer_put_uint16_le (&bw, 0); /* output gain *//* TODO: endianness ? */ - gst_byte_writer_put_uint8 (&bw, 0); /* channel mapping *//* TODO: what is this ? */ + gst_byte_writer_put_uint8 (&bw, channel_mapping_family); + if (channel_mapping_family > 0) { + gst_byte_writer_put_uint8 (&bw, (nchannels + 1) / 2); + gst_byte_writer_put_uint8 (&bw, nchannels / 2); + gst_byte_writer_put_data (&bw, channel_mapping, nchannels); + } buffer = gst_byte_writer_reset_and_get_buffer (&bw); @@ -136,23 +142,11 @@ _gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, } void -gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, - gint sample_rate, const GstTagList * tags) +gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, + GstBuffer * buf1, GstBuffer * buf2) { - GstBuffer *buf1, *buf2; - g_return_if_fail (caps); g_return_if_fail (headers && !*headers); - g_return_if_fail (nchannels > 0); - g_return_if_fail (sample_rate >= 0); /* 0 -> unset */ - - /* Opus streams in Ogg begin with two 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. */ - - /* create header buffers */ - buf1 = gst_opus_enc_create_id_buffer (nchannels, sample_rate); - buf2 = gst_opus_enc_create_metadata_buffer (tags); /* mark and put on caps */ *caps = gst_caps_from_string ("audio/x-opus"); @@ -162,9 +156,75 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, *headers = g_slist_prepend (*headers, buf1); } +void +gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, + gint sample_rate, guint8 channel_mapping_family, + const guint8 * channel_mapping, const GstTagList * tags) +{ + GstBuffer *buf1, *buf2; + + g_return_if_fail (caps); + g_return_if_fail (headers && !*headers); + g_return_if_fail (nchannels > 0); + g_return_if_fail (sample_rate >= 0); /* 0 -> unset */ + g_return_if_fail (channel_mapping_family == 0 || channel_mapping); + + /* Opus streams in Ogg begin with two 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. */ + + /* create header buffers */ + buf1 = + gst_opus_enc_create_id_buffer (nchannels, sample_rate, + channel_mapping_family, channel_mapping); + buf2 = gst_opus_enc_create_metadata_buffer (tags); + + gst_opus_header_create_caps_from_headers (caps, headers, buf1, buf2); +} + gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size) { return (GST_BUFFER_SIZE (buf) >= magic_size && !memcmp (magic, GST_BUFFER_DATA (buf), magic_size)); } + +gboolean +gst_opus_header_is_id_header (GstBuffer * buf) +{ + gsize size = GST_BUFFER_SIZE (buf); + const guint8 *data = GST_BUFFER_DATA (buf); + guint8 channels, channel_mapping_family, n_streams, n_stereo_streams; + + if (size < 19) + return FALSE; + if (!gst_opus_header_is_header (buf, "OpusHead", 8)) + return FALSE; + channels = data[9]; + if (channels == 0) + return FALSE; + channel_mapping_family = data[18]; + if (channel_mapping_family == 0) { + if (channels > 2) + return FALSE; + } else { + channels = data[9]; + if (size < 21 + channels) + return FALSE; + n_streams = data[19]; + n_stereo_streams = data[20]; + if (n_streams == 0) + return FALSE; + if (n_stereo_streams > n_streams) + return FALSE; + if (n_streams + n_stereo_streams > 255) + return FALSE; + } + return TRUE; +} + +gboolean +gst_opus_header_is_comment_header (GstBuffer * buf) +{ + return gst_opus_header_is_header (buf, "OpusTags", 8); +} diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 4594264ccd..3b2cfc265f 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -25,8 +25,16 @@ G_BEGIN_DECLS -extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, gint nchannels, gint sample_rate, const GstTagList *tags); -extern gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size); +extern void gst_opus_header_create_caps_from_headers (GstCaps **caps, GSList **headers, + GstBuffer *id_header, GstBuffer *comment_header); +extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, + gint nchannels, gint sample_rate, + guint8 channel_mapping_family, const guint8 *channel_mapping, + const GstTagList *tags); +extern gboolean gst_opus_header_is_header (GstBuffer * buf, + const char *magic, guint magic_size); +extern gboolean gst_opus_header_is_id_header (GstBuffer * buf); +extern gboolean gst_opus_header_is_comment_header (GstBuffer * buf); G_END_DECLS From 32e9de842fc963a4af4d9a7148f7e3c909ab96c8 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 24 Nov 2011 13:38:59 +0000 Subject: [PATCH 068/197] opus: pre-skip and output gain are little endian, remove reminder note --- ext/opus/gstopusheader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 0379c909f4..1aa3b0d01b 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -39,9 +39,9 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); gst_byte_writer_put_uint8 (&bw, 0); /* version number */ gst_byte_writer_put_uint8 (&bw, nchannels); - gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip *//* TODO: endianness ? */ + gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip */ gst_byte_writer_put_uint32_le (&bw, sample_rate); - gst_byte_writer_put_uint16_le (&bw, 0); /* output gain *//* TODO: endianness ? */ + gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ gst_byte_writer_put_uint8 (&bw, channel_mapping_family); if (channel_mapping_family > 0) { gst_byte_writer_put_uint8 (&bw, (nchannels + 1) / 2); From 1e00fbdacb8f9afd3113fdccf56632277552248c Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 25 Nov 2011 12:39:20 +0000 Subject: [PATCH 069/197] opusdec: fix bogus assertion --- ext/opus/gstopusdec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 58283973a2..cc1bdb2a11 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -224,7 +224,8 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) const GstAudioChannelPosition *pos = NULL; g_return_val_if_fail (gst_opus_header_is_id_header (buf), GST_FLOW_ERROR); - g_return_val_if_fail (dec->n_channels != data[9], GST_FLOW_ERROR); + g_return_val_if_fail (dec->n_channels == 0 + || dec->n_channels == data[9], GST_FLOW_ERROR); dec->n_channels = data[9]; dec->pre_skip = GST_READ_UINT16_LE (data + 10); From 605d407b16d3ae7bea5c5a10f4908be223245784 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 25 Nov 2011 12:40:31 +0000 Subject: [PATCH 070/197] opusenc: do not cause the decoder to apply the channel mapping again Since we already reorder channels, we do not want to write that reordering in the header, or the decoder will do it again. --- ext/opus/gstopusenc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 6dad531156..11ddcc127e 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -532,13 +532,17 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { - int error = OPUS_OK; + int error = OPUS_OK, n; + guint8 trivial_mapping[256]; GST_DEBUG_OBJECT (enc, "setup"); + for (n = 0; n < 256; ++n) + trivial_mapping[n] = n; + enc->state = opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, - (enc->n_channels + 1) / 2, enc->n_channels / 2, enc->channel_mapping, + (enc->n_channels + 1) / 2, enc->n_channels / 2, trivial_mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) From 287a0c646e1ef7f7e9fcf07c80e189406e51a150 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 25 Nov 2011 12:47:42 +0000 Subject: [PATCH 071/197] opus: add some more debug information about channel mapping --- ext/opus/gstopuscommon.c | 16 ++++++++++++++++ ext/opus/gstopuscommon.h | 1 + ext/opus/gstopusdec.c | 1 + ext/opus/gstopusenc.c | 10 ++++++---- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c index fc3e0376b9..426c5b8973 100644 --- a/ext/opus/gstopuscommon.c +++ b/ext/opus/gstopuscommon.c @@ -70,3 +70,19 @@ const GstAudioChannelPosition gst_opus_channel_positions[][8] = { GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_LFE}, }; + +const char *gst_opus_channel_names[] = { + "mono", + "front left", + "front right", + "rear center", + "rear left", + "rear right", + "lfe", + "front center", + "front left of center", + "front right of center", + "side left", + "side right", + "none" +}; diff --git a/ext/opus/gstopuscommon.h b/ext/opus/gstopuscommon.h index 96a303e303..65b944e9e2 100644 --- a/ext/opus/gstopuscommon.h +++ b/ext/opus/gstopuscommon.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS extern const GstAudioChannelPosition gst_opus_channel_positions[][8]; +extern const char *gst_opus_channel_names[]; G_END_DECLS diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index cc1bdb2a11..ee1886040d 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -294,6 +294,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) dec->sample_rate); if (pos) { + GST_DEBUG_OBJECT (dec, "Setting channel positions on caps"); gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); } diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 11ddcc127e..a926bbdb4c 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -466,7 +466,8 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) GstAudioChannelPosition pos = GST_AUDIO_INFO_POSITION (info, n); int c; - GST_DEBUG_OBJECT (enc, "Channel %d has position %d", n, pos); + GST_DEBUG_OBJECT (enc, "Channel %d has position %d (%s)", n, pos, + gst_opus_channel_names[pos]); for (c = 0; c < enc->n_channels; ++c) { if (gst_opus_channel_positions[enc->n_channels - 1][c] == pos) { GST_DEBUG_OBJECT (enc, "Found in Vorbis mapping as channel %d", c); @@ -476,12 +477,13 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) if (c == enc->n_channels) { /* We did not find that position, so use undefined */ GST_WARNING_OBJECT (enc, - "Position %d not found in Vorbis mapping, using unknown mapping", - pos); + "Position %d (%s) not found in Vorbis mapping, using unknown mapping", + pos, gst_opus_channel_positions[pos]); enc->channel_mapping_family = 255; return; } - GST_DEBUG_OBJECT (enc, "Mapping output channel %d to %d", c, n); + GST_DEBUG_OBJECT (enc, "Mapping output channel %d to %d (%s)", c, n, + gst_opus_channel_names[pos]); enc->channel_mapping[c] = n; } GST_INFO_OBJECT (enc, "Permutation found, using Vorbis mapping"); From 8ac75d10fd72b98be4b9b526a82f1286a2c2f48d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 25 Nov 2011 14:00:18 +0000 Subject: [PATCH 072/197] opusenc: only use mono streams for > 2 channels I'm getting odd results with packing streams into stereo streams, and using only mono streams is enough in all cases. --- ext/opus/gstopusenc.c | 2 +- ext/opus/gstopusheader.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index a926bbdb4c..c6604c2196 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -544,7 +544,7 @@ gst_opus_enc_setup (GstOpusEnc * enc) enc->state = opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, - (enc->n_channels + 1) / 2, enc->n_channels / 2, trivial_mapping, + enc->n_channels, 0, trivial_mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 1aa3b0d01b..42df7b35fb 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -44,8 +44,8 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ gst_byte_writer_put_uint8 (&bw, channel_mapping_family); if (channel_mapping_family > 0) { - gst_byte_writer_put_uint8 (&bw, (nchannels + 1) / 2); - gst_byte_writer_put_uint8 (&bw, nchannels / 2); + gst_byte_writer_put_uint8 (&bw, nchannels); + gst_byte_writer_put_uint8 (&bw, 0); gst_byte_writer_put_data (&bw, channel_mapping, nchannels); } From 64c3d736426a947823dfceda15504dfb8b6da370 Mon Sep 17 00:00:00 2001 From: Danilo Cesar Lemes de Paula Date: Fri, 25 Nov 2011 11:41:19 -0200 Subject: [PATCH 073/197] opusenc: Fixing "Unused var" compiling error for opus codec https://bugzilla.gnome.org/show_bug.cgi?id=664815 --- ext/opus/gstopusdec.c | 2 -- ext/opus/gstopusenc.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index ee1886040d..89639b12b8 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -118,11 +118,9 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) { GObjectClass *gobject_class; GstAudioDecoderClass *adclass; - GstElementClass *gstelement_class; gobject_class = (GObjectClass *) klass; adclass = (GstAudioDecoderClass *) klass; - gstelement_class = (GstElementClass *) klass; gobject_class->set_property = gst_opus_dec_set_property; gobject_class->get_property = gst_opus_dec_get_property; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index c6604c2196..8ca1158c99 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -218,11 +218,9 @@ static void gst_opus_enc_class_init (GstOpusEncClass * klass) { GObjectClass *gobject_class; - GstElementClass *gstelement_class; GstAudioEncoderClass *base_class; gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; base_class = (GstAudioEncoderClass *) klass; gobject_class->set_property = gst_opus_enc_set_property; From f88f6c18a35eef4a4ee9bb48e35f5f5b92fae6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 27 Nov 2011 23:33:45 +0000 Subject: [PATCH 074/197] Merge remote-tracking branch 'origin/master' into 0.11 From b8431a03a7e1388028b03f01678657bee8b36bc1 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 28 Nov 2011 13:08:27 +0000 Subject: [PATCH 075/197] various: fix pad template ref leaks https://bugzilla.gnome.org/show_bug.cgi?id=662664 --- ext/opus/gstopusdec.c | 8 ++++---- ext/opus/gstopusenc.c | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 89639b12b8..585b38335e 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -103,10 +103,10 @@ gst_opus_dec_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&opus_dec_src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&opus_dec_sink_factory)); + gst_element_class_add_static_pad_template (element_class, + &opus_dec_src_factory); + gst_element_class_add_static_pad_template (element_class, + &opus_dec_sink_factory); gst_element_class_set_details_simple (element_class, "Opus audio decoder", "Codec/Decoder/Audio", "decode opus streams to audio", diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 8ca1158c99..f747a37362 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -204,10 +204,8 @@ gst_opus_enc_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 (&src_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_static_pad_template (element_class, &src_factory); + gst_element_class_add_static_pad_template (element_class, &sink_factory); gst_element_class_set_details_simple (element_class, "Opus audio encoder", "Codec/Encoder/Audio", "Encodes audio in Opus format", From db90df6d028fc975d6080e298c59706a81c3bf25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 28 Nov 2011 23:20:02 +0000 Subject: [PATCH 076/197] Merge commit '26d6add9457f00ce8ec13844368466f0e3816e5d' into 0.11 Conflicts: ext/rtmp/gstrtmpsink.c From 91bf8e5de689763051448cbacfc2afb39d154fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 28 Nov 2011 23:20:58 +0000 Subject: [PATCH 077/197] Merge remote-tracking branch 'origin/master' into 0.11 From 51b02f61fce8f0d61d99561a642cd1bbd76aede2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 28 Nov 2011 19:38:34 +0000 Subject: [PATCH 078/197] opusdec: guard against decoding 0 samples https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 585b38335e..420caf1b5a 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -414,11 +414,11 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_INFO_OBJECT (dec, "Skipping %u samples (%u at 48000 Hz, %u left to skip)", skip, scaled_skip, dec->pre_skip); + } - if (GST_BUFFER_SIZE (outbuf) == 0) { - gst_buffer_unref (outbuf); - outbuf = NULL; - } + if (GST_BUFFER_SIZE (outbuf) == 0) { + gst_buffer_unref (outbuf); + outbuf = NULL; } /* Apply gain */ From f6ebc2de8ec979115419247c86bf323ba4f0f284 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 28 Nov 2011 19:47:34 +0000 Subject: [PATCH 079/197] opusdec: default to stereo 48000 Hz if possible when no headers seen https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 420caf1b5a..af0f0f3771 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -213,12 +213,27 @@ gst_opus_dec_get_r128_volume (gint16 r128_gain) return DB_TO_LINEAR (gst_opus_dec_get_r128_gain (r128_gain)); } +static GstCaps * +gst_opus_dec_negotiate (GstOpusDec * dec) +{ + GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); + GstStructure *s = gst_caps_get_structure (caps, 0); + gst_structure_fixate_field_nearest_int (s, "rate", 48000); + gst_structure_get_int (s, "rate", &dec->sample_rate); + gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); + gst_structure_get_int (s, "channels", &dec->n_channels); + + GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, + dec->sample_rate); + + return caps; +} + static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { const guint8 *data = GST_BUFFER_DATA (buf); GstCaps *caps; - GstStructure *s; const GstAudioChannelPosition *pos = NULL; g_return_val_if_fail (gst_opus_header_is_id_header (buf), GST_FLOW_ERROR); @@ -280,16 +295,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) } } - /* negotiate width with downstream */ - caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); - s = gst_caps_get_structure (caps, 0); - gst_structure_fixate_field_nearest_int (s, "rate", 48000); - gst_structure_get_int (s, "rate", &dec->sample_rate); - gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); - gst_structure_get_int (s, "channels", &dec->n_channels); - - GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, - dec->sample_rate); + caps = gst_opus_dec_negotiate (dec); if (pos) { GST_DEBUG_OBJECT (dec, "Setting channel positions on caps"); @@ -307,7 +313,6 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) return GST_FLOW_OK; } - static GstFlowReturn gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf) { @@ -328,6 +333,24 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GstBuffer *buf; if (dec->state == NULL) { + /* If we did not get any headers, default to 2 channels */ + if (dec->n_channels == 0) { + GstCaps *caps; + GST_INFO_OBJECT (dec, "No header, assuming single stream"); + dec->n_channels = 2; + dec->sample_rate = 48000; + caps = gst_opus_dec_negotiate (dec); + GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); + gst_caps_unref (caps); + /* default stereo mapping */ + dec->channel_mapping_family = 0; + dec->channel_mapping[0] = 0; + dec->channel_mapping[1] = 1; + dec->n_streams = 1; + dec->n_stereo_streams = 1; + } + GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); dec->state = opus_multistream_decoder_create (dec->sample_rate, From b7d53c866b0dc379db62a99c19cd5cca1ca51719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 7 Dec 2011 00:06:11 -0500 Subject: [PATCH 080/197] opusdec: Truncate caps first https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index af0f0f3771..6c57648b8a 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -217,7 +217,12 @@ static GstCaps * gst_opus_dec_negotiate (GstOpusDec * dec) { GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); - GstStructure *s = gst_caps_get_structure (caps, 0); + GstStructure *s; + + caps = gst_caps_make_writable (caps); + gst_caps_truncate (caps); + + s = gst_caps_get_structure (caps, 0); gst_structure_fixate_field_nearest_int (s, "rate", 48000); gst_structure_get_int (s, "rate", &dec->sample_rate); gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); From 16bb45d64ac020758724ea87ddfb80548b3a55af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 7 Dec 2011 00:06:11 -0500 Subject: [PATCH 081/197] opusdec: header cleanup https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusdec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 6c57648b8a..625c477975 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -38,12 +38,11 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #endif #include #include -#include #include "gstopusheader.h" #include "gstopuscommon.h" #include "gstopusdec.h" From f8079057864100db0eb88f88482edc8b7f68a2bd Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 8 Dec 2011 18:45:27 +0000 Subject: [PATCH 082/197] opus: properly create channel mapping tables There are two of them, unintuitively enough; the one passed to the encoder should not be the one that gets written to the file. The former maps the input to an ordering which puts paired channels first, while the latter moves the channels to Vorbis order. So add code to calculate both, and we now have properly paired channels where appropriate. https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopuscommon.c | 18 ++++ ext/opus/gstopuscommon.h | 3 + ext/opus/gstopusdec.c | 13 ++- ext/opus/gstopusenc.c | 207 +++++++++++++++++++++++++++++++-------- ext/opus/gstopusenc.h | 4 +- ext/opus/gstopusheader.c | 17 ++-- ext/opus/gstopusheader.h | 2 +- 7 files changed, 213 insertions(+), 51 deletions(-) diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c index 426c5b8973..dbf585a82f 100644 --- a/ext/opus/gstopuscommon.c +++ b/ext/opus/gstopuscommon.c @@ -17,6 +17,8 @@ * Boston, MA 02111-1307, USA. */ +#include +#include #include "gstopuscommon.h" /* http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 */ @@ -86,3 +88,19 @@ const char *gst_opus_channel_names[] = { "side right", "none" }; + +void +gst_opus_common_log_channel_mapping_table (GstElement * element, + GstDebugCategory * category, const char *msg, int n_channels, + const guint8 * table) +{ + char s[8 + 256 * 4] = "[ "; /* enough for 256 times "255 " at most */ + int n; + + for (n = 0; n < n_channels; ++n) { + size_t len = strlen (s); + snprintf (s + len, sizeof (s) - len, "%d ", table[n]); + } + strcat (s, "]"); + GST_CAT_LEVEL_LOG (category, GST_LEVEL_INFO, element, "%s: %s", msg, s); +} diff --git a/ext/opus/gstopuscommon.h b/ext/opus/gstopuscommon.h index 65b944e9e2..1fba5650d7 100644 --- a/ext/opus/gstopuscommon.h +++ b/ext/opus/gstopuscommon.h @@ -28,6 +28,9 @@ G_BEGIN_DECLS extern const GstAudioChannelPosition gst_opus_channel_positions[][8]; extern const char *gst_opus_channel_names[]; +extern void gst_opus_common_log_channel_mapping_table (GstElement *element, + GstDebugCategory * category, const char *msg, + int n_channels, const guint8 *table); G_END_DECLS diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 625c477975..7776f58122 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -357,9 +357,16 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); - dec->state = opus_multistream_decoder_create (dec->sample_rate, - dec->n_channels, dec->n_streams, dec->n_stereo_streams, - dec->channel_mapping, &err); +#ifndef GST_DISABLE_DEBUG + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (dec), opusdec_debug, + "Mapping table", dec->n_channels, dec->channel_mapping); +#endif + + GST_DEBUG_OBJECT (dec, "%d streams, %d stereo", dec->n_streams, + dec->n_stereo_streams); + dec->state = + opus_multistream_decoder_create (dec->sample_rate, dec->n_channels, + dec->n_streams, dec->n_stereo_streams, dec->channel_mapping, &err); if (!dec->state || err != OPUS_OK) goto creation_failed; } diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f747a37362..e7d6842a12 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -417,7 +417,50 @@ gst_opus_enc_get_frame_samples (GstOpusEnc * enc) } static void -gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) +gst_opus_enc_setup_trivial_mapping (GstOpusEnc * enc, guint8 mapping[256]) +{ + int n; + + for (n = 0; n < 255; ++n) + mapping[n] = n; +} + +static int +gst_opus_enc_find_channel_position (GstOpusEnc * enc, const GstAudioInfo * info, + GstAudioChannelPosition position) +{ + int n; + for (n = 0; n < enc->n_channels; ++n) { + if (GST_AUDIO_INFO_POSITION (info, n) == position) { + return n; + } + } + return -1; +} + +static int +gst_opus_enc_find_channel_position_in_vorbis_order (GstOpusEnc * enc, + GstAudioChannelPosition position) +{ + int c; + + for (c = 0; c < enc->n_channels; ++c) { + if (gst_opus_channel_positions[enc->n_channels - 1][c] == position) { + GST_INFO_OBJECT (enc, + "Channel position %s maps to index %d in Vorbis order", + gst_opus_channel_names[position], c); + return c; + } + } + GST_WARNING_OBJECT (enc, + "Channel position %s is not representable in Vorbis order", + gst_opus_channel_names[position]); + return -1; +} + +static void +gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, + const GstAudioInfo * info) { #define MAPS(idx,pos) (GST_AUDIO_INFO_POSITION (info, (idx)) == GST_AUDIO_CHANNEL_POSITION_##pos) @@ -427,14 +470,15 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) enc->n_channels); /* Start by setting up a default trivial mapping */ - for (n = 0; n < 255; ++n) - enc->channel_mapping[n] = n; + enc->n_stereo_streams = 0; + gst_opus_enc_setup_trivial_mapping (enc, enc->encoding_channel_mapping); + gst_opus_enc_setup_trivial_mapping (enc, enc->decoding_channel_mapping); /* For one channel, use the basic RTP mapping */ if (enc->n_channels == 1) { GST_INFO_OBJECT (enc, "Mono, trivial RTP mapping"); enc->channel_mapping_family = 0; - enc->channel_mapping[0] = 0; + /* implicit mapping for family 0 */ return; } @@ -444,9 +488,11 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) if (MAPS (0, FRONT_LEFT) && MAPS (1, FRONT_RIGHT)) { GST_INFO_OBJECT (enc, "Stereo, canonical mapping"); enc->channel_mapping_family = 0; + enc->n_stereo_streams = 1; /* The channel mapping is implicit for family 0, that's why we do not attempt to create one for right/left - this will be mapped to the Vorbis mapping below. */ + return; } else { GST_DEBUG_OBJECT (enc, "Stereo, but not canonical mapping, continuing"); } @@ -454,42 +500,115 @@ gst_opus_enc_setup_channel_mapping (GstOpusEnc * enc, const GstAudioInfo * info) /* For channels between 1 and 8, we use the Vorbis mapping if we can find a permutation that matches it. Mono will have been taken care - of earlier, but this code also handles it. */ + of earlier, but this code also handles it. Same for left/right stereo. + There are two mappings. One maps the input channels to an ordering + which has the natural pairs first so they can benefit from the Opus + stereo channel coupling, and the other maps this ordering to the + Vorbis ordering. */ if (enc->n_channels >= 1 && enc->n_channels <= 8) { - GST_DEBUG_OBJECT (enc, - "In range for the Vorbis mapping, checking channel positions"); - for (n = 0; n < enc->n_channels; ++n) { - GstAudioChannelPosition pos = GST_AUDIO_INFO_POSITION (info, n); - int c; + int c0, c1, c0v, c1v; + int mapped; + gboolean positions_done[256]; + static const GstAudioChannelPosition pairs[][2] = { + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, + {GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + }; + size_t pair; - GST_DEBUG_OBJECT (enc, "Channel %d has position %d (%s)", n, pos, - gst_opus_channel_names[pos]); - for (c = 0; c < enc->n_channels; ++c) { - if (gst_opus_channel_positions[enc->n_channels - 1][c] == pos) { - GST_DEBUG_OBJECT (enc, "Found in Vorbis mapping as channel %d", c); - break; + GST_DEBUG_OBJECT (enc, + "In range for the Vorbis mapping, building channel mapping tables"); + + enc->n_stereo_streams = 0; + mapped = 0; + for (n = 0; n < 256; ++n) + positions_done[n] = FALSE; + + /* First, find any natural pairs, and move them to the front */ + for (pair = 0; pair < G_N_ELEMENTS (pairs); ++pair) { + GstAudioChannelPosition p0 = pairs[pair][0]; + GstAudioChannelPosition p1 = pairs[pair][1]; + c0 = gst_opus_enc_find_channel_position (enc, info, p0); + c1 = gst_opus_enc_find_channel_position (enc, info, p1); + if (c0 >= 0 && c1 >= 0) { + /* We found a natural pair */ + GST_DEBUG_OBJECT (enc, "Natural pair '%s/%s' found at %d %d", + gst_opus_channel_names[p0], gst_opus_channel_names[p1], c0, c1); + /* Find where they map in Vorbis order */ + c0v = gst_opus_enc_find_channel_position_in_vorbis_order (enc, p0); + c1v = gst_opus_enc_find_channel_position_in_vorbis_order (enc, p1); + if (c0v < 0 || c1v < 0) { + GST_WARNING_OBJECT (enc, + "Cannot map channel positions to Vorbis order, using unknown mapping"); + enc->channel_mapping_family = 255; + enc->n_stereo_streams = 0; + return; } + + enc->encoding_channel_mapping[mapped] = c0; + enc->encoding_channel_mapping[mapped + 1] = c1; + enc->decoding_channel_mapping[c0v] = mapped; + enc->decoding_channel_mapping[c1v] = mapped + 1; + enc->n_stereo_streams++; + mapped += 2; + positions_done[p0] = positions_done[p1] = TRUE; } - if (c == enc->n_channels) { - /* We did not find that position, so use undefined */ - GST_WARNING_OBJECT (enc, - "Position %d (%s) not found in Vorbis mapping, using unknown mapping", - pos, gst_opus_channel_positions[pos]); - enc->channel_mapping_family = 255; - return; - } - GST_DEBUG_OBJECT (enc, "Mapping output channel %d to %d (%s)", c, n, - gst_opus_channel_names[pos]); - enc->channel_mapping[c] = n; } - GST_INFO_OBJECT (enc, "Permutation found, using Vorbis mapping"); + + /* Now add all other input channels as mono streams */ + for (n = 0; n < enc->n_channels; ++n) { + GstAudioChannelPosition position = GST_AUDIO_INFO_POSITION (info, n); + + /* if we already mapped it while searching for pairs, nothing else + needs to be done */ + if (!positions_done[position]) { + int cv; + GST_DEBUG_OBJECT (enc, "Channel position %s is not mapped yet, adding", + gst_opus_channel_names[position]); + cv = gst_opus_enc_find_channel_position_in_vorbis_order (enc, position); + if (cv < 0) { + GST_WARNING_OBJECT (enc, + "Cannot map channel positions to Vorbis order, using unknown mapping"); + enc->channel_mapping_family = 255; + enc->n_stereo_streams = 0; + return; + } + enc->encoding_channel_mapping[mapped] = n; + enc->decoding_channel_mapping[cv] = mapped; + mapped++; + } + } + +#ifndef GST_DISABLE_DEBUG + GST_INFO_OBJECT (enc, + "Mapping tables built: %d channels, %d stereo streams", enc->n_channels, + enc->n_stereo_streams); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Encoding mapping table", enc->n_channels, + enc->encoding_channel_mapping); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Decoding mapping table", enc->n_channels, + enc->decoding_channel_mapping); +#endif + enc->channel_mapping_family = 1; return; } - /* For other cases, we use undefined, with the default trivial mapping */ + /* More than 8 channels, if future mappings are added for those */ + + /* For other cases, we use undefined, with the default trivial mapping + and all mono streams */ GST_WARNING_OBJECT (enc, "Unknown mapping"); enc->channel_mapping_family = 255; + enc->n_stereo_streams = 0; #undef MAPS } @@ -505,7 +624,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); enc->sample_rate = GST_AUDIO_INFO_RATE (info); - gst_opus_enc_setup_channel_mapping (enc, info); + gst_opus_enc_setup_channel_mappings (enc, info); GST_DEBUG_OBJECT (benc, "Setup with %d channels, %d Hz", enc->n_channels, enc->sample_rate); @@ -530,17 +649,24 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { - int error = OPUS_OK, n; - guint8 trivial_mapping[256]; + int error = OPUS_OK; - GST_DEBUG_OBJECT (enc, "setup"); +#ifndef GST_DISABLE_DEBUG + GST_DEBUG_OBJECT (enc, + "setup: %d Hz, %d channels, %d stereo streams, family %d", + enc->sample_rate, enc->n_channels, enc->n_stereo_streams, + enc->channel_mapping_family); + GST_INFO_OBJECT (enc, "Mapping tables built: %d channels, %d stereo streams", + enc->n_channels, enc->n_stereo_streams); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Encoding mapping table", enc->n_channels, enc->encoding_channel_mapping); + gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, + "Decoding mapping table", enc->n_channels, enc->decoding_channel_mapping); +#endif - for (n = 0; n < 256; ++n) - trivial_mapping[n] = n; - - enc->state = - opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, - enc->n_channels, 0, trivial_mapping, + enc->state = opus_multistream_encoder_create (enc->sample_rate, + enc->n_channels, enc->n_channels - enc->n_stereo_streams, + enc->n_stereo_streams, enc->encoding_channel_mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) @@ -698,7 +824,8 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) enc->headers = NULL; gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, - enc->sample_rate, enc->channel_mapping_family, enc->channel_mapping, + enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, + enc->decoding_channel_mapping, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 8c2c3c6e82..1e39ad03d4 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -79,7 +79,9 @@ struct _GstOpusEnc { GstTagList *tags; guint8 channel_mapping_family; - guint8 channel_mapping[256]; + guint8 encoding_channel_mapping[256]; + guint8 decoding_channel_mapping[256]; + guint8 n_stereo_streams; }; struct _GstOpusEncClass { diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 42df7b35fb..7f49e47113 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -27,12 +27,17 @@ #include "gstopusheader.h" static GstBuffer * -gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, - guint8 channel_mapping_family, const guint8 * channel_mapping) +gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, + gint sample_rate, guint8 channel_mapping_family, + const guint8 * channel_mapping) { GstBuffer *buffer; GstByteWriter bw; + g_return_val_if_fail (nchannels > 0 && nchannels < 256, NULL); + g_return_val_if_fail (n_stereo_streams >= 0, NULL); + g_return_val_if_fail (n_stereo_streams <= nchannels - n_stereo_streams, NULL); + gst_byte_writer_init (&bw); /* See http://wiki.xiph.org/OggOpus */ @@ -44,8 +49,8 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint sample_rate, gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ gst_byte_writer_put_uint8 (&bw, channel_mapping_family); if (channel_mapping_family > 0) { - gst_byte_writer_put_uint8 (&bw, nchannels); - gst_byte_writer_put_uint8 (&bw, 0); + gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); + gst_byte_writer_put_uint8 (&bw, n_stereo_streams); gst_byte_writer_put_data (&bw, channel_mapping, nchannels); } @@ -158,7 +163,7 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, void gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, - gint sample_rate, guint8 channel_mapping_family, + gint n_stereo_streams, gint sample_rate, guint8 channel_mapping_family, const guint8 * channel_mapping, const GstTagList * tags) { GstBuffer *buf1, *buf2; @@ -175,7 +180,7 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, /* create header buffers */ buf1 = - gst_opus_enc_create_id_buffer (nchannels, sample_rate, + gst_opus_enc_create_id_buffer (nchannels, n_stereo_streams, sample_rate, channel_mapping_family, channel_mapping); buf2 = gst_opus_enc_create_metadata_buffer (tags); diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 3b2cfc265f..c6278eff30 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -28,7 +28,7 @@ G_BEGIN_DECLS extern void gst_opus_header_create_caps_from_headers (GstCaps **caps, GSList **headers, GstBuffer *id_header, GstBuffer *comment_header); extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, - gint nchannels, gint sample_rate, + gint nchannels, gint n_stereo_streams, gint sample_rate, guint8 channel_mapping_family, const guint8 *channel_mapping, const GstTagList *tags); extern gboolean gst_opus_header_is_header (GstBuffer * buf, From dbca14b23b3a933f3bacec4d6e9d7bb3f151144d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 8 Dec 2011 19:47:55 +0000 Subject: [PATCH 083/197] opus: include streams count in caps https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusenc.c | 2 +- ext/opus/gstopusheader.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index e7d6842a12..502f4a9d94 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -126,7 +126,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-opus") + GST_STATIC_CAPS ("audio/x-opus, streams = (int) [1, 255 ]") ); #define DEFAULT_AUDIO TRUE diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 7f49e47113..185c85ef18 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -150,11 +150,26 @@ void gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, GstBuffer * buf1, GstBuffer * buf2) { + int n_streams, family; + g_return_if_fail (caps); g_return_if_fail (headers && !*headers); + g_return_if_fail (GST_BUFFER_SIZE (buf1) >= 19); + + /* work out the number of streams */ + family = GST_BUFFER_DATA (buf1)[18]; + if (family == 0) { + n_streams = 1; + } else { + /* only included in the header for family > 0 */ + g_return_if_fail (GST_BUFFER_SIZE (buf1) >= 20); + n_streams = GST_BUFFER_DATA (buf1)[19]; + } /* mark and put on caps */ - *caps = gst_caps_from_string ("audio/x-opus"); + *caps = + gst_caps_new_simple ("audio/x-opus", "streams", G_TYPE_INT, n_streams, + NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); *headers = g_slist_prepend (*headers, buf2); From 3a978f5a69728119b633d246f91cb1eaaf16b4f4 Mon Sep 17 00:00:00 2001 From: Danilo Cesar Lemes de Paula Date: Wed, 7 Dec 2011 15:13:11 -0200 Subject: [PATCH 084/197] Adding opus RTP payloader/depayloader element Adding OPUS RTP module based on the current draft: http://tools.ietf.org/id/draft-spittka-payload-rtp-opus-00.txt https://bugzilla.gnome.org/show_bug.cgi?id=664817 --- ext/opus/Makefile.am | 5 +++-- ext/opus/gstopus.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index cb0a9b338a..cdf3c30ac1 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstopus.la -libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c gstopuscommon.c +libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c gstopuscommon.c gstrtpopuspay.c gstrtpopusdepay.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -9,10 +9,11 @@ libgstopus_la_CFLAGS = \ libgstopus_la_LIBADD = \ -lgstaudio-$(GST_MAJORMINOR) \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ + -lgstrtp-@GST_MAJORMINOR@ \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(OPUS_LIBS) libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) libgstopus_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h gstopuscommon.h +noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h gstopuscommon.h gstrtpopuspay.h gstrtpopusdepay.h diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index c5f68a131c..8db6e197f9 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -25,6 +25,9 @@ #include "gstopusenc.h" #include "gstopusparse.h" +#include "gstrtpopuspay.h" +#include "gstrtpopusdepay.h" + #include static gboolean @@ -43,6 +46,14 @@ plugin_init (GstPlugin * plugin) GST_TYPE_OPUS_PARSE)) return FALSE; + if (!gst_element_register (plugin, "rtpopusdepay", GST_RANK_NONE, + GST_TYPE_RTP_OPUS_DEPAY)) + return FALSE; + + if (!gst_element_register (plugin, "rtpopuspay", GST_RANK_NONE, + GST_TYPE_RTP_OPUS_PAY)) + return FALSE; + gst_tag_register_musicbrainz_tags (); return TRUE; From 3f47a43f2a0f65db98838eff67ff68affd068a02 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 9 Dec 2011 17:25:41 +0000 Subject: [PATCH 085/197] opusenc: add upstream negotiation for multistream ability This will help elements that cannot deal with multistream, such as the RTP payloader. The caps now do not include a "streams" field anymore, but a "multistream" boolean, since we have no real use for knowing the exact amount of streams. https://bugzilla.gnome.org/show_bug.cgi?id=665078 --- ext/opus/gstopusenc.c | 66 +++++++++++++++++++++++++++++++++++++++- ext/opus/gstopusheader.c | 7 +++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 502f4a9d94..0ccbbebc5f 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -126,7 +126,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-opus, streams = (int) [1, 255 ]") + GST_STATIC_CAPS ("audio/x-opus") ); #define DEFAULT_AUDIO TRUE @@ -161,6 +161,7 @@ static void gst_opus_enc_finalize (GObject * object); static gboolean gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event); +static GstCaps *gst_opus_enc_sink_getcaps (GstAudioEncoder * benc); static gboolean gst_opus_enc_setup (GstOpusEnc * enc); static void gst_opus_enc_get_property (GObject * object, guint prop_id, @@ -229,6 +230,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) base_class->set_format = GST_DEBUG_FUNCPTR (gst_opus_enc_set_format); base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_enc_handle_frame); base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); + base_class->getcaps = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps); g_object_class_install_property (gobject_class, PROP_AUDIO, g_param_spec_boolean ("audio", "Audio or voice", @@ -721,6 +723,68 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) return FALSE; } +static GstCaps * +gst_opus_enc_sink_getcaps (GstAudioEncoder * benc) +{ + GstOpusEnc *enc; + GstCaps *caps; + GstCaps *peercaps = NULL; + GstCaps *intersect = NULL; + guint i; + gboolean allow_multistream; + + enc = GST_OPUS_ENC (benc); + + GST_DEBUG_OBJECT (enc, "sink getcaps"); + + peercaps = gst_pad_peer_get_caps (GST_AUDIO_ENCODER_SRC_PAD (benc)); + if (!peercaps) { + GST_DEBUG_OBJECT (benc, "No peercaps, returning template sink caps"); + return + gst_caps_copy (gst_pad_get_pad_template_caps + (GST_AUDIO_ENCODER_SINK_PAD (benc))); + } + + intersect = gst_caps_intersect (peercaps, + gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc))); + gst_caps_unref (peercaps); + + if (gst_caps_is_empty (intersect)) + return intersect; + + allow_multistream = FALSE; + for (i = 0; i < gst_caps_get_size (intersect); i++) { + GstStructure *s = gst_caps_get_structure (intersect, i); + gboolean multistream; + if (gst_structure_get_boolean (s, "multistream", &multistream)) { + if (multistream) { + allow_multistream = TRUE; + } + } else { + allow_multistream = TRUE; + } + } + + gst_caps_unref (intersect); + + caps = + gst_caps_copy (gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD + (benc))); + if (!allow_multistream) { + GValue range = { 0 }; + g_value_init (&range, GST_TYPE_INT_RANGE); + gst_value_set_int_range (&range, 1, 2); + for (i = 0; i < gst_caps_get_size (caps); i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + gst_structure_set_value (s, "channels", &range); + } + g_value_unset (&range); + } + + GST_DEBUG_OBJECT (enc, "Returning caps: %" GST_PTR_FORMAT, caps); + return caps; +} + static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 185c85ef18..d3d631fe74 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -151,6 +151,7 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, GstBuffer * buf1, GstBuffer * buf2) { int n_streams, family; + gboolean multistream; g_return_if_fail (caps); g_return_if_fail (headers && !*headers); @@ -167,9 +168,9 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, } /* mark and put on caps */ - *caps = - gst_caps_new_simple ("audio/x-opus", "streams", G_TYPE_INT, n_streams, - NULL); + multistream = n_streams > 1; + *caps = gst_caps_new_simple ("audio/x-opus", + "multistream", G_TYPE_BOOLEAN, multistream, NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); *headers = g_slist_prepend (*headers, buf2); From b20879ea9e280f739d102d559a7a143a2c04b17f Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 15 Dec 2011 16:42:20 +0000 Subject: [PATCH 086/197] opus: fix bad merge (stray unmap, undeclared var) --- ext/opus/gstopusenc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 72af29a97b..ff9243ad4c 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -188,9 +188,11 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) { GObjectClass *gobject_class; GstAudioEncoderClass *base_class; + GstElementClass *gstelement_class; gobject_class = (GObjectClass *) klass; base_class = (GstAudioEncoderClass *) klass; + gstelement_class = (GstElementClass *) klass; gobject_class->set_property = gst_opus_enc_set_property; gobject_class->get_property = gst_opus_enc_get_property; From dd54722176560d8c4bfad980973e48728090b968 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Fri, 30 Dec 2011 11:49:27 +0100 Subject: [PATCH 087/197] Merge remote-tracking branch 'origin/master' into 0.11 Conflicts: tests/examples/camerabin2/Makefile.am From 2ca8f88ecc00824baed1c6a5e3ca74b305528edc Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 10 Jan 2012 13:38:42 +0000 Subject: [PATCH 088/197] opusenc: fix caps leak --- ext/opus/gstopusenc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 0ccbbebc5f..016b891c81 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -897,6 +897,7 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps); + gst_caps_unref (caps); enc->header_sent = TRUE; } From d6b60c62e68169b6e02809f9ef8fc1dde3d28520 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 10 Jan 2012 13:38:50 +0000 Subject: [PATCH 089/197] opusenc: fix slist leak --- ext/opus/gstopusenc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 016b891c81..9b475cf8c7 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -360,6 +360,7 @@ gst_opus_enc_stop (GstAudioEncoder * benc) gst_tag_list_free (enc->tags); enc->tags = NULL; g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + g_slist_free (enc->headers); enc->headers = NULL; gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); @@ -885,6 +886,7 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) GstCaps *caps; g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); + g_slist_free (enc->headers); enc->headers = NULL; gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, From 3cb98f2674e6d763d9069f0ad1086e36cf883d33 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 11 Jan 2012 13:32:36 +0000 Subject: [PATCH 090/197] tests: fix buffer leaks in opus tests --- tests/check/elements/opus.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index 66c12a3237..d18f6230fe 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -110,7 +110,7 @@ cleanup_opusenc (GstElement * opusenc) } static void -check_buffers (guint expected, gboolean headers_in_caps) +check_buffers (guint expected) { GstBuffer *outbuffer; guint i, num_buffers; @@ -152,7 +152,7 @@ GST_START_TEST (test_opus_id_header) /* ... and nothing ends up on the global buffer list */ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); gst_buffer_unref (inbuffer); - fail_unless (g_list_length (buffers) == 0); + check_buffers (0); /* cleanup */ cleanup_opusdec (opusdec); @@ -248,7 +248,7 @@ GST_START_TEST (test_opus_encode_samples) "could not set to ready"); /* default frame size is 20 ms, at 48000 Hz that's 960 samples */ - check_buffers ((nsamples + 959) / 960, FALSE); + check_buffers ((nsamples + 959) / 960); /* cleanup */ cleanup_opusenc (opusenc); @@ -331,6 +331,8 @@ GST_START_TEST (test_opus_encode_properties) /* change random parameters */ g_object_set (opusenc, param_changes[step].param, param_changes[step].value, NULL); + + check_buffers (1); } gst_caps_unref (caps); From ba014b1ae01fcfcf6b0df56ac2d33d52d912f435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 25 Jan 2012 13:22:43 +0100 Subject: [PATCH 091/197] Merge branch 'master' into 0.11 Conflicts: configure.ac ext/kate/gstkateenc.c gst/colorspace/colorspace.c gst/mpegvideoparse/mpegvideoparse.c From fdb5c79b8b505ee6c681f52f44cbb477d861b48a Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 27 Jan 2012 14:49:58 +0000 Subject: [PATCH 092/197] plenty: fixup glib deprecations --- ext/opus/Makefile.am | 1 + ext/opus/gstopusenc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index cdf3c30ac1..1aaed79247 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -4,6 +4,7 @@ libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstop libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_PLUGINS_BAD_CFLAGS) \ $(GST_CFLAGS) \ $(OPUS_CFLAGS) libgstopus_la_LIBADD = \ diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 9b475cf8c7..29b254ee23 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -48,6 +48,7 @@ #include #include +#include #include "gstopusheader.h" #include "gstopuscommon.h" #include "gstopusenc.h" From 3ecfd258082554def80227ae01038d2ecbc3797b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 3 Feb 2012 00:50:33 +0000 Subject: [PATCH 093/197] build: fix CFLAGS order and LIBS order _BAD_CFLAGS should always come first, then GST_PLUGINS_BASE_CFLAGS, then GST_BASE_CFLAGS then GST_CFLAGS. Same for libs: first plugins base libs, then GST_BASE_LIB then GST_LIBS. --- ext/opus/Makefile.am | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 1aaed79247..48a7eade40 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -3,14 +3,13 @@ plugin_LTLIBRARIES = libgstopus.la libgstopus_la_SOURCES = gstopus.c gstopusdec.c gstopusenc.c gstopusparse.c gstopusheader.c gstopuscommon.c gstrtpopuspay.c gstrtpopusdepay.c libgstopus_la_CFLAGS = \ -DGST_USE_UNSTABLE_API \ - $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_CFLAGS) \ $(OPUS_CFLAGS) libgstopus_la_LIBADD = \ - -lgstaudio-$(GST_MAJORMINOR) \ - $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ - -lgstrtp-@GST_MAJORMINOR@ \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \ + -lgsttag-$(GST_MAJORMINOR) -lgstrtp-$(GST_MAJORMINOR) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(OPUS_LIBS) From 3bf276d4622e6fe28aaff6125138f9b56dfa86ca Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 14 Feb 2012 11:19:04 +0100 Subject: [PATCH 094/197] Merge branch 'master' into 0.11 From eface67e001353f6b6ae94bdd71d396dbeb99dc7 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 15 Feb 2012 17:14:34 +0100 Subject: [PATCH 095/197] Merge branch 'master' into 0.11 From 3625d8367a054d52e28fc706f29cf40a5ca5cbd7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 16 Feb 2012 14:33:20 +0100 Subject: [PATCH 096/197] Merge branch 'master' into 0.11 Conflicts: gst/mpegtsdemux/mpegtsbase.c gst/mpegtsdemux/mpegtspacketizer.c gst/mpegtsdemux/tsdemux.c gst/mve/gstmvedemux.c From e45e7a3d1ff68b052f7e3fc97aac51e699b953c5 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 17 Feb 2012 09:01:56 +0100 Subject: [PATCH 097/197] Merge branch 'master' into 0.11 From 0dbfad52d9311622985f4b7dd947e69376fc8b36 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 20 Feb 2012 16:07:50 +0100 Subject: [PATCH 098/197] Merge branch 'master' into 0.11 Conflicts: ext/opus/gstopusparse.c gst/colorspace/colorspace.c From a2bb20e522268780340c445f781b2b0c89527e2f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 21 Feb 2012 10:06:16 +0100 Subject: [PATCH 099/197] Merge branch 'master' into 0.11 Conflicts: gst/colorspace/colorspace.c From ea3f41e06ac2cd8ec60d615270a8bab6160a9c4d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Feb 2012 13:13:14 +0100 Subject: [PATCH 100/197] audioencoders: chain up to parent event handler --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 167d195ffc..c7053cdcc8 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -708,7 +708,7 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) break; } - return FALSE; + return GST_AUDIO_ENCODER_CLASS (parent_class)->event (benc, event); } static GstCaps * From 11f2207d1f1062b55809747c5d2cda70adcb1fe0 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 7 Mar 2012 12:55:43 +0100 Subject: [PATCH 101/197] opusenc: configure baseclass requested samples really in samples ... as opposed to bytes. --- ext/opus/gstopusenc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 29b254ee23..a87e08b3f7 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -382,10 +382,8 @@ gst_opus_enc_setup_base_class (GstOpusEnc * enc, GstAudioEncoder * benc) { gst_audio_encoder_set_latency (benc, gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc)); - gst_audio_encoder_set_frame_samples_min (benc, - enc->frame_samples * enc->n_channels * 2); - gst_audio_encoder_set_frame_samples_max (benc, - enc->frame_samples * enc->n_channels * 2); + gst_audio_encoder_set_frame_samples_min (benc, enc->frame_samples); + gst_audio_encoder_set_frame_samples_max (benc, enc->frame_samples); gst_audio_encoder_set_frame_max (benc, 0); } From 74256b69af2afcd236900f0ed3acee6f1f6cd1a7 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 7 Mar 2012 12:59:28 +0100 Subject: [PATCH 102/197] opusenc: only request and process 1 frame at a time ... since it is specified in _finish_frame that input buffer may be invalidated after calling it, and is as such not reliably available for further encoding. Also, requesting or allowing several frames is only useful if subclass intends to process these "in 1 run" (as in, 1 output buffer), not for having another (inner) loop in subclass where the baseclass one will do just fine. --- ext/opus/gstopusenc.c | 72 +++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index a87e08b3f7..6a19ffd89f 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -384,7 +384,7 @@ gst_opus_enc_setup_base_class (GstOpusEnc * enc, GstAudioEncoder * benc) gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (enc)); gst_audio_encoder_set_frame_samples_min (benc, enc->frame_samples); gst_audio_encoder_set_frame_samples_max (benc, enc->frame_samples); - gst_audio_encoder_set_frame_max (benc, 0); + gst_audio_encoder_set_frame_max (benc, 1); } static gint @@ -792,6 +792,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) gsize bsize, size; gsize bytes; gint ret = GST_FLOW_OK; + gint outsize; + GstBuffer *outbuf; g_mutex_lock (enc->property_lock); @@ -816,52 +818,42 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) goto done; } + g_assert (size == bytes); - while (size) { - gint outsize; - GstBuffer *outbuf; + ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), + GST_BUFFER_OFFSET_NONE, enc->max_payload_size * enc->n_channels, + GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf); - ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), - GST_BUFFER_OFFSET_NONE, enc->max_payload_size * enc->n_channels, - GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf); + if (GST_FLOW_OK != ret) + goto done; - if (GST_FLOW_OK != ret) - goto done; + GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", + enc->frame_samples, (int) bytes); - GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - enc->frame_samples, (int) bytes); + outsize = + opus_multistream_encode (enc->state, (const gint16 *) data, + enc->frame_samples, GST_BUFFER_DATA (outbuf), + enc->max_payload_size * enc->n_channels); - outsize = - opus_multistream_encode (enc->state, (const gint16 *) data, - enc->frame_samples, GST_BUFFER_DATA (outbuf), - enc->max_payload_size * enc->n_channels); - - if (outsize < 0) { - GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); - ret = GST_FLOW_ERROR; - goto done; - } else if (outsize > enc->max_payload_size) { - GST_WARNING_OBJECT (enc, - "Encoded size %d is higher than max payload size (%d bytes)", - outsize, enc->max_payload_size); - ret = GST_FLOW_ERROR; - goto done; - } - - GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); - GST_BUFFER_SIZE (outbuf) = outsize; - - ret = - gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, - enc->frame_samples); - - if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) - goto done; - - data += bytes; - size -= bytes; + if (outsize < 0) { + GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); + ret = GST_FLOW_ERROR; + goto done; + } else if (outsize > enc->max_payload_size) { + GST_WARNING_OBJECT (enc, + "Encoded size %d is higher than max payload size (%d bytes)", + outsize, enc->max_payload_size); + ret = GST_FLOW_ERROR; + goto done; } + GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); + GST_BUFFER_SIZE (outbuf) = outsize; + + ret = + gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, + enc->frame_samples); + done: g_mutex_unlock (enc->property_lock); From fa02111b7c1506c8ac67b04c64e552b74be3eea8 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 7 Mar 2012 17:14:29 +0100 Subject: [PATCH 103/197] opus: port to updated 0.11 --- ext/opus/gstopuscommon.c | 8 +-- ext/opus/gstopuscommon.h | 2 +- ext/opus/gstopusdec.c | 102 ++++++++++++++++++++++----------------- ext/opus/gstopusdec.h | 3 ++ ext/opus/gstopusenc.c | 25 +++++----- ext/opus/gstopusheader.c | 22 +++++---- 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c index dbf585a82f..80414ad05f 100644 --- a/ext/opus/gstopuscommon.c +++ b/ext/opus/gstopuscommon.c @@ -25,7 +25,7 @@ /* copy of the same structure in the vorbis plugin */ const GstAudioChannelPosition gst_opus_channel_positions[][8] = { { /* Mono */ - GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}, + GST_AUDIO_CHANNEL_POSITION_MONO}, { /* Stereo */ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, @@ -52,7 +52,7 @@ const GstAudioChannelPosition gst_opus_channel_positions[][8] = { GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_LFE, + GST_AUDIO_CHANNEL_POSITION_LFE1, }, { /* 6.1 Surround, in Vorbis spec since 2010-01-13 */ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, @@ -61,7 +61,7 @@ const GstAudioChannelPosition gst_opus_channel_positions[][8] = { GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE}, + GST_AUDIO_CHANNEL_POSITION_LFE1}, { /* 7.1 Surround, in Vorbis spec since 2010-01-13 */ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, @@ -70,7 +70,7 @@ const GstAudioChannelPosition gst_opus_channel_positions[][8] = { GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_LFE}, + GST_AUDIO_CHANNEL_POSITION_LFE1}, }; const char *gst_opus_channel_names[] = { diff --git a/ext/opus/gstopuscommon.h b/ext/opus/gstopuscommon.h index 1fba5650d7..21c2733fff 100644 --- a/ext/opus/gstopuscommon.h +++ b/ext/opus/gstopuscommon.h @@ -22,7 +22,7 @@ #define __GST_OPUS_COMMON_H__ #include -#include +#include G_BEGIN_DECLS diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 67662456e4..5c9b363e9c 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -56,6 +56,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " "format = (string) { " GST_AUDIO_NE (S16) " }, " + "layout = (string) interleaved, " "rate = (int) { 48000, 24000, 16000, 12000, 8000 }, " "channels = (int) [ 1, 8 ] ") ); @@ -206,11 +207,12 @@ gst_opus_dec_get_r128_volume (gint16 r128_gain) return DB_TO_LINEAR (gst_opus_dec_get_r128_gain (r128_gain)); } -static GstCaps * -gst_opus_dec_negotiate (GstOpusDec * dec) +static void +gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) { GstCaps *caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dec)); GstStructure *s; + GstAudioInfo info; caps = gst_caps_make_writable (caps); gst_caps_truncate (caps); @@ -224,19 +226,41 @@ gst_opus_dec_negotiate (GstOpusDec * dec) GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, dec->sample_rate); - return caps; + /* pass valid order to audio info */ + if (pos) { + memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); + gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); + } + + /* set up source format */ + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16, + dec->sample_rate, dec->n_channels, pos ? dec->opus_pos : NULL); + gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (dec), &info); + + /* but we still need the opus order for later reordering */ + if (pos) { + memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); + gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); + } else { + dec->opus_pos[0] = GST_AUDIO_CHANNEL_POSITION_INVALID; + } + + dec->info = info; } static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { const guint8 *data; - GstCaps *caps; - const GstAudioChannelPosition *pos = NULL; + GstAudioChannelPosition pos[64]; + const GstAudioChannelPosition *posn = NULL; + GstMapInfo map; g_return_val_if_fail (gst_opus_header_is_id_header (buf), GST_FLOW_ERROR); - data = gst_buffer_map (buf, NULL, NULL, GST_MAP_READ); + gst_buffer_map (buf, &map, GST_MAP_READ); + data = map.data; g_return_val_if_fail (dec->n_channels == 0 || dec->n_channels == data[9], GST_FLOW_ERROR); @@ -274,20 +298,18 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) case 6: case 7: case 8: - pos = gst_opus_channel_positions[dec->n_channels - 1]; + posn = gst_opus_channel_positions[dec->n_channels - 1]; break; default:{ gint i; - GstAudioChannelPosition *posn = - g_new (GstAudioChannelPosition, dec->n_channels); GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, (NULL), ("Using NONE channel layout for more than 8 channels")); for (i = 0; i < dec->n_channels; i++) - posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + pos[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - pos = posn; + posn = pos; } } } else { @@ -296,22 +318,9 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) } } - caps = gst_opus_dec_negotiate (dec); + gst_opus_dec_negotiate (dec, posn); - if (pos) { - GST_DEBUG_OBJECT (dec, "Setting channel positions on caps"); - gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); - } - - if (dec->n_channels > 8) { - g_free ((GstAudioChannelPosition *) pos); - } - - GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); - gst_caps_unref (caps); - - gst_buffer_unmap (buf, (guint8 *) data, -1); + gst_buffer_unmap (buf, &map); return GST_FLOW_OK; } @@ -327,7 +336,7 @@ static GstFlowReturn opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) { GstFlowReturn res = GST_FLOW_OK; - gsize size, out_size; + gsize size; guint8 *data; GstBuffer *outbuf; gint16 *out_data; @@ -335,24 +344,22 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) int samples; unsigned int packet_size; GstBuffer *buf; + GstMapInfo map, omap; if (dec->state == NULL) { /* If we did not get any headers, default to 2 channels */ if (dec->n_channels == 0) { - GstCaps *caps; GST_INFO_OBJECT (dec, "No header, assuming single stream"); dec->n_channels = 2; dec->sample_rate = 48000; - caps = gst_opus_dec_negotiate (dec); - GST_INFO_OBJECT (dec, "Setting src caps to %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); - gst_caps_unref (caps); /* default stereo mapping */ dec->channel_mapping_family = 0; dec->channel_mapping[0] = 0; dec->channel_mapping[1] = 1; dec->n_streams = 1; dec->n_stereo_streams = 1; + + gst_opus_dec_negotiate (dec, NULL); } GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", @@ -389,7 +396,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) buf = dec->use_inband_fec && dec->last_buffer ? dec->last_buffer : buffer; if (buf) { - data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ); + gst_buffer_map (buf, &map, GST_MAP_READ); + data = map.data; + size = map.size; GST_DEBUG_OBJECT (dec, "Using buffer of size %u", size); } else { @@ -409,7 +418,8 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) goto buffer_failed; } - out_data = (gint16 *) gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); + gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); + out_data = (gint16 *) omap.data; if (dec->use_inband_fec) { if (dec->last_buffer) { @@ -425,8 +435,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) /* normal decode */ n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } - gst_buffer_unmap (buf, data, size); - gst_buffer_unmap (outbuf, out_data, out_size); + gst_buffer_unmap (outbuf, &omap); + if (buf) + gst_buffer_unmap (buf, &map); if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); @@ -451,6 +462,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) if (gst_buffer_get_size (outbuf) == 0) { gst_buffer_unref (outbuf); outbuf = NULL; + } else if (dec->opus_pos[0] != GST_AUDIO_CHANNEL_POSITION_INVALID) { + gst_audio_buffer_reorder_channels (outbuf, GST_AUDIO_FORMAT_S16, + dec->n_channels, dec->opus_pos, dec->info.position); } /* Apply gain */ @@ -463,16 +477,18 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) gsize rsize; unsigned int i, nsamples; double volume = dec->r128_gain_volume; - gint16 *samples = - (gint16 *) gst_buffer_map (outbuf, &rsize, NULL, GST_MAP_READWRITE); + gint16 *samples; + gst_buffer_map (outbuf, &omap, GST_MAP_READWRITE); + samples = (gint16 *) omap.data; + rsize = omap.size; GST_DEBUG_OBJECT (dec, "Applying gain: volume %f", volume); nsamples = rsize / 2; for (i = 0; i < nsamples; ++i) { int sample = (int) (samples[i] * volume + 0.5); samples[i] = sample < -32768 ? -32768 : sample > 32767 ? 32767 : sample; } - gst_buffer_unmap (outbuf, samples, rsize); + gst_buffer_unmap (outbuf, &omap); } res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); @@ -542,8 +558,8 @@ static gboolean memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) { gsize size1, size2; - gpointer data1; gboolean res; + GstMapInfo map; size1 = gst_buffer_get_size (buf1); size2 = gst_buffer_get_size (buf2); @@ -551,9 +567,9 @@ memcmp_buffers (GstBuffer * buf1, GstBuffer * buf2) if (size1 != size2) return FALSE; - data1 = gst_buffer_map (buf1, NULL, NULL, GST_MAP_READ); - res = gst_buffer_memcmp (buf2, 0, data1, size1) == 0; - gst_buffer_unmap (buf1, data1, size1); + gst_buffer_map (buf1, &map, GST_MAP_READ); + res = gst_buffer_memcmp (buf2, 0, map.data, map.size) == 0; + gst_buffer_unmap (buf1, &map); return res; } diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 3ccfa26969..0bea2c081a 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -56,6 +56,9 @@ struct _GstOpusDec { guint32 pre_skip; gint16 r128_gain; + GstAudioChannelPosition opus_pos[64]; + GstAudioInfo info; + guint8 n_streams; guint8 n_stereo_streams; guint8 channel_mapping_family; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index c7053cdcc8..9c10d1a4b4 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -120,6 +120,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " "format = (string) " FORMAT_STR ", " + "layout = (string) interleaved, " "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " "channels = (int) [ 1, 2 ] ") ); @@ -725,7 +726,7 @@ gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) GST_DEBUG_OBJECT (enc, "sink getcaps"); - peercaps = gst_pad_peer_query_caps (GST_AUDIO_ENCODER_SRC_PAD (benc), filter); + peercaps = gst_pad_peer_query_caps (GST_AUDIO_ENCODER_SRC_PAD (benc), NULL); if (!peercaps) { GST_DEBUG_OBJECT (benc, "No peercaps, returning template sink caps"); return @@ -787,11 +788,14 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) gsize bsize, size; gsize bytes = enc->frame_samples * enc->n_channels * 2; gint ret = GST_FLOW_OK; + GstMapInfo map; g_mutex_lock (enc->property_lock); if (G_LIKELY (buf)) { - bdata = gst_buffer_map (buf, &bsize, NULL, GST_MAP_READ); + gst_buffer_map (buf, &map, GST_MAP_READ); + bdata = map.data; + bsize = map.size; if (G_UNLIKELY (bsize % bytes)) { GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); @@ -799,8 +803,6 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); memcpy (mdata, bdata, bsize); - gst_buffer_unmap (buf, bdata, bsize); - bdata = NULL; data = mdata; } else { data = bdata; @@ -813,9 +815,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) while (size) { gint encoded_size; - unsigned char *out_data; - gsize out_size; GstBuffer *outbuf; + GstMapInfo omap; outbuf = gst_buffer_new_and_alloc (enc->max_payload_size * enc->n_channels); if (!outbuf) @@ -824,11 +825,11 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", enc->frame_samples, (int) bytes); - out_data = gst_buffer_map (outbuf, &out_size, NULL, GST_MAP_WRITE); + gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); encoded_size = opus_multistream_encode (enc->state, (const gint16 *) data, - enc->frame_samples, out_data, enc->max_payload_size * enc->n_channels); - gst_buffer_unmap (outbuf, out_data, out_size); + enc->frame_samples, omap.data, enc->max_payload_size * enc->n_channels); + gst_buffer_unmap (outbuf, &omap); if (encoded_size < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", encoded_size); @@ -837,7 +838,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) } else if (encoded_size > enc->max_payload_size) { GST_WARNING_OBJECT (enc, "Encoded size %d is higher than max payload size (%d bytes)", - out_size, enc->max_payload_size); + encoded_size, enc->max_payload_size); ret = GST_FLOW_ERROR; goto done; } @@ -859,7 +860,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) done: if (bdata) - gst_buffer_unmap (buf, bdata, bsize); + gst_buffer_unmap (buf, &map); g_mutex_unlock (enc->property_lock); if (mdata) @@ -893,7 +894,7 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps); + gst_audio_encoder_set_output_format (benc, caps); gst_caps_unref (caps); enc->header_sent = TRUE; diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 68826a56b7..2de891db4b 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -127,11 +127,11 @@ _gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, g_assert (gst_buffer_is_writable (buf)); /* mark buffer */ - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_copy (buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_value_array_append_value (&array, &value); @@ -152,14 +152,15 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, { int n_streams, family; gboolean multistream; + GstMapInfo map; guint8 *data; - gsize size; g_return_if_fail (caps); g_return_if_fail (headers && !*headers); g_return_if_fail (gst_buffer_get_size (buf1) >= 19); - data = gst_buffer_map (buf1, &size, NULL, GST_MAP_READ); + gst_buffer_map (buf1, &map, GST_MAP_READ); + data = map.data; /* work out the number of streams */ family = data[18]; @@ -167,16 +168,16 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, n_streams = 1; } else { /* only included in the header for family > 0 */ - if (size >= 20) + if (map.size >= 20) n_streams = data[19]; else { g_warning ("family > 0 but header buffer size < 20"); - gst_buffer_unmap (buf1, data, size); + gst_buffer_unmap (buf1, &map); return; } } - gst_buffer_unmap (buf1, data, size); + gst_buffer_unmap (buf1, &map); /* mark and put on caps */ multistream = n_streams > 1; @@ -228,13 +229,16 @@ gst_opus_header_is_id_header (GstBuffer * buf) guint8 *data = NULL; guint8 channels, channel_mapping_family, n_streams, n_stereo_streams; gboolean ret = FALSE; + GstMapInfo map; if (size < 19) goto beach; if (!gst_opus_header_is_header (buf, "OpusHead", 8)) goto beach; - data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ); + gst_buffer_map (buf, &map, GST_MAP_READ); + data = map.data; + size = map.size; channels = data[9]; @@ -263,7 +267,7 @@ gst_opus_header_is_id_header (GstBuffer * buf) beach: if (data) - gst_buffer_unmap (buf, data, size); + gst_buffer_unmap (buf, &map); return ret; } From c3672b88f16ecdc6410a905f419b91e008e2af81 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 8 Mar 2012 11:32:27 +0100 Subject: [PATCH 104/197] tests: fix more caps --- tests/check/elements/opus.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index d18f6230fe..282589c257 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -228,8 +228,7 @@ GST_START_TEST (test_opus_encode_samples) ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); caps = - gst_caps_from_string - ("audio/x-raw-int,rate=48000,channels=1,signed=true,width=16,depth=16,endianness=1234"); + gst_caps_from_string ("audio/x-raw,format=S16LE,rate=48000,channels=1"); fail_unless (caps != NULL); gst_buffer_set_caps (inbuffer, caps); gst_caps_unref (caps); @@ -301,8 +300,7 @@ GST_START_TEST (test_opus_encode_properties) "could not set to playing"); caps = - gst_caps_from_string - ("audio/x-raw-int,rate=48000,channels=1,signed=true,width=16,depth=16,endianness=1234"); + gst_caps_from_string ("audio/x-raw,format=S16LE,rate=48000,channels=1"); fail_unless (caps != NULL); for (step = 0; step < steps; ++step) { From 0d84de3fa9d824f3182539ec7966e426a930774f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 12 Mar 2012 17:06:11 +0100 Subject: [PATCH 105/197] opusdec: fix for caps api change --- ext/opus/gstopusdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 5c9b363e9c..829d029e09 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -214,9 +214,9 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) GstStructure *s; GstAudioInfo info; - caps = gst_caps_make_writable (caps); - gst_caps_truncate (caps); + caps = gst_caps_truncate (caps); + caps = gst_caps_make_writable (caps); s = gst_caps_get_structure (caps, 0); gst_structure_fixate_field_nearest_int (s, "rate", 48000); gst_structure_get_int (s, "rate", &dec->sample_rate); From b34b0129dabcb875c0f20a57da80764bc233d6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 27 Mar 2012 15:13:24 -0400 Subject: [PATCH 106/197] opus: Rank rtp pay/depay This way they can be auto-plugged. --- ext/opus/gstopus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index 8db6e197f9..aea0110f44 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -46,11 +46,11 @@ plugin_init (GstPlugin * plugin) GST_TYPE_OPUS_PARSE)) return FALSE; - if (!gst_element_register (plugin, "rtpopusdepay", GST_RANK_NONE, + if (!gst_element_register (plugin, "rtpopusdepay", GST_RANK_SECONDARY, GST_TYPE_RTP_OPUS_DEPAY)) return FALSE; - if (!gst_element_register (plugin, "rtpopuspay", GST_RANK_NONE, + if (!gst_element_register (plugin, "rtpopuspay", GST_RANK_SECONDARY, GST_TYPE_RTP_OPUS_PAY)) return FALSE; From 776b25b06424828344b541c6f38248314582576f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 29 Mar 2012 18:04:36 +0200 Subject: [PATCH 107/197] Merge branch 'master' of ssh://git.freedesktop.org/git/gstreamer/gst-plugins-bad From 76d2eac1cc2983b4fe30f695984519485ecd3484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 30 Mar 2012 12:22:48 +0200 Subject: [PATCH 108/197] ext: Update for GstAudioEncoder API changes --- ext/opus/gstopusenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index be95fb7980..1dfcf48ca0 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -214,7 +214,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) base_class->stop = GST_DEBUG_FUNCPTR (gst_opus_enc_stop); base_class->set_format = GST_DEBUG_FUNCPTR (gst_opus_enc_set_format); base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_opus_enc_handle_frame); - base_class->event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); + base_class->sink_event = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_event); base_class->getcaps = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps); g_object_class_install_property (gobject_class, PROP_AUDIO, @@ -707,7 +707,7 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) break; } - return GST_AUDIO_ENCODER_CLASS (parent_class)->event (benc, event); + return GST_AUDIO_ENCODER_CLASS (parent_class)->sink_event (benc, event); } static GstCaps * From 2f53680369482d340b5ffaae92a5b56b54816d71 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 30 Mar 2012 17:09:34 +0200 Subject: [PATCH 109/197] opusenc: fixup merge --- ext/opus/gstopusenc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 1dfcf48ca0..43301de0b7 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -830,8 +830,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) outsize = opus_multistream_encode (enc->state, (const gint16 *) data, - enc->frame_samples, GST_BUFFER_DATA (outbuf), - enc->max_payload_size * enc->n_channels); + enc->frame_samples, omap.data, enc->max_payload_size * enc->n_channels); gst_buffer_unmap (outbuf, &omap); @@ -848,7 +847,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) } GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); - GST_BUFFER_SIZE (outbuf) = outsize; + gst_buffer_set_size (outbuf, outsize); ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, From 27739c4a3c3bf8ce4ae7826b112363882087b193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 2 Apr 2012 15:31:38 +0200 Subject: [PATCH 110/197] Merge remote-tracking branch 'origin/0.10' Conflicts: gst/mpegtsdemux/tsdemux.c From dfaec6eccd91f0981d7c9f4e20824d91bb40c175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 4 Apr 2012 12:06:08 +0200 Subject: [PATCH 111/197] Merge remote-tracking branch 'origin/0.10' From 6d1665cc91698e958a8d57cfea82cab2abd16bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 4 Apr 2012 14:41:22 +0200 Subject: [PATCH 112/197] gst: Update versioning --- ext/opus/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 48a7eade40..13ae8a2f03 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -8,8 +8,8 @@ libgstopus_la_CFLAGS = \ $(GST_CFLAGS) \ $(OPUS_CFLAGS) libgstopus_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \ - -lgsttag-$(GST_MAJORMINOR) -lgstrtp-$(GST_MAJORMINOR) \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \ + -lgsttag-$(GST_API_VERSION) -lgstrtp-$(GST_API_VERSION) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(OPUS_LIBS) From 49fd40952a6c91444baafb07a24677a08cb16812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 5 Apr 2012 18:02:56 +0200 Subject: [PATCH 113/197] gst: Update for GST_PLUGIN_DEFINE() API changes --- ext/opus/gstopus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index aea0110f44..55e19c49de 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -61,6 +61,6 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "opus", + opus, "OPUS plugin library", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) From 72f951f6d7931c461f72dca69d90eb2b80b635da Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 5 Apr 2012 17:15:11 -0400 Subject: [PATCH 114/197] Merge remote-tracking branch 'origin/0.10' From 4d744d7027aaf5f3115a6ecdd2a31a67b58daecc Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 6 Apr 2012 14:52:12 +0200 Subject: [PATCH 115/197] Merge remote-tracking branch 'origin/0.10' Conflicts: gst/h264parse/gsth264parse.c gst/videoparsers/gsth264parse.c From 5bb216046e0af7cfc87df70c317729d3ad909d0e Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 10 Apr 2012 17:22:44 +0200 Subject: [PATCH 116/197] opusdec: tweak caps negotiation ... so as to avoid leaking caps or manipulating NULL caps. --- ext/opus/gstopusdec.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 829d029e09..a21b1878bc 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -214,14 +214,18 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) GstStructure *s; GstAudioInfo info; - caps = gst_caps_truncate (caps); - - caps = gst_caps_make_writable (caps); - s = gst_caps_get_structure (caps, 0); - gst_structure_fixate_field_nearest_int (s, "rate", 48000); - gst_structure_get_int (s, "rate", &dec->sample_rate); - gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); - gst_structure_get_int (s, "channels", &dec->n_channels); + if (caps) { + caps = gst_caps_truncate (caps); + caps = gst_caps_make_writable (caps); + s = gst_caps_get_structure (caps, 0); + gst_structure_fixate_field_nearest_int (s, "rate", 48000); + gst_structure_get_int (s, "rate", &dec->sample_rate); + gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); + gst_structure_get_int (s, "channels", &dec->n_channels); + gst_caps_unref (caps); + } else { + dec->sample_rate = 48000; + } GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, dec->sample_rate); From 9c90e728ad6dea737f8ce3a500f2ff4310904927 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 10 Apr 2012 17:24:05 +0200 Subject: [PATCH 117/197] tests: port some more to 1.0 --- tests/check/elements/opus.c | 50 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index 282589c257..be5b2cf623 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -24,6 +24,18 @@ #include +#if G_BYTE_ORDER == G_BIG_ENDIAN +#define AFORMAT "S16BE" +#else +#define AFORMAT "S16LE" +#endif + +#define AUDIO_CAPS_STRING "audio/x-raw, " \ + "format = (string) " AFORMAT ", "\ + "layout = (string) interleaved, " \ + "rate = (int) 48000, " \ + "channels = (int) 1 " + static const guint8 opus_ogg_id_header[19] = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 @@ -60,8 +72,8 @@ setup_opusdec (void) GST_DEBUG ("setup_opusdec"); opusdec = gst_check_setup_element ("opusdec"); - mydecsrcpad = gst_check_setup_src_pad (opusdec, &srctemplate, NULL); - mydecsinkpad = gst_check_setup_sink_pad (opusdec, &sinktemplate, NULL); + mydecsrcpad = gst_check_setup_src_pad (opusdec, &srctemplate); + mydecsinkpad = gst_check_setup_sink_pad (opusdec, &sinktemplate); gst_pad_set_active (mydecsrcpad, TRUE); gst_pad_set_active (mydecsinkpad, TRUE); @@ -88,8 +100,8 @@ setup_opusenc (void) GST_DEBUG ("setup_opusenc"); opusenc = gst_check_setup_element ("opusenc"); - myencsrcpad = gst_check_setup_src_pad (opusenc, &srctemplate, NULL); - myencsinkpad = gst_check_setup_sink_pad (opusenc, &sinktemplate, NULL); + myencsrcpad = gst_check_setup_src_pad (opusenc, &srctemplate); + myencsinkpad = gst_check_setup_sink_pad (opusenc, &sinktemplate); gst_pad_set_active (myencsrcpad, TRUE); gst_pad_set_active (myencsinkpad, TRUE); @@ -121,7 +133,7 @@ check_buffers (guint expected) for (i = 0; i < num_buffers; ++i) { outbuffer = GST_BUFFER (buffers->data); fail_if (outbuffer == NULL); - fail_if (GST_BUFFER_SIZE (outbuffer) == 0); + fail_if (gst_buffer_get_size (outbuffer) == 0); buffers = g_list_remove (buffers, outbuffer); @@ -142,7 +154,7 @@ GST_START_TEST (test_opus_id_header) "could not set to playing"); inbuffer = gst_buffer_new_and_alloc (sizeof (opus_ogg_id_header)); - memcpy (GST_BUFFER_DATA (inbuffer), opus_ogg_id_header, + gst_buffer_fill (inbuffer, 0, opus_ogg_id_header, sizeof (opus_ogg_id_header)); ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); gst_buffer_ref (inbuffer); @@ -208,8 +220,6 @@ GST_START_TEST (test_opus_encode_samples) GstElement *opusenc; GstBuffer *inbuffer; GstCaps *caps; - guint16 *samples; - unsigned int n; opusenc = setup_opusenc (); @@ -218,19 +228,15 @@ GST_START_TEST (test_opus_encode_samples) "could not set to playing"); inbuffer = gst_buffer_new_and_alloc (nsamples * 2); - samples = (guint16 *) GST_BUFFER_DATA (inbuffer); - for (n = 0; n < nsamples; ++n) { - samples[n] = 0; - } + gst_buffer_memset (inbuffer, 0, 0, nsamples * 2); GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) = 0; GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE; ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - caps = - gst_caps_from_string ("audio/x-raw,format=S16LE,rate=48000,channels=1"); + caps = gst_caps_from_string (AUDIO_CAPS_STRING); fail_unless (caps != NULL); - gst_buffer_set_caps (inbuffer, caps); + gst_pad_set_caps (myencsrcpad, caps); gst_caps_unref (caps); gst_buffer_ref (inbuffer); @@ -264,8 +270,7 @@ GST_START_TEST (test_opus_encode_properties) GstElement *opusenc; GstBuffer *inbuffer; GstCaps *caps; - guint16 *samples; - unsigned int n, step; + unsigned int step; static const struct { const char *param; @@ -299,22 +304,19 @@ GST_START_TEST (test_opus_encode_properties) GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, "could not set to playing"); - caps = - gst_caps_from_string ("audio/x-raw,format=S16LE,rate=48000,channels=1"); + caps = gst_caps_from_string (AUDIO_CAPS_STRING); fail_unless (caps != NULL); + gst_pad_set_caps (myencsrcpad, caps); + for (step = 0; step < steps; ++step) { inbuffer = gst_buffer_new_and_alloc (nsamples * 2); - samples = (guint16 *) GST_BUFFER_DATA (inbuffer); - for (n = 0; n < nsamples; ++n) { - samples[n] = 0; - } + gst_buffer_memset (inbuffer, 0, 0, nsamples * 2); GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) = 0; GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE; ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - gst_buffer_set_caps (inbuffer, caps); gst_buffer_ref (inbuffer); /* pushing gives away my reference ... */ From 220687e602b1dcac38eb46c4f6149b1a0c1339d3 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 4 Apr 2012 11:51:28 +0200 Subject: [PATCH 118/197] opus: Handle GstByteWriter return values --- ext/opus/gstopusheader.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 2de891db4b..8dc5d27199 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -33,6 +33,7 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, { GstBuffer *buffer; GstByteWriter bw; + gboolean hdl = TRUE; g_return_val_if_fail (nchannels > 0 && nchannels < 256, NULL); g_return_val_if_fail (n_stereo_streams >= 0, NULL); @@ -41,19 +42,22 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, gst_byte_writer_init (&bw); /* See http://wiki.xiph.org/OggOpus */ - gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); - gst_byte_writer_put_uint8 (&bw, 0); /* version number */ - gst_byte_writer_put_uint8 (&bw, nchannels); - gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip */ - gst_byte_writer_put_uint32_le (&bw, sample_rate); - gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ - gst_byte_writer_put_uint8 (&bw, channel_mapping_family); + hdl &= gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); + hdl &= gst_byte_writer_put_uint8 (&bw, 0); /* version number */ + hdl &= gst_byte_writer_put_uint8 (&bw, nchannels); + hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip */ + hdl &= gst_byte_writer_put_uint32_le (&bw, sample_rate); + hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ + hdl &= gst_byte_writer_put_uint8 (&bw, channel_mapping_family); if (channel_mapping_family > 0) { - gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); - gst_byte_writer_put_uint8 (&bw, n_stereo_streams); - gst_byte_writer_put_data (&bw, channel_mapping, nchannels); + hdl &= gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); + hdl &= gst_byte_writer_put_uint8 (&bw, n_stereo_streams); + hdl &= gst_byte_writer_put_data (&bw, channel_mapping, nchannels); } + if (!hdl) + GST_WARNING ("Error creating header"); + buffer = gst_byte_writer_reset_and_get_buffer (&bw); GST_BUFFER_OFFSET (buffer) = 0; From 7d731ac155857be39858b605d8554256a177d8aa Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 30 Apr 2012 14:40:02 +0100 Subject: [PATCH 119/197] opusdec: fix lost packet handling for FEC/PLC The base audio decoder sends zero size packets, not NULL buffers, to signal dropped packets. --- ext/opus/gstopusdec.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index a21b1878bc..626e785afa 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -393,17 +393,20 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) to potentially wait for next buffer to decode a missing buffer */ if (dec->use_inband_fec && !dec->primed) { GST_DEBUG_OBJECT (dec, "First buffer received in FEC mode, early out"); + gst_buffer_replace (&dec->last_buffer, buffer); + dec->primed = TRUE; goto done; } /* That's the buffer we'll be sending to the opus decoder. */ - buf = dec->use_inband_fec && dec->last_buffer ? dec->last_buffer : buffer; + buf = (dec->use_inband_fec + && gst_buffer_get_size (dec->last_buffer) > + 0) ? dec->last_buffer : buffer; - if (buf) { + if (buf && gst_buffer_get_size (buf) > 0) { gst_buffer_map (buf, &map, GST_MAP_READ); data = map.data; size = map.size; - GST_DEBUG_OBJECT (dec, "Using buffer of size %u", size); } else { /* concealment data, pass NULL as the bits parameters */ @@ -428,15 +431,18 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) if (dec->use_inband_fec) { if (dec->last_buffer) { /* normal delayed decode */ + GST_LOG_OBJECT (dec, "FEC enabled, decoding last delayed buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } else { /* FEC reconstruction decode */ + GST_LOG_OBJECT (dec, "FEC enabled, reconstructing last buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, 1); } } else { /* normal decode */ + GST_LOG_OBJECT (dec, "FEC disabled, decoding buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } gst_buffer_unmap (outbuf, &omap); @@ -445,6 +451,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) if (n < 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); + gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } GST_DEBUG_OBJECT (dec, "decoded %d samples", n); @@ -495,17 +502,16 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) gst_buffer_unmap (outbuf, &omap); } + if (dec->use_inband_fec) { + gst_buffer_replace (&dec->last_buffer, buffer); + } + res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1); if (res != GST_FLOW_OK) GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); done: - if (dec->use_inband_fec) { - gst_buffer_replace (&dec->last_buffer, buffer); - dec->primed = TRUE; - } - return res; creation_failed: From 62d6c0814889c4a487ec9f02b21c1e1a52a4ed3b Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 24 May 2012 21:58:44 +0100 Subject: [PATCH 120/197] opus: bump written version from 0 to 0x01 as per the spec update at https://wiki.xiph.org/OggOpus#ID_Header --- ext/opus/gstopusheader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 8dc5d27199..36cb4ec936 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -43,7 +43,7 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, /* See http://wiki.xiph.org/OggOpus */ hdl &= gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); - hdl &= gst_byte_writer_put_uint8 (&bw, 0); /* version number */ + hdl &= gst_byte_writer_put_uint8 (&bw, 0x01); /* version number */ hdl &= gst_byte_writer_put_uint8 (&bw, nchannels); hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip */ hdl &= gst_byte_writer_put_uint32_le (&bw, sample_rate); From 221a840eb8fd067b23d8a59c8c0f1f56cc72c755 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 24 May 2012 22:12:56 +0100 Subject: [PATCH 121/197] opus: reject major version number above what we grok --- ext/opus/gstopusheader.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 36cb4ec936..5c4edba49f 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -231,7 +231,7 @@ gst_opus_header_is_id_header (GstBuffer * buf) { gsize size = gst_buffer_get_size (buf); guint8 *data = NULL; - guint8 channels, channel_mapping_family, n_streams, n_stereo_streams; + guint8 version, channels, channel_mapping_family, n_streams, n_stereo_streams; gboolean ret = FALSE; GstMapInfo map; @@ -244,6 +244,10 @@ gst_opus_header_is_id_header (GstBuffer * buf) data = map.data; size = map.size; + version = data[8]; + if (version >= 0x0f) /* major version >=0 is what we grok */ + goto beach; + channels = data[9]; if (channels == 0) From 209e3c5e391e9ec440f3859ca88781031d7d3177 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sun, 27 May 2012 23:41:24 +0100 Subject: [PATCH 122/197] opusdec: do not assert on bad header, error out instead --- ext/opus/gstopusdec.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 626e785afa..15bec7b421 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -261,13 +261,19 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) const GstAudioChannelPosition *posn = NULL; GstMapInfo map; - g_return_val_if_fail (gst_opus_header_is_id_header (buf), GST_FLOW_ERROR); + if (!gst_opus_header_is_id_header (buf)) { + GST_ERROR_OBJECT (dec, "Header is not an Opus ID header"); + return GST_FLOW_ERROR; + } gst_buffer_map (buf, &map, GST_MAP_READ); data = map.data; - g_return_val_if_fail (dec->n_channels == 0 - || dec->n_channels == data[9], GST_FLOW_ERROR); + if (!(dec->n_channels == 0 || dec->n_channels == data[9])) { + gst_buffer_unmap (buf, &map); + GST_ERROR_OBJECT (dec, "Opus ID header has invalid channels"); + return GST_FLOW_ERROR; + } dec->n_channels = data[9]; dec->pre_skip = GST_READ_UINT16_LE (data + 10); From 3dc1ef35b1a923bcd4bc460ac4e8fd731644c7c2 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 29 May 2012 17:24:02 +0100 Subject: [PATCH 123/197] opusdec: read gain from the right place in the header It's at byte offset 16, not 14. --- ext/opus/gstopusdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 15bec7b421..b0f33ae746 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -277,7 +277,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) dec->n_channels = data[9]; dec->pre_skip = GST_READ_UINT16_LE (data + 10); - dec->r128_gain = GST_READ_UINT16_LE (data + 14); + dec->r128_gain = GST_READ_UINT16_LE (data + 16); dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); GST_INFO_OBJECT (dec, "Found pre-skip of %u samples, R128 gain %d (volume %f)", From ad4da8dd0e01a1e60213b5dbb2bb0c03a010e542 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 15 Jun 2012 10:24:24 +0100 Subject: [PATCH 124/197] opus: set author to myself, and update copyright notices because as slomo noted, in fact pretty much all the code in there is mine. --- ext/opus/gstopusdec.c | 3 ++- ext/opus/gstopusdec.h | 1 + ext/opus/gstopusenc.c | 2 +- ext/opus/gstopusenc.h | 1 + ext/opus/gstopusheader.h | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index b0f33ae746..0e2805cc0d 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -2,6 +2,7 @@ * Copyright (C) 2004 Wim Taymans * Copyright (C) 2006 Tim-Philipp Müller * Copyright (C) 2008 Sebastian Dröge + * Copyright (C) 2011-2012 Vincent Penquerc'h * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -123,7 +124,7 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) gst_element_class_set_details_simple (element_class, "Opus audio decoder", "Codec/Decoder/Audio", "decode opus streams to audio", - "Sebastian Dröge "); + "Vincent Penquerc'h "); g_object_class_install_property (gobject_class, PROP_USE_INBAND_FEC, g_param_spec_boolean ("use-inband-fec", "Use in-band FEC", "Use forward error correction if available", DEFAULT_USE_INBAND_FEC, diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 0bea2c081a..1367000e64 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2008> Sebastian Dröge + * Copyright (C) <2011-2012> Vincent Penquerc'h * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 43301de0b7..861bd415d8 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -208,7 +208,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) gst_element_class_set_details_simple (gstelement_class, "Opus audio encoder", "Codec/Encoder/Audio", "Encodes audio in Opus format", - "Sebastian Dröge "); + "Vincent Penquerc'h "); base_class->start = GST_DEBUG_FUNCPTR (gst_opus_enc_start); base_class->stop = GST_DEBUG_FUNCPTR (gst_opus_enc_stop); diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 1e39ad03d4..00f1b991bf 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -1,6 +1,7 @@ /* GStreamer Opus Encoder * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2008> Sebastian Dröge + * Copyright (C) <2011-2012> Vincent Penquerc'h * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index c6278eff30..c6eaa033e7 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2008> Sebastian Dröge + * Copyright (C) <2011-2012> Vincent Penquerc'h * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public From de739ee1d1883033add89381d01d733891d5e5ad Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 15 Jun 2012 10:32:39 +0100 Subject: [PATCH 125/197] opusenc: add missing mutex unlock on error path --- ext/opus/gstopusenc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 861bd415d8..5386286ff2 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -621,8 +621,10 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } - if (!gst_opus_enc_setup (enc)) + if (!gst_opus_enc_setup (enc)) { + g_mutex_unlock (enc->property_lock); return FALSE; + } enc->frame_samples = gst_opus_enc_get_frame_samples (enc); From a052550ed7738f2847cd35f373141d10b4a15944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 4 Aug 2012 16:31:30 +0100 Subject: [PATCH 126/197] gst_tag_list_free -> gst_tag_list_unref --- ext/opus/gstopusenc.c | 2 +- ext/opus/gstopusheader.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 5386286ff2..337be8c0e4 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -344,7 +344,7 @@ gst_opus_enc_stop (GstAudioEncoder * benc) opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } - gst_tag_list_free (enc->tags); + gst_tag_list_unref (enc->tags); enc->tags = NULL; g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); g_slist_free (enc->headers); diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 5c4edba49f..af97172955 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -88,7 +88,7 @@ gst_opus_enc_create_metadata_buffer (const GstTagList * tags) GST_BUFFER_OFFSET_END (comments) = 0; if (empty_tags) - gst_tag_list_free (empty_tags); + gst_tag_list_unref (empty_tags); return comments; } From 59e408f54e540f5b50a24e9886c5572ff063924e Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 11 Sep 2012 14:31:49 +0200 Subject: [PATCH 127/197] opusenc: port to the new GLib thread API --- ext/opus/gstopusenc.c | 34 +++++++++++++++++----------------- ext/opus/gstopusenc.h | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 337be8c0e4..240a2cb165 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -287,7 +287,7 @@ gst_opus_enc_finalize (GObject * object) enc = GST_OPUS_ENC (object); - g_mutex_free (enc->property_lock); + g_mutex_clear (&enc->property_lock); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -299,7 +299,7 @@ gst_opus_enc_init (GstOpusEnc * enc) GST_DEBUG_OBJECT (enc, "init"); - enc->property_lock = g_mutex_new (); + g_mutex_init (&enc->property_lock); enc->n_channels = -1; enc->sample_rate = -1; @@ -608,7 +608,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc = GST_OPUS_ENC (benc); - g_mutex_lock (enc->property_lock); + g_mutex_lock (&enc->property_lock); enc->n_channels = GST_AUDIO_INFO_CHANNELS (info); enc->sample_rate = GST_AUDIO_INFO_RATE (info); @@ -622,7 +622,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) enc->state = NULL; } if (!gst_opus_enc_setup (enc)) { - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); return FALSE; } @@ -631,7 +631,7 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) /* feedback to base class */ gst_opus_enc_setup_base_class (enc, benc); - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); return TRUE; } @@ -793,7 +793,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) gint outsize; GstBuffer *outbuf; - g_mutex_lock (enc->property_lock); + g_mutex_lock (&enc->property_lock); if (G_LIKELY (buf)) { gst_buffer_map (buf, &map, GST_MAP_READ); @@ -859,7 +859,7 @@ done: if (bdata) gst_buffer_unmap (buf, &map); - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); if (mdata) g_free (mdata); @@ -914,7 +914,7 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, enc = GST_OPUS_ENC (object); - g_mutex_lock (enc->property_lock); + g_mutex_lock (&enc->property_lock); switch (prop_id) { case PROP_AUDIO: @@ -955,7 +955,7 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, break; } - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); } static void @@ -967,12 +967,12 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, enc = GST_OPUS_ENC (object); #define GST_OPUS_UPDATE_PROPERTY(prop,type,ctl) do { \ - g_mutex_lock (enc->property_lock); \ + g_mutex_lock (&enc->property_lock); \ enc->prop = g_value_get_##type (value); \ if (enc->state) { \ opus_multistream_encoder_ctl (enc->state, OPUS_SET_##ctl (enc->prop)); \ } \ - g_mutex_unlock (enc->property_lock); \ + g_mutex_unlock (&enc->property_lock); \ } while(0) switch (prop_id) { @@ -986,18 +986,18 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, GST_OPUS_UPDATE_PROPERTY (bandwidth, enum, BANDWIDTH); break; case PROP_FRAME_SIZE: - g_mutex_lock (enc->property_lock); + g_mutex_lock (&enc->property_lock); enc->frame_size = g_value_get_enum (value); enc->frame_samples = gst_opus_enc_get_frame_samples (enc); gst_opus_enc_setup_base_class (enc, GST_AUDIO_ENCODER (enc)); - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); break; case PROP_CBR: /* this one has an opposite meaning to the opus ctl... */ - g_mutex_lock (enc->property_lock); + g_mutex_lock (&enc->property_lock); enc->cbr = g_value_get_boolean (value); opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); break; case PROP_CONSTRAINED_VBR: GST_OPUS_UPDATE_PROPERTY (constrained_vbr, boolean, VBR_CONSTRAINT); @@ -1015,9 +1015,9 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, GST_OPUS_UPDATE_PROPERTY (packet_loss_percentage, int, PACKET_LOSS_PERC); break; case PROP_MAX_PAYLOAD_SIZE: - g_mutex_lock (enc->property_lock); + g_mutex_lock (&enc->property_lock); enc->max_payload_size = g_value_get_uint (value); - g_mutex_unlock (enc->property_lock); + g_mutex_unlock (&enc->property_lock); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 00f1b991bf..df994e30b4 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -54,7 +54,7 @@ struct _GstOpusEnc { OpusMSEncoder *state; /* Locks those properties which may be changed at play time */ - GMutex *property_lock; + GMutex property_lock; /* properties */ gboolean audio_or_voip; From f4f714fc6b63e34f51be4e2923f7b172eec13884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 11 Sep 2012 18:01:58 -0400 Subject: [PATCH 128/197] test: Flush opus encoder between tests --- tests/check/elements/opus.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index be5b2cf623..b7e36ff108 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -333,6 +333,11 @@ GST_START_TEST (test_opus_encode_properties) NULL); check_buffers (1); + + fail_unless (gst_pad_push_event (myencsrcpad, + gst_event_new_flush_start ()) == TRUE); + fail_unless (gst_pad_push_event (myencsrcpad, + gst_event_new_flush_stop (FALSE)) == TRUE); } gst_caps_unref (caps); From 24fa2ac653f45792113f3bc2b7cca18bac328970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 11 Sep 2012 18:02:28 -0400 Subject: [PATCH 129/197] test: Flush opus encoder between tests --- tests/check/elements/opus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index b7e36ff108..84d39b34dc 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -337,7 +337,7 @@ GST_START_TEST (test_opus_encode_properties) fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_flush_start ()) == TRUE); fail_unless (gst_pad_push_event (myencsrcpad, - gst_event_new_flush_stop (FALSE)) == TRUE); + gst_event_new_flush_stop (TRUE)) == TRUE); } gst_caps_unref (caps); From 331b660e65f437e2bf5c986c9171d4a48ce93759 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 12 Sep 2012 09:10:35 +0200 Subject: [PATCH 130/197] opus + jpegformat: unbreak non-debug build opus + jpegformat plugin builds fail when gstreamer is configured with --disable-gst-debug as they are checking the GST_DISABLE_DEBUG symbol instead of GST_DISABLE_GST_DEBUG. Signed-off-by: Peter Korsgaard https://bugzilla.gnome.org/show_bug.cgi?id=683850 --- ext/opus/gstopusdec.c | 2 +- ext/opus/gstopusenc.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 0e2805cc0d..ab7221d7b0 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -375,7 +375,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_DEBUG_OBJECT (dec, "Creating decoder with %d channels, %d Hz", dec->n_channels, dec->sample_rate); -#ifndef GST_DISABLE_DEBUG +#ifndef GST_DISABLE_GST_DEBUG gst_opus_common_log_channel_mapping_table (GST_ELEMENT (dec), opusdec_debug, "Mapping table", dec->n_channels, dec->channel_mapping); #endif diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 240a2cb165..d9917951c9 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -574,7 +574,7 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, } } -#ifndef GST_DISABLE_DEBUG +#ifndef GST_DISABLE_GST_DEBUG GST_INFO_OBJECT (enc, "Mapping tables built: %d channels, %d stereo streams", enc->n_channels, enc->n_stereo_streams); @@ -641,7 +641,7 @@ gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; -#ifndef GST_DISABLE_DEBUG +#ifndef GST_DISABLE_GST_DEBUG GST_DEBUG_OBJECT (enc, "setup: %d Hz, %d channels, %d stereo streams, family %d", enc->sample_rate, enc->n_channels, enc->n_stereo_streams, From 42024f70dc881b167d134e5c7c7240c8d5db950f Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 14 Sep 2012 17:08:49 +0200 Subject: [PATCH 131/197] replace gst_element_class_set_details_simple with gst_element_class_set_metadata --- ext/opus/gstopusdec.c | 2 +- ext/opus/gstopusenc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index ab7221d7b0..c4335265f6 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -121,7 +121,7 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) gst_static_pad_template_get (&opus_dec_src_factory)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&opus_dec_sink_factory)); - gst_element_class_set_details_simple (element_class, "Opus audio decoder", + gst_element_class_set_metadata (element_class, "Opus audio decoder", "Codec/Decoder/Audio", "decode opus streams to audio", "Vincent Penquerc'h "); diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index d9917951c9..a33f0e45a3 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -205,7 +205,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) gst_static_pad_template_get (&src_factory)); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sink_factory)); - gst_element_class_set_details_simple (gstelement_class, "Opus audio encoder", + gst_element_class_set_metadata (gstelement_class, "Opus audio encoder", "Codec/Encoder/Audio", "Encodes audio in Opus format", "Vincent Penquerc'h "); From f1227a374ad320e90d9d15494c401d167eb63b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 20 Sep 2012 18:42:50 -0400 Subject: [PATCH 132/197] opusenc: Rank as Primary --- ext/opus/gstopus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index 55e19c49de..2e0fe7cbe3 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -34,7 +34,7 @@ static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_element_register (plugin, "opusenc", GST_RANK_NONE, + if (!gst_element_register (plugin, "opusenc", GST_RANK_PRIMARY, GST_TYPE_OPUS_ENC)) return FALSE; From 942680d97985035b6de1d8ae67ac0556884e48a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 17 Oct 2012 17:34:26 +0100 Subject: [PATCH 133/197] Use gst_element_class_set_static_metadata() where possible. Avoids some string copies. Also re-indent some stuff. Also some indent fixes here and there. --- ext/opus/gstopusdec.c | 2 +- ext/opus/gstopusenc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index c4335265f6..66e4b5d858 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -121,7 +121,7 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) gst_static_pad_template_get (&opus_dec_src_factory)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&opus_dec_sink_factory)); - gst_element_class_set_metadata (element_class, "Opus audio decoder", + gst_element_class_set_static_metadata (element_class, "Opus audio decoder", "Codec/Decoder/Audio", "decode opus streams to audio", "Vincent Penquerc'h "); diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index a33f0e45a3..f6f83788b3 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -205,7 +205,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) gst_static_pad_template_get (&src_factory)); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sink_factory)); - gst_element_class_set_metadata (gstelement_class, "Opus audio encoder", + gst_element_class_set_static_metadata (gstelement_class, "Opus audio encoder", "Codec/Encoder/Audio", "Encodes audio in Opus format", "Vincent Penquerc'h "); From a01598c1f19e87ab69ee75dec209e8415a23a0e4 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Wed, 24 Oct 2012 23:40:20 +0200 Subject: [PATCH 134/197] opusdec: fixed buffer unmapping bug When the decoder received a NULL buffer, it tried to unmap a not mapped buffer. https://bugzilla.gnome.org/show_bug.cgi?id=686829 --- ext/opus/gstopusdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 66e4b5d858..12e1652f26 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -453,7 +453,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) n = opus_multistream_decode (dec->state, data, size, out_data, samples, 0); } gst_buffer_unmap (outbuf, &omap); - if (buf) + if (data != NULL) gst_buffer_unmap (buf, &map); if (n < 0) { From 3e208df632d3f24ded101ce60acf79dd91467b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 3 Nov 2012 20:38:00 +0000 Subject: [PATCH 135/197] Fix FSF address https://bugzilla.gnome.org/show_bug.cgi?id=687520 --- ext/opus/gstopus.c | 4 ++-- ext/opus/gstopuscommon.c | 4 ++-- ext/opus/gstopuscommon.h | 4 ++-- ext/opus/gstopusdec.c | 4 ++-- ext/opus/gstopusdec.h | 4 ++-- ext/opus/gstopusenc.c | 4 ++-- ext/opus/gstopusenc.h | 4 ++-- ext/opus/gstopusheader.c | 4 ++-- ext/opus/gstopusheader.h | 4 ++-- tests/check/elements/opus.c | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ext/opus/gstopus.c b/ext/opus/gstopus.c index 2e0fe7cbe3..a3427b2849 100644 --- a/ext/opus/gstopus.c +++ b/ext/opus/gstopus.c @@ -14,8 +14,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c index 80414ad05f..227d44a0d4 100644 --- a/ext/opus/gstopuscommon.c +++ b/ext/opus/gstopuscommon.c @@ -13,8 +13,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include diff --git a/ext/opus/gstopuscommon.h b/ext/opus/gstopuscommon.h index 21c2733fff..71771ae744 100644 --- a/ext/opus/gstopuscommon.h +++ b/ext/opus/gstopuscommon.h @@ -13,8 +13,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 12e1652f26..ebef6e44e6 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /* diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 1367000e64..72487787cb 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __GST_OPUS_DEC_H__ diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f6f83788b3..142701b35d 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /* diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index df994e30b4..64f95e37c0 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index af97172955..70844c4915 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index c6eaa033e7..14c88c04d5 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __GST_OPUS_HEADER_H__ diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index 84d39b34dc..b7d1c64c47 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include From 41412f08eca9f1ad727bc8db6b1de599008e47ac Mon Sep 17 00:00:00 2001 From: Thijs Vermeir Date: Tue, 18 Dec 2012 16:56:28 +0100 Subject: [PATCH 136/197] opus: use appropriate printf format for gsize --- ext/opus/gstopusdec.c | 4 ++-- ext/opus/gstopusenc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index ebef6e44e6..5b4a156865 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -390,7 +390,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) } if (buffer) { - GST_DEBUG_OBJECT (dec, "Received buffer of size %u", + GST_DEBUG_OBJECT (dec, "Received buffer of size %" G_GSIZE_FORMAT, gst_buffer_get_size (buffer)); } else { GST_DEBUG_OBJECT (dec, "Received missing buffer"); @@ -414,7 +414,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) gst_buffer_map (buf, &map, GST_MAP_READ); data = map.data; size = map.size; - GST_DEBUG_OBJECT (dec, "Using buffer of size %u", size); + GST_DEBUG_OBJECT (dec, "Using buffer of size %" G_GSIZE_FORMAT, size); } else { /* concealment data, pass NULL as the bits parameters */ GST_DEBUG_OBJECT (dec, "Using NULL buffer"); diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 142701b35d..d6dfa9d173 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -898,7 +898,7 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) enc->header_sent = TRUE; } - GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf, + GST_DEBUG_OBJECT (enc, "received buffer %p of %" G_GSIZE_FORMAT " bytes", buf, buf ? gst_buffer_get_size (buf) : 0); ret = gst_opus_enc_encode (enc, buf); From 4b0c8a07da5f05b8132beac1e240c14424db1913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 28 Jan 2013 14:12:56 +0000 Subject: [PATCH 137/197] opusenc: fix crash when setting "cbr" property when encoder is not running yet https://bugzilla.gnome.org/show_bug.cgi?id=692698 --- ext/opus/gstopusenc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index d6dfa9d173..9eefe9ca76 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -996,7 +996,8 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, /* this one has an opposite meaning to the opus ctl... */ g_mutex_lock (&enc->property_lock); enc->cbr = g_value_get_boolean (value); - opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); + if (enc->state) + opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); g_mutex_unlock (&enc->property_lock); break; case PROP_CONSTRAINED_VBR: From 70ac2717a9eebc334d4e3b6952c9972976c623c6 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 11 Feb 2013 11:06:32 +0100 Subject: [PATCH 138/197] opusdec: clear the state of the decoder Set the channels and rate back to their default values in _stop because they are used to renegotiate when needed. See https://bugzilla.gnome.org/show_bug.cgi?id=692950 --- ext/opus/gstopusdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 5b4a156865..456cc624be 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -155,13 +155,13 @@ gst_opus_dec_reset (GstOpusDec * dec) dec->pre_skip = 0; dec->r128_gain = 0; + dec->sample_rate = 0; + dec->n_channels = 0; } static void gst_opus_dec_init (GstOpusDec * dec) { - dec->sample_rate = 0; - dec->n_channels = 0; dec->use_inband_fec = FALSE; dec->apply_gain = DEFAULT_APPLY_GAIN; From 990788d561eacddca73370135e0a6dc526e56f96 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 23 Aug 2007 19:12:23 +0000 Subject: [PATCH 139/197] sbc: Add SBC encoder and decoder skeletons for GStreamer From 49d8a1e769d5e18b78a0d424ad6b324c45144873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 24 Oct 2012 12:16:39 +0200 Subject: [PATCH 140/197] gst: Add better support for static plugins --- ext/opus/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index 13ae8a2f03..d22c664e0f 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -14,6 +14,6 @@ libgstopus_la_LIBADD = \ $(GST_LIBS) \ $(OPUS_LIBS) libgstopus_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM) -libgstopus_la_LIBTOOLFLAGS = --tag=disable-static +libgstopus_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) noinst_HEADERS = gstopusenc.h gstopusdec.h gstopusparse.h gstopusheader.h gstopuscommon.h gstrtpopuspay.h gstrtpopusdepay.h From 061b3e5fec329ad36102c2c365ba4420c8730c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 15 May 2013 10:18:01 +0200 Subject: [PATCH 141/197] opus: Fix event handling in unit test --- tests/check/elements/opus.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index b7d1c64c47..dbc73a487f 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -147,12 +147,17 @@ GST_START_TEST (test_opus_id_header) { GstElement *opusdec; GstBuffer *inbuffer; + GstCaps *caps; opusdec = setup_opusdec (); fail_unless (gst_element_set_state (opusdec, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, "could not set to playing"); + caps = gst_caps_new_empty_simple ("audio/x-opus"); + gst_check_setup_events (mydecsrcpad, opusdec, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + inbuffer = gst_buffer_new_and_alloc (sizeof (opus_ogg_id_header)); gst_buffer_fill (inbuffer, 0, opus_ogg_id_header, sizeof (opus_ogg_id_header)); @@ -236,7 +241,7 @@ GST_START_TEST (test_opus_encode_samples) caps = gst_caps_from_string (AUDIO_CAPS_STRING); fail_unless (caps != NULL); - gst_pad_set_caps (myencsrcpad, caps); + gst_check_setup_events (myencsrcpad, opusenc, caps, GST_FORMAT_TIME); gst_caps_unref (caps); gst_buffer_ref (inbuffer); @@ -307,9 +312,14 @@ GST_START_TEST (test_opus_encode_properties) caps = gst_caps_from_string (AUDIO_CAPS_STRING); fail_unless (caps != NULL); - gst_pad_set_caps (myencsrcpad, caps); + gst_check_setup_events (myencsrcpad, opusenc, caps, GST_FORMAT_TIME); for (step = 0; step < steps; ++step) { + GstSegment segment; + + gst_segment_init (&segment, GST_FORMAT_TIME); + gst_pad_push_event (myencsrcpad, gst_event_new_segment (&segment)); + inbuffer = gst_buffer_new_and_alloc (nsamples * 2); gst_buffer_memset (inbuffer, 0, 0, nsamples * 2); From 0c301bfee7d79801a8fd4d3d2485ea31dc7e6d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 5 Dec 2013 12:04:59 +0100 Subject: [PATCH 142/197] opusdec: Require caps to be set before any data processing --- ext/opus/gstopusdec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 456cc624be..d2fc6465dc 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -165,6 +165,8 @@ gst_opus_dec_init (GstOpusDec * dec) dec->use_inband_fec = FALSE; dec->apply_gain = DEFAULT_APPLY_GAIN; + gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (dec), TRUE); + gst_opus_dec_reset (dec); } From 80df2cb0633a75f7ac390b08a6d3c87a3c9215b4 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Fri, 27 Dec 2013 14:29:46 +0000 Subject: [PATCH 143/197] opusenc: increase max payload size to 4000 bytes 1275 is the maximum size of a frame, but the encoder may return up to 3 frames, and we need a few extra bytes for TOC, etc. We use 4000, which is a bit more, and suggested in the libopus docs. --- ext/opus/gstopusenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 9eefe9ca76..02190f9bd0 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -141,7 +141,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", #define DEFAULT_INBAND_FEC FALSE #define DEFAULT_DTX FALSE #define DEFAULT_PACKET_LOSS_PERCENT 0 -#define DEFAULT_MAX_PAYLOAD_SIZE 1024 +#define DEFAULT_MAX_PAYLOAD_SIZE 4000 enum { @@ -270,7 +270,7 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_PAYLOAD_SIZE, g_param_spec_uint ("max-payload-size", - "Max payload size", "Maximum payload size in bytes", 2, 1275, + "Max payload size", "Maximum payload size in bytes", 2, 4000, DEFAULT_MAX_PAYLOAD_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)); From 97d0927a9cd4eaa071da3f17bff47baccc6770cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Feb 2014 20:08:29 +0100 Subject: [PATCH 144/197] opus: Remove unused variable from unit test --- tests/check/elements/opus.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index dbc73a487f..e7f9c3a9d7 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -41,13 +41,6 @@ static const guint8 opus_ogg_id_header[19] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static const guint8 opus_ogg_comments_header[] = { - 0x4f, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73, 0x1e, 0x00, 0x00, 0x00, 0x45, - 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x72, 0x20, 0x4f, 0x70, 0x75, 0x73, - 0x65, 0x6e, 0x63, 0x00, 0x00, 0x00, 0x00 -}; - /* A lot of these taken from the vorbisdec test */ /* For ease of programming we use globals to keep refs for our floating @@ -316,7 +309,7 @@ GST_START_TEST (test_opus_encode_properties) for (step = 0; step < steps; ++step) { GstSegment segment; - + gst_segment_init (&segment, GST_FORMAT_TIME); gst_pad_push_event (myencsrcpad, gst_event_new_segment (&segment)); From 16042fac2bf5795aed79c70afbb8df73973976dc Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 9 Apr 2014 11:02:00 +0100 Subject: [PATCH 145/197] opus: add missing va_end in variadic function Coverity 1139944 --- ext/opus/gstopusheader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 70844c4915..b654aad325 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -143,6 +143,7 @@ _gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, buf = va_arg (va, GstBuffer *); } + va_end (va); gst_structure_set_value (structure, field, &array); g_value_unset (&array); From fd77bb1eff0fc9252cbcd927bd89ac00f2104a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= Date: Sat, 10 May 2014 18:32:28 +0200 Subject: [PATCH 146/197] opusenc: Use aux vars to minimize critical region This avoid dead lock between gst_audio_encoder_finish_frame() and gst_opus_enc_get_property(). Also, now bytes var is set into protected section. https://bugzilla.gnome.org/show_bug.cgi?id=729882 --- ext/opus/gstopusenc.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 02190f9bd0..195d88fecd 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -786,15 +786,24 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { guint8 *bdata = NULL, *data, *mdata = NULL; gsize bsize, size; - gsize bytes = enc->frame_samples * enc->n_channels * 2; + gsize bytes; gint ret = GST_FLOW_OK; GstMapInfo map; GstMapInfo omap; gint outsize; GstBuffer *outbuf; + guint max_payload_size; + gint frame_samples; + g_mutex_lock (&enc->property_lock); + bytes = enc->frame_samples * enc->n_channels * 2; + max_payload_size = enc->max_payload_size; + frame_samples = enc->frame_samples; + + g_mutex_unlock (&enc->property_lock); + if (G_LIKELY (buf)) { gst_buffer_map (buf, &map, GST_MAP_READ); bdata = map.data; @@ -818,21 +827,21 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) g_assert (size == bytes); - outbuf = gst_buffer_new_and_alloc (enc->max_payload_size * enc->n_channels); + outbuf = gst_buffer_new_and_alloc (max_payload_size * enc->n_channels); if (!outbuf) goto done; GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - enc->frame_samples, (int) bytes); + frame_samples, (int) bytes); gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - enc->frame_samples, (int) bytes); + frame_samples, (int) bytes); outsize = opus_multistream_encode (enc->state, (const gint16 *) data, - enc->frame_samples, omap.data, enc->max_payload_size * enc->n_channels); + frame_samples, omap.data, max_payload_size * enc->n_channels); gst_buffer_unmap (outbuf, &omap); @@ -840,10 +849,10 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; goto done; - } else if (outsize > enc->max_payload_size) { + } else if (outsize > max_payload_size) { GST_WARNING_OBJECT (enc, "Encoded size %d is higher than max payload size (%d bytes)", - outsize, enc->max_payload_size); + outsize, max_payload_size); ret = GST_FLOW_ERROR; goto done; } @@ -853,13 +862,12 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, - enc->frame_samples); + frame_samples); done: if (bdata) gst_buffer_unmap (buf, &map); - g_mutex_unlock (&enc->property_lock); if (mdata) g_free (mdata); From 01d2956c4ed5a888a18476602181be416233e7a6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 10 Jul 2014 15:52:46 +0100 Subject: [PATCH 147/197] opus: Fix a double-unref in the Opus header code The headers were never getting reffed when being added to the headers list, which is later unreffed-and-freed by the caller (e.g. gst_opus_parse_parse_frame()). https://bugzilla.gnome.org/show_bug.cgi?id=733013 --- ext/opus/gstopusheader.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index b654aad325..3011716403 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -190,8 +190,8 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, "multistream", G_TYPE_BOOLEAN, multistream, NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); - *headers = g_slist_prepend (*headers, buf2); - *headers = g_slist_prepend (*headers, buf1); + *headers = g_slist_prepend (*headers, gst_buffer_ref (buf2)); + *headers = g_slist_prepend (*headers, gst_buffer_ref (buf1)); } void @@ -218,6 +218,9 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, buf2 = gst_opus_enc_create_metadata_buffer (tags); gst_opus_header_create_caps_from_headers (caps, headers, buf1, buf2); + + gst_buffer_unref (buf2); + gst_buffer_unref (buf1); } gboolean From 6e48675dfc1941ebea5370bbe5ca4d7f2a9e213c Mon Sep 17 00:00:00 2001 From: Sebastian Rasmussen Date: Sat, 9 Aug 2014 14:24:59 +0200 Subject: [PATCH 148/197] opus: Improve annotation of internal function https://bugzilla.gnome.org/show_bug.cgi?id=734543 --- ext/opus/gstopusheader.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 3011716403..d190cc227b 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -96,16 +96,17 @@ gst_opus_enc_create_metadata_buffer (const GstTagList * tags) /* * (really really) FIXME: move into core (dixit tpm) */ -/** +/* * _gst_caps_set_buffer_array: - * @caps: a #GstCaps + * @caps: (transfer full): a #GstCaps * @field: field in caps to set * @buf: header buffers * * Adds given buffers to an array of buffers set as the given @field * on the given @caps. List of buffer arguments must be NULL-terminated. * - * Returns: input caps with a streamheader field added, or NULL if some error + * Returns: (transfer full): input caps with a streamheader field added, or NULL + * if some error occurred */ static GstCaps * _gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, From d8b6375e0118f4722f9bce5459208e6f2487c9aa Mon Sep 17 00:00:00 2001 From: Sebastian Rasmussen Date: Fri, 8 Aug 2014 14:08:19 +0200 Subject: [PATCH 149/197] opusenc: Unref pad template caps after usage Fixes https://bugzilla.gnome.org/show_bug.cgi?id=734517 --- ext/opus/gstopusenc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 195d88fecd..f9cf624448 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -717,6 +717,7 @@ gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) { GstOpusEnc *enc; GstCaps *caps; + GstCaps *tcaps; GstCaps *peercaps = NULL; GstCaps *intersect = NULL; guint i; @@ -734,8 +735,9 @@ gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) (GST_AUDIO_ENCODER_SINK_PAD (benc))); } - intersect = gst_caps_intersect (peercaps, - gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc))); + tcaps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc)); + intersect = gst_caps_intersect (peercaps, tcaps); + gst_caps_unref (tcaps); gst_caps_unref (peercaps); if (gst_caps_is_empty (intersect)) @@ -756,9 +758,8 @@ gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) gst_caps_unref (intersect); - caps = - gst_caps_copy (gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD - (benc))); + caps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD (benc)); + caps = gst_caps_make_writable (caps); if (!allow_multistream) { GValue range = { 0 }; g_value_init (&range, GST_TYPE_INT_RANGE); From ebe01db234b4f444a5898566fb7b2d8a32783e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 10 Sep 2014 17:24:39 +0100 Subject: [PATCH 150/197] Fix up one-element lists in template caps --- ext/opus/gstopusdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index d2fc6465dc..c148beeff5 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -56,7 +56,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " - "format = (string) { " GST_AUDIO_NE (S16) " }, " + "format = (string) " GST_AUDIO_NE (S16) ", " "layout = (string) interleaved, " "rate = (int) { 48000, 24000, 16000, 12000, 8000 }, " "channels = (int) [ 1, 8 ] ") From fe6a1d5b8872959bea1295f1e4ad1e3708435eba Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 10 Jun 2014 09:33:40 +0100 Subject: [PATCH 151/197] opusenc: update output segment stop time to match clipped samples This will let oggmux generate a granpos on the last page that properly represents the clipped samples at the end of the stream. --- ext/opus/gstopusenc.c | 27 +++++++++++++++++++++++++++ ext/opus/gstopusenc.h | 1 + 2 files changed, 28 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f9cf624448..4d118b054c 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -329,6 +329,7 @@ gst_opus_enc_start (GstAudioEncoder * benc) GST_DEBUG_OBJECT (enc, "start"); enc->tags = gst_tag_list_new_empty (); enc->header_sent = FALSE; + enc->encoded_samples = 0; return TRUE; } @@ -704,6 +705,9 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) gst_tag_setter_merge_tags (setter, list, mode); break; } + case GST_EVENT_SEGMENT: + enc->encoded_samples = 0; + break; default: break; @@ -793,6 +797,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GstMapInfo omap; gint outsize; GstBuffer *outbuf; + GstSegment *segment; + GstClockTime duration; guint max_payload_size; gint frame_samples; @@ -813,6 +819,26 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) if (G_UNLIKELY (bsize % bytes)) { GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); + /* If encoding part of a frame, and we have no set stop time on + * the output segment, we update the segment stop time to reflect + * the last sample. This will let oggmux set the last page's + * granpos to tell a decoder the dummy samples should be clipped. + */ + segment = &GST_AUDIO_ENCODER_OUTPUT_SEGMENT (enc); + if (!GST_CLOCK_TIME_IS_VALID (segment->stop)) { + int input_samples = bsize / (enc->n_channels * 2); + GST_DEBUG_OBJECT (enc, + "No stop time and partial frame, updating segment"); + duration = + gst_util_uint64_scale (enc->encoded_samples + input_samples, + GST_SECOND, enc->sample_rate); + segment->stop = segment->start + duration; + GST_DEBUG_OBJECT (enc, "new output segment %" GST_SEGMENT_FORMAT, + segment); + gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (enc), + gst_event_new_segment (segment)); + } + size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); memcpy (mdata, bdata, bsize); @@ -864,6 +890,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, frame_samples); + enc->encoded_samples += frame_samples; done: diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 64f95e37c0..9e1d3600b2 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -74,6 +74,7 @@ struct _GstOpusEnc { gint sample_rate; gboolean header_sent; + guint64 encoded_samples; GSList *headers; From fb29483259a126006be9df0127a568a1150eb5a6 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 17 Dec 2014 21:52:13 -0300 Subject: [PATCH 152/197] opusenc: plug ref leak of template caps the pad template caps is already a new ref. No need to copy. --- ext/opus/gstopusenc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 4d118b054c..f0d69d362e 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -734,9 +734,7 @@ gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) peercaps = gst_pad_peer_query_caps (GST_AUDIO_ENCODER_SRC_PAD (benc), NULL); if (!peercaps) { GST_DEBUG_OBJECT (benc, "No peercaps, returning template sink caps"); - return - gst_caps_copy (gst_pad_get_pad_template_caps - (GST_AUDIO_ENCODER_SINK_PAD (benc))); + return gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD (benc)); } tcaps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc)); From da4f7e53873b5e1423754b294d40d622a2b4d951 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 28 Jan 2015 16:43:59 +0000 Subject: [PATCH 153/197] opusenc: change audio property to audio-type This is now an enum with values generic (default) and voice. https://bugzilla.gnome.org/show_bug.cgi?id=740891 --- ext/opus/gstopusenc.c | 60 ++++++++++++++++++++++++++++++++++++------- ext/opus/gstopusenc.h | 2 +- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f0d69d362e..3413a095fe 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -114,6 +114,28 @@ gst_opus_enc_frame_size_get_type (void) return id; } +#define GST_OPUS_ENC_TYPE_AUDIO_TYPE (gst_opus_enc_audio_type_get_type()) +static GType +gst_opus_enc_audio_type_get_type (void) +{ + static const GEnumValue values[] = { + {OPUS_APPLICATION_AUDIO, "Generic audio", "generic"}, + {OPUS_APPLICATION_VOIP, "Voice", "voice"}, + {0, NULL, NULL} + }; + static volatile GType id = 0; + + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + + _id = g_enum_register_static ("GstOpusEncAudioType", values); + + g_once_init_leave ((gsize *) & id, _id); + } + + return id; +} + #define FORMAT_STR GST_AUDIO_NE(S16) static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -132,6 +154,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", ); #define DEFAULT_AUDIO TRUE +#define DEFAULT_AUDIO_TYPE OPUS_APPLICATION_AUDIO #define DEFAULT_BITRATE 64000 #define DEFAULT_BANDWIDTH OPUS_BANDWIDTH_FULLBAND #define DEFAULT_FRAMESIZE 20 @@ -147,6 +170,7 @@ enum { PROP_0, PROP_AUDIO, + PROP_AUDIO_TYPE, PROP_BITRATE, PROP_BANDWIDTH, PROP_FRAME_SIZE, @@ -218,13 +242,18 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) base_class->getcaps = GST_DEBUG_FUNCPTR (gst_opus_enc_sink_getcaps); g_object_class_install_property (gobject_class, PROP_AUDIO, - g_param_spec_boolean ("audio", "Audio or voice", - "Audio or voice", DEFAULT_AUDIO, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_param_spec_boolean ("audio", + "Audio or voice (obsolete, use audio-type)", + "Audio or voice (obsolete, use audio-type)", DEFAULT_AUDIO, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED)); + g_object_class_install_property (gobject_class, PROP_AUDIO_TYPE, + g_param_spec_enum ("audio-type", "What type of audio to optimize for", + "What type of audio to optimize for", GST_OPUS_ENC_TYPE_AUDIO_TYPE, + DEFAULT_AUDIO_TYPE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE, g_param_spec_int ("bitrate", "Encoding Bit-rate", - "Specify an encoding bit-rate (in bps).", - LOWEST_BITRATE, HIGHEST_BITRATE, DEFAULT_BITRATE, + "Specify an encoding bit-rate (in bps).", LOWEST_BITRATE, + HIGHEST_BITRATE, DEFAULT_BITRATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_BANDWIDTH, @@ -315,6 +344,7 @@ gst_opus_enc_init (GstOpusEnc * enc) enc->dtx = DEFAULT_DTX; enc->packet_loss_percentage = DEFAULT_PACKET_LOSS_PERCENT; enc->max_payload_size = DEFAULT_MAX_PAYLOAD_SIZE; + enc->audio_type = DEFAULT_AUDIO_TYPE; /* arrange granulepos marking (and required perfect ts) */ gst_audio_encoder_set_mark_granule (benc, TRUE); @@ -658,8 +688,7 @@ gst_opus_enc_setup (GstOpusEnc * enc) enc->state = opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, enc->n_channels - enc->n_stereo_streams, enc->n_stereo_streams, enc->encoding_channel_mapping, - enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, - &error); + enc->audio_type, &error); if (!enc->state || error != OPUS_OK) goto encoder_creation_failed; @@ -952,7 +981,13 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, switch (prop_id) { case PROP_AUDIO: - g_value_set_boolean (value, enc->audio_or_voip); + g_warning + ("opusenc's audio property is obsolete, use audio-type instead"); + g_value_set_boolean (value, + enc->audio_type == OPUS_APPLICATION_AUDIO ? TRUE : FALSE); + break; + case PROP_AUDIO_TYPE: + g_value_set_enum (value, enc->audio_type); break; case PROP_BITRATE: g_value_set_int (value, enc->bitrate); @@ -1011,7 +1046,14 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_AUDIO: - enc->audio_or_voip = g_value_get_boolean (value); + g_warning + ("opusenc's audio property is obsolete, use audio-type instead"); + enc->audio_type = + g_value_get_boolean (value) ? OPUS_APPLICATION_AUDIO : + OPUS_APPLICATION_VOIP; + break; + case PROP_AUDIO_TYPE: + enc->audio_type = g_value_get_enum (value); break; case PROP_BITRATE: GST_OPUS_UPDATE_PROPERTY (bitrate, int, BITRATE); diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 9e1d3600b2..ef2d3f6b28 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -57,7 +57,7 @@ struct _GstOpusEnc { GMutex property_lock; /* properties */ - gboolean audio_or_voip; + gint audio_type; gint bitrate; gint bandwidth; gint frame_size; From 111c831c08b9a68379f30f1600eea463405c301f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 11 Feb 2015 14:16:21 +0100 Subject: [PATCH 154/197] Improve and fix LATENCY query handling This now follows the design docs everywhere, especially the maximum latency handling. https://bugzilla.gnome.org/show_bug.cgi?id=744106 --- ext/opus/gstopusdec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index c148beeff5..366b6b81ca 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -181,6 +181,9 @@ gst_opus_dec_start (GstAudioDecoder * dec) gst_audio_decoder_set_plc_aware (dec, TRUE); if (odec->use_inband_fec) { + /* FIXME: Is our maximum latency really 120ms, i.e. are we going + * to buffer up to 120ms? + */ gst_audio_decoder_set_latency (dec, 2 * GST_MSECOND + GST_MSECOND / 2, 120 * GST_MSECOND); } From db20b21a514162e63324aefa24eb386004fc79b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 18 Feb 2015 17:41:25 +0200 Subject: [PATCH 155/197] opusenc: Remove g_warnings() for the deprecated audio property Otherwise there are g_warnings() already when just using gst-inspect or dumping a pipeline graph. --- ext/opus/gstopusenc.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 3413a095fe..603fad8704 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -981,8 +981,6 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, switch (prop_id) { case PROP_AUDIO: - g_warning - ("opusenc's audio property is obsolete, use audio-type instead"); g_value_set_boolean (value, enc->audio_type == OPUS_APPLICATION_AUDIO ? TRUE : FALSE); break; @@ -1046,8 +1044,6 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_AUDIO: - g_warning - ("opusenc's audio property is obsolete, use audio-type instead"); enc->audio_type = g_value_get_boolean (value) ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP; From b8565d3ab6481e85848f810af076a87c18fa572d Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 4 Mar 2015 09:24:27 +0000 Subject: [PATCH 156/197] opusdec: fix latency query in FEC case The max latency parameter is "the maximum time an element synchronizing to the clock is allowed to wait for receiving all data for the current running time" (docs/design/part-latency.txt). https://bugzilla.gnome.org/show_bug.cgi?id=744338 --- ext/opus/gstopusdec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 366b6b81ca..cfa711a18c 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -181,11 +181,11 @@ gst_opus_dec_start (GstAudioDecoder * dec) gst_audio_decoder_set_plc_aware (dec, TRUE); if (odec->use_inband_fec) { - /* FIXME: Is our maximum latency really 120ms, i.e. are we going - * to buffer up to 120ms? + /* opusdec outputs samples directly from an input buffer, except if + * FEC is on, in which case it buffers one buffer in case one buffer + * goes missing. */ - gst_audio_decoder_set_latency (dec, 2 * GST_MSECOND + GST_MSECOND / 2, - 120 * GST_MSECOND); + gst_audio_decoder_set_latency (dec, 120 * GST_MSECOND, 120 * GST_MSECOND); } return TRUE; From fcce2fe05913d1ea0fdf6ab656951b9a010fb2b9 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 12 Mar 2015 12:49:40 +0000 Subject: [PATCH 157/197] opusenc: replace cbr and constrained-vbr properties with an enum It was deemed confusing before. https://bugzilla.gnome.org/show_bug.cgi?id=744909 --- ext/opus/gstopusenc.c | 87 +++++++++++++++++++++++++++++++++++++------ ext/opus/gstopusenc.h | 10 ++++- 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 603fad8704..cb3f276f4c 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -136,6 +136,29 @@ gst_opus_enc_audio_type_get_type (void) return id; } +#define GST_OPUS_ENC_TYPE_BITRATE_TYPE (gst_opus_enc_bitrate_type_get_type()) +static GType +gst_opus_enc_bitrate_type_get_type (void) +{ + static const GEnumValue values[] = { + {BITRATE_TYPE_CBR, "CBR", "cbr"}, + {BITRATE_TYPE_VBR, "VBR", "vbr"}, + {BITRATE_TYPE_CONSTRAINED_VBR, "Constrained VBR", "constrained-vbr"}, + {0, NULL, NULL} + }; + static volatile GType id = 0; + + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + + _id = g_enum_register_static ("GstOpusEncBitrateType", values); + + g_once_init_leave ((gsize *) & id, _id); + } + + return id; +} + #define FORMAT_STR GST_AUDIO_NE(S16) static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -160,6 +183,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", #define DEFAULT_FRAMESIZE 20 #define DEFAULT_CBR TRUE #define DEFAULT_CONSTRAINED_VBR TRUE +#define DEFAULT_BITRATE_TYPE BITRATE_TYPE_CBR #define DEFAULT_COMPLEXITY 10 #define DEFAULT_INBAND_FEC FALSE #define DEFAULT_DTX FALSE @@ -176,6 +200,7 @@ enum PROP_FRAME_SIZE, PROP_CBR, PROP_CONSTRAINED_VBR, + PROP_BITRATE_TYPE, PROP_COMPLEXITY, PROP_INBAND_FEC, PROP_DTX, @@ -271,11 +296,17 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) g_param_spec_boolean ("cbr", "Constant bit rate", "Constant bit rate", DEFAULT_CBR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | - GST_PARAM_MUTABLE_PLAYING)); + GST_PARAM_MUTABLE_PLAYING | G_PARAM_DEPRECATED)); g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR, g_param_spec_boolean ("constrained-vbr", "Constrained VBR", "Constrained VBR", DEFAULT_CONSTRAINED_VBR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING | G_PARAM_DEPRECATED)); + g_object_class_install_property (gobject_class, PROP_BITRATE_TYPE, + g_param_spec_enum ("bitrate-type", "Bitrate type", + "Bitrate type", GST_OPUS_ENC_TYPE_BITRATE_TYPE, + DEFAULT_BITRATE_TYPE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_COMPLEXITY, g_param_spec_int ("complexity", "Complexity", "Complexity", 0, 10, @@ -337,8 +368,7 @@ gst_opus_enc_init (GstOpusEnc * enc) enc->bitrate = DEFAULT_BITRATE; enc->bandwidth = DEFAULT_BANDWIDTH; enc->frame_size = DEFAULT_FRAMESIZE; - enc->cbr = DEFAULT_CBR; - enc->constrained_vbr = DEFAULT_CONSTRAINED_VBR; + enc->bitrate_type = DEFAULT_BITRATE_TYPE; enc->complexity = DEFAULT_COMPLEXITY; enc->inband_fec = DEFAULT_INBAND_FEC; enc->dtx = DEFAULT_DTX; @@ -695,9 +725,11 @@ gst_opus_enc_setup (GstOpusEnc * enc) opus_multistream_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); - opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr), 0); opus_multistream_encoder_ctl (enc->state, - OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), 0); + OPUS_SET_VBR (enc->bitrate_type != BITRATE_TYPE_CBR), 0); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_VBR_CONSTRAINT (enc->bitrate_type == + BITRATE_TYPE_CONSTRAINED_VBR), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0); opus_multistream_encoder_ctl (enc->state, @@ -997,10 +1029,16 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_enum (value, enc->frame_size); break; case PROP_CBR: - g_value_set_boolean (value, enc->cbr); + g_warning ("cbr property is deprecated; use bitrate-type instead"); + g_value_set_boolean (value, enc->bitrate_type == BITRATE_TYPE_CBR); break; case PROP_CONSTRAINED_VBR: - g_value_set_boolean (value, enc->constrained_vbr); + g_warning + ("constrained-vbr property is deprecated; use bitrate-type instead"); + g_value_set_boolean (value, + enc->bitrate_type == BITRATE_TYPE_CONSTRAINED_VBR); + case PROP_BITRATE_TYPE: + g_value_set_enum (value, enc->bitrate_type); break; case PROP_COMPLEXITY: g_value_set_int (value, enc->complexity); @@ -1065,15 +1103,40 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, g_mutex_unlock (&enc->property_lock); break; case PROP_CBR: - /* this one has an opposite meaning to the opus ctl... */ + g_warning ("cbr property is deprecated; use bitrate-type instead"); g_mutex_lock (&enc->property_lock); - enc->cbr = g_value_get_boolean (value); - if (enc->state) - opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); + enc->bitrate_type = BITRATE_TYPE_CBR; + if (enc->state) { + opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (FALSE)); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_VBR_CONSTRAINT (FALSE), 0); + } g_mutex_unlock (&enc->property_lock); break; case PROP_CONSTRAINED_VBR: - GST_OPUS_UPDATE_PROPERTY (constrained_vbr, boolean, VBR_CONSTRAINT); + g_warning + ("constrained-vbr property is deprecated; use bitrate-type instead"); + g_mutex_lock (&enc->property_lock); + enc->bitrate_type = BITRATE_TYPE_CONSTRAINED_VBR; + if (enc->state) { + opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (TRUE)); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_VBR_CONSTRAINT (TRUE), 0); + } + g_mutex_unlock (&enc->property_lock); + break; + case PROP_BITRATE_TYPE: + /* this one has an opposite meaning to the opus ctl... */ + g_mutex_lock (&enc->property_lock); + enc->bitrate_type = g_value_get_enum (value); + if (enc->state) { + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_VBR (enc->bitrate_type != BITRATE_TYPE_CBR)); + opus_multistream_encoder_ctl (enc->state, + OPUS_SET_VBR_CONSTRAINT (enc->bitrate_type == + BITRATE_TYPE_CONSTRAINED_VBR), 0); + } + g_mutex_unlock (&enc->property_lock); break; case PROP_COMPLEXITY: GST_OPUS_UPDATE_PROPERTY (complexity, int, COMPLEXITY); diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index ef2d3f6b28..4ff9610d6b 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -45,6 +45,13 @@ G_BEGIN_DECLS #define MAX_FRAME_SIZE 2000*2 #define MAX_FRAME_BYTES 2000 +typedef enum +{ + BITRATE_TYPE_CBR, + BITRATE_TYPE_VBR, + BITRATE_TYPE_CONSTRAINED_VBR, +} GstOpusEncBitrateType; + typedef struct _GstOpusEnc GstOpusEnc; typedef struct _GstOpusEncClass GstOpusEncClass; @@ -61,8 +68,7 @@ struct _GstOpusEnc { gint bitrate; gint bandwidth; gint frame_size; - gboolean cbr; - gboolean constrained_vbr; + GstOpusEncBitrateType bitrate_type; gint complexity; gboolean inband_fec; gboolean dtx; From e252b13dd00c1a237b08dd34ffbfeca93180d815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 11:56:09 +0100 Subject: [PATCH 158/197] opusheader: Put number of channels and sample rate into the caps https://bugzilla.gnome.org/show_bug.cgi?id=746617 --- ext/opus/gstopusheader.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index d190cc227b..53969602b4 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -157,6 +157,7 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, GstBuffer * buf1, GstBuffer * buf2) { int n_streams, family; + gint channels, rate; gboolean multistream; GstMapInfo map; guint8 *data; @@ -168,6 +169,9 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, gst_buffer_map (buf1, &map, GST_MAP_READ); data = map.data; + channels = data[9]; + rate = GST_READ_UINT32_LE (data + 12); + /* work out the number of streams */ family = data[18]; if (family == 0) { @@ -183,12 +187,16 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, } } + /* TODO: should probably also put the channel mapping into the caps too once + * we actually support multi channel, and pre-skip and other fields */ + gst_buffer_unmap (buf1, &map); /* mark and put on caps */ multistream = n_streams > 1; *caps = gst_caps_new_simple ("audio/x-opus", - "multistream", G_TYPE_BOOLEAN, multistream, NULL); + "multistream", G_TYPE_BOOLEAN, multistream, + "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); *headers = g_slist_prepend (*headers, gst_buffer_ref (buf2)); From ef29b92990665d4db51d252afe6178ad5501011c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 11:57:09 +0100 Subject: [PATCH 159/197] opusdec: Take output sample rate from the stream headers too This way we let opusdec do the resampling if needed and don't carry around buffers with a too high sample rate if not required. While Opus always uses 48kHz internally, this information from the header specifies which frequencies are safe to drop. --- ext/opus/gstopusdec.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index cfa711a18c..2b7bcb2834 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -224,13 +224,11 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) caps = gst_caps_truncate (caps); caps = gst_caps_make_writable (caps); s = gst_caps_get_structure (caps, 0); - gst_structure_fixate_field_nearest_int (s, "rate", 48000); + gst_structure_fixate_field_nearest_int (s, "rate", dec->sample_rate); gst_structure_get_int (s, "rate", &dec->sample_rate); gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); gst_structure_get_int (s, "channels", &dec->n_channels); gst_caps_unref (caps); - } else { - dec->sample_rate = 48000; } GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, @@ -282,6 +280,7 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) } dec->n_channels = data[9]; + dec->sample_rate = GST_READ_UINT32_LE (data + 12); dec->pre_skip = GST_READ_UINT16_LE (data + 10); dec->r128_gain = GST_READ_UINT16_LE (data + 16); dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); From 65662c89978df493c2005c87bcd375db9178b51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 12:07:52 +0100 Subject: [PATCH 160/197] opusdec: Reset the decoder if the caps change --- ext/opus/gstopusdec.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 2b7bcb2834..7cffd175ba 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -541,9 +541,22 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) gboolean ret = TRUE; GstStructure *s; const GValue *streamheader; + GstCaps *old_caps; GST_DEBUG_OBJECT (dec, "set_format: %" GST_PTR_FORMAT, caps); + if ((old_caps = gst_pad_get_current_caps (GST_AUDIO_DECODER_SINK_PAD (bdec)))) { + if (gst_caps_is_equal (caps, old_caps)) { + gst_caps_unref (old_caps); + GST_DEBUG_OBJECT (dec, "caps didn't change"); + goto done; + } + + GST_DEBUG_OBJECT (dec, "caps have changed, resetting decoder"); + gst_opus_dec_reset (dec); + gst_caps_unref (old_caps); + } + s = gst_caps_get_structure (caps, 0); if ((streamheader = gst_structure_get_value (s, "streamheader")) && G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && From dd674942baf568cc6b0650f5350944ba9ce96d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 12:09:25 +0100 Subject: [PATCH 161/197] opusdec: Take channels and sample rate from the caps if we have no stream header --- ext/opus/gstopusdec.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 7cffd175ba..d3c8e8d9cd 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -582,6 +582,22 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) goto done; gst_buffer_replace (&dec->vorbiscomment, buf); } + } else { + /* defaults if not in the caps */ + dec->n_channels = 2; + dec->sample_rate = 48000; + + gst_structure_get_int (s, "channels", &dec->n_channels); + gst_structure_get_int (s, "rate", &dec->sample_rate); + + /* default stereo mapping */ + dec->channel_mapping_family = 0; + dec->channel_mapping[0] = 0; + dec->channel_mapping[1] = 1; + dec->n_streams = 1; + dec->n_stereo_streams = 1; + + gst_opus_dec_negotiate (dec, NULL); } done: From 37d87bf3527a352e5b67a24933f9312587681426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 13:11:42 +0100 Subject: [PATCH 162/197] opusenc: Remove useless headers and header_sent variables from the instance struct They are only used inside a single function. --- ext/opus/gstopusenc.c | 16 ++-------------- ext/opus/gstopusenc.h | 3 --- ext/opus/gstopusheader.c | 10 ++++++---- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index cb3f276f4c..d31962ea68 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -388,7 +388,6 @@ gst_opus_enc_start (GstAudioEncoder * benc) GST_DEBUG_OBJECT (enc, "start"); enc->tags = gst_tag_list_new_empty (); - enc->header_sent = FALSE; enc->encoded_samples = 0; return TRUE; @@ -400,16 +399,12 @@ gst_opus_enc_stop (GstAudioEncoder * benc) GstOpusEnc *enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "stop"); - enc->header_sent = FALSE; if (enc->state) { opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } gst_tag_list_unref (enc->tags); enc->tags = NULL; - g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); - g_slist_free (enc->headers); - enc->headers = NULL; gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; @@ -971,26 +966,19 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "handle_frame"); - if (!enc->header_sent) { + if (!gst_pad_has_current_caps (GST_AUDIO_ENCODER_SRC_PAD (benc))) { GstCaps *caps; - g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); - g_slist_free (enc->headers); - enc->headers = NULL; - - gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, + gst_opus_header_create_caps (&caps, NULL, enc->n_channels, enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, enc->decoding_channel_mapping, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); - /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_audio_encoder_set_output_format (benc, caps); gst_caps_unref (caps); - - enc->header_sent = TRUE; } GST_DEBUG_OBJECT (enc, "received buffer %p of %" G_GSIZE_FORMAT " bytes", buf, diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 4ff9610d6b..80ac328605 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -79,11 +79,8 @@ struct _GstOpusEnc { gint n_channels; gint sample_rate; - gboolean header_sent; guint64 encoded_samples; - GSList *headers; - GstTagList *tags; guint8 channel_mapping_family; diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 53969602b4..6155ac0696 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -163,7 +163,7 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, guint8 *data; g_return_if_fail (caps); - g_return_if_fail (headers && !*headers); + g_return_if_fail (!headers || !*headers); g_return_if_fail (gst_buffer_get_size (buf1) >= 19); gst_buffer_map (buf1, &map, GST_MAP_READ); @@ -199,8 +199,10 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL); *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); - *headers = g_slist_prepend (*headers, gst_buffer_ref (buf2)); - *headers = g_slist_prepend (*headers, gst_buffer_ref (buf1)); + if (headers) { + *headers = g_slist_prepend (*headers, gst_buffer_ref (buf2)); + *headers = g_slist_prepend (*headers, gst_buffer_ref (buf1)); + } } void @@ -211,7 +213,7 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, GstBuffer *buf1, *buf2; g_return_if_fail (caps); - g_return_if_fail (headers && !*headers); + g_return_if_fail (!headers || !*headers); g_return_if_fail (nchannels > 0); g_return_if_fail (sample_rate >= 0); /* 0 -> unset */ g_return_if_fail (channel_mapping_family == 0 || channel_mapping); From 78634dc2c384e2b39b35ab8f06133fbac7dc53e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 13:13:35 +0100 Subject: [PATCH 163/197] opusenc: Remove another unused variable --- ext/opus/gstopusenc.c | 3 --- ext/opus/gstopusenc.h | 2 -- 2 files changed, 5 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index d31962ea68..5f897c82fd 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -387,7 +387,6 @@ gst_opus_enc_start (GstAudioEncoder * benc) GstOpusEnc *enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "start"); - enc->tags = gst_tag_list_new_empty (); enc->encoded_samples = 0; return TRUE; @@ -403,8 +402,6 @@ gst_opus_enc_stop (GstAudioEncoder * benc) opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } - gst_tag_list_unref (enc->tags); - enc->tags = NULL; gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 80ac328605..02f47d2dba 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -81,8 +81,6 @@ struct _GstOpusEnc { guint64 encoded_samples; - GstTagList *tags; - guint8 channel_mapping_family; guint8 encoding_channel_mapping[256]; guint8 decoding_channel_mapping[256]; From 6f33c20b00cf23ededb6324453be8d8115db6b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 23 Mar 2015 13:15:30 +0100 Subject: [PATCH 164/197] opusenc: Set output format immediately after creating the encoder instance We know the caps by then, there's no need to wait until we actually receive the first buffer. --- ext/opus/gstopusenc.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 5f897c82fd..ef1553fc3a 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -693,6 +693,8 @@ static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; + GstCaps *caps; + gboolean ret; #ifndef GST_DISABLE_GST_DEBUG GST_DEBUG_OBJECT (enc, @@ -732,7 +734,18 @@ gst_opus_enc_setup (GstOpusEnc * enc) GST_LOG_OBJECT (enc, "we have frame size %d", enc->frame_size); - return TRUE; + gst_opus_header_create_caps (&caps, NULL, enc->n_channels, + enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, + enc->decoding_channel_mapping, + gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); + + /* negotiate with these caps */ + GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); + + ret = gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (enc), caps); + gst_caps_unref (caps); + + return ret; encoder_creation_failed: GST_ERROR_OBJECT (enc, "Encoder creation failed"); @@ -962,22 +975,6 @@ gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "handle_frame"); - - if (!gst_pad_has_current_caps (GST_AUDIO_ENCODER_SRC_PAD (benc))) { - GstCaps *caps; - - gst_opus_header_create_caps (&caps, NULL, enc->n_channels, - enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, - enc->decoding_channel_mapping, - gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); - - /* negotiate with these caps */ - GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); - - gst_audio_encoder_set_output_format (benc, caps); - gst_caps_unref (caps); - } - GST_DEBUG_OBJECT (enc, "received buffer %p of %" G_GSIZE_FORMAT " bytes", buf, buf ? gst_buffer_get_size (buf) : 0); From 56485e3b14365b92b7f7e1a2d514a628e353c975 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Tue, 24 Mar 2015 15:13:52 +0000 Subject: [PATCH 165/197] opusenc: fall through switch statement Adding a comment makes coverity happy and quells the issue. CID 1291629 --- ext/opus/gstopusenc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index ef1553fc3a..f75175c1e5 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1019,6 +1019,7 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, ("constrained-vbr property is deprecated; use bitrate-type instead"); g_value_set_boolean (value, enc->bitrate_type == BITRATE_TYPE_CONSTRAINED_VBR); + /* fall through */ case PROP_BITRATE_TYPE: g_value_set_enum (value, enc->bitrate_type); break; From 20af81edb5342fc983795d2cac33cb4c1ff14998 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Fri, 3 Apr 2015 11:46:12 +0530 Subject: [PATCH 166/197] opus: Fix incorrect fall-through condition in property getter --- ext/opus/gstopusenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index f75175c1e5..0d6612bfd5 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1019,7 +1019,7 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, ("constrained-vbr property is deprecated; use bitrate-type instead"); g_value_set_boolean (value, enc->bitrate_type == BITRATE_TYPE_CONSTRAINED_VBR); - /* fall through */ + break; case PROP_BITRATE_TYPE: g_value_set_enum (value, enc->bitrate_type); break; From 443cc3baab80d00d124ea415ee92c2a966363efe Mon Sep 17 00:00:00 2001 From: Mersad Jelacic Date: Tue, 28 Apr 2015 16:58:21 +0200 Subject: [PATCH 167/197] opus: don't use deprecated gst_buffer_new_and_alloc Use the helper function available in the base class instead. https://bugzilla.gnome.org/show_bug.cgi?id=748585 --- ext/opus/gstopusdec.c | 4 +++- ext/opus/gstopusenc.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index d3c8e8d9cd..a634787699 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -431,7 +431,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) samples = 120 * dec->sample_rate / 1000; packet_size = samples * dec->n_channels * 2; - outbuf = gst_buffer_new_and_alloc (packet_size); + outbuf = + gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (dec), + packet_size); if (!outbuf) { goto buffer_failed; } diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 0d6612bfd5..566992077e 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -918,7 +918,9 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) g_assert (size == bytes); - outbuf = gst_buffer_new_and_alloc (max_payload_size * enc->n_channels); + outbuf = + gst_audio_encoder_allocate_output_buffer (GST_AUDIO_ENCODER (enc), + max_payload_size * enc->n_channels); if (!outbuf) goto done; From 97c3e548d66b8a1bd1f0fc7c7047a69f0896c4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 28 Apr 2015 17:24:04 +0100 Subject: [PATCH 168/197] opus: fix includes and compilation against opus in non-standard prefix https://bugzilla.gnome.org/show_bug.cgi?id=748594 --- ext/opus/gstopusdec.h | 2 +- ext/opus/gstopusenc.c | 2 +- ext/opus/gstopusenc.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 72487787cb..4a270fe7bb 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -24,7 +24,7 @@ #include #include -#include +#include G_BEGIN_DECLS diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 566992077e..4bac854d2c 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 02f47d2dba..43eaac8d27 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -27,7 +27,7 @@ #include #include -#include +#include G_BEGIN_DECLS From 8dde6b170bfa2058fadbd73df714541129fb19aa Mon Sep 17 00:00:00 2001 From: Jose Antonio Santos Cadenas Date: Mon, 4 May 2015 10:35:55 +0200 Subject: [PATCH 169/197] opusheader: Do not include rate in caps if it is 0 As expressed in gst_opus_header_create_caps, value 0 means unset. Setting rate value to 0 make negotiation with decoder fail. https://bugzilla.gnome.org/show_bug.cgi?id=748875 --- ext/opus/gstopusheader.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 6155ac0696..1521c1c071 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -196,7 +196,12 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, multistream = n_streams > 1; *caps = gst_caps_new_simple ("audio/x-opus", "multistream", G_TYPE_BOOLEAN, multistream, - "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL); + "channels", G_TYPE_INT, channels, NULL); + + if (rate > 0) { + gst_caps_set_simple (*caps, "rate", G_TYPE_INT, rate, NULL); + } + *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); if (headers) { From ee3135c4a677e27a1256c6c4728c11bd1a5885d8 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Tue, 2 Jun 2015 16:02:37 +0200 Subject: [PATCH 170/197] check: Use GST_CHECK_MAIN () macro everywhere Makes source code smaller, and ensures we go through common initialization path (like the one that sets up XML unit test output ...) --- tests/check/elements/opus.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index e7f9c3a9d7..3afeb4ed53 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -375,19 +375,4 @@ opus_suite (void) return s; } -int -main (int argc, char **argv) -{ - int nf; - - Suite *s = opus_suite (); - SRunner *sr = srunner_create (s); - - gst_check_init (&argc, &argv); - - srunner_run_all (sr, CK_NORMAL); - nf = srunner_ntests_failed (sr); - srunner_free (sr); - - return nf; -} +GST_CHECK_MAIN (opus); From cecb83e590714c376253dad1d769b3a78162daea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 4 Jun 2015 11:45:05 +0200 Subject: [PATCH 171/197] opusdec: gst_structure_fixate_field_nearest_int() only works if the structure has this field Just set the rate/channels directly if the caps don't have this field. --- ext/opus/gstopusdec.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index a634787699..bd9847b0ea 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -224,10 +224,19 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) caps = gst_caps_truncate (caps); caps = gst_caps_make_writable (caps); s = gst_caps_get_structure (caps, 0); - gst_structure_fixate_field_nearest_int (s, "rate", dec->sample_rate); + + if (gst_structure_has_field (s, "rate")) + gst_structure_fixate_field_nearest_int (s, "rate", dec->sample_rate); + else + gst_structure_set (s, "rate", G_TYPE_INT, dec->sample_rate, NULL); gst_structure_get_int (s, "rate", &dec->sample_rate); - gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); + + if (gst_structure_has_field (s, "channels")) + gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); + else + gst_structure_set (s, "channels", G_TYPE_INT, dec->n_channels, NULL); gst_structure_get_int (s, "channels", &dec->n_channels); + gst_caps_unref (caps); } From 0ea7a89c1429507f29a761da34bbde56307eea6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 4 Jun 2015 11:54:24 +0200 Subject: [PATCH 172/197] opusdec: If channel/rate negotiation fails, fall back to stereo and 48kHz --- ext/opus/gstopusdec.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index bd9847b0ea..c88b9eeddf 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -240,6 +240,17 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) gst_caps_unref (caps); } + if (dec->n_channels == 0) { + GST_DEBUG_OBJECT (dec, "Using a default of 2 channels"); + dec->n_channels = 2; + pos = NULL; + } + + if (dec->sample_rate == 0) { + GST_DEBUG_OBJECT (dec, "Using a default of 48kHz sample rate"); + dec->sample_rate = 48000; + } + GST_INFO_OBJECT (dec, "Negotiated %d channels, %d Hz", dec->n_channels, dec->sample_rate); From de5500b6a9b1d771fea440c6a238e19c495e38fb Mon Sep 17 00:00:00 2001 From: Mersad Jelacic Date: Mon, 15 Jun 2015 13:43:53 +0200 Subject: [PATCH 173/197] opusenc: Add bitrate to the tags https://bugzilla.gnome.org/show_bug.cgi?id=750992 --- ext/opus/gstopusenc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 4bac854d2c..70c7718067 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -221,6 +221,7 @@ static void gst_opus_enc_get_property (GObject * object, guint prop_id, static void gst_opus_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_opus_enc_set_tags (GstOpusEnc * enc); static gboolean gst_opus_enc_start (GstAudioEncoder * benc); static gboolean gst_opus_enc_stop (GstAudioEncoder * benc); static gboolean gst_opus_enc_set_format (GstAudioEncoder * benc, @@ -236,6 +237,22 @@ G_DEFINE_TYPE_WITH_CODE (GstOpusEnc, gst_opus_enc, GST_TYPE_AUDIO_ENCODER, G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL); G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL)); +static void +gst_opus_enc_set_tags (GstOpusEnc * enc) +{ + GstTagList *taglist; + + /* create a taglist and add a bitrate tag to it */ + taglist = gst_tag_list_new_empty (); + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_BITRATE, enc->bitrate, NULL); + + gst_audio_encoder_merge_tags (GST_AUDIO_ENCODER (enc), taglist, + GST_TAG_MERGE_REPLACE); + + gst_tag_list_unref (taglist); +} + static void gst_opus_enc_class_init (GstOpusEncClass * klass) { @@ -679,6 +696,9 @@ gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info) return FALSE; } + /* update the tags */ + gst_opus_enc_set_tags (enc); + enc->frame_samples = gst_opus_enc_get_frame_samples (enc); /* feedback to base class */ From e5409361b8c18442d103dbef3ad3c0afa3b781f0 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Mon, 20 Apr 2015 15:04:56 +0200 Subject: [PATCH 174/197] opusdec: Fix PLC frame size calculations Previously, PLC frames always had a length of 120ms, which caused audio quality degradation and synchronization errors. Fix this by calculating an appropriate length for the PLC frame. The length must be a multiple of 2.5ms. Calculate a multiple of 2.5ms that is nearest to the current PLC length. Any leftover PLC length that didn't make it into this frame is accumulated for the next PLC frame. https://bugzilla.gnome.org/show_bug.cgi?id=725167 --- ext/opus/gstopusdec.c | 53 +++++++++++++++++++++++++++++++++++++++---- ext/opus/gstopusdec.h | 2 ++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index c88b9eeddf..20eb442455 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -157,6 +157,7 @@ gst_opus_dec_reset (GstOpusDec * dec) dec->r128_gain = 0; dec->sample_rate = 0; dec->n_channels = 0; + dec->leftover_plc_duration = 0; } static void @@ -446,9 +447,53 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) size = 0; } - /* use maximum size (120 ms) as the number of returned samples is - not constant over the stream. */ - samples = 120 * dec->sample_rate / 1000; + if (gst_buffer_get_size (buffer) == 0) { + GstClockTime const opus_plc_alignment = 2500 * GST_USECOND; + GstClockTime aligned_missing_duration; + GstClockTime missing_duration = GST_BUFFER_DURATION (buffer); + + GST_DEBUG_OBJECT (dec, + "missing buffer, doing PLC duration %" GST_TIME_FORMAT + " plus leftover %" GST_TIME_FORMAT, GST_TIME_ARGS (missing_duration), + GST_TIME_ARGS (dec->leftover_plc_duration)); + + /* add the leftover PLC duration to that of the buffer */ + missing_duration += dec->leftover_plc_duration; + + /* align the combined buffer and leftover PLC duration to multiples + * of 2.5ms, always rounding down, and store excess duration for later */ + aligned_missing_duration = + (missing_duration / opus_plc_alignment) * opus_plc_alignment; + dec->leftover_plc_duration = missing_duration - aligned_missing_duration; + + /* Opus' PLC cannot operate with less than 2.5ms; skip PLC + * and accumulate the missing duration in the leftover_plc_duration + * for the next PLC attempt */ + if (aligned_missing_duration < opus_plc_alignment) { + GST_DEBUG_OBJECT (dec, + "current duration %" GST_TIME_FORMAT + " of missing data not enough for PLC (minimum needed: %" + GST_TIME_FORMAT ") - skipping", GST_TIME_ARGS (missing_duration), + GST_TIME_ARGS (opus_plc_alignment)); + goto done; + } + + /* convert the duration (in nanoseconds) to sample count */ + samples = + gst_util_uint64_scale_int (aligned_missing_duration, dec->sample_rate, + GST_SECOND); + + GST_DEBUG_OBJECT (dec, + "calculated PLC frame length: %" GST_TIME_FORMAT + " num frame samples: %d new leftover: %" GST_TIME_FORMAT, + GST_TIME_ARGS (aligned_missing_duration), samples, + GST_TIME_ARGS (dec->leftover_plc_duration)); + } else { + /* use maximum size (120 ms) as the number of returned samples is + not constant over the stream. */ + samples = 120 * dec->sample_rate / 1000; + } + packet_size = samples * dec->n_channels * 2; outbuf = @@ -462,7 +507,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) out_data = (gint16 *) omap.data; if (dec->use_inband_fec) { - if (dec->last_buffer) { + if (gst_buffer_get_size (dec->last_buffer) > 0) { /* normal delayed decode */ GST_LOG_OBJECT (dec, "FEC enabled, decoding last delayed buffer"); n = opus_multistream_decode (dec->state, data, size, out_data, samples, diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index 4a270fe7bb..f8d43a9266 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -71,6 +71,8 @@ struct _GstOpusDec { gboolean use_inband_fec; GstBuffer *last_buffer; gboolean primed; + + guint64 leftover_plc_duration; }; struct _GstOpusDecClass { From f04d4dd7c8bc4fe1f662ecbe0c7da6da224299be Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 27 Jul 2015 18:39:13 +0530 Subject: [PATCH 175/197] opuscommon: Use GString instead of snprintf for concating Safer, easier to understand, and more portable. Also, skip all this if the log level is too low. --- ext/opus/gstopuscommon.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ext/opus/gstopuscommon.c b/ext/opus/gstopuscommon.c index 227d44a0d4..febccd85f4 100644 --- a/ext/opus/gstopuscommon.c +++ b/ext/opus/gstopuscommon.c @@ -94,13 +94,18 @@ gst_opus_common_log_channel_mapping_table (GstElement * element, GstDebugCategory * category, const char *msg, int n_channels, const guint8 * table) { - char s[8 + 256 * 4] = "[ "; /* enough for 256 times "255 " at most */ int n; + GString *s; + if (gst_debug_category_get_threshold (category) < GST_LEVEL_INFO) + return; + + s = g_string_new ("[ "); for (n = 0; n < n_channels; ++n) { - size_t len = strlen (s); - snprintf (s + len, sizeof (s) - len, "%d ", table[n]); + g_string_append_printf (s, "%d ", table[n]); } - strcat (s, "]"); - GST_CAT_LEVEL_LOG (category, GST_LEVEL_INFO, element, "%s: %s", msg, s); + g_string_append (s, "]"); + + GST_CAT_LEVEL_LOG (category, GST_LEVEL_INFO, element, "%s: %s", msg, s->str); + g_string_free (s, TRUE); } From eada1366bcba4467b3d91b9b4e548814df239206 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sat, 15 Aug 2015 12:58:40 -0300 Subject: [PATCH 176/197] audiodecoders: use default pad accept-caps handling Avoids useless check of downstream caps when handling an accept-caps query Elements: dtsdec, faad, gsmdec, mpg123audiodec, opusdec, sbcdec, adpcmdec, sirendec --- ext/opus/gstopusdec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 20eb442455..d8a7eb85fa 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -167,6 +167,9 @@ gst_opus_dec_init (GstOpusDec * dec) dec->apply_gain = DEFAULT_APPLY_GAIN; gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (dec), TRUE); + gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST + (dec), TRUE); + GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (dec)); gst_opus_dec_reset (dec); } From f1cbca94805a1c936948ed9ddeda62491c3d89e2 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sun, 16 Aug 2015 07:18:34 -0300 Subject: [PATCH 177/197] audioencoders: use template subset check for accept-caps It is faster than doing a query that propagates downstream and should be enough Elements: faac, gsmenc, opusenc, sbcenc, voamrwbenc, adpcmenc, sirenenc --- ext/opus/gstopusenc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 70c7718067..19b6d6e9f3 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -376,6 +376,8 @@ gst_opus_enc_init (GstOpusEnc * enc) GST_DEBUG_OBJECT (enc, "init"); + GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (enc)); + g_mutex_init (&enc->property_lock); enc->n_channels = -1; From 16e93d206f91475ef560921ddf913139b06bbe25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= Date: Fri, 11 Sep 2015 11:11:09 +0200 Subject: [PATCH 178/197] opusenc: do not throw g_warning when getting deprecated properties https://bugzilla.gnome.org/show_bug.cgi?id=754819 --- ext/opus/gstopusenc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 19b6d6e9f3..e47b9eee04 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1035,12 +1035,13 @@ gst_opus_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_enum (value, enc->frame_size); break; case PROP_CBR: - g_warning ("cbr property is deprecated; use bitrate-type instead"); + GST_WARNING_OBJECT (enc, + "cbr property is deprecated; use bitrate-type instead"); g_value_set_boolean (value, enc->bitrate_type == BITRATE_TYPE_CBR); break; case PROP_CONSTRAINED_VBR: - g_warning - ("constrained-vbr property is deprecated; use bitrate-type instead"); + GST_WARNING_OBJECT (enc, + "constrained-vbr property is deprecated; use bitrate-type instead"); g_value_set_boolean (value, enc->bitrate_type == BITRATE_TYPE_CONSTRAINED_VBR); break; @@ -1110,6 +1111,8 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, g_mutex_unlock (&enc->property_lock); break; case PROP_CBR: + GST_WARNING_OBJECT (enc, + "cbr property is deprecated; use bitrate-type instead"); g_warning ("cbr property is deprecated; use bitrate-type instead"); g_mutex_lock (&enc->property_lock); enc->bitrate_type = BITRATE_TYPE_CBR; @@ -1121,6 +1124,8 @@ gst_opus_enc_set_property (GObject * object, guint prop_id, g_mutex_unlock (&enc->property_lock); break; case PROP_CONSTRAINED_VBR: + GST_WARNING_OBJECT (enc, + "constrained-vbr property is deprecated; use bitrate-type instead"); g_warning ("constrained-vbr property is deprecated; use bitrate-type instead"); g_mutex_lock (&enc->property_lock); From a4ffb14c558ff55b48c51d6f4c45574373e479c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Par=C3=ADs=20D=C3=ADaz?= Date: Fri, 11 Sep 2015 11:22:35 +0200 Subject: [PATCH 179/197] opusenc: improve deprecated properties docs https://bugzilla.gnome.org/show_bug.cgi?id=754819 --- ext/opus/gstopusenc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index e47b9eee04..7fad248816 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -285,8 +285,8 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) g_object_class_install_property (gobject_class, PROP_AUDIO, g_param_spec_boolean ("audio", - "Audio or voice (obsolete, use audio-type)", - "Audio or voice (obsolete, use audio-type)", DEFAULT_AUDIO, + "Audio or voice", + "Audio or voice (DEPRECATED: use audio-type)", DEFAULT_AUDIO, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED)); g_object_class_install_property (gobject_class, PROP_AUDIO_TYPE, g_param_spec_enum ("audio-type", "What type of audio to optimize for", @@ -310,19 +310,19 @@ gst_opus_enc_class_init (GstOpusEncClass * klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_CBR, - g_param_spec_boolean ("cbr", "Constant bit rate", "Constant bit rate", - DEFAULT_CBR, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | - GST_PARAM_MUTABLE_PLAYING | G_PARAM_DEPRECATED)); + g_param_spec_boolean ("cbr", "Constant bit rate", + "Constant bit rate (DEPRECATED: use bitrate-type)", DEFAULT_CBR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING + | G_PARAM_DEPRECATED)); g_object_class_install_property (gobject_class, PROP_CONSTRAINED_VBR, g_param_spec_boolean ("constrained-vbr", "Constrained VBR", - "Constrained VBR", DEFAULT_CONSTRAINED_VBR, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | - GST_PARAM_MUTABLE_PLAYING | G_PARAM_DEPRECATED)); + "Constrained VBR (DEPRECATED: use bitrate-type)", + DEFAULT_CONSTRAINED_VBR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING + | G_PARAM_DEPRECATED)); g_object_class_install_property (gobject_class, PROP_BITRATE_TYPE, - g_param_spec_enum ("bitrate-type", "Bitrate type", - "Bitrate type", GST_OPUS_ENC_TYPE_BITRATE_TYPE, - DEFAULT_BITRATE_TYPE, + g_param_spec_enum ("bitrate-type", "Bitrate type", "Bitrate type", + GST_OPUS_ENC_TYPE_BITRATE_TYPE, DEFAULT_BITRATE_TYPE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)); g_object_class_install_property (gobject_class, PROP_COMPLEXITY, From 54a5859960f2b82f065c2dd499c6b537c71193cf Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Tue, 15 Sep 2015 15:39:11 -0300 Subject: [PATCH 180/197] opusdec: remove check for number of channels opus decoder can convert from different number of channels, no need to check, just let it negotiate and create a new decoder if needed. https://bugzilla.gnome.org/show_bug.cgi?id=755059 --- ext/opus/gstopusdec.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index d8a7eb85fa..7b7a10bb56 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -297,12 +297,6 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) gst_buffer_map (buf, &map, GST_MAP_READ); data = map.data; - if (!(dec->n_channels == 0 || dec->n_channels == data[9])) { - gst_buffer_unmap (buf, &map); - GST_ERROR_OBJECT (dec, "Opus ID header has invalid channels"); - return GST_FLOW_ERROR; - } - dec->n_channels = data[9]; dec->sample_rate = GST_READ_UINT32_LE (data + 12); dec->pre_skip = GST_READ_UINT16_LE (data + 10); From 7cadfcb0b0f7bb9ecfbe5f106b97e526c2d90801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 30 Oct 2015 20:59:41 +0200 Subject: [PATCH 181/197] opusenc: Place 48kHz first in the caps For all the other sample rates the encoder will have to resample internally. --- ext/opus/gstopusenc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 7fad248816..7dfd441513 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -166,7 +166,12 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-raw, " "format = (string) " FORMAT_STR ", " "layout = (string) interleaved, " - "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, " + "rate = (int) 48000, " + "channels = (int) [ 1, 2 ]; " + "audio/x-raw, " + "format = (string) " FORMAT_STR ", " + "layout = (string) interleaved, " + "rate = (int) { 8000, 12000, 16000, 24000 }, " "channels = (int) [ 1, 2 ] ") ); From 9c195e37da54b07ee843b42fc02847d8496b9e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 1 Nov 2015 23:34:32 +0200 Subject: [PATCH 182/197] opusdec: Assume 48kHz if no sample rate is given in the header --- ext/opus/gstopusdec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 7b7a10bb56..b081b99b2e 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -299,6 +299,8 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) dec->n_channels = data[9]; dec->sample_rate = GST_READ_UINT32_LE (data + 12); + if (dec->sample_rate == 0) + dec->sample_rate = 48000; dec->pre_skip = GST_READ_UINT16_LE (data + 10); dec->r128_gain = GST_READ_UINT16_LE (data + 16); dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); From 6ffb90e0375de1a31ff2f1bd6dc8a353fce976ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 30 Oct 2015 20:47:20 +0200 Subject: [PATCH 183/197] opusenc: Put lookahead/pre-skip into the OpusHead header https://bugzilla.gnome.org/show_bug.cgi?id=757153 --- ext/opus/gstopusenc.c | 13 ++++++++++--- ext/opus/gstopusheader.c | 19 +++++++++---------- ext/opus/gstopusheader.h | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 7dfd441513..62773278ba 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -722,6 +722,7 @@ gst_opus_enc_setup (GstOpusEnc * enc) int error = OPUS_OK; GstCaps *caps; gboolean ret; + gint32 lookahead; #ifndef GST_DISABLE_GST_DEBUG GST_DEBUG_OBJECT (enc, @@ -759,10 +760,16 @@ gst_opus_enc_setup (GstOpusEnc * enc) opus_multistream_encoder_ctl (enc->state, OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0); - GST_LOG_OBJECT (enc, "we have frame size %d", enc->frame_size); + opus_multistream_encoder_ctl (enc->state, OPUS_GET_LOOKAHEAD (&lookahead), 0); - gst_opus_header_create_caps (&caps, NULL, enc->n_channels, - enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, + GST_LOG_OBJECT (enc, "we have frame size %d, lookahead %d", enc->frame_size, + lookahead); + + /* lookahead is samples, the Opus header wants it in 48kHz samples */ + lookahead = lookahead * 48000 / enc->sample_rate; + + gst_opus_header_create_caps (&caps, NULL, lookahead, enc->sample_rate, + enc->n_channels, enc->n_stereo_streams, enc->channel_mapping_family, enc->decoding_channel_mapping, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 1521c1c071..7c1cebe3cf 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -27,16 +27,15 @@ #include "gstopusheader.h" static GstBuffer * -gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, - gint sample_rate, guint8 channel_mapping_family, +gst_opus_enc_create_id_buffer (guint16 pre_skip, guint sample_rate, + guint8 nchannels, guint8 n_stereo_streams, guint8 channel_mapping_family, const guint8 * channel_mapping) { GstBuffer *buffer; GstByteWriter bw; gboolean hdl = TRUE; - g_return_val_if_fail (nchannels > 0 && nchannels < 256, NULL); - g_return_val_if_fail (n_stereo_streams >= 0, NULL); + g_return_val_if_fail (nchannels > 0, NULL); g_return_val_if_fail (n_stereo_streams <= nchannels - n_stereo_streams, NULL); gst_byte_writer_init (&bw); @@ -45,7 +44,7 @@ gst_opus_enc_create_id_buffer (gint nchannels, gint n_stereo_streams, hdl &= gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); hdl &= gst_byte_writer_put_uint8 (&bw, 0x01); /* version number */ hdl &= gst_byte_writer_put_uint8 (&bw, nchannels); - hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* pre-skip */ + hdl &= gst_byte_writer_put_uint16_le (&bw, pre_skip); hdl &= gst_byte_writer_put_uint32_le (&bw, sample_rate); hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ hdl &= gst_byte_writer_put_uint8 (&bw, channel_mapping_family); @@ -211,8 +210,9 @@ gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, } void -gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, - gint n_stereo_streams, gint sample_rate, guint8 channel_mapping_family, +gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, + guint16 pre_skip, guint sample_rate, guint8 nchannels, + guint8 n_stereo_streams, guint8 channel_mapping_family, const guint8 * channel_mapping, const GstTagList * tags) { GstBuffer *buf1, *buf2; @@ -220,7 +220,6 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, g_return_if_fail (caps); g_return_if_fail (!headers || !*headers); g_return_if_fail (nchannels > 0); - g_return_if_fail (sample_rate >= 0); /* 0 -> unset */ g_return_if_fail (channel_mapping_family == 0 || channel_mapping); /* Opus streams in Ogg begin with two headers; the initial header (with @@ -229,8 +228,8 @@ gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, gint nchannels, /* create header buffers */ buf1 = - gst_opus_enc_create_id_buffer (nchannels, n_stereo_streams, sample_rate, - channel_mapping_family, channel_mapping); + gst_opus_enc_create_id_buffer (pre_skip, sample_rate, nchannels, + n_stereo_streams, channel_mapping_family, channel_mapping); buf2 = gst_opus_enc_create_metadata_buffer (tags); gst_opus_header_create_caps_from_headers (caps, headers, buf1, buf2); diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 14c88c04d5..567d88c1f0 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -29,7 +29,7 @@ G_BEGIN_DECLS extern void gst_opus_header_create_caps_from_headers (GstCaps **caps, GSList **headers, GstBuffer *id_header, GstBuffer *comment_header); extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, - gint nchannels, gint n_stereo_streams, gint sample_rate, + guint16 pre_skip, guint sample_rate32, guint8 nchannels, guint8 n_stereo_streams, guint8 channel_mapping_family, const guint8 *channel_mapping, const GstTagList *tags); extern gboolean gst_opus_header_is_header (GstBuffer * buf, From 4df2ffaad608b632302e04d9be2fdd90ea6e1e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 30 Oct 2015 20:57:37 +0200 Subject: [PATCH 184/197] opusenc: Encode exactly the amount of samples we got as input and put correct timestamps on it The first frame has lookahead less samples, the last frame might have some padding or we might have to encode another frame of silence to get all our input into the encoded data. This is because of a) the lookahead at the beginning of the encoding, which shifts all data by that amount of samples and b) the padding needed to fill the very last frame completely. Ideally we would use LPC to calculate something better than silence for the padding to make the encoding as smooth as possible. With this we get exactly the same amount of samples again in an opusenc ! opusdec pipeline. https://bugzilla.gnome.org/show_bug.cgi?id=757153 --- ext/opus/gstopusenc.c | 66 +++++++++++++++++++++++++++++++++++-------- ext/opus/gstopusenc.h | 3 +- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 62773278ba..94c43ddf3a 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -412,6 +412,7 @@ gst_opus_enc_start (GstAudioEncoder * benc) GST_DEBUG_OBJECT (enc, "start"); enc->encoded_samples = 0; + enc->consumed_samples = 0; return TRUE; } @@ -766,6 +767,7 @@ gst_opus_enc_setup (GstOpusEnc * enc) lookahead); /* lookahead is samples, the Opus header wants it in 48kHz samples */ + enc->lookahead = enc->pending_lookahead = lookahead; lookahead = lookahead * 48000 / enc->sample_rate; gst_opus_header_create_caps (&caps, NULL, lookahead, enc->sample_rate, @@ -807,6 +809,7 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) } case GST_EVENT_SEGMENT: enc->encoded_samples = 0; + enc->consumed_samples = 0; break; default: @@ -899,13 +902,13 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GstClockTime duration; guint max_payload_size; - gint frame_samples; + gint frame_samples, input_samples, output_samples; g_mutex_lock (&enc->property_lock); bytes = enc->frame_samples * enc->n_channels * 2; max_payload_size = enc->max_payload_size; - frame_samples = enc->frame_samples; + frame_samples = input_samples = enc->frame_samples; g_mutex_unlock (&enc->property_lock); @@ -915,20 +918,23 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) bsize = map.size; if (G_UNLIKELY (bsize % bytes)) { + gint64 diff; + GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); + g_assert (bsize < bytes); /* If encoding part of a frame, and we have no set stop time on * the output segment, we update the segment stop time to reflect * the last sample. This will let oggmux set the last page's * granpos to tell a decoder the dummy samples should be clipped. */ + input_samples = bsize / (enc->n_channels * 2); segment = &GST_AUDIO_ENCODER_OUTPUT_SEGMENT (enc); if (!GST_CLOCK_TIME_IS_VALID (segment->stop)) { - int input_samples = bsize / (enc->n_channels * 2); GST_DEBUG_OBJECT (enc, "No stop time and partial frame, updating segment"); duration = - gst_util_uint64_scale (enc->encoded_samples + input_samples, + gst_util_uint64_scale_ceil (enc->consumed_samples + input_samples, GST_SECOND, enc->sample_rate); segment->stop = segment->start + duration; GST_DEBUG_OBJECT (enc, "new output segment %" GST_SEGMENT_FORMAT, @@ -937,6 +943,21 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) gst_event_new_segment (segment)); } + diff = + (enc->encoded_samples + frame_samples) - (enc->consumed_samples + + input_samples); + if (diff >= 0) { + GST_DEBUG_OBJECT (enc, + "%" G_GINT64_FORMAT " extra samples of padding in this frame", + diff); + output_samples = frame_samples - diff; + } else { + GST_DEBUG_OBJECT (enc, + "Need to add %" G_GINT64_FORMAT " extra samples in the next frame", + -diff); + output_samples = frame_samples; + } + size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); memcpy (mdata, bdata, bsize); @@ -944,10 +965,34 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) } else { data = bdata; size = bsize; + + /* Adjust for lookahead here */ + if (enc->pending_lookahead) { + if (input_samples > enc->pending_lookahead) { + output_samples = input_samples - enc->pending_lookahead; + enc->pending_lookahead = 0; + } else { + enc->pending_lookahead -= input_samples; + output_samples = 0; + } + } else { + output_samples = input_samples; + } } } else { - GST_DEBUG_OBJECT (enc, "nothing to drain"); - goto done; + if (enc->encoded_samples < enc->consumed_samples) { + data = mdata = g_malloc0 (bytes); + size = bytes; + output_samples = enc->consumed_samples - enc->encoded_samples; + input_samples = 0; + GST_DEBUG_OBJECT (enc, "draining %d samples", output_samples); + } else if (enc->encoded_samples == enc->consumed_samples) { + GST_DEBUG_OBJECT (enc, "nothing to drain"); + goto done; + } else { + g_assert_not_reached (); + goto done; + } } g_assert (size == bytes); @@ -963,9 +1008,6 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); - GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", - frame_samples, (int) bytes); - outsize = opus_multistream_encode (enc->state, (const gint16 *) data, frame_samples, omap.data, max_payload_size * enc->n_channels); @@ -987,10 +1029,12 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); gst_buffer_set_size (outbuf, outsize); + ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, - frame_samples); - enc->encoded_samples += frame_samples; + output_samples); + enc->encoded_samples += output_samples; + enc->consumed_samples += input_samples; done: diff --git a/ext/opus/gstopusenc.h b/ext/opus/gstopusenc.h index 43eaac8d27..f447292af4 100644 --- a/ext/opus/gstopusenc.h +++ b/ext/opus/gstopusenc.h @@ -79,7 +79,8 @@ struct _GstOpusEnc { gint n_channels; gint sample_rate; - guint64 encoded_samples; + guint64 encoded_samples, consumed_samples; + guint16 lookahead, pending_lookahead; guint8 channel_mapping_family; guint8 encoding_channel_mapping[256]; From 7773e1eb5869d3cd4223da432ea1bdf1d8cfd51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 31 Oct 2015 15:02:50 +0200 Subject: [PATCH 185/197] opusenc: Add some FIXME comments about calculating padding with LPC https://bugzilla.gnome.org/show_bug.cgi?id=757153 --- ext/opus/gstopusenc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 94c43ddf3a..718643a680 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -960,6 +960,10 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); + /* FIXME: Instead of silence, use LPC with the last real samples. + * Otherwise we will create a discontinuity here, which will distort the + * last few encoded samples + */ memcpy (mdata, bdata, bsize); data = mdata; } else { @@ -981,6 +985,10 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) } } else { if (enc->encoded_samples < enc->consumed_samples) { + /* FIXME: Instead of silence, use LPC with the last real samples. + * Otherwise we will create a discontinuity here, which will distort the + * last few encoded samples + */ data = mdata = g_malloc0 (bytes); size = bytes; output_samples = enc->consumed_samples - enc->encoded_samples; From 6b751360ae56583197ac678f05aadbc8b7aa46ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 2 Nov 2015 10:30:52 +0200 Subject: [PATCH 186/197] opusenc: Disable granule position calculations by the base class It is doing the wrong thing because of the Opus pre-skip: while the timestamps are shifted by the pre-skip, the granule positions are not shifted. oggmux is doing the right thing here already. https://bugzilla.gnome.org/show_bug.cgi?id=757153 --- ext/opus/gstopusenc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 718643a680..24bdeafc4f 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -377,8 +377,6 @@ gst_opus_enc_finalize (GObject * object) static void gst_opus_enc_init (GstOpusEnc * enc) { - GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc); - GST_DEBUG_OBJECT (enc, "init"); GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_ENCODER_SINK_PAD (enc)); @@ -399,10 +397,6 @@ gst_opus_enc_init (GstOpusEnc * enc) enc->packet_loss_percentage = DEFAULT_PACKET_LOSS_PERCENT; enc->max_payload_size = DEFAULT_MAX_PAYLOAD_SIZE; enc->audio_type = DEFAULT_AUDIO_TYPE; - - /* arrange granulepos marking (and required perfect ts) */ - gst_audio_encoder_set_mark_granule (benc, TRUE); - gst_audio_encoder_set_perfect_timestamp (benc, TRUE); } static gboolean From 328f9088f340cb4fc84d79698e7f6220f5bb4db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 2 Nov 2015 16:52:28 +0200 Subject: [PATCH 187/197] opusenc: Add GstAudioClippingMeta to buffers that need to be clipped https://bugzilla.gnome.org/show_bug.cgi?id=757153 --- ext/opus/gstopusenc.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 24bdeafc4f..17004ce1b8 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -761,8 +761,8 @@ gst_opus_enc_setup (GstOpusEnc * enc) lookahead); /* lookahead is samples, the Opus header wants it in 48kHz samples */ - enc->lookahead = enc->pending_lookahead = lookahead; lookahead = lookahead * 48000 / enc->sample_rate; + enc->lookahead = enc->pending_lookahead = lookahead; gst_opus_header_create_caps (&caps, NULL, lookahead, enc->sample_rate, enc->n_channels, enc->n_stereo_streams, enc->channel_mapping_family, @@ -894,6 +894,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GstBuffer *outbuf; GstSegment *segment; GstClockTime duration; + guint64 trim_start = 0, trim_end = 0; guint max_payload_size; gint frame_samples, input_samples, output_samples; @@ -945,6 +946,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) "%" G_GINT64_FORMAT " extra samples of padding in this frame", diff); output_samples = frame_samples - diff; + trim_end = diff * 48000 / enc->sample_rate; } else { GST_DEBUG_OBJECT (enc, "Need to add %" G_GINT64_FORMAT " extra samples in the next frame", @@ -966,11 +968,16 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) /* Adjust for lookahead here */ if (enc->pending_lookahead) { - if (input_samples > enc->pending_lookahead) { - output_samples = input_samples - enc->pending_lookahead; + guint scaled_lookahead = + enc->pending_lookahead * enc->sample_rate / 48000; + + if (input_samples > scaled_lookahead) { + output_samples = input_samples - scaled_lookahead; + trim_start = enc->pending_lookahead; enc->pending_lookahead = 0; } else { - enc->pending_lookahead -= input_samples; + trim_start = input_samples * 48000 / enc->sample_rate; + enc->pending_lookahead -= trim_start; output_samples = 0; } } else { @@ -988,6 +995,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) output_samples = enc->consumed_samples - enc->encoded_samples; input_samples = 0; GST_DEBUG_OBJECT (enc, "draining %d samples", output_samples); + trim_end = (frame_samples - output_samples) * 48000 / enc->sample_rate; } else if (enc->encoded_samples == enc->consumed_samples) { GST_DEBUG_OBJECT (enc, "nothing to drain"); goto done; @@ -1008,6 +1016,14 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", frame_samples, (int) bytes); + if (trim_start || trim_end) { + GST_DEBUG_OBJECT (enc, + "Adding trim-start %" G_GUINT64_FORMAT " trim-end %" G_GUINT64_FORMAT, + trim_start, trim_end); + gst_buffer_add_audio_clipping_meta (outbuf, GST_FORMAT_DEFAULT, trim_start, + trim_end); + } + gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); outsize = From fc475ce01a3c9d444c47cda15ceac2e93ca9621e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 2 Nov 2015 17:33:53 +0200 Subject: [PATCH 188/197] opusdec: Handle GstAudioClippingMeta instead of the pre-skip field in the OpusHead oggdemux is outputting the meta now, and only outputs if it should really apply to the current buffer. Previously we would skip N samples also if we started the decoder in the middle of the stream. https://bugzilla.gnome.org/show_bug.cgi?id=757153 --- ext/opus/gstopusdec.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index b081b99b2e..23649d2abc 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -380,6 +380,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) unsigned int packet_size; GstBuffer *buf; GstMapInfo map, omap; + GstAudioClippingMeta *cmeta = NULL; if (dec->state == NULL) { /* If we did not get any headers, default to 2 channels */ @@ -534,17 +535,41 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GST_DEBUG_OBJECT (dec, "decoded %d samples", n); gst_buffer_set_size (outbuf, n * 2 * dec->n_channels); + cmeta = gst_buffer_get_audio_clipping_meta (buf); + + g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT); + /* Skip any samples that need skipping */ - if (dec->pre_skip > 0) { - guint scaled_pre_skip = dec->pre_skip * dec->sample_rate / 48000; + if (cmeta && cmeta->start) { + guint pre_skip = cmeta->start; + guint scaled_pre_skip = pre_skip * dec->sample_rate / 48000; guint skip = scaled_pre_skip > n ? n : scaled_pre_skip; guint scaled_skip = skip * 48000 / dec->sample_rate; gst_buffer_resize (outbuf, skip * 2 * dec->n_channels, -1); - dec->pre_skip -= scaled_skip; + GST_INFO_OBJECT (dec, - "Skipping %u samples (%u at 48000 Hz, %u left to skip)", skip, - scaled_skip, dec->pre_skip); + "Skipping %u samples at the beginning (%u at 48000 Hz)", + skip, scaled_skip); + } + + if (cmeta && cmeta->end) { + guint post_skip = cmeta->end; + guint scaled_post_skip = post_skip * dec->sample_rate / 48000; + guint skip = scaled_post_skip > n ? n : scaled_post_skip; + guint scaled_skip = skip * 48000 / dec->sample_rate; + guint outsize = gst_buffer_get_size (outbuf); + guint skip_bytes = skip * 2 * dec->n_channels; + + if (outsize > skip_bytes) + outsize -= skip_bytes; + else + outsize = 0; + + gst_buffer_resize (outbuf, 0, outsize); + + GST_INFO_OBJECT (dec, + "Skipping %u samples at the end (%u at 48000 Hz)", skip, scaled_skip); } if (gst_buffer_get_size (outbuf) == 0) { From 4ca84a9b1a0ee1643a698662680aab4cd0d46ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 3 Nov 2015 14:50:53 +0200 Subject: [PATCH 189/197] opus: Add proper support for multichannel audio https://bugzilla.gnome.org/show_bug.cgi?id=757152 --- ext/opus/Makefile.am | 1 + ext/opus/gstopusdec.c | 127 +++++++++++------------ ext/opus/gstopusdec.h | 6 +- ext/opus/gstopusenc.c | 196 ++++++++++++++++++++---------------- ext/opus/gstopusheader.c | 212 --------------------------------------- ext/opus/gstopusheader.h | 6 -- 6 files changed, 174 insertions(+), 374 deletions(-) diff --git a/ext/opus/Makefile.am b/ext/opus/Makefile.am index d22c664e0f..c643692525 100644 --- a/ext/opus/Makefile.am +++ b/ext/opus/Makefile.am @@ -10,6 +10,7 @@ libgstopus_la_CFLAGS = \ libgstopus_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \ -lgsttag-$(GST_API_VERSION) -lgstrtp-$(GST_API_VERSION) \ + -lgstpbutils-$(GST_API_VERSION) \ $(GST_BASE_LIBS) \ $(GST_LIBS) \ $(OPUS_LIBS) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 23649d2abc..8ca2809a30 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -47,6 +47,7 @@ #include "gstopusheader.h" #include "gstopuscommon.h" #include "gstopusdec.h" +#include GST_DEBUG_CATEGORY_STATIC (opusdec_debug); #define GST_CAT_DEFAULT opusdec_debug @@ -225,6 +226,8 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) GstAudioInfo info; if (caps) { + gint rate, channels; + caps = gst_caps_truncate (caps); caps = gst_caps_make_writable (caps); s = gst_caps_get_structure (caps, 0); @@ -233,13 +236,15 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) gst_structure_fixate_field_nearest_int (s, "rate", dec->sample_rate); else gst_structure_set (s, "rate", G_TYPE_INT, dec->sample_rate, NULL); - gst_structure_get_int (s, "rate", &dec->sample_rate); + gst_structure_get_int (s, "rate", &rate); + dec->sample_rate = rate; if (gst_structure_has_field (s, "channels")) gst_structure_fixate_field_nearest_int (s, "channels", dec->n_channels); else gst_structure_set (s, "channels", G_TYPE_INT, dec->n_channels, NULL); - gst_structure_get_int (s, "channels", &dec->n_channels); + gst_structure_get_int (s, "channels", &channels); + dec->n_channels = channels; gst_caps_unref (caps); } @@ -273,7 +278,6 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) /* but we still need the opus order for later reordering */ if (pos) { memcpy (dec->opus_pos, pos, sizeof (pos[0]) * dec->n_channels); - gst_audio_channel_positions_to_valid_order (dec->opus_pos, dec->n_channels); } else { dec->opus_pos[0] = GST_AUDIO_CHANNEL_POSITION_INVALID; } @@ -284,79 +288,64 @@ gst_opus_dec_negotiate (GstOpusDec * dec, const GstAudioChannelPosition * pos) static GstFlowReturn gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf) { - const guint8 *data; GstAudioChannelPosition pos[64]; const GstAudioChannelPosition *posn = NULL; - GstMapInfo map; if (!gst_opus_header_is_id_header (buf)) { GST_ERROR_OBJECT (dec, "Header is not an Opus ID header"); return GST_FLOW_ERROR; } - gst_buffer_map (buf, &map, GST_MAP_READ); - data = map.data; - - dec->n_channels = data[9]; - dec->sample_rate = GST_READ_UINT32_LE (data + 12); - if (dec->sample_rate == 0) - dec->sample_rate = 48000; - dec->pre_skip = GST_READ_UINT16_LE (data + 10); - dec->r128_gain = GST_READ_UINT16_LE (data + 16); + if (!gst_codec_utils_opus_parse_header (buf, + &dec->sample_rate, + &dec->n_channels, + &dec->channel_mapping_family, + &dec->n_streams, + &dec->n_stereo_streams, + dec->channel_mapping, &dec->pre_skip, &dec->r128_gain)) { + GST_ERROR_OBJECT (dec, "Failed to parse Opus ID header"); + return GST_FLOW_ERROR; + } dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain); + GST_INFO_OBJECT (dec, "Found pre-skip of %u samples, R128 gain %d (volume %f)", dec->pre_skip, dec->r128_gain, dec->r128_gain_volume); - dec->channel_mapping_family = data[18]; - if (dec->channel_mapping_family == 0) { - /* implicit mapping */ - GST_INFO_OBJECT (dec, "Channel mapping family 0, implicit mapping"); - dec->n_streams = dec->n_stereo_streams = 1; - dec->channel_mapping[0] = 0; - dec->channel_mapping[1] = 1; - } else { - dec->n_streams = data[19]; - dec->n_stereo_streams = data[20]; - memcpy (dec->channel_mapping, data + 21, dec->n_channels); + if (dec->channel_mapping_family == 1) { + GST_INFO_OBJECT (dec, "Channel mapping family 1, Vorbis mapping"); + switch (dec->n_channels) { + case 1: + case 2: + /* nothing */ + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + posn = gst_opus_channel_positions[dec->n_channels - 1]; + break; + default:{ + gint i; - if (dec->channel_mapping_family == 1) { - GST_INFO_OBJECT (dec, "Channel mapping family 1, Vorbis mapping"); - switch (dec->n_channels) { - case 1: - case 2: - /* nothing */ - break; - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - posn = gst_opus_channel_positions[dec->n_channels - 1]; - break; - default:{ - gint i; + GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, + (NULL), ("Using NONE channel layout for more than 8 channels")); - GST_ELEMENT_WARNING (GST_ELEMENT (dec), STREAM, DECODE, - (NULL), ("Using NONE channel layout for more than 8 channels")); + for (i = 0; i < dec->n_channels; i++) + pos[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - for (i = 0; i < dec->n_channels; i++) - pos[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - - posn = pos; - } + posn = pos; } - } else { - GST_INFO_OBJECT (dec, "Channel mapping family %d", - dec->channel_mapping_family); } + } else { + GST_INFO_OBJECT (dec, "Channel mapping family %d", + dec->channel_mapping_family); } gst_opus_dec_negotiate (dec, posn); - gst_buffer_unmap (buf, &map); - return GST_FLOW_OK; } @@ -660,8 +649,10 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) { buf = gst_value_get_buffer (header); res = gst_opus_dec_parse_header (dec, buf); - if (res != GST_FLOW_OK) + if (res != GST_FLOW_OK) { + ret = FALSE; goto done; + } gst_buffer_replace (&dec->streamheader, buf); } @@ -669,26 +660,26 @@ gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) { buf = gst_value_get_buffer (vorbiscomment); res = gst_opus_dec_parse_comments (dec, buf); - if (res != GST_FLOW_OK) + if (res != GST_FLOW_OK) { + ret = FALSE; goto done; + } gst_buffer_replace (&dec->vorbiscomment, buf); } } else { - /* defaults if not in the caps */ - dec->n_channels = 2; - dec->sample_rate = 48000; + const GstAudioChannelPosition *posn = NULL; - gst_structure_get_int (s, "channels", &dec->n_channels); - gst_structure_get_int (s, "rate", &dec->sample_rate); + if (!gst_codec_utils_opus_parse_caps (caps, &dec->sample_rate, + &dec->n_channels, &dec->channel_mapping_family, &dec->n_streams, + &dec->n_stereo_streams, dec->channel_mapping)) { + ret = FALSE; + goto done; + } - /* default stereo mapping */ - dec->channel_mapping_family = 0; - dec->channel_mapping[0] = 0; - dec->channel_mapping[1] = 1; - dec->n_streams = 1; - dec->n_stereo_streams = 1; + if (dec->channel_mapping_family == 1 && dec->n_channels <= 8) + posn = gst_opus_channel_positions[dec->n_channels - 1]; - gst_opus_dec_negotiate (dec, NULL); + gst_opus_dec_negotiate (dec, posn); } done: diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index f8d43a9266..df52cfb6f2 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -52,9 +52,9 @@ struct _GstOpusDec { GstBuffer *streamheader; GstBuffer *vorbiscomment; - int sample_rate; - int n_channels; - guint32 pre_skip; + guint32 sample_rate; + guint8 n_channels; + guint16 pre_skip; gint16 r128_gain; GstAudioChannelPosition opus_pos[64]; diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 17004ce1b8..4196c1c8c3 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -48,6 +48,8 @@ #include #include +#include +#include #include #include "gstopusheader.h" #include "gstopuscommon.h" @@ -167,12 +169,12 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", "format = (string) " FORMAT_STR ", " "layout = (string) interleaved, " "rate = (int) 48000, " - "channels = (int) [ 1, 2 ]; " + "channels = (int) [ 1, 8 ]; " "audio/x-raw, " "format = (string) " FORMAT_STR ", " "layout = (string) interleaved, " "rate = (int) { 8000, 12000, 16000, 24000 }, " - "channels = (int) [ 1, 2 ] ") + "channels = (int) [ 1, 8 ] ") ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", @@ -545,27 +547,20 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, /* For two channels, use the basic RTP mapping if the channels are mapped as left/right. */ if (enc->n_channels == 2) { - if (MAPS (0, FRONT_LEFT) && MAPS (1, FRONT_RIGHT)) { - GST_INFO_OBJECT (enc, "Stereo, canonical mapping"); - enc->channel_mapping_family = 0; - enc->n_stereo_streams = 1; - /* The channel mapping is implicit for family 0, that's why we do not - attempt to create one for right/left - this will be mapped to the - Vorbis mapping below. */ - return; - } else { - GST_DEBUG_OBJECT (enc, "Stereo, but not canonical mapping, continuing"); - } + GST_INFO_OBJECT (enc, "Stereo, trivial RTP mapping"); + enc->channel_mapping_family = 0; + enc->n_stereo_streams = 1; + /* implicit mapping for family 0 */ + return; } - /* For channels between 1 and 8, we use the Vorbis mapping if we can - find a permutation that matches it. Mono will have been taken care - of earlier, but this code also handles it. Same for left/right stereo. - There are two mappings. One maps the input channels to an ordering - which has the natural pairs first so they can benefit from the Opus - stereo channel coupling, and the other maps this ordering to the - Vorbis ordering. */ - if (enc->n_channels >= 1 && enc->n_channels <= 8) { + /* For channels between 3 and 8, we use the Vorbis mapping if we can + find a permutation that matches it. Mono and stereo will have been taken + care of earlier, but this code also handles it. There are two mappings. + One maps the input channels to an ordering which has the natural pairs + first so they can benefit from the Opus stereo channel coupling, and the + other maps this ordering to the Vorbis ordering. */ + if (enc->n_channels >= 3 && enc->n_channels <= 8) { int c0, c1, c0v, c1v; int mapped; gboolean positions_done[256]; @@ -580,6 +575,8 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, }; size_t pair; @@ -633,13 +630,8 @@ gst_opus_enc_setup_channel_mappings (GstOpusEnc * enc, GST_DEBUG_OBJECT (enc, "Channel position %s is not mapped yet, adding", gst_opus_channel_names[position]); cv = gst_opus_enc_find_channel_position_in_vorbis_order (enc, position); - if (cv < 0) { - GST_WARNING_OBJECT (enc, - "Cannot map channel positions to Vorbis order, using unknown mapping"); - enc->channel_mapping_family = 255; - enc->n_stereo_streams = 0; - return; - } + if (cv < 0) + g_assert_not_reached (); enc->encoding_channel_mapping[mapped] = n; enc->decoding_channel_mapping[cv] = mapped; mapped++; @@ -718,6 +710,8 @@ gst_opus_enc_setup (GstOpusEnc * enc) GstCaps *caps; gboolean ret; gint32 lookahead; + const GstTagList *tags; + GstBuffer *header, *comments; #ifndef GST_DISABLE_GST_DEBUG GST_DEBUG_OBJECT (enc, @@ -764,10 +758,17 @@ gst_opus_enc_setup (GstOpusEnc * enc) lookahead = lookahead * 48000 / enc->sample_rate; enc->lookahead = enc->pending_lookahead = lookahead; - gst_opus_header_create_caps (&caps, NULL, lookahead, enc->sample_rate, - enc->n_channels, enc->n_stereo_streams, enc->channel_mapping_family, - enc->decoding_channel_mapping, - gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); + header = gst_codec_utils_opus_create_header (enc->sample_rate, + enc->n_channels, enc->channel_mapping_family, + enc->n_channels - enc->n_stereo_streams, enc->n_stereo_streams, + enc->decoding_channel_mapping, lookahead, 0); + tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); + comments = + gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", + 8, "Encoded with GStreamer opusenc"); + caps = gst_codec_utils_opus_create_caps_from_header (header, comments); + gst_buffer_unref (header); + gst_buffer_unref (comments); /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); @@ -813,71 +814,96 @@ gst_opus_enc_sink_event (GstAudioEncoder * benc, GstEvent * event) return GST_AUDIO_ENCODER_CLASS (parent_class)->sink_event (benc, event); } +static GstCaps * +gst_opus_enc_get_sink_template_caps (void) +{ + static volatile gsize init = 0; + static GstCaps *caps = NULL; + + if (g_once_init_enter (&init)) { + GValue rate_array = G_VALUE_INIT; + GValue v = G_VALUE_INIT; + GstStructure *s1, *s2, *s; + gint i, c; + + caps = gst_caps_new_empty (); + + /* Generate our two template structures */ + g_value_init (&rate_array, GST_TYPE_LIST); + g_value_init (&v, G_TYPE_INT); + g_value_set_int (&v, 8000); + gst_value_list_append_value (&rate_array, &v); + g_value_set_int (&v, 12000); + gst_value_list_append_value (&rate_array, &v); + g_value_set_int (&v, 16000); + gst_value_list_append_value (&rate_array, &v); + g_value_set_int (&v, 24000); + gst_value_list_append_value (&rate_array, &v); + + s1 = gst_structure_new ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 48000, NULL); + s2 = gst_structure_new ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), + "layout", G_TYPE_STRING, "interleaved", NULL); + gst_structure_set_value (s2, "rate", &rate_array); + g_value_unset (&rate_array); + g_value_unset (&v); + + /* Mono */ + s = gst_structure_copy (s1); + gst_structure_set (s, "channels", G_TYPE_INT, 1, NULL); + gst_caps_append_structure (caps, s); + + s = gst_structure_copy (s2); + gst_structure_set (s, "channels", G_TYPE_INT, 1, NULL); + gst_caps_append_structure (caps, s); + + /* Stereo and further */ + for (i = 2; i <= 8; i++) { + guint64 channel_mask = 0; + const GstAudioChannelPosition *pos = gst_opus_channel_positions[i - 1]; + + for (c = 0; c < i; c++) { + channel_mask |= G_GUINT64_CONSTANT (1) << pos[c]; + } + + s = gst_structure_copy (s1); + gst_structure_set (s, "channels", G_TYPE_INT, i, "channel-mask", + GST_TYPE_BITMASK, channel_mask, NULL); + gst_caps_append_structure (caps, s); + + s = gst_structure_copy (s2); + gst_structure_set (s, "channels", G_TYPE_INT, i, "channel-mask", + GST_TYPE_BITMASK, channel_mask, NULL); + gst_caps_append_structure (caps, s); + } + + gst_structure_free (s1); + gst_structure_free (s2); + + g_once_init_leave (&init, 1); + } + + return caps; +} + static GstCaps * gst_opus_enc_sink_getcaps (GstAudioEncoder * benc, GstCaps * filter) { GstOpusEnc *enc; GstCaps *caps; - GstCaps *tcaps; - GstCaps *peercaps = NULL; - GstCaps *intersect = NULL; - guint i; - gboolean allow_multistream; enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "sink getcaps"); - peercaps = gst_pad_peer_query_caps (GST_AUDIO_ENCODER_SRC_PAD (benc), NULL); - if (!peercaps) { - GST_DEBUG_OBJECT (benc, "No peercaps, returning template sink caps"); - return gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD (benc)); - } - - tcaps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SRC_PAD (benc)); - intersect = gst_caps_intersect (peercaps, tcaps); - gst_caps_unref (tcaps); - gst_caps_unref (peercaps); - - if (gst_caps_is_empty (intersect)) - return intersect; - - allow_multistream = FALSE; - for (i = 0; i < gst_caps_get_size (intersect); i++) { - GstStructure *s = gst_caps_get_structure (intersect, i); - gboolean multistream; - if (gst_structure_get_boolean (s, "multistream", &multistream)) { - if (multistream) { - allow_multistream = TRUE; - } - } else { - allow_multistream = TRUE; - } - } - - gst_caps_unref (intersect); - - caps = gst_pad_get_pad_template_caps (GST_AUDIO_ENCODER_SINK_PAD (benc)); - caps = gst_caps_make_writable (caps); - if (!allow_multistream) { - GValue range = { 0 }; - g_value_init (&range, GST_TYPE_INT_RANGE); - gst_value_set_int_range (&range, 1, 2); - for (i = 0; i < gst_caps_get_size (caps); i++) { - GstStructure *s = gst_caps_get_structure (caps, i); - gst_structure_set_value (s, "channels", &range); - } - g_value_unset (&range); - } - - if (filter) { - GstCaps *tmp = gst_caps_intersect_full (caps, filter, - GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - caps = tmp; - } + caps = gst_opus_enc_get_sink_template_caps (); + caps = gst_audio_encoder_proxy_getcaps (benc, caps, filter); GST_DEBUG_OBJECT (enc, "Returning caps: %" GST_PTR_FORMAT, caps); + return caps; } diff --git a/ext/opus/gstopusheader.c b/ext/opus/gstopusheader.c index 7c1cebe3cf..dcc85897d1 100644 --- a/ext/opus/gstopusheader.c +++ b/ext/opus/gstopusheader.c @@ -26,218 +26,6 @@ #include #include "gstopusheader.h" -static GstBuffer * -gst_opus_enc_create_id_buffer (guint16 pre_skip, guint sample_rate, - guint8 nchannels, guint8 n_stereo_streams, guint8 channel_mapping_family, - const guint8 * channel_mapping) -{ - GstBuffer *buffer; - GstByteWriter bw; - gboolean hdl = TRUE; - - g_return_val_if_fail (nchannels > 0, NULL); - g_return_val_if_fail (n_stereo_streams <= nchannels - n_stereo_streams, NULL); - - gst_byte_writer_init (&bw); - - /* See http://wiki.xiph.org/OggOpus */ - hdl &= gst_byte_writer_put_data (&bw, (const guint8 *) "OpusHead", 8); - hdl &= gst_byte_writer_put_uint8 (&bw, 0x01); /* version number */ - hdl &= gst_byte_writer_put_uint8 (&bw, nchannels); - hdl &= gst_byte_writer_put_uint16_le (&bw, pre_skip); - hdl &= gst_byte_writer_put_uint32_le (&bw, sample_rate); - hdl &= gst_byte_writer_put_uint16_le (&bw, 0); /* output gain */ - hdl &= gst_byte_writer_put_uint8 (&bw, channel_mapping_family); - if (channel_mapping_family > 0) { - hdl &= gst_byte_writer_put_uint8 (&bw, nchannels - n_stereo_streams); - hdl &= gst_byte_writer_put_uint8 (&bw, n_stereo_streams); - hdl &= gst_byte_writer_put_data (&bw, channel_mapping, nchannels); - } - - if (!hdl) - GST_WARNING ("Error creating header"); - - buffer = gst_byte_writer_reset_and_get_buffer (&bw); - - GST_BUFFER_OFFSET (buffer) = 0; - GST_BUFFER_OFFSET_END (buffer) = 0; - - return buffer; -} - -static GstBuffer * -gst_opus_enc_create_metadata_buffer (const GstTagList * tags) -{ - GstTagList *empty_tags = NULL; - GstBuffer *comments = NULL; - - GST_DEBUG ("tags = %" GST_PTR_FORMAT, tags); - - if (tags == NULL) { - /* FIXME: better fix chain of callers to not write metadata at all, - * if there is none */ - empty_tags = gst_tag_list_new_empty (); - tags = empty_tags; - } - comments = - gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", - 8, "Encoded with GStreamer Opusenc"); - - GST_BUFFER_OFFSET (comments) = 0; - GST_BUFFER_OFFSET_END (comments) = 0; - - if (empty_tags) - gst_tag_list_unref (empty_tags); - - return comments; -} - -/* - * (really really) FIXME: move into core (dixit tpm) - */ -/* - * _gst_caps_set_buffer_array: - * @caps: (transfer full): a #GstCaps - * @field: field in caps to set - * @buf: header buffers - * - * Adds given buffers to an array of buffers set as the given @field - * on the given @caps. List of buffer arguments must be NULL-terminated. - * - * Returns: (transfer full): input caps with a streamheader field added, or NULL - * if some error occurred - */ -static GstCaps * -_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, - GstBuffer * buf, ...) -{ - GstStructure *structure = NULL; - va_list va; - GValue array = { 0 }; - GValue value = { 0 }; - - g_return_val_if_fail (caps != NULL, NULL); - g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); - g_return_val_if_fail (field != NULL, NULL); - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - g_value_init (&array, GST_TYPE_ARRAY); - - va_start (va, buf); - /* put buffers in a fixed list */ - while (buf) { - g_assert (gst_buffer_is_writable (buf)); - - /* mark buffer */ - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); - - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - - buf = va_arg (va, GstBuffer *); - } - va_end (va); - - gst_structure_set_value (structure, field, &array); - g_value_unset (&array); - - return caps; -} - -void -gst_opus_header_create_caps_from_headers (GstCaps ** caps, GSList ** headers, - GstBuffer * buf1, GstBuffer * buf2) -{ - int n_streams, family; - gint channels, rate; - gboolean multistream; - GstMapInfo map; - guint8 *data; - - g_return_if_fail (caps); - g_return_if_fail (!headers || !*headers); - g_return_if_fail (gst_buffer_get_size (buf1) >= 19); - - gst_buffer_map (buf1, &map, GST_MAP_READ); - data = map.data; - - channels = data[9]; - rate = GST_READ_UINT32_LE (data + 12); - - /* work out the number of streams */ - family = data[18]; - if (family == 0) { - n_streams = 1; - } else { - /* only included in the header for family > 0 */ - if (map.size >= 20) - n_streams = data[19]; - else { - g_warning ("family > 0 but header buffer size < 20"); - gst_buffer_unmap (buf1, &map); - return; - } - } - - /* TODO: should probably also put the channel mapping into the caps too once - * we actually support multi channel, and pre-skip and other fields */ - - gst_buffer_unmap (buf1, &map); - - /* mark and put on caps */ - multistream = n_streams > 1; - *caps = gst_caps_new_simple ("audio/x-opus", - "multistream", G_TYPE_BOOLEAN, multistream, - "channels", G_TYPE_INT, channels, NULL); - - if (rate > 0) { - gst_caps_set_simple (*caps, "rate", G_TYPE_INT, rate, NULL); - } - - *caps = _gst_caps_set_buffer_array (*caps, "streamheader", buf1, buf2, NULL); - - if (headers) { - *headers = g_slist_prepend (*headers, gst_buffer_ref (buf2)); - *headers = g_slist_prepend (*headers, gst_buffer_ref (buf1)); - } -} - -void -gst_opus_header_create_caps (GstCaps ** caps, GSList ** headers, - guint16 pre_skip, guint sample_rate, guint8 nchannels, - guint8 n_stereo_streams, guint8 channel_mapping_family, - const guint8 * channel_mapping, const GstTagList * tags) -{ - GstBuffer *buf1, *buf2; - - g_return_if_fail (caps); - g_return_if_fail (!headers || !*headers); - g_return_if_fail (nchannels > 0); - g_return_if_fail (channel_mapping_family == 0 || channel_mapping); - - /* Opus streams in Ogg begin with two 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. */ - - /* create header buffers */ - buf1 = - gst_opus_enc_create_id_buffer (pre_skip, sample_rate, nchannels, - n_stereo_streams, channel_mapping_family, channel_mapping); - buf2 = gst_opus_enc_create_metadata_buffer (tags); - - gst_opus_header_create_caps_from_headers (caps, headers, buf1, buf2); - - gst_buffer_unref (buf2); - gst_buffer_unref (buf1); -} - gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size) { diff --git a/ext/opus/gstopusheader.h b/ext/opus/gstopusheader.h index 567d88c1f0..2a91c03f7c 100644 --- a/ext/opus/gstopusheader.h +++ b/ext/opus/gstopusheader.h @@ -26,12 +26,6 @@ G_BEGIN_DECLS -extern void gst_opus_header_create_caps_from_headers (GstCaps **caps, GSList **headers, - GstBuffer *id_header, GstBuffer *comment_header); -extern void gst_opus_header_create_caps (GstCaps **caps, GSList **headers, - guint16 pre_skip, guint sample_rate32, guint8 nchannels, guint8 n_stereo_streams, - guint8 channel_mapping_family, const guint8 *channel_mapping, - const GstTagList *tags); extern gboolean gst_opus_header_is_header (GstBuffer * buf, const char *magic, guint magic_size); extern gboolean gst_opus_header_is_id_header (GstBuffer * buf); From 85984fa94689c318590a55fc83eac834463b21b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 4 Nov 2015 00:12:22 +0200 Subject: [PATCH 190/197] opusenc: Create an empty taglist if there is none There always have to be 2 buffers in the streamheaders, even if the comment buffer is basically empty. --- ext/opus/gstopusenc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 4196c1c8c3..729b2150c1 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -711,6 +711,7 @@ gst_opus_enc_setup (GstOpusEnc * enc) gboolean ret; gint32 lookahead; const GstTagList *tags; + GstTagList *empty_tags = NULL; GstBuffer *header, *comments; #ifndef GST_DISABLE_GST_DEBUG @@ -763,10 +764,14 @@ gst_opus_enc_setup (GstOpusEnc * enc) enc->n_channels - enc->n_stereo_streams, enc->n_stereo_streams, enc->decoding_channel_mapping, lookahead, 0); tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); + if (!tags) + tags = empty_tags = gst_tag_list_new_empty (); comments = gst_tag_list_to_vorbiscomment_buffer (tags, (const guint8 *) "OpusTags", 8, "Encoded with GStreamer opusenc"); caps = gst_codec_utils_opus_create_caps_from_header (header, comments); + if (empty_tags) + gst_tag_list_unref (empty_tags); gst_buffer_unref (header); gst_buffer_unref (comments); From 387839c57e85187874b4e6ec82da2654df5d5a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 4 Nov 2015 00:12:52 +0200 Subject: [PATCH 191/197] opus: Remove invalid unit test Opus headers should never be in-band, so don't test for correct handling of that. --- tests/check/elements/opus.c | 40 ------------------------------------- 1 file changed, 40 deletions(-) diff --git a/tests/check/elements/opus.c b/tests/check/elements/opus.c index 3afeb4ed53..c4b4b84e03 100644 --- a/tests/check/elements/opus.c +++ b/tests/check/elements/opus.c @@ -36,11 +36,6 @@ "rate = (int) 48000, " \ "channels = (int) 1 " -static const guint8 opus_ogg_id_header[19] = { - 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - /* A lot of these taken from the vorbisdec test */ /* For ease of programming we use globals to keep refs for our floating @@ -136,40 +131,6 @@ check_buffers (guint expected) } } -GST_START_TEST (test_opus_id_header) -{ - GstElement *opusdec; - GstBuffer *inbuffer; - GstCaps *caps; - - opusdec = setup_opusdec (); - fail_unless (gst_element_set_state (opusdec, - GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, - "could not set to playing"); - - caps = gst_caps_new_empty_simple ("audio/x-opus"); - gst_check_setup_events (mydecsrcpad, opusdec, caps, GST_FORMAT_TIME); - gst_caps_unref (caps); - - inbuffer = gst_buffer_new_and_alloc (sizeof (opus_ogg_id_header)); - gst_buffer_fill (inbuffer, 0, opus_ogg_id_header, - sizeof (opus_ogg_id_header)); - ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - gst_buffer_ref (inbuffer); - - /* pushing gives away my reference ... */ - fail_unless (gst_pad_push (mydecsrcpad, inbuffer) == GST_FLOW_OK); - /* ... and nothing ends up on the global buffer list */ - ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - gst_buffer_unref (inbuffer); - check_buffers (0); - - /* cleanup */ - cleanup_opusdec (opusdec); -} - -GST_END_TEST; - GST_START_TEST (test_opus_encode_nothing) { GstElement *opusenc; @@ -365,7 +326,6 @@ opus_suite (void) suite_add_tcase (s, tc_chain); #define X if (0) - tcase_add_test (tc_chain, test_opus_id_header); tcase_add_test (tc_chain, test_opus_encode_nothing); tcase_add_test (tc_chain, test_opus_decode_nothing); tcase_add_test (tc_chain, test_opus_encode_samples); From dd741e6412cf9e31aefb0de5aeb66e196a0499e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 5 Nov 2015 12:11:19 +0100 Subject: [PATCH 192/197] opusdec: Update sink pad templates We always require the channel-mapping-field. If it's 0 we require nothing else, otherwise we need channels, stream-count and coupled count to be available. --- ext/opus/gstopusdec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 8ca2809a30..c7cbb7c29c 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -64,10 +64,15 @@ GST_STATIC_PAD_TEMPLATE ("src", ); static GstStaticPadTemplate opus_dec_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-opus") + GST_STATIC_CAPS ("audio/x-opus, " + "channel-mapping-family = (int) 0; " + "audio/x-opus, " + "channel-mapping-family = (int) [1, 255], " + "channels = (int) [1, 255], " + "stream-count = (int) [1, 255], " "coupled-count = (int) [0, 255]") ); G_DEFINE_TYPE (GstOpusDec, gst_opus_dec, GST_TYPE_AUDIO_DECODER); From 6463ff198e8c106b8ceba7405777327e1beabd41 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 12 Nov 2015 12:21:54 +0000 Subject: [PATCH 193/197] opusenc: avoid potential overflow expression The result of the two expressions will be promoted to guint64 anyway, perform all the arithmetic in 64 bits to avoid potential overflows. CID 1338690, CID 1338691 --- ext/opus/gstopusenc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 729b2150c1..d4a685adcb 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1007,7 +1007,7 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) trim_start = enc->pending_lookahead; enc->pending_lookahead = 0; } else { - trim_start = input_samples * 48000 / enc->sample_rate; + trim_start = ((guint64) input_samples) * 48000 / enc->sample_rate; enc->pending_lookahead -= trim_start; output_samples = 0; } @@ -1026,7 +1026,8 @@ gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) output_samples = enc->consumed_samples - enc->encoded_samples; input_samples = 0; GST_DEBUG_OBJECT (enc, "draining %d samples", output_samples); - trim_end = (frame_samples - output_samples) * 48000 / enc->sample_rate; + trim_end = + ((guint64) frame_samples - output_samples) * 48000 / enc->sample_rate; } else if (enc->encoded_samples == enc->consumed_samples) { GST_DEBUG_OBJECT (enc, "nothing to drain"); goto done; From 71c2ddd07d004fc22d610f0072158598089eccd4 Mon Sep 17 00:00:00 2001 From: "Reynaldo H. Verdejo Pinochet" Date: Tue, 17 Nov 2015 15:23:17 -0800 Subject: [PATCH 194/197] Remove unnecessary NULL checks before g_free() g_free() is NULL-safe --- ext/opus/gstopusenc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index d4a685adcb..9214837caf 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -1091,8 +1091,7 @@ done: if (bdata) gst_buffer_unmap (buf, &map); - if (mdata) - g_free (mdata); + g_free (mdata); return ret; } From 95bf0ae2ae3b4ae2843ef880417014cfd742d351 Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Mon, 14 Dec 2015 11:09:46 +0900 Subject: [PATCH 195/197] plugins-bad: Fix example pipelines rename gst-launch --> gst-launch-1.0 replace old elements with new elements(ffmpegcolorspace -> videoconvert, ffenc_** -> avenc_**) fix caps in examples https://bugzilla.gnome.org/show_bug.cgi?id=759432 --- ext/opus/gstopusdec.c | 2 +- ext/opus/gstopusenc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index c7cbb7c29c..f2512d119f 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -33,7 +33,7 @@ * * Example pipelines * |[ - * gst-launch -v filesrc location=opus.ogg ! oggdemux ! opusdec ! audioconvert ! audioresample ! alsasink + * gst-launch-1.0 -v filesrc location=opus.ogg ! oggdemux ! opusdec ! audioconvert ! audioresample ! alsasink * ]| Decode an Ogg/Opus file. To create an Ogg/Opus file refer to the documentation of opusenc. * */ diff --git a/ext/opus/gstopusenc.c b/ext/opus/gstopusenc.c index 9214837caf..7737bf5758 100644 --- a/ext/opus/gstopusenc.c +++ b/ext/opus/gstopusenc.c @@ -32,7 +32,7 @@ * * Example pipelines * |[ - * gst-launch -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! opusenc ! oggmux ! filesink location=sine.ogg + * gst-launch-1.0 -v audiotestsrc wave=sine num-buffers=100 ! audioconvert ! opusenc ! oggmux ! filesink location=sine.ogg * ]| Encode a test sine signal to Ogg/OPUS. * */ From 8e9345ec865a3d76b12026066df9d607cf3f1145 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 2 Feb 2016 15:20:48 +0000 Subject: [PATCH 196/197] opusdec: fix wrong buffer being checked for missing data This caused a decoding error if the resulting (wrong) buffer size was passed to the Opus decoding API. https://bugzilla.gnome.org/show_bug.cgi?id=758158 --- ext/opus/gstopusdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index f2512d119f..649cc0c61f 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -441,10 +441,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) size = 0; } - if (gst_buffer_get_size (buffer) == 0) { + if (gst_buffer_get_size (buf) == 0) { GstClockTime const opus_plc_alignment = 2500 * GST_USECOND; GstClockTime aligned_missing_duration; - GstClockTime missing_duration = GST_BUFFER_DURATION (buffer); + GstClockTime missing_duration = GST_BUFFER_DURATION (buf); GST_DEBUG_OBJECT (dec, "missing buffer, doing PLC duration %" GST_TIME_FORMAT From 1ef601e7d3c5689ef6832c21e05b1c75865d9194 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Wed, 3 Feb 2016 16:28:42 +0000 Subject: [PATCH 197/197] opus: fix FEC FEC may only be used when PLC is enabled on the audio decoder, as it relies on empty buffers to generate audio from the next buffer. Hooking to the gap events doesn't work as the audio decoder does not like more buffers output than it sends. The length of data to generate using FEC from the next packet is determined by rounding the gap duration to nearest. This ensures that duration imprecision does not cause quantization to 2.5 milliseconds less than available. Doing so causes the Opus API to fail decoding. Such duration imprecision is common in live cases. The buffer to consider when determining the length of audio to be decoded is the previous buffer when using FEC, and the new buffer otherwise. In the FEC case, this means we determine the amount of audio from the previous buffer, whether it was missing or not (and get the data either from this buffer, or the current one if the previous one was missing). --- ext/opus/gstopusdec.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 649cc0c61f..1470ea3214 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -133,8 +133,8 @@ gst_opus_dec_class_init (GstOpusDecClass * klass) "Vincent Penquerc'h "); g_object_class_install_property (gobject_class, PROP_USE_INBAND_FEC, g_param_spec_boolean ("use-inband-fec", "Use in-band FEC", - "Use forward error correction if available", DEFAULT_USE_INBAND_FEC, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Use forward error correction if available (needs PLC enabled)", + DEFAULT_USE_INBAND_FEC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_APPLY_GAIN, g_param_spec_boolean ("apply-gain", "Apply gain", @@ -367,7 +367,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) GstFlowReturn res = GST_FLOW_OK; gsize size; guint8 *data; - GstBuffer *outbuf; + GstBuffer *outbuf, *bufd; gint16 *out_data; int n, err; int samples; @@ -429,6 +429,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) && gst_buffer_get_size (dec->last_buffer) > 0) ? dec->last_buffer : buffer; + /* That's the buffer we get duration from */ + bufd = dec->use_inband_fec ? dec->last_buffer : buffer; + if (buf && gst_buffer_get_size (buf) > 0) { gst_buffer_map (buf, &map, GST_MAP_READ); data = map.data; @@ -441,10 +444,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) size = 0; } - if (gst_buffer_get_size (buf) == 0) { + if (gst_buffer_get_size (bufd) == 0) { GstClockTime const opus_plc_alignment = 2500 * GST_USECOND; GstClockTime aligned_missing_duration; - GstClockTime missing_duration = GST_BUFFER_DURATION (buf); + GstClockTime missing_duration = GST_BUFFER_DURATION (bufd); GST_DEBUG_OBJECT (dec, "missing buffer, doing PLC duration %" GST_TIME_FORMAT @@ -455,9 +458,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer) missing_duration += dec->leftover_plc_duration; /* align the combined buffer and leftover PLC duration to multiples - * of 2.5ms, always rounding down, and store excess duration for later */ + * of 2.5ms, rounding to nearest, and store excess duration for later */ aligned_missing_duration = - (missing_duration / opus_plc_alignment) * opus_plc_alignment; + ((missing_duration + + opus_plc_alignment / 2) / opus_plc_alignment) * opus_plc_alignment; dec->leftover_plc_duration = missing_duration - aligned_missing_duration; /* Opus' PLC cannot operate with less than 2.5ms; skip PLC