]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blobdiff - ext/amrnb/amrnbdec.c
amrnbdec: _parse should not return OK if not enough data yet
[glsdk/gst-plugins-ugly0-10.git] / ext / amrnb / amrnbdec.c
index 0749b7c4a759fd1151591bd580ffc41721e85e44..bf59d8de5005917a7378b81f11c99c065f6f6206 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+/**
+ * SECTION:element-amrnbdec
+ * @see_also: #GstAmrnbEnc, #GstAmrParse
+ *
+ * AMR narrowband decoder based on the 
+ * <ulink url="http://sourceforge.net/projects/opencore-amr">opencore codec implementation</ulink>.
+ * 
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch filesrc location=abc.amr ! amrparse ! amrnbdec ! audioresample ! audioconvert ! alsasink
+ * ]|
+ * </refsect2>
+ */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -40,109 +55,171 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
         "rate = (int) 8000," "channels = (int) 1")
     );
 
-static const gint block_size[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5,
-  -1, -1, -1, -1, -1, -1, 0
+GST_DEBUG_CATEGORY_STATIC (gst_amrnbdec_debug);
+#define GST_CAT_DEFAULT gst_amrnbdec_debug
+
+static const gint block_size_if1[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5,
+  0, 0, 0, 0, 0, 0, 0
 };
 
-static void gst_amrnbdec_base_init (GstAmrnbDecClass * klass);
-static void gst_amrnbdec_class_init (GstAmrnbDecClass * klass);
-static void gst_amrnbdec_init (GstAmrnbDec * amrnbdec);
+static const gint block_size_if2[16] = { 12, 13, 15, 17, 18, 20, 25, 30, 5,
+  0, 0, 0, 0, 0, 0, 0
+};
 
-static gboolean gst_amrnbdec_event (GstPad * pad, GstEvent * event);
-static GstFlowReturn gst_amrnbdec_chain (GstPad * pad, GstBuffer * buffer);
-static gboolean gst_amrnbdec_setcaps (GstPad * pad, GstCaps * caps);
-static GstStateChangeReturn gst_amrnbdec_state_change (GstElement * element,
-    GstStateChange transition);
+static GType
+gst_amrnb_variant_get_type (void)
+{
+  static GType gst_amrnb_variant_type = 0;
+  static const GEnumValue gst_amrnb_variant[] = {
+    {GST_AMRNB_VARIANT_IF1, "IF1", "IF1"},
+    {GST_AMRNB_VARIANT_IF2, "IF2", "IF2"},
+    {0, NULL, NULL},
+  };
+  if (!gst_amrnb_variant_type) {
+    gst_amrnb_variant_type =
+        g_enum_register_static ("GstAmrnbVariant", gst_amrnb_variant);
+  }
+  return gst_amrnb_variant_type;
+}
 
-static GstElementClass *parent_class = NULL;
+#define GST_AMRNB_VARIANT_TYPE (gst_amrnb_variant_get_type())
 
-GType
-gst_amrnbdec_get_type (void)
+#define VARIANT_DEFAULT GST_AMRNB_VARIANT_IF1
+enum
 {
-  static GType amrnbdec_type = 0;
-
-  if (!amrnbdec_type) {
-    static const GTypeInfo amrnbdec_info = {
-      sizeof (GstAmrnbDecClass),
-      (GBaseInitFunc) gst_amrnbdec_base_init,
-      NULL,
-      (GClassInitFunc) gst_amrnbdec_class_init,
-      NULL,
-      NULL,
-      sizeof (GstAmrnbDec),
-      0,
-      (GInstanceInitFunc) gst_amrnbdec_init,
-    };
-
-    amrnbdec_type = g_type_register_static (GST_TYPE_ELEMENT,
-        "GstAmrnbDec", &amrnbdec_info, 0);
-  }
+  PROP_0,
+  PROP_VARIANT
+};
 
-  return amrnbdec_type;
-}
+static void gst_amrnbdec_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_amrnbdec_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static gboolean gst_amrnbdec_start (GstAudioDecoder * dec);
+static gboolean gst_amrnbdec_stop (GstAudioDecoder * dec);
+static gboolean gst_amrnbdec_set_format (GstAudioDecoder * dec, GstCaps * caps);
+static gboolean gst_amrnbdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
+    gint * offset, gint * length);
+static GstFlowReturn gst_amrnbdec_handle_frame (GstAudioDecoder * dec,
+    GstBuffer * buffer);
+
+#define _do_init(bla) \
+    GST_DEBUG_CATEGORY_INIT (gst_amrnbdec_debug, "amrnbdec", 0, "AMR-NB audio decoder");
+
+GST_BOILERPLATE_FULL (GstAmrnbDec, gst_amrnbdec, GstAudioDecoder,
+    GST_TYPE_AUDIO_DECODER, _do_init);
 
 static void
-gst_amrnbdec_base_init (GstAmrnbDecClass * klass)
+gst_amrnbdec_base_init (gpointer klass)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-  GstElementDetails gst_amrnbdec_details = {
-    "AMR-NB decoder",
-    "Codec/Decoder/Audio",
-    "Adaptive Multi-Rate Narrow-Band audio decoder",
-    "Ronald Bultje <rbultje@ronald.bitfreak.net>"
-  };
 
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&sink_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&src_template));
+  gst_element_class_add_static_pad_template (element_class,
+      &sink_template);
+  gst_element_class_add_static_pad_template (element_class, &src_template);
 
-  gst_element_class_set_details (element_class, &gst_amrnbdec_details);
+  gst_element_class_set_details_simple (element_class, "AMR-NB audio decoder",
+      "Codec/Decoder/Audio",
+      "Adaptive Multi-Rate Narrow-Band audio decoder",
+      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
 }
 
 static void
 gst_amrnbdec_class_init (GstAmrnbDecClass * klass)
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
+
+  object_class->set_property = gst_amrnbdec_set_property;
+  object_class->get_property = gst_amrnbdec_get_property;
+
+  base_class->start = GST_DEBUG_FUNCPTR (gst_amrnbdec_start);
+  base_class->stop = GST_DEBUG_FUNCPTR (gst_amrnbdec_stop);
+  base_class->set_format = GST_DEBUG_FUNCPTR (gst_amrnbdec_set_format);
+  base_class->parse = GST_DEBUG_FUNCPTR (gst_amrnbdec_parse);
+  base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_amrnbdec_handle_frame);
+
+  g_object_class_install_property (object_class, PROP_VARIANT,
+      g_param_spec_enum ("variant", "Variant",
+          "The decoder variant", GST_AMRNB_VARIANT_TYPE,
+          VARIANT_DEFAULT,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_amrnbdec_init (GstAmrnbDec * amrnbdec, GstAmrnbDecClass * klass)
+{
+}
+
+static gboolean
+gst_amrnbdec_start (GstAudioDecoder * dec)
+{
+  GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
+
+  GST_DEBUG_OBJECT (dec, "start");
+  if (!(amrnbdec->handle = Decoder_Interface_init ()))
+    return FALSE;
+
+  amrnbdec->rate = 0;
+  amrnbdec->channels = 0;
+
+  return TRUE;
+}
+
+static gboolean
+gst_amrnbdec_stop (GstAudioDecoder * dec)
+{
+  GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  GST_DEBUG_OBJECT (dec, "stop");
+  Decoder_Interface_exit (amrnbdec->handle);
 
-  element_class->change_state = gst_amrnbdec_state_change;
+  return TRUE;
 }
 
 static void
-gst_amrnbdec_init (GstAmrnbDec * amrnbdec)
+gst_amrnbdec_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
 {
-  /* create the sink pad */
-  amrnbdec->sinkpad =
-      gst_pad_new_from_template (gst_static_pad_template_get (&sink_template),
-      "sink");
-  gst_pad_set_setcaps_function (amrnbdec->sinkpad, gst_amrnbdec_setcaps);
-  gst_pad_set_event_function (amrnbdec->sinkpad, gst_amrnbdec_event);
-  gst_pad_set_chain_function (amrnbdec->sinkpad, gst_amrnbdec_chain);
-  gst_element_add_pad (GST_ELEMENT (amrnbdec), amrnbdec->sinkpad);
-
-  /* create the src pad */
-  amrnbdec->srcpad =
-      gst_pad_new_from_template (gst_static_pad_template_get (&src_template),
-      "src");
-  gst_pad_use_fixed_caps (amrnbdec->srcpad);
-  gst_element_add_pad (GST_ELEMENT (amrnbdec), amrnbdec->srcpad);
-
-  amrnbdec->adapter = gst_adapter_new ();
-
-  /* init rest */
-  amrnbdec->handle = NULL;
+  GstAmrnbDec *self = GST_AMRNBDEC (object);
+
+  switch (prop_id) {
+    case PROP_VARIANT:
+      self->variant = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  return;
+}
+
+static void
+gst_amrnbdec_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstAmrnbDec *self = GST_AMRNBDEC (object);
+
+  switch (prop_id) {
+    case PROP_VARIANT:
+      g_value_set_enum (value, self->variant);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+  return;
 }
 
 static gboolean
-gst_amrnbdec_setcaps (GstPad * pad, GstCaps * caps)
+gst_amrnbdec_set_format (GstAudioDecoder * dec, GstCaps * caps)
 {
   GstStructure *structure;
   GstAmrnbDec *amrnbdec;
   GstCaps *copy;
 
-  amrnbdec = GST_AMRNBDEC (gst_pad_get_parent (pad));
+  amrnbdec = GST_AMRNBDEC (dec);
 
   structure = gst_caps_get_structure (caps, 0);
 
@@ -158,163 +235,95 @@ gst_amrnbdec_setcaps (GstPad * pad, GstCaps * caps)
       "endianness", G_TYPE_INT, G_BYTE_ORDER,
       "rate", G_TYPE_INT, amrnbdec->rate, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
 
-  amrnbdec->duration = gst_util_uint64_scale_int (GST_SECOND, 160,
-      amrnbdec->rate * amrnbdec->channels);
-
-  gst_pad_set_caps (amrnbdec->srcpad, copy);
+  gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), copy);
   gst_caps_unref (copy);
 
-  gst_object_unref (amrnbdec);
-
   return TRUE;
 }
 
-static gboolean
-gst_amrnbdec_event (GstPad * pad, GstEvent * event)
+static GstFlowReturn
+gst_amrnbdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
+    gint * offset, gint * length)
 {
-  GstAmrnbDec *amrnbdec;
-  gboolean ret = TRUE;
+  GstAmrnbDec *amrnbdec = GST_AMRNBDEC (dec);
+  const guint8 *data;
+  guint size;
+  gboolean sync, eos;
+  gint block, mode;
 
-  amrnbdec = GST_AMRNBDEC (gst_pad_get_parent (pad));
+  size = gst_adapter_available (adapter);
+  g_return_val_if_fail (size > 0, GST_FLOW_ERROR);
 
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_FLUSH_START:
-      ret = gst_pad_push_event (amrnbdec->srcpad, event);
-      break;
-    case GST_EVENT_FLUSH_STOP:
-      ret = gst_pad_push_event (amrnbdec->srcpad, event);
-      gst_adapter_clear (amrnbdec->adapter);
-      amrnbdec->ts = -1;
+  gst_audio_decoder_get_parse_state (dec, &sync, &eos);
+
+  /* need to peek data to get the size */
+  if (size < 1)
+    return GST_FLOW_ERROR;
+
+  data = gst_adapter_peek (adapter, 1);
+
+  /* get size */
+  switch (amrnbdec->variant) {
+    case GST_AMRNB_VARIANT_IF1:
+      mode = (data[0] >> 3) & 0x0F;
+      block = block_size_if1[mode] + 1;
       break;
-    case GST_EVENT_EOS:
-      gst_adapter_clear (amrnbdec->adapter);
-      ret = gst_pad_push_event (amrnbdec->srcpad, event);
+    case GST_AMRNB_VARIANT_IF2:
+      mode = data[0] & 0x0F;
+      block = block_size_if2[mode] + 1;
       break;
     default:
-      ret = gst_pad_push_event (amrnbdec->srcpad, event);
+      g_assert_not_reached ();
+      return GST_FLOW_ERROR;
       break;
   }
-  gst_object_unref (amrnbdec);
 
-  return ret;
+  GST_DEBUG_OBJECT (amrnbdec, "mode %d, block %d", mode, block);
+
+  if (block > size)
+    return GST_FLOW_UNEXPECTED;
+
+  *offset = 0;
+  *length = block;
+
+  return GST_FLOW_OK;
 }
 
 static GstFlowReturn
-gst_amrnbdec_chain (GstPad * pad, GstBuffer * buffer)
+gst_amrnbdec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
 {
   GstAmrnbDec *amrnbdec;
-  GstFlowReturn ret;
+  guint8 *data;
+  GstBuffer *out;
+
+  amrnbdec = GST_AMRNBDEC (dec);
 
-  amrnbdec = GST_AMRNBDEC (gst_pad_get_parent (pad));
+  /* no fancy flushing */
+  if (!buffer || !GST_BUFFER_SIZE (buffer))
+    return GST_FLOW_OK;
 
   if (amrnbdec->rate == 0 || amrnbdec->channels == 0)
     goto not_negotiated;
 
-  /* discontinuity, don't combine samples before and after the
-   * DISCONT */
-  if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
-    gst_adapter_clear (amrnbdec->adapter);
-    amrnbdec->ts = -1;
-  }
-
-  /* take latest timestamp, FIXME timestamp is the one of the
-   * first buffer in the adapter. */
-  if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
-    amrnbdec->ts = GST_BUFFER_TIMESTAMP (buffer);
-
-  gst_adapter_push (amrnbdec->adapter, buffer);
+  /* the library seems to write into the source data, hence
+   * the copy. */
+  /* should not be a problem though */
+  data = GST_BUFFER_DATA (buffer);
 
-  ret = GST_FLOW_OK;
+  /* get output */
+  out = gst_buffer_new_and_alloc (160 * 2);
 
-  while (TRUE) {
-    GstBuffer *out;
-    guint8 *data;
-    gint block, mode;
-
-    if (gst_adapter_available (amrnbdec->adapter) < 1)
-      break;
-    data = (guint8 *) gst_adapter_peek (amrnbdec->adapter, 1);
-
-    /* get size */
-    mode = (data[0] >> 3) & 0x0F;
-    block = block_size[mode] + 1;
-
-    if (gst_adapter_available (amrnbdec->adapter) < block)
-      break;
-    /* the library seems to write into the source data, hence
-     * the copy. */
-    data = gst_adapter_take (amrnbdec->adapter, block);
-
-    /* get output */
-    out = gst_buffer_new_and_alloc (160 * 2);
-    GST_BUFFER_DURATION (out) = amrnbdec->duration;
-    GST_BUFFER_TIMESTAMP (out) = amrnbdec->ts;
-    if (amrnbdec->ts != -1)
-      amrnbdec->ts += amrnbdec->duration;
-    gst_buffer_set_caps (out, GST_PAD_CAPS (amrnbdec->srcpad));
-
-    /* decode */
-    Decoder_Interface_Decode (amrnbdec->handle, data,
-        (short *) GST_BUFFER_DATA (out), 0);
-    g_free (data);
-
-    /* play */
-    ret = gst_pad_push (amrnbdec->srcpad, out);
-  }
-  gst_object_unref (amrnbdec);
+  /* decode */
+  Decoder_Interface_Decode (amrnbdec->handle, data,
+      (short *) GST_BUFFER_DATA (out), 0);
 
-  return ret;
+  return gst_audio_decoder_finish_frame (dec, out, 1);
 
   /* ERRORS */
 not_negotiated:
   {
     GST_ELEMENT_ERROR (amrnbdec, STREAM, TYPE_NOT_FOUND, (NULL),
         ("Decoder is not initialized"));
-    gst_object_unref (amrnbdec);
     return GST_FLOW_NOT_NEGOTIATED;
   }
 }
-
-static GstStateChangeReturn
-gst_amrnbdec_state_change (GstElement * element, GstStateChange transition)
-{
-  GstAmrnbDec *amrnbdec;
-  GstStateChangeReturn ret;
-
-  amrnbdec = GST_AMRNBDEC (element);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      if (!(amrnbdec->handle = Decoder_Interface_init ()))
-        goto init_failed;
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      gst_adapter_clear (amrnbdec->adapter);
-      amrnbdec->rate = 0;
-      amrnbdec->channels = 0;
-      amrnbdec->ts = -1;
-      break;
-    default:
-      break;
-  }
-
-  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      Decoder_Interface_exit (amrnbdec->handle);
-      break;
-    default:
-      break;
-  }
-
-  return ret;
-
-  /* ERRORS */
-init_failed:
-  {
-    GST_ELEMENT_ERROR (amrnbdec, LIBRARY, INIT, (NULL),
-        ("Failed to open AMR Decoder"));
-    return GST_STATE_CHANGE_FAILURE;
-  }
-}