-/*
- *
+/* GStreamer SBC audio decoder
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
#include <string.h>
-#include "gstpragma.h"
#include "gstsbcutil.h"
#include "gstsbcdec.h"
+#include <gst/audio/audio.h>
+/* FIXME: where does this come from? how is it derived? */
#define BUF_SIZE 8192
GST_DEBUG_CATEGORY_STATIC (sbc_dec_debug);
static void gst_sbc_dec_finalize (GObject * obj);
-GST_BOILERPLATE (GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT);
-
-static const GstElementDetails sbc_dec_details =
-GST_ELEMENT_DETAILS ("Bluetooth SBC decoder",
- "Codec/Decoder/Audio",
- "Decode a SBC audio stream",
- "Marcel Holtmann <marcel@holtmann.org>");
+/* FIXME: port to GstAudioDecoder base class */
+#define parent_class gst_sbc_dec_parent_class
+G_DEFINE_TYPE (GstSbcDec, gst_sbc_dec, GST_TYPE_ELEMENT);
static GstStaticPadTemplate sbc_dec_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
static GstStaticPadTemplate sbc_dec_src_factory =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-int, "
+ GST_STATIC_CAPS ("audio/x-raw, format=" GST_AUDIO_NE (S16) ", "
"rate = (int) { 16000, 32000, 44100, 48000 }, "
- "channels = (int) [ 1, 2 ], "
- "endianness = (int) BYTE_ORDER, "
- "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16"));
+ "channels = (int) [ 1, 2 ], layout=interleaved"));
static GstFlowReturn
gst_sbc_dec_flush (GstSbcDec * dec, GstBuffer * outbuf,
GstClockTime outtime, duration;
/* we will reuse the same caps object */
- if (dec->outcaps == NULL) {
+ if (dec->send_caps) {
GstCaps *caps;
- GstPadTemplate *template;
-
- caps = gst_caps_new_simple ("audio/x-raw-int",
- "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL);
- template = gst_static_pad_template_get (&sbc_dec_src_factory);
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
+ "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels,
+ "layout", G_TYPE_STRING, "interleaved", NULL);
- dec->outcaps = gst_caps_intersect (caps,
- gst_pad_template_get_caps (template));
+ gst_pad_push_event (dec->srcpad, gst_event_new_caps (caps));
gst_caps_unref (caps);
- gst_object_unref (template);
}
- gst_buffer_set_caps (outbuf, dec->outcaps);
-
/* calculate duration */
outtime = GST_BUFFER_TIMESTAMP (outbuf);
if (dec->next_timestamp != (guint64) - 1 && outtime != (guint64) - 1) {
duration = GST_CLOCK_TIME_NONE;
}
GST_BUFFER_DURATION (outbuf) = duration;
- GST_BUFFER_SIZE (outbuf) = outoffset;
+ gst_buffer_resize (outbuf, 0, outoffset);
return gst_pad_push (dec->srcpad, outbuf);
}
static GstFlowReturn
-sbc_dec_chain (GstPad * pad, GstBuffer * buffer)
+sbc_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
- GstSbcDec *dec = GST_SBC_DEC (gst_pad_get_parent (pad));
+ GstSbcDec *dec = GST_SBC_DEC (parent);
GstFlowReturn res = GST_FLOW_OK;
const guint8 *indata;
guint insize;
GstClockTime timestamp;
gboolean discont;
+ GstMapInfo out_map;
GstBuffer *outbuf;
- guint8 *outdata;
guint inoffset, outoffset;
gint rate, channels;
dec->next_timestamp = timestamp;
insize = gst_adapter_available (dec->adapter);
- indata = gst_adapter_peek (dec->adapter, insize);
-
+ indata = gst_adapter_map (dec->adapter, insize);
inoffset = 0;
outbuf = NULL;
size_t outconsumed;
if (outbuf == NULL) {
- res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad,
- GST_BUFFER_OFFSET_NONE, BUF_SIZE, NULL, &outbuf);
-
- if (res != GST_FLOW_OK)
- goto done;
+ outbuf = gst_buffer_new_and_alloc (BUF_SIZE);
if (discont) {
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
}
GST_BUFFER_TIMESTAMP (outbuf) = dec->next_timestamp;
- outdata = GST_BUFFER_DATA (outbuf);
- outsize = GST_BUFFER_SIZE (outbuf);
+
+ gst_buffer_map (outbuf, &out_map, GST_MAP_WRITE);
+ outsize = out_map.size;
outoffset = 0;
}
insize, outoffset, outsize);
inconsumed = sbc_decode (&dec->sbc, indata + inoffset, insize,
- outdata + outoffset, outsize, &outconsumed);
+ out_map.data + outoffset, outsize, &outconsumed);
GST_INFO_OBJECT (dec, "consumed %d, produced %d", inconsumed, outconsumed);
/* check for space, push outbuf buffer */
outlen = sbc_get_codesize (&dec->sbc);
if (outsize < outlen) {
+ gst_buffer_unmap (outbuf, &out_map);
+
res = gst_sbc_dec_flush (dec, outbuf, outoffset, channels, rate);
- if (res != GST_FLOW_OK)
- goto done;
outbuf = NULL;
- }
+ if (res != GST_FLOW_OK)
+ goto done;
+ }
}
- if (outbuf)
+ if (outbuf) {
+ gst_buffer_unmap (outbuf, &out_map);
+
res = gst_sbc_dec_flush (dec, outbuf, outoffset, channels, rate);
+ }
- gst_adapter_flush (dec->adapter, inoffset);
done:
- gst_object_unref (dec);
+
+ gst_adapter_unmap (dec->adapter);
+ gst_adapter_flush (dec->adapter, inoffset);
return res;
}
case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_DEBUG ("Setup subband codec");
sbc_init (&dec->sbc, 0);
- dec->outcaps = NULL;
+ dec->send_caps = TRUE;
dec->next_sample = -1;
break;
default:
break;
}
- result = parent_class->change_state (element, transition);
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG ("Finish subband codec");
gst_adapter_clear (dec->adapter);
sbc_finish (&dec->sbc);
- if (dec->outcaps) {
- gst_caps_unref (dec->outcaps);
- dec->outcaps = NULL;
- }
+ dec->send_caps = TRUE;
break;
default:
}
static void
-gst_sbc_dec_base_init (gpointer g_class)
+gst_sbc_dec_class_init (GstSbcDecClass * klass)
{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ object_class->finalize = gst_sbc_dec_finalize;
+
+ element_class->change_state = GST_DEBUG_FUNCPTR (gst_sbc_dec_change_state);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sbc_dec_sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sbc_dec_src_factory));
- gst_element_class_set_details (element_class, &sbc_dec_details);
-}
-
-static void
-gst_sbc_dec_class_init (GstSbcDecClass * klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- parent_class = g_type_class_peek_parent (klass);
-
- object_class->finalize = GST_DEBUG_FUNCPTR (gst_sbc_dec_finalize);
-
- element_class->change_state = GST_DEBUG_FUNCPTR (gst_sbc_dec_change_state);
+ gst_element_class_set_static_metadata (element_class,
+ "Bluetooth SBC audio decoder", "Codec/Decoder/Audio",
+ "Decode an SBC audio stream", "Marcel Holtmann <marcel@holtmann.org>");
GST_DEBUG_CATEGORY_INIT (sbc_dec_debug, "sbcdec", 0, "SBC decoding element");
}
static void
-gst_sbc_dec_init (GstSbcDec * self, GstSbcDecClass * klass)
+gst_sbc_dec_init (GstSbcDec * self)
{
self->sinkpad =
gst_pad_new_from_static_template (&sbc_dec_sink_factory, "sink");
gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
self->adapter = gst_adapter_new ();
- self->outcaps = NULL;
+ self->send_caps = TRUE;
}
static void
GstSbcDec *self = GST_SBC_DEC (obj);
g_object_unref (self->adapter);
+ self->adapter = NULL;
G_OBJECT_CLASS (parent_class)->finalize (obj);
-
-}
-
-gboolean
-gst_sbc_dec_plugin_init (GstPlugin * plugin)
-{
- return gst_element_register (plugin, "sbcdec", GST_RANK_PRIMARY,
- GST_TYPE_SBC_DEC);
}
-/*
- *
+/* GStreamer SBC audio encoder
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
#include <string.h>
-#include "gstpragma.h"
#include "gstsbcutil.h"
#include "gstsbcenc.h"
-#define SBC_ENC_DEFAULT_MODE SBC_MODE_AUTO
+#include <gst/audio/audio.h>
+
+#define SBC_ENC_DEFAULT_CHANNEL_MODE SBC_MODE_AUTO
#define SBC_ENC_DEFAULT_BLOCKS 0
#define SBC_ENC_DEFAULT_SUB_BANDS 0
-#define SBC_ENC_DEFAULT_ALLOCATION SBC_AM_AUTO
+#define SBC_ENC_DEFAULT_ALLOCATION_METHOD SBC_AM_AUTO
#define SBC_ENC_DEFAULT_RATE 0
#define SBC_ENC_DEFAULT_CHANNELS 0
GST_DEBUG_CATEGORY_STATIC (sbc_enc_debug);
#define GST_CAT_DEFAULT sbc_enc_debug
-#define GST_TYPE_SBC_MODE (gst_sbc_mode_get_type())
+#define GST_TYPE_SBC_CHANNEL_MODE (gst_sbc_channel_mode_get_type())
static GType
-gst_sbc_mode_get_type (void)
+gst_sbc_channel_mode_get_type (void)
{
- static GType sbc_mode_type = 0;
- static GEnumValue sbc_modes[] = {
+ static GType sbc_channel_mode_type = 0;
+ static GEnumValue sbc_channel_modes[] = {
{SBC_MODE_MONO, "Mono", "mono"},
- {SBC_MODE_DUAL_CHANNEL, "Dual Channel", "dual"},
+ {SBC_MODE_DUAL_CHANNEL, "Dual", "dual"},
{SBC_MODE_STEREO, "Stereo", "stereo"},
{SBC_MODE_JOINT_STEREO, "Joint Stereo", "joint"},
{SBC_MODE_AUTO, "Auto", "auto"},
{-1, NULL, NULL}
};
- if (!sbc_mode_type)
- sbc_mode_type = g_enum_register_static ("GstSbcMode", sbc_modes);
+ if (!sbc_channel_mode_type) {
+ sbc_channel_mode_type =
+ g_enum_register_static ("GstSbcChannelMode", sbc_channel_modes);
+ }
- return sbc_mode_type;
+ return sbc_channel_mode_type;
}
-#define GST_TYPE_SBC_ALLOCATION (gst_sbc_allocation_get_type())
+#define GST_TYPE_SBC_ALLOCATION_METHOD (gst_sbc_allocation_method_get_type())
static GType
-gst_sbc_allocation_get_type (void)
+gst_sbc_allocation_method_get_type (void)
{
- static GType sbc_allocation_type = 0;
- static GEnumValue sbc_allocations[] = {
+ static GType sbc_allocation_method_type = 0;
+ static GEnumValue sbc_allocation_methods[] = {
{SBC_AM_LOUDNESS, "Loudness", "loudness"},
{SBC_AM_SNR, "SNR", "snr"},
{SBC_AM_AUTO, "Auto", "auto"},
{-1, NULL, NULL}
};
- if (!sbc_allocation_type)
- sbc_allocation_type =
- g_enum_register_static ("GstSbcAllocation", sbc_allocations);
+ if (!sbc_allocation_method_type)
+ sbc_allocation_method_type =
+ g_enum_register_static ("GstSbcAllocationMethod",
+ sbc_allocation_methods);
- return sbc_allocation_type;
+ return sbc_allocation_method_type;
}
#define GST_TYPE_SBC_BLOCKS (gst_sbc_blocks_get_type())
PROP_BITPOOL
};
-GST_BOILERPLATE (GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT);
-
-static const GstElementDetails sbc_enc_details =
-GST_ELEMENT_DETAILS ("Bluetooth SBC encoder",
- "Codec/Encoder/Audio",
- "Encode a SBC audio stream",
- "Marcel Holtmann <marcel@holtmann.org>");
+/* FIXME: rewrite based on GstAudioEncoder base class */
+#define parent_class gst_sbc_enc_parent_class
+G_DEFINE_TYPE (GstSbcEnc, gst_sbc_enc, GST_TYPE_ELEMENT);
static GstStaticPadTemplate sbc_enc_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-int, "
+ GST_STATIC_CAPS ("audio/x-raw, format=" GST_AUDIO_NE (S16) ", "
"rate = (int) { 16000, 32000, 44100, 48000 }, "
- "channels = (int) [ 1, 2 ], "
- "endianness = (int) BYTE_ORDER, "
- "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16"));
+ "channels = (int) [ 1, 2 ]"));
static GstStaticPadTemplate sbc_enc_src_factory =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-sbc, "
"rate = (int) { 16000, 32000, 44100, 48000 }, "
"channels = (int) [ 1, 2 ], "
- "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+ "channel-mode = (string) { mono, dual, stereo, joint }, "
"blocks = (int) { 4, 8, 12, 16 }, "
"subbands = (int) { 4, 8 }, "
- "allocation = (string) { \"snr\", \"loudness\" }, "
+ "allocation-method = (string) { snr, loudness }, "
"bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR
", " SBC_ENC_BITPOOL_MAX_STR " ]"));
-gboolean gst_sbc_enc_fill_sbc_params (GstSbcEnc * enc, GstCaps * caps);
+static gboolean gst_sbc_enc_fill_sbc_params (GstSbcEnc * enc, GstCaps * caps);
+
+static gboolean gst_sbc_enc_sink_event (GstPad * pad, GstObject * parent,
+ GstEvent * event);
+
+/* FIXME does this make sense? */
static GstCaps *
sbc_enc_generate_srcpad_caps (GstSbcEnc * enc)
{
src_caps = gst_caps_copy (gst_pad_get_pad_template_caps (enc->srcpad));
structure = gst_caps_get_structure (src_caps, 0);
+ /* FIXME: use gst_structure_set() */
value = g_new0 (GValue, 1);
if (enc->rate != 0)
gst_sbc_util_set_structure_int_param (structure, "bitpool",
enc->bitpool, value);
- if (enc->mode != SBC_ENC_DEFAULT_MODE) {
- enum_class = g_type_class_ref (GST_TYPE_SBC_MODE);
+ if (enc->mode != SBC_ENC_DEFAULT_CHANNEL_MODE) {
+ enum_class = g_type_class_ref (GST_TYPE_SBC_CHANNEL_MODE);
enum_value = g_enum_get_value (enum_class, enc->mode);
- gst_sbc_util_set_structure_string_param (structure, "mode",
+ gst_sbc_util_set_structure_string_param (structure, "channel-mode",
enum_value->value_nick, value);
g_type_class_unref (enum_class);
}
if (enc->allocation != SBC_AM_AUTO) {
- enum_class = g_type_class_ref (GST_TYPE_SBC_ALLOCATION);
+ enum_class = g_type_class_ref (GST_TYPE_SBC_ALLOCATION_METHOD);
enum_value = g_enum_get_value (enum_class, enc->allocation);
- gst_sbc_util_set_structure_string_param (structure, "allocation",
+ gst_sbc_util_set_structure_string_param (structure, "allocation-method",
enum_value->value_nick, value);
g_type_class_unref (enum_class);
}
}
static gboolean
-sbc_enc_src_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstSbcEnc *enc = GST_SBC_ENC (GST_PAD_PARENT (pad));
-
- GST_LOG_OBJECT (enc, "setting srcpad caps");
-
- return gst_sbc_enc_fill_sbc_params (enc, caps);
-}
-
-static GstCaps *
-sbc_enc_src_caps_fixate (GstSbcEnc * enc, GstCaps * caps)
+sbc_enc_negotiate_output_caps (GstSbcEnc * enc, GstCaps * in_caps)
{
- gchar *error_message = NULL;
- GstCaps *result;
-
- result = gst_sbc_util_caps_fixate (caps, &error_message);
-
- if (!result) {
- GST_WARNING_OBJECT (enc, "Invalid input caps caused parsing "
- "error: %s", error_message);
- g_free (error_message);
- return NULL;
- }
-
- return result;
-}
+ GstStructure *s;
+ GstCaps *caps, *filter_caps;
+ GstCaps *output_caps;
-static GstCaps *
-sbc_enc_get_fixed_srcpad_caps (GstSbcEnc * enc)
-{
- GstCaps *caps;
- gboolean res = TRUE;
- GstCaps *result_caps = NULL;
+ GST_INFO_OBJECT (enc, "input caps %" GST_PTR_FORMAT, in_caps);
caps = gst_pad_get_allowed_caps (enc->srcpad);
if (caps == NULL)
caps = sbc_enc_src_getcaps (enc->srcpad);
if (caps == GST_CAPS_NONE || gst_caps_is_empty (caps)) {
- res = FALSE;
- goto done;
+ gst_caps_unref (caps);
+ return FALSE;
}
- result_caps = sbc_enc_src_caps_fixate (enc, caps);
-
-done:
+ /* fixate output caps */
+ filter_caps = gst_caps_new_simple ("audio/x-sbc", "rate", G_TYPE_INT,
+ enc->rate, "channels", G_TYPE_INT, enc->channels, NULL);
+ output_caps = gst_caps_intersect (caps, filter_caps);
+ gst_caps_unref (filter_caps);
+
+ if (output_caps == NULL || gst_caps_is_empty (output_caps)) {
+ GST_DEBUG_OBJECT (enc, "Couldn't negotiate output caps with input rate %d "
+ "and input channels %d and allowed output caps %" GST_PTR_FORMAT,
+ enc->rate, enc->channels, caps);
+ if (output_caps)
+ gst_caps_unref (output_caps);
+ gst_caps_unref (caps);
+ return FALSE;
+ }
gst_caps_unref (caps);
- if (!res)
- return NULL;
+ GST_DEBUG_OBJECT (enc, "fixating caps %" GST_PTR_FORMAT, output_caps);
+ output_caps = gst_caps_truncate (output_caps);
+ s = gst_caps_get_structure (output_caps, 0);
+ if (enc->channels == 1) {
+ if (!gst_structure_fixate_field_string (s, "channel-mode", "mono")) {
+ GST_DEBUG_OBJECT (enc, "Failed to fixate channel-mode to mono");
+ gst_caps_unref (output_caps);
+ return FALSE;
+ }
+ } else {
+ if (gst_structure_fixate_field_string (s, "channel-mode", "joint")
+ || gst_structure_fixate_field_string (s, "channel-mode", "stereo")) {
+ gst_structure_fixate_field_string (s, "channel-mode", "dual");
+ }
+ }
+
+ gst_structure_fixate_field_nearest_int (s, "bitpool", 64);
+ gst_structure_fixate_field_nearest_int (s, "blocks", 16);
+ gst_structure_fixate_field_nearest_int (s, "subbands", 8);
+ gst_structure_fixate_field_string (s, "allocation-method", "loudness");
+ s = NULL;
+
+ /* in case there's anything else left to fixate */
+ output_caps = gst_caps_fixate (output_caps);
+
+ GST_INFO_OBJECT (enc, "output caps %" GST_PTR_FORMAT, output_caps);
+ if (!gst_sbc_enc_fill_sbc_params (enc, output_caps)) {
+ GST_WARNING_OBJECT (enc, "failed to configure encoder from output "
+ "caps %" GST_PTR_FORMAT, output_caps);
+ return FALSE;
+ }
+
+ gst_pad_send_event (enc->sinkpad, gst_event_new_caps (output_caps));
+ gst_caps_unref (output_caps);
- return result_caps;
+ return TRUE;
}
static gboolean
-sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
+sbc_enc_sink_setcaps (GstSbcEnc * enc, GstCaps * caps)
{
- GstSbcEnc *enc;
GstStructure *structure;
- GstCaps *src_caps;
gint rate, channels;
- gboolean res;
- enc = GST_SBC_ENC (GST_PAD_PARENT (pad));
structure = gst_caps_get_structure (caps, 0);
if (!gst_structure_get_int (structure, "rate", &rate))
enc->rate = rate;
enc->channels = channels;
- src_caps = sbc_enc_get_fixed_srcpad_caps (enc);
- if (!src_caps)
- return FALSE;
- res = gst_pad_set_caps (enc->srcpad, src_caps);
- gst_caps_unref (src_caps);
+ return sbc_enc_negotiate_output_caps (enc, caps);
+}
- return res;
+static gboolean
+gst_sbc_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GstSbcEnc *enc = GST_SBC_ENC (parent);
+ gboolean ret;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:{
+ GstCaps *caps;
+
+ gst_event_parse_caps (event, &caps);
+ ret = sbc_enc_sink_setcaps (enc, caps);
+ break;
+ }
+ default:
+ ret = gst_pad_event_default (pad, parent, event);
+ break;
+ }
+
+ return ret;
}
-gboolean
+/* configure encoder based on output caps */
+static gboolean
gst_sbc_enc_fill_sbc_params (GstSbcEnc * enc, GstCaps * caps)
{
if (!gst_caps_is_fixed (caps)) {
- GST_DEBUG_OBJECT (enc, "didn't receive fixed caps, " "returning false");
+ GST_DEBUG_OBJECT (enc, "output caps %" GST_PTR_FORMAT " not fixed!", caps);
return FALSE;
}
&& gst_sbc_parse_subbands_from_sbc (enc->sbc.subbands) != enc->subbands)
goto fail;
- if (enc->mode != SBC_ENC_DEFAULT_MODE && enc->sbc.mode != enc->mode)
+ if (enc->mode != SBC_ENC_DEFAULT_CHANNEL_MODE && enc->sbc.mode != enc->mode)
goto fail;
if (enc->allocation != SBC_AM_AUTO && enc->sbc.allocation != enc->allocation)
}
static GstFlowReturn
-sbc_enc_chain (GstPad * pad, GstBuffer * buffer)
+sbc_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
- GstSbcEnc *enc = GST_SBC_ENC (gst_pad_get_parent (pad));
+ GstSbcEnc *enc = GST_SBC_ENC (parent);
GstAdapter *adapter = enc->adapter;
GstFlowReturn res = GST_FLOW_OK;
+ if (enc->codesize == 0)
+ goto not_negotiated;
+
gst_adapter_push (adapter, buffer);
- while (gst_adapter_available (adapter) >= enc->codesize && res == GST_FLOW_OK) {
- GstBuffer *output;
- GstCaps *caps;
+ while (gst_adapter_available (adapter) >= enc->codesize) {
+ GstMapInfo out_map;
+ GstBuffer *outbuf;
const guint8 *data;
gint consumed;
- caps = GST_PAD_CAPS (enc->srcpad);
- res = gst_pad_alloc_buffer_and_set_caps (enc->srcpad,
- GST_BUFFER_OFFSET_NONE, enc->frame_length, caps, &output);
- if (res != GST_FLOW_OK)
- goto done;
+ outbuf = gst_buffer_new_and_alloc (enc->frame_length);
+ gst_buffer_map (outbuf, &out_map, GST_MAP_WRITE);
- data = gst_adapter_peek (adapter, enc->codesize);
+ data = gst_adapter_map (adapter, enc->codesize);
consumed = sbc_encode (&enc->sbc, (gpointer) data,
- enc->codesize,
- GST_BUFFER_DATA (output), GST_BUFFER_SIZE (output), NULL);
+ enc->codesize, out_map.data, out_map.size, NULL);
+
+ gst_adapter_unmap (adapter);
+ gst_buffer_unmap (outbuf, &out_map);
+
if (consumed <= 0) {
- GST_DEBUG_OBJECT (enc, "comsumed < 0, codesize: %d", enc->codesize);
+ GST_DEBUG_OBJECT (enc, "consumed < 0, codesize: %d", enc->codesize);
+ gst_buffer_unref (outbuf);
break;
}
gst_adapter_flush (adapter, consumed);
- GST_BUFFER_TIMESTAMP (output) = GST_BUFFER_TIMESTAMP (buffer);
+ /* FIXME: this is not right if we don't have a 1:1 mapping of
+ * input and output data */
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
/* we have only 1 frame */
- GST_BUFFER_DURATION (output) = enc->frame_duration;
+ GST_BUFFER_DURATION (outbuf) = enc->frame_duration;
- res = gst_pad_push (enc->srcpad, output);
+ res = gst_pad_push (enc->srcpad, outbuf);
if (res != GST_FLOW_OK)
- goto done;
+ break;
}
-done:
- gst_object_unref (enc);
-
return res;
+
+not_negotiated:
+ {
+ GST_ERROR_OBJECT (enc, "output caps not negotiated yet");
+ gst_buffer_unref (buffer);
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
}
static GstStateChangeReturn
break;
}
- return parent_class->change_state (element, transition);
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
}
static void
-gst_sbc_enc_dispose (GObject * object)
+gst_sbc_enc_finalize (GObject * object)
{
GstSbcEnc *enc = GST_SBC_ENC (object);
- if (enc->adapter != NULL)
- g_object_unref (G_OBJECT (enc->adapter));
-
- enc->adapter = NULL;
-}
-
-static void
-gst_sbc_enc_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&sbc_enc_sink_factory));
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&sbc_enc_src_factory));
+ if (enc->adapter != NULL) {
+ g_object_unref (enc->adapter);
+ enc->adapter = NULL;
+ }
- gst_element_class_set_details (element_class, &sbc_enc_details);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
parent_class = g_type_class_peek_parent (klass);
- object_class->set_property = GST_DEBUG_FUNCPTR (gst_sbc_enc_set_property);
- object_class->get_property = GST_DEBUG_FUNCPTR (gst_sbc_enc_get_property);
- object_class->dispose = GST_DEBUG_FUNCPTR (gst_sbc_enc_dispose);
+ object_class->set_property = gst_sbc_enc_set_property;
+ object_class->get_property = gst_sbc_enc_get_property;
+ object_class->finalize = gst_sbc_enc_finalize;
element_class->change_state = GST_DEBUG_FUNCPTR (sbc_enc_change_state);
g_object_class_install_property (object_class, PROP_MODE,
- g_param_spec_enum ("mode", "Mode",
- "Encoding mode", GST_TYPE_SBC_MODE,
- SBC_ENC_DEFAULT_MODE, G_PARAM_READWRITE));
+ g_param_spec_enum ("channel-mode", "Channel Mode",
+ "Channel mode", GST_TYPE_SBC_CHANNEL_MODE,
+ SBC_ENC_DEFAULT_CHANNEL_MODE, G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_ALLOCATION,
- g_param_spec_enum ("allocation", "Allocation",
- "Allocation method", GST_TYPE_SBC_ALLOCATION,
- SBC_ENC_DEFAULT_ALLOCATION, G_PARAM_READWRITE));
+ g_param_spec_enum ("allocation-method", "Allocation Method",
+ "Allocation method", GST_TYPE_SBC_ALLOCATION_METHOD,
+ SBC_ENC_DEFAULT_ALLOCATION_METHOD, G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_BLOCKS,
g_param_spec_enum ("blocks", "Blocks",
SBC_ENC_BITPOOL_AUTO, SBC_ENC_BITPOOL_MAX,
SBC_ENC_BITPOOL_AUTO, G_PARAM_READWRITE));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sbc_enc_sink_factory));
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sbc_enc_src_factory));
+
+ gst_element_class_set_static_metadata (element_class,
+ "Bluetooth SBC audio encoder", "Codec/Encoder/Audio",
+ "Encode an SBC audio stream", "Marcel Holtmann <marcel@holtmann.org>");
+
GST_DEBUG_CATEGORY_INIT (sbc_enc_debug, "sbcenc", 0, "SBC encoding element");
}
static void
-gst_sbc_enc_init (GstSbcEnc * self, GstSbcEncClass * klass)
+gst_sbc_enc_init (GstSbcEnc * self)
{
self->sinkpad =
gst_pad_new_from_static_template (&sbc_enc_sink_factory, "sink");
- gst_pad_set_setcaps_function (self->sinkpad,
- GST_DEBUG_FUNCPTR (sbc_enc_sink_setcaps));
+ gst_pad_set_event_function (self->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_sbc_enc_sink_event));
gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
self->srcpad = gst_pad_new_from_static_template (&sbc_enc_src_factory, "src");
+#if 0
gst_pad_set_getcaps_function (self->srcpad,
GST_DEBUG_FUNCPTR (sbc_enc_src_getcaps));
- gst_pad_set_setcaps_function (self->srcpad,
- GST_DEBUG_FUNCPTR (sbc_enc_src_setcaps));
+#endif
gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
gst_pad_set_chain_function (self->sinkpad, GST_DEBUG_FUNCPTR (sbc_enc_chain));
self->subbands = SBC_ENC_DEFAULT_SUB_BANDS;
self->blocks = SBC_ENC_DEFAULT_BLOCKS;
- self->mode = SBC_ENC_DEFAULT_MODE;
- self->allocation = SBC_ENC_DEFAULT_ALLOCATION;
+ self->mode = SBC_ENC_DEFAULT_CHANNEL_MODE;
+ self->allocation = SBC_ENC_DEFAULT_ALLOCATION_METHOD;
self->rate = SBC_ENC_DEFAULT_RATE;
self->channels = SBC_ENC_DEFAULT_CHANNELS;
self->bitpool = SBC_ENC_BITPOOL_AUTO;
-/*
- *
+/* SBC audio utilities
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
gst_sbc_parse_rate_from_sbc (sbc->frequency),
"channels", G_TYPE_INT,
gst_sbc_get_channel_number (sbc->mode),
- "mode", G_TYPE_STRING, mode_str,
+ "channel-mode", G_TYPE_STRING, mode_str,
"subbands", G_TYPE_INT,
gst_sbc_parse_subbands_from_sbc (sbc->subbands),
"blocks", G_TYPE_INT,
gst_sbc_parse_blocks_from_sbc (sbc->blocks),
- "allocation", G_TYPE_STRING, allocation_str,
+ "allocation-method", G_TYPE_STRING, allocation_str,
"bitpool", G_TYPE_INT, sbc->bitpool, NULL);
return caps;
}
-/*
- * Given a GstCaps, this will return a fixed GstCaps on successful conversion.
- * If an error occurs, it will return NULL and error_message will contain the
- * error message.
- *
- * error_message must be passed NULL, if an error occurs, the caller has the
- * ownership of the error_message, it must be freed after use.
- */
-GstCaps *
-gst_sbc_util_caps_fixate (GstCaps * caps, gchar ** error_message)
-{
- GstCaps *result;
- GstStructure *structure;
- const GValue *value;
- gboolean error = FALSE;
- gint temp, rate, channels, blocks, subbands, bitpool;
- const gchar *allocation = NULL;
- const gchar *mode = NULL;
-
- g_assert (*error_message == NULL);
-
- structure = gst_caps_get_structure (caps, 0);
-
- if (!gst_structure_has_field (structure, "rate")) {
- error = TRUE;
- *error_message = g_strdup ("no rate");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "rate");
- if (GST_VALUE_HOLDS_LIST (value))
- temp = gst_sbc_select_rate_from_list (value);
- else
- temp = g_value_get_int (value);
- rate = temp;
- }
-
- if (!gst_structure_has_field (structure, "channels")) {
- error = TRUE;
- *error_message = g_strdup ("no channels");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "channels");
- if (GST_VALUE_HOLDS_INT_RANGE (value))
- temp = gst_sbc_select_channels_from_range (value);
- else
- temp = g_value_get_int (value);
- channels = temp;
- }
-
- if (!gst_structure_has_field (structure, "blocks")) {
- error = TRUE;
- *error_message = g_strdup ("no blocks.");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "blocks");
- if (GST_VALUE_HOLDS_LIST (value))
- temp = gst_sbc_select_blocks_from_list (value);
- else
- temp = g_value_get_int (value);
- blocks = temp;
- }
-
- if (!gst_structure_has_field (structure, "subbands")) {
- error = TRUE;
- *error_message = g_strdup ("no subbands");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "subbands");
- if (GST_VALUE_HOLDS_LIST (value))
- temp = gst_sbc_select_subbands_from_list (value);
- else
- temp = g_value_get_int (value);
- subbands = temp;
- }
-
- if (!gst_structure_has_field (structure, "bitpool")) {
- error = TRUE;
- *error_message = g_strdup ("no bitpool");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "bitpool");
- if (GST_VALUE_HOLDS_INT_RANGE (value))
- temp = gst_sbc_select_bitpool_from_range (value);
- else
- temp = g_value_get_int (value);
- bitpool = temp;
- }
-
- if (!gst_structure_has_field (structure, "allocation")) {
- error = TRUE;
- *error_message = g_strdup ("no allocation");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "allocation");
- if (GST_VALUE_HOLDS_LIST (value))
- allocation = gst_sbc_get_allocation_from_list (value);
- else
- allocation = g_value_get_string (value);
- }
-
- if (!gst_structure_has_field (structure, "mode")) {
- error = TRUE;
- *error_message = g_strdup ("no mode");
- goto error;
- } else {
- value = gst_structure_get_value (structure, "mode");
- if (GST_VALUE_HOLDS_LIST (value)) {
- mode = gst_sbc_get_mode_from_list (value, channels);
- } else
- mode = g_value_get_string (value);
- }
-
- /* perform validation
- * if channels is 1, we must have channel mode = mono
- * if channels is 2, we can't have channel mode = mono */
- if ((channels == 1 && (strcmp (mode, "mono") != 0)) ||
- (channels == 2 && (strcmp (mode, "mono") == 0))) {
- *error_message = g_strdup_printf ("Invalid combination of "
- "channels (%d) and channel mode (%s)", channels, mode);
- error = TRUE;
- }
-
-error:
- if (error)
- return NULL;
-
- result = gst_caps_new_simple ("audio/x-sbc",
- "rate", G_TYPE_INT, rate,
- "channels", G_TYPE_INT, channels,
- "mode", G_TYPE_STRING, mode,
- "blocks", G_TYPE_INT, blocks,
- "subbands", G_TYPE_INT, subbands,
- "allocation", G_TYPE_STRING, allocation,
- "bitpool", G_TYPE_INT, bitpool, NULL);
-
- return result;
-}
-
+/* FIXME: this function is not needed, just use gst_structure_set() */
/**
* Sets the int field_value to the param "field" on the structure.
* value is used to do the operation, it must be a uninitialized (zero-filled)
if (!gst_structure_get_int (structure, "bitpool", &bitpool))
return FALSE;
- if (!(mode = gst_structure_get_string (structure, "mode")))
+ if (!(mode = gst_structure_get_string (structure, "channel-mode")))
return FALSE;
- if (!(allocation = gst_structure_get_string (structure, "allocation")))
+ if (!(allocation = gst_structure_get_string (structure, "allocation-method")))
return FALSE;
if (channels == 1 && strcmp (mode, "mono") != 0)