You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1218 lines
47 KiB
1218 lines
47 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
#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 "stream_apis.h"
|
|
#include "utils.h"
|
|
|
|
using ::android::base::StringPrintf;
|
|
using ::android::bluetooth::audio::BluetoothAudioPortOut;
|
|
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_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(kBluetoothDefaultOutputBufferMs * 1000);
|
|
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_.UpdateMetadata(source_metadata);
|
|
}
|
|
|
|
static size_t samples_per_ticks(size_t milliseconds, uint32_t sample_rate,
|
|
size_t channel_count) {
|
|
return milliseconds * sample_rate * channel_count / 1000;
|
|
}
|
|
|
|
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 = new BluetoothStreamOut{};
|
|
if (!out->bluetooth_output_.SetUp(devices)) {
|
|
delete out;
|
|
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
|
|
out->frames_count_ =
|
|
samples_per_ticks(kBluetoothDefaultOutputBufferMs, out->sample_rate_, 1);
|
|
out->frames_rendered_ = 0;
|
|
out->frames_presented_ = 0;
|
|
|
|
{
|
|
auto* bluetooth_device = reinterpret_cast<BluetoothAudioDevice*>(dev);
|
|
std::lock_guard<std::mutex> guard(bluetooth_device->mutex_);
|
|
bluetooth_device->opened_stream_outs_.push_back(out);
|
|
}
|
|
*stream_out = &out->stream_out_;
|
|
LOG(INFO) << __func__ << ": state=" << out->bluetooth_output_.GetState() << ", sample_rate=" << out->sample_rate_
|
|
<< ", channels=" << StringPrintf("%#x", out->channel_mask_) << ", format=" << out->format_
|
|
<< ", frames=" << out->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 suspending"
|
|
" 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) {
|
|
const auto* in = reinterpret_cast<const BluetoothStreamIn*>(stream);
|
|
LOG(VERBOSE) << __func__
|
|
<< ": NOT HANDLED! state=" << in->bluetooth_input_.GetState();
|
|
}
|
|
|
|
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 = new BluetoothStreamIn{};
|
|
if (!in->bluetooth_input_.SetUp(devices)) {
|
|
delete in;
|
|
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
|
|
in->frames_count_ =
|
|
samples_per_ticks(kBluetoothDefaultInputBufferMs, in->sample_rate_, 1);
|
|
in->frames_presented_ = 0;
|
|
|
|
*stream_in = &in->stream_in_;
|
|
LOG(INFO) << __func__ << ": state=" << in->bluetooth_input_.GetState()
|
|
<< ", sample_rate=" << in->sample_rate_
|
|
<< ", channels=" << StringPrintf("%#x", in->channel_mask_)
|
|
<< ", format=" << in->format_ << ", frames=" << in->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;
|
|
}
|