FFmpeg
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"
33 #include "libavformat/internal.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  */
43 struct 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  */
91 static LRESULT CALLBACK
92 gdigrab_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  */
125 static 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 
188 error:
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  */
206 static int
208 {
209  int result = AVERROR(EIO);
210 
211  /* Start the thread and wait for it to try creating a window */
212  gdigrab->region_result = 0;
213  gdigrab->region_init = CreateEvent(NULL, TRUE, FALSE, NULL);
214  if (!gdigrab->region_init)
215  goto end;
218  if (!gdigrab->region_thread)
219  goto end;
220 
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  */
241 static 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  */
255 static 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) {
297  av_log(s1, AV_LOG_ERROR,
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  {
317  av_log(s1, AV_LOG_ERROR,
318  "Invalid window handle '%s', must be a valid integer.\n", name);
319  ret = AVERROR(EINVAL);
320  goto error;
321  }
322  } else {
323  av_log(s1, AV_LOG_ERROR,
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;
367  clip_rect.top = gdigrab->offset_y;
368  clip_rect.right = gdigrab->width + gdigrab->offset_x;
369  clip_rect.bottom = gdigrab->height + gdigrab->offset_y;
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) {
376  av_log(s1, AV_LOG_ERROR,
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) {
388  av_log(s1, AV_LOG_INFO,
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 {
395  av_log(s1, AV_LOG_INFO,
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;
463  gdigrab->buffer = buffer;
465 
467 
468  if (gdigrab->show_region) {
470  ret = AVERROR(EIO);
471  goto error;
472  }
473  }
474 
476 
480 
481  return 0;
482 
483 error:
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;
517  RECT clip_rect = gdigrab->clip_rect;
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 
570 icon_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 
593  HDC dest_hdc = gdigrab->dest_hdc;
595  RECT clip_rect = gdigrab->clip_rect;
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,
632  source_hdc,
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
690 static 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 
700 static 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 };
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:32
wchar_filename.h
av_gettime_relative
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition: time.c:56
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:53
gdigrab_region_wnd_destroy
static void gdigrab_region_wnd_destroy(AVFormatContext *s1, struct gdigrab *gdigrab)
Cleanup/free the region outline window.
Definition: gdigrab.c:242
gdigrab_read_packet
static int gdigrab_read_packet(AVFormatContext *s1, AVPacket *pkt)
Grabs a frame from gdi (public device demuxer API).
Definition: gdigrab.c:589
avformat_new_stream
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
gdigrab::dest_hdc
HDC dest_hdc
Destination, source-compatible DC.
Definition: gdigrab.c:61
AV_OPT_TYPE_VIDEO_RATE
@ AV_OPT_TYPE_VIDEO_RATE
Underlying C type is AVRational.
Definition: opt.h:315
rect
Definition: f_ebur128.c:78
int64_t
long long int64_t
Definition: coverity.c:34
gdigrab::offset_y
int offset_y
Capture y offset (private option)
Definition: gdigrab.c:57
AVPacket::data
uint8_t * data
Definition: packet.h:603
ff_gdigrab_demuxer
const FFInputFormat ff_gdigrab_demuxer
gdi grabber device demuxer declaration
Definition: gdigrab.c:709
AVOption
AVOption.
Definition: opt.h:429
AVStream::avg_frame_rate
AVRational avg_frame_rate
Average framerate.
Definition: avformat.h:836
gdigrab::time_frame
int64_t time_frame
Current time.
Definition: gdigrab.c:49
OFFSET
#define OFFSET(x)
Definition: gdigrab.c:688
gdigrab::cursor_error_printed
int cursor_error_printed
Definition: gdigrab.c:72
DEC
#define DEC
Definition: gdigrab.c:689
avpriv_set_pts_info
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:902
gdigrab::frame_size
int frame_size
Size in bytes of the frame pixel data.
Definition: gdigrab.c:46
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
gdigrab::height
int height
Height of the grab frame (private option)
Definition: gdigrab.c:55
gdigrab::region_thread
HANDLE region_thread
Region window thread.
Definition: gdigrab.c:67
gdigrab_read_close
static int gdigrab_read_close(AVFormatContext *s1)
Closes gdi frame grabber (public device demuxer API).
Definition: gdigrab.c:669
s
#define s(width, name)
Definition: cbs_vp9.c:198
av_new_packet
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
AV_CODEC_ID_BMP
@ AV_CODEC_ID_BMP
Definition: codec_id.h:130
AVFormatContext::flags
int flags
Flags modifying the (de)muxer behaviour.
Definition: avformat.h:1465
AVInputFormat::name
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:551
gdigrab::region_init
HANDLE region_init
Region window ready signal.
Definition: gdigrab.c:69
av_q2d
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
info
MIPS optimizations info
Definition: mips.txt:2
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:231
av_usleep
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition: time.c:84
WIN32_API_ERROR
#define WIN32_API_ERROR(str)
Definition: gdigrab.c:75
AVFormatContext
Format I/O context.
Definition: avformat.h:1314
internal.h
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:770
framerate
float framerate
Definition: av1_levels.c:29
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
gdigrab_region_wnd_init
static int gdigrab_region_wnd_init(struct gdigrab *gdigrab)
Start the region outline window thread.
Definition: gdigrab.c:207
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
result
and forward the result(frame or status change) to the corresponding input. If nothing is possible
gdigrab::region_thread_id
DWORD region_thread_id
Region window thread ID.
Definition: gdigrab.c:68
gdigrab_region_wnd_proc
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
NULL
#define NULL
Definition: coverity.c:32
gdigrab::bmi
BITMAPINFO bmi
Information describing DIB format.
Definition: gdigrab.c:62
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
AV_OPT_TYPE_IMAGE_SIZE
@ AV_OPT_TYPE_IMAGE_SIZE
Underlying C type is two consecutive integers.
Definition: opt.h:303
REGION_WND_BORDER
#define REGION_WND_BORDER
Definition: gdigrab.c:78
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:242
options
Definition: swscale.c:50
gdigrab::time_base
AVRational time_base
Time base.
Definition: gdigrab.c:48
gdigrab::header_size
int header_size
Size in bytes of the DIB header.
Definition: gdigrab.c:47
gdigrab::clip_rect
RECT clip_rect
The subarea of the screen or window to clip.
Definition: gdigrab.c:65
time.h
AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT
@ AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT
Definition: log.h:42
paint_mouse_pointer
static void paint_mouse_pointer(AVFormatContext *s1, struct gdigrab *gdigrab)
Paints a mouse pointer in a Win32 image.
Definition: gdigrab.c:501
gdigrab::framerate
AVRational framerate
Capture framerate (private option)
Definition: gdigrab.c:53
gdigrab::hbmp
HBITMAP hbmp
Information on the bitmap captured.
Definition: gdigrab.c:63
gdigrab_read_header
static int gdigrab_read_header(AVFormatContext *s1)
Initializes the gdi grab device demuxer (public device demuxer API).
Definition: gdigrab.c:256
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
AVFormatContext::url
char * url
input or output URL.
Definition: avformat.h:1430
gdigrab::offset_x
int offset_x
Capture x offset (private option)
Definition: gdigrab.c:56
gdigrab::show_region
int show_region
Draw border (private option)
Definition: gdigrab.c:52
gdigrab::source_hdc
HDC source_hdc
Source device context.
Definition: gdigrab.c:60
gdigrab_class
static const AVClass gdigrab_class
Definition: gdigrab.c:700
AVFMT_NOFILE
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition: avformat.h:469
FFInputFormat::p
AVInputFormat p
The public AVInputFormat.
Definition: demux.h:70
gdigrab::hwnd
HWND hwnd
Handle of the window for the grab.
Definition: gdigrab.c:59
gdigrab_region_wnd_task
static DWORD WINAPI gdigrab_region_wnd_task(LPVOID opaque)
Run the region outline window loop.
Definition: gdigrab.c:126
read_header
static int read_header(FFV1Context *f, RangeCoder *c)
Definition: ffv1dec.c:574
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:221
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:596
av_inv_q
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition: rational.h:159
demux.h
AVFMT_FLAG_NONBLOCK
#define AVFMT_FLAG_NONBLOCK
Do not block when reading packets from input.
Definition: avformat.h:1468
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:747
gdigrab::region_result
volatile LONG region_result
Region window status, 0 success.
Definition: gdigrab.c:70
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:81
pos
unsigned int pos
Definition: spdifenc.c:414
gdigrab::buffer
void * buffer
The buffer containing the bitmap image data.
Definition: gdigrab.c:64
gdigrab::draw_mouse
int draw_mouse
Draw mouse cursor (private option)
Definition: gdigrab.c:51
buffer
the frame and frame reference mechanism is intended to as much as expensive copies of that data while still allowing the filters to produce correct results The data is stored in buffers represented by AVFrame structures Several references can point to the same frame buffer
Definition: filter_design.txt:49
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
gdigrab
GDI Device Demuxer context.
Definition: gdigrab.c:43
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
av_gettime
int64_t av_gettime(void)
Get the current time in microseconds.
Definition: time.c:39
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
mem.h
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:57
AVPacket
This structure stores compressed data.
Definition: packet.h:580
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
FFInputFormat
Definition: demux.h:66
WaitForSingleObject
#define WaitForSingleObject(a, b)
Definition: w32pthreads.h:64
AVCodecParameters::bit_rate
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition: codec_par.h:99
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
options
static const AVOption options[]
Definition: gdigrab.c:690
gdigrab::width
int width
Width of the grab frame (private option)
Definition: gdigrab.c:54
pkt
static AVPacket * pkt
Definition: demux_decode.c:55
width
#define width
Definition: dsp.h:89
CURSOR_ERROR
#define CURSOR_ERROR(str)
AVFormatContext::priv_data
void * priv_data
Format private data.
Definition: avformat.h:1342