gst/elements/: Added an element that writes to multiple filedescriptors at once.
[platform/upstream/gstreamer.git] / gst / elements / gstmultifdsink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2004 Wim Taymans <wim@fluendo.com>
4  *
5  * gstmultifdsink.c: 
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "gstmultifdsink.h"
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 GST_DEBUG_CATEGORY_STATIC (gst_multifdsink_debug);
33 #define GST_CAT_DEFAULT gst_multifdsink_debug
34
35 GstElementDetails gst_multifdsink_details =
36 GST_ELEMENT_DETAILS ("Filedescriptor Sink",
37     "Sink/File",
38     "Write data to one or more file descriptors",
39     "Erik Walthinsen <omega@cse.ogi.edu>");
40
41
42 /* MultiFdSink signals and args */
43 enum
44 {
45   ADD_SIGNAL,
46   REMOVE_SIGNAL,
47   CLEAR_SIGNAL,
48   /* FILL ME */
49   LAST_SIGNAL
50 };
51 static guint gst_multifdsink_signals[LAST_SIGNAL] = { 0 };
52
53 enum
54 {
55   ARG_0,
56   ARG_FDS
57 };
58
59
60 #define _do_init(bla) \
61     GST_DEBUG_CATEGORY_INIT (gst_multifdsink_debug, "multifdsink", 0, "multifdsink element");
62
63 GST_BOILERPLATE_FULL (GstMultiFdSink, gst_multifdsink, GstElement,
64     GST_TYPE_ELEMENT, _do_init);
65
66 static void gst_multifdsink_add (GstElement * sink, gint fd);
67 static void gst_multifdsink_remove (GstElement * sink, gint fd);
68 static void gst_multifdsink_clear (GstElement * sink);
69
70 static void gst_multifdsink_set_property (GObject * object, guint prop_id,
71     const GValue * value, GParamSpec * pspec);
72 static void gst_multifdsink_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec);
74
75 static void gst_multifdsink_chain (GstPad * pad, GstData * _data);
76
77
78 static void
79 gst_multifdsink_base_init (gpointer g_class)
80 {
81   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
82
83   gst_element_class_set_details (gstelement_class, &gst_multifdsink_details);
84 }
85 static void
86 gst_multifdsink_class_init (GstMultiFdSinkClass * klass)
87 {
88   GObjectClass *gobject_class;
89
90   gobject_class = G_OBJECT_CLASS (klass);
91
92   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FDS,
93       g_param_spec_int ("fds", "fds", "A GArray of filedescriptors",
94           0, G_MAXINT, 1, G_PARAM_READWRITE));
95
96   gst_multifdsink_signals[ADD_SIGNAL] =
97       g_signal_new ("add", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
98       G_STRUCT_OFFSET (GstMultiFdSinkClass, add),
99       NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 0);
100   gst_multifdsink_signals[REMOVE_SIGNAL] =
101       g_signal_new ("remove", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
102       G_STRUCT_OFFSET (GstMultiFdSinkClass, remove),
103       NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 0);
104   gst_multifdsink_signals[CLEAR_SIGNAL] =
105       g_signal_new ("clear", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
106       G_STRUCT_OFFSET (GstMultiFdSinkClass, clear),
107       NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
108
109   klass->add = gst_multifdsink_add;
110   klass->remove = gst_multifdsink_remove;
111   klass->clear = gst_multifdsink_clear;
112
113   gobject_class->set_property = gst_multifdsink_set_property;
114   gobject_class->get_property = gst_multifdsink_get_property;
115 }
116
117 static void
118 gst_multifdsink_init (GstMultiFdSink * multifdsink)
119 {
120   multifdsink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
121   gst_element_add_pad (GST_ELEMENT (multifdsink), multifdsink->sinkpad);
122   gst_pad_set_chain_function (multifdsink->sinkpad, gst_multifdsink_chain);
123
124   FD_ZERO (&multifdsink->writefds);
125 }
126
127 static void
128 gst_multifdsink_add (GstElement * element, gint fd)
129 {
130   GstMultiFdSink *sink = GST_MULTIFDSINK (element);
131
132   FD_SET (fd, &sink->writefds);
133 }
134
135 static void
136 gst_multifdsink_remove (GstElement * element, gint fd)
137 {
138   GstMultiFdSink *sink = GST_MULTIFDSINK (element);
139
140   FD_CLR (fd, &sink->writefds);
141 }
142
143 static void
144 gst_multifdsink_clear (GstElement * element)
145 {
146   GstMultiFdSink *sink = GST_MULTIFDSINK (element);
147
148   FD_ZERO (&sink->writefds);
149 }
150
151 static void
152 gst_multifdsink_chain (GstPad * pad, GstData * _data)
153 {
154   GstBuffer *buf;
155   GstMultiFdSink *sink;
156   struct timeval timeout;
157   struct timeval *timeoutp;
158   gint result;
159   int fd;
160
161   sink = GST_MULTIFDSINK (gst_pad_get_parent (pad));
162   buf = GST_BUFFER (_data);
163
164   /* if the incoming buffer has a duration, we can use that as the timeout
165    * value; otherwise, we block */
166   GST_LOG_OBJECT (sink, "incoming buffer duration: %" GST_TIME_FORMAT,
167       GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
168
169   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buf))) {
170     GST_TIME_TO_TIMEVAL (GST_BUFFER_DURATION (buf), timeout);
171     timeoutp = &timeout;
172     GST_LOG_OBJECT (sink, "select will be with timeout %" GST_TIME_FORMAT,
173         GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
174     GST_LOG_OBJECT (sink, "select will be with timeout %d.%d",
175         timeout.tv_sec, timeout.tv_usec);
176   } else {
177     timeout.tv_sec = 0;
178     timeout.tv_usec = 0;
179     timeoutp = NULL;
180   }
181
182   result = select (FD_SETSIZE, (fd_set *) 0, &sink->writefds, (fd_set *) 0,
183       timeoutp);
184
185   /* Check the writes */
186   for (fd = 0; fd < FD_SETSIZE; fd++) {
187     if (FD_ISSET (fd, &sink->writefds)) {
188       gint writesize = GST_BUFFER_SIZE (buf);
189       gint written;
190
191       GST_DEBUG ("writing %d bytes to file descriptor %d", writesize, fd);
192       written = write (fd, GST_BUFFER_DATA (buf), writesize);
193       if (written < writesize) {
194         GST_DEBUG ("wrote only %d bytes, removing filedescriptor %d ", written,
195             fd);
196         gst_multifdsink_remove (GST_ELEMENT (sink), fd);
197       }
198     }
199   }
200
201   gst_buffer_unref (buf);
202 }
203
204 static void
205 gst_multifdsink_set_property (GObject * object, guint prop_id,
206     const GValue * value, GParamSpec * pspec)
207 {
208   GstMultiFdSink *multifdsink;
209
210   multifdsink = GST_MULTIFDSINK (object);
211
212   switch (prop_id) {
213     case ARG_FDS:
214       break;
215     default:
216       break;
217   }
218 }
219
220 static void
221 gst_multifdsink_get_property (GObject * object, guint prop_id, GValue * value,
222     GParamSpec * pspec)
223 {
224   GstMultiFdSink *multifdsink;
225
226   multifdsink = GST_MULTIFDSINK (object);
227
228   switch (prop_id) {
229     case ARG_FDS:
230       break;
231     default:
232       break;
233   }
234 }