audio VTS: add CompressedOffloadOutputStream test am: 56f1666feb am: 96ddc8f20f

Original change: https://googleplex-android-review.googlesource.com/c/platform/hardware/interfaces/+/19345352

Change-Id: I245c82c92b0f1a0f6275d3b81c3679e77701856b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mikhail Naganov 2022-08-02 23:53:19 +00:00 committed by Automerger Merge Worker
commit 1808705ea7
7 changed files with 138 additions and 14 deletions

View file

@ -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);

View file

@ -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
};
}

View file

@ -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.

View file

@ -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;

View file

@ -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" >

View file

@ -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" >