8#include <private/qcameradevice_p.h>
9#include <private/qmemoryvideobuffer_p.h>
10#include <private/qwindowsmultimediautils_p.h>
11#include <private/qvideoframe_p.h>
12#include <private/qcomobject_p.h>
13#include <private/qwmf_support_p.h>
15#include <QtCore/private/qsystemerror_p.h>
20#include <mfreadwrite.h>
22#include <system_error>
26using namespace QWindowsMultimediaUtils;
32class CameraReaderCallback :
public QComObject<IMFSourceReaderCallback>
36 STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
override;
37 STDMETHODIMP OnFlush(DWORD)
override;
38 STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *)
override {
return S_OK; }
43 m_activeCamera = activeCamera;
47 ~CameraReaderCallback() override =
default;
53ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
54 const ComPtr<CameraReaderCallback> &callback)
56 ComPtr<IMFSourceReader> sourceReader;
57 ComPtr<IMFAttributes> readerAttributes;
59 HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
61 hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
63 hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
69 qWarning() <<
"Failed to create camera IMFSourceReader" << hr;
73ComPtr<IMFMediaSource> createCameraSource(
const QString &deviceId)
75 ComPtr<IMFMediaSource> mediaSource;
76 ComPtr<IMFAttributes> sourceAttributes;
77 HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2);
79 hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
80 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
82 hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
83 reinterpret_cast<LPCWSTR>(deviceId.utf16()));
85 hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf());
91 qWarning() <<
"Failed to create camera IMFMediaSource" << hr;
95int calculateVideoFrameStride(IMFMediaType *videoType,
int width)
99 GUID subtype = GUID_NULL;
100 HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
103 hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
105 return int(qAbs(stride));
108 qWarning() <<
"Failed to calculate video stride" << QSystemError::windowsComString(hr);
112bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
114 Q_ASSERT(sourceReader);
117 HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
nullptr,
120 qWarning() <<
"Failed to set video format" << QSystemError::windowsComString(hr);
122 return SUCCEEDED(hr);
125ComPtr<IMFMediaType> findVideoType(IMFSourceReader *reader,
const QCameraFormat &format)
127 for (DWORD i = 0;; ++i) {
128 ComPtr<IMFMediaType> candidate;
129 HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
130 candidate.GetAddressOf());
134 GUID subtype = GUID_NULL;
135 if (FAILED(candidate->GetGUID(MF_MT_SUBTYPE, &subtype)))
138 if (format.pixelFormat() != pixelFormatFromMediaSubtype(subtype))
143 if (FAILED(MFGetAttributeSize(candidate.Get(), MF_MT_FRAME_SIZE, &width, &height)))
146 if (format.resolution() != QSize{
int(width),
int(height) })
161 auto ac = std::unique_ptr<ActiveCamera>(
new ActiveCamera(wc));
162 ac->m_source = createCameraSource(QString::fromUtf8(device.id()));
166 ac->m_readerCallback = makeComObject<CameraReaderCallback>();
167 ac->m_readerCallback->setActiveCamera(ac.get());
168 ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
172 if (!ac->setFormat(format))
182 auto videoType = findVideoType(m_reader.Get(), format);
184 if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) {
185 m_frameFormat = { format.resolution(), format.pixelFormat() };
186 m_frameFormat.setColorRange(QCameraFormatPrivate::getColorRange(format));
187 m_frameFormat.setColorSpace(QCameraFormatPrivate::getColorSpace(format));
189 calculateVideoFrameStride(videoType.Get(), format.resolution().width());
193 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
nullptr,
nullptr,
198 void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
200 if (FAILED(status)) {
201 const std::string msg{
std::system_category().message(status) };
202 m_windowsCamera.updateError(QCamera::CameraError, QString::fromStdString(msg));
207 ComPtr<IMFMediaBuffer> mediaBuffer;
208 if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
210 auto result = QWMF::withLockedBuffer(mediaBuffer,
211 [&](QSpan<BYTE> data, QSpan<BYTE> ) {
212 QByteArray bytes(data);
213 auto buffer = std::make_unique<QMemoryVideoBuffer>(std::move(bytes),
216 QVideoFramePrivate::createFrame(std::move(buffer), m_frameFormat);
219 frame.setStartTime(timestamp / 10);
221 LONGLONG duration = -1;
222 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
223 frame.setEndTime((timestamp + duration) / 10);
229 emit m_windowsCamera.newVideoFrame(result.value());
233 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
234 nullptr,
nullptr,
nullptr);
239 m_flushWait.release();
245 if (m_readerCallback)
246 m_readerCallback->setActiveCamera(
nullptr);
250 explicit ActiveCamera(
QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) { }
254 if (m_reader && SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) {
255 m_flushWait.acquire();
261 QSemaphore m_flushWait;
263 ComPtr<IMFMediaSource> m_source;
264 ComPtr<IMFSourceReader> m_reader;
265 ComPtr<CameraReaderCallback> m_readerCallback;
267 QVideoFrameFormat m_frameFormat;
268 int m_videoFrameStride = 0;
271STDMETHODIMP CameraReaderCallback::OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
275 m_activeCamera->onReadSample(status, timestamp, sample);
280STDMETHODIMP CameraReaderCallback::OnFlush(DWORD)
291 m_cameraDevice = camera ? camera->cameraDevice() : QCameraDevice{};
301 if (
bool(m_active) == active)
305 if (m_cameraDevice.isNull())
308 if (m_cameraFormat.isNull())
309 m_cameraFormat = findBestCameraFormat(m_cameraDevice);
311 m_active = ActiveCamera::create(*
this, m_cameraDevice, m_cameraFormat);
317 emit activeChanged(
false);
323 bool active =
bool(m_active);
326 m_cameraDevice = camera;
337 bool ok = m_active ? m_active->setFormat(format) :
true;
339 m_cameraFormat = format;
void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
bool setFormat(const QCameraFormat &format)
static std::unique_ptr< ActiveCamera > create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format)
bool setCameraFormat(const QCameraFormat &) override
void setCamera(const QCameraDevice &camera) override
QWindowsCamera(QCamera *parent)
void setActive(bool active) override
~QWindowsCamera() override
std::conditional_t< QT_FFMPEG_AVIO_WRITE_CONST, const uint8_t *, uint8_t * > AvioWriteBufferType