#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
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);
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",
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
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);
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:
{
}
}
+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)
{
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;
}
{
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;
}
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;
+}