node.js實現ffmpeg動態切換輸入源推流(不關閉進程)

node.js自己已經封裝了ffmpeg了,但是依然沒有解決動態改變輸入源的方法,因爲ffmpeg同一個進程只接受一個輸入源,這個輸入源可以是文件路徑,也可以是一個可讀的流數據。起初的想法是用開啓一個進程的方法

const spawn = require('child_process').spawn

我們來操作命令去不斷結束上一次操作然後重新操作指令,但是我們推流到服務器的話,發現服務器要重新接受數據,而且如果操作頻繁的話,對於關閉開啓的子進程比較麻煩,在用進程命令調用ffmpeg時,因爲,ffmpeg也是一個進程,所以一旦執行命令,就說額外開啓了兩個進程,關閉進程很麻煩。

const Ffmpegs = require('fluent-ffmpeg')

後來發現node.js本身是有封裝的對ffmpeg。用法如下

	playsure = new Ffmpegs()
    playsure.input('E://KuGou//08.mp3')
    playsure.inputOption('-re')
    playsure.outputOptions([
      '-rtsp_transport',
          'tcp',
          '-f',
          'rtsp'
   ])
    playsure.output('rtsp://47.103.130.92:554')
    playsure.run()
    playsure.on('end', () => {
      console.log('結束end')
    })

進行向特定服務器推流。我們指定了一幀一幀的推,且是tcp方式推送rtsp流。
這樣也實現了推流的效果,但是當你重新指定輸入源的時候是不允許的,需要先結束之前的ffmpeg在指定ffmpeg輸入源,這樣還是要先斷開一次連接,服務器那邊還要重新接受數據纔行。
後來想到用管道來傳輸,想的只用改變輸入端的數據,將管道的輸出端放到ffmpeg的輸入端,只要不關閉管道,因該就可以實現輸入源的切換,進行了多方搜索,發現在node.js中只能有

var fs = require('fs')
var rs = fs.createReadStream('E://KuGou//08.mp3', { highWaterMark: 4608 })
var ws = fs.createWriteStream('E://KuGou//')
fs.pipe(ws)

這兩個數據流進行管道操作,也有雙工流,是有讀寫功能的,但是沒有研究出來同時讀寫操作。
顯然我們操作ffmpeg需要的是能讀能寫的管道,而且不能關閉。後來想到了我們本來就是用的tcp協議的,tcp的socket也可以充當管道作用,它也是用來分發數據的。於是乎我又寫了tcp客戶端和服務器接收端專門用socket來充當管道。

server:

var net = require('net')
var  TcpServer = net.createServer()
    TcpServer.listen(3000, function () {
      console.log('tcp_server listening 3000')
    })
   
    TcpServer.on('connection', (Socket) => {

	      if (trsocket) {
	       var playsure1 = new Ffmpegs()
	        playsure1.input(Socket)
	        playsure1.inputOption('-re')
	        playsure1.outputOptions([
	          '-rtsp_transport',
	          'tcp',
	          '-f',
	          'rtsp'
	        ])
	        playsure1.pipe('rtsp://47.103.130.92:554')
	      }

      Socket.on('data', (data) => {
        console.log('接受到數據:')

        // console.log('read data ', data)
      })
        .on('close', () => {
          console.log('close server')
        })
        .on('error', (error) => {
          console.log('error :' + error)
        })
    })

client

var options = {
  host: '127.0.0.1',
  port: 3000
}
var Tcpclient = new net.Socket()
 	Tcpclient.connect(options, () => {
 	var playsure = new Ffmpegs()
     var rs = fs.ReadStream('E://KuGou//08.mp3')
        rs.pipe(Tcpclient)
      console.log('開始發送數據:')
    })

這個可以實現tcp傳輸了,但是當一首歌傳完,socket盡然也自動斷開連接了。無語。。。。
還是server主動斷開的。
後來嘗試去找ffmpeg傳輸結束髮出的信號,發現它就是會自動斷開連接。於是我將它的設置更改一下

playsure1.pipe('rtsp://47.103.130.92:554', { end: false, autoClose: false })//不讓它結束後斷開連接

對ffmpeg的輸出進行了設置。但是發現還是會斷開socket,於是想是不是和輸入端有關
對輸入端進行更改

rs.pipe(Tcpclient, { end: false, autoClose: false })

是的它不斷開連接,感覺接近成功了。
但是我去操作輸入文件的時候發現不行,還是改不了輸入源(可能方法不對,後面沒有直接更改文件)
考慮到我們要控制輸入源,還要實現隨時停止等操作,要對輸入傳輸速度有要求。想到ffmpeg本身就可以設置輸出格式,於是乾脆在客戶端直接在創建一個ffmpeg來控制傳輸速度和格式客戶端的代碼改變如下

	var Tcpclient = null
    Tcpclient = new net.Socket()
   var playsure = new Ffmpegs()
    Tcpclient.connect(options, () => {
      playsure.input('E://KuGou//08.mp3')
      playsure.inputOption('-re')
      playsure.outputOptions([
        '-rtsp_transport',
        'tcp',
        '-f',
        'mp3'
      ])
      playsure.output(Tcpclient, { end: false, autoClose: false })
      playsure.run()

因爲現在socket已經不會斷開了,所以我們可以更改輸入源的數據了,client裏面的ffmpeg只是用來控制數據格式的,我們只用記錄好當前連接的socket只向它裏面傳輸數據就可以了。client中的ffmpeg我們可以隨意斷開,進行切換傳到socket中的數據進而實現動態切換ffmpeg的輸入數據的,當ffmpeg傳輸結束會拋出end信號

 playsure.on('end', () => {
       console.log('數據傳輸結束')
      })

關閉ffmpeg在node.js中我使用kill()來結束的,但是ffmpeg結束是結束了,就是會跑出來一個錯誤

Error: ffmpeg was killed with signal SIGKILL

目前還沒有對它進行研究,我是直接把它try掉了,沒有管,下次重新調用的時候也沒有問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章