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