Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1350187c authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio: Provide a way for Module to specify nominal latency

The latency figure depends on the module implementation.
Instead of using a hardcoded value, each module should be
able to specify its own value. This value is then used
for calculating the minimum buffer size.

Set the nominal latency of the primary (CF) module to a high
value since the virtual device implementation fails CTS tests
if it attempts to pretend that it provides low latency.

Bug: 302132812
Test: atest CtsMediaAudioTestCases --test-filter=".*AudioTrackTest.*"
Test: atest CtsMediaAudioTestCases --test-filter=".*AudioRecordTest.*"
Change-Id: I8ce9f230378eea787c9b3c7ce3660c1e4e7bc895
parent 2aab766d
Loading
Loading
Loading
Loading
+27 −9
Original line number Original line Diff line number Diff line
@@ -202,15 +202,17 @@ ndk::ScopedAStatus Module::createStreamContext(
        LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
        LOG(ERROR) << __func__ << ": non-positive buffer size " << in_bufferSizeFrames;
        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
        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& configs = getConfig().portConfigs;
    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
    auto portConfigIt = findById<AudioPortConfig>(configs, in_portConfigId);
    // Since this is a private method, it is assumed that
    // Since this is a private method, it is assumed that
    // validity of the portConfigId has already been checked.
    // 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 =
    const size_t frameSize =
            getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
            getFrameSizeInBytes(portConfigIt->format.value(), portConfigIt->channelMask.value());
    if (frameSize == 0) {
    if (frameSize == 0) {
@@ -238,8 +240,8 @@ ndk::ScopedAStatus Module::createStreamContext(
        StreamContext temp(
        StreamContext temp(
                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
                std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
                portConfigIt->portId, portConfigIt->format.value(),
                portConfigIt->format.value(), portConfigIt->channelMask.value(),
                portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags,
                portConfigIt->sampleRate.value().value, flags, getNominalLatencyMs(*portConfigIt),
                portConfigIt->ext.get<AudioPortExt::mix>().handle,
                portConfigIt->ext.get<AudioPortExt::mix>().handle,
                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
                std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
                asyncCallback, outEventCallback,
                asyncCallback, outEventCallback,
@@ -359,6 +361,12 @@ std::unique_ptr<Module::Configuration> Module::initializeConfig() {
    return internal::getConfiguration(getType());
    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*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
    std::vector<AudioRoute*> result;
    std::vector<AudioRoute*> result;
    auto& routes = getConfig().routes;
    auto& routes = getConfig().routes;
@@ -965,11 +973,21 @@ ndk::ScopedAStatus Module::setAudioPatch(const AudioPatch& in_requested, AudioPa
            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
            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 = 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.clear();
    _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
    _aidl_return->latenciesMs.insert(_aidl_return->latenciesMs.end(),
                                     _aidl_return->sinkPortConfigIds.size(), kLatencyMs);
                                     _aidl_return->sinkPortConfigIds.size(), latencyMs);
    AudioPatch oldPatch{};
    AudioPatch oldPatch{};
    if (existing == patches.end()) {
    if (existing == patches.end()) {
        _aidl_return->id = getConfig().nextPatchId++;
        _aidl_return->id = getConfig().nextPatchId++;
+8 −0
Original line number Original line Diff line number Diff line
@@ -58,4 +58,12 @@ ndk::ScopedAStatus ModulePrimary::createOutputStream(
                                                  offloadInfo);
                                                  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
}  // namespace aidl::android::hardware::audio::core
+3 −3
Original line number Original line Diff line number Diff line
@@ -138,7 +138,7 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
    reply->status = STATUS_OK;
    reply->status = STATUS_OK;
    if (isConnected) {
    if (isConnected) {
        reply->observable.frames = mContext->getFrameCount();
        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) {
        if (auto status = mDriver->refinePosition(&reply->observable); status == ::android::OK) {
            return;
            return;
        }
        }
@@ -315,7 +315,7 @@ bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply
    const size_t frameSize = mContext->getFrameSize();
    const size_t frameSize = mContext->getFrameSize();
    size_t actualFrameCount = 0;
    size_t actualFrameCount = 0;
    bool fatal = false;
    bool fatal = false;
    int32_t latency = Module::kLatencyMs;
    int32_t latency = mContext->getNominalLatencyMs();
    if (isConnected) {
    if (isConnected) {
        if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
        if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
                                                           &actualFrameCount, &latency);
                                                           &actualFrameCount, &latency);
@@ -581,7 +581,7 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep
    const size_t readByteCount = dataMQ->availableToRead();
    const size_t readByteCount = dataMQ->availableToRead();
    const size_t frameSize = mContext->getFrameSize();
    const size_t frameSize = mContext->getFrameSize();
    bool fatal = false;
    bool fatal = false;
    int32_t latency = Module::kLatencyMs;
    int32_t latency = mContext->getNominalLatencyMs();
    if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
    if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
        const bool isConnected = mIsConnected;
        const bool isConnected = mIsConnected;
        LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
        LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
+1 −1
Original line number Original line Diff line number Diff line
@@ -119,7 +119,7 @@ StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int rea


::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) {
::android::status_t StreamAlsa::refinePosition(StreamDescriptor::Position* position) {
    if (mAlsaDeviceProxies.empty()) {
    if (mAlsaDeviceProxies.empty()) {
        LOG(FATAL) << __func__ << ": no opened devices";
        LOG(WARNING) << __func__ << ": no opened devices";
        return ::android::NO_INIT;
        return ::android::NO_INIT;
    }
    }
    // Since the proxy can only count transferred frames since its creation,
    // Since the proxy can only count transferred frames since its creation,
+9 −5
Original line number Original line Diff line number Diff line
@@ -50,9 +50,6 @@ class Module : public BnModule {
                       std::weak_ptr<IBluetoothLe>>
                       std::weak_ptr<IBluetoothLe>>
            BtProfileHandles;
            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) {
    static std::shared_ptr<Module> createInstance(Type type) {
        return createInstance(type, std::make_unique<Configuration>());
        return createInstance(type, std::make_unique<Configuration>());
    }
    }
@@ -145,8 +142,6 @@ class Module : public BnModule {
    ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
    ndk::ScopedAStatus getAAudioMixerBurstCount(int32_t* _aidl_return) override;
    ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(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;
    // The maximum stream buffer size is 1 GiB = 2 ** 30 bytes;
    static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;
    static constexpr int32_t kMaximumStreamBufferSizeBytes = 1 << 30;


@@ -207,8 +202,17 @@ class Module : public BnModule {
    virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
    virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
    virtual std::vector<::aidl::android::media::audio::common::MicrophoneInfo> getMicrophoneInfos();
    virtual std::vector<::aidl::android::media::audio::common::MicrophoneInfo> getMicrophoneInfos();
    virtual std::unique_ptr<Configuration> initializeConfig();
    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.
    // Utility and helper functions accessible to subclasses.
    static int32_t calculateBufferSizeFrames(int32_t latencyMs, int32_t sampleRateHz) {
        const int32_t rawSizeFrames = (latencyMs * sampleRateHz) / 1000;
        int32_t powerOf2 = 1;
        while (powerOf2 < rawSizeFrames) powerOf2 <<= 1;
        return powerOf2;
    }

    ndk::ScopedAStatus bluetoothParametersUpdated();
    ndk::ScopedAStatus bluetoothParametersUpdated();
    void cleanUpPatch(int32_t patchId);
    void cleanUpPatch(int32_t patchId);
    ndk::ScopedAStatus createStreamContext(
    ndk::ScopedAStatus createStreamContext(
Loading