音視頻八股文(12)-- ffmpeg 音頻重采樣

1重采樣

1.1 什么是重采樣

所謂的重采樣,就是改變?頻的采樣率、sample format、聲道數(shù)等參數(shù),使之按照我們期望的參數(shù)輸出。

1.2 為什么要重采樣

為什么要重采樣?當(dāng)然是原有的?頻參數(shù)不滿?我們的需求,?如在FFmpeg解碼?頻的時(shí)候,不同的?源有不同的格式,采樣率等,在解碼后的數(shù)據(jù)中的這些參數(shù)也會(huì)不?致(最新FFmpeg 解碼?頻后,?頻格
式為AV_SAMPLE_FMT_FLTP,這個(gè)參數(shù)應(yīng)該是?致的),如果我們接下來需要使?解碼后的?頻數(shù)據(jù)做其他操作,?這些參數(shù)的不?致導(dǎo)致會(huì)有很多額外?作,此時(shí)直接對其進(jìn)?重采樣,獲取我們制定的?頻參數(shù),這樣就會(huì)?便很多。

再?如在將?頻進(jìn)?SDL播放時(shí)候,因?yàn)楫?dāng)前的SDL2.0不?持planar格式,也不?持浮點(diǎn)型的,?最新的FFMPEG 16年會(huì)將?頻解碼為AV_SAMPLE_FMT_FLTP格式,因此此時(shí)就需要我們對其重采樣,使之可以在SDL2.0上進(jìn)?播放。

2 對應(yīng)參數(shù)解析

2.1 采樣率

采樣設(shè)備每秒抽取樣本的次數(shù)

2.2采樣格式及量化精度(位寬)

每種?頻格式有不同的量化精度(位寬),位數(shù)越多,表示值就越精確,聲?表現(xiàn)?然就越精準(zhǔn)。FFMpeg中?頻格式有以下?種,每種格式有其占?的字節(jié)數(shù)信息(libavutil/samplefmt.h):

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

2.3 分?(plane)和打包(packed)

以雙聲道為例,帶P(plane)的數(shù)據(jù)格式在存儲(chǔ)時(shí),其左聲道和右聲道的數(shù)據(jù)是分開存儲(chǔ)的,左聲道的數(shù)據(jù)存儲(chǔ)在data[0],右聲道的數(shù)據(jù)存儲(chǔ)在data[1],每個(gè)聲道的所占?的字節(jié)數(shù)為linesize[0]和linesize[1];

不帶P(packed)的?頻數(shù)據(jù)在存儲(chǔ)時(shí),是按照LRLRLR...的格式交替存儲(chǔ)在data[0]中,linesize[0]表示總的數(shù)據(jù)量。

2.4 聲道分布(channel_layout)

聲道分布在FFmpeg\libavutil\channel_layout.h中有定義,?般來說?的?較多的是AV_CH_LAYOUT_STEREO(雙聲道)和AV_CH_LAYOUT_SURROUND(三聲道),這兩者的定義如下:

#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO | AV_CH_FRONT_CENTER)

2.5 ?頻幀的數(shù)據(jù)量計(jì)算

?幀?頻的數(shù)據(jù)量(字節(jié))=channel數(shù) * nb_samples樣本數(shù) * 每個(gè)樣本占?的字節(jié)數(shù)

如果該?頻幀是FLTP格式的PCM數(shù)據(jù),包含1024個(gè)樣本,雙聲道,那么該?頻幀包含的?頻數(shù)據(jù)量是210244=8192字節(jié)。

AV_SAMPLE_FMT_DBL : 210248 = 16384

2.6 ?頻播放時(shí)間計(jì)算

以采樣率44100Hz來計(jì)算,每秒44100個(gè)sample,?正常?幀為1024個(gè)sample,可知每幀播放時(shí)間/1024=1000ms/44100,得到每幀播放時(shí)間=1024*1000/44100=23.2ms (更精確的是23.21995464852608)。

?幀播放時(shí)間(毫秒) = nb_samples樣本數(shù) *1000/采樣率 =

(1)1024*1000/44100=23.21995464852608ms ->約等于 23.2ms,精度損失了0.011995464852608ms,如果累計(jì)10萬幀,誤差>1199毫秒,如果有視頻?起的就會(huì)有?視頻同步的問題。 如果按著23.2去計(jì)算pts(0 23.2 46.4 )就會(huì)有累積誤差。

(2)1024*1000/48000=21.33333333333333ms

3 FFmpeg重采樣API

分配?頻重采樣的上下?

struct SwrContext *swr_alloc(void);

當(dāng)設(shè)置好相關(guān)的參數(shù)后,使?此函數(shù)來初始化SwrContext結(jié)構(gòu)體

int swr_init(struct SwrContext *s);

分配SwrContext并設(shè)置/重置常?的參數(shù)。

struct SwrContext* swr_alloc_set_opts(struct SwrContext* s, // ?頻重采樣上下?
    int64_t out_ch_layout, // 輸出的layout, 如:5.1聲道
    enum AVSampleFormat out_sample_fmt, // 輸出的采樣格式。Float, S16,?般選?是s16 絕?部分聲卡?持
    int out_sample_rate, //輸出采樣率
    int64_t in_ch_layout, // 輸?的layout
    enum AVSampleFormat in_sample_fmt, // 輸?的采樣格式
    int in_sample_rate, // 輸?的采樣率
    int log_offset, // ?志相關(guān),不?管先,直接為0
    void* log_ctx // ?志相關(guān),不?管先,直接為NULL
);

將輸?的?頻按照定義的參數(shù)進(jìn)?轉(zhuǎn)換并輸出

int swr_convert(struct SwrContext* s, // ?頻重采樣的上下?
    uint8_t** out, // 輸出的指針。傳遞的輸出的數(shù)組
    int out_count, //輸出的樣本數(shù)量,不是字節(jié)數(shù)。單通道的樣本數(shù)量。
    const uint8_t** in, //輸?的數(shù)組,AVFrame解碼出來的DATA
    int in_count // 輸?的單通道的樣本數(shù)量。
);

in和in_count可以設(shè)置為0,以最后刷新最后?個(gè)樣本。

釋放掉SwrContext結(jié)構(gòu)體并將此結(jié)構(gòu)體置為NULL;

void swr_free(struct SwrContext **s);

?頻重采樣,采樣格式轉(zhuǎn)換和混合庫。與lswr的交互是通過SwrContext完成的,SwrContext被分配給swr_alloc()或
swr_alloc_set_opts()。 它是不透明的,所以所有參數(shù)必須使?AVOptions API設(shè)置。為了使?lswr,你需要做的第?件事就是分配SwrContext。 這可以使?swr_alloc()或 swr_alloc_set_opts()來完成。 如果您使?前者,則必須通過AVOptions API設(shè)置選項(xiàng)。 后?個(gè)函數(shù)提供了相同的功能,但它允許您在同?語句中設(shè)置?些常?選項(xiàng)。

例如,以下代碼將設(shè)置從平?浮動(dòng)樣本格式到交織的帶符號16位整數(shù)的轉(zhuǎn)換,從48kHz到44.1kHz的下采
樣,以及從5.1聲道到?體聲的下混合(使?默認(rèn)混合矩陣)。 這是使?swr_alloc()函數(shù)。

SwrContext * swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0);
av_opt_set_channel_layout(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
av_opt_set_int(swr, "in_sample_rate", 48000, 0);
av_opt_set_int(swr, "out_sample_rate", 44100, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);

同樣的?作也可以使?swr_alloc_set_opts():

SwrContext * swr = swr_alloc_set_opts(NULL, // we're allocating a new context
    AV_CH_LAYOUT_STEREO, // out_ch_layout
    AV_SAMPLE_FMT_S16, // out_sample_fmt
    44100, // out_sample_rate
    AV_CH_LAYOUT_5POINT1, // in_ch_layout
    AV_SAMPLE_FMT_FLTP, // in_sample_fmt
    48000, // in_sample_rate
    0, // log_offset
    NULL); // log_ctx

?旦設(shè)置了所有值,它必須?swr_init()初始化。 如果需要更改轉(zhuǎn)換參數(shù),可以使?AVOptions來更改參數(shù),如上?第?個(gè)例?所述; 或者使?swr_alloc_set_opts(),但是第?個(gè)參數(shù)是分配的上下?。 您必須再次調(diào)?swr_init()。?旦設(shè)置了所有值,它必須?swr_init()初始化。 如果需要更改轉(zhuǎn)換參數(shù),可以使?AVOptions來更改參數(shù),如上?第?個(gè)例?所述; 或者使?swr_alloc_set_opts(),但是第?個(gè)參數(shù)是分配的上下?。 您必須再次調(diào)?swr_init()。
轉(zhuǎn)換本身通過重復(fù)調(diào)?swr_convert()來完成。 請注意,如果提供的輸出空間不?或采樣率轉(zhuǎn)換完成后,樣本可能會(huì)在swr中緩沖,這需要“未來”樣本。 可以隨時(shí)通過使?swr_convert()(in_count可以設(shè)置為0)來檢索不需要將來輸?的樣本。 在轉(zhuǎn)換結(jié)束時(shí),可以通過調(diào)?具有NULL in和in incount的swr_convert()來刷新重采樣緩沖區(qū)。

4 go代碼

moonfdd/ffmpeg-go

[圖片上傳失敗...(image-b245ed-1683900615599)]

博客園驗(yàn)證

var code = "8f40c43c-7877-4744-93de-3f49e5d51a11"
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容