/* ******************************************************************************* * INCLUDE ******************************************************************************* */ #include "Output.h" #include "PCM.h" #include "SoundCard.h" #include "Util.h" #include "Logger.h" #include "../core/System.h" namespace Video { namespace ALSA { /* ******************************************************************************* * CLASS DEFINE ******************************************************************************* */ Output::Output(const std::string& arg): Core::AudioOutput("ALSA::Output(" + arg + ")") { auto pos = arg.find(':'); _soundCardID = (pos != std::string::npos) ? arg.substr(pos + 1) : arg; } Output::~Output() { } void Output::initialize() { setSoundCardEstablished(tryEstablishSoundCard()); if (!getSoundCardEstablished()) runSearchTask(); } void Output::deinitialize() { _searchTask.cancel(); if (getSoundCardEstablished()) _pcm.close(); } void Output::onConfigChanged(ParameterNames& names) { } void Output::writeFrame(Core::Frame::Ptr pFrame) { if (!getSoundCardEstablished()) return; int rc; if (SND_PCM_ACCESS_RW_NONINTERLEAVED == _access) { uint8_t* ptrs[_channels]; for (int i = 0; i < _channels; i++) ptrs[i] = pFrame->data[i].get(); rc = _pcm.write(ptrs, _samples); } else { rc = _pcm.write(pFrame->data[0].get(), _samples); } if (rc < 0) { setSoundCardEstablished(false); _pcm.close(); } } SoundCard::Ptr Output::getSoundCard() { auto cards = SoundCard::getSoundCards([this](SoundCard::Ptr pCard) -> bool { return (pCard->info().components == _soundCardID); }); if (cards.size() > 0) return *(cards.begin()); return nullptr; } bool Output::tryEstablishSoundCard() { try { auto pCard = getSoundCard(); if (pCard) { int rc = _pcm.open(pCard, SND_PCM_STREAM_PLAYBACK); if (rc >= 0) { const auto sampleRate = Util::getSuitableSampleRate(pCard, getSampleRate()); const auto access = Util::getSuitableAccess(pCard, sampleFormatIsPlanar(getSampleFormat())); const auto format = Util::getSuitableSampleFormat(pCard, getSampleFormat()); const auto sampleFormat = Util::convertSampleFormat(format, access == SND_PCM_ACCESS_RW_INTERLEAVED); logger().debug("%s: Suitable config found: %s %s %uHz", name(), std::string(snd_pcm_access_name(access)), std::string(snd_pcm_format_name(format)), sampleRate); rc = _pcm.config(access, format, pCard->info().channels, sampleRate, getFrameSize(), sampleFormatBytesPerSample(sampleFormat)); if (rc >= 0) { if (pCard->info().channels == 1 && getChannelLayout() != Core::ChannelLayout::MONO) setChannelLayout(Core::ChannelLayout::MONO); setSampleRate(sampleRate); setSampleFormat(sampleFormat); _channels = channelLayoutChannels(getChannelLayout()); _samples = getFrameSize(); _access = access; serialCodeManager().increment(); return true; } _pcm.close(); } } } catch (Poco::Exception& e) { logger().error("%s: %s", name(), e.displayText()); } catch (std::exception& e) { logger().error("%s: %s", name(), std::string(e.what())); } return false; } void Output::runSearchTask() { if (_searchTask.isRunning()) return; _searchTask.run( [this](std::atomic& isCancelled) -> void { while (!isCancelled) { if (tryEstablishSoundCard()) { setSoundCardEstablished(true); break; } Poco::Thread::sleep(2000); } }); } } }