You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1190 lines
32 KiB
1190 lines
32 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkWin32VideoSource.cxx,v $
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkWin32VideoSource.h"
|
|
|
|
#include "vtkCriticalSection.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkTimerLog.h"
|
|
#include "vtkUnsignedCharArray.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
// because of warnings in windows header push and pop the warning level
|
|
#ifdef _MSC_VER
|
|
#pragma warning (push, 3)
|
|
#endif
|
|
|
|
#include "vtkWindows.h"
|
|
#include <winuser.h>
|
|
#include <vfw.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning (pop)
|
|
#endif
|
|
|
|
class vtkWin32VideoSourceInternal
|
|
{
|
|
public:
|
|
vtkWin32VideoSourceInternal() {}
|
|
HWND CapWnd;
|
|
HWND ParentWnd;
|
|
CAPSTATUS CapStatus;
|
|
CAPDRIVERCAPS CapDriverCaps;
|
|
CAPTUREPARMS CaptureParms;
|
|
LPBITMAPINFO BitMapPtr;
|
|
};
|
|
|
|
// VFW compressed formats are listed at http://www.webartz.com/fourcc/
|
|
#define VTK_BI_UYVY 0x59565955
|
|
|
|
vtkCxxRevisionMacro(vtkWin32VideoSource, "$Revision: 1.27.12.1 $");
|
|
vtkStandardNewMacro(vtkWin32VideoSource);
|
|
|
|
#if ( _MSC_VER >= 1300 ) // Visual studio .NET
|
|
#pragma warning ( disable : 4311 )
|
|
#pragma warning ( disable : 4312 )
|
|
# define vtkGetWindowLong GetWindowLongPtr
|
|
# define vtkSetWindowLong SetWindowLongPtr
|
|
# define vtkGWL_USERDATA GWLP_USERDATA
|
|
#else // regular Visual studio
|
|
# define vtkGetWindowLong GetWindowLong
|
|
# define vtkSetWindowLong SetWindowLong
|
|
# define vtkGWL_USERDATA GWL_USERDATA
|
|
#endif //
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkWin32VideoSource::vtkWin32VideoSource()
|
|
{
|
|
this->Internal = new vtkWin32VideoSourceInternal;
|
|
this->Initialized = 0;
|
|
|
|
this->FrameRate = 30;
|
|
this->OutputFormat = VTK_RGB;
|
|
this->NumberOfScalarComponents = 3;
|
|
this->FrameBufferBitsPerPixel = 24;
|
|
this->FlipFrames = 0;
|
|
this->FrameBufferRowAlignment = 4;
|
|
|
|
this->Internal->CapWnd = NULL;
|
|
this->Internal->ParentWnd = NULL;
|
|
this->BitMapSize = 0;
|
|
this->Internal->BitMapPtr = NULL;
|
|
this->WndClassName[0] = '\0';
|
|
|
|
this->Preview = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkWin32VideoSource::~vtkWin32VideoSource()
|
|
{
|
|
this->vtkWin32VideoSource::ReleaseSystemResources();
|
|
|
|
if (this->Internal->BitMapPtr != NULL)
|
|
{
|
|
delete [] (char *)(this->Internal->BitMapPtr);
|
|
}
|
|
this->Internal->BitMapPtr = NULL;
|
|
this->BitMapSize = 0;
|
|
delete this->Internal;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
os << indent << "Preview: " << (this->Preview ? "On\n" : "Off\n");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This is empty for now because we aren't displaying the capture window
|
|
LONG FAR PASCAL
|
|
vtkWin32VideoSourceWinProc(HWND hwnd, UINT message,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
vtkWin32VideoSource *self = (vtkWin32VideoSource *)\
|
|
(vtkGetWindowLong(hwnd,vtkGWL_USERDATA));
|
|
|
|
switch(message) {
|
|
|
|
case WM_MOVE:
|
|
//cerr << "WM_MOVE\n";
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
//cerr << "WM_SIZE\n";
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
//cerr << "WM_DESTROY\n";
|
|
self->OnParentWndDestroy();
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
//cerr << "WM_CLOSE\n";
|
|
self->PreviewOff();
|
|
return 0;
|
|
}
|
|
|
|
return(DefWindowProc(hwnd, message, wParam, lParam));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
LRESULT PASCAL vtkWin32VideoSourceCapControlProc(HWND hwndC, int nState)
|
|
{
|
|
vtkWin32VideoSource *self = (vtkWin32VideoSource *)(capGetUserData(hwndC));
|
|
|
|
if (nState == CONTROLCALLBACK_PREROLL)
|
|
{
|
|
//cerr << "controlcallback preroll\n";
|
|
self->SetStartTimeStamp(vtkTimerLog::GetUniversalTime());
|
|
}
|
|
else if (nState == CONTROLCALLBACK_CAPTURING)
|
|
{
|
|
//cerr << "controlcallback capturing\n";
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
LRESULT PASCAL vtkWin32VideoSourceCallbackProc(HWND hwndC, LPVIDEOHDR lpVHdr)
|
|
{
|
|
vtkWin32VideoSource *self = (vtkWin32VideoSource *)(capGetUserData(hwndC));
|
|
self->LocalInternalGrab(lpVHdr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// this callback is left in for debug purposes
|
|
LRESULT PASCAL vtkWin32VideoSourceStatusCallbackProc(HWND vtkNotUsed(hwndC),
|
|
int nID,
|
|
LPCSTR vtkNotUsed(lpsz))
|
|
{
|
|
//vtkWin32VideoSource *self = (vtkWin32VideoSource *)(capGetUserData(hwndC));
|
|
|
|
if (nID == IDS_CAP_BEGIN)
|
|
{
|
|
//cerr << "start of capture\n";
|
|
}
|
|
|
|
if (nID == IDS_CAP_END)
|
|
{
|
|
//cerr << "end of capture\n";
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
LRESULT PASCAL vtkWin32VideoSourceErrorCallbackProc(HWND hwndC,
|
|
int ErrID,
|
|
LPSTR lpErrorText)
|
|
{
|
|
if (ErrID)
|
|
{
|
|
char buff[84];
|
|
sprintf(buff,"Error# %d",ErrID);
|
|
MessageBox(hwndC,lpErrorText, buff, MB_OK | MB_ICONEXCLAMATION);
|
|
//vtkGenericWarningMacro(<< buff << ' ' << lpErrorText);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::Initialize()
|
|
{
|
|
int i;
|
|
|
|
if (this->Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Preliminary update of frame buffer, just in case we don't get
|
|
// though the initialization but need the framebuffer for Updates
|
|
this->UpdateFrameBuffer();
|
|
|
|
// It is necessary to create not one, but two windows in order to
|
|
// do frame grabbing under VFW. Why do we need any?
|
|
|
|
// get necessary process info
|
|
HINSTANCE hinstance = GetModuleHandle(NULL);
|
|
|
|
strcpy(this->WndClassName,"VTKVideo");
|
|
|
|
// set up a class for the main window
|
|
WNDCLASS wc;
|
|
wc.lpszClassName = this->WndClassName;
|
|
wc.hInstance = hinstance;
|
|
wc.lpfnWndProc = reinterpret_cast<WNDPROC>(&vtkWin32VideoSourceWinProc);
|
|
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
|
|
wc.hIcon = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.style = CS_HREDRAW|CS_VREDRAW;
|
|
wc.cbClsExtra = sizeof(void *);
|
|
wc.cbWndExtra = 0;
|
|
|
|
for (i = 1; i <= 10; i++)
|
|
{
|
|
if (RegisterClass(&wc))
|
|
{
|
|
break;
|
|
}
|
|
// try again with a slightly different name
|
|
sprintf(this->WndClassName,"VTKVideo %d",i);
|
|
}
|
|
|
|
if (i > 32)
|
|
{
|
|
vtkErrorMacro(<< "Initialize: failed to register VTKVideo class"\
|
|
<< " (" << GetLastError() << ")");
|
|
return;
|
|
}
|
|
|
|
DWORD style = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|
|
|
WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
|
|
|
|
if (this->Preview)
|
|
{
|
|
style |= WS_VISIBLE;
|
|
}
|
|
|
|
// set up the parent window, but don't show it
|
|
this->Internal->ParentWnd = CreateWindow(
|
|
this->WndClassName,
|
|
"VTK Video Window",
|
|
style,
|
|
0, 0,
|
|
this->FrameSize[0]+2*GetSystemMetrics(SM_CXFIXEDFRAME),
|
|
this->FrameSize[1]+2*GetSystemMetrics(SM_CYFIXEDFRAME)
|
|
+GetSystemMetrics(SM_CYBORDER)
|
|
+GetSystemMetrics(SM_CYSIZE),
|
|
NULL,
|
|
NULL,
|
|
hinstance,
|
|
NULL);
|
|
|
|
if (!this->Internal->ParentWnd)
|
|
{
|
|
vtkErrorMacro(<< "Initialize: failed to create window"\
|
|
<< " (" << GetLastError() << ")");
|
|
return;
|
|
}
|
|
|
|
// set the user data to 'this'
|
|
vtkSetWindowLong(this->Internal->ParentWnd,vtkGWL_USERDATA,(LONG)this);
|
|
|
|
// Create the capture window
|
|
this->Internal->CapWnd = capCreateCaptureWindow("Capture",
|
|
WS_CHILD|WS_VISIBLE, 0, 0,
|
|
this->FrameSize[0], this->FrameSize[1],
|
|
this->Internal->ParentWnd,1);
|
|
|
|
if (!this->Internal->CapWnd)
|
|
{
|
|
vtkErrorMacro(<< "Initialize: failed to create capture window"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
|
|
// connect to the driver
|
|
if (!capDriverConnect(this->Internal->CapWnd,0))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't connect to driver"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
|
|
capDriverGetCaps(this->Internal->CapWnd,&this->Internal->CapDriverCaps,sizeof(CAPDRIVERCAPS));
|
|
|
|
// set up the video format
|
|
this->DoVFWFormatSetup();
|
|
|
|
// set the capture parameters
|
|
capCaptureGetSetup(this->Internal->CapWnd,&this->Internal->CaptureParms,sizeof(CAPTUREPARMS));
|
|
|
|
if (this->FrameRate > 0)
|
|
{
|
|
this->Internal->CaptureParms.dwRequestMicroSecPerFrame =
|
|
int(1000000/this->FrameRate);
|
|
}
|
|
else
|
|
{
|
|
this->Internal->CaptureParms.dwRequestMicroSecPerFrame = 0;
|
|
}
|
|
|
|
this->Internal->CaptureParms.fMakeUserHitOKToCapture = FALSE;
|
|
this->Internal->CaptureParms.fYield = 1;
|
|
this->Internal->CaptureParms.fCaptureAudio = FALSE;
|
|
this->Internal->CaptureParms.vKeyAbort = 0x00;
|
|
this->Internal->CaptureParms.fAbortLeftMouse = FALSE;
|
|
this->Internal->CaptureParms.fAbortRightMouse = FALSE;
|
|
this->Internal->CaptureParms.fLimitEnabled = FALSE;
|
|
this->Internal->CaptureParms.wNumAudioRequested = 0;
|
|
this->Internal->CaptureParms.wPercentDropForError = 100;
|
|
this->Internal->CaptureParms.dwAudioBufferSize = 0;
|
|
this->Internal->CaptureParms.AVStreamMaster = AVSTREAMMASTER_NONE;
|
|
|
|
if (!capCaptureSetSetup(this->Internal->CapWnd,&this->Internal->CaptureParms,
|
|
sizeof(CAPTUREPARMS)))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: setup of capture parameters failed"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
|
|
// set user data for callbacks
|
|
if (!capSetUserData(this->Internal->CapWnd,(long)this))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't set user data for callback"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
|
|
// install the callback to precisely time beginning of grab
|
|
if (!capSetCallbackOnCapControl(this->Internal->CapWnd,
|
|
&vtkWin32VideoSourceCapControlProc))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't set control callback"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
|
|
// install the callback to copy frames into the buffer on sync grabs
|
|
if (!capSetCallbackOnFrame(this->Internal->CapWnd,
|
|
&vtkWin32VideoSourceCallbackProc))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't set frame callback"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
// install the callback to copy frames into the buffer on stream grabs
|
|
if (!capSetCallbackOnVideoStream(this->Internal->CapWnd,
|
|
&vtkWin32VideoSourceCallbackProc))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't set stream callback"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
// install the callback to get info on start/end of streaming
|
|
if (!capSetCallbackOnStatus(this->Internal->CapWnd,
|
|
&vtkWin32VideoSourceStatusCallbackProc))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't set status callback"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
// install the callback to send messages to user
|
|
if (!capSetCallbackOnError(this->Internal->CapWnd,
|
|
&vtkWin32VideoSourceErrorCallbackProc))
|
|
{
|
|
vtkErrorMacro(<< "Initialize: couldn't set error callback"\
|
|
<< " (" << GetLastError() << ")");
|
|
this->ReleaseSystemResources();
|
|
return;
|
|
}
|
|
|
|
capOverlay(this->Internal->CapWnd,TRUE);
|
|
|
|
// update framebuffer again to reflect any changes which
|
|
// might have occurred
|
|
this->UpdateFrameBuffer();
|
|
|
|
this->Initialized = 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::SetPreview(int p)
|
|
{
|
|
if (this->Preview == p)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->Preview = p;
|
|
this->Modified();
|
|
|
|
if (this->Internal->CapWnd == NULL || this->Internal->ParentWnd == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p)
|
|
{
|
|
ShowWindow(this->Internal->ParentWnd,SW_SHOWNORMAL);
|
|
}
|
|
else
|
|
{
|
|
ShowWindow(this->Internal->ParentWnd,SW_HIDE);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::ReleaseSystemResources()
|
|
{
|
|
// destruction of ParentWnd causes OnParentWndDestroy to be called
|
|
if (this->Internal->ParentWnd)
|
|
{
|
|
DestroyWindow(this->Internal->ParentWnd);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::OnParentWndDestroy()
|
|
{
|
|
if (this->Playing || this->Recording)
|
|
{
|
|
this->Stop();
|
|
}
|
|
|
|
if (this->Internal->CapWnd)
|
|
{
|
|
//MessageBox(this->Internal->ParentWnd, "capDriverDisconnect(this->Internal->CapWnd)", "", MB_OK | MB_ICONEXCLAMATION);
|
|
capDriverDisconnect(this->Internal->CapWnd);
|
|
//MessageBox(this->Internal->ParentWnd, "DestroyWindow(this->Internal->CapWnd)", "", MB_OK | MB_ICONEXCLAMATION);
|
|
DestroyWindow(this->Internal->CapWnd);
|
|
this->Internal->CapWnd = NULL;
|
|
}
|
|
if (this->WndClassName[0] != '\0')
|
|
{
|
|
UnregisterClass(this->WndClassName,GetModuleHandle(NULL));
|
|
this->WndClassName[0] = '\0';
|
|
}
|
|
|
|
this->Internal->ParentWnd = NULL;
|
|
this->Initialized = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// copy the Device Independent Bitmap from the VFW framebuffer into the
|
|
// vtkVideoSource framebuffer (don't do the unpacking yet)
|
|
void vtkWin32VideoSource::LocalInternalGrab(void* lpptr)
|
|
{
|
|
LPVIDEOHDR lpVHdr = static_cast<LPVIDEOHDR>(lpptr);
|
|
// cerr << "Grabbed\n";
|
|
|
|
// the VIDEOHDR has the following contents, for quick ref:
|
|
//
|
|
// lpData pointer to locked data buffer
|
|
// dwBufferLength Length of data buffer
|
|
// dwBytesUsed Bytes actually used
|
|
// dwTimeCaptured Milliseconds from start of stream
|
|
// dwUser for client's use
|
|
// dwFlags assorted flags (see VFW.H)
|
|
// dwReserved[4] reserved for driver
|
|
|
|
unsigned char *cptrDIB = lpVHdr->lpData;
|
|
|
|
// get a thread lock on the frame buffer
|
|
this->FrameBufferMutex->Lock();
|
|
|
|
if (this->AutoAdvance)
|
|
{
|
|
this->AdvanceFrameBuffer(1);
|
|
if (this->FrameIndex + 1 < this->FrameBufferSize)
|
|
{
|
|
this->FrameIndex++;
|
|
}
|
|
}
|
|
|
|
int index = this->FrameBufferIndex;
|
|
|
|
this->FrameCount++;
|
|
this->FrameBufferTimeStamps[index] = this->StartTimeStamp + \
|
|
0.001 * lpVHdr->dwTimeCaptured;
|
|
|
|
unsigned char *ptr = (unsigned char *)
|
|
((reinterpret_cast<vtkUnsignedCharArray*>(this->FrameBuffer[index])) \
|
|
->GetPointer(0));
|
|
|
|
// the DIB has rows which are multiples of 4 bytes
|
|
int outBytesPerRow = ((this->FrameBufferExtent[1]-
|
|
this->FrameBufferExtent[0]+1)
|
|
* this->FrameBufferBitsPerPixel + 7)/8;
|
|
outBytesPerRow += outBytesPerRow % this->FrameBufferRowAlignment;
|
|
int inBytesPerRow = this->FrameSize[0]
|
|
* (this->Internal->BitMapPtr->bmiHeader.biBitCount/8);
|
|
outBytesPerRow += outBytesPerRow % 4;
|
|
int rows = this->FrameBufferExtent[3]-this->FrameBufferExtent[2]+1;
|
|
|
|
cptrDIB += this->FrameBufferExtent[0]*\
|
|
(this->Internal->BitMapPtr->bmiHeader.biBitCount/8);
|
|
cptrDIB += this->FrameBufferExtent[2]*inBytesPerRow;
|
|
|
|
// uncompress or simply copy the DIB
|
|
switch (this->Internal->BitMapPtr->bmiHeader.biCompression)
|
|
{
|
|
case BI_RGB:
|
|
case VTK_BI_UYVY:
|
|
if (outBytesPerRow == inBytesPerRow)
|
|
{
|
|
memcpy(ptr,cptrDIB,inBytesPerRow*rows);
|
|
}
|
|
else
|
|
{
|
|
while (--rows >= 0)
|
|
{
|
|
memcpy(ptr,cptrDIB,outBytesPerRow);
|
|
ptr += outBytesPerRow;
|
|
cptrDIB += inBytesPerRow;
|
|
}
|
|
}
|
|
break;
|
|
case BI_RLE8: // not handled
|
|
case BI_RLE4:
|
|
case BI_BITFIELDS:
|
|
break;
|
|
}
|
|
|
|
this->Modified();
|
|
|
|
this->FrameBufferMutex->Unlock();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::Grab()
|
|
{
|
|
if (this->Recording)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// ensure that the frame buffer is properly initialized
|
|
this->Initialize();
|
|
if (!this->Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// just do the grab, the callback does the rest
|
|
this->SetStartTimeStamp(vtkTimerLog::GetUniversalTime());
|
|
capGrabFrameNoStop(this->Internal->CapWnd);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::Record()
|
|
{
|
|
this->Initialize();
|
|
if (!this->Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (this->Playing)
|
|
{
|
|
this->Stop();
|
|
}
|
|
|
|
if (!this->Recording)
|
|
{
|
|
this->Recording = 1;
|
|
this->Modified();
|
|
capCaptureSequenceNoFile(this->Internal->CapWnd);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::Play()
|
|
{
|
|
this->vtkVideoSource::Play();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::Stop()
|
|
{
|
|
if (this->Recording)
|
|
{
|
|
this->Recording = 0;
|
|
this->Modified();
|
|
|
|
capCaptureStop(this->Internal->CapWnd);
|
|
}
|
|
else if (this->Playing)
|
|
{
|
|
this->vtkVideoSource::Stop();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// codecs
|
|
|
|
static inline void vtkYUVToRGB(unsigned char *yuv, unsigned char *rgb)
|
|
{
|
|
/* // floating point
|
|
int Y = yuv[0] - 16;
|
|
int U = yuv[1] - 128;
|
|
int V = yuv[2] - 128;
|
|
|
|
int R = 1.164*Y + 1.596*V + 0.5;
|
|
int G = 1.164*Y - 0.813*V - 0.391*U + 0.5;
|
|
int B = 1.164*Y + 2.018*U + 0.5;
|
|
*/
|
|
|
|
// integer math
|
|
int Y = (yuv[0] - 16)*76284;
|
|
int U = yuv[1] - 128;
|
|
int V = yuv[2] - 128;
|
|
|
|
int R = Y + 104595*V ;
|
|
int G = Y - 53281*V - 25625*U;
|
|
int B = Y + 132252*U;
|
|
|
|
// round
|
|
R += 32768;
|
|
G += 32768;
|
|
B += 32768;
|
|
|
|
// shift
|
|
R >>= 16;
|
|
G >>= 16;
|
|
B >>= 16;
|
|
|
|
// clamp
|
|
if (R < 0) { R = 0; }
|
|
if (G < 0) { G = 0; }
|
|
if (B < 0) { B = 0; }
|
|
|
|
if (R > 255) { R = 255; };
|
|
if (G > 255) { G = 255; };
|
|
if (B > 255) { B = 255; };
|
|
|
|
// output
|
|
rgb[0] = R;
|
|
rgb[1] = G;
|
|
rgb[2] = B;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::UnpackRasterLine(char *outptr, char *inptr,
|
|
int start, int count)
|
|
{
|
|
char alpha = (char)(this->Opacity*255);
|
|
int compression = this->Internal->BitMapPtr->bmiHeader.biCompression;
|
|
int i;
|
|
|
|
switch (this->FrameBufferBitsPerPixel)
|
|
{
|
|
case 1:
|
|
{
|
|
int rawBits;
|
|
inptr += start/8;
|
|
i = start % 8;
|
|
while (count >= 0)
|
|
{
|
|
rawBits = *inptr++;
|
|
for (; i < 8 && --count >= 0; i++)
|
|
{
|
|
*outptr++ = -((rawBits >> i) & 0x01);
|
|
}
|
|
i = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 4:
|
|
{
|
|
int rawNibbles;
|
|
inptr += start/2;
|
|
i = start % 2;
|
|
while (count >= 0)
|
|
{
|
|
rawNibbles = *inptr++;
|
|
for (; i < 8 && --count >= 0; i += 4)
|
|
{
|
|
*outptr++ = ((rawNibbles >> i) & 0x0f) << 4;
|
|
}
|
|
i = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 8:
|
|
{
|
|
inptr += start;
|
|
memcpy(outptr,inptr,count);
|
|
}
|
|
break;
|
|
case 16:
|
|
{
|
|
inptr += 2*start;
|
|
if (compression == VTK_BI_UYVY)
|
|
{
|
|
switch (this->OutputFormat)
|
|
{
|
|
case VTK_LUMINANCE:
|
|
{ // unpack UY half-megapixel to one Y pixel
|
|
while (--count >= 0)
|
|
{
|
|
inptr++;
|
|
*outptr++ = *inptr++;
|
|
}
|
|
}
|
|
case VTK_RGB:
|
|
case VTK_RGBA:
|
|
{ // unpack UYVY megapixel to two RGB or RGBA pixels
|
|
unsigned char YUV[3];
|
|
//int finish = start + count;
|
|
int odd = (start % 2 == 1);
|
|
if (count > 0) { YUV[1+odd] = inptr[0]; }
|
|
if (count > 1) { YUV[0] = inptr[1]; }
|
|
if (count > 2) { YUV[2-odd] = inptr[2]; }
|
|
while (--count >= 0)
|
|
{
|
|
YUV[1+odd] = *inptr++;
|
|
YUV[0] = *inptr++;
|
|
odd = !odd;
|
|
vtkYUVToRGB(YUV,(unsigned char *)outptr);
|
|
outptr += 3;
|
|
if (this->OutputFormat == VTK_RGB)
|
|
{
|
|
continue;
|
|
}
|
|
*outptr++ = alpha;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned short rawWord;
|
|
unsigned short *shptr = (unsigned short *)inptr;
|
|
switch (this->OutputFormat)
|
|
{
|
|
case VTK_RGB:
|
|
{ // unpack 16 bits to 24 bits
|
|
while (--count >= 0)
|
|
{
|
|
rawWord = *shptr++;
|
|
*outptr++ = (rawWord & 0x7c00) >> 7;
|
|
*outptr++ = (rawWord & 0x03e0) >> 2;
|
|
*outptr++ = (rawWord & 0x001f) << 3;
|
|
}
|
|
}
|
|
break;
|
|
case VTK_RGBA:
|
|
{ // unpack 16 bits to 32 bits
|
|
while (--count >= 0)
|
|
{
|
|
rawWord = *shptr++;
|
|
*outptr++ = (rawWord & 0x7c00) >> 7;
|
|
*outptr++ = (rawWord & 0x03e0) >> 2;
|
|
*outptr++ = (rawWord & 0x001f) << 3;
|
|
*outptr++ = alpha;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case 24:
|
|
{
|
|
inptr += 3*start;
|
|
switch (this->OutputFormat)
|
|
{
|
|
case VTK_RGB:
|
|
{ // must do BGR to RGB conversion
|
|
outptr += 3;
|
|
while (--count >= 0)
|
|
{
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
outptr += 6;
|
|
}
|
|
}
|
|
break;
|
|
case VTK_RGBA:
|
|
{ // must do BGR to RGBX conversion
|
|
outptr += 4;
|
|
while (--count >= 0)
|
|
{
|
|
*--outptr = alpha;
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
outptr += 8;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 32:
|
|
inptr += 4*start;
|
|
switch (this->OutputFormat)
|
|
{
|
|
case VTK_RGB:
|
|
{ // must do BGRX to RGB conversion
|
|
outptr += 3;
|
|
while (--count >= 0)
|
|
{
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
inptr++;
|
|
outptr += 6;
|
|
}
|
|
}
|
|
break;
|
|
case VTK_RGBA:
|
|
{
|
|
outptr += 4;
|
|
while (--count >= 0)
|
|
{
|
|
*--outptr = alpha;
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
*--outptr = *inptr++;
|
|
inptr++;
|
|
outptr += 8;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::VideoFormatDialog()
|
|
{
|
|
this->Initialize();
|
|
if (!this->Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//if (!this->Internal->CapDriverCaps.fHasDlgVideoFormat)
|
|
// {
|
|
// MessageBox(this->Internal->ParentWnd,"The video device has no Format dialog.","",
|
|
// MB_OK | MB_ICONEXCLAMATION);
|
|
// return;
|
|
// }
|
|
|
|
capGetStatus(this->Internal->CapWnd,&this->Internal->CapStatus,sizeof(CAPSTATUS));
|
|
if (this->Internal->CapStatus.fCapturingNow)
|
|
{
|
|
MessageBox(this->Internal->ParentWnd, "Can't alter video format while grabbing.","",
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
return;
|
|
}
|
|
|
|
int success = capDlgVideoFormat(this->Internal->CapWnd);
|
|
if (success)
|
|
{
|
|
this->FrameBufferMutex->Lock();
|
|
this->DoVFWFormatCheck();
|
|
this->FrameBufferMutex->Unlock();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::VideoSourceDialog()
|
|
{
|
|
this->Initialize();
|
|
if (!this->Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//if (!this->Internal->CapDriverCaps.fHasDlgVideoSource)
|
|
// {
|
|
// MessageBox(this->Internal->ParentWnd,"The video device has no Source dialog.","",
|
|
// MB_OK | MB_ICONEXCLAMATION);
|
|
// return;
|
|
// }
|
|
|
|
capGetStatus(this->Internal->CapWnd,&this->Internal->CapStatus,sizeof(CAPSTATUS));
|
|
if (this->Internal->CapStatus.fCapturingNow)
|
|
{
|
|
MessageBox(this->Internal->ParentWnd, "Can't alter video source while grabbing.","",
|
|
MB_OK | MB_ICONEXCLAMATION);
|
|
return;
|
|
}
|
|
|
|
int success = capDlgVideoSource(this->Internal->CapWnd);
|
|
if (success)
|
|
{
|
|
this->FrameBufferMutex->Lock();
|
|
this->DoVFWFormatCheck();
|
|
this->FrameBufferMutex->Unlock();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// try for the specified frame size
|
|
void vtkWin32VideoSource::SetFrameSize(int x, int y, int z)
|
|
{
|
|
if (x == this->FrameSize[0] &&
|
|
y == this->FrameSize[1] &&
|
|
z == this->FrameSize[2])
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (x < 1 || y < 1 || z != 1)
|
|
{
|
|
vtkErrorMacro(<< "SetFrameSize: Illegal frame size");
|
|
return;
|
|
}
|
|
|
|
this->FrameSize[0] = x;
|
|
this->FrameSize[1] = y;
|
|
this->FrameSize[2] = z;
|
|
this->Modified();
|
|
|
|
if (this->Initialized)
|
|
{
|
|
this->FrameBufferMutex->Lock();
|
|
this->UpdateFrameBuffer();
|
|
this->DoVFWFormatSetup();
|
|
this->FrameBufferMutex->Unlock();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::SetFrameRate(float rate)
|
|
{
|
|
if (rate == this->FrameRate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->FrameRate = rate;
|
|
this->Modified();
|
|
|
|
if (this->Initialized)
|
|
{
|
|
capCaptureGetSetup(this->Internal->CapWnd,&this->Internal->CaptureParms,sizeof(CAPTUREPARMS));
|
|
if (this->FrameRate > 0)
|
|
{
|
|
this->Internal->CaptureParms.dwRequestMicroSecPerFrame =
|
|
int(1000000/this->FrameRate);
|
|
}
|
|
else
|
|
{
|
|
this->Internal->CaptureParms.dwRequestMicroSecPerFrame = 0;
|
|
}
|
|
capCaptureSetSetup(this->Internal->CapWnd,&this->Internal->CaptureParms,sizeof(CAPTUREPARMS));
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::SetOutputFormat(int format)
|
|
{
|
|
if (format == this->OutputFormat)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->OutputFormat = format;
|
|
|
|
// convert color format to number of scalar components
|
|
int numComponents;
|
|
|
|
switch (this->OutputFormat)
|
|
{
|
|
case VTK_RGBA:
|
|
numComponents = 4;
|
|
break;
|
|
case VTK_RGB:
|
|
numComponents = 3;
|
|
break;
|
|
case VTK_LUMINANCE:
|
|
numComponents = 1;
|
|
break;
|
|
default:
|
|
numComponents = 0;
|
|
vtkErrorMacro(<< "SetOutputFormat: Unrecognized color format.");
|
|
break;
|
|
}
|
|
this->NumberOfScalarComponents = numComponents;
|
|
|
|
if (this->FrameBufferBitsPerPixel != numComponents*8)
|
|
{
|
|
this->FrameBufferMutex->Lock();
|
|
this->FrameBufferBitsPerPixel = numComponents*8;
|
|
if (this->Initialized)
|
|
{
|
|
this->UpdateFrameBuffer();
|
|
this->DoVFWFormatSetup();
|
|
}
|
|
this->FrameBufferMutex->Unlock();
|
|
}
|
|
|
|
this->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// check the current video format and set up the VTK video framebuffer to match
|
|
void vtkWin32VideoSource::DoVFWFormatCheck()
|
|
{
|
|
// get the real video format
|
|
int formatSize = capGetVideoFormatSize(this->Internal->CapWnd);
|
|
if (formatSize > this->BitMapSize)
|
|
{
|
|
if (this->Internal->BitMapPtr)
|
|
{
|
|
delete [] ((char *)this->Internal->BitMapPtr);
|
|
}
|
|
this->Internal->BitMapPtr = (LPBITMAPINFO) new char[formatSize];
|
|
this->BitMapSize = formatSize;
|
|
}
|
|
capGetVideoFormat(this->Internal->CapWnd,this->Internal->BitMapPtr,formatSize);
|
|
|
|
int bpp = this->Internal->BitMapPtr->bmiHeader.biBitCount;
|
|
int width = this->Internal->BitMapPtr->bmiHeader.biWidth;
|
|
int height = this->FrameSize[1] = this->Internal->BitMapPtr->bmiHeader.biHeight;
|
|
int compression = this->Internal->BitMapPtr->bmiHeader.biCompression;
|
|
|
|
if (compression == VTK_BI_UYVY)
|
|
{
|
|
this->FlipFrames = 1;
|
|
}
|
|
else if (compression == BI_RGB)
|
|
{
|
|
this->FlipFrames = 0;
|
|
}
|
|
else
|
|
{
|
|
char fourcchex[16], fourcc[8];
|
|
sprintf(fourcchex,"0x%08x",compression);
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
fourcc[i] = (compression >> (8*i)) & 0xff;
|
|
if (!isprint(fourcc[i]))
|
|
{
|
|
fourcc[i] = '?';
|
|
}
|
|
}
|
|
fourcc[4] = '\0';
|
|
vtkWarningMacro(<< "DoVFWFormatCheck: video compression mode " <<
|
|
fourcchex << " \"" << fourcc << "\": can't grab");
|
|
}
|
|
|
|
if (bpp != this->FrameBufferBitsPerPixel)
|
|
{
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
case 4:
|
|
case 8:
|
|
this->OutputFormat = VTK_LUMINANCE;
|
|
this->NumberOfScalarComponents = 1;
|
|
break;
|
|
case 16:
|
|
if (compression != VTK_BI_UYVY)
|
|
{
|
|
this->OutputFormat = VTK_RGB;
|
|
this->NumberOfScalarComponents = 3;
|
|
}
|
|
break;
|
|
case 24:
|
|
case 32:
|
|
if (this->OutputFormat != VTK_RGBA)
|
|
{
|
|
this->OutputFormat = VTK_RGB;
|
|
this->NumberOfScalarComponents = 3;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bpp != this->FrameBufferBitsPerPixel ||
|
|
this->FrameSize[0] != width ||
|
|
this->FrameSize[1] != height)
|
|
{
|
|
this->FrameBufferBitsPerPixel = bpp;
|
|
this->FrameSize[0] = width;
|
|
this->FrameSize[1] = height;
|
|
this->Modified();
|
|
this->UpdateFrameBuffer();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkWin32VideoSource::DoVFWFormatSetup()
|
|
{
|
|
static int colorBits[3] = { 24, 32, 16 };
|
|
static int greyBits[3] = { 8, 4, 1 };
|
|
int i, bytesPerRow, bitCount;
|
|
|
|
// get the real video format
|
|
int formatSize = capGetVideoFormatSize(this->Internal->CapWnd);
|
|
if (formatSize > this->BitMapSize)
|
|
{
|
|
if (this->Internal->BitMapPtr)
|
|
{
|
|
delete [] ((char *)this->Internal->BitMapPtr);
|
|
}
|
|
this->Internal->BitMapPtr = (LPBITMAPINFO) new char[formatSize];
|
|
this->BitMapSize = formatSize;
|
|
}
|
|
capGetVideoFormat(this->Internal->CapWnd,this->Internal->BitMapPtr,formatSize);
|
|
|
|
// set the format of the captured frames
|
|
this->Internal->BitMapPtr->bmiHeader.biWidth = this->FrameSize[0];
|
|
this->Internal->BitMapPtr->bmiHeader.biHeight = this->FrameSize[1];
|
|
this->Internal->BitMapPtr->bmiHeader.biCompression = BI_RGB;
|
|
this->Internal->BitMapPtr->bmiHeader.biClrUsed = 0;
|
|
this->Internal->BitMapPtr->bmiHeader.biClrImportant = 0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{ // try for a
|
|
if (this->OutputFormat == VTK_RGBA || this->OutputFormat == VTK_RGB)
|
|
{
|
|
bitCount = colorBits[i];
|
|
}
|
|
else
|
|
{
|
|
bitCount = greyBits[i];
|
|
}
|
|
bytesPerRow = (this->FrameSize[0]*bitCount+7)/8;
|
|
bytesPerRow += bytesPerRow % this->FrameBufferRowAlignment;
|
|
this->Internal->BitMapPtr->bmiHeader.biBitCount = bitCount;
|
|
this->Internal->BitMapPtr->bmiHeader.biSizeImage = bytesPerRow*this->FrameSize[1];
|
|
if (capSetVideoFormat(this->Internal->CapWnd,this->Internal->BitMapPtr,
|
|
sizeof(BITMAPINFOHEADER)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i > 4)
|
|
{
|
|
vtkWarningMacro(<< "DoVFWFormatSetup: invalid video format for device"\
|
|
<< " (" << GetLastError() << ")");
|
|
}
|
|
this->DoVFWFormatCheck();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|