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