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