📄 gsta2dpsink.c
字号:
} 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_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;}static gboolean gst_a2dp_sink_get_capabilities(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); 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"); } 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"); 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; } memcpy(&self->data->caps, rsp, sizeof(*rsp)); if (!gst_a2dp_sink_update_caps(self)) { GST_WARNING_OBJECT(self, "failed to update capabilities"); return FALSE; } 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; } 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_sink_get_capabilities(self)) goto failed; return TRUE;failed: bt_audio_service_close(sk); return FALSE;}static gboolean gst_a2dp_sink_stream_start(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"); 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"); 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; } 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; } 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 (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; } memcpy(&self->data->cfg, rsp, sizeof(*rsp)); GST_DEBUG_OBJECT(self, "configuration set"); 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); if (!ret) return GST_FLOW_ERROR; return GST_FLOW_OK;}static int gst_a2dp_sink_avdtp_write(GstA2dpSink *self){ 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;}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; encoded = GST_BUFFER_SIZE(buffer); if (data->count + encoded >= data->cfg.link_mtu) { ret = gst_a2dp_sink_avdtp_write(self); if (ret < 0) return GST_FLOW_ERROR; } 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); return ret;}static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink){ GstA2dpSink *self = GST_A2DP_SINK(basesink); if (self->stream != NULL) g_io_channel_flush (self->stream, NULL); return TRUE;}static void gst_a2dp_sink_class_init(GstA2dpSinkClass *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_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)); GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0, "A2DP sink element");}static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass){ self->device = NULL; self->data = NULL; self->stream = NULL; self->dev_caps = NULL; self->sink_lock = g_mutex_new();}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); } return error;}static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *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 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); return status;}static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, bt_audio_msg_header_t *outmsg, int expected_type){ 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -