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