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