Add sharing mode for Oboe, log device settings

This commit is contained in:
badaix 2020-05-05 21:46:21 +02:00
parent ad2351a42b
commit 424487a48e
14 changed files with 101 additions and 49 deletions

View file

@ -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;
};

View file

@ -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");

View file

@ -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);

View file

@ -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;

View file

@ -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";

View file

@ -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;

View file

@ -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())

View file

@ -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";
}

View file

@ -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.

View file

@ -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;

View file

@ -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_;

View file

@ -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);

View file

@ -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_);

View file

@ -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;
}