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 "nsISerialEventTarget.h"
22 class RLBoxSoundTouch
;
25 * AudioDecoderInputTrack is used as a source for the audio decoder data, which
26 * supports adjusting playback rate and preserve pitch.
27 * The owner of this track would be responsible to push audio data via
28 * `AppendData()` into a SPSC queue, which is a thread-safe queue between the
29 * decoder thread (producer) and the graph thread (consumer). MediaTrackGraph
30 * requires data via `ProcessInput()`, then AudioDecoderInputTrack would convert
31 * (based on sample rate and playback rate) and append the amount of needed
32 * audio frames onto the output segment that would be used by MediaTrackGraph.
34 class AudioDecoderInputTrack final
: public ProcessedMediaTrack
{
36 static AudioDecoderInputTrack
* Create(MediaTrackGraph
* aGraph
,
37 nsISerialEventTarget
* aDecoderThread
,
38 const AudioInfo
& aInfo
,
39 float aPlaybackRate
, float aVolume
,
40 bool aPreservesPitch
);
42 // SPSCData suppports filling different supported type variants, and is used
43 // to achieve a thread-safe information exchange between the decoder thread
44 // and the graph thread.
45 struct SPSCData final
{
47 struct ClearFutureData
{};
50 : mStartTime(media::TimeUnit::Invalid()),
51 mEndTime(media::TimeUnit::Invalid()) {}
52 DecodedData(DecodedData
&& aDecodedData
)
53 : mSegment(std::move(aDecodedData
.mSegment
)) {
54 mStartTime
= aDecodedData
.mStartTime
;
55 mEndTime
= aDecodedData
.mEndTime
;
58 DecodedData(media::TimeUnit aStartTime
, media::TimeUnit aEndTime
)
59 : mStartTime(aStartTime
), mEndTime(aEndTime
) {}
60 DecodedData(const DecodedData
&) = delete;
61 DecodedData
& operator=(const DecodedData
&) = delete;
64 mStartTime
= media::TimeUnit::Invalid();
65 mEndTime
= media::TimeUnit::Invalid();
67 AudioSegment mSegment
;
68 media::TimeUnit mStartTime
;
69 media::TimeUnit mEndTime
;
73 SPSCData() : mData(Empty()){};
74 explicit SPSCData(ClearFutureData
&& aArg
) : mData(std::move(aArg
)){};
75 explicit SPSCData(DecodedData
&& aArg
) : mData(std::move(aArg
)){};
76 explicit SPSCData(EOS
&& aArg
) : mData(std::move(aArg
)){};
78 bool HasData() const { return !mData
.is
<Empty
>(); }
79 bool IsClearFutureData() const { return mData
.is
<ClearFutureData
>(); }
80 bool IsDecodedData() const { return mData
.is
<DecodedData
>(); }
81 bool IsEOS() const { return mData
.is
<EOS
>(); }
83 DecodedData
* AsDecodedData() {
84 return IsDecodedData() ? &mData
.as
<DecodedData
>() : nullptr;
87 Variant
<Empty
, ClearFutureData
, DecodedData
, EOS
> mData
;
91 void AppendData(AudioData
* aAudio
, const PrincipalHandle
& aPrincipalHandle
);
92 void AppendData(nsTArray
<RefPtr
<AudioData
>>& aAudioArray
,
93 const PrincipalHandle
& aPrincipalHandle
);
94 void NotifyEndOfStream();
95 void ClearFutureData();
96 void SetVolume(float aVolume
);
97 void SetPlaybackRate(float aPlaybackRate
);
98 void SetPreservesPitch(bool aPreservesPitch
);
99 // After calling this, the track are not expected to receive any new data.
101 bool HasBatchedData() const;
103 MediaEventSource
<int64_t>& OnOutput() { return mOnOutput
; }
104 MediaEventSource
<void>& OnEnd() { return mOnEnd
; }
107 void DestroyImpl() override
;
108 void ProcessInput(GraphTime aFrom
, GraphTime aTo
, uint32_t aFlags
) override
;
109 uint32_t NumberOfChannels() const override
;
111 // The functions below are only used for testing.
112 TrackTime
WrittenFrames() const {
113 AssertOnGraphThread();
114 return mWrittenFrames
;
116 float Volume() const {
117 AssertOnGraphThread();
120 float PlaybackRate() const {
121 AssertOnGraphThread();
122 return mPlaybackRate
;
126 ~AudioDecoderInputTrack();
129 AudioDecoderInputTrack(nsISerialEventTarget
* aDecoderThread
,
130 TrackRate aGraphRate
, const AudioInfo
& aInfo
,
131 float aPlaybackRate
, float aVolume
,
132 bool aPreservesPitch
);
134 // Return false if the converted segment contains zero duration.
135 bool ConvertAudioDataToSegment(AudioData
* aAudio
, AudioSegment
& aSegment
,
136 const PrincipalHandle
& aPrincipalHandle
);
138 void HandleSPSCData(SPSCData
& aData
);
140 // These methods would return the total frames that we consumed from
142 TrackTime
AppendBufferedDataToOutput(TrackTime aExpectedDuration
);
143 TrackTime
FillDataToTimeStretcher(TrackTime aExpectedDuration
);
144 TrackTime
AppendTimeStretchedDataToSegment(TrackTime aExpectedDuration
,
145 AudioSegment
& aOutput
);
146 TrackTime
AppendUnstretchedDataToSegment(TrackTime aExpectedDuration
,
147 AudioSegment
& aOutput
);
149 // Return the total frames that we retrieve from the time stretcher.
150 TrackTime
DrainStretchedDataIfNeeded(TrackTime aExpectedDuration
,
151 AudioSegment
& aOutput
);
152 TrackTime
GetDataFromTimeStretcher(TrackTime aExpectedDuration
,
153 AudioSegment
& aOutput
);
154 void NotifyInTheEndOfProcessInput(TrackTime aFillDuration
);
156 bool HasSentAllData() const;
158 bool ShouldBatchData() const;
159 void BatchData(AudioData
* aAudio
, const PrincipalHandle
& aPrincipalHandle
);
160 void DispatchPushBatchedDataIfNeeded();
161 void PushBatchedDataIfNeeded();
162 void PushDataToSPSCQueue(SPSCData
& data
);
164 void SetVolumeImpl(float aVolume
);
165 void SetPlaybackRateImpl(float aPlaybackRate
);
166 void SetPreservesPitchImpl(bool aPreservesPitch
);
168 void EnsureTimeStretcher();
169 void SetTempoAndRateForTimeStretcher();
170 uint32_t GetChannelCountForTimeStretcher() const;
172 inline void AssertOnDecoderThread() const {
173 MOZ_ASSERT(mDecoderThread
->IsOnCurrentThread());
176 const RefPtr
<nsISerialEventTarget
> mDecoderThread
;
178 // Notify the amount of audio frames which have been sent to the track.
179 MediaEventProducer
<int64_t> mOnOutput
;
180 // Notify when the track is ended.
181 MediaEventProducer
<void> mOnEnd
;
183 // These variables are ONLY used in the decoder thread.
184 nsAutoRef
<SpeexResamplerState
> mResampler
;
185 uint32_t mResamplerChannelCount
;
186 const uint32_t mInitialInputChannels
;
187 TrackRate mInputSampleRate
;
188 DelayedScheduler mDelayedScheduler
;
189 bool mShutdownSPSCQueue
= false;
191 // These attributes are ONLY used in the graph thread.
192 bool mReceivedEOS
= false;
193 TrackTime mWrittenFrames
= 0;
196 bool mPreservesPitch
;
198 // A thread-safe queue shared by the decoder thread and the graph thread.
199 // The decoder thread is the producer side, and the graph thread is the
200 // consumer side. This queue should NEVER get full. In order to achieve that,
201 // we would batch input samples when SPSC queue doesn't have many available
203 // In addition, as the media track isn't guaranteed to be destroyed on the
204 // graph thread (it could be destroyed on the main thread as well) so we might
205 // not clear all data in SPSC queue when the track's `DestroyImpl()` gets
206 // called. We leave to destroy the queue later when the track gets destroyed.
207 SPSCQueue
<SPSCData
> mSPSCQueue
{40};
209 // When the graph requires the less amount of audio frames than the amount of
210 // frames an audio data has, then the remaining part of frames would be stored
211 // and used in next iteration.
212 // This is ONLY used in the graph thread.
213 AudioSegment mBufferedData
;
215 // In order to prevent SPSC queue from being full, we want to batch multiple
216 // data into one to control the density of SPSC queue, the length of batched
217 // data would be dynamically adjusted by queue's available capacity.
218 // This is ONLY used in the decoder thread.
219 SPSCData::DecodedData mBatchedData
;
221 // True if we've sent all data to the graph, then the track will be marked as
222 // ended in the next iteration.
223 bool mSentAllData
= false;
225 // This is used to adjust the playback rate and pitch.
226 RLBoxSoundTouch
* mTimeStretcher
= nullptr;
228 // Buffers that would be used for the time stretching.
229 AutoTArray
<AudioDataValue
, 2> mInterleavedBuffer
;
232 } // namespace mozilla
234 #endif // AudioDecoderInputTrack_h