978ccd40f9
On platform release T, the legacy `audio.a2dp.default` HAL no longer exists, and cannot be trivially restored for generic system-side A2DP audio support. Rather than trying to cling on to its existence (which does not even work anymore even if one did so), it is time to come up with a new solution. This commit introduces a system-side implementation of the generic bluetooth audio HAL. The HAL itself is modelled after the default bluetooth audio HAL, while substantial components of the default audio HAL are also included as it has to also implement the audio HAL interfaces. The audio HAL implementation is delegated to `audio.sysbta.default`, forked from `audio.bluetooth.default` from the Bluetooth apex package. It then communicates with the bluetooth audio HAL interfaces, which need to live in the same process (in this case, the process of the sysbta HAL). This is why we cannot just load `audio.sysbta.default` into the default audio HAL or the audioserver process directly, but rather have to include our own simplistic audio HAL implementation. For now, the audio HAL implementation only includes one for the 6.0 version, but technically, an interface for *every* audio HAL version needs to exist. This, along with other messiness in the sysbta implementation, will be addressed in a future commit. Note that to actually make use of the sysbta audio hal, patches in frameworks/av and packages/modules/Bluetooth are required to introduce support for the property `persist.bluetooth.system_audio_hal.enabled`.
1309 lines
50 KiB
C++
1309 lines
50 KiB
C++
/*
|
|
* Copyright 2019 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 "device_port_proxy.h"
|
|
#define LOG_TAG "BTAudioHalStream"
|
|
|
|
#include <android-base/logging.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <cutils/properties.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <log/log.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "BluetoothAudioSession.h"
|
|
#include "stream_apis.h"
|
|
#include "utils.h"
|
|
|
|
using ::android::base::StringPrintf;
|
|
using ::android::bluetooth::audio::utils::GetAudioParamString;
|
|
using ::android::bluetooth::audio::utils::ParseAudioParams;
|
|
|
|
namespace {
|
|
|
|
constexpr unsigned int kMinimumDelayMs = 50;
|
|
constexpr unsigned int kMaximumDelayMs = 1000;
|
|
constexpr int kExtraAudioSyncMs = 200;
|
|
|
|
std::ostream& operator<<(std::ostream& os, const audio_config& config) {
|
|
return os << "audio_config[sample_rate=" << config.sample_rate
|
|
<< ", channels=" << StringPrintf("%#x", config.channel_mask)
|
|
<< ", format=" << config.format << "]";
|
|
}
|
|
|
|
void out_calculate_feeding_delay_ms(const BluetoothStreamOut* out,
|
|
uint32_t* latency_ms,
|
|
uint64_t* frames = nullptr,
|
|
struct timespec* timestamp = nullptr) {
|
|
if (latency_ms == nullptr && frames == nullptr && timestamp == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// delay_report is the audio delay from the remote headset receiving data to
|
|
// the headset playing sound in units of nanoseconds
|
|
uint64_t delay_report_ns = 0;
|
|
uint64_t delay_report_ms = 0;
|
|
// absorbed_bytes is the total number of bytes sent by the Bluetooth stack to
|
|
// a remote headset
|
|
uint64_t absorbed_bytes = 0;
|
|
// absorbed_timestamp is the ...
|
|
struct timespec absorbed_timestamp = {};
|
|
bool timestamp_fetched = false;
|
|
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
if (out->bluetooth_output_->GetPresentationPosition(
|
|
&delay_report_ns, &absorbed_bytes, &absorbed_timestamp)) {
|
|
delay_report_ms = delay_report_ns / 1000000;
|
|
// assume kMinimumDelayMs (50ms) < delay_report_ns < kMaximumDelayMs
|
|
// (1000ms), or it is invalid / ignored and use old delay calculated
|
|
// by ourselves.
|
|
if (delay_report_ms > kMinimumDelayMs &&
|
|
delay_report_ms < kMaximumDelayMs) {
|
|
timestamp_fetched = true;
|
|
} else if (delay_report_ms >= kMaximumDelayMs) {
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", delay_report=" << delay_report_ns << "ns abnormal";
|
|
}
|
|
}
|
|
if (!timestamp_fetched) {
|
|
// default to old delay if any failure is found when fetching from ports
|
|
// audio_a2dp_hw:
|
|
// frames_count = buffer_size / frame_size
|
|
// latency (sec.) = frames_count / samples_per_second (sample_rate)
|
|
// Sync from audio_a2dp_hw to add extra delay kExtraAudioSyncMs(+200ms)
|
|
delay_report_ms =
|
|
out->frames_count_ * 1000 / out->sample_rate_ + kExtraAudioSyncMs;
|
|
if (timestamp != nullptr) {
|
|
clock_gettime(CLOCK_MONOTONIC, &absorbed_timestamp);
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " uses the legacy delay " << delay_report_ms << " ms";
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", delay=" << delay_report_ms << "ms, data=" << absorbed_bytes
|
|
<< " bytes, timestamp=" << absorbed_timestamp.tv_sec << "."
|
|
<< StringPrintf("%09ld", absorbed_timestamp.tv_nsec) << "s";
|
|
|
|
if (latency_ms != nullptr) {
|
|
*latency_ms = delay_report_ms;
|
|
}
|
|
if (frames != nullptr) {
|
|
const uint64_t latency_frames = delay_report_ms * out->sample_rate_ / 1000;
|
|
*frames = absorbed_bytes / audio_stream_out_frame_size(&out->stream_out_);
|
|
if (out->frames_presented_ < *frames) {
|
|
// Are we (the audio HAL) reset?! The stack counter is obsoleted.
|
|
*frames = out->frames_presented_;
|
|
} else if ((out->frames_presented_ - *frames) > latency_frames) {
|
|
// Is the Bluetooth output reset / restarted by AVDTP reconfig?! Its
|
|
// counter was reset but could not be used.
|
|
*frames = out->frames_presented_;
|
|
}
|
|
// suppose frames would be queued in the headset buffer for delay_report
|
|
// period, so those frames in buffers should not be included in the number
|
|
// of presented frames at the timestamp.
|
|
if (*frames > latency_frames) {
|
|
*frames -= latency_frames;
|
|
} else {
|
|
*frames = 0;
|
|
}
|
|
}
|
|
if (timestamp != nullptr) {
|
|
*timestamp = absorbed_timestamp;
|
|
}
|
|
}
|
|
|
|
void in_calculate_starving_delay_ms(const BluetoothStreamIn* in,
|
|
int64_t* frames, int64_t* time) {
|
|
// delay_report is the audio delay from the remote headset receiving data to
|
|
// the headset playing sound in units of nanoseconds
|
|
uint64_t delay_report_ns = 0;
|
|
uint64_t delay_report_ms = 0;
|
|
// dispersed_bytes is the total number of bytes received by the Bluetooth
|
|
// stack from a remote headset
|
|
uint64_t dispersed_bytes = 0;
|
|
struct timespec dispersed_timestamp = {};
|
|
|
|
std::unique_lock<std::mutex> lock(in->mutex_);
|
|
in->bluetooth_input_->GetPresentationPosition(
|
|
&delay_report_ns, &dispersed_bytes, &dispersed_timestamp);
|
|
delay_report_ms = delay_report_ns / 1000000;
|
|
|
|
const uint64_t latency_frames = delay_report_ms * in->sample_rate_ / 1000;
|
|
*frames = dispersed_bytes / audio_stream_in_frame_size(&in->stream_in_);
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", delay=" << delay_report_ms
|
|
<< "ms, data=" << dispersed_bytes
|
|
<< " bytes, timestamp=" << dispersed_timestamp.tv_sec << "."
|
|
<< StringPrintf("%09ld", dispersed_timestamp.tv_nsec) << "s";
|
|
|
|
if (in->frames_presented_ < *frames) {
|
|
// Was audio HAL reset?! The stack counter is obsoleted.
|
|
*frames = in->frames_presented_;
|
|
} else if ((in->frames_presented_ - *frames) > latency_frames) {
|
|
// Is the Bluetooth input reset ?! Its counter was reset but could not be
|
|
// used.
|
|
*frames = in->frames_presented_;
|
|
}
|
|
// suppose frames would be queued in the headset buffer for delay_report
|
|
// period, so those frames in buffers should not be included in the number
|
|
// of presented frames at the timestamp.
|
|
if (*frames > latency_frames) {
|
|
*frames -= latency_frames;
|
|
} else {
|
|
*frames = 0;
|
|
}
|
|
|
|
*time = (dispersed_timestamp.tv_sec * 1000000000LL +
|
|
dispersed_timestamp.tv_nsec) /
|
|
1000;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
|
|
switch (state) {
|
|
case BluetoothStreamState::DISABLED:
|
|
return os << "DISABLED";
|
|
case BluetoothStreamState::STANDBY:
|
|
return os << "STANDBY";
|
|
case BluetoothStreamState::STARTING:
|
|
return os << "STARTING";
|
|
case BluetoothStreamState::STARTED:
|
|
return os << "STARTED";
|
|
case BluetoothStreamState::SUSPENDING:
|
|
return os << "SUSPENDING";
|
|
case BluetoothStreamState::UNKNOWN:
|
|
return os << "UNKNOWN";
|
|
default:
|
|
return os << StringPrintf("%#hhx", state);
|
|
}
|
|
}
|
|
|
|
static uint32_t out_get_sample_rate(const struct audio_stream* stream) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
audio_config_t audio_cfg;
|
|
if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
return audio_cfg.sample_rate;
|
|
} else {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", sample_rate=" << out->sample_rate_ << " failed";
|
|
return out->sample_rate_;
|
|
}
|
|
}
|
|
|
|
static int out_set_sample_rate(struct audio_stream* stream, uint32_t rate) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", sample_rate=" << out->sample_rate_;
|
|
return (rate == out->sample_rate_ ? 0 : -1);
|
|
}
|
|
|
|
static size_t out_get_buffer_size(const struct audio_stream* stream) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
size_t buffer_size =
|
|
out->frames_count_ * audio_stream_out_frame_size(&out->stream_out_);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", buffer_size=" << buffer_size;
|
|
return buffer_size;
|
|
}
|
|
|
|
static audio_channel_mask_t out_get_channels(
|
|
const struct audio_stream* stream) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
audio_config_t audio_cfg;
|
|
if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
return audio_cfg.channel_mask;
|
|
} else {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", channels=" << StringPrintf("%#x", out->channel_mask_)
|
|
<< " failure";
|
|
return out->channel_mask_;
|
|
}
|
|
}
|
|
|
|
static audio_format_t out_get_format(const struct audio_stream* stream) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
audio_config_t audio_cfg;
|
|
if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
return audio_cfg.format;
|
|
} else {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", format=" << out->format_ << " failure";
|
|
return out->format_;
|
|
}
|
|
}
|
|
|
|
static int out_set_format(struct audio_stream* stream, audio_format_t format) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", format=" << out->format_;
|
|
return (format == out->format_ ? 0 : -1);
|
|
}
|
|
|
|
static int out_standby(struct audio_stream* stream) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
int retval = 0;
|
|
|
|
// out->last_write_time_us_ = 0; unnecessary as a stale write time has same
|
|
// effect
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " being standby (suspend)";
|
|
if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
|
|
out->frames_rendered_ = 0;
|
|
retval = (out->bluetooth_output_->Suspend() ? 0 : -EIO);
|
|
} else if (out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::STARTING ||
|
|
out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::SUSPENDING) {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " NOT ready to be standby";
|
|
retval = -EBUSY;
|
|
} else {
|
|
LOG(DEBUG) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " standby already";
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " standby (suspend) retval=" << retval;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int out_dump(const struct audio_stream* stream, int fd) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState();
|
|
return 0;
|
|
}
|
|
|
|
static int out_set_parameters(struct audio_stream* stream,
|
|
const char* kvpairs) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
int retval = 0;
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", kvpairs=[" << kvpairs << "]";
|
|
|
|
std::unordered_map<std::string, std::string> params =
|
|
ParseAudioParams(kvpairs);
|
|
if (params.empty()) return retval;
|
|
|
|
LOG(VERBOSE) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
|
|
<< "]";
|
|
|
|
audio_config_t audio_cfg;
|
|
if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end() ||
|
|
params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end() ||
|
|
params.find(AUDIO_PARAMETER_STREAM_SUP_FORMATS) != params.end()) {
|
|
if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
|
|
out->sample_rate_ = audio_cfg.sample_rate;
|
|
out->channel_mask_ = audio_cfg.channel_mask;
|
|
out->format_ = audio_cfg.format;
|
|
LOG(VERBOSE) << "state=" << out->bluetooth_output_->GetState()
|
|
<< ", sample_rate=" << out->sample_rate_
|
|
<< ", channels=" << StringPrintf("%#x", out->channel_mask_)
|
|
<< ", format=" << out->format_;
|
|
} else {
|
|
LOG(WARNING) << __func__
|
|
<< ": state=" << out->bluetooth_output_->GetState()
|
|
<< " failed to get audio config";
|
|
}
|
|
}
|
|
|
|
if (params.find("routing") != params.end()) {
|
|
auto routing_param = params.find("routing");
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", stream param '" << routing_param->first.c_str() << "="
|
|
<< routing_param->second.c_str() << "'";
|
|
}
|
|
|
|
if (params.find("A2dpSuspended") != params.end() &&
|
|
out->bluetooth_output_->IsA2dp()) {
|
|
if (params["A2dpSuspended"] == "true") {
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " stream param stopped";
|
|
out->frames_rendered_ = 0;
|
|
if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
|
|
out->bluetooth_output_->Suspend();
|
|
out->bluetooth_output_->SetState(BluetoothStreamState::DISABLED);
|
|
} else if (out->bluetooth_output_->GetState() !=
|
|
BluetoothStreamState::DISABLED) {
|
|
out->bluetooth_output_->Stop();
|
|
}
|
|
} else {
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " stream param standby";
|
|
if (out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::DISABLED) {
|
|
out->bluetooth_output_->SetState(BluetoothStreamState::STANDBY);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (params.find("closing") != params.end()) {
|
|
if (params["closing"] == "true") {
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " stream param closing, disallow any writes?";
|
|
if (out->bluetooth_output_->GetState() !=
|
|
BluetoothStreamState::DISABLED) {
|
|
out->frames_rendered_ = 0;
|
|
out->frames_presented_ = 0;
|
|
out->bluetooth_output_->Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (params.find("exiting") != params.end()) {
|
|
if (params["exiting"] == "1") {
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " stream param exiting";
|
|
if (out->bluetooth_output_->GetState() !=
|
|
BluetoothStreamState::DISABLED) {
|
|
out->frames_rendered_ = 0;
|
|
out->frames_presented_ = 0;
|
|
out->bluetooth_output_->Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", kvpairs=[" << kvpairs << "], retval=" << retval;
|
|
return retval;
|
|
}
|
|
|
|
static char* out_get_parameters(const struct audio_stream* stream,
|
|
const char* keys) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", keys=[" << keys << "]";
|
|
|
|
std::unordered_map<std::string, std::string> params = ParseAudioParams(keys);
|
|
if (params.empty()) return strdup("");
|
|
|
|
audio_config_t audio_cfg;
|
|
if (out->bluetooth_output_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
} else {
|
|
LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " failed to get audio config";
|
|
}
|
|
|
|
std::unordered_map<std::string, std::string> return_params;
|
|
if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end()) {
|
|
std::string param;
|
|
if (audio_cfg.sample_rate == 16000) {
|
|
param = "16000";
|
|
}
|
|
if (audio_cfg.sample_rate == 24000) {
|
|
param = "24000";
|
|
}
|
|
if (audio_cfg.sample_rate == 44100) {
|
|
param = "44100";
|
|
}
|
|
if (audio_cfg.sample_rate == 48000) {
|
|
param = "48000";
|
|
}
|
|
if (audio_cfg.sample_rate == 88200) {
|
|
param = "88200";
|
|
}
|
|
if (audio_cfg.sample_rate == 96000) {
|
|
param = "96000";
|
|
}
|
|
if (audio_cfg.sample_rate == 176400) {
|
|
param = "176400";
|
|
}
|
|
if (audio_cfg.sample_rate == 192000) {
|
|
param = "192000";
|
|
}
|
|
return_params[AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES] = param;
|
|
}
|
|
|
|
if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end()) {
|
|
std::string param;
|
|
if (audio_cfg.channel_mask == AUDIO_CHANNEL_OUT_MONO) {
|
|
param = "AUDIO_CHANNEL_OUT_MONO";
|
|
}
|
|
if (audio_cfg.channel_mask == AUDIO_CHANNEL_OUT_STEREO) {
|
|
param = "AUDIO_CHANNEL_OUT_STEREO";
|
|
}
|
|
return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
|
|
}
|
|
|
|
if (params.find(AUDIO_PARAMETER_STREAM_SUP_FORMATS) != params.end()) {
|
|
std::string param;
|
|
if (audio_cfg.format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
param = "AUDIO_FORMAT_PCM_16_BIT";
|
|
}
|
|
if (audio_cfg.format == AUDIO_FORMAT_PCM_24_BIT_PACKED) {
|
|
param = "AUDIO_FORMAT_PCM_24_BIT_PACKED";
|
|
}
|
|
if (audio_cfg.format == AUDIO_FORMAT_PCM_8_24_BIT) {
|
|
param = "AUDIO_FORMAT_PCM_8_24_BIT";
|
|
}
|
|
if (audio_cfg.format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
param = "AUDIO_FORMAT_PCM_32_BIT";
|
|
}
|
|
return_params[AUDIO_PARAMETER_STREAM_SUP_FORMATS] = param;
|
|
}
|
|
|
|
std::string result;
|
|
for (const auto& ptr : return_params) {
|
|
result += ptr.first + "=" + ptr.second + ";";
|
|
}
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", result=[" << result << "]";
|
|
return strdup(result.c_str());
|
|
}
|
|
|
|
static uint32_t out_get_latency_ms(const struct audio_stream_out* stream) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
uint32_t latency_ms = 0;
|
|
out_calculate_feeding_delay_ms(out, &latency_ms);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", latency=" << latency_ms << "ms";
|
|
return latency_ms;
|
|
}
|
|
|
|
static int out_set_volume(struct audio_stream_out* stream, float left,
|
|
float right) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", Left=" << left << ", Right=" << right;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t out_write(struct audio_stream_out* stream, const void* buffer,
|
|
size_t bytes) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
size_t totalWritten = 0;
|
|
|
|
if (out->bluetooth_output_->GetState() != BluetoothStreamState::STARTED) {
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " first time bytes=" << bytes;
|
|
lock.unlock();
|
|
if (stream->resume(stream)) {
|
|
LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " failed to resume";
|
|
if (out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::DISABLED) {
|
|
// drop data for cases of A2dpSuspended=true / closing=true
|
|
totalWritten = bytes;
|
|
}
|
|
usleep(out->preferred_data_interval_us);
|
|
return totalWritten;
|
|
}
|
|
lock.lock();
|
|
}
|
|
lock.unlock();
|
|
totalWritten = out->bluetooth_output_->WriteData(buffer, bytes);
|
|
lock.lock();
|
|
|
|
struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
if (totalWritten) {
|
|
const size_t frames = bytes / audio_stream_out_frame_size(stream);
|
|
out->frames_rendered_ += frames;
|
|
out->frames_presented_ += frames;
|
|
out->last_write_time_us_ = (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;
|
|
} else {
|
|
const int64_t now = (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;
|
|
const int64_t elapsed_time_since_last_write =
|
|
now - out->last_write_time_us_;
|
|
// frames_count = written_data / frame_size
|
|
// play_time (ms) = frames_count / (sample_rate (Sec.) / 1000000)
|
|
// sleep_time (ms) = play_time - elapsed_time
|
|
int64_t sleep_time = bytes * 1000000LL /
|
|
audio_stream_out_frame_size(stream) /
|
|
out_get_sample_rate(&stream->common) -
|
|
elapsed_time_since_last_write;
|
|
if (sleep_time > 0) {
|
|
LOG(VERBOSE) << __func__ << ": sleep " << (sleep_time / 1000)
|
|
<< " ms when writting FMQ datapath";
|
|
lock.unlock();
|
|
usleep(sleep_time);
|
|
lock.lock();
|
|
} else {
|
|
// we don't sleep when we exit standby (this is typical for a real alsa
|
|
// buffer).
|
|
sleep_time = 0;
|
|
}
|
|
out->last_write_time_us_ = now + sleep_time;
|
|
}
|
|
return totalWritten;
|
|
}
|
|
|
|
static int out_get_render_position(const struct audio_stream_out* stream,
|
|
uint32_t* dsp_frames) {
|
|
if (dsp_frames == nullptr) return -EINVAL;
|
|
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
// frames = (latency (ms) / 1000) * samples_per_second (sample_rate)
|
|
const uint64_t latency_frames =
|
|
(uint64_t)out_get_latency_ms(stream) * out->sample_rate_ / 1000;
|
|
if (out->frames_rendered_ >= latency_frames) {
|
|
*dsp_frames = (uint32_t)(out->frames_rendered_ - latency_frames);
|
|
} else {
|
|
*dsp_frames = 0;
|
|
}
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", dsp_frames=" << *dsp_frames;
|
|
return 0;
|
|
}
|
|
|
|
static int out_add_audio_effect(const struct audio_stream* stream,
|
|
effect_handle_t effect) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", effect=" << effect;
|
|
return 0;
|
|
}
|
|
|
|
static int out_remove_audio_effect(const struct audio_stream* stream,
|
|
effect_handle_t effect) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", effect=" << effect;
|
|
return 0;
|
|
}
|
|
|
|
static int out_get_next_write_timestamp(const struct audio_stream_out* stream,
|
|
int64_t* timestamp) {
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
*timestamp = 0;
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", timestamp=" << *timestamp;
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int out_pause(struct audio_stream_out* stream) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
int retval = 0;
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", pausing (suspend)";
|
|
if (out->bluetooth_output_->GetState() == BluetoothStreamState::STARTED) {
|
|
out->frames_rendered_ = 0;
|
|
retval = (out->bluetooth_output_->Suspend() ? 0 : -EIO);
|
|
} else if (out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::STARTING ||
|
|
out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::SUSPENDING) {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " NOT ready to pause?!";
|
|
retval = -EBUSY;
|
|
} else {
|
|
LOG(DEBUG) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " paused already";
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", pausing (suspend) retval=" << retval;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int out_resume(struct audio_stream_out* stream) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
int retval = 0;
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", resuming (start)";
|
|
if (out->bluetooth_output_->GetState() == BluetoothStreamState::STANDBY) {
|
|
retval = (out->bluetooth_output_->Start() ? 0 : -EIO);
|
|
} else if (out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::STARTING ||
|
|
out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::SUSPENDING) {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " NOT ready to resume?!";
|
|
retval = -EBUSY;
|
|
} else if (out->bluetooth_output_->GetState() ==
|
|
BluetoothStreamState::DISABLED) {
|
|
LOG(WARNING) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " NOT allow to resume?!";
|
|
retval = -EINVAL;
|
|
} else {
|
|
LOG(DEBUG) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< " resumed already";
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", resuming (start) retval=" << retval;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int out_get_presentation_position(const struct audio_stream_out* stream,
|
|
uint64_t* frames,
|
|
struct timespec* timestamp) {
|
|
if (frames == nullptr || timestamp == nullptr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
const auto* out = reinterpret_cast<const BluetoothStreamOut*>(stream);
|
|
out_calculate_feeding_delay_ms(out, nullptr, frames, timestamp);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", frames=" << *frames << ", timestamp=" << timestamp->tv_sec
|
|
<< "." << StringPrintf("%09ld", timestamp->tv_nsec) << "s";
|
|
return 0;
|
|
}
|
|
|
|
static void out_update_source_metadata(
|
|
struct audio_stream_out* stream,
|
|
const struct source_metadata* source_metadata) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
std::unique_lock<std::mutex> lock(out->mutex_);
|
|
if (source_metadata == nullptr || source_metadata->track_count == 0) {
|
|
return;
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", " << source_metadata->track_count << " track(s)";
|
|
out->bluetooth_output_->UpdateSourceMetadata(source_metadata);
|
|
}
|
|
|
|
static size_t frame_count(size_t microseconds, uint32_t sample_rate) {
|
|
return (microseconds * sample_rate) / 1000000;
|
|
}
|
|
|
|
int adev_open_output_stream(struct audio_hw_device* dev,
|
|
audio_io_handle_t handle, audio_devices_t devices,
|
|
audio_output_flags_t flags,
|
|
struct audio_config* config,
|
|
struct audio_stream_out** stream_out,
|
|
const char* address __unused) {
|
|
*stream_out = nullptr;
|
|
auto out = std::make_unique<BluetoothStreamOut>();
|
|
if (::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::
|
|
IsAidlAvailable()) {
|
|
out->bluetooth_output_ = std::make_unique<
|
|
::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut>();
|
|
out->is_aidl = true;
|
|
} else {
|
|
out->bluetooth_output_ = std::make_unique<
|
|
::android::bluetooth::audio::hidl::BluetoothAudioPortHidlOut>();
|
|
out->is_aidl = false;
|
|
}
|
|
if (!out->bluetooth_output_->SetUp(devices)) {
|
|
out->bluetooth_output_ = nullptr;
|
|
LOG(ERROR) << __func__ << ": cannot init HAL";
|
|
return -EINVAL;
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": device=" << StringPrintf("%#x", devices);
|
|
|
|
out->stream_out_.common.get_sample_rate = out_get_sample_rate;
|
|
out->stream_out_.common.set_sample_rate = out_set_sample_rate;
|
|
out->stream_out_.common.get_buffer_size = out_get_buffer_size;
|
|
out->stream_out_.common.get_channels = out_get_channels;
|
|
out->stream_out_.common.get_format = out_get_format;
|
|
out->stream_out_.common.set_format = out_set_format;
|
|
out->stream_out_.common.standby = out_standby;
|
|
out->stream_out_.common.dump = out_dump;
|
|
out->stream_out_.common.set_parameters = out_set_parameters;
|
|
out->stream_out_.common.get_parameters = out_get_parameters;
|
|
out->stream_out_.common.add_audio_effect = out_add_audio_effect;
|
|
out->stream_out_.common.remove_audio_effect = out_remove_audio_effect;
|
|
out->stream_out_.get_latency = out_get_latency_ms;
|
|
out->stream_out_.set_volume = out_set_volume;
|
|
out->stream_out_.write = out_write;
|
|
out->stream_out_.get_render_position = out_get_render_position;
|
|
out->stream_out_.get_next_write_timestamp = out_get_next_write_timestamp;
|
|
out->stream_out_.pause = out_pause;
|
|
out->stream_out_.resume = out_resume;
|
|
out->stream_out_.get_presentation_position = out_get_presentation_position;
|
|
out->stream_out_.update_source_metadata = out_update_source_metadata;
|
|
|
|
if (!out->bluetooth_output_->LoadAudioConfig(config)) {
|
|
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=" << 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;
|
|
// frame is number of samples per channel
|
|
|
|
size_t preferred_data_interval_us = kBluetoothDefaultOutputBufferMs * 1000;
|
|
if (out->bluetooth_output_->GetPreferredDataIntervalUs(
|
|
&preferred_data_interval_us) &&
|
|
preferred_data_interval_us != 0) {
|
|
out->preferred_data_interval_us = preferred_data_interval_us;
|
|
} else {
|
|
out->preferred_data_interval_us = kBluetoothDefaultOutputBufferMs * 1000;
|
|
}
|
|
|
|
// Ensure minimum buffer duration for spatialized output
|
|
if ((flags == (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_DEEP_BUFFER) ||
|
|
flags == AUDIO_OUTPUT_FLAG_SPATIALIZER) &&
|
|
out->preferred_data_interval_us <
|
|
kBluetoothSpatializerOutputBufferMs * 1000) {
|
|
out->preferred_data_interval_us =
|
|
kBluetoothSpatializerOutputBufferMs * 1000;
|
|
LOG(INFO) << __func__
|
|
<< ": adjusting to minimum buffer duration for spatializer: "
|
|
<< StringPrintf("%zu", out->preferred_data_interval_us);
|
|
}
|
|
|
|
out->frames_count_ =
|
|
frame_count(out->preferred_data_interval_us, out->sample_rate_);
|
|
|
|
out->frames_rendered_ = 0;
|
|
out->frames_presented_ = 0;
|
|
|
|
BluetoothStreamOut* out_ptr = out.release();
|
|
{
|
|
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
|
|
std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
|
|
bluetooth_device->opened_stream_outs_.push_back(out_ptr);
|
|
}
|
|
|
|
*stream_out = &out_ptr->stream_out_;
|
|
LOG(INFO) << __func__ << ": state=" << out_ptr->bluetooth_output_->GetState()
|
|
<< ", sample_rate=" << out_ptr->sample_rate_
|
|
<< ", channels=" << StringPrintf("%#x", out_ptr->channel_mask_)
|
|
<< ", format=" << out_ptr->format_
|
|
<< ", preferred_data_interval_us="
|
|
<< out_ptr->preferred_data_interval_us
|
|
<< ", frames=" << out_ptr->frames_count_;
|
|
return 0;
|
|
}
|
|
|
|
void adev_close_output_stream(struct audio_hw_device* dev,
|
|
struct audio_stream_out* stream) {
|
|
auto* out = reinterpret_cast<BluetoothStreamOut*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", stopping";
|
|
{
|
|
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
|
|
std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
|
|
bluetooth_device->opened_stream_outs_.remove(out);
|
|
}
|
|
if (out->bluetooth_output_->GetState() != BluetoothStreamState::DISABLED) {
|
|
out->frames_rendered_ = 0;
|
|
out->frames_presented_ = 0;
|
|
out->bluetooth_output_->Stop();
|
|
}
|
|
out->bluetooth_output_->TearDown();
|
|
LOG(VERBOSE) << __func__ << ": state=" << out->bluetooth_output_->GetState()
|
|
<< ", stopped";
|
|
delete out;
|
|
}
|
|
|
|
size_t adev_get_input_buffer_size(const struct audio_hw_device* dev,
|
|
const struct audio_config* config) {
|
|
/* TODO: Adjust this value */
|
|
LOG(VERBOSE) << __func__;
|
|
return 320;
|
|
}
|
|
|
|
static uint32_t in_get_sample_rate(const struct audio_stream* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
|
|
return in->sample_rate_;
|
|
}
|
|
|
|
static int in_set_sample_rate(struct audio_stream* stream, uint32_t rate) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", sample_rate=" << in->sample_rate_;
|
|
return (rate == in->sample_rate_ ? 0 : -ENOSYS);
|
|
}
|
|
|
|
static size_t in_get_buffer_size(const struct audio_stream* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
size_t buffer_size =
|
|
in->frames_count_ * audio_stream_in_frame_size(&in->stream_in_);
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", buffer_size=" << buffer_size;
|
|
return buffer_size;
|
|
}
|
|
|
|
static audio_channel_mask_t in_get_channels(const struct audio_stream* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
audio_config_t audio_cfg;
|
|
if (in->bluetooth_input_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
return audio_cfg.channel_mask;
|
|
} else {
|
|
LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", channels=" << StringPrintf("%#x", in->channel_mask_)
|
|
<< " failure";
|
|
return in->channel_mask_;
|
|
}
|
|
}
|
|
|
|
static audio_format_t in_get_format(const struct audio_stream* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
audio_config_t audio_cfg;
|
|
if (in->bluetooth_input_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
return audio_cfg.format;
|
|
} else {
|
|
LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", format=" << in->format_ << " failure";
|
|
return in->format_;
|
|
}
|
|
}
|
|
|
|
static int in_set_format(struct audio_stream* stream, audio_format_t format) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", format=" << in->format_;
|
|
return (format == in->format_ ? 0 : -ENOSYS);
|
|
}
|
|
|
|
static bool in_state_transition_timeout(BluetoothStreamIn* in,
|
|
std::unique_lock<std::mutex>& lock,
|
|
const BluetoothStreamState& state,
|
|
uint16_t timeout_ms) {
|
|
/* Don't loose suspend request, AF will not retry */
|
|
while (in->bluetooth_input_->GetState() == state) {
|
|
lock.unlock();
|
|
usleep(1000);
|
|
lock.lock();
|
|
|
|
/* Don't block AF forever */
|
|
if (--timeout_ms <= 0) {
|
|
LOG(WARNING) << __func__ << ", can't suspend - stucked in: "
|
|
<< static_cast<int>(state) << " state";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int in_standby(struct audio_stream* stream) {
|
|
auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
|
|
std::unique_lock<std::mutex> lock(in->mutex_);
|
|
int retval = 0;
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " being standby (suspend)";
|
|
|
|
/* Give some time to start up */
|
|
if (!in_state_transition_timeout(in, lock, BluetoothStreamState::STARTING,
|
|
kBluetoothDefaultInputStateTimeoutMs)) {
|
|
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " NOT ready to by standby";
|
|
return retval;
|
|
}
|
|
|
|
if (in->bluetooth_input_->GetState() == BluetoothStreamState::STARTED) {
|
|
retval = (in->bluetooth_input_->Suspend() ? 0 : -EIO);
|
|
} else if (in->bluetooth_input_->GetState() !=
|
|
BluetoothStreamState::SUSPENDING) {
|
|
LOG(DEBUG) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " standby already";
|
|
return retval;
|
|
}
|
|
|
|
/* Give some time to suspend */
|
|
if (!in_state_transition_timeout(in, lock, BluetoothStreamState::SUSPENDING,
|
|
kBluetoothDefaultInputStateTimeoutMs)) {
|
|
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " NOT ready to by standby";
|
|
return 0;
|
|
}
|
|
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " standby (suspend) retval=" << retval;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int in_dump(const struct audio_stream* stream, int fd) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int in_set_parameters(struct audio_stream* stream, const char* kvpairs) {
|
|
auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
|
|
std::unique_lock<std::mutex> lock(in->mutex_);
|
|
int retval = 0;
|
|
|
|
LOG(INFO) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState()
|
|
<< ", kvpairs=[" << kvpairs << "]";
|
|
|
|
std::unordered_map<std::string, std::string> params =
|
|
ParseAudioParams(kvpairs);
|
|
|
|
if (params.empty()) return retval;
|
|
|
|
LOG(INFO) << __func__ << ": ParamsMap=[" << GetAudioParamString(params)
|
|
<< "]";
|
|
|
|
return retval;
|
|
}
|
|
|
|
static char* in_get_parameters(const struct audio_stream* stream,
|
|
const char* keys) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
std::unique_lock<std::mutex> lock(in->mutex_);
|
|
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState()
|
|
<< ", keys=[" << keys << "]";
|
|
|
|
std::unordered_map<std::string, std::string> params = ParseAudioParams(keys);
|
|
if (params.empty()) return strdup("");
|
|
|
|
audio_config_t audio_cfg;
|
|
if (in->bluetooth_input_->LoadAudioConfig(&audio_cfg)) {
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " audio_cfg=" << audio_cfg;
|
|
} else {
|
|
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " failed to get audio config";
|
|
}
|
|
|
|
std::unordered_map<std::string, std::string> return_params;
|
|
|
|
/* TODO: Implement parameter getter */
|
|
|
|
std::string result;
|
|
for (const auto& ptr : return_params) {
|
|
result += ptr.first + "=" + ptr.second + ";";
|
|
}
|
|
|
|
return strdup(result.c_str());
|
|
}
|
|
|
|
static int in_add_audio_effect(const struct audio_stream* stream,
|
|
effect_handle_t effect) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", effect=" << effect;
|
|
return 0;
|
|
}
|
|
|
|
static int in_remove_audio_effect(const struct audio_stream* stream,
|
|
effect_handle_t effect) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", effect=" << effect;
|
|
return 0;
|
|
}
|
|
|
|
static int in_set_gain(struct audio_stream_in* stream, float gain) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t in_read(struct audio_stream_in* stream, void* buffer,
|
|
size_t bytes) {
|
|
auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
|
|
std::unique_lock<std::mutex> lock(in->mutex_);
|
|
size_t totalRead = 0;
|
|
|
|
/* Give some time to start up */
|
|
if (!in_state_transition_timeout(in, lock, BluetoothStreamState::STARTING,
|
|
kBluetoothDefaultInputStateTimeoutMs))
|
|
return -EBUSY;
|
|
|
|
if (in->bluetooth_input_->GetState() != BluetoothStreamState::STARTED) {
|
|
LOG(INFO) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " first time bytes=" << bytes;
|
|
|
|
int retval = 0;
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", starting";
|
|
if (in->bluetooth_input_->GetState() == BluetoothStreamState::STANDBY) {
|
|
retval = (in->bluetooth_input_->Start() ? 0 : -EIO);
|
|
} else if (in->bluetooth_input_->GetState() ==
|
|
BluetoothStreamState::SUSPENDING) {
|
|
LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " NOT ready to start?!";
|
|
retval = -EBUSY;
|
|
} else if (in->bluetooth_input_->GetState() ==
|
|
BluetoothStreamState::DISABLED) {
|
|
LOG(WARNING) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " NOT allow to start?!";
|
|
retval = -EINVAL;
|
|
} else {
|
|
LOG(DEBUG) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " started already";
|
|
}
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", starting (start) retval=" << retval;
|
|
|
|
if (retval) {
|
|
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " failed to start";
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
lock.unlock();
|
|
totalRead = in->bluetooth_input_->ReadData(buffer, bytes);
|
|
lock.lock();
|
|
|
|
struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
in->last_read_time_us_ = (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;
|
|
|
|
const size_t frames = totalRead / audio_stream_in_frame_size(stream);
|
|
in->frames_presented_ += frames;
|
|
|
|
return totalRead;
|
|
}
|
|
|
|
static uint32_t in_get_input_frames_lost(struct audio_stream_in* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int in_get_capture_position(const struct audio_stream_in* stream,
|
|
int64_t* frames, int64_t* time) {
|
|
if (stream == NULL || frames == NULL || time == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
|
|
if (in->bluetooth_input_->GetState() == BluetoothStreamState::STANDBY) {
|
|
LOG(WARNING) << __func__ << ": state= " << in->bluetooth_input_->GetState();
|
|
return -ENOSYS;
|
|
}
|
|
|
|
in_calculate_starving_delay_ms(in, frames, time);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int in_start(const struct audio_stream_in* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int in_stop(const struct audio_stream_in* stream) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int in_create_mmap_buffer(const struct audio_stream_in* stream,
|
|
int32_t min_size_frames,
|
|
struct audio_mmap_buffer_info* info) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int in_get_mmap_position(const struct audio_stream_in* stream,
|
|
struct audio_mmap_position* position) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int in_get_active_microphones(
|
|
const struct audio_stream_in* stream,
|
|
struct audio_microphone_characteristic_t* mic_array, size_t* mic_count) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int in_set_microphone_direction(const struct audio_stream_in* stream,
|
|
audio_microphone_direction_t direction) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int in_set_microphone_field_dimension(
|
|
const struct audio_stream_in* stream, float zoom) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_->GetState();
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static void in_update_sink_metadata(struct audio_stream_in* stream,
|
|
const struct sink_metadata* sink_metadata) {
|
|
LOG(INFO) << __func__;
|
|
if (sink_metadata == nullptr || sink_metadata->track_count == 0) {
|
|
return;
|
|
}
|
|
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(INFO) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", " << sink_metadata->track_count << " track(s)";
|
|
|
|
if (!in->is_aidl) {
|
|
LOG(WARNING) << __func__
|
|
<< " is only supported in AIDL but using HIDL now!";
|
|
return;
|
|
}
|
|
static_cast<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl*>(
|
|
in->bluetooth_input_.get())
|
|
->UpdateSinkMetadata(sink_metadata);
|
|
}
|
|
|
|
int adev_open_input_stream(struct audio_hw_device* dev,
|
|
audio_io_handle_t handle, audio_devices_t devices,
|
|
struct audio_config* config,
|
|
struct audio_stream_in** stream_in,
|
|
audio_input_flags_t flags __unused,
|
|
const char* address __unused,
|
|
audio_source_t source __unused) {
|
|
*stream_in = nullptr;
|
|
auto in = std::make_unique<BluetoothStreamIn>();
|
|
if (::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::
|
|
IsAidlAvailable()) {
|
|
in->bluetooth_input_ = std::make_unique<
|
|
::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn>();
|
|
in->is_aidl = true;
|
|
} else {
|
|
in->bluetooth_input_ = std::make_unique<
|
|
::android::bluetooth::audio::hidl::BluetoothAudioPortHidlIn>();
|
|
in->is_aidl = false;
|
|
}
|
|
if (!in->bluetooth_input_->SetUp(devices)) {
|
|
in->bluetooth_input_ = nullptr;
|
|
LOG(ERROR) << __func__ << ": cannot init HAL";
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG(INFO) << __func__ << ": device=" << StringPrintf("%#x", devices);
|
|
|
|
in->stream_in_.common.get_sample_rate = in_get_sample_rate;
|
|
in->stream_in_.common.set_sample_rate = in_set_sample_rate;
|
|
in->stream_in_.common.get_buffer_size = in_get_buffer_size;
|
|
in->stream_in_.common.get_channels = in_get_channels;
|
|
in->stream_in_.common.get_format = in_get_format;
|
|
in->stream_in_.common.set_format = in_set_format;
|
|
in->stream_in_.common.standby = in_standby;
|
|
in->stream_in_.common.dump = in_dump;
|
|
in->stream_in_.common.set_parameters = in_set_parameters;
|
|
in->stream_in_.common.get_parameters = in_get_parameters;
|
|
in->stream_in_.common.add_audio_effect = in_add_audio_effect;
|
|
in->stream_in_.common.remove_audio_effect = in_remove_audio_effect;
|
|
in->stream_in_.set_gain = in_set_gain;
|
|
in->stream_in_.read = in_read;
|
|
in->stream_in_.get_input_frames_lost = in_get_input_frames_lost;
|
|
in->stream_in_.get_capture_position = in_get_capture_position;
|
|
in->stream_in_.start = in_start;
|
|
in->stream_in_.stop = in_stop;
|
|
in->stream_in_.create_mmap_buffer = in_create_mmap_buffer;
|
|
in->stream_in_.get_mmap_position = in_get_mmap_position;
|
|
in->stream_in_.get_active_microphones = in_get_active_microphones;
|
|
in->stream_in_.set_microphone_direction = in_set_microphone_direction;
|
|
in->stream_in_.set_microphone_field_dimension =
|
|
in_set_microphone_field_dimension;
|
|
in->stream_in_.update_sink_metadata = in_update_sink_metadata;
|
|
|
|
if (!in->bluetooth_input_->LoadAudioConfig(config)) {
|
|
LOG(ERROR) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< " failed to get audio config";
|
|
return -EINVAL;
|
|
}
|
|
|
|
in->sample_rate_ = config->sample_rate;
|
|
in->channel_mask_ = config->channel_mask;
|
|
in->format_ = config->format;
|
|
// frame is number of samples per channel
|
|
|
|
size_t preferred_data_interval_us = kBluetoothDefaultInputBufferMs * 1000;
|
|
if (in->bluetooth_input_->GetPreferredDataIntervalUs(
|
|
&preferred_data_interval_us) &&
|
|
preferred_data_interval_us != 0) {
|
|
in->preferred_data_interval_us = preferred_data_interval_us;
|
|
} else {
|
|
in->preferred_data_interval_us = kBluetoothDefaultInputBufferMs * 1000;
|
|
}
|
|
|
|
in->frames_count_ =
|
|
frame_count(in->preferred_data_interval_us, in->sample_rate_);
|
|
in->frames_presented_ = 0;
|
|
|
|
BluetoothStreamIn* in_ptr = in.release();
|
|
*stream_in = &in_ptr->stream_in_;
|
|
LOG(INFO) << __func__ << ": state=" << in_ptr->bluetooth_input_->GetState()
|
|
<< ", sample_rate=" << in_ptr->sample_rate_
|
|
<< ", channels=" << StringPrintf("%#x", in_ptr->channel_mask_)
|
|
<< ", format=" << in_ptr->format_ << ", preferred_data_interval_us="
|
|
<< in_ptr->preferred_data_interval_us
|
|
<< ", frames=" << in_ptr->frames_count_;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void adev_close_input_stream(struct audio_hw_device* dev,
|
|
struct audio_stream_in* stream) {
|
|
auto* in = reinterpret_cast<BluetoothStreamIn*>(stream);
|
|
|
|
if (in->bluetooth_input_->GetState() != BluetoothStreamState::DISABLED) {
|
|
in->bluetooth_input_->Stop();
|
|
}
|
|
|
|
in->bluetooth_input_->TearDown();
|
|
LOG(VERBOSE) << __func__ << ": state=" << in->bluetooth_input_->GetState()
|
|
<< ", stopped";
|
|
|
|
delete in;
|
|
}
|