rtp: add SAP parsing

This commit is contained in:
Wim Taymans 2022-10-04 13:19:14 +02:00
parent d21fc6f378
commit 38f908e758
3 changed files with 226 additions and 54 deletions

View file

@ -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[] = {

View 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 */

View 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 */