Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
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"
11#include "private/qplatformcamera_p.h"
12#include "qvideosink.h"
13#include "qffmpegaudioinput_p.h"
14#include "qaudiosink.h"
15#include "qaudiobuffer.h"
16#include "qaudiooutput.h"
17
18#include <qloggingcategory.h>
19
21
22static Q_LOGGING_CATEGORY(qLcFFmpegMediaCaptureSession, "qt.multimedia.ffmpeg.mediacapturesession")
23
24static int preferredAudioSinkBufferSize(const QFFmpegAudioInput &input)
25{
26 // Heuristic params to avoid jittering
27 // TODO: investigate the reason of jittering and probably reduce the factor
28 constexpr int BufferSizeFactor = 2;
29 constexpr int BufferSizeExceeding = 4096;
30
31 return input.bufferSize() * BufferSizeFactor + BufferSizeExceeding;
32}
33
35{
37 &QFFmpegMediaCaptureSession::updateVideoFrameConnection);
38}
39
41
43{
44 return m_camera;
45}
46
48{
49 if (setVideoSource(m_camera, camera))
51}
52
54{
55 return m_screenCapture;
56}
57
59{
60 if (setVideoSource(m_screenCapture, screenCapture))
62}
63
65{
66 return m_windowCapture;
67}
68
70{
71 if (setVideoSource(m_windowCapture, windowCapture))
73}
74
76{
77 return m_imageCapture;
78}
79
81{
82 if (m_imageCapture == imageCapture)
83 return;
84
85 if (m_imageCapture)
86 m_imageCapture->setCaptureSession(nullptr);
87
88 m_imageCapture = static_cast<QFFmpegImageCapture *>(imageCapture);
89
90 if (m_imageCapture)
91 m_imageCapture->setCaptureSession(this);
92
94}
95
97{
98 auto *r = static_cast<QFFmpegMediaRecorder *>(recorder);
99 if (m_mediaRecorder == r)
100 return;
101
102 if (m_mediaRecorder)
103 m_mediaRecorder->setCaptureSession(nullptr);
104 m_mediaRecorder = r;
105 if (m_mediaRecorder)
106 m_mediaRecorder->setCaptureSession(this);
107
109}
110
112{
113 return m_mediaRecorder;
114}
115
117{
118 qCDebug(qLcFFmpegMediaCaptureSession)
119 << "set audio input:" << (input ? input->device.description() : "null");
120
121 auto ffmpegAudioInput = dynamic_cast<QFFmpegAudioInput *>(input);
122 Q_ASSERT(!!input == !!ffmpegAudioInput);
123
124 if (m_audioInput == ffmpegAudioInput)
125 return;
126
127 if (m_audioInput)
128 m_audioInput->q->disconnect(this);
129
130 m_audioInput = ffmpegAudioInput;
131 if (m_audioInput)
132 // TODO: implement the signal in QPlatformAudioInput and connect to it, QTBUG-112294
133 connect(m_audioInput->q, &QAudioInput::deviceChanged, this,
134 &QFFmpegMediaCaptureSession::updateAudioSink);
135
136 updateAudioSink();
137}
138
139void QFFmpegMediaCaptureSession::updateAudioSink()
140{
141 if (m_audioSink) {
142 m_audioSink->reset();
143 m_audioSink.reset();
144 }
145
146 if (!m_audioInput || !m_audioOutput)
147 return;
148
149 auto format = m_audioInput->device.preferredFormat();
150
151 if (!m_audioOutput->device.isFormatSupported(format))
152 qWarning() << "Audio source format" << format << "is not compatible with the audio output";
153
154 m_audioSink = std::make_unique<QAudioSink>(m_audioOutput->device, format);
155
156 m_audioBufferSize = preferredAudioSinkBufferSize(*m_audioInput);
157 m_audioSink->setBufferSize(m_audioBufferSize);
158
159 qCDebug(qLcFFmpegMediaCaptureSession)
160 << "Create audiosink, format:" << format << "bufferSize:" << m_audioSink->bufferSize()
161 << "output device:" << m_audioOutput->device.description();
162
163 m_audioIODevice = m_audioSink->start();
164 if (m_audioIODevice) {
165 connect(m_audioInput, &QFFmpegAudioInput::newAudioBuffer, m_audioSink.get(),
166 [=](const QAudioBuffer &buffer) {
167 if (m_audioBufferSize < preferredAudioSinkBufferSize(*m_audioInput)) {
168 qCDebug(qLcFFmpegMediaCaptureSession)
169 << "Recreate audiosink due to small buffer size:"
170 << m_audioBufferSize;
171
172 updateAudioSink();
173 }
174
175 const auto written =
176 m_audioIODevice->write(buffer.data<const char>(), buffer.byteCount());
177
178 if (written < buffer.byteCount())
179 qCWarning(qLcFFmpegMediaCaptureSession)
180 << "Not all bytes written:" << written << "vs"
181 << buffer.byteCount();
182 });
183 } else {
184 qWarning() << "Failed to start audiosink push mode";
185 }
186
187 updateVolume();
188}
189
190void QFFmpegMediaCaptureSession::updateVolume()
191{
192 if (m_audioSink)
193 m_audioSink->setVolume(m_audioOutput->muted ? 0.f : m_audioOutput->volume);
194}
195
197{
198 return m_audioInput;
199}
200
202{
203 if (std::exchange(m_videoSink, sink) == sink)
204 return;
205
206 updateVideoFrameConnection();
207}
208
210{
211 qCDebug(qLcFFmpegMediaCaptureSession)
212 << "set audio output:" << (output ? output->device.description() : "null");
213
214 if (m_audioOutput == output)
215 return;
216
217 if (m_audioOutput)
218 m_audioOutput->q->disconnect(this);
219
220 m_audioOutput = output;
221 if (m_audioOutput) {
222 // TODO: implement the signals in QPlatformAudioOutput and connect to them, QTBUG-112294
223 connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this,
224 &QFFmpegMediaCaptureSession::updateAudioSink);
225 connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this,
226 &QFFmpegMediaCaptureSession::updateVolume);
227 connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this,
228 &QFFmpegMediaCaptureSession::updateVolume);
229 }
230
231 updateAudioSink();
232}
233
234void QFFmpegMediaCaptureSession::updateVideoFrameConnection()
235{
236 disconnect(m_videoFrameConnection);
237
238 if (m_primaryActiveVideoSource && m_videoSink) {
239 // deliver frames directly to video sink;
240 // AutoConnection type might be a pessimization due to an extra queuing
241 // TODO: investigate and integrate direct connection
242 m_videoFrameConnection =
243 connect(m_primaryActiveVideoSource, &QPlatformVideoSource::newVideoFrame,
244 m_videoSink, &QVideoSink::setVideoFrame);
245 }
246}
247
248void QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource()
249{
251 auto source = sources.empty() ? nullptr : sources.front();
252 if (std::exchange(m_primaryActiveVideoSource, source) != source)
254}
255
256template<typename VideoSource>
257bool QFFmpegMediaCaptureSession::setVideoSource(QPointer<VideoSource> &source,
258 VideoSource *newSource)
259{
260 if (source == newSource)
261 return false;
262
263 if (auto prevSource = std::exchange(source, newSource)) {
264 prevSource->setCaptureSession(nullptr);
265 prevSource->disconnect(this);
266 }
267
268 if (source) {
269 source->setCaptureSession(this);
271 &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource);
273 &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource, Qt::QueuedConnection);
274 }
275
276 updatePrimaryActiveVideoSource();
277
278 return true;
279}
280
282{
283 return m_primaryActiveVideoSource;
284}
285
287
288#include "moc_qffmpegmediacapturesession_p.cpp"
\inmodule QtMultimedia
bool isFormatSupported(const QAudioFormat &format) const
Returns true if the supplied settings are supported by the audio device described by this QAudioDevic...
QString description
\qmlproperty string QtMultimedia::audioDevice::description
QAudioFormat preferredFormat() const
Returns the default audio format settings for this device.
void deviceChanged()
void deviceChanged()
void mutedChanged(bool muted)
void volumeChanged(float volume)
void newAudioBuffer(const QAudioBuffer &buffer)
void setCaptureSession(QPlatformMediaCaptureSession *session)
void setMediaRecorder(QPlatformMediaRecorder *recorder) override
void setVideoPreview(QVideoSink *sink) override
QPlatformSurfaceCapture * windowCapture() override
void setWindowCapture(QPlatformSurfaceCapture *) override
void setScreenCapture(QPlatformSurfaceCapture *) override
void setAudioInput(QPlatformAudioInput *input) override
void setImageCapture(QPlatformImageCapture *imageCapture) override
QPlatformSurfaceCapture * screenCapture() override
QPlatformImageCapture * imageCapture() override
QPlatformVideoSource * primaryActiveVideoSource()
void setCamera(QPlatformCamera *camera) override
QPlatformMediaRecorder * mediaRecorder() override
QPlatformCamera * camera() override
~QFFmpegMediaCaptureSession() override
void setAudioOutput(QPlatformAudioOutput *output) override
void setCaptureSession(QPlatformMediaCaptureSession *session)
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3099
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
std::vector< QPlatformVideoSource * > activeVideoSources()
void newVideoFrame(const QVideoFrame &)
void activeChanged(bool)
\inmodule QtCore
Definition qpointer.h:18
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
void setVideoFrame(const QVideoFrame &frame)
Sets the current video frame.
QMediaRecorder * recorder
Definition camera.cpp:20
QCamera * camera
Definition camera.cpp:19
QImageCapture * imageCapture
Definition camera.cpp:21
Combined button and popup list for selecting options.
@ QueuedConnection
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLboolean r
[2]
GLenum GLuint buffer
GLsizei GLenum * sources
GLint GLsizei GLsizei GLenum format
GLsizei GLsizei GLchar * source
GLsizei GLenum GLboolean sink
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
QT_BEGIN_NAMESPACE typedef uchar * output
QObject::connect nullptr
myObject disconnect()
[26]