284 lines
7.9 KiB
C++
284 lines
7.9 KiB
C++
/*
|
|
*******************************************************************************
|
|
* INCLUDE
|
|
*******************************************************************************
|
|
*/
|
|
#include "AbstractAudioInput.h"
|
|
#include "System.h"
|
|
#include "Logger.h"
|
|
#include "util/Math.h"
|
|
#include <atomic>
|
|
#include <climits>
|
|
#include <cmath>
|
|
#include <unistd.h>
|
|
|
|
namespace Video
|
|
{
|
|
namespace Core
|
|
{
|
|
/*
|
|
*******************************************************************************
|
|
* CLASS DEFINE
|
|
*******************************************************************************
|
|
*/
|
|
AbstractAudioInput::AbstractAudioInput(const std::string& name)
|
|
: AudioComponent(name)
|
|
{
|
|
const auto len = 1024 * 32;
|
|
_pSilentData = new uint8_t[len];
|
|
memset(_pSilentData, 0x00, len);
|
|
|
|
_silentDataCode = serialCodeManager().code();
|
|
_pSilentFrame = new Frame;
|
|
|
|
for (int i = 0; i < Frame::DATA_LINES; i++)
|
|
_pSilentFrame->data[i] = _pSilentData;
|
|
}
|
|
|
|
AbstractAudioInput::~AbstractAudioInput()
|
|
{
|
|
}
|
|
|
|
void AbstractAudioInput::run()
|
|
{
|
|
updateSilentFrame();
|
|
|
|
_task.run(
|
|
[this](std::atomic<bool>& isCancelled) -> void
|
|
{
|
|
Util::Task probeTask;
|
|
std::atomic<bool> attached(true);
|
|
setSignalLost(false);
|
|
|
|
Frame::Ptr pFrame;
|
|
SerialCodeWatcher watcher;
|
|
uint64_t timeline = 0;
|
|
while (!isCancelled)
|
|
{
|
|
if (attached)
|
|
{
|
|
pFrame = tryReadFrame(true);
|
|
if (pFrame.isNull())
|
|
{
|
|
probeTask.run(
|
|
[&](std::atomic<bool>& isCancelled) -> void
|
|
{
|
|
while (!isCancelled)
|
|
{
|
|
if (!tryReadFrame(false).isNull())
|
|
{
|
|
attached = true;
|
|
setSignalLost(false);
|
|
break;
|
|
}
|
|
|
|
Poco::Thread::sleep(2000);
|
|
}
|
|
});
|
|
|
|
attached = false;
|
|
setSignalLost(true);
|
|
continue;
|
|
}
|
|
|
|
const int gain = _gain.load(std::memory_order_relaxed);
|
|
if (gain != 0)
|
|
applyGain(pFrame, gain);
|
|
|
|
const int64_t ptsOffset = _ptsOffsetUs.load(std::memory_order_relaxed);
|
|
if (ptsOffset != 0)
|
|
applyPTSOffset(pFrame, ptsOffset);
|
|
|
|
pFrame->code = serialCodeManager().code();
|
|
if (watcher.isChanged(pFrame->code, true))
|
|
{
|
|
updateSilentFrame();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFrame = readSilent(false);
|
|
}
|
|
|
|
if (pFrame->timestamp < timeline)
|
|
pFrame->timestamp = timeline;
|
|
else
|
|
timeline = pFrame->timestamp;
|
|
|
|
if (_isMute)
|
|
pFrame = readSilent(true);
|
|
|
|
postToNextComponents(sharedFromThis(), pFrame, MediaType::AUDIO, isCancelled);
|
|
}
|
|
|
|
probeTask.cancel();
|
|
});
|
|
}
|
|
|
|
void AbstractAudioInput::cancel()
|
|
{
|
|
_task.cancel();
|
|
}
|
|
|
|
void AbstractAudioInput::onConfigChanged(ParameterNames& names)
|
|
{
|
|
updateSilentFrame();
|
|
if (names.exists(_Gain))
|
|
_gain.store(getGain(), std::memory_order_relaxed);
|
|
|
|
if (names.exists(_PTSOffset))
|
|
_ptsOffsetUs.store(getPTSOffset(), std::memory_order_relaxed);
|
|
}
|
|
|
|
Frame::Ptr AbstractAudioInput::tryReadFrame(bool shouldLog)
|
|
{
|
|
try
|
|
{
|
|
return readFrame();
|
|
}
|
|
catch (Poco::Exception& e)
|
|
{
|
|
if (shouldLog)
|
|
logger().error("%s: %s", name(), e.displayText());
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
if (shouldLog)
|
|
logger().error("%s: %s", name(), std::string(e.what()));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Frame::Ptr AbstractAudioInput::readSilent(bool freezeChannel)
|
|
{
|
|
uint64_t now = System::timestamp();
|
|
_pSilentFrame->timestamp = now;
|
|
_pSilentFrame->code = _silentDataCode;
|
|
if (!freezeChannel)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::microseconds(_duration));
|
|
}
|
|
|
|
return _pSilentFrame;
|
|
}
|
|
|
|
void AbstractAudioInput::updateSilentFrame()
|
|
{
|
|
_pSilentFrame->length = Core::sampleFormatCalcLineSize(getSampleFormat(), getChannelLayout(), getFrameSize());
|
|
_duration = (float)1000000 / getSampleRate() * getFrameSize() * 0.85;
|
|
_silentDataCode = serialCodeManager().code();
|
|
}
|
|
|
|
void AbstractAudioInput::applyGain(Frame::Ptr pFrame, int gain)
|
|
{
|
|
gain = ::Util::Math::clamp(gain, -50, 20);
|
|
if (gain == 0)
|
|
return;
|
|
|
|
const auto format = getSampleFormat();
|
|
if (format == SampleFormat::NONE)
|
|
return;
|
|
|
|
const uint32_t channels = (uint32_t)channelLayoutChannels(getChannelLayout());
|
|
const uint32_t samplesPerChannel = getFrameSize();
|
|
const double scale = std::pow(10.0, (double)gain / 20.0);
|
|
|
|
auto apply = [&](uint8_t* base, uint32_t n)
|
|
{
|
|
switch (format)
|
|
{
|
|
case SampleFormat::U8:
|
|
case SampleFormat::U8P:
|
|
{
|
|
auto p = (uint8_t*)base;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
{
|
|
const int64_t v = (int64_t)(p[i] * scale + 0.5);
|
|
p[i] = (uint8_t)::Util::Math::clamp(v, (int64_t)0, (int64_t)255);
|
|
}
|
|
break;
|
|
}
|
|
case SampleFormat::S16:
|
|
case SampleFormat::S16P:
|
|
{
|
|
auto p = (int16_t*)base;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
{
|
|
const int64_t v = (int64_t)(p[i] * scale + 0.5);
|
|
p[i] = (int16_t)::Util::Math::clamp(v, (int64_t)INT16_MIN, (int64_t)INT16_MAX);
|
|
}
|
|
break;
|
|
}
|
|
case SampleFormat::S32:
|
|
case SampleFormat::S32P:
|
|
{
|
|
auto p = (int32_t*)base;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
{
|
|
const int64_t v = (int64_t)(p[i] * scale + 0.5);
|
|
p[i] = (int32_t)::Util::Math::clamp(v, (int64_t)INT32_MIN, (int64_t)INT32_MAX);
|
|
}
|
|
break;
|
|
}
|
|
case SampleFormat::S64:
|
|
case SampleFormat::S64P:
|
|
{
|
|
auto p = (int64_t*)base;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
p[i] = (int64_t)(p[i] * scale);
|
|
break;
|
|
}
|
|
case SampleFormat::FLT:
|
|
case SampleFormat::FLTP:
|
|
{
|
|
auto p = (float*)base;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
p[i] = (float)(p[i] * scale);
|
|
break;
|
|
}
|
|
case SampleFormat::DBL:
|
|
case SampleFormat::DBLP:
|
|
{
|
|
auto p = (double*)base;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
p[i] *= scale;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
if (sampleFormatIsPlanar(format))
|
|
{
|
|
for (uint32_t ch = 0; ch < channels; ch++)
|
|
{
|
|
if (!pFrame->data[ch])
|
|
continue;
|
|
apply(pFrame->data[ch].get(), samplesPerChannel);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!pFrame->data[0])
|
|
return;
|
|
apply(pFrame->data[0].get(), samplesPerChannel * channels);
|
|
}
|
|
}
|
|
|
|
void AbstractAudioInput::applyPTSOffset(Frame::Ptr pFrame, int64_t ptsOffset)
|
|
{
|
|
if (ptsOffset != 0)
|
|
{
|
|
const int64_t ts = (int64_t)pFrame->timestamp + ptsOffset;
|
|
pFrame->timestamp = (ts > 0) ? (uint64_t)ts : 0ULL;
|
|
}
|
|
}
|
|
|
|
void AbstractAudioInput::reset()
|
|
{
|
|
}
|
|
|
|
}
|
|
}
|