概述
本文介绍音频编码,使用了ffmpeg在音频编码器实现aac的编码。
技术简介
使用ffmpeg的编码器
使用模块(库)
使用ffmpeg的avcodec-58.dll
主要流程和代码
1、初始化音频编码器
int AacEncoder::init(AVSampleFormat format, int samplerate, int bitrate, int channels)
{
int err = ERROR_CODE_OK;
if (m_inited) {
return err;
}
do {
m_ringBuffer = new HELPER::RingBuffer<AVFrame>();
if (m_ringBuffer == nullptr) {
err = ERROR_CODE_ALLOC_FAILED;
break;
}
if (!m_ringBuffer->init(AAC_ENCODER_RINGBUFFER_SIZE)) {
err = ERROR_CODE_ALLOC_FAILED;
break;
}
AVCodecID codecId = AV_CODEC_ID_AAC;
m_codec = avcodec_find_encoder(codecId);
if (m_codec == nullptr) {
err = ERROR_CODE_FFMPEG_FIND_ENCODER_FAILED;
break;
}
m_encodeContext = avcodec_alloc_context3(m_codec);
if (m_encodeContext == nullptr) {
err = ERROR_CODE_FFMPEG_ALLOC_CONTEXT_FAILED;
break;
}
m_encodeContext->codec_type = AVMEDIA_TYPE_AUDIO;
m_encodeContext->codec_id = codecId;
m_encodeContext->sample_fmt = format;
m_encodeContext->bit_rate = bitrate;
m_encodeContext->sample_rate = samplerate;
m_encodeContext->channels = channels;
m_encodeContext->channel_layout = av_get_default_channel_layout(channels);
m_encodeContext->time_base = { 1, samplerate };
m_encodeContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
m_encodeContext->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
int ret = avcodec_open2(m_encodeContext, m_codec, nullptr);
if (ret < 0) {
err = ERROR_CODE_FFMPEG_OPEN_CODEC_FAILED;
break;
}
m_frame = av_frame_alloc();
if (m_frame == nullptr) {
err = ERROR_CODE_FFMPEG_ALLOC_FRAME_FAILED;
break;
}
m_bufferSize = av_samples_get_buffer_size(nullptr, channels, m_encodeContext->frame_size, format, 1);
m_buffer = (uint8_t*)av_malloc(m_bufferSize);
if (m_buffer == nullptr) {
err = ERROR_CODE_ALLOC_FAILED;
break;
}
m_frame->format = format;
m_frame->nb_samples = m_encodeContext->frame_size;
m_frame->sample_rate = samplerate;
m_frame->channels = channels;
m_frame->channel_layout = av_get_default_channel_layout(channels);
ret = avcodec_fill_audio_frame(m_frame, channels, format, m_buffer, m_bufferSize, 0);
if (ret < 0) {
err = ERROR_CODE_FFMPEG_FILL_AUDIO_FRAME_FAILED;
break;
}
#ifdef AAC_ENCODER_SAVE_AAC_FILE
m_formatCtx = avformat_alloc_context();
if (m_formatCtx == nullptr) {
err = ERROR_CODE_FFMPEG_ALLOC_CONTEXT_FAILED;
break;
}
const char* aacFilename = "AacEncoder.aac";
ret = avio_open(&m_ioCtx, aacFilename, AVIO_FLAG_READ_WRITE);
if (ret < 0) {
err = ERROR_CODE_FFMPEG_OPEN_IO_FAILED;
break;
}
m_formatCtx->pb = m_ioCtx;
m_formatCtx->oformat = av_guess_format(nullptr, aacFilename, nullptr);
m_formatCtx->url = av_strdup(aacFilename);
m_stream = avformat_new_stream(m_formatCtx, nullptr);
if (m_stream == nullptr) {
err = ERROR_CODE_FFMPEG_NEW_STREAM_FAILED;
break;
}
ret = avcodec_parameters_from_context(m_stream->codecpar, m_encodeContext);
if (ret < 0) {
err = ERROR_CODE_FFMPEG_COPY_PARAMS_FAILED;
break;
}
if (m_formatCtx->oformat->flags | AV_CODEC_FLAG_GLOBAL_HEADER) {
m_stream->codecpar->extradata_size = m_encodeContext->extradata_size;
m_stream->codecpar->extradata = (uint8_t*)av_memdup(m_encodeContext->extradata, m_encodeContext->extradata_size);
}
#endif
m_inited = true;
} while (0);
if (err != ERROR_CODE_OK) {
LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] init aac encoder error: %s", __FUNCTION__,
HCMDR_GET_ERROR_DESC(err));
cleanup();
}
return err;
}
2、开启编码线程
int AacEncoder::start()
{
int err = ERROR_CODE_OK;
if (m_running) {
return err;
}
if (!m_inited) {
err = ERROR_CODE_UNINITIALIZED;
return err;
}
m_running = true;
m_thread = std::thread(std::bind(&AacEncoder::encodeProcess, this));
return err;
}
编码线程函数,ringBuffer是循环内存
void AacEncoder::encodeProcess()
{
int err = ERROR_CODE_OK;
AVPacket* packet = av_packet_alloc();
if (packet == nullptr) {
err = ERROR_CODE_FFMPEG_ALLOC_PACKET_FAILED;
if (m_onAudioEncodeError != nullptr) {
m_onAudioEncodeError(err);
}
LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] alloc packet error: %s", __FUNCTION__,
HCMDR_GET_ERROR_DESC(err));
return;
}
#ifdef AAC_ENCODER_SAVE_AAC_FILE
avformat_write_header(m_formatCtx, nullptr);
#endif
while (m_running) {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_running && !m_conditionFlag) {
m_condition.wait_for(lock, std::chrono::microseconds(AAC_ENCODER_WAIT_FOR_FRAME_TIMEOUT));
}
AVFrame frame;
while (m_ringBuffer->pop(m_buffer, m_bufferSize, frame) > 0) {
m_frame->pkt_dts = frame.pkt_dts;
m_frame->pts = frame.pts;
err = encode(m_frame, packet);
if (err != ERROR_CODE_OK) {
if (m_onAudioEncodeError != nullptr) {
m_onAudioEncodeError(err);
}
LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] encode frame error: %s", __FUNCTION__,
HCMDR_GET_ERROR_DESC(err));
break;
}
}
m_conditionFlag = false;
}
// Flush the last frame in encoder
encode(nullptr, packet);
av_packet_free(&packet);
#ifdef AAC_ENCODER_SAVE_AAC_FILE
av_write_trailer(m_formatCtx);
#endif
}
编码能力函数
int AacEncoder::encode(AVFrame* frame, AVPacket* packet)
{
int err = ERROR_CODE_OK;
int ret = avcodec_send_frame(m_encodeContext, frame);
if (ret < 0) {
err = ERROR_CODE_FFMPEG_ENCODE_FRAME_FAILED;
return err;
}
while (ret >= 0) {
av_init_packet(packet);
ret = avcodec_receive_packet(m_encodeContext, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
if (ret < 0) {
err = ERROR_CODE_FFMPEG_READ_PACKET_FAILED;
break;
}
if (ret == 0) {
#ifdef AAC_ENCODER_SAVE_AAC_FILE
av_packet_rescale_ts(packet, m_encodeContext->time_base, m_stream->time_base);
packet->stream_index = m_stream->index;
av_write_frame(m_formatCtx, packet);
#endif
if (m_onAudioEncodeData != nullptr) {
m_onAudioEncodeData(packet);
}
}
av_packet_unref(packet);
}
return err;
}
3、数据源输入(输入线程调用)
int AacEncoder::addFrame(const uint8_t* data, int len, AVFrame* frame)
{
int err = ERROR_CODE_OK;
if (data == nullptr || len <= 0 || frame == nullptr) {
err = ERROR_CODE_PARAMS_ERROR;
return err;
}
std::unique_lock<std::mutex> lock(m_mutex);
if (m_ringBuffer == nullptr) {
err = ERROR_CODE_EXCEED_MEMORY_LIMIT;
return err;
}
AVFrame inputFrame;
memcpy_s(&inputFrame, sizeof(AVFrame), frame, sizeof(AVFrame));
m_ringBuffer->push(data, len, inputFrame);
m_conditionFlag = true;
m_condition.notify_all();
return err;
}
4、结束编码线程
int AacEncoder::stop()
{
int err = ERROR_CODE_OK;
if (!m_running) {
return err;
}
m_running = false;
m_conditionFlag = true;
m_condition.notify_all();
if (m_thread.joinable()) {
m_thread.join();
}
return err;
}
至此,完成aac的编码,亦可将保存的aac文件使用Windows Media Player工具播放。
最后
以上就是无心芹菜为你收集整理的【音视频】音频编码-aac(七)的全部内容,希望文章能够帮你解决【音视频】音频编码-aac(七)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复