基于FFmpeg命令行的实时图像处理与RTSP推流解决方案
前言
在一些项目开发过程中需要将实时处理的图像再实时的将结果展示出来,此时如果再使用一张一张图片显示的方式展示给开发者,那么图像窗口的反复开关将会出现窗口闪烁的问题,实际上无法体现出动态画面的效果。因此,需要使用码流的方式来展示图像处理后的效果。
FFmpeg是一款开源且广泛使用的码流开发工具,其支持通过调用C库来实现码流处理软件,同时还支持通过调用命令行的方式实现码流相关的功能。FFmpeg由法国天才程序员Fabrice Bellard在2000年时开发初版,后于2004年找到接手人Michael Niedermayer维护到至今。FFmpeg提供了多种多媒体格式的封装和解封装,包括多种音频视频编码、多种协议的流媒体、多种色彩格式转换、多种采样率转换、多种码率转换等。当FFmpeg经过编译后可以获得可执行程序ffmpeg用于码流处理、可执行程序ffplay用于播放各种媒体文件或流、可执行程序ffprobe用于分析媒体文件或媒体流。
使用FFmpeg C库开发的问题
由于多年的发展FFmpeg的C库API在2016年6月26日发布的6.1.6版本后,一些关键API被弃用和替换。比如,弃用初始化API:av_register_all;替换解码、编码API:avcodec_decode_video2,avcodec_encode_video2替换为avcodec_send_frame,avcodec_receive_packet,avcodec_send_frame,avcodec_receive_packet;数据包管理:av_init_packet替换为av_packet_alloc,av_packet_free;以及编解码器上下文管理:av_malloc替换为avcodec_alloc_context3,avcodec_free_context。在学习使用过程中由于网络上的教程或购买的书籍中的内容不一致很容易陷入开发学习错误的陷阱。
假设开发者通过一段时间的学习,将所有需要使用的API区分开来并能够熟练编程。此时,开发者如果没有足够的码流相关的知识,那么又会陷入报错不知是什么原因的另一个陷阱中。在多媒体开发时需要了解视频抽象层(Network Abstraction Layer,NAL)和编码层(Video Coding Layer,VCL),且主要了解NAL。如果没有足够的知识,那么在FFmpeg在实现逻辑上的报错时可能就会束手无策。由图1所示,提示了使用了弃用的像素格式,但是却没有提示可以使用哪些像素格式,且使用格式AV_PIX_FMT_YUV420P能在头文件pixfmt.h中找到,还需要额外的手册说明哪些格式可以使用,学习难度陡峭。
图1 av_make_error_string报错
使用FFmpeg命令行开发实现
由于FFmpeg经过编译之后可以获得ffmpeg、ffplay和ffprobe可执行文件,通过命令行的方式即可实现各种码流处理功能。其中ffmpeg命令行支持的常用选项如下:
选项 | 参数 | 功能 |
-re | 控制码流速度 | |
-i | - | stdin标准输入 |
-i | 多媒体文件 | 指定输入文件 |
-c:v和-c:a | 编码类型 | 修改视频音频编码 |
-f | 封装类型 | 修改封装 |
表1 ffmpeg命令行部分常用选项参数
当需要开发实时图像处理与推流经过处理后的图像为码流数据时,通过stdin的方式向ffmpeg的推流命令行进程中实时写入图像数据即可将码流推流至码流服务器(例用mediamtx作为RTSP码流服务器),如以下实现核心代码所示,在while(1)循环中持续读取图片文件写入ffmpeg进程的标准输入。
command_line = 'ffmpeg -loglevel error -re -framerate {} -i - -c:v libx264 -f rtsp {}'.format(1/frame_duration, RTSP_URL) command_list = shlex.split(command_line) process = subprocess.Popen(command_list, stdin=subprocess.PIPE, text=False) while(1) { with open(real_time_picture, 'rb') as f: process.stdin.write(f.read()) process.stdin.flush() process.stdin.write('q\n'.encode()) process.stdin.flush() } |
在上述实现中,退出process进程需要输入字符q。通过查看FFmpeg的源代码也可以得知,在输入字符q之后,ffmpeg进程需要结束一些处理,同时有必要(ffmpeg保存媒体文件等)时还需要写入文件尾来结束ffmpeg进程处理。
图2 键盘输入q后完整退出ffmpeg进程
命令行ffmpeg也支持通过文件名模式,如pic_name%d.jpg作为选项-i的参数持续读取文件夹中的图片并推流。但是这种方式在进程运行之后就只能遍历完匹配文件名的图片并停止,如果ffmpeg边读取图片,同时修改图片会导致进程异常退出。而且,在windows系统下默认不支持多个进程同时访问修改一个文件。
总结
在需要实时获取图像处理结果的需求中,可以直接只用ffmpeg命令行的方式将实时处理的图片进行推流,通过播放码流的方式查看图片处理结果,并大幅度加速需求的开发进度,减少由于在FFmpeg的API学习中的开发投入。同时,建议使用标准输入的方式将实时处理的图片送入ffmpeg命令行进程和RTSP推流的方式将码流推送至码流服务器,高效利用标准输入的灵活性避免其他问题的出现。