Jul. 13, 2019

可能与最近几月坚持跑步有关,头脑突然灵活了很多。

前几日闲来无事摸鱼时,突然就翻回到了NodeMediaServer(NMS)的代码。从服务启动,到视频的解析推流,大致三到四个文件,脉络清晰。NMS的文件结构给了我很大的启发。我突然就想到,完全可以按照NMS的结构,把JT1078的解析也整合进去。

想到去年年底,第一次接触JT1078,视频拉流推流这些概念的时候,完全是一脸懵比。当时准备在NMS上改造,也是在视频推流之后,在NMS解析视频包的位置进行后续处理。甚至研究了一段时间的RTMP握手,由于设备推流不是RTMP协议,在代码里还要判断,如果是设备推流过来的,要跳过RTMP握手阶段,再推流出去。现在回想起来,甚是痛苦。好在现在知道了如何使用FFmpeg作为推流工具,使得这一功能实现。

今日,我仿照NMS的结构,和前几篇文章提到的FFmpeg命令,主要以FFmpeg推流实现的JT1078解析代码推到了仓库里。明显这不是最优的方案。其中一个很大的问题就是一个通道就要启动三个子进程(视频,音频和合并音视频),一般每台设备会有六个通道,观看一个设备的视频直播时就要启动大量的FFmpeg,相当耗费资源。而且这些子进程管理起来比较麻烦,有可能随时某一个在收不到数据时的进程关闭,就会出现 EPIPE 错误。而我理想中的最佳方案并不是这样的。最初,我尝试将设备推流过来的视频和音频数据,分别使用NMS原代码中的RTMP封包函数处理,送到播放者的socket中去,但是并没有成功。尝试几轮,服务端没有报错,看起来成功了,但是播放端并没有数据。未找到原因在哪,就暂时转换思路,使用FFmpeg来实现了。

后来又想到,直接使用NMS的封包函数是不行的。视频数据倒是不需要额外处理,音频数据是G7xx格式的,RTMP并不支持,所以需要转码。看来代码层面的封包和解码还需要研究一段时间。

唉!现在是不能够用代码直接封装音视频包,只得使用FFmpeg命令。我要是懂得音视频解封装,还能吃这个亏?

Jul. 16, 2019

今日,研究了一下FFmpeg的命令,想到可以使用两条输入流(视频和音频)转化成一条输出流(RTMP)的方式实现转码和推流。这样就能够不创造出上一方案中多出来的视频和音频子进程了。在将设备的视频和音频流数据,分别存储下来后,使用FFmpeg命令推流

1
ffmpeg -i test.264 -i test.mp3 -map 0:v -map 1:a -c:v copy -c:a copy -f flv rtmp://xxxx:1935/live/stream_xxxx

命令执行成功,证明了这种方案可能行。于是,我将代码改造为,先将数据缓存,然后使用FFmpeg推流。谁知,实际情况是,缓存数据太小,FFmpeg进程启动后,瞬间就完成了推流,然后关闭了进程。造成的现象就是程序完全没有在(持续)推流。如果FFmpeg随时都要启动的话,有可能会遇到,总将文件从头开始推流的问题。遂暂时搁置了这种方案。

根据上一方案存在的问题,又想到了启动子进程后,可以使用写入 stdin 的方式,将数据写入FFmpeg的进程中。但是并没有找到NodeJs写入两条输入流到一个子进程中去的方法。就算是将视频流利用管道符串接到音频流的转码进程中,还是需要有两条输入流。看来这种方案暂时也不行了。

无意中翻到了 fluent-ffmpeg 这个库,好像有支持两条输入流的接口,待有时间再测试。

Dec. 31, 2019

万万没想到,在2020以前我能将上面的问题解决。 先忘记fluent-ffmpeg这件事吧
注: 以下内容适用于Linux系统
解决方案仍然是,将视频和音频分流后存储为文件,不同的是文件类别为pipe文件,即命名管道文件(Named Pipe).它遵循先进先出原则(FIFO),可以像普通文件一样管理。
可以使用以下命令创建管道文件:

1
2
$ mkfifo /tmp/one.pipe
$ mkfifo /tmp/two.pipe

然后将程序分离出的视频数据和音频数据像写文件一样写入即可,ffmpeg的进程则从这两个文件中读取数据,再转换成rtmp推流发送出去。
ffmpeg命令如下:

1
$ ffmpeg -loglevel panic -probesize 32 -re -r 16 -f h264 -i /tmp/one.pipe -f mp3 -i /tmp/two.pipe -map 0:v -map 1:a -c:v copy -c:a aac -strict -2 -preset ultrafast -tune zerolatency -f flv rtmp://xxxx:1935/live/stream_xxxx

读入的文件需要指定格式,如h264和mp3(在上一步中将g726转为了mp3).
具体代码实现见我此次提交 commit