一.视频推流与拉流

推流,指的是把采集阶段封包好的内容传输到服务器的过程。

java rtmp 推流 拉流 rtmp推流与拉流_ide

拉流,指的是服务器已有直播内容,用指定地址进行拉取的过程

java rtmp 推流 拉流 rtmp推流与拉流_数据_02

二. 主流的推送协议和优缺点

1.RTMP协议 (1)是流媒体协议。 (2)RTMP协议是 Adobe 的私有协议,未完全公开。 (3)RTMP协议一般传输的是 flv,f4v 格式流。 (4)RTMP一般在 TCP 1个通道上传输命令和数据。 (5) 优势在于低延迟,稳定性高,支持所有摄像头格式,浏览器加载 flash插件就可以直接播放。 (6) 不支持浏览器,且Adobe已不再更新。因此直播服务要支持浏览器的话,需要另外的推送协议支持。

2.RTSP协议 (1)是流媒体协议。 (2)RTSP协议是共有协议,并有专门机构做维护。. (3)RTSP协议一般传输的是 ts、mp4 格式的流。 (4)RTSP传输一般需要 2-3 个通道,命令和数据通道分离。 (5) RTSP实时效果非常好,适合视频聊天,视频监控等方向。 (6) RTSP虽然实时性最好,但是实现复杂,适合视频聊天和视频监控;RTMP强在浏览器支持好,加载flash插件后就能直接播放,所以非常火,相反在浏览器里播放rtsp就很困难了。

3.HLS协议 Http Live Streaming是由Apple公司定义的基于HTTP的流媒体实时传输协议。它的原理是将整个流分为多个小的文件来下载,每次只下载若干个。服务器端会将最新的直播数据生成新的小文件,客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。基本上,HLS是以点播的技术实现了直播的体验。因为每个小文件的时长很短,客户端可以很快地切换码率,以适应不同带宽条件下的播放。 分段推送的技术特点,决定了HLS的延迟一般会高于普通的流媒体直播协议。 传输内容包括两部分:一是M3U8描述文件,二是TS媒体文件。TS媒体文件中的视频必须是H264编码,音频必须是AAC或MP3编码。 由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短。

4.HTTP协议 (1)不是是流媒体协议,通常用来做点播 (2)HTTP协议是共有协议,并有专门机构做维护。 (3)HTTP协议没有特定的传输流。 (4)HTTP传输一般需要 2-3 个通道,命令和数据通道分离。

5.WebRTC WebRTC(Web Real-Time Communication),即“源自网页即时通信”。WebRTC是一个支持浏览器进行实时语音、视频对话的开源协议。WebRTC的支持者甚多,Google、Mozilla、Opera推动其成为W3C推荐标准。WebRTC支持目前的主流浏览器,并且基于SRTP和UDP,即便在网络信号一般的情况下也具备较好的稳定性。此外,WebRTC可以实现点对点通信,通信双方延时低,此外,WebRTC可以实现点对点通信,通信双方延时低,是实现“连麦”功能比较好的选择。

RTMP、RTSP的区别 前提知识:从网络上接收视频时首先要解协议(RTSP/RTMP/HTTP),然后是解格式(MKV,RMVB),之后才是将视频(H264)和音频(AAC)格式数据分别解码为图像(RGB/YUV)和声音(PCM),再根据时间戳同步播放。总结:先解协议(RTSP/RTMP/HTTP),再解格式(MKV,RMVB),最后将视频(H264)和音频(AAC)格式数据解码为图像   RTSP+RTP主要用于IPTV,原因是传输数据使用的是UDP,在网络环境比较稳定的情况下,传输效率是比较高的;   RTMP主要用于互联网音视频传输,它使用的是TCP传输,因为互联网环境相对较差,采用RTMP保证了视频的传输质量,但是其传输延迟相对较高,传输效率相对较低。   使用RTMP技术的流媒体系统有一个非常明显的特点:使用 Flash Player 作为播放器客户端,而Flash Player 现在已经安装在了全世界将近99%的PC上,因此一般情况下收看RTMP流媒体系统的视音频是不需要安装插件的。用户只需要打开网页,就可以直接收看流媒体,十分方便。直播服务普遍采用了RTMP作为流媒体协议,FLV作为封装格式,H.264作为视频编码格式,AAC作为音频编码格式。FLV是RTMP使用的封装格式,H.264是当今实际应用中编码效率最高的视频编码标准,AAC则是当今实际应用中编码效率最高的音频编码标准。

拓展资料为什么现在的视频直播不使用RTSP协议而是使用RTMP?

三. websoket

看完让你彻底理解 WebSocket 原理,附完整的实战代码(包含前端和后端)html5-websocket初探websoket概念基础WebSocket协议深入理解基于WebSocket前端开发H.264流实现视频实时播放

webSoket概念总结:

WebSocket是为解决客户端与服务端实时通信而产生的技术。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信

java rtmp 推流 拉流 rtmp推流与拉流_java rtmp 推流 拉流_03

ArrayBuffer 和 Blob 对象ArrayBuffer类型化数组

java rtmp 推流 拉流 rtmp推流与拉流_java rtmp 推流 拉流_04

前端代码demo:

文件目录截图:

java rtmp 推流 拉流 rtmp推流与拉流_HTTP_05

<!DOCTYPE html>
<html>
<script src="wfs/wfs.js"></script>

<body>
    <div>
        <p>ip:<input id="host" value="192.168.0.133" /></p>
        <p>端口:<input id="port" value="9999" />
            <button id="btn" onclick="play()">连接</button>
            <button id="btn" onclick="reload1()">刷新</button>
        </p>
        <video id="video" style="height: 500px; width: 500px"></video>
    </div>
    <script>
        function play() {
            // console.log(1)
            // let ws = new WebSocket('ws://192.168.0.133:2345')
            // ws.onopen = function() {
            //     ws.send('start')
            // }
            // 除了动态判断收到的数据类型,也可以使用binaryType属性,显式指定收到的二进制数据类型

            // 收到的是 ArrayBuffer 数据 ArrayBuffer 对象代表储存二进制数据的一段内存
            // ArrayBuffer是js操作二进制数据的一个接口,它是以数组的语法处理二进制数据,也称二进制数组
            // ws.binaryType = 'arraybuffer'

            // 收到的是 blob 数据 binary Large Object (二进制大型对象)
            // Blob 对象表示一个二进制文件的数据内容,通常用来读写文件,比如一个图片文件的内容就可以通过 Blob 对象读写
            // ws.binaryType = "blob";
            
            // ArrayBuffer和blob的区别
            // Blob 用于操作二进制文件
            // ArrayBuffer 用于操作内存

            // ws.onopen = function () {
            //     ws.send('start')
            // }

            // ws.onmessage = function (msg) {
            //     console.log(typeof msg , msg.data) // object
            //     console.log(msg.data) // ArrayBuffer(80) {}
            //     console.log(msg.data.toString()) // [object ArrayBuffer]
            // }
            // ws.onclose = function (msg) {
            //     console.log(msg + "Connection closed.");
            // }
            if (Wfs.isSupported()) {
                const ip = document.getElementById('host').value
                const port = document.getElementById('port').value
                const video1 = document.getElementById('video')
                const wfs = new Wfs()
                wfs.attachMedia(video1, ip, port)
            }
        }
        function reload1() {
            window.location.reload()
        }
    </script>
</body>

</html>
后端服务器(使用node生成)

文件目录结构:

java rtmp 推流 拉流 rtmp推流与拉流_数据_06

const fs = require('fs')
const path = require('path')

const fileName = path.join(__dirname, 'b1.tlv')
const typeUseByte = 4 // 类型占用字节
const lengthUseByte = 4 // 长度占用字节
let readedByte = 0 // 已读已发送字节长度

const WebSocket = require('ws')
const wss = new WebSocket.Server({
//   host: 'localhost',
  host: '192.168.0.133',
  port: 9999,
  // perMessageDeflate: {
  //   serverMaxWindowBits: 20
  // }
})

let ws1

console.log('socket已打开')
wss.on('connection', function (ws) {
  console.log(ws)
  ws1 = ws
  ws.on('message', function(message) {
    console.log('server: 收到')
    fs.open(fileName, 'r', function(err, fd) {
      console.log('文件已打开')
      const array = []
      if (err) {
        throw err
      }
      fs.readFile(fileName, function(err, data) {
        console.log('文件已读取')

        if (err) {
          throw err
        }
        while (readedByte < data.length) {
          const typeBuffer = data.slice(readedByte, readedByte + typeUseByte)
          // typeBuffer = typeBuffer.reverse()
          const type = typeBuffer // hex2int(typeBuffer.toString('hex'))

          readedByte += typeUseByte
          let lengthBuffer = data.slice(readedByte, readedByte + lengthUseByte)
          // console.log(lengthBuffer)
          // console.log(lengthBuffer)
          const length = hex2int(lengthBuffer.reverse().toString('hex'))
          lengthBuffer = lengthBuffer.reverse()

          readedByte += lengthUseByte

          const val = data.subarray(readedByte - 8, readedByte + length)
          readedByte += length
          // console.log(val)
          // wss.clients.forEach(client => {
          //   if (client.readyState === WebSocket.OPEN) {
          //     client.send(val)
          //   }
          // })
          ws.send(val)
          array.push({
            type,
            length,
            val
          })
        }
        console.log('文件已发送')

        fs.close(fd, function() {
          readedByte = 0
          console.log('文件关闭')
        })
      })
    })
  })
  // ws.send('server: hello,client')
})

function hex2int(i) {
  let two = parseInt(i, 16).toString(2)
  const bitNum = i.length * 4
  if (two.length < bitNum) {
    while (two.length < bitNum) {
      two = '0' + two
    }
  }

  if (two.substring(0, 1) == '0') {
    two = parseInt(two, 2)
    return two
  } else {
    let two_unsign = ''
    two = parseInt(two, 2) - 1
    two = two.toString(2)

    two_unsign = two.substring(1, bitNum)
    two_unsign = two_unsign.replace(/0/g, 'z')

    two_unsign = two_unsign.replace(/1/g, '0')

    two_unsign = two_unsign.replace(/z/g, '1')
    console.log(parseInt(-two_unsign, 10))
    two = parseInt(-two_unsign, 2)

    return two
  }
}

服务端操作需要注意两点

1.如果使用的ws模块,那么需要安装node里的ws,npm install ws

详细请戳Websocket之ws模块(一)

java rtmp 推流 拉流 rtmp推流与拉流_ide_07

2.如果使用nodejs-websocket,需要先安装nodejs-websocket,npm install nodejs-websocket

java rtmp 推流 拉流 rtmp推流与拉流_ide_08

详细请戳nodejs-websocket 的简单用法和安装

2.在进行前端视频播放的时候,需要开启node服务指令, node index.js

注意事项

开启防火墙端口号,上述代码中我指定的webSoket通信端口号是9999。

记得在防火墙“出站规则”这里新建规则,选择端口号9999即可

java rtmp 推流 拉流 rtmp推流与拉流_java rtmp 推流 拉流_09

java rtmp 推流 拉流 rtmp推流与拉流_java rtmp 推流 拉流_10

java rtmp 推流 拉流 rtmp推流与拉流_数据_11

java rtmp 推流 拉流 rtmp推流与拉流_java rtmp 推流 拉流_12

java rtmp 推流 拉流 rtmp推流与拉流_HTTP_13

java rtmp 推流 拉流 rtmp推流与拉流_数据_14

完成之后可以看下自己设置的端口信息

java rtmp 推流 拉流 rtmp推流与拉流_HTTP_15