check/: Add tests for fdsrc seekability
[platform/upstream/gstreamer.git] / plugins / elements / gstfdsrc.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2005 Philippe Khalaf <burger@speedy.org>
5  *
6  * gstfdsrc.c:
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 #include "gst/gst_private.h"
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/socket.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef _MSC_VER
38 #include <io.h>
39 #endif
40 #include <stdlib.h>
41 #include <errno.h>
42
43 #include "gstfdsrc.h"
44
45 /* the select call is also performed on the control sockets, that way
46  * we can send special commands to unblock the select call */
47 #define CONTROL_STOP            'S'     /* stop the select call */
48 #define CONTROL_SOCKETS(src)   src->control_sock
49 #define WRITE_SOCKET(src)      src->control_sock[1]
50 #define READ_SOCKET(src)       src->control_sock[0]
51
52 #define SEND_COMMAND(src, command)          \
53 G_STMT_START {                              \
54   unsigned char c; c = command;             \
55   write (WRITE_SOCKET(src), &c, 1);         \
56 } G_STMT_END
57
58 #define READ_COMMAND(src, command, res)        \
59 G_STMT_START {                                 \
60   res = read(READ_SOCKET(src), &command, 1);   \
61 } G_STMT_END
62
63 #define DEFAULT_BLOCKSIZE       4096
64
65 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
66     GST_PAD_SRC,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS_ANY);
69
70 GST_DEBUG_CATEGORY_STATIC (gst_fdsrc_debug);
71 #define GST_CAT_DEFAULT gst_fdsrc_debug
72
73 static GstElementDetails gst_fdsrc_details = GST_ELEMENT_DETAILS ("Disk Source",
74     "Source/File",
75     "Synchronous read from a file",
76     "Erik Walthinsen <omega@cse.ogi.edu>");
77
78 enum
79 {
80   PROP_0,
81   PROP_FD,
82 };
83
84 static void gst_fdsrc_uri_handler_init (gpointer g_iface, gpointer iface_data);
85
86 static void
87 _do_init (GType fdsrc_type)
88 {
89   static const GInterfaceInfo urihandler_info = {
90     gst_fdsrc_uri_handler_init,
91     NULL,
92     NULL
93   };
94
95   g_type_add_interface_static (fdsrc_type, GST_TYPE_URI_HANDLER,
96       &urihandler_info);
97
98   GST_DEBUG_CATEGORY_INIT (gst_fdsrc_debug, "fdsrc", 0, "fdsrc element");
99 }
100
101 GST_BOILERPLATE_FULL (GstFdSrc, gst_fdsrc, GstElement, GST_TYPE_PUSH_SRC,
102     _do_init);
103
104 static void gst_fdsrc_set_property (GObject * object, guint prop_id,
105     const GValue * value, GParamSpec * pspec);
106 static void gst_fdsrc_get_property (GObject * object, guint prop_id,
107     GValue * value, GParamSpec * pspec);
108 static void gst_fdsrc_dispose (GObject * obj);
109
110 static gboolean gst_fdsrc_start (GstBaseSrc * bsrc);
111 static gboolean gst_fdsrc_stop (GstBaseSrc * bsrc);
112 static gboolean gst_fdsrc_unlock (GstBaseSrc * bsrc);
113 static gboolean gst_fdsrc_is_seekable (GstBaseSrc * bsrc);
114 static gboolean gst_fdsrc_get_size (GstBaseSrc * src, guint64 * size);
115
116 static GstFlowReturn gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf);
117
118 static void
119 gst_fdsrc_base_init (gpointer g_class)
120 {
121   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
122
123   gst_element_class_add_pad_template (gstelement_class,
124       gst_static_pad_template_get (&srctemplate));
125   gst_element_class_set_details (gstelement_class, &gst_fdsrc_details);
126 }
127
128 static void
129 gst_fdsrc_class_init (GstFdSrcClass * klass)
130 {
131   GObjectClass *gobject_class;
132   GstBaseSrcClass *gstbasesrc_class;
133   GstElementClass *gstelement_class;
134   GstPushSrcClass *gstpush_src_class;
135
136   gobject_class = G_OBJECT_CLASS (klass);
137   gstelement_class = GST_ELEMENT_CLASS (klass);
138   gstbasesrc_class = (GstBaseSrcClass *) klass;
139   gstpush_src_class = (GstPushSrcClass *) klass;
140
141   parent_class = g_type_class_ref (GST_TYPE_PUSH_SRC);
142
143   gobject_class->set_property = gst_fdsrc_set_property;
144   gobject_class->get_property = gst_fdsrc_get_property;
145   gobject_class->dispose = gst_fdsrc_dispose;
146
147   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FD,
148       g_param_spec_int ("fd", "fd", "An open file descriptor to read from",
149           0, G_MAXINT, 0, G_PARAM_READWRITE));
150
151   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_fdsrc_start);
152   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_fdsrc_stop);
153   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_fdsrc_unlock);
154   gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_fdsrc_is_seekable);
155   gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_fdsrc_get_size);
156
157   gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_fdsrc_create);
158 }
159
160 static void
161 gst_fdsrc_init (GstFdSrc * fdsrc, GstFdSrcClass * klass)
162 {
163   fdsrc->fd = 0;
164   fdsrc->new_fd = 0;
165   fdsrc->seekable_fd = FALSE;
166   fdsrc->uri = g_strdup_printf ("fd://%d", fdsrc->fd);
167   fdsrc->curoffset = 0;
168 }
169
170 static void
171 gst_fdsrc_dispose (GObject * obj)
172 {
173   GstFdSrc *src = GST_FDSRC (obj);
174
175   g_free (src->uri);
176   src->uri = NULL;
177
178   G_OBJECT_CLASS (parent_class)->dispose (obj);
179 }
180
181 static void
182 gst_fdsrc_update_fd (GstFdSrc * src)
183 {
184   struct stat stat_results;
185
186   src->fd = src->new_fd;
187   g_free (src->uri);
188   src->uri = g_strdup_printf ("fd://%d", src->fd);
189
190   if (fstat (src->fd, &stat_results) < 0)
191     goto not_seekable;
192
193   if (!S_ISREG (stat_results.st_mode))
194     goto not_seekable;
195
196   /* Try a seek of 0 bytes offset to check for seekability */
197   if (lseek (src->fd, SEEK_CUR, 0) < 0)
198     goto not_seekable;
199
200   src->seekable_fd = TRUE;
201   return;
202
203 not_seekable:
204   src->seekable_fd = FALSE;
205 }
206
207 static gboolean
208 gst_fdsrc_start (GstBaseSrc * bsrc)
209 {
210   GstFdSrc *src = GST_FDSRC (bsrc);
211   gint control_sock[2];
212
213   src->curoffset = 0;
214
215   gst_fdsrc_update_fd (src);
216
217   if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
218     goto socket_pair;
219
220   READ_SOCKET (src) = control_sock[0];
221   WRITE_SOCKET (src) = control_sock[1];
222
223   fcntl (READ_SOCKET (src), F_SETFL, O_NONBLOCK);
224   fcntl (WRITE_SOCKET (src), F_SETFL, O_NONBLOCK);
225
226   return TRUE;
227
228   /* ERRORS */
229 socket_pair:
230   {
231     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ_WRITE, (NULL),
232         GST_ERROR_SYSTEM);
233     return FALSE;
234   }
235 }
236
237 static gboolean
238 gst_fdsrc_stop (GstBaseSrc * bsrc)
239 {
240   GstFdSrc *src = GST_FDSRC (bsrc);
241
242   close (READ_SOCKET (src));
243   close (WRITE_SOCKET (src));
244
245   return TRUE;
246 }
247
248 static gboolean
249 gst_fdsrc_unlock (GstBaseSrc * bsrc)
250 {
251   GstFdSrc *src = GST_FDSRC (bsrc);
252
253   SEND_COMMAND (src, CONTROL_STOP);
254
255   return TRUE;
256 }
257
258 static void
259 gst_fdsrc_set_property (GObject * object, guint prop_id, const GValue * value,
260     GParamSpec * pspec)
261 {
262   GstFdSrc *src = GST_FDSRC (object);
263
264   switch (prop_id) {
265     case PROP_FD:
266       src->new_fd = g_value_get_int (value);
267
268       /* If state is ready or below, update the current fd immediately
269        * so it is reflected in get_properties and uri */
270       GST_OBJECT_LOCK (object);
271       if (GST_STATE (GST_ELEMENT (src)) <= GST_STATE_READY) {
272         gst_fdsrc_update_fd (src);
273       }
274       GST_OBJECT_UNLOCK (object);
275       break;
276     default:
277       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
278       break;
279   }
280 }
281
282 static void
283 gst_fdsrc_get_property (GObject * object, guint prop_id, GValue * value,
284     GParamSpec * pspec)
285 {
286   GstFdSrc *src = GST_FDSRC (object);
287
288   switch (prop_id) {
289     case PROP_FD:
290       g_value_set_int (value, src->fd);
291       break;
292     default:
293       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
294       break;
295   }
296 }
297
298 static GstFlowReturn
299 gst_fdsrc_create (GstPushSrc * psrc, GstBuffer ** outbuf)
300 {
301   GstFdSrc *src;
302   GstBuffer *buf;
303   glong readbytes;
304   guint blocksize;
305
306 #ifndef HAVE_WIN32
307   fd_set readfds;
308   gint retval;
309 #endif
310
311   src = GST_FDSRC (psrc);
312
313 #ifndef HAVE_WIN32
314   FD_ZERO (&readfds);
315   FD_SET (src->fd, &readfds);
316   FD_SET (READ_SOCKET (src), &readfds);
317
318   do {
319     retval = select (FD_SETSIZE, &readfds, NULL, NULL, NULL);
320   } while ((retval == -1 && errno == EINTR));
321
322   if (retval == -1)
323     goto select_error;
324
325   if (FD_ISSET (READ_SOCKET (src), &readfds)) {
326     /* read all stop commands */
327     while (TRUE) {
328       gchar command;
329       int res;
330
331       READ_COMMAND (src, command, res);
332       if (res < 0) {
333         GST_LOG_OBJECT (src, "no more commands");
334         /* no more commands */
335         break;
336       }
337     }
338     goto stopped;
339   }
340 #endif
341
342   blocksize = GST_BASE_SRC (src)->blocksize;
343
344   /* create the buffer */
345   buf = gst_buffer_new_and_alloc (blocksize);
346
347   do {
348     readbytes = read (src->fd, GST_BUFFER_DATA (buf), blocksize);
349   } while (readbytes == -1 && errno == EINTR);  /* retry if interrupted */
350
351   if (readbytes < 0)
352     goto read_error;
353
354   if (readbytes == 0)
355     goto eos;
356
357   GST_BUFFER_OFFSET (buf) = src->curoffset;
358   GST_BUFFER_SIZE (buf) = readbytes;
359   GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
360   src->curoffset += readbytes;
361
362   GST_DEBUG_OBJECT (psrc, "Read buffer of size %u.", readbytes);
363
364   /* we're done, return the buffer */
365   *outbuf = buf;
366
367   return GST_FLOW_OK;
368
369   /* ERRORS */
370 #ifndef HAVE_WIN32
371 select_error:
372   {
373     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
374         ("select on file descriptor: %s.", g_strerror (errno)));
375     GST_DEBUG_OBJECT (psrc, "Error during select");
376     return GST_FLOW_ERROR;
377   }
378 stopped:
379   {
380     GST_DEBUG_OBJECT (psrc, "Select stopped");
381     return GST_FLOW_WRONG_STATE;
382   }
383 #endif
384 eos:
385   {
386     GST_DEBUG_OBJECT (psrc, "Read 0 bytes. EOS.");
387     gst_buffer_unref (buf);
388     return GST_FLOW_UNEXPECTED;
389   }
390 read_error:
391   {
392     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
393         ("read on file descriptor: %s.", g_strerror (errno)));
394     GST_DEBUG_OBJECT (psrc, "Error reading from fd");
395     gst_buffer_unref (buf);
396     return GST_FLOW_ERROR;
397   }
398 }
399
400 gboolean
401 gst_fdsrc_is_seekable (GstBaseSrc * bsrc)
402 {
403   GstFdSrc *src = GST_FDSRC (bsrc);
404
405   return src->seekable_fd;
406 }
407
408 gboolean
409 gst_fdsrc_get_size (GstBaseSrc * bsrc, guint64 * size)
410 {
411   GstFdSrc *src = GST_FDSRC (bsrc);
412   struct stat stat_results;
413
414   if (!src->seekable_fd) {
415     /* If it isn't seekable, we won't know the length (but fstat will still
416      * succeed, and wrongly say our length is zero. */
417     return FALSE;
418   }
419
420   if (fstat (src->fd, &stat_results) < 0)
421     goto could_not_stat;
422
423   *size = stat_results.st_size;
424
425   return TRUE;
426
427   /* ERROR */
428 could_not_stat:
429   {
430     return FALSE;
431   }
432
433 }
434
435 /*** GSTURIHANDLER INTERFACE *************************************************/
436
437 static guint
438 gst_fdsrc_uri_get_type (void)
439 {
440   return GST_URI_SRC;
441 }
442 static gchar **
443 gst_fdsrc_uri_get_protocols (void)
444 {
445   static gchar *protocols[] = { "fd", NULL };
446
447   return protocols;
448 }
449 static const gchar *
450 gst_fdsrc_uri_get_uri (GstURIHandler * handler)
451 {
452   GstFdSrc *src = GST_FDSRC (handler);
453
454   return src->uri;
455 }
456
457 static gboolean
458 gst_fdsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
459 {
460   gchar *protocol;
461   GstFdSrc *src = GST_FDSRC (handler);
462   gint fd;
463
464   protocol = gst_uri_get_protocol (uri);
465   if (strcmp (protocol, "fd") != 0) {
466     g_free (protocol);
467     return FALSE;
468   }
469   g_free (protocol);
470
471   if (sscanf (uri, "fd://%d", &fd) != 1)
472     return FALSE;
473
474   src->new_fd = fd;
475
476   GST_OBJECT_LOCK (src);
477   if (GST_STATE (GST_ELEMENT (src)) <= GST_STATE_READY) {
478     gst_fdsrc_update_fd (src);
479   }
480   GST_OBJECT_UNLOCK (src);
481
482   return TRUE;
483 }
484
485 static void
486 gst_fdsrc_uri_handler_init (gpointer g_iface, gpointer iface_data)
487 {
488   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
489
490   iface->get_type = gst_fdsrc_uri_get_type;
491   iface->get_protocols = gst_fdsrc_uri_get_protocols;
492   iface->get_uri = gst_fdsrc_uri_get_uri;
493   iface->set_uri = gst_fdsrc_uri_set_uri;
494 }