pub(crate) struct AudioCapture {
pub(crate) shared: Arc<Shared>,
}Expand description
Python-facing capture handle. Owns the Shared lifecycle state and exposes
start_capture / stop_capture / update_audio_bitrate / is_capturing to Python.
Fields§
Implementations§
Source§impl AudioCapture
impl AudioCapture
pub(crate) fn new() -> Self
Sourcepub(crate) fn start_capture(
&self,
py: Python<'_>,
settings: &Bound<'_, PyAny>,
callback: Py<PyAny>,
) -> PyResult<()>
pub(crate) fn start_capture( &self, py: Python<'_>, settings: &Bound<'_, PyAny>, callback: Py<PyAny>, ) -> PyResult<()>
Start (or restart) audio capture, delivering encoded frames to callback.
- Re-entrancy guard: if called on one of the run’s own threads — the delivery
thread (where the Python callback actually executes) or the capture thread — it
cannot join/recreate the run it is part of (the capture thread joins the delivery
thread on teardown, so a join from either closes a cycle), so it just undoes a
nested SELF-stop and returns. That undo is a compare-exchange that clears the stop
ONLY if this thread still owns it — if an external stop stored
STOP_EXTERNALmeanwhile, the CAS fails and that stop stands (clearing it would strand its in-flight join forever). - Spawn with the GIL released:
spawn_workerstops/joins any prior thread and spawns the new one viapy.detach, because the lifecycle lock andjoin()must not be held while holding the GIL — joining the capture thread transitively joins the delivery thread, whose in-flight callback needs the GIL, so that would deadlock. The stop/clear ordering (the lost-stop invariant) lives inspawn_worker. - Register the handle for the atexit sweep (best-effort), pruning dead weaks.
- Startup handshake (
await_start): waits up to ~2 s (GIL released) for the thread to publishRUNNINGorFAILED, returningOkwhile it is stillSTARTING— the retry ladder can run longer than the window, so a later failure is observed throughstate/last_error. OnFAILED,join_failed_starttears down ONLY the thread this call spawned (identity-checked) — a concurrent start may already own the slot with a live run that must survive — andlast_erroris raised.
Sourcepub(crate) fn stop_capture(&self, py: Python<'_>)
pub(crate) fn stop_capture(&self, py: Python<'_>)
Stop audio capture, joining the capture thread.
A re-entrant stop from one of the run’s own threads — the delivery thread (where the
Python callback executes) or the capture thread — only records a self-stop: a join
from inside the run would cycle (the capture thread joins the delivery thread on
teardown). It must not clobber an external stop already in effect, which has to win
the join. Otherwise it takes the lifecycle lock and joins with the GIL released (via
py.detach): joining the capture thread transitively joins the delivery thread,
whose in-flight callback needs the GIL, so holding it would deadlock. The
authoritative external stop is set INSIDE the lock immediately before the join, so
it wins over any concurrent self-stop.
Sourcepub(crate) fn update_audio_bitrate(&self, bps: i32)
pub(crate) fn update_audio_bitrate(&self, bps: i32)
Set the live Opus target bitrate (bits/s) via the atomic mirror; the capture loop applies it on the next frame, without a restart. Values are clamped to the valid Opus range so an out-of-range request can never wedge the encoder.
Sourcepub(crate) fn is_capturing(&self) -> bool
pub(crate) fn is_capturing(&self) -> bool
True while a capture worker is connected and running with no stop pending; false
while still starting and after a failure — see state to tell those apart.
Sourcepub(crate) fn state(&self) -> &'static str
pub(crate) fn state(&self) -> &'static str
Lifecycle phase: "idle", "starting", "running" or "failed". A run that
fails after start_capture returned (its retry ladder gave up, or a mid-run
reconnect budget was spent) reads "failed" with the reason in last_error.
Sourcepub(crate) fn last_error(&self) -> Option<String>
pub(crate) fn last_error(&self) -> Option<String>
Why the last run failed, or None while no run has failed since the last start.
Trait Implementations§
impl DerefToPyAny for AudioCapture
Source§impl Drop for AudioCapture
impl Drop for AudioCapture
Source§fn drop(&mut self)
fn drop(&mut self)
Best-effort stop on GC/dealloc: the re-entrant case (running on the run’s own
delivery or capture thread) records a self-stop only (never clobbering a pending
external stop); otherwise it takes the lifecycle lock and joins the capture thread
with the GIL released, matching stop_capture.
impl ExtractPyClassWithClone for AudioCapture
Source§impl<'py> IntoPyObject<'py> for AudioCapture
impl<'py> IntoPyObject<'py> for AudioCapture
Source§type Target = AudioCapture
type Target = AudioCapture
Source§type Output = Bound<'py, <AudioCapture as IntoPyObject<'py>>::Target>
type Output = Bound<'py, <AudioCapture as IntoPyObject<'py>>::Target>
Source§fn into_pyobject(
self,
py: Python<'py>,
) -> Result<<Self as IntoPyObject<'_>>::Output, <Self as IntoPyObject<'_>>::Error>
fn into_pyobject( self, py: Python<'py>, ) -> Result<<Self as IntoPyObject<'_>>::Output, <Self as IntoPyObject<'_>>::Error>
Source§impl PyClass for AudioCapture
impl PyClass for AudioCapture
Source§impl PyClassImpl for AudioCapture
impl PyClassImpl for AudioCapture
Source§const MODULE: Option<&str> = ::core::option::Option::None
const MODULE: Option<&str> = ::core::option::Option::None
Source§const IS_BASETYPE: bool = false
const IS_BASETYPE: bool = false
Source§const IS_SUBCLASS: bool = false
const IS_SUBCLASS: bool = false
Source§const IS_MAPPING: bool = false
const IS_MAPPING: bool = false
Source§const IS_SEQUENCE: bool = false
const IS_SEQUENCE: bool = false
Source§const IS_IMMUTABLE_TYPE: bool = false
const IS_IMMUTABLE_TYPE: bool = false
Source§const RAW_DOC: &'static CStr = c"Python-facing capture handle. Owns the `Shared` lifecycle state and exposes\n`start_capture` / `stop_capture` / `update_audio_bitrate` / `is_capturing` to Python.\x00"
const RAW_DOC: &'static CStr = c"Python-facing capture handle. Owns the `Shared` lifecycle state and exposes\n`start_capture` / `stop_capture` / `update_audio_bitrate` / `is_capturing` to Python.\x00"
Source§const DOC: &'static CStr
const DOC: &'static CStr
text_signature if a constructor is defined. Read moreSource§type Layout = <<AudioCapture as PyClassImpl>::BaseNativeType as PyClassBaseType>::Layout<AudioCapture>
type Layout = <<AudioCapture as PyClassImpl>::BaseNativeType as PyClassBaseType>::Layout<AudioCapture>
Source§type ThreadChecker = NoopThreadChecker
type ThreadChecker = NoopThreadChecker
Source§type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::MutableChild
type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::MutableChild
Source§type BaseNativeType = PyAny
type BaseNativeType = PyAny
PyAny by default, and when you declare
#[pyclass(extends=PyDict)], it’s PyDict.fn items_iter() -> PyClassItemsIter
fn lazy_type_object() -> &'static LazyTypeObject<Self>
§fn dict_offset() -> Option<PyObjectOffset>
fn dict_offset() -> Option<PyObjectOffset>
§fn weaklist_offset() -> Option<PyObjectOffset>
fn weaklist_offset() -> Option<PyObjectOffset>
Source§impl PyClassNewTextSignature for AudioCapture
impl PyClassNewTextSignature for AudioCapture
const TEXT_SIGNATURE: &'static str = "()"
Source§impl PyMethods<AudioCapture> for PyClassImplCollector<AudioCapture>
impl PyMethods<AudioCapture> for PyClassImplCollector<AudioCapture>
fn py_methods(self) -> &'static PyClassItems
Source§impl PyTypeInfo for AudioCapture
impl PyTypeInfo for AudioCapture
Source§const NAME: &str = <Self as ::pyo3::PyClass>::NAME
const NAME: &str = <Self as ::pyo3::PyClass>::NAME
prefer using ::type_object(py).name() to get the correct runtime value
Source§const MODULE: Option<&str> = <Self as ::pyo3::impl_::pyclass::PyClassImpl>::MODULE
const MODULE: Option<&str> = <Self as ::pyo3::impl_::pyclass::PyClassImpl>::MODULE
prefer using ::type_object(py).module() to get the correct runtime value
Source§fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject
fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject
§fn type_object(py: Python<'_>) -> Bound<'_, PyType>
fn type_object(py: Python<'_>) -> Bound<'_, PyType>
§fn is_type_of(object: &Bound<'_, PyAny>) -> bool
fn is_type_of(object: &Bound<'_, PyAny>) -> bool
object is an instance of this type or a subclass of this type.§fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool
fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool
object is an instance of this type.Auto Trait Implementations§
impl Freeze for AudioCapture
impl RefUnwindSafe for AudioCapture
impl Send for AudioCapture
impl Sync for AudioCapture
impl Unpin for AudioCapture
impl UnsafeUnpin for AudioCapture
impl UnwindSafe for AudioCapture
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<'py, T> IntoPyObjectExt<'py> for Twhere
T: IntoPyObject<'py>,
impl<'py, T> IntoPyObjectExt<'py> for Twhere
T: IntoPyObject<'py>,
§fn into_bound_py_any(self, py: Python<'py>) -> Result<Bound<'py, PyAny>, PyErr>
fn into_bound_py_any(self, py: Python<'py>) -> Result<Bound<'py, PyAny>, PyErr>
self into an owned Python object, dropping type information.§fn into_py_any(self, py: Python<'py>) -> Result<Py<PyAny>, PyErr>
fn into_py_any(self, py: Python<'py>) -> Result<Py<PyAny>, PyErr>
self into an owned Python object, dropping type information and unbinding it
from the 'py lifetime.§fn into_pyobject_or_pyerr(self, py: Python<'py>) -> Result<Self::Output, PyErr>
fn into_pyobject_or_pyerr(self, py: Python<'py>) -> Result<Self::Output, PyErr>
self into a Python object. Read more§impl<T> PyErrArguments for T
impl<T> PyErrArguments for T
§impl<T> PyTypeCheck for Twhere
T: PyTypeInfo,
impl<T> PyTypeCheck for Twhere
T: PyTypeInfo,
§fn type_check(object: &Bound<'_, PyAny>) -> bool
fn type_check(object: &Bound<'_, PyAny>) -> bool
§fn classinfo_object(py: Python<'_>) -> Bound<'_, PyAny>
fn classinfo_object(py: Python<'_>) -> Bound<'_, PyAny>
isinstance and issubclass function. Read more