179 lines
5.7 KiB
C++
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);
|
|
}
|
|
}
|
|
}
|