mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 21:37:44 +00:00
Merge changes I26fd4fd2,Iaadf6e9b,I7d42f8a7,I0482376e into stage-aosp-tm-ts-dev am: 744c329227
Original change: https://googleplex-android-review.googlesource.com/c/platform/hardware/interfaces/+/19345935 Change-Id: I2b54fab89ae38c8060fbec12c080304830a467fc Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -898,10 +898,49 @@ class CompressedOffloadOutputStreamTest : public PcmOnlyConfigOutputStreamTest {
|
||||
}
|
||||
};
|
||||
|
||||
class OffloadCallbacks : public IStreamOutCallback {
|
||||
public:
|
||||
Return<void> onDrainReady() override {
|
||||
ALOGI("onDrainReady");
|
||||
{
|
||||
std::lock_guard lg(mLock);
|
||||
mOnDrainReady = true;
|
||||
}
|
||||
mCondVar.notify_one();
|
||||
return {};
|
||||
}
|
||||
Return<void> onWriteReady() override { return {}; }
|
||||
Return<void> onError() override {
|
||||
ALOGW("onError");
|
||||
{
|
||||
std::lock_guard lg(mLock);
|
||||
mOnError = true;
|
||||
}
|
||||
mCondVar.notify_one();
|
||||
return {};
|
||||
}
|
||||
bool waitForDrainReadyOrError() {
|
||||
std::unique_lock l(mLock);
|
||||
if (!mOnDrainReady && !mOnError) {
|
||||
mCondVar.wait(l, [&]() { return mOnDrainReady || mOnError; });
|
||||
}
|
||||
const bool success = !mOnError;
|
||||
mOnDrainReady = mOnError = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mLock;
|
||||
bool mOnDrainReady = false;
|
||||
bool mOnError = false;
|
||||
std::condition_variable mCondVar;
|
||||
};
|
||||
|
||||
TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
|
||||
doc::test("Check that compressed offload mix ports for MP3 implement gapless offload");
|
||||
const auto& flags = getOutputFlags();
|
||||
const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
|
||||
// See test instantiation, only offload MP3 mix ports are used.
|
||||
if (std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
|
||||
return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD);
|
||||
}) == flags.end()) {
|
||||
@@ -912,8 +951,7 @@ TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
|
||||
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());
|
||||
std::vector<uint8_t> offloadData;
|
||||
ASSERT_NO_FATAL_FAILURE(loadData("/data/local/tmp/sine882hz3s.mp3", &offloadData));
|
||||
ASSERT_FALSE(offloadData.empty());
|
||||
ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
|
||||
@@ -923,38 +961,70 @@ TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
|
||||
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.
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
auto callbacks = sp<OffloadCallbacks>::make();
|
||||
std::mutex presentationEndLock;
|
||||
std::vector<float> presentationEndTimes;
|
||||
// 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 'onDataStart'
|
||||
// each time it starts writing the buffer from the beginning, and '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)},
|
||||
stream.get(), stream->getBufferSize(), std::move(offloadData),
|
||||
[&]() /* onDataStart */ { start = std::chrono::steady_clock::now(); },
|
||||
[&]() /* onDataWrap */ {
|
||||
Return<Result> ret(Result::OK);
|
||||
// Decrease the volume since the test plays a loud sine wave.
|
||||
ret = stream->setVolume(0.1, 0.1);
|
||||
if (!ret.isOk() || ret != Result::OK) {
|
||||
ALOGE("%s: setVolume failed: %s", __func__, toString(ret).c_str());
|
||||
return false;
|
||||
}
|
||||
ret = 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);
|
||||
if (!ret.isOk() || ret != Result::OK) {
|
||||
ALOGE("%s: setParameters failed: %s", __func__, toString(ret).c_str());
|
||||
return false;
|
||||
}
|
||||
ret = stream->drain(AudioDrain::EARLY_NOTIFY);
|
||||
if (!ret.isOk() || ret != Result::OK) {
|
||||
ALOGE("%s: drain failed: %s", __func__, toString(ret).c_str());
|
||||
return false;
|
||||
}
|
||||
// FIXME: On some SoCs intermittent errors are possible, ignore them.
|
||||
if (callbacks->waitForDrainReadyOrError()) {
|
||||
const float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - start)
|
||||
.count();
|
||||
std::lock_guard lg(presentationEndLock);
|
||||
presentationEndTimes.push_back(duration);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
ASSERT_OK(stream->setCallback(callbacks));
|
||||
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());
|
||||
for (bool done = false; !done;) {
|
||||
usleep(presentationeEndPrecisionMs * 1000);
|
||||
{
|
||||
std::lock_guard lg(presentationEndLock);
|
||||
done = presentationEndTimes.size() >= playbackNumber;
|
||||
}
|
||||
ASSERT_FALSE(writer.hasError()) << "Recent write or drain operation has failed";
|
||||
}
|
||||
const float avgDuration =
|
||||
std::accumulate(presentationEndTimes.begin(), presentationEndTimes.end(), 0.0) /
|
||||
presentationEndTimes.size();
|
||||
EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1);
|
||||
std::stringstream observedEndTimes;
|
||||
std::copy(presentationEndTimes.begin(), presentationEndTimes.end(),
|
||||
std::ostream_iterator<float>(observedEndTimes, ", "));
|
||||
EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1)
|
||||
<< "Observed durations: " << observedEndTimes.str();
|
||||
writer.stop();
|
||||
EXPECT_OK(stream->clearCallback());
|
||||
releasePatchIfNeeded();
|
||||
}
|
||||
|
||||
|
||||
@@ -938,12 +938,13 @@ 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)
|
||||
std::function<void()> onDataStart, std::function<bool()> onDataWrap)
|
||||
: mStream(stream),
|
||||
mBufferSize(bufferSize),
|
||||
mData(std::move(data)),
|
||||
mOnDataStart(onDataStart),
|
||||
mOnDataWrap(onDataWrap) {
|
||||
ALOGW("StreamWriter data size: %d", (int)mData.size());
|
||||
ALOGI("StreamWriter data size: %d", (int)mData.size());
|
||||
}
|
||||
~StreamWriter() {
|
||||
stop();
|
||||
@@ -1010,6 +1011,7 @@ class StreamWriter : public StreamWorker<StreamWriter> {
|
||||
ALOGE("command message queue write failed");
|
||||
return false;
|
||||
}
|
||||
if (mDataPosition == 0) mOnDataStart();
|
||||
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");
|
||||
@@ -1040,7 +1042,9 @@ class StreamWriter : public StreamWorker<StreamWriter> {
|
||||
ALOGE("bad wait status: %d", ret);
|
||||
success = false;
|
||||
}
|
||||
if (success && mDataPosition == 0) mOnDataWrap();
|
||||
if (success && mDataPosition == 0) {
|
||||
success = mOnDataWrap();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -1048,7 +1052,8 @@ class StreamWriter : public StreamWorker<StreamWriter> {
|
||||
IStreamOut* const mStream;
|
||||
const size_t mBufferSize;
|
||||
std::vector<uint8_t> mData;
|
||||
std::function<void()> mOnDataWrap = []() {};
|
||||
std::function<void()> mOnDataStart = []() {};
|
||||
std::function<bool()> mOnDataWrap = []() { return true; };
|
||||
size_t mDataPosition = 0;
|
||||
std::unique_ptr<CommandMQ> mCommandMQ;
|
||||
std::unique_ptr<DataMQ> mDataMQ;
|
||||
|
||||
Reference in New Issue
Block a user