初版
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user