Skip to content
Closed
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
48cf18a
TEL-6738: Add RFC9143 BUNDLE v2 design note
baloeng Apr 28, 2026
38c2791
TEL-6738: Add RFC9143 BUNDLE negotiation groundwork
baloeng Apr 28, 2026
f518daf
TEL-6738: Protect shared BUNDLE RTP transport ownership
baloeng Apr 28, 2026
34a7562
TEL-6738: Runtime gate MID RTP extension writes
baloeng Apr 28, 2026
ba45e08
TEL-6738: Add BUNDLE RTP demux helpers
baloeng Apr 28, 2026
d1c3c51
TEL-6738: Add RTP MID receive parsing groundwork
baloeng Apr 28, 2026
59353fe
TEL-6738: Demux bundled RTP receive path
baloeng Apr 30, 2026
ec017c5
TEL-6738: Address BUNDLE RTP review blockers
baloeng Apr 30, 2026
c2e459d
TEL-6738: Fix BUNDLE unit test build
baloeng Apr 30, 2026
2843629
TEL-6738: Fix BUNDLE unit C90 warnings
baloeng Apr 30, 2026
61ead47
TEL-6738: Guard video helper null reads
baloeng Apr 30, 2026
1079667
TEL-6738: send bundled video with video SSRC and MID
baloeng Apr 30, 2026
dffa06d
TEL-6738: Isolate bundled RTP video write state
baloeng May 1, 2026
cae673a
TEL-6738: Reset RTP extension state before MID writes
baloeng May 1, 2026
1cc9862
TEL-6738: Stabilize bundled video RTP timestamps
baloeng May 1, 2026
cad81e3
TEL-6738: Restore bundled video writes over audio RTP
baloeng May 4, 2026
7c0c3b8
TEL-6738: Add video bridge diagnostics
baloeng May 5, 2026
53b232f
TEL-6738: Preserve generated video RTP timestamps
baloeng May 5, 2026
5ff9625
TEL-6738: Normalize external video RTP timestamps
baloeng May 5, 2026
a9b53b1
TEL-6738 fix BUNDLE video RTP timestamp and MID forwarding
baloeng May 6, 2026
efe2c8c
TEL-6738 add video refresh diagnostics
baloeng May 13, 2026
48e70bf
TEL-6738 fix BUNDLE video RTCP feedback (SSRC/stats/skip) and add vis…
May 15, 2026
361ac65
Merge telnyx/telephony/deploy-development into jira-tel-6738-bundle-v2
May 15, 2026
37cffb3
TEL-6738 fix RTP MID extension drops on BUNDLE audio + bridge legs
May 15, 2026
cc27fa9
TEL-6738 Guard 1: strip inbound BEDE extension on BUNDLE audio skip
May 15, 2026
71188fb
TEL-6738 revert Guard 1 BUNDLE audio MID skip (regression fix)
May 15, 2026
4ff802e
TEL-6738 Phase 5+5b: fix inbound RTCP FIR/PLI receive + partner propa…
May 16, 2026
5c894e2
TEL-6738 buffer early BUNDLE video before DTLS ready
baloeng May 18, 2026
d23ce4e
Merge remote-tracking branch 'origin/telnyx/telephony/deploy-developm…
baloeng Jun 24, 2026
bae80ce
TEL-6738: drain open BUNDLE video frame before interleaving audio
baloeng Jun 25, 2026
6e9f3d6
TEL-6738: fix C90 declaration-after-statement for audio_defer_deadline
baloeng Jun 25, 2026
5a17b24
TEL-6738: defer BUNDLE audio until video frame closes (frame-boundary)
baloeng Jun 26, 2026
d3ad215
TEL-6738: fix NULL packet crash in pre-DTLS BUNDLE video flush
baloeng Jun 26, 2026
6af480c
TEL-6738: stamp correct PT on BUNDLE audio writes (shared send_msg co…
baloeng Jun 26, 2026
0b009ea
TEL-6738: use frame->payload (not rtp_session->payload) for BUNDLE au…
baloeng Jun 26, 2026
478641c
TEL-6738: capture local audio PT at BUNDLE setup, use as authoritativ…
baloeng Jun 26, 2026
e491063
TEL-6738: Add VP8 payload-integrity CRC probes on video forward path
baloeng Jun 27, 2026
51a7845
rtp: send video RTCP feedback during RTCP warmup window
baloeng Jun 27, 2026
9273be5
TEL-6738: buffer pre-DTLS video on non-BUNDLE write path
baloeng Jun 27, 2026
1270922
TEL-6738: request source keyframe when non-BUNDLE pre-DTLS buffer flu…
baloeng Jun 27, 2026
0c6c93f
TEL-6738: force-bypass 1s video-refresh throttle for first post-conne…
baloeng Jun 27, 2026
bda4938
TEL-6738: fix pre-DTLS buffer re-arming after DTLS ready (non-BUNDLE)
baloeng Jun 29, 2026
2c952fd
TEL-6738: fix pre-DTLS buffer to drop-newest on overflow (preserve in…
Jun 29, 2026
ca39a58
TEL-6738: fix post-flush drop guard — re-arm on write failure instead…
Jun 29, 2026
c33e639
TEL-6738: remove orphaned tel6738_enqueue_done label (goto removed in…
Jun 29, 2026
3b53925
TEL-6738: fix bridge FIR propagation — forward refresh to session_b w…
Jun 29, 2026
714c727
TEL-6738 throttle bundle video refresh propagation
baloeng Jun 30, 2026
8f408ec
TEL-6738 Guard video ICE auto-adjust during DTLS init
baloeng Jun 30, 2026
9d0b709
TEL-6738 add VP8 pre-SRTP diagnostics
baloeng Jun 30, 2026
064e551
TEL-6738 drop pre-DTLS video buffer on readiness
baloeng Jun 30, 2026
2d68ad9
TEL-6738 guard pre-DTLS keyframe refresh during teardown
baloeng Jun 30, 2026
e2d2951
TEL-6738 gate startup video until keyframe
baloeng Jun 30, 2026
bfc60cc
TEL-6738 use RTP write-ready for startup gate
baloeng Jul 1, 2026
de88a06
TEL-6738 reduce PLI-relay throttle from 1s to 200ms in video bridge
baloeng Jul 1, 2026
9b3114f
TEL-6738 add DTLS setup passive diagnostic
baloeng Jul 1, 2026
0798563
Merge remote-tracking branch 'origin/telnyx/telephony/deploy-developm…
baloeng Jul 1, 2026
46a44f5
TEL-6738 remove DTLS passive diagnostic
baloeng Jul 1, 2026
5c3447f
TEL-6738 fix BUNDLE SDP and drain queue race
baloeng Jul 2, 2026
0c7b565
TEL-6738 align BUNDLE group with emitted MIDs
baloeng Jul 2, 2026
eb20ee1
TEL-6738 guard BUNDLE audio queue consumers
baloeng Jul 2, 2026
88b5431
TEL-6738 avoid BUNDLE audio frame-buffer dup
baloeng Jul 2, 2026
d2bdef7
TEL-6738 avoid BUNDLE video frame-buffer dup
baloeng Jul 2, 2026
b5d353a
TEL-6738 preserve BUNDLE RTP packet layout
baloeng Jul 2, 2026
7f473b3
TEL-6738 send SIP INFO keyframe request to non-rtcp-fb video callees
baloeng Jul 2, 2026
545f24e
TEL-6738 fix RTP MID unit test port allocation
baloeng Jul 2, 2026
c89e8b9
Revert "TEL-6738 send SIP INFO keyframe request to non-rtcp-fb video …
baloeng Jul 2, 2026
a589eef
TEL-6738 fix RTP header extension test packets
baloeng Jul 2, 2026
50bf4aa
TEL-6738 add BUNDLE data-model unit tests
baloeng Jul 3, 2026
1e66235
TEL-6738 address BUNDLE review comments
baloeng Jul 3, 2026
0b64a9c
TEL-6738 clean up BUNDLE review leftovers
baloeng Jul 3, 2026
5c96b97
TEL-6738 remove noisy BUNDLE debug logs
baloeng Jul 4, 2026
463f835
TEL-6738 remove dead BUNDLE debug counters
baloeng Jul 4, 2026
db2ba3f
TEL-6738 emit audio MID for BUNDLE offers
baloeng Jul 6, 2026
18cebd6
TEL-6738 cover BUNDLE audio MID SDP
baloeng Jul 6, 2026
f2714ef
TEL-6738 run BUNDLE MID regression in switch_sdp
baloeng Jul 6, 2026
b233bbe
TEL-6738 harden BUNDLE RTP read concurrency
baloeng Jul 6, 2026
ec08b9c
TEL-6738 stop raw-forwarding queued BUNDLE audio header extensions
baloeng Jul 6, 2026
c149ab7
TEL-6738 drop peer RTP extensions when stamping MID
baloeng Jul 6, 2026
d18953d
TEL-6738 target MID extension rewrite
baloeng Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ library_include_HEADERS = \
src/include/switch.h \
src/include/switch_apr.h \
src/include/switch_buffer.h \
src/include/switch_bundle.h \
src/include/switch_caller.h \
src/include/switch_channel.h \
src/include/switch_console.h \
Expand Down Expand Up @@ -323,6 +324,7 @@ libfreeswitch_la_SOURCES = \
src/switch_apr.c \
src/switch_apr_queue.c \
src/switch_buffer.c \
src/switch_bundle.c \
src/switch_caller.c \
src/switch_channel.c \
src/switch_console.c \
Expand Down
332 changes: 332 additions & 0 deletions docs/TEL-6738-BUNDLE-v2-design.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/include/switch.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
#include "switch_resample.h"
#include "switch_ivr.h"
#include "switch_rtp.h"
#include "switch_bundle.h"
#include "switch_log.h"
#include "switch_xml.h"
#include "switch_xml_config.h"
Expand Down
86 changes: 86 additions & 0 deletions src/include/switch_bundle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#ifndef SWITCH_BUNDLE_H
#define SWITCH_BUNDLE_H

#include <switch.h>

SWITCH_BEGIN_EXTERN_C

#define SWITCH_BUNDLE_MAX_MLINES 8
#define SWITCH_BUNDLE_MAX_MIDS 8
#define SWITCH_BUNDLE_MAX_MID_LEN 31
#define SWITCH_BUNDLE_MAX_PAYLOADS 32
#define SWITCH_BUNDLE_REJECT_REASON_LEN 128

typedef enum {
SWITCH_BUNDLE_POLICY_OFF = 0,
SWITCH_BUNDLE_POLICY_AUTO,
SWITCH_BUNDLE_POLICY_FORCE
} switch_bundle_policy_t;

typedef enum {
SWITCH_BUNDLE_STATE_NONE = 0,
SWITCH_BUNDLE_STATE_OFFERED,
SWITCH_BUNDLE_STATE_ACCEPTED,
SWITCH_BUNDLE_STATE_REJECTED
} switch_bundle_state_t;

typedef enum {
SWITCH_BUNDLE_DEMUX_NONE = 0,
SWITCH_BUNDLE_DEMUX_MID,
SWITCH_BUNDLE_DEMUX_SSRC,
SWITCH_BUNDLE_DEMUX_PAYLOAD_TYPE
} switch_bundle_demux_t;

typedef struct switch_bundle_mline_s {
int mline_index;
switch_media_type_t media_type;
char mid[SWITCH_BUNDLE_MAX_MID_LEN + 1];
uint8_t in_bundle_group;
uint8_t bundle_tag;
uint8_t bundle_only;
uint8_t rtcp_mux;
uint8_t rtcp_mux_only;
uint8_t rejected;
uint8_t zero_port;
uint8_t local_mid_ext_id;
uint8_t remote_mid_ext_id;
switch_payload_t payloads[SWITCH_BUNDLE_MAX_PAYLOADS];
uint32_t payload_count;
uint32_t local_ssrc;
uint32_t remote_ssrc;
} switch_bundle_mline_t;

typedef struct switch_bundle_group_s {
switch_bundle_policy_t policy;
switch_bundle_state_t state;
uint32_t generation;
char offered_mids[SWITCH_BUNDLE_MAX_MIDS][SWITCH_BUNDLE_MAX_MID_LEN + 1];
uint32_t offered_mid_count;
int bundle_tag_mline_index;
char bundle_tag_mid[SWITCH_BUNDLE_MAX_MID_LEN + 1];
switch_bundle_mline_t mlines[SWITCH_BUNDLE_MAX_MLINES];
uint32_t mline_count;
char reject_reason[SWITCH_BUNDLE_REJECT_REASON_LEN];
} switch_bundle_group_t;

SWITCH_DECLARE(switch_bundle_policy_t) switch_bundle_policy_parse(const char *value, switch_bundle_policy_t dft);
SWITCH_DECLARE(const char *) switch_bundle_policy_str(switch_bundle_policy_t policy);
SWITCH_DECLARE(const char *) switch_bundle_state_str(switch_bundle_state_t state);
SWITCH_DECLARE(void) switch_bundle_group_init(switch_bundle_group_t *group, switch_bundle_policy_t policy);
SWITCH_DECLARE(void) switch_bundle_group_reset(switch_bundle_group_t *group);
SWITCH_DECLARE(switch_status_t) switch_bundle_group_set_offered_mids(switch_bundle_group_t *group, const char *value);
SWITCH_DECLARE(switch_bundle_mline_t *) switch_bundle_group_add_mline(switch_bundle_group_t *group, int mline_index, switch_media_type_t media_type, const char *mid, switch_port_t port, switch_bool_t rtcp_mux, switch_bool_t bundle_only, switch_bool_t rtcp_mux_only);
SWITCH_DECLARE(switch_bundle_mline_t *) switch_bundle_group_find_mline_by_mid(switch_bundle_group_t *group, const char *mid);
SWITCH_DECLARE(switch_bundle_mline_t *) switch_bundle_group_find_mline_by_index(switch_bundle_group_t *group, int mline_index);
SWITCH_DECLARE(switch_status_t) switch_bundle_mline_set_remote_mid_ext(switch_bundle_mline_t *mline, uint8_t ext_id);
SWITCH_DECLARE(switch_status_t) switch_bundle_mline_set_local_mid_ext(switch_bundle_mline_t *mline, uint8_t ext_id);
SWITCH_DECLARE(switch_status_t) switch_bundle_mline_add_payload_type(switch_bundle_mline_t *mline, switch_payload_t pt);
SWITCH_DECLARE(switch_status_t) switch_bundle_group_learn_remote_ssrc(switch_bundle_group_t *group, switch_bundle_mline_t *mline, uint32_t ssrc);
SWITCH_DECLARE(switch_bool_t) switch_bundle_group_payload_type_unique(switch_bundle_group_t *group, switch_payload_t pt, switch_bundle_mline_t **mline_out);
SWITCH_DECLARE(switch_bundle_mline_t *) switch_bundle_group_demux_rtp(switch_bundle_group_t *group, const char *mid, uint32_t ssrc, switch_payload_t pt, switch_bundle_demux_t *method);
SWITCH_DECLARE(switch_status_t) switch_bundle_group_validate(switch_bundle_group_t *group);
SWITCH_DECLARE(const char *) switch_bundle_group_reject_reason(const switch_bundle_group_t *group);

SWITCH_END_EXTERN_C

#endif
5 changes: 5 additions & 0 deletions src/include/switch_core_media.h
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_unlock_video_file(switch_core_
SWITCH_DECLARE(switch_status_t) switch_core_media_set_video_file(switch_core_session_t *session, switch_file_handle_t *fh, switch_rw_t rw);
SWITCH_DECLARE(switch_file_handle_t *) switch_core_media_get_video_file(switch_core_session_t *session, switch_rw_t rw);
SWITCH_DECLARE(switch_bool_t) switch_core_session_in_video_thread(switch_core_session_t *session);
/* TEL-6738: Returns SWITCH_TRUE when this session's video engine shares the BUNDLE
* transport with audio (bundled_with_audio) and that shared audio RTP session is ready.
* Used by writers/bridge to bypass video-specific gates (e.g. CF_VIDEO_READY) on
* a BUNDLE leg whose underlying transport (ICE+DTLS) is already up via audio. */
SWITCH_DECLARE(switch_bool_t) switch_core_media_video_is_bundled(switch_core_session_t *session);
SWITCH_DECLARE(switch_bool_t) switch_core_media_check_dtls(switch_core_session_t *session, switch_media_type_t type);
SWITCH_DECLARE(switch_status_t) switch_core_media_set_outgoing_bitrate(switch_core_session_t *session, switch_media_type_t type, uint32_t bitrate);
SWITCH_DECLARE(uint32_t) switch_core_media_get_orig_bitrate(switch_core_session_t *session, switch_media_type_t type);
Expand Down
21 changes: 19 additions & 2 deletions src/include/switch_rtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,17 @@ struct switch_rtp_crypto_key {
};
typedef struct switch_rtp_crypto_key switch_rtp_crypto_key_t;

typedef struct switch_rtp_write_state switch_rtp_write_state_t;

typedef enum {
IPR_RTP,
IPR_RTCP
} ice_proto_t;

typedef struct ext_mid_s {
uint8_t ext_id; /* negotiated extmap ID for MID (1-14 for 1-byte header) */
char mid[17]; /* MID string (max 16 bytes for 1-byte hdr format) */
uint8_t ext_id; /* negotiated extmap ID for MID (1-14 for 1-byte header) */
char local_mid[17]; /* MID string to write (max 16 bytes for 1-byte hdr format) */
char remote_mid[17]; /* MID string read from the current packet (max 16 bytes for 1-byte hdr format) */
uint8_t enabled;
} ext_mid_t;

Expand Down Expand Up @@ -562,6 +565,8 @@ SWITCH_DECLARE(switch_jb_t *) switch_rtp_get_jitter_buffer(switch_rtp_t *rtp_ses
\param flags the flags to set
*/
SWITCH_DECLARE(void) switch_rtp_set_flag(switch_rtp_t *rtp_session, switch_rtp_flag_t flag);
SWITCH_DECLARE(void) switch_rtp_set_bundle_has_video(switch_rtp_t *rtp_session, switch_bool_t enabled);
SWITCH_DECLARE(void) switch_rtp_set_bundle_video_ssrcs(switch_rtp_t *rtp_session, uint32_t local_ssrc, uint32_t remote_ssrc);
SWITCH_DECLARE(void) switch_rtp_set_flags(switch_rtp_t *rtp_session, switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID]);
SWITCH_DECLARE(void) switch_rtp_clear_flags(switch_rtp_t *rtp_session, switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID]);

Expand Down Expand Up @@ -744,8 +749,16 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_offer_audio_level_extension(switch_co
\param rtp_session the RTP session to write to
\param frame the frame to write
\return the number of bytes written
\note switch_rtp_write_frame_ex_state can use write_state to isolate
seq/timestamp/write-tracking state from rtp_session while sharing the
session socket/security context.
*/
SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_frame_t *frame);
SWITCH_DECLARE(int) switch_rtp_write_frame_ex(switch_rtp_t *rtp_session, switch_frame_t *frame, uint32_t ssrc, uint8_t mid_ext_id, const char *mid);
SWITCH_DECLARE(int) switch_rtp_write_frame_ex_state(switch_rtp_t *rtp_session, switch_frame_t *frame, switch_rtp_write_state_t *write_state,
uint32_t ssrc, uint8_t mid_ext_id, const char *mid, switch_bool_t force_video, switch_payload_t payload_override);
SWITCH_DECLARE(switch_status_t) switch_rtp_write_state_create(switch_rtp_write_state_t **write_state, switch_memory_pool_t *pool);
SWITCH_DECLARE(void) switch_rtp_write_state_reset(switch_rtp_write_state_t *write_state);

/*!
\brief Write data with a specified payload and sequence number to a given RTP session
Expand Down Expand Up @@ -826,7 +839,11 @@ SWITCH_DECLARE(switch_time_t) switch_rtp_session_set_dtls_checks_started(switch_
SWITCH_DECLARE(switch_sockaddr_t*) switch_rtp_session_get_remote_addr(switch_rtp_t *rtp_session);
SWITCH_DECLARE(switch_sockaddr_t*) switch_rtp_session_get_rtcp_remote_addr(switch_rtp_t *rtp_session);
SWITCH_DECLARE(char *) switch_rtp_session_get_type(switch_rtp_t *rtp_session);
/* Enables MID receive parsing for ext_id and clears any configured local MID, so outbound MID insertion is disabled until switch_rtp_enable_mid() is called. */
SWITCH_DECLARE(switch_status_t) switch_rtp_enable_mid_receive(switch_rtp_t *rtp_session, uint8_t ext_id);
SWITCH_DECLARE(switch_status_t) switch_rtp_enable_mid(switch_rtp_t *rtp_session, uint8_t ext_id, const char *mid);
/* Returns the session-owned MID parsed from the current returned RTP packet, or NULL when that packet did not carry MID. The returned pointer is overwritten by the next RTP read and must not be retained across reads or threads. */
SWITCH_DECLARE(const char *) switch_rtp_get_received_mid(switch_rtp_t *rtp_session);


/* Trickle ICE extensions */
Expand Down
4 changes: 3 additions & 1 deletion src/include/switch_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,7 @@ typedef enum {
SWITCH_RTP_FLAG_ICE_NO_RESPONSE_IGNORED,
SWITCH_RTP_FLAG_ICE_FAILED,
SWITCH_RTP_FLAG_IGNORE_RTP_DURING_DTMF,
SWITCH_RTP_FLAG_BUNDLE_ACCEPT_ANY_PT,
SWITCH_RTP_FLAG_INVALID
} switch_rtp_flag_t;

Expand Down Expand Up @@ -2056,7 +2057,8 @@ typedef enum {
SWITCH_IO_FLAG_NOBLOCK = (1 << 0),
SWITCH_IO_FLAG_SINGLE_READ = (1 << 1),
SWITCH_IO_FLAG_FORCE = (1 << 2),
SWITCH_IO_FLAG_QUEUED = (1 << 3)
SWITCH_IO_FLAG_QUEUED = (1 << 3),
SWITCH_IO_FLAG_BUNDLE_DRAIN = (1 << 4)
} switch_io_flag_enum_t;
typedef uint32_t switch_io_flag_t;

Expand Down
1 change: 1 addition & 0 deletions src/include/switch_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,7 @@ SWITCH_DECLARE(switch_status_t) switch_frame_buffer_push(switch_frame_buffer_t *
SWITCH_DECLARE(switch_status_t) switch_frame_buffer_trypush(switch_frame_buffer_t *fb, void *ptr);
SWITCH_DECLARE(switch_status_t) switch_frame_buffer_pop(switch_frame_buffer_t *fb, void **ptr);
SWITCH_DECLARE(switch_status_t) switch_frame_buffer_trypop(switch_frame_buffer_t *fb, void **ptr);
SWITCH_DECLARE(switch_status_t) switch_frame_buffer_pop_timeout(switch_frame_buffer_t *fb, void **ptr, switch_interval_time_t timeout);
SWITCH_DECLARE(int) switch_frame_buffer_size(switch_frame_buffer_t *fb);

typedef struct {
Expand Down
Loading
Loading