mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-28 17:57:05 +02:00
Add sharing mode for Oboe, log device settings
This commit is contained in:
parent
ad2351a42b
commit
424487a48e
14 changed files with 101 additions and 49 deletions
|
@ -30,6 +30,7 @@ struct ClientSettings
|
|||
{
|
||||
enum class SharingMode
|
||||
{
|
||||
unspecified,
|
||||
exclusive,
|
||||
shared
|
||||
};
|
||||
|
@ -60,7 +61,7 @@ struct ClientSettings
|
|||
int latency{0};
|
||||
PcmDevice pcm_device;
|
||||
SampleFormat sample_format;
|
||||
SharingMode sharing_mode{SharingMode::shared};
|
||||
SharingMode sharing_mode{SharingMode::unspecified};
|
||||
Mixer mixer;
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,18 @@ Controller::Controller(boost::asio::io_context& io_context, const ClientSettings
|
|||
}
|
||||
|
||||
|
||||
template <typename PlayerType>
|
||||
std::unique_ptr<Player> Controller::createPlayer(ClientSettings::Player& settings, const std::string& player_name)
|
||||
{
|
||||
if (settings.player_name.empty() || settings.player_name == player_name)
|
||||
{
|
||||
settings.player_name = player_name;
|
||||
return make_unique<AlsaPlayer>(io_context_, settings, stream_);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Controller::getNextMessage()
|
||||
{
|
||||
clientConnection_->getNextMessage([this](const boost::system::error_code& ec, std::unique_ptr<msg::BaseMessage> response) {
|
||||
|
@ -68,7 +80,7 @@ void Controller::getNextMessage()
|
|||
// boost::asio::post(io_context_, [this, response = std::move(response)]() mutable {
|
||||
auto pcmChunk = msg::message_cast<msg::PcmChunk>(std::move(response));
|
||||
pcmChunk->format = sampleFormat_;
|
||||
// LOG(TRACE, LOG_TAG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.getFormat() << "\n";
|
||||
// LOG(TRACE, LOG_TAG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.toString() << "\n";
|
||||
if (decoder_->decode(pcmChunk.get()))
|
||||
{
|
||||
// LOG(TRACE, LOG_TAG) << ", decoded: " << pcmChunk->payloadSize << ", Duration: " << pcmChunk->durationMs() << ", sec: " <<
|
||||
|
@ -99,7 +111,6 @@ void Controller::getNextMessage()
|
|||
else if (response->type == message_type::kCodecHeader)
|
||||
{
|
||||
headerChunk_ = msg::message_cast<msg::CodecHeader>(std::move(response));
|
||||
LOG(INFO, LOG_TAG) << "Codec: " << headerChunk_->codec << "\n";
|
||||
decoder_.reset(nullptr);
|
||||
stream_ = nullptr;
|
||||
player_.reset(nullptr);
|
||||
|
@ -122,33 +133,32 @@ void Controller::getNextMessage()
|
|||
throw SnapException("codec not supported: \"" + headerChunk_->codec + "\"");
|
||||
|
||||
sampleFormat_ = decoder_->setHeader(headerChunk_.get());
|
||||
LOG(NOTICE, LOG_TAG) << TAG("state") << "sampleformat: " << sampleFormat_.getFormat() << "\n";
|
||||
LOG(INFO, LOG_TAG) << "Codec: " << headerChunk_->codec << ", sampleformat: " << sampleFormat_.toString() << "\n";
|
||||
LOG(NOTICE, LOG_TAG) << TAG("state") << "sampleformat: " << sampleFormat_.toString() << "\n";
|
||||
|
||||
stream_ = make_shared<Stream>(sampleFormat_, settings_.player.sample_format);
|
||||
stream_->setBufferLen(std::max(0, serverSettings_->getBufferMs() - serverSettings_->getLatency() - settings_.player.latency));
|
||||
|
||||
const auto& player_settings = settings_.player;
|
||||
const auto& player_name = settings_.player.player_name;
|
||||
player_ = nullptr;
|
||||
auto& player_settings = settings_.player;
|
||||
#ifdef HAS_ALSA
|
||||
if (!player_ && (player_name.empty() || (player_name == "alsa")))
|
||||
player_ = make_unique<AlsaPlayer>(io_context_, player_settings, stream_);
|
||||
if (!player_)
|
||||
player_ = createPlayer<AlsaPlayer>(settings_.player, "alsa");
|
||||
#endif
|
||||
#ifdef HAS_OBOE
|
||||
if (!player_ && (player_name.empty() || (player_name == "oboe")))
|
||||
player_ = make_unique<OboePlayer>(io_context_, player_settings, stream_);
|
||||
if (!player_)
|
||||
player_ = createPlayer<OboePlayer>(settings_.player, "oboe");
|
||||
#endif
|
||||
#ifdef HAS_OPENSL
|
||||
if (!player_ && (player_name.empty() || (player_name == "opensl")))
|
||||
player_ = make_unique<OpenslPlayer>(io_context_, player_settings, stream_);
|
||||
if (!player_)
|
||||
player_ = createPlayer<OpenslPlayer>(settings_.player, "opensl");
|
||||
#endif
|
||||
#ifdef HAS_COREAUDIO
|
||||
if (!player_ && (player_name.empty() || (player_name == "coreaudio")))
|
||||
player_ = make_unique<CoreAudioPlayer>(io_context_, player_settings, stream_);
|
||||
if (!player_)
|
||||
player_ = createPlayer<CoreAudioPlayer>(settings_.player, "coreaudio");
|
||||
#endif
|
||||
#ifdef HAS_WASAPI
|
||||
if (!player_ && (player_name.empty() || (player_name == "wasapi")))
|
||||
player_ = make_unique<WASAPIPlayer>(io_context_, player_settings, stream_);
|
||||
if (!player_)
|
||||
player_ = createPlayer<WASAPIPlayer>(settings_.player, "wasapi");
|
||||
#endif
|
||||
if (!player_)
|
||||
throw SnapException("No audio player support");
|
||||
|
|
|
@ -67,6 +67,9 @@ private:
|
|||
void reconnect();
|
||||
void browseMdns(const MdnsHandler& handler);
|
||||
|
||||
template <typename PlayerType>
|
||||
std::unique_ptr<Player> createPlayer(ClientSettings::Player& settings, const std::string& player_name);
|
||||
|
||||
void getNextMessage();
|
||||
void sendTimeSyncMessage(int quick_syncs);
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ SampleFormat OpusDecoder::setHeader(msg::CodecHeader* chunk)
|
|||
memcpy(&channels, chunk->payload + 10, sizeof(channels));
|
||||
|
||||
sample_format_.setFormat(SWAP_32(rate), SWAP_16(bits), SWAP_16(channels));
|
||||
LOG(DEBUG, LOG_TAG) << "Opus sampleformat: " << sample_format_.getFormat() << "\n";
|
||||
LOG(DEBUG, LOG_TAG) << "Opus sampleformat: " << sample_format_.toString() << "\n";
|
||||
|
||||
// create the decoder
|
||||
int error;
|
||||
|
|
|
@ -332,7 +332,7 @@ void AlsaPlayer::initAlsa()
|
|||
|
||||
/* Allocate buffer to hold single period */
|
||||
snd_pcm_hw_params_get_period_size(params, &frames_, nullptr);
|
||||
LOG(INFO, LOG_TAG) << "frames: " << frames_ << "\n";
|
||||
LOG(DEBUG, LOG_TAG) << "frames: " << frames_ << "\n";
|
||||
|
||||
snd_pcm_hw_params_get_period_time(params, &tmp, nullptr);
|
||||
LOG(DEBUG, LOG_TAG) << "period time: " << tmp << "\n";
|
||||
|
|
|
@ -33,7 +33,6 @@ public:
|
|||
AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream);
|
||||
~AlsaPlayer() override;
|
||||
|
||||
/// Set audio volume in range [0..1]
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
|
|
|
@ -45,9 +45,13 @@ OboePlayer::OboePlayer(boost::asio::io_context& io_context, const ClientSettings
|
|||
LOG(INFO, LOG_TAG) << "DefaultStreamValues::SampleRate: " << oboe::DefaultStreamValues::SampleRate
|
||||
<< ", DefaultStreamValues::FramesPerBurst: " << oboe::DefaultStreamValues::FramesPerBurst << "\n";
|
||||
|
||||
oboe::SharingMode sharing_mode = oboe::SharingMode::Shared;
|
||||
if (settings.sharing_mode == ClientSettings::SharingMode::exclusive)
|
||||
sharing_mode = oboe::SharingMode::Exclusive;
|
||||
|
||||
// The builder set methods can be chained for convenience.
|
||||
oboe::AudioStreamBuilder builder;
|
||||
auto result = builder.setSharingMode(oboe::SharingMode::Exclusive)
|
||||
auto result = builder.setSharingMode(sharing_mode)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setChannelCount(stream->getFormat().channels())
|
||||
->setSampleRate(stream->getFormat().rate())
|
||||
|
|
|
@ -39,6 +39,49 @@ static constexpr auto LOG_TAG = "Player";
|
|||
Player::Player(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream)
|
||||
: io_context_(io_context), active_(false), stream_(stream), settings_(settings), volume_(1.0), muted_(false), volCorrection_(1.0)
|
||||
{
|
||||
string sharing_mode;
|
||||
switch (settings_.sharing_mode)
|
||||
{
|
||||
case ClientSettings::SharingMode::unspecified:
|
||||
sharing_mode = "unspecified";
|
||||
break;
|
||||
case ClientSettings::SharingMode::exclusive:
|
||||
sharing_mode = "exclusive";
|
||||
break;
|
||||
case ClientSettings::SharingMode::shared:
|
||||
sharing_mode = "shared";
|
||||
break;
|
||||
}
|
||||
|
||||
auto coalesce = [](const std::string& value, const std::string& fallback = "<none>") {
|
||||
if (!value.empty())
|
||||
return value;
|
||||
else
|
||||
return fallback;
|
||||
};
|
||||
LOG(INFO, LOG_TAG) << "Player name: " << coalesce(settings_.player_name) << ", device: " << coalesce(settings_.pcm_device.name)
|
||||
<< ", description: " << coalesce(settings_.pcm_device.description) << ", idx: " << settings_.pcm_device.idx
|
||||
<< ", sharing mode: " << sharing_mode << "\n";
|
||||
|
||||
string mixer;
|
||||
switch (settings_.mixer.mode)
|
||||
{
|
||||
case ClientSettings::Mixer::Mode::hardware:
|
||||
mixer = "hardware";
|
||||
break;
|
||||
case ClientSettings::Mixer::Mode::software:
|
||||
mixer = "software";
|
||||
break;
|
||||
case ClientSettings::Mixer::Mode::script:
|
||||
mixer = "script";
|
||||
break;
|
||||
case ClientSettings::Mixer::Mode::none:
|
||||
mixer = "none";
|
||||
break;
|
||||
}
|
||||
LOG(INFO, LOG_TAG) << "Mixer mode: " << mixer << ", parameters: " << coalesce(settings_.mixer.parameter) << "\n";
|
||||
LOG(INFO, LOG_TAG) << "Sampleformat: " << (settings_.sample_format.isInitialized() ? settings_.sample_format.toString() : stream->getFormat().toString())
|
||||
<< ", stream: " << stream->getFormat().toString() << "\n";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ int main(int argc, char** argv)
|
|||
#ifdef HAS_SOXR
|
||||
auto sample_format = op.add<Value<string>>("", "sampleformat", "resample audio stream to <rate>:<bits>:<channels>", "");
|
||||
#endif
|
||||
#ifdef HAS_WASAPI
|
||||
#if defined(HAS_OBOE) || defined(HAS_WASAPI)
|
||||
auto sharing_mode = op.add<Value<string>>("", "sharingmode", "audio mode to use [shared|exclusive]", "shared");
|
||||
#endif
|
||||
bool hw_mixer_supported = false;
|
||||
|
@ -299,29 +299,21 @@ int main(int argc, char** argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_WASAPI
|
||||
if (sharing_mode->is_set())
|
||||
{
|
||||
settings.player.sharing_mode =
|
||||
(sharing_mode->value() == "exclusive") ? ClientSettings::SharingMode::exclusive : ClientSettings::SharingMode::shared;
|
||||
}
|
||||
#if defined(HAS_OBOE) || defined(HAS_WASAPI)
|
||||
settings.player.sharing_mode = (sharing_mode->value() == "exclusive") ? ClientSettings::SharingMode::exclusive : ClientSettings::SharingMode::shared;
|
||||
#endif
|
||||
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::software;
|
||||
if (mixer_mode->is_set())
|
||||
{
|
||||
string mode = utils::string::split_left(mixer_mode->value(), ':', settings.player.mixer.parameter);
|
||||
if (mode == "software")
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::software;
|
||||
else if ((mode == "hardware") && hw_mixer_supported)
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::hardware;
|
||||
else if (mode == "script")
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::script;
|
||||
else if (mode == "none")
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::none;
|
||||
else
|
||||
throw SnapException("Mixer mode not supported: " + mode);
|
||||
}
|
||||
string mode = utils::string::split_left(mixer_mode->value(), ':', settings.player.mixer.parameter);
|
||||
if (mode == "software")
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::software;
|
||||
else if ((mode == "hardware") && hw_mixer_supported)
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::hardware;
|
||||
else if (mode == "script")
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::script;
|
||||
else if (mode == "none")
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::none;
|
||||
else
|
||||
throw SnapException("Mixer mode not supported: " + mode);
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
// Construct a signal set registered for process termination.
|
||||
|
|
|
@ -61,7 +61,7 @@ x = 1,000016667 / (1,000016667 - 1)
|
|||
soxr_ = nullptr;
|
||||
if ((format_.rate() != in_format_.rate()) || (format_.bits() != in_format_.bits()))
|
||||
{
|
||||
LOG(INFO, LOG_TAG) << "Resampling from " << in_format_.getFormat() << " to " << format_.getFormat() << "\n";
|
||||
LOG(INFO, LOG_TAG) << "Resampling from " << in_format_.toString() << " to " << format_.toString() << "\n";
|
||||
soxr_error_t error;
|
||||
|
||||
soxr_datatype_t in_type = SOXR_INT16_I;
|
||||
|
|
|
@ -49,7 +49,7 @@ SampleFormat::SampleFormat(uint32_t sampleRate, uint16_t bitsPerSample, uint16_t
|
|||
}
|
||||
|
||||
|
||||
string SampleFormat::getFormat() const
|
||||
string SampleFormat::toString() const
|
||||
{
|
||||
stringstream ss;
|
||||
ss << rate_ << ":" << bits_ << ":" << channels_;
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
SampleFormat(const std::string& format);
|
||||
SampleFormat(uint32_t rate, uint16_t bits, uint16_t channels);
|
||||
|
||||
std::string getFormat() const;
|
||||
std::string toString() const;
|
||||
|
||||
void setFormat(const std::string& format);
|
||||
void setFormat(uint32_t rate, uint16_t bits, uint16_t channels);
|
||||
|
|
|
@ -220,7 +220,7 @@ void OggEncoder::initEncoder()
|
|||
vorbis_comment_init(&vc_);
|
||||
vorbis_comment_add_tag(&vc_, "TITLE", "SnapStream");
|
||||
vorbis_comment_add_tag(&vc_, "VERSION", VERSION);
|
||||
vorbis_comment_add_tag(&vc_, "SAMPLE_FORMAT", sampleFormat_.getFormat().c_str());
|
||||
vorbis_comment_add_tag(&vc_, "SAMPLE_FORMAT", sampleFormat_.toString().c_str());
|
||||
|
||||
/* set up the analysis state and auxiliary encoding storage */
|
||||
vorbis_analysis_init(&vd_, &vi_);
|
||||
|
|
|
@ -50,7 +50,7 @@ PcmStream::PcmStream(PcmListener* pcmListener, boost::asio::io_context& ioc, con
|
|||
if (uri_.query.find(kUriSampleFormat) == uri_.query.end())
|
||||
throw SnapException("Stream URI must have a sampleformat");
|
||||
sampleFormat_ = SampleFormat(uri_.query[kUriSampleFormat]);
|
||||
LOG(INFO, LOG_TAG) << "PcmStream sampleFormat: " << sampleFormat_.getFormat() << "\n";
|
||||
LOG(INFO, LOG_TAG) << "PcmStream sampleFormat: " << sampleFormat_.toString() << "\n";
|
||||
|
||||
if (uri_.query.find(kUriChunkMs) != uri_.query.end())
|
||||
chunk_ms_ = cpt::stoul(uri_.query[kUriChunkMs]);
|
||||
|
@ -97,7 +97,7 @@ const SampleFormat& PcmStream::getSampleFormat() const
|
|||
|
||||
void PcmStream::start()
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Start, sampleformat: " << sampleFormat_.getFormat() << "\n";
|
||||
LOG(DEBUG, LOG_TAG) << "Start, sampleformat: " << sampleFormat_.toString() << "\n";
|
||||
encoder_->init(this, sampleFormat_);
|
||||
active_ = true;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue