suffix errors with period.
[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 <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32
33 #ifdef HAVE_CONFIG_H
34 #  include "config.h"
35 #endif
36
37 #include "../gst-i18n-lib.h"
38 #include "gstpipefilter.h"
39
40 GST_DEBUG_CATEGORY_STATIC (gst_pipefilter_debug);
41 #define GST_CAT_DEFAULT gst_pipefilter_debug
42
43 GstElementDetails gst_pipefilter_details = GST_ELEMENT_DETAILS (
44   "Pipefilter",
45   "Filter",
46   "Interoperate with an external program using stdin and stdout",
47   "Erik Walthinsen <omega@cse.ogi.edu>, "
48   "Wim Taymans <wim.taymans@chello.be>"
49 );
50
51
52 /* Pipefilter signals and args */
53 enum {
54   /* FILL ME */
55   LAST_SIGNAL
56 };
57
58 enum {
59   ARG_0,
60   ARG_COMMAND
61 };
62
63
64 #define _do_init(bla) \
65     GST_DEBUG_CATEGORY_INIT (gst_pipefilter_debug, "pipefilter", 0, "pipefilter element");
66
67 GST_BOILERPLATE_FULL (GstPipefilter, gst_pipefilter, GstElement, GST_TYPE_ELEMENT, _do_init);
68
69 static void                     gst_pipefilter_set_property     (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
70 static void                     gst_pipefilter_get_property     (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
71
72 static GstData*                 gst_pipefilter_get              (GstPad *pad);
73 static void                     gst_pipefilter_chain            (GstPad *pad, GstData *_data);
74 static gboolean                 gst_pipefilter_handle_event     (GstPad *pad, GstEvent *event);
75
76 static GstElementStateReturn    gst_pipefilter_change_state     (GstElement *element);
77
78 static void
79 gst_pipefilter_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_pipefilter_details);
84 }
85 static void 
86 gst_pipefilter_class_init (GstPipefilterClass *klass)
87 {
88   GObjectClass *gobject_class;
89   GstElementClass *gstelement_class;
90
91   gobject_class = (GObjectClass*)klass;
92   gstelement_class = (GstElementClass*)klass;
93
94
95   gobject_class->set_property = gst_pipefilter_set_property;  
96   gobject_class->get_property = gst_pipefilter_get_property;
97
98   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_COMMAND,
99     g_param_spec_string("command","command","command",
100                         NULL, G_PARAM_READWRITE)); /* CHECKME */
101
102   gstelement_class->change_state = gst_pipefilter_change_state;
103 }
104
105 static void
106 gst_pipefilter_init (GstPipefilter *pipefilter)
107 {
108   GST_FLAG_SET (pipefilter, GST_ELEMENT_DECOUPLED);
109
110   pipefilter->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
111   gst_element_add_pad (GST_ELEMENT (pipefilter), pipefilter->sinkpad);
112   gst_pad_set_chain_function (pipefilter->sinkpad, gst_pipefilter_chain);
113
114   pipefilter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
115   gst_element_add_pad (GST_ELEMENT (pipefilter), pipefilter->srcpad);
116   gst_pad_set_get_function (pipefilter->srcpad, gst_pipefilter_get);
117
118   pipefilter->command = NULL;
119   pipefilter->curoffset = 0;
120   pipefilter->bytes_per_read = 4096;
121   pipefilter->seq = 0;
122 }
123
124 static gboolean
125 gst_pipefilter_handle_event (GstPad *pad, GstEvent *event)
126 {
127   GstPipefilter *pipefilter;
128
129   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
130
131   GST_DEBUG ("pipefilter: %s received event", GST_ELEMENT_NAME (pipefilter));
132
133   switch (GST_EVENT_TYPE (event)) {
134     case GST_EVENT_EOS:
135       if (close (pipefilter->fdin[1]) < 0)
136         perror("close");
137       if (close (pipefilter->fdout[0]) < 0)
138         perror("close");
139       break;
140     default:
141       break;
142   }
143
144   gst_pad_event_default (pad, event);
145
146   return TRUE;
147 }
148
149 static GstData* 
150 gst_pipefilter_get (GstPad *pad)
151 {
152   GstPipefilter *pipefilter;
153   GstBuffer *newbuf;
154   glong readbytes;
155
156   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
157
158   /* create the buffer */
159   /* FIXME: should eventually use a bufferpool for this */
160   newbuf = gst_buffer_new();
161   g_return_val_if_fail(newbuf, NULL);
162
163   /* allocate the space for the buffer data */
164   GST_BUFFER_DATA(newbuf) = g_malloc(pipefilter->bytes_per_read);
165   g_return_val_if_fail(GST_BUFFER_DATA(newbuf) != NULL, NULL);
166
167   /* read it in from the file */
168   GST_DEBUG ("attemting to read %ld bytes", pipefilter->bytes_per_read);
169   readbytes = read(pipefilter->fdout[0], GST_BUFFER_DATA(newbuf), pipefilter->bytes_per_read);
170   GST_DEBUG ("read %ld bytes", readbytes);
171   if (readbytes < 0) {
172     GST_ELEMENT_ERROR (pipefilter, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
173     return NULL;
174   }
175   /* if we didn't get as many bytes as we asked for, we're at EOF */
176   if (readbytes == 0) {
177     return GST_DATA (gst_event_new (GST_EVENT_EOS));
178
179   }
180
181   GST_BUFFER_OFFSET(newbuf) = pipefilter->curoffset;
182   GST_BUFFER_SIZE(newbuf) = readbytes;
183   pipefilter->curoffset += readbytes;
184
185   return GST_DATA (newbuf);
186 }
187
188 static void
189 gst_pipefilter_chain (GstPad *pad,GstData *_data)
190 {
191   GstBuffer *buf;
192   GstPipefilter *pipefilter;
193   glong writebytes;
194   guchar *data;
195   gulong size;
196
197   g_return_if_fail(pad != NULL);
198   g_return_if_fail(GST_IS_PAD(pad));
199
200   if (GST_IS_EVENT (_data)) {
201     gst_pipefilter_handle_event (pad, GST_EVENT (_data));
202     return;
203   }
204
205   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
206
207   buf = GST_BUFFER (_data);
208   data = GST_BUFFER_DATA(buf);
209   size = GST_BUFFER_SIZE(buf);
210
211   GST_DEBUG ("attemting to write %ld bytes", size);
212   writebytes = write(pipefilter->fdin[1],data,size);
213   GST_DEBUG ("written %ld bytes", writebytes);
214   if (writebytes < 0) {
215     GST_ELEMENT_ERROR (pipefilter, RESOURCE, WRITE, (NULL), GST_ERROR_SYSTEM);
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     GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL), GST_ERROR_SYSTEM);
271     return FALSE;
272   }
273
274   if(src->childpid == 0)
275   {
276     close(src->fdin[1]);
277     close(src->fdout[0]);
278     /* child */
279     dup2(src->fdin[0], STDIN_FILENO);  /* set the childs input stream */
280     dup2(src->fdout[1], STDOUT_FILENO);  /* set the childs output stream */
281     execvp(src->command[0], &src->command[0]);
282     /* will only be reached if execvp has an error */
283     GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY, (NULL), GST_ERROR_SYSTEM);
284     return FALSE;
285     
286   }
287   else {
288     close(src->fdin[0]);
289     close(src->fdout[1]);
290   }
291   
292   GST_FLAG_SET(src,GST_PIPEFILTER_OPEN);
293   return TRUE;
294 }
295
296 /* close the file */
297 static void
298 gst_pipefilter_close_file (GstPipefilter *src)
299 {
300   g_return_if_fail(GST_FLAG_IS_SET(src,GST_PIPEFILTER_OPEN));
301
302   /* close the file */
303   close(src->fdout[0]);
304   close(src->fdout[1]);
305   close(src->fdin[0]);
306   close(src->fdin[1]);
307
308   /* zero out a lot of our state */
309   src->curoffset = 0;
310   src->seq = 0;
311
312   GST_FLAG_UNSET(src,GST_PIPEFILTER_OPEN);
313 }
314
315 static GstElementStateReturn
316 gst_pipefilter_change_state (GstElement *element)
317 {
318   g_return_val_if_fail(GST_IS_PIPEFILTER(element), FALSE);
319
320   /* if going down into NULL state, close the file if it's open */ 
321   if (GST_STATE_PENDING(element) == GST_STATE_NULL) {
322     if (GST_FLAG_IS_SET(element,GST_PIPEFILTER_OPEN))
323       gst_pipefilter_close_file(GST_PIPEFILTER(element));
324   /* otherwise (READY or higher) we need to open the file */
325   } else {
326     if (!GST_FLAG_IS_SET(element,GST_PIPEFILTER_OPEN)) {
327       if (!gst_pipefilter_open_file(GST_PIPEFILTER(element)))
328         return GST_STATE_FAILURE;
329     }
330   }
331       
332   if (GST_ELEMENT_CLASS(parent_class)->change_state)
333     return GST_ELEMENT_CLASS(parent_class)->change_state(element);
334   return GST_STATE_SUCCESS;
335 }