目录
1.5每个AAC码流添加ADTSHeader头部
一RV1126多线程获取音频编码AAC码流的流程

上面是RV1126多线程获取AAC码流的流程,分为六步:AI模块的初始化并使能、AENC模块的初始化、绑定AI模块和AENC模块、创建多线程获取AAC码流、向每个AAC码流添加ADTSHeader头部(这个是重点)、写入具体每一帧AAC的ES码流。
1.1AI模块的初始化并使能
AI模块的初始化实际上就是对AI_CHN_ATTR_S 的参数进行设置、然后调用RK_MPI_AI_SetChnAttr 设置AI模块并使能****RK_MPI_AI_EnableChn,****伪代码如下:
AI_CHN_ATTR_S ai_chn_s;
ai_chn_s.pcAudioNode = AUDIO_PATH;
ai_chn_s.u32Channels = 2;
ai_chn_s.u32NbSamples = 1024;
ai_chn_s.u32SampleRate = 48000;
ai_chn_s.enAiLayout = AI_LAYOUT_NORMAL;
ai_chn_s.enSampleFormat = RK_SAMPLE_FMT_S16;
ret = RK_MPI_AI_SetChnAttr(AI_CHN, &ai_chn_s);
if(ret)
{
printf("RK_MPI_AI_SetChnAttr Failed...\n");
}
ret = RK_MPI_AI_EnableChn( AI_CHN );
if(ret)
{
printf("RK_MPI_AI_EnableChn Failed...\n");
}
1.2AENC模块的初始化
AENC模块的初始化实际上就是对AI_CHN_ATTR_S 的参数进行设置、然后调用RK_MPI_AENC_CreateChn设置AENC模块伪代码如下:
AENC_CHN_ATTR_S aenc_attr;
aenc_attr.enCodecType = RK_CODEC_TYPE_AAC;
aenc_attr.u32Bitrate = 64000;
aenc_attr.u32Quality = 1;
aenc_attr.stAencAAC.u32Channels = 2;
aenc_attr.stAencAAC.u32SampleRate = 48000;
ret = RK_MPI_AENC_CreateChn(AENC_CHN, &aenc_attr);
if (ret)
{
printf("Create AENC[0] failed! ret=%d\n", ret);
return -1;
}else{
printf("Create AENC[0] success!\n");
}
1.3绑定AI模块和AENC模块
分别创建两个MPP_CHN_S结构体,分别是AI模块的MPP_CHN_S和AENC模块的MPP_CHN_S,创建完成之后则用RK_MPI_SYS_Bind对两个模块进行绑定
MPP_CHN_S ai_mpp_chn_s;
ai_mpp_chn_s.enModId = RK_ID_AI;
ai_mpp_chn_s.s32ChnId = 0;
MPP_CHN_S aenc_mpp_chn_s;
aenc_mpp_chn_s.enModId = RK_ID_AENC;
aenc_mpp_chn_s.s32ChnId = 0;
ret = RK_MPI_SYS_Bind(&ai_mpp_chn_s, &aenc_mpp_chn_s);
if (ret)
{
printf("RK_MPI_SYS_Bind Failed....\n");
} else{
printf("RK_MPI_SYS_Bind Success....\n");
}
1.4多线程获取每一帧AAC码流
开启一个线程去采集每一帧AENC模块的数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_AENC,通道号ID是AENC创建的通道ID号****。这里需要注意的是,要进行两层写入。第一层要进行adts header头部的写入,第二层则需要进行adts es流的写入****
....................................................
While(1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_AENC, AENC_CHN , -1);
if (!mb)
{
printf("Get Aenc_Buffer break...\n");
break;
}
...................................
fwrite(aac_header, 1, 7, aac_file); //对每一帧AAC码流写入adts_header头部
fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), aac_file); //写入每一帧AAC的ES码流
}
.......................................................
1.5每个AAC码流添加ADTSHeader头部
在写入AACES码流前需要对其进行ADTS Header的封装,adts header头部分为:adts_fixed_header和adts_variable_header。在对其写入的时候需要把这两个头部都一并写到码流上面。下面就是封装的具体方法。
typedef struct AacFreqIdx_
{
RK_S32 u32SmpRate;
RK_U8 u8FreqIdx;
} AacFreqIdx;
AacFreqIdx AacFreqIdxTbl[13] = {{96000, 0}, {88200, 1}, {64000, 2}, {48000, 3}, {44100, 4}, {32000, 5}, {24000, 6}, {22050, 7}, {16000, 8}, {12000, 9}, {11025, 10}, {8000, 11}, {7350, 12}};
static void GetAdtsHeader(RK_U8 *pu8AdtsHdr, RK_S32 u32Smp le Rate, RK_U8 u8Channel, RK_U32 u32DataLen)
{
RK_U8 u8FreqIdx = 0;
for (int i = 0; i < 13; i++)
{
if (u32Sm l pRate == AacFreqIdxTbl[i].u32SmpRate)
{
u8FreqIdx = AacFreqIdxTbl[i].u8FreqIdx;
break;
}
}
RK_U32 u32PacketLen = u32DataLen + 7;
pu8AdtsHdr[0] = 0xFF; //主要是写入syncword同步字节的前8位
pu8AdtsHdr[1] = 0xF1; //主要是写入syncword同步字节的后4位,并且设置ID号、layer、protection_absent
pu8AdtsHdr[2] = ((AAC_PROFILE_LOW) << 6) + (u8FreqIdx << 2) + (u8Channel >> 2); //设置音频profile、sample_rate_index、声道数
pu8AdtsHdr[3] = (((u8Channel & 3) << 6) + (u32PacketLen >> 11)); //设置声道数,original_copy,home,copyright_identification_bit、copyright_identification_start、aac_frame_length
pu8AdtsHdr[4] = ((u32PacketLen & 0x7FF) >> 3); //设置aac_frame_length+adts_buffer_fullness
pu8AdtsHdr[5] = (((u32PacketLen & 7) << 5) + 0x1F); //设置adts_buffer_fullness + number_of_raw_data_blocks_in_frame
pu8AdtsHdr[6] = 0xFC; //设置 number_of_raw_data_blocks_in_frame
}
上面这个方法需要传入四个参数,分别是:
第一个参数pu8AdtsHd ****r:****需要处理输出的字符串
第二个参数u32S a mp le Rate : 音频的采样率(根据音频采样率去查找对应的采样率索引,这里的索引是AacFreqIdx)
第三个参数u8Channel ****:****音频通道数
第四个参数u32DataLen ****:****每一帧aac码流的长度(这里需要注意的是,在写入AAC码流的时候需要把每个AAC长度+7,因为头部是adts的头部是7个字节)
设置完成之后,就要对每个码流进行7个字节头部的写入。
fwrite(aac_header, 1, 7, aac_file); //对每一帧AAC码流写入adts_header头部
1.6写入具体每一帧AAC的ES码流
在写入AAC头部之后,就可以写入每一帧具体的AAC的ES码流
..................................................
fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), aac_file); //写入每一帧AAC的ES码流
二代码实战
cpp
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "include/rkmedia/rkmedia_api.h"
#include "include/rkmedia/rkmedia_buffer.h"
#include "include/rkmedia/rkmedia_common.h"
#include "rkmedia_api.h"
#define AUDIO_PATH "default"
#define AI_CHN 0
#define AENC_CHN 0
#define AAC_PROFILE_LOW 1
typedef struct AacFreqIdx_
{
RK_S32 u32SampleRate;
RK_U8 u8FreqIdx;
} AacFreqIdx;
AacFreqIdx AacFreqIdxTbl[13] = {{96000, 0}, {88200, 1}, {64000, 2}, {48000, 3}, {44100, 4}, {32000, 5}, {24000, 6}, {22050, 7}, {16000, 8}, {12000, 9}, {11025, 10}, {8000, 11}, {7350, 12}};
static void GetAdtsHeader(RK_U8 *pu8AdtsHdr, RK_S32 u32SmpleRate, RK_U8 u8Channel,
RK_U32 u32DataLen)
{
RK_U8 u8FreqIdx = 0;
for (int i = 0; i < 13; i++)
{
if (u32SmpleRate == AacFreqIdxTbl[i].u32SampleRate)
{
u8FreqIdx = AacFreqIdxTbl[i].u8FreqIdx;
break;
}
}
RK_U32 u32PacketLen = u32DataLen + 7;
pu8AdtsHdr[0] = 0xFF; // 主要是写入syncword同步字节的前8位
pu8AdtsHdr[1] = 0xF1; // 主要是写入syncword同步字节的后4位,并且设置ID号、layer、protection_absent
pu8AdtsHdr[2] = ((AAC_PROFILE_LOW) << 6) + (u8FreqIdx << 2) + (u8Channel >> 2); // 设置音频profile、sample_rate_index、声道数
pu8AdtsHdr[3] = (((u8Channel & 3) << 6) + (u32PacketLen >> 11)); // 设置声道数,original_copy,home,copyright_identification_bit、copyright_identification_start、aac_frame_length
pu8AdtsHdr[4] = ((u32PacketLen & 0x7FF) >> 3); // 设置aac_frame_length+adts_buffer_fullness
pu8AdtsHdr[5] = (((u32PacketLen & 7) << 5) + 0x1F); // 设置adts_buffer_fullness + number_of_raw_data_blocks_in_frame
pu8AdtsHdr[6] = 0xFC; // 设置 number_of_raw_data_blocks_in_frame
}
void *get_audio_aenc_thread(void *args)
{
pthread_detach(pthread_self());
FILE *aac_file = fopen("test_capture.aac", "w+");
MEDIA_BUFFER mb = NULL;
RK_U8 aac_header[7];
while (1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_AENC, AENC_CHN, -1);
if (!mb)
{
printf("RK_MPI_SYS_GetMediaBuffer break....\n");
break;
}
printf("Get AAC_Buffer Success...\n");
//获取AENC的AAC码流头部
GetAdtsHeader(aac_header, 48000, 2, RK_MPI_MB_GetSize(mb));
fwrite(aac_header, 1, 7, aac_file); //写入7个字节的头部
fwrite(RK_MPI_MB_GetPtr(mb),1, RK_MPI_MB_GetSize(mb), aac_file); //写入AAC的ES码流
RK_MPI_MB_ReleaseBuffer(mb);
}
}
int main(int argc, char *argv[])
{
int ret;
AI_CHN_ATTR_S ai_chn_s;
ai_chn_s.pcAudioNode = AUDIO_PATH; //音频采样路径
ai_chn_s.u32SampleRate = 48000; //音频采样率
ai_chn_s.u32NbSamples = 1024; //音频采样个数
ai_chn_s.u32Channels = 2; //音频采样通道
ai_chn_s.enSampleFormat = RK_SAMPLE_FMT_S16; //采样格式
ai_chn_s.enAiLayout = AI_LAYOUT_NORMAL; //采样布局
ret = RK_MPI_AI_SetChnAttr(AI_CHN, &ai_chn_s); //设置AI属性
if (ret)
{
printf("RK_MPI_AI_SetChnAttr failed...\n");
}
else
{
printf("RK_MPI_AI_SetChnAttr success...\n");
}
ret = RK_MPI_AI_EnableChn(AI_CHN); //使能AI模块
if (ret)
{
printf("RK_MPI_AI_EnableChn failed...\n");
}
else
{
printf("RK_MPI_AI_EnableChn success...\n");
}
AENC_CHN_ATTR_S aenc_chn_attrs; //AENC
aenc_chn_attrs.enCodecType = RK_CODEC_TYPE_AAC;//AENC模块的编码协议
aenc_chn_attrs.u32Bitrate = 64000; //音频编码比特率,64kbps
aenc_chn_attrs.u32Quality = 1; //音频质量
aenc_chn_attrs.stAencAAC.u32Channels = 2; //音频通道数
aenc_chn_attrs.stAencAAC.u32SampleRate = 48000; //音频编码采样率,这里要和AI模块的采样率一致
ret = RK_MPI_AENC_CreateChn(AENC_CHN, &aenc_chn_attrs); //创建AENC模块
if (ret)
{
printf("RK_MPI_AENC_CreateChn failed....\n");
}
else
{
printf("RK_MPI_AENC_CreateChn success....\n");
}
MPP_CHN_S ai_mpp_chn_s;
ai_mpp_chn_s.enModId = RK_ID_AI;
ai_mpp_chn_s.s32ChnId = AI_CHN;
MPP_CHN_S aenc_mpp_chn_s;
aenc_mpp_chn_s.enModId = RK_ID_AENC;
aenc_mpp_chn_s.s32ChnId = AENC_CHN;
ret = RK_MPI_SYS_Bind(&ai_mpp_chn_s, &aenc_mpp_chn_s); //绑定AI模块和AENC模块
if (ret)
{
printf("RK_MPI_SYS_Bind failed....\n");
}
else
{
printf("RK_MPI_SYS_Bind success....\n");
}
pthread_t pid;
pthread_create(&pid, NULL, get_audio_aenc_thread, NULL); //创建线程获取AENC码流
while (1)
{
sleep(2);
}
RK_MPI_SYS_UnBind(&ai_mpp_chn_s, &aenc_mpp_chn_s);
RK_MPI_AENC_DestroyChn(AENC_CHN);
RK_MPI_AI_DisableChn(AI_CHN);
return 0;
}