//整个的写流程如下,请在对应的调整点/数据记录监控点进行记录调试,确认问题。
//eq_drc层:(调整点1,是否可以增大buffsize)
#define READ_FRAME_DEFAULT 1920
#define PERIOD_SIZE_DEFAULT (READ_FRAME_DEFAULT)
#define PERIOD_COUNTS_DEFAULT (8*2)
#define BUFFER_SIZE_DEFAULT (PERIOD_SIZE_DEFAULT * PERIOD_COUNTS_DEFAULT) /* Keeping a large buffer_size ASAP */
#define MUTE_TIME_DEFAULT (3) /* seconds */
//从虚拟声卡读 (数据记录监控点1:主要是确认aplay送下来的数据是否完整)
err = snd_pcm_readi(capture_handle, buffer, g_read_frame);
//寻找播放设备
alsa_fake_device_write_open()
if (device_flag == DEVICE_FLAG_BLUETOOTH) {
sprintf(bluealsa_device, "%s%s", "bluealsa:HCI=hci0,PROFILE=a2dp,DEV=",
g_bt_mac_addr);
eq_debug("[EQ_WRITE_OPEN] Open PCM: %s\n", bluealsa_device);
//打开bluez的蓝牙设备
write_err = snd_pcm_open(write_handle, bluealsa_device,
SND_PCM_STREAM_PLAYBACK, 0);
}
//设置各类参数, 对应上面的define的各种参数
snd_pcm_hw_params_set_access()
snd_pcm_hw_params_set_format()
snd_pcm_hw_params_set_channels()
snd_pcm_hw_params_set_buffer_size_near()
snd_pcm_hw_params_set_period_size_near()
//对应的输出如下:
//eq_drc_process: [EQ_WRITE_OPEN] interleaved mode
//eq_drc_process: [EQ_WRITE_OPEN] snd_pcm_hw_params_alloca
//eq_drc_process: [EQ_WRITE_OPEN] interleaved mode
//eq_drc_process: [EQ_WRITE_OPEN] format successed
//eq_drc_process: [EQ_WRITE_OPEN] channels = 2
//eq_drc_process: [EQ_WRITE_OPEN] setting sampling rate (48000)
//eq_drc_process: [EQ_WRITE_OPEN] write_bufferSize = 15360
//eq_drc_process: [EQ_WRITE_OPEN] write_periodSize = 1920
//写音频数据到write_handle(数据记录监控点2,对比是否跟监控1一致,及写是否会出错)
err = snd_pcm_writei(write_handle, buffer, g_read_frame);
//bluez-alsa层:
//bluez-alsa\src\asound\bluealsa-pcm.c //这是一个模拟蓝牙音频插件
//eq-drc的相关蓝牙操作都会调用到这里:
static const snd_pcm_ioplug_callback_t bluealsa_callback = {
.start = bluealsa_start,
.stop = bluealsa_stop,
.pointer = bluealsa_pointer,
.close = bluealsa_close,
.hw_params = bluealsa_hw_params,
.hw_free = bluealsa_hw_free,
.sw_params = bluealsa_sw_params,
.prepare = bluealsa_prepare,
.drain = bluealsa_drain,
.pause = bluealsa_pause,
.dump = bluealsa_dump,
.delay = bluealsa_delay,
.poll_descriptors_count = bluealsa_poll_descriptors_count,
.poll_descriptors = bluealsa_poll_descriptors,
.poll_revents = bluealsa_poll_revents,
};
//eqdrc设置的各类参数都会调到这里:
bluealsa_hw_params()
//从框架获取一个pcm_fd,先记住整个pcm_fd
pcm->pcm_fd = bluealsa_open_transport(pcm->fd, &pcm->transport)
if (pcm->io.stream == SND_PCM_STREAM_PLAYBACK) {
/* By default, the size of the pipe buffer is set to a too large value for
* our purpose. On modern Linux system it is 65536 bytes. Large buffer in
* the playback mode might contribute to an unnecessary audio delay. Since
* it is possible to modify the size of this buffer we will set is to some
* low value, but big enough to prevent audio tearing. Note, that the size
* will be rounded up to the page size (typically 4096 bytes). */
//配置pcm_fd的buffer大小,(调整点2, 参考上面的描述)
pcm->pcm_buffer_size = fcntl(pcm->pcm_fd, F_SETPIPE_SZ, 2048);
debug("FIFO buffer 2048 size: %zd", pcm->pcm_buffer_size);
}
//根据eqdrc设置,最终得到的实际的参数
debug("Selected HW buffer: %zd periods x %zd bytes %c= %zd bytes",
io->buffer_size / io->period_size, pcm->frame_size * io->period_size,
io->period_size * (io->buffer_size / io->period_size) == io->buffer_size ? '=' : '<',
io->buffer_size * pcm->frame_size);
//启动接收数据线程,eqdrc的数据都会被写到这里
static int bluealsa_start(snd_pcm_ioplug_t *io)
pthread_create(&pcm->io_thread, NULL, io_thread, io)
io_thread()
debug("Starting IO loop xy");
for (;;) {
//eqdrc的snd_pcm_writei写数据都会体现在pcm->io_ptr这里
snd_pcm_uframes_t io_ptr = pcm->io_ptr;
snd_pcm_uframes_t io_buffer_size = io->buffer_size;
snd_pcm_uframes_t io_hw_ptr = pcm->io_hw_ptr;
snd_pcm_uframes_t io_hw_boundary = pcm->io_hw_boundary;
snd_pcm_uframes_t frames = io->period_size;
... ...
/* Perform atomic write - see the explanation above. */
do {
//把上面从eqdrc获取的数据 都写入到bluealsa_hw_params获取的pcm_fd
//(数据记录监控点3,对比是否跟监控2一致)
if ((ret = write(pcm->pcm_fd, head, len)) == -1) {
debug("pcm write ret: %d, len: %d", ret, len);
if (errno == EINTR)
continue;
SNDERR("PCM FIFO write error: %s", strerror(errno));
goto final;
}
debug("pcm write ret: %d, len: %d", ret, len);
head += ret;
len -= ret;
} while (len != 0);
}
//bluealsa在eqdrc打开蓝牙虚拟设备时会创建一个线程,整个线程专门把接收到的音频数据送给bluez
//bluez-alsa\src\io.c
void *io_thread_a2dp_source_sbc(void *arg) {
for (;;) {
//这里会从上面的bluealsa_start(io_thread)的pcm_fd读eqdrc的数据
//(数据记录监控点4,对比是否跟监控3一致)
io_thread_read_pcm(&t->a2dp.pcm, pcm.tail, ffb_len_in(&pcm))
//SBC编码
sbc_encode()
//最后送给bluez
io_thread_write_bt()
}
}
bluez-alsa流程说明
于 2022-07-13 10:22:22 首次发布