FFmpeg API 之 libswresample

libswresample 库是用于音频重采样,格式转换,音频混合的库。

重采样:即前后的 sample rate 不同

格式转化:即前后的 format 不同

音频混合:即前后的 channel_layout不同,而且前面的通道数小于后面的通道数

 

库中最重要的结构体是 SwrContext,我们对音频的操作都是通过这个结构体进行的。SwrContext 结构体是不透明的,因此其参数必须通过 option API 来设置其选项。

如果想要指定一个音频重采样,其步骤如下:

  1. 分配一个SwrContext
  2. 为SwrContext实例设置相关选项
  3. 初始化SwrContext
  4. 使用SwrContext执行音频转化
  5. 使用完后,销毁SwrContext

 

如果想要使用 SwrContext ,那么第一步就是分配一个 SwrContext,这可以通过函数 swr_alloc() 或者 swr_alloc_set_opts() 完成。如果使用的是前者,函数仅分配了一个空的 SwrContext 实例,尚未设置任何参数,我们必须使用 option API 来设置其相关参数。 swr_alloc_set_opts() 函数为我们提供了便利,它在内部实现了常见的选项设置,只需通过参数传入相关的值,函数就会自动将它设置好,其本质是一样的。

例如,以下代码将设置音频转化的参数:从 planar float 的采样格式转化为 signed 16-bit 采样格式;从 48kHz 到 44.1kHz;从 5.1 通道到 双通道。代码如下:

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() 函数来初始化 SwrContext 。初始化后,如果用户想要再次修改转化的参数,那么仍然可以使用 option API 来修改参数,或者将该 SwrContext 实例传入 swr_alloc_set_opts() 函数,重新指定参数,之后再次调用 swr_init() 来初始化,这样就可以使用新的转化设置。

音频的转换是通过函数 swr_convert() 完成的。注意,SwrContext 可能会在两个地方对采样做缓存。第一个是 output FIFO,它用来存储转化后的音频采样,当用户没有提供足够的输出空间,从而导致无法将所有转换后的采样输出时,剩余的无法输出的采样就保存在了这个output FIFO中。第二个地方是重采样延迟缓冲区(resampling delay buffer),只有执行重采样时(即前后sample rate不同的转化)才会用到这个缓冲区,此时由于转化操作对输入的采样数量有特定的最小数量要求(即凑足N个输入采样才能执行转换),因此其中会保存剩余的输入,直到有新的输入进来(FFmpeg 中称其为 Future 采样,囧),凑够足够的数量才能被执行重采样。不需要 future 的输入采样可以在任何时候通过 swr_convert() 函数(in_count可以设置为0,即不传入输入数据)获取。在转换操作的最后,相关的缓存可以通过 flush 操作输出到output中,即调用 swr_convert() 时传入in设置为 NULL,in_cout 设置为 0。

在转化中使用的音频采样数据可以通过 libavtuil 中的原始音频存储 API 来管理(请查看 AVSampleFormat 一节)。

在 input 和 output 之间的延迟,我们可以通过函数 swr_get_delay() 函数返回。这里详细解释一下,所谓input和output之间的延迟:上面描述了SwrContext中可能会有的缓存,那么当有一个input采样输入后,立刻从output中出来的采样并不是该input转化后的采样,它需要一段时间之后才会实际从output中输出,这段时间就叫做“input 和 output 之间的延迟”。换句话说,它描述了SwrContext中当前缓冲的采样数据所对表示的音频时间。

下面的代码演示了音频转换的循环操作,我们假设其转化参数来自上面的示例,而且有良好定义的函数get_input()和handle_output():

uint8_t **input;
int in_samples;

while (get_input(&input, &in_samples)) {
    uint8_t *output;
    int out_samples = av_rescale_rnd(swr_get_delay(swr, 48000) +
                                     in_samples, 44100, 48000, AV_ROUND_UP);
    av_samples_alloc(&output, NULL, 2, out_samples,
                     AV_SAMPLE_FMT_S16, 0);
    out_samples = swr_convert(swr, &output, out_samples,
                                     input, in_samples);
    handle_output(output, out_samples);
    av_freep(&output);
}

在转换完成后,我们必须调用 swr_free() 函数来释放 SwsContext 对象以及其相关的一切。

swr_close() 函数也可以被使用,但它的存在主要是为了保持libavresample 的兼容性,因此并不需要被调用。

如果在 swr_free() 函数调用之前,数据没有完全 flush,也不会出现内存泄露的情况。

 

以下是 libswresample 中常用的 API:

struct SwrContext *swr_alloc(void);

/*
	分配一个SwrContext实例,此时其选项尚未被设置。
	
	在调用 swr_init() 之前,需要对该实例设置相关的选项。
*/

struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
                                      int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                                      int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
                                      int log_offset, void *log_ctx);

/*
	分配一个SwrContext实例,并设置其相关选项。
	
	out_ch_layout:输出音频的通道布局
	out_sample_fmt:输出音频的采样格式
	out_sample_rate:输出音频的采样频率
	in_ch_layout:输入音频的通道布局
	in_sample_fmt:输入音频的采样格式
	in_sample_rate:输入音频的采样频率
	log_offset:输出日志的水平便宜
	log_ctx:日志上下文,可为NULL
*/
int swr_init(struct SwrContext *s);

/*
	设置完相关的选项后,初始化一个SwrContext。
	
	成功返回0,失败返回负的错误值
*/
int swr_is_initialized(struct SwrContext *s);

/*
	检测s是否已经初始化。
	
	返回正值表示已经初始化,返回0表示尚未初始化。
*/
void swr_free(struct SwrContext **s);

/*
	释放SwrContext,以及其所关联的一切,并将指针置为NULL。
*/

void swr_close(struct SwrContext *s);
/*
	关闭SwrContext,使其处于未打开状态,关闭后 swr_is_initialized() 返回 0.
	
	关闭后的SwrContext可以通过 swr_init() 函数再次初始化。
	
	这个函数的主要目的是用于简化想要同时支持libavresample和libswresample的用例。
*/
int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
                                const uint8_t **in , int in_count);

/*
	转化音频。
	
	in和in_count可以置为NULL和0,用于flush操作,将缓存的采样输出到out中。
	
	当提供的输出空间小于转化后的音频采样数,那么SwrContext将会缓存剩余的输出部分。
	我们可以通过 swr_get_out_samples() 来获取下次给定数量的输入将产生的输出采样的上限值。这样只要将输出空间设置为这个最大可能值,我们就可以避免发生缓存。
	
	out:输出缓冲区
	out_count:输出缓冲区可储存的采样次数
	in:输入缓存区
	in_out:输入的采样次数
	
	返回值:成功时返回实际输出的采样次数,错误时返回负的错误值
*/
int swr_drop_output(struct SwrContext *s, int count);

/*
	丢弃指定数目的输出采样。
	
	如果需要“硬”补偿时,swr_next_pts()函数会调用本函数,和swr_inject_silence()。
	
	成功返回 >= 0,失败返回一个负的AVERROR。
*/
int swr_inject_silence(struct SwrContext *s, int count);

/*
	注入指定数量的静音采样。
	
	如果需要“硬”补偿时,swr_next_pts()函数回去调用本函数,和swr_drop_output()。
	
	成功返回 >= 0,失败返回一个负的AVERROR。
*/
int64_t swr_get_delay(struct SwrContext *s, int64_t base);

/*
	获取下一个输入采样相对于下一个输出采样所经历的延迟。详情见上面的描述。

	确切的延迟时间不一定是input或output的采样频率的整数倍。特别是当向下采样的值很大时,使用output采样率来表示这个延迟可能会是一个非常糟糕的选择,类似地,向上采样时input采样率来表示也会非常糟糕。
	
	base:返回的delay所使用的timebase。当其为1时,返回的值以秒为单位;为1000时,返回值以毫秒为单位;为input采样频率时,以输入采样为单位;为output采样频率时,以输出采样为单位;如果它是in_sample_rate和out_sample_rate的最小公倍数,那么返回值将是一个无舍入法操作的整数值。
	
	返回值:以base为单位的delay值
*/
int swr_get_out_samples(struct SwrContext *s, int in_samples);

/*
	找到下一个swr_convert()函数调用时将会输出的的采样数量的上限,参数in_samples表示调用swr_convert()时将会实际传入的输入采样数。

	这个函数的返回值取决于SwrContext的内部状态,任何能够改变内部状态的函数调用,都将影响返回的值。比如,如果我们首先获取到了一个上限值,然后又去调用了swr_inject_silence(), swr_convert(), swr_next_pts() 或者 swr_set_compensation(),那么此时,之前返回的上限值就变成无效的了,因为SwrContext的内部状态已经发生了改变,此时我们应该重新调用本函数获取对应的上限值。
	
	返回值:成功时返回下一个swr_convert()的输出采样数量的上限值,失败时返回负的AVERROR
*/
int swr_convert_frame(SwrContext *swr,AVFrame *output, const AVFrame *input);

/*
	将input中的音频采样做转化,然后写入到output中。
	
	input和output,必须设置channel_layout,sample_rate和format三个字段。
	
	如果output中的字段data为空,即没有分配数据缓冲区,那么函数会去调用av_frame_get_buffer()并传入output.nb_samples来指定分配的缓冲区大小来为output分配相关缓冲区。
	
	当output为NULL,或者output的数据缓冲区可保存的采样数比所需的要小,在这种情况下,未能写入到output的所有剩余采样都会被保存到内部的 output FIFO 缓冲中,这些缓冲将在下次调用本函数或者swr_convert()时返回。如果发生的是采样频率上的音频转化,即重采样,那么在内部的重采样延迟缓冲区中将会有保留剩余的未能转化的采样数据。swr_get_delay()函数将会返回这些保留的采样对应的音频时长。如果想要将这些保留的采样写入到输出中,那么只需将input置为NULL,重新调用本函数即可。
	
	如果SwrContext中的配置和传入的input和output不匹配,那么不会执行转换。此时,如果是和input不匹配,将返回AVERROR_INPUT_CHANGED,如果是output不匹配,则会返回AVERROR_OUTPUT_CHANGED,如果两者都不匹配,则返回AVERROR_INPUT_CHANGED|AVERROR_OUTPUT_CHANGED。
	
	返回值:成功时返回保存到output中的采样数,失败时返回负的AVERROR值。
*/
int swr_config_frame(SwrContext *swr, const AVFrame *out, const AVFrame *in);

/*
	使用AVFrame中的参数对swr的选项做配置,如果当前swr已经处于打开状态,那么首先调用swr_close()关闭它,然后再做配置。
	
	如果调用失败,函数将会恢复swr的原有配置。
	
	成功返回0,失败返回负的AVERROR。
*/

 

你可能感兴趣的:(FFmpeg,API,详解)