diff options
Diffstat (limited to 'src/plugins/winrt/qwinrtcameracontrol.cpp')
-rw-r--r-- | src/plugins/winrt/qwinrtcameracontrol.cpp | 298 |
1 files changed, 169 insertions, 129 deletions
diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp index a28d57219..527dd6e8f 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.cpp +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -47,6 +47,7 @@ #include "qwinrtcameralockscontrol.h" #include <QtCore/qfunctions_winrt.h> +#include <QtCore/QMutex> #include <QtCore/QPointer> #include <QtGui/QGuiApplication> #include <private/qeventdispatcher_winrt_p.h> @@ -227,8 +228,6 @@ public: { Q_ASSERT(m_videoRenderer); - InitializeCriticalSectionEx(&m_mutex, 0, 0); - HRESULT hr; hr = MFCreateEventQueue(&m_eventQueue); Q_ASSERT_SUCCEEDED(hr); @@ -238,9 +237,8 @@ public: ~MediaStream() { - CriticalSectionLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); m_eventQueue->Shutdown(); - DeleteCriticalSection(&m_mutex); } HRESULT RequestSample() @@ -254,30 +252,30 @@ public: HRESULT __stdcall GetEvent(DWORD flags, IMFMediaEvent **event) Q_DECL_OVERRIDE { - EnterCriticalSection(&m_mutex); + QMutexLocker locker(&m_mutex); // Create an extra reference to avoid deadlock ComPtr<IMFMediaEventQueue> eventQueue = m_eventQueue; - LeaveCriticalSection(&m_mutex); + locker.unlock(); return eventQueue->GetEvent(flags, event); } HRESULT __stdcall BeginGetEvent(IMFAsyncCallback *callback, IUnknown *state) Q_DECL_OVERRIDE { - CriticalSectionLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); HRESULT hr = m_eventQueue->BeginGetEvent(callback, state); return hr; } HRESULT __stdcall EndGetEvent(IMFAsyncResult *result, IMFMediaEvent **event) Q_DECL_OVERRIDE { - CriticalSectionLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); return m_eventQueue->EndGetEvent(result, event); } HRESULT __stdcall QueueEvent(MediaEventType eventType, const GUID &extendedType, HRESULT status, const PROPVARIANT *value) Q_DECL_OVERRIDE { - CriticalSectionLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); return m_eventQueue->QueueEventParamVar(eventType, extendedType, status, value); } @@ -372,7 +370,7 @@ public: } private: - CRITICAL_SECTION m_mutex; + QMutex m_mutex; ComPtr<IMFMediaType> m_type; IMFMediaSink *m_sink; ComPtr<IMFMediaEventQueue> m_eventQueue; @@ -560,6 +558,10 @@ public: QPointer<QWinRTCameraLocksControl> cameraLocksControl; QAtomicInt framesMapped; QEventLoop *delayClose; + + bool initializing = false; + bool initialized = false; + HANDLE initializationCompleteEvent; }; QWinRTCameraControl::QWinRTCameraControl(QObject *parent) @@ -584,6 +586,8 @@ QWinRTCameraControl::QWinRTCameraControl(QObject *parent) d->cameraFocusControl = new QWinRTCameraFocusControl(this); d->cameraLocksControl = new QWinRTCameraLocksControl(this); + d->initializationCompleteEvent = CreateEvent(NULL, false, false, NULL); + if (qGuiApp) { connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QWinRTCameraControl::onApplicationStateChanged); @@ -614,8 +618,23 @@ void QWinRTCameraControl::setState(QCamera::State state) case QCamera::ActiveState: { // Capture has not been created or initialized if (d->state == QCamera::UnloadedState) { - hr = initialize(); - RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture"); + if (!d->initialized) { + if (!d->initializing) { + hr = initialize(); + RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture"); + } + DWORD waitResult = WaitForSingleObjectEx(d->initializationCompleteEvent, 30000, FALSE); + if (waitResult != WAIT_OBJECT_0) { + RETURN_VOID_AND_EMIT_ERROR("Failed to initialize camera control."); + return; + } + } + + d->state = QCamera::LoadedState; + emit stateChanged(d->state); + + d->status = QCamera::LoadedStatus; + emit statusChanged(d->status); } Q_ASSERT(d->state == QCamera::LoadedState); @@ -654,8 +673,23 @@ void QWinRTCameraControl::setState(QCamera::State state) case QCamera::LoadedState: { // If moving from unloaded, initialize the camera if (d->state == QCamera::UnloadedState) { - hr = initialize(); - RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture"); + if (!d->initialized) { + if (!d->initializing) { + hr = initialize(); + RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture"); + } + DWORD waitResult = WaitForSingleObjectEx(d->initializationCompleteEvent, 30000, FALSE); + if (waitResult != WAIT_OBJECT_0) { + RETURN_VOID_AND_EMIT_ERROR("Failed to initialize camera control."); + return; + } + } + + d->state = QCamera::LoadedState; + emit stateChanged(d->state); + + d->status = QCamera::LoadedStatus; + emit statusChanged(d->status); } // fall through } @@ -745,6 +779,7 @@ void QWinRTCameraControl::setState(QCamera::State state) d->status = QCamera::UnloadedStatus; emit statusChanged(d->status); } + d->initialized = false; } break; } @@ -884,8 +919,7 @@ HRESULT QWinRTCameraControl::initialize() emit statusChanged(d->status); } - boolean isFocusSupported; - HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d, &isFocusSupported]() { + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { HRESULT hr; ComPtr<IInspectable> capture; hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(), @@ -935,121 +969,11 @@ HRESULT QWinRTCameraControl::initialize() ComPtr<IAsyncAction> op; hr = d->capture->InitializeWithSettingsAsync(settings.Get(), &op); RETURN_HR_IF_FAILED("Failed to begin initialization of media capture manager"); - hr = QWinRTFunctions::await(op, QWinRTFunctions::ProcessThreadEvents); - if (hr == E_ACCESSDENIED) { - qWarning("Access denied when initializing the media capture manager. " - "Check your manifest settings for microphone and webcam access."); - } - RETURN_HR_IF_FAILED("Failed to initialize media capture manager"); - - ComPtr<IVideoDeviceController> videoDeviceController; - hr = d->capture->get_VideoDeviceController(&videoDeviceController); - ComPtr<IAdvancedVideoCaptureDeviceController2> advancedVideoDeviceController; - hr = videoDeviceController.As(&advancedVideoDeviceController); - Q_ASSERT_SUCCEEDED(hr); - hr = advancedVideoDeviceController->get_FocusControl(&d->focusControl); - Q_ASSERT_SUCCEEDED(hr); - - d->cameraFlashControl->initialize(advancedVideoDeviceController); - - hr = d->focusControl->get_Supported(&isFocusSupported); - Q_ASSERT_SUCCEEDED(hr); - if (isFocusSupported) { - hr = advancedVideoDeviceController->get_RegionsOfInterestControl(&d->regionsOfInterestControl); - if (FAILED(hr)) - qCDebug(lcMMCamera) << "Focus supported, but no control for regions of interest available"; - hr = initializeFocus(); - Q_ASSERT_SUCCEEDED(hr); - } - - Q_ASSERT_SUCCEEDED(hr); - ComPtr<IMediaDeviceController> deviceController; - hr = videoDeviceController.As(&deviceController); - Q_ASSERT_SUCCEEDED(hr); - - // Get preview stream properties. - ComPtr<IVectorView<IMediaEncodingProperties *>> previewPropertiesList; - QVector<QSize> previewResolutions; - hr = getMediaStreamResolutions(deviceController.Get(), - MediaStreamType_VideoPreview, - &previewPropertiesList, - &previewResolutions); - RETURN_HR_IF_FAILED("Failed to find a suitable video format"); - - MediaStreamType mediaStreamType = - d->captureMode == QCamera::CaptureVideo ? MediaStreamType_VideoRecord : MediaStreamType_Photo; - - // Get capture stream properties. - ComPtr<IVectorView<IMediaEncodingProperties *>> capturePropertiesList; - QVector<QSize> captureResolutions; - hr = getMediaStreamResolutions(deviceController.Get(), - mediaStreamType, - &capturePropertiesList, - &captureResolutions); - RETURN_HR_IF_FAILED("Failed to find a suitable video format"); - - // Set capture resolutions. - d->imageEncoderControl->setSupportedResolutionsList(captureResolutions.toList()); - const QSize captureResolution = d->imageEncoderControl->imageSettings().resolution(); - const quint32 captureResolutionIndex = captureResolutions.indexOf(captureResolution); - ComPtr<IMediaEncodingProperties> captureProperties; - hr = capturePropertiesList->GetAt(captureResolutionIndex, &captureProperties); - Q_ASSERT_SUCCEEDED(hr); - hr = deviceController->SetMediaStreamPropertiesAsync(mediaStreamType, captureProperties.Get(), &op); - Q_ASSERT_SUCCEEDED(hr); - hr = QWinRTFunctions::await(op); - Q_ASSERT_SUCCEEDED(hr); - - // Set preview resolution. - QVector<QSize> filtered; - const float captureAspectRatio = float(captureResolution.width()) / captureResolution.height(); - for (const QSize &resolution : qAsConst(previewResolutions)) { - const float aspectRatio = float(resolution.width()) / resolution.height(); - if (qAbs(aspectRatio - captureAspectRatio) <= ASPECTRATIO_EPSILON) - filtered.append(resolution); - } - qSort(filtered.begin(), - filtered.end(), - [](QSize size1, QSize size2) { return size1.width() * size1.height() < size2.width() * size2.height(); }); - - const QSize &viewfinderResolution = filtered.first(); - const quint32 viewfinderResolutionIndex = previewResolutions.indexOf(viewfinderResolution); - hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(), - &d->encodingProfile); - Q_ASSERT_SUCCEEDED(hr); - ComPtr<IMediaEncodingProperties> previewProperties; - hr = previewPropertiesList->GetAt(viewfinderResolutionIndex, &previewProperties); - Q_ASSERT_SUCCEEDED(hr); - hr = deviceController->SetMediaStreamPropertiesAsync(MediaStreamType_VideoPreview, previewProperties.Get(), &op); - Q_ASSERT_SUCCEEDED(hr); - hr = QWinRTFunctions::await(op); - Q_ASSERT_SUCCEEDED(hr); - ComPtr<IVideoEncodingProperties> videoPreviewProperties; - hr = previewProperties.As(&videoPreviewProperties); - Q_ASSERT_SUCCEEDED(hr); - hr = d->encodingProfile->put_Video(videoPreviewProperties.Get()); - Q_ASSERT_SUCCEEDED(hr); - - if (d->videoRenderer) - d->videoRenderer->setSize(viewfinderResolution); - + hr = op.Get()->put_Completed(Callback<IAsyncActionCompletedHandler>( + this, &QWinRTCameraControl::onInitializationCompleted).Get()); + RETURN_HR_IF_FAILED("Failed to register initialization callback"); return S_OK; }); - - if (!isFocusSupported) { - d->cameraFocusControl->setSupportedFocusMode(0); - d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); - } - d->cameraLocksControl->initialize(); - - if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) { - d->state = QCamera::LoadedState; - emit stateChanged(d->state); - } - if (SUCCEEDED(hr) && d->status != QCamera::LoadedStatus) { - d->status = QCamera::LoadedStatus; - emit statusChanged(d->status); - } return hr; } @@ -1413,6 +1337,122 @@ HRESULT QWinRTCameraControl::onRecordLimitationExceeded(IMediaCapture *) return S_OK; } +HRESULT QWinRTCameraControl::onInitializationCompleted(IAsyncAction *, AsyncStatus status) +{ + qCDebug(lcMMCamera) << __FUNCTION__; + Q_D(QWinRTCameraControl); + + if (status != Completed) { + d->initializing = false; + d->initialized = true; + return S_OK; + } + + ComPtr<IVideoDeviceController> videoDeviceController; + HRESULT hr = d->capture->get_VideoDeviceController(&videoDeviceController); + ComPtr<IAdvancedVideoCaptureDeviceController2> advancedVideoDeviceController; + hr = videoDeviceController.As(&advancedVideoDeviceController); + Q_ASSERT_SUCCEEDED(hr); + hr = advancedVideoDeviceController->get_FocusControl(&d->focusControl); + Q_ASSERT_SUCCEEDED(hr); + + d->cameraFlashControl->initialize(advancedVideoDeviceController); + + boolean isFocusSupported; + hr = d->focusControl->get_Supported(&isFocusSupported); + Q_ASSERT_SUCCEEDED(hr); + if (isFocusSupported) { + hr = advancedVideoDeviceController->get_RegionsOfInterestControl(&d->regionsOfInterestControl); + if (FAILED(hr)) + qCDebug(lcMMCamera) << "Focus supported, but no control for regions of interest available"; + hr = initializeFocus(); + Q_ASSERT_SUCCEEDED(hr); + } + + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IMediaDeviceController> deviceController; + hr = videoDeviceController.As(&deviceController); + Q_ASSERT_SUCCEEDED(hr); + + // Get preview stream properties. + ComPtr<IVectorView<IMediaEncodingProperties *>> previewPropertiesList; + QVector<QSize> previewResolutions; + hr = getMediaStreamResolutions(deviceController.Get(), + MediaStreamType_VideoPreview, + &previewPropertiesList, + &previewResolutions); + RETURN_HR_IF_FAILED("Failed to find a suitable video format"); + + MediaStreamType mediaStreamType = + d->captureMode == QCamera::CaptureVideo ? MediaStreamType_VideoRecord : MediaStreamType_Photo; + + // Get capture stream properties. + ComPtr<IVectorView<IMediaEncodingProperties *>> capturePropertiesList; + QVector<QSize> captureResolutions; + hr = getMediaStreamResolutions(deviceController.Get(), + mediaStreamType, + &capturePropertiesList, + &captureResolutions); + RETURN_HR_IF_FAILED("Failed to find a suitable video format"); + + // Set capture resolutions. + d->imageEncoderControl->setSupportedResolutionsList(captureResolutions.toList()); + const QSize captureResolution = d->imageEncoderControl->imageSettings().resolution(); + const quint32 captureResolutionIndex = captureResolutions.indexOf(captureResolution); + ComPtr<IMediaEncodingProperties> captureProperties; + hr = capturePropertiesList->GetAt(captureResolutionIndex, &captureProperties); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncAction> op; + hr = deviceController->SetMediaStreamPropertiesAsync(mediaStreamType, captureProperties.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); + + // Set preview resolution. + QVector<QSize> filtered; + const float captureAspectRatio = float(captureResolution.width()) / captureResolution.height(); + for (const QSize &resolution : qAsConst(previewResolutions)) { + const float aspectRatio = float(resolution.width()) / resolution.height(); + if (qAbs(aspectRatio - captureAspectRatio) <= ASPECTRATIO_EPSILON) + filtered.append(resolution); + } + qSort(filtered.begin(), + filtered.end(), + [](QSize size1, QSize size2) { return size1.width() * size1.height() < size2.width() * size2.height(); }); + + const QSize &viewfinderResolution = filtered.first(); + const quint32 viewfinderResolutionIndex = previewResolutions.indexOf(viewfinderResolution); + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(), + &d->encodingProfile); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IMediaEncodingProperties> previewProperties; + hr = previewPropertiesList->GetAt(viewfinderResolutionIndex, &previewProperties); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceController->SetMediaStreamPropertiesAsync(MediaStreamType_VideoPreview, previewProperties.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVideoEncodingProperties> videoPreviewProperties; + hr = previewProperties.As(&videoPreviewProperties); + Q_ASSERT_SUCCEEDED(hr); + hr = d->encodingProfile->put_Video(videoPreviewProperties.Get()); + Q_ASSERT_SUCCEEDED(hr); + + if (d->videoRenderer) + d->videoRenderer->setSize(viewfinderResolution); + + if (!isFocusSupported) { + d->cameraFocusControl->setSupportedFocusMode(0); + d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>()); + } + d->cameraLocksControl->initialize(); + + d->initializing = false; + d->initialized = true; + SetEvent(d->initializationCompleteEvent); + return S_OK; +} + void QWinRTCameraControl::emitError(int errorCode, const QString &errorString) { qCDebug(lcMMCamera) << __FUNCTION__ << errorString << errorCode; |