239 lines
6.2 KiB
C++
239 lines
6.2 KiB
C++
/*
|
||
*******************************************************************************
|
||
* 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;
|
||
}
|
||
}
|
||
}
|