本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8
本系列主要分析各种 ffmpeg 命令参数 在代码里是如何实现的。a.yuv 下载链接:百度网盘,提取码:1h1w 。
命令如下:ffmpeg -s 720*404 -pix_fmt yuv420p -i a.yuv -vcodec libx264 a-666.mp4
上面的命令是 把 yuv 数据 编码成 H264,然后封装进 MP4 格式里面。
yuv文件本身没有 宽高信息,像素格式信息,所以需要命令行指定 -s 720*404 ,否则会报错。
下面就来讲解 ffmpeg 工程 是如何读取 yuv 数据的。
先看 open_input_file() 函数,打开输入文件的逻辑都在这个函数里面。
如上图所示,在 avformat_open_input() 函数前面打了一个 断点,运行后可以发现 ic->iformat->long_name 等于 "raw video",直接在 FFmpeg 4.2 的源码中搜索 "raw video" 这个关键词,可以发现,yuv 的解析是在 libavformat\rawvideodec.c 里面实现的,请看下面代码
libavformat\rawvideodec.c
AVInputFormat ff_rawvideo_demuxer = {
.name = "rawvideo",
.long_name = NULL_IF_CONFIG_SMALL("raw video"),
.priv_data_size = sizeof(RawVideoDemuxerContext),
.read_header = rawvideo_read_header,
.read_packet = rawvideo_read_packet,
.flags = AVFMT_GENERIC_INDEX,
.extensions = "yuv,cif,qcif,rgb",
.raw_codec_id = AV_CODEC_ID_RAWVIDEO,
.priv_class = &rawvideo_demuxer_class,
};
我们知道, yuv 是原始数据,不需要解码的,在这种情况,ffmpeg.c 工程中的 struct InputStream 的 dec_ctx 解码器上下文会初始化成什么样呢?继续分析ffmpeg.c 的实现。
在《FFMpeg 源码分析-命令行》的时候讲解过, 初始化解码器是在 add_input_streams() 函数里面完成的,如图:
在 add_input_streams() 函数里面的 avcodec_alloc_context3 附近打一个断点,看看数据。
可以看到 即使是 yuv 这种原始数据格式,不需要进行解码操作,ffmpeg也声明了一个解码类。也就是 libavcodec\rawdec.c 这个文件。
应该是为了api 函数的通用性,即使是原始数据yuv,也可以 调用 av_read_frame() 把数据读取进 AVPacket,然后丢给 解码器,再从 AVPacket 转成 AVFrame。
总结:
虽然 yuv可以直接读取数据,然后自己拼接YUV数据到 AVFrame 的data 等等字段,但是最好还是调 ffmpeg 的api 函数 av_read_frame() 读出 AVPacket,再转成 AVFrame,自己写一套 yuv 的解释器会比较容易出错,调 ffmpeg 写好的东西比较稳定。
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。