diff options
Diffstat (limited to 'src/plugins/directshow/camera/dscamerasession.cpp')
-rw-r--r-- | src/plugins/directshow/camera/dscamerasession.cpp | 1177 |
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, ¶m1, ¶m2, 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 |