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;
31class CameraReaderCallback :
public QComObject<IMFSourceReaderCallback>
35 STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
override;
36 STDMETHODIMP OnFlush(DWORD)
override;
37 STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *)
override {
return S_OK; }
42 m_activeCamera = activeCamera;
46 ~CameraReaderCallback() override =
default;
52ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
53 const ComPtr<CameraReaderCallback> &callback)
55 ComPtr<IMFSourceReader> sourceReader;
56 ComPtr<IMFAttributes> readerAttributes;
58 HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
60 hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
62 hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
68 qWarning() <<
"Failed to create camera IMFSourceReader" << hr;
72ComPtr<IMFMediaSource> createCameraSource(
const QString &deviceId)
74 ComPtr<IMFMediaSource> mediaSource;
75 ComPtr<IMFAttributes> sourceAttributes;
76 HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2);
78 hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
79 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
81 hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
82 reinterpret_cast<LPCWSTR>(deviceId.utf16()));
84 hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf());
90 qWarning() <<
"Failed to create camera IMFMediaSource" << hr;
94int calculateVideoFrameStride(IMFMediaType *videoType,
int width)
98 GUID subtype = GUID_NULL;
99 HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
102 hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
104 return int(qAbs(stride));
107 qWarning() <<
"Failed to calculate video stride" << QSystemError::windowsComString(hr);
111bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
113 Q_ASSERT(sourceReader);
116 HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
nullptr,
119 qWarning() <<
"Failed to set video format" << QSystemError::windowsComString(hr);
121 return SUCCEEDED(hr);
124ComPtr<IMFMediaType> findVideoType(IMFSourceReader *reader,
const QCameraFormat &format)
126 for (DWORD i = 0;; ++i) {
127 ComPtr<IMFMediaType> candidate;
128 HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
129 candidate.GetAddressOf());
133 GUID subtype = GUID_NULL;
134 if (FAILED(candidate->GetGUID(MF_MT_SUBTYPE, &subtype)))
137 if (format.pixelFormat() != pixelFormatFromMediaSubtype(subtype))
142 if (FAILED(MFGetAttributeSize(candidate.Get(), MF_MT_FRAME_SIZE, &width, &height)))
145 if (format.resolution() != QSize{
int(width),
int(height) })
160 auto ac = std::unique_ptr<ActiveCamera>(
new ActiveCamera(wc));
161 ac->m_source = createCameraSource(QString::fromUtf8(device.id()));
165 ac->m_readerCallback = makeComObject<CameraReaderCallback>();
166 ac->m_readerCallback->setActiveCamera(ac.get());
167 ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
171 if (!ac->setFormat(format))
181 auto videoType = findVideoType(m_reader.Get(), format);
183 if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) {
184 m_frameFormat = { format.resolution(), format.pixelFormat() };
186 calculateVideoFrameStride(videoType.Get(), format.resolution().width());
190 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
nullptr,
nullptr,
195 void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
197 if (FAILED(status)) {
198 const std::string msg{
std::system_category().message(status) };
199 m_windowsCamera.updateError(QCamera::CameraError, QString::fromStdString(msg));
204 ComPtr<IMFMediaBuffer> mediaBuffer;
205 if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
207 auto result = QWMF::withLockedBuffer(mediaBuffer,
208 [&](QSpan<BYTE> data, QSpan<BYTE> ) {
209 QByteArray bytes(data);
210 auto buffer = std::make_unique<QMemoryVideoBuffer>(std::move(bytes),
213 QVideoFramePrivate::createFrame(std::move(buffer), m_frameFormat);
216 frame.setStartTime(timestamp / 10);
218 LONGLONG duration = -1;
219 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
220 frame.setEndTime((timestamp + duration) / 10);
226 emit m_windowsCamera.newVideoFrame(result.value());
230 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0,
nullptr,
231 nullptr,
nullptr,
nullptr);
236 m_flushWait.release();
242 if (m_readerCallback)
243 m_readerCallback->setActiveCamera(
nullptr);
247 explicit ActiveCamera(
QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) { }
251 if (m_reader && SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) {
252 m_flushWait.acquire();
258 QSemaphore m_flushWait;
260 ComPtr<IMFMediaSource> m_source;
261 ComPtr<IMFSourceReader> m_reader;
262 ComPtr<CameraReaderCallback> m_readerCallback;
264 QVideoFrameFormat m_frameFormat;
265 int m_videoFrameStride = 0;
268STDMETHODIMP CameraReaderCallback::OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
272 m_activeCamera->onReadSample(status, timestamp, sample);
277STDMETHODIMP CameraReaderCallback::OnFlush(DWORD)
288 m_cameraDevice = camera ? camera->cameraDevice() : QCameraDevice{};
298 if (
bool(m_active) == active)
302 if (m_cameraDevice.isNull())
305 if (m_cameraFormat.isNull())
306 m_cameraFormat = findBestCameraFormat(m_cameraDevice);
308 m_active = ActiveCamera::create(*
this, m_cameraDevice, m_cameraFormat);
314 emit activeChanged(
false);
320 bool active =
bool(m_active);
323 m_cameraDevice = camera;
334 bool ok = m_active ? m_active->setFormat(format) :
true;
336 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