Bug 1743834 - Wait for the audio callbacks to start being called before using the audio clock. r=alwu,media-playback-reviewers
authorPaul Adenot <paul@paul.cx>
Tue, 24 May 2022 13:09:06 +0000
changeset 618728 30cd5bcfee929d4d1244c7135af0892614a553e4
parent 618727 acb4ae1c1e57de7130cbfb499d6c30dcf134dfbb
child 618729 37f01a81dac4f4767e2e05cd9e8dfcbac0ab7282
push id163385
push userpadenot@mozilla.com
push dateTue, 24 May 2022 13:12:53 +0000
treeherderautoland@3774e6ab3869 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersalwu, media-playback-reviewers
bugs1743834
milestone102.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1743834 - Wait for the audio callbacks to start being called before using the audio clock. r=alwu,media-playback-reviewers Because we're generally using high latency on the cubeb stream used by AudioStream instance, it can take some time for the callbacks to start being called, and the for the audio clock (cubeb_stream_get_position(...)) to advance. This waits for the first callback to be called before using the clock of the audio stream, and the system clock keeps being in use until then. Differential Revision: https://phabricator.services.mozilla.com/D136235
dom/media/AudioStream.cpp
dom/media/AudioStream.h
dom/media/mediasink/AudioSink.h
dom/media/mediasink/AudioSinkWrapper.cpp
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -143,17 +143,19 @@ AudioStream::AudioStream(DataSource& aSo
       mMonitor("AudioStream"),
       mOutChannels(aOutputChannels),
       mState(INITIALIZED),
       mDataSource(aSource),
       mAudioThreadId(ProfilerThreadId{}),
       mSandboxed(CubebUtils::SandboxEnabled()),
       mPlaybackComplete(false),
       mPlaybackRate(1.0f),
-      mPreservesPitch(true) {}
+      mPreservesPitch(true),
+      mCallbacksStarted(false)
+    {}
 
 AudioStream::~AudioStream() {
   LOG("deleted, state %d", mState.load());
   MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
              "Should've called Shutdown() before deleting an AudioStream");
 }
 
 size_t AudioStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
@@ -226,18 +228,18 @@ int AudioStream::InvokeCubeb(Function aF
   return aFunction(mCubebStream.get(), std::forward<Args>(aArgs)...);
 }
 
 nsresult AudioStream::Init(AudioDeviceInfo* aSinkInfo)
     NO_THREAD_SAFETY_ANALYSIS {
   auto startTime = TimeStamp::Now();
   TRACE("AudioStream::Init");
 
-  LOG("%s channels: %d, rate: %d", __FUNCTION__, mOutChannels,
-      mAudioClock.GetInputRate());
+  LOG("%s channels: %d, rate: %d", __FUNCTION__, mOutChannels, mAudioClock.GetInputRate());
+
   mSinkInfo = aSinkInfo;
 
   cubeb_stream_params params;
   params.rate = mAudioClock.GetInputRate();
   params.channels = mOutChannels;
   params.layout = static_cast<uint32_t>(mChannelMap);
   params.format = CubebUtils::ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
   params.prefs = CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_OUTPUT);
@@ -590,16 +592,19 @@ void AudioStream::UpdatePlaybackRateIfNe
   }
 }
 
 long AudioStream::DataCallback(void* aBuffer, long aFrames) {
   if (CheckThreadIdChanged() && !mSandboxed) {
     CubebUtils::GetAudioThreadRegistry()->Register(mAudioThreadId);
   }
   WebCore::DenormalDisabler disabler;
+  if (!mCallbacksStarted) {
+    mCallbacksStarted = true;
+  }
 
   TRACE_AUDIO_CALLBACK_BUDGET(aFrames, mAudioClock.GetInputRate());
   TRACE("AudioStream::DataCallback");
   MOZ_ASSERT(mState != SHUTDOWN, "No data callback after shutdown");
 
   if (SoftRealTimeLimitReached()) {
     DemoteThreadFromRealTime();
   }
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -292,16 +292,19 @@ class AudioStream final {
   // Switch between resampling (if false) and time stretching (if true,
   // default).
   nsresult SetPreservesPitch(bool aPreservesPitch);
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   bool IsPlaybackCompleted() const;
 
+  // Returns true if at least one DataCallback has been called.
+  bool CallbackStarted() const { return mCallbacksStarted; }
+
  protected:
   friend class AudioClock;
 
   // Return the position, measured in audio frames played since the stream was
   // opened, of the audio hardware, not adjusted for the changes of playback
   // rate or underrun frames.
   // Caller must own the monitor.
   int64_t GetPositionInFramesUnlocked();
@@ -381,13 +384,14 @@ class AudioStream final {
 
   MozPromiseHolder<MediaSink::EndedPromise> mEndedPromise GUARDED_BY(mMonitor);
   std::atomic<bool> mPlaybackComplete;
   // Both written on the MDSM thread, read on the audio thread.
   std::atomic<float> mPlaybackRate;
   std::atomic<bool> mPreservesPitch;
   // Audio thread only
   bool mAudioThreadChanged = false;
+  Atomic<bool> mCallbacksStarted;
 };
 
 }  // namespace mozilla
 
 #endif
--- a/dom/media/mediasink/AudioSink.h
+++ b/dom/media/mediasink/AudioSink.h
@@ -72,16 +72,22 @@ class AudioSink : private AudioStream::D
   void SetPlaying(bool aPlaying);
 
   MediaEventSource<bool>& AudibleEvent() { return mAudibleEvent; }
 
   void GetDebugInfo(dom::MediaSinkDebugInfo& aInfo);
 
   const RefPtr<AudioDeviceInfo>& AudioDevice() { return mAudioDevice; }
 
+  // This returns true if the audio callbacks are being called, and so the
+  // audio stream-based clock is moving forward.
+  bool AudioStreamCallbackStarted() {
+    return mAudioStream && mAudioStream->CallbackStarted();
+  }
+
  private:
   // Allocate and initialize mAudioStream. Returns NS_OK on success.
   nsresult InitializeAudioStream(const PlaybackParams& aParams);
 
   // Interface of AudioStream::DataSource.
   // Called on the callback thread of cubeb. Returns the number of frames that
   // were available.
   uint32_t PopFrames(AudioDataValue* aBuffer, uint32_t aFrames,
--- a/dom/media/mediasink/AudioSinkWrapper.cpp
+++ b/dom/media/mediasink/AudioSinkWrapper.cpp
@@ -72,18 +72,17 @@ bool AudioSinkWrapper::IsMuted() const {
 
 TimeUnit AudioSinkWrapper::GetPosition(TimeStamp* aTimeStamp) {
   AssertOwnerThread();
   MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
 
   TimeUnit pos;
   TimeStamp t = TimeStamp::Now();
 
-  if (!mAudioEnded && !IsMuted()) {
-    MOZ_ASSERT(mAudioSink);
+  if (!mAudioEnded && !IsMuted() && mAudioSink && mAudioSink->AudioStreamCallbackStarted()) {
     // Rely on the audio sink to report playback position when it is not ended.
     pos = mAudioSink->GetPosition();
     LOGV("%p: Getting position from the Audio Sink %lf", this, pos.ToSeconds());
   } else if (!mPlayStartTime.IsNull()) {
     // Calculate playback position using system clock if we are still playing,
     // but not rendering the audio, because this audio sink is muted.
     pos = GetSystemClockPosition(t);
     LOGV("%p: Getting position from the system clock %lf", this,