WebRTC:在带有基于 Go 的对等点的 Safari 上未调用 ontrack()

问题描述 投票:0回答:1

我正在尝试通过 WebRTC 将视频从我的 golang 后端(使用此 WebRTC 实现)流式传输到客户端浏览器。

我的实现适用于 Chrome,但不适用于 Safari,因为尽管远程对等点提供了视频轨道,但

RTCPeerConnection.ontrack()
回调从未被触发。 ICE 连接似乎成功。当使用 Safari 本身作为后端对等点时,会正常调用
ontrack()

我见过很多人都遇到过

ontrack()
没有被呼叫的问题,但他们都没有遇到这个具体问题。

这是一个最小的可重现示例。运行它:

  • index.html
    放入您的工作目录
  • 奔跑
    main.go
  • 使用 Chrome 访问
    localhost:8080
    ,注意控制台中打印的
    on track called!
    消息
  • 使用Safari访问页面,注意
    ontrack()
    没有被调用。

main.go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"

    "github.com/pion/webrtc/v3"
)

var localDescription webrtc.SessionDescription

func openConnection(offer *webrtc.SessionDescription) *webrtc.SessionDescription {
    peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{
        ICEServers: []webrtc.ICEServer{
            {
                URLs: []string{"stun:stun.l.google.com:19302"},
            },
        },
    })
    if err != nil {
        panic(err)
    }

    videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
    if err != nil {
        panic(err)
    }

    rtpSender, err := peerConnection.AddTrack(videoTrack)
    if err != nil {
        panic(err)
    }

    go func() {
        rtcpBuf := make([]byte, 1500)
        for {
            if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
                return
            }
        }
    }()

    peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
        fmt.Printf("Connection State has changed %s \n", connectionState.String())
    })

    // `offer` received from browser
    err = peerConnection.SetRemoteDescription(*offer)
    if err != nil {
        panic(err)
    }

    answer, err := peerConnection.CreateAnswer(nil)
    if err != nil {
        panic(err)
    }

    gatherComplete := webrtc.GatheringCompletePromise(peerConnection)

    if err = peerConnection.SetLocalDescription(answer); err != nil {
        panic(err)
    }

    <-gatherComplete

    return peerConnection.LocalDescription()
}

func main() {
    http.HandleFunc("/rtc", func(rw http.ResponseWriter, r *http.Request) {
        if r.Method != "POST" {
            rw.Header().Add("Access-Control-Allow-Origin", "*")
            rw.Header().Add("Access-Control-Allow-Methods", "*")
            return
        }

        offer := webrtc.SessionDescription{}
        err := json.NewDecoder(r.Body).Decode(&offer)
        if err != nil {
            panic(err)
        }

        answer := openConnection(&offer)
        err = json.NewEncoder(rw).Encode(answer)
        if err != nil {
            panic(err)
        }
    })

    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        ui, err := os.ReadFile("./index.html")
        if err != nil {
            rw.Write([]byte("missing index.html next to main.go: " + err.Error()))
            return
        }

        rw.Write([]byte(ui))
    })

    http.ListenAndServe(":8080", nil)
}

index.html

<!DOCTYPE html>
<head></head>
<body>
    <script>
        const pc = new RTCPeerConnection({
            iceServers: [
                {
                    urls: 'stun:stun.l.google.com:19302',
                },
            ],
        });

        pc.oniceconnectionstatechange = (e) => console.log(pc.iceConnectionState);

        pc.ontrack = (event) => console.log('on track called!');

        pc.onicecandidate = async (event) => {
            if (event.candidate === null) {
                const response = await fetch('/rtc', {
                    method: 'post',
                    body: JSON.stringify(pc.localDescription)
                })

                const body = await response.json()
                pc.setRemoteDescription(new RTCSessionDescription(body));
            }
        };


        // at least one track or data channel is required for the offer to succeed
        pc.createDataChannel('dummy');
        pc
            .createOffer({
                offerToReceiveVideo: true,
                offerToReceiveAudio: true,
            })
            .then(description => pc.setLocalDescription(description));
    </script>
</body>
javascript go webrtc
1个回答
0
投票

由于某种原因,即使您将

m=video
标记为
offerToReceiveVideo
,Safari 也不会在报价 SDP 中发送
true

解决方案是在将报价发送到您的 go 应用程序之前创建一个虚拟连接轨道:

function getDummyTrack() {
  const canvas = document.createElement("canvas");
  canvas.width = 640;
  canvas.height = 480;
  const ctx = canvas.getContext("2d");

  // Fill the canvas with a solid color, e.g., black
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Create a stream from the canvas
  const stream = canvas.captureStream(30); // 30 FPS

  // Get the video track from the stream
  const [videoTrack] = stream.getVideoTracks();
  return videoTrack;
}

peerConnection = new RTCPeerConnection();
createDataChannel(peerConnection);

peerConnection.onicecandidate = handleIceCandidateEvent;
peerConnection.ontrack = handleTrackEvent;

peerConnection.addTrack(getDummyTrack());
const offer = await peerConnection.createOffer({
  offerToReceiveVideo: true,
});

await peerConnection.setLocalDescription(offer);
signalingChannel.send(JSON.stringify({ type: "offer", data: offer.sdp }));

© www.soinside.com 2019 - 2024. All rights reserved.