From: Peter Enderborg via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: <michael@niedermayer.cc>, <ffmpeg-devel@ffmpeg.org>, <remi@remlab.net> Cc: Peter Enderborg <peterend@axis.com> Subject: [FFmpeg-devel] [PATCH v3 3/5] libavformat: udp.c Add support for multi or single join Date: Fri, 29 Aug 2025 16:04:57 +0200 Message-ID: <20250829140459.3220037-4-peterend@axis.com> (raw) In-Reply-To: <20250829140459.3220037-1-peterend@axis.com> A new commandline option to set for joining multiple interfaces. The default is one, and it is also a special case falling back to output routing table for selecting interface on group join. On group join it search over all interfaces. Change-Id: Idbf88c3ecd7d27cba0a236a8fa2451a85143d068 Signed-off-by: Peter Enderborg <peterend@axis.com> --- libavformat/rtpproto.c | 3 ++ libavformat/rtsp.c | 3 +- libavformat/rtsp.h | 1 + libavformat/udp.c | 82 +++++++++++++++++++++++++++++++----------- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 15d0050936..c6abd7a02c 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -61,6 +61,7 @@ typedef struct RTPContext { char *fec_options_str; int64_t rw_timeout; char *localaddr; + int multicast_max_join; } RTPContext; #define OFFSET(x) offsetof(RTPContext, x) @@ -81,6 +82,7 @@ static const AVOption options[] = { { "block", "Block list", OFFSET(block), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, { "fec", "FEC", OFFSET(fec_options_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = E }, { "localaddr", "Local address", OFFSET(localaddr), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, + { "multicast_max_join", "Number of ipv6 network intefaces to join multicast group", OFFSET(multicast_max_join), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, .flags = D|E }, { NULL } }; @@ -199,6 +201,7 @@ static void build_udp_url(RTPContext *s, url_add_option(buf, buf_size, "block=%s", exclude_sources); if (localaddr && localaddr[0]) url_add_option(buf, buf_size, "localaddr=%s", localaddr); + url_add_option(buf, buf_size, "multicast_max_join=%d", s->multicast_max_join); } /** diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 10355b89b8..08ce628b0c 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -81,6 +81,7 @@ #define COMMON_OPTS() \ { "reorder_queue_size", "set number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC }, \ { "buffer_size", "Underlying protocol send/receive buffer size", OFFSET(buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC|ENC }, \ + { "multicast_max_join", "Number of ipv6 network intefaces to join multicast group", OFFSET(multicast_max_join), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, ENC }, \ { "pkt_size", "Underlying protocol send packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = 1472 }, -1, INT_MAX, ENC } \ @@ -139,7 +140,7 @@ static AVDictionary *map_to_opts(RTSPState *rt) av_dict_set_int(&opts, "pkt_size", rt->pkt_size, 0); if (rt->localaddr && rt->localaddr[0]) av_dict_set(&opts, "localaddr", rt->localaddr, 0); - + av_dict_set_int(&opts, "multicast_max_join", rt->multicast_max_join, 0); return opts; } diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index ca278acd43..626114fb5a 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -419,6 +419,7 @@ typedef struct RTSPState { int buffer_size; int pkt_size; char *localaddr; + int multicast_max_join; /** * Options used for TLS based RTSP streams. diff --git a/libavformat/udp.c b/libavformat/udp.c index 9ffd55e017..9c2f904e50 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -74,6 +74,7 @@ #include <sys/ioctl.h> #include <ifaddrs.h> #endif +#define IPV6_DEFAULT_JOIN 1 #ifndef IPV6_ADD_MEMBERSHIP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP @@ -124,6 +125,7 @@ typedef struct UDPContext { uint8_t tmp[UDP_MAX_PKT_SIZE + sizeof(UDPQueuedPacketHeader)]; int remaining_in_dg; char *localaddr; + int multicast_max_join; int timeout; struct sockaddr_storage local_addr_storage; char *sources; @@ -143,6 +145,7 @@ static const AVOption options[] = { { "localport", "Local port", OFFSET(local_port), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, D|E }, { "local_port", "Local port", OFFSET(local_port), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "localaddr", "Local address", OFFSET(localaddr), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, + { "multicast_max_join", "Number of ipv6 network intefaces to join multicast group", OFFSET(multicast_max_join), AV_OPT_TYPE_INT, { .i64 = IPV6_DEFAULT_JOIN }, -1, INT_MAX, .flags = D|E }, { "udplite_coverage", "choose UDPLite head size which should be validated by checksum", OFFSET(udplite_coverage), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D|E }, { "pkt_size", "Maximum UDP packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = 1472 }, -1, INT_MAX, .flags = D|E }, { "reuse", "explicitly allow reusing UDP sockets", OFFSET(reuse_socket), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D|E }, @@ -255,7 +258,8 @@ static int udp_ipv6_mc_devname_membership(int mop, const char *name, } static int udp_ipv6_multicast_iterate(int sockfd, struct sockaddr_in6 *addr, - struct sockaddr_in6 *local_addr, int mop, void *logctx) + struct sockaddr_in6 *local_addr, int mop, + int multicast_max_join, void *logctx) { struct in6_addr anyipv6 = IN6ADDR_ANY_INIT; struct ifaddrs *ifal=NULL,*ife=NULL; @@ -267,13 +271,16 @@ static int udp_ipv6_multicast_iterate(int sockfd, struct sockaddr_in6 *addr, if (ife->ifa_addr && (ife->ifa_addr->sa_family == AF_INET6) && (ife->ifa_flags & IFF_MULTICAST) && - (ife->ifa_flags & IFF_UP)) { + (ife->ifa_flags & IFF_UP) && + multicast_max_join ) { if ((!memcmp(&local_addr->sin6_addr, &anyipv6, sizeof(struct in6_addr))) || (!memcmp(&local_addr->sin6_addr, &((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr, sizeof(struct in6_addr)))) { if (udp_ipv6_mc_devname_membership(mop,ife->ifa_name, iindex_fd, &addr->sin6_addr, - sockfd, logctx) == 1) + sockfd, logctx) == 1) { membership_changed = 1; + multicast_max_join--; + } } } } @@ -290,7 +297,8 @@ static int udp_ipv6_multicast_iterate(int sockfd, struct sockaddr_in6 *addr, #endif static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, - struct sockaddr *local_addr, void *logctx) + struct sockaddr *local_addr, + int multicast_max_join, void *logctx) { #ifdef IP_ADD_MEMBERSHIP if (addr->sa_family == AF_INET) { @@ -305,19 +313,33 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IP_ADD_MEMBERSHIP)"); return ff_neterrno(); } + if (multicast_max_join) + ff_log_net_error(logctx, AV_LOG_WARNING, "multicast_max_join is not used in ip4"); } #endif #if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6) if (addr->sa_family == AF_INET6) { + struct ipv6_mreq mreq6; #if HAVE_IOCTL_GIFINDEX - return udp_ipv6_multicast_iterate(sockfd, - (struct sockaddr_in6 *)addr, - (struct sockaddr_in6 *)local_addr, - IPV6_JOIN_GROUP, - logctx); + /* Fallback to use multicast output routing if we do not have + multiple interface join or a interface name or a local address */ + if (multicast_max_join == IPV6_DEFAULT_JOIN && !local_addr) { + memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + mreq6.ipv6mr_interface = 0; + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_ADD_MEMBERSHIP)"); + return ff_neterrno(); + } + } + else { + return udp_ipv6_multicast_iterate(sockfd, + (struct sockaddr_in6 *)addr, + (struct sockaddr_in6 *)local_addr, + IPV6_JOIN_GROUP, + multicast_max_join, + logctx); + } #else - struct ipv6_mreq mreq6; - memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); mreq6.ipv6mr_interface = 0; if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { @@ -331,7 +353,8 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, } static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr, - struct sockaddr *local_addr, void *logctx) + struct sockaddr *local_addr, + int multicast_max_join, void *logctx) { #ifdef IP_DROP_MEMBERSHIP if (addr->sa_family == AF_INET) { @@ -350,15 +373,27 @@ static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr, #endif #if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6) if (addr->sa_family == AF_INET6) { + struct ipv6_mreq mreq6; #if HAVE_IOCTL_GIFINDEX - return udp_ipv6_multicast_iterate(sockfd, - (struct sockaddr_in6 *)addr, - (struct sockaddr_in6 *)local_addr, - IPV6_LEAVE_GROUP, - logctx); + /* Fallback to use multicast output routing if we do not have + multiple interface join or a interface name or a local address */ + if (multicast_max_join == IPV6_DEFAULT_JOIN && !local_addr) { + memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); + mreq6.ipv6mr_interface = 0; + if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { + ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_ADD_MEMBERSHIP)"); + return ff_neterrno(); + } + } + else { + return udp_ipv6_multicast_iterate(sockfd, + (struct sockaddr_in6 *)addr, + (struct sockaddr_in6 *)local_addr, + IPV6_LEAVE_GROUP, + multicast_max_join, + logctx); + } #else - struct ipv6_mreq mreq6; - memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); mreq6.ipv6mr_interface = 0; if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { @@ -847,6 +882,9 @@ static int udp_open(URLContext *h, const char *uri, int flags) if (av_find_info_tag(buf, sizeof(buf), "localport", p)) { s->local_port = strtol(buf, NULL, 10); } + if (av_find_info_tag(buf, sizeof(buf), "multicast_max_join", p)) { + s->multicast_max_join = strtol(buf, NULL, 10); + } if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) { s->pkt_size = strtol(buf, NULL, 10); } @@ -1012,7 +1050,8 @@ static int udp_open(URLContext *h, const char *uri, int flags) goto fail; } else { if ((ret = udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr, - (struct sockaddr *)&s->local_addr_storage, h)) < 0) + (struct sockaddr *)&s->local_addr_storage, + s->multicast_max_join, h)) < 0) goto fail; } if (s->filters.nb_exclude_addrs) { @@ -1266,7 +1305,8 @@ static int udp_close(URLContext *h) if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr, - (struct sockaddr *)&s->local_addr_storage, h); + (struct sockaddr *)&s->local_addr_storage, + s->multicast_max_join, h); #if HAVE_PTHREAD_CANCEL if (s->thread_started) { int ret; -- 2.34.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
next prev parent reply other threads:[~2025-08-29 14:07 UTC|newest] Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <0250811223419.GF29660@pb2> 2025-08-29 14:04 ` [FFmpeg-devel] [PATCH v3 0/5] Select output interfaces for ipv6 multicast Peter Enderborg via ffmpeg-devel 2025-08-29 14:04 ` [FFmpeg-devel] [PATCH v3 1/5] configure: Add test_ioctl and test for SIOCGIFINDEX Peter Enderborg via ffmpeg-devel 2025-08-29 17:38 ` [FFmpeg-devel] " Rémi Denis-Courmont via ffmpeg-devel 2025-08-29 14:04 ` [FFmpeg-devel] [PATCH v3 2/5] avformat/udp: Select output interfaces for ipv6 multicast Peter Enderborg via ffmpeg-devel 2025-08-29 14:04 ` Peter Enderborg via ffmpeg-devel [this message] 2025-08-29 14:04 ` [FFmpeg-devel] [PATCH v3 4/5] libavformat: add multicast interface Peter Enderborg via ffmpeg-devel 2025-08-29 14:04 ` [FFmpeg-devel] [PATCH v3 5/5] doc/protocols: Add command-line description for ipv6 options Peter Enderborg via ffmpeg-devel 2025-08-30 0:06 ` [FFmpeg-devel] " Marton Balint via ffmpeg-devel
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20250829140459.3220037-4-peterend@axis.com \ --to=ffmpeg-devel@ffmpeg.org \ --cc=michael@niedermayer.cc \ --cc=peterend@axis.com \ --cc=remi@remlab.net \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git