8d43417614daeee8c945d910d0e3f315d702e8bb
[platform/upstream/gstreamer.git] / plugins / elements / gstpipefilter.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstpipefilter.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
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 #ifdef HAVE_CONFIG_H
33 #  include "config.h"
34 #endif
35
36 #include "gstpipefilter.h"
37
38 GST_DEBUG_CATEGORY_STATIC (gst_pipefilter_debug);
39 #define GST_CAT_DEFAULT gst_pipefilter_debug
40
41 GstElementDetails gst_pipefilter_details = GST_ELEMENT_DETAILS (
42   "Pipefilter",
43   "Filter",
44   "Interoperate with an external program using stdin and stdout",
45   "Erik Walthinsen <omega@cse.ogi.edu>, "
46   "Wim Taymans <wim.taymans@chello.be>"
47 );
48
49
50 /* Pipefilter signals and args */
51 enum {
52   /* FILL ME */
53   LAST_SIGNAL
54 };
55
56 enum {
57   ARG_0,
58   ARG_COMMAND
59 };
60
61
62 #define _do_init(bla) \
63     GST_DEBUG_CATEGORY_INIT (gst_pipefilter_debug, "pipefilter", 0, "pipefilter element");
64
65 GST_BOILERPLATE_FULL (GstPipefilter, gst_pipefilter, GstElement, GST_TYPE_ELEMENT, _do_init);
66
67 static void                     gst_pipefilter_set_property     (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
68 static void                     gst_pipefilter_get_property     (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
69
70 static GstData*                 gst_pipefilter_get              (GstPad *pad);
71 static void                     gst_pipefilter_chain            (GstPad *pad, GstData *_data);
72 static gboolean                 gst_pipefilter_handle_event     (GstPad *pad, GstEvent *event);
73
74 static GstElementStateReturn    gst_pipefilter_change_state     (GstElement *element);
75
76 static void
77 gst_pipefilter_base_init (gpointer g_class)
78 {
79   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
80   
81   gst_element_class_set_details (gstelement_class, &gst_pipefilter_details);
82 }
83 static void 
84 gst_pipefilter_class_init (GstPipefilterClass *klass)
85 {
86   GObjectClass *gobject_class;
87   GstElementClass *gstelement_class;
88
89   gobject_class = (GObjectClass*)klass;
90   gstelement_class = (GstElementClass*)klass;
91
92
93   gobject_class->set_property = gst_pipefilter_set_property;  
94   gobject_class->get_property = gst_pipefilter_get_property;
95
96   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_COMMAND,
97     g_param_spec_string("command","command","command",
98                         NULL, G_PARAM_READWRITE)); /* CHECKME */
99
100   gstelement_class->change_state = gst_pipefilter_change_state;
101 }
102
103 static void
104 gst_pipefilter_init (GstPipefilter *pipefilter)
105 {
106   GST_FLAG_SET (pipefilter, GST_ELEMENT_DECOUPLED);
107
108   pipefilter->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
109   gst_element_add_pad (GST_ELEMENT (pipefilter), pipefilter->sinkpad);
110   gst_pad_set_chain_function (pipefilter->sinkpad, gst_pipefilter_chain);
111
112   pipefilter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
113   gst_element_add_pad (GST_ELEMENT (pipefilter), pipefilter->srcpad);
114   gst_pad_set_get_function (pipefilter->srcpad, gst_pipefilter_get);
115
116   pipefilter->command = NULL;
117   pipefilter->curoffset = 0;
118   pipefilter->bytes_per_read = 4096;
119   pipefilter->seq = 0;
120 }
121
122 static gboolean
123 gst_pipefilter_handle_event (GstPad *pad, GstEvent *event)
124 {
125   GstPipefilter *pipefilter;
126
127   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
128
129   GST_DEBUG ("pipefilter: %s received event", GST_ELEMENT_NAME (pipefilter));
130
131   switch (GST_EVENT_TYPE (event)) {
132     case GST_EVENT_EOS:
133       if (close (pipefilter->fdin[1]) < 0)
134         perror("close");
135       if (close (pipefilter->fdout[0]) < 0)
136         perror("close");
137       break;
138     default:
139       break;
140   }
141
142   gst_pad_event_default (pad, event);
143
144   return TRUE;
145 }
146
147 static GstData* 
148 gst_pipefilter_get (GstPad *pad)
149 {
150   GstPipefilter *pipefilter;
151   GstBuffer *newbuf;
152   glong readbytes;
153
154   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
155
156   /* create the buffer */
157   /* FIXME: should eventually use a bufferpool for this */
158   newbuf = gst_buffer_new();
159   g_return_val_if_fail(newbuf, NULL);
160
161   /* allocate the space for the buffer data */
162   GST_BUFFER_DATA(newbuf) = g_malloc(pipefilter->bytes_per_read);
163   g_return_val_if_fail(GST_BUFFER_DATA(newbuf) != NULL, NULL);
164
165   /* read it in from the file */
166   GST_DEBUG ("attemting to read %ld bytes", pipefilter->bytes_per_read);
167   readbytes = read(pipefilter->fdout[0], GST_BUFFER_DATA(newbuf), pipefilter->bytes_per_read);
168   GST_DEBUG ("read %ld bytes", readbytes);
169   if (readbytes < 0) {
170     perror("read");
171     gst_element_error(GST_ELEMENT(pipefilter),"reading");
172     return NULL;
173   }
174   /* if we didn't get as many bytes as we asked for, we're at EOF */
175   if (readbytes == 0) {
176     return GST_DATA (gst_event_new (GST_EVENT_EOS));
177
178   }
179
180   GST_BUFFER_OFFSET(newbuf) = pipefilter->curoffset;
181   GST_BUFFER_SIZE(newbuf) = readbytes;
182   pipefilter->curoffset += readbytes;
183
184   return GST_DATA (newbuf);
185 }
186
187 static void
188 gst_pipefilter_chain (GstPad *pad,GstData *_data)
189 {
190   GstBuffer *buf;
191   GstPipefilter *pipefilter;
192   glong writebytes;
193   guchar *data;
194   gulong size;
195
196   g_return_if_fail(pad != NULL);
197   g_return_if_fail(GST_IS_PAD(pad));
198
199   if (GST_IS_EVENT (_data)) {
200     gst_pipefilter_handle_event (pad, GST_EVENT (_data));
201     return;
202   }
203
204   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
205
206   buf = GST_BUFFER (_data);
207   data = GST_BUFFER_DATA(buf);
208   size = GST_BUFFER_SIZE(buf);
209
210   GST_DEBUG ("attemting to write %ld bytes", size);
211   writebytes = write(pipefilter->fdin[1],data,size);
212   GST_DEBUG ("written %ld bytes", writebytes);
213   if (writebytes < 0) {
214     perror("write");
215     gst_element_error(GST_ELEMENT(pipefilter),"writing");
216     return;
217   }
218   gst_buffer_unref(buf);
219 }
220
221 static void
222 gst_pipefilter_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
223 {
224   GstPipefilter *pipefilter;
225
226   /* it's not null if we got it, but it might not be ours */
227   g_return_if_fail(GST_IS_PIPEFILTER(object));
228   pipefilter = GST_PIPEFILTER(object);
229
230   switch (prop_id) {
231     case ARG_COMMAND:
232       pipefilter->orig_command = g_strdup(g_value_get_string (value));
233       pipefilter->command = g_strsplit(g_value_get_string (value), " ", 0);
234       break;
235     default:
236       break;
237   }
238 }
239
240 static void
241 gst_pipefilter_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
242 {
243   GstPipefilter *pipefilter;
244
245   /* it's not null if we got it, but it might not be ours */
246   g_return_if_fail(GST_IS_PIPEFILTER(object));
247   pipefilter = GST_PIPEFILTER(object);
248
249   switch (prop_id) {
250     case ARG_COMMAND:
251       g_value_set_string (value, pipefilter->orig_command);
252       break;
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255       break;
256   }
257 }
258
259 /* open the file, necessary to go to RUNNING state */
260 static gboolean
261 gst_pipefilter_open_file (GstPipefilter *src)
262 {
263   g_return_val_if_fail(!GST_FLAG_IS_SET(src,GST_PIPEFILTER_OPEN), FALSE);
264
265   pipe(src->fdin);
266   pipe(src->fdout);
267
268   if((src->childpid = fork()) == -1)
269   {
270     perror("fork");
271     gst_element_error(GST_ELEMENT(src),"forking");
272     return FALSE;
273   }
274
275   if(src->childpid == 0)
276   {
277     close(src->fdin[1]);
278     close(src->fdout[0]);
279     /* child */
280     dup2(src->fdin[0], STDIN_FILENO);  /* set the childs input stream */
281     dup2(src->fdout[1], STDOUT_FILENO);  /* set the childs output stream */
282     execvp(src->command[0], &src->command[0]);
283     /* will only reach if error */
284     perror("exec");
285     gst_element_error(GST_ELEMENT(src),"starting child process");
286     return FALSE;
287     
288   }
289   else {
290     close(src->fdin[0]);
291     close(src->fdout[1]);
292   }
293   
294   GST_FLAG_SET(src,GST_PIPEFILTER_OPEN);
295   return TRUE;
296 }
297
298 /* close the file */
299 static void
300 gst_pipefilter_close_file (GstPipefilter *src)
301 {
302   g_return_if_fail(GST_FLAG_IS_SET(src,GST_PIPEFILTER_OPEN));
303
304   /* close the file */
305   close(src->fdout[0]);
306   close(src->fdout[1]);
307   close(src->fdin[0]);
308   close(src->fdin[1]);
309
310   /* zero out a lot of our state */
311   src->curoffset = 0;
312   src->seq = 0;
313
314   GST_FLAG_UNSET(src,GST_PIPEFILTER_OPEN);
315 }
316
317 static GstElementStateReturn
318 gst_pipefilter_change_state (GstElement *element)
319 {
320   g_return_val_if_fail(GST_IS_PIPEFILTER(element), FALSE);
321
322   /* if going down into NULL state, close the file if it's open */ 
323   if (GST_STATE_PENDING(element) == GST_STATE_NULL) {
324     if (GST_FLAG_IS_SET(element,GST_PIPEFILTER_OPEN))
325       gst_pipefilter_close_file(GST_PIPEFILTER(element));
326   /* otherwise (READY or higher) we need to open the file */
327   } else {
328     if (!GST_FLAG_IS_SET(element,GST_PIPEFILTER_OPEN)) {
329       if (!gst_pipefilter_open_file(GST_PIPEFILTER(element)))
330         return GST_STATE_FAILURE;
331     }
332   }
333       
334   if (GST_ELEMENT_CLASS(parent_class)->change_state)
335     return GST_ELEMENT_CLASS(parent_class)->change_state(element);
336   return GST_STATE_SUCCESS;
337 }