A fix-up for 4fbef394
Since that commit, the printer adds the `mid` from the egress media
extmap even when the input had none and the older bug (with the missing
room in `codec_add_dtmf_packet()`) started corrupting packets.
Just reserve the extension room in the same way the `codec_add_raw_packet_dup()`
and the `packet_encoded_packetize()` already do.
An example of bug triggered: chrome sending the DTMF with no extensions.
Change-Id: I87a61e1f87e896609e76bbdf87b4c7827027f39d
(cherry picked from commit f950a9f7be)
(cherry picked from commit b05488fd5d)
Replace spinning for websocket_service_lock with a short mutex-protected
writable queue. All lws_callback_on_writable() calls are now marshalled
onto the LWS service thread and only run after lws_service() returns,
instead of being issued directly from worker threads while the service
thread may be holding the same lock. The callback queue mutex is
released before calling into libwebsockets, and the amount of queued
work drained per service cycle is bounded to avoid starving other
connections under a connection-abort storm. Pending callbacks are
dropped on teardown before the LWS session storage is freed.
Add an HTTP disconnect-storm regression test (two worker threads
hammering /metrics with aborted connections while a liveness /ping
request keeps proving the service thread is not stuck) to
t/auto-daemon-tests-websocket.py, plus a standalone stress script. The
stress test waits for the listening socket to come up before firing,
avoiding a FileNotFoundError race on CI.
Closes#2206
Change-Id: I81e03c1d7f1a0b5650aa719d00d60be3d3d0213b
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit beb1a204da)
(cherry picked from commit 13d5e98070)
The decoder output frame was hard-coded to 960 samples, which is 20 ms
at 48 kHz. libopus refuses to decode a packet that does not fit the
given frame size, so every 40 ms and 60 ms packet (both ptimes accepted
by opus_init) failed with "buffer too small" and was dropped.
Size the buffer for 120 ms, the longest duration an Opus packet can
carry, at the decoder's clock rate.
Closes#2224
Change-Id: Ic2fcb32977ad0e262d42b74948103b0f68511d95
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 47cd1458a7)
(cherry picked from commit 8591ed023a)
When the negotiated fmtp has a bitrate range (br=A-B), the encoder's
lower bound check compared the bitrate against max_br instead of
min_br. Any bitrate below the upper bound was therefore forced down
to the minimum, e.g. br=9.6-24.4 with the default 16.4 kbit/s ended
up encoding at 9.6 kbit/s instead of staying at 16.4.
Compare against min_br so the bitrate is only raised when it is
actually below the lower bound.
Closes#2223
Change-Id: I8bfaff332476e351e0b7a6a375a9c55bb3fa772f
(cherry picked from commit 0402477b96)
(cherry picked from commit e9d9792bd9)
stats->ext_seq has been the SRTP receive index since srtp_index moved into
shm (08c7f5853d). The transcode sequencer overwrote it after every
emitted packet with its own roc<<16|seq, which lags the received packets
and, whenever the sequencer emits a late or replayed packet with a lower
sequence number, carries a roc that was incremented without a real
wrap-around. packet_index() then estimated the wrong index, and once the
sender's real roc reached 1 none of the fallback guesses matched, so every
packet of the stream failed authentication until the call ended.
packet_index() already maintains the index for every received packet, so
drop the write. Also make the sequencer's own roc increment only on a
forward wrap-around, never on a backwards step.
Closes#2221
Change-Id: Ia38bf8cbc63dd3435717927f1adfd81ac94ea347
(cherry picked from commit 956d7add09)
(cherry picked from commit dd5c0f7d87)
Ref #2211
cli_handle() takes a str, but most "set" handlers parsed their value
with strtol(), strtod() or atoi() on instr->s and printed it with "%s",
reading up to the next NUL. The ng "cli" command passes a slice of the
request: "set maxsessions 1000" set 10007, "set rediscmdtimeout 0" set
7 ms, and error messages quoted the rest of the request.
Parse with the str_to_X helpers, plus a new str_to_d for maxcpu and
maxload, and print with STR_FORMAT. As with the str_to_X family, a
value with trailing characters ("10x") is refused, and so is a
non-number in the seven handlers that read it as 0. "terminate" now
matches all, own and foreign exactly (str_cmp()), not as a prefix.
Tested by auto-daemon-tests-redis-errors.pl and the new t/test-str.c.
Change-Id: I92ed33a4229688aba513c3d0410b9d77a8bcd516
(cherry picked from commit f54da0bcec)
(cherry picked from commit 562a0e0208)
Use epoll's u64 field
Change-Id: If56e980b2d65cc0e32736c58a12cad1000628f2a
(cherry picked from commit c6508f4fd9)
(cherry picked from commit f03c19febe)
Currently unused and transparent
Change-Id: I35604c9d5f724ae6a3bce714d708bed0fbed563f
(cherry picked from commit b2cf472a20)
(cherry picked from commit bc6a35656c)
Ref #2211
When json_restore_call() fails, it sends "DEL <call-id>" twice: on the
write connection, in its own DB, and on the notification connection, in
whatever DB the last keyspace notification selected. For a foreign call
(a keyspace notification, or the restore of a subscribed keyspace at
startup) that key belongs to the instance that owns the call, which
keeps writing it: a standby that fails to restore one record deletes the
owner's copy, and the call cannot be taken over any more. For an own
call, the DEL on the notification connection can hit a peer's DB.
Only delete an unrestorable record when the call is our own, and only on
the write connection, in our own DB. A foreign record that can never be
restored now stays until its owner rewrites or deletes it, or until it
expires (--redis-expires). Seen in a lab with Redis Sentinel, where a
subscriber's DEL was refused only because its node had just become a
replica.
Tests, in auto-daemon-tests-redis-subscribe.pl: a keyspace notification
for a foreign call whose record cannot be parsed is followed by no
command on either connection, and an own record that cannot be parsed at
startup is deleted once, on the write connection only. Without this
change both get a DEL on both connections.
Change-Id: I5d8a53f3515596cf3fc2c58ea97847476385b876
(cherry picked from commit 6b3bf8299b)
(cherry picked from commit fef185612e)
Ref #2211
redis_set_timeout() receives the timeout in microseconds (the
--redis-cmd-timeout value times 1000) and logged it unconverted, so
--redis-cmd-timeout=1000 was reported as:
Setting timeout for Redis commands to 1000000 milliseconds
Log the value in milliseconds, as the message says. The value passed to
hiredis is unchanged.
No functional change. Seen in the log of every connection made with a
command timeout set; there is no test, as no test reads this line.
Change-Id: Ie4d81e4f8baab1969210a589c98245407b088561
(cherry picked from commit 1c447b509b)
(cherry picked from commit c6a6ee320f)
Ref #2211
redis_connect() finds "role:" in the INFO reply and compares what
follows with memcmp(): 11 bytes for "role:master", 19 for
"role:active-replica" and 10 for "role:slave". memcmp() may read all n
bytes, so a reply that ends less than 19 bytes after "role:" (for
example a replica's "role:slave\r\n" with nothing after it) is read
past its end. Real Redis and KeyDB servers send many more lines after
the role, so this does not show in practice, but AddressSanitizer
aborts on it (heap-buffer-overflow in the memcmp interceptor) with a
minimal reply, such as one from a test server or a proxy.
Use strncmp(): hiredis NUL-terminates the reply string, so the compare
stops at its end.
Tested by auto-daemon-tests-redis-errors.pl, added later in this
series, which answers a reconnect's INFO with just "role:slave": an
ASan build aborts there without this change. The change is early in
the series so that it can be taken on its own.
Change-Id: Icdf3d3a23464d861f57406436328d0b166794362
(cherry picked from commit e5ff3ea8af)
(cherry picked from commit 880136b868)
Ref #2211
"set rediscmdtimeout 0" reconnects the Redis connections, but each of
the three calls is guarded by rtpe_redis instead of its own pointer:
- with --redis and no notification connection (no --redis-subscribe and
no subscribed keyspace), redis_reconnect(rtpe_redis_notify) locks a
NULL mutex and rtpengine crashes;
- with --redis-write and no --redis, the write connection is never
reconnected, so its command timeout is never cleared.
Guard each call with its own pointer, as the non-zero branch already
does.
Tested by auto-daemon-tests-redis-errors.pl, added later in this
series ("set rediscmdtimeout 0" over the ng "cli" command with --redis
only): without this change rtpengine crashes and never replies. The
change is first in the series so that it can be taken on its own.
Change-Id: Ia458a6eb0b70a6906ab410fd91087ad5af581f3b
(cherry picked from commit 20bcdce73c)
(cherry picked from commit d2860e0b8d)
Reported-by: Matthew White <on3moda@gmail.com>
Change-Id: I2404f379e42ed29d0d6b95863ad40364687c205f
(cherry picked from commit e34a892daf)
(cherry picked from commit 5ef15fcfc3)
monologue_publish() never copied the offered label into the media,
so sdp_create() omitted a=label from the publish answer and from
subscribe requests for that publisher. Add the missing
media_update_label() call.
Closes#2200
Change-Id: Id9aaf4ab09c21f98d3dcaf78632413b99f4a97ff
(cherry picked from commit b66c59bed6)
(cherry picked from commit 472c915f39)
Has the benefit of not needing to write to the buffer
Change-Id: I5897d4fcd7a37e143bceb5569b9bfa1237593b3e
(cherry picked from commit bc08b05912)
(cherry picked from commit a73ce2f128)
The kernel tracks lost packets per SSRC through the `lost_bits` window
in the shared ssrc_stats, which was left zeroed, while userspace starts
its own window as all-received. As userspace already advances `ext_seq`
before a stream is kernelised, the kernel then counted up to 32 packets
it did receive as lost, both per SSRC and in the interface and global
packets_lost counters. Start the shared window as all-received too.
Change-Id: I3d6cd85a1b764a6bb3296daf7489091767449554
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3b73432826)
(cherry picked from commit 9fc1340070)
ssrc_collect_metrics() converted the running RFC 3550 jitter estimate
to milliseconds in place on every call timer run with --measure-rtp,
dividing it by clock_rate/1000 each time. The estimator is kept in
RTP timestamp units and also feeds generated RTCP RRs, which therefore
reported a jitter decaying towards zero. Convert a copy instead.
The RTCP test expected the resulting zero jitter; it now gets the
value actually measured from its back-to-back packets.
Change-Id: Ice29029322764fd76374eb3f7dda517570f7f75b
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4dd1826ffa)
(cherry picked from commit 3b9407818d)
Copy create-answer and subscribe-answer SDP into call-owned storage before parsing, matching offer/answer and publish. Retained stream parameters otherwise reference released NG request buffers and later subscriptions can lose their media type and codecs.
Initialize created-answer monologue session state so generated subscriptions retain a valid SDP origin and session name. Add request-buffer-churn and decoded-audio regressions for created and subscribed endpoints.
Validation on standalone Sipwise master: daemon build, all 89 retained-SDP assertions, all 652 create assertions, and all 932 publish/subscribe assertions pass. The cumulative 26.2.1 release also has isolated browser Opus decoding, privacy, recording, and mixer regression coverage.
Closes#2194
Change-Id: Id8ca0e0ff691356d905ee94c0f41090f973fc70f
(cherry picked from commit 12ecdbc2cf)
(cherry picked from commit e0960d121e)
Update media player, send timer, jitter buffer objects.
Set final memory arena pointer as the releasing the last reference might
reset it to null.
Exclude media_player stuff from no-transcoding builds and adjust ifdef
guards accordingly.
Closes#2186
Change-Id: I42dc5a449816d765bc4d1da2b35315e0b6f7c29b
(cherry picked from commit 2366392202)
(cherry picked from commit 2ee9247bb7)
After a takeover, FOREIGN_MEDIA holds off the media timeout until the
call receives media. The flag is cleared by the first packet that
arrives on any of the call's sockets, before the packet is checked, so
a packet that is then dropped, for example because of strict-source,
ends the grace period without refreshing the stream's last packet time.
A call that was restored longer than the timeout ago is then closed on
the next timer run, before its real media has had a chance to arrive.
With the peer address confirmation now restored from Redis, this is
what happens to an adopted strict-source call when a stray packet
arrives ahead of the peer's first one.
Clear the flag only once a packet has passed the address check. The
timer still clears it for kernel-forwarded media, and the grace period
stays bounded by the timeout after the takeover.
Ref #2184
Change-Id: Ib06a170d271e8aea709abc57cbfd3bcc59196ab5
(cherry picked from commit 1d783ba1b9)
(cherry picked from commit 415c6293d5)
Should make more sense there
Change-Id: I5a16d42510ee93e72f8cf605d9463e220b53aa6b
(cherry picked from commit 4751218ce5)
(cherry picked from commit b4f6b6f5fe)