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

Commit 9d171af8 authored by Mikhail Naganov's avatar Mikhail Naganov Committed by Automerger Merge Worker
Browse files

audio VTS: add CompressedOffloadOutputStream test am: 56f1666f am: 96ddc8f2

parents 1d8b62bf 96ddc8f2
Loading
Loading
Loading
Loading
+116 −10
Original line number Diff line number Diff line
@@ -14,6 +14,9 @@
 * limitations under the License.
 */

#include <fstream>
#include <numeric>

#include <android-base/chrono_utils.h>

#include "Generators.h"
@@ -561,6 +564,7 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
        // Sometimes HAL doesn't have enough information until the audio data actually gets
        // consumed by the hardware.
        bool timedOut = false;
        if (!firstPosition || *firstPosition == std::numeric_limits<uint64_t>::max()) {
            res = Result::INVALID_STATE;
            for (android::base::Timer elapsed;
                 res != Result::OK && !writer.hasError() &&
@@ -571,6 +575,11 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
            }
            ASSERT_FALSE(writer.hasError());
            ASSERT_FALSE(timedOut);
        } else {
            // Use `firstPosition` instead of querying it from the HAL. This is used when
            // `waitForPresentationPositionAdvance` is called in a loop.
            framesInitial = *firstPosition;
        }

        uint64_t frames = framesInitial;
        for (android::base::Timer elapsed;
@@ -632,7 +641,7 @@ TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionPreservedOnStandby) {
    ASSERT_OK(stream->standby());
    writer.resume();

    uint64_t frames;
    uint64_t frames = std::numeric_limits<uint64_t>::max();
    ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, &frames));
    EXPECT_GT(frames, framesInitial);

@@ -853,3 +862,100 @@ INSTANTIATE_TEST_CASE_P(MicrophoneInfoInputStream, MicrophoneInfoInputStreamTest
                        ::testing::ValuesIn(getBuiltinMicConfigParameters()),
                        &DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MicrophoneInfoInputStreamTest);

static const std::vector<DeviceConfigParameter>& getOutputDeviceCompressedConfigParameters(
        const AudioConfigBase& configToMatch) {
    static const std::vector<DeviceConfigParameter> parameters = [&] {
        auto allParams = getOutputDeviceConfigParameters();
        std::vector<DeviceConfigParameter> compressedParams;
        std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(compressedParams),
                     [&](auto cfg) {
                         if (std::get<PARAM_CONFIG>(cfg).base != configToMatch) return false;
                         const auto& flags = std::get<PARAM_FLAGS>(cfg);
                         return std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
                                    return flag ==
                                           toString(xsd::AudioInOutFlag::
                                                            AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
                                }) != flags.end();
                     });
        return compressedParams;
    }();
    return parameters;
}

class CompressedOffloadOutputStreamTest : public PcmOnlyConfigOutputStreamTest {
  public:
    void loadData(const std::string& fileName, std::vector<uint8_t>* data) {
        std::ifstream is(fileName, std::ios::in | std::ios::binary);
        ASSERT_TRUE(is.good()) << "Failed to open file " << fileName;
        is.seekg(0, is.end);
        data->reserve(data->size() + is.tellg());
        is.seekg(0, is.beg);
        data->insert(data->end(), std::istreambuf_iterator<char>(is),
                     std::istreambuf_iterator<char>());
        ASSERT_TRUE(!is.fail()) << "Failed to read from file " << fileName;
    }
};

TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
    doc::test("Check that compressed offload mix ports for MP3 implement gapless offload");
    const auto& flags = getOutputFlags();
    if (std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
            return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD);
        }) == flags.end()) {
        GTEST_SKIP() << "Compressed offload mix port does not support gapless offload";
    }
    // FIXME: The presentation position is not updated if there is no zero padding in data.
    std::vector<uint8_t> offloadData(stream->getBufferSize());
    ASSERT_NO_FATAL_FAILURE(loadData("/data/local/tmp/sine882hz3s.mp3", &offloadData));
    ASSERT_FALSE(offloadData.empty());
    ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
    const int presentationeEndPrecisionMs = 1000;
    const int sampleRate = 44100;
    const int significantSampleNumber = (presentationeEndPrecisionMs * sampleRate) / 1000;
    const int delay = 576 + 1000;
    const int padding = 756 + 1000;
    const int durationMs = 3000 - 44;
    // StreamWriter plays 'offloadData' in a loop, possibly using multiple calls to 'write',
    // this depends on the relative sizes of 'offloadData' and the HAL buffer. Writer calls
    // 'onDataWrap' callback each time it wraps around the buffer.
    StreamWriter writer(
            stream.get(), stream->getBufferSize(), std::move(offloadData), [&]() /* onDataWrap */ {
                Parameters::set(stream,
                                {{AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, std::to_string(delay)},
                                 {AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, std::to_string(padding)}});
                stream->drain(AudioDrain::EARLY_NOTIFY);
            });
    ASSERT_TRUE(writer.start());
    ASSERT_TRUE(writer.waitForAtLeastOneCycle());
    // Decrease the volume since the test plays a loud sine wave.
    ASSERT_OK(stream->setVolume(0.1, 0.1));
    // How many times to loop the track so that the sum of gapless delay and padding from
    // the first presentation end to the last is at least 'presentationeEndPrecisionMs'.
    const int playbackNumber = (int)(significantSampleNumber / ((float)delay + padding) + 1);
    std::vector<float> presentationEndTimes;
    uint64_t previousPosition = std::numeric_limits<uint64_t>::max();
    for (int i = 0; i < playbackNumber; ++i) {
        const auto start = std::chrono::steady_clock::now();
        ASSERT_NO_FATAL_FAILURE(
                waitForPresentationPositionAdvance(writer, &previousPosition, &previousPosition));
        presentationEndTimes.push_back(std::chrono::duration_cast<std::chrono::milliseconds>(
                                               std::chrono::steady_clock::now() - start)
                                               .count());
    }
    const float avgDuration =
            std::accumulate(presentationEndTimes.begin(), presentationEndTimes.end(), 0.0) /
            presentationEndTimes.size();
    EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1);
    writer.stop();
    releasePatchIfNeeded();
}

INSTANTIATE_TEST_CASE_P(
        CompressedOffloadOutputStream, CompressedOffloadOutputStreamTest,
        ::testing::ValuesIn(getOutputDeviceCompressedConfigParameters(AudioConfigBase{
                .format = xsd::toString(xsd::AudioFormat::AUDIO_FORMAT_MP3),
                .sampleRateHz = 44100,
                .channelMask = xsd::toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO)})),
        &DeviceConfigParameterToString);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompressedOffloadOutputStreamTest);
+2 −2
Original line number Diff line number Diff line
@@ -78,10 +78,10 @@ static AudioOffloadInfo generateOffloadInfo(const AudioConfigBase& base) {
            .base = base,
            .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
            .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
            .bitRatePerSecond = 320,
            .bitRatePerSecond = 192,  // as in sine882hz3s.mp3
            .durationMicroseconds = -1,
            .bitWidth = 16,
            .bufferSize = 256  // arbitrary value
            .bufferSize = 72000  // 3 seconds at 192 kbps, as in sine882hz3s.mp3
    };
}

+2 −0
Original line number Diff line number Diff line
@@ -190,6 +190,7 @@ cc_test {
    ],
    data: [
        ":audio_policy_configuration_V7_0",
        "data/sine882hz3s.mp3",
    ],
    // Use test_config for vts suite.
    // TODO(b/146104851): Add auto-gen rules and remove it.
@@ -223,6 +224,7 @@ cc_test {
    ],
    data: [
        ":audio_policy_configuration_V7_1",
        "data/sine882hz3s.mp3",
    ],
    // Use test_config for vts suite.
    // TODO(b/146104851): Add auto-gen rules and remove it.
+15 −2
Original line number Diff line number Diff line
@@ -937,6 +937,14 @@ class StreamWriter : public StreamWorker<StreamWriter> {

    StreamWriter(IStreamOut* stream, size_t bufferSize)
        : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
    StreamWriter(IStreamOut* stream, size_t bufferSize, std::vector<uint8_t>&& data,
                 std::function<void()> onDataWrap)
        : mStream(stream),
          mBufferSize(bufferSize),
          mData(std::move(data)),
          mOnDataWrap(onDataWrap) {
        ALOGW("StreamWriter data size: %d", (int)mData.size());
    }
    ~StreamWriter() {
        stop();
        if (mEfGroup) {
@@ -1002,9 +1010,11 @@ class StreamWriter : public StreamWorker<StreamWriter> {
            ALOGE("command message queue write failed");
            return false;
        }
        const size_t dataSize = std::min(mData.size(), mDataMQ->availableToWrite());
        bool success = mDataMQ->write(mData.data(), dataSize);
        const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite());
        bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize);
        ALOGE_IF(!success, "data message queue write failed");
        mDataPosition += dataSize;
        if (mDataPosition >= mData.size()) mDataPosition = 0;
        mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));

        uint32_t efState = 0;
@@ -1030,6 +1040,7 @@ class StreamWriter : public StreamWorker<StreamWriter> {
            ALOGE("bad wait status: %d", ret);
            success = false;
        }
        if (success && mDataPosition == 0) mOnDataWrap();
        return success;
    }

@@ -1037,6 +1048,8 @@ class StreamWriter : public StreamWorker<StreamWriter> {
    IStreamOut* const mStream;
    const size_t mBufferSize;
    std::vector<uint8_t> mData;
    std::function<void()> mOnDataWrap = []() {};
    size_t mDataPosition = 0;
    std::unique_ptr<CommandMQ> mCommandMQ;
    std::unique_ptr<DataMQ> mDataMQ;
    std::unique_ptr<StatusMQ> mStatusMQ;
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
        <option name="cleanup" value="true" />
        <option name="push" value="VtsHalAudioV7_0TargetTest->/data/local/tmp/VtsHalAudioV7_0TargetTest" />
        <option name="push" value="audio_policy_configuration_V7_0.xsd->/data/local/tmp/audio_policy_configuration_V7_0.xsd" />
        <option name="push" value="sine882hz3s.mp3->/data/local/tmp/sine882hz3s.mp3" />
    </target_preparer>

    <test class="com.android.tradefed.testtype.GTest" >
Loading