| /****************************************************************************** |
| * |
| * Copyright 2016 The Android Open Source Project |
| * Copyright 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 <atomic> |
| #include <cstdio> |
| #include <cstring> |
| #include <mutex> |
| #include <string> |
| |
| #include <base/bind.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 "common/message_loop_thread.h" |
| #include "osi/include/fixed_queue.h" |
| #include "osi/include/log.h" |
| #include "osi/include/osi.h" |
| |
| using bluetooth::common::MessageLoopThread; |
| using LockGuard = std::lock_guard<std::mutex>; |
| |
| /** |
| * 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; |
| |
| /* BTIF A2DP Sink control block */ |
| class BtifA2dpSinkControlBlock { |
| public: |
| explicit BtifA2dpSinkControlBlock(const std::string& thread_name) |
| : worker_thread(thread_name), |
| rx_audio_queue(nullptr), |
| rx_flush(false), |
| decode_alarm(nullptr), |
| sample_rate(0), |
| channel_count(0), |
| rx_focus_state(BTIF_A2DP_SINK_FOCUS_NOT_GRANTED), |
| audio_track(nullptr), |
| decoder_interface(nullptr) {} |
| |
| void Reset() { |
| if (audio_track != nullptr) { |
| BtifAvrcpAudioTrackStop(audio_track); |
| BtifAvrcpAudioTrackDelete(audio_track); |
| } |
| audio_track = nullptr; |
| fixed_queue_free(rx_audio_queue, nullptr); |
| rx_audio_queue = nullptr; |
| alarm_free(decode_alarm); |
| decode_alarm = nullptr; |
| rx_flush = false; |
| rx_focus_state = BTIF_A2DP_SINK_FOCUS_NOT_GRANTED; |
| sample_rate = 0; |
| channel_count = 0; |
| decoder_interface = nullptr; |
| } |
| |
| MessageLoopThread worker_thread; |
| fixed_queue_t* rx_audio_queue; |
| bool rx_flush; /* discards any incoming data when true */ |
| alarm_t* decode_alarm; |
| tA2DP_SAMPLE_RATE sample_rate; |
| tA2DP_BITS_PER_SAMPLE bits_per_sample; |
| tA2DP_CHANNEL_COUNT channel_count; |
| btif_a2dp_sink_focus_state_t rx_focus_state; /* audio focus state */ |
| void* audio_track; |
| const tA2DP_DECODER_INTERFACE* decoder_interface; |
| }; |
| |
| // Mutex for below data structures. |
| static std::mutex g_mutex; |
| |
| static BtifA2dpSinkControlBlock btif_a2dp_sink_cb("bt_a2dp_sink_worker_thread"); |
| |
| static std::atomic<int> btif_a2dp_sink_state{BTIF_A2DP_SINK_STATE_OFF}; |
| |
| static void btif_a2dp_sink_init_delayed(); |
| static void btif_a2dp_sink_startup_delayed(); |
| static void btif_a2dp_sink_start_session_delayed( |
| std::promise<void> peer_ready_promise); |
| static void btif_a2dp_sink_end_session_delayed(); |
| static void btif_a2dp_sink_shutdown_delayed(); |
| static void btif_a2dp_sink_cleanup_delayed(); |
| static void btif_a2dp_sink_command_ready(BT_HDR* p_msg); |
| static void btif_a2dp_sink_audio_handle_stop_decoding(); |
| static void btif_decode_alarm_cb(void* context); |
| static void btif_a2dp_sink_audio_handle_start_decoding(); |
| static void btif_a2dp_sink_avk_handle_timer(); |
| static void btif_a2dp_sink_audio_rx_flush_req(); |
| /* Handle incoming media packets A2DP SINK streaming */ |
| static void btif_a2dp_sink_handle_inc_media(BT_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(); |
| 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(); |
| static void btif_a2dp_sink_clear_track_event_req(); |
| |
| 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_init() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| |
| if (btif_a2dp_sink_state != BTIF_A2DP_SINK_STATE_OFF) { |
| LOG_ERROR(LOG_TAG, "%s: A2DP Sink media task already running", __func__); |
| return false; |
| } |
| |
| btif_a2dp_sink_cb.Reset(); |
| btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_STARTING_UP; |
| |
| /* Start A2DP Sink media task */ |
| btif_a2dp_sink_cb.worker_thread.StartUp(); |
| if (!btif_a2dp_sink_cb.worker_thread.IsRunning()) { |
| LOG_ERROR(LOG_TAG, "%s: unable to start up media thread", __func__); |
| btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF; |
| return false; |
| } |
| |
| btif_a2dp_sink_cb.rx_audio_queue = fixed_queue_new(SIZE_MAX); |
| |
| /* Schedule the rest of the operations */ |
| if (!btif_a2dp_sink_cb.worker_thread.EnableRealTimeScheduling()) { |
| LOG(FATAL) << __func__ |
| << ": Failed to increase A2DP decoder thread priority"; |
| } |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_init_delayed)); |
| return true; |
| } |
| |
| static void btif_a2dp_sink_init_delayed() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_RUNNING; |
| } |
| |
| bool btif_a2dp_sink_startup() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_startup_delayed)); |
| return true; |
| } |
| |
| static void btif_a2dp_sink_startup_delayed() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| // Nothing to do |
| } |
| |
| bool btif_a2dp_sink_start_session(const RawAddress& peer_address, |
| std::promise<void> peer_ready_promise) { |
| LOG(INFO) << __func__ << ": peer_address=" << peer_address; |
| if (btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_start_session_delayed, |
| std::move(peer_ready_promise)))) { |
| return true; |
| } else { |
| // cannot set promise but triggers crash |
| LOG(FATAL) << __func__ << ": peer_address=" << peer_address |
| << " fails to context switch"; |
| return false; |
| } |
| } |
| |
| static void btif_a2dp_sink_start_session_delayed( |
| std::promise<void> peer_ready_promise) { |
| LOG(INFO) << __func__; |
| LockGuard lock(g_mutex); |
| peer_ready_promise.set_value(); |
| // Nothing to do |
| } |
| |
| bool btif_a2dp_sink_restart_session(const RawAddress& old_peer_address, |
| const RawAddress& new_peer_address, |
| std::promise<void> peer_ready_promise) { |
| LOG(INFO) << __func__ << ": old_peer_address=" << old_peer_address |
| << " new_peer_address=" << new_peer_address; |
| |
| CHECK(!new_peer_address.IsEmpty()); |
| |
| if (!old_peer_address.IsEmpty()) { |
| btif_a2dp_sink_end_session(old_peer_address); |
| } |
| |
| if (!bta_av_co_set_active_peer(new_peer_address)) { |
| LOG(ERROR) << __func__ |
| << ": Cannot stream audio: cannot set active peer to " |
| << new_peer_address; |
| peer_ready_promise.set_value(); |
| return false; |
| } |
| |
| if (old_peer_address.IsEmpty()) { |
| btif_a2dp_sink_startup(); |
| } |
| btif_a2dp_sink_start_session(new_peer_address, std::move(peer_ready_promise)); |
| |
| return true; |
| } |
| |
| bool btif_a2dp_sink_end_session(const RawAddress& peer_address) { |
| LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__, |
| peer_address.ToString().c_str()); |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_end_session_delayed)); |
| return true; |
| } |
| |
| static void btif_a2dp_sink_end_session_delayed() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| // Nothing to do |
| } |
| |
| void btif_a2dp_sink_shutdown() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_shutdown_delayed)); |
| } |
| |
| static void btif_a2dp_sink_shutdown_delayed() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| // Nothing to do |
| } |
| |
| void btif_a2dp_sink_cleanup() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| |
| alarm_t* decode_alarm; |
| |
| // Make sure the sink is shutdown |
| btif_a2dp_sink_shutdown(); |
| |
| { |
| LockGuard lock(g_mutex); |
| 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; |
| |
| decode_alarm = btif_a2dp_sink_cb.decode_alarm; |
| btif_a2dp_sink_cb.decode_alarm = nullptr; |
| } |
| |
| // Stop the timer |
| alarm_free(decode_alarm); |
| |
| // Exit the thread |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_cleanup_delayed)); |
| btif_a2dp_sink_cb.worker_thread.ShutDown(); |
| } |
| |
| static void btif_a2dp_sink_cleanup_delayed() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| |
| fixed_queue_free(btif_a2dp_sink_cb.rx_audio_queue, nullptr); |
| btif_a2dp_sink_cb.rx_audio_queue = nullptr; |
| btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF; |
| } |
| |
| tA2DP_SAMPLE_RATE btif_a2dp_sink_get_sample_rate() { |
| LockGuard lock(g_mutex); |
| return btif_a2dp_sink_cb.sample_rate; |
| } |
| |
| tA2DP_BITS_PER_SAMPLE btif_a2dp_sink_get_bits_per_sample() { |
| LockGuard lock(g_mutex); |
| return btif_a2dp_sink_cb.bits_per_sample; |
| } |
| |
| tA2DP_CHANNEL_COUNT btif_a2dp_sink_get_channel_count() { |
| LockGuard lock(g_mutex); |
| return btif_a2dp_sink_cb.channel_count; |
| } |
| |
| static void btif_a2dp_sink_command_ready(BT_HDR* p_msg) { |
| 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: |
| LOG_ERROR(LOG_TAG, "%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) { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| 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; |
| |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, (BT_HDR*)p_buf)); |
| } |
| |
| void btif_a2dp_sink_on_idle() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| 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(); |
| } |
| |
| void btif_a2dp_sink_on_stopped(UNUSED_ATTR tBTA_AV_SUSPEND* p_av_suspend) { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| 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) { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| 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() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| alarm_t* old_alarm; |
| { |
| LockGuard lock(g_mutex); |
| btif_a2dp_sink_cb.rx_flush = true; |
| btif_a2dp_sink_audio_rx_flush_req(); |
| old_alarm = btif_a2dp_sink_cb.decode_alarm; |
| btif_a2dp_sink_cb.decode_alarm = nullptr; |
| } |
| |
| // Drop the lock here, btif_decode_alarm_cb may in the process of being called |
| // while we alarm free leading to deadlock. |
| // |
| // alarm_free waits for btif_decode_alarm_cb which is waiting for g_mutex. |
| alarm_free(old_alarm); |
| |
| { |
| LockGuard lock(g_mutex); |
| #ifndef OS_GENERIC |
| BtifAvrcpAudioTrackPause(btif_a2dp_sink_cb.audio_track); |
| #endif |
| } |
| } |
| |
| static void btif_decode_alarm_cb(UNUSED_ATTR void* context) { |
| LockGuard lock(g_mutex); |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_avk_handle_timer)); |
| } |
| |
| static void btif_a2dp_sink_clear_track_event() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| |
| #ifndef OS_GENERIC |
| BtifAvrcpAudioTrackStop(btif_a2dp_sink_cb.audio_track); |
| BtifAvrcpAudioTrackDelete(btif_a2dp_sink_cb.audio_track); |
| #endif |
| btif_a2dp_sink_cb.audio_track = nullptr; |
| } |
| |
| // Must be called while locked. |
| static void btif_a2dp_sink_audio_handle_start_decoding() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| if (btif_a2dp_sink_cb.decode_alarm != nullptr) |
| 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 == nullptr) { |
| 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, nullptr); |
| } |
| |
| static void btif_a2dp_sink_on_decode_complete(uint8_t* data, uint32_t len) { |
| #ifndef OS_GENERIC |
| BtifAvrcpAudioTrackWriteData(btif_a2dp_sink_cb.audio_track, |
| reinterpret_cast<void*>(data), len); |
| #endif |
| } |
| |
| // Must be called while locked. |
| static void btif_a2dp_sink_handle_inc_media(BT_HDR* p_msg) { |
| if ((btif_av_get_peer_sep() == AVDT_TSEP_SNK) || |
| (btif_a2dp_sink_cb.rx_flush)) { |
| APPL_TRACE_DEBUG("%s: state changed happened in this tick", __func__); |
| return; |
| } |
| |
| CHECK(btif_a2dp_sink_cb.decoder_interface != nullptr); |
| if (!btif_a2dp_sink_cb.decoder_interface->decode_packet(p_msg)) { |
| LOG_ERROR(LOG_TAG, "%s: decoding failed", __func__); |
| } |
| } |
| |
| static void btif_a2dp_sink_avk_handle_timer() { |
| LockGuard lock(g_mutex); |
| |
| BT_HDR* p_msg; |
| 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; |
| } |
| |
| APPL_TRACE_DEBUG("%s: process frames begin", __func__); |
| while (true) { |
| p_msg = (BT_HDR*)fixed_queue_try_dequeue(btif_a2dp_sink_cb.rx_audio_queue); |
| if (p_msg == NULL) { |
| break; |
| } |
| APPL_TRACE_DEBUG("%s: number of packets in queue %zu", __func__, |
| fixed_queue_length(btif_a2dp_sink_cb.rx_audio_queue)); |
| |
| /* Queue packet has less frames */ |
| btif_a2dp_sink_handle_inc_media(p_msg); |
| osi_free(p_msg); |
| } |
| APPL_TRACE_DEBUG("%s: process frames end", __func__); |
| } |
| |
| /* when true media task discards any rx frames */ |
| void btif_a2dp_sink_set_rx_flush(bool enable) { |
| LOG_INFO(LOG_TAG, "%s: enable=%s", __func__, (enable) ? "true" : "false"); |
| LockGuard lock(g_mutex); |
| |
| btif_a2dp_sink_cb.rx_flush = enable; |
| } |
| |
| static void btif_a2dp_sink_audio_rx_flush_event() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| // Flush all received encoded audio buffers |
| 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) { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| LockGuard lock(g_mutex); |
| 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) { |
| LOG_ERROR(LOG_TAG, "%s: cannot get the track frequency", __func__); |
| return; |
| } |
| int bits_per_sample = A2DP_GetTrackBitsPerSample(p_buf->codec_info); |
| if (bits_per_sample == -1) { |
| LOG_ERROR(LOG_TAG, "%s: cannot get the bits per sample", __func__); |
| return; |
| } |
| int channel_count = A2DP_GetTrackChannelCount(p_buf->codec_info); |
| if (channel_count == -1) { |
| LOG_ERROR(LOG_TAG, "%s: cannot get the channel count", __func__); |
| return; |
| } |
| int channel_type = A2DP_GetSinkTrackChannelType(p_buf->codec_info); |
| if (channel_type == -1) { |
| LOG_ERROR(LOG_TAG, "%s: cannot get the Sink channel type", __func__); |
| return; |
| } |
| btif_a2dp_sink_cb.sample_rate = sample_rate; |
| btif_a2dp_sink_cb.bits_per_sample = bits_per_sample; |
| btif_a2dp_sink_cb.channel_count = channel_count; |
| |
| btif_a2dp_sink_cb.rx_flush = false; |
| APPL_TRACE_DEBUG("%s: reset to Sink role", __func__); |
| |
| btif_a2dp_sink_cb.decoder_interface = bta_av_co_get_decoder_interface(); |
| if (btif_a2dp_sink_cb.decoder_interface == nullptr) { |
| LOG_ERROR(LOG_TAG, "%s: cannot stream audio: no source decoder interface", |
| __func__); |
| return; |
| } |
| |
| if (!btif_a2dp_sink_cb.decoder_interface->decoder_init( |
| btif_a2dp_sink_on_decode_complete)) { |
| LOG_ERROR(LOG_TAG, "%s: failed to initialize decoder", __func__); |
| return; |
| } |
| |
| APPL_TRACE_DEBUG("%s: create audio track", __func__); |
| btif_a2dp_sink_cb.audio_track = |
| #ifndef OS_GENERIC |
| BtifAvrcpAudioTrackCreate(sample_rate, bits_per_sample, channel_type); |
| #else |
| NULL; |
| #endif |
| if (btif_a2dp_sink_cb.audio_track == nullptr) { |
| LOG_ERROR(LOG_TAG, "%s: track creation failed", __func__); |
| return; |
| } |
| } |
| |
| uint8_t btif_a2dp_sink_enqueue_buf(BT_HDR* p_pkt) { |
| LockGuard lock(g_mutex); |
| 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 */ |
| BT_HDR* p_msg = |
| reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(*p_msg) + p_pkt->len)); |
| memcpy(p_msg, p_pkt, sizeof(*p_msg)); |
| p_msg->offset = 0; |
| memcpy(p_msg->data, p_pkt->data + p_pkt->offset, p_pkt->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() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| 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; |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, 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) { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| tBTIF_MEDIA_SINK_FOCUS_UPDATE* p_buf = |
| reinterpret_cast<tBTIF_MEDIA_SINK_FOCUS_UPDATE*>( |
| osi_malloc(sizeof(tBTIF_MEDIA_SINK_FOCUS_UPDATE))); |
| p_buf->focus_state = state; |
| p_buf->hdr.event = BTIF_MEDIA_SINK_SET_FOCUS_STATE; |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, (BT_HDR*)p_buf)); |
| } |
| |
| static void btif_a2dp_sink_set_focus_state_event( |
| btif_a2dp_sink_focus_state_t state) { |
| LOG_INFO(LOG_TAG, "%s: state=%d", __func__, state); |
| LockGuard lock(g_mutex); |
| |
| 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) { |
| LOG_INFO(LOG_TAG, "%s: set gain to %f", __func__, gain); |
| LockGuard lock(g_mutex); |
| |
| #ifndef OS_GENERIC |
| BtifAvrcpSetAudioTrackGain(btif_a2dp_sink_cb.audio_track, gain); |
| #endif |
| } |
| |
| static void btif_a2dp_sink_clear_track_event_req() { |
| LOG_INFO(LOG_TAG, "%s", __func__); |
| BT_HDR* p_buf = reinterpret_cast<BT_HDR*>(osi_malloc(sizeof(BT_HDR))); |
| |
| p_buf->event = BTIF_MEDIA_SINK_CLEAR_TRACK; |
| btif_a2dp_sink_cb.worker_thread.DoInThread( |
| FROM_HERE, base::BindOnce(btif_a2dp_sink_command_ready, p_buf)); |
| } |