首页 技术 正文
技术 2022年11月15日
0 收藏 388 点赞 4,120 浏览 25020 个字

之前分析了a2dp profile 的初始化的流程,这篇文章分析一下,音频流在bluedroid中的处理流程。

上层的音频接口是调用a2dp hal 里面的接口来进行命令以及数据的发送的。

关于控制通道的初始化以及建立的过程,这里就不分析了,我们主要看数据的流向和处理。我们从控制通道的最后一个命令start 开始分析流程。

我们直接看a2dp hal 中out_write的实现:

static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
size_t bytes)
{
struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
int sent;
...
if (out->common.state == AUDIO_A2DP_STATE_SUSPENDED)
{
DEBUG("stream suspended");
pthread_mutex_unlock(&out->common.lock);
return -;
} /* only allow autostarting if we are in stopped or standby */
if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) ||
(out->common.state == AUDIO_A2DP_STATE_STANDBY))
{
if (start_audio_datapath(&out->common) < )//新建audio patch
{
/* emulate time this write represents to avoid very fast write
failures during transition periods or remote suspend */ int us_delay = calc_audiotime(out->common.cfg, bytes); DEBUG("emulate a2dp write delay (%d us)", us_delay); usleep(us_delay);
pthread_mutex_unlock(&out->common.lock);
return -;
}
}
else if (out->common.state != AUDIO_A2DP_STATE_STARTED)
{
ERROR("stream not in stopped or standby");
pthread_mutex_unlock(&out->common.lock);
return -;
} pthread_mutex_unlock(&out->common.lock);
sent = skt_write(out->common.audio_fd, buffer, bytes);//发送数据到audio patch if (sent == -)
{
/*错误处理*/
}
return sent;
}

当a2dp 刚连接的时候,这边的out->common.state 还是standby 状态,那么首先要进行a2dp data patch的建立:

static int start_audio_datapath(struct a2dp_stream_common *common)
{
... int oldstate = common->state;
common->state = AUDIO_A2DP_STATE_STARTING;//设置新的状态 int a2dp_status = a2dp_command(common, A2DP_CTRL_CMD_START);//向socket里面写数据,在bluedroid:btif_media_task.c里面的btif_recv_ctrl_data将处理该指令
... /* connect socket if not yet connected */
if (common->audio_fd == AUDIO_SKT_DISCONNECTED)
{
common->audio_fd = skt_connect(A2DP_DATA_PATH, common->buffer_sz);//之前已经在uipc_open里面先新建了socket的服务器端,现在可以连接
...
common->state = AUDIO_A2DP_STATE_STARTED;
} return ;
}

这里主要做了两件事:

  1. a2dp_command(common, A2DP_CTRL_CMD_START)
  2. skt_connect(A2DP_DATA_PATH, common->buffer_sz);

我们分别看看,前者就是下发了 A2DP_CTRL_CMD_START这个命令,对应hci log 中的:

a2dp播放流程源码分析

后者的作用是建立socket连接,供后续的数据的传输。

我们先看 第一个流程:

static int a2dp_command(struct a2dp_stream_common *common, char cmd)
{
char ack; DEBUG("A2DP COMMAND %s", dump_a2dp_ctrl_event(cmd)); /* send command */
if (send(common->ctrl_fd, &cmd, , MSG_NOSIGNAL) == -)//发送到这个socket,btif_media_task这个线程去处理
{
...
} /* wait for ack byte */
if (a2dp_ctrl_receive(common, &ack, ) < )//接收返回的消息
return -;
...
return ;
}

就是往之前建立好的  控制通道里面 写数据进去。该command 由谁来处理呢?答案 是btif_a2dp_ctrl_cb ,我们看看在btif_media_thread_init 做的事情:

static void btif_media_thread_init(UNUSED_ATTR void *context) {
memset(&btif_media_cb, , sizeof(btif_media_cb));
UIPC_Init(NULL);#if (BTA_AV_INCLUDED == TRUE)
UIPC_Open(UIPC_CH_ID_AV_CTRL , btif_a2dp_ctrl_cb);//注册了 btif_a2dp_ctrl_cb为控制通道的处理函数
#endif raise_priority_a2dp(TASK_HIGH_MEDIA);
media_task_running = MEDIA_TASK_STATE_ON;
}

接下来我们看看  btif_a2dp_ctrl_cb 对于A2DP_CTRL_CMD_START的处理:

在UIPC的机制中,有数据来就会携带UIPC_RX_DATA_READY_EVT:

static void btif_a2dp_ctrl_cb(tUIPC_CH_ID ch_id, tUIPC_EVENT event)
{
UNUSED(ch_id); switch(event)
{
case UIPC_OPEN_EVT:
...
break; case UIPC_CLOSE_EVT:
...
break; case UIPC_RX_DATA_READY_EVT:
btif_recv_ctrl_data();
break; default :
APPL_TRACE_ERROR("### A2DP-CTRL-CHANNEL EVENT %d NOT HANDLED ###", event);
break;
}
}

确定是控制数据之后,路由到btif_recv_ctrl_data来处理:

static void btif_recv_ctrl_data(void)
{
UINT8 cmd = ;
int n;
n = UIPC_Read(UIPC_CH_ID_AV_CTRL, NULL, &cmd, );//先把数据读出来
...
btif_media_cb.a2dp_cmd_pending = cmd; switch(cmd)
{
case A2DP_CTRL_CMD_CHECK_READY:
...
break; case A2DP_CTRL_CMD_START:
/* Don't sent START request to stack while we are in call.
Some headsets like the Sony MW600, don't allow AVDTP START
in call and respond BAD_STATE. */
if (!btif_hf_is_call_idle())//如果正在打电话,不发起连接
{
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_INCALL_FAILURE);
break;
} if (btif_av_stream_ready() == TRUE)//如果已经准备好就 就进行socket 以及一些audio的处理
{
/* setup audio data channel listener */
UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb); /* post start event and wait for audio path to open */
btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, );#if (BTA_AV_SINK_INCLUDED == TRUE)
if (btif_media_cb.peer_sep == AVDT_TSEP_SRC)
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
#endif
}
else if (btif_av_stream_started_ready())
{
/* already started, setup audio data channel listener
and ack back immediately */
UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb); a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
}
else
{
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_FAILURE);
break;
}
break; case A2DP_CTRL_CMD_STOP:
...
break; case A2DP_CTRL_CMD_SUSPEND:
/* local suspend */
if (btif_av_stream_started_ready())
{
btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, NULL, );
}
else
{
/* if we are not in started state, just ack back ok and let
audioflinger close the channel. This can happen if we are
remotely suspended, clear REMOTE SUSPEND Flag */
btif_av_clear_remote_suspend_flag();
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
}
break; case A2DP_CTRL_GET_AUDIO_CONFIG:
{
...
break;
} default:
APPL_TRACE_ERROR("UNSUPPORTED CMD (%d)", cmd);
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_FAILURE);
break;
}
APPL_TRACE_DEBUG("a2dp-ctrl-cmd : %s DONE", dump_a2dp_ctrl_event(cmd));
}

当前是A2DP_CTRL_CMD_START 这个命令:

如果当前正在打电话,那么就不会发起连接。接下来就做两件事:

  1. 建立起socket的服务端,等待连接。
  2. 发送BTIF_AV_START_STREAM_REQ_EVT这个事件到状态机等待音频通道打开。

我们先看第一个:

  UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb);

BOOLEAN UIPC_Open(tUIPC_CH_ID ch_id, tUIPC_RCV_CBACK *p_cback)
{
...
switch(ch_id)
{
case UIPC_CH_ID_AV_AUDIO:
            uipc_setup_server_locked(ch_id, A2DP_DATA_PATH, p_cback);//先把socket的服务器端建立起来
            break;
...

主要就是建立 socket的服务器端:并把服务器的socket fd 放入到uipc_main.ch[UIPC_CH_ID_AV_AUDIO].srvfd

    fd = create_server_socket(name);    uipc_main.ch[ch_id].srvfd = fd;
uipc_main.ch[ch_id].cback = cback;
uipc_main.ch[ch_id].read_poll_tmo_ms = DEFAULT_READ_POLL_TMO_MS;

下面我们 看看

btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);

/* used to pass events to AV statemachine from other tasks */
void btif_dispatch_sm_event(btif_av_sm_event_t event, void *p_data, int len)
{
/* Switch to BTIF context */
btif_transfer_context(btif_av_handle_event, event,
(char*)p_data, len, NULL);
}

把事件pass 到AV statemachine,

static void btif_av_handle_event(UINT16 event, char* p_param)
{
btif_sm_dispatch(btif_av_cb.sm_handle, event, (void*)p_param);
btif_av_event_free_data(event, p_param);
}

我们看一下状态机的轮转:

static const btif_sm_handler_t btif_av_state_handlers[] =
{
btif_av_state_idle_handler,
btif_av_state_opening_handler,
btif_av_state_opened_handler,
btif_av_state_started_handler,
btif_av_state_closing_handler
};

当前的状态是 opend 的状态,处理的handle 是btif_av_state_opened_handler:

看看其对于事件的处理:

 case BTIF_AV_START_STREAM_REQ_EVT:
if (btif_av_cb.peer_sep != AVDT_TSEP_SRC)
btif_a2dp_setup_codec();
BTIF_TRACE_EVENT("BTIF_AV_START_STREAM_REQ_EVT begin BTA_AvStart libs_liu");
BTA_AvStart();
BTIF_TRACE_EVENT("BTIF_AV_START_STREAM_REQ_EVT end BTA_AvStart libs_liu");
btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START;
break;

首先保存并设置了codec的参数,然后BTA_AvStart(发送BTA_AV_API_START_EVT 消息),并设置btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START,标记为pending start 状态。

这里主要分析一下BTA_AvStart的流程:

void BTA_AvStart(void)
{
BT_HDR *p_buf; if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
{
p_buf->event = BTA_AV_API_START_EVT;
bta_sys_sendmsg(p_buf);
}
}

发送了BTA_AV_API_START_EVT(0x1238),他是由bta_av_nsm_act来处理:

    bta_av_api_to_ssm,      /* BTA_AV_API_START_EVT */

我们发现执行到steam state machine里面了:

/*******************************************************************************
**
** Function bta_av_api_to_ssm
**
** Description forward the API request to stream state machine
**
**
** Returns void
**
*******************************************************************************/
static void bta_av_api_to_ssm(tBTA_AV_DATA *p_data)
{
int xx;
UINT16 event = p_data->hdr.event - BTA_AV_FIRST_A2S_API_EVT + BTA_AV_FIRST_A2S_SSM_EVT; for(xx=; xx<BTA_AV_NUM_STRS; xx++)/* maximum number of streams created: 1 for audio, 1 for video */
{
bta_av_ssm_execute(bta_av_cb.p_scb[xx], event, p_data);//肯定是有 一个没有注册 因为 是video
}
}

这里就进入到stream statemachine的状态机了,

AV Sevent(0x41)=0x120b(AP_START) state=(OPEN)

当前的stream 的状态机是 open 状态。

/* AP_START_EVT */          {BTA_AV_DO_START,       BTA_AV_SIGNORE,        BTA_AV_OPEN_SST },

执行的action 是BTA_AV_DO_START,下一个状态依然是open状态,我们看一下这个函数的实现:

/*******************************************************************************
**
** Function bta_av_do_start
**
** Description Start stream.
**
** Returns void
**
*******************************************************************************/
void bta_av_do_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
UINT8 policy = HCI_ENABLE_SNIFF_MODE;
UINT8 cur_role;
... if ((p_scb->started == FALSE) && ((p_scb->role & BTA_AV_ROLE_START_INT) == ))
{
p_scb->role |= BTA_AV_ROLE_START_INT;
bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr); AVDT_StartReq(&p_scb->avdt_handle, );//avdt start
}
else if (p_scb->started)
{
...
}
}

上面avdt start的过程就对应于hci log 中的a2dp播放流程源码分析

到这里上面的start_audio_datapath中的第一点a2dp_command(common, A2DP_CTRL_CMD_START)  已经基本分析完了,接下来我们看看skt_connect(A2DP_DATA_PATH, common->buffer_sz);的流程,当然这个流程就简单很多,他是做了一个socket的连接。连接的patch 是/data/misc/bluedroid/.a2dp_data ,

common->audio_fd = skt_connect(A2DP_DATA_PATH, common->buffer_sz);

那后续 audio 传下来的数据只要写到common->audio_fd就可以了。

那关于  a2dp的数据通道的打开,这里就分析结束了。

接下来我们看看音频数据流:

当上面的socket连接上之后,第一件事就是 发送UIPC_OPEN_EVT和UIPC_RX_DATA_READY_EVT事件:

static int uipc_check_fd_locked(tUIPC_CH_ID ch_id)
{
if (SAFE_FD_ISSET(uipc_main.ch[ch_id].srvfd, &uipc_main.read_set))
{
BTIF_TRACE_EVENT("INCOMING CONNECTION ON CH %d", ch_id);
uipc_main.ch[ch_id].fd = accept_server_socket(uipc_main.ch[ch_id].srvfd);
...
if (uipc_main.ch[ch_id].cback)
uipc_main.ch[ch_id].cback(ch_id, UIPC_OPEN_EVT);//发送通道打开的通知
} if (SAFE_FD_ISSET(uipc_main.ch[ch_id].fd, &uipc_main.read_set))
{
BTIF_TRACE_EVENT("INCOMING DATA ON CH %d", ch_id); if (uipc_main.ch[ch_id].cback)
uipc_main.ch[ch_id].cback(ch_id, UIPC_RX_DATA_READY_EVT);//有数据过来
}
return ;
}

我们先看看UIPC_OPEN_EVT的处理流程:

首先这里的callback 就是btif_a2dp_data_cb,

static void btif_a2dp_data_cb(tUIPC_CH_ID ch_id, tUIPC_EVENT event)
{
switch(event)
{
case UIPC_OPEN_EVT: /* read directly from media task from here on (keep callback for
connection events */
UIPC_Ioctl(UIPC_CH_ID_AV_AUDIO, UIPC_REG_REMOVE_ACTIVE_READSET, NULL);//将uipc_main.ch[ch_id].fd移出uipc_main.active_set
UIPC_Ioctl(UIPC_CH_ID_AV_AUDIO, UIPC_SET_READ_POLL_TMO,
(void *)A2DP_DATA_READ_POLL_MS);//设置uipc_main.ch[ch_id].read_poll_tmo_ms = 10,这里是uipc poll的超时时间 if (btif_media_cb.peer_sep == AVDT_TSEP_SNK) { /* Start the media task to encode SBC */
btif_media_task_start_aa_req();//给media task 发送BTIF_MEDIA_START_AA_TX /* make sure we update any changed sbc encoder params */
btif_a2dp_encoder_update();//更新sbc 参数相关
}
btif_media_cb.data_channel_open = TRUE; /* ack back when media task is fully started */
break; case UIPC_CLOSE_EVT:
a2dp_cmd_acknowledge(A2DP_CTRL_ACK_SUCCESS);
btif_audiopath_detached();
btif_media_cb.data_channel_open = FALSE;
break; default :
APPL_TRACE_ERROR("### A2DP-DATA EVENT %d NOT HANDLED ###", event);
break;
}
}

我们发现上面的函数竟然没有UIPC_RX_DATA_READY_EVT的处理流程,为什么呢?因为在UIPC_OPEN_EVT的处理中已经把这uipc_main.ch[ch_id].fd移出uipc_main.active_set

UIPC_OPEN_EVT的处理中接着又设置了定时器,等用到的时候我们再分析。下面我们看看其给media task 发送BTIF_MEDIA_START_AA_TX的流程:

BOOLEAN btif_media_task_start_aa_req(void)
{
BT_HDR *p_buf;
if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR))))
{
APPL_TRACE_EVENT("GKI failed");
return FALSE;
} p_buf->event = BTIF_MEDIA_START_AA_TX; fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);//放到队列,media task 会自动处理
return TRUE;
}

在btif_a2dp_start_media_task 中,我们已经绑定了media task 线程和队列以及消息处理函数:

    fixed_queue_register_dequeue(btif_media_cmd_msg_queue,
thread_get_reactor(worker_thread),
btif_media_thread_handle_cmd,
NULL);

看看其处理:

static void btif_media_thread_handle_cmd(fixed_queue_t *queue, UNUSED_ATTR void *context)
{
BT_HDR *p_msg = (BT_HDR *)fixed_queue_dequeue(queue);
LOG_VERBOSE("btif_media_thread_handle_cmd : %d %s", p_msg->event,
dump_media_event(p_msg->event)); switch (p_msg->event)
{
#if (BTA_AV_INCLUDED == TRUE)
case BTIF_MEDIA_START_AA_TX:
btif_media_task_aa_start_tx();
break;

继续看:

/*******************************************************************************
**
** Function btif_media_task_aa_start_tx
**
** Description Start media task encoding
**
** Returns void
**
*******************************************************************************/
static void btif_media_task_aa_start_tx(void)
{ /* Use a timer to poll the UIPC, get rid of the UIPC call back */ btif_media_cb.is_tx_timer = TRUE;
last_frame_us = ; /* Reset the media feeding state */
btif_media_task_feeding_state_reset(); btif_media_cb.media_alarm = alarm_new(); alarm_set_periodic(btif_media_cb.media_alarm, BTIF_MEDIA_TIME_TICK, btif_media_task_alarm_cb, NULL);}

这边设置了一个定时器,#define BTIF_MEDIA_TIME_TICK                     (20 * BTIF_MEDIA_NUM_TICK)

每20ms 去读一次数据,而不是通过UIPC的回调函数来操作。

我们继续看看 定时器的函数实现:

static void btif_media_task_alarm_cb(UNUSED_ATTR void *context) {
thread_post(worker_thread, btif_media_task_aa_handle_timer, NULL);//media task 线程中执行
}
static void btif_media_task_aa_handle_timer(UNUSED_ATTR void *context)
{
log_tstamps_us("media task tx timer");#if (BTA_AV_INCLUDED == TRUE)
if(btif_media_cb.is_tx_timer == TRUE)//前面已经标记这个位
{
btif_media_send_aa_frame();//函数名send,但是应该包含先包含读audio数据的操作
}
else
{
APPL_TRACE_ERROR("ERROR Media task Scheduled after Suspend");
}
#endif
}

下面我们分析一下btif_media_send_aa_frame:

/*******************************************************************************
**
** Function btif_media_send_aa_frame
**
** Description
**
** Returns void
**
*******************************************************************************/
static void btif_media_send_aa_frame(void)
{
UINT8 nb_frame_2_send; /* get the number of frame to send */
nb_frame_2_send = btif_get_num_aa_frame();//计算出应该获取的frame的数量,是根据时间间隔来计算的 if (nb_frame_2_send != )
{
/* format and Q buffer to send */
btif_media_aa_prep_2_send(nb_frame_2_send);//读取数据,并放置到队列里面
} /* send it */
LOG_VERBOSE("btif_media_send_aa_frame : send %d frames", nb_frame_2_send);
bta_av_ci_src_data_ready(BTA_AV_CHNL_AUDIO);//发送数据
}

这里重点分析一下btif_media_aa_prep_2_send和bta_av_ci_src_data_ready

btif_media_aa_prep_2_send

/*******************************************************************************
**
** Function btif_media_aa_prep_2_send
**
** Description
**
** Returns void
**
*******************************************************************************/static void btif_media_aa_prep_2_send(UINT8 nb_frame)
{
// Check for TX queue overflow,队列btif_media_cb.TxAaQ数据太多就会丢弃一些
while (GKI_queue_length(&btif_media_cb.TxAaQ) > (MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ - nb_frame))
GKI_freebuf(GKI_dequeue(&(btif_media_cb.TxAaQ))); // Transcode frame switch (btif_media_cb.TxTranscoding)
{
case BTIF_MEDIA_TRSCD_PCM_2_SBC:
btif_media_aa_prep_sbc_2_send(nb_frame);
break; default:
...
}
}

我们继续看btif_media_aa_prep_sbc_2_send的实现:

/*******************************************************************************
**
** Function btif_media_aa_prep_sbc_2_send
**
** Description
**
** Returns void
**
*******************************************************************************/
static void btif_media_aa_prep_sbc_2_send(UINT8 nb_frame)
{
BT_HDR * p_buf;
UINT16 blocm_x_subband = btif_media_cb.encoder.s16NumOfSubBands *
btif_media_cb.encoder.s16NumOfBlocks; while (nb_frame)
{
if (NULL == (p_buf = GKI_getpoolbuf(BTIF_MEDIA_AA_POOL_ID)))
{
...
return;
} /* Init buffer */
p_buf->offset = BTIF_MEDIA_AA_SBC_OFFSET;
p_buf->len = ;
p_buf->layer_specific = ; do
{
/* Write @ of allocated buffer in encoder.pu8Packet */
btif_media_cb.encoder.pu8Packet = (UINT8 *) (p_buf + ) + p_buf->offset + p_buf->len;
/* Fill allocated buffer with 0 */
memset(btif_media_cb.encoder.as16PcmBuffer, , blocm_x_subband
* btif_media_cb.encoder.s16NumOfChannels); /* Read PCM data and upsample them if needed */
if (btif_media_aa_read_feeding(UIPC_CH_ID_AV_AUDIO))//读audio的数据
{
/* SBC encode and descramble frame */
SBC_Encoder(&(btif_media_cb.encoder));//sbc 编码相关
A2D_SbcChkFrInit(btif_media_cb.encoder.pu8Packet);
A2D_SbcDescramble(btif_media_cb.encoder.pu8Packet, btif_media_cb.encoder.u16PacketLength);
/* Update SBC frame length */
p_buf->len += btif_media_cb.encoder.u16PacketLength;
nb_frame--;//frame numb --
p_buf->layer_specific++;
}
else//没有读到数据
{
APPL_TRACE_WARNING("btif_media_aa_prep_sbc_2_send underflow %d, %d",
nb_frame, btif_media_cb.media_feeding_state.pcm.aa_feed_residue);
/*需要把应该发送的数据量+回来*/
btif_media_cb.media_feeding_state.pcm.counter += nb_frame *
btif_media_cb.encoder.s16NumOfSubBands *
btif_media_cb.encoder.s16NumOfBlocks *
btif_media_cb.media_feeding.cfg.pcm.num_channel *
btif_media_cb.media_feeding.cfg.pcm.bit_per_sample / ;
/* no more pcm to read */
nb_frame = ;
...
} } while (((p_buf->len + btif_media_cb.encoder.u16PacketLength) < btif_media_cb.TxAaMtuSize)
&& (p_buf->layer_specific < 0x0F) && nb_frame); if(p_buf->len)
{
/* timestamp of the media packet header represent the TS of the first SBC frame
i.e the timestamp before including this frame */
*((UINT32 *) (p_buf + )) = btif_media_cb.timestamp; btif_media_cb.timestamp += p_buf->layer_specific * blocm_x_subband;
...
/* Enqueue the encoded SBC frame in AA Tx Queue */
GKI_enqueue(&(btif_media_cb.TxAaQ), p_buf);//加入到队列
}
else
{
GKI_freebuf(p_buf);
}
}
}

我们现在分析一下btif_media_aa_read_feeding(UIPC_CH_ID_AV_AUDIO) 的流程:

/*******************************************************************************
**
** Function btif_media_aa_read_feeding
**
** Description
**
** Returns void
**
*******************************************************************************/BOOLEAN btif_media_aa_read_feeding(tUIPC_CH_ID channel_id)
{
UINT16 event;
UINT16 blocm_x_subband = btif_media_cb.encoder.s16NumOfSubBands * \
btif_media_cb.encoder.s16NumOfBlocks;
UINT32 read_size;
UINT16 sbc_sampling = ;
UINT32 src_samples;
UINT16 bytes_needed = blocm_x_subband * btif_media_cb.encoder.s16NumOfChannels * \
btif_media_cb.media_feeding.cfg.pcm.bit_per_sample / ;
static UINT16 up_sampled_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS
* SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS * ];
static UINT16 read_buffer[SBC_MAX_NUM_FRAME * SBC_MAX_NUM_OF_BLOCKS
* SBC_MAX_NUM_OF_CHANNELS * SBC_MAX_NUM_OF_SUBBANDS];
UINT32 src_size_used;
UINT32 dst_size_used;
BOOLEAN fract_needed;
INT32 fract_max;
INT32 fract_threshold;
UINT32 nb_byte_read; /* Get the SBC sampling rate */
switch (btif_media_cb.encoder.s16SamplingFreq)
{
case SBC_sf48000:
sbc_sampling = ;
break;
case SBC_sf44100:
sbc_sampling = ;
break;
case SBC_sf32000:
sbc_sampling = ;
break;
case SBC_sf16000:
sbc_sampling = ;
break;
} if (sbc_sampling == btif_media_cb.media_feeding.cfg.pcm.sampling_freq) {//btif_a2dp_setup_codec中设置media_feeding.cfg.pcm.sampling_freq = 44.1
read_size = bytes_needed - btif_media_cb.media_feeding_state.pcm.aa_feed_residue;
nb_byte_read = UIPC_Read(channel_id, &event,
((UINT8 *)btif_media_cb.encoder.as16PcmBuffer) +
btif_media_cb.media_feeding_state.pcm.aa_feed_residue,
read_size);
if (nb_byte_read == read_size) {
btif_media_cb.media_feeding_state.pcm.aa_feed_residue = ;
return TRUE;
} else {//没有读到预期的数据,打印underflow
APPL_TRACE_WARNING("### UNDERFLOW :: ONLY READ %d BYTES OUT OF %d ###",
nb_byte_read, read_size);
btif_media_cb.media_feeding_state.pcm.aa_feed_residue += nb_byte_read;
return FALSE;
}
} ...

btif_media_cb.encoder.s16SamplingFreq这里注意到是用btif_media_cb.encoder.s16SamplingFreq 来调节sbc_sampling 的大小的,那么btif_media_cb.encoder.s16SamplingFreq又是哪里设置的呢?

我们先看看btif_media_cb.media_feeding.cfg.pcm.sampling_freq的设置:

void btif_a2dp_setup_codec(void)
{
tBTIF_AV_MEDIA_FEEDINGS media_feeding;
tBTIF_STATUS status; APPL_TRACE_EVENT("## A2DP SETUP CODEC ##"); GKI_disable(); /* for now hardcode 44.1 khz 16 bit stereo PCM format */
media_feeding.cfg.pcm.sampling_freq = ;//直接设置
media_feeding.cfg.pcm.bit_per_sample = ;

btif_media_cb.encoder.s16SamplingFreq的设置

/*******************************************************************************
**
** Function btif_media_task_pcm2sbc_init
**
** Description Init encoding task for PCM to SBC according to feeding
**
** Returns void
**
*******************************************************************************/
static void btif_media_task_pcm2sbc_init(tBTIF_MEDIA_INIT_AUDIO_FEEDING * p_feeding)
{
BOOLEAN reconfig_needed = FALSE; APPL_TRACE_DEBUG("PCM feeding:");
APPL_TRACE_DEBUG("sampling_freq:%d", p_feeding->feeding.cfg.pcm.sampling_freq);
APPL_TRACE_DEBUG("num_channel:%d", p_feeding->feeding.cfg.pcm.num_channel);
APPL_TRACE_DEBUG("bit_per_sample:%d", p_feeding->feeding.cfg.pcm.bit_per_sample); /* Check the PCM feeding sampling_freq */
switch (p_feeding->feeding.cfg.pcm.sampling_freq)//根据feeding.cfg.pcm.sampling_freq的值来设置btif_media_cb.encoder.s16SamplingFreq
{
case :
case :
case :
case :
case :
case :
/* For these sampling_freq the AV connection must be 48000 */
if (btif_media_cb.encoder.s16SamplingFreq != SBC_sf48000)
{
/* Reconfiguration needed at 48000 */
APPL_TRACE_DEBUG("SBC Reconfiguration needed at 48000");
btif_media_cb.encoder.s16SamplingFreq = SBC_sf48000;
reconfig_needed = TRUE;
}
break; case :
case :
case :
/* For these sampling_freq the AV connection must be 44100 */
if (btif_media_cb.encoder.s16SamplingFreq != SBC_sf44100)
{
/* Reconfiguration needed at 44100 */
APPL_TRACE_DEBUG("SBC Reconfiguration needed at 44100");
btif_media_cb.encoder.s16SamplingFreq = SBC_sf44100;
reconfig_needed = TRUE;
}
break;
default:
APPL_TRACE_DEBUG("Feeding PCM sampling_freq unsupported");
break;
}

关于采样率关系:根据feeding.cfg.pcm.sampling_freq的值来设置btif_media_cb.encoder.s16SamplingFreq,encoder的参数也就是sbc的参数。那么也就是说在协议栈中,feeding.cfg.pcm.sampling_freq的值是具体决定性的,其设置在btif_a2dp_setup_codec 中。

关于btif_media_aa_prep_2_send的分析就到这里,现在我们接着分析bta_av_ci_src_data_ready的流程:

bta_av_ci_src_data_ready

/*******************************************************************************
**
** Function bta_av_ci_src_data_ready
**
** Description This function sends an event to the AV indicating that
** the phone has audio stream data ready to send and AV
** should call bta_av_co_audio_src_data_path() or
** bta_av_co_video_src_data_path().
**
** Returns void
**
*******************************************************************************/
void bta_av_ci_src_data_ready(tBTA_AV_CHNL chnl)
{
BT_HDR *p_buf; if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
{
p_buf->layer_specific = chnl;
p_buf->event = BTA_AV_CI_SRC_DATA_READY_EVT;
bta_sys_sendmsg(p_buf);
}
}

执行的函数:

    bta_av_ci_data,         /* BTA_AV_CI_SRC_DATA_READY_EVT */
/*******************************************************************************
**
** Function bta_av_ci_data
**
** Description forward the BTA_AV_CI_SRC_DATA_READY_EVT to stream state machine
**
**
** Returns void
**
*******************************************************************************/
static void bta_av_ci_data(tBTA_AV_DATA *p_data)
{
tBTA_AV_SCB *p_scb;
int i;
UINT8 chnl = (UINT8)p_data->hdr.layer_specific; for( i=; i < BTA_AV_NUM_STRS; i++ )
{
p_scb = bta_av_cb.p_scb[i]; if(p_scb && p_scb->chnl == chnl)
{
bta_av_ssm_execute(p_scb, BTA_AV_SRC_DATA_READY_EVT, p_data);
}
}
}

这里bta_av_ssm_execute(p_scb, BTA_AV_SRC_DATA_READY_EVT, p_data); 看看其流程:

AV Sevent(0x41)=0x1211(SRC_DATA_READY) state=(OPEN)
/* SRC_DATA_READY_EVT */    {BTA_AV_DATA_PATH,      BTA_AV_SIGNORE,        BTA_AV_OPEN_SST },

执行的action是BTA_AV_DATA_PATH,具体函数如下:

/*******************************************************************************
**
** Function bta_av_data_path
**
** Description Handle stream data path.
**
** Returns void
**
*******************************************************************************/
void bta_av_data_path (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
BT_HDR *p_buf = NULL;
UINT32 data_len;
UINT32 timestamp;
BOOLEAN new_buf = FALSE;
UINT8 m_pt = 0x60 | p_scb->codec_type;
tAVDT_DATA_OPT_MASK opt;
UNUSED(p_data); //Always get the current number of bufs que'd up
p_scb->l2c_bufs = (UINT8)L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_GET); if (!list_is_empty(p_scb->a2d_list)) {
p_buf = (BT_HDR *)list_front(p_scb->a2d_list);
list_remove(p_scb->a2d_list, p_buf);
/* use q_info.a2d data, read the timestamp */
timestamp = *(UINT32 *)(p_buf + );
}
else
{
new_buf = TRUE;
/* a2d_list empty, call co_data, dup data to other channels */
p_buf = (BT_HDR *)p_scb->p_cos->data(p_scb->codec_type, &data_len,
&timestamp);//组件数据准备发送 if (p_buf)
{
/* use the offset area for the time stamp */
*(UINT32 *)(p_buf + ) = timestamp;
...
}
} if(p_buf)
{
if(p_scb->l2c_bufs < (BTA_AV_QUEUE_DATA_CHK_NUM))
{
/* there's a buffer, just queue it to L2CAP */
/* There's no need to increment it here, it is always read from L2CAP see above */
/* p_scb->l2c_bufs++; */
/*
APPL_TRACE_ERROR("qw: %d", p_scb->l2c_bufs);
*/ /* opt is a bit mask, it could have several options set */
opt = AVDT_DATA_OPT_NONE;
if (p_scb->no_rtp_hdr)
{
opt |= AVDT_DATA_OPT_NO_RTP;
} AVDT_WriteReqOpt(p_scb->avdt_handle, p_buf, timestamp, m_pt, opt);//真正发送
p_scb->cong = TRUE;
}
else
{
/* there's a buffer, but L2CAP does not seem to be moving data */
if(new_buf)
{
/* just got this buffer from co_data,
* put it in queue */
list_append(p_scb->a2d_list, p_buf);
}
else
{
/* just dequeue it from the a2d_list */
if (list_length(p_scb->a2d_list) < ) {
/* put it back to the queue */
list_prepend(p_scb->a2d_list, p_buf);
}
else
{
/* too many buffers in a2d_list, drop it. */
bta_av_co_audio_drop(p_scb->hndl);
GKI_freebuf(p_buf);
}
}
}
}
}

这里主要分析p_scb->p_cos->data(p_scb->codec_type, &data_len,&timestamp);的流程。

/* the call out functions for audio stream */
const tBTA_AV_CO_FUNCTS bta_av_a2d_cos =
{
bta_av_co_audio_init,
bta_av_co_audio_disc_res,
bta_av_co_audio_getconfig,
bta_av_co_audio_setconfig,
bta_av_co_audio_open,
bta_av_co_audio_close,
bta_av_co_audio_start,
bta_av_co_audio_stop,
bta_av_co_audio_src_data_path,
bta_av_co_audio_delay
};

执行的函数是

/*******************************************************************************
**
** Function bta_av_co_audio_src_data_path
**
** Description This function is called to manage data transfer from
** the audio codec to AVDTP.
**
** Returns Pointer to the GKI buffer to send, NULL if no buffer to send
**
*******************************************************************************/
void * bta_av_co_audio_src_data_path(tBTA_AV_CODEC codec_type, UINT32 *p_len,
UINT32 *p_timestamp)
{
BT_HDR *p_buf;
UNUSED(p_len); FUNC_TRACE(); p_buf = btif_media_aa_readbuf();// return GKI_dequeue(&(btif_media_cb.TxAaQ));
if (p_buf != NULL)
{
switch (codec_type)
{
case BTA_AV_CODEC_SBC:
/* In media packet SBC, the following information is available:
* p_buf->layer_specific : number of SBC frames in the packet
* p_buf->word[0] : timestamp
*/
/* Retrieve the timestamp information from the media packet */
*p_timestamp = *((UINT32 *) (p_buf + )); /* Set up packet header */
bta_av_sbc_bld_hdr(p_buf, p_buf->layer_specific);
break; default:
APPL_TRACE_ERROR("bta_av_co_audio_src_data_path Unsupported codec type (%d)", codec_type);
break;
}
}
return p_buf;
}

这里我们主要是明白最终数据是从btif_media_cb.TxAaQ 中取出来了,然后经过AVDT_WriteReqOpt 发送到设备。

做个简单总结,主要有如下的要点:

  1. 通过audio 的控制通道发送A2DP_CTRL_CMD_START 命令
  2. 打开数据通道: UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb);
  3. 通过socket 连接上数据通道:common->audio_fd = skt_connect(A2DP_DATA_PATH, common->buffer_sz);
  4. 平台audio 往socket:common->audio_fd 发送数据
  5. 设置定时器,每隔20ms 去读audio的数据alarm_set_periodic(btif_media_cb.media_alarm, BTIF_MEDIA_TIME_TICK, btif_media_task_alarm_cb, NULL),并发送到设备

那到这里a2dp的数据发送的过程就分析完了。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,024
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,514
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,362
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,143
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,776
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,854