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 4/5] libavformat: add multicast interface Date: Fri, 29 Aug 2025 16:04:58 +0200 Message-ID: <20250829140459.3220037-5-peterend@axis.com> (raw) In-Reply-To: <20250829140459.3220037-1-peterend@axis.com> Add a commandline option for selecting interface by name. This is useful when your multicast routing table for sending multicast does not match the interface reciving is wanted. Signed-off-by: Peter Enderborg <peterend@axis.com> --- libavformat/rtpproto.c | 4 ++++ libavformat/rtsp.c | 3 +++ libavformat/rtsp.h | 1 + libavformat/udp.c | 40 ++++++++++++++++++++++++++++++---------- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index c6abd7a02c..74c920ddc8 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; + char *multicast_interface; int multicast_max_join; } RTPContext; @@ -83,6 +84,7 @@ static const AVOption options[] = { { "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 }, + { "multicast_interface", "Name of network inteface to join multicast group", OFFSET(multicast_interface), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, { NULL } }; @@ -202,6 +204,8 @@ static void build_udp_url(RTPContext *s, 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); + if (s->multicast_interface) + url_add_option(buf, buf_size, "multicast_interface=%s", s->multicast_interface); } /** diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 08ce628b0c..59e39fa97c 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -82,6 +82,7 @@ { "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 }, \ + { "multicast_interface", "Name of network inteface to join multicast group", OFFSET(multicast_interface), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, \ { "pkt_size", "Underlying protocol send packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = 1472 }, -1, INT_MAX, ENC } \ @@ -141,6 +142,8 @@ static AVDictionary *map_to_opts(RTSPState *rt) 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); + if (rt->multicast_interface) + av_dict_set(&opts, "multicast_interface", rt->multicast_interface, 0); return opts; } diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 626114fb5a..e48af2feca 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -420,6 +420,7 @@ typedef struct RTSPState { int pkt_size; char *localaddr; int multicast_max_join; + char *multicast_interface; /** * Options used for TLS based RTSP streams. diff --git a/libavformat/udp.c b/libavformat/udp.c index 9c2f904e50..004d2a6a1c 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -126,6 +126,7 @@ typedef struct UDPContext { int remaining_in_dg; char *localaddr; int multicast_max_join; + char *multicast_interface; int timeout; struct sockaddr_storage local_addr_storage; char *sources; @@ -146,6 +147,7 @@ static const AVOption options[] = { { "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 }, + { "multicast_interface", "Name of network inteface to join multicast group", OFFSET(multicast_interface), AV_OPT_TYPE_STRING, { .str = NULL }, .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 }, @@ -259,7 +261,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, - int multicast_max_join, void *logctx) + int multicast_max_join, + char *interfacename, void *logctx) { struct in6_addr anyipv6 = IN6ADDR_ANY_INIT; struct ifaddrs *ifal=NULL,*ife=NULL; @@ -275,11 +278,13 @@ static int udp_ipv6_multicast_iterate(int sockfd, struct sockaddr_in6 *addr, 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) { - membership_changed = 1; - multicast_max_join--; + if (!interfacename || !strncmp(interfacename, ife->ifa_name, IFNAMSIZ)) { + if (udp_ipv6_mc_devname_membership(mop,ife->ifa_name, + iindex_fd, &addr->sin6_addr, + sockfd, logctx) == 1) { + membership_changed = 1; + multicast_max_join--; + } } } } @@ -298,7 +303,8 @@ static int udp_ipv6_multicast_iterate(int sockfd, struct sockaddr_in6 *addr, static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, struct sockaddr *local_addr, - int multicast_max_join, void *logctx) + int multicast_max_join, + char *multicast_interface,void *logctx) { #ifdef IP_ADD_MEMBERSHIP if (addr->sa_family == AF_INET) { @@ -313,6 +319,8 @@ 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_interface) + ff_log_net_error(logctx, AV_LOG_WARNING, "multicast_inteface is not used in ip4"); if (multicast_max_join) ff_log_net_error(logctx, AV_LOG_WARNING, "multicast_max_join is not used in ip4"); } @@ -337,6 +345,7 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, (struct sockaddr_in6 *)local_addr, IPV6_JOIN_GROUP, multicast_max_join, + multicast_interface, logctx); } #else @@ -354,7 +363,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, - int multicast_max_join, void *logctx) + int multicast_max_join, + char *multicast_interface,void *logctx) { #ifdef IP_DROP_MEMBERSHIP if (addr->sa_family == AF_INET) { @@ -391,6 +401,7 @@ static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr, (struct sockaddr_in6 *)local_addr, IPV6_LEAVE_GROUP, multicast_max_join, + multicast_interface, logctx); } #else @@ -922,6 +933,14 @@ static int udp_open(URLContext *h, const char *uri, int flags) goto fail; } } + if (av_find_info_tag(buf, sizeof(buf), "multicast_interface", p)) { + av_freep(&s->multicast_interface); + s->multicast_interface = av_strdup(buf); + if (!s->multicast_interface) { + ret = AVERROR(ENOMEM); + goto fail; + } + } if (av_find_info_tag(buf, sizeof(buf), "sources", p)) { if ((ret = ff_ip_parse_sources(h, buf, &s->filters)) < 0) goto fail; @@ -1051,7 +1070,8 @@ static int udp_open(URLContext *h, const char *uri, int flags) } else { if ((ret = udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr, (struct sockaddr *)&s->local_addr_storage, - s->multicast_max_join, h)) < 0) + s->multicast_max_join, + s->multicast_interface, h)) < 0) goto fail; } if (s->filters.nb_exclude_addrs) { @@ -1306,7 +1326,7 @@ 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, - s->multicast_max_join, h); + s->multicast_max_join, s->multicast_interface, 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 ` [FFmpeg-devel] [PATCH v3 3/5] libavformat: udp.c Add support for multi or single join 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 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-5-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