A DirectShow camera control for live webcam display in wNim applications. This control captures video from DirectShow-compatible cameras and displays it in real-time using native Windows APIs with support for multiple video formats, camera controls, and frame preprocessing.
Features
- Multiple Video Formats: Native support for RGB24, RGB32, YUY2, and MJPEG formats
- Format Enumeration: Query and switch between available camera formats and resolutions
- Camera Controls: Full access to VideoProcAmp (brightness, contrast, etc.) and CameraControl (exposure, focus, etc.)
- Frame Preprocessing: Register callbacks to process raw video frames before display
- Format-Aware Processing: Preprocessor receives format information for native format handling
- Flexible Display: Fit, stretch, or actual size display modes with vertical flip support
Basic Usage
import wnim/[wApp, wFrame, wPanel] import wDSCamera let app = App() let frame = Frame(title="Camera", size=(800, 600)) let panel = Panel(frame) # Create camera with fit mode let camera = DSCamera(panel, cameraIndex=0, pos=(10, 10), size=(640, 480), style=wDSCameraFit) frame.show() app.mainLoop()
Format Control
# Enumerate available formats let formats = camera.enumerateFormats() for fmt in formats: echo fmt.width, "x", fmt.height, " @ ", fmt.fps, "fps - ", fmt.formatName # Set specific format if formats.len > 0: discard camera.setFormat(formats[0]) # Or set by resolution (automatically picks best format) discard camera.setFormatByResolution(1280, 720, preferUncompressed=false) # Get current format let currentFormat = camera.getCurrentPixelFormat() echo "Current format: ", currentFormat # e.g., YUY2, RGB24, MJPG
Camera Controls
# Get brightness range let range = camera.getVideoProcAmpRange(VideoProcAmp_Brightness) echo "Brightness: ", range.min, " to ", range.max, " (default: ", range.default, ")" # Set brightness manually discard camera.setVideoProcAmp(VideoProcAmp_Brightness, 50, isAuto=false) # Enable auto brightness discard camera.setVideoProcAmp(VideoProcAmp_Brightness, 0, isAuto=true) # Get current brightness let current = camera.getVideoProcAmp(VideoProcAmp_Brightness) echo "Value: ", current.value, " Auto: ", current.isAuto # Control exposure discard camera.setCameraControl(CameraControl_Exposure, -5, isAuto=false)
Frame Preprocessing
Register a callback to process raw video frames before display. The preprocessor receives the native format (YUY2, RGB24, etc.) for optimal processing.
# Example: Add timestamp overlay camera.setFramePreprocessor( proc(frameBuffer: var seq[byte], width, height: int32, format: wPixelFormat, userdata: pointer) = case format of wPixelFormat.YUY2: # Process native YUY2 data (4 bytes per 2 pixels: Y0,U,Y1,V) # Y channel is at indices 0, 2, 4, 6... for grayscale processing for i in 0..<(width * height): let yIndex = (i div 2) * 4 + (if (i mod 2) == 0: 0 else: 2) frameBuffer[yIndex] = min(255, frameBuffer[yIndex] + 20) # Brighten of wPixelFormat.RGB24: # Process RGB24 data (3 bytes per pixel: B,G,R) for i in 0..<(width * height): let idx = i * 3 frameBuffer[idx] = min(255, frameBuffer[idx].int + 20).byte # Boost blue else: discard , nil # optional userdata pointer )
Events
# Camera connected and ready camera.wEvent_DSCameraConnected do(): let size = camera.getFrameSize() echo "Camera ready: ", size.width, "x", size.height # Can now query formats, controls, etc. let formats = camera.enumerateFormats() echo "Available formats: ", formats.len # New frame available (fires at camera framerate) camera.wEvent_DSCameraFrame do(): # Frame is ready, can get buffer with getFrameBuffer() discard # Camera error camera.wEvent_DSCameraError do(): echo "Camera error occurred"
| Superclass: | wControl |
|---|
Styles
| Style | Description |
|---|---|
| wDSCameraFlipVertical | Flip the camera image vertically |
| wDSCameraFit | Scale camera image to fit control (maintains aspect ratio) |
| wDSCameraStretch | Stretch camera image to fill control |
Events
| Event | Description |
|---|---|
| wEvent_DSCameraConnected | Camera successfully connected and started |
| wEvent_DSCameraDisconnected | Camera disconnected |
| wEvent_DSCameraError | Error occurred during capture |
| wEvent_DSCameraFrame | New frame captured (fires at camera framerate) |
See Also
- VideoProcAmp Controls: Brightness, Contrast, Hue, Saturation, etc.
- CameraControl Controls: Exposure, Focus, Zoom, Pan, Tilt, etc.
- wPixelFormat enum: RGB24, RGB32, YUY2, MJPG format identifiers
- wVideoFormat type: Video format descriptor with resolution, FPS, etc.
Types
AM_MEDIA_TYPE {.pure.} = object majortype*: GUID subtype*: GUID bFixedSizeSamples*: BOOL bTemporalCompression*: BOOL lSampleSize*: ULONG formattype*: GUID pUnk*: ptr IUnknown cbFormat*: ULONG pbFormat*: ptr BYTE
CameraControlFlags {.pure.} = enum CameraControl_Flags_Auto = 1, CameraControl_Flags_Manual = 2
CameraControlProperty {.pure.} = enum CameraControl_Pan = 0, CameraControl_Tilt = 1, CameraControl_Roll = 2, CameraControl_Zoom = 3, CameraControl_Exposure = 4, CameraControl_Iris = 5, CameraControl_Focus = 6
IAMCameraControl {.pure.} = object lpVtbl*: ptr IAMCameraControlVtbl
IAMCameraControlVtbl {.pure.} = object of IUnknownVtbl GetRange*: proc (this: ptr IAMCameraControl; Property: LONG; pMin: ptr LONG; pMax: ptr LONG; pSteppingDelta: ptr LONG; pDefault: ptr LONG; pCapsFlags: ptr LONG): HRESULT {.stdcall.} Set*: proc (this: ptr IAMCameraControl; Property: LONG; lValue: LONG; Flags: LONG): HRESULT {.stdcall.} Get*: proc (this: ptr IAMCameraControl; Property: LONG; lValue: ptr LONG; Flags: ptr LONG): HRESULT {.stdcall.}
IAMStreamConfig {.pure.} = object lpVtbl*: ptr IAMStreamConfigVtbl
IAMStreamConfigVtbl {.pure.} = object of IUnknownVtbl SetFormat*: proc (this: ptr IAMStreamConfig; pmt: ptr AM_MEDIA_TYPE): HRESULT {. stdcall.} GetFormat*: proc (this: ptr IAMStreamConfig; ppmt: ptr ptr AM_MEDIA_TYPE): HRESULT {. stdcall.} GetNumberOfCapabilities*: proc (this: ptr IAMStreamConfig; piCount: ptr int32; piSize: ptr int32): HRESULT {.stdcall.} GetStreamCaps*: proc (this: ptr IAMStreamConfig; iIndex: int32; ppmt: ptr ptr AM_MEDIA_TYPE; pSCC: pointer): HRESULT {. stdcall.}
IAMVideoProcAmp {.pure.} = object lpVtbl*: ptr IAMVideoProcAmpVtbl
IAMVideoProcAmpVtbl {.pure.} = object of IUnknownVtbl GetRange*: proc (this: ptr IAMVideoProcAmp; Property: LONG; pMin: ptr LONG; pMax: ptr LONG; pSteppingDelta: ptr LONG; pDefault: ptr LONG; pCapsFlags: ptr LONG): HRESULT {.stdcall.} Set*: proc (this: ptr IAMVideoProcAmp; Property: LONG; lValue: LONG; Flags: LONG): HRESULT {.stdcall.} Get*: proc (this: ptr IAMVideoProcAmp; Property: LONG; lValue: ptr LONG; Flags: ptr LONG): HRESULT {.stdcall.}
IBaseFilter {.pure.} = object lpVtbl*: ptr IBaseFilterVtbl
IBaseFilterVtbl {.pure.} = object of IUnknownVtbl GetClassID*: proc (this: ptr IBaseFilter; pClassID: ptr CLSID): HRESULT {. stdcall.} Stop*: proc (this: ptr IBaseFilter): HRESULT {.stdcall.} Pause*: proc (this: ptr IBaseFilter): HRESULT {.stdcall.} Run*: proc (this: ptr IBaseFilter; tStart: REFERENCE_TIME): HRESULT {.stdcall.} GetState*: proc (this: ptr IBaseFilter; dwMilliSecsTimeout: DWORD; State: ptr int32): HRESULT {.stdcall.} SetSyncSource*: proc (this: ptr IBaseFilter; pClock: pointer): HRESULT {. stdcall.} GetSyncSource*: proc (this: ptr IBaseFilter; pClock: ptr pointer): HRESULT {. stdcall.} EnumPins*: proc (this: ptr IBaseFilter; ppEnum: ptr pointer): HRESULT {. stdcall.} FindPin*: proc (this: ptr IBaseFilter; Id: LPCWSTR; ppPin: ptr pointer): HRESULT {. stdcall.} QueryFilterInfo*: proc (this: ptr IBaseFilter; pInfo: pointer): HRESULT {. stdcall.} JoinFilterGraph*: proc (this: ptr IBaseFilter; pGraph: pointer; pName: LPCWSTR): HRESULT {. stdcall.} QueryVendorInfo*: proc (this: ptr IBaseFilter; pVendorInfo: ptr LPWSTR): HRESULT {. stdcall.}
ICaptureGraphBuilder2 {.pure.} = object lpVtbl*: ptr ICaptureGraphBuilder2Vtbl
ICaptureGraphBuilder2Vtbl {.pure.} = object of IUnknownVtbl SetFiltergraph*: proc (this: ptr ICaptureGraphBuilder2; pfg: ptr IGraphBuilder): HRESULT {. stdcall.} GetFiltergraph*: proc (this: ptr ICaptureGraphBuilder2; ppfg: ptr ptr IGraphBuilder): HRESULT {.stdcall.} SetOutputFileName*: proc (this: ptr ICaptureGraphBuilder2; pType: ptr GUID; lpstrFile: LPCOLESTR; ppf: ptr ptr IBaseFilter; ppSink: ptr pointer): HRESULT {.stdcall.} FindInterface*: proc (this: ptr ICaptureGraphBuilder2; pCategory: ptr GUID; pType: ptr GUID; pf: ptr IBaseFilter; riid: REFIID; ppint: ptr pointer): HRESULT {.stdcall.} RenderStream*: proc (this: ptr ICaptureGraphBuilder2; pCategory: ptr GUID; pType: ptr GUID; pSource: ptr IUnknown; pfCompressor: ptr IBaseFilter; pfRenderer: ptr IBaseFilter): HRESULT {.stdcall.} ControlStream*: proc (this: ptr ICaptureGraphBuilder2; pCategory: ptr GUID; pType: ptr GUID; pFilter: ptr IBaseFilter; pstart: ptr REFERENCE_TIME; pstop: ptr REFERENCE_TIME; wStartCookie: WORD; wStopCookie: WORD): HRESULT {. stdcall.} AllocCapFile*: proc (this: ptr ICaptureGraphBuilder2; lpstr: LPCOLESTR; dwlSize: DWORDLONG): HRESULT {.stdcall.} CopyCaptureFile*: proc (this: ptr ICaptureGraphBuilder2; lpwstrOld: LPOLESTR; lpwstrNew: LPOLESTR; fAllowEscAbort: int32; pCallback: pointer): HRESULT {.stdcall.} FindPin*: proc (this: ptr ICaptureGraphBuilder2; pSource: ptr IUnknown; pindir: int32; pCategory: ptr GUID; pType: ptr GUID; fUnconnected: BOOL; num: int32; ppPin: ptr pointer): HRESULT {. stdcall.}
ICreateDevEnum {.pure.} = object lpVtbl*: ptr ICreateDevEnumVtbl
ICreateDevEnumVtbl {.pure.} = object of IUnknownVtbl CreateClassEnumerator*: proc (this: ptr ICreateDevEnum; clsidDeviceClass: REFCLSID; ppEnumMoniker: ptr ptr IEnumMoniker; dwFlags: DWORD): HRESULT {.stdcall.}
IEnumPins {.pure.} = object lpVtbl*: ptr IEnumPinsVtbl
IEnumPinsVtbl {.pure.} = object of IUnknownVtbl Next*: proc (this: ptr IEnumPins; cPins: ULONG; ppPins: ptr ptr IPin; pcFetched: ptr ULONG): HRESULT {.stdcall.} Skip*: proc (this: ptr IEnumPins; cPins: ULONG): HRESULT {.stdcall.} Reset*: proc (this: ptr IEnumPins): HRESULT {.stdcall.} Clone*: proc (this: ptr IEnumPins; ppEnum: ptr ptr IEnumPins): HRESULT {. stdcall.}
IGraphBuilder {.pure.} = object lpVtbl*: ptr IGraphBuilderVtbl
IGraphBuilderVtbl {.pure.} = object of IUnknownVtbl AddFilter*: proc (this: ptr IGraphBuilder; pFilter: ptr IBaseFilter; pName: LPCWSTR): HRESULT {.stdcall.} RemoveFilter*: proc (this: ptr IGraphBuilder; pFilter: ptr IBaseFilter): HRESULT {. stdcall.} EnumFilters*: proc (this: ptr IGraphBuilder; ppEnum: ptr pointer): HRESULT {. stdcall.} FindFilterByName*: proc (this: ptr IGraphBuilder; pName: LPCWSTR; ppFilter: ptr ptr IBaseFilter): HRESULT {.stdcall.} ConnectDirect*: proc (this: ptr IGraphBuilder; ppinOut: pointer; ppinIn: pointer; pmt: ptr AM_MEDIA_TYPE): HRESULT {. stdcall.} Reconnect*: proc (this: ptr IGraphBuilder; ppin: pointer): HRESULT {.stdcall.} Disconnect*: proc (this: ptr IGraphBuilder; ppin: pointer): HRESULT {.stdcall.} SetDefaultSyncSource*: proc (this: ptr IGraphBuilder): HRESULT {.stdcall.} Connect*: proc (this: ptr IGraphBuilder; ppinOut: pointer; ppinIn: pointer): HRESULT {. stdcall.} Render*: proc (this: ptr IGraphBuilder; ppinOut: pointer): HRESULT {.stdcall.} RenderFile*: proc (this: ptr IGraphBuilder; lpcwstrFile: LPCWSTR; lpcwstrPlayList: LPCWSTR): HRESULT {.stdcall.} AddSourceFilter*: proc (this: ptr IGraphBuilder; lpcwstrFileName: LPCWSTR; lpcwstrFilterName: LPCWSTR; ppFilter: ptr ptr IBaseFilter): HRESULT {.stdcall.} SetLogFile*: proc (this: ptr IGraphBuilder; hFile: DWORD_PTR): HRESULT {. stdcall.} Abort*: proc (this: ptr IGraphBuilder): HRESULT {.stdcall.} ShouldOperationContinue*: proc (this: ptr IGraphBuilder): HRESULT {.stdcall.}
IMediaControl {.pure.} = object lpVtbl*: ptr IMediaControlVtbl
IMediaControlVtbl {.pure.} = object of IUnknownVtbl GetTypeInfoCount*: proc (this: ptr IMediaControl; pctinfo: ptr UINT): HRESULT {. stdcall.} GetTypeInfo*: proc (this: ptr IMediaControl; iTInfo: UINT; lcid: LCID; ppTInfo: ptr pointer): HRESULT {.stdcall.} GetIDsOfNames*: proc (this: ptr IMediaControl; riid: REFIID; rgszNames: ptr LPOLESTR; cNames: UINT; lcid: LCID; rgDispId: ptr DISPID): HRESULT {.stdcall.} Invoke*: proc (this: ptr IMediaControl; dispIdMember: DISPID; riid: REFIID; lcid: LCID; wFlags: WORD; pDispParams: pointer; pVarResult: pointer; pExcepInfo: pointer; puArgErr: ptr UINT): HRESULT {. stdcall.} Run*: proc (this: ptr IMediaControl): HRESULT {.stdcall.} Pause*: proc (this: ptr IMediaControl): HRESULT {.stdcall.} Stop*: proc (this: ptr IMediaControl): HRESULT {.stdcall.} GetState*: proc (this: ptr IMediaControl; msTimeout: LONG; pfs: ptr OAFilterState): HRESULT {.stdcall.} RenderFile*: proc (this: ptr IMediaControl; strFilename: BSTR): HRESULT {. stdcall.} AddSourceFilter*: proc (this: ptr IMediaControl; strFilename: BSTR; ppUnk: ptr ptr IDispatch): HRESULT {.stdcall.} get_FilterCollection*: proc (this: ptr IMediaControl; ppUnk: ptr ptr IDispatch): HRESULT {. stdcall.} get_RegFilterCollection*: proc (this: ptr IMediaControl; ppUnk: ptr ptr IDispatch): HRESULT {.stdcall.} StopWhenReady*: proc (this: ptr IMediaControl): HRESULT {.stdcall.}
IPinVtbl {.pure.} = object of IUnknownVtbl Connect*: proc (this: ptr IPin; pReceivePin: ptr IPin; pmt: ptr AM_MEDIA_TYPE): HRESULT {. stdcall.} ReceiveConnection*: proc (this: ptr IPin; pConnector: ptr IPin; pmt: ptr AM_MEDIA_TYPE): HRESULT {.stdcall.} Disconnect*: proc (this: ptr IPin): HRESULT {.stdcall.} ConnectedTo*: proc (this: ptr IPin; pPin: ptr ptr IPin): HRESULT {.stdcall.} ConnectionMediaType*: proc (this: ptr IPin; pmt: ptr AM_MEDIA_TYPE): HRESULT {. stdcall.} QueryPinInfo*: proc (this: ptr IPin; pInfo: pointer): HRESULT {.stdcall.} QueryDirection*: proc (this: ptr IPin; pPinDir: ptr int32): HRESULT {.stdcall.} QueryId*: proc (this: ptr IPin; Id: ptr LPWSTR): HRESULT {.stdcall.} QueryAccept*: proc (this: ptr IPin; pmt: ptr AM_MEDIA_TYPE): HRESULT {.stdcall.} EnumMediaTypes*: proc (this: ptr IPin; ppEnum: ptr pointer): HRESULT {.stdcall.} QueryInternalConnections*: proc (this: ptr IPin; apPin: ptr ptr IPin; nPin: ptr ULONG): HRESULT {.stdcall.} EndOfStream*: proc (this: ptr IPin): HRESULT {.stdcall.} BeginFlush*: proc (this: ptr IPin): HRESULT {.stdcall.} EndFlush*: proc (this: ptr IPin): HRESULT {.stdcall.} NewSegment*: proc (this: ptr IPin; tStart: REFERENCE_TIME; tStop: REFERENCE_TIME; dRate: float64): HRESULT {.stdcall.}
IPropertyBag {.pure.} = object lpVtbl*: ptr IPropertyBagVtbl
IPropertyBagVtbl {.pure.} = object QueryInterface*: proc (This: ptr IPropertyBag; riid: ptr IID; ppvObject: ptr pointer): HRESULT {.stdcall.} AddRef*: proc (This: ptr IPropertyBag): ULONG {.stdcall.} Release*: proc (This: ptr IPropertyBag): ULONG {.stdcall.} Read*: proc (This: ptr IPropertyBag; pszPropName: LPCOLESTR; pVar: ptr VARIANT; pErrorLog: pointer): HRESULT {.stdcall.} Write*: proc (This: ptr IPropertyBag; pszPropName: LPCOLESTR; pVar: ptr VARIANT): HRESULT {.stdcall.}
ISampleGrabber {.pure.} = object lpVtbl*: ptr ISampleGrabberVtbl
ISampleGrabberCB {.pure.} = object
ISampleGrabberVtbl {.pure.} = object of IUnknownVtbl SetOneShot*: proc (this: ptr ISampleGrabber; OneShot: BOOL): HRESULT {.stdcall.} SetMediaType*: proc (this: ptr ISampleGrabber; pType: ptr AM_MEDIA_TYPE): HRESULT {. stdcall.} GetConnectedMediaType*: proc (this: ptr ISampleGrabber; pType: ptr AM_MEDIA_TYPE): HRESULT {.stdcall.} SetBufferSamples*: proc (this: ptr ISampleGrabber; BufferThem: BOOL): HRESULT {. stdcall.} GetCurrentBuffer*: proc (this: ptr ISampleGrabber; pBufferSize: ptr LONG; pBuffer: ptr LONG): HRESULT {.stdcall.} GetCurrentSample*: proc (this: ptr ISampleGrabber; ppSample: ptr pointer): HRESULT {. stdcall.} SetCallback*: proc (this: ptr ISampleGrabber; pCallback: ptr ISampleGrabberCB; WhichMethodToCallback: LONG): HRESULT {.stdcall.}
OAFilterState = LONG
REFERENCE_TIME = LONGLONG
REFTIME = float64
VIDEOINFOHEADER {.pure.} = object rcSource*: RECT rcTarget*: RECT dwBitRate*: DWORD dwBitErrorRate*: DWORD AvgTimePerFrame*: REFERENCE_TIME bmiHeader*: BITMAPINFOHEADER
VideoProcAmpFlags {.pure.} = enum VideoProcAmp_Flags_Auto = 1, VideoProcAmp_Flags_Manual = 2
VideoProcAmpProperty {.pure.} = enum VideoProcAmp_Brightness = 0, VideoProcAmp_Contrast = 1, VideoProcAmp_Hue = 2, VideoProcAmp_Saturation = 3, VideoProcAmp_Sharpness = 4, VideoProcAmp_Gamma = 5, VideoProcAmp_ColorEnable = 6, VideoProcAmp_WhiteBalance = 7, VideoProcAmp_BacklightCompensation = 8, VideoProcAmp_Gain = 9
wDSCamera = ref object of wControl framePreprocessor*: wDSCameraFrameProc preprocessorUserdata*: pointer
-
DirectShow camera control for wNim.
Provides live webcam capture and display with format control, camera settings, and frame preprocessing support.
wDSCameraFrameProc = proc (frameBuffer: var seq[byte]; width, height: int32; format: wPixelFormat; userdata: pointer) {.closure.}
-
Frame preprocessor callback type.
Called for each captured frame before display, allowing modification of raw video data. The callback receives:
- frameBuffer: Raw video data (modifiable). Format depends on format parameter
- width, height: Frame dimensions in pixels
- format: Native pixel format (YUY2, RGB24, MJPG, etc.)
- userdata: Optional user-provided pointer passed to setFramePreprocessor()
Frame buffer layouts:
- RGB24: width * height * 3 bytes, format: B,G,R, B,G,R, ...
- YUY2: width * height * 2 bytes, format: Y0,U,Y1,V, Y2,U,Y3,V, ...
- MJPG: Decoded to RGB24 by DirectShow
Example:
proc myPreprocessor(frameBuffer: var seq[byte], width, height: int32, format: wPixelFormat, userdata: pointer) = case format of wPixelFormat.YUY2: # Brighten Y channel (luminance) for i in 0..<(width * height): let yIdx = (i div 2) * 4 + (if (i mod 2) == 0: 0 else: 2) frameBuffer[yIdx] = min(255, frameBuffer[yIdx] + 20) of wPixelFormat.RGB24: # Add blue tint for i in 0..<(width * height * 3) by 3: frameBuffer[i] = min(255, frameBuffer[i].int + 30).byte # Blue channel else: discard camera.setFramePreprocessor(myPreprocessor, nil)
wPixelFormat {.pure.} = enum RGB24, RGB32, YUY2, MJPG, Unknown
-
Pixel format identifier for the current camera output format.
Used to identify the native format of frame data in the preprocessor callback.
Formats:
- RGB24: 24-bit RGB, 3 bytes per pixel (B,G,R order)
- RGB32: 32-bit RGB with alpha, 4 bytes per pixel (B,G,R,A order)
- YUY2: 4:2:2 packed YUV, 4 bytes per 2 pixels (Y0,U,Y1,V order)
- MJPG: Motion JPEG (decoded to RGB24 by DirectShow)
- Unknown: Format not recognized
Example:
let format = camera.getCurrentPixelFormat() case format of wPixelFormat.YUY2: echo "Camera outputting native YUY2" of wPixelFormat.RGB24: echo "Camera outputting RGB24" else: echo "Other format"
wVideoFormat = object width*: int32 ## Frame width in pixels height*: int32 ## Frame height in pixels fps*: float ## Frames per second formatName*: string ## Format name: "RGB24", "RGB32", "YUY2", "MJPG", etc. compressed*: bool ## True if format is compressed (MJPG, H264, etc.) index*: int32 ## Internal index for setting format (used by DirectShow)
-
Video format descriptor containing resolution, framerate, and format information.
Returned by enumerateFormats() and used with setFormat().
Example:
let formats = camera.enumerateFormats() for fmt in formats: echo fmt.width, "x", fmt.height, " @ ", fmt.fps, "fps" echo " Format: ", fmt.formatName, " Compressed: ", fmt.compressed
Vars
CLSID_CaptureGraphBuilder2 = GUID(Data1: 0xBF87B6E1'i32, Data2: 0x00008C27, Data3: 0x000011D0, Data4: [0xB3'u8, 0x000000F0, 0x00000000, 0x000000AA, 0x00000000, 0x00000037, 0x00000061, 0x000000C5])
CLSID_FilterGraph = GUID(Data1: 0xE436EBB3'i32, Data2: 0x0000524F, Data3: 0x000011CE, Data4: [0x9F'u8, 0x00000053, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
CLSID_NullRenderer = GUID(Data1: 0xC1F400A4'i32, Data2: 0x00003F08, Data3: 0x000011D3, Data4: [0x9F'u8, 0x0000000B, 0x00000000, 0x00000060, 0x00000008, 0x00000003, 0x0000009E, 0x00000037])
CLSID_SampleGrabber = GUID(Data1: 0xC1F400A0'i32, Data2: 0x00003F08, Data3: 0x000011D3, Data4: [0x9F'u8, 0x0000000B, 0x00000000, 0x00000060, 0x00000008, 0x00000003, 0x0000009E, 0x00000037])
CLSID_SystemDeviceEnum = GUID(Data1: 0x62BE5D10'i32, Data2: 0x000060EB, Data3: 0x000011D0, Data4: [0xBD'u8, 0x0000003B, 0x00000000, 0x000000A0, 0x000000C9, 0x00000011, 0x000000CE, 0x00000086])
CLSID_VideoInputDeviceCategory = GUID(Data1: 0x860BB310'i32, Data2: 0x00005D01, Data3: 0x000011D0, Data4: [0xBD'u8, 0x0000003B, 0x00000000, 0x000000A0, 0x000000C9, 0x00000011, 0x000000CE, 0x00000086])
FORMAT_VideoInfo = GUID(Data1: 0x05589F80'i32, Data2: 0x0000C356, Data3: 0x000011CE, Data4: [0xBF'u8, 0x00000001, 0x00000000, 0x000000AA, 0x00000000, 0x00000055, 0x00000059, 0x0000005A])
IID_IAMCameraControl = GUID(Data1: 0xC6E13370'i32, Data2: 0x000030AC, Data3: 0x000011D0, Data4: [0xA1'u8, 0x0000008C, 0x00000000, 0x000000A0, 0x000000C9, 0x00000011, 0x00000089, 0x00000056])
IID_IAMStreamConfig = GUID(Data1: 0xC6E13340'i32, Data2: 0x000030AC, Data3: 0x000011D0, Data4: [0xA1'u8, 0x0000008C, 0x00000000, 0x000000A0, 0x000000C9, 0x00000011, 0x00000089, 0x00000056])
IID_IAMVideoProcAmp = GUID(Data1: 0xC6E13360'i32, Data2: 0x000030AC, Data3: 0x000011D0, Data4: [0xA1'u8, 0x0000008C, 0x00000000, 0x000000A0, 0x000000C9, 0x00000011, 0x00000089, 0x00000056])
IID_IBaseFilter = GUID(Data1: 0x56A86895'i32, Data2: 0x00000AD4, Data3: 0x000011CE, Data4: [0xB0'u8, 0x0000003A, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
IID_ICaptureGraphBuilder2 = GUID(Data1: 0x93E5A4E0'i32, Data2: 0x00002D50, Data3: 0x000011D2, Data4: [0xAB'u8, 0x000000FA, 0x00000000, 0x000000A0, 0x000000C9, 0x000000C6, 0x000000E3, 0x0000008D])
IID_ICreateDevEnum = GUID(Data1: 0x29840822'i32, Data2: 0x00005B84, Data3: 0x000011D0, Data4: [0xBD'u8, 0x0000003B, 0x00000000, 0x000000A0, 0x000000C9, 0x00000011, 0x000000CE, 0x00000086])
IID_IGraphBuilder = GUID(Data1: 0x56A868A9'i32, Data2: 0x00000AD4, Data3: 0x000011CE, Data4: [0xB0'u8, 0x0000003A, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
IID_IMediaControl = GUID(Data1: 0x56A868B1'i32, Data2: 0x00000AD4, Data3: 0x000011CE, Data4: [0xB0'u8, 0x0000003A, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
IID_IPin = GUID(Data1: 0x56A86891'i32, Data2: 0x00000AD4, Data3: 0x000011CE, Data4: [ 0xB0'u8, 0x0000003A, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
IID_IPropertyBag = GUID(Data1: 0x55272A00'i32, Data2: 0x000042CB, Data3: 0x000011CE, Data4: [0x81'u8, 0x00000035, 0x00000000, 0x000000AA, 0x00000000, 0x0000004B, 0x000000B8, 0x00000051])
IID_ISampleGrabber = GUID(Data1: 0x6B652FFF'i32, Data2: 0x000011FE, Data3: 0x00004FCE, Data4: [0x92'u8, 0x000000AD, 0x00000002, 0x00000066, 0x000000B5, 0x000000D7, 0x000000C7, 0x0000008F])
MEDIASUBTYPE_MJPG = GUID(Data1: 0x47504A4D'i32, Data2: 0x00000000, Data3: 0x00000010, Data4: [0x80'u8, 0x00000000, 0x00000000, 0x000000AA, 0x00000000, 0x00000038, 0x0000009B, 0x00000071])
MEDIASUBTYPE_RGB24 = GUID(Data1: 0xE436EB7D'i32, Data2: 0x0000524F, Data3: 0x000011CE, Data4: [0x9F'u8, 0x00000053, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
MEDIASUBTYPE_RGB32 = GUID(Data1: 0xE436EB7E'i32, Data2: 0x0000524F, Data3: 0x000011CE, Data4: [0x9F'u8, 0x00000053, 0x00000000, 0x00000020, 0x000000AF, 0x0000000B, 0x000000A7, 0x00000070])
MEDIASUBTYPE_YUY2 = GUID(Data1: 0x32595559'i32, Data2: 0x00000000, Data3: 0x00000010, Data4: [0x80'u8, 0x00000000, 0x00000000, 0x000000AA, 0x00000000, 0x00000038, 0x0000009B, 0x00000071])
MEDIATYPE_Video = GUID(Data1: 0x73646976'i32, Data2: 0x00000000, Data3: 0x00000010, Data4: [0x80'u8, 0x00000000, 0x00000000, 0x000000AA, 0x00000000, 0x00000038, 0x0000009B, 0x00000071])
Consts
wDSCameraFit = 0x00020000
wDSCameraFlipVertical = 0x00010000
wDSCameraStretch = 0x00040000
wEvent_DSCameraConnected = 33961'i32
wEvent_DSCameraDisconnected = 33962'i32
wEvent_DSCameraError = 33963'i32
wEvent_DSCameraFirst = 33960'i32
wEvent_DSCameraFrame = 33964'i32
wEvent_DSCameraLast = 33964'i32
Procs
proc DSCamera(parent: wWindow; id = wDefaultID; cameraIndex: int = 0; pos = wDefaultPoint; size = wDefaultSize; style: wStyle = 0): wDSCamera {. inline, discardable, ...raises: [wWindowError, wCursorError, wBrushError, Exception, wFontError], tags: [RootEffect, TimeEffect], forbids: [].}
- Constructor.
proc enumerateCameras(): seq[tuple[index: int, name: string]] {. ...raises: [Exception], tags: [RootEffect], forbids: [].}
- Enumerate all DirectShow video capture devices Returns a sequence of (index, name) tuples
proc enumerateFormats(self: wDSCamera): seq[wVideoFormat] {....raises: [Exception], tags: [RootEffect], forbids: [].}
-
Enumerate all supported video formats for the current camera.
Returns a sequence of wVideoFormat objects describing available resolutions, frame rates, and pixel formats (RGB24, RGB32, YUY2, MJPG, etc.).
The camera will be automatically paused if running, then resumed after enumeration. This is safe to call while the camera is active.
Returns: A sequence of available video formats. Empty sequence if enumeration fails.
Example:
let formats = camera.enumerateFormats() for i, fmt in formats: echo "[", i, "] ", fmt.width, "x", fmt.height, " @ ", fmt.fps, "fps" echo " Format: ", fmt.formatName echo " Compressed: ", fmt.compressed # Use with setFormat to switch formats if formats.len > 0: discard camera.setFormat(formats[0])
proc getAvailableCameras(self: wDSCamera): seq[tuple[index: int, name: string]] {. ...raises: [Exception], tags: [RootEffect], forbids: [].}
-
Get list of all available DirectShow video capture devices.
Returns: Sequence of tuples with camera index and friendly name
Example:
let cameras = camera.getAvailableCameras() for cam in cameras: echo "[", cam.index, "] ", cam.name
proc getCameraControl(self: wDSCamera; property: CameraControlProperty): tuple[ value: int, isAuto: bool] {....raises: [Exception], tags: [RootEffect], forbids: [].}
-
Get the current value and mode of a CameraControl property.
Args: property: CameraControl property to query
Returns: Tuple with:
- value: Current property value
- isAuto: Whether automatic adjustment is currently enabled
Returns (0, false) if property is not supported.
Example:
let exposure = camera.getCameraControl(CameraControl_Exposure) if exposure.isAuto: echo "Exposure is on AUTO, value: ", exposure.value else: echo "Exposure is MANUAL, value: ", exposure.value
proc getCameraControlRange(self: wDSCamera; property: CameraControlProperty): tuple[ min, max, step, default: int, supportsAuto: bool] {....raises: [Exception], tags: [RootEffect], forbids: [].}
-
Get the range and capabilities for a CameraControl property.
CameraControl properties include exposure, focus, zoom, pan, tilt, roll, and iris. These control physical camera mechanisms and positioning.
Args: property: CameraControl property to query (e.g., CameraControl_Exposure)
Returns: Tuple with:
- min: Minimum value
- max: Maximum value
- step: Step size for value changes
- default: Default/recommended value
- supportsAuto: Whether automatic adjustment is available
Returns (0, 0, 0, 0, false) if property is not supported.
Example:
let expRange = camera.getCameraControlRange(CameraControl_Exposure) if expRange.max > expRange.min: echo "Exposure range: ", expRange.min, " to ", expRange.max echo "Default: ", expRange.default # Set to darker exposure discard camera.setCameraControl(CameraControl_Exposure, expRange.min + 10)
proc getCameraIndex(self: wDSCamera): int {....raises: [], tags: [], forbids: [].}
- Get the current camera index (0-based).
proc getCameraName(cameraIndex: int): string {....raises: [Exception], tags: [RootEffect], forbids: [].}
- Get the friendly name of a camera by index
proc getCurrentCameraName(self: wDSCamera): string {....raises: [Exception], tags: [RootEffect], forbids: [].}
- Get the friendly name of the currently selected camera.
proc getCurrentFormat(self: wDSCamera): wVideoFormat {....raises: [Exception], tags: [RootEffect], forbids: [].}
- Get the current video format Returns a wVideoFormat with current settings, or default values if not available
proc getCurrentPixelFormat(self: wDSCamera): wPixelFormat {....raises: [], tags: [], forbids: [].}
-
Get the current pixel format of the frame buffer.
This indicates the native format of data in the frame buffer:
- RGB24: 24-bit RGB (3 bytes/pixel: B,G,R)
- RGB32: 32-bit RGBA (4 bytes/pixel: B,G,R,A)
- YUY2: 4:2:2 YUV (4 bytes/2 pixels: Y0,U,Y1,V)
- MJPG: Motion JPEG (decoded to RGB24)
- Unknown: Format not recognized
Returns: Current pixel format enum value
Example:
let format = camera.getCurrentPixelFormat() case format of wPixelFormat.YUY2: echo "Native YUY2 - use Y channel for grayscale" of wPixelFormat.RGB24: echo "RGB24 - 3 bytes per pixel" else: echo "Other format: ", format
proc getDrawInfo(self: wDSCamera): tuple[ drawX, drawY, drawWidth, drawHeight: int32, scale: float] {....raises: [], tags: [], forbids: [].}
- Get information about how the image is drawn in the control Useful for coordinate transformations and overlay drawing
proc getFlipVertical(self: wDSCamera): bool {....raises: [], tags: [], forbids: [].}
- Get whether the camera image is flipped vertically
proc getFrameBuffer(self: wDSCamera): seq[byte] {....raises: [], tags: [], forbids: [].}
-
Get a copy of the current frame buffer in native format.
The format of the data depends on the current camera configuration. Use getCurrentPixelFormat() to determine the format.
Frame buffer layouts:
- RGB24: width * height * 3 bytes (B,G,R triplets)
- YUY2: width * height * 2 bytes (Y0,U,Y1,V quads for 2 pixels)
- MJPG: Decoded to RGB24 by DirectShow
Returns: Copy of frame buffer. Empty sequence if no frame is ready.
Example:
let buffer = camera.getFrameBuffer() if buffer.len > 0: let format = camera.getCurrentPixelFormat() echo "Got ", buffer.len, " bytes in ", format, " format"
proc getFrameBufferPtr(self: wDSCamera): ptr seq[byte] {....raises: [], tags: [], forbids: [].}
-
Get a pointer to the frame buffer for zero-copy processing.Warning: Do not modify the buffer through this pointer, and do not store the pointer beyond the scope of your event handler. Use getFrameBuffer() if you need to modify or store the data.
Frame data is in native format - check getCurrentPixelFormat() to determine layout.
Returns: Pointer to frame buffer, or nil if no frame is ready
proc getFrameSize(self: wDSCamera): tuple[width, height: int32] {....raises: [], tags: [], forbids: [].}
-
Get the current frame dimensions in pixels.
Returns: A tuple of (width, height) in pixels
Example:
let size = camera.getFrameSize() echo "Camera resolution: ", size.width, "x", size.height
proc getVideoProcAmp(self: wDSCamera; property: VideoProcAmpProperty): tuple[ value: int, isAuto: bool] {....raises: [Exception], tags: [RootEffect], forbids: [].}
-
Get the current value and mode of a VideoProcAmp property.
Args: property: VideoProcAmp property to query
Returns: Tuple with:
- value: Current property value
- isAuto: Whether automatic adjustment is currently enabled
Returns (0, false) if property is not supported.
Example:
let current = camera.getVideoProcAmp(VideoProcAmp_Brightness) if current.isAuto: echo "Brightness is on AUTO, value: ", current.value else: echo "Brightness is MANUAL, value: ", current.value
proc getVideoProcAmpRange(self: wDSCamera; property: VideoProcAmpProperty): tuple[ min, max, step, default: int, supportsAuto: bool] {....raises: [Exception], tags: [RootEffect], forbids: [].}
-
Get the range and capabilities for a VideoProcAmp property.
VideoProcAmp controls include brightness, contrast, hue, saturation, sharpness, gamma, white balance, backlight compensation, and gain.
Args: property: VideoProcAmp property to query (e.g., VideoProcAmp_Brightness)
Returns: Tuple with:
- min: Minimum value
- max: Maximum value
- step: Step size for value changes
- default: Default/recommended value
- supportsAuto: Whether automatic adjustment is available
Returns (0, 0, 0, 0, false) if property is not supported.
Example:
let range = camera.getVideoProcAmpRange(VideoProcAmp_Brightness) if range.max > range.min: echo "Brightness range: ", range.min, " to ", range.max echo "Default value: ", range.default echo "Auto support: ", range.supportsAuto # Set to midpoint let mid = (range.min + range.max) div 2 discard camera.setVideoProcAmp(VideoProcAmp_Brightness, mid)
proc imageToWindowCoords(self: wDSCamera; imageX, imageY: float): tuple[ windowX, windowY: int32, valid: bool] {....raises: [], tags: [], forbids: [].}
- Convert image coordinates to window coordinates Takes into account the fit mode, scaling, and vertical flip Returns (windowX, windowY, valid). valid=false if outside visible area
proc init(self: wDSCamera; parent: wWindow; id = wDefaultID; cameraIndex: int = 0; pos = wDefaultPoint; size = wDefaultSize; style: wStyle = 0) {....raises: [wWindowError, wCursorError, wBrushError, Exception, wFontError], tags: [RootEffect, TimeEffect], forbids: [].}
- Initializes a DirectShow camera control
proc setCameraControl(self: wDSCamera; property: CameraControlProperty; value: int; auto: bool = false): bool {.discardable, ...raises: [Exception], tags: [RootEffect], forbids: [].}
-
Set a CameraControl property value.
CameraControl properties adjust physical camera mechanisms:
- CameraControl_Pan: Horizontal camera rotation
- CameraControl_Tilt: Vertical camera rotation
- CameraControl_Roll: Rotation around view axis
- CameraControl_Zoom: Optical or digital zoom
- CameraControl_Exposure: Exposure time
- CameraControl_Iris: Iris aperture opening
- CameraControl_Focus: Focus distance
Args: property: CameraControl property to set value: New value (use getCameraControlRange() to get valid range) auto: Enable automatic adjustment (if supported by camera)
Returns: true if successfully set, false on error
Example:
# Manual focus adjustment discard camera.setCameraControl(CameraControl_Focus, 100, auto=false) # Enable auto exposure discard camera.setCameraControl(CameraControl_Exposure, 0, auto=true) # Pan camera discard camera.setCameraControl(CameraControl_Pan, 500)
proc setCameraIndex(self: wDSCamera; index: int): bool {.discardable, ...raises: [Exception], tags: [RootEffect], forbids: [].}
- Change to a different camera by index
proc setFlipVertical(self: wDSCamera; flip: bool) {....raises: [], tags: [], forbids: [].}
- Set whether to flip the camera image vertically
proc setFormat(self: wDSCamera; format: wVideoFormat): bool {.discardable, ...raises: [Exception], tags: [RootEffect], forbids: [].}
-
Set the video format using a wVideoFormat object from enumerateFormats().
Automatically stops and restarts the camera as needed. The camera will output in the native format (YUY2, MJPG, RGB24, etc.) after the change.
For MJPG formats, DirectShow will automatically decode to RGB24 for the Sample Grabber. For uncompressed formats (YUY2, RGB24, RGB32), the native format is preserved.
Args: format: Video format obtained from enumerateFormats()
Returns: true if format was set successfully, false on error
Example:
# Get available formats let formats = camera.enumerateFormats() # Find 1280x720 format for fmt in formats: if fmt.width == 1280 and fmt.height == 720: if camera.setFormat(fmt): echo "Switched to 720p" break
proc setFormatByResolution(self: wDSCamera; width, height: int32; preferUncompressed: bool = true): bool {.discardable, ...raises: [Exception], tags: [RootEffect], forbids: [].}
-
Set video format by desired resolution, automatically choosing the best matching format.
Scores available formats by resolution match, frame rate, and compression preferences. Higher frame rates are preferred, and compression preference is determined by preferUncompressed parameter.
Automatically stops and restarts the camera as needed.
Args: width: Desired frame width in pixels height: Desired frame height in pixels preferUncompressed: If true, prioritizes RGB24/RGB32/YUY2 over MJPG. If false, prefers MJPG.
Returns: true if a matching format was found and set successfully, false otherwise
Example:
# Set to 1280x720, prefer uncompressed if camera.setFormatByResolution(1280, 720, preferUncompressed=true): echo "Set to 720p uncompressed (YUY2 or RGB24)" # Set to 1920x1080, prefer compressed for bandwidth if camera.setFormatByResolution(1920, 1080, preferUncompressed=false): echo "Set to 1080p MJPG"
proc setFramePreprocessor(self: wDSCamera; callback: wDSCameraFrameProc; userdata: pointer = nil) {....raises: [], tags: [], forbids: [].}
-
Set a frame preprocessing callback that will be called before each frame is displayed.
The callback receives:
- frameBuffer: Mutable raw video data in native format
- width, height: Frame dimensions
- format: Native pixel format (YUY2, RGB24, MJPG, etc.)
- userdata: Optional pointer passed to this function
The callback can modify the frame buffer in-place. Changes will be visible in the display. This is useful for highlighting regions, applying filters, overlaying graphics, etc.
Args: callback: Preprocessor function (see wDSCameraFrameProc) userdata: Optional pointer passed to callback (default: nil)
Example:
proc brighten(frameBuffer: var seq[byte], width, height: int32, format: wPixelFormat, userdata: pointer) = case format of wPixelFormat.RGB24: # Brighten each pixel for i in 0..<(width * height * 3): frameBuffer[i] = min(255, frameBuffer[i].int + 20).byte else: discard camera.setFramePreprocessor(brighten, nil)
proc setVideoProcAmp(self: wDSCamera; property: VideoProcAmpProperty; value: int; auto: bool = false): bool {.discardable, ...raises: [Exception], tags: [RootEffect], forbids: [].}
-
Set a VideoProcAmp property value.
VideoProcAmp controls adjust image processing parameters:
- VideoProcAmp_Brightness: Image brightness
- VideoProcAmp_Contrast: Contrast level
- VideoProcAmp_Hue: Color hue
- VideoProcAmp_Saturation: Color saturation
- VideoProcAmp_Sharpness: Image sharpness
- VideoProcAmp_Gamma: Gamma correction
- VideoProcAmp_WhiteBalance: White balance
- VideoProcAmp_BacklightCompensation: Backlight compensation
- VideoProcAmp_Gain: Gain control
Args: property: VideoProcAmp property to set value: New value (use getVideoProcAmpRange() to get valid range) auto: Enable automatic adjustment (if supported by camera)
Returns: true if successfully set, false on error
Example:
# Manual brightness adjustment discard camera.setVideoProcAmp(VideoProcAmp_Brightness, 50, auto=false) # Enable auto white balance discard camera.setVideoProcAmp(VideoProcAmp_WhiteBalance, 0, auto=true) # Reset to default let range = camera.getVideoProcAmpRange(VideoProcAmp_Contrast) discard camera.setVideoProcAmp(VideoProcAmp_Contrast, range.default)
proc windowToImageCoords(self: wDSCamera; windowX, windowY: int32): tuple[ imageX, imageY: float, valid: bool] {....raises: [], tags: [], forbids: [].}
- Convert window coordinates to image coordinates Takes into account the fit mode, scaling, and vertical flip Returns (imageX, imageY, valid). valid=false if click is outside image area