/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). ** 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 "qwinrtcameraimagecapturecontrol.h" #include "qwinrtcameracontrol.h" #include "qwinrtimageencodercontrol.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Media::Capture; using namespace ABI::Windows::Media::Devices; using namespace ABI::Windows::Media::MediaProperties; using namespace ABI::Windows::Storage::Streams; using namespace ABI::Windows::Graphics::Imaging; QT_BEGIN_NAMESPACE #define wchar(str) reinterpret_cast(str.utf16()) struct QWinRTCameraImageCaptureControlGlobal { QWinRTCameraImageCaptureControlGlobal() { HRESULT hr; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_ImageEncodingProperties).Get(), &encodingPropertiesFactory); Q_ASSERT_SUCCEEDED(hr); hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory); Q_ASSERT_SUCCEEDED(hr); hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), &dataReaderFactory); } ComPtr encodingPropertiesFactory; ComPtr bufferFactory; ComPtr dataReaderFactory; }; Q_GLOBAL_STATIC(QWinRTCameraImageCaptureControlGlobal, g) struct CaptureRequest { quint16 id; QString fileName; ComPtr imageFormat; ComPtr stream; ComPtr op; }; // Do not use CoTaskMemFree directly for image cleanup as it leads to crashes in release static void freeImageData(void *data) { CoTaskMemFree(data); } class QWinRTCameraImageCaptureControlPrivate { public: QWinRTCameraImageCaptureControlPrivate() : isActive(false) { } QPointer cameraControl; QHash requests; quint16 currentCaptureId; QMediaStorageLocation location; bool isActive; }; QWinRTCameraImageCaptureControl::QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent) : QCameraImageCaptureControl(parent), d_ptr(new QWinRTCameraImageCaptureControlPrivate) { qCDebug(lcMMCamera) << __FUNCTION__ << parent; Q_D(QWinRTCameraImageCaptureControl); d->cameraControl = parent; connect(d->cameraControl, &QCameraControl::stateChanged, this, &QWinRTCameraImageCaptureControl::onCameraStateChanged); d->currentCaptureId = 0; } bool QWinRTCameraImageCaptureControl::isReadyForCapture() const { Q_D(const QWinRTCameraImageCaptureControl); return d->isActive; } QCameraImageCapture::DriveMode QWinRTCameraImageCaptureControl::driveMode() const { return QCameraImageCapture::SingleImageCapture; } void QWinRTCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode) { Q_UNUSED(mode); } int QWinRTCameraImageCaptureControl::capture(const QString &fileName) { qCDebug(lcMMCamera) << __FUNCTION__ << fileName; Q_D(QWinRTCameraImageCaptureControl); ++d->currentCaptureId; ComPtr capture = d->cameraControl->handle(); if (!capture) { emit error(d->currentCaptureId, QCameraImageCapture::NotReadyError, tr("Camera not ready")); return -1; } CaptureRequest request = { d->currentCaptureId, d->location.generateFileName(fileName, QMediaStorageLocation::Pictures, QStringLiteral("IMG_"), fileName.isEmpty() ? QStringLiteral("jpg") : QFileInfo(fileName).suffix()), nullptr, nullptr, nullptr }; HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d, capture, &request]() { HRESULT hr; hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &request.stream); Q_ASSERT_SUCCEEDED(hr); hr = g->encodingPropertiesFactory->CreateBmp(&request.imageFormat); Q_ASSERT_SUCCEEDED(hr); const QSize imageSize = static_cast(d->cameraControl->imageEncoderControl())->imageSettings().resolution(); hr = request.imageFormat->put_Width(UINT32(imageSize.width())); Q_ASSERT_SUCCEEDED(hr); hr = request.imageFormat->put_Height(UINT32(imageSize.height())); Q_ASSERT_SUCCEEDED(hr); hr = capture->CapturePhotoToStreamAsync(request.imageFormat.Get(), request.stream.Get(), &request.op); Q_ASSERT_SUCCEEDED(hr); if (!request.op) { qErrnoWarning("Camera photo capture failed."); return E_FAIL; } emit captureQueueChanged(false); d->requests.insert(request.op.Get(), request); hr = request.op->put_Completed(Callback( this, &QWinRTCameraImageCaptureControl::onCaptureCompleted).Get()); Q_ASSERT_SUCCEEDED(hr); return hr; }); if (FAILED(hr)) return -1; return request.id; } void QWinRTCameraImageCaptureControl::cancelCapture() { qCDebug(lcMMCamera) << __FUNCTION__; Q_D(QWinRTCameraImageCaptureControl); QHash::iterator it = d->requests.begin(); while (it != d->requests.end()) { ComPtr info; it->op.As(&info); info->Cancel(); it = d->requests.erase(it); } emit captureQueueChanged(true); } void QWinRTCameraImageCaptureControl::onCameraStateChanged(QCamera::State state) { Q_D(QWinRTCameraImageCaptureControl); const bool newActive = state == QCamera::ActiveState; if (d->isActive == newActive) return; d->isActive = newActive; emit readyForCaptureChanged(newActive); } HRESULT QWinRTCameraImageCaptureControl::onCaptureCompleted(IAsyncAction *asyncInfo, AsyncStatus status) { qCDebug(lcMMCamera) << __FUNCTION__; Q_D(QWinRTCameraImageCaptureControl); if (status == Canceled || !d->requests.contains(asyncInfo)) return S_OK; CaptureRequest request = d->requests.take(asyncInfo); emit captureQueueChanged(d->requests.isEmpty()); HRESULT hr; if (status == Error) { hr = asyncInfo->GetResults(); emit error(request.id, QCameraImageCapture::ResourceError, qt_error_string(hr)); return S_OK; } quint64 dataLength; hr = request.stream->get_Size(&dataLength); Q_ASSERT_SUCCEEDED(hr); if (dataLength == 0 || dataLength > INT_MAX) { emit error(request.id, QCameraImageCapture::FormatError, tr("Invalid photo data length.")); return S_OK; } ComPtr bitmapFactory; hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapDecoder).Get(), &bitmapFactory); Q_ASSERT_SUCCEEDED(hr); ComPtr> op; hr = bitmapFactory->CreateAsync(request.stream.Get(), &op); Q_ASSERT_SUCCEEDED(hr); ComPtr decoder; hr = QWinRTFunctions::await(op, decoder.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); ComPtr> op2; hr = decoder->GetFrameAsync(0, &op2); Q_ASSERT_SUCCEEDED(hr); ComPtr frame; hr = QWinRTFunctions::await(op2, frame.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); ComPtr transform; hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapTransform).Get(), &transform); Q_ASSERT_SUCCEEDED(hr); ComPtr> op3; hr = frame->GetPixelDataTransformedAsync(BitmapPixelFormat_Rgba8, BitmapAlphaMode_Straight, transform.Get(), ExifOrientationMode_IgnoreExifOrientation, ColorManagementMode_DoNotColorManage, &op3); Q_ASSERT_SUCCEEDED(hr); ComPtr pixelDataProvider; hr = QWinRTFunctions::await(op3, pixelDataProvider.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); UINT32 pixelDataSize; BYTE *pixelData; hr = pixelDataProvider->DetachPixelData(&pixelDataSize, &pixelData); UINT32 pixelHeight; hr = frame->get_PixelHeight(&pixelHeight); Q_ASSERT_SUCCEEDED(hr); UINT32 pixelWidth; hr = frame->get_PixelWidth(&pixelWidth); Q_ASSERT_SUCCEEDED(hr); const QImage image(pixelData, int(pixelWidth), int(pixelHeight), QImage::Format_RGBA8888, reinterpret_cast(&freeImageData), pixelData); emit imageCaptured(request.id, image); QWinRTImageEncoderControl *imageEncoderControl = static_cast(d->cameraControl->imageEncoderControl()); int imageQuality = 100; switch (imageEncoderControl->imageSettings().quality()) { case QMultimedia::VeryLowQuality: imageQuality = 20; break; case QMultimedia::LowQuality: imageQuality = 40; break; case QMultimedia::NormalQuality: imageQuality = 60; break; case QMultimedia::HighQuality: imageQuality = 80; break; case QMultimedia::VeryHighQuality: imageQuality = 100; break; } if (image.save(request.fileName, imageEncoderControl->imageSettings().codec().toLatin1().data(), imageQuality)) emit imageSaved(request.id, request.fileName); else emit error(request.id, QCameraImageCapture::ResourceError, tr("Image saving failed")); return S_OK; } QT_END_NAMESPACE