整理代码
This commit is contained in:
commit
c7b4e178ec
364 changed files with 59691 additions and 0 deletions
144
video/alsa/Input.cpp
Normal file
144
video/alsa/Input.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Input.h"
|
||||
#include "Util.h"
|
||||
#include "../core/System.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Input::Input(const std::string& name): Core::AbstractAudioInput(name)
|
||||
{
|
||||
setSoundCard(nullptr);
|
||||
}
|
||||
|
||||
Input::~Input()
|
||||
{
|
||||
}
|
||||
|
||||
void Input::start()
|
||||
{
|
||||
_established = tryEstablishSoundCard();
|
||||
run();
|
||||
}
|
||||
|
||||
void Input::stop()
|
||||
{
|
||||
cancel();
|
||||
if (_established)
|
||||
{
|
||||
_pcm.close();
|
||||
_established = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Input::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
if (!started())
|
||||
return;
|
||||
|
||||
stop();
|
||||
start();
|
||||
AbstractAudioInput::onConfigChanged(names);
|
||||
}
|
||||
|
||||
Core::Frame::Ptr Input::readFrame()
|
||||
{
|
||||
if (!_established)
|
||||
_established = tryEstablishSoundCard();
|
||||
|
||||
if (!_established)
|
||||
{
|
||||
_traffic = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Core::Frame::Ptr pFrame = new Core::Frame;
|
||||
pFrame->length = _linesize;
|
||||
pFrame->timestamp = Core::System::timestamp();
|
||||
int rc;
|
||||
|
||||
if (SND_PCM_ACCESS_RW_NONINTERLEAVED == _access)
|
||||
{
|
||||
uint8_t* ptrs[_channels];
|
||||
for (int i = 0; i < _channels; i++)
|
||||
{
|
||||
pFrame->data[i] = new uint8_t[_linesize];
|
||||
ptrs[i] = pFrame->data[i].get();
|
||||
}
|
||||
|
||||
rc = _pcm.read(ptrs, _samples);
|
||||
}
|
||||
else
|
||||
{
|
||||
pFrame->data[0] = new uint8_t[_linesize];
|
||||
uint8_t* ptr = pFrame->data[0].get();
|
||||
rc = _pcm.read(ptr, _samples);
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
_established = false;
|
||||
_pcm.close();
|
||||
_traffic = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_traffic++;
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
bool Input::tryEstablishSoundCard()
|
||||
{
|
||||
auto pCard = getSoundCard();
|
||||
if (pCard)
|
||||
{
|
||||
int rc = _pcm.open(pCard, SND_PCM_STREAM_CAPTURE);
|
||||
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);
|
||||
|
||||
_linesize = sampleFormatCalcLineSize(sampleFormat, getChannelLayout(), getFrameSize());
|
||||
_channels = channelLayoutChannels(getChannelLayout());
|
||||
_samples = getFrameSize();
|
||||
_access = access;
|
||||
|
||||
serialCodeManager().increment();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_pcm.close();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
video/alsa/Input.h
Normal file
53
video/alsa/Input.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef C0535B0C_9BD8_4FDC_9429_8FEF7E58BFCE
|
||||
#define C0535B0C_9BD8_4FDC_9429_8FEF7E58BFCE
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "../core/AbstractAudioInput.h"
|
||||
#include "SoundCard.h"
|
||||
#include "PCM.h"
|
||||
#include "util/SimpleFactory.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Input: public Core::AbstractAudioInput, public ::Util::SimpleFactory<Input, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<Input>;
|
||||
|
||||
public:
|
||||
Input(const std::string& name);
|
||||
~Input();
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(SoundCard, SoundCard::Ptr);
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
|
||||
Core::Frame::Ptr readFrame() override;
|
||||
|
||||
bool tryEstablishSoundCard();
|
||||
|
||||
private:
|
||||
PCM _pcm;
|
||||
std::atomic<bool> _established;
|
||||
int _linesize;
|
||||
int _channels;
|
||||
int _samples;
|
||||
snd_pcm_access_t _access;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
162
video/alsa/Output.cpp
Normal file
162
video/alsa/Output.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
50
video/alsa/Output.h
Normal file
50
video/alsa/Output.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef A7586F47_A0A8_4B55_9701_8E79240D6736
|
||||
#define A7586F47_A0A8_4B55_9701_8E79240D6736
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "../core/AudioOutput.h"
|
||||
#include "PCM.h"
|
||||
#include "SoundCard.h"
|
||||
#include "util/Task.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Output: public Core::AudioOutput
|
||||
{
|
||||
public:
|
||||
Output(const std::string& arg);
|
||||
~Output();
|
||||
|
||||
protected:
|
||||
void initialize() override;
|
||||
void deinitialize() override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
void writeFrame(Core::Frame::Ptr pFrame) override;
|
||||
|
||||
SoundCard::Ptr getSoundCard();
|
||||
bool tryEstablishSoundCard();
|
||||
void runSearchTask();
|
||||
|
||||
PROPERTY_DECLARE(SoundCardEstablished, bool, false, protected, protected)
|
||||
|
||||
private:
|
||||
std::string _soundCardID;
|
||||
::Util::Task _searchTask;
|
||||
PCM _pcm;
|
||||
snd_pcm_access_t _access;
|
||||
int _samples;
|
||||
int _channels;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
144
video/alsa/PCM.cpp
Normal file
144
video/alsa/PCM.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "PCM.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
PCM::PCM()
|
||||
{
|
||||
}
|
||||
|
||||
PCM::~PCM()
|
||||
{
|
||||
}
|
||||
|
||||
int PCM::open(SoundCard::Ptr pSoundCard, snd_pcm_stream_t stream)
|
||||
{
|
||||
_stream = stream;
|
||||
return snd_pcm_open((snd_pcm_t**)&_pPCM, pSoundCard->info().device.c_str(), stream, 0);
|
||||
}
|
||||
|
||||
int PCM::close()
|
||||
{
|
||||
snd_pcm_drain((snd_pcm_t*)_pPCM);
|
||||
snd_pcm_close((snd_pcm_t*)_pPCM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PCM::config(snd_pcm_access_t access, snd_pcm_format_t format, int channels, int rate, int framesz, int samplesz)
|
||||
{
|
||||
#define __check_error(x) \
|
||||
rc = x; \
|
||||
if (rc < 0) \
|
||||
{ \
|
||||
std::cout << "Error: " << snd_strerror(rc) << "@" << #x << std::endl; \
|
||||
goto out; \
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_t* hwParams = nullptr;
|
||||
snd_pcm_sw_params_t* swParams = nullptr;
|
||||
int rc;
|
||||
|
||||
__check_error(snd_pcm_hw_params_malloc(&hwParams));
|
||||
|
||||
__check_error(snd_pcm_hw_params_any((snd_pcm_t*)_pPCM, hwParams));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_access((snd_pcm_t*)_pPCM, hwParams, access));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_format((snd_pcm_t*)_pPCM, hwParams, format));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_channels((snd_pcm_t*)_pPCM, hwParams, channels));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_rate((snd_pcm_t*)_pPCM, hwParams, rate, 0));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_period_size((snd_pcm_t*)_pPCM, hwParams, framesz, 0));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_periods((snd_pcm_t*)_pPCM, hwParams, samplesz, 0));
|
||||
|
||||
__check_error(snd_pcm_hw_params_set_buffer_size((snd_pcm_t*)_pPCM, hwParams, samplesz * framesz));
|
||||
|
||||
__check_error(snd_pcm_hw_params((snd_pcm_t*)_pPCM, hwParams));
|
||||
|
||||
if (_stream == SND_PCM_STREAM_PLAYBACK)
|
||||
{
|
||||
__check_error(snd_pcm_sw_params_malloc(&swParams));
|
||||
__check_error(snd_pcm_sw_params_current((snd_pcm_t*)_pPCM, swParams));
|
||||
__check_error(snd_pcm_sw_params_set_avail_min((snd_pcm_t*)_pPCM, swParams, framesz));
|
||||
|
||||
__check_error(snd_pcm_sw_params_set_start_threshold((snd_pcm_t*)_pPCM, swParams, framesz * samplesz));
|
||||
__check_error(snd_pcm_sw_params((snd_pcm_t*)_pPCM, swParams));
|
||||
}
|
||||
|
||||
out:
|
||||
if (hwParams)
|
||||
snd_pcm_hw_params_free(hwParams);
|
||||
|
||||
if (swParams)
|
||||
snd_pcm_sw_params_free(swParams);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int PCM::write(const void* data, uint32_t framesz)
|
||||
{
|
||||
int rc = snd_pcm_writei((snd_pcm_t*)_pPCM, data, framesz);
|
||||
if (rc < 0)
|
||||
{
|
||||
if (rc == -EPIPE)
|
||||
rc = snd_pcm_prepare((snd_pcm_t*)_pPCM);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int PCM::write(void** datas, uint32_t framesz)
|
||||
{
|
||||
int rc = snd_pcm_writen((snd_pcm_t*)_pPCM, datas, framesz);
|
||||
if (rc < 0)
|
||||
{
|
||||
if (rc == -EPIPE)
|
||||
rc = snd_pcm_prepare((snd_pcm_t*)_pPCM);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int PCM::read(void* buffer, uint32_t framesz)
|
||||
{
|
||||
int rc = snd_pcm_readi((snd_pcm_t*)_pPCM, buffer, (snd_pcm_uframes_t)framesz);
|
||||
if (rc < 0)
|
||||
{
|
||||
std::cout << "ALSA error: " << rc << " -> " << snd_strerror(rc) << "@" << framesz << std::endl;
|
||||
if (rc == -EPIPE)
|
||||
rc = snd_pcm_prepare((snd_pcm_t*)_pPCM);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int PCM::read(void** buffers, uint32_t framesz)
|
||||
{
|
||||
int rc = snd_pcm_readn((snd_pcm_t*)_pPCM, buffers, (snd_pcm_uframes_t)framesz);
|
||||
if (rc < 0)
|
||||
{
|
||||
std::cout << "ALSA error: " << rc << " -> " << snd_strerror(rc) << "@" << framesz << std::endl;
|
||||
if (rc == -EPIPE)
|
||||
rc = snd_pcm_prepare((snd_pcm_t*)_pPCM);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
video/alsa/PCM.h
Normal file
43
video/alsa/PCM.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef F1145346_5CCE_48F8_8398_073AFB8758C6
|
||||
#define F1145346_5CCE_48F8_8398_073AFB8758C6
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "SoundCard.h"
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class PCM
|
||||
{
|
||||
public:
|
||||
PCM();
|
||||
~PCM();
|
||||
|
||||
public:
|
||||
int open(SoundCard::Ptr pSoundCard, snd_pcm_stream_t st);
|
||||
int close();
|
||||
|
||||
int config(snd_pcm_access_t access, snd_pcm_format_t format, int channels, int rate, int framesz, int samplesz);
|
||||
|
||||
int write(const void* data, uint32_t framesz);
|
||||
int write(void** datas, uint32_t framesz);
|
||||
int read(void* buffer, uint32_t framesz);
|
||||
int read(void** buffers, uint32_t framesz);
|
||||
|
||||
private:
|
||||
void* _pPCM;
|
||||
snd_pcm_stream_t _stream;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
124
video/alsa/SoundCard.cpp
Normal file
124
video/alsa/SoundCard.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "SoundCard.h"
|
||||
#include <Poco/Format.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
SoundCard::SoundCard(int index): _index(index)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
SoundCard::~SoundCard()
|
||||
{
|
||||
}
|
||||
|
||||
void SoundCard::initialize()
|
||||
{
|
||||
_info.device = Poco::format("hw:%d", _index);
|
||||
int rc;
|
||||
snd_ctl_t* handle;
|
||||
if ((rc = snd_ctl_open(&handle, _info.device.c_str(), 0)) < 0)
|
||||
throw Poco::InvalidAccessException("SoundCard: Cannot open device");
|
||||
|
||||
snd_ctl_card_info_t* info;
|
||||
snd_ctl_card_info_alloca(&info);
|
||||
if ((rc = snd_ctl_card_info(handle, info)) < 0)
|
||||
throw Poco::InvalidAccessException("SoundCard: Cannot get card info");
|
||||
|
||||
_info.id = snd_ctl_card_info_get_id(info);
|
||||
_info.name = snd_ctl_card_info_get_name(info);
|
||||
_info.longname = snd_ctl_card_info_get_longname(info);
|
||||
_info.driver = snd_ctl_card_info_get_driver(info);
|
||||
_info.components = snd_ctl_card_info_get_components(info);
|
||||
_info.mixername = snd_ctl_card_info_get_mixername(info);
|
||||
|
||||
snd_ctl_close(handle);
|
||||
|
||||
auto enumerateCapabilities = [](snd_pcm_t* pcm, Info& info)
|
||||
{
|
||||
snd_pcm_hw_params_t* params;
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
snd_pcm_hw_params_any(pcm, params);
|
||||
|
||||
for (int access = 0; access <= SND_PCM_ACCESS_LAST; access++)
|
||||
info.access[access] = (snd_pcm_hw_params_test_access(pcm, params, (snd_pcm_access_t)access) == 0);
|
||||
|
||||
for (int format = 0; format <= SND_PCM_FORMAT_LAST; format++)
|
||||
{
|
||||
if (snd_pcm_hw_params_test_format(pcm, params, (snd_pcm_format_t)format) == 0)
|
||||
info.formats.push_back((snd_pcm_format_t)format);
|
||||
}
|
||||
|
||||
for (unsigned int rate = 1000; rate <= 384000; rate += 1000)
|
||||
{
|
||||
if (snd_pcm_hw_params_test_rate(pcm, params, rate, 0) == 0)
|
||||
info.sampleRates.push_back(rate);
|
||||
}
|
||||
|
||||
for (int channels = 1; channels <= 16; channels++)
|
||||
{
|
||||
if (snd_pcm_hw_params_test_channels(pcm, params, channels) == 0)
|
||||
{
|
||||
info.channels = channels;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (snd_pcm_uframes_t val = 16; val <= 1048576; val *= 2)
|
||||
{
|
||||
if (snd_pcm_hw_params_test_buffer_size(pcm, params, val) == 0)
|
||||
info.bufferSizes.push_back(val);
|
||||
}
|
||||
};
|
||||
|
||||
snd_pcm_t* pcm;
|
||||
int err = snd_pcm_open(&pcm, _info.device.c_str(), SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (err < 0)
|
||||
_info.supportCapture = false;
|
||||
else
|
||||
{
|
||||
_info.supportCapture = true;
|
||||
enumerateCapabilities(pcm, _info);
|
||||
snd_pcm_close(pcm);
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&pcm, _info.device.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (err < 0)
|
||||
_info.supportPlayback = false;
|
||||
else
|
||||
{
|
||||
_info.supportPlayback = true;
|
||||
if (!_info.supportCapture)
|
||||
enumerateCapabilities(pcm, _info);
|
||||
snd_pcm_close(pcm);
|
||||
}
|
||||
}
|
||||
|
||||
SoundCard::Info& SoundCard::info()
|
||||
{
|
||||
return _info;
|
||||
}
|
||||
|
||||
SoundCard::SoundCards SoundCard::getSoundCards()
|
||||
{
|
||||
SoundCards cards;
|
||||
int id = -1;
|
||||
while (snd_card_next(&id), id >= 0)
|
||||
cards.push_back(new SoundCard(id));
|
||||
|
||||
return cards;
|
||||
}
|
||||
}
|
||||
}
|
||||
83
video/alsa/SoundCard.h
Normal file
83
video/alsa/SoundCard.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef A83739DB_6BC9_4B7C_8FA0_11DC60C05AA3
|
||||
#define A83739DB_6BC9_4B7C_8FA0_11DC60C05AA3
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class SoundCard
|
||||
{
|
||||
public:
|
||||
struct Info
|
||||
{
|
||||
std::string id;
|
||||
std::string device;
|
||||
std::string name;
|
||||
std::string longname;
|
||||
std::string driver;
|
||||
std::string components;
|
||||
std::string mixername;
|
||||
bool access[SND_PCM_ACCESS_LAST + 1];
|
||||
std::vector<snd_pcm_format_t> formats;
|
||||
int channels;
|
||||
std::vector<uint32_t> sampleRates;
|
||||
std::vector<uint32_t> bufferSizes;
|
||||
bool supportCapture;
|
||||
bool supportPlayback;
|
||||
|
||||
bool operator==(const Info& other)
|
||||
{
|
||||
return components == other.components;
|
||||
}
|
||||
};
|
||||
|
||||
using Ptr = Poco::SharedPtr<SoundCard>;
|
||||
using SoundCards = std::vector<Ptr>;
|
||||
|
||||
private:
|
||||
SoundCard(int index);
|
||||
void initialize();
|
||||
|
||||
public:
|
||||
~SoundCard();
|
||||
|
||||
Info& info();
|
||||
|
||||
private:
|
||||
int _index;
|
||||
Info _info;
|
||||
|
||||
public:
|
||||
static SoundCards getSoundCards();
|
||||
|
||||
template <class F> static SoundCards getSoundCards(F&& filter);
|
||||
};
|
||||
|
||||
template <class F> inline SoundCard::SoundCards SoundCard::getSoundCards(F&& filter)
|
||||
{
|
||||
SoundCard::SoundCards cards;
|
||||
for (auto& card : ALSA::SoundCard::getSoundCards())
|
||||
{
|
||||
if (filter(card))
|
||||
cards.push_back(card);
|
||||
}
|
||||
|
||||
return cards;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
154
video/alsa/Util.cpp
Normal file
154
video/alsa/Util.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Util.h"
|
||||
#include "Logger.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
uint32_t Util::getSuitableSampleRate(SoundCard::Ptr pCard, uint32_t target)
|
||||
{
|
||||
uint32_t suitable = target;
|
||||
if (!pCard->info().sampleRates.empty())
|
||||
{
|
||||
for (auto sr : pCard->info().sampleRates)
|
||||
{
|
||||
if (sr % target == 0 || target % sr == 0)
|
||||
{
|
||||
suitable = sr;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
int minDiff = __INT32_MAX__;
|
||||
for (auto sr : pCard->info().sampleRates)
|
||||
{
|
||||
int diff = std::abs(static_cast<int>(sr - target));
|
||||
if (diff < minDiff || (diff == minDiff && sr > suitable))
|
||||
{
|
||||
minDiff = diff;
|
||||
suitable = sr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return suitable;
|
||||
}
|
||||
|
||||
snd_pcm_format_t Util::getSuitableSampleFormat(SoundCard::Ptr pCard, Core::SampleFormat refer)
|
||||
{
|
||||
auto target = convertSampleFormat(refer);
|
||||
if (pCard->info().formats.empty())
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
|
||||
for (auto& fmt : pCard->info().formats)
|
||||
{
|
||||
if (fmt == (int)target)
|
||||
return target;
|
||||
}
|
||||
|
||||
target = *(pCard->info().formats.begin());
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
snd_pcm_access_t Util::getSuitableAccess(SoundCard::Ptr pCard, bool planar)
|
||||
{
|
||||
if (planar && (pCard->info().access[SND_PCM_ACCESS_RW_NONINTERLEAVED]))
|
||||
return SND_PCM_ACCESS_RW_NONINTERLEAVED;
|
||||
|
||||
return SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
}
|
||||
|
||||
snd_pcm_format_t Util::convertSampleFormat(Core::SampleFormat format)
|
||||
{
|
||||
switch (sampleFormatGetPlanarFormat(format))
|
||||
{
|
||||
case Core::SampleFormat::U8P:
|
||||
return SND_PCM_FORMAT_U8;
|
||||
case Core::SampleFormat::S16P:
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
case Core::SampleFormat::S32P:
|
||||
return SND_PCM_FORMAT_S32_LE;
|
||||
case Core::SampleFormat::FLTP:
|
||||
return SND_PCM_FORMAT_FLOAT_LE;
|
||||
case Core::SampleFormat::DBLP:
|
||||
return SND_PCM_FORMAT_FLOAT64_LE;
|
||||
default:
|
||||
return SND_PCM_FORMAT_S16_LE;
|
||||
}
|
||||
}
|
||||
|
||||
Core::SampleFormat Util::convertSampleFormat(snd_pcm_format_t format, bool interleaved)
|
||||
{
|
||||
Core::SampleFormat result;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case SND_PCM_FORMAT_U8:
|
||||
result = Core::SampleFormat::U8P;
|
||||
break;
|
||||
case SND_PCM_FORMAT_S8:
|
||||
result = Core::SampleFormat::U8P;
|
||||
break;
|
||||
case SND_PCM_FORMAT_S16_LE:
|
||||
result = Core::SampleFormat::S16P;
|
||||
break;
|
||||
case SND_PCM_FORMAT_S16_BE:
|
||||
result = Core::SampleFormat::S16P;
|
||||
break;
|
||||
case SND_PCM_FORMAT_S32_LE:
|
||||
result = Core::SampleFormat::S32P;
|
||||
break;
|
||||
case SND_PCM_FORMAT_S32_BE:
|
||||
result = Core::SampleFormat::S32P;
|
||||
break;
|
||||
case SND_PCM_FORMAT_FLOAT_LE:
|
||||
result = Core::SampleFormat::FLTP;
|
||||
break;
|
||||
case SND_PCM_FORMAT_FLOAT_BE:
|
||||
result = Core::SampleFormat::FLTP;
|
||||
break;
|
||||
case SND_PCM_FORMAT_FLOAT64_LE:
|
||||
result = Core::SampleFormat::DBLP;
|
||||
break;
|
||||
case SND_PCM_FORMAT_FLOAT64_BE:
|
||||
result = Core::SampleFormat::DBLP;
|
||||
break;
|
||||
|
||||
case SND_PCM_FORMAT_SPECIAL:
|
||||
case SND_PCM_FORMAT_S24_3LE:
|
||||
case SND_PCM_FORMAT_S24_3BE:
|
||||
case SND_PCM_FORMAT_U24_3LE:
|
||||
case SND_PCM_FORMAT_U24_3BE:
|
||||
case SND_PCM_FORMAT_S20_3LE:
|
||||
case SND_PCM_FORMAT_S20_3BE:
|
||||
case SND_PCM_FORMAT_U20_3LE:
|
||||
case SND_PCM_FORMAT_U20_3BE:
|
||||
case SND_PCM_FORMAT_S18_3LE:
|
||||
case SND_PCM_FORMAT_S18_3BE:
|
||||
case SND_PCM_FORMAT_U18_3LE:
|
||||
default:
|
||||
logger().warning(": Unsupported format: %s", std::string(snd_pcm_format_name(format)));
|
||||
result = Core::SampleFormat::S32P;
|
||||
break;
|
||||
}
|
||||
|
||||
if (interleaved)
|
||||
result = Core::sampleFormatGetPackedFormat(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
video/alsa/Util.h
Normal file
31
video/alsa/Util.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef C0D42E11_A47F_4E62_820B_17AD3C7FE57C
|
||||
#define C0D42E11_A47F_4E62_820B_17AD3C7FE57C
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "SoundCard.h"
|
||||
#include "../core/SampleFormat.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace ALSA
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Util
|
||||
{
|
||||
public:
|
||||
static uint32_t getSuitableSampleRate(SoundCard::Ptr pCard, uint32_t target);
|
||||
static snd_pcm_format_t getSuitableSampleFormat(SoundCard::Ptr pCard, Core::SampleFormat refer);
|
||||
static snd_pcm_access_t getSuitableAccess(SoundCard::Ptr pCard, bool planar);
|
||||
static snd_pcm_format_t convertSampleFormat(Core::SampleFormat format);
|
||||
static Core::SampleFormat convertSampleFormat(snd_pcm_format_t format, bool interleaved);
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue