📄 gstavdtpsink.c
字号:
GST_DEBUG_OBJECT(self, "stream start packet sent"); io_error = gst_avdtp_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_avdtp_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_avdtp_sink_conf_recv_stream_fd(self)) return FALSE; return TRUE;}static gboolean gst_avdtp_sink_init_mp3_pkt_conf( GstAvdtpSink *self, GstCaps *caps, mpeg_capabilities_t *pkt){ const GValue *value = NULL; gint rate, layer; const gchar* name; GstStructure *structure = gst_caps_get_structure(caps, 0); name = gst_structure_get_name(structure); if (!(IS_MPEG_AUDIO(name))) { GST_ERROR_OBJECT(self, "Unexpected format %s, " "was expecting mp3", name); return FALSE; } /* 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; return TRUE;}static gboolean gst_avdtp_sink_configure(GstAvdtpSink *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; GstStructure *structure; 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); structure = gst_caps_get_structure(caps, 0); if (gst_structure_has_name(structure, "audio/x-sbc")) ret = gst_avdtp_sink_init_sbc_pkt_conf(self, caps, &req->sbc_capabilities); else if (gst_structure_has_name(structure, "audio/mpeg")) ret = gst_avdtp_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"); return FALSE; } io_error = gst_avdtp_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_avdtp_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_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 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_unlock(GstBaseSink *basesink){ GstAvdtpSink *self = GST_AVDTP_SINK(basesink); if (self->stream != NULL) g_io_channel_flush (self->stream, NULL); 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 gst_avdtp_sink_class_init(GstAvdtpSinkClass *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_avdtp_sink_finalize); object_class->set_property = GST_DEBUG_FUNCPTR( gst_avdtp_sink_set_property); object_class->get_property = GST_DEBUG_FUNCPTR( gst_avdtp_sink_get_property); basesink_class->start = GST_DEBUG_FUNCPTR(gst_avdtp_sink_start); basesink_class->stop = GST_DEBUG_FUNCPTR(gst_avdtp_sink_stop); basesink_class->render = GST_DEBUG_FUNCPTR( gst_avdtp_sink_render); basesink_class->preroll = GST_DEBUG_FUNCPTR( gst_avdtp_sink_preroll); basesink_class->unlock = GST_DEBUG_FUNCPTR( gst_avdtp_sink_unlock); 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_param_spec_string("device", "Device", "Bluetooth remote device address", NULL, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_AUTOCONNECT, g_param_spec_boolean("auto-connect", "Auto-connect", "Automatically attempt to connect " "to device", DEFAULT_AUTOCONNECT, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0, "A2DP headset sink element");}static void gst_avdtp_sink_init(GstAvdtpSink *self, GstAvdtpSinkClass *klass){ self->device = NULL; self->data = NULL; self->stream = NULL; self->dev_caps = NULL; self->autoconnect = DEFAULT_AUTOCONNECT; self->sink_lock = g_mutex_new(); /* FIXME this is for not synchronizing with clock, should be tested * with devices to see the behaviour gst_base_sink_set_sync(GST_BASE_SINK(self), FALSE); */}static GIOError gst_avdtp_sink_audioservice_send( GstAvdtpSink *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_avdtp_sink_audioservice_recv( GstAvdtpSink *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_avdtp_sink_audioservice_expect( GstAvdtpSink *self, bt_audio_msg_header_t *outmsg, int expected_type){ GIOError status; status = gst_avdtp_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_avdtp_sink_plugin_init (GstPlugin * plugin){ return gst_element_register (plugin, "avdtpsink", GST_RANK_NONE, GST_TYPE_AVDTP_SINK);}/* public functions */GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink){ if (sink->dev_caps == NULL) return NULL; return gst_caps_copy(sink->dev_caps);}gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *self, GstCaps *caps){ gboolean ret; GST_DEBUG_OBJECT(self, "setting device caps"); GST_AVDTP_SINK_MUTEX_LOCK(self); ret = gst_avdtp_sink_configure(self, caps); if (self->stream_caps) gst_caps_unref(self->stream_caps); self->stream_caps = gst_caps_ref(caps); GST_AVDTP_SINK_MUTEX_UNLOCK(self); return ret;}guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink){ return sink->data->link_mtu;}void gst_avdtp_sink_set_device(GstAvdtpSink *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_avdtp_sink_get_device(GstAvdtpSink *self){ return g_strdup(self->device);}void gst_avdtp_sink_set_crc(GstAvdtpSink *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_WARNING_OBJECT(self, "crc changed during stream"); return; } self->mp3_using_crc = new_crc;}void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, const gchar *mode){ gint new_mode; new_mode = gst_avdtp_sink_get_channel_mode(mode); if (self->channel_mode != -1 && new_mode != self->channel_mode) { GST_WARNING_OBJECT(self, "channel mode changed during stream"); return; } self->channel_mode = new_mode; if (self->channel_mode == -1) GST_WARNING_OBJECT(self, "Received invalid channel " "mode: %s", mode);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -