[FFMPEG]音频pcm编码为AAC学习及代码
程序员文章站
2022-07-06 10:19:39
...
一 整体结构
创建一个aac的编码器,通过该编码器配置一个编码器上下文。采集足够的pcm数据进行重采样,封装为一帧,发送给aac编码器上下文,由aac上下文获取一个包(aac的data)。
封装一个aac的head,head+data 一起写入aac文件。
需要的ffmpeg结构
Aac转pcm涉及编解码层和原始数据层,所以需要对应层级的数据结构进行处理
AVCodec *codec;
AVCodecContext *c;
AVFrame *frame;
AVPacket *packet;
同时最新的ffmpeg只支持AV_SAMPLE_FMT_FLTP的采样格式,所以对于其他采样格式的pcm需要进行重采样转换格式,需要的结构为
SwrContext *resample_context;
需要的参数
由于pcm是原始数据,不包含采样率等必不可少的数据,所以需要手动传递参数进去。
需要采样率,采样格式(8位还是16位也包含布局格式等),声道数
int Pcm2Acc(int pcm_Sample_rate, AVSampleFormat pcm_Sample_fmt, int pcm_Channels,char * in_file_name,char *out_file_name)
一些注意点
读取结束时候,需要向编码器发送空,进行刷新,代表结束,读取编码器缓存的数据。
avcodec_send_frame(c, NULL);
while (GetData(pOut, iSize))
{
}
编码器上下文的参数为输入的pcm参数,若为pcm格式为AV_SAMPLE_FMT_FLTP则不需要重采样
c->channels = m_PcmChannel;
c->channel_layout = av_get_default_channel_layout(m_PcmChannel);
c->sample_rate = m_PcmSampleRate;
c->sample_fmt = AV_SAMPLE_FMT_FLTP;// 只支持该格式
c->bit_rate = 64000;
下面贴完整代码
main.cpp
int main()
{
Pcm2Acc(8000, AV_SAMPLE_FMT_S16, 1, "../pcm.pcm", "../aac.aac");
system("pause");
return 0;
}
EncoderAac.cpp
#include"EncoderAAC.h"
#include <stdio.h>
AVSampleFormat m_PcmFormat;
int m_PcmChannel;
int m_PcmSampleRate;
AVCodec *codec;
AVCodecContext *c;
AVFrame *frame;
AVPacket *packet;
SwrContext *resample_context;
char m_Pcm[10240];
int m_PcmSize = 0;
char *m_PcmPointer[8];
int64_t pts;
char m_pOutData[1024 * 10];
int test1 = 0, test2 = 0;
void AddADTS(int packetLen)
{
int profile = 1; // AAC LC
int freqIdx = 0xb; // 44.1KHz
int chanCfg = m_PcmChannel; // CPE
if (96000 == m_PcmSampleRate)
{
freqIdx = 0x00;
}
else if (88200 == m_PcmSampleRate)
{
freqIdx = 0x01;
}
else if (64000 == m_PcmSampleRate)
{
freqIdx = 0x02;
}
else if (48000 == m_PcmSampleRate)
{
freqIdx = 0x03;
}
else if (44100 == m_PcmSampleRate)
{
freqIdx = 0x04;
}
else if (32000 == m_PcmSampleRate)
{
freqIdx = 0x05;
}
else if (24000 == m_PcmSampleRate)
{
freqIdx = 0x06;
}
else if (22050 == m_PcmSampleRate)
{
freqIdx = 0x07;
}
else if (16000 == m_PcmSampleRate)
{
freqIdx = 0x08;
}
else if (12000 == m_PcmSampleRate)
{
freqIdx = 0x09;
}
else if (11025 == m_PcmSampleRate)
{
freqIdx = 0x0a;
}
else if (8000 == m_PcmSampleRate)
{
freqIdx = 0x0b;
}
else if (7350 == m_PcmSampleRate)
{
freqIdx = 0xc;
}
// fill in ADTS data
m_pOutData[0] = 0xFF;
m_pOutData[1] = 0xF1;
m_pOutData[2] = ((profile) << 6) + (freqIdx << 2) + (chanCfg >> 2);
m_pOutData[3] = (((chanCfg & 3) << 6) + (packetLen >> 11));
m_pOutData[4] = ((packetLen & 0x7FF) >> 3);
m_pOutData[5] = (((packetLen & 7) << 5) + 0x1F);
m_pOutData[6] = 0xFC;
}
bool Init()
{
for (int i = 0; i < 8; i++)
{
m_PcmPointer[i] = new char[10240];
}
codec = avcodec_find_encoder(AV_CODEC_ID_AAC);//1发现一个编码器
if (!codec) {
fprintf(stderr, "Codec not found\n");
return false;
}
c = avcodec_alloc_context3(codec);//2通过编码器创建一个编码器上下文
if (!c) {
fprintf(stderr, "Could not allocate audio codec context\n");
return false;
}
//3 修改上下文参数
c->channels = m_PcmChannel;
c->channel_layout = av_get_default_channel_layout(m_PcmChannel);
c->sample_rate = m_PcmSampleRate;
c->sample_fmt = AV_SAMPLE_FMT_FLTP;//AV_SAMPLE_FMT_FLTP; 只支持该格式
c->bit_rate = 64000;
/* Allow the use of the experimental AAC encoder. */
c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
/* open it */
//初始化一个视音频编解码器的AVCodecContext
//开辟内存,判断输入参数是否复合要求等
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
return false;
}
packet = av_packet_alloc();
//初始化一个包
if (NULL == packet)
{
return false;
}
frame = av_frame_alloc();
//初始化一个帧
if (NULL == frame)
{
return false;
}
//其他不变,采样格式转变
resample_context = swr_alloc_set_opts(NULL, c->channel_layout, c->sample_fmt,
c->sample_rate, c->channel_layout, m_PcmFormat, c->sample_rate, 0, NULL);
if (NULL == resample_context)
{
fprintf(stderr, "Could not allocate resample context\n");
return false;
}
if (swr_init(resample_context) < 0)//重采样初始化
{
fprintf(stderr, "Could not open resample context\n");
return false;
}
return true;
}
void AddData(char *pData, int size)
{
memcpy(m_Pcm + m_PcmSize, pData, size);
m_PcmSize = m_PcmSize + size;
int data_size = av_get_bytes_per_sample(m_PcmFormat);//返回采样格式的字节数
if (m_PcmSize <= data_size * 1024 * m_PcmChannel)//小于则不处理
{
return;
}
memcpy(m_PcmPointer[0], m_Pcm, data_size * 1024 * m_PcmChannel);
//格式字节数* 单个声道采样率* 声道数
//aac的采样率为1024
m_PcmSize = m_PcmSize - data_size * 1024 * m_PcmChannel;
//重置 大小和内存
memcpy(m_Pcm, m_Pcm + data_size * 1024 * m_PcmChannel, m_PcmSize);
//设置帧
frame->pts = pts;
pts += 1024;
frame->nb_samples = 1024;
frame->format = c->sample_fmt;
frame->channel_layout = c->channel_layout;
frame->sample_rate = c->sample_rate;
if (av_frame_get_buffer(frame, 0)<0)//为帧分配缓冲区
{
fprintf(stderr, "Could not allocate audio data buffers\n");
return;
}
int ret = 0;
//重采样上下文,输出的指针,输出单通道样本的数量,输入的数据,输出的单通道样本数量
if (swr_convert(resample_context, frame->extended_data, frame->nb_samples, (const uint8_t**)m_PcmPointer, 1024)<0)
{
fprintf(stderr, "Could not convert input samples (error )\n");
if (NULL != frame)
{
av_frame_unref(frame);
}
return;
}
//将原始视频或音频帧提供给编码器
ret = avcodec_send_frame(c, frame);
//printf("test1==%d\n",++test1);
if (ret < 0) {
fprintf(stderr, "Error sending the frame to the encoder\n");
if (NULL != frame)
{
av_frame_unref(frame);
}
return;
}
if (NULL != frame)
{
av_frame_unref(frame);
}
}
bool GetData(char *&pOutData, int &iSize)
{
//从编码器读取编码数据
//
int ret = avcodec_receive_packet(c, packet);
if (ret <0)
{
return false;
}
//printf("test2==%d\n", ++test2);
AddADTS(packet->size + 7);
memcpy(m_pOutData + 7, packet->data, packet->size);
iSize = packet->size + 7;
pOutData = m_pOutData;
av_packet_unref(packet);
return true;
}
void FreeData()
{
avcodec_send_frame(c, NULL);
char *pOut = NULL;
int iSize = 0;
while (GetData(pOut, iSize))
{
}
for (int i = 0; i < 8; i++)
{
delete[] m_PcmPointer[i];
}
if (NULL != packet)
{
av_packet_free(&packet);
}
if (NULL != frame)
{
av_frame_free(&frame);
}
if (NULL != c)
{
avcodec_free_context(&c);
}
if (NULL != resample_context)
{
swr_free(&resample_context);
}
}
int Pcm2Acc(int pcm_Sample_rate, AVSampleFormat pcm_Sample_fmt, int pcm_Channels,char * in_file_name,char *out_file_name)
{
m_PcmFormat = pcm_Sample_fmt;
m_PcmChannel = pcm_Channels;
m_PcmSampleRate = pcm_Sample_rate;
if (!Init())//采样率,采样格式,声道数量
{
printf("pcm2AAC Init error\r\n");
getchar();
}
//printf("pasing start\r\n");
char frame_buf[1024] = { 0 };
//读取文件
FILE * InFile = fopen(in_file_name, "rb");
//输出文件
FILE * OutFile = fopen(out_file_name, "wb");
char * pOutData = NULL;
int OutSize = 0;
while (true)
{
int iReadSize = fread(frame_buf, 1, 512, InFile);
if (iReadSize <= 0)
{
break;
}
AddData(frame_buf, iReadSize);//每2048字节给编码器增加一帧
while (true)
{
//帧转换为包,增加adfs帧头
//每两个帧才能转换一个包
if (!GetData(pOutData, OutSize))
{
break;
}
fwrite(pOutData, 1, OutSize, OutFile);
}
}
fclose(OutFile);
fclose(InFile);
FreeData();
return 0;
}
EncoderAac.h
extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/frame.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
};
int Pcm2Acc(int pcm_Sample_rate, AVSampleFormat pcm_Sample_fmt, int pcm_Channels, char * in_file_name, char *out_file_name);
上一篇: Java笔记-PageHelper
下一篇: 一个提升PostgreSQL性能的小技巧