mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-04 05:22:30 +00:00
bluez: make bluez elements compile with 1.0
Builds, but very likely doesn't work yet at all. Some things are still commented out. https://bugzilla.gnome.org/show_bug.cgi?id=690582
This commit is contained in:
parent
49a69e394a
commit
c994ae021d
4 changed files with 129 additions and 154 deletions
|
@ -21,16 +21,20 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* FIXME:
|
||||||
|
* - the segment_event caching and re-sending should not be needed any
|
||||||
|
* longer with sticky events
|
||||||
|
*/
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "gstpragma.h"
|
|
||||||
#include "gsta2dpsink.h"
|
#include "gsta2dpsink.h"
|
||||||
|
|
||||||
|
#include <gst/rtp/gstrtpbasepayload.h>
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (gst_a2dp_sink_debug);
|
GST_DEBUG_CATEGORY_STATIC (gst_a2dp_sink_debug);
|
||||||
#define GST_CAT_DEFAULT gst_a2dp_sink_debug
|
#define GST_CAT_DEFAULT gst_a2dp_sink_debug
|
||||||
|
|
||||||
|
@ -47,13 +51,8 @@ enum
|
||||||
PROP_TRANSPORT
|
PROP_TRANSPORT
|
||||||
};
|
};
|
||||||
|
|
||||||
GST_BOILERPLATE (GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
|
#define parent_class gst_a2dp_sink_parent_class
|
||||||
|
G_DEFINE_TYPE (GstA2dpSink, gst_a2dp_sink, GST_TYPE_BIN);
|
||||||
static const GstElementDetails gst_a2dp_sink_details =
|
|
||||||
GST_ELEMENT_DETAILS ("Bluetooth A2DP sink",
|
|
||||||
"Sink/Audio",
|
|
||||||
"Plays audio to an A2DP device",
|
|
||||||
"Marcel Holtmann <marcel@holtmann.org>");
|
|
||||||
|
|
||||||
static GstStaticPadTemplate gst_a2dp_sink_factory =
|
static GstStaticPadTemplate gst_a2dp_sink_factory =
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||||
|
@ -66,9 +65,11 @@ static GstStaticPadTemplate gst_a2dp_sink_factory =
|
||||||
"allocation = (string) { \"snr\", \"loudness\" }, "
|
"allocation = (string) { \"snr\", \"loudness\" }, "
|
||||||
"bitpool = (int) [ 2, " TEMPLATE_MAX_BITPOOL_STR " ]; " "audio/mpeg"));
|
"bitpool = (int) [ 2, " TEMPLATE_MAX_BITPOOL_STR " ]; " "audio/mpeg"));
|
||||||
|
|
||||||
static gboolean gst_a2dp_sink_handle_event (GstPad * pad, GstEvent * event);
|
static gboolean gst_a2dp_sink_handle_event (GstPad * pad,
|
||||||
static gboolean gst_a2dp_sink_set_caps (GstPad * pad, GstCaps * caps);
|
GstObject * pad_parent, GstEvent * event);
|
||||||
static GstCaps *gst_a2dp_sink_get_caps (GstPad * pad);
|
static gboolean gst_a2dp_sink_query (GstPad * pad, GstObject * parent,
|
||||||
|
GstQuery * query);
|
||||||
|
static GstCaps *gst_a2dp_sink_get_caps (GstA2dpSink * self);
|
||||||
static gboolean gst_a2dp_sink_init_caps_filter (GstA2dpSink * self);
|
static gboolean gst_a2dp_sink_init_caps_filter (GstA2dpSink * self);
|
||||||
static gboolean gst_a2dp_sink_init_fakesink (GstA2dpSink * self);
|
static gboolean gst_a2dp_sink_init_fakesink (GstA2dpSink * self);
|
||||||
static gboolean gst_a2dp_sink_remove_fakesink (GstA2dpSink * self);
|
static gboolean gst_a2dp_sink_remove_fakesink (GstA2dpSink * self);
|
||||||
|
@ -78,7 +79,7 @@ gst_a2dp_sink_finalize (GObject * obj)
|
||||||
{
|
{
|
||||||
GstA2dpSink *self = GST_A2DP_SINK (obj);
|
GstA2dpSink *self = GST_A2DP_SINK (obj);
|
||||||
|
|
||||||
g_mutex_free (self->cb_mutex);
|
g_mutex_clear (&self->cb_mutex);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
||||||
}
|
}
|
||||||
|
@ -144,16 +145,6 @@ cleanup_and_fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (&gst_a2dp_sink_factory));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_a2dp_sink_set_property (GObject * object, guint prop_id,
|
gst_a2dp_sink_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec)
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
@ -237,23 +228,18 @@ gst_a2dp_sink_init_ghost_pad (GstA2dpSink * self)
|
||||||
capsfilter_pad = gst_element_get_static_pad (self->capsfilter, "sink");
|
capsfilter_pad = gst_element_get_static_pad (self->capsfilter, "sink");
|
||||||
|
|
||||||
/* now we add a ghostpad */
|
/* now we add a ghostpad */
|
||||||
self->ghostpad = GST_GHOST_PAD (gst_ghost_pad_new ("sink", capsfilter_pad));
|
self->ghostpad = gst_ghost_pad_new ("sink", capsfilter_pad);
|
||||||
g_object_unref (capsfilter_pad);
|
g_object_unref (capsfilter_pad);
|
||||||
|
|
||||||
/* the getcaps of our ghostpad must reflect the device caps */
|
/* the getcaps of our ghostpad must reflect the device caps */
|
||||||
gst_pad_set_getcaps_function (GST_PAD (self->ghostpad),
|
gst_pad_set_query_function (self->ghostpad, gst_a2dp_sink_query);
|
||||||
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
|
/* we need to handle events on our own and we also need the eventfunc
|
||||||
* of the ghostpad for forwarding calls */
|
* of the ghostpad for forwarding calls */
|
||||||
self->ghostpad_eventfunc = GST_PAD_EVENTFUNC (GST_PAD (self->ghostpad));
|
self->ghostpad_eventfunc = GST_PAD_EVENTFUNC (self->ghostpad);
|
||||||
gst_pad_set_event_function (GST_PAD (self->ghostpad),
|
gst_pad_set_event_function (self->ghostpad, gst_a2dp_sink_handle_event);
|
||||||
gst_a2dp_sink_handle_event);
|
|
||||||
|
|
||||||
if (!gst_element_add_pad (GST_ELEMENT (self), GST_PAD (self->ghostpad)))
|
if (!gst_element_add_pad (GST_ELEMENT (self), self->ghostpad))
|
||||||
GST_ERROR_OBJECT (self, "failed to add ghostpad");
|
GST_ERROR_OBJECT (self, "failed to add ghostpad");
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -279,7 +265,7 @@ gst_a2dp_sink_change_state (GstElement * element, GstStateChange transition)
|
||||||
|
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
self->taglist = gst_tag_list_new ();
|
self->taglist = gst_tag_list_new_empty ();
|
||||||
|
|
||||||
gst_a2dp_sink_init_fakesink (self);
|
gst_a2dp_sink_init_fakesink (self);
|
||||||
break;
|
break;
|
||||||
|
@ -316,12 +302,12 @@ gst_a2dp_sink_change_state (GstElement * element, GstStateChange transition)
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
if (self->taglist) {
|
if (self->taglist) {
|
||||||
gst_tag_list_free (self->taglist);
|
gst_tag_list_unref (self->taglist);
|
||||||
self->taglist = NULL;
|
self->taglist = NULL;
|
||||||
}
|
}
|
||||||
if (self->newseg_event != NULL) {
|
if (self->segment_event != NULL) {
|
||||||
gst_event_unref (self->newseg_event);
|
gst_event_unref (self->segment_event);
|
||||||
self->newseg_event = NULL;
|
self->segment_event = NULL;
|
||||||
}
|
}
|
||||||
gst_a2dp_sink_remove_fakesink (self);
|
gst_a2dp_sink_remove_fakesink (self);
|
||||||
break;
|
break;
|
||||||
|
@ -374,8 +360,15 @@ gst_a2dp_sink_class_init (GstA2dpSinkClass * klass)
|
||||||
g_param_spec_string ("transport", "Transport",
|
g_param_spec_string ("transport", "Transport",
|
||||||
"Use configured transport", NULL, G_PARAM_READWRITE));
|
"Use configured transport", NULL, G_PARAM_READWRITE));
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata (element_class, "Bluetooth A2DP sink",
|
||||||
|
"Sink/Audio", "Plays audio to an A2DP device",
|
||||||
|
"Marcel Holtmann <marcel@holtmann.org>");
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_a2dp_sink_debug, "a2dpsink", 0,
|
GST_DEBUG_CATEGORY_INIT (gst_a2dp_sink_debug, "a2dpsink", 0,
|
||||||
"A2DP sink element");
|
"A2DP sink element");
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
gst_static_pad_template_get (&gst_a2dp_sink_factory));
|
||||||
}
|
}
|
||||||
|
|
||||||
GstCaps *
|
GstCaps *
|
||||||
|
@ -385,11 +378,10 @@ gst_a2dp_sink_get_device_caps (GstA2dpSink * self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstCaps *
|
static GstCaps *
|
||||||
gst_a2dp_sink_get_caps (GstPad * pad)
|
gst_a2dp_sink_get_caps (GstA2dpSink * self)
|
||||||
{
|
{
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
GstCaps *caps_aux;
|
GstCaps *caps_aux;
|
||||||
GstA2dpSink *self = GST_A2DP_SINK (GST_PAD_PARENT (pad));
|
|
||||||
|
|
||||||
if (self->sink == NULL) {
|
if (self->sink == NULL) {
|
||||||
GST_DEBUG_OBJECT (self, "a2dpsink isn't initialized "
|
GST_DEBUG_OBJECT (self, "a2dpsink isn't initialized "
|
||||||
|
@ -476,8 +468,8 @@ gst_a2dp_sink_init_rtp_sbc_element (GstA2dpSink * self)
|
||||||
if (rtppay == NULL)
|
if (rtppay == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
self->rtp = GST_BASE_RTP_PAYLOAD (rtppay);
|
self->rtp = rtppay;
|
||||||
g_object_set (G_OBJECT (self->rtp), "min-frames", -1, NULL);
|
g_object_set (self->rtp, "min-frames", -1, NULL);
|
||||||
|
|
||||||
gst_element_set_state (rtppay, GST_STATE_PAUSED);
|
gst_element_set_state (rtppay, GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
@ -503,7 +495,7 @@ gst_a2dp_sink_init_rtp_mpeg_element (GstA2dpSink * self)
|
||||||
if (rtppay == NULL)
|
if (rtppay == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
self->rtp = GST_BASE_RTP_PAYLOAD (rtppay);
|
self->rtp = rtppay;
|
||||||
|
|
||||||
gst_element_set_state (rtppay, GST_STATE_PAUSED);
|
gst_element_set_state (rtppay, GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
@ -554,7 +546,7 @@ gst_a2dp_sink_init_dynamic_elements (GstA2dpSink * self, GstCaps * caps)
|
||||||
if (gst_tag_list_get_string (self->taglist, "channel-mode", &mode))
|
if (gst_tag_list_get_string (self->taglist, "channel-mode", &mode))
|
||||||
gst_avdtp_sink_set_channel_mode (self->sink, mode);
|
gst_avdtp_sink_set_channel_mode (self->sink, mode);
|
||||||
|
|
||||||
capsfilterpad = gst_ghost_pad_get_target (self->ghostpad);
|
capsfilterpad = gst_ghost_pad_get_target (GST_GHOST_PAD (self->ghostpad));
|
||||||
gst_pad_send_event (capsfilterpad, event);
|
gst_pad_send_event (capsfilterpad, event);
|
||||||
self->taglist = NULL;
|
self->taglist = NULL;
|
||||||
g_free (mode);
|
g_free (mode);
|
||||||
|
@ -563,50 +555,39 @@ gst_a2dp_sink_init_dynamic_elements (GstA2dpSink * self, GstCaps * caps)
|
||||||
if (!gst_avdtp_sink_set_device_caps (self->sink, caps))
|
if (!gst_avdtp_sink_set_device_caps (self->sink, caps))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
g_object_set (G_OBJECT (self->rtp), "mtu",
|
g_object_set (self->rtp, "mtu",
|
||||||
gst_avdtp_sink_get_link_mtu (self->sink), NULL);
|
gst_avdtp_sink_get_link_mtu (self->sink), NULL);
|
||||||
|
|
||||||
/* we forward our new segment here if we have one */
|
#if 0
|
||||||
if (self->newseg_event) {
|
/* we forward our new segment here if we have one (FIXME: not needed any more) */
|
||||||
|
if (self->segment_event) {
|
||||||
gst_pad_send_event (GST_BASE_RTP_PAYLOAD_SINKPAD (self->rtp),
|
gst_pad_send_event (GST_BASE_RTP_PAYLOAD_SINKPAD (self->rtp),
|
||||||
self->newseg_event);
|
self->segment_event);
|
||||||
self->newseg_event = NULL;
|
self->segment_event = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return TRUE;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* used for catching newsegment events while we don't have a sink, for
|
/* used for catching newsegment events while we don't have a sink, for
|
||||||
* later forwarding it to the sink */
|
* later forwarding it to the sink */
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_a2dp_sink_handle_event (GstPad * pad, GstEvent * event)
|
gst_a2dp_sink_handle_event (GstPad * pad, GstObject * pad_parent,
|
||||||
|
GstEvent * event)
|
||||||
{
|
{
|
||||||
GstA2dpSink *self;
|
GstA2dpSink *self;
|
||||||
GstTagList *taglist = NULL;
|
GstTagList *taglist = NULL;
|
||||||
GstObject *parent;
|
GstObject *parent;
|
||||||
|
|
||||||
self = GST_A2DP_SINK (GST_PAD_PARENT (pad));
|
self = GST_A2DP_SINK (pad_parent);
|
||||||
parent = gst_element_get_parent (GST_ELEMENT (self->sink));
|
parent = gst_element_get_parent (GST_ELEMENT (self->sink));
|
||||||
|
|
||||||
if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT &&
|
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT &&
|
||||||
parent != GST_OBJECT_CAST (self)) {
|
parent != GST_OBJECT_CAST (self)) {
|
||||||
if (self->newseg_event != NULL)
|
if (self->segment_event != NULL)
|
||||||
gst_event_unref (self->newseg_event);
|
gst_event_unref (self->segment_event);
|
||||||
self->newseg_event = gst_event_ref (event);
|
self->segment_event = gst_event_ref (event);
|
||||||
|
|
||||||
} else if (GST_EVENT_TYPE (event) == GST_EVENT_TAG &&
|
} else if (GST_EVENT_TYPE (event) == GST_EVENT_TAG &&
|
||||||
parent != GST_OBJECT_CAST (self)) {
|
parent != GST_OBJECT_CAST (self)) {
|
||||||
|
@ -616,14 +597,43 @@ gst_a2dp_sink_handle_event (GstPad * pad, GstEvent * event)
|
||||||
gst_event_parse_tag (event, &taglist);
|
gst_event_parse_tag (event, &taglist);
|
||||||
gst_tag_list_insert (self->taglist, taglist, GST_TAG_MERGE_REPLACE);
|
gst_tag_list_insert (self->taglist, taglist, GST_TAG_MERGE_REPLACE);
|
||||||
}
|
}
|
||||||
|
} else if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS &&
|
||||||
|
parent != GST_OBJECT_CAST (self)) {
|
||||||
|
GstCaps *caps = NULL;
|
||||||
|
|
||||||
|
/* FIXME: really check for parent != self above? */
|
||||||
|
/* now we know the caps */
|
||||||
|
gst_event_parse_caps (event, &caps);
|
||||||
|
gst_a2dp_sink_init_dynamic_elements (self, caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent != NULL)
|
if (parent != NULL)
|
||||||
gst_object_unref (GST_OBJECT (parent));
|
gst_object_unref (GST_OBJECT (parent));
|
||||||
|
|
||||||
return self->ghostpad_eventfunc (GST_PAD (self->ghostpad), event);
|
return self->ghostpad_eventfunc (self->ghostpad, GST_OBJECT (self), event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_a2dp_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
||||||
|
{
|
||||||
|
GstA2dpSink *sink = GST_A2DP_SINK (parent);
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
if (GST_QUERY_TYPE (query) == GST_QUERY_CAPS) {
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
caps = gst_a2dp_sink_get_caps (sink);
|
||||||
|
gst_query_set_caps_result (query, caps);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
ret = TRUE;
|
||||||
|
} else {
|
||||||
|
ret = sink->ghostpad_queryfunc (pad, parent, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_a2dp_sink_init_caps_filter (GstA2dpSink * self)
|
gst_a2dp_sink_init_caps_filter (GstA2dpSink * self)
|
||||||
{
|
{
|
||||||
|
@ -650,10 +660,10 @@ gst_a2dp_sink_init_fakesink (GstA2dpSink * self)
|
||||||
if (self->fakesink != NULL)
|
if (self->fakesink != NULL)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
g_mutex_lock (self->cb_mutex);
|
g_mutex_lock (&self->cb_mutex);
|
||||||
self->fakesink = gst_a2dp_sink_init_element (self, "fakesink",
|
self->fakesink = gst_a2dp_sink_init_element (self, "fakesink",
|
||||||
"fakesink", self->capsfilter);
|
"fakesink", self->capsfilter);
|
||||||
g_mutex_unlock (self->cb_mutex);
|
g_mutex_unlock (&self->cb_mutex);
|
||||||
|
|
||||||
if (!self->fakesink)
|
if (!self->fakesink)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -664,7 +674,7 @@ gst_a2dp_sink_init_fakesink (GstA2dpSink * self)
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_a2dp_sink_remove_fakesink (GstA2dpSink * self)
|
gst_a2dp_sink_remove_fakesink (GstA2dpSink * self)
|
||||||
{
|
{
|
||||||
g_mutex_lock (self->cb_mutex);
|
g_mutex_lock (&self->cb_mutex);
|
||||||
|
|
||||||
if (self->fakesink != NULL) {
|
if (self->fakesink != NULL) {
|
||||||
gst_element_set_locked_state (self->fakesink, TRUE);
|
gst_element_set_locked_state (self->fakesink, TRUE);
|
||||||
|
@ -674,13 +684,13 @@ gst_a2dp_sink_remove_fakesink (GstA2dpSink * self)
|
||||||
self->fakesink = NULL;
|
self->fakesink = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_mutex_unlock (self->cb_mutex);
|
g_mutex_unlock (&self->cb_mutex);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_a2dp_sink_init (GstA2dpSink * self, GstA2dpSinkClass * klass)
|
gst_a2dp_sink_init (GstA2dpSink * self)
|
||||||
{
|
{
|
||||||
self->sink = NULL;
|
self->sink = NULL;
|
||||||
self->fakesink = NULL;
|
self->fakesink = NULL;
|
||||||
|
@ -689,12 +699,12 @@ gst_a2dp_sink_init (GstA2dpSink * self, GstA2dpSinkClass * klass)
|
||||||
self->transport = NULL;
|
self->transport = NULL;
|
||||||
self->autoconnect = DEFAULT_AUTOCONNECT;
|
self->autoconnect = DEFAULT_AUTOCONNECT;
|
||||||
self->capsfilter = NULL;
|
self->capsfilter = NULL;
|
||||||
self->newseg_event = NULL;
|
self->segment_event = NULL;
|
||||||
self->taglist = NULL;
|
self->taglist = NULL;
|
||||||
self->ghostpad = NULL;
|
self->ghostpad = NULL;
|
||||||
self->sink_is_in_bin = FALSE;
|
self->sink_is_in_bin = FALSE;
|
||||||
|
|
||||||
self->cb_mutex = g_mutex_new ();
|
g_mutex_init (&self->cb_mutex);
|
||||||
|
|
||||||
/* we initialize our capsfilter */
|
/* we initialize our capsfilter */
|
||||||
gst_a2dp_sink_init_caps_filter (self);
|
gst_a2dp_sink_init_caps_filter (self);
|
||||||
|
@ -704,12 +714,4 @@ gst_a2dp_sink_init (GstA2dpSink * self, GstA2dpSinkClass * klass)
|
||||||
gst_a2dp_sink_init_fakesink (self);
|
gst_a2dp_sink_init_fakesink (self);
|
||||||
|
|
||||||
gst_a2dp_sink_init_ghost_pad (self);
|
gst_a2dp_sink_init_ghost_pad (self);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
gst_a2dp_sink_plugin_init (GstPlugin * plugin)
|
|
||||||
{
|
|
||||||
return gst_element_register (plugin, "a2dpsink",
|
|
||||||
GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#define __GST_A2DP_SINK_H__
|
#define __GST_A2DP_SINK_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/rtp/gstbasertppayload.h>
|
|
||||||
#include "gstavdtpsink.h"
|
#include "gstavdtpsink.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
@ -47,7 +46,7 @@ typedef struct _GstA2dpSinkClass GstA2dpSinkClass;
|
||||||
struct _GstA2dpSink {
|
struct _GstA2dpSink {
|
||||||
GstBin bin;
|
GstBin bin;
|
||||||
|
|
||||||
GstBaseRTPPayload *rtp;
|
GstElement *rtp;
|
||||||
GstAvdtpSink *sink;
|
GstAvdtpSink *sink;
|
||||||
GstElement *capsfilter;
|
GstElement *capsfilter;
|
||||||
GstElement *fakesink;
|
GstElement *fakesink;
|
||||||
|
@ -57,16 +56,16 @@ struct _GstA2dpSink {
|
||||||
gboolean autoconnect;
|
gboolean autoconnect;
|
||||||
gboolean sink_is_in_bin;
|
gboolean sink_is_in_bin;
|
||||||
|
|
||||||
GstGhostPad *ghostpad;
|
GstPad *ghostpad;
|
||||||
GstPadSetCapsFunction ghostpad_setcapsfunc;
|
GstPadQueryFunction ghostpad_queryfunc;
|
||||||
GstPadEventFunction ghostpad_eventfunc;
|
GstPadEventFunction ghostpad_eventfunc;
|
||||||
|
|
||||||
GstEvent *newseg_event;
|
GstEvent *segment_event;
|
||||||
/* Store the tags received before the a2dpsender sink is created
|
/* Store the tags received before the a2dpsender sink is created
|
||||||
* when it is created we forward this to it */
|
* when it is created we forward this to it */
|
||||||
GstTagList *taglist;
|
GstTagList *taglist;
|
||||||
|
|
||||||
GMutex *cb_mutex;
|
GMutex cb_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstA2dpSinkClass {
|
struct _GstA2dpSinkClass {
|
||||||
|
|
|
@ -25,26 +25,21 @@
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* FIXME: check which includes are really required */
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include <bluetooth/bluetooth.h>
|
|
||||||
|
|
||||||
#include <gst/rtp/gstrtpbuffer.h>
|
|
||||||
|
|
||||||
#include <dbus/dbus.h>
|
#include <dbus/dbus.h>
|
||||||
|
|
||||||
#include "rtp.h"
|
|
||||||
#include "a2dp-codecs.h"
|
#include "a2dp-codecs.h"
|
||||||
|
|
||||||
#include "gstpragma.h"
|
|
||||||
#include "gstavdtpsink.h"
|
#include "gstavdtpsink.h"
|
||||||
|
|
||||||
|
#include <gst/rtp/rtp.h>
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (avdtp_sink_debug);
|
GST_DEBUG_CATEGORY_STATIC (avdtp_sink_debug);
|
||||||
#define GST_CAT_DEFAULT avdtp_sink_debug
|
#define GST_CAT_DEFAULT avdtp_sink_debug
|
||||||
|
|
||||||
|
@ -56,11 +51,11 @@ GST_DEBUG_CATEGORY_STATIC (avdtp_sink_debug);
|
||||||
#define DEFAULT_AUTOCONNECT TRUE
|
#define DEFAULT_AUTOCONNECT TRUE
|
||||||
|
|
||||||
#define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START { \
|
#define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START { \
|
||||||
g_mutex_lock(s->sink_lock); \
|
g_mutex_lock(&s->sink_lock); \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
#define GST_AVDTP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \
|
#define GST_AVDTP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \
|
||||||
g_mutex_unlock(s->sink_lock); \
|
g_mutex_unlock(&s->sink_lock); \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
struct bluetooth_data
|
struct bluetooth_data
|
||||||
|
@ -87,13 +82,8 @@ enum
|
||||||
PROP_TRANSPORT
|
PROP_TRANSPORT
|
||||||
};
|
};
|
||||||
|
|
||||||
GST_BOILERPLATE (GstAvdtpSink, gst_avdtp_sink, GstBaseSink, GST_TYPE_BASE_SINK);
|
#define parent_class gst_avdtp_sink_parent_class
|
||||||
|
G_DEFINE_TYPE (GstAvdtpSink, gst_avdtp_sink, GST_TYPE_BASE_SINK);
|
||||||
static const GstElementDetails avdtp_sink_details =
|
|
||||||
GST_ELEMENT_DETAILS ("Bluetooth AVDTP sink",
|
|
||||||
"Sink/Audio",
|
|
||||||
"Plays audio to an A2DP device",
|
|
||||||
"Marcel Holtmann <marcel@holtmann.org>");
|
|
||||||
|
|
||||||
static GstStaticPadTemplate avdtp_sink_factory =
|
static GstStaticPadTemplate avdtp_sink_factory =
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
||||||
|
@ -115,17 +105,6 @@ static GstStaticPadTemplate avdtp_sink_factory =
|
||||||
GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
|
GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
|
||||||
"clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\""));
|
"clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\""));
|
||||||
|
|
||||||
static void
|
|
||||||
gst_avdtp_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 (&avdtp_sink_factory));
|
|
||||||
|
|
||||||
gst_element_class_set_details (element_class, &avdtp_sink_details);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_avdtp_sink_transport_release (GstAvdtpSink * self)
|
gst_avdtp_sink_transport_release (GstAvdtpSink * self)
|
||||||
{
|
{
|
||||||
|
@ -197,7 +176,7 @@ gst_avdtp_sink_finalize (GObject * object)
|
||||||
if (self->transport)
|
if (self->transport)
|
||||||
g_free (self->transport);
|
g_free (self->transport);
|
||||||
|
|
||||||
g_mutex_free (self->sink_lock);
|
g_mutex_clear (&self->sink_lock);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
@ -265,7 +244,7 @@ gst_avdtp_sink_parse_sbc_raw (GstAvdtpSink * self)
|
||||||
GValue *list;
|
GValue *list;
|
||||||
gboolean mono, stereo;
|
gboolean mono, stereo;
|
||||||
|
|
||||||
structure = gst_structure_empty_new ("audio/x-sbc");
|
structure = gst_structure_new_empty ("audio/x-sbc");
|
||||||
value = g_value_init (g_new0 (GValue, 1), G_TYPE_STRING);
|
value = g_value_init (g_new0 (GValue, 1), G_TYPE_STRING);
|
||||||
|
|
||||||
/* mode */
|
/* mode */
|
||||||
|
@ -433,7 +412,7 @@ gst_avdtp_sink_parse_mpeg_raw (GstAvdtpSink * self)
|
||||||
|
|
||||||
GST_LOG_OBJECT (self, "parsing mpeg caps");
|
GST_LOG_OBJECT (self, "parsing mpeg caps");
|
||||||
|
|
||||||
structure = gst_structure_empty_new ("audio/mpeg");
|
structure = gst_structure_new_empty ("audio/mpeg");
|
||||||
value = g_new0 (GValue, 1);
|
value = g_new0 (GValue, 1);
|
||||||
g_value_init (value, G_TYPE_INT);
|
g_value_init (value, G_TYPE_INT);
|
||||||
|
|
||||||
|
@ -939,20 +918,29 @@ gst_avdtp_sink_preroll (GstBaseSink * basesink, GstBuffer * buffer)
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_avdtp_sink_render (GstBaseSink * basesink, GstBuffer * buffer)
|
gst_avdtp_sink_render (GstBaseSink * basesink, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
|
GstFlowReturn flow_ret = GST_FLOW_OK;
|
||||||
GstAvdtpSink *self = GST_AVDTP_SINK (basesink);
|
GstAvdtpSink *self = GST_AVDTP_SINK (basesink);
|
||||||
|
GstMapInfo map;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = g_io_channel_unix_get_fd (self->stream);
|
if (!gst_buffer_map (buffer, &map, GST_MAP_READ))
|
||||||
|
|
||||||
ret = write (fd, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
|
|
||||||
if (ret < 0) {
|
|
||||||
GST_ERROR_OBJECT (self, "Error while writting to socket: %s",
|
|
||||||
strerror (errno));
|
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
|
|
||||||
|
/* FIXME: temporary sanity check */
|
||||||
|
g_assert (!(g_io_channel_get_flags (self->stream) & G_IO_FLAG_NONBLOCK));
|
||||||
|
|
||||||
|
/* FIXME: why not use g_io_channel_write_chars() instead? */
|
||||||
|
fd = g_io_channel_unix_get_fd (self->stream);
|
||||||
|
ret = write (fd, map.data, map.size);
|
||||||
|
if (ret < 0) {
|
||||||
|
/* FIXME: since this is probably fatal, shouldn't we post an error here? */
|
||||||
|
GST_ERROR_OBJECT (self, "Error writing to socket: %s", g_strerror (errno));
|
||||||
|
flow_ret = GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
gst_buffer_unmap (buffer, &map);
|
||||||
|
return flow_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -966,29 +954,11 @@ gst_avdtp_sink_unlock (GstBaseSink * basesink)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
|
||||||
gst_avdtp_sink_buffer_alloc (GstBaseSink * basesink,
|
|
||||||
guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf)
|
|
||||||
{
|
|
||||||
GstAvdtpSink *self = GST_AVDTP_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
|
static void
|
||||||
gst_avdtp_sink_class_init (GstAvdtpSinkClass * klass)
|
gst_avdtp_sink_class_init (GstAvdtpSinkClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
|
GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS (klass);
|
||||||
|
|
||||||
parent_class = g_type_class_peek_parent (klass);
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
@ -1004,9 +974,6 @@ gst_avdtp_sink_class_init (GstAvdtpSinkClass * klass)
|
||||||
basesink_class->unlock = GST_DEBUG_FUNCPTR (gst_avdtp_sink_unlock);
|
basesink_class->unlock = GST_DEBUG_FUNCPTR (gst_avdtp_sink_unlock);
|
||||||
basesink_class->event = GST_DEBUG_FUNCPTR (gst_avdtp_sink_event);
|
basesink_class->event = GST_DEBUG_FUNCPTR (gst_avdtp_sink_event);
|
||||||
|
|
||||||
basesink_class->buffer_alloc =
|
|
||||||
GST_DEBUG_FUNCPTR (gst_avdtp_sink_buffer_alloc);
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class, PROP_DEVICE,
|
g_object_class_install_property (object_class, PROP_DEVICE,
|
||||||
g_param_spec_string ("device", "Device",
|
g_param_spec_string ("device", "Device",
|
||||||
"Bluetooth remote device address", NULL, G_PARAM_READWRITE));
|
"Bluetooth remote device address", NULL, G_PARAM_READWRITE));
|
||||||
|
@ -1023,10 +990,17 @@ gst_avdtp_sink_class_init (GstAvdtpSinkClass * klass)
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (avdtp_sink_debug, "avdtpsink", 0,
|
GST_DEBUG_CATEGORY_INIT (avdtp_sink_debug, "avdtpsink", 0,
|
||||||
"A2DP headset sink element");
|
"A2DP headset sink element");
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
gst_static_pad_template_get (&avdtp_sink_factory));
|
||||||
|
|
||||||
|
gst_element_class_set_static_metadata (element_class, "Bluetooth AVDTP sink",
|
||||||
|
"Sink/Audio", "Plays audio to an A2DP device",
|
||||||
|
"Marcel Holtmann <marcel@holtmann.org>");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_avdtp_sink_init (GstAvdtpSink * self, GstAvdtpSinkClass * klass)
|
gst_avdtp_sink_init (GstAvdtpSink * self)
|
||||||
{
|
{
|
||||||
self->device = NULL;
|
self->device = NULL;
|
||||||
self->transport = NULL;
|
self->transport = NULL;
|
||||||
|
@ -1038,7 +1012,7 @@ gst_avdtp_sink_init (GstAvdtpSink * self, GstAvdtpSinkClass * klass)
|
||||||
|
|
||||||
self->autoconnect = DEFAULT_AUTOCONNECT;
|
self->autoconnect = DEFAULT_AUTOCONNECT;
|
||||||
|
|
||||||
self->sink_lock = g_mutex_new ();
|
g_mutex_init (&self->sink_lock);
|
||||||
|
|
||||||
/* FIXME this is for not synchronizing with clock, should be tested
|
/* FIXME this is for not synchronizing with clock, should be tested
|
||||||
* with devices to see the behaviour
|
* with devices to see the behaviour
|
||||||
|
|
|
@ -66,7 +66,7 @@ struct _GstAvdtpSink {
|
||||||
|
|
||||||
GstCaps *dev_caps;
|
GstCaps *dev_caps;
|
||||||
|
|
||||||
GMutex *sink_lock;
|
GMutex sink_lock;
|
||||||
|
|
||||||
guint watch_id;
|
guint watch_id;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue