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  HWND region_hwnd; /**< Handle of the region border window */
68 
70 };
71 
72 #define WIN32_API_ERROR(str) \
73  av_log(s1, AV_LOG_ERROR, str " (error %li)\n", GetLastError())
74 
75 #define REGION_WND_BORDER 3
76 
77 /**
78  * Callback to handle Windows messages for the region outline window.
79  *
80  * In particular, this handles painting the frame rectangle.
81  *
82  * @param hwnd The region outline window handle.
83  * @param msg The Windows message.
84  * @param wparam First Windows message parameter.
85  * @param lparam Second Windows message parameter.
86  * @return 0 success, !0 failure
87  */
88 static LRESULT CALLBACK
89 gdigrab_region_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
90 {
91  PAINTSTRUCT ps;
92  HDC hdc;
93  RECT rect;
94 
95  switch (msg) {
96  case WM_PAINT:
97  hdc = BeginPaint(hwnd, &ps);
98 
99  GetClientRect(hwnd, &rect);
100  FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
101 
102  rect.left++; rect.top++; rect.right--; rect.bottom--;
103  FrameRect(hdc, &rect, GetStockObject(WHITE_BRUSH));
104 
105  rect.left++; rect.top++; rect.right--; rect.bottom--;
106  FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
107 
108  EndPaint(hwnd, &ps);
109  break;
110  default:
111  return DefWindowProc(hwnd, msg, wparam, lparam);
112  }
113  return 0;
114 }
115 
116 /**
117  * Initialize the region outline window.
118  *
119  * @param s1 The format context.
120  * @param gdigrab gdigrab context.
121  * @return 0 success, !0 failure
122  */
123 static int
125 {
126  HWND hwnd;
127  RECT rect = gdigrab->clip_rect;
128  HRGN region = NULL;
129  HRGN region_interior = NULL;
130 
131  DWORD style = WS_POPUP | WS_VISIBLE;
132  DWORD ex = WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_TRANSPARENT;
133 
135  rect.right += REGION_WND_BORDER; rect.bottom += REGION_WND_BORDER;
136 
137  AdjustWindowRectEx(&rect, style, FALSE, ex);
138 
139  // Create a window with no owner; use WC_DIALOG instead of writing a custom
140  // window class
141  hwnd = CreateWindowEx(ex, WC_DIALOG, NULL, style, rect.left, rect.top,
142  rect.right - rect.left, rect.bottom - rect.top,
143  NULL, NULL, NULL, NULL);
144  if (!hwnd) {
145  WIN32_API_ERROR("Could not create region display window");
146  goto error;
147  }
148 
149  // Set the window shape to only include the border area
150  GetClientRect(hwnd, &rect);
151  region = CreateRectRgn(0, 0,
152  rect.right - rect.left, rect.bottom - rect.top);
153  region_interior = CreateRectRgn(REGION_WND_BORDER, REGION_WND_BORDER,
154  rect.right - rect.left - REGION_WND_BORDER,
155  rect.bottom - rect.top - REGION_WND_BORDER);
156  CombineRgn(region, region, region_interior, RGN_DIFF);
157  if (!SetWindowRgn(hwnd, region, FALSE)) {
158  WIN32_API_ERROR("Could not set window region");
159  goto error;
160  }
161  // The "region" memory is now owned by the window
162  region = NULL;
163  DeleteObject(region_interior);
164 
165  SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) gdigrab_region_wnd_proc);
166 
167  ShowWindow(hwnd, SW_SHOW);
168 
169  gdigrab->region_hwnd = hwnd;
170 
171  return 0;
172 
173 error:
174  if (region)
175  DeleteObject(region);
176  if (region_interior)
177  DeleteObject(region_interior);
178  if (hwnd)
179  DestroyWindow(hwnd);
180  return 1;
181 }
182 
183 /**
184  * Cleanup/free the region outline window.
185  *
186  * @param s1 The format context.
187  * @param gdigrab gdigrab context.
188  */
189 static void
191 {
192  if (gdigrab->region_hwnd)
193  DestroyWindow(gdigrab->region_hwnd);
195 }
196 
197 /**
198  * Process the Windows message queue.
199  *
200  * This is important to prevent Windows from thinking the window has become
201  * unresponsive. As well, things like WM_PAINT (to actually draw the window
202  * contents) are handled from the message queue context.
203  *
204  * @param s1 The format context.
205  * @param gdigrab gdigrab context.
206  */
207 static void
209 {
210  HWND hwnd = gdigrab->region_hwnd;
211  MSG msg;
212 
213  while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
214  DispatchMessage(&msg);
215  }
216 }
217 
218 /**
219  * Initializes the gdi grab device demuxer (public device demuxer API).
220  *
221  * @param s1 Context from avformat core
222  * @return AVERROR_IO error, 0 success
223  */
224 static int
226 {
227  struct gdigrab *gdigrab = s1->priv_data;
228 
229  HWND hwnd;
230  HDC source_hdc = NULL;
231  HDC dest_hdc = NULL;
232  BITMAPINFO bmi;
233  HBITMAP hbmp = NULL;
234  void *buffer = NULL;
235 
236  const char *filename = s1->url;
237  const char *name = NULL;
238  AVStream *st = NULL;
239 
240  int bpp;
241  int horzres;
242  int vertres;
243  int desktophorzres;
244  int desktopvertres;
245  RECT virtual_rect;
246  RECT clip_rect;
247  BITMAP bmp;
248  int ret;
249 
250  if (!strncmp(filename, "title=", 6)) {
251  wchar_t *name_w = NULL;
252  name = filename + 6;
253 
254  if(utf8towchar(name, &name_w)) {
255  ret = AVERROR(errno);
256  goto error;
257  }
258  if(!name_w) {
259  ret = AVERROR(EINVAL);
260  goto error;
261  }
262 
263  hwnd = FindWindowW(NULL, name_w);
264  av_freep(&name_w);
265  if (!hwnd) {
267  "Can't find window '%s', aborting.\n", name);
268  ret = AVERROR(EIO);
269  goto error;
270  }
271  if (gdigrab->show_region) {
273  "Can't show region when grabbing a window.\n");
274  gdigrab->show_region = 0;
275  }
276  } else if (!strcmp(filename, "desktop")) {
277  hwnd = NULL;
278  } else if (!strncmp(filename, "hwnd=", 5)) {
279  char *p;
280  name = filename + 5;
281 
282  hwnd = (HWND) strtoull(name, &p, 0);
283 
284  if (p == NULL || p == name || p[0] == '\0')
285  {
287  "Invalid window handle '%s', must be a valid integer.\n", name);
288  ret = AVERROR(EINVAL);
289  goto error;
290  }
291  } else {
293  "Please use \"desktop\", \"title=<windowname>\" or \"hwnd=<hwnd>\" to specify your target.\n");
294  ret = AVERROR(EIO);
295  goto error;
296  }
297 
298  /* This will get the device context for the selected window, or if
299  * none, the primary screen */
300  source_hdc = GetDC(hwnd);
301  if (!source_hdc) {
302  WIN32_API_ERROR("Couldn't get window device context");
303  ret = AVERROR(EIO);
304  goto error;
305  }
306  bpp = GetDeviceCaps(source_hdc, BITSPIXEL);
307 
308  horzres = GetDeviceCaps(source_hdc, HORZRES);
309  vertres = GetDeviceCaps(source_hdc, VERTRES);
310  desktophorzres = GetDeviceCaps(source_hdc, DESKTOPHORZRES);
311  desktopvertres = GetDeviceCaps(source_hdc, DESKTOPVERTRES);
312 
313  if (hwnd) {
314  GetClientRect(hwnd, &virtual_rect);
315  /* window -- get the right height and width for scaling DPI */
316  virtual_rect.left = virtual_rect.left * desktophorzres / horzres;
317  virtual_rect.right = virtual_rect.right * desktophorzres / horzres;
318  virtual_rect.top = virtual_rect.top * desktopvertres / vertres;
319  virtual_rect.bottom = virtual_rect.bottom * desktopvertres / vertres;
320  } else {
321  /* desktop -- get the right height and width for scaling DPI */
322  virtual_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
323  virtual_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
324  virtual_rect.right = (virtual_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN)) * desktophorzres / horzres;
325  virtual_rect.bottom = (virtual_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN)) * desktopvertres / vertres;
326  }
327 
328  /* If no width or height set, use full screen/window area */
329  if (!gdigrab->width || !gdigrab->height) {
330  clip_rect.left = virtual_rect.left;
331  clip_rect.top = virtual_rect.top;
332  clip_rect.right = virtual_rect.right;
333  clip_rect.bottom = virtual_rect.bottom;
334  } else {
335  clip_rect.left = gdigrab->offset_x;
336  clip_rect.top = gdigrab->offset_y;
337  clip_rect.right = gdigrab->width + gdigrab->offset_x;
338  clip_rect.bottom = gdigrab->height + gdigrab->offset_y;
339  }
340 
341  if (clip_rect.left < virtual_rect.left ||
342  clip_rect.top < virtual_rect.top ||
343  clip_rect.right > virtual_rect.right ||
344  clip_rect.bottom > virtual_rect.bottom) {
346  "Capture area (%li,%li),(%li,%li) extends outside window area (%li,%li),(%li,%li)",
347  clip_rect.left, clip_rect.top,
348  clip_rect.right, clip_rect.bottom,
349  virtual_rect.left, virtual_rect.top,
350  virtual_rect.right, virtual_rect.bottom);
351  ret = AVERROR(EIO);
352  goto error;
353  }
354 
355 
356  if (name) {
358  "Found window %s, capturing %lix%lix%i at (%li,%li)\n",
359  name,
360  clip_rect.right - clip_rect.left,
361  clip_rect.bottom - clip_rect.top,
362  bpp, clip_rect.left, clip_rect.top);
363  } else {
365  "Capturing whole desktop as %lix%lix%i at (%li,%li)\n",
366  clip_rect.right - clip_rect.left,
367  clip_rect.bottom - clip_rect.top,
368  bpp, clip_rect.left, clip_rect.top);
369  }
370 
371  if (clip_rect.right - clip_rect.left <= 0 ||
372  clip_rect.bottom - clip_rect.top <= 0 || bpp%8) {
373  av_log(s1, AV_LOG_ERROR, "Invalid properties, aborting\n");
374  ret = AVERROR(EIO);
375  goto error;
376  }
377 
378  dest_hdc = CreateCompatibleDC(source_hdc);
379  if (!dest_hdc) {
380  WIN32_API_ERROR("Screen DC CreateCompatibleDC");
381  ret = AVERROR(EIO);
382  goto error;
383  }
384 
385  /* Create a DIB and select it into the dest_hdc */
386  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
387  bmi.bmiHeader.biWidth = clip_rect.right - clip_rect.left;
388  bmi.bmiHeader.biHeight = -(clip_rect.bottom - clip_rect.top);
389  bmi.bmiHeader.biPlanes = 1;
390  bmi.bmiHeader.biBitCount = bpp;
391  bmi.bmiHeader.biCompression = BI_RGB;
392  bmi.bmiHeader.biSizeImage = 0;
393  bmi.bmiHeader.biXPelsPerMeter = 0;
394  bmi.bmiHeader.biYPelsPerMeter = 0;
395  bmi.bmiHeader.biClrUsed = 0;
396  bmi.bmiHeader.biClrImportant = 0;
397  hbmp = CreateDIBSection(dest_hdc, &bmi, DIB_RGB_COLORS,
398  &buffer, NULL, 0);
399  if (!hbmp) {
400  WIN32_API_ERROR("Creating DIB Section");
401  ret = AVERROR(EIO);
402  goto error;
403  }
404 
405  if (!SelectObject(dest_hdc, hbmp)) {
406  WIN32_API_ERROR("SelectObject");
407  ret = AVERROR(EIO);
408  goto error;
409  }
410 
411  /* Get info from the bitmap */
412  GetObject(hbmp, sizeof(BITMAP), &bmp);
413 
414  st = avformat_new_stream(s1, NULL);
415  if (!st) {
416  ret = AVERROR(ENOMEM);
417  goto error;
418  }
419  avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
420 
421  gdigrab->frame_size = bmp.bmWidthBytes * bmp.bmHeight * bmp.bmPlanes;
422  gdigrab->header_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
423  (bpp <= 8 ? (1 << bpp) : 0) * sizeof(RGBQUAD) /* palette size */;
426 
427  gdigrab->hwnd = hwnd;
430  gdigrab->hbmp = hbmp;
431  gdigrab->bmi = bmi;
432  gdigrab->buffer = buffer;
434 
436 
437  if (gdigrab->show_region) {
439  ret = AVERROR(EIO);
440  goto error;
441  }
442  }
443 
445 
449 
450  return 0;
451 
452 error:
453  if (source_hdc)
454  ReleaseDC(hwnd, source_hdc);
455  if (dest_hdc)
456  DeleteDC(dest_hdc);
457  if (hbmp)
458  DeleteObject(hbmp);
459  if (source_hdc)
460  DeleteDC(source_hdc);
461  return ret;
462 }
463 
464 /**
465  * Paints a mouse pointer in a Win32 image.
466  *
467  * @param s1 Context of the log information
468  * @param s Current grad structure
469  */
471 {
472  CURSORINFO ci = {0};
473 
474 #define CURSOR_ERROR(str) \
475  if (!gdigrab->cursor_error_printed) { \
476  WIN32_API_ERROR(str); \
477  gdigrab->cursor_error_printed = 1; \
478  }
479 
480  ci.cbSize = sizeof(ci);
481 
482  if (GetCursorInfo(&ci)) {
483  HCURSOR icon = CopyCursor(ci.hCursor);
484  ICONINFO info;
485  POINT pos;
486  RECT clip_rect = gdigrab->clip_rect;
487  HWND hwnd = gdigrab->hwnd;
488  int horzres = GetDeviceCaps(gdigrab->source_hdc, HORZRES);
489  int vertres = GetDeviceCaps(gdigrab->source_hdc, VERTRES);
490  int desktophorzres = GetDeviceCaps(gdigrab->source_hdc, DESKTOPHORZRES);
491  int desktopvertres = GetDeviceCaps(gdigrab->source_hdc, DESKTOPVERTRES);
492  info.hbmMask = NULL;
493  info.hbmColor = NULL;
494 
495  if (ci.flags != CURSOR_SHOWING)
496  return;
497 
498  if (!icon) {
499  /* Use the standard arrow cursor as a fallback.
500  * You'll probably only hit this in Wine, which can't fetch
501  * the current system cursor. */
502  icon = CopyCursor(LoadCursor(NULL, IDC_ARROW));
503  }
504 
505  if (!GetIconInfo(icon, &info)) {
506  CURSOR_ERROR("Could not get icon info");
507  goto icon_error;
508  }
509 
510  if (hwnd) {
511  RECT rect;
512 
513  if (GetWindowRect(hwnd, &rect)) {
514  pos.x = ci.ptScreenPos.x - clip_rect.left - info.xHotspot - rect.left;
515  pos.y = ci.ptScreenPos.y - clip_rect.top - info.yHotspot - rect.top;
516 
517  //that would keep the correct location of mouse with hidpi screens
518  pos.x = pos.x * desktophorzres / horzres;
519  pos.y = pos.y * desktopvertres / vertres;
520  } else {
521  CURSOR_ERROR("Couldn't get window rectangle");
522  goto icon_error;
523  }
524  } else {
525  //that would keep the correct location of mouse with hidpi screens
526  pos.x = ci.ptScreenPos.x * desktophorzres / horzres - clip_rect.left - info.xHotspot;
527  pos.y = ci.ptScreenPos.y * desktopvertres / vertres - clip_rect.top - info.yHotspot;
528  }
529 
530  av_log(s1, AV_LOG_DEBUG, "Cursor pos (%li,%li) -> (%li,%li)\n",
531  ci.ptScreenPos.x, ci.ptScreenPos.y, pos.x, pos.y);
532 
533  if (pos.x >= 0 && pos.x <= clip_rect.right - clip_rect.left &&
534  pos.y >= 0 && pos.y <= clip_rect.bottom - clip_rect.top) {
535  if (!DrawIcon(gdigrab->dest_hdc, pos.x, pos.y, icon))
536  CURSOR_ERROR("Couldn't draw icon");
537  }
538 
539 icon_error:
540  if (info.hbmMask)
541  DeleteObject(info.hbmMask);
542  if (info.hbmColor)
543  DeleteObject(info.hbmColor);
544  if (icon)
545  DestroyCursor(icon);
546  } else {
547  CURSOR_ERROR("Couldn't get cursor info");
548  }
549 }
550 
551 /**
552  * Grabs a frame from gdi (public device demuxer API).
553  *
554  * @param s1 Context from avformat core
555  * @param pkt Packet holding the grabbed frame
556  * @return frame size in bytes
557  */
559 {
560  struct gdigrab *gdigrab = s1->priv_data;
561 
562  HDC dest_hdc = gdigrab->dest_hdc;
564  RECT clip_rect = gdigrab->clip_rect;
566  int64_t time_frame = gdigrab->time_frame;
567 
568  BITMAPFILEHEADER bfh;
569  int file_size = gdigrab->header_size + gdigrab->frame_size;
570 
571  int64_t curtime, delay;
572 
573  /* Calculate the time of the next frame */
574  time_frame += INT64_C(1000000);
575 
576  /* Run Window message processing queue */
577  if (gdigrab->show_region)
579 
580  /* wait based on the frame rate */
581  for (;;) {
582  curtime = av_gettime_relative();
583  delay = time_frame * av_q2d(time_base) - curtime;
584  if (delay <= 0) {
585  if (delay < INT64_C(-1000000) * av_q2d(time_base)) {
586  time_frame += INT64_C(1000000);
587  }
588  break;
589  }
590  if (s1->flags & AVFMT_FLAG_NONBLOCK) {
591  return AVERROR(EAGAIN);
592  } else {
593  av_usleep(delay);
594  }
595  }
596 
597  if (av_new_packet(pkt, file_size) < 0)
598  return AVERROR(ENOMEM);
599  pkt->pts = av_gettime();
600 
601  /* Blit screen grab */
602  if (!BitBlt(dest_hdc, 0, 0,
603  clip_rect.right - clip_rect.left,
604  clip_rect.bottom - clip_rect.top,
605  source_hdc,
606  clip_rect.left, clip_rect.top, SRCCOPY | CAPTUREBLT)) {
607  WIN32_API_ERROR("Failed to capture image");
608  return AVERROR(EIO);
609  }
610  if (gdigrab->draw_mouse)
612 
613  /* Copy bits to packet data */
614 
615  bfh.bfType = 0x4d42; /* "BM" in little-endian */
616  bfh.bfSize = file_size;
617  bfh.bfReserved1 = 0;
618  bfh.bfReserved2 = 0;
619  bfh.bfOffBits = gdigrab->header_size;
620 
621  memcpy(pkt->data, &bfh, sizeof(bfh));
622 
623  memcpy(pkt->data + sizeof(bfh), &gdigrab->bmi.bmiHeader, sizeof(gdigrab->bmi.bmiHeader));
624 
625  if (gdigrab->bmi.bmiHeader.biBitCount <= 8)
626  GetDIBColorTable(dest_hdc, 0, 1 << gdigrab->bmi.bmiHeader.biBitCount,
627  (RGBQUAD *) (pkt->data + sizeof(bfh) + sizeof(gdigrab->bmi.bmiHeader)));
628 
630 
632 
634 }
635 
636 /**
637  * Closes gdi frame grabber (public device demuxer API).
638  *
639  * @param s1 Context from avformat core
640  * @return 0 success, !0 failure
641  */
643 {
644  struct gdigrab *s = s1->priv_data;
645 
646  if (s->show_region)
648 
649  if (s->source_hdc)
650  ReleaseDC(s->hwnd, s->source_hdc);
651  if (s->dest_hdc)
652  DeleteDC(s->dest_hdc);
653  if (s->hbmp)
654  DeleteObject(s->hbmp);
655  if (s->source_hdc)
656  DeleteDC(s->source_hdc);
657 
658  return 0;
659 }
660 
661 #define OFFSET(x) offsetof(struct gdigrab, x)
662 #define DEC AV_OPT_FLAG_DECODING_PARAM
663 static const AVOption options[] = {
664  { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, DEC },
665  { "show_region", "draw border around capture area", OFFSET(show_region), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
666  { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, INT_MAX, DEC },
667  { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
668  { "offset_x", "capture area x offset", OFFSET(offset_x), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
669  { "offset_y", "capture area y offset", OFFSET(offset_y), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
670  { NULL },
671 };
672 
673 static const AVClass gdigrab_class = {
674  .class_name = "GDIgrab indev",
675  .item_name = av_default_item_name,
676  .option = options,
677  .version = LIBAVUTIL_VERSION_INT,
679 };
680 
681 /** gdi grabber device demuxer declaration */
683  .p.name = "gdigrab",
684  .p.long_name = NULL_IF_CONFIG_SMALL("GDI API Windows frame grabber"),
685  .p.flags = AVFMT_NOFILE,
686  .p.priv_class = &gdigrab_class,
687  .priv_data_size = sizeof(struct gdigrab),
689  .read_packet = gdigrab_read_packet,
690  .read_close = gdigrab_read_close,
691 };
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:186
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:51
gdigrab_region_wnd_destroy
static void gdigrab_region_wnd_destroy(AVFormatContext *s1, struct gdigrab *gdigrab)
Cleanup/free the region outline window.
Definition: gdigrab.c:190
gdigrab_read_packet
static int gdigrab_read_packet(AVFormatContext *s1, AVPacket *pkt)
Grabs a frame from gdi (public device demuxer API).
Definition: gdigrab.c:558
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
offset must point to AVRational
Definition: opt.h:248
rect
Definition: f_ebur128.c:77
gdigrab::offset_y
int offset_y
Capture y offset (private option)
Definition: gdigrab.c:57
AVPacket::data
uint8_t * data
Definition: packet.h:524
ff_gdigrab_demuxer
const FFInputFormat ff_gdigrab_demuxer
gdi grabber device demuxer declaration
Definition: gdigrab.c:682
AVOption
AVOption.
Definition: opt.h:346
AVStream::avg_frame_rate
AVRational avg_frame_rate
Average framerate.
Definition: avformat.h:832
gdigrab::time_frame
int64_t time_frame
Current time.
Definition: gdigrab.c:49
gdigrab::region_hwnd
HWND region_hwnd
Handle of the region border window.
Definition: gdigrab.c:67
OFFSET
#define OFFSET(x)
Definition: gdigrab.c:661
gdigrab::cursor_error_printed
int cursor_error_printed
Definition: gdigrab.c:69
DEC
#define DEC
Definition: gdigrab.c:662
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:853
gdigrab::frame_size
int frame_size
Size in bytes of the frame pixel data.
Definition: gdigrab.c:46
pkt
AVPacket * pkt
Definition: movenc.c:60
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
gdigrab::height
int height
Height of the grab frame (private option)
Definition: gdigrab.c:55
width
#define width
gdigrab_read_close
static int gdigrab_read_close(AVFormatContext *s1)
Closes gdi frame grabber (public device demuxer API).
Definition: gdigrab.c:642
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
AVInputFormat::name
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:553
s1
#define s1
Definition: regdef.h:38
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:201
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:72
AVFormatContext
Format I/O context.
Definition: avformat.h:1255
internal.h
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:766
framerate
float framerate
Definition: av1_levels.c:29
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
read_header
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:550
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:66
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:89
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
offset must point to two consecutive integers
Definition: opt.h:245
REGION_WND_BORDER
#define REGION_WND_BORDER
Definition: gdigrab.c:75
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:237
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:41
paint_mouse_pointer
static void paint_mouse_pointer(AVFormatContext *s1, struct gdigrab *gdigrab)
Paints a mouse pointer in a Win32 image.
Definition: gdigrab.c:470
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:225
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
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:673
AVFMT_NOFILE
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition: avformat.h:468
FFInputFormat::p
AVInputFormat p
The public AVInputFormat.
Definition: demux.h:41
gdigrab::hwnd
HWND hwnd
Handle of the window for the grab.
Definition: gdigrab.c:59
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:191
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:517
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:1409
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:743
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:71
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
gdigrab_region_wnd_init
static int gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
Initialize the region outline window.
Definition: gdigrab.c:124
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
Definition: opt.h:235
gdigrab
GDI Device Demuxer context.
Definition: gdigrab.c:43
gdigrab_region_wnd_update
static void gdigrab_region_wnd_update(AVFormatContext *s1, struct gdigrab *gdigrab)
Process the Windows message queue.
Definition: gdigrab.c:208
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:201
mem.h
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:55
AVPacket
This structure stores compressed data.
Definition: packet.h:501
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
FFInputFormat
Definition: demux.h:37
AVCodecParameters::bit_rate
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition: codec_par.h:97
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
options
static const AVOption options[]
Definition: gdigrab.c:663
gdigrab::width
int width
Width of the grab frame (private option)
Definition: gdigrab.c:54
CURSOR_ERROR
#define CURSOR_ERROR(str)