Return-Path: Message-ID: <20050424150930.71134.qmail@web8308.mail.in.yahoo.com> From: Mayank Batra To: BLUEZ DEVELOPERS LIST Cc: Brad Midgley MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="0-563832256-1114355370=:68292" Subject: [Bluez-devel] A2DP sink code finally Sender: bluez-devel-admin@lists.sourceforge.net Errors-To: bluez-devel-admin@lists.sourceforge.net Reply-To: bluez-devel@lists.sourceforge.net List-Unsubscribe: , List-Id: BlueZ development List-Post: List-Help: List-Subscribe: , List-Archive: Date: Sun, 24 Apr 2005 16:09:30 +0100 (BST) --0-563832256-1114355370=:68292 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Content-Id: Content-Disposition: inline Hi Brad, This is the code for the A2DP sink application. Compilation: gcc -o a2snk a2snk.c -lbluetooth Running the application: ./a2snk Right now the following bugs exist: 1) Poor sound quality. 2) Unclean disconnection. Help me improve the above areas. Please add this code to the CVS. Thanks and Regards, Mayank ________________________________________________________________________ Yahoo! India Matrimony: Find your life partner online Go to: http://yahoo.shaadi.com/india-matrimony --0-563832256-1114355370=:68292 Content-Type: text/plain; name="a2snk.c" Content-Description: a2snk.c Content-Disposition: inline; filename="a2snk.c" /* * a2snk.c * This program functions as an A2DP sink * (Emulation of an A2DP headset) * Mayank Batra * Abhinav Mathur * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include //To play the sound on the sound card #include "sbc/sbc.h" #include #include #include #include #include #include #include /* AVDTP structures */ /* packet components */ struct avdtp_header { //uint8_t packet_type:2; uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction_label:4; uint8_t signal_id:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct acp_seid_info { uint8_t rfa0:1; uint8_t inuse0:1; uint8_t acp_seid:6; uint8_t rfa2:3; uint8_t tsep:1; uint8_t media_type:4; } __attribute__ ((packed)); struct sbc_codec_specific_elements { // a2dp p. 20 uint8_t channel_mode:4; uint8_t frequency:4; uint8_t allocation_method:2; uint8_t subbands:2; uint8_t block_length:4; uint8_t min_bitpool; uint8_t max_bitpool; } __attribute__ ((packed)); #define MAX_ADDITIONAL_CODEC 0 //Right now only SBC is supported #define MAX_ADDITIONAL_CODEC_OCTETS (MAX_ADDITIONAL_CODEC*sizeof(struct acp_seid_info)) /* packets */ struct sepd_req { struct avdtp_header header; } __attribute__ ((packed)); struct sepd_resp { struct avdtp_header header; struct acp_seid_info infos[1 + MAX_ADDITIONAL_CODEC]; } __attribute__ ((packed)); struct getcap_req { struct avdtp_header header; uint8_t rfa1:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct getcap_resp { struct avdtp_header header; uint8_t serv_cap; uint8_t serv_cap_len; uint8_t cap_type; uint8_t length; uint8_t media_type; uint8_t media_codec_type; struct sbc_codec_specific_elements sbc_elements; } __attribute__ ((packed)); struct set_config { struct avdtp_header header; uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t rfa1:2; uint8_t int_seid:6; uint8_t serv_cap; uint8_t serv_cap_len; uint8_t cap_type; uint8_t length; uint8_t media_type; uint8_t media_codec_type; struct sbc_codec_specific_elements sbc_elements; } __attribute__ ((packed)); struct set_config_resp { struct avdtp_header header; // only present for an error uint8_t serv_cat; uint8_t error_code; } __attribute__ ((packed)); struct open_stream_cmd { struct avdtp_header header; uint8_t rfa0:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct open_stream_rsp { struct avdtp_header header; // only present for an error uint8_t error; } __attribute__ ((packed)); struct start_stream_cmd { struct avdtp_header header; uint8_t rfa0:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct start_stream_rsp { struct avdtp_header header; // only present for an error uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t error; } __attribute__ ((packed)); struct close_stream_cmd { struct avdtp_header header; uint8_t rfa0:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct close_stream_rsp { struct avdtp_header header; // only present for an error uint8_t error; } __attribute__ ((packed)); // this is an rtp, not bluetooth header, so values are big endian struct media_packet_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 media_payload_header { 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)); // SBC file format header struct sbc_frame_header { uint8_t syncword:8; /* Sync word */ uint8_t subbands:1; /* Subbands */ uint8_t allocation_method:1; /* Allocation method */ uint8_t channel_mode:2; /* Channel mode */ uint8_t blocks:2; /* Blocks */ uint8_t sampling_frequency:2; /* Sampling frequency */ uint8_t bitpool:8; /* Bitpool */ uint8_t crc_check:8; /* CRC check */ } __attribute__ ((packed)); //A2DP signal types #define AVDTP_DISCOVER 1 #define AVDTP_GET_CAPABILITIES 2 #define AVDTP_SET_CONFIGURATION 3 #define AVDTP_OPEN 6 #define AVDTP_START 7 #define AVDTP_CLOSE 8 #define MEDIA_TRANSPORT_CATEGORY 1 #define MEDIA_CODEC 7 #define SBC_MEDIA_CODEC_TYPE 0 #define MPEG12_MEDIA_CODEC_TYPE 1 #define AUDIO_MEDIA_TYPE 0 //Packet Types #define PACKET_TYPE_SINGLE 0 #define PACKET_TYPE_START 4 #define PACKET_TYPE_CONTINUE 8 #define PACKET_TYPE_END 12 //Message Types #define MESSAGE_TYPE_COMMAND 0 #define MESSAGE_TYPE_ACCEPT 2 #define MESSAGE_TYPE_REJECT 3 #define BUFS 1024 #define MEDIA_PACKET_HEADER_LENGTH 14 #define NONSPECAUDIO 1 static volatile int terminate = 0; static int cmdfd; static struct sbc_frame_header sbc_info; sbc_t sbc; int audio_fd; #define BUF_SIZE 4096 unsigned char audio_buffer[BUF_SIZE]; int speed, channels; static void sig_term(int sig) { terminate = 1; } static void usage() { fprintf(stderr, "use: ./a2snk\n"); } int opensound() { int format=AFMT_S16_BE,len,i; char c; if((audio_fd=open("/dev/dsp",O_WRONLY,0))==-1) { perror("\nFile open error\n"); exit(1); } if(ioctl(audio_fd,SNDCTL_DSP_SETFMT,&format)==-1) { perror("\nioctl no. 1\n"); exit(1); } if(ioctl(audio_fd,SNDCTL_DSP_CHANNELS,&channels)==-1) { perror("\nioctl no. 2\n"); exit(1); } if(ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed)==-1) { perror("\nioctl no. 3\n"); exit(1); } return 1; } int closesound() { if(close(audio_fd)<0) { perror("\nUnable to close the sound card"); exit(1); } } static ssize_t __write(int fd,void *buf,size_t count) { ssize_t len,pos=0; while(count>0){ len=write(fd,buf+pos,count); if(len<=0) return len; count -= len; pos += len; } return pos; } static void decode(char *stream,int streamlen) { int fd, id, pos, framelen; static int turn = 1; pos = 0; framelen = sbc_decode(&sbc, stream, streamlen); printf("%d Hz, %d channels\n", sbc.rate, sbc.channels); channels=sbc.channels; speed=sbc.rate; if(turn == 1) { /* Open the sound card only once during the streaming */ opensound(); turn = 0; } char c; while (framelen > 0) { //dump_packet(sbc.data,sbc.len); write(audio_fd,sbc.data,sbc.len); pos += framelen; framelen = sbc_decode(&sbc, stream + pos, streamlen - pos); } } int sk; static int do_listen(bdaddr_t *src, unsigned short psm, uint16_t *mtu) { struct sockaddr_l2 addr; struct l2cap_options opts; int opt; int nsk; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sk < 0) { fprintf(stderr, "Can't create socket. %s(%d)\n", strerror(errno), errno); return -1; } memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); addr.l2_psm=htobs(psm); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { fprintf(stderr, "Can't bind socket. %s(%d)\n", strerror(errno), errno); return -1; } /* Get default options */ opt = sizeof(opts); if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) { fprintf(stderr, "Can't get default L2CAP options. %s(%d)\n", strerror(errno), errno); return -1; } /* Set new options */ //opts.omtu = 48; //opts.imtu = imtu; if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0) { fprintf(stderr, "Can't set L2CAP options. %s(%d)\n", strerror(errno), errno); return -1; } if(listen(sk,5)<0) { fprintf(stderr,"\nCan't listen.%s(%d)\n",strerror(errno),errno); close(sk); return -1; } socklen_t addrlen; memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0) return -1; else printf("\nConnected"); opt = sizeof(opts); if (getsockopt(nsk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) { fprintf(stderr, "Can't get L2CAP options. %s(%d)\n", strerror(errno), errno); close(nsk); return -1; } fprintf(stderr, "Connected [imtu %d, omtu %d, flush_to %d]\n", opts.imtu, opts.omtu, opts.flush_to); if (mtu) *mtu = opts.omtu; return nsk; } #if 0 static void dump_packet(void *p, int size) { uint8_t *c = (uint8_t *) p; while (size-- > 0) printf(" %02x\n", *c++); printf("\n"); } #endif static void init_request(struct avdtp_header * header, int request_id) { static int transaction = 0; header->packet_type = PACKET_TYPE_SINGLE; header->message_type = MESSAGE_TYPE_ACCEPT; header->transaction_label = transaction; header->signal_id = request_id; // clear rfa bits header->rfa0 = 0; if(header->signal_id!=AVDTP_OPEN) transaction = (transaction + 1) & 0xf; else transaction = (transaction + 2) & 0xf; } static int calc_frame_len(struct sbc_frame_header *hdr) { int tmp, nrof_subbands, nrof_blocks; nrof_subbands = (hdr->subbands + 1) * 4; nrof_blocks = (hdr->blocks + 1) * 4; switch (hdr->channel_mode) { case 0x00: nrof_subbands /= 2; tmp = nrof_blocks * hdr->bitpool; break; case 0x01: tmp = nrof_blocks * hdr->bitpool * 2; break; case 0x02: tmp = nrof_blocks * hdr->bitpool; break; case 0x03: tmp = nrof_blocks * hdr->bitpool + nrof_subbands; break; default: return 0; } return (nrof_subbands + ((tmp + 7) / 8)); } static int read_header(int fd, struct sbc_frame_header *sbc_info) { if (read(fd, sbc_info, sizeof(*sbc_info)) < sizeof(*sbc_info)) { fprintf(stderr, "reached end of file?\n"); return -1; } if (sbc_info->syncword != 0x9c) { printf("out of sync (0x%02x)\n", sbc_info->syncword); return -1; } return calc_frame_len(sbc_info); } int main(int argc, char *argv[]) { struct sigaction sa; int streamfd; bdaddr_t src, dst; unsigned short psm_cmd, psm_stream; unsigned long flags; int frame_len; time_t timestamp; uint16_t mtu, seq_num; int fd; bacpy(&src, BDADDR_ANY); psm_cmd=25; cmdfd = do_listen(&src, psm_cmd, NULL); if (cmdfd < 0) { fprintf(stderr, "cannot open psm_cmd = %d\n", psm_cmd); exit(-1); } // avdtp discover request //Reading the discover request struct sepd_req get_resp; int size; size = read(cmdfd, &get_resp, sizeof(get_resp)); if(get_resp.header.signal_id!=AVDTP_DISCOVER) { fprintf(stderr,"Couldn't get avdtp_discover\n"); close(cmdfd); exit(-1); } else printf("\nGot Stream End Point Discovery Request"); //Writing the discover response struct sepd_resp send_resp; //Fill in the values in send_resp memset(&send_resp,0,sizeof(send_resp)); init_request(&send_resp.header,AVDTP_DISCOVER); send_resp.infos[0].rfa0=0; send_resp.infos[0].inuse0=0; send_resp.infos[0].acp_seid=1; send_resp.infos[0].rfa2=2; send_resp.infos[0].tsep=1; send_resp.infos[0].media_type=0; if(write(cmdfd,&send_resp,sizeof(send_resp))!=sizeof(send_resp)) { fprintf(stderr,"\nCould not send discover response\n"); close(cmdfd); exit(-1); } else printf("\nSent Stream End Point Discovery Response\n"); //Now read the get capablities request from the source struct getcap_req get_req; memset(&get_req,0,sizeof(get_req)); if(read(cmdfd,&get_req,sizeof(get_req))!=sizeof(get_req) || (get_req.header.signal_id!=AVDTP_GET_CAPABILITIES)) { fprintf(stderr,"\nDidn't get a get cap req"); } else printf("\nGot a get capabilities request\n"); //Send a get cap resp struct getcap_resp cap_resp; memset(&cap_resp,0,sizeof(cap_resp)); init_request(&cap_resp.header,AVDTP_GET_CAPABILITIES); //Fill in the values of the structure cap_resp.serv_cap=MEDIA_TRANSPORT_CATEGORY; cap_resp.serv_cap_len=0; cap_resp.cap_type=MEDIA_CODEC; cap_resp.media_type=AUDIO_MEDIA_TYPE; cap_resp.length=6; cap_resp.media_codec_type=SBC_MEDIA_CODEC_TYPE; cap_resp.sbc_elements.channel_mode=15; cap_resp.sbc_elements.frequency=15; cap_resp.sbc_elements.allocation_method=3; cap_resp.sbc_elements.subbands=3; cap_resp.sbc_elements.min_bitpool=2; cap_resp.sbc_elements.max_bitpool=250; cap_resp.sbc_elements.block_length=15; if(write(cmdfd,&cap_resp,sizeof(cap_resp)) BUFS) mtu = BUFS; terminate = 0; seq_num = 1; sbc_init(&sbc,SBC_NULL); struct media_packet_header packet_header; struct media_payload_header payload_header; memset(&payload_header, 0, sizeof(payload_header)); int packsize; //Size of the packet that is read timestamp = 0; packsize=read(streamfd,buf,1024); decode(buf+(sizeof(packet_header)+sizeof(payload_header)),(packsize-sizeof(packet_header)-sizeof(payload_header))); printf("Channels=%d,speed=%d",channels,speed); struct close_stream_cmd close_stream; memset(&close_stream,0,sizeof(close_stream)); while (!terminate) { packsize=read(streamfd, buf,1024); if(packsize < 0) break; printf("\nRead:%d bytes",packsize); decode(buf+(sizeof(packet_header)+sizeof(payload_header)),(packsize-sizeof(packet_header)-sizeof(payload_header))); seq_num++; } sbc_finish(&sbc); printf("Received %d packets\n", seq_num); // signal the stream close if (read(cmdfd, &close_stream, sizeof(close_stream)) != sizeof(close_stream)) { fprintf(stderr, "couldn't get close_stream\n"); close(streamfd); close(cmdfd); exit(-1); } if(close_stream.header.signal_id==AVDTP_CLOSE) { printf("Got stream-close\n"); struct close_stream_rsp close_resp; init_request(&close_resp.header,AVDTP_CLOSE); if (write(cmdfd, &close_resp, sizeof(close_resp)) < sizeof(close_resp)) { fprintf(stderr, "Couldn't send close_resp confirm \n"); close(streamfd); close(cmdfd); return (-1); } else printf("Sent close stream confirm\n"); } else { printf("\nDidnt get a stream close as expected"); close(streamfd); close(cmdfd); exit(-1); } closesound(); close(streamfd); close(cmdfd); return 0; } --0-563832256-1114355370=:68292-- ------------------------------------------------------- SF email is sponsored by - The IT Product Guide Read honest & candid reviews on hundreds of IT Products from real users. Discover which products truly live up to the hype. Start reading now. http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click _______________________________________________ Bluez-devel mailing list Bluez-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/bluez-devel