1.libavformat
AVFormatContext可以进行格式的封装与解封装,它的数据部分由底层提供,底层使用了AVIOContext,这个AVIOContext实际上就是为普通的I/O增加了一层Buffer缓冲区,再往 底层就是URLContext,也就是到达了协议层。如图:
libavformat
相关函数及结构体:
av_register_all()
编译FFmpeg的时候,做了一个configure的配置,configure的配置会生成两个文件:config.mk与config.h。 config.mk实际上就是makefile文件需要包含进去的子模块,会作用在编译阶段,帮助开发者编译出正确的库;而config.h是作用在运行阶段, 这一阶段将确定需要注册哪些容器以及编解码格式到FFmpeg框架中。 所以该函数的内部实现会先调用avcodec_register_all来注册所有config.h 里面开放的编解码器,然后会注册所有的Muxer和Demuxer(也就是封 装格式),最后注册所有的Protocol(即协议层的东西)。
avformat_open_input()
avformat_open_input会根据所提供的文件路径判断文件的格式,其实就是通过这一步来决定使用的到底是哪一个Demuxer。
avformat_find_stream_info()
该方法的作用就是把所有Stream的MetaData信息填充好。方法内部会先查找对应的解码器,然后打开对应的解码器,紧接着会利用Demuxer中的 read_packet函数读取一段数据进行解码,当然解码的数据越多,分析出的流信息就会越准确,如果是本地资源,那么很快就可以得到非常准确的信息了,但是对于网络资源来说,则会比较慢,因此该函数有几个参数可以控制读取数据的长度,一个是probe size,一个是max_analyze_duration,还有一个是fps_probe_size,这三个参数共同控制解码数据的长度,当然,如果配置这几个参数的值越小,那么这个函数执行的时间就会越快,但是会导致AVStream结构体里面一些信息(视频的宽、高、fps、编码类型等)不准确。
av_find_best_stream()
获取各种流的索引。
av_read_frame()
使用该方法读取出来的数据是AVPacket,在FFmpeg的早期版本中 开放给开发者的函数其实就是av_read_packet,但是需要开发者自己来处理AVPacket中的数据不能被解码器完全处理完的情况,即需要把未处理完的压缩数据缓存起来的问题。所以到了新版本的FFmpeg中,其提供了该函数,用于处理此状况。该函数的实现首先会委托到Demuxer的 read_packet方法中去,当然read_packet通过解复用层和协议层的处理之后,会将数据返回到这里,在该函数中进行数据缓冲处理。为了保证每次读取完整的一帧,读取到的数据长度可能不一样。
2.libavcodec
对于开发者来说,这一层我们能接触到的最顶层的结构体就是 AVCodecContext,该结构体包含的就是与实际的编解码有关的部分。首先,AVCodecContext是包含在一个AVStream里面的,即描述了这路流的编码格式是什么,其中存放了具体的编码格式信息,根据Codec的信息可以打开编码器或者解码器,然后利用该编码器或者解码器进行 AVPacket与AVFrame之间的转换(实际上就是解码或者编码的过程), 这是FFmpeg中最重要的一部分。结构如图:
libavformat
相关函数及结构体:
avcodec_find_decoder()
根据上下文找到解码器。
avcodec_open2()
打开解码器。
av_frame_alloc()
AVFrame对象必须调用av_frame_alloc()在堆上分配。
avcodec_decode_audio4()
参数1:avctx编解码器上下文
参数2:frame用于存储解码音频样本的AVFrame
参数3:got_frame_ptr如果没有帧可以解码则为零,否则为非零
参数4:avpkt包含输入缓冲区的输入AVPacket
参数5:如果在解码期间发生错误,则返回否定错误代码,否则返回从输入AVPacket消耗的字节数。
avcodec_send_packet():发送解码未处理数据
avcodec_receive_frame():接收解码后的数据
avcodec_send_frame:发送编码未处理数据
avcodec_receive_packet():接收编码后的数据
一个packet会被解码出一个frame,不过也存在一个packet被解码出多个frame或者多个packet才能解码出一个frame的情况,甚至也有些解码器在输入以及输出端上可能会有延迟。了解更多看这里
返回状态:
send 0 :send_packet返回值为0,正常状态,意味着输入的packet被解码器正常接收。
send EAGAIN :send_packet返回值为EAGAIN,输入的packet未被接收,需要输出一个或多个的frame后才能重新输入当前packet。
send EOF :send_packet返回值为EOF,当send_packet输入为NULL时才会触发该状态,用于通知解码器输入packet已结束。
send EINVAL :没有打开×××,或者这是一个编码器,或者要求刷新
receive 0 :receive_frame返回值为0,正常状态,意味着已经输出一帧。
receive EAGAIN:receive_frame返回值为EAGAIN,未能输出frame,需要输入更多的packet才能输出当前frame。
receive EOF :receive_frame返回值为EOF,当处于send EOF状态后,调用一次或者多次receive_frame后就能得到该状态,表示所有的帧已经被输出。
简略流程:av_read_frame读取一帧数据,avcodec_send_packet发送数据,解码后avcodec_receive_frame接收一个完整帧,但因为一些特殊原因avcodec_send_packet发送的不是一个完成帧,此时avcodec_receive_frame会返回
3.libavutil
av_image_get_buffer_size()
给缓冲区设置类型类型,得到YUV420P缓冲区大小。
参数一:视频像素数据格式类型->YUV420P格式
参数二:一帧视频像素数据宽 = 视频宽
参数三:一帧视频像素数据高 = 视频高
参数四:字节对齐方式->默认是1
int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
videoCodecCtx->width,
videoCodecCtx->height,
1);
av_image_fill_arrays()
缓存区填充数据。
参数一:目标->填充数据(avframe_yuv420p)
参数二:目标->每一行大小
参数三:原始数据
参数四:目标->格式类型
参数五:宽
参数六:高
参数七:字节对齐方式
av_image_fill_arrays(avframe_yuv420p->data,
avframe_yuv420p->linesize,
out_buffer,
AV_PIX_FMT_YUV420P,
videoCodecCtx->width,
videoCodecCtx->height,
1);
4.libswresample
音频重采样,就是改变音频的采样率、sample format、声道数等参数,使之按照我们期望的参数输出。
SwrContext
音频格式转换上下文。
swr_alloc()
初始化上下文结构体。
swr_alloc_set_opts()
设置重采样参数。
swr_init()
在swr_alloc_set_opts之后调用,将采样参数设置到上下文。
参数1:重采样上下文
参数2:输出的layout, 如:5.1声道…
参数3:输出的样本格式。Float, S16, S24
参数4:输出的样本率。可以不变。
参数5:输入的layout。
参数6:输入的样本格式。
参数7:输入的样本率。
参数8,参数9,日志,不用管,可直接传0 */
av_samples_get_buffer_size()
使用av_sample_get_buffer_size来计算音频占用的字节数。
参数1:linesize calculated linesize, may be NULL
参数2:nb_channels 声道
参数3:nb_samples 单个通道中的样本数
参数4:sample_fmt 采样格式
参数5:align 对齐缓冲区大小对齐(0 =默认,1 =无对齐)
swr_convert()
转码。使用avcodec_decode_audio4函数解码音频得到的数据类型为float 4bit,而播放器播放的格式一般为S16(signed 16bit),这就需要对解码得到的数据进行转换。
参数1:音频重采样的上下文
参数2:输出的指针。传递的输出的数组
参数3:输出的样本数量,不是字节数。单通道的样本数量。
参数4:输入的数组,AVFrame解码出来的DATA
参数5:输入的单通道的样本数量。
5.libswscale
SwsContext
视频格式转换上下文,编解码中可能需要对视频格式重新设置,就需要用到SwsContext。
sws_getContext()
设置视频转换上下文。
参数一:源文件->原始视频像素数据格式宽
参数二:源文件->原始视频像素数据格式高
参数三:源文件->原始视频像素数据格式类型
参数四:目标文件->目标视频像素数据格式宽
参数五:目标文件->目标视频像素数据格式高
参数六:目标文件->目标视频像素数据格式类型
sws_getContext(videoCodecCtx->width,
videoCodecCtx->height,
videoCodecCtx->pix_fmt,
videoCodecCtx->width,
videoCodecCtx->height,
AV_PIX_FMT_YUV420P,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL);
sws_scale()
视频参数转换。
参数一:视频像素数据格式上下文
参数二:原来的视频像素数据格式->输入数据
参数三:原来的视频像素数据格式->输入画面每一行大小
参数四:原来的视频像素数据格式->输入画面每一行开始位置(填写:0->表示从原点开始读取)
参数五:原来的视频像素数据格式->输入数据行数
参数六:转换类型后视频像素数据格式->输出数据
参数七:转换类型后视频像素数据格式->输出画面每一行大小
sws_scale(swscontext,
(const uint8_t *const *)avframe_in->data,
avframe_in->linesize,
0,
avcodec_context->height,
avframe_yuv420p->data,
avframe_yuv420p->linesize);
网友评论