二.使用ffmpeg對原始音頻數據重采樣并進行AAC編碼

重采樣:將音頻三元組【采樣率 采樣格式 通道數】之中的任何一個或者多個值改變。

一.為什么要進行重采樣?

1.原始音頻數據和編碼器的數據格式不一致

2.播放器要求的和獲取的數據不一致

3.方便運算

二.本次編碼流程

1.了解自己本機麥克風參數,我的切換為44100/16/2;包括麥克風錄音的size可能不一樣,本機windows下錄音的size為88200;

1.ffmpeg獲取麥克風數據

2.ffmpeg對數據進行重采樣(本次三元組無需變換)

3.使用AAC編碼器對重采樣后的數據進行AAC編碼,然后存入.aac文件

4.使用ffplay播放測試

三.整體代碼

#include "customcodex.hpp"int add_samples_to_fifo(AVAudioFifo *fifo,uint8_t **input_data,const int frame_size)
{int ret = 0;int size = 0;printf("add_samples_to_fifo size:%d \n", frame_size);size = av_audio_fifo_size(fifo) + frame_size;ret = av_audio_fifo_realloc(fifo, size);if (ret < 0){printf("Error, Failed to reallocate fifo!\n");return ret;}ret = av_audio_fifo_write(fifo, reinterpret_cast<void **>(input_data), frame_size);if (ret < frame_size){printf("Error, Failed to write data to fifo!\n");return AVERROR_EXIT;}return 0;
}
int read_fifo_and_encode(AVAudioFifo *fifo,AVFormatContext *fmt_ctx,AVCodecContext *c_ctx,AVFrame *frame)
{int ret = 0;const int frame_size = FFMIN(av_audio_fifo_size(fifo),c_ctx->frame_size);printf("read fifo - size : %d ,c_ctx->frame_size : %d\n", av_audio_fifo_size(fifo), c_ctx->frame_size);ret = av_audio_fifo_read(fifo, reinterpret_cast<void **>(frame->data), frame_size);if (ret < frame_size){printf("Error, Failed to read data from fifo!\n");return AVERROR_EXIT;}return 0;
}
int open_coder(AVCodecContext **codec_ctx)
{// 編碼器const AVCodec *codex = avcodec_find_encoder_by_name("libfdk_aac");// codex->capabilities = AV_CODEC_CAP_VARIABLE_FRAME_SIZE;if (!codex){fprintf(stderr, "Codec not found\n");return -1;}// 編碼器上下文*codec_ctx = avcodec_alloc_context3(codex);(*codec_ctx)->sample_fmt = AV_SAMPLE_FMT_S16;       // 采樣大小(*codec_ctx)->channel_layout = AV_CH_LAYOUT_STEREO; //(*codec_ctx)->channels = 2;                         // 聲道數(*codec_ctx)->sample_rate = 44100;                  // 采樣率(*codec_ctx)->bit_rate = 0;                         // AAC 128k;AAC HE 64k; AAC_HE V2:32K// codec_ctx->profile = FF_PROFILE_AAC_HE_V2;       // 用哪個AACif (avcodec_open2(*codec_ctx, codex, NULL) < 0){fprintf(stderr, "failed avcodec_open2 \n");return -1;}return 0;
}
int encode(AVCodecContext *codec_ctx, AVFrame *avframe, AVPacket *outpkt, FILE *outfile)
{printf("open_coder - codec_ctx->frame_size: %d ,avframe-size:%d\n", codec_ctx->frame_size, avframe->nb_samples);int ret = avcodec_send_frame(codec_ctx, avframe);printf("avcodec_send_frame:%d\n", ret);if (ret < 0){fprintf(stderr, "Error sending frame to encoder\n");return -1;}while (ret >= 0){// 獲取編碼后的音頻數據ret = avcodec_receive_packet(codec_ctx, outpkt);printf("avcodec_receive_packet:%d\n", ret);if (ret < 0){printf("encode - ret: %d \n", ret);// 有數據但是不夠了生成一幀了   沒有數據了if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return 0;exit(-1);}printf("fwrite - outpkt.size: %d \n", outpkt->size);fwrite(outpkt->data, 1, outpkt->size, outfile);fflush(outfile);}return 0;
}int read_audio()
{int ret = 0;char errors[1024];AVFormatContext *fmt_ctx = NULL;AVDictionary *options = NULL;AVAudioFifo *fifo = nullptr;FILE *outfile = fopen("./out.pcm", "wb+");FILE *outfile_aac = fopen("./out.aac", "wb+");if (outfile == nullptr){printf("filed open out file\n");}AVPacket pkt;av_init_packet(&pkt);int frame_count = 0;const char *devicename = "audio=麥克風 (Realtek(R) Audio)";// 找到采集工具const AVInputFormat *iformat = av_find_input_format("dshow");if (iformat == NULL){printf("AVInputFormat find failed \n");return -1;}// 打開音頻設備ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);if (ret < 0){av_strerror(ret, errors, 1024);av_log(NULL, AV_LOG_ERROR, "error:%s", errors);return -1;}// 重采樣緩沖區uint8_t **src_data = NULL;int src_linesize = 0;uint8_t **dst_data = NULL;int dst_linesize = 0;// 初始化重采樣上下文SwrContext *swr_ctx = initSwr();if (!swr_ctx){printf("failed init swr\n");return -1;}// 編碼器上下文AVCodecContext *codec_ctx;ret = open_coder(&codec_ctx);if (ret != 0){return -1;}// 音頻輸入數據AVFrame *avframe = initInAvframe();//AVPacket *outpkt = av_packet_alloc();av_init_packet(outpkt);// Create the FIFO buffer for the audio samples to be encoded.fifo = av_audio_fifo_alloc(codec_ctx->sample_fmt, codec_ctx->channels, 1);if (!fifo){printf("Error, Failed to alloc fifo!\n");return -1;}// 88200/2=44100/2=22050// 每次讀取數據大小是88200,16位2個字節,雙聲道// 創建輸入緩沖區initBuffer(&src_data, src_linesize, &dst_data, dst_linesize);av_log(NULL, AV_LOG_DEBUG, "src-size:%d , dst-size:%d\n", src_linesize, dst_linesize);int count = 0;while (1){int frame_size = codec_ctx->frame_size;static bool finished = false;while (av_audio_fifo_size(fifo) < frame_size){printf("av_audio_fifo_size(fifo) :%d , frame_size :%d\n", av_audio_fifo_size(fifo), frame_size);ret = av_read_frame(fmt_ctx, &pkt);printf("av_read_frame-ret : %d\n", ret);if (ret == 0){printf("pkt-size:%d\n", pkt.size);memcpy((void *)src_data[0], (void *)pkt.data, pkt.size);ret = swr_convert(swr_ctx,                    // 重采樣的上下文dst_data,                   // 輸出結果緩沖區22050,                      // 每個通道的采樣數(const uint8_t **)src_data, // 輸入緩沖區22050);                     // 輸入單個通道的采樣數printf("swr_convert-ret:%d\n", ret);ret = add_samples_to_fifo(fifo, dst_data, 22050);printf("add_samples_to_fifo-ret:%d\n", ret);}if (count >= 20){finished = true;break;}count++;printf("##################### count:%d\n", count);}while (av_audio_fifo_size(fifo) > frame_size || (finished && av_audio_fifo_size(fifo) > 0)){ret = read_fifo_and_encode(fifo, fmt_ctx, codec_ctx, avframe);encode(codec_ctx, avframe, outpkt, outfile_aac);}if (finished){// 強制將編碼器緩沖區中的音頻進行編碼輸出encode(codec_ctx, nullptr, outpkt, outfile_aac);break;}}freePtr(src_data, dst_data, swr_ctx, fmt_ctx, outfile, outfile_aac);return 0;
}
void freePtr(uint8_t **src_data, uint8_t **dst_data, SwrContext *swr_ctx, AVFormatContext *fmt_ctx,FILE *outfile, FILE *outfile_aac)
{// 釋放輸入輸出緩沖區if (src_data){av_freep(&src_data[0]);}av_freep(src_data);if (dst_data){av_freep(&dst_data[0]);}av_freep(dst_data);// 釋放重采樣的上下文swr_free(&swr_ctx);avformat_close_input(&fmt_ctx);fclose(outfile);fclose(outfile_aac);av_log(NULL, AV_LOG_DEBUG, "end");
}
SwrContext *initSwr()
{SwrContext *swr_ctx = swr_alloc();// 設置重采樣參數av_opt_set_int(swr_ctx, "in_channel_count", 2, 0);av_opt_set_int(swr_ctx, "in_sample_rate", 44100, 0); // 輸入采樣率av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);av_opt_set_int(swr_ctx, "out_channel_count", 2, 0);av_opt_set_int(swr_ctx, "out_sample_rate", 44100, 0); // 輸出采樣率av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);swr_init(swr_ctx);return swr_ctx;
}
AVFrame *initInAvframe()
{AVFrame *avframe = av_frame_alloc();avframe->nb_samples = 1024; // 單通道一個音頻的采樣數avframe->format = AV_SAMPLE_FMT_S16;avframe->channel_layout = AV_CH_LAYOUT_STEREO; // AV_CH_LAYOUT_STEREOav_frame_get_buffer(avframe, 0);               // 22050*2*2=88200if (!avframe || !avframe->buf){printf("failed get frame buffer\n");return nullptr;}return avframe;
}
void initBuffer(uint8_t ***src_data, int &src_linesize, uint8_t ***dst_data, int &dst_linesize)
{av_samples_alloc_array_and_samples(src_data,          // 輸出緩沖區地址&src_linesize,     // 緩沖區的大小2,                 // 通道個數22050,             // 單通道采樣個數AV_SAMPLE_FMT_S16, // 采樣格式0);// 創建輸出緩沖區av_samples_alloc_array_and_samples(dst_data,          // 輸出緩沖區地址&dst_linesize,     // 緩沖區的大小2,                 // 通道個數22050,             // 單通道采樣個數AV_SAMPLE_FMT_S16, // 采樣格式0);
}

四.遇到的問題

1.采樣格式和采樣個數不明確

解決辦法:查看系統聲音設置中,相應設備的輸出格式,一般包含位深和采樣率

采樣個數的話通過打開并讀取音頻數據,可以通過pkt.size打印出來。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/73901.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/73901.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/73901.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

器材借用管理系統詳細設計基于Spring Boot-SSM

? 目錄 ?摘要 一、系統概述? ?二、系統架構設計? 2?.1技術選型? ?2.2系統架構? ?三、需求分析 3.1用戶需求分析 3.2功能模塊設計? 3.3、性能需求分析 3.4、安全需求分析 ?四、數據庫設計? ?五、安全性設計? ?六、系統測試與維護? ?七、總結?…

麒麟V10 arm cpu aarch64 下編譯 RocketMQ-Client-CPP 2.2.0

國產自主可控服務器需要訪問RocketMQ消息隊列&#xff0c;最新的CSDK是2020年發布的 rocketmq-client-cpp-2.2.0 這個版本支持TLS模式。 用默認的版本安裝遇到一些問題&#xff0c;記錄一下。 下載Releases apache/rocketmq-client-cpp GitHubhttps://github.com/apache/roc…

C語言每日一練——day_12(最后一天)

引言 針對初學者&#xff0c;每日練習幾個題&#xff0c;快速上手C語言。第十二天。&#xff08;最后一天&#xff0c;完結散花啦&#xff09; 采用在線OJ的形式 什么是在線OJ&#xff1f; 在線判題系統&#xff08;英語&#xff1a;Online Judge&#xff0c;縮寫OJ&#xff0…

網絡安全應急入門到實戰

奇安信&#xff1a;95015網絡安全應急響應分析報告&#xff08;2022-2024年&#xff09;官網可以下載 https://github.com/Bypass007/Emergency-Response-Notes 應急響應實戰筆記 網絡安全應急響應技術實戰指南 .pdf 常見場景 第4章 勒索病毒網絡安全應急響應 第5章 挖礦木…

jvm中每個類的Class對象是唯一的嗎

jvm中每個類的Class對象是唯一的嗎 在 Java 中&#xff0c;同一個類的 Class 對象在由同一個類加載器加載時是唯一的。析&#xff1a; 1. 同一類加載器的唯一性 規則&#xff1a;若一個類被同一個類加載器加載&#xff0c;無論創建多少實例&#xff0c;其 Class 對象始終唯一…

Visual Studio里的調試(debugging)功能介紹

參考 1- Introduction to Debugging | Basic Visual Studio Debugging&#xff08;這是一位印度博主視頻&#xff0c;我下面做到筆記也主要參考她的視頻&#xff0c;但不得不說口音太重了&#xff0c;一股咖喱味&#xff09; 目錄 個人對調試淺顯的認識和對調試的介紹逐行調…

NLP高頻面試題(六)——decoder-only、encoder-only和encoder-decoder的區別與聯系

一、基本概念與代表模型 1. Encoder-only 架構 Encoder-only 架構最具代表性的模型是 BERT。BERT 使用 masked language modeling&#xff08;MLM&#xff09;進行預訓練&#xff0c;即隨機遮蔽部分輸入詞匯&#xff0c;讓模型預測被遮蔽的詞匯。由于這種架構能夠同時看到輸入…

如何判斷 MSF 的 Payload 是 Staged 還是 Stageless(含 Meterpreter 與普通 Shell 對比)

在滲透測試領域&#xff0c;Metasploit Framework&#xff08;MSF&#xff09;的 msfvenom 工具是生成 Payload&#xff08;載荷&#xff09;的核心利器。然而&#xff0c;當我們選擇 Payload 時&#xff0c;經常會遇到一個問題&#xff1a;這個 Payload 是 Staged&#xff08;…

基于FPGA的3U機箱模擬量高速采樣板ADI板卡,應用于軌道交通/電力儲能等

板卡簡介&#xff1a; 本板為模擬量高速采樣板&#xff08;ADI&#xff09;&#xff0c;主要用于電機轉速和相電流檢測&#xff0c;以實現電機閉環控制。 性能規格&#xff1a; 電源&#xff1a;DC5V&#xff0c;DC3.3V&#xff0c;DC15V&#xff0c;DC24V FPGA&#xff1a;…

Gymnasium Cart Pole 環境與 REINFORCE 算法 —— 強化學習入門 2

Title: Gymnasium Cart Pole 環境與 REINFORCE 算法 —— 強化學習入門 2 文章目錄 I. Gymnasium Cart Pole 環境II. REINFORCE 算法1. 原理說明2. REINFORCE 算法實現 I. Gymnasium Cart Pole 環境 Gymnasium Cart Pole 環境是一個倒立擺的動力學仿真環境. 狀態空間: 0: Ca…

Python高級:GIL、C擴展與分布式系統深度解析

文章目錄 &#x1f4cc; **前言**&#x1f527; **第一章&#xff1a;Python語言的本質與生態**1.1 **Python的實現與版本演進**1.2 **開發環境與工具鏈** &#x1f527; **第二章&#xff1a;元編程與動態特性**2.1 **描述符協議&#xff08;Descriptor Protocol&#xff09;*…

C++學習筆記(二十一)——文件讀寫

一、文件讀寫 作用&#xff1a; 文件讀寫指的是將數據從程序存儲到文件&#xff0c;或從文件讀取數據&#xff0c;以實現數據的持久化存儲。 C 提供了 fstream 頭文件&#xff0c;用于文件操作&#xff0c;主要包括&#xff1a; ofstream&#xff08;輸出文件流&#xff09;—…

RBA+minibatch的嘗試

目錄 還是咬著牙來寫 RBA了 JAX JAX->TORCH torch tensor的變形 pytorch怎么把一個【3,3,5】的tensor變成【3,10,5】&#xff0c;多的用0填充 pytorch如何把shape【100】轉成【100,1】 把torch shape【100,1】變成【100】 SQUEEZE grad_fn 不能兩次反向傳播 還…

基于Python+Django的二手房信息管理系統

項目介紹 PythonDjango二手房信息管理系統(Pycharm Django Vue Mysql) 平臺采用B/S結構&#xff0c;后端采用主流的Python語言進行開發&#xff0c;前端采用主流的Vue.js進行開發。 整個平臺包括前臺和后臺兩個部分。 - 前臺功能包括&#xff1a;首頁、二手房信息、公告管理、…

爬蟲基礎之爬取貓眼Top100 可視化

網站: TOP100榜 - 貓眼電影 - 一網打盡好電影 本次案例所需用到的模塊 requests (發送HTTP請求) pandas(數據處理和分析 保存數據) parsel(解析HTML數據) pyecharts(數據可視化圖表) pymysql(連接和操作MySQL數據庫) lxml(數據解析模塊) 確定爬取的內容: 電影名稱 電影主演…

解決Qt信號在構造函數中失效的問題

情景引入&#xff1a;音樂播放器的“幽靈列表”問題 假設你正在開發一個音樂播放器應用&#xff0c;其中有一個功能是用戶首次打開應用時&#xff0c;需要從服務器拉取最新的歌曲列表并顯示在“本地音樂”頁面中。你可能會寫出類似這樣的代碼&#xff1a; // LocalSong 類的構…

Hadoop 啟動,發現 namenode、secondary namenodes,這兩個沒有啟動,報錯超時。

今天在啟動 hadoop 的時候&#xff0c;發現本應該同時啟動的 namenode、secondary namenodes 卻都沒有啟動。我還以為是壞了又重新裝了虛擬機&#xff0c;重新下載 Hadoop 重新配置結果還是同樣的問題&#xff0c;那沒辦法只能去解決問題了。 首先先再次嘗試啟動看他報錯是什么…

Ranger 鑒權

Apache Ranger 是一個用來在 Hadoop 平臺上進行監控&#xff0c;啟用服務&#xff0c;以及全方位數據安全訪問管理的安全框架。 使用 ranger 后&#xff0c;會通過在 Ranger 側配置權限代替在 Doris 中執行 Grant 語句授權。 Ranger 的安裝和配置見下文&#xff1a;安裝和配置 …

Sqlserver安全篇之_啟用和禁用Named Pipes的案列介紹

https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/named-pipes-properties?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/client-protocols-named-pipes-properties-protocol-tab?viewsql-server-ver16 默認…

深入解析過濾器模式(Filter Pattern):一種靈活高效的設計模式

過濾器模式&#xff08;Filter Pattern&#xff09;&#xff0c;也被稱為標準模式&#xff0c;是一種常見的結構型設計模式。它通過將對象分為不同的標準或條件&#xff0c;使得對對象集合的操作變得更加靈活和高效。特別適用于處理復雜查詢和條件過濾的場景。過濾器模式不僅能…