I’m dealing with an RTP port issue involving FreeSWITCH, a Go RTP listener, and GStreamer.
Scenario
My Go app listens for RTP on a specific UDP port A.
GStreamer needs to send RTP from the same source portA back to FreeSWITCH.
FreeSWITCH uses symmetric RTP: if it receives RTP from a different source port, it switches and starts sending media to that port instead of the port in the SDP.
Problem
Go binds to port A to receive packets.
GStreamer also needs to bind port A to use it as the RTP source port.
But two processes cannot bind to the same UDP port, so GStreamer cannot use that port.
If GStreamer uses a different source port, FreeSWITCH detects it and sends RTP there, breaking the Go receiver.
The best way to do it is to share the same socket. To do that you have 2 options, either you run your GStreamer pipeline in the same process as your other code, or you send the socket’s file descriptor over a UNIX socket between the processes.
You can also create the sockets in 2 different ways, you can let the GStreamer elemnet do it, when it’s in the PLAYING state, you can read the “used-socket” property to get it. If you’d rather do it the other way, you can create the socket however you want, then create a GSocket around it with g_socket_new_from_fd(), and then pass that to the “socket” property on udpsink while it is in the NULL state (before setting to the PLAYING state).
Thank you for the reply. I’m trying to understand how to do this using only GStreamer.
If I listen on a UDP port with udpsrc, I can access the "used-socket" property to retrieve the underlying socket that udpsrc is using. What I want to do next is send outgoing packets from the same socket essentially passing that socket to a udpsink.
My question is:
How can I take the socket obtained from udpsrc’s "used-socket" property and assign it to a udpsink, so that both elements share the same UDP socket?
In addition to the answers already, I can give a couple of general comments.
If only one port is different (in this case destination port) you should be able to force FreeSwitch to use asymmetric RTP by setting disable_rtp_auto_adjust
Other media / streaming apps out there typically detect any difference in source/dest IP addr and port, and optionally RTP payload type, as a “new stream” (or session) and go from there. One of these is the Mediashark software (mediaMin in this case)
Disclaimer – I work for the company that maintains mediaMin
I added disable_rtp_auto_adjust = true in FreeSWITCH, but it still isn’t doing what I need. The goal is to tell FreeSWITCH: “Here are the ports I’m listening on send the RTP packets here.” But if we then send RTP back to FreeSWITCH from a different source port than the one negotiated in the SDP, it stops working.
FreeSWITCH continues sending video to the ports defined in the SDP (which is correct).
But when FreeSWITCH notices that the source port of the incoming RTP doesn’t match the on in the invite SDP, it ignores those packets.
As a result, the SIP participant never sees the video, even though the packets are being sent to the correct destination IP/port on the FreeSWITCH side.
I tested this using a GStreamer pipeline where I set the bind-port to the same port that I included in the INVITE SDP sent to FreeSWITCH.
Hi @ggwebrc, sorry for my slow reply. Sounds like either disable_rtp_auto_adjust = true doesn’t work as advertised or possibly other settings are required. Can you put this on the “#freeswitch-dev” discord channel ? I can follow along and possibly help out. My company is working on adding EVS and other codecs to FreeSwitch so I’ve been learning how to test and debug changes to FreeSwitch source.
FreeSwitch should have some type of override. Mediashark has several combinations of auto-detect, “use SDP info”, .sdp file on the cmd line etc. These types of mismatches are common, especially when midpoints are involved like SBCs, media servers, gateways, etc.
Also can you clarify:
“I tested this using a GStreamer pipeline where I set the bind-port to the same port that I included in the INVITE SDP sent to FreeSWITCH”
Do you mean you tried that and it fixes the problem, or you mean that’s the test you run when you see the problem ?
Hey @jbrower thanks for the reply! I’ll definitely check out the Discord community.
Sure here’s what I meant:
When testing, I noticed the issue happens specifically when I’m using a different UDP socket to send video back to FreeSWITCH. If the socket isn’t the same one I used to receive video (the one bound to the port in the INVITE SDP), then nothing shows up on the SIP client side.
But if I reuse the exact same socket for both sending and receiving RTP video from FreeSWITCH, everything works correctly.
The bigger problem is that I ultimately want to take a single incoming video stream and send copies of it to multiple SIP participants. Each SIP participant receives a different port in their INVITE SDP, so I can’t use the same UDP port for all of them which is why reusing the same socket won’t work in that scenario.
Ok that’s clear, and I see your question on Freeswitch Discord. Let’s see if anyone has something to say.
It’s simple in theory, for example something in the middle like an SBC could easily relay traffic and send on the port expected by Freeswitch, but in practice not so much. I notice that rtpengine (Kamailio) has options for asymmetric RTP, one being to “learn” source ports in case different than what was established via SIP invite. Mediashark does something similar by “auto-detecting” IP header changes and assigning a new stream and handling it based on command line options or static session config files. One might think Freeswitch would have something similar
Based on the issues I’m seeing, it appears that FreeSWITCH validates the source port of incoming RTP packets and ignores them if they don’t match the port advertised in the INVITE’s SDP. I now create and bind the RTP socket in my application and then pass that socket to GStreamer.
Originally, I wanted GStreamer to send the same RTP directly to different FreeSWITCH ports, but instead I now send the RTP to sockets owned by my application and forward the packets to FreeSWITCH using the same socket bound to the original port provided in the SDP. Its a extra step but hey if it works it works