Merge changes Ic51d603d,Ia50def0d,I22f65b8b,I8ce9f230,Id8455eb1, ... into main

* changes:
  audio: Query minimum buffer size before opening streams
  audio: Implement getters for hardware mixer controls
  audio: Clean up and fix the bluetooth HAL module
  audio: Provide a way for Module to specify nominal latency
  audio: Fix default remote submix HAL implementation and VTS
  CSD: Add default AIDL HAL implementation
This commit is contained in:
Mikhail Naganov
2023-11-06 17:01:00 +00:00
committed by Gerrit Code Review
31 changed files with 1204 additions and 662 deletions

View File

@@ -174,4 +174,12 @@ constexpr U makeBitPositionFlagMask(std::initializer_list<E> flags) {
return result;
}
constexpr int32_t frameCountFromDurationUs(long durationUs, int32_t sampleRateHz) {
return (durationUs * sampleRateHz) / 1000000LL;
}
constexpr int32_t frameCountFromDurationMs(int32_t durationMs, int32_t sampleRateHz) {
return frameCountFromDurationUs(durationMs * 1000, sampleRateHz);
}
} // namespace aidl::android::hardware::audio::common

View File

@@ -18,7 +18,6 @@
#include <set>
#define LOG_TAG "AHAL_Module"
#include <Utils.h>
#include <aidl/android/media/audio/common/AudioInputFlags.h>
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
#include <android-base/logging.h>
@@ -35,6 +34,7 @@
#include "core-impl/SoundDose.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::frameCountFromDurationMs;
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
using aidl::android::hardware::audio::common::isBitPositionFlagSet;
using aidl::android::hardware::audio::common::isValidAudioMode;
@@ -202,15 +202,17 @@ ndk::ScopedAStatus Module::createStreamContext(
LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (in_bufferSizeFrames < kMinimumStreamBufferSizeFrames) {
LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
<< ", must be at least " << kMinimumStreamBufferSizeFrames;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto& configs = getConfig().portConfigs;
auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
// Since this is a private method, it is assumed that
// validity of the portConfigId has already been checked.
const int32_t minimumStreamBufferSizeFrames = calculateBufferSizeFrames(
getNominalLatencyMs(*portConfigIt), portConfigIt->sampleRate.value().value);
if (in_bufferSizeFrames < minimumStreamBufferSizeFrames) {
LOG(ERROR) << __func__ << ": insufficient buffer size " << in_bufferSizeFrames
<< ", must be at least " << minimumStreamBufferSizeFrames;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const size_t frameSize =
getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
if (frameSize == 0) {
@@ -238,11 +240,12 @@ ndk::ScopedAStatus Module::createStreamContext(
StreamContext temp(
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
portConfigIt->portId, portConfigIt->format.value(),
portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
portConfigIt->format.value(), portConfigIt->channelMask.value(),
portConfigIt->sampleRate.value().value, flags, getNominalLatencyMs(*portConfigIt),
portConfigIt->ext.get<AudioPortExt::mix>().handle,
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
asyncCallback, outEventCallback, params);
asyncCallback, outEventCallback,
std::weak_ptr<sounddose::StreamDataProcessorInterface>{}, params);
if (temp.isValid()) {
*out_context = std::move(temp);
} else {
@@ -358,6 +361,12 @@ std::unique_ptr<Module::Configuration> Module::initializeConfig() {
return internal::getConfiguration(getType());
}
int32_t Module::getNominalLatencyMs(const AudioPortConfig&) {
// Arbitrary value. Implementations must override this method to provide their actual latency.
static constexpr int32_t kLatencyMs = 5;
return kLatencyMs;
}
std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
std::vector<AudioRoute*> result;
auto& routes = getConfig().routes;
@@ -608,32 +617,30 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
std::vector<AudioRoute*> routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId);
std::set<int32_t> routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts);
if (hasDynamicProfilesOnly(connectedPort.profiles)) {
if (!mDebug.simulateDeviceConnections) {
RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
} else {
auto& connectedProfiles = getConfig().connectedProfiles;
if (auto connectedProfilesIt = connectedProfiles.find(templateId);
connectedProfilesIt != connectedProfiles.end()) {
connectedPort.profiles = connectedProfilesIt->second;
}
if (!mDebug.simulateDeviceConnections) {
// Even if the device port has static profiles, the HAL module might need to update
// them, or abort the connection process.
RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
} else if (hasDynamicProfilesOnly(connectedPort.profiles)) {
auto& connectedProfiles = getConfig().connectedProfiles;
if (auto connectedProfilesIt = connectedProfiles.find(templateId);
connectedProfilesIt != connectedProfiles.end()) {
connectedPort.profiles = connectedProfilesIt->second;
}
if (hasDynamicProfilesOnly(connectedPort.profiles)) {
// Possible case 2. Check if all routable mix ports have static profiles.
if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
[&routableMixPortIds](const auto& p) {
return routableMixPortIds.count(p.id) >
0 &&
hasDynamicProfilesOnly(p.profiles);
});
dynamicMixPortIt != ports.end()) {
LOG(ERROR) << __func__
<< ": connected port only has dynamic profiles after connecting "
<< "external device " << connectedPort.toString() << ", and there exist "
<< "a routable mix port with dynamic profiles: "
<< dynamicMixPortIt->toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
if (hasDynamicProfilesOnly(connectedPort.profiles)) {
// Possible case 2. Check if all routable mix ports have static profiles.
if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
[&routableMixPortIds](const auto& p) {
return routableMixPortIds.count(p.id) > 0 &&
hasDynamicProfilesOnly(p.profiles);
});
dynamicMixPortIt != ports.end()) {
LOG(ERROR) << __func__ << ": connected port only has dynamic profiles after connecting "
<< "external device " << connectedPort.toString() << ", and there exist "
<< "a routable mix port with dynamic profiles: "
<< dynamicMixPortIt->toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
@@ -964,11 +971,21 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
// Find the highest sample rate among mix port configs.
std::map<int32_t, AudioPortConfig*> sampleRates;
std::vector<AudioPortConfig*>& mixPortConfigs =
sources[0]->ext.getTag() == AudioPortExt::mix ? sources : sinks;
for (auto mix : mixPortConfigs) {
sampleRates.emplace(mix->sampleRate.value().value, mix);
}
*_aidl_return = in_requested;
_aidl_return->minimumStreamBufferSizeFrames = kMinimumStreamBufferSizeFrames;
auto maxSampleRateIt = std::max_element(sampleRates.begin(), sampleRates.end());
const int32_t latencyMs = getNominalLatencyMs(*(maxSampleRateIt->second));
_aidl_return->minimumStreamBufferSizeFrames =
calculateBufferSizeFrames(latencyMs, maxSampleRateIt->first);
_aidl_return->latenciesMs.clear();
_aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
_aidl_return->sinkPortConfigIds.size(), kLatencyMs);
_aidl_return->sinkPortConfigIds.size(), latencyMs);
AudioPatch oldPatch{};
if (existing == patches.end()) {
_aidl_return->id = getConfig().nextPatchId++;
@@ -1210,7 +1227,7 @@ ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
// Reset master mute if it failed.
onMasterMuteChanged(mMasterMute);
}
return std::move(result);
return result;
}
ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
@@ -1232,7 +1249,7 @@ ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
<< "), error=" << result;
onMasterVolumeChanged(mMasterVolume);
}
return std::move(result);
return result;
}
LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@@ -1553,11 +1570,6 @@ std::vector<MicrophoneInfo> Module::getMicrophoneInfos() {
return result;
}
Module::BtProfileHandles Module::getBtProfileManagerHandles() {
return std::make_tuple(std::weak_ptr<IBluetooth>(), std::weak_ptr<IBluetoothA2dp>(),
std::weak_ptr<IBluetoothLe>());
}
ndk::ScopedAStatus Module::bluetoothParametersUpdated() {
return mStreams.bluetoothParametersUpdated();
}

View File

@@ -58,4 +58,12 @@ ndk::ScopedAStatus ModulePrimary::createOutputStream(
offloadInfo);
}
int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) {
// 85 ms is chosen considering 4096 frames @ 48 kHz. This is the value which allows
// the virtual Android device implementation to pass CTS. Hardware implementations
// should have significantly lower latency.
static constexpr int32_t kLatencyMs = 85;
return kLatencyMs;
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -90,6 +90,14 @@ bool StreamContext::isValid() const {
return true;
}
void StreamContext::startStreamDataProcessor() {
auto streamDataProcessor = mStreamDataProcessor.lock();
if (streamDataProcessor != nullptr) {
streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout),
mFormat);
}
}
void StreamContext::reset() {
mCommandMQ.reset();
mReplyMQ.reset();
@@ -130,7 +138,7 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
reply->status = STATUS_OK;
if (isConnected) {
reply->observable.frames = mContext->getFrameCount();
reply->observable.timeNs = ::android::elapsedRealtimeNano();
reply->observable.timeNs = ::android::uptimeNanos();
if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
return;
}
@@ -307,7 +315,7 @@ bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply
const size_t frameSize = mContext->getFrameSize();
size_t actualFrameCount = 0;
bool fatal = false;
int32_t latency = Module::kLatencyMs;
int32_t latency = mContext->getNominalLatencyMs();
if (isConnected) {
if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
&actualFrameCount, &latency);
@@ -573,7 +581,7 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep
const size_t readByteCount = dataMQ->availableToRead();
const size_t frameSize = mContext->getFrameSize();
bool fatal = false;
int32_t latency = Module::kLatencyMs;
int32_t latency = mContext->getNominalLatencyMs();
if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
const bool isConnected = mIsConnected;
LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
@@ -593,6 +601,10 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep
fatal = true;
LOG(ERROR) << __func__ << ": write failed: " << status;
}
auto streamDataProcessor = mContext->getStreamDataProcessor().lock();
if (streamDataProcessor != nullptr) {
streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize);
}
} else {
if (mContext->getAsyncCallback() == nullptr) {
usleep(3000); // Simulate blocking transfer delay.
@@ -836,7 +848,7 @@ ndk::ScopedAStatus StreamIn::setHwGain(const std::vector<float>& in_channelGains
}
StreamInHwGainHelper::StreamInHwGainHelper(const StreamContext* context)
: mChannelCount(getChannelCount(context->getChannelLayout())) {}
: mChannelCount(getChannelCount(context->getChannelLayout())), mHwGains(mChannelCount, 0.0f) {}
ndk::ScopedAStatus StreamInHwGainHelper::getHwGainImpl(std::vector<float>* _aidl_return) {
*_aidl_return = mHwGains;
@@ -967,7 +979,8 @@ ndk::ScopedAStatus StreamOut::selectPresentation(int32_t in_presentationId, int3
}
StreamOutHwVolumeHelper::StreamOutHwVolumeHelper(const StreamContext* context)
: mChannelCount(getChannelCount(context->getChannelLayout())) {}
: mChannelCount(getChannelCount(context->getChannelLayout())),
mHwVolumes(mChannelCount, 0.0f) {}
ndk::ScopedAStatus StreamOutHwVolumeHelper::getHwVolumeImpl(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;

View File

@@ -20,9 +20,24 @@
#define LOG_TAG "AHAL_AlsaMixer"
#include <android-base/logging.h>
#include <android/binder_status.h>
#include <error/expected_utils.h>
#include "Mixer.h"
namespace ndk {
// This enables use of 'error/expected_utils' for ScopedAStatus.
inline bool errorIsOk(const ScopedAStatus& s) {
return s.isOk();
}
inline std::string errorToString(const ScopedAStatus& s) {
return s.getDescription();
}
} // namespace ndk
namespace aidl::android::hardware::audio::core::alsa {
// static
@@ -93,6 +108,36 @@ Mixer::~Mixer() {
}
}
ndk::ScopedAStatus Mixer::getMasterMute(bool* muted) {
return getMixerControlMute(MASTER_SWITCH, muted);
}
ndk::ScopedAStatus Mixer::getMasterVolume(float* volume) {
return getMixerControlVolume(MASTER_VOLUME, volume);
}
ndk::ScopedAStatus Mixer::getMicGain(float* gain) {
return getMixerControlVolume(MIC_GAIN, gain);
}
ndk::ScopedAStatus Mixer::getMicMute(bool* muted) {
return getMixerControlMute(MIC_SWITCH, muted);
}
ndk::ScopedAStatus Mixer::getVolumes(std::vector<float>* volumes) {
struct mixer_ctl* mctl;
RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
std::vector<int> percents;
std::lock_guard l(mMixerAccess);
if (int err = getMixerControlPercent(mctl, &percents); err != 0) {
LOG(ERROR) << __func__ << ": failed to get volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::transform(percents.begin(), percents.end(), std::back_inserter(*volumes),
[](int percent) -> float { return std::clamp(percent / 100.0f, 0.0f, 1.0f); });
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
return setMixerControlMute(MASTER_SWITCH, muted);
}
@@ -110,35 +155,70 @@ ndk::ScopedAStatus Mixer::setMicMute(bool muted) {
}
ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
if (!isValid()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
auto it = mMixerControls.find(Mixer::HW_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
struct mixer_ctl* mctl;
RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
std::vector<int> percents;
std::transform(
volumes.begin(), volumes.end(), std::back_inserter(percents),
[](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); });
std::lock_guard l(mMixerAccess);
if (int err = setMixerControlPercent(it->second, percents); err != 0) {
if (int err = setMixerControlPercent(mctl, percents); err != 0) {
LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) {
ndk::ScopedAStatus Mixer::findControl(Control ctl, struct mixer_ctl** result) {
if (!isValid()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
auto it = mMixerControls.find(ctl);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
if (auto it = mMixerControls.find(ctl); it != mMixerControls.end()) {
*result = it->second;
return ndk::ScopedAStatus::ok();
}
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus Mixer::getMixerControlMute(Control ctl, bool* muted) {
struct mixer_ctl* mctl;
RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
std::lock_guard l(mMixerAccess);
if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) {
std::vector<int> mutedValues;
if (int err = getMixerControlValues(mctl, &mutedValues); err != 0) {
LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mutedValues.empty()) {
LOG(ERROR) << __func__ << ": got no values for " << ctl;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
*muted = mutedValues[0] != 0;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Mixer::getMixerControlVolume(Control ctl, float* volume) {
struct mixer_ctl* mctl;
RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
std::lock_guard l(mMixerAccess);
std::vector<int> percents;
if (int err = getMixerControlPercent(mctl, &percents); err != 0) {
LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (percents.empty()) {
LOG(ERROR) << __func__ << ": got no values for " << ctl;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
*volume = std::clamp(percents[0] / 100.0f, 0.0f, 1.0f);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Mixer::setMixerControlMute(Control ctl, bool muted) {
struct mixer_ctl* mctl;
RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
std::lock_guard l(mMixerAccess);
if (int err = setMixerControlValue(mctl, muted ? 0 : 1); err != 0) {
LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
@@ -146,54 +226,72 @@ ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) {
}
ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) {
if (!isValid()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
auto it = mMixerControls.find(ctl);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
struct mixer_ctl* mctl;
RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl));
volume = std::clamp(volume, 0.0f, 1.0f);
std::lock_guard l(mMixerAccess);
if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) {
if (int err = setMixerControlPercent(mctl, std::floor(volume * 100)); err != 0) {
LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
int Mixer::getMixerControlPercent(struct mixer_ctl* ctl, std::vector<int>* percents) {
const unsigned int n = mixer_ctl_get_num_values(ctl);
percents->resize(n);
for (unsigned int id = 0; id < n; id++) {
if (int valueOrError = mixer_ctl_get_percent(ctl, id); valueOrError >= 0) {
(*percents)[id] = valueOrError;
} else {
return valueOrError;
}
}
return 0;
}
int Mixer::getMixerControlValues(struct mixer_ctl* ctl, std::vector<int>* values) {
const unsigned int n = mixer_ctl_get_num_values(ctl);
values->resize(n);
for (unsigned int id = 0; id < n; id++) {
if (int valueOrError = mixer_ctl_get_value(ctl, id); valueOrError >= 0) {
(*values)[id] = valueOrError;
} else {
return valueOrError;
}
}
return 0;
}
int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) {
int ret = 0;
const unsigned int n = mixer_ctl_get_num_values(ctl);
for (unsigned int id = 0; id < n; id++) {
if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) {
ret = error;
return error;
}
}
return ret;
return 0;
}
int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents) {
int ret = 0;
const unsigned int n = mixer_ctl_get_num_values(ctl);
for (unsigned int id = 0; id < n; id++) {
if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0);
error != 0) {
ret = error;
return error;
}
}
return ret;
return 0;
}
int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) {
int ret = 0;
const unsigned int n = mixer_ctl_get_num_values(ctl);
for (unsigned int id = 0; id < n; id++) {
if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) {
ret = error;
return error;
}
}
return ret;
return 0;
}
} // namespace aidl::android::hardware::audio::core::alsa

View File

@@ -39,6 +39,11 @@ class Mixer {
bool isValid() const { return mMixer != nullptr; }
ndk::ScopedAStatus getMasterMute(bool* muted);
ndk::ScopedAStatus getMasterVolume(float* volume);
ndk::ScopedAStatus getMicGain(float* gain);
ndk::ScopedAStatus getMicMute(bool* muted);
ndk::ScopedAStatus getVolumes(std::vector<float>* volumes);
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
ndk::ScopedAStatus setMicGain(float gain);
@@ -60,9 +65,16 @@ class Mixer {
static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
static Controls initializeMixerControls(struct mixer* mixer);
ndk::ScopedAStatus findControl(Control ctl, struct mixer_ctl** result);
ndk::ScopedAStatus getMixerControlMute(Control ctl, bool* muted);
ndk::ScopedAStatus getMixerControlVolume(Control ctl, float* volume);
ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted);
ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume);
int getMixerControlPercent(struct mixer_ctl* ctl, std::vector<int>* percents)
REQUIRES(mMixerAccess);
int getMixerControlValues(struct mixer_ctl* ctl, std::vector<int>* values)
REQUIRES(mMixerAccess);
int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess);
int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents)
REQUIRES(mMixerAccess);

View File

@@ -119,7 +119,7 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea
::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no opened devices";
LOG(WARNING) << __func__ << ": no opened devices";
return ::android::NO_INIT;
}
// Since the proxy can only count transferred frames since its creation,

View File

@@ -19,11 +19,25 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <audio_utils/primitives.h>
#include <inttypes.h>
#include <log/log.h>
#include "BluetoothAudioSessionControl.h"
#include "core-impl/DevicePortProxy.h"
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::hardware::bluetooth::audio::AudioConfiguration;
using aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
using aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
using aidl::android::hardware::bluetooth::audio::ChannelMode;
using aidl::android::hardware::bluetooth::audio::PcmConfiguration;
using aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
using aidl::android::hardware::bluetooth::audio::PresentationPosition;
using aidl::android::hardware::bluetooth::audio::SessionType;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using android::base::StringPrintf;
namespace android::bluetooth::audio::aidl {
namespace {
@@ -33,20 +47,6 @@ constexpr unsigned int kMaxWaitingTimeMs = 4500;
} // namespace
using ::aidl::android::hardware::audio::common::SinkMetadata;
using ::aidl::android::hardware::audio::common::SourceMetadata;
using ::aidl::android::hardware::bluetooth::audio::AudioConfiguration;
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSessionControl;
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioStatus;
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
using ::aidl::android::hardware::bluetooth::audio::PortStatusCallbacks;
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
using ::aidl::android::hardware::bluetooth::audio::SessionType;
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioDeviceType;
using ::android::base::StringPrintf;
std::ostream& operator<<(std::ostream& os, const BluetoothStreamState& state) {
switch (state) {
case BluetoothStreamState::DISABLED:

View File

@@ -18,50 +18,56 @@
#include <android-base/logging.h>
#include "BluetoothAudioSessionControl.h"
#include "BluetoothAudioSession.h"
#include "core-impl/ModuleBluetooth.h"
#include "core-impl/StreamBluetooth.h"
namespace aidl::android::hardware::audio::core {
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::MicrophoneInfo;
using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
using ::aidl::android::hardware::audio::common::SinkMetadata;
using ::aidl::android::hardware::audio::common::SourceMetadata;
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession;
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioDeviceType;
using ::aidl::android::media::audio::common::AudioOffloadInfo;
using ::aidl::android::media::audio::common::AudioPort;
using ::aidl::android::media::audio::common::AudioPortExt;
using ::aidl::android::media::audio::common::MicrophoneInfo;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
namespace aidl::android::hardware::audio::core {
ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
if (!mBluetoothA2dp) {
auto handle = ndk::SharedRefBase::make<BluetoothA2dp>();
handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
mBluetoothA2dp = handle;
}
*_aidl_return = mBluetoothA2dp.getInstance();
*_aidl_return = getBtA2dp().getInstance();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleBluetooth::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
*_aidl_return = getBtLe().getInstance();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
ChildInterface<BluetoothA2dp>& ModuleBluetooth::getBtA2dp() {
if (!mBluetoothA2dp) {
auto handle = ndk::SharedRefBase::make<BluetoothA2dp>();
handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
mBluetoothA2dp = handle;
}
return mBluetoothA2dp;
}
ChildInterface<BluetoothLe>& ModuleBluetooth::getBtLe() {
if (!mBluetoothLe) {
auto handle = ndk::SharedRefBase::make<BluetoothLe>();
handle->registerHandler(std::bind(&ModuleBluetooth::bluetoothParametersUpdated, this));
mBluetoothLe = handle;
}
*_aidl_return = mBluetoothLe.getInstance();
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
return mBluetoothLe;
}
Module::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
return std::make_tuple(std::weak_ptr<IBluetooth>(), mBluetoothA2dp.getInstance(),
mBluetoothLe.getInstance());
ModuleBluetooth::BtProfileHandles ModuleBluetooth::getBtProfileManagerHandles() {
return std::make_tuple(std::weak_ptr<IBluetooth>(), getBtA2dp().getPtr(), getBtLe().getPtr());
}
ndk::ScopedAStatus ModuleBluetooth::getMicMute(bool* _aidl_return __unused) {
@@ -101,30 +107,35 @@ ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audio
if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) {
bool isA2dpEnabled = false;
if (!!mBluetoothA2dp) {
RETURN_STATUS_IF_ERROR(mBluetoothA2dp.getInstance()->isEnabled(&isA2dpEnabled));
RETURN_STATUS_IF_ERROR((*mBluetoothA2dp).isEnabled(&isA2dpEnabled));
}
LOG(DEBUG) << __func__ << ": isA2dpEnabled: " << isA2dpEnabled;
return isA2dpEnabled ? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) {
bool isLeEnabled = false;
if (!!mBluetoothLe) {
RETURN_STATUS_IF_ERROR(mBluetoothLe.getInstance()->isEnabled(&isLeEnabled));
RETURN_STATUS_IF_ERROR((*mBluetoothLe).isEnabled(&isLeEnabled));
}
LOG(DEBUG) << __func__ << ": isLeEnabled: " << isLeEnabled;
return isLeEnabled ? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
description.type == AudioDeviceType::OUT_HEARING_AID) {
// Hearing aids can use a number of profiles, thus the only way to check
// connectivity is to try to talk to the BT HAL.
if (!BluetoothAudioSession::IsAidlAvailable()) {
if (!::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession::
IsAidlAvailable()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::shared_ptr<BluetoothAudioPortAidl> proxy = std::shared_ptr<BluetoothAudioPortAidl>(
std::make_shared<BluetoothAudioPortAidlOut>());
if (proxy->registerPort(description)) {
LOG(DEBUG) << __func__ << ": registered hearing aid port";
proxy->unregisterPort();
return ndk::ScopedAStatus::ok();
}
LOG(DEBUG) << __func__ << ": failed to register hearing aid port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString();

View File

@@ -20,52 +20,49 @@
#include <android-base/logging.h>
#include <audio_utils/clock.h>
#include "BluetoothAudioSessionControl.h"
#include "BluetoothAudioSession.h"
#include "core-impl/StreamBluetooth.h"
namespace aidl::android::hardware::audio::core {
using aidl::android::hardware::audio::common::frameCountFromDurationUs;
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::hardware::audio::core::VendorParameter;
using aidl::android::hardware::bluetooth::audio::ChannelMode;
using aidl::android::hardware::bluetooth::audio::PcmConfiguration;
using aidl::android::hardware::bluetooth::audio::PresentationPosition;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
using android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
using android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
using android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
using android::bluetooth::audio::aidl::BluetoothStreamState;
using ::aidl::android::hardware::audio::common::SinkMetadata;
using ::aidl::android::hardware::audio::common::SourceMetadata;
using ::aidl::android::hardware::audio::core::VendorParameter;
using ::aidl::android::hardware::bluetooth::audio::ChannelMode;
using ::aidl::android::hardware::bluetooth::audio::PcmConfiguration;
using ::aidl::android::hardware::bluetooth::audio::PresentationPosition;
using ::aidl::android::media::audio::common::AudioChannelLayout;
using ::aidl::android::media::audio::common::AudioDevice;
using ::aidl::android::media::audio::common::AudioDeviceAddress;
using ::aidl::android::media::audio::common::AudioFormatDescription;
using ::aidl::android::media::audio::common::AudioFormatType;
using ::aidl::android::media::audio::common::AudioOffloadInfo;
using ::aidl::android::media::audio::common::MicrophoneDynamicInfo;
using ::aidl::android::media::audio::common::MicrophoneInfo;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlIn;
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
using ::android::bluetooth::audio::aidl::BluetoothStreamState;
namespace aidl::android::hardware::audio::core {
constexpr int kBluetoothDefaultInputBufferMs = 20;
constexpr int kBluetoothDefaultOutputBufferMs = 10;
// constexpr int kBluetoothSpatializerOutputBufferMs = 10;
size_t getFrameCount(uint64_t durationUs, uint32_t sampleRate) {
return (durationUs * sampleRate) / 1000000;
}
// pcm configuration params are not really used by the module
StreamBluetooth::StreamBluetooth(StreamContext* context, const Metadata& metadata,
Module::BtProfileHandles&& btHandles)
ModuleBluetooth::BtProfileHandles&& btHandles)
: StreamCommonImpl(context, metadata),
mSampleRate(getContext().getSampleRate()),
mChannelLayout(getContext().getChannelLayout()),
mFormat(getContext().getFormat()),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)),
mBluetoothA2dp(std::move(std::get<Module::BtInterface::BTA2DP>(btHandles))),
mBluetoothLe(std::move(std::get<Module::BtInterface::BTLE>(btHandles))) {
mBluetoothA2dp(std::move(std::get<ModuleBluetooth::BtInterface::BTA2DP>(btHandles))),
mBluetoothLe(std::move(std::get<ModuleBluetooth::BtInterface::BTLE>(btHandles))) {
mPreferredDataIntervalUs =
mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs;
mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
(mIsInput ? kBluetoothDefaultInputBufferMs : kBluetoothDefaultOutputBufferMs) * 1000;
mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate);
mIsInitialized = false;
mIsReadyToClose = false;
}
@@ -226,7 +223,7 @@ bool StreamBluetooth::checkConfigParams(
if (config.dataIntervalUs > 0) {
mPreferredDataIntervalUs =
std::min((int32_t)mPreferredDataIntervalUs, config.dataIntervalUs);
mPreferredFrameCount = getFrameCount(mPreferredDataIntervalUs, mSampleRate);
mPreferredFrameCount = frameCountFromDurationUs(mPreferredDataIntervalUs, mSampleRate);
}
return true;
}
@@ -318,7 +315,7 @@ ndk::ScopedAStatus StreamBluetooth::bluetoothParametersUpdated() {
StreamInBluetooth::StreamInBluetooth(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
Module::BtProfileHandles&& btProfileHandles)
ModuleBluetooth::BtProfileHandles&& btProfileHandles)
: StreamIn(std::move(context), microphones),
StreamBluetooth(&mContextInstance, sinkMetadata, std::move(btProfileHandles)) {}
@@ -331,7 +328,7 @@ ndk::ScopedAStatus StreamInBluetooth::getActiveMicrophones(
StreamOutBluetooth::StreamOutBluetooth(StreamContext&& context,
const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo,
Module::BtProfileHandles&& btProfileHandles)
ModuleBluetooth::BtProfileHandles&& btProfileHandles)
: StreamOut(std::move(context), offloadInfo),
StreamBluetooth(&mContextInstance, sourceMetadata, std::move(btProfileHandles)) {}

View File

@@ -46,9 +46,9 @@ class Bluetooth : public BnBluetooth {
class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler {
public:
BluetoothA2dp() = default;
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
private:
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setEnabled(bool in_enabled) override;
ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override;
ndk::ScopedAStatus reconfigureOffload(
@@ -61,9 +61,9 @@ class BluetoothA2dp : public BnBluetoothA2dp, public ParamChangeHandler {
class BluetoothLe : public BnBluetoothLe, public ParamChangeHandler {
public:
BluetoothLe() = default;
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
private:
ndk::ScopedAStatus isEnabled(bool* _aidl_return) override;
ndk::ScopedAStatus setEnabled(bool in_enabled) override;
ndk::ScopedAStatus supportsOffloadReconfiguration(bool* _aidl_return) override;
ndk::ScopedAStatus reconfigureOffload(

View File

@@ -40,6 +40,7 @@ struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
explicit operator bool() const { return !!this->first; }
C& operator*() const { return *(this->first); }
C* operator->() const { return this->first; }
std::shared_ptr<C> getPtr() { return this->first; }
// Use 'getInstance' when returning the interface instance.
std::shared_ptr<C> getInstance() {
(void)getBinder();

View File

@@ -25,11 +25,10 @@
#include <aidl/android/hardware/audio/common/SourceMetadata.h>
#include <aidl/android/hardware/bluetooth/audio/BluetoothAudioStatus.h>
#include <aidl/android/hardware/bluetooth/audio/PcmConfiguration.h>
#include <aidl/android/hardware/bluetooth/audio/PresentationPosition.h>
#include <aidl/android/hardware/bluetooth/audio/SessionType.h>
#include <aidl/android/media/audio/common/AudioDeviceDescription.h>
#include "BluetoothAudioSessionControl.h"
namespace android::bluetooth::audio::aidl {
enum class BluetoothStreamState : uint8_t {
@@ -239,4 +238,4 @@ class BluetoothAudioPortAidlIn : public BluetoothAudioPortAidl {
size_t readData(void* buffer, size_t bytes) const override;
};
} // namespace android::bluetooth::audio::aidl
} // namespace android::bluetooth::audio::aidl

View File

@@ -22,6 +22,7 @@
#include <optional>
#include <set>
#include <Utils.h>
#include <aidl/android/hardware/audio/core/BnModule.h>
#include "core-impl/ChildInterface.h"
@@ -45,13 +46,6 @@ class Module : public BnModule {
int32_t nextPatchId = 1;
};
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB, BLUETOOTH };
enum BtInterface : int { BTCONF, BTA2DP, BTLE };
typedef std::tuple<std::weak_ptr<IBluetooth>, std::weak_ptr<IBluetoothA2dp>,
std::weak_ptr<IBluetoothLe>>
BtProfileHandles;
// This value is used by default for all AudioPatches and reported by all streams.
static constexpr int32_t kLatencyMs = 10;
static std::shared_ptr<Module> createInstance(Type type) {
return createInstance(type, std::make_unique<Configuration>());
@@ -145,8 +139,6 @@ class Module : public BnModule {
ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
// This value is used for all AudioPatches.
static constexpr int32_t kMinimumStreamBufferSizeFrames = 256;
// The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
@@ -175,7 +167,7 @@ class Module : public BnModule {
bool mMicMute = false;
bool mMasterMute = false;
float mMasterVolume = 1.0f;
ChildInterface<sounddose::ISoundDose> mSoundDose;
ChildInterface<sounddose::SoundDose> mSoundDose;
std::optional<bool> mIsMmapSupported;
protected:
@@ -207,8 +199,19 @@ class Module : public BnModule {
virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
virtual std::vector<::aidl::android::media::audio::common::MicrophoneInfo> getMicrophoneInfos();
virtual std::unique_ptr<Configuration> initializeConfig();
virtual int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig);
// Utility and helper functions accessible to subclasses.
static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) {
const int32_t rawSizeFrames =
aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs,
sampleRateHz);
int32_t powerOf2 = 1;
while (powerOf2 < rawSizeFrames) powerOf2 <<= 1;
return powerOf2;
}
ndk::ScopedAStatus bluetoothParametersUpdated();
void cleanUpPatch(int32_t patchId);
ndk::ScopedAStatus createStreamContext(
@@ -222,7 +225,6 @@ class Module : public BnModule {
ndk::ScopedAStatus findPortIdForNewStream(
int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
std::vector<AudioRoute*> getAudioRoutesForAudioPortImpl(int32_t portId);
virtual BtProfileHandles getBtProfileManagerHandles();
Configuration& getConfig();
const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; }
bool getMasterMute() const { return mMasterMute; }

View File

@@ -23,11 +23,18 @@ namespace aidl::android::hardware::audio::core {
class ModuleBluetooth final : public Module {
public:
enum BtInterface : int { BTSCO, BTA2DP, BTLE };
typedef std::tuple<std::weak_ptr<IBluetooth>, std::weak_ptr<IBluetoothA2dp>,
std::weak_ptr<IBluetoothLe>>
BtProfileHandles;
ModuleBluetooth(std::unique_ptr<Configuration>&& config)
: Module(Type::BLUETOOTH, std::move(config)) {}
private:
BtProfileHandles getBtProfileManagerHandles() override;
ChildInterface<BluetoothA2dp>& getBtA2dp();
ChildInterface<BluetoothLe>& getBtLe();
BtProfileHandles getBtProfileManagerHandles();
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
@@ -50,8 +57,8 @@ class ModuleBluetooth final : public Module {
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
ChildInterface<IBluetoothLe> mBluetoothLe;
ChildInterface<BluetoothA2dp> mBluetoothA2dp;
ChildInterface<BluetoothLe> mBluetoothLe;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -39,6 +39,8 @@ class ModulePrimary final : public Module {
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
private:
ChildInterface<ITelephony> mTelephony;

View File

@@ -50,6 +50,9 @@ class ModuleRemoteSubmix : public Module {
override;
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
int32_t getNominalLatencyMs(
const ::aidl::android::media::audio::common::AudioPortConfig& portConfig) override;
// TODO(b/307586684): Report proper minimum stream buffer size by overriding 'setAudioPatch'.
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -20,11 +20,23 @@
#include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
using aidl::android::media::audio::common::AudioDevice;
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
namespace aidl::android::hardware::audio::core::sounddose {
// Interface used for processing the data received by a stream.
class StreamDataProcessorInterface {
public:
virtual ~StreamDataProcessorInterface() = default;
virtual void startDataProcessor(
uint32_t samplerate, uint32_t channelCount,
const ::aidl::android::media::audio::common::AudioFormatDescription& format) = 0;
virtual void setAudioDevice(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice) = 0;
virtual void process(const void* buffer, size_t size) = 0;
};
class SoundDose : public BnSoundDose {
public:
SoundDose() : mRs2Value(DEFAULT_MAX_RS2){};

View File

@@ -44,6 +44,7 @@
#include <utils/Errors.h>
#include "core-impl/ChildInterface.h"
#include "core-impl/SoundDose.h"
#include "core-impl/utils.h"
namespace aidl::android::hardware::audio::core {
@@ -80,59 +81,28 @@ class StreamContext {
StreamContext() = default;
StreamContext(std::unique_ptr<CommandMQ> commandMQ, std::unique_ptr<ReplyMQ> replyMQ,
int portId,
const ::aidl::android::media::audio::common::AudioFormatDescription& format,
const ::aidl::android::media::audio::common::AudioChannelLayout& channelLayout,
int sampleRate, const ::aidl::android::media::audio::common::AudioIoFlags& flags,
int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
int32_t nominalLatencyMs, int32_t mixPortHandle, std::unique_ptr<DataMQ> dataMQ,
std::shared_ptr<IStreamCallback> asyncCallback,
std::shared_ptr<IStreamOutEventCallback> outEventCallback,
std::weak_ptr<sounddose::StreamDataProcessorInterface> streamDataProcessor,
DebugParameters debugParameters)
: mCommandMQ(std::move(commandMQ)),
mInternalCommandCookie(std::rand()),
mReplyMQ(std::move(replyMQ)),
mPortId(portId),
mFormat(format),
mChannelLayout(channelLayout),
mSampleRate(sampleRate),
mFlags(flags),
mNominalLatencyMs(nominalLatencyMs),
mMixPortHandle(mixPortHandle),
mDataMQ(std::move(dataMQ)),
mAsyncCallback(asyncCallback),
mOutEventCallback(outEventCallback),
mStreamDataProcessor(streamDataProcessor),
mDebugParameters(debugParameters) {}
StreamContext(StreamContext&& other)
: mCommandMQ(std::move(other.mCommandMQ)),
mInternalCommandCookie(other.mInternalCommandCookie),
mReplyMQ(std::move(other.mReplyMQ)),
mPortId(other.mPortId),
mFormat(other.mFormat),
mChannelLayout(other.mChannelLayout),
mSampleRate(other.mSampleRate),
mFlags(std::move(other.mFlags)),
mMixPortHandle(other.mMixPortHandle),
mDataMQ(std::move(other.mDataMQ)),
mAsyncCallback(std::move(other.mAsyncCallback)),
mOutEventCallback(std::move(other.mOutEventCallback)),
mDebugParameters(std::move(other.mDebugParameters)),
mFrameCount(other.mFrameCount) {}
StreamContext& operator=(StreamContext&& other) {
mCommandMQ = std::move(other.mCommandMQ);
mInternalCommandCookie = other.mInternalCommandCookie;
mReplyMQ = std::move(other.mReplyMQ);
mPortId = std::move(other.mPortId);
mFormat = std::move(other.mFormat);
mChannelLayout = std::move(other.mChannelLayout);
mSampleRate = other.mSampleRate;
mFlags = std::move(other.mFlags);
mMixPortHandle = other.mMixPortHandle;
mDataMQ = std::move(other.mDataMQ);
mAsyncCallback = std::move(other.mAsyncCallback);
mOutEventCallback = std::move(other.mOutEventCallback);
mDebugParameters = std::move(other.mDebugParameters);
mFrameCount = other.mFrameCount;
return *this;
}
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
@@ -151,10 +121,14 @@ class StreamContext {
size_t getFrameSize() const;
int getInternalCommandCookie() const { return mInternalCommandCookie; }
int32_t getMixPortHandle() const { return mMixPortHandle; }
int32_t getNominalLatencyMs() const { return mNominalLatencyMs; }
std::shared_ptr<IStreamOutEventCallback> getOutEventCallback() const {
return mOutEventCallback;
}
int getPortId() const { return mPortId; }
std::weak_ptr<sounddose::StreamDataProcessorInterface> getStreamDataProcessor() const {
return mStreamDataProcessor;
}
void startStreamDataProcessor();
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
int getSampleRate() const { return mSampleRate; }
@@ -167,18 +141,20 @@ class StreamContext {
long getFrameCount() const { return mFrameCount; }
private:
// Fields are non const to allow move assignment.
std::unique_ptr<CommandMQ> mCommandMQ;
int mInternalCommandCookie; // The value used to confirm that the command was posted internally
std::unique_ptr<ReplyMQ> mReplyMQ;
int mPortId;
::aidl::android::media::audio::common::AudioFormatDescription mFormat;
::aidl::android::media::audio::common::AudioChannelLayout mChannelLayout;
int mSampleRate;
::aidl::android::media::audio::common::AudioIoFlags mFlags;
int32_t mNominalLatencyMs;
int32_t mMixPortHandle;
std::unique_ptr<DataMQ> mDataMQ;
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
std::weak_ptr<sounddose::StreamDataProcessorInterface> mStreamDataProcessor;
DebugParameters mDebugParameters;
long mFrameCount = 0;
};

View File

@@ -24,7 +24,7 @@
#include <aidl/android/hardware/audio/core/IBluetoothLe.h>
#include "core-impl/DevicePortProxy.h"
#include "core-impl/Module.h"
#include "core-impl/ModuleBluetooth.h"
#include "core-impl/Stream.h"
namespace aidl::android::hardware::audio::core {
@@ -32,7 +32,7 @@ namespace aidl::android::hardware::audio::core {
class StreamBluetooth : public StreamCommonImpl {
public:
StreamBluetooth(StreamContext* context, const Metadata& metadata,
Module::BtProfileHandles&& btHandles);
ModuleBluetooth::BtProfileHandles&& btHandles);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
@@ -80,7 +80,7 @@ class StreamInBluetooth final : public StreamIn, public StreamBluetooth {
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
Module::BtProfileHandles&& btHandles);
ModuleBluetooth::BtProfileHandles&& btHandles);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }
@@ -97,7 +97,7 @@ class StreamOutBluetooth final : public StreamOut, public StreamBluetooth {
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
Module::BtProfileHandles&& btHandles);
ModuleBluetooth::BtProfileHandles&& btHandles);
private:
void onClose(StreamDescriptor::State) override { defaultOnClose(); }

View File

@@ -27,10 +27,13 @@ class StreamPrimary : public StreamAlsa {
public:
StreamPrimary(StreamContext* context, const Metadata& metadata);
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
protected:
std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
const bool mIsInput;
const bool mIsAsynchronous;
};
class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper {
@@ -79,6 +82,10 @@ class StreamOutPrimary final : public StreamOut,
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
ndk::ScopedAStatus setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
override;
};
} // namespace aidl::android::hardware::audio::core

View File

@@ -46,7 +46,7 @@ class StreamRemoteSubmix : public StreamCommonImpl {
ndk::ScopedAStatus prepareToClose() override;
private:
size_t getPipeSizeInFrames();
long getDelayInUsForFrameCount(size_t frameCount);
size_t getStreamPipeSizeInFrames();
::android::status_t outWrite(void* buffer, size_t frameCount, size_t* actualFrameCount);
::android::status_t inRead(void* buffer, size_t frameCount, size_t* actualFrameCount);

View File

@@ -14,11 +14,12 @@
* limitations under the License.
*/
#include <limits>
#include <chrono>
#define LOG_TAG "AHAL_StreamPrimary"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <audio_utils/clock.h>
#include <error/expected_utils.h>
#include "PrimaryMixer.h"
@@ -37,7 +38,33 @@ using android::base::GetBoolProperty;
namespace aidl::android::hardware::audio::core {
StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata)
: StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {}
: StreamAlsa(context, metadata, 3 /*readWriteRetries*/),
mIsAsynchronous(!!getContext().getAsyncCallback()) {
context->startStreamDataProcessor();
}
::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount,
size_t* actualFrameCount, int32_t* latencyMs) {
auto start = std::chrono::steady_clock::now();
if (auto status = StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
status != ::android::OK) {
return status;
}
// This is a workaround for the emulator implementation which has a host-side buffer
// and this can result in reading faster than real time.
if (mIsInput && !mIsAsynchronous) {
auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start);
const long projectedVsObservedOffsetUs =
*actualFrameCount * MICROS_PER_SECOND / mContext.getSampleRate() -
recordDurationUs.count();
if (projectedVsObservedOffsetUs > 0) {
LOG(VERBOSE) << __func__ << ": sleeping for " << projectedVsObservedOffsetUs << " us";
usleep(projectedVsObservedOffsetUs);
}
}
return ::android::OK;
}
std::vector<alsa::DeviceProfile> StreamPrimary::getDeviceProfiles() {
static const std::vector<alsa::DeviceProfile> kBuiltInSource{
@@ -64,7 +91,8 @@ bool StreamInPrimary::useStubStream(const AudioDevice& device) {
GetBoolProperty("ro.boot.audio.tinyalsa.simulate_input", false);
return kSimulateInput || device.type.type == AudioDeviceType::IN_TELEPHONY_RX ||
device.type.type == AudioDeviceType::IN_FM_TUNER ||
device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
(device.type.type == AudioDeviceType::IN_DEVICE && device.type.connection.empty());
}
StreamSwitcher::DeviceSwitchBehavior StreamInPrimary::switchCurrentStream(
@@ -99,6 +127,11 @@ ndk::ScopedAStatus StreamInPrimary::getHwGain(std::vector<float>* _aidl_return)
if (isStubStream()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
float gain;
RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getMicGain(&gain));
_aidl_return->resize(0);
_aidl_return->resize(mChannelCount, gain);
RETURN_STATUS_IF_ERROR(setHwGainImpl(*_aidl_return));
return getHwGainImpl(_aidl_return);
}
@@ -130,7 +163,8 @@ bool StreamOutPrimary::useStubStream(const AudioDevice& device) {
static const bool kSimulateOutput =
GetBoolProperty("ro.boot.audio.tinyalsa.ignore_output", false);
return kSimulateOutput || device.type.type == AudioDeviceType::OUT_TELEPHONY_TX ||
device.type.connection == AudioDeviceDescription::CONNECTION_BUS;
device.type.connection == AudioDeviceDescription::CONNECTION_BUS ||
(device.type.type == AudioDeviceType::OUT_DEVICE && device.type.connection.empty());
}
StreamSwitcher::DeviceSwitchBehavior StreamOutPrimary::switchCurrentStream(
@@ -165,6 +199,9 @@ ndk::ScopedAStatus StreamOutPrimary::getHwVolume(std::vector<float>* _aidl_retur
if (isStubStream()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
RETURN_STATUS_IF_ERROR(primary::PrimaryMixer::getInstance().getVolumes(_aidl_return));
_aidl_return->resize(mChannelCount);
RETURN_STATUS_IF_ERROR(setHwVolumeImpl(*_aidl_return));
return getHwVolumeImpl(_aidl_return);
}
@@ -183,4 +220,15 @@ ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector<float>& in_ch
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices(
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) {
if (!devices.empty()) {
auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock();
if (streamDataProcessor != nullptr) {
streamDataProcessor->setAudioDevice(devices[0]);
}
}
return StreamSwitcher::setConnectedDevices(devices);
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <error/expected_utils.h>
#include "SubmixRoute.h"
#include "core-impl/ModuleRemoteSubmix.h"
#include "core-impl/StreamRemoteSubmix.h"
@@ -106,4 +107,12 @@ ndk::ScopedAStatus ModuleRemoteSubmix::onMasterVolumeChanged(float __unused) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
int32_t ModuleRemoteSubmix::getNominalLatencyMs(const AudioPortConfig&) {
// See the note on kDefaultPipePeriodCount.
static constexpr int32_t kMaxLatencyMs =
(r_submix::kDefaultPipeSizeInFrames * 1000) / r_submix::kDefaultSampleRateHz;
static constexpr int32_t kMinLatencyMs = kMaxLatencyMs / r_submix::kDefaultPipePeriodCount;
return (kMaxLatencyMs + kMinLatencyMs) / 2;
}
} // namespace aidl::android::hardware::audio::core

View File

@@ -17,8 +17,6 @@
#define LOG_TAG "AHAL_StreamRemoteSubmix"
#include <android-base/logging.h>
#include <cmath>
#include "core-impl/StreamRemoteSubmix.h"
using aidl::android::hardware::audio::common::SinkMetadata;
@@ -158,27 +156,8 @@ void StreamRemoteSubmix::shutdown() {
::android::status_t StreamRemoteSubmix::transfer(void* buffer, size_t frameCount,
size_t* actualFrameCount, int32_t* latencyMs) {
*latencyMs = (getStreamPipeSizeInFrames() * MILLIS_PER_SECOND) / mStreamConfig.sampleRate;
*latencyMs = getDelayInUsForFrameCount(getStreamPipeSizeInFrames()) / 1000;
LOG(VERBOSE) << __func__ << ": Latency " << *latencyMs << "ms";
sp<MonoPipe> sink = mCurrentRoute->getSink();
if (sink != nullptr) {
if (sink->isShutdown()) {
sink.clear();
LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the transfer.";
// the pipe has already been shutdown, this buffer will be lost but we must simulate
// timing so we don't drain the output faster than realtime
const size_t delayUs = static_cast<size_t>(
std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
usleep(delayUs);
*actualFrameCount = frameCount;
return ::android::OK;
}
} else {
LOG(ERROR) << __func__ << ": transfer without a pipe!";
return ::android::UNEXPECTED_NULL;
}
mCurrentRoute->exitStandby(mIsInput);
return (mIsInput ? inRead(buffer, frameCount, actualFrameCount)
: outWrite(buffer, frameCount, actualFrameCount));
@@ -202,6 +181,10 @@ void StreamRemoteSubmix::shutdown() {
return ::android::OK;
}
long StreamRemoteSubmix::getDelayInUsForFrameCount(size_t frameCount) {
return frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate;
}
// Calculate the maximum size of the pipe buffer in frames for the specified stream.
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
auto pipeConfig = mCurrentRoute->mPipeConfig;
@@ -215,11 +198,11 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
if (sink != nullptr) {
if (sink->isShutdown()) {
sink.clear();
LOG(VERBOSE) << __func__ << ": pipe shutdown, ignoring the write.";
const auto delayUs = getDelayInUsForFrameCount(frameCount);
LOG(DEBUG) << __func__ << ": pipe shutdown, ignoring the write, sleeping for "
<< delayUs << " us";
// the pipe has already been shutdown, this buffer will be lost but we must
// simulate timing so we don't drain the output faster than realtime
const size_t delayUs = static_cast<size_t>(
std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
usleep(delayUs);
*actualFrameCount = frameCount;
return ::android::OK;
@@ -229,17 +212,18 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
return ::android::UNKNOWN_ERROR;
}
const size_t availableToWrite = sink->availableToWrite();
const bool shouldBlockWrite = mCurrentRoute->shouldBlockWrite();
size_t availableToWrite = sink->availableToWrite();
// NOTE: sink has been checked above and sink and source life cycles are synchronized
sp<MonoPipeReader> source = mCurrentRoute->getSource();
// If the write to the sink should be blocked, flush enough frames from the pipe to make space
// to write the most recent data.
if (!mCurrentRoute->shouldBlockWrite() && availableToWrite < frameCount) {
if (!shouldBlockWrite && availableToWrite < frameCount) {
static uint8_t flushBuffer[64];
const size_t flushBufferSizeFrames = sizeof(flushBuffer) / mStreamConfig.frameSize;
size_t framesToFlushFromSource = frameCount - availableToWrite;
LOG(VERBOSE) << __func__ << ": flushing " << framesToFlushFromSource
<< " frames from the pipe to avoid blocking";
LOG(DEBUG) << __func__ << ": flushing " << framesToFlushFromSource
<< " frames from the pipe to avoid blocking";
while (framesToFlushFromSource) {
const size_t flushSize = std::min(framesToFlushFromSource, flushBufferSizeFrames);
framesToFlushFromSource -= flushSize;
@@ -247,7 +231,12 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
source->read(flushBuffer, flushSize);
}
}
availableToWrite = sink->availableToWrite();
if (!shouldBlockWrite && frameCount > availableToWrite) {
// Truncate the request to avoid blocking.
frameCount = availableToWrite;
}
ssize_t writtenFrames = sink->write(buffer, frameCount);
if (writtenFrames < 0) {
if (writtenFrames == (ssize_t)::android::NEGOTIATE) {
@@ -261,7 +250,6 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
writtenFrames = sink->write(buffer, frameCount);
}
}
sink.clear();
if (writtenFrames < 0) {
LOG(ERROR) << __func__ << ": failed writing to pipe with " << writtenFrames;
@@ -286,8 +274,9 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
} else {
LOG(ERROR) << __func__ << ": Read errors " << readErrorCount;
}
const size_t delayUs = static_cast<size_t>(
std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate));
const auto delayUs = getDelayInUsForFrameCount(frameCount);
LOG(DEBUG) << __func__ << ": no source, ignoring the read, sleeping for " << delayUs
<< " us";
usleep(delayUs);
memset(buffer, 0, mStreamConfig.frameSize * frameCount);
*actualFrameCount = frameCount;
@@ -296,7 +285,7 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
// read the data from the pipe
int attempts = 0;
const size_t delayUs = static_cast<size_t>(std::roundf(kReadAttemptSleepUs));
const long delayUs = kReadAttemptSleepUs;
char* buff = (char*)buffer;
size_t remainingFrames = frameCount;
int availableToRead = source->availableToRead();
@@ -313,11 +302,12 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
buff += framesRead * mStreamConfig.frameSize;
availableToRead -= framesRead;
LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead
<< " frames, remaining=" << remainingFrames;
<< " frames, remaining =" << remainingFrames;
} else {
attempts++;
LOG(WARNING) << __func__ << ": read returned " << framesRead
<< " , read failure attempts = " << attempts;
<< " , read failure attempts = " << attempts << ", sleeping for "
<< delayUs << " us";
usleep(delayUs);
}
}
@@ -337,18 +327,18 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
// compute how much we need to sleep after reading the data by comparing the wall clock with
// the projected time at which we should return.
// wall clock after reading from the pipe
auto recordDurationUs = std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime();
auto recordDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - mCurrentRoute->getRecordStartTime());
// readCounterFrames contains the number of frames that have been read since the beginning of
// recording (including this call): it's converted to usec and compared to how long we've been
// recording for, which gives us how long we must wait to sync the projected recording time, and
// the observed recording time.
const int projectedVsObservedOffsetUs =
std::roundf((readCounterFrames * MICROS_PER_SECOND / mStreamConfig.sampleRate) -
recordDurationUs.count());
const long projectedVsObservedOffsetUs =
getDelayInUsForFrameCount(readCounterFrames) - recordDurationUs.count();
LOG(VERBOSE) << __func__ << ": record duration " << recordDurationUs.count()
<< " microseconds, will wait: " << projectedVsObservedOffsetUs << " microseconds";
<< " us, will wait: " << projectedVsObservedOffsetUs << " us";
if (projectedVsObservedOffsetUs > 0) {
usleep(projectedVsObservedOffsetUs);
}

View File

@@ -14,16 +14,19 @@
* limitations under the License.
*/
#pragma once
#include <chrono>
#include <mutex>
#include <android-base/thread_annotations.h>
#include <audio_utils/clock.h>
#include <media/nbaio/MonoPipe.h>
#include <media/nbaio/MonoPipeReader.h>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include "core-impl/Stream.h"
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatDescription;
@@ -36,9 +39,13 @@ using ::android::sp;
namespace aidl::android::hardware::audio::core::r_submix {
static constexpr int kDefaultSampleRateHz = 48000;
// Size at default sample rate
// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe().
static constexpr int kDefaultPipeSizeInFrames = (1024 * 4);
// Value used to divide the MonoPipe buffer into segments that are written to the source and
// read from the sink. The maximum latency of the device is the size of the MonoPipe's buffer
// the minimum latency is the MonoPipe buffer size divided by this value.
static constexpr int kDefaultPipePeriodCount = 4;
// Size at the default sample rate
// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe.
static constexpr int kDefaultPipeSizeInFrames = 1024 * kDefaultPipePeriodCount;
// Configuration of the audio stream.
struct AudioConfig {

View File

@@ -94,7 +94,7 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
}
::android::status_t StreamStub::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
int32_t*) {
if (!mIsInitialized) {
LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
}
@@ -117,7 +117,6 @@ StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
}
}
*actualFrameCount = frameCount;
*latencyMs = Module::kLatencyMs;
return ::android::OK;
}

View File

@@ -67,20 +67,7 @@ std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
return {};
}
std::vector<aidl::android::media::audio::common::AudioPort>
ModuleConfig::getAudioPortsForDeviceTypes(const std::vector<AudioDeviceType>& deviceTypes,
const std::string& connection) {
return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection);
}
// static
std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
return getAudioPortsForDeviceTypes(
ports, std::vector<AudioDeviceType>{AudioDeviceType::IN_MICROPHONE,
AudioDeviceType::IN_MICROPHONE_BACK});
}
std::vector<aidl::android::media::audio::common::AudioPort>
ModuleConfig::getAudioPortsForDeviceTypes(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
@@ -100,6 +87,14 @@ ModuleConfig::getAudioPortsForDeviceTypes(
return result;
}
// static
std::vector<aidl::android::media::audio::common::AudioPort> ModuleConfig::getBuiltInMicPorts(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports) {
return getAudioPortsForDeviceTypes(
ports, std::vector<AudioDeviceType>{AudioDeviceType::IN_MICROPHONE,
AudioDeviceType::IN_MICROPHONE_BACK});
}
template <typename T>
auto findById(const std::vector<T>& v, int32_t id) {
return std::find_if(v.begin(), v.end(), [&](const auto& p) { return p.id == id; });
@@ -119,10 +114,7 @@ ModuleConfig::ModuleConfig(IModule* module) {
} else {
mAttachedSinkDevicePorts.insert(port.id);
}
} else if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_VIRTUAL
// The "virtual" connection is used for remote submix which is a dynamic
// device but it can be connected and used w/o external hardware.
&& port.profiles.empty()) {
} else {
mExternalDevicePorts.insert(port.id);
}
}
@@ -141,6 +133,12 @@ std::vector<AudioPort> ModuleConfig::getAttachedDevicePorts() const {
return result;
}
std::vector<aidl::android::media::audio::common::AudioPort>
ModuleConfig::getAudioPortsForDeviceTypes(const std::vector<AudioDeviceType>& deviceTypes,
const std::string& connection) const {
return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection);
}
std::vector<AudioPort> ModuleConfig::getConnectedExternalDevicePorts() const {
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
@@ -229,6 +227,16 @@ std::vector<AudioPort> ModuleConfig::getMmapInMixPorts(bool connectedOnly, bool
});
}
std::vector<AudioPort> ModuleConfig::getRemoteSubmixPorts(bool isInput, bool singlePort) const {
AudioDeviceType deviceType = isInput ? AudioDeviceType::IN_SUBMIX : AudioDeviceType::OUT_SUBMIX;
auto ports = getAudioPortsForDeviceTypes(std::vector<AudioDeviceType>{deviceType},
AudioDeviceDescription::CONNECTION_VIRTUAL);
if (singlePort) {
if (!ports.empty()) ports.resize(1);
}
return ports;
}
std::vector<AudioPort> ModuleConfig::getConnectedDevicesPortsForMixPort(
bool isInput, const AudioPortConfig& mixPortConfig) const {
const auto mixPortIt = findById<AudioPort>(mPorts, mixPortConfig.portId);
@@ -281,19 +289,29 @@ std::optional<AudioPort> ModuleConfig::getSourceMixPortForConnectedDevice() cons
return {};
}
std::vector<AudioPort> ModuleConfig::getRoutableMixPortsForDevicePort(const AudioPort& port) const {
std::set<int32_t> portIds;
for (const auto& route : mRoutes) {
if (port.id == route.sinkPortId) {
portIds.insert(route.sourcePortIds.begin(), route.sourcePortIds.end());
} else if (auto it = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
port.id);
it != route.sourcePortIds.end()) {
portIds.insert(route.sinkPortId);
}
}
std::vector<AudioPort> ModuleConfig::getRoutableDevicePortsForMixPort(const AudioPort& port,
bool connectedOnly) const {
std::set<int32_t> portIds = findRoutablePortIds(port.id);
const bool isInput = port.flags.getTag() == AudioIoFlags::input;
return findMixPorts(isInput, false /*connectedOnly*/, false /*singlePort*/,
std::set<int32_t> devicePortIds;
if (connectedOnly) {
devicePortIds = isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts();
} else {
devicePortIds = portIds;
}
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
return port.ext.getTag() == AudioPortExt::Tag::device && portIds.count(port.id) > 0 &&
devicePortIds.count(port.id) > 0;
});
return result;
}
std::vector<AudioPort> ModuleConfig::getRoutableMixPortsForDevicePort(const AudioPort& port,
bool connectedOnly) const {
std::set<int32_t> portIds = findRoutablePortIds(port.id);
const bool isInput = port.flags.getTag() == AudioIoFlags::input;
return findMixPorts(isInput, connectedOnly, false /*singlePort*/,
[&portIds](const AudioPort& p) { return portIds.count(p.id) > 0; });
}
@@ -470,6 +488,20 @@ std::vector<AudioPort> ModuleConfig::findMixPorts(
return result;
}
std::set<int32_t> ModuleConfig::findRoutablePortIds(int32_t portId) const {
std::set<int32_t> portIds;
for (const auto& route : mRoutes) {
if (portId == route.sinkPortId) {
portIds.insert(route.sourcePortIds.begin(), route.sourcePortIds.end());
} else if (auto it = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
portId);
it != route.sourcePortIds.end()) {
portIds.insert(route.sinkPortId);
}
}
return portIds;
}
std::vector<AudioPortConfig> ModuleConfig::generateAudioMixPortConfigs(
const std::vector<AudioPort>& ports, bool isInput, bool singleProfile) const {
std::vector<AudioPortConfig> result;

View File

@@ -38,9 +38,6 @@ class ModuleConfig {
generateOffloadInfoIfNeeded(
const aidl::android::media::audio::common::AudioPortConfig& portConfig);
std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
const std::string& connection = "");
static std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
@@ -53,6 +50,9 @@ class ModuleConfig {
std::string getError() const { return mStatus.getMessage(); }
std::vector<aidl::android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
const std::string& connection = "") const;
std::vector<aidl::android::media::audio::common::AudioPort> getConnectedExternalDevicePorts()
const;
std::set<int32_t> getConnectedSinkDevicePorts() const;
@@ -85,6 +85,8 @@ class ModuleConfig {
std::vector<aidl::android::media::audio::common::AudioPort> getMmapInMixPorts(
bool connectedOnly /*Permanently attached and connected external devices*/,
bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getRemoteSubmixPorts(
bool isInput, bool singlePort) const;
std::vector<aidl::android::media::audio::common::AudioPort> getConnectedDevicesPortsForMixPort(
bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const {
@@ -103,8 +105,12 @@ class ModuleConfig {
std::optional<aidl::android::media::audio::common::AudioPort>
getSourceMixPortForConnectedDevice() const;
std::vector<aidl::android::media::audio::common::AudioPort> getRoutableDevicePortsForMixPort(
const aidl::android::media::audio::common::AudioPort& port,
bool connectedOnly /*Permanently attached and connected external devices*/) const;
std::vector<aidl::android::media::audio::common::AudioPort> getRoutableMixPortsForDevicePort(
const aidl::android::media::audio::common::AudioPort& port) const;
const aidl::android::media::audio::common::AudioPort& port,
bool connectedOnly /*Permanently attached and connected external devices*/) const;
std::optional<SrcSinkPair> getNonRoutableSrcSinkPair(bool isInput) const;
std::optional<SrcSinkPair> getRoutableSrcSinkPair(bool isInput) const;
@@ -176,6 +182,7 @@ class ModuleConfig {
bool isInput, bool connectedOnly, bool singlePort,
const std::function<bool(const aidl::android::media::audio::common::AudioPort&)>& pred)
const;
std::set<int32_t> findRoutablePortIds(int32_t portId) const;
std::vector<aidl::android::media::audio::common::AudioPortConfig> generateAudioMixPortConfigs(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports, bool isInput,
bool singleProfile) const;

View File

@@ -18,7 +18,6 @@
#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <android/binder_auto_utils.h>
#include <gtest/gtest.h>
@@ -93,4 +92,4 @@ inline ::testing::AssertionResult assertResult(const char* exp_expr, const char*
if ((flags).hwAcceleratorMode == Flags::HardwareAccelerator::TUNNEL || (flags).bypass) { \
GTEST_SKIP() << "Skip data path for offload"; \
} \
})
})

File diff suppressed because it is too large Load Diff