2 * This file is a part of MAFW
4 * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
6 * Contact: Visa Smolander <visa.smolander@nokia.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; version 2.1 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 #ifndef MAFW_GST_RENDERER_DISABLE_PULSE_VOLUME
30 #include <pulse/pulseaudio.h>
31 #include <pulse/glib-mainloop.h>
32 #include <pulse/ext-stream-restore.h>
35 #include "mafw-gst-renderer-worker-volume.h"
39 #define G_LOG_DOMAIN "mafw-gst-renderer-worker-volume"
41 #define MAFW_GST_RENDERER_WORKER_VOLUME_SERVER NULL
43 #define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PROPERTY "PULSE_PROP_media.role"
44 #define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX "sink-input-by-media-role:"
45 #define MAFW_GST_RENDERER_WORKER_VOLUME_ROLE "x-maemo"
47 #define MAFW_GST_RENDERER_WORKER_SET_TIMEOUT 200
50 struct _MafwGstRendererWorkerVolume {
51 pa_glib_mainloop *mainloop;
55 MafwGstRendererWorkerVolumeChangedCb cb;
57 MafwGstRendererWorkerVolumeMuteCb mute_cb;
58 gpointer mute_user_data;
59 gdouble current_volume;
60 gboolean current_mute;
61 gboolean pending_operation;
62 gdouble pending_operation_volume;
63 gboolean pending_operation_mute;
64 guint change_request_id;
65 pa_operation *pa_operation;
69 MafwGstRendererWorkerVolume *wvolume;
70 MafwGstRendererWorkerVolumeInitCb cb;
74 #define _pa_volume_to_per_one(volume) \
75 ((guint) ((((gdouble)(volume) / (gdouble) PA_VOLUME_NORM) + \
76 (gdouble) 0.005) * (gdouble) 100.0) / (gdouble) 100.0)
77 #define _pa_volume_from_per_one(volume) \
78 ((pa_volume_t)((gdouble)(volume) * (gdouble) PA_VOLUME_NORM))
80 #define _pa_operation_running(wvolume) \
81 (wvolume->pa_operation != NULL && \
82 pa_operation_get_state(wvolume->pa_operation) == PA_OPERATION_RUNNING)
84 static void _state_cb_init(pa_context *c, void *data);
87 static gchar *_get_client_name(void) {
91 if (pa_get_binary_name(buf, sizeof(buf)))
92 name = g_strdup_printf("mafw-gst-renderer[%s]", buf);
94 name = g_strdup("mafw-gst-renderer");
99 static void _ext_stream_restore_read_cb(pa_context *c,
100 const pa_ext_stream_restore2_info *i,
104 MafwGstRendererWorkerVolume *wvolume = userdata;
109 g_critical("eol parameter should not be < 1. "
110 "Discarding volume event");
115 strcmp(i->name, MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX
116 MAFW_GST_RENDERER_WORKER_VOLUME_ROLE) != 0) {
120 volume = _pa_volume_to_per_one(pa_cvolume_max(&i->volume));
121 mute = i->mute != 0 ? TRUE : FALSE;
123 if (_pa_operation_running(wvolume) ||
124 (wvolume->pending_operation &&
125 (wvolume->pending_operation_volume != volume
126 #ifdef MAFW_GST_RENDERER_ENABLE_MUTE
127 || wvolume->pending_operation_mute != mute
130 g_debug("volume notification, but operation running, ignoring");
134 wvolume->pulse_volume = volume;
135 wvolume->pulse_mute = mute;
138 g_debug("ext stream volume is %lf (mute: %d) for role %s in device %s",
139 wvolume->pulse_volume, wvolume->pulse_mute, i->name, i->device);
140 if (!wvolume->pending_operation &&
141 wvolume->pulse_volume != wvolume->current_volume) {
142 wvolume->current_volume = wvolume->pulse_volume;
143 if (wvolume->cb != NULL) {
144 g_debug("signalling volume");
145 wvolume->cb(wvolume, wvolume->current_volume,
149 #ifdef MAFW_GST_RENDERER_ENABLE_MUTE
150 if (!wvolume->pending_operation &&
151 wvolume->pulse_mute != wvolume->current_mute) {
152 wvolume->current_mute = wvolume->pulse_mute;
153 if (wvolume->mute_cb != NULL) {
154 g_debug("signalling mute");
155 wvolume->mute_cb(wvolume, wvolume->current_mute,
156 wvolume->mute_user_data);
161 wvolume->pending_operation = FALSE;
164 static void _destroy_context(MafwGstRendererWorkerVolume *wvolume)
166 if (wvolume->pa_operation != NULL) {
167 if (pa_operation_get_state(wvolume->pa_operation) ==
168 PA_OPERATION_RUNNING) {
169 pa_operation_cancel(wvolume->pa_operation);
171 pa_operation_unref(wvolume->pa_operation);
172 wvolume->pa_operation = NULL;
174 pa_context_unref(wvolume->context);
177 static InitCbClosure *_init_cb_closure_new(MafwGstRendererWorkerVolume *wvolume,
178 MafwGstRendererWorkerVolumeInitCb cb,
181 InitCbClosure *closure;
183 closure = g_new(InitCbClosure, 1);
184 closure->wvolume = wvolume;
186 closure->user_data = user_data;
191 static void _connect(gpointer user_data)
194 pa_mainloop_api *api = NULL;
195 InitCbClosure *closure = user_data;
196 MafwGstRendererWorkerVolume *wvolume = closure->wvolume;
198 name = _get_client_name();
200 /* get the mainloop api and create a context */
201 api = pa_glib_mainloop_get_api(wvolume->mainloop);
202 wvolume->context = pa_context_new(api, name);
203 g_assert(wvolume->context != NULL);
205 /* register some essential callbacks */
206 pa_context_set_state_callback(wvolume->context, _state_cb_init,
209 g_debug("connecting to pulse");
211 g_assert(pa_context_connect(wvolume->context,
212 MAFW_GST_RENDERER_WORKER_VOLUME_SERVER,
213 PA_CONTEXT_NOAUTOSPAWN | PA_CONTEXT_NOFAIL,
218 static gboolean _reconnect(gpointer user_data)
220 InitCbClosure *closure = user_data;
221 MafwGstRendererWorkerVolume *wvolume = closure->wvolume;
223 g_warning("got disconnected from pulse, reconnecting");
224 _destroy_context(wvolume);
231 _state_cb(pa_context *c, void *data)
233 MafwGstRendererWorkerVolume *wvolume = data;
234 pa_context_state_t state;
236 state = pa_context_get_state(c);
239 case PA_CONTEXT_TERMINATED:
240 case PA_CONTEXT_FAILED:
242 InitCbClosure *closure;
244 closure = _init_cb_closure_new(wvolume, NULL, NULL);
245 g_idle_add(_reconnect, closure);
248 case PA_CONTEXT_READY: {
251 o = pa_ext_stream_restore2_read(c, _ext_stream_restore_read_cb,
254 pa_operation_unref(o);
263 static void _ext_stream_restore_read_cb_init(pa_context *c,
264 const pa_ext_stream_restore2_info *i,
268 InitCbClosure *closure = userdata;
271 g_critical("eol parameter should not be < 1");
275 strcmp(i->name, MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX
276 MAFW_GST_RENDERER_WORKER_VOLUME_ROLE) != 0)
279 closure->wvolume->pulse_volume =
280 _pa_volume_to_per_one(pa_cvolume_max(&i->volume));
281 closure->wvolume->pulse_mute = i->mute != 0 ? TRUE : FALSE;
282 closure->wvolume->current_volume = closure->wvolume->pulse_volume;
283 closure->wvolume->current_mute = closure->wvolume->pulse_mute;
285 /* NOT EMIT VOLUME, BUT DEBUG */
286 g_debug("ext stream volume is %lf (mute: %d) for role %s in device %s",
287 closure->wvolume->pulse_volume, i->mute, i->name, i->device);
289 if (closure->cb != NULL) {
290 g_debug("initialized: returning volume manager");
291 closure->cb(closure->wvolume, closure->user_data);
293 if (closure->wvolume->cb != NULL) {
294 g_debug("signalling volume after reconnection");
295 closure->wvolume->cb(closure->wvolume,
296 closure->wvolume->current_volume,
297 closure->wvolume->user_data);
299 if (closure->wvolume->mute_cb != NULL) {
300 g_debug("signalling mute after reconnection");
301 closure->wvolume->mute_cb(closure->wvolume,
309 pa_context_set_state_callback(closure->wvolume->context, _state_cb,
315 static void _ext_stream_restore_subscribe_cb(pa_context *c, void *userdata)
319 o = pa_ext_stream_restore2_read(c, _ext_stream_restore_read_cb, userdata);
321 pa_operation_unref(o);
325 _state_cb_init(pa_context *c, void *data)
327 InitCbClosure *closure = data;
328 MafwGstRendererWorkerVolume *wvolume = closure->wvolume;
329 pa_context_state_t state;
331 state = pa_context_get_state(c);
333 g_debug("state: %d", state);
336 case PA_CONTEXT_TERMINATED:
337 case PA_CONTEXT_FAILED:
338 g_critical("Connection to pulse failed, reconnection in 1 "
340 g_timeout_add_seconds(1, _reconnect, closure);
342 case PA_CONTEXT_READY: {
345 g_debug("PA_CONTEXT_READY");
347 o = pa_ext_stream_restore2_read(c,
348 _ext_stream_restore_read_cb_init,
351 pa_operation_unref(o);
353 pa_ext_stream_restore_set_subscribe_cb(
354 c, _ext_stream_restore_subscribe_cb, wvolume);
356 o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL);
358 pa_operation_unref(o);
367 static gboolean _destroy_idle(gpointer data)
369 MafwGstRendererWorkerVolume *wvolume = data;
371 g_debug("destroying");
373 _destroy_context(wvolume);
374 pa_glib_mainloop_free(wvolume->mainloop);
381 _state_cb_destroy(pa_context *c, void *data)
383 pa_context_state_t state;
385 state = pa_context_get_state(c);
388 case PA_CONTEXT_TERMINATED:
389 g_idle_add(_destroy_idle, data);
391 case PA_CONTEXT_FAILED:
392 g_error("Unexpected problem in volume management");
399 static void _success_cb(pa_context *c, int success, void *userdata)
402 g_critical("Setting volume to pulse operation failed");
406 static void _remove_set_timeout(MafwGstRendererWorkerVolume *wvolume)
408 if (wvolume->change_request_id != 0) {
409 g_source_remove(wvolume->change_request_id);
411 wvolume->change_request_id = 0;
414 static gboolean _set_timeout(gpointer data)
416 pa_ext_stream_restore2_info info;
417 pa_ext_stream_restore2_info *infos[1];
418 MafwGstRendererWorkerVolume *wvolume = data;
420 if (wvolume->pending_operation) {
421 g_debug("setting volume ignored as there is still a pending "
422 "operation. Waiting till next iteration");
423 } else if (wvolume->pulse_volume != wvolume->current_volume
424 #ifdef MAFW_GST_RENDERER_ENABLE_MUTE
425 || wvolume->pulse_mute != wvolume->current_mute
429 info.name = MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PREFIX
430 MAFW_GST_RENDERER_WORKER_VOLUME_ROLE;
431 info.channel_map.channels = 1;
432 info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
434 info.volume_is_absolute = TRUE;
437 info.mute = wvolume->current_mute;
438 pa_cvolume_init(&info.volume);
439 pa_cvolume_set(&info.volume, info.channel_map.channels,
440 _pa_volume_from_per_one(wvolume->
443 g_debug("setting volume to %lf and mute to %d",
444 wvolume->current_volume, wvolume->current_mute);
446 if (wvolume->pa_operation != NULL) {
447 pa_operation_unref(wvolume->pa_operation);
450 wvolume->pending_operation = TRUE;
451 wvolume->pending_operation_volume = wvolume->current_volume;
452 wvolume->pending_operation_mute = wvolume->current_mute;
454 wvolume->pa_operation = pa_ext_stream_restore2_write(
457 (const pa_ext_stream_restore2_info*
459 1, TRUE, _success_cb, wvolume);
461 if (wvolume->pa_operation == NULL) {
462 g_critical("NULL operation when writing volume to "
464 _remove_set_timeout(wvolume);
467 g_debug("removing volume timeout");
468 _remove_set_timeout(wvolume);
471 return wvolume->change_request_id != 0;
474 void mafw_gst_renderer_worker_volume_init(GMainContext *main_context,
475 MafwGstRendererWorkerVolumeInitCb cb,
477 MafwGstRendererWorkerVolumeChangedCb
479 gpointer changed_user_data,
480 MafwGstRendererWorkerVolumeMuteCb
481 mute_cb, gpointer mute_user_data)
483 MafwGstRendererWorkerVolume *wvolume = NULL;
484 InitCbClosure *closure;
486 g_return_if_fail(cb != NULL);
488 g_assert(g_setenv(MAFW_GST_RENDERER_WORKER_VOLUME_ROLE_PROPERTY,
489 MAFW_GST_RENDERER_WORKER_VOLUME_ROLE, FALSE));
491 g_debug("initializing volume manager");
493 wvolume = g_new0(MafwGstRendererWorkerVolume, 1);
495 wvolume->pulse_volume = 1.0;
496 wvolume->pulse_mute = FALSE;
497 wvolume->cb = changed_cb;
498 wvolume->user_data = changed_user_data;
499 wvolume->mute_cb = mute_cb;
500 wvolume->mute_user_data = mute_user_data;
502 wvolume->mainloop = pa_glib_mainloop_new(main_context);
503 g_assert(wvolume->mainloop != NULL);
505 closure = _init_cb_closure_new(wvolume, cb, user_data);
509 void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume,
510 gdouble volume, gboolean mute)
512 gboolean signal_volume, signal_mute;
514 g_return_if_fail(wvolume != NULL);
515 g_return_if_fail(pa_context_get_state(wvolume->context) ==
518 #ifndef MAFW_GST_RENDERER_ENABLE_MUTE
522 signal_volume = wvolume->current_volume != volume &&
524 signal_mute = wvolume->current_mute != mute && wvolume->mute_cb != NULL;
526 wvolume->current_volume = volume;
527 wvolume->current_mute = mute;
529 g_debug("volume set: %lf (mute %d)", volume, mute);
532 g_debug("signalling volume");
533 wvolume->cb(wvolume, volume, wvolume->user_data);
537 g_debug("signalling mute");
538 wvolume->mute_cb(wvolume, mute, wvolume->mute_user_data);
541 if ((signal_mute || signal_volume) && wvolume->change_request_id == 0) {
542 wvolume->change_request_id =
543 g_timeout_add(MAFW_GST_RENDERER_WORKER_SET_TIMEOUT,
544 _set_timeout, wvolume);
546 _set_timeout(wvolume);
550 gdouble mafw_gst_renderer_worker_volume_get(
551 MafwGstRendererWorkerVolume *wvolume)
553 g_return_val_if_fail(wvolume != NULL, 0.0);
555 g_debug("getting volume; %lf", wvolume->current_volume);
557 return wvolume->current_volume;
560 gboolean mafw_gst_renderer_worker_volume_is_muted(
561 MafwGstRendererWorkerVolume *wvolume)
563 g_return_val_if_fail(wvolume != NULL, FALSE);
565 g_debug("getting mute; %d", wvolume->current_mute);
567 return wvolume->current_mute;
570 void mafw_gst_renderer_worker_volume_destroy(
571 MafwGstRendererWorkerVolume *wvolume)
573 g_return_if_fail(wvolume != NULL);
575 g_debug("disconnecting");
577 pa_ext_stream_restore_set_subscribe_cb(wvolume->context, NULL, NULL);
578 pa_context_set_state_callback(wvolume->context, _state_cb_destroy,
580 pa_context_disconnect(wvolume->context);
588 #include "mafw-gst-renderer-worker-volume.h"
591 #define G_LOG_DOMAIN "mafw-gst-renderer-worker-volume-fake"
593 struct _MafwGstRendererWorkerVolume {
594 MafwGstRendererWorkerVolumeChangedCb cb;
596 MafwGstRendererWorkerVolumeMuteCb mute_cb;
597 gpointer mute_user_data;
598 gdouble current_volume;
599 gboolean current_mute;
603 MafwGstRendererWorkerVolume *wvolume;
604 MafwGstRendererWorkerVolumeInitCb cb;
608 static gboolean _init_cb_closure(gpointer user_data)
610 InitCbClosure *closure = user_data;
612 if (closure->cb != NULL) {
613 closure->cb(closure->wvolume, closure->user_data);
620 void mafw_gst_renderer_worker_volume_init(GMainContext *main_context,
621 MafwGstRendererWorkerVolumeInitCb cb,
623 MafwGstRendererWorkerVolumeChangedCb
625 gpointer changed_user_data,
626 MafwGstRendererWorkerVolumeMuteCb
627 mute_cb, gpointer mute_user_data)
629 MafwGstRendererWorkerVolume *wvolume = NULL;
630 InitCbClosure *closure;
632 g_return_if_fail(cb != NULL);
634 g_debug("initializing volume manager");
636 wvolume = g_new0(MafwGstRendererWorkerVolume, 1);
638 wvolume->cb = changed_cb;
639 wvolume->user_data = changed_user_data;
640 wvolume->mute_cb = mute_cb;
641 wvolume->mute_user_data = mute_user_data;
642 wvolume->current_volume = 0.485;
644 closure = g_new0(InitCbClosure, 1);
645 closure->wvolume = wvolume;
647 closure->user_data = user_data;
648 g_idle_add(_init_cb_closure, closure);
651 void mafw_gst_renderer_worker_volume_set(MafwGstRendererWorkerVolume *wvolume,
652 gdouble volume, gboolean mute)
654 gboolean signal_volume, signal_mute;
656 g_return_if_fail(wvolume != NULL);
658 #ifndef MAFW_GST_RENDERER_ENABLE_MUTE
662 signal_volume = wvolume->current_volume != volume &&
664 signal_mute = wvolume->current_mute != mute && wvolume->mute_cb != NULL;
666 wvolume->current_volume = volume;
667 wvolume->current_mute = mute;
669 g_debug("volume set: %lf (mute %d)", volume, mute);
672 g_debug("signalling volume");
673 wvolume->cb(wvolume, volume, wvolume->user_data);
677 g_debug("signalling mute");
678 wvolume->mute_cb(wvolume, mute, wvolume->mute_user_data);
682 gdouble mafw_gst_renderer_worker_volume_get(
683 MafwGstRendererWorkerVolume *wvolume)
685 g_return_val_if_fail(wvolume != NULL, 0.0);
687 g_debug("getting volume; %lf", wvolume->current_volume);
689 return wvolume->current_volume;
692 gboolean mafw_gst_renderer_worker_volume_is_muted(
693 MafwGstRendererWorkerVolume *wvolume)
695 g_return_val_if_fail(wvolume != NULL, FALSE);
697 g_debug("getting mute; %d", wvolume->current_mute);
699 return wvolume->current_mute;
702 void mafw_gst_renderer_worker_volume_destroy(
703 MafwGstRendererWorkerVolume *wvolume)
705 g_return_if_fail(wvolume != NULL);