Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / media / MediaQueue.h
blob9c4c829c87446411dcd6df9f452ffae3b35ccfa0
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 #if !defined(MediaQueue_h_)
7 # define MediaQueue_h_
9 # include <type_traits>
11 # include "mozilla/RecursiveMutex.h"
12 # include "mozilla/TaskQueue.h"
14 # include "nsDeque.h"
15 # include "MediaEventSource.h"
16 # include "TimeUnits.h"
18 namespace mozilla {
20 extern LazyLogModule gMediaDecoderLog;
22 # define QLOG(msg, ...) \
23 MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
24 ("MediaQueue=%p " msg, this, ##__VA_ARGS__))
26 class AudioData;
27 class VideoData;
28 class EncodedFrame;
30 template <typename T>
31 struct TimestampAdjustmentTrait {
32 static const bool mValue = false;
35 template <>
36 struct TimestampAdjustmentTrait<AudioData> {
37 static const bool mValue = true;
40 template <>
41 struct TimestampAdjustmentTrait<VideoData> {
42 static const bool mValue = true;
45 template <typename T>
46 struct NonTimestampAdjustmentTrait {
47 static const bool mValue = !TimestampAdjustmentTrait<T>::mValue;
50 template <typename T>
51 struct DurationTypeTrait {
52 using type = media::TimeUnit;
55 template <>
56 struct DurationTypeTrait<EncodedFrame> {
57 using type = uint64_t;
60 template <class T>
61 class MediaQueue : private nsRefPtrDeque<T> {
62 public:
63 explicit MediaQueue(bool aEnablePreciseDuration = false)
64 : nsRefPtrDeque<T>(),
65 mRecursiveMutex("mediaqueue"),
66 mEndOfStream(false),
67 mEnablePreciseDuration(aEnablePreciseDuration) {}
69 ~MediaQueue() { Reset(); }
71 inline size_t GetSize() const {
72 RecursiveMutexAutoLock lock(mRecursiveMutex);
73 return nsRefPtrDeque<T>::GetSize();
76 template <typename U,
77 std::enable_if_t<TimestampAdjustmentTrait<U>::mValue, bool> = true>
78 inline void AdjustTimeStampIfNeeded(U* aItem) {
79 static_assert(std::is_same_v<U, AudioData> || std::is_same_v<U, VideoData>);
80 if (mOffset != media::TimeUnit::Zero()) {
81 const auto prev = aItem->mTime, prevEndTime = aItem->GetEndTime();
82 aItem->mTime += mOffset;
83 if (!aItem->mTime.IsValid()) {
84 NS_WARNING("Reverting timestamp adjustment due to sample overflow!");
85 aItem->mTime = prev;
86 } else {
87 QLOG("adjusted %s sample [%" PRId64 ",%" PRId64 "] -> [%" PRId64
88 ",%" PRId64 "]",
89 std::is_same_v<U, AudioData> ? "audio" : "video",
90 prev.ToMicroseconds(), prevEndTime.ToMicroseconds(),
91 aItem->mTime.ToMicroseconds(),
92 aItem->GetEndTime().ToMicroseconds());
97 template <typename U, std::enable_if_t<NonTimestampAdjustmentTrait<U>::mValue,
98 bool> = true>
99 inline void AdjustTimeStampIfNeeded(U* aItem) {}
101 enum class TimestampAdjustment {
102 Enable,
103 Disable,
105 inline void PushFront(
106 T* aItem, TimestampAdjustment aIsEnabled = TimestampAdjustment::Enable) {
107 RecursiveMutexAutoLock lock(mRecursiveMutex);
108 if (aIsEnabled == TimestampAdjustment::Enable) {
109 AdjustTimeStampIfNeeded(aItem);
111 nsRefPtrDeque<T>::PushFront(aItem);
112 AddDurationToPreciseDuration(aItem);
115 inline void Push(T* aItem) {
116 MOZ_DIAGNOSTIC_ASSERT(aItem);
117 Push(do_AddRef(aItem));
120 inline void Push(already_AddRefed<T> aItem) {
121 RecursiveMutexAutoLock lock(mRecursiveMutex);
122 T* item = aItem.take();
124 MOZ_DIAGNOSTIC_ASSERT(item);
125 MOZ_DIAGNOSTIC_ASSERT(item->GetEndTime() >= item->mTime);
126 AdjustTimeStampIfNeeded(item);
127 nsRefPtrDeque<T>::Push(dont_AddRef(item));
128 AddDurationToPreciseDuration(item);
129 mPushEvent.Notify(RefPtr<T>(item));
131 // Pushing new data after queue has ended means that the stream is active
132 // again, so we should not mark it as ended.
133 if (mEndOfStream) {
134 mEndOfStream = false;
138 inline already_AddRefed<T> PopFront() {
139 RecursiveMutexAutoLock lock(mRecursiveMutex);
140 RefPtr<T> rv = nsRefPtrDeque<T>::PopFront();
141 if (rv) {
142 MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
143 SubtractDurationFromPreciseDuration(rv);
144 mPopFrontEvent.Notify(RefPtr<T>(rv));
146 return rv.forget();
149 inline already_AddRefed<T> PopBack() {
150 RecursiveMutexAutoLock lock(mRecursiveMutex);
151 RefPtr<T> rv = nsRefPtrDeque<T>::Pop();
152 if (rv) {
153 MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
154 SubtractDurationFromPreciseDuration(rv);
156 return rv.forget();
159 inline RefPtr<T> PeekFront() const {
160 RecursiveMutexAutoLock lock(mRecursiveMutex);
161 return nsRefPtrDeque<T>::PeekFront();
164 inline RefPtr<T> PeekBack() const {
165 RecursiveMutexAutoLock lock(mRecursiveMutex);
166 return nsRefPtrDeque<T>::Peek();
169 void Reset() {
170 RecursiveMutexAutoLock lock(mRecursiveMutex);
171 nsRefPtrDeque<T>::Erase();
172 SetOffset(media::TimeUnit::Zero());
173 mEndOfStream = false;
174 ResetPreciseDuration();
177 bool AtEndOfStream() const {
178 RecursiveMutexAutoLock lock(mRecursiveMutex);
179 return GetSize() == 0 && mEndOfStream;
182 // Returns true if the media queue has had its last item added to it.
183 // This happens when the media stream has been completely decoded. Note this
184 // does not mean that the corresponding stream has finished playback.
185 bool IsFinished() const {
186 RecursiveMutexAutoLock lock(mRecursiveMutex);
187 return mEndOfStream;
190 // Informs the media queue that it won't be receiving any more items.
191 void Finish() {
192 RecursiveMutexAutoLock lock(mRecursiveMutex);
193 if (!mEndOfStream) {
194 mEndOfStream = true;
195 mFinishEvent.Notify();
199 // Returns the approximate number of microseconds of items in the queue.
200 int64_t Duration() const {
201 RecursiveMutexAutoLock lock(mRecursiveMutex);
202 if (GetSize() == 0) {
203 return 0;
205 T* last = nsRefPtrDeque<T>::Peek();
206 T* first = nsRefPtrDeque<T>::PeekFront();
207 return (last->GetEndTime() - first->mTime).ToMicroseconds();
210 // Return a precise duration if the feature is enabled. Otherwise, return -1.
211 int64_t PreciseDuration() const {
212 RecursiveMutexAutoLock lock(mRecursiveMutex);
213 return GetPreciseDuration();
216 void LockedForEach(nsDequeFunctor<T>& aFunctor) const {
217 RecursiveMutexAutoLock lock(mRecursiveMutex);
218 nsRefPtrDeque<T>::ForEach(aFunctor);
221 // Fill aResult with the elements which end later than the given time aTime.
222 void GetElementsAfter(const media::TimeUnit& aTime,
223 nsTArray<RefPtr<T>>* aResult) {
224 GetElementsAfterStrict(aTime.ToMicroseconds(), aResult);
227 void GetFirstElements(uint32_t aMaxElements, nsTArray<RefPtr<T>>* aResult) {
228 RecursiveMutexAutoLock lock(mRecursiveMutex);
229 for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) {
230 *aResult->AppendElement() = nsRefPtrDeque<T>::ObjectAt(i);
234 uint32_t AudioFramesCount() {
235 static_assert(std::is_same_v<T, AudioData>,
236 "Only usable with MediaQueue<AudioData>");
237 RecursiveMutexAutoLock lock(mRecursiveMutex);
238 uint32_t frames = 0;
239 for (size_t i = 0; i < GetSize(); ++i) {
240 T* v = nsRefPtrDeque<T>::ObjectAt(i);
241 frames += v->Frames();
243 return frames;
246 bool SetOffset(const media::TimeUnit& aOffset) {
247 if (!aOffset.IsValid()) {
248 QLOG("Invalid offset!");
249 return false;
251 RecursiveMutexAutoLock lock(mRecursiveMutex);
252 mOffset = aOffset;
253 QLOG("Set media queue offset %" PRId64, mOffset.ToMicroseconds());
254 return true;
257 media::TimeUnit GetOffset() const {
258 RecursiveMutexAutoLock lock(mRecursiveMutex);
259 return mOffset;
262 MediaEventSource<RefPtr<T>>& PopFrontEvent() { return mPopFrontEvent; }
264 MediaEventSource<RefPtr<T>>& PushEvent() { return mPushEvent; }
266 MediaEventSource<void>& FinishEvent() { return mFinishEvent; }
268 private:
269 // Extracts elements from the queue into aResult, in order.
270 // Elements whose end time is before or equal to aTime are ignored.
271 void GetElementsAfterStrict(int64_t aTime, nsTArray<RefPtr<T>>* aResult) {
272 RecursiveMutexAutoLock lock(mRecursiveMutex);
273 if (GetSize() == 0) return;
274 size_t i;
275 for (i = GetSize() - 1; i > 0; --i) {
276 T* v = nsRefPtrDeque<T>::ObjectAt(i);
277 if (v->GetEndTime().ToMicroseconds() < aTime) break;
279 for (; i < GetSize(); ++i) {
280 RefPtr<T> elem = nsRefPtrDeque<T>::ObjectAt(i);
281 if (elem->GetEndTime().ToMicroseconds() > aTime) {
282 aResult->AppendElement(elem);
287 mutable RecursiveMutex mRecursiveMutex MOZ_UNANNOTATED;
288 MediaEventProducer<RefPtr<T>> mPopFrontEvent;
289 MediaEventProducer<RefPtr<T>> mPushEvent;
290 MediaEventProducer<void> mFinishEvent;
291 // True when we've decoded the last frame of data in the
292 // bitstream for which we're queueing frame data.
293 bool mEndOfStream;
294 // This offset will be added to any data pushed into the queue. We use it when
295 // the media queue starts receiving looped data, which timestamp needs to be
296 // modified.
297 media::TimeUnit mOffset;
299 inline void AddDurationToPreciseDuration(T* aItem) {
300 if (!mEnablePreciseDuration) {
301 return;
303 if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
304 media::TimeUnit> ||
305 std::is_same_v<typename DurationTypeTrait<T>::type,
306 uint64_t>) {
307 mPreciseDuration += aItem->mDuration;
311 inline void SubtractDurationFromPreciseDuration(T* aItem) {
312 if (!mEnablePreciseDuration) {
313 return;
315 if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
316 media::TimeUnit> ||
317 std::is_same_v<typename DurationTypeTrait<T>::type,
318 uint64_t>) {
319 mPreciseDuration -= aItem->mDuration;
323 inline void ResetPreciseDuration() {
324 if (!mEnablePreciseDuration) {
325 return;
327 if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
328 media::TimeUnit>) {
329 mPreciseDuration = media::TimeUnit::Zero();
330 } else if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
331 uint64_t>) {
332 mPreciseDuration = 0;
336 inline int64_t GetPreciseDuration() const {
337 if (mEnablePreciseDuration) {
338 if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
339 media::TimeUnit>) {
340 return mPreciseDuration.ToMicroseconds();
341 } else if constexpr (std::is_same_v<typename DurationTypeTrait<T>::type,
342 uint64_t>) {
343 return mPreciseDuration;
346 return -1;
349 typename DurationTypeTrait<T>::type mPreciseDuration;
350 const bool mEnablePreciseDuration = false;
353 } // namespace mozilla
355 # undef QLOG
357 #endif