wDSCamera

Search:
Group by:

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

StyleDescription
wDSCameraFlipVerticalFlip the camera image vertically
wDSCameraFitScale camera image to fit control (maintains aspect ratio)
wDSCameraStretchStretch camera image to fill control

Events

wCommandEvent

EventDescription
wEvent_DSCameraConnectedCamera successfully connected and started
wEvent_DSCameraDisconnectedCamera disconnected
wEvent_DSCameraErrorError occurred during capture
wEvent_DSCameraFrameNew frame captured (fires at camera framerate)

See Also

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.}
IPin {.pure.} = object
  lpVtbl*: ptr IPinVtbl
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])

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 isRunning(self: wDSCamera): bool {....raises: [], tags: [], forbids: [].}

Check if camera is currently capturing.

Returns: true if camera is actively capturing frames, false otherwise

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 start(self: wDSCamera): bool {.discardable, ...raises: [Exception],
                                    tags: [RootEffect], forbids: [].}
Start or restart camera capture Uses the currently configured format (from setFormat or default RGB24)
proc stop(self: wDSCamera) {....raises: [Exception], tags: [RootEffect],
                             forbids: [].}
Stop camera capture and release DirectShow graph (except source filter for format changes)
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

Methods

method release(self: wDSCamera) {....raises: [Exception], tags: [RootEffect],
                                  forbids: [].}
Release all resources during destruction