diff options
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qwindowscamera.cpp')
-rw-r--r-- | src/plugins/multimedia/ffmpeg/qwindowscamera.cpp | 255 |
1 files changed, 105 insertions, 150 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp index b6e031633..d298e2c99 100644 --- a/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp +++ b/src/plugins/multimedia/ffmpeg/qwindowscamera.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2022 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$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwindowscamera_p.h" #include "qsemaphore.h" @@ -43,53 +7,23 @@ #include <private/qmemoryvideobuffer_p.h> #include <private/qwindowsmfdefs_p.h> +#include <private/qwindowsmultimediautils_p.h> +#include <private/qcomobject_p.h> #include <mfapi.h> #include <mfidl.h> -#include <Mferror.h> -#include <Mfreadwrite.h> +#include <mferror.h> +#include <mfreadwrite.h> #include <system_error> QT_BEGIN_NAMESPACE -class CameraReaderCallback : public IMFSourceReaderCallback +using namespace QWindowsMultimediaUtils; + +class CameraReaderCallback : public QComObject<IMFSourceReaderCallback> { public: - CameraReaderCallback() : m_cRef(1) {} - virtual ~CameraReaderCallback() {} - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override - { - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFSourceReaderCallback) { - *ppvObject = static_cast<IMFSourceReaderCallback*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFSourceReaderCallback*>(this)); - } else { - *ppvObject = nullptr; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - STDMETHODIMP_(ULONG) AddRef() override - { - return InterlockedIncrement(&m_cRef); - } - - STDMETHODIMP_(ULONG) Release() override - { - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) { - delete this; - } - return cRef; - } - //from IMFSourceReaderCallback STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample) override; STDMETHODIMP OnFlush(DWORD) override; @@ -101,22 +35,24 @@ public: m_activeCamera = activeCamera; } private: - LONG m_cRef; + // Destructor is not public. Caller should call Release. + ~CameraReaderCallback() override = default; + ActiveCamera *m_activeCamera = nullptr; QMutex m_mutex; }; -static QWindowsIUPointer<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource, - const QWindowsIUPointer<CameraReaderCallback> &callback) +static ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource, + const ComPtr<CameraReaderCallback> &callback) { - QWindowsIUPointer<IMFSourceReader> sourceReader; - QWindowsIUPointer<IMFAttributes> readerAttributes; + ComPtr<IMFSourceReader> sourceReader; + ComPtr<IMFAttributes> readerAttributes; - HRESULT hr = MFCreateAttributes(readerAttributes.address(), 1); + HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1); if (SUCCEEDED(hr)) { - hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.get()); + hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get()); if (SUCCEEDED(hr)) { - hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.get(), sourceReader.address()); + hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf()); if (SUCCEEDED(hr)) return sourceReader; } @@ -126,18 +62,18 @@ static QWindowsIUPointer<IMFSourceReader> createCameraReader(IMFMediaSource *med return sourceReader; } -static QWindowsIUPointer<IMFMediaSource> createCameraSource(const QString &deviceId) +static ComPtr<IMFMediaSource> createCameraSource(const QString &deviceId) { - QWindowsIUPointer<IMFMediaSource> mediaSource; - QWindowsIUPointer<IMFAttributes> sourceAttributes; - HRESULT hr = MFCreateAttributes(sourceAttributes.address(), 2); + ComPtr<IMFMediaSource> mediaSource; + ComPtr<IMFAttributes> sourceAttributes; + HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2); if (SUCCEEDED(hr)) { hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); if (SUCCEEDED(hr)) { hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, reinterpret_cast<LPCWSTR>(deviceId.utf16())); if (SUCCEEDED(hr)) { - hr = MFCreateDeviceSource(sourceAttributes.get(), mediaSource.address()); + hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf()); if (SUCCEEDED(hr)) return mediaSource; } @@ -147,53 +83,68 @@ static QWindowsIUPointer<IMFMediaSource> createCameraSource(const QString &devic return mediaSource; } -static int calculateVideoFrameStride(IMFSourceReader *sourceReader, qsizetype formatIndex, int width) +static int calculateVideoFrameStride(IMFMediaType *videoType, int width) { - Q_ASSERT(sourceReader); - QWindowsIUPointer<IMFMediaType> videoType; - HRESULT hr = sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, - formatIndex, videoType.address()); + Q_ASSERT(videoType); + + GUID subtype = GUID_NULL; + HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype); if (SUCCEEDED(hr)) { - GUID subtype = GUID_NULL; - hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype); - if (SUCCEEDED(hr)) { - LONG stride = 0; - hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride); - if (SUCCEEDED(hr)) - return int(stride); - } + LONG stride = 0; + hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride); + if (SUCCEEDED(hr)) + return int(qAbs(stride)); } - qWarning() << "Failed to calculate video stride" << hr; + qWarning() << "Failed to calculate video stride" << errorString(hr); return 0; } -static bool setCameraReaderFormat(IMFSourceReader *sourceReader, qsizetype formatIndex) +static bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType) { Q_ASSERT(sourceReader); + Q_ASSERT(videoType); - QWindowsIUPointer<IMFMediaType> videoType; - HRESULT hr = sourceReader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, - formatIndex, videoType.address()); - if (SUCCEEDED(hr)) { - hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, - nullptr, videoType.get()); - if (SUCCEEDED(hr)) { - return true; - } else { - qWarning() << "Failed to set video format" << hr << std::system_category().message(hr).c_str(); - } - } else { - qWarning() << "Failed to select video format at index" << formatIndex << hr << std::system_category().message(hr).c_str(); - } + HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, + videoType); + if (FAILED(hr)) + qWarning() << "Failed to set video format" << errorString(hr); - return false; + return SUCCEEDED(hr); +} + +static ComPtr<IMFMediaType> findVideoType(IMFSourceReader *reader, + const QCameraFormat &format) +{ + for (DWORD i = 0;; ++i) { + ComPtr<IMFMediaType> candidate; + HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i, + candidate.GetAddressOf()); + if (FAILED(hr)) + break; + + GUID subtype = GUID_NULL; + if (FAILED(candidate->GetGUID(MF_MT_SUBTYPE, &subtype))) + continue; + + if (format.pixelFormat() != pixelFormatFromMediaSubtype(subtype)) + continue; + + UINT32 width = 0u; + UINT32 height = 0u; + if (FAILED(MFGetAttributeSize(candidate.Get(), MF_MT_FRAME_SIZE, &width, &height))) + continue; + + if (format.resolution() != QSize{ int(width), int(height) }) + continue; + + return candidate; + } + return {}; } class ActiveCamera { public: - ActiveCamera() = delete; - static std::unique_ptr<ActiveCamera> create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format) { auto ac = std::unique_ptr<ActiveCamera>(new ActiveCamera(wc)); @@ -201,48 +152,47 @@ public: if (!ac->m_source) return {}; - ac->m_readerCallback = QWindowsIUPointer<CameraReaderCallback>(new CameraReaderCallback); + ac->m_readerCallback = makeComObject<CameraReaderCallback>(); ac->m_readerCallback->setActiveCamera(ac.get()); - ac->m_reader = createCameraReader(ac->m_source.get(), ac->m_readerCallback); + ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback); if (!ac->m_reader) return {}; - qsizetype index = device.videoFormats().indexOf(format); - if (index < 0) - return {}; - - if (!ac->setFormat(format, index)) + if (!ac->setFormat(format)) return {}; return ac; } - bool setFormat(const QCameraFormat &format, int formatIndex) + bool setFormat(const QCameraFormat &format) { - m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM); - m_flushWait.acquire(); - - if (!setCameraReaderFormat(m_reader.get(), formatIndex)) - return false; - - m_frameFormat = { format.resolution(), format.pixelFormat() }; - m_videoFrameStride = calculateVideoFrameStride(m_reader.get(), formatIndex, format.resolution().width()); + flush(); + + auto videoType = findVideoType(m_reader.Get(), format); + if (videoType) { + if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) { + m_frameFormat = { format.resolution(), format.pixelFormat() }; + m_videoFrameStride = + calculateVideoFrameStride(videoType.Get(), format.resolution().width()); + } + } - m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr, - nullptr, nullptr, nullptr); + m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr, nullptr, nullptr, + nullptr); return true; } void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample) { if (FAILED(status)) { - emit m_windowsCamera.error(int(status), std::system_category().message(status).c_str()); + const std::string msg{ std::system_category().message(status) }; + m_windowsCamera.updateError(QCamera::CameraError, QString::fromStdString(msg)); return; } if (sample) { - QWindowsIUPointer<IMFMediaBuffer> mediaBuffer; - if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.address()))) { + ComPtr<IMFMediaBuffer> mediaBuffer; + if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) { DWORD bufLen = 0; BYTE *buffer = nullptr; @@ -274,21 +224,27 @@ public: ~ActiveCamera() { - m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM); - m_flushWait.acquire(); + flush(); m_readerCallback->setActiveCamera(nullptr); } private: explicit ActiveCamera(QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) {}; + void flush() + { + if (SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) { + m_flushWait.acquire(); + } + } + QWindowsCamera &m_windowsCamera; QSemaphore m_flushWait; - QWindowsIUPointer<IMFMediaSource> m_source; - QWindowsIUPointer<IMFSourceReader> m_reader; - QWindowsIUPointer<CameraReaderCallback> m_readerCallback; + ComPtr<IMFMediaSource> m_source; + ComPtr<IMFSourceReader> m_reader; + ComPtr<CameraReaderCallback> m_readerCallback; QVideoFrameFormat m_frameFormat; int m_videoFrameStride = 0; @@ -339,8 +295,8 @@ void QWindowsCamera::setActive(bool active) activeChanged(true); } else { - emit activeChanged(false); m_active.reset(); + emit activeChanged(false); } } @@ -357,11 +313,10 @@ void QWindowsCamera::setCamera(const QCameraDevice &camera) bool QWindowsCamera::setCameraFormat(const QCameraFormat &format) { - qsizetype index = m_cameraDevice.videoFormats().indexOf(format); - if (index < 0) + if (format.isNull()) return false; - bool ok = m_active ? m_active->setFormat(format, index) : true; + bool ok = m_active ? m_active->setFormat(format) : true; if (ok) m_cameraFormat = format; |