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 */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include "../gst-i18n-lib.h"
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 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
41 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
42 GST_PAD_SINK,
43 GST_PAD_ALWAYS,
44 GST_STATIC_CAPS_ANY);
46 GST_DEBUG_CATEGORY_STATIC (gst_file_sink_debug);
47 #define GST_CAT_DEFAULT gst_file_sink_debug
49 GstElementDetails gst_file_sink_details = GST_ELEMENT_DETAILS ("File Sink",
50 "Sink/File",
51 "Write stream to a file",
52 "Thomas <thomas@apestaart.org>");
55 /* FileSink signals and args */
56 enum
57 {
58 /* FILL ME */
59 SIGNAL_HANDOFF,
60 LAST_SIGNAL
61 };
63 enum
64 {
65 ARG_0,
66 ARG_LOCATION
67 };
69 static void gst_file_sink_dispose (GObject * object);
71 static void gst_file_sink_set_property (GObject * object, guint prop_id,
72 const GValue * value, GParamSpec * pspec);
73 static void gst_file_sink_get_property (GObject * object, guint prop_id,
74 GValue * value, GParamSpec * pspec);
76 static gboolean gst_file_sink_open_file (GstFileSink * sink);
77 static void gst_file_sink_close_file (GstFileSink * sink);
79 static gboolean gst_file_sink_event (GstBaseSink * sink, GstEvent * event);
80 static GstFlowReturn gst_file_sink_render (GstBaseSink * sink,
81 GstBuffer * buffer);
83 static gboolean gst_file_sink_query (GstPad * pad, GstQuery * query);
85 static void gst_file_sink_uri_handler_init (gpointer g_iface,
86 gpointer iface_data);
88 static GstElementStateReturn gst_file_sink_change_state (GstElement * element);
90 //static guint gst_file_sink_signals[LAST_SIGNAL] = { 0 };
92 static void
93 _do_init (GType filesink_type)
94 {
95 static const GInterfaceInfo urihandler_info = {
96 gst_file_sink_uri_handler_init,
97 NULL,
98 NULL
99 };
101 g_type_add_interface_static (filesink_type, GST_TYPE_URI_HANDLER,
102 &urihandler_info);
103 GST_DEBUG_CATEGORY_INIT (gst_file_sink_debug, "filesink", 0,
104 "filesink element");
105 }
107 GST_BOILERPLATE_FULL (GstFileSink, gst_file_sink, GstBaseSink,
108 GST_TYPE_BASE_SINK, _do_init);
110 static void
111 gst_file_sink_base_init (gpointer g_class)
112 {
113 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
115 gstelement_class->change_state = gst_file_sink_change_state;
116 gst_element_class_add_pad_template (gstelement_class,
117 gst_static_pad_template_get (&sinktemplate));
118 gst_element_class_set_details (gstelement_class, &gst_file_sink_details);
119 }
121 static void
122 gst_file_sink_class_init (GstFileSinkClass * klass)
123 {
124 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
125 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
127 gobject_class->set_property = gst_file_sink_set_property;
128 gobject_class->get_property = gst_file_sink_get_property;
130 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
131 g_param_spec_string ("location", "File Location",
132 "Location of the file to write", NULL, G_PARAM_READWRITE));
134 gobject_class->dispose = gst_file_sink_dispose;
136 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_file_sink_render);
137 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_file_sink_event);
138 }
140 static void
141 gst_file_sink_init (GstFileSink * filesink)
142 {
143 GstPad *pad;
145 pad = GST_BASE_SINK_PAD (filesink);
147 gst_pad_set_query_function (pad, gst_file_sink_query);
149 filesink->filename = NULL;
150 filesink->file = NULL;
151 }
153 static void
154 gst_file_sink_dispose (GObject * object)
155 {
156 GstFileSink *sink = GST_FILE_SINK (object);
158 G_OBJECT_CLASS (parent_class)->dispose (object);
160 g_free (sink->uri);
161 sink->uri = NULL;
162 g_free (sink->filename);
163 sink->filename = NULL;
164 }
166 static gboolean
167 gst_file_sink_set_location (GstFileSink * sink, const gchar * location)
168 {
169 /* the element must be stopped or paused in order to do this */
170 if (GST_STATE (sink) >= GST_STATE_PAUSED)
171 return FALSE;
173 g_free (sink->filename);
174 g_free (sink->uri);
175 if (location != NULL) {
176 sink->filename = g_strdup (location);
177 sink->uri = gst_uri_construct ("file", location);
178 } else {
179 sink->filename = NULL;
180 sink->uri = NULL;
181 }
183 return TRUE;
184 }
185 static void
186 gst_file_sink_set_property (GObject * object, guint prop_id,
187 const GValue * value, GParamSpec * pspec)
188 {
189 GstFileSink *sink;
191 sink = GST_FILE_SINK (object);
193 switch (prop_id) {
194 case ARG_LOCATION:
195 gst_file_sink_set_location (sink, g_value_get_string (value));
196 break;
197 default:
198 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199 break;
200 }
201 }
203 static void
204 gst_file_sink_get_property (GObject * object, guint prop_id, GValue * value,
205 GParamSpec * pspec)
206 {
207 GstFileSink *sink;
209 g_return_if_fail (GST_IS_FILE_SINK (object));
211 sink = GST_FILE_SINK (object);
213 switch (prop_id) {
214 case ARG_LOCATION:
215 g_value_set_string (value, sink->filename);
216 break;
217 default:
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
219 break;
220 }
221 }
223 static gboolean
224 gst_file_sink_open_file (GstFileSink * sink)
225 {
226 /* open the file */
227 if (sink->filename == NULL || sink->filename[0] == '\0') {
228 GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
229 (_("No file name specified for writing.")), (NULL));
230 return FALSE;
231 }
233 sink->file = fopen (sink->filename, "wb");
234 if (sink->file == NULL) {
235 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
236 (_("Could not open file \"%s\" for writing."), sink->filename),
237 GST_ERROR_SYSTEM);
238 return FALSE;
239 }
241 sink->data_written = 0;
243 return TRUE;
244 }
246 static void
247 gst_file_sink_close_file (GstFileSink * sink)
248 {
249 if (fclose (sink->file) != 0) {
250 GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
251 (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM);
252 }
253 }
255 static gboolean
256 gst_file_sink_query (GstPad * pad, GstQuery * query)
257 {
258 GstFileSink *self;
259 GstFormat format;
261 self = GST_FILE_SINK (GST_PAD_PARENT (pad));
263 switch (GST_QUERY_TYPE (query)) {
264 case GST_QUERY_POSITION:
265 gst_query_parse_position (query, &format, NULL, NULL);
266 switch (format) {
267 case GST_FORMAT_DEFAULT:
268 case GST_FORMAT_BYTES:
269 gst_query_set_position (query, GST_FORMAT_BYTES,
270 self->data_written, self->data_written);
271 return TRUE;
272 default:
273 return FALSE;
274 }
276 case GST_QUERY_FORMATS:
277 gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
278 return TRUE;
280 default:
281 return gst_pad_query_default (pad, query);
282 }
283 }
285 /* handle events (search) */
286 static gboolean
287 gst_file_sink_event (GstBaseSink * sink, GstEvent * event)
288 {
289 GstEventType type;
290 GstFileSink *filesink;
292 filesink = GST_FILE_SINK (sink);
294 type = GST_EVENT_TYPE (event);
296 switch (type) {
297 case GST_EVENT_NEWSEGMENT:
298 {
299 gint64 soffset, eoffset;
300 GstFormat format;
302 gst_event_parse_newsegment (event, NULL, &format, &soffset, &eoffset,
303 NULL);
305 if (format == GST_FORMAT_BYTES) {
306 fseek (filesink->file, soffset, SEEK_SET);
307 }
308 break;
309 }
310 case GST_EVENT_EOS:
311 if (fflush (filesink->file)) {
312 GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
313 (_("Error while writing to file \"%s\"."), filesink->filename),
314 GST_ERROR_SYSTEM);
315 }
316 break;
317 default:
318 break;
319 }
321 return TRUE;
322 }
324 /**
325 * gst_file_sink_chain:
326 * @pad: the pad this filesink is connected to
327 * @buf: the buffer that has to be absorbed
328 *
329 * take the buffer from the pad and write to file if it's open
330 */
331 static GstFlowReturn
332 gst_file_sink_render (GstBaseSink * sink, GstBuffer * buffer)
333 {
334 GstFileSink *filesink;
335 guint size, back_pending = 0;
337 size = GST_BUFFER_SIZE (buffer);
339 filesink = GST_FILE_SINK (sink);
341 if (ftell (filesink->file) < filesink->data_written)
342 back_pending = filesink->data_written - ftell (filesink->file);
344 if (fwrite (GST_BUFFER_DATA (buffer), size, 1, filesink->file) != 1) {
345 GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE,
346 (_("Error while writing to file \"%s\"."), filesink->filename),
347 ("%s", g_strerror (errno)));
348 return GST_FLOW_ERROR;
349 }
351 filesink->data_written += size - back_pending;
353 return GST_FLOW_OK;
354 }
356 static GstElementStateReturn
357 gst_file_sink_change_state (GstElement * element)
358 {
359 GstElementStateReturn ret;
360 gint transition;
362 transition = GST_STATE_TRANSITION (element);
364 switch (transition) {
365 case GST_STATE_NULL_TO_READY:
366 break;
367 case GST_STATE_READY_TO_PAUSED:
368 if (!gst_file_sink_open_file (GST_FILE_SINK (element)))
369 goto open_error;
370 break;
371 case GST_STATE_PAUSED_TO_PLAYING:
372 break;
373 default:
374 break;
375 }
377 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
379 switch (transition) {
380 case GST_STATE_PLAYING_TO_PAUSED:
381 break;
382 case GST_STATE_PAUSED_TO_READY:
383 gst_file_sink_close_file (GST_FILE_SINK (element));
384 break;
385 case GST_STATE_READY_TO_NULL:
386 break;
387 default:
388 break;
389 }
391 return ret;
393 open_error:
394 {
395 return GST_STATE_FAILURE;
396 }
397 }
399 /*** GSTURIHANDLER INTERFACE *************************************************/
401 static guint
402 gst_file_sink_uri_get_type (void)
403 {
404 return GST_URI_SINK;
405 }
406 static gchar **
407 gst_file_sink_uri_get_protocols (void)
408 {
409 static gchar *protocols[] = { "file", NULL };
411 return protocols;
412 }
413 static const gchar *
414 gst_file_sink_uri_get_uri (GstURIHandler * handler)
415 {
416 GstFileSink *sink = GST_FILE_SINK (handler);
418 return sink->uri;
419 }
421 static gboolean
422 gst_file_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
423 {
424 gchar *protocol, *location;
425 gboolean ret;
426 GstFileSink *sink = GST_FILE_SINK (handler);
428 protocol = gst_uri_get_protocol (uri);
429 if (strcmp (protocol, "file") != 0) {
430 g_free (protocol);
431 return FALSE;
432 }
433 g_free (protocol);
434 location = gst_uri_get_location (uri);
435 ret = gst_file_sink_set_location (sink, location);
436 g_free (location);
438 return ret;
439 }
441 static void
442 gst_file_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
443 {
444 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
446 iface->get_type = gst_file_sink_uri_get_type;
447 iface->get_protocols = gst_file_sink_uri_get_protocols;
448 iface->get_uri = gst_file_sink_uri_get_uri;
449 iface->set_uri = gst_file_sink_uri_set_uri;
450 }