fix tabs
[gst-pulse.git] / src / pulsesrc.c
bloba66acdc058c08c35359e1e9b0d0d2fd177d5b9e9
1 /* $Id$ */
3 /***
4 This file is part of gst-pulse.
6 gst-pulse is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 gst-pulse is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with gst-pulse; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #include <string.h>
27 #include <stdio.h>
29 #include <gst/base/gstbasesrc.h>
30 #include <gst/gsttaglist.h>
32 #include "pulsesrc.h"
33 #include "pulseutil.h"
34 #include "pulsemixerctrl.h"
36 GST_DEBUG_CATEGORY_EXTERN(pulse_debug);
37 #define GST_CAT_DEFAULT pulse_debug
39 enum {
40 PROP_SERVER = 1,
41 PROP_DEVICE
44 static GstAudioSrcClass *parent_class = NULL;
46 GST_IMPLEMENT_PULSEMIXER_CTRL_METHODS(GstPulseSrc, gst_pulsesrc)
48 static void gst_pulsesrc_destroy_stream(GstPulseSrc *pulsesrc);
49 static void gst_pulsesrc_destroy_context(GstPulseSrc *pulsesrc);
51 static void gst_pulsesrc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
52 static void gst_pulsesrc_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
53 static void gst_pulsesrc_finalize(GObject *object);
54 static void gst_pulsesrc_dispose(GObject *object);
56 static gboolean gst_pulsesrc_open(GstAudioSrc *asrc);
57 static gboolean gst_pulsesrc_close(GstAudioSrc *asrc);
59 static gboolean gst_pulsesrc_prepare(GstAudioSrc *asrc, GstRingBufferSpec *spec);
60 static gboolean gst_pulsesrc_unprepare(GstAudioSrc *asrc);
62 static guint gst_pulsesrc_read(GstAudioSrc *asrc, gpointer data, guint length);
63 static guint gst_pulsesrc_delay(GstAudioSrc *asrc);
65 static GstStateChangeReturn gst_pulsesrc_change_state(GstElement *element, GstStateChange transition);
67 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
68 # define ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN"
69 #else
70 # define ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN"
71 #endif
73 static gboolean gst_pulsesrc_interface_supported(GstImplementsInterface* iface, GType interface_type) {
74 GstPulseSrc *this = GST_PULSESRC(iface);
76 if (interface_type == GST_TYPE_MIXER && this->mixer)
77 return TRUE;
79 return FALSE;
82 static void gst_pulsesrc_implements_interface_init(GstImplementsInterfaceClass* klass) {
83 klass->supported = gst_pulsesrc_interface_supported;
86 static void gst_pulsesrc_init_interfaces(GType type) {
87 static const GInterfaceInfo implements_iface_info = {
88 (GInterfaceInitFunc) gst_pulsesrc_implements_interface_init,
89 NULL,
90 NULL,
92 static const GInterfaceInfo mixer_iface_info = {
93 (GInterfaceInitFunc) gst_pulsesrc_mixer_interface_init,
94 NULL,
95 NULL,
98 g_type_add_interface_static(type, GST_TYPE_IMPLEMENTS_INTERFACE, &implements_iface_info);
99 g_type_add_interface_static(type, GST_TYPE_MIXER, &mixer_iface_info);
102 static void gst_pulsesrc_base_init(gpointer g_class) {
104 static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE(
105 "src",
106 GST_PAD_SRC,
107 GST_PAD_ALWAYS,
108 GST_STATIC_CAPS(
109 "audio/x-raw-int, "
110 "endianness = (int) { " ENDIANNESS " }, "
111 "signed = (boolean) TRUE, "
112 "width = (int) 16, "
113 "depth = (int) 16, "
114 "rate = (int) [ 1, MAX ], "
115 "channels = (int) [ 1, 16 ];"
117 "audio/x-raw-int, "
118 "signed = (boolean) FALSE, "
119 "width = (int) 8, "
120 "depth = (int) 8, "
121 "rate = (int) [ 1, MAX ], "
122 "channels = (int) [ 1, 16 ];"
124 "audio/x-raw-float, "
125 "endianness = (int) { " ENDIANNESS " }, "
126 "width = (int) 32, "
127 "rate = (int) [ 1, MAX ], "
128 "channels = (int) [ 1, 16 ];"
130 "audio/x-alaw, "
131 "rate = (int) [ 1, MAX], "
132 "channels = (int) [ 1, 16 ];"
134 "audio/x-mulaw, "
135 "rate = (int) [ 1, MAX], "
136 "channels = (int) [ 1, 16 ]"
140 static const GstElementDetails details =
141 GST_ELEMENT_DETAILS(
142 "PulseAudio Audio Source",
143 "Source/Audio",
144 "Captures audio from a PulseAudio server",
145 "Lennart Poettering");
147 GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
149 gst_element_class_set_details(element_class, &details);
150 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&pad_template));
153 static void gst_pulsesrc_class_init(
154 gpointer g_class,
155 gpointer class_data) {
157 GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
158 GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_CLASS(g_class);
159 GstElementClass *gstelement_class = GST_ELEMENT_CLASS(g_class);
160 parent_class = g_type_class_peek_parent(g_class);
162 gstelement_class->change_state = GST_DEBUG_FUNCPTR(gst_pulsesrc_change_state);
164 gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_pulsesrc_dispose);
165 gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_pulsesrc_finalize);
166 gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_pulsesrc_set_property);
167 gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_pulsesrc_get_property);
169 gstaudiosrc_class->open = GST_DEBUG_FUNCPTR(gst_pulsesrc_open);
170 gstaudiosrc_class->close = GST_DEBUG_FUNCPTR(gst_pulsesrc_close);
171 gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR(gst_pulsesrc_prepare);
172 gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR(gst_pulsesrc_unprepare);
173 gstaudiosrc_class->read = GST_DEBUG_FUNCPTR(gst_pulsesrc_read);
174 gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR(gst_pulsesrc_delay);
176 /* Overwrite GObject fields */
177 g_object_class_install_property(
178 gobject_class,
179 PROP_SERVER,
180 g_param_spec_string("server", "Server", "The PulseAudio server to connect to", NULL, G_PARAM_READWRITE));
181 g_object_class_install_property(
182 gobject_class,
183 PROP_DEVICE,
184 g_param_spec_string("device", "Source", "The PulseAudio source device to connect to", NULL, G_PARAM_READWRITE));
187 static void gst_pulsesrc_init(
188 GTypeInstance * instance,
189 gpointer g_class) {
191 GstPulseSrc *pulsesrc = GST_PULSESRC(instance);
192 int e;
194 pulsesrc->server = pulsesrc->device = NULL;
196 pulsesrc->context = NULL;
197 pulsesrc->stream = NULL;
199 pulsesrc->read_buffer = NULL;
200 pulsesrc->read_buffer_length = 0;
202 pulsesrc->mainloop = pa_threaded_mainloop_new();
203 g_assert(pulsesrc->mainloop);
205 e = pa_threaded_mainloop_start(pulsesrc->mainloop);
206 g_assert(e == 0);
208 pulsesrc->mixer = NULL;
211 static void gst_pulsesrc_destroy_stream(GstPulseSrc* pulsesrc) {
212 if (pulsesrc->stream) {
213 pa_stream_disconnect(pulsesrc->stream);
214 pa_stream_unref(pulsesrc->stream);
215 pulsesrc->stream = NULL;
219 static void gst_pulsesrc_destroy_context(GstPulseSrc* pulsesrc) {
221 gst_pulsesrc_destroy_stream(pulsesrc);
223 if (pulsesrc->context) {
224 pa_context_disconnect(pulsesrc->context);
225 pa_context_unref(pulsesrc->context);
226 pulsesrc->context = NULL;
230 static void gst_pulsesrc_finalize(GObject * object) {
231 GstPulseSrc *pulsesrc = GST_PULSESRC(object);
233 pa_threaded_mainloop_stop(pulsesrc->mainloop);
235 gst_pulsesrc_destroy_context(pulsesrc);
237 g_free(pulsesrc->server);
238 g_free(pulsesrc->device);
240 pa_threaded_mainloop_free(pulsesrc->mainloop);
242 if (pulsesrc->mixer)
243 gst_pulsemixer_ctrl_free(pulsesrc->mixer);
245 G_OBJECT_CLASS(parent_class)->finalize(object);
248 static void gst_pulsesrc_dispose(GObject * object) {
249 G_OBJECT_CLASS(parent_class)->dispose(object);
252 static void gst_pulsesrc_set_property(
253 GObject * object,
254 guint prop_id,
255 const GValue * value,
256 GParamSpec * pspec) {
258 GstPulseSrc *pulsesrc = GST_PULSESRC(object);
260 switch (prop_id) {
261 case PROP_SERVER:
262 g_free(pulsesrc->server);
263 pulsesrc->server = g_value_dup_string(value);
264 break;
266 case PROP_DEVICE:
267 g_free(pulsesrc->device);
268 pulsesrc->device = g_value_dup_string(value);
269 break;
271 default:
272 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
273 break;
277 static void gst_pulsesrc_get_property(
278 GObject * object,
279 guint prop_id,
280 GValue * value,
281 GParamSpec * pspec) {
283 GstPulseSrc *pulsesrc = GST_PULSESRC(object);
285 switch(prop_id) {
286 case PROP_SERVER:
287 g_value_set_string(value, pulsesrc->server);
288 break;
290 case PROP_DEVICE:
291 g_value_set_string(value, pulsesrc->device);
292 break;
294 default:
295 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
296 break;
300 static void gst_pulsesrc_context_state_cb(pa_context *c, void *userdata) {
301 GstPulseSrc *pulsesrc = GST_PULSESRC(userdata);
303 switch (pa_context_get_state(c)) {
304 case PA_CONTEXT_READY:
305 case PA_CONTEXT_TERMINATED:
306 case PA_CONTEXT_FAILED:
307 pa_threaded_mainloop_signal(pulsesrc->mainloop, 0);
308 break;
310 case PA_CONTEXT_UNCONNECTED:
311 case PA_CONTEXT_CONNECTING:
312 case PA_CONTEXT_AUTHORIZING:
313 case PA_CONTEXT_SETTING_NAME:
314 break;
318 static void gst_pulsesrc_stream_state_cb(pa_stream *s, void * userdata) {
319 GstPulseSrc *pulsesrc = GST_PULSESRC(userdata);
321 switch (pa_stream_get_state(s)) {
323 case PA_STREAM_READY:
324 case PA_STREAM_FAILED:
325 case PA_STREAM_TERMINATED:
326 pa_threaded_mainloop_signal(pulsesrc->mainloop, 0);
327 break;
329 case PA_STREAM_UNCONNECTED:
330 case PA_STREAM_CREATING:
331 break;
335 static void gst_pulsesrc_stream_request_cb(pa_stream *s, size_t length, void *userdata) {
336 GstPulseSrc *pulsesrc = GST_PULSESRC(userdata);
338 pa_threaded_mainloop_signal(pulsesrc->mainloop, 0);
341 static gboolean gst_pulsesrc_open(GstAudioSrc *asrc) {
342 GstPulseSrc *pulsesrc = GST_PULSESRC(asrc);
343 gchar *name = gst_pulse_client_name();
345 pa_threaded_mainloop_lock(pulsesrc->mainloop);
347 if (!(pulsesrc->context = pa_context_new(pa_threaded_mainloop_get_api(pulsesrc->mainloop), name))) {
348 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Failed to create context"), (NULL));
349 goto unlock_and_fail;
352 pa_context_set_state_callback(pulsesrc->context, gst_pulsesrc_context_state_cb, pulsesrc);
354 if (pa_context_connect(pulsesrc->context, pulsesrc->server, 0, NULL) < 0) {
355 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
356 goto unlock_and_fail;
359 /* Wait until the context is ready */
360 pa_threaded_mainloop_wait(pulsesrc->mainloop);
362 if (pa_context_get_state(pulsesrc->context) != PA_CONTEXT_READY) {
363 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
364 goto unlock_and_fail;
367 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
369 g_free(name);
370 return TRUE;
372 unlock_and_fail:
374 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
376 g_free(name);
377 return FALSE;
380 static gboolean gst_pulsesrc_close(GstAudioSrc *asrc) {
381 GstPulseSrc *pulsesrc = GST_PULSESRC(asrc);
383 pa_threaded_mainloop_lock(pulsesrc->mainloop);
384 gst_pulsesrc_destroy_context(pulsesrc);
385 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
387 return TRUE;
389 static gboolean gst_pulsesrc_prepare(GstAudioSrc *asrc, GstRingBufferSpec *spec) {
390 pa_buffer_attr buf_attr;
392 GstPulseSrc *pulsesrc = GST_PULSESRC(asrc);
394 if (!gst_pulse_fill_sample_spec(spec, &pulsesrc->sample_spec)) {
395 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, SETTINGS, ("Invalid sample specification."), (NULL));
396 goto unlock_and_fail;
399 pa_threaded_mainloop_lock(pulsesrc->mainloop);
401 if (!pulsesrc->context || pa_context_get_state(pulsesrc->context) != PA_CONTEXT_READY) {
402 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Bad context state: %s", pulsesrc->context ? pa_strerror(pa_context_errno(pulsesrc->context)) : NULL), (NULL));
403 goto unlock_and_fail;
406 if (!(pulsesrc->stream = pa_stream_new(pulsesrc->context, "Record Stream", &pulsesrc->sample_spec, NULL))) {
407 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Failed to create stream: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
408 goto unlock_and_fail;
411 pa_stream_set_state_callback(pulsesrc->stream, gst_pulsesrc_stream_state_cb, pulsesrc);
412 pa_stream_set_read_callback(pulsesrc->stream, gst_pulsesrc_stream_request_cb, pulsesrc);
414 memset(&buf_attr, 0, sizeof(buf_attr));
415 buf_attr.maxlength = spec->segtotal*spec->segsize*2;
416 buf_attr.fragsize = spec->segsize;
418 if (pa_stream_connect_record(pulsesrc->stream, pulsesrc->device, &buf_attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_NOT_MONOTONOUS) < 0) {
419 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Failed to connect stream: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
420 goto unlock_and_fail;
423 /* Wait until the stream is ready */
424 pa_threaded_mainloop_wait(pulsesrc->mainloop);
426 if (pa_stream_get_state(pulsesrc->stream) != PA_STREAM_READY) {
427 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("Failed to connect stream: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
428 goto unlock_and_fail;
431 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
433 spec->bytes_per_sample = pa_frame_size(&pulsesrc->sample_spec);
434 memset(spec->silence_sample, 0, spec->bytes_per_sample);
436 return TRUE;
438 unlock_and_fail:
440 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
441 return FALSE;
444 static gboolean gst_pulsesrc_unprepare(GstAudioSrc * asrc) {
445 GstPulseSrc *pulsesrc = GST_PULSESRC(asrc);
447 pa_threaded_mainloop_lock(pulsesrc->mainloop);
448 gst_pulsesrc_destroy_stream(pulsesrc);
450 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
452 pulsesrc->read_buffer = NULL;
453 pulsesrc->read_buffer_length = 0;
455 return TRUE;
458 #define CHECK_DEAD_GOTO(pulsesrc, label) \
459 if (!(pulsesrc)->context || pa_context_get_state((pulsesrc)->context) != PA_CONTEXT_READY || \
460 !(pulsesrc)->stream || pa_stream_get_state((pulsesrc)->stream) != PA_STREAM_READY) { \
461 GST_ELEMENT_ERROR((pulsesrc), RESOURCE, FAILED, ("Disconnected: %s", (pulsesrc)->context ? pa_strerror(pa_context_errno((pulsesrc)->context)) : NULL), (NULL)); \
462 goto label; \
465 static guint gst_pulsesrc_read(GstAudioSrc *asrc, gpointer data, guint length) {
466 GstPulseSrc *pulsesrc = GST_PULSESRC(asrc);
467 size_t sum = 0;
469 pa_threaded_mainloop_lock(pulsesrc->mainloop);
471 CHECK_DEAD_GOTO(pulsesrc, unlock_and_fail);
473 while (length > 0) {
474 size_t l;
476 if (!pulsesrc->read_buffer) {
478 for (;;) {
479 if (pa_stream_peek(pulsesrc->stream, &pulsesrc->read_buffer, &pulsesrc->read_buffer_length) < 0) {
480 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("pa_stream_peek() failed: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
481 goto unlock_and_fail;
484 if (pulsesrc->read_buffer)
485 break;
487 pa_threaded_mainloop_wait(pulsesrc->mainloop);
489 CHECK_DEAD_GOTO(pulsesrc, unlock_and_fail);
493 g_assert(pulsesrc->read_buffer && pulsesrc->read_buffer_length);
495 l = pulsesrc->read_buffer_length > length ? length : pulsesrc->read_buffer_length;
497 memcpy(data, pulsesrc->read_buffer, l);
499 pulsesrc->read_buffer = (const guint8*) pulsesrc->read_buffer + l;
500 pulsesrc->read_buffer_length -= l;
502 data = (guint8*) data + l;
503 length -= l;
505 sum += l;
507 if (pulsesrc->read_buffer_length <= 0) {
509 if (pa_stream_drop(pulsesrc->stream) < 0) {
510 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("pa_stream_drop() failed: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
511 goto unlock_and_fail;
514 pulsesrc->read_buffer = NULL;
515 pulsesrc->read_buffer_length = 0;
519 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
521 return sum;
523 unlock_and_fail:
524 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
525 return 0;
528 static guint gst_pulsesrc_delay(GstAudioSrc *asrc) {
529 GstPulseSrc *pulsesrc = GST_PULSESRC(asrc);
530 pa_usec_t t;
531 int negative;
533 pa_threaded_mainloop_lock(pulsesrc->mainloop);
535 CHECK_DEAD_GOTO(pulsesrc, unlock_and_fail);
537 if (pa_stream_get_latency(pulsesrc->stream, &t, &negative) < 0) {
539 if (pa_context_errno(pulsesrc->context) != PA_ERR_NODATA) {
540 GST_ELEMENT_ERROR(pulsesrc, RESOURCE, FAILED, ("pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(pulsesrc->context))), (NULL));
541 goto unlock_and_fail;
544 GST_WARNING("Not data while querying latency");
545 t = 0;
546 } else if (negative)
547 t = 0;
549 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
551 return (guint) ((t * pulsesrc->sample_spec.rate) / 1000000LL);
553 unlock_and_fail:
555 pa_threaded_mainloop_unlock(pulsesrc->mainloop);
556 return 0;
559 static GstStateChangeReturn gst_pulsesrc_change_state(GstElement *element, GstStateChange transition) {
560 GstPulseSrc *this = GST_PULSESRC(element);
562 switch (transition) {
563 case GST_STATE_CHANGE_NULL_TO_READY:
565 if (!this->mixer)
566 this->mixer = gst_pulsemixer_ctrl_new(this->server, this->device, GST_PULSEMIXER_SOURCE);
568 break;
570 case GST_STATE_CHANGE_READY_TO_NULL:
572 if (this->mixer) {
573 gst_pulsemixer_ctrl_free(this->mixer);
574 this->mixer = NULL;
577 break;
579 default:
583 if (GST_ELEMENT_CLASS(parent_class)->change_state)
584 return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
586 return GST_STATE_CHANGE_SUCCESS;
589 GType gst_pulsesrc_get_type(void) {
590 static GType pulsesrc_type = 0;
592 if (!pulsesrc_type) {
594 static const GTypeInfo pulsesrc_info = {
595 sizeof(GstPulseSrcClass),
596 gst_pulsesrc_base_init,
597 NULL,
598 gst_pulsesrc_class_init,
599 NULL,
600 NULL,
601 sizeof(GstPulseSrc),
603 gst_pulsesrc_init,
606 pulsesrc_type = g_type_register_static(
607 GST_TYPE_AUDIO_SRC,
608 "GstPulseSrc",
609 &pulsesrc_info,
612 gst_pulsesrc_init_interfaces(pulsesrc_type);
615 return pulsesrc_type;