1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "playbackengine/qffmpegtimecontroller_p.h" |
5 | |
6 | #include "qglobal.h" |
7 | #include "qdebug.h" |
8 | |
9 | #include <algorithm> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | namespace QFFmpeg { |
14 | |
15 | TimeController::TimeController() |
16 | { |
17 | sync(); |
18 | } |
19 | |
20 | TimeController::PlaybackRate TimeController::playbackRate() const |
21 | { |
22 | return m_playbackRate; |
23 | } |
24 | |
25 | void TimeController::setPlaybackRate(PlaybackRate playbackRate) |
26 | { |
27 | if (playbackRate == m_playbackRate) |
28 | return; |
29 | |
30 | Q_ASSERT(playbackRate > 0.f); |
31 | |
32 | scrollTimeTillNow(); |
33 | m_playbackRate = playbackRate; |
34 | |
35 | if (m_softSyncData) |
36 | m_softSyncData = makeSoftSyncData(srcTp: m_timePoint, srcPos: m_position, dstTp: m_softSyncData->dstTimePoint); |
37 | } |
38 | |
39 | void TimeController::sync(qint64 trackPos) |
40 | { |
41 | sync(tp: Clock::now(), pos: trackPos); |
42 | } |
43 | |
44 | void TimeController::sync(const TimePoint &tp, qint64 pos) |
45 | { |
46 | m_softSyncData.reset(); |
47 | m_position = TrackTime(pos); |
48 | m_timePoint = tp; |
49 | } |
50 | |
51 | void TimeController::syncSoft(const TimePoint &tp, qint64 pos, const Clock::duration &fixingTime) |
52 | { |
53 | const auto srcTime = Clock::now(); |
54 | const auto srcPos = positionFromTime(tp: srcTime, ignorePause: true); |
55 | const auto dstTime = srcTime + fixingTime; |
56 | |
57 | m_position = TrackTime(pos); |
58 | m_timePoint = tp; |
59 | |
60 | m_softSyncData = makeSoftSyncData(srcTp: srcTime, srcPos: TrackTime(srcPos), dstTp: dstTime); |
61 | } |
62 | |
63 | qint64 TimeController::currentPosition(const Clock::duration &offset) const |
64 | { |
65 | return positionFromTime(tp: Clock::now() + offset); |
66 | } |
67 | |
68 | void TimeController::setPaused(bool paused) |
69 | { |
70 | if (m_paused == paused) |
71 | return; |
72 | |
73 | scrollTimeTillNow(); |
74 | m_paused = paused; |
75 | } |
76 | |
77 | qint64 TimeController::positionFromTime(TimePoint tp, bool ignorePause) const |
78 | { |
79 | tp = m_paused && !ignorePause ? m_timePoint : tp; |
80 | |
81 | if (m_softSyncData && tp < m_softSyncData->dstTimePoint) { |
82 | const PlaybackRate rate = |
83 | tp > m_softSyncData->srcTimePoint ? m_softSyncData->internalRate : m_playbackRate; |
84 | |
85 | return (m_softSyncData->srcPosition |
86 | + toTrackTime(t: (tp - m_softSyncData->srcTimePoint) * rate)) |
87 | .count(); |
88 | } |
89 | |
90 | return positionFromTimeInternal(tp).count(); |
91 | } |
92 | |
93 | TimeController::TimePoint TimeController::timeFromPosition(qint64 pos, bool ignorePause) const |
94 | { |
95 | auto position = m_paused && !ignorePause ? m_position : TrackTime(pos); |
96 | |
97 | if (m_softSyncData && position < m_softSyncData->dstPosition) { |
98 | const auto rate = position > m_softSyncData->srcPosition ? m_softSyncData->internalRate |
99 | : m_playbackRate; |
100 | return m_softSyncData->srcTimePoint |
101 | + toClockTime(t: (position - m_softSyncData->srcPosition) / rate); |
102 | } |
103 | |
104 | return timeFromPositionInternal(pos: position); |
105 | } |
106 | |
107 | TimeController::SoftSyncData TimeController::makeSoftSyncData(const TimePoint &srcTp, |
108 | const TrackTime &srcPos, |
109 | const TimePoint &dstTp) const |
110 | { |
111 | SoftSyncData result; |
112 | result.srcTimePoint = srcTp; |
113 | result.srcPosition = srcPos; |
114 | result.dstTimePoint = dstTp; |
115 | result.srcPosOffest = srcPos - positionFromTimeInternal(tp: srcTp); |
116 | result.dstPosition = positionFromTimeInternal(tp: dstTp); |
117 | result.internalRate = |
118 | static_cast<PlaybackRate>(toClockTime(t: TrackTime(result.dstPosition - srcPos)).count()) |
119 | / (dstTp - srcTp).count(); |
120 | |
121 | return result; |
122 | } |
123 | |
124 | TimeController::TrackTime TimeController::positionFromTimeInternal(const TimePoint &tp) const |
125 | { |
126 | return m_position + toTrackTime(t: (tp - m_timePoint) * m_playbackRate); |
127 | } |
128 | |
129 | TimeController::TimePoint TimeController::timeFromPositionInternal(const TrackTime &pos) const |
130 | { |
131 | return m_timePoint + toClockTime(t: TrackTime(pos - m_position) / m_playbackRate); |
132 | } |
133 | |
134 | void TimeController::scrollTimeTillNow() |
135 | { |
136 | const auto now = Clock::now(); |
137 | if (!m_paused) { |
138 | m_position = positionFromTimeInternal(tp: now); |
139 | |
140 | // let's forget outdated syncronizations |
141 | if (m_softSyncData && m_softSyncData->dstTimePoint <= now) |
142 | m_softSyncData.reset(); |
143 | } else if (m_softSyncData) { |
144 | m_softSyncData->dstTimePoint += now - m_timePoint; |
145 | m_softSyncData->srcTimePoint += now - m_timePoint; |
146 | } |
147 | |
148 | m_timePoint = now; |
149 | } |
150 | |
151 | template<typename T> |
152 | TimeController::Clock::duration TimeController::toClockTime(const T &t) |
153 | { |
154 | return std::chrono::duration_cast<Clock::duration>(t); |
155 | } |
156 | |
157 | template<typename T> |
158 | TimeController::TrackTime TimeController::toTrackTime(const T &t) |
159 | { |
160 | return std::chrono::duration_cast<TrackTime>(t); |
161 | } |
162 | |
163 | } // namespace QFFmpeg |
164 | |
165 | QT_END_NAMESPACE |
166 | |