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