Files
AC707N/SDK/audio/framework/plugs/source/a2dp_streamctrl.c
T
2025-12-03 11:12:34 +08:00

538 lines
17 KiB
C

#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".a2dp_streamctrl.data.bss")
#pragma data_seg(".a2dp_streamctrl.data")
#pragma const_seg(".a2dp_streamctrl.text.const")
#pragma code_seg(".a2dp_streamctrl.text")
#endif
/*************************************************************************************************/
/*!
* \file a2dp_streamctrl.c
*
* \brief a2dp plug stream control file.
*
* Copyright (c) 2011-2022 ZhuHai Jieli Technology Co.,Ltd.
*
*/
/*************************************************************************************************/
#include "btstack/a2dp_media_codec.h"
#include "sync/audio_syncts.h"
#include "system/timer.h"
#include "media/audio_base.h"
#include "source_node.h"
#include "jiffies.h"
#include "a2dp_streamctrl.h"
extern const int CONFIG_A2DP_DELAY_TIME_AAC;
extern const int CONFIG_A2DP_DELAY_TIME_SBC;
extern const int CONFIG_A2DP_ADAPTIVE_MAX_LATENCY;
extern const int CONFIG_A2DP_DELAY_TIME_AAC_LO;
extern const int CONFIG_A2DP_DELAY_TIME_SBC_LO;
extern const int CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
extern const int CONFIG_DONGLE_SPEAK_ENABLE;
extern const int CONFIG_A2DP_MAX_BUF_SIZE;
extern u32 bt_audio_reference_clock_time(u8 network);
#define A2DP_FLUENT_DETECT_INTERVAL 90000//ms 流畅播放延时检测时长
#define JL_DONGLE_FLUENT_DETECT_INTERVAL 30000//ms
#define A2DP_ADAPTIVE_DELAY_ENABLE 1
#define A2DP_STREAM_NO_ERR 0
#define A2DP_STREAM_UNDERRUN 1
#define A2DP_STREAM_OVERRUN 2
#define A2DP_STREAM_MISSED 3
#define A2DP_STREAM_DECODE_ERR 4
#define A2DP_STREAM_LOW_UNDERRUN 5
#define RB16(b) (u16)(((u8 *)b)[0] << 8 | (((u8 *)b))[1])
#define bt_time_to_msecs(clk) (((clk) * 625) / 1000)
#define DELAY_INCREMENT 150
#define LOW_LATENCY_DELAY_INCREMENT 50
#define DELAY_DECREMENT 50
#define LOW_LATENCY_DELAY_DECREMENT 10
#define MAX_DELAY_INCREMENT 150
struct a2dp_stream_control {
u8 plan;
u8 stream_error;
u8 frame_free;
u8 first_in;
u8 low_latency;
u8 jl_dongle;
u16 timer;
u16 seqn;
u16 overrun_seqn;
u16 missed_num;
s16 repair_frames;
s16 initial_latency;
s16 adaptive_latency;
s16 adaptive_max_latency;
s16 max_rx_interval;
u32 detect_timeout;
u32 codec_type;
u32 frame_time;
struct a2dp_media_frame frame;
int frame_len;
void *stream;
void *sample_detect;
u32 next_timestamp;
void *underrun_signal;
void (*underrun_callback)(void *);
};
void a2dp_stream_mark_next_timestamp(void *_ctrl, u32 next_timestamp)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
if (ctrl) {
ctrl->next_timestamp = next_timestamp;
}
}
void a2dp_stream_bandwidth_detect_handler(void *_ctrl, int pcm_frames, int sample_rate)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
int max_latency = 0;
if (ctrl->low_latency) {
return;
}
if (ctrl->frame_len) {
max_latency = (CONFIG_A2DP_MAX_BUF_SIZE * pcm_frames / ctrl->frame_len) * 1000 / sample_rate * 9 / 10;
}
if (!max_latency) {
return;
}
if (max_latency < ctrl->adaptive_max_latency) {
ctrl->adaptive_max_latency = max_latency;
}
if (ctrl->adaptive_latency > ctrl->adaptive_max_latency) {
ctrl->adaptive_latency = ctrl->adaptive_max_latency;
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
}
}
static void a2dp_stream_underrun_adaptive_handler(struct a2dp_stream_control *ctrl)
{
#if A2DP_ADAPTIVE_DELAY_ENABLE
ctrl->detect_timeout = jiffies + msecs_to_jiffies(ctrl->jl_dongle ? JL_DONGLE_FLUENT_DETECT_INTERVAL : A2DP_FLUENT_DETECT_INTERVAL);
if (!ctrl->low_latency) {
ctrl->adaptive_latency = ctrl->adaptive_max_latency;
} else {
ctrl->adaptive_latency += LOW_LATENCY_DELAY_INCREMENT;
if (ctrl->adaptive_latency < ctrl->max_rx_interval) {
ctrl->adaptive_latency = ctrl->max_rx_interval;
}
if (ctrl->adaptive_latency > ctrl->adaptive_max_latency) {
ctrl->adaptive_latency = ctrl->adaptive_max_latency;
}
}
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
/*printf("---underrun, adaptive : %dms---\n", ctrl->adaptive_latency);*/
#endif
}
/*
* 自适应延时策略
*/
static void a2dp_stream_adaptive_detect_handler(struct a2dp_stream_control *ctrl, u8 new_frame, u32 new_frame_time)
{
#if A2DP_ADAPTIVE_DELAY_ENABLE
if (ctrl->stream_error || !new_frame) {
return;
}
int rx_interval = 0;
if (ctrl->frame_time) {
rx_interval = bt_time_to_msecs((new_frame_time - ctrl->frame_time) & 0x7ffffff) + 1;
}
ctrl->frame_time = new_frame_time;
if (rx_interval > ctrl->max_rx_interval) {
if (CONFIG_DONGLE_SPEAK_ENABLE && ctrl->jl_dongle) {
return;
}
ctrl->max_rx_interval = rx_interval;
if (ctrl->max_rx_interval > ctrl->initial_latency + MAX_DELAY_INCREMENT) {
ctrl->max_rx_interval = ctrl->initial_latency + MAX_DELAY_INCREMENT;
}
if (ctrl->adaptive_latency < ctrl->max_rx_interval) {
ctrl->adaptive_latency = ctrl->max_rx_interval;
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
ctrl->max_rx_interval = ctrl->initial_latency;
return;
}
/*printf("---rx interval, adaptive : %dms, %dms---\n", ctrl->adaptive_latency, ctrl->max_rx_interval);*/
}
if (time_after(jiffies, ctrl->detect_timeout)) {
ctrl->adaptive_latency -= DELAY_DECREMENT;
if (ctrl->adaptive_latency < ctrl->max_rx_interval) {
ctrl->adaptive_latency = ctrl->max_rx_interval;
}
if (ctrl->adaptive_latency < ctrl->initial_latency) {
ctrl->adaptive_latency = ctrl->initial_latency;
}
/*printf("---adaptive detect : %dms, %dms---\n", ctrl->adaptive_latency, ctrl->max_rx_interval);*/
if (ctrl->low_latency) {
ctrl->max_rx_interval -= 10;//ctrl->initial_latency;
} else {
ctrl->max_rx_interval = ctrl->initial_latency;
}
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
}
#endif
}
void *a2dp_stream_control_plan_select(void *stream, int low_latency, u32 codec_type, u8 plan)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)zalloc(sizeof(struct a2dp_stream_control));
if (!ctrl) {
return NULL;
}
switch (plan) {
case A2DP_STREAM_JL_DONGLE_CONTROL:
if (CONFIG_DONGLE_SPEAK_ENABLE) {
ctrl->initial_latency = CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
ctrl->adaptive_latency = ctrl->initial_latency;
ctrl->max_rx_interval = ctrl->initial_latency;
ctrl->adaptive_max_latency = low_latency ? (ctrl->initial_latency + MAX_DELAY_INCREMENT) : CONFIG_A2DP_ADAPTIVE_MAX_LATENCY;
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
ctrl->jl_dongle = 1;
}
break;
default:
if (low_latency) {
ctrl->initial_latency = codec_type == A2DP_CODEC_MPEG24 ? CONFIG_A2DP_DELAY_TIME_AAC_LO : CONFIG_A2DP_DELAY_TIME_SBC_LO;
} else {
ctrl->initial_latency = codec_type == A2DP_CODEC_MPEG24 ? CONFIG_A2DP_DELAY_TIME_AAC : CONFIG_A2DP_DELAY_TIME_SBC;
}
ctrl->adaptive_max_latency = low_latency ? (ctrl->initial_latency + MAX_DELAY_INCREMENT) : CONFIG_A2DP_ADAPTIVE_MAX_LATENCY;
ctrl->adaptive_latency = ctrl->initial_latency;
ctrl->max_rx_interval = ctrl->initial_latency;
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
break;
}
ctrl->low_latency = low_latency;
ctrl->first_in = 1;
ctrl->codec_type = codec_type;
ctrl->plan = plan;
ctrl->stream = stream;
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
return ctrl;
}
static void a2dp_stream_underrun_signal(void *arg)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)arg;
local_irq_disable();
if (ctrl->underrun_callback) {
ctrl->underrun_callback(ctrl->underrun_signal);
}
ctrl->timer = 0;
local_irq_enable();
}
static int a2dp_audio_is_underrun(struct a2dp_stream_control *ctrl)
{
int underrun_time = ctrl->low_latency ? 2 : 30;
if (ctrl->next_timestamp) {
u32 reference_clock = bt_audio_reference_clock_time(0);
if (reference_clock == (u32) - 1) {
return true;
}
u32 reference_time = reference_clock * 625 * TIMESTAMP_US_DENOMINATOR;
int distance_time = ctrl->next_timestamp - reference_time;
if (distance_time > 67108863L || distance_time < -67108863L) {
if (ctrl->next_timestamp > reference_time) {
distance_time = ctrl->next_timestamp - 0xffffffff - reference_time;
} else {
distance_time = 0xffffffff - reference_time + ctrl->next_timestamp;
}
}
distance_time = distance_time / 1000 / TIMESTAMP_US_DENOMINATOR;
/*printf("distance_time %d %u %u\n", distance_time, ctrl->next_timestamp, reference_time);*/
if (distance_time < underrun_time) {
return true;
}
local_irq_disable();
if (ctrl->timer) {
sys_hi_timeout_del(ctrl->timer);
ctrl->timer = 0;
}
ctrl->timer = sys_hi_timeout_add(ctrl, a2dp_stream_underrun_signal, distance_time - underrun_time);
local_irq_enable();
}
return 0;
}
static int a2dp_stream_underrun_handler(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame)
{
if (!a2dp_audio_is_underrun(ctrl)) {
/*putchar('x');*/
return 0;
}
putchar('X');
a2dp_stream_underrun_adaptive_handler(ctrl);
if (ctrl->stream_error != A2DP_STREAM_UNDERRUN) {
if (!ctrl->stream_error) {
}
ctrl->stream_error = A2DP_STREAM_UNDERRUN;
}
memcpy(frame, &ctrl->frame, sizeof(ctrl->frame));
ctrl->repair_frames++;
return ctrl->frame_len;
}
static void a2dp_stream_control_free_frames(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame)
{
if (frame && frame->packet) {
a2dp_media_free_packet(ctrl->stream, frame->packet);
if ((void *)frame->packet == (void *)ctrl->frame.packet) {
ctrl->frame.packet = NULL;
}
}
if (ctrl->frame.packet) {
a2dp_media_free_packet(ctrl->stream, ctrl->frame.packet);
ctrl->frame.packet = NULL;
}
ctrl->frame_len = 0;
}
static int a2dp_stream_overrun_handler(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int *len)
{
while (1) {
if (1) {//!ctrl->low_latency) {
int msecs = a2dp_media_get_remain_play_time(ctrl->stream, 1);
if (msecs < ctrl->adaptive_latency) {
/*printf("adaptive latency %d, msecs %d\n", ctrl->adaptive_latency, msecs);*/
break;
}
}
int rlen = a2dp_media_try_get_packet(ctrl->stream, frame);
if (rlen <= 0) {
break;
}
a2dp_stream_control_free_frames(ctrl, NULL);
memcpy(&ctrl->frame, frame, sizeof(ctrl->frame));
ctrl->frame_len = rlen;
*len = rlen;
putchar('n');
return 1;
}
memcpy(frame, &ctrl->frame, sizeof(ctrl->frame));
*len = ctrl->frame_len;
putchar('o');
return 0;
}
static int a2dp_stream_missed_handler(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int *len)
{
memcpy(frame, &ctrl->frame, sizeof(ctrl->frame));
*len = ctrl->frame_len;
if (--ctrl->missed_num == 0) {
return 1;
}
return 0;
}
static int a2dp_stream_error_filter(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int len)
{
int err = 0;
int header_len = a2dp_media_get_rtp_header_len(ctrl->codec_type, frame->packet, len);
if (header_len >= len) {
printf("##A2DP header error : %d\n", header_len);
a2dp_stream_control_free_frames(ctrl, frame);
return -EFAULT;
}
u16 seqn = RB16(frame->packet + 2);
if (ctrl->first_in) {
ctrl->first_in = 0;
goto __exit;
}
if (seqn != ctrl->seqn) {
if (ctrl->stream_error == A2DP_STREAM_UNDERRUN) {
int missed_frames = (u16)(seqn - ctrl->seqn) - 1;
if (missed_frames > ctrl->repair_frames) {
ctrl->stream_error = A2DP_STREAM_MISSED;
ctrl->missed_num = 2;//missed_frames - ctrl->repair_frames + 1;
/*printf("case 0 : %d, %d\n", missed_frames, ctrl->repair_frames);*/
err = -EAGAIN;
} else if (missed_frames < ctrl->repair_frames) {
ctrl->stream_error = A2DP_STREAM_OVERRUN;
ctrl->overrun_seqn = seqn + ctrl->repair_frames - missed_frames;
/*printf("case 1 : %d, %d, seqn : %d, %d\n", missed_frames, ctrl->repair_frames, seqn, ctrl->overrun_seqn);*/
err = -EAGAIN;
}
} else if (!ctrl->stream_error && (u16)(seqn - ctrl->seqn) > 1) {
err = -EAGAIN;
if ((u16)(seqn - ctrl->seqn) > 32768) {
a2dp_stream_control_free_frames(ctrl, frame);
return err;
}
ctrl->stream_error = A2DP_STREAM_MISSED;
ctrl->missed_num = 2;//(u16)(seqn - ctrl->seqn);
/*printf("case 2 : %d, %d, %d\n", seqn, ctrl->seqn, ctrl->missed_num); */
}
ctrl->repair_frames = 0;
}
__exit:
ctrl->seqn = seqn;
memcpy(&ctrl->frame, frame, sizeof(ctrl->frame));
ctrl->frame_len = len;
return err;
}
static int a2dp_get_frame_and_check_errors(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int *len)
{
int rlen = 0;
int err = 0;
u8 new_packet = 0;
try_again:
switch (ctrl->stream_error) {
case A2DP_STREAM_OVERRUN:
new_packet = a2dp_stream_overrun_handler(ctrl, frame, len);
break;
case A2DP_STREAM_MISSED:
new_packet = a2dp_stream_missed_handler(ctrl, frame, len);
if (frame->packet) {
break;
}
//注意:这里不break是因为很有可能由于位流的错误导致补包无法再正常补上
default:
rlen = a2dp_media_try_get_packet(ctrl->stream, frame);
if (rlen <= 0) {
rlen = a2dp_stream_underrun_handler(ctrl, frame);
} else {
if (ctrl->frame.packet) {
a2dp_media_free_packet(ctrl->stream, ctrl->frame.packet);
ctrl->frame.packet = NULL;
}
ctrl->frame_len = 0;
new_packet = 1;
}
*len = rlen;
break;
}
if (*len <= 0) {
return 0;
}
err = a2dp_stream_error_filter(ctrl, frame, *len);
if (err) {
if (-err == EAGAIN) {
goto try_again;
}
*len = 0;
return 0;
}
a2dp_stream_adaptive_detect_handler(ctrl, new_packet, frame->clkn);
if (ctrl->stream_error) {
if (new_packet) {
ctrl->stream_error = 0;
return FRAME_FLAG_RESET_TIMESTAMP_BIT;
}
return FRAME_FLAG_FILL_PACKET;
}
return 0;
}
int a2dp_stream_control_pull_frame(void *_ctrl, struct a2dp_media_frame *frame, int *len)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
if (!ctrl) {
*len = 0;
return 0;
}
switch (ctrl->plan) {
default:
return a2dp_get_frame_and_check_errors(ctrl, frame, len);
}
return 0;
}
void a2dp_stream_control_free_frame(void *_ctrl, struct a2dp_media_frame *frame)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
switch (ctrl->plan) {
default:
if (ctrl->frame.packet == frame->packet) {
ctrl->frame_free = 1;
}
}
}
void a2dp_stream_control_set_underrun_callback(void *_ctrl, void *priv, void (*callback)(void *priv))
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
ctrl->underrun_signal = priv;
ctrl->underrun_callback = callback;
}
int a2dp_stream_control_delay_time(void *_ctrl)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
if (ctrl) {
return ctrl->adaptive_latency;
}
return CONFIG_A2DP_DELAY_TIME_AAC;
}
void a2dp_stream_control_free(void *_ctrl)
{
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
if (!ctrl) {
return;
}
a2dp_stream_control_free_frames(ctrl, NULL);
local_irq_disable();
if (ctrl->timer) {
sys_hi_timeout_del(ctrl->timer);
ctrl->timer = 0;
}
local_irq_enable();
free(ctrl);
}