opus: port to encoder/decoder base classes
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Mon, 14 Nov 2011 13:41:58 +0000 (13:41 +0000)
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Mon, 14 Nov 2011 13:50:23 +0000 (13:50 +0000)
ext/opus/Makefile.am
ext/opus/gstopusdec.c
ext/opus/gstopusdec.h
ext/opus/gstopusenc.c
ext/opus/gstopusenc.h

index aa50ba9..57e1692 100644 (file)
@@ -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) \
index 199731e..8aa9946 100644 (file)
@@ -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 <sebastian.droege@collabora.co.uk>");
 
-  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)
-{
-  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;
-  }
-}
-
 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",
-      "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)
+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,
@@ -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;
+
+  GST_DEBUG_OBJECT (dec, "set_format: %" GST_PTR_FORMAT, caps);
 
-  if (GST_BUFFER_IS_DISCONT (buf)) {
-    dec->discont = TRUE;
+  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;
 }
index 57db884..c29b6b1 100644 (file)
@@ -22,6 +22,7 @@
 #define __GST_OPUS_DEC_H__
 
 #include <gst/gst.h>
+#include <gst/audio/gstaudiodecoder.h>
 #include <opus/opus.h>
 
 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);
index 484a84b..363eb69 100644 (file)
@@ -47,6 +47,7 @@
 
 #include <gst/gsttagsetter.h>
 #include <gst/tag/tag.h>
+#include <gst/base/gstbytewriter.h>
 #include <gst/audio/audio.h>
 #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 <sebastian.droege@collabora.co.uk>");
 
+  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,386 +237,167 @@ 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)
+static void
+gst_opus_enc_init (GstOpusEnc * enc)
 {
-  GstOpusEnc *enc;
-  GstStructure *structure;
-  GstCaps *otherpadcaps;
+  GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc);
 
-  enc = GST_OPUS_ENC (GST_PAD_PARENT (pad));
-  enc->setup = FALSE;
+  GST_DEBUG_OBJECT (enc, "init");
+
+  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;
-  otherpadcaps = gst_pad_get_allowed_caps (pad);
+  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;
 
-  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);
+  /* arrange granulepos marking (and required perfect ts) */
+  gst_audio_encoder_set_mark_granule (benc, TRUE);
+  gst_audio_encoder_set_perfect_timestamp (benc, TRUE);
+}
 
-  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);
+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;
+}
 
-  GST_DEBUG_OBJECT (pad, "channels=%d rate=%d frame-size=%d",
-      enc->n_channels, enc->sample_rate, enc->frame_size);
+static gint
+gst_opus_enc_get_frame_samples (GstOpusEnc * enc)
+{
+  gint frame_samples = 0;
   switch (enc->frame_size) {
     case 2:
-      enc->frame_samples = enc->sample_rate / 400;
+      frame_samples = enc->sample_rate / 400;
       break;
     case 5:
-      enc->frame_samples = enc->sample_rate / 200;
+      frame_samples = enc->sample_rate / 200;
       break;
     case 10:
-      enc->frame_samples = enc->sample_rate / 100;
+      frame_samples = enc->sample_rate / 100;
       break;
     case 20:
-      enc->frame_samples = enc->sample_rate / 50;
+      frame_samples = enc->sample_rate / 50;
       break;
     case 40:
-      enc->frame_samples = enc->sample_rate / 25;
+      frame_samples = enc->sample_rate / 25;
       break;
     case 60:
-      enc->frame_samples = 3 * enc->sample_rate / 50;
+      frame_samples = 3 * enc->sample_rate / 50;
       break;
     default:
       GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size);
-      return FALSE;
+      frame_samples = 0;
       break;
   }
-  GST_DEBUG_OBJECT (pad, "frame_samples %d", enc->frame_samples);
-
-  gst_opus_enc_setup (enc);
-
-  return TRUE;
+  return frame_samples;
 }
 
-
-static GstCaps *
-gst_opus_enc_sink_getcaps (GstPad * pad, GstCaps * filter)
+static gboolean
+gst_opus_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
 {
-  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);
-      }
+  GstOpusEnc *enc;
 
-      if (gst_structure_get_int (ps, "channels", &channels)) {
-        gst_structure_fixate_field_nearest_int (s, "channels", channels);
-      }
-    }
-    gst_caps_unref (peercaps);
-  }
+  enc = GST_OPUS_ENC (benc);
 
-  gst_object_unref (enc);
+  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);
 
-  if (filter) {
-    GstCaps *new_caps =
-        gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
-    gst_caps_unref (caps);
-    caps = new_caps;
+  /* handle reconfigure */
+  if (enc->state) {
+    opus_encoder_destroy (enc->state);
+    enc->state = NULL;
   }
 
-  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)
+  if (!gst_opus_enc_setup (enc))
     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;
-}
+  enc->frame_samples = gst_opus_enc_get_frame_samples (enc);
 
-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;
+  /* feedback to base class */
+  gst_audio_encoder_set_latency (benc,
+      gst_opus_enc_get_latency (enc), gst_opus_enc_get_latency (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;
+  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)
 {
-  return gst_util_uint64_scale (enc->frame_samples, GST_SECOND,
+  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 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)
+static GstBuffer *
+gst_opus_enc_create_id_buffer (GstOpusEnc * enc)
 {
-  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;
-  }
+  GstBuffer *buffer;
+  GstByteWriter bw;
 
-error:
-  return res;
-}
+  gst_byte_writer_init (&bw);
 
-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));
-
-  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);
-
-  enc->n_channels = -1;
-  enc->sample_rate = -1;
-  enc->frame_samples = 0;
+  /* 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 ? */
 
-  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;
+  buffer = gst_byte_writer_reset_and_get_buffer (&bw);
 
-  enc->setup = FALSE;
-  enc->header_sent = FALSE;
+  GST_BUFFER_OFFSET (buffer) = 0;
+  GST_BUFFER_OFFSET_END (buffer) = 0;
 
-  enc->adapter = gst_adapter_new ();
+  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 = 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_map (buf, &bsize, NULL, GST_MAP_READ);
 
-    gst_buffer_memset (buf, 0, 0, diff);
-    gst_adapter_push (enc->adapter, buf);
-  }
+    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 (gst_adapter_available (enc->adapter) >= bytes) {
-    gint16 *data;
+  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;
 }
 
-static GstFlowReturn
-gst_opus_enc_chain (GstPad * pad, GstBuffer * buf)
+/*
+ * (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, ...)
 {
-  GstOpusEnc *enc;
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  enc = GST_OPUS_ENC (GST_PAD_PARENT (pad));
+  GstStructure *structure = NULL;
+  va_list va;
+  GValue array = { 0 };
+  GValue value = { 0 };
 
-  if (!enc->setup)
-    goto not_setup;
+  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);
 
-  if (!enc->header_sent) {
-    GstCaps *caps;
+  caps = gst_caps_make_writable (caps);
+  structure = gst_caps_get_structure (caps, 0);
 
-    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);
+  g_value_init (&array, GST_TYPE_ARRAY);
 
-    /* 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_caps_unref (caps);
+  va_start (va, buf);
+  /* put buffers in a fixed list */
+  while (buf) {
+    g_assert (gst_buffer_is_writable (buf));
 
-    enc->header_sent = TRUE;
-  }
+    /* mark buffer */
+    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
 
-  GST_DEBUG_OBJECT (enc, "received buffer of %u bytes",
-      gst_buffer_get_size (buf));
+    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);
 
-  /* 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;
-    }
+    buf = va_arg (va, GstBuffer *);
   }
 
+  gst_structure_set_value (structure, field, &array);
+  g_value_unset (&array);
 
-  /* 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);
+  return caps;
+}
 
-    GST_BUFFER_TIMESTAMP (buf) += diff;
-    if (GST_BUFFER_DURATION_IS_VALID (buf))
-      GST_BUFFER_DURATION (buf) -= diff;
-  }
+static GstFlowReturn
+gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
+{
+  GstOpusEnc *enc;
+  GstFlowReturn ret = GST_FLOW_OK;
 
-  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);
+  enc = GST_OPUS_ENC (benc);
 
-    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_DEBUG_OBJECT (enc, "handle_frame");
 
-      gst_opus_enc_encode (enc, TRUE);
+  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;
 
-      enc->frameno_out = 0;
-      enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
-    }
-  }
+    /* create header buffers */
+    buf1 = gst_opus_enc_create_id_buffer (enc);
+    buf2 = gst_opus_enc_create_metadata_buffer (enc);
 
-  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;
+    /* 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);
 
-  /* push buffer to adapter */
-  gst_adapter_push (enc->adapter, buf);
-  buf = NULL;
+    /* negotiate with these caps */
+    GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps);
 
-  ret = gst_opus_enc_encode (enc, FALSE);
+    gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps);
+    gst_caps_unref (caps);
 
-done:
+    /* 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);
 
-  if (buf)
-    gst_buffer_unref (buf);
+    enc->header_sent = TRUE;
+  }
 
-  return ret;
+  GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf,
+      buf ? gst_buffer_get_size (buf) : 0);
 
-  /* ERRORS */
-not_setup:
-  {
-    GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL),
-        ("encoder not initialized (input is not audio?)"));
-    ret = GST_FLOW_NOT_NEGOTIATED;
-    goto done;
-  }
+  ret = gst_opus_enc_encode (enc, buf);
 
+  return ret;
 }
 
-
 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;
-}
index 5cb5459..5d4e3c4 100644 (file)
@@ -24,7 +24,7 @@
 
 
 #include <gst/gst.h>
-#include <gst/base/gstadapter.h>
+#include <gst/audio/gstaudioencoder.h>
 
 #include <opus/opus.h>
 
@@ -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;
-
-  guint64               samples_in;
-  guint64               bytes_out;
-
-  guint64               frameno;
-  guint64               frameno_out;
+  GSList               *headers;
 
-  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);