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