Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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.  
  66.     int cursor_error_printed;
  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
  121. gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
  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
  187. gdigrab_region_wnd_destroy(AVFormatContext *s1, struct gdigrab *gdigrab)
  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
  205. gdigrab_region_wnd_update(AVFormatContext *s1, struct gdigrab *gdigrab)
  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
  222. gdigrab_read_header(AVFormatContext *s1)
  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->filename;
  234.     const char *name     = NULL;
  235.     AVStream   *st       = NULL;
  236.  
  237.     int bpp;
  238.     RECT virtual_rect;
  239.     RECT clip_rect;
  240.     BITMAP bmp;
  241.     int ret;
  242.  
  243.     if (!strncmp(filename, "title=", 6)) {
  244.         name = filename + 6;
  245.         hwnd = FindWindow(NULL, name);
  246.         if (!hwnd) {
  247.             av_log(s1, AV_LOG_ERROR,
  248.                    "Can't find window '%s', aborting.\n", name);
  249.             ret = AVERROR(EIO);
  250.             goto error;
  251.         }
  252.         if (gdigrab->show_region) {
  253.             av_log(s1, AV_LOG_WARNING,
  254.                     "Can't show region when grabbing a window.\n");
  255.             gdigrab->show_region = 0;
  256.         }
  257.     } else if (!strcmp(filename, "desktop")) {
  258.         hwnd = NULL;
  259.     } else {
  260.         av_log(s1, AV_LOG_ERROR,
  261.                "Please use \"desktop\" or \"title=<windowname>\" to specify your target.\n");
  262.         ret = AVERROR(EIO);
  263.         goto error;
  264.     }
  265.  
  266.     if (hwnd) {
  267.         GetClientRect(hwnd, &virtual_rect);
  268.     } else {
  269.         virtual_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
  270.         virtual_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
  271.         virtual_rect.right = virtual_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
  272.         virtual_rect.bottom = virtual_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
  273.     }
  274.  
  275.     /* If no width or height set, use full screen/window area */
  276.     if (!gdigrab->width || !gdigrab->height) {
  277.         clip_rect.left = virtual_rect.left;
  278.         clip_rect.top = virtual_rect.top;
  279.         clip_rect.right = virtual_rect.right;
  280.         clip_rect.bottom = virtual_rect.bottom;
  281.     } else {
  282.         clip_rect.left = gdigrab->offset_x;
  283.         clip_rect.top = gdigrab->offset_y;
  284.         clip_rect.right = gdigrab->width + gdigrab->offset_x;
  285.         clip_rect.bottom = gdigrab->height + gdigrab->offset_y;
  286.     }
  287.  
  288.     if (clip_rect.left < virtual_rect.left ||
  289.             clip_rect.top < virtual_rect.top ||
  290.             clip_rect.right > virtual_rect.right ||
  291.             clip_rect.bottom > virtual_rect.bottom) {
  292.             av_log(s1, AV_LOG_ERROR,
  293.                     "Capture area (%li,%li),(%li,%li) extends outside window area (%li,%li),(%li,%li)",
  294.                     clip_rect.left, clip_rect.top,
  295.                     clip_rect.right, clip_rect.bottom,
  296.                     virtual_rect.left, virtual_rect.top,
  297.                     virtual_rect.right, virtual_rect.bottom);
  298.             ret = AVERROR(EIO);
  299.             goto error;
  300.     }
  301.  
  302.     /* This will get the device context for the selected window, or if
  303.      * none, the primary screen */
  304.     source_hdc = GetDC(hwnd);
  305.     if (!source_hdc) {
  306.         WIN32_API_ERROR("Couldn't get window device context");
  307.         ret = AVERROR(EIO);
  308.         goto error;
  309.     }
  310.     bpp = GetDeviceCaps(source_hdc, BITSPIXEL);
  311.  
  312.     if (name) {
  313.         av_log(s1, AV_LOG_INFO,
  314.                "Found window %s, capturing %lix%lix%i at (%li,%li)\n",
  315.                name,
  316.                clip_rect.right - clip_rect.left,
  317.                clip_rect.bottom - clip_rect.top,
  318.                bpp, clip_rect.left, clip_rect.top);
  319.     } else {
  320.         av_log(s1, AV_LOG_INFO,
  321.                "Capturing whole desktop as %lix%lix%i at (%li,%li)\n",
  322.                clip_rect.right - clip_rect.left,
  323.                clip_rect.bottom - clip_rect.top,
  324.                bpp, clip_rect.left, clip_rect.top);
  325.     }
  326.  
  327.     if (clip_rect.right - clip_rect.left <= 0 ||
  328.             clip_rect.bottom - clip_rect.top <= 0 || bpp%8) {
  329.         av_log(s1, AV_LOG_ERROR, "Invalid properties, aborting\n");
  330.         ret = AVERROR(EIO);
  331.         goto error;
  332.     }
  333.  
  334.     dest_hdc = CreateCompatibleDC(source_hdc);
  335.     if (!dest_hdc) {
  336.         WIN32_API_ERROR("Screen DC CreateCompatibleDC");
  337.         ret = AVERROR(EIO);
  338.         goto error;
  339.     }
  340.  
  341.     /* Create a DIB and select it into the dest_hdc */
  342.     bmi.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
  343.     bmi.bmiHeader.biWidth         = clip_rect.right - clip_rect.left;
  344.     bmi.bmiHeader.biHeight        = -(clip_rect.bottom - clip_rect.top);
  345.     bmi.bmiHeader.biPlanes        = 1;
  346.     bmi.bmiHeader.biBitCount      = bpp;
  347.     bmi.bmiHeader.biCompression   = BI_RGB;
  348.     bmi.bmiHeader.biSizeImage     = 0;
  349.     bmi.bmiHeader.biXPelsPerMeter = 0;
  350.     bmi.bmiHeader.biYPelsPerMeter = 0;
  351.     bmi.bmiHeader.biClrUsed       = 0;
  352.     bmi.bmiHeader.biClrImportant  = 0;
  353.     hbmp = CreateDIBSection(dest_hdc, &bmi, DIB_RGB_COLORS,
  354.             &buffer, NULL, 0);
  355.     if (!hbmp) {
  356.         WIN32_API_ERROR("Creating DIB Section");
  357.         ret = AVERROR(EIO);
  358.         goto error;
  359.     }
  360.  
  361.     if (!SelectObject(dest_hdc, hbmp)) {
  362.         WIN32_API_ERROR("SelectObject");
  363.         ret = AVERROR(EIO);
  364.         goto error;
  365.     }
  366.  
  367.     /* Get info from the bitmap */
  368.     GetObject(hbmp, sizeof(BITMAP), &bmp);
  369.  
  370.     st = avformat_new_stream(s1, NULL);
  371.     if (!st) {
  372.         ret = AVERROR(ENOMEM);
  373.         goto error;
  374.     }
  375.     avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
  376.  
  377.     gdigrab->frame_size  = bmp.bmWidthBytes * bmp.bmHeight * bmp.bmPlanes;
  378.     gdigrab->header_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
  379.                            (bpp <= 8 ? (1 << bpp) : 0) * sizeof(RGBQUAD) /* palette size */;
  380.     gdigrab->time_base   = av_inv_q(gdigrab->framerate);
  381.     gdigrab->time_frame  = av_gettime() / av_q2d(gdigrab->time_base);
  382.  
  383.     gdigrab->hwnd       = hwnd;
  384.     gdigrab->source_hdc = source_hdc;
  385.     gdigrab->dest_hdc   = dest_hdc;
  386.     gdigrab->hbmp       = hbmp;
  387.     gdigrab->bmi        = bmi;
  388.     gdigrab->buffer     = buffer;
  389.     gdigrab->clip_rect  = clip_rect;
  390.  
  391.     gdigrab->cursor_error_printed = 0;
  392.  
  393.     if (gdigrab->show_region) {
  394.         if (gdigrab_region_wnd_init(s1, gdigrab)) {
  395.             ret = AVERROR(EIO);
  396.             goto error;
  397.         }
  398.     }
  399.  
  400.     st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
  401.     st->codec->codec_id   = AV_CODEC_ID_BMP;
  402.     st->codec->time_base  = gdigrab->time_base;
  403.     st->codec->bit_rate   = (gdigrab->header_size + gdigrab->frame_size) * 1/av_q2d(gdigrab->time_base) * 8;
  404.  
  405.     return 0;
  406.  
  407. error:
  408.     if (source_hdc)
  409.         ReleaseDC(hwnd, source_hdc);
  410.     if (dest_hdc)
  411.         DeleteDC(dest_hdc);
  412.     if (hbmp)
  413.         DeleteObject(hbmp);
  414.     if (source_hdc)
  415.         DeleteDC(source_hdc);
  416.     return ret;
  417. }
  418.  
  419. /**
  420.  * Paints a mouse pointer in a Win32 image.
  421.  *
  422.  * @param s1 Context of the log information
  423.  * @param s  Current grad structure
  424.  */
  425. static void paint_mouse_pointer(AVFormatContext *s1, struct gdigrab *gdigrab)
  426. {
  427.     CURSORINFO ci = {0};
  428.  
  429. #define CURSOR_ERROR(str)                 \
  430.     if (!gdigrab->cursor_error_printed) {       \
  431.         WIN32_API_ERROR(str);             \
  432.         gdigrab->cursor_error_printed = 1;      \
  433.     }
  434.  
  435.     ci.cbSize = sizeof(ci);
  436.  
  437.     if (GetCursorInfo(&ci)) {
  438.         HCURSOR icon = CopyCursor(ci.hCursor);
  439.         ICONINFO info;
  440.         POINT pos;
  441.         RECT clip_rect = gdigrab->clip_rect;
  442.         HWND hwnd = gdigrab->hwnd;
  443.         info.hbmMask = NULL;
  444.         info.hbmColor = NULL;
  445.  
  446.         if (ci.flags != CURSOR_SHOWING)
  447.             return;
  448.  
  449.         if (!icon) {
  450.             /* Use the standard arrow cursor as a fallback.
  451.              * You'll probably only hit this in Wine, which can't fetch
  452.              * the current system cursor. */
  453.             icon = CopyCursor(LoadCursor(NULL, IDC_ARROW));
  454.         }
  455.  
  456.         if (!GetIconInfo(icon, &info)) {
  457.             CURSOR_ERROR("Could not get icon info");
  458.             goto icon_error;
  459.         }
  460.  
  461.         pos.x = ci.ptScreenPos.x - clip_rect.left - info.xHotspot;
  462.         pos.y = ci.ptScreenPos.y - clip_rect.top - info.yHotspot;
  463.  
  464.         if (hwnd) {
  465.             RECT rect;
  466.  
  467.             if (GetWindowRect(hwnd, &rect)) {
  468.                 pos.x -= rect.left;
  469.                 pos.y -= rect.top;
  470.             } else {
  471.                 CURSOR_ERROR("Couldn't get window rectangle");
  472.                 goto icon_error;
  473.             }
  474.         }
  475.  
  476.         av_log(s1, AV_LOG_DEBUG, "Cursor pos (%li,%li) -> (%li,%li)\n",
  477.                 ci.ptScreenPos.x, ci.ptScreenPos.y, pos.x, pos.y);
  478.  
  479.         if (pos.x >= 0 && pos.x <= clip_rect.right - clip_rect.left &&
  480.                 pos.y >= 0 && pos.y <= clip_rect.bottom - clip_rect.top) {
  481.             if (!DrawIcon(gdigrab->dest_hdc, pos.x, pos.y, icon))
  482.                 CURSOR_ERROR("Couldn't draw icon");
  483.         }
  484.  
  485. icon_error:
  486.         if (info.hbmMask)
  487.             DeleteObject(info.hbmMask);
  488.         if (info.hbmColor)
  489.             DeleteObject(info.hbmColor);
  490.         if (icon)
  491.             DestroyCursor(icon);
  492.     } else {
  493.         CURSOR_ERROR("Couldn't get cursor info");
  494.     }
  495. }
  496.  
  497. /**
  498.  * Grabs a frame from gdi (public device demuxer API).
  499.  *
  500.  * @param s1 Context from avformat core
  501.  * @param pkt Packet holding the grabbed frame
  502.  * @return frame size in bytes
  503.  */
  504. static int gdigrab_read_packet(AVFormatContext *s1, AVPacket *pkt)
  505. {
  506.     struct gdigrab *gdigrab = s1->priv_data;
  507.  
  508.     HDC        dest_hdc   = gdigrab->dest_hdc;
  509.     HDC        source_hdc = gdigrab->source_hdc;
  510.     RECT       clip_rect  = gdigrab->clip_rect;
  511.     AVRational time_base  = gdigrab->time_base;
  512.     int64_t    time_frame = gdigrab->time_frame;
  513.  
  514.     BITMAPFILEHEADER bfh;
  515.     int file_size = gdigrab->header_size + gdigrab->frame_size;
  516.  
  517.     int64_t curtime, delay;
  518.  
  519.     /* Calculate the time of the next frame */
  520.     time_frame += INT64_C(1000000);
  521.  
  522.     /* Run Window message processing queue */
  523.     if (gdigrab->show_region)
  524.         gdigrab_region_wnd_update(s1, gdigrab);
  525.  
  526.     /* wait based on the frame rate */
  527.     for (;;) {
  528.         curtime = av_gettime();
  529.         delay = time_frame * av_q2d(time_base) - curtime;
  530.         if (delay <= 0) {
  531.             if (delay < INT64_C(-1000000) * av_q2d(time_base)) {
  532.                 time_frame += INT64_C(1000000);
  533.             }
  534.             break;
  535.         }
  536.         if (s1->flags & AVFMT_FLAG_NONBLOCK) {
  537.             return AVERROR(EAGAIN);
  538.         } else {
  539.             av_usleep(delay);
  540.         }
  541.     }
  542.  
  543.     if (av_new_packet(pkt, file_size) < 0)
  544.         return AVERROR(ENOMEM);
  545.     pkt->pts = curtime;
  546.  
  547.     /* Blit screen grab */
  548.     if (!BitBlt(dest_hdc, 0, 0,
  549.                 clip_rect.right - clip_rect.left,
  550.                 clip_rect.bottom - clip_rect.top,
  551.                 source_hdc,
  552.                 clip_rect.left, clip_rect.top, SRCCOPY | CAPTUREBLT)) {
  553.         WIN32_API_ERROR("Failed to capture image");
  554.         return AVERROR(EIO);
  555.     }
  556.     if (gdigrab->draw_mouse)
  557.         paint_mouse_pointer(s1, gdigrab);
  558.  
  559.     /* Copy bits to packet data */
  560.  
  561.     bfh.bfType = 0x4d42; /* "BM" in little-endian */
  562.     bfh.bfSize = file_size;
  563.     bfh.bfReserved1 = 0;
  564.     bfh.bfReserved2 = 0;
  565.     bfh.bfOffBits = gdigrab->header_size;
  566.  
  567.     memcpy(pkt->data, &bfh, sizeof(bfh));
  568.  
  569.     memcpy(pkt->data + sizeof(bfh), &gdigrab->bmi.bmiHeader, sizeof(gdigrab->bmi.bmiHeader));
  570.  
  571.     if (gdigrab->bmi.bmiHeader.biBitCount <= 8)
  572.         GetDIBColorTable(dest_hdc, 0, 1 << gdigrab->bmi.bmiHeader.biBitCount,
  573.                 (RGBQUAD *) (pkt->data + sizeof(bfh) + sizeof(gdigrab->bmi.bmiHeader)));
  574.  
  575.     memcpy(pkt->data + gdigrab->header_size, gdigrab->buffer, gdigrab->frame_size);
  576.  
  577.     gdigrab->time_frame = time_frame;
  578.  
  579.     return gdigrab->header_size + gdigrab->frame_size;
  580. }
  581.  
  582. /**
  583.  * Closes gdi frame grabber (public device demuxer API).
  584.  *
  585.  * @param s1 Context from avformat core
  586.  * @return 0 success, !0 failure
  587.  */
  588. static int gdigrab_read_close(AVFormatContext *s1)
  589. {
  590.     struct gdigrab *s = s1->priv_data;
  591.  
  592.     if (s->show_region)
  593.         gdigrab_region_wnd_destroy(s1, s);
  594.  
  595.     if (s->source_hdc)
  596.         ReleaseDC(s->hwnd, s->source_hdc);
  597.     if (s->dest_hdc)
  598.         DeleteDC(s->dest_hdc);
  599.     if (s->hbmp)
  600.         DeleteObject(s->hbmp);
  601.     if (s->source_hdc)
  602.         DeleteDC(s->source_hdc);
  603.  
  604.     return 0;
  605. }
  606.  
  607. #define OFFSET(x) offsetof(struct gdigrab, x)
  608. #define DEC AV_OPT_FLAG_DECODING_PARAM
  609. static const AVOption options[] = {
  610.     { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, DEC },
  611.     { "show_region", "draw border around capture area", OFFSET(show_region), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
  612.     { "framerate", "set video frame rate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, 0, DEC },
  613.     { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC },
  614.     { "offset_x", "capture area x offset", OFFSET(offset_x), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
  615.     { "offset_y", "capture area y offset", OFFSET(offset_y), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
  616.     { NULL },
  617. };
  618.  
  619. static const AVClass gdigrab_class = {
  620.     .class_name = "GDIgrab indev",
  621.     .item_name  = av_default_item_name,
  622.     .option     = options,
  623.     .version    = LIBAVUTIL_VERSION_INT,
  624. };
  625.  
  626. /** gdi grabber device demuxer declaration */
  627. AVInputFormat ff_gdigrab_demuxer = {
  628.     .name           = "gdigrab",
  629.     .long_name      = NULL_IF_CONFIG_SMALL("GDI API Windows frame grabber"),
  630.     .priv_data_size = sizeof(struct gdigrab),
  631.     .read_header    = gdigrab_read_header,
  632.     .read_packet    = gdigrab_read_packet,
  633.     .read_close     = gdigrab_read_close,
  634.     .flags          = AVFMT_NOFILE,
  635.     .priv_class     = &gdigrab_class,
  636. };
  637.