p26001-store-acs-soc-fw-app/video/ffmpeg/AudioResample.cpp
2026-09-28 21:33:04 +08:00

181 lines
6.7 KiB
C++

/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "AudioResample.h"
#include "Util.h"
#include <Poco/Format.h>
#include <vector>
namespace Video
{
namespace FFmpeg
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
AudioResample::AudioResample(Core::ChannelLayout ilayout, Core::SampleFormat iformat, int irate,
Core::ChannelLayout olayout, Core::SampleFormat oformat, int orate)
: Core::AudioResample(Poco::format("FFmpeg::AudioResample(%s:%s:%d->%s:%s:%d)",
Core::channelLayoutToString(ilayout), Core::sampleFormatName(iformat), irate,
Core::channelLayoutToString(olayout), Core::sampleFormatName(oformat), orate),
ilayout, iformat, irate, olayout, oformat, orate),
_pSwrCtx(nullptr)
{
}
AudioResample::~AudioResample()
{
if (_pSwrCtx)
{
swr_close(_pSwrCtx);
swr_free(&_pSwrCtx);
}
}
Core::Frame::Ptr AudioResample::process(Core::Frame::Ptr in)
{
if (!_pSwrCtx)
{
auto iAVLayout = Util::getChannelLayout(inLayout());
auto oAVLayout = Util::getChannelLayout(outLayout());
auto iAVFormat = Util::getSampleFormat(inFormat());
auto oAVFormat = Util::getSampleFormat(outFormat());
int rc = swr_alloc_set_opts2(&_pSwrCtx, &oAVLayout, oAVFormat, outRate(), &iAVLayout, iAVFormat, inRate(), 0,
nullptr);
if (rc < 0)
throw Poco::InvalidArgumentException(
Poco::format("%s: Could not allocate SwrContext: %d(%s)", name(), rc, Util::getErrorString(rc)), rc);
if (outLayout() == Core::ChannelLayout::L || outLayout() == Core::ChannelLayout::R)
{
const int inCh = iAVLayout.nb_channels;
const int outCh = (int)oAVLayout.nb_channels;
const int srcCh = (outLayout() == Core::ChannelLayout::L) ? 0 : inCh - 1;
std::vector<double> matrix(inCh * outCh, 0.0);
matrix[srcCh] = 1.0;
rc = swr_set_matrix(_pSwrCtx, matrix.data(), inCh);
if (rc < 0)
throw Poco::InvalidAccessException(
Poco::format("%s: Could not set SwrContext matrix: %d(%s)", name(), rc, Util::getErrorString(rc)),
rc);
}
rc = swr_init(_pSwrCtx);
if (rc < 0)
throw Poco::InvalidAccessException(
Poco::format("%s: Could not init SwrContext: %d(%s)", name(), rc, Util::getErrorString(rc)), rc);
setAssignPointersMethod(_assignInPointers, inFormat(), inLayout());
setAssignPointersMethod(_assignOutPointers, outFormat(), outLayout());
if (av_sample_fmt_is_planar(iAVFormat))
_iFrameSize = in->length / av_get_bytes_per_sample(iAVFormat);
else
_iFrameSize = in->length / (av_get_bytes_per_sample(iAVFormat) * iAVLayout.nb_channels);
_oSampleSize = av_get_bytes_per_sample(oAVFormat);
if (!av_sample_fmt_is_planar(oAVFormat))
_oSampleSize *= oAVLayout.nb_channels;
_oFrameSize = av_rescale_rnd(swr_get_delay(_pSwrCtx, inRate()) + _iFrameSize, outRate(), inRate(), AV_ROUND_UP);
const int c = Core::channelLayoutChannels(outLayout());
if (Core::sampleFormatIsPlanar(outFormat()))
{
_oBytesPerChannel = av_samples_get_buffer_size(NULL, 1, _oFrameSize, oAVFormat, 1);
switch (c)
{
case 1:
_frameCreator = [this]() -> Core::Frame::Ptr
{
Core::Frame::Ptr pFrame = new Core::Frame;
pFrame->length = _oBytesPerChannel;
pFrame->data[0] = new uint8_t[_oBytesPerChannel << 1];
return pFrame;
};
break;
case 2:
_frameCreator = [this]() -> Core::Frame::Ptr
{
Core::Frame::Ptr pFrame = new Core::Frame;
pFrame->length = _oBytesPerChannel;
pFrame->data[0] = new uint8_t[_oBytesPerChannel << 1];
pFrame->data[1] = new uint8_t[_oBytesPerChannel << 1];
return pFrame;
};
break;
default:
throw Poco::BugcheckException(Poco::format("%s: Unsupported channle-count: %d", name(), c), -EINVAL);
}
}
else
{
_oBytesPerChannel = av_samples_get_buffer_size(NULL, c, _oFrameSize, oAVFormat, 1);
_frameCreator = [this]() -> Core::Frame::Ptr
{
Core::Frame::Ptr pFrame = new Core::Frame;
pFrame->length = _oBytesPerChannel;
pFrame->data[0] = new uint8_t[_oBytesPerChannel << 1];
return pFrame;
};
}
}
Core::Frame::Ptr out = _frameCreator();
out->timestamp = in->timestamp;
uint8_t* iptrs[Core::Frame::DATA_LINES] = {nullptr};
uint8_t* optrs[Core::Frame::DATA_LINES] = {nullptr};
_assignInPointers(iptrs, in);
_assignOutPointers(optrs, out);
int rc = swr_convert(_pSwrCtx, optrs, _oFrameSize, (const uint8_t**)iptrs, _iFrameSize);
if (rc < 0)
throw Poco::InvalidAccessException(
Poco::format("%s: Convert FAIL: %d(%s)", name(), rc, Util::getErrorString(rc)), rc);
out->length = rc * _oSampleSize;
return out;
}
void AudioResample::setAssignPointersMethod(AssignPointersMethod& method, Video::Core::SampleFormat iformat,
Video::Core::ChannelLayout ilayout)
{
if (Core::sampleFormatIsPlanar(iformat))
{
const int c = Core::channelLayoutChannels(ilayout);
switch (c)
{
case 1:
method = [](uint8_t* ptrs[], Core::Frame::Ptr pFrame) -> void { ptrs[0] = pFrame->data[0].get(); };
break;
case 2:
method = [](uint8_t* ptrs[], Core::Frame::Ptr pFrame) -> void
{
ptrs[0] = pFrame->data[0].get();
ptrs[1] = pFrame->data[1].get();
};
break;
default:
throw Poco::BugcheckException(Poco::format("%s: Unsupported channle-count: %d", name(), c), -EINVAL);
}
}
else
{
method = [](uint8_t* ptrs[], Core::Frame::Ptr pFrame) -> void { ptrs[0] = pFrame->data[0].get(); };
}
}
}
}