pub struct DxgiCaptureSource { /* private fields */ }dxgi-capture and (crate features dxgi-capture or wasapi-capture) only.Expand description
Captures the desktop via Windows’ DXGI Desktop Duplication API
(IDXGIOutputDuplication) — GStreamer’s d3d11screencapturesrc
equivalent. One src pad, pushing Pixel::BGRA frames (no internal
color conversion — same division of labor as every other source in
this crate: chain a crate::elements::Scaler downstream if
something needs YUV420P, e.g. crate::elements::D3d12Renderer’s
CPU-upload path or crate::elements::SwEncoder).
Emits at a constant rate — DxgiCaptureOptions::fps — not one
push per real desktop change. An earlier version of this pushed
variable-rate (VFR): a real wall-clock pts per actual change, nothing
in between. That turned out to cause real problems, both for muxing
(most consumers assume something closer to a steady rate) and,
concretely, for live rendering: D3d12Renderer presents on a
vsync-locked swap chain (Present(1, ..)) that only ever shows the
latest submitted frame each tick, silently dropping anything else
queued behind it — submission timing straight off an irregular VFR
source has no relationship to that vsync grid, so real changes would
unpredictably race into the same tick (one silently discarded) or
land in a gap (stale frame held an extra tick), which is visible
judder even though the average rate was exactly right.
So instead: this element always keeps the most recently captured
desktop image on hand, and SourceElement::run’s own loop emits it
— the same one again if nothing changed since the last tick — at a
steady 1 / fps cadence, entirely on the one thread run() already
has (no extra threads spawned; see this crate’s own “elements never
spawn their own threads” rule). Same shape as
crate::elements::TestVideoSource: DxgiCaptureSource::time_base
is 1 / fps and pts is a plain incrementing tick counter, one per
emitted frame, not per real capture.
Confirmed (examples/render/screen_capture, with and without a
downstream crate::elements::Pacer) that this constant-rate,
drift-free schedule is what actually mattered — not whether a
separate Pacer stage exists. The VFR version needed one to paper
over its own irregular submission timing; once emission here is
steady and drift-free, Scaler’s modest, fairly consistent per-frame
conversion cost isn’t enough on its own to reintroduce the same vsync
misalignment, so a straight DxgiCaptureSource -> Scaler -> D3d12Renderer
chain stays smooth with no Pacer at all. Pacer remains genuinely
useful for other reasons (multi-stream sync against a shared Clock,
or a stage with real per-frame variance like SwEncoder), just not
load-bearing here purely for vsync alignment the way it first
appeared to be.
Deliberately does not retry internally on DXGI_ERROR_ACCESS_LOST
(lock screen, UAC prompt, display mode change, …) — same “fail fast,
caller rebuilds” contract as crate::elements::RtspSource; watch for
DxgiCaptureSourceError::AccessLost and call
DxgiCaptureSource::open again.
Runs until Stop — never reaches Eos on its own, same as
TestVideoSource (there’s no natural end to a live desktop capture).
May capture from more than one output at once — see
CaptureArea::Region — in which case every field below that used
to describe “the” duplication instead describes one CaptureUnit
per contributing output.
Implementations§
Source§impl DxgiCaptureSource
impl DxgiCaptureSource
Sourcepub fn open(
name: impl Into<String>,
options: DxgiCaptureOptions,
) -> Result<(Self, u32, u32, Option<ID3D11Device>), DxgiCaptureSourceError>
pub fn open( name: impl Into<String>, options: DxgiCaptureOptions, ) -> Result<(Self, u32, u32, Option<ID3D11Device>), DxgiCaptureSourceError>
Opens whichever output(s) DxgiCaptureOptions::area resolves to
and starts duplicating them. Returns the element alongside the
captured composite’s actual (width, height) — what the caller
needs to build a matching downstream
crate::elements::Scaler/crate::elements::Pacer, same
pattern as crate::elements::RtspSource::open returning stream
info — plus, under CaptureMode::Gpu, the ID3D11Device this
capture was opened on (None under CaptureMode::Cpu, where
nothing downstream needs to share it). This is always built from
whichever adapter area actually resolves to — see
CaptureMode::Gpu’s own docs on why callers should build every
other D3D11 element sharing this capture from the returned device
rather than a separately-created one.
Sourcepub fn time_base(&self) -> Rational
pub fn time_base(&self) -> Rational
The unit each emitted frame’s pts is expressed in — what you
need to construct a matching crate::elements::Pacer. 1 / fps, same convention as crate::elements::TestVideoSource::time_base.
Trait Implementations§
Source§impl Element for DxgiCaptureSource
impl Element for DxgiCaptureSource
Source§fn name(&self) -> Arc<str> ⓘ
fn name(&self) -> Arc<str> ⓘ
crate::bus::BusEvent stores names as
Arc<str> for exactly this reason: a hot path like
crate::queue::Queue posting BusEvent::Dropped once per
overflowed buffer shouldn’t pay for a fresh heap allocation every
time it wants to report which element it is.Source§fn element_type(&self) -> ElementType
fn element_type(&self) -> ElementType
ElementType.Source§fn pp_log(&self) -> &PpLog
fn pp_log(&self) -> &PpLog
crate::bus::Bus::post — same
id/name as Element::name, just already wrapped as the
crate::pp_log::PpLog its pp_info!/pp_warn!/pp_error! macros need. A
stored private field, not built fresh per call, for the same reason
name() returns a cheap Arc<str> clone instead of a fresh String
— see its own docs.Source§fn pp_log_mut(&mut self) -> &mut PpLog
fn pp_log_mut(&mut self) -> &mut PpLog
Element::pp_log reads — used by
crate::pipeline::ChainBuilder to stamp the owning
crate::pipeline::Pipeline’s id onto every element that
passes through it, via element_pp_log. Not meant to be called
from anywhere else.impl Send for DxgiCaptureSource
Source§impl SourceElement for DxgiCaptureSource
impl SourceElement for DxgiCaptureSource
Source§fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()>
fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()>
Eos (normal completion),
crate::pipeline::Pipeline::finish, or Stop (see
ControlMsg::Stop) — call crate::control::drain_control
once per loop iteration to make control responsive between
blocking reads. Read moreSource§fn seek(&mut self, _target: Duration) -> Result<Duration>
fn seek(&mut self, _target: Duration) -> Result<Duration>
target, an absolute position from the
start of the media (e.g. av_seek_frame for
crate::elements::FileDemuxer). Called by
crate::control::drain_control as part of handling
ControlMsg::Seek, before that message is forwarded to the
source’s own pads — so whatever’s read next comes from the new
position by the time downstream elements are told to flush for it. Read more