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
qwindowsmediadevicesession.cpp
Go to the documentation of this file.
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
5
6#include <mediacapture/qwindowsmediadevicereader_p.h>
7
8#include <QtMultimedia/qaudioinput.h>
9#include <QtMultimedia/qaudiooutput.h>
10#include <QtMultimedia/qvideosink.h>
11#include <QtMultimedia/private/qplatformvideosink_p.h>
12#include <QtMultimedia/private/qwindowsmultimediautils_p.h>
13#include <QtCore/qdebug.h>
14
15#include <cguid.h>
16
18
21{
22 m_mediaDeviceReader = new QWindowsMediaDeviceReader(this);
23 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingStarted,
24 this, &QWindowsMediaDeviceSession::handleStreamingStarted);
25 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingStopped,
26 this, &QWindowsMediaDeviceSession::handleStreamingStopped);
27 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingError,
28 this, &QWindowsMediaDeviceSession::handleStreamingError);
29 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::videoFrameChanged,
30 this, &QWindowsMediaDeviceSession::handleVideoFrameChanged);
31 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingStarted,
32 this, &QWindowsMediaDeviceSession::recordingStarted);
33 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingStopped,
34 this, &QWindowsMediaDeviceSession::recordingStopped);
35 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingError,
36 this, &QWindowsMediaDeviceSession::recordingError);
37 connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::durationChanged,
38 this, &QWindowsMediaDeviceSession::durationChanged);
39}
40
41QWindowsMediaDeviceSession::~QWindowsMediaDeviceSession()
42{
43 delete m_mediaDeviceReader;
44}
45
47{
48 return m_active;
49}
50
52{
53 return m_activating;
54}
55
57{
58 if ((active && (m_active || m_activating)) || (!active && !m_active && !m_activating))
59 return;
60
61 if (active) {
62 auto camId = QString::fromUtf8(m_activeCameraDevice.id());
63 auto micId = m_audioInput ? QString::fromUtf8(m_audioInput->device().id()) : QString();
64 if (!camId.isEmpty() || !micId.isEmpty()) {
65 if (m_mediaDeviceReader->activate(camId, m_cameraFormat, micId)) {
66 m_activating = true;
67 } else {
68 emit streamingError(MF_E_NOT_AVAILABLE);
69 }
70 } else {
71 qWarning() << Q_FUNC_INFO << "Camera ID and Microphone ID both undefined.";
72 }
73 } else {
74 m_mediaDeviceReader->deactivate();
75 m_active = false;
76 m_activating = false;
77 emit activeChanged(m_active);
78 emit readyForCaptureChanged(m_active);
79 }
80}
81
82void QWindowsMediaDeviceSession::reactivate()
83{
84 if (m_active || m_activating) {
86 setActive(false);
87 setActive(true);
89 }
90}
91
92void QWindowsMediaDeviceSession::setActiveCamera(const QCameraDevice &camera)
93{
94 m_activeCameraDevice = camera;
95 reactivate();
96}
97
99{
100 return m_activeCameraDevice;
101}
102
103void QWindowsMediaDeviceSession::setCameraFormat(const QCameraFormat &cameraFormat)
104{
105 m_cameraFormat = cameraFormat;
106}
107
108void QWindowsMediaDeviceSession::setVideoSink(QVideoSink *surface)
109{
110 m_surface = surface;
111}
112
113void QWindowsMediaDeviceSession::handleStreamingStarted()
114{
115 if (m_activating) {
116 m_active = true;
117 m_activating = false;
118 emit activeChanged(m_active);
119 emit readyForCaptureChanged(m_active);
120 }
121}
122
123void QWindowsMediaDeviceSession::handleStreamingStopped()
124{
125 m_active = false;
126 emit activeChanged(m_active);
127 emit readyForCaptureChanged(m_active);
128}
129
130void QWindowsMediaDeviceSession::handleStreamingError(HRESULT errorCode)
131{
132 if (m_surface)
133 m_surface->platformVideoSink()->setVideoFrame(QVideoFrame());
134 emit streamingError(errorCode);
135}
136
137void QWindowsMediaDeviceSession::handleVideoFrameChanged(const QVideoFrame &frame)
138{
139 if (m_surface)
140 m_surface->platformVideoSink()->setVideoFrame(frame);
141 emit videoFrameChanged(frame);
142}
143
144void QWindowsMediaDeviceSession::setAudioInputMuted(bool muted)
145{
146 m_mediaDeviceReader->setInputMuted(muted);
147}
148
150{
151 m_mediaDeviceReader->setInputVolume(volume);
152}
153
155{
156 reactivate();
157}
158
160{
161 m_mediaDeviceReader->setOutputMuted(muted);
162}
163
165{
166 m_mediaDeviceReader->setOutputVolume(volume);
167}
168
170{
171 if (m_active || m_activating)
172 m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id()));
173}
174
176{
177 if (m_audioInput == input)
178 return;
179 if (m_audioInput)
180 m_audioInput->disconnect(this);
181 m_audioInput = input;
182
184
185 if (!m_audioInput)
186 return;
187 connect(m_audioInput, &QAudioInput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioInputMuted);
188 connect(m_audioInput, &QAudioInput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioInputVolume);
189 connect(m_audioInput, &QAudioInput::deviceChanged, this, &QWindowsMediaDeviceSession::audioInputDeviceChanged);
190}
191
193{
194 if (m_audioOutput == output)
195 return;
196 if (m_audioOutput)
197 m_audioOutput->disconnect(this);
198 m_audioOutput = output;
199 if (!m_audioOutput) {
200 m_mediaDeviceReader->setAudioOutput({});
201 return;
202 }
203
204 m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id()));
205
206 connect(m_audioOutput, &QAudioOutput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioOutputMuted);
207 connect(m_audioOutput, &QAudioOutput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioOutputVolume);
208 connect(m_audioOutput, &QAudioOutput::deviceChanged, this, &QWindowsMediaDeviceSession::audioOutputDeviceChanged);
209}
210
211QMediaRecorder::Error QWindowsMediaDeviceSession::startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly)
212{
213 std::optional<GUID> container = audioOnly
214 ? QWMF::containerForAudioFileFormat(settings.mediaFormat().fileFormat())
215 : QWMF::containerForVideoFileFormat(settings.mediaFormat().fileFormat());
216 if (!container)
217 return QMediaRecorder::FormatError;
218
219 GUID videoFormat = QWMF::videoFormatForCodec(settings.videoCodec());
220 GUID audioFormat = QWMF::audioFormatForCodec(settings.audioCodec());
221
222 QSize res = settings.videoResolution();
223 UINT32 width, height;
224 if (res.width() > 0 && res.height() > 0) {
225 width = UINT32(res.width());
226 height = UINT32(res.height());
227 } else {
228 width = m_mediaDeviceReader->frameWidth();
229 height = m_mediaDeviceReader->frameHeight();
230 settings.setVideoResolution(QSize(int(width), int(height)));
231 }
232
233 qreal frameRate = settings.videoFrameRate();
234 if (frameRate <= 0) {
235 frameRate = m_mediaDeviceReader->frameRate();
236 settings.setVideoFrameRate(frameRate);
237 }
238
239 auto quality = settings.quality();
240
241 UINT32 videoBitRate = 0;
242 if (settings.videoBitRate() > 0) {
243 videoBitRate = UINT32(settings.videoBitRate());
244 } else {
245 videoBitRate = estimateVideoBitRate(videoFormat, width, height, frameRate, quality);
246 settings.setVideoBitRate(int(videoBitRate));
247 }
248
249 UINT32 audioBitRate = 0;
250 if (settings.audioBitRate() > 0) {
251 audioBitRate = UINT32(settings.audioBitRate());
252 } else {
253 audioBitRate = estimateAudioBitRate(audioFormat, quality);
254 settings.setAudioBitRate(int(audioBitRate));
255 }
256
257 return m_mediaDeviceReader->startRecording(fileName, *container, audioOnly ? GUID_NULL : videoFormat,
258 videoBitRate, width, height, frameRate,
259 audioFormat, audioBitRate);
260}
261
263{
264 m_mediaDeviceReader->stopRecording();
265}
266
268{
269 return m_mediaDeviceReader->pauseRecording();
270}
271
273{
274 return m_mediaDeviceReader->resumeRecording();
275}
276
277// empirical estimate of the required video bitrate (for H.264)
278quint32 QWindowsMediaDeviceSession::estimateVideoBitRate(const GUID &videoFormat, quint32 width, quint32 height,
279 qreal frameRate, QMediaRecorder::Quality quality)
280{
281 Q_UNUSED(videoFormat);
282
283 qreal bitsPerPixel;
284 switch (quality) {
285 case QMediaRecorder::Quality::VeryLowQuality:
286 bitsPerPixel = 0.08;
287 break;
288 case QMediaRecorder::Quality::LowQuality:
289 bitsPerPixel = 0.2;
290 break;
291 case QMediaRecorder::Quality::NormalQuality:
292 bitsPerPixel = 0.3;
293 break;
294 case QMediaRecorder::Quality::HighQuality:
295 bitsPerPixel = 0.5;
296 break;
297 case QMediaRecorder::Quality::VeryHighQuality:
298 bitsPerPixel = 0.8;
299 break;
300 default:
301 bitsPerPixel = 0.3;
302 }
303
304 // Required bitrate is not linear on the number of pixels; small resolutions
305 // require more BPP, thus the minimum values, to try to compensate it.
306 quint32 pixelsPerSec = quint32(qMax(width, 320u) * qMax(height, 240u) * qMax(frameRate, 6.0));
307 return pixelsPerSec * bitsPerPixel;
308}
309
310quint32 QWindowsMediaDeviceSession::estimateAudioBitRate(const GUID &audioFormat, QMediaRecorder::Quality quality)
311{
312 if (audioFormat == MFAudioFormat_AAC) {
313 // Bitrates supported by the AAC encoder are 96K, 128K, 160K, 192K.
314 switch (quality) {
315 case QMediaRecorder::Quality::VeryLowQuality:
316 return 96000;
317 case QMediaRecorder::Quality::LowQuality:
318 return 96000;
319 case QMediaRecorder::Quality::NormalQuality:
320 return 128000;
321 case QMediaRecorder::Quality::HighQuality:
322 return 160000;
323 case QMediaRecorder::Quality::VeryHighQuality:
324 return 192000;
325 default:
326 return 128000;
327 }
328 } else if (audioFormat == MFAudioFormat_MP3) {
329 // Bitrates supported by the MP3 encoder are
330 // 32K, 40K, 48K, 56K, 64K, 80K, 96K, 112K, 128K, 160K, 192K, 224K, 256K, 320K.
331 switch (quality) {
332 case QMediaRecorder::Quality::VeryLowQuality:
333 return 48000;
334 case QMediaRecorder::Quality::LowQuality:
335 return 96000;
336 case QMediaRecorder::Quality::NormalQuality:
337 return 128000;
338 case QMediaRecorder::Quality::HighQuality:
339 return 224000;
340 case QMediaRecorder::Quality::VeryHighQuality:
341 return 320000;
342 default:
343 return 128000;
344 }
345 } else if (audioFormat == MFAudioFormat_WMAudioV8) {
346 // Bitrates supported by the Windows Media Audio 8 encoder
347 switch (quality) {
348 case QMediaRecorder::Quality::VeryLowQuality:
349 return 32000;
350 case QMediaRecorder::Quality::LowQuality:
351 return 96000;
352 case QMediaRecorder::Quality::NormalQuality:
353 return 192000;
354 case QMediaRecorder::Quality::HighQuality:
355 return 256016;
356 case QMediaRecorder::Quality::VeryHighQuality:
357 return 320032;
358 default:
359 return 192000;
360 }
361 } else if (audioFormat == MFAudioFormat_WMAudioV9) {
362 // Bitrates supported by the Windows Media Audio 9 encoder
363 switch (quality) {
364 case QMediaRecorder::Quality::VeryLowQuality:
365 return 32000;
366 case QMediaRecorder::Quality::LowQuality:
367 return 96000;
368 case QMediaRecorder::Quality::NormalQuality:
369 return 192000;
370 case QMediaRecorder::Quality::HighQuality:
371 return 256016;
372 case QMediaRecorder::Quality::VeryHighQuality:
373 return 384000;
374 default:
375 return 192000;
376 }
377 }
378 return 0; // Use default for format
379}
380
381QT_END_NAMESPACE
382
383#include "moc_qwindowsmediadevicesession_p.cpp"
QObject * parent
Definition qobject.h:74
\inmodule QtCore
Definition qobject.h:106
void setActiveCamera(const QCameraDevice &camera)
QMediaRecorder::Error startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly)
void setAudioOutput(QAudioOutput *output)
void setCameraFormat(const QCameraFormat &cameraFormat)
Combined button and popup list for selecting options.