2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
24 #include <gst/gst-i18n-plugin.h>
26 #include <sys/ioctl.h>
28 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
29 #include <sys/filio.h>
33 #include "gsttcpserversink.h"
34 #include "gsttcp-marshal.h"
38 /* elementfactory information */
39 static GstElementDetails gst_tcpserversink_details =
40 GST_ELEMENT_DETAILS ("TCP Server sink",
42 "Send data as a server over the network via TCP",
43 "Thomas Vander Stichele <thomas at apestaart dot org>");
45 GST_DEBUG_CATEGORY (tcpserversink_debug);
46 #define GST_CAT_DEFAULT (tcpserversink_debug)
55 static void gst_tcpserversink_base_init (gpointer g_class);
56 static void gst_tcpserversink_class_init (GstTCPServerSink * klass);
57 static void gst_tcpserversink_init (GstTCPServerSink * tcpserversink);
58 static void gst_tcpserversink_finalize (GObject * gobject);
60 static gboolean gst_tcpserversink_handle_wait (GstMultiFdSink * sink,
62 static gboolean gst_tcpserversink_init_send (GstMultiFdSink * this);
63 static gboolean gst_tcpserversink_close (GstMultiFdSink * this);
64 static void gst_tcpserversink_removed (GstMultiFdSink * sink, int fd);
66 static void gst_tcpserversink_set_property (GObject * object, guint prop_id,
67 const GValue * value, GParamSpec * pspec);
68 static void gst_tcpserversink_get_property (GObject * object, guint prop_id,
69 GValue * value, GParamSpec * pspec);
72 static GstMultiFdSinkClass *parent_class = NULL;
75 gst_tcpserversink_get_type (void)
77 static GType tcpserversink_type = 0;
80 if (!tcpserversink_type) {
81 static const GTypeInfo tcpserversink_info = {
82 sizeof (GstTCPServerSinkClass),
83 gst_tcpserversink_base_init,
85 (GClassInitFunc) gst_tcpserversink_class_init,
88 sizeof (GstTCPServerSink),
90 (GInstanceInitFunc) gst_tcpserversink_init,
95 g_type_register_static (GST_TYPE_MULTIFDSINK, "GstTCPServerSink",
96 &tcpserversink_info, 0);
98 return tcpserversink_type;
102 gst_tcpserversink_base_init (gpointer g_class)
104 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
106 gst_element_class_set_details (element_class, &gst_tcpserversink_details);
110 gst_tcpserversink_class_init (GstTCPServerSink * klass)
112 GObjectClass *gobject_class;
113 GstElementClass *gstelement_class;
114 GstMultiFdSinkClass *gstmultifdsink_class;
116 gobject_class = (GObjectClass *) klass;
117 gstelement_class = (GstElementClass *) klass;
118 gstmultifdsink_class = (GstMultiFdSinkClass *) klass;
120 parent_class = g_type_class_ref (GST_TYPE_MULTIFDSINK);
122 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HOST,
123 g_param_spec_string ("host", "host", "The host/IP to send the packets to",
124 TCP_DEFAULT_HOST, G_PARAM_READWRITE));
125 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT,
126 g_param_spec_int ("port", "port", "The port to send the packets to",
127 0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT, G_PARAM_READWRITE));
129 gobject_class->set_property = gst_tcpserversink_set_property;
130 gobject_class->get_property = gst_tcpserversink_get_property;
131 gobject_class->finalize = gst_tcpserversink_finalize;
133 gstmultifdsink_class->init = gst_tcpserversink_init_send;
134 gstmultifdsink_class->wait = gst_tcpserversink_handle_wait;
135 gstmultifdsink_class->close = gst_tcpserversink_close;
136 gstmultifdsink_class->removed = gst_tcpserversink_removed;
138 GST_DEBUG_CATEGORY_INIT (tcpserversink_debug, "tcpserversink", 0, "TCP sink");
142 gst_tcpserversink_init (GstTCPServerSink * this)
144 this->server_port = TCP_DEFAULT_PORT;
145 /* should support as minimum 576 for IPV4 and 1500 for IPV6 */
146 /* this->mtu = 1500; */
147 this->host = g_strdup (TCP_DEFAULT_HOST);
149 this->server_sock.fd = -1;
153 gst_tcpserversink_finalize (GObject * gobject)
155 GstTCPServerSink *this = GST_TCPSERVERSINK (gobject);
160 /* handle a read request on the server,
161 * which indicates a new client connection */
163 gst_tcpserversink_handle_server_read (GstTCPServerSink * sink)
167 struct sockaddr_in client_address;
168 int client_address_len;
170 /* For some stupid reason, client_address and client_address_len has to be
172 memset (&client_address, 0, sizeof (client_address));
173 client_address_len = 0;
176 accept (sink->server_sock.fd, (struct sockaddr *) &client_address,
177 &client_address_len);
178 if (client_sock_fd == -1) {
179 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
180 ("Could not accept client on server socket %d: %s (%d)",
181 sink->server_sock.fd, g_strerror (errno), errno));
185 gst_multifdsink_add (GST_MULTIFDSINK (sink), client_sock_fd);
187 GST_DEBUG_OBJECT (sink, "added new client ip %s with fd %d",
188 inet_ntoa (client_address.sin_addr), client_sock_fd);
194 gst_tcpserversink_removed (GstMultiFdSink * sink, int fd)
196 GstTCPServerSink *this = GST_TCPSERVERSINK (sink);
198 GST_LOG_OBJECT (this, "closing fd %d", fd);
199 if (close (fd) < 0) {
200 GST_WARNING_OBJECT (this, "error closing fd %d: %s", fd,
206 gst_tcpserversink_handle_wait (GstMultiFdSink * sink, GstFDSet * set)
208 GstTCPServerSink *this = GST_TCPSERVERSINK (sink);
210 if (gst_fdset_fd_can_read (set, &this->server_sock)) {
211 /* handle new client connection on server socket */
212 if (!gst_tcpserversink_handle_server_read (this)) {
213 GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL),
214 ("client connection failed: %s", g_strerror (errno)));
222 gst_tcpserversink_set_property (GObject * object, guint prop_id,
223 const GValue * value, GParamSpec * pspec)
225 GstTCPServerSink *sink;
227 g_return_if_fail (GST_IS_TCPSERVERSINK (object));
228 sink = GST_TCPSERVERSINK (object);
232 if (!g_value_get_string (value)) {
233 g_warning ("host property cannot be NULL");
237 sink->host = g_strdup (g_value_get_string (value));
240 sink->server_port = g_value_get_int (value);
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
250 gst_tcpserversink_get_property (GObject * object, guint prop_id, GValue * value,
253 GstTCPServerSink *sink;
255 g_return_if_fail (GST_IS_TCPSERVERSINK (object));
256 sink = GST_TCPSERVERSINK (object);
260 g_value_set_string (value, sink->host);
263 g_value_set_int (value, sink->server_port);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
273 /* create a socket for sending to remote machine */
275 gst_tcpserversink_init_send (GstMultiFdSink * parent)
278 GstTCPServerSink *this = GST_TCPSERVERSINK (parent);
280 /* create sending server socket */
281 if ((this->server_sock.fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
282 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, (NULL), GST_ERROR_SYSTEM);
285 GST_DEBUG_OBJECT (this, "opened sending server socket with fd %d",
286 this->server_sock.fd);
288 /* make address reusable */
290 if (setsockopt (this->server_sock.fd, SOL_SOCKET, SO_REUSEADDR,
291 (void *) &ret, sizeof (ret)) < 0) {
292 gst_tcp_socket_close (&this->server_sock.fd);
293 GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL),
294 ("Could not setsockopt: %s", g_strerror (errno)));
297 /* keep connection alive; avoids SIGPIPE during write */
299 if (setsockopt (this->server_sock.fd, SOL_SOCKET, SO_KEEPALIVE,
300 (void *) &ret, sizeof (ret)) < 0) {
301 gst_tcp_socket_close (&this->server_sock.fd);
302 GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL),
303 ("Could not setsockopt: %s", g_strerror (errno)));
307 /* name the socket */
308 memset (&this->server_sin, 0, sizeof (this->server_sin));
309 this->server_sin.sin_family = AF_INET; /* network socket */
310 this->server_sin.sin_port = htons (this->server_port); /* on port */
311 this->server_sin.sin_addr.s_addr = htonl (INADDR_ANY); /* for hosts */
314 GST_DEBUG_OBJECT (this, "binding server socket to address");
315 ret = bind (this->server_sock.fd, (struct sockaddr *) &this->server_sin,
316 sizeof (this->server_sin));
319 gst_tcp_socket_close (&this->server_sock.fd);
322 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
323 ("bind on port %d failed: %s", this->server_port,
324 g_strerror (errno)));
330 /* set the server socket to nonblocking */
331 fcntl (this->server_sock.fd, F_SETFL, O_NONBLOCK);
333 GST_DEBUG_OBJECT (this, "listening on server socket %d with queue of %d",
334 this->server_sock.fd, TCP_BACKLOG);
335 if (listen (this->server_sock.fd, TCP_BACKLOG) == -1) {
336 gst_tcp_socket_close (&this->server_sock.fd);
337 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
338 ("Could not listen on server socket: %s", g_strerror (errno)));
341 GST_DEBUG_OBJECT (this,
342 "listened on server socket %d, returning from connection setup",
343 this->server_sock.fd);
345 gst_fdset_add_fd (parent->fdset, &this->server_sock);
346 gst_fdset_fd_ctl_read (parent->fdset, &this->server_sock, TRUE);
352 gst_tcpserversink_close (GstMultiFdSink * parent)
354 GstTCPServerSink *this = GST_TCPSERVERSINK (parent);
356 if (this->server_sock.fd != -1) {
357 gst_fdset_remove_fd (parent->fdset, &this->server_sock);
359 close (this->server_sock.fd);
360 this->server_sock.fd = -1;