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 &timestamp) 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 &timestamp) const;
-    void reset();
-
-    uint64_t mFrames = 0;
-    nsecs_t mTimestamp;
-};
+TimeSpec nsecs2TimeSpec(nsecs_t);
 
 }  // namespace util
 }  // namespace implementation