This is a megapatch with the following changes:
[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 //#define DEBUG_ENABLED
33 #include "gstpipefilter.h"
34
35
36 GstElementDetails gst_pipefilter_details = {
37   "Pipefilter",
38   "Filter",
39   "Pass data without modification",
40   VERSION,
41   "Erik Walthinsen <omega@cse.ogi.edu>",
42   "(C) 1999",
43 };
44
45
46 /* Pipefilter signals and args */
47 enum {
48   /* FILL ME */
49   LAST_SIGNAL
50 };
51
52 enum {
53   ARG_0,
54   ARG_COMMAND
55 };
56
57
58 static void gst_pipefilter_class_init(GstPipefilterClass *klass);
59 static void gst_pipefilter_init(GstPipefilter *pipefilter);
60 static void gst_pipefilter_set_arg(GtkObject *object,GtkArg *arg,guint id);
61 static void gst_pipefilter_get_arg(GtkObject *object,GtkArg *arg,guint id);
62
63 void gst_pipefilter_chain(GstPad *pad,GstBuffer *buf);
64
65 static GstElementStateReturn gst_pipefilter_change_state(GstElement *element);
66
67 static GstElementClass *parent_class = NULL;
68 //static guint gst_pipefilter_signals[LAST_SIGNAL] = { 0 };
69
70 GtkType
71 gst_pipefilter_get_type(void) {
72   static GtkType pipefilter_type = 0;
73
74   if (!pipefilter_type) {
75     static const GtkTypeInfo pipefilter_info = {
76       "GstPipefilter",
77       sizeof(GstPipefilter),
78       sizeof(GstPipefilterClass),
79       (GtkClassInitFunc)gst_pipefilter_class_init,
80       (GtkObjectInitFunc)gst_pipefilter_init,
81       (GtkArgSetFunc)gst_pipefilter_set_arg,
82       (GtkArgGetFunc)gst_pipefilter_get_arg,
83       (GtkClassInitFunc)NULL,
84     };
85     pipefilter_type = gtk_type_unique(GST_TYPE_ELEMENT,&pipefilter_info);
86   }
87   return pipefilter_type;
88 }
89
90 static void gst_pipefilter_class_init(GstPipefilterClass *klass) {
91   GtkObjectClass *gtkobject_class;
92   GstElementClass *gstelement_class;
93
94   gtkobject_class = (GtkObjectClass*)klass;
95   gstelement_class = (GstElementClass*)klass;
96
97   parent_class = gtk_type_class(GST_TYPE_ELEMENT);
98
99   gstelement_class->change_state = gst_pipefilter_change_state;
100
101   gtk_object_add_arg_type("GstPipefilter::command", GTK_TYPE_STRING,
102                           GTK_ARG_READWRITE, ARG_COMMAND);
103
104   gtkobject_class->set_arg = gst_pipefilter_set_arg;  
105   gtkobject_class->get_arg = gst_pipefilter_get_arg;
106 }
107
108 static void gst_pipefilter_init(GstPipefilter *pipefilter) {
109   pipefilter->sinkpad = gst_pad_new("sink",GST_PAD_SINK);
110   gst_element_add_pad(GST_ELEMENT(pipefilter),pipefilter->sinkpad);
111   gst_pad_set_chain_function(pipefilter->sinkpad,gst_pipefilter_chain);
112   pipefilter->srcpad = gst_pad_new("src",GST_PAD_SRC);
113   gst_element_add_pad(GST_ELEMENT(pipefilter),pipefilter->srcpad);
114
115   pipefilter->command = NULL;
116   pipefilter->curoffset = 0;
117   pipefilter->bytes_per_read = 4096;
118   pipefilter->seq = 0;
119 }
120
121 static gboolean gst_pipefilter_read_and_push(GstPipefilter *pipefilter) {
122   GstBuffer *newbuf;
123   glong readbytes;
124
125   /* create the buffer */
126   // FIXME: should eventually use a bufferpool for this
127   newbuf = gst_buffer_new();
128   g_return_val_if_fail(newbuf, FALSE);
129
130   /* allocate the space for the buffer data */
131   GST_BUFFER_DATA(newbuf) = g_malloc(pipefilter->bytes_per_read);
132   g_return_val_if_fail(GST_BUFFER_DATA(newbuf) != NULL, FALSE);
133
134   /* read it in from the file */
135   GST_DEBUG (0,"attemting to read %ld bytes\n", pipefilter->bytes_per_read);
136   readbytes = read(pipefilter->fdout[0],GST_BUFFER_DATA(newbuf),pipefilter->bytes_per_read);
137   GST_DEBUG (0,"read %ld bytes\n", readbytes);
138   if (readbytes < 0) {
139     if (errno == EAGAIN) {
140       GST_DEBUG (0,"no input yet\n");
141       gst_buffer_unref(newbuf);
142       return FALSE;
143     }
144     else {
145       perror("read");
146       gst_element_error(GST_ELEMENT(pipefilter),"reading");
147       return FALSE;
148     }
149   }
150   if (readbytes == 0) {
151     gst_buffer_unref(newbuf);
152     return FALSE;
153   }
154   /* if we didn't get as many bytes as we asked for, we're at EOF */
155   if (readbytes < pipefilter->bytes_per_read)
156     GST_BUFFER_FLAG_SET(newbuf,GST_BUFFER_EOS);
157   GST_BUFFER_OFFSET(newbuf) = pipefilter->curoffset;
158   GST_BUFFER_SIZE(newbuf) = readbytes;
159   pipefilter->curoffset += readbytes;
160
161   /* we're done, push the buffer off now */
162   gst_pad_push(pipefilter->srcpad,newbuf);
163   return TRUE;
164 }
165 void gst_pipefilter_chain(GstPad *pad,GstBuffer *buf) {
166   GstPipefilter *pipefilter;
167   glong writebytes;
168   guchar *data;
169   gulong size;
170
171   g_return_if_fail(pad != NULL);
172   g_return_if_fail(GST_IS_PAD(pad));
173   g_return_if_fail(buf != NULL);
174
175   pipefilter = GST_PIPEFILTER (gst_pad_get_parent (pad));
176
177   while (gst_pipefilter_read_and_push(pipefilter));
178
179   data = GST_BUFFER_DATA(buf);
180   size = GST_BUFFER_SIZE(buf);
181
182   GST_DEBUG (0,"attemting to write %ld bytes\n", size);
183   writebytes = write(pipefilter->fdin[1],data,size);
184   GST_DEBUG (0,"written %ld bytes\n", writebytes);
185   if (writebytes < 0) {
186     perror("write");
187     gst_element_error(GST_ELEMENT(pipefilter),"writing");
188     return;
189   }
190   gst_buffer_unref(buf);
191
192   while (gst_pipefilter_read_and_push(pipefilter));
193 }
194
195 static void gst_pipefilter_set_arg(GtkObject *object,GtkArg *arg,guint id) {
196   GstPipefilter *pipefilter;
197
198   /* it's not null if we got it, but it might not be ours */
199   g_return_if_fail(GST_IS_PIPEFILTER(object));
200   pipefilter = GST_PIPEFILTER(object);
201
202   switch(id) {
203     case ARG_COMMAND:
204       pipefilter->orig_command = g_strdup(GTK_VALUE_STRING(*arg));
205       pipefilter->command = g_strsplit(GTK_VALUE_STRING(*arg), " ", 0);
206       break;
207     default:
208       break;
209   }
210 }
211
212 static void gst_pipefilter_get_arg(GtkObject *object,GtkArg *arg,guint id) {
213   GstPipefilter *pipefilter;
214
215   /* it's not null if we got it, but it might not be ours */
216   g_return_if_fail(GST_IS_PIPEFILTER(object));
217   pipefilter = GST_PIPEFILTER(object);
218
219   switch (id) {
220     case ARG_COMMAND:
221       GTK_VALUE_STRING(*arg) = pipefilter->orig_command;
222       break;
223     default:
224       arg->type = GTK_TYPE_INVALID;
225       break;
226   }
227 }
228
229 /* open the file, necessary to go to RUNNING state */
230 static gboolean gst_pipefilter_open_file(GstPipefilter *src) {
231   g_return_val_if_fail(!GST_FLAG_IS_SET(src,GST_PIPEFILTER_OPEN), FALSE);
232
233   pipe(src->fdin);
234   pipe(src->fdout);
235
236   if (fcntl(src->fdout[0], F_SETFL, O_NONBLOCK) < 0) {
237     perror("fcntl");
238     gst_element_error(GST_ELEMENT(src),"fcntl");
239     return FALSE;
240   }
241
242   if((src->childpid = fork()) == -1)
243   {
244     perror("fork");
245     gst_element_error(GST_ELEMENT(src),"forking");
246     return FALSE;
247   }
248
249   if(src->childpid == 0)
250   {
251     // child
252     dup2(src->fdin[0], STDIN_FILENO);  /* set the childs input stream */
253     dup2(src->fdout[1], STDOUT_FILENO);  /* set the childs output stream */
254     execvp(src->command[0], &src->command[0]);
255     // will only reach if error
256     perror("exec");
257     gst_element_error(GST_ELEMENT(src),"starting child process");
258     return FALSE;
259     
260   }
261   
262   GST_FLAG_SET(src,GST_PIPEFILTER_OPEN);
263   return TRUE;
264 }
265
266 /* close the file */
267 static void gst_pipefilter_close_file(GstPipefilter *src) {
268   g_return_if_fail(GST_FLAG_IS_SET(src,GST_PIPEFILTER_OPEN));
269
270   /* close the file */
271   close(src->fdout[0]);
272   close(src->fdout[1]);
273   close(src->fdin[0]);
274   close(src->fdin[1]);
275
276   /* zero out a lot of our state */
277   src->curoffset = 0;
278   src->seq = 0;
279
280   GST_FLAG_UNSET(src,GST_PIPEFILTER_OPEN);
281 }
282
283 static GstElementStateReturn gst_pipefilter_change_state(GstElement *element) {
284   g_return_val_if_fail(GST_IS_PIPEFILTER(element), FALSE);
285
286   /* if going down into NULL state, close the file if it's open */ 
287   if (GST_STATE_PENDING(element) == GST_STATE_NULL) {
288     if (GST_FLAG_IS_SET(element,GST_PIPEFILTER_OPEN))
289       gst_pipefilter_close_file(GST_PIPEFILTER(element));
290   /* otherwise (READY or higher) we need to open the file */
291   } else {
292     if (!GST_FLAG_IS_SET(element,GST_PIPEFILTER_OPEN)) {
293       if (!gst_pipefilter_open_file(GST_PIPEFILTER(element)))
294         return GST_STATE_FAILURE;
295     }
296   }
297       
298   if (GST_ELEMENT_CLASS(parent_class)->change_state)
299     return GST_ELEMENT_CLASS(parent_class)->change_state(element);
300   return GST_STATE_SUCCESS;
301 }