From 4566ef694b0e3b36069e1861bb5b142a865974da Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 25 Sep 2017 12:24:51 +0200 Subject: Ask for camera permissions Task-number: QTBUG-63308 Change-Id: Ic66d5d50b026350cf03afc19c58fc7c93029a69a Reviewed-by: Maurice Kalinowski --- src/plugins/winrt/qwinrtcameracontrol.cpp | 280 +++++++++++++++++------------- src/plugins/winrt/qwinrtcameracontrol.h | 2 + 2 files changed, 163 insertions(+), 119 deletions(-) diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp index e44f81666..527dd6e8f 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.cpp +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -558,6 +558,10 @@ public: QPointer cameraLocksControl; QAtomicInt framesMapped; QEventLoop *delayClose; + + bool initializing = false; + bool initialized = false; + HANDLE initializationCompleteEvent; }; QWinRTCameraControl::QWinRTCameraControl(QObject *parent) @@ -582,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); @@ -612,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); @@ -652,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 } @@ -743,6 +779,7 @@ void QWinRTCameraControl::setState(QCamera::State state) d->status = QCamera::UnloadedStatus; emit statusChanged(d->status); } + d->initialized = false; } break; } @@ -882,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 capture; hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(), @@ -933,121 +969,11 @@ HRESULT QWinRTCameraControl::initialize() ComPtr 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 videoDeviceController; - hr = d->capture->get_VideoDeviceController(&videoDeviceController); - ComPtr 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 deviceController; - hr = videoDeviceController.As(&deviceController); - Q_ASSERT_SUCCEEDED(hr); - - // Get preview stream properties. - ComPtr> previewPropertiesList; - QVector 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> capturePropertiesList; - QVector 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 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 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 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 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( + 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()); - } - 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; } @@ -1411,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 videoDeviceController; + HRESULT hr = d->capture->get_VideoDeviceController(&videoDeviceController); + ComPtr 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 deviceController; + hr = videoDeviceController.As(&deviceController); + Q_ASSERT_SUCCEEDED(hr); + + // Get preview stream properties. + ComPtr> previewPropertiesList; + QVector 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> capturePropertiesList; + QVector 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 captureProperties; + hr = capturePropertiesList->GetAt(captureResolutionIndex, &captureProperties); + Q_ASSERT_SUCCEEDED(hr); + ComPtr op; + hr = deviceController->SetMediaStreamPropertiesAsync(mediaStreamType, captureProperties.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op); + Q_ASSERT_SUCCEEDED(hr); + + // Set preview resolution. + QVector 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 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 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()); + } + 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; diff --git a/src/plugins/winrt/qwinrtcameracontrol.h b/src/plugins/winrt/qwinrtcameracontrol.h index f978a8b2c..7ef67e957 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.h +++ b/src/plugins/winrt/qwinrtcameracontrol.h @@ -123,6 +123,8 @@ private: HRESULT onCaptureFailed(ABI::Windows::Media::Capture::IMediaCapture *, ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs *); HRESULT onRecordLimitationExceeded(ABI::Windows::Media::Capture::IMediaCapture *); + HRESULT onInitializationCompleted(ABI::Windows::Foundation::IAsyncAction *, + ABI::Windows::Foundation::AsyncStatus); QScopedPointer d_ptr; Q_DECLARE_PRIVATE(QWinRTCameraControl) -- cgit v1.2.1