Final round of doc updates.
[platform/upstream/gst-plugins-good.git] / sys / oss4 / oss4-source.c
1 /* GStreamer OSS4 audio source
2  * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-oss4src
22  *
23  * This element lets you record sound using the Open Sound System (OSS)
24  * version 4.
25  * 
26  * <refsect2>
27  * <title>Example pipelines</title>
28  * |[
29  * gst-launch -v oss4src ! queue ! audioconvert ! vorbisenc ! oggmux ! filesink location=mymusic.ogg
30  * ]| will record sound from your sound card using OSS4 and encode it to an
31  * Ogg/Vorbis file (this will only work if your mixer settings are right
32  * and the right inputs areenabled etc.)
33  * </refsect2>
34  *
35  * Since: 0.10.7
36  */
37
38 /* FIXME: make sure we're not doing ioctls from the app thread (e.g. via the
39  * mixer interface) while recording */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <unistd.h>
51 #include <string.h>
52
53 #include <gst/interfaces/mixer.h>
54 #include <gst/gst-i18n-plugin.h>
55
56 #define NO_LEGACY_MIXER
57 #include "oss4-audio.h"
58 #include "oss4-source.h"
59 #include "oss4-property-probe.h"
60 #include "oss4-soundcard.h"
61
62 #define GST_OSS4_SOURCE_IS_OPEN(src)  (GST_OSS4_SOURCE(src)->fd != -1)
63
64 GST_DEBUG_CATEGORY_EXTERN (oss4src_debug);
65 #define GST_CAT_DEFAULT oss4src_debug
66
67 #define DEFAULT_DEVICE       NULL
68 #define DEFAULT_DEVICE_NAME  NULL
69
70 enum
71 {
72   PROP_0,
73   PROP_DEVICE,
74   PROP_DEVICE_NAME
75 };
76
77 static void gst_oss4_source_init_interfaces (GType type);
78
79 GST_BOILERPLATE_FULL (GstOss4Source, gst_oss4_source, GstAudioSrc,
80     GST_TYPE_AUDIO_SRC, gst_oss4_source_init_interfaces);
81
82 static void gst_oss4_source_get_property (GObject * object, guint prop_id,
83     GValue * value, GParamSpec * pspec);
84 static void gst_oss4_source_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86
87 static void gst_oss4_source_dispose (GObject * object);
88 static void gst_oss4_source_finalize (GstOss4Source * osssrc);
89
90 static GstCaps *gst_oss4_source_getcaps (GstBaseSrc * bsrc);
91
92 static gboolean gst_oss4_source_open (GstAudioSrc * asrc,
93     gboolean silent_errors);
94 static gboolean gst_oss4_source_open_func (GstAudioSrc * asrc);
95 static gboolean gst_oss4_source_close (GstAudioSrc * asrc);
96 static gboolean gst_oss4_source_prepare (GstAudioSrc * asrc,
97     GstRingBufferSpec * spec);
98 static gboolean gst_oss4_source_unprepare (GstAudioSrc * asrc);
99 static guint gst_oss4_source_read (GstAudioSrc * asrc, gpointer data,
100     guint length);
101 static guint gst_oss4_source_delay (GstAudioSrc * asrc);
102 static void gst_oss4_source_reset (GstAudioSrc * asrc);
103
104 static void
105 gst_oss4_source_base_init (gpointer g_class)
106 {
107   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
108   GstPadTemplate *templ;
109
110   gst_element_class_set_details_simple (element_class,
111       "OSS v4 Audio Source", "Source/Audio",
112       "Capture from a sound card via OSS version 4",
113       "Tim-Philipp Müller <tim centricular net>");
114
115   templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
116       gst_oss4_audio_get_template_caps ());
117   gst_element_class_add_pad_template (element_class, templ);
118 }
119 static void
120 gst_oss4_source_class_init (GstOss4SourceClass * klass)
121 {
122   GObjectClass *gobject_class;
123   GstElementClass *gstelement_class;
124   GstBaseSrcClass *gstbasesrc_class;
125   GstBaseAudioSrcClass *gstbaseaudiosrc_class;
126   GstAudioSrcClass *gstaudiosrc_class;
127
128   gobject_class = (GObjectClass *) klass;
129   gstelement_class = (GstElementClass *) klass;
130   gstbasesrc_class = (GstBaseSrcClass *) klass;
131   gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
132   gstaudiosrc_class = (GstAudioSrcClass *) klass;
133
134   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_oss4_source_dispose);
135   gobject_class->finalize =
136       (GObjectFinalizeFunc) GST_DEBUG_FUNCPTR (gst_oss4_source_finalize);
137   gobject_class->get_property =
138       GST_DEBUG_FUNCPTR (gst_oss4_source_get_property);
139   gobject_class->set_property =
140       GST_DEBUG_FUNCPTR (gst_oss4_source_set_property);
141
142   gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_source_getcaps);
143
144   gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_oss4_source_open_func);
145   gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_oss4_source_prepare);
146   gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss4_source_unprepare);
147   gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_oss4_source_close);
148   gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_oss4_source_read);
149   gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_oss4_source_delay);
150   gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_oss4_source_reset);
151
152   g_object_class_install_property (gobject_class, PROP_DEVICE,
153       g_param_spec_string ("device", "Device",
154           "OSS4 device (e.g. /dev/oss/hdaudio0/pcm0 or /dev/dspN) "
155           "(NULL = use first available device)",
156           DEFAULT_DEVICE, G_PARAM_READWRITE));
157
158   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
159       g_param_spec_string ("device-name", "Device name",
160           "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
161           G_PARAM_READABLE));
162 }
163
164 static void
165 gst_oss4_source_init (GstOss4Source * osssrc, GstOss4SourceClass * g_class)
166 {
167   const gchar *device;
168
169   device = g_getenv ("AUDIODEV");
170   if (device == NULL)
171     device = DEFAULT_DEVICE;
172
173   osssrc->fd = -1;
174   osssrc->device = g_strdup (device);
175   osssrc->device_name = g_strdup (DEFAULT_DEVICE_NAME);
176   osssrc->device_name = NULL;
177 }
178
179 static void
180 gst_oss4_source_finalize (GstOss4Source * oss)
181 {
182   g_free (oss->device);
183   oss->device = NULL;
184
185   g_list_free (oss->property_probe_list);
186   oss->property_probe_list = NULL;
187
188   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (oss));
189 }
190
191 static void
192 gst_oss4_source_dispose (GObject * object)
193 {
194   GstOss4Source *oss = GST_OSS4_SOURCE (object);
195
196   if (oss->probed_caps) {
197     gst_caps_unref (oss->probed_caps);
198     oss->probed_caps = NULL;
199   }
200
201   G_OBJECT_CLASS (parent_class)->dispose (object);
202 }
203
204 static void
205 gst_oss4_source_set_property (GObject * object, guint prop_id,
206     const GValue * value, GParamSpec * pspec)
207 {
208   GstOss4Source *oss;
209
210   oss = GST_OSS4_SOURCE (object);
211
212   switch (prop_id) {
213     case PROP_DEVICE:
214       GST_OBJECT_LOCK (oss);
215       if (oss->fd == -1) {
216         g_free (oss->device);
217         oss->device = g_value_dup_string (value);
218         g_free (oss->device_name);
219         oss->device_name = NULL;
220       } else {
221         g_warning ("%s: can't change \"device\" property while audio source "
222             "is open", GST_OBJECT_NAME (oss));
223       }
224       GST_OBJECT_UNLOCK (oss);
225       break;
226     default:
227       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228       break;
229   }
230 }
231
232 static void
233 gst_oss4_source_get_property (GObject * object, guint prop_id,
234     GValue * value, GParamSpec * pspec)
235 {
236   GstOss4Source *oss;
237
238   oss = GST_OSS4_SOURCE (object);
239
240   switch (prop_id) {
241     case PROP_DEVICE:
242       GST_OBJECT_LOCK (oss);
243       g_value_set_string (value, oss->device);
244       GST_OBJECT_UNLOCK (oss);
245       break;
246     case PROP_DEVICE_NAME:
247       GST_OBJECT_LOCK (oss);
248       /* If device is set, try to retrieve the name even if we're not open */
249       if (oss->fd == -1 && oss->device != NULL) {
250         if (gst_oss4_source_open (GST_AUDIO_SRC (oss), TRUE)) {
251           g_value_set_string (value, oss->device_name);
252           gst_oss4_source_close (GST_AUDIO_SRC (oss));
253         } else {
254           gchar *name = NULL;
255
256           gst_oss4_property_probe_find_device_name_nofd (GST_OBJECT (oss),
257               oss->device, &name);
258           g_value_set_string (value, name);
259           g_free (name);
260         }
261       } else {
262         g_value_set_string (value, oss->device_name);
263       }
264
265       GST_OBJECT_UNLOCK (oss);
266       break;
267     default:
268       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269       break;
270   }
271 }
272
273 static GstCaps *
274 gst_oss4_source_getcaps (GstBaseSrc * bsrc)
275 {
276   GstOss4Source *oss;
277   GstCaps *caps;
278
279   oss = GST_OSS4_SOURCE (bsrc);
280
281   if (oss->fd == -1) {
282     caps = gst_caps_copy (gst_oss4_audio_get_template_caps ());
283   } else if (oss->probed_caps) {
284     caps = gst_caps_copy (oss->probed_caps);
285   } else {
286     caps = gst_oss4_audio_probe_caps (GST_OBJECT (oss), oss->fd);
287     if (caps != NULL && !gst_caps_is_empty (caps)) {
288       oss->probed_caps = gst_caps_copy (caps);
289     }
290   }
291
292   return caps;
293 }
294
295 /* note: we must not take the object lock here unless we fix up get_property */
296 static gboolean
297 gst_oss4_source_open (GstAudioSrc * asrc, gboolean silent_errors)
298 {
299   GstOss4Source *oss;
300   gchar *device;
301   int mode;
302
303   oss = GST_OSS4_SOURCE (asrc);
304
305   if (oss->device)
306     device = g_strdup (oss->device);
307   else
308     device = gst_oss4_audio_find_device (GST_OBJECT_CAST (oss));
309
310   /* desperate times, desperate measures */
311   if (device == NULL)
312     device = g_strdup ("/dev/dsp0");
313
314   GST_INFO_OBJECT (oss, "Trying to open OSS4 device '%s'", device);
315
316   /* we open in non-blocking mode even if we don't really want to do writes
317    * non-blocking because we can't be sure that this is really a genuine
318    * OSS4 device with well-behaved drivers etc. We really don't want to
319    * hang forever under any circumstances. */
320   oss->fd = open (device, O_RDONLY | O_NONBLOCK, 0);
321   if (oss->fd == -1) {
322     switch (errno) {
323       case EBUSY:
324         goto busy;
325       case EACCES:
326         goto no_permission;
327       default:
328         goto open_failed;
329     }
330   }
331
332   GST_INFO_OBJECT (oss, "Opened device");
333
334   /* Make sure it's OSS4. If it's old OSS, let osssink handle it */
335   if (!gst_oss4_audio_check_version (GST_OBJECT_CAST (oss), oss->fd))
336     goto legacy_oss;
337
338   /* now remove the non-blocking flag. */
339   mode = fcntl (oss->fd, F_GETFL);
340   mode &= ~O_NONBLOCK;
341   if (fcntl (oss->fd, F_SETFL, mode) < 0) {
342     /* some drivers do no support unsetting the non-blocking flag, try to
343      * close/open the device then. This is racy but we error out properly. */
344     GST_WARNING_OBJECT (oss, "failed to unset O_NONBLOCK (buggy driver?), "
345         "will try to re-open device now");
346     gst_oss4_source_close (asrc);
347     if ((oss->fd = open (device, O_RDONLY, 0)) == -1)
348       goto non_block;
349   }
350
351   oss->open_device = device;
352
353   /* not using ENGINEINFO here because it sometimes returns a different and
354    * less useful name than AUDIOINFO for the same device */
355   if (!gst_oss4_property_probe_find_device_name (GST_OBJECT (oss), oss->fd,
356           oss->open_device, &oss->device_name)) {
357     oss->device_name = NULL;
358   }
359
360   return TRUE;
361
362   /* ERRORS */
363 busy:
364   {
365     if (!silent_errors) {
366       GST_ELEMENT_ERROR (oss, RESOURCE, BUSY,
367           (_("Could not open audio device for playback. "
368                   "Device is being used by another application.")), (NULL));
369     }
370     g_free (device);
371     return FALSE;
372   }
373 no_permission:
374   {
375     if (!silent_errors) {
376       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
377           (_("Could not open audio device for playback."
378                   "You don't have permission to open the device.")),
379           GST_ERROR_SYSTEM);
380     }
381     g_free (device);
382     return FALSE;
383   }
384 open_failed:
385   {
386     if (!silent_errors) {
387       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
388           (_("Could not open audio device for playback.")), GST_ERROR_SYSTEM);
389     }
390     g_free (device);
391     return FALSE;
392   }
393 legacy_oss:
394   {
395     gst_oss4_source_close (asrc);
396     if (!silent_errors) {
397       GST_ELEMENT_ERROR (oss, RESOURCE, OPEN_READ,
398           (_("Could not open audio device for playback."
399                   "This version of the Open Sound System is not supported by this "
400                   "element.")), ("Try the 'osssink' element instead"));
401     }
402     g_free (device);
403     return FALSE;
404   }
405 non_block:
406   {
407     if (!silent_errors) {
408       GST_ELEMENT_ERROR (oss, RESOURCE, SETTINGS, (NULL),
409           ("Unable to set device %s into non-blocking mode: %s",
410               oss->device, g_strerror (errno)));
411     }
412     g_free (device);
413     return FALSE;
414   }
415 }
416
417 static gboolean
418 gst_oss4_source_open_func (GstAudioSrc * asrc)
419 {
420   return gst_oss4_source_open (asrc, FALSE);
421 }
422
423 static void
424 gst_oss4_source_free_mixer_tracks (GstOss4Source * oss)
425 {
426   g_list_foreach (oss->tracks, (GFunc) g_object_unref, NULL);
427   g_list_free (oss->tracks);
428   oss->tracks = NULL;
429 }
430
431 static gboolean
432 gst_oss4_source_close (GstAudioSrc * asrc)
433 {
434   GstOss4Source *oss;
435
436   oss = GST_OSS4_SOURCE (asrc);
437
438   if (oss->fd != -1) {
439     GST_DEBUG_OBJECT (oss, "closing device");
440     close (oss->fd);
441     oss->fd = -1;
442   }
443
444   oss->bytes_per_sample = 0;
445
446   gst_caps_replace (&oss->probed_caps, NULL);
447
448   g_free (oss->open_device);
449   oss->open_device = NULL;
450
451   g_free (oss->device_name);
452   oss->device_name = NULL;
453
454   gst_oss4_source_free_mixer_tracks (oss);
455
456   return TRUE;
457 }
458
459 static gboolean
460 gst_oss4_source_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
461 {
462   GstOss4Source *oss;
463
464   oss = GST_OSS4_SOURCE (asrc);
465
466   if (!gst_oss4_audio_set_format (GST_OBJECT_CAST (oss), oss->fd, spec)) {
467     GST_WARNING_OBJECT (oss, "Couldn't set requested format %" GST_PTR_FORMAT,
468         spec->caps);
469     return FALSE;
470   }
471
472   oss->bytes_per_sample = spec->bytes_per_sample;
473   return TRUE;
474 }
475
476 static gboolean
477 gst_oss4_source_unprepare (GstAudioSrc * asrc)
478 {
479   /* could do a SNDCTL_DSP_HALT, but the OSS manual recommends a close/open,
480    * since HALT won't properly reset some devices, apparently */
481
482   if (!gst_oss4_source_close (asrc))
483     goto couldnt_close;
484
485   if (!gst_oss4_source_open_func (asrc))
486     goto couldnt_reopen;
487
488   return TRUE;
489
490   /* ERRORS */
491 couldnt_close:
492   {
493     GST_DEBUG_OBJECT (asrc, "Couldn't close the audio device");
494     return FALSE;
495   }
496 couldnt_reopen:
497   {
498     GST_DEBUG_OBJECT (asrc, "Couldn't reopen the audio device");
499     return FALSE;
500   }
501 }
502
503 static guint
504 gst_oss4_source_read (GstAudioSrc * asrc, gpointer data, guint length)
505 {
506   GstOss4Source *oss;
507   int n;
508
509   oss = GST_OSS4_SOURCE_CAST (asrc);
510
511   n = read (oss->fd, data, length);
512   GST_LOG_OBJECT (asrc, "%u bytes, %u samples", n, n / oss->bytes_per_sample);
513
514   if (G_UNLIKELY (n < 0)) {
515     switch (errno) {
516       case ENOTSUP:
517       case EACCES:{
518         /* This is the most likely cause, I think */
519         GST_ELEMENT_ERROR (asrc, RESOURCE, READ,
520             (_("Recording is not supported by this audio device.")),
521             ("read: %s (device: %s) (maybe this is an output-only device?)",
522                 g_strerror (errno), oss->open_device));
523         break;
524       }
525       default:{
526         GST_ELEMENT_ERROR (asrc, RESOURCE, READ,
527             (_("Error recording from audio device.")),
528             ("read: %s (device: %s)", g_strerror (errno), oss->open_device));
529         break;
530       }
531     }
532   }
533
534   return (guint) n;
535 }
536
537 static guint
538 gst_oss4_source_delay (GstAudioSrc * asrc)
539 {
540   audio_buf_info info = { 0, };
541   GstOss4Source *oss;
542   guint delay;
543
544   oss = GST_OSS4_SOURCE_CAST (asrc);
545
546   if (ioctl (oss->fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
547     GST_LOG_OBJECT (oss, "GETISPACE failed: %s", g_strerror (errno));
548     return 0;
549   }
550
551   delay = (info.fragstotal * info.fragsize) - info.bytes;
552   GST_LOG_OBJECT (oss, "fragstotal:%d, fragsize:%d, bytes:%d, delay:%d",
553       info.fragstotal, info.fragsize, info.bytes, delay);
554   return delay;
555 }
556
557 static void
558 gst_oss4_source_reset (GstAudioSrc * asrc)
559 {
560   /* There's nothing we can do here really: OSS can't handle access to the
561    * same device/fd from multiple threads and might deadlock or blow up in
562    * other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
563 }
564
565 /* GstMixer interface, which we abuse here for input selection, because we
566  * don't have a proper interface for that and because that's what
567  * gnome-sound-recorder does. */
568
569 /* GstMixerTrack is a plain GObject, so let's just use the GLib macro here */
570 G_DEFINE_TYPE (GstOss4SourceInput, gst_oss4_source_input, GST_TYPE_MIXER_TRACK);
571
572 static void
573 gst_oss4_source_input_class_init (GstOss4SourceInputClass * klass)
574 {
575   /* nothing to do here */
576 }
577
578 static void
579 gst_oss4_source_input_init (GstOss4SourceInput * i)
580 {
581   /* nothing to do here */
582 }
583
584 #if 0
585
586 static void
587 gst_ossmixer_ensure_track_list (GstOssMixer * mixer)
588 {
589   gint i, master = -1;
590
591   g_return_if_fail (mixer->fd != -1);
592
593   if (mixer->tracklist)
594     return;
595
596   /* find master volume */
597   if (mixer->devmask & SOUND_MASK_VOLUME)
598     master = SOUND_MIXER_VOLUME;
599   else if (mixer->devmask & SOUND_MASK_PCM)
600     master = SOUND_MIXER_PCM;
601   else if (mixer->devmask & SOUND_MASK_SPEAKER)
602     master = SOUND_MIXER_SPEAKER;       /* doubtful... */
603   /* else: no master, so we won't set any */
604
605   /* build track list */
606   for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
607     if (mixer->devmask & (1 << i)) {
608       GstMixerTrack *track;
609       gboolean input = FALSE, stereo = FALSE, record = FALSE;
610
611       /* track exists, make up capabilities */
612       if (MASK_BIT_IS_SET (mixer->stereomask, i))
613         stereo = TRUE;
614       if (MASK_BIT_IS_SET (mixer->recmask, i))
615         input = TRUE;
616       if (MASK_BIT_IS_SET (mixer->recdevs, i))
617         record = TRUE;
618
619       /* do we want mixer in our list? */
620       if (!((mixer->dir & GST_OSS_MIXER_CAPTURE && input == TRUE) ||
621               (mixer->dir & GST_OSS_MIXER_PLAYBACK && i != SOUND_MIXER_PCM)))
622         /* the PLAYBACK case seems hacky, but that's how 0.8 had it */
623         continue;
624
625       /* add track to list */
626       track = gst_ossmixer_track_new (mixer->fd, i, stereo ? 2 : 1,
627           (record ? GST_MIXER_TRACK_RECORD : 0) |
628           (input ? GST_MIXER_TRACK_INPUT :
629               GST_MIXER_TRACK_OUTPUT) |
630           ((master != i) ? 0 : GST_MIXER_TRACK_MASTER));
631       mixer->tracklist = g_list_append (mixer->tracklist, track);
632     }
633   }
634 }
635
636 /* unused with G_DISABLE_* */
637 static G_GNUC_UNUSED gboolean
638 gst_ossmixer_contains_track (GstOssMixer * mixer, GstOssMixerTrack * osstrack)
639 {
640   const GList *item;
641
642   for (item = mixer->tracklist; item != NULL; item = item->next)
643     if (item->data == osstrack)
644       return TRUE;
645
646   return FALSE;
647 }
648
649 const GList *
650 gst_ossmixer_list_tracks (GstOssMixer * mixer)
651 {
652   gst_ossmixer_ensure_track_list (mixer);
653
654   return (const GList *) mixer->tracklist;
655 }
656
657 void
658 gst_ossmixer_get_volume (GstOssMixer * mixer,
659     GstMixerTrack * track, gint * volumes)
660 {
661   gint volume;
662   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
663
664   g_return_if_fail (mixer->fd != -1);
665   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
666
667   if (track->flags & GST_MIXER_TRACK_MUTE) {
668     volumes[0] = osstrack->lvol;
669     if (track->num_channels == 2) {
670       volumes[1] = osstrack->rvol;
671     }
672   } else {
673     /* get */
674     if (ioctl (mixer->fd, MIXER_READ (osstrack->track_num), &volume) < 0) {
675       g_warning ("Error getting recording device (%d) volume: %s",
676           osstrack->track_num, g_strerror (errno));
677       volume = 0;
678     }
679
680     osstrack->lvol = volumes[0] = (volume & 0xff);
681     if (track->num_channels == 2) {
682       osstrack->rvol = volumes[1] = ((volume >> 8) & 0xff);
683     }
684   }
685 }
686
687 void
688 gst_ossmixer_set_mute (GstOssMixer * mixer, GstMixerTrack * track,
689     gboolean mute)
690 {
691   int volume;
692   GstOssMixerTrack *osstrack = GST_OSSMIXER_TRACK (track);
693
694   g_return_if_fail (mixer->fd != -1);
695   g_return_if_fail (gst_ossmixer_contains_track (mixer, osstrack));
696
697   if (mute) {
698     volume = 0;
699   } else {
700     volume = (osstrack->lvol & 0xff);
701     if (MASK_BIT_IS_SET (mixer->stereomask, osstrack->track_num)) {
702       volume |= ((osstrack->rvol & 0xff) << 8);
703     }
704   }
705
706   if (ioctl (mixer->fd, MIXER_WRITE (osstrack->track_num), &volume) < 0) {
707     g_warning ("Error setting mixer recording device volume (0x%x): %s",
708         volume, g_strerror (errno));
709     return;
710   }
711
712   if (mute) {
713     track->flags |= GST_MIXER_TRACK_MUTE;
714   } else {
715     track->flags &= ~GST_MIXER_TRACK_MUTE;
716   }
717 }
718 #endif
719
720 static gint
721 gst_oss4_source_mixer_get_current_input (GstOss4Source * oss)
722 {
723   int cur = -1;
724
725   if (ioctl (oss->fd, SNDCTL_DSP_GET_RECSRC, &cur) == -1 || cur < 0)
726     return -1;
727
728   return cur;
729 }
730
731 static const gchar *
732 gst_oss4_source_mixer_update_record_flags (GstOss4Source * oss, gint cur_route)
733 {
734   const gchar *cur_name = "";
735   GList *t;
736
737   for (t = oss->tracks; t != NULL; t = t->next) {
738     GstMixerTrack *track = t->data;
739
740     if (GST_OSS4_SOURCE_INPUT (track)->route == cur_route) {
741       if (!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)) {
742         track->flags |= GST_MIXER_TRACK_RECORD;
743         /* no point in sending a mixer-record-changes message here */
744       }
745       cur_name = track->label;
746     } else {
747       if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)) {
748         track->flags &= ~GST_MIXER_TRACK_RECORD;
749         /* no point in sending a mixer-record-changes message here */
750       }
751     }
752   }
753
754   return cur_name;
755 }
756
757 static const GList *
758 gst_oss4_source_mixer_list_tracks (GstMixer * mixer)
759 {
760   oss_mixer_enuminfo names = { 0, };
761   GstOss4Source *oss;
762   const gchar *cur_name;
763   GList *tracks = NULL;
764   gint i, cur;
765
766   g_return_val_if_fail (mixer != NULL, NULL);
767   g_return_val_if_fail (GST_IS_OSS4_SOURCE (mixer), NULL);
768   g_return_val_if_fail (GST_OSS4_SOURCE_IS_OPEN (mixer), NULL);
769
770   oss = GST_OSS4_SOURCE (mixer);
771
772   if (oss->tracks != NULL && oss->tracks_static)
773     goto done;
774
775   if (ioctl (oss->fd, SNDCTL_DSP_GET_RECSRC_NAMES, &names) == -1)
776     goto get_recsrc_names_error;
777
778   oss->tracks_static = (names.version == 0);
779
780   GST_INFO_OBJECT (oss, "%d inputs (list is static: %s):", names.nvalues,
781       (oss->tracks_static) ? "yes" : "no");
782
783   for (i = 0; i < MIN (names.nvalues, OSS_ENUM_MAXVALUE + 1); ++i) {
784     GstMixerTrack *track;
785
786     track = g_object_new (GST_TYPE_OSS4_SOURCE_INPUT, NULL);
787     track->label = g_strdup (&names.strings[names.strindex[i]]);
788     track->flags = GST_MIXER_TRACK_INPUT;
789     track->num_channels = 2;
790     track->min_volume = 0;
791     track->max_volume = 100;
792     GST_OSS4_SOURCE_INPUT (track)->route = i;
793
794     GST_INFO_OBJECT (oss, " [%d] %s", i, track->label);
795     tracks = g_list_append (tracks, track);
796   }
797
798   gst_oss4_source_free_mixer_tracks (oss);
799   oss->tracks = tracks;
800
801 done:
802
803   /* update RECORD flags */
804   cur = gst_oss4_source_mixer_get_current_input (oss);
805   cur_name = gst_oss4_source_mixer_update_record_flags (oss, cur);
806   GST_DEBUG_OBJECT (oss, "current input route: %d (%s)", cur, cur_name);
807
808   return (const GList *) oss->tracks;
809
810 /* ERRORS */
811 get_recsrc_names_error:
812   {
813     GST_WARNING_OBJECT (oss, "GET_RECSRC_NAMES failed: %s", g_strerror (errno));
814     return NULL;
815   }
816 }
817
818 static void
819 gst_oss4_source_mixer_set_volume (GstMixer * mixer, GstMixerTrack * track,
820     gint * volumes)
821 {
822   GstOss4Source *oss;
823   int new_vol, cur;
824
825   g_return_if_fail (mixer != NULL);
826   g_return_if_fail (track != NULL);
827   g_return_if_fail (GST_IS_MIXER_TRACK (track));
828   g_return_if_fail (GST_IS_OSS4_SOURCE (mixer));
829   g_return_if_fail (GST_OSS4_SOURCE_IS_OPEN (mixer));
830
831   oss = GST_OSS4_SOURCE (mixer);
832
833   cur = gst_oss4_source_mixer_get_current_input (oss);
834   if (cur != GST_OSS4_SOURCE_INPUT (track)->route) {
835     GST_DEBUG_OBJECT (oss, "track not selected input route, ignoring request");
836     return;
837   }
838
839   new_vol = (volumes[1] << 8) | volumes[0];
840   if (ioctl (oss->fd, SNDCTL_DSP_SETRECVOL, &new_vol) == -1) {
841     GST_WARNING_OBJECT (oss, "SETRECVOL failed: %s", g_strerror (errno));
842   }
843 }
844
845 static void
846 gst_oss4_source_mixer_get_volume (GstMixer * mixer, GstMixerTrack * track,
847     gint * volumes)
848 {
849   GstOss4Source *oss;
850   int cur;
851
852   g_return_if_fail (mixer != NULL);
853   g_return_if_fail (GST_IS_OSS4_SOURCE (mixer));
854   g_return_if_fail (GST_OSS4_SOURCE_IS_OPEN (mixer));
855
856   oss = GST_OSS4_SOURCE (mixer);
857
858   cur = gst_oss4_source_mixer_get_current_input (oss);
859   if (cur != GST_OSS4_SOURCE_INPUT (track)->route) {
860     volumes[0] = 0;
861     volumes[1] = 0;
862   } else {
863     int vol = -1;
864
865     if (ioctl (oss->fd, SNDCTL_DSP_GETRECVOL, &vol) == -1 || vol < 0) {
866       GST_WARNING_OBJECT (oss, "GETRECVOL failed: %s", g_strerror (errno));
867       volumes[0] = 100;
868       volumes[1] = 100;
869     } else {
870       volumes[0] = MIN (100, vol & 0xff);
871       volumes[1] = MIN (100, (vol >> 8) & 0xff);
872     }
873   }
874 }
875
876 static void
877 gst_oss4_source_mixer_set_record (GstMixer * mixer, GstMixerTrack * track,
878     gboolean record)
879 {
880   GstOss4Source *oss;
881   const gchar *cur_name;
882   gint cur;
883
884   g_return_if_fail (mixer != NULL);
885   g_return_if_fail (track != NULL);
886   g_return_if_fail (GST_IS_MIXER_TRACK (track));
887   g_return_if_fail (GST_IS_OSS4_SOURCE (mixer));
888   g_return_if_fail (GST_OSS4_SOURCE_IS_OPEN (mixer));
889
890   oss = GST_OSS4_SOURCE (mixer);
891
892   cur = gst_oss4_source_mixer_get_current_input (oss);
893
894   /* stop recording for an input that's not selected anyway => nothing to do */
895   if (!record && cur != GST_OSS4_SOURCE_INPUT (track)->route)
896     goto done;
897
898   /* select recording for an input that's already selected => nothing to do
899    * (or should we mess with the recording volume in this case maybe?) */
900   if (record && cur == GST_OSS4_SOURCE_INPUT (track)->route)
901     goto done;
902
903   /* make current input stop recording: we can't really make an input stop
904    * recording, we can only select an input FOR recording, so we'll just ignore
905    * all requests to stop for now */
906   if (!record) {
907     GST_WARNING_OBJECT (oss, "Can't un-select an input as such, only switch "
908         "to a different input source");
909     /* FIXME: set recording volume to 0 maybe? */
910   } else {
911     int new_route = GST_OSS4_SOURCE_INPUT (track)->route;
912
913     /* select this input for recording */
914
915     if (ioctl (oss->fd, SNDCTL_DSP_SET_RECSRC, &new_route) == -1) {
916       GST_WARNING_OBJECT (oss, "Could not select input %d for recording: %s",
917           new_route, g_strerror (errno));
918     } else {
919       cur = new_route;
920     }
921   }
922
923 done:
924
925   cur_name = gst_oss4_source_mixer_update_record_flags (oss, cur);
926   GST_DEBUG_OBJECT (oss, "active input route: %d (%s)", cur, cur_name);
927 }
928
929 static void
930 gst_oss4_source_mixer_set_mute (GstMixer * mixer, GstMixerTrack * track,
931     gboolean mute)
932 {
933   GstOss4Source *oss;
934
935   g_return_if_fail (mixer != NULL);
936   g_return_if_fail (track != NULL);
937   g_return_if_fail (GST_IS_MIXER_TRACK (track));
938   g_return_if_fail (GST_IS_OSS4_SOURCE (mixer));
939   g_return_if_fail (GST_OSS4_SOURCE_IS_OPEN (mixer));
940
941   oss = GST_OSS4_SOURCE (mixer);
942
943   /* FIXME: implement gst_oss4_source_mixer_set_mute() - what to do here? */
944   /* oss4_mixer_set_mute (mixer->mixer, track, mute); */
945 }
946
947 static void
948 gst_oss4_source_mixer_interface_init (GstMixerClass * klass)
949 {
950   GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE;
951
952   klass->list_tracks = gst_oss4_source_mixer_list_tracks;
953   klass->set_volume = gst_oss4_source_mixer_set_volume;
954   klass->get_volume = gst_oss4_source_mixer_get_volume;
955   klass->set_mute = gst_oss4_source_mixer_set_mute;
956   klass->set_record = gst_oss4_source_mixer_set_record;
957 }
958
959 /* Implement the horror that is GstImplementsInterface */
960
961 static gboolean
962 gst_oss4_source_mixer_supported (GstImplementsInterface * iface,
963     GType iface_type)
964 {
965   GstOss4Source *oss;
966   gboolean is_open;
967
968   g_return_val_if_fail (GST_IS_OSS4_SOURCE (iface), FALSE);
969   g_return_val_if_fail (iface_type == GST_TYPE_MIXER, FALSE);
970
971   oss = GST_OSS4_SOURCE (iface);
972
973   GST_OBJECT_LOCK (oss);
974   is_open = GST_OSS4_SOURCE_IS_OPEN (iface);
975   GST_OBJECT_UNLOCK (oss);
976
977   return is_open;
978 }
979
980 static void
981 gst_oss4_source_mixer_implements_interface_init (GstImplementsInterfaceClass *
982     klass)
983 {
984   klass->supported = gst_oss4_source_mixer_supported;
985 }
986
987 static void
988 gst_oss4_source_init_interfaces (GType type)
989 {
990   static const GInterfaceInfo implements_iface_info = {
991     (GInterfaceInitFunc) gst_oss4_source_mixer_implements_interface_init,
992     NULL,
993     NULL,
994   };
995   static const GInterfaceInfo mixer_iface_info = {
996     (GInterfaceInitFunc) gst_oss4_source_mixer_interface_init,
997     NULL,
998     NULL,
999   };
1000
1001   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
1002       &implements_iface_info);
1003   g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info);
1004
1005   gst_oss4_add_property_probe_interface (type);
1006 }