diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 1 | ||||
-rw-r--r-- | src/pipelines.c | 1 | ||||
-rw-r--r-- | src/sydra.c | 121 | ||||
-rw-r--r-- | src/udp.c | 158 | ||||
-rw-r--r-- | src/udp.h | 43 |
5 files changed, 205 insertions, 119 deletions
diff --git a/src/Makefile b/src/Makefile index ac91c33..eb9f39b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,6 +45,7 @@ C_OBJS := log.o \ string_list.o \ utils.o \ pipelines.o \ + udp.o \ sydra.o C_SRCS := $(C_OBJS:%.o=%.c) diff --git a/src/pipelines.c b/src/pipelines.c index f2f787c..5c687fb 100644 --- a/src/pipelines.c +++ b/src/pipelines.c @@ -43,6 +43,7 @@ #include "log.h" + struct media_elements { const char* name_; diff --git a/src/sydra.c b/src/sydra.c index 680836d..44abc38 100644 --- a/src/sydra.c +++ b/src/sydra.c @@ -53,6 +53,8 @@ #include "daemon.h" #include "utils.h" #include "pipelines.h" +#include "udp.h" + static gboolean sig_handler_terminate(gpointer user_data) { @@ -163,125 +165,6 @@ static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) return TRUE; } - - - - -static void udp_add_client(struct sockaddr_storage *addr, socklen_t addrlen, struct udp_sink* sink) -{ - struct addr_port* client = g_new(struct addr_port, 1); - if(!client) return; - - switch(addr->ss_family) - { - case AF_INET: client->type_ = IPv4; client->port_ = ntohs(((struct sockaddr_in*)addr)->sin_port); break; - case AF_INET6: client->type_ = IPv6; client->port_ = ntohs(((struct sockaddr_in6*)addr)->sin6_port); break; - default: return; - } - - int errcode = getnameinfo((struct sockaddr *)addr, addrlen, client->addr_, sizeof(client->addr_), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); - if (errcode != 0) { - log_printf(WARNING, "getnameinfo() error: %s", gai_strerror(errcode)); - return; - } - gchar* name = gst_element_get_name(sink->udp_); - if(!g_list_find_custom(sink->clients_, client, cmp_addr_port)) { - log_printf(INFO, "adding host %s%c%d to list of receivers for element %s", client->addr_, client->type_ == IPv4 ? ':' : '.', client->port_, name); - g_signal_emit_by_name(G_OBJECT(sink->udp_), "add", client->addr_, client->port_, NULL); - sink->clients_ = g_list_append(sink->clients_, client); - } else { - log_printf(DEBUG, "not adding host %s%c%d to list of receivers for element %s - already added", client->addr_, client->type_ == IPv4 ? ':' : '.', client->port_, name); - g_free(client); - } - g_free(name); -} - -static void udp_remove_client(struct sockaddr_storage *addr, socklen_t addrlen, struct udp_sink* sink) -{ - // TODO: implement this! - gchar* name = gst_element_get_name(sink->udp_); - log_printf(ERROR, "removing client from %s failed! - removing not yet implemented!", name); - g_free(name); -} - -#define UDP_PROTO_MAGIC "SYDRA:" -#define UDP_PROTO_MAGIC_LEN (sizeof(UDP_PROTO_MAGIC)-1) -#define UDP_PROTO_CMD_ADD_CLIENT "add_client\n" -#define UDP_PROTO_CMD_ADD_CLIENT_LEN (sizeof(UDP_PROTO_CMD_ADD_CLIENT)-1) -#define UDP_PROTO_CMD_RM_CLIENT "remove_client\n" -#define UDP_PROTO_CMD_RM_CLIENT_LEN (sizeof(UDP_PROTO_CMD_RM_CLIENT)-1) - -static gboolean on_udp_desc_ready(gint fd, GIOCondition cond, gpointer user_data) -{ - struct udp_sink* sink = (struct udp_sink*)user_data; - if(!sink || !(sink->udp_)) { - log_printf(WARNING, "File descriptor %d is ready for reading - but supplied element is NULL removing callback", fd); - return FALSE; - } - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - u_int8_t buf[2048]; - ssize_t bytes = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &addrlen); - if(bytes < 1) { - if(errno == EINTR) - return TRUE; - - log_printf(WARNING, "Error while receiving UDP data on fd %d, will remove callback", fd); - return FALSE; - } - - if(bytes < UDP_PROTO_MAGIC_LEN || memcmp(UDP_PROTO_MAGIC, buf, UDP_PROTO_MAGIC_LEN)) { - log_printf(DEBUG, "client discovery: ignoring invalid incoming packet"); - return TRUE; - } - - u_int8_t* cmd = &(buf[UDP_PROTO_MAGIC_LEN]); - if(bytes == (UDP_PROTO_MAGIC_LEN+UDP_PROTO_CMD_ADD_CLIENT_LEN) && - !memcmp(UDP_PROTO_CMD_ADD_CLIENT, cmd, UDP_PROTO_CMD_ADD_CLIENT_LEN)) { - udp_add_client(&addr, addrlen, sink); - } else if(bytes == (UDP_PROTO_MAGIC_LEN+UDP_PROTO_CMD_RM_CLIENT_LEN) && - !memcmp(UDP_PROTO_CMD_RM_CLIENT, cmd, UDP_PROTO_CMD_RM_CLIENT_LEN)) { - udp_remove_client(&addr, addrlen, sink); - } else { - log_printf(DEBUG, "client discovery: ignoring invalid command"); - } - return TRUE; -} - -static gboolean attach_udp_descriptor(struct udp_sink* sink, const char* name, const char* prop) -{ - GSocket *sock; - g_object_get(G_OBJECT(sink->udp_), prop, &sock, NULL); - int fd = g_socket_get_fd(sock); - - guint id = g_unix_fd_add(fd, G_IO_IN, on_udp_desc_ready, sink); - if(id <= 0) { - log_printf(ERROR, "Error while adding %s file descriptor (%d) to main loop", name, fd); - return FALSE; - } - log_printf(DEBUG, "successfully installed callback for %s (fd: %d) for reading (id: %d)", name, fd, id); - return TRUE; -} - - -static gboolean attach_udp_descriptors(struct udp_sinks *sinks) -{ - if(!attach_udp_descriptor(&(sinks->rtp_video_), "RTP(video) IPv4", "used-socket") || - !attach_udp_descriptor(&(sinks->rtp_video_), "RTP(video) IPv6", "used-socket-v6") || - !attach_udp_descriptor(&(sinks->rtcp_video_), "RTCP(video) IPv4", "used-socket") || - !attach_udp_descriptor(&(sinks->rtcp_video_), "RTCP(video) IPv6", "used-socket-v6") || - !attach_udp_descriptor(&(sinks->rtp_audio_), "RTP(audio) IPv4", "used-socket") || - !attach_udp_descriptor(&(sinks->rtp_audio_), "RTP(audio) IPv6", "used-socket-v6") || - !attach_udp_descriptor(&(sinks->rtcp_audio_), "RTCP(audio) IPv4", "used-socket") || - !attach_udp_descriptor(&(sinks->rtcp_audio_), "RTCP(audio) IPv6", "used-socket-v6")) { - return FALSE; - } - return TRUE; -} - - - int main_loop(options_t* opt) { log_printf(INFO, "entering main loop"); diff --git a/src/udp.c b/src/udp.c new file mode 100644 index 0000000..bce74d7 --- /dev/null +++ b/src/udp.c @@ -0,0 +1,158 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see <http://www.gnu.org/licenses/>. + * + * In addition, as a special exception, the copyright holders hereby + * grant permission for non-GPL-compatible GStreamer plugins to be used + * and distributed together with GStreamer and sydra. + * This permission goes above and beyond the permissions granted by the + * GPL license sydra is covered by. + */ + +#include "datatypes.h" + +#include <string.h> +#include <glib.h> +#include <glib-unix.h> +#include <gio/gio.h> +#include <gst/gst.h> + +#include "utils.h" +#include "log.h" + + +static void udp_add_client(struct sockaddr_storage *addr, socklen_t addrlen, struct udp_sink* sink) +{ + struct addr_port* client = g_new(struct addr_port, 1); + if(!client) return; + + switch(addr->ss_family) + { + case AF_INET: client->type_ = IPv4; client->port_ = ntohs(((struct sockaddr_in*)addr)->sin_port); break; + case AF_INET6: client->type_ = IPv6; client->port_ = ntohs(((struct sockaddr_in6*)addr)->sin6_port); break; + default: return; + } + + int errcode = getnameinfo((struct sockaddr *)addr, addrlen, client->addr_, sizeof(client->addr_), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + if (errcode != 0) { + log_printf(WARNING, "getnameinfo() error: %s", gai_strerror(errcode)); + return; + } + gchar* name = gst_element_get_name(sink->udp_); + if(!g_list_find_custom(sink->clients_, client, cmp_addr_port)) { + log_printf(INFO, "adding host %s%c%d to list of receivers for element %s", client->addr_, client->type_ == IPv4 ? ':' : '.', client->port_, name); + g_signal_emit_by_name(G_OBJECT(sink->udp_), "add", client->addr_, client->port_, NULL); + sink->clients_ = g_list_append(sink->clients_, client); + } else { + log_printf(DEBUG, "not adding host %s%c%d to list of receivers for element %s - already added", client->addr_, client->type_ == IPv4 ? ':' : '.', client->port_, name); + g_free(client); + } + g_free(name); +} + +static void udp_remove_client(struct sockaddr_storage *addr, socklen_t addrlen, struct udp_sink* sink) +{ + // TODO: implement this! + gchar* name = gst_element_get_name(sink->udp_); + log_printf(ERROR, "removing client from %s failed! - removing not yet implemented!", name); + g_free(name); +} + +#define UDP_PROTO_MAGIC "SYDRA:" +#define UDP_PROTO_MAGIC_LEN (sizeof(UDP_PROTO_MAGIC)-1) +#define UDP_PROTO_CMD_ADD_CLIENT "add_client\n" +#define UDP_PROTO_CMD_ADD_CLIENT_LEN (sizeof(UDP_PROTO_CMD_ADD_CLIENT)-1) +#define UDP_PROTO_CMD_RM_CLIENT "remove_client\n" +#define UDP_PROTO_CMD_RM_CLIENT_LEN (sizeof(UDP_PROTO_CMD_RM_CLIENT)-1) + +static gboolean on_udp_desc_ready(gint fd, GIOCondition cond, gpointer user_data) +{ + struct udp_sink* sink = (struct udp_sink*)user_data; + if(!sink || !(sink->udp_)) { + log_printf(WARNING, "File descriptor %d is ready for reading - but supplied element is NULL removing callback", fd); + return FALSE; + } + + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + u_int8_t buf[2048]; + ssize_t bytes = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &addrlen); + if(bytes < 1) { + if(errno == EINTR) + return TRUE; + + log_printf(WARNING, "Error while receiving UDP data on fd %d, will remove callback", fd); + return FALSE; + } + + if(bytes < UDP_PROTO_MAGIC_LEN || memcmp(UDP_PROTO_MAGIC, buf, UDP_PROTO_MAGIC_LEN)) { + log_printf(DEBUG, "client discovery: ignoring invalid incoming packet"); + return TRUE; + } + + u_int8_t* cmd = &(buf[UDP_PROTO_MAGIC_LEN]); + if(bytes == (UDP_PROTO_MAGIC_LEN+UDP_PROTO_CMD_ADD_CLIENT_LEN) && + !memcmp(UDP_PROTO_CMD_ADD_CLIENT, cmd, UDP_PROTO_CMD_ADD_CLIENT_LEN)) { + udp_add_client(&addr, addrlen, sink); + } else if(bytes == (UDP_PROTO_MAGIC_LEN+UDP_PROTO_CMD_RM_CLIENT_LEN) && + !memcmp(UDP_PROTO_CMD_RM_CLIENT, cmd, UDP_PROTO_CMD_RM_CLIENT_LEN)) { + udp_remove_client(&addr, addrlen, sink); + } else { + log_printf(DEBUG, "client discovery: ignoring invalid command"); + } + return TRUE; +} + +static gboolean attach_udp_descriptor(struct udp_sink* sink, const char* name, const char* prop) +{ + GSocket *sock; + g_object_get(G_OBJECT(sink->udp_), prop, &sock, NULL); + int fd = g_socket_get_fd(sock); + + guint id = g_unix_fd_add(fd, G_IO_IN, on_udp_desc_ready, sink); + if(id <= 0) { + log_printf(ERROR, "Error while adding %s file descriptor (%d) to main loop", name, fd); + return FALSE; + } + log_printf(DEBUG, "successfully installed callback for %s (fd: %d) for reading (id: %d)", name, fd, id); + return TRUE; +} + +gboolean attach_udp_descriptors(struct udp_sinks *sinks) +{ + if(!attach_udp_descriptor(&(sinks->rtp_video_), "RTP(video) IPv4", "used-socket") || + !attach_udp_descriptor(&(sinks->rtp_video_), "RTP(video) IPv6", "used-socket-v6") || + !attach_udp_descriptor(&(sinks->rtcp_video_), "RTCP(video) IPv4", "used-socket") || + !attach_udp_descriptor(&(sinks->rtcp_video_), "RTCP(video) IPv6", "used-socket-v6") || + !attach_udp_descriptor(&(sinks->rtp_audio_), "RTP(audio) IPv4", "used-socket") || + !attach_udp_descriptor(&(sinks->rtp_audio_), "RTP(audio) IPv6", "used-socket-v6") || + !attach_udp_descriptor(&(sinks->rtcp_audio_), "RTCP(audio) IPv4", "used-socket") || + !attach_udp_descriptor(&(sinks->rtcp_audio_), "RTCP(audio) IPv6", "used-socket-v6")) { + return FALSE; + } + return TRUE; +} diff --git a/src/udp.h b/src/udp.h new file mode 100644 index 0000000..dcdc5ca --- /dev/null +++ b/src/udp.h @@ -0,0 +1,43 @@ +/* + * sydra + * + * sydra is a toolbox which allows you to set up multiple bidirectional + * Video/Audio streams from external locations. + * sydra has been written to be used for the Elevate Festival in Graz + * Austria in order to involve external locations to present themselves + * at the festival. + * Sydra is based on GStreamer and is written in C. + * + * + * Copyright (C) 2014 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of sydra. + * + * sydra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * sydra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sydra. If not, see <http://www.gnu.org/licenses/>. + * + * In addition, as a special exception, the copyright holders hereby + * grant permission for non-GPL-compatible GStreamer plugins to be used + * and distributed together with GStreamer and sydra. + * This permission goes above and beyond the permissions granted by the + * GPL license sydra is covered by. + */ + +#ifndef SYDRA_udp_h_INCLUDED +#define SYDRA_udp_h_INCLUDED + +#include "datatypes.h" + +gboolean attach_udp_descriptors(struct udp_sinks *sinks); + +#endif |