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

179 lines
5.7 KiB
C++

/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "AudioEncoder.h"
#include "Util.h"
#include "Logger.h"
#include "../core/System.h"
namespace Video
{
namespace FFmpeg
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
AudioEncoder::AudioEncoder(Core::CodecID codecID)
: Core::AudioEncoder(Poco::format("FFmpeg::AudioEncoder(%s)", Core::codecIDToString(codecID)), codecID),
_pCodecCtx(nullptr), _pFrame(nullptr), _pPacket(nullptr)
{
}
AudioEncoder::~AudioEncoder()
{
}
void AudioEncoder::initialize()
{
auto codecID = getCodecID();
const AVCodec* pCodec = Util::getEncoder(codecID);
if (!pCodec)
throw Poco::NotImplementedException(
Poco::format("%s: Unsupported codec: %s", name(), Core::codecIDToString(codecID)), -EINVAL);
_pCodecCtx = avcodec_alloc_context3(pCodec);
if (!_pCodecCtx)
throw Poco::InvalidAccessException(Poco::format("%s: Could not alloc an encoding context", name()), -EACCES);
setChannelLayout(Core::ChannelLayout::STEREO);
switch (pCodec->id)
{
case AV_CODEC_ID_PCM_ALAW:
case AV_CODEC_ID_PCM_MULAW:
setSampleFormat(Core::SampleFormat::S16P);
setSampleRate(8000);
if (Core::channelLayoutChannels(getChannelLayout()) != 1)
setChannelLayout(Core::ChannelLayout::MONO);
setFrameSize(320);
break;
case AV_CODEC_ID_AAC:
setSampleFormat(Core::SampleFormat::FLTP);
setFrameSize(1024);
break;
case AV_CODEC_ID_MP3:
setSampleFormat(Core::SampleFormat::FLTP);
setFrameSize(1152);
break;
case AV_CODEC_ID_OPUS:
_pCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
setSampleFormat(Core::SampleFormat::S16);
setFrameSize(960);
break;
default:
throw Poco::BugcheckException(
Poco::format("%s: Unspecified encoder(%s) parameters", name(), Core::codecIDToString(codecID)), -EINVAL);
}
_pCodecCtx->ch_layout = Util::getChannelLayout(getChannelLayout());
_pCodecCtx->sample_rate = getSampleRate();
_pCodecCtx->bit_rate = getBitrate();
_pCodecCtx->sample_fmt = Util::getSampleFormat(getSampleFormat());
_pCodecCtx->time_base = (AVRational){1, getSampleRate()};
int rc = avcodec_open2(_pCodecCtx, pCodec, nullptr);
if (rc < 0)
throw Poco::InvalidAccessException(
Poco::format("%s: Open %s FAIL: %d(%s)", name(), std::string(pCodec->name), rc, Util::getErrorString(rc)),
rc);
if (_pCodecCtx->frame_size == 0)
_pCodecCtx->frame_size = getFrameSize();
_pFrame = av_frame_alloc();
_pFrame->format = _pCodecCtx->sample_fmt;
_pFrame->sample_rate = _pCodecCtx->sample_rate;
_pFrame->nb_samples = _pCodecCtx->frame_size;
av_channel_layout_copy(&_pFrame->ch_layout, &_pCodecCtx->ch_layout);
av_frame_get_buffer(_pFrame, 0);
if (Core::sampleFormatIsPlanar(getSampleFormat()))
{
const int c = Core::channelLayoutChannels(getChannelLayout());
switch (c)
{
case 1:
_frameConvertor = [](Core::Frame::Ptr from, AVFrame* to) -> void
{ memcpy(to->data[0], from->data[0], from->length); };
break;
case 2:
_frameConvertor = [](Core::Frame::Ptr from, AVFrame* to) -> void
{
memcpy(to->data[0], from->data[0], from->length);
memcpy(to->data[1], from->data[1], from->length);
};
break;
default:
throw Poco::BugcheckException(Poco::format("%s: Unsupported channle-count: %d", name(), c), -EINVAL);
}
}
else
{
_frameConvertor = [](Core::Frame::Ptr from, AVFrame* to) -> void
{ memcpy(to->data[0], from->data[0], from->length); };
}
}
void AudioEncoder::deinitialize()
{
av_frame_free(&_pFrame);
avcodec_free_context(&_pCodecCtx);
}
Core::Packet::Ptr AudioEncoder::encode()
{
if (_pPacket == nullptr)
_pPacket = av_packet_alloc();
int rc = avcodec_receive_packet(_pCodecCtx, _pPacket);
if (rc > 0)
{
return new Core::Packet(serialCodeManager().code(), false, &_pPacket);
}
while (true)
{
auto pIn = readFrame();
ts = pIn->timestamp;
rc = av_frame_make_writable(_pFrame);
if (rc < 0)
throw Poco::InvalidAccessException(
Poco::format("%s: Make frame writable failed: %d(%s)", name(), rc, Util::getErrorString(rc)), rc);
_frameConvertor(pIn, _pFrame);
_pFrame->pts = pIn->timestamp;
int rc = avcodec_send_frame(_pCodecCtx, _pFrame);
if (rc < 0)
throw Poco::InvalidAccessException(Poco::format("%s: Sending the frame to the encoder failed: %d(%s)",
name(), rc, Util::getErrorString(rc)),
rc);
rc = avcodec_receive_packet(_pCodecCtx, _pPacket);
if (rc == AVERROR(EAGAIN) || rc == AVERROR_EOF)
continue;
if (rc < 0)
throw Poco::InvalidAccessException(
Poco::format("%s: Error encoding audio frame: %d(%s)", name(), rc, Util::getErrorString(rc)), rc);
return new Core::Packet(serialCodeManager().code(), false, &_pPacket);
}
}
void AudioEncoder::onConfigChanged(ParameterNames& names)
{
Core::AudioEncoder::onConfigChanged(names);
}
}
}