diff options
author | Martin Storsjö <martin@martin.st> | 2013-08-13 13:20:42 +0300 |
---|---|---|
committer | Martin Storsjö <martin@martin.st> | 2013-08-14 11:21:32 +0300 |
commit | 6b58e11a8331690ec32e9869db89ae10c54614e9 (patch) | |
tree | caff2e9ae6d1d6738e5d4742c8c3dea8ba77c88c | |
parent | 2427ac6ccd868811d1fe9df7c64c50ca58abe6f6 (diff) | |
download | ffmpeg-6b58e11a8331690ec32e9869db89ae10c54614e9.tar.gz |
rtpproto: Add an option for writing return packets to the address of the last received packets
If we've received packets on the same socket before, the return
packets are sent to that address. If we've only received packets
on the other socket, try to guess the source port for the other
one assuming the basic +1/-1 logic.
Signed-off-by: Martin Storsjö <martin@martin.st>
-rw-r--r-- | libavformat/rtpproto.c | 89 |
1 files changed, 84 insertions, 5 deletions
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 27ea37099a..6fa3182c96 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -45,6 +45,9 @@ typedef struct RTPContext { URLContext *rtp_hd, *rtcp_hd; int rtp_fd, rtcp_fd, nb_ssm_include_addrs, nb_ssm_exclude_addrs; struct sockaddr_storage **ssm_include_addrs, **ssm_exclude_addrs; + int write_to_source; + struct sockaddr_storage last_rtp_source, last_rtcp_source; + socklen_t last_rtp_source_len, last_rtcp_source_len; } RTPContext; /** @@ -125,6 +128,27 @@ static int compare_addr(const struct sockaddr_storage *a, return 1; } +static int get_port(const struct sockaddr_storage *ss) +{ + if (ss->ss_family == AF_INET) + return ntohs(((const struct sockaddr_in *)ss)->sin_port); +#ifdef AF_INET6 + if (ss->ss_family == AF_INET6) + return ntohs(((const struct sockaddr_in6 *)ss)->sin6_port); +#endif + return 0; +} + +static void set_port(struct sockaddr_storage *ss, int port) +{ + if (ss->ss_family == AF_INET) + ((struct sockaddr_in *)ss)->sin_port = htons(port); +#ifdef AF_INET6 + else if (ss->ss_family == AF_INET6) + ((struct sockaddr_in6 *)ss)->sin6_port = htons(port); +#endif +} + static int rtp_check_source_lists(RTPContext *s, struct sockaddr_storage *source_addr_ptr) { int i; @@ -236,6 +260,7 @@ static void rtp_parse_addr_list(URLContext *h, char *buf, * 'connect=0/1' : do a connect() on the UDP socket * 'sources=ip[,ip]' : list allowed source IP addresses * 'block=ip[,ip]' : list disallowed source IP addresses + * 'write_to_source=0/1' : send packets to the source address of the latest received packet * deprecated option: * 'localport=n' : set the local port to n * @@ -289,6 +314,9 @@ static int rtp_open(URLContext *h, const char *uri, int flags) if (av_find_info_tag(buf, sizeof(buf), "connect", p)) { connect = strtol(buf, NULL, 10); } + if (av_find_info_tag(buf, sizeof(buf), "write_to_source", p)) { + s->write_to_source = strtol(buf, NULL, 10); + } if (av_find_info_tag(buf, sizeof(buf), "sources", p)) { av_strlcpy(include_sources, buf, sizeof(include_sources)); rtp_parse_addr_list(h, buf, &s->ssm_include_addrs, &s->nb_ssm_include_addrs); @@ -333,11 +361,11 @@ static int rtp_open(URLContext *h, const char *uri, int flags) static int rtp_read(URLContext *h, uint8_t *buf, int size) { RTPContext *s = h->priv_data; - struct sockaddr_storage from; - socklen_t from_len; int len, n, i; struct pollfd p[2] = {{s->rtp_fd, POLLIN, 0}, {s->rtcp_fd, POLLIN, 0}}; int poll_delay = h->flags & AVIO_FLAG_NONBLOCK ? 0 : 100; + struct sockaddr_storage *addrs[2] = { &s->last_rtp_source, &s->last_rtcp_source }; + socklen_t *addr_lens[2] = { &s->last_rtp_source_len, &s->last_rtcp_source_len }; for(;;) { if (ff_check_interrupt(&h->interrupt_callback)) @@ -348,16 +376,16 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size) for (i = 1; i >= 0; i--) { if (!(p[i].revents & POLLIN)) continue; - from_len = sizeof(from); + *addr_lens[i] = sizeof(*addrs[i]); len = recvfrom(p[i].fd, buf, size, 0, - (struct sockaddr *)&from, &from_len); + (struct sockaddr *)addrs[i], addr_lens[i]); if (len < 0) { if (ff_neterrno() == AVERROR(EAGAIN) || ff_neterrno() == AVERROR(EINTR)) continue; return AVERROR(EIO); } - if (rtp_check_source_lists(s, &from)) + if (rtp_check_source_lists(s, addrs[i])) continue; return len; } @@ -381,6 +409,57 @@ static int rtp_write(URLContext *h, const uint8_t *buf, int size) if (size < 2) return AVERROR(EINVAL); + if (s->write_to_source) { + int fd; + struct sockaddr_storage *source, temp_source; + socklen_t *source_len, temp_len; + if (!s->last_rtp_source.ss_family && !s->last_rtcp_source.ss_family) { + av_log(h, AV_LOG_ERROR, + "Unable to send packet to source, no packets received yet\n"); + // Intentionally not returning an error here + return size; + } + + if (RTP_PT_IS_RTCP(buf[1])) { + fd = s->rtcp_fd; + source = &s->last_rtcp_source; + source_len = &s->last_rtcp_source_len; + } else { + fd = s->rtp_fd; + source = &s->last_rtp_source; + source_len = &s->last_rtp_source_len; + } + if (!source->ss_family) { + source = &temp_source; + source_len = &temp_len; + if (RTP_PT_IS_RTCP(buf[1])) { + temp_source = s->last_rtp_source; + temp_len = s->last_rtp_source_len; + set_port(source, get_port(source) + 1); + av_log(h, AV_LOG_INFO, + "Not received any RTCP packets yet, inferring peer port " + "from the RTP port\n"); + } else { + temp_source = s->last_rtcp_source; + temp_len = s->last_rtcp_source_len; + set_port(source, get_port(source) - 1); + av_log(h, AV_LOG_INFO, + "Not received any RTP packets yet, inferring peer port " + "from the RTCP port\n"); + } + } + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd(fd, 1); + if (ret < 0) + return ret; + } + ret = sendto(fd, buf, size, 0, (struct sockaddr *) source, + *source_len); + + return ret < 0 ? ff_neterrno() : ret; + } + if (RTP_PT_IS_RTCP(buf[1])) { /* RTCP payload type */ hd = s->rtcp_hd; |