音视频八股文(11)--ffmpeg音频重采样

博客 分享
0 251
优雅殿下
优雅殿下 2023-05-11 21:54:59
悬赏:0 积分 收藏

音视频八股文(11)-- ffmpeg 音频重采样

1重采样

1.1 什么是重采样

所谓的重采样,就是改变?频的采样率、sample format、声道数等参数,使之按照我们期望的参数输出。

1.2 为什么要重采样

为什么要重采样?当然是原有的?频参数不满?我们的需求,?如在FFmpeg解码?频的时候,不同的?源有不同的格式,采样率等,在解码后的数据中的这些参数也会不?致(最新FFmpeg 解码?频后,?频格
式为AV_SAMPLE_FMT_FLTP,这个参数应该是?致的),如果我们接下来需要使?解码后的?频数据做其他操作,?这些参数的不?致导致会有很多额外?作,此时直接对其进?重采样,获取我们制定的?频参数,这样就会?便很多。

再?如在将?频进?SDL播放时候,因为当前的SDL2.0不?持planar格式,也不?持浮点型的,?最新的FFMPEG 16年会将?频解码为AV_SAMPLE_FMT_FLTP格式,因此此时就需要我们对其重采样,使之可以在SDL2.0上进?播放。

2 对应参数解析

2.1 采样率

采样设备每秒抽取样本的次数

2.2采样格式及量化精度(位宽)

每种?频格式有不同的量化精度(位宽),位数越多,表示值就越精确,声?表现?然就越精准。FFMpeg中?频格式有以下?种,每种格式有其占?的字节数信息(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)的数据格式在存储时,其左声道和右声道的数据是分开存储的,左声道的数据存储在data[0],右声道的数据存储在data[1],每个声道的所占?的字节数为linesize[0]和linesize[1];

不带P(packed)的?频数据在存储时,是按照LRLRLR...的格式交替存储在data[0]中,linesize[0]表示总的数据量。

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 ?频帧的数据量计算

?帧?频的数据量(字节)=channel数 * nb_samples样本数 * 每个样本占?的字节数

如果该?频帧是FLTP格式的PCM数据,包含1024个样本,双声道,那么该?频帧包含的?频数据量是210244=8192字节。

AV_SAMPLE_FMT_DBL : 210248 = 16384

2.6 ?频播放时间计算

以采样率44100Hz来计算,每秒44100个sample,?正常?帧为1024个sample,可知每帧播放时间/1024=1000ms/44100,得到每帧播放时间=1024*1000/44100=23.2ms (更精确的是23.21995464852608)。

?帧播放时间(毫秒) = nb_samples样本数 *1000/采样率 =

(1)1024*1000/44100=23.21995464852608ms ->约等于 23.2ms,精度损失了0.011995464852608ms,如果累计10万帧,误差>1199毫秒,如果有视频?起的就会有?视频同步的问题。 如果按着23.2去计算pts(0 23.2 46.4 )就会有累积误差。

(2)1024*1000/48000=21.33333333333333ms

3 FFmpeg重采样API

分配?频重采样的上下?

struct SwrContext *swr_alloc(void);

当设置好相关的参数后,使?此函数来初始化SwrContext结构体

int swr_init(struct SwrContext *s);

分配SwrContext并设置/重置常?的参数。

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, // ?志相关,不?管先,直接为0
    void* log_ctx // ?志相关,不?管先,直接为NULL
);

将输?的?频按照定义的参数进?转换并输出

int swr_convert(struct SwrContext* s, // ?频重采样的上下?
    uint8_t** out, // 输出的指针。传递的输出的数组
    int out_count, //输出的样本数量,不是字节数。单通道的样本数量。
    const uint8_t** in, //输?的数组,AVFrame解码出来的DATA
    int in_count // 输?的单通道的样本数量。
);

in和in_count可以设置为0,以最后刷新最后?个样本。

释放掉SwrContext结构体并将此结构体置为NULL;

void swr_free(struct SwrContext **s);

?频重采样,采样格式转换和混合库。与lswr的交互是通过SwrContext完成的,SwrContext被分配给swr_alloc()或
swr_alloc_set_opts()。 它是不透明的,所以所有参数必须使?AVOptions API设置。为了使?lswr,你需要做的第?件事就是分配SwrContext。 这可以使?swr_alloc()或 swr_alloc_set_opts()来完成。 如果您使?前者,则必须通过AVOptions API设置选项。 后?个函数提供了相同的功能,但它允许您在同?语句中设置?些常?选项。

例如,以下代码将设置从平?浮动样本格式到交织的带符号16位整数的转换,从48kHz到44.1kHz的下采
样,以及从5.1声道到?体声的下混合(使?默认混合矩阵)。 这是使?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 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

?旦设置了所有值,它必须?swr_init()初始化。 如果需要更改转换参数,可以使?AVOptions来更改参数,如上?第?个例?所述; 或者使?swr_alloc_set_opts(),但是第?个参数是分配的上下?。 您必须再次调?swr_init()。?旦设置了所有值,它必须?swr_init()初始化。 如果需要更改转换参数,可以使?AVOptions来更改参数,如上?第?个例?所述; 或者使?swr_alloc_set_opts(),但是第?个参数是分配的上下?。 您必须再次调?swr_init()。
转换本身通过重复调?swr_convert()来完成。 请注意,如果提供的输出空间不?或采样率转换完成后,样本可能会在swr中缓冲,这需要“未来”样本。 可以随时通过使?swr_convert()(in_count可以设置为0)来检索不需要将来输?的样本。 在转换结束时,可以通过调?具有NULL in和in incount的swr_convert()来刷新重采样缓冲区。

4 go代码

moonfdd/ffmpeg-go

在这里插入图片描述

posted @ 2023-05-11 21:48  福大大架构师每日一题  阅读(0)  评论(0编辑  收藏  举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2017 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员