1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 #ifndef AudioSink_h__
7 #define AudioSink_h__
8 
9 #include "AudioStream.h"
10 #include "AudibilityMonitor.h"
11 #include "MediaEventSource.h"
12 #include "MediaInfo.h"
13 #include "MediaQueue.h"
14 #include "MediaSink.h"
15 #include "mozilla/Atomics.h"
16 #include "mozilla/Maybe.h"
17 #include "mozilla/Monitor.h"
18 #include "mozilla/MozPromise.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/Result.h"
21 #include "nsISupportsImpl.h"
22 
23 namespace mozilla {
24 
25 class AudioConverter;
26 
27 class AudioSink : private AudioStream::DataSource {
28  public:
29   struct PlaybackParams {
PlaybackParamsPlaybackParams30     PlaybackParams(double aVolume, double aPlaybackRate, bool aPreservesPitch)
31         : mVolume(aVolume),
32           mPlaybackRate(aPlaybackRate),
33           mPreservesPitch(aPreservesPitch) {}
34     double mVolume;
35     double mPlaybackRate;
36     bool mPreservesPitch;
37   };
38 
39   AudioSink(AbstractThread* aThread, MediaQueue<AudioData>& aAudioQueue,
40             const media::TimeUnit& aStartTime, const AudioInfo& aInfo,
41             AudioDeviceInfo* aAudioDevice);
42 
43   ~AudioSink();
44 
45   // Start audio playback and return a promise which will be resolved when the
46   // playback finishes, or return an error result if any error occurs.
47   Result<already_AddRefed<MediaSink::EndedPromise>, nsresult> Start(
48       const PlaybackParams& aParams);
49 
50   /*
51    * All public functions are not thread-safe.
52    * Called on the task queue of MDSM only.
53    */
54   media::TimeUnit GetPosition();
55   media::TimeUnit GetEndTime() const;
56 
57   // Check whether we've pushed more frames to the audio stream than it
58   // has played.
59   bool HasUnplayedFrames();
60 
61   // The duration of the buffered frames.
62   media::TimeUnit UnplayedDuration() const;
63 
64   // Shut down the AudioSink's resources.
65   void Shutdown();
66 
67   void SetVolume(double aVolume);
68   void SetStreamName(const nsAString& aStreamName);
69   void SetPlaybackRate(double aPlaybackRate);
70   void SetPreservesPitch(bool aPreservesPitch);
71   void SetPlaying(bool aPlaying);
72 
AudibleEvent()73   MediaEventSource<bool>& AudibleEvent() { return mAudibleEvent; }
74 
75   void GetDebugInfo(dom::MediaSinkDebugInfo& aInfo);
76 
AudioDevice()77   const RefPtr<AudioDeviceInfo>& AudioDevice() { return mAudioDevice; }
78 
79  private:
80   // Allocate and initialize mAudioStream. Returns NS_OK on success.
81   nsresult InitializeAudioStream(const PlaybackParams& aParams);
82 
83   // Interface of AudioStream::DataSource.
84   // Called on the callback thread of cubeb. Returns the number of frames that
85   // were available.
86   uint32_t PopFrames(AudioDataValue* aBuffer, uint32_t aFrames,
87                      bool aAudioThreadChanged) override;
88   bool Ended() const override;
89 
90   void CheckIsAudible(const Span<AudioDataValue>& aInterleaved,
91                       size_t aChannel);
92 
93   // The audio stream resource. Used on the task queue of MDSM only.
94   RefPtr<AudioStream> mAudioStream;
95 
96   // The presentation time of the first audio frame that was played.
97   // We can add this to the audio stream position to determine
98   // the current audio time.
99   const media::TimeUnit mStartTime;
100 
101   // Keep the last good position returned from the audio stream. Used to ensure
102   // position returned by GetPosition() is mono-increasing in spite of audio
103   // stream error. Used on the task queue of MDSM only.
104   media::TimeUnit mLastGoodPosition;
105 
106   const AudioInfo mInfo;
107 
108   // The output device this AudioSink is playing data to. The system's default
109   // device is used if this is null.
110   const RefPtr<AudioDeviceInfo> mAudioDevice;
111 
112   // Used on the task queue of MDSM only.
113   bool mPlaying;
114 
115   // PCM frames written to the stream so far. Written on the callback thread,
116   // read on the MDSM thread.
117   Atomic<int64_t> mWritten;
118 
119   // True if there is any error in processing audio data like overflow.
120   Atomic<bool> mErrored;
121 
122   const RefPtr<AbstractThread> mOwnerThread;
123 
124   // Audio Processing objects and methods
125   void OnAudioPopped();
126   void OnAudioPushed(const RefPtr<AudioData>& aSample);
127   void NotifyAudioNeeded();
128   // Drain the converter and add the output to the processed audio queue.
129   // A maximum of aMaxFrames will be added.
130   uint32_t DrainConverter(uint32_t aMaxFrames = UINT32_MAX);
131   already_AddRefed<AudioData> CreateAudioFromBuffer(
132       AlignedAudioBuffer&& aBuffer, AudioData* aReference);
133   // Add data to the processsed queue return the number of frames added.
134   uint32_t PushProcessedAudio(AudioData* aData);
135   uint32_t AudioQueuedInRingBufferMS() const;
136   uint32_t SampleToFrame(uint32_t aSamples) const;
137   UniquePtr<AudioConverter> mConverter;
138   UniquePtr<SPSCQueue<AudioDataValue>> mProcessedSPSCQueue;
139   MediaEventListener mAudioQueueListener;
140   MediaEventListener mAudioQueueFinishListener;
141   MediaEventListener mProcessedQueueListener;
142   // Number of frames processed from mAudioQueue. Used to determine gaps in
143   // the input stream. It indicates the time in frames since playback started
144   // at the current input framerate.
145   int64_t mFramesParsed;
146   Maybe<RefPtr<AudioData>> mLastProcessedPacket;
147   media::TimeUnit mLastEndTime;
148   // Never modifed after construction.
149   uint32_t mOutputRate;
150   uint32_t mOutputChannels;
151   AudibilityMonitor mAudibilityMonitor;
152   bool mIsAudioDataAudible;
153   MediaEventProducer<bool> mAudibleEvent;
154   // Only signed on the real-time audio thread.
155   MediaEventProducer<void> mAudioPopped;
156 
157   Atomic<bool> mProcessedQueueFinished;
158   MediaQueue<AudioData>& mAudioQueue;
159   const float mProcessedQueueThresholdMS;
160 };
161 
162 }  // namespace mozilla
163 
164 #endif  // AudioSink_h__
165