日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

FFmpeg学习4:音频格式转换

發布時間:2023/11/27 生活经验 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FFmpeg学习4:音频格式转换 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前段時間,在學習試用FFmpeg播放音頻的時候總是有雜音,網上的很多教程是基于之前版本的FFmpeg的,而新的FFmepg3中audio增加了平面(planar)格式,而SDL播放音頻是不支持平面格式的,所以通過FFmpeg解碼出來的數據不能直接發送到SDL進行播放,需要進行一個格式轉換。通過網上一些資料,也能夠正確的播放音頻了,但是對具體的音頻轉換過程不是很了解,這里就對FFmpeg的對音頻的存儲格式及格式轉換做個總結。本文主要有以下幾個方面的內容:

  • AVSampleFormat 音頻sample的存儲格式
  • channel layout 各個通道存儲順序
  • 使用FFmpeg對音頻數據進行格式轉換
  • 音頻解碼API avcodec_decode_audio4在新版中已廢棄,替換為使用更為簡單的avcodec_send_packetavcodec_receive_frame。本文簡單的介紹了該API的使用。

    AVSampleFormat

在FFmpeg中使用枚舉AVSampleFormat表示音頻的采樣格式,其聲明如下:

enum AVSampleFormat {AV_SAMPLE_FMT_NONE = -1,AV_SAMPLE_FMT_U8,          ///< unsigned 8 bitsAV_SAMPLE_FMT_S16,         ///< signed 16 bitsAV_SAMPLE_FMT_S32,         ///< signed 32 bitsAV_SAMPLE_FMT_FLT,         ///< floatAV_SAMPLE_FMT_DBL,         ///< doubleAV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planarAV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planarAV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planarAV_SAMPLE_FMT_FLTP,        ///< float, planarAV_SAMPLE_FMT_DBLP,        ///< double, planarAV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

和圖像的像素存儲格式類似,可以使用8位無符號整數、16位有符號整數、32位有符號整數以及單精度浮點數,雙精度浮點數表示一個采樣。但是,沒有使用
24位的有符號整數,這是因為這些不同的格式使用的是原生的C類型,而C中是沒有24位的長度的類型的。

Sample value can be expressed by native C types,hence the lack of a signed 24-bit sample format even though
it is a common raw audio data format.

對于浮點格式,其值在[-1.0,1.0]之間,任何在該區間之外的值都超過了最大音量的范圍。
和YUV的圖像格式格式,音頻的采樣格式分為平面(planar)和打包(packed)兩種類型,在枚舉值中上半部分是packed類型,后面(有P后綴的)是planar類型。
對于planar格式的,每一個通道的值都有一個單獨的plane,所有的plane必須有相同的大小;對于packed類型,所有的數據在同一個數據平面中,不同通道的數據
交叉保存。
另外,在AVFrame中表示音頻采樣格式的字段format是一個int型,在使用AVSampleFormat時候需要進行一個類型轉換,將int轉換為AVSampleFormat枚舉值。

在頭文件samplefmt.h提供了和音頻采樣格式相關的一些函數,現列舉一些如下:

  • const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt)
    根據枚舉值獲取其相應的格式名稱(字符串)
  • enum AVSampleFormat av_get_sample_fmt(const char *name)
    根據格式名字(字符串)獲取相應的枚舉值
  • enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt)
    傳入planar類型的采樣格式,返回其可轉換的packed類型的采樣格式。例如傳入 AV_SAMPLE_FMT_S32P,其返回值為 AV_SAMPLE_FMT_S32
  • enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt)
    和上面函數類似,不同的是傳入的是packed類型的格式。
  • int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt
    判斷一個采樣格式是不是planar類型的
  • int av_get_bytes_per_sample(enum AVSampleFormat sample_fmt)
    每個采樣值所占用的字節數
  • int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,enum AVSampleFormat sample_fmt, int align)
    根據輸入的參數,計算其所占用空間的大小(字節數)。linesize可設為null,align是buff空間的對齊格式(0=default,1 = no alignment)

channel_layout

從上面可知,sample有兩種類型的存儲方式:平面(planar)和打包(packed),在planar中每一個通道獨自占用一個存儲平面;在packed中,所有通道的sample交織存儲在同一個
平面。但是,對于planar格式不知道具體的某一通道所在的平面;對于packed格式各個通道的數據是以怎么樣的順序交織存儲的。這就需要借助于channel_layout。
首先來看下FFmpeg對channel_layout的定義:
channel_layout是一個64位整數,每個值為1的位對應一個通道。也就說,channel_layout的位模式中值為1的個數等于其通道數量。

A channel_layout is a 64-bits interget with a bit set for every channel.The number of bits set must be equal to the number of channels.

在頭文件channel_layout.h中為將每個通道定義了一個mask,其定義如下:

#define AV_CH_FRONT_LEFT             0x00000001
#define AV_CH_FRONT_RIGHT            0x00000002
#define AV_CH_FRONT_CENTER           0x00000004
#define AV_CH_LOW_FREQUENCY          0x00000008
#define AV_CH_BACK_LEFT              0x00000010
#define AV_CH_BACK_RIGHT             0x00000020
#define AV_CH_FRONT_LEFT_OF_CENTER   0x00000040
#define AV_CH_FRONT_RIGHT_OF_CENTER  0x00000080
#define AV_CH_BACK_CENTER            0x00000100
#define AV_CH_SIDE_LEFT              0x00000200
#define AV_CH_SIDE_RIGHT             0x00000400
#define AV_CH_TOP_CENTER             0x00000800
#define AV_CH_TOP_FRONT_LEFT         0x00001000
#define AV_CH_TOP_FRONT_CENTER       0x00002000
#define AV_CH_TOP_FRONT_RIGHT        0x00004000
#define AV_CH_TOP_BACK_LEFT          0x00008000
#define AV_CH_TOP_BACK_CENTER        0x00010000
#define AV_CH_TOP_BACK_RIGHT         0x00020000
#define AV_CH_STEREO_LEFT            0x20000000  ///< Stereo downmix.
#define AV_CH_STEREO_RIGHT           0x40000000  ///< See AV_CH_STEREO_LEFT.

這樣,一個channel_layout就是上述channel mask的組合,部分定義如下:

#define AV_CH_LAYOUT_MONO              (AV_CH_FRONT_CENTER)
#define AV_CH_LAYOUT_STEREO            (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_LAYOUT_2POINT1           (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_2_1               (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_SURROUND          (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)
#define AV_CH_LAYOUT_3POINT1           (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_4POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)
#define AV_CH_LAYOUT_4POINT1           (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)
#define AV_CH_LAYOUT_2_2               (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
#define AV_CH_LAYOUT_QUAD              (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)
#define AV_CH_LAYOUT_5POINT0           (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)
#define AV_CH_LAYOUT_5POINT1           (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)
...

AV_CH_LAYOUT_STEREO是立體聲(2通道),其通道的存放順序為LEFT | RIGHTAV_CH_LAYOUT_4POINT0是4通道,其通道的存放順序為
LEFT|RIGHT|FRONT-CENTER|BACK-CENTER;其它數量的聲道與此類似。
下面列舉一些和channel_layout相關的函數

  • uint64_t av_get_channel_layout(const char *name) 根據傳入的字符串,返回相對應的channel_layout。傳入的參數可以是:
    • 常用的channel layout的名稱:mono,stereo,4.0,quad,5.0,5.0(side),5.1等。
    • 一個單通道的名稱:FL,FR,FC,BL,BR,FLC,FRC等
    • 通道的數量
    • channel_layout mask,以"0x"開頭的十六進制串。
      更多詳細的說明,參見該函數的文檔。
  • int av_get_channel_layout_nb_channels(uint64_t channel_layout) 根據通道的layout返回通道的個數
  • int64_t av_get_default_channel_layout(int nb_channels) 根據通道的個數返回默認的layout
  • int av_get_channel_layout_channel_index(uint64_t channel_layout,uint64_t channel); 返回通道在layout中的index,也就是某一通道
    在layout的存儲位置。
    av_get_channel_layout_channel_index的實現如下:
int av_get_channel_layout_channel_index(uint64_t channel_layout,uint64_t channel)
{if (!(channel_layout & channel) ||av_get_channel_layout_nb_channels(channel) != 1)return AVERROR(EINVAL);channel_layout &= channel - 1;return av_get_channel_layout_nb_channels(channel_layout);
}

首先判斷傳入的layout包含該通道,并且保證該傳入的通道是一個單通道。
以4通道AV_CH_LAYOUT_4POINT0為例,說明下計算方法。AV_CH_LAYOUT_4POINT0 = AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT | AV_CH_FRONT_CENTER | AV_CH_BACK_CENTER
其二進制表示為0001,0000,0111,假如想找AV_CH_BACK_CENTER在該layout中的index。AV_CH_BACK_CENTER的十六進制為0x0100,二進制為0001,0000,0000,那么
AV_CH_BACK_CENTER - 1 = 1111,11110001,0000,0111 & 0000,1111,1111 = 0111,函數av_get_channel_layout_nb_channels是獲取某個layout對應的通道的數量,
前面提到,layout中值為1的位的個數和通道的數量相等,所以AV_CH_BACK_CENTER在layoutAV_CH_LAYOUT_4POINT0的index為3。

Audio 格式轉換

在FFmpeg中進行音頻的格式轉換主要有三個步驟

  1. 實例化SwrContext,并設置轉換所需的參數:通道數量、channel layout、sample rate

有以下兩種方式來實例SwrContext,并設置參數:

  • 使用swr_alloc
 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 contextAV_CH_LAYOUT_STEREO,  // out_ch_layoutAV_SAMPLE_FMT_S16,    // out_sample_fmt44100,                // out_sample_rateAV_CH_LAYOUT_5POINT1, // in_ch_layoutAV_SAMPLE_FMT_FLTP,   // in_sample_fmt48000,                // in_sample_rate0,                    // log_offsetNULL);                // log_ctx

上述兩種方法設置那個的參數是將5.1聲道,channel layout為AV_CH_LAYOUT_5POINT1,采樣率為48KHz轉換為2聲道,channel_layout為AV_SAMPLE_FMT_S16,采樣率為44.1KHz。

  1. 計算轉換后的sample個數
    轉后后的sample個數的計算公式為:src_nb_samples * dst_sample_rate / src_sample_rate,其計算如下:
int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, frame->sample_rate) + frame->nb_samples, frame->sample_rate, frame->sample_rate, AVRounding(1));

函數av_rescale_rnd是按照指定的舍入方式計算a * b / c 。
函數swr_get_delay得到輸入sample和輸出sample之間的延遲,并且其返回值的根據傳入的第二個參數不同而不同。如果是輸入的采樣率,則返回值是輸入sample個數;如果輸入的是輸出采樣率,則返回值是輸出sample個數。

  1. 調用 swr_convert進行轉換
int nb = swr_convert(swr_ctx, &audio_buf, dst_nb_samples, (const uint8_t**)frame->data, frame->nb_samples);

其返回值為轉換的sample個數。

SDL播放音頻時的格式轉換

  • 首先使用avcodec_send_packetavcodec_receive_frame獲取解碼后的原始數據
    int ret = avcodec_send_packet(aCodecCtx, &pkt);if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)return -1;ret = avcodec_receive_frame(aCodecCtx, frame);if (ret < 0 && ret != AVERROR_EOF)return -1;

這里不再使用avcodec_decode_audio4進行音頻的解碼,在FFmpeg3中該函數已被廢棄,使用avcodec_send_packetavcodec_receive_frame替代。新的解碼API使用更為方便,
具體參見官方文檔send/receive encoding and decoding API overview。

  • 設置通道數量和channel layout
    在編碼的時候有可能丟失通道數量或者channel layout ,這里根據獲取的參數設置其默認值
    if (frame->channels > 0 && frame->channel_layout == 0)frame->channel_layout = av_get_default_channel_layout(frame->channels);else if (frame->channels == 0 && frame->channel_layout > 0)frame->channels = av_get_channel_layout_nb_channels(frame->channel_layout);

如果channel layout未知(channel_layout = 0),根據通道數量獲取其默認的channel layout;如同通道的數量未知,則根據其channel layout得到其通道數量。

  • 設置輸出格式
    由于SDL2的sample格式不支持浮點型(FFmpeg中是支持的浮點型的),這里簡單的設置輸出格式為AV_SAMPLE_FMT_S16(16位有符號整型),輸出的channel layout也
    根據通道數量設置為默認值 dst_layout = av_get_default_channel_layout(frame->channels)(SDL2不支持planar格式)。實例化SwrContext
    swr_ctx = swr_alloc_set_opts(nullptr, dst_layout, dst_format, frame->sample_rate,frame->channel_layout, (AVSampleFormat)frame->format, frame->sample_rate, 0, nullptr);if (!swr_ctx || swr_init(swr_ctx) < 0)return -1;

在設置完參數后,一定要調用swr_init進行初始化。

  • 轉換
    // 計算轉換后的sample個數 a * b / cint dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, frame->sample_rate) + frame->nb_samples, frame->sample_rate, frame->sample_rate, AVRounding(1));// 轉換,返回值為轉換后的sample個數int nb = swr_convert(swr_ctx, &audio_buf, dst_nb_samples, (const uint8_t**)frame->data, frame->nb_samples);data_size = frame->channels * nb * av_get_bytes_per_sample(dst_format);

最后data_size中保存的是轉換的數據的字節數:通道數 * sample個數 * 每個sample的字節數。

總結

本文主要介紹了在FFmepg中對音頻兩個重要屬性:采樣格式和channel layout的表示方法,并簡單的實現了一個音頻的格式轉換。

  • 采樣格式 使用AVSampleFormat枚舉值表示,并可分為planar和packed兩類。
  • channel layout 是一個64位的整數,表示各個通道數據的存放順序,其二進制位中1的個數等于其通道的數量。

本文代碼 FFmpeg-playAudio.cpp

轉載于:https://www.cnblogs.com/wangguchangqing/p/5851490.html

總結

以上是生活随笔為你收集整理的FFmpeg学习4:音频格式转换的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。