[FFmpeg-devel] [PATCH] Screen frame grabbing for Win32 platform (bump)

Ramiro Polla ramiro.polla
Fri Mar 19 21:09:12 CET 2010


Hi,

On Fri, Mar 19, 2010 at 3:43 PM, Christophe Gisquet
<christophe.gisquet at gmail.com> wrote:
> now for an even older thread bump:
> https://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2007-February/022360.html
>
> I've just adapted it to the new system and just left in the cursor capture in.
>
> It should be noted the frame grabbing itself is very slow and nothing
> much can be done about it, except going a _completely_ different way.

> Index: configure
> ===================================================================
> --- configure	(revision 22593)
> +++ configure	(working copy)
> @@ -96,6 +96,7 @@
>    --enable-pthreads        use pthreads [no]
>    --enable-w32threads      use Win32 threads [no]
>    --enable-x11grab         enable X11 grabbing [no]
> +  --enable-win32grab       enable Windows grabbing [no]

I'd prefer a name with gdi in it. And you don't need to add this
option to configure. There is an option for x11 because it is GPL'd,
so you need to enable it explicitly. You can also remove more stuff
below...

>    --disable-network        disable network support [no]
>    --disable-mpegaudio-hp   faster (but less accurate) MPEG audio decoding [no]
>    --enable-gray            enable full grayscale support (slower color)
> @@ -949,6 +950,7 @@
>      vdpau
>      version3
>      x11grab
> +    win32grab

...here...

>      zlib
>  "
>
> @@ -1403,6 +1405,8 @@
>  vfwcap_indev_extralibs="-lavicap32"
>  x11_grab_device_indev_deps="x11grab XShmCreateImage"
>  x11_grab_device_indev_extralibs="-lX11 -lXext -lXfixes"
> +win32_grab_device_indev_deps="win32grab GetDC"

...the win32grab dependency here...

> +win32_grab_device_indev_extralibs="-lgdi32"
>
>  # protocols
>  gopher_protocol_deps="network"
> @@ -2701,6 +2705,9 @@
>  check_func XShmCreateImage -lX11 -lXext &&
>  check_func XFixesGetCursorImage -lX11 -lXext -lXfixes
>
> +enabled win32grab                       &&

...and here.

> +check_func_headers "windows.h" GetDC "$win32_grab_device_indev_extralibs"
> +
>  if ! disabled vdpau && enabled vdpau_vdpau_h; then
>  check_cpp_condition \
>      vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||

> Index: doc/ffmpeg-doc.texi
> ===================================================================
> --- doc/ffmpeg-doc.texi	(revision 22593)
> +++ doc/ffmpeg-doc.texi	(working copy)
> @@ -57,6 +57,31 @@
>  0.0 is display.screen number of your X11 server, same as the DISPLAY environment
>  variable. 10 is the x-offset and 20 the y-offset for the grabbing.
>
> + at section Win32 grabbing
> +
> +FFmpeg can grab the Windows display through the old GDI interface.
> +
> + at example
> +ffmpeg -f win32grab -s cif /tmp/out.mpg

no -i?

[...]
> Index: libavdevice/win32grab.c
> ===================================================================
> --- libavdevice/win32grab.c	(revision 0)
> +++ libavdevice/win32grab.c	(revision 0)
[...]
> +/**
> + * Initializes the win32 grab device demuxer (public device demuxer API).
> + *
> + * Format: <option1>=<param1>:<options2>=<param2>
> + *  offset=%i,%i: offset on x and y against the final source window
> + *  cursor: grab cursor
> + *  title=%s\0: window caption, must be last option
> + *
> + * @param s1 Context from avformat core
> + * @param ap Parameters from avformat core
> + * @return AVERROR_IO error, 0 success
> + */
> +static int
> +win32grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
> +{
> +    win32_grab *s           = s1->priv_data;
> +    const char *param       = s1->filename;
> +    const char *name        = NULL;
> +    AVStream   *st          = NULL;
> +    int         input_pixfmt;
> +    long int    screenwidth;
> +    long int    screenheight;
> +    BITMAP      bmp;
> +
> +    if (!ap || ap->time_base.den <= 0) {
> +        av_log(s1, AV_LOG_ERROR, "AVParameters don't have rate. Use -r.\n");
> +        return AVERROR(EIO);
> +    }
> +
> +    do {

> +#undef printf
> +        printf("Now: %s\n", param);

Hmm...

> +        if (!strncmp(param, "offset=", 7))
> +            sscanf(param+7, "%i,%i", &s->x_off, &s->y_off);

If the user specifies a window, an offset will make it capture past
the end of the window.

> +        else if (!strncmp(param, "cursor", 6))
> +            s->cursor = 1;
> +        else if (!strncmp(param, "title=", 6)) {
> +            name = param+6;
> +            s->window_handle = FindWindow(NULL, name);

> +        }
> +        param++;
> +    } while ((param = strchr(param, ':')+1) != 1);
> +
> +    s->source_hdc = GetDC(s->window_handle);

If the window is not found above, GetDC will return the Desktop. IMO
it should fail if the window can't be found.

> +    if (NULL == s->source_hdc) {

The "s->source_hdc == errorcode" order is preferred, but in this case
it should be if (!s->source_hdc). Did you run the patch through
patcheck?

> +        WIN32_API_ERROR("Couldn't get window DC");
> +        return AVERROR(EIO);
> +    }
> +
> +    screenwidth  = GetDeviceCaps(s->source_hdc, HORZRES);
> +    screenheight = GetDeviceCaps(s->source_hdc, VERTRES);
> +    if (s->window_handle) {
> +        RECT dim;
> +
> +        GetClientRect(s->window_handle, &dim);
> +        s->width  = dim.right-dim.left;
> +        s->height = dim.bottom-dim.top;
> +    }
> +    else {
> +        s->width  = screenwidth;
> +        s->height = screenheight;
> +    }
> +
> +    if (s->x_off + s->width > screenwidth)
> +        s->width  = screenwidth-s->x_off;
> +    if (s->y_off + s->height > screenheight)
> +        s->height = screenheight-s->y_off;
> +
> +    s->bpp = GetDeviceCaps(s->source_hdc, BITSPIXEL);
> +
> +    av_log(s1, AV_LOG_INFO, "Found window %s, capturing %ix%ix%i at (%i,%i)\n",
> +           (name) ? name : "NONE", s->width, s->height, s->bpp,
> +           s->x_off, s->y_off);
> +    if (s->width<0 || s->height<0 || s->bpp%8) {
> +        av_log(s1, AV_LOG_ERROR, "Invalid properties, aborting\n");
> +        return AVERROR(EIO);
> +    }
> +    if (ap->width>s->width || ap->height>s->height) {

Spaces could help readability here around > and <.

> +        av_log(s1, AV_LOG_ERROR, "Can't force %ix%i resolution, aborting.\n",
> +               ap->width, ap->height);

The error message should mention that the specified width/height are
greater than that of the window.

[...]
> +/**
> + * Paints a mouse pointer in an Win32 image.
> + *
> + * @param image image to paint the mouse pointer to
> + * @param s context used to retrieve original grabbing rectangle
> + *          coordinates
> + * @param x Mouse pointer coordinate
> + * @param y Mouse pointer coordinate
> + */
> +static void
> +paint_mouse_pointer(AVFormatContext *s1, win32_grab *s)
> +{
> +    CURSORINFO ci;
> +
> +    ci.cbSize = sizeof(ci);
> +
> +    if (GetCursorInfo(&ci) && ci.flags == CURSOR_SHOWING) {
> +        HICON     icon = CopyIcon(ci.hCursor);
> +        ICONINFO  info;
> +        if(GetIconInfo(icon, &info)) {
> +            long int x = ci.ptScreenPos.x - info.xHotspot;
> +            long int y = ci.ptScreenPos.y - info.yHotspot;
> +
> +            if (s->window_handle) {
> +                RECT rect;
> +
> +                if (GetWindowRect(s->window_handle, &rect)) {
> +                    av_log(s1, AV_LOG_DEBUG, "Pos(%li,%li) -> (%li,%li)\n",
> +                           x, y, x - rect.left, y - rect.top);
> +                    x -= rect.left;
> +                    y -= rect.top;
> +                }
> +                else {
> +                    WIN32_API_ERROR("Couldn't get icon rectangle");
> +                }
> +            }
> +
> +            if (!DrawIcon(s->window_hdc, x, y, icon))
> +                WIN32_API_ERROR("Couldn't draw icon");
> +        }
> +        else {
> +            WIN32_API_ERROR("Couldn't get cursor info");
> +        }
> +
> +        DestroyIcon(icon);
> +    }
> +    else
> +        WIN32_API_ERROR("Cursor not showing?");
> +}

Won't these errors spam the console for every frame in case it always fails?

> +/**
> + * Grabs a frame from win32 (public device demuxer API).
> + *
> + * @param s1 Context from avformat core
> + * @param pkt Packet holding the brabbed frame
> + * @return frame size in bytes
> + */
> +static int
> +win32grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
> +{
> +    win32_grab *s = s1->priv_data;
> +    int64_t    curtime, delay;
> +
> +    /* Calculate the time of the next frame */
> +    s->time_frame += INT64_C(1000000);
> +
> +    /* wait based on the frame rate */
> +    for(;;) {
> +        curtime = av_gettime();
> +        delay = s->time_frame * av_q2d(s->time_base) - curtime;
> +        if (delay <= 0) {
> +            if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
> +                s->time_frame += INT64_C(1000000);
> +            }
> +            break;
> +        }
> +        Sleep(delay/1000);
> +    }

You should also support nonblocking reads.

[...]



More information about the ffmpeg-devel mailing list