endif
bdroid_CFLAGS += -Wall -Werror
+bdroid_CFLAGS += -DBTA_AVK_INCLUDED
include $(call all-subdir-makefiles)
0 /* AVDT_DELAY_REPORT_CFM_EVT */
};
+void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt);
static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
static void bta_av_stream1_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
#if BTA_AV_NUM_STRS > 2
,bta_av_stream5_cback
#endif
};
+/***********************************************
+**
+** Function bta_get_scb_handle
+**
+** Description gives the registered AVDT handle.by checking with sep_type.
+**
+**
+** Returns void
+***********************************************/
+UINT8 bta_av_get_scb_handle ( tBTA_AV_SCB *p_scb, UINT8 local_sep )
+{
+ UINT8 xx =0;
+ for (xx = 0; xx<BTA_AV_MAX_SEPS; xx++)
+ {
+ if ((p_scb->seps[xx].tsep == local_sep) &&
+ (p_scb->seps[xx].codec_type == p_scb->codec_type))
+ return (p_scb->seps[xx].av_handle);
+ }
+ APPL_TRACE_DEBUG0(" bta_av_get_scb_handle appropiate sep_type not found")
+ return 0; /* return invalid handle */
+}
+
+/***********************************************
+**
+** Function bta_av_get_scb_sep_type
+**
+** Description gives the sep type by cross-checking with AVDT handle
+**
+**
+** Returns void
+***********************************************/
+UINT8 bta_av_get_scb_sep_type ( tBTA_AV_SCB *p_scb, UINT8 tavdt_handle)
+{
+ UINT8 xx =0;
+ for (xx = 0; xx<BTA_AV_MAX_SEPS; xx++)
+ {
+ if (p_scb->seps[xx].av_handle == tavdt_handle)
+ return (p_scb->seps[xx].tsep);
+ }
+ APPL_TRACE_DEBUG0(" bta_av_get_scb_sep_type appropiate handle not found")
+ return 3; /* return invalid sep type */
+}
/*******************************************************************************
**
int i;
tAVDT_GETCAP_REQ *p_req;
BOOLEAN sent_cmd = FALSE;
+ UINT16 uuid_int = p_scb->uuid_int;
+ UINT8 sep_requested = 0;
+
+ if(uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
+ sep_requested = AVDT_TSEP_SNK;
+ else if(uuid_int == UUID_SERVCLASS_AUDIO_SINK)
+ sep_requested = AVDT_TSEP_SRC;
for (i = p_scb->sep_info_idx; i < p_scb->num_seps; i++)
{
/* steam not in use, is a sink, and is the right media type (audio/video) */
if ((p_scb->sep_info[i].in_use == FALSE) &&
- (p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) &&
+ (p_scb->sep_info[i].tsep == sep_requested) &&
(p_scb->sep_info[i].media_type == p_scb->media_type))
{
p_scb->sep_info_idx = i;
/*******************************************************************************
**
+** Function bta_av_stream_data_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt)
+{
+ int index = 0;
+ tBTA_AV_SCB *p_scb ;
+ APPL_TRACE_DEBUG3("bta_av_stream_data_cback avdt_handle: %d pkt_len=0x%x ofst = 0x%x", handle,p_pkt->len,p_pkt->offset);
+ APPL_TRACE_DEBUG1(" Number of frames 0x%x",*((UINT8*)(p_pkt + 1) + p_pkt->offset));
+ APPL_TRACE_DEBUG1("Sequence Number 0x%x",p_pkt->layer_specific);
+ /* Get SCB and correct sep type*/
+ for(index = 0; index < BTA_AV_NUM_STRS;index ++ )
+ {
+ p_scb = bta_av_cb.p_scb[index];
+ if((p_scb->avdt_handle == handle)&&(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK))
+ break;
+ }
+ if(index == BTA_AV_NUM_STRS) /* cannot find correct handler */
+ {
+ GKI_freebuf(p_pkt);
+ return;
+ }
+ p_pkt->event = BTA_AV_MEDIA_DATA_EVT;
+ p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_DATA_EVT, (tBTA_AV_MEDIA*)p_pkt);
+ GKI_freebuf(p_pkt); /* a copy of packet had been delivered, we free this buffer */
+}
+
+/*******************************************************************************
+**
** Function bta_av_stream0_cback
**
** Description This is the AVDTP callback function for stream events.
** Returns
**
*******************************************************************************/
-static void bta_av_adjust_seps_idx(tBTA_AV_SCB *p_scb)
+static void bta_av_adjust_seps_idx(tBTA_AV_SCB *p_scb, UINT8 avdt_handle)
{
- int xx;
-
+ int xx;
APPL_TRACE_DEBUG1("bta_av_adjust_seps_idx codec_type: %d", p_scb->codec_type);
for(xx=0; xx<BTA_AV_MAX_SEPS; xx++)
{
APPL_TRACE_DEBUG2("av_handle: %d codec_type: %d",
p_scb->seps[xx].av_handle, p_scb->seps[xx].codec_type);
- if(p_scb->seps[xx].av_handle && p_scb->codec_type == p_scb->seps[xx].codec_type)
+ if((p_scb->seps[xx].av_handle && p_scb->codec_type == p_scb->seps[xx].codec_type)
+ && (p_scb->seps[xx].av_handle == avdt_handle))
{
p_scb->sep_idx = xx;
p_scb->avdt_handle = p_scb->seps[xx].av_handle;
av_open.chnl = p_scb->chnl;
av_open.hndl = p_scb->hndl;
start.status = BTA_AV_FAIL_ROLE;
+ if(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ av_open.sep = AVDT_TSEP_SNK;
+ else if(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ av_open.sep = AVDT_TSEP_SRC;
(*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *)&av_open);
}
else
UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
ATTR_ID_PROTOCOL_DESC_LIST,
ATTR_ID_BT_PROFILE_DESC_LIST};
+ UINT16 sdp_uuid = 0; /* UUID for which SDP has to be done */
APPL_TRACE_DEBUG3("bta_av_do_disc_a2d use_rc: %d rs:%d, oc:%d",
p_data->api_open.use_rc, p_data->api_open.switch_res, bta_av_cb.audio_open_cnt);
db_params.num_attr = 3;
db_params.p_db = p_scb->p_disc_db;
db_params.p_attrs = attr_list;
-
- if(A2D_FindService(UUID_SERVCLASS_AUDIO_SINK, p_scb->peer_addr, &db_params,
+ p_scb->uuid_int = p_data->api_open.uuid;
+ if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SINK)
+ sdp_uuid = UUID_SERVCLASS_AUDIO_SOURCE;
+ else if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
+ sdp_uuid = UUID_SERVCLASS_AUDIO_SINK;
+
+ APPL_TRACE_DEBUG2("uuid_int 0x%x, Doing SDP For 0x%x", p_scb->uuid_int, sdp_uuid);
+ if(A2D_FindService(sdp_uuid, p_scb->peer_addr, &db_params,
bta_av_a2d_sdp_cback) == A2D_SUCCESS)
{
return;
tAVDT_SEP_INFO *p_info;
tAVDT_CFG *p_evt_cfg = &p_data->str_msg.cfg;
UINT8 psc_mask = (p_evt_cfg->psc_mask | p_scb->cfg.psc_mask);
+ UINT8 local_sep; /* sep type of local handle on which connection was received */
+ UINT8 count = 0;
+ tBTA_AV_STR_MSG *p_msg = (tBTA_AV_STR_MSG *)p_data;
UNUSED(p_data);
+ local_sep = bta_av_get_scb_sep_type(p_scb, p_msg->handle);
p_scb->avdt_label = p_data->str_msg.msg.hdr.label;
memcpy(p_scb->cfg.codec_info, p_evt_cfg->codec_info, AVDT_CODEC_SIZE);
p_scb->codec_type = p_evt_cfg->codec_info[BTA_AV_CODEC_TYPE_IDX];
p_info->in_use = 0;
p_info->media_type = p_scb->media_type;
p_info->seid = p_data->str_msg.msg.config_ind.int_seid;
- p_info->tsep = AVDT_TSEP_SNK;
+
+ /* Sep type of Peer will be oppsite role to our local sep */
+ if (local_sep == AVDT_TSEP_SRC)
+ p_info->tsep = AVDT_TSEP_SNK;
+ else if (local_sep == AVDT_TSEP_SNK)
+ p_info->tsep = AVDT_TSEP_SRC;
+
p_scb->role |= BTA_AV_ROLE_AD_ACP;
p_scb->cur_psc_mask = p_evt_cfg->psc_mask;
if (bta_av_cb.features & BTA_AV_FEAT_RCTG)
p_scb->num_seps = 1;
p_scb->sep_info_idx = 0;
APPL_TRACE_DEBUG3("bta_av_config_ind: SEID: %d use_rc: %d cur_psc_mask:0x%x", p_info->seid, p_scb->use_rc, p_scb->cur_psc_mask);
-
- p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type,
+ /* in case of A2DP SINK this is the first time peer data is being sent to co functions */
+ if (local_sep == AVDT_TSEP_SNK)
+ {
+ p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type,
+ p_evt_cfg->codec_info,
+ p_info->seid,
+ p_scb->peer_addr,
+ p_evt_cfg->num_protect,
+ p_evt_cfg->protect_info,
+ AVDT_TSEP_SNK,
+ p_msg->handle);
+ }
+ else
+ {
+ p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type,
p_evt_cfg->codec_info,
p_info->seid,
p_scb->peer_addr,
p_evt_cfg->num_protect,
- p_evt_cfg->protect_info);
+ p_evt_cfg->protect_info,
+ AVDT_TSEP_SRC,
+ p_msg->handle);
+ }
}
}
void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
UINT8 num = p_data->ci_setconfig.num_seid + 1;
+ UINT8 avdt_handle = p_data->ci_setconfig.avdt_handle;
UINT8 *p_seid = p_data->ci_setconfig.p_seid;
int i;
+ UINT8 local_sep;
/* we like this codec_type. find the sep_idx */
- bta_av_adjust_seps_idx(p_scb);
+ local_sep = bta_av_get_scb_sep_type(p_scb,avdt_handle);
+ bta_av_adjust_seps_idx(p_scb, avdt_handle);
APPL_TRACE_DEBUG2("bta_av_setconfig_rsp: sep_idx: %d cur_psc_mask:0x%x", p_scb->sep_idx, p_scb->cur_psc_mask);
+ if ((AVDT_TSEP_SNK == local_sep) && (p_data->ci_setconfig.err_code == AVDT_SUCCESS) &&
+ (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL))
+ p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
+ (tBTA_AV_MEDIA*)p_scb->cfg.codec_info);
+
+
AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, p_data->ci_setconfig.err_code,
p_data->ci_setconfig.category);
if (p_scb->codec_type == BTA_AV_CODEC_SBC || num > 1)
{
/* if SBC is used by the SNK as INT, discover req is not sent in bta_av_config_ind.
- * call disc_res now */
- p_scb->p_cos->disc_res(p_scb->hndl, num, num, p_scb->peer_addr);
+ * call disc_res now */
+ /* this is called in A2DP SRC path only, In case of SINK we don't need it */
+ if (local_sep == AVDT_TSEP_SRC)
+ p_scb->p_cos->disc_res(p_scb->hndl, num, num, 0, p_scb->peer_addr,
+ UUID_SERVCLASS_AUDIO_SOURCE);
}
else
{
p_scb->sep_info[i].media_type = p_scb->media_type;
p_scb->sep_info[i].seid = p_seid[i-1];
}
- bta_av_next_getcap(p_scb, p_data);
+ /* only in case of local sep as SRC we need to look for other SEPs, In case of SINK we don't */
+ if (local_sep == AVDT_TSEP_SRC)
+ bta_av_next_getcap(p_scb, p_data);
}
}
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr);
#endif
+ if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ open.sep = AVDT_TSEP_SNK;
+ else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ open.sep = AVDT_TSEP_SRC;
+
(*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open);
if(open.starting)
{
*******************************************************************************/
void bta_av_disc_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
- UINT8 num_snks = 0, i;
+ UINT8 num_snks = 0, num_srcs =0, i;
+ /* our uuid in case we initiate connection */
+ UINT16 uuid_int = p_scb->uuid_int;
+ APPL_TRACE_DEBUG1(" initiator UUID 0x%x", uuid_int);
/* store number of stream endpoints returned */
p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps;
{
/* steam not in use, is a sink, and is audio */
if ((p_scb->sep_info[i].in_use == FALSE) &&
- (p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) &&
(p_scb->sep_info[i].media_type == p_scb->media_type))
{
- num_snks++;
+ if((p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) &&
+ (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE))
+ num_snks++;
+
+ if((p_scb->sep_info[i].tsep == AVDT_TSEP_SRC) &&
+ (uuid_int == UUID_SERVCLASS_AUDIO_SINK))
+ num_srcs++;
+
}
}
- p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, p_scb->peer_addr);
+ p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, num_srcs, p_scb->peer_addr,
+ uuid_int);
p_scb->num_disc_snks = num_snks;
+ p_scb->num_disc_srcs = num_srcs;
/* if we got any */
if (p_scb->num_seps > 0)
num_snks++;
}
}
-
- p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, p_scb->peer_addr);
+ p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, 0, p_scb->peer_addr,
+ UUID_SERVCLASS_AUDIO_SOURCE);
p_scb->num_disc_snks = num_snks;
+ p_scb->num_disc_srcs = 0;
/* if we got any */
if (p_scb->num_seps > 0)
/* set the state back to initial state */
bta_av_set_scb_sst_init(p_scb);
+ if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ open.sep = AVDT_TSEP_SNK;
+ else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ open.sep = AVDT_TSEP_SRC;
+
(*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open);
}
tAVDT_CFG cfg;
UINT8 media_type;
tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx];
+ UINT16 uuid_int; /* UUID for which connection was initiatied */
memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG));
cfg.num_codec = 1;
/* save copy of codec type and configuration */
p_scb->codec_type = cfg.codec_info[BTA_AV_CODEC_TYPE_IDX];
memcpy(&p_scb->cfg, &cfg, sizeof(tAVDT_CFG));
- bta_av_adjust_seps_idx(p_scb);
+
+ uuid_int = p_scb->uuid_int;
+ APPL_TRACE_DEBUG1(" initiator UUID = 0x%x ", uuid_int);
+ if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
+ bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC));
+ else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK)
+ bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SNK));
+
/* use only the services peer supports */
cfg.psc_mask &= p_scb->p_cap->psc_mask;
p_scb->cur_psc_mask = cfg.psc_mask;
+ if ((uuid_int == UUID_SERVCLASS_AUDIO_SINK) &&
+ (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL))
+ {
+ APPL_TRACE_DEBUG0(" Configure Deoder for Sink Connection ");
+ p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
+ (tBTA_AV_MEDIA*)p_scb->cfg.codec_info);
+ }
+
/* open the stream */
AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr,
p_scb->sep_info[p_scb->sep_info_idx].seid, &cfg);
void bta_av_setconfig_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
{
tBTA_AV_REJECT reject;
+ UINT8 avdt_handle = p_data->ci_setconfig.avdt_handle;
+
+ bta_av_adjust_seps_idx(p_scb, avdt_handle);
+ APPL_TRACE_DEBUG1("bta_av_setconfig_rej: sep_idx: %d",p_scb->sep_idx);
+ AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_UNSUP_CFG, 0);
- APPL_TRACE_DEBUG0("bta_av_setconfig_rej");
- AVDT_ConfigRsp(p_data->str_msg.handle, p_data->str_msg.msg.hdr.label, AVDT_ERR_BAD_STATE, 0);
bdcpy(reject.bd_addr, p_data->str_msg.bd_addr);
reject.hndl = p_scb->hndl;
(*bta_av_cb.p_cback)(BTA_AV_REJECT_EVT, (tBTA_AV *) &reject);
if (new_role & BTA_AV_ROLE_START_INT)
initiator = TRUE;
+ /* for A2DP SINK we do not send get_caps */
+ if ((p_scb->avdt_handle == p_scb->seps[p_scb->sep_idx].av_handle)
+ &&(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK))
+ {
+ p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON);
+ APPL_TRACE_DEBUG1(" Local SEP type is SNK new wait is 0x%x",p_scb->wait);
+ }
if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_FAILED)
{
/* role switch has failed */
data.open.status = p_scb->open_status;
data.open.chnl = p_scb->chnl;
data.open.hndl = p_scb->hndl;
+
+ if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ data.open.sep = AVDT_TSEP_SNK;
+ else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ data.open.sep = AVDT_TSEP_SRC;
+
event = BTA_AV_OPEN_EVT;
p_scb->open_status = BTA_AV_SUCCESS;
{
UNUSED(p_data);
- APPL_TRACE_DEBUG1("bta_av_rcfg_open, num_disc_snks = %d", p_scb->num_disc_snks);
+ APPL_TRACE_DEBUG1("bta_av_rcfg_open, num_disc_snks = %d", p_scb->num_disc_snks);
if (p_scb->num_disc_snks == 0)
{
memcpy(p_scb->cfg.codec_info, p_scb->p_cap->codec_info, AVDT_CODEC_SIZE);
/* we may choose to use a different SEP at reconfig.
* adjust the sep_idx now */
- bta_av_adjust_seps_idx(p_scb);
+ bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC));
/* open the stream with the new config */
p_scb->sep_info_idx = p_scb->rcfg_idx;
#endif
bta_av_del_sdp_rec(&p_cb->sdp_a2d_handle);
bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
+
+#ifdef BTA_AVK_INCLUDED
+ bta_av_del_sdp_rec(&p_cb->sdp_a2d_snk_handle);
+ bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK);
+#endif
}
}
else
** Returns void
**
*******************************************************************************/
-void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id)
+void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name, UINT8 app_id, tBTA_AV_DATA_CBACK *p_data_cback)
{
tBTA_AV_API_REG *p_buf;
p_buf->p_service_name[0] = 0;
}
p_buf->app_id = app_id;
+ p_buf->p_app_data_cback = p_data_cback;
bta_sys_sendmsg(p_buf);
}
}
** Returns void
**
*******************************************************************************/
-void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle, BOOLEAN use_rc, tBTA_SEC sec_mask)
+void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle, BOOLEAN use_rc, tBTA_SEC sec_mask,
+ UINT16 uuid)
{
tBTA_AV_API_OPEN *p_buf;
p_buf->use_rc = use_rc;
p_buf->sec_mask = sec_mask;
p_buf->switch_res = BTA_AV_RS_NONE;
+ p_buf->uuid = uuid;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
+** Function BTA_AvEnable_Sink
+**
+** Description Enable/Disable A2DP Sink..
+**
+** Returns void
+**
+*******************************************************************************/
+void BTA_AvEnable_Sink(int enable)
+{
+ BT_HDR *p_buf;
+
+#ifdef BTA_AVK_INCLUDED
+ if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
+ {
+ p_buf->event = BTA_AV_API_SINK_ENABLE_EVT;
+ p_buf->layer_specific = enable;
+ bta_sys_sendmsg(p_buf);
+ }
+#else
+ return;
+#endif
+}
+
+/*******************************************************************************
+**
** Function BTA_AvStop
**
** Description Stop audio/video stream data transfer.
**
*******************************************************************************/
void bta_av_ci_setconfig(tBTA_AV_HNDL hndl, UINT8 err_code, UINT8 category,
- UINT8 num_seid, UINT8 *p_seid, BOOLEAN recfg_needed)
+ UINT8 num_seid, UINT8 *p_seid, BOOLEAN recfg_needed, UINT8 avdt_handle)
{
tBTA_AV_CI_SETCONFIG *p_buf;
p_buf->category = category;
p_buf->recfg_needed = recfg_needed;
p_buf->num_seid = num_seid;
+ p_buf->avdt_handle= avdt_handle;
if(p_seid && num_seid)
{
p_buf->p_seid = (UINT8 *)(p_buf + 1);
BTA_AV_AVRC_CLOSE_EVT,
BTA_AV_CONN_CHG_EVT,
BTA_AV_DEREG_COMP_EVT,
+#ifdef BTA_AVK_INCLUDED
+ BTA_AV_API_SINK_ENABLE_EVT,
+#endif
#if (AVDT_REPORTING == TRUE)
BTA_AV_AVDT_RPT_CONN_EVT,
#endif
/* function types for call-out functions */
typedef BOOLEAN (*tBTA_AV_CO_INIT) (UINT8 *p_codec_type, UINT8 *p_codec_info,
UINT8 *p_num_protect, UINT8 *p_protect_info, UINT8 index);
-
typedef void (*tBTA_AV_CO_DISC_RES) (tBTA_AV_HNDL hndl, UINT8 num_seps,
- UINT8 num_snk, BD_ADDR addr);
-
+ UINT8 num_snk, UINT8 num_src, BD_ADDR addr, UINT16 uuid_local);
typedef UINT8 (*tBTA_AV_CO_GETCFG) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid,
UINT8 *p_num_protect, UINT8 *p_protect_info);
typedef void (*tBTA_AV_CO_SETCFG) (tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
- UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr,
- UINT8 num_protect, UINT8 *p_protect_info);
+ UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr,
+ UINT8 num_protect, UINT8 *p_protect_info,
+ UINT8 t_local_sep, UINT8 avdt_handle);
typedef void (*tBTA_AV_CO_OPEN) (tBTA_AV_HNDL hndl,
tBTA_AV_CODEC codec_type, UINT8 *p_codec_info,
UINT16 mtu);
BT_HDR hdr;
char p_service_name[BTA_SERVICE_NAME_LEN+1];
UINT8 app_id;
+ tBTA_AV_DATA_CBACK *p_app_data_cback;
} tBTA_AV_API_REG;
BOOLEAN use_rc;
tBTA_SEC sec_mask;
tBTA_AV_RS_RES switch_res;
+ UINT16 uuid; /* uuid of initiator */
} tBTA_AV_API_OPEN;
/* data type for BTA_AV_API_STOP_EVT */
UINT8 num_seid;
UINT8 *p_seid;
BOOLEAN recfg_needed;
+ UINT8 avdt_handle; /* local sep type for which this stream will be set up */
} tBTA_AV_CI_SETCONFIG;
/* data type for all stream events from AVDTP */
/* type for SEP control block */
typedef struct
{
- UINT8 av_handle; /* AVDTP handle */
- tBTA_AV_CODEC codec_type; /* codec type */
+ UINT8 av_handle; /* AVDTP handle */
+ tBTA_AV_CODEC codec_type; /* codec type */
+ UINT8 tsep; /* SEP type of local SEP */
+ tBTA_AV_DATA_CBACK *p_app_data_cback; /* Application callback for media packets */
} tBTA_AV_SEP;
UINT8 hdi; /* the index to SCB[] */
UINT8 num_seps; /* number of seps returned by stream discovery */
UINT8 num_disc_snks; /* number of discovered snks */
+ UINT8 num_disc_srcs; /* number of discovered srcs */
UINT8 sep_info_idx; /* current index into sep_info */
UINT8 sep_idx; /* current index into local seps[] */
UINT8 rcfg_idx; /* reconfig requested index into sep_info */
UINT8 wait; /* set 0x1, when getting Caps as ACP, set 0x2, when started */
UINT8 q_tag; /* identify the associated q_info union member */
BOOLEAN no_rtp_hdr; /* TRUE if add no RTP header*/
+ UINT16 uuid_int; /*intended UUID of Initiator to connect to */
} tBTA_AV_SCB;
#define BTA_AV_RC_ROLE_MASK 0x10
TIMER_LIST_ENT sig_tmr; /* link timer */
TIMER_LIST_ENT acp_sig_tmr; /* timer to monitor signalling when accepting */
UINT32 sdp_a2d_handle; /* SDP record handle for audio src */
+#ifdef BTA_AVK_INCLUDED
+ UINT32 sdp_a2d_snk_handle; /* SDP record handle for audio snk */
+#endif
UINT32 sdp_vdp_handle; /* SDP record handle for video src */
tBTA_AV_FEAT features; /* features mask */
tBTA_SEC sec_mask; /* security mask */
extern const tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
extern const tBTA_AV_SACT bta_av_vdp_action[];
extern tAVDT_CTRL_CBACK * const bta_av_dt_cback[];
+extern void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt);
/*****************************************************************************
** Function prototypes
*****************************************************************************/
/* AVDTP protocol timeout values */
+#define BTIF_AVK_SERVICE_NAME "Advanced Audio Sink"
+
#ifndef BTA_AV_RET_TOUT
#define BTA_AV_RET_TOUT 4
#endif
typedef void (*tBTA_AV_NSM_ACT)(tBTA_AV_DATA *p_data);
static void bta_av_api_enable(tBTA_AV_DATA *p_data);
static void bta_av_api_register(tBTA_AV_DATA *p_data);
+#ifdef BTA_AVK_INCLUDED
+static void bta_av_api_sink_enable(tBTA_AV_DATA *p_data);
+#endif
static void bta_av_ci_data(tBTA_AV_DATA *p_data);
#if (AVDT_REPORTING == TRUE)
static void bta_av_rpc_conn(tBTA_AV_DATA *p_data);
bta_av_rc_closed, /* BTA_AV_AVRC_CLOSE_EVT */
bta_av_conn_chg, /* BTA_AV_CONN_CHG_EVT */
bta_av_dereg_comp, /* BTA_AV_DEREG_COMP_EVT */
+#ifdef BTA_AVK_INCLUDED
+ bta_av_api_sink_enable, /* BTA_AV_API_SINK_ENABLE_EVT */
+#endif
#if (AVDT_REPORTING == TRUE)
bta_av_rpc_conn, /* BTA_AV_AVDT_RPT_CONN_EVT */
#endif
}
#endif
+#ifdef BTA_AVK_INCLUDED
+/*******************************************************************************
+**
+** Function bta_av_api_sink_enable
+**
+** Description activate, deactive A2DP Sink,
+**
+** Returns void
+**
+*******************************************************************************/
+
+static void bta_av_api_sink_enable(tBTA_AV_DATA *p_data)
+{
+ UINT16 activate_sink = 0;
+ activate_sink = p_data->hdr.layer_specific;
+ APPL_TRACE_DEBUG1("bta_av_api_sink_enable %d ", activate_sink)
+ char p_service_name[BTA_SERVICE_NAME_LEN+1];
+ BCM_STRNCPY_S(p_service_name, sizeof(p_service_name),
+ BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN);
+
+ if(activate_sink)
+ {
+ AVDT_SINK_Activate();
+ if (bta_av_cb.sdp_a2d_snk_handle == 0)
+ {
+ bta_av_cb.sdp_a2d_snk_handle = SDP_CreateRecord();
+ A2D_AddRecord(UUID_SERVCLASS_AUDIO_SINK, p_service_name, NULL,
+ A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_snk_handle);
+ bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SINK);
+ }
+ }
+ else
+ {
+ AVDT_SINK_Deactivate();
+ if (bta_av_cb.sdp_a2d_snk_handle != 0)
+ {
+ SDP_DeleteRecord(bta_av_cb.sdp_a2d_snk_handle);
+ bta_av_cb.sdp_a2d_snk_handle = 0;
+ bta_sys_remove_uuid(UUID_SERVCLASS_AUDIO_SINK);
+ }
+ }
+}
+#endif
/*******************************************************************************
**
** Function bta_av_api_register
tBTA_AV_CODEC codec_type;
tBTA_UTL_COD cod;
UINT8 index = 0;
+ char p_avk_service_name[BTA_SERVICE_NAME_LEN+1];
+ BCM_STRNCPY_S(p_avk_service_name, sizeof(p_avk_service_name), BTIF_AVK_SERVICE_NAME, BTA_SERVICE_NAME_LEN);
memset(&cs,0,sizeof(tAVDT_CS));
}
/* Set the Capturing service class bit */
+#ifdef BTA_AVK_INCLUDED
+ cod.service = BTM_COD_SERVICE_CAPTURING | BTM_COD_SERVICE_RENDERING;
+#else
cod.service = BTM_COD_SERVICE_CAPTURING;
+#endif
utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS);
} /* if 1st channel */
(*bta_av_a2d_cos.init)(&codec_type, cs.cfg.codec_info,
&cs.cfg.num_protect, cs.cfg.protect_info, index) == TRUE)
{
+
+#ifdef BTA_AVK_INCLUDED
+ if(index == 1)
+ {
+ cs.tsep = AVDT_TSEP_SNK;
+ cs.p_data_cback = bta_av_stream_data_cback;
+ }
+ APPL_TRACE_DEBUG1(" SEP Type = %d",cs.tsep);
+#endif
if(AVDT_CreateStream(&p_scb->seps[index].av_handle, &cs) == AVDT_SUCCESS)
{
p_scb->seps[index].codec_type = codec_type;
+
+#ifdef BTA_AVK_INCLUDED
+ p_scb->seps[index].tsep = cs.tsep;
+ if(cs.tsep == AVDT_TSEP_SNK)
+ p_scb->seps[index].p_app_data_cback = p_data->api_reg.p_app_data_cback;
+ else
+ p_scb->seps[index].p_app_data_cback = NULL; /* In case of A2DP SOURCE we don't need a callback to handle media packets */
+#endif
+
APPL_TRACE_DEBUG3("audio[%d] av_handle: %d codec_type: %d",
index, p_scb->seps[index].av_handle, p_scb->seps[index].codec_type);
index++;
A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_handle);
bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SOURCE);
+#ifdef BTA_AVK_INCLUDED
+ bta_av_cb.sdp_a2d_snk_handle = SDP_CreateRecord();
+ A2D_AddRecord(UUID_SERVCLASS_AUDIO_SINK, p_avk_service_name, NULL,
+ A2D_SUPF_PLAYER, bta_av_cb.sdp_a2d_snk_handle);
+ bta_sys_add_uuid(UUID_SERVCLASS_AUDIO_SINK);
+#endif
/* start listening when A2DP is registered */
if (bta_av_cb.features & BTA_AV_FEAT_RCTG)
bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
case BTA_AV_AVRC_CLOSE_EVT: return "AVRC_CLOSE";
case BTA_AV_CONN_CHG_EVT: return "CONN_CHG";
case BTA_AV_DEREG_COMP_EVT: return "DEREG_COMP";
+#ifdef BTA_AVK_INCLUDED
+ case BTA_AV_API_SINK_ENABLE_EVT: return "SINK_ENABLE";
+#endif
#if (AVDT_REPORTING == TRUE)
case BTA_AV_AVDT_RPT_CONN_EVT: return "RPT_CONN";
#endif
/*******************************************************************************
**
+** Function bta_av_sbc_cfg_matches_cap
+**
+** Description This function checks whether an SBC codec configuration
+** matched with capabilities. Here we check subset.
+**
+** Returns 0 if ok, nonzero if error.
+**
+*******************************************************************************/
+UINT8 bta_av_sbc_cfg_matches_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap)
+{
+ UINT8 status = 0;
+ tA2D_SBC_CIE cfg_cie;
+
+ /* parse configuration */
+ if ((status = A2D_ParsSbcInfo(&cfg_cie, p_cfg, TRUE)) != 0)
+ {
+ APPL_TRACE_ERROR1(" bta_av_sbc_cfg_matches_cap Parsing Failed %d", status);
+ return status;
+ }
+
+ /* verify that each parameter is in range */
+
+ APPL_TRACE_DEBUG2(" FREQ peer: 0%x, capability 0%x", cfg_cie.samp_freq, p_cap->samp_freq);
+ APPL_TRACE_DEBUG2(" CH_MODE peer: 0%x, capability 0%x", cfg_cie.ch_mode, p_cap->ch_mode);
+ APPL_TRACE_DEBUG2(" BLOCK_LEN peer: 0%x, capability 0%x", cfg_cie.block_len, p_cap->block_len);
+ APPL_TRACE_DEBUG2(" SUB_BAND peer: 0%x, capability 0%x", cfg_cie.num_subbands, p_cap->num_subbands);
+ APPL_TRACE_DEBUG2(" ALLOC_MTHD peer: 0%x, capability 0%x", cfg_cie.alloc_mthd, p_cap->alloc_mthd);
+ APPL_TRACE_DEBUG2(" MAX_BitPool peer: 0%x, capability 0%x", cfg_cie.max_bitpool, p_cap->max_bitpool);
+ APPL_TRACE_DEBUG2(" Min_bitpool peer: 0%x, capability 0%x", cfg_cie.min_bitpool, p_cap->min_bitpool);
+
+ /* sampling frequency */
+ if ((cfg_cie.samp_freq & p_cap->samp_freq) == 0)
+ {
+ status = A2D_NS_SAMP_FREQ;
+ }
+ /* channel mode */
+ else if ((cfg_cie.ch_mode & p_cap->ch_mode) == 0)
+ {
+ status = A2D_NS_CH_MODE;
+ }
+ /* block length */
+ else if ((cfg_cie.block_len & p_cap->block_len) == 0)
+ {
+ status = A2D_BAD_BLOCK_LEN;
+ }
+ /* subbands */
+ else if ((cfg_cie.num_subbands & p_cap->num_subbands) == 0)
+ {
+ status = A2D_NS_SUBBANDS;
+ }
+ /* allocation method */
+ else if ((cfg_cie.alloc_mthd & p_cap->alloc_mthd) == 0)
+ {
+ status = A2D_NS_ALLOC_MTHD;
+ }
+ /* max bitpool */
+ else if (cfg_cie.max_bitpool > p_cap->max_bitpool)
+ {
+ status = A2D_NS_MAX_BITPOOL;
+ }
+ /* min bitpool */
+ else if (cfg_cie.min_bitpool < p_cap->min_bitpool)
+ {
+ status = A2D_NS_MIN_BITPOOL;
+ }
+
+ return status;
+}
+
+
+/*******************************************************************************
+**
** Function bta_av_sbc_cfg_in_cap
**
** Description This function checks whether an SBC codec configuration
/* verify that each parameter is in range */
+
/* sampling frequency */
if ((cfg_cie.samp_freq & p_cap->samp_freq) == 0)
{
/* API_RC_OPEN_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST },
/* SRC_DATA_READY_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST },
/* CI_SETCONFIG_OK_EVT */ {BTA_AV_SETCONFIG_RSP, BTA_AV_ST_RC_TIMER, BTA_AV_INCOMING_SST },
-/* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SETCONFIG_RSP, BTA_AV_CLEANUP, BTA_AV_INIT_SST },
+/* CI_SETCONFIG_FAIL_EVT */ {BTA_AV_SETCONFIG_REJ, BTA_AV_CLEANUP, BTA_AV_INIT_SST },
/* SDP_DISC_OK_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST },
/* SDP_DISC_FAIL_EVT */ {BTA_AV_FREE_SDB, BTA_AV_SIGNORE, BTA_AV_INCOMING_SST },
/* STR_DISC_OK_EVT */ {BTA_AV_DISC_RES_AS_ACP,BTA_AV_SIGNORE, BTA_AV_INCOMING_SST },
#define BTA_AV_META_MSG_EVT 17 /* metadata messages */
#define BTA_AV_REJECT_EVT 18 /* incoming connection rejected */
#define BTA_AV_RC_FEAT_EVT 19 /* remote control channel peer supported features update */
+#define BTA_AV_MEDIA_SINK_CFG_EVT 20 /* command to configure codec */
+#define BTA_AV_MEDIA_DATA_EVT 21 /* sending data to Media Task */
/* Max BTA event */
-#define BTA_AV_MAX_EVT 20
+#define BTA_AV_MAX_EVT 22
+
typedef UINT8 tBTA_AV_EVT;
tBTA_AV_STATUS status;
BOOLEAN starting;
tBTA_AV_EDR edr; /* 0, if peer device does not support EDR */
+ UINT8 sep; /* sep type of peer device */
} tBTA_AV_OPEN;
/* data associated with BTA_AV_CLOSE_EVT */
tBTA_AV_RC_FEAT rc_feat;
} tBTA_AV;
+/* union of data associated with AV Media callback */
+typedef union
+{
+ BT_HDR *p_data;
+ UINT8 *codec_info;
+} tBTA_AV_MEDIA;
+
#define BTA_AVC_PACKET_LEN AVRC_PACKET_LEN
#define BTA_VENDOR_DATA_OFFSET 6
/* AV callback */
typedef void (tBTA_AV_CBACK)(tBTA_AV_EVT event, tBTA_AV *p_data);
+typedef void (tBTA_AV_DATA_CBACK)(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data);
/* type for stream state machine action functions */
typedef void (*tBTA_AV_ACT)(void *p_cb, void *p_data);
**
*******************************************************************************/
BTA_API void BTA_AvRegister(tBTA_AV_CHNL chnl, const char *p_service_name,
- UINT8 app_id);
+ UINT8 app_id, tBTA_AV_DATA_CBACK *p_data_cback);
/*******************************************************************************
**
**
*******************************************************************************/
BTA_API void BTA_AvOpen(BD_ADDR bd_addr, tBTA_AV_HNDL handle,
- BOOLEAN use_rc, tBTA_SEC sec_mask);
+ BOOLEAN use_rc, tBTA_SEC sec_mask, UINT16 uuid);
/*******************************************************************************
**
/*******************************************************************************
**
+** Function BTA_AvEnable_Sink
+**
+** Description Enable/Disable A2DP Sink.
+**
+** Returns void
+**
+*******************************************************************************/
+void BTA_AvEnable_Sink(int enable);
+
+/*******************************************************************************
+**
** Function BTA_AvStart
**
** Description Start audio/video stream data transfer.
*******************************************************************************/
BTA_API extern void bta_av_ci_setconfig(tBTA_AV_HNDL hndl, UINT8 err_code,
UINT8 category, UINT8 num_seid, UINT8 *p_seid,
- BOOLEAN recfg_needed);
+ BOOLEAN recfg_needed, UINT8 avdt_handle);
#ifdef __cplusplus
**
*******************************************************************************/
BTA_API extern void bta_av_co_audio_disc_res(tBTA_AV_HNDL hndl, UINT8 num_seps,
- UINT8 num_snk, BD_ADDR addr);
+ UINT8 num_snk, UINT8 num_src, BD_ADDR addr, UINT16 uuid_local);
/*******************************************************************************
**
*******************************************************************************/
BTA_API extern void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr,
- UINT8 num_protect, UINT8 *p_protect_info);
+ UINT8 num_protect, UINT8 *p_protect_info,UINT8 t_local_sep, UINT8 avdt_handle);
/*******************************************************************************
**
/*******************************************************************************
**
+** Function bta_av_sbc_cfg_matches_cap
+**
+** Description This function checks whether an SBC codec configuration
+** matched with capabilities. Here we check subset.
+**
+** Returns 0 if ok, nonzero if error.
+**
+*******************************************************************************/
+extern UINT8 bta_av_sbc_cfg_matches_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap);
+
+/*******************************************************************************
+**
** Function bta_av_sbc_bld_hdr
**
** Description This function builds the packet header for MPF1.
/* SCMS-T protect info */
const UINT8 bta_av_co_cp_scmst[BTA_AV_CP_INFO_LEN] = "\x02\x02\x00";
-/* SBC codec capabilities */
+/* SBC SRC codec capabilities */
const tA2D_SBC_CIE bta_av_co_sbc_caps =
{
(A2D_SBC_IE_SAMP_FREQ_44), /* samp_freq */
A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */
};
+/* SBC SINK codec capabilities */
+const tA2D_SBC_CIE bta_av_co_sbc_sink_caps =
+{
+ (A2D_SBC_IE_SAMP_FREQ_48 | A2D_SBC_IE_SAMP_FREQ_44), /* samp_freq */
+ (A2D_SBC_IE_CH_MD_MONO | A2D_SBC_IE_CH_MD_STEREO | A2D_SBC_IE_CH_MD_JOINT | A2D_SBC_IE_CH_MD_DUAL), /* ch_mode */
+ (A2D_SBC_IE_BLOCKS_16 | A2D_SBC_IE_BLOCKS_12 | A2D_SBC_IE_BLOCKS_8 | A2D_SBC_IE_BLOCKS_4), /* block_len */
+ (A2D_SBC_IE_SUBBAND_4 | A2D_SBC_IE_SUBBAND_8), /* num_subbands */
+ (A2D_SBC_IE_ALLOC_MD_L | A2D_SBC_IE_ALLOC_MD_S), /* alloc_mthd */
+ A2D_SBC_IE_MAX_BITPOOL, /* max_bitpool */
+ A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */
+};
+
#if !defined(BTIF_AV_SBC_DEFAULT_SAMP_FREQ)
#define BTIF_AV_SBC_DEFAULT_SAMP_FREQ A2D_SBC_IE_SAMP_FREQ_44
#endif
{
BD_ADDR addr; /* address of audio/video peer */
tBTA_AV_CO_SINK snks[BTIF_SV_AV_AA_SEP_INDEX]; /* array of supported sinks */
+ tBTA_AV_CO_SINK srcs[BTIF_SV_AV_AA_SEP_INDEX]; /* array of supported srcs */
UINT8 num_snks; /* total number of sinks at peer */
+ UINT8 num_srcs; /* total number of srcs at peer */
UINT8 num_seps; /* total number of seids at peer */
UINT8 num_rx_snks; /* number of received sinks */
+ UINT8 num_rx_srcs; /* number of received srcs */
UINT8 num_sup_snks; /* number of supported sinks in the snks array */
+ UINT8 num_sup_srcs; /* number of supported srcs in the srcs array */
tBTA_AV_CO_SINK *p_snk; /* currently selected sink */
+ tBTA_AV_CO_SINK *p_src; /* currently selected src */
UINT8 codec_cfg[AVDT_CODEC_SIZE]; /* current codec configuration */
BOOLEAN cp_active; /* current CP configuration */
BOOLEAN acp; /* acceptor */
BOOLEAN recfg_needed; /* reconfiguration is needed */
BOOLEAN opened; /* opened */
UINT16 mtu; /* maximum transmit unit size */
+ UINT16 uuid_to_connect; /* uuid of peer device */
} tBTA_AV_CO_PEER;
typedef struct
static BOOLEAN bta_av_co_audio_sink_has_scmst(const tBTA_AV_CO_SINK *p_sink);
static BOOLEAN bta_av_co_audio_peer_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_snk_index);
static BOOLEAN bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg);
-
+static BOOLEAN bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg);
+static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index);
switch (index)
{
case BTIF_SV_AV_AA_SBC_INDEX:
- /* Set up for SBC codec */
+ /* Set up for SBC codec for SRC*/
*p_codec_type = BTA_AV_CODEC_SBC;
/* This should not fail because we are using constants for parameters */
/* Codec is valid */
return TRUE;
+#ifdef BTA_AVK_INCLUDED
+ case BTIF_SV_AV_AA_SBC_SINK_INDEX:
+ *p_codec_type = BTA_AV_CODEC_SBC;
+ /* This should not fail because we are using constants for parameters */
+ A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_sink_caps, p_codec_info);
+ /* Codec is valid */
+ return TRUE;
+#endif
default:
/* Not valid */
return FALSE;
**
*******************************************************************************/
BTA_API void bta_av_co_audio_disc_res(tBTA_AV_HNDL hndl, UINT8 num_seps, UINT8 num_snk,
- BD_ADDR addr)
+ UINT8 num_src, BD_ADDR addr, UINT16 uuid_local)
{
tBTA_AV_CO_PEER *p_peer;
FUNC_TRACE();
- APPL_TRACE_DEBUG3("bta_av_co_audio_disc_res h:x%x num_seps:%d num_snk:%d",
- hndl, num_seps, num_snk);
+ APPL_TRACE_DEBUG4("bta_av_co_audio_disc_res h:x%x num_seps:%d num_snk:%d num_src:%d",
+ hndl, num_seps, num_snk, num_src);
/* Find the peer info */
p_peer = bta_av_co_get_peer(hndl);
/* Copy the discovery results */
bdcpy(p_peer->addr, addr);
p_peer->num_snks = num_snk;
+ p_peer->num_srcs = num_src;
p_peer->num_seps = num_seps;
p_peer->num_rx_snks = 0;
+ p_peer->num_rx_srcs = 0;
p_peer->num_sup_snks = 0;
+ if (uuid_local == UUID_SERVCLASS_AUDIO_SINK)
+ p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SOURCE;
+ else if (uuid_local == UUID_SERVCLASS_AUDIO_SOURCE)
+ p_peer->uuid_to_connect = UUID_SERVCLASS_AUDIO_SINK;
}
/*******************************************************************************
**
+ ** Function bta_av_build_src_cfg
+ **
+ ** Description This function will build preferred config from src capabilities
+ **
+ **
+ ** Returns Pass or Fail for current getconfig.
+ **
+ *******************************************************************************/
+void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap)
+{
+ tA2D_SBC_CIE src_cap;
+ tA2D_SBC_CIE pref_cap;
+ UINT8 status = 0;
+
+ /* initialize it to default SBC configuration */
+ A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &btif_av_sbc_default_config, p_pref_cfg);
+ /* now try to build a preferred one */
+ /* parse configuration */
+ if ((status = A2D_ParsSbcInfo(&src_cap, p_src_cap, TRUE)) != 0)
+ {
+ APPL_TRACE_DEBUG1(" Cant parse src cap ret = %d", status);
+ return ;
+ }
+
+ if (src_cap.samp_freq & A2D_SBC_IE_SAMP_FREQ_48)
+ pref_cap.samp_freq = A2D_SBC_IE_SAMP_FREQ_48;
+ else if (src_cap.samp_freq & A2D_SBC_IE_SAMP_FREQ_44)
+ pref_cap.samp_freq = A2D_SBC_IE_SAMP_FREQ_44;
+
+ if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_JOINT)
+ pref_cap.ch_mode = A2D_SBC_IE_CH_MD_JOINT;
+ else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_STEREO)
+ pref_cap.ch_mode = A2D_SBC_IE_CH_MD_STEREO;
+ else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL)
+ pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL;
+ else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_MONO)
+ pref_cap.ch_mode = A2D_SBC_IE_CH_MD_MONO;
+
+ if (src_cap.block_len & A2D_SBC_IE_BLOCKS_16)
+ pref_cap.block_len = A2D_SBC_IE_BLOCKS_16;
+ else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_12)
+ pref_cap.block_len = A2D_SBC_IE_BLOCKS_12;
+ else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_8)
+ pref_cap.block_len = A2D_SBC_IE_BLOCKS_8;
+ else if (src_cap.block_len & A2D_SBC_IE_BLOCKS_4)
+ pref_cap.block_len = A2D_SBC_IE_BLOCKS_4;
+
+ if (src_cap.num_subbands & A2D_SBC_IE_SUBBAND_8)
+ pref_cap.num_subbands = A2D_SBC_IE_SUBBAND_8;
+ else if(src_cap.num_subbands & A2D_SBC_IE_SUBBAND_4)
+ pref_cap.num_subbands = A2D_SBC_IE_SUBBAND_4;
+
+ if (src_cap.alloc_mthd & A2D_SBC_IE_ALLOC_MD_L)
+ pref_cap.alloc_mthd = A2D_SBC_IE_ALLOC_MD_L;
+ else if(src_cap.alloc_mthd & A2D_SBC_IE_ALLOC_MD_S)
+ pref_cap.alloc_mthd = A2D_SBC_IE_ALLOC_MD_S;
+
+ pref_cap.max_bitpool = src_cap.max_bitpool;
+ pref_cap.min_bitpool = src_cap.min_bitpool;
+
+ A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &pref_cap, p_pref_cfg);
+}
+
+/*******************************************************************************
+ **
+ ** Function bta_av_audio_sink_getconfig
+ **
+ ** Description This callout function is executed by AV to retrieve the
+ ** desired codec and content protection configuration for the
+ ** A2DP Sink audio stream in Initiator.
+ **
+ **
+ ** Returns Pass or Fail for current getconfig.
+ **
+ *******************************************************************************/
+UINT8 bta_av_audio_sink_getconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
+ UINT8 *p_codec_info, UINT8 *p_sep_info_idx, UINT8 seid, UINT8 *p_num_protect,
+ UINT8 *p_protect_info)
+{
+
+ UINT8 result = A2D_FAIL;
+ BOOLEAN supported;
+ tBTA_AV_CO_PEER *p_peer;
+ tBTA_AV_CO_SINK *p_src;
+ UINT8 codec_cfg[AVDT_CODEC_SIZE];
+ UINT8 pref_cfg[AVDT_CODEC_SIZE];
+ UINT8 index;
+
+ FUNC_TRACE();
+
+ APPL_TRACE_DEBUG3("bta_av_audio_sink_getconfig handle:0x%x codec_type:%d seid:%d",
+ hndl, codec_type, seid);
+ APPL_TRACE_DEBUG4("num_protect:0x%02x protect_info:0x%02x%02x%02x",
+ *p_num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]);
+
+ /* Retrieve the peer info */
+ p_peer = bta_av_co_get_peer(hndl);
+ if (p_peer == NULL)
+ {
+ APPL_TRACE_ERROR0("bta_av_audio_sink_getconfig could not find peer entry");
+ return A2D_FAIL;
+ }
+
+ APPL_TRACE_DEBUG4("bta_av_audio_sink_getconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)",
+ p_peer->opened, p_peer->num_srcs, p_peer->num_rx_srcs, p_peer->num_sup_srcs);
+
+ p_peer->num_rx_srcs++;
+
+ /* Check if this is a supported configuration */
+ supported = FALSE;
+ switch (codec_type)
+ {
+ case BTA_AV_CODEC_SBC:
+ supported = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ if (supported)
+ {
+ /* If there is room for a new one */
+ if (p_peer->num_sup_srcs < BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs))
+ {
+ p_src = &p_peer->srcs[p_peer->num_sup_srcs++];
+
+ APPL_TRACE_DEBUG6("bta_av_audio_sink_getconfig saved caps[%x:%x:%x:%x:%x:%x]",
+ p_codec_info[1], p_codec_info[2], p_codec_info[3],
+ p_codec_info[4], p_codec_info[5], p_codec_info[6]);
+
+ memcpy(p_src->codec_caps, p_codec_info, AVDT_CODEC_SIZE);
+ p_src->codec_type = codec_type;
+ p_src->sep_info_idx = *p_sep_info_idx;
+ p_src->seid = seid;
+ p_src->num_protect = *p_num_protect;
+ memcpy(p_src->protect_info, p_protect_info, BTA_AV_CP_INFO_LEN);
+ }
+ else
+ {
+ APPL_TRACE_ERROR0("bta_av_audio_sink_getconfig no more room for SRC info");
+ }
+ }
+
+ /* If last SNK get capabilities or all supported codec caps retrieved */
+ if ((p_peer->num_rx_srcs == p_peer->num_srcs) ||
+ (p_peer->num_sup_srcs == BTA_AV_CO_NUM_ELEMENTS(p_peer->srcs)))
+ {
+ APPL_TRACE_DEBUG0("bta_av_audio_sink_getconfig last SRC reached");
+
+ /* Protect access to bta_av_co_cb.codec_cfg */
+ GKI_disable();
+
+ /* Find a src that matches the codec config */
+ if (bta_av_co_audio_peer_src_supports_codec(p_peer, &index))
+ {
+ APPL_TRACE_DEBUG0(" Codec Supported ");
+ p_src = &p_peer->srcs[index];
+
+ /* Build the codec configuration for this sink */
+ {
+ /* Save the new configuration */
+ p_peer->p_src = p_src;
+ /* get preferred config from src_caps */
+ bta_av_build_src_cfg(pref_cfg, p_src->codec_caps);
+ memcpy(p_peer->codec_cfg, pref_cfg, AVDT_CODEC_SIZE);
+
+ APPL_TRACE_DEBUG6("bta_av_audio_sink_getconfig p_codec_info[%x:%x:%x:%x:%x:%x]",
+ p_peer->codec_cfg[1], p_peer->codec_cfg[2], p_peer->codec_cfg[3],
+ p_peer->codec_cfg[4], p_peer->codec_cfg[5], p_peer->codec_cfg[6]);
+ /* By default, no content protection */
+ *p_num_protect = 0;
+
+#if defined(BTA_AV_CO_CP_SCMS_T) && (BTA_AV_CO_CP_SCMS_T == TRUE)
+ /* Check if this sink supports SCMS */
+ if (bta_av_co_audio_sink_has_scmst(p_sink))
+ {
+ p_peer->cp_active = TRUE;
+ bta_av_co_cb.cp.active = TRUE;
+ *p_num_protect = BTA_AV_CP_INFO_LEN;
+ memcpy(p_protect_info, bta_av_co_cp_scmst, BTA_AV_CP_INFO_LEN);
+ }
+ else
+ {
+ p_peer->cp_active = FALSE;
+ bta_av_co_cb.cp.active = FALSE;
+ }
+#endif
+
+ *p_sep_info_idx = p_src->sep_info_idx;
+ memcpy(p_codec_info, p_peer->codec_cfg, AVDT_CODEC_SIZE);
+ result = A2D_SUCCESS;
+ }
+ }
+ /* Protect access to bta_av_co_cb.codec_cfg */
+ GKI_enable();
+ }
+ return result;
+}
+/*******************************************************************************
+ **
** Function bta_av_co_audio_getconfig
**
** Description This callout function is executed by AV to retrieve the
FUNC_TRACE();
- APPL_TRACE_DEBUG3("bta_av_co_audio_getconfig handle:0x%x codec_type:%d seid:%d", hndl, codec_type, seid);
- APPL_TRACE_DEBUG4("num_protect:0x%02x protect_info:0x%02x%02x%02x",
- *p_num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]);
-
/* Retrieve the peer info */
p_peer = bta_av_co_get_peer(hndl);
if (p_peer == NULL)
return A2D_FAIL;
}
+ if (p_peer->uuid_to_connect == UUID_SERVCLASS_AUDIO_SOURCE)
+ {
+ result = bta_av_audio_sink_getconfig(hndl, codec_type, p_codec_info, p_sep_info_idx,
+ seid, p_num_protect, p_protect_info);
+ return result;
+ }
+ APPL_TRACE_DEBUG3("bta_av_co_audio_getconfig handle:0x%x codec_type:%d seid:%d",
+ hndl, codec_type, seid);
+ APPL_TRACE_DEBUG4("num_protect:0x%02x protect_info:0x%02x%02x%02x",
+ *p_num_protect, p_protect_info[0], p_protect_info[1], p_protect_info[2]);
+
APPL_TRACE_DEBUG4("bta_av_co_audio_getconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)",
p_peer->opened, p_peer->num_snks, p_peer->num_rx_snks, p_peer->num_sup_snks);
- /* Increment the number of received sinks capabilities */
p_peer->num_rx_snks++;
/* Check if this is a supported configuration */
**
*******************************************************************************/
BTA_API void bta_av_co_audio_setconfig(tBTA_AV_HNDL hndl, tBTA_AV_CODEC codec_type,
- UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr, UINT8 num_protect, UINT8 *p_protect_info)
+ UINT8 *p_codec_info, UINT8 seid, BD_ADDR addr, UINT8 num_protect, UINT8 *p_protect_info,
+ UINT8 t_local_sep, UINT8 avdt_handle)
{
tBTA_AV_CO_PEER *p_peer;
UINT8 status = A2D_SUCCESS;
UINT8 category = A2D_SUCCESS;
BOOLEAN recfg_needed = FALSE;
+ BOOLEAN codec_cfg_supported = FALSE;
UNUSED(seid);
UNUSED(addr);
APPL_TRACE_ERROR0("bta_av_co_audio_setconfig could not find peer entry");
/* Call call-in rejecting the configuration */
- bta_av_ci_setconfig(hndl, A2D_BUSY, AVDT_ASC_CODEC, 0, NULL, FALSE);
+ bta_av_ci_setconfig(hndl, A2D_BUSY, AVDT_ASC_CODEC, 0, NULL, FALSE, avdt_handle);
return;
}
+ APPL_TRACE_DEBUG4("bta_av_co_audio_setconfig peer(o=%d,n_snks=%d,n_rx_snks=%d,n_sup_snks=%d)",
+ p_peer->opened, p_peer->num_snks, p_peer->num_rx_snks, p_peer->num_sup_snks);
/* Sanity check: should not be opened at this point */
if (p_peer->opened)
#endif
if (status == A2D_SUCCESS)
{
+ if(AVDT_TSEP_SNK == t_local_sep)
+ {
+ codec_cfg_supported = bta_av_co_audio_sink_supports_config(codec_type, p_codec_info);
+ APPL_TRACE_DEBUG0(" Peer is A2DP SRC ");
+ }
+ if(AVDT_TSEP_SRC == t_local_sep)
+ {
+ codec_cfg_supported = bta_av_co_audio_media_supports_config(codec_type, p_codec_info);
+ APPL_TRACE_DEBUG0(" Peer is A2DP SINK ");
+ }
/* Check if codec configuration is supported */
- if (bta_av_co_audio_media_supports_config(codec_type, p_codec_info))
+ if (codec_cfg_supported)
{
+
/* Protect access to bta_av_co_cb.codec_cfg */
GKI_disable();
bta_av_co_cb.codec_cfg_setconfig.id = BTIF_AV_CODEC_SBC;
memcpy(bta_av_co_cb.codec_cfg_setconfig.info, p_codec_info, AVDT_CODEC_SIZE);
+ if(AVDT_TSEP_SNK == t_local_sep)
+ {
+ /* If Peer is SRC, and our cfg subset matches with what is requested by peer, then
+ just accept what peer wants */
+ memcpy(bta_av_co_cb.codec_cfg.info, p_codec_info, AVDT_CODEC_SIZE);
+ recfg_needed = FALSE;
+ }
break;
APPL_TRACE_DEBUG2("bta_av_co_audio_setconfig reject s=%d c=%d", status, category);
/* Call call-in rejecting the configuration */
- bta_av_ci_setconfig(hndl, status, category, 0, NULL, FALSE);
+ bta_av_ci_setconfig(hndl, status, category, 0, NULL, FALSE, avdt_handle);
}
else
{
APPL_TRACE_DEBUG1("bta_av_co_audio_setconfig accept reconf=%d", recfg_needed);
/* Call call-in accepting the configuration */
- bta_av_ci_setconfig(hndl, A2D_SUCCESS, A2D_SUCCESS, 0, NULL, recfg_needed);
+ bta_av_ci_setconfig(hndl, A2D_SUCCESS, A2D_SUCCESS, 0, NULL, recfg_needed, avdt_handle);
}
}
/*******************************************************************************
**
- ** Function bta_av_co_audio_media_supports_config
+ ** Function bta_av_co_audio_peer_src_supports_codec
+ **
+ ** Description Check if a peer acting as src supports codec config
+ **
+ ** Returns TRUE if the connection supports this codec, FALSE otherwise
+ **
+ *******************************************************************************/
+static BOOLEAN bta_av_co_audio_peer_src_supports_codec(tBTA_AV_CO_PEER *p_peer, UINT8 *p_src_index)
+{
+ int index;
+ UINT8 codec_type;
+ FUNC_TRACE();
+
+ /* Configure the codec type to look for */
+ codec_type = bta_av_co_cb.codec_cfg.id;
+
+
+ for (index = 0; index < p_peer->num_sup_srcs; index++)
+ {
+ if (p_peer->srcs[index].codec_type == codec_type)
+ {
+ switch (bta_av_co_cb.codec_cfg.id)
+ {
+ case BTIF_AV_CODEC_SBC:
+ if (p_src_index) *p_src_index = index;
+ if (0 == bta_av_sbc_cfg_matches_cap((UINT8 *)p_peer->srcs[index].codec_caps,
+ (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps))
+ {
+ return TRUE;
+ }
+ break;
+
+ default:
+ APPL_TRACE_ERROR1("peer_src_supports_codec: unsupported codec id %d",
+ bta_av_co_cb.codec_cfg.id);
+ return FALSE;
+ break;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/*******************************************************************************
+ **
+ ** Function bta_av_co_audio_sink_supports_config
**
** Description Check if the media source supports a given configuration
**
** Returns TRUE if the media source supports this config, FALSE otherwise
**
*******************************************************************************/
+static BOOLEAN bta_av_co_audio_sink_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg)
+{
+ FUNC_TRACE();
+
+ switch (codec_type)
+ {
+ case BTA_AV_CODEC_SBC:
+ if (bta_av_sbc_cfg_in_cap((UINT8 *)p_codec_cfg, (tA2D_SBC_CIE *)&bta_av_co_sbc_sink_caps))
+ {
+ return FALSE;
+ }
+ break;
+
+
+ default:
+ APPL_TRACE_ERROR1("bta_av_co_audio_media_supports_config unsupported codec type %d", codec_type);
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+/*******************************************************************************
+ **
+ ** Function bta_av_co_audio_media_supports_config
+ **
+ ** Description Check if the media sink supports a given configuration
+ **
+ ** Returns TRUE if the media source supports this config, FALSE otherwise
+ **
+ *******************************************************************************/
static BOOLEAN bta_av_co_audio_media_supports_config(UINT8 codec_type, const UINT8 *p_codec_cfg)
{
FUNC_TRACE();
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#if defined (__cplusplus) || (cplusplus)
+extern "C" {
+#endif
+
+int btCreateTrack(int trackFreq, int channelType);
+void btDeleteTrack();
+void btStopTrack();
+void btStartTrack();
+void btPauseTrack();
+int btWriteData(void *audioBuffer, int bufferlen);
+
+#if defined (__cplusplus) || (cplusplus)
+}
+#endif
BTIF_AV_STOP_STREAM_REQ_EVT,
BTIF_AV_SUSPEND_STREAM_REQ_EVT,
BTIF_AV_RECONFIGURE_REQ_EVT,
+ BTIF_AV_REQUEST_AUDIO_FOCUS_EVT,
+ BTIF_AV_REQUEST_ACTIVATE_SINK_EVT,
} btif_av_sm_event_t;
enum
{
BTIF_SV_AV_AA_SBC_INDEX = 0,
+ BTIF_SV_AV_AA_SBC_SINK_INDEX,
BTIF_SV_AV_AA_SEP_INDEX /* Last index */
};
tBTIF_AV_FEEDING_MODE feeding_mode;
tBTIF_AV_MEDIA_FEEDINGS feeding;
} tBTIF_MEDIA_INIT_AUDIO_FEEDING;
+
+typedef struct
+{
+ BT_HDR hdr;
+ UINT8 codec_info[AVDT_CODEC_SIZE];
+} tBTIF_MEDIA_SINK_CFG_UPDATE;
#endif
+typedef enum {
+ BTIF_MEDIA_AUDIOFOCUS_LOSS = 0,
+ BTIF_MEDIA_AUDIOFOCUS_GAIN,
+ BTIF_MEDIA_AUDIOFOCUS_LOSS_TRANSIENT
+} btif_media_AudioFocus_state;
+
/*******************************************************************************
** Public functions
*******************************************************************************/
extern BOOLEAN btif_media_task_stop_aa_req(void);
-
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush_req
+ **
+ ** Description Request to flush audio decoding pipe
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+extern BOOLEAN btif_media_task_aa_rx_flush_req(void);
/*******************************************************************************
**
** Function btif_media_task_aa_tx_flush_req
/*******************************************************************************
**
+ ** Function btif_media_sink_enque_buf
+ **
+ ** Description This function is called by the av_co to fill A2DP Sink Queue
+ **
+ **
+ ** Returns size of the queue
+ *******************************************************************************/
+ UINT8 btif_media_sink_enque_buf(BT_HDR *p_buf);
+
+
+
+/*******************************************************************************
+ **
** Function btif_media_aa_writebuf
**
** Description Enqueue a Advance Audio media GKI buffer to be processed by btif media task.
void btif_a2dp_on_suspend(void);
void btif_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av);
void btif_a2dp_set_tx_flush(BOOLEAN enable);
+void btif_a2dp_set_rx_flush(BOOLEAN enable);
+void btif_media_check_iop_exceptions(UINT8 *peer_bda);
+void btif_reset_decoder(UINT8 *p_av);
+BOOLEAN btif_media_task_start_decoding_req(void);
+void btif_a2dp_set_audio_focus_state(btif_media_AudioFocus_state state);
#endif
#ifndef BTIF_PROFILE_QUEUE_H
#define BTIF_PROFILE_QUEUE_H
-typedef bt_status_t (*btif_connect_cb_t)(bt_bdaddr_t *bda);
+typedef bt_status_t (*btif_connect_cb_t) (bt_bdaddr_t *bda, uint16_t uuid);
bt_status_t btif_queue_connect(uint16_t uuid, const bt_bdaddr_t *bda, btif_connect_cb_t connect_cb);
void btif_queue_advance();
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "bluetoothTrack.h"
+#include <media/AudioTrack.h>
+
+//#define DUMP_PCM_DATA TRUE
+#if (defined(DUMP_PCM_DATA) && (DUMP_PCM_DATA == TRUE))
+FILE *outputPcmSampleFile;
+char outputFilename [50] = "/data/misc/bluedroid/output_sample.pcm";
+#endif
+
+struct BluetoothTrack {
+ android::sp<android::AudioTrack> mTrack;
+};
+
+typedef struct BluetoothTrack BluetoothTrack;
+
+BluetoothTrack *track = NULL;
+
+int btCreateTrack(int trackFreq, int channelType)
+{
+ int ret = -1;
+ if (track == NULL)
+ track = new BluetoothTrack;
+ track->mTrack = NULL;
+ track->mTrack = new android::AudioTrack(AUDIO_STREAM_MUSIC, trackFreq, AUDIO_FORMAT_PCM_16_BIT,
+ channelType, (int)0, (audio_output_flags_t)AUDIO_OUTPUT_FLAG_FAST, NULL, NULL, 0, 0, android::AudioTrack::TRANSFER_SYNC);
+ if (track->mTrack == NULL)
+ {
+ delete track;
+ track = NULL;
+ return ret;
+ }
+ if (track->mTrack->initCheck() != 0)
+ {
+ delete track;
+ track = NULL;
+ return ret;
+ }
+#if (defined(DUMP_PCM_DATA) && (DUMP_PCM_DATA == TRUE))
+ outputPcmSampleFile = fopen(outputFilename, "ab");
+#endif
+ ret = 0;
+ track->mTrack->setVolume(1, 1);
+ return ret;
+}
+
+void btStartTrack()
+{
+ if ((track != NULL) && (track->mTrack.get() != NULL))
+ {
+ track->mTrack->start();
+ }
+}
+
+
+void btDeleteTrack()
+{
+ if ((track != NULL) && (track->mTrack.get() != NULL))
+ {
+ track->mTrack.clear();
+ delete track;
+ track = NULL;
+ }
+#if (defined(DUMP_PCM_DATA) && (DUMP_PCM_DATA == TRUE))
+ if (outputPcmSampleFile)
+ {
+ fclose(outputPcmSampleFile);
+ }
+ outputPcmSampleFile = NULL;
+#endif
+}
+
+void btPauseTrack()
+{
+ if ((track != NULL) && (track->mTrack.get() != NULL))
+ {
+ track->mTrack->pause();
+ track->mTrack->flush();
+ }
+}
+
+void btStopTrack()
+{
+ if ((track != NULL) && (track->mTrack.get() != NULL))
+ {
+ track->mTrack->stop();
+ }
+}
+
+int btWriteData(void *audioBuffer, int bufferlen)
+{
+ int retval = -1;
+ if ((track != NULL) && (track->mTrack.get() != NULL))
+ {
+#if (defined(DUMP_PCM_DATA) && (DUMP_PCM_DATA == TRUE))
+ if (outputPcmSampleFile)
+ {
+ fwrite ((audioBuffer), 1, (size_t)bufferlen, outputPcmSampleFile);
+ }
+#endif
+ retval = track->mTrack->write(audioBuffer, (size_t)bufferlen);
+ }
+ return retval;
+}
+
/*****************************************************************************
** Local type definitions
******************************************************************************/
+typedef enum
+{
+ SEP_SRC = 0x0,
+ SEP_SNK,
+ SEP_NOT_OPENED
+}tbtif_AV_SEP_TYPE;
typedef struct
{
btif_sm_handle_t sm_handle;
UINT8 flags;
tBTA_AV_EDR edr;
+ tbtif_AV_SEP_TYPE sep; /* sep type of peer device */
} btif_av_cb_t;
+typedef struct
+{
+ bt_bdaddr_t *target_bda;
+ uint16_t uuid;
+} btif_av_connect_req_t;
/*****************************************************************************
** Static variables
******************************************************************************/
CASE_RETURN_STR(BTIF_AV_STOP_STREAM_REQ_EVT)
CASE_RETURN_STR(BTIF_AV_SUSPEND_STREAM_REQ_EVT)
CASE_RETURN_STR(BTIF_AV_RECONFIGURE_REQ_EVT)
-
+ CASE_RETURN_STR(BTIF_AV_REQUEST_AUDIO_FOCUS_EVT)
+ CASE_RETURN_STR(BTIF_AV_REQUEST_ACTIVATE_SINK_EVT)
default: return "UNKNOWN_EVENT";
}
}
+/*******************************************************************************
+**
+** Function btif_av_request_audio_focus
+**
+** Description send request to gain audio focus
+**
+** Returns void
+**
+*******************************************************************************/
+void btif_av_request_audio_focus( BOOLEAN enable)
+{
+ btif_sm_state_t state;
+ state= btif_sm_get_state(btif_av_cb.sm_handle);
+ /* We shld be in started state */
+ if (state != BTIF_AV_STATE_STARTED)
+ return;
+ /* If we are in started state, suspend shld not have been initiated */
+ if ((btif_av_cb.flags & BTIF_AV_FLAG_REMOTE_SUSPEND )||
+ (btif_av_cb.flags & BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING))
+ return;
+ if(enable)
+ {
+ btif_dispatch_sm_event(BTIF_AV_REQUEST_AUDIO_FOCUS_EVT, NULL, 0);
+ }
+}
+
/****************************************************************************
** Local helper functions
*****************************************************************************/
{
BD_ADDR peer_addr;
UNUSED(tle);
-
+ btif_av_connect_req_t connect_req;
+ UNUSED(tle);
/* is there at least one RC connection - There should be */
if (btif_rc_get_connected_peer(peer_addr)) {
BTIF_TRACE_DEBUG1("%s Issuing connect to the remote RC peer", __FUNCTION__);
- btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (void*)&peer_addr);
+ /* In case of AVRCP connection request, we will initiate SRC connection */
+ connect_req.target_bda = (bt_bdaddr_t*)&peer_addr;
+ connect_req.uuid = UUID_SERVCLASS_AUDIO_SOURCE;
+ btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char*)&connect_req);
}
else
{
/*****************************************************************************
**
-** Function btif_av_state_idle_handler
+** Function btif_av_state_idle_handler
**
** Description State managing disconnected AV link
**
memset(&btif_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t));
btif_av_cb.flags = 0;
btif_av_cb.edr = 0;
+ btif_av_cb.sep = SEP_NOT_OPENED;
btif_a2dp_on_idle();
break;
case BTA_AV_ENABLE_EVT:
break;
+ case BTIF_AV_REQUEST_ACTIVATE_SINK_EVT:
+ {
+ int enable = *((int*)p_data);
+ BTIF_TRACE_DEBUG1(" Active_Sink enable %d", enable)
+ BTA_AvEnable_Sink(enable);
+ }
+ break;
+
case BTA_AV_REGISTER_EVT:
btif_av_cb.bta_handle = ((tBTA_AV*)p_data)->registr.hndl;
break;
{
if (event == BTIF_AV_CONNECT_REQ_EVT)
{
- memcpy(&btif_av_cb.peer_bda, (bt_bdaddr_t*)p_data, sizeof(bt_bdaddr_t));
+ memcpy(&btif_av_cb.peer_bda, ((btif_av_connect_req_t*)p_data)->target_bda,
+ sizeof(bt_bdaddr_t));
+ BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
+ TRUE, BTA_SEC_NONE, ((btif_av_connect_req_t*)p_data)->uuid);
}
else if (event == BTA_AV_PENDING_EVT)
{
bdcpy(btif_av_cb.peer_bda.address, ((tBTA_AV*)p_data)->pend.bd_addr);
+ BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
+ TRUE, BTA_SEC_NONE, UUID_SERVCLASS_AUDIO_SOURCE);
}
- BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
- TRUE, BTA_SEC_NONE);
btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENING);
} break;
case BTIF_SM_EXIT_EVT:
break;
+ case BTA_AV_REJECT_EVT:
+ BTIF_TRACE_DEBUG0(" Received BTA_AV_REJECT_EVT ");
+ HAL_CBACK(bt_av_callbacks, connection_state_cb,
+ BTAV_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
case BTA_AV_OPEN_EVT:
{
tBTA_AV *p_bta_data = (tBTA_AV*)p_data;
state = BTAV_CONNECTION_STATE_CONNECTED;
av_state = BTIF_AV_STATE_OPENED;
btif_av_cb.edr = p_bta_data->open.edr;
+
+ if (p_bta_data->open.sep == AVDT_TSEP_SRC)
+ btif_av_cb.sep = SEP_SRC;
+ else if (p_bta_data->open.sep == AVDT_TSEP_SNK)
+ btif_av_cb.sep = SEP_SNK;
}
else
{
state, &(btif_av_cb.peer_bda));
/* change state to open/idle based on the status */
btif_sm_change_state(btif_av_cb.sm_handle, av_state);
- /* if queued PLAY command, send it now */
- btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr,
+ if (btif_av_cb.sep == SEP_SNK)
+ {
+ /* if queued PLAY command, send it now */
+ btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr,
(p_bta_data->open.status == BTA_AV_SUCCESS));
+ }
+ else if (btif_av_cb.sep == SEP_SRC)
+ {
+ /* if queued PLAY command, send it now */
+ btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr, FALSE);
+ }
btif_queue_advance();
} break;
switch (event)
{
case BTIF_SM_ENTER_EVT:
-
- /* immediately stop transmission of frames */
- btif_a2dp_set_tx_flush(TRUE);
- /* wait for audioflinger to stop a2dp */
+ if (btif_av_cb.sep == SEP_SNK)
+ {
+ /* immediately stop transmission of frames */
+ btif_a2dp_set_tx_flush(TRUE);
+ /* wait for audioflinger to stop a2dp */
+ }
+ if (btif_av_cb.sep == SEP_SRC)
+ {
+ btif_a2dp_set_rx_flush(TRUE);
+ }
break;
case BTA_AV_STOP_EVT:
case BTIF_AV_STOP_STREAM_REQ_EVT:
+ if (btif_av_cb.sep == SEP_SNK)
+ {
/* immediately flush any pending tx frames while suspend is pending */
btif_a2dp_set_tx_flush(TRUE);
+ }
+ if (btif_av_cb.sep == SEP_SRC)
+ {
+ btif_a2dp_set_rx_flush(TRUE);
+ }
- btif_a2dp_on_stopped(NULL);
-
+ btif_a2dp_on_stopped(NULL);
break;
case BTIF_SM_EXIT_EVT:
break;
case BTIF_AV_START_STREAM_REQ_EVT:
+ if (btif_av_cb.sep == SEP_SRC)
+ {
+ BTA_AvStart();
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START;
+ break;
+ }
btif_a2dp_setup_codec();
BTA_AvStart();
btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START;
if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE))
return TRUE;
- if (btif_a2dp_on_started(&p_av->start,
- ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) != 0))) {
- /* only clear pending flag after acknowledgement */
- btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ /* In case peer is A2DP SRC we do not want to ack commands on UIPC*/
+ if (btif_av_cb.sep == SEP_SNK)
+ {
+ if (btif_a2dp_on_started(&p_av->start,
+ ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) != 0)))
+ {
+ /* only clear pending flag after acknowledgement */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ }
}
/* remain in open state if status failed */
if (p_av->start.status != BTA_AV_SUCCESS)
return FALSE;
+ if (btif_av_cb.sep == SEP_SRC)
+ {
+ btif_a2dp_set_rx_flush(FALSE); /* remove flush state, ready for streaming*/
+ }
+
/* change state to started, send acknowledgement if start is pending */
if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
- btif_a2dp_on_started(NULL, TRUE);
+ if (btif_av_cb.sep == SEP_SNK)
+ btif_a2dp_on_started(NULL, TRUE);
/* pending start flag will be cleared when exit current state */
}
btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_STARTED);
break;
case BTA_AV_CLOSE_EVT:
-
- /* avdtp link is closed */
+ /* avdtp link is closed */
btif_a2dp_on_stopped(NULL);
/* inform the application that we are disconnected */
case BTIF_AV_START_STREAM_REQ_EVT:
/* we were remotely started, just ack back the local request */
- btif_a2dp_on_started(NULL, TRUE);
+ if (btif_av_cb.sep == SEP_SNK)
+ btif_a2dp_on_started(NULL, TRUE);
break;
/* fixme -- use suspend = true always to work around issue with BTA AV */
always overrides */
btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+ if (btif_av_cb.sep == SEP_SNK)
+ {
/* immediately stop transmission of frames while suspend is pending */
- btif_a2dp_set_tx_flush(TRUE);
+ btif_a2dp_set_tx_flush(TRUE);
+ }
+
+ if (btif_av_cb.sep == SEP_SRC)
+ btif_a2dp_set_rx_flush(TRUE);
BTA_AvStop(TRUE);
break;
{
btif_av_cb.flags &= ~BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
+ if (btif_av_cb.sep == SEP_SNK)
+ {
/* suspend failed, reset back tx flush state */
- btif_a2dp_set_tx_flush(FALSE);
+ btif_a2dp_set_tx_flush(FALSE);
+ }
return FALSE;
}
case BTA_AV_STOP_EVT:
btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
-
btif_a2dp_on_stopped(&p_av->suspend);
HAL_CBACK(bt_av_callbacks, audio_state_cb,
break;
+ case BTIF_AV_REQUEST_AUDIO_FOCUS_EVT:
+ HAL_CBACK(bt_av_callbacks, audio_focus_request_cb,
+ 1, &(btif_av_cb.peer_bda));
+ break;
+
case BTA_AV_CLOSE_EVT:
btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
/* avdtp link is closed */
-
btif_a2dp_on_stopped(NULL);
/* inform the application that we are disconnected */
(char*)p_data, sizeof(tBTA_AV), NULL);
}
+static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
+{
+ btif_sm_state_t state;
+ UINT8 que_len;
+
+ if (event == BTA_AV_MEDIA_DATA_EVT)/* Switch to BTIF_MEDIA context */
+ {
+ state= btif_sm_get_state(btif_av_cb.sm_handle);
+ if ( (state == BTIF_AV_STATE_STARTED) || /* send SBC packets only in Started State */
+ (state == BTIF_AV_STATE_OPENED) )
+ {
+ que_len = btif_media_sink_enque_buf((BT_HDR *)p_data);
+ BTIF_TRACE_DEBUG1(" Packets in Que %d",que_len);
+ }
+ else
+ return;
+ }
+
+ if (event == BTA_AV_MEDIA_SINK_CFG_EVT) /* send a command to BT Media Task */
+ btif_reset_decoder((UINT8*)p_data);
+}
/*******************************************************************************
**
** Function btif_av_init
**
*******************************************************************************/
-static bt_status_t connect_int(bt_bdaddr_t *bd_addr)
+static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
{
+ btif_av_connect_req_t connect_req;
+ connect_req.target_bda = bd_addr;
+ connect_req.uuid = uuid;
BTIF_TRACE_EVENT1("%s", __FUNCTION__);
- btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char*)bd_addr);
+ btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char*)&connect_req);
return BT_STATUS_SUCCESS;
}
-static bt_status_t connect(bt_bdaddr_t *bd_addr)
+static bt_status_t connect_sink(bt_bdaddr_t *bd_addr)
+{
+ BTIF_TRACE_EVENT1("%s", __FUNCTION__);
+ CHECK_BTAV_INIT();
+ return btif_queue_connect(UUID_SERVCLASS_AUDIO_SINK, bd_addr,
+ connect_int);
+}
+
+static bt_status_t connect_src(bt_bdaddr_t *bd_addr)
{
BTIF_TRACE_EVENT1("%s", __FUNCTION__);
CHECK_BTAV_INIT();
return;
}
+/*******************************************************************************
+**
+** Function is_src
+**
+** Description Checks if peer device is A2DP SRC
+**
+** Returns Success in case peer is A2DP Src, FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t is_src( bt_bdaddr_t *bd_addr )
+{
+ BTIF_TRACE_DEBUG0(" isSrc: Check if peer device with bd_addr is audio src or sink");
+ if (btif_av_cb.sep == SEP_SRC)
+ {
+ BTIF_TRACE_DEBUG0(" Current Peer is SRC");
+ return BT_STATUS_SUCCESS;
+ }
+ else if (btif_av_cb.sep == SEP_SNK)
+ {
+ BTIF_TRACE_DEBUG0(" Current Peer is SNK");
+ return BT_STATUS_FAIL;
+ }
+ else
+ {
+ BTIF_TRACE_DEBUG0(" Stream not opened till now");
+ return BT_STATUS_NOT_READY;
+ }
+}
+
+/*******************************************************************************
+**
+** Function activate_sink
+**
+** Description Activates/Deactivates A2DP Sink
+**
+** Returns None
+**
+*******************************************************************************/
+void activate_sink(int enable)
+{
+ BTIF_TRACE_DEBUG1(" Activate Sink %d", enable);
+ btif_dispatch_sm_event(BTIF_AV_REQUEST_ACTIVATE_SINK_EVT, (char*)&enable, sizeof(int));
+}
+
+/*******************************************************************************
+**
+** Function suspend_sink
+**
+** Description Suspends stream in case of A2DP Sink
+**
+** Returns None
+**
+*******************************************************************************/
+void suspend_sink()
+{
+ BTIF_TRACE_DEBUG0(" suspend Stream Suspend called");
+ if (btif_av_cb.sep == SEP_SRC)
+ btif_dispatch_sm_event(BTIF_AV_SUSPEND_STREAM_REQ_EVT, NULL, 0);
+}
+
+/*******************************************************************************
+**
+** Function resume_sink
+**
+** Description Resumes stream in case of A2DP Sink
+**
+** Returns None
+**
+*******************************************************************************/
+void resume_sink()
+{
+ BTIF_TRACE_DEBUG0(" resume Stream called");
+ if (btif_av_cb.sep == SEP_SRC)
+ btif_dispatch_sm_event(BTIF_AV_START_STREAM_REQ_EVT, NULL, 0);
+}
+
+/*******************************************************************************
+**
+** Function audio_focus_status
+**
+** Description Updates audio focus state
+**
+** Returns None
+**
+*******************************************************************************/
+static void audio_focus_status(int state)
+{
+ BTIF_TRACE_DEBUG1(" Audio Focus granted %d",state);
+
+ btif_a2dp_set_audio_focus_state(state);
+}
+
static const btav_interface_t bt_av_interface = {
sizeof(btav_interface_t),
init,
- connect,
+ connect_src,
+ connect_sink,
disconnect,
cleanup,
+ is_src,
+ suspend_sink,
+ resume_sink,
+ audio_focus_status,
+ activate_sink,
};
/*******************************************************************************
BTA_AvEnable(BTA_SEC_AUTHENTICATE, (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_NO_SCO_SSPD),
bte_av_callback);
#endif
- BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0);
+ BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0, bte_av_media_callback);
}
else {
BTA_AvDeregister(btif_av_cb.bta_handle);
** Returns bt_status_t
**
*******************************************************************************/
-static bt_status_t connect_int( bt_bdaddr_t *bd_addr )
+static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
{
CHECK_BTHF_INIT();
int i;
** Returns bt_status_t
**
*******************************************************************************/
-static bt_status_t connect_int( bt_bdaddr_t *bd_addr )
+static bt_status_t connect_int( bt_bdaddr_t *bd_addr, uint16_t uuid )
{
if (is_connected(bd_addr))
return BT_STATUS_BUSY;
#include "btif_av.h"
#include "btif_sm.h"
#include "btif_util.h"
+#ifdef BTA_AVK_INCLUDED
+#include "oi_codec_sbc.h"
+#include "oi_status.h"
+#endif
+#include "stdio.h"
+#include <dlfcn.h>
+#include "bluetoothTrack.h"
+
+//#define DEBUG_MEDIA_AV_FLOW TRUE
+
+#ifdef BTA_AVK_INCLUDED
+OI_CODEC_SBC_DECODER_CONTEXT context;
+OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)];
+OI_INT16 pcmData[15*SBC_MAX_SAMPLES_PER_FRAME*SBC_MAX_CHANNELS];
+void *dlhandle = NULL;
+oi_sbc_decoder_vendor_interface_t *oi_sbc_decode_vnd_if = NULL;
+#endif
/*****************************************************************************
** Constants
*****************************************************************************/
-//#define DEBUG_MEDIA_AV_FLOW TRUE
+#ifndef AUDIO_CHANNEL_OUT_MONO
+#define AUDIO_CHANNEL_OUT_MONO 0x01
+#endif
+
+#ifndef AUDIO_CHANNEL_OUT_STEREO
+#define AUDIO_CHANNEL_OUT_STEREO 0x03
+#endif
/* BTIF media task gki event definition */
#define BTIF_MEDIA_TASK_CMD TASK_MBOX_0_EVT_MASK
#define BTIF_MEDIA_AA_TASK_TIMER_ID TIMER_0
#define BTIF_MEDIA_AV_TASK_TIMER_ID TIMER_1
+#define BTIF_MEDIA_AVK_TASK_TIMER_ID TIMER_2
+
#define BTIF_MEDIA_AA_TASK_TIMER TIMER_0_EVT_MASK
#define BTIF_MEDIA_AV_TASK_TIMER TIMER_1_EVT_MASK
+#define BTIF_MEDIA_AVK_TASK_TIMER TIMER_2_EVT_MASK
+
#define BTIF_MEDIA_TASK_CMD_MBOX TASK_MBOX_0 /* cmd mailbox */
#define BTIF_MEDIA_TASK_DATA_MBOX TASK_MBOX_1 /* data mailbox */
BTIF_MEDIA_FLUSH_AA_TX,
BTIF_MEDIA_FLUSH_AA_RX,
BTIF_MEDIA_AUDIO_FEEDING_INIT,
- BTIF_MEDIA_AUDIO_RECEIVING_INIT
+ BTIF_MEDIA_AUDIO_RECEIVING_INIT,
+ BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE,
+ BTIF_MEDIA_AUDIO_SINK_START_DECODING,
+ BTIF_MEDIA_AUDIO_SINK_STOP_DECODING,
+ BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK
};
enum {
#define BTIF_MEDIA_TIME_TICK (20 * BTIF_MEDIA_NUM_TICK)
#define A2DP_DATA_READ_POLL_MS (BTIF_MEDIA_TIME_TICK / 2)
+#define BTIF_SINK_MEDIA_TIME_TICK (20 * BTIF_MEDIA_NUM_TICK)
+
/* buffer pool */
#define BTIF_MEDIA_AA_POOL_ID GKI_POOL_ID_3
#define RESET_RATE_COUNTER_THRESHOLD_MS 2000
//#define BTIF_MEDIA_VERBOSE_ENABLED
+/* In case of A2DP SINK, we will delay start by 5 AVDTP Packets*/
+#define MAX_A2DP_DELAYED_START_FRAME_COUNT 5
+#define PACKET_PLAYED_PER_TICK_48 8
+#define PACKET_PLAYED_PER_TICK_44 7
+#define PACKET_PLAYED_PER_TICK_32 5
+#define PACKET_PLAYED_PER_TICK_16 3
+
#ifdef BTIF_MEDIA_VERBOSE_ENABLED
#define VERBOSE(fmt, ...) \
/*****************************************************************************
** Data types
*****************************************************************************/
+typedef struct
+{
+ UINT16 num_frames_to_be_processed;
+ UINT16 len;
+ UINT16 offset;
+ UINT16 layer_specific;
+} tBT_SBC_HDR;
typedef struct
{
{
#if (BTA_AV_INCLUDED == TRUE)
BUFFER_Q TxAaQ;
+ BUFFER_Q RxSbcQ;
BOOLEAN is_tx_timer;
+ BOOLEAN is_rx_timer;
UINT16 TxAaMtuSize;
UINT32 timestamp;
UINT8 TxTranscoding;
void* av_sm_hdl;
UINT8 a2dp_cmd_pending; /* we can have max one command pending */
BOOLEAN tx_flush; /* discards any outgoing data when true */
+ BOOLEAN rx_flush; /* discards any incoming data when true */
+ BOOLEAN is_source;
+ UINT8 frames_to_process;
+ BOOLEAN rx_audio_focus_gained;
#endif
} tBTIF_MEDIA_CB;
static void btif_a2dp_ctrl_cb(tUIPC_CH_ID ch_id, tUIPC_EVENT event);
static void btif_a2dp_encoder_update(void);
const char* dump_media_event(UINT16 event);
+#ifdef BTA_AVK_INCLUDED
+void btif_load_decoder_library();
+#endif
+static void btif_media_flush_q(BUFFER_Q *p_q);
+static void btif_media_task_aa_handle_stop_decoding(void );
+static void btif_media_task_aa_rx_flush(void);
+static BOOLEAN btif_media_task_stop_decoding_req(void);
/*****************************************************************************
** Externs
*****************************************************************************/
static void btif_media_task_handle_cmd(BT_HDR *p_msg);
-static void btif_media_task_handle_media(BT_HDR *p_msg);
+static void btif_media_task_handle_media(BT_HDR*p_msg);
+/* Handle incoming media packets A2DP SINK streaming*/
+#ifdef BTA_AVK_INCLUDED
+static void btif_media_task_handle_inc_media(tBT_SBC_HDR*p_msg);
+#endif
#if (BTA_AV_INCLUDED == TRUE)
static void btif_media_send_aa_frame(void);
static void btif_media_task_audio_feeding_init(BT_HDR *p_msg);
static void btif_media_task_aa_tx_flush(BT_HDR *p_msg);
static void btif_media_aa_prep_2_send(UINT8 nb_frame);
+#ifdef BTA_AVK_INCLUDED
+static void btif_media_task_aa_handle_decoder_reset(BT_HDR *p_msg);
+static void btif_media_task_aa_handle_clear_track(void);
#endif
-
-
+static void btif_media_task_aa_handle_start_decoding(void );
+#endif
+extern void btif_av_request_audio_focus(BOOLEAN enable);
+BOOLEAN btif_media_task_start_decoding_req(void);
+BOOLEAN btif_media_task_clear_track(void);
/*****************************************************************************
** Misc helper functions
*****************************************************************************/
CASE_RETURN_STR(BTIF_MEDIA_FLUSH_AA_RX)
CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_INIT)
CASE_RETURN_STR(BTIF_MEDIA_AUDIO_RECEIVING_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_START_DECODING)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_STOP_DECODING)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK)
default:
return "UNKNOWN MEDIA EVENT";
{
UINT8 cmd = 0;
int n;
-
n = UIPC_Read(UIPC_CH_ID_AV_CTRL, NULL, &cmd, 1);
/* detach on ctrl channel means audioflinger process was terminated */
void btif_a2dp_on_idle(void)
{
APPL_TRACE_EVENT0("## ON A2DP IDLE ##");
-
- /* Make sure media task is stopped */
- btif_media_task_stop_aa_req();
+ if(btif_media_cb.is_source)
+ {
+ /* Make sure media task is stopped */
+ btif_media_task_stop_aa_req();
+ }
bta_av_co_init();
+#ifdef BTA_AVK_INCLUDED
+ if (!btif_media_cb.is_source)
+ {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_stop_decoding_req();
+ btif_media_task_clear_track();
+ APPL_TRACE_DEBUG0("Stopped BT track");
+ APPL_TRACE_DEBUG0("Reset to Source role");
+ btif_media_cb.is_source = TRUE;
+ btif_media_cb.rx_audio_focus_gained = BTIF_MEDIA_AUDIOFOCUS_LOSS;
+ }
+#endif
}
/*****************************************************************************
UIPC_Open(UIPC_CH_ID_AV_AUDIO, btif_a2dp_data_cb);
}
+/*******************************************************************************
+ **
+ ** Function btif_media_task_clear_track
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_clear_track(void)
+{
+ BT_HDR *p_buf;
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR))))
+ {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK;
+
+ GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
+ return TRUE;
+}
+/*******************************************************************************
+ **
+ ** Function btif_media_task_stop_decoding_req
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_stop_decoding_req(void)
+{
+ BT_HDR *p_buf;
+
+ if (!btif_media_cb.is_rx_timer)
+ return TRUE; /* if timer is not running no need to send message */
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR))))
+ {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_AUDIO_SINK_STOP_DECODING;
+
+ GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
+ return TRUE;
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_start_decoding_req
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_start_decoding_req(void)
+{
+ BT_HDR *p_buf;
+
+ if(btif_media_cb.is_rx_timer)
+ return FALSE; /* if timer is already running no need to send message */
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR))))
+ {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_AUDIO_SINK_START_DECODING;
+
+ GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
+ return TRUE;
+}
+
+/*****************************************************************************
+**
+** Function btif_reset_decoder
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_reset_decoder(UINT8 *p_av)
+{
+ APPL_TRACE_EVENT0("btif_reset_decoder");
+ APPL_TRACE_DEBUG6("btif_reset_decoder p_codec_info[%x:%x:%x:%x:%x:%x]",
+ p_av[1], p_av[2], p_av[3],
+ p_av[4], p_av[5], p_av[6]);
+
+ tBTIF_MEDIA_SINK_CFG_UPDATE *p_buf;
+ if (NULL == (p_buf = GKI_getbuf(sizeof(tBTIF_MEDIA_SINK_CFG_UPDATE))))
+ {
+ APPL_TRACE_EVENT0("btif_reset_decoder No Buffer ");
+ return;
+ }
+
+ memcpy(p_buf->codec_info,p_av, AVDT_CODEC_SIZE);
+ p_buf->hdr.event = BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE;
+
+ GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
+}
+
/*****************************************************************************
**
** Function btif_a2dp_on_started
void btif_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av)
{
APPL_TRACE_EVENT0("## ON A2DP STOPPED ##");
-
+ if ((!btif_media_cb.is_source)) /* Handling for A2DP SINK cases*/
+ {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_stop_decoding_req();
+ return;
+ }
/* allow using this api for other than suspend */
if (p_av != NULL)
{
void btif_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av)
{
APPL_TRACE_EVENT0("## ON A2DP SUSPENDED ##");
+ if ((!btif_media_cb.is_source))
+ {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_stop_decoding_req();
+ return;
+ }
/* check for status failures */
if (p_av->status != BTA_AV_SUCCESS)
btif_media_task_stop_aa_req();
}
+/* when true media task discards any rx frames */
+void btif_a2dp_set_rx_flush(BOOLEAN enable)
+{
+ APPL_TRACE_EVENT1("## DROP RX %d ##", enable);
+ btif_media_cb.rx_flush = enable;
+}
+
/* when true media task discards any tx frames */
void btif_a2dp_set_tx_flush(BOOLEAN enable)
{
btif_media_cb.tx_flush = enable;
}
+/* when true media task discards any rx frames */
+void btif_a2dp_set_audio_focus_state(btif_media_AudioFocus_state state)
+{
+ APPL_TRACE_EVENT1("## Audio_focus_state Rx %d ##", state);
+ btif_media_cb.rx_audio_focus_gained = state;
+}
+
+#ifdef BTA_AVK_INCLUDED
+/*******************************************************************************
+ **
+ ** Function btif_media_task_avk_handle_timer
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_avk_handle_timer ( void )
+{
+ UINT8 count;
+ tBT_SBC_HDR *p_msg;
+ int num_sbc_frames;
+ int num_frames_to_process;
+
+ count = btif_media_cb.RxSbcQ.count;
+ if (0 == count)
+ {
+ APPL_TRACE_DEBUG0(" QUE EMPTY ");
+ }
+ else
+ {
+ if (btif_media_cb.rx_flush == TRUE)
+ {
+ btif_media_flush_q(&(btif_media_cb.RxSbcQ));
+ return;
+ }
+ if (btif_media_cb.rx_audio_focus_gained == BTIF_MEDIA_AUDIOFOCUS_LOSS_TRANSIENT)
+ {
+ APPL_TRACE_DEBUG0("Received Transient Focus Loss, Ignoring");
+ return;
+ }
+
+ if (btif_media_cb.rx_audio_focus_gained == BTIF_MEDIA_AUDIOFOCUS_LOSS)
+ {
+ /* Send a Audio Focus Request */
+ btif_av_request_audio_focus(TRUE);
+ return;
+ }
+ num_frames_to_process = btif_media_cb.frames_to_process;
+ APPL_TRACE_DEBUG0(" Process Frames + ");
+
+ do
+ {
+ p_msg = (tBT_SBC_HDR *)GKI_getfirst(&(btif_media_cb.RxSbcQ));
+ if (p_msg == NULL)
+ return;
+ num_sbc_frames = p_msg->num_frames_to_be_processed; /* num of frames in Que Packets */
+ APPL_TRACE_DEBUG1(" Frames left in topmost packet %d", num_sbc_frames);
+ APPL_TRACE_DEBUG1(" Remaining frames to process in tick %d", num_frames_to_process);
+ APPL_TRACE_DEBUG1(" Num of Packets in Que %d", btif_media_cb.RxSbcQ.count);
+
+ if ( num_sbc_frames > num_frames_to_process) /* Que Packet has more frames*/
+ {
+ p_msg->num_frames_to_be_processed= num_frames_to_process;
+ btif_media_task_handle_inc_media(p_msg);
+ p_msg->num_frames_to_be_processed = num_sbc_frames - num_frames_to_process;
+ num_frames_to_process = 0;
+ break;
+ }
+ else /* Que packet has less frames */
+ {
+ btif_media_task_handle_inc_media(p_msg);
+ p_msg = (tBT_SBC_HDR *)GKI_dequeue(&(btif_media_cb.RxSbcQ));
+ if( p_msg == NULL )
+ {
+ APPL_TRACE_ERROR0("Insufficient data in que ");
+ break;
+ }
+ num_frames_to_process = num_frames_to_process - p_msg->num_frames_to_be_processed;
+ GKI_freebuf(p_msg);
+ }
+ }while(num_frames_to_process > 0);
+
+ APPL_TRACE_DEBUG0(" Process Frames - ");
+ }
+}
+#endif
+
/*******************************************************************************
**
** Function btif_media_task_aa_handle_timer
#if (BTA_AV_INCLUDED == TRUE)
UIPC_Open(UIPC_CH_ID_AV_CTRL , btif_a2dp_ctrl_cb);
#endif
-
-
+ btif_media_cb.is_source = TRUE;
+ APPL_TRACE_DEBUG0("Reset to Source role");
}
/*******************************************************************************
**
if (event & BTIF_MEDIA_TASK_DATA)
{
+ VERBOSE("================= Received Media Packets %d ===============", event);
/* Process all messages in the queue */
while ((p_msg = (BT_HDR *) GKI_read_mbox(BTIF_MEDIA_TASK_DATA_MBOX)) != NULL)
{
btif_media_task_aa_handle_timer();
}
+ if (event & BTIF_MEDIA_AVK_TASK_TIMER)
+ {
+#ifdef BTA_AVK_INCLUDED
+ /* advance audio timer expiration for a2dp sink */
+ btif_media_task_avk_handle_timer();
+#endif
+ }
+
+
VERBOSE("=============== MEDIA TASK EVENT %d DONE ============", event);
*******************************************************************************/
static void btif_media_flush_q(BUFFER_Q *p_q)
{
- while (GKI_IS_QUEUE_EMPTY(p_q) == FALSE)
+ while (GKI_queue_is_empty(p_q) == FALSE)
{
GKI_freebuf(GKI_dequeue(p_q));
}
case BTIF_MEDIA_UIPC_RX_RDY:
btif_media_task_aa_handle_uipc_rx_rdy();
break;
+ case BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE:
+#ifdef BTA_AVK_INCLUDED
+ btif_media_task_aa_handle_decoder_reset(p_msg);
+#endif
+ break;
+ case BTIF_MEDIA_AUDIO_SINK_START_DECODING:
+ btif_media_task_aa_handle_start_decoding();
+ break;
+ case BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK:
+#ifdef BTA_AVK_INCLUDED
+ btif_media_task_aa_handle_clear_track();
+#endif
+ break;
+ case BTIF_MEDIA_AUDIO_SINK_STOP_DECODING:
+ btif_media_task_aa_handle_stop_decoding();
+ break;
+ case BTIF_MEDIA_FLUSH_AA_RX:
+ btif_media_task_aa_rx_flush();
+ break;
#endif
default:
APPL_TRACE_ERROR1("ERROR in btif_media_task_handle_cmd unknown event %d", p_msg->event);
VERBOSE("btif_media_task_handle_cmd : %s DONE", dump_media_event(p_msg->event));
}
+#ifdef BTA_AVK_INCLUDED
/*******************************************************************************
**
- ** Function btif_media_task_handle_media
+ ** Function btif_media_task_handle_inc_media
**
** Description
**
** Returns void
**
*******************************************************************************/
-static void btif_media_task_handle_media(BT_HDR *p_msg)
+static void btif_media_task_handle_inc_media(tBT_SBC_HDR*p_msg)
{
- APPL_TRACE_ERROR0("ERROR btif_media_task_handle_media: not in use");
-
- GKI_freebuf(p_msg);
-}
-
+ UINT8 *sbc_start_frame = ((UINT8*)(p_msg + 1) + p_msg->offset + 1);
+ int count;
+ UINT32 pcmBytes, availPcmBytes;
+ OI_INT16 *pcmDataPointer = pcmData; /*Will be overwritten on next packet receipt*/
+ OI_STATUS status;
+ int num_sbc_frames = p_msg->num_frames_to_be_processed;
+ UINT32 sbc_frame_len = p_msg->len - 1;
+ int retwriteAudioTrack = 0;
+ availPcmBytes = 2*sizeof(pcmData);
+
+ if ((btif_media_cb.is_source) || (btif_media_cb.rx_flush))
+ {
+ APPL_TRACE_DEBUG0(" State Changed happened in this tick ");
+ return;
+ }
+ APPL_TRACE_DEBUG2("Number of sbc frames %d, frame_len %d", num_sbc_frames, sbc_frame_len);
+ for(count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++)
+ {
+ pcmBytes = availPcmBytes;
+ status = oi_sbc_decode_vnd_if->OI_CODEC_SBC_DecodeFrame(&context, (const OI_BYTE**)&sbc_start_frame,
+ &sbc_frame_len, pcmDataPointer, &pcmBytes);
+ if (!OI_SUCCESS(status)) {
+ APPL_TRACE_ERROR1("Decoding failure: %d\n", status);
+ break;
+ }
+ availPcmBytes -= pcmBytes;
+ pcmDataPointer += pcmBytes/2;
+ p_msg->offset += (p_msg->len - 1) - sbc_frame_len;
+ p_msg->len = sbc_frame_len + 1;
+ }
+ retwriteAudioTrack = btWriteData((void*)pcmData, (2*sizeof(pcmData) - availPcmBytes));
+}
+#endif
+/*******************************************************************************
+ **
+ ** Function btif_media_task_handle_media
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_handle_media(BT_HDR*p_msg)
+{
+ APPL_TRACE_DEBUG0(" btif_media_task_handle_media ");
+ GKI_freebuf(p_msg);
+}
#if (BTA_AV_INCLUDED == TRUE)
/*******************************************************************************
**
GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
return TRUE;
}
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush_req
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_aa_rx_flush_req(void)
+{
+ BT_HDR *p_buf;
+
+ if (GKI_queue_is_empty(&(btif_media_cb.RxSbcQ))== TRUE) /* Que is already empty */
+ return TRUE;
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR))))
+ {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_FLUSH_AA_RX;
+
+ GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
+ return TRUE;
+}
/*******************************************************************************
**
GKI_send_msg(BT_MEDIA_TASK, BTIF_MEDIA_TASK_CMD_MBOX, p_buf);
return TRUE;
}
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_rx_flush(void)
+{
+ /* Flush all enqueued GKI SBC buffers (encoded) */
+ APPL_TRACE_DEBUG0("btif_media_task_aa_rx_flush");
+
+ btif_media_flush_q(&(btif_media_cb.RxSbcQ));
+}
+
/*******************************************************************************
**
}
}
+int a2dp_get_track_frequency(UINT8 frequency) {
+ int freq = 48000;
+ switch (frequency) {
+ case A2D_SBC_IE_SAMP_FREQ_16:
+ freq = 16000;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_32:
+ freq = 32000;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_44:
+ freq = 44100;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_48:
+ freq = 48000;
+ break;
+ }
+ return freq;
+}
+
+int a2dp_get_track_channel_type(UINT8 channeltype) {
+ int channel = AUDIO_CHANNEL_OUT_MONO;
+ switch (channeltype) {
+ case A2D_SBC_IE_CH_MD_MONO:
+ channel = AUDIO_CHANNEL_OUT_MONO;
+ break;
+ case A2D_SBC_IE_CH_MD_DUAL:
+ case A2D_SBC_IE_CH_MD_STEREO:
+ case A2D_SBC_IE_CH_MD_JOINT:
+ channel = AUDIO_CHANNEL_OUT_STEREO;
+ break;
+ }
+ return channel;
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_handle_stop_decoding
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_handle_stop_decoding(void )
+{
+ btif_media_cb.is_rx_timer = FALSE;
+ GKI_stop_timer(BTIF_MEDIA_AVK_TASK_TIMER_ID);
+ btPauseTrack();
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_handle_start_decoding
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_handle_start_decoding(void )
+{
+ if(btif_media_cb.is_rx_timer == TRUE)
+ return;
+ btStartTrack();
+ btif_media_cb.is_rx_timer = TRUE;
+ GKI_start_timer(BTIF_MEDIA_AVK_TASK_TIMER_ID, GKI_MS_TO_TICKS(BTIF_SINK_MEDIA_TIME_TICK), TRUE);
+}
+
+#ifdef BTA_AVK_INCLUDED
+
+static void btif_media_task_aa_handle_clear_track (void)
+{
+ APPL_TRACE_DEBUG0("btif_media_task_aa_handle_clear_track");
+ btStopTrack();
+ btDeleteTrack();
+ if (dlhandle)
+ {
+ APPL_TRACE_DEBUG0("Unload Decoder lib");
+ dlclose(dlhandle);
+ dlhandle = NULL;
+ oi_sbc_decode_vnd_if = NULL;
+ }
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_handle_decoder_reset
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_handle_decoder_reset(BT_HDR *p_msg)
+{
+ tBTIF_MEDIA_SINK_CFG_UPDATE *p_buf = (tBTIF_MEDIA_SINK_CFG_UPDATE*) p_msg;
+ tA2D_STATUS a2d_status;
+ tA2D_SBC_CIE sbc_cie;
+ OI_STATUS status;
+ UINT32 freq_multiple; /* frequency multiple for 20ms of data */
+ UINT32 num_blocks;
+ UINT32 num_subbands;
+ UINT32 num_channel;
+
+ APPL_TRACE_DEBUG6("btif_media_task_aa_handle_decoder_reset p_codec_info[%x:%x:%x:%x:%x:%x]",
+ p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3],
+ p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]);
+
+ a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE);
+ if (a2d_status != A2D_SUCCESS)
+ {
+ APPL_TRACE_ERROR1("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d", a2d_status);
+ return;
+ }
+ btif_media_cb.is_source = FALSE;
+ btif_media_cb.rx_flush = FALSE;
+ APPL_TRACE_DEBUG0("Reset to sink role");
+ btif_load_decoder_library();
+ status = oi_sbc_decode_vnd_if->OI_CODEC_SBC_DecoderReset(&context, contextData, sizeof(contextData), 2, 2, FALSE);
+ if (!OI_SUCCESS(status)) {
+ APPL_TRACE_ERROR1("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status);
+ }
+ APPL_TRACE_DEBUG0("A2dpSink: Crate Track");
+ if (btCreateTrack(a2dp_get_track_frequency(sbc_cie.samp_freq), a2dp_get_track_channel_type(sbc_cie.ch_mode)) == -1) {
+ APPL_TRACE_ERROR0("A2dpSink: Track creation fails!!!");
+ return;
+ }
+
+ switch(sbc_cie.samp_freq)
+ {
+ case A2D_SBC_IE_SAMP_FREQ_16:
+ APPL_TRACE_DEBUG1("\tsamp_freq:%d (16000)", sbc_cie.samp_freq);
+ freq_multiple = 16*20;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_32:
+ APPL_TRACE_DEBUG1("\tsamp_freq:%d (32000)", sbc_cie.samp_freq);
+ freq_multiple = 32*20;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_44:
+ APPL_TRACE_DEBUG1("\tsamp_freq:%d (44100)", sbc_cie.samp_freq);
+ freq_multiple = 441*2;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_48:
+ APPL_TRACE_DEBUG1("\tsamp_freq:%d (48000)", sbc_cie.samp_freq);
+ freq_multiple = 48*20;
+ break;
+ default:
+ APPL_TRACE_DEBUG0(" Unknown Frequency ");
+ break;
+ }
+
+ switch(sbc_cie.ch_mode)
+ {
+ case A2D_SBC_IE_CH_MD_MONO:
+ APPL_TRACE_DEBUG1("\tch_mode:%d (Mono)", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_DUAL:
+ APPL_TRACE_DEBUG1("\tch_mode:%d (DUAL)", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_STEREO:
+ APPL_TRACE_DEBUG1("\tch_mode:%d (STEREO)", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_JOINT:
+ APPL_TRACE_DEBUG1("\tch_mode:%d (JOINT)", sbc_cie.ch_mode);
+ break;
+ default:
+ APPL_TRACE_DEBUG0(" Unknown Mode ");
+ break;
+ }
+
+ switch(sbc_cie.block_len)
+ {
+ case A2D_SBC_IE_BLOCKS_4:
+ APPL_TRACE_DEBUG1("\tblock_len:%d (4)", sbc_cie.block_len);
+ num_blocks = 4;
+ break;
+ case A2D_SBC_IE_BLOCKS_8:
+ APPL_TRACE_DEBUG1("\tblock_len:%d (8)", sbc_cie.block_len);
+ num_blocks = 8;
+ break;
+ case A2D_SBC_IE_BLOCKS_12:
+ APPL_TRACE_DEBUG1("\tblock_len:%d (12)", sbc_cie.block_len);
+ num_blocks = 12;
+ break;
+ case A2D_SBC_IE_BLOCKS_16:
+ APPL_TRACE_DEBUG1("\tblock_len:%d (16)", sbc_cie.block_len);
+ num_blocks = 16;
+ break;
+ default:
+ APPL_TRACE_DEBUG0(" Unknown BlockLen ");
+ break;
+ }
+
+ switch(sbc_cie.num_subbands)
+ {
+ case A2D_SBC_IE_SUBBAND_4:
+ APPL_TRACE_DEBUG1("\tnum_subbands:%d (4)", sbc_cie.num_subbands);
+ num_subbands = 4;
+ break;
+ case A2D_SBC_IE_SUBBAND_8:
+ APPL_TRACE_DEBUG1("\tnum_subbands:%d (8)", sbc_cie.num_subbands);
+ num_subbands = 8;
+ break;
+ default:
+ APPL_TRACE_DEBUG0(" Unknown SubBands ");
+ break;
+ }
+
+ switch(sbc_cie.alloc_mthd)
+ {
+ case A2D_SBC_IE_ALLOC_MD_S:
+ APPL_TRACE_DEBUG1("\talloc_mthd:%d (SNR)", sbc_cie.alloc_mthd);
+ break;
+ case A2D_SBC_IE_ALLOC_MD_L:
+ APPL_TRACE_DEBUG1("\talloc_mthd:%d (Loudness)", sbc_cie.alloc_mthd);
+ break;
+ default:
+ APPL_TRACE_DEBUG0(" Unknown Allocation Method");
+ break;
+ }
+
+ APPL_TRACE_DEBUG2("\tBit pool Min:%d Max:%d", sbc_cie.min_bitpool, sbc_cie.max_bitpool);
+
+ btif_media_cb.frames_to_process = ((freq_multiple)/(num_blocks*num_subbands)) + 1;
+ APPL_TRACE_DEBUG1(" Frames to be processed in 20 ms %d",btif_media_cb.frames_to_process);
+}
+#endif
+
/*******************************************************************************
**
** Function btif_media_task_feeding_state_reset
/*******************************************************************************
**
+ ** Function btif_media_sink_enque_buf
+ **
+ ** Description This function is called by the av_co to fill A2DP Sink Queue
+ **
+ **
+ ** Returns size of the queue
+ *******************************************************************************/
+UINT8 btif_media_sink_enque_buf(BT_HDR *p_pkt)
+{
+ tBT_SBC_HDR *p_msg;
+
+ if(btif_media_cb.rx_flush == TRUE) /* Flush enabled, do not enque*/
+ return btif_media_cb.RxSbcQ.count;
+ if(btif_media_cb.RxSbcQ.count == MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ)
+ {
+ GKI_freebuf(GKI_dequeue(&(btif_media_cb.RxSbcQ)));
+ }
+
+ BTIF_TRACE_VERBOSE0("btif_media_sink_enque_buf + ");
+ /* allocate and Queue this buffer */
+ if ((p_msg = (tBT_SBC_HDR *) GKI_getbuf(sizeof(tBT_SBC_HDR) +
+ p_pkt->offset+ p_pkt->len)) != NULL)
+ {
+ memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len));
+ p_msg->num_frames_to_be_processed = (*((UINT8*)(p_msg + 1) + p_msg->offset)) & 0x0f;
+ BTIF_TRACE_VERBOSE1("btif_media_sink_enque_buf + ", p_msg->num_frames_to_be_processed);
+ GKI_enqueue(&(btif_media_cb.RxSbcQ), p_msg);
+ if(btif_media_cb.RxSbcQ.count == MAX_A2DP_DELAYED_START_FRAME_COUNT)
+ {
+ BTIF_TRACE_DEBUG0(" Initiate Decoding ");
+ btif_media_task_start_decoding_req();
+ }
+ }
+ else
+ {
+ /* let caller deal with a failed allocation */
+ BTIF_TRACE_VERBOSE0("btif_media_sink_enque_buf No Buffer left - ");
+ }
+ return btif_media_cb.RxSbcQ.count;
+}
+
+/*******************************************************************************
+ **
** Function btif_media_aa_readbuf
**
** Description This function is called by the av_co to get the next buffer to send
APPL_TRACE_DEBUG2("\tBit pool Min:%d Max:%d", sbc_cie.min_bitpool, sbc_cie.max_bitpool);
}
+
+#ifdef BTA_AVK_INCLUDED
+void btif_load_decoder_library()
+{
+ dlhandle = dlopen("liboi_sbc_decoder.so", RTLD_NOW);
+ APPL_TRACE_DEBUG0("Load decoder library");
+ if (!dlhandle)
+ {
+ APPL_TRACE_ERROR0("!!! Failed to load oi_sbc_decoder.so !!!");
+ return;
+ }
+
+ oi_sbc_decode_vnd_if = (oi_sbc_decoder_vendor_interface_t *) dlsym(dlhandle, "OI_SBC_DECODER_VENDOR_LIB_INTERFACE");
+ if (!oi_sbc_decode_vnd_if)
+ {
+ APPL_TRACE_ERROR0("!!! Failed to get oi sbc decode vendor interface !!!");
+ return;
+ }
+}
+#endif
return BT_STATUS_SUCCESS;
p_head->busy = true;
- return p_head->connect_cb(&p_head->bda);
+ return p_head->connect_cb(&p_head->bda, p_head->uuid);
}
static void queue_int_handle_evt(UINT16 event, char *p_param) {
#define BTA_GATT_INCLUDED TRUE
#endif
+/* defined BTA_AVK_INCLUDED in Android.mk file based on target selected*/
+
#ifndef BTA_DISABLE_DELAY
#define BTA_DISABLE_DELAY 200 /* in milliseconds */
#endif
../btif/src/btif_gatt_test.c \
../btif/src/btif_config.c \
../btif/src/btif_config_util.cpp \
- ../btif/src/btif_profile_queue.c
+ ../btif/src/btif_profile_queue.c \
+ ../btif/src/bluetoothTrack.cpp
# callouts
LOCAL_SRC_FILES+= \
$(LOCAL_PATH)/../audio_a2dp_hw \
$(LOCAL_PATH)/../utils/include \
$(bdroid_C_INCLUDES) \
+ $(TOP)/frameworks/av/include/media \
external/tinyxml2
LOCAL_CFLAGS += -DBUILDCFG $(bdroid_CFLAGS) -Werror -Wno-error=maybe-uninitialized -Wno-error=uninitialized -Wno-error=unused-parameter
avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT *) &p_buf);
}
else
+ {
GKI_freebuf(p_buf);
+ AVDT_TRACE_ERROR0(" avdt_ad_tc_data_ind buffer freed");
+ }
}
}
/*******************************************************************************
**
+** Function AVDT_SINK_Activate
+**
+** Description Activate SEP of A2DP Sink. In Use parameter is adjusted.
+** In Use will be made false in case of activation. A2DP SRC
+** will receive in_use as false and can open A2DP Sink
+** connection
+**
+** Returns void.
+**
+*******************************************************************************/
+void AVDT_SINK_Activate()
+{
+ tAVDT_SCB *p_scb = &avdt_cb.scb[0];
+ int i;
+ AVDT_TRACE_DEBUG0("AVDT_SINK_Activate");
+ /* for all allocated scbs */
+ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++)
+ {
+ if ((p_scb->allocated) && (p_scb->cs.tsep == AVDT_TSEP_SNK))
+ {
+ AVDT_TRACE_DEBUG0("AVDT_SINK_Activate found scb");
+ p_scb->sink_activated = TRUE;
+ /* update in_use */
+ p_scb->in_use = FALSE;
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function AVDT_SINK_Deactivate
+**
+** Description Deactivate SEP of A2DP Sink. In Use parameter is adjusted.
+** In Use will be made TRUE in case of activation. A2DP SRC
+** will receive in_use as true and will not open A2DP Sink
+** connection
+**
+** Returns void.
+**
+*******************************************************************************/
+void AVDT_SINK_Deactivate()
+{
+ tAVDT_SCB *p_scb = &avdt_cb.scb[0];
+ int i;
+ AVDT_TRACE_DEBUG0("AVDT_SINK_Deactivate");
+ /* for all allocated scbs */
+ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++)
+ {
+ if ((p_scb->allocated) && (p_scb->cs.tsep == AVDT_TSEP_SNK))
+ {
+ AVDT_TRACE_DEBUG0("AVDT_SINK_Deactivate, found scb");
+ p_scb->sink_activated = FALSE;
+ /* update in_use */
+ p_scb->in_use = TRUE;
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
** Function AVDT_CreateStream
**
** Description Create a stream endpoint. After a stream endpoint is
UINT16 media_seq; /* media packet sequence number */
BOOLEAN allocated; /* whether scb is allocated or unused */
BOOLEAN in_use; /* whether stream being used by peer */
+ BOOLEAN sink_activated; /* A2DP Sink activated/de-activated from Application */
UINT8 role; /* initiator/acceptor role in current procedure */
BOOLEAN remove; /* whether CB is marked for removal */
UINT8 state; /* state machine state */
memset(p_scb,0,sizeof(tAVDT_SCB));
p_scb->allocated = TRUE;
p_scb->p_ccb = NULL;
+
+ /* initialize sink as activated */
+ if (p_cs->tsep == AVDT_TSEP_SNK)
+ {
+ p_scb->sink_activated = TRUE;
+ }
+
memcpy(&p_scb->cs, p_cs, sizeof(tAVDT_CS));
#if AVDT_MULTIPLEXING == TRUE
/* initialize fragments gueue */
UNUSED(p_scb);
GKI_freebuf(p_data->p_pkt);
- AVDT_TRACE_WARNING0("Dropped incoming media packet");
+ AVDT_TRACE_ERROR0(" avdt_scb_drop_pkt Dropped incoming media packet");
}
/*******************************************************************************
p_scb->peer_seid = p_data->msg.config_cmd.int_seid;
memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG));
/* call app callback */
- (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), /* handle of scb- which is same as sep handle of bta_av_cb.p_scb*/
p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
AVDT_CONFIG_IND_EVT,
(tAVDT_CTRL *) &p_data->msg.config_cmd);
{
UNUSED(p_data);
- p_scb->in_use = FALSE;
+ if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && (!p_scb->sink_activated))
+ {
+ p_scb->in_use = TRUE;
+ }
+ else
+ {
+ p_scb->in_use = FALSE;
+ }
p_scb->p_ccb = NULL;
p_scb->peer_seid = 0;
}
*******************************************************************************/
AVDT_API extern void AVDT_Deregister(void);
+
+/*******************************************************************************
+**
+** Function AVDT_SINK_Activate
+**
+** Description Activate SEP of A2DP Sink. In Use parameter is adjusted.
+** In Use will be made false in case of activation. A2DP SRC
+** will receive in_use as false and can open A2DP Sink
+** connection
+**
+** Returns void
+**
+*******************************************************************************/
+AVDT_API extern void AVDT_SINK_Activate(void);
+
+/*******************************************************************************
+**
+** Function AVDT_SINK_Deactivate
+**
+** Description Deactivate SEP of A2DP Sink. In Use parameter is adjusted.
+** In Use will be made TRUE in case of activation. A2DP SRC
+** will receive in_use as true and will not open A2DP Sink
+** connection
+**
+** Returns void.
+**
+*******************************************************************************/
+AVDT_API extern void AVDT_SINK_Deactivate(void);
+
/*******************************************************************************
**
** Function AVDT_CreateStream