通过多线程获取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 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/71384.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM常用概念之对象初始化的成本

在JVM常用概念之新对象实例化博客中我讲到了对象的实例化&#xff0c;主要包含分配&#xff08;TLAB&#xff09;、系统初始化、用户初始化&#xff0c;而我在JVM常用概念之线程本地分配缓冲区&#xff08;ThreadLocal Allocation Buffer&#xff0c;TLAB&#xff09;博客中也讲…

java后端开发day27--常用API(二)正则表达式爬虫

&#xff08;以下内容全部来自上述课程&#xff09; 1.正则表达式&#xff08;regex&#xff09; 可以校验字符串是否满足一定的规则&#xff0c;并用来校验数据格式的合法性。 1.作用 校验字符串是否满足规则在一段文本中查找满足要求的内容 2.内容定义 ps&#xff1a;一…

AI---DevOps常备工具(‌AI-Integrated DevOps Essential Tools)

AI---DevOps常备工具 技术领域正在迅速发展&#xff0c;随着我们步入 2025 年&#xff0c;有一点是明确的&#xff1a;人工智能&#xff08;AI&#xff09;不再只是一个流行词&#xff0c;它是每个 DevOps 工程师都需要掌握的工具。随着云环境的复杂性增加、对更快部署的需求以…

Pytorch中的主要函数

目录 一、torch.manual_seed(seed)二、torch.cuda.manual_seed(seed)三、torch.rand(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)四、给大家写一个常用的自动选择电脑cuda 或者cpu 的小技巧五、torch.version.cuda&#xff1b;torch.bac…

Spring Boot中对接Twilio以实现发送验证码和验证短信码

Twilio介绍 Twilio是一家提供云通信服务的公司&#xff0c;旨在帮助开发者和企业通过简单的API实现各种通信功能。以下是Twilio的一些主要特点和服务介绍&#xff1a; 核心功能 短信服务&#xff08;SMS&#xff09;&#xff1a;允许用户通过API发送和接收短信&#xff0c;支…

VSCode详细安装步骤,适用于 Windows/macOS/Linux 系统

以下是 Visual Studio Code (VSCode) 的详细安装步骤&#xff0c;适用于 Windows/macOS/Linux 系统&#xff1a; VSCode 的详细安装步骤 一、Windows 系统安装1. 下载安装包2. 运行安装程序3. 验证安装 二、macOS 系统安装1. 方法一&#xff1a;官网下载安装包2. 方法二&#x…

基于PyTorch的深度学习3——基于autograd的反向传播

反向传播&#xff0c;可以理解为函数关系的反向传播。

设备管理系统功能与.NET+VUE(IVIEW)技术实现

在现代工业和商业环境中&#xff0c;设备管理系统&#xff08;Equipment Management System&#xff0c;简称EMS&#xff09;是确保设备高效运行和维护的关键工具。本文采用多租户设计的设备管理系统&#xff0c;基于.NET后端和VUE前端&#xff08;使用IVIEW UI框架&#xff09…

PHP之特性

在你有别的编程语言的基础下&#xff0c;你想学习PHP&#xff0c;可能要了解的PHP特有的东西。 定界符 使用<<<TT(可以是任意字符&#xff0c;但是不可以在别的地方使用过)和TT&#xff0c;会解析html格式和变量&#xff0c;如果在<<<后面加上单引号就会不…

9-Agent大模型中工作流的使用方法分析

目录 关键词 摘要 速览 配置插件进行新闻内容查找的工作流设置 自动化调用用户输入变量的插件配置教程 配置大模型以整理并简要输出新闻内容 新闻内容总结功能调试与优化 搭建与发布工作流优化布局的流程详解 创建和配置智能体工作流程 调试页面与工作流配置演示 思…

记一次:泛微OA集成Mybatis后 insert/update执行成功,但未真正插入或修改数据

背景&#xff1a;通过Mybatis插入数据或更新数据&#xff0c;显示插入/更新成功&#xff0c;查询数据库&#xff0c;发现并未插入成功、数据也没更新成功。下面是Mapper文件 public interface TestOrmMapper {int insertByTest(Param("requestId") Integer requestI…

使用 Spring Boot 实现前后端分离的海康威视 SDK 视频监控

使用 Spring Boot 实现前后端分离的海康威视 SDK 视频监控系统&#xff0c;可以分为以下几个步骤&#xff1a; 1. 系统架构设计 前端&#xff1a;使用 Vue.js、React 或 Angular 等前端框架实现用户界面。后端&#xff1a;使用 Spring Boot 提供 RESTful API&#xff0c;负责与…

【大模型系列篇】国产开源大模型DeepSeek-V3技术报告解析

DeepSeek-V3技术报告 目录 DeepSeek-V3技术报告 1. 摘要 2. 引言 3. DeepSeek V3 架构 3.1 基础架构 3.1.1. 多头潜在注意力 3.1.2. DeepSeekMoE和无辅助损失的负载均衡 3.2 多令牌预测 4. 基础设施 4.1 计算集群 4.2 训练框架 4.2.1. DualPipe算法与计算通信协同优…

负载均衡 - 一致性hash算法

构建场景 假如我们有三台缓存服务器编号node0、node1、node2&#xff0c;现在有3000万个key&#xff0c;希望可以将这些个key均匀的缓存到三台机器上&#xff0c;你会想到什么方案呢&#xff1f; 我们可能首先想到的方案&#xff0c;是取模算法hash&#xff08;key&#xff0…

pdfplumber 解析 PDF 表格的原理

&#x1f4cc; pdfplumber 解析 PDF 表格的原理 pdfplumber 处理表格的原理是基于几何分析&#xff08;geometric analysis&#xff09;&#xff0c;它通过分析 PDF 页面中的线条、单元格间距和文本分布&#xff0c;提取表格数据。它主要利用 垂直线&#xff08;vertical line…

洛谷P1334

题目如下 思路&#xff1a; 每次选择最短的两块木板进行合并&#xff0c;直到只剩下一块木板。使用最小堆&#xff08;优先队列&#xff09;来实现这一过程。使用最小堆&#xff1a; 将所有木板的长度放入最小堆&#xff08;优先队列&#xff09; 每次从堆中取出两块最短的木…

JVM(Java Virtual Machine,Java 虚拟机)的作用

JVM&#xff08;Java Virtual Machine&#xff0c;Java 虚拟机&#xff09;的作用至关重要&#xff0c;它是 Java 语言“一次编写&#xff0c;到处运行”&#xff08;Write Once, Run Anywhere&#xff0c;WORA&#xff09;特性的基石&#xff0c;也是 Java 平台的核心组成部分…

总结(尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程)

1.Vue简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece 1.1.性能的提升 打包大小减少41%。 初次渲染快55%, 更新渲染快133%。 内存减少54%。 1.2.源码的升级 使用Proxy代替defineProperty实现响应式。 重写虚拟DOM的实现和Tree-Shak…

SolidWorks 转 PDF3D 技术详解

在现代工程设计与制造流程中&#xff0c;不同软件间的数据交互与格式转换至关重要。将 SolidWorks 模型转换为 PDF3D 格式&#xff0c;能有效解决模型展示、数据共享以及跨平台协作等问题。本文将深入探讨 SolidWorks 转 PDF3D 的技术原理、操作流程及相关注意事项&#xff0c;…

【深度学习CV】【图像分类】从CNN(卷积神经网络)、ResNet迁移学习到GPU高效训练优化【案例代码】详解

摘要 本文分类使用的是resNet34,什么不用yolo v8&#xff0c;yolo v10系列,虽然他们也可以分类&#xff0c;因为yolo系列模型不纯粹&#xff0c;里面包含了目标检测的架构&#xff0c;所以分类使用的是resNet 本文详细介绍了三种不同的方法来训练卷积神经网络进行 CIFAR-10 图…