ffmpeg 包含哪些库(FFmpegFFmpeg常用API函数介绍)
ffmpeg 包含哪些库(FFmpegFFmpeg常用API函数介绍)1.1.2 内存的分配和释放(av_malloc()、av_free()等)注:FFmpeg4.0 以上的版本,这个函数已经被废弃。• 注册复用器的函数是av_register_output_format()。• 注册解复用器的函数是av_register_input_format()。• 注册协议处理器的函数是ffurl_register_protocol()。
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()初始化具体的解码器。
【007免费分享】资料包括《Andoird音视频开发必备手册 音视频学习视频 学习文档资料包 大厂面试真题 2022最新学习路线图》等,有需要的可点击下方超链接获取~FFmpegWebRTCRTMPRTSPHLSRTP播放器-音视频流媒体高级开发-资料领取
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。
下面是示例程序的一个流程图:
函数说明:
• 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,并且把指针置为空。
【免费】学习视频,先关注,不迷路~
【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂