|
1 /* GStreamer |
|
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
|
3 * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> |
|
4 * |
|
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. |
|
9 * |
|
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. |
|
14 * |
|
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. |
|
19 */ |
|
20 |
|
21 /** |
|
22 * SECTION:tcpserversink |
|
23 * @short_description: a sink that acts as a TCP server and sends data to |
|
24 * multiple clients |
|
25 * @see_also: #multifdsink |
|
26 */ |
|
27 |
|
28 #ifdef HAVE_CONFIG_H |
|
29 #include "config.h" |
|
30 #endif |
|
31 #ifdef __SYMBIAN32__ |
|
32 #include "gst/gst-i18n-plugin.h" |
|
33 #else |
|
34 #include <gst/gst-i18n-plugin.h> |
|
35 #endif |
|
36 #include <string.h> /* memset */ |
|
37 |
|
38 #include <sys/ioctl.h> |
|
39 |
|
40 #ifdef HAVE_FIONREAD_IN_SYS_FILIO |
|
41 #include <sys/filio.h> |
|
42 #endif |
|
43 |
|
44 #include "gsttcp.h" |
|
45 #include "gsttcpserversink.h" |
|
46 #include "gsttcp-marshal.h" |
|
47 |
|
48 #define TCP_BACKLOG 5 |
|
49 |
|
50 /* elementfactory information */ |
|
51 static const GstElementDetails gst_tcp_server_sink_details = |
|
52 GST_ELEMENT_DETAILS ("TCP server sink", |
|
53 "Sink/Network", |
|
54 "Send data as a server over the network via TCP", |
|
55 "Thomas Vander Stichele <thomas at apestaart dot org>"); |
|
56 |
|
57 GST_DEBUG_CATEGORY_STATIC (tcpserversink_debug); |
|
58 #define GST_CAT_DEFAULT (tcpserversink_debug) |
|
59 |
|
60 enum |
|
61 { |
|
62 ARG_0, |
|
63 ARG_HOST, |
|
64 ARG_PORT, |
|
65 }; |
|
66 |
|
67 static void gst_tcp_server_sink_finalize (GObject * gobject); |
|
68 |
|
69 static gboolean gst_tcp_server_sink_handle_wait (GstMultiFdSink * sink, |
|
70 GstPoll * set); |
|
71 static gboolean gst_tcp_server_sink_init_send (GstMultiFdSink * this); |
|
72 static gboolean gst_tcp_server_sink_close (GstMultiFdSink * this); |
|
73 static void gst_tcp_server_sink_removed (GstMultiFdSink * sink, int fd); |
|
74 |
|
75 static void gst_tcp_server_sink_set_property (GObject * object, guint prop_id, |
|
76 const GValue * value, GParamSpec * pspec); |
|
77 static void gst_tcp_server_sink_get_property (GObject * object, guint prop_id, |
|
78 GValue * value, GParamSpec * pspec); |
|
79 |
|
80 |
|
81 GST_BOILERPLATE (GstTCPServerSink, gst_tcp_server_sink, GstMultiFdSink, |
|
82 GST_TYPE_MULTI_FD_SINK); |
|
83 |
|
84 |
|
85 static void |
|
86 gst_tcp_server_sink_base_init (gpointer g_class) |
|
87 { |
|
88 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
|
89 |
|
90 gst_element_class_set_details (element_class, &gst_tcp_server_sink_details); |
|
91 } |
|
92 |
|
93 static void |
|
94 gst_tcp_server_sink_class_init (GstTCPServerSinkClass * klass) |
|
95 { |
|
96 GObjectClass *gobject_class; |
|
97 GstMultiFdSinkClass *gstmultifdsink_class; |
|
98 |
|
99 gobject_class = (GObjectClass *) klass; |
|
100 gstmultifdsink_class = (GstMultiFdSinkClass *) klass; |
|
101 |
|
102 gobject_class->set_property = gst_tcp_server_sink_set_property; |
|
103 gobject_class->get_property = gst_tcp_server_sink_get_property; |
|
104 gobject_class->finalize = gst_tcp_server_sink_finalize; |
|
105 |
|
106 g_object_class_install_property (gobject_class, ARG_HOST, |
|
107 g_param_spec_string ("host", "host", "The host/IP to send the packets to", |
|
108 TCP_DEFAULT_HOST, G_PARAM_READWRITE)); |
|
109 g_object_class_install_property (gobject_class, ARG_PORT, |
|
110 g_param_spec_int ("port", "port", "The port to send the packets to", |
|
111 0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT, G_PARAM_READWRITE)); |
|
112 |
|
113 gstmultifdsink_class->init = gst_tcp_server_sink_init_send; |
|
114 gstmultifdsink_class->wait = gst_tcp_server_sink_handle_wait; |
|
115 gstmultifdsink_class->close = gst_tcp_server_sink_close; |
|
116 gstmultifdsink_class->removed = gst_tcp_server_sink_removed; |
|
117 |
|
118 GST_DEBUG_CATEGORY_INIT (tcpserversink_debug, "tcpserversink", 0, "TCP sink"); |
|
119 } |
|
120 |
|
121 static void |
|
122 gst_tcp_server_sink_init (GstTCPServerSink * this, |
|
123 GstTCPServerSinkClass * klass) |
|
124 { |
|
125 this->server_port = TCP_DEFAULT_PORT; |
|
126 /* should support as minimum 576 for IPV4 and 1500 for IPV6 */ |
|
127 /* this->mtu = 1500; */ |
|
128 this->host = g_strdup (TCP_DEFAULT_HOST); |
|
129 |
|
130 this->server_sock.fd = -1; |
|
131 } |
|
132 |
|
133 static void |
|
134 gst_tcp_server_sink_finalize (GObject * gobject) |
|
135 { |
|
136 GstTCPServerSink *this = GST_TCP_SERVER_SINK (gobject); |
|
137 |
|
138 g_free (this->host); |
|
139 |
|
140 G_OBJECT_CLASS (parent_class)->finalize (gobject); |
|
141 } |
|
142 |
|
143 /* handle a read request on the server, |
|
144 * which indicates a new client connection */ |
|
145 static gboolean |
|
146 gst_tcp_server_sink_handle_server_read (GstTCPServerSink * sink) |
|
147 { |
|
148 /* new client */ |
|
149 int client_sock_fd; |
|
150 struct sockaddr_in client_address; |
|
151 unsigned int client_address_len; |
|
152 |
|
153 /* For some stupid reason, client_address and client_address_len has to be |
|
154 * zeroed */ |
|
155 memset (&client_address, 0, sizeof (client_address)); |
|
156 client_address_len = 0; |
|
157 |
|
158 client_sock_fd = |
|
159 accept (sink->server_sock.fd, (struct sockaddr *) &client_address, |
|
160 &client_address_len); |
|
161 if (client_sock_fd == -1) { |
|
162 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), |
|
163 ("Could not accept client on server socket %d: %s (%d)", |
|
164 sink->server_sock.fd, g_strerror (errno), errno)); |
|
165 return FALSE; |
|
166 } |
|
167 |
|
168 gst_multi_fd_sink_add (GST_MULTI_FD_SINK (sink), client_sock_fd); |
|
169 |
|
170 GST_DEBUG_OBJECT (sink, "added new client ip %s with fd %d", |
|
171 inet_ntoa (client_address.sin_addr), client_sock_fd); |
|
172 |
|
173 return TRUE; |
|
174 } |
|
175 |
|
176 static void |
|
177 gst_tcp_server_sink_removed (GstMultiFdSink * sink, int fd) |
|
178 { |
|
179 #ifndef GST_DISABLE_GST_DEBUG |
|
180 GstTCPServerSink *this = GST_TCP_SERVER_SINK (sink); |
|
181 #endif |
|
182 |
|
183 GST_LOG_OBJECT (this, "closing fd %d", fd); |
|
184 if (close (fd) < 0) { |
|
185 GST_WARNING_OBJECT (this, "error closing fd %d: %s", fd, |
|
186 g_strerror (errno)); |
|
187 } |
|
188 } |
|
189 |
|
190 static gboolean |
|
191 gst_tcp_server_sink_handle_wait (GstMultiFdSink * sink, GstPoll * set) |
|
192 { |
|
193 GstTCPServerSink *this = GST_TCP_SERVER_SINK (sink); |
|
194 |
|
195 if (gst_poll_fd_can_read (set, &this->server_sock)) { |
|
196 /* handle new client connection on server socket */ |
|
197 if (!gst_tcp_server_sink_handle_server_read (this)) { |
|
198 GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL), |
|
199 ("client connection failed: %s", g_strerror (errno))); |
|
200 return FALSE; |
|
201 } |
|
202 } |
|
203 return TRUE; |
|
204 } |
|
205 |
|
206 static void |
|
207 gst_tcp_server_sink_set_property (GObject * object, guint prop_id, |
|
208 const GValue * value, GParamSpec * pspec) |
|
209 { |
|
210 GstTCPServerSink *sink; |
|
211 |
|
212 g_return_if_fail (GST_IS_TCP_SERVER_SINK (object)); |
|
213 sink = GST_TCP_SERVER_SINK (object); |
|
214 |
|
215 switch (prop_id) { |
|
216 case ARG_HOST: |
|
217 if (!g_value_get_string (value)) { |
|
218 g_warning ("host property cannot be NULL"); |
|
219 break; |
|
220 } |
|
221 g_free (sink->host); |
|
222 sink->host = g_strdup (g_value_get_string (value)); |
|
223 break; |
|
224 case ARG_PORT: |
|
225 sink->server_port = g_value_get_int (value); |
|
226 break; |
|
227 |
|
228 default: |
|
229 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
230 break; |
|
231 } |
|
232 } |
|
233 |
|
234 static void |
|
235 gst_tcp_server_sink_get_property (GObject * object, guint prop_id, |
|
236 GValue * value, GParamSpec * pspec) |
|
237 { |
|
238 GstTCPServerSink *sink; |
|
239 |
|
240 g_return_if_fail (GST_IS_TCP_SERVER_SINK (object)); |
|
241 sink = GST_TCP_SERVER_SINK (object); |
|
242 |
|
243 switch (prop_id) { |
|
244 case ARG_HOST: |
|
245 g_value_set_string (value, sink->host); |
|
246 break; |
|
247 case ARG_PORT: |
|
248 g_value_set_int (value, sink->server_port); |
|
249 break; |
|
250 |
|
251 default: |
|
252 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
253 break; |
|
254 } |
|
255 } |
|
256 |
|
257 |
|
258 /* create a socket for sending to remote machine */ |
|
259 static gboolean |
|
260 gst_tcp_server_sink_init_send (GstMultiFdSink * parent) |
|
261 { |
|
262 int ret; |
|
263 GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent); |
|
264 |
|
265 /* create sending server socket */ |
|
266 if ((this->server_sock.fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) { |
|
267 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, (NULL), GST_ERROR_SYSTEM); |
|
268 return FALSE; |
|
269 } |
|
270 GST_DEBUG_OBJECT (this, "opened sending server socket with fd %d", |
|
271 this->server_sock.fd); |
|
272 |
|
273 /* make address reusable */ |
|
274 ret = 1; |
|
275 if (setsockopt (this->server_sock.fd, SOL_SOCKET, SO_REUSEADDR, |
|
276 (void *) &ret, sizeof (ret)) < 0) { |
|
277 gst_tcp_socket_close (&this->server_sock); |
|
278 GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL), |
|
279 ("Could not setsockopt: %s", g_strerror (errno))); |
|
280 return FALSE; |
|
281 } |
|
282 /* keep connection alive; avoids SIGPIPE during write */ |
|
283 ret = 1; |
|
284 if (setsockopt (this->server_sock.fd, SOL_SOCKET, SO_KEEPALIVE, |
|
285 (void *) &ret, sizeof (ret)) < 0) { |
|
286 gst_tcp_socket_close (&this->server_sock); |
|
287 GST_ELEMENT_ERROR (this, RESOURCE, SETTINGS, (NULL), |
|
288 ("Could not setsockopt: %s", g_strerror (errno))); |
|
289 return FALSE; |
|
290 } |
|
291 |
|
292 /* name the socket */ |
|
293 memset (&this->server_sin, 0, sizeof (this->server_sin)); |
|
294 this->server_sin.sin_family = AF_INET; /* network socket */ |
|
295 this->server_sin.sin_port = htons (this->server_port); /* on port */ |
|
296 this->server_sin.sin_addr.s_addr = htonl (INADDR_ANY); /* for hosts */ |
|
297 |
|
298 /* bind it */ |
|
299 GST_DEBUG_OBJECT (this, "binding server socket to address"); |
|
300 ret = bind (this->server_sock.fd, (struct sockaddr *) &this->server_sin, |
|
301 sizeof (this->server_sin)); |
|
302 |
|
303 if (ret) { |
|
304 gst_tcp_socket_close (&this->server_sock); |
|
305 switch (errno) { |
|
306 default: |
|
307 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL), |
|
308 ("bind on port %d failed: %s", this->server_port, |
|
309 g_strerror (errno))); |
|
310 return FALSE; |
|
311 break; |
|
312 } |
|
313 } |
|
314 |
|
315 /* set the server socket to nonblocking */ |
|
316 fcntl (this->server_sock.fd, F_SETFL, O_NONBLOCK); |
|
317 |
|
318 GST_DEBUG_OBJECT (this, "listening on server socket %d with queue of %d", |
|
319 this->server_sock.fd, TCP_BACKLOG); |
|
320 if (listen (this->server_sock.fd, TCP_BACKLOG) == -1) { |
|
321 gst_tcp_socket_close (&this->server_sock); |
|
322 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL), |
|
323 ("Could not listen on server socket: %s", g_strerror (errno))); |
|
324 return FALSE; |
|
325 } |
|
326 GST_DEBUG_OBJECT (this, |
|
327 "listened on server socket %d, returning from connection setup", |
|
328 this->server_sock.fd); |
|
329 |
|
330 gst_poll_add_fd (parent->fdset, &this->server_sock); |
|
331 gst_poll_fd_ctl_read (parent->fdset, &this->server_sock, TRUE); |
|
332 |
|
333 return TRUE; |
|
334 } |
|
335 |
|
336 static gboolean |
|
337 gst_tcp_server_sink_close (GstMultiFdSink * parent) |
|
338 { |
|
339 GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent); |
|
340 |
|
341 if (this->server_sock.fd != -1) { |
|
342 gst_poll_remove_fd (parent->fdset, &this->server_sock); |
|
343 |
|
344 close (this->server_sock.fd); |
|
345 this->server_sock.fd = -1; |
|
346 } |
|
347 return TRUE; |
|
348 } |