1 /* -*- Mode: C++; tab-width: 8; 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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MediaEventSource.h"
8 #include "MediaTimer.h"
9 #include "mozilla/TaskQueue.h"
12 #ifndef DOM_MEDIA_PACER_H_
13 # define DOM_MEDIA_PACER_H_
18 * Pacer<T> takes a queue of Ts tied to timestamps, and emits PacedItemEvents
19 * for every T at its corresponding timestamp.
21 * The queue is ordered. Enqueing an item at time t will drop all items at times
22 * later than T. This is because of how video sources work (some send out frames
23 * in the future, some don't), and to allow swapping one source for another.
25 * It supports a duplication interval. If there is no new item enqueued within
26 * the duplication interval since the last enqueued item, the last enqueud item
32 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Pacer
)
34 Pacer(RefPtr
<TaskQueue
> aTaskQueue
, TimeDuration aDuplicationInterval
)
35 : mTaskQueue(std::move(aTaskQueue
)),
36 mDuplicationInterval(aDuplicationInterval
),
37 mTimer(MakeAndAddRef
<MediaTimer
>()) {}
40 * Enqueues an item and schedules a timer to pass it on to PacedItemEvent() at
41 * t=aTime. Already queued items with t>=aTime will be dropped.
43 void Enqueue(T aItem
, TimeStamp aTime
) {
44 MOZ_ALWAYS_SUCCEEDS(mTaskQueue
->Dispatch(NS_NewRunnableFunction(
46 [this, self
= RefPtr
<Pacer
>(this), aItem
= std::move(aItem
), aTime
] {
47 MOZ_DIAGNOSTIC_ASSERT(!mIsShutdown
);
48 while (const auto* item
= mQueue
.Peek()) {
49 if (item
->mTime
< aTime
) {
52 RefPtr
<QueueItem
> dropping
= mQueue
.Pop();
54 mQueue
.Push(MakeAndAddRef
<QueueItem
>(std::move(aItem
), aTime
));
55 EnsureTimerScheduled(aTime
);
59 RefPtr
<GenericPromise
> Shutdown() {
61 mTaskQueue
, __func__
, [this, self
= RefPtr
<Pacer
>(this)] {
65 mCurrentTimerTarget
= Nothing();
66 return GenericPromise::CreateAndResolve(true, "Pacer::Shutdown");
70 MediaEventSourceExc
<T
, TimeStamp
>& PacedItemEvent() {
71 return mPacedItemEvent
;
77 void EnsureTimerScheduled(TimeStamp aTime
) {
78 if (mCurrentTimerTarget
&& *mCurrentTimerTarget
<= aTime
) {
82 if (mCurrentTimerTarget
) {
84 mCurrentTimerTarget
= Nothing();
87 mTimer
->WaitUntil(aTime
, __func__
)
90 [this, self
= RefPtr
<Pacer
>(this)] { OnTimerTick(); },
92 // Timer was rejected. This is fine.
94 mCurrentTimerTarget
= Some(aTime
);
98 MOZ_ASSERT(mTaskQueue
->IsOnCurrentThread());
100 mCurrentTimerTarget
= Nothing();
102 while (RefPtr
<QueueItem
> item
= mQueue
.PopFront()) {
103 auto now
= TimeStamp::Now();
105 if (item
->mTime
<= now
) {
106 // It's time to process this item.
107 if (const auto& next
= mQueue
.PeekFront();
108 !next
|| next
->mTime
> (item
->mTime
+ mDuplicationInterval
)) {
109 // No future frame within the duplication interval exists. Schedule
111 mQueue
.PushFront(MakeAndAddRef
<QueueItem
>(
112 item
->mItem
, item
->mTime
+ mDuplicationInterval
));
114 mPacedItemEvent
.Notify(std::move(item
->mItem
), item
->mTime
);
118 // This item is in the future. Put it back.
119 mQueue
.PushFront(item
.forget());
123 if (const auto& next
= mQueue
.PeekFront(); next
) {
124 // The queue is not empty. Schedule the timer.
125 EnsureTimerScheduled(next
->mTime
);
130 const RefPtr
<TaskQueue
> mTaskQueue
;
131 const TimeDuration mDuplicationInterval
;
135 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QueueItem
)
137 QueueItem(T aItem
, TimeStamp aTime
)
138 : mItem(std::forward
<T
>(aItem
)), mTime(aTime
) {}
144 ~QueueItem() = default;
147 // Accessed on mTaskQueue.
148 nsRefPtrDeque
<QueueItem
> mQueue
;
150 // Accessed on mTaskQueue.
151 RefPtr
<MediaTimer
> mTimer
;
153 // Accessed on mTaskQueue.
154 Maybe
<TimeStamp
> mCurrentTimerTarget
;
156 // Accessed on mTaskQueue.
157 bool mIsShutdown
= false;
159 MediaEventProducerExc
<T
, TimeStamp
> mPacedItemEvent
;
162 } // namespace mozilla