背景:zlmediakit作为流媒体服务,我们应用最多的是将rtsp、rtmp、gb28181等与摄像机终端设备相关的安防协议转为flv、webrtc等可以在浏览器播放的协议。最近在工作中遇到并实现了HTTP长连接图片流播放的需求,即扩展zlmediakit实现视频解码并通过HTTP长连接方式对外提供jpg图片流。虽然这和现在主流的webrtc等媒体流视频播放相比显得非主流。但也有其价值和应用场景:1)可以解决H265视频在前端难以直接播放的问题;2)前端便捷集成视频播放的场景(毕竟只要一个http地址和img标签即可,无需集成播放器)。
一方面觉得这个需求比较有意思,能解决一些场景的切实问题。另一方面在实现的过程中也是对zlmediakit源码的又一次深入了解和扩展。因此记录此实现过程,并希望这个帖子有其存在的意义。
-------------- 实现思路 ---------------
1。想要实现http-jpg图片流主要有两个核心的内容:1)实现一个HTTP长连接服务端;2)将视频流转为jpg图片。
2。zlmediakit提供有http-flv视频输出协议,输出的是flv协议的长连接视频流。可以借鉴参考它的http-flv的实现模式。
3。zlmediakit主要是进行流媒体协议各种转封装。实现http-jpg图片流,还需要在zlm中增加一个将视频流转为jpg图片的解码器。(已有经验)
-------------- 实现方式 ---------------
1.查看zlmediakit的HttpSession对象。zlm作为服务端,对每一个客户端发起的HTTP请求,都会分配一个session对象去进行相应的处理逻辑。
2.HttpSession对象继承的有tookit::Session,FlvMuxer、HttpRequestSplitter、WebSocketSplitter等父类。其中tookit::Session是负责处理tcp连接相关操作。FlvMuxer是将rtmp协议的视频流进行flv封装操作,HttpSplitter实现的是对Http数据的分包处理,WebSocketSplitter实现的是对websocket数据的分包处理。
3.查看其onHttpRequest_GET()函数,里面实现了checkLiveStreamFlv、checkLiveStreamTS等函数。我们仿照着扩展一个checkLiveStreamJpeg函数。
4.各个chekcLiveStreamXXX函数,都调用的是checkLiveStream函数,在这个函数里根据HTTP请求URL来判断是哪种协议的输出类型。设置HTTP请求后缀为".live.jpeg"时认为是图片流。
5.分析checkLiveStream函数的实现逻辑,实现了从HTTP请求URL中解析出app和stream字段,查找到对应的对应的MediaSource媒体对象。里面还封装了播放鉴权、查找流失败处理等。
6.上接第4步,当查找到MediaSource媒体流时,执行自己封装的start_pushJpeg函数,启动推送图片流。
7.实现方式是模仿FlvMuxer实现一个PushJpeg对象,将图片流相关的操作进行封装。作为父类让HttpSession对象继承,在HttpSession对象中只需要调用start_pushJpeg函数即可。
8.核心的处理逻辑在start_pushJpeg函数中。里面实现了构造并发送HTTP头,创建FFMPEG解码器,订阅MediaSourece的媒体流等方法。
9.在onWriteJpegHeader函数中,发送HTTP的头部信息。在发送的HTTP头部信息中,设置Content-Type为multipart/x-mixed-replace,并设置boundary分割符。通知浏览器不断更新接收到的图片。客户端接收到的HTTP长连接回复数据大致如下。发送完HTTP头之后,后面就逐张发送JPG图片二进制流即可。
10.在start_pushJpeg函数就需要用到zlmediakit中获取媒体流的基本使用方法。1)找到媒体流的reader指针。2)设置setReadCB视频帧读取回调。3)设置setMessageCB消息回调(非必须)
11.在setReadCB视频帧回调函数中,将得到的视频流放入到onRtmpVideoFrame函数中进行帧处理。
12.在onRtmpVideoFrame函数中实现将获取到的zlm内部的原始帧,转为ffmpeg解码器认识的frame格式。包括提取原始帧的类型(H265/H264),提取pts和dts,添加帧分割等操作。最终将视频帧放入到解码器中进行解码。
13.ffmpeg解码器的实现不再赘述。需要说明的是在初始化ffmpeg解码器时,可以根据VideoTrack的信息选择H265解码器或者H264解码器。
14.视频流通常为每秒25帧,但是人眼对每秒15张图片就认为是连续的。因此在setOnDecode解码回调中可以进行抽帧处理,每秒只发送15张图片即可。
15.在我这次的实现过程中,还结合了算法分析服务的数据,对图片叠加上分析得到的检测框、文字等信息。采用的是opencv方式叠加。不再过多展开。
16.在onWriteJpegBody函数中,实现了对图片的裁剪、编码成jpg二进制流、添加分割符等操作。最终调用onWrite函数,实现将图片二进制流对外发送。
17.剩余还有一些如http断开、流断开等异常处理。不再赘述。至此就完成了在zlmediakit中扩展实现对外提供HTTP图片流的主体功能。