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/avformat.h" 23#include <vfw.h> 24#include <windows.h> 25 26//#define DEBUG_VFW 27 28/* Defines for VFW missing from MinGW. 29 * Remove this when MinGW incorporates them. */ 30#define HWND_MESSAGE ((HWND)-3) 31 32#define BI_RGB 0 33 34/* End of missing MinGW defines */ 35 36struct vfw_ctx { 37 HWND hwnd; 38 HANDLE mutex; 39 HANDLE event; 40 AVPacketList *pktl; 41 AVFormatContext *s; 42 unsigned int curbufsize; 43 unsigned int frame_num; 44}; 45 46static enum PixelFormat vfw_pixfmt(DWORD biCompression, WORD biBitCount) 47{ 48 switch(biCompression) { 49 case MKTAG('Y', 'U', 'Y', '2'): 50 return PIX_FMT_YUYV422; 51 case MKTAG('I', '4', '2', '0'): 52 return PIX_FMT_YUV420P; 53 case BI_RGB: 54 switch(biBitCount) { /* 1-8 are untested */ 55 case 1: 56 return PIX_FMT_MONOWHITE; 57 case 4: 58 return PIX_FMT_RGB4; 59 case 8: 60 return PIX_FMT_RGB8; 61 case 16: 62 return PIX_FMT_RGB555; 63 case 24: 64 return PIX_FMT_BGR24; 65 case 32: 66 return PIX_FMT_RGB32; 67 } 68 } 69 return -1; 70} 71 72#define dstruct(pctx, sname, var, type) \ 73 av_log(pctx, AV_LOG_DEBUG, #var":\t%"type"\n", sname->var) 74 75static void dump_captureparms(AVFormatContext *s, CAPTUREPARMS *cparms) 76{ 77 av_log(s, AV_LOG_DEBUG, "CAPTUREPARMS\n"); 78 dstruct(s, cparms, dwRequestMicroSecPerFrame, "lu"); 79 dstruct(s, cparms, fMakeUserHitOKToCapture, "d"); 80 dstruct(s, cparms, wPercentDropForError, "u"); 81 dstruct(s, cparms, fYield, "d"); 82 dstruct(s, cparms, dwIndexSize, "lu"); 83 dstruct(s, cparms, wChunkGranularity, "u"); 84 dstruct(s, cparms, fUsingDOSMemory, "d"); 85 dstruct(s, cparms, wNumVideoRequested, "u"); 86 dstruct(s, cparms, fCaptureAudio, "d"); 87 dstruct(s, cparms, wNumAudioRequested, "u"); 88 dstruct(s, cparms, vKeyAbort, "u"); 89 dstruct(s, cparms, fAbortLeftMouse, "d"); 90 dstruct(s, cparms, fAbortRightMouse, "d"); 91 dstruct(s, cparms, fLimitEnabled, "d"); 92 dstruct(s, cparms, wTimeLimit, "u"); 93 dstruct(s, cparms, fMCIControl, "d"); 94 dstruct(s, cparms, fStepMCIDevice, "d"); 95 dstruct(s, cparms, dwMCIStartTime, "lu"); 96 dstruct(s, cparms, dwMCIStopTime, "lu"); 97 dstruct(s, cparms, fStepCaptureAt2x, "d"); 98 dstruct(s, cparms, wStepCaptureAverageFrames, "u"); 99 dstruct(s, cparms, dwAudioBufferSize, "lu"); 100 dstruct(s, cparms, fDisableWriteCache, "d"); 101 dstruct(s, cparms, AVStreamMaster, "u"); 102} 103 104static void dump_videohdr(AVFormatContext *s, VIDEOHDR *vhdr) 105{ 106#ifdef DEBUG_VFW 107 av_log(s, AV_LOG_DEBUG, "VIDEOHDR\n"); 108 dstruct(s, vhdr, lpData, "p"); 109 dstruct(s, vhdr, dwBufferLength, "lu"); 110 dstruct(s, vhdr, dwBytesUsed, "lu"); 111 dstruct(s, vhdr, dwTimeCaptured, "lu"); 112 dstruct(s, vhdr, dwUser, "lu"); 113 dstruct(s, vhdr, dwFlags, "lu"); 114 dstruct(s, vhdr, dwReserved[0], "lu"); 115 dstruct(s, vhdr, dwReserved[1], "lu"); 116 dstruct(s, vhdr, dwReserved[2], "lu"); 117 dstruct(s, vhdr, dwReserved[3], "lu"); 118#endif 119} 120 121static void dump_bih(AVFormatContext *s, BITMAPINFOHEADER *bih) 122{ 123 av_log(s, AV_LOG_DEBUG, "BITMAPINFOHEADER\n"); 124 dstruct(s, bih, biSize, "lu"); 125 dstruct(s, bih, biWidth, "ld"); 126 dstruct(s, bih, biHeight, "ld"); 127 dstruct(s, bih, biPlanes, "d"); 128 dstruct(s, bih, biBitCount, "d"); 129 dstruct(s, bih, biCompression, "lu"); 130 av_log(s, AV_LOG_DEBUG, " biCompression:\t\"%.4s\"\n", 131 (char*) &bih->biCompression); 132 dstruct(s, bih, biSizeImage, "lu"); 133 dstruct(s, bih, biXPelsPerMeter, "lu"); 134 dstruct(s, bih, biYPelsPerMeter, "lu"); 135 dstruct(s, bih, biClrUsed, "lu"); 136 dstruct(s, bih, biClrImportant, "lu"); 137} 138 139static int shall_we_drop(struct vfw_ctx *ctx) 140{ 141 AVFormatContext *s = ctx->s; 142 const uint8_t dropscore[] = {62, 75, 87, 100}; 143 const int ndropscores = FF_ARRAY_ELEMS(dropscore); 144 unsigned int buffer_fullness = (ctx->curbufsize*100)/s->max_picture_buffer; 145 146 if(dropscore[++ctx->frame_num%ndropscores] <= buffer_fullness) { 147 av_log(ctx->s, AV_LOG_ERROR, 148 "real-time buffer %d%% full! frame dropped!\n", buffer_fullness); 149 return 1; 150 } 151 152 return 0; 153} 154 155static LRESULT CALLBACK videostream_cb(HWND hwnd, LPVIDEOHDR vdhdr) 156{ 157 struct vfw_ctx *ctx; 158 AVPacketList **ppktl, *pktl_next; 159 160 ctx = (struct vfw_ctx *) GetWindowLongPtr(hwnd, GWLP_USERDATA); 161 162 dump_videohdr(ctx->s, vdhdr); 163 164 if(shall_we_drop(ctx)) 165 return FALSE; 166 167 WaitForSingleObject(ctx->mutex, INFINITE); 168 169 pktl_next = av_mallocz(sizeof(AVPacketList)); 170 if(!pktl_next) 171 goto fail; 172 173 if(av_new_packet(&pktl_next->pkt, vdhdr->dwBytesUsed) < 0) { 174 av_free(pktl_next); 175 goto fail; 176 } 177 178 pktl_next->pkt.pts = vdhdr->dwTimeCaptured; 179 memcpy(pktl_next->pkt.data, vdhdr->lpData, vdhdr->dwBytesUsed); 180 181 for(ppktl = &ctx->pktl ; *ppktl ; ppktl = &(*ppktl)->next); 182 *ppktl = pktl_next; 183 184 ctx->curbufsize += vdhdr->dwBytesUsed; 185 186 SetEvent(ctx->event); 187 ReleaseMutex(ctx->mutex); 188 189 return TRUE; 190fail: 191 ReleaseMutex(ctx->mutex); 192 return FALSE; 193} 194 195static int vfw_read_close(AVFormatContext *s); 196 197static int vfw_read_header(AVFormatContext *s, AVFormatParameters *ap) 198{ 199 struct vfw_ctx *ctx = s->priv_data; 200 AVCodecContext *codec; 201 AVStream *st; 202 int devnum; 203 int bisize; 204 BITMAPINFO *bi; 205 CAPTUREPARMS cparms; 206 DWORD biCompression; 207 WORD biBitCount; 208 int width; 209 int height; 210 int ret; 211 212 if(!ap->time_base.den) { 213 av_log(s, AV_LOG_ERROR, "A time base must be specified.\n"); 214 return AVERROR_IO; 215 } 216 217 ctx->s = s; 218 219 ctx->hwnd = capCreateCaptureWindow(NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0); 220 if(!ctx->hwnd) { 221 av_log(s, AV_LOG_ERROR, "Could not create capture window.\n"); 222 return AVERROR_IO; 223 } 224 225 /* If atoi fails, devnum==0 and the default device is used */ 226 devnum = atoi(s->filename); 227 228 ret = SendMessage(ctx->hwnd, WM_CAP_DRIVER_CONNECT, devnum, 0); 229 if(!ret) { 230 av_log(s, AV_LOG_ERROR, "Could not connect to device.\n"); 231 DestroyWindow(ctx->hwnd); 232 return AVERROR(ENODEV); 233 } 234 235 SendMessage(ctx->hwnd, WM_CAP_SET_OVERLAY, 0, 0); 236 SendMessage(ctx->hwnd, WM_CAP_SET_PREVIEW, 0, 0); 237 238 ret = SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 239 (LPARAM) videostream_cb); 240 if(!ret) { 241 av_log(s, AV_LOG_ERROR, "Could not set video stream callback.\n"); 242 goto fail_io; 243 } 244 245 SetWindowLongPtr(ctx->hwnd, GWLP_USERDATA, (LONG_PTR) ctx); 246 247 st = av_new_stream(s, 0); 248 if(!st) { 249 vfw_read_close(s); 250 return AVERROR_NOMEM; 251 } 252 253 /* Set video format */ 254 bisize = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, 0, 0); 255 if(!bisize) 256 goto fail_io; 257 bi = av_malloc(bisize); 258 if(!bi) { 259 vfw_read_close(s); 260 return AVERROR_NOMEM; 261 } 262 ret = SendMessage(ctx->hwnd, WM_CAP_GET_VIDEOFORMAT, bisize, (LPARAM) bi); 263 if(!ret) 264 goto fail_bi; 265 266 dump_bih(s, &bi->bmiHeader); 267 268 width = ap->width ? ap->width : bi->bmiHeader.biWidth ; 269 height = ap->height ? ap->height : bi->bmiHeader.biHeight; 270 bi->bmiHeader.biWidth = width ; 271 bi->bmiHeader.biHeight = height; 272 273#if 0 274 /* For testing yet unsupported compressions 275 * Copy these values from user-supplied verbose information */ 276 bi->bmiHeader.biWidth = 320; 277 bi->bmiHeader.biHeight = 240; 278 bi->bmiHeader.biPlanes = 1; 279 bi->bmiHeader.biBitCount = 12; 280 bi->bmiHeader.biCompression = MKTAG('I','4','2','0'); 281 bi->bmiHeader.biSizeImage = 115200; 282 dump_bih(s, &bi->bmiHeader); 283#endif 284 285 ret = SendMessage(ctx->hwnd, WM_CAP_SET_VIDEOFORMAT, bisize, (LPARAM) bi); 286 if(!ret) { 287 av_log(s, AV_LOG_ERROR, "Could not set Video Format.\n"); 288 goto fail_bi; 289 } 290 291 biCompression = bi->bmiHeader.biCompression; 292 biBitCount = bi->bmiHeader.biBitCount; 293 294 av_free(bi); 295 296 /* Set sequence setup */ 297 ret = SendMessage(ctx->hwnd, WM_CAP_GET_SEQUENCE_SETUP, sizeof(cparms), 298 (LPARAM) &cparms); 299 if(!ret) 300 goto fail_io; 301 302 dump_captureparms(s, &cparms); 303 304 cparms.fYield = 1; // Spawn a background thread 305 cparms.dwRequestMicroSecPerFrame = 306 (ap->time_base.num*1000000) / ap->time_base.den; 307 cparms.fAbortLeftMouse = 0; 308 cparms.fAbortRightMouse = 0; 309 cparms.fCaptureAudio = 0; 310 cparms.vKeyAbort = 0; 311 312 ret = SendMessage(ctx->hwnd, WM_CAP_SET_SEQUENCE_SETUP, sizeof(cparms), 313 (LPARAM) &cparms); 314 if(!ret) 315 goto fail_io; 316 317 codec = st->codec; 318 codec->time_base = ap->time_base; 319 codec->codec_type = CODEC_TYPE_VIDEO; 320 codec->width = width; 321 codec->height = height; 322 codec->codec_id = CODEC_ID_RAWVIDEO; 323 codec->pix_fmt = vfw_pixfmt(biCompression, biBitCount); 324 if(biCompression == BI_RGB) 325 codec->bits_per_coded_sample = biBitCount; 326 327 av_set_pts_info(st, 32, 1, 1000); 328 329 if(codec->pix_fmt == -1) { 330 av_log(s, AV_LOG_ERROR, "Unknown compression type." 331 "Please report verbose (-v 99) debug information.\n"); 332 vfw_read_close(s); 333 return AVERROR_PATCHWELCOME; 334 } 335 336 ctx->mutex = CreateMutex(NULL, 0, NULL); 337 if(!ctx->mutex) { 338 av_log(s, AV_LOG_ERROR, "Could not create Mutex.\n" ); 339 goto fail_io; 340 } 341 ctx->event = CreateEvent(NULL, 1, 0, NULL); 342 if(!ctx->event) { 343 av_log(s, AV_LOG_ERROR, "Could not create Event.\n" ); 344 goto fail_io; 345 } 346 347 ret = SendMessage(ctx->hwnd, WM_CAP_SEQUENCE_NOFILE, 0, 0); 348 if(!ret) { 349 av_log(s, AV_LOG_ERROR, "Could not start capture sequence.\n" ); 350 goto fail_io; 351 } 352 353 return 0; 354 355fail_bi: 356 av_free(bi); 357 358fail_io: 359 vfw_read_close(s); 360 return AVERROR_IO; 361} 362 363static int vfw_read_packet(AVFormatContext *s, AVPacket *pkt) 364{ 365 struct vfw_ctx *ctx = s->priv_data; 366 AVPacketList *pktl = NULL; 367 368 while(!pktl) { 369 WaitForSingleObject(ctx->mutex, INFINITE); 370 pktl = ctx->pktl; 371 if(ctx->pktl) { 372 *pkt = ctx->pktl->pkt; 373 ctx->pktl = ctx->pktl->next; 374 av_free(pktl); 375 } 376 ResetEvent(ctx->event); 377 ReleaseMutex(ctx->mutex); 378 if(!pktl) { 379 if(s->flags & AVFMT_FLAG_NONBLOCK) { 380 return AVERROR(EAGAIN); 381 } else { 382 WaitForSingleObject(ctx->event, INFINITE); 383 } 384 } 385 } 386 387 ctx->curbufsize -= pkt->size; 388 389 return pkt->size; 390} 391 392static int vfw_read_close(AVFormatContext *s) 393{ 394 struct vfw_ctx *ctx = s->priv_data; 395 396 if(ctx->hwnd) { 397 SendMessage(ctx->hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0); 398 SendMessage(ctx->hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0); 399 DestroyWindow(ctx->hwnd); 400 } 401 if(ctx->mutex) 402 CloseHandle(ctx->mutex); 403 if(ctx->event) 404 CloseHandle(ctx->event); 405 406 return 0; 407} 408 409AVInputFormat vfwcap_demuxer = { 410 "vfwcap", 411 NULL_IF_CONFIG_SMALL("VFW video capture"), 412 sizeof(struct vfw_ctx), 413 NULL, 414 vfw_read_header, 415 vfw_read_packet, 416 vfw_read_close, 417 .flags = AVFMT_NOFILE, 418}; 419