之前我们做一个视频点播的功能,大概的流程就是将上传上来的各种格式的视频,用FFmpeg统一进行一次转码,如果probe到视频的编码格式是H.264就调用-vcodec copy,如果probe到视频的编码格式不是H.264就调用-vcodec libx264,音频就统一调用 -acodec aac,这样客户端每上传一个新的视频文件,服务端就调用ffmpeg.exe或者ffmpeg可执行文件,执行一次ffmpeg.exe -i xxx.avi -vcodec copy -acodec aac -f hls xxx.m3u8这样的流程,这样就能很好地将各种不同格式的视频进行统一转码、存储和点播了。
功能是搞定了,但是不是总感觉不够高大上,程序这么写是不是感觉有点low~
后来,做EasyDSS流媒体服务器的拉转推,又是差不多的需求,把各种网络流格式(例如RTSP/RTMP/HTTP/UDP等各种格式)拉到EasyDSS再转推到RTMP服务里面,形成一个本地化的流分发,这就像RTMP CDN里面的边缘节点从中心节点取流分发或者像酒店视频系统里面的外部视频资源转内部视频资源分发,这一套下来,基本上的做法就跟上面的点播过程差不多,1、把视频源从文件换成了网络流地址;2、把视频HLS文件输出换成了RTMP推流输出;至于里面什么各种网络流的协议适配,RTMP推流协议的开发,都丢给ffmpeg.exe去做,每来一个"拉转推"的条目需求,我们就启动一个ffmpeg.exe完成这项工作,这样功能不也就完成了吗?
功能又搞定了,但还是感觉有点low~,难道100个节目就让100个ffmpeg.exe来完成吗?中间出现网络错误中断了怎么办,又重启ffmpeg.exe?
基于以上两个功能需求,我在想,ffmpeg.exe其实就是ffmpeg.c里面的main(char* parameter)方法,对不对,如果说我把main入口改造成一个FFMPEG对象instance接口,例如:instance.start(char* parameter),传进去的参数还是跟ffmpeg.exe命令后面的参数一样的,只不过从原来的进程调用方式,改成了接口调用方式,至少进程维护比以前简单了。
就基于这种想法,我们就开始改造ffmpeg.c,做封装定义,于是就定义了一套适合各种场景的接口流程:
方法名称 | 说明 |
---|---|
EasyAVFilter_Create | 创建句柄,相当于创建了一个ffmpeg.exe |
EasyAVFilter_Release | 释放句柄 |
EasyAVFilter_SetCallback | 设置回调函数和自定义指针,回调过程中的各种媒体信息/连接信息/转码进度 |
EasyAVFilter_AddInput | 添加输入参数(源地址) |
EasyAVFilter_AddFilter | 添加中间参数,如:转码,兼容ffmpeg命令所有参数(例如-vcodec copy -acodec aac) |
EasyAVFilter_SetOutput | 设置输出参数(目标地址) |
EasyAVFilter_GetFilters | 获取所有参数(review参数输入是否正确) |
EasyAVFilter_Start | 开始工作 |
EasyAVFilter_Stop | 停止工作 |
这样,整个ffmpeg的调用流程就清爽了,不用再去关注一大堆的avfilter、avcodec过程了,而且也不用费劲维护一堆ffmpeg.exe,音视频的开发难度直线下降,后面我们举几个例子,实战一下,看看效果!