diff options
author | Mikhail Naganov <mnaganov@google.com> | 2022-06-27 21:34:09 +0000 |
---|---|---|
committer | Mikhail Naganov <mnaganov@google.com> | 2022-07-19 19:56:59 +0000 |
commit | 56f1666febb747d67f2a6d9749eb3351e720307b (patch) | |
tree | e24cd0a5f1ba11c592e74b5610421b625cbe8848 /audio | |
parent | 1e703f182e2fcbb9123bf661d81d22a04d0e1f8d (diff) |
audio VTS: add CompressedOffloadOutputStream test
CompressedOffloadOutputStreamTest#Mp3FormatGaplessOffload verifies
that gapless offload is supported by compressed offload
mix ports.
Bug: 219767875
Test: atest VtsHalAudioV7_0TargetTest
Test: atest VtsHalAudioV7_1TargetTest
Change-Id: I7d42f8a714da2923b8775445ba301938ca90b885
Merged-In: I7d42f8a714da2923b8775445ba301938ca90b885
(cherry picked from commit 158a2ddb2fafa01e2e52a4c9033b81c29d05dfdd)
Diffstat (limited to 'audio')
7 files changed, 138 insertions, 14 deletions
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp index be90b2180c..37992ae720 100644 --- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp +++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <fstream> +#include <numeric> + #include <android-base/chrono_utils.h> #include "Generators.h" @@ -561,16 +564,22 @@ class PcmOnlyConfigOutputStreamTest : public OutputStreamTest { // Sometimes HAL doesn't have enough information until the audio data actually gets // consumed by the hardware. bool timedOut = false; - res = Result::INVALID_STATE; - for (android::base::Timer elapsed; - res != Result::OK && !writer.hasError() && - !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) { - usleep(kWriteDurationUs); - ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts))); - ASSERT_RESULT(okOrInvalidState, res); + if (!firstPosition || *firstPosition == std::numeric_limits<uint64_t>::max()) { + res = Result::INVALID_STATE; + for (android::base::Timer elapsed; + res != Result::OK && !writer.hasError() && + !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) { + usleep(kWriteDurationUs); + ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts))); + ASSERT_RESULT(okOrInvalidState, res); + } + 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; } - ASSERT_FALSE(writer.hasError()); - ASSERT_FALSE(timedOut); 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); diff --git a/audio/core/all-versions/vts/functional/7.0/Generators.cpp b/audio/core/all-versions/vts/functional/7.0/Generators.cpp index cd84c41308..8b955b6c84 100644 --- a/audio/core/all-versions/vts/functional/7.0/Generators.cpp +++ b/audio/core/all-versions/vts/functional/7.0/Generators.cpp @@ -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 }; } diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp index c757032e0f..5b0a7f28f9 100644 --- a/audio/core/all-versions/vts/functional/Android.bp +++ b/audio/core/all-versions/vts/functional/Android.bp @@ -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. diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h index afc25f0a1e..6c5584d427 100644 --- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h +++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h @@ -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; diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml index f0e26958b9..8da5744089 100644 --- a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml @@ -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" > diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml index 7ce1477e1a..227df1803d 100644 --- a/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml +++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_1TargetTest.xml @@ -29,6 +29,8 @@ <option name="cleanup" value="true" /> <option name="push" value="VtsHalAudioV7_1TargetTest->/data/local/tmp/VtsHalAudioV7_1TargetTest" /> <option name="push" value="audio_policy_configuration_V7_1.xsd->/data/local/tmp/audio_policy_configuration_V7_1.xsd" /> + <option name="push" value="sine882hz3s.mp3->/data/local/tmp/sine882hz3s.mp3" /> + </target_preparer> <test class="com.android.tradefed.testtype.GTest" > diff --git a/audio/core/all-versions/vts/functional/data/sine882hz3s.mp3 b/audio/core/all-versions/vts/functional/data/sine882hz3s.mp3 Binary files differnew file mode 100644 index 0000000000..0604f9b3b4 --- /dev/null +++ b/audio/core/all-versions/vts/functional/data/sine882hz3s.mp3 |