static gboolean gst_a2dp_sink_init_avdtp_sink (GstA2dpSink * self) { GstElement *sink; if (self->sink == NULL) sink = gst_element_factory_make ("avdtpsink", "avdtpsink"); else sink = GST_ELEMENT (self->sink); if (sink == NULL) { GST_ERROR_OBJECT (self, "Couldn't create avdtpsink"); return FALSE; } if (!gst_bin_add (GST_BIN (self), sink)) { GST_ERROR_OBJECT (self, "failed to add avdtpsink " "to the bin"); goto cleanup_and_fail; } self->sink = GST_AVDTP_SINK (sink); g_object_set (G_OBJECT (self->sink), "device", self->device, NULL); g_object_set (G_OBJECT (self->sink), "transport", self->transport, NULL); gst_element_sync_state_with_parent (sink); return TRUE; cleanup_and_fail: if (sink != NULL) g_object_unref (G_OBJECT (sink)); return FALSE; }
static void gst_avdtp_sink_tag(const GstTagList *taglist, const gchar *tag, gpointer user_data) { gboolean crc; gchar *channel_mode = NULL; GstAvdtpSink *self = GST_AVDTP_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"); return; } gst_avdtp_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"); return; } self->channel_mode = gst_avdtp_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_avdtp_sink_unlock(GstBaseSink *basesink) { GstAvdtpSink *self = GST_AVDTP_SINK(basesink); if (self->stream != NULL) g_io_channel_flush(self->stream, NULL); return TRUE; }
static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self) { GstElement *sink; /* check if we don't need a new sink */ if (self->sink_is_in_bin) return TRUE; if (self->sink == NULL) sink = gst_element_factory_make("avdtpsink", "avdtpsink"); else sink = GST_ELEMENT(self->sink); if (sink == NULL) { GST_ERROR_OBJECT(self, "Couldn't create avdtpsink"); return FALSE; } if (!gst_bin_add(GST_BIN(self), sink)) { GST_ERROR_OBJECT(self, "failed to add avdtpsink " "to the bin"); goto cleanup_and_fail; } if (gst_element_set_state(sink, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) { GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready"); goto remove_element_and_fail; } if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) { GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay " "to avdtpsink"); goto remove_element_and_fail; } self->sink = GST_AVDTP_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); return TRUE; remove_element_and_fail: gst_element_set_state(sink, GST_STATE_NULL); gst_bin_remove(GST_BIN(self), sink); return FALSE; cleanup_and_fail: if (sink != NULL) g_object_unref(G_OBJECT(sink)); return FALSE; }
static gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { if (cond & G_IO_HUP || cond & G_IO_NVAL) return FALSE; else if (cond & G_IO_ERR) GST_WARNING_OBJECT(GST_AVDTP_SINK(data), "Untreated callback G_IO_ERR"); return TRUE; }
static void gst_avdtp_sink_finalize(GObject *object) { GstAvdtpSink *self = GST_AVDTP_SINK(object); if (self->data) gst_avdtp_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 gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { GstAvdtpSink *sink; if (cond & G_IO_HUP || cond & G_IO_NVAL) return FALSE; else if (cond & G_IO_ERR) { sink = GST_AVDTP_SINK(data); GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); } return TRUE; }
static gboolean gst_avdtp_sink_event(GstBaseSink *basesink, GstEvent *event) { GstAvdtpSink *self = GST_AVDTP_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_avdtp_sink_tag, self); } return TRUE; }
static GstFlowReturn gst_avdtp_sink_preroll(GstBaseSink *basesink, GstBuffer *buffer) { GstAvdtpSink *sink = GST_AVDTP_SINK(basesink); gboolean ret; GST_AVDTP_SINK_MUTEX_LOCK(sink); ret = gst_avdtp_sink_stream_start(sink); GST_AVDTP_SINK_MUTEX_UNLOCK(sink); if (!ret) return GST_FLOW_ERROR; return GST_FLOW_OK; }
static void gst_avdtp_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GstAvdtpSink *sink = GST_AVDTP_SINK(object); switch (prop_id) { case PROP_DEVICE: g_value_set_string(value, sink->device); break; case PROP_AUTOCONNECT: g_value_set_boolean(value, sink->autoconnect); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } }
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 GstFlowReturn gst_avdtp_sink_render(GstBaseSink *basesink, GstBuffer *buffer) { GstAvdtpSink *self = GST_AVDTP_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_avdtp_sink_start(GstBaseSink *basesink) { GstAvdtpSink *self = GST_AVDTP_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); self->stream = NULL; self->stream_caps = NULL; self->mp3_using_crc = -1; self->channel_mode = -1; if (!gst_avdtp_sink_get_capabilities(self)) { GST_ERROR_OBJECT(self, "failed to get capabilities " "from device"); goto failed; } return TRUE; failed: bt_audio_service_close(sk); return FALSE; }
static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink) { GstAvdtpSink *self = GST_AVDTP_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_shutdown(self->stream, TRUE, NULL); 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 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(); gst_a2dp_sink_init_fakesink(self); break; case GST_STATE_CHANGE_NULL_TO_READY: self->sink_is_in_bin = FALSE; self->sink = GST_AVDTP_SINK(gst_element_factory_make( "avdtpsink", "avdtpsink")); if (self->sink == NULL) { GST_WARNING_OBJECT(self, "failed to create avdtpsink"); return GST_STATE_CHANGE_FAILURE; } if (self->device != NULL) gst_avdtp_sink_set_device(self->sink, self->device); g_object_set(G_OBJECT(self->sink), "auto-connect", self->autoconnect, NULL); ret = gst_element_set_state(GST_ELEMENT(self->sink), GST_STATE_READY); break; default: break; } 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; } gst_a2dp_sink_remove_fakesink(self); break; case GST_STATE_CHANGE_READY_TO_NULL: if (self->sink_is_in_bin) { if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink))) GST_WARNING_OBJECT(self, "Failed to remove " "avdtpsink 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: break; } return ret; }