欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[FFMPEG]音频pcm编码为AAC学习及代码

程序员文章站 2022-07-06 10:19:39
...

一 整体结构

创建一个aac的编码器,通过该编码器配置一个编码器上下文。采集足够的pcm数据进行重采样,封装为一帧,发送给aac编码器上下文,由aac上下文获取一个包(aac的data)。
封装一个aac的head,head+data 一起写入aac文件。

[FFMPEG]音频pcm编码为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);