4bdcb0d2dd2b8f0c85e040af60e02b87b9e6db16
[platform/upstream/gst-plugins-good.git] / sys / oss / gstosssrc.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstosssrc.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  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <sys/soundcard.h>
30 #include <sys/ioctl.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <string.h>
34
35 #include <gstosssrc.h>
36 #include <gstosselement.h>
37 #include <gst/audio/audioclock.h>
38
39 /* elementfactory information */
40 static GstElementDetails gst_osssrc_details = GST_ELEMENT_DETAILS (
41   "Audio Source (OSS)",
42   "Source/Audio",
43   "Read from the sound card",
44   "Erik Walthinsen <omega@cse.ogi.edu>"
45 );
46
47
48 /* OssSrc signals and args */
49 enum {
50   /* FILL ME */
51   LAST_SIGNAL
52 };
53
54 enum {
55   ARG_0,
56   ARG_BUFFERSIZE,
57   ARG_FRAGMENT,
58 };
59
60 static GstStaticPadTemplate osssrc_src_factory =
61 GST_STATIC_PAD_TEMPLATE (
62   "src",
63   GST_PAD_SRC,
64   GST_PAD_ALWAYS,
65   GST_STATIC_CAPS ("audio/x-raw-int, "
66       "endianness = (int) BYTE_ORDER, "
67       "signed = (boolean) { TRUE, FALSE }, "
68       "width = (int) { 8, 16 }, "
69       "depth = (int) { 8, 16 }, "
70       "rate = (int) [ 1000, 48000 ], "
71       "channels = (int) [ 1, 2 ]"
72   )
73 );
74
75 static void                     gst_osssrc_base_init    (gpointer g_class);
76 static void                     gst_osssrc_class_init   (GstOssSrcClass *klass);
77 static void                     gst_osssrc_init         (GstOssSrc *osssrc);
78 static void                     gst_osssrc_dispose      (GObject *object);
79
80 static GstPadLinkReturn         gst_osssrc_srcconnect   (GstPad *pad, const GstCaps *caps);
81 static const GstFormat*         gst_osssrc_get_formats  (GstPad *pad);
82 static gboolean                 gst_osssrc_convert      (GstPad *pad, 
83                                                          GstFormat src_format, gint64 src_value,
84                                                          GstFormat *dest_format, gint64 *dest_value);
85
86 static void                     gst_osssrc_set_property (GObject *object, guint prop_id, 
87                                                          const GValue *value, GParamSpec *pspec);
88 static void                     gst_osssrc_get_property (GObject *object, guint prop_id,
89                                                          GValue *value, GParamSpec *pspec);
90 static GstElementStateReturn    gst_osssrc_change_state (GstElement *element);
91
92 static void                     gst_osssrc_set_clock    (GstElement *element, GstClock *clock);
93 static GstClock*                gst_osssrc_get_clock    (GstElement *element);
94 static GstClockTime             gst_osssrc_get_time     (GstClock *clock, gpointer data);
95
96 static const GstEventMask*      gst_osssrc_get_event_masks (GstPad *pad);
97 static gboolean                 gst_osssrc_src_event    (GstPad *pad, GstEvent *event);
98 static gboolean                 gst_osssrc_send_event   (GstElement *element, GstEvent *event);
99 static const GstQueryType*      gst_osssrc_get_query_types (GstPad *pad);
100 static gboolean                 gst_osssrc_src_query    (GstPad *pad, GstQueryType type, 
101                                                          GstFormat *format, gint64 *value);
102
103 static GstData *                gst_osssrc_get          (GstPad *pad);
104
105 static GstElementClass *parent_class = NULL;
106 /*static guint gst_osssrc_signals[LAST_SIGNAL] = { 0 }; */
107
108 GType
109 gst_osssrc_get_type (void) 
110 {
111   static GType osssrc_type = 0;
112
113   if (!osssrc_type) {
114     static const GTypeInfo osssrc_info = {
115       sizeof(GstOssSrcClass),
116       gst_osssrc_base_init,
117       NULL,
118       (GClassInitFunc)gst_osssrc_class_init,
119       NULL,
120       NULL,
121       sizeof(GstOssSrc),
122       0,
123       (GInstanceInitFunc)gst_osssrc_init,
124     };
125     osssrc_type = g_type_register_static (GST_TYPE_OSSELEMENT, "GstOssSrc", &osssrc_info, 0);
126   }
127   return osssrc_type;
128 }
129
130 static void
131 gst_osssrc_base_init (gpointer g_class)
132 {
133   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
134   
135   gst_element_class_set_details (element_class, &gst_osssrc_details);
136   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&osssrc_src_factory));
137 }
138 static void
139 gst_osssrc_class_init (GstOssSrcClass *klass) 
140 {
141   GObjectClass *gobject_class;
142   GstElementClass *gstelement_class;
143
144   gobject_class = (GObjectClass*)klass;
145   gstelement_class = (GstElementClass*)klass;
146
147   parent_class = g_type_class_ref (GST_TYPE_OSSELEMENT);
148
149   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFERSIZE,
150     g_param_spec_ulong ("buffersize","Buffer Size","The size of the buffers with samples",
151                         0, G_MAXULONG, 0, G_PARAM_READWRITE));
152   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FRAGMENT,
153     g_param_spec_int ("fragment", "Fragment",
154                       "The fragment as 0xMMMMSSSS (MMMM = total fragments, 2^SSSS = fragment size)",
155                       0, G_MAXINT, 6, G_PARAM_READWRITE));
156   
157   gobject_class->set_property = gst_osssrc_set_property;
158   gobject_class->get_property = gst_osssrc_get_property;
159   gobject_class->dispose      = gst_osssrc_dispose;
160
161   gstelement_class->change_state = gst_osssrc_change_state;
162   gstelement_class->send_event = gst_osssrc_send_event;
163
164   gstelement_class->set_clock = gst_osssrc_set_clock;
165   gstelement_class->get_clock = gst_osssrc_get_clock;
166 }
167
168 static void 
169 gst_osssrc_init (GstOssSrc *osssrc) 
170 {
171   osssrc->srcpad = gst_pad_new_from_template (
172                   gst_static_pad_template_get (&osssrc_src_factory), "src");
173   gst_pad_set_get_function (osssrc->srcpad, gst_osssrc_get);
174   gst_pad_set_link_function (osssrc->srcpad, gst_osssrc_srcconnect);
175   gst_pad_set_convert_function (osssrc->srcpad, gst_osssrc_convert);
176   gst_pad_set_formats_function (osssrc->srcpad, gst_osssrc_get_formats);
177   gst_pad_set_event_function (osssrc->srcpad, gst_osssrc_src_event);
178   gst_pad_set_event_mask_function (osssrc->srcpad, gst_osssrc_get_event_masks);
179   gst_pad_set_query_function (osssrc->srcpad, gst_osssrc_src_query);
180   gst_pad_set_query_type_function (osssrc->srcpad, gst_osssrc_get_query_types);
181
182
183   gst_element_add_pad (GST_ELEMENT (osssrc), osssrc->srcpad);
184
185   osssrc->buffersize = 4096;
186   osssrc->curoffset = 0;
187
188   osssrc->provided_clock = gst_audio_clock_new ("ossclock", gst_osssrc_get_time, osssrc);
189   gst_object_set_parent (GST_OBJECT (osssrc->provided_clock), GST_OBJECT (osssrc));
190   
191   osssrc->clock = NULL;
192 }
193
194 static void
195 gst_osssrc_dispose (GObject *object)
196 {
197   GstOssSrc *osssrc = (GstOssSrc *) object;
198
199   gst_object_unparent (GST_OBJECT (osssrc->provided_clock));
200
201   G_OBJECT_CLASS (parent_class)->dispose (object);
202 }
203
204 static GstPadLinkReturn 
205 gst_osssrc_srcconnect (GstPad *pad, const GstCaps *caps)
206 {
207   GstOssSrc *src;
208
209   src = GST_OSSSRC(gst_pad_get_parent (pad));
210
211   if (!gst_osselement_parse_caps (GST_OSSELEMENT (src), caps))
212     return GST_PAD_LINK_REFUSED;
213
214   if (!gst_osselement_sync_parms (GST_OSSELEMENT (src)))
215     return GST_PAD_LINK_REFUSED;
216
217   return GST_PAD_LINK_OK;
218 }
219
220 static gboolean
221 gst_osssrc_negotiate (GstPad *pad)
222 {
223   GstOssSrc *src;
224   GstCaps *allowed;
225
226   src = GST_OSSSRC(gst_pad_get_parent (pad));
227
228   allowed = gst_pad_get_allowed_caps (pad);
229
230   if (!gst_osselement_merge_fixed_caps (GST_OSSELEMENT (src), allowed))
231     return FALSE;
232
233   if (!gst_osselement_sync_parms (GST_OSSELEMENT (src)))
234     return FALSE;
235     
236   /* set caps on src pad */
237   if (gst_pad_try_set_caps (src->srcpad, 
238         gst_caps_new_simple("audio/x-raw-int",
239             "endianness", G_TYPE_INT, GST_OSSELEMENT (src)->endianness,
240             "signed",     G_TYPE_BOOLEAN, GST_OSSELEMENT (src)->sign,
241             "width",      G_TYPE_INT, GST_OSSELEMENT (src)->width,
242             "depth",      G_TYPE_INT, GST_OSSELEMENT (src)->depth,
243             "rate",       G_TYPE_INT, GST_OSSELEMENT (src)->rate,
244             "channels",   G_TYPE_INT, GST_OSSELEMENT (src)->channels,
245             NULL)) <= 0) {
246     return FALSE;
247   }
248   return TRUE;
249 }
250
251 static GstClockTime 
252 gst_osssrc_get_time (GstClock *clock, gpointer data) 
253 {
254   GstOssSrc *osssrc = GST_OSSSRC (data);
255   audio_buf_info info;
256
257   if (!GST_OSSELEMENT (osssrc)->bps)
258     return 0;
259
260   if (ioctl(GST_OSSELEMENT (osssrc)->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
261     return 0;
262
263   return (osssrc->curoffset + info.bytes) * GST_SECOND / GST_OSSELEMENT (osssrc)->bps;
264 }
265
266 static GstClock*
267 gst_osssrc_get_clock (GstElement *element)
268 {
269   GstOssSrc *osssrc;
270             
271   osssrc = GST_OSSSRC (element);
272
273   return GST_CLOCK (osssrc->provided_clock);
274 }
275
276 static void
277 gst_osssrc_set_clock (GstElement *element, GstClock *clock)
278 {
279   GstOssSrc *osssrc;
280   
281   osssrc = GST_OSSSRC (element);
282
283   osssrc->clock = clock;
284 }
285         
286 static GstData *
287 gst_osssrc_get (GstPad *pad)
288 {
289   GstOssSrc *src;
290   GstBuffer *buf;
291   glong readbytes;
292
293   src = GST_OSSSRC(gst_pad_get_parent (pad));
294
295   GST_DEBUG ("attempting to read something from the soundcard");
296
297   if (src->need_eos) {
298     src->need_eos = FALSE;
299     return GST_DATA (gst_event_new (GST_EVENT_EOS));
300   }
301   
302   buf = gst_buffer_new_and_alloc (src->buffersize);
303   
304   if (!GST_PAD_CAPS (pad)) {
305     /* nothing was negotiated, we can decide on a format */
306     if (!gst_osssrc_negotiate (pad)) {
307       gst_buffer_unref (buf);
308       GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), (NULL));
309       return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
310     }
311   }
312   if (GST_OSSELEMENT (src)->bps == 0) {
313     gst_buffer_unref (buf);
314     GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL),
315                        ("format wasn't negotiated before chain function"));
316     return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
317   }
318
319   readbytes = read (GST_OSSELEMENT (src)->fd,GST_BUFFER_DATA (buf),
320                     src->buffersize);
321   if (readbytes < 0) {
322     gst_buffer_unref (buf);
323     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
324     return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
325   }
326
327   if (readbytes == 0) {
328     gst_buffer_unref (buf);
329     gst_element_set_eos (GST_ELEMENT (src));
330     return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
331   }
332
333   GST_BUFFER_SIZE (buf) = readbytes;
334   GST_BUFFER_OFFSET (buf) = src->curoffset;
335
336   /* FIXME: we are falsely assuming that we are the master clock here */
337   GST_BUFFER_TIMESTAMP (buf) = src->curoffset * GST_SECOND / GST_OSSELEMENT (src)->bps;
338   GST_BUFFER_DURATION (buf) = (GST_SECOND * GST_BUFFER_SIZE (buf)) /
339                               (GST_OSSELEMENT (src)->bps * GST_OSSELEMENT (src)->rate);
340
341   src->curoffset += readbytes;
342
343   GST_DEBUG ("pushed buffer from soundcard of %ld bytes, timestamp %" G_GINT64_FORMAT, 
344                   readbytes, GST_BUFFER_TIMESTAMP (buf));
345
346   return GST_DATA (buf);
347 }
348
349 static void 
350 gst_osssrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
351 {
352   GstOssSrc *src;
353
354   src = GST_OSSSRC (object);
355
356   switch (prop_id) {
357     case ARG_BUFFERSIZE:
358       src->buffersize = g_value_get_ulong (value);
359       break;
360     case ARG_FRAGMENT:
361       GST_OSSELEMENT (src)->fragment = g_value_get_int (value);
362       gst_osselement_sync_parms (GST_OSSELEMENT (src));
363       break; 
364     default:
365       break;
366   }
367 }
368
369 static void 
370 gst_osssrc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 
371 {
372   GstOssSrc *src;
373
374   src = GST_OSSSRC (object);
375
376   switch (prop_id) {
377     case ARG_BUFFERSIZE:
378       g_value_set_ulong (value, src->buffersize);
379       break;
380     case ARG_FRAGMENT:
381       g_value_set_int (value, GST_OSSELEMENT (src)->fragment);
382       break;
383     default:
384       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
385       break;
386   }
387 }
388
389 static GstElementStateReturn 
390 gst_osssrc_change_state (GstElement *element) 
391 {
392   GstOssSrc *osssrc = GST_OSSSRC (element);
393   
394   GST_DEBUG ("osssrc: state change");
395
396   switch (GST_STATE_TRANSITION (element)) {
397     case GST_STATE_READY_TO_PAUSED:
398       osssrc->curoffset = 0;
399       break;
400     case GST_STATE_PAUSED_TO_PLAYING:
401       gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssrc->provided_clock), TRUE);
402       break;
403     case GST_STATE_PLAYING_TO_PAUSED:
404       gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssrc->provided_clock), FALSE);
405       break;
406     case GST_STATE_PAUSED_TO_READY:
407       if (GST_FLAG_IS_SET (element, GST_OSSSRC_OPEN))
408         ioctl (GST_OSSELEMENT (osssrc)->fd, SNDCTL_DSP_RESET, 0);
409       break;
410     default:
411       break;
412   }
413
414   if (GST_ELEMENT_CLASS (parent_class)->change_state)
415     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
416   
417   return GST_STATE_SUCCESS;
418 }
419
420 static const GstFormat*
421 gst_osssrc_get_formats (GstPad *pad)
422 {
423   static const GstFormat formats[] = {
424     GST_FORMAT_TIME,
425     GST_FORMAT_DEFAULT,
426     GST_FORMAT_BYTES,
427     0
428   };
429   return formats;
430 }
431
432 static gboolean
433 gst_osssrc_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
434                                      GstFormat *dest_format, gint64 *dest_value)
435 {
436   GstOssSrc *osssrc;
437
438   osssrc = GST_OSSSRC (gst_pad_get_parent (pad));
439
440   return gst_osselement_convert (GST_OSSELEMENT (osssrc), src_format, src_value,
441                                 dest_format, dest_value);
442 }
443
444 static const GstEventMask*
445 gst_osssrc_get_event_masks (GstPad *pad)
446 {
447   static const GstEventMask gst_osssrc_src_event_masks[] = {
448     { GST_EVENT_EOS, 0 },
449     { GST_EVENT_SIZE, 0 },
450     { 0, } 
451   };
452   return gst_osssrc_src_event_masks;
453
454
455 static gboolean
456 gst_osssrc_src_event (GstPad *pad, GstEvent *event)
457 {
458   GstOssSrc *osssrc;
459   gboolean retval = FALSE;
460
461   osssrc = GST_OSSSRC (gst_pad_get_parent (pad));
462
463   switch (GST_EVENT_TYPE (event)) {
464     case GST_EVENT_EOS:
465       osssrc->need_eos = TRUE;
466       retval = TRUE;
467       break;
468     case GST_EVENT_SIZE:
469     {
470       GstFormat format;
471       gint64 value;
472
473       format = GST_FORMAT_BYTES;
474       
475       /* convert to bytes */
476       if (gst_osselement_convert (GST_OSSELEMENT (osssrc), 
477                                   GST_EVENT_SIZE_FORMAT (event), 
478                                   GST_EVENT_SIZE_VALUE (event),
479                                   &format, &value)) 
480       {
481         osssrc->buffersize = GST_EVENT_SIZE_VALUE (event);
482         g_object_notify (G_OBJECT (osssrc), "buffersize");
483         retval = TRUE;
484       }
485     }
486     default:
487       break;
488   }
489   gst_event_unref (event);
490   return retval;
491 }
492
493 static gboolean
494 gst_osssrc_send_event (GstElement *element,
495                        GstEvent *event)
496 {
497   GstOssSrc *osssrc = GST_OSSSRC (element);
498
499   return gst_osssrc_src_event (osssrc->srcpad, event);
500 }
501
502 static const GstQueryType*
503 gst_osssrc_get_query_types (GstPad *pad)
504 {
505   static const GstQueryType query_types[] = {
506     GST_QUERY_POSITION,
507     0,
508   };
509   return query_types;
510
511
512 static gboolean
513 gst_osssrc_src_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value)
514 {
515   gboolean res = FALSE;
516   GstOssSrc *osssrc;
517               
518   osssrc = GST_OSSSRC (gst_pad_get_parent (pad));
519                 
520   switch (type) {
521     case GST_QUERY_POSITION:
522       res = gst_osselement_convert (GST_OSSELEMENT (osssrc), 
523                                     GST_FORMAT_BYTES, osssrc->curoffset,
524                                     format, value); 
525       break;
526     default:
527       break;
528   }
529   return res;
530