This is a megapatch with the following changes:
[platform/upstream/gstreamer.git] / gst / elements / gstasyncdisksrc.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstasyncdisksrc.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 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/mman.h>
28
29 //#define GST_DEBUG_ENABLED
30
31 #include "gstasyncdisksrc.h"
32
33
34 GstElementDetails gst_asyncdisksrc_details = {
35   "Asynchronous Disk Source",
36   "Source/File",
37   "Read from arbitrary point in a file",
38   VERSION,
39   "Erik Walthinsen <omega@cse.ogi.edu>",
40   "(C) 1999",
41 };
42
43
44 /* AsyncDiskSrc signals and args */
45 enum {
46   /* FILL ME */
47   LAST_SIGNAL
48 };
49
50 enum {
51   ARG_0,
52   ARG_LOCATION,
53   ARG_BYTESPERREAD,
54   ARG_OFFSET,
55   ARG_SIZE,
56 };
57
58
59 static void                     gst_asyncdisksrc_class_init     (GstAsyncDiskSrcClass *klass);
60 static void                     gst_asyncdisksrc_init           (GstAsyncDiskSrc *asyncdisksrc);
61
62 static void                     gst_asyncdisksrc_set_arg        (GtkObject *object, GtkArg *arg, guint id);
63 static void                     gst_asyncdisksrc_get_arg        (GtkObject *object, GtkArg *arg, guint id);
64
65 static GstBuffer *              gst_asyncdisksrc_get            (GstPad *pad);
66 static GstBuffer *              gst_asyncdisksrc_get_region     (GstPad *pad, gulong offset, gulong size);
67
68 static GstElementStateReturn    gst_asyncdisksrc_change_state   (GstElement *element);
69
70
71 static GstElementClass *parent_class = NULL;
72 //static guint gst_asyncdisksrc_signals[LAST_SIGNAL] = { 0 };
73
74 GtkType
75 gst_asyncdisksrc_get_type(void) 
76 {
77   static GtkType asyncdisksrc_type = 0;
78
79   if (!asyncdisksrc_type) {
80     static const GtkTypeInfo asyncdisksrc_info = {
81       "GstAsyncDiskSrc",
82       sizeof(GstAsyncDiskSrc),
83       sizeof(GstAsyncDiskSrcClass),
84       (GtkClassInitFunc)gst_asyncdisksrc_class_init,
85       (GtkObjectInitFunc)gst_asyncdisksrc_init,
86       (GtkArgSetFunc)gst_asyncdisksrc_set_arg,
87       (GtkArgGetFunc)gst_asyncdisksrc_get_arg,
88       (GtkClassInitFunc)NULL,
89     };
90     asyncdisksrc_type = gtk_type_unique (GST_TYPE_ELEMENT, &asyncdisksrc_info);
91   }
92   return asyncdisksrc_type;
93 }
94
95 static void
96 gst_asyncdisksrc_class_init (GstAsyncDiskSrcClass *klass) 
97 {
98   GtkObjectClass *gtkobject_class;
99   GstElementClass *gstelement_class;
100
101   gtkobject_class = (GtkObjectClass*)klass;
102   gstelement_class = (GstElementClass*)klass;
103
104   parent_class = gtk_type_class (GST_TYPE_ELEMENT);
105
106   gtk_object_add_arg_type ("GstAsyncDiskSrc::location", GST_TYPE_FILENAME,
107                            GTK_ARG_READWRITE, ARG_LOCATION);
108   gtk_object_add_arg_type ("GstAsyncDiskSrc::bytesperread", GTK_TYPE_INT,
109                            GTK_ARG_READWRITE, ARG_BYTESPERREAD);
110   gtk_object_add_arg_type ("GstAsyncDiskSrc::offset", GTK_TYPE_LONG,
111                            GTK_ARG_READWRITE, ARG_OFFSET);
112   gtk_object_add_arg_type ("GstAsyncDiskSrc::size", GTK_TYPE_LONG,
113                            GTK_ARG_READABLE, ARG_SIZE);
114
115   gtkobject_class->set_arg = gst_asyncdisksrc_set_arg;
116   gtkobject_class->get_arg = gst_asyncdisksrc_get_arg;
117
118   gstelement_class->change_state = gst_asyncdisksrc_change_state;
119 }
120
121 static void 
122 gst_asyncdisksrc_init (GstAsyncDiskSrc *asyncdisksrc) 
123 {
124 //  GST_FLAG_SET (asyncdisksrc, GST_SRC_ASYNC);
125
126   asyncdisksrc->srcpad = gst_pad_new ("src", GST_PAD_SRC);
127   gst_pad_set_get_function (asyncdisksrc->srcpad,gst_asyncdisksrc_get);
128   gst_pad_set_getregion_function (asyncdisksrc->srcpad,gst_asyncdisksrc_get_region);
129   gst_element_add_pad (GST_ELEMENT (asyncdisksrc), asyncdisksrc->srcpad);
130
131   asyncdisksrc->filename = NULL;
132   asyncdisksrc->fd = 0;
133   asyncdisksrc->size = 0;
134   asyncdisksrc->map = NULL;
135   asyncdisksrc->curoffset = 0;
136   asyncdisksrc->bytes_per_read = 4096;
137   asyncdisksrc->seq = 0;
138   asyncdisksrc->new_seek = FALSE;
139 }
140
141
142 static void 
143 gst_asyncdisksrc_set_arg (GtkObject *object, GtkArg *arg, guint id) 
144 {
145   GstAsyncDiskSrc *src;
146
147   /* it's not null if we got it, but it might not be ours */
148   g_return_if_fail (GST_IS_ASYNCDISKSRC (object));
149   
150   src = GST_ASYNCDISKSRC (object);
151
152   switch(id) {
153     case ARG_LOCATION:
154       /* the element must be stopped in order to do this */
155       g_return_if_fail (GST_STATE (src) < GST_STATE_PLAYING);
156
157       if (src->filename) g_free (src->filename);
158       /* clear the filename if we get a NULL (is that possible?) */
159       if (GTK_VALUE_STRING (*arg) == NULL) {
160         gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL);
161         src->filename = NULL;
162       /* otherwise set the new filename */
163       } else {
164         src->filename = g_strdup (GTK_VALUE_STRING (*arg));
165       }
166       break;
167     case ARG_BYTESPERREAD:
168       src->bytes_per_read = GTK_VALUE_INT (*arg);
169       break;
170     case ARG_OFFSET:
171       src->curoffset = GTK_VALUE_LONG (*arg);
172       src->new_seek = TRUE;
173       break;
174     default:
175       break;
176   }
177 }
178
179 static void 
180 gst_asyncdisksrc_get_arg (GtkObject *object, GtkArg *arg, guint id) 
181 {
182   GstAsyncDiskSrc *src;
183
184   /* it's not null if we got it, but it might not be ours */
185   g_return_if_fail (GST_IS_ASYNCDISKSRC (object));
186   
187   src = GST_ASYNCDISKSRC (object);
188
189   switch (id) {
190     case ARG_LOCATION:
191       GTK_VALUE_STRING (*arg) = src->filename;
192       break;
193     case ARG_BYTESPERREAD:
194       GTK_VALUE_INT (*arg) = src->bytes_per_read;
195       break;
196     case ARG_OFFSET:
197       GTK_VALUE_LONG (*arg) = src->curoffset;
198       break;
199     case ARG_SIZE:
200       GTK_VALUE_LONG (*arg) = src->size;
201       break;
202     default:
203       arg->type = GTK_TYPE_INVALID;
204       break;
205   }
206 }
207
208 /**
209  * gst_asyncdisksrc_get:
210  * @pad: #GstPad to push a buffer from
211  *
212  * Push a new buffer from the asyncdisksrc at the current offset.
213  */
214 static GstBuffer *
215 gst_asyncdisksrc_get (GstPad *pad) 
216 {
217   GstAsyncDiskSrc *src;
218   GstBuffer *buf;
219
220   g_return_val_if_fail (pad != NULL, NULL);
221   src = GST_ASYNCDISKSRC (gst_pad_get_parent (pad));
222   g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_ASYNCDISKSRC_OPEN), NULL);
223
224   /* deal with EOF state */
225   if (src->curoffset >= src->size) {
226     gst_element_signal_eos (GST_ELEMENT (src));
227     return NULL;
228   }
229
230   /* create the buffer */
231   // FIXME: should eventually use a bufferpool for this
232   buf = gst_buffer_new ();
233
234   g_return_val_if_fail (buf != NULL, NULL);
235
236   /* simply set the buffer to point to the correct region of the file */
237   GST_BUFFER_DATA (buf) = src->map + src->curoffset;
238   GST_BUFFER_OFFSET (buf) = src->curoffset;
239   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_DONTFREE);
240
241   if ((src->curoffset + src->bytes_per_read) > src->size) {
242     GST_BUFFER_SIZE (buf) = src->size - src->curoffset;
243     // FIXME: set the buffer's EOF bit here
244   } else
245     GST_BUFFER_SIZE (buf) = src->bytes_per_read;
246
247   GST_DEBUG (0,"map %p, offset %ld, size %d\n", src->map, src->curoffset, GST_BUFFER_SIZE (buf));
248
249   //gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
250
251   src->curoffset += GST_BUFFER_SIZE (buf);
252
253   if (src->new_seek) {
254     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLUSH);
255     GST_DEBUG (0,"new seek\n");
256     src->new_seek = FALSE;
257   }
258
259   /* we're done, return the buffer */
260   return buf;
261 }
262
263 /**
264  * gst_asyncdisksrc_get_region:
265  * @src: #GstSrc to push a buffer from
266  * @offset: offset in file
267  * @size: number of bytes
268  *
269  * Push a new buffer from the asyncdisksrc of given size at given offset.
270  */
271 static GstBuffer *
272 gst_asyncdisksrc_get_region (GstPad *pad, gulong offset, gulong size) 
273 {
274   GstAsyncDiskSrc *src;
275   GstBuffer *buf;
276
277   g_return_val_if_fail (pad != NULL, NULL);
278
279   src = GST_ASYNCDISKSRC (gst_pad_get_parent (pad));
280
281   g_return_val_if_fail (GST_IS_ASYNCDISKSRC (src), NULL);
282   g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_ASYNCDISKSRC_OPEN), NULL);
283   
284   /* deal with EOF state */
285   if (offset >= src->size) {
286     gst_element_signal_eos (GST_ELEMENT (src));
287     return NULL;
288   }
289
290   /* create the buffer */
291   // FIXME: should eventually use a bufferpool for this
292   buf = gst_buffer_new ();
293   g_return_val_if_fail (buf != NULL, NULL);
294
295   /* simply set the buffer to point to the correct region of the file */
296   GST_BUFFER_DATA (buf) = src->map + offset;
297   GST_BUFFER_OFFSET (buf) = offset;
298   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_DONTFREE);
299
300   if ((offset + size) > src->size) {
301     GST_BUFFER_SIZE (buf) = src->size - offset;
302     // FIXME: set the buffer's EOF bit here
303   } else
304     GST_BUFFER_SIZE (buf) = size;
305
306   GST_DEBUG (0,"map %p, offset %ld, size %d\n", src->map, offset, GST_BUFFER_SIZE (buf));
307
308   /* we're done, return the buffer off now */
309   return buf;
310 }
311
312
313 /* open the file and mmap it, necessary to go to READY state */
314 static 
315 gboolean gst_asyncdisksrc_open_file (GstAsyncDiskSrc *src) 
316 {
317   g_return_val_if_fail (!GST_FLAG_IS_SET (src ,GST_ASYNCDISKSRC_OPEN), FALSE);
318
319   /* open the file */
320   src->fd = open (src->filename, O_RDONLY);
321   if (src->fd < 0) {
322     perror ("open");
323     gst_element_error (GST_ELEMENT (src), g_strconcat("opening file \"", src->filename, "\"", NULL));
324     return FALSE;
325   } else {
326     /* find the file length */
327     src->size = lseek (src->fd, 0, SEEK_END);
328     lseek (src->fd, 0, SEEK_SET);
329     /* map the file into memory */
330     src->map = mmap (NULL, src->size, PROT_READ, MAP_SHARED, src->fd, 0);
331     madvise (src->map,src->size, 2);
332     /* collapse state if that failed */
333     if (src->map == NULL) {
334       close (src->fd);
335       gst_element_error (GST_ELEMENT (src),"mmapping file");
336       return FALSE;
337     }
338     GST_FLAG_SET (src, GST_ASYNCDISKSRC_OPEN);
339     src->new_seek = TRUE;
340   }
341   return TRUE;
342 }
343
344 /* unmap and close the file */
345 static void 
346 gst_asyncdisksrc_close_file (GstAsyncDiskSrc *src) 
347 {
348   g_return_if_fail (GST_FLAG_IS_SET (src, GST_ASYNCDISKSRC_OPEN));
349
350   /* unmap the file from memory */
351   munmap (src->map, src->size);
352   /* close the file */
353   close (src->fd);
354
355   /* zero out a lot of our state */
356   src->fd = 0;
357   src->size = 0;
358   src->map = NULL;
359   src->curoffset = 0;
360   src->seq = 0;
361   src->new_seek = FALSE;
362
363   GST_FLAG_UNSET (src, GST_ASYNCDISKSRC_OPEN);
364 }
365
366
367 static GstElementStateReturn 
368 gst_asyncdisksrc_change_state (GstElement *element) 
369 {
370   g_return_val_if_fail (GST_IS_ASYNCDISKSRC (element), GST_STATE_FAILURE);
371
372   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
373     if (GST_FLAG_IS_SET (element, GST_ASYNCDISKSRC_OPEN))
374       gst_asyncdisksrc_close_file (GST_ASYNCDISKSRC (element));
375   } else {
376     if (!GST_FLAG_IS_SET (element, GST_ASYNCDISKSRC_OPEN)) {
377       if (!gst_asyncdisksrc_open_file (GST_ASYNCDISKSRC (element))) 
378         return GST_STATE_FAILURE;
379     }
380   }
381
382   if (GST_ELEMENT_CLASS (parent_class)->change_state)
383     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
384
385   return GST_STATE_SUCCESS;
386 }