Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / tcp / gsttcpclientsink.c
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:element-tcpclientsink
23  * @see_also: #tcpclientsrc
24  *
25  * <refsect2>
26  * <title>Example launch line</title>
27  * |[
28  * # server:
29  * nc -l -p 3000
30  * # client:
31  * gst-launch fdsrc fd=1 ! tcpclientsink protocol=none port=3000
32  * ]| everything you type in the client is shown on the server
33  * </refsect2>
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 #include <gst/gst-i18n-plugin.h>
40 #include <gst/dataprotocol/dataprotocol.h>
41 #include "gsttcp.h"
42 #include "gsttcpclientsink.h"
43 #include <string.h>             /* memset */
44
45 /* TCPClientSink signals and args */
46 enum
47 {
48   FRAME_ENCODED,
49   /* FILL ME */
50   LAST_SIGNAL
51 };
52
53 GST_DEBUG_CATEGORY_STATIC (tcpclientsink_debug);
54 #define GST_CAT_DEFAULT (tcpclientsink_debug)
55
56 enum
57 {
58   ARG_0,
59   ARG_HOST,
60   ARG_PORT
61 };
62
63 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
64     GST_PAD_SINK,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS_ANY);
67
68 static void gst_tcp_client_sink_base_init (gpointer g_class);
69 static void gst_tcp_client_sink_class_init (GstTCPClientSink * klass);
70 static void gst_tcp_client_sink_init (GstTCPClientSink * tcpclientsink);
71 static void gst_tcp_client_sink_finalize (GObject * gobject);
72
73 static gboolean gst_tcp_client_sink_setcaps (GstBaseSink * bsink,
74     GstCaps * caps);
75 static GstFlowReturn gst_tcp_client_sink_render (GstBaseSink * bsink,
76     GstBuffer * buf);
77 static GstStateChangeReturn gst_tcp_client_sink_change_state (GstElement *
78     element, GstStateChange transition);
79
80 static void gst_tcp_client_sink_set_property (GObject * object, guint prop_id,
81     const GValue * value, GParamSpec * pspec);
82 static void gst_tcp_client_sink_get_property (GObject * object, guint prop_id,
83     GValue * value, GParamSpec * pspec);
84
85
86 static GstElementClass *parent_class = NULL;
87
88 /*static guint gst_tcp_client_sink_signals[LAST_SIGNAL] = { 0 }; */
89
90 GType
91 gst_tcp_client_sink_get_type (void)
92 {
93   static GType tcpclientsink_type = 0;
94
95
96   if (!tcpclientsink_type) {
97     static const GTypeInfo tcpclientsink_info = {
98       sizeof (GstTCPClientSinkClass),
99       gst_tcp_client_sink_base_init,
100       NULL,
101       (GClassInitFunc) gst_tcp_client_sink_class_init,
102       NULL,
103       NULL,
104       sizeof (GstTCPClientSink),
105       0,
106       (GInstanceInitFunc) gst_tcp_client_sink_init,
107       NULL
108     };
109
110     tcpclientsink_type =
111         g_type_register_static (GST_TYPE_BASE_SINK, "GstTCPClientSink",
112         &tcpclientsink_info, 0);
113   }
114   return tcpclientsink_type;
115 }
116
117 static void
118 gst_tcp_client_sink_base_init (gpointer g_class)
119 {
120   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
121
122   gst_element_class_add_pad_template (element_class,
123       gst_static_pad_template_get (&sinktemplate));
124
125   gst_element_class_set_details_simple (element_class,
126       "TCP client sink", "Sink/Network",
127       "Send data as a client over the network via TCP",
128       "Thomas Vander Stichele <thomas at apestaart dot org>");
129 }
130
131 static void
132 gst_tcp_client_sink_class_init (GstTCPClientSink * klass)
133 {
134   GObjectClass *gobject_class;
135   GstElementClass *gstelement_class;
136   GstBaseSinkClass *gstbasesink_class;
137
138   gobject_class = (GObjectClass *) klass;
139   gstelement_class = (GstElementClass *) klass;
140   gstbasesink_class = (GstBaseSinkClass *) klass;
141
142   parent_class = g_type_class_peek_parent (klass);
143
144   gobject_class->set_property = gst_tcp_client_sink_set_property;
145   gobject_class->get_property = gst_tcp_client_sink_get_property;
146   gobject_class->finalize = gst_tcp_client_sink_finalize;
147
148   g_object_class_install_property (gobject_class, ARG_HOST,
149       g_param_spec_string ("host", "Host", "The host/IP to send the packets to",
150           TCP_DEFAULT_HOST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151   g_object_class_install_property (gobject_class, ARG_PORT,
152       g_param_spec_int ("port", "Port", "The port to send the packets to",
153           0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT,
154           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155
156   gstelement_class->change_state = gst_tcp_client_sink_change_state;
157
158   gstbasesink_class->set_caps = gst_tcp_client_sink_setcaps;
159   gstbasesink_class->render = gst_tcp_client_sink_render;
160
161   GST_DEBUG_CATEGORY_INIT (tcpclientsink_debug, "tcpclientsink", 0, "TCP sink");
162 }
163
164 static void
165 gst_tcp_client_sink_init (GstTCPClientSink * this)
166 {
167   this->host = g_strdup (TCP_DEFAULT_HOST);
168   this->port = TCP_DEFAULT_PORT;
169
170   this->sock_fd.fd = -1;
171   GST_OBJECT_FLAG_UNSET (this, GST_TCP_CLIENT_SINK_OPEN);
172 }
173
174 static void
175 gst_tcp_client_sink_finalize (GObject * gobject)
176 {
177   GstTCPClientSink *this = GST_TCP_CLIENT_SINK (gobject);
178
179   g_free (this->host);
180
181   G_OBJECT_CLASS (parent_class)->finalize (gobject);
182 }
183
184 static gboolean
185 gst_tcp_client_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
186 {
187   GstTCPClientSink *sink;
188
189   sink = GST_TCP_CLIENT_SINK (bsink);
190
191   return TRUE;
192 }
193
194 static GstFlowReturn
195 gst_tcp_client_sink_render (GstBaseSink * bsink, GstBuffer * buf)
196 {
197   size_t wrote = 0;
198   GstTCPClientSink *sink;
199   gint size;
200
201   sink = GST_TCP_CLIENT_SINK (bsink);
202
203   g_return_val_if_fail (GST_OBJECT_FLAG_IS_SET (sink, GST_TCP_CLIENT_SINK_OPEN),
204       GST_FLOW_WRONG_STATE);
205
206   size = GST_BUFFER_SIZE (buf);
207
208   GST_LOG_OBJECT (sink, "writing %d bytes for buffer data", size);
209
210   /* write buffer data */
211   wrote = gst_tcp_socket_write (sink->sock_fd.fd, GST_BUFFER_DATA (buf), size);
212
213   if (wrote < size)
214     goto write_error;
215
216   sink->data_written += wrote;
217
218   return GST_FLOW_OK;
219
220   /* ERRORS */
221 write_error:
222   {
223     GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
224         (_("Error while sending data to \"%s:%d\"."), sink->host, sink->port),
225         ("Only %" G_GSIZE_FORMAT " of %u bytes written: %s",
226             wrote, GST_BUFFER_SIZE (buf), g_strerror (errno)));
227     return GST_FLOW_ERROR;
228   }
229 }
230
231 static void
232 gst_tcp_client_sink_set_property (GObject * object, guint prop_id,
233     const GValue * value, GParamSpec * pspec)
234 {
235   GstTCPClientSink *tcpclientsink;
236
237   g_return_if_fail (GST_IS_TCP_CLIENT_SINK (object));
238   tcpclientsink = GST_TCP_CLIENT_SINK (object);
239
240   switch (prop_id) {
241     case ARG_HOST:
242       if (!g_value_get_string (value)) {
243         g_warning ("host property cannot be NULL");
244         break;
245       }
246       g_free (tcpclientsink->host);
247       tcpclientsink->host = g_strdup (g_value_get_string (value));
248       break;
249     case ARG_PORT:
250       tcpclientsink->port = g_value_get_int (value);
251       break;
252
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255       break;
256   }
257 }
258
259 static void
260 gst_tcp_client_sink_get_property (GObject * object, guint prop_id,
261     GValue * value, GParamSpec * pspec)
262 {
263   GstTCPClientSink *tcpclientsink;
264
265   g_return_if_fail (GST_IS_TCP_CLIENT_SINK (object));
266   tcpclientsink = GST_TCP_CLIENT_SINK (object);
267
268   switch (prop_id) {
269     case ARG_HOST:
270       g_value_set_string (value, tcpclientsink->host);
271       break;
272     case ARG_PORT:
273       g_value_set_int (value, tcpclientsink->port);
274       break;
275
276     default:
277       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
278       break;
279   }
280 }
281
282
283 /* create a socket for sending to remote machine */
284 static gboolean
285 gst_tcp_client_sink_start (GstTCPClientSink * this)
286 {
287   int ret;
288   gchar *ip;
289
290   if (GST_OBJECT_FLAG_IS_SET (this, GST_TCP_CLIENT_SINK_OPEN))
291     return TRUE;
292
293   /* reset caps_sent flag */
294   this->caps_sent = FALSE;
295
296   /* create sending client socket */
297   GST_DEBUG_OBJECT (this, "opening sending client socket to %s:%d", this->host,
298       this->port);
299   if ((this->sock_fd.fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
300     GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE, (NULL), GST_ERROR_SYSTEM);
301     return FALSE;
302   }
303   GST_DEBUG_OBJECT (this, "opened sending client socket with fd %d",
304       this->sock_fd.fd);
305
306   /* look up name if we need to */
307   ip = gst_tcp_host_to_ip (GST_ELEMENT (this), this->host);
308   if (!ip) {
309     gst_tcp_socket_close (&this->sock_fd);
310     return FALSE;
311   }
312   GST_DEBUG_OBJECT (this, "IP address for host %s is %s", this->host, ip);
313
314   /* connect to server */
315   memset (&this->server_sin, 0, sizeof (this->server_sin));
316   this->server_sin.sin_family = AF_INET;        /* network socket */
317   this->server_sin.sin_port = htons (this->port);       /* on port */
318   this->server_sin.sin_addr.s_addr = inet_addr (ip);    /* on host ip */
319   g_free (ip);
320
321   GST_DEBUG_OBJECT (this, "connecting to server");
322   ret = connect (this->sock_fd.fd, (struct sockaddr *) &this->server_sin,
323       sizeof (this->server_sin));
324
325   if (ret) {
326     gst_tcp_socket_close (&this->sock_fd);
327     switch (errno) {
328       case ECONNREFUSED:
329         GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
330             (_("Connection to %s:%d refused."), this->host, this->port),
331             (NULL));
332         return FALSE;
333         break;
334       default:
335         GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
336             ("connect to %s:%d failed: %s", this->host, this->port,
337                 g_strerror (errno)));
338         return FALSE;
339         break;
340     }
341   }
342
343   GST_OBJECT_FLAG_SET (this, GST_TCP_CLIENT_SINK_OPEN);
344
345   this->data_written = 0;
346
347   return TRUE;
348 }
349
350 static gboolean
351 gst_tcp_client_sink_stop (GstTCPClientSink * this)
352 {
353   if (!GST_OBJECT_FLAG_IS_SET (this, GST_TCP_CLIENT_SINK_OPEN))
354     return TRUE;
355
356   gst_tcp_socket_close (&this->sock_fd);
357
358   GST_OBJECT_FLAG_UNSET (this, GST_TCP_CLIENT_SINK_OPEN);
359
360   return TRUE;
361 }
362
363 static GstStateChangeReturn
364 gst_tcp_client_sink_change_state (GstElement * element,
365     GstStateChange transition)
366 {
367   GstTCPClientSink *sink;
368   GstStateChangeReturn res;
369
370   sink = GST_TCP_CLIENT_SINK (element);
371
372   switch (transition) {
373     case GST_STATE_CHANGE_NULL_TO_READY:
374     case GST_STATE_CHANGE_READY_TO_PAUSED:
375       if (!gst_tcp_client_sink_start (sink))
376         goto start_failure;
377       break;
378     default:
379       break;
380   }
381   res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
382
383   switch (transition) {
384     case GST_STATE_CHANGE_READY_TO_NULL:
385       gst_tcp_client_sink_stop (sink);
386     default:
387       break;
388   }
389   return res;
390
391 start_failure:
392   {
393     return GST_STATE_CHANGE_FAILURE;
394   }
395 }