/* ******************************************************************************* * INCLUDE ******************************************************************************* */ #include "AudioResample.h" #include "Util.h" #include #include 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 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(); }; } } } }