From fecddde2c2de612bbeca5d3ee7d573fd929b949e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Wed, 16 Jan 2013 11:36:25 +0000 Subject: [PATCH] sbc: import sbc decoder/encoder from bluez and port to 1.0 https://bugzilla.gnome.org/show_bug.cgi?id=690582 --- configure.ac | 9 ++ ext/Makefile.am | 8 + ext/sbc/Makefile.am | 24 +++ ext/sbc/gstsbcdec.c | 129 +++++++--------- ext/sbc/gstsbcdec.h | 38 +++-- ext/sbc/gstsbcenc.c | 340 ++++++++++++++++++++++++------------------- ext/sbc/gstsbcenc.h | 2 +- ext/sbc/gstsbcutil.c | 150 +------------------ ext/sbc/gstsbcutil.h | 7 +- ext/sbc/sbc-plugin.c | 41 ++++++ 10 files changed, 351 insertions(+), 397 deletions(-) create mode 100644 ext/sbc/Makefile.am create mode 100644 ext/sbc/sbc-plugin.c diff --git a/configure.ac b/configure.ac index b4cba5feb5..5534694015 100644 --- a/configure.ac +++ b/configure.ac @@ -2082,6 +2082,14 @@ AG_GST_CHECK_FEATURE(VDPAU, [VDPAU], vdpau, [ fi ]) +dnl *** sbc *** +translit(dnm, m, l) AM_CONDITIONAL(USE_SBC, true) +AG_GST_CHECK_FEATURE(SBC, [SBC bluetooth audio codec], sbc, [ + AG_GST_PKG_CHECK_MODULES(SBC, [sbc >= 1.0 sbc <= 1.0]) + AC_MSG_NOTICE([Disabling sbc plugin for now, not working yet]) + HAVE_SBC=no +]) + dnl *** schroedinger *** translit(dnm, m, l) AM_CONDITIONAL(USE_SCHRO, true) AG_GST_CHECK_FEATURE(SCHRO, [Schroedinger video codec], schro, [ @@ -2456,6 +2464,7 @@ ext/opus/Makefile ext/rsvg/Makefile ext/resindvd/Makefile ext/rtmp/Makefile +ext/sbc/Makefile ext/schroedinger/Makefile ext/sdl/Makefile ext/sndfile/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 5d5592b7ba..7d37399975 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -274,6 +274,12 @@ if USE_FLUIDSYNTH FLUIDSYNTH_DIR=fluidsynth endif +if USE_SBC +SBC_DIR=sbc +else +SBC_DIR= +endif + if USE_SCHRO SCHRO_DIR=schroedinger else @@ -417,6 +423,7 @@ SUBDIRS=\ $(OPENJPEG_DIR) \ $(OPUS_DIR) \ $(RSVG_DIR) \ + $(SBC_DIR) \ $(SCHRO_DIR) \ $(SDL_DIR) \ $(SMOOTHWAVE_DIR) \ @@ -476,6 +483,7 @@ DIST_SUBDIRS = \ opus \ rsvg \ resindvd \ + sbc \ schroedinger \ sdl \ sndfile \ diff --git a/ext/sbc/Makefile.am b/ext/sbc/Makefile.am new file mode 100644 index 0000000000..3d4da05b26 --- /dev/null +++ b/ext/sbc/Makefile.am @@ -0,0 +1,24 @@ + +plugin_LTLIBRARIES = libgstsbc.la + +noinst_HEADERS = \ + gstsbcdec.h \ + gstsbcenc.h \ + gstsbcutil.h + +libgstsbc_la_SOURCES = \ + gstsbcdec.c \ + gstsbcenc.c \ + gstsbcutil.c \ + sbc-plugin.c +libgstsbc_la_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(SBC_CFLAGS) +libgstsbc_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) \ + $(SBC_LIBS) +libgstsbc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstsbc_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/ext/sbc/gstsbcdec.c b/ext/sbc/gstsbcdec.c index 12245f9d7a..1176a2cdf7 100644 --- a/ext/sbc/gstsbcdec.c +++ b/ext/sbc/gstsbcdec.c @@ -1,5 +1,4 @@ -/* - * +/* GStreamer SBC audio decoder * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann @@ -27,10 +26,11 @@ #include -#include "gstpragma.h" #include "gstsbcutil.h" #include "gstsbcdec.h" +#include +/* FIXME: where does this come from? how is it derived? */ #define BUF_SIZE 8192 GST_DEBUG_CATEGORY_STATIC (sbc_dec_debug); @@ -38,13 +38,9 @@ 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 "); +/* 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, @@ -52,11 +48,9 @@ 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, @@ -65,24 +59,19 @@ 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) { @@ -95,23 +84,23 @@ gst_sbc_dec_flush (GstSbcDec * dec, GstBuffer * outbuf, 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; @@ -130,8 +119,7 @@ sbc_dec_chain (GstPad * pad, GstBuffer * buffer) 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; @@ -143,11 +131,7 @@ sbc_dec_chain (GstPad * pad, GstBuffer * buffer) 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); @@ -155,8 +139,9 @@ sbc_dec_chain (GstPad * pad, GstBuffer * buffer) } 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; } @@ -164,7 +149,7 @@ sbc_dec_chain (GstPad * pad, GstBuffer * buffer) 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); @@ -209,21 +194,27 @@ sbc_dec_chain (GstPad * pad, GstBuffer * buffer) /* 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; } @@ -238,24 +229,21 @@ gst_sbc_dec_change_state (GstElement * element, GstStateChange transition) 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: @@ -266,9 +254,14 @@ gst_sbc_dec_change_state (GstElement * element, GstStateChange transition) } 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)); @@ -276,26 +269,15 @@ gst_sbc_dec_base_init (gpointer g_class) 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 "); 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"); @@ -306,7 +288,7 @@ gst_sbc_dec_init (GstSbcDec * self, GstSbcDecClass * klass) gst_element_add_pad (GST_ELEMENT (self), self->srcpad); self->adapter = gst_adapter_new (); - self->outcaps = NULL; + self->send_caps = TRUE; } static void @@ -315,14 +297,7 @@ gst_sbc_dec_finalize (GObject * obj) 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); } diff --git a/ext/sbc/gstsbcdec.h b/ext/sbc/gstsbcdec.h index f5b9416a98..c7c43eea11 100644 --- a/ext/sbc/gstsbcdec.h +++ b/ext/sbc/gstsbcdec.h @@ -1,5 +1,4 @@ -/* - * +/* GStreamer SBC audio decoder * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann @@ -24,46 +23,43 @@ #include #include -#include "sbc.h" +#include G_BEGIN_DECLS #define GST_TYPE_SBC_DEC \ - (gst_sbc_dec_get_type()) + (gst_sbc_dec_get_type()) #define GST_SBC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_DEC,GstSbcDec)) + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_DEC,GstSbcDec)) #define GST_SBC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_DEC,GstSbcDecClass)) + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_DEC,GstSbcDecClass)) #define GST_IS_SBC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_DEC)) + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_DEC)) #define GST_IS_SBC_DEC_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_DEC)) + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_DEC)) typedef struct _GstSbcDec GstSbcDec; typedef struct _GstSbcDecClass GstSbcDecClass; struct _GstSbcDec { - GstElement element; + GstElement element; - GstPad *sinkpad; - GstPad *srcpad; + GstPad *sinkpad; + GstPad *srcpad; - GstAdapter *adapter; + GstAdapter *adapter; - /* caps for outgoing buffers */ - GstCaps *outcaps; + gboolean send_caps; - sbc_t sbc; - guint64 next_sample; - guint64 next_timestamp; + sbc_t sbc; + guint64 next_sample; + guint64 next_timestamp; }; struct _GstSbcDecClass { - GstElementClass parent_class; + GstElementClass parent_class; }; -GType gst_sbc_dec_get_type(void); - -gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin); +GType gst_sbc_dec_get_type (void); G_END_DECLS diff --git a/ext/sbc/gstsbcenc.c b/ext/sbc/gstsbcenc.c index 6f80959d70..812a3724f8 100644 --- a/ext/sbc/gstsbcenc.c +++ b/ext/sbc/gstsbcenc.c @@ -1,5 +1,4 @@ -/* - * +/* GStreamer SBC audio encoder * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann @@ -27,14 +26,15 @@ #include -#include "gstpragma.h" #include "gstsbcutil.h" #include "gstsbcenc.h" -#define SBC_ENC_DEFAULT_MODE SBC_MODE_AUTO +#include + +#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 @@ -47,45 +47,48 @@ 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()) @@ -138,36 +141,35 @@ enum 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 "); +/* 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) { @@ -180,6 +182,7 @@ 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) @@ -201,18 +204,18 @@ sbc_enc_generate_srcpad_caps (GstSbcEnc * enc) 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); } @@ -233,70 +236,84 @@ sbc_enc_src_getcaps (GstPad * pad) } 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)) @@ -307,20 +324,37 @@ sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps) 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; } @@ -343,7 +377,7 @@ gst_sbc_enc_fill_sbc_params (GstSbcEnc * enc, GstCaps * caps) && 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) @@ -367,51 +401,61 @@ fail: } 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 @@ -434,32 +478,20 @@ sbc_enc_change_state (GstElement * element, GstStateChange transition) 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 @@ -529,21 +561,21 @@ gst_sbc_enc_class_init (GstSbcEncClass * klass) 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", @@ -561,31 +593,41 @@ gst_sbc_enc_class_init (GstSbcEncClass * klass) 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 "); + 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; diff --git a/ext/sbc/gstsbcenc.h b/ext/sbc/gstsbcenc.h index 0329351fa6..7904c3ef13 100644 --- a/ext/sbc/gstsbcenc.h +++ b/ext/sbc/gstsbcenc.h @@ -24,7 +24,7 @@ #include #include -#include "sbc.h" +#include G_BEGIN_DECLS diff --git a/ext/sbc/gstsbcutil.c b/ext/sbc/gstsbcutil.c index 47e997cec9..63fef55020 100644 --- a/ext/sbc/gstsbcutil.c +++ b/ext/sbc/gstsbcutil.c @@ -1,5 +1,4 @@ -/* - * +/* SBC audio utilities * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann @@ -317,155 +316,18 @@ gst_sbc_parse_caps_from_sbc (sbc_t * sbc) 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) @@ -519,9 +381,9 @@ gst_sbc_util_fill_sbc_params (sbc_t * sbc, GstCaps * caps) 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) diff --git a/ext/sbc/gstsbcutil.h b/ext/sbc/gstsbcutil.h index 962532f786..8d594c0fa8 100644 --- a/ext/sbc/gstsbcutil.h +++ b/ext/sbc/gstsbcutil.h @@ -1,5 +1,4 @@ -/* - * +/* SBC audio utilities * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2004-2010 Marcel Holtmann @@ -23,7 +22,7 @@ #include -#include "sbc.h" +#include #include #define SBC_AM_AUTO 0x02 @@ -61,8 +60,6 @@ gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation); GstCaps *gst_sbc_parse_caps_from_sbc(sbc_t *sbc); -GstCaps *gst_sbc_util_caps_fixate(GstCaps *caps, gchar **error_message); - void gst_sbc_util_set_structure_int_param(GstStructure *structure, const gchar *field, gint field_value, GValue *value); diff --git a/ext/sbc/sbc-plugin.c b/ext/sbc/sbc-plugin.c new file mode 100644 index 0000000000..7020753ddf --- /dev/null +++ b/ext/sbc/sbc-plugin.c @@ -0,0 +1,41 @@ +/* GStreamer SBC audio plugin + * Copyright (C) 2013 Tim-Philipp Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gstsbcdec.h" +#include "gstsbcenc.h" + +#include + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gst_element_register (plugin, "sbcdec", GST_RANK_NONE, GST_TYPE_SBC_DEC); + gst_element_register (plugin, "sbcenc", GST_RANK_NONE, GST_TYPE_SBC_ENC); + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + sbc, + "SBC bluetooth audio support", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); -- 2.34.1