summaryrefslogtreecommitdiffstats
path: root/gst/dccp/gstdccpserversink.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/dccp/gstdccpserversink.c')
-rw-r--r--gst/dccp/gstdccpserversink.c435
1 files changed, 435 insertions, 0 deletions
diff --git a/gst/dccp/gstdccpserversink.c b/gst/dccp/gstdccpserversink.c
new file mode 100644
index 00000000..72ffa16c
--- /dev/null
+++ b/gst/dccp/gstdccpserversink.c
@@ -0,0 +1,435 @@
+/* GStreamer
+ * Copyright (C) <2007> Leandro Melo de Sales <leandroal@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-dccpserversink
+ * @see_also: dccpclientsink, dccpclientsrc, dccpserversrc
+ *
+ * This element wait for connections from clients and send data to them.
+ * <ulink url="http://www.linuxfoundation.org/en/Net:DCCP">DCCP</ulink> (Datagram
+ * Congestion Control Protocol) is a Transport Layer protocol like
+ * TCP and UDP.
+ *
+ * <refsect2>
+ * <title>Example pipeline</title>
+ * <para>
+ * |[
+ * gst-launch -v dccpclientsrc host=localhost port=9011 ccid=2 ! decodebin ! alsasink
+ * ]| Client
+ * |[
+ * gst-launch -v filesrc location=music.mp3 ! mp3parse ! dccpserversink port=9011 ccid=2
+ * ]| Server
+ *
+ * This example pipeline will send a MP3 stream to the client using DCCP.
+ * The client will decode the MP3 and play it. Run the server pipeline
+ * first than the client pipeline. If you want, you can run more than one dccpclientsrc
+ * to connect to the same server (see wait-connections property at dccpserversink).
+ * </para>
+ * </refsect2>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdccpserversink.h"
+#include "gstdccp.h"
+#include <string.h> /* memset */
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+/* signals */
+enum
+{
+ SIGNAL_CONNECTED,
+ LAST_SIGNAL
+};
+
+/* properties */
+enum
+{
+ PROP_0,
+ PROP_PORT,
+ PROP_CLIENT_SOCK_FD,
+ PROP_CCID,
+ PROP_CLOSED,
+ PROP_WAIT_CONNECTIONS
+};
+
+pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static gboolean gst_dccp_server_sink_stop (GstBaseSink * bsink);
+
+GST_DEBUG_CATEGORY_STATIC (dccpserversink_debug);
+
+static const GstElementDetails gst_dccp_server_sink_details =
+GST_ELEMENT_DETAILS ("DCCP server sink",
+ "Sink/Network",
+ "Send data as a server over the network via DCCP",
+ "E-Phone Team at Federal University of Campina Grande <leandroal@gmail.com>");
+
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_BOILERPLATE (GstDCCPServerSink, gst_dccp_server_sink, GstBaseSink,
+ GST_TYPE_BASE_SINK);
+
+static guint gst_dccp_server_sink_signals[LAST_SIGNAL] = { 0 };
+
+static Client *
+gst_dccp_server_create_client (GstElement * element, int socket)
+{
+ Client *client = (Client *) g_malloc (sizeof (Client));
+ client->socket = socket;
+ client->pksize = gst_dccp_get_max_packet_size (element, client->socket);
+ client->flow_status = GST_FLOW_OK;
+
+ /* the socket is connected */
+ g_signal_emit (element, gst_dccp_server_sink_signals[SIGNAL_CONNECTED], 0,
+ socket);
+
+ return client;
+}
+
+static void *
+gst_dccp_server_accept_new_clients (void *arg)
+{
+ GstDCCPServerSink *sink = (GstDCCPServerSink *) arg;
+ int newsockfd;
+ Client *client;
+
+ while (1) {
+ newsockfd =
+ gst_dccp_server_wait_connections (GST_ELEMENT (sink), sink->sock_fd);
+
+ client = gst_dccp_server_create_client (GST_ELEMENT (sink), newsockfd);
+
+ pthread_mutex_lock (&lock);
+ sink->clients = g_list_append (sink->clients, client);
+ pthread_mutex_unlock (&lock);
+ }
+}
+
+static void *
+gst_dccp_server_send_buffer (void *arg)
+{
+ Client *client = (Client *) arg;
+ GstDCCPServerSink *sink = client->server;
+ GstBuffer *buf = client->buf;
+ int client_sock_fd = client->socket;
+ int pksize = client->pksize;
+
+ if (gst_dccp_send_buffer (GST_ELEMENT (sink), buf, client_sock_fd,
+ pksize) == GST_FLOW_ERROR) {
+ client->flow_status = GST_FLOW_ERROR;
+ }
+ return NULL;
+}
+
+/* Remove clients with problems to send */
+static void *
+gst_dccp_server_delete_dead_clients (void *arg)
+{
+ GstDCCPServerSink *sink = (GstDCCPServerSink *) arg;
+ int i;
+ GList *tmp = NULL;
+
+ pthread_mutex_lock (&lock);
+ for (i = 0; i < g_list_length (sink->clients); i++) {
+ Client *client = (Client *) g_list_nth_data (sink->clients, i);
+ if (client->flow_status == GST_FLOW_OK) {
+ tmp = g_list_append (tmp, client);
+ } else {
+ close (client->socket);
+ g_free (client);
+ }
+ }
+ g_list_free (sink->clients);
+ sink->clients = tmp;
+ pthread_mutex_unlock (&lock);
+ return 0;
+}
+
+static void
+gst_dccp_server_sink_init (GstDCCPServerSink * this,
+ GstDCCPServerSinkClass * g_class)
+{
+ this->port = DCCP_DEFAULT_PORT;
+ this->sock_fd = DCCP_DEFAULT_SOCK_FD;
+ this->client_sock_fd = DCCP_DEFAULT_CLIENT_SOCK_FD;
+ this->closed = DCCP_DEFAULT_CLOSED;
+ this->ccid = DCCP_DEFAULT_CCID;
+ this->wait_connections = DCCP_DEFAULT_WAIT_CONNECTIONS;
+ this->clients = NULL;
+}
+
+static gboolean
+gst_dccp_server_sink_start (GstBaseSink * bsink)
+{
+ GstDCCPServerSink *sink = GST_DCCP_SERVER_SINK (bsink);
+ int ret = 1;
+ Client *client;
+
+ /* create socket */
+ if ((sink->sock_fd = gst_dccp_create_new_socket (GST_ELEMENT (sink))) < 0) {
+ return FALSE;
+ }
+
+ /* make address reusable */
+ if (setsockopt (sink->sock_fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &ret, sizeof (ret)) < 0) {
+ GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL),
+ ("Could not setsockopt: %s", g_strerror (errno)));
+ return FALSE;
+ }
+
+ /* name the server socket */
+ memset (&sink->server_sin, 0, sizeof (sink->server_sin));
+ sink->server_sin.sin_family = AF_INET; /* network socket */
+ sink->server_sin.sin_port = htons (sink->port); /* on port */
+ sink->server_sin.sin_addr.s_addr = htonl (INADDR_ANY); /* for hosts */
+
+ if (!gst_dccp_bind_server_socket (GST_ELEMENT (sink), sink->sock_fd,
+ sink->server_sin)) {
+ return FALSE;
+ }
+
+ if (!gst_dccp_set_ccid (GST_ELEMENT (sink), sink->sock_fd, sink->ccid)) {
+ return FALSE;
+ }
+
+ if (!gst_dccp_listen_server_socket (GST_ELEMENT (sink), sink->sock_fd)) {
+ return FALSE;
+ }
+
+
+ if (sink->client_sock_fd == DCCP_DEFAULT_CLIENT_SOCK_FD) {
+ sink->client_sock_fd =
+ gst_dccp_server_wait_connections (GST_ELEMENT (sink), sink->sock_fd);
+ }
+
+ if (sink->client_sock_fd == -1) {
+ return FALSE;
+ }
+
+ client =
+ gst_dccp_server_create_client (GST_ELEMENT (sink), sink->client_sock_fd);
+ sink->clients = g_list_append (sink->clients, client);
+
+ pthread_mutex_init (&lock, NULL);
+
+ if (sink->wait_connections == TRUE) {
+ pthread_create (&accept_thread_id, NULL, gst_dccp_server_accept_new_clients,
+ sink);
+ pthread_detach (accept_thread_id);
+ }
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_dccp_server_sink_render (GstBaseSink * bsink, GstBuffer * buf)
+{
+ GstDCCPServerSink *sink = GST_DCCP_SERVER_SINK (bsink);
+
+ pthread_t thread_id;
+ int i;
+
+ pthread_mutex_lock (&lock);
+
+ for (i = 0; i < g_list_length (sink->clients); i++) {
+ Client *client = (Client *) g_list_nth_data (sink->clients, i);
+ client->buf = buf;
+ client->server = sink;
+
+ if (client->flow_status == GST_FLOW_OK) {
+ pthread_create (&thread_id, NULL, gst_dccp_server_send_buffer,
+ (void *) client);
+ pthread_detach (thread_id);
+ } else {
+ pthread_create (&thread_id, NULL, gst_dccp_server_delete_dead_clients,
+ (void *) sink);
+ pthread_detach (thread_id);
+ }
+ }
+
+ pthread_mutex_unlock (&lock);
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_dccp_server_sink_stop (GstBaseSink * bsink)
+{
+ GstDCCPServerSink *sink;
+ int i;
+ sink = GST_DCCP_SERVER_SINK (bsink);
+
+ if (sink->wait_connections == TRUE) {
+ pthread_cancel (accept_thread_id);
+ }
+
+ if (sink->sock_fd != -1 && sink->closed == TRUE) {
+ GST_DEBUG_OBJECT (sink, "closing socket");
+ close (sink->sock_fd);
+ sink->sock_fd = -1;
+ }
+
+ pthread_mutex_lock (&lock);
+ for (i = 0; i < g_list_length (sink->clients); i++) {
+ Client *client = (Client *) g_list_nth_data (sink->clients, i);
+ close (client->socket);
+ g_free (client);
+ }
+ pthread_mutex_unlock (&lock);
+
+ return TRUE;
+}
+
+static void
+gst_dccp_server_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sinktemplate));
+
+ gst_element_class_set_details (element_class, &gst_dccp_server_sink_details);
+}
+
+/*
+ * Set the value of a property for the server sink.
+ */
+static void
+gst_dccp_server_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDCCPServerSink *sink = GST_DCCP_SERVER_SINK (object);
+
+ switch (prop_id) {
+ case PROP_PORT:
+ sink->port = g_value_get_int (value);
+ break;
+ case PROP_CLIENT_SOCK_FD:
+ sink->client_sock_fd = g_value_get_int (value);
+ break;
+ case PROP_CLOSED:
+ sink->closed = g_value_get_boolean (value);
+ break;
+ case PROP_WAIT_CONNECTIONS:
+ sink->wait_connections = g_value_get_boolean (value);
+ break;
+ case PROP_CCID:
+ sink->ccid = g_value_get_int (value);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+gst_dccp_server_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstDCCPServerSink *sink = GST_DCCP_SERVER_SINK (object);
+
+ switch (prop_id) {
+ case PROP_PORT:
+ g_value_set_int (value, sink->port);
+ break;
+ case PROP_CLIENT_SOCK_FD:
+ g_value_set_int (value, sink->client_sock_fd);
+ break;
+ case PROP_CLOSED:
+ g_value_set_boolean (value, sink->closed);
+ break;
+ case PROP_WAIT_CONNECTIONS:
+ g_value_set_boolean (value, sink->wait_connections);
+ break;
+ case PROP_CCID:
+ g_value_set_int (value, sink->ccid);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_dccp_server_sink_class_init (GstDCCPServerSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstBaseSinkClass *gstbasesink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+
+ gobject_class->set_property = gst_dccp_server_sink_set_property;
+ gobject_class->get_property = gst_dccp_server_sink_get_property;
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PORT,
+ g_param_spec_int ("port", "Port",
+ "The port to receive the packets from, 0=allocate", 0, G_MAXUINT16,
+ DCCP_DEFAULT_PORT, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_CLIENT_SOCK_FD,
+ g_param_spec_int ("sockfd", "Socket fd",
+ "The client socket file descriptor", -1, G_MAXINT,
+ DCCP_DEFAULT_CLIENT_SOCK_FD, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_CLOSED,
+ g_param_spec_boolean ("close-socket", "Close",
+ "Close socket at end of stream",
+ DCCP_DEFAULT_CLOSED, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_CCID,
+ g_param_spec_int ("ccid", "CCID",
+ "The Congestion Control IDentified to be used", 2, G_MAXINT,
+ DCCP_DEFAULT_CCID, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_WAIT_CONNECTIONS,
+ g_param_spec_boolean ("wait-connections", "Wait connections",
+ "Wait for many client connections",
+ DCCP_DEFAULT_WAIT_CONNECTIONS, G_PARAM_READWRITE));
+
+
+ /* signals */
+ /**
+ * GstDccpServerSink::connected:
+ * @src: the gstdccpserversink instance
+ * @fd: the connected socket fd
+ *
+ * Reports that the element has connected, giving the fd of the socket
+ */
+ gst_dccp_server_sink_signals[SIGNAL_CONNECTED] =
+ g_signal_new ("connected", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GstDCCPServerSinkClass, connected), NULL, NULL,
+ gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+ gstbasesink_class->start = gst_dccp_server_sink_start;
+ gstbasesink_class->stop = gst_dccp_server_sink_stop;
+ gstbasesink_class->render = gst_dccp_server_sink_render;
+
+ GST_DEBUG_CATEGORY_INIT (dccpserversink_debug, "dccpserversink", 0,
+ "DCCP Server Sink");
+}