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__))
30 struct TimestampAdjustmentTrait
{
31 static const bool mValue
= false;
35 struct TimestampAdjustmentTrait
<AudioData
> {
36 static const bool mValue
= true;
40 struct TimestampAdjustmentTrait
<VideoData
> {
41 static const bool mValue
= true;
45 struct NonTimestampAdjustmentTrait
{
46 static const bool mValue
= !TimestampAdjustmentTrait
<T
>::mValue
;
50 class MediaQueue
: private 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();
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!");
75 QLOG("adjusted %s sample [%" PRId64
",%" PRId64
"] -> [%" 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
,
87 inline void AdjustTimeStampIfNeeded(U
* aItem
) {}
89 enum class TimestampAdjustment
{
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.
120 mEndOfStream
= false;
124 inline already_AddRefed
<T
> PopFront() {
125 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
126 RefPtr
<T
> rv
= nsRefPtrDeque
<T
>::PopFront();
128 MOZ_DIAGNOSTIC_ASSERT(rv
->GetEndTime() >= rv
->mTime
);
129 mPopFrontEvent
.Notify(RefPtr
<T
>(rv
));
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();
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
);
169 // Informs the media queue that it won't be receiving any more items.
171 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
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) {
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
);
212 for (size_t i
= 0; i
< GetSize(); ++i
) {
213 T
* v
= nsRefPtrDeque
<T
>::ObjectAt(i
);
214 frames
+= v
->Frames();
219 bool SetOffset(const media::TimeUnit
& aOffset
) {
220 if (!aOffset
.IsValid()) {
221 QLOG("Invalid offset!");
224 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
226 QLOG("Set media queue offset %" PRId64
, mOffset
.ToMicroseconds());
230 media::TimeUnit
GetOffset() const {
231 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
235 MediaEventSource
<RefPtr
<T
>>& PopFrontEvent() { return mPopFrontEvent
; }
237 MediaEventSource
<RefPtr
<T
>>& PushEvent() { return mPushEvent
; }
239 MediaEventSource
<void>& FinishEvent() { return mFinishEvent
; }
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;
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.
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
270 media::TimeUnit mOffset
;
273 } // namespace mozilla