8#include <private/qmemoryvideobuffer_p.h>
9#include <private/qwindowsmultimediautils_p.h>
10#include <private/qvideoframe_p.h>
11#include <private/qcomobject_p.h>
12#include <private/qwmf_support_p.h>
14#include <QtCore/private/qsystemerror_p.h>
19#include <mfreadwrite.h>
21#include <system_error>
25using namespace QWindowsMultimediaUtils;
38 m_activeCamera = activeCamera;
49 const ComPtr<CameraReaderCallback> &callback)
51 ComPtr<IMFSourceReader> sourceReader;
52 ComPtr<IMFAttributes> readerAttributes;
54 HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
56 hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
58 hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
64 qWarning() <<
"Failed to create camera IMFSourceReader" << hr;
70 ComPtr<IMFMediaSource> mediaSource;
71 ComPtr<IMFAttributes> sourceAttributes;
72 HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2);
74 hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
75 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
77 hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
78 reinterpret_cast<LPCWSTR>(deviceId.utf16()));
80 hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf());
86 qWarning() <<
"Failed to create camera IMFMediaSource" << hr;
94 GUID subtype = GUID_NULL;
95 HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
98 hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
100 return int(qAbs(stride));
103 qWarning() <<
"Failed to calculate video stride" << QSystemError::windowsComString(hr);
109 Q_ASSERT(sourceReader);
112 HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
nullptr,
115 qWarning() <<
"Failed to set video format" << QSystemError::windowsComString(hr);
117 return SUCCEEDED(hr);
121 const QCameraFormat &format)
123 for (DWORD i = 0;; ++i) {
124 ComPtr<IMFMediaType> candidate;
125 HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
126 candidate.GetAddressOf());
130 GUID subtype = GUID_NULL;
131 if (FAILED(candidate->GetGUID(MF_MT_SUBTYPE, &subtype)))
134 if (format.pixelFormat() != pixelFormatFromMediaSubtype(subtype))
139 if (FAILED(MFGetAttributeSize(candidate.Get(), MF_MT_FRAME_SIZE, &width, &height)))
142 if (format.resolution() != QSize{
int(width),
int(height) })
154 auto ac = std::unique_ptr<ActiveCamera>(
new ActiveCamera(wc));
155 ac->m_source = createCameraSource(QString::fromUtf8(device.id()));
160 ac->m_readerCallback->setActiveCamera(ac.get());
161 ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
165 if (!ac->setFormat(format))
175 auto videoType = findVideoType(m_reader.Get(), format);
177 if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) {
178 m_frameFormat = { format.resolution(), format.pixelFormat() };
180 calculateVideoFrameStride(videoType.Get(), format.resolution().width());
184 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
nullptr,
nullptr,
189 void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
191 if (FAILED(status)) {
192 const std::string msg{
std::system_category().message(status) };
193 m_windowsCamera.updateError(QCamera::CameraError, QString::fromStdString(msg));
198 ComPtr<IMFMediaBuffer> mediaBuffer;
199 if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
201 auto result = QWMF::withLockedBuffer(mediaBuffer,
202 [&](QSpan<BYTE> data, QSpan<BYTE> ) {
203 QByteArray bytes(data);
204 auto buffer = std::make_unique<QMemoryVideoBuffer>(std::move(bytes),
207 QVideoFramePrivate::createFrame(std::move(buffer), m_frameFormat);
210 frame.setStartTime(timestamp / 10);
212 LONGLONG duration = -1;
213 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
214 frame.setEndTime((timestamp + duration) / 10);
220 emit m_windowsCamera.newVideoFrame(result.value());
224 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
225 nullptr,
nullptr,
nullptr);
230 m_flushWait.release();
236 if (m_readerCallback)
237 m_readerCallback->setActiveCamera(
nullptr);
241 explicit ActiveCamera(
QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) {};
245 if (m_reader && SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) {
246 m_flushWait.acquire();
252 QSemaphore m_flushWait;
254 ComPtr<IMFMediaSource> m_source;
255 ComPtr<IMFSourceReader> m_reader;
256 ComPtr<CameraReaderCallback> m_readerCallback;
258 QVideoFrameFormat m_frameFormat;
259 int m_videoFrameStride = 0;
266 m_activeCamera->onReadSample(status, timestamp, sample);
280 : QPlatformCamera(camera)
282 m_cameraDevice = camera ? camera->cameraDevice() : QCameraDevice{};
292 if (
bool(m_active) == active)
296 if (m_cameraDevice.isNull())
299 if (m_cameraFormat.isNull())
300 m_cameraFormat = findBestCameraFormat(m_cameraDevice);
302 m_active = ActiveCamera::create(*
this, m_cameraDevice, m_cameraFormat);
308 emit activeChanged(
false);
314 bool active =
bool(m_active);
317 m_cameraDevice = camera;
328 bool ok = m_active ? m_active->setFormat(format) :
true;
330 m_cameraFormat = format;
bool setFormat(const QCameraFormat &format)
static std::unique_ptr< ActiveCamera > create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format)
void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample) override
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override
STDMETHODIMP OnFlush(DWORD) override
void setActiveCamera(ActiveCamera *activeCamera)
~CameraReaderCallback() override=default
bool setCameraFormat(const QCameraFormat &) override
void setCamera(const QCameraDevice &camera) override
void setActive(bool active) override
~QWindowsCamera() override
static int calculateVideoFrameStride(IMFMediaType *videoType, int width)
static bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
static ComPtr< IMFSourceReader > createCameraReader(IMFMediaSource *mediaSource, const ComPtr< CameraReaderCallback > &callback)
static ComPtr< IMFMediaType > findVideoType(IMFSourceReader *reader, const QCameraFormat &format)
static ComPtr< IMFMediaSource > createCameraSource(const QString &deviceId)