19class QGdiWindowCapture::Grabber :
public QFFmpegSurfaceCaptureGrabber
22 static std::unique_ptr<Grabber> create(QGdiWindowCapture &capture, HWND hWnd)
24 auto hdcWindow = GetDC(hWnd);
26 capture.updateError(QPlatformSurfaceCapture::CaptureFailed,
27 QLatin1String(
"Cannot create a window drawing context"));
31 auto hdcMem = CreateCompatibleDC(hdcWindow);
34 capture.updateError(QPlatformSurfaceCapture::CaptureFailed,
35 QLatin1String(
"Cannot create a compatible drawing context"));
39 std::unique_ptr<Grabber> result(
new Grabber(capture, hWnd, hdcWindow, hdcMem));
40 if (!result->update())
52 DeleteObject(m_hBitmap);
58 ReleaseDC(m_hwnd, m_hdcWindow);
61 QVideoFrameFormat format()
const {
return m_format; }
64 Grabber(QGdiWindowCapture &capture, HWND hWnd, HDC hdcWindow, HDC hdcMem)
65 : QFFmpegSurfaceCaptureGrabber(),
67 m_hdcWindow(hdcWindow),
70 addFrameCallback(&capture, &QGdiWindowCapture::newVideoFrame);
71 connect(
this, &Grabber::errorUpdated, &capture, &QGdiWindowCapture::updateError);
73 const auto vrefresh = GetDeviceCaps(hdcWindow, VREFRESH);
74 const auto defaultRate = vrefresh > 0
75 ? qMin(
static_cast<qreal>(vrefresh), DefaultScreenCaptureFrameRate)
76 : DefaultScreenCaptureFrameRate;
77 setFrameRate(capture.frameRate().value_or(defaultRate));
83 if (!GetWindowRect(m_hwnd, &windowRect)) {
84 updateError(QPlatformSurfaceCapture::CaptureFailed,
85 QLatin1String(
"Cannot get window size"));
89 const QSize size{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
91 if (m_format.isValid() && size == m_format.frameSize() && m_hBitmap)
95 DeleteObject(std::exchange(m_hBitmap,
nullptr));
99 updateError(QPlatformSurfaceCapture::CaptureFailed,
100 QLatin1String(
"Invalid window size"));
104 m_hBitmap = CreateCompatibleBitmap(m_hdcWindow, size.width(), size.height());
108 updateError(QPlatformSurfaceCapture::CaptureFailed,
109 QLatin1String(
"Cannot create a compatible bitmap"));
113 QVideoFrameFormat format(size, QVideoFrameFormat::Format_BGRX8888);
114 format.setStreamFrameRate(frameRate());
119 QVideoFrame grabFrame() override
124 const auto oldBitmap = SelectObject(m_hdcMem, m_hBitmap);
125 auto deselect = qScopeGuard([&]() { SelectObject(m_hdcMem, oldBitmap); });
127 const auto size = m_format.frameSize();
129 if (!BitBlt(m_hdcMem, 0, 0, size.width(), size.height(), m_hdcWindow, 0, 0, SRCCOPY)) {
130 updateError(QPlatformSurfaceCapture::CaptureFailed,
131 QLatin1String(
"Cannot copy image to the compatible DC"));
136 auto &header = info.bmiHeader;
137 header.biSize =
sizeof(BITMAPINFOHEADER);
138 header.biWidth = size.width();
139 header.biHeight = -size.height();
141 header.biBitCount = 32;
142 header.biCompression = BI_RGB;
144 const auto bytesPerLine = size.width() * 4;
146 QByteArray array(size.height() * bytesPerLine, Qt::Uninitialized);
148 const auto copiedHeight = GetDIBits(m_hdcMem, m_hBitmap, 0, size.height(), array.data(), &info, DIB_RGB_COLORS);
149 if (copiedHeight != size.height()) {
150 qCWarning(qLcGdiWindowCapture) << copiedHeight <<
"lines have been copied, expected:" << size.height();
158 if (header.biWidth != size.width() || header.biHeight != -size.height()
159 || header.biPlanes != 1 || header.biBitCount != 32 || header.biCompression != BI_RGB) {
160 updateError(QPlatformSurfaceCapture::CaptureFailed,
161 QLatin1String(
"Output bitmap info is unexpected"));
165 return QVideoFramePrivate::createFrame(
166 std::make_unique<QMemoryVideoBuffer>(std::move(array), bytesPerLine), m_format);
171 QVideoFrameFormat m_format;
172 HDC m_hdcWindow = {};
174 HBITMAP m_hBitmap = {};
186bool QGdiWindowCapture::setActiveInternal(
bool active)
188 if (active ==
static_cast<
bool>(m_grabber))
194 auto window = source<WindowSource>();
195 auto handle = QCapturableWindowPrivate::handle(window);
197 m_grabber = Grabber::create(*
this,
reinterpret_cast<HWND>(handle ? handle->id : 0));
200 return static_cast<
bool>(m_grabber) == active;