181 lines
6.7 KiB
C++
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(); };
|
|
}
|
|
}
|
|
}
|
|
}
|