Browse code

VFW capture support

Originally committed as revision 12384 to svn://svn.ffmpeg.org/ffmpeg/trunk

Ramiro Polla authored on 2008/03/09 07:10:28
Showing 4 changed files
... ...
@@ -836,6 +836,8 @@ rtsp_demuxer_deps="sdp_demuxer"
836 836
 sdp_demuxer_deps="rtp_protocol mpegts_demuxer"
837 837
 v4l2_demuxer_deps="linux_videodev2_h"
838 838
 v4l_demuxer_deps="linux_videodev_h"
839
+vfwcap_demuxer_deps="capCreateCaptureWindow"
840
+vfwcap_demuxer_extralibs="-lvfw32"
839 841
 x11_grab_device_demuxer_deps="x11grab XShmCreateImage"
840 842
 x11_grab_device_demuxer_extralibs="-lX11 -lXext"
841 843
 
... ...
@@ -1753,6 +1755,8 @@ EOF
1753 1753
 check_header linux/videodev.h
1754 1754
 check_header linux/videodev2.h
1755 1755
 
1756
+check_func2 "windows.h vfw.h" capCreateCaptureWindow -lvfw32
1757
+
1756 1758
 # check for ioctl_meteor.h, ioctl_bt848.h and alternatives
1757 1759
 { check_header dev/bktr/ioctl_meteor.h &&
1758 1760
   check_header dev/bktr/ioctl_bt848.h; } ||
... ...
@@ -14,6 +14,7 @@ OBJS-$(CONFIG_OSS_DEMUXER)               += audio.o
14 14
 OBJS-$(CONFIG_OSS_MUXER)                 += audio.o
15 15
 OBJS-$(CONFIG_V4L2_DEMUXER)              += v4l2.o
16 16
 OBJS-$(CONFIG_V4L_DEMUXER)               += v4l.o
17
+OBJS-$(CONFIG_VFWCAP_DEMUXER)            += vfwcap.o
17 18
 OBJS-$(CONFIG_X11_GRAB_DEVICE_DEMUXER)   += x11grab.o
18 19
 
19 20
 # external libraries
... ...
@@ -43,6 +43,7 @@ void avdevice_register_all(void)
43 43
     REGISTER_MUXDEMUX (OSS, oss);
44 44
     REGISTER_DEMUXER  (V4L2, v4l2);
45 45
     REGISTER_DEMUXER  (V4L, v4l);
46
+    REGISTER_DEMUXER  (VFWCAP, vfwcap);
46 47
     REGISTER_DEMUXER  (X11_GRAB_DEVICE, x11_grab_device);
47 48
 
48 49
     /* external libraries */
49 50
new file mode 100644
... ...
@@ -0,0 +1,460 @@
0
+/*
1
+ * VFW capture interface
2
+ * Copyright (c) 2006-2008 Ramiro Polla.
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#include "avformat.h"
22
+#include <vfw.h>
23
+#include <windows.h>
24
+
25
+//#define DEBUG_VFW
26
+
27
+/* Defines for VFW missing from MinGW.
28
+ * Remove this when MinGW incorporates them. */
29
+#define WM_CAP_START                (0x0400)
30
+#define WM_CAP_SET_CALLBACK_VIDEOSTREAM (WM_CAP_START + 6)
31
+#define WM_CAP_DRIVER_CONNECT       (WM_CAP_START + 10)
32
+#define WM_CAP_DRIVER_DISCONNECT    (WM_CAP_START + 11)
33
+#define WM_CAP_GET_VIDEOFORMAT      (WM_CAP_START + 44)
34
+#define WM_CAP_SET_VIDEOFORMAT      (WM_CAP_START + 45)
35
+#define WM_CAP_SET_PREVIEW          (WM_CAP_START + 50)
36
+#define WM_CAP_SET_OVERLAY          (WM_CAP_START + 51)
37
+#define WM_CAP_SEQUENCE_NOFILE      (WM_CAP_START + 63)
38
+#define WM_CAP_SET_SEQUENCE_SETUP   (WM_CAP_START + 64)
39
+#define WM_CAP_GET_SEQUENCE_SETUP   (WM_CAP_START + 65)
40
+
41
+#define HWND_MESSAGE                ((HWND)-3)
42
+
43
+#define BI_RGB                      0
44
+
45
+typedef struct videohdr_tag {
46
+    LPBYTE      lpData;
47
+    DWORD       dwBufferLength;
48
+    DWORD       dwBytesUsed;
49
+    DWORD       dwTimeCaptured;
50
+    DWORD       dwUser;
51
+    DWORD       dwFlags;
52
+    DWORD_PTR   dwReserved[4];
53
+} VIDEOHDR, NEAR *PVIDEOHDR, FAR * LPVIDEOHDR;
54
+
55
+typedef struct {
56
+    DWORD dwRequestMicroSecPerFrame;
57
+    BOOL  fMakeUserHitOKToCapture;
58
+    UINT  wPercentDropForError;
59
+    BOOL  fYield;
60
+    DWORD dwIndexSize;
61
+    UINT  wChunkGranularity;
62
+    BOOL  fUsingDOSMemory;
63
+    UINT  wNumVideoRequested;
64
+    BOOL  fCaptureAudio;
65
+    UINT  wNumAudioRequested;
66
+    UINT  vKeyAbort;
67
+    BOOL  fAbortLeftMouse;
68
+    BOOL  fAbortRightMouse;
69
+    BOOL  fLimitEnabled;
70
+    UINT  wTimeLimit;
71
+    BOOL  fMCIControl;
72
+    BOOL  fStepMCIDevice;
73
+    DWORD dwMCIStartTime;
74
+    DWORD dwMCIStopTime;
75
+    BOOL  fStepCaptureAt2x;
76
+    UINT  wStepCaptureAverageFrames;
77
+    DWORD dwAudioBufferSize;
78
+    BOOL  fDisableWriteCache;
79
+    UINT  AVStreamMaster;
80
+} CAPTUREPARMS;
81
+/* End of missing MinGW defines */
82
+
83
+struct vfw_ctx {
84
+    HWND hwnd;
85
+    HANDLE mutex;
86
+    HANDLE event;
87
+    AVPacketList *pktl;
88
+    AVFormatContext *s;
89
+    unsigned int curbufsize;
90
+    unsigned int frame_num;
91
+};
92
+
93
+static enum PixelFormat vfw_pixfmt(DWORD biCompression, WORD biBitCount)
94
+{
95
+    switch(biCompression) {
96
+    case MKTAG('Y', 'U', 'Y', '2'):
97
+        return PIX_FMT_YUYV422;
98
+    case BI_RGB:
99
+        switch(biBitCount) { /* 1-8 are untested */
100
+            case 1:
101
+                return PIX_FMT_MONOWHITE;
102
+            case 4:
103
+                return PIX_FMT_RGB4;
104
+            case 8:
105
+                return PIX_FMT_RGB8;
106
+            case 16:
107
+                return PIX_FMT_RGB555;
108
+            case 24:
109
+                return PIX_FMT_BGR24;
110
+            case 32:
111
+                return PIX_FMT_RGB32;
112
+        }
113
+    }
114
+    return -1;
115
+}
116
+
117
+#define dstruct(pctx, sname, var, type) \
118
+    av_log(pctx, AV_LOG_DEBUG, #var":\t%"type"\n", sname->var)
119
+
120
+static void dump_captureparms(AVFormatContext *s, CAPTUREPARMS *cparms)
121
+{
122
+    av_log(s, AV_LOG_DEBUG, "CAPTUREPARMS\n");
123
+    dstruct(s, cparms, dwRequestMicroSecPerFrame, "lu");
124
+    dstruct(s, cparms, fMakeUserHitOKToCapture, "d");
125
+    dstruct(s, cparms, wPercentDropForError, "u");
126
+    dstruct(s, cparms, fYield, "d");
127
+    dstruct(s, cparms, dwIndexSize, "lu");
128
+    dstruct(s, cparms, wChunkGranularity, "u");
129
+    dstruct(s, cparms, fUsingDOSMemory, "d");
130
+    dstruct(s, cparms, wNumVideoRequested, "u");
131
+    dstruct(s, cparms, fCaptureAudio, "d");
132
+    dstruct(s, cparms, wNumAudioRequested, "u");
133
+    dstruct(s, cparms, vKeyAbort, "u");
134
+    dstruct(s, cparms, fAbortLeftMouse, "d");
135
+    dstruct(s, cparms, fAbortRightMouse, "d");
136
+    dstruct(s, cparms, fLimitEnabled, "d");
137
+    dstruct(s, cparms, wTimeLimit, "u");
138
+    dstruct(s, cparms, fMCIControl, "d");
139
+    dstruct(s, cparms, fStepMCIDevice, "d");
140
+    dstruct(s, cparms, dwMCIStartTime, "lu");
141
+    dstruct(s, cparms, dwMCIStopTime, "lu");
142
+    dstruct(s, cparms, fStepCaptureAt2x, "d");
143
+    dstruct(s, cparms, wStepCaptureAverageFrames, "u");
144
+    dstruct(s, cparms, dwAudioBufferSize, "lu");
145
+    dstruct(s, cparms, fDisableWriteCache, "d");
146
+    dstruct(s, cparms, AVStreamMaster, "u");
147
+}
148
+
149
+static void dump_videohdr(AVFormatContext *s, VIDEOHDR *vhdr)
150
+{
151
+#ifdef DEBUG_VFW
152
+    av_log(s, AV_LOG_DEBUG, "VIDEOHDR\n");
153
+    dstruct(s, vhdr, lpData, "p");
154
+    dstruct(s, vhdr, dwBufferLength, "lu");
155
+    dstruct(s, vhdr, dwBytesUsed, "lu");
156
+    dstruct(s, vhdr, dwTimeCaptured, "lu");
157
+    dstruct(s, vhdr, dwUser, "lu");
158
+    dstruct(s, vhdr, dwFlags, "lu");
159
+    dstruct(s, vhdr, dwReserved[0], "lu");
160
+    dstruct(s, vhdr, dwReserved[1], "lu");
161
+    dstruct(s, vhdr, dwReserved[2], "lu");
162
+    dstruct(s, vhdr, dwReserved[3], "lu");
163
+#endif
164
+}
165
+
166
+static void dump_bih(AVFormatContext *s, BITMAPINFOHEADER *bih)
167
+{
168
+    av_log(s, AV_LOG_DEBUG, "BITMAPINFOHEADER\n");
169
+    dstruct(s, bih, biSize, "lu");
170
+    dstruct(s, bih, biWidth, "ld");
171
+    dstruct(s, bih, biHeight, "ld");
172
+    dstruct(s, bih, biPlanes, "d");
173
+    dstruct(s, bih, biBitCount, "d");
174
+    dstruct(s, bih, biCompression, "lu");
175
+    av_log(s, AV_LOG_DEBUG, "    biCompression:\t\"%.4s\"\n",
176
+                   (char*) &bih->biCompression);
177
+    dstruct(s, bih, biSizeImage, "lu");
178
+    dstruct(s, bih, biXPelsPerMeter, "lu");
179
+    dstruct(s, bih, biYPelsPerMeter, "lu");
180
+    dstruct(s, bih, biClrUsed, "lu");
181
+    dstruct(s, bih, biClrImportant, "lu");
182
+}
183
+
184
+static int shall_we_drop(struct vfw_ctx *ctx)
185
+{
186
+    AVFormatContext *s = ctx->s;
187
+    const uint8_t dropscore[] = {62, 75, 87, 100};
188
+    const int ndropscores = sizeof(dropscore)/sizeof(dropscore[0]);
189
+    unsigned int buffer_fullness = (ctx->curbufsize*100)/s->max_picture_buffer;
190
+
191
+    if(dropscore[++ctx->frame_num%ndropscores] < buffer_fullness) {
192
+        av_log(ctx->s, AV_LOG_ERROR,
193
+              "real-time buffer %d%% full! frame dropped!\n", buffer_fullness);
194
+        return 1;
195
+    }
196
+
197
+    return 0;
198
+}
199
+
200
+static LRESULT CALLBACK videostream_cb(HWND hwnd, LPVIDEOHDR vdhdr)
201
+{
202
+    struct vfw_ctx *ctx;
203
+    AVPacketList **ppktl, *pktl_next;
204
+
205
+    ctx = (struct vfw_ctx *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
206
+
207
+    dump_videohdr(ctx->s, vdhdr);
208
+
209
+    if(shall_we_drop(ctx))
210
+        return FALSE;
211
+
212
+    WaitForSingleObject(ctx->mutex, INFINITE);
213
+
214
+    pktl_next = av_mallocz(sizeof(AVPacketList));
215
+    if(!pktl_next)
216
+        goto fail;
217
+
218
+    if(av_new_packet(&pktl_next->pkt, vdhdr->dwBytesUsed) < 0) {
219
+        av_free(pktl_next);
220
+        goto fail;
221
+    }
222
+
223
+    pktl_next->pkt.pts = vdhdr->dwTimeCaptured;
224
+    memcpy(pktl_next->pkt.data, vdhdr->lpData, vdhdr->dwBytesUsed);
225
+
226
+    for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next);
227
+    *ppktl = pktl_next;
228
+
229
+    ctx->curbufsize += vdhdr->dwBytesUsed;
230
+
231
+    SetEvent(ctx->event);
232
+    ReleaseMutex(ctx->mutex);
233
+
234
+    return TRUE;
235
+fail:
236
+    ReleaseMutex(ctx->mutex);
237
+    return FALSE;
238
+}
239
+
240
+static int vfw_read_close(AVFormatContext *s);
241
+
242
+static int vfw_read_header(AVFormatContext *s, AVFormatParameters *ap)
243
+{
244
+    struct vfw_ctx *ctx = s->priv_data;
245
+    AVCodecContext *codec;
246
+    AVStream *st;
247
+    int devnum;
248
+    int bisize;
249
+    BITMAPINFO *bi;
250
+    CAPTUREPARMS cparms;
251
+    DWORD biCompression;
252
+    WORD biBitCount;
253
+    int width;
254
+    int height;
255
+    int ret;
256
+
257
+    if(!ap->time_base.den) {
258
+        av_log(s, AV_LOG_ERROR, "A time base must be specified.\n");
259
+        return AVERROR_IO;
260
+    }
261
+
262
+    ctx->s = s;
263
+
264
+    ctx->hwnd = capCreateCaptureWindow(NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0);
265
+    if(!ctx->hwnd) {
266
+        av_log(s, AV_LOG_ERROR, "Could not create capture window.\n");
267
+        return AVERROR_IO;
268
+    }
269
+
270
+    /* If atoi fails, devnum==0 and the default device is used */
271
+    devnum = atoi(s->filename);
272
+
273
+    ret = SendMessage(ctx->hwnd, WM_CAP_DRIVER_CONNECT, devnum, 0);
274
+    if(!ret) {
275
+        av_log(s, AV_LOG_ERROR, "Could not connect to device.\n");
276
+        DestroyWindow(ctx->hwnd);
277
+        return AVERROR(ENODEV);
278
+    }
279
+
280
+    SendMessage(ctx->hwnd, WM_CAP_SET_OVERLAY, 0, 0);
281
+    SendMessage(ctx->hwnd, WM_CAP_SET_PREVIEW, 0, 0);
282
+
283
+    ret = SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0,
284
+                      (LPARAM) videostream_cb);
285
+    if(!ret) {
286
+        av_log(s, AV_LOG_ERROR, "Could not set video stream callback.\n");
287
+        vfw_read_close(s);
288
+        return AVERROR_IO;
289
+    }
290
+
291
+    SetWindowLongPtr(ctx->hwnd, GWLP_USERDATA, (LONG_PTR) ctx);
292
+
293
+    st = av_new_stream(s, 0);
294
+    if(!st) {
295
+        vfw_read_close(s);
296
+        return AVERROR_NOMEM;
297
+    }
298
+
299
+    /* Set video format */
300
+    bisize = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0);
301
+    if(!bisize) {
302
+        vfw_read_close(s);
303
+        return AVERROR_IO;
304
+    }
305
+    bi = av_malloc(bisize);
306
+    if(!bi) {
307
+        vfw_read_close(s);
308
+        return AVERROR_NOMEM;
309
+    }
310
+    ret = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, bisize, (LPARAM) bi);
311
+    if(!ret) {
312
+        av_free(bi);
313
+        vfw_read_close(s);
314
+        return AVERROR_IO;
315
+    }
316
+
317
+    dump_bih(s, &bi->bmiHeader);
318
+
319
+    width  = ap->width  ? ap->width  : bi->bmiHeader.biWidth ;
320
+    height = ap->height ? ap->height : bi->bmiHeader.biHeight;
321
+    bi->bmiHeader.biWidth  = width ;
322
+    bi->bmiHeader.biHeight = height;
323
+
324
+    ret = SendMessage(ctx->hwnd, WM_CAP_SET_VIDEOFORMAT, bisize, (LPARAM) bi);
325
+    if(!ret) {
326
+        av_log(s, AV_LOG_ERROR, "Could not set Video Format.\n");
327
+        av_free(bi);
328
+        vfw_read_close(s);
329
+        return AVERROR_IO;
330
+    }
331
+
332
+    biCompression = bi->bmiHeader.biCompression;
333
+    biBitCount = bi->bmiHeader.biBitCount;
334
+
335
+    av_free(bi);
336
+
337
+    /* Set sequence setup */
338
+    ret = SendMessage(ctx->hwnd, WM_CAP_GET_SEQUENCE_SETUP, sizeof(cparms),
339
+                      (LPARAM) &cparms);
340
+    if(!ret) {
341
+        vfw_read_close(s);
342
+        return AVERROR_IO;
343
+    }
344
+
345
+    dump_captureparms(s, &cparms);
346
+
347
+    cparms.fYield = 1; // Spawn a background thread
348
+    cparms.dwRequestMicroSecPerFrame =
349
+                               (ap->time_base.num*1000000) / ap->time_base.den;
350
+    cparms.fAbortLeftMouse = 0;
351
+    cparms.fAbortRightMouse = 0;
352
+    cparms.fCaptureAudio = 0;
353
+    cparms.vKeyAbort = 0;
354
+
355
+    ret = SendMessage(ctx->hwnd, WM_CAP_SET_SEQUENCE_SETUP, sizeof(cparms),
356
+                      (LPARAM) &cparms);
357
+    if(!ret) {
358
+        vfw_read_close(s);
359
+        return AVERROR_IO;
360
+    }
361
+
362
+    codec = st->codec;
363
+    codec->time_base = ap->time_base;
364
+    codec->codec_type = CODEC_TYPE_VIDEO;
365
+    codec->width = width;
366
+    codec->height = height;
367
+    codec->codec_id = CODEC_ID_RAWVIDEO;
368
+    codec->pix_fmt = vfw_pixfmt(biCompression, biBitCount);
369
+    if(biCompression == BI_RGB)
370
+        codec->bits_per_sample = biBitCount;
371
+
372
+    av_set_pts_info(st, 32, 1, 1000);
373
+
374
+    if(codec->pix_fmt == -1) {
375
+        av_log(s, AV_LOG_ERROR, "Unknown compression type."
376
+                         "Please report verbose (-v 99) debug information.\n");
377
+        vfw_read_close(s);
378
+        return AVERROR_PATCHWELCOME;
379
+    }
380
+
381
+    ctx->mutex = CreateMutex(NULL, 0, NULL);
382
+    if(!ctx->mutex) {
383
+        av_log(s, AV_LOG_ERROR, "Could not create Mutex.\n" );
384
+        vfw_read_close(s);
385
+        return AVERROR_IO;
386
+    }
387
+    ctx->event = CreateEvent(NULL, 1, 0, NULL);
388
+    if(!ctx->event) {
389
+        av_log(s, AV_LOG_ERROR, "Could not create Event.\n" );
390
+        vfw_read_close(s);
391
+        return AVERROR_IO;
392
+    }
393
+
394
+    ret = SendMessage(ctx->hwnd, WM_CAP_SEQUENCE_NOFILE, 0, 0);
395
+    if(!ret) {
396
+        av_log(s, AV_LOG_ERROR, "Could not start capture sequence.\n" );
397
+        vfw_read_close(s);
398
+        return AVERROR_IO;
399
+    }
400
+
401
+    return 0;
402
+}
403
+
404
+static int vfw_read_packet(AVFormatContext *s, AVPacket *pkt)
405
+{
406
+    struct vfw_ctx *ctx = s->priv_data;
407
+    AVPacketList *pktl = NULL;
408
+
409
+    while(!pktl) {
410
+        WaitForSingleObject(ctx->mutex, INFINITE);
411
+        pktl = ctx->pktl;
412
+        if(ctx->pktl) {
413
+            *pkt = ctx->pktl->pkt;
414
+            ctx->pktl = ctx->pktl->next;
415
+            av_free(pktl);
416
+        }
417
+        ResetEvent(ctx->event);
418
+        ReleaseMutex(ctx->mutex);
419
+        if(!pktl) {
420
+            if(s->flags & AVFMT_FLAG_NONBLOCK) {
421
+                return AVERROR(EAGAIN);
422
+            } else {
423
+                WaitForSingleObject(ctx->event, INFINITE);
424
+            }
425
+        }
426
+    }
427
+
428
+    ctx->curbufsize -= pkt->size;
429
+
430
+    return pkt->size;
431
+}
432
+
433
+static int vfw_read_close(AVFormatContext *s)
434
+{
435
+    struct vfw_ctx *ctx = s->priv_data;
436
+
437
+    if(ctx->hwnd) {
438
+        SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
439
+        SendMessage(ctx->hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
440
+        DestroyWindow(ctx->hwnd);
441
+    }
442
+    if(ctx->mutex)
443
+        CloseHandle(ctx->mutex);
444
+    if(ctx->event)
445
+        CloseHandle(ctx->event);
446
+
447
+    return 0;
448
+}
449
+
450
+AVInputFormat vfwcap_demuxer = {
451
+    "vfwcap",
452
+    "VFW video capture",
453
+    sizeof(struct vfw_ctx),
454
+    NULL,
455
+    vfw_read_header,
456
+    vfw_read_packet,
457
+    vfw_read_close,
458
+    .flags = AVFMT_NOFILE,
459
+};