Fix getPresentationPosition
The count [frame counter] is not reset to zero
when output enters standby.
Resetting this counter to zero caused the
MediaPlayer to delay the completion notification
which failed CTS tests.
Bug: 160606842
Test: atest android.mediastress.cts.H263QcifShortPlayerTest#testPlay10
Signed-off-by: Roman Kiryanov <rkir@google.com>
Change-Id: I41981be96a36039fce17a4c289fabbe4588af7e1
diff --git a/audio/device_port_sink.cpp b/audio/device_port_sink.cpp
index 8c17b6d..c0d89ff 100644
--- a/audio/device_port_sink.cpp
+++ b/audio/device_port_sink.cpp
@@ -15,6 +15,7 @@
*/
#include <log/log.h>
+#include <utils/Timers.h>
#include "device_port_sink.h"
#include "talsa.h"
#include "util.h"
@@ -28,19 +29,18 @@
namespace {
struct TinyalsaSink : public DevicePortSink {
- TinyalsaSink(unsigned pcmCard, unsigned pcmDevice, const AudioConfig &cfg)
- : mSampleRateHz(cfg.sampleRateHz)
+ TinyalsaSink(unsigned pcmCard, unsigned pcmDevice,
+ const AudioConfig &cfg, uint64_t &frames)
+ : mFrames(frames)
, mPcm(talsa::pcmOpen(pcmCard, pcmDevice,
util::countChannels(cfg.channelMask),
cfg.sampleRateHz,
cfg.frameCount,
true /* isOut */)) {}
- Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) const override {
- nsecs_t t = 0;
- mPos.now(mSampleRateHz, frames, t);
- ts.tvSec = ns2s(t);
- ts.tvNSec = t - s2ns(ts.tvSec);
+ Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
+ frames = mFrames;
+ ts = util::nsecs2TimeSpec(systemTime(SYSTEM_TIME_MONOTONIC));
return Result::OK;
}
@@ -49,18 +49,19 @@
if (res < 0) {
return res;
} else if (res == 0) {
- mPos.addFrames(::pcm_bytes_to_frames(mPcm.get(), nBytes));
+ mFrames += ::pcm_bytes_to_frames(mPcm.get(), nBytes);
return nBytes;
} else {
- mPos.addFrames(::pcm_bytes_to_frames(mPcm.get(), res));
+ mFrames += ::pcm_bytes_to_frames(mPcm.get(), res);
return res;
}
}
static std::unique_ptr<TinyalsaSink> create(unsigned pcmCard,
unsigned pcmDevice,
- const AudioConfig &cfg) {
- auto src = std::make_unique<TinyalsaSink>(pcmCard, pcmDevice, cfg);
+ const AudioConfig &cfg,
+ uint64_t &frames) {
+ auto src = std::make_unique<TinyalsaSink>(pcmCard, pcmDevice, cfg, frames);
if (src->mPcm) {
return src;
} else {
@@ -69,37 +70,56 @@
}
private:
- const uint32_t mSampleRateHz;
- util::StreamPosition mPos;
+ uint64_t &mFrames;
talsa::PcmPtr mPcm;
};
struct NullSink : public DevicePortSink {
- explicit NullSink(const AudioConfig &cfg)
- : mSampleRateHz(cfg.sampleRateHz)
- , mNChannels(util::countChannels(cfg.channelMask)) {}
+ NullSink(const AudioConfig &cfg, uint64_t &frames)
+ : mFrames(frames)
+ , mSampleRateHz(cfg.sampleRateHz)
+ , mNChannels(util::countChannels(cfg.channelMask))
+ , mTimestamp(systemTime(SYSTEM_TIME_MONOTONIC)) {}
- Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) const override {
- nsecs_t t = 0;
- mPos.now(mSampleRateHz, frames, t);
- ts.tvSec = ns2s(t);
- ts.tvNSec = t - s2ns(ts.tvSec);
+ Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) override {
+ simulatePresentationPosition();
+ frames = mFrames;
+ ts = util::nsecs2TimeSpec(mTimestamp);
return Result::OK;
}
int write(const void *, size_t nBytes) override {
- mPos.addFrames(nBytes / mNChannels / sizeof(int16_t));
+ simulatePresentationPosition();
+ mAvailableFrames += nBytes / mNChannels / sizeof(int16_t);
return nBytes;
}
- static std::unique_ptr<NullSink> create(const AudioConfig &cfg) {
- return std::make_unique<NullSink>(cfg);
+ void simulatePresentationPosition() {
+ const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t deltaNs = nowNs - mTimestamp;
+ const uint64_t deltaFrames = uint64_t(mSampleRateHz) * ns2ms(deltaNs) / 1000;
+ const uint64_t f = std::min(deltaFrames, mAvailableFrames);
+
+ mFrames += f;
+ mAvailableFrames -= f;
+ if (mAvailableFrames) {
+ mTimestamp += us2ns(f * 1000000 / mSampleRateHz);
+ } else {
+ mTimestamp = nowNs;
+ }
+ }
+
+ static std::unique_ptr<NullSink> create(const AudioConfig &cfg,
+ uint64_t &frames) {
+ return std::make_unique<NullSink>(cfg, frames);
}
private:
+ uint64_t &mFrames;
const unsigned mSampleRateHz;
const unsigned mNChannels;
- util::StreamPosition mPos;
+ uint64_t mAvailableFrames = 0;
+ nsecs_t mTimestamp;
};
} // namespace
@@ -107,7 +127,8 @@
std::unique_ptr<DevicePortSink>
DevicePortSink::create(const DeviceAddress &address,
const AudioConfig &cfg,
- const hidl_bitfield<AudioOutputFlag> &flags) {
+ const hidl_bitfield<AudioOutputFlag> &flags,
+ uint64_t &frames) {
(void)flags;
if (cfg.format != AudioFormat::PCM_16_BIT) {
@@ -117,10 +138,11 @@
switch (address.device) {
case AudioDevice::OUT_SPEAKER:
- return TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+ return TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice,
+ cfg, frames);
case AudioDevice::OUT_TELEPHONY_TX:
- return NullSink::create(cfg);
+ return NullSink::create(cfg, frames);
default:
return nullptr;
diff --git a/audio/device_port_sink.h b/audio/device_port_sink.h
index 16955c1..5714cd0 100644
--- a/audio/device_port_sink.h
+++ b/audio/device_port_sink.h
@@ -30,12 +30,13 @@
struct DevicePortSink {
virtual ~DevicePortSink() {}
- virtual Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) const = 0;
+ virtual Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) = 0;
virtual int write(const void *data, size_t nBytes) = 0;
static std::unique_ptr<DevicePortSink> create(const DeviceAddress &,
const AudioConfig &,
- const hidl_bitfield<AudioOutputFlag> &);
+ const hidl_bitfield<AudioOutputFlag> &,
+ uint64_t &frames);
};
} // namespace implementation
diff --git a/audio/stream_out.cpp b/audio/stream_out.cpp
index 1464637..b8300f0 100644
--- a/audio/stream_out.cpp
+++ b/audio/stream_out.cpp
@@ -123,7 +123,8 @@
mSink = DevicePortSink::create(mStream->getDeviceAddress(),
mStream->getAudioConfig(),
- mStream->getAudioOutputFlags());
+ mStream->getAudioOutputFlags(),
+ mStream->getFrameCounter());
LOG_ALWAYS_FATAL_IF(!mSink);
}
diff --git a/audio/stream_out.h b/audio/stream_out.h
index a9d747c..4f3e44d 100644
--- a/audio/stream_out.h
+++ b/audio/stream_out.h
@@ -102,6 +102,8 @@
const AudioConfig &getAudioConfig() const { return mCommon.m_config; }
const hidl_bitfield<AudioOutputFlag> &getAudioOutputFlags() const { return mCommon.m_flags; }
+ uint64_t &getFrameCounter() { return mFrames; }
+
private:
sp<IDevice> mDev;
void (* const mUnrefDevice)(IDevice*);
@@ -109,6 +111,9 @@
const SourceMetadata mSourceMetadata;
std::unique_ptr<IOThread> mWriteThread;
std::atomic<int16_t> mVolumeNumerator = kVolumeDenominator;
+
+ // The count is not reset to zero when output enters standby.
+ uint64_t mFrames = 0;
};
} // namespace implementation
diff --git a/audio/util.cpp b/audio/util.cpp
index 138c7db..4656d08 100644
--- a/audio/util.cpp
+++ b/audio/util.cpp
@@ -145,25 +145,11 @@
return valid;
}
-StreamPosition::StreamPosition() : mTimestamp(systemTime(SYSTEM_TIME_MONOTONIC)) {}
-
-void StreamPosition::addFrames(uint64_t n) {
- mTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- mFrames += n;
-}
-
-void StreamPosition::now(const size_t sampleRateHz,
- uint64_t &frames,
- nsecs_t ×tamp) const {
- const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- const uint64_t deltaUs = ns2us(now - mTimestamp);
-
- frames = mFrames + sampleRateHz * deltaUs / 1000000;
- timestamp = now;
-}
-
-void StreamPosition::reset() {
- *this = StreamPosition();
+TimeSpec nsecs2TimeSpec(nsecs_t ns) {
+ TimeSpec ts;
+ ts.tvSec = ns2s(ns);
+ ts.tvNSec = ns - s2ns(ts.tvSec);
+ return ts;
}
} // namespace util
diff --git a/audio/util.h b/audio/util.h
index bfac69a..aae8f53 100644
--- a/audio/util.h
+++ b/audio/util.h
@@ -43,15 +43,7 @@
const AudioConfig &cfg,
AudioConfig &suggested);
-struct StreamPosition {
- StreamPosition();
- void addFrames(uint64_t n);
- void now(size_t sampleRateHz, uint64_t &frames, nsecs_t ×tamp) const;
- void reset();
-
- uint64_t mFrames = 0;
- nsecs_t mTimestamp;
-};
+TimeSpec nsecs2TimeSpec(nsecs_t);
} // namespace util
} // namespace implementation