From eaa0110045d12d5cf2a4466168f19c8a80d1144b Mon Sep 17 00:00:00 2001 From: Cheney Ni Date: Mon, 11 Mar 2019 20:49:11 +0800 Subject: [PATCH] Add a workaround to play A2DP SBC Mono There is a similar WAR of aosp/522661 at A2DP legacy HAL. In order to suport MONO channel mode, the PCM audio is pulled as STEREO and mixed into MONO by the Bluetooth Audio HAL. Test: Playing SBC mono with Headset Bug: 127593318 Change-Id: I78f3973ba6c8c733dc18122288a915daed97be65 --- audio_bluetooth_hw/Android.bp | 1 + audio_bluetooth_hw/device_port_proxy.cc | 27 ++++++++++++++++++++------- audio_bluetooth_hw/device_port_proxy.h | 7 +++++++ audio_bluetooth_hw/stream_apis.cc | 7 +++++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/audio_bluetooth_hw/Android.bp b/audio_bluetooth_hw/Android.bp index df4904be0..9cdd64a39 100644 --- a/audio_bluetooth_hw/Android.bp +++ b/audio_bluetooth_hw/Android.bp @@ -13,6 +13,7 @@ cc_library_shared { header_libs: ["libhardware_headers"], shared_libs: [ "android.hardware.bluetooth.audio@2.0", + "libaudioutils", "libbase", "libbluetooth_audio_session", "libcutils", diff --git a/audio_bluetooth_hw/device_port_proxy.cc b/audio_bluetooth_hw/device_port_proxy.cc index c7bf457cb..0bbf1d5cc 100644 --- a/audio_bluetooth_hw/device_port_proxy.cc +++ b/audio_bluetooth_hw/device_port_proxy.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -280,7 +281,8 @@ bool BluetoothAudioPortOut::LoadAudioConfig(audio_config_t* audio_cfg) const { return false; } audio_cfg->sample_rate = SampleRateToAudioFormat(pcm_cfg.sampleRate); - audio_cfg->channel_mask = ChannelModeToAudioFormat(pcm_cfg.channelMode); + audio_cfg->channel_mask = + (is_stereo_to_mono_ ? AUDIO_CHANNEL_OUT_STEREO : ChannelModeToAudioFormat(pcm_cfg.channelMode)); audio_cfg->format = BitsPerSampleToAudioFormat(pcm_cfg.bitsPerSample); return true; } @@ -321,7 +323,8 @@ bool BluetoothAudioPortOut::Start() { } LOG(INFO) << __func__ << ": session_type=" << toString(session_type_) << ", cookie=0x" - << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " request"; + << StringPrintf("%04hx", cookie_) << ", state=" << state_ + << ", mono=" << (is_stereo_to_mono_ ? "true" : "false") << " request"; bool retval = false; if (state_ == BluetoothStreamState::STANDBY) { state_ = BluetoothStreamState::STARTING; @@ -335,7 +338,8 @@ bool BluetoothAudioPortOut::Start() { if (retval) { LOG(INFO) << __func__ << ": session_type=" << toString(session_type_) << ", cookie=0x" - << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " done"; + << StringPrintf("%04hx", cookie_) << ", state=" << state_ + << ", mono=" << (is_stereo_to_mono_ ? "true" : "false") << " done"; } else { LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_) << ", cookie=0x" << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " failure"; @@ -387,11 +391,20 @@ void BluetoothAudioPortOut::Stop() { << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " done"; } -size_t BluetoothAudioPortOut::WriteData(const void* buffer, - size_t bytes) const { +size_t BluetoothAudioPortOut::WriteData(const void* buffer, size_t bytes) const { if (!in_use()) return 0; - return BluetoothAudioSessionControl::OutWritePcmData(session_type_, buffer, - bytes); + if (!is_stereo_to_mono_) { + return BluetoothAudioSessionControl::OutWritePcmData(session_type_, buffer, bytes); + } + + // WAR to mix the stereo into Mono (16 bits per sample) + const size_t write_frames = bytes >> 2; + if (write_frames == 0) return 0; + auto src = static_cast(buffer); + std::unique_ptr dst{new int16_t[write_frames]}; + downmix_to_mono_i16_from_stereo_i16(dst.get(), src, write_frames); + // a frame is 16 bits, and the size of a mono frame is equal to half a stereo. + return BluetoothAudioSessionControl::OutWritePcmData(session_type_, dst.get(), write_frames * 2) * 2; } bool BluetoothAudioPortOut::GetPresentationPosition(uint64_t* delay_ns, diff --git a/audio_bluetooth_hw/device_port_proxy.h b/audio_bluetooth_hw/device_port_proxy.h index e4b6b252d..16db27458 100644 --- a/audio_bluetooth_hw/device_port_proxy.h +++ b/audio_bluetooth_hw/device_port_proxy.h @@ -51,6 +51,11 @@ class BluetoothAudioPortOut { // Bluetooth stack bool LoadAudioConfig(audio_config_t* audio_cfg) const; + // WAR to support Mono mode / 16 bits per sample + void ForcePcmStereoToMono(bool force) { + is_stereo_to_mono_ = force; + } + // When the Audio framework / HAL wants to change the stream state, it invokes // these 3 functions to control the Bluetooth stack (Audio Control Path). // Note: Both Start() and Suspend() will return ture when there are no errors. @@ -85,6 +90,8 @@ class BluetoothAudioPortOut { uint16_t cookie_; mutable std::mutex cv_mutex_; std::condition_variable internal_cv_; + // WR to support Mono: True if fetching Stereo and mixing into Mono + bool is_stereo_to_mono_ = false; // Check and initialize session type for |devices| If failed, this // BluetoothAudioPortOut is not initialized and must be deleted. diff --git a/audio_bluetooth_hw/stream_apis.cc b/audio_bluetooth_hw/stream_apis.cc index ab8d90d2c..be48437b2 100644 --- a/audio_bluetooth_hw/stream_apis.cc +++ b/audio_bluetooth_hw/stream_apis.cc @@ -644,6 +644,13 @@ int adev_open_output_stream(struct audio_hw_device* dev, LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_.GetState() << " failed to get audio config"; } + // WAR to support Mono / 16 bits per sample as the Bluetooth stack required + if (config->channel_mask == AUDIO_CHANNEL_OUT_MONO && config->format == AUDIO_FORMAT_PCM_16_BIT) { + LOG(INFO) << __func__ << ": force channels=0x" << android::base::StringPrintf("%x", out->channel_mask_) + << " to be AUDIO_CHANNEL_OUT_STEREO"; + out->bluetooth_output_.ForcePcmStereoToMono(true); + config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; + } out->sample_rate_ = config->sample_rate; out->channel_mask_ = config->channel_mask; out->format_ = config->format; -- 2.11.0