RTP header extensions are only processed, and unknown ones stripped, if
at least one side of the call negotiated header extensions through
`a=extmap` attributes. Otherwise RTP is forwarded untouched, including
any header extensions an endpoint may include without having announced
them.
Add a `force strip extmap` flag which selects header extension
processing for all streams of a call regardless of whether any
extensions were negotiated, so that unannounced header extensions are
removed. This is useful when forwarding media from sources that always
include header extensions towards endpoints that do not cope with them.
Change-Id: I57e14389c3610550a6859ac08e65ad866283554f
Define types of packetizers, with dedicated init and destroy functions,
and move maintenance of sample stream into encoder
Change-Id: I992f696c2b986485a89582909e299b2f6d462e5b
Add an opt-in rollback message to the NG protocol. It lets a signalling
application undo an SDP offer that rtpengine has applied but the remote
endpoint subsequently rejects, without deleting the established call.
A client enables checkpointing with track-state on an offer. rtpengine
snapshots the affected dialogue before applying it; an answer commits the
exchange and discards the snapshot, and rollback restores and consumes it.
A dialogue holds at most one outstanding checkpoint, so offers arriving
before an exchange completes keep the existing snapshot and a rollback
returns to the last completed offer/answer.
A checkpoint holds a snapshot in the call record format the Redis encoder
already produces, so there is one definition of what a call's state looks
like and no second encoder to keep in step. Decoding a snapshot needs the
field readers the Redis restore path already had, so those are shared
rather than duplicated; json_restore_call() reads the same fields, in the
same order, with the same strict-return behaviour.
Applying a snapshot copies negotiated state back onto the live objects,
matched by unique id. The objects are never recreated, so local sockets
and ports survive. State a rejected offer introduced is removed as well as
overwritten: a field the encoder writes only when set is absent from a
snapshot taken before it existed, and that absence clears it.
ICE and DTLS reconverge rather than being rewound, since applying the
offer has already reset the agent and shut down the DTLS association.
Restoring the accepted credentials and candidates lets connectivity checks
rebuild ICE state, and restoring the fingerprint, TLS id and setup permits
a fresh handshake.
Outstanding checkpoints are stored in the Redis call record so they
survive takeover. Checkpoint data is auxiliary: one that cannot be read is
discarded in full while the call itself is restored without rollback
capability.
The preload shims no longer inherit the sanitizer options from CFLAGS.
They are loaded by the harness into the test scripts' own interpreter,
where a sanitizer runtime crashed the daemon tests on aarch64 during
library initialisation. Everything else in CFLAGS, the Debian hardening
flags included, still applies to them.
The Redis tests no longer assume the record length is four digits, and
read the record across as many reads as it takes.
Squashed:
commit ad49459152
Author: Daniel Donoghue <oneofsomany@hotmail.com>
Date: Fri Aug 28 13:33:17 2026 +0200
MT#55283 address review comments
Snapshots are now bencode, GLib macros are used throughout, and keys and
offsets are built with the existing helpers rather than by hand.
New tests cover the snapshot format and the state that exists only inside
a snapshot.
commit ff47944ad5
Author: Daniel Donoghue <oneofsomany@hotmail.com>
Date: Fri Aug 28 15:24:56 2026 +0200
MT#55283 scope snapshots to the dialogue
A snapshot now holds only the two monologues being checkpointed, and only
the entries the decoder reads: the socket and endpoint map pools, sinks,
subscriptions and aliases are left out.
The from-tag check in the rollback handler is dropped, since
call_get_monologue() is keyed on the tag.
commit 3bb19f2804
Author: Daniel Donoghue <oneofsomany@hotmail.com>
Date: Mon Aug 31 08:41:08 2026 +0200
MT#55283 give each monologue its own checkpoint
The checkpoint moves from a list on the call to a pointer on the monologue,
and its snapshot covers only that monologue. Checkpoints are stored in the
call record as checkpoint-<monologue id>; num_checkpoints and the offerer
and answerer ids are gone.
A monologue is shared between the branches of a forked call, so rolling one
branch back no longer reinstates what rolling another back had undone.
The call-level json dict is no longer written into snapshots, since nothing
reads it back.
commit 0c986a81e2
Author: Daniel Donoghue <oneofsomany@hotmail.com>
Date: Mon Aug 31 10:01:54 2026 +0200
MT#55283 do not report a rollback that did not happen
call_merge() renumbers every unique id, and a snapshot is keyed on them, so
a checkpoint taken before a merge no longer describes anything. It is
dropped with the ids it refers to, and rollback reports none outstanding
rather than a success that restored nothing.
Both sides of a dialogue are checkpointed together. A monologue is shared
between the branches of a forked call, so one side could already hold a
checkpoint while the other had never been tracked, leaving a rollback to
restore half a dialogue and still report success.
Each fix has a test that fails without it.
commit dfe7a72aff
Author: Daniel Donoghue <oneofsomany@hotmail.com>
Date: Mon Aug 31 10:41:50 2026 +0200
MT#55283 document that merging a call drops its checkpoints
Merging renumbers the state a snapshot refers to, so an outstanding
checkpoint is discarded and a later rollback reports none.
Closes#2159
Change-Id: Ie773e2cd4a39fc1c8f39fafe959784e269c0c80e
Forcing the DTLS role to passive only makes sense for the backwards
(torwards the offerer) direction anyway, as towards the answerer (in an
offer) it's not allowed by the RFC, and in an answer it's not possible
as the active DTLS connection would already be in progress.
Convert DTLS= processing into a list as multiple flags might be
required.
Change-Id: I54239280460e1bbce1966bc17a384489eb978618
Amends: 78ee0451 / Ib1d07831
Related: #2156
Related: #2145
Make both `media_player_opts_t.db_id` and `media_player_content_index.db_id`
unsigned because:
- they aren't used with any guards/init values
- most DB-media API functionality actually uses `unsigned long long`
So this makes the ids consistent with the usage of them
in the actual code.
Change-Id: I6c756c537b3c992099a6857683078c169d5e030d
Make this sentinel visible via header, because anywhere
where the `kernel_idx` is declared/used,
this won't be usable due to limited visibility to .c file.
Change-Id: I159fe1a87e92656fdd790dd98569cfebe80d4ddd
So that their naming is self-evident and correlates
with the actual options flag:
- `media-files-expire` -> `media_files_expire_us`
- `db-cache-expire` -> `db_cache_expire_us`
- `db-media-expire` -> `db_media_expire_us`
Change-Id: I76ebe9f92ba22de7c19fbb2ff47d0bf1952e1480
Introduce a common NG commands table `ng_command_defs`
for all type of things: OP enum, name, escaped name,
short name and most important the handler.
This table operates on and returns a dedicated struct `ng_command_def`,
which has all the information required on the command (like
all type of things mentioned above) and may have either
single-parameter or double-parameter signature (for cases with
the `addr` requiring commands such as the offer command).
Also add unified signatures for X and XA handlers,
they will be used later accordingly. Most of those use single-parameter,
and those exceptional use the second one.
Also add dummy plug-functions for ping and block/unblock silence media.
The ping one doesn't have any particular handler, but because a new
structure of NG commands wants to see a real declaration of the func
handler, just give it. For the block/unblock silence media, there aren't
even handling of commands in the control NG, they are just dummy OPs,
so fulfill the actual NG commands structure, and give them dummy funcs.
Change-Id: I2064ddca1595079959a6c6843119556a8b6bf5d5
Keep enum `ng_opmode` generated from the command list,
and also keep `ng_command_strings[]`, `ng_command_strings_esc[]`
and `ng_command_strings_short[]`.
And add the wrapper func name into the array, to be able
to appeal to it later, when processing any coming NG command.
Change-Id: I6aa725679d2405277f4497db46c3a8010d9ccd56
The `OP_...` enum is meant to only represent commands list.
Then make the list from the NG commands macro
the actual source of truth for the enum count.
This eliminates human mistakes, when introducing a new command,
because no more need to update enum manually.
Additionally:
- move the command list macro to the header
- existing extern declarations remain untouched
- `OP_OTHER` remains sentinel
- eventually replace the manual enum with macro based one
Change-Id: I33d928367fe0a254571cb0b7d4aafe4445b30b77
Resolve file name conflicts between different components
Ensure shared log levels are identical across components
Encapsulates lib/*
Change-Id: If848d6fa0e4cd8eac88ce9e63ee77fc5a7a481fd
Expression like macroses should usually be
wrapped in parentheses.
Then they behave predictably, when for example
embedded into larger expressions,
and also so future edits do not accidentally introduce precedence bugs.
Change-Id: I4ac485c2e1ed58507f4efeec69de63a6080523c2