gst/: Make UDP and TCP elements use PushSrc.
[platform/upstream/gstreamer.git] / gst / udp / gstudpsink.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include "gstudpsink.h"
25
26 #define UDP_DEFAULT_HOST        "localhost"
27 #define UDP_DEFAULT_PORT        4951
28
29 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
30     GST_PAD_SINK,
31     GST_PAD_ALWAYS,
32     GST_STATIC_CAPS_ANY);
33
34 /* elementfactory information */
35 static GstElementDetails gst_udpsink_details =
36 GST_ELEMENT_DETAILS ("UDP packet sender",
37     "Sink/Network",
38     "Send data over the network via UDP",
39     "Wim Taymans <wim.taymans@chello.be>");
40
41 /* UDPSink signals and args */
42 enum
43 {
44   /* FILL ME */
45   LAST_SIGNAL
46 };
47
48 enum
49 {
50   ARG_0,
51   ARG_HOST,
52   ARG_PORT,
53   /* FILL ME */
54 };
55
56 static void gst_udpsink_base_init (gpointer g_class);
57 static void gst_udpsink_class_init (GstUDPSink * klass);
58 static void gst_udpsink_init (GstUDPSink * udpsink);
59
60 static void gst_udpsink_get_times (GstBaseSink * sink, GstBuffer * buffer,
61     GstClockTime * start, GstClockTime * end);
62 static GstFlowReturn gst_udpsink_render (GstBaseSink * sink,
63     GstBuffer * buffer);
64 static GstElementStateReturn gst_udpsink_change_state (GstElement * element);
65
66 static void gst_udpsink_set_property (GObject * object, guint prop_id,
67     const GValue * value, GParamSpec * pspec);
68 static void gst_udpsink_get_property (GObject * object, guint prop_id,
69     GValue * value, GParamSpec * pspec);
70
71 static GstElementClass *parent_class = NULL;
72
73 /*static guint gst_udpsink_signals[LAST_SIGNAL] = { 0 }; */
74
75 GType
76 gst_udpsink_get_type (void)
77 {
78   static GType udpsink_type = 0;
79
80   if (!udpsink_type) {
81     static const GTypeInfo udpsink_info = {
82       sizeof (GstUDPSinkClass),
83       gst_udpsink_base_init,
84       NULL,
85       (GClassInitFunc) gst_udpsink_class_init,
86       NULL,
87       NULL,
88       sizeof (GstUDPSink),
89       0,
90       (GInstanceInitFunc) gst_udpsink_init,
91       NULL
92     };
93
94     udpsink_type =
95         g_type_register_static (GST_TYPE_BASESINK, "GstUDPSink", &udpsink_info,
96         0);
97   }
98   return udpsink_type;
99 }
100
101 static void
102 gst_udpsink_base_init (gpointer g_class)
103 {
104   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
105
106   gst_element_class_add_pad_template (element_class,
107       gst_static_pad_template_get (&sink_template));
108
109   gst_element_class_set_details (element_class, &gst_udpsink_details);
110 }
111
112 static void
113 gst_udpsink_class_init (GstUDPSink * klass)
114 {
115   GObjectClass *gobject_class;
116   GstElementClass *gstelement_class;
117   GstBaseSinkClass *gstbasesink_class;
118
119   gobject_class = (GObjectClass *) klass;
120   gstelement_class = (GstElementClass *) klass;
121   gstbasesink_class = (GstBaseSinkClass *) klass;
122
123   parent_class = g_type_class_ref (GST_TYPE_BASESINK);
124
125   gobject_class->set_property = gst_udpsink_set_property;
126   gobject_class->get_property = gst_udpsink_get_property;
127
128   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HOST,
129       g_param_spec_string ("host", "host",
130           "The host/IP/Multicast group to send the packets to",
131           UDP_DEFAULT_HOST, G_PARAM_READWRITE));
132   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PORT,
133       g_param_spec_int ("port", "port", "The port to send the packets to",
134           0, 32768, UDP_DEFAULT_PORT, G_PARAM_READWRITE));
135
136   gstelement_class->change_state = gst_udpsink_change_state;
137
138   gstbasesink_class->get_times = gst_udpsink_get_times;
139   gstbasesink_class->render = gst_udpsink_render;
140 }
141
142
143 static void
144 gst_udpsink_init (GstUDPSink * udpsink)
145 {
146   udpsink->host = g_strdup (UDP_DEFAULT_HOST);
147   udpsink->port = UDP_DEFAULT_PORT;
148 }
149
150 static void
151 gst_udpsink_get_times (GstBaseSink * sink, GstBuffer * buffer,
152     GstClockTime * start, GstClockTime * end)
153 {
154   *start = GST_BUFFER_TIMESTAMP (buffer);
155   *end = *start + GST_BUFFER_DURATION (buffer);
156 }
157
158 static GstFlowReturn
159 gst_udpsink_render (GstBaseSink * sink, GstBuffer * buffer)
160 {
161   GstUDPSink *udpsink;
162   gint ret, size;
163   guint8 *data;
164
165   udpsink = GST_UDPSINK (sink);
166
167   size = GST_BUFFER_SIZE (buffer);
168   data = GST_BUFFER_DATA (buffer);
169
170   while (TRUE) {
171     ret = sendto (udpsink->sock, data, size, 0,
172         (struct sockaddr *) &udpsink->theiraddr, sizeof (udpsink->theiraddr));
173
174     if (ret < 0) {
175       if (errno != EINTR && errno != EAGAIN)
176         goto send_error;
177     } else
178       break;
179   }
180   return GST_FLOW_OK;
181
182 send_error:
183   {
184     GST_DEBUG ("got send error");
185     return GST_FLOW_ERROR;
186   }
187 }
188
189 static void
190 gst_udpsink_set_property (GObject * object, guint prop_id, const GValue * value,
191     GParamSpec * pspec)
192 {
193   GstUDPSink *udpsink;
194
195   udpsink = GST_UDPSINK (object);
196
197   switch (prop_id) {
198     case ARG_HOST:
199       if (udpsink->host != NULL)
200         g_free (udpsink->host);
201       if (g_value_get_string (value) == NULL)
202         udpsink->host = NULL;
203       else
204         udpsink->host = g_strdup (g_value_get_string (value));
205       break;
206     case ARG_PORT:
207       udpsink->port = g_value_get_int (value);
208       break;
209     default:
210       break;
211   }
212 }
213
214 static void
215 gst_udpsink_get_property (GObject * object, guint prop_id, GValue * value,
216     GParamSpec * pspec)
217 {
218   GstUDPSink *udpsink;
219
220   udpsink = GST_UDPSINK (object);
221
222   switch (prop_id) {
223     case ARG_HOST:
224       g_value_set_string (value, udpsink->host);
225       break;
226     case ARG_PORT:
227       g_value_set_int (value, udpsink->port);
228       break;
229     default:
230       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231       break;
232   }
233 }
234
235
236 /* create a socket for sending to remote machine */
237 static gboolean
238 gst_udpsink_init_send (GstUDPSink * sink)
239 {
240   struct hostent *he;
241   struct in_addr addr;
242   guint bc_val;
243
244   memset (&sink->theiraddr, 0, sizeof (sink->theiraddr));
245   sink->theiraddr.sin_family = AF_INET; /* host byte order */
246   sink->theiraddr.sin_port = htons (sink->port);        /* short, network byte order */
247
248   /* if its an IP address */
249   if (inet_aton (sink->host, &addr)) {
250     /* check if its a multicast address */
251     if ((ntohl (addr.s_addr) & 0xe0000000) == 0xe0000000) {
252       sink->multi_addr.imr_multiaddr.s_addr = addr.s_addr;
253       sink->multi_addr.imr_interface.s_addr = INADDR_ANY;
254
255       sink->theiraddr.sin_addr = sink->multi_addr.imr_multiaddr;
256
257       /* Joining the multicast group */
258       setsockopt (sink->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &sink->multi_addr,
259           sizeof (sink->multi_addr));
260     }
261
262     else {
263       sink->theiraddr.sin_addr = *((struct in_addr *) &addr);
264     }
265   }
266
267   /* we dont need to lookup for localhost */
268   else if (strcmp (sink->host, UDP_DEFAULT_HOST) == 0 &&
269       inet_aton ("127.0.0.1", &addr)) {
270     sink->theiraddr.sin_addr = *((struct in_addr *) &addr);
271   }
272
273   /* if its a hostname */
274   else if ((he = gethostbyname (sink->host))) {
275     sink->theiraddr.sin_addr = *((struct in_addr *) he->h_addr);
276   }
277
278   else {
279     perror ("hostname lookup error?");
280     return FALSE;
281   }
282
283   if ((sink->sock = socket (AF_INET, SOCK_DGRAM, 0)) == -1) {
284     perror ("socket");
285     return FALSE;
286   }
287
288   bc_val = 1;
289   setsockopt (sink->sock, SOL_SOCKET, SO_BROADCAST, &bc_val, sizeof (bc_val));
290
291   return TRUE;
292 }
293
294 static void
295 gst_udpsink_close (GstUDPSink * sink)
296 {
297   close (sink->sock);
298 }
299
300 static GstElementStateReturn
301 gst_udpsink_change_state (GstElement * element)
302 {
303   GstElementStateReturn ret;
304   GstUDPSink *sink;
305   gint transition;
306
307   sink = GST_UDPSINK (element);
308   transition = GST_STATE_TRANSITION (element);
309
310   switch (transition) {
311     case GST_STATE_READY_TO_PAUSED:
312       if (!gst_udpsink_init_send (sink))
313         goto no_init;
314       break;
315     default:
316       break;
317   }
318
319   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
320
321   switch (transition) {
322     case GST_STATE_PAUSED_TO_READY:
323       gst_udpsink_close (sink);
324       break;
325     default:
326       break;
327   }
328
329   return ret;
330
331 no_init:
332   {
333     return GST_STATE_FAILURE;
334   }
335 }