vfwcap.c 14.5 KB
Newer Older
Ramiro Polla's avatar
Ramiro Polla committed
1 2
/*
 * VFW capture interface
3
 * Copyright (c) 2006-2008 Ramiro Polla
Ramiro Polla's avatar
Ramiro Polla committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
 */

22
#include "libavformat/internal.h"
23 24 25
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
Ramiro Polla's avatar
Ramiro Polla committed
26
#include <windows.h>
27
#include <vfw.h>
28
#include "avdevice.h"
Ramiro Polla's avatar
Ramiro Polla committed
29 30 31 32 33 34 35 36

/* Defines for VFW missing from MinGW.
 * Remove this when MinGW incorporates them. */
#define HWND_MESSAGE                ((HWND)-3)

/* End of missing MinGW defines */

struct vfw_ctx {
37
    const AVClass *class;
Ramiro Polla's avatar
Ramiro Polla committed
38 39 40 41 42 43
    HWND hwnd;
    HANDLE mutex;
    HANDLE event;
    AVPacketList *pktl;
    unsigned int curbufsize;
    unsigned int frame_num;
44
    char *video_size;       /**< A string describing video size, set by a private option. */
45
    char *framerate;        /**< Set by a private option. */
Ramiro Polla's avatar
Ramiro Polla committed
46 47
};

48
static enum AVPixelFormat vfw_pixfmt(DWORD biCompression, WORD biBitCount)
Ramiro Polla's avatar
Ramiro Polla committed
49 50
{
    switch(biCompression) {
51
    case MKTAG('U', 'Y', 'V', 'Y'):
52
        return AV_PIX_FMT_UYVY422;
Ramiro Polla's avatar
Ramiro Polla committed
53
    case MKTAG('Y', 'U', 'Y', '2'):
54
        return AV_PIX_FMT_YUYV422;
55
    case MKTAG('I', '4', '2', '0'):
56
        return AV_PIX_FMT_YUV420P;
Ramiro Polla's avatar
Ramiro Polla committed
57 58 59
    case BI_RGB:
        switch(biBitCount) { /* 1-8 are untested */
            case 1:
60
                return AV_PIX_FMT_MONOWHITE;
Ramiro Polla's avatar
Ramiro Polla committed
61
            case 4:
62
                return AV_PIX_FMT_RGB4;
Ramiro Polla's avatar
Ramiro Polla committed
63
            case 8:
64
                return AV_PIX_FMT_RGB8;
Ramiro Polla's avatar
Ramiro Polla committed
65
            case 16:
66
                return AV_PIX_FMT_RGB555;
Ramiro Polla's avatar
Ramiro Polla committed
67
            case 24:
68
                return AV_PIX_FMT_BGR24;
Ramiro Polla's avatar
Ramiro Polla committed
69
            case 32:
70
                return AV_PIX_FMT_RGB32;
Ramiro Polla's avatar
Ramiro Polla committed
71 72
        }
    }
73
    return AV_PIX_FMT_NONE;
Ramiro Polla's avatar
Ramiro Polla committed
74 75
}

76
static enum AVCodecID vfw_codecid(DWORD biCompression)
77 78 79
{
    switch(biCompression) {
    case MKTAG('d', 'v', 's', 'd'):
80
        return AV_CODEC_ID_DVVIDEO;
81 82
    case MKTAG('M', 'J', 'P', 'G'):
    case MKTAG('m', 'j', 'p', 'g'):
83
        return AV_CODEC_ID_MJPEG;
84
    }
85
    return AV_CODEC_ID_NONE;
86 87
}

Ramiro Polla's avatar
Ramiro Polla committed
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
#define dstruct(pctx, sname, var, type) \
    av_log(pctx, AV_LOG_DEBUG, #var":\t%"type"\n", sname->var)

static void dump_captureparms(AVFormatContext *s, CAPTUREPARMS *cparms)
{
    av_log(s, AV_LOG_DEBUG, "CAPTUREPARMS\n");
    dstruct(s, cparms, dwRequestMicroSecPerFrame, "lu");
    dstruct(s, cparms, fMakeUserHitOKToCapture, "d");
    dstruct(s, cparms, wPercentDropForError, "u");
    dstruct(s, cparms, fYield, "d");
    dstruct(s, cparms, dwIndexSize, "lu");
    dstruct(s, cparms, wChunkGranularity, "u");
    dstruct(s, cparms, fUsingDOSMemory, "d");
    dstruct(s, cparms, wNumVideoRequested, "u");
    dstruct(s, cparms, fCaptureAudio, "d");
    dstruct(s, cparms, wNumAudioRequested, "u");
    dstruct(s, cparms, vKeyAbort, "u");
    dstruct(s, cparms, fAbortLeftMouse, "d");
    dstruct(s, cparms, fAbortRightMouse, "d");
    dstruct(s, cparms, fLimitEnabled, "d");
    dstruct(s, cparms, wTimeLimit, "u");
    dstruct(s, cparms, fMCIControl, "d");
    dstruct(s, cparms, fStepMCIDevice, "d");
    dstruct(s, cparms, dwMCIStartTime, "lu");
    dstruct(s, cparms, dwMCIStopTime, "lu");
    dstruct(s, cparms, fStepCaptureAt2x, "d");
    dstruct(s, cparms, wStepCaptureAverageFrames, "u");
    dstruct(s, cparms, dwAudioBufferSize, "lu");
    dstruct(s, cparms, fDisableWriteCache, "d");
    dstruct(s, cparms, AVStreamMaster, "u");
}

static void dump_videohdr(AVFormatContext *s, VIDEOHDR *vhdr)
{
122
#ifdef DEBUG
Ramiro Polla's avatar
Ramiro Polla committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    av_log(s, AV_LOG_DEBUG, "VIDEOHDR\n");
    dstruct(s, vhdr, lpData, "p");
    dstruct(s, vhdr, dwBufferLength, "lu");
    dstruct(s, vhdr, dwBytesUsed, "lu");
    dstruct(s, vhdr, dwTimeCaptured, "lu");
    dstruct(s, vhdr, dwUser, "lu");
    dstruct(s, vhdr, dwFlags, "lu");
    dstruct(s, vhdr, dwReserved[0], "lu");
    dstruct(s, vhdr, dwReserved[1], "lu");
    dstruct(s, vhdr, dwReserved[2], "lu");
    dstruct(s, vhdr, dwReserved[3], "lu");
#endif
}

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

155
static int shall_we_drop(AVFormatContext *s)
Ramiro Polla's avatar
Ramiro Polla committed
156
{
157
    struct vfw_ctx *ctx = s->priv_data;
Ramiro Polla's avatar
Ramiro Polla committed
158
    const uint8_t dropscore[] = {62, 75, 87, 100};
159
    const int ndropscores = FF_ARRAY_ELEMS(dropscore);
Ramiro Polla's avatar
Ramiro Polla committed
160 161
    unsigned int buffer_fullness = (ctx->curbufsize*100)/s->max_picture_buffer;

162
    if(dropscore[++ctx->frame_num%ndropscores] <= buffer_fullness) {
163
        av_log(s, AV_LOG_ERROR,
Ramiro Polla's avatar
Ramiro Polla committed
164 165 166 167 168 169 170 171 172
              "real-time buffer %d%% full! frame dropped!\n", buffer_fullness);
        return 1;
    }

    return 0;
}

static LRESULT CALLBACK videostream_cb(HWND hwnd, LPVIDEOHDR vdhdr)
{
173
    AVFormatContext *s;
Ramiro Polla's avatar
Ramiro Polla committed
174 175 176
    struct vfw_ctx *ctx;
    AVPacketList **ppktl, *pktl_next;

177 178
    s = (AVFormatContext *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
    ctx = s->priv_data;
Ramiro Polla's avatar
Ramiro Polla committed
179

180
    dump_videohdr(s, vdhdr);
Ramiro Polla's avatar
Ramiro Polla committed
181

182
    if(shall_we_drop(s))
Ramiro Polla's avatar
Ramiro Polla committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
        return FALSE;

    WaitForSingleObject(ctx->mutex, INFINITE);

    pktl_next = av_mallocz(sizeof(AVPacketList));
    if(!pktl_next)
        goto fail;

    if(av_new_packet(&pktl_next->pkt, vdhdr->dwBytesUsed) < 0) {
        av_free(pktl_next);
        goto fail;
    }

    pktl_next->pkt.pts = vdhdr->dwTimeCaptured;
    memcpy(pktl_next->pkt.data, vdhdr->lpData, vdhdr->dwBytesUsed);

    for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next);
    *ppktl = pktl_next;

    ctx->curbufsize += vdhdr->dwBytesUsed;

    SetEvent(ctx->event);
    ReleaseMutex(ctx->mutex);

    return TRUE;
fail:
    ReleaseMutex(ctx->mutex);
    return FALSE;
}

213 214 215
static int vfw_read_close(AVFormatContext *s)
{
    struct vfw_ctx *ctx = s->priv_data;
216
    AVPacketList *pktl;
217 218 219 220 221 222 223 224 225 226 227

    if(ctx->hwnd) {
        SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
        SendMessage(ctx->hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
        DestroyWindow(ctx->hwnd);
    }
    if(ctx->mutex)
        CloseHandle(ctx->mutex);
    if(ctx->event)
        CloseHandle(ctx->event);

228 229 230 231 232 233 234 235
    pktl = ctx->pktl;
    while (pktl) {
        AVPacketList *next = pktl->next;
        av_destruct_packet(&pktl->pkt);
        av_free(pktl);
        pktl = next;
    }

236 237
    return 0;
}
Ramiro Polla's avatar
Ramiro Polla committed
238

239
static int vfw_read_header(AVFormatContext *s)
Ramiro Polla's avatar
Ramiro Polla committed
240 241 242 243 244 245
{
    struct vfw_ctx *ctx = s->priv_data;
    AVCodecContext *codec;
    AVStream *st;
    int devnum;
    int bisize;
246
    BITMAPINFO *bi = NULL;
Ramiro Polla's avatar
Ramiro Polla committed
247 248 249 250
    CAPTUREPARMS cparms;
    DWORD biCompression;
    WORD biBitCount;
    int ret;
251
    AVRational framerate_q;
Ramiro Polla's avatar
Ramiro Polla committed
252

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    if (!strcmp(s->filename, "list")) {
        for (devnum = 0; devnum <= 9; devnum++) {
            char driver_name[256];
            char driver_ver[256];
            ret = capGetDriverDescription(devnum,
                                          driver_name, sizeof(driver_name),
                                          driver_ver, sizeof(driver_ver));
            if (ret) {
                av_log(s, AV_LOG_INFO, "Driver %d\n", devnum);
                av_log(s, AV_LOG_INFO, " %s\n", driver_name);
                av_log(s, AV_LOG_INFO, " %s\n", driver_ver);
            }
        }
        return AVERROR(EIO);
    }

Ramiro Polla's avatar
Ramiro Polla committed
269 270 271
    ctx->hwnd = capCreateCaptureWindow(NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0);
    if(!ctx->hwnd) {
        av_log(s, AV_LOG_ERROR, "Could not create capture window.\n");
272
        return AVERROR(EIO);
Ramiro Polla's avatar
Ramiro Polla committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
    }

    /* 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) {
        av_log(s, AV_LOG_ERROR, "Could not connect to device.\n");
        DestroyWindow(ctx->hwnd);
        return AVERROR(ENODEV);
    }

    SendMessage(ctx->hwnd, WM_CAP_SET_OVERLAY, 0, 0);
    SendMessage(ctx->hwnd, WM_CAP_SET_PREVIEW, 0, 0);

    ret = SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0,
                      (LPARAM) videostream_cb);
    if(!ret) {
        av_log(s, AV_LOG_ERROR, "Could not set video stream callback.\n");
292
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
293 294
    }

295
    SetWindowLongPtr(ctx->hwnd, GWLP_USERDATA, (LONG_PTR) s);
Ramiro Polla's avatar
Ramiro Polla committed
296

297
    st = avformat_new_stream(s, NULL);
Ramiro Polla's avatar
Ramiro Polla committed
298 299
    if(!st) {
        vfw_read_close(s);
300
        return AVERROR(ENOMEM);
Ramiro Polla's avatar
Ramiro Polla committed
301 302 303 304
    }

    /* Set video format */
    bisize = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0);
305
    if(!bisize)
306
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
307 308 309
    bi = av_malloc(bisize);
    if(!bi) {
        vfw_read_close(s);
310
        return AVERROR(ENOMEM);
Ramiro Polla's avatar
Ramiro Polla committed
311 312
    }
    ret = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, bisize, (LPARAM) bi);
313
    if(!ret)
314
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
315 316 317

    dump_bih(s, &bi->bmiHeader);

318 319 320 321 322
    ret = av_parse_video_rate(&framerate_q, ctx->framerate);
    if (ret < 0) {
        av_log(s, AV_LOG_ERROR, "Could not parse framerate '%s'.\n", ctx->framerate);
        goto fail;
    }
323 324 325 326 327

    if (ctx->video_size) {
        ret = av_parse_video_size(&bi->bmiHeader.biWidth, &bi->bmiHeader.biHeight, ctx->video_size);
        if (ret < 0) {
            av_log(s, AV_LOG_ERROR, "Couldn't parse video size.\n");
328
            goto fail;
329 330
        }
    }
Ramiro Polla's avatar
Ramiro Polla committed
331

332
    if (0) {
Ramiro Polla's avatar
Ramiro Polla committed
333 334 335 336 337 338 339 340 341
        /* For testing yet unsupported compressions
         * Copy these values from user-supplied verbose information */
        bi->bmiHeader.biWidth       = 320;
        bi->bmiHeader.biHeight      = 240;
        bi->bmiHeader.biPlanes      = 1;
        bi->bmiHeader.biBitCount    = 12;
        bi->bmiHeader.biCompression = MKTAG('I','4','2','0');
        bi->bmiHeader.biSizeImage   = 115200;
        dump_bih(s, &bi->bmiHeader);
342
    }
343

Ramiro Polla's avatar
Ramiro Polla committed
344 345 346
    ret = SendMessage(ctx->hwnd, WM_CAP_SET_VIDEOFORMAT, bisize, (LPARAM) bi);
    if(!ret) {
        av_log(s, AV_LOG_ERROR, "Could not set Video Format.\n");
347
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
348 349 350 351 352 353 354 355
    }

    biCompression = bi->bmiHeader.biCompression;
    biBitCount = bi->bmiHeader.biBitCount;

    /* Set sequence setup */
    ret = SendMessage(ctx->hwnd, WM_CAP_GET_SEQUENCE_SETUP, sizeof(cparms),
                      (LPARAM) &cparms);
356
    if(!ret)
357
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
358 359 360 361 362

    dump_captureparms(s, &cparms);

    cparms.fYield = 1; // Spawn a background thread
    cparms.dwRequestMicroSecPerFrame =
363
                               (framerate_q.den*1000000) / framerate_q.num;
Ramiro Polla's avatar
Ramiro Polla committed
364 365 366 367 368 369 370
    cparms.fAbortLeftMouse = 0;
    cparms.fAbortRightMouse = 0;
    cparms.fCaptureAudio = 0;
    cparms.vKeyAbort = 0;

    ret = SendMessage(ctx->hwnd, WM_CAP_SET_SEQUENCE_SETUP, sizeof(cparms),
                      (LPARAM) &cparms);
371
    if(!ret)
372
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
373 374

    codec = st->codec;
375
    codec->time_base = av_inv_q(framerate_q);
376
    codec->codec_type = AVMEDIA_TYPE_VIDEO;
377 378
    codec->width  = bi->bmiHeader.biWidth;
    codec->height = bi->bmiHeader.biHeight;
Ramiro Polla's avatar
Ramiro Polla committed
379
    codec->pix_fmt = vfw_pixfmt(biCompression, biBitCount);
380
    if(codec->pix_fmt == AV_PIX_FMT_NONE) {
381
        codec->codec_id = vfw_codecid(biCompression);
382
        if(codec->codec_id == AV_CODEC_ID_NONE) {
Ramiro Polla's avatar
Ramiro Polla committed
383 384 385 386
            av_log(s, AV_LOG_ERROR, "Unknown compression type. "
                             "Please report verbose (-v 9) debug information.\n");
            vfw_read_close(s);
            return AVERROR_PATCHWELCOME;
387 388
        }
        codec->bits_per_coded_sample = biBitCount;
Ramiro Polla's avatar
Ramiro Polla committed
389
    } else {
390
        codec->codec_id = AV_CODEC_ID_RAWVIDEO;
391
        if(biCompression == BI_RGB) {
Ramiro Polla's avatar
Ramiro Polla committed
392
            codec->bits_per_coded_sample = biBitCount;
393 394 395 396 397 398
            codec->extradata = av_malloc(9 + FF_INPUT_BUFFER_PADDING_SIZE);
            if (codec->extradata) {
                codec->extradata_size = 9;
                memcpy(codec->extradata, "BottomUp", 9);
            }
        }
399
    }
400

401 402
    av_freep(&bi);

403
    avpriv_set_pts_info(st, 32, 1, 1000);
Ramiro Polla's avatar
Ramiro Polla committed
404 405 406 407

    ctx->mutex = CreateMutex(NULL, 0, NULL);
    if(!ctx->mutex) {
        av_log(s, AV_LOG_ERROR, "Could not create Mutex.\n" );
408
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
409 410 411 412
    }
    ctx->event = CreateEvent(NULL, 1, 0, NULL);
    if(!ctx->event) {
        av_log(s, AV_LOG_ERROR, "Could not create Event.\n" );
413
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
414 415 416 417 418
    }

    ret = SendMessage(ctx->hwnd, WM_CAP_SEQUENCE_NOFILE, 0, 0);
    if(!ret) {
        av_log(s, AV_LOG_ERROR, "Could not start capture sequence.\n" );
419
        goto fail;
Ramiro Polla's avatar
Ramiro Polla committed
420 421 422
    }

    return 0;
423

424 425
fail:
    av_freep(&bi);
426
    vfw_read_close(s);
427
    return AVERROR(EIO);
Ramiro Polla's avatar
Ramiro Polla committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
}

static int vfw_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    struct vfw_ctx *ctx = s->priv_data;
    AVPacketList *pktl = NULL;

    while(!pktl) {
        WaitForSingleObject(ctx->mutex, INFINITE);
        pktl = ctx->pktl;
        if(ctx->pktl) {
            *pkt = ctx->pktl->pkt;
            ctx->pktl = ctx->pktl->next;
            av_free(pktl);
        }
        ResetEvent(ctx->event);
        ReleaseMutex(ctx->mutex);
        if(!pktl) {
            if(s->flags & AVFMT_FLAG_NONBLOCK) {
                return AVERROR(EAGAIN);
            } else {
                WaitForSingleObject(ctx->event, INFINITE);
            }
        }
    }

    ctx->curbufsize -= pkt->size;

    return pkt->size;
}

459 460 461
#define OFFSET(x) offsetof(struct vfw_ctx, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
462 463
    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
464 465 466 467 468 469 470 471 472 473
    { NULL },
};

static const AVClass vfw_class = {
    .class_name = "VFW indev",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

474
AVInputFormat ff_vfwcap_demuxer = {
475 476 477 478 479 480 481 482
    .name           = "vfwcap",
    .long_name      = NULL_IF_CONFIG_SMALL("VfW video capture"),
    .priv_data_size = sizeof(struct vfw_ctx),
    .read_header    = vfw_read_header,
    .read_packet    = vfw_read_packet,
    .read_close     = vfw_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &vfw_class,
Ramiro Polla's avatar
Ramiro Polla committed
483
};