[FFmpeg-devel] [PATCH] Send NAT punching packets when starting to read an RTP/UDP stream
Thu Feb 11 10:19:19 CET 2010
On Thu, 11 Feb 2010, Luca Abeni wrote:
> Martin Storsj? wrote:
> > The current code for receiving RTP/UDP streams doesn't work too well if the
> > receiver is behind NAT (unless the NAT inspects the RTSP setup conversation
> > and sets up forwarding for those ports).
> > Other players (such as RealPlayer on Symbian S60) handles this by sending
> > one dummy packet on each UDP port when starting the stream, in order to set
> > up proper forwarding rules in the NAT.
> Yes, this is an interesting feature, but... How does it work?
> I mean: assume you have a client C with a private IP, which is
> behind a NAT and wants to play a stream from a server S (having
> a public IP, obviously).
> C contacts the server via RTSP and negotiates two ports for the audio
> and the video streams. These are "private" ports (meaning, they are
> opened on the private interface of C). Then C sends some UDP packets to
> S, from those ports (to which destination ports? The same as the RTP
> ports on C?).
No, not to the same ports as on C. They're sent to the same destination
port as will be used as source port for sending RTP packets with the
payload data (and RTCP packets with sender reports) - this is the normal
way for bidirectional UDP traffic - in the same way as sender reports and
receiver reports are exchanged with RTCP.
For the RTCP part, what we really do is that we simply send an empty
receiver report before we've actually received anything, just like we
would do later during the session anyway when sending the proper RR.
> The packets pass through the NAT, and arrive to S from a different
> source IP (the NAT public IP) and a different source port (the port on
> which the NAT machine remaps the "private" source port used by C).
If the NAT changes the port numbers, this probably doesn't work. Usually,
they don't change the port numbers, though. The "source port, destination
port, destination ip" tuple is often enough to keep track of connections,
mapping them uniquely to the private IP of C.
> So, the forwarding rules in the NAT are properly set up,
> but now if S works according to the RTSP standard (sending
> packets to the UDP ports negotiated through RTSP) these packets
> will hardly arrive to C...
> I _suspect_ that this feature would be pretty useless without
> using some "STUN like" protocol, no?
As long as the NAT doesn't remap ports (and none of the ones I've tested
with do), nothing such is needed. Otherwise, we would first have to bind a
local port, use something STUN-like to find out the public mapping of this
port, and only then proceed with the RTSP setup phase, announcing the
publicly mapped port in the RTSP SETUP command.
> So, I assume this works only if the server does something smart,
> which does not seem to be described in the RTSP standard?
No, this doesn't require anything at all from the RTSP server.
> > The dummy packets sent from RealPlayer on Symbian are empty RTCP receiver
> > reports (sent on both the RTP and RTCP ports)
> I suspect sending an RTCP packet on an RTP port is highly
> non-standard ;-)
:-) Ok. And generally, the sender of an RTP stream probably doesn't expect
any packets on that port, so it won't even notice something was sent to
it, regardless of the packet content.
> > but due to the nature of the RTP protocol in libavformat, we can't force an
> > RTCP packet on the RTP port, so instead I chose to send a minimal RTP packet
> > with no payload content.
> This would be more standard, I guess... did you check if this works?
> Which kind of RTSP server did you use?
Yes, I've tested it, and it works behind normal linux NAT setups (both at
home and at work). I used a Darwin Streaming Server, but that shouldn't
matter at all.
I've also tested it with two ffmpeg instances on two different machines
behind the same NAT, which worked just as well.
So, yes, there sure are NAT setups where this doesn't work, but for the
ones where it does help (the vast majority or just some, I don't know),
it's a simple and easy trick to make things work.
More information about the ffmpeg-devel