天天看点

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

一 整体结构

创建一个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需要进行重采样转换格式,需要的结构为

需要的参数

由于pcm是原始数据,不包含采样率等必不可少的数据,所以需要手动传递参数进去。

需要采样率,采样格式(8位还是16位也包含布局格式等),声道数

一些注意点

读取结束时候,需要向编码器发送空,进行刷新,代表结束,读取编码器缓存的数据。

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);