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 inline void PushFront(T
* aItem
) {
90 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
91 AdjustTimeStampIfNeeded(aItem
);
92 nsRefPtrDeque
<T
>::PushFront(aItem
);
95 inline void Push(T
* aItem
) {
96 MOZ_DIAGNOSTIC_ASSERT(aItem
);
97 Push(do_AddRef(aItem
));
100 inline void Push(already_AddRefed
<T
> aItem
) {
101 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
102 T
* item
= aItem
.take();
104 MOZ_DIAGNOSTIC_ASSERT(item
);
105 MOZ_DIAGNOSTIC_ASSERT(item
->GetEndTime() >= item
->mTime
);
106 AdjustTimeStampIfNeeded(item
);
107 nsRefPtrDeque
<T
>::Push(dont_AddRef(item
));
108 mPushEvent
.Notify(RefPtr
<T
>(item
));
110 // Pushing new data after queue has ended means that the stream is active
111 // again, so we should not mark it as ended.
113 mEndOfStream
= false;
117 inline already_AddRefed
<T
> PopFront() {
118 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
119 RefPtr
<T
> rv
= nsRefPtrDeque
<T
>::PopFront();
121 MOZ_DIAGNOSTIC_ASSERT(rv
->GetEndTime() >= rv
->mTime
);
122 mPopFrontEvent
.Notify(RefPtr
<T
>(rv
));
127 inline already_AddRefed
<T
> PopBack() {
128 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
129 return nsRefPtrDeque
<T
>::Pop();
132 inline RefPtr
<T
> PeekFront() const {
133 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
134 return nsRefPtrDeque
<T
>::PeekFront();
137 inline RefPtr
<T
> PeekBack() const {
138 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
139 return nsRefPtrDeque
<T
>::Peek();
143 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
144 nsRefPtrDeque
<T
>::Erase();
145 SetOffset(media::TimeUnit::Zero());
146 mEndOfStream
= false;
149 bool AtEndOfStream() const {
150 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
151 return GetSize() == 0 && mEndOfStream
;
154 // Returns true if the media queue has had its last item added to it.
155 // This happens when the media stream has been completely decoded. Note this
156 // does not mean that the corresponding stream has finished playback.
157 bool IsFinished() const {
158 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
162 // Informs the media queue that it won't be receiving any more items.
164 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
167 mFinishEvent
.Notify();
171 // Returns the approximate number of microseconds of items in the queue.
172 int64_t Duration() const {
173 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
174 if (GetSize() == 0) {
177 T
* last
= nsRefPtrDeque
<T
>::Peek();
178 T
* first
= nsRefPtrDeque
<T
>::PeekFront();
179 return (last
->GetEndTime() - first
->mTime
).ToMicroseconds();
182 void LockedForEach(nsDequeFunctor
<T
>& aFunctor
) const {
183 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
184 nsRefPtrDeque
<T
>::ForEach(aFunctor
);
187 // Fill aResult with the elements which end later than the given time aTime.
188 void GetElementsAfter(const media::TimeUnit
& aTime
,
189 nsTArray
<RefPtr
<T
>>* aResult
) {
190 GetElementsAfterStrict(aTime
.ToMicroseconds(), aResult
);
193 void GetFirstElements(uint32_t aMaxElements
, nsTArray
<RefPtr
<T
>>* aResult
) {
194 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
195 for (size_t i
= 0; i
< aMaxElements
&& i
< GetSize(); ++i
) {
196 *aResult
->AppendElement() = nsRefPtrDeque
<T
>::ObjectAt(i
);
200 uint32_t AudioFramesCount() {
201 static_assert(std::is_same_v
<T
, AudioData
>,
202 "Only usable with MediaQueue<AudioData>");
203 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
205 for (size_t i
= 0; i
< GetSize(); ++i
) {
206 T
* v
= nsRefPtrDeque
<T
>::ObjectAt(i
);
207 frames
+= v
->Frames();
212 bool SetOffset(const media::TimeUnit
& aOffset
) {
213 if (!aOffset
.IsValid()) {
214 QLOG("Invalid offset!");
217 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
219 QLOG("Set media queue offset %" PRId64
, mOffset
.ToMicroseconds());
223 media::TimeUnit
GetOffset() const {
224 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
228 MediaEventSource
<RefPtr
<T
>>& PopFrontEvent() { return mPopFrontEvent
; }
230 MediaEventSource
<RefPtr
<T
>>& PushEvent() { return mPushEvent
; }
232 MediaEventSource
<void>& FinishEvent() { return mFinishEvent
; }
235 // Extracts elements from the queue into aResult, in order.
236 // Elements whose end time is before or equal to aTime are ignored.
237 void GetElementsAfterStrict(int64_t aTime
, nsTArray
<RefPtr
<T
>>* aResult
) {
238 RecursiveMutexAutoLock
lock(mRecursiveMutex
);
239 if (GetSize() == 0) return;
241 for (i
= GetSize() - 1; i
> 0; --i
) {
242 T
* v
= nsRefPtrDeque
<T
>::ObjectAt(i
);
243 if (v
->GetEndTime().ToMicroseconds() < aTime
) break;
245 for (; i
< GetSize(); ++i
) {
246 RefPtr
<T
> elem
= nsRefPtrDeque
<T
>::ObjectAt(i
);
247 if (elem
->GetEndTime().ToMicroseconds() > aTime
) {
248 aResult
->AppendElement(elem
);
253 mutable RecursiveMutex mRecursiveMutex MOZ_UNANNOTATED
;
254 MediaEventProducer
<RefPtr
<T
>> mPopFrontEvent
;
255 MediaEventProducer
<RefPtr
<T
>> mPushEvent
;
256 MediaEventProducer
<void> mFinishEvent
;
257 // True when we've decoded the last frame of data in the
258 // bitstream for which we're queueing frame data.
260 // This offset will be added to any data pushed into the queue. We use it when
261 // the media queue starts receiving looped data, which timestamp needs to be
263 media::TimeUnit mOffset
;
266 } // namespace mozilla