From 6b58e11a8331690ec32e9869db89ae10c54614e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Tue, 13 Aug 2013 13:20:42 +0300 Subject: rtpproto: Add an option for writing return packets to the address of the last received packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ö --- libavformat/rtpproto.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 27ea370..6fa3182 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; -- cgit v1.1