通过多线程获取RV1126的AAC码流

目录

一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 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;
}
相关推荐
黎明晓月28 分钟前
‌CentOS 7.9 安装 Docker 步骤
linux·docker·centos
菜鸟xy..37 分钟前
winhex软件简单讲解,虚拟磁盘分区介绍
linux·运维·服务器
网硕互联的小客服40 分钟前
如何排查服务器内存泄漏问题
linux·运维·服务器·安全·ssh
Evoxt 益沃斯1 小时前
How to enable Qemu Guest Agent for Virtual Machines
linux·运维·服务器·qemu
钟离墨笺1 小时前
【Linux】【网络】UDP打洞-->不同子网下的客户端和服务器通信(未成功版)
linux·服务器·网络
llkk星期五1 小时前
ubuntu 22.04附加驱动安装NVIDIA显卡驱动重启后无WiFi蓝牙等问题
linux·ubuntu
CVer儿1 小时前
ubuntu挂载固态硬盘
linux·运维·ubuntu
music&movie1 小时前
Win11安装VMware和Ubuntu并使用ssh访问部署模型
linux·ubuntu·ssh
J.Pei1 小时前
Linux(ubuntu)环境下部署The Fuck项目的方法(保姆级教程)
linux·ubuntu
地球空间-技术小鱼1 小时前
学习笔记-AMD CPU 命名
linux·服务器·人工智能·笔记·学习