/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include <netinet/in.h> #include <sbc/sbc.h> #include <syslog.h> #include "cras_a2dp_info.h" #include "cras_sbc_codec.h" #include "cras_types.h" #include "rtp.h" int init_a2dp(struct a2dp_info *a2dp, a2dp_sbc_t *sbc) { uint8_t frequency = 0, mode = 0, subbands = 0, allocation, blocks = 0, bitpool; if (sbc->frequency & SBC_SAMPLING_FREQ_48000) frequency = SBC_FREQ_48000; else if (sbc->frequency & SBC_SAMPLING_FREQ_44100) frequency = SBC_FREQ_44100; else if (sbc->frequency & SBC_SAMPLING_FREQ_32000) frequency = SBC_FREQ_32000; else if (sbc->frequency & SBC_SAMPLING_FREQ_16000) frequency = SBC_FREQ_16000; if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) mode = SBC_MODE_JOINT_STEREO; else if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) mode = SBC_MODE_STEREO; else if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) mode = SBC_MODE_DUAL_CHANNEL; else if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) mode = SBC_MODE_MONO; if (sbc->allocation_method & SBC_ALLOCATION_LOUDNESS) allocation = SBC_AM_LOUDNESS; else allocation = SBC_AM_SNR; switch (sbc->subbands) { case SBC_SUBBANDS_4: subbands = SBC_SB_4; break; case SBC_SUBBANDS_8: subbands = SBC_SB_8; break; } switch (sbc->block_length) { case SBC_BLOCK_LENGTH_4: blocks = SBC_BLK_4; break; case SBC_BLOCK_LENGTH_8: blocks = SBC_BLK_8; break; case SBC_BLOCK_LENGTH_12: blocks = SBC_BLK_12; break; case SBC_BLOCK_LENGTH_16: blocks = SBC_BLK_16; break; } bitpool = sbc->max_bitpool; a2dp->codec = cras_sbc_codec_create(frequency, mode, subbands, allocation, blocks, bitpool); if (!a2dp->codec) return -1; /* SBC info */ a2dp->codesize = cras_sbc_get_codesize(a2dp->codec); a2dp->frame_length = cras_sbc_get_frame_length(a2dp->codec); a2dp->a2dp_buf_used = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->frame_count = 0; a2dp->seq_num = 0; a2dp->samples = 0; return 0; } void destroy_a2dp(struct a2dp_info *a2dp) { cras_sbc_codec_destroy(a2dp->codec); } int a2dp_codesize(struct a2dp_info *a2dp) { return a2dp->codesize; } int a2dp_block_size(struct a2dp_info *a2dp, int a2dp_bytes) { return a2dp_bytes / a2dp->frame_length * a2dp->codesize; } int a2dp_queued_frames(const struct a2dp_info *a2dp) { return a2dp->samples; } void a2dp_drain(struct a2dp_info *a2dp) { a2dp->a2dp_buf_used = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->samples = 0; a2dp->seq_num = 0; a2dp->frame_count = 0; } static int avdtp_write(int stream_fd, struct a2dp_info *a2dp) { int err, samples; struct rtp_header *header; struct rtp_payload *payload; header = (struct rtp_header *)a2dp->a2dp_buf; payload = (struct rtp_payload *)(a2dp->a2dp_buf + sizeof(*header)); memset(a2dp->a2dp_buf, 0, sizeof(*header) + sizeof(*payload)); payload->frame_count = a2dp->frame_count; header->v = 2; header->pt = 1; header->sequence_number = htons(a2dp->seq_num); header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); err = send(stream_fd, a2dp->a2dp_buf, a2dp->a2dp_buf_used, MSG_DONTWAIT); if (err < 0) return -errno; /* Returns the number of samples in frame. */ samples = a2dp->samples; /* Reset some data */ a2dp->a2dp_buf_used = sizeof(*header) + sizeof(*payload); a2dp->frame_count = 0; a2dp->samples = 0; a2dp->seq_num++; return samples; } int a2dp_encode(struct a2dp_info *a2dp, const void *pcm_buf, int pcm_buf_size, int format_bytes, size_t link_mtu) { int processed; size_t out_encoded; if (link_mtu > A2DP_BUF_SIZE_BYTES) link_mtu = A2DP_BUF_SIZE_BYTES; if (link_mtu == a2dp->a2dp_buf_used) return 0; processed = a2dp->codec->encode(a2dp->codec, pcm_buf, pcm_buf_size, a2dp->a2dp_buf + a2dp->a2dp_buf_used, link_mtu - a2dp->a2dp_buf_used, &out_encoded); if (processed < 0) { syslog(LOG_ERR, "a2dp encode error %d", processed); return processed; } if (a2dp->codesize > 0) a2dp->frame_count += processed / a2dp->codesize; a2dp->a2dp_buf_used += out_encoded; a2dp->samples += processed / format_bytes; a2dp->nsamples += processed / format_bytes; return processed; } int a2dp_write(struct a2dp_info *a2dp, int stream_fd, size_t link_mtu) { /* Do avdtp write when the max number of SBC frames is reached. */ if (a2dp->a2dp_buf_used + a2dp->frame_length > link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) return avdtp_write(stream_fd, a2dp); return 0; }