mpeg12 support patches
"Luiz Augusto von Dentz" <[email protected]>
| Newsgroups | gmane.linux.bluez.devel |
|---|---|
| Message-ID | <[email protected]> |
This patches enable audio service to announce the capabilities of mpeg12 SEPs and use them with gstreamer plugin. To test them it requires Thiagos's patch against cvs mp3parse: http://bugzilla.gnome.org/show_bug.cgi?id=504493 then use gstreamer: gst-launch filesrc location=<some mp3 file> ! mp3parse ! a2dpsink device=<address> ps: these changes can also be pulled for my git tree here: git://git.infradead/users/vudentz/bluez-utils.git -- Luiz Augusto von Dentz Engenheiro de Computação ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ Bluez-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/bluez-devel
0001-Initial-support-of-mpeg12-codec.patch
(text/x-diff, 11.8 KB)
From 58d4228ee302d686635fca2edcf5152eedf93766 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Tue, 11 Dec 2007 18:48:41 -0300 Subject: [PATCH] Initial support of mpeg12 codec. --- audio/a2dp.c | 30 ++++++++++---------- audio/a2dp.h | 77 +++++++++++++++++++++++++++++++++++++------------- audio/ipc.h | 8 ++++- audio/sink.c | 88 +++++++++++++++++++++++++++++----------------------------- audio/unix.c | 19 ++++++++++-- 5 files changed, 139 insertions(+), 83 deletions(-) diff --git a/audio/a2dp.c b/audio/a2dp.c index 74699cd..1c59374 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -355,25 +355,25 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; - sbc_cap.frequency = ( A2DP_SAMPLING_FREQ_48000 | - A2DP_SAMPLING_FREQ_44100 | - A2DP_SAMPLING_FREQ_32000 | - A2DP_SAMPLING_FREQ_16000 ); + sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 | + SBC_SAMPLING_FREQ_44100 | + SBC_SAMPLING_FREQ_32000 | + SBC_SAMPLING_FREQ_16000 ); - sbc_cap.channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO | - A2DP_CHANNEL_MODE_STEREO | - A2DP_CHANNEL_MODE_DUAL_CHANNEL | - A2DP_CHANNEL_MODE_MONO ); + sbc_cap.channel_mode = ( SBC_CHANNEL_MODE_JOINT_STEREO | + SBC_CHANNEL_MODE_STEREO | + SBC_CHANNEL_MODE_DUAL_CHANNEL | + SBC_CHANNEL_MODE_MONO ); - sbc_cap.block_length = ( A2DP_BLOCK_LENGTH_16 | - A2DP_BLOCK_LENGTH_12 | - A2DP_BLOCK_LENGTH_8 | - A2DP_BLOCK_LENGTH_4 ); + sbc_cap.block_length = ( SBC_BLOCK_LENGTH_16 | + SBC_BLOCK_LENGTH_12 | + SBC_BLOCK_LENGTH_8 | + SBC_BLOCK_LENGTH_4 ); - sbc_cap.subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 ); + sbc_cap.subbands = ( SBC_SUBBANDS_8 | SBC_SUBBANDS_4 ); - sbc_cap.allocation_method = ( A2DP_ALLOCATION_LOUDNESS | - A2DP_ALLOCATION_SNR ); + sbc_cap.allocation_method = ( SBC_ALLOCATION_LOUDNESS | + SBC_ALLOCATION_SNR ); sbc_cap.min_bitpool = MIN_BITPOOL; sbc_cap.max_bitpool = MAX_BITPOOL; diff --git a/audio/a2dp.h b/audio/a2dp.h index 8227296..cfd1c47 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -27,26 +27,37 @@ #define A2DP_CODEC_MPEG24 0x02 #define A2DP_CODEC_ATRAC 0x03 -#define A2DP_SAMPLING_FREQ_16000 (1 << 3) -#define A2DP_SAMPLING_FREQ_32000 (1 << 2) -#define A2DP_SAMPLING_FREQ_44100 (1 << 1) -#define A2DP_SAMPLING_FREQ_48000 1 - -#define A2DP_CHANNEL_MODE_MONO (1 << 3) -#define A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define A2DP_CHANNEL_MODE_STEREO (1 << 1) -#define A2DP_CHANNEL_MODE_JOINT_STEREO 1 - -#define A2DP_BLOCK_LENGTH_4 (1 << 3) -#define A2DP_BLOCK_LENGTH_8 (1 << 2) -#define A2DP_BLOCK_LENGTH_12 (1 << 1) -#define A2DP_BLOCK_LENGTH_16 1 - -#define A2DP_SUBBANDS_4 (1 << 1) -#define A2DP_SUBBANDS_8 1 - -#define A2DP_ALLOCATION_SNR (1 << 1) -#define A2DP_ALLOCATION_LOUDNESS 1 +#define SBC_SAMPLING_FREQ_16000 (1 << 3) +#define SBC_SAMPLING_FREQ_32000 (1 << 2) +#define SBC_SAMPLING_FREQ_44100 (1 << 1) +#define SBC_SAMPLING_FREQ_48000 1 + +#define SBC_CHANNEL_MODE_MONO (1 << 3) +#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define SBC_CHANNEL_MODE_STEREO (1 << 1) +#define SBC_CHANNEL_MODE_JOINT_STEREO 1 + +#define SBC_BLOCK_LENGTH_4 (1 << 3) +#define SBC_BLOCK_LENGTH_8 (1 << 2) +#define SBC_BLOCK_LENGTH_12 (1 << 1) +#define SBC_BLOCK_LENGTH_16 1 + +#define SBC_SUBBANDS_4 (1 << 1) +#define SBC_SUBBANDS_8 1 + +#define SBC_ALLOCATION_SNR (1 << 1) +#define SBC_ALLOCATION_LOUDNESS 1 + +#define MPEG_LAYER_MP1 (1 << 2) +#define MPEG_LAYER_MP2 (1 << 1) +#define MPEG_LAYER_MP3 1 + +#define MPEG_SAMPLING_FREQ_16000 (1 << 5) +#define MPEG_SAMPLING_FREQ_22050 (1 << 4) +#define MPEG_SAMPLING_FREQ_24000 (1 << 3) +#define MPEG_SAMPLING_FREQ_32000 (1 << 2) +#define MPEG_SAMPLING_FREQ_44100 (1 << 1) +#define MPEG_SAMPLING_FREQ_48000 1 #define MAX_BITPOOL 64 #define MIN_BITPOOL 2 @@ -64,6 +75,19 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +struct mpeg_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t channel_mode:4; + uint8_t crc:1; + uint8_t layer:3; + uint8_t frequency:6; + uint8_t mpf:1; + uint8_t rfa:1; + uint8_t bitrate0:7; + uint8_t vbr:1; + uint8_t bitrate1; +} __attribute__ ((packed)); + #elif __BYTE_ORDER == __BIG_ENDIAN struct sbc_codec_cap { @@ -77,6 +101,19 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +struct mpeg_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t layer:3; + uint8_t crc:1; + uint8_t channel_mode:4; + uint8_t rfa:1; + uint8_t mpf:1; + uint8_t frequency:6; + uint8_t vbr:1; + uint8_t bitrate0:7; + uint8_t bitrate1; +} __attribute__ ((packed)); + #else #error "Unknown byte order" #endif diff --git a/audio/ipc.h b/audio/ipc.h index 3e1200f..64ed65a 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -162,8 +162,14 @@ typedef struct { uint8_t max_bitpool; } __attribute__ ((packed)) sbc_capabilities_t; -/* To be defined */ typedef struct { + uint8_t channel_mode; + uint8_t crc; + uint8_t layer; + uint8_t frequency; + uint8_t mpf; + uint8_t vbr; + uint16_t bitrate; } __attribute__ ((packed)) mpeg_capabilities_t; struct bt_getcapabilities_rsp { diff --git a/audio/sink.c b/audio/sink.c index 259abf8..a95c2e4 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -193,28 +193,28 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { switch (freq) { - case A2DP_SAMPLING_FREQ_16000: - case A2DP_SAMPLING_FREQ_32000: + case SBC_SAMPLING_FREQ_16000: + case SBC_SAMPLING_FREQ_32000: return 53; - case A2DP_SAMPLING_FREQ_44100: + case SBC_SAMPLING_FREQ_44100: switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: return 31; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: return 53; default: error("Invalid channel mode %u", mode); return 53; } - case A2DP_SAMPLING_FREQ_48000: + case SBC_SAMPLING_FREQ_48000: switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: return 29; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: return 51; default: error("Invalid channel mode %u", mode); @@ -236,58 +236,58 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; cap->cap.media_codec_type = A2DP_CODEC_SBC; - if (supported->frequency & A2DP_SAMPLING_FREQ_44100) - cap->frequency = A2DP_SAMPLING_FREQ_44100; - else if (supported->frequency & A2DP_SAMPLING_FREQ_48000) - cap->frequency = A2DP_SAMPLING_FREQ_48000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) - cap->frequency = A2DP_SAMPLING_FREQ_32000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) - cap->frequency = A2DP_SAMPLING_FREQ_16000; + if (supported->frequency & SBC_SAMPLING_FREQ_44100) + cap->frequency = SBC_SAMPLING_FREQ_44100; + else if (supported->frequency & SBC_SAMPLING_FREQ_48000) + cap->frequency = SBC_SAMPLING_FREQ_48000; + else if (supported->frequency & SBC_SAMPLING_FREQ_32000) + cap->frequency = SBC_SAMPLING_FREQ_32000; + else if (supported->frequency & SBC_SAMPLING_FREQ_16000) + cap->frequency = SBC_SAMPLING_FREQ_16000; else { error("No supported frequencies"); return FALSE; } - if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) - cap->channel_mode = A2DP_CHANNEL_MODE_MONO; + if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; + else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO) + cap->channel_mode = SBC_CHANNEL_MODE_STEREO; + else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; + else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO) + cap->channel_mode = SBC_CHANNEL_MODE_MONO; else { error("No supported channel modes"); return FALSE; } - if (supported->block_length & A2DP_BLOCK_LENGTH_16) - cap->block_length = A2DP_BLOCK_LENGTH_16; - else if (supported->block_length & A2DP_BLOCK_LENGTH_12) - cap->block_length = A2DP_BLOCK_LENGTH_12; - else if (supported->block_length & A2DP_BLOCK_LENGTH_8) - cap->block_length = A2DP_BLOCK_LENGTH_8; - else if (supported->block_length & A2DP_BLOCK_LENGTH_4) - cap->block_length = A2DP_BLOCK_LENGTH_4; + if (supported->block_length & SBC_BLOCK_LENGTH_16) + cap->block_length = SBC_BLOCK_LENGTH_16; + else if (supported->block_length & SBC_BLOCK_LENGTH_12) + cap->block_length = SBC_BLOCK_LENGTH_12; + else if (supported->block_length & SBC_BLOCK_LENGTH_8) + cap->block_length = SBC_BLOCK_LENGTH_8; + else if (supported->block_length & SBC_BLOCK_LENGTH_4) + cap->block_length = SBC_BLOCK_LENGTH_4; else { error("No supported block lengths"); return FALSE; } - if (supported->subbands & A2DP_SUBBANDS_8) - cap->subbands = A2DP_SUBBANDS_8; - else if (supported->subbands & A2DP_SUBBANDS_4) - cap->subbands = A2DP_SUBBANDS_4; + if (supported->subbands & SBC_SUBBANDS_8) + cap->subbands = SBC_SUBBANDS_8; + else if (supported->subbands & SBC_SUBBANDS_4) + cap->subbands = SBC_SUBBANDS_4; else { error("No supported subbands"); return FALSE; } - if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) - cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - else if (supported->allocation_method & A2DP_ALLOCATION_SNR) - cap->allocation_method = A2DP_ALLOCATION_SNR; + if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS) + cap->allocation_method = SBC_ALLOCATION_LOUDNESS; + else if (supported->allocation_method & SBC_ALLOCATION_SNR) + cap->allocation_method = SBC_ALLOCATION_SNR; min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), diff --git a/audio/unix.c b/audio/unix.c index d9f7c67..2226579 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -341,6 +341,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, struct bt_getcapabilities_rsp *rsp = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; struct sbc_codec_cap *sbc_cap = NULL; + struct mpeg_codec_cap *mpeg_cap = NULL; GSList *l; if (err) @@ -364,10 +365,11 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, codec_cap = (void *) cap->data; - if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) { + if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) sbc_cap = (void *) codec_cap; - break; - } + + if (codec_cap->media_codec_type == A2DP_CODEC_MPEG12 && !mpeg_cap) + mpeg_cap = (void *) codec_cap; } /* endianess prevent direct cast */ @@ -381,6 +383,17 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; } + if (mpeg_cap) { + rsp->mpeg_capabilities.channel_mode = mpeg_cap->channel_mode; + rsp->mpeg_capabilities.crc = mpeg_cap->crc; + rsp->mpeg_capabilities.layer = mpeg_cap->layer; + rsp->mpeg_capabilities.frequency = mpeg_cap->frequency; + rsp->mpeg_capabilities.mpf = mpeg_cap->mpf; + rsp->mpeg_capabilities.vbr = mpeg_cap->vbr; + rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate1 & + mpeg_cap->bitrate0 << 8; + } + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); return; -- 1.5.3.7
0002-Make-a2dpsink-to-act-like-a-bin-and-split-payloader.patch
(text/x-diff, 61 KB)
From e01446b589d04325c183b344fd73cdd947af1939 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Wed, 12 Dec 2007 12:05:42 -0300 Subject: [PATCH] Make a2dpsink to act like a bin and split payloader code in another element. --- audio/Makefile.am | 4 +- audio/gsta2dpsink.c | 1042 ++++++++++++-------------------------------------- audio/gsta2dpsink.h | 29 +- audio/gstbluetooth.c | 20 +- audio/gstsbcdec.c | 17 +- audio/gstsbcdec.h | 2 + audio/gstsbcenc.c | 131 +++---- audio/gstsbcenc.h | 2 + audio/gstsbcparse.c | 223 ++++------- audio/gstsbcparse.h | 5 + audio/gstsbcutil.c | 100 +++++ audio/gstsbcutil.h | 19 + 12 files changed, 532 insertions(+), 1062 deletions(-) diff --git a/audio/Makefile.am b/audio/Makefile.am index f9284b0..5d838c8 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -43,11 +43,13 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ + gsta2dpsendersink.h gsta2dpsendersink.c \ gsta2dpsink.h gsta2dpsink.c \ gstsbcutil.h gstsbcutil.c \ + gstrtpsbcpay.h gstrtpsbcpay.c \ rtp.h ipc.h ipc.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc -libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 +libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 5798bc2..d90909c 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -26,166 +26,67 @@ #endif #include <unistd.h> -#include <sys/un.h> -#include <sys/socket.h> -#include <fcntl.h> #include <pthread.h> -#include <netinet/in.h> - -#include <bluetooth/bluetooth.h> - -#include "ipc.h" -#include "rtp.h" #include "gstsbcutil.h" - #include "gsta2dpsink.h" -GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); -#define GST_CAT_DEFAULT a2dp_sink_debug - -#define BUFFER_SIZE 2048 -#define TEMPLATE_MAX_BITPOOL_VALUE 64 +GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug); +#define GST_CAT_DEFAULT gst_a2dp_sink_debug -#define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START { \ - g_mutex_lock (s->sink_lock); \ - } G_STMT_END - -#define GST_A2DP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ - g_mutex_unlock (s->sink_lock); \ - } G_STMT_END - - -struct bluetooth_data { - struct bt_getcapabilities_rsp caps; /* Bluetooth device capabilities */ - struct bt_setconfiguration_rsp cfg; /* Bluetooth device configuration */ - int samples; /* Number of encoded samples */ - gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ - gsize count; /* Codec transfer buffer counter */ - - int nsamples; /* Cumulative number of codec samples */ - uint16_t seq_num; /* Cumulative packet sequence */ - int frame_count; /* Current frames in buffer*/ -}; - - -#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) -#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) +#define A2DP_SBC_RTP_PAYLOAD_TYPE 1 +#define TEMPLATE_MAX_BITPOOL_STR "64" enum { PROP_0, - PROP_DEVICE, + PROP_DEVICE }; -GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBaseSink, GST_TYPE_BASE_SINK); +GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN); -static const GstElementDetails a2dp_sink_details = +static const GstElementDetails gst_a2dp_sink_details = GST_ELEMENT_DETAILS("Bluetooth A2DP sink", "Sink/Audio", "Plays audio to an A2DP device", "Marcel Holtmann <[email protected]>"); -static GstStaticPadTemplate a2dp_sink_factory = +static GstStaticPadTemplate gst_a2dp_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " + GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " "mode = (string) { mono, dual, stereo, joint }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }," - /* FIXME use constant here */ - "bitpool = (int) [ 2, 64 ]; " - "audio/mpeg, " - "mpegversion = (int) 1, " - "layer = (int) [ 1, 3 ], " - "rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ]")); - -static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, - const bt_audio_msg_header_t *msg); -static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, - bt_audio_msg_header_t *outmsg, - int expected_type); - + "allocation = (string) { snr, loudness }, " + "bitpool = (int) [ 2, " + TEMPLATE_MAX_BITPOOL_STR " ]; " + )); static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + gst_element_class_set_details(element_class, + &gst_a2dp_sink_details); gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&a2dp_sink_factory)); - - gst_element_class_set_details(element_class, &a2dp_sink_details); -} - -static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - - GST_INFO_OBJECT(self, "stop"); - - if (self->watch_id != 0) { - g_source_remove(self->watch_id); - self->watch_id = 0; - } - - if (self->stream) { - g_io_channel_flush(self->stream, NULL); - g_io_channel_close(self->stream); - g_io_channel_unref(self->stream); - self->stream = NULL; - } - - if (self->server) { - bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); - g_io_channel_unref(self->server); - self->server = NULL; - } - - if (self->data) { - g_free(self->data); - self->data = NULL; - } - - if (self->stream_caps) { - gst_caps_unref(self->stream_caps); - self->stream_caps = NULL; - } - - if (self->dev_caps) { - gst_caps_unref(self->dev_caps); - self->dev_caps = NULL; - } - - return TRUE; -} - -static void gst_a2dp_sink_finalize(GObject *object) -{ - GstA2dpSink *self = GST_A2DP_SINK(object); - - if (self->data) - gst_a2dp_sink_stop(GST_BASE_SINK(self)); - - if (self->device) - g_free(self->device); - - g_mutex_free(self->sink_lock); - - G_OBJECT_CLASS(parent_class)->finalize(object); + gst_static_pad_template_get(&gst_a2dp_sink_factory)); } static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - GstA2dpSink *sink = GST_A2DP_SINK(object); + GstA2dpSink *self = GST_A2DP_SINK(object); switch (prop_id) { case PROP_DEVICE: - if (sink->device) - g_free(sink->device); - sink->device = g_value_dup_string(value); + if (self->sink != NULL) + gst_a2dp_sender_sink_set_device(self->sink, + g_value_get_string(value)); + + if (self->device != NULL) + g_free(self->device); + self->device = g_value_dup_string(value); break; default: @@ -197,11 +98,16 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - GstA2dpSink *sink = GST_A2DP_SINK(object); + GstA2dpSink *self = GST_A2DP_SINK(object); + gchar *device; switch (prop_id) { case PROP_DEVICE: - g_value_set_string(value, sink->device); + if (self->sink != NULL) { + device = gst_a2dp_sender_sink_get_device(self->sink); + if (device != NULL) + g_value_take_string(value, device); + } break; default: @@ -210,775 +116,305 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, } } -static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) -{ - int err, ret; - - ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server)); - - if (ret < 0) { - err = errno; - GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", - strerror(err), err); - return -err; - } - - sink->stream = g_io_channel_unix_new(ret); - GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); - - return 0; -} - -static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, - GstCaps *caps, - sbc_capabilities_t *pkt) +static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, + GstStateChange transition) { - sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; - const GValue *value = NULL; - const char *pref, *name; - gint rate, blocks, subbands; - GstStructure *structure = gst_caps_get_structure(caps, 0); - - name = gst_structure_get_name(structure); - /* FIXME only sbc supported here, should suport mp3 */ - if (!(IS_SBC(name))) { - GST_ERROR_OBJECT(sink, "Unsupported format %s", name); - return FALSE; - } - - value = gst_structure_get_value(structure, "rate"); - rate = g_value_get_int(value); - if (rate == 44100) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; - else if (rate == 48000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; - else if (rate == 32000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; - else if (rate == 16000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; - else { - GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); - return FALSE; - } - - value = gst_structure_get_value(structure, "mode"); - pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; - else if (strcmp(pref, "mono") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - else if (strcmp(pref, "dual") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (strcmp(pref, "stereo") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - else if (strcmp(pref, "joint") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else { - GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); - return FALSE; - } + GstA2dpSink *self = GST_A2DP_SINK(element); - value = gst_structure_get_value(structure, "allocation"); - pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; - else if (strcmp(pref, "loudness") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - else if (strcmp(pref, "snr") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; - else { - GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); - return FALSE; - } + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + gst_element_set_state(GST_ELEMENT(self->sink), + GST_STATE_READY); + break; - value = gst_structure_get_value(structure, "subbands"); - subbands = g_value_get_int(value); - if (subbands == 8) - cfg->subbands = BT_A2DP_SUBBANDS_8; - else if (subbands == 4) - cfg->subbands = BT_A2DP_SUBBANDS_4; - else { - GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); - return FALSE; - } + case GST_STATE_CHANGE_READY_TO_NULL: + if (self->newseg_event != NULL) { + gst_event_unref(self->newseg_event); + self->newseg_event = NULL; + } + break; - value = gst_structure_get_value(structure, "blocks"); - blocks = g_value_get_int(value); - if (blocks == 16) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; - else if (blocks == 12) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; - else if (blocks == 8) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; - else if (blocks == 4) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; - else { - GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); - return FALSE; + default: + break; } - /* FIXME min and max ??? */ - value = gst_structure_get_value(structure, "bitpool"); - - cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); - - memcpy(pkt, cfg, sizeof(*pkt)); - - return TRUE; + return GST_ELEMENT_CLASS(parent_class)->change_state(element, + transition); } -static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) +static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { - struct bluetooth_data *data = self->data; - gint ret; - GIOError err; - GError *gerr = NULL; - GIOStatus status; - GIOFlags flags; - gsize read; - - ret = gst_a2dp_sink_bluetooth_recvmsg_fd(self); - if (ret < 0) - return FALSE; - - if (!self->stream) { - GST_ERROR_OBJECT(self, "Error while configuring device: " - "could not acquire audio socket"); - return FALSE; - } + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); - /* set stream socket to nonblock */ - GST_LOG_OBJECT(self, "setting stream socket to nonblock"); - flags = g_io_channel_get_flags(self->stream); - flags |= G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to nonblock: " - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to nonblock"); - } + parent_class = g_type_class_peek_parent(klass); - /* It is possible there is some outstanding - data in the pipe - we have to empty it */ - GST_LOG_OBJECT(self, "emptying stream pipe"); - while (1) { - err = g_io_channel_read(self->stream, data->buffer, - (gsize) data->cfg.link_mtu, - &read); - if (err != G_IO_ERROR_NONE || read <= 0) - break; - } + object_class->set_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_get_property); - /* set stream socket to block */ - GST_LOG_OBJECT(self, "setting stream socket to block"); - flags = g_io_channel_get_flags(self->stream); - flags &= ~G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to block:" - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to block"); - } + element_class->change_state = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_change_state); - memset(data->buffer, 0, sizeof(data->buffer)); + g_object_class_install_property(object_class, PROP_DEVICE, + g_param_spec_string("device", "Device", + "Bluetooth remote device address", + NULL, G_PARAM_READWRITE)); - return TRUE; + GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0, + "A2DP sink element"); } -static gboolean server_callback(GIOChannel *chan, - GIOCondition cond, gpointer data) +static GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self) { - GstA2dpSink *sink; - - if (cond & G_IO_HUP || cond & G_IO_NVAL) - return FALSE; - else if (cond & G_IO_ERR) { - sink = GST_A2DP_SINK(data); - GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); - } - - return TRUE; + return gst_a2dp_sender_sink_get_device_caps(self->sink); } -static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) +static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad) { - sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; - GstStructure *structure; - GValue *value; - GValue *list; - gchar *tmp; - - GST_LOG_OBJECT(self, "updating device caps"); - - structure = gst_structure_empty_new("audio/x-sbc"); - value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); - - /* mode */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } else { - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - } - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "mode", list); - g_free(list); - list = NULL; - } - - /* subbands */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - value = g_value_init(value, G_TYPE_INT); - if (sbc->subbands & BT_A2DP_SUBBANDS_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - if (sbc->subbands & BT_A2DP_SUBBANDS_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "subbands", list); - g_free(list); - list = NULL; - } - - /* blocks */ - value = g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { - g_value_set_int(value, 16); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { - g_value_set_int(value, 12); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "blocks", list); - g_free(list); - list = NULL; - } - - /* allocation */ - g_value_init(value, G_TYPE_STRING); - list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); - if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); + GstCaps *caps; + GstCaps *caps_aux; + GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + + if (self->sink == NULL) { + GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized " + "returning template caps"); + caps = gst_static_pad_template_get_caps( + &gst_a2dp_sink_factory); } else { - if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - } - if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "allocation", list); - g_free(list); - list = NULL; - } - - /* rate */ - g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* bitpool */ - value = g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, - MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL_VALUE), - MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE)); - gst_structure_set_value(structure, "bitpool", value); - - /* channels */ - gst_value_set_int_range(value, 1, 2); - gst_structure_set_value(structure, "channels", value); - - g_free(value); - - if (self->dev_caps != NULL) - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_new_full(structure, NULL); - - tmp = gst_caps_to_string(self->dev_caps); - GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); - g_free(tmp); - - return TRUE; + GST_LOG_OBJECT(self, "Getting device caps"); + caps = gst_a2dp_sink_get_device_caps(self); + if (caps == NULL) + caps = gst_static_pad_template_get_caps( + &gst_a2dp_sink_factory); + } + caps_aux = gst_caps_copy(caps); + g_object_set(self->capsfilter, "caps", caps_aux, NULL); + gst_caps_unref(caps_aux); + return caps; } -static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) +static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self) { - gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_getcapabilities_req *req = (void *) buf; - bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; - struct bt_getcapabilities_rsp *rsp = (void *) buf; - GIOError io_error; - - memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + GstElement *sink; - req->h.msg_type = BT_GETCAPABILITIES_REQ; - strncpy(req->device, self->device, 18); - - io_error = gst_a2dp_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while asking device caps"); - } + if (self->sink == NULL) + sink = gst_element_factory_make("a2dpsendersink", "sendersink"); + else + sink = GST_ELEMENT(self->sink); - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, - BT_GETCAPABILITIES_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while getting device caps"); + if (sink == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create a2dpsendersink"); return FALSE; } - if (rsp_hdr->posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return FALSE; + if (!gst_bin_add(GST_BIN(self), sink)) { + GST_ERROR_OBJECT(self, "failed to add a2dpsendersink " + "to the bin"); + goto cleanup_and_fail; } - memcpy(&self->data->caps, rsp, sizeof(*rsp)); - if (!gst_a2dp_sink_update_caps(self)) { - GST_WARNING_OBJECT(self, "failed to update capabilities"); - return FALSE; + if (gst_element_set_state(sink, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "a2dpsendersink failed to go to ready"); + goto remove_element_and_fail; } - return TRUE; -} - -static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - gint sk; - gint err; - - GST_INFO_OBJECT(self, "start"); - - self->watch_id = 0; - - sk = bt_audio_service_open(); - if (sk <= 0) { - err = errno; - GST_ERROR_OBJECT(self, "Cannot open connection to bt " - "audio service: %s %d", strerror(err), err); - goto failed; + if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) { + GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay " + "to a2dpsendersink"); + goto remove_element_and_fail; } - self->server = g_io_channel_unix_new(sk); - self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | - G_IO_NVAL, server_callback, self); + self->sink = GST_A2DP_SENDER_SINK(sink); + g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); - self->data = g_new0(struct bluetooth_data, 1); - memset(self->data, 0, sizeof(struct bluetooth_data)); + gst_element_set_state(sink, GST_STATE_PAUSED); - self->stream = NULL; - self->stream_caps = NULL; + return TRUE; - if (!gst_a2dp_sink_get_capabilities(self)) - goto failed; +remove_element_and_fail: + gst_element_set_state (sink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), sink); + return FALSE; - return TRUE; +cleanup_and_fail: + if (sink != NULL) + g_object_unref(G_OBJECT(sink)); -failed: - bt_audio_service_close(sk); return FALSE; } -static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self) +static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) { - gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_streamstart_req *req = (void *) buf; - bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; - struct bt_streamfd_ind *ind = (void*) buf; - GIOError io_error; - - GST_DEBUG_OBJECT(self, "stream start"); - - memset (req, 0, sizeof(buf)); - req->h.msg_type = BT_STREAMSTART_REQ; - - io_error = gst_a2dp_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error ocurred while sending " - "start packet"); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "stream start packet sent"); - - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, - BT_STREAMSTART_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while stream start confirmation"); - return FALSE; - } - - if (rsp_hdr->posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "stream started"); + GstElement *rtppay; - io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h, - BT_STREAMFD_IND); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while receiving stream fd"); + rtppay = gst_element_factory_make("rtpsbcpay", "rtp"); + if (rtppay == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create rtpsbcpay"); return FALSE; } - if (!gst_a2dp_sink_conf_recv_stream_fd(self)) - return FALSE; - - return TRUE; -} - -static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) -{ - gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_setconfiguration_req *req = (void *) buf; - bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; - struct bt_setconfiguration_rsp *rsp = (void *) buf; - gboolean ret; - GIOError io_error; - - GST_DEBUG_OBJECT(self, "configuring device"); - - memset (req, 0, sizeof(buf)); - req->h.msg_type = BT_SETCONFIGURATION_REQ; - req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; - strncpy(req->device, self->device, 18); - ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities); - if (!ret) { - GST_ERROR_OBJECT(self, "Couldn't parse caps " - "to packet configuration"); - return FALSE; + if (!gst_bin_add(GST_BIN(self), rtppay)) { + GST_ERROR_OBJECT(self, "failed to add rtp sbc pay to the bin"); + goto cleanup_and_fail; } - io_error = gst_a2dp_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error ocurred while sending " - "configurarion packet"); - return FALSE; + if (gst_element_set_state(rtppay, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "rtpsbcpay failed to go to ready"); + goto remove_element_and_fail; } - GST_DEBUG_OBJECT(self, "configuration packet sent"); - - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, - BT_SETCONFIGURATION_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while receiving device confirmation"); - return FALSE; + if (!gst_element_link(self->capsfilter, rtppay)) { + GST_ERROR_OBJECT(self, "couldn't link capsfilter " + "to rtpsbcpay"); + goto remove_element_and_fail; } - if (rsp_hdr->posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return FALSE; - } + self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); + g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL); - memcpy(&self->data->cfg, rsp, sizeof(*rsp)); - GST_DEBUG_OBJECT(self, "configuration set"); + gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE; -} -static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstA2dpSink *sink = GST_A2DP_SINK(basesink); - gboolean ret; - - GST_A2DP_SINK_MUTEX_LOCK(sink); - - ret = gst_a2dp_sink_stream_start(sink); - - GST_A2DP_SINK_MUTEX_UNLOCK(sink); +remove_element_and_fail: + gst_element_set_state (rtppay, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), rtppay); + return FALSE; - if (!ret) - return GST_FLOW_ERROR; +cleanup_and_fail: + if (rtppay != NULL) + g_object_unref(G_OBJECT(rtppay)); - return GST_FLOW_OK; + return FALSE; } -static int gst_a2dp_sink_avdtp_write(GstA2dpSink *self) +static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) { - gsize ret; - struct bluetooth_data *data = self->data; - struct rtp_header *header; - struct rtp_payload *payload; - GIOError err; - - header = (void *) data->buffer; - payload = (void *) (data->buffer + sizeof(*header)); - - memset(data->buffer, 0, sizeof(*header) + sizeof(*payload)); - - payload->frame_count = data->frame_count; - header->v = 2; - header->pt = 1; - header->sequence_number = htons(data->seq_num); - header->timestamp = htonl(data->nsamples); - header->ssrc = htonl(1); - - err = g_io_channel_write(self->stream, data->buffer, data->count, &ret); - if (err != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while sending data"); - ret = -1; - } - - /* Reset buffer of data to send */ - data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - data->frame_count = 0; - data->samples = 0; - data->seq_num++; - - return ret; -} + GstA2dpSink *self; + GstStructure *structure; -static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - struct bluetooth_data *data = self->data; - gint encoded; - gint ret; + self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + GST_INFO_OBJECT(self, "setting caps"); - encoded = GST_BUFFER_SIZE(buffer); + structure = gst_caps_get_structure(caps, 0); - if (data->count + encoded >= data->cfg.link_mtu) { - ret = gst_a2dp_sink_avdtp_write(self); - if (ret < 0) - return GST_FLOW_ERROR; + /* first, we need to create our rtp payloader */ + if (gst_structure_has_name(structure, "audio/x-sbc")) + gst_a2dp_sink_init_rtp_sbc_element(self); + else { + GST_ERROR_OBJECT(self, "Unexpected media type"); + return FALSE; } - memcpy(data->buffer + data->count, GST_BUFFER_DATA(buffer), encoded); - data->count += encoded; - data->frame_count++; - - return GST_FLOW_OK; -} - -static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - - if (self->dev_caps) - return gst_caps_ref(self->dev_caps); - - return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(self))); -} - -static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - gboolean ret; - - GST_A2DP_SINK_MUTEX_LOCK(self); - ret = gst_a2dp_sink_configure(self, caps); - - if (self->stream_caps) - gst_caps_unref(self->stream_caps); - self->stream_caps = gst_caps_ref(caps); - - GST_A2DP_SINK_MUTEX_UNLOCK(self); + if (!gst_a2dp_sink_init_sender_sink(self)) + return FALSE; - return ret; -} + if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps)) + return FALSE; -static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); + g_object_set(G_OBJECT(self->rtp), "mtu", + gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL); - if (self->stream != NULL) - g_io_channel_flush (self->stream, NULL); + /* we forward our new segment here if we have one */ + gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), + self->newseg_event); + self->newseg_event = NULL; - return TRUE; + return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps); } -static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) +/* used for catching newsegment events while we don't have a sink, for + * later forwarding it to the sink */ +static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); + GstA2dpSink *self; - parent_class = g_type_class_peek_parent(klass); + self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - object_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize); - object_class->set_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_get_property); - - basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sink_start); - basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sink_stop); - basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render); - basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll); - basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); - basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps); - basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); - - g_object_class_install_property(object_class, PROP_DEVICE, - g_param_spec_string("device", "Device", - "Bluetooth remote device address", - NULL, G_PARAM_READWRITE)); + if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT && + gst_element_get_parent(GST_ELEMENT(self->sink)) != + GST_OBJECT_CAST(self)) { + if (self->newseg_event != NULL) + gst_event_unref(self->newseg_event); + self->newseg_event = gst_event_ref(event); + } - GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0, - "A2DP sink element"); + return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event); } -static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) +static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self) { - self->device = NULL; - self->data = NULL; - - self->stream = NULL; + GstElement *element; - self->dev_caps = NULL; + element = gst_element_factory_make("capsfilter", "filter"); + if (element == NULL) + goto failed; - self->sink_lock = g_mutex_new(); -} + if (!gst_bin_add(GST_BIN(self), element)) + goto failed; -static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, - const bt_audio_msg_header_t *msg) -{ - gint err; - GIOError error; - gsize written; - - GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type)); - - error = g_io_channel_write(self->server, (const gchar*) msg, - BT_AUDIO_IPC_PACKET_SIZE, &written); - if (error != G_IO_ERROR_NONE) { - err = errno; - GST_ERROR_OBJECT(self, "Error sending data to audio service:" - " %s(%d)", strerror(err), err); - } + self->capsfilter = element; + return TRUE; - return error; +failed: + GST_ERROR_OBJECT(self, "Failed to initialize caps filter"); + return FALSE; } -static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self, - bt_audio_msg_header_t *inmsg) +static void gst_a2dp_sink_init(GstA2dpSink *self, + GstA2dpSinkClass *klass) { - GIOError status; - gsize bytes_read; - const char *type; - - status = g_io_channel_read(self->server, (gchar*) inmsg, - BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); - if (status != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error receiving data from service"); - return status; - } - - type = bt_audio_strmsg(inmsg->msg_type); - if (!type) { - GST_ERROR_OBJECT(self, "Bogus message type %d " - "received from audio service", - inmsg->msg_type); - return G_IO_ERROR_INVAL; - } - - GST_DEBUG_OBJECT(self, "Received %s", type); + GstPad *capsfilter_pad; - return status; + self->sink = NULL; + self->rtp = NULL; + self->device = NULL; + self->capsfilter = NULL; + self->newseg_event = NULL; + + /* we initialize our capsfilter */ + gst_a2dp_sink_init_caps_filter(self); + g_object_set(self->capsfilter, "caps", + gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), + NULL); + + /* we search for the capsfilter sinkpad */ + capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); + + /* now we add a ghostpad */ + self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", + capsfilter_pad)); + g_object_unref(capsfilter_pad); + + /* the getcaps of our ghostpad must reflect the device caps */ + gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_get_caps); + self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); + gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), + GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); + + /* we need to handle events on our own and we also need the eventfunc + * of the ghostpad for forwarding calls */ + self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); + gst_pad_set_event_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_handle_event); + + if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) + GST_ERROR_OBJECT(self, "failed to add ghostpad"); + + self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( + "a2dpsendersink", "sendersink")); + if (self->sink == NULL) + GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); } -static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, - bt_audio_msg_header_t *outmsg, - int expected_type) +gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin) { - GIOError status; - - status = gst_a2dp_sink_audioservice_recv(self, outmsg); - if (status != G_IO_ERROR_NONE) - return status; - - if (outmsg->msg_type != expected_type) { - GST_ERROR_OBJECT(self, "Bogus message %s " - "received while %s was expected", - bt_audio_strmsg(outmsg->msg_type), - bt_audio_strmsg(expected_type)); - return G_IO_ERROR_INVAL; - } - - return status; + return gst_element_register (plugin, "a2dpsink", + GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK); } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index f5b9b69..4bf9d60 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -22,7 +22,8 @@ */ #include <gst/gst.h> -#include <gst/base/gstbasesink.h> +#include <gst/rtp/gstbasertppayload.h> +#include "gsta2dpsendersink.h" G_BEGIN_DECLS @@ -40,31 +41,27 @@ G_BEGIN_DECLS typedef struct _GstA2dpSink GstA2dpSink; typedef struct _GstA2dpSinkClass GstA2dpSinkClass; -struct bluetooth_data; - struct _GstA2dpSink { - GstBaseSink sink; - - gchar *device; - GIOChannel *stream; + GstBin bin; - struct bluetooth_data *data; - GIOChannel *server; + GstBaseRTPPayload *rtp; + GstA2dpSenderSink *sink; + GstElement *capsfilter; - /* stream connection data */ - GstCaps *stream_caps; - - GstCaps *dev_caps; + gchar *device; - GMutex *sink_lock; + GstGhostPad *ghostpad; + GstPadSetCapsFunction ghostpad_setcapsfunc; + GstPadEventFunction ghostpad_eventfunc; - guint watch_id; + GstEvent *newseg_event; }; struct _GstA2dpSinkClass { - GstBaseSinkClass parent_class; + GstBinClass parent_class; }; GType gst_a2dp_sink_get_type(void); +gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin); G_END_DECLS diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 593a311..764bc89 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -28,7 +28,9 @@ #include "gstsbcenc.h" #include "gstsbcdec.h" #include "gstsbcparse.h" +#include "gsta2dpsendersink.h" #include "gsta2dpsink.h" +#include "gstrtpsbcpay.h" static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); @@ -55,20 +57,22 @@ static gboolean plugin_init(GstPlugin *plugin) SBC_CAPS, NULL, NULL) == FALSE) return FALSE; - if (gst_element_register(plugin, "sbcenc", - GST_RANK_NONE, GST_TYPE_SBC_ENC) == FALSE) + if (!gst_sbc_enc_plugin_init(plugin)) return FALSE; - if (gst_element_register(plugin, "sbcdec", - GST_RANK_PRIMARY, GST_TYPE_SBC_DEC) == FALSE) + if (!gst_sbc_dec_plugin_init(plugin)) return FALSE; - if (gst_element_register(plugin, "sbcparse", - GST_RANK_PRIMARY, GST_TYPE_SBC_PARSE) == FALSE) + if (!gst_sbc_parse_plugin_init(plugin)) return FALSE; - if (gst_element_register(plugin, "a2dpsink", - GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) + if (!gst_a2dp_sender_sink_plugin_init(plugin)) + return FALSE; + + if (!gst_a2dp_sink_plugin_init(plugin)) + return FALSE; + + if (!gst_rtp_sbc_pay_plugin_init(plugin)) return FALSE; return TRUE; diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index a60c3e6..8c27dab 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -188,10 +188,21 @@ static void gst_sbc_dec_class_init(GstSbcDecClass *klass) static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass) { - self->sinkpad = gst_pad_new_from_static_template(&sbc_dec_sink_factory, "sink"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_dec_chain)); + self->sinkpad = gst_pad_new_from_static_template( + &sbc_dec_sink_factory, "sink"); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR( + sbc_dec_chain)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - self->srcpad = gst_pad_new_from_static_template(&sbc_dec_src_factory, "src"); + self->srcpad = gst_pad_new_from_static_template( + &sbc_dec_src_factory, "src"); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } + +gboolean gst_sbc_dec_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "sbcdec", + GST_RANK_PRIMARY, GST_TYPE_SBC_DEC); +} + + diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index 4a6922a..0bb0b57 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -58,4 +58,6 @@ struct _GstSbcDecClass { GType gst_sbc_dec_get_type(void); +gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin); + G_END_DECLS diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 185151f..08ddc14 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -120,32 +120,6 @@ static GstStaticPadTemplate sbc_enc_src_factory = gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps); -static void sbc_enc_set_structure_int_param(GstSbcEnc *enc, - GstStructure *structure, const gchar* field, - gint field_value) -{ - GValue *value; - - value = g_new0(GValue,1); - value = g_value_init(value, G_TYPE_INT); - g_value_set_int(value, field_value); - gst_structure_set_value(structure, field, value); - g_free(value); -} - -static void sbc_enc_set_structure_string_param(GstSbcEnc *enc, - GstStructure *structure, const gchar* field, - const gchar* field_value) -{ - GValue *value; - - value = g_new0(GValue,1); - value = g_value_init(value, G_TYPE_STRING); - g_value_set_string(value, field_value); - gst_structure_set_value(structure, field, value); - g_free(value); -} - static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) { GstCaps* src_caps; @@ -153,45 +127,49 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) GEnumValue *enum_value; GEnumClass *enum_class; gchar* temp; + GValue *value; src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad)); structure = gst_caps_get_structure(src_caps, 0); + value = g_new0(GValue, 1); + if (enc->rate != 0) - sbc_enc_set_structure_int_param(enc, structure, "rate", - enc->rate); + gst_sbc_util_set_structure_int_param(structure, "rate", + enc->rate, value); if (enc->channels != 0) - sbc_enc_set_structure_int_param(enc, structure, "channels", - enc->channels); + gst_sbc_util_set_structure_int_param(structure, "channels", + enc->channels, value); if (enc->subbands != 0) - sbc_enc_set_structure_int_param(enc, structure, "subbands", - enc->subbands); + gst_sbc_util_set_structure_int_param(structure, "subbands", + enc->subbands, value); if (enc->blocks != 0) - sbc_enc_set_structure_int_param(enc, structure, "blocks", - enc->blocks); + gst_sbc_util_set_structure_int_param(structure, "blocks", + enc->blocks, value); if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) { enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); enum_value = g_enum_get_value(enum_class, enc->mode); - sbc_enc_set_structure_string_param(enc, structure, "mode", - enum_value->value_nick); + gst_sbc_util_set_structure_string_param(structure, "mode", + enum_value->value_nick, value); g_type_class_unref(enum_class); } if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) { enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); enum_value = g_enum_get_value(enum_class, enc->allocation); - sbc_enc_set_structure_string_param(enc, structure, "allocation", - enum_value->value_nick); + gst_sbc_util_set_structure_string_param(structure, "allocation", + enum_value->value_nick, value); g_type_class_unref(enum_class); } temp = gst_caps_to_string(src_caps); GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp); g_free(temp); + g_free(value); return src_caps; } @@ -207,23 +185,10 @@ static GstCaps* sbc_enc_src_getcaps (GstPad * pad) static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps) { - GstCaps* srcpad_caps; - GstCaps* temp_caps; - gboolean res = TRUE; GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); GST_LOG_OBJECT(enc, "setting srcpad caps"); - srcpad_caps = sbc_enc_generate_srcpad_caps(enc); - temp_caps = gst_caps_intersect(srcpad_caps, caps); - if (temp_caps == GST_CAPS_NONE) - res = FALSE; - - gst_caps_unref(temp_caps); - gst_caps_unref(srcpad_caps); - - g_return_val_if_fail(res, FALSE); - return gst_sbc_enc_fill_sbc_params(enc, caps); } @@ -313,38 +278,16 @@ error: gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) { - GstStructure *structure; - gint rate, channels, subbands, blocks, bitpool; - const gchar* mode; - const gchar* allocation; - - g_assert(gst_caps_is_fixed(caps)); - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - if (!gst_structure_get_int(structure, "subbands", &subbands)) - return FALSE; - if (!gst_structure_get_int(structure, "blocks", &blocks)) - return FALSE; - if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps)) return FALSE; - if (!(mode = gst_structure_get_string(structure, "mode"))) - return FALSE; - if (!(allocation = gst_structure_get_string(structure, "allocation"))) - return FALSE; - - enc->rate = enc->sbc.rate = rate; - enc->channels = enc->sbc.channels = channels; - enc->blocks = enc->sbc.blocks = blocks; - enc->subbands = enc->sbc.subbands = subbands; - enc->sbc.bitpool = bitpool; - enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode); - enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); + enc->rate = enc->sbc.rate; + enc->channels = enc->sbc.channels; + enc->blocks = enc->sbc.blocks; + enc->subbands = enc->sbc.subbands; + enc->mode = enc->sbc.joint; + enc->allocation = enc->sbc.allocation; enc->codesize = sbc_get_codesize(&enc->sbc); enc->frame_length = sbc_get_frame_length(&enc->sbc); enc->frame_duration = sbc_get_frame_duration(&enc->sbc); @@ -390,6 +333,8 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) gst_adapter_flush(adapter, consumed); GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); + /* we have only 1 frame */ + GST_BUFFER_DURATION(output) = enc->frame_duration; res = gst_pad_push(enc->srcpad, output); if (res != GST_FLOW_OK) @@ -559,17 +504,22 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) { - self->sinkpad = gst_pad_new_from_static_template(&sbc_enc_sink_factory, "sink"); + 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_element_add_pad(GST_ELEMENT(self), self->sinkpad); - self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); - 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)); + self->srcpad = gst_pad_new_from_static_template( + &sbc_enc_src_factory, "src"); + 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)); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + 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; @@ -578,5 +528,16 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) self->rate = SBC_ENC_DEFAULT_RATE; self->channels = SBC_ENC_DEFAULT_CHANNELS; + self->frame_length = 0; + self->frame_duration = 0; + self->adapter = gst_adapter_new(); } + +gboolean gst_sbc_enc_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "sbcenc", + GST_RANK_NONE, GST_TYPE_SBC_ENC); +} + + diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index d81428c..c7b2163 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -68,4 +68,6 @@ struct _GstSbcEncClass { GType gst_sbc_enc_get_type(void); +gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin); + G_END_DECLS diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index bae7d62..49d0bb6 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -56,171 +56,83 @@ static GstStaticPadTemplate sbc_parse_src_factory = "allocation = (string) { snr, loudness }," "bitpool = (int) [ 2, 64 ]")); -/* Creates a fixed caps from the caps given. */ -/* FIXME use gstsbcutil caps fixating function */ -static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) +static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) { - GstCaps *result; + GstSbcParse *parse; GstStructure *structure; - const GValue *value; - gboolean error = FALSE; - gint temp, rate, channels, blocks, subbands, bitpool; - const gchar* allocation = NULL; - const gchar* mode = NULL; - const gchar* error_message = NULL; - gchar* str; - - str = gst_caps_to_string(caps); - GST_DEBUG_OBJECT(parse, "Parsing caps: %s", str); - g_free(str); + gint rate, channels; + + parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); structure = gst_caps_get_structure(caps, 0); - if (!gst_structure_has_field(structure, "rate")) { - error = TRUE; - error_message = "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_get_int(structure, "rate", &rate)) + return FALSE; - if (!gst_structure_has_field(structure, "channels")) { - error = TRUE; - error_message = "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_get_int(structure, "channels", &channels)) + return FALSE; - if (!gst_structure_has_field(structure, "blocks")) { - error = TRUE; - error_message = "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 (!(parse->rate == 0 || rate == parse->rate)) + return FALSE; - if (!gst_structure_has_field(structure, "subbands")) { - error = TRUE; - error_message = "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 (!(parse->channels == 0 || channels == parse->channels)) + return FALSE; - if (!gst_structure_has_field(structure, "bitpool")) { - error = TRUE; - error_message = "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; - } + parse->rate = rate; + parse->channels = channels; - if (!gst_structure_has_field(structure, "allocation")) { - error = TRUE; - error_message = "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); - } + return gst_sbc_util_fill_sbc_params(&parse->sbc, caps); +} - if (!gst_structure_has_field(structure, "mode")) { - error = TRUE; - error_message = "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); - else - mode = g_value_get_string(value); - } +static GstCaps* sbc_parse_src_getcaps(GstPad *pad) +{ + GstCaps *caps; + const GstCaps *allowed_caps; + GstStructure *structure; + GValue *value; + GstSbcParse *parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); -error: - if (error) { - GST_ERROR_OBJECT (parse, "Invalid input caps: %s", - error_message); - return NULL; - } + allowed_caps = gst_pad_get_allowed_caps(pad); + if (allowed_caps == NULL) + allowed_caps = gst_pad_get_pad_template_caps(pad); + caps = gst_caps_copy(allowed_caps); - 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); - parse->sbc.rate = rate; - parse->sbc.channels = channels; - parse->sbc.blocks = blocks; - parse->sbc.subbands = subbands; - parse->sbc.bitpool = bitpool; - parse->sbc.joint = gst_sbc_get_mode_int(mode); - parse->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); - - return result; + value = g_new0(GValue, 1); + + structure = gst_caps_get_structure(caps, 0); + + if (parse->rate != 0) + gst_sbc_util_set_structure_int_param(structure, "rate", + parse->rate, value); + if (parse->channels != 0) + gst_sbc_util_set_structure_int_param(structure, "channels", + parse->channels, value); + + g_free(value); + + return caps; } -static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) +static gboolean sbc_parse_src_acceptcaps(GstPad *pad, GstCaps *caps) { + GstStructure *structure; GstSbcParse *parse; - GstCaps *inter, *other, *srccaps; + gint rate, channels; parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); - other = gst_pad_peer_get_caps(parse->srcpad); - if (other == NULL) - other = gst_caps_new_any(); + structure = gst_caps_get_structure(caps, 0); - inter = gst_caps_intersect(caps, other); - if (gst_caps_is_empty(inter)) { - gst_caps_unref(inter); + if (!gst_structure_get_int(structure, "rate", &rate)) return FALSE; - } - srccaps = sbc_parse_select_caps(parse, inter); - if (srccaps == NULL) { - gst_caps_unref(inter); + if (!gst_structure_get_int(structure, "channels", &channels)) return FALSE; - } - gst_pad_set_caps(parse->srcpad, srccaps); + if ((parse->rate == 0 || parse->rate == rate) + && (parse->channels == 0 || parse->channels == channels)) + return TRUE; - gst_caps_unref(inter); - gst_caps_unref(other); - gst_caps_unref(srccaps); - - return TRUE; + return FALSE; } static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) @@ -234,11 +146,13 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) timestamp = GST_BUFFER_TIMESTAMP(buffer); if (parse->buffer) { - GstBuffer *temp = buffer; + GstBuffer *temp; + temp = buffer; buffer = gst_buffer_span(parse->buffer, 0, buffer, - GST_BUFFER_SIZE(parse->buffer) + GST_BUFFER_SIZE(buffer)); - gst_buffer_unref(temp); + GST_BUFFER_SIZE(parse->buffer) + + GST_BUFFER_SIZE(buffer)); gst_buffer_unref(parse->buffer); + gst_buffer_unref(temp); parse->buffer = NULL; } @@ -300,11 +214,13 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG("Finish subband codec"); + if (parse->buffer) { gst_buffer_unref(parse->buffer); parse->buffer = NULL; } sbc_finish(&parse->sbc); + break; default: @@ -341,12 +257,27 @@ static void gst_sbc_parse_class_init(GstSbcParseClass *klass) static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) { - self->sinkpad = gst_pad_new_from_static_template(&sbc_parse_sink_factory, "sink"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_parse_chain)); + self->sinkpad = gst_pad_new_from_static_template( + &sbc_parse_sink_factory, "sink"); + gst_pad_set_chain_function(self->sinkpad, + GST_DEBUG_FUNCPTR(sbc_parse_chain)); gst_pad_set_setcaps_function (self->sinkpad, GST_DEBUG_FUNCPTR (sbc_parse_sink_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - self->srcpad = gst_pad_new_from_static_template(&sbc_parse_src_factory, "src"); + self->srcpad = gst_pad_new_from_static_template( + &sbc_parse_src_factory, "src"); + gst_pad_set_getcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (sbc_parse_src_getcaps)); + gst_pad_set_acceptcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (sbc_parse_src_acceptcaps)); + /* FIXME get encoding parameters on set caps */ gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } + +gboolean gst_sbc_parse_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "sbcparse", + GST_RANK_NONE, GST_TYPE_SBC_PARSE); +} + diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h index ceaf219..eb9ca44 100644 --- a/audio/gstsbcparse.h +++ b/audio/gstsbcparse.h @@ -50,6 +50,9 @@ struct _GstSbcParse { GstBuffer *buffer; sbc_t sbc; + + gint channels; + gint rate; }; struct _GstSbcParseClass { @@ -58,4 +61,6 @@ struct _GstSbcParseClass { GType gst_sbc_parse_get_type(void); +gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin); + G_END_DECLS diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index f2351e6..d791a8d 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -26,6 +26,7 @@ #endif #include "ipc.h" +#include <math.h> #include "gstsbcutil.h" /* @@ -306,4 +307,103 @@ error: return result; } +/** + * 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) + * GValue, it will be left unitialized at the end of the function. + */ +void gst_sbc_util_set_structure_int_param(GstStructure *structure, + const gchar* field, gint field_value, + GValue *value) +{ + value = g_value_init(value, G_TYPE_INT); + g_value_set_int(value, field_value); + gst_structure_set_value(structure, field, value); + g_value_unset(value); +} + +/** + * Sets the string field_value to the param "field" on the structure. + * value is used to do the operation, it must be a uninitialized (zero-filled) + * GValue, it will be left unitialized at the end of the function. + */ +void gst_sbc_util_set_structure_string_param(GstStructure *structure, + const gchar* field, const gchar* field_value, + GValue *value) +{ + value = g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, field_value); + gst_structure_set_value(structure, field, value); + g_value_unset(value); +} + +gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps) +{ + GstStructure *structure; + gint rate, channels, subbands, blocks, bitpool; + const gchar* mode; + const gchar* allocation; + + g_assert(gst_caps_is_fixed(caps)); + + structure = gst_caps_get_structure(caps, 0); + + if (!gst_structure_get_int(structure, "rate", &rate)) + return FALSE; + if (!gst_structure_get_int(structure, "channels", &channels)) + return FALSE; + if (!gst_structure_get_int(structure, "subbands", &subbands)) + return FALSE; + if (!gst_structure_get_int(structure, "blocks", &blocks)) + return FALSE; + if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + return FALSE; + + if (!(mode = gst_structure_get_string(structure, "mode"))) + return FALSE; + if (!(allocation = gst_structure_get_string(structure, "allocation"))) + return FALSE; + + sbc->rate = rate; + sbc->channels = channels; + sbc->blocks = blocks; + sbc->subbands = subbands; + sbc->bitpool = bitpool; + sbc->joint = gst_sbc_get_mode_int(mode); + sbc->allocation = gst_sbc_get_allocation_mode_int(allocation); + + return TRUE; +} + +gint gst_sbc_util_calc_frame_len(gint subbands, gint channels, + gint blocks, gint bitpool, gint channel_mode) +{ + gint len; + gint join; + len = 4 + (4 * subbands * channels)/8; + + if (channel_mode == BT_A2DP_CHANNEL_MODE_MONO || + channel_mode == BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) + len += ((blocks * channels * bitpool)+7) / 8; + else { + join = channel_mode == BT_A2DP_CHANNEL_MODE_JOINT_STEREO + ? 1 : 0; + len += ((join * subbands + blocks * bitpool)+7)/8; + } + + return len; +} + +gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands, + gint blocks) +{ + return (((frame_len * 8 * rate / subbands) / blocks) / 1000); +} + +gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands) +{ + gint64 res = 1000000; + return res * blocks * subbands / rate; +} + diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 4581abf..a67bf1b 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -49,3 +49,22 @@ const gchar *gst_sbc_get_mode_string(int joint); GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); 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); + +void gst_sbc_util_set_structure_string_param(GstStructure *structure, + const gchar* field, const gchar* field_value, + GValue *value); + +gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps); + +gint gst_sbc_util_calc_frame_len(gint subbands, gint channels, + gint blocks, gint bitpool, gint channel_mode); + +gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands, + gint blocks); + +gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands); + -- 1.5.3.7
0003-Add-missing-files.patch
(text/x-diff, 42.8 KB)
From 227dda4b0863a48826a1d42adeecebfe87134940 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Wed, 12 Dec 2007 12:08:36 -0300 Subject: [PATCH] Add missing files. --- audio/gsta2dpsendersink.c | 1013 +++++++++++++++++++++++++++++++++++++++++++++ audio/gsta2dpsendersink.h | 90 ++++ audio/gstrtpsbcpay.c | 337 +++++++++++++++ audio/gstrtpsbcpay.h | 65 +++ 4 files changed, 1505 insertions(+), 0 deletions(-) create mode 100644 audio/gsta2dpsendersink.c create mode 100644 audio/gsta2dpsendersink.h create mode 100644 audio/gstrtpsbcpay.c create mode 100644 audio/gstrtpsbcpay.h diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c new file mode 100644 index 0000000..cfb67b8 --- /dev/null +++ b/audio/gsta2dpsendersink.c @@ -0,0 +1,1013 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann <[email protected]> + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <config.h> +#endif + +#include <unistd.h> +#include <sys/un.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <pthread.h> + +#include <netinet/in.h> + +#include <bluetooth/bluetooth.h> + +#include "ipc.h" +#include "rtp.h" +#include "gstsbcutil.h" + +#include "gsta2dpsendersink.h" + +GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug); +#define GST_CAT_DEFAULT a2dp_sender_sink_debug + +#define BUFFER_SIZE 2048 +#define TEMPLATE_MAX_BITPOOL 64 + +#define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START { \ + g_mutex_lock (s->sink_lock); \ + } G_STMT_END + +#define GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ + g_mutex_unlock (s->sink_lock); \ + } G_STMT_END + + +struct bluetooth_data { + struct bt_getcapabilities_rsp caps; /* Bluetooth device caps */ + guint link_mtu; + + gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ +}; + +#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) +#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) + +enum { + PROP_0, + PROP_DEVICE, +}; + +GST_BOILERPLATE(GstA2dpSenderSink, gst_a2dp_sender_sink, GstBaseSink, + GST_TYPE_BASE_SINK); + +static const GstElementDetails a2dp_sender_sink_details = + GST_ELEMENT_DETAILS("Bluetooth A2DP sink", + "Sink/Audio", + "Plays audio to an A2DP device", + "Marcel Holtmann <[email protected]>"); + +static GstStaticPadTemplate a2dp_sender_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"SBC\"" + )); + +static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self, + const bt_audio_msg_header_t *msg); +static GIOError gst_a2dp_sender_sink_audioservice_expect( + GstA2dpSenderSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type); + + +static void gst_a2dp_sender_sink_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(&a2dp_sender_sink_factory)); + + gst_element_class_set_details(element_class, &a2dp_sender_sink_details); +} + +static gboolean gst_a2dp_sender_sink_stop(GstBaseSink *basesink) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + + GST_INFO_OBJECT(self, "stop"); + + if (self->watch_id != 0) { + g_source_remove(self->watch_id); + self->watch_id = 0; + } + + if (self->server) { + bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); + g_io_channel_unref(self->server); + self->server = NULL; + } + + if (self->stream) { + g_io_channel_flush(self->stream, NULL); + g_io_channel_close(self->stream); + g_io_channel_unref(self->stream); + self->stream = NULL; + } + + if (self->data) { + g_free(self->data); + self->data = NULL; + } + + if (self->stream_caps) { + gst_caps_unref(self->stream_caps); + self->stream_caps = NULL; + } + + if (self->dev_caps) { + gst_caps_unref(self->dev_caps); + self->dev_caps = NULL; + } + + return TRUE; +} + +static void gst_a2dp_sender_sink_finalize(GObject *object) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(object); + + if (self->data) + gst_a2dp_sender_sink_stop(GST_BASE_SINK(self)); + + if (self->device) + g_free(self->device); + + g_mutex_free(self->sink_lock); + + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void gst_a2dp_sender_sink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object); + + switch (prop_id) { + case PROP_DEVICE: + if (sink->device) + g_free(sink->device); + sink->device = g_value_dup_string(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gst_a2dp_sender_sink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string(value, sink->device); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gint gst_a2dp_sender_sink_bluetooth_recvmsg_fd(GstA2dpSenderSink *sink) +{ + int err, ret; + + ret = bt_audio_service_get_data_fd( + g_io_channel_unix_get_fd(sink->server)); + + if (ret < 0) { + err = errno; + GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", + strerror(err), err); + return -err; + } + + sink->stream = g_io_channel_unix_new(ret); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); + + return 0; +} + +static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink, + GstCaps *caps, + sbc_capabilities_t *pkt) +{ + sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; + const GValue *value = NULL; + const char *pref, *name; + gint rate, subbands, blocks; + GstStructure *structure = gst_caps_get_structure(caps, 0); + + name = gst_structure_get_name(structure); + /* FIXME only sbc supported here, should suport mp3 */ + if (!(IS_SBC(name))) { + GST_ERROR_OBJECT(sink, "Unsupported format %s", name); + return FALSE; + } + + value = gst_structure_get_value(structure, "rate"); + rate = g_value_get_int(value); + if (rate == 44100) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; + else if (rate == 48000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; + else if (rate == 32000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; + else if (rate == 16000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); + return FALSE; + } + + value = gst_structure_get_value(structure, "mode"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; + else if (strcmp(pref, "mono") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + else if (strcmp(pref, "dual") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(pref, "stereo") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + else if (strcmp(pref, "joint") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else { + GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "allocation"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; + else if (strcmp(pref, "loudness") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; + else if (strcmp(pref, "snr") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; + else { + GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "subbands"); + subbands = g_value_get_int(value); + if (subbands == 8) + cfg->subbands = BT_A2DP_SUBBANDS_8; + else if (subbands == 4) + cfg->subbands = BT_A2DP_SUBBANDS_4; + else { + GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); + return FALSE; + } + + value = gst_structure_get_value(structure, "blocks"); + blocks = g_value_get_int(value); + if (blocks == 16) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; + else if (blocks == 12) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; + else if (blocks == 8) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; + else if (blocks == 4) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; + else { + GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); + return FALSE; + } + + value = gst_structure_get_value(structure, "bitpool"); + cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); + + memcpy(pkt, cfg, sizeof(*pkt)); + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_conf_recv_stream_fd( + GstA2dpSenderSink *self) +{ + struct bluetooth_data *data = self->data; + gint ret; + GIOError err; + GError *gerr = NULL; + GIOStatus status; + GIOFlags flags; + gsize read; + + ret = gst_a2dp_sender_sink_bluetooth_recvmsg_fd(self); + if (ret < 0) + return FALSE; + + if (!self->stream) { + GST_ERROR_OBJECT(self, "Error while configuring device: " + "could not acquire audio socket"); + return FALSE; + } + + /* set stream socket to nonblock */ + GST_LOG_OBJECT(self, "setting stream socket to nonblock"); + flags = g_io_channel_get_flags(self->stream); + flags |= G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to nonblock: " + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to nonblock"); + } + + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + GST_LOG_OBJECT(self, "emptying stream pipe"); + while (1) { + err = g_io_channel_read(self->stream, data->buffer, + (gsize) data->link_mtu, + &read); + if (err != G_IO_ERROR_NONE || read <= 0) + break; + } + + /* set stream socket to block */ + GST_LOG_OBJECT(self, "setting stream socket to block"); + flags = g_io_channel_get_flags(self->stream); + flags &= ~G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to block:" + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to block"); + } + + memset(data->buffer, 0, sizeof(data->buffer)); + + return TRUE; +} + +static gboolean server_callback(GIOChannel *chan, + GIOCondition cond, gpointer data) +{ + GstA2dpSenderSink *sink; + + if (cond & G_IO_HUP || cond & G_IO_NVAL) + return FALSE; + else if (cond & G_IO_ERR) { + sink = GST_A2DP_SENDER_SINK(data); + GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); + } + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) +{ + sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; + GstStructure *structure; + GValue *value; + GValue *list; + gchar *tmp; + gboolean mono, stereo; + + GST_LOG_OBJECT(self, "updating device caps"); + + structure = gst_structure_empty_new("audio/x-sbc"); + value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); + + /* mode */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "mode", list); + g_free(list); + list = NULL; + } + + /* subbands */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + value = g_value_init(value, G_TYPE_INT); + if (sbc->subbands & BT_A2DP_SUBBANDS_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + if (sbc->subbands & BT_A2DP_SUBBANDS_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "subbands", list); + g_free(list); + list = NULL; + } + + /* blocks */ + value = g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { + g_value_set_int(value, 16); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { + g_value_set_int(value, 12); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "blocks", list); + g_free(list); + list = NULL; + } + + /* allocation */ + g_value_init(value, G_TYPE_STRING); + list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); + if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + } + if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "allocation", list); + g_free(list); + list = NULL; + } + + /* rate */ + g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* bitpool */ + value = g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, + MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), + MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); + gst_structure_set_value(structure, "bitpool", value); + g_value_unset(value); + + /* channels */ + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + mono = FALSE; + stereo = FALSE; + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); + } + } + } + gst_structure_set_value(structure, "channels", value); + g_free(value); + + if (self->dev_caps != NULL) + gst_caps_unref(self->dev_caps); + self->dev_caps = gst_caps_new_full(structure, NULL); + + tmp = gst_caps_to_string(self->dev_caps); + GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); + g_free(tmp); + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) +{ + gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_req *req = (void *) buf; + struct bt_getcapabilities_rsp *rsp = (void *) buf; + GIOError io_error; + + memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + + req->h.msg_type = BT_GETCAPABILITIES_REQ; + if (self->device == NULL) + return FALSE; + strncpy(req->device, self->device, 18); + + io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while asking device caps"); + } + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_GETCAPABILITIES_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while getting device caps"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + memcpy(&self->data->caps, rsp, sizeof(*rsp)); + if (!gst_a2dp_sender_sink_update_caps(self)) { + GST_WARNING_OBJECT(self, "failed to update capabilities"); + return FALSE; + } + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + gint sk; + gint err; + + GST_INFO_OBJECT(self, "start"); + + self->watch_id = 0; + + sk = bt_audio_service_open(); + if (sk <= 0) { + err = errno; + GST_ERROR_OBJECT(self, "Cannot open connection to bt " + "audio service: %s %d", strerror(err), err); + goto failed; + } + + self->server = g_io_channel_unix_new(sk); + self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | + G_IO_NVAL, server_callback, self); + + self->data = g_new0(struct bluetooth_data, 1); + memset(self->data, 0, sizeof(struct bluetooth_data)); + + self->stream = NULL; + self->stream_caps = NULL; + + if (!gst_a2dp_sender_sink_get_capabilities(self)) + goto failed; + + return TRUE; + +failed: + bt_audio_service_close(sk); + return FALSE; +} + +static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self) +{ + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *req = (void *) buf; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_streamfd_ind *ind = (void*) buf; + GIOError io_error; + + GST_DEBUG_OBJECT(self, "stream start"); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_STREAMSTART_REQ; + + io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "start packet"); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream start packet sent"); + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_STREAMSTART_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while stream " + "start confirmation"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream started"); + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, &ind->h, + BT_STREAMFD_IND); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving " + "stream filedescriptor"); + return FALSE; + } + + if (!gst_a2dp_sender_sink_conf_recv_stream_fd(self)) + return FALSE; + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, + GstCaps *caps) +{ + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *req = (void *) buf; + struct bt_setconfiguration_rsp *rsp = (void *) buf; + gboolean ret; + GIOError io_error; + gchar *temp; + + temp = gst_caps_to_string(caps); + GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); + g_free(temp); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_SETCONFIGURATION_REQ; + req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + strncpy(req->device, self->device, 18); + ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, + &req->sbc_capabilities); + if (!ret) { + GST_ERROR_OBJECT(self, "Couldn't parse caps " + "to packet configuration"); + return FALSE; + } + + io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "configurarion packet"); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "configuration packet sent"); + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_SETCONFIGURATION_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving device " + "confirmation"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : " + "%s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + self->data->link_mtu = rsp->link_mtu; + GST_DEBUG_OBJECT(self, "configuration set"); + + return TRUE; +} + +static GstFlowReturn gst_a2dp_sender_sink_preroll(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(basesink); + gboolean ret; + + GST_A2DP_SENDER_SINK_MUTEX_LOCK(sink); + + ret = gst_a2dp_sender_sink_stream_start(sink); + + GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(sink); + + if (!ret) + return GST_FLOW_ERROR; + + return GST_FLOW_OK; +} + +static GstFlowReturn gst_a2dp_sender_sink_render(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + gsize ret; + GIOError err; + + err = g_io_channel_write(self->stream, (gchar*)GST_BUFFER_DATA(buffer), + (gsize)(GST_BUFFER_SIZE(buffer)), &ret); + + if (err != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while writting to socket: %d %s", + errno, strerror(errno)); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + +static gboolean gst_a2dp_sender_sink_unlock(GstBaseSink *basesink) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + + if (self->stream != NULL) + g_io_channel_flush (self->stream, NULL); + + return TRUE; +} + +static GstFlowReturn gst_a2dp_sender_sink_buffer_alloc(GstBaseSink *basesink, + guint64 offset, guint size, GstCaps* caps, + GstBuffer **buf) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + + *buf = gst_buffer_new_and_alloc(size); + if (!(*buf)) { + GST_ERROR_OBJECT(self, "buffer allocation failed"); + return GST_FLOW_ERROR; + } + + gst_buffer_set_caps(*buf, caps); + + GST_BUFFER_OFFSET(*buf) = offset; + + return GST_FLOW_OK; +} + +static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + object_class->finalize = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_finalize); + object_class->set_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_get_property); + + basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_start); + basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_stop); + basesink_class->render = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_render); + basesink_class->preroll = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_preroll); + basesink_class->unlock = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_unlock); + + basesink_class->buffer_alloc = + GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc); + + g_object_class_install_property(object_class, PROP_DEVICE, + g_param_spec_string("device", "Device", + "Bluetooth remote device address", + NULL, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT(a2dp_sender_sink_debug, "a2dpsendersink", 0, + "A2DP sink element"); +} + +static void gst_a2dp_sender_sink_init(GstA2dpSenderSink *self, + GstA2dpSenderSinkClass *klass) +{ + self->device = NULL; + self->data = NULL; + + self->stream = NULL; + + self->dev_caps = NULL; + + self->sink_lock = g_mutex_new(); +} + +static GIOError gst_a2dp_sender_sink_audioservice_send( + GstA2dpSenderSink *self, + const bt_audio_msg_header_t *msg) +{ + GIOError error; + gsize written; + + error = g_io_channel_write(self->server, (const gchar*) msg, + BT_AUDIO_IPC_PACKET_SIZE, &written); + if (error != G_IO_ERROR_NONE) + GST_ERROR_OBJECT(self, "Error sending data to audio service:" + " %s(%d)", strerror(errno), errno); + + return error; +} + +static GIOError gst_a2dp_sender_sink_audioservice_recv( + GstA2dpSenderSink *self, + bt_audio_msg_header_t *inmsg) +{ + GIOError status; + gsize bytes_read; + const char *type; + + status = g_io_channel_read(self->server, (gchar*) inmsg, + BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); + if (status != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error receiving data from " + "audio service"); + return status; + } + + type = bt_audio_strmsg(inmsg->msg_type); + if (!type) { + status = G_IO_ERROR_INVAL; + GST_ERROR_OBJECT(self, "Bogus message type %d " + "received from audio service", + inmsg->msg_type); + } + + return status; +} + +static GIOError gst_a2dp_sender_sink_audioservice_expect( + GstA2dpSenderSink *self, bt_audio_msg_header_t *outmsg, + int expected_type) +{ + GIOError status; + + status = gst_a2dp_sender_sink_audioservice_recv(self, outmsg); + if (status != G_IO_ERROR_NONE) + return status; + + if (outmsg->msg_type != expected_type) + status = G_IO_ERROR_INVAL; + + return status; +} + +gboolean gst_a2dp_sender_sink_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "a2dpsendersink", + GST_RANK_NONE, GST_TYPE_A2DP_SENDER_SINK); +} + + +/* public functions */ +GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink) +{ + if (sink->dev_caps == NULL) + return NULL; + + return gst_caps_copy(sink->dev_caps); +} + +gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *self, + GstCaps *caps) +{ + gboolean ret; + + GST_DEBUG_OBJECT(self, "setting device caps"); + GST_A2DP_SENDER_SINK_MUTEX_LOCK(self); + ret = gst_a2dp_sender_sink_configure(self, caps); + + if (self->stream_caps) + gst_caps_unref(self->stream_caps); + self->stream_caps = gst_caps_ref(caps); + + GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(self); + + return ret; +} + +guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink) +{ + return sink->data->link_mtu; +} + +void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *self, const gchar* dev) +{ + if (self->device != NULL) + g_free(self->device); + + GST_LOG_OBJECT(self, "Setting device: %s", dev); + self->device = g_strdup(dev); +} + +gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self) +{ + return g_strdup(self->device); +} + diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h new file mode 100644 index 0000000..863ef6c --- /dev/null +++ b/audio/gsta2dpsendersink.h @@ -0,0 +1,90 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann <[email protected]> + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + * + */ + +#ifndef __GST_A2DP_SENDER_SINK_H +#define __GST_A2DP_SENDER_SINK_H + +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> + +G_BEGIN_DECLS + +#define GST_TYPE_A2DP_SENDER_SINK \ + (gst_a2dp_sender_sink_get_type()) +#define GST_A2DP_SENDER_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SENDER_SINK,\ + GstA2dpSenderSink)) +#define GST_A2DP_SENDER_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SENDER_SINK,\ + GstA2dpSenderSinkClass)) +#define GST_IS_A2DP_SENDER_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SENDER_SINK)) +#define GST_IS_A2DP_SENDER_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SENDER_SINK)) + +typedef struct _GstA2dpSenderSink GstA2dpSenderSink; +typedef struct _GstA2dpSenderSinkClass GstA2dpSenderSinkClass; + +struct bluetooth_data; + +struct _GstA2dpSenderSink { + GstBaseSink sink; + + gchar *device; + GIOChannel *stream; + + struct bluetooth_data *data; + GIOChannel *server; + + /* stream connection data */ + GstCaps *stream_caps; + + GstCaps *dev_caps; + + GMutex *sink_lock; + + guint watch_id; +}; + +struct _GstA2dpSenderSinkClass { + GstBaseSinkClass parent_class; +}; + +GType gst_a2dp_sender_sink_get_type(void); + +GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink); +gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *sink, + GstCaps *caps); + +guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink); + +void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *sink, + const gchar* device); + +gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink); + +gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_A2DP_SENDER_SINK_H */ diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c new file mode 100644 index 0000000..68aa28a --- /dev/null +++ b/audio/gstrtpsbcpay.c @@ -0,0 +1,337 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann <[email protected]> + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <config.h> +#endif + +#include "gstrtpsbcpay.h" +#include <math.h> +#include <string.h> + +#define RTP_SBC_PAYLOAD_HEADER_SIZE 1 +#define DEFAULT_MIN_FRAMES 0 +#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE) + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_payload { + guint8 frame_count:4; + guint8 rfa0:1; + guint8 is_last_fragment:1; + guint8 is_first_fragment:1; + guint8 is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_payload { + guint8 is_fragmented:1; + guint8 is_first_fragment:1; + guint8 is_last_fragment:1; + guint8 rfa0:1; + guint8 frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +enum { + PROP_0, + PROP_MIN_FRAMES +}; + +GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug); +#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug + +GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload, + GST_TYPE_BASE_RTP_PAYLOAD); + +static const GstElementDetails gst_rtp_sbc_pay_details = + GST_ELEMENT_DETAILS("RTP packet payloader", + "Codec/Payloader/Network", + "Payload SBC audio as RTP packets", + "Thiago Sousa Santos " + "<[email protected]>"); + +static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-sbc, " /* FIXME remove those caps? */ + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation = (string) { snr, loudness }," + "bitpool = (int) [ 2, 64 ]; ") + ); + +static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory = + GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("application/x-rtp") /* FIXME put things here */ + ); + +static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels, + gint blocks, gint bitpool, const gchar* channel_mode) +{ + gint len; + gint join; + + len = 4 + (4 * subbands * channels)/8; + + if (strcmp(channel_mode, "mono") == 0 || + strcmp(channel_mode, "dual") == 0) + len += ((blocks * channels * bitpool)+7) / 8; + else { + join = strcmp(channel_mode, "joint") == 0 ? 1 : 0; + len += ((join * subbands + blocks * bitpool)+7)/8; + } + + return len; +} + +static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload, + GstCaps *caps) +{ + GstRtpSBCPay *sbcpay; + gint rate, subbands, channels, blocks, bitpool; + gint frame_len; + const gchar* channel_mode; + GstStructure *structure; + + sbcpay = GST_RTP_SBC_PAY(payload); + + structure = gst_caps_get_structure(caps, 0); + if (!gst_structure_get_int(structure, "rate", &rate)) + return FALSE; + if (!gst_structure_get_int(structure, "channels", &channels)) + return FALSE; + if (!gst_structure_get_int(structure, "blocks", &blocks)) + return FALSE; + if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + return FALSE; + if (!gst_structure_get_int(structure, "subbands", &subbands)) + return FALSE; + + channel_mode = gst_structure_get_string(structure, "mode"); + if (!channel_mode) + return FALSE; + + frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks, + bitpool, channel_mode); + + sbcpay->frame_length = frame_len; + + gst_basertppayload_set_options (payload, "audio", FALSE, "SBC", rate); + + GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len); + + return gst_basertppayload_set_outcaps (payload, NULL); +} + +static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) +{ + guint available; + guint max_payload; + GstBuffer* outbuf; + guint8 *payload_data; + guint8 *data; + struct rtp_payload *payload; + + if (sbcpay->frame_length == 0) { + GST_ERROR_OBJECT(sbcpay, "Frame length is 0"); + return GST_FLOW_ERROR; + } + + available = gst_adapter_available(sbcpay->adapter); + + max_payload = gst_rtp_buffer_calc_payload_len( + GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE, + 0, 0); + + max_payload = MIN(max_payload, available); + + outbuf = gst_rtp_buffer_new_allocate(max_payload + + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); + + gst_rtp_buffer_set_payload_type(outbuf, + GST_BASE_RTP_PAYLOAD_PT(sbcpay)); + + data = gst_adapter_take(sbcpay->adapter, max_payload); + payload_data = gst_rtp_buffer_get_payload(outbuf); + + payload = (struct rtp_payload*) payload_data; + memset(payload, 0, sizeof(struct rtp_payload)); + payload->frame_count = max_payload / sbcpay->frame_length; + + memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, max_payload); + g_free(data); + + /* FIXME - timestamp it! */ + GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", max_payload); + + return gst_basertppayload_push (GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); +} + +static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, + GstBuffer *buffer) +{ + GstRtpSBCPay *sbcpay; + guint available; + + sbcpay = GST_RTP_SBC_PAY(payload); + gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer)); + + available = gst_adapter_available(sbcpay->adapter); + if (available + RTP_SBC_HEADER_TOTAL >= + GST_BASE_RTP_PAYLOAD_MTU(sbcpay) || + (sbcpay->min_frames != -1 && available > + (sbcpay->min_frames * sbcpay->frame_length))) + return gst_rtp_sbc_pay_flush_buffers(sbcpay); + + return GST_FLOW_OK; +} + +static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad, + GstEvent *event) +{ + GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad)); + + switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_EOS: + gst_rtp_sbc_pay_flush_buffers(sbcpay); + break; + default: + break; + } + + return FALSE; +} + +static void gst_rtp_sbc_pay_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(&gst_rtp_sbc_pay_sink_factory)); + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory)); + + gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details); +} + +static void gst_rtp_sbc_pay_finalize(GObject *object) +{ + GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object); + g_object_unref (sbcpay->adapter); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass) +{ + GObjectClass *gobject_class; + GstBaseRTPPayloadClass *payload_class = + GST_BASE_RTP_PAYLOAD_CLASS(klass); + + gobject_class = G_OBJECT_CLASS(klass); + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize); + gobject_class->set_property = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_get_property); + + payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps); + payload_class->handle_buffer = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_handle_buffer); + payload_class->handle_event = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_handle_event); + + /* properties */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_MIN_FRAMES, + g_param_spec_int ("min-frames", "minimum frame number", + "Minimum quantity of frames to send in one packet " + "(-1 for maximum allowed by the mtu)", + -1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0, + "RTP SBC payloader"); +} + +static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpSBCPay *sbcpay; + + sbcpay = GST_RTP_SBC_PAY (object); + + switch (prop_id) { + case PROP_MIN_FRAMES: + sbcpay->min_frames = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpSBCPay *sbcpay; + + sbcpay = GST_RTP_SBC_PAY (object); + + switch (prop_id) { + case PROP_MIN_FRAMES: + g_value_set_int(value, sbcpay->min_frames); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass) +{ + self->adapter = gst_adapter_new(); + self->frame_length = 0; + + self->min_frames = DEFAULT_MIN_FRAMES; +} + +gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpsbcpay", + GST_RANK_NONE, GST_TYPE_RTP_SBC_PAY); +} + diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h new file mode 100644 index 0000000..f086a1c --- /dev/null +++ b/audio/gstrtpsbcpay.h @@ -0,0 +1,65 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann <[email protected]> + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + * + */ + +#include <gst/gst.h> +#include <gst/rtp/gstbasertppayload.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbuffer.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_SBC_PAY \ + (gst_rtp_sbc_pay_get_type()) +#define GST_RTP_SBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\ + GstRtpSBCPay)) +#define GST_RTP_SBC_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\ + GstRtpSBCPayClass)) +#define GST_IS_RTP_SBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY)) +#define GST_IS_RTP_SBC_PAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY)) + +typedef struct _GstRtpSBCPay GstRtpSBCPay; +typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass; + +struct _GstRtpSBCPay { + GstBaseRTPPayload base; + + GstAdapter *adapter; + + guint frame_length; + + guint min_frames; +}; + +struct _GstRtpSBCPayClass { + GstBaseRTPPayloadClass parent_class; +}; + +GType gst_rtp_sbc_pay_get_type(void); + +gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS -- 1.5.3.7
0004-Fix-bitrate-in-mpeg12-to-be-2-bytes-long.patch
(text/x-diff, 1.8 KB)
From 26155d22dac47ae46ce75b073ae4c9a4e4d4a54c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Wed, 12 Dec 2007 18:40:00 -0300 Subject: [PATCH] Fix bitrate in mpeg12 to be 2 bytes long. --- audio/a2dp.h | 8 ++------ audio/ipc.h | 1 - audio/unix.c | 4 +--- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/audio/a2dp.h b/audio/a2dp.h index cfd1c47..5508c96 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -83,9 +83,7 @@ struct mpeg_codec_cap { uint8_t frequency:6; uint8_t mpf:1; uint8_t rfa:1; - uint8_t bitrate0:7; - uint8_t vbr:1; - uint8_t bitrate1; + uint16_t bitrate; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN @@ -109,9 +107,7 @@ struct mpeg_codec_cap { uint8_t rfa:1; uint8_t mpf:1; uint8_t frequency:6; - uint8_t vbr:1; - uint8_t bitrate0:7; - uint8_t bitrate1; + uint16_t bitrate; } __attribute__ ((packed)); #else diff --git a/audio/ipc.h b/audio/ipc.h index 64ed65a..1f80a47 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -168,7 +168,6 @@ typedef struct { uint8_t layer; uint8_t frequency; uint8_t mpf; - uint8_t vbr; uint16_t bitrate; } __attribute__ ((packed)) mpeg_capabilities_t; diff --git a/audio/unix.c b/audio/unix.c index 2226579..d12699e 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -389,9 +389,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, rsp->mpeg_capabilities.layer = mpeg_cap->layer; rsp->mpeg_capabilities.frequency = mpeg_cap->frequency; rsp->mpeg_capabilities.mpf = mpeg_cap->mpf; - rsp->mpeg_capabilities.vbr = mpeg_cap->vbr; - rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate1 & - mpeg_cap->bitrate0 << 8; + rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate; } unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); -- 1.5.3.7
0005-Add-defines-for-mpeg12-codec.patch
(text/x-diff, 8.4 KB)
From 010162374c3fc9a1962e88fa82f046e9f71c1002 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Thu, 13 Dec 2007 11:33:20 -0300 Subject: [PATCH] Add defines for mpeg12 codec. --- audio/gsta2dpsendersink.c | 37 ++++++++++++++++++++++++------------- audio/ipc.h | 19 +++++++++++++++---- audio/pcm_bluetooth.c | 39 ++++++++++++++++++++++----------------- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c index cfb67b8..1f54f29 100644 --- a/audio/gsta2dpsendersink.c +++ b/audio/gsta2dpsendersink.c @@ -236,13 +236,13 @@ static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink, value = gst_structure_get_value(structure, "rate"); rate = g_value_get_int(value); if (rate == 44100) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; + cfg->frequency = BT_SBC_SAMPLING_FREQ_44100; else if (rate == 48000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; + cfg->frequency = BT_SBC_SAMPLING_FREQ_48000; else if (rate == 32000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; + cfg->frequency = BT_SBC_SAMPLING_FREQ_32000; else if (rate == 16000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; + cfg->frequency = BT_SBC_SAMPLING_FREQ_16000; else { GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); return FALSE; @@ -396,17 +396,14 @@ static gboolean server_callback(GIOChannel *chan, return TRUE; } -static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) +static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps( + GstA2dpSenderSink *self, sbc_capabilities_t *sbc) { - sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; GstStructure *structure; GValue *value; GValue *list; - gchar *tmp; gboolean mono, stereo; - GST_LOG_OBJECT(self, "updating device caps"); - structure = gst_structure_empty_new("audio/x-sbc"); value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); @@ -518,19 +515,19 @@ static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) /* rate */ g_value_init(value, G_TYPE_INT); list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) { g_value_set_int(value, 48000); gst_value_list_prepend_value(list, value); } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) { g_value_set_int(value, 44100); gst_value_list_prepend_value(list, value); } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) { g_value_set_int(value, 32000); gst_value_list_prepend_value(list, value); } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) { g_value_set_int(value, 16000); gst_value_list_prepend_value(list, value); } @@ -584,6 +581,20 @@ static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) gst_structure_set_value(structure, "channels", value); g_free(value); + return structure; +} + +static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) +{ + sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; + mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities; + GstStructure *structure; + gchar *tmp; + + GST_LOG_OBJECT(self, "updating device caps"); + + structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); + if (self->dev_caps != NULL) gst_caps_unref(self->dev_caps); self->dev_caps = gst_caps_new_full(structure, NULL); diff --git a/audio/ipc.h b/audio/ipc.h index 1f80a47..3768dcf 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -129,10 +129,10 @@ struct bt_getcapabilities_req { * SBC Codec parameters as per A2DP profile 1.0 § 4.3 */ -#define BT_A2DP_SAMPLING_FREQ_16000 (1 << 3) -#define BT_A2DP_SAMPLING_FREQ_32000 (1 << 2) -#define BT_A2DP_SAMPLING_FREQ_44100 (1 << 1) -#define BT_A2DP_SAMPLING_FREQ_48000 1 +#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3) +#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2) +#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1) +#define BT_SBC_SAMPLING_FREQ_48000 1 #define BT_A2DP_CHANNEL_MODE_MONO (1 << 3) #define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) @@ -152,6 +152,17 @@ struct bt_getcapabilities_req { #define BT_A2DP_ALLOCATION_LOUDNESS 1 #define BT_A2DP_ALLOCATION_AUTO 0 +#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5) +#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4) +#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3) +#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2) +#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1) +#define BT_MPEG_SAMPLING_FREQ_48000 1 + +#define BT_MPEG_LAYER_1 (1 << 2) +#define BT_MPEG_LAYER_2 (1 << 1) +#define BT_MPEG_LAYER_3 1 + typedef struct { uint8_t channel_mode; uint8_t frequency; diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b0e3838..d423460 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -468,10 +468,10 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { switch (freq) { - case BT_A2DP_SAMPLING_FREQ_16000: - case BT_A2DP_SAMPLING_FREQ_32000: + case BT_SBC_SAMPLING_FREQ_16000: + case BT_SBC_SAMPLING_FREQ_32000: return 53; - case BT_A2DP_SAMPLING_FREQ_44100: + case BT_SBC_SAMPLING_FREQ_44100: switch (mode) { case BT_A2DP_CHANNEL_MODE_MONO: case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: @@ -483,7 +483,7 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode) DBG("Invalid channel mode %u", mode); return 53; } - case BT_A2DP_SAMPLING_FREQ_48000: + case BT_SBC_SAMPLING_FREQ_48000: switch (mode) { case BT_A2DP_CHANNEL_MODE_MONO: case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: @@ -508,16 +508,16 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, switch (rate) { case 48000: - cap->frequency = BT_A2DP_SAMPLING_FREQ_48000; + cap->frequency = BT_SBC_SAMPLING_FREQ_48000; break; case 44100: - cap->frequency = BT_A2DP_SAMPLING_FREQ_44100; + cap->frequency = BT_SBC_SAMPLING_FREQ_44100; break; case 32000: - cap->frequency = BT_A2DP_SAMPLING_FREQ_32000; + cap->frequency = BT_SBC_SAMPLING_FREQ_32000; break; case 16000: - cap->frequency = BT_A2DP_SAMPLING_FREQ_16000; + cap->frequency = BT_SBC_SAMPLING_FREQ_16000; break; default: DBG("Rate %d not supported", rate); @@ -641,16 +641,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, sbc_init(&a2dp->sbc, 0); a2dp->sbc_initialized = 1; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000) a2dp->sbc.rate = 16000; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000) a2dp->sbc.rate = 32000; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100) a2dp->sbc.rate = 44100; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000) a2dp->sbc.rate = 48000; if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) @@ -659,7 +659,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, a2dp->sbc.channels = 2; if (active_capabilities.channel_mode & - (BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + (BT_A2DP_CHANNEL_MODE_MONO || + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) a2dp->sbc.joint = 1; else a2dp->sbc.joint = 0; @@ -1231,22 +1232,26 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) /* supported rates */ rate_count = 0; - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_16000) { rate_list[rate_count] = 16000; rate_count++; } - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_32000) { rate_list[rate_count] = 32000; rate_count++; } - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_44100) { rate_list[rate_count] = 44100; rate_count++; } - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_48000) { rate_list[rate_count] = 48000; rate_count++; } -- 1.5.3.7
0006-Add-mpeg12-sep-registration.patch
(text/x-diff, 10.7 KB)
From 4f9b1d51c6f6973c2deaf0bf50d1e84ec4c439d6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Thu, 13 Dec 2007 20:01:29 -0300 Subject: [PATCH] Add mpeg12 sep registration. --- audio/a2dp.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- audio/a2dp.h | 5 ++ audio/avdtp.c | 2 + audio/avdtp.h | 1 + audio/unix.c | 45 +++++++++++++------ 5 files changed, 168 insertions(+), 26 deletions(-) diff --git a/audio/a2dp.c b/audio/a2dp.c index 1c59374..ec74300 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -58,6 +58,7 @@ struct a2dp_sep { uint8_t type; + uint8_t codec; struct avdtp_local_sep *sep; struct avdtp *session; struct avdtp_stream *stream; @@ -280,7 +281,7 @@ static void stream_state_changed(struct avdtp_stream *stream, } -static gboolean setconf_ind(struct avdtp *session, +static gboolean sbc_setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, @@ -331,7 +332,7 @@ static gboolean setconf_ind(struct avdtp *session, return TRUE; } -static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, +static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; @@ -386,6 +387,84 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, return TRUE; } +static gboolean mpeg_setconf_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + GSList *caps, uint8_t *err, + uint8_t *category, void *user_data) +{ + struct a2dp_sep *a2dp_sep = user_data; + struct device *dev; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) + debug("SBC Sink: Set_Configuration_Ind"); + else + debug("SBC Source: Set_Configuration_Ind"); + + dev = a2dp_get_dev(session); + if (!dev) { + *err = AVDTP_UNSUPPORTED_CONFIGURATION; + *category = 0x00; + return FALSE; + } + + avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); + a2dp_sep->stream = stream; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) + sink_new_stream(dev, session, stream); + + return TRUE; +} + +static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, void *user_data) +{ + struct a2dp_sep *a2dp_sep = user_data; + struct avdtp_service_capability *media_transport, *media_codec; + struct mpeg_codec_cap mpeg_cap; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) + debug("SBC Sink: Get_Capability_Ind"); + else + debug("SBC Source: Get_Capability_Ind"); + + *caps = NULL; + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + memset(&mpeg_cap, 0, sizeof(struct mpeg_codec_cap)); + + mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12; + + mpeg_cap.frequency = ( MPEG_SAMPLING_FREQ_48000 | + MPEG_SAMPLING_FREQ_44100 | + MPEG_SAMPLING_FREQ_32000 | + MPEG_SAMPLING_FREQ_24000 | + MPEG_SAMPLING_FREQ_22050 | + MPEG_SAMPLING_FREQ_16000 ); + + mpeg_cap.channel_mode = ( MPEG_CHANNEL_MODE_JOINT_STEREO | + MPEG_CHANNEL_MODE_STEREO | + MPEG_CHANNEL_MODE_DUAL_CHANNEL | + MPEG_CHANNEL_MODE_MONO ); + + mpeg_cap.layer = ( MPEG_LAYER_MP3 | MPEG_LAYER_MP2 | MPEG_LAYER_MP1 ); + + mpeg_cap.bitrate = 0xFFFF; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, + sizeof(mpeg_cap)); + + *caps = g_slist_append(*caps, media_codec); + + return TRUE; +} + static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) @@ -797,9 +876,21 @@ static struct avdtp_sep_cfm cfm = { .reconfigure = reconf_cfm }; -static struct avdtp_sep_ind ind = { - .get_capability = getcap_ind, - .set_configuration = setconf_ind, +static struct avdtp_sep_ind sbc_ind = { + .get_capability = sbc_getcap_ind, + .set_configuration = sbc_setconf_ind, + .get_configuration = getconf_ind, + .open = open_ind, + .start = start_ind, + .suspend = suspend_ind, + .close = close_ind, + .abort = abort_ind, + .reconfigure = reconf_ind +}; + +static struct avdtp_sep_ind mpeg_ind = { + .get_capability = mpeg_getcap_ind, + .set_configuration = mpeg_setconf_ind, .get_configuration = getconf_ind, .open = open_ind, .start = start_ind, @@ -880,23 +971,27 @@ static int a2dp_sink_record(sdp_buf_t *buf) return 0; } -static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type) +static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, + uint8_t codec) { struct a2dp_sep *sep; GSList **l; int (*create_record)(sdp_buf_t *buf); uint32_t *record_id; sdp_buf_t buf; + struct avdtp_sep_ind *ind; sep = g_new0(struct a2dp_sep, 1); - sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm, sep); + ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind; + sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, codec, + ind, &cfm, sep); if (sep->sep == NULL) { g_free(sep); return NULL; } + sep->codec = codec; sep->type = type; if (type == AVDTP_SEP_TYPE_SOURCE) { @@ -945,11 +1040,15 @@ int a2dp_init(DBusConnection *conn, int sources, int sinks) avdtp_init(); - for (i = 0; i < sources; i++) - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); + for (i = 0; i < sources; i++) { + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_SBC); + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_MPEG12); + } - for (i = 0; i < sinks; i++) - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK); + for (i = 0; i < sinks; i++) { + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_SBC); + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_MPEG12); + } return 0; } @@ -1025,14 +1124,32 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, struct a2dp_sep *sep = NULL; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap = NULL; int posix_err; + for (l = caps; l != NULL; l = l->next) { + cap = l->data; + + if (cap->category != AVDTP_MEDIA_CODEC) + continue; + + codec_cap = (void *) cap->data; + break; + } + + if (!codec_cap) + return 0; + for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; if (tmp->locked) continue; + if (tmp->codec != codec_cap->media_codec_type) + continue; + if (!tmp->stream || avdtp_has_stream(session, tmp->stream)) { sep = tmp; break; diff --git a/audio/a2dp.h b/audio/a2dp.h index 5508c96..84301ba 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -48,6 +48,11 @@ #define SBC_ALLOCATION_SNR (1 << 1) #define SBC_ALLOCATION_LOUDNESS 1 +#define MPEG_CHANNEL_MODE_MONO (1 << 3) +#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define MPEG_CHANNEL_MODE_STEREO (1 << 1) +#define MPEG_CHANNEL_MODE_JOINT_STEREO 1 + #define MPEG_LAYER_MP1 (1 << 2) #define MPEG_LAYER_MP2 (1 << 1) #define MPEG_LAYER_MP3 1 diff --git a/audio/avdtp.c b/audio/avdtp.c index d543345..97868a0 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2696,6 +2696,7 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) } struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data) @@ -2711,6 +2712,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, sep->info.seid = free_seid++; sep->info.type = type; sep->info.media_type = media_type; + sep->codec = codec_type; sep->ind = ind; sep->cfm = cfm; sep->user_data = user_data; diff --git a/audio/avdtp.h b/audio/avdtp.h index 241fa71..01ca50c 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -242,6 +242,7 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream); int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data); diff --git a/audio/unix.c b/audio/unix.c index d12699e..f4d29d2 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -792,6 +792,7 @@ static void handle_setconfiguration_req(struct unix_client *client, { struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap; + struct mpeg_codec_cap mpeg_cap; struct device *dev; bdaddr_t bdaddr; int err = 0; @@ -840,20 +841,36 @@ static void handle_setconfiguration_req(struct unix_client *client, client->caps = g_slist_append(client->caps, media_transport); - memset(&sbc_cap, 0, sizeof(sbc_cap)); - - sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; - sbc_cap.channel_mode = req->sbc_capabilities.channel_mode; - sbc_cap.frequency = req->sbc_capabilities.frequency; - sbc_cap.allocation_method = req->sbc_capabilities.allocation_method; - sbc_cap.subbands = req->sbc_capabilities.subbands; - sbc_cap.block_length = req->sbc_capabilities.block_length ; - sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool; - sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool; - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); + if (req->mpeg_capabilities.frequency) { + memset(&mpeg_cap, 0, sizeof(mpeg_cap)); + + mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12; + mpeg_cap.channel_mode = req->mpeg_capabilities.channel_mode; + mpeg_cap.crc = req->mpeg_capabilities.crc; + mpeg_cap.layer = req->mpeg_capabilities.layer; + mpeg_cap.frequency = req->mpeg_capabilities.frequency; + mpeg_cap.mpf = req->mpeg_capabilities.mpf; + mpeg_cap.bitrate = req->mpeg_capabilities.bitrate; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, + sizeof(mpeg_cap)); + } else { + memset(&sbc_cap, 0, sizeof(sbc_cap)); + + sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; + sbc_cap.channel_mode = req->sbc_capabilities.channel_mode; + sbc_cap.frequency = req->sbc_capabilities.frequency; + sbc_cap.allocation_method = req->sbc_capabilities.allocation_method; + sbc_cap.subbands = req->sbc_capabilities.subbands; + sbc_cap.block_length = req->sbc_capabilities.block_length; + sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool; + sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + } client->caps = g_slist_append(client->caps, media_codec); -- 1.5.3.7
0007-Fix-bug-selecting-SEPs.patch
(text/x-diff, 1.9 KB)
From 5e0f114b83fd6e0434a25fb141dd795ac9d2712e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Fri, 21 Dec 2007 16:08:21 -0300 Subject: [PATCH] Fix bug selecting SEPs. --- audio/a2dp.c | 3 ++- audio/unix.c | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletions(-) diff --git a/audio/a2dp.c b/audio/a2dp.c index ec74300..b1409f5 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1184,7 +1184,8 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, - AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, + codec_cap->media_type, + codec_cap->media_codec_type, &lsep, &rsep) < 0) { error("No matching ACP and INT SEPs found"); goto failed; diff --git a/audio/unix.c b/audio/unix.c index f4d29d2..d71c420 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -855,6 +855,12 @@ static void handle_setconfiguration_req(struct unix_client *client, media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, sizeof(mpeg_cap)); + + info("config mpeg - frequency = %u channel_mode = %u " + "layer = %u crc = %u mpf = %u bitrate = %u", + mpeg_cap.frequency, mpeg_cap.channel_mode, + mpeg_cap.layer, mpeg_cap.crc, mpeg_cap.mpf, + mpeg_cap.bitrate); } else { memset(&sbc_cap, 0, sizeof(sbc_cap)); @@ -870,6 +876,13 @@ static void handle_setconfiguration_req(struct unix_client *client, media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, sizeof(sbc_cap)); + + info("config sbc - frequency = %u channel_mode = %u " + "allocation = %u subbands = %u blocks = %u " + "bitpool = %u", sbc_cap.frequency, + sbc_cap.channel_mode, sbc_cap.allocation_method, + sbc_cap.subbands, sbc_cap.block_length, + sbc_cap.max_bitpool); } client->caps = g_slist_append(client->caps, media_codec); -- 1.5.3.7
0008-Add-mp3-support-for-gstreamer-plugin.patch
(text/x-diff, 20.8 KB)
From 624e9758f56cfe1c7305bcc318954ae400aa2375 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Wed, 16 Jan 2008 14:00:34 -0300 Subject: [PATCH] Add mp3 support for gstreamer plugin. --- audio/gsta2dpsendersink.c | 336 ++++++++++++++++++++++++++++++++++++++++++++- audio/gsta2dpsendersink.h | 11 ++ audio/gsta2dpsink.c | 105 ++++++++++++++- audio/gsta2dpsink.h | 4 + audio/gstsbcutil.c | 18 ++- 5 files changed, 461 insertions(+), 13 deletions(-) diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c index 1f54f29..d0d8d59 100644 --- a/audio/gsta2dpsendersink.c +++ b/audio/gsta2dpsendersink.c @@ -35,6 +35,8 @@ #include <bluetooth/bluetooth.h> +#include <gst/rtp/gstrtpbuffer.h> + #include "ipc.h" #include "rtp.h" #include "gstsbcutil.h" @@ -46,6 +48,8 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug); #define BUFFER_SIZE 2048 #define TEMPLATE_MAX_BITPOOL 64 +#define CRC_PROTECTED 1 +#define CRC_UNPROTECTED 0 #define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START { \ g_mutex_lock (s->sink_lock); \ @@ -84,7 +88,15 @@ static GstStaticPadTemplate a2dp_sender_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("application/x-rtp, " "media = (string) \"audio\", " - "encoding-name = (string) \"SBC\"" + "encoding-name = (string) \"SBC\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", " + "clock-rate = (int) 90000; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"" )); static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self, @@ -584,20 +596,145 @@ static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps( return structure; } +static GstStructure *gst_a2dp_sender_sink_parse_mpeg_caps( + GstA2dpSenderSink *self, mpeg_capabilities_t *mpeg) +{ + GstStructure *structure; + GValue *value; + GValue *list; + gboolean valid_layer = FALSE; + gboolean mono, stereo; + + GST_LOG_OBJECT(self, "parsing mpeg caps"); + + structure = gst_structure_empty_new("audio/mpeg"); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_INT); + + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + g_value_set_int(value, 1); + gst_value_list_prepend_value(list, value); + g_value_set_int(value, 2); + gst_value_list_prepend_value(list, value); + gst_structure_set_value(structure, "mpegversion", list); + g_free(list); + + /* layer */ + GST_LOG_OBJECT(self, "setting mpeg layer"); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (mpeg->layer & BT_MPEG_LAYER_1) { + g_value_set_int(value, 1); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (mpeg->layer & BT_MPEG_LAYER_2) { + g_value_set_int(value, 2); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (mpeg->layer & BT_MPEG_LAYER_3) { + g_value_set_int(value, 3); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (list) { + gst_structure_set_value(structure, "layer", list); + g_free(list); + list = NULL; + } + + if (!valid_layer) { + gst_structure_free(structure); + g_free(value); + return NULL; + } + + /* rate */ + GST_LOG_OBJECT(self, "setting mpeg rate"); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) { + g_value_set_int(value, 24000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) { + g_value_set_int(value, 22050); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* channels */ + GST_LOG_OBJECT(self, "setting mpeg channels"); + mono = FALSE; + stereo = FALSE; + if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (mpeg->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (mpeg->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); + } + } + gst_structure_set_value(structure, "channels", value); + g_free(value); + + return structure; +} + static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) { sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities; - GstStructure *structure; + GstStructure *sbc_structure; + GstStructure *mpeg_structure; gchar *tmp; GST_LOG_OBJECT(self, "updating device caps"); - structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); + sbc_structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); + mpeg_structure = gst_a2dp_sender_sink_parse_mpeg_caps(self, mpeg); if (self->dev_caps != NULL) gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_new_full(structure, NULL); + self->dev_caps = gst_caps_new_full(sbc_structure, NULL); + if (mpeg_structure != NULL) + gst_caps_append_structure(self->dev_caps, mpeg_structure); tmp = gst_caps_to_string(self->dev_caps); GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); @@ -623,6 +760,7 @@ static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(self, "Error while asking device caps"); + return FALSE; } io_error = gst_a2dp_sender_sink_audioservice_expect(self, @@ -648,6 +786,72 @@ static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) return TRUE; } +static gint gst_a2dp_sender_sink_get_channel_mode(const gchar *mode) +{ + if (strcmp(mode, "stereo") == 0) + return BT_A2DP_CHANNEL_MODE_STEREO; + else if (strcmp(mode, "joint") == 0) + return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (strcmp(mode, "dual") == 0) + return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(mode, "mono") == 0) + return BT_A2DP_CHANNEL_MODE_MONO; + else + return -1; +} + +static void gst_a2dp_sender_sink_tag(const GstTagList *taglist, + const gchar* tag, gpointer user_data) +{ + gboolean crc; + gchar *channel_mode = NULL; + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(user_data); + + if (strcmp(tag, "has-crc") == 0) { + + if (!gst_tag_list_get_boolean(taglist, tag, &crc)) { + GST_WARNING_OBJECT(self, "failed to get crc tag"); + self->mpeg_stream_changed = TRUE; + } + + gst_a2dp_sender_sink_set_crc(self, crc); + + } else if (strcmp(tag, "channel-mode") == 0) { + + if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { + GST_WARNING_OBJECT(self, "failed to get channel-mode tag"); + self->mpeg_stream_changed = TRUE; + } + + self->channel_mode = gst_a2dp_sender_sink_get_channel_mode( + channel_mode); + if (self->channel_mode == -1) + GST_WARNING_OBJECT(self, "Received invalid channel " + "mode: %s", channel_mode); + g_free(channel_mode); + + } else + GST_DEBUG_OBJECT(self, "received unused tag: %s", tag); +} + +static gboolean gst_a2dp_sender_sink_event(GstBaseSink *basesink, + GstEvent *event) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + GstTagList *taglist = NULL; + + if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { + /* we check the tags, mp3 has tags that are importants and + * are outside caps */ + gst_event_parse_tag(event, &taglist); + gst_tag_list_foreach(taglist, gst_a2dp_sender_sink_tag, self); + /*g_free(taglist); FIXME this is causing segfault*/ + return TRUE; + } + + return FALSE; +} + static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) { GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); @@ -675,6 +879,9 @@ static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) self->stream = NULL; self->stream_caps = NULL; + self->mp3_using_crc = -1; + self->channel_mode = -1; + self->mpeg_stream_changed = FALSE; if (!gst_a2dp_sender_sink_get_capabilities(self)) goto failed; @@ -739,6 +946,75 @@ static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self) return TRUE; } +static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf( + GstA2dpSenderSink *self, GstCaps *caps, + mpeg_capabilities_t *pkt) +{ + const GValue *value = NULL; + gint rate, layer; + GstStructure *structure = gst_caps_get_structure(caps, 0); + + /* layer */ + value = gst_structure_get_value(structure, "layer"); + layer = g_value_get_int(value); + if (layer == 1) + pkt->layer = BT_MPEG_LAYER_1; + else if (layer == 2) + pkt->layer = BT_MPEG_LAYER_2; + else if (layer == 3) + pkt->layer = BT_MPEG_LAYER_3; + else { + GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer); + return FALSE; + } + + /* crc */ + if (self->mp3_using_crc != -1) + pkt->crc = self->mp3_using_crc; + else { + GST_ERROR_OBJECT(self, "No info about crc was received, can't proceed"); + return FALSE; + } + + /* channel mode */ + if (self->channel_mode != -1) + pkt->channel_mode = self->channel_mode; + else { + GST_ERROR_OBJECT(self, "No info about channel mode received, can't proceed"); + return FALSE; + } + + /* mpf - we will only use the mandatory one */ + pkt->mpf = 0; + + value = gst_structure_get_value(structure, "rate"); + rate = g_value_get_int(value); + if (rate == 44100) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100; + else if (rate == 48000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000; + else if (rate == 32000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000; + else if (rate == 24000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000; + else if (rate == 22050) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050; + else if (rate == 16000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(self, "Invalid rate while setting caps"); + return FALSE; + } + + /* vbr - we always say its vbr, we don't have how to know it */ + pkt->bitrate = 0x8000; + + /* bitrate - we don't set anything, its vbr */ + /* FIXME - is this right? */ + + return TRUE; +} + static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, GstCaps *caps) { @@ -748,6 +1024,7 @@ static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, gboolean ret; GIOError io_error; gchar *temp; + GstStructure *structure; temp = gst_caps_to_string(caps); GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); @@ -757,8 +1034,17 @@ static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, req->h.msg_type = BT_SETCONFIGURATION_REQ; req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; strncpy(req->device, self->device, 18); - ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, - &req->sbc_capabilities); + structure = gst_caps_get_structure(caps, 0); + + if (gst_structure_has_name(structure, "audio/x-sbc")) + ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, + &req->sbc_capabilities); + else if (gst_structure_has_name(structure, "audio/mpeg")) + ret = gst_a2dp_sender_sink_init_mp3_pkt_conf(self, caps, + &req->mpeg_capabilities); + else + ret = FALSE; + if (!ret) { GST_ERROR_OBJECT(self, "Couldn't parse caps " "to packet configuration"); @@ -884,6 +1170,8 @@ static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass) gst_a2dp_sender_sink_preroll); basesink_class->unlock = GST_DEBUG_FUNCPTR( gst_a2dp_sender_sink_unlock); + basesink_class->event = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_event); basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc); @@ -1022,3 +1310,39 @@ gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self) return g_strdup(self->device); } +void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc) +{ + gint new_crc; + + new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED; + + /* test if we already received a different crc */ + if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) { + GST_ERROR_OBJECT(self, "crc changed during stream"); + /* FIXME test this, its not being used anywhere */ + self->mpeg_stream_changed = TRUE; + return; + } + self->mp3_using_crc = new_crc; + +} + +void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self, + const gchar *mode) +{ + gint new_mode; + + new_mode = gst_a2dp_sender_sink_get_channel_mode(mode); + + if (self->channel_mode != -1 && new_mode != self->channel_mode) { + GST_ERROR_OBJECT(self, "channel mode changed during stream"); + self->mpeg_stream_changed = TRUE; + } + + self->channel_mode = new_mode; + if (self->channel_mode == -1) + GST_WARNING_OBJECT(self, "Received invalid channel " + "mode: %s", mode); +} + + diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h index 863ef6c..f23c86b 100644 --- a/audio/gsta2dpsendersink.h +++ b/audio/gsta2dpsendersink.h @@ -56,6 +56,11 @@ struct _GstA2dpSenderSink { struct bluetooth_data *data; GIOChannel *server; + /* mp3 stream data (outside caps data)*/ + gboolean mpeg_stream_changed; + gint mp3_using_crc; + gint channel_mode; + /* stream connection data */ GstCaps *stream_caps; @@ -85,6 +90,12 @@ gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink); gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin); +void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc); + +void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self, + const gchar *mode); + + G_END_DECLS #endif /* __GST_A2DP_SENDER_SINK_H */ diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index d90909c..83c907e 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -61,8 +61,11 @@ static GstStaticPadTemplate gst_a2dp_sink_factory = "allocation = (string) { snr, loudness }, " "bitpool = (int) [ 2, " TEMPLATE_MAX_BITPOOL_STR " ]; " + "audio/mpeg;" )); +/* FIXME we need a finalize */ + static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -132,6 +135,10 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, gst_event_unref(self->newseg_event); self->newseg_event = NULL; } + if (self->taglist) { + gst_tag_list_free(self->taglist); + self->taglist = NULL; + } break; default: @@ -292,10 +299,63 @@ cleanup_and_fail: return FALSE; } +static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self) +{ + GstElement *rtppay; + + /* FIXME we will need a internal mpegparse for identifying + * stream stuff */ + + rtppay = gst_element_factory_make("rtpmpapay", "rtp"); + if (rtppay == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create rtpmpapay"); + return FALSE; + } + + if (!gst_bin_add(GST_BIN(self), rtppay)) { + GST_ERROR_OBJECT(self, "failed to add rtpmpapay to the bin"); + goto cleanup_and_fail; + } + + if (gst_element_set_state(rtppay, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "rtpmpapay failed to go to ready"); + goto remove_element_and_fail; + } + + if (!gst_element_link(self->capsfilter, rtppay)) { + GST_ERROR_OBJECT(self, "couldn't link capsfilter " + "to rtpmpapay"); + goto remove_element_and_fail; + } + + self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); + + gst_element_set_state(rtppay, GST_STATE_PAUSED); + + return TRUE; + +remove_element_and_fail: + gst_element_set_state (rtppay, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), rtppay); + return FALSE; + +cleanup_and_fail: + if (rtppay != NULL) + g_object_unref(G_OBJECT(rtppay)); + + return FALSE; +} + + static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) { GstA2dpSink *self; GstStructure *structure; + GstEvent *event; + GstPad *capsfilterpad; + gboolean crc; + gchar *mode = NULL; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); GST_INFO_OBJECT(self, "setting caps"); @@ -303,9 +363,13 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) structure = gst_caps_get_structure(caps, 0); /* first, we need to create our rtp payloader */ - if (gst_structure_has_name(structure, "audio/x-sbc")) - gst_a2dp_sink_init_rtp_sbc_element(self); - else { + if (gst_structure_has_name(structure, "audio/x-sbc")) { + if(!gst_a2dp_sink_init_rtp_sbc_element(self)) + return FALSE; + } else if (gst_structure_has_name(structure, "audio/mpeg")) { + if(!gst_a2dp_sink_init_rtp_mpeg_element(self)) + return FALSE; + } else { GST_ERROR_OBJECT(self, "Unexpected media type"); return FALSE; } @@ -313,6 +377,27 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) if (!gst_a2dp_sink_init_sender_sink(self)) return FALSE; + /* check if we should push the taglist FIXME should we push this? + * we can send the tags directly if needed */ + if (self->taglist != NULL && + gst_structure_has_name(structure, "audio/mpeg")) { + + event = gst_event_new_tag(self->taglist); + + /* send directly the crc */ + if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc)) + gst_a2dp_sender_sink_set_crc(self->sink, crc); + + if (gst_tag_list_get_string(self->taglist, "channel-mode", + &mode)) + gst_a2dp_sender_sink_set_channel_mode(self->sink, mode); + + capsfilterpad = gst_ghost_pad_get_target(self->ghostpad); + gst_pad_send_event(capsfilterpad, event); + /*gst_tag_list_free(self->taglist);*/ + self->taglist = NULL; + } + if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps)) return FALSE; @@ -332,6 +417,7 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) { GstA2dpSink *self; + GstTagList *taglist = NULL; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); @@ -341,6 +427,17 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) if (self->newseg_event != NULL) gst_event_unref(self->newseg_event); self->newseg_event = gst_event_ref(event); + } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG && + gst_element_get_parent(GST_ELEMENT(self->sink)) != + GST_OBJECT_CAST(self)) { + if (self->taglist == NULL) { + gst_event_parse_tag(event, &self->taglist); + } else { + gst_event_parse_tag(event, &taglist); + gst_tag_list_insert(self->taglist, taglist, + GST_TAG_MERGE_REPLACE); + } + /* FIXME handle tag events */ } return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event); @@ -374,7 +471,9 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, self->rtp = NULL; self->device = NULL; self->capsfilter = NULL; + self->mpegparse = NULL; self->newseg_event = NULL; + self->taglist = gst_tag_list_new(); /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 4bf9d60..f74185e 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -47,6 +47,7 @@ struct _GstA2dpSink { GstBaseRTPPayload *rtp; GstA2dpSenderSink *sink; GstElement *capsfilter; + GstElement *mpegparse; gchar *device; @@ -55,6 +56,9 @@ struct _GstA2dpSink { GstPadEventFunction ghostpad_eventfunc; GstEvent *newseg_event; + /* Store the tags received before the a2dpsender sink is created + * when it is created we forward this to it */ + GstTagList *taglist; }; struct _GstA2dpSinkClass { diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index d791a8d..c4d602f 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -89,12 +89,22 @@ const gchar *gst_sbc_get_allocation_from_list(const GValue *value) /* * Selects one mode from the ones on the list - * TODO - use a better aproach */ -const gchar *gst_sbc_get_mode_from_list(const GValue *value) +const gchar *gst_sbc_get_mode_from_list(const GValue *list) { - guint size = gst_value_list_get_size(value); - return g_value_get_string(gst_value_list_get_value(value, size-1)); + int i; + const GValue *value; + const gchar *aux; + + guint size = gst_value_list_get_size(list); + for (i = 0; i < size; i++) { + value = gst_value_list_get_value(list, i); + aux = g_value_get_string(value); + if (strcmp("stereo", aux) == 0) { + return "stereo"; + } + } + return g_value_get_string(gst_value_list_get_value(list, size-1)); } gint gst_sbc_get_allocation_mode_int(const gchar *allocation) -- 1.5.3.7
0009-Add-fixes-to-gstreamer-typefind.patch
(text/x-diff, 13.1 KB)
From 9ec779a87796a18efb18a582510dabc1be2730a3 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Thu, 17 Jan 2008 19:15:38 -0300 Subject: [PATCH] Add fixes to gstreamer typefind. --- audio/gsta2dpsendersink.c | 27 ++++++---- audio/gsta2dpsink.c | 121 ++++++++++++++++++++++++++++++--------------- audio/gstbluetooth.c | 23 ++++++++- audio/gstsbcutil.c | 55 ++++++++++++++++++++ audio/gstsbcutil.h | 1 + 5 files changed, 175 insertions(+), 52 deletions(-) diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c index d0d8d59..0256bb9 100644 --- a/audio/gsta2dpsendersink.c +++ b/audio/gsta2dpsendersink.c @@ -91,12 +91,15 @@ static GstStaticPadTemplate a2dp_sender_sink_factory = "encoding-name = (string) \"SBC\";" "application/x-rtp, " "media = (string) \"audio\", " - "payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", " + "payload = (int) " + GST_RTP_PAYLOAD_MPA_STRING ", " "clock-rate = (int) 90000; " "application/x-rtp, " "media = (string) \"audio\", " - "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " - "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"" + "payload = (int) " + GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " + "encoding-name = (string) \"MPA\"" )); static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self, @@ -819,7 +822,8 @@ static void gst_a2dp_sender_sink_tag(const GstTagList *taglist, } else if (strcmp(tag, "channel-mode") == 0) { if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { - GST_WARNING_OBJECT(self, "failed to get channel-mode tag"); + GST_WARNING_OBJECT(self, + "failed to get channel-mode tag"); self->mpeg_stream_changed = TRUE; } @@ -845,11 +849,9 @@ static gboolean gst_a2dp_sender_sink_event(GstBaseSink *basesink, * are outside caps */ gst_event_parse_tag(event, &taglist); gst_tag_list_foreach(taglist, gst_a2dp_sender_sink_tag, self); - /*g_free(taglist); FIXME this is causing segfault*/ - return TRUE; } - return FALSE; + return TRUE; } static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) @@ -883,8 +885,11 @@ static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) self->channel_mode = -1; self->mpeg_stream_changed = FALSE; - if (!gst_a2dp_sender_sink_get_capabilities(self)) + if (!gst_a2dp_sender_sink_get_capabilities(self)) { + GST_ERROR_OBJECT(self, "failed to get capabilities " + "from device"); goto failed; + } return TRUE; @@ -972,7 +977,8 @@ static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf( if (self->mp3_using_crc != -1) pkt->crc = self->mp3_using_crc; else { - GST_ERROR_OBJECT(self, "No info about crc was received, can't proceed"); + GST_ERROR_OBJECT(self, "No info about crc was received, " + " can't proceed"); return FALSE; } @@ -980,7 +986,8 @@ static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf( if (self->channel_mode != -1) pkt->channel_mode = self->channel_mode; else { - GST_ERROR_OBJECT(self, "No info about channel mode received, can't proceed"); + GST_ERROR_OBJECT(self, "No info about channel mode " + "received, can't proceed"); return FALSE; } diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 83c907e..091d547 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -64,7 +64,10 @@ static GstStaticPadTemplate gst_a2dp_sink_factory = "audio/mpeg;" )); -/* FIXME we need a finalize */ +static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event); +static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps); +static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad); +static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self); static void gst_a2dp_sink_base_init(gpointer g_class) { @@ -119,18 +122,79 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, } } +static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self) +{ + GstPad *capsfilter_pad; + + /* we search for the capsfilter sinkpad */ + capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); + + /* now we add a ghostpad */ + self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", + capsfilter_pad)); + g_object_unref(capsfilter_pad); + + /* the getcaps of our ghostpad must reflect the device caps */ + gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_get_caps); + self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); + gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), + GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); + + /* we need to handle events on our own and we also need the eventfunc + * of the ghostpad for forwarding calls */ + self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); + gst_pad_set_event_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_handle_event); + + if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) + GST_ERROR_OBJECT(self, "failed to add ghostpad"); + + return TRUE; +} + static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, GstStateChange transition) { + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstA2dpSink *self = GST_A2DP_SINK(element); switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + self->taglist = gst_tag_list_new(); + break; + case GST_STATE_CHANGE_NULL_TO_READY: - gst_element_set_state(GST_ELEMENT(self->sink), + self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( + "a2dpsendersink", "sendersink")); + if (self->sink == NULL) { + GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); + return GST_STATE_CHANGE_FAILURE; + } + + if (self->device != NULL) + gst_a2dp_sender_sink_set_device(self->sink, + self->device); + + ret = gst_element_set_state(GST_ELEMENT(self->sink), GST_STATE_READY); break; + default: + break; + } - case GST_STATE_CHANGE_READY_TO_NULL: + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, + transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (self->taglist) { + gst_tag_list_free(self->taglist); + self->taglist = NULL; + } if (self->newseg_event != NULL) { gst_event_unref(self->newseg_event); self->newseg_event = NULL; @@ -141,12 +205,17 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, } break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink))) + GST_WARNING_OBJECT(self, "Failed to remove " + "a2dpsendersink from bin"); + break; + default: break; } - return GST_ELEMENT_CLASS(parent_class)->change_state(element, - transition); + return ret; } static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) @@ -288,7 +357,7 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) return TRUE; remove_element_and_fail: - gst_element_set_state (rtppay, GST_STATE_NULL); + gst_element_set_state(rtppay, GST_STATE_NULL); gst_bin_remove(GST_BIN(self), rtppay); return FALSE; @@ -347,7 +416,6 @@ cleanup_and_fail: return FALSE; } - static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) { GstA2dpSink *self; @@ -364,10 +432,10 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) /* first, we need to create our rtp payloader */ if (gst_structure_has_name(structure, "audio/x-sbc")) { - if(!gst_a2dp_sink_init_rtp_sbc_element(self)) + if (!gst_a2dp_sink_init_rtp_sbc_element(self)) return FALSE; } else if (gst_structure_has_name(structure, "audio/mpeg")) { - if(!gst_a2dp_sink_init_rtp_mpeg_element(self)) + if (!gst_a2dp_sink_init_rtp_mpeg_element(self)) return FALSE; } else { GST_ERROR_OBJECT(self, "Unexpected media type"); @@ -394,8 +462,8 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) capsfilterpad = gst_ghost_pad_get_target(self->ghostpad); gst_pad_send_event(capsfilterpad, event); - /*gst_tag_list_free(self->taglist);*/ self->taglist = NULL; + g_free(mode); } if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps)) @@ -465,15 +533,14 @@ failed: static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { - GstPad *capsfilter_pad; - self->sink = NULL; self->rtp = NULL; self->device = NULL; self->capsfilter = NULL; self->mpegparse = NULL; self->newseg_event = NULL; - self->taglist = gst_tag_list_new(); + self->taglist = NULL; + self->ghostpad = NULL; /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); @@ -481,34 +548,8 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), NULL); - /* we search for the capsfilter sinkpad */ - capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); + gst_a2dp_sink_init_ghost_pad(self); - /* now we add a ghostpad */ - self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", - capsfilter_pad)); - g_object_unref(capsfilter_pad); - - /* the getcaps of our ghostpad must reflect the device caps */ - gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), - gst_a2dp_sink_get_caps); - self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); - gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), - GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); - - /* we need to handle events on our own and we also need the eventfunc - * of the ghostpad for forwarding calls */ - self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); - gst_pad_set_event_function(GST_PAD(self->ghostpad), - gst_a2dp_sink_handle_event); - - if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) - GST_ERROR_OBJECT(self, "failed to add ghostpad"); - - self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( - "a2dpsendersink", "sendersink")); - if (self->sink == NULL) - GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); } gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin) diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 764bc89..eaab23d 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -25,6 +25,11 @@ #include <config.h> #endif +#include <gst/gst.h> + +#include "gstsbcutil.h" +#include <sbc.h> + #include "gstsbcenc.h" #include "gstsbcdec.h" #include "gstsbcparse.h" @@ -38,12 +43,26 @@ static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); static void sbc_typefind(GstTypeFind *tf, gpointer ignore) { - guint8 *data = gst_type_find_peek(tf, 0, 1); + GstCaps *caps; + guint8 *aux; + sbc_t sbc; + guint8 *data = gst_type_find_peek(tf, 0, 32); + + if (sbc_init(&sbc, 0) < 0) + return; if (data == NULL || *data != 0x9c) /* SBC syncword */ return; - gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, SBC_CAPS); + aux = g_new(guint8, 32); + memcpy(aux, data, 32); + sbc_parse(&sbc, aux, 32); + g_free(aux); + caps = gst_sbc_parse_caps_from_sbc(&sbc); + sbc_finish(&sbc); + + gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, caps); + gst_caps_unref(caps); } static gchar *sbc_exts[] = { "sbc", NULL }; diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index c4d602f..04b7217 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -167,6 +167,61 @@ const gchar *gst_sbc_get_allocation_string(int alloc) } } +/* channel mode */ +#define SBC_CM_MONO 0x00 +#define SBC_CM_DUAL_CHANNEL 0x01 +#define SBC_CM_STEREO 0x02 +#define SBC_CM_JOINT_STEREO 0x03 + +/* allocation mode */ +#define SBC_AM_LOUDNESS 0x00 +#define SBC_AM_SNR 0x01 + +const gchar *gst_sbc_get_mode_string_from_sbc_t(int channels, int joint) +{ + if (channels == 2 && joint == 1) + return "joint"; + else if (channels == 2 && joint == 0) + return "stereo"; + else + return NULL; +} + +const gchar *gst_sbc_get_allocation_string_from_sbc_t(int alloc) +{ + switch (alloc) { + case SBC_AM_LOUDNESS: + return "loudness"; + case SBC_AM_SNR: + return "snr"; + default: + return NULL; + } +} + +GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc) +{ + GstCaps *caps; + const gchar *mode_str; + const gchar *allocation_str; + + mode_str = gst_sbc_get_mode_string_from_sbc_t(sbc->channels, + sbc->joint); + allocation_str = gst_sbc_get_allocation_string_from_sbc_t( + sbc->allocation); + caps = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, sbc->rate, + "channels", G_TYPE_INT, sbc->channels, + "mode", G_TYPE_STRING, mode_str, + "subbands", G_TYPE_INT, sbc->subbands, + "blocks", G_TYPE_INT, sbc->blocks, + "allocation", G_TYPE_STRING, allocation_str, + "bitpool", G_TYPE_INT, sbc->bitpool, + NULL); + + return caps; +} + GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels) { GstCaps *caps; diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index a67bf1b..4bdb5ac 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -47,6 +47,7 @@ gint gst_sbc_get_mode_int(const gchar *mode); const gchar *gst_sbc_get_mode_string(int joint); GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); +GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc); GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); -- 1.5.3.7
0010-Make-debug-logs-per-sep.patch
(text/x-diff, 8.8 KB)
From ec37457030a779094ddef178f97c95f2a7744262 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Fri, 18 Jan 2008 16:42:07 -0300 Subject: [PATCH] Make debug logs per sep. --- audio/a2dp.c | 85 +++++++++++++++++++++++++++++---------------------------- 1 files changed, 43 insertions(+), 42 deletions(-) diff --git a/audio/a2dp.c b/audio/a2dp.c index b1409f5..0173811 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -294,9 +294,9 @@ static gboolean sbc_setconf_ind(struct avdtp *session, struct sbc_codec_cap *sbc_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Ind"); + debug("Sink %p: Set_Configuration_Ind", sep); else - debug("SBC Source: Set_Configuration_Ind"); + debug("Source %p: Set_Configuration_Ind", sep); dev = a2dp_get_dev(session); if (!dev) { @@ -340,9 +340,9 @@ static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *se struct sbc_codec_cap sbc_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Get_Capability_Ind"); + debug("Sink %p: Get_Capability_Ind", sep); else - debug("SBC Source: Get_Capability_Ind"); + debug("Source %p: Get_Capability_Ind", sep); *caps = NULL; @@ -397,9 +397,9 @@ static gboolean mpeg_setconf_ind(struct avdtp *session, struct device *dev; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Ind"); + debug("Sink %p: Set_Configuration_Ind", sep); else - debug("SBC Source: Set_Configuration_Ind"); + debug("Source %p: Set_Configuration_Ind", sep); dev = a2dp_get_dev(session); if (!dev) { @@ -417,7 +417,8 @@ static gboolean mpeg_setconf_ind(struct avdtp *session, return TRUE; } -static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, +static gboolean mpeg_getcap_ind(struct avdtp *session, + struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; @@ -425,9 +426,9 @@ static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *s struct mpeg_codec_cap mpeg_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Get_Capability_Ind"); + debug("Sink %p: Get_Capability_Ind", sep); else - debug("SBC Source: Get_Capability_Ind"); + debug("Source %p: Get_Capability_Ind", sep); *caps = NULL; @@ -475,9 +476,9 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, int ret; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Cfm"); + debug("Sink %p: Set_Configuration_Cfm", sep); else - debug("SBC Source: Set_Configuration_Cfm"); + debug("Source %p: Set_Configuration_Cfm", sep); setup = find_setup_by_session(session); @@ -515,9 +516,9 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Get_Configuration_Ind"); + debug("Sink %p: Get_Configuration_Ind"); else - debug("SBC Source: Get_Configuration_Ind"); + debug("Source %p: Get_Configuration_Ind"); return TRUE; } @@ -528,9 +529,9 @@ static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Cfm"); + debug("Sink %p: Set_Configuration_Cfm", sep); else - debug("SBC Source: Set_Configuration_Cfm"); + debug("Source %p: Set_Configuration_Cfm", sep); } static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -540,9 +541,9 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Open_Ind"); + debug("Sink %p: Open_Ind", sep); else - debug("SBC Source: Open_Ind"); + debug("Source %p: Open_Ind", sep); return TRUE; } @@ -554,9 +555,9 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Open_Cfm"); + debug("Sink %p: Open_Cfm", sep); else - debug("SBC Source: Open_Cfm"); + debug("Source %p: Open_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -601,9 +602,9 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Start_Ind"); + debug("Sink %p: Start_Ind", sep); else - debug("SBC Source: Start_Ind"); + debug("Source %p: Start_Ind", sep); if (!a2dp_sep->locked) { a2dp_sep->session = avdtp_ref(session); @@ -623,9 +624,9 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Start_Cfm"); + debug("Sink %p: Start_Cfm", sep); else - debug("SBC Source: Start_Cfm"); + debug("Source %p: Start_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -654,9 +655,9 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Suspend_Ind"); + debug("Sink %p: Suspend_Ind", sep); else - debug("SBC Source: Suspend_Ind"); + debug("Source %p: Suspend_Ind", sep); if (a2dp_sep->suspend_timer) { g_source_remove(a2dp_sep->suspend_timer); @@ -677,9 +678,9 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, gboolean start; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Suspend_Cfm"); + debug("Sink %p: Suspend_Cfm", sep); else - debug("SBC Source: Suspend_Cfm"); + debug("Source %p: Suspend_Cfm", sep); a2dp_sep->suspending = FALSE; @@ -720,9 +721,9 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Close_Ind"); + debug("Sink %p: Close_Ind", sep); else - debug("SBC Source: Close_Ind"); + debug("Source %p: Close_Ind", sep); return TRUE; } @@ -762,9 +763,9 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Close_Cfm"); + debug("Sink %p: Close_Cfm", sep); else - debug("SBC Source: Close_Cfm"); + debug("Source %p: Close_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -793,9 +794,9 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Abort_Ind"); + debug("Sink %p: Abort_Ind", sep); else - debug("SBC Source: Abort_Ind"); + debug("Source %p: Abort_Ind", sep); a2dp_sep->stream = NULL; @@ -810,9 +811,9 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Abort_Cfm"); + debug("Sink %p: Abort_Cfm", sep); else - debug("SBC Source: Abort_Cfm"); + debug("Source %p: Abort_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -827,9 +828,9 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: ReConfigure_Ind"); + debug("Sink %p: ReConfigure_Ind", sep); else - debug("SBC Source: ReConfigure_Ind"); + debug("Source %p: ReConfigure_Ind", sep); return TRUE; } @@ -841,9 +842,9 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: ReConfigure_Cfm"); + debug("Sink %p: ReConfigure_Cfm", sep); else - debug("SBC Source: ReConfigure_Cfm"); + debug("Source %p: ReConfigure_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -1161,7 +1162,7 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, return 0; } - debug("a2dp_source_config: selected SEP %p", sep); + debug("a2dp_source_config: selected SEP %p", sep->sep); cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->config_cb = cb; @@ -1337,7 +1338,7 @@ gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session) if (sep->locked) return FALSE; - debug("SBC Source SEP %p locked", sep); + debug("SEP %p locked", sep->sep); sep->locked = TRUE; return TRUE; @@ -1351,7 +1352,7 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session) sep->locked = FALSE; - debug("SBC Source SEP %p unlocked", sep); + debug("SEP %p unlocked", sep->sep); if (!sep->stream || state == AVDTP_STATE_IDLE) return TRUE; -- 1.5.3.7
0011-Bug-fixes-for-gstreamer-plugin.patch
(text/x-diff, 9.3 KB)
From 887c340e06c248d55b0a77cbe3d7de5579b7cd3e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <[email protected]> Date: Mon, 21 Jan 2008 14:17:30 -0300 Subject: [PATCH] Bug fixes for gstreamer plugin. --- audio/gsta2dpsink.c | 194 ++++++++++++++++++++++++++++++--------------------- audio/gsta2dpsink.h | 2 +- 2 files changed, 114 insertions(+), 82 deletions(-) diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 091d547..7c68d17 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -69,6 +69,56 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps); static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad); static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self); +/* + * Helper function to create elements, add to the bin and link it + * to another element. + */ +static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, + const gchar* elementname, const gchar* name, + GstElement *link_to) +{ + GstElement *element; + + GST_LOG_OBJECT(self, "Initializing %s", elementname); + + element = gst_element_factory_make(elementname, name); + if (element == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create %s", elementname); + return NULL; + } + + if (!gst_bin_add(GST_BIN(self), element)) { + GST_ERROR_OBJECT(self, "failed to add %s to the bin", + elementname); + goto cleanup_and_fail; + } + + if (gst_element_set_state(element, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "%s failed to go to ready", + elementname); + goto remove_element_and_fail; + } + + if (!gst_element_link(link_to, element)) { + GST_ERROR_OBJECT(self, "couldn't link %s", elementname); + goto remove_element_and_fail; + } + + return element; + +remove_element_and_fail: + gst_element_set_state(element, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), element); + return NULL; + +cleanup_and_fail: + if (element != NULL) + g_object_unref(G_OBJECT(element)); + + return NULL; +} + static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -153,6 +203,18 @@ static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self) return TRUE; } +static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self) +{ + if (self->rtp) { + GST_LOG_OBJECT(self, "removing rtp element from the bin"); + if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp))) + GST_WARNING_OBJECT(self, "failed to remove rtp " + "element from bin"); + else + self->rtp = NULL; + } +} + static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, GstStateChange transition) { @@ -165,6 +227,8 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, break; case GST_STATE_CHANGE_NULL_TO_READY: + self->sink_is_in_bin = FALSE; + self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( "a2dpsendersink", "sendersink")); if (self->sink == NULL) { @@ -199,16 +263,24 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, gst_event_unref(self->newseg_event); self->newseg_event = NULL; } - if (self->taglist) { - gst_tag_list_free(self->taglist); - self->taglist = NULL; - } break; case GST_STATE_CHANGE_READY_TO_NULL: - if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink))) - GST_WARNING_OBJECT(self, "Failed to remove " - "a2dpsendersink from bin"); + if (self->sink_is_in_bin) { + if (!gst_bin_remove(GST_BIN(self), + GST_ELEMENT(self->sink))) + GST_WARNING_OBJECT(self, "Failed to remove " + "a2dpsendersink from bin"); + } else if (self->sink != NULL) { + gst_element_set_state(GST_ELEMENT(self->sink), + GST_STATE_NULL); + g_object_unref(G_OBJECT(self->sink)); + } + + self->sink = NULL; + + gst_a2dp_sink_remove_dynamic_elements(self); + break; default: @@ -304,6 +376,7 @@ static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self) } self->sink = GST_A2DP_SENDER_SINK(sink); + self->sink_is_in_bin = TRUE; g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); gst_element_set_state(sink, GST_STATE_PAUSED); @@ -326,28 +399,10 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) { GstElement *rtppay; - rtppay = gst_element_factory_make("rtpsbcpay", "rtp"); - if (rtppay == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create rtpsbcpay"); + rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp", + self->capsfilter); + if (rtppay == NULL) return FALSE; - } - - if (!gst_bin_add(GST_BIN(self), rtppay)) { - GST_ERROR_OBJECT(self, "failed to add rtp sbc pay to the bin"); - goto cleanup_and_fail; - } - - if (gst_element_set_state(rtppay, GST_STATE_READY) == - GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "rtpsbcpay failed to go to ready"); - goto remove_element_and_fail; - } - - if (!gst_element_link(self->capsfilter, rtppay)) { - GST_ERROR_OBJECT(self, "couldn't link capsfilter " - "to rtpsbcpay"); - goto remove_element_and_fail; - } self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL); @@ -355,86 +410,48 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE; - -remove_element_and_fail: - gst_element_set_state(rtppay, GST_STATE_NULL); - gst_bin_remove(GST_BIN(self), rtppay); - return FALSE; - -cleanup_and_fail: - if (rtppay != NULL) - g_object_unref(G_OBJECT(rtppay)); - - return FALSE; } static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self) { GstElement *rtppay; - /* FIXME we will need a internal mpegparse for identifying - * stream stuff */ + GST_LOG_OBJECT(self, "Initializing rtp mpeg element"); - rtppay = gst_element_factory_make("rtpmpapay", "rtp"); - if (rtppay == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create rtpmpapay"); + /* if capsfilter is not created then we can't have our rtp element */ + if (self->capsfilter == NULL) return FALSE; - } - if (!gst_bin_add(GST_BIN(self), rtppay)) { - GST_ERROR_OBJECT(self, "failed to add rtpmpapay to the bin"); - goto cleanup_and_fail; - } - - if (gst_element_set_state(rtppay, GST_STATE_READY) == - GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "rtpmpapay failed to go to ready"); - goto remove_element_and_fail; - } - - if (!gst_element_link(self->capsfilter, rtppay)) { - GST_ERROR_OBJECT(self, "couldn't link capsfilter " - "to rtpmpapay"); - goto remove_element_and_fail; - } + rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", + self->capsfilter); + if (rtppay == NULL) + return FALSE; self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE; - -remove_element_and_fail: - gst_element_set_state (rtppay, GST_STATE_NULL); - gst_bin_remove(GST_BIN(self), rtppay); - return FALSE; - -cleanup_and_fail: - if (rtppay != NULL) - g_object_unref(G_OBJECT(rtppay)); - - return FALSE; } -static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) +static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, + GstCaps *caps) { - GstA2dpSink *self; GstStructure *structure; GstEvent *event; GstPad *capsfilterpad; gboolean crc; gchar *mode = NULL; - self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - GST_INFO_OBJECT(self, "setting caps"); - structure = gst_caps_get_structure(caps, 0); /* first, we need to create our rtp payloader */ if (gst_structure_has_name(structure, "audio/x-sbc")) { + GST_LOG_OBJECT(self, "sbc media received"); if (!gst_a2dp_sink_init_rtp_sbc_element(self)) return FALSE; } else if (gst_structure_has_name(structure, "audio/mpeg")) { + GST_LOG_OBJECT(self, "mp3 media received"); if (!gst_a2dp_sink_init_rtp_mpeg_element(self)) return FALSE; } else { @@ -473,9 +490,24 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL); /* we forward our new segment here if we have one */ - gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), - self->newseg_event); - self->newseg_event = NULL; + if (self->newseg_event) { + gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), + self->newseg_event); + self->newseg_event = NULL; + } + + return TRUE; +} + +static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) +{ + GstA2dpSink *self; + + self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + GST_INFO_OBJECT(self, "setting caps"); + + /* now we know the caps */ + gst_a2dp_sink_init_dynamic_elements(self, caps); return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps); } @@ -537,10 +569,10 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, self->rtp = NULL; self->device = NULL; self->capsfilter = NULL; - self->mpegparse = NULL; self->newseg_event = NULL; self->taglist = NULL; self->ghostpad = NULL; + self->sink_is_in_bin = FALSE; /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index f74185e..368f837 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -47,9 +47,9 @@ struct _GstA2dpSink { GstBaseRTPPayload *rtp; GstA2dpSenderSink *sink; GstElement *capsfilter; - GstElement *mpegparse; gchar *device; + gboolean sink_is_in_bin; GstGhostPad *ghostpad; GstPadSetCapsFunction ghostpad_setcapsfunc; -- 1.5.3.7