Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 1 | /* Implementation to manage two RTP streams that make up an MO or MT call leg's RTP forwarding. */ |
| 2 | /* |
Vadim Yanitskiy | 999a593 | 2023-05-18 17:22:26 +0700 | [diff] [blame] | 3 | * (C) 2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 4 | * All Rights Reserved |
| 5 | * |
| 6 | * Author: Neels Hofmeyr |
| 7 | * |
Harald Welte | 1b1a39b | 2024-02-17 10:11:18 +0100 | [diff] [blame] | 8 | * SPDX-License-Identifier: AGPL-3.0+ |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify |
Harald Welte | 1b1a39b | 2024-02-17 10:11:18 +0100 | [diff] [blame] | 11 | * it under the terms of the GNU Affero General Public License as published by |
| 12 | * the Free Software Foundation; either version 3 of the License, or |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 13 | * (at your option) any later version. |
| 14 | * |
| 15 | * This program is distributed in the hope that it will be useful, |
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
Harald Welte | 1b1a39b | 2024-02-17 10:11:18 +0100 | [diff] [blame] | 18 | * GNU Affero General Public License for more details. |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 19 | */ |
| 20 | #include <osmocom/core/fsm.h> |
| 21 | #include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h> |
| 22 | |
| 23 | #include <osmocom/msc/debug.h> |
| 24 | #include <osmocom/msc/gsm_data.h> |
| 25 | #include <osmocom/msc/msc_a.h> |
| 26 | |
| 27 | #include <osmocom/msc/call_leg.h> |
| 28 | #include <osmocom/msc/rtp_stream.h> |
| 29 | |
| 30 | #define LOG_CALL_LEG(cl, level, fmt, args...) \ |
| 31 | LOGPFSML(cl ? cl->fi : NULL, level, fmt, ##args) |
| 32 | |
| 33 | static struct gsm_network *gsmnet = NULL; |
| 34 | |
| 35 | enum call_leg_state { |
| 36 | CALL_LEG_ST_ESTABLISHING, |
| 37 | CALL_LEG_ST_ESTABLISHED, |
| 38 | CALL_LEG_ST_RELEASING, |
| 39 | }; |
| 40 | |
| 41 | struct osmo_tdef g_mgw_tdefs[] = { |
Neels Hofmeyr | d4099c3 | 2020-09-16 01:59:29 +0200 | [diff] [blame] | 42 | { .T=-2427, .default_val=4, .desc="MGCP response timeout" }, |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 43 | { .T=-2, .default_val=30, .desc="RTP stream establishing timeout" }, |
| 44 | {} |
| 45 | }; |
| 46 | |
| 47 | static const struct osmo_tdef_state_timeout call_leg_fsm_timeouts[32] = { |
| 48 | [CALL_LEG_ST_ESTABLISHING] = { .T = -2 }, |
| 49 | [CALL_LEG_ST_RELEASING] = { .T = -2 }, |
| 50 | }; |
| 51 | |
| 52 | #define call_leg_state_chg(cl, state) \ |
| 53 | osmo_tdef_fsm_inst_state_chg((cl)->fi, state, call_leg_fsm_timeouts, g_mgw_tdefs, 10) |
| 54 | |
| 55 | static struct osmo_fsm call_leg_fsm; |
| 56 | |
| 57 | void call_leg_init(struct gsm_network *net) |
| 58 | { |
| 59 | gsmnet = net; |
| 60 | OSMO_ASSERT( osmo_fsm_register(&call_leg_fsm) == 0 ); |
| 61 | } |
| 62 | |
Neels Hofmeyr | f50d130 | 2019-05-09 16:23:11 +0200 | [diff] [blame] | 63 | /* Allocate a call leg FSM instance as child of an arbitrary other FSM instance. |
| 64 | * The call leg FSM dispatches events to its parent FSM instance on specific events: |
| 65 | * - parent_event_term: dispatch this to the parent FI when the call leg terminates (call ended, either planned or by |
| 66 | * failure). |
| 67 | * - parent_event_rtp_addr_available: one of the rtp_stream instances managed by the call leg has received an RTP |
| 68 | * address from the MGW. The struct rtp_stream instance is passed as data argument for the event dispatch. |
| 69 | * - parent_event_rtp_complete: one of the rtp_stream instances entered the RTP_STREAM_ST_ESTABLISHED state. |
| 70 | */ |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 71 | struct call_leg *call_leg_alloc(struct osmo_fsm_inst *parent_fi, |
| 72 | uint32_t parent_event_term, |
| 73 | uint32_t parent_event_rtp_addr_available, |
Neels Hofmeyr | 265a4c7 | 2019-05-09 16:20:51 +0200 | [diff] [blame] | 74 | uint32_t parent_event_rtp_complete) |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 75 | { |
| 76 | struct call_leg *cl; |
| 77 | struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&call_leg_fsm, parent_fi, parent_event_term); |
| 78 | |
| 79 | OSMO_ASSERT(fi); |
| 80 | |
| 81 | cl = talloc(fi, struct call_leg); |
| 82 | OSMO_ASSERT(cl); |
| 83 | fi->priv = cl; |
| 84 | *cl = (struct call_leg){ |
| 85 | .fi = fi, |
| 86 | .parent_event_rtp_addr_available = parent_event_rtp_addr_available, |
| 87 | .parent_event_rtp_complete = parent_event_rtp_complete, |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 88 | }; |
| 89 | |
| 90 | return cl; |
| 91 | } |
| 92 | |
| 93 | void call_leg_reparent(struct call_leg *cl, |
| 94 | struct osmo_fsm_inst *new_parent_fi, |
| 95 | uint32_t parent_event_term, |
| 96 | uint32_t parent_event_rtp_addr_available, |
Neels Hofmeyr | 265a4c7 | 2019-05-09 16:20:51 +0200 | [diff] [blame] | 97 | uint32_t parent_event_rtp_complete) |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 98 | { |
| 99 | LOG_CALL_LEG(cl, LOGL_DEBUG, "Reparenting from parent %s to parent %s\n", |
| 100 | cl->fi->proc.parent->name, new_parent_fi->name); |
| 101 | osmo_fsm_inst_change_parent(cl->fi, new_parent_fi, parent_event_term); |
| 102 | talloc_steal(new_parent_fi, cl->fi); |
| 103 | cl->parent_event_rtp_addr_available = parent_event_rtp_addr_available; |
| 104 | cl->parent_event_rtp_complete = parent_event_rtp_complete; |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | static int call_leg_fsm_timer_cb(struct osmo_fsm_inst *fi) |
| 108 | { |
| 109 | struct call_leg *cl = fi->priv; |
| 110 | call_leg_release(cl); |
| 111 | return 0; |
| 112 | } |
| 113 | |
| 114 | void call_leg_release(struct call_leg *cl) |
| 115 | { |
| 116 | if (!cl) |
| 117 | return; |
| 118 | if (cl->fi->state == CALL_LEG_ST_RELEASING) |
| 119 | return; |
| 120 | call_leg_state_chg(cl, CALL_LEG_ST_RELEASING); |
| 121 | } |
| 122 | |
| 123 | static void call_leg_mgw_endpoint_gone(struct call_leg *cl) |
| 124 | { |
Pau Espin Pedrol | b44cf2d | 2022-10-17 18:09:15 +0200 | [diff] [blame] | 125 | struct mgcp_client *mgcp_client; |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 126 | int i; |
Pau Espin Pedrol | b44cf2d | 2022-10-17 18:09:15 +0200 | [diff] [blame] | 127 | |
| 128 | /* Put MGCP client back into MGW pool */ |
| 129 | mgcp_client = osmo_mgcpc_ep_client(cl->mgw_endpoint); |
| 130 | mgcp_client_pool_put(mgcp_client); |
| 131 | |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 132 | cl->mgw_endpoint = NULL; |
| 133 | for (i = 0; i < ARRAY_SIZE(cl->rtp); i++) { |
| 134 | if (!cl->rtp[i]) |
| 135 | continue; |
| 136 | cl->rtp[i]->ci = NULL; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | static void call_leg_fsm_establishing_established(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 141 | { |
| 142 | struct call_leg *cl = fi->priv; |
| 143 | struct rtp_stream *rtps; |
| 144 | int i; |
| 145 | bool established; |
| 146 | |
| 147 | switch (event) { |
| 148 | |
| 149 | case CALL_LEG_EV_RTP_STREAM_ESTABLISHED: |
| 150 | /* An rtp_stream says it is established. If all are now established, change to state |
| 151 | * CALL_LEG_ST_ESTABLISHED. */ |
| 152 | established = true; |
| 153 | for (i = 0; i < ARRAY_SIZE(cl->rtp); i++) { |
| 154 | if (!rtp_stream_is_established(cl->rtp[i])) { |
| 155 | established = false; |
| 156 | break; |
| 157 | } |
| 158 | } |
| 159 | if (!established) |
| 160 | break; |
Neels Hofmeyr | d7d3ece | 2023-11-23 00:05:32 +0100 | [diff] [blame] | 161 | if (cl->fi->state != CALL_LEG_ST_ESTABLISHED) |
| 162 | call_leg_state_chg(cl, CALL_LEG_ST_ESTABLISHED); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 163 | break; |
| 164 | |
| 165 | case CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE: |
| 166 | rtps = data; |
| 167 | osmo_fsm_inst_dispatch(fi->proc.parent, cl->parent_event_rtp_addr_available, rtps); |
| 168 | break; |
| 169 | |
| 170 | case CALL_LEG_EV_RTP_STREAM_GONE: |
| 171 | call_leg_release(cl); |
| 172 | break; |
| 173 | |
| 174 | case CALL_LEG_EV_MGW_ENDPOINT_GONE: |
| 175 | call_leg_mgw_endpoint_gone(cl); |
| 176 | call_leg_release(cl); |
| 177 | break; |
| 178 | |
| 179 | default: |
| 180 | OSMO_ASSERT(false); |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | void call_leg_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) |
| 185 | { |
| 186 | struct call_leg *cl = fi->priv; |
| 187 | osmo_fsm_inst_dispatch(fi->proc.parent, cl->parent_event_rtp_complete, cl); |
| 188 | } |
| 189 | |
| 190 | void call_leg_fsm_releasing_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) |
| 191 | { |
Pau Espin Pedrol | 093fd2e | 2022-10-19 16:56:43 +0200 | [diff] [blame] | 192 | /* Trigger termination of children FSMs (rtp_stream(s)) before |
| 193 | * terminating ourselves, otherwise we are not able to receive |
| 194 | * CALL_LEG_EV_MGW_ENDPOINT_GONE from cl->mgw_endpoint (call_leg => |
| 195 | * rtp_stream => mgw_endpoint), because osmo_fsm disabled dispatching |
| 196 | * events to an FSM in process of terminating. */ |
| 197 | osmo_fsm_inst_term_children(fi, OSMO_FSM_TERM_PARENT, NULL); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 198 | osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); |
| 199 | } |
| 200 | |
| 201 | static void call_leg_fsm_releasing(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
| 202 | { |
| 203 | struct call_leg *cl = fi->priv; |
| 204 | |
| 205 | switch (event) { |
| 206 | |
| 207 | case CALL_LEG_EV_RTP_STREAM_GONE: |
| 208 | /* We're already terminating, child RTP streams will also terminate, there is nothing left to do. */ |
| 209 | break; |
| 210 | |
| 211 | case CALL_LEG_EV_MGW_ENDPOINT_GONE: |
| 212 | call_leg_mgw_endpoint_gone(cl); |
| 213 | break; |
| 214 | |
| 215 | default: |
| 216 | OSMO_ASSERT(false); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | static const struct value_string call_leg_fsm_event_names[] = { |
| 221 | OSMO_VALUE_STRING(CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE), |
| 222 | OSMO_VALUE_STRING(CALL_LEG_EV_RTP_STREAM_ESTABLISHED), |
| 223 | OSMO_VALUE_STRING(CALL_LEG_EV_RTP_STREAM_GONE), |
| 224 | OSMO_VALUE_STRING(CALL_LEG_EV_MGW_ENDPOINT_GONE), |
| 225 | {} |
| 226 | }; |
| 227 | |
| 228 | #define S(x) (1 << (x)) |
| 229 | |
| 230 | static const struct osmo_fsm_state call_leg_fsm_states[] = { |
| 231 | [CALL_LEG_ST_ESTABLISHING] = { |
| 232 | .name = "ESTABLISHING", |
| 233 | .in_event_mask = 0 |
| 234 | | S(CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE) |
| 235 | | S(CALL_LEG_EV_RTP_STREAM_ESTABLISHED) |
| 236 | | S(CALL_LEG_EV_RTP_STREAM_GONE) |
| 237 | | S(CALL_LEG_EV_MGW_ENDPOINT_GONE) |
| 238 | , |
| 239 | .out_state_mask = 0 |
| 240 | | S(CALL_LEG_ST_ESTABLISHED) |
| 241 | | S(CALL_LEG_ST_RELEASING) |
| 242 | , |
| 243 | .action = call_leg_fsm_establishing_established, |
| 244 | }, |
| 245 | [CALL_LEG_ST_ESTABLISHED] = { |
| 246 | .name = "ESTABLISHED", |
| 247 | .in_event_mask = 0 |
| 248 | | S(CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE) |
| 249 | | S(CALL_LEG_EV_RTP_STREAM_ESTABLISHED) |
| 250 | | S(CALL_LEG_EV_RTP_STREAM_GONE) |
| 251 | | S(CALL_LEG_EV_MGW_ENDPOINT_GONE) |
| 252 | , |
| 253 | .out_state_mask = 0 |
| 254 | | S(CALL_LEG_ST_ESTABLISHING) |
| 255 | | S(CALL_LEG_ST_RELEASING) |
| 256 | , |
| 257 | .onenter = call_leg_fsm_established_onenter, |
| 258 | .action = call_leg_fsm_establishing_established, /* same action function as above */ |
| 259 | }, |
| 260 | [CALL_LEG_ST_RELEASING] = { |
| 261 | .name = "RELEASING", |
| 262 | .in_event_mask = 0 |
| 263 | | S(CALL_LEG_EV_RTP_STREAM_GONE) |
| 264 | | S(CALL_LEG_EV_MGW_ENDPOINT_GONE) |
| 265 | , |
| 266 | .onenter = call_leg_fsm_releasing_onenter, |
| 267 | .action = call_leg_fsm_releasing, |
| 268 | }, |
| 269 | }; |
| 270 | |
| 271 | static struct osmo_fsm call_leg_fsm = { |
| 272 | .name = "call_leg", |
| 273 | .states = call_leg_fsm_states, |
| 274 | .num_states = ARRAY_SIZE(call_leg_fsm_states), |
| 275 | .log_subsys = DCC, |
| 276 | .event_names = call_leg_fsm_event_names, |
| 277 | .timer_cb = call_leg_fsm_timer_cb, |
| 278 | }; |
| 279 | |
| 280 | const struct value_string rtp_direction_names[] = { |
| 281 | OSMO_VALUE_STRING(RTP_TO_RAN), |
| 282 | OSMO_VALUE_STRING(RTP_TO_CN), |
| 283 | {} |
| 284 | }; |
| 285 | |
| 286 | int call_leg_ensure_rtp_alloc(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans) |
| 287 | { |
| 288 | if (cl->rtp[dir]) |
| 289 | return 0; |
| 290 | |
Pau Espin Pedrol | b44cf2d | 2022-10-17 18:09:15 +0200 | [diff] [blame] | 291 | if (!cl->mgw_endpoint) { |
| 292 | struct mgcp_client *mgcp_client = mgcp_client_pool_get(gsmnet->mgw.mgw_pool); |
| 293 | if (!mgcp_client) { |
| 294 | LOG_CALL_LEG(cl, LOGL_ERROR, |
| 295 | "cannot ensure MGW endpoint -- no MGW configured, check configuration!\n"); |
| 296 | return -ENODEV; |
| 297 | } |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 298 | cl->mgw_endpoint = osmo_mgcpc_ep_alloc(cl->fi, CALL_LEG_EV_MGW_ENDPOINT_GONE, |
Pau Espin Pedrol | b44cf2d | 2022-10-17 18:09:15 +0200 | [diff] [blame] | 299 | mgcp_client, gsmnet->mgw.tdefs, cl->fi->id, |
| 300 | "%s", mgcp_client_rtpbridge_wildcard(mgcp_client)); |
| 301 | } |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 302 | if (!cl->mgw_endpoint) { |
| 303 | LOG_CALL_LEG(cl, LOGL_ERROR, "failed to setup MGW endpoint\n"); |
| 304 | return -EIO; |
| 305 | } |
| 306 | |
Andreas Eversberg | bcb4d6b | 2023-07-05 15:03:19 +0200 | [diff] [blame] | 307 | cl->rtp[dir] = rtp_stream_alloc(cl->fi, CALL_LEG_EV_RTP_STREAM_GONE, CALL_LEG_EV_RTP_STREAM_ADDR_AVAILABLE, |
| 308 | CALL_LEG_EV_RTP_STREAM_ESTABLISHED, dir, call_id, for_trans); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 309 | OSMO_ASSERT(cl->rtp[dir]); |
| 310 | return 0; |
| 311 | } |
| 312 | |
| 313 | struct osmo_sockaddr_str *call_leg_local_ip(struct call_leg *cl, enum rtp_direction dir) |
| 314 | { |
| 315 | struct rtp_stream *rtps; |
| 316 | if (!cl) |
| 317 | return NULL; |
| 318 | rtps = cl->rtp[dir]; |
| 319 | if (!rtps) |
| 320 | return NULL; |
Neels Hofmeyr | 84ce206 | 2019-10-05 05:15:25 +0200 | [diff] [blame] | 321 | if (!osmo_sockaddr_str_is_nonzero(&rtps->local)) |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 322 | return NULL; |
| 323 | return &rtps->local; |
| 324 | } |
| 325 | |
| 326 | /* Make sure an MGW endpoint CI is set up for an RTP connection. |
| 327 | * This is the one-stop for all to either completely set up a new endpoint connection, or to modify an existing one. |
| 328 | * If not yet present, allocate the rtp_stream for the given direction. |
Neels Hofmeyr | 62bfa37 | 2022-10-31 18:51:07 +0100 | [diff] [blame] | 329 | * Then, call rtp_stream_set_codecs() if codecs_if_known is non-NULL, and/or rtp_stream_set_remote_addr() if |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 330 | * remote_addr_if_known is non-NULL. |
| 331 | * Finally make sure that a CRCX is sent out for this direction, if this has not already happened. |
| 332 | * If the CRCX has already happened but new codec / remote_addr data was passed, call rtp_stream_commit() to trigger an |
| 333 | * MDCX. |
| 334 | */ |
| 335 | int call_leg_ensure_ci(struct call_leg *cl, enum rtp_direction dir, uint32_t call_id, struct gsm_trans *for_trans, |
Neels Hofmeyr | 62bfa37 | 2022-10-31 18:51:07 +0100 | [diff] [blame] | 336 | const struct sdp_audio_codecs *codecs_if_known, |
| 337 | const struct osmo_sockaddr_str *remote_addr_if_known) |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 338 | { |
| 339 | if (call_leg_ensure_rtp_alloc(cl, dir, call_id, for_trans)) |
| 340 | return -EIO; |
Andreas Eversberg | 58fe2e0 | 2023-06-21 12:37:18 +0200 | [diff] [blame] | 341 | rtp_stream_set_mode(cl->rtp[dir], cl->crcx_conn_mode[dir]); |
Pau Espin Pedrol | a3cdab4 | 2019-05-09 17:54:08 +0200 | [diff] [blame] | 342 | if (dir == RTP_TO_RAN && cl->ran_peer_supports_osmux) { |
| 343 | cl->rtp[dir]->use_osmux = true; |
| 344 | cl->rtp[dir]->remote_osmux_cid = -1; /* wildcard */ |
| 345 | } |
Neels Hofmeyr | 62bfa37 | 2022-10-31 18:51:07 +0100 | [diff] [blame] | 346 | if (codecs_if_known) |
| 347 | rtp_stream_set_codecs(cl->rtp[dir], codecs_if_known); |
Neels Hofmeyr | 84ce206 | 2019-10-05 05:15:25 +0200 | [diff] [blame] | 348 | if (remote_addr_if_known && osmo_sockaddr_str_is_nonzero(remote_addr_if_known)) |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 349 | rtp_stream_set_remote_addr(cl->rtp[dir], remote_addr_if_known); |
| 350 | return rtp_stream_ensure_ci(cl->rtp[dir], cl->mgw_endpoint); |
| 351 | } |
| 352 | |
| 353 | int call_leg_local_bridge(struct call_leg *cl1, uint32_t call_id1, struct gsm_trans *trans1, |
| 354 | struct call_leg *cl2, uint32_t call_id2, struct gsm_trans *trans2) |
| 355 | { |
Neels Hofmeyr | b5fe973 | 2023-09-13 22:41:29 +0200 | [diff] [blame] | 356 | struct sdp_audio_codecs *cn_codecs = NULL; |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 357 | |
| 358 | cl1->local_bridge = cl2; |
| 359 | cl2->local_bridge = cl1; |
| 360 | |
Neels Hofmeyr | b5fe973 | 2023-09-13 22:41:29 +0200 | [diff] [blame] | 361 | /* Marry the two CN sides of the call legs. Call establishment should have made all efforts for these to be |
| 362 | * compatible. However, for local bridging, the codecs and payload type numbers must be exactly identical on |
| 363 | * both sides. Both sides may so far have different payload type numbers or slightly differing codecs, but it |
| 364 | * will only work when the SDP on the RTP_TO_CN sides of the call legs talk the same payload type numbers. |
| 365 | * So, simply take the SDP from one RTP_TO_CN side, and overwrite the other RTP_TO_CN side's SDP with it. |
| 366 | * If all goes to plan, the codecs will be identical, or possibly the MGW will do a conversion like AMR-BE to |
| 367 | * AMR-OA. In the worst case, the other call leg cannot transcode, and the call fails -- because codec |
| 368 | * negotiation did not do a good enough job. |
| 369 | * |
| 370 | * Copy one call leg's CN config to the other: |
| 371 | * |
| 372 | * call leg 1 call leg 2 |
| 373 | * ---MGW-ep------- ---MGW-ep------- |
| 374 | * RAN CN CN RAN |
| 375 | * AMR:112 AMR:112 AMR:96 AMR:96 |
| 376 | * | |
| 377 | * +-------+ |
| 378 | * | |
| 379 | * V |
| 380 | * AMR:112 AMR:112 AMR:112 AMR:96 |
| 381 | * ^MGW-endpoint converts payload type numbers between 112 and 96. |
| 382 | */ |
| 383 | if (cl1->rtp[RTP_TO_CN] && cl1->rtp[RTP_TO_CN]->codecs_known) |
| 384 | cn_codecs = &cl1->rtp[RTP_TO_CN]->codecs; |
| 385 | else if (cl2->rtp[RTP_TO_CN] && cl2->rtp[RTP_TO_CN]->codecs_known) |
| 386 | cn_codecs = &cl2->rtp[RTP_TO_CN]->codecs; |
| 387 | if (!cn_codecs) { |
| 388 | LOG_CALL_LEG(cl1, LOGL_ERROR, "RAN-side CN stream codec is not known, not ready for bridging\n"); |
| 389 | LOG_CALL_LEG(cl2, LOGL_ERROR, "RAN-side CN stream codec is not known, not ready for bridging\n"); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 390 | return -EINVAL; |
| 391 | } |
Pau Espin Pedrol | dd26226 | 2022-01-13 14:06:44 +0100 | [diff] [blame] | 392 | |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 393 | call_leg_ensure_ci(cl1, RTP_TO_CN, call_id1, trans1, |
Neels Hofmeyr | b5fe973 | 2023-09-13 22:41:29 +0200 | [diff] [blame] | 394 | cn_codecs, &cl2->rtp[RTP_TO_CN]->local); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 395 | call_leg_ensure_ci(cl2, RTP_TO_CN, call_id2, trans2, |
Neels Hofmeyr | b5fe973 | 2023-09-13 22:41:29 +0200 | [diff] [blame] | 396 | cn_codecs, &cl1->rtp[RTP_TO_CN]->local); |
Neels Hofmeyr | c4628a3 | 2018-12-07 14:47:34 +0100 | [diff] [blame] | 397 | return 0; |
| 398 | } |