update plugin initialization restructuring (see email for details
[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 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include <gst/gst.h>
29 #include <errno.h>
30 #include "gstfilesink.h"
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35
36 GST_DEBUG_CATEGORY_STATIC (gst_filesink_debug);
37 #define GST_CAT_DEFAULT gst_filesink_debug
38
39 GstElementDetails gst_filesink_details = GST_ELEMENT_DETAILS (
40   "File Sink",
41   "Sink/File",
42   "Write stream to a file",
43   "Thomas <thomas@apestaart.org>"
44 );
45
46
47 /* FileSink signals and args */
48 enum {
49   /* FILL ME */
50   SIGNAL_HANDOFF,
51   LAST_SIGNAL
52 };
53
54 enum {
55   ARG_0,
56   ARG_LOCATION
57 };
58
59 GST_PAD_QUERY_TYPE_FUNCTION (gst_filesink_get_query_types,
60   GST_QUERY_TOTAL,
61   GST_QUERY_POSITION
62 )
63
64 GST_PAD_FORMATS_FUNCTION (gst_filesink_get_formats,
65   GST_FORMAT_BYTES
66 )
67
68
69 static void     gst_filesink_base_init          (gpointer g_class);
70 static void     gst_filesink_class_init         (GstFileSinkClass *klass);
71 static void     gst_filesink_init               (GstFileSink *filesink);
72
73 static void     gst_filesink_set_property       (GObject *object, guint prop_id, 
74                                                  const GValue *value, GParamSpec *pspec);
75 static void     gst_filesink_get_property       (GObject *object, guint prop_id, 
76                                                  GValue *value, GParamSpec *pspec);
77
78 static gboolean gst_filesink_open_file          (GstFileSink *sink);
79 static void     gst_filesink_close_file         (GstFileSink *sink);
80
81 static gboolean gst_filesink_handle_event       (GstPad *pad, GstEvent *event);
82 static gboolean gst_filesink_pad_query          (GstPad *pad, GstQueryType type,
83                                                  GstFormat *format, gint64 *value);
84 static void     gst_filesink_chain              (GstPad *pad,GstData *_data);
85
86 static GstElementStateReturn gst_filesink_change_state (GstElement *element);
87
88 static GstElementClass *parent_class = NULL;
89 static guint gst_filesink_signals[LAST_SIGNAL] = { 0 };
90
91 GType
92 gst_filesink_get_type (void) 
93 {
94   static GType filesink_type = 0;
95
96   if (!filesink_type) {
97     static const GTypeInfo filesink_info = {
98       sizeof(GstFileSinkClass),
99       gst_filesink_base_init,
100       NULL,
101       (GClassInitFunc)gst_filesink_class_init,
102       NULL,
103       NULL,
104       sizeof(GstFileSink),
105       0,
106       (GInstanceInitFunc)gst_filesink_init,
107     };
108     filesink_type = g_type_register_static (GST_TYPE_ELEMENT, "GstFileSink", &filesink_info, 0);
109   
110     GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink", 0, "filesink element");
111   }
112   return filesink_type;
113 }
114
115 static void
116 gst_filesink_base_init (gpointer g_class)
117 {
118   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
119
120   gstelement_class->change_state = gst_filesink_change_state;
121   gst_element_class_set_details (gstelement_class, &gst_filesink_details);
122 }
123 static void
124 gst_filesink_class_init (GstFileSinkClass *klass) 
125 {
126   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
127
128   parent_class = g_type_class_peek_parent (klass);
129
130   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
131     g_param_spec_string ("location", "File Location", "Location of the file to write",
132                          NULL, G_PARAM_READWRITE));
133
134   gst_filesink_signals[SIGNAL_HANDOFF] =
135     g_signal_new ("handoff", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
136                     G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL,
137                     g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
138
139   gobject_class->set_property = gst_filesink_set_property;
140   gobject_class->get_property = gst_filesink_get_property;
141 }
142
143 static void 
144 gst_filesink_init (GstFileSink *filesink) 
145 {
146   GstPad *pad;
147
148   pad = gst_pad_new ("sink", GST_PAD_SINK);
149   gst_element_add_pad (GST_ELEMENT (filesink), pad);
150   gst_pad_set_chain_function (pad, gst_filesink_chain);
151
152   GST_FLAG_SET (GST_ELEMENT(filesink), GST_ELEMENT_EVENT_AWARE);
153
154   gst_pad_set_query_function (pad, gst_filesink_pad_query);
155   gst_pad_set_query_type_function (pad, gst_filesink_get_query_types);
156   gst_pad_set_formats_function (pad, gst_filesink_get_formats);
157
158   filesink->filename = NULL;
159   filesink->file = NULL;
160 }
161
162 static void
163 gst_filesink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
164 {
165   GstFileSink *sink;
166
167   /* it's not null if we got it, but it might not be ours */
168   sink = GST_FILESINK (object);
169
170   switch (prop_id) {
171     case ARG_LOCATION:
172       /* the element must be stopped or paused in order to do this */
173       g_return_if_fail (GST_STATE (sink) <= GST_STATE_PAUSED);
174       if (GST_STATE (sink) == GST_STATE_PAUSED)
175         g_return_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));
176
177       if (sink->filename)
178         g_free (sink->filename);
179       sink->filename = g_strdup (g_value_get_string (value));
180       if (GST_STATE (sink) == GST_STATE_PAUSED)
181         gst_filesink_open_file (sink);   
182       break;
183     default:
184       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185       break;
186   }
187 }
188
189 static void   
190 gst_filesink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
191 {
192   GstFileSink *sink;
193  
194   /* it's not null if we got it, but it might not be ours */
195   g_return_if_fail (GST_IS_FILESINK (object));
196  
197   sink = GST_FILESINK (object);
198   
199   switch (prop_id) {
200     case ARG_LOCATION:
201       g_value_set_string (value, sink->filename);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205       break;
206   }
207 }
208
209 static gboolean
210 gst_filesink_open_file (GstFileSink *sink)
211 {
212   g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE);
213
214   /* open the file */
215   if (!sink->filename)
216   {
217     gst_element_error (GST_ELEMENT (sink),
218                        "Error opening file: no file given");
219     return FALSE;
220   }
221
222   sink->file = fopen (sink->filename, "w");
223   if (sink->file == NULL) {
224     gst_element_error (GST_ELEMENT (sink),
225                        "Error opening file %s: %s",
226                        sink->filename, g_strerror(errno));
227     return FALSE;
228   } 
229
230   GST_FLAG_SET (sink, GST_FILESINK_OPEN);
231
232   sink->data_written = 0;
233
234   return TRUE;
235 }
236
237 static void
238 gst_filesink_close_file (GstFileSink *sink)
239 {
240   g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));
241
242   if (fclose (sink->file) != 0)
243   {
244     gst_element_error (GST_ELEMENT (sink),
245                        "Error closing file %s: %s",
246                        sink->filename, g_strerror(errno));
247   }
248   else {
249     GST_FLAG_UNSET (sink, GST_FILESINK_OPEN);
250   }
251 }
252
253 static gboolean
254 gst_filesink_pad_query (GstPad *pad, GstQueryType type,
255                         GstFormat *format, gint64 *value)
256 {
257   GstFileSink *sink = GST_FILESINK (GST_PAD_PARENT (pad));
258
259   switch (type) {
260     case GST_QUERY_TOTAL:
261       switch (*format) {
262         case GST_FORMAT_BYTES:
263           if (GST_FLAG_IS_SET (GST_ELEMENT(sink), GST_FILESINK_OPEN)) {
264             *value = sink->data_written; /* FIXME - doesn't the kernel provide
265                                             such a function? */
266             break;
267           }
268         default:
269           return FALSE;
270       }
271       break;
272     case GST_QUERY_POSITION:
273       switch (*format) {
274         case GST_FORMAT_BYTES:
275           if (GST_FLAG_IS_SET (GST_ELEMENT(sink), GST_FILESINK_OPEN)) {
276             *value = ftell (sink->file);
277             break;
278           }
279         default:
280           return FALSE;
281       }
282       break;
283     default:
284       return FALSE;
285   }
286
287   return TRUE;
288 }
289
290 /* handle events (search) */
291 static gboolean
292 gst_filesink_handle_event (GstPad *pad, GstEvent *event)
293 {
294   GstEventType type;
295   GstFileSink *filesink;
296
297   filesink = GST_FILESINK (gst_pad_get_parent (pad));
298
299   g_return_val_if_fail (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN),
300                         FALSE);
301
302   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
303
304   switch (type) {
305     case GST_EVENT_SEEK:
306       g_return_val_if_fail (GST_EVENT_SEEK_FORMAT (event) == GST_FORMAT_BYTES,
307                             FALSE);
308
309       if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH)
310         if (fflush (filesink->file))
311           gst_element_error (GST_ELEMENT (filesink),
312                              "Error flushing file %s: %s",
313                              filesink->filename, g_strerror(errno));
314
315       switch (GST_EVENT_SEEK_METHOD(event))
316       {
317         case GST_SEEK_METHOD_SET:
318           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_SET);
319           break;
320         case GST_SEEK_METHOD_CUR:
321           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_CUR);
322           break;
323         case GST_SEEK_METHOD_END:
324           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_END);
325           break;
326         default:
327           g_warning ("unknown seek method!");
328           break;
329       }
330       break;
331     case GST_EVENT_DISCONTINUOUS:
332     {
333       gint64 offset;
334       
335       if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset))
336         fseek (filesink->file, offset, SEEK_SET);
337
338       gst_event_unref (event);
339       break;
340     }
341     case GST_EVENT_FLUSH:
342       if (fflush (filesink->file)) {
343         gst_element_error (GST_ELEMENT (filesink),
344                            "Error flushing file %s: %s",
345                            filesink->filename, g_strerror(errno));
346       }
347       break;
348     case GST_EVENT_EOS:
349       gst_filesink_close_file (filesink);
350       gst_element_set_eos (GST_ELEMENT (filesink));
351       break;
352     default:
353       gst_pad_event_default (pad, event);
354       break;
355   }
356
357   return TRUE;
358 }
359
360 /**
361  * gst_filesink_chain:
362  * @pad: the pad this filesink is connected to
363  * @buf: the buffer that has to be absorbed
364  *
365  * take the buffer from the pad and write to file if it's open
366  */
367 static void 
368 gst_filesink_chain (GstPad *pad, GstData *_data) 
369 {
370   GstBuffer *buf = GST_BUFFER (_data);
371   GstFileSink *filesink;
372
373   g_return_if_fail (pad != NULL);
374   g_return_if_fail (GST_IS_PAD (pad));
375   g_return_if_fail (buf != NULL);
376
377   filesink = GST_FILESINK (gst_pad_get_parent (pad));
378
379   if (GST_IS_EVENT(buf))
380   {
381     gst_filesink_handle_event(pad, GST_EVENT(buf));
382     return;
383   }
384
385   if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN))
386   {
387     guint bytes_written = 0, back_pending = 0;
388     if (ftell(filesink->file) < filesink->data_written)
389       back_pending = filesink->data_written - ftell(filesink->file);
390     while (bytes_written < GST_BUFFER_SIZE (buf)) {
391       size_t wrote = fwrite (GST_BUFFER_DATA (buf) + bytes_written, 1,
392                              GST_BUFFER_SIZE (buf) - bytes_written,
393                              filesink->file);
394       if (wrote <= 0) {
395         gst_element_error (GST_ELEMENT (filesink),
396                            "Only %d of %d bytes written: %s",
397                            bytes_written, GST_BUFFER_SIZE (buf),
398                            strerror (errno));
399         break;
400       }
401       bytes_written += wrote;
402     }
403
404     filesink->data_written += bytes_written - back_pending;
405   }
406
407   gst_buffer_unref (buf);
408
409   g_signal_emit (G_OBJECT (filesink),
410                  gst_filesink_signals[SIGNAL_HANDOFF], 0,
411                  filesink);
412 }
413
414 static GstElementStateReturn
415 gst_filesink_change_state (GstElement *element)
416 {
417   g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE);
418
419   switch (GST_STATE_TRANSITION (element)) {
420     case GST_STATE_PAUSED_TO_READY:
421       if (GST_FLAG_IS_SET (element, GST_FILESINK_OPEN))
422         gst_filesink_close_file (GST_FILESINK (element));
423       break;
424
425     case GST_STATE_READY_TO_PAUSED:
426       if (!GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) {
427         if (!gst_filesink_open_file (GST_FILESINK (element)))
428           return GST_STATE_FAILURE;
429       }
430       break;
431   }
432
433   if (GST_ELEMENT_CLASS (parent_class)->change_state)
434     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
435
436   return GST_STATE_SUCCESS;
437 }