summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libavformat/rtpproto.c89
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;