ICE连接在WebRTC中失败

时间:2019-02-25 16:45:51

标签: javascript python webrtc sdp

我们正在尝试将浏览器(客户端)与aiortc库(服务器一起发送单个视频流)相连接。当前,连接已成功建立(在signalingstatechange稳定时)。但是,由于ICE连接失败,因此从未建立媒体连接。两台主机位于同一LAN上,并且已经验证了直接连接。二手的STUN服务器是stun.l.google.com:19302。

服务器登录如下:

DEBUG:asyncio:Using selector: EpollSelector

<RTCSessionDescription is received>

DEBUG:ice:Connection(0) protocol(0) connection_made(<_SelectorDatagramTransport fd=11 read=idle write=<idle, bufsize=0>>)
DEBUG:ice:Connection(0) protocol(0) > ('66.102.1.127', 19302) Message(message_method=Method.BINDING, message_class=Class.REQUEST, transaction_id=b'\xb1\x1f\xdek(\x9c\x10\xe8\x86\xd1Wn')
DEBUG:ice:Connection(0) protocol(0) < ('66.102.1.127', 19302) Message(message_method=Method.BINDING, message_class=Class.RESPONSE, transaction_id=b'\xb1\x1f\xdek(\x9c\x10\xe8\x86\xd1Wn')

<RTCSessionDescription is sent>

<candidate is received>

Raw messages:
SDP offer:
v=0
o=- 7142951019689507792 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic: WMS
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:A5nH
a=ice-pwd:P0LIjOkkOl/wTtup5n1JA1mC
a=ice-options:trickle
a=fingerprint:sha-256 E3:8E:87:44:E8:D3:A7:02:C0:70:EA:D8:7F:84:F6:80:17:42:33:07:D8:C3:5D:44:FF:DC:25:4C:68:7E:09:19
a=setup:actpass
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 urn:3gpp:video-orientation
a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:10 http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 H264/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:123 rtx/90000
a=fmtp:123 apt=102
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:122 rtx/90000
a=fmtp:122 apt=127
a=rtpmap:125 H264/90000
a=rtcp-fb:125 goog-remb
a=rtcp-fb:125 transport-cc
a=rtcp-fb:125 ccm fir
a=rtcp-fb:125 nack
a=rtcp-fb:125 nack pli
a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=125
a=rtpmap:108 red/90000
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:124 ulpfec/90000

SDP answer:
v=0
o=- 3760099490 3760099490 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic:WMS *
m=video 36695 UDP/TLS/RTP/SAVPF 96 97 100 101 127 122
c=IN IP4 147.175.160.240
a=sendonly
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=mid:video
a=msid:25c4a009-f81c-4fe7-b811-a39c525da778 6ffba6fa-08fb-401f-9555-8df85942bd71
a=rtcp:9 IN IP4 0.0.0.0
a=rtcp-mux
a=ssrc-group:FID 461163217 873864728
a=ssrc:461163217 cname:{af4834d3-8db4-41cc-a9cd-6565cb8d82bd}
a=ssrc:873864728 cname:{af4834d3-8db4-41cc-a9cd-6565cb8d82bd}
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtcp-fb:96 goog-remb
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:100 H264/90000
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtcp-fb:100 goog-remb
a=fmtp:100 packetization-mode=1;level-asymmetry-allowed=1;profile-level-id=42001f
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 H264/90000
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=rtcp-fb:127 goog-remb
a=fmtp:127 packetization-mode=1;level-asymmetry-allowed=1;profile-level-id=42e01f
a=rtpmap:122 rtx/90000
a=fmtp:122 apt=127
a=candidate:a2cdfa74ef4f12d00d150bfa588cf805 1 udp 2130706431 147.175.160.240 36695 typ host
a=candidate:237395b2276e331724a06e8dbed92f44 1 udp 1694498815 147.175.160.240 36695 typ srflx raddr 147.175.160.240 rport 36695
a=end-of-candidates
a=ice-ufrag:IHJz
a=ice-pwd:RNwQMxbAm2EjNIBxklFZKd
a=fingerprint:sha-256 9B:BB:E0:63:DE:5D:BC:1F:CC:A2:FA:FA:0B:29:EB:F1:46:24:D6:A4:E7:9C:B3:C1:50:9D:A3:FB:83:EC:74:2D
a=setup:active

{"candidate":"candidate:1963203886 1 udp 2113937151 147.175.160.205 36583 typ host generation 0 ufrag A5nH network-cost 999","sdpMid":"video","sdpMLineIndex":0,"type":"candidate"}

服务器代码:

class VideoImageTrack(VideoStreamTrack):
    def __init__(self):
        super().__init__()

    async def recv(self):
        pts, time_base = await self.next_timestamp()
        # create frame
        return frame

async def run(pc, recorder, signaling, session_id):
    def add_tracks():
        pc.addTrack(VideoImageTrack())

    # consume signaling
    while True:
        obj = signaling.receive()
        if isinstance(obj, RTCSessionDescription):
            await pc.setRemoteDescription(obj)
            await recorder.start()
            if obj.type == 'offer':
                # send answer
                add_tracks()
                await pc.setLocalDescription(await pc.createAnswer())
                signaling.send(pc.localDescription)
        elif isinstance(obj, RTCIceCandidate):
            pc.addIceCandidate(obj)
        else:
            print('Exiting')
            break

def livestream(session_id,config):
    # create signaling and peer connection
    signaling = Signaling()
    pc = RTCPeerConnection()
    # create media sink
    recorder = MediaBlackhole()
    # run event loop
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        loop.run_until_complete(run(
            pc=pc,
            recorder=recorder,
            signaling=signaling,
            session_id=session_id))
    except KeyboardInterrupt:
        pass
    finally:
        loop.run_until_complete(recorder.stop())
        loop.run_until_complete(pc.close())

前端代码(重要部分):

var configuration = { iceServers: [
      {urls: 'stun:stun.l.google.com:19302'}]
};
this.pc = new RTCPeerConnection(configuration);


const offerOptions = {
      offerToReceiveVideo: 1,
      offerToReceiveAudio: 0
};

let offer = this.pc.createOffer(offerOptions);
offer.then(async value => {
  await this.pc.setLocalDescription(value);
  this.connection.send(JSON.stringify(value))
});


this.connection.onmessage = async event => {
let data = JSON.parse(event.data);
if (data) {
    if (data.type === 'offer') {
      await this.pc.setRemoteDescription(data);
      await this.pc.setLocalDescription(await this.pc.createAnswer());
      this.connection.send({ 'desc': this.pc.localDescription }.toString());
    } else if (data.type === 'answer') {
      await this.pc.setRemoteDescription(data);
    }
  }
};

this.pc.onnegotiationneeded = async () => {
  await this.pc.setLocalDescription(await this.pc.createOffer());
  this.connection.send({ 'desc': this.pc.localDescription }.toString());
};

this.pc.onicecandidate = (event) => {
    if (event.candidate !== null) {
      let data = JSON.parse(JSON.stringify(event.candidate));
      data['type'] = 'candidate';
      this.connection.send(JSON.stringify(data));
    }
};

1 个答案:

答案 0 :(得分:0)

我们的实现存在几个问题:

  1. 从服务器端删除了异步调用-则某些aiortc协程未正确运行
  2. 在前端缺少对“候选”类型消息的处理
  3. 错误的消息{'desc':this.pc.localDescription} .toString()格式-应该为JSON.stringify({desc:this.pc.localDescription})

我们设法解决了所有问题,因此我关闭了这个问题。