Add support for controlling PulseAudio stream volume.
PulseAudio can controll the individual stream volume in addition to controlling
the sink volume. Since I use MythTV on my desktop computer, muting and changing
the sink-volume changes all sounds, including other sounds like desktop events
and other audio streams.
This patch adds support to controlling the stream volume by selecting "PCM" for
the MixerControl. Selecting "Master" uses the old schema of controlling the
sink volume.
The second part of this patch also sets several properties which help in
identifying the right stream with PulseAudio enabled applications.
|
|
|
|
| 335 | 335 | volume = min(100, volume); |
| 336 | 336 | volume = max(0, volume); |
| 337 | 337 | |
| 338 | | uint32_t sink_index = pa_stream_get_device_index(pstream); |
| 339 | | pa_threaded_mainloop_lock(mainloop); |
| 340 | | pa_operation *op = |
| 341 | | pa_context_set_sink_volume_by_index(pcontext, sink_index, |
| 342 | | &volume_control, |
| 343 | | OpCompletionCallback, this); |
| 344 | | pa_threaded_mainloop_unlock(mainloop); |
| 345 | | if (op) |
| 346 | | pa_operation_unref(op); |
| | 338 | if (gCoreContext->GetSetting("MixerControl", "PCM").toLower() == "pcm") |
| | 339 | { |
| | 340 | uint32_t stream_index = pa_stream_get_index(pstream); |
| | 341 | pa_threaded_mainloop_lock(mainloop); |
| | 342 | pa_operation *op = |
| | 343 | pa_context_set_sink_input_volume(pcontext, stream_index, |
| | 344 | &volume_control, |
| | 345 | OpCompletionCallback, this); |
| | 346 | pa_threaded_mainloop_unlock(mainloop); |
| | 347 | if (op) |
| | 348 | pa_operation_unref(op); |
| | 349 | else |
| | 350 | VBERROR(fn_log_tag + |
| | 351 | QString("set stream volume operation failed, stream %1, error %2 ") |
| | 352 | .arg(stream_index).arg(pa_strerror(pa_context_errno(pcontext)))); |
| | 353 | } |
| 347 | 354 | else |
| 348 | | VBERROR(fn_log_tag + |
| 349 | | QString("set sink volume operation failed, sink %1, error %2 ") |
| 350 | | .arg(sink_index).arg(pa_strerror(pa_context_errno(pcontext)))); |
| | 355 | { |
| | 356 | uint32_t sink_index = pa_stream_get_device_index(pstream); |
| | 357 | pa_threaded_mainloop_lock(mainloop); |
| | 358 | pa_operation *op = |
| | 359 | pa_context_set_sink_volume_by_index(pcontext, sink_index, |
| | 360 | &volume_control, |
| | 361 | OpCompletionCallback, this); |
| | 362 | pa_threaded_mainloop_unlock(mainloop); |
| | 363 | if (op) |
| | 364 | pa_operation_unref(op); |
| | 365 | else |
| | 366 | VBERROR(fn_log_tag + |
| | 367 | QString("set sink volume operation failed, sink %1, error %2 ") |
| | 368 | .arg(sink_index).arg(pa_strerror(pa_context_errno(pcontext)))); |
| | 369 | } |
| 351 | 370 | } |
| 352 | 371 | |
| 353 | 372 | void AudioOutputPulseAudio::Drain(void) |
| … |
… |
|
| 373 | 392 | pcontext = NULL; |
| 374 | 393 | return false; |
| 375 | 394 | } |
| 376 | | pcontext = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "MythTV"); |
| | 395 | pa_proplist *proplist = pa_proplist_new(); |
| | 396 | if (!proplist) |
| | 397 | { |
| | 398 | VERBOSE(VB_IMPORTANT, LOC_ERR + fn_log_tag + |
| | 399 | QString("failed to create new proplist")); |
| | 400 | return false; |
| | 401 | } |
| | 402 | pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "MythTV"); |
| | 403 | pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mythtv"); |
| | 404 | pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video"); |
| | 405 | pcontext = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mainloop), "MythTV", proplist); |
| 377 | 406 | if (!pcontext) |
| 378 | 407 | { |
| 379 | 408 | VBERROR(fn_log_tag + "failed to acquire new context"); |
| … |
… |
|
| 480 | 509 | bool AudioOutputPulseAudio::ConnectPlaybackStream(void) |
| 481 | 510 | { |
| 482 | 511 | QString fn_log_tag = "ConnectPlaybackStream, "; |
| 483 | | pstream = pa_stream_new(pcontext, "MythTV playback", &sample_spec, |
| 484 | | &channel_map); |
| | 512 | pa_proplist *proplist = pa_proplist_new(); |
| | 513 | if (!proplist) |
| | 514 | { |
| | 515 | VERBOSE(VB_IMPORTANT, LOC_ERR + fn_log_tag + |
| | 516 | QString("failed to create new proplist")); |
| | 517 | return false; |
| | 518 | } |
| | 519 | pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video"); |
| | 520 | pstream = pa_stream_new_with_proplist(pcontext, "MythTV playback", &sample_spec, |
| | 521 | &channel_map, proplist); |
| 485 | 522 | if (!pstream) |
| 486 | 523 | { |
| 487 | 524 | VBERROR(fn_log_tag + QString("failed to create new playback stream")); |