gst/elements/gstfilesink.c: Don't use NULL pointers.
[platform/upstream/gstreamer.git] / plugins / 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  * SECTION:gstfilesink
24  * @short_description: write stream to a file
25  * @see_also: #GstFileSrc
26  *
27  * Wrtite incomming data to a file in the local file system.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #  include "config.h"
32 #endif
33
34 #include "../gst-i18n-lib.h"
35
36 #include <gst/gst.h>
37 #include <errno.h>
38 #include "gstfilesink.h"
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46
47 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
48     GST_PAD_SINK,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS_ANY);
51
52 GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
53 #define GST_CAT_DEFAULT gst_file_sink_debug
54
55 static GstElementDetails gst_file_sink_details =
56 GST_ELEMENT_DETAILS ("File Sink",
57     "Sink/File",
58     "Write stream to a file",
59     "Thomas <thomas@apestaart.org>");
60
61
62 /* FileSink signals and args */
63 enum
64 {
65   /* FILL ME */
66   SIGNAL_HANDOFF,
67   LAST_SIGNAL
68 };
69
70 enum
71 {
72   ARG_0,
73   ARG_LOCATION
74 };
75
76 static void gst_file_sink_dispose (GObject * object);
77
78 static void gst_file_sink_set_property (GObject * object, guint prop_id,
79     const GValue * value, GParamSpec * pspec);
80 static void gst_file_sink_get_property (GObject * object, guint prop_id,
81     GValue * value, GParamSpec * pspec);
82
83 static gboolean gst_file_sink_open_file (GstFileSink * sink);
84 static void gst_file_sink_close_file (GstFileSink * sink);
85
86 static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
87 static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
88     GstBuffer * buffer);
89
90 static gboolean gst_file_sink_query (GstPad * pad, GstQuery * query);
91
92 static void gst_file_sink_uri_handler_init (gpointer g_iface,
93     gpointer iface_data);
94
95 static GstStateChangeReturn gst_file_sink_change_state (GstElement * element,
96     GstStateChange transition);
97
98 //static guint gst_file_sink_signals[LAST_SIGNAL] = { 0 };
99
100 static void
101 _do_init (GType filesink_type)
102 {
103   static const GInterfaceInfo urihandler_info = {
104     gst_file_sink_uri_handler_init,
105     NULL,
106     NULL
107   };
108
109   g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
110       &urihandler_info);
111   GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0,
112       "filesink element");
113 }
114
115 GST_BOILERPLATE_FULL (GstFileSink, gst_file_sink, GstBaseSink,
116     GST_TYPE_BASE_SINK, _do_init);
117
118 static void
119 gst_file_sink_base_init (gpointer g_class)
120 {
121   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
122
123   gstelement_class->change_state = gst_file_sink_change_state;
124   gst_element_class_add_pad_template (gstelement_class,
125       gst_static_pad_template_get (&sinktemplate));
126   gst_element_class_set_details (gstelement_class, &gst_file_sink_details);
127 }
128
129 static void
130 gst_file_sink_class_init (GstFileSinkClass * klass)
131 {
132   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
133   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
134
135   gobject_class->set_property = gst_file_sink_set_property;
136   gobject_class->get_property = gst_file_sink_get_property;
137
138   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
139       g_param_spec_string ("location", "File Location",
140           "Location of the file to write", NULL, G_PARAM_READWRITE));
141
142   gobject_class->dispose = gst_file_sink_dispose;
143
144   gstbasesink_class->get_times = NULL;
145   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
146   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
147
148   if (sizeof (off_t) < 8) {
149     GST_LOG ("No large file support, sizeof (off_t) = %u", sizeof (off_t));
150   }
151 }
152
153 static void
154 gst_file_sink_init (GstFileSink * filesink, GstFileSinkClass * g_class)
155 {
156   GstPad *pad;
157
158   pad = GST_BASE_SINK_PAD (filesink);
159
160   gst_pad_set_query_function (pad, gst_file_sink_query);
161
162   filesink->filename = NULL;
163   filesink->file = NULL;
164
165   GST_BASE_SINK (filesink)->sync = FALSE;
166 }
167
168 static void
169 gst_file_sink_dispose (GObject * object)
170 {
171   GstFileSink *sink = GST_FILE_SINK (object);
172
173   G_OBJECT_CLASS (parent_class)->dispose (object);
174
175   g_free (sink->uri);
176   sink->uri = NULL;
177   g_free (sink->filename);
178   sink->filename = NULL;
179 }
180
181 static gboolean
182 gst_file_sink_set_location (GstFileSink * sink, const gchar * location)
183 {
184   /* the element must be stopped or paused in order to do this */
185   if (GST_STATE (sink) >= GST_STATE_PAUSED)
186     return FALSE;
187
188   g_free (sink->filename);
189   g_free (sink->uri);
190   if (location != NULL) {
191     sink->filename = g_strdup (location);
192     sink->uri = gst_uri_construct ("file", location);
193   } else {
194     sink->filename = NULL;
195     sink->uri = NULL;
196   }
197
198   return TRUE;
199 }
200 static void
201 gst_file_sink_set_property (GObject * object, guint prop_id,
202     const GValue * value, GParamSpec * pspec)
203 {
204   GstFileSink *sink;
205
206   sink = GST_FILE_SINK (object);
207
208   switch (prop_id) {
209     case ARG_LOCATION:
210       gst_file_sink_set_location (sink, g_value_get_string (value));
211       break;
212     default:
213       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214       break;
215   }
216 }
217
218 static void
219 gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
220     GParamSpec * pspec)
221 {
222   GstFileSink *sink;
223
224   g_return_if_fail (GST_IS_FILE_SINK (object));
225
226   sink = GST_FILE_SINK (object);
227
228   switch (prop_id) {
229     case ARG_LOCATION:
230       g_value_set_string (value, sink->filename);
231       break;
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234       break;
235   }
236 }
237
238 static gboolean
239 gst_file_sink_open_file (GstFileSink * sink)
240 {
241   /* open the file */
242   if (sink->filename == NULL || sink->filename[0] == '\0') {
243     GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
244         (_("No file name specified for writing.")), (NULL));
245     return FALSE;
246   }
247
248   sink->file = fopen (sink->filename, "wb");
249   if (sink->file == NULL) {
250     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
251         (_("Could not open file \"%s\" for writing."), sink->filename),
252         GST_ERROR_SYSTEM);
253     return FALSE;
254   }
255
256   sink->data_written = 0;
257
258   return TRUE;
259 }
260
261 static void
262 gst_file_sink_close_file (GstFileSink * sink)
263 {
264   if (sink->file) {
265     if (fclose (sink->file) != 0) {
266       GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
267           (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
268     }
269   }
270 }
271
272 static gboolean
273 gst_file_sink_query (GstPad * pad, GstQuery * query)
274 {
275   GstFileSink *self;
276   GstFormat format;
277
278   self = GST_FILE_SINK (GST_PAD_PARENT (pad));
279
280   switch (GST_QUERY_TYPE (query)) {
281     case GST_QUERY_POSITION:
282       gst_query_parse_position (query, &format, NULL, NULL);
283       switch (format) {
284         case GST_FORMAT_DEFAULT:
285         case GST_FORMAT_BYTES:
286           gst_query_set_position (query, GST_FORMAT_BYTES,
287               self->data_written, self->data_written);
288           return TRUE;
289         default:
290           return FALSE;
291       }
292
293     case GST_QUERY_FORMATS:
294       gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
295       return TRUE;
296
297     default:
298       return gst_pad_query_default (pad, query);
299   }
300 }
301
302 static void
303 gst_file_sink_do_seek (GstFileSink * filesink, guint64 new_offset)
304 {
305   GST_DEBUG_OBJECT (filesink, "Seeking to offset %" G_GUINT64_FORMAT,
306       new_offset);
307
308 #ifdef G_OS_UNIX
309   if (lseek (fileno (filesink->file), (off_t) new_offset,
310           SEEK_SET) != (off_t) - 1)
311     return;
312 #else
313   if (fseek (filesink->file, (long) new_offset, SEEK_SET) == 0)
314     return;
315 #endif
316
317   GST_DEBUG_OBJECT (filesink, "Seeking failed: %s", g_strerror (errno));
318 }
319
320 /* handle events (search) */
321 static gboolean
322 gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
323 {
324   GstEventType type;
325   GstFileSink *filesink;
326
327   filesink = GST_FILE_SINK (sink);
328
329   type = GST_EVENT_TYPE (event);
330
331   switch (type) {
332     case GST_EVENT_NEWSEGMENT:
333     {
334       gint64 soffset, eoffset;
335       GstFormat format;
336
337       gst_event_parse_newsegment (event, NULL, &format, &soffset, &eoffset,
338           NULL);
339
340       if (format == GST_FORMAT_BYTES) {
341         gst_file_sink_do_seek (filesink, (guint64) soffset);
342       } else {
343         GST_DEBUG ("Ignored NEWSEGMENT event of format %u", (guint) format);
344       }
345       break;
346     }
347     case GST_EVENT_EOS:
348       if (fflush (filesink->file)) {
349         GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
350             (_("Error while writing to file \"%s\"."), filesink->filename),
351             GST_ERROR_SYSTEM);
352       }
353       break;
354     default:
355       break;
356   }
357
358   return TRUE;
359 }
360
361 static gboolean
362 gst_file_sink_get_current_offset (GstFileSink * filesink, guint64 * p_pos)
363 {
364   off_t ret;
365
366 #ifdef G_OS_UNIX
367   ret = lseek (fileno (filesink->file), 0, SEEK_CUR);
368 #else
369   ret = (off_t) ftell (filesink->file);
370 #endif
371
372   *p_pos = (guint64) ret;
373
374   return (ret != (off_t) - 1);
375 }
376
377 static GstFlowReturn
378 gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
379 {
380   GstFileSink *filesink;
381   guint64 cur_pos;
382   guint size, back_pending = 0;
383
384   size = GST_BUFFER_SIZE (buffer);
385
386   filesink = GST_FILE_SINK (sink);
387
388   if (!gst_file_sink_get_current_offset (filesink, &cur_pos))
389     goto handle_error;
390
391   if (cur_pos < filesink->data_written)
392     back_pending = filesink->data_written - cur_pos;
393
394   if (fwrite (GST_BUFFER_DATA (buffer), size, 1, filesink->file) != 1)
395     goto handle_error;
396
397   filesink->data_written += size - back_pending;
398
399   return GST_FLOW_OK;
400
401 handle_error:
402
403   GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
404       (_("Error while writing to file \"%s\"."), filesink->filename),
405       ("%s", g_strerror (errno)));
406
407   return GST_FLOW_ERROR;
408 }
409
410 static GstStateChangeReturn
411 gst_file_sink_change_state (GstElement * element, GstStateChange transition)
412 {
413   GstStateChangeReturn ret;
414
415   switch (transition) {
416     case GST_STATE_CHANGE_NULL_TO_READY:
417       break;
418     case GST_STATE_CHANGE_READY_TO_PAUSED:
419       if (!gst_file_sink_open_file (GST_FILE_SINK (element)))
420         goto open_error;
421       break;
422     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
423       break;
424     default:
425       break;
426   }
427
428   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
429
430   switch (transition) {
431     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
432       break;
433     case GST_STATE_CHANGE_PAUSED_TO_READY:
434       gst_file_sink_close_file (GST_FILE_SINK (element));
435       break;
436     case GST_STATE_CHANGE_READY_TO_NULL:
437       break;
438     default:
439       break;
440   }
441
442   return ret;
443
444 open_error:
445   {
446     return GST_STATE_CHANGE_FAILURE;
447   }
448 }
449
450 /*** GSTURIHANDLER INTERFACE *************************************************/
451
452 static guint
453 gst_file_sink_uri_get_type (void)
454 {
455   return GST_URI_SINK;
456 }
457 static gchar **
458 gst_file_sink_uri_get_protocols (void)
459 {
460   static gchar *protocols[] = { "file", NULL };
461
462   return protocols;
463 }
464 static const gchar *
465 gst_file_sink_uri_get_uri (GstURIHandler * handler)
466 {
467   GstFileSink *sink = GST_FILE_SINK (handler);
468
469   return sink->uri;
470 }
471
472 static gboolean
473 gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
474 {
475   gchar *protocol, *location;
476   gboolean ret;
477   GstFileSink *sink = GST_FILE_SINK (handler);
478
479   protocol = gst_uri_get_protocol (uri);
480   if (strcmp (protocol, "file") != 0) {
481     g_free (protocol);
482     return FALSE;
483   }
484   g_free (protocol);
485   location = gst_uri_get_location (uri);
486   ret = gst_file_sink_set_location (sink, location);
487   g_free (location);
488
489   return ret;
490 }
491
492 static void
493 gst_file_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
494 {
495   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
496
497   iface->get_type = gst_file_sink_uri_get_type;
498   iface->get_protocols = gst_file_sink_uri_get_protocols;
499   iface->get_uri = gst_file_sink_uri_get_uri;
500   iface->set_uri = gst_file_sink_uri_set_uri;
501 }