AAC是什么?
在视频编码时 可以使用H264对视频进行编码
AAC就是在音频编码使用的压缩技术
如果直接把音频裸流进行传输,也会占用很大的资源,因此我们需要使用压缩技术对音频数据进行压缩后在进行压缩
这很类似于我们在电脑中习惯性的把文件夹压缩为压缩包在进行传输数据
AAC又分为了ADIF与ADTS,其中ADIF包含一个头与很多数据,这样的类型很适合与存储,而ADTS对每一帧进行编码,因此ADTS适合用于网络传输。
ADTS(Audio Data Transport Stream)
1.ADTS结构
ADTS对于每一帧音频都进行编码,因此可以想到其每一帧都是由包头+数据组成
其结构如下:ADTS header + AAC ES
而ADTS header = 固定头部(fixed header)+可变头部(variable header)
下面分别介绍这两个头部是什么
2.固定头部
补充:
1.其中profile表示该帧的重要性or等级,其中0为main profile表示高等级 ;1为low profile表示低等级;
2.sampling_frequency_index使用四bit来表示采样率,对应表如下:(下表为十进制)
3.private bit编码的时候设置为0,解码的时候忽略这个
4.channel configuration配置方法如下:
5.copy: 编码时设置为0,解码的时候忽略 home:编码时设置为0,解码的时候忽略
3.可变头部
可变头部有以下五部分组成
copyright_identificaion_bit:编码时设置为0
copyright_identificaion_start:编码时设置为0
aac_frame_length:ADTS帧的长度。所以aac_frame_length=ADTS_HEADER_LENGTH+AAC_DATA_LENGTH(ADTS头部的长度取决于protection_absent
字段的值。无CRC时为7字节,有CRC时为9字节。需要在计算时考虑这个差异。)
adts_buffer_fullness:固定为0x7FF。这个标识符是指可变的码流
number_of_raw_data_blocks_in_frame :
4.固定头部与可变头部的区别
即固定头部是我这次压缩的一个整体的指标,对于整个压缩内容几乎是不变的
而可变头部更多是针对于当前音频帧的一些信息
5.使用rv1126进行AAC操作
类似于之前使用rkmedia的库操作摄像头进行视频采集
aac操作也有对应的库
AAC压缩操作思路:
1.配置AI采集模块
2.配置AVENC音频压缩模块
3.建立AI->AVENC连接管道
4.创建压缩线程
5.1AI采集模块
u32SampleRate:采样率,即每秒采样的次数,单位是HZ,即采样频率,根据奈奎斯特采样定理采样频率大于等于原始频率即可无失真的采样出音频数据
u32BitRate:比特率表示音频数据的编码速率,即每秒钟传输的比特数,单位是比特每秒 (bps)
u32ChnCnt:声道数量,两个相当于双声道
u32FrameCnt:每帧采样数(Frame Count),表示每个编码帧中的采样数。对于AAC和MP3等编码格式,这个值通常固定为1024,这意味着每个帧包含1024个采样点。
SAMPLE_FORMAT_E:是采样的方法,其规定了采样位数与采样格式,采样位数越多音频还原度肯定是越好,类似于一个东西你用八位与用十六位来表达的效果肯定是不一样的
后面两个分别表示设备节点以及压缩文件存储路径,通常情况下我们设备节点选用default
下面就是类似于视频采集的配置分别第设备节点、采样方式等参数进行配置,如何设置并使能管道,默认情况下我们采用0管道
int ret;
MPP_CHN_S mpp_chn_ai, mpp_chn_aenc;
RK_U32 u32SampleRate = 16000;
RK_U32 u32BitRate = 64000; // 64kbps
RK_U32 u32ChnCnt = 2;
RK_U32 u32FrameCnt = 1024; // always 1024 for mp3 aac
SAMPLE_FORMAT_E enSampleFmt = RK_SAMPLE_FMT_FLTP;
// default:CARD=rockchiprk809co
RK_CHAR *pDeviceName = "default";
RK_CHAR *pOutPath = "/tmp/aenc.mp3";
AI_CHN_ATTR_S ai_attr;
ai_attr.pcAudioNode = pDeviceName;
ai_attr.enSampleFormat = enSampleFmt;
ai_attr.u32NbSamples = u32FrameCnt;
ai_attr.u32SampleRate = u32SampleRate;
ai_attr.u32Channels = u32ChnCnt;
ai_attr.enAiLayout = AI_LAYOUT_NORMAL;
ret = RK_MPI_AI_SetChnAttr(0, &ai_attr);
ret |= RK_MPI_AI_EnableChn(0);
if (ret)
{
printf("Create AI[0] failed! ret=%d\n", ret);
return -1;
}
5.2AENC
这里的编码模式选择AAC,编码质量通常设置为1即可,其余参数保持与AI模块一样
不同的是AENC模块只需要创建管道即可不需要使能
AENC_CHN_ATTR_S aenc_attr;
aenc_attr.enCodecType = RK_CODEC_TYPE_AAC;
aenc_attr.u32Bitrate = u32BitRate;
aenc_attr.u32Quality = 1;
aenc_attr.stAencAAC.u32Channels = u32ChnCnt;
aenc_attr.stAencAAC.u32SampleRate = u32SampleRate;
ret = RK_MPI_AENC_CreateChn(0, &aenc_attr);
if (ret)
{
printf("Create AENC[0] failed! ret=%d\n", ret);
return -1;
}
5.3通道绑定
MPP_CHN_S mpp_chn_ai, mpp_chn_aenc;
是最初定义的两个管道接口,分别配置器对应的模式以及管道好,然后进行连接,使得输入流与压缩流管道进行连接
mpp_chn_ai.enModId = RK_ID_AI;
mpp_chn_ai.s32ChnId = 0;
mpp_chn_aenc.enModId = RK_ID_AENC;
mpp_chn_aenc.s32ChnId = 0;
ret = RK_MPI_SYS_Bind(&mpp_chn_ai, &mpp_chn_aenc);
if (ret)
{
printf("Bind AI[0] to AENC[0] failed! ret=%d\n", ret);
return -1;
}
5.4创建压缩线程
压缩线程首先打开了一个aac文件
(w+
模式的含义:
w
:以写入模式打开文件。如果文件不存在,会创建一个新文件。如果文件已存在,则清空文件内容。+
:在写入的同时允许读取。这意味着可以对文件进行读写操作。
),然后在循环中不断对AENC的数据进行读取,读取成功后写入指定文件
pthread_t aenc_thread;
ret = pthread_create(&aenc_thread, NULL, get_audio_aenc_thread, NULL);
if(ret != 0)
{
printf("create aenc thread failed....\n");
}
void *get_audio_aenc_thread(void *args)
{
MEDIA_BUFFER mb = NULL;
FILE * audio_file = fopen("test_out.aac", "w+");
while (1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_AENC, 0, -1);
if (!mb)
{
printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n");
break;
}
printf("#Get Frame:ptr:%p, size:%zu, mode:%d, channel:%d, "
"timestamp:%lld\n",
RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb),
RK_MPI_MB_GetModeID(mb), RK_MPI_MB_GetChannelID(mb),
RK_MPI_MB_GetTimestamp(mb));
fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), audio_file);
}
return NULL;
}