Backed out 15 changesets (bug 1852806) for causing mda failures on test_video_low_pow...
[gecko.git] / dom / media / MediaQueue.h
blob46094933398318a3f252f897776b1bee4b10f58a
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;
29 template <typename T>
30 struct TimestampAdjustmentTrait {
31 static const bool mValue = false;
34 template <>
35 struct TimestampAdjustmentTrait<AudioData> {
36 static const bool mValue = true;
39 template <>
40 struct TimestampAdjustmentTrait<VideoData> {
41 static const bool mValue = true;
44 template <typename T>
45 struct NonTimestampAdjustmentTrait {
46 static const bool mValue = !TimestampAdjustmentTrait<T>::mValue;
49 template <class T>
50 class MediaQueue : private nsRefPtrDeque<T> {
51 public:
52 MediaQueue()
53 : nsRefPtrDeque<T>(),
54 mRecursiveMutex("mediaqueue"),
55 mEndOfStream(false) {}
57 ~MediaQueue() { Reset(); }
59 inline size_t GetSize() const {
60 RecursiveMutexAutoLock lock(mRecursiveMutex);
61 return nsRefPtrDeque<T>::GetSize();
64 template <typename U,
65 std::enable_if_t<TimestampAdjustmentTrait<U>::mValue, bool> = true>
66 inline void AdjustTimeStampIfNeeded(U* aItem) {
67 static_assert(std::is_same_v<U, AudioData> || std::is_same_v<U, VideoData>);
68 if (mOffset != media::TimeUnit::Zero()) {
69 const auto prev = aItem->mTime, prevEndTime = aItem->GetEndTime();
70 aItem->mTime += mOffset;
71 if (!aItem->mTime.IsValid()) {
72 NS_WARNING("Reverting timestamp adjustment due to sample overflow!");
73 aItem->mTime = prev;
74 } else {
75 QLOG("adjusted %s sample [%" PRId64 ",%" PRId64 "] -> [%" PRId64
76 ",%" PRId64 "]",
77 std::is_same_v<U, AudioData> ? "audio" : "video",
78 prev.ToMicroseconds(), prevEndTime.ToMicroseconds(),
79 aItem->mTime.ToMicroseconds(),
80 aItem->GetEndTime().ToMicroseconds());
85 template <typename U, std::enable_if_t<NonTimestampAdjustmentTrait<U>::mValue,
86 bool> = true>
87 inline void AdjustTimeStampIfNeeded(U* aItem) {}
89 enum class TimestampAdjustment {
90 Enable,
91 Disable,
93 inline void PushFront(
94 T* aItem, TimestampAdjustment aIsEnabled = TimestampAdjustment::Enable) {
95 RecursiveMutexAutoLock lock(mRecursiveMutex);
96 if (aIsEnabled == TimestampAdjustment::Enable) {
97 AdjustTimeStampIfNeeded(aItem);
99 nsRefPtrDeque<T>::PushFront(aItem);
102 inline void Push(T* aItem) {
103 MOZ_DIAGNOSTIC_ASSERT(aItem);
104 Push(do_AddRef(aItem));
107 inline void Push(already_AddRefed<T> aItem) {
108 RecursiveMutexAutoLock lock(mRecursiveMutex);
109 T* item = aItem.take();
111 MOZ_DIAGNOSTIC_ASSERT(item);
112 MOZ_DIAGNOSTIC_ASSERT(item->GetEndTime() >= item->mTime);
113 AdjustTimeStampIfNeeded(item);
114 nsRefPtrDeque<T>::Push(dont_AddRef(item));
115 mPushEvent.Notify(RefPtr<T>(item));
117 // Pushing new data after queue has ended means that the stream is active
118 // again, so we should not mark it as ended.
119 if (mEndOfStream) {
120 mEndOfStream = false;
124 inline already_AddRefed<T> PopFront() {
125 RecursiveMutexAutoLock lock(mRecursiveMutex);
126 RefPtr<T> rv = nsRefPtrDeque<T>::PopFront();
127 if (rv) {
128 MOZ_DIAGNOSTIC_ASSERT(rv->GetEndTime() >= rv->mTime);
129 mPopFrontEvent.Notify(RefPtr<T>(rv));
131 return rv.forget();
134 inline already_AddRefed<T> PopBack() {
135 RecursiveMutexAutoLock lock(mRecursiveMutex);
136 return nsRefPtrDeque<T>::Pop();
139 inline RefPtr<T> PeekFront() const {
140 RecursiveMutexAutoLock lock(mRecursiveMutex);
141 return nsRefPtrDeque<T>::PeekFront();
144 inline RefPtr<T> PeekBack() const {
145 RecursiveMutexAutoLock lock(mRecursiveMutex);
146 return nsRefPtrDeque<T>::Peek();
149 void Reset() {
150 RecursiveMutexAutoLock lock(mRecursiveMutex);
151 nsRefPtrDeque<T>::Erase();
152 SetOffset(media::TimeUnit::Zero());
153 mEndOfStream = false;
156 bool AtEndOfStream() const {
157 RecursiveMutexAutoLock lock(mRecursiveMutex);
158 return GetSize() == 0 && mEndOfStream;
161 // Returns true if the media queue has had its last item added to it.
162 // This happens when the media stream has been completely decoded. Note this
163 // does not mean that the corresponding stream has finished playback.
164 bool IsFinished() const {
165 RecursiveMutexAutoLock lock(mRecursiveMutex);
166 return mEndOfStream;
169 // Informs the media queue that it won't be receiving any more items.
170 void Finish() {
171 RecursiveMutexAutoLock lock(mRecursiveMutex);
172 if (!mEndOfStream) {
173 mEndOfStream = true;
174 mFinishEvent.Notify();
178 // Returns the approximate number of microseconds of items in the queue.
179 int64_t Duration() const {
180 RecursiveMutexAutoLock lock(mRecursiveMutex);
181 if (GetSize() == 0) {
182 return 0;
184 T* last = nsRefPtrDeque<T>::Peek();
185 T* first = nsRefPtrDeque<T>::PeekFront();
186 return (last->GetEndTime() - first->mTime).ToMicroseconds();
189 void LockedForEach(nsDequeFunctor<T>& aFunctor) const {
190 RecursiveMutexAutoLock lock(mRecursiveMutex);
191 nsRefPtrDeque<T>::ForEach(aFunctor);
194 // Fill aResult with the elements which end later than the given time aTime.
195 void GetElementsAfter(const media::TimeUnit& aTime,
196 nsTArray<RefPtr<T>>* aResult) {
197 GetElementsAfterStrict(aTime.ToMicroseconds(), aResult);
200 void GetFirstElements(uint32_t aMaxElements, nsTArray<RefPtr<T>>* aResult) {
201 RecursiveMutexAutoLock lock(mRecursiveMutex);
202 for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) {
203 *aResult->AppendElement() = nsRefPtrDeque<T>::ObjectAt(i);
207 uint32_t AudioFramesCount() {
208 static_assert(std::is_same_v<T, AudioData>,
209 "Only usable with MediaQueue<AudioData>");
210 RecursiveMutexAutoLock lock(mRecursiveMutex);
211 uint32_t frames = 0;
212 for (size_t i = 0; i < GetSize(); ++i) {
213 T* v = nsRefPtrDeque<T>::ObjectAt(i);
214 frames += v->Frames();
216 return frames;
219 bool SetOffset(const media::TimeUnit& aOffset) {
220 if (!aOffset.IsValid()) {
221 QLOG("Invalid offset!");
222 return false;
224 RecursiveMutexAutoLock lock(mRecursiveMutex);
225 mOffset = aOffset;
226 QLOG("Set media queue offset %" PRId64, mOffset.ToMicroseconds());
227 return true;
230 media::TimeUnit GetOffset() const {
231 RecursiveMutexAutoLock lock(mRecursiveMutex);
232 return mOffset;
235 MediaEventSource<RefPtr<T>>& PopFrontEvent() { return mPopFrontEvent; }
237 MediaEventSource<RefPtr<T>>& PushEvent() { return mPushEvent; }
239 MediaEventSource<void>& FinishEvent() { return mFinishEvent; }
241 private:
242 // Extracts elements from the queue into aResult, in order.
243 // Elements whose end time is before or equal to aTime are ignored.
244 void GetElementsAfterStrict(int64_t aTime, nsTArray<RefPtr<T>>* aResult) {
245 RecursiveMutexAutoLock lock(mRecursiveMutex);
246 if (GetSize() == 0) return;
247 size_t i;
248 for (i = GetSize() - 1; i > 0; --i) {
249 T* v = nsRefPtrDeque<T>::ObjectAt(i);
250 if (v->GetEndTime().ToMicroseconds() < aTime) break;
252 for (; i < GetSize(); ++i) {
253 RefPtr<T> elem = nsRefPtrDeque<T>::ObjectAt(i);
254 if (elem->GetEndTime().ToMicroseconds() > aTime) {
255 aResult->AppendElement(elem);
260 mutable RecursiveMutex mRecursiveMutex MOZ_UNANNOTATED;
261 MediaEventProducer<RefPtr<T>> mPopFrontEvent;
262 MediaEventProducer<RefPtr<T>> mPushEvent;
263 MediaEventProducer<void> mFinishEvent;
264 // True when we've decoded the last frame of data in the
265 // bitstream for which we're queueing frame data.
266 bool mEndOfStream;
267 // This offset will be added to any data pushed into the queue. We use it when
268 // the media queue starts receiving looped data, which timestamp needs to be
269 // modified.
270 media::TimeUnit mOffset;
273 } // namespace mozilla
275 # undef QLOG
277 #endif