This commit is contained in:
huxi
2025-12-03 11:12:34 +08:00
parent c23ae4f24c
commit bc195654bf
8163 changed files with 3799544 additions and 92 deletions
+601
View File
@@ -0,0 +1,601 @@
#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".a2dp_tx_node.data.bss")
#pragma data_seg(".a2dp_tx_node.data")
#pragma const_seg(".a2dp_tx_node.text.const")
#pragma code_seg(".a2dp_tx_node.text")
#endif
#include "jlstream.h"
#include "btstack/avctp_user.h"
#include "classic/hci_lmp.h"
#include "media/audio_base.h"
#include "effects/effects_adj.h"
#include "sync/audio_syncts.h"
#include "codec/sbc_enc.h"
#include "app_config.h"
#if TCFG_A2DP_TX_NODE_ENABLE
#define LOG_TAG "[STREAM]"
/* #define LOG_TAG_CONST JLSTREAM */
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#define BT_PROTOCOL_A2DP 0
#define BT_PROTOCOL_BIS 1
#define BT_PROTOCOL_CIS 2
#define AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST 1 // 使用蓝牙时钟校准
#define AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME 1 // 使用系统时钟来做传输时间戳参考
#define TIMESTAMP_USE_AUDIO_JIFFIES 0 // 是否用杰理时间戳
struct bt_tx_param {
u8 protocol;
u8 jl_timestamp;
u8 frame_num;
} __attribute__((packed));
struct a2dp_tx_sync_node {
u8 trigger;
void *syncts;
struct list_head entry;
};
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
struct audio_bt_emitter_timestamp {
u16 id;
u16 run_once;
u32 run_points;
#if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST
u32 bt_time;
int bt_cnt;
#endif /* #if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST */
};
#endif
struct a2dp_tx_hdl {
u8 start;
u8 num;
u8 reference_network;
u8 first_timestamp;
u8 bt_addr[6];
u16 packet_size;
u8 *packet;
u32 timestamp;
u32 start_timestamp;
void *conn;
int offset;
u32 sample_rate;
u32 sample_offset;
u32 coding_type;
u32 packet_sn;
struct stream_node *node;
struct bt_tx_param tx_param;
u32 local_latch_time;
struct list_head sync_list;
struct stream_frame *frame;
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
u32 sbc_input_len;
struct audio_bt_emitter_timestamp *ts;
#endif
};
extern int bt_a2dp_get_packet_frame_num(void);
extern void bt_edr_conn_system_clock_init(void *addr, u8 factor);
extern u32 bt_edr_conn_master_to_local_time(void *addr, u32 usec);
extern u32 bt_edr_conn_local_to_master_time(void *addr, u32 usec);
extern void *a2dp_sbc_encoder_get_param(u8 *addr);
extern int bt_source_a2dp_send_media_packet(void *priv, u8 *packet, int len, int frame_sum, u32 TS);
extern int bt_get_source_send_a2dp_buf_size();
extern u8 *get_cur_connect_emitter_mac_addr(void);
extern void local_bt_us_time(u32 *_clkn, u32 *finecnt);
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
static void audio_bt_emitter_time_func(void *priv)
{
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)priv;
local_irq_disable();
if (hdl->ts) {
u32 run_once = hdl->ts->run_once;
#if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST
u32 _clkn, finecnt;
local_bt_us_time(&_clkn, &finecnt);
int offset_time = ((_clkn - hdl->ts->bt_time) & 0x7ffffff) * 625 / 1000;
hdl->ts->bt_cnt += 10;
if (offset_time >= (hdl->ts->bt_cnt + 10)) {
hdl->ts->bt_time = _clkn;
run_once += hdl->ts->run_once;
hdl->ts->bt_cnt = (hdl->ts->bt_cnt + 10) - offset_time;
} else if (offset_time <= (hdl->ts->bt_cnt - 10)) {
hdl->ts->bt_time = _clkn;
run_once -= hdl->ts->run_once;
hdl->ts->bt_cnt = (hdl->ts->bt_cnt - 10) - offset_time;
}
/* printf("t:%d, %d, o:%d \n", hdl->ts->bt_cnt, offset_time, run_once); */
#endif /* #if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST */
hdl->ts->run_points += run_once;
}
local_irq_enable();
}
#endif
#if TIMESTAMP_USE_AUDIO_JIFFIES
static int a2dp_tx_timestamp_init(struct a2dp_tx_hdl *hdl)
{
if (!hdl->tx_param.jl_timestamp) {
return 0;
}
hdl->local_latch_time = audio_jiffies_usec();
hdl->start_timestamp = hdl->local_latch_time * TIMESTAMP_US_DENOMINATOR;
/*r_printf("a2dp tx timestamp init : %u, %u\n", hdl->local_latch_time, hdl->start_timestamp);*/
hdl->first_timestamp = 1;
return 0;
}
static void a2dp_tx_timestamp_handler(struct a2dp_tx_hdl *hdl)
{
int tx_pcm_frames = 0;
struct a2dp_tx_sync_node *node;
if (!hdl->tx_param.jl_timestamp) {
return;
}
if (hdl->coding_type == AUDIO_CODING_SBC) {
tx_pcm_frames = hdl->num * 128;
} else if (hdl->coding_type == AUDIO_CODING_AAC) {
tx_pcm_frames = 1024;
}
hdl->sample_offset += tx_pcm_frames;
if (hdl->sample_offset >= hdl->sample_rate) {
hdl->sample_offset -= hdl->sample_rate;
hdl->start_timestamp += PCM_SAMPLE_ONE_SECOND;
hdl->local_latch_time += 1000000L;
}
u32 time = hdl->local_latch_time + ((u64)hdl->sample_offset * 1000000) / hdl->sample_rate;
list_for_each_entry(node, &hdl->sync_list, entry) {
if (!node->trigger) {
node->trigger = 1;
sound_pcm_syncts_latch_trigger(node->syncts);
}
sound_pcm_update_frame_num(node->syncts, tx_pcm_frames);
if (audio_syncts_latch_enable(node->syncts)) {
sound_pcm_update_frame_num_and_time(node->syncts, 0, time, 0);
}
}
}
#endif
static void a2dp_tx_handle_frame(struct stream_iport *iport, struct stream_note *note)
{
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
while (1) {
if (hdl->frame == NULL) {
hdl->frame = jlstream_pull_frame(iport, note);
}
if (!hdl->frame) {
break;
}
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
if (hdl->ts->run_points < hdl->sbc_input_len) {
break;
}
#endif
if (hdl->offset + hdl->frame->len > hdl->packet_size) {
/* g_printf("send : %d, %d\n", hdl->num, hdl->offset); */
hdl->packet_sn++;
hdl->timestamp = hdl->packet_sn * (128 * hdl->num);
bt_source_a2dp_send_media_packet(hdl->conn, hdl->packet, hdl->offset, hdl->num, hdl->timestamp);
#if TIMESTAMP_USE_AUDIO_JIFFIES
a2dp_tx_timestamp_handler(hdl);
#endif
hdl->offset = 0;
hdl->num = 0;
}
if (hdl->offset == 0) {
if (hdl->tx_param.jl_timestamp == 1) {
#if TIMESTAMP_USE_AUDIO_JIFFIES
if (hdl->first_timestamp) {
int time_diff = hdl->frame->timestamp - hdl->start_timestamp;
time_diff /= TIMESTAMP_US_DENOMINATOR;
hdl->local_latch_time += time_diff;
hdl->start_timestamp = bt_edr_conn_local_to_master_time(hdl->bt_addr, hdl->frame->timestamp);
hdl->first_timestamp = 0;
hdl->sample_offset = 0;
/*r_printf("start : %u, %u, %d, %u\n", frame->timestamp, hdl->start_timestamp, time_diff, hdl->local_latch_time);*/
} else {
hdl->timestamp = hdl->start_timestamp + PCM_SAMPLE_TO_TIMESTAMP(hdl->sample_offset, hdl->sample_rate);
}
#endif
} else {
hdl->timestamp = hdl->frame->timestamp;
}
}
memcpy(hdl->packet + hdl->offset, hdl->frame->data, hdl->frame->len);
hdl->num++;
hdl->offset += hdl->frame->len;
if (hdl->num >= hdl->tx_param.frame_num) {
/* g_printf("send 2: %d, %d \n", hdl->num, hdl->offset); */
hdl->packet_sn++;
hdl->timestamp = hdl->packet_sn * (128 * hdl->num);
bt_source_a2dp_send_media_packet(hdl->conn, hdl->packet, hdl->offset, hdl->num, hdl->timestamp);
#if TIMESTAMP_USE_AUDIO_JIFFIES
a2dp_tx_timestamp_handler(hdl);
#endif
hdl->offset = 0;
hdl->num = 0;
}
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
local_irq_disable();
if (hdl->ts->run_points >= hdl->sbc_input_len) {
hdl->ts->run_points -= hdl->sbc_input_len;
} else {
ASSERT((hdl->ts->run_points >= hdl->sbc_input_len));
}
local_irq_enable();
#endif
jlstream_free_frame(hdl->frame);
hdl->frame = NULL;
}
}
static int a2dp_tx_bind(struct stream_node *node, u16 uuid)
{
struct a2dp_tx_hdl *hdl = zalloc(sizeof(*hdl));
hdl->node = node;
node->private_data = hdl; //保存私有信息
return 0;
}
static void a2dp_tx_open_iport(struct stream_iport *iport)
{
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
iport->private_data = iport->node->private_data;
iport->handle_frame = a2dp_tx_handle_frame;
INIT_LIST_HEAD(&hdl->sync_list);
int len = jlstream_read_node_data_new(NODE_UUID_A2DP_TX, hdl->node->subid, (void *)&hdl->tx_param, NULL);
if (len < sizeof(hdl->tx_param)) {
log_error("a2dp tx get params error\n");
return;
}
}
static void a2dp_tx_set_bt_addr(struct a2dp_tx_hdl *hdl, void *bt_addr)
{
memcpy(hdl->bt_addr, bt_addr, 6);
#if TIMESTAMP_USE_AUDIO_JIFFIES
if (hdl->tx_param.jl_timestamp == 1) {
bt_edr_conn_system_clock_init(hdl->bt_addr, TIMESTAMP_US_DENOMINATOR);
}
#endif
}
u32 get_a2dp_tx_sr(u8 sbc_freqency)
{
/* #define SBC_FREQ_16000 0x00 */
/* #define SBC_FREQ_32000 0x01 */
/* #define SBC_FREQ_44100 0x02 */
/* #define SBC_FREQ_48000 0x03 */
u32 sr[] = {16000, 32000, 44100, 48000};
if (sbc_freqency >= ARRAY_SIZE(sr)) {
log_error("a2dp sbc get sample_rate error %d\n", sbc_freqency);
}
return sr[sbc_freqency];
}
u8 get_a2dp_channel_mode(u8 mode)
{
/* [> channel mode <] */
/* #define SBC_MODE_MONO 0x00 */
/* #define SBC_MODE_DUAL_CHANNEL 0x01 */
/* #define SBC_MODE_STEREO 0x02 */
/* #define SBC_MODE_JOINT_STEREO 0x03 */
if (mode == SBC_MODE_MONO) {
return AUDIO_CH_MIX;
} else {
return AUDIO_CH_LR;
}
}
static int a2dp_tx_ioc_fmt_nego(struct stream_iport *iport)
{
void *bt_addr = get_cur_connect_emitter_mac_addr();
if (!bt_addr) {
return 0; // 若无蓝牙发射连接,直接返回
}
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
struct stream_fmt *in_fmt = &iport->prev->fmt;
int ret = NEGO_STA_ACCPTED;
hdl->coding_type = in_fmt->coding_type;
if (hdl->coding_type == AUDIO_CODING_AAC) {
/*TDDO : AAC格式发送*/
}
u8 sbc_need_free = 0;
sbc_t *sbc_param = a2dp_sbc_encoder_get_param(hdl->bt_addr);
if (!sbc_param) {
log_info("no sbc param, use initlized param\n");
sbc_param = zalloc(sizeof(sbc_t));
sbc_param->frequency = SBC_FREQ_44100; // 给sbc_param初值,防止未连接emitter的时候nego不过
sbc_param->blocks = SBC_BLK_16;
sbc_param->subbands = SBC_SB_8;
sbc_param->mode = SBC_MODE_STEREO;
sbc_param->allocation = 0;
sbc_param->endian = SBC_LE;
sbc_param->bitpool = 38;
sbc_need_free = 1;
}
u32 sample_rate = get_a2dp_tx_sr(sbc_param->frequency);
if (in_fmt->sample_rate != sample_rate) {
in_fmt->sample_rate = sample_rate;
ret = NEGO_STA_CONTINUE;
}
u8 channel_mode = get_a2dp_channel_mode(sbc_param->mode);
if (in_fmt->channel_mode != channel_mode) {
in_fmt->channel_mode = channel_mode;
ret = NEGO_STA_CONTINUE;
}
u32 bit_rate = ((sbc_param->bitpool) | (sbc_param->mode << 8) | (sbc_param->blocks << 16) | (sbc_param->subbands << 20) | (sbc_param->allocation << 24) | (sbc_param->endian << 28));
if (in_fmt->bit_rate != bit_rate) {//sbc编码使用
in_fmt->bit_rate = bit_rate;
ret = NEGO_STA_CONTINUE;
}
hdl->sample_rate = in_fmt->sample_rate;
/* printf("====bit_rate %x\n", bit_rate); */
/* printf("a2dp_tx frequency %d, sr %d, bitpool %d, mode %d, channle_mode %x,allocation %d, blocks %d, subbands %d, endian %d\n", sbc_param->frequency, sample_rate, sbc_param->bitpool, sbc_param->mode, channel_mode, sbc_param->allocation, sbc_param->blocks, sbc_param->subbands, sbc_param->endian); */
// 调整sbc编码参数与耳机sbc参数一致
stream_node_ioctl(iport->prev->node, NODE_UUID_ENCODER, NODE_IOC_SET_PARAM, (int)sbc_param);
if (sbc_need_free) {
free(sbc_param);
}
return ret;
}
__attribute__((weak))
int bt_a2dp_get_packet_frame_num(void)
{
return 7;
}
static int a2dp_tx_start(struct a2dp_tx_hdl *hdl)
{
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
sbc_t *sbc_param = a2dp_sbc_encoder_get_param(hdl->bt_addr);
u8 subbands = 0;
u8 blocks = 0;
u8 channels = 0;
if (sbc_param) {
channels = sbc_param->mode == SBC_MODE_MONO ? 1 : 2;
subbands = sbc_param->subbands ? 8 : 4;
blocks = ((sbc_param->blocks) + 1) * 4;
}
hdl->sbc_input_len = subbands * blocks * channels * 2;
if (!hdl->ts) {
hdl->ts = zalloc(sizeof(struct audio_bt_emitter_timestamp));
}
hdl->ts->run_points = 0;
hdl->ts->run_once = (hdl->sample_rate * 10 / 1000) * channels * 2;
#if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST
u32 _clkn, finecnt;
local_bt_us_time(&_clkn, &finecnt);
hdl->ts->bt_time = _clkn;
hdl->ts->bt_cnt = 0;
#endif /* #if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST */
hdl->ts->id = sys_s_hi_timer_add((void *)hdl, audio_bt_emitter_time_func, 10);
#endif
hdl->start = 1;
/*TODO */
/*hdl->conn = a2dp_tx_open();*/
hdl->packet_size = bt_get_source_send_a2dp_buf_size();
//根据协商设置组帧数
if (bt_a2dp_get_packet_frame_num()) {
hdl->tx_param.frame_num = bt_a2dp_get_packet_frame_num();
}
ASSERT(hdl->packet_size != 0);
hdl->packet = malloc(hdl->packet_size);
if (!hdl->packet) {
log_error("a2dp tx packet buffer error.");
}
g_printf("a2dp tx packet size : %d\n", hdl->packet_size);
#if TIMESTAMP_USE_AUDIO_JIFFIES
a2dp_tx_timestamp_init(hdl);
#endif
hdl->packet_sn = 0;
hdl->offset = 0;
hdl->num = 0;
return 0;
}
static int a2dp_tx_stop(struct a2dp_tx_hdl *hdl)
{
hdl->start = 0;
if (hdl->packet) {
free(hdl->packet);
hdl->packet = NULL;
}
local_irq_disable();
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
if (hdl->ts) {
if (hdl->ts->id) {
sys_s_hi_timer_del(hdl->ts->id);
}
free(hdl->ts);
hdl->ts = NULL;
}
#endif
local_irq_enable();
if (hdl->frame) {
jlstream_free_frame(hdl->frame);
hdl->frame = NULL;
}
/*TODO */
/*
if (hdl->conn) {
a2dp_tx_close(hdl->conn);
}
*/
return 0;
}
static int a2dp_tx_mount_syncts(struct a2dp_tx_hdl *hdl, void *syncts, u32 timestamp, u8 network)
{
struct a2dp_tx_sync_node *node = NULL;
#if TIMESTAMP_USE_AUDIO_JIFFIES
if (!hdl->tx_param.jl_timestamp) {
return 0;
}
#endif
list_for_each_entry(node, &hdl->sync_list, entry) {
if ((u32)node->syncts == (u32)syncts) {
return 0;
}
}
node = (struct a2dp_tx_sync_node *)zalloc(sizeof(struct a2dp_tx_sync_node));
node->syncts = syncts;
/*g_printf("a2dp tx mount syncts : 0x%x, %u\n", (u32)syncts, timestamp);*/
if (hdl->reference_network == 0xff) {
hdl->reference_network = network;
}
list_add(&node->entry, &hdl->sync_list);
return 0;
}
static void a2dp_tx_unmount_syncts(struct a2dp_tx_hdl *hdl, void *syncts)
{
struct a2dp_tx_sync_node *node;
#if TIMESTAMP_USE_AUDIO_JIFFIES
if (!hdl->tx_param.jl_timestamp) {
return;
}
#endif
list_for_each_entry(node, &hdl->sync_list, entry) {
if (node->syncts == syncts) {
goto unmount;
}
}
return;
unmount:
/*g_printf("a2dp tx unmount syncts : 0x%x\n", syncts);*/
list_del(&node->entry);
free(node);
}
static int a2dp_tx_syncts_handler(struct a2dp_tx_hdl *hdl, struct audio_syncts_ioc_params *params)
{
if (!params) {
return 0;
}
switch (params->cmd) {
case AUDIO_SYNCTS_MOUNT_ON_SNDPCM:
a2dp_tx_mount_syncts(hdl, (void *)params->data[0], params->data[1], params->data[2]);
break;
case AUDIO_SYNCTS_UMOUNT_ON_SNDPCM:
a2dp_tx_unmount_syncts(hdl, (void *)params->data[0]);
break;
}
return 0;
}
static int a2dp_tx_buffer_latency(struct a2dp_tx_hdl *hdl)
{
return 0;
}
static int a2dp_tx_ioctl(struct stream_iport *iport, int cmd, int arg)
{
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
switch (cmd) {
case NODE_IOC_OPEN_IPORT:
a2dp_tx_open_iport(iport);
break;
case NODE_IOC_SET_BTADDR:
a2dp_tx_set_bt_addr(hdl, (void *)arg);
break;
case NODE_IOC_CLOSE_IPORT:
break;
case NODE_IOC_START:
a2dp_tx_start(hdl);
break;
case NODE_IOC_SUSPEND:
case NODE_IOC_STOP:
a2dp_tx_stop(hdl);
break;
case NODE_IOC_SYNCTS:
a2dp_tx_syncts_handler(hdl, (struct audio_syncts_ioc_params *)arg);
break;
case NODE_IOC_NEGOTIATE:
*(int *)arg |= a2dp_tx_ioc_fmt_nego(iport);
break;
case NODE_IOC_GET_DELAY:
return a2dp_tx_buffer_latency(hdl);
}
return 0;
}
static void a2dp_tx_release(struct stream_node *node)
{
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)node->private_data;
if (hdl) {
free(hdl);
}
}
REGISTER_STREAM_NODE_ADAPTER(a2dp_tx_adapter) = {
.name = "a2dp_tx",
.uuid = NODE_UUID_A2DP_TX,
.bind = a2dp_tx_bind,
.ioctl = a2dp_tx_ioctl,
.release = a2dp_tx_release,
};
#endif