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