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.
22 * SECTION:element-tcpserversink
23 * @see_also: #multifdsink
26 * <title>Example launch line</title>
29 * gst-launch fdsrc fd=1 ! tcpserversink port=3000
31 * gst-launch tcpclientsrc port=3000 ! fdsink fd=2
39 #include <gst/gst-i18n-plugin.h>
40 #include <string.h> /* memset */
43 #include "gsttcpserversink.h"
44 #include "gsttcp-marshal.h"
48 GST_DEBUG_CATEGORY_STATIC (tcpserversink_debug);
49 #define GST_CAT_DEFAULT (tcpserversink_debug)
59 static void gst_tcp_server_sink_finalize (GObject * gobject);
61 static gboolean gst_tcp_server_sink_init_send (GstMultiHandleSink * this);
62 static gboolean gst_tcp_server_sink_close (GstMultiHandleSink * this);
63 static void gst_tcp_server_sink_removed (GstMultiHandleSink * sink,
64 GstMultiSinkHandle handle);
66 static void gst_tcp_server_sink_set_property (GObject * object, guint prop_id,
67 const GValue * value, GParamSpec * pspec);
68 static void gst_tcp_server_sink_get_property (GObject * object, guint prop_id,
69 GValue * value, GParamSpec * pspec);
71 #define gst_tcp_server_sink_parent_class parent_class
72 G_DEFINE_TYPE (GstTCPServerSink, gst_tcp_server_sink,
73 GST_TYPE_MULTI_SOCKET_SINK);
76 gst_tcp_server_sink_class_init (GstTCPServerSinkClass * klass)
78 GObjectClass *gobject_class;
79 GstElementClass *gstelement_class;
80 GstMultiHandleSinkClass *gstmultihandlesink_class;
82 gobject_class = (GObjectClass *) klass;
83 gstelement_class = (GstElementClass *) klass;
84 gstmultihandlesink_class = (GstMultiHandleSinkClass *) klass;
86 gobject_class->set_property = gst_tcp_server_sink_set_property;
87 gobject_class->get_property = gst_tcp_server_sink_get_property;
88 gobject_class->finalize = gst_tcp_server_sink_finalize;
90 g_object_class_install_property (gobject_class, PROP_HOST,
91 g_param_spec_string ("host", "host", "The host/IP to send the packets to",
92 TCP_DEFAULT_HOST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
93 g_object_class_install_property (gobject_class, PROP_PORT,
94 g_param_spec_int ("port", "port", "The port to send the packets to",
95 0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT,
96 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
97 g_object_class_install_property (gobject_class, PROP_CURRENT_PORT,
98 g_param_spec_int ("current-port", "current-port",
99 "The port number the socket is currently bound to", 0,
100 TCP_HIGHEST_PORT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
102 gst_element_class_set_static_metadata (gstelement_class,
103 "TCP server sink", "Sink/Network",
104 "Send data as a server over the network via TCP",
105 "Thomas Vander Stichele <thomas at apestaart dot org>");
107 gstmultihandlesink_class->init = gst_tcp_server_sink_init_send;
108 gstmultihandlesink_class->close = gst_tcp_server_sink_close;
109 gstmultihandlesink_class->removed = gst_tcp_server_sink_removed;
111 GST_DEBUG_CATEGORY_INIT (tcpserversink_debug, "tcpserversink", 0, "TCP sink");
115 gst_tcp_server_sink_init (GstTCPServerSink * this)
117 this->server_port = TCP_DEFAULT_PORT;
118 /* should support as minimum 576 for IPV4 and 1500 for IPV6 */
119 /* this->mtu = 1500; */
120 this->host = g_strdup (TCP_DEFAULT_HOST);
122 this->server_socket = NULL;
126 gst_tcp_server_sink_finalize (GObject * gobject)
128 GstTCPServerSink *this = GST_TCP_SERVER_SINK (gobject);
130 if (this->server_socket)
131 g_object_unref (this->server_socket);
132 this->server_socket = NULL;
136 G_OBJECT_CLASS (parent_class)->finalize (gobject);
139 /* handle a read request on the server,
140 * which indicates a new client connection */
142 gst_tcp_server_sink_handle_server_read (GstTCPServerSink * sink)
144 GSocket *client_socket;
147 /* wait on server socket for connections */
149 g_socket_accept (sink->server_socket, sink->element.cancellable, &err);
153 gst_multi_handle_sink_add (GST_MULTI_HANDLE_SINK (sink),
154 (GstMultiSinkHandle) client_socket);
156 #ifndef GST_DISABLE_GST_DEBUG
158 GInetSocketAddress *addr =
159 G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (client_socket,
162 g_inet_address_to_string (g_inet_socket_address_get_address (addr));
164 GST_DEBUG_OBJECT (sink, "added new client ip %s:%u with socket %p",
165 ip, g_inet_socket_address_get_port (addr), client_socket);
176 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
177 ("Could not accept client on server socket %p: %s",
178 sink->server_socket, err->message));
179 g_clear_error (&err);
185 gst_tcp_server_sink_removed (GstMultiHandleSink * sink,
186 GstMultiSinkHandle handle)
188 #ifndef GST_DISABLE_GST_DEBUG
189 GstTCPServerSink *this = GST_TCP_SERVER_SINK (sink);
193 GST_DEBUG_OBJECT (this, "closing socket");
195 if (!g_socket_close (handle.socket, &err)) {
196 GST_ERROR_OBJECT (this, "Failed to close socket: %s", err->message);
197 g_clear_error (&err);
202 gst_tcp_server_sink_socket_condition (GSocket * socket, GIOCondition condition,
203 GstTCPServerSink * sink)
205 if ((condition & G_IO_ERR)) {
207 } else if ((condition & G_IO_IN) || (condition & G_IO_PRI)) {
208 if (!gst_tcp_server_sink_handle_server_read (sink))
215 GST_ELEMENT_ERROR (sink, RESOURCE, READ, (NULL),
216 ("client connection failed"));
222 gst_tcp_server_sink_set_property (GObject * object, guint prop_id,
223 const GValue * value, GParamSpec * pspec)
225 GstTCPServerSink *sink;
227 g_return_if_fail (GST_IS_TCP_SERVER_SINK (object));
228 sink = GST_TCP_SERVER_SINK (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);
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
249 gst_tcp_server_sink_get_property (GObject * object, guint prop_id,
250 GValue * value, GParamSpec * pspec)
252 GstTCPServerSink *sink;
254 g_return_if_fail (GST_IS_TCP_SERVER_SINK (object));
255 sink = GST_TCP_SERVER_SINK (object);
259 g_value_set_string (value, sink->host);
262 g_value_set_int (value, sink->server_port);
264 case PROP_CURRENT_PORT:
265 g_value_set_int (value, g_atomic_int_get (&sink->current_port));
268 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
274 /* create a socket for sending to remote machine */
276 gst_tcp_server_sink_init_send (GstMultiHandleSink * parent)
278 GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent);
281 GSocketAddress *saddr;
285 /* look up name if we need to */
286 addr = g_inet_address_new_from_string (this->host);
290 resolver = g_resolver_get_default ();
293 g_resolver_lookup_by_name (resolver, this->host,
294 this->element.cancellable, &err);
297 addr = G_INET_ADDRESS (g_object_ref (results->data));
299 g_resolver_free_addresses (results);
300 g_object_unref (resolver);
302 #ifndef GST_DISABLE_GST_DEBUG
304 gchar *ip = g_inet_address_to_string (addr);
306 GST_DEBUG_OBJECT (this, "IP address for host %s is %s", this->host, ip);
310 saddr = g_inet_socket_address_new (addr, this->server_port);
311 g_object_unref (addr);
313 /* create the server listener socket */
314 this->server_socket =
315 g_socket_new (g_socket_address_get_family (saddr), G_SOCKET_TYPE_STREAM,
316 G_SOCKET_PROTOCOL_TCP, &err);
317 if (!this->server_socket)
320 GST_DEBUG_OBJECT (this, "opened sending server socket with socket %p",
321 this->server_socket);
323 g_socket_set_blocking (this->server_socket, FALSE);
326 GST_DEBUG_OBJECT (this, "binding server socket to address");
327 if (!g_socket_bind (this->server_socket, saddr, TRUE, &err))
330 g_object_unref (saddr);
332 GST_DEBUG_OBJECT (this, "listening on server socket");
333 g_socket_set_listen_backlog (this->server_socket, TCP_BACKLOG);
335 if (!g_socket_listen (this->server_socket, &err))
338 GST_DEBUG_OBJECT (this,
339 "listened on server socket %p, returning from connection setup",
340 this->server_socket);
342 if (this->server_port == 0) {
343 saddr = g_socket_get_local_address (this->server_socket, NULL);
344 bound_port = g_inet_socket_address_get_port ((GInetSocketAddress *) saddr);
345 g_object_unref (saddr);
347 bound_port = this->server_port;
350 GST_DEBUG_OBJECT (this, "listening on port %d", bound_port);
352 g_atomic_int_set (&this->current_port, bound_port);
354 g_object_notify (G_OBJECT (this), "current-port");
356 this->server_source =
357 g_socket_create_source (this->server_socket,
358 G_IO_IN | G_IO_OUT | G_IO_PRI | G_IO_ERR | G_IO_HUP,
359 this->element.cancellable);
360 g_source_set_callback (this->server_source,
361 (GSourceFunc) gst_tcp_server_sink_socket_condition, gst_object_ref (this),
362 (GDestroyNotify) gst_object_unref);
363 g_source_attach (this->server_source, this->element.main_context);
370 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
371 ("Failed to create socket: %s", err->message));
372 g_clear_error (&err);
373 g_object_unref (saddr);
378 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
379 GST_DEBUG_OBJECT (this, "Cancelled name resolval");
381 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
382 ("Failed to resolve host '%s': %s", this->host, err->message));
384 g_clear_error (&err);
385 g_object_unref (resolver);
390 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
391 GST_DEBUG_OBJECT (this, "Cancelled binding");
393 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
394 ("Failed to bind on host '%s:%d': %s", this->host, this->server_port,
397 g_clear_error (&err);
398 g_object_unref (saddr);
399 gst_tcp_server_sink_close (GST_MULTI_HANDLE_SINK (&this->element));
404 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
405 GST_DEBUG_OBJECT (this, "Cancelled listening");
407 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
408 ("Failed to listen on host '%s:%d': %s", this->host,
409 this->server_port, err->message));
411 g_clear_error (&err);
412 gst_tcp_server_sink_close (GST_MULTI_HANDLE_SINK (&this->element));
418 gst_tcp_server_sink_close (GstMultiHandleSink * parent)
420 GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent);
422 if (this->server_source) {
423 g_source_destroy (this->server_source);
424 g_source_unref (this->server_source);
425 this->server_source = NULL;
428 if (this->server_socket) {
431 GST_DEBUG_OBJECT (this, "closing socket");
433 if (!g_socket_close (this->server_socket, &err)) {
434 GST_ERROR_OBJECT (this, "Failed to close socket: %s", err->message);
435 g_clear_error (&err);
437 g_object_unref (this->server_socket);
438 this->server_socket = NULL;
440 g_atomic_int_set (&this->current_port, 0);
441 g_object_notify (G_OBJECT (this), "current-port");