目錄
一RV1126多線程獲取音頻編碼AAC碼流的流程
1.1AI模塊的初始化并使能
1.2AENC模塊的初始化
???????1.3綁定AI模塊和AENC模塊
???????1.4多線程獲取每一幀AAC碼流
???????1.5每個AAC碼流添加ADTSHeader頭部
???????1.6寫入具體每一幀AAC的ES碼流
二代碼實戰
一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 u32SmpleRate, RK_U8 u8Channel,RK_U32 u32DataLen)
{
????RK_U8 u8FreqIdx = 0;
????for (int i = 0; i < 13; i++)
????{
????????if (u32SmlpRate == 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
}
上面這個方法需要傳入四個參數,分別是:
第一個參數pu8AdtsHdr:需要處理輸出的字符串
第二個參數u32SampleRate:音頻的采樣率(根據音頻采樣率去查找對應的采樣率索引,這里的索引是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碼流
二代碼實戰
#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 1typedef 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_absentpu8AdtsHdr[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_lengthpu8AdtsHdr[4] = ((u32PacketLen & 0x7FF) >> 3); // 設置aac_frame_length+adts_buffer_fullnesspu8AdtsHdr[5] = (((u32PacketLen & 7) << 5) + 0x1F); // 設置adts_buffer_fullness + number_of_raw_data_blocks_in_framepu8AdtsHdr[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; //AENCaenc_chn_attrs.enCodecType = RK_CODEC_TYPE_AAC;//AENC模塊的編碼協議aenc_chn_attrs.u32Bitrate = 64000; //音頻編碼比特率,64kbpsaenc_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;
}