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