rubycam
Pure-Ruby V4L2 webcam library plus a GTK4 viewer, built against an OBSBOT
Tiny 2. No C extension and no GStreamer: controls go through ioctl and
frames come from memory-mapped kernel buffers via Fiddle.
The OBSBOT is a standard UVC device, so the kernel's uvcvideo driver
already handles it — this library talks V4L2 and works with any UVC webcam.
Install
Two gems ship from this repo:
| Gem | Provides | Depends on |
|---|---|---|
rubycam |
the V4L2/OBSBOT library and the rubycam CLI |
dry-cli |
rubycam-gtk |
the GTK4 viewer and the rubycam-gtk app |
rubycam, gtk4 |
gem install rubycam # library + CLI (pure Ruby, light install)
gem install rubycam-gtk # adds the GTK4 viewer (pulls in the GTK stack)
The rubycam gem has no dependencies beyond dry-cli; the GTK stack only
comes in with rubycam-gtk.
Library
require 'rubycam'
Rubycam::Device.open('/dev/video0') do |cam|
cam.controls.each_value { |c| puts c } # discover controls
cam[:zoom_absolute] = 50 # get/set by symbol
cam[:pan_absolute] = 20 * 3600 # gimbal units: 1/3600 degree
cam.set_format(width: 1920, height: 1080, pixel_format: 'MJPG')
cam.set_fps(30)
File.binwrite('frame.jpg', cam.capture_frame) # blocking
cam.poll_frame # non-blocking, nil if not ready
end
OBSBOT vendor commands go through the camera's UVC extension unit — the
same channel the official OBSBOT software uses (protocol from the
Tiny4Linux project; see
TINY4LINUX_FEATURES.md for the full feature map):
cam = Rubycam::Device.find('OBSBOT Tiny 2') # path, /dev name or card/bus hint
bot = Rubycam::Obsbot.new(cam)
bot.status # => { asleep: false, hdr: true, ai_mode: :no_tracking,
# tracking_speed: :standard }
bot.sleep! # privacy sleep (gimbal folds down)
bot.wake! # wakes even after the camera was folded down by hand
bot.ai_mode = :normal_tracking # :no_tracking :upper_body :close_up :headless
# :lower_body :desk_mode :whiteboard :hand :group
bot.tracking_speed = :sport # or :standard
bot.goto_preset(0) # stored gimbal positions 0..2
bot.hdr = true
bot.exposure_mode = :face # :manual, :global or :face
bot.debug = true # log raw traffic to stderr
bot.send_hex('16 02 00 00') # raw command to the extension unit
bot.dump # current status block as hex
Notes:
- The camera delivers 4K@30 / 1080p@60 in MJPG; frames are JPEG strings.
- Folding the camera down by hand is privacy sleep; V4L2 keeps working but
video stops.
Obsbot#wake!is the only software way back. - Writing
tilt_absoluteto its minimum does NOT trigger privacy sleep — that gesture is hardware-only. - The first frame after STREAMON takes a few seconds (ISP startup).
- On newer Tiny 2 firmware the status block lags mode changes by seconds and reports tracking speed at byte 0x24 instead of 0x21 (both handled).
Viewer app
With rubycam-gtk installed the viewer is on your PATH:
rubycam-gtk # first camera at /dev/video0
rubycam-gtk /dev/video2 # explicit device
rubycam-gtk 'OBSBOT Tiny 2' # find by name
rubycam-gtk --obsbot # full OBSBOT viewer (default)
rubycam-gtk --v4l2 # generic V4L2 viewer, no OBSBOT features
From a checkout, use the dev shell (it provides the native GTK libs):
nix develop # or let direnv do it (.envrc: use flake)
bundle install # or bin/setup
rubycam-gtk # exe/ is on PATH via direnv
Live preview, sliders for gimbal/zoom/image controls, and an OBSBOT panel with power switch, live status, AI tracking modes, tracking speed, preset positions, HDR, exposure modes and a raw-hex debug console. The ⤢ button toggles a compact widget mode (panel only); if the camera disappears the app keeps polling and reconnects when it returns.
--v4l2 launches the same live preview and control sliders without any of
the OBSBOT panel or vendor commands, so it works with any UVC webcam.
CLI
Everything the OBSBOT panel does, scriptable — shipped by the rubycam
gem (needs only Ruby + the dry-cli gem, no GTK):
rubycam # list commands
rubycam status # sleep state, AI mode, speed, HDR
rubycam wake # wake from privacy sleep
rubycam track upper_body # AI tracking mode
rubycam speed sport
rubycam preset 2 # gimbal preset 1-3
rubycam hdr off
rubycam exposure face
rubycam devices # every /dev/video* node
rubycam controls # V4L2 controls with ranges
rubycam set zoom_absolute 50
rubycam reset # all controls back to defaults
rubycam snapshot shot.jpg --width=3840 --height=2160
rubycam xu dump # debug: status block as hex
rubycam xu send '16 02 02 00' --selector=0x06
Generic V4L2 commands default to /dev/video0; OBSBOT commands find the
camera by name. Both take -d to target a path, /dev name or card/bus
substring.
Examples
ruby examples/snapshot.rb # capture snapshot.jpg
ruby examples/controls.rb # list all controls
ruby examples/controls.rb zoom_absolute 50
The library itself has no dependencies beyond the Ruby stdlib; only the
GTK viewer needs the dev shell and bundle install.