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