don't mix tabs and spaces
[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-i18n-lib.h"
29
30 #include <gst/gst.h>
31 #include <errno.h>
32 #include "gstfilesink.h"
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37
38
39 GST_DEBUG_CATEGORY_STATIC (gst_filesink_debug);
40 #define GST_CAT_DEFAULT gst_filesink_debug
41
42 GstElementDetails gst_filesink_details = GST_ELEMENT_DETAILS ("File Sink",
43     "Sink/File",
44     "Write stream to a file",
45     "Thomas <thomas@apestaart.org>");
46
47
48 /* FileSink signals and args */
49 enum
50 {
51   /* FILL ME */
52   SIGNAL_HANDOFF,
53   LAST_SIGNAL
54 };
55
56 enum
57 {
58   ARG_0,
59   ARG_LOCATION
60 };
61
62 static const GstFormat *
63 gst_filesink_get_formats (GstPad * pad)
64 {
65   static const GstFormat formats[] = {
66     GST_FORMAT_BYTES,
67     0,
68   };
69
70   return formats;
71 }
72
73 static const GstQueryType *
74 gst_filesink_get_query_types (GstPad * pad)
75 {
76   static const GstQueryType types[] = {
77     GST_QUERY_TOTAL,
78     GST_QUERY_POSITION,
79     0
80   };
81
82   return types;
83 }
84
85 static void gst_filesink_dispose (GObject * object);
86
87 static void gst_filesink_set_property (GObject * object, guint prop_id,
88     const GValue * value, GParamSpec * pspec);
89 static void gst_filesink_get_property (GObject * object, guint prop_id,
90     GValue * value, GParamSpec * pspec);
91
92 static gboolean gst_filesink_open_file (GstFileSink * sink);
93 static void gst_filesink_close_file (GstFileSink * sink);
94
95 static gboolean gst_filesink_handle_event (GstPad * pad, GstEvent * event);
96 static gboolean gst_filesink_pad_query (GstPad * pad, GstQueryType type,
97     GstFormat * format, gint64 * value);
98 static void gst_filesink_chain (GstPad * pad, GstData * _data);
99
100 static void gst_filesink_uri_handler_init (gpointer g_iface,
101     gpointer iface_data);
102
103 static GstElementStateReturn gst_filesink_change_state (GstElement * element);
104
105 static guint gst_filesink_signals[LAST_SIGNAL] = { 0 };
106
107 static void
108 _do_init (GType filesink_type)
109 {
110   static const GInterfaceInfo urihandler_info = {
111     gst_filesink_uri_handler_init,
112     NULL,
113     NULL
114   };
115
116   g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
117       &urihandler_info);
118   GST_DEBUG_CATEGORY_INIT (gst_filesink_debug, "filesink", 0,
119       "filesink element");
120 }
121
122 GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstElement, GST_TYPE_ELEMENT,
123     _do_init);
124
125
126 static void
127 gst_filesink_base_init (gpointer g_class)
128 {
129   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
130
131   gstelement_class->change_state = gst_filesink_change_state;
132   gst_element_class_set_details (gstelement_class, &gst_filesink_details);
133 }
134 static void
135 gst_filesink_class_init (GstFileSinkClass * klass)
136 {
137   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
138
139
140   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
141       g_param_spec_string ("location", "File Location",
142           "Location of the file to write", NULL, G_PARAM_READWRITE));
143
144   gst_filesink_signals[SIGNAL_HANDOFF] =
145       g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
146       G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL,
147       g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
148
149   gobject_class->set_property = gst_filesink_set_property;
150   gobject_class->get_property = gst_filesink_get_property;
151   gobject_class->dispose = gst_filesink_dispose;
152 }
153 static void
154 gst_filesink_init (GstFileSink * filesink)
155 {
156   GstPad *pad;
157
158   pad = gst_pad_new ("sink", GST_PAD_SINK);
159   gst_element_add_pad (GST_ELEMENT (filesink), pad);
160   gst_pad_set_chain_function (pad, gst_filesink_chain);
161
162   GST_FLAG_SET (GST_ELEMENT (filesink), GST_ELEMENT_EVENT_AWARE);
163
164   gst_pad_set_query_function (pad, gst_filesink_pad_query);
165   gst_pad_set_query_type_function (pad, gst_filesink_get_query_types);
166   gst_pad_set_formats_function (pad, gst_filesink_get_formats);
167
168   filesink->filename = NULL;
169   filesink->file = NULL;
170 }
171 static void
172 gst_filesink_dispose (GObject * object)
173 {
174   GstFileSink *sink = GST_FILESINK (object);
175
176   G_OBJECT_CLASS (parent_class)->dispose (object);
177
178   g_free (sink->uri);
179   sink->uri = NULL;
180   g_free (sink->filename);
181   sink->filename = NULL;
182 }
183
184 static gboolean
185 gst_filesink_set_location (GstFileSink * sink, const gchar * location)
186 {
187   /* the element must be stopped or paused in order to do this */
188   if (GST_STATE (sink) > GST_STATE_PAUSED)
189     return FALSE;
190   if (GST_STATE (sink) == GST_STATE_PAUSED &&
191       GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN))
192     return FALSE;
193
194   g_free (sink->filename);
195   g_free (sink->uri);
196   if (location != NULL) {
197     sink->filename = g_strdup (location);
198     sink->uri = gst_uri_construct ("file", location);
199   } else {
200     sink->filename = NULL;
201     sink->uri = NULL;
202   }
203
204   if (GST_STATE (sink) == GST_STATE_PAUSED)
205     gst_filesink_open_file (sink);
206
207   return TRUE;
208 }
209 static void
210 gst_filesink_set_property (GObject * object, guint prop_id,
211     const GValue * value, GParamSpec * pspec)
212 {
213   GstFileSink *sink;
214
215   /* it's not null if we got it, but it might not be ours */
216   sink = GST_FILESINK (object);
217
218   switch (prop_id) {
219     case ARG_LOCATION:
220       gst_filesink_set_location (sink, g_value_get_string (value));
221       break;
222     default:
223       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224       break;
225   }
226 }
227
228 static void
229 gst_filesink_get_property (GObject * object, guint prop_id, GValue * value,
230     GParamSpec * pspec)
231 {
232   GstFileSink *sink;
233
234   /* it's not null if we got it, but it might not be ours */
235   g_return_if_fail (GST_IS_FILESINK (object));
236
237   sink = GST_FILESINK (object);
238
239   switch (prop_id) {
240     case ARG_LOCATION:
241       g_value_set_string (value, sink->filename);
242       break;
243     default:
244       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
245       break;
246   }
247 }
248
249 static gboolean
250 gst_filesink_open_file (GstFileSink * sink)
251 {
252   g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE);
253
254   /* open the file */
255   if (sink->filename == NULL || sink->filename[0] == '\0') {
256     GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
257         (_("No file name specified for writing.")), (NULL));
258     return FALSE;
259   }
260
261   sink->file = fopen (sink->filename, "w");
262   if (sink->file == NULL) {
263     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
264         (_("Could not open file \"%s\" for writing."), sink->filename),
265         GST_ERROR_SYSTEM);
266     return FALSE;
267   }
268
269   GST_FLAG_SET (sink, GST_FILESINK_OPEN);
270
271   sink->data_written = 0;
272
273   return TRUE;
274 }
275
276 static void
277 gst_filesink_close_file (GstFileSink * sink)
278 {
279   g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN));
280
281   if (fclose (sink->file) != 0) {
282     GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
283         (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
284   } else {
285     GST_FLAG_UNSET (sink, GST_FILESINK_OPEN);
286   }
287 }
288
289 static gboolean
290 gst_filesink_pad_query (GstPad * pad, GstQueryType type,
291     GstFormat * format, gint64 * value)
292 {
293   GstFileSink *sink = GST_FILESINK (GST_PAD_PARENT (pad));
294
295   switch (type) {
296     case GST_QUERY_TOTAL:
297       switch (*format) {
298         case GST_FORMAT_BYTES:
299           if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) {
300             *value = sink->data_written;        /* FIXME - doesn't the kernel provide
301                                                    such a function? */
302             break;
303           }
304         default:
305           return FALSE;
306       }
307       break;
308     case GST_QUERY_POSITION:
309       switch (*format) {
310         case GST_FORMAT_BYTES:
311           if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) {
312             *value = ftell (sink->file);
313             break;
314           }
315         default:
316           return FALSE;
317       }
318       break;
319     default:
320       return FALSE;
321   }
322
323   return TRUE;
324 }
325
326 /* handle events (search) */
327 static gboolean
328 gst_filesink_handle_event (GstPad * pad, GstEvent * event)
329 {
330   GstEventType type;
331   GstFileSink *filesink;
332
333   filesink = GST_FILESINK (gst_pad_get_parent (pad));
334
335   g_return_val_if_fail (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN), FALSE);
336
337   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
338
339   switch (type) {
340     case GST_EVENT_SEEK:
341       g_return_val_if_fail (GST_EVENT_SEEK_FORMAT (event) == GST_FORMAT_BYTES,
342           FALSE);
343
344       if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH)
345         if (fflush (filesink->file))
346           GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
347               (_("Error while writing to file \"%s\"."), filesink->filename),
348               GST_ERROR_SYSTEM);
349
350       switch (GST_EVENT_SEEK_METHOD (event)) {
351         case GST_SEEK_METHOD_SET:
352           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_SET);
353           break;
354         case GST_SEEK_METHOD_CUR:
355           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_CUR);
356           break;
357         case GST_SEEK_METHOD_END:
358           fseek (filesink->file, GST_EVENT_SEEK_OFFSET (event), SEEK_END);
359           break;
360         default:
361           g_warning ("unknown seek method!");
362           break;
363       }
364       break;
365     case GST_EVENT_DISCONTINUOUS:
366     {
367       gint64 offset;
368
369       if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset))
370         fseek (filesink->file, offset, SEEK_SET);
371
372       gst_event_unref (event);
373       break;
374     }
375     case GST_EVENT_FLUSH:
376       if (fflush (filesink->file)) {
377         GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
378             (_("Error while writing to file \"%s\"."), filesink->filename),
379             GST_ERROR_SYSTEM);
380       }
381       break;
382     case GST_EVENT_EOS:
383       gst_filesink_close_file (filesink);
384       gst_element_set_eos (GST_ELEMENT (filesink));
385       break;
386     default:
387       gst_pad_event_default (pad, event);
388       break;
389   }
390
391   return TRUE;
392 }
393
394 /**
395  * gst_filesink_chain:
396  * @pad: the pad this filesink is connected to
397  * @buf: the buffer that has to be absorbed
398  *
399  * take the buffer from the pad and write to file if it's open
400  */
401 static void
402 gst_filesink_chain (GstPad * pad, GstData * _data)
403 {
404   GstBuffer *buf = GST_BUFFER (_data);
405   GstFileSink *filesink;
406
407   g_return_if_fail (pad != NULL);
408   g_return_if_fail (GST_IS_PAD (pad));
409   g_return_if_fail (buf != NULL);
410
411   filesink = GST_FILESINK (gst_pad_get_parent (pad));
412
413   if (GST_IS_EVENT (buf)) {
414     gst_filesink_handle_event (pad, GST_EVENT (buf));
415     return;
416   }
417
418   if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN)) {
419     guint bytes_written = 0, back_pending = 0;
420
421     if (ftell (filesink->file) < filesink->data_written)
422       back_pending = filesink->data_written - ftell (filesink->file);
423     while (bytes_written < GST_BUFFER_SIZE (buf)) {
424       size_t wrote = fwrite (GST_BUFFER_DATA (buf) + bytes_written, 1,
425           GST_BUFFER_SIZE (buf) - bytes_written,
426           filesink->file);
427
428       if (wrote <= 0) {
429         GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
430             (_("Error while writing to file \"%s\"."), filesink->filename),
431             ("Only %d of %d bytes written: %s",
432                 bytes_written, GST_BUFFER_SIZE (buf), strerror (errno)));
433         break;
434       }
435       bytes_written += wrote;
436     }
437
438     filesink->data_written += bytes_written - back_pending;
439   }
440
441   gst_buffer_unref (buf);
442
443   g_signal_emit (G_OBJECT (filesink),
444       gst_filesink_signals[SIGNAL_HANDOFF], 0, filesink);
445 }
446
447 static GstElementStateReturn
448 gst_filesink_change_state (GstElement * element)
449 {
450   g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE);
451
452   switch (GST_STATE_TRANSITION (element)) {
453     case GST_STATE_PAUSED_TO_READY:
454       if (GST_FLAG_IS_SET (element, GST_FILESINK_OPEN))
455         gst_filesink_close_file (GST_FILESINK (element));
456       break;
457
458     case GST_STATE_READY_TO_PAUSED:
459       if (!GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) {
460         if (!gst_filesink_open_file (GST_FILESINK (element)))
461           return GST_STATE_FAILURE;
462       }
463       break;
464   }
465
466   if (GST_ELEMENT_CLASS (parent_class)->change_state)
467     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
468
469   return GST_STATE_SUCCESS;
470 }
471
472 /*** GSTURIHANDLER INTERFACE *************************************************/
473
474 static guint
475 gst_filesink_uri_get_type (void)
476 {
477   return GST_URI_SINK;
478 }
479 static gchar **
480 gst_filesink_uri_get_protocols (void)
481 {
482   static gchar *protocols[] = { "file", NULL };
483
484   return protocols;
485 }
486 static const gchar *
487 gst_filesink_uri_get_uri (GstURIHandler * handler)
488 {
489   GstFileSink *sink = GST_FILESINK (handler);
490
491   return sink->uri;
492 }
493
494 static gboolean
495 gst_filesink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
496 {
497   gchar *protocol, *location;
498   gboolean ret;
499   GstFileSink *sink = GST_FILESINK (handler);
500
501   protocol = gst_uri_get_protocol (uri);
502   if (strcmp (protocol, "file") != 0) {
503     g_free (protocol);
504     return FALSE;
505   }
506   g_free (protocol);
507   location = gst_uri_get_location (uri);
508   ret = gst_filesink_set_location (sink, location);
509   g_free (location);
510
511   return ret;
512 }
513
514 static void
515 gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data)
516 {
517   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
518
519   iface->get_type = gst_filesink_uri_get_type;
520   iface->get_protocols = gst_filesink_uri_get_protocols;
521   iface->get_uri = gst_filesink_uri_get_uri;
522   iface->set_uri = gst_filesink_uri_set_uri;
523 }