821 lines
27 KiB
C
821 lines
27 KiB
C
#ifdef SUPPORT_MS_EXTENSIONS
|
|
#pragma bss_seg(".a2dp_file.data.bss")
|
|
#pragma data_seg(".a2dp_file.data")
|
|
#pragma const_seg(".a2dp_file.text.const")
|
|
#pragma code_seg(".a2dp_file.text")
|
|
#endif
|
|
#include "btstack/a2dp_media_codec.h"
|
|
#include "btstack/avctp_user.h"
|
|
#include "source_node.h"
|
|
#include "classic/tws_api.h"
|
|
#include "system/timer.h"
|
|
#include "sync/audio_syncts.h"
|
|
#include "media/bt_audio_timestamp.h"
|
|
#include "os/os_api.h"
|
|
#include "jiffies.h"
|
|
#include "a2dp_streamctrl.h"
|
|
#include "reference_time.h"
|
|
#include "effects/effects_adj.h"
|
|
#include "app_config.h"
|
|
|
|
#if TCFG_USER_BT_CLASSIC_ENABLE
|
|
|
|
#define A2DP_TIMESTAMP_ENABLE 1
|
|
|
|
struct a2dp_file_params {
|
|
u8 edr_to_local_time;
|
|
} __attribute__((packed));
|
|
|
|
struct a2dp_file_hdl {
|
|
u8 start;
|
|
/*u8 reassemble;*/
|
|
u16 timer;
|
|
u16 wake_up_timer;
|
|
void *file;
|
|
int media_type;
|
|
struct stream_node *node;
|
|
|
|
void *ts_handle;
|
|
u32 sample_rate;
|
|
u16 codec_version;
|
|
u8 channel_num;
|
|
u16 seqn;
|
|
u32 base_time;
|
|
u32 timestamp;
|
|
u32 ts_sample_rate;
|
|
u32 dts;//total frams
|
|
|
|
u16 delay_time;
|
|
u8 sync_step;
|
|
u8 reference;
|
|
struct a2dp_media_frame frame;
|
|
int frame_len;
|
|
void *stream_ctrl;
|
|
u8 bt_addr[6];
|
|
u8 tws_case;
|
|
u8 handshake_state;
|
|
u32 request_timeout;
|
|
u32 handshake_timeout;
|
|
/*struct stream_frame *reassembled_frame;*/
|
|
|
|
u8 link_jl_dongle; //连接jl_dongle
|
|
u8 rtp_ts_en; //使用rtp的时间戳
|
|
u16 jl_dongle_latency ;
|
|
u8 edr_to_local_time;
|
|
u8 timestamp_enable;
|
|
u32 ts_align_time;//统计时间戳对齐动作的耗时
|
|
};
|
|
|
|
extern const uint32_t CONFIG_A2DP_DELAY_TIME_AAC;
|
|
extern const uint32_t CONFIG_A2DP_DELAY_TIME_SBC;
|
|
extern const int CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
|
|
extern const int CONFIG_JL_DONGLE_PLAYBACK_DYNAMIC_LATENCY_ENABLE;
|
|
extern const int CONFIG_A2DP_DELAY_TIME_LO;
|
|
extern const int CONFIG_A2DP_SBC_DELAY_TIME_LO;
|
|
extern const int CONFIG_BTCTLER_TWS_ENABLE;
|
|
extern const int CONFIG_DONGLE_SPEAK_ENABLE;
|
|
|
|
extern void bt_audio_reference_clock_select(void *addr, u8 network);
|
|
extern u32 bt_audio_reference_clock_time(u8 network);
|
|
extern int a2dp_get_packet_pcm_frames(struct a2dp_file_hdl *hdl, u8 *data, int len);
|
|
static int a2dp_stream_ts_enable_detect(struct a2dp_file_hdl *hdl, u8 *packet, int *drop);
|
|
static void a2dp_frame_pack_timestamp(struct a2dp_file_hdl *hdl, struct stream_frame *frame, u8 *data, int pcm_frames);
|
|
static void a2dp_file_timestamp_setup(struct a2dp_file_hdl *hdl);
|
|
|
|
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);
|
|
|
|
static u8 a2dp_low_latency = 0;
|
|
|
|
#define msecs_to_bt_time(m) (((m + 1)* 1000) / 625)
|
|
#define a2dp_seqn_before(a, b) ((a < b && (u16)(b - a) < 1000) || (a > b && (u16)(a - b) > 1000))
|
|
#define RB16(b) (u16)(((u8 *)b)[0] << 8 | (((u8 *)b))[1])
|
|
#define RB32(b) (u32)(((u8 *)b)[0] << 24 | (((u8 *)b))[1] << 16 | (((u8 *)b))[2] << 8 | (((u8 *)b))[3])
|
|
|
|
#include "a2dp_handshake.c"
|
|
/*#include "a2dp_aac_demuxer.c"*/
|
|
|
|
void a2dp_file_low_latency_enable(u8 enable)
|
|
{
|
|
a2dp_low_latency = enable;
|
|
}
|
|
|
|
static void abandon_a2dp_data(void *p)
|
|
{
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)p;
|
|
struct a2dp_media_frame _frame;
|
|
while (a2dp_media_try_get_packet(hdl->file, &_frame) > 0) {
|
|
a2dp_media_free_packet(hdl->file, _frame.packet);
|
|
}
|
|
/*a2dp_media_clear_packet_before_seqn(hdl->file, 0);*/
|
|
}
|
|
|
|
static void a2dp_file_start_abandon_data(struct a2dp_file_hdl *hdl)
|
|
{
|
|
int role = TWS_ROLE_MASTER;
|
|
|
|
/*if (CONFIG_BTCTLER_TWS_ENABLE) {
|
|
role = tws_api_get_role();
|
|
}*/
|
|
if (role == TWS_ROLE_MASTER) {
|
|
if (hdl->timer == 0) {
|
|
hdl->timer = sys_timer_add(hdl, abandon_a2dp_data, 100);
|
|
puts("start_abandon_a2dp_data\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void a2dp_file_stop_abandon_data(struct a2dp_file_hdl *hdl)
|
|
{
|
|
if (hdl->timer) {
|
|
sys_timer_del(hdl->timer);
|
|
hdl->timer = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void a2dp_source_wake_jlstream_run(void *_hdl)
|
|
{
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
|
|
|
if (hdl->start && (hdl->node->state & NODE_STA_SOURCE_NO_DATA)) {
|
|
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
|
}
|
|
}
|
|
static void a2dp_source_direct_wake_jlstream_run(void *_hdl)
|
|
{
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
|
if (hdl) {
|
|
if (hdl->node) {
|
|
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
|
}
|
|
hdl->ts_align_time += 4;
|
|
}
|
|
}
|
|
|
|
static enum stream_node_state a2dp_get_frame(void *_hdl, struct stream_frame **pframe)
|
|
{
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
|
struct a2dp_media_frame _frame;
|
|
int drop = 0;
|
|
int stream_error = 0;
|
|
|
|
*pframe = NULL;
|
|
|
|
#if A2DP_TIMESTAMP_ENABLE
|
|
if (!hdl->rtp_ts_en && !hdl->ts_handle) {
|
|
int err = a2dp_tws_media_handshake(hdl);
|
|
if (hdl->timestamp_enable && err) {
|
|
if (!hdl->wake_up_timer) {//快速唤醒数据流,加速tws时间戳交互的过程
|
|
hdl->ts_align_time = 0;
|
|
hdl->wake_up_timer = sys_hi_timer_add((void *)hdl, a2dp_source_direct_wake_jlstream_run, 4);
|
|
}
|
|
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
|
}
|
|
log_d(">>>>handshake time %d ms<<<<\n", hdl->ts_align_time);
|
|
if (hdl->wake_up_timer) {
|
|
sys_hi_timer_del(hdl->wake_up_timer);
|
|
hdl->wake_up_timer = 0;
|
|
}
|
|
a2dp_file_timestamp_setup(hdl);
|
|
}
|
|
#endif
|
|
if ((!hdl->ts_handle /* || hdl->edr_to_local_time */) && hdl->start == 0) {
|
|
int delay = a2dp_media_get_remain_play_time(hdl->file, 1);
|
|
if (delay < (hdl->ts_handle ? hdl->delay_time : 300)) {
|
|
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
|
}
|
|
hdl->start = 1;
|
|
}
|
|
|
|
int len = hdl->frame_len;
|
|
if (len == 0) {
|
|
if (hdl->stream_ctrl) {
|
|
stream_error = a2dp_stream_control_pull_frame(hdl->stream_ctrl, &_frame, &len);
|
|
} else {
|
|
len = a2dp_media_try_get_packet(hdl->file, &_frame);
|
|
}
|
|
if (len <= 0) {
|
|
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
|
}
|
|
hdl->frame_len = len;
|
|
memcpy(&hdl->frame, &_frame, sizeof(struct a2dp_media_frame));
|
|
} else {
|
|
memcpy(&_frame, &hdl->frame, sizeof(struct a2dp_media_frame));
|
|
}
|
|
|
|
hdl->seqn = RB16((u8 *)_frame.packet + 2);
|
|
int err = a2dp_stream_ts_enable_detect(hdl, _frame.packet, &drop);
|
|
if (err) {
|
|
if (drop) {
|
|
if (hdl->stream_ctrl) {
|
|
a2dp_stream_control_free_frame(hdl->stream_ctrl, &_frame);
|
|
} else {
|
|
a2dp_media_free_packet(hdl->file, _frame.packet);
|
|
}
|
|
hdl->frame_len = 0;
|
|
}
|
|
a2dp_tws_media_try_handshake_ack(0, hdl->seqn);
|
|
if (!hdl->wake_up_timer) {//快速唤醒数据流,加速tws时间戳交互的过程
|
|
hdl->ts_align_time = 0;
|
|
hdl->wake_up_timer = sys_hi_timer_add((void *)hdl, a2dp_source_direct_wake_jlstream_run, 4);
|
|
}
|
|
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
|
}
|
|
if (hdl->wake_up_timer) {
|
|
sys_hi_timer_del(hdl->wake_up_timer);
|
|
hdl->wake_up_timer = 0;
|
|
}
|
|
|
|
int head_len = a2dp_media_get_rtp_header_len(hdl->media_type, _frame.packet, len);
|
|
|
|
struct stream_frame *frame;
|
|
int frame_len = len - head_len;
|
|
frame = jlstream_get_frame(hdl->node->oport, frame_len);
|
|
if (frame == NULL) {
|
|
return NODE_STA_RUN;
|
|
}
|
|
frame->len = frame_len;
|
|
frame->timestamp = _frame.clkn;
|
|
frame->flags |= (stream_error);
|
|
a2dp_frame_pack_timestamp(hdl, frame, _frame.packet + 4, //时间戳的地址
|
|
a2dp_get_packet_pcm_frames(hdl,
|
|
_frame.packet + head_len, frame_len));
|
|
|
|
a2dp_tws_media_try_handshake_ack(1, hdl->seqn);
|
|
|
|
memcpy(frame->data, _frame.packet + head_len, frame_len);
|
|
|
|
if (hdl->stream_ctrl) {
|
|
a2dp_stream_control_free_frame(hdl->stream_ctrl, &_frame);
|
|
} else {
|
|
a2dp_media_free_packet(hdl->file, _frame.packet);
|
|
}
|
|
hdl->frame_len = 0;
|
|
hdl->start = 1;
|
|
|
|
ASSERT(frame);
|
|
*pframe = frame;
|
|
|
|
return NODE_STA_RUN;
|
|
}
|
|
|
|
static void *a2dp_init(void *priv, struct stream_node *node)
|
|
{
|
|
struct a2dp_file_hdl *hdl = zalloc(sizeof(*hdl));
|
|
hdl->node = node;
|
|
u16 plug_uuid = get_source_node_plug_uuid(priv);
|
|
|
|
struct a2dp_file_params params = {0};
|
|
jlstream_read_node_data_by_cfg_index(plug_uuid, hdl->node->subid, 0, (void *)¶ms, NULL);
|
|
hdl->edr_to_local_time = params.edr_to_local_time;
|
|
return hdl;
|
|
}
|
|
|
|
static const u32 aac_sample_rates[] = {
|
|
96000, 88200, 64000, 48000, 44100, 32000,
|
|
24000, 22050, 16000, 12000, 11025, 8000,
|
|
};
|
|
|
|
static const u16 sbc_sample_rates[] = {16000, 32000, 44100, 48000};
|
|
|
|
static const u32 ldac_sample_rates[] = {44100, 48000, 88200, 96000};
|
|
|
|
static int a2dp_ioc_get_fmt(struct a2dp_file_hdl *hdl, struct stream_fmt *fmt)
|
|
{
|
|
struct a2dp_media_frame _frame;
|
|
int type = a2dp_media_get_codec_type(hdl->file);
|
|
char *code_type;
|
|
switch (type) {
|
|
case A2DP_CODEC_SBC:
|
|
fmt->coding_type = AUDIO_CODING_SBC;
|
|
code_type = "SBC";
|
|
break;
|
|
#if (defined(TCFG_BT_SUPPORT_AAC) && TCFG_BT_SUPPORT_AAC)
|
|
case A2DP_CODEC_MPEG24:
|
|
fmt->coding_type = AUDIO_CODING_AAC;
|
|
code_type = "AAC";
|
|
break;
|
|
#endif
|
|
#if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC)
|
|
case A2DP_CODEC_LDAC:
|
|
fmt->coding_type = AUDIO_CODING_LDAC;
|
|
code_type = "LDAC";
|
|
break;
|
|
#endif
|
|
#if (defined(TCFG_BT_SUPPORT_LHDC) && TCFG_BT_SUPPORT_LHDC)
|
|
case A2DP_CODEC_LHDC: //LHDC 直接从蓝牙获取格式信息。
|
|
fmt->coding_type = AUDIO_CODING_LHDC;
|
|
fmt->sample_rate = a2dp_media_get_sample_rate(hdl->file);
|
|
fmt->dec_bit_wide = a2dp_media_get_bit_wide(hdl->file);
|
|
fmt->codec_version = a2dp_media_get_codec_version(hdl->file);
|
|
fmt->channel_mode = AUDIO_CH_LR;
|
|
hdl->media_type = type;
|
|
hdl->codec_version = fmt->codec_version;
|
|
hdl->sample_rate = fmt->sample_rate;
|
|
hdl->channel_num = (fmt->channel_mode == AUDIO_CH_LR) ? 2 : 1;
|
|
|
|
printf("a2dp format %s, sample_rate %d, bit_wide %d, codec_version %d\n",
|
|
"lhdc", hdl->sample_rate, fmt->bit_wide, fmt->codec_version);
|
|
return 0;
|
|
#endif
|
|
default:
|
|
code_type = "unknown";
|
|
break;
|
|
}
|
|
|
|
if (hdl->sample_rate) {
|
|
fmt->sample_rate = hdl->sample_rate;
|
|
fmt->channel_mode = hdl->channel_num == 2 ? AUDIO_CH_LR : AUDIO_CH_MIX;
|
|
return 0;
|
|
}
|
|
hdl->media_type = type;
|
|
|
|
__again:
|
|
int len = a2dp_media_try_get_packet(hdl->file, &_frame);
|
|
if (len <= 0) {
|
|
return -EAGAIN;
|
|
}
|
|
u8 *packet = _frame.packet;
|
|
|
|
int head_len = a2dp_media_get_rtp_header_len(type, packet, len);
|
|
if (head_len >= len) {
|
|
a2dp_media_free_packet(hdl->file, packet);
|
|
goto __again;
|
|
}
|
|
/*put_buf(packet, head_len + 8);*/
|
|
u8 *frame = packet + head_len;
|
|
if (frame[0] == 0x47) { //常见mux aac格式
|
|
#if (defined(TCFG_BT_SUPPORT_AAC) && TCFG_BT_SUPPORT_AAC)
|
|
u8 sr = (frame[5] & 0x3C) >> 2;
|
|
/* u8 ch = ((frame[5] & 0x3) << 2) | ((frame[6] & 0xC0) >> 6); */
|
|
fmt->channel_mode = AUDIO_CH_LR;
|
|
fmt->sample_rate = aac_sample_rates[sr];
|
|
} else if (frame[0] == 0x20) { //特殊LATM aac格式
|
|
u8 sr = ((frame[2] & 0x7) << 1) | ((frame[3] & 0x80) >> 7);
|
|
/* u8 ch = ((frame[3] & 0x78) >> 3) ; */
|
|
fmt->channel_mode = AUDIO_CH_LR;
|
|
fmt->sample_rate = aac_sample_rates[sr];
|
|
#endif
|
|
} else if (frame[0] == 0x9C) { //sbc 格式
|
|
/*
|
|
* 检查数据是否为AAC格式,
|
|
* 可以切换AAC和SBC格式的手机可能切换后数据格式和type不对应
|
|
*/
|
|
head_len = a2dp_media_get_rtp_header_len(A2DP_CODEC_MPEG24, packet, len);
|
|
if (head_len < len) {
|
|
if (packet[head_len] == 0x47 || packet[head_len] == 0x20) {
|
|
a2dp_media_free_packet(hdl->file, packet);
|
|
goto __again;
|
|
}
|
|
}
|
|
|
|
u8 sr = (frame[1] >> 6) & 0x3;
|
|
u8 ch = (frame[1] >> 2) & 0x3;
|
|
if (ch == 0) {
|
|
fmt->channel_mode = AUDIO_CH_MIX;
|
|
} else {
|
|
fmt->channel_mode = AUDIO_CH_LR;
|
|
}
|
|
fmt->sample_rate = sbc_sample_rates[sr];
|
|
#if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC)
|
|
} else if (frame[1] == 0xAA) {
|
|
u8 sr = (frame[2] >> (8 - 3)) & 0x7;
|
|
int chconfig_id = (frame[2] >> (8 - 5)) & 0x03;
|
|
|
|
fmt->channel_mode = AUDIO_CH_LR;
|
|
fmt ->sample_rate = ldac_sample_rates[sr];
|
|
fmt->chconfig_id = chconfig_id;
|
|
//printf(" %x %x %x\n",frame[0],frame[1],frame[2]);
|
|
//printf("sr:%d, sample_rate : %d chconfig_id : %d\n",sr,fmt->sample_rate,chconfig_id);
|
|
#endif
|
|
} else {
|
|
/*
|
|
* 小米8手机先播sbc,暂停后切成AAC格式点播放,有时第一包数据还是sbc格式
|
|
* 导致这里获取头信息错误
|
|
*/
|
|
a2dp_media_free_packet(hdl->file, packet);
|
|
goto __again;
|
|
}
|
|
a2dp_media_put_packet(hdl->file, packet);
|
|
|
|
hdl->sample_rate = fmt->sample_rate;
|
|
hdl->channel_num = (fmt->channel_mode == AUDIO_CH_LR) ? 2 : 1;
|
|
printf("a2dp %d, format %s\n", hdl->sample_rate, code_type);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int a2dp_ioc_set_bt_addr(struct a2dp_file_hdl *hdl, u8 *bt_addr)
|
|
{
|
|
hdl->file = a2dp_open_media_file(bt_addr);
|
|
if (!hdl->file) {
|
|
printf("open_file_faild\n");
|
|
put_buf(bt_addr, 6);
|
|
return -EINVAL;
|
|
}
|
|
memcpy(hdl->bt_addr, bt_addr, 6);
|
|
|
|
if (CONFIG_DONGLE_SPEAK_ENABLE) {
|
|
if (btstack_get_dev_type_for_addr(hdl->bt_addr) == REMOTE_DEV_DONGLE_SPEAK) {
|
|
hdl->link_jl_dongle = 1;
|
|
hdl->jl_dongle_latency = CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
|
|
if (!CONFIG_JL_DONGLE_PLAYBACK_DYNAMIC_LATENCY_ENABLE) {
|
|
hdl->rtp_ts_en = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void a2dp_ioc_stop(struct a2dp_file_hdl *hdl)
|
|
{
|
|
if (hdl->wake_up_timer) {
|
|
sys_hi_timer_del(hdl->wake_up_timer);
|
|
hdl->wake_up_timer = 0;
|
|
}
|
|
/*hdl->sample_rate = 0;*/
|
|
if (hdl->frame_len) {
|
|
if (hdl->stream_ctrl) {
|
|
a2dp_stream_control_free_frame(hdl->stream_ctrl, &hdl->frame);
|
|
} else {
|
|
a2dp_media_free_packet(hdl->file, hdl->frame.packet);
|
|
}
|
|
hdl->frame_len = 0;
|
|
}
|
|
a2dp_file_stop_abandon_data(hdl);
|
|
/*a2dp_media_clear_packet_before_seqn(hdl->file, 0);*/
|
|
a2dp_media_stop_play(hdl->file);
|
|
hdl->start = 0;
|
|
}
|
|
|
|
static int sbc_get_packet_pcm_frames(u8 *data, int len)
|
|
{
|
|
data++;
|
|
|
|
u8 ch = (data[0] >> 2) & 0x3;
|
|
u8 subbands = (data[0] & 0x01) ? 8 : 4;
|
|
u8 blocks = (((data[0] >> 4) & 0x03) + 1) * 4;
|
|
u8 channels = ch == 0x0 ? 1 : 2;
|
|
u8 joint = ch == 0x3 ? 1 : 0;
|
|
u8 bitpool = data[1] & 0xff;
|
|
int frame_len = 4 + ((4 * subbands * channels) >> 3);
|
|
if (ch >= 0x2) {
|
|
frame_len += (((joint ? subbands : 0) + blocks * bitpool) + 7) >> 3;
|
|
} else {
|
|
frame_len += ((blocks * channels * bitpool) + 7) >> 3;
|
|
}
|
|
|
|
return (len / frame_len) * (blocks * subbands);
|
|
}
|
|
|
|
static int lhdc_get_packet_pcm_frames(void *_hdl, u8 *data, int len, int unit)
|
|
{
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
|
int point = 0;
|
|
|
|
u8 frame_num = (data[0] & 0x3C) >> 2;
|
|
|
|
//目前测试LHDC 每帧输出点数是这样的。如果存在兼容性问题。这里需要同步兼容
|
|
if (hdl->codec_version == 400) { //lhdc_v4
|
|
point = 256;
|
|
} else if (hdl->codec_version == 500) {
|
|
/* if(hdl->sample_rate == 48000){ */
|
|
/* point = 240 ; */
|
|
/* }else if(hdl->sample_rate == 44100){ */
|
|
/* point = 220; */
|
|
/* } */
|
|
point = hdl->sample_rate / 1000 * 5; //5ms 的数据量
|
|
} else if (hdl->codec_version == 300) { //lhdc_v3
|
|
point = 256;
|
|
}
|
|
|
|
return frame_num * point * (unit);
|
|
|
|
}
|
|
|
|
|
|
static int a2dp_get_packet_pcm_frames(struct a2dp_file_hdl *hdl, u8 *data, int len)
|
|
{
|
|
u8 unit = 1;
|
|
u32 frames = 0;
|
|
|
|
switch (hdl->media_type) {
|
|
case A2DP_CODEC_SBC:
|
|
frames = sbc_get_packet_pcm_frames(data, len);//frame_num * 128 * (unit);
|
|
break;
|
|
case A2DP_CODEC_MPEG24:
|
|
u32 audio_mux_element = 0xffffffff;
|
|
memcpy(&audio_mux_element, data, len >= sizeof(audio_mux_element) ? sizeof(audio_mux_element) : len);
|
|
if (audio_mux_element == 0xFC47 || audio_mux_element == 0x10120020) {
|
|
frames = 1024 * (unit);
|
|
}
|
|
break;
|
|
#if (defined(TCFG_BT_SUPPORT_LHDC) && TCFG_BT_SUPPORT_LHDC)
|
|
case A2DP_CODEC_LHDC:
|
|
frames = lhdc_get_packet_pcm_frames(hdl, data, len, unit);//frame_num * point * (unit);
|
|
break;
|
|
#endif
|
|
#if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC)
|
|
case A2DP_CODEC_LDAC:
|
|
u8 frame_num = data[0] & 0xf;
|
|
/* printf("ldac : frame_num = %d\n",frame_num); */
|
|
if (hdl->sample_rate <= 48000) {
|
|
frames = frame_num * 128 * (unit);//frame_num * point * (unit);
|
|
} else {
|
|
frames = frame_num * 256 * (unit);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
log_e("unsupport codec_type : 0x%x\n", hdl->media_type);
|
|
break;
|
|
}
|
|
|
|
/* printf("frames %d\n", frames); */
|
|
return frames;
|
|
}
|
|
|
|
|
|
static void a2dp_stream_control_open(struct a2dp_file_hdl *hdl)
|
|
{
|
|
/*
|
|
* 策略选择:
|
|
* 1、是否低延时
|
|
* 2、解码格式?
|
|
* 3、策略方案 - 默认0,其他为定制方案
|
|
*/
|
|
if (hdl->stream_ctrl) {
|
|
return;
|
|
}
|
|
if (hdl->link_jl_dongle) {
|
|
hdl->stream_ctrl = a2dp_stream_control_plan_select(hdl->file, a2dp_low_latency, hdl->media_type, A2DP_STREAM_JL_DONGLE_CONTROL);
|
|
|
|
} else {
|
|
hdl->stream_ctrl = a2dp_stream_control_plan_select(hdl->file, a2dp_low_latency, hdl->media_type, 0);
|
|
}
|
|
if (hdl->stream_ctrl) {
|
|
hdl->delay_time = a2dp_stream_control_delay_time(hdl->stream_ctrl);
|
|
a2dp_stream_control_set_underrun_callback(hdl->stream_ctrl, hdl, a2dp_source_wake_jlstream_run);
|
|
}
|
|
}
|
|
|
|
static void a2dp_stream_control_close(struct a2dp_file_hdl *hdl)
|
|
{
|
|
if (hdl->stream_ctrl) {
|
|
a2dp_stream_control_free(hdl->stream_ctrl);
|
|
hdl->stream_ctrl = NULL;
|
|
}
|
|
}
|
|
|
|
static u32 a2dp_stream_update_base_time(struct a2dp_file_hdl *hdl)
|
|
{
|
|
struct a2dp_media_frame frame;
|
|
int distance_time = 0;
|
|
int len = a2dp_media_try_get_packet(hdl->file, &frame);
|
|
if (len > 0) {
|
|
u32 base_clkn = frame.clkn;
|
|
/* if (CONFIG_BTCTLER_TWS_ENABLE && a2dp_low_latency) { */
|
|
/* base_clkn = bt_audio_reference_clock_time(0); */
|
|
/* } */
|
|
a2dp_media_put_packet(hdl->file, frame.packet);
|
|
u32 base_time = base_clkn + msecs_to_bt_time((hdl->delay_time < 100 ? 100 : hdl->delay_time));
|
|
if ((int)(base_time - bt_audio_reference_clock_time(0)) < msecs_to_bt_time(150)) {//启动过程耗时很长,此处为避免时间戳超时,加上150ms
|
|
base_time = bt_audio_reference_clock_time(0) + msecs_to_bt_time(150);
|
|
}
|
|
return base_time;
|
|
}
|
|
distance_time = a2dp_low_latency ? hdl->delay_time : (hdl->delay_time - a2dp_media_get_remain_play_time(hdl->file, 1));
|
|
if (!a2dp_low_latency) {
|
|
distance_time = hdl->delay_time;
|
|
} else if (distance_time < 100) {
|
|
distance_time = 100;
|
|
}
|
|
/*printf("distance time : %d, %d, %d\n", hdl->delay_time, a2dp_media_get_remain_play_time(hdl->file, 1), distance_time);*/
|
|
return bt_audio_reference_clock_time(0) + msecs_to_bt_time(distance_time);
|
|
}
|
|
|
|
|
|
void a2dp_ts_handle_create(struct a2dp_file_hdl *hdl)
|
|
{
|
|
if (!hdl || (hdl->rtp_ts_en)) {
|
|
return;
|
|
}
|
|
if (hdl->ts_handle) {
|
|
return;
|
|
}
|
|
#if A2DP_TIMESTAMP_ENABLE
|
|
if (!hdl->timestamp_enable) {
|
|
return;
|
|
}
|
|
|
|
hdl->base_time = a2dp_stream_update_base_time(hdl);
|
|
int check_diff = hdl->base_time - bt_audio_reference_clock_time(0);
|
|
if (check_diff < 0) {
|
|
printf("a2dp base_time is outdated: %d ms\n", (int)(check_diff * 0.625));
|
|
} else {
|
|
printf("a2dp features play time: after %d ms\n", (int)(check_diff * 0.625));
|
|
}
|
|
printf("a2dp timestamp base time : %d, %d\n", hdl->base_time, bt_audio_reference_clock_time(0));
|
|
hdl->ts_handle = a2dp_audio_timestamp_create(hdl->sample_rate, hdl->base_time, TIMESTAMP_US_DENOMINATOR);
|
|
if (hdl->edr_to_local_time) {
|
|
bt_edr_conn_system_clock_init(hdl->bt_addr, TIMESTAMP_US_DENOMINATOR);
|
|
/*printf("--[%s - %d] bt edr system clock init : %u, %lu--\n", __FUNCTION__, __LINE__, hdl->base_time, jiffies_usec());*/
|
|
}
|
|
hdl->sync_step = 0;
|
|
hdl->frame_len = 0;
|
|
hdl->dts = 0;
|
|
#endif
|
|
}
|
|
|
|
void a2dp_ts_handle_release(struct a2dp_file_hdl *hdl)
|
|
{
|
|
if (!hdl) {
|
|
return;
|
|
}
|
|
#if A2DP_TIMESTAMP_ENABLE
|
|
if (hdl->ts_handle) {
|
|
a2dp_audio_timestamp_close(hdl->ts_handle);
|
|
hdl->ts_handle = NULL;
|
|
a2dp_tws_audio_conn_offline();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void a2dp_frame_pack_timestamp(struct a2dp_file_hdl *hdl, struct stream_frame *frame, u8 *data, int pcm_frames)
|
|
{
|
|
if (CONFIG_DONGLE_SPEAK_ENABLE) {
|
|
if (hdl->link_jl_dongle && hdl->rtp_ts_en) {
|
|
u32 ts = RB32(data);
|
|
frame->timestamp = ts + hdl->jl_dongle_latency * 1000 * 32;
|
|
frame->flags |= (FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_UPDATE_TIMESTAMP);
|
|
/* printf("ts : %u, %u, %u\n",ts,frame->timestamp, bt_audio_reference_clock_time(0)); */
|
|
return;
|
|
}
|
|
}
|
|
if (!hdl->ts_handle || pcm_frames == 0) {
|
|
frame->flags &= ~FRAME_FLAG_TIMESTAMP_ENABLE;
|
|
return;
|
|
}
|
|
|
|
if (CONFIG_BTCTLER_TWS_ENABLE && (frame->flags & FRAME_FLAG_RESET_TIMESTAMP_BIT)) {
|
|
/*printf("----stream error : resume----\n");*/
|
|
tws_a2dp_share_timestamp(hdl->ts_handle);
|
|
}
|
|
u32 timestamp = a2dp_audio_update_timestamp(hdl->ts_handle, hdl->seqn, hdl->dts);
|
|
int delay_time = hdl->stream_ctrl ? a2dp_stream_control_delay_time(hdl->stream_ctrl) : hdl->delay_time;
|
|
int frame_delay = (timestamp - (frame->timestamp * 625 * TIME_US_FACTOR)) / 1000 / TIME_US_FACTOR;
|
|
/*int distance_time = (int)(timestamp - (frame->timestamp * 625 * TIME_US_FACTOR)) / 1000 / TIME_US_FACTOR - delay_time;*/
|
|
int distance_time = frame_delay - delay_time;
|
|
a2dp_audio_delay_offset_update(hdl->ts_handle, distance_time);
|
|
frame->flags |= (FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_UPDATE_TIMESTAMP | FRAME_FLAG_UPDATE_DRIFT_SAMPLE_RATE);
|
|
if (hdl->edr_to_local_time) {
|
|
timestamp = bt_edr_conn_master_to_local_time(hdl->bt_addr, timestamp);
|
|
}
|
|
frame->timestamp = timestamp;
|
|
frame->d_sample_rate = a2dp_audio_sample_rate(hdl->ts_handle) - hdl->sample_rate;
|
|
/*printf("drift : %d\n", frame->d_sample_rate);*/
|
|
/*printf("-%u, %u, %u-\n", timestamp, bt_edr_conn_master_to_local_time(hdl->bt_addr, timestamp), local_time);*/
|
|
hdl->dts += pcm_frames;
|
|
a2dp_stream_mark_next_timestamp(hdl->stream_ctrl, timestamp + PCM_SAMPLE_TO_TIMESTAMP(pcm_frames, hdl->sample_rate));
|
|
a2dp_stream_bandwidth_detect_handler(hdl->stream_ctrl, pcm_frames, hdl->sample_rate);
|
|
}
|
|
|
|
static int a2dp_stream_ts_enable_detect(struct a2dp_file_hdl *hdl, u8 *packet, int *drop)
|
|
{
|
|
if (hdl->sync_step) {
|
|
return 0;
|
|
}
|
|
|
|
if (!drop) {
|
|
printf("wrong argument 'drop'!\n");
|
|
}
|
|
if (CONFIG_BTCTLER_TWS_ENABLE && hdl->ts_handle) {
|
|
if (hdl->tws_case != 1 && \
|
|
!a2dp_audio_timestamp_is_available(hdl->ts_handle, hdl->seqn, 0, drop)) {
|
|
if (*drop) {
|
|
hdl->base_time = a2dp_stream_update_base_time(hdl);
|
|
a2dp_audio_set_base_time(hdl->ts_handle, hdl->base_time);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
log_d(">>>>ts align time %d ms<<<<\n", hdl->ts_align_time);
|
|
hdl->sync_step = 2;
|
|
return 0;
|
|
}
|
|
|
|
static void a2dp_media_reference_time_setup(struct a2dp_file_hdl *hdl)
|
|
{
|
|
#if A2DP_TIMESTAMP_ENABLE
|
|
int err = stream_node_ioctl(hdl->node, NODE_UUID_BT_AUDIO_SYNC, NODE_IOC_SYNCTS, 0);
|
|
if (err) {
|
|
err = stream_node_ioctl(hdl->node, NODE_UUID_CAPTURE_SYNC, NODE_IOC_SYNCTS, 0);
|
|
if (err) {
|
|
return;
|
|
}
|
|
/* hdl->edr_to_local_time = 1; //由界面配置*/
|
|
}
|
|
if (CONFIG_BTCTLER_TWS_ENABLE) {
|
|
a2dp_tws_audio_conn_delete();//处理两边交流时主机吴判断从机离线,结束对齐时间戳的情况
|
|
}
|
|
|
|
hdl->timestamp_enable = 1;
|
|
if (!hdl->edr_to_local_time) {
|
|
hdl->reference = audio_reference_clock_select(hdl->bt_addr, 0);//0 - a2dp主机,1 - tws, 2 - BLE
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void a2dp_media_reference_time_close(struct a2dp_file_hdl *hdl)
|
|
{
|
|
#if A2DP_TIMESTAMP_ENABLE
|
|
if (!hdl->edr_to_local_time) {
|
|
audio_reference_clock_exit(hdl->reference);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void a2dp_file_timestamp_setup(struct a2dp_file_hdl *hdl)
|
|
{
|
|
a2dp_stream_control_open(hdl);
|
|
a2dp_ts_handle_create(hdl);
|
|
}
|
|
|
|
static void a2dp_file_timestamp_close(struct a2dp_file_hdl *hdl)
|
|
{
|
|
a2dp_tws_media_handshake_exit(hdl);
|
|
a2dp_stream_control_close(hdl);
|
|
a2dp_ts_handle_release(hdl);
|
|
}
|
|
|
|
static int a2dp_ioctl(void *_hdl, int cmd, int arg)
|
|
{
|
|
int err = 0;
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
|
|
|
switch (cmd) {
|
|
case NODE_IOC_SET_BTADDR:
|
|
err = a2dp_ioc_set_bt_addr(hdl, (u8 *)arg);
|
|
break;
|
|
case NODE_IOC_GET_BTADDR:
|
|
memcpy((u8 *)arg, hdl->bt_addr, 6);
|
|
break;
|
|
case NODE_IOC_GET_FMT:
|
|
err = a2dp_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
|
stream_node_ioctl(hdl->node, NODE_UUID_BT_AUDIO_SYNC, NODE_IOC_SET_SYNC_NETWORK, hdl->edr_to_local_time ? AUDIO_NETWORK_LOCAL : AUDIO_NETWORK_BT2_1);
|
|
break;
|
|
case NODE_IOC_SET_PRIV_FMT:
|
|
break;
|
|
case NODE_IOC_START:
|
|
a2dp_media_start_play(hdl->file);
|
|
a2dp_media_set_rx_notify(hdl->file, hdl, a2dp_source_wake_jlstream_run);
|
|
a2dp_file_stop_abandon_data(hdl);
|
|
a2dp_media_reference_time_setup(hdl);
|
|
break;
|
|
case NODE_IOC_SUSPEND:
|
|
/*hdl->sample_rate = 0;*/
|
|
a2dp_media_set_rx_notify(hdl->file, NULL, NULL);
|
|
a2dp_ioc_stop(hdl);
|
|
a2dp_file_timestamp_close(hdl);
|
|
a2dp_media_reference_time_close(hdl);
|
|
a2dp_file_start_abandon_data(hdl);
|
|
break;
|
|
case NODE_IOC_STOP:
|
|
a2dp_media_set_rx_notify(hdl->file, NULL, NULL);
|
|
a2dp_ioc_stop(hdl);
|
|
a2dp_file_timestamp_close(hdl);
|
|
a2dp_media_reference_time_close(hdl);
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void a2dp_release(void *_hdl)
|
|
{
|
|
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
|
|
|
a2dp_close_media_file(hdl->file);
|
|
a2dp_file_stop_abandon_data(hdl);
|
|
|
|
free(hdl);
|
|
}
|
|
|
|
|
|
REGISTER_SOURCE_NODE_PLUG(a2dp_file_plug) = {
|
|
.uuid = NODE_UUID_A2DP_RX,
|
|
.frame_size = 1024,
|
|
.init = a2dp_init,
|
|
.get_frame = a2dp_get_frame,
|
|
.ioctl = a2dp_ioctl,
|
|
.release = a2dp_release,
|
|
};
|
|
|
|
|
|
|
|
#endif /* #if TCFG_USER_BT_CLASSIC_ENABLE */
|
|
|