Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/*
2
 * VFW capture interface
3
 * Copyright (c) 2006-2008 Ramiro Polla
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
 
22
#include "libavformat/internal.h"
23
#include "libavutil/log.h"
24
#include "libavutil/opt.h"
25
#include "libavutil/parseutils.h"
26
#include 
27
#include 
28
#include "avdevice.h"
29
 
30
/* Defines for VFW missing from MinGW.
31
 * Remove this when MinGW incorporates them. */
32
#define HWND_MESSAGE                ((HWND)-3)
33
 
34
/* End of missing MinGW defines */
35
 
36
struct vfw_ctx {
37
    const AVClass *class;
38
    HWND hwnd;
39
    HANDLE mutex;
40
    HANDLE event;
41
    AVPacketList *pktl;
42
    unsigned int curbufsize;
43
    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. */
46
};
47
 
48
static enum AVPixelFormat vfw_pixfmt(DWORD biCompression, WORD biBitCount)
49
{
50
    switch(biCompression) {
51
    case MKTAG('U', 'Y', 'V', 'Y'):
52
        return AV_PIX_FMT_UYVY422;
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;
57
    case BI_RGB:
58
        switch(biBitCount) { /* 1-8 are untested */
59
            case 1:
60
                return AV_PIX_FMT_MONOWHITE;
61
            case 4:
62
                return AV_PIX_FMT_RGB4;
63
            case 8:
64
                return AV_PIX_FMT_RGB8;
65
            case 16:
66
                return AV_PIX_FMT_RGB555;
67
            case 24:
68
                return AV_PIX_FMT_BGR24;
69
            case 32:
70
                return AV_PIX_FMT_RGB32;
71
        }
72
    }
73
    return AV_PIX_FMT_NONE;
74
}
75
 
76
static enum AVCodecID vfw_codecid(DWORD biCompression)
77
{
78
    switch(biCompression) {
79
    case MKTAG('d', 'v', 's', 'd'):
80
        return AV_CODEC_ID_DVVIDEO;
81
    case MKTAG('M', 'J', 'P', 'G'):
82
    case MKTAG('m', 'j', 'p', 'g'):
83
        return AV_CODEC_ID_MJPEG;
84
    }
85
    return AV_CODEC_ID_NONE;
86
}
87
 
88
#define dstruct(pctx, sname, var, type) \
89
    av_log(pctx, AV_LOG_DEBUG, #var":\t%"type"\n", sname->var)
90
 
91
static void dump_captureparms(AVFormatContext *s, CAPTUREPARMS *cparms)
92
{
93
    av_log(s, AV_LOG_DEBUG, "CAPTUREPARMS\n");
94
    dstruct(s, cparms, dwRequestMicroSecPerFrame, "lu");
95
    dstruct(s, cparms, fMakeUserHitOKToCapture, "d");
96
    dstruct(s, cparms, wPercentDropForError, "u");
97
    dstruct(s, cparms, fYield, "d");
98
    dstruct(s, cparms, dwIndexSize, "lu");
99
    dstruct(s, cparms, wChunkGranularity, "u");
100
    dstruct(s, cparms, fUsingDOSMemory, "d");
101
    dstruct(s, cparms, wNumVideoRequested, "u");
102
    dstruct(s, cparms, fCaptureAudio, "d");
103
    dstruct(s, cparms, wNumAudioRequested, "u");
104
    dstruct(s, cparms, vKeyAbort, "u");
105
    dstruct(s, cparms, fAbortLeftMouse, "d");
106
    dstruct(s, cparms, fAbortRightMouse, "d");
107
    dstruct(s, cparms, fLimitEnabled, "d");
108
    dstruct(s, cparms, wTimeLimit, "u");
109
    dstruct(s, cparms, fMCIControl, "d");
110
    dstruct(s, cparms, fStepMCIDevice, "d");
111
    dstruct(s, cparms, dwMCIStartTime, "lu");
112
    dstruct(s, cparms, dwMCIStopTime, "lu");
113
    dstruct(s, cparms, fStepCaptureAt2x, "d");
114
    dstruct(s, cparms, wStepCaptureAverageFrames, "u");
115
    dstruct(s, cparms, dwAudioBufferSize, "lu");
116
    dstruct(s, cparms, fDisableWriteCache, "d");
117
    dstruct(s, cparms, AVStreamMaster, "u");
118
}
119
 
120
static void dump_videohdr(AVFormatContext *s, VIDEOHDR *vhdr)
121
{
122
#ifdef DEBUG
123
    av_log(s, AV_LOG_DEBUG, "VIDEOHDR\n");
124
    dstruct(s, vhdr, lpData, "p");
125
    dstruct(s, vhdr, dwBufferLength, "lu");
126
    dstruct(s, vhdr, dwBytesUsed, "lu");
127
    dstruct(s, vhdr, dwTimeCaptured, "lu");
128
    dstruct(s, vhdr, dwUser, "lu");
129
    dstruct(s, vhdr, dwFlags, "lu");
130
    dstruct(s, vhdr, dwReserved[0], "lu");
131
    dstruct(s, vhdr, dwReserved[1], "lu");
132
    dstruct(s, vhdr, dwReserved[2], "lu");
133
    dstruct(s, vhdr, dwReserved[3], "lu");
134
#endif
135
}
136
 
137
static void dump_bih(AVFormatContext *s, BITMAPINFOHEADER *bih)
138
{
139
    av_log(s, AV_LOG_DEBUG, "BITMAPINFOHEADER\n");
140
    dstruct(s, bih, biSize, "lu");
141
    dstruct(s, bih, biWidth, "ld");
142
    dstruct(s, bih, biHeight, "ld");
143
    dstruct(s, bih, biPlanes, "d");
144
    dstruct(s, bih, biBitCount, "d");
145
    dstruct(s, bih, biCompression, "lu");
146
    av_log(s, AV_LOG_DEBUG, "    biCompression:\t\"%.4s\"\n",
147
                   (char*) &bih->biCompression);
148
    dstruct(s, bih, biSizeImage, "lu");
149
    dstruct(s, bih, biXPelsPerMeter, "lu");
150
    dstruct(s, bih, biYPelsPerMeter, "lu");
151
    dstruct(s, bih, biClrUsed, "lu");
152
    dstruct(s, bih, biClrImportant, "lu");
153
}
154
 
155
static int shall_we_drop(AVFormatContext *s)
156
{
157
    struct vfw_ctx *ctx = s->priv_data;
158
    static const uint8_t dropscore[] = {62, 75, 87, 100};
159
    const int ndropscores = FF_ARRAY_ELEMS(dropscore);
160
    unsigned int buffer_fullness = (ctx->curbufsize*100)/s->max_picture_buffer;
161
 
162
    if(dropscore[++ctx->frame_num%ndropscores] <= buffer_fullness) {
163
        av_log(s, AV_LOG_ERROR,
164
              "real-time buffer %d%% full! frame dropped!\n", buffer_fullness);
165
        return 1;
166
    }
167
 
168
    return 0;
169
}
170
 
171
static LRESULT CALLBACK videostream_cb(HWND hwnd, LPVIDEOHDR vdhdr)
172
{
173
    AVFormatContext *s;
174
    struct vfw_ctx *ctx;
175
    AVPacketList **ppktl, *pktl_next;
176
 
177
    s = (AVFormatContext *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
178
    ctx = s->priv_data;
179
 
180
    dump_videohdr(s, vdhdr);
181
 
182
    if(shall_we_drop(s))
183
        return FALSE;
184
 
185
    WaitForSingleObject(ctx->mutex, INFINITE);
186
 
187
    pktl_next = av_mallocz(sizeof(AVPacketList));
188
    if(!pktl_next)
189
        goto fail;
190
 
191
    if(av_new_packet(&pktl_next->pkt, vdhdr->dwBytesUsed) < 0) {
192
        av_free(pktl_next);
193
        goto fail;
194
    }
195
 
196
    pktl_next->pkt.pts = vdhdr->dwTimeCaptured;
197
    memcpy(pktl_next->pkt.data, vdhdr->lpData, vdhdr->dwBytesUsed);
198
 
199
    for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next);
200
    *ppktl = pktl_next;
201
 
202
    ctx->curbufsize += vdhdr->dwBytesUsed;
203
 
204
    SetEvent(ctx->event);
205
    ReleaseMutex(ctx->mutex);
206
 
207
    return TRUE;
208
fail:
209
    ReleaseMutex(ctx->mutex);
210
    return FALSE;
211
}
212
 
213
static int vfw_read_close(AVFormatContext *s)
214
{
215
    struct vfw_ctx *ctx = s->priv_data;
216
    AVPacketList *pktl;
217
 
218
    if(ctx->hwnd) {
219
        SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
220
        SendMessage(ctx->hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
221
        DestroyWindow(ctx->hwnd);
222
    }
223
    if(ctx->mutex)
224
        CloseHandle(ctx->mutex);
225
    if(ctx->event)
226
        CloseHandle(ctx->event);
227
 
228
    pktl = ctx->pktl;
229
    while (pktl) {
230
        AVPacketList *next = pktl->next;
231
        av_destruct_packet(&pktl->pkt);
232
        av_free(pktl);
233
        pktl = next;
234
    }
235
 
236
    return 0;
237
}
238
 
239
static int vfw_read_header(AVFormatContext *s)
240
{
241
    struct vfw_ctx *ctx = s->priv_data;
242
    AVCodecContext *codec;
243
    AVStream *st;
244
    int devnum;
245
    int bisize;
246
    BITMAPINFO *bi = NULL;
247
    CAPTUREPARMS cparms;
248
    DWORD biCompression;
249
    WORD biBitCount;
250
    int ret;
251
    AVRational framerate_q;
252
 
253
    if (!strcmp(s->filename, "list")) {
254
        for (devnum = 0; devnum <= 9; devnum++) {
255
            char driver_name[256];
256
            char driver_ver[256];
257
            ret = capGetDriverDescription(devnum,
258
                                          driver_name, sizeof(driver_name),
259
                                          driver_ver, sizeof(driver_ver));
260
            if (ret) {
261
                av_log(s, AV_LOG_INFO, "Driver %d\n", devnum);
262
                av_log(s, AV_LOG_INFO, " %s\n", driver_name);
263
                av_log(s, AV_LOG_INFO, " %s\n", driver_ver);
264
            }
265
        }
266
        return AVERROR(EIO);
267
    }
268
 
269
    ctx->hwnd = capCreateCaptureWindow(NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0);
270
    if(!ctx->hwnd) {
271
        av_log(s, AV_LOG_ERROR, "Could not create capture window.\n");
272
        return AVERROR(EIO);
273
    }
274
 
275
    /* If atoi fails, devnum==0 and the default device is used */
276
    devnum = atoi(s->filename);
277
 
278
    ret = SendMessage(ctx->hwnd, WM_CAP_DRIVER_CONNECT, devnum, 0);
279
    if(!ret) {
280
        av_log(s, AV_LOG_ERROR, "Could not connect to device.\n");
281
        DestroyWindow(ctx->hwnd);
282
        return AVERROR(ENODEV);
283
    }
284
 
285
    SendMessage(ctx->hwnd, WM_CAP_SET_OVERLAY, 0, 0);
286
    SendMessage(ctx->hwnd, WM_CAP_SET_PREVIEW, 0, 0);
287
 
288
    ret = SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0,
289
                      (LPARAM) videostream_cb);
290
    if(!ret) {
291
        av_log(s, AV_LOG_ERROR, "Could not set video stream callback.\n");
292
        goto fail;
293
    }
294
 
295
    SetWindowLongPtr(ctx->hwnd, GWLP_USERDATA, (LONG_PTR) s);
296
 
297
    st = avformat_new_stream(s, NULL);
298
    if(!st) {
299
        vfw_read_close(s);
300
        return AVERROR(ENOMEM);
301
    }
302
 
303
    /* Set video format */
304
    bisize = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0);
305
    if(!bisize)
306
        goto fail;
307
    bi = av_malloc(bisize);
308
    if(!bi) {
309
        vfw_read_close(s);
310
        return AVERROR(ENOMEM);
311
    }
312
    ret = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, bisize, (LPARAM) bi);
313
    if(!ret)
314
        goto fail;
315
 
316
    dump_bih(s, &bi->bmiHeader);
317
 
318
    ret = av_parse_video_rate(&framerate_q, ctx->framerate);
319
    if (ret < 0) {
320
        av_log(s, AV_LOG_ERROR, "Could not parse framerate '%s'.\n", ctx->framerate);
321
        goto fail;
322
    }
323
 
324
    if (ctx->video_size) {
325
        ret = av_parse_video_size(&bi->bmiHeader.biWidth, &bi->bmiHeader.biHeight, ctx->video_size);
326
        if (ret < 0) {
327
            av_log(s, AV_LOG_ERROR, "Couldn't parse video size.\n");
328
            goto fail;
329
        }
330
    }
331
 
332
    if (0) {
333
        /* For testing yet unsupported compressions
334
         * Copy these values from user-supplied verbose information */
335
        bi->bmiHeader.biWidth       = 320;
336
        bi->bmiHeader.biHeight      = 240;
337
        bi->bmiHeader.biPlanes      = 1;
338
        bi->bmiHeader.biBitCount    = 12;
339
        bi->bmiHeader.biCompression = MKTAG('I','4','2','0');
340
        bi->bmiHeader.biSizeImage   = 115200;
341
        dump_bih(s, &bi->bmiHeader);
342
    }
343
 
344
    ret = SendMessage(ctx->hwnd, WM_CAP_SET_VIDEOFORMAT, bisize, (LPARAM) bi);
345
    if(!ret) {
346
        av_log(s, AV_LOG_ERROR, "Could not set Video Format.\n");
347
        goto fail;
348
    }
349
 
350
    biCompression = bi->bmiHeader.biCompression;
351
    biBitCount = bi->bmiHeader.biBitCount;
352
 
353
    /* Set sequence setup */
354
    ret = SendMessage(ctx->hwnd, WM_CAP_GET_SEQUENCE_SETUP, sizeof(cparms),
355
                      (LPARAM) &cparms);
356
    if(!ret)
357
        goto fail;
358
 
359
    dump_captureparms(s, &cparms);
360
 
361
    cparms.fYield = 1; // Spawn a background thread
362
    cparms.dwRequestMicroSecPerFrame =
363
                               (framerate_q.den*1000000) / framerate_q.num;
364
    cparms.fAbortLeftMouse = 0;
365
    cparms.fAbortRightMouse = 0;
366
    cparms.fCaptureAudio = 0;
367
    cparms.vKeyAbort = 0;
368
 
369
    ret = SendMessage(ctx->hwnd, WM_CAP_SET_SEQUENCE_SETUP, sizeof(cparms),
370
                      (LPARAM) &cparms);
371
    if(!ret)
372
        goto fail;
373
 
374
    codec = st->codec;
375
    codec->time_base = av_inv_q(framerate_q);
376
    codec->codec_type = AVMEDIA_TYPE_VIDEO;
377
    codec->width  = bi->bmiHeader.biWidth;
378
    codec->height = bi->bmiHeader.biHeight;
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) {
383
            av_log(s, AV_LOG_ERROR, "Unknown compression type. "
384
                             "Please report verbose (-v 9) debug information.\n");
385
            vfw_read_close(s);
386
            return AVERROR_PATCHWELCOME;
387
        }
388
        codec->bits_per_coded_sample = biBitCount;
389
    } else {
390
        codec->codec_id = AV_CODEC_ID_RAWVIDEO;
391
        if(biCompression == BI_RGB) {
392
            codec->bits_per_coded_sample = biBitCount;
393
            codec->extradata = av_malloc(9 + FF_INPUT_BUFFER_PADDING_SIZE);
394
            if (codec->extradata) {
395
                codec->extradata_size = 9;
396
                memcpy(codec->extradata, "BottomUp", 9);
397
            }
398
        }
399
    }
400
 
401
    av_freep(&bi);
402
 
403
    avpriv_set_pts_info(st, 32, 1, 1000);
404
 
405
    ctx->mutex = CreateMutex(NULL, 0, NULL);
406
    if(!ctx->mutex) {
407
        av_log(s, AV_LOG_ERROR, "Could not create Mutex.\n" );
408
        goto fail;
409
    }
410
    ctx->event = CreateEvent(NULL, 1, 0, NULL);
411
    if(!ctx->event) {
412
        av_log(s, AV_LOG_ERROR, "Could not create Event.\n" );
413
        goto fail;
414
    }
415
 
416
    ret = SendMessage(ctx->hwnd, WM_CAP_SEQUENCE_NOFILE, 0, 0);
417
    if(!ret) {
418
        av_log(s, AV_LOG_ERROR, "Could not start capture sequence.\n" );
419
        goto fail;
420
    }
421
 
422
    return 0;
423
 
424
fail:
425
    av_freep(&bi);
426
    vfw_read_close(s);
427
    return AVERROR(EIO);
428
}
429
 
430
static int vfw_read_packet(AVFormatContext *s, AVPacket *pkt)
431
{
432
    struct vfw_ctx *ctx = s->priv_data;
433
    AVPacketList *pktl = NULL;
434
 
435
    while(!pktl) {
436
        WaitForSingleObject(ctx->mutex, INFINITE);
437
        pktl = ctx->pktl;
438
        if(ctx->pktl) {
439
            *pkt = ctx->pktl->pkt;
440
            ctx->pktl = ctx->pktl->next;
441
            av_free(pktl);
442
        }
443
        ResetEvent(ctx->event);
444
        ReleaseMutex(ctx->mutex);
445
        if(!pktl) {
446
            if(s->flags & AVFMT_FLAG_NONBLOCK) {
447
                return AVERROR(EAGAIN);
448
            } else {
449
                WaitForSingleObject(ctx->event, INFINITE);
450
            }
451
        }
452
    }
453
 
454
    ctx->curbufsize -= pkt->size;
455
 
456
    return pkt->size;
457
}
458
 
459
#define OFFSET(x) offsetof(struct vfw_ctx, x)
460
#define DEC AV_OPT_FLAG_DECODING_PARAM
461
static const AVOption options[] = {
462
    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
463
    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
464
    { NULL },
465
};
466
 
467
static const AVClass vfw_class = {
468
    .class_name = "VFW indev",
469
    .item_name  = av_default_item_name,
470
    .option     = options,
471
    .version    = LIBAVUTIL_VERSION_INT,
472
};
473
 
474
AVInputFormat ff_vfwcap_demuxer = {
475
    .name           = "vfwcap",
476
    .long_name      = NULL_IF_CONFIG_SMALL("VfW video capture"),
477
    .priv_data_size = sizeof(struct vfw_ctx),
478
    .read_header    = vfw_read_header,
479
    .read_packet    = vfw_read_packet,
480
    .read_close     = vfw_read_close,
481
    .flags          = AVFMT_NOFILE,
482
    .priv_class     = &vfw_class,
483
};