Home | History | Annotate | Download | only in server
      1 /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include <netinet/in.h>
      7 #include <sbc/sbc.h>
      8 #include <syslog.h>
      9 
     10 #include "cras_a2dp_info.h"
     11 #include "cras_sbc_codec.h"
     12 #include "cras_types.h"
     13 #include "rtp.h"
     14 
     15 int init_a2dp(struct a2dp_info *a2dp, a2dp_sbc_t *sbc)
     16 {
     17 	uint8_t frequency = 0, mode = 0, subbands = 0, allocation, blocks = 0,
     18 		bitpool;
     19 
     20 	if (sbc->frequency & SBC_SAMPLING_FREQ_48000)
     21 		frequency = SBC_FREQ_48000;
     22 	else if (sbc->frequency & SBC_SAMPLING_FREQ_44100)
     23 		frequency = SBC_FREQ_44100;
     24 	else if (sbc->frequency & SBC_SAMPLING_FREQ_32000)
     25 		frequency = SBC_FREQ_32000;
     26 	else if (sbc->frequency & SBC_SAMPLING_FREQ_16000)
     27 		frequency = SBC_FREQ_16000;
     28 
     29 	if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
     30 		mode = SBC_MODE_JOINT_STEREO;
     31 	else if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO)
     32 		mode = SBC_MODE_STEREO;
     33 	else if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
     34 		mode = SBC_MODE_DUAL_CHANNEL;
     35 	else if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO)
     36 		mode = SBC_MODE_MONO;
     37 
     38 	if (sbc->allocation_method & SBC_ALLOCATION_LOUDNESS)
     39 		allocation = SBC_AM_LOUDNESS;
     40 	else
     41 		allocation = SBC_AM_SNR;
     42 
     43 	switch (sbc->subbands) {
     44 	case SBC_SUBBANDS_4:
     45 		subbands = SBC_SB_4;
     46 		break;
     47 	case SBC_SUBBANDS_8:
     48 		subbands = SBC_SB_8;
     49 		break;
     50 	}
     51 
     52 	switch (sbc->block_length) {
     53 	case SBC_BLOCK_LENGTH_4:
     54 		blocks = SBC_BLK_4;
     55 		break;
     56 	case SBC_BLOCK_LENGTH_8:
     57 		blocks = SBC_BLK_8;
     58 		break;
     59 	case SBC_BLOCK_LENGTH_12:
     60 		blocks = SBC_BLK_12;
     61 		break;
     62 	case SBC_BLOCK_LENGTH_16:
     63 		blocks = SBC_BLK_16;
     64 		break;
     65 	}
     66 
     67 	bitpool = sbc->max_bitpool;
     68 
     69 	a2dp->codec = cras_sbc_codec_create(frequency, mode, subbands,
     70 					    allocation, blocks, bitpool);
     71 	if (!a2dp->codec)
     72 		return -1;
     73 
     74 	/* SBC info */
     75 	a2dp->codesize = cras_sbc_get_codesize(a2dp->codec);
     76 	a2dp->frame_length = cras_sbc_get_frame_length(a2dp->codec);
     77 
     78 	a2dp->a2dp_buf_used = sizeof(struct rtp_header)
     79 			+ sizeof(struct rtp_payload);
     80 	a2dp->frame_count = 0;
     81 	a2dp->seq_num = 0;
     82 	a2dp->samples = 0;
     83 
     84 	return 0;
     85 }
     86 
     87 void destroy_a2dp(struct a2dp_info *a2dp)
     88 {
     89 	cras_sbc_codec_destroy(a2dp->codec);
     90 }
     91 
     92 int a2dp_codesize(struct a2dp_info *a2dp)
     93 {
     94 	return a2dp->codesize;
     95 }
     96 
     97 int a2dp_block_size(struct a2dp_info *a2dp, int a2dp_bytes)
     98 {
     99 	return a2dp_bytes / a2dp->frame_length * a2dp->codesize;
    100 }
    101 
    102 int a2dp_queued_frames(const struct a2dp_info *a2dp)
    103 {
    104 	return a2dp->samples;
    105 }
    106 
    107 void a2dp_drain(struct a2dp_info *a2dp)
    108 {
    109 	a2dp->a2dp_buf_used = sizeof(struct rtp_header)
    110 			+ sizeof(struct rtp_payload);
    111 	a2dp->samples = 0;
    112 	a2dp->seq_num = 0;
    113 	a2dp->frame_count = 0;
    114 }
    115 
    116 static int avdtp_write(int stream_fd, struct a2dp_info *a2dp)
    117 {
    118 	int err, samples;
    119 	struct rtp_header *header;
    120 	struct rtp_payload *payload;
    121 
    122 	header = (struct rtp_header *)a2dp->a2dp_buf;
    123 	payload = (struct rtp_payload *)(a2dp->a2dp_buf + sizeof(*header));
    124 	memset(a2dp->a2dp_buf, 0, sizeof(*header) + sizeof(*payload));
    125 
    126 	payload->frame_count = a2dp->frame_count;
    127 	header->v = 2;
    128 	header->pt = 1;
    129 	header->sequence_number = htons(a2dp->seq_num);
    130 	header->timestamp = htonl(a2dp->nsamples);
    131 	header->ssrc = htonl(1);
    132 
    133 	err = send(stream_fd, a2dp->a2dp_buf, a2dp->a2dp_buf_used,
    134 		   MSG_DONTWAIT);
    135 	if (err < 0)
    136 		return -errno;
    137 
    138 	/* Returns the number of samples in frame. */
    139 	samples = a2dp->samples;
    140 
    141 	/* Reset some data */
    142 	a2dp->a2dp_buf_used = sizeof(*header) + sizeof(*payload);
    143 	a2dp->frame_count = 0;
    144 	a2dp->samples = 0;
    145 	a2dp->seq_num++;
    146 
    147 	return samples;
    148 }
    149 
    150 int a2dp_encode(struct a2dp_info *a2dp, const void *pcm_buf, int pcm_buf_size,
    151 		int format_bytes, size_t link_mtu)
    152 {
    153 	int processed;
    154 	size_t out_encoded;
    155 
    156 	if (link_mtu > A2DP_BUF_SIZE_BYTES)
    157 		link_mtu = A2DP_BUF_SIZE_BYTES;
    158 	if (link_mtu == a2dp->a2dp_buf_used)
    159 		return 0;
    160 
    161 	processed = a2dp->codec->encode(a2dp->codec, pcm_buf, pcm_buf_size,
    162 					a2dp->a2dp_buf + a2dp->a2dp_buf_used,
    163 					link_mtu - a2dp->a2dp_buf_used,
    164 					&out_encoded);
    165 	if (processed < 0) {
    166 		syslog(LOG_ERR, "a2dp encode error %d", processed);
    167 		return processed;
    168 	}
    169 
    170 	if (a2dp->codesize > 0)
    171 		a2dp->frame_count += processed / a2dp->codesize;
    172 	a2dp->a2dp_buf_used += out_encoded;
    173 
    174 	a2dp->samples += processed / format_bytes;
    175 	a2dp->nsamples += processed / format_bytes;
    176 
    177 	return processed;
    178 }
    179 
    180 int a2dp_write(struct a2dp_info *a2dp, int stream_fd, size_t link_mtu)
    181 {
    182 	/* Do avdtp write when the max number of SBC frames is reached. */
    183 	if (a2dp->a2dp_buf_used + a2dp->frame_length >
    184 	    link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload))
    185 		return avdtp_write(stream_fd, a2dp);
    186 
    187 	return 0;
    188 }
    189