Documentation
¶
Overview ¶
Package screencapture is a pure-Go, CGO-free wrapper over Apple's ScreenCaptureKit. It enumerates the displays and windows a process may capture, and streams a display (or a single window) as raw BGRA pixels.
ScreenCaptureKit is the ONLY capture route left on current macOS: the legacy CGDisplayStream path is deprecated and, on macOS 26, no longer produces frames. Everything here goes through SCStream.
The hot path ¶
The package is written for a compositor that redraws every frame and cannot afford a copy or an allocation per frame. Stream.Frame hands back a BORROWED view of the most recent captured frame — the bytes are the IOSurface the window server itself rendered into, not a copy — together with a boolean saying whether it is newer than the one the previous call returned. In steady state a Frame call performs no allocation at all.
The borrow is valid until the next call to Stream.Frame, Stream.WaitFrame or Stream.Close. Copy out of it (see Frame.CopyTight or Frame.NRGBA) if you need to keep it longer.
Stride ¶
A captured frame's rows are PADDED. Stride is the number of bytes per row and it is NOT Width*4 — the window server aligns rows (a 400-pixel-wide capture was measured at stride 1664, not 1600). Always index with Stride, or use Frame.Row. This is the single most common way to get a sheared image.
Frames only arrive when something changes ¶
ScreenCaptureKit is change-driven. FPS is a CEILING, not a rate: a stream on a motionless surface delivers one frame and then nothing until a pixel moves (a static wallpaper was measured at 1 frame in 3.1 s). Do not treat a missing frame as a failure; treat the "fresh" flag from Stream.Frame as the truth about whether anything changed.
Permission ¶
Capturing anything that belongs to another process needs the Screen Recording TCC grant. See Authorized, RequestAuthorization and ErrPermissionDenied. Capturing content owned by the CALLING process ([CurrentProcessContent]) needs no grant at all, which is what makes this package testable on a machine where the grant is missing.
Index ¶
Examples ¶
Constants ¶
const ( // DefaultFPS is the frame-rate ceiling used when Options.FPS is zero. DefaultFPS = 60.0 // DefaultQueueDepth is the in-flight frame count used when // Options.QueueDepth is zero. Three is the documented minimum that keeps a // consumer holding one frame from starving the stream. DefaultQueueDepth = 6 // MinQueueDepth is the smallest queue depth this package accepts. MinQueueDepth = 3 // MaxDimension is the largest frame edge accepted, a sanity bound well // above any real display; it exists so a mistaken value fails loudly // instead of asking the window server for a terabyte. MaxDimension = 32768 )
Defaults applied to the zero value of the corresponding Options field.
Variables ¶
var ( // ErrUnsupported is reported on every non-darwin platform, and on a macOS // too old to carry ScreenCaptureKit (before 12.3). ErrUnsupported = errors.New("screencapture: unsupported on this platform (macOS 12.3 or later only)") // ErrPermissionDenied is reported when the Screen Recording TCC grant is // missing. Its message names the exact remedy; see also [Authorized]. ErrPermissionDenied = errors.New("screencapture: Screen Recording permission denied — " + "grant it in System Settings > Privacy & Security > Screen & System Audio Recording " + "to the application that launched this program (for a program started from a shell " + "that is the terminal or editor, not the program itself), then restart that application") // ErrNoDisplay is reported when a capture was asked for and the system // listed no display at all. ErrNoDisplay = errors.New("screencapture: no capturable display") // ErrNotFound is reported when a display or window ID does not name // anything currently capturable. ErrNotFound = errors.New("screencapture: no such display or window") // ErrClosed is reported by every [Stream] method after [Stream.Close]. ErrClosed = errors.New("screencapture: stream is closed") // ErrNoFrame is reported by [Stream.WaitFrame] when no frame arrived // before its context expired. It is NOT a malfunction: a motionless // surface legitimately produces no frames. ErrNoFrame = errors.New("screencapture: no frame available") // ErrInvalidOption is reported by [Options.Validate] and wraps a // description of the offending field. ErrInvalidOption = errors.New("screencapture: invalid option") // ErrShortBuffer is reported by [Frame.CopyTight] when the destination is // too small to hold the frame. ErrShortBuffer = errors.New("screencapture: destination buffer too short") )
Sentinel errors. All are stable and may be matched with errors.Is.
Functions ¶
func Authorized ¶
func Authorized() bool
Authorized reports false: there is no Screen Recording grant to hold.
func Available ¶
func Available() bool
Available reports false: ScreenCaptureKit exists only on macOS.
func RequestAuthorization ¶
func RequestAuthorization() bool
RequestAuthorization reports false and prompts nothing.
Types ¶
type Application ¶
Application is a process owning capturable windows.
type Content ¶
type Content struct {
Displays []Display
Windows []Window
Applications []Application
}
Content is a snapshot of what the calling process may capture. It is a snapshot: windows open and close, so re-read it rather than caching it.
func CurrentProcessShareable ¶
CurrentProcessShareable reports ErrUnsupported.
func Shareable ¶
Shareable reports ErrUnsupported.
func (*Content) MainDisplay ¶
MainDisplay returns the display carrying the menu bar, or the first one if none is flagged as main.
func (*Content) WindowsByTitle ¶
WindowsByTitle returns every window whose title is exactly title.
func (*Content) WindowsOfPID ¶
WindowsOfPID returns every window owned by the given process.
type Display ¶
type Display struct {
ID uint32 // CGDirectDisplayID
Width int // points
Height int // points
PixelWidth int // native pixels
PixelHeight int // native pixels
Frame Rect // global desktop position, points
Main bool // this is the display carrying the menu bar
}
Display is a capturable display.
Width and Height are in POINTS, as ScreenCaptureKit reports them. PixelWidth and PixelHeight are the display's native backing store in PIXELS, read from CoreGraphics — on a Retina display they are the larger pair, and they are what you want to hand to Options for a capture with no resampling.
func Displays ¶
Displays reports ErrUnsupported.
type Frame ¶
type Frame struct {
// Pix is the frame's bytes in [FormatBGRA], Stride bytes per row,
// Height rows. len(Pix) == Stride*Height.
Pix []byte
// Width and Height are the frame's size in pixels.
Width, Height int
// Stride is the number of BYTES per row. It is padded and is NOT
// necessarily Width*4.
Stride int
// Seq counts frames since the stream started; it is 0 before the first
// frame and strictly increases afterwards.
Seq uint64
// At is when the delivery callback received the frame.
At time.Time
}
Frame is a BORROWED view of one captured frame.
Pix aliases memory owned by the window server. It stays valid only until the next Stream.Frame, Stream.WaitFrame or Stream.Close on the stream that produced it. Do not retain it; copy with Frame.CopyTight or Frame.NRGBA if you need it to outlive the borrow.
func (Frame) CopyTight ¶
CopyTight copies the frame into dst with the row padding removed, so dst holds Width*4*Height bytes of contiguous BGRA. It reports how many bytes it wrote, or ErrShortBuffer if dst is too small. It allocates nothing.
func (Frame) NRGBA ¶
NRGBA copies the frame into a freshly allocated image.NRGBA, swapping BGRA to RGBA as it goes. It is the convenience path for saving a frame to disk; it allocates, so it does not belong in a per-frame loop.
func (Frame) Row ¶
Row returns row y of the frame, Width*4 bytes with the padding trimmed off. It does not allocate. It returns nil for an out-of-range y or an invalid frame.
Example ¶
f := makeFrame(2, 2, 8) fmt.Println(f.Stride, len(f.Row(0)))
Output: 16 8
type Options ¶
type Options struct {
// Width and Height are the requested frame size in PIXELS. Zero means
// "the source's native pixel size", which for a display is its backing
// store and for a window is its frame scaled by the display's scale.
Width, Height int
// FPS is the CEILING on the frame rate, not a guarantee: ScreenCaptureKit
// only emits a frame when the content changed. Zero means
// [DefaultFPS]. It is converted to SCStreamConfiguration's
// minimumFrameInterval.
FPS float64
// ShowsCursor draws the mouse pointer into the captured frames.
ShowsCursor bool
// QueueDepth is how many frames ScreenCaptureKit keeps in flight. Zero
// means [DefaultQueueDepth]. It must leave room for the two frames this
// package holds on the consumer's behalf (the one lent out and the one
// waiting), so values below 3 are rejected.
QueueDepth int
// ExcludeWindows lists CGWindowIDs to keep out of a DISPLAY capture — for
// example your own overlay, so capturing the screen it sits on does not
// feed it back into itself. Ignored for a window capture.
ExcludeWindows []uint32
// ScalesToFit letterboxes the source into Width×Height instead of
// cropping it when the aspect ratios differ.
ScalesToFit bool
}
Options configures a capture stream.
The zero Options is usable: it captures the source at its native pixel size, at up to 60 frames per second, without the cursor.
func (Options) Validate ¶
Validate reports whether the options are self-consistent, wrapping ErrInvalidOption. It does not consult the system.
type PixelFormat ¶
type PixelFormat uint32
PixelFormat is a CoreVideo OSType naming the layout of a captured frame.
const FormatBGRA PixelFormat = 0x42475241 // 'BGRA'
FormatBGRA is 32-bit BGRA, kCVPixelFormatType_32BGRA. It is the only format this package streams: it is what a compositor wants, it is what the window server produces natively, and it is packed rather than planar so a frame is one contiguous run of bytes.
func (PixelFormat) BytesPerPixel ¶
func (f PixelFormat) BytesPerPixel() int
BytesPerPixel is the size of one pixel in this format.
func (PixelFormat) String ¶
func (f PixelFormat) String() string
String renders the OSType as its four-character code, e.g. "BGRA".
type Rect ¶
type Rect struct {
X, Y, W, H float64
}
Rect is a rectangle in the global desktop coordinate space, in POINTS (not pixels). It mirrors CGRect.
type Stats ¶
type Stats struct {
// Frames is the number of frames actually delivered with pixels.
Frames uint64
// Idle is the number of callbacks that carried no image, which is how
// ScreenCaptureKit says "nothing changed".
Idle uint64
// Superseded is the number of delivered frames that were replaced by a
// newer one before the consumer ever asked for them. A large value next to
// Frames means the consumer is slower than the capture.
Superseded uint64
// Last is when the most recent frame with pixels arrived.
Last time.Time
// Interval is the gap between the two most recent frames with pixels.
Interval time.Duration
}
Stats reports what a stream has seen since it started.
func (Stats) FPS ¶
FPS is the instantaneous rate implied by Stats.Interval, 0 when fewer than two frames have arrived.
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
Stream is the non-darwin stand-in for a live capture. It can never be created here — CaptureDisplay and CaptureWindow always fail — but the type and its methods exist so consumer code compiles unchanged.
func CaptureDisplay ¶
CaptureDisplay reports ErrUnsupported. It still validates the options first, so a consumer's option bug surfaces identically on every platform.
func CaptureWindow ¶
CaptureWindow reports ErrUnsupported, after the same option validation as CaptureDisplay.
type StreamError ¶
type StreamError struct {
// Code is the NSError code in SCStreamErrorDomain.
Code int
// Name is Apple's constant for Code, or "" for a code this package does
// not know.
Name string
// Message is the NSError's localizedDescription.
Message string
// Op names the operation that failed, e.g. "getShareableContent".
Op string
}
StreamError is an error reported by ScreenCaptureKit itself, carrying the SCStreamErrorDomain code. Codes this package recognises unwrap to a sentinel — notably -3801 (SCStreamErrorUserDeclined) unwraps to ErrPermissionDenied — so errors.Is works without anyone having to know the numbers.
func (*StreamError) Error ¶
func (e *StreamError) Error() string
Error renders the code, Apple's name for it and the system's message.
func (*StreamError) Unwrap ¶
func (e *StreamError) Unwrap() error
Unwrap maps the codes with a sentinel to that sentinel.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
sccheck
command
Command sccheck reports what github.com/go-macos/screencapture can see and capture on this machine, and is the quickest way to find out whether the Screen Recording permission is in place.
|
Command sccheck reports what github.com/go-macos/screencapture can see and capture on this machine, and is the quickest way to find out whether the Screen Recording permission is in place. |