Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qffmpegmediacapturesession.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5
6#include "private/qplatformaudioinput_p.h"
7#include "private/qplatformaudiooutput_p.h"
8#include "private/qplatformsurfacecapture_p.h"
9#include "private/qplatformaudiobufferinput_p.h"
10#include "private/qplatformvideoframeinput_p.h"
11#include "private/qplatformcamera_p.h"
12
15#include "qvideosink.h"
17#include "qaudiosink.h"
18#include "qaudiobuffer.h"
19#include "qaudiooutput.h"
20
21#include <qloggingcategory.h>
22
24
25Q_STATIC_LOGGING_CATEGORY(qLcFFmpegMediaCaptureSession, "qt.multimedia.ffmpeg.mediacapturesession")
26
28{
29 // Heuristic params to avoid jittering
30 // TODO: investigate the reason of jittering and probably reduce the factor
31 constexpr int BufferSizeFactor = 2;
32 constexpr int BufferSizeExceeding = 4096;
33
34 return input.bufferSize() * BufferSizeFactor + BufferSizeExceeding;
35}
36
38{
39 connect(this, &QFFmpegMediaCaptureSession::primaryActiveVideoSourceChanged, this,
40 &QFFmpegMediaCaptureSession::updateVideoFrameConnection);
41}
42
44
46{
47 return m_camera;
48}
49
50void QFFmpegMediaCaptureSession::setCamera(QPlatformCamera *camera)
51{
52 if (setVideoSource(m_camera, camera))
53 emit cameraChanged();
54}
55
60
61void QFFmpegMediaCaptureSession::setScreenCapture(QPlatformSurfaceCapture *screenCapture)
62{
63 if (setVideoSource(m_screenCapture, screenCapture))
64 emit screenCaptureChanged();
65}
66
71
72void QFFmpegMediaCaptureSession::setWindowCapture(QPlatformSurfaceCapture *windowCapture)
73{
74 if (setVideoSource(m_windowCapture, windowCapture))
75 emit windowCaptureChanged();
76}
77
82
83void QFFmpegMediaCaptureSession::setVideoFrameInput(QPlatformVideoFrameInput *input)
84{
85 if (setVideoSource(m_videoFrameInput, input))
86 emit videoFrameInputChanged();
87}
88
93
94void QFFmpegMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture)
95{
96 if (m_imageCapture == imageCapture)
97 return;
98
99 if (m_imageCapture)
100 m_imageCapture->setCaptureSession(nullptr);
101
102 m_imageCapture = static_cast<QFFmpegImageCapture *>(imageCapture);
103
104 if (m_imageCapture)
105 m_imageCapture->setCaptureSession(this);
106
107 emit imageCaptureChanged();
108}
109
110void QFFmpegMediaCaptureSession::setMediaRecorder(QPlatformMediaRecorder *recorder)
111{
112 auto *r = static_cast<QFFmpegMediaRecorder *>(recorder);
113 if (m_mediaRecorder == r)
114 return;
115
116 if (m_mediaRecorder)
117 m_mediaRecorder->setCaptureSession(nullptr);
118 m_mediaRecorder = r;
119 if (m_mediaRecorder)
120 m_mediaRecorder->setCaptureSession(this);
121
122 emit encoderChanged();
123}
124
126{
127 return m_mediaRecorder;
128}
129
130void QFFmpegMediaCaptureSession::setAudioInput(QPlatformAudioInput *input)
131{
132 qCDebug(qLcFFmpegMediaCaptureSession)
133 << "set audio input:" << (input ? input->device.description() : "null");
134
135 auto ffmpegAudioInput = dynamic_cast<QFFmpegAudioInput *>(input);
136 Q_ASSERT(!!input == !!ffmpegAudioInput);
137
138 if (m_audioInput == ffmpegAudioInput)
139 return;
140
141 if (m_audioInput)
142 m_audioInput->q->disconnect(this);
143
144 m_audioInput = ffmpegAudioInput;
145 if (m_audioInput)
146 // TODO: implement the signal in QPlatformAudioInput and connect to it, QTBUG-112294
147 connect(m_audioInput->q, &QAudioInput::deviceChanged, this,
148 &QFFmpegMediaCaptureSession::updateAudioSink);
149
150 updateAudioSink();
151}
152
153void QFFmpegMediaCaptureSession::setAudioBufferInput(QPlatformAudioBufferInput *input)
154{
155 // TODO: implement binding to audio sink like setAudioInput does
156 m_audioBufferInput = input;
157}
158
159void QFFmpegMediaCaptureSession::updateAudioSink()
160{
161 if (m_audioSink) {
162 m_audioSink->reset();
163 m_audioSink.reset();
164 }
165
166 if (!m_audioInput || !m_audioOutput)
167 return;
168
169 auto format = m_audioInput->device.preferredFormat();
170
171 if (!m_audioOutput->device.isFormatSupported(format))
172 qWarning() << "Audio source format" << format << "is not compatible with the audio output";
173
174 m_audioSink = std::make_unique<QAudioSink>(m_audioOutput->device, format);
175
176 m_audioBufferSize = preferredAudioSinkBufferSize(*m_audioInput);
177 m_audioSink->setBufferSize(m_audioBufferSize);
178
179 qCDebug(qLcFFmpegMediaCaptureSession)
180 << "Create audiosink, format:" << format << "bufferSize:" << m_audioSink->bufferSize()
181 << "output device:" << m_audioOutput->device.description();
182
183 m_audioIODevice = m_audioSink->start();
184 if (m_audioIODevice) {
185 auto writeToDevice = [this](const QAudioBuffer &buffer) {
186 if (m_audioBufferSize < preferredAudioSinkBufferSize(*m_audioInput)) {
187 qCDebug(qLcFFmpegMediaCaptureSession)
188 << "Recreate audiosink due to small buffer size:" << m_audioBufferSize;
189
190 updateAudioSink();
191 }
192
193 const auto written =
194 m_audioIODevice->write(buffer.data<const char>(), buffer.byteCount());
195
196 if (written < buffer.byteCount())
197 qCWarning(qLcFFmpegMediaCaptureSession)
198 << "Not all bytes written:" << written << "vs" << buffer.byteCount();
199 };
200 connect(m_audioInput, &QFFmpegAudioInput::newAudioBuffer, m_audioSink.get(), writeToDevice);
201 } else {
202 qWarning() << "Failed to start audiosink push mode";
203 }
204
205 updateVolume();
206}
207
208void QFFmpegMediaCaptureSession::updateVolume()
209{
210 if (m_audioSink)
211 m_audioSink->setVolume(m_audioOutput->muted ? 0.f : m_audioOutput->volume);
212}
213
214QPlatformAudioInput *QFFmpegMediaCaptureSession::audioInput() const
215{
216 return m_audioInput;
217}
218
219void QFFmpegMediaCaptureSession::setVideoPreview(QVideoSink *sink)
220{
221 if (std::exchange(m_videoSink, sink) == sink)
222 return;
223
224 updateVideoFrameConnection();
225}
226
227void QFFmpegMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
228{
229 qCDebug(qLcFFmpegMediaCaptureSession)
230 << "set audio output:" << (output ? output->device.description() : "null");
231
232 if (m_audioOutput == output)
233 return;
234
235 if (m_audioOutput)
236 m_audioOutput->q->disconnect(this);
237
238 m_audioOutput = output;
239 if (m_audioOutput) {
240 // TODO: implement the signals in QPlatformAudioOutput and connect to them, QTBUG-112294
241 connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this,
242 &QFFmpegMediaCaptureSession::updateAudioSink);
243 connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this,
244 &QFFmpegMediaCaptureSession::updateVolume);
245 connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this,
246 &QFFmpegMediaCaptureSession::updateVolume);
247 }
248
249 updateAudioSink();
250}
251
252void QFFmpegMediaCaptureSession::updateVideoFrameConnection()
253{
254 disconnect(m_videoFrameConnection);
255
256 if (m_primaryActiveVideoSource && m_videoSink) {
257 // deliver frames directly to video sink;
258 // AutoConnection type might be a pessimization due to an extra queuing
259 // TODO: investigate and integrate direct connection
260 m_videoFrameConnection =
261 connect(m_primaryActiveVideoSource, &QPlatformVideoSource::newVideoFrame,
262 m_videoSink, &QVideoSink::setVideoFrame);
263 }
264}
265
266void QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource()
267{
268 auto sources = activeVideoSources();
269 auto source = sources.empty() ? nullptr : sources.front();
270 if (std::exchange(m_primaryActiveVideoSource, source) != source)
271 emit primaryActiveVideoSourceChanged();
272}
273
274template<typename VideoSource>
275bool QFFmpegMediaCaptureSession::setVideoSource(QPointer<VideoSource> &source,
276 VideoSource *newSource)
277{
278 if (source == newSource)
279 return false;
280
281 if (auto prevSource = std::exchange(source, newSource)) {
282 prevSource->setCaptureSession(nullptr);
283 prevSource->disconnect(this);
284 }
285
286 if (source) {
287 source->setCaptureSession(this);
288 connect(source, &QPlatformVideoSource::activeChanged, this,
289 &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource);
290 connect(source, &QObject::destroyed, this,
291 &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource, Qt::QueuedConnection);
292 }
293
294 updatePrimaryActiveVideoSource();
295
296 return true;
297}
298
300{
301 return m_primaryActiveVideoSource;
302}
303
305{
306 std::vector<QPlatformAudioBufferInputBase *> result;
307 if (m_audioInput)
308 result.push_back(m_audioInput);
309
310 if (m_audioBufferInput)
311 result.push_back(m_audioBufferInput);
312
313 return result;
314}
315
316QT_END_NAMESPACE
317
318#include "moc_qffmpegmediacapturesession_p.cpp"
void setMediaRecorder(QPlatformMediaRecorder *recorder) override
QPlatformSurfaceCapture * windowCapture() override
void setWindowCapture(QPlatformSurfaceCapture *) override
void setVideoFrameInput(QPlatformVideoFrameInput *) override
void setScreenCapture(QPlatformSurfaceCapture *) override
QPlatformVideoFrameInput * videoFrameInput() override
void setImageCapture(QPlatformImageCapture *imageCapture) override
QPlatformSurfaceCapture * screenCapture() override
QPlatformImageCapture * imageCapture() override
QPlatformAudioInput * audioInput() const
QPlatformVideoSource * primaryActiveVideoSource()
void setCamera(QPlatformCamera *camera) override
std::vector< QPlatformAudioBufferInputBase * > activeAudioInputs() const
QPlatformMediaRecorder * mediaRecorder() override
QPlatformCamera * camera() override
~QFFmpegMediaCaptureSession() override
void setAudioBufferInput(QPlatformAudioBufferInput *input) override
void setCaptureSession(QFFmpegMediaCaptureSession *session)
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE int preferredAudioSinkBufferSize(const QFFmpegAudioInput &input)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)