mirror of
https://github.com/badaix/snapcast.git
synced 2025-06-04 11:51:44 +02:00
Add support for PulseAudio properties
This commit is contained in:
parent
be9c15cba6
commit
432e4dfad0
5 changed files with 57 additions and 11 deletions
|
@ -116,7 +116,7 @@ Available audio backends are configured using the `--player` command line parame
|
|||
| Backend | OS | Description | Parameters |
|
||||
| --------- | ------- | ------------ | ---------- |
|
||||
| alsa | Linux | ALSA | `buffer_time=<total buffer size [ms]>` (default 80, min 10)<br />`fragments=<number of buffers>` (default 4, min 2) |
|
||||
| pulse | Linux | PulseAudio | `buffer_time=<buffer size [ms]>` (default 100, min 10)<br />`server=<PulseAudio server>` - default not-set: use the default server |
|
||||
| pulse | Linux | PulseAudio | `buffer_time=<buffer size [ms]>` (default 100, min 10)<br />`server=<PulseAudio server>` - default not-set: use the default server<br />`property=<key>=<value>` set PA property, can be used multiple times (default `media.role=music`) |
|
||||
| oboe | Android | Oboe, using OpenSL ES on Android 4.1 and AAudio on 8.1 | |
|
||||
| opensl | Android | OpenSL ES | |
|
||||
| coreaudio | macOS | Core Audio | |
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <pulse/proplist.h>
|
||||
|
||||
#include "common/aixlog.hpp"
|
||||
#include "common/snap_exception.hpp"
|
||||
#include "common/str_compat.hpp"
|
||||
|
@ -133,13 +135,30 @@ vector<PcmDevice> PulsePlayer::pcm_list(const std::string& parameter)
|
|||
|
||||
|
||||
PulsePlayer::PulsePlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream)
|
||||
: Player(io_context, settings, stream), latency_(BUFFER_TIME), pa_ml_(nullptr), pa_ctx_(nullptr), playstream_(nullptr), server_(boost::none)
|
||||
: Player(io_context, settings, stream), latency_(BUFFER_TIME), pa_ml_(nullptr), pa_ctx_(nullptr), playstream_(nullptr), proplist_(nullptr),
|
||||
server_(boost::none)
|
||||
{
|
||||
auto params = utils::string::split_pairs(settings.parameter, ',', '=');
|
||||
auto params = utils::string::split_pairs_to_container<std::vector<std::string>>(settings.parameter, ',', '=');
|
||||
if (params.find("buffer_time") != params.end())
|
||||
latency_ = std::chrono::milliseconds(std::max(cpt::stoi(params["buffer_time"]), 10));
|
||||
latency_ = std::chrono::milliseconds(std::max(cpt::stoi(params["buffer_time"].front()), 10));
|
||||
if (params.find("server") != params.end())
|
||||
server_ = params["server"];
|
||||
server_ = params["server"].front();
|
||||
properties_[PA_PROP_MEDIA_ROLE] = "music";
|
||||
if (params.find("property") != params.end())
|
||||
{
|
||||
for (const auto& p : params["property"])
|
||||
{
|
||||
std::string value;
|
||||
std::string key = utils::string::split_left(p, '=', value);
|
||||
if (!key.empty())
|
||||
properties_[key] = value;
|
||||
}
|
||||
}
|
||||
for (const auto& property : properties_)
|
||||
{
|
||||
if (!property.second.empty())
|
||||
LOG(INFO, LOG_TAG) << "Setting property \"" << property.first << "\" to \"" << property.second << "\"\n";
|
||||
}
|
||||
|
||||
LOG(INFO, LOG_TAG) << "Using buffer_time: " << latency_.count() / 1000 << " ms, server: " << server_.value_or("default") << "\n";
|
||||
}
|
||||
|
@ -337,7 +356,15 @@ void PulsePlayer::start()
|
|||
pa_ready_ = 0;
|
||||
pa_ml_ = pa_mainloop_new();
|
||||
pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml_);
|
||||
pa_ctx_ = pa_context_new(pa_mlapi, "Snapcast");
|
||||
|
||||
proplist_ = pa_proplist_new();
|
||||
for (const auto& property : properties_)
|
||||
{
|
||||
if (!property.second.empty())
|
||||
pa_proplist_sets(proplist_, property.first.c_str(), property.second.c_str());
|
||||
}
|
||||
|
||||
pa_ctx_ = pa_context_new_with_proplist(pa_mlapi, "Snapcast", proplist_);
|
||||
|
||||
const char* server = server_.has_value() ? server_.value().c_str() : nullptr;
|
||||
if (pa_context_connect(pa_ctx_, server, PA_CONTEXT_NOFLAGS, nullptr) < 0)
|
||||
|
@ -476,6 +503,12 @@ void PulsePlayer::stop()
|
|||
pa_stream_unref(playstream_);
|
||||
playstream_ = nullptr;
|
||||
}
|
||||
|
||||
if (proplist_ != nullptr)
|
||||
{
|
||||
pa_proplist_free(proplist_);
|
||||
proplist_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace player
|
||||
|
|
|
@ -73,7 +73,9 @@ protected:
|
|||
pa_mainloop* pa_ml_;
|
||||
pa_context* pa_ctx_;
|
||||
pa_stream* playstream_;
|
||||
pa_proplist* proplist_;
|
||||
boost::optional<std::string> server_;
|
||||
std::map<std::string, std::string> properties_;
|
||||
|
||||
// cache of the last volume change
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_change_;
|
||||
|
|
|
@ -363,7 +363,8 @@ int main(int argc, char** argv)
|
|||
{
|
||||
cout << "Options are a comma separated list of:\n"
|
||||
<< " \"buffer_time=<buffer size [ms]>\" - default 100, min 10\n"
|
||||
<< " \"server=<PulseAudio server>\" - default not-set: use the default server\n";
|
||||
<< " \"server=<PulseAudio server>\" - default not-set: use the default server\n"
|
||||
<< " \"property=<key>=<value>\" - can be set multiple times, default 'media.role=music'\n";
|
||||
}
|
||||
#endif
|
||||
#ifdef HAS_ALSA
|
||||
|
|
|
@ -143,10 +143,10 @@ static std::vector<std::string> split(const std::string& s, char delim)
|
|||
return elems;
|
||||
}
|
||||
|
||||
|
||||
static std::map<std::string, std::string> split_pairs(const std::string& s, char pair_delim, char key_value_delim)
|
||||
template <typename T>
|
||||
static std::map<std::string, T> split_pairs_to_container(const std::string& s, char pair_delim, char key_value_delim)
|
||||
{
|
||||
std::map<std::string, std::string> result;
|
||||
std::map<std::string, T> result;
|
||||
auto keyValueList = split(s, pair_delim);
|
||||
for (auto& kv : keyValueList)
|
||||
{
|
||||
|
@ -155,13 +155,23 @@ static std::map<std::string, std::string> split_pairs(const std::string& s, char
|
|||
{
|
||||
std::string key = trim_copy(kv.substr(0, pos));
|
||||
std::string value = trim_copy(kv.substr(pos + 1));
|
||||
result[key] = value;
|
||||
result[key].push_back(std::move(value));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static std::map<std::string, std::string> split_pairs(const std::string& s, char pair_delim, char key_value_delim)
|
||||
{
|
||||
std::map<std::string, std::string> result;
|
||||
auto pairs = split_pairs_to_container<std::vector<std::string>>(s, pair_delim, key_value_delim);
|
||||
for (auto& pair : pairs)
|
||||
result[pair.first] = *pair.second.begin();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace string
|
||||
} // namespace utils
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue