Skip to content

RTSP & WebRTC

real-time-stream-protocal and web real-time communication

就我的理解,WebRTC 支持渲染 RTSP 流,但是在这基础上增加了协商握手的过程,也就是 signaling。我之前不理解为什么浏览器不直接支持 RTSP,其实是因为流协议还未标准化的缘故。

协商握手:peerToPeer,规范不会要求你通过什么方式握手,只要能拿到一串 sdp(session description)字符并设置即可。

sdp 里具体有什么呢?关于音像文件的 address, timing, codec。codec (a blend word derived from "coder-decoder") is a program, algorithm, or device that encodes or decodes a data stream.

video Codecs Supported: H264; Audio Codecs Supported: pcm alaw and pcm mulaw

所以总流程相当于:

// 1.
const pc = new RTCPeerConnection();

pc.onnegotiationneeded = async () => {
  // 3. 这一段来启动本端
  let offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
  // 4. 通过某种方式拿到另一端的 sdp 并设置
  $.post(
      '../receiver/' + suuid,
      {
        suuid: suuid,
        data: btoa(pc.localDescription.sdp),
      },function (data) {
          pc.setRemoteDescription(
            new RTCSessionDescription({
              type: 'answer',
              sdp: atob(data),
            }),
          );
      },
    );
}
//....以上行为称为 Signaling

pc.ontrack = function (event) {
  // 5. event 为 WebRTCTrackEvent 其 track 字段给 stream 对象 add 上,
  // 显然一个 stream 可以接受好多 track
    let stream = new MediaStream();
    stream.addTrack(event.track);
  // 6. video 标签可以播放此 stream 了
    videoNode.srcObject = stream;
  };

// 2. 不明应设
pc.addTransceiver('video', { direction: 'sendrecv',});
pc.addTransceiver('audio', { direction: 'sendrecv',});

RTSPToWebRTC

  • go 服务向远端 RTSP 的请求过程
  conn, err := net.DialTimeout("tcp", client.pURL.Host, client.options.DialTimeout)
  client.conn = conn
  client.connRW = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
  // 接下来通过 TCP 连接,go 服务向远端连续发出了好几个请求,解读应当是遵循 rtsp 协议的
  // round 1: options
  2022/06/23 11:06:38 [RTSP/1.0 200 OK
  CSeq: 1
  Date: Thu, Jun 23 2022 03:01:37 GMT
  Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER

  ]
  2022/06/23 11:07:21 [DESCRIBE rtsp://192.168.2.53:8557/h264 RTSP/1.0
  CSeq: 2
  Accept: application/sdp
  User-Agent: Lavf58.76.100

  ]
  // round 2: DESCRIBE
  2022/06/23 11:07:21 [DESCRIBE rtsp://192.168.2.53:8557/h264 RTSP/1.0
  CSeq: 2
  Accept: application/sdp
  User-Agent: Lavf58.76.100

  ]
  2022/06/23 11:09:21 [RTSP/1.0 200 OK
  CSeq: 2
  Date: Thu, Jun 23 2022 03:07:27 GMT
  Content-Base: rtsp://192.168.2.53:8557/h264/
  Content-Type: application/sdp
  Content-Length: 691

  v=0
  o=- 18278359 1 IN IP4 192.168.2.53
  s=RTSP/RTP stream from VZ Smart-IPC
  a=rtpmap:8 PCMA/8000/1
  a=fmtp:8 complaw=al
  i=h264
  t=0 0... // 后面这一堆数据就是 sdp
  // round 3: track1 setup
  2022/06/23 11:11:56 [SETUP rtsp://192.168.2.53:8557/h264/track1 RTSP/1.0
  CSeq: 3
  Transport: RTP/AVP/TCP;unicast;interleaved=0-1
  User-Agent: Lavf58.76.100

  ]
  2022/06/23 11:13:25 [RTSP/1.0 200 OK
  CSeq: 3
  Date: Thu, Jun 23 2022 03:11:57 GMT
  Transport: RTP/AVP/TCP;unicast;destination=192.168.2.128;source=192.168.2.53;interleaved=0-1
  Session: 25E22894;timeout=60

  ]
  // round 4: track2 setup
  2022/06/23 11:14:27 [SETUP rtsp://192.168.2.53:8557/h264/track2 RTSP/1.0
  CSeq: 4
  Transport: RTP/AVP/TCP;unicast;interleaved=2-3
  User-Agent: Lavf58.76.100
  Session: 25E22894

  ]
  2022/06/23 11:20:23 [RTSP/1.0 200 OK
  CSeq: 4
  Date: Thu, Jun 23 2022 03:20:24 GMT
  Transport: RTP/AVP/TCP;unicast;destination=192.168.2.128;source=192.168.2.53;interleaved=2-3
  Session: 629DD327;timeout=60

  ]
  // round 5: play
  022/06/23 11:20:23 [PLAY rtsp://192.168.2.53:8557/h264/ RTSP/1.0
  CSeq: 5
  User-Agent: Lavf58.76.100
  Session: 629DD327

  ]
  2022/06/23 11:20:23 [$0��� ����<�X�������� ��(none)$0���}���<ۗ�J�����}�(none)RTSP/1.0 200 OK
  CSeq: 5
  Date: Thu, Jun 23 2022 03:20:24 GMT
  Range: npt=0.000-
  Session: 629DD327
  RTP-Info: url=rtsp://192.168.2.53:8557/h264/track1;seq=46978;rtptime=2279775039,url=rtsp://192.168.2.53:8557/h264/track2;seq=23770;rtptime=1252527567,url=rtsp://192.168.2.53:8557/h264/track3;seq=0;rtptime=0

推荐看以下这篇博客:我第一次真正理解了 text based protocol!!

https://csrgxtu.github.io/2015/04/08/An-Introduction-To-RTSP/

  • toWebRTC

这一段越看越晕…我不理解为什么 rtsp 已经有很完善的握手和传递过程了,浏览器还要再制定一个 web 版的协议?

但是 go 里有一点我看明白啦 —> 中间服务器向远端拿来的数据会缓存进通道里**make**(chan *av.Packet, 3000),然后这个通道一有数据,会再吐出来分发给各个消费者:

  // 从远端拿来存
  client.OutgoingProxyQueue <- &content

  // 分发
  select {
   case packetAV := <-RTSPClient.OutgoingPacketQueue:
    if AudioOnly || packetAV.IsKeyFrame {
     keyTest.Reset(20 * time.Second)
    }
    Config.cast(name, *packetAV)
   }
  }

  func (element *ConfigST) cast(uuid string, pck av.Packet) {
   element.mutex.Lock()
   defer element.mutex.Unlock()
   for _, v := range element.Streams[uuid].Cl {
    if len(v.c) < cap(v.c) {
     v.c <- pck
    }
   }
  }

Ref

https://webrtc.org/getting-started/turn-server?hl=en

https://codelabs.developers.google.com/codelabs/webrtc-web#0

chrome://webrtc-internals/