162 lines
4.3 KiB
C++
162 lines
4.3 KiB
C++
/*
|
|
*******************************************************************************
|
|
* 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<bool>& isCancelled) -> void
|
|
{
|
|
while (!isCancelled)
|
|
{
|
|
if (tryEstablishSoundCard())
|
|
{
|
|
setSoundCardEstablished(true);
|
|
break;
|
|
}
|
|
|
|
Poco::Thread::sleep(2000);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|