Backed out changeset 1f72106b618f (bug 1923744) on request for causing crashes. a...
[gecko.git] / dom / media / mediasink / AudioDecoderInputTrack.h
blob995ebffbcc93fe2e003c74c80aae8da3f8b2f827
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifndef AudioDecoderInputTrack_h
6 #define AudioDecoderInputTrack_h
8 #include "AudioSegment.h"
9 #include "MediaEventSource.h"
10 #include "MediaTimer.h"
11 #include "MediaTrackGraph.h"
12 #include "MediaSegment.h"
13 #include "TimeUnits.h"
14 #include "mozilla/SPSCQueue.h"
15 #include "mozilla/StateMirroring.h"
16 #include "mozilla/TimeStamp.h"
17 #include "nsISerialEventTarget.h"
19 namespace mozilla {
21 class AudioData;
22 class AudioInfo;
23 class RLBoxSoundTouch;
25 /**
26 * AudioDecoderInputTrack is used as a source for the audio decoder data, which
27 * supports adjusting playback rate and preserve pitch.
28 * The owner of this track would be responsible to push audio data via
29 * `AppendData()` into a SPSC queue, which is a thread-safe queue between the
30 * decoder thread (producer) and the graph thread (consumer). MediaTrackGraph
31 * requires data via `ProcessInput()`, then AudioDecoderInputTrack would convert
32 * (based on sample rate and playback rate) and append the amount of needed
33 * audio frames onto the output segment that would be used by MediaTrackGraph.
35 class AudioDecoderInputTrack final : public ProcessedMediaTrack {
36 public:
37 static AudioDecoderInputTrack* Create(MediaTrackGraph* aGraph,
38 nsISerialEventTarget* aDecoderThread,
39 const AudioInfo& aInfo,
40 float aPlaybackRate, float aVolume,
41 bool aPreservesPitch);
43 // SPSCData suppports filling different supported type variants, and is used
44 // to achieve a thread-safe information exchange between the decoder thread
45 // and the graph thread.
46 struct SPSCData final {
47 struct Empty {};
48 struct ClearFutureData {};
49 struct DecodedData {
50 DecodedData()
51 : mStartTime(media::TimeUnit::Invalid()),
52 mEndTime(media::TimeUnit::Invalid()) {}
53 DecodedData(DecodedData&& aDecodedData)
54 : mSegment(std::move(aDecodedData.mSegment)) {
55 mStartTime = aDecodedData.mStartTime;
56 mEndTime = aDecodedData.mEndTime;
57 aDecodedData.Clear();
59 DecodedData(media::TimeUnit aStartTime, media::TimeUnit aEndTime)
60 : mStartTime(aStartTime), mEndTime(aEndTime) {}
61 DecodedData(const DecodedData&) = delete;
62 DecodedData& operator=(const DecodedData&) = delete;
63 void Clear() {
64 mSegment.Clear();
65 mStartTime = media::TimeUnit::Invalid();
66 mEndTime = media::TimeUnit::Invalid();
68 AudioSegment mSegment;
69 media::TimeUnit mStartTime;
70 media::TimeUnit mEndTime;
72 struct EOS {};
74 SPSCData() : mData(Empty()) {};
75 explicit SPSCData(ClearFutureData&& aArg) : mData(std::move(aArg)) {};
76 explicit SPSCData(DecodedData&& aArg) : mData(std::move(aArg)) {};
77 explicit SPSCData(EOS&& aArg) : mData(std::move(aArg)) {};
79 bool HasData() const { return !mData.is<Empty>(); }
80 bool IsClearFutureData() const { return mData.is<ClearFutureData>(); }
81 bool IsDecodedData() const { return mData.is<DecodedData>(); }
82 bool IsEOS() const { return mData.is<EOS>(); }
84 DecodedData* AsDecodedData() {
85 return IsDecodedData() ? &mData.as<DecodedData>() : nullptr;
88 Variant<Empty, ClearFutureData, DecodedData, EOS> mData;
91 // Decoder thread API
92 void AppendData(AudioData* aAudio, const PrincipalHandle& aPrincipalHandle);
93 void AppendData(nsTArray<RefPtr<AudioData>>& aAudioArray,
94 const PrincipalHandle& aPrincipalHandle);
95 void NotifyEndOfStream();
96 void ClearFutureData();
97 void SetVolume(float aVolume);
98 void SetPlaybackRate(float aPlaybackRate);
99 void SetPreservesPitch(bool aPreservesPitch);
100 // After calling this, the track are not expected to receive any new data.
101 void Close();
102 bool HasBatchedData() const;
104 MediaEventSource<int64_t>& OnOutput() { return mOnOutput; }
105 MediaEventSource<void>& OnEnd() { return mOnEnd; }
107 // Graph Thread API
108 void DestroyImpl() override;
109 void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
110 uint32_t NumberOfChannels() const override;
112 // The functions below are only used for testing.
113 TrackTime WrittenFrames() const {
114 AssertOnGraphThread();
115 return mWrittenFrames;
117 float Volume() const {
118 AssertOnGraphThread();
119 return mVolume;
121 float PlaybackRate() const {
122 AssertOnGraphThread();
123 return mPlaybackRate;
126 protected:
127 ~AudioDecoderInputTrack();
129 private:
130 AudioDecoderInputTrack(nsISerialEventTarget* aDecoderThread,
131 TrackRate aGraphRate, const AudioInfo& aInfo,
132 float aPlaybackRate, float aVolume,
133 bool aPreservesPitch);
135 // Return false if the converted segment contains zero duration.
136 bool ConvertAudioDataToSegment(AudioData* aAudio, AudioSegment& aSegment,
137 const PrincipalHandle& aPrincipalHandle);
139 void HandleSPSCData(SPSCData& aData);
141 // These methods would return the total frames that we consumed from
142 // `mBufferedData`.
143 TrackTime AppendBufferedDataToOutput(TrackTime aExpectedDuration);
144 TrackTime FillDataToTimeStretcher(TrackTime aExpectedDuration);
145 TrackTime AppendTimeStretchedDataToSegment(TrackTime aExpectedDuration,
146 AudioSegment& aOutput);
147 TrackTime AppendUnstretchedDataToSegment(TrackTime aExpectedDuration,
148 AudioSegment& aOutput);
150 // Return the total frames that we retrieve from the time stretcher.
151 TrackTime DrainStretchedDataIfNeeded(TrackTime aExpectedDuration,
152 AudioSegment& aOutput);
153 TrackTime GetDataFromTimeStretcher(TrackTime aExpectedDuration,
154 AudioSegment& aOutput);
155 void NotifyInTheEndOfProcessInput(TrackTime aFillDuration);
157 bool HasSentAllData() const;
159 bool ShouldBatchData() const;
160 void BatchData(AudioData* aAudio, const PrincipalHandle& aPrincipalHandle);
161 void DispatchPushBatchedDataIfNeeded();
162 void PushBatchedDataIfNeeded();
163 void PushDataToSPSCQueue(SPSCData& data);
165 void SetVolumeImpl(float aVolume);
166 void SetPlaybackRateImpl(float aPlaybackRate);
167 void SetPreservesPitchImpl(bool aPreservesPitch);
169 void EnsureTimeStretcher();
170 void SetTempoAndRateForTimeStretcher();
171 uint32_t GetChannelCountForTimeStretcher() const;
173 inline void AssertOnDecoderThread() const {
174 MOZ_ASSERT(mDecoderThread->IsOnCurrentThread());
177 const RefPtr<nsISerialEventTarget> mDecoderThread;
179 // Notify the amount of audio frames which have been sent to the track.
180 MediaEventProducer<int64_t> mOnOutput;
181 // Notify when the track is ended.
182 MediaEventProducer<void> mOnEnd;
184 // These variables are ONLY used in the decoder thread.
185 nsAutoRef<SpeexResamplerState> mResampler;
186 uint32_t mResamplerChannelCount;
187 const uint32_t mInitialInputChannels;
188 TrackRate mInputSampleRate;
189 DelayedScheduler<TimeStamp> mDelayedScheduler;
190 bool mShutdownSPSCQueue = false;
192 // These attributes are ONLY used in the graph thread.
193 bool mReceivedEOS = false;
194 TrackTime mWrittenFrames = 0;
195 float mPlaybackRate;
196 float mVolume;
197 bool mPreservesPitch;
199 // A thread-safe queue shared by the decoder thread and the graph thread.
200 // The decoder thread is the producer side, and the graph thread is the
201 // consumer side. This queue should NEVER get full. In order to achieve that,
202 // we would batch input samples when SPSC queue doesn't have many available
203 // capacity.
204 // In addition, as the media track isn't guaranteed to be destroyed on the
205 // graph thread (it could be destroyed on the main thread as well) so we might
206 // not clear all data in SPSC queue when the track's `DestroyImpl()` gets
207 // called. We leave to destroy the queue later when the track gets destroyed.
208 SPSCQueue<SPSCData> mSPSCQueue{40};
210 // When the graph requires the less amount of audio frames than the amount of
211 // frames an audio data has, then the remaining part of frames would be stored
212 // and used in next iteration.
213 // This is ONLY used in the graph thread.
214 AudioSegment mBufferedData;
216 // In order to prevent SPSC queue from being full, we want to batch multiple
217 // data into one to control the density of SPSC queue, the length of batched
218 // data would be dynamically adjusted by queue's available capacity.
219 // This is ONLY used in the decoder thread.
220 SPSCData::DecodedData mBatchedData;
222 // True if we've sent all data to the graph, then the track will be marked as
223 // ended in the next iteration.
224 bool mSentAllData = false;
226 // This is used to adjust the playback rate and pitch.
227 RLBoxSoundTouch* mTimeStretcher = nullptr;
229 // Buffers that would be used for the time stretching.
230 AutoTArray<AudioDataValue, 2> mInterleavedBuffer;
233 } // namespace mozilla
235 #endif // AudioDecoderInputTrack_h