Skip to content

Releases: OxideAV/oxideav-rtmp

v0.0.6

15 Jun 05:09
119d2fb

Choose a tag to compare

Other

  • strongly-typed colorInfo HDR metadata for VideoPacketType.Metadata
  • decode AMF0 object references (marker 0x07) per FLV §E.4.4.2
  • decode externalizable objects via registered per-class handlers (§3.12 U29O-traits-ext)
  • RTMP §5.3 Acknowledgement honoured on received-byte window
  • Enhanced RTMP v2 NetConnection.Connect.ReconnectRequest end-to-end
  • RTMP §5.2 Abort Message builder + reader partial-discard
  • typed UserControlEvent enum + round-trip parser
  • drop release-plz.toml — use release-plz defaults across the workspace
  • bump publisher-close drain windows for Ubuntu CI scheduling
  • RTMP §3.7 StreamDry / StreamIsRecorded / PingResponse + builders
  • typed MessageStreamKind accessor + spec-§5 protocol-control invariant validator
  • bind set_read_timeout to the reader's actual socket clone
  • replace drop(client) with close() so Windows CI doesn't race the flush
  • route Aggregate Messages (type 22) through next_packet + poll_event
  • fold ModEx TimestampOffsetNano onto the Packet timeline
  • Aggregate Message (type 22) parser + builder
  • Enhanced RTMP v1+v2 NetConnection connect capability negotiation
  • rephrase FlvReader::with_max_tag_size docs
  • FLV file / byte-stream reader (Annex E)
  • FLV file / byte-stream writer (Annex E)

Added

  • Strongly-typed colorInfo HDR metadata for VideoPacketType.Metadata
    (src/flv.rs). Enhanced RTMP §"Metadata Frame" defines the
    VideoPacketType.Metadata (= 4) video message as an AMF-encoded
    sequence of [name, value] pairs, the only defined name being
    "colorInfo" — an HDR metadata object carrying colorConfig
    (bitDepth + the ITU-T H.273 colourPrimaries / transferCharacteristics /
    matrixCoefficients enumeration indices), hdrCll (maxFall / maxCLL
    content light level in cd/m2) and hdrMdcv (SMPTE ST 2086:2018
    mastering-display chromaticity coordinates + min/max luminance). The
    body previously passed through as opaque AMF bytes; it now lifts into
    the typed [ColorInfo] / [ColorConfig] / [HdrCll] / [HdrMdcv]
    views via VideoTag::color_info(), and VideoTag::color_info_tag(fourcc, &ColorInfo) rebuilds the matching outbound metadata tag. Every property
    is Option<f64> (AMF's native double, kept byte-exact rather than
    coerced to an integer that would lose a fractional luminance value), and
    each sub-object is Option so a partial colorInfo — e.g. only
    colorConfig — round-trips. The spec's "reset to original color state"
    signal is preserved: an Undefined value (the RECOMMENDED form) and an
    empty {} object both decode to [ColorInfo::is_reset], and a reset
    ColorInfo re-encodes as Undefined. A colorInfo value of the wrong
    AMF type is a clean Error::Other; a metadata tag with a different
    (future) pair name yields Ok(None) rather than an error. Seven tests
    cover full-HDR10 round-trip, colorConfig-only partial, the two reset
    forms, the non-metadata-tag / non-colorInfo-name None cases, and the
    wrong-type rejection.

  • AMF0 object references (marker 0x07) — decoded and dereferenced
    transparently
    (src/amf.rs). The FLV v10.1 spec §E.4.4.2
    SCRIPTDATAVALUE table defines Type == 7 with the wire shape
    IF Type == 7 { UI16 } — a 16-bit big-endian index into the table of
    complex objects (Object, ECMA array, strict array) serialized so far
    in the same context. The decoder previously rejected marker 0x07
    outright, so any onMetaData/command payload that used a reference to
    deduplicate a repeated complex value failed to decode entirely. The
    decoder now maintains a per-context reference table (scoped to one
    decode_all packet, or one top-level value for decode), appends
    each complex value as it is decoded — reserving the slot before the
    body so a reference appearing inside that body still resolves — and
    resolves a reference to a clone of the indexed value. Callers never
    see a Reference variant in the value graph; encoding always emits
    the expanded (inline) form, which is byte-valid AMF0. An
    out-of-range or truncated reference index surfaces a clean
    InvalidAmf0 error rather than a panic. Six tests cover prior-object
    resolution, second-object indexing, in-body references, out-of-range
    and truncated indices, and the per-value scope of decode.

  • AMF3 §3.12 U29O-traits-ext externalizable objects — decodable via
    registered per-class handlers
    (src/amf3.rs). The spec encodes an
    externalizable object's body as "an indeterminable number of bytes as
    *(U8)" whose framing is a private agreement between the sending and
    receiving classes; the generic decoder cannot know where the body
    ends and so previously refused every externalizable object outright,
    even though the encoder already re-emitted the externalizable_body
    field verbatim. Decoder::register_externalizable(class_name, reader)
    closes that asymmetry: a caller that knows a specific class's
    IExternalizable.writeExternal framing registers a body-length
    resolver (ExternalizableReader = Box<dyn Fn(&[u8], usize) -> Result<usize>>), and decode then captures exactly that many body
    bytes into Amf3Value::Object::externalizable_body, advances pos
    past them, and inserts the object into the object reference table like
    any other complex value. An over-long body length is rejected before
    any out-of-bounds read; an unregistered externalizable class is still
    refused loudly (the decoder never guesses). Handlers are decoder
    configuration and survive reset_tables. Six tests: fixed-length and
    length-prefixed handlers, decode→encode wire round-trip, overrun
    rejection, handler persistence across reset, and object-reference
    participation.

  • RTMP 1.0 §5.3 Acknowledgement / §5.5 Window Acknowledgement Size —
    honoured end-to-end
    (src/chunk.rs, src/server.rs,
    src/client.rs). Until now both peers advertised a Window
    Acknowledgement Size but neither side ever sent the §5.3
    Acknowledgement the spec mandates ("the client or the server sends
    the acknowledgment to the peer after receiving bytes equal to the
    window size") — the client code even carried a // future refinement comment in its control-message branch. ChunkReader now
    counts every byte it consumes off the wire (basic header, message
    header, extended timestamp, payload) as the §5.3 sequence number via
    a new read_exact_counted funnel, stores the peer-negotiated window
    (set_window_ack_size, fed from inbound §5.5 Window Ack Size and the
    §5.6 Set Peer Bandwidth output-bandwidth value, which the spec
    defines as equal to the window size), and exposes ack_due()
    returns Some(seq) the first time the received-byte count crosses
    the window, re-arming only after another full window so a steady
    stream never spams acks. RtmpSession::next_packet (server) and
    RtmpClient::poll_event (client) call it after each wire read and
    emit build_ack(seq) when one is owed; both setup paths
    (drive_until_publish / wait_for_result) seed the window so the
    obligation is live before the first media frame. received_bytes()
    / window_ack_size() accessors round out the public surface.
    Resetting the window re-bases the byte accounting so an
    already-counted byte never instantly owes an ack, and with no window
    negotiated the obligation stays dormant (byte-identical to the
    pre-§5.3 behaviour). New tests/acknowledgement_window.rs drives a
    raw publisher (built from the public chunk / message /
    handshake modules) that advertises a 256-byte window and confirms
    the real RtmpServer acks back with a plausible sequence number;
    four chunk.rs unit tests cover the byte-count, window-crossing,
    one-ack-per-window, and re-base behaviours.

  • Enhanced RTMP v2 Reconnect Request — end-to-end
    (src/message.rs, src/server.rs, src/client.rs). Round 277 wires
    the NetConnection.Connect.ReconnectRequest status event from
    enhanced-rtmp-v2.pdf §"Reconnect Request" — until now the crate
    advertised the matching capsEx Reconnect bit (CAPS_EX_RECONNECT)
    during connect-capability negotiation but had no way to send or
    react to the event itself. New
    message::build_reconnect_request(tc_url, description) emits the
    spec's NetConnection-level onStatus shape — ["onStatus", 0.0, null, info] on message stream 0, with the Info Object carrying the
    mandatory code = NetConnection.Connect.ReconnectRequest
    (exported as RECONNECT_REQUEST_CODE) + level = "status" pairs
    and the optional tcUrl / description properties (omitted from
    the wire when None, per their "optional" marking in the spec's
    Info Object table). RtmpSession::send_reconnect_request is the
    ingest-side helper — used "prior to the shutdown of the live
    streaming server or when the server intends to remap the client to
    another server instance," after which the old server keeps
    processing publisher messages per spec (so next_packet pumping
    continues unchanged). On the publisher side,
    RtmpClient::poll_event now classifies the event into the new typed
    ClientEvent::ReconnectRequest { tc_url, description } variant
    (code match alone is not enough — the spec says level MUST be
    status, so a mismatched level falls through as a plain
    OnStatus). The new resolve_tc_url(base, reference) /
    RtmpClient::resolve_reconnect_url(Option<&str>) helpers apply the
    spec's target-resolution rule — "if not specified, use the tcUrl for
    the current connection. A relative URI reference should be resolved
    relative to the tcUrl for the current connection" — covering all
    four reference shapes the Info Object table gives as examples
    (absolute rtmp://host:port/app, network-path //host/app,
    absolute-path /app, and relative-path app); RtmpClient::tc_url()
    exposes the stored base. Tests: wire-shape + optional-property
    omission in src/message.rs, classification + resolution tables in
    src/client.rs, and `tests/reconnect_requ...

Read more

v0.0.5

30 May 02:38
a6e0be1

Choose a tag to compare

Other

  • Enhanced RTMP v2 Multitrack body parser + builder (audio + video)
  • decode Enhanced-RTMP v2 MultichannelConfig audio body
  • injection-robust property tests + AMF nesting depth guards
  • poll_event surfaces server-originated UserControl + onStatus
  • emit UserControl StreamEOF before Unpublish.Success on close

Added

  • Enhanced RTMP v2 Multitrack audio + video body parser + builder
    (src/flv.rs, tests/multitrack.rs). The VideoPacketType.Multitrack = 6
    and AudioPacketType.Multitrack = 5 body shapes from
    enhanced-rtmp-v2.pdf §"ExVideoTagBody" / §"ExAudioTagBody" are now
    decoded end-to-end. The multitrackType (UB[4]) | realPacketType (UB[4]) byte plus the optional shared FourCC (omitted in
    ManyTracksManyCodecs mode per spec) are consumed inline by
    parse_video / parse_audio ahead of the existing FourCC slot, and
    the per-track list ((trackFourCc if ManyTracksManyCodecs) | trackId(UI8) | (sizeOfTrack(UI24) if not OneTrack) | body) is lifted
    into a typed Multitrack { multitrack_type, tracks } struct on the
    new VideoTag::multitrack / AudioTag::multitrack fields. The
    outer tag's ex_packet_type now holds the real inner PacketType
    (e.g. CodedFrames, SequenceStart) so a downstream ex_packet_type == SequenceStart check still works for multitrack tags, and
    fourcc / audio_fourcc hold the shared codec for OneTrack /
    ManyTracks modes (and None for ManyTracksManyCodecs, where each
    MultitrackTrack::fourcc carries the per-track codec).
    VideoTag::multitrack_tag and AudioTag::multitrack_tag are the
    outbound builders; VideoTag::is_multitrack() / AudioTag::is_multitrack()
    are the discriminators. New constants AV_MULTITRACK_TYPE_ONE_TRACK
    / AV_MULTITRACK_TYPE_MANY_TRACKS / AV_MULTITRACK_TYPE_MANY_TRACKS_MANY_CODECS
    cover the spec's enum AvMultitrackType. Reserved discriminators
    (3..=15) round-trip verbatim through Multitrack::parse /
    Multitrack::encode so a forwarding ingest preserves future modes.
    The spec invariant that the inner real PacketType MUST NOT itself be
    Multitrack is enforced — a forged inner nibble of 6 (video) or
    5 (audio) surfaces a clean Error::Other("…MUST NOT…") rather
    than recursing, and a truncated sizeOfTrack overrun yields a clean
    …overruns remaining N bytes error. 23 new tests (11 unit in
    src/flv.rs, 12 integration in tests/multitrack.rs) cover: video
    OneTrack AVC CodedFrames byte-exact wire layout; video ManyTracks
    HEVC two-track byte-exact UI24 sizes; video ManyTracksManyCodecs
    HEVC+AV1; video SequenceStart ManyTracks VVC; audio OneTrack
    Opus CodedFrames; audio ManyTracksManyCodecs Opus+FLAC mixed-codec;
    audio ManyTracks AAC; audio SequenceStart ManyTracks AAC with
    per-track ASC; the inner-PacketType-MUST-NOT-be-Multitrack invariant
    for both audio and video; size-overrun-error and three other
    truncation paths; track ordering preserved verbatim through round-trip
    (trackIds [7, 0, 3] stay [7, 0, 3]); empty per-track body
    (sizeOfTrack = 0) round-trips; ManyTracks ModEx-prelude
    composition with TimestampOffsetNano = 123_456 (proves the
    ModEx + Multitrack preludes compose on the wire); and a reserved
    multitrack_type = 4 direct Multitrack::encode+parse symmetry.
    Resolves the Multitrack portion of the r177 / r186 README notes
    "Multitrack still parses to an opaque body pending a follow-up
    round." Total integration-test count: 49 → 61 (+12); total tests:
    191 → 202.

  • Enhanced RTMP v2 MultichannelConfig audio body parser + builder
    (src/flv.rs). The AudioPacketType.MultichannelConfig = 4 body
    shape from enhanced-rtmp-v2.pdf §"ExAudioTagBody" is now decoded
    end-to-end via the new MultichannelConfig struct + the
    MultichannelConfigOrder discriminated union (Unspecified /
    Native { flags: u32 } / Custom { mapping: Vec<u8> } /
    Reserved(u8)). On the wire the body is
    audioChannelOrder(UI8) | channelCount(UI8) | (mapping[UI8] | flags(UI32) | nothing); lengths line up at 2 bytes for Unspecified,
    6 bytes for Native, and 2 + channelCount for Custom. Truncated
    bodies, stray trailing bytes on Unspecified, and short Custom
    mappings all return Err(Error::Other) cleanly; an unrecognised
    audioChannelOrder value is preserved as
    MultichannelConfigOrder::Reserved with the trailing bytes in
    extra so a forwarding tag never silently loses data.
    AudioTag::is_multichannel_config(),
    AudioTag::multichannel_config(), and
    AudioTag::multichannel_config_tag(fourcc, &cfg) provide the
    lift / round-trip helpers; the existing parse_audio / build_audio
    bytes path is unchanged (the body is still carried verbatim through
    AudioTag::body). New audio_channel and audio_channel_mask
    public submodules expose the 24 spec-defined channel positions and
    their corresponding UI32 mask bits (including the 22.2 surround
    extensions per SMPTE ST 2036-2-2008), plus audio_channel::UNUSED
    (0xFE) and UNKNOWN (0xFF) sentinels. New
    AUDIO_CHANNEL_ORDER_UNSPECIFIED / _NATIVE / _CUSTOM constants
    cover the order discriminator. 10 new unit tests cover: 2-byte
    Unspecified round-trip; 6-byte Native 5.1 mask round-trip with
    byte-exact wire bytes; Custom stereo round-trip; full 22.2
    Custom (24-byte mapping) round-trip exercising every
    audio_channel::* constant; UNUSED / UNKNOWN sentinel
    preservation; six truncation paths (empty body, partial header,
    partial flags, short mapping, stray bytes on Unspecified);
    Reserved order verbatim round-trip; end-to-end build → parse →
    lift through build_audio + parse_audio on an Opus FourCC tag;
    accessor returns None for non-MultichannelConfig packet types and
    for legacy tags whose body happens to start with valid-looking
    bytes; and a bit-position check confirming 1 << channel_index
    equals the matching audio_channel_mask entry for every one of the
    24 channels. Resolves the MultichannelConfig portion of the r177
    README note "Multitrack and MultichannelConfig AudioPacketTypes
    parse to opaque bodies pending follow-up rounds." Multitrack
    remains deferred — its AvMultitrackType + per-track FourCc + track id + sizeOfAudioTrack framing needs a richer follow-up.

  • Injection-robustness property tests + AMF0/AMF3 nested-container
    depth guards
    (src/amf.rs, src/amf3.rs,
    tests/injection_robustness.rs). Every public parser surface — AMF0
    (decode / decode_all), AMF3 (decode / decode_all /
    decode_data_message), FLV (parse_video / parse_audio), the
    chunk-stream reader (ChunkReader::read_message), and both
    handshake directions (client_handshake / server_handshake) — is
    now fuzzed with a deterministic xorshift PRNG (no rand dep): 1024
    random-byte iterations for each AMF surface, 2048 for each FLV
    surface, 512 for ChunkReader, plus a 1024-iteration "valid frame
    with 1..=4 random byte flips" mutation pass on a built
    onMetaData payload. Adversarial structural inputs are also covered:
    truncated handshakes from both directions and at every truncation
    boundary, wrong RTMP version bytes (0x00 / 0x01 / 0x06 /
    0xFF), AMF0 M_STRICT_ARRAY with a u32::MAX length, AMF0
    M_STRING claiming 65535 bytes from a 3-byte buffer, a fmt-0 chunk
    with an oversize 24-bit msg_length and a forged fmt-1 chunk
    arriving with no prior fmt-0 state. The runtime guarantee: every
    call either returns Ok or Err, never panics, never spins, and
    never over-allocates (amf0_strict_array_with_huge_count_errors_fast
    asserts the error path is under 100 ms even with u32::MAX).
    Stack-overflow protection: amf::MAX_DECODE_DEPTH = 64 and
    amf3::MAX_DECODE_DEPTH = 64 cap nested-container recursion before
    the call stack runs out; AMF0 routes through a new
    decode_at_depth(buf, pos, depth) and AMF3 tracks depth as a
    field on Decoder (incremented on entry to decode, decremented on
    return). Tests build 2_000-level-deep forged Object frames in both
    formats and assert the guard surfaces a clean Error::InvalidAmf0
    before the default 8 MiB stack overflows. Integration-test count:
    28 → 49 (+21).

  • RtmpClient::poll_event surfaces server-originated events; symmetric
    UserControl StreamEOF recognition on the client side
    (src/client.rs,
    src/lib.rs). Round 154 added the server-side teardown that emits a
    UserControl StreamEOF(stream_id) (RTMP 1.0 §7.1.7) before
    onStatus(NetStream.Unpublish.Success) and the write-half FIN. The
    pre-r158 RtmpClient swallowed those server-originated bytes — the
    reader field was #[allow(dead_code)]-flagged and the only
    post-publish reads happened opportunistically when the underlying
    TcpStream dropped, so a publisher couldn't tell "server cleanly
    closed our publish" from "TCP connection died." This round wires up a
    poll_event(&mut self) -> Result<Option<ClientEvent>> surface: each
    call reads one inbound RTMP message, handles protocol-control
    housekeeping internally (Set Chunk Size, Window Ack Size, Set Peer
    Bandwidth, Ping Request → Ping Response auto-reply), and returns the
    externally-visible notifications as a new ClientEvent enum:
    StreamBegin { stream_id }, StreamEof { stream_id },
    OnStatus { level, code, description },
    Result { transaction_id, values },
    ErrorReply { transaction_id, values }, and Other. StreamEof is
    not itself terminal — the server's close path emits onStatus after
    StreamEOF, so poll_event keeps reading until the TCP read half
    observes EOF / connection-reset, at which point a read_eof latch
    makes subsequent calls return Ok(None) immediately without
    re-entering the chunk reader on a dead socket. New
    tests/client_stream_eof.rs covers end-to-end against our own
    RtmpServer::close: the client observes
    `ClientEvent::StreamEof { stre...

Read more

v0.0.4

24 May 12:03
3dcfa7b

Choose a tag to compare

Other

  • graceful FIN on close to stop teardown truncating A/V frames
  • Enhanced RTMP v2 ModEx packet-type prelude (audio + video)
  • route AMF3 data/command messages into message dispatch
  • AMF3 wire-format parser + builder (full §3.1 + §1.3.1 + §2.2)
  • Enhanced RTMP v2 video FourCC additions (vp08 / avc1 / vvc1)
  • Enhanced RTMP v2 audio framing (FourCC Opus / FLAC / AC-3 / E-AC-3 / MP3 / AAC)
  • Enhanced RTMP v1 video framing (FourCC HEVC / AV1 / VP9)

Fixed

  • Client teardown no longer truncates in-flight A/V frames
    (src/client.rs). RtmpClient::close previously did
    TcpStream::shutdown(Shutdown::Both) immediately after writing the
    closeStream command. Closing the read half at the same instant lets
    the kernel answer the peer's still-unacked data with a RST on some
    platforms, which discards any audio/video messages the peer hasn't yet
    drained from its receive buffer — so the last frames plus closeStream
    could vanish mid-stream. close now shuts down only the write half
    (Shutdown::Write, a graceful FIN); the peer drains every buffered
    frame and our closeStream command before observing EOF. This
    resolves the intermittent loopback_publish failure (server saw 2 of
    4 video tags) that surfaced on fast Linux CI runners.

Added

  • Enhanced RTMP v2 ModEx prelude (src/flv.rs). flv::parse_video
    / flv::build_video and flv::parse_audio / flv::build_audio now
    decode and re-emit the ModEx packet-type prelude chain per
    enhanced-rtmp-v2.pdf §"ExVideoTagHeader" / §"ExAudioTagHeader" (the
    while (packetType == ModEx) loop). When the PacketType nibble of the
    header byte is ModEx (7 for video, 7 for audio), a chain of
    size-prefixed entries precedes the FourCC: each entry is a
    modExDataSize (UI8 + 1, escaping to a 0xFF sentinel + UI16 + 1
    for 256..=65536 bytes), the modExData bytes, and a single byte whose
    high nibble is the modExType (UB[4]) and whose low nibble is the
    next PacketType (UB[4]) — looping until a non-ModEx PacketType
    terminates the chain. New flv::ModEx { mod_ex_type, data } captures
    each entry; new VideoTag::mod_ex / AudioTag::mod_ex fields hold the
    ordered chain and round-trip it verbatim ahead of the real packet
    type. The only mod_ex_type defined today is
    TimestampOffsetNano = 0 (a bytesToUI24 0..=999_999 ns
    sub-millisecond presentation offset); ModEx::timestamp_offset_nano,
    ModEx::timestamp_offset_nano_entry, and
    VideoTag::timestamp_offset_nano / AudioTag::timestamp_offset_nano
    expose it. Crucially, after parsing, ex_packet_type holds the real
    PacketType recovered from the chain (not ModEx), so the
    video_to_packet / audio_to_packet adapters route a ModEx-prefixed
    tag to the correct CodecId + packet flags transparently — previously
    the header's 7 nibble would have been mis-read as an unknown
    PacketType and the chain bytes mistaken for the FourCC. New public
    constants EX_PACKET_TYPE_MOD_EX, EX_PACKET_TYPE_MULTITRACK,
    MOD_EX_TYPE_TIMESTAMP_OFFSET_NANO; flv::ModEx re-exported at the
    crate root.
  • 9 new tests (8 unit in src/flv.rs, 1 integration in
    tests/enhanced_rtmp_video.rs) cover: video + audio
    TimestampOffsetNano single-entry round-trips, a two-entry chain
    (TimestampOffsetNano + an unknown subtype preserved verbatim), the
    UI16 size escape (300-byte modExData), the accessor rejecting the
    wrong subtype / short data, controlled-failure on truncated chains
    (missing data / nibble / FourCC) for both audio and video, byte-exact
    no-prelude output for an empty mod_ex, and a full
    ModEx-wire-bytes → parse_videovideo_to_packet → CodecId
    resolution + build_video round-trip proving the prelude is
    transparent to the adapter.
  • AMF3 data / command message routing (src/amf3.rs,
    src/server.rs, src/client.rs). Wires the r93 AMF3 parser into the
    RTMP message-dispatch path so AMF3-encoded onMetaData /
    data-messages (message type id 15) and AMF3 commands (type 17) decode
    end-to-end. Per AMF 3 spec §4.1 + AMF 0 spec §3.1, the outer
    NetConnection message structure is AMF0 and a value switches to AMF3
    via the avmplus-object-marker (0x11); new
    amf3::decode_data_message parses a type-15/17 body that is either
    0x11-prefixed (the spec-mandated switch) or already-AMF3
    (no-prefix, for channels negotiated to AMF3 from the start), sharing
    one reference-table context across the whole body. New
    Amf3Value::to_amf0() bridges the decoded AMF3 value graph onto the
    Amf0Value enum so server::RtmpSession::next_packet surfaces AMF3
    metadata through the same StreamPacket::Metadata(Amf0Value) path as
    AMF0 — Integer/Date collapse to Number/Date, sealed +
    dynamic object members concatenate into one ordered Object, the
    AMF3 Array dense slot becomes an ECMA-array under stringified
    ordinal keys, and ByteArray/Vector/Dictionary/Xml* map to
    their nearest AMF0 shape. The server's MSG_DATA_AMF3 /
    MSG_COMMAND_AMF3 arms now route (the AMF3 command path detects the
    same closeStream / deleteStream / FCUnpublish teardown as
    AMF0). New RtmpClient::send_metadata_amf3 emits an AMF3-encoded
    onMetaData for peers on an AMF3 channel. pub const amf3::AVMPLUS_OBJECT_MARKER; Amf0Value / Amf3Value re-exported
    at the crate root. This resolves the r93 follow-up noted below.
  • 9 new tests: 4 unit tests in src/amf3.rs cover decode_data_message
    framing (avmplus-wrapped sequence, unprefixed-AMF3, shared reference
    context, dangling-marker error); 5 cover the to_amf0 bridge
    (scalars with Integer/Date collapse, sealed+dynamic merge ordering,
    Array→ECMA ordinal keys, Vector/ByteArray→StrictArray, and a full
    realistic onMetaData body bridged into an AMF0 object). A new
    tests/amf3_metadata.rs integration test drives a full
    client→server loopback publishing an AMF3 onMetaData and asserts
    the server surfaces every field through StreamPacket::Metadata.
  • AMF3 wire-format parser + builder (src/amf3.rs). Implements the
    full Adobe "Action Message Format -- AMF 3" (January 2013)
    specification mirrored under docs/streaming/rtmp/amf3-file-format- spec-adobe.pdf: all thirteen value markers (Undefined / Null /
    False / True / Integer / Double / String / XMLDocument / Date /
    Array / Object / XML / ByteArray / Vector{Int,UInt,Double,Object} /
    Dictionary), U29 variable-length integers (§1.3.1) with explicit
    sign-extension for the Integer marker (§3.6), and the three
    reference tables (strings / objects / traits) maintained per
    decode_all invocation per §2.2. Object support distinguishes
    anonymous / typed / dynamic / externalizable shapes (§3.12);
    externalizable bodies surface as Some(Vec<u8>) on the
    Amf3Value::Object::externalizable_body field for round-tripping,
    with generic decode refusing externalizable inputs (no class
    handler registered) rather than silently corrupting pos.
    Decoder::reset_tables() provides the §4.1 packet-boundary reset.
    Encoder always emits literal (non-reference) values — the wire
    bytes remain valid per spec, and any literal can re-enter the
    decoder which will resolve references encountered later in the
    same payload. New helpers anon_object / dynamic_object /
    anon_object_unordered mirror the AMF0 builder ergonomics.
  • New 26 unit tests in src/amf3.rs exercise: U29 length-class
    boundaries (1-byte, 2-byte, 3-byte, 4-byte) and a spec-Table-1
    canonical-bytes check for 0x7F / 0x80 / 0x4000 / 0x200000; all
    simple-marker (Undefined / Null / Boolean) round-trips;
    Integer sign extension at the negative boundary plus the
    out-of-range fallback to Double; literal-then-reference for both
    string and object tables; empty-string-never-in-table per §1.3.2;
    Date / ByteArray round-trips; dense + associative Array shapes;
    anonymous, dynamic, and typed-with-sealed-and-dynamic Object
    shapes; externalizable-without-handler refuses cleanly;
    Vector.<int> / <uint> / <Number> / <Object> (mixed-type)
    round-trips; Dictionary with both String and Integer keys;
    Xml / XmlDocument round-trips; multi-value packet sharing the
    string table across values; dangling-reference rejected; unknown
    marker rejected; trait reference re-used between two consecutive
    typed-object encodings; and object reference resolving to the
    same Date value.

Notes

  • AMF3 message routing via the AMF0 avmplus-object-marker (0x11) is
    now wired into the server's message dispatch (see the r96 entry
    above) through amf3::decode_data_message + Amf3Value::to_amf0,
    rather than by extending amf::Amf0Value with a wrapping variant.
    The standalone amf::decode path still consumes pure AMF0 only;
    AMF3-channel callers use the amf3 module (directly or via the
    server / client routing) — the cleaner split given AMF3's
    per-message reference-table context.

  • Enhanced RTMP v2 video FourCC additions (Veovera 2026).
    flv::parse_video / flv::build_video now recognise the three
    new VideoFourCc values from enhanced-rtmp-v2.pdf
    §"Enhanced Video": vp08 (VP8 — VPCodecConfigurationRecord
    for SequenceStart, no SI24 on the wire), avc1 (FourCC-mode
    AVC/H.264 — AVCDecoderConfigurationRecord for SequenceStart,
    SI24 compositionTimeOffset on the wire for CodedFrames and
    implied-zero for CodedFramesX, mirroring the legacy AVC path),
    and vvc1 (VVC/H.266 — VVCDecoderConfigurationRecord for
    SequenceStart, SI24 on the wire for CodedFrames and
    implied-zero for CodedFramesX, parallel to HEVC's row). The
    parse-side needs_cts rule and the build-side cts_on_wire
    rule are widened symmetrically: the three NALU-based FourCCs
    (hvc1 / avc1 / vvc1) all carry the SI24 with
    CodedFrames; the non-NALU FourCCs (av01 / vp09 / vp08)
    and the SequenceStart / SequenceEnd / Metadata / CodedFramesX
    shapes never do.

  • **Fo...

Read more

v0.0.3

03 May 06:15
badc05c

Choose a tag to compare

Other

  • replace never-match regex with semver_check = false
  • migrate to centralized OxideAV/.github reusable workflows
  • SourceRegistry PacketSource for rtmp:// URIs
  • pin release-plz to patch-only bumps

Added

  • SourceRegistry integration via register(registry). New
    RtmpPacketSource adapter implements
    oxideav_core::PacketSource, wrapping an RtmpSession and
    emitting oxideav_core::Packets on stream 0 (audio) and
    stream 1 (video) with TimeBase 1/1000 (RTMP's native
    millisecond unit). Codec ids resolved from the first audio +
    video tags via audio_codec_id / video_codec_id
    (aac/mp3/pcm_*/speex/nellymoser for audio,
    h264/h263/vp6f/vp6a/flashsv/flashsv2 for video).
    AVC composition-time offsets are applied to PTS;
    sequence-header packets carry the header flag and
    AVCDecoderConfigurationRecord / AudioSpecificConfig in
    CodecParameters::extradata. Listen-style opener:
    rtmp://host:port/app/stream-name URIs bind a one-shot
    listener that accepts one publisher and validates the
    announced app + stream-name against the URL path. The
    historical RtmpServer::accept / RtmpClient::connect API is
    unchanged — registry support is purely additive.
  • New integration test (tests/packet_source.rs) round-trips a
    synthetic publisher → registry opener → PacketSource flow,
    asserting stream descriptors, packet ordering, pts/dts, and
    the header / keyframe flags.

v0.0.2

25 Apr 07:41
d563e4d

Choose a tag to compare

Other

  • release v0.0.1 (#1)

v0.0.1

19 Apr 21:31

Choose a tag to compare

chore: Release package oxideav-rtmp version 0.0.1