[FFmpeg-devel] [PATCH] VFW capture support

Måns Rullgård mans
Mon Mar 3 00:15:21 CET 2008


Ramiro Polla <ramiro at lisha.ufsc.br> writes:

> Hello,
>
> Attached patch adds VFW capture support for Windows. Tested on MinGW
> and IIRC V?ctor said once it kind of worked on Cygwin.
>
> Issues:
> - I tried to make the code look as much as possible as the code given
> in MSDN, so that means I used "LRESULT CALLBACK" without even knowing
> what it does or if it can be removed.

I'm afraid that's unacceptable in FFmpeg.  Please find out what it
does.

> I have also used "var == FALSE" and "var == NULL" checks. I prefer
> that instead of "!var" just to be closer to MSDN.

Using "var == FALSE", and "var == TRUE" more so, not only not our
style, it's plain stupid.  Being closer to msdn is hardly a desirable
goal.

> - Some defines are missing from MinGW include headers. I'll try to get
> them integrated into MinGW. After that, we can properly check for
> MinGW versions to define them, or just plain remove the definitions
> here and require a newer version of MinGW. In the meantime, I think
> there's no problem in defining them here.

Are these values guaranteed to be correct for all Windows versions?
If not, this is not acceptable.

> - If no "-r" parameter is given, way too many frames are read. I
> thought FFmpeg was supposed to read the first few frames and decide
> the rate based on that. The capture blocks, so the timestamps are
> correct, and if some frames were captured and then the frame rate
> calculated, it should find the correct frame rate, right?

FFmpeg does not estimate frame rate based on elapsed real time,
AFAIK.

> Please review.

I am shocked an appalled by the ugly casting required by the win32
API, but there's of course nothing we can do about that.

> Index: configure
> ===================================================================
> --- configure	(revision 12291)
> +++ configure	(working copy)
> @@ -848,6 +848,8 @@
>  sdp_demuxer_deps="rtp_protocol mpegts_demuxer"
>  v4l2_demuxer_deps="linux_videodev2_h"
>  v4l_demuxer_deps="linux_videodev_h"
> +vfwcap_demuxer_deps="capCreateCaptureWindow"
> +vfwcap_demuxer_extralibs="-lvfw32"
>  x11_grab_device_demuxer_deps="x11grab XShmCreateImage"
>  x11_grab_device_demuxer_extralibs="-lX11 -lXext"
>
> @@ -1776,6 +1778,8 @@
>  check_header linux/videodev.h
>  check_header linux/videodev2.h
>
> +check_func2 "windows.h vfw.h" capCreateCaptureWindow -lvfw32
> +
>  # check for ioctl_meteor.h, ioctl_bt848.h and alternatives
>  { check_header dev/bktr/ioctl_meteor.h &&
>    check_header dev/bktr/ioctl_bt848.h; } ||

OK

> Index: libavdevice/alldevices.c
> ===================================================================
> --- libavdevice/alldevices.c	(revision 12291)
> +++ libavdevice/alldevices.c	(working copy)
> @@ -43,6 +43,7 @@
>      REGISTER_MUXDEMUX (OSS, oss);
>      REGISTER_DEMUXER  (V4L2, v4l2);
>      REGISTER_DEMUXER  (V4L, v4l);
> +    REGISTER_DEMUXER  (VFWCAP, vfwcap);
>      REGISTER_DEMUXER  (X11_GRAB_DEVICE, x11_grab_device);
>
>      /* external libraries */

OK

> Index: libavdevice/Makefile
> ===================================================================
> --- libavdevice/Makefile	(revision 12291)
> +++ libavdevice/Makefile	(working copy)
> @@ -21,6 +21,7 @@
>  OBJS-$(CONFIG_OSS_MUXER)                 += audio.o
>  OBJS-$(CONFIG_V4L2_DEMUXER)              += v4l2.o
>  OBJS-$(CONFIG_V4L_DEMUXER)               += v4l.o
> +OBJS-$(CONFIG_VFWCAP_DEMUXER)            += vfwcap.o
>  OBJS-$(CONFIG_X11_GRAB_DEVICE_DEMUXER)   += x11grab.o
>
>  # external libraries
>

OK

> /*
>  * VFW capture interface
>  * Copyright (c) 2006-2008 Ramiro Polla.
>  *
>  * This file is part of FFmpeg.
>  *
>  * FFmpeg is free software; you can redistribute it and/or
>  * modify it under the terms of the GNU Lesser General Public
>  * License as published by the Free Software Foundation; either
>  * version 2.1 of the License, or (at your option) any later version.
>  *
>  * FFmpeg is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>  * Lesser General Public License for more details.
>  *
>  * You should have received a copy of the GNU Lesser General Public
>  * License along with FFmpeg; if not, write to the Free Software
>  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>  */
>
> #include "avformat.h"
> #include <vfw.h>
> #include <windows.h>
>
> /* Defines for VFW missing from MinGW.
>  * Remove this when MinGW incorporates them. */
> #define WM_CAP_START                (0x0400)
> #define WM_CAP_SET_CALLBACK_FRAME   (WM_CAP_START + 5)
> #define WM_CAP_GET_USER_DATA        (WM_CAP_START + 8)
> #define WM_CAP_SET_USER_DATA        (WM_CAP_START + 9)
> #define WM_CAP_DRIVER_CONNECT       (WM_CAP_START + 10)
> #define WM_CAP_DRIVER_DISCONNECT    (WM_CAP_START + 11)
> #define WM_CAP_GRAB_FRAME           (WM_CAP_START + 60)
> #define WM_CAP_GET_VIDEOFORMAT      (WM_CAP_START + 44)
> #define WM_CAP_SET_PREVIEW          (WM_CAP_START + 50)
> #define WM_CAP_SET_OVERLAY          (WM_CAP_START + 51)
>
> #define HWND_MESSAGE                ((HWND)-3)
>
> typedef struct videohdr_tag {
>     LPBYTE      lpData;
>     DWORD       dwBufferLength;
>     DWORD       dwBytesUsed;
>     DWORD       dwTimeCaptured;
>     DWORD       dwUser;
>     DWORD       dwFlags;
>     DWORD_PTR   dwReserved[4];
> } VIDEOHDR, NEAR *PVIDEOHDR, FAR * LPVIDEOHDR;
> /* End of missing MinGW defines */

As I said, can we be sure these are always correct?

> struct vfw_ctx {
>     HWND hwnd;
>     int grabbed;
>     AVPacket *pkt;
> };
>
> static int vfw_pixfmt( DWORD biCompression )

Do we really have to use those dreadful windows typedefs and naming
conventions?

It is preferred to have no whitespace immediately inside () in FFmpeg.

> {
>     switch( biCompression ) {
>     case MKTAG( 'Y', 'U', 'Y', '2' ):
>         return PIX_FMT_YUYV422;
>     default:
>         return PIX_FMT_BGR24;
>     }

Is this really complete and correct?

> }
>
> static void dump_bih( AVFormatContext *s, BITMAPINFO *bih )
> {
>     av_log( s, AV_LOG_DEBUG, " biSize:\t%d\n", (int) bih->bmiHeader.biSize );
>     av_log( s, AV_LOG_DEBUG, " biWidth:\t%d\n", (int) bih->bmiHeader.biWidth );
>     av_log( s, AV_LOG_DEBUG, " biHeight:\t%d\n", (int) bih->bmiHeader.biHeight );
>     av_log( s, AV_LOG_DEBUG, " biPlanes:\t%d\n", (int) bih->bmiHeader.biPlanes );
>     av_log( s, AV_LOG_DEBUG, " biBitCount:\t%d\n", (int) bih->bmiHeader.biBitCount );
>     av_log( s, AV_LOG_DEBUG, " biCompression:\t%.4s\n", (char*) &bih->bmiHeader.biCompression );
>     av_log( s, AV_LOG_DEBUG, " biSizeImage:\t%d\n", (int) bih->bmiHeader.biSizeImage );
>     av_log( s, AV_LOG_DEBUG, " biXPelsPerMeter:\t%d\n", (int) bih->bmiHeader.biXPelsPerMeter );
>     av_log( s, AV_LOG_DEBUG, " biYPelsPerMeter:\t%d\n", (int) bih->bmiHeader.biYPelsPerMeter );
>     av_log( s, AV_LOG_DEBUG, " biClrUsed:\t%d\n", (int) bih->bmiHeader.biClrUsed );
>     av_log( s, AV_LOG_DEBUG, " biClrImportant:\t%d\n", (int) bih->bmiHeader.biClrImportant );

All those (int) casts are unnecessary.  If any of the fields are
unsigned, the corresponding format specifiers should be %u.

> }
>
> static LRESULT CALLBACK FrameCallbackProc( HWND hwnd, LPVIDEOHDR vdhdr )
> {
>     struct vfw_ctx *ctx;
>     AVPacket *pkt;
>
>     ctx = (struct vfw_ctx *) SendMessage( hwnd, WM_CAP_GET_USER_DATA, 0, 0 );

Call a function named *send* something to *get* data?  How fucked up
is that?

>     if( ctx == FALSE )
>         return (LRESULT) FALSE;

Useless cast.

>     pkt = ctx->pkt;
>
>     if( av_new_packet( pkt, vdhdr->dwBytesUsed ) < 0 )
>         return (LRESULT) FALSE;

Useless cast.

>     pkt->pts = GetTickCount( );
>     memcpy( pkt->data, vdhdr->lpData, vdhdr->dwBytesUsed );

Is it really not possible to capture without this memcpy()?  I'm not
surprised if that's how crap it is, of course.

>     ctx->grabbed = 1;
>
>     return (LRESULT) TRUE;

Useless cast.

> }
>
> static int vfw_read_header( AVFormatContext *s, AVFormatParameters *ap )
> {
>     struct vfw_ctx *ctx = s->priv_data;
>     AVCodecContext *codec;
>     AVStream *st;
>     int devnum, ret;
>     BITMAPINFO *bih;
>
>     if( s->flags & AVFMT_FLAG_NONBLOCK ) {
>         av_log( s, AV_LOG_ERROR, "Non blocking capture not yet implemented.\n" );
>         return AVERROR_IO;
>     }
>
>     ctx->hwnd = capCreateCaptureWindow( NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0 );
>     if( ctx->hwnd == NULL ) {
>         av_log( s, AV_LOG_ERROR, "Could not create capture window.\n" );
>         return AVERROR_IO;
>     }
>
>     /* If atoi fails, devnum==0 and the default device is used */
>     devnum = atoi( s->filename );
>
>     ret = SendMessage( ctx->hwnd, WM_CAP_DRIVER_CONNECT, devnum, 0 );
>     if( ret == FALSE ) {
>         av_log( s, AV_LOG_ERROR, "Could not connect to device.\n" );
>         return AVERROR_IO;
>     }
>
>     SendMessage( ctx->hwnd, WM_CAP_SET_OVERLAY, 0, 0 );
>     SendMessage( ctx->hwnd, WM_CAP_SET_PREVIEW, 0, 0 );

Error checking?

>     ret = SendMessage( ctx->hwnd, WM_CAP_SET_CALLBACK_FRAME, 0,
>                        (LPARAM) FrameCallbackProc );
>     if( ret == FALSE )
>         return AVERROR_IO;
>
>     ret = SendMessage( ctx->hwnd, WM_CAP_SET_USER_DATA, 0, (LPARAM) ctx );
>     if( ret == FALSE )
>         return AVERROR_IO;
>
>     st = av_new_stream( s, 0 );
>     if ( !st )
>         return AVERROR_NOMEM;
>
>     ret = SendMessage( ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, (WPARAM) 0,
>                       (LPARAM) 0 );

Useless casts.

>     if( !ret )
>         return AVERROR_NOMEM;
>     bih = av_malloc( ret );

Missing check of av_malloc() return value.

>     SendMessage( ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, (WPARAM) ret,
>                  (LPARAM) bih );

Error checking?

>     dump_bih( s, bih );
>
>     codec = st->codec;
>     codec->time_base = ap->time_base;
>     codec->codec_type = CODEC_TYPE_VIDEO;
>     codec->width = bih->bmiHeader.biWidth;
>     codec->height = bih->bmiHeader.biHeight;
>     codec->codec_id = CODEC_ID_RAWVIDEO;
>     codec->pix_fmt = vfw_pixfmt( bih->bmiHeader.biCompression );
>
>     av_set_pts_info( st, 32, 1, 1000 );
>
>     av_free( bih );
>
>     return 0;
> }
>
> static int vfw_read_packet( AVFormatContext *s, AVPacket *pkt )
> {
>     struct vfw_ctx *ctx = s->priv_data;
>     int ret;
>
>     ctx->pkt = pkt;
>     ctx->grabbed = 0;
>
>     ret = SendMessage( ctx->hwnd, WM_CAP_GRAB_FRAME, 0, 0 );
>     if( ret == FALSE || !ctx->grabbed )
>         return AVERROR_IO;
>
>     return pkt->size;
> }
>
> static int vfw_read_close( AVFormatContext *s )
> {
>     struct vfw_ctx *ctx = s->priv_data;
>
>     SendMessage( ctx->hwnd, WM_CAP_SET_CALLBACK_FRAME, 0, 0 );
>     SendMessage( ctx->hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0 );

Doesn't the window need to be destroyed?

>     return 0;
> }
>
> AVInputFormat vfwcap_demuxer = {
>     "vfwcap",
>     "VFW video capture",
>     sizeof(struct vfw_ctx),
>     NULL,
>     vfw_read_header,
>     vfw_read_packet,
>     vfw_read_close,
>     .flags = AVFMT_NOFILE,
> };
>

-- 
M?ns Rullg?rd
mans at mansr.com




More information about the ffmpeg-devel mailing list