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