========
Improve accuracy of time reported from pause to sample count reset after resume with HXSymbianAudioSession.
Details ======
Currently when HXSymbianAudioSession is paused we prepare for a sample count reset from CMMFDevSound. Just before we pause we note how many samples are unplayed. We then increase the current time by the time represented by those samples and wait for a resume followed by the reset (a sample count less then the highest we've seen so far). After we request the device to resume we may or may not pick up a few more sample counts that increase over the previous sample count before we see the reset. If we (It depends on the timer.) When we see the sample count reset we continue accumulating time.
The problem with the above is that between the time when we resume and the time of the reset we are actually reporting time that is slightly ahead of the true time.
This change does the following. Instead of adding the amount of unplayed time at the time of the pause we store that value in m_unplayedSampleCount . Then, when we resume we watch the sample counts we get back. If they increase over the previous values we increase time by that much and decrement m_unplayedSampleCount. When we see the reset we finally add anything that is left in m_unplayedSampleCount.
It does appear that when we resume everything buffered in the device at the time of the pause is in fact played out, and that the sample count resets once all this data plays out. The sample count we get when we pause always seems accurate. (Conceivable a few samples can play out between the time we check the sample count and the time the device actually pauses. We don't get a "pause" complete event.)
Testing
=======
Tested a variety of clips. Rapid and/or frequent pause/resume while monitoring a/v sync and resume continuity. Seeking. Both on-demand and live. I've been using the the last few days and it appears to work well.
WINS and thumb 6630
-Liam
Index: platform/symbian/audiosvr/mmf/audio_session-mmf.cpp
===================================================================
RCS file: /cvsroot/audio/device/platform/symbian/audiosvr/mmf/audio_session-mmf.cpp,v
retrieving revision 1.8
diff -u -w -r1.8 audio_session-mmf.cpp
--- platform/symbian/audiosvr/mmf/audio_session-mmf.cpp 13 Apr 2005 20:34:17 -0000 1.8
+++ platform/symbian/audiosvr/mmf/audio_session-mmf.cpp 20 Apr 2005 01:04:59 -0000
@@ -204,7 +204,9 @@
m_lastSampleCount(0),
m_unplayedSampleCount(0),
m_msTimePlayed(0),
- m_sampleCountResetPending(FALSE)
+ m_sampleCountResetPending(FALSE),
+ m_resetTriggerSampleCount(0),
+ m_resetTriggerUnplayedCount(0)
{
// add the session to the server
HX_ASSERT(m_pServer);
@@ -494,25 +496,23 @@
}
}
-// calculate diff from 'base' to 'current'
-inline
-TUint WrapDiff(TUint base, TUint current)
-{
- if (base <= current)
+void HXSymbianAudioSession::OnResetSampleCount()
{
- return current - base;
- }
+ // sample count may reset prior to us seeing all the samples play out
+ TUint msUndetected = SamplesToMS(m_unplayedSampleCount, m_sampleRate);
+ HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::OnResetSampleCount(): adding undetected time (%lu ms)", msUndetected);
- // assume 'current' wrapped around - return KMaxTUint - base + current + 1; -} + // add time not accounted for + m_msTimePlayed += msUndetected;
-void HXSymbianAudioSession::OnResetSampleCount() -{ - HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::OnResetSampleCount()"); - m_sampleCountResetPending = FALSE; m_lastSampleCount = 0; m_unplayedSampleCount = 0; + + m_resetTriggerSampleCount = 0; + m_resetTriggerUnplayedCount = 0; + + m_sampleCountResetPending = FALSE; + }
void HXSymbianAudioSession::CheckSampleCountReset(TUint sampleCount) @@ -528,14 +528,16 @@
if (m_sampleCountResetPending)
{
- HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::HandleSampleCountReset(): checking for reset (last count = %lu; new count = %lu; samps written = %lu)", m_lastSampleCount, sampleCount, m_samplesWritten);
+ HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::HandleSampleCountReset(): checking for reset (last count = %lu; new count = %lu)", m_lastSampleCount, sampleCount);
if ( sampleCount < m_lastSampleCount || 0 == m_lastSampleCount)
{
OnResetSampleCount();
}
else
{
- if (m_unplayedSampleCount > 0 && sampleCount - m_lastSampleCount > m_unplayedSampleCount)
+ HX_ASSERT(sampleCount >= m_resetTriggerSampleCount);
+ TUint samplesElapsedSinceTrigger = sampleCount - m_resetTriggerSampleCount;
+ if (m_resetTriggerUnplayedCount > 0 && samplesElapsedSinceTrigger > m_resetTriggerUnplayedCount)
{
// Special case:
//
@@ -545,18 +547,54 @@
// samples played with the number of unwritten samples at the time of the underflow/pause.
// If the value is greater it must be from newly written samples.
//
- HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::HandleSampleCountReset(): special case additional played %lu > unplayed %lu; doing sample count reset", sampleCount - m_lastSampleCount, m_unplayedSampleCount);
+ HXLOGL3(HXLOG_ADEV,
+ "HXSymbianAudioSession::HandleSampleCountReset(): special case played since trigger %lu > unplayed at time of trigger %lu", samplesElapsedSinceTrigger, m_resetTriggerUnplayedCount);
OnResetSampleCount();
}
+ else
+ {
+ TUint samplesElapsed = sampleCount - m_lastSampleCount;
+
+ // sample count has not reset yet; some more samples have played out before the reset
+ OnNewSampleCount(sampleCount);
+
+ // decrement unplayed samples
+ HX_ASSERT(samplesElapsed <= m_unplayedSampleCount);
+ m_unplayedSampleCount -= samplesElapsed;
+ }
}
}
}
+void HXSymbianAudioSession::OnNewSampleCount(UINT32 sampleCount)
+{
+ // determine how much time has elapsed since last time computation
+
+ // assert likely indicates error; wrap-around is rare case
+ HX_ASSERT(m_lastSampleCount <= sampleCount);
+
+ // calculate additional samples played since last update
+ TUint samplesElapsed = sampleCount - m_lastSampleCount;
+
+ // convert samples-played to time-played
+ TUint msElapsed = SamplesToMS(samplesElapsed, m_sampleRate);
+
+ // update current play time
+ m_msTimePlayed += msElapsed;
+
+ HXLOGL4(HXLOG_ADEV,
+ "HXSymbianAudioSession::OnNewSampleCount(): last = %lu; current = %lu; added = %lu ms; total = %lu ms",
+ m_lastSampleCount, sampleCount, msElapsed, m_msTimePlayed);
+
+ // update sample count for next time
+ m_lastSampleCount = sampleCount;
+}
+
void HXSymbianAudioSession::UpdateUnplayedSampleCount()
{
- // keep track of samples that we wrote but haven't been played yet
if (!m_sampleCountResetPending)
{
+ // keep track of samples that we wrote but haven't been played yet
HX_ASSERT(m_samplesWritten >= m_lastSampleCount);
m_unplayedSampleCount = m_samplesWritten - m_lastSampleCount;
}
@@ -564,15 +602,14 @@
HXLOGL4(HXLOG_ADEV, "HXSymbianAudioSession::UpdateUnplayedSampleCount(): unplayed samps = %lu (%lu ms)", m_unplayedSampleCount, SamplesToMS(m_unplayedSampleCount, m_sampleRate));
}
- void HXSymbianAudioSession::UpdateTimeSampleStats() { if (PLAYING != m_state) { - // samples written count may have increased + // in case samples written increased since last check UpdateUnplayedSampleCount();
- // sample count is only reliable in PLAYING state
+ // sample count is only reliable in PLAYING state; don't do anything else at this time
HXLOGL4(HXLOG_ADEV, "HXSymbianAudioSession::UpdateUnplayedSampleCount(): state = %s (no update)", StringifyState(m_state));
return;
}
@@ -597,47 +634,23 @@
// at time of the reset. Once those are played out the sample count is reset again.
//
- //
-
- // determine how much time has elapsed since last time computation
TUint sampleCount = m_pStream->SamplesPlayed(); //returns TInt (max ~13.5 hours for 44Khz)
CheckSampleCountReset(sampleCount);
if (m_sampleCountResetPending)
{
- // don't update time; we only want to add time for samples played since sample count reset
return;
}
-
- if (sampleCount < m_lastSampleCount)
+ else if (sampleCount < m_lastSampleCount)
{
// Assume this is case where we see two resets, not the relatively rare wrap-arround case. The
- // first reset is apparantly for for unplayed samples at time of underflow. See (3) above
-
+ // first reset is apparantly for for unplayed samples at time of underflow. See (3) above.
HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::UpdateTimeSampleStats(): oops (unexpected reset): %lu (last) > %lu (current); unplayed = %lu", m_lastSampleCount, sampleCount, m_unplayedSampleCount);
return;
}
- // assert likely indicates error; wrap-around is rare case
- HX_ASSERT(m_lastSampleCount <= sampleCount);
-
- // calculate additional samples played since last update
- TUint samplesElapsed = WrapDiff(m_lastSampleCount, sampleCount);
-
- // convert samples-played to time-played
- TUint msElapsed = SamplesToMS(samplesElapsed, m_sampleRate);
-
- // update current play time
- m_msTimePlayed += msElapsed;
-
+ OnNewSampleCount(sampleCount);
UpdateUnplayedSampleCount();
-
- HXLOGL4(HXLOG_ADEV,
- "HXSymbianAudioSession::UpdateTimeSampleStats(): last = %lu; current = %lu; added = %lu ms; total = %lu ms",
- m_lastSampleCount, sampleCount, msElapsed, m_msTimePlayed);
-
- // update sample count for next time
- m_lastSampleCount = sampleCount;
}
//
@@ -832,22 +845,29 @@
//
void HXSymbianAudioSession::PrepareForDeviceReset()
{
+ HX_ASSERT(!m_sampleCountResetPending);
+
if (KErrNone == m_lastPlayError)
{
- // Add unwritten samples to time played. Otherwise we can miss counting these
- // samples when the sample count resets. We don't know for sure when the count
- // will reset. This causes us to jump ahead in time until we catch up.
-
+ // preserve monotically increasing time when device resumes
UpdateTimeSampleStats();
- TUint msPending = SamplesToMS(m_unplayedSampleCount, m_sampleRate);
- HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::PrepareForDeviceReset(): unplayed = %lu (%lu ms)", m_unplayedSampleCount, msPending);
-
- m_msTimePlayed += msPending;
+ m_resetTriggerSampleCount = m_lastSampleCount;
+ m_resetTriggerUnplayedCount = m_unplayedSampleCount;
+ HXLOGL3(HXLOG_ADEV, "HXSymbianAudioSession::PrepareForDeviceReset(): unplayed = %lu (%lu ms)", m_unplayedSampleCount, SamplesToMS(m_unplayedSampleCount, m_sampleRate));
+ }
+ else
+ {
+ // next playback must resume from beginning
+ HX_ASSERT(STOPPED == m_state);
+ m_resetTriggerSampleCount = 0;
+ m_resetTriggerUnplayedCount = 0;
+ m_lastSampleCount = 0;
+ m_unplayedSampleCount = 0;
}
m_sampleCountResetPending = TRUE;
- // samples written track samples written post device reset + // this tracks samples written after device reset trigger m_samplesWritten = 0;
m_pPendingFillBuffer = NULL;
Index: platform/symbian/audiosvr/mmf/audio_session-mmf.h
===================================================================
RCS file: /cvsroot/audio/device/platform/symbian/audiosvr/mmf/audio_session-mmf.h,v
retrieving revision 1.7
diff -u -w -r1.7 audio_session-mmf.h
--- platform/symbian/audiosvr/mmf/audio_session-mmf.h 13 Apr 2005 20:34:17 -0000 1.7
+++ platform/symbian/audiosvr/mmf/audio_session-mmf.h 20 Apr 2005 01:04:59 -0000
@@ -115,6 +115,7 @@
void DoPlayInit(HXBOOL setPriority = TRUE);
void UpdateTimeSampleStats();
void UpdateUnplayedSampleCount();
+ void OnNewSampleCount(UINT32 sampleCount);
void CheckSampleCountReset(TUint sampleCount);
void OnResetSampleCount();
void PrepareForDeviceReset();
@@ -154,6 +155,8 @@
TUint m_unplayedSampleCount;
TUint m_msTimePlayed;
HXBOOL m_sampleCountResetPending;
+ TUint m_resetTriggerSampleCount;
+ TUint m_resetTriggerUnplayedCount;
};
_______________________________________________ Audio-dev mailing list Audio-dev@helixcommunity.org http://lists.helixcommunity.org/mailman/listinfo/audio-dev