more macro splitting; fix po problem
[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/ioctl.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <string.h>
33
34 #ifdef HAVE_OSS_INCLUDE_IN_SYS
35 #include <sys/soundcard.h>
36 #else
37
38 #ifdef HAVE_OSS_INCLUDE_IN_ROOT
39 #include <soundcard.h>
40 #else
41
42 #include <machine/soundcard.h>
43
44 #endif /* HAVE_OSS_INCLUDE_IN_ROOT */
45
46 #endif /* HAVE_OSS_INCLUDE_IN_SYS */
47
48 #include <gstosssrc.h>
49 #include <gstosselement.h>
50 #include <gst/audio/audioclock.h>
51
52 /* elementfactory information */
53 static GstElementDetails gst_oss_src_details =
54 GST_ELEMENT_DETAILS ("Audio Source (OSS)",
55     "Source/Audio",
56     "Read from the sound card",
57     "Erik Walthinsen <omega@cse.ogi.edu>");
58
59
60 /* OssSrc signals and args */
61 enum
62 {
63   /* FILL ME */
64   LAST_SIGNAL
65 };
66
67 enum
68 {
69   ARG_0,
70   ARG_BUFFERSIZE,
71   ARG_FRAGMENT
72 };
73
74 static GstStaticPadTemplate osssrc_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
75     GST_PAD_SRC,
76     GST_PAD_ALWAYS,
77     GST_STATIC_CAPS ("audio/x-raw-int, "
78         "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "
79         "signed = (boolean) { TRUE, FALSE }, "
80         "width = (int) 16, "
81         "depth = (int) { 8, 16 }, "
82         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
83         "audio/x-raw-int, "
84         "signed = (boolean) { TRUE, FALSE }, "
85         "width = (int) 8, "
86         "depth = (int) 8, "
87         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
88     );
89
90 static void gst_oss_src_base_init (gpointer g_class);
91 static void gst_oss_src_class_init (GstOssSrcClass * klass);
92 static void gst_oss_src_init (GstOssSrc * osssrc);
93 static void gst_oss_src_dispose (GObject * object);
94
95 static GstPadLinkReturn gst_oss_src_src_link (GstPad * pad, GstPad * peer);
96 static GstCaps *gst_oss_src_getcaps (GstPad * pad);
97 static const GstFormat *gst_oss_src_get_formats (GstPad * pad);
98 static gboolean gst_oss_src_convert (GstPad * pad,
99     GstFormat src_format, gint64 src_value,
100     GstFormat * dest_format, gint64 * dest_value);
101
102 static void gst_oss_src_set_property (GObject * object, guint prop_id,
103     const GValue * value, GParamSpec * pspec);
104 static void gst_oss_src_get_property (GObject * object, guint prop_id,
105     GValue * value, GParamSpec * pspec);
106 static GstElementStateReturn gst_oss_src_change_state (GstElement * element);
107
108 static void gst_oss_src_set_clock (GstElement * element, GstClock * clock);
109 static GstClock *gst_oss_src_get_clock (GstElement * element);
110 static GstClockTime gst_oss_src_get_time (GstClock * clock, gpointer data);
111
112 static const GstEventMask *gst_oss_src_get_event_masks (GstPad * pad);
113 static gboolean gst_oss_src_src_event (GstPad * pad, GstEvent * event);
114 static gboolean gst_oss_src_send_event (GstElement * element, GstEvent * event);
115 static const GstQueryType *gst_oss_src_get_query_types (GstPad * pad);
116 static gboolean gst_oss_src_src_query (GstPad * pad, GstQueryType type,
117     GstFormat * format, gint64 * value);
118
119 static void gst_oss_src_loop (GstPad * pad);
120
121 static GstElementClass *parent_class = NULL;
122
123 /*static guint gst_oss_src_signals[LAST_SIGNAL] = { 0 }; */
124
125 GType
126 gst_oss_src_get_type (void)
127 {
128   static GType osssrc_type = 0;
129
130   if (!osssrc_type) {
131     static const GTypeInfo osssrc_info = {
132       sizeof (GstOssSrcClass),
133       gst_oss_src_base_init,
134       NULL,
135       (GClassInitFunc) gst_oss_src_class_init,
136       NULL,
137       NULL,
138       sizeof (GstOssSrc),
139       0,
140       (GInstanceInitFunc) gst_oss_src_init,
141     };
142
143     osssrc_type =
144         g_type_register_static (GST_TYPE_OSSELEMENT, "GstOssSrc", &osssrc_info,
145         0);
146   }
147   return osssrc_type;
148 }
149
150 static void
151 gst_oss_src_base_init (gpointer g_class)
152 {
153   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
154
155   gst_element_class_set_details (element_class, &gst_oss_src_details);
156   gst_element_class_add_pad_template (element_class,
157       gst_static_pad_template_get (&osssrc_src_factory));
158 }
159 static void
160 gst_oss_src_class_init (GstOssSrcClass * klass)
161 {
162   GObjectClass *gobject_class;
163   GstElementClass *gstelement_class;
164
165   gobject_class = (GObjectClass *) klass;
166   gstelement_class = (GstElementClass *) klass;
167
168   parent_class = g_type_class_ref (GST_TYPE_OSSELEMENT);
169
170   gobject_class->set_property = gst_oss_src_set_property;
171   gobject_class->get_property = gst_oss_src_get_property;
172
173   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFERSIZE,
174       g_param_spec_ulong ("buffersize", "Buffer Size",
175           "The size of the buffers with samples", 0, G_MAXULONG, 0,
176           G_PARAM_READWRITE));
177   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FRAGMENT,
178       g_param_spec_int ("fragment", "Fragment",
179           "The fragment as 0xMMMMSSSS (MMMM = total fragments, 2^SSSS = fragment size)",
180           0, G_MAXINT, 6, G_PARAM_READWRITE));
181
182   gobject_class->dispose = gst_oss_src_dispose;
183
184   gstelement_class->change_state = gst_oss_src_change_state;
185   gstelement_class->send_event = gst_oss_src_send_event;
186
187   gstelement_class->set_clock = gst_oss_src_set_clock;
188   gstelement_class->get_clock = gst_oss_src_get_clock;
189 }
190
191 static void
192 gst_oss_src_init (GstOssSrc * osssrc)
193 {
194   osssrc->srcpad =
195       gst_pad_new_from_template (gst_static_pad_template_get
196       (&osssrc_src_factory), "src");
197   gst_pad_set_loop_function (osssrc->srcpad, gst_oss_src_loop);
198   gst_pad_set_getcaps_function (osssrc->srcpad, gst_oss_src_getcaps);
199   gst_pad_set_link_function (osssrc->srcpad, gst_oss_src_src_link);
200   gst_pad_set_convert_function (osssrc->srcpad, gst_oss_src_convert);
201   gst_pad_set_formats_function (osssrc->srcpad, gst_oss_src_get_formats);
202   gst_pad_set_event_function (osssrc->srcpad, gst_oss_src_src_event);
203   gst_pad_set_event_mask_function (osssrc->srcpad, gst_oss_src_get_event_masks);
204   gst_pad_set_query_function (osssrc->srcpad, gst_oss_src_src_query);
205   gst_pad_set_query_type_function (osssrc->srcpad, gst_oss_src_get_query_types);
206
207   gst_element_add_pad (GST_ELEMENT (osssrc), osssrc->srcpad);
208
209   osssrc->buffersize = 4096;
210   osssrc->curoffset = 0;
211
212   osssrc->provided_clock =
213       gst_audio_clock_new ("ossclock", gst_oss_src_get_time, osssrc);
214   gst_object_set_parent (GST_OBJECT (osssrc->provided_clock),
215       GST_OBJECT (osssrc));
216
217   osssrc->clock = NULL;
218 }
219
220 static void
221 gst_oss_src_dispose (GObject * object)
222 {
223   GstOssSrc *osssrc = (GstOssSrc *) object;
224
225   if (osssrc->provided_clock != NULL) {
226     gst_object_unparent (GST_OBJECT (osssrc->provided_clock));
227     osssrc->provided_clock = NULL;
228   }
229
230   G_OBJECT_CLASS (parent_class)->dispose (object);
231 }
232
233 static GstCaps *
234 gst_oss_src_getcaps (GstPad * pad)
235 {
236   GstOssSrc *src;
237   GstCaps *caps;
238
239   src = GST_OSSSRC (GST_PAD_PARENT (pad));
240
241   gst_osselement_probe_caps (GST_OSSELEMENT (src));
242
243   if (GST_OSSELEMENT (src)->probed_caps == NULL) {
244     caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
245   } else {
246     caps = gst_caps_copy (GST_OSSELEMENT (src)->probed_caps);
247   }
248
249   return caps;
250 }
251
252 static GstPadLinkReturn
253 gst_oss_src_src_link (GstPad * pad, GstPad * peer)
254 {
255   return GST_RPAD_LINKFUNC (peer) (peer, pad);
256 }
257
258 static gboolean
259 gst_oss_src_negotiate (GstPad * pad)
260 {
261   GstOssSrc *src;
262   GstCaps *allowed;
263
264   src = GST_OSSSRC (GST_PAD_PARENT (pad));
265
266   //allowed = gst_pad_get_allowed_caps (pad);
267   allowed = NULL;
268
269   if (!gst_osselement_merge_fixed_caps (GST_OSSELEMENT (src), allowed))
270     return FALSE;
271
272   if (!gst_osselement_sync_parms (GST_OSSELEMENT (src)))
273     return FALSE;
274
275   /* set caps on src pad */
276   GST_PAD_CAPS (src->srcpad) =
277       gst_caps_new_simple ("audio/x-raw-int",
278       "endianness", G_TYPE_INT, GST_OSSELEMENT (src)->endianness,
279       "signed", G_TYPE_BOOLEAN, GST_OSSELEMENT (src)->sign,
280       "width", G_TYPE_INT, GST_OSSELEMENT (src)->width,
281       "depth", G_TYPE_INT, GST_OSSELEMENT (src)->depth,
282       "rate", G_TYPE_INT, GST_OSSELEMENT (src)->rate,
283       "channels", G_TYPE_INT, GST_OSSELEMENT (src)->channels, NULL);
284
285   return TRUE;
286 }
287
288 static GstClockTime
289 gst_oss_src_get_time (GstClock * clock, gpointer data)
290 {
291   GstOssSrc *osssrc = GST_OSSSRC (data);
292   audio_buf_info info;
293
294   if (!GST_OSSELEMENT (osssrc)->bps)
295     return 0;
296
297   if (ioctl (GST_OSSELEMENT (osssrc)->fd, SNDCTL_DSP_GETISPACE, &info) < 0)
298     return 0;
299
300   return (osssrc->curoffset * GST_OSSELEMENT (osssrc)->sample_width +
301       info.bytes) * GST_SECOND / GST_OSSELEMENT (osssrc)->bps;
302 }
303
304 static GstClock *
305 gst_oss_src_get_clock (GstElement * element)
306 {
307   GstOssSrc *osssrc;
308
309   osssrc = GST_OSSSRC (element);
310
311   return GST_CLOCK (osssrc->provided_clock);
312 }
313
314 static void
315 gst_oss_src_set_clock (GstElement * element, GstClock * clock)
316 {
317   GstOssSrc *osssrc;
318
319   osssrc = GST_OSSSRC (element);
320
321   osssrc->clock = clock;
322 }
323
324 static void
325 gst_oss_src_loop (GstPad * pad)
326 {
327   GstOssSrc *src;
328   GstBuffer *buf;
329   glong readbytes;
330   glong readsamples;
331
332   src = GST_OSSSRC (GST_PAD_PARENT (pad));
333
334   GST_DEBUG ("attempting to read something from the soundcard");
335
336   if (src->need_eos) {
337     src->need_eos = FALSE;
338     gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
339     return;
340   }
341
342   buf = gst_buffer_new_and_alloc (src->buffersize);
343
344   if (!GST_PAD_CAPS (pad)) {
345     /* nothing was negotiated, we can decide on a format */
346     if (!gst_oss_src_negotiate (pad)) {
347       gst_buffer_unref (buf);
348       GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL), (NULL));
349       return;
350     }
351   }
352   if (GST_OSSELEMENT (src)->bps == 0) {
353     gst_buffer_unref (buf);
354     GST_ELEMENT_ERROR (src, CORE, NEGOTIATION, (NULL),
355         ("format wasn't negotiated before chain function"));
356     return;
357   }
358
359   readbytes = read (GST_OSSELEMENT (src)->fd, GST_BUFFER_DATA (buf),
360       src->buffersize);
361   if (readbytes < 0) {
362     gst_buffer_unref (buf);
363     GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
364     return;
365   }
366
367   if (readbytes == 0) {
368     gst_buffer_unref (buf);
369     gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
370     return;
371   }
372
373   readsamples = readbytes * GST_OSSELEMENT (src)->rate /
374       GST_OSSELEMENT (src)->bps;
375
376   GST_BUFFER_SIZE (buf) = readbytes;
377   GST_BUFFER_OFFSET (buf) = src->curoffset;
378   GST_BUFFER_OFFSET_END (buf) = src->curoffset + readsamples;
379   GST_BUFFER_DURATION (buf) =
380       readsamples * GST_SECOND / GST_OSSELEMENT (src)->rate;
381
382   /* if we have a clock */
383   if (src->clock) {
384     if (src->clock == src->provided_clock) {
385       /* if it's our own clock, we can be very accurate */
386       GST_BUFFER_TIMESTAMP (buf) =
387           src->curoffset * GST_SECOND / GST_OSSELEMENT (src)->rate;
388     } else {
389       /* somebody elses clock, timestamp with that clock, no discontinuity in
390        * the stream since the OFFSET is updated correctly. Elements can stretch
391        * to match timestamps */
392       GST_BUFFER_TIMESTAMP (buf) =
393           gst_element_get_time (GST_ELEMENT (src)) - GST_BUFFER_DURATION (buf);
394     }
395   } else {
396     /* no clock, no timestamp */
397     GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
398   }
399
400   src->curoffset += readsamples;
401
402   GST_DEBUG ("pushed buffer from soundcard of %ld bytes, timestamp %"
403       G_GINT64_FORMAT, readbytes, GST_BUFFER_TIMESTAMP (buf));
404
405   gst_pad_push (pad, buf);
406
407   return;
408 }
409
410 static void
411 gst_oss_src_set_property (GObject * object, guint prop_id, const GValue * value,
412     GParamSpec * pspec)
413 {
414   GstOssSrc *src;
415
416   src = GST_OSSSRC (object);
417
418   switch (prop_id) {
419     case ARG_BUFFERSIZE:
420       src->buffersize = g_value_get_ulong (value);
421       break;
422     case ARG_FRAGMENT:
423       GST_OSSELEMENT (src)->fragment = g_value_get_int (value);
424       gst_osselement_sync_parms (GST_OSSELEMENT (src));
425       break;
426     default:
427       break;
428   }
429 }
430
431 static void
432 gst_oss_src_get_property (GObject * object, guint prop_id, GValue * value,
433     GParamSpec * pspec)
434 {
435   GstOssSrc *src;
436
437   src = GST_OSSSRC (object);
438
439   switch (prop_id) {
440     case ARG_BUFFERSIZE:
441       g_value_set_ulong (value, src->buffersize);
442       break;
443     case ARG_FRAGMENT:
444       g_value_set_int (value, GST_OSSELEMENT (src)->fragment);
445       break;
446     default:
447       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
448       break;
449   }
450 }
451
452 static GstElementStateReturn
453 gst_oss_src_change_state (GstElement * element)
454 {
455   GstOssSrc *osssrc = GST_OSSSRC (element);
456
457   GST_DEBUG ("osssrc: state change");
458
459   switch (GST_STATE_TRANSITION (element)) {
460     case GST_STATE_READY_TO_PAUSED:
461       osssrc->curoffset = 0;
462       break;
463     case GST_STATE_PAUSED_TO_PLAYING:
464       gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssrc->provided_clock),
465           TRUE);
466       break;
467     case GST_STATE_PLAYING_TO_PAUSED:
468       gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssrc->provided_clock),
469           FALSE);
470       break;
471     case GST_STATE_PAUSED_TO_READY:
472       if (GST_FLAG_IS_SET (element, GST_OSSSRC_OPEN))
473         ioctl (GST_OSSELEMENT (osssrc)->fd, SNDCTL_DSP_RESET, 0);
474       break;
475     default:
476       break;
477   }
478
479   if (GST_ELEMENT_CLASS (parent_class)->change_state)
480     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
481
482   return GST_STATE_SUCCESS;
483 }
484
485 static const GstFormat *
486 gst_oss_src_get_formats (GstPad * pad)
487 {
488   static const GstFormat formats[] = {
489     GST_FORMAT_TIME,
490     GST_FORMAT_DEFAULT,
491     GST_FORMAT_BYTES,
492     0
493   };
494
495   return formats;
496 }
497
498 static gboolean
499 gst_oss_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
500     GstFormat * dest_format, gint64 * dest_value)
501 {
502   GstOssSrc *osssrc;
503
504   osssrc = GST_OSSSRC (GST_PAD_PARENT (pad));
505
506   return gst_osselement_convert (GST_OSSELEMENT (osssrc), src_format, src_value,
507       dest_format, dest_value);
508 }
509
510 static const GstEventMask *
511 gst_oss_src_get_event_masks (GstPad * pad)
512 {
513   static const GstEventMask gst_oss_src_src_event_masks[] = {
514     {GST_EVENT_EOS, 0},
515     {GST_EVENT_SIZE, 0},
516     {0,}
517   };
518
519   return gst_oss_src_src_event_masks;
520 }
521
522 static gboolean
523 gst_oss_src_src_event (GstPad * pad, GstEvent * event)
524 {
525   GstOssSrc *osssrc;
526   gboolean retval = FALSE;
527
528   osssrc = GST_OSSSRC (GST_PAD_PARENT (pad));
529
530   switch (GST_EVENT_TYPE (event)) {
531     case GST_EVENT_EOS:
532       osssrc->need_eos = TRUE;
533       retval = TRUE;
534       break;
535     case GST_EVENT_SIZE:
536     {
537       GstFormat format;
538       gint64 value;
539
540       format = GST_FORMAT_BYTES;
541
542       /* convert to bytes */
543       if (gst_osselement_convert (GST_OSSELEMENT (osssrc),
544               GST_EVENT_SIZE_FORMAT (event),
545               GST_EVENT_SIZE_VALUE (event), &format, &value)) {
546         osssrc->buffersize = GST_EVENT_SIZE_VALUE (event);
547         g_object_notify (G_OBJECT (osssrc), "buffersize");
548         retval = TRUE;
549       }
550     }
551     default:
552       break;
553   }
554   gst_event_unref (event);
555   return retval;
556 }
557
558 static gboolean
559 gst_oss_src_send_event (GstElement * element, GstEvent * event)
560 {
561   GstOssSrc *osssrc = GST_OSSSRC (element);
562
563   return gst_oss_src_src_event (osssrc->srcpad, event);
564 }
565
566 static const GstQueryType *
567 gst_oss_src_get_query_types (GstPad * pad)
568 {
569   static const GstQueryType query_types[] = {
570     GST_QUERY_POSITION,
571     0,
572   };
573
574   return query_types;
575 }
576
577 static gboolean
578 gst_oss_src_src_query (GstPad * pad, GstQueryType type, GstFormat * format,
579     gint64 * value)
580 {
581   gboolean res = FALSE;
582   GstOssSrc *osssrc;
583
584   osssrc = GST_OSSSRC (GST_PAD_PARENT (pad));
585
586   switch (type) {
587     case GST_QUERY_POSITION:
588       res = gst_osselement_convert (GST_OSSELEMENT (osssrc),
589           GST_FORMAT_DEFAULT, osssrc->curoffset, format, value);
590       break;
591     default:
592       break;
593   }
594   return res;
595 }