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 QFFMPEGPLAYBACKENGINE_P_H |
4 | #define QFFMPEGPLAYBACKENGINE_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 | /* Playback engine design description. |
18 | * |
19 | * |
20 | * PLAYBACK ENGINE OBJECTS |
21 | * |
22 | * - Playback engine manages 7 objects inside, each one works in a separate thread. |
23 | * Each object inherits PlaybackEngineObject. The objects are: |
24 | * Demuxer |
25 | * Stream Decoders: audio, video, subtitles |
26 | * Renderers: audio, video, subtitles |
27 | * |
28 | * |
29 | * THREADS: |
30 | * |
31 | * - By default, each object works in a separate thread. It's easy to reconfigure |
32 | * to using several objects in thread. |
33 | * - New thread is allocated if a new object is created and the engine doesn't |
34 | * have free threads. If it does, the thread is to be reused. |
35 | * - If all objects for some thread are deleted, the thread becomes free and the engine |
36 | * postpones its termination. |
37 | * |
38 | * OBJECTS WEAK CONNECTIVITY |
39 | * |
40 | * - The objects know nothing about others and about PlaybackEngine. |
41 | * For any interactions the objects use slots/signals. |
42 | * |
43 | * - PlaybackEngine knows the objects object and is able to create/delete them and |
44 | * call their public methods. |
45 | * |
46 | */ |
47 | |
48 | #include <QtFFmpegMediaPluginImpl/private/qffmpegplaybackenginedefs_p.h> |
49 | #include <QtFFmpegMediaPluginImpl/private/qffmpegtimecontroller_p.h> |
50 | #include <QtFFmpegMediaPluginImpl/private/qffmpegmediadataholder_p.h> |
51 | #include <QtFFmpegMediaPluginImpl/private/qffmpegcodeccontext_p.h> |
52 | #include <QtFFmpegMediaPluginImpl/private/qffmpegplaybackutils_p.h> |
53 | #include <QtFFmpegMediaPluginImpl/private/qffmpegtime_p.h> |
54 | |
55 | #include <QtCore/qpointer.h> |
56 | |
57 | #include <unordered_map> |
58 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | class QAudioSink; |
62 | class QVideoSink; |
63 | class QAudioOutput; |
64 | class QAudioBufferOutput; |
65 | class QFFmpegMediaPlayer; |
66 | |
67 | namespace QFFmpeg |
68 | { |
69 | |
70 | class PlaybackEngine : public QObject |
71 | { |
72 | Q_OBJECT |
73 | public: |
74 | PlaybackEngine(); |
75 | |
76 | ~PlaybackEngine() override; |
77 | |
78 | void setMedia(MediaDataHolder media); |
79 | |
80 | void setVideoSink(QVideoSink *sink); |
81 | |
82 | void setAudioSink(QAudioOutput *output); |
83 | |
84 | void setAudioSink(QPlatformAudioOutput *output); |
85 | |
86 | void setAudioBufferOutput(QAudioBufferOutput *output); |
87 | |
88 | void setState(QMediaPlayer::PlaybackState state); |
89 | |
90 | void play() { |
91 | setState(QMediaPlayer::PlayingState); |
92 | } |
93 | void pause() { |
94 | setState(QMediaPlayer::PausedState); |
95 | } |
96 | void stop() { |
97 | setState(QMediaPlayer::StoppedState); |
98 | } |
99 | |
100 | void seek(TrackPosition pos); |
101 | |
102 | void setLoops(int loopsCount); |
103 | |
104 | void setPlaybackRate(float rate); |
105 | |
106 | float playbackRate() const; |
107 | |
108 | void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber); |
109 | |
110 | TrackPosition currentPosition(bool topPos = true) const; |
111 | |
112 | TrackDuration duration() const; |
113 | |
114 | bool isSeekable() const; |
115 | |
116 | const QList<MediaDataHolder::StreamInfo> & |
117 | streamInfo(QPlatformMediaPlayer::TrackType trackType) const; |
118 | |
119 | const QMediaMetaData &metaData() const; |
120 | |
121 | int activeTrack(QPlatformMediaPlayer::TrackType type) const; |
122 | |
123 | signals: |
124 | void endOfStream(); |
125 | void errorOccured(int, const QString &); |
126 | void loopChanged(); |
127 | void buffered(); |
128 | |
129 | protected: // objects managing |
130 | struct ObjectDeleter |
131 | { |
132 | void operator()(PlaybackEngineObject *) const; |
133 | |
134 | PlaybackEngine *engine = nullptr; |
135 | }; |
136 | |
137 | template<typename T> |
138 | using ObjectPtr = std::unique_ptr<T, ObjectDeleter>; |
139 | |
140 | using RendererPtr = ObjectPtr<Renderer>; |
141 | using StreamPtr = ObjectPtr<StreamDecoder>; |
142 | |
143 | template<typename T, typename... Args> |
144 | ObjectPtr<T> createPlaybackEngineObject(Args &&...args); |
145 | |
146 | virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType); |
147 | |
148 | template <typename AudioOutput> |
149 | void updateActiveAudioOutput(AudioOutput *output); |
150 | |
151 | void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput = false); |
152 | |
153 | private: |
154 | void createStreamAndRenderer(QPlatformMediaPlayer::TrackType trackType); |
155 | |
156 | void createDemuxer(); |
157 | |
158 | void registerObject(PlaybackEngineObject &object); |
159 | |
160 | template<typename C, typename Action> |
161 | void forEachExistingObject(Action &&action); |
162 | |
163 | template<typename Action> |
164 | void forEachExistingObject(Action &&action); |
165 | |
166 | void forceUpdate(); |
167 | |
168 | void recreateObjects(); |
169 | |
170 | void createObjectsIfNeeded(); |
171 | |
172 | void updateObjectsPausedState(); |
173 | |
174 | void deleteFreeThreads(); |
175 | |
176 | void onFirsPacketFound(quint64 id, TrackPosition absSeekPos); |
177 | |
178 | void onRendererSynchronized(quint64 id, RealClock::time_point timePoint, |
179 | TrackPosition trackPosition); |
180 | |
181 | void onRendererFinished(); |
182 | |
183 | void onRendererLoopChanged(quint64 id, TrackPosition offset, int loopIndex); |
184 | |
185 | void triggerStepIfNeeded(); |
186 | |
187 | static QString objectThreadName(const PlaybackEngineObject &object); |
188 | |
189 | std::optional<CodecContext> codecContextForTrack(QPlatformMediaPlayer::TrackType trackType); |
190 | |
191 | bool hasMediaStream() const; |
192 | |
193 | void finilizeTime(TrackPosition pos); |
194 | |
195 | void finalizeOutputs(); |
196 | |
197 | bool hasRenderer(quint64 id) const; |
198 | |
199 | void updateVideoSinkSize(QVideoSink *prevSink = nullptr); |
200 | |
201 | TrackPosition boundPosition(TrackPosition position) const; |
202 | |
203 | private: |
204 | MediaDataHolder m_media; |
205 | |
206 | TimeController m_timeController; |
207 | |
208 | std::unordered_map<QString, std::unique_ptr<QThread>> m_threads; |
209 | bool m_threadsDirty = false; |
210 | |
211 | QPointer<QVideoSink> m_videoSink; |
212 | QPointer<QAudioOutput> m_audioOutput; |
213 | QPointer<QAudioBufferOutput> m_audioBufferOutput; |
214 | |
215 | QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState; |
216 | |
217 | ObjectPtr<Demuxer> m_demuxer; |
218 | std::array<StreamPtr, QPlatformMediaPlayer::NTrackTypes> m_streams; |
219 | std::array<RendererPtr, QPlatformMediaPlayer::NTrackTypes> m_renderers; |
220 | |
221 | bool m_shouldUpdateTimeOnFirstPacket = false; |
222 | bool m_seekPending = false; |
223 | |
224 | std::array<std::optional<CodecContext>, QPlatformMediaPlayer::NTrackTypes> m_codecContexts; |
225 | int m_loops = QMediaPlayer::Once; |
226 | LoopOffset m_currentLoopOffset; |
227 | }; |
228 | |
229 | template<typename T, typename... Args> |
230 | PlaybackEngine::ObjectPtr<T> PlaybackEngine::createPlaybackEngineObject(Args &&...args) |
231 | { |
232 | auto result = ObjectPtr<T>(new T(std::forward<Args>(args)...), { this }); |
233 | registerObject(object&: *result); |
234 | return result; |
235 | } |
236 | } // namespace QFFmpeg |
237 | |
238 | QT_END_NAMESPACE |
239 | |
240 | #endif // QFFMPEGPLAYBACKENGINE_P_H |
241 | |