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>
30 const QCameraFormat &requested)
32 qsizetype closest = -1;
33 std::pair<
float,
float> closestDistance{};
35 for (qsizetype i = 0; i != candidates.size(); ++i) {
36 const QCameraFormat &candidate = candidates[i];
37 if (candidate.pixelFormat() != requested.pixelFormat()
38 || candidate.resolution() != requested.resolution()) {
42 const std::pair distance{ qAbs(candidate.maxFrameRate() - requested.maxFrameRate()),
43 qAbs(candidate.minFrameRate() - requested.minFrameRate()) };
44 if (closest < 0 || distance < closestDistance) {
46 closestDistance = distance;
55class CameraReaderCallback :
public QComObject<IMFSourceReaderCallback>
59 STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
override;
60 STDMETHODIMP OnFlush(DWORD)
override;
61 STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *)
override {
return S_OK; }
66 m_activeCamera = activeCamera;
70 ~CameraReaderCallback() override =
default;
76ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
77 const ComPtr<CameraReaderCallback> &callback)
79 ComPtr<IMFSourceReader> sourceReader;
80 ComPtr<IMFAttributes> readerAttributes;
82 HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
84 hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
86 hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
92 qWarning() <<
"Failed to create camera IMFSourceReader" << hr;
96ComPtr<IMFMediaSource> createCameraSource(
const QString &deviceId)
98 ComPtr<IMFMediaSource> mediaSource;
99 ComPtr<IMFAttributes> sourceAttributes;
100 HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2);
102 hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
103 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
105 hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
106 reinterpret_cast<LPCWSTR>(deviceId.utf16()));
108 hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf());
114 qWarning() <<
"Failed to create camera IMFMediaSource" << hr;
118int calculateVideoFrameStride(IMFMediaType *videoType,
int width)
122 GUID subtype = GUID_NULL;
123 HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
126 hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
128 return int(qAbs(stride));
131 qWarning() <<
"Failed to calculate video stride" << QSystemError::windowsComString(hr);
135bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
137 Q_ASSERT(sourceReader);
140 HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
nullptr,
143 qWarning() <<
"Failed to set video format" << QSystemError::windowsComString(hr);
145 return SUCCEEDED(hr);
148ComPtr<IMFMediaType> findVideoType(IMFSourceReader *reader,
const QCameraFormat &format)
150 std::vector<ComPtr<IMFMediaType>> mediaTypes;
151 std::vector<QCameraFormat> candidates;
153 for (DWORD i = 0;; ++i) {
154 ComPtr<IMFMediaType> mediaType;
155 HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
156 mediaType.GetAddressOf());
160 const std::optional<QCameraFormat> candidate =
161 QWMF::cameraFormatFromMediaType(mediaType.Get());
165 mediaTypes.push_back(std::move(mediaType));
166 candidates.push_back(std::move(*candidate));
169 const qsizetype index = indexOfClosestCameraFormat(candidates, format);
173 return mediaTypes.at(index);
183 auto ac = std::unique_ptr<ActiveCamera>(
new ActiveCamera(wc));
184 ac->m_source = createCameraSource(QString::fromUtf8(device.id()));
188 ac->m_readerCallback = makeComObject<CameraReaderCallback>();
189 ac->m_readerCallback->setActiveCamera(ac.get());
190 ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
194 if (!ac->setFormat(format))
204 auto videoType = findVideoType(m_reader.Get(), format);
206 if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) {
207 m_frameFormat = { format.resolution(), format.pixelFormat() };
208 m_frameFormat.setColorRange(QCameraFormatPrivate::getColorRange(format));
209 m_frameFormat.setColorSpace(QCameraFormatPrivate::getColorSpace(format));
211 calculateVideoFrameStride(videoType.Get(), format.resolution().width());
215 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
nullptr,
nullptr,
220 void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
222 if (FAILED(status)) {
223 const std::string msg{
std::system_category().message(status) };
224 m_windowsCamera.updateError(QCamera::CameraError, QString::fromStdString(msg));
229 ComPtr<IMFMediaBuffer> mediaBuffer;
230 if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
232 auto result = QWMF::withLockedBuffer(mediaBuffer,
233 [&](QSpan<BYTE> data, QSpan<BYTE> ) {
234 QByteArray bytes(data);
235 auto buffer = std::make_unique<QMemoryVideoBuffer>(std::move(bytes),
238 QVideoFramePrivate::createFrame(std::move(buffer), m_frameFormat);
241 frame.setStartTime(timestamp / 10);
243 LONGLONG duration = -1;
244 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
245 frame.setEndTime((timestamp + duration) / 10);
251 emit m_windowsCamera.newVideoFrame(result.value());
255 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
256 nullptr,
nullptr,
nullptr);
261 m_flushWait.release();
267 if (m_readerCallback)
268 m_readerCallback->setActiveCamera(
nullptr);
272 explicit ActiveCamera(
QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) { }
276 if (m_reader && SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) {
277 m_flushWait.acquire();
283 QSemaphore m_flushWait;
285 ComPtr<IMFMediaSource> m_source;
286 ComPtr<IMFSourceReader> m_reader;
287 ComPtr<CameraReaderCallback> m_readerCallback;
289 QVideoFrameFormat m_frameFormat;
290 int m_videoFrameStride = 0;
293STDMETHODIMP CameraReaderCallback::OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
297 m_activeCamera->onReadSample(status, timestamp, sample);
302STDMETHODIMP CameraReaderCallback::OnFlush(DWORD)
313 m_cameraDevice = camera ? camera->cameraDevice() : QCameraDevice{};
323 if (
bool(m_active) == active)
327 if (m_cameraDevice.isNull())
330 if (m_cameraFormat.isNull())
331 m_cameraFormat = findBestCameraFormat(m_cameraDevice);
333 m_active = ActiveCamera::create(*
this, m_cameraDevice, m_cameraFormat);
339 emit activeChanged(
false);
345 bool active =
bool(m_active);
348 m_cameraDevice = camera;
359 bool ok = m_active ? m_active->setFormat(format) :
true;
361 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
QT_MANGLE_NAMESPACE(QMacScreenCaptureStreamDelegate) QMacScreenCaptureStreamDelegate
qsizetype indexOfClosestCameraFormat(QSpan< const QCameraFormat > candidates, const QCameraFormat &requested)