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

Commit e693002b authored by Eric Laurent's avatar Eric Laurent
Browse files

audio policy: select output with best sample format match

Add rules allowing selection of the output stream with the sample
format offering the best match to the client track sample format
when more than one output is compatible with a request.

Bug: 27210844

Change-Id: I31981940be5d1c06d713d52187cfb0a7bf355773
parent d25c4552
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -126,3 +126,18 @@ static inline int32_t source_priority(audio_source_t inputSource)
    }
    return 0;
}

/* Indicates if audio formats are equivalent when considering a match between
 * audio HAL supported formats and client requested formats
 */
static inline bool audio_formats_match(audio_format_t format1,
                                       audio_format_t format2)
{
    if (audio_is_linear_pcm(format1) &&
            (audio_bytes_per_sample(format1) > 2) &&
            audio_is_linear_pcm(format2) &&
            (audio_bytes_per_sample(format2) > 2)) {
        return true;
    }
    return format1 == format2;
}
+6 −0
Original line number Diff line number Diff line
@@ -110,6 +110,12 @@ public:

    static int compareFormats(audio_format_t format1, audio_format_t format2);

    // Used to select an audio HAL output stream with a sample format providing the
    // less degradation for a given AudioTrack sample format.
    static bool isBetterFormatMatch(audio_format_t newFormat,
                                        audio_format_t currentFormat,
                                        audio_format_t targetFormat);

    audio_module_handle_t getModuleHandle() const;
    uint32_t getModuleVersion() const;
    const char *getModuleName() const;
+26 −0
Original line number Diff line number Diff line
@@ -268,6 +268,32 @@ int AudioPort::compareFormats(audio_format_t format1, audio_format_t format2)
    return index1 - index2;
}

bool AudioPort::isBetterFormatMatch(audio_format_t newFormat,
                                    audio_format_t currentFormat,
                                    audio_format_t targetFormat)
{
    if (newFormat == currentFormat) {
        return false;
    }
    if (currentFormat == AUDIO_FORMAT_INVALID) {
        return true;
    }
    if (newFormat == targetFormat) {
        return true;
    }
    int currentDiffBytes = (int)audio_bytes_per_sample(targetFormat) -
            audio_bytes_per_sample(currentFormat);
    int newDiffBytes = (int)audio_bytes_per_sample(targetFormat) -
            audio_bytes_per_sample(newFormat);

    if (abs(newDiffBytes) < abs(currentDiffBytes)) {
        return true;
    } else if (abs(newDiffBytes) == abs(currentDiffBytes)) {
        return (newDiffBytes >= 0);
    }
    return false;
}

void AudioPort::pickAudioProfile(uint32_t &samplingRate,
                                 audio_channel_mask_t &channelMask,
                                 audio_format_t &format) const
+1 −1
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ namespace android {
status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask,
                                  audio_format_t format) const
{
    if (format == mFormat &&
    if (audio_formats_match(format, mFormat) &&
            (mChannelMasks.isEmpty() || supportsChannels(channelMask)) &&
            (mSamplingRates.isEmpty() || supportsRate(samplingRate))) {
        return NO_ERROR;
+38 −17
Original line number Diff line number Diff line
@@ -876,7 +876,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
                outputDesc = desc;
                // reuse direct output if currently open and configured with same parameters
                if ((samplingRate == outputDesc->mSamplingRate) &&
                        (format == outputDesc->mFormat) &&
                        audio_formats_match(format, outputDesc->mFormat) &&
                        (channelMask == outputDesc->mChannelMask)) {
                    outputDesc->mDirectOpenCount++;
                    ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i));
@@ -927,7 +927,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
        // only accept an output with the requested parameters
        if (status != NO_ERROR ||
            (samplingRate != 0 && samplingRate != config.sample_rate) ||
            (format != AUDIO_FORMAT_DEFAULT && format != config.format) ||
            (format != AUDIO_FORMAT_DEFAULT && !audio_formats_match(format, config.format)) ||
            (channelMask != 0 && channelMask != config.channel_mask)) {
            ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d,"
                    "format %d %d, channelMask %04x %04x", output, samplingRate,
@@ -992,8 +992,9 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_h
    // devices (the list was previously build by getOutputsForDevice()).
    // The priority is as follows:
    // 1: the output with the highest number of requested policy flags
    // 2: the primary output
    // 3: the first output in the list
    // 2: the output with the bit depth the closest to the requested one
    // 3: the primary output
    // 4: the first output in the list

    if (outputs.size() == 0) {
        return 0;
@@ -1003,8 +1004,11 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_h
    }

    int maxCommonFlags = 0;
    audio_io_handle_t outputFlags = 0;
    audio_io_handle_t outputPrimary = 0;
    audio_io_handle_t outputForFlags = 0;
    audio_io_handle_t outputForPrimary = 0;
    audio_io_handle_t outputForFormat = 0;
    audio_format_t bestFormat = AUDIO_FORMAT_INVALID;
    audio_format_t bestFormatForFlags = AUDIO_FORMAT_INVALID;

    for (size_t i = 0; i < outputs.size(); i++) {
        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
@@ -1012,31 +1016,48 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_h
            // if a valid format is specified, skip output if not compatible
            if (format != AUDIO_FORMAT_INVALID) {
                if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
                    if (format != outputDesc->mFormat) {
                    if (!audio_formats_match(format, outputDesc->mFormat)) {
                        continue;
                    }
                } else if (!audio_is_linear_pcm(format)) {
                    continue;
                }
                if (AudioPort::isBetterFormatMatch(
                        outputDesc->mFormat, bestFormat, format)) {
                    outputForFormat = outputs[i];
                    bestFormat = outputDesc->mFormat;
                }
            }

            int commonFlags = popcount(outputDesc->mProfile->getFlags() & flags);
            if (commonFlags > maxCommonFlags) {
                outputFlags = outputs[i];
            if (commonFlags >= maxCommonFlags) {
                if (commonFlags == maxCommonFlags) {
                    if (AudioPort::isBetterFormatMatch(
                            outputDesc->mFormat, bestFormatForFlags, format)) {
                        outputForFlags = outputs[i];
                        bestFormatForFlags = outputDesc->mFormat;
                    }
                } else {
                    outputForFlags = outputs[i];
                    maxCommonFlags = commonFlags;
                    bestFormatForFlags = outputDesc->mFormat;
                }
                ALOGV("selectOutput() commonFlags for output %d, %04x", outputs[i], commonFlags);
            }
            if (outputDesc->mProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
                outputPrimary = outputs[i];
                outputForPrimary = outputs[i];
            }
        }
    }

    if (outputFlags != 0) {
        return outputFlags;
    if (outputForFlags != 0) {
        return outputForFlags;
    }
    if (outputForFormat != 0) {
        return outputForFormat;
    }
    if (outputPrimary != 0) {
        return outputPrimary;
    if (outputForPrimary != 0) {
        return outputForPrimary;
    }

    return outputs[0];
@@ -1509,7 +1530,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device,
            // and current input properties are not exactly as requested.
            if ((desc->mSamplingRate != samplingRate ||
                    desc->mChannelMask != channelMask ||
                    desc->mFormat != format) &&
                    !audio_formats_match(desc->mFormat, format)) &&
                    (source_priority(desc->getHighestPrioritySource(false /*activeOnly*/)) <
                     source_priority(inputSource))) {
                ALOGV("%s: ", __FUNCTION__);
@@ -1544,7 +1565,7 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device,
    // only accept input with the exact requested set of parameters
    if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE ||
        (profileSamplingRate != config.sample_rate) ||
        (profileFormat != config.format) ||
        !audio_formats_match(profileFormat, config.format) ||
        (profileChannelMask != config.channel_mask)) {
        ALOGW("getInputForAttr() failed opening input: samplingRate %d"
              ", format %d, channelMask %x",