summaryrefslogtreecommitdiffstats
path: root/src/plugins/directshow/camera/dscamerasession.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2020-12-18 08:54:44 +0100
committerLars Knoll <lars.knoll@qt.io>2021-01-20 19:14:09 +0000
commitaa441d63a1ed0843f4632da2d0a97a7b8bb24b43 (patch)
tree0059466afbde487245696891431d4cfbc26ee557 /src/plugins/directshow/camera/dscamerasession.cpp
parent5acdceb51f18e46d31ae3a99844a751c6742c8dc (diff)
Remove the DirectShow backend
It's been deprecated by Microsoft since quite some time. We should only have one backend on Windows, and then WMF seems to be the correct one. WMF is missing features like Camera, so some additional implementation work on WMF will be required. Change-Id: I5c3db4020b130efa78575c9da116a8ae6c34f7a1 Reviewed-by: Doris Verria <doris.verria@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/plugins/directshow/camera/dscamerasession.cpp')
-rw-r--r--src/plugins/directshow/camera/dscamerasession.cpp1177
1 files changed, 0 insertions, 1177 deletions
diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp
deleted file mode 100644
index 7ceefe2c5..000000000
--- a/src/plugins/directshow/camera/dscamerasession.cpp
+++ /dev/null
@@ -1,1177 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/qdebug.h>
-#include <QFile>
-#include <QtConcurrent/QtConcurrentRun>
-#include <QtMultimedia/qabstractvideobuffer.h>
-#include <QtMultimedia/qvideosurfaceformat.h>
-#include <QtMultimedia/qcameraimagecapture.h>
-#include <private/qmemoryvideobuffer_p.h>
-
-#include "dscamerasession.h"
-#include "dsvideorenderer.h"
-#include "directshowsamplegrabber.h"
-#include "directshowcameraglobal.h"
-#include "directshowmediatype.h"
-#include "directshowutils.h"
-#include "directshowvideoprobecontrol.h"
-
-QT_BEGIN_NAMESPACE
-
-DSCameraSession::DSCameraSession(QObject *parent)
- : QObject(parent)
-{
- connect(this, &DSCameraSession::statusChanged,
- this, &DSCameraSession::updateReadyForCapture);
-
- m_deviceLostEventTimer.setSingleShot(true);
- connect(&m_deviceLostEventTimer, &QTimer::timeout, [&]() {
- IMediaEvent *pEvent = com_cast<IMediaEvent>(m_filterGraph, IID_IMediaEvent);
- if (!pEvent)
- return;
-
- long eventCode;
- LONG_PTR param1;
- LONG_PTR param2;
- while (pEvent->GetEvent(&eventCode, &param1, &param2, 0) == S_OK) {
- switch (eventCode) {
- case EC_DEVICE_LOST:
- unload();
- break;
- default:
- break;
- }
-
- pEvent->FreeEventParams(eventCode, param1, param2);
- }
-
- pEvent->Release();
- });
-}
-
-DSCameraSession::~DSCameraSession()
-{
- unload();
-}
-
-void DSCameraSession::setSurface(QAbstractVideoSurface* surface)
-{
- m_surface = surface;
-}
-
-void DSCameraSession::setDevice(const QString &device)
-{
- m_sourceDeviceName = device;
-}
-
-QCameraViewfinderSettings DSCameraSession::viewfinderSettings() const
-{
- return m_status == QCamera::ActiveStatus ? m_actualViewfinderSettings : m_viewfinderSettings;
-}
-
-void DSCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
-{
- m_viewfinderSettings = settings;
-}
-
-qreal DSCameraSession::scaledImageProcessingParameterValue(
- const ImageProcessingParameterInfo &sourceValueInfo)
-{
- if (sourceValueInfo.currentValue == sourceValueInfo.defaultValue)
- return 0.0f;
- if (sourceValueInfo.currentValue < sourceValueInfo.defaultValue) {
- return ((sourceValueInfo.currentValue - sourceValueInfo.minimumValue)
- / qreal(sourceValueInfo.defaultValue - sourceValueInfo.minimumValue))
- + (-1.0f);
- }
- return ((sourceValueInfo.currentValue - sourceValueInfo.defaultValue)
- / qreal(sourceValueInfo.maximumValue - sourceValueInfo.defaultValue));
-}
-
-qint32 DSCameraSession::sourceImageProcessingParameterValue(
- qreal scaledValue, const ImageProcessingParameterInfo &valueRange)
-{
- if (qFuzzyIsNull(scaledValue))
- return valueRange.defaultValue;
- if (scaledValue < 0.0f) {
- return ((scaledValue - (-1.0f)) * (valueRange.defaultValue - valueRange.minimumValue))
- + valueRange.minimumValue;
- }
- return (scaledValue * (valueRange.maximumValue - valueRange.defaultValue))
- + valueRange.defaultValue;
-}
-
-static QCameraImageProcessingControl::ProcessingParameter searchRelatedResultingParameter(
- QCameraImageProcessingControl::ProcessingParameter sourceParameter)
-{
- if (sourceParameter == QCameraImageProcessingControl::WhiteBalancePreset)
- return QCameraImageProcessingControl::ColorTemperature;
- return sourceParameter;
-}
-
-bool DSCameraSession::isImageProcessingParameterSupported(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- return m_imageProcessingParametersInfos.contains(resultingParameter);
-}
-
-bool DSCameraSession::isImageProcessingParameterValueSupported(
- QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value) const
-{
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- QMap<QCameraImageProcessingControl::ProcessingParameter,
- ImageProcessingParameterInfo>::const_iterator sourceValueInfo =
- m_imageProcessingParametersInfos.constFind(resultingParameter);
-
- if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
- return false;
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode checkedValue =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- // Supports only the Manual and the Auto values
- if (checkedValue != QCameraImageProcessing::WhiteBalanceManual
- && checkedValue != QCameraImageProcessing::WhiteBalanceAuto) {
- return false;
- }
- }
- break;
-
- case QCameraImageProcessingControl::ColorTemperature: {
- const qint32 checkedValue = value.toInt();
- if (checkedValue < (*sourceValueInfo).minimumValue
- || checkedValue > (*sourceValueInfo).maximumValue) {
- return false;
- }
- }
- break;
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment: {
- const qint32 sourceValue = sourceImageProcessingParameterValue(
- value.toReal(), (*sourceValueInfo));
- if (sourceValue < (*sourceValueInfo).minimumValue
- || sourceValue > (*sourceValueInfo).maximumValue)
- return false;
- }
- break;
-
- default:
- return false;
- }
-
- return true;
-}
-
-QVariant DSCameraSession::imageProcessingParameter(
- QCameraImageProcessingControl::ProcessingParameter parameter) const
-{
- if (!m_graphBuilder) {
- auto it = m_pendingImageProcessingParametrs.find(parameter);
- return it != m_pendingImageProcessingParametrs.end() ? it.value() : QVariant();
- }
-
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- QMap<QCameraImageProcessingControl::ProcessingParameter,
- ImageProcessingParameterInfo>::const_iterator sourceValueInfo =
- m_imageProcessingParametersInfos.constFind(resultingParameter);
-
- if (sourceValueInfo == m_imageProcessingParametersInfos.constEnd())
- return QVariant();
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset:
- return QVariant::fromValue<QCameraImageProcessing::WhiteBalanceMode>(
- (*sourceValueInfo).capsFlags == VideoProcAmp_Flags_Auto
- ? QCameraImageProcessing::WhiteBalanceAuto
- : QCameraImageProcessing::WhiteBalanceManual);
-
- case QCameraImageProcessingControl::ColorTemperature:
- return QVariant::fromValue<qint32>((*sourceValueInfo).currentValue);
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment:
- return scaledImageProcessingParameterValue((*sourceValueInfo));
-
- default:
- return QVariant();
- }
-}
-
-void DSCameraSession::setImageProcessingParameter(
- QCameraImageProcessingControl::ProcessingParameter parameter,
- const QVariant &value)
-{
- if (!m_graphBuilder) {
- m_pendingImageProcessingParametrs.insert(parameter, value);
- return;
- }
-
- const QCameraImageProcessingControl::ProcessingParameter resultingParameter =
- searchRelatedResultingParameter(parameter);
-
- QMap<QCameraImageProcessingControl::ProcessingParameter,
- ImageProcessingParameterInfo>::iterator sourceValueInfo =
- m_imageProcessingParametersInfos.find(resultingParameter);
-
- if (sourceValueInfo == m_imageProcessingParametersInfos.end())
- return;
-
- LONG sourceValue = 0;
- LONG capsFlags = VideoProcAmp_Flags_Manual;
-
- switch (parameter) {
-
- case QCameraImageProcessingControl::WhiteBalancePreset: {
- const QCameraImageProcessing::WhiteBalanceMode checkedValue =
- value.value<QCameraImageProcessing::WhiteBalanceMode>();
- // Supports only the Manual and the Auto values
- if (checkedValue == QCameraImageProcessing::WhiteBalanceManual)
- capsFlags = VideoProcAmp_Flags_Manual;
- else if (checkedValue == QCameraImageProcessing::WhiteBalanceAuto)
- capsFlags = VideoProcAmp_Flags_Auto;
- else
- return;
-
- sourceValue = ((*sourceValueInfo).hasBeenExplicitlySet)
- ? (*sourceValueInfo).currentValue
- : (*sourceValueInfo).defaultValue;
- }
- break;
-
- case QCameraImageProcessingControl::ColorTemperature:
- sourceValue = value.isValid() ?
- value.value<qint32>() : (*sourceValueInfo).defaultValue;
- capsFlags = (*sourceValueInfo).capsFlags;
- break;
-
- case QCameraImageProcessingControl::ContrastAdjustment: // falling back
- case QCameraImageProcessingControl::SaturationAdjustment: // falling back
- case QCameraImageProcessingControl::BrightnessAdjustment: // falling back
- case QCameraImageProcessingControl::SharpeningAdjustment:
- if (value.isValid()) {
- sourceValue = sourceImageProcessingParameterValue(
- value.toReal(), (*sourceValueInfo));
- } else {
- sourceValue = (*sourceValueInfo).defaultValue;
- }
- break;
-
- default:
- return;
- }
-
- IAMVideoProcAmp *pVideoProcAmp = nullptr;
- HRESULT hr = m_graphBuilder->FindInterface(
- nullptr,
- nullptr,
- m_sourceFilter,
- IID_IAMVideoProcAmp,
- reinterpret_cast<void**>(&pVideoProcAmp)
- );
-
- if (FAILED(hr) || !pVideoProcAmp) {
- qWarning() << "failed to find the video proc amp";
- return;
- }
-
- hr = pVideoProcAmp->Set(
- (*sourceValueInfo).videoProcAmpProperty,
- sourceValue,
- capsFlags);
-
- pVideoProcAmp->Release();
-
- if (FAILED(hr)) {
- qWarning() << "failed to set the parameter value";
- } else {
- (*sourceValueInfo).capsFlags = capsFlags;
- (*sourceValueInfo).hasBeenExplicitlySet = true;
- (*sourceValueInfo).currentValue = sourceValue;
- }
-}
-
-bool DSCameraSession::getCameraControlInterface(IAMCameraControl **cameraControl) const
-{
- if (!m_sourceFilter) {
- qCDebug(qtDirectShowPlugin, "getCameraControlInterface failed: No capture filter!");
- return false;
- }
-
- if (!cameraControl) {
- qCDebug(qtDirectShowPlugin, "getCameraControlInterface failed: Invalid out argument!");
- return false;
- }
-
- if (FAILED(m_sourceFilter->QueryInterface(IID_IAMCameraControl, reinterpret_cast<void **>(cameraControl)))) {
- qCDebug(qtDirectShowPlugin, "getCameraControlInterface failed: Querying camera control failed!");
- return false;
- }
-
- return true;
-}
-
-bool DSCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
-{
- return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
-}
-
-QCameraImageCapture::CaptureDestinations DSCameraSession::captureDestination() const
-{
- return m_captureDestinations;
-}
-
-void DSCameraSession::setCaptureDestination(QCameraImageCapture::CaptureDestinations destinations)
-{
- if (m_captureDestinations == destinations)
- return;
-
- m_captureDestinations = destinations;
- Q_EMIT captureDestinationChanged(m_captureDestinations);
-}
-
-void DSCameraSession::addVideoProbe(DirectShowVideoProbeControl *probe)
-{
- const QMutexLocker locker(&m_probeMutex);
- m_videoProbeControl = probe;
-}
-
-void DSCameraSession::removeVideoProbe(DirectShowVideoProbeControl *probe)
-{
- Q_UNUSED(probe);
- Q_ASSERT(m_videoProbeControl == probe);
- const QMutexLocker locker(&m_probeMutex);
- m_videoProbeControl = nullptr;
-}
-
-bool DSCameraSession::load()
-{
- unload();
-
- setStatus(QCamera::LoadingStatus);
-
- bool succeeded = createFilterGraph();
- if (succeeded)
- setStatus(QCamera::LoadedStatus);
- else
- setStatus(QCamera::UnavailableStatus);
-
- return succeeded;
-}
-
-bool DSCameraSession::unload()
-{
- if (!m_graphBuilder)
- return false;
-
- if (!stopPreview())
- return false;
-
- setStatus(QCamera::UnloadingStatus);
-
- m_needsHorizontalMirroring = false;
- m_supportedViewfinderSettings.clear();
- m_supportedFormats.clear();
- SAFE_RELEASE(m_sourceFilter);
- SAFE_RELEASE(m_nullRendererFilter);
- SAFE_RELEASE(m_filterGraph);
- SAFE_RELEASE(m_graphBuilder);
- SAFE_RELEASE(m_outputPin);
-
- setStatus(QCamera::UnloadedStatus);
-
- return true;
-}
-
-bool DSCameraSession::startPreview()
-{
- if (m_previewStarted)
- return true;
-
- if (!m_graphBuilder)
- return false;
-
- setStatus(QCamera::StartingStatus);
-
- QString errorString;
- HRESULT hr = S_OK;
- IMediaControl* pControl = nullptr;
-
- if (!configurePreviewFormat()) {
- errorString = tr("Failed to configure preview format");
- goto failed;
- }
-
- if (!connectGraph()) {
- errorString = tr("Failed to connect graph");
- goto failed;
- }
-
- if (m_surface)
- m_surface->start(m_previewSurfaceFormat);
-
- hr = m_filterGraph->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&pControl));
- if (FAILED(hr)) {
- errorString = tr("Failed to get stream control");
- goto failed;
- }
- hr = pControl->Run();
- pControl->Release();
-
- if (FAILED(hr)) {
- errorString = tr("Failed to start");
- goto failed;
- }
-
- setStatus(QCamera::ActiveStatus);
- m_previewStarted = true;
- return true;
-
-failed:
- // go back to a clean state
- if (m_surface && m_surface->isActive())
- m_surface->stop();
- disconnectGraph();
- setError(QCamera::CameraError, errorString, hr);
- return false;
-}
-
-bool DSCameraSession::stopPreview()
-{
- if (!m_previewStarted)
- return true;
-
- setStatus(QCamera::StoppingStatus);
-
- if (m_previewSampleGrabber)
- m_previewSampleGrabber->stop();
-
- QString errorString;
- IMediaControl* pControl = nullptr;
- HRESULT hr = m_filterGraph->QueryInterface(IID_IMediaControl,
- reinterpret_cast<void**>(&pControl));
- if (FAILED(hr)) {
- errorString = tr("Failed to get stream control");
- goto failed;
- }
-
- hr = pControl->Stop();
- pControl->Release();
- if (FAILED(hr)) {
- errorString = tr("Failed to stop");
- goto failed;
- }
-
- disconnectGraph();
-
- m_sourceFormat.clear();
-
- m_previewStarted = false;
- setStatus(QCamera::LoadedStatus);
- return true;
-
-failed:
- setError(QCamera::CameraError, errorString, hr);
- return false;
-}
-
-void DSCameraSession::setError(int error, const QString &errorString, HRESULT hr)
-{
- qErrnoWarning(hr, "[0x%x] %s", hr, qPrintable(errorString));
- emit cameraError(error, errorString);
- setStatus(QCamera::UnloadedStatus);
-}
-
-void DSCameraSession::setStatus(QCamera::Status status)
-{
- if (m_status == status)
- return;
-
- m_status = status;
- emit statusChanged(m_status);
-}
-
-bool DSCameraSession::isReadyForCapture()
-{
- return m_readyForCapture;
-}
-
-void DSCameraSession::updateReadyForCapture()
-{
- bool isReady = (m_status == QCamera::ActiveStatus && m_imageCaptureFileName.isEmpty());
- if (isReady != m_readyForCapture) {
- m_readyForCapture = isReady;
- emit readyForCaptureChanged(isReady);
- }
-}
-
-int DSCameraSession::captureImage(const QString &fileName)
-{
- ++m_imageIdCounter;
-
- if (!m_readyForCapture) {
- emit captureError(m_imageIdCounter, QCameraImageCapture::NotReadyError,
- tr("Camera not ready for capture"));
- return m_imageIdCounter;
- }
-
- const QString ext = !m_imageEncoderSettings.codec().isEmpty()
- ? m_imageEncoderSettings.codec().toLower()
- : QLatin1String("jpg");
- m_imageCaptureFileName = m_fileNameGenerator.generateFileName(fileName,
- QMediaStorageLocation::Pictures,
- QLatin1String("IMG_"),
- ext);
-
- updateReadyForCapture();
-
- m_captureMutex.lock();
- m_currentImageId = m_imageIdCounter;
- m_captureMutex.unlock();
-
- return m_imageIdCounter;
-}
-
-void DSCameraSession::onFrameAvailable(double time, const QByteArray &data)
-{
- // !!! Not called on the main thread
- Q_UNUSED(time);
-
- m_presentMutex.lock();
-
- // In case the source produces frames faster than we can display them,
- // only keep the most recent one
- m_currentFrame = QVideoFrame(new QMemoryVideoBuffer(data, m_stride),
- m_previewSize,
- m_previewPixelFormat);
-
- m_presentMutex.unlock();
-
- {
- const QMutexLocker locker(&m_probeMutex);
- if (m_currentFrame.isValid() && m_videoProbeControl)
- Q_EMIT m_videoProbeControl->videoFrameProbed(m_currentFrame);
- }
-
- // Image capture
- QMutexLocker locker(&m_captureMutex);
- if (m_currentImageId != -1 && !m_capturedFrame.isValid()) {
- m_capturedFrame = m_currentFrame;
- QMetaObject::invokeMethod(this, "imageExposed", Qt::QueuedConnection, Q_ARG(int, m_currentImageId));
- }
-
- QMetaObject::invokeMethod(this, "presentFrame", Qt::QueuedConnection);
-}
-
-void DSCameraSession::presentFrame()
-{
- // If no frames provided from ISampleGrabber for some time
- // the device might be potentially unplugged.
- m_deviceLostEventTimer.start(100);
-
- m_presentMutex.lock();
-
- if (m_currentFrame.isValid() && m_surface) {
- m_surface->present(m_currentFrame);
- m_currentFrame = QVideoFrame();
- }
-
- m_presentMutex.unlock();
-
- QImage captureImage;
- const int captureId = m_currentImageId;
-
- m_captureMutex.lock();
-
- if (m_capturedFrame.isValid()) {
-
- captureImage = m_capturedFrame.image();
-
- const bool needsVerticalMirroring = m_previewSurfaceFormat.scanLineDirection() != QVideoSurfaceFormat::TopToBottom;
- captureImage = captureImage.mirrored(m_needsHorizontalMirroring, needsVerticalMirroring); // also causes a deep copy of the data
-
- QtConcurrent::run(&DSCameraSession::processCapturedImage, this,
- m_currentImageId, m_captureDestinations, captureImage, m_imageCaptureFileName);
-
- m_imageCaptureFileName.clear();
- m_currentImageId = -1;
-
- m_capturedFrame = QVideoFrame();
- }
-
- m_captureMutex.unlock();
-
- if (!captureImage.isNull())
- emit imageCaptured(captureId, captureImage);
-
- updateReadyForCapture();
-}
-
-void DSCameraSession::processCapturedImage(int id,
- QCameraImageCapture::CaptureDestinations captureDestinations,
- const QImage &image,
- const QString &path)
-{
- const QString format = m_imageEncoderSettings.codec();
- if (captureDestinations & QCameraImageCapture::CaptureToFile) {
- if (image.save(path, !format.isEmpty() ? format.toUtf8().constData() : "JPG")) {
- Q_EMIT imageSaved(id, path);
- } else {
- Q_EMIT captureError(id, QCameraImageCapture::ResourceError,
- tr("Could not save image to file."));
- }
- }
-
- if (captureDestinations & QCameraImageCapture::CaptureToBuffer)
- Q_EMIT imageAvailable(id, QVideoFrame(image));
-}
-
-bool DSCameraSession::createFilterGraph()
-{
- // Previously containered in <qedit.h>.
- static const CLSID cLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } };
-
- QString errorString;
- HRESULT hr;
- IMoniker* pMoniker = nullptr;
- ICreateDevEnum* pDevEnum = nullptr;
- IEnumMoniker* pEnum = nullptr;
-
- // Create the filter graph
- hr = CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC,
- IID_IGraphBuilder, reinterpret_cast<void**>(&m_filterGraph));
- if (FAILED(hr)) {
- errorString = tr("Failed to create filter graph");
- goto failed;
- }
-
- // Create the capture graph builder
- hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, nullptr, CLSCTX_INPROC,
- IID_ICaptureGraphBuilder2,
- reinterpret_cast<void**>(&m_graphBuilder));
- if (FAILED(hr)) {
- errorString = tr("Failed to create graph builder");
- goto failed;
- }
-
- // Attach the filter graph to the capture graph
- hr = m_graphBuilder->SetFiltergraph(m_filterGraph);
- if (FAILED(hr)) {
- errorString = tr("Failed to connect capture graph and filter graph");
- goto failed;
- }
-
- // Find the Capture device
- hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
- CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
- reinterpret_cast<void**>(&pDevEnum));
- if (SUCCEEDED(hr)) {
- // Create an enumerator for the video capture category
- hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
- pDevEnum->Release();
- if (S_OK == hr) {
- pEnum->Reset();
- IMalloc *mallocInterface = nullptr;
- CoGetMalloc(1, (LPMALLOC*)&mallocInterface);
- //go through and find all video capture devices
- while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
-
- BSTR strName = nullptr;
- hr = pMoniker->GetDisplayName(nullptr, nullptr, &strName);
- if (SUCCEEDED(hr)) {
- QString output = QString::fromWCharArray(strName);
- mallocInterface->Free(strName);
- if (m_sourceDeviceName.contains(output)) {
- hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter,
- reinterpret_cast<void**>(&m_sourceFilter));
- if (SUCCEEDED(hr)) {
- pMoniker->Release();
- break;
- }
- }
- }
- pMoniker->Release();
- }
- mallocInterface->Release();
- if (nullptr == m_sourceFilter)
- {
- if (m_sourceDeviceName.contains(QLatin1String("default")))
- {
- pEnum->Reset();
- // still have to loop to discard bind to storage failure case
- while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
- IPropertyBag *pPropBag = nullptr;
-
- hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag,
- reinterpret_cast<void**>(&pPropBag));
- if (FAILED(hr)) {
- pMoniker->Release();
- continue; // Don't panic yet
- }
-
- // No need to get the description, just grab it
-
- hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter,
- reinterpret_cast<void**>(&m_sourceFilter));
- pPropBag->Release();
- pMoniker->Release();
- if (SUCCEEDED(hr))
- break; // done, stop looping through
- qWarning("Object bind failed");
- }
- }
- }
- pEnum->Release();
- }
- }
-
- if (!m_sourceFilter) {
- errorString = tr("No capture device found");
- goto failed;
- }
-
- if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, &m_outputPin, &hr))
- qWarning() << "Failed to get the pin for the video control:" << hr;
-
- // Sample grabber filter
- if (!m_previewSampleGrabber) {
- m_previewSampleGrabber = new DirectShowSampleGrabber(this);
- connect(m_previewSampleGrabber, &DirectShowSampleGrabber::bufferAvailable,
- this, &DSCameraSession::onFrameAvailable, Qt::DirectConnection);
- }
-
-
- // Null renderer. Input connected to the sample grabber's output. Simply
- // discard the samples it receives.
- hr = CoCreateInstance(cLSID_NullRenderer, nullptr, CLSCTX_INPROC,
- IID_IBaseFilter, (void**)&m_nullRendererFilter);
- if (FAILED(hr)) {
- errorString = tr("Failed to create null renderer");
- goto failed;
- }
-
- updateSourceCapabilities();
-
- return true;
-
-failed:
- m_needsHorizontalMirroring = false;
- SAFE_RELEASE(m_sourceFilter);
- SAFE_RELEASE(m_nullRendererFilter);
- SAFE_RELEASE(m_filterGraph);
- SAFE_RELEASE(m_graphBuilder);
- setError(QCamera::CameraError, errorString, hr);
-
- return false;
-}
-
-bool DSCameraSession::configurePreviewFormat()
-{
- // Resolve viewfinder settings
- int settingsIndex = 0;
- const QSize captureResolution = m_imageEncoderSettings.resolution();
- const QSize resolution = captureResolution.isValid() ? captureResolution : m_viewfinderSettings.resolution();
- QCameraViewfinderSettings resolvedViewfinderSettings;
- for (const QCameraViewfinderSettings &s : qAsConst(m_supportedViewfinderSettings)) {
- if ((resolution.isEmpty() || resolution == s.resolution())
- && (qFuzzyIsNull(m_viewfinderSettings.minimumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.minimumFrameRate(), (float)s.minimumFrameRate()))
- && (qFuzzyIsNull(m_viewfinderSettings.maximumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.maximumFrameRate(), (float)s.maximumFrameRate()))
- && (m_viewfinderSettings.pixelFormat() == QVideoFrame::Format_Invalid || m_viewfinderSettings.pixelFormat() == s.pixelFormat())
- && (m_viewfinderSettings.pixelAspectRatio().isEmpty() || m_viewfinderSettings.pixelAspectRatio() == s.pixelAspectRatio())) {
- resolvedViewfinderSettings = s;
- break;
- }
- ++settingsIndex;
- }
-
- if (resolvedViewfinderSettings.isNull()) {
- qWarning("Invalid viewfinder settings");
- return false;
- }
-
- m_actualViewfinderSettings = resolvedViewfinderSettings;
-
- m_sourceFormat = m_supportedFormats[settingsIndex];
- // Set frame rate.
- // We don't care about the minimumFrameRate, DirectShow only allows to set an
- // average frame rate, so set that to the maximumFrameRate.
- VIDEOINFOHEADER *videoInfo = reinterpret_cast<VIDEOINFOHEADER*>(m_sourceFormat->pbFormat);
- videoInfo->AvgTimePerFrame = 10000000 / resolvedViewfinderSettings.maximumFrameRate();
-
- m_previewPixelFormat = resolvedViewfinderSettings.pixelFormat();
- const AM_MEDIA_TYPE *resolvedGrabberFormat = &m_sourceFormat;
-
- if (m_surface) {
- const auto surfaceFormats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::NoHandle);
- if (!surfaceFormats.contains(m_previewPixelFormat)) {
- if (surfaceFormats.contains(QVideoFrame::Format_RGB32)) {
- // As a fallback, we support RGB32, if the capture source doesn't support
- // that format, the graph builder will automatically insert a
- // converter (when possible).
-
- static const AM_MEDIA_TYPE rgb32GrabberFormat { MEDIATYPE_Video, MEDIASUBTYPE_ARGB32, 0, 0, 0, FORMAT_VideoInfo, nullptr, 0, nullptr};
- resolvedGrabberFormat = &rgb32GrabberFormat;
- m_previewPixelFormat = QVideoFrame::Format_RGB32;
-
- } else {
- qWarning() << "Video surface needs to support at least RGB32 pixel format";
- return false;
- }
- }
- }
-
- m_previewSize = resolvedViewfinderSettings.resolution();
- m_previewSurfaceFormat = QVideoSurfaceFormat(m_previewSize,
- m_previewPixelFormat,
- QAbstractVideoBuffer::NoHandle);
- m_previewSurfaceFormat.setScanLineDirection(DirectShowMediaType::scanLineDirection(m_previewPixelFormat, videoInfo->bmiHeader));
- m_stride = DirectShowMediaType::bytesPerLine(m_previewSurfaceFormat);
-
- HRESULT hr;
- IAMStreamConfig* pConfig = nullptr;
- hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter, IID_IAMStreamConfig,
- reinterpret_cast<void**>(&pConfig));
- if (FAILED(hr)) {
- qWarning() << "Failed to get config for capture device";
- return false;
- }
-
- hr = pConfig->SetFormat(&m_sourceFormat);
-
- pConfig->Release();
-
- if (FAILED(hr)) {
- qWarning() << "Unable to set video format on capture device";
- return false;
- }
-
- if (!m_previewSampleGrabber->setMediaType(resolvedGrabberFormat))
- return false;
-
- m_previewSampleGrabber->start(DirectShowSampleGrabber::CallbackMethod::BufferCB);
-
- return true;
-}
-
-void DSCameraSession::updateImageProcessingParametersInfos()
-{
- if (!m_graphBuilder) {
- qWarning() << "failed to access to the graph builder";
- return;
- }
-
- IAMVideoProcAmp *pVideoProcAmp = nullptr;
- const HRESULT hr = m_graphBuilder->FindInterface(
- nullptr,
- nullptr,
- m_sourceFilter,
- IID_IAMVideoProcAmp,
- reinterpret_cast<void**>(&pVideoProcAmp)
- );
-
- if (FAILED(hr) || !pVideoProcAmp) {
- qWarning() << "failed to find the video proc amp";
- return;
- }
-
- for (int property = VideoProcAmp_Brightness; property <= VideoProcAmp_Gain; ++property) {
-
- QCameraImageProcessingControl::ProcessingParameter processingParameter; // not initialized
-
- switch (property) {
- case VideoProcAmp_Brightness:
- processingParameter = QCameraImageProcessingControl::BrightnessAdjustment;
- break;
- case VideoProcAmp_Contrast:
- processingParameter = QCameraImageProcessingControl::ContrastAdjustment;
- break;
- case VideoProcAmp_Saturation:
- processingParameter = QCameraImageProcessingControl::SaturationAdjustment;
- break;
- case VideoProcAmp_Sharpness:
- processingParameter = QCameraImageProcessingControl::SharpeningAdjustment;
- break;
- case VideoProcAmp_WhiteBalance:
- processingParameter = QCameraImageProcessingControl::ColorTemperature;
- break;
- default: // unsupported or not implemented yet parameter
- continue;
- }
-
- ImageProcessingParameterInfo sourceValueInfo;
- LONG steppingDelta = 0;
-
- HRESULT hr = pVideoProcAmp->GetRange(
- property,
- &sourceValueInfo.minimumValue,
- &sourceValueInfo.maximumValue,
- &steppingDelta,
- &sourceValueInfo.defaultValue,
- &sourceValueInfo.capsFlags);
-
- if (FAILED(hr))
- continue;
-
- hr = pVideoProcAmp->Get(
- property,
- &sourceValueInfo.currentValue,
- &sourceValueInfo.capsFlags);
-
- if (FAILED(hr))
- continue;
-
- sourceValueInfo.videoProcAmpProperty = static_cast<VideoProcAmpProperty>(property);
-
- m_imageProcessingParametersInfos.insert(processingParameter, sourceValueInfo);
- }
-
- pVideoProcAmp->Release();
-
- for (auto it = m_pendingImageProcessingParametrs.cbegin();
- it != m_pendingImageProcessingParametrs.cend();
- ++it) {
- setImageProcessingParameter(it.key(), it.value());
- }
- m_pendingImageProcessingParametrs.clear();
-}
-
-bool DSCameraSession::connectGraph()
-{
- HRESULT hr = m_filterGraph->AddFilter(m_sourceFilter, L"Capture Filter");
- if (FAILED(hr)) {
- qWarning() << "failed to add capture filter to graph";
- return false;
- }
-
- if (FAILED(m_filterGraph->AddFilter(m_previewSampleGrabber->filter(), L"Sample Grabber"))) {
- qWarning() << "failed to add sample grabber to graph";
- return false;
- }
-
- hr = m_filterGraph->AddFilter(m_nullRendererFilter, L"Null Renderer");
- if (FAILED(hr)) {
- qWarning() << "failed to add null renderer to graph";
- return false;
- }
-
- hr = m_graphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter,
- m_previewSampleGrabber->filter(),
- m_nullRendererFilter);
- if (FAILED(hr)) {
- qWarning() << "Graph failed to connect filters" << hr;
- return false;
- }
-
- return true;
-}
-
-void DSCameraSession::disconnectGraph()
-{
- // To avoid increasing the memory usage every time the graph is re-connected it's
- // important that all filters are released; also the ones added by the "Intelligent Connect".
- IEnumFilters *enumFilters = nullptr;
- if (SUCCEEDED(m_filterGraph->EnumFilters(&enumFilters))) {
- IBaseFilter *filter = nullptr;
- while (enumFilters->Next(1, &filter, nullptr) == S_OK) {
- m_filterGraph->RemoveFilter(filter);
- enumFilters->Reset();
- filter->Release();
- }
- enumFilters->Release();
- }
-}
-
-static bool qt_frameRateRangeGreaterThan(const QCamera::FrameRateRange &r1, const QCamera::FrameRateRange &r2)
-{
- return r1.maximumFrameRate > r2.maximumFrameRate;
-}
-
-void DSCameraSession::updateSourceCapabilities()
-{
- HRESULT hr;
- AM_MEDIA_TYPE *pmt = nullptr;
- VIDEOINFOHEADER *pvi = nullptr;
- VIDEO_STREAM_CONFIG_CAPS scc;
- IAMStreamConfig* pConfig = nullptr;
-
- m_supportedViewfinderSettings.clear();
- m_needsHorizontalMirroring = false;
- m_supportedFormats.clear();
- m_imageProcessingParametersInfos.clear();
-
- IAMVideoControl *pVideoControl = nullptr;
- hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter, IID_IAMVideoControl,
- reinterpret_cast<void**>(&pVideoControl));
- if (FAILED(hr)) {
- qWarning() << "Failed to get the video control";
- } else if (m_outputPin) {
- long supportedModes;
- hr = pVideoControl->GetCaps(m_outputPin, &supportedModes);
- if (FAILED(hr)) {
- qWarning() << "Failed to get the supported modes of the video control";
- } else if (supportedModes & VideoControlFlag_FlipHorizontal) {
- long mode;
- hr = pVideoControl->GetMode(m_outputPin, &mode);
- if (FAILED(hr))
- qWarning() << "Failed to get the mode of the video control";
- else if (supportedModes & VideoControlFlag_FlipHorizontal)
- m_needsHorizontalMirroring = (mode & VideoControlFlag_FlipHorizontal);
- }
- pVideoControl->Release();
- }
-
- hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
- m_sourceFilter, IID_IAMStreamConfig,
- reinterpret_cast<void**>(&pConfig));
- if (FAILED(hr)) {
- qWarning() << "failed to get config on capture device";
- return;
- }
-
- int iCount;
- int iSize;
- hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
- if (FAILED(hr)) {
- qWarning() << "failed to get capabilities";
- return;
- }
-
- for (int iIndex = 0; iIndex < iCount; ++iIndex) {
- hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast<BYTE*>(&scc));
- if (hr == S_OK) {
- QVideoFrame::PixelFormat pixelFormat = DirectShowMediaType::pixelFormatFromType(pmt);
-
- if (pmt->majortype == MEDIATYPE_Video
- && pmt->formattype == FORMAT_VideoInfo
- && pixelFormat != QVideoFrame::Format_Invalid) {
-
- pvi = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
- QSize resolution(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight);
-
- QList<QCamera::FrameRateRange> frameRateRanges;
-
- if (pVideoControl && m_outputPin) {
- long listSize = 0;
- LONGLONG *frameRates = nullptr;
- SIZE size = { resolution.width(), resolution.height() };
- hr = pVideoControl->GetFrameRateList(m_outputPin, iIndex, size, &listSize, &frameRates);
- if (hr == S_OK && listSize > 0 && frameRates) {
- for (long i = 0; i < listSize; ++i) {
- qreal fr = qreal(10000000) / frameRates[i];
- frameRateRanges.append(QCamera::FrameRateRange(fr, fr));
- }
-
- // Make sure higher frame rates come first
- std::sort(frameRateRanges.begin(), frameRateRanges.end(), qt_frameRateRangeGreaterThan);
- }
-
- CoTaskMemFree(frameRates);
- }
-
- if (frameRateRanges.isEmpty()) {
- frameRateRanges.append(QCamera::FrameRateRange(qreal(10000000) / scc.MaxFrameInterval,
- qreal(10000000) / scc.MinFrameInterval));
- }
-
- for (const QCamera::FrameRateRange &frameRateRange : qAsConst(frameRateRanges)) {
- QCameraViewfinderSettings settings;
- settings.setResolution(resolution);
- settings.setMinimumFrameRate(frameRateRange.minimumFrameRate);
- settings.setMaximumFrameRate(frameRateRange.maximumFrameRate);
- settings.setPixelFormat(pixelFormat);
- settings.setPixelAspectRatio(1, 1);
- m_supportedViewfinderSettings.append(settings);
- m_supportedFormats.append(DirectShowMediaType(*pmt));
- }
- } else {
- OLECHAR *guidString = nullptr;
- StringFromCLSID(pmt->subtype, &guidString);
- if (guidString)
- qWarning() << "Unsupported media type:" << QString::fromWCharArray(guidString);
- ::CoTaskMemFree(guidString);
- }
-
- DirectShowMediaType::deleteType(pmt);
- }
- }
-
- pConfig->Release();
-
- updateImageProcessingParametersInfos();
-}
-
-QList<QSize> DSCameraSession::supportedResolutions(bool *continuous) const
-{
- if (continuous)
- *continuous = false;
-
- QList<QSize> res;
- for (auto &settings : m_supportedViewfinderSettings) {
- auto size = settings.resolution();
- if (!res.contains(size))
- res << size;
- }
-
- std::sort(res.begin(), res.end(), [](const QSize &r1, const QSize &r2) {
- return qlonglong(r1.width()) * r1.height() < qlonglong(r2.width()) * r2.height();
- });
-
- return res;
-}
-
-QT_END_NAMESPACE