整理代码
This commit is contained in:
commit
c7b4e178ec
364 changed files with 59691 additions and 0 deletions
284
video/core/AbstractAudioInput.cpp
Normal file
284
video/core/AbstractAudioInput.cpp
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* 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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
56
video/core/AbstractAudioInput.h
Normal file
56
video/core/AbstractAudioInput.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef E2E4239C_C93E_478C_AD4C_D74AC3D4ED3E
|
||||
#define E2E4239C_C93E_478C_AD4C_D74AC3D4ED3E
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioComponent.h"
|
||||
#include "Frame.h"
|
||||
#include "util/Task.h"
|
||||
#include "util/Broadcast.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AbstractAudioInput: public AudioComponent
|
||||
{
|
||||
public:
|
||||
AbstractAudioInput(const std::string& name);
|
||||
~AbstractAudioInput();
|
||||
void reset() override;
|
||||
|
||||
protected:
|
||||
virtual Frame::Ptr readFrame() = 0;
|
||||
void run();
|
||||
void cancel();
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
void applyGain(Frame::Ptr pFrame, int gain);
|
||||
void applyPTSOffset(Frame::Ptr pFrame, int64_t ptsOffset);
|
||||
|
||||
private:
|
||||
Frame::Ptr tryReadFrame(bool shuldLog = false);
|
||||
Frame::Ptr readSilent(bool freezeChannel);
|
||||
void updateSilentFrame();
|
||||
|
||||
private:
|
||||
Util::Task _task;
|
||||
Frame::Ptr _pSilentFrame;
|
||||
Poco::SharedPtr<uint8_t> _pSilentData;
|
||||
SerialCode _silentDataCode;
|
||||
uint64_t _duration;
|
||||
uint32_t _audioChannelId;
|
||||
bool _isMute;
|
||||
std::atomic<int> _gain{0};
|
||||
std::atomic<int64_t> _ptsOffsetUs{0};
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
83
video/core/AudioAdaptablePipe.cpp
Normal file
83
video/core/AudioAdaptablePipe.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioAdaptablePipe.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
void AudioAdaptablePipe::onLink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
if (!frameQueues().empty() && frameQueues().firstComponent() != pPrevious)
|
||||
throw Poco::LogicException(name() + ": Support only one previous component, conflict with: " +
|
||||
pPrevious->name() + ", Current: " + frameQueues().firstComponent()->name());
|
||||
|
||||
frameQueues().add(pPrevious, name(), MediaType::AUDIO);
|
||||
}
|
||||
|
||||
void AudioAdaptablePipe::onUnlink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
frameQueues().remove(pPrevious);
|
||||
}
|
||||
|
||||
Frame::Ptr AudioAdaptablePipe::readFrame(uint32_t timeout)
|
||||
{
|
||||
if (previousComponents().size() == 0)
|
||||
throw Poco::NotFoundException(name() + ": No previous components");
|
||||
|
||||
auto pFrameQueue = frameQueues().first();
|
||||
while (true)
|
||||
{
|
||||
Frame::Ptr pFrame = nullptr;
|
||||
if (!_pFrameAdapter.isNull())
|
||||
{
|
||||
pFrame = _pFrameAdapter->get();
|
||||
if (!pFrame.isNull())
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
pFrame = pFrameQueue->dequeue(timeout);
|
||||
if (serialCodeWatcher().isChanged(pFrame->code, true))
|
||||
{
|
||||
auto pPrevious = previousComponents().first().cast<AudioComponent>();
|
||||
|
||||
auto inLayout = pPrevious->getChannelLayout();
|
||||
auto inFormat = pPrevious->getSampleFormat();
|
||||
auto inRate = pPrevious->getSampleRate();
|
||||
auto outLayout = getChannelLayout();
|
||||
auto outFormat = getSampleFormat();
|
||||
auto outRate = getSampleRate();
|
||||
|
||||
if (inLayout != outLayout || inFormat != outFormat || inRate != outRate)
|
||||
_pResample = AudioResample::create(inLayout, inFormat, inRate, outLayout, outFormat, outRate);
|
||||
else
|
||||
_pResample.reset();
|
||||
|
||||
if (inRate != outRate || pPrevious->getFrameSize() != getFrameSize())
|
||||
_pFrameAdapter = new AudioFrameAdapter(outLayout, outFormat, getFrameSize(), outRate);
|
||||
else
|
||||
_pFrameAdapter.reset();
|
||||
}
|
||||
|
||||
if (_pFrameAdapter.isNull() && _pResample.isNull())
|
||||
return pFrame;
|
||||
|
||||
if (!_pResample.isNull())
|
||||
pFrame = _pResample->process(pFrame);
|
||||
|
||||
if (!_pFrameAdapter.isNull())
|
||||
_pFrameAdapter->put(pFrame);
|
||||
else
|
||||
return pFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
video/core/AudioAdaptablePipe.h
Normal file
38
video/core/AudioAdaptablePipe.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef BCE1B4DD_976E_456D_AE9A_87A17E300579
|
||||
#define BCE1B4DD_976E_456D_AE9A_87A17E300579
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioComponent.h"
|
||||
#include "AudioFrameAdapter.h"
|
||||
#include "AudioResample.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioAdaptablePipe: public AudioComponent
|
||||
{
|
||||
public:
|
||||
using AudioComponent::AudioComponent;
|
||||
|
||||
protected:
|
||||
void onLink(Core::Component::Ptr pPrevious) override;
|
||||
void onUnlink(Core::Component::Ptr pPrevious) override;
|
||||
|
||||
virtual Frame::Ptr readFrame(uint32_t timeout = 1000);
|
||||
|
||||
private:
|
||||
AudioResample::Ptr _pResample;
|
||||
AudioFrameAdapter::Ptr _pFrameAdapter;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
32
video/core/AudioComponent.cpp
Normal file
32
video/core/AudioComponent.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioComponent.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioComponent::AudioComponent(const std::string& name): Component(name)
|
||||
{
|
||||
setSampleRate(48000);
|
||||
setChannelLayout(ChannelLayout::STEREO);
|
||||
setSampleFormat(SampleFormat::S16P);
|
||||
setFrameSize(1024);
|
||||
setGain(0);
|
||||
setPTSOffset(0);
|
||||
setSignalLost(true);
|
||||
}
|
||||
|
||||
AudioComponent::~AudioComponent()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
43
video/core/AudioComponent.h
Normal file
43
video/core/AudioComponent.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef D770D9A1_7014_4DBA_99C9_708BEDAA10F6
|
||||
#define D770D9A1_7014_4DBA_99C9_708BEDAA10F6
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Component.h"
|
||||
#include "CodecID.h"
|
||||
#include "ChannelLayout.h"
|
||||
#include "SampleFormat.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioComponent: public Component
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<AudioComponent>;
|
||||
|
||||
public:
|
||||
AudioComponent(const std::string& name);
|
||||
virtual ~AudioComponent();
|
||||
virtual void reset() {};
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(SampleRate, int);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(ChannelLayout, ChannelLayout);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(SampleFormat, SampleFormat);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(FrameSize, int);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Gain, int);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(PTSOffset, int64_t);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(SignalLost, bool);
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
239
video/core/AudioDecoder.cpp
Normal file
239
video/core/AudioDecoder.cpp
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioDecoder.h"
|
||||
#include "Logger.h"
|
||||
#include "Demuxer.h"
|
||||
#include "System.h"
|
||||
#include <Poco/Exception.h>
|
||||
#include <Logger.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* 追帧(catch-up)阈值配置
|
||||
* 说明:所有阈值均以"队列中待解码包个数"为单位。
|
||||
* 音频帧典型时长约 20ms(AAC),队列上限 300,约 6s 缓冲。
|
||||
*******************************************************************************
|
||||
*/
|
||||
namespace
|
||||
{
|
||||
constexpr int CATCH_UP_ENTER = 10;
|
||||
constexpr int CATCH_UP_EXIT = 2;
|
||||
|
||||
constexpr int DROP_LEVEL_1 = 6;
|
||||
constexpr int DROP_LEVEL_2 = 15;
|
||||
constexpr int DROP_LEVEL_3 = 30;
|
||||
}
|
||||
/*
|
||||
*******************************************************************************
|
||||
* * CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioDecodeHandler::AudioDecodeHandler(const std::string& name, CodecID codecID, AudioComponent::Ptr pPrevious)
|
||||
: _pPrevious(pPrevious)
|
||||
{
|
||||
}
|
||||
|
||||
AudioDecodeHandler::~AudioDecodeHandler()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioDecodeHandler::conceal(Packet::Ptr pPacket)
|
||||
{
|
||||
(void)pPacket;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioDecoder::AudioDecoder(const std::string& name)
|
||||
: AbstractAudioInput(name), _synchronizer(name + "/sync"),
|
||||
|
||||
_catchUpEnabled(name.find("NET") != std::string::npos ? true : false), _isCatchingUp(false), _dropPhase(0)
|
||||
{
|
||||
}
|
||||
|
||||
AudioDecoder::~AudioDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioDecoder::start()
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
void AudioDecoder::stop()
|
||||
{
|
||||
cancel();
|
||||
setTimeline(0);
|
||||
|
||||
_isCatchingUp = false;
|
||||
_dropPhase = 0;
|
||||
}
|
||||
|
||||
Frame::Ptr AudioDecoder::readFrame()
|
||||
{
|
||||
if (previousComponents().size() == 0 || packetQueues().empty())
|
||||
throw Poco::NotFoundException("No previous components");
|
||||
|
||||
updateCatchUpState();
|
||||
|
||||
Frame::Ptr pFrame = nullptr;
|
||||
|
||||
if (!_pHandler.isNull())
|
||||
{
|
||||
pFrame = _pHandler->read();
|
||||
if (!pFrame.isNull())
|
||||
{
|
||||
syncFrame(pFrame, _isCatchingUp);
|
||||
return pFrame;
|
||||
}
|
||||
}
|
||||
|
||||
while (pFrame.isNull())
|
||||
{
|
||||
Packet::Ptr pPacket;
|
||||
|
||||
try
|
||||
{
|
||||
long timeOut = _isCatchingUp ? 1 : 1000;
|
||||
pPacket = packetQueues().first()->dequeue(timeOut);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
if (packetQueues().first()->size() == 0)
|
||||
{
|
||||
if (_isCatchingUp)
|
||||
{
|
||||
_isCatchingUp = false;
|
||||
_dropPhase = 0;
|
||||
_synchronizer.reset();
|
||||
logger().information("%s: catch-up exit (queue drained)", name());
|
||||
}
|
||||
}
|
||||
setTimeline(0);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (serialCodeWatcher().isChanged(pPacket->serialCode(), true))
|
||||
{
|
||||
auto pPrevious = previousComponents().first();
|
||||
auto pDemuxer = pPrevious.cast<Demuxer>();
|
||||
if (pDemuxer)
|
||||
{
|
||||
auto pEncoder = pDemuxer->audioEncoder();
|
||||
pEncoder->config().copyTo(config());
|
||||
serialCodeManager().increment();
|
||||
|
||||
_pHandler = AudioDecodeHandler::create(pEncoder->getCodecID(), pEncoder);
|
||||
|
||||
if (_catchUpEnabled)
|
||||
{
|
||||
while (packetQueues().first()->size() > 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
packetQueues().first()->dequeue(0);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
throw Poco::NotImplementedException(
|
||||
Poco::format("Unsupported previous component: %s", pPrevious->name()));
|
||||
}
|
||||
|
||||
if (_isCatchingUp && shouldDropPacket())
|
||||
{
|
||||
_pHandler->conceal(pPacket);
|
||||
continue;
|
||||
}
|
||||
|
||||
_pHandler->write(pPacket);
|
||||
pFrame = _pHandler->read();
|
||||
}
|
||||
|
||||
syncFrame(pFrame, _isCatchingUp);
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
void AudioDecoder::onLink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
if (!packetQueues().empty() && packetQueues().firstComponent() != pPrevious)
|
||||
throw Poco::LogicException(name() + ": Support only one previous component, conflict with: " +
|
||||
pPrevious->name() + ", Current: " + packetQueues().firstComponent()->name());
|
||||
|
||||
packetQueues().add(pPrevious, name(), MediaType::AUDIO);
|
||||
}
|
||||
|
||||
void AudioDecoder::onUnlink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
packetQueues().remove(pPrevious);
|
||||
}
|
||||
|
||||
void AudioDecoder::syncFrame(Frame::Ptr pFrame, bool isCatchingUp)
|
||||
{
|
||||
if (!isCatchingUp)
|
||||
_synchronizer.syncFrame(pFrame->timestamp);
|
||||
setTimeline(pFrame->timestamp);
|
||||
pFrame->timestamp = System::timestamp();
|
||||
}
|
||||
|
||||
void AudioDecoder::updateCatchUpState()
|
||||
{
|
||||
if (!_catchUpEnabled)
|
||||
return;
|
||||
|
||||
const int queueSize = packetQueues().first()->size();
|
||||
|
||||
if (!_isCatchingUp && queueSize > CATCH_UP_ENTER)
|
||||
{
|
||||
_isCatchingUp = true;
|
||||
_dropPhase = 0;
|
||||
logger().information("%s: catch-up enter (queue=%d)", name(), queueSize);
|
||||
}
|
||||
else if (_isCatchingUp && queueSize <= CATCH_UP_EXIT)
|
||||
{
|
||||
_isCatchingUp = false;
|
||||
_dropPhase = 0;
|
||||
|
||||
_synchronizer.reset();
|
||||
logger().information("%s: catch-up exit (queue=%d)", name(), queueSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioDecoder::shouldDropPacket()
|
||||
{
|
||||
const int queueSize = packetQueues().first()->size();
|
||||
bool drop = false;
|
||||
|
||||
if (queueSize >= DROP_LEVEL_3)
|
||||
{
|
||||
drop = (_dropPhase % 3) != 2;
|
||||
}
|
||||
else if (queueSize >= DROP_LEVEL_2)
|
||||
{
|
||||
drop = (_dropPhase % 2) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
drop = (_dropPhase % 3) == 0;
|
||||
}
|
||||
|
||||
_dropPhase++;
|
||||
return drop;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
video/core/AudioDecoder.h
Normal file
119
video/core/AudioDecoder.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#ifndef D60BD16B_B6AC_40EF_9E0E_D70A203EA49C
|
||||
#define D60BD16B_B6AC_40EF_9E0E_D70A203EA49C
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AbstractAudioInput.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/Task.h"
|
||||
#include "System.h"
|
||||
#include "FrameSynchronizer.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioDecodeHandlerSelector
|
||||
{
|
||||
public:
|
||||
static CodecID select(CodecID codec, AudioComponent::Ptr)
|
||||
{
|
||||
return codec;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioDecodeHandler: public ::Util::FactoryManager<AudioDecodeHandler, CodecID, AudioDecodeHandlerSelector,
|
||||
CodecID, AudioComponent::Ptr>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<AudioDecodeHandler>;
|
||||
|
||||
public:
|
||||
AudioDecodeHandler(const std::string& name, CodecID codecID, AudioComponent::Ptr pPrevious);
|
||||
virtual ~AudioDecodeHandler();
|
||||
|
||||
public:
|
||||
const std::string& name() const;
|
||||
AudioComponent::Ptr previous() const;
|
||||
|
||||
virtual void write(Packet::Ptr pPacket) = 0;
|
||||
virtual Frame::Ptr read() = 0;
|
||||
|
||||
virtual void conceal(Packet::Ptr pPacket);
|
||||
|
||||
private:
|
||||
const std::string _name;
|
||||
AudioComponent::Ptr _pPrevious;
|
||||
};
|
||||
|
||||
inline const std::string& AudioDecodeHandler::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
inline AudioComponent::Ptr AudioDecodeHandler::previous() const
|
||||
{
|
||||
return _pPrevious;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioDecoderSelector
|
||||
{
|
||||
public:
|
||||
static int select(const std::string&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioDecoder: public AbstractAudioInput,
|
||||
public ::Util::FactoryManager<AudioDecoder, int, AudioDecoderSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<AudioDecoder>;
|
||||
|
||||
public:
|
||||
AudioDecoder(const std::string& name);
|
||||
virtual ~AudioDecoder();
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
Frame::Ptr readFrame() override;
|
||||
|
||||
void onLink(Core::Component::Ptr pPrevious) override;
|
||||
void onUnlink(Core::Component::Ptr pPrevious) override;
|
||||
|
||||
void syncFrame(Frame::Ptr pFrame, bool isCatchingUp = false);
|
||||
|
||||
PROPERTY_DECLARE(Timeline, uint64_t, 0, public, public)
|
||||
|
||||
private:
|
||||
void updateCatchUpState();
|
||||
|
||||
bool shouldDropPacket();
|
||||
|
||||
AudioDecodeHandler::Ptr _pHandler;
|
||||
FrameSynchronizer _synchronizer;
|
||||
|
||||
bool _catchUpEnabled;
|
||||
|
||||
bool _isCatchingUp;
|
||||
|
||||
uint32_t _dropPhase;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
85
video/core/AudioEncoder.cpp
Normal file
85
video/core/AudioEncoder.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioEncoder.h"
|
||||
#include "Logger.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioEncoder::AudioEncoder(const std::string& name, CodecID codecID): AudioAdaptablePipe(name)
|
||||
{
|
||||
setCodecID(codecID);
|
||||
|
||||
setBitrate(128000);
|
||||
}
|
||||
|
||||
AudioEncoder::~AudioEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioEncoder::start()
|
||||
{
|
||||
_task.run(
|
||||
[this](std::atomic<bool>& isCancelled) -> void
|
||||
{
|
||||
Packet::Ptr pPacket;
|
||||
while (!isCancelled)
|
||||
{
|
||||
try
|
||||
{
|
||||
pPacket = encode();
|
||||
if (!pPacket)
|
||||
continue;
|
||||
|
||||
postToNextComponents(sharedFromThis(), pPacket, MediaType::AUDIO);
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), e.displayText());
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), std::string(e.what()));
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioEncoder::stop()
|
||||
{
|
||||
_task.cancel();
|
||||
}
|
||||
|
||||
Frame::Ptr AudioEncoder::readFrame(uint32_t timeout)
|
||||
{
|
||||
Frame::Ptr pFrame = AudioAdaptablePipe::readFrame(timeout);
|
||||
|
||||
const int64_t off = _ptsOffsetUs.load(std::memory_order_relaxed);
|
||||
if (off != 0)
|
||||
{
|
||||
const int64_t ts = (int64_t)pFrame->timestamp + off;
|
||||
pFrame->timestamp = (ts > 0) ? (uint64_t)ts : 0ULL;
|
||||
}
|
||||
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
void AudioEncoder::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
if (names.exists(_PTSOffset))
|
||||
_ptsOffsetUs.store(getPTSOffset(), std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
video/core/AudioEncoder.h
Normal file
66
video/core/AudioEncoder.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#ifndef E7CCDC55_4323_4B0A_A08E_1C11B939AA61
|
||||
#define E7CCDC55_4323_4B0A_A08E_1C11B939AA61
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioAdaptablePipe.h"
|
||||
#include "CodecID.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/Task.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioEncoderSelector
|
||||
{
|
||||
public:
|
||||
static CodecID select(CodecID codecID)
|
||||
{
|
||||
return codecID;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioEncoder: public AudioAdaptablePipe,
|
||||
public ::Util::FactoryManager<AudioEncoder, CodecID, AudioEncoderSelector, CodecID>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<AudioEncoder>;
|
||||
|
||||
public:
|
||||
AudioEncoder(const std::string& name, CodecID codecID);
|
||||
virtual ~AudioEncoder();
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(CodecID, CodecID);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Bitrate, int);
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
Frame::Ptr readFrame(uint32_t timeout = 1000) override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
|
||||
virtual Packet::Ptr encode() = 0;
|
||||
|
||||
protected:
|
||||
std::atomic<int64_t> _ptsOffsetUs{0};
|
||||
|
||||
private:
|
||||
Util::Task _task;
|
||||
AudioResample::Ptr _pResample;
|
||||
AudioFrameAdapter::Ptr _pFrameAdapter;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
122
video/core/AudioFrameAdapter.cpp
Normal file
122
video/core/AudioFrameAdapter.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioFrameAdapter.h"
|
||||
#include "System.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioFrameAdapter::AudioFrameAdapter(ChannelLayout layout, SampleFormat format, int frameSize, int sampleRate)
|
||||
{
|
||||
int channels = channelLayoutChannels(layout);
|
||||
_linesize = sampleFormatCalcLineSize(format, layout, frameSize);
|
||||
_lines = sampleFormatIsPlanar(format) ? channels : 1;
|
||||
_sampleDuration = 1000000000ul / sampleRate;
|
||||
_sampleSize = sampleFormatBytesPerSample(format);
|
||||
if (!sampleFormatIsPlanar(format))
|
||||
_sampleSize *= channels;
|
||||
|
||||
_readIndex = 0;
|
||||
|
||||
_sampleRate = sampleRate;
|
||||
_frameSize = frameSize;
|
||||
_encodeClock = 0;
|
||||
}
|
||||
|
||||
void AudioFrameAdapter::put(Frame::Ptr pFrame)
|
||||
{
|
||||
if (!_pInFrame.isNull())
|
||||
throw Poco::InvalidAccessException();
|
||||
|
||||
_pInFrame = pFrame;
|
||||
}
|
||||
|
||||
Frame::Ptr AudioFrameAdapter::get()
|
||||
{
|
||||
int inRemain = remainOfReadFrame();
|
||||
if (inRemain == 0)
|
||||
return nullptr;
|
||||
|
||||
if (_pOutFrame.isNull())
|
||||
{
|
||||
_pOutFrame = new Frame();
|
||||
|
||||
for (int i = 0; i < _lines; i++)
|
||||
_pOutFrame->data[i] = new uint8_t[_linesize];
|
||||
_pOutFrame->length = _linesize;
|
||||
|
||||
_writeIndex = 0;
|
||||
}
|
||||
|
||||
if (0 == _pOutFrame->timestamp)
|
||||
{
|
||||
_pOutFrame->timestamp = getInFrameTimestamp();
|
||||
|
||||
const uint64_t wall = System::timestamp();
|
||||
if (0 == _encodeClock)
|
||||
_encodeClock = wall;
|
||||
else
|
||||
{
|
||||
const int64_t diff = (int64_t)(wall - _encodeClock);
|
||||
if (diff > 300000 || diff < -300000)
|
||||
_encodeClock = wall;
|
||||
else
|
||||
_encodeClock += (uint64_t)(diff / 8);
|
||||
}
|
||||
_pOutFrame->timestamp = _encodeClock;
|
||||
_encodeClock += 1000000ull * _frameSize / _sampleRate;
|
||||
}
|
||||
int bytes = std::min(inRemain, spaceOfOutFrame());
|
||||
for (int i = 0; i < _lines; i++)
|
||||
memcpy(_pOutFrame->data[i] + _writeIndex, _pInFrame->data[i] + _readIndex, bytes);
|
||||
|
||||
_writeIndex += bytes;
|
||||
_readIndex += bytes;
|
||||
|
||||
if (remainOfReadFrame() == 0)
|
||||
{
|
||||
_pInFrame.reset();
|
||||
_readIndex = 0;
|
||||
}
|
||||
|
||||
Frame::Ptr pFrame = nullptr;
|
||||
if (spaceOfOutFrame() == 0)
|
||||
{
|
||||
pFrame = _pOutFrame;
|
||||
_pOutFrame.reset();
|
||||
}
|
||||
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
int AudioFrameAdapter::remainOfReadFrame()
|
||||
{
|
||||
if (_pInFrame.isNull())
|
||||
return 0;
|
||||
|
||||
return _pInFrame->length - _readIndex;
|
||||
}
|
||||
|
||||
int AudioFrameAdapter::spaceOfOutFrame()
|
||||
{
|
||||
if (_pOutFrame.isNull())
|
||||
return 0;
|
||||
|
||||
return _pOutFrame->length - _writeIndex;
|
||||
}
|
||||
|
||||
uint64_t AudioFrameAdapter::getInFrameTimestamp()
|
||||
{
|
||||
return _pInFrame->timestamp + _readIndex / _sampleSize * _sampleDuration / 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
video/core/AudioFrameAdapter.h
Normal file
58
video/core/AudioFrameAdapter.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef B0BCC994_AC90_4B90_9CD2_10586E958794
|
||||
#define B0BCC994_AC90_4B90_9CD2_10586E958794
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Frame.h"
|
||||
#include "SampleFormat.h"
|
||||
#include "ChannelLayout.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioFrameAdapter
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<AudioFrameAdapter>;
|
||||
|
||||
public:
|
||||
AudioFrameAdapter(ChannelLayout layout, SampleFormat format, int frameSize, int sampleRate);
|
||||
|
||||
public:
|
||||
void put(Frame::Ptr pFrame);
|
||||
Frame::Ptr get();
|
||||
|
||||
protected:
|
||||
int remainOfReadFrame();
|
||||
int spaceOfOutFrame();
|
||||
uint64_t getInFrameTimestamp();
|
||||
|
||||
private:
|
||||
int _linesize;
|
||||
int _lines;
|
||||
int _sampleSize;
|
||||
Frame::Ptr _pInFrame;
|
||||
Frame::Ptr _pOutFrame;
|
||||
uint32_t _readIndex;
|
||||
uint32_t _writeIndex;
|
||||
|
||||
uint32_t _sampleDuration;
|
||||
|
||||
int _sampleRate;
|
||||
|
||||
uint32_t _frameSize;
|
||||
|
||||
uint64_t _encodeClock;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
244
video/core/AudioMixer.cpp
Normal file
244
video/core/AudioMixer.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioMixer.h"
|
||||
#include "System.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioMixer::Channel::Channel(AudioMixer::Ptr pMixer, uint32_t id)
|
||||
: AudioAdaptablePipe(Poco::format(pMixer->name() + "/CH#%u", id)), _pMixer(pMixer), _id(id)
|
||||
{
|
||||
setAttach(false);
|
||||
setInput(nullptr);
|
||||
}
|
||||
|
||||
AudioMixer::Channel::~Channel()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioMixer::Channel::start()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioMixer::Channel::stop()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioMixer::Channel::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
if (names.exists(_Input))
|
||||
unlink(true, false);
|
||||
|
||||
if (names.exists(_Attach))
|
||||
{
|
||||
auto pInput = getInput();
|
||||
if (pInput)
|
||||
{
|
||||
if (getAttach())
|
||||
{
|
||||
getInput()->link(sharedFromThis());
|
||||
link(mixer());
|
||||
}
|
||||
else
|
||||
getInput()->unlink(sharedFromThis());
|
||||
}
|
||||
}
|
||||
|
||||
serialCodeWatcher().reset();
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioMixer::AudioMixer(const std::string& name): AbstractAudioInput(name)
|
||||
{
|
||||
_mixFrame = new Frame();
|
||||
for (int i = 0; i < Frame::DATA_LINES; i++)
|
||||
_mixFrame->data[i] = new uint8_t[1024 * 32];
|
||||
}
|
||||
|
||||
AudioMixer::~AudioMixer()
|
||||
{
|
||||
}
|
||||
|
||||
AudioMixer::Channel::Ptr AudioMixer::getChannel(uint32_t id)
|
||||
{
|
||||
if (_channels.find(id) == _channels.end())
|
||||
{
|
||||
Channel::Ptr pChannel = new Channel(Ptr(this, true), id);
|
||||
syncConfigToChannel(pChannel);
|
||||
_channels[id] = pChannel;
|
||||
}
|
||||
|
||||
return _channels[id];
|
||||
}
|
||||
|
||||
void AudioMixer::syncConfigToChannel(AudioMixer::Channel::Ptr pChannel)
|
||||
{
|
||||
pChannel->setSampleRate(getSampleRate());
|
||||
pChannel->setChannelLayout(getChannelLayout());
|
||||
pChannel->setSampleFormat(getSampleFormat());
|
||||
pChannel->setFrameSize(getFrameSize());
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
static inline void mix(const S MIN, const S MAX, uint8_t* i0, uint8_t* i1, uint8_t* o, uint32_t bytes)
|
||||
{
|
||||
T* p0 = reinterpret_cast<T*>(i0);
|
||||
T* p1 = reinterpret_cast<T*>(i1);
|
||||
T* output = reinterpret_cast<T*>(o);
|
||||
|
||||
const size_t count = static_cast<size_t>(bytes / sizeof(T));
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
S a = static_cast<S>(p0[i]);
|
||||
S b = static_cast<S>(p1[i]);
|
||||
|
||||
const int64_t product = static_cast<int64_t>(a) * b;
|
||||
S c = a + b - static_cast<S>(product >> 0x10);
|
||||
|
||||
c = (c > MAX) ? MAX : ((c < MIN) ? MIN : c);
|
||||
output[i] = static_cast<T>(c);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::start()
|
||||
{
|
||||
refreshOutputParams();
|
||||
run();
|
||||
}
|
||||
|
||||
void AudioMixer::stop()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void AudioMixer::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
for (auto& m : _channels)
|
||||
{
|
||||
auto pChannel = m.second;
|
||||
syncConfigToChannel(pChannel);
|
||||
pChannel->commit();
|
||||
}
|
||||
|
||||
AbstractAudioInput::onConfigChanged(names);
|
||||
}
|
||||
|
||||
Frame::Ptr AudioMixer::readFrame()
|
||||
{
|
||||
Frame::Ptr pFrame = nullptr;
|
||||
uint64_t pts = 0;
|
||||
uint32_t index = 0;
|
||||
for (auto& m : _channels)
|
||||
{
|
||||
auto pChannel = m.second;
|
||||
Frame::Ptr pReadFrame;
|
||||
|
||||
try
|
||||
{
|
||||
pReadFrame = pChannel->readFrame(32);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pReadFrame = nullptr;
|
||||
}
|
||||
|
||||
if (!pReadFrame.isNull())
|
||||
{
|
||||
if (pts == 0 || pts > pReadFrame->timestamp)
|
||||
pts = pReadFrame->timestamp;
|
||||
|
||||
if (pFrame == nullptr)
|
||||
pFrame = pReadFrame;
|
||||
else
|
||||
{
|
||||
#define AUDIO_DATA(i) index > 0 ? _mixFrame->data[i].get() : pFrame->data[i].get()
|
||||
switch (getSampleFormat())
|
||||
{
|
||||
case SampleFormat::U8:
|
||||
case SampleFormat::U8P:
|
||||
for (uint32_t i = 0; i < getChannels(); i++)
|
||||
mix<uint8_t, uint16_t>(0, UINT8_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
||||
_mixFrame->data[i].get(), pFrame->length);
|
||||
break;
|
||||
|
||||
case SampleFormat::S16:
|
||||
case SampleFormat::S16P:
|
||||
for (uint32_t i = 0; i < getChannels(); i++)
|
||||
mix<int16_t, int32_t>(INT16_MIN, INT16_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
||||
_mixFrame->data[i].get(), pFrame->length);
|
||||
break;
|
||||
|
||||
case SampleFormat::S32:
|
||||
case SampleFormat::S32P:
|
||||
for (uint32_t i = 0; i < getChannels(); i++)
|
||||
mix<int32_t, int64_t>(INT32_MIN, INT32_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
||||
_mixFrame->data[i].get(), pFrame->length);
|
||||
break;
|
||||
|
||||
case SampleFormat::S64:
|
||||
case SampleFormat::S64P:
|
||||
for (uint32_t i = 0; i < getChannels(); i++)
|
||||
mix<int64_t, int64_t>(INT64_MIN, INT64_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
||||
_mixFrame->data[i].get(), pFrame->length);
|
||||
break;
|
||||
|
||||
case SampleFormat::FLT:
|
||||
case SampleFormat::FLTP:
|
||||
for (uint32_t i = 0; i < getChannels(); i++)
|
||||
mix<float, float>(-1.0f, 1.0f, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
||||
_mixFrame->data[i].get(), pFrame->length);
|
||||
break;
|
||||
|
||||
case SampleFormat::DBL:
|
||||
case SampleFormat::DBLP:
|
||||
for (uint32_t i = 0; i < getChannels(); i++)
|
||||
mix<double, double>(-1.0, 1.0, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
||||
_mixFrame->data[i].get(), pFrame->length);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pFrame.isNull())
|
||||
return nullptr;
|
||||
|
||||
_traffic++;
|
||||
if (index > 0)
|
||||
{
|
||||
_mixFrame->timestamp = pts;
|
||||
_mixFrame->length = pFrame->length;
|
||||
_mixFrame->code = pFrame->code;
|
||||
return _mixFrame;
|
||||
}
|
||||
else
|
||||
return pFrame;
|
||||
}
|
||||
|
||||
void AudioMixer::refreshOutputParams()
|
||||
{
|
||||
setFrameDuration((float)1000000 / getSampleRate() * getFrameSize());
|
||||
setChannels(channelLayoutChannels(getChannelLayout()));
|
||||
setLineSize(sampleFormatCalcLineSize(getSampleFormat(), getChannelLayout(), getFrameSize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
102
video/core/AudioMixer.h
Normal file
102
video/core/AudioMixer.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef AC195091_8442_4268_B843_700AB594416A
|
||||
#define AC195091_8442_4268_B843_700AB594416A
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioAdaptablePipe.h"
|
||||
#include "AbstractAudioInput.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioMixerSelector
|
||||
{
|
||||
public:
|
||||
static int select(const std::string&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioMixer: public AbstractAudioInput,
|
||||
public ::Util::FactoryManager<AudioMixer, int, AudioMixerSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<AudioMixer>;
|
||||
class Channel: public AudioAdaptablePipe
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<Channel>;
|
||||
|
||||
protected:
|
||||
Channel(AudioMixer::Ptr, uint32_t id);
|
||||
|
||||
public:
|
||||
~Channel();
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Attach, bool)
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Input, AudioComponent::Ptr)
|
||||
|
||||
uint32_t id() const;
|
||||
|
||||
protected:
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
|
||||
AudioMixer::Ptr mixer();
|
||||
|
||||
private:
|
||||
AudioMixer::Ptr _pMixer;
|
||||
uint32_t _id;
|
||||
|
||||
friend AudioMixer;
|
||||
};
|
||||
|
||||
public:
|
||||
AudioMixer(const std::string& name);
|
||||
~AudioMixer();
|
||||
|
||||
Channel::Ptr getChannel(uint32_t id);
|
||||
|
||||
void syncConfigToChannel(Channel::Ptr pChannel);
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
Frame::Ptr readFrame() override;
|
||||
|
||||
void refreshOutputParams();
|
||||
|
||||
PROPERTY_DECLARE(FrameDuration, uint32_t, 0, protected, protected)
|
||||
PROPERTY_DECLARE(Channels, uint32_t, 0, protected, protected)
|
||||
PROPERTY_DECLARE(LineSize, uint32_t, 0, protected, protected)
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, Channel::Ptr> _channels;
|
||||
Frame::Ptr _mixFrame;
|
||||
};
|
||||
|
||||
inline uint32_t AudioMixer::Channel::id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
inline AudioMixer::Ptr AudioMixer::Channel::mixer()
|
||||
{
|
||||
return _pMixer;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
58
video/core/AudioOutput.cpp
Normal file
58
video/core/AudioOutput.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioOutput.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
void AudioOutput::start()
|
||||
{
|
||||
_task.run(
|
||||
[this](std::atomic<bool>& isCancelled) -> void
|
||||
{
|
||||
while (!isCancelled)
|
||||
{
|
||||
try
|
||||
{
|
||||
Frame::Ptr pFrame = readFrame(1000);
|
||||
writeFrame(pFrame);
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), e.displayText());
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), std::string(e.what()));
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioOutput::stop()
|
||||
{
|
||||
_task.cancel();
|
||||
}
|
||||
|
||||
void AudioOutput::onLink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
if (!frameQueues().empty() && frameQueues().firstComponent() != pPrevious)
|
||||
throw Poco::LogicException(name() + ": Support only one previous component, conflict with: " +
|
||||
pPrevious->name() + ", Current: " + frameQueues().firstComponent()->name());
|
||||
|
||||
frameQueues().add(pPrevious, name(), MediaType::AUDIO, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
video/core/AudioOutput.h
Normal file
52
video/core/AudioOutput.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef F16FAA29_322A_4A89_945E_E56FFADAC2CD
|
||||
#define F16FAA29_322A_4A89_945E_E56FFADAC2CD
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioAdaptablePipe.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/Task.h"
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
class AudioOutputSelector
|
||||
{
|
||||
public:
|
||||
static const std::string select(const std::string& arg)
|
||||
{
|
||||
auto pos = arg.find(':');
|
||||
return (pos != std::string::npos) ? arg.substr(0, pos) : arg;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioOutput
|
||||
: public AudioAdaptablePipe,
|
||||
public ::Util::FactoryManager<AudioOutput, const std::string, AudioOutputSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using AudioAdaptablePipe::AudioAdaptablePipe;
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
virtual void writeFrame(Frame::Ptr pFrame) = 0;
|
||||
void onLink(Core::Component::Ptr pPrevious) override;
|
||||
|
||||
private:
|
||||
Util::Task _task;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
28
video/core/AudioResample.cpp
Normal file
28
video/core/AudioResample.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioResample.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
AudioResample::AudioResample(const std::string& name, Core::ChannelLayout ilayout, Core::SampleFormat iformat,
|
||||
int irate, Core::ChannelLayout olayout, Core::SampleFormat oformat, int orate)
|
||||
: _name(name), _ilayout(ilayout), _iformat(iformat), _irate(irate), _olayout(olayout), _oformat(oformat),
|
||||
_orate(orate)
|
||||
{
|
||||
}
|
||||
|
||||
AudioResample::~AudioResample()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
100
video/core/AudioResample.h
Normal file
100
video/core/AudioResample.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#ifndef F62FEA52_888A_474C_B58C_B83007D75F89
|
||||
#define F62FEA52_888A_474C_B58C_B83007D75F89
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "ChannelLayout.h"
|
||||
#include "SampleFormat.h"
|
||||
#include "Frame.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class AudioResampleSelector
|
||||
{
|
||||
public:
|
||||
static int select(ChannelLayout, SampleFormat, int, ChannelLayout, SampleFormat, int)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioResample: public ::Util::FactoryManager<AudioResample, int, AudioResampleSelector, ChannelLayout,
|
||||
SampleFormat, int, ChannelLayout, SampleFormat, int>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<AudioResample>;
|
||||
|
||||
public:
|
||||
AudioResample(const std::string& name, Core::ChannelLayout ilayout, Core::SampleFormat iformat, int irate,
|
||||
Core::ChannelLayout olayout, Core::SampleFormat oformat, int orate);
|
||||
virtual ~AudioResample();
|
||||
|
||||
public:
|
||||
virtual Frame::Ptr process(Frame::Ptr in) = 0;
|
||||
|
||||
const std::string name() const;
|
||||
const Core::ChannelLayout inLayout() const;
|
||||
const Core::SampleFormat inFormat() const;
|
||||
const int inRate() const;
|
||||
const Core::ChannelLayout outLayout() const;
|
||||
const Core::SampleFormat outFormat() const;
|
||||
const int outRate() const;
|
||||
|
||||
private:
|
||||
const std::string _name;
|
||||
Core::ChannelLayout _ilayout;
|
||||
Core::SampleFormat _iformat;
|
||||
int _irate;
|
||||
Core::ChannelLayout _olayout;
|
||||
Core::SampleFormat _oformat;
|
||||
int _orate;
|
||||
};
|
||||
|
||||
inline const std::string AudioResample::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
inline const Core::ChannelLayout AudioResample::inLayout() const
|
||||
{
|
||||
return _ilayout;
|
||||
}
|
||||
|
||||
inline const Core::SampleFormat AudioResample::inFormat() const
|
||||
{
|
||||
return _iformat;
|
||||
}
|
||||
|
||||
inline const int AudioResample::inRate() const
|
||||
{
|
||||
return _irate;
|
||||
}
|
||||
|
||||
inline const Core::ChannelLayout AudioResample::outLayout() const
|
||||
{
|
||||
return _olayout;
|
||||
}
|
||||
|
||||
inline const Core::SampleFormat AudioResample::outFormat() const
|
||||
{
|
||||
return _oformat;
|
||||
}
|
||||
|
||||
inline const int AudioResample::outRate() const
|
||||
{
|
||||
return _orate;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
37
video/core/BitrateControl.cpp
Normal file
37
video/core/BitrateControl.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "BitrateControl.h"
|
||||
#include <Poco/Format.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL VARIABLES
|
||||
*******************************************************************************
|
||||
*/
|
||||
static const std::string _bcStrings[] = {
|
||||
"CBR", "VBR", "AVBR", "QVBR", "CVBR", "FIXQP", "QPMAP",
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string bitrateControlToString(BitrateControl bc)
|
||||
{
|
||||
unsigned index = (unsigned)bc;
|
||||
|
||||
if (index >= (unsigned)BitrateControl::NB)
|
||||
return Poco::format("UNKNOWN(%u)", index);
|
||||
|
||||
return _bcStrings[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
40
video/core/BitrateControl.h
Normal file
40
video/core/BitrateControl.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef BE785709_C62A_4233_A050_C24D88194CE4
|
||||
#define BE785709_C62A_4233_A050_C24D88194CE4
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class BitrateControl
|
||||
{
|
||||
CBR,
|
||||
VBR,
|
||||
AVBR,
|
||||
QVBR,
|
||||
CVBR,
|
||||
FIXQP,
|
||||
QPMAP,
|
||||
|
||||
NB,
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string bitrateControlToString(BitrateControl bc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
79
video/core/ChannelLayout.cpp
Normal file
79
video/core/ChannelLayout.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "ChannelLayout.h"
|
||||
#include <Poco/Format.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* TYPEDEFS
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct ChannelLayoutInfo
|
||||
{
|
||||
std::string name;
|
||||
int channels;
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL VARIABLES
|
||||
*******************************************************************************
|
||||
*/
|
||||
static const ChannelLayoutInfo channelLayoutInfo[(int)ChannelLayout::NB] = {
|
||||
{
|
||||
"L",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"R",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"MONO",
|
||||
1,
|
||||
},
|
||||
{
|
||||
"STEREO",
|
||||
2,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
static inline bool isChannelLayoutValid(ChannelLayout layout)
|
||||
{
|
||||
return ((int)layout >= 0) && (layout < ChannelLayout::NB);
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
int channelLayoutChannels(ChannelLayout layout)
|
||||
{
|
||||
if (isChannelLayoutValid(layout))
|
||||
return channelLayoutInfo[(int)layout].channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string channelLayoutToString(const ChannelLayout layout)
|
||||
{
|
||||
if (isChannelLayoutValid(layout))
|
||||
return channelLayoutInfo[(int)layout].name;
|
||||
|
||||
return Poco::format("UNKNOWN(%u)", (uint32_t)layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
video/core/ChannelLayout.h
Normal file
34
video/core/ChannelLayout.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef CD9FD626_A6CF_41FF_B2B2_050F7BEA7195
|
||||
#define CD9FD626_A6CF_41FF_B2B2_050F7BEA7195
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class ChannelLayout
|
||||
{
|
||||
L,
|
||||
R,
|
||||
|
||||
MONO,
|
||||
STEREO,
|
||||
|
||||
NB,
|
||||
};
|
||||
|
||||
int channelLayoutChannels(ChannelLayout layout);
|
||||
const std::string channelLayoutToString(const ChannelLayout layout);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
61
video/core/CodecCapabilityManager.cpp
Normal file
61
video/core/CodecCapabilityManager.cpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "CodecCapabilityManager.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
const int CodecCapabilityManager::INVALID_HANDLE = -1;
|
||||
|
||||
CodecCapabilityManager::CodecCapabilityManager(uint64_t total): _total(total), _handles(1024)
|
||||
{
|
||||
}
|
||||
|
||||
CodecCapabilityManager::Handle CodecCapabilityManager::alloc(uint32_t width, uint32_t height, uint32_t frameRate)
|
||||
{
|
||||
uint64_t v = width * height * frameRate;
|
||||
Poco::FastMutex::ScopedLock lock(_lock);
|
||||
if (v <= _total)
|
||||
{
|
||||
try
|
||||
{
|
||||
Handle h = _handles.allocHandle();
|
||||
_allocRecord[h] = v;
|
||||
_total -= v;
|
||||
return h;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
|
||||
void CodecCapabilityManager::free(Handle handle)
|
||||
{
|
||||
if (handle != INVALID_HANDLE)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_lock);
|
||||
try
|
||||
{
|
||||
_handles.freeHandle(handle);
|
||||
_total += _allocRecord[handle];
|
||||
_allocRecord.erase(handle);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
video/core/CodecCapabilityManager.h
Normal file
42
video/core/CodecCapabilityManager.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef B88D46AC_14A3_4C6F_ADE1_2AF8BB03F51A
|
||||
#define B88D46AC_14A3_4C6F_ADE1_2AF8BB03F51A
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "util/HandlesManager.h"
|
||||
#include <Poco/Mutex.h>
|
||||
#include <map>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class CodecCapabilityManager
|
||||
{
|
||||
public:
|
||||
using Handle = int;
|
||||
static const int INVALID_HANDLE;
|
||||
|
||||
public:
|
||||
CodecCapabilityManager(uint64_t total);
|
||||
|
||||
public:
|
||||
Handle alloc(uint32_t width, uint32_t height, uint32_t frameRate);
|
||||
void free(Handle handle);
|
||||
|
||||
private:
|
||||
uint64_t _total;
|
||||
Util::HandlesManager<Handle> _handles;
|
||||
std::map<Handle, uint64_t> _allocRecord;
|
||||
Poco::FastMutex _lock;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
39
video/core/CodecID.cpp
Normal file
39
video/core/CodecID.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "CodecID.h"
|
||||
#include <Poco/Format.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL VARIABLES
|
||||
*******************************************************************************
|
||||
*/
|
||||
static const std::string _strings[] = {
|
||||
"H264", "H265", "MJPEG",
|
||||
|
||||
"MP3", "AAC", "OPUS", "G711A", "G711U",
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string codecIDToString(const CodecID id)
|
||||
{
|
||||
unsigned index = (unsigned)id;
|
||||
|
||||
if (index >= (unsigned)CodecID::NB)
|
||||
return Poco::format("UNKNOWN(%u)", index);
|
||||
|
||||
return _strings[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
42
video/core/CodecID.h
Normal file
42
video/core/CodecID.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef BB532961_7DA1_415B_A98F_EB80C5C9DC17
|
||||
#define BB532961_7DA1_415B_A98F_EB80C5C9DC17
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class CodecID
|
||||
{
|
||||
H264,
|
||||
H265,
|
||||
MJPEG,
|
||||
|
||||
MP3,
|
||||
AAC,
|
||||
OPUS,
|
||||
G711A,
|
||||
G711U,
|
||||
|
||||
NB,
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string codecIDToString(const CodecID codecID);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
35
video/core/CodecProfile.h
Normal file
35
video/core/CodecProfile.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef D1AC8D55_FFF4_4CDD_9A78_3DD77F9417B4
|
||||
#define D1AC8D55_FFF4_4CDD_9A78_3DD77F9417B4
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class CodecProfile
|
||||
{
|
||||
H264_BASELINE,
|
||||
H264_MAIN,
|
||||
H264_HIGH,
|
||||
H264_EXTENDED,
|
||||
H264_SVC_T,
|
||||
H264_SVC_Q,
|
||||
H264_SVC_S,
|
||||
|
||||
H265_MAIN,
|
||||
H265_MAIN_10,
|
||||
|
||||
NB,
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
82
video/core/Color.cpp
Executable file
82
video/core/Color.cpp
Executable file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Color.h"
|
||||
#include "Poco/NumberParser.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Color::Color(std::string s)
|
||||
{
|
||||
uint32_t value = 0xffffff;
|
||||
try
|
||||
{
|
||||
int pos = s.find('#');
|
||||
if (pos >= 0)
|
||||
value = Poco::NumberParser::parseHex(s.substr(pos + 1));
|
||||
else
|
||||
value = Poco::NumberParser::parse(s);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
new (this) Color(value);
|
||||
}
|
||||
|
||||
Color::Color(uint32_t n)
|
||||
{
|
||||
new (this) Color((n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff);
|
||||
}
|
||||
|
||||
Color::Color()
|
||||
{
|
||||
new (this) Color(0);
|
||||
}
|
||||
|
||||
void Color::toARGB1555Pixel(uint8_t v[2])
|
||||
{
|
||||
uint32_t alpha = (a + 127) / 255;
|
||||
uint32_t red = (r * 31 + 127) / 255;
|
||||
uint32_t green = (g * 31 + 127) / 255;
|
||||
uint32_t blue = (b * 31 + 127) / 255;
|
||||
|
||||
uint16_t pixel = (alpha << 15) | (red << 10) | (green << 5) | blue;
|
||||
|
||||
v[0] = pixel;
|
||||
v[1] = pixel >> 8;
|
||||
}
|
||||
|
||||
void Color::toARGB4444Pixel(uint8_t v[2])
|
||||
{
|
||||
uint16_t pixel = (a >> 4) << 12 | (r >> 4) << 8 | (g >> 4) << 4 | (b >> 4);
|
||||
|
||||
v[0] = pixel;
|
||||
v[1] = pixel >> 8;
|
||||
}
|
||||
|
||||
void Color::toRGB888Pixel(uint8_t v[3])
|
||||
{
|
||||
v[0] = r;
|
||||
v[1] = g;
|
||||
v[2] = b;
|
||||
}
|
||||
|
||||
void Color::toARGB8888Pixel(uint8_t v[4])
|
||||
{
|
||||
v[0] = b;
|
||||
v[1] = g;
|
||||
v[2] = r;
|
||||
v[3] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
video/core/Color.h
Executable file
47
video/core/Color.h
Executable file
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef D9CEFFBC_A4C8_4C8D_9D61_5A2E533AFA42
|
||||
#define D9CEFFBC_A4C8_4C8D_9D61_5A2E533AFA42
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct Color
|
||||
{
|
||||
Color(uint8_t a, uint8_t r, uint8_t g, uint8_t b): a(a), r(r), g(g), b(b)
|
||||
{
|
||||
}
|
||||
|
||||
Color(uint8_t r, uint8_t g, uint8_t b): Color(255, r, g, b)
|
||||
{
|
||||
}
|
||||
|
||||
Color(std::string s);
|
||||
Color(uint32_t n);
|
||||
Color();
|
||||
|
||||
void toARGB1555Pixel(uint8_t v[2]);
|
||||
void toARGB4444Pixel(uint8_t v[2]);
|
||||
void toRGB888Pixel(uint8_t v[3]);
|
||||
void toARGB8888Pixel(uint8_t v[4]);
|
||||
|
||||
uint8_t a;
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
350
video/core/Component.cpp
Normal file
350
video/core/Component.cpp
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Component.h"
|
||||
#include "System.h"
|
||||
#include "util/Math.h"
|
||||
#include "Logger.h"
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
bool Component::Collection::add(Ptr pObject)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_lock);
|
||||
auto iter = std::find(_list.begin(), _list.end(), pObject);
|
||||
if (iter == _list.end())
|
||||
{
|
||||
_list.push_back(pObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Component::Collection::remove(Ptr pObject)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_lock);
|
||||
auto iter = std::find(_list.begin(), _list.end(), pObject);
|
||||
if (iter != _list.end())
|
||||
{
|
||||
_list.remove(pObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Component::Collection::foreach (std::function<void(Ptr)> func)
|
||||
{
|
||||
std::vector<Ptr> v;
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_lock);
|
||||
for (auto& ptr : _list)
|
||||
v.push_back(ptr);
|
||||
}
|
||||
|
||||
for (auto& ptr : v)
|
||||
func(ptr);
|
||||
}
|
||||
|
||||
uint32_t Component::Collection::size()
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_lock);
|
||||
return _list.size();
|
||||
}
|
||||
|
||||
Component::Ptr Component::Collection::first()
|
||||
{
|
||||
return *(_list.begin());
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Component::Component(const std::string& name): _name(name), _frameQueues(this), _packetQueues(this), _started(false)
|
||||
{
|
||||
logger().debug("%s(%#x): CREATE.", name, reinterpret_cast<uintptr_t>(this));
|
||||
}
|
||||
|
||||
Component::~Component()
|
||||
{
|
||||
logger().debug("%s(%#x): RELEASE.", name(), reinterpret_cast<uintptr_t>(this));
|
||||
}
|
||||
|
||||
Component::Ptr Component::link(Ptr pNext)
|
||||
{
|
||||
if (pNext.isNull())
|
||||
return nullptr;
|
||||
|
||||
if (nextComponents().add(pNext))
|
||||
{
|
||||
logger().debug("%s(%#x): ===> %s(%#x)", name(), reinterpret_cast<uintptr_t>(this), pNext->name(),
|
||||
reinterpret_cast<uintptr_t>(pNext.get()));
|
||||
pNext->doLink(sharedFromThis());
|
||||
}
|
||||
|
||||
return pNext;
|
||||
}
|
||||
|
||||
void Component::unlink(Component::Ptr pNext)
|
||||
{
|
||||
if (pNext.isNull())
|
||||
return;
|
||||
|
||||
if (nextComponents().remove(pNext))
|
||||
{
|
||||
logger().debug("%s(%#x): =X=> %s(%#x)", name(), reinterpret_cast<uintptr_t>(this), pNext->name(),
|
||||
reinterpret_cast<uintptr_t>(pNext.get()));
|
||||
pNext->doUnlink(sharedFromThis());
|
||||
}
|
||||
}
|
||||
|
||||
void Component::unlink(bool unlinkAllPrevious, bool unlinkAllNext)
|
||||
{
|
||||
if (unlinkAllPrevious)
|
||||
{
|
||||
previousComponents().foreach ([this](Ptr pObject) -> void { pObject->unlink(sharedFromThis()); });
|
||||
}
|
||||
|
||||
if (unlinkAllNext)
|
||||
{
|
||||
nextComponents().foreach ([this](Ptr pObject) -> void { unlink(pObject); });
|
||||
}
|
||||
}
|
||||
|
||||
void Component::enable(const void* pUser, const std::string& desc)
|
||||
{
|
||||
logger().debug("%s(%#x): Enabled by %s(%#x)", name(), reinterpret_cast<uintptr_t>(this), desc,
|
||||
reinterpret_cast<uintptr_t>(pUser));
|
||||
Poco::Mutex::ScopedLock lock(_componentsLock);
|
||||
bool shouldStart = (userCount() == 0);
|
||||
incrementUserRefCount(pUser);
|
||||
if (shouldStart)
|
||||
{
|
||||
frameQueues().flush();
|
||||
packetQueues().flush();
|
||||
|
||||
initialize();
|
||||
|
||||
previousComponents().foreach ([&](Ptr ptr) -> void { ptr->enable(this); });
|
||||
|
||||
logger().debug("%s(%#x): START.", name(), reinterpret_cast<uintptr_t>(this));
|
||||
start();
|
||||
_traffic = 0;
|
||||
_started = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Component::disable(const void* pUser, const std::string& desc, bool ignoreRefCount)
|
||||
{
|
||||
logger().debug("%s(%#x): Disabled by %s(%#x)", name(), reinterpret_cast<uintptr_t>(this), desc,
|
||||
reinterpret_cast<uintptr_t>(pUser));
|
||||
Poco::Mutex::ScopedLock lock(_componentsLock);
|
||||
if (!started())
|
||||
{
|
||||
logger().debug("%s(%#x): Stop already.", name(), reinterpret_cast<uintptr_t>(this));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreRefCount)
|
||||
eraseUser(pUser);
|
||||
else
|
||||
decrementUserRefCount(pUser);
|
||||
|
||||
if (userCount() == 0)
|
||||
{
|
||||
logger().debug("%s(%#x): STOP.", name(), reinterpret_cast<uintptr_t>(this));
|
||||
stop();
|
||||
deinitialize();
|
||||
_traffic = 0;
|
||||
_started = false;
|
||||
|
||||
previousComponents().foreach ([&](Component::Ptr ptr) -> void { ptr->disable(this); });
|
||||
|
||||
frameQueues().flush();
|
||||
packetQueues().flush();
|
||||
}
|
||||
}
|
||||
|
||||
int Component::userCount()
|
||||
{
|
||||
Poco::Mutex::ScopedLock lock(_componentsLock);
|
||||
return _users.size();
|
||||
}
|
||||
|
||||
int Component::nextComponentCount()
|
||||
{
|
||||
return nextComponents().size();
|
||||
}
|
||||
|
||||
int Component::previousComponentCount()
|
||||
{
|
||||
return previousComponents().size();
|
||||
}
|
||||
|
||||
QueueStatus Component::queueStatus(Ptr who)
|
||||
{
|
||||
{
|
||||
auto pQueue = packetQueues().find(who);
|
||||
if (pQueue)
|
||||
return pQueue->status();
|
||||
}
|
||||
|
||||
{
|
||||
auto pQueue = frameQueues().find(who);
|
||||
if (pQueue)
|
||||
return pQueue->status();
|
||||
}
|
||||
|
||||
return QueueStatus::UNKNOWN;
|
||||
}
|
||||
|
||||
void Component::initialize()
|
||||
{
|
||||
}
|
||||
|
||||
void Component::deinitialize()
|
||||
{
|
||||
}
|
||||
|
||||
void Component::doLink(Ptr pPrevious)
|
||||
{
|
||||
if (previousComponents().add(pPrevious))
|
||||
{
|
||||
onLink(pPrevious);
|
||||
if (started())
|
||||
pPrevious->enable(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Component::onLink(Ptr pPrevious)
|
||||
{
|
||||
}
|
||||
|
||||
void Component::doUnlink(Ptr pPrevious)
|
||||
{
|
||||
if (previousComponents().remove(pPrevious))
|
||||
{
|
||||
onUnlink(pPrevious);
|
||||
pPrevious->disable(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Component::onUnlink(Ptr pPrevious)
|
||||
{
|
||||
}
|
||||
|
||||
void Component::post(Ptr pSender, Frame::Ptr pFrame, MediaType type, bool force)
|
||||
{
|
||||
post(pSender, frameQueues(), pFrame, type, force);
|
||||
}
|
||||
|
||||
void Component::post(Ptr pSender, Packet::Ptr pPacket, MediaType type, bool force)
|
||||
{
|
||||
post(pSender, packetQueues(), pPacket->clone(), type, force);
|
||||
}
|
||||
|
||||
void Component::notify(Notification::Ptr pNotification, bool shouldPropagate)
|
||||
{
|
||||
_notificationCenter.postNotification(pNotification);
|
||||
if (shouldPropagate)
|
||||
{
|
||||
logger().debug("%s(Sender is '%s'): Notify '%s' to next components", name(), pNotification->getSender()->name(),
|
||||
pNotification->name());
|
||||
nextComponents().foreach (
|
||||
[&](Component::Ptr ptr) -> void
|
||||
{
|
||||
if (ptr->started())
|
||||
ptr->notify(pNotification, shouldPropagate);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Component::notifyNextComponents(Ptr pSender, Notification::Ptr pNotification, bool shouldPropagate)
|
||||
{
|
||||
logger().debug("%s: Notify '%s' to next components", pSender->name(), pNotification->name());
|
||||
pNotification->setSender(pSender);
|
||||
nextComponents().foreach (
|
||||
[&](Component::Ptr ptr) -> void
|
||||
{
|
||||
if (ptr->started())
|
||||
ptr->notify(pNotification, shouldPropagate);
|
||||
});
|
||||
}
|
||||
|
||||
void Component::incrementUserRefCount(const void* pUser)
|
||||
{
|
||||
if (_users.find(pUser) == _users.end())
|
||||
_users[pUser] = 0;
|
||||
|
||||
_users[pUser]++;
|
||||
}
|
||||
|
||||
void Component::decrementUserRefCount(const void* pUser)
|
||||
{
|
||||
if (_users.find(pUser) != _users.end())
|
||||
{
|
||||
_users[pUser]--;
|
||||
if (_users[pUser] <= 0)
|
||||
_users.erase(pUser);
|
||||
}
|
||||
}
|
||||
|
||||
void Component::eraseUser(const void* pUser)
|
||||
{
|
||||
_users.erase(pUser);
|
||||
}
|
||||
|
||||
bool Component::syncWait(uint64_t ts, std::atomic<bool>& isCancelled)
|
||||
{
|
||||
uint64_t now = System::timestamp();
|
||||
while (ts > now && !isCancelled)
|
||||
{
|
||||
now = System::timestamp();
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
return (now >= ts);
|
||||
}
|
||||
|
||||
bool Component::syncWait(uint64_t ts, uint32_t maxWaitTime)
|
||||
{
|
||||
const uint64_t startTime = System::timestamp();
|
||||
const uint64_t timeout = maxWaitTime * 1000;
|
||||
while (true)
|
||||
{
|
||||
uint64_t now = System::timestamp();
|
||||
|
||||
if (now >= ts)
|
||||
return true;
|
||||
|
||||
if (now - startTime >= timeout)
|
||||
return false;
|
||||
|
||||
const uint64_t remaining = ::Util::Math::min(ts - now, timeout - (now - startTime));
|
||||
const useconds_t us = ::Util::Math::clamp(remaining / 10, 1000ULL, 100000ULL);
|
||||
usleep(us);
|
||||
}
|
||||
}
|
||||
|
||||
void Component::publishEvent(Event event, bool sync)
|
||||
{
|
||||
if (sync)
|
||||
EventOccurred.notify(this, event);
|
||||
else
|
||||
EventOccurred.notifyAsync(this, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
408
video/core/Component.h
Normal file
408
video/core/Component.h
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
#ifndef AB01F7C9_DADD_4ADA_88F6_ED371CF36813
|
||||
#define AB01F7C9_DADD_4ADA_88F6_ED371CF36813
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "util/Configurable.h"
|
||||
#include "util/ClassAssistant.h"
|
||||
#include "Frame.h"
|
||||
#include "Packet.h"
|
||||
#include <Poco/AutoPtr.h>
|
||||
#include <Poco/Mutex.h>
|
||||
#include <Poco/NotificationCenter.h>
|
||||
#include <Poco/NObserver.h>
|
||||
#include <Poco/RefCountedObject.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Poco/BasicEvent.h>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Component: public Poco::RefCountedObject, public Util::Configurable
|
||||
{
|
||||
public:
|
||||
enum class Event
|
||||
{
|
||||
IMAGE_SIZE_CHANGED,
|
||||
};
|
||||
|
||||
using Ptr = Poco::AutoPtr<Component>;
|
||||
class Collection
|
||||
{
|
||||
public:
|
||||
bool add(Ptr);
|
||||
bool remove(Ptr);
|
||||
void foreach (std::function<void(Ptr)> func);
|
||||
uint32_t size();
|
||||
Ptr first();
|
||||
|
||||
private:
|
||||
std::list<Ptr> _list;
|
||||
Poco::FastMutex _lock;
|
||||
};
|
||||
|
||||
class Notification: public Poco::Notification
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<Notification>;
|
||||
|
||||
public:
|
||||
PROPERTY_DECLARE(Sender, Component::Ptr, nullptr, public, public);
|
||||
|
||||
std::string name() const override
|
||||
{
|
||||
return Util::ClassAssistant::demangle(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QT> class Queues
|
||||
{
|
||||
public:
|
||||
Queues(Ptr pOwner);
|
||||
|
||||
void add(Ptr, const std::string& name, MediaType, int limit = 300);
|
||||
void remove(Ptr);
|
||||
void clear();
|
||||
|
||||
bool exists(Ptr);
|
||||
bool empty();
|
||||
typename QT::Ptr first();
|
||||
typename QT::Ptr find(Ptr);
|
||||
Ptr firstComponent();
|
||||
void flush();
|
||||
|
||||
template <typename F> void foreach (const F&& func);
|
||||
|
||||
private:
|
||||
Ptr _pOwner;
|
||||
Poco::FastMutex _mutex;
|
||||
std::map<Ptr, typename QT::Ptr> _queues;
|
||||
};
|
||||
|
||||
public:
|
||||
Component(const std::string& name);
|
||||
Component(const Component&) = delete;
|
||||
virtual ~Component();
|
||||
|
||||
public:
|
||||
const std::string& name() const;
|
||||
|
||||
Ptr link(Ptr pNext);
|
||||
void unlink(Ptr pNext);
|
||||
void unlink(bool unlinkAllPrevious, bool unlinkAllNext);
|
||||
|
||||
template <class T> void enable(T* pObject);
|
||||
template <class T> void disable(T* pObject, bool ignoreRefCount = false);
|
||||
template <class T> bool enabled(T* pObject);
|
||||
|
||||
Ptr sharedFromThis();
|
||||
void enable(const void* pUser, const std::string& desc);
|
||||
void disable(const void* pUser, const std::string& desc, bool ignoreRefCount = false);
|
||||
|
||||
int userCount();
|
||||
int nextComponentCount();
|
||||
int previousComponentCount();
|
||||
const SerialCode& code();
|
||||
|
||||
QueueStatus queueStatus(Ptr who);
|
||||
|
||||
virtual uint32_t traffic();
|
||||
|
||||
bool started() const;
|
||||
|
||||
Poco::BasicEvent<Event> EventOccurred;
|
||||
|
||||
protected:
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void initialize();
|
||||
virtual void deinitialize();
|
||||
virtual void doLink(Ptr pPrevious);
|
||||
virtual void onLink(Ptr pPrevious);
|
||||
virtual void doUnlink(Ptr pPrevious);
|
||||
virtual void onUnlink(Ptr pPrevious);
|
||||
|
||||
Collection& previousComponents();
|
||||
Collection& nextComponents();
|
||||
|
||||
SerialCodeManager& serialCodeManager();
|
||||
SerialCodeWatcher& serialCodeWatcher();
|
||||
|
||||
void post(Ptr pSender, Frame::Ptr pFrame, MediaType type, bool force);
|
||||
void post(Ptr pSender, Packet::Ptr pPacket, MediaType type, bool force);
|
||||
|
||||
template <class TQueue, class TObjectPtr>
|
||||
void post(Ptr pSender, TQueue& queues, TObjectPtr pObject, MediaType type, bool force);
|
||||
|
||||
template <class TObjectPtr> void postToNextComponents(Ptr pSender, TObjectPtr pObject, MediaType type);
|
||||
|
||||
template <class TObjectPtr>
|
||||
bool postToNextComponents(Ptr pSender, TObjectPtr pObject, MediaType type, std::atomic<bool>& isCancelled);
|
||||
|
||||
template <class N, class C, class H> void registerNotificationHandle(C& object, const H& handle);
|
||||
template <class N, class C, class H> void unregisterNotificationHandle(C& object, const H& handle);
|
||||
|
||||
void notify(Notification::Ptr pNotification, bool shouldPropagate);
|
||||
void notifyNextComponents(Ptr pSender, Notification::Ptr pNotification, bool shouldPropagate = false);
|
||||
|
||||
Queues<FrameQueue>& frameQueues();
|
||||
Queues<PacketQueue>& packetQueues();
|
||||
|
||||
void incrementUserRefCount(const void* pUser);
|
||||
void decrementUserRefCount(const void* pUser);
|
||||
void eraseUser(const void* pUser);
|
||||
|
||||
bool syncWait(uint64_t ts, std::atomic<bool>& isCancelled);
|
||||
bool syncWait(uint64_t ts, uint32_t maxWaitTime);
|
||||
|
||||
void publishEvent(Event event, bool sync = true);
|
||||
|
||||
protected:
|
||||
uint32_t _traffic;
|
||||
|
||||
private:
|
||||
const std::string _name;
|
||||
Collection _previousComponents;
|
||||
Collection _nextComponents;
|
||||
Poco::Mutex _componentsLock;
|
||||
Queues<FrameQueue> _frameQueues;
|
||||
Queues<PacketQueue> _packetQueues;
|
||||
std::unordered_map<const void*, int> _users;
|
||||
bool _started;
|
||||
SerialCodeManager _serialCodeManager;
|
||||
SerialCodeWatcher _serialCodeWatcher;
|
||||
Poco::NotificationCenter _notificationCenter;
|
||||
};
|
||||
|
||||
template <class QT> inline Component::Queues<QT>::Queues(Ptr pOwner): _pOwner(pOwner)
|
||||
{
|
||||
}
|
||||
|
||||
template <class QT> void Component::Queues<QT>::add(Ptr pComponent, const std::string& name, MediaType type, int limit)
|
||||
{
|
||||
if (exists(pComponent))
|
||||
throw Poco::ExistsException(_pOwner->name() + "'s queue: " + pComponent->name() + " EXISTS!");
|
||||
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
_queues[pComponent] = new QT(name, type, limit);
|
||||
}
|
||||
|
||||
template <class QT> void Component::Queues<QT>::remove(Ptr pComponent)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
_queues.erase(pComponent);
|
||||
}
|
||||
|
||||
template <class QT> inline void Component::Queues<QT>::clear()
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
return _queues.clear();
|
||||
}
|
||||
|
||||
template <class QT> inline bool Component::Queues<QT>::exists(Ptr pComponent)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
return _queues.find(pComponent) != _queues.end();
|
||||
}
|
||||
|
||||
template <class QT> inline bool Component::Queues<QT>::empty()
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
return _queues.empty();
|
||||
}
|
||||
|
||||
template <class QT> inline typename QT::Ptr Component::Queues<QT>::first()
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
if (_queues.empty())
|
||||
return nullptr;
|
||||
|
||||
return _queues.begin()->second;
|
||||
}
|
||||
|
||||
template <class QT> inline typename QT::Ptr Component::Queues<QT>::find(Ptr pComponent)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
const auto it = _queues.find(pComponent);
|
||||
if (it != _queues.end())
|
||||
return it->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class QT> inline Component::Ptr Component::Queues<QT>::firstComponent()
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
if (_queues.empty())
|
||||
return nullptr;
|
||||
|
||||
return _queues.begin()->first;
|
||||
}
|
||||
|
||||
template <class QT> inline void Component::Queues<QT>::flush()
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
for (auto& q : _queues)
|
||||
q.second->clear();
|
||||
}
|
||||
|
||||
template <class QT> template <typename F> inline void Component::Queues<QT>::foreach (const F&& func)
|
||||
{
|
||||
Poco::FastMutex::ScopedLock lock(_mutex);
|
||||
for (auto& q : _queues)
|
||||
func(q.first, q.second);
|
||||
}
|
||||
|
||||
inline const std::string& Component::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
inline bool Component::started() const
|
||||
{
|
||||
return _started;
|
||||
}
|
||||
|
||||
inline const SerialCode& Component::code()
|
||||
{
|
||||
return serialCodeManager().code();
|
||||
}
|
||||
|
||||
inline uint32_t Component::traffic()
|
||||
{
|
||||
return _traffic;
|
||||
}
|
||||
|
||||
inline Component::Collection& Component::previousComponents()
|
||||
{
|
||||
return _previousComponents;
|
||||
}
|
||||
|
||||
inline Component::Collection& Component::nextComponents()
|
||||
{
|
||||
return _nextComponents;
|
||||
}
|
||||
|
||||
inline SerialCodeManager& Component::serialCodeManager()
|
||||
{
|
||||
return _serialCodeManager;
|
||||
}
|
||||
|
||||
inline SerialCodeWatcher& Component::serialCodeWatcher()
|
||||
{
|
||||
return _serialCodeWatcher;
|
||||
}
|
||||
|
||||
inline Component::Queues<FrameQueue>& Component::frameQueues()
|
||||
{
|
||||
return _frameQueues;
|
||||
}
|
||||
|
||||
inline Component::Queues<PacketQueue>& Component::packetQueues()
|
||||
{
|
||||
return _packetQueues;
|
||||
}
|
||||
|
||||
template <class T> inline void Component::enable(T* pObject)
|
||||
{
|
||||
enable(pObject, std::is_base_of<Component, T>::value ? reinterpret_cast<Component*>(pObject)->name()
|
||||
: Util::ClassAssistant::demangle<T>());
|
||||
}
|
||||
|
||||
template <class T> inline void Component::disable(T* pObject, bool ignoreRefCount)
|
||||
{
|
||||
disable(pObject,
|
||||
std::is_base_of<Component, T>::value ? reinterpret_cast<Component*>(pObject)->name()
|
||||
: Util::ClassAssistant::demangle<T>(),
|
||||
ignoreRefCount);
|
||||
}
|
||||
|
||||
template <class T> inline bool Video::Core::Component::enabled(T* pUser)
|
||||
{
|
||||
Poco::Mutex::ScopedLock lock(_componentsLock);
|
||||
return (_users.find(pUser) != _users.end());
|
||||
}
|
||||
|
||||
template <class TQueue, class TObjectPtr>
|
||||
inline void Component::post(Ptr pSender, TQueue& queues, TObjectPtr pObject, MediaType type, bool force)
|
||||
{
|
||||
auto pQueue = queues.find(pSender);
|
||||
if (pQueue && pQueue->type() == type)
|
||||
{
|
||||
pQueue->enqueue(pObject, force);
|
||||
_traffic++;
|
||||
}
|
||||
}
|
||||
|
||||
template <class TObjectPtr> inline void Component::postToNextComponents(Ptr pSender, TObjectPtr pObject, MediaType type)
|
||||
{
|
||||
nextComponents().foreach (
|
||||
[&](Ptr ptr) -> void
|
||||
{
|
||||
if (ptr->started())
|
||||
ptr->post(pSender, pObject, type, true);
|
||||
});
|
||||
}
|
||||
|
||||
template <class TObjectPtr>
|
||||
inline bool Component::postToNextComponents(Ptr pSender, TObjectPtr pObject, MediaType type,
|
||||
std::atomic<bool>& isCancelled)
|
||||
{
|
||||
int count = 0;
|
||||
int num = 0;
|
||||
nextComponents().foreach (
|
||||
[&](Ptr ptr) -> void
|
||||
{
|
||||
if (ptr->started())
|
||||
{
|
||||
num++;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
ptr->post(pSender, pObject, type, false);
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (!isCancelled)
|
||||
::usleep(1000);
|
||||
}
|
||||
} while (!isCancelled);
|
||||
}
|
||||
});
|
||||
|
||||
return count == num;
|
||||
}
|
||||
|
||||
template <class N, class C, class H> inline void Component::registerNotificationHandle(C& object, const H& handle)
|
||||
{
|
||||
_notificationCenter.addObserver(Poco::NObserver<C, N>(object, handle));
|
||||
}
|
||||
|
||||
template <class N, class C, class H> inline void Component::unregisterNotificationHandle(C& object, const H& handle)
|
||||
{
|
||||
_notificationCenter.removeObserver(Poco::NObserver<C, N>(object, handle));
|
||||
}
|
||||
|
||||
inline Component::Ptr Component::sharedFromThis()
|
||||
{
|
||||
return Ptr(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
312
video/core/Demuxer.cpp
Normal file
312
video/core/Demuxer.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Demuxer.h"
|
||||
#include "System.h"
|
||||
#include "Logger.h"
|
||||
#include "util/Broadcast.h"
|
||||
#include <Poco/Format.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class DummyVideoEncoder: public VideoEncoder
|
||||
{
|
||||
public:
|
||||
DummyVideoEncoder(const std::string& name): VideoEncoder(name, CodecID::H264)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void onConfigChanged(ParameterNames& names) override
|
||||
{
|
||||
if (names.exists(_CodecID))
|
||||
serialCodeManager().put(_CodecID, getCodecID());
|
||||
|
||||
if (names.exists(_Width))
|
||||
serialCodeManager().put(_Width, getWidth());
|
||||
|
||||
if (names.exists(_Height))
|
||||
serialCodeManager().put(_Height, getHeight());
|
||||
|
||||
if (names.exists(_FrameRate))
|
||||
serialCodeManager().put(_FrameRate, getFrameRate());
|
||||
}
|
||||
};
|
||||
|
||||
class DummyAudioEncoder: public AudioEncoder
|
||||
{
|
||||
public:
|
||||
DummyAudioEncoder(const std::string& name): AudioEncoder(name, CodecID::AAC)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
Core::Packet::Ptr encode() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void onConfigChanged(ParameterNames& names) override
|
||||
{
|
||||
if (names.exists(_CodecID))
|
||||
serialCodeManager().put(_CodecID, getCodecID());
|
||||
|
||||
if (names.exists(_SampleRate))
|
||||
serialCodeManager().put(_SampleRate, getSampleRate());
|
||||
|
||||
if (names.exists(_SampleFormat))
|
||||
serialCodeManager().put(_SampleFormat, getSampleFormat());
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
DemuxHandler::DemuxHandler(const std::string& name, const std::string& uri)
|
||||
: _name(name), _uri(uri), _scheme(Poco::URI(uri).getScheme()), _timeDrift(0)
|
||||
{
|
||||
}
|
||||
|
||||
DemuxHandler::~DemuxHandler()
|
||||
{
|
||||
}
|
||||
|
||||
int64_t DemuxHandler::getTimeDrift(uint64_t ts)
|
||||
{
|
||||
if (0 == _timeDrift)
|
||||
_timeDrift = getBaseline() - ts;
|
||||
|
||||
return _timeDrift;
|
||||
}
|
||||
|
||||
void DemuxHandler::resetTimeDrift()
|
||||
{
|
||||
_timeDrift = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class DemuxerHandlerController: public Util::BroadcastReceiver, public Util::BroadcastReceiverRegistry
|
||||
{
|
||||
public:
|
||||
DemuxerHandlerController(DemuxHandler::Ptr pHandler, int index, std::atomic<bool>& isCancelled)
|
||||
: _skip(false), _newIndex(0), _newPosition(0), _pHandler(pHandler), _index(index), _isCancelled(isCancelled)
|
||||
{
|
||||
Util::IntentFilter* pFilter = new Util::IntentFilter;
|
||||
pFilter->addAction("carousel.position");
|
||||
registerReceiver(*this, pFilter);
|
||||
}
|
||||
|
||||
~DemuxerHandlerController()
|
||||
{
|
||||
unregisterReceiver(*this);
|
||||
}
|
||||
|
||||
protected:
|
||||
void onReceive(Util::Intent::Ptr pIntent) override
|
||||
{
|
||||
int index = pIntent->getExtra<int>("index");
|
||||
int position = pIntent->getExtra<int>("position");
|
||||
if (index < 0 || position < 0)
|
||||
return;
|
||||
|
||||
uint64_t ts = position * 1000000u;
|
||||
|
||||
if (index != _index)
|
||||
{
|
||||
_skip = true;
|
||||
_newIndex = index;
|
||||
_newPosition = ts;
|
||||
|
||||
_isCancelled = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_pHandler->seek(ts);
|
||||
}
|
||||
|
||||
READONLY_PROPERTY_DECLARE(skip, bool)
|
||||
READONLY_PROPERTY_DECLARE(newIndex, int)
|
||||
READONLY_PROPERTY_DECLARE(newPosition, uint64_t)
|
||||
|
||||
private:
|
||||
DemuxHandler::Ptr _pHandler;
|
||||
int _index;
|
||||
std::atomic<bool>& _isCancelled;
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Demuxer::Demuxer(const std::string& name)
|
||||
: Component(name), _pVideoEncoder(new DummyVideoEncoder(name + "/VideoEncoder")),
|
||||
_pAudioEncoder(new DummyAudioEncoder(name + "/AudioEncoder"))
|
||||
{
|
||||
setUsingTCPTransport(false);
|
||||
setURICollection({});
|
||||
}
|
||||
|
||||
Demuxer::~Demuxer()
|
||||
{
|
||||
}
|
||||
|
||||
#define __try_run(x) \
|
||||
try \
|
||||
{ \
|
||||
x \
|
||||
} \
|
||||
catch (Poco::Exception & e) \
|
||||
{ \
|
||||
logger().error("%s: %s", name(), e.displayText()); \
|
||||
Poco::Thread::sleep(2000); \
|
||||
} \
|
||||
catch (std::exception & e) \
|
||||
{ \
|
||||
logger().error("%s: %s", name(), std::string(e.what())); \
|
||||
Poco::Thread::sleep(2000); \
|
||||
}
|
||||
|
||||
void Demuxer::start()
|
||||
{
|
||||
if (getURICollection().size() == 0)
|
||||
{
|
||||
logger().warning("%s: URI collection is empty!", name());
|
||||
return;
|
||||
}
|
||||
|
||||
_task.run(
|
||||
[this](std::atomic<bool>& isCancelled) -> void
|
||||
{
|
||||
_playIndex = 0;
|
||||
PlayIndexChanged.notify(this);
|
||||
|
||||
int index = 0;
|
||||
|
||||
uint64_t position = 0;
|
||||
|
||||
uint64_t timeline = 1000000;
|
||||
while (!isCancelled)
|
||||
{
|
||||
std::string uri;
|
||||
DemuxHandler::Ptr pHandler;
|
||||
|
||||
__try_run({ uri = getURICollection().at(index); });
|
||||
__try_run({ pHandler = DemuxHandler::create(uri); });
|
||||
|
||||
if (!pHandler.isNull())
|
||||
{
|
||||
__try_run({
|
||||
_playPosition = 0;
|
||||
|
||||
if (Poco::URI(uri).getScheme() == "rtsp")
|
||||
pHandler->setUsingTCPTransport(getUsingTCPTransport());
|
||||
pHandler->setBaseline(timeline);
|
||||
|
||||
Poco::SharedPtr<DemuxerHandlerController> pController;
|
||||
if (Poco::URI(uri).getScheme() == "file")
|
||||
{
|
||||
pController = new DemuxerHandlerController(pHandler, index, isCancelled);
|
||||
if (position)
|
||||
{
|
||||
pHandler->seek(position);
|
||||
position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pHandler->run(
|
||||
videoEncoder(), audioEncoder(),
|
||||
[&](Packet::Ptr pPacket, MediaType type) -> void
|
||||
{
|
||||
if (postToNextComponents(sharedFromThis(), pPacket, type, isCancelled))
|
||||
{
|
||||
int64_t pos = pHandler->getPosition();
|
||||
int64_t diff = std::abs(pos - _playPosition);
|
||||
if (diff >= 1000000)
|
||||
{
|
||||
_playPosition = pos;
|
||||
PlayPositionChanged.notify(this);
|
||||
}
|
||||
}
|
||||
},
|
||||
isCancelled);
|
||||
|
||||
timeline = pHandler->getTimeline();
|
||||
|
||||
if (pController && pController->skip())
|
||||
{
|
||||
isCancelled = false;
|
||||
|
||||
index = pController->newIndex();
|
||||
position = pController->newPosition();
|
||||
|
||||
_playIndex = index;
|
||||
_playPosition = position;
|
||||
|
||||
PlayIndexChanged.notify(this);
|
||||
PlayPositionChanged.notify(this);
|
||||
|
||||
continue;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
logger().error("%s: Not implemented URI scheme '%s'", name(), Poco::URI(uri).getScheme());
|
||||
|
||||
if (++index >= (int)getURICollection().size())
|
||||
index = 0;
|
||||
|
||||
if (_playIndex != index)
|
||||
{
|
||||
_playIndex = index;
|
||||
PlayIndexChanged.notify(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Demuxer::stop()
|
||||
{
|
||||
_task.cancel();
|
||||
}
|
||||
|
||||
void Demuxer::onLink(Component::Ptr pPrevious)
|
||||
{
|
||||
throw Poco::LogicException("Not supported for link");
|
||||
}
|
||||
|
||||
void Demuxer::onUnlink(Component::Ptr pPrevious)
|
||||
{
|
||||
throw Poco::LogicException("Not supported for link");
|
||||
}
|
||||
|
||||
void Demuxer::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
if (!started())
|
||||
return;
|
||||
|
||||
if (names.exists(_UsingTCPTransport) || names.exists(_URICollection))
|
||||
{
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
video/core/Demuxer.h
Normal file
149
video/core/Demuxer.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#ifndef ADECE10E_B193_46F7_B377_651677F4AD89
|
||||
#define ADECE10E_B193_46F7_B377_651677F4AD89
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Component.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/SimpleFactory.h"
|
||||
#include "VideoEncoder.h"
|
||||
#include "AudioEncoder.h"
|
||||
#include "util/Task.h"
|
||||
#include "util/ClassAssistant.h"
|
||||
#include <Poco/BasicEvent.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <Poco/URI.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class DemuxHandlerSelector
|
||||
{
|
||||
public:
|
||||
static const std::string select(const std::string& uri)
|
||||
{
|
||||
return Poco::URI(uri).getScheme();
|
||||
}
|
||||
};
|
||||
|
||||
class DemuxHandler
|
||||
: public ::Util::FactoryManager<DemuxHandler, const std::string, DemuxHandlerSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<DemuxHandler>;
|
||||
using Post = std::function<void(Packet::Ptr, MediaType)>;
|
||||
using ShouldWait = std::function<bool(void)>;
|
||||
|
||||
public:
|
||||
DemuxHandler(const std::string& name, const std::string& uri);
|
||||
virtual ~DemuxHandler();
|
||||
|
||||
public:
|
||||
virtual void run(VideoEncoder::Ptr pVideoEncoder, AudioEncoder::Ptr pAudioEncoder, const Post& post,
|
||||
std::atomic<bool>& isCancelled) = 0;
|
||||
|
||||
const std::string& name() const;
|
||||
const std::string& uri() const;
|
||||
const std::string& scheme() const;
|
||||
|
||||
virtual void seek(uint64_t ts) = 0;
|
||||
|
||||
PROPERTY_DECLARE(UsingTCPTransport, bool, false, public, public)
|
||||
PROPERTY_DECLARE(Baseline, uint64_t, 0, public, public)
|
||||
PROPERTY_DECLARE(Timeline, uint64_t, 0, public, public)
|
||||
PROPERTY_DECLARE(Position, int64_t, 0, protected, public)
|
||||
|
||||
protected:
|
||||
int64_t getTimeDrift(uint64_t ts);
|
||||
void resetTimeDrift();
|
||||
|
||||
private:
|
||||
const std::string _name;
|
||||
const std::string _uri;
|
||||
const std::string _scheme;
|
||||
int64_t _timeDrift;
|
||||
};
|
||||
|
||||
inline const std::string& DemuxHandler::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
inline const std::string& DemuxHandler::uri() const
|
||||
{
|
||||
return _uri;
|
||||
}
|
||||
|
||||
inline const std::string& DemuxHandler::scheme() const
|
||||
{
|
||||
return _scheme;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Demuxer: public Component, public ::Util::SimpleFactory<Demuxer, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<Demuxer>;
|
||||
using URICollection = std::vector<std::string>;
|
||||
|
||||
public:
|
||||
Demuxer(const std::string& name);
|
||||
~Demuxer();
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(UsingTCPTransport, bool)
|
||||
CONFIGURABLE_DECLARE_PARAMETER(URICollection, URICollection&)
|
||||
|
||||
Poco::BasicEvent<void> PlayIndexChanged;
|
||||
Poco::BasicEvent<void> PlayPositionChanged;
|
||||
|
||||
const int playIndex() const;
|
||||
VideoEncoder::Ptr videoEncoder();
|
||||
AudioEncoder::Ptr audioEncoder();
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void onLink(Component::Ptr pPrevious) override;
|
||||
void onUnlink(Component::Ptr pPrevious) override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
|
||||
READONLY_PROPERTY_DECLARE(playIndex, int)
|
||||
READONLY_PROPERTY_DECLARE(playPosition, int64_t)
|
||||
|
||||
private:
|
||||
Util::Task _task;
|
||||
VideoEncoder::Ptr _pVideoEncoder;
|
||||
AudioEncoder::Ptr _pAudioEncoder;
|
||||
};
|
||||
|
||||
inline const int Demuxer::playIndex() const
|
||||
{
|
||||
return _playIndex;
|
||||
}
|
||||
|
||||
inline VideoEncoder::Ptr Demuxer::videoEncoder()
|
||||
{
|
||||
return _pVideoEncoder;
|
||||
}
|
||||
|
||||
inline AudioEncoder::Ptr Demuxer::audioEncoder()
|
||||
{
|
||||
return _pAudioEncoder;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
19
video/core/Frame.cpp
Normal file
19
video/core/Frame.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Frame.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
const int Frame::DATA_LINES = NUM_DATA_POINTERS;
|
||||
}
|
||||
}
|
||||
42
video/core/Frame.h
Normal file
42
video/core/Frame.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef D8F82C97_DBD1_40D1_9ABD_E78F9B225CF5
|
||||
#define D8F82C97_DBD1_40D1_9ABD_E78F9B225CF5
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "NotificationQueue.h"
|
||||
#include "SerialCode.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CONSTANTS
|
||||
*******************************************************************************
|
||||
*/
|
||||
#define NUM_DATA_POINTERS 8
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct Frame
|
||||
{
|
||||
using Ptr = Poco::SharedPtr<Frame>;
|
||||
static const int DATA_LINES;
|
||||
|
||||
Poco::SharedPtr<uint8_t> data[NUM_DATA_POINTERS];
|
||||
uint32_t length;
|
||||
uint64_t timestamp;
|
||||
SerialCode code;
|
||||
};
|
||||
|
||||
using FrameQueue = NotificationQueue<Frame>;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
111
video/core/FrameSynchronizer.cpp
Normal file
111
video/core/FrameSynchronizer.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "FrameSynchronizer.h"
|
||||
#include "util/Math.h"
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
FrameSynchronizer::DriftPredictor::DriftPredictor(size_t windowSize): _windowSize(windowSize), _lastPrediction(0)
|
||||
{
|
||||
}
|
||||
|
||||
void FrameSynchronizer::DriftPredictor::update(int64_t drift)
|
||||
{
|
||||
_samples.push_back(drift);
|
||||
|
||||
if (_samples.size() > _windowSize)
|
||||
_samples.pop_front();
|
||||
|
||||
constexpr double alpha = 0.2;
|
||||
_lastPrediction = alpha * drift + (1 - alpha) * _lastPrediction;
|
||||
}
|
||||
|
||||
int64_t FrameSynchronizer::DriftPredictor::predict()
|
||||
{
|
||||
if (_samples.empty())
|
||||
return _lastPrediction;
|
||||
|
||||
if (_samples.size() < 5)
|
||||
return _lastPrediction;
|
||||
|
||||
double sum = 0;
|
||||
for (auto v : _samples)
|
||||
sum += v;
|
||||
double mean = sum / _samples.size();
|
||||
|
||||
constexpr int64_t MAX_DRIFT = 30000;
|
||||
|
||||
int64_t result = static_cast<int64_t>(0.7 * mean + 0.3 * _lastPrediction);
|
||||
return Util::Math::clamp(result, -MAX_DRIFT, MAX_DRIFT);
|
||||
}
|
||||
|
||||
void FrameSynchronizer::DriftPredictor::reset()
|
||||
{
|
||||
_samples.clear();
|
||||
_lastPrediction = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
FrameSynchronizer::FrameSynchronizer(const std::string& name): _name(name), _baseClock(0), _lastSysTS(0)
|
||||
{
|
||||
_predictor.reset();
|
||||
}
|
||||
|
||||
FrameSynchronizer::~FrameSynchronizer()
|
||||
{
|
||||
}
|
||||
|
||||
void FrameSynchronizer::reset()
|
||||
{
|
||||
_baseClock = 0;
|
||||
_lastSysTS = 0;
|
||||
_predictor.reset();
|
||||
}
|
||||
|
||||
void FrameSynchronizer::syncFrame(uint64_t frameTS)
|
||||
{
|
||||
const uint64_t now = System::timestamp();
|
||||
|
||||
if (_baseClock == 0)
|
||||
{
|
||||
_baseClock = now - frameTS;
|
||||
_lastSysTS = now;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t ts = frameTS + _baseClock;
|
||||
if (ts + 500000 < _lastSysTS || ts > _lastSysTS + 10000000)
|
||||
{
|
||||
_predictor.reset();
|
||||
_baseClock = now - frameTS;
|
||||
_lastSysTS = now;
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t targetTime = frameTS + _baseClock + _predictor.predict();
|
||||
int64_t waitTime = static_cast<int64_t>(targetTime) - static_cast<int64_t>(now);
|
||||
if (waitTime > 0)
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(waitTime));
|
||||
|
||||
const uint64_t actualTime = System::timestamp();
|
||||
_predictor.update(static_cast<int64_t>(actualTime - targetTime));
|
||||
_lastSysTS = actualTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
video/core/FrameSynchronizer.h
Normal file
58
video/core/FrameSynchronizer.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef B6D2EB85_A2D9_4037_BDBB_5ADCDA3E3B01
|
||||
#define B6D2EB85_A2D9_4037_BDBB_5ADCDA3E3B01
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "System.h"
|
||||
#include "util/ClassAssistant.h"
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class FrameSynchronizer
|
||||
{
|
||||
public:
|
||||
FrameSynchronizer(const std::string& name);
|
||||
~FrameSynchronizer();
|
||||
|
||||
void syncFrame(uint64_t frameTs);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
class DriftPredictor
|
||||
{
|
||||
public:
|
||||
DriftPredictor(size_t windowSize = 10);
|
||||
void update(int64_t drift);
|
||||
int64_t predict();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::deque<int64_t> _samples;
|
||||
size_t _windowSize;
|
||||
double _lastPrediction;
|
||||
};
|
||||
|
||||
READONLY_PROPERTY_DECLARE(name, std::string)
|
||||
|
||||
uint64_t _baseClock;
|
||||
uint64_t _lastSysTS;
|
||||
DriftPredictor _predictor;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
54
video/core/Frontend.cpp
Normal file
54
video/core/Frontend.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Frontend.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Frontend::Frontend(std::string name): _name(name)
|
||||
{
|
||||
}
|
||||
|
||||
Frontend::~Frontend()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Frontend::name()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
void Frontend::notifyStatusChanged()
|
||||
{
|
||||
MediaInfo info;
|
||||
read(info);
|
||||
notifyStatusChanged(info);
|
||||
}
|
||||
|
||||
void Frontend::notifyStatusChanged(MediaInfo& info)
|
||||
{
|
||||
try
|
||||
{
|
||||
StatusChanged.notify(this, info);
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s: status changed: %s", name(), e.displayText());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s: status changed: %s", name(), std::string(e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
video/core/Frontend.h
Normal file
63
video/core/Frontend.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef F4486E13_26A4_47A9_B4C0_193369C1A700
|
||||
#define F4486E13_26A4_47A9_B4C0_193369C1A700
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoScanMode.h"
|
||||
#include <Poco/BasicEvent.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Frontend
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<Frontend>;
|
||||
|
||||
struct MediaInfo
|
||||
{
|
||||
bool haveVideo;
|
||||
bool haveAudio;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t frameRate;
|
||||
VideoScanMode scanMode;
|
||||
} video;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t sampleRate;
|
||||
} audio;
|
||||
};
|
||||
|
||||
public:
|
||||
Poco::BasicEvent<MediaInfo> StatusChanged;
|
||||
|
||||
public:
|
||||
Frontend(std::string name);
|
||||
virtual ~Frontend();
|
||||
|
||||
std::string name();
|
||||
virtual void read(MediaInfo& info) = 0;
|
||||
|
||||
protected:
|
||||
void notifyStatusChanged();
|
||||
void notifyStatusChanged(MediaInfo& info);
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
40
video/core/GOPMode.cpp
Normal file
40
video/core/GOPMode.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef BB0BDE8C_97B1_4665_97FE_505C703706CD
|
||||
#define BB0BDE8C_97B1_4665_97FE_505C703706CD
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "GOPMode.h"
|
||||
#include <Poco/Format.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL VARIABLES
|
||||
*******************************************************************************
|
||||
*/
|
||||
static const std::string _gmString[] = {
|
||||
"NORMAL_P", "DUAL_P", "SMART_P", "ADV_SMART_P", "BIPRED_B", "LOWDELAY_B",
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string gopModeToString(GOPMode gm)
|
||||
{
|
||||
unsigned index = (unsigned)gm;
|
||||
|
||||
if (index >= (unsigned)GOPMode::NB)
|
||||
return Poco::format("UNKNOWN(%u)", index);
|
||||
|
||||
return _gmString[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
39
video/core/GOPMode.h
Normal file
39
video/core/GOPMode.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef E0A1BF3F_980A_41BE_A542_2066821A64EB
|
||||
#define E0A1BF3F_980A_41BE_A542_2066821A64EB
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class GOPMode
|
||||
{
|
||||
NORMAL_P,
|
||||
DUAL_P,
|
||||
SMART_P,
|
||||
ADV_SMART_P,
|
||||
BIPRED_B,
|
||||
LOWDELAY_B,
|
||||
|
||||
NB
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string gopModeToString(GOPMode gm);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
27
video/core/GeometryTypes.cpp
Normal file
27
video/core/GeometryTypes.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "GeometryTypes.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
bool Rect::operator!=(const Rect& other)
|
||||
{
|
||||
return x != other.x || y != other.y || width != other.width || height != other.height;
|
||||
}
|
||||
|
||||
bool Size::operator!=(const Size& other)
|
||||
{
|
||||
return width != other.width || height != other.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
video/core/GeometryTypes.h
Normal file
37
video/core/GeometryTypes.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef B9536093_A419_43A3_9220_7B1A65A2B31F
|
||||
#define B9536093_A419_43A3_9220_7B1A65A2B31F
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct Rect
|
||||
{
|
||||
bool operator!=(const Rect& other);
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct Size
|
||||
{
|
||||
bool operator!=(const Size& other);
|
||||
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
279
video/core/Image.cpp
Normal file
279
video/core/Image.cpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Image.h"
|
||||
#include "Logger.h"
|
||||
#include "util/Math.h"
|
||||
#include <freetype/ftglyph.h>
|
||||
#include <freetype/fterrors.h>
|
||||
#include <Poco/Exception.h>
|
||||
#include <Poco/Format.h>
|
||||
#include <Poco/UnicodeConverter.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
void Image::drawText(const std::string& text, const std::string& font, int size, Color foreColor, Color bgColor)
|
||||
{
|
||||
get_shared_channel(0).fill(bgColor.r);
|
||||
get_shared_channel(1).fill(bgColor.g);
|
||||
get_shared_channel(2).fill(bgColor.b);
|
||||
get_shared_channel(3).fill(bgColor.a);
|
||||
|
||||
int x = 0, y = 0;
|
||||
|
||||
y = height() - y;
|
||||
|
||||
std::wstring wtext;
|
||||
convertText(text, wtext);
|
||||
runOnFreeType(font, size,
|
||||
[&](FT_Face& face) -> void
|
||||
{
|
||||
FT_BBox bbox;
|
||||
|
||||
computeStringBBox(wtext, face, bbox);
|
||||
|
||||
FT_Vector pen;
|
||||
pen.x = (x - bbox.xMin) * 64;
|
||||
pen.y = (y - bbox.yMax) * 64;
|
||||
|
||||
for (size_t i = 0; i < wtext.length(); i++)
|
||||
{
|
||||
FT_Set_Transform(face, 0, &pen);
|
||||
FT_UInt glyph_index = FT_Get_Char_Index(face, wtext.at(i));
|
||||
int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
|
||||
if (error)
|
||||
{
|
||||
throw Poco::InvalidAccessException("字形加载失败", error);
|
||||
}
|
||||
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
if (error)
|
||||
{
|
||||
throw Poco::InvalidAccessException("字形渲染失败", error);
|
||||
}
|
||||
|
||||
int offsetX = face->glyph->bitmap_left;
|
||||
int offsetY = height() - face->glyph->bitmap_top;
|
||||
for (size_t y = 0; y < face->glyph->bitmap.rows; y++)
|
||||
{
|
||||
for (size_t x = 0; x < face->glyph->bitmap.width; x++)
|
||||
{
|
||||
uint8_t c = face->glyph->bitmap.buffer[y * face->glyph->bitmap.width + x];
|
||||
if (c < 50)
|
||||
continue;
|
||||
|
||||
(*this)(x + offsetX, y + offsetY, 0) = foreColor.r * c / 255;
|
||||
(*this)(x + offsetX, y + offsetY, 1) = foreColor.g * c / 255;
|
||||
(*this)(x + offsetX, y + offsetY, 2) = foreColor.b * c / 255;
|
||||
(*this)(x + offsetX, y + offsetY, 3) = foreColor.a * c / 255;
|
||||
}
|
||||
}
|
||||
|
||||
pen.x += face->glyph->advance.x;
|
||||
pen.y += face->glyph->advance.y;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Image::drawRectangle(const Rect& rect, Color& color, int border)
|
||||
{
|
||||
if (border <= 0)
|
||||
{
|
||||
get_shared_channel(0).fill(color.r);
|
||||
get_shared_channel(1).fill(color.g);
|
||||
get_shared_channel(2).fill(color.b);
|
||||
get_shared_channel(3).fill(color.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
get_shared_channel(3).fill(0);
|
||||
|
||||
uint8_t c[4] = {color.r, color.g, color.b, color.a};
|
||||
|
||||
draw_rectangle(rect.x, rect.y, rect.x + rect.width, rect.y + border, c);
|
||||
draw_rectangle(rect.x, rect.y + border, rect.x + border, rect.y + rect.height, c);
|
||||
draw_rectangle(rect.x + rect.width - border, rect.y + border, rect.x + rect.width, rect.y + rect.height, c);
|
||||
draw_rectangle(rect.x + border, rect.y + rect.height - border, rect.x + rect.width - border,
|
||||
rect.y + rect.height, c);
|
||||
}
|
||||
}
|
||||
|
||||
void Image::drawImage(int x, int y, Ptr pImage)
|
||||
{
|
||||
const int width = pImage->width();
|
||||
const int height = pImage->height();
|
||||
|
||||
for (int sy = 0; sy < height; ++sy)
|
||||
{
|
||||
for (int sx = 0; sx < width; ++sx)
|
||||
{
|
||||
const int dx = x + sx;
|
||||
const int dy = y + sy;
|
||||
|
||||
if (dx >= 0 && dx < this->width() && dy >= 0 && dy < this->height())
|
||||
{
|
||||
Color color;
|
||||
pImage->getPixel(sx, sy, color);
|
||||
|
||||
(*this)(dx, dy, 0, 0) = color.r;
|
||||
(*this)(dx, dy, 0, 1) = color.g;
|
||||
(*this)(dx, dy, 0, 2) = color.b;
|
||||
(*this)(dx, dy, 0, 3) = color.a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Image::getPixel(int x, int y, Color& color)
|
||||
{
|
||||
uint8_t originAlpha = 0xff;
|
||||
|
||||
if (spectrum() == 1)
|
||||
{
|
||||
color.r = (*this)(x, y, 0, 0);
|
||||
color.g = color.r;
|
||||
color.b = color.r;
|
||||
}
|
||||
else if (spectrum() == 2)
|
||||
{
|
||||
color.r = (*this)(x, y, 0, 0);
|
||||
color.g = (*this)(x, y, 0, 1);
|
||||
color.b = 0;
|
||||
}
|
||||
|
||||
else if (spectrum() == 3)
|
||||
{
|
||||
color.r = (*this)(x, y, 0, 0);
|
||||
color.g = (*this)(x, y, 0, 1);
|
||||
color.b = (*this)(x, y, 0, 2);
|
||||
}
|
||||
else if (spectrum() == 4)
|
||||
{
|
||||
color.r = (*this)(x, y, 0, 0);
|
||||
color.g = (*this)(x, y, 0, 1);
|
||||
color.b = (*this)(x, y, 0, 2);
|
||||
originAlpha = (*this)(x, y, 0, 3);
|
||||
}
|
||||
|
||||
if (_forceAlphaEnabled && originAlpha > _forceAlphaValue)
|
||||
color.a = _forceAlphaValue;
|
||||
else
|
||||
color.a = originAlpha;
|
||||
}
|
||||
|
||||
void Image::forceAlpha(uint8_t alpha)
|
||||
{
|
||||
_forceAlphaEnabled = true;
|
||||
_forceAlphaValue = alpha;
|
||||
}
|
||||
|
||||
void Image::computeStringSize(const std::string& text, const std::string& font, int fontsz, Size& size)
|
||||
{
|
||||
runOnFreeType(font, fontsz,
|
||||
[&](FT_Face& face) -> void
|
||||
{
|
||||
FT_BBox bbox;
|
||||
std::wstring wstr;
|
||||
convertText(text, wstr);
|
||||
|
||||
computeStringBBox(wstr, face, bbox);
|
||||
size.width = bbox.xMax - bbox.xMin;
|
||||
size.height = bbox.yMax - bbox.yMin;
|
||||
|
||||
size.width = Util::Math::alignup(size.width, 2);
|
||||
size.height = Util::Math::alignup(size.height, 2);
|
||||
});
|
||||
}
|
||||
|
||||
int Image::computeStringBBox(std::wstring text, FT_Face face, FT_BBox& bbox)
|
||||
{
|
||||
size_t i;
|
||||
int error;
|
||||
FT_BBox glyph_bbox;
|
||||
FT_Vector pen;
|
||||
FT_Glyph glyph;
|
||||
FT_GlyphSlot slot = face->glyph;
|
||||
|
||||
bbox.xMin = bbox.yMin = 32000;
|
||||
bbox.xMax = bbox.yMax = -32000;
|
||||
|
||||
pen.x = 0;
|
||||
pen.y = 0;
|
||||
|
||||
for (i = 0; i < text.length(); i++)
|
||||
{
|
||||
FT_Set_Transform(face, 0, &pen);
|
||||
|
||||
error = FT_Load_Char(face, text.at(i), FT_LOAD_RENDER);
|
||||
if (error)
|
||||
{
|
||||
logger().error("FT_Load_Char error: %d(%s)", error, std::string(FT_Error_String(error)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = FT_Get_Glyph(face->glyph, &glyph);
|
||||
if (error)
|
||||
{
|
||||
logger().error("FT_Get_Glyph error: %d(%s)", error, std::string(FT_Error_String(error)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
|
||||
|
||||
if (glyph_bbox.xMin < bbox.xMin)
|
||||
bbox.xMin = glyph_bbox.xMin;
|
||||
|
||||
if (glyph_bbox.yMin < bbox.yMin)
|
||||
bbox.yMin = glyph_bbox.yMin;
|
||||
|
||||
if (glyph_bbox.xMax > bbox.xMax)
|
||||
bbox.xMax = glyph_bbox.xMax;
|
||||
|
||||
if (glyph_bbox.yMax > bbox.yMax)
|
||||
bbox.yMax = glyph_bbox.yMax;
|
||||
|
||||
FT_Done_Glyph(glyph);
|
||||
|
||||
pen.x += slot->advance.x;
|
||||
pen.y += slot->advance.y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Image::convertText(const std::string& from, std::wstring& to)
|
||||
{
|
||||
Poco::UnicodeConverter::toUTF16(from, to);
|
||||
}
|
||||
|
||||
void Image::runOnFreeType(const std::string& font, int size, std::function<void(FT_Face&)> func)
|
||||
{
|
||||
FT_Library library;
|
||||
FT_Face face;
|
||||
FT_Error error;
|
||||
|
||||
error = FT_Init_FreeType(&library);
|
||||
if (error)
|
||||
throw Poco::InvalidAccessException(
|
||||
Poco::format("FreeType init error: %d(%s)", error, std::string(FT_Error_String(error))), error);
|
||||
error = FT_New_Face(library, font.c_str(), 0, &face);
|
||||
if (error)
|
||||
throw Poco::InvalidAccessException(
|
||||
Poco::format("FreeType new face error: %d(%s)", error, std::string(FT_Error_String(error))), error);
|
||||
|
||||
FT_Set_Pixel_Sizes(face, 0, size);
|
||||
func(face);
|
||||
|
||||
FT_Done_Face(face);
|
||||
FT_Done_FreeType(library);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
video/core/Image.h
Normal file
52
video/core/Image.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef CF80473A_40DD_43E5_B0D7_73F2205E9DF6
|
||||
#define CF80473A_40DD_43E5_B0D7_73F2205E9DF6
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Color.h"
|
||||
#include "GeometryTypes.h"
|
||||
#include <CImg.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <functional>
|
||||
|
||||
using namespace cimg_library;
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Image: public CImg<uint8_t>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<Image>;
|
||||
using CImg::CImg;
|
||||
|
||||
void drawText(const std::string& text, const std::string& font, int size, Color foreColor, Color bgColor);
|
||||
void drawRectangle(const Rect& rect, Color& color, int border = 0);
|
||||
void drawImage(int x, int y, Ptr pImage);
|
||||
void getPixel(int x, int y, Color& color);
|
||||
void forceAlpha(uint8_t alpha);
|
||||
|
||||
static void computeStringSize(const std::string& text, const std::string& font, int fontsz, Size& size);
|
||||
|
||||
protected:
|
||||
static int computeStringBBox(std::wstring text, FT_Face face, FT_BBox& bbox);
|
||||
static void convertText(const std::string& from, std::wstring& to);
|
||||
static void runOnFreeType(const std::string& font, int size, std::function<void(FT_Face&)> func);
|
||||
|
||||
private:
|
||||
bool _forceAlphaEnabled = false;
|
||||
uint8_t _forceAlphaValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
28
video/core/MediaType.h
Normal file
28
video/core/MediaType.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef D3D5F4A2_6250_4C2B_A293_7D6F3A83F95F
|
||||
#define D3D5F4A2_6250_4C2B_A293_7D6F3A83F95F
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class MediaType
|
||||
{
|
||||
UNKNOWN = -1,
|
||||
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
|
||||
DATA,
|
||||
SUBTITLE,
|
||||
ATTACHMENT,
|
||||
|
||||
NB
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
95
video/core/Muxer.cpp
Normal file
95
video/core/Muxer.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Muxer.h"
|
||||
#include "AudioComponent.h"
|
||||
#include "VideoComponent.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Muxer::Muxer(const std::string& name, const std::string& url): Component(name), _url(url)
|
||||
{
|
||||
}
|
||||
|
||||
Muxer::~Muxer()
|
||||
{
|
||||
}
|
||||
|
||||
void Muxer::start()
|
||||
{
|
||||
_task.run(
|
||||
[this](std::atomic<bool>& isCancelled) -> void
|
||||
{
|
||||
while (!isCancelled)
|
||||
{
|
||||
try
|
||||
{
|
||||
connect();
|
||||
while (!isCancelled)
|
||||
mux();
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s(CONNECT/MUX): %s", name(), e.displayText());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s(CONNECT/MUX): %s", name(), std::string(e.what()));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s(DISCONNECT): %s", name(), e.displayText());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s(DISCONNECT): %s", name(), std::string(e.what()));
|
||||
}
|
||||
|
||||
if (!isCancelled)
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Muxer::stop()
|
||||
{
|
||||
_task.cancel();
|
||||
}
|
||||
|
||||
void Muxer::onLink(Component::Ptr pPrevious)
|
||||
{
|
||||
if (dynamic_cast<AudioComponent*>(pPrevious.get()))
|
||||
packetQueues().add(pPrevious, name(), MediaType::AUDIO);
|
||||
else if (dynamic_cast<VideoComponent*>(pPrevious.get()))
|
||||
packetQueues().add(pPrevious, name(), MediaType::VIDEO);
|
||||
else
|
||||
{
|
||||
logger().error("%s: Unsupport class '%s'", name(), std::string(typeid(*pPrevious).name()));
|
||||
}
|
||||
}
|
||||
|
||||
void Muxer::onUnlink(Component::Ptr pPrevious)
|
||||
{
|
||||
packetQueues().remove(pPrevious);
|
||||
}
|
||||
|
||||
void Muxer::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
72
video/core/Muxer.h
Normal file
72
video/core/Muxer.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef B61EDCD6_1C1C_4355_AD2C_4A09C55099D0
|
||||
#define B61EDCD6_1C1C_4355_AD2C_4A09C55099D0
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Component.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/Task.h"
|
||||
#include <Poco/URI.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class MuxerSelector
|
||||
{
|
||||
public:
|
||||
static const std::string select(const std::string& url)
|
||||
{
|
||||
return Poco::URI(url).getScheme();
|
||||
}
|
||||
};
|
||||
|
||||
class Muxer: public Component,
|
||||
public ::Util::FactoryManager<Muxer, const std::string, MuxerSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<Muxer>;
|
||||
|
||||
public:
|
||||
Muxer(const std::string& name, const std::string& url);
|
||||
virtual ~Muxer();
|
||||
|
||||
public:
|
||||
const std::string& url() const;
|
||||
std::string scheme() const;
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void onLink(Component::Ptr pPrevious) override;
|
||||
void onUnlink(Component::Ptr pPrevious) override;
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
|
||||
virtual void connect() = 0;
|
||||
virtual void mux() = 0;
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
private:
|
||||
std::string _url;
|
||||
Util::Task _task;
|
||||
};
|
||||
|
||||
inline const std::string& Muxer::url() const
|
||||
{
|
||||
return _url;
|
||||
}
|
||||
|
||||
inline std::string Muxer::scheme() const
|
||||
{
|
||||
return Poco::URI(url()).getScheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
18
video/core/NotificationQueue.cpp
Normal file
18
video/core/NotificationQueue.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "NotificationQueue.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
}
|
||||
}
|
||||
132
video/core/NotificationQueue.h
Normal file
132
video/core/NotificationQueue.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#ifndef D93EB1DD_2BCB_47B7_9361_91841576013D
|
||||
#define D93EB1DD_2BCB_47B7_9361_91841576013D
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "MediaType.h"
|
||||
#include <Poco/Notification.h>
|
||||
#include <Poco/NotificationQueue.h>
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
template <class T> class Notification: public Poco::Notification
|
||||
{
|
||||
public:
|
||||
Notification(typename T::Ptr pObject): _pObject(pObject)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
typename T::Ptr get()
|
||||
{
|
||||
return _pObject;
|
||||
}
|
||||
|
||||
private:
|
||||
typename T::Ptr _pObject;
|
||||
};
|
||||
|
||||
enum class QueueStatus
|
||||
{
|
||||
UNKNOWN = -1,
|
||||
NORMAL,
|
||||
OVERHALF,
|
||||
FULL,
|
||||
};
|
||||
|
||||
template <class T> class NotificationQueue
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<NotificationQueue<T>>;
|
||||
|
||||
public:
|
||||
NotificationQueue(const std::string& name, MediaType type, int limit = 300);
|
||||
|
||||
public:
|
||||
MediaType type() const;
|
||||
|
||||
void enqueue(typename T::Ptr pObject, bool force = true);
|
||||
typename T::Ptr dequeue(long timeout);
|
||||
void clear();
|
||||
QueueStatus status();
|
||||
int size();
|
||||
|
||||
private:
|
||||
Poco::NotificationQueue _queue;
|
||||
std::string _name;
|
||||
MediaType _type;
|
||||
int _limit;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline NotificationQueue<T>::NotificationQueue(const std::string& name, MediaType type, int limit)
|
||||
: _name(name), _type(type), _limit(limit)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T> inline MediaType NotificationQueue<T>::type() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
template <class T> inline void NotificationQueue<T>::enqueue(typename T::Ptr pObject, bool force)
|
||||
{
|
||||
if (_queue.size() >= _limit)
|
||||
{
|
||||
if (!force)
|
||||
throw std::overflow_error("Queue overflow!");
|
||||
|
||||
std::cout << "NotificationQueue(" + _name + "[" << (type() == MediaType::VIDEO ? "Video" : "Audio")
|
||||
<< "]): OVERFLOW!" << std::endl;
|
||||
_queue.dequeueNotification()->release();
|
||||
}
|
||||
|
||||
_queue.enqueueNotification(new Notification<T>(pObject));
|
||||
}
|
||||
|
||||
template <class T> inline typename T::Ptr NotificationQueue<T>::dequeue(long timeout)
|
||||
{
|
||||
Poco::Notification::Ptr p = _queue.waitDequeueNotification(timeout);
|
||||
if (!p)
|
||||
throw Poco::TimeoutException("Dequeue timeout: " + std::string(type() == MediaType::VIDEO ? "Video" : "Audio"));
|
||||
|
||||
return p.cast<Notification<T>>()->get();
|
||||
}
|
||||
|
||||
template <class T> inline void NotificationQueue<T>::clear()
|
||||
{
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
template <class T> inline QueueStatus NotificationQueue<T>::status()
|
||||
{
|
||||
QueueStatus s = QueueStatus::NORMAL;
|
||||
const auto size = _queue.size();
|
||||
|
||||
if (size >= _limit)
|
||||
s = QueueStatus::FULL;
|
||||
else if (size >= (_limit >> 1))
|
||||
s = QueueStatus::OVERHALF;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class T> inline int NotificationQueue<T>::size()
|
||||
{
|
||||
return _queue.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
18
video/core/Notifications.cpp
Normal file
18
video/core/Notifications.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Notifications.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
}
|
||||
}
|
||||
39
video/core/Notifications.h
Normal file
39
video/core/Notifications.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef EE166245_4519_4C28_AD01_A52B7613E95A
|
||||
#define EE166245_4519_4C28_AD01_A52B7613E95A
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Component.h"
|
||||
#include "util/Configuration.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class ParameterChangedNotification: public Component::Notification
|
||||
{
|
||||
public:
|
||||
using Parameters = Util::Configuration::ParameterNames;
|
||||
|
||||
ParameterChangedNotification(const Parameters& parameters): _parameters(parameters)
|
||||
{
|
||||
}
|
||||
|
||||
const Parameters& parameters() const
|
||||
{
|
||||
return _parameters;
|
||||
}
|
||||
|
||||
private:
|
||||
Parameters _parameters;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
137
video/core/Packet.cpp
Normal file
137
video/core/Packet.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Packet.h"
|
||||
#include "Logger.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
const uint32_t Packet::FLAG_IS_VIDEO = 0x80000000;
|
||||
const uint32_t Packet::FLAG_IS_KEY = 0x00000001;
|
||||
const uint32_t Packet::FLAG_IS_AAC_HEADER = 0x00000002;
|
||||
const uint32_t Packet::FLAG_IS_SEI = 0x04000000;
|
||||
|
||||
Packet::Packet(): _flags(0), _pts(0), _dts(0), _pData(nullptr), _length(0), _pAVPacket(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Packet::Packet(const SerialCode& code, uint32_t flags, uint8_t* data, uint32_t length, int64_t pts, int64_t dts)
|
||||
: _code(code), _flags(flags), _pts(pts), _dts(dts), _pData(data), _length(length), _pAVPacket(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(USING_FFMPEG)
|
||||
Packet::Packet(const SerialCode& code, bool isVideo, AVPacket** packet)
|
||||
: _code(code), _pts((*packet)->pts), _dts((*packet)->dts), _pData(nullptr), _length((*packet)->size),
|
||||
_pAVPacket(*packet)
|
||||
{
|
||||
_flags = 0;
|
||||
if ((*packet)->flags & AV_PKT_FLAG_KEY)
|
||||
_flags |= Packet::FLAG_IS_KEY;
|
||||
if (isVideo)
|
||||
_flags |= Packet::FLAG_IS_VIDEO;
|
||||
|
||||
*packet = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
Packet::Packet(const Packet& other)
|
||||
{
|
||||
_code = other._code;
|
||||
_flags = other._flags;
|
||||
_pData = other._pData;
|
||||
_length = other._length;
|
||||
_pts = other._pts;
|
||||
_dts = other._dts;
|
||||
|
||||
#if defined(USING_FFMPEG)
|
||||
if (other._pAVPacket)
|
||||
_pAVPacket = av_packet_clone(other._pAVPacket);
|
||||
else
|
||||
_pAVPacket = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
Packet::~Packet()
|
||||
{
|
||||
#if defined(USING_FFMPEG)
|
||||
if (_pAVPacket)
|
||||
av_packet_free(&_pAVPacket);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USING_FFMPEG)
|
||||
void Packet::moveTo(AVPacket** packet)
|
||||
{
|
||||
if (_pAVPacket)
|
||||
{
|
||||
*packet = _pAVPacket;
|
||||
_pData = nullptr;
|
||||
_length = 0;
|
||||
_pAVPacket = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
*packet = buildAVPacket();
|
||||
_pData = nullptr;
|
||||
_length = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t* Packet::data()
|
||||
{
|
||||
#if defined(USING_FFMPEG)
|
||||
if (_pAVPacket)
|
||||
return _pAVPacket->data;
|
||||
#endif
|
||||
|
||||
return _pData.get();
|
||||
}
|
||||
|
||||
#if defined(USING_FFMPEG)
|
||||
AVPacket* Packet::buildAVPacket()
|
||||
{
|
||||
AVPacket* pPacket = av_packet_alloc();
|
||||
pPacket->pts = _pts;
|
||||
pPacket->dts = _dts;
|
||||
if (_flags & FLAG_IS_KEY)
|
||||
pPacket->flags |= AV_PKT_FLAG_KEY;
|
||||
else
|
||||
pPacket->flags &= ~AV_PKT_FLAG_KEY;
|
||||
|
||||
convertToAVPacket(pPacket);
|
||||
return pPacket;
|
||||
}
|
||||
|
||||
void Packet::convertToAVPacket(AVPacket* pPacket)
|
||||
{
|
||||
if (_pData != nullptr)
|
||||
{
|
||||
uint8_t* data = (uint8_t*)av_malloc(_length);
|
||||
memcpy(data, _pData, _length);
|
||||
int rc = av_packet_from_data(pPacket, data, _length);
|
||||
if (rc < 0)
|
||||
{
|
||||
logger().error("Create av-packet FAIL!");
|
||||
av_free(data);
|
||||
}
|
||||
|
||||
_pData.reset();
|
||||
}
|
||||
else
|
||||
throw Poco::DataException("Media::Packet::buildAVPacket: No source data");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
126
video/core/Packet.h
Normal file
126
video/core/Packet.h
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#ifndef AE3F35DC_04CA_417F_8D57_CADC8913678F
|
||||
#define AE3F35DC_04CA_417F_8D57_CADC8913678F
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "NotificationQueue.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include "SerialCode.h"
|
||||
#if defined(USING_FFMPEG)
|
||||
extern "C"
|
||||
{
|
||||
#include <libavcodec/packet.h>
|
||||
#include <libavutil/mem.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<Packet>;
|
||||
|
||||
static const uint32_t FLAG_IS_VIDEO;
|
||||
static const uint32_t FLAG_IS_KEY;
|
||||
static const uint32_t FLAG_IS_AAC_HEADER;
|
||||
static const uint32_t FLAG_IS_SEI;
|
||||
|
||||
public:
|
||||
Packet();
|
||||
Packet(const SerialCode& code, uint32_t flags, uint8_t* data, uint32_t length, int64_t pts, int64_t dts);
|
||||
#if defined(USING_FFMPEG)
|
||||
Packet(const SerialCode& code, bool isVideo, AVPacket** packet);
|
||||
#endif
|
||||
Packet(const Packet& other);
|
||||
virtual ~Packet();
|
||||
|
||||
virtual void moveTo(AVPacket** packet);
|
||||
bool isVideo() const;
|
||||
bool isKeyFrame() const;
|
||||
Ptr clone();
|
||||
|
||||
const SerialCode& serialCode();
|
||||
const uint8_t* data();
|
||||
uint32_t length();
|
||||
uint64_t pts();
|
||||
uint64_t dts();
|
||||
uint32_t flags();
|
||||
|
||||
Poco::SharedPtr<uint8_t>& sharedData();
|
||||
|
||||
protected:
|
||||
#if defined(USING_FFMPEG)
|
||||
AVPacket* buildAVPacket();
|
||||
virtual void convertToAVPacket(AVPacket* pPacket);
|
||||
#endif
|
||||
|
||||
private:
|
||||
SerialCode _code;
|
||||
uint32_t _flags;
|
||||
int64_t _pts, _dts;
|
||||
Poco::SharedPtr<uint8_t> _pData;
|
||||
uint32_t _length;
|
||||
#if defined(USING_FFMPEG)
|
||||
AVPacket* _pAVPacket;
|
||||
#endif
|
||||
};
|
||||
|
||||
using PacketQueue = NotificationQueue<Packet>;
|
||||
|
||||
inline bool Packet::isVideo() const
|
||||
{
|
||||
return (_flags & FLAG_IS_VIDEO) > 0;
|
||||
}
|
||||
|
||||
inline bool Packet::isKeyFrame() const
|
||||
{
|
||||
return (_flags & FLAG_IS_KEY) > 0;
|
||||
}
|
||||
|
||||
inline Packet::Ptr Packet::clone()
|
||||
{
|
||||
return new Packet(*this);
|
||||
}
|
||||
|
||||
inline uint32_t Packet::length()
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
inline uint64_t Packet::pts()
|
||||
{
|
||||
return _pts;
|
||||
}
|
||||
|
||||
inline uint64_t Packet::dts()
|
||||
{
|
||||
return _dts;
|
||||
}
|
||||
|
||||
inline uint32_t Packet::flags()
|
||||
{
|
||||
return _flags;
|
||||
}
|
||||
|
||||
inline const SerialCode& Packet::serialCode()
|
||||
{
|
||||
return _code;
|
||||
}
|
||||
|
||||
inline Poco::SharedPtr<uint8_t>& Packet::sharedData()
|
||||
{
|
||||
return _pData;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
119
video/core/SampleFormat.cpp
Normal file
119
video/core/SampleFormat.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "SampleFormat.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* TYPEDEFS
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct SampleFormatInfo
|
||||
{
|
||||
std::string name;
|
||||
int bits;
|
||||
bool isPlanar;
|
||||
SampleFormat altFormat;
|
||||
};
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL VARIABLES
|
||||
*******************************************************************************
|
||||
*/
|
||||
static const SampleFormatInfo sampleFormatInfo[(int)SampleFormat::NB] = {
|
||||
{"u8", 8, false, SampleFormat::U8P}, {"s16", 16, false, SampleFormat::S16P},
|
||||
{"s32", 32, false, SampleFormat::S32P}, {"s64", 64, false, SampleFormat::S64P},
|
||||
{"flt", 32, false, SampleFormat::FLTP}, {"dbl", 64, false, SampleFormat::DBLP},
|
||||
{"u8p", 8, true, SampleFormat::U8}, {"s16p", 16, true, SampleFormat::S16},
|
||||
{"s32p", 32, true, SampleFormat::S32}, {"s64p", 64, true, SampleFormat::S64},
|
||||
{"fltp", 32, true, SampleFormat::FLT}, {"dblp", 64, true, SampleFormat::DBL},
|
||||
};
|
||||
|
||||
static const std::string UNKNOWN = "UNKNOWN";
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* LOCAL FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
static inline bool isSampleFormatValid(SampleFormat format)
|
||||
{
|
||||
return ((int)format >= 0 && format < SampleFormat::NB);
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* FUNCTIONS
|
||||
*******************************************************************************
|
||||
*/
|
||||
const std::string& sampleFormatName(SampleFormat format)
|
||||
{
|
||||
if (isSampleFormatValid(format))
|
||||
return sampleFormatInfo[(int)format].name;
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
int sampleFormatBitWidth(SampleFormat format)
|
||||
{
|
||||
if (isSampleFormatValid(format))
|
||||
return sampleFormatInfo[(int)format].bits;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sampleFormatBytesPerSample(SampleFormat format)
|
||||
{
|
||||
if (isSampleFormatValid(format))
|
||||
return sampleFormatInfo[(int)format].bits >> 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool sampleFormatIsPlanar(SampleFormat format)
|
||||
{
|
||||
if (isSampleFormatValid(format))
|
||||
return sampleFormatInfo[(int)format].isPlanar;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int sampleFormatCalcLineSize(SampleFormat format, ChannelLayout layout, int samples)
|
||||
{
|
||||
int size = sampleFormatBytesPerSample(format) * samples;
|
||||
if (!sampleFormatIsPlanar(format))
|
||||
size *= channelLayoutChannels(layout);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
SampleFormat sampleFormatGetPackedFormat(SampleFormat format)
|
||||
{
|
||||
if (!isSampleFormatValid(format))
|
||||
return SampleFormat::NONE;
|
||||
|
||||
if (!sampleFormatIsPlanar(format))
|
||||
return format;
|
||||
|
||||
return sampleFormatInfo[(int)format].altFormat;
|
||||
}
|
||||
|
||||
SampleFormat sampleFormatGetPlanarFormat(SampleFormat format)
|
||||
{
|
||||
if (!isSampleFormatValid(format))
|
||||
return SampleFormat::NONE;
|
||||
|
||||
if (sampleFormatIsPlanar(format))
|
||||
return format;
|
||||
|
||||
return sampleFormatInfo[(int)format].altFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
video/core/SampleFormat.h
Normal file
50
video/core/SampleFormat.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef D05E30E1_E0D8_43BB_A05A_20ABEF7E2072
|
||||
#define D05E30E1_E0D8_43BB_A05A_20ABEF7E2072
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "ChannelLayout.h"
|
||||
#include <string>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class SampleFormat
|
||||
{
|
||||
NONE = -1,
|
||||
|
||||
U8,
|
||||
S16,
|
||||
S32,
|
||||
S64,
|
||||
FLT,
|
||||
DBL,
|
||||
|
||||
U8P,
|
||||
S16P,
|
||||
S32P,
|
||||
S64P,
|
||||
FLTP,
|
||||
DBLP,
|
||||
|
||||
NB,
|
||||
};
|
||||
|
||||
const std::string& sampleFormatName(SampleFormat format);
|
||||
int sampleFormatBitWidth(SampleFormat format);
|
||||
int sampleFormatBytesPerSample(SampleFormat format);
|
||||
bool sampleFormatIsPlanar(SampleFormat format);
|
||||
int sampleFormatCalcLineSize(SampleFormat format, ChannelLayout layout, int samples);
|
||||
SampleFormat sampleFormatGetPackedFormat(SampleFormat format);
|
||||
SampleFormat sampleFormatGetPlanarFormat(SampleFormat format);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
107
video/core/SerialCode.cpp
Normal file
107
video/core/SerialCode.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "SerialCode.h"
|
||||
#include <Poco/Random.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
bool SerialCode::empty() const
|
||||
{
|
||||
return _value.isNull();
|
||||
}
|
||||
|
||||
void SerialCode::reset()
|
||||
{
|
||||
return _value.clear();
|
||||
}
|
||||
|
||||
const SerialCode& SerialCode::operator++()
|
||||
{
|
||||
if (empty())
|
||||
_value = 0;
|
||||
|
||||
++_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SerialCode& SerialCode::operator++(int)
|
||||
{
|
||||
if (empty())
|
||||
_value = 0;
|
||||
|
||||
_value++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SerialCode::operator!=(const SerialCode& other)
|
||||
{
|
||||
return _value != other._value;
|
||||
}
|
||||
|
||||
const SerialCode& SerialCode::operator=(const SerialCode& other)
|
||||
{
|
||||
_value = other._value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const SerialCode& SerialCode::operator=(const uint32_t value)
|
||||
{
|
||||
_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
SerialCodeManager::SerialCodeManager()
|
||||
{
|
||||
Poco::Random r;
|
||||
r.seed();
|
||||
_code = r.next();
|
||||
}
|
||||
|
||||
void SerialCodeManager::increment()
|
||||
{
|
||||
_code++;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
bool SerialCodeWatcher::isChanged(const SerialCode& code, bool firstAsChanged)
|
||||
{
|
||||
if (_code.empty())
|
||||
{
|
||||
_code = code;
|
||||
return firstAsChanged;
|
||||
}
|
||||
|
||||
if (_code != code)
|
||||
{
|
||||
_code = code;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SerialCodeWatcher::reset()
|
||||
{
|
||||
_code.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
77
video/core/SerialCode.h
Normal file
77
video/core/SerialCode.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef F54A2617_3BA7_4779_88A5_AB040FD9B002
|
||||
#define F54A2617_3BA7_4779_88A5_AB040FD9B002
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "CodecID.h"
|
||||
#include <Poco/Nullable.h>
|
||||
#include <Poco/Any.h>
|
||||
#include <map>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class SerialCode
|
||||
{
|
||||
public:
|
||||
bool empty() const;
|
||||
void reset();
|
||||
const SerialCode& operator++();
|
||||
const SerialCode& operator++(int);
|
||||
bool operator!=(const SerialCode& other);
|
||||
const SerialCode& operator=(const SerialCode& other);
|
||||
const SerialCode& operator=(const uint32_t other);
|
||||
|
||||
private:
|
||||
Poco::Nullable<uint32_t> _value;
|
||||
};
|
||||
|
||||
class SerialCodeManager
|
||||
{
|
||||
public:
|
||||
SerialCodeManager();
|
||||
|
||||
template <class T> void put(const std::string& name, const T& value);
|
||||
void increment();
|
||||
|
||||
const SerialCode& code() const;
|
||||
|
||||
private:
|
||||
SerialCode _code;
|
||||
std::map<std::string, Poco::Any> _values;
|
||||
};
|
||||
|
||||
template <class T> inline void SerialCodeManager::put(const std::string& name, const T& value)
|
||||
{
|
||||
if (_values.find(name) == _values.end() || Poco::AnyCast<T>(_values[name]) != value)
|
||||
{
|
||||
_values[name] = value;
|
||||
_code++;
|
||||
}
|
||||
}
|
||||
|
||||
inline const SerialCode& SerialCodeManager::code() const
|
||||
{
|
||||
return _code;
|
||||
}
|
||||
|
||||
class SerialCodeWatcher
|
||||
{
|
||||
public:
|
||||
bool isChanged(const SerialCode& code, bool firstAsChanged = false);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
SerialCode _code;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
37
video/core/System.cpp
Normal file
37
video/core/System.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "System.h"
|
||||
#include <Poco/Exception.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
System* System::_pInstance = nullptr;
|
||||
System::System()
|
||||
{
|
||||
}
|
||||
|
||||
System::~System()
|
||||
{
|
||||
delete _pInstance;
|
||||
_pInstance = nullptr;
|
||||
}
|
||||
|
||||
uint64_t System::timestamp()
|
||||
{
|
||||
if (!_pInstance)
|
||||
throw Poco::IllegalStateException("Unregister implement class.");
|
||||
|
||||
return _pInstance->getTimestamp();
|
||||
}
|
||||
}
|
||||
}
|
||||
45
video/core/System.h
Normal file
45
video/core/System.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef BC5898D8_C525_4FD9_9609_B2112CB1BE92
|
||||
#define BC5898D8_C525_4FD9_9609_B2112CB1BE92
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <Poco/Poco.h>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class System
|
||||
{
|
||||
protected:
|
||||
System();
|
||||
|
||||
public:
|
||||
virtual ~System();
|
||||
|
||||
public:
|
||||
static uint64_t timestamp();
|
||||
|
||||
template <class T> static void registerClass();
|
||||
|
||||
protected:
|
||||
virtual uint64_t getTimestamp() = 0;
|
||||
|
||||
private:
|
||||
static System* _pInstance;
|
||||
};
|
||||
|
||||
template <class T> inline void System::registerClass()
|
||||
{
|
||||
_pInstance = new T();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
31
video/core/VideoComponent.cpp
Normal file
31
video/core/VideoComponent.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "AudioComponent.h"
|
||||
#include "VideoComponent.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
Core::VideoComponent::VideoComponent(const std::string& name): Component(name)
|
||||
{
|
||||
setWidth(1920);
|
||||
setHeight(1080);
|
||||
setFrameRate(30);
|
||||
setVideoScanMode(VideoScanMode::PROGRESSIVE);
|
||||
setSignalLost(true);
|
||||
}
|
||||
|
||||
VideoComponent::~VideoComponent()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
45
video/core/VideoComponent.h
Normal file
45
video/core/VideoComponent.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef CAD9434C_3708_4C44_8A9D_2E146CFECEAC
|
||||
#define CAD9434C_3708_4C44_8A9D_2E146CFECEAC
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "Component.h"
|
||||
#include "CodecID.h"
|
||||
#include "ChannelLayout.h"
|
||||
#include "CodecProfile.h"
|
||||
#include "BitrateControl.h"
|
||||
#include "GOPMode.h"
|
||||
#include "SampleFormat.h"
|
||||
#include "VideoScanMode.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoComponent: public Component
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<VideoComponent>;
|
||||
|
||||
public:
|
||||
VideoComponent(const std::string& name);
|
||||
virtual ~VideoComponent();
|
||||
virtual void reset() {};
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Width, uint32_t);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Height, uint32_t);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(FrameRate, int);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(VideoScanMode, VideoScanMode);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(SignalLost, bool);
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
125
video/core/VideoDecoder.cpp
Normal file
125
video/core/VideoDecoder.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoDecoder.h"
|
||||
#include "Logger.h"
|
||||
#include "Demuxer.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoDecodeHandler::VideoDecodeHandler(const std::string& name, CodecID codecID, VideoComponent::Ptr pPrevious)
|
||||
: _name(name), _pPrevious(pPrevious)
|
||||
{
|
||||
}
|
||||
|
||||
VideoDecodeHandler::~VideoDecodeHandler()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoDecoder::VideoDecoder(const std::string& name): VideoComponent(name)
|
||||
{
|
||||
}
|
||||
|
||||
VideoDecoder::~VideoDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoDecoder::start()
|
||||
{
|
||||
_task.run(
|
||||
[this](std::atomic<bool>& isCancelled) -> void
|
||||
{
|
||||
VideoDecodeHandler::Ptr pHandler;
|
||||
while (!isCancelled)
|
||||
{
|
||||
try
|
||||
{
|
||||
Frame::Ptr pFrame = nullptr;
|
||||
do
|
||||
{
|
||||
if (pHandler.isNull())
|
||||
{
|
||||
pFrame = pHandler->read();
|
||||
if (!pFrame.isNull())
|
||||
break;
|
||||
}
|
||||
|
||||
Packet::Ptr pPacket = readPacket(1000);
|
||||
if (serialCodeWatcher().isChanged(pPacket->serialCode(), true))
|
||||
{
|
||||
auto pPrevious = previousComponents().first();
|
||||
auto pDemuxer = pPrevious.cast<Demuxer>();
|
||||
if (pDemuxer)
|
||||
{
|
||||
auto pEncoder = pDemuxer->videoEncoder();
|
||||
pEncoder->config().copyTo(config());
|
||||
serialCodeManager().increment();
|
||||
|
||||
pHandler = VideoDecodeHandler::create(pEncoder->getCodecID(), pEncoder);
|
||||
}
|
||||
else
|
||||
throw Poco::NotImplementedException(
|
||||
Poco::format("Unsupported previous component: %s", pPrevious->name()));
|
||||
}
|
||||
|
||||
pHandler->write(pPacket);
|
||||
pFrame = pHandler->read();
|
||||
} while (false);
|
||||
|
||||
if (!pFrame)
|
||||
continue;
|
||||
|
||||
postToNextComponents(sharedFromThis(), pFrame, MediaType::VIDEO);
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), e.displayText());
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), std::string(e.what()));
|
||||
Poco::Thread::sleep(1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void VideoDecoder::stop()
|
||||
{
|
||||
_task.cancel();
|
||||
}
|
||||
|
||||
Packet::Ptr VideoDecoder::readPacket(uint32_t timeout)
|
||||
{
|
||||
if (packetQueues().empty())
|
||||
throw Poco::NotFoundException("Packet queues is empty.");
|
||||
|
||||
return packetQueues().first()->dequeue(timeout);
|
||||
}
|
||||
|
||||
void VideoDecoder::onLink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
packetQueues().add(pPrevious, name(), MediaType::VIDEO);
|
||||
}
|
||||
|
||||
void VideoDecoder::onUnlink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
packetQueues().remove(pPrevious);
|
||||
}
|
||||
}
|
||||
}
|
||||
115
video/core/VideoDecoder.h
Normal file
115
video/core/VideoDecoder.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef ACE5DB6F_B3E6_42E2_8DAE_6D520F93DC50
|
||||
#define ACE5DB6F_B3E6_42E2_8DAE_6D520F93DC50
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoComponent.h"
|
||||
#include "AudioDecoder.h"
|
||||
#include "CodecID.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/Task.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <functional>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoDecodeHandlerSelector
|
||||
{
|
||||
public:
|
||||
static CodecID select(CodecID codec, VideoComponent::Ptr)
|
||||
{
|
||||
return codec;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoDecodeHandler: public ::Util::FactoryManager<VideoDecodeHandler, CodecID, VideoDecodeHandlerSelector,
|
||||
CodecID, VideoComponent::Ptr>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<VideoDecodeHandler>;
|
||||
|
||||
public:
|
||||
VideoDecodeHandler(const std::string& name, CodecID codecID, VideoComponent::Ptr pPrevious);
|
||||
virtual ~VideoDecodeHandler();
|
||||
|
||||
public:
|
||||
const std::string& name() const;
|
||||
VideoComponent::Ptr previous() const;
|
||||
|
||||
virtual void write(Packet::Ptr pPacket) = 0;
|
||||
virtual Frame::Ptr read() = 0;
|
||||
|
||||
private:
|
||||
const std::string _name;
|
||||
VideoComponent::Ptr _pPrevious;
|
||||
};
|
||||
|
||||
inline const std::string& VideoDecodeHandler::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
inline VideoComponent::Ptr VideoDecodeHandler::previous() const
|
||||
{
|
||||
return _pPrevious;
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoDecoderSelector
|
||||
{
|
||||
public:
|
||||
static int select(const std::string&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoDecoder: public VideoComponent,
|
||||
public ::Util::FactoryManager<VideoDecoder, int, VideoDecoderSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<VideoDecoder>;
|
||||
|
||||
public:
|
||||
VideoDecoder(const std::string& name);
|
||||
virtual ~VideoDecoder();
|
||||
virtual void reset() {};
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
virtual Frame::Ptr decode() = 0;
|
||||
Packet::Ptr readPacket(uint32_t timeout = 4000);
|
||||
|
||||
void onLink(Core::Component::Ptr pPrevious) override;
|
||||
void onUnlink(Core::Component::Ptr pPrevious) override;
|
||||
|
||||
Util::Task& task();
|
||||
|
||||
PROPERTY_DECLARE(AssociatedAudio, AudioDecoder::Ptr, nullptr, public, public)
|
||||
|
||||
private:
|
||||
Util::Task _task;
|
||||
};
|
||||
|
||||
inline Util::Task& VideoDecoder::task()
|
||||
{
|
||||
return _task;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
87
video/core/VideoEncoder.cpp
Normal file
87
video/core/VideoEncoder.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoEncoder.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoEncoder::VideoEncoder(const std::string& name, CodecID codecID): VideoComponent(name)
|
||||
{
|
||||
setCodecID(codecID);
|
||||
|
||||
setCodecProfile(CodecProfile::H264_MAIN);
|
||||
setBitrate(4000000);
|
||||
setGOP(30);
|
||||
setGOPMode(GOPMode::NORMAL_P);
|
||||
setBitrateControl(BitrateControl::VBR);
|
||||
setAdaptable(false);
|
||||
}
|
||||
|
||||
VideoEncoder::~VideoEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoEncoder::start()
|
||||
{
|
||||
_task.run(
|
||||
[this](std::atomic<bool>& isCancelled) -> void
|
||||
{
|
||||
while (!isCancelled)
|
||||
{
|
||||
try
|
||||
{
|
||||
postToNextComponents(sharedFromThis(), encode(readFrame()), MediaType::VIDEO);
|
||||
}
|
||||
catch (Poco::Exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), e.displayText());
|
||||
Poco::Thread::sleep(100);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
logger().error("%s: %s", name(), std::string(e.what()));
|
||||
Poco::Thread::sleep(100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void VideoEncoder::stop()
|
||||
{
|
||||
_task.cancel();
|
||||
}
|
||||
|
||||
Packet::Ptr VideoEncoder::encode(Frame::Ptr pFrame)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Frame::Ptr VideoEncoder::readFrame(uint32_t timeout)
|
||||
{
|
||||
if (previousComponents().size() == 0)
|
||||
throw Poco::NotFoundException("No previous components");
|
||||
|
||||
return frameQueues().first()->dequeue(timeout);
|
||||
}
|
||||
|
||||
void VideoEncoder::onLink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
frameQueues().add(pPrevious, name(), MediaType::VIDEO);
|
||||
}
|
||||
|
||||
void VideoEncoder::onUnlink(Core::Component::Ptr pPrevious)
|
||||
{
|
||||
frameQueues().remove(pPrevious);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
video/core/VideoEncoder.h
Normal file
75
video/core/VideoEncoder.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef ED3217CD_F637_44D5_A6D9_E7AE0B9C3A4D
|
||||
#define ED3217CD_F637_44D5_A6D9_E7AE0B9C3A4D
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoComponent.h"
|
||||
#include "CodecID.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include "util/Task.h"
|
||||
#include <Poco/SharedPtr.h>
|
||||
#include <functional>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoEncoderSelector
|
||||
{
|
||||
public:
|
||||
static CodecID select(CodecID codecID)
|
||||
{
|
||||
return codecID;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoEncoder: public VideoComponent,
|
||||
public ::Util::FactoryManager<VideoEncoder, CodecID, VideoEncoderSelector, CodecID>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<VideoEncoder>;
|
||||
|
||||
public:
|
||||
VideoEncoder(const std::string& name, CodecID codecID);
|
||||
virtual ~VideoEncoder();
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(CodecID, CodecID);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(CodecProfile, CodecProfile);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Bitrate, int64_t);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(GOP, int);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(GOPMode, GOPMode);
|
||||
CONFIGURABLE_DECLARE_PARAMETER(BitrateControl, BitrateControl);
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Adaptable, bool);
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
virtual Packet::Ptr encode(Frame::Ptr pFrame);
|
||||
Frame::Ptr readFrame(uint32_t timeout = 4000);
|
||||
|
||||
void onLink(Core::Component::Ptr pPrevious) override;
|
||||
void onUnlink(Core::Component::Ptr pPrevious) override;
|
||||
|
||||
Util::Task& task();
|
||||
|
||||
private:
|
||||
Util::Task _task;
|
||||
};
|
||||
|
||||
inline Util::Task& VideoEncoder::task()
|
||||
{
|
||||
return _task;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
99
video/core/VideoMixer.cpp
Normal file
99
video/core/VideoMixer.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoMixer.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoMixer::Channel::Channel(const std::string& name, VideoMixer::Ptr pMixer, uint32_t id)
|
||||
: VideoComponent(Poco::format(name + "/CH#%u", id)), _pMixer(pMixer), _id(id)
|
||||
{
|
||||
Region region = {
|
||||
0.0,
|
||||
0.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
};
|
||||
setRegion(region);
|
||||
setAttach(false);
|
||||
setInput(nullptr);
|
||||
setZOrder(0);
|
||||
}
|
||||
|
||||
VideoMixer::Channel::~Channel()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoMixer::Channel::onConfigChanged(ParameterNames& names)
|
||||
{
|
||||
if (names.exists(_Input))
|
||||
{
|
||||
unlink(true, false);
|
||||
if (getAttach())
|
||||
{
|
||||
auto pInput = getInput();
|
||||
if (pInput)
|
||||
{
|
||||
getInput()->link(sharedFromThis());
|
||||
link(mixer());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (names.exists(_Attach))
|
||||
{
|
||||
auto pInput = getInput();
|
||||
if (pInput)
|
||||
{
|
||||
if (getAttach())
|
||||
{
|
||||
getInput()->link(sharedFromThis());
|
||||
link(mixer());
|
||||
}
|
||||
else
|
||||
getInput()->unlink(sharedFromThis());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoMixer::VideoMixer(const std::string& name): VideoComponent(name)
|
||||
{
|
||||
}
|
||||
|
||||
VideoMixer::~VideoMixer()
|
||||
{
|
||||
}
|
||||
|
||||
VideoMixer::Channel::Ptr VideoMixer::getChannel(uint32_t id)
|
||||
{
|
||||
if (_channels.find(id) == _channels.end())
|
||||
_channels[id] = createChannel(id);
|
||||
|
||||
return _channels[id];
|
||||
}
|
||||
|
||||
void VideoMixer::start()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoMixer::stop()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
118
video/core/VideoMixer.h
Normal file
118
video/core/VideoMixer.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef D84CC9FA_711B_4439_A0F8_424C43E082CB
|
||||
#define D84CC9FA_711B_4439_A0F8_424C43E082CB
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoComponent.h"
|
||||
#include "util/FactoryManager.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoMixerSelector
|
||||
{
|
||||
public:
|
||||
static int select(const std::string&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoMixer: public VideoComponent,
|
||||
public ::Util::FactoryManager<VideoMixer, int, VideoMixerSelector, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<VideoMixer>;
|
||||
class Channel: public VideoComponent
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<Channel>;
|
||||
|
||||
struct Region
|
||||
{
|
||||
double x;
|
||||
double y;
|
||||
double width;
|
||||
double height;
|
||||
|
||||
bool operator==(const Region& other) const
|
||||
{
|
||||
return std::abs(x - other.x) < 1e-9 && std::abs(y - other.y) < 1e-9 &&
|
||||
std::abs(width - other.width) < 1e-9 && std::abs(height - other.height) < 1e-9;
|
||||
}
|
||||
|
||||
bool operator!=(const Region& other) const
|
||||
{
|
||||
return !(this->operator==(other));
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
Channel(const std::string& name, VideoMixer::Ptr, uint32_t id);
|
||||
|
||||
public:
|
||||
~Channel();
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Region, Region)
|
||||
CONFIGURABLE_DECLARE_PARAMETER(ZOrder, int)
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Attach, bool)
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Input, VideoComponent::Ptr)
|
||||
|
||||
uint32_t id() const;
|
||||
|
||||
protected:
|
||||
void onConfigChanged(ParameterNames& names) override;
|
||||
VideoMixer::Ptr mixer();
|
||||
|
||||
private:
|
||||
VideoMixer::Ptr _pMixer;
|
||||
uint32_t _id;
|
||||
|
||||
friend VideoMixer;
|
||||
};
|
||||
|
||||
public:
|
||||
VideoMixer(const std::string& name);
|
||||
~VideoMixer();
|
||||
|
||||
Channel::Ptr getChannel(uint32_t id);
|
||||
|
||||
protected:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
virtual Channel::Ptr createChannel(uint32_t id) = 0;
|
||||
|
||||
template <class T, typename F> void foreachChannel(F&&);
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, Channel::Ptr> _channels;
|
||||
};
|
||||
|
||||
inline uint32_t VideoMixer::Channel::id() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
inline VideoMixer::Ptr VideoMixer::Channel::mixer()
|
||||
{
|
||||
return _pMixer;
|
||||
}
|
||||
|
||||
template <class T, typename F> inline void VideoMixer::foreachChannel(F&& exec)
|
||||
{
|
||||
for (auto m : _channels)
|
||||
exec(m.second.cast<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
25
video/core/VideoOutput.cpp
Normal file
25
video/core/VideoOutput.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoOutput.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoOutput::VideoOutput(const std::string& name): VideoComponent(name)
|
||||
{
|
||||
}
|
||||
|
||||
VideoOutput::~VideoOutput()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
62
video/core/VideoOutput.h
Normal file
62
video/core/VideoOutput.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef BB15C48A_9D47_46F6_B428_E9355AD79570
|
||||
#define BB15C48A_9D47_46F6_B428_E9355AD79570
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoComponent.h"
|
||||
#include "util/FactoryManager.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoOutput: public VideoComponent,
|
||||
public ::Util::FactoryManager<VideoOutput, const std::string,
|
||||
::Util::SimpleSelector<const std::string&>, const std::string&>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<VideoOutput>;
|
||||
|
||||
enum class ColorType
|
||||
{
|
||||
RGB,
|
||||
YUV,
|
||||
};
|
||||
|
||||
enum class Mode
|
||||
{
|
||||
HDMI,
|
||||
DVI,
|
||||
};
|
||||
|
||||
VideoOutput(const std::string& name);
|
||||
~VideoOutput();
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Timing, std::string)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Mode, Mode)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Luma, int)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Contrast, int)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Hue, int)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Saturation, int)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Mirror, int)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Rotation, int)
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(ColorType, ColorType)
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
36
video/core/VideoOverlay.cpp
Normal file
36
video/core/VideoOverlay.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoOverlay.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoOverlay::VideoOverlay(VideoOverlayType type, const std::string& name, const Rect& region)
|
||||
: _type(type), _name(name), _region(region)
|
||||
{
|
||||
setZOrder(0);
|
||||
setBlock(4);
|
||||
}
|
||||
|
||||
VideoOverlay::~VideoOverlay()
|
||||
{
|
||||
}
|
||||
|
||||
void VideoOverlay::attach(VideoComponent::Ptr target)
|
||||
{
|
||||
if (target.isNull())
|
||||
throw Poco::BugcheckException(name() + ": Target is NULL");
|
||||
|
||||
_target = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
video/core/VideoOverlay.h
Normal file
64
video/core/VideoOverlay.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef CBC5C20D_24D0_4D1F_B00E_8299B36FD74E
|
||||
#define CBC5C20D_24D0_4D1F_B00E_8299B36FD74E
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoComponent.h"
|
||||
#include "GeometryTypes.h"
|
||||
#include "Image.h"
|
||||
#include "util/FactoryManager.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class VideoOverlayType
|
||||
{
|
||||
CANVAS,
|
||||
MOSAIC,
|
||||
};
|
||||
|
||||
class VideoOverlaySelector
|
||||
{
|
||||
public:
|
||||
static VideoOverlayType select(VideoOverlayType type, const std::string&, const Rect&)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoOverlay: public ::Util::FactoryManager<VideoOverlay, VideoOverlayType, VideoOverlaySelector,
|
||||
VideoOverlayType, const std::string&, const Rect&>,
|
||||
public ::Util::Configurable
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::SharedPtr<VideoOverlay>;
|
||||
|
||||
protected:
|
||||
VideoOverlay(VideoOverlayType type, const std::string& name, const Rect& region);
|
||||
|
||||
public:
|
||||
virtual ~VideoOverlay();
|
||||
|
||||
virtual void render(Image::Ptr pImage) = 0;
|
||||
virtual void scrollRender(Image::Ptr pImage, int hscroll) = 0;
|
||||
virtual void attach(VideoComponent::Ptr target);
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(ZOrder, int)
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Block, int)
|
||||
|
||||
READONLY_PROPERTY_DECLARE(type, VideoOverlayType)
|
||||
READONLY_PROPERTY_DECLARE(name, std::string)
|
||||
READONLY_PROPERTY_DECLARE(target, VideoComponent::Ptr)
|
||||
READONLY_PROPERTY_DECLARE(region, Rect)
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
29
video/core/VideoProcessor.cpp
Normal file
29
video/core/VideoProcessor.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoProcessor.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
VideoProcessor::VideoProcessor(const std::string& name): Core::VideoComponent(name)
|
||||
{
|
||||
setRotation(0);
|
||||
setCrop(Rect{0, 0, 0, 0});
|
||||
setMirror(false);
|
||||
setFlip(false);
|
||||
}
|
||||
|
||||
VideoProcessor::~VideoProcessor()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
52
video/core/VideoProcessor.h
Normal file
52
video/core/VideoProcessor.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef D5B26384_2A3D_4FBE_95FD_606CBAD0DA82
|
||||
#define D5B26384_2A3D_4FBE_95FD_606CBAD0DA82
|
||||
/*
|
||||
*******************************************************************************
|
||||
* INCLUDE
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include "VideoComponent.h"
|
||||
#include "GeometryTypes.h"
|
||||
#include "util/FactoryManager.h"
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DEFINE
|
||||
*******************************************************************************
|
||||
*/
|
||||
class VideoProcessorSelector
|
||||
{
|
||||
public:
|
||||
static int select()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoProcessor: public VideoComponent, public ::Util::FactoryManager<VideoProcessor, int, VideoProcessorSelector>
|
||||
{
|
||||
public:
|
||||
using Ptr = Poco::AutoPtr<VideoProcessor>;
|
||||
|
||||
public:
|
||||
VideoProcessor(const std::string& name);
|
||||
virtual ~VideoProcessor();
|
||||
|
||||
public:
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Rotation, int);
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Crop, Rect);
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Mirror, bool);
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(Flip, bool);
|
||||
|
||||
CONFIGURABLE_DECLARE_PARAMETER(VideoDirection, int);
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
22
video/core/VideoScanMode.h
Normal file
22
video/core/VideoScanMode.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef A4F9D6E2_E09A_42F1_AE90_74D89A97557F
|
||||
#define A4F9D6E2_E09A_42F1_AE90_74D89A97557F
|
||||
|
||||
namespace Video
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
/*
|
||||
*******************************************************************************
|
||||
* CLASS DECLARE
|
||||
*******************************************************************************
|
||||
*/
|
||||
enum class VideoScanMode
|
||||
{
|
||||
PROGRESSIVE,
|
||||
INTERLACED,
|
||||
|
||||
NB,
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue