Index: audio/gsta2dpsink.c =================================================================== RCS file: /cvsroot/bluez/utils/audio/gsta2dpsink.c,v retrieving revision 1.3 diff -u -r1.3 gsta2dpsink.c --- audio/gsta2dpsink.c 26 Aug 2007 14:14:34 -0000 1.3 +++ audio/gsta2dpsink.c 5 Oct 2007 18:49:39 -0000 @@ -28,8 +28,11 @@ #include #include #include +#include +#include #include "ipc.h" +#include "sbc.h" #include "gsta2dpsink.h" @@ -37,13 +40,117 @@ #define GST_CAT_DEFAULT a2dp_sink_debug #define DEFAULT_DEVICE "default" +#define BUFFER_SIZE 2048 + +#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 + +#define GST_A2DP_SINK_WAIT_CON_END(s) G_STMT_START { \ + s->waiting_con_conf = TRUE; \ + g_cond_wait (s->con_conf_end, s->sink_lock); \ + s->waiting_con_conf = FALSE; \ + } G_STMT_END + +#define GST_A2DP_SINK_CONFIGURATION_FAIL(s) G_STMT_START { \ + s->con_state = NOT_CONFIGURED; \ + g_cond_signal (s->con_conf_end); \ + } G_STMT_END + +#define GST_A2DP_SINK_CONFIGURATION_SUCCESS(s) G_STMT_START { \ + s->con_state = CONFIGURED; \ + g_cond_signal (s->con_conf_end); \ + } G_STMT_END + +struct bluetooth_a2dp { + sbc_t sbc; /* Codec data */ + int codesize; /* SBC codesize */ + int samples; /* Number of encoded samples */ + uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + int 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*/ +}; +struct bluetooth_data { + struct ipc_data_cfg cfg; /* Bluetooth device config */ + uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ + int count; /* Transfer buffer counter */ + struct bluetooth_a2dp a2dp; /* A2DP data */ + + pthread_t hw_thread; /* Makes virtual hw pointer move */ + int pipefd[2]; /* Inter thread communication */ + int stopped; +}; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { + uint8_t cc:4; + uint8_t x:1; + uint8_t p:1; + uint8_t v:2; + + uint8_t pt:7; + uint8_t m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t frame_count:4; + uint8_t rfa0:1; + uint8_t is_last_fragment:1; + uint8_t is_first_fragment:1; + uint8_t is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + uint8_t v:2; + uint8_t p:1; + uint8_t x:1; + uint8_t cc:4; + + uint8_t m:1; + uint8_t pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t is_fragmented:1; + uint8_t is_first_fragment:1; + uint8_t is_last_fragment:1; + uint8_t rfa0:1; + uint8_t frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +#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(GstA2dpSink, gst_a2dp_sink, GstAudioSink, GST_TYPE_AUDIO_SINK); +GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBaseSink, GST_TYPE_BASE_SINK); static const GstElementDetails a2dp_sink_details = GST_ELEMENT_DETAILS("Bluetooth A2DP sink", @@ -77,14 +184,45 @@ gst_element_class_set_details(element_class, &a2dp_sink_details); } +static void gst_a2dp_sink_bluetooth_exit(GstA2dpSink *sink) +{ + struct bluetooth_a2dp *a2dp = &sink->data->a2dp; + + if (sink->stream_fd >= 0) { + close(sink->stream_fd); + sink->stream_fd = -1; + } + + if (sink->data->hw_thread) { + pthread_cancel(sink->data->hw_thread); + pthread_join(sink->data->hw_thread, 0); + } + + if (sink->data->cfg.codec == CFG_CODEC_SBC) + sbc_finish(&a2dp->sbc); + + if (sink->data->pipefd[0] > 0) + close(sink->data->pipefd[0]); + + if (sink->data->pipefd[1] > 0) + close(sink->data->pipefd[1]); + + g_free(sink->data); +} + static void gst_a2dp_sink_finalize(GObject *object) { GstA2dpSink *sink = GST_A2DP_SINK(object); g_io_channel_close(sink->server); + g_io_channel_unref(sink->server); + gst_a2dp_sink_bluetooth_exit(sink); g_free(sink->device); + g_cond_free(sink->con_conf_end); + g_mutex_free(sink->sink_lock); + G_OBJECT_CLASS(parent_class)->finalize(object); } @@ -124,62 +262,447 @@ } } -static gboolean gst_a2dp_sink_open(GstAudioSink *self) +static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) +{ + char cmsg_b[CMSG_SPACE(sizeof(int))], m; + int err, ret; + struct iovec iov = { &m, sizeof(m) }; + struct msghdr msgh; + struct cmsghdr *cmsg; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(sizeof(int)); + + ret = recvmsg(g_io_channel_unix_get_fd(sink->server), &msgh, 0); + if (ret < 0) { + err = errno; + GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", + strerror(err), err); + return -err; + } + + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + sink->stream_fd = (*(int *) CMSG_DATA(cmsg)); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", + sink->stream_fd); + return 0; + } + } + + return -EINVAL; +} + +static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *sink, + struct ipc_codec_sbc *sbc) { - GstA2dpSink *sink = GST_A2DP_SINK(self); + struct bluetooth_a2dp *a2dp = &sink->data->a2dp; + struct ipc_data_cfg *cfg = &sink->data->cfg; + + if (cfg == NULL) { + GST_ERROR_OBJECT(sink, "Error getting codec parameters"); + return -1; + } + + if (cfg->codec != CFG_CODEC_SBC) + return -1; - printf("device %s\n", sink->device); - printf("open\n"); + /* FIXME: init using flags? */ + sbc_init(&a2dp->sbc, 0); + a2dp->sbc.rate = cfg->rate; + a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; + if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) + a2dp->sbc.joint = 1; + a2dp->sbc.allocation = sbc->allocation; + a2dp->sbc.subbands = sbc->subbands; + a2dp->sbc.blocks = sbc->blocks; + a2dp->sbc.bitpool = sbc->bitpool; + a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * + a2dp->sbc.channels * 2; + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + + GST_DEBUG_OBJECT(sink, "Codec parameters: \ + \tallocation=%u\n\tsubbands=%u\n \ + \tblocks=%u\n\tbitpool=%u\n", + a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, + a2dp->sbc.bitpool); + + return 0; +} + +static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, + GstCaps *caps, struct ipc_packet *pkt) +{ + + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + const GValue *value = NULL; + const char *pref, *name; + 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; + } + + strncpy(pkt->device, sink->device, 18); + pkt->role = PKT_ROLE_HIFI; + + value = gst_structure_get_value(structure, "rate"); + cfg->rate = g_value_get_int(value); + + value = gst_structure_get_value(structure, "mode"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->mode = CFG_MODE_AUTO; + else if (strcmp(pref, "mono") == 0) + cfg->mode = CFG_MODE_MONO; + else if (strcmp(pref, "dual") == 0) + cfg->mode = CFG_MODE_DUAL_CHANNEL; + else if (strcmp(pref, "stereo") == 0) + cfg->mode = CFG_MODE_STEREO; + else if (strcmp(pref, "joint") == 0) + cfg->mode = CFG_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) + sbc->allocation = CFG_ALLOCATION_AUTO; + else if (strcmp(pref, "loudness") == 0) + sbc->allocation = CFG_ALLOCATION_LOUDNESS; + else if (strcmp(pref, "snr") == 0) + sbc->allocation = CFG_ALLOCATION_SNR; + else { + GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "subbands"); + sbc->subbands = g_value_get_int(value); + + value = gst_structure_get_value(structure, "blocks"); + sbc->blocks = g_value_get_int(value); +/* FIXME how can I obtain the bitpool ? + if (strcmp(id, "bitpool") == 0) { + if (snd_config_get_string(n, &bitpool) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + sbc->bitpool = atoi(bitpool); + continue; + } + + SNDERR("Unknown field %s", id); + return -EINVAL; + } +*/ + sbc->bitpool = 32; + + pkt->length = sizeof(*cfg) + sizeof(*sbc); + pkt->type = PKT_TYPE_CFG_REQ; + pkt->error = PKT_ERROR_NONE; return TRUE; + } -static gboolean gst_a2dp_sink_prepare(GstAudioSink *self, - GstRingBufferSpec *spec) +static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) { - printf("perpare\n"); - printf("rate %d\n", spec->rate); - printf("channels %d\n", spec->channels); + g_print("start\n"); return TRUE; } -static gboolean gst_a2dp_sink_unprepare(GstAudioSink *self) +static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) { - printf("unprepare\n"); + g_print("stop\n"); return TRUE; } -static gboolean gst_a2dp_sink_close(GstAudioSink *self) +static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) { - printf("close\n"); + gchar buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + gboolean ret; + GIOError io_error; + + g_assert(sink->con_state == NOT_CONFIGURED); + + pkt = (void*) buf; + ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt); + if (!ret) { + GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps \ + to packet configuration"); + return FALSE; + } + + sink->con_state = CONFIGURING_INIT; + + io_error = g_io_channel_write(sink->server, (gchar*)pkt, + sizeof(*pkt) + pkt->length, &ret); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(sink, "Error ocurred while sending \ + configurarion packet"); + sink->con_state = NOT_CONFIGURED; + return FALSE; + } + + GST_DEBUG_OBJECT(sink, "%d bytes sent", ret); + sink->con_state = CONFIGURING_SENT_CONF; return TRUE; } -static guint gst_a2dp_sink_write(GstAudioSink *self, - gpointer data, guint length) +static gboolean gst_a2dp_sink_start_dev_conf(GstA2dpSink *sink, GstCaps *caps) { - return 0; + gboolean ret; + + g_assert(sink->con_state == NOT_CONFIGURED); + + GST_DEBUG_OBJECT(sink, "starting device configuration"); + + ret = gst_a2dp_sink_send_conf_pkt(sink, caps); + + return ret; } -static guint gst_a2dp_sink_delay(GstAudioSink *audiosink) +static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, + GstBuffer *buffer) { - printf("delay\n"); + GstA2dpSink *sink; + sink = GST_A2DP_SINK(basesink); - return 0; + GST_A2DP_SINK_MUTEX_LOCK(sink); + + if (sink->con_state == NOT_CONFIGURED) + gst_a2dp_sink_start_dev_conf(sink, GST_BUFFER_CAPS(buffer)); + + /* wait for the connection process to finish */ + if (sink->con_state != CONFIGURED) + GST_A2DP_SINK_WAIT_CON_END(sink); + + GST_A2DP_SINK_MUTEX_UNLOCK(sink); + + if (sink->con_state != CONFIGURED) + return GST_FLOW_ERROR; + + return GST_FLOW_OK; } -static void gst_a2dp_sink_reset(GstAudioSink *audiosink) +static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, + GstBuffer *buffer) { - printf("reset\n"); + g_print("render\n"); + + return GST_FLOW_OK; +} + +static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) +{ + gchar buf[IPC_MTU]; + GIOError io_error; + guint ret; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_cfg *cfg = (void *) pkt->data; + + memset(buf, 0, sizeof(buf)); + + io_error = g_io_channel_read(sink->server, (gchar*)buf, + sizeof(*pkt) + sizeof(*cfg), &ret); + if (io_error != G_IO_ERROR_NONE && ret > 0) { + GST_ERROR_OBJECT(sink, "Error ocurred while receiving \ + configurarion packet answer"); + return FALSE; + } + + sink->total = ret; + if (pkt->type != PKT_TYPE_CFG_RSP) { + GST_ERROR_OBJECT(sink, "Unexpected packet type %d \ + received", pkt->type); + return FALSE; + } + + if (pkt->error != PKT_ERROR_NONE) { + GST_ERROR_OBJECT(sink, "Error %d while configuring \ + device", pkt->error); + return FALSE; + } + + if (cfg->codec != CFG_CODEC_SBC) { + GST_ERROR_OBJECT(sink, "Unsupported format"); + return FALSE; + } + + return TRUE; +} + +static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) +{ + gchar buf[IPC_MTU]; + GIOError io_error; + guint ret=0; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + + io_error = g_io_channel_read(sink->server, (gchar*)sbc, + sizeof(*sbc), &ret); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(sink, "Error while reading data from socket \ + %s (%d)", strerror(errno), errno); + return FALSE; + } else if (ret == 0) { + GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); + return FALSE; + } + + sink->total += ret; + GST_DEBUG_OBJECT(sink, "OK - %d bytes received", sink->total); + + if (pkt->length != (sink->total - sizeof(struct ipc_packet))) { + GST_ERROR_OBJECT(sink, "Error while configuring device: \ + packet size doesn't match"); + return FALSE; + } + + memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); + + GST_DEBUG_OBJECT(sink, "Device configuration:\n\tfd=%d\n\tfd_opt=%u\n\ + \tpkt_len=%u\n\tsample_size=%u\n\trate=%u", + sink->stream_fd, sink->data->cfg.fd_opt, \ + sink->data->cfg.pkt_len, sink->data->cfg.sample_size, \ + sink->data->cfg.rate); + + if (sink->data->cfg.codec == CFG_CODEC_SBC) { + ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); + if (ret < 0) + return FALSE; + + } + return TRUE; +} + +static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *sink) +{ + guint ret=0; + ret = gst_a2dp_sink_bluetooth_recvmsg_fd(sink); + if (ret < 0) + return FALSE; + + if (sink->stream_fd == -1) { + GST_ERROR_OBJECT(sink, "Error while configuring device: \ + could not acquire audio socket"); + return FALSE; + } + + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + while (recv(sink->stream_fd, sink->data->buffer, + sink->data->cfg.pkt_len, MSG_DONTWAIT) > 0); + + memset(sink->data->buffer, 0, sizeof(sink->data->buffer)); + + return TRUE; +} + +static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) +{ + /* + * We hold the lock, since we can send a signal. + * It is a good practice, according to the glib api. + */ + GST_A2DP_SINK_MUTEX_LOCK(sink); + + switch (sink->con_state) { + case CONFIGURING_SENT_CONF: + if (gst_a2dp_sink_conf_resp(sink)) { + sink->con_state = CONFIGURING_RCVD_CONF_RSP; + } else { + GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + } + break; + case CONFIGURING_RCVD_CONF_RSP: + if (gst_a2dp_sink_conf_recv_dev_conf(sink)) { + sink->con_state = CONFIGURING_RCVD_DEV_CONF; + } else { + GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + } + case CONFIGURING_RCVD_DEV_CONF: + if (gst_a2dp_sink_conf_recv_stream_fd(sink)) { + GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink); + } else { + GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + } + break; + } + + GST_A2DP_SINK_MUTEX_UNLOCK(sink); + +} + +static gboolean gst_a2dp_sink_set_caps(GstBaseSink *self, GstCaps *caps) +{ + GstA2dpSink *sink; + + sink = GST_A2DP_SINK(self); + + GST_A2DP_SINK_MUTEX_LOCK(sink); + if (sink->con_state == NOT_CONFIGURED) { + gst_a2dp_sink_start_dev_conf(sink, caps); + } + GST_A2DP_SINK_MUTEX_UNLOCK(sink); + + return TRUE; +} + +static gboolean gst_a2dp_sink_unlock(GstBaseSink *self) +{ + g_print("unlock\n"); + + return FALSE; } static gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { - printf("callback\n"); + GstA2dpSink *sink = GST_A2DP_SINK(data); + + switch (cond) { + case G_IO_IN: + if (sink->con_state != NOT_CONFIGURED && + sink->con_state != CONFIGURED) + gst_a2dp_sink_conf_recv_data(sink); + else + GST_WARNING_OBJECT(sink, "Unexpected data received"); + break; + case G_IO_HUP: + g_print("callback G_IO_HUP\n"); + break; + case G_IO_ERR: + g_print("callback G_IO_ERR\n"); + break; + case G_IO_NVAL: + g_print("callback G_IO_NVAL\n"); + break; + default: + g_print("unexpected callback\n"); + break; + + } return TRUE; } @@ -187,21 +710,22 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstAudioSinkClass *audiosink_class = GST_AUDIO_SINK_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); - - audiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dp_sink_open); - audiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_prepare); - audiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unprepare); - audiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dp_sink_close); - audiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dp_sink_write); - audiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dp_sink_delay); - audiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dp_sink_reset); + 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->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); g_object_class_install_property(object_class, PROP_DEVICE, g_param_spec_string("device", "Device", @@ -215,15 +739,23 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; - int sk; + gint sk; + gint err; self->device = g_strdup(DEFAULT_DEVICE); sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) + if (sk < 0) { + err = errno; + GST_ERROR_OBJECT(self, "Cannot open socket: %s (%d)", + strerror(err), err); return; + } if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + GST_ERROR_OBJECT(self, "Connection fail %s (%d)", + strerror(err), err); close(sk); return; } @@ -233,5 +765,24 @@ g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, server_callback, self); - g_io_channel_unref(self->server); + self->data = g_new0(struct bluetooth_data, 1); + memset(self->data, 0, sizeof(struct bluetooth_data)); + + self->data->pipefd[0] = -1; + self->data->pipefd[1] = -1; + + if (pipe(self->data->pipefd) < 0) + GST_ERROR_OBJECT(self, "%s (%d)", strerror(errno), errno); + if (fcntl(self->data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) + GST_ERROR_OBJECT(self, "%s (%d)", strerror(errno), errno); + if (fcntl(self->data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) + GST_ERROR_OBJECT(self, "%s (%d)", strerror(errno), errno); + + self->stream_fd = -1; + self->con_state = NOT_CONFIGURED; + + self->con_conf_end = g_cond_new(); + self->waiting_con_conf = FALSE; + self->sink_lock = g_mutex_new(); + } Index: audio/gsta2dpsink.h =================================================================== RCS file: /cvsroot/bluez/utils/audio/gsta2dpsink.h,v retrieving revision 1.3 diff -u -r1.3 gsta2dpsink.h --- audio/gsta2dpsink.h 26 Aug 2007 14:14:34 -0000 1.3 +++ audio/gsta2dpsink.h 5 Oct 2007 18:49:40 -0000 @@ -22,7 +22,7 @@ */ #include -#include +#include G_BEGIN_DECLS @@ -37,19 +37,41 @@ #define GST_IS_A2DP_SINK_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK)) +enum { + NOT_CONFIGURED, + CONFIGURING_INIT, + CONFIGURING_SENT_CONF, + CONFIGURING_RCVD_CONF_RSP, + CONFIGURING_RCVD_DEV_CONF, + CONFIGURED +}; + typedef struct _GstA2dpSink GstA2dpSink; typedef struct _GstA2dpSinkClass GstA2dpSinkClass; +struct bluetooth_data; + struct _GstA2dpSink { - GstAudioSink sink; + GstBaseSink sink; gchar *device; + gint stream_fd; + struct bluetooth_data *data; GIOChannel *server; + + gint con_state; + + GCond *con_conf_end; + gboolean waiting_con_conf; + GMutex *sink_lock; + + gint total; + }; struct _GstA2dpSinkClass { - GstAudioSinkClass parent_class; + GstBaseSinkClass parent_class; }; GType gst_a2dp_sink_get_type(void);