搜索
 找回密码
 立即注册

FFmpeg —— FFmpeg常用API函数介绍

音视频开发进阶 2022-6-29 21:16:17 376

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中。
  3. SwrContext初始化:swr_init()。
  4. 分配样本数据内存空间:使用av_samples_alloc_array_and_samples、av_samples_alloc等工具函数。
  5. 开启重采样转换:通过重复地调用swr_convert来完成。
  6. 重采样转换完成, 释放相关资源:通过swr_free()释放SwrContext。

下面是示例程序的一个流程图:

[]()

函数说明:

• swr_alloc() :创建SwrContext对象。

• av_optset*():设置输入和输出音频的信息。

• 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,并且把指针置为空。

原文地址:https://blog.csdn.net/guoyunfei123/article/details/105791724?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165650813616781818762435%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165650813616781818762435&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-105791724-null-null.142^v26^control,157^v15^new_3&utm_term=FFmpegAPI&spm=1018.2226.3001.4187

使用道具 举报

随机推荐

0 回复

游客
返回顶部