/****************************************************************************** * * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2009-2012 Broadcom Corporation * * 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. * ******************************************************************************/ #define LOG_TAG "bt_btif_a2dp_sink" #include <string.h> #include "bt_common.h" #include "btif_a2dp.h" #include "btif_a2dp_sink.h" #include "btif_av.h" #include "btif_av_co.h" #include "btif_avrcp_audio_track.h" #include "btif_util.h" #include "osi/include/fixed_queue.h" #include "osi/include/log.h" #include "osi/include/osi.h" #include "osi/include/thread.h" #include "oi_codec_sbc.h" #include "oi_status.h" /** * The receiving queue buffer size. */ #define MAX_INPUT_A2DP_FRAME_QUEUE_SZ (MAX_PCM_FRAME_NUM_PER_TICK * 2) #define BTIF_SINK_MEDIA_TIME_TICK_MS 20 /* In case of A2DP Sink, we will delay start by 5 AVDTP Packets */ #define MAX_A2DP_DELAYED_START_FRAME_COUNT 5 enum { BTIF_A2DP_SINK_STATE_OFF, BTIF_A2DP_SINK_STATE_STARTING_UP, BTIF_A2DP_SINK_STATE_RUNNING, BTIF_A2DP_SINK_STATE_SHUTTING_DOWN }; /* BTIF Media Sink command event definition */ enum { BTIF_MEDIA_SINK_DECODER_UPDATE = 1, BTIF_MEDIA_SINK_CLEAR_TRACK, BTIF_MEDIA_SINK_SET_FOCUS_STATE, BTIF_MEDIA_SINK_AUDIO_RX_FLUSH }; typedef struct { BT_HDR hdr; uint8_t codec_info[AVDT_CODEC_SIZE]; } tBTIF_MEDIA_SINK_DECODER_UPDATE; typedef struct { BT_HDR hdr; btif_a2dp_sink_focus_state_t focus_state; } tBTIF_MEDIA_SINK_FOCUS_UPDATE; typedef struct { uint16_t num_frames_to_be_processed; uint16_t len; uint16_t offset; uint16_t layer_specific; } tBT_SBC_HDR; /* BTIF A2DP Sink control block */ typedef struct { thread_t* worker_thread; fixed_queue_t* cmd_msg_queue; fixed_queue_t* rx_audio_queue; bool rx_flush; /* discards any incoming data when true */ alarm_t* decode_alarm; uint8_t frames_to_process; tA2DP_SAMPLE_RATE sample_rate; tA2DP_CHANNEL_COUNT channel_count; btif_a2dp_sink_focus_state_t rx_focus_state; /* audio focus state */ void* audio_track; } tBTIF_A2DP_SINK_CB; static tBTIF_A2DP_SINK_CB btif_a2dp_sink_cb; static int btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF; static OI_CODEC_SBC_DECODER_CONTEXT btif_a2dp_sink_context; static uint32_t btif_a2dp_sink_context_data[CODEC_DATA_WORDS( 2, SBC_CODEC_FAST_FILTER_BUFFERS)]; static int16_t btif_a2dp_sink_pcm_data[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS]; static void btif_a2dp_sink_startup_delayed(void* context); static void btif_a2dp_sink_shutdown_delayed(void* context); static void btif_a2dp_sink_command_ready(fixed_queue_t* queue, void* context); static void btif_a2dp_sink_audio_handle_stop_decoding(void); static void btif_decode_alarm_cb(void* context); static void btif_a2dp_sink_audio_handle_start_decoding(void); static void btif_a2dp_sink_avk_handle_timer(UNUSED_ATTR void* context); static void btif_a2dp_sink_audio_rx_flush_req(void); /* Handle incoming media packets A2DP SINK streaming */ static void btif_a2dp_sink_handle_inc_media(tBT_SBC_HDR* p_msg); static void btif_a2dp_sink_decoder_update_event( tBTIF_MEDIA_SINK_DECODER_UPDATE* p_buf); static void btif_a2dp_sink_clear_track_event(void); static void btif_a2dp_sink_set_focus_state_event( btif_a2dp_sink_focus_state_t state); static void btif_a2dp_sink_audio_rx_flush_event(void); static void btif_a2dp_sink_clear_track_event_req(void); UNUSED_ATTR static const char* dump_media_event(uint16_t event) { switch (event) { CASE_RETURN_STR(BTIF_MEDIA_SINK_DECODER_UPDATE) CASE_RETURN_STR(BTIF_MEDIA_SINK_CLEAR_TRACK) CASE_RETURN_STR(BTIF_MEDIA_SINK_SET_FOCUS_STATE) CASE_RETURN_STR(BTIF_MEDIA_SINK_AUDIO_RX_FLUSH) default: break; } return "UNKNOWN A2DP SINK EVENT"; } bool btif_a2dp_sink_startup(void) { if (btif_a2dp_sink_state != BTIF_A2DP_SINK_STATE_OFF) { APPL_TRACE_ERROR("%s: A2DP Sink media task already running", __func__); return false; } memset(&btif_a2dp_sink_cb, 0, sizeof(btif_a2dp_sink_cb)); btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_STARTING_UP; APPL_TRACE_EVENT("## A2DP SINK START MEDIA THREAD ##"); /* Start A2DP Sink media task */ btif_a2dp_sink_cb.worker_thread = thread_new("btif_a2dp_sink_worker_thread"); if (btif_a2dp_sink_cb.worker_thread == NULL) { APPL_TRACE_ERROR("%s: unable to start up media thread", __func__); btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF; return false; } btif_a2dp_sink_cb.rx_focus_state = BTIF_A2DP_SINK_FOCUS_NOT_GRANTED; btif_a2dp_sink_cb.audio_track = NULL; btif_a2dp_sink_cb.rx_audio_queue = fixed_queue_new(SIZE_MAX); btif_a2dp_sink_cb.cmd_msg_queue = fixed_queue_new(SIZE_MAX); fixed_queue_register_dequeue( btif_a2dp_sink_cb.cmd_msg_queue, thread_get_reactor(btif_a2dp_sink_cb.worker_thread), btif_a2dp_sink_command_ready, NULL); APPL_TRACE_EVENT("## A2DP SINK MEDIA THREAD STARTED ##"); /* Schedule the rest of the startup operations */ thread_post(btif_a2dp_sink_cb.worker_thread, btif_a2dp_sink_startup_delayed, NULL); return true; } static void btif_a2dp_sink_startup_delayed(UNUSED_ATTR void* context) { raise_priority_a2dp(TASK_HIGH_MEDIA); btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_RUNNING; } void btif_a2dp_sink_shutdown(void) { if ((btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) || (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_SHUTTING_DOWN)) { return; } /* Make sure no channels are restarted while shutting down */ btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_SHUTTING_DOWN; APPL_TRACE_EVENT("## A2DP SINK STOP MEDIA THREAD ##"); // Stop the timer alarm_free(btif_a2dp_sink_cb.decode_alarm); btif_a2dp_sink_cb.decode_alarm = NULL; // Exit the thread fixed_queue_free(btif_a2dp_sink_cb.cmd_msg_queue, NULL); btif_a2dp_sink_cb.cmd_msg_queue = NULL; thread_post(btif_a2dp_sink_cb.worker_thread, btif_a2dp_sink_shutdown_delayed, NULL); thread_free(btif_a2dp_sink_cb.worker_thread); btif_a2dp_sink_cb.worker_thread = NULL; } static void btif_a2dp_sink_shutdown_delayed(UNUSED_ATTR void* context) { fixed_queue_free(btif_a2dp_sink_cb.rx_audio_queue, NULL); btif_a2dp_sink_cb.rx_audio_queue = NULL; btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF; } tA2DP_SAMPLE_RATE btif_a2dp_sink_get_sample_rate(void) { return btif_a2dp_sink_cb.sample_rate; } tA2DP_CHANNEL_COUNT btif_a2dp_sink_get_channel_count(void) { return btif_a2dp_sink_cb.channel_count; } static void btif_a2dp_sink_command_ready(fixed_queue_t* queue, UNUSED_ATTR void* context) { BT_HDR* p_msg = (BT_HDR*)fixed_queue_dequeue(queue); LOG_VERBOSE(LOG_TAG, "%s: event %d %s", __func__, p_msg->event, dump_media_event(p_msg->event)); switch (p_msg->event) { case BTIF_MEDIA_SINK_DECODER_UPDATE: btif_a2dp_sink_decoder_update_event( (tBTIF_MEDIA_SINK_DECODER_UPDATE*)p_msg); break; case BTIF_MEDIA_SINK_CLEAR_TRACK: btif_a2dp_sink_clear_track_event(); break; case BTIF_MEDIA_SINK_SET_FOCUS_STATE: { btif_a2dp_sink_focus_state_t state = ((tBTIF_MEDIA_SINK_FOCUS_UPDATE*)p_msg)->focus_state; btif_a2dp_sink_set_focus_state_event(state); break; } case BTIF_MEDIA_SINK_AUDIO_RX_FLUSH: btif_a2dp_sink_audio_rx_flush_event(); break; default: APPL_TRACE_ERROR("ERROR in %s unknown event %d", __func__, p_msg->event); break; } osi_free(p_msg); LOG_VERBOSE(LOG_TAG, "%s: %s DONE", __func__, dump_media_event(p_msg->event)); } void btif_a2dp_sink_update_decoder(const uint8_t* p_codec_info) { tBTIF_MEDIA_SINK_DECODER_UPDATE* p_buf = reinterpret_cast<tBTIF_MEDIA_SINK_DECODER_UPDATE*>( osi_malloc(sizeof(tBTIF_MEDIA_SINK_DECODER_UPDATE))); APPL_TRACE_EVENT("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__, 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_buf->codec_info, p_codec_info, AVDT_CODEC_SIZE); p_buf->hdr.event = BTIF_MEDIA_SINK_DECODER_UPDATE; fixed_queue_enqueue(btif_a2dp_sink_cb.cmd_msg_queue, p_buf); } void btif_a2dp_sink_on_idle(void) { if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return; btif_a2dp_sink_audio_handle_stop_decoding(); btif_a2dp_sink_clear_track_event_req(); APPL_TRACE_DEBUG("Stopped BT track"); } void btif_a2dp_sink_on_stopped(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) { if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return; btif_a2dp_sink_audio_handle_stop_decoding(); } void btif_a2dp_sink_on_suspended(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) { if (btif_a2dp_sink_state == BTIF_A2DP_SINK_STATE_OFF) return; btif_a2dp_sink_audio_handle_stop_decoding(); } static void btif_a2dp_sink_audio_handle_stop_decoding(void) { btif_a2dp_sink_cb.rx_flush = true; btif_a2dp_sink_audio_rx_flush_req(); alarm_free(btif_a2dp_sink_cb.decode_alarm); btif_a2dp_sink_cb.decode_alarm = NULL; #ifndef OS_GENERIC BtifAvrcpAudioTrackPause(btif_a2dp_sink_cb.audio_track); #endif } static void btif_decode_alarm_cb(UNUSED_ATTR void* context) { if (btif_a2dp_sink_cb.worker_thread != NULL) { thread_post(btif_a2dp_sink_cb.worker_thread, btif_a2dp_sink_avk_handle_timer, NULL); } } static void btif_a2dp_sink_clear_track_event(void) { APPL_TRACE_DEBUG("%s", __func__); #ifndef OS_GENERIC BtifAvrcpAudioTrackStop(btif_a2dp_sink_cb.audio_track); BtifAvrcpAudioTrackDelete(btif_a2dp_sink_cb.audio_track); #endif btif_a2dp_sink_cb.audio_track = NULL; } static void btif_a2dp_sink_audio_handle_start_decoding(void) { if (btif_a2dp_sink_cb.decode_alarm != NULL) return; // Already started decoding #ifndef OS_GENERIC BtifAvrcpAudioTrackStart(btif_a2dp_sink_cb.audio_track); #endif btif_a2dp_sink_cb.decode_alarm = alarm_new_periodic("btif.a2dp_sink_decode"); if (btif_a2dp_sink_cb.decode_alarm == NULL) { LOG_ERROR(LOG_TAG, "%s: unable to allocate decode alarm", __func__); return; } alarm_set(btif_a2dp_sink_cb.decode_alarm, BTIF_SINK_MEDIA_TIME_TICK_MS, btif_decode_alarm_cb, NULL); } static void btif_a2dp_sink_handle_inc_media(tBT_SBC_HDR* p_msg) { uint8_t* sbc_start_frame = ((uint8_t*)(p_msg + 1) + p_msg->offset + 1); int count; uint32_t pcmBytes, availPcmBytes; int16_t* pcmDataPointer = btif_a2dp_sink_pcm_data; /* Will be overwritten on next packet receipt */ OI_STATUS status; int num_sbc_frames = p_msg->num_frames_to_be_processed; uint32_t sbc_frame_len = p_msg->len - 1; availPcmBytes = sizeof(btif_a2dp_sink_pcm_data); if ((btif_av_get_peer_sep() == AVDT_TSEP_SNK) || (btif_a2dp_sink_cb.rx_flush)) { APPL_TRACE_DEBUG("State Changed happened in this tick"); return; } APPL_TRACE_DEBUG("%s Number of SBC frames %d, frame_len %d", __func__, num_sbc_frames, sbc_frame_len); for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count++) { pcmBytes = availPcmBytes; status = OI_CODEC_SBC_DecodeFrame( &btif_a2dp_sink_context, (const OI_BYTE**)&sbc_start_frame, (uint32_t*)&sbc_frame_len, (int16_t*)pcmDataPointer, (uint32_t*)&pcmBytes); if (!OI_SUCCESS(status)) { APPL_TRACE_ERROR("%s: Decoding failure: %d", __func__, status); break; } availPcmBytes -= pcmBytes; pcmDataPointer += pcmBytes / 2; p_msg->offset += (p_msg->len - 1) - sbc_frame_len; p_msg->len = sbc_frame_len + 1; } #ifndef OS_GENERIC BtifAvrcpAudioTrackWriteData( btif_a2dp_sink_cb.audio_track, (void*)btif_a2dp_sink_pcm_data, (sizeof(btif_a2dp_sink_pcm_data) - availPcmBytes)); #endif } static void btif_a2dp_sink_avk_handle_timer(UNUSED_ATTR void* context) { tBT_SBC_HDR* p_msg; int num_sbc_frames; int num_frames_to_process; if (fixed_queue_is_empty(btif_a2dp_sink_cb.rx_audio_queue)) { APPL_TRACE_DEBUG("%s: empty queue", __func__); return; } /* Don't do anything in case of focus not granted */ if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_NOT_GRANTED) { APPL_TRACE_DEBUG("%s: skipping frames since focus is not present", __func__); return; } /* Play only in BTIF_A2DP_SINK_FOCUS_GRANTED case */ if (btif_a2dp_sink_cb.rx_flush) { fixed_queue_flush(btif_a2dp_sink_cb.rx_audio_queue, osi_free); return; } num_frames_to_process = btif_a2dp_sink_cb.frames_to_process; APPL_TRACE_DEBUG(" Process Frames + "); do { p_msg = (tBT_SBC_HDR*)fixed_queue_try_peek_first( btif_a2dp_sink_cb.rx_audio_queue); if (p_msg == NULL) return; /* Number of frames in queue packets */ num_sbc_frames = p_msg->num_frames_to_be_processed; APPL_TRACE_DEBUG("Frames left in topmost packet %d", num_sbc_frames); APPL_TRACE_DEBUG("Remaining frames to process in tick %d", num_frames_to_process); APPL_TRACE_DEBUG("Number of packets in queue %d", fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue)); if (num_sbc_frames > num_frames_to_process) { /* Queue packet has more frames */ p_msg->num_frames_to_be_processed = num_frames_to_process; btif_a2dp_sink_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; } /* Queue packet has less frames */ btif_a2dp_sink_handle_inc_media(p_msg); p_msg = (tBT_SBC_HDR*)fixed_queue_try_dequeue(btif_a2dp_sink_cb.rx_audio_queue); if (p_msg == NULL) { APPL_TRACE_ERROR("Insufficient data in queue"); break; } num_frames_to_process = num_frames_to_process - p_msg->num_frames_to_be_processed; osi_free(p_msg); } while (num_frames_to_process > 0); APPL_TRACE_DEBUG("Process Frames - "); } /* when true media task discards any rx frames */ void btif_a2dp_sink_set_rx_flush(bool enable) { APPL_TRACE_EVENT("## DROP RX %d ##", enable); btif_a2dp_sink_cb.rx_flush = enable; } static void btif_a2dp_sink_audio_rx_flush_event(void) { /* Flush all received SBC buffers (encoded) */ APPL_TRACE_DEBUG("%s", __func__); fixed_queue_flush(btif_a2dp_sink_cb.rx_audio_queue, osi_free); } static void btif_a2dp_sink_decoder_update_event( tBTIF_MEDIA_SINK_DECODER_UPDATE* p_buf) { OI_STATUS status; APPL_TRACE_DEBUG("%s: p_codec_info[%x:%x:%x:%x:%x:%x]", __func__, 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]); int sample_rate = A2DP_GetTrackSampleRate(p_buf->codec_info); if (sample_rate == -1) { APPL_TRACE_ERROR("%s: cannot get the track frequency", __func__); return; } int channel_count = A2DP_GetTrackChannelCount(p_buf->codec_info); if (channel_count == -1) { APPL_TRACE_ERROR("%s: cannot get the channel count", __func__); return; } int channel_type = A2DP_GetSinkTrackChannelType(p_buf->codec_info); if (channel_type == -1) { APPL_TRACE_ERROR("%s: cannot get the Sink channel type", __func__); return; } btif_a2dp_sink_cb.sample_rate = sample_rate; btif_a2dp_sink_cb.channel_count = channel_count; btif_a2dp_sink_cb.rx_flush = false; APPL_TRACE_DEBUG("%s: Reset to Sink role", __func__); status = OI_CODEC_SBC_DecoderReset( &btif_a2dp_sink_context, btif_a2dp_sink_context_data, sizeof(btif_a2dp_sink_context_data), 2, 2, false); if (!OI_SUCCESS(status)) { APPL_TRACE_ERROR("%s: OI_CODEC_SBC_DecoderReset failed with error code %d", __func__, status); } APPL_TRACE_DEBUG("%s: A2dpSink: SBC create track", __func__); btif_a2dp_sink_cb.audio_track = #ifndef OS_GENERIC BtifAvrcpAudioTrackCreate(sample_rate, channel_type); #else NULL; #endif if (btif_a2dp_sink_cb.audio_track == NULL) { APPL_TRACE_ERROR("%s: A2dpSink: Track creation failed", __func__); return; } btif_a2dp_sink_cb.frames_to_process = A2DP_GetSinkFramesCountToProcess( BTIF_SINK_MEDIA_TIME_TICK_MS, p_buf->codec_info); APPL_TRACE_DEBUG("%s: Frames to be processed in 20 ms %d", __func__, btif_a2dp_sink_cb.frames_to_process); if (btif_a2dp_sink_cb.frames_to_process == 0) { APPL_TRACE_ERROR("%s: Cannot compute the number of frames to process", __func__); } } uint8_t btif_a2dp_sink_enqueue_buf(BT_HDR* p_pkt) { if (btif_a2dp_sink_cb.rx_flush) /* Flush enabled, do not enqueue */ return fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue); if (fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue) == MAX_INPUT_A2DP_FRAME_QUEUE_SZ) { uint8_t ret = fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue); osi_free(fixed_queue_try_dequeue(btif_a2dp_sink_cb.rx_audio_queue)); return ret; } BTIF_TRACE_VERBOSE("%s +", __func__); /* Allocate and queue this buffer */ tBT_SBC_HDR* p_msg = reinterpret_cast<tBT_SBC_HDR*>( osi_malloc(sizeof(tBT_SBC_HDR) + p_pkt->offset + p_pkt->len)); memcpy((uint8_t*)(p_msg + 1), (uint8_t*)(p_pkt + 1) + p_pkt->offset, p_pkt->len); p_msg->num_frames_to_be_processed = (*((uint8_t*)(p_pkt + 1) + p_pkt->offset)) & 0x0f; p_msg->len = p_pkt->len; p_msg->offset = 0; p_msg->layer_specific = p_pkt->layer_specific; BTIF_TRACE_VERBOSE("%s: frames to process %d, len %d", __func__, p_msg->num_frames_to_be_processed, p_msg->len); fixed_queue_enqueue(btif_a2dp_sink_cb.rx_audio_queue, p_msg); if (fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue) == MAX_A2DP_DELAYED_START_FRAME_COUNT) { BTIF_TRACE_DEBUG("%s: Initiate decoding", __func__); btif_a2dp_sink_audio_handle_start_decoding(); } return fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue); } void btif_a2dp_sink_audio_rx_flush_req(void) { if (fixed_queue_is_empty(btif_a2dp_sink_cb.rx_audio_queue)) { /* Queue is already empty */ return; } BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR))); p_buf->event = BTIF_MEDIA_SINK_AUDIO_RX_FLUSH; fixed_queue_enqueue(btif_a2dp_sink_cb.cmd_msg_queue, p_buf); } void btif_a2dp_sink_debug_dump(UNUSED_ATTR int fd) { // Nothing to do } void btif_a2dp_sink_set_focus_state_req(btif_a2dp_sink_focus_state_t state) { tBTIF_MEDIA_SINK_FOCUS_UPDATE* p_buf = reinterpret_cast<tBTIF_MEDIA_SINK_FOCUS_UPDATE*>( osi_malloc(sizeof(tBTIF_MEDIA_SINK_FOCUS_UPDATE))); APPL_TRACE_EVENT("%s", __func__); p_buf->focus_state = state; p_buf->hdr.event = BTIF_MEDIA_SINK_SET_FOCUS_STATE; fixed_queue_enqueue(btif_a2dp_sink_cb.cmd_msg_queue, p_buf); } static void btif_a2dp_sink_set_focus_state_event( btif_a2dp_sink_focus_state_t state) { if (!btif_av_is_connected()) return; APPL_TRACE_DEBUG("%s: setting focus state to %d", __func__, state); btif_a2dp_sink_cb.rx_focus_state = state; if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_NOT_GRANTED) { fixed_queue_flush(btif_a2dp_sink_cb.rx_audio_queue, osi_free); btif_a2dp_sink_cb.rx_flush = true; } else if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_GRANTED) { btif_a2dp_sink_cb.rx_flush = false; } } void btif_a2dp_sink_set_audio_track_gain(float gain) { APPL_TRACE_DEBUG("%s set gain to %f", __func__, gain); #ifndef OS_GENERIC BtifAvrcpSetAudioTrackGain(btif_a2dp_sink_cb.audio_track, gain); #endif } static void btif_a2dp_sink_clear_track_event_req(void) { BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR))); p_buf->event = BTIF_MEDIA_SINK_CLEAR_TRACK; fixed_queue_enqueue(btif_a2dp_sink_cb.cmd_msg_queue, p_buf); }