欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

FFmpeg?音頻可視化解碼流程詳解

 更新時(shí)間:2022年10月09日 08:38:02   作者:Mystatic  
這篇文章主要為大家介紹了FFmpeg?音頻可視化解碼流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、解碼流程

解碼流程大致分為以下三個(gè)部分,以FFmpge源碼下的ffmpeg\doc\examples\decode_audio.c為參考。

1.1、解析音頻信息

avformat_open_input負(fù)責(zé)打開需要解碼的音頻文件,如果文件打開成功的話會(huì)初始化AVFormatContext,avformat_find_stream_info開啟音頻流遍歷,av_find_best_stream找到最合適解析數(shù)據(jù)的幀,解析完后我們可以通過返回的AVStream獲取到我們需要用的解碼器id、通道數(shù)、采樣率、位深、音頻時(shí)長(zhǎng)、數(shù)據(jù)排列結(jié)構(gòu)。拿到解碼器id我們通過解碼器id獲取解碼器avcodec_find_decoder,有些解碼器并不是FFmpeg內(nèi)置的,所以有些需要在編譯時(shí)就加進(jìn)去,我之前的文章也有講過AAC和MP3編解碼第三方庫。如果找到了解碼器,下一步就是avcodec_alloc_context3對(duì)解碼器上下文AVCodecContext進(jìn)行初始化,初始化完成后avcodec_parameters_to_context將解碼器參數(shù)設(shè)置給解碼器上下文,例如通道數(shù),采樣率,采樣位深等等信息。如果未設(shè)置可能會(huì)出現(xiàn)invalid argument的錯(cuò)誤,導(dǎo)致后續(xù)無法繼續(xù)。最后通過avcodec_open2打開解碼器,如果打開成功我們就可以開始對(duì)音頻數(shù)據(jù)進(jìn)行讀取。

1.2、從原始數(shù)據(jù)packet到frame

我們解碼的目的就是為了拿到底層播放器能播的PCM數(shù)據(jù)。既然我們已經(jīng)獲取到了解碼器,那么下面就是一幀一幀的讀取解碼器解析出來的數(shù)據(jù)。首先我們需要av_packet_alloc初始化包對(duì)象AVPacket,包對(duì)象是未解碼的數(shù)據(jù),原始的音頻數(shù)據(jù)被打包成一個(gè)一個(gè)的包,然后送給解碼器去把包打開,變成幀對(duì)象,所以我們又需要通過av_frame_alloc初始化幀對(duì)象AVFrame,把它送給解碼器,解碼器用數(shù)據(jù)把它裝滿后返回回來。av_read_frame就是從打開的文件讀取一個(gè)數(shù)據(jù)包,對(duì)于AAC/MP3來說他們是未解碼的壓縮數(shù)據(jù)。然后通過avcodec_send_packet把數(shù)據(jù)包送給解碼器,返回0表示解碼器解包成功,接下來就可以從解碼器讀數(shù)據(jù),這時(shí)的數(shù)據(jù)就是以幀的形式存在,avcodec_receive_frame讀取幀,因?yàn)橐粋€(gè)包可能有幾個(gè)幀,所以需要循環(huán)讀取,當(dāng)avcodec_receive_frame返回0時(shí)表示讀取成功,可以進(jìn)行下一步操作,當(dāng)返回值是AVERROR_EOF表示讀取完畢可以跳出循環(huán)了,返回AVERROR(EAGAIN)表示解碼器輸出已經(jīng)是不可用的狀態(tài),必須向解碼器送新包來激活輸出,同樣也可以跳出讀取和解析幀的循環(huán)。

1.3、從frame到PCM byte

我們的PCM數(shù)據(jù)就在frame的data里,但是我們并不能直接拿,首先我們得知道拿多少,怎么拿。拿多少取決于采樣位數(shù),通道數(shù)和幀里面的樣本數(shù)。例如44100HZ的話一秒就有44100通道數(shù)個(gè)樣本。那一個(gè)幀里面就一共有 采樣位數(shù)/8通道數(shù)*樣本數(shù)個(gè)字節(jié)數(shù)據(jù)。怎么拿取決于音頻數(shù)據(jù)的存儲(chǔ)方式,音頻存儲(chǔ)方式有兩種:

  • planar:音頻左右聲道數(shù)據(jù)分開放置,數(shù)據(jù)存儲(chǔ)格式為

data[0]:LLLLLLLLLLLLLLLL

data[1]:RRRRRRRRRRRRR

  • packet:音頻左右聲道數(shù)據(jù)交替放置,數(shù)據(jù)存儲(chǔ)格式為

data[0]:LRLRLRLRLRLRLRLR

最終拿到的數(shù)據(jù)都是以LRLRLRLRLR的方式排列,到這里我們可以把它送給播放器或者在送給播放器前加一些我們自己的音頻算法,全部解碼完成后,最后記得釋放掉相關(guān)資源。在這里我們簡(jiǎn)單點(diǎn),計(jì)算它的分貝,實(shí)現(xiàn)音頻可視化的功能。

二、分貝計(jì)算

我們音頻的分貝往往不需要計(jì)算每一個(gè)樣本的分貝數(shù),第一計(jì)算密度太大超出人眼感知對(duì)顯示沒有益處,二是計(jì)算量太大會(huì)導(dǎo)致我們的計(jì)算時(shí)間大大延長(zhǎng)。因?yàn)槁曇艟哂幸欢ǖ难永m(xù)性,所以我們可以計(jì)算一個(gè)時(shí)間段內(nèi)的平均值來獲得一段音頻范圍的分布值,這樣既減小了工作量又達(dá)到了合理可視化的效果。首先是獲取平均值,假設(shè)我們每秒想獲取10個(gè)分貝值,那么我們需要計(jì)算采樣率通道數(shù)采樣位數(shù)/8/10個(gè)字節(jié)數(shù)據(jù)的平均值,我們不妨自己把它叫dB采樣區(qū)間樣本數(shù),一個(gè)16bit位深的音頻每?jī)蓚€(gè)字節(jié)組成一個(gè)樣本,將區(qū)間內(nèi)樣本相加再除以樣本數(shù)取平均值即可。接下來就是dB的計(jì)算,dB其實(shí)并不特指分貝,它只是在音頻描述領(lǐng)域。它描述的是音頻的增益關(guān)系,如果想詳細(xì)了解db是什么可以自行百度相關(guān)的知識(shí)。分貝的計(jì)算公式是

20*log10(value)

所以聲音的分貝描述的并不是線性關(guān)系而是指數(shù)關(guān)系,例如70db比50db的聲音大了20倍,例如16bit可以描述的音頻范圍為0-65535那么它的最大dB值在96.3左右,32bit可以描述音頻范圍在0-4294967296,那么它的最大dB值在192.6。把我們剛才計(jì)算的平均值帶入value就能獲得我們的區(qū)間的分貝,把它存起來解析完一起返回或者逐個(gè)回調(diào)都可以,看你的業(yè)務(wù)需求。下面是計(jì)算16bit采樣位數(shù)的分貝的方法,32bit的處理方法類似,主要注意值的大小,和每次位移的byte步長(zhǎng)。拿到了了分貝我們就可以將它們變成條變成塊的繪制到屏幕了。

void getPcmDB16(const unsigned char *pcmdata, size_t size) {
    int db = 0;
    short int value = 0;
    double sum = 0;
    for(int i = 0; i < size; i += bit_format/8)
    {
        memcpy(&value, pcmdata+i, bit_format/8); //獲取2個(gè)字節(jié)的大?。ㄖ担?
        sum += abs(value); //絕對(duì)值求和
    }
    sum = sum / (size / (bit_format/8)); //求平均值(2個(gè)字節(jié)表示一個(gè)振幅,所以振幅個(gè)數(shù)為:size/2個(gè))
    if(sum > 0)
    {
        db = (int)(20.0*log10(sum));
    }
    memcpy(wave_buffer+wave_index,(char*)&db,1);
    wave_index++;
}

需要注意的是我們?cè)诮獯a時(shí)ffmpeg的音頻格式類型除了packet和planar兩個(gè)大類外,對(duì)于32位的音頻又區(qū)分了32位整形和32位浮點(diǎn)型。

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double
    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar
    AV_SAMPLE_FMT_S64,         ///< signed 64 bits
    AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar
    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

浮點(diǎn)型的取值范圍在-1到1的區(qū)間,所以我們?cè)谟?jì)算時(shí)需要乘以0x7fff來獲得和16位同比例的數(shù)據(jù),達(dá)到同樣的顯示效果。

void getPcmDBFloat(const unsigned char *pcmdata, size_t size) {
    int db = 0;
    float value = 0;
    double sum = 0;
    for(int i = 0; i < size; i += bit_format/8)
    {
        memcpy(&value, pcmdata+i, bit_format/8); //獲取4個(gè)字節(jié)的大?。ㄖ担?
        sum += abs(value*0x7fff); //絕對(duì)值求和
    }
    sum = sum / (size / (bit_format/8)); 
    if(sum > 0)
    {
        db = (int)(20.0*log10(sum));
    }
    memcpy(wave_buffer+wave_index,(char*)&db,1);
    wave_index++;
}

三、實(shí)現(xiàn)效果

以上就是FFmpeg 音頻可視化解碼流程詳解的詳細(xì)內(nèi)容,更多關(guān)于FFmpeg 音頻可視化解碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論