FFmpeg —— FFmpeg常用API函数介绍

目录

1.常用API

1.1    通用API

1.1.1    av_register_all()——弃用

1.1.2    内存的分配和释放(av_malloc()、av_free()等)

1.1.3    常见结构体的初始化和销毁(AVFormatContext、AVFrame)

1.1.4    avio_open2()

1.1.5    avcodec_find_encoder() 和 avcodec_find_decoder()

1.1.6    avcodec_open2()

1.1.7    avcodec_close()

1.2    解码API

1.2.1    avformat_open_input()

1.2.2    avformat_find_stream_info()

1.2.3    av_read_frame

1.2.4    avcodec_send_packet()

1.2.5    avcodec_receive_frame

1.2.6    解码API使用说明

1.2.7    avformat_close_input()

1.3    编码API

1.1.1    avformat_alloc_output_context2()

1.1.2    avformat_write_header()

1.1.3    avcodec_send_frame()

1.1.4    avcodec_receive_packet

1.1.5    编码API使用说明

1.1.6    av_write_frame()/av_interleaved_write_frame()

1.1.7    av_write_trailer()

1.4    图像处理API

1.4.1    sws_getContext()

1.4.2    sws_scale()

1.4.3    sws_freeContext()

1.5    重采样API

1.5.1    使用说明

1.5.2    swr_alloc()

1.5.3    swr_alloc_set_opts()

1.5.4    swr_init()

1.5.5    av_sample_alloc()

1.5.6    swr_convert()

1.5.7    swr_free()

2.参考


1.常用API

1.1    通用API

1.1.1    av_register_all()——弃用

初始化 libavformat 和注册所有的复用器、解复用器和协议处理器。如果不调用这个函数,可以调用下面的三个函数来选择支持的格式。
•    注册复用器的函数是av_register_output_format()。
•    注册解复用器的函数是av_register_input_format()。
•    注册协议处理器的函数是ffurl_register_protocol()。
注:FFmpeg4.0 以上的版本,这个函数已经被废弃。

1.1.2    内存的分配和释放(av_malloc()、av_free()等)

av_malloc() 和 av_free() 都是简单的封装了系统函数 malloc() 和free(),并做了一些错误检查工作。同理的还有 av_realloc()。

1.1.3    常见结构体的初始化和销毁(AVFormatContext、AVFrame)

https://blog.csdn.net/leixiaohua1020/article/details/41181155

1.1.4    avio_open2()

https://blog.csdn.net/leixiaohua1020/article/details/41199947

该函数用于打开FFmpeg的输入输出文件。avio_open2()的声明位于libavformat\avio.h文件中,如下所示。
/**
 * Create and initialize a AVIOContext for accessing the
 * resource indicated by url.
 * @note When the resource indicated by url has been opened in
 * read+write mode, the AVIOContext can be used only for writing.
 *
 * @param s Used to return the pointer to the created AVIOContext.
 * In case of failure the pointed to value is set to NULL.
 * @param url resource to access
 * @param flags flags which control how the resource indicated by url
 * is to be opened
 * @param int_cb an interrupt callback to be used at the protocols level
 * @param options  A dictionary filled with protocol-private options. On return
 * this parameter will be destroyed and replaced with a dict containing options
 * that were not found. May be NULL.
 * @return >= 0 in case of success, a negative value corresponding to an
 * AVERROR code in case of failure
 */
int avio_open2(AVIOContext **s, const char *url, int flags,
               const AVIOInterruptCB *int_cb, AVDictionary **options);
函数参数:
•    s:函数调用成功之后创建的AVIOContext结构体。
•    url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
•    flags:打开地址的方式。可以选择只读,只写,或者读写。取值如下。
o    AVIO_FLAG_READ:只读。
o    AVIO_FLAG_WRITE:只写。
o    AVIO_FLAG_READ_WRITE:读写。
•    int_cb:目前还没有用过。
•    options:目前还没有用过。

1.1.5    avcodec_find_encoder() 和 avcodec_find_decoder()

avcodec_find_encoder() 用于查找 FFmpeg 的编码器
avcodec_find_decoder() 用于查找 FFmpeg 的解码器
声明都位于 libavcodec\avcodec.h。其原型如下:

// 函数的参数是一个编码器的ID,返回查找到的编码器(没有找到就返回NULL)。
AVCodec *avcodec_find_encoder(enum AVCodecID id);

// 函数的参数是一个解码器的ID,返回查找到的解码器(没有找到就返回NULL)。
AVCodec *avcodec_find_decoder(enum AVCodecID id);

其实这两个函数的实质就是遍历AVCodec链表并且获得符合条件的元素。

1.1.6    avcodec_open2()

https://blog.csdn.net/leixiaohua1020/article/details/44117891

用于初始化一个音视频编解码器的 AVCodecContext,其原型如下:

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 

•    avctx:需要初始化的 AVCodecContext。
•    codec:输入的AVCodec。
•    options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置。

我们可以简单梳理一下avcodec_open2()所做的工作,如下所列:
(1)为各种结构体分配内存(通过各种av_malloc()实现)。
(2)将输入的AVDictionary形式的选项设置到AVCodecContext。
(3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。
(4)如果是编码器,检查输入参数是否符合编码器的要求
(5)调用AVCodec的init()初始化具体的解码器。

1.1.7    avcodec_close()

https://blog.csdn.net/leixiaohua1020/article/details/44206699

用于关闭编码器,其原型如下:
int avcodec_close(AVCodecContext *avctx);

该函数只有一个参数,就是需要关闭的编码器的 AVCodecContext。

1.2    解码API

下面介绍解码需要用到的几个函数,声明都位于 libavformat\avformat.h。
解码使用 avcodec_send_packet() 和 avcodec_receive_frame() 两个函数。

1.2.1    avformat_open_input()

https://blog.csdn.net/leixiaohua1020/article/details/8661601
https://blog.csdn.net/leixiaohua1020/article/details/44064715

打开输出的流和读取头信息。其原型如下:

int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options); 
•    ps:函数调用成功之后处理过的 AVFormatContext 结构体。
•    url:打开的视音频流的 URL。
•    fmt:强制指定 AVFormatContext 中 AVInputFormat 的。这个参数一般情况下可以设置为 NULL,这样 FFmpeg 可以自动检测 AVInputFormat。
•    options:附加的一些选项,一般情况下可以设置为 NULL。

函数执行成功的话,其返回值大于等于 0。

1.2.2    avformat_find_stream_info()

https://blog.csdn.net/leixiaohua1020/article/details/44084321
读取音视频数据来获取一些相关的信息。其原型如下:
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)  
简单解释一下它的参数的含义:
•    ic:输入的AVFormatContext。
options:额外的选项,目前没有深入研究过。
函数正常执行后返回值大于等于0。
该函数主要用于给每个媒体流(音频/视频)的AVStream结构体赋值。我们大致浏览一下这个函数的代码,会发现它其实已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作。换句话说,该函数实际上已经“走通”的解码的整个流程。下面看一下除了成员变量赋值之外,该函数的几个关键流程。
1.查找解码器:find_decoder()
2.打开解码器:avcodec_open2()
1.读取完整的一帧压缩编码的数据:read_frame_internal()
注:av_read_frame()内部实际上就是调用的read_frame_internal()。
4.解码一些压缩编码数据:try_decode_frame()

1.2.3    av_read_frame

https://blog.csdn.net/leixiaohua1020/article/details/12678577
读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame() 获得一帧视频的压缩数据,然后才能对该数据进行解码。其原型如下:
int av_read_frame(AVFormatContext *s, AVPacket *pkt)    
•    s:输入的AVFormatContext
•    pkt:输出的AVPacket
如果返回0则说明读取正常。

1.2.4    avcodec_send_packet()

发送数据到ffmepg,放到解码队列中
函数原型:
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

1.2.5    avcodec_receive_frame

将成功的解码队列中取出1个frame
函数原型:
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

avcodec_send_packet和avcodec_receive_frame调用关系并不一定是一对一的,比如一些音频数据一个AVPacket中包含了1秒钟的音频,调用一次avcodec_send_packet之后,可能需要调用25次 avcodec_receive_frame才能获取全部的解码音频数据,所以要做如下处理:

int re = avcodec_send_packet(codec, pkt);
if (re != 0)
{
    return;
}

while( avcodec_receive_frame(codec, frame) == 0)
{
    //读取到一帧音频或者视频
    //处理解码后音视频 frame
}

@返回值 0 表示成功,其他的异常值说明:
AVERROR(EAGAIN):当前不接受输出,必须重新发送
AVERROR_EOF:已经刷新×××,没有新的包可以被刷新
AVERROR(EINVAL):没有打开×××,或者这是一个编码器,或者要求刷新
AVERRO(ENOMEN):无法添加包到内部队列

输入参数可以为NULL,或者AVPacket的data域设置为NULL或者size域设置为0,表示将刷新所有的包,意味着数据流已经结束了。第一次发送刷新会总会成功,第二次发送刷新包是没有必要的,并且返回AVERROR_EOF,如果×××缓存了一些帧,返回一个刷新包,将会返回所有的解码包

1.2.6    解码API使用说明

关于 avcodec_send_packet() 与 avcodec_receive_frame() 的使用说明:

[1] 按 dts 递增的顺序向解码器送入编码帧 packet,解码器按 pts 递增的顺序输出原始帧 frame,实际上解码器不关注输入 packe t的 dts(错值都没关系),它只管依次处理收到的 packet,按需缓冲和解码

[2] avcodec_receive_frame() 输出 frame 时,会根据各种因素设置好 frame->best_effort_timestamp(文档明确说明),实测 frame->pts 也会被设置(通常直接拷贝自对应的 packet.pts,文档未明确说明)用户应确保 avcodec_send_packet() 发送的 packet 具有正确的 pts,编码帧 packet 与原始帧 frame 间的对应关系通过 pts 确定

[3] avcodec_receive_frame() 输出 frame 时,frame->pkt_dts 拷贝自当前avcodec_send_packet() 发送的 packet 中的 dts,如果当前 packet 为 NULL(flush packet),解码器进入 flush 模式,当前及剩余的 frame->pkt_dts 值总为 AV_NOPTS_VALUE。因为解码器中有缓存帧,当前输出的 frame 并不是由当前输入的 packet 解码得到的,所以这个 frame->pkt_dts 没什么实际意义,可以不必关注

[4] avcodec_send_packet() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF

[5] avcodec_send_packet() 多次发送 NULL 并不会导致解码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉解码器中缓存帧。因此播放完毕时应 avcodec_send_packet(NULL) 来取完缓存的帧,而 SEEK 操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧

[6] 解码器通常的冲洗方法:调用一次 avcodec_send_packet(NULL)(返回成功),然后不停调用 avcodec_receive_frame() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_frame() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志

1.2.7    avformat_close_input()

关闭打开的流。其原型如下:
void avformat_close_input(AVFormatContext **s)  

1.3    编码API

在基于 FFmpeg 的音视频编码器程序中,avformat_alloc_output_context2() 函数通常是第一个调用的函数(除了组件注册函数 av_register_all())。另外介绍 FFmpeg 的写文件用到的 3 个函数,声明都位于 libavformat\avformat.h:
•    av_write_frame() 用于写视频数据;
•    avformat_write_header() 用于写视频文件头;
•    av_write_trailer() -用于写视频文件尾。

1.1.1    avformat_alloc_output_context2()

https://blog.csdn.net/leixiaohua1020/article/details/41198929
初始化一个用于输出的 AVFormatContext 结构体。其原型如下:
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat * oformat, const char * format_name, const char * filename)
•    ctx:函数调用成功之后创建的AVFormatContext结构体。
•    oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由FFmpeg猜测输出格式。
PS:使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。
•    format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。
•    filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。
函数执行成功的话,其返回值大于等于0。

1.1.2    avformat_write_header()

https://blog.csdn.net/leixiaohua1020/article/details/44116215
分配一个 stream 的私有数据而且写 stream 的 header 到一个输出的媒体文件。其原型如下:
int avformat_write_header(AVFormatContext *s, AVDictionary ** options)
简单解释一下它的参数的含义:
•    s:用于输出的AVFormatContext。
•    options:额外的选项,目前没有深入研究过,一般为NULL。

1.1.3    avcodec_send_frame()

为编码器提供包含未压缩音频或视频的AVFrame
函数原型:
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);

1.1.4    avcodec_receive_packet

从编码器读取编码数据,一旦成功,它将返回带有压缩帧的AVPacket
函数原型:
int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);

1.1.5    编码API使用说明

关于 avcodec_send_frame() 与 avcodec_receive_packet() 的使用说明:
[1] 按 pts 递增的顺序向编码器送入原始帧 frame,编码器按 dts 递增的顺序输出编码帧 packet,实际上编码器关注输入 frame 的 pts 不关注其 dts,它只管依次处理收到的 frame,按需缓冲和编码
[2] avcodec_receive_packet() 输出 packet 时,会设置 packet.dts,从 0 开始,每次输出的 packet 的 dts 加 1,这是视频层的 dts,用户写输出前应将其转换为容器层的 dts
[3] avcodec_receive_packet() 输出 packet 时,packet.pts 拷贝自对应的 frame.pts,这是视频层的 pts,用户写输出前应将其转换为容器层的 pts
[4] avcodec_send_frame() 发送 NULL frame 时,编码器进入 flush 模式
[5] avcodec_send_frame() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF
[6] avcodec_send_frame() 多次发送 NULL 并不会导致编码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉编码器中缓存帧。因此编码完毕时应使用 avcodec_send_frame(NULL) 来取完缓存的帧,而SEEK操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧
[7] 编码器通常的冲洗方法:调用一次 avcodec_send_frame(NULL)(返回成功),然后不停调用 avcodec_receive_packet() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_packet() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志
[8] 对音频来说,如果 AV_CODEC_CAP_VARIABLE_FRAME_SIZE(在 AVCodecContext.codec.capabilities 变量中,只读)标志有效,表示编码器支持可变尺寸音频帧,送入编码器的音频帧可以包含任意数量的采样点。如果此标志无效,则每一个音频帧的采样点数目(frame->nb_samples)必须等于编码器设定的音频帧尺寸(avctx->frame_size),最后一帧除外,最后一帧音频帧采样点数可以小于 avctx->frame_size

1.1.6    av_write_frame()/av_interleaved_write_frame()

https://blog.csdn.net/leixiaohua1020/article/details/44199673
用于输出一帧视音频数据。其原型如下:
int av_write_frame(AVFormatContext *s, AVPacket *pkt)
•    参数 s 为用于输出的 AVFormatContext
•    参数 pkt 为等待输出的 AVPacket。
函数正常执行后返回值等于0。

int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);

将packet写入输出媒体。

本函数将按需在内部缓存packet,从而确保输出媒体中不同流的packet能按照dts增长的顺序正确交织。

区别:

得出的结论是:在有多个流的情况下要用av_interleaved_write_frame,只有单一流2个函数都可以用。

https://blog.csdn.net/dancing_night/article/details/46469865 

1.1.7    av_write_trailer()

https://blog.csdn.net/leixiaohua1020/article/details/44201645
用于输出文件尾。其原型如下:
int av_write_trailer(AVFormatContext *s)
它只需要指定一个参数,即用于输出的 AVFormatContext,函数正常执行后返回值等于 0。

1.4    图像处理API

libswscale 是一个主要用于处理图片像素数据的类库。可以完成图片像素格式的转换,图片的拉伸等工作。libswscale 常用的函数数量很少,一般情况下就3个,声明都位于 libswscale\swscale.h:

•    sws_getContext():分配和返回一个SwsContext。
•    sws_scale():处理图像数据。
•    sws_freeContext():释放一个SwsContext。

其中 sws_getContext() 也可以用 sws_getCachedContext() 取代。

https://www.cnblogs.com/yongdaimi/p/10715830.html

1.4.1    sws_getContext()

分配和返回一个 SwsContext。其原型如下:
struct SwsContext *sws_getContext(
            int srcW, /* 输入图像的宽度 */
            int srcH, /* 输入图像的宽度 */
            enum AVPixelFormat srcFormat, /* 输入图像的像素格式 */
            int dstW, /* 输出图像的宽度 */
            int dstH, /* 输出图像的高度 */
            enum AVPixelFormat dstFormat, /* 输出图像的像素格式 */
            int flags,/* 选择缩放算法(只有当输入输出图像大小不同时有效),一般选择SWS_FAST_BILINEAR */
            SwsFilter *srcFilter, /* 输入图像的滤波器信息, 若不需要传NULL */
            SwsFilter *dstFilter, /* 输出图像的滤波器信息, 若不需要传NULL */
            const double *param /* 特定缩放算法需要的参数(?),默认为NULL */
            );
参数1:被转换源的宽

参数2:被转换源的高

参数3:被转换源的格式,eg:YUV、RGB……(枚举格式,也可以直接用枚举的代号表示eg:AV_PIX_FMT_YUV420P这些枚举的格式在libavutil/pixfmt.h中列出)

参数4:转换后指定的宽

参数5:转换后指定的高

参数6:转换后指定的格式同参数3的格式

参数7:转换所使用的算法,

参数8:NULL

参数9:NULL

参数10:NULL

成功后返回SwsContext 类型的结构体。

与其类似的函数还有: sws_getCachedContext ,区别在于: sws_getContext 可以用于多路码流转换,为每个不同的码流都指定一个不同的转换上下文,而 sws_getCachedContext 只能用于一路码流转换。

1.4.2    sws_scale()

处理图像数据。
其原型如下:
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
              const int srcStride[], int srcSliceY, int srcSliceH,
              uint8_t *const dst[], const int dstStride[]);

sws_scale() 函数主要是用来做视频像素格式和分辨率的转换,其优势在于:可以在同一个函数里实现:1.图像色彩空间转换, 2:分辨率缩放,3:前后图像滤波处理。不足之处在于:效率相对较低,不如libyuv或shader

1.参数 SwsContext *c, 转换格式的上下文。也就是 sws_getContext 函数返回的结果。
2.参数 const uint8_t *const srcSlice[], 输入图像的每个颜色通道的数据指针。其实就是解码后的AVFrame中的data[]数组。因为不同像素的存储格式不同,所以srcSlice[]维数也有可能不同。
以YUV420P为例,它是planar格式,它在内存中的排布如下:
YYYYYYYY UUUU VVVV
使用FFmpeg解码后存储在AVFrame的data[]数组中时:
data[0]——-Y分量, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8…… 
data[1]——-U分量, U1, U2, U3, U4…… 
data[2]——-V分量, V1, V2, V3, V4…… 
linesize[]数组中保存的是对应通道的数据宽度 , 
linesize[0]——-Y分量的宽度 
linesize[1]——-U分量的宽度 
linesize[2]——-V分量的宽度
而RGB24,它是packed格式,它在data[]数组中则只有一维,它在存储方式如下:
data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……
这里要特别注意,linesize[0]的值并不一定等于图片的宽度,有时候为了对齐各解码器的CPU,实际尺寸会大于图片的宽度,这点在我们编程时(比如OpengGL硬件转换/渲染)要特别注意,否则解码出来的图像会异常。
1.参数const int srcStride[],输入图像的每个颜色通道的跨度。.也就是每个通道的行字节数,对应的是解码后的AVFrame中的linesize[]数组。根据它可以确立下一行的起始位置,不过stride和width不一定相同,这是因为:
a.由于数据帧存储的对齐,有可能会向每行后面增加一些填充字节这样 stride = width + N;
b.packet色彩空间下,每个像素几个通道数据混合在一起,例如RGB24,每个像素3字节连续存放,因此下一行的位置需要跳过3*width字节。
4.参数int srcSliceY, int srcSliceH,定义在输入图像上处理区域,srcSliceY是起始位置,srcSliceH是处理多少行。如果srcSliceY=0,srcSliceH=height,表示一次性处理完整个图像。这种设置是为了多线程并行,例如可以创建两个线程,第一个线程处理 [0, h/2-1]行,第二个线程处理 [h/2, h-1]行。并行处理加快速度。
5.参数uint8_t *const dst[], const int dstStride[]定义输出图像信息(输出的每个颜色通道数据指针,每个颜色通道行字节数)

1.4.3    sws_freeContext()

释放一个 SwsContext。其原型如下:
void sws_freeContext(struct SwsContext *swsContext)

1.5    重采样API

https://www.jianshu.com/p/bf5e54f553a4
FFmpeg中重采样的功能由libswresample(后面简写为lswr)提供。
lswr提供了高度优化的转换音频的采样频率、声道格式或样本格式的功能。
功能说明:
•    采样频率转换:对音频的采样频率进行转换的处理,例如把音频从一个高的44100Hz的采样频率转换到8000Hz。从高采样频率到低采样频率的音频转换是一个有损的过程。API提供了多种的重采样选项和算法。
•    声道格式转换:对音频的声道格式进行转换的处理,例如立体声转换为单声道。当输入通道不能映射到输出流时,这个过程是有损的,因为它涉及不同的增益因素和混合。
•    样本格式转换:对音频的样本格式进行转换的处理,例如把s16的PCM数据转换为s8格式或者f32的PCM数据。此外提供了Packed和Planar包装格式之间相互转换的功能,Packed和Planar的区别见FFmpeg中Packed和Planar的PCM数据区别。
此外,还提供了一些其他音频转换的功能如拉伸和填充,通过专门的设置来启用。

1.5.1    使用说明

重采样的处理流程:
1.    创建上下文环境:重采样过程上下文环境为SwrContext数据结构。
2.    参数设置:转换的参数设置到SwrContext中。
1.    SwrContext初始化:swr_init()。
4.    分配样本数据内存空间:使用av_samples_alloc_array_and_samples、av_samples_alloc等工具函数。
5.    开启重采样转换:通过重复地调用swr_convert来完成。
6.    重采样转换完成, 释放相关资源:通过swr_free()释放SwrContext。
下面是示例程序的一个流程图:
FFmpeg —— FFmpeg常用API函数介绍_第1张图片
函数说明:
•    swr_alloc() :创建SwrContext对象。
•    av_opt_set_*():设置输入和输出音频的信息。
•    swr_init(): 初始化SwrContext。
•    av_samples_alloc_array_and_samples:根据音频格式分配相应大小的内存空间。
•    av_samples_alloc:根据音频格式分配相应大小的内存空间。用于转换过程中对输出内存大小进行调整。
•    swr_convert:进行重采样转换。

1.5.2    swr_alloc()

函数原型:
struct SwrContext *swr_alloc(void);

创建上下文环境,分配一个SwrContext,如果你使用这个函数,需要在调用swr_init()之前设置SwrContext的参数(手工的或者调用swr_alloc_set_opts())
重采样过程上下文环境为SwrContext数据结构(SwrContext的定义没有对外暴露)。
创建SwrContext的方式有两种:
1.    swr_alloc() : 创建SwrContext之后再通过AVOptions的API来设置参数。
2.    swr_alloc_set_opts():在创建SwrContext的同时设置必要的参数。

1.5.3    swr_alloc_set_opts()

函数原型:
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);
设置参数,参数设置的方式有两种:
1.    AVOptions的API
2.    swr_alloc_set_opts():如果第一个参数为NULL则创建一个新的SwrContext,否则对已有的SwrContext进行参数设置。
假定要进行如下的重采样转换:
“f32le格式、采样频率48kHz、5.1声道格式”的PCM数据
转换为
“s16le格式、采样频率44.1kHz、立体声格式”的PCM数据
swr_alloc()的使用方式如下所示:
SwrContext *swr = swr_alloc();
av_opt_set_channel_layout(swr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0);
av_opt_set_channel_layou(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_FLPT, 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

1.5.4    swr_init()

参数设置好之后必须调用swr_init()对SwrContext进行初始化。
函数原型:
int swr_init(struct SwrContext *s);
如果需要修改转换的参数:
1.    重新进行参数设置。
2.    再次调用swr_init()。

1.5.5    av_sample_alloc()

分配样本数据内存空间
函数原型:
/**
 * @param[out] audio_data  输出数组,每个元素是指向一个通道的数据的指针。
 * @param[out] linesize    aligned size for audio buffer(s), may be NULL
 * @param nb_channels      通道的个数。
 * @param nb_samples       每个通道的样本个数。
 * @param align            buffer size alignment (0 = default, 1 = no alignment)
 * @return                 成功返回大于0的数,错误返回负数。
 */
int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels,
                     int nb_samples, enum AVSampleFormat sample_fmt, int align);

转换之前需要分配内存空间用于保存重采样的输出数据,内存空间的大小跟通道个数、样本格式需要、容纳的样本个数都有关系。libavutil中的samples处理API提供了一些函数方便管理样本数据,例如av_samples_alloc()函数用于分配存储sample的buffer。

1.5.6    swr_convert()

重采样转换是通过重复地调用swr_convert()来完成的。
swr_convert()函数的定义如下:
 * @param out            输出缓冲区,当PCM数据为Packed包装格式时,只有out[0]会填充有数据。
 * @param out_count      每个通道可存储输出PCM数据的sample数量。
 * @param in             输入缓冲区,当PCM数据为Packed包装格式时,只有in[0]需要填充有数据。
 * @param in_count       输入PCM数据中每个通道可用的sample数量。
 *
 * @return               返回每个通道输出的sample数量,发生错误的时候返回负数。
 */
int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
                                const uint8_t **in , int in_count);
说明:
如果没有提供足够的空间用于保存输出数据,采样数据会缓存在swr中。可以通过 swr_get_out_samples()来获取下一次调用swr_convert在给定输入样本数量下输出样本数量的上限,来提供足够的空间。
如果是采样频率转换,转换完成后采样数据可能会缓存在swr中,它期待你提供更多的输入数据。
如果实际上并不需要更多输入数据,通过调用swr_convert(),其中参数in_count设置为0来获取缓存在swr中的数据。
转换结束之后需要冲刷swr_context的缓冲区,通过调用swr_convert(),其中参数in设置为NULL,参数in_count设置为0。

1.5.7    swr_free()

原型
void swr_free(struct SwrContext **  s)  
释放给定的SwrContext,并且把指针置为空。

2.参考

雷神的系列博文 

音频重采样API 

你可能感兴趣的:(FFmpeg源码,FFmpeg,音视频)