FFmpeg
Loading...
Searching...
No Matches
gdigrab.c
Go to the documentation of this file.
1/*
2 * GDI video grab interface
3 *
4 * This file is part of FFmpeg.
5 *
6 * Copyright (C) 2013 Calvin Walton <calvin.walton@kepstin.ca>
7 * Copyright (C) 2007-2010 Christophe Gisquet <word1.word2@gmail.com>
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24/**
25 * @file
26 * GDI frame device demuxer
27 * @author Calvin Walton <calvin.walton@kepstin.ca>
28 * @author Christophe Gisquet <word1.word2@gmail.com>
29 */
30
31#include "config.h"
32#include "libavformat/demux.h"
34#include "libavutil/mem.h"
35#include "libavutil/opt.h"
36#include "libavutil/time.h"
38#include <windows.h>
39
40/**
41 * GDI Device Demuxer context
42 */
43struct gdigrab {
44 const AVClass *class; /**< Class for private options */
45
46 int frame_size; /**< Size in bytes of the frame pixel data */
47 int header_size; /**< Size in bytes of the DIB header */
48 AVRational time_base; /**< Time base */
49 int64_t time_frame; /**< Current time */
50
51 int draw_mouse; /**< Draw mouse cursor (private option) */
52 int show_region; /**< Draw border (private option) */
53 AVRational framerate; /**< Capture framerate (private option) */
54 int width; /**< Width of the grab frame (private option) */
55 int height; /**< Height of the grab frame (private option) */
56 int offset_x; /**< Capture x offset (private option) */
57 int offset_y; /**< Capture y offset (private option) */
58
59 HWND hwnd; /**< Handle of the window for the grab */
60 HDC source_hdc; /**< Source device context */
61 HDC dest_hdc; /**< Destination, source-compatible DC */
62 BITMAPINFO bmi; /**< Information describing DIB format */
63 HBITMAP hbmp; /**< Information on the bitmap captured */
64 void *buffer; /**< The buffer containing the bitmap image data */
65 RECT clip_rect; /**< The subarea of the screen or window to clip */
66
67 HANDLE region_thread; /**< Region window thread */
68 DWORD region_thread_id; /**< Region window thread ID */
69 HANDLE region_init; /**< Region window ready signal */
70 volatile LONG region_result; /**< Region window status, 0 success */
71
73};
74
75#define WIN32_API_ERROR(str) \
76 av_log(s1, AV_LOG_ERROR, str " (error %li)\n", GetLastError())
77
78#define REGION_WND_BORDER 3
79
80/**
81 * Callback to handle Windows messages for the region outline window.
82 *
83 * In particular, this handles painting the frame rectangle.
84 *
85 * @param hwnd The region outline window handle.
86 * @param msg The Windows message.
87 * @param wparam First Windows message parameter.
88 * @param lparam Second Windows message parameter.
89 * @return 0 success, !0 failure
90 */
91static LRESULT CALLBACK
92gdigrab_region_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
93{
94 PAINTSTRUCT ps;
95 HDC hdc;
96 RECT rect;
97
98 switch (msg) {
99 case WM_PAINT:
100 hdc = BeginPaint(hwnd, &ps);
101
102 GetClientRect(hwnd, &rect);
103 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
104
105 rect.left++; rect.top++; rect.right--; rect.bottom--;
106 FrameRect(hdc, &rect, GetStockObject(WHITE_BRUSH));
107
108 rect.left++; rect.top++; rect.right--; rect.bottom--;
109 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
110
111 EndPaint(hwnd, &ps);
112 break;
113 default:
114 return DefWindowProc(hwnd, msg, wparam, lparam);
115 }
116 return 0;
117}
118
119/**
120 * Run the region outline window loop.
121 *
122 * @param opaque gdigrab context.
123 * @return 0
124 */
125static DWORD WINAPI
127{
128 struct gdigrab *gdigrab = opaque;
129
130 HWND hwnd;
131 RECT rect = gdigrab->clip_rect;
132 HRGN region = NULL;
133 HRGN region_interior = NULL;
134 MSG msg;
135
136 DWORD style = WS_POPUP | WS_VISIBLE;
137 DWORD ex = WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW |
138 WS_EX_TOPMOST | WS_EX_TRANSPARENT;
139
141 rect.right += REGION_WND_BORDER; rect.bottom += REGION_WND_BORDER;
142
143 AdjustWindowRectEx(&rect, style, FALSE, ex);
144
145 // Create a window with no owner; use WC_DIALOG instead of writing a custom
146 // window class
147 hwnd = CreateWindowEx(ex, WC_DIALOG, NULL, style, rect.left, rect.top,
148 rect.right - rect.left, rect.bottom - rect.top,
149 NULL, NULL, NULL, NULL);
150 if (!hwnd) {
151 av_log(NULL, AV_LOG_ERROR, "Could not create region display window "
152 "(error %li)\n", GetLastError());
153 goto error;
154 }
155
156 // Set the window shape to only include the border area
157 GetClientRect(hwnd, &rect);
158 region = CreateRectRgn(0, 0,
159 rect.right - rect.left, rect.bottom - rect.top);
160 region_interior = CreateRectRgn(REGION_WND_BORDER, REGION_WND_BORDER,
161 rect.right - rect.left - REGION_WND_BORDER,
162 rect.bottom - rect.top - REGION_WND_BORDER);
163 CombineRgn(region, region, region_interior, RGN_DIFF);
164 if (!SetWindowRgn(hwnd, region, FALSE)) {
165 av_log(NULL, AV_LOG_ERROR, "Could not set window region "
166 "(error %li)\n", GetLastError());
167 goto error;
168 }
169 // The "region" memory is now owned by the window
170 region = NULL;
171 DeleteObject(region_interior);
172
173 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) gdigrab_region_wnd_proc);
174 SetLayeredWindowAttributes(hwnd, 0, 0xFF, LWA_ALPHA);
175
176 ShowWindow(hwnd, SW_SHOW);
177
178 InterlockedExchange(&gdigrab->region_result, 0);
179 SetEvent(gdigrab->region_init);
180
181 while (GetMessage(&msg, NULL, 0, 0)) {
182 DispatchMessage(&msg);
183 }
184
185 DestroyWindow(hwnd);
186 return 0;
187
188error:
189 if (region)
190 DeleteObject(region);
191 if (region_interior)
192 DeleteObject(region_interior);
193 if (hwnd)
194 DestroyWindow(hwnd);
195 InterlockedExchange(&gdigrab->region_result, AVERROR(EIO));
196 SetEvent(gdigrab->region_init);
197 return 0;
198}
199
200/**
201 * Start the region outline window thread.
202 *
203 * @param gdigrab gdigrab context.
204 * @return 0 success, !0 failure
205 */
206static int
208{
209 int result = AVERROR(EIO);
210
211 /* Start the thread and wait for it to try creating a window */
213 gdigrab->region_init = CreateEvent(NULL, TRUE, FALSE, NULL);
214 if (!gdigrab->region_init)
215 goto end;
219 goto end;
220
222 result = gdigrab->region_result;
223
224 end:
225 if (gdigrab->region_thread && result < 0) {
227 CloseHandle(gdigrab->region_thread);
228 }
229 if (gdigrab->region_init) {
230 CloseHandle(gdigrab->region_init);
231 }
232 return result;
233}
234
235/**
236 * Cleanup/free the region outline window.
237 *
238 * @param s1 The format context.
239 * @param gdigrab gdigrab context.
240 */
241static void
243{
244 PostThreadMessage(gdigrab->region_thread_id, WM_QUIT, 0, 0);
246 CloseHandle(gdigrab->region_thread);
247}
248
249/**
250 * Initializes the gdi grab device demuxer (public device demuxer API).
251 *
252 * @param s1 Context from avformat core
253 * @return AVERROR_IO error, 0 success
254 */
255static int
257{
258 struct gdigrab *gdigrab = s1->priv_data;
259
260 HWND hwnd;
261 HDC source_hdc = NULL;
262 HDC dest_hdc = NULL;
263 BITMAPINFO bmi;
264 HBITMAP hbmp = NULL;
265 void *buffer = NULL;
266
267 const char *filename = s1->url;
268 const char *name = NULL;
269 AVStream *st = NULL;
270
271 int bpp;
272 int horzres;
273 int vertres;
274 int desktophorzres;
275 int desktopvertres;
276 RECT virtual_rect;
277 RECT clip_rect;
278 BITMAP bmp;
279 int ret;
280
281 if (!strncmp(filename, "title=", 6)) {
282 wchar_t *name_w = NULL;
283 name = filename + 6;
284
285 if(utf8towchar(name, &name_w)) {
286 ret = AVERROR(errno);
287 goto error;
288 }
289 if(!name_w) {
290 ret = AVERROR(EINVAL);
291 goto error;
292 }
293
294 hwnd = FindWindowW(NULL, name_w);
295 av_freep(&name_w);
296 if (!hwnd) {
298 "Can't find window '%s', aborting.\n", name);
299 ret = AVERROR(EIO);
300 goto error;
301 }
302 if (gdigrab->show_region) {
304 "Can't show region when grabbing a window.\n");
305 gdigrab->show_region = 0;
306 }
307 } else if (!strcmp(filename, "desktop")) {
308 hwnd = NULL;
309 } else if (!strncmp(filename, "hwnd=", 5)) {
310 char *p;
311 name = filename + 5;
312
313 hwnd = (HWND)(intptr_t) strtoull(name, &p, 0);
314
315 if (p == NULL || p == name || p[0] != '\0')
316 {
318 "Invalid window handle '%s', must be a valid integer.\n", name);
319 ret = AVERROR(EINVAL);
320 goto error;
321 }
322 } else {
324 "Please use \"desktop\", \"title=<windowname>\" or \"hwnd=<hwnd>\" to specify your target.\n");
325 ret = AVERROR(EIO);
326 goto error;
327 }
328
329 /* This will get the device context for the selected window, or if
330 * none, the primary screen */
331 source_hdc = GetDC(hwnd);
332 if (!source_hdc) {
333 WIN32_API_ERROR("Couldn't get window device context");
334 ret = AVERROR(EIO);
335 goto error;
336 }
337 bpp = GetDeviceCaps(source_hdc, BITSPIXEL);
338
339 horzres = GetDeviceCaps(source_hdc, HORZRES);
340 vertres = GetDeviceCaps(source_hdc, VERTRES);
341 desktophorzres = GetDeviceCaps(source_hdc, DESKTOPHORZRES);
342 desktopvertres = GetDeviceCaps(source_hdc, DESKTOPVERTRES);
343
344 if (hwnd) {
345 GetClientRect(hwnd, &virtual_rect);
346 /* window -- get the right height and width for scaling DPI */
347 virtual_rect.left = virtual_rect.left * desktophorzres / horzres;
348 virtual_rect.right = virtual_rect.right * desktophorzres / horzres;
349 virtual_rect.top = virtual_rect.top * desktopvertres / vertres;
350 virtual_rect.bottom = virtual_rect.bottom * desktopvertres / vertres;
351 } else {
352 /* desktop -- get the right height and width for scaling DPI */
353 virtual_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
354 virtual_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
355 virtual_rect.right = (virtual_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN)) * desktophorzres / horzres;
356 virtual_rect.bottom = (virtual_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN)) * desktopvertres / vertres;
357 }
358
359 /* If no width or height set, use full screen/window area */
360 if (!gdigrab->width || !gdigrab->height) {
361 clip_rect.left = virtual_rect.left;
362 clip_rect.top = virtual_rect.top;
363 clip_rect.right = virtual_rect.right;
364 clip_rect.bottom = virtual_rect.bottom;
365 } else {
366 clip_rect.left = gdigrab->offset_x;
370 }
371
372 if (clip_rect.left < virtual_rect.left ||
373 clip_rect.top < virtual_rect.top ||
374 clip_rect.right > virtual_rect.right ||
375 clip_rect.bottom > virtual_rect.bottom) {
377 "Capture area (%li,%li),(%li,%li) extends outside window area (%li,%li),(%li,%li)",
378 clip_rect.left, clip_rect.top,
379 clip_rect.right, clip_rect.bottom,
380 virtual_rect.left, virtual_rect.top,
381 virtual_rect.right, virtual_rect.bottom);
382 ret = AVERROR(EIO);
383 goto error;
384 }
385
386
387 if (name) {
389 "Found window %s, capturing %lix%lix%i at (%li,%li)\n",
390 name,
391 clip_rect.right - clip_rect.left,
392 clip_rect.bottom - clip_rect.top,
393 bpp, clip_rect.left, clip_rect.top);
394 } else {
396 "Capturing whole desktop as %lix%lix%i at (%li,%li)\n",
397 clip_rect.right - clip_rect.left,
398 clip_rect.bottom - clip_rect.top,
399 bpp, clip_rect.left, clip_rect.top);
400 }
401
402 if (clip_rect.right - clip_rect.left <= 0 ||
403 clip_rect.bottom - clip_rect.top <= 0 || bpp%8) {
404 av_log(s1, AV_LOG_ERROR, "Invalid properties, aborting\n");
405 ret = AVERROR(EIO);
406 goto error;
407 }
408
409 dest_hdc = CreateCompatibleDC(source_hdc);
410 if (!dest_hdc) {
411 WIN32_API_ERROR("Screen DC CreateCompatibleDC");
412 ret = AVERROR(EIO);
413 goto error;
414 }
415
416 /* Create a DIB and select it into the dest_hdc */
417 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
418 bmi.bmiHeader.biWidth = clip_rect.right - clip_rect.left;
419 bmi.bmiHeader.biHeight = -(clip_rect.bottom - clip_rect.top);
420 bmi.bmiHeader.biPlanes = 1;
421 bmi.bmiHeader.biBitCount = bpp;
422 bmi.bmiHeader.biCompression = BI_RGB;
423 bmi.bmiHeader.biSizeImage = 0;
424 bmi.bmiHeader.biXPelsPerMeter = 0;
425 bmi.bmiHeader.biYPelsPerMeter = 0;
426 bmi.bmiHeader.biClrUsed = 0;
427 bmi.bmiHeader.biClrImportant = 0;
428 hbmp = CreateDIBSection(dest_hdc, &bmi, DIB_RGB_COLORS,
429 &buffer, NULL, 0);
430 if (!hbmp) {
431 WIN32_API_ERROR("Creating DIB Section");
432 ret = AVERROR(EIO);
433 goto error;
434 }
435
436 if (!SelectObject(dest_hdc, hbmp)) {
437 WIN32_API_ERROR("SelectObject");
438 ret = AVERROR(EIO);
439 goto error;
440 }
441
442 /* Get info from the bitmap */
443 GetObject(hbmp, sizeof(BITMAP), &bmp);
444
445 st = avformat_new_stream(s1, NULL);
446 if (!st) {
447 ret = AVERROR(ENOMEM);
448 goto error;
449 }
450 avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
451
452 gdigrab->frame_size = bmp.bmWidthBytes * bmp.bmHeight * bmp.bmPlanes;
453 gdigrab->header_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
454 (bpp <= 8 ? (1 << bpp) : 0) * sizeof(RGBQUAD) /* palette size */;
457
458 gdigrab->hwnd = hwnd;
461 gdigrab->hbmp = hbmp;
462 gdigrab->bmi = bmi;
465
467
468 if (gdigrab->show_region) {
470 ret = AVERROR(EIO);
471 goto error;
472 }
473 }
474
476
480
481 return 0;
482
483error:
484 if (source_hdc)
485 ReleaseDC(hwnd, source_hdc);
486 if (dest_hdc)
487 DeleteDC(dest_hdc);
488 if (hbmp)
489 DeleteObject(hbmp);
490 if (source_hdc)
491 DeleteDC(source_hdc);
492 return ret;
493}
494
495/**
496 * Paints a mouse pointer in a Win32 image.
497 *
498 * @param s1 Context of the log information
499 * @param s Current grad structure
500 */
502{
503 CURSORINFO ci = {0};
504
505#define CURSOR_ERROR(str) \
506 if (!gdigrab->cursor_error_printed) { \
507 WIN32_API_ERROR(str); \
508 gdigrab->cursor_error_printed = 1; \
509 }
510
511 ci.cbSize = sizeof(ci);
512
513 if (GetCursorInfo(&ci)) {
514 HCURSOR icon = CopyCursor(ci.hCursor);
515 ICONINFO info;
516 POINT pos;
518 HWND hwnd = gdigrab->hwnd;
519 int horzres = GetDeviceCaps(gdigrab->source_hdc, HORZRES);
520 int vertres = GetDeviceCaps(gdigrab->source_hdc, VERTRES);
521 int desktophorzres = GetDeviceCaps(gdigrab->source_hdc, DESKTOPHORZRES);
522 int desktopvertres = GetDeviceCaps(gdigrab->source_hdc, DESKTOPVERTRES);
523 info.hbmMask = NULL;
524 info.hbmColor = NULL;
525
526 if (ci.flags != CURSOR_SHOWING)
527 return;
528
529 if (!icon) {
530 /* Use the standard arrow cursor as a fallback.
531 * You'll probably only hit this in Wine, which can't fetch
532 * the current system cursor. */
533 icon = CopyCursor(LoadCursor(NULL, IDC_ARROW));
534 }
535
536 if (!GetIconInfo(icon, &info)) {
537 CURSOR_ERROR("Could not get icon info");
538 goto icon_error;
539 }
540
541 if (hwnd) {
542 RECT rect;
543
544 if (GetWindowRect(hwnd, &rect)) {
545 pos.x = ci.ptScreenPos.x - clip_rect.left - info.xHotspot - rect.left;
546 pos.y = ci.ptScreenPos.y - clip_rect.top - info.yHotspot - rect.top;
547
548 //that would keep the correct location of mouse with hidpi screens
549 pos.x = pos.x * desktophorzres / horzres;
550 pos.y = pos.y * desktopvertres / vertres;
551 } else {
552 CURSOR_ERROR("Couldn't get window rectangle");
553 goto icon_error;
554 }
555 } else {
556 //that would keep the correct location of mouse with hidpi screens
557 pos.x = ci.ptScreenPos.x * desktophorzres / horzres - clip_rect.left - info.xHotspot;
558 pos.y = ci.ptScreenPos.y * desktopvertres / vertres - clip_rect.top - info.yHotspot;
559 }
560
561 av_log(s1, AV_LOG_DEBUG, "Cursor pos (%li,%li) -> (%li,%li)\n",
562 ci.ptScreenPos.x, ci.ptScreenPos.y, pos.x, pos.y);
563
564 if (pos.x >= 0 && pos.x <= clip_rect.right - clip_rect.left &&
565 pos.y >= 0 && pos.y <= clip_rect.bottom - clip_rect.top) {
566 if (!DrawIcon(gdigrab->dest_hdc, pos.x, pos.y, icon))
567 CURSOR_ERROR("Couldn't draw icon");
568 }
569
570icon_error:
571 if (info.hbmMask)
572 DeleteObject(info.hbmMask);
573 if (info.hbmColor)
574 DeleteObject(info.hbmColor);
575 if (icon)
576 DestroyCursor(icon);
577 } else {
578 CURSOR_ERROR("Couldn't get cursor info");
579 }
580}
581
582/**
583 * Grabs a frame from gdi (public device demuxer API).
584 *
585 * @param s1 Context from avformat core
586 * @param pkt Packet holding the grabbed frame
587 * @return frame size in bytes
588 */
590{
591 struct gdigrab *gdigrab = s1->priv_data;
592
598
599 BITMAPFILEHEADER bfh;
600 int file_size = gdigrab->header_size + gdigrab->frame_size;
601
602 int64_t curtime, delay;
603
604 /* Calculate the time of the next frame */
605 time_frame += INT64_C(1000000);
606
607 /* wait based on the frame rate */
608 for (;;) {
609 curtime = av_gettime_relative();
610 delay = time_frame * av_q2d(time_base) - curtime;
611 if (delay <= 0) {
612 if (delay < INT64_C(-1000000) * av_q2d(time_base)) {
613 time_frame += INT64_C(1000000);
614 }
615 break;
616 }
617 if (s1->flags & AVFMT_FLAG_NONBLOCK) {
618 return AVERROR(EAGAIN);
619 } else {
620 av_usleep(delay);
621 }
622 }
623
624 if (av_new_packet(pkt, file_size) < 0)
625 return AVERROR(ENOMEM);
626 pkt->pts = av_gettime();
627
628 /* Blit screen grab */
629 if (!BitBlt(dest_hdc, 0, 0,
630 clip_rect.right - clip_rect.left,
631 clip_rect.bottom - clip_rect.top,
633 clip_rect.left, clip_rect.top, SRCCOPY | CAPTUREBLT)) {
634 WIN32_API_ERROR("Failed to capture image");
635 return AVERROR(EIO);
636 }
637 if (gdigrab->draw_mouse)
639
640 /* Copy bits to packet data */
641
642 bfh.bfType = 0x4d42; /* "BM" in little-endian */
643 bfh.bfSize = file_size;
644 bfh.bfReserved1 = 0;
645 bfh.bfReserved2 = 0;
646 bfh.bfOffBits = gdigrab->header_size;
647
648 memcpy(pkt->data, &bfh, sizeof(bfh));
649
650 memcpy(pkt->data + sizeof(bfh), &gdigrab->bmi.bmiHeader, sizeof(gdigrab->bmi.bmiHeader));
651
652 if (gdigrab->bmi.bmiHeader.biBitCount <= 8)
653 GetDIBColorTable(dest_hdc, 0, 1 << gdigrab->bmi.bmiHeader.biBitCount,
654 (RGBQUAD *) (pkt->data + sizeof(bfh) + sizeof(gdigrab->bmi.bmiHeader)));
655
657
659
661}
662
663/**
664 * Closes gdi frame grabber (public device demuxer API).
665 *
666 * @param s1 Context from avformat core
667 * @return 0 success, !0 failure
668 */
670{
671 struct gdigrab *s = s1->priv_data;
672
673 if (s->show_region)
675
676 if (s->source_hdc)
677 ReleaseDC(s->hwnd, s->source_hdc);
678 if (s->dest_hdc)
679 DeleteDC(s->dest_hdc);
680 if (s->hbmp)
681 DeleteObject(s->hbmp);
682 if (s->source_hdc)
683 DeleteDC(s->source_hdc);
684
685 return 0;
686}
687
688#define OFFSET(x) offsetof(struct gdigrab, x)
689#define DEC AV_OPT_FLAG_DECODING_PARAM
690static const AVOption options[] = {
691 { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, DEC },
692 { "show_region", "draw border around capture area", OFFSET(show_region), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
693 { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, INT_MAX, DEC },
694 { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
695 { "offset_x", "capture area x offset", OFFSET(offset_x), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
696 { "offset_y", "capture area y offset", OFFSET(offset_y), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
697 { NULL },
698};
699
700static const AVClass gdigrab_class = {
701 .class_name = "GDIgrab indev",
702 .item_name = av_default_item_name,
703 .option = options,
704 .version = LIBAVUTIL_VERSION_INT,
706};
707
708/** gdi grabber device demuxer declaration */
710 .p.name = "gdigrab",
711 .p.long_name = NULL_IF_CONFIG_SMALL("GDI API Windows frame grabber"),
712 .p.flags = AVFMT_NOFILE,
713 .p.priv_class = &gdigrab_class,
714 .priv_data_size = sizeof(struct gdigrab),
716 .read_packet = gdigrab_read_packet,
717 .read_close = gdigrab_read_close,
718};
const FFInputFormat ff_gdigrab_demuxer
gdi grabber device demuxer declaration
Definition gdigrab.c:709
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
#define AVFMT_FLAG_NONBLOCK
Do not block when reading packets from input.
Definition avformat.h:1487
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition avformat.h:488
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVPacket * pkt
static int read_header(FFV1Context *f, RangeCoder *c)
Definition ffv1dec.c:578
static int gdigrab_read_close(AVFormatContext *s1)
Closes gdi frame grabber (public device demuxer API).
Definition gdigrab.c:669
static void gdigrab_region_wnd_destroy(AVFormatContext *s1, struct gdigrab *gdigrab)
Cleanup/free the region outline window.
Definition gdigrab.c:242
static LRESULT CALLBACK gdigrab_region_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
Callback to handle Windows messages for the region outline window.
Definition gdigrab.c:92
#define REGION_WND_BORDER
Definition gdigrab.c:78
#define CURSOR_ERROR(str)
#define WIN32_API_ERROR(str)
Definition gdigrab.c:75
static int gdigrab_read_packet(AVFormatContext *s1, AVPacket *pkt)
Grabs a frame from gdi (public device demuxer API).
Definition gdigrab.c:589
static int gdigrab_read_header(AVFormatContext *s1)
Initializes the gdi grab device demuxer (public device demuxer API).
Definition gdigrab.c:256
static void paint_mouse_pointer(AVFormatContext *s1, struct gdigrab *gdigrab)
Paints a mouse pointer in a Win32 image.
Definition gdigrab.c:501
static DWORD WINAPI gdigrab_region_wnd_task(LPVOID opaque)
Run the region outline window loop.
Definition gdigrab.c:126
static const AVClass gdigrab_class
Definition gdigrab.c:700
#define OFFSET(x)
Definition gdigrab.c:688
static int gdigrab_region_wnd_init(struct gdigrab *gdigrab)
Start the region outline window thread.
Definition gdigrab.c:207
@ AV_OPT_TYPE_IMAGE_SIZE
Underlying C type is two consecutive integers.
Definition opt.h:302
@ AV_OPT_TYPE_VIDEO_RATE
Underlying C type is AVRational.
Definition opt.h:314
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_CODEC_ID_BMP
Definition codec_id.h:128
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition packet.c:98
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition rational.h:104
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition rational.h:159
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define DEC
Definition librsvgdec.c:149
@ AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT
Definition log.h:42
Memory handling functions.
AVOptions.
const char * name
Definition qsvenc.c:142
unsigned int pos
Definition spdifenc.c:431
Describe the class of an AVClass context structure.
Definition log.h:76
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition codec_par.h:99
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
Format I/O context.
Definition avformat.h:1333
int flags
Flags modifying the (de)muxer behaviour.
Definition avformat.h:1484
char * url
input or output URL.
Definition avformat.h:1449
void * priv_data
Format private data.
Definition avformat.h:1361
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
Rational number (pair of numerator and denominator).
Definition rational.h:58
Stream structure.
Definition avformat.h:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
AVRational avg_frame_rate
Average framerate.
Definition avformat.h:855
GDI Device Demuxer context.
Definition gdigrab.c:43
HDC dest_hdc
Destination, source-compatible DC.
Definition gdigrab.c:61
int height
Height of the grab frame (private option)
Definition gdigrab.c:55
int show_region
Draw border (private option)
Definition gdigrab.c:52
HWND hwnd
Handle of the window for the grab.
Definition gdigrab.c:59
int frame_size
Size in bytes of the frame pixel data.
Definition gdigrab.c:46
int offset_y
Capture y offset (private option)
Definition gdigrab.c:57
HBITMAP hbmp
Information on the bitmap captured.
Definition gdigrab.c:63
AVRational time_base
Time base.
Definition gdigrab.c:48
int width
Width of the grab frame (private option)
Definition gdigrab.c:54
RECT clip_rect
The subarea of the screen or window to clip.
Definition gdigrab.c:65
AVRational framerate
Capture framerate (private option)
Definition gdigrab.c:53
HANDLE region_thread
Region window thread.
Definition gdigrab.c:67
HDC source_hdc
Source device context.
Definition gdigrab.c:60
int offset_x
Capture x offset (private option)
Definition gdigrab.c:56
DWORD region_thread_id
Region window thread ID.
Definition gdigrab.c:68
void * buffer
The buffer containing the bitmap image data.
Definition gdigrab.c:64
int64_t time_frame
Current time.
Definition gdigrab.c:49
HANDLE region_init
Region window ready signal.
Definition gdigrab.c:69
BITMAPINFO bmi
Information describing DIB format.
Definition gdigrab.c:62
int draw_mouse
Draw mouse cursor (private option)
Definition gdigrab.c:51
volatile LONG region_result
Region window status, 0 success.
Definition gdigrab.c:70
int cursor_error_printed
Definition gdigrab.c:72
int header_size
Size in bytes of the DIB header.
Definition gdigrab.c:47
#define av_freep(p)
#define av_log(a,...)
static void error(const char *err)
float framerate
Definition av1_levels.c:29
static char buffer[20]
Definition seek.c:32
#define width
Definition dsp.h:89
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition time.c:93
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition time.c:57
int64_t av_gettime(void)
Get the current time in microseconds.
Definition time.c:40
#define WaitForSingleObject(a, b)
Definition w32pthreads.h:64