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

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

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

一、解碼流程

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

1.1、解析音頻信息

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

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

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

1.3、從frame到PCM byte

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

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

data[0]:LLLLLLLLLLLLLLLL

data[1]:RRRRRRRRRRRRR

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

data[0]:LRLRLRLRLRLRLRLR

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

二、分貝計算

我們音頻的分貝往往不需要計算每一個樣本的分貝數(shù),第一計算密度太大超出人眼感知對顯示沒有益處,二是計算量太大會導致我們的計算時間大大延長。因為聲音具有一定的延續(xù)性,所以我們可以計算一個時間段內的平均值來獲得一段音頻范圍的分布值,這樣既減小了工作量又達到了合理可視化的效果。首先是獲取平均值,假設我們每秒想獲取10個分貝值,那么我們需要計算采樣率通道數(shù)采樣位數(shù)/8/10個字節(jié)數(shù)據的平均值,我們不妨自己把它叫dB采樣區(qū)間樣本數(shù),一個16bit位深的音頻每兩個字節(jié)組成一個樣本,將區(qū)間內樣本相加再除以樣本數(shù)取平均值即可。接下來就是dB的計算,dB其實并不特指分貝,它只是在音頻描述領域。它描述的是音頻的增益關系,如果想詳細了解db是什么可以自行百度相關的知識。分貝的計算公式是

20*log10(value)

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

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個字節(jié)的大?。ㄖ担?
        sum += abs(value); //絕對值求和
    }
    sum = sum / (size / (bit_format/8)); //求平均值(2個字節(jié)表示一個振幅,所以振幅個數(shù)為:size/2個)
    if(sum > 0)
    {
        db = (int)(20.0*log10(sum));
    }
    memcpy(wave_buffer+wave_index,(char*)&db,1);
    wave_index++;
}

需要注意的是我們在解碼時ffmpeg的音頻格式類型除了packet和planar兩個大類外,對于32位的音頻又區(qū)分了32位整形和32位浮點型。

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
};

浮點型的取值范圍在-1到1的區(qū)間,所以我們在計算時需要乘以0x7fff來獲得和16位同比例的數(shù)據,達到同樣的顯示效果。

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個字節(jié)的大?。ㄖ担?
        sum += abs(value*0x7fff); //絕對值求和
    }
    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++;
}

三、實現(xiàn)效果

以上就是FFmpeg 音頻可視化解碼流程詳解的詳細內容,更多關于FFmpeg 音頻可視化解碼的資料請關注腳本之家其它相關文章!

相關文章

最新評論