mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	rtp: add SAP parsing
This commit is contained in:
		
							parent
							
								
									d21fc6f378
								
							
						
					
					
						commit
						38f908e758
					
				
					 3 changed files with 226 additions and 54 deletions
				
			
		| 
						 | 
				
			
			@ -22,18 +22,24 @@
 | 
			
		|||
 * DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include <spa/param/audio/format-utils.h>
 | 
			
		||||
#include <spa/utils/hook.h>
 | 
			
		||||
#include <spa/debug/mem.h>
 | 
			
		||||
 | 
			
		||||
#include <pipewire/pipewire.h>
 | 
			
		||||
#include <pipewire/private.h>
 | 
			
		||||
#include <spa/param/audio/format-utils.h>
 | 
			
		||||
#include <spa/utils/hook.h>
 | 
			
		||||
 | 
			
		||||
#include <module-rtp/sap.h>
 | 
			
		||||
#include <module-rtp/rtp.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** \page page_module_rtp_source PipeWire Module: RTP source
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +68,8 @@
 | 
			
		|||
 * context.modules = [
 | 
			
		||||
 *  {   name = libpipewire-module-rtp-source
 | 
			
		||||
 *      args = {
 | 
			
		||||
 *          local.ip = 0.0.0.0
 | 
			
		||||
 *          sess.latency.msec = 5000
 | 
			
		||||
 *          local.ip = 224.0.0.56
 | 
			
		||||
 *          sess.latency.msec = 200
 | 
			
		||||
 *          source.name = "RTP Source"
 | 
			
		||||
 *          source.props = {
 | 
			
		||||
 *             node.name = "rtp-source"
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +86,7 @@
 | 
			
		|||
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
 | 
			
		||||
#define PW_LOG_TOPIC_DEFAULT mod_topic
 | 
			
		||||
 | 
			
		||||
#define SAP_DEFAULT_IP		"0.0.0.0"
 | 
			
		||||
#define SAP_DEFAULT_IP		"224.0.0.56"
 | 
			
		||||
#define SAP_DEFAULT_PORT	9875
 | 
			
		||||
#define DEFAULT_SESS_LATENCY	200
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +102,6 @@ struct impl {
 | 
			
		|||
	struct spa_hook core_listener;
 | 
			
		||||
	struct spa_hook core_proxy_listener;
 | 
			
		||||
 | 
			
		||||
	int sap_fd;
 | 
			
		||||
	struct spa_source *sap_source;
 | 
			
		||||
 | 
			
		||||
	struct pw_stream *playback;
 | 
			
		||||
| 
						 | 
				
			
			@ -269,17 +274,83 @@ static int rtp_source_setup(struct impl *data)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int make_mcast_socket(struct sockaddr *sa, socklen_t salen)
 | 
			
		||||
static int parse_sdp(struct impl *impl, const char *sdp)
 | 
			
		||||
{
 | 
			
		||||
	pw_log_info("%s", sdp);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_sap(struct impl *impl, const void *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct sap_header *header;
 | 
			
		||||
	const char *mime;
 | 
			
		||||
	const char *sdp;
 | 
			
		||||
 | 
			
		||||
	if (len < 8)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	header = (struct sap_header*) data;
 | 
			
		||||
	if (header->v != 1)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	mime = SPA_PTROFF(data, 8, char);
 | 
			
		||||
	if (spa_strstartswith(mime, "v=0"))
 | 
			
		||||
		sdp = mime;
 | 
			
		||||
	else if (spa_streq(mime, "application/sdp"))
 | 
			
		||||
		sdp = SPA_PTROFF(mime, strlen(mime)+1, char);
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return parse_sdp(impl, sdp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_sap_io(void *data, int fd, uint32_t mask)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = data;
 | 
			
		||||
 | 
			
		||||
	if (mask & SPA_IO_IN) {
 | 
			
		||||
		uint8_t buffer[2048];
 | 
			
		||||
		ssize_t len;
 | 
			
		||||
 | 
			
		||||
		pw_log_info("got sap");
 | 
			
		||||
		if ((len = recv(fd, buffer, sizeof(buffer), 0)) < 0) {
 | 
			
		||||
			pw_log_warn("recv error: %m");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if ((size_t)len >= sizeof(buffer))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		buffer[len] = 0;
 | 
			
		||||
		parse_sap(impl, buffer, len);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int start_sap_listener(struct impl *impl)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in sa4;
 | 
			
		||||
	struct sockaddr_in6 sa6;
 | 
			
		||||
	struct sockaddr *sa;
 | 
			
		||||
	socklen_t salen;
 | 
			
		||||
	int af, fd, val, res;
 | 
			
		||||
 | 
			
		||||
	af = sa->sa_family;
 | 
			
		||||
	if (inet_pton(AF_INET, impl->local_ip, &sa4.sin_addr) > 0) {
 | 
			
		||||
		af = sa4.sin_family = AF_INET;
 | 
			
		||||
		sa4.sin_port = htons(impl->local_port);
 | 
			
		||||
		sa = (struct sockaddr*) &sa4;
 | 
			
		||||
		salen = sizeof(sa4);
 | 
			
		||||
	} else if (inet_pton(AF_INET6, impl->local_ip, &sa6.sin6_addr) > 0) {
 | 
			
		||||
		af = sa6.sin6_family = AF_INET6;
 | 
			
		||||
		sa6.sin6_port = htons(impl->local_port);
 | 
			
		||||
		sa = (struct sockaddr*) &sa6;
 | 
			
		||||
		salen = sizeof(sa6);
 | 
			
		||||
	} else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if ((fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) {
 | 
			
		||||
		pw_log_error("socket failed: %m");
 | 
			
		||||
		return -errno;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef SO_TIMESTAMP
 | 
			
		||||
	val = 1;
 | 
			
		||||
	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(val)) < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -294,11 +365,9 @@ static int make_mcast_socket(struct sockaddr *sa, socklen_t salen)
 | 
			
		|||
		pw_log_error("setsockopt failed: %m");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res = 0;
 | 
			
		||||
	if (af == AF_INET) {
 | 
			
		||||
		static const uint32_t ipv4_mcast_mask = 0xe0000000;
 | 
			
		||||
 | 
			
		||||
		if ((ntohl(((const struct sockaddr_in*) sa)->sin_addr.s_addr) &
 | 
			
		||||
					ipv4_mcast_mask) == ipv4_mcast_mask) {
 | 
			
		||||
			struct ip_mreq mr4;
 | 
			
		||||
| 
						 | 
				
			
			@ -324,57 +393,24 @@ static int make_mcast_socket(struct sockaddr *sa, socklen_t salen)
 | 
			
		|||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (bind(fd, sa, salen) < 0) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		pw_log_warn("bind() failed: %m");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	return fd;
 | 
			
		||||
 | 
			
		||||
	pw_log_info("starting SAP listener");
 | 
			
		||||
	impl->sap_source = pw_loop_add_io(impl->loop, fd,
 | 
			
		||||
				SPA_IO_IN, true, on_sap_io, impl);
 | 
			
		||||
	if (impl->sap_source == NULL) {
 | 
			
		||||
		res = -errno;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	close(fd);
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_sap_io(void *data, int fd, uint32_t mask)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = data;
 | 
			
		||||
 | 
			
		||||
	if (mask & SPA_IO_IN) {
 | 
			
		||||
		pw_log_info("got sap");
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int start_sap_listener(struct impl *impl)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in sa4;
 | 
			
		||||
	struct sockaddr_in6 sa6;
 | 
			
		||||
	struct sockaddr *sa;
 | 
			
		||||
	socklen_t salen;
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	if (inet_pton(AF_INET, impl->local_ip, &sa4.sin_addr) > 0) {
 | 
			
		||||
		sa4.sin_family = AF_INET;
 | 
			
		||||
		sa4.sin_port = htons(impl->local_port);
 | 
			
		||||
		sa = (struct sockaddr*) &sa4;
 | 
			
		||||
		salen = sizeof(sa4);
 | 
			
		||||
	} else if (inet_pton(AF_INET6, impl->local_ip, &sa6.sin6_addr) > 0) {
 | 
			
		||||
		sa6.sin6_family = AF_INET6;
 | 
			
		||||
		sa6.sin6_port = htons(impl->local_port);
 | 
			
		||||
		sa = (struct sockaddr*) &sa6;
 | 
			
		||||
		salen = sizeof(sa6);
 | 
			
		||||
	} else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if ((fd = make_mcast_socket(sa, salen)) < 0)
 | 
			
		||||
		return fd;
 | 
			
		||||
 | 
			
		||||
	impl->sap_fd = fd;
 | 
			
		||||
	impl->sap_source = pw_loop_add_io(impl->loop, impl->sap_fd,
 | 
			
		||||
				SPA_IO_IN, false, on_sap_io, impl);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct spa_dict_item module_info[] = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										78
									
								
								src/modules/module-rtp/rtp.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/modules/module-rtp/rtp.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* PipeWire
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
			
		||||
 * copy of this software and associated documentation files (the "Software"),
 | 
			
		||||
 * to deal in the Software without restriction, including without limitation
 | 
			
		||||
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
			
		||||
 * and/or sell copies of the Software, and to permit persons to whom the
 | 
			
		||||
 * Software is furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice (including the next
 | 
			
		||||
 * paragraph) shall be included in all copies or substantial portions of the
 | 
			
		||||
 * Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
			
		||||
 * DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef PIPEWIRE_RTP_H
 | 
			
		||||
#define PIPEWIRE_RTP_H
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct rtp_header {
 | 
			
		||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
 | 
			
		||||
	unsigned cc:4;
 | 
			
		||||
	unsigned x:1;
 | 
			
		||||
	unsigned p:1;
 | 
			
		||||
	unsigned v:2;
 | 
			
		||||
 | 
			
		||||
	unsigned pt:7;
 | 
			
		||||
	unsigned m:1;
 | 
			
		||||
#elif __BYTE_ORDER == __BIG_ENDIAN
 | 
			
		||||
	unsigned v:2;
 | 
			
		||||
	unsigned p:1;
 | 
			
		||||
	unsigned x:1;
 | 
			
		||||
	unsigned cc:4;
 | 
			
		||||
 | 
			
		||||
	unsigned m:1;
 | 
			
		||||
	unsigned pt:7;
 | 
			
		||||
#else
 | 
			
		||||
#error "Unknown byte order"
 | 
			
		||||
#endif
 | 
			
		||||
	uint16_t sequence_number;
 | 
			
		||||
	uint32_t timestamp;
 | 
			
		||||
	uint32_t ssrc;
 | 
			
		||||
	uint32_t csrc[0];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct rtp_payload {
 | 
			
		||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
 | 
			
		||||
	unsigned frame_count:4;
 | 
			
		||||
	unsigned rfa0:1;
 | 
			
		||||
	unsigned is_last_fragment:1;
 | 
			
		||||
	unsigned is_first_fragment:1;
 | 
			
		||||
	unsigned is_fragmented:1;
 | 
			
		||||
#elif __BYTE_ORDER == __BIG_ENDIAN
 | 
			
		||||
	unsigned is_fragmented:1;
 | 
			
		||||
	unsigned is_first_fragment:1;
 | 
			
		||||
	unsigned is_last_fragment:1;
 | 
			
		||||
	unsigned rfa0:1;
 | 
			
		||||
	unsigned frame_count:4;
 | 
			
		||||
#endif
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* PIPEWIRE_RTP_H */
 | 
			
		||||
							
								
								
									
										58
									
								
								src/modules/module-rtp/sap.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/modules/module-rtp/sap.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/* PipeWire
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright © 2022 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
			
		||||
 * copy of this software and associated documentation files (the "Software"),
 | 
			
		||||
 * to deal in the Software without restriction, including without limitation
 | 
			
		||||
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
			
		||||
 * and/or sell copies of the Software, and to permit persons to whom the
 | 
			
		||||
 * Software is furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice (including the next
 | 
			
		||||
 * paragraph) shall be included in all copies or substantial portions of the
 | 
			
		||||
 * Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
			
		||||
 * DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef PIPEWIRE_SAP_H
 | 
			
		||||
#define PIPEWIRE_SAP_H
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct sap_header {
 | 
			
		||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
 | 
			
		||||
	unsigned c:1;
 | 
			
		||||
	unsigned e:1;
 | 
			
		||||
	unsigned t:1;
 | 
			
		||||
	unsigned r:1;
 | 
			
		||||
	unsigned a:1;
 | 
			
		||||
	unsigned v:3;
 | 
			
		||||
#elif __BYTE_ORDER == __BIG_ENDIAN
 | 
			
		||||
	unsigned v:3;
 | 
			
		||||
	unsigned a:1;
 | 
			
		||||
	unsigned r:1;
 | 
			
		||||
	unsigned t:1;
 | 
			
		||||
	unsigned e:1;
 | 
			
		||||
	unsigned c:1;
 | 
			
		||||
#else
 | 
			
		||||
#error "Unknown byte order"
 | 
			
		||||
#endif
 | 
			
		||||
	uint8_t auth_len;
 | 
			
		||||
	uint16_t msg_id_hash;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* PIPEWIRE_SAP_H */
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue