Skip to main content

SessionEvent

Enum SessionEvent 

Source
#[non_exhaustive]
pub enum SessionEvent { Established, NewProgram { program: ProgramId, tracks: Vec<TrackSpec>, }, Sample { program: ProgramId, track_id: u32, retention: RetentionClass, sample: Sample, }, TracksChanged { program: ProgramId, tracks: Vec<TrackSpec>, }, }
Available on crate feature std only.
Expand description

What an IngestSession’s Stage::poll hands back to a driver.

#[non_exhaustive]: a later step (egress track-set negotiation, §1.3’s upstream program-split) may still need a ProgramEnded variant — SessionEvent::TracksChanged (issue #781) closed the mid-stream track-set half of this gap, but this step does not add a variant it has no correct producer for yet (this crate’s own precedent — crate::byte_merge’s Hitless2022_7 note).

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Established

The handshake finished: this session’s transport is usable and it is now live. Exactly one of these per session lifetime — see Establishment is ordinary driving. Until it arrives the driver reports HealthState::Establishing; after it, HealthState::Live.

§Why not called TracksResolved

transmux::DemuxEvent::TracksResolved { generation } already owns that name for the analogous demux-side question, and this is deliberately not a synonym for it: tracks here are a per-program fact carried by SessionEvent::NewProgram (finding B5 — one connection, N programs), whereas establishment is a per-connection fact. Folding tracks into this variant would force a session ingesting an MPTS to nominate some arbitrary program as “the one that resolved the connection”, and would leave a session that has finished its handshake but not yet seen a PAT with no way to say so. It carries no payload for the same “no field without a correct producer” reason.

The two events do line up on the awkward case, though, and this is where that lands: DemuxEvent::TracksResolved’s own docs note that a container with no up-front track declaration (FLV/RTMP) legitimately never emits it, and that the asymmetry “is for the media plane’s ingress layer to handle explicitly (e.g. gating on the first DemuxEvent::Sample)”. This variant is that explicit handling: an RTMP session decides for itself when it is ready — gating on the first sample, exactly as those docs suggest — and says so here.

§

NewProgram

A new program was discovered — mint a fresh Trunk for it. May be the very first event a session ever produces, or arrive after many samples for other programs already have — both are the same case; see the module docs.

Fields

§program: ProgramId

Identifies this program for every subsequent SessionEvent::Sample carrying it.

§tracks: Vec<TrackSpec>

The demuxed track specs for this program, so far. Reuses transmux::TrackSpec rather than inventing a parallel type — this crate’s established pattern (see crate::trunk::SegmentEntry’s module doc for the same reuse-don’t-duplicate reasoning).

§

Sample

One decoded sample for track_id, belonging to program (must have been announced via a prior NewProgram with this program — a Sample for an unannounced program is a contract violation by the IngestSession implementor, and a driver drops it rather than panicking; see IngestDriver’s docs).

Fields

§program: ProgramId

Which program this sample belongs to.

§track_id: u32

Track id within that program, matching TrunkWriter::publish’s own track_id.

§retention: RetentionClass

Which ring this sample’s track publishes into — the publisher (ultimately, the IngestSession implementor) decides this, same as any other TrunkWriter::publish caller.

§sample: Sample

The decoded sample itself.

§

TracksChanged

program’s track set changed mid-stream (issue #781) — e.g. transmux’s PMT version diffing detects a broadcaster adding an audio language. Must have been announced via a prior NewProgram with this program, exactly like SessionEvent::Sample — a TracksChanged for an unannounced program is the identical contract violation, and a driver drops it the identical way (see IngestDriver’s docs); it never mints a Trunk on its own.

§Why tracks is the complete replacement set, not a delta

A PMT carries the whole elementary-stream list on every version bump, not just what changed — there is no “here is the one track that was added” signal at that layer, only “here is the program’s full track list, as of now”. Carrying the complete set here mirrors that fact rather than fighting it, and buys two things a delta encoding cannot:

  • Idempotence. Re-delivering the same TracksChanged twice (a retried demux pass, a duplicate event) leaves the trunk’s track set unchanged in content — replacing a set with an identical set is a no-op in substance, whereas replaying an “add track” delta twice would double-add it.
  • Immunity to delta-ordering bugs. A dropped or reordered TrackAdded/TrackRemoved pair (exactly the demux-layer events transmux already emits — see below) can never leave a consumer’s view of the track set permanently wrong: the next TracksChanged is a fresh, authoritative snapshot, not an increment on top of whatever state happened to accumulate.

A consumer that cares which track appeared or vanished diffs this snapshot against the previous one it already holds (or against crate::Trunk::tracks, which this event’s application updates) — that comparison is the consumer’s to make, not this event’s to pre-compute.

§Why this layer does not mirror transmux::DemuxEvent’s three events

transmux already emits DemuxEvent::TrackAdded/TrackRemoved/ TrackUpdated at the demux layer — finer-grained, delta-shaped events aimed at a caller that wants to react to what changed. This layer deliberately does not mirror that shape: IngestSession implementors translate whatever demux-layer deltas they see into one full snapshot per change, for the same reason SessionEvent::NewProgram carries a full tracks: Vec<TrackSpec> rather than a “here is track N” event per track — see the module docs.

§Scope: ingress→Trunk plumbing only

This variant and IngestDriver’s handling of it stop at storing the new set on the program’s Trunk (see crate::TrunkWriter::set_tracks). Deciding whether/when to admit a newly-appeared track into an egress manifest (LL-HLS/DASH rendering) is a separate, deliberate decision belonging to its own issue — not something a track-set snapshot arriving at the Trunk should trigger implicitly.

Fields

§program: ProgramId

Which program’s track set changed — must match a program already announced via SessionEvent::NewProgram.

§tracks: Vec<TrackSpec>

The complete replacement track set — see this variant’s own doc for why this is a full snapshot rather than a delta.

Trait Implementations§

Source§

impl Clone for SessionEvent

Source§

fn clone(&self) -> SessionEvent

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SessionEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.