Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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"
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
46
48{
49 if (setVideoSource(m_camera, camera))
51}
52
57
59{
60 if (setVideoSource(m_screenCapture, screenCapture))
62}
63
68
70{
71 if (setVideoSource(m_windowCapture, windowCapture))
73}
74
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
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 auto writeToDevice = [this](const QAudioBuffer &buffer) {
166 if (m_audioBufferSize < preferredAudioSinkBufferSize(*m_audioInput)) {
167 qCDebug(qLcFFmpegMediaCaptureSession)
168 << "Recreate audiosink due to small buffer size:" << m_audioBufferSize;
169
170 updateAudioSink();
171 }
172
173 const auto written =
174 m_audioIODevice->write(buffer.data<const char>(), buffer.byteCount());
175
176 if (written < buffer.byteCount())
177 qCWarning(qLcFFmpegMediaCaptureSession)
178 << "Not all bytes written:" << written << "vs" << buffer.byteCount();
179 };
180 connect(m_audioInput, &QFFmpegAudioInput::newAudioBuffer, m_audioSink.get(), writeToDevice);
181 } else {
182 qWarning() << "Failed to start audiosink push mode";
183 }
184
185 updateVolume();
186}
187
188void QFFmpegMediaCaptureSession::updateVolume()
189{
190 if (m_audioSink)
191 m_audioSink->setVolume(m_audioOutput->muted ? 0.f : m_audioOutput->volume);
192}
193
198
200{
201 if (std::exchange(m_videoSink, sink) == sink)
202 return;
203
204 updateVideoFrameConnection();
205}
206
208{
209 qCDebug(qLcFFmpegMediaCaptureSession)
210 << "set audio output:" << (output ? output->device.description() : "null");
211
212 if (m_audioOutput == output)
213 return;
214
215 if (m_audioOutput)
216 m_audioOutput->q->disconnect(this);
217
218 m_audioOutput = output;
219 if (m_audioOutput) {
220 // TODO: implement the signals in QPlatformAudioOutput and connect to them, QTBUG-112294
221 connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this,
222 &QFFmpegMediaCaptureSession::updateAudioSink);
223 connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this,
224 &QFFmpegMediaCaptureSession::updateVolume);
225 connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this,
226 &QFFmpegMediaCaptureSession::updateVolume);
227 }
228
229 updateAudioSink();
230}
231
232void QFFmpegMediaCaptureSession::updateVideoFrameConnection()
233{
234 disconnect(m_videoFrameConnection);
235
236 if (m_primaryActiveVideoSource && m_videoSink) {
237 // deliver frames directly to video sink;
238 // AutoConnection type might be a pessimization due to an extra queuing
239 // TODO: investigate and integrate direct connection
240 m_videoFrameConnection =
241 connect(m_primaryActiveVideoSource, &QPlatformVideoSource::newVideoFrame,
242 m_videoSink, &QVideoSink::setVideoFrame);
243 }
244}
245
246void QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource()
247{
249 auto source = sources.empty() ? nullptr : sources.front();
250 if (std::exchange(m_primaryActiveVideoSource, source) != source)
252}
253
254template<typename VideoSource>
255bool QFFmpegMediaCaptureSession::setVideoSource(QPointer<VideoSource> &source,
256 VideoSource *newSource)
257{
258 if (source == newSource)
259 return false;
260
261 if (auto prevSource = std::exchange(source, newSource)) {
262 prevSource->setCaptureSession(nullptr);
263 prevSource->disconnect(this);
264 }
265
266 if (source) {
267 source->setCaptureSession(this);
269 &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource);
271 &QFFmpegMediaCaptureSession::updatePrimaryActiveVideoSource, Qt::QueuedConnection);
272 }
273
274 updatePrimaryActiveVideoSource();
275
276 return true;
277}
278
280{
281 return m_primaryActiveVideoSource;
282}
283
285
286#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(QFFmpegMediaCaptureSession *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:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
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)
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:166
#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]