通過多線程獲取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.pswp.cn/web/71384.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71384.shtml
英文地址,請注明出處:http://en.pswp.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 圖…