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_)
9 # include <type_traits>
11 # include "mozilla/RecursiveMutex.h"
12 # include "mozilla/TaskQueue.h"
15 # include "MediaEventSource.h"
16 # include "TimeUnits.h"
20 extern LazyLogModule gMediaDecoderLog
;
22 # define QLOG(msg, ...) \
23 MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
24 ("MediaQueue=%p " msg, this, ##__VA_ARGS__))
31 struct TimestampAdjustmentTrait
{
32 static const bool mValue
= false;
36 struct TimestampAdjustmentTrait
<AudioData
> {
37 static const bool mValue
= true;
41 struct TimestampAdjustmentTrait
<VideoData
> {
42 static const bool mValue
= true;
46 struct NonTimestampAdjustmentTrait
{
47 static const bool mValue
= !TimestampAdjustmentTrait
<T
>::mValue
;
51 struct DurationTypeTrait
{
52 using type
= media::TimeUnit
;
56 struct DurationTypeTrait
<EncodedFrame
> {
57 using type
= uint64_t;
61 class MediaQueue
: private nsRefPtrDeque
<T
> {
63 explicit MediaQueue(bool aEnablePreciseDuration
= false)
65 mRecursiveMutex("mediaqueue"),
67 mEnablePreciseDuration(aEnablePreciseDuration
) {}
69 ~MediaQueue() { Reset(); }
71 inline size_t GetSize() const {
72 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
73 return nsRefPtrDeque
<T
>::GetSize();
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!");
87 QLOG("adjusted %s sample [%" PRId64
",%" PRId64
"] -> [%" 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
,
99 inline void AdjustTimeStampIfNeeded(U
* aItem
) {}
101 enum class TimestampAdjustment
{
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.
134 mEndOfStream
= false;
138 inline already_AddRefed
<T
> PopFront() {
139 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
140 RefPtr
<T
> rv
= nsRefPtrDeque
<T
>::PopFront();
142 MOZ_DIAGNOSTIC_ASSERT(rv
->GetEndTime() >= rv
->mTime
);
143 SubtractDurationFromPreciseDuration(rv
);
144 mPopFrontEvent
.Notify(RefPtr
<T
>(rv
));
149 inline already_AddRefed
<T
> PopBack() {
150 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
151 RefPtr
<T
> rv
= nsRefPtrDeque
<T
>::Pop();
153 MOZ_DIAGNOSTIC_ASSERT(rv
->GetEndTime() >= rv
->mTime
);
154 SubtractDurationFromPreciseDuration(rv
);
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();
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
);
190 // Informs the media queue that it won't be receiving any more items.
192 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
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) {
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
);
239 for (size_t i
= 0; i
< GetSize(); ++i
) {
240 T
* v
= nsRefPtrDeque
<T
>::ObjectAt(i
);
241 frames
+= v
->Frames();
246 bool SetOffset(const media::TimeUnit
& aOffset
) {
247 if (!aOffset
.IsValid()) {
248 QLOG("Invalid offset!");
251 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
253 QLOG("Set media queue offset %" PRId64
, mOffset
.ToMicroseconds());
257 media::TimeUnit
GetOffset() const {
258 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
262 MediaEventSource
<RefPtr
<T
>>& PopFrontEvent() { return mPopFrontEvent
; }
264 MediaEventSource
<RefPtr
<T
>>& PushEvent() { return mPushEvent
; }
266 MediaEventSource
<void>& FinishEvent() { return mFinishEvent
; }
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;
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.
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
297 media::TimeUnit mOffset
;
299 inline void AddDurationToPreciseDuration(T
* aItem
) {
300 if (!mEnablePreciseDuration
) {
303 if constexpr (std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
305 std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
307 mPreciseDuration
+= aItem
->mDuration
;
311 inline void SubtractDurationFromPreciseDuration(T
* aItem
) {
312 if (!mEnablePreciseDuration
) {
315 if constexpr (std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
317 std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
319 mPreciseDuration
-= aItem
->mDuration
;
323 inline void ResetPreciseDuration() {
324 if (!mEnablePreciseDuration
) {
327 if constexpr (std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
329 mPreciseDuration
= media::TimeUnit::Zero();
330 } else if constexpr (std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
332 mPreciseDuration
= 0;
336 inline int64_t GetPreciseDuration() const {
337 if (mEnablePreciseDuration
) {
338 if constexpr (std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
340 return mPreciseDuration
.ToMicroseconds();
341 } else if constexpr (std::is_same_v
<typename DurationTypeTrait
<T
>::type
,
343 return mPreciseDuration
;
349 typename DurationTypeTrait
<T
>::type mPreciseDuration
;
350 const bool mEnablePreciseDuration
= false;
353 } // namespace mozilla