AudioSinkWrapper.h (7065B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef AudioSinkWrapper_h_ 8 #define AudioSinkWrapper_h_ 9 10 #include "AudioSink.h" 11 #include "MediaSink.h" 12 #include "mozilla/AbstractThread.h" 13 #include "mozilla/EventTargetCapability.h" 14 #include "mozilla/RefPtr.h" 15 #include "mozilla/TimeStamp.h" 16 #include "mozilla/UniquePtr.h" 17 18 namespace mozilla { 19 class MediaData; 20 template <class T> 21 class MediaQueue; 22 23 /** 24 * A wrapper around AudioSink to provide the interface of MediaSink. 25 */ 26 class AudioSinkWrapper : public MediaSink { 27 using PlaybackParams = AudioSink::PlaybackParams; 28 using SinkCreator = std::function<UniquePtr<AudioSink>()>; 29 30 public: 31 AudioSinkWrapper(AbstractThread* aOwnerThread, 32 MediaQueue<AudioData>& aAudioQueue, SinkCreator aFunc, 33 double aVolume, double aPlaybackRate, bool aPreservesPitch, 34 RefPtr<AudioDeviceInfo> aAudioDevice) 35 : mOwnerThread(aOwnerThread), 36 mAsyncInitTaskQueue(CreateAsyncInitTaskQueue()), 37 mSinkCreator(std::move(aFunc)), 38 mAudioDevice(std::move(aAudioDevice)), 39 mParams(aVolume, aPlaybackRate, aPreservesPitch), 40 mAudioQueue(aAudioQueue), 41 mRetrySinkTime(TimeStamp::Now()) { 42 MOZ_ASSERT(mAsyncInitTaskQueue); 43 } 44 45 RefPtr<EndedPromise> OnEnded(TrackType aType) override; 46 media::TimeUnit GetEndTime(TrackType aType) const override; 47 media::TimeUnit GetPosition(TimeStamp* aTimeStamp = nullptr) override; 48 bool HasUnplayedFrames(TrackType aType) const override; 49 media::TimeUnit UnplayedDuration(TrackType aType) const override; 50 void DropAudioPacketsIfNeeded(const media::TimeUnit& aMediaPosition); 51 52 void SetVolume(double aVolume) override; 53 void SetStreamName(const nsAString& aStreamName) override; 54 void SetPlaybackRate(double aPlaybackRate) override; 55 void SetPreservesPitch(bool aPreservesPitch) override; 56 void SetPlaying(bool aPlaying) override; 57 RefPtr<GenericPromise> SetAudioDevice( 58 RefPtr<AudioDeviceInfo> aDevice) override; 59 60 double PlaybackRate() const override; 61 62 nsresult Start(const media::TimeUnit& aStartTime, 63 const MediaInfo& aInfo) override; 64 void Stop() override; 65 bool IsStarted() const override; 66 bool IsPlaying() const override; 67 68 void Shutdown() override; 69 70 void GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) override; 71 72 private: 73 // The clock that was in use for the previous position query, allowing to 74 // detect clock switches. 75 enum class ClockSource { 76 // The clock comes from an underlying system-level audio stream. 77 AudioStream, 78 // The clock comes from the system clock. 79 SystemClock, 80 // The stream is paused, a constant time is reported. 81 Paused 82 } mLastClockSource = ClockSource::Paused; 83 static already_AddRefed<TaskQueue> CreateAsyncInitTaskQueue(); 84 bool IsMuted() const; 85 void OnMuted(bool aMuted); 86 virtual ~AudioSinkWrapper(); 87 88 void AssertOwnerThread() const MOZ_ASSERT_CAPABILITY(mOwnerThread) { 89 mOwnerThread.AssertOnCurrentThread(); 90 } 91 92 bool NeedAudioSink(); 93 void StartAudioSink(UniquePtr<AudioSink> aAudioSink, 94 const media::TimeUnit& aStartTime); 95 void ShutDownAudioSink(); 96 // Create and start mAudioSink. 97 // An AudioSink can be started synchronously from the MDSM thread, or 98 // asynchronously. 99 // In synchronous mode, the clock doesn't advance until the sink has been 100 // created, initialized and started. This is useful for the initial startup, 101 // and when seeking. 102 // In asynchronous mode, the clock will keep going forward (using the system 103 // clock) until the AudioSink is started, at which point the clock will use 104 // the AudioSink clock. This is used when unmuting a media element or 105 // switching audio output devices. The promise is resolved when the 106 // previous device is no longer in use and an attempt to open the new device 107 // completes (successfully or not) or is deemed unnecessary because the 108 // device is not required for output at this time. 109 nsresult SyncCreateAudioSink(const media::TimeUnit& aStartTime); 110 RefPtr<GenericPromise> MaybeAsyncCreateAudioSink( 111 RefPtr<AudioDeviceInfo> aDevice); 112 void ScheduleRetrySink(); 113 114 // Get the current media position using the system clock. This is used when 115 // the audio is muted, or when the media has no audio track. Otherwise, the 116 // media's position is based on the clock of the AudioStream. 117 media::TimeUnit GetSystemClockPosition(TimeStamp aNow) const; 118 bool CheckIfEnded() const; 119 120 void OnAudioEnded(const EndedPromise::ResolveOrRejectValue& aValue); 121 122 bool IsAudioSourceEnded(const MediaInfo& aInfo) const; 123 124 const EventTargetCapability<AbstractThread> mOwnerThread; 125 const RefPtr<TaskQueue> mAsyncInitTaskQueue; 126 SinkCreator mSinkCreator; 127 UniquePtr<AudioSink> mAudioSink; 128 // The output device this AudioSink is playing data to. The system's default 129 // device is used if this is null. 130 RefPtr<AudioDeviceInfo> mAudioDevice; 131 // Will only exist when media has an audio track. 132 RefPtr<EndedPromise> mEndedPromise; 133 MozPromiseHolder<EndedPromise> mEndedPromiseHolder; 134 // true between Start() and Stop() 135 bool mIsStarted = false; 136 PlaybackParams MOZ_GUARDED_BY(mOwnerThread) mParams; 137 // mClockStartTime is null before Start(), after Stop(), and between 138 // SetPlaying(false) and SetPlaying(true). When the system time is used for 139 // the clock, this is the time corresponding to mPositionAtClockStart. When 140 // an AudioStream is used for the clock, non-null values don't have specific 141 // meaning beyond indicating that the clock is advancing. 142 TimeStamp mClockStartTime; 143 // The media position at the clock datum. If the clock is not advancing, 144 // then this is the media position from which to resume playback. The value 145 // is Invalid() before Start() to facilitate debug. 146 media::TimeUnit mPositionAtClockStart = media::TimeUnit::Invalid(); 147 // End time of last packet played or dropped. 148 // Only up-to-date when there is no AudioSink. 149 media::TimeUnit mLastPacketEndTime; 150 151 bool mAudioEnded = true; 152 // mAudioSinkEndedRequest is connected when and only when mAudioSink is set 153 // and not ended. 154 MozPromiseRequestHolder<EndedPromise> mAudioSinkEndedRequest; 155 MediaQueue<AudioData>& mAudioQueue; 156 157 // Time when next to re-try AudioSink creation. 158 // Set to a useful value only when another sink is needed. At other times 159 // it needs to be non-null for a comparison where the result will be 160 // irrelevant. 161 // This is checked in GetPosition() which is triggered periodically during 162 // playback by MediaDecoderStateMachine::UpdatePlaybackPositionPeriodically() 163 TimeStamp mRetrySinkTime; 164 // Number of async AudioSink creation tasks in flight 165 uint32_t mAsyncCreateCount = 0; 166 }; 167 168 } // namespace mozilla 169 170 #endif // AudioSinkWrapper_h_