docs: remove outdated and pointless 'Last reviewed' lines from docs
[platform/upstream/gstreamer.git] / plugins / elements / gstfdsink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstfdsink.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., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 /**
24  * SECTION:element-fdsink
25  * @see_also: #GstFdSrc
26  *
27  * Write data to a unix file descriptor.
28  *
29  * This element will synchronize on the clock before writing the data on the
30  * socket. For file descriptors where this does not make sense (files, ...) the
31  * #GstBaseSink:sync property can be used to disable synchronisation.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #  include "config.h"
36 #endif
37
38 #include "../../gst/gst-i18n-lib.h"
39
40 #include <sys/types.h>
41
42 #include <sys/stat.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46 #include <fcntl.h>
47 #include <stdio.h>
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #ifdef _MSC_VER
52 #undef stat
53 #define stat _stat
54 #define fstat _fstat
55 #define S_ISREG(m)      (((m)&S_IFREG)==S_IFREG)
56 #endif
57 #include <errno.h>
58 #include <string.h>
59
60 #include "gstfdsink.h"
61
62 #ifdef G_OS_WIN32
63 #include <io.h>                 /* lseek, open, close, read */
64 #undef lseek
65 #define lseek _lseeki64
66 #undef off_t
67 #define off_t guint64
68 #endif
69
70 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
71     GST_PAD_SINK,
72     GST_PAD_ALWAYS,
73     GST_STATIC_CAPS_ANY);
74
75 GST_DEBUG_CATEGORY_STATIC (gst_fd_sink__debug);
76 #define GST_CAT_DEFAULT gst_fd_sink__debug
77
78
79 /* FdSink signals and args */
80 enum
81 {
82   /* FILL ME */
83   LAST_SIGNAL
84 };
85
86 enum
87 {
88   ARG_0,
89   ARG_FD
90 };
91
92 static void gst_fd_sink_uri_handler_init (gpointer g_iface,
93     gpointer iface_data);
94
95 #define _do_init \
96   G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_fd_sink_uri_handler_init); \
97   GST_DEBUG_CATEGORY_INIT (gst_fd_sink__debug, "fdsink", 0, "fdsink element");
98 #define gst_fd_sink_parent_class parent_class
99 G_DEFINE_TYPE_WITH_CODE (GstFdSink, gst_fd_sink, GST_TYPE_BASE_SINK, _do_init);
100
101 static void gst_fd_sink_set_property (GObject * object, guint prop_id,
102     const GValue * value, GParamSpec * pspec);
103 static void gst_fd_sink_get_property (GObject * object, guint prop_id,
104     GValue * value, GParamSpec * pspec);
105 static void gst_fd_sink_dispose (GObject * obj);
106
107 static gboolean gst_fd_sink_query (GstBaseSink * bsink, GstQuery * query);
108 static GstFlowReturn gst_fd_sink_render (GstBaseSink * sink,
109     GstBuffer * buffer);
110 static gboolean gst_fd_sink_start (GstBaseSink * basesink);
111 static gboolean gst_fd_sink_stop (GstBaseSink * basesink);
112 static gboolean gst_fd_sink_unlock (GstBaseSink * basesink);
113 static gboolean gst_fd_sink_unlock_stop (GstBaseSink * basesink);
114 static gboolean gst_fd_sink_event (GstBaseSink * sink, GstEvent * event);
115
116 static gboolean gst_fd_sink_do_seek (GstFdSink * fdsink, guint64 new_offset);
117
118 static void
119 gst_fd_sink_class_init (GstFdSinkClass * klass)
120 {
121   GObjectClass *gobject_class;
122   GstElementClass *gstelement_class;
123   GstBaseSinkClass *gstbasesink_class;
124
125   gobject_class = G_OBJECT_CLASS (klass);
126   gstelement_class = GST_ELEMENT_CLASS (klass);
127   gstbasesink_class = GST_BASE_SINK_CLASS (klass);
128
129   gobject_class->set_property = gst_fd_sink_set_property;
130   gobject_class->get_property = gst_fd_sink_get_property;
131   gobject_class->dispose = gst_fd_sink_dispose;
132
133   gst_element_class_set_static_metadata (gstelement_class,
134       "Filedescriptor Sink",
135       "Sink/File",
136       "Write data to a file descriptor", "Erik Walthinsen <omega@cse.ogi.edu>");
137   gst_element_class_add_pad_template (gstelement_class,
138       gst_static_pad_template_get (&sinktemplate));
139
140   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_fd_sink_render);
141   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_fd_sink_start);
142   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_fd_sink_stop);
143   gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_fd_sink_unlock);
144   gstbasesink_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_fd_sink_unlock_stop);
145   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_fd_sink_event);
146   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_fd_sink_query);
147
148   g_object_class_install_property (gobject_class, ARG_FD,
149       g_param_spec_int ("fd", "fd", "An open file descriptor to write to",
150           0, G_MAXINT, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 }
152
153 static void
154 gst_fd_sink_init (GstFdSink * fdsink)
155 {
156   fdsink->fd = 1;
157   fdsink->uri = g_strdup_printf ("fd://%d", fdsink->fd);
158   fdsink->bytes_written = 0;
159   fdsink->current_pos = 0;
160
161   gst_base_sink_set_sync (GST_BASE_SINK (fdsink), FALSE);
162 }
163
164 static void
165 gst_fd_sink_dispose (GObject * obj)
166 {
167   GstFdSink *fdsink = GST_FD_SINK (obj);
168
169   g_free (fdsink->uri);
170   fdsink->uri = NULL;
171
172   G_OBJECT_CLASS (parent_class)->dispose (obj);
173 }
174
175 static gboolean
176 gst_fd_sink_query (GstBaseSink * bsink, GstQuery * query)
177 {
178   gboolean res = FALSE;
179   GstFdSink *fdsink;
180
181   fdsink = GST_FD_SINK (bsink);
182
183   switch (GST_QUERY_TYPE (query)) {
184     case GST_QUERY_POSITION:
185     {
186       GstFormat format;
187
188       gst_query_parse_position (query, &format, NULL);
189
190       switch (format) {
191         case GST_FORMAT_DEFAULT:
192         case GST_FORMAT_BYTES:
193           gst_query_set_position (query, GST_FORMAT_BYTES, fdsink->current_pos);
194           res = TRUE;
195           break;
196         default:
197           break;
198       }
199       break;
200     }
201     case GST_QUERY_FORMATS:
202       gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
203       res = TRUE;
204       break;
205     case GST_QUERY_URI:
206       gst_query_set_uri (query, fdsink->uri);
207       res = TRUE;
208       break;
209     case GST_QUERY_SEEKING:{
210       GstFormat format;
211
212       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
213       if (format == GST_FORMAT_BYTES || format == GST_FORMAT_DEFAULT) {
214         gst_query_set_seeking (query, GST_FORMAT_BYTES, fdsink->seekable, 0,
215             -1);
216       } else {
217         gst_query_set_seeking (query, format, FALSE, 0, -1);
218       }
219       res = TRUE;
220       break;
221     }
222     default:
223       res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
224       break;
225
226   }
227   return res;
228 }
229
230 static GstFlowReturn
231 gst_fd_sink_render (GstBaseSink * sink, GstBuffer * buffer)
232 {
233   GstFdSink *fdsink;
234   GstMapInfo info;
235   guint8 *ptr;
236   gsize left;
237   gint written;
238
239 #ifndef HAVE_WIN32
240   gint retval;
241 #endif
242
243   fdsink = GST_FD_SINK (sink);
244
245   g_return_val_if_fail (fdsink->fd >= 0, GST_FLOW_ERROR);
246
247   gst_buffer_map (buffer, &info, GST_MAP_READ);
248
249   ptr = info.data;
250   left = info.size;
251
252 again:
253 #ifndef HAVE_WIN32
254   do {
255     GST_DEBUG_OBJECT (fdsink, "going into select, have %" G_GSIZE_FORMAT
256         " bytes to write", info.size);
257     retval = gst_poll_wait (fdsink->fdset, GST_CLOCK_TIME_NONE);
258   } while (retval == -1 && (errno == EINTR || errno == EAGAIN));
259
260   if (retval == -1) {
261     if (errno == EBUSY)
262       goto stopped;
263     else
264       goto select_error;
265   }
266 #endif
267
268   GST_DEBUG_OBJECT (fdsink, "writing %" G_GSIZE_FORMAT " bytes to"
269       " file descriptor %d", info.size, fdsink->fd);
270
271   written = write (fdsink->fd, ptr, left);
272
273   /* check for errors */
274   if (G_UNLIKELY (written < 0)) {
275     /* try to write again on non-fatal errors */
276     if (errno == EAGAIN || errno == EINTR)
277       goto again;
278
279     /* else go to our error handler */
280     goto write_error;
281   }
282
283   /* all is fine when we get here */
284   left -= written;
285   ptr += written;
286   fdsink->bytes_written += written;
287   fdsink->current_pos += written;
288
289   GST_DEBUG_OBJECT (fdsink, "wrote %d bytes, %" G_GSIZE_FORMAT " left", written,
290       left);
291
292   /* short write, select and try to write the remainder */
293   if (G_UNLIKELY (left > 0))
294     goto again;
295
296   gst_buffer_unmap (buffer, &info);
297
298   return GST_FLOW_OK;
299
300 #ifndef HAVE_WIN32
301 select_error:
302   {
303     GST_ELEMENT_ERROR (fdsink, RESOURCE, READ, (NULL),
304         ("select on file descriptor: %s.", g_strerror (errno)));
305     GST_DEBUG_OBJECT (fdsink, "Error during select");
306     gst_buffer_unmap (buffer, &info);
307     return GST_FLOW_ERROR;
308   }
309 stopped:
310   {
311     GST_DEBUG_OBJECT (fdsink, "Select stopped");
312     gst_buffer_unmap (buffer, &info);
313     return GST_FLOW_FLUSHING;
314   }
315 #endif
316
317 write_error:
318   {
319     switch (errno) {
320       case ENOSPC:
321         GST_ELEMENT_ERROR (fdsink, RESOURCE, NO_SPACE_LEFT, (NULL), (NULL));
322         break;
323       default:{
324         GST_ELEMENT_ERROR (fdsink, RESOURCE, WRITE, (NULL),
325             ("Error while writing to file descriptor %d: %s",
326                 fdsink->fd, g_strerror (errno)));
327       }
328     }
329     gst_buffer_unmap (buffer, &info);
330     return GST_FLOW_ERROR;
331   }
332 }
333
334 static gboolean
335 gst_fd_sink_check_fd (GstFdSink * fdsink, int fd, GError ** error)
336 {
337   struct stat stat_results;
338   off_t result;
339
340   /* see that it is a valid file descriptor */
341   if (fstat (fd, &stat_results) < 0)
342     goto invalid;
343
344   if (!S_ISREG (stat_results.st_mode))
345     goto not_seekable;
346
347   /* see if it is a seekable stream */
348   result = lseek (fd, 0, SEEK_CUR);
349   if (result == -1) {
350     switch (errno) {
351       case EINVAL:
352       case EBADF:
353         goto invalid;
354
355       case ESPIPE:
356         goto not_seekable;
357     }
358   } else
359     GST_DEBUG_OBJECT (fdsink, "File descriptor %d is seekable", fd);
360
361   return TRUE;
362
363 invalid:
364   {
365     GST_ELEMENT_ERROR (fdsink, RESOURCE, WRITE, (NULL),
366         ("File descriptor %d is not valid: %s", fd, g_strerror (errno)));
367     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
368         "File descriptor %d is not valid: %s", fd, g_strerror (errno));
369     return FALSE;
370   }
371 not_seekable:
372   {
373     GST_DEBUG_OBJECT (fdsink, "File descriptor %d is a pipe", fd);
374     return TRUE;
375   }
376 }
377
378 static gboolean
379 gst_fd_sink_start (GstBaseSink * basesink)
380 {
381   GstFdSink *fdsink;
382   GstPollFD fd = GST_POLL_FD_INIT;
383
384   fdsink = GST_FD_SINK (basesink);
385   if (!gst_fd_sink_check_fd (fdsink, fdsink->fd, NULL))
386     return FALSE;
387
388   if ((fdsink->fdset = gst_poll_new (TRUE)) == NULL)
389     goto socket_pair;
390
391   fd.fd = fdsink->fd;
392   gst_poll_add_fd (fdsink->fdset, &fd);
393   gst_poll_fd_ctl_write (fdsink->fdset, &fd, TRUE);
394
395   fdsink->bytes_written = 0;
396   fdsink->current_pos = 0;
397
398   fdsink->seekable = gst_fd_sink_do_seek (fdsink, 0);
399   GST_INFO_OBJECT (fdsink, "seeking supported: %d", fdsink->seekable);
400
401   return TRUE;
402
403   /* ERRORS */
404 socket_pair:
405   {
406     GST_ELEMENT_ERROR (fdsink, RESOURCE, OPEN_READ_WRITE, (NULL),
407         GST_ERROR_SYSTEM);
408     return FALSE;
409   }
410 }
411
412 static gboolean
413 gst_fd_sink_stop (GstBaseSink * basesink)
414 {
415   GstFdSink *fdsink = GST_FD_SINK (basesink);
416
417   if (fdsink->fdset) {
418     gst_poll_free (fdsink->fdset);
419     fdsink->fdset = NULL;
420   }
421
422   return TRUE;
423 }
424
425 static gboolean
426 gst_fd_sink_unlock (GstBaseSink * basesink)
427 {
428   GstFdSink *fdsink = GST_FD_SINK (basesink);
429
430   GST_LOG_OBJECT (fdsink, "Flushing");
431   GST_OBJECT_LOCK (fdsink);
432   gst_poll_set_flushing (fdsink->fdset, TRUE);
433   GST_OBJECT_UNLOCK (fdsink);
434
435   return TRUE;
436 }
437
438 static gboolean
439 gst_fd_sink_unlock_stop (GstBaseSink * basesink)
440 {
441   GstFdSink *fdsink = GST_FD_SINK (basesink);
442
443   GST_LOG_OBJECT (fdsink, "No longer flushing");
444   GST_OBJECT_LOCK (fdsink);
445   gst_poll_set_flushing (fdsink->fdset, FALSE);
446   GST_OBJECT_UNLOCK (fdsink);
447
448   return TRUE;
449 }
450
451 static gboolean
452 gst_fd_sink_update_fd (GstFdSink * fdsink, int new_fd, GError ** error)
453 {
454   if (new_fd < 0) {
455     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
456         "File descriptor %d is not valid", new_fd);
457     return FALSE;
458   }
459
460   if (!gst_fd_sink_check_fd (fdsink, new_fd, error))
461     goto invalid;
462
463   /* assign the fd */
464   GST_OBJECT_LOCK (fdsink);
465   if (fdsink->fdset) {
466     GstPollFD fd = GST_POLL_FD_INIT;
467
468     fd.fd = fdsink->fd;
469     gst_poll_remove_fd (fdsink->fdset, &fd);
470
471     fd.fd = new_fd;
472     gst_poll_add_fd (fdsink->fdset, &fd);
473     gst_poll_fd_ctl_write (fdsink->fdset, &fd, TRUE);
474   }
475   fdsink->fd = new_fd;
476   g_free (fdsink->uri);
477   fdsink->uri = g_strdup_printf ("fd://%d", fdsink->fd);
478
479   GST_OBJECT_UNLOCK (fdsink);
480
481   return TRUE;
482
483 invalid:
484   {
485     return FALSE;
486   }
487 }
488
489 static void
490 gst_fd_sink_set_property (GObject * object, guint prop_id,
491     const GValue * value, GParamSpec * pspec)
492 {
493   GstFdSink *fdsink;
494
495   fdsink = GST_FD_SINK (object);
496
497   switch (prop_id) {
498     case ARG_FD:{
499       int fd;
500
501       fd = g_value_get_int (value);
502       gst_fd_sink_update_fd (fdsink, fd, NULL);
503       break;
504     }
505     default:
506       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
507       break;
508   }
509 }
510
511 static void
512 gst_fd_sink_get_property (GObject * object, guint prop_id, GValue * value,
513     GParamSpec * pspec)
514 {
515   GstFdSink *fdsink;
516
517   fdsink = GST_FD_SINK (object);
518
519   switch (prop_id) {
520     case ARG_FD:
521       g_value_set_int (value, fdsink->fd);
522       break;
523     default:
524       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
525       break;
526   }
527 }
528
529 static gboolean
530 gst_fd_sink_do_seek (GstFdSink * fdsink, guint64 new_offset)
531 {
532   off_t result;
533
534   result = lseek (fdsink->fd, new_offset, SEEK_SET);
535
536   if (result == -1)
537     goto seek_failed;
538
539   fdsink->current_pos = new_offset;
540
541   GST_DEBUG_OBJECT (fdsink, "File descriptor %d to seek to position "
542       "%" G_GUINT64_FORMAT, fdsink->fd, fdsink->current_pos);
543
544   return TRUE;
545
546   /* ERRORS */
547 seek_failed:
548   {
549     GST_DEBUG_OBJECT (fdsink, "File descriptor %d failed to seek to position "
550         "%" G_GUINT64_FORMAT, fdsink->fd, new_offset);
551     return FALSE;
552   }
553 }
554
555 static gboolean
556 gst_fd_sink_event (GstBaseSink * sink, GstEvent * event)
557 {
558   GstEventType type;
559   GstFdSink *fdsink;
560
561   fdsink = GST_FD_SINK (sink);
562
563   type = GST_EVENT_TYPE (event);
564
565   switch (type) {
566     case GST_EVENT_SEGMENT:
567     {
568       const GstSegment *segment;
569
570       gst_event_parse_segment (event, &segment);
571
572       if (segment->format == GST_FORMAT_BYTES) {
573         /* only try to seek and fail when we are going to a different
574          * position */
575         if (fdsink->current_pos != segment->start) {
576           /* FIXME, the seek should be performed on the pos field, start/stop are
577            * just boundaries for valid bytes offsets. We should also fill the file
578            * with zeroes if the new position extends the current EOF (sparse streams
579            * and segment accumulation). */
580           if (!gst_fd_sink_do_seek (fdsink, (guint64) segment->start))
581             goto seek_failed;
582         }
583       } else {
584         GST_DEBUG_OBJECT (fdsink,
585             "Ignored SEGMENT event of format %u (%s)", (guint) segment->format,
586             gst_format_get_name (segment->format));
587       }
588       break;
589     }
590     default:
591       break;
592   }
593
594   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
595
596 seek_failed:
597   {
598     GST_ELEMENT_ERROR (fdsink, RESOURCE, SEEK, (NULL),
599         ("Error while seeking on file descriptor %d: %s",
600             fdsink->fd, g_strerror (errno)));
601     gst_event_unref (event);
602     return FALSE;
603   }
604
605 }
606
607 /*** GSTURIHANDLER INTERFACE *************************************************/
608
609 static GstURIType
610 gst_fd_sink_uri_get_type (GType type)
611 {
612   return GST_URI_SINK;
613 }
614
615 static const gchar *const *
616 gst_fd_sink_uri_get_protocols (GType type)
617 {
618   static const gchar *protocols[] = { "fd", NULL };
619
620   return protocols;
621 }
622
623 static gchar *
624 gst_fd_sink_uri_get_uri (GstURIHandler * handler)
625 {
626   GstFdSink *sink = GST_FD_SINK (handler);
627
628   /* FIXME: make thread-safe */
629   return g_strdup (sink->uri);
630 }
631
632 static gboolean
633 gst_fd_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri,
634     GError ** error)
635 {
636   GstFdSink *sink = GST_FD_SINK (handler);
637   gint fd;
638
639   if (sscanf (uri, "fd://%d", &fd) != 1) {
640     g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
641         "File descriptor URI could not be parsed");
642     return FALSE;
643   }
644
645   return gst_fd_sink_update_fd (sink, fd, error);
646 }
647
648 static void
649 gst_fd_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
650 {
651   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
652
653   iface->get_type = gst_fd_sink_uri_get_type;
654   iface->get_protocols = gst_fd_sink_uri_get_protocols;
655   iface->get_uri = gst_fd_sink_uri_get_uri;
656   iface->set_uri = gst_fd_sink_uri_set_uri;
657 }