Remove plugin specific static build option
[platform/upstream/gst-plugins-good.git] / gst / audioparsers / gstsbcparse.c
index c954b5c..12c39d9 100644 (file)
@@ -40,9 +40,8 @@
 #include <string.h>
 #include <gst/tag/tag.h>
 #include <gst/audio/audio.h>
-
-#include <gst/base/gstbitreader.h>
-#include <gst/base/gstbytereader.h>
+#include <gst/base/base.h>
+#include <gst/pbutils/pbutils.h>
 
 #define SBC_SYNCBYTE 0x9C
 
@@ -67,6 +66,8 @@ static gboolean gst_sbc_parse_start (GstBaseParse * parse);
 static gboolean gst_sbc_parse_stop (GstBaseParse * parse);
 static GstFlowReturn gst_sbc_parse_handle_frame (GstBaseParse * parse,
     GstBaseParseFrame * frame, gint * skipsize);
+static GstFlowReturn gst_sbc_parse_pre_push_frame (GstBaseParse * parse,
+    GstBaseParseFrame * frame);
 static GstCaps *gst_sbc_parse_get_sink_caps (GstBaseParse * parse,
     GstCaps * filter);
 
@@ -90,16 +91,15 @@ gst_sbc_parse_class_init (GstSbcParseClass * klass)
 
   baseparse_class->start = GST_DEBUG_FUNCPTR (gst_sbc_parse_start);
   baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_sbc_parse_stop);
-
+  baseparse_class->pre_push_frame =
+      GST_DEBUG_FUNCPTR (gst_sbc_parse_pre_push_frame);
   baseparse_class->handle_frame =
       GST_DEBUG_FUNCPTR (gst_sbc_parse_handle_frame);
   baseparse_class->get_sink_caps =
       GST_DEBUG_FUNCPTR (gst_sbc_parse_get_sink_caps);
 
-  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_static_metadata (element_class, "SBC audio parser",
       "Codec/Parser/Audio", "Parses an SBC bluetooth audio stream",
@@ -115,12 +115,15 @@ gst_sbc_parse_reset (GstSbcParse * sbcparse)
   sbcparse->n_blocks = -1;
   sbcparse->n_subbands = -1;
   sbcparse->bitpool = -1;
+  sbcparse->sent_codec_tag = FALSE;
 }
 
 static void
 gst_sbc_parse_init (GstSbcParse * sbcparse)
 {
   gst_sbc_parse_reset (sbcparse);
+  GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (sbcparse));
+  GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (sbcparse));
 }
 
 static gboolean
@@ -184,7 +187,8 @@ gst_sbc_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame,
   GstSbcChannelMode ch_mode = GST_SBC_CHANNEL_MODE_INVALID;
   GstMapInfo map;
   guint rate = 0, n_blocks = 0, n_subbands = 0, bitpool = 0;
-  gsize frame_len;
+  gsize frame_len, next_len;
+  gint i, max_frames;
 
   gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
 
@@ -241,8 +245,31 @@ gst_sbc_parse_handle_frame (GstBaseParse * parse, GstBaseParseFrame * frame,
   GST_BUFFER_OFFSET (frame->buffer) = GST_BUFFER_OFFSET_NONE;
   GST_BUFFER_OFFSET_END (frame->buffer) = GST_BUFFER_OFFSET_NONE;
 
+  /* completely arbitrary limit, we only process data we already have,
+   * so we aren't introducing latency here */
+  max_frames = MIN (map.size / frame_len, n_blocks * n_subbands * 5);
+  GST_LOG_OBJECT (sbcparse, "parsing up to %d frames", max_frames);
+
+  for (i = 1; i < max_frames; ++i) {
+    next_len = gst_sbc_parse_header (map.data + (i * frame_len), &rate,
+        &n_blocks, &ch_mode, &alloc_method, &n_subbands, &bitpool);
+
+    if (next_len != frame_len || sbcparse->alloc_method != alloc_method ||
+        sbcparse->ch_mode != ch_mode || sbcparse->rate != rate ||
+        sbcparse->n_blocks != n_blocks || sbcparse->n_subbands != n_subbands ||
+        sbcparse->bitpool != bitpool) {
+      break;
+    }
+  }
+  GST_LOG_OBJECT (sbcparse, "packing %d SBC frames into next output buffer", i);
+
+  /* Note: local n_subbands and n_blocks variables might be tainted if we
+   * bailed out of the loop above because of a header configuration mismatch */
+  gst_base_parse_set_frame_rate (parse, rate,
+      sbcparse->n_subbands * sbcparse->n_blocks * i, 0, 0);
+
   gst_buffer_unmap (frame->buffer, &map);
-  return gst_base_parse_finish_frame (parse, frame, frame_len);
+  return gst_base_parse_finish_frame (parse, frame, i * frame_len);
 
 resync:
   {
@@ -275,6 +302,19 @@ need_more_data:
   }
 }
 
+static void
+remove_fields (GstCaps * caps)
+{
+  guint i, n;
+
+  n = gst_caps_get_size (caps);
+  for (i = 0; i < n; i++) {
+    GstStructure *s = gst_caps_get_structure (caps, i);
+
+    gst_structure_remove_field (s, "parsed");
+  }
+}
+
 static GstCaps *
 gst_sbc_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter)
 {
@@ -282,29 +322,23 @@ gst_sbc_parse_get_sink_caps (GstBaseParse * parse, GstCaps * filter)
   GstCaps *res;
 
   templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse));
-  peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), filter);
+  if (filter) {
+    GstCaps *fcopy = gst_caps_copy (filter);
+    /* Remove the fields we convert */
+    remove_fields (fcopy);
+    peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), fcopy);
+    gst_caps_unref (fcopy);
+  } else
+    peercaps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), NULL);
 
   if (peercaps) {
-    guint i, n;
-
     /* Remove the parsed field */
     peercaps = gst_caps_make_writable (peercaps);
-    n = gst_caps_get_size (peercaps);
-    for (i = 0; i < n; i++) {
-      GstStructure *s = gst_caps_get_structure (peercaps, i);
-
-      gst_structure_remove_field (s, "parsed");
-    }
+    remove_fields (peercaps);
 
     res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST);
     gst_caps_unref (peercaps);
-    res = gst_caps_make_writable (res);
-
-    /* Append the template caps because we still want to accept
-     * caps without any fields in the case upstream does not
-     * know anything.
-     */
-    gst_caps_append (res, templ);
+    gst_caps_unref (templ);
   } else {
     res = templ;
   }
@@ -387,13 +421,13 @@ gst_sbc_calc_framelen (guint subbands, GstSbcChannelMode ch_mode,
 {
   switch (ch_mode) {
     case GST_SBC_CHANNEL_MODE_MONO:
-      return 4 + (subbands * 1) / 2 + (blocks * 1 * bitpool) / 8;
+      return 4 + (subbands * 1) / 2 + ((blocks * 1 * bitpool) + 7) / 8;
     case GST_SBC_CHANNEL_MODE_DUAL:
-      return 4 + (subbands * 2) / 2 + (blocks * 2 * bitpool) / 8;
+      return 4 + (subbands * 2) / 2 + ((blocks * 2 * bitpool) + 7) / 8;
     case GST_SBC_CHANNEL_MODE_STEREO:
-      return 4 + (subbands * 2) / 2 + (blocks * bitpool) / 8;
+      return 4 + (subbands * 2) / 2 + ((blocks * bitpool) + 7) / 8;
     case GST_SBC_CHANNEL_MODE_JOINT_STEREO:
-      return 4 + (subbands * 2) / 2 + (subbands + blocks * bitpool) / 8;
+      return 4 + (subbands * 2) / 2 + ((subbands + blocks * bitpool) + 7) / 8;
     default:
       break;
   }
@@ -461,3 +495,39 @@ gst_sbc_parse_header (const guint8 * data, guint * rate, guint * n_blocks,
 
   return gst_sbc_calc_framelen (*n_subbands, *ch_mode, *n_blocks, *bitpool);
 }
+
+static GstFlowReturn
+gst_sbc_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
+{
+  GstSbcParse *sbcparse = GST_SBC_PARSE (parse);
+
+  if (!sbcparse->sent_codec_tag) {
+    GstTagList *taglist;
+    GstCaps *caps;
+
+    /* codec tag */
+    caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
+    if (G_UNLIKELY (caps == NULL)) {
+      if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
+        GST_INFO_OBJECT (parse, "Src pad is flushing");
+        return GST_FLOW_FLUSHING;
+      } else {
+        GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
+        return GST_FLOW_NOT_NEGOTIATED;
+      }
+    }
+
+    taglist = gst_tag_list_new_empty ();
+    gst_pb_utils_add_codec_description_to_tag_list (taglist,
+        GST_TAG_AUDIO_CODEC, caps);
+    gst_caps_unref (caps);
+
+    gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
+    gst_tag_list_unref (taglist);
+
+    /* also signals the end of first-frame processing */
+    sbcparse->sent_codec_tag = TRUE;
+  }
+
+  return GST_FLOW_OK;
+}