/* ******************************************************************************* * 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); } } }