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 "playbackengine/qffmpegplaybackenginedefs_p.h" |
49 | #include "playbackengine/qffmpegtimecontroller_p.h" |
50 | #include "playbackengine/qffmpegmediadataholder_p.h" |
51 | #include "playbackengine/qffmpegcodec_p.h" |
52 | #include "playbackengine/qffmpegpositionwithoffset_p.h" |
53 | |
54 | #include <QtCore/qpointer.h> |
55 | |
56 | #include <unordered_map> |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | class QAudioSink; |
61 | class QVideoSink; |
62 | class QAudioOutput; |
63 | class QAudioBufferOutput; |
64 | class QFFmpegMediaPlayer; |
65 | |
66 | namespace QFFmpeg |
67 | { |
68 | |
69 | class PlaybackEngine : public QObject |
70 | { |
71 | Q_OBJECT |
72 | public: |
73 | PlaybackEngine(); |
74 | |
75 | ~PlaybackEngine() override; |
76 | |
77 | void setMedia(MediaDataHolder media); |
78 | |
79 | void setVideoSink(QVideoSink *sink); |
80 | |
81 | void setAudioSink(QAudioOutput *output); |
82 | |
83 | void setAudioSink(QPlatformAudioOutput *output); |
84 | |
85 | void setAudioBufferOutput(QAudioBufferOutput *output); |
86 | |
87 | void setState(QMediaPlayer::PlaybackState state); |
88 | |
89 | void play() { |
90 | setState(QMediaPlayer::PlayingState); |
91 | } |
92 | void pause() { |
93 | setState(QMediaPlayer::PausedState); |
94 | } |
95 | void stop() { |
96 | setState(QMediaPlayer::StoppedState); |
97 | } |
98 | |
99 | void seek(qint64 pos); |
100 | |
101 | void setLoops(int loopsCount); |
102 | |
103 | void setPlaybackRate(float rate); |
104 | |
105 | float playbackRate() const; |
106 | |
107 | void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber); |
108 | |
109 | qint64 currentPosition(bool topPos = true) const; |
110 | |
111 | qint64 duration() const; |
112 | |
113 | bool isSeekable() const; |
114 | |
115 | const QList<MediaDataHolder::StreamInfo> & |
116 | streamInfo(QPlatformMediaPlayer::TrackType trackType) const; |
117 | |
118 | const QMediaMetaData &metaData() const; |
119 | |
120 | int activeTrack(QPlatformMediaPlayer::TrackType type) const; |
121 | |
122 | signals: |
123 | void endOfStream(); |
124 | void errorOccured(int, const QString &); |
125 | void loopChanged(); |
126 | void buffered(); |
127 | |
128 | protected: // objects managing |
129 | struct ObjectDeleter |
130 | { |
131 | void operator()(PlaybackEngineObject *) const; |
132 | |
133 | PlaybackEngine *engine = nullptr; |
134 | }; |
135 | |
136 | template<typename T> |
137 | using ObjectPtr = std::unique_ptr<T, ObjectDeleter>; |
138 | |
139 | using RendererPtr = ObjectPtr<Renderer>; |
140 | using StreamPtr = ObjectPtr<StreamDecoder>; |
141 | |
142 | template<typename T, typename... Args> |
143 | ObjectPtr<T> createPlaybackEngineObject(Args &&...args); |
144 | |
145 | virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType); |
146 | |
147 | template <typename AudioOutput> |
148 | void updateActiveAudioOutput(AudioOutput *output); |
149 | |
150 | void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput = false); |
151 | |
152 | private: |
153 | void createStreamAndRenderer(QPlatformMediaPlayer::TrackType trackType); |
154 | |
155 | void createDemuxer(); |
156 | |
157 | void registerObject(PlaybackEngineObject &object); |
158 | |
159 | template<typename C, typename Action> |
160 | void forEachExistingObject(Action &&action); |
161 | |
162 | template<typename Action> |
163 | void forEachExistingObject(Action &&action); |
164 | |
165 | void forceUpdate(); |
166 | |
167 | void recreateObjects(); |
168 | |
169 | void createObjectsIfNeeded(); |
170 | |
171 | void updateObjectsPausedState(); |
172 | |
173 | void deleteFreeThreads(); |
174 | |
175 | void onRendererSynchronized(quint64 id, std::chrono::steady_clock::time_point time, |
176 | qint64 trackTime); |
177 | |
178 | void onRendererFinished(); |
179 | |
180 | void onRendererLoopChanged(quint64 id, qint64 offset, int loopIndex); |
181 | |
182 | void triggerStepIfNeeded(); |
183 | |
184 | static QString objectThreadName(const PlaybackEngineObject &object); |
185 | |
186 | std::optional<Codec> codecForTrack(QPlatformMediaPlayer::TrackType trackType); |
187 | |
188 | bool hasMediaStream() const; |
189 | |
190 | void finilizeTime(qint64 pos); |
191 | |
192 | void finalizeOutputs(); |
193 | |
194 | bool hasRenderer(quint64 id) const; |
195 | |
196 | void updateVideoSinkSize(QVideoSink *prevSink = nullptr); |
197 | |
198 | qint64 boundPosition(qint64 position) const; |
199 | |
200 | private: |
201 | MediaDataHolder m_media; |
202 | |
203 | TimeController m_timeController; |
204 | |
205 | std::unordered_map<QString, std::unique_ptr<QThread>> m_threads; |
206 | bool m_threadsDirty = false; |
207 | |
208 | QPointer<QVideoSink> m_videoSink; |
209 | QPointer<QAudioOutput> m_audioOutput; |
210 | QPointer<QAudioBufferOutput> m_audioBufferOutput; |
211 | |
212 | QMediaPlayer::PlaybackState m_state = QMediaPlayer::StoppedState; |
213 | |
214 | ObjectPtr<Demuxer> m_demuxer; |
215 | std::array<StreamPtr, QPlatformMediaPlayer::NTrackTypes> m_streams; |
216 | std::array<RendererPtr, QPlatformMediaPlayer::NTrackTypes> m_renderers; |
217 | |
218 | std::array<std::optional<Codec>, QPlatformMediaPlayer::NTrackTypes> m_codecs; |
219 | int m_loops = QMediaPlayer::Once; |
220 | LoopOffset m_currentLoopOffset; |
221 | }; |
222 | |
223 | template<typename T, typename... Args> |
224 | PlaybackEngine::ObjectPtr<T> PlaybackEngine::createPlaybackEngineObject(Args &&...args) |
225 | { |
226 | auto result = ObjectPtr<T>(new T(std::forward<Args>(args)...), { this }); |
227 | registerObject(object&: *result); |
228 | return result; |
229 | } |
230 | } |
231 | |
232 | QT_END_NAMESPACE |
233 | |
234 | #endif // QFFMPEGPLAYBACKENGINE_P_H |
235 | |