国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn)

這篇具有很好參考價(jià)值的文章主要介紹了音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

原理:

  1. 消除冗余信息,壓縮量最大,也叫有損壓縮
  • 剔除人耳聽覺范圍外的音頻信號(hào)20Hz以下和20000Hz以上;
  • 去除被掩蔽的音頻信號(hào),信號(hào)的遮蔽可以分為頻域遮蔽和時(shí)域遮蔽;
  • 頻域遮蔽效應(yīng)
    屏蔽70分貝以下,20HZ以下,20000HZ以上
    屏蔽分貝小,頻率小的聲音
    兩個(gè)頻率相近發(fā)出的聲音,去除低強(qiáng)度的,也就是分貝高的會(huì)蓋住分貝低的

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

  • 時(shí)域遮蔽效應(yīng):
    根根時(shí)間推移,相近頻率且同時(shí)出現(xiàn)的聲音,聲音強(qiáng)度高的遮蔽強(qiáng)度低的聲音,并且去除同一時(shí)間段前后雜音,前遮蔽50毫秒,后遮蔽200毫秒,在這段時(shí)間內(nèi)的聲音,強(qiáng)度越接近就越會(huì)被屏蔽。

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

  1. 去除冗余信息后,再進(jìn)行無損壓縮;
  • 無損壓縮就是壓縮后的數(shù)據(jù)能夠解壓縮進(jìn)行還原,有損則不能;
  • 熵編碼中有
    哈夫曼編碼:用一個(gè)很小的二進(jìn)制數(shù)代替一個(gè)長(zhǎng)的字符串,頻率越高,編碼越小,頻率越低,編碼越長(zhǎng)
    算術(shù)編碼:利用小數(shù)進(jìn)行編碼,在香農(nóng)編碼的基礎(chǔ)改進(jìn)而來的
    香農(nóng)編碼

音頻編碼過程

數(shù)據(jù)先同時(shí)通過 時(shí)域轉(zhuǎn)頻域變換器和心理學(xué)模型處理數(shù)據(jù),前者將數(shù)據(jù)轉(zhuǎn)換成多種頻段的數(shù)據(jù),然后剔除不需要的頻段數(shù)據(jù),后者會(huì)去除非人耳聽到的范圍聲音和一些復(fù)合聲音,最后將兩者合并經(jīng)過量化編碼,無損編碼之類的,形成比特流數(shù)據(jù),在此之前還會(huì)有一些輔助數(shù)據(jù),此后數(shù)據(jù)就會(huì)變得非常??;

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

常見的音頻編碼器

opus、aac、Ogg、Speex、iLBC、AMR、G.711, 最常用的編碼器是opus aac。
opus常用于直播,尤其是無延遲的直播,webrtc默認(rèn)使用opus;
AAC是應(yīng)用最廣泛的編解碼;
Ogg收費(fèi);
Speex支持回音消除;
G.711一般用于固定電話,聲音損耗嚴(yán)重,通話會(huì)失真;

本文福利, 免費(fèi)領(lǐng)取C++音視頻學(xué)習(xí)資料包+學(xué)習(xí)路線大綱、技術(shù)視頻/代碼,內(nèi)容包括(音視頻開發(fā),面試題,F(xiàn)Fmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,編解碼,推拉流,srs),有需要的可以進(jìn)企鵝裙927239107領(lǐng)取哦~

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼?

AAC比較適合有一定延遲的直播,AAC-LD屬于低延遲編碼器

  • AAC編碼器:目前應(yīng)用最廣泛,如iOS、安卓和其他嵌入式設(shè)備都包含了AAC硬件編解碼器,主要學(xué)習(xí)這個(gè)編碼器;
    用來取代mp3,比mp3更高的壓縮比和保真性更強(qiáng);

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

常用的規(guī)格有AAC LC、AAC HE V1 、AAC HE V2三種;

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

AAC HE V1 = AAC + SBR;
AAV HE V2 = AAC + SBR + PS;
目前AAC HE V1 已經(jīng)被取代 V2 取代了;

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

V2的碼流跟V1的差別不是很大,根據(jù)聲音的數(shù)據(jù)變化,如果兩個(gè)聲道的差別很大,碼流差別就會(huì)越??;

AAC 中header有兩種格式:

就相當(dāng)于在aac數(shù)據(jù)前面加了個(gè)Header,header里面就會(huì)包含aac數(shù)據(jù)的一些信息,方便進(jìn)行編解碼

  1. ADIF(Audio data interchange format): 特點(diǎn)是只能從頭開始解碼,可以確定的找到音頻數(shù)據(jù)的開始部分,不能從音頻數(shù)據(jù)中間開始,這種格式常用于磁盤文件中;
  2. ADTS(Audio Data Transport Format):在每一幀的數(shù)據(jù)里面都會(huì)有一個(gè)同步字,也就是每幀都有一個(gè)header,所以他可以在任意的位置開始進(jìn)行解碼,就像流式數(shù)據(jù);
  • ADTS結(jié)構(gòu): 由7-9個(gè)字節(jié)組成,通常情況下是7個(gè)字節(jié),如果有CRC 就是9個(gè)字節(jié),字節(jié)中的每一位都有獨(dú)特的含義;
    • 1~12bit:全部是1也就是0xFFF,表示是同步字;
    • 13:編碼規(guī)范 0 = MPEG-4 1 = MPEG-2;
    • 14~15:總是0;
    • 16:是否有保護(hù) 1 代表 沒有 CRC 0 代表有CRC;
    • 17~18:表示的是MPEG-4的音頻類型:AAC LC、 AAC HE V1 、AAC HE V2
    • 19~22:表示的是采樣率
    • 24~26:通道數(shù)
    • 31~33:數(shù)據(jù)長(zhǎng)度,也包括了header的長(zhǎng)度
  • 剩余的之后補(bǔ)上

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

其中每一十進(jìn)制數(shù)對(duì)應(yīng)的含義:

Audio Object Type: 在代碼中實(shí)際獲取類型的時(shí)候需要進(jìn)行+1,才是下面的類型
1 == AAC main
2 == AAC LC
5 == SBR == HE V1
29 == ps == HE V2

其中的采樣率是通過十進(jìn)制數(shù)表示的一個(gè)采樣率,有一個(gè)表,比如:0 == 96000Hz 1 == 88200HZ 等

音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn),音視頻開發(fā),音視頻,音視頻開發(fā),C++音視頻,PCM,AAC,視音頻編解碼,編解碼

音頻采集實(shí)戰(zhàn)

每個(gè)端音頻采集的底層和應(yīng)用層的庫是不一樣的,所以使用ffmpeg中間層能夠?qū)崿F(xiàn)跨平臺(tái)開發(fā);

  • Android端的底層庫是AudioRecorder,應(yīng)用層是MediaRecorder;
  • iOS端的底層庫是AudioUnit,應(yīng)用層是AVFoundation;
  • Windows端的常用的是Directshow OpenAL 還有Windows7之上的AudioCore;

使用ffmpeg有兩種采集方式:

  1. 使用命令方式,命令詳情查看ffmpeg相關(guān)指令的那篇
  2. 使用代碼調(diào)用api的方式
  • 在mac下的動(dòng)態(tài)庫需要對(duì)動(dòng)態(tài)庫進(jìn)行簽名

獲取本地簽名證書列表:/usr/bin/security find-identity -v -p codesigning
查看動(dòng)態(tài)庫是否簽名: codesign -d -vv 動(dòng)態(tài)庫文件
簽名命令:codesign -fs "iPhone Distribution: 你的簽名證書." 動(dòng)態(tài)庫文件
xcode環(huán)境:13.2.1
簽名了如果還是報(bào)錯(cuò),關(guān)掉沙盒并且設(shè)置 Enable Hardened Runtime 為NO
在項(xiàng)目中設(shè)置user header search path的時(shí)候,要使用全路徑方式,我使用$(PROJECT_NAME)方式,有的頭文件在鏈接的時(shí)候會(huì)報(bào)錯(cuò);

采集音頻的步驟:

  1. 打開輸入輸出設(shè)備,涉及的包是avdevice avformat 注冊(cè)設(shè)備 設(shè)置采集方式,根據(jù)平臺(tái)選擇,即設(shè)置輸入 打開音頻設(shè)備
  2. 獲取數(shù)據(jù)包 包:avcodec 主要使用av_read_frame方法獲取數(shù)據(jù) 將數(shù)據(jù)放入packet中 在讀取的時(shí)候注意緩沖區(qū)無未準(zhǔn)備好的情況
  3. 將數(shù)據(jù)輸出到文件 創(chuàng)建文件--- fopen 將數(shù)據(jù)寫入文件-- fwrite 關(guān)閉文件 -- fclose
  • 打開設(shè)備 ·
void startRecorder(void) {
    // 上下文
    AVFormatContext *av_context = NULL;
    AVDictionary *options = NULL;
    // 1. 注冊(cè)設(shè)備
    avdevice_register_all();
    
    // 2. 設(shè)置采集方式
    //設(shè)置采集方式 mac os 下是AVfoundation Windows下是dshow  linux 下是alsa
    AVInputFormat *format = av_find_input_format("avfoundation");
    
    // 3. 打開設(shè)備
    //里面的識(shí)別格式為[[video device]:[audio device]]  這里寫0 是獲取第1個(gè)音頻設(shè)備
    char *name = ":0";

    // url 是路徑 可以是網(wǎng)絡(luò)路徑也可以是本地路徑 本地路徑mac下的格式是 video : audio 這里表示獲取第一個(gè)音頻設(shè)備
    int result = avformat_open_input(&av_context, name, format, &options);
    
    if (result != 0) {
        
        char errors[1024];
        // 根據(jù)返回值生成錯(cuò)誤信息
        av_make_error_string(errors, 1024, result);
        printf("打開設(shè)備失?。?s\n", errors);
        return;
    }
    
    printf("打開設(shè)備成功!\n");
    

    get_audio_packet(av_context,&packet_callback);
    
    // 關(guān)閉輸入 上下文
    avformat_close_input(&av_context);
    
}
  • 讀取數(shù)據(jù)和存儲(chǔ)到文件
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 寫  b == 二進(jìn)制  + == 沒有就創(chuàng)建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音視頻學(xué)習(xí)/音視頻文件/code_recorder.pcm", "wb+");
    
    
    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循環(huán)讀取設(shè)備信息
    // result == -35 是Resource temporarily unavailable 因?yàn)楂@取太頻繁 設(shè)備未準(zhǔn)備好,還正在處理數(shù)據(jù)  
  // 因?yàn)檩斎朐O(shè)備準(zhǔn)備好需要時(shí)間  睡一秒后再讀取  
     sleep(1.0);
    while ((result = av_read_frame(·context, packet)) == 0  || result == -35) {
     
        if (packet->size > 0) {
            packet_callback(*packet);
            fwrite(packet->data, packet->size, 1, f);
            // 每讀取一次 就清空數(shù)據(jù)包 不然數(shù)據(jù)包會(huì)一直增大
            av_packet_unref(packet);
          
        }
    }
    
    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);
        
        printf("get packet occured error is \"%s\" \n", errors);
    }
    
    // 將緩沖區(qū)剩余的數(shù)據(jù) 強(qiáng)制寫入文件
    fflush(f);
    fclose(f);
    
    // 釋放packet空間
    av_packet_free(&packet);
    
}
// 回調(diào)函數(shù)
void packet_callback(AVPacket packet) {
    
    printf("packet size is %d\n",packet.size);

}
  • 播放
  • ffplay 播放pcm數(shù)據(jù): ffplay -ar(采樣率) 44100 -ac(通道數(shù)) 2 -f(采樣大?。ゝ32le 文件名

音頻編解碼實(shí)戰(zhàn)

音頻重采樣

就是將音頻三元組(采樣率 采樣大小 通道數(shù))的值轉(zhuǎn)成另外一組值

1. 應(yīng)用場(chǎng)景:

1、從設(shè)備采集的音頻數(shù)據(jù)與編碼器要求的不一致;
2、揚(yáng)聲器要求的音頻數(shù)據(jù)與要播放的音頻數(shù)據(jù)不一致;
3、方便運(yùn)算:例如回音消除 將多聲道變?yōu)閱温暤?

2. 如何判斷是否需要重采樣

  • 了解音頻設(shè)備的參數(shù)
  • 查看ffmpeg源碼

3. 重采樣的步驟

api:需要使用libswresample庫

1. 創(chuàng)建重采樣上下文

 - swr_alloc_set_opts 通過設(shè)置采樣參數(shù)獲取上下文      

2. 設(shè)置參數(shù)

- 參數(shù)大體分為輸出的采樣率、采樣大小、聲道和輸入的采樣率、采樣大小、聲道;
- out_ch_layout:表示聲道也可以是布局(揚(yáng)聲器的布局)AV_CH_LAYOUT_STEREO  立體聲;
- out_sample_fmt:輸出的采樣格式 16 = AV_SAMPLE_FMT_S16 或者 32 =  AV_SAMPLE_FMT_FLT;
-  av_sample_fmt_s16 in_ch_layout:輸入的聲道布局  ;
-  in_sample_fmt 輸入的采樣格式 ;
-  in_sample_rate:  輸入的采樣率;
-  后兩位是log相關(guān) 0,null

3. 初始化重采樣

- swr_init 初始化上下文  

4. 進(jìn)行重采樣

- swr_convert 開始轉(zhuǎn)換 ,目的就是將輸入緩沖區(qū)的數(shù)據(jù)寫入輸出緩沖區(qū)
  out:輸出結(jié)果緩沖區(qū) out_count:每個(gè)通道的采樣數(shù) 
  in:輸入的緩沖區(qū) in_count:輸入的單個(gè)通道的采樣數(shù)  
- 因?yàn)橹夭蓸拥臄?shù)據(jù)需要重新構(gòu)造所以需要?jiǎng)?chuàng)建輸入緩沖區(qū)和輸出緩沖區(qū)  
  使用av_sample_array_and_samples audio_data創(chuàng)建
  其中的單通道采樣數(shù)(單位是字節(jié))`nb_samples = pkt.size / (32位 / 8) / 2(通道數(shù))` 
  linessize:緩沖區(qū)大小  align:對(duì)齊 0 
- 在轉(zhuǎn)換前需要將pkt的data按字節(jié)拷貝到輸入緩沖區(qū),調(diào)用memcpy需要引用string.h

- 將輸出數(shù)據(jù)寫入文件
  將輸出緩沖區(qū)已經(jīng)轉(zhuǎn)換的數(shù)據(jù)寫入文件

5. 釋放資源

- 還有輸入輸出緩沖區(qū)的釋放av_freep
- swr_free釋放上下文

重采樣上下文初始化代碼

SwrContext * init_swr_context(void) {
    
    SwrContext *context = NULL;
    
    // 假設(shè)已經(jīng)提前知道輸入音頻數(shù)據(jù)的三要素的值 AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLT, 44100
    context = swr_alloc_set_opts(NULL,
                                 AV_CH_LAYOUT_STEREO,
                                 AV_SAMPLE_FMT_S16,
                                 44100,
                                 AV_CH_LAYOUT_STEREO,
                                 AV_SAMPLE_FMT_FLT,
                                 44100,
                                 0, NULL);
    
    int result = swr_init(context);
    
    if (result != 0) {
        char error[1024];
        av_make_error_string(error, 1024, result);
        printf("初始化重采樣上下文失敗:%s", error);
    }
    
    return context;
}

將采集的數(shù)據(jù)重采樣后 寫入文件代碼

void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 寫  b == 二進(jìn)制  + == 沒有就創(chuàng)建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音視頻學(xué)習(xí)/音視頻文件/resample.pcm", "wb+");

    // 初始化重采樣上下文
    SwrContext *swr_context = init_swr_context();

    // 初始化轉(zhuǎn)換的輸入輸出緩沖區(qū)
    uint8_t **out_buffer = NULL;
    int linesize_out = 0;
    av_samples_alloc_array_and_samples(&out_buffer, &linesize_out, 2, 512, AV_SAMPLE_FMT_S16, 0);
    uint8_t **in_buffer = NULL;
    int linesize_in = 0;
    // nb_samples 單通道采樣數(shù) 4096 / (32 / 8) / 2 = 1024
    av_samples_alloc_array_and_samples(&in_buffer, &linesize_in, 2, 512, AV_SAMPLE_FMT_FLT, 0);


    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循環(huán)讀取設(shè)備信息
    // result == -35 是Resource temporarily unavailable 因?yàn)楂@取太頻繁 設(shè)備未準(zhǔn)備好,還正在處理數(shù)據(jù)
    sleep(1);
    while (((result = av_read_frame(context, packet)) == 0  || result == -35) && isRecording == 1) {

        if (packet->size > 0) {


            // 開始轉(zhuǎn)換數(shù)據(jù)
            // 先將音頻數(shù)據(jù)拷貝到輸入緩沖區(qū)  只是重采樣音頻的話  只需要處理數(shù)組的第一個(gè)
            memcpy(in_buffer[0], packet->data, packet->size);
            // 再進(jìn)行轉(zhuǎn)換
            swr_convert(swr_context, out_buffer, 512, (const uint8_t **)in_buffer, 512);

            fwrite(out_buffer[0],linesize_out, 1, f);
            // 每讀取一次 就清空數(shù)據(jù)包 不然數(shù)據(jù)包會(huì)一直增大
            av_packet_unref(packet);
        }

    }

    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);

        printf("get packet occured error is \"%s\" \n", errors);
    }

    // 釋放重采樣資源
    if (in_buffer) {
        av_freep(&in_buffer[0]);
    }
    if (out_buffer) {
        av_freep(&out_buffer[0]);
    }
    av_freep(&in_buffer);
    av_freep(&out_buffer);
    swr_free(&swr_context);

    // 將緩沖區(qū)剩余的數(shù)據(jù) 強(qiáng)制寫入文件
    fflush(f);
    fclose(f);

    // 釋放packet空間
    av_packet_free(&packet);
    
    
}

ffmpeg 音頻數(shù)據(jù)編碼

本文福利, 免費(fèi)領(lǐng)取C++音視頻學(xué)習(xí)資料包+學(xué)習(xí)路線大綱、技術(shù)視頻/代碼,內(nèi)容包括(音視頻開發(fā),面試題,F(xiàn)Fmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,編解碼,推拉流,srs),有需要的可以進(jìn)企鵝裙927239107領(lǐng)取哦~

在使用fdk_aac編碼器的時(shí)候,由于默認(rèn)的ffmpeg有自帶的aac,所以通過avcodec_find_encoder_by_name("libfdk_aac")就獲取不到。在編譯的時(shí)候加上--enable-libfdk-aac。注意:重新編譯安裝ffmpeg之前最好先刪掉之前的ffmpeg,然后更新項(xiàng)目中的動(dòng)態(tài)庫;
如果還不行,試試單獨(dú)下載安裝[fdk_aac](https://www.linuxfromscratch.org/blfs/view/svn/multimedia/fdk-aac.html),再重新編譯ffmpeg

  1. 創(chuàng)建編碼器 avcodec
    1. avcodec_find_encoder 一種通過名字查找 一種是通過id查找,id的查找方式只會(huì)找默認(rèn)的編碼器,比如aac,如果是fdkaac就需要通過名字查找;
  2. AV_CODEC_ID_AAC | opus 其他編碼器
  3. "libfdk_aac", aac默認(rèn)的規(guī)格是AAC LC
  4. 創(chuàng)建上下文 avcodexcontext
    設(shè)置音頻三要素
  5. avcodec_alloc_context3
    3表示第三個(gè)版本
  6. sample_fmt = av_sample_FMT_S16 aac編碼器不支持flt 32位
  7. chnnel_layout = AV_CH_LAYOUT_STEREO( 或者chanels = 2)
  8. sample_rate = 44100
  9. bit_rate = 64000; (KB 碼率)可選設(shè)置
  10. profile = FF_PROFILE_AAC_HE_V2; (只有bit_rate=0 才有用) 可選設(shè)置,設(shè)置編碼器規(guī)格
  1. 打開編碼器
  2. avcodex_opne2
    2表示第二個(gè)版本
    送數(shù)據(jù)給編碼器時(shí),編碼器內(nèi)部有一個(gè)緩沖區(qū),緩沖一部分?jǐn)?shù)據(jù)后才進(jìn)行編碼
  1. 編碼
  2. 用AVFrame包裝未編碼的數(shù)據(jù),相當(dāng)于是個(gè)輸入,用AVPacket包裝已編碼的數(shù)據(jù),相當(dāng)于是個(gè)輸出;
  3. 調(diào)用avcodec_send_frame 將avframe緩沖區(qū)的數(shù)據(jù)發(fā)送給編碼器,如果返回值大于0,就表示數(shù)據(jù)成功發(fā)送到了編碼器,接著就可以通過循環(huán)使用 avcodec_receive_packet讀取編碼好的數(shù)據(jù)到AVPacket,并寫入文件中,如果讀取的結(jié)果是AVERROR(EAGIN)或者是AVERROR_EOF,就停止讀取,如果是其他的負(fù)數(shù),就停止編碼;
  4. av_frame_alloc 堆區(qū)初始化frame
  5. 設(shè)置frame的nb_samples 單通道一個(gè)數(shù)據(jù)幀采樣數(shù) 512
  1. format 每個(gè)采樣的大小 av_sample_fmt_s16
  2. channel_layout 聲道 av_ch_layout_stereo
  3. av_frame_get_buffer 分配frame里面buffer的大小
  4. 還要判斷frame的buffer是否分配成功
  5. 將重采樣后的數(shù)據(jù)memcpy到frame->data中
  6. 再將frame中的數(shù)據(jù)塞到編碼器上下文中 avcodec_send_frame,該函數(shù)會(huì)返回一個(gè)int , 當(dāng)結(jié)果>=0的時(shí)候表明有數(shù)據(jù)已經(jīng)在編碼緩沖區(qū)了;
  7. avcodec_receive_packet 讀取編碼好的數(shù)據(jù) avpacket
  8. av_packet_alloc 分配編碼后的數(shù)據(jù)空間
  9. 因?yàn)榫幋a器上下文中有一個(gè)緩沖區(qū),其中會(huì)緩存多個(gè)frame,因此并不是每塞一個(gè)frame就會(huì)有一個(gè)packet出來,所以需要通過一個(gè)while循環(huán)判斷編碼器的數(shù)據(jù)是否>=0,再通過avcodec_receive_packet獲取packet,該函數(shù)也會(huì)返回一個(gè)int,如果返回值>=0表明獲取成功,如果失敗直接退出編碼,這個(gè)值返回值還有其他含義,需要判斷eagain 表明編碼器沒有數(shù)據(jù)了或者是有數(shù)據(jù)但是不夠編碼 這個(gè)eagain需要用AVERROR包裝成一個(gè)負(fù)數(shù),表明數(shù)據(jù)還沒準(zhǔn)備好 averror_eof 表明一點(diǎn)數(shù)據(jù)都沒有了;
  10. 最后將數(shù)據(jù)編碼后的數(shù)據(jù)寫入到文件pkt->data,數(shù)據(jù)格式就是aac了;
  11. 在停止錄制的時(shí)候,由于編碼的緩存區(qū)可能還有數(shù)據(jù),在最后關(guān)閉之前,再去取一遍編碼數(shù)據(jù)放入文件;
  1. 釋放資源

在結(jié)束的時(shí)候釋放frame(av_frame_free) 和packet(av_packet_frame);文章來源地址http://www.zghlxwxcb.cn/news/detail-745411.html

編碼實(shí)戰(zhàn)代碼:

1. 創(chuàng)建fdk_aac編碼器及上下文

AVCodecContext* init_codec_context(void) {
    
    // 創(chuàng)建aac編碼器
    AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");
    
    // 初始化上下文
    AVCodecContext *context = NULL;
    context = avcodec_alloc_context3(codec);
    context->sample_fmt = AV_SAMPLE_FMT_S16;
    context->sample_rate = 44100;
    context->channel_layout = AV_CH_LAYOUT_STEREO;
    context->bit_rate = 0;
    // bitrate == 0 才會(huì)生效
    context->profile = FF_PROFILE_AAC_HE_V2;
    
    int result = avcodec_open2(context, codec, NULL);
    if (result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, result);
        av_log(NULL, AV_LOG_DEBUG, "創(chuàng)建AAC編碼器失?。?s",error);
    }
    
    return context;
    
}

2. 創(chuàng)建輸入緩沖區(qū)

AVFrame* create_audio_input_frame(void) {
    
    AVFrame *codec_frame = NULL;
    codec_frame = av_frame_alloc();
    
    codec_frame->nb_samples = 512;
    codec_frame->channel_layout = AV_CH_LAYOUT_STEREO;
    codec_frame->format = AV_SAMPLE_FMT_S16;
    int buffer_result = av_frame_get_buffer(codec_frame, 0);
    if (buffer_result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, buffer_result);
        printf("frame 緩沖區(qū)分配失敗:%s", error);
    }
    
    return codec_frame;
}

3. 開始編碼并寫入文件

void audio_encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet, FILE *fl) {
    
    
    // 將數(shù)據(jù)送入編碼器
    int codec_result = avcodec_send_frame(ctx, frame);
    while (codec_result >= 0) {
        // 從packet中循環(huán)讀取編碼好的數(shù)據(jù)
        codec_result = avcodec_receive_packet(ctx, packet);
        if (codec_result == AVERROR(EAGAIN) || codec_result == AVERROR_EOF) {
        
            break;
        } else if (codec_result < 0) {
            char error[1024];
            av_make_error_string(error, 1024, codec_result);
            printf("編碼器出錯(cuò):%s     停止編碼", error);
        } else {
            fwrite(packet->data, 1,packet->size, fl);
        }
    }
    if (codec_result < 0) {
        char error[1024];
        av_make_error_string(error, 1024, codec_result);
        printf("將數(shù)據(jù)送入編碼器錯(cuò)誤: %s\n",error);
    }
}

4. 調(diào)用

  • 先將重采樣的數(shù)據(jù)放入avframe的緩沖區(qū)中
memcpy(codec_frame->data[0], out_buffer[0], linesize_out);
  • 再開始編碼
audio_encode(codec_context, codec_frame, codec_packet, f);
  • 總覽
void get_audio_packet(AVFormatContext *context, void (*packet_callback)(AVPacket)) {
    
    // w == 寫  b == 二進(jìn)制  + == 沒有就創(chuàng)建文件
    FILE *f = fopen("/Users/cunw/Desktop/learning/音視頻學(xué)習(xí)/音視頻文件/encoder.aac", "wb+");
    
    // 創(chuàng)建編碼器上下文
    AVCodecContext *codec_context = init_codec_context();
    // 初始化輸入緩沖區(qū)  AVframe
    AVFrame *codec_frame = create_audio_input_frame();
    // 初始化編碼輸出緩沖區(qū)
    AVPacket *codec_packet = av_packet_alloc();
    
    // 初始化重采樣上下文
    SwrContext *swr_context = init_swr_context();
    // 初始化重采樣的緩沖區(qū)
    uint8_t **out_buffer = NULL;
    int linesize_out = 0;
    uint8_t **in_buffer = NULL;
    int linesize_in = 0;
    init_resammple_buffer(&in_buffer, &linesize_in, &out_buffer, &linesize_out);
    

    AVPacket *packet = av_packet_alloc();
    int result = -1;
    // 循環(huán)讀取設(shè)備信息
    
    while (isRecording == 1) {
        
        result = av_read_frame(context, packet);
        if (packet->size > 0 && result == 0) {

            packet_callback(*packet);
            // 開始轉(zhuǎn)換數(shù)據(jù)
            // 先將音頻數(shù)據(jù)拷貝到輸入緩沖區(qū)  只是重采樣音頻的話  只需要處理數(shù)組的第一個(gè)
            memcpy(in_buffer[0], packet->data, packet->size);
            // 再進(jìn)行轉(zhuǎn)換
            swr_convert(swr_context, out_buffer, 512, (const uint8_t **)in_buffer, 512);
            // 將重采樣好的數(shù)據(jù)按字節(jié)拷貝到frame緩沖區(qū)
            memcpy(codec_frame->data[0], out_buffer[0], linesize_out);
            audio_encode(codec_context, codec_frame, codec_packet, f);
            // 每讀取一次 就清空數(shù)據(jù)包 不然數(shù)據(jù)包會(huì)一直增大
            av_packet_unref(packet);
        } else if (result == -EAGAIN) {
            // result == -35 是Resource temporarily unavailable 因?yàn)樵O(shè)備未準(zhǔn)備好,還正在處理數(shù)據(jù)
            av_usleep(1);
        }
    }
    // 把緩沖區(qū)剩余的數(shù)據(jù)拿出來編碼
    audio_encode(codec_context, NULL, codec_packet, f);

    if (result != 0) {
        char errors[1024];
        av_make_error_string(errors, 1024, result);
        printf("get packet occured error is \"%s\" \n", errors);
    }

    // 釋放重采樣資源
    if (in_buffer) {
        av_freep(&in_buffer[0]);
    }
    if (out_buffer) {
        av_freep(&out_buffer[0]);
    }
    av_freep(&in_buffer);
    av_freep(&out_buffer);
    swr_free(&swr_context);
    
    av_frame_free(&codec_frame);
    av_packet_free(&codec_packet);

    // 將緩沖區(qū)剩余的數(shù)據(jù) 強(qiáng)制寫入文件
    fflush(f);
    fclose(f);

    // 釋放packet空間
    av_packet_free(&packet);
    
    
}

到了這里,關(guān)于音視頻開發(fā):音頻編碼原理+采集+編碼實(shí)戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 從原理到實(shí)踐:音視頻編碼與解碼技術(shù)解析

    從原理到實(shí)踐:音視頻編碼與解碼技術(shù)解析

    1.1 引言 音視頻編碼與解碼技術(shù)在現(xiàn)代數(shù)字媒體領(lǐng)域中扮演著至關(guān)重要的角色。隨著互聯(lián)網(wǎng)和移動(dòng)設(shè)備的快速發(fā)展,音視頻數(shù)據(jù)的傳輸和處理變得越來越普遍和重要。理解音視頻編碼與解碼的原理與實(shí)踐對(duì)于開發(fā)高質(zhì)量、高效率的音視頻應(yīng)用程序至關(guān)重要。 1.2 音視頻編碼與解

    2024年02月03日
    瀏覽(22)
  • 音視頻開發(fā) RTMP協(xié)議發(fā)送H.264編碼及AAC編碼的音視頻(C++實(shí)現(xiàn))

    音視頻開發(fā) RTMP協(xié)議發(fā)送H.264編碼及AAC編碼的音視頻(C++實(shí)現(xiàn))

    RTMP(Real Time Messaging Protocol)是專門用來傳輸音視頻數(shù)據(jù)的流媒體協(xié)議,最初由Macromedia 公司創(chuàng)建,后來歸Adobe公司所有,是一種私有協(xié)議,主要用來聯(lián)系Flash Player和RtmpServer,如 FMS , Red5 , crtmpserver 等。RTMP協(xié)議可用于實(shí)現(xiàn)直播、點(diǎn)播應(yīng)用,通過 FMLE(Flash Media Live Encoder) 推送音

    2023年04月08日
    瀏覽(28)
  • Android 音視頻開發(fā)—MediaPlayer音頻與視頻的播放介紹

    Android 音視頻開發(fā)—MediaPlayer音頻與視頻的播放介紹

    Android多媒體中的——MediaPlayer,我們可以通過這個(gè)API來播放音頻和視頻該類是Androd多媒體框架中的一個(gè)重要組件,通過該類,我們可以以最小的步驟來獲取,解碼和播放音視頻。 它支持三種不同的媒體來源: 本地資源 內(nèi)部的URI,比如你可以通過ContentResolver來獲取 外部URL(流

    2024年02月10日
    瀏覽(27)
  • 音視頻編碼實(shí)戰(zhàn)-------pcm+yuv數(shù)據(jù)轉(zhuǎn)成MP4

    音視頻編碼實(shí)戰(zhàn)-------pcm+yuv數(shù)據(jù)轉(zhuǎn)成MP4

    avcodec_find_encoder: 根據(jù)編碼器ID查找編碼器 avcodec_alloc_context3:創(chuàng)建編碼器上下文 avcodec_open2:打開編碼器 avformat_alloc_output_context2:為輸出格式創(chuàng)建復(fù)用器上下文 avformat_new_stream:創(chuàng)建音視頻流 avcodec_parameters_from_context:將編碼器上下文中的參數(shù)拷貝到音視頻流中的編碼器參數(shù)中AVCodec

    2024年02月15日
    瀏覽(32)
  • 音視頻處理 ffmpeg中級(jí)開發(fā) H264編碼

    音視頻處理 ffmpeg中級(jí)開發(fā) H264編碼

    libavcodec/avcodec.h 常用的數(shù)據(jù)結(jié)構(gòu) AVCodec 編碼器結(jié)構(gòu)體 AVCodecContext 編碼器上下文 AVFrame 解碼后的幀 結(jié)構(gòu)體內(nèi)存的分配和釋放 av_frame_alloc 申請(qǐng) av_frame_free() 釋放 avcodec_alloc_context3() 創(chuàng)建編碼器上下文 avcodec_free_context() 釋放編碼器上下文 解碼步驟 avcodec_find_decoder 查找解碼器 avcod

    2024年02月01日
    瀏覽(109)
  • Qt音視頻開發(fā)40-ffmpeg采集桌面并錄制

    之前用ffmpeg打通了各種視頻文件和視頻流以及本地?cái)z像頭設(shè)備的采集,近期有個(gè)客戶需求要求將整個(gè)桌面屏幕采集下來,并可以錄制保存成MP4文件,以前也遇到過類似的需求,由于沒有搞過,也沒有精力去摸索和測(cè)試,所以也就一直耽擱著,近期剛好這個(gè)需求又來了,定下心

    2023年04月25日
    瀏覽(21)
  • 【ESP32音視頻傳輸】②通過I2S采集SPH0645麥克風(fēng)音頻數(shù)據(jù)并上傳到服務(wù)端實(shí)時(shí)播放

    【ESP32音視頻傳輸】②通過I2S采集SPH0645麥克風(fēng)音頻數(shù)據(jù)并上傳到服務(wù)端實(shí)時(shí)播放

    提示:文章寫完后,目錄可以自動(dòng)生成,如何生成可參考右邊的幫助文檔 本文章基于Arduino ESP32 2.07版本,因?yàn)?.04版本開始I2S驅(qū)動(dòng)被更改了,所以相同代碼可能效果不太同 本文主要參考了:https://atomic14.com/2020/09/12/esp32-audio-input.html ESP32有多種方式從外置麥克風(fēng)中讀取數(shù)據(jù):

    2024年02月11日
    瀏覽(22)
  • 音視頻開發(fā)系列(10):基于qt的音頻推流

    音視頻開發(fā)系列(10):基于qt的音頻推流

    今天分享一下利用qt錄制音頻,然后再利用ffmpeg推流到nginx服務(wù)器,最后再利用vlc進(jìn)行拉流的demo。 首先介紹一下如何利用qt來進(jìn)行音頻的錄制,qt的音頻錄制主要利用qt的QAudioFormat先進(jìn)行音頻信息的配置。主要需要配置以下的信息: 然后使用QAudioDeviceInfo來獲取是否支持改設(shè)置

    2024年02月02日
    瀏覽(25)
  • 安卓音視頻開發(fā)(3)—— AudioTrack兩種方式播放pcm音頻

    前言 之前學(xué)習(xí)了AudioRecord錄制pcm音頻,與之對(duì)應(yīng)的就是AudioTrack播放pcm音頻(MediaPlayer、SoundPool有其他應(yīng)用場(chǎng)景),它有兩種數(shù)據(jù)加載模式(MODE_STATIC、MODE_STREAM)。 模式 MODE_STATIC :這種模式下,一次將所有的數(shù)據(jù)放入一個(gè)固定的buffer,然后寫入到AudioTrack中,后續(xù)就不用繼續(xù)

    2023年04月22日
    瀏覽(25)
  • RK3568平臺(tái)開發(fā)系列講解(音視頻篇)H264 的編碼結(jié)構(gòu)

    RK3568平臺(tái)開發(fā)系列講解(音視頻篇)H264 的編碼結(jié)構(gòu)

    ??返回專欄總目錄 沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!?? ??視頻編碼的碼流結(jié)構(gòu)其實(shí)就是指視頻經(jīng)過編碼之后得到的二進(jìn)制數(shù)據(jù)是怎么組織的,換句話說,就是編碼后的碼流我們?cè)趺磳⒁粠瑤幋a后的圖像數(shù)據(jù)分離出來,以及在二進(jìn)制碼流數(shù)據(jù)中,哪一塊數(shù)

    2024年02月09日
    瀏覽(92)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包