summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp')
-rw-r--r--src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp376
1 files changed, 376 insertions, 0 deletions
diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
new file mode 100644
index 000000000..b13599444
--- /dev/null
+++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediadevicesession.cpp
@@ -0,0 +1,376 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwindowsmediadevicesession_p.h"
+
+#include "qwindowsmediadevicereader_p.h"
+#include "private/qwindowsmultimediautils_p.h"
+#include "private/qplatformvideosink_p.h"
+#include <qvideosink.h>
+#include <QtCore/qdebug.h>
+#include <qaudioinput.h>
+#include <qaudiooutput.h>
+
+QT_BEGIN_NAMESPACE
+
+QWindowsMediaDeviceSession::QWindowsMediaDeviceSession(QObject *parent)
+ : QObject(parent)
+{
+ m_mediaDeviceReader = new QWindowsMediaDeviceReader(this);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingStarted,
+ this, &QWindowsMediaDeviceSession::handleStreamingStarted);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingStopped,
+ this, &QWindowsMediaDeviceSession::handleStreamingStopped);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::streamingError,
+ this, &QWindowsMediaDeviceSession::handleStreamingError);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::videoFrameChanged,
+ this, &QWindowsMediaDeviceSession::handleVideoFrameChanged);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingStarted,
+ this, &QWindowsMediaDeviceSession::recordingStarted);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingStopped,
+ this, &QWindowsMediaDeviceSession::recordingStopped);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::recordingError,
+ this, &QWindowsMediaDeviceSession::recordingError);
+ connect(m_mediaDeviceReader, &QWindowsMediaDeviceReader::durationChanged,
+ this, &QWindowsMediaDeviceSession::durationChanged);
+}
+
+QWindowsMediaDeviceSession::~QWindowsMediaDeviceSession()
+{
+ delete m_mediaDeviceReader;
+}
+
+bool QWindowsMediaDeviceSession::isActive() const
+{
+ return m_active;
+}
+
+bool QWindowsMediaDeviceSession::isActivating() const
+{
+ return m_activating;
+}
+
+void QWindowsMediaDeviceSession::setActive(bool active)
+{
+ if ((active && (m_active || m_activating)) || (!active && !m_active && !m_activating))
+ return;
+
+ if (active) {
+ auto camId = QString::fromUtf8(m_activeCameraDevice.id());
+ auto micId = m_audioInput ? QString::fromUtf8(m_audioInput->device().id()) : QString();
+ if (!camId.isEmpty() || !micId.isEmpty()) {
+ if (m_mediaDeviceReader->activate(camId, m_cameraFormat, micId)) {
+ m_activating = true;
+ } else {
+ emit streamingError(MF_E_NOT_AVAILABLE);
+ }
+ } else {
+ qWarning() << Q_FUNC_INFO << "Camera ID and Microphone ID both undefined.";
+ }
+ } else {
+ m_mediaDeviceReader->deactivate();
+ m_active = false;
+ m_activating = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+ }
+}
+
+void QWindowsMediaDeviceSession::reactivate()
+{
+ if (m_active || m_activating) {
+ pauseRecording();
+ setActive(false);
+ setActive(true);
+ resumeRecording();
+ }
+}
+
+void QWindowsMediaDeviceSession::setActiveCamera(const QCameraDevice &camera)
+{
+ m_activeCameraDevice = camera;
+ reactivate();
+}
+
+QCameraDevice QWindowsMediaDeviceSession::activeCamera() const
+{
+ return m_activeCameraDevice;
+}
+
+void QWindowsMediaDeviceSession::setCameraFormat(const QCameraFormat &cameraFormat)
+{
+ m_cameraFormat = cameraFormat;
+}
+
+void QWindowsMediaDeviceSession::setVideoSink(QVideoSink *surface)
+{
+ m_surface = surface;
+}
+
+void QWindowsMediaDeviceSession::handleStreamingStarted()
+{
+ if (m_activating) {
+ m_active = true;
+ m_activating = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+ }
+}
+
+void QWindowsMediaDeviceSession::handleStreamingStopped()
+{
+ m_active = false;
+ emit activeChanged(m_active);
+ emit readyForCaptureChanged(m_active);
+}
+
+void QWindowsMediaDeviceSession::handleStreamingError(int errorCode)
+{
+ if (m_surface)
+ m_surface->platformVideoSink()->setVideoFrame(QVideoFrame());
+ emit streamingError(errorCode);
+}
+
+void QWindowsMediaDeviceSession::handleVideoFrameChanged(const QVideoFrame &frame)
+{
+ if (m_surface)
+ m_surface->platformVideoSink()->setVideoFrame(frame);
+ emit videoFrameChanged(frame);
+}
+
+void QWindowsMediaDeviceSession::setAudioInputMuted(bool muted)
+{
+ m_mediaDeviceReader->setInputMuted(muted);
+}
+
+void QWindowsMediaDeviceSession::setAudioInputVolume(float volume)
+{
+ m_mediaDeviceReader->setInputVolume(volume);
+}
+
+void QWindowsMediaDeviceSession::audioInputDeviceChanged()
+{
+ reactivate();
+}
+
+void QWindowsMediaDeviceSession::setAudioOutputMuted(bool muted)
+{
+ m_mediaDeviceReader->setOutputMuted(muted);
+}
+
+void QWindowsMediaDeviceSession::setAudioOutputVolume(float volume)
+{
+ m_mediaDeviceReader->setOutputVolume(volume);
+}
+
+void QWindowsMediaDeviceSession::audioOutputDeviceChanged()
+{
+ if (m_active || m_activating)
+ m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id()));
+}
+
+void QWindowsMediaDeviceSession::setAudioInput(QAudioInput *input)
+{
+ if (m_audioInput == input)
+ return;
+ if (m_audioInput)
+ m_audioInput->disconnect(this);
+ m_audioInput = input;
+
+ audioInputDeviceChanged();
+
+ if (!m_audioInput)
+ return;
+ connect(m_audioInput, &QAudioInput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioInputMuted);
+ connect(m_audioInput, &QAudioInput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioInputVolume);
+ connect(m_audioInput, &QAudioInput::deviceChanged, this, &QWindowsMediaDeviceSession::audioInputDeviceChanged);
+}
+
+void QWindowsMediaDeviceSession::setAudioOutput(QAudioOutput *output)
+{
+ if (m_audioOutput == output)
+ return;
+ if (m_audioOutput)
+ m_audioOutput->disconnect(this);
+ m_audioOutput = output;
+ if (!m_audioOutput) {
+ m_mediaDeviceReader->setAudioOutput({});
+ return;
+ }
+
+ m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id()));
+
+ connect(m_audioOutput, &QAudioOutput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioOutputMuted);
+ connect(m_audioOutput, &QAudioOutput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioOutputVolume);
+ connect(m_audioOutput, &QAudioOutput::deviceChanged, this, &QWindowsMediaDeviceSession::audioOutputDeviceChanged);
+}
+
+QMediaRecorder::Error QWindowsMediaDeviceSession::startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly)
+{
+ GUID container = audioOnly ? QWindowsMultimediaUtils::containerForAudioFileFormat(settings.mediaFormat().fileFormat())
+ : QWindowsMultimediaUtils::containerForVideoFileFormat(settings.mediaFormat().fileFormat());
+ GUID videoFormat = QWindowsMultimediaUtils::videoFormatForCodec(settings.videoCodec());
+ GUID audioFormat = QWindowsMultimediaUtils::audioFormatForCodec(settings.audioCodec());
+
+ QSize res = settings.videoResolution();
+ UINT32 width, height;
+ if (res.width() > 0 && res.height() > 0) {
+ width = UINT32(res.width());
+ height = UINT32(res.height());
+ } else {
+ width = m_mediaDeviceReader->frameWidth();
+ height = m_mediaDeviceReader->frameHeight();
+ settings.setVideoResolution(QSize(int(width), int(height)));
+ }
+
+ qreal frameRate = settings.videoFrameRate();
+ if (frameRate <= 0) {
+ frameRate = m_mediaDeviceReader->frameRate();
+ settings.setVideoFrameRate(frameRate);
+ }
+
+ auto quality = settings.quality();
+
+ UINT32 videoBitRate = 0;
+ if (settings.videoBitRate() > 0) {
+ videoBitRate = UINT32(settings.videoBitRate());
+ } else {
+ videoBitRate = estimateVideoBitRate(videoFormat, width, height, frameRate, quality);
+ settings.setVideoBitRate(int(videoBitRate));
+ }
+
+ UINT32 audioBitRate = 0;
+ if (settings.audioBitRate() > 0) {
+ audioBitRate = UINT32(settings.audioBitRate());
+ } else {
+ audioBitRate = estimateAudioBitRate(audioFormat, quality);
+ settings.setAudioBitRate(int(audioBitRate));
+ }
+
+ return m_mediaDeviceReader->startRecording(fileName, container, audioOnly ? GUID_NULL : videoFormat,
+ videoBitRate, width, height, frameRate,
+ audioFormat, audioBitRate);
+}
+
+void QWindowsMediaDeviceSession::stopRecording()
+{
+ m_mediaDeviceReader->stopRecording();
+}
+
+bool QWindowsMediaDeviceSession::pauseRecording()
+{
+ return m_mediaDeviceReader->pauseRecording();
+}
+
+bool QWindowsMediaDeviceSession::resumeRecording()
+{
+ return m_mediaDeviceReader->resumeRecording();
+}
+
+// empirical estimate of the required video bitrate (for H.264)
+quint32 QWindowsMediaDeviceSession::estimateVideoBitRate(const GUID &videoFormat, quint32 width, quint32 height,
+ qreal frameRate, QMediaRecorder::Quality quality)
+{
+ Q_UNUSED(videoFormat);
+
+ qreal bitsPerPixel;
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ bitsPerPixel = 0.08;
+ break;
+ case QMediaRecorder::Quality::LowQuality:
+ bitsPerPixel = 0.2;
+ break;
+ case QMediaRecorder::Quality::NormalQuality:
+ bitsPerPixel = 0.3;
+ break;
+ case QMediaRecorder::Quality::HighQuality:
+ bitsPerPixel = 0.5;
+ break;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ bitsPerPixel = 0.8;
+ break;
+ default:
+ bitsPerPixel = 0.3;
+ }
+
+ // Required bitrate is not linear on the number of pixels; small resolutions
+ // require more BPP, thus the minimum values, to try to compensate it.
+ quint32 pixelsPerSec = quint32(qMax(width, 320u) * qMax(height, 240u) * qMax(frameRate, 6.0));
+ return pixelsPerSec * bitsPerPixel;
+}
+
+quint32 QWindowsMediaDeviceSession::estimateAudioBitRate(const GUID &audioFormat, QMediaRecorder::Quality quality)
+{
+ if (audioFormat == MFAudioFormat_AAC) {
+ // Bitrates supported by the AAC encoder are 96K, 128K, 160K, 192K.
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 128000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 160000;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 192000;
+ default:
+ return 128000;
+ }
+ } else if (audioFormat == MFAudioFormat_MP3) {
+ // Bitrates supported by the MP3 encoder are
+ // 32K, 40K, 48K, 56K, 64K, 80K, 96K, 112K, 128K, 160K, 192K, 224K, 256K, 320K.
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 48000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 128000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 224000;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 320000;
+ default:
+ return 128000;
+ }
+ } else if (audioFormat == MFAudioFormat_WMAudioV8) {
+ // Bitrates supported by the Windows Media Audio 8 encoder
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 32000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 192000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 256016;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 320032;
+ default:
+ return 192000;
+ }
+ } else if (audioFormat == MFAudioFormat_WMAudioV9) {
+ // Bitrates supported by the Windows Media Audio 9 encoder
+ switch (quality) {
+ case QMediaRecorder::Quality::VeryLowQuality:
+ return 32000;
+ case QMediaRecorder::Quality::LowQuality:
+ return 96000;
+ case QMediaRecorder::Quality::NormalQuality:
+ return 192000;
+ case QMediaRecorder::Quality::HighQuality:
+ return 256016;
+ case QMediaRecorder::Quality::VeryHighQuality:
+ return 384000;
+ default:
+ return 192000;
+ }
+ }
+ return 0; // Use default for format
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowsmediadevicesession_p.cpp"