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 | #ifndef QFFMPEGRENDERER_P_H |
4 | #define QFFMPEGRENDERER_P_H |
5 | |
6 | // |
7 | // W A R N I N G |
8 | // ------------- |
9 | // |
10 | // This file is not part of the Qt API. It exists purely as an |
11 | // implementation detail. This header file may change from version to |
12 | // version without notice, or even be removed. |
13 | // |
14 | // We mean it. |
15 | // |
16 | |
17 | #include "playbackengine/qffmpegplaybackengineobject_p.h" |
18 | #include "playbackengine/qffmpegtimecontroller_p.h" |
19 | #include "playbackengine/qffmpegframe_p.h" |
20 | |
21 | #include <QtCore/qpointer.h> |
22 | |
23 | #include <chrono> |
24 | |
25 | QT_BEGIN_NAMESPACE |
26 | |
27 | namespace QFFmpeg { |
28 | |
29 | class Renderer : public PlaybackEngineObject |
30 | { |
31 | Q_OBJECT |
32 | public: |
33 | using TimePoint = TimeController::TimePoint; |
34 | using Clock = TimeController::Clock; |
35 | Renderer(const TimeController &tc, const std::chrono::microseconds &seekPosTimeOffset = {}); |
36 | |
37 | void syncSoft(TimePoint tp, qint64 trackPos); |
38 | |
39 | qint64 seekPosition() const; |
40 | |
41 | qint64 lastPosition() const; |
42 | |
43 | void setPlaybackRate(float rate); |
44 | |
45 | void doForceStep(); |
46 | |
47 | bool isStepForced() const; |
48 | |
49 | public slots: |
50 | void setInitialPosition(TimePoint tp, qint64 trackPos); |
51 | |
52 | void onFinalFrameReceived(); |
53 | |
54 | void render(Frame); |
55 | |
56 | signals: |
57 | void frameProcessed(Frame); |
58 | |
59 | void synchronized(Id id, TimePoint tp, qint64 pos); |
60 | |
61 | void forceStepDone(); |
62 | |
63 | void loopChanged(Id id, qint64 offset, int index); |
64 | |
65 | protected: |
66 | bool setForceStepDone(); |
67 | |
68 | void onPauseChanged() override; |
69 | |
70 | bool canDoNextStep() const override; |
71 | |
72 | int timerInterval() const override; |
73 | |
74 | virtual void onPlaybackRateChanged() { } |
75 | |
76 | struct RenderingResult |
77 | { |
78 | bool done = true; |
79 | std::chrono::microseconds recheckInterval = std::chrono::microseconds(0); |
80 | }; |
81 | |
82 | virtual RenderingResult renderInternal(Frame frame) = 0; |
83 | |
84 | float playbackRate() const; |
85 | |
86 | std::chrono::microseconds frameDelay(const Frame &frame, |
87 | TimePoint timePoint = Clock::now()) const; |
88 | |
89 | void changeRendererTime(std::chrono::microseconds offset); |
90 | |
91 | template<typename Output, typename ChangeHandler> |
92 | void setOutputInternal(QPointer<Output> &actual, Output *desired, ChangeHandler &&changeHandler) |
93 | { |
94 | const auto connectionType = thread() == QThread::currentThread() |
95 | ? Qt::AutoConnection |
96 | : Qt::BlockingQueuedConnection; |
97 | auto doer = [desired, changeHandler, &actual]() { |
98 | const auto prev = std::exchange(actual, desired); |
99 | if (prev != desired) |
100 | changeHandler(prev); |
101 | }; |
102 | QMetaObject::invokeMethod(this, doer, connectionType); |
103 | } |
104 | |
105 | private: |
106 | void doNextStep() override; |
107 | |
108 | private: |
109 | TimeController m_timeController; |
110 | qint64 m_lastFrameEnd = 0; |
111 | QAtomicInteger<qint64> m_lastPosition = 0; |
112 | QAtomicInteger<qint64> m_seekPos = 0; |
113 | |
114 | int m_loopIndex = 0; |
115 | QQueue<Frame> m_frames; |
116 | |
117 | QAtomicInteger<bool> m_isStepForced = false; |
118 | std::optional<TimePoint> m_explicitNextFrameTime; |
119 | }; |
120 | |
121 | } // namespace QFFmpeg |
122 | |
123 | QT_END_NAMESPACE |
124 | |
125 | #endif // QFFMPEGRENDERER_P_H |
126 | |