Small cleanups, more descriptive properties
[platform/upstream/gstreamer.git] / gst / elements / gstfilesink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstfilesink.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 #include <gst/gst.h>
25 #include <errno.h>
26 #include "gstfilesink.h"
27 #include <string.h>
28
29 GstElementDetails gst_filesink_details = {
30   "File Sink",
31   "Sink/File",
32   "LGPL",
33   "Write stream to a file",
34   VERSION,
35   "Thomas <thomas@apestaart.org>",
36   "(C) 2001"
37 };
38
39
40 /* FileSink signals and args */
41 enum {
42   /* FILL ME */
43   SIGNAL_HANDOFF,
44   LAST_SIGNAL
45 };
46
47 enum {
48   ARG_0,
49   ARG_LOCATION,
50   ARG_MAXFILESIZE,
51 };
52
53 GST_EVENT_MASK_FUNCTION (gst_filesink_get_event_mask,
54   { GST_EVENT_SEEK, GST_SEEK_METHOD_CUR |
55                     GST_SEEK_METHOD_SET |
56                     GST_SEEK_METHOD_END |
57                     GST_SEEK_FLAG_FLUSH },
58   { GST_EVENT_FLUSH, 0 },
59   { GST_EVENT_DISCONTINUOUS, 0 },
60   { GST_EVENT_NEW_MEDIA, 0 }
61 )
62
63
64 static void     gst_filesink_class_init         (GstFileSinkClass *klass);
65 static void     gst_filesink_init               (GstFileSink *filesink);
66
67 static void     gst_filesink_set_property       (GObject *object, guint prop_id, 
68                                                  const GValue *value, GParamSpec *pspec);
69 static void     gst_filesink_get_property       (GObject *object, guint prop_id, 
70                                                  GValue *value, GParamSpec *pspec);
71
72 static gboolean gst_filesink_open_file          (GstFileSink *sink);
73 static void     gst_filesink_close_file         (GstFileSink *sink);
74
75 static gboolean gst_filesink_handle_event       (GstPad *pad, GstEvent *event);
76 static void     gst_filesink_chain              (GstPad *pad,GstBuffer *buf);
77
78 static GstElementStateReturn gst_filesink_change_state (GstElement *element);
79
80 static GstElementClass *parent_class = NULL;
81 static guint gst_filesink_signals[LAST_SIGNAL] = { 0 };
82
83 GType
84 gst_filesink_get_type (void) 
85 {
86   static GType filesink_type = 0;
87
88   if (!filesink_type) {
89     static const GTypeInfo filesink_info = {
90       sizeof(GstFileSinkClass),      NULL,
91       NULL,
92       (GClassInitFunc)gst_filesink_class_init,
93       NULL,
94       NULL,
95       sizeof(GstFileSink),
96       0,
97       (GInstanceInitFunc)gst_filesink_init,
98     };
99     filesink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSink", &filesink_info, 0);
100   }
101   return filesink_type;
102 }
103
104 static void
105 gst_filesink_class_init (GstFileSinkClass *klass) 
106 {
107   GObjectClass *gobject_class;
108   GstElementClass *gstelement_class;
109
110   gobject_class = (GObjectClass*)klass;
111   gstelement_class = (GstElementClass*)klass;
112
113   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
114
115   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
116     g_param_spec_string ("location", "File Location", "Location of the file to write",
117                          NULL, G_PARAM_READWRITE));
118   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAXFILESIZE,
119     g_param_spec_int ("maxfilesize", "MaxFileSize", "Maximum Size Per File in MB (-1 == no limit)",
120                       -1, G_MAXINT, -1, G_PARAM_READWRITE));
121
122   gst_filesink_signals[SIGNAL_HANDOFF] =
123     g_signal_new ("handoff", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
124                     G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL,
125                     g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
126
127   gobject_class->set_property = gst_filesink_set_property;
128   gobject_class->get_property = gst_filesink_get_property;
129
130   gstelement_class->change_state = gst_filesink_change_state;
131 }
132
133 static void 
134 gst_filesink_init (GstFileSink *filesink) 
135 {
136   GstPad *pad;
137
138   pad = gst_pad_new ("sink", GST_PAD_SINK);
139   gst_element_add_pad (GST_ELEMENT (filesink), pad);
140   gst_pad_set_chain_function (pad, gst_filesink_chain);
141
142   GST_FLAG_SET (GST_ELEMENT(filesink), GST_ELEMENT_EVENT_AWARE);
143   gst_pad_set_event_function(pad, gst_filesink_handle_event);
144   gst_pad_set_event_mask_function(pad, gst_filesink_get_event_mask);
145
146   filesink->filename = NULL;
147   filesink->file = NULL;
148   filesink->filenum = 0;
149
150   filesink->maxfilesize = -1;
151 }
152
153 static char *
154 gst_filesink_getcurrentfilename (GstFileSink *filesink)
155 {
156   g_return_val_if_fail(filesink != NULL, NULL);
157   g_return_val_if_fail(GST_IS_FILESINK(filesink), NULL);
158   if (filesink->filename == NULL) return NULL;
159   g_return_val_if_fail(filesink->filenum >= 0, NULL);
160
161   if (!strstr(filesink->filename, "%"))
162   {
163     if (!filesink->filenum)
164       return g_strdup(filesink->filename);
165     else
166       return NULL;
167   }
168
169   return g_strdup_printf(filesink->filename, filesink->filenum);
170 }
171
172 static void
173 gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
174 {
175   GstFileSink *sink;
176
177   /* it's not null if we got it, but it might not be ours */
178   sink = GST_FILESINK (object);
179
180   switch (prop_id) {
181     case ARG_LOCATION:
182       /* the element must be stopped or paused in order to do this */
183       g_return_if_fail ((GST_STATE (sink) < GST_STATE_PLAYING)
184                       || (GST_STATE (sink) == GST_STATE_PAUSED));
185       if (sink->filename)
186         g_free (sink->filename);
187       sink->filename = g_strdup (g_value_get_string (value));
188       if ( (GST_STATE (sink) == GST_STATE_PAUSED) 
189         && (sink->filename != NULL))
190       {
191               gst_filesink_close_file (sink);
192               gst_filesink_open_file (sink);   
193       }
194  
195       break;
196     case ARG_MAXFILESIZE:
197       sink->maxfilesize = g_value_get_int(value);
198       break;
199     default:
200       break;
201   }
202 }
203
204 static void   
205 gst_filesink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
206 {
207   GstFileSink *sink;
208  
209   /* it's not null if we got it, but it might not be ours */
210   g_return_if_fail (GST_IS_FILESINK (object));
211  
212   sink = GST_FILESINK (object);
213   
214   switch (prop_id) {
215     case ARG_LOCATION:
216       g_value_set_string (value, gst_filesink_getcurrentfilename(sink));
217       break;
218     case ARG_MAXFILESIZE:
219       g_value_set_int (value, sink->maxfilesize);
220       break;
221     default:
222       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
223       break;
224   }
225 }
226
227 static gboolean
228 gst_filesink_open_file (GstFileSink *sink)
229 {
230   g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE);
231
232   /* open the file */
233   if (!gst_filesink_getcurrentfilename(sink))
234   {
235     /* Out of files */
236     return FALSE;
237   }
238   sink->file = fopen (gst_filesink_getcurrentfilename(sink), "w");
239   if (sink->file == NULL) {
240     perror ("open");
241     gst_element_error (GST_ELEMENT (sink), g_strconcat("Error opening file \"",
242       gst_filesink_getcurrentfilename(sink), "\": ", g_strerror(errno), NULL));
243     return FALSE;
244   } 
245
246   GST_FLAG_SET (sink, GST_FILESINK_OPEN);
247
248   sink->data_written = 0;
249
250   return TRUE;
251 }
252
253 static void
254 gst_filesink_close_file (GstFileSink *sink)
255 {
256   g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));
257
258   if (fclose (sink->file) != 0)
259   {
260     perror ("close");
261     gst_element_error (GST_ELEMENT (sink), g_strconcat("Error closing file \"",
262       gst_filesink_getcurrentfilename(sink), "\": ", g_strerror(errno), NULL));
263   }
264   else {
265     GST_FLAG_UNSET (sink, GST_FILESINK_OPEN);
266   }
267 }
268
269 /* handle events (search) */
270 static gboolean
271 gst_filesink_handle_event (GstPad *pad, GstEvent *event)
272 {
273   GstEventType type;
274   GstFileSink *filesink;
275
276   filesink = GST_FILESINK (gst_pad_get_parent (pad));
277
278   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
279
280   switch (type) {
281     case GST_EVENT_SEEK:
282       /* we need to seek */
283       if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH)
284         if (fflush(filesink->file))
285           gst_element_error(GST_ELEMENT(filesink),
286             "Error flushing the buffer cache of file \'%s\' to disk: %s",
287             gst_filesink_getcurrentfilename(filesink), g_strerror(errno));
288
289       if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) {
290         g_warning("Any other then byte-offset seeking is not supported!\n");
291       }
292
293       switch (GST_EVENT_SEEK_METHOD(event))
294       {
295         case GST_SEEK_METHOD_SET:
296           fseek(filesink->file, GST_EVENT_SEEK_OFFSET(event), SEEK_SET);
297           break;
298         case GST_SEEK_METHOD_CUR:
299           fseek(filesink->file, GST_EVENT_SEEK_OFFSET(event), SEEK_CUR);
300           break;
301         case GST_SEEK_METHOD_END:
302           fseek(filesink->file, GST_EVENT_SEEK_OFFSET(event), SEEK_END);
303           break;
304         default:
305           g_warning("unkown seek method!\n");
306           break;
307       }
308       break;
309     case GST_EVENT_DISCONTINUOUS:
310     {
311       gint64 offset;
312       
313       if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset))
314         fseek(filesink->file, offset, SEEK_SET);
315
316       gst_event_unref (event);
317       break;
318     }
319     case GST_EVENT_NEW_MEDIA:
320       /* we need to open a new file! */
321       gst_filesink_close_file(filesink);
322       filesink->filenum++;
323       if (!gst_filesink_open_file(filesink)) {
324         /* no more files, give EOS */
325         gst_element_set_eos(GST_ELEMENT(filesink));
326         return FALSE;
327       }
328       break;
329     case GST_EVENT_FLUSH:
330       if (fflush(filesink->file))
331         gst_element_error(GST_ELEMENT(filesink),
332           "Error flushing the buffer cache of file \'%s\' to disk: %s",
333           gst_filesink_getcurrentfilename(filesink), g_strerror(errno));
334       break;
335     default:
336       gst_pad_event_default (pad, event);
337       break;
338   }
339
340   return TRUE;
341 }
342
343 /**
344  * gst_filesink_chain:
345  * @pad: the pad this filesink is connected to
346  * @buf: the buffer that has to be absorbed
347  *
348  * take the buffer from the pad and write to file if it's open
349  */
350 static void 
351 gst_filesink_chain (GstPad *pad, GstBuffer *buf) 
352 {
353   GstFileSink *filesink;
354   gint bytes_written = 0;
355
356   g_return_if_fail (pad != NULL);
357   g_return_if_fail (GST_IS_PAD (pad));
358   g_return_if_fail (buf != NULL);
359
360   filesink = GST_FILESINK (gst_pad_get_parent (pad));
361
362   if (GST_IS_EVENT(buf))
363   {
364     gst_filesink_handle_event(pad, GST_EVENT(buf));
365     return;
366   }
367
368   if (filesink->maxfilesize > 0)
369   {
370     if ((filesink->data_written + GST_BUFFER_SIZE(buf))/(1024*1024) > filesink->maxfilesize)
371     {
372       if (GST_ELEMENT_IS_EVENT_AWARE(GST_ELEMENT(filesink)))
373       {
374         GstEvent *event;
375         event = gst_event_new(GST_EVENT_NEW_MEDIA);
376         gst_pad_send_event(pad, event);
377       }
378     }
379   }
380
381   if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN))
382   {
383     bytes_written = fwrite (GST_BUFFER_DATA (buf), 1, GST_BUFFER_SIZE (buf), filesink->file);
384     if (bytes_written < GST_BUFFER_SIZE (buf))
385     {
386       printf ("filesink : Warning : %d bytes should be written, only %d bytes written\n",
387                   GST_BUFFER_SIZE (buf), bytes_written);
388     }
389   }
390   filesink->data_written += GST_BUFFER_SIZE(buf);
391
392   gst_buffer_unref (buf);
393
394   g_signal_emit (G_OBJECT (filesink), gst_filesink_signals[SIGNAL_HANDOFF], 0,
395                               filesink);
396 }
397
398 static GstElementStateReturn
399 gst_filesink_change_state (GstElement *element)
400 {
401   g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE);
402
403   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
404     if (GST_FLAG_IS_SET (element, GST_FILESINK_OPEN))
405       gst_filesink_close_file (GST_FILESINK (element));
406   } else {
407     if (!GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) {
408       if (!gst_filesink_open_file (GST_FILESINK (element)))
409         return GST_STATE_FAILURE;
410     }
411   }
412
413   if (GST_ELEMENT_CLASS (parent_class)->change_state)
414     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
415
416   return GST_STATE_SUCCESS;
417 }
418