你能告诉我在Go代码中使用WebSocket时哪个WebRTC出错了吗?

huangapple go评论79阅读模式
英文:

Can you tell me which webrtc is wrong using websocket in the Go code?

问题

我在进行一个简单的webrtc项目时遇到了以下错误。我应该如何处理它?我将一起参考前端和后端代码。

这是错误信息:

处理答案时出错:DOMException: 在 'RTCPeerConnection' 上执行 'setRemoteDescription' 失败:无法设置远程答案 sdp:在错误的状态下调用:stable

前端代码:

<!DOCTYPE html>
<html>
<head>
    <title>WebRTC示例</title>
</head>
<body>
<h1>WebRTC示例</h1>
<video id="localVideo" autoplay></video>
<video id="remoteVideo" autoplay></video>
<script>
    const ws = new WebSocket('ws://localhost:8080/ws');

    const localVideo = document.getElementById('localVideo');
    const remoteVideo = document.getElementById('remoteVideo');

    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
        .then(stream => {
            localVideo.srcObject = stream;

            const pc = new RTCPeerConnection();

            // 将本地流添加到RTCPeerConnection对象
            stream.getTracks().forEach(track => {
                pc.addTrack(track, stream);
            });

            // 处理由其他对等方发送的ICE候选项
            ws.addEventListener('message', async (event) => {
                let message = event.data;
                if (typeof message === 'string') {
                    message = JSON.parse(message);
                } else if (message instanceof Blob) {
                    message = await message.text();
                    message = JSON.parse(message);
                }
                if (message.type === 'candidate') {
                    try {
                        await pc.addIceCandidate(message.candidate);
                    } catch (error) {
                        console.error('添加ICE候选项时出错:', error);
                    }
                } else if (message.type === 'offer') {
                    try {
                        await pc.setRemoteDescription(new RTCSessionDescription(message));
                        const answer = await pc.createAnswer();
                        await pc.setLocalDescription(answer);
                        ws.send(JSON.stringify({
                            type: 'answer',
                            sdp: pc.localDescription.sdp
                        }));
                    } catch (error) {
                        console.error('处理offer时出错:', error);
                    }
                } else if (message.type === 'answer') {
                    try {
                        await pc.setRemoteDescription(new RTCSessionDescription(message));
                    } catch (error) {
                        console.error('处理答案时出错:', error);
                    }
                }
            });

            // 将ICE候选项发送给其他对等方
            pc.addEventListener('icecandidate', (event) => {
                if (event.candidate) {
                    ws.send(JSON.stringify({
                        type: 'candidate',
                        candidate: event.candidate,
                    }));
                }
            });

            // 当可用时显示远程视频流
            pc.addEventListener('track', (event) => {
                remoteVideo.srcObject = event.streams[0];
            });

            // 创建offer并将其发送给其他对等方
            pc.createOffer()
                .then(offer => {
                    pc.setLocalDescription(offer);
                    ws.send(JSON.stringify({
                        type: 'offer',
                        sdp: pc.localDescription.sdp
                    }));
                })
                .catch(error => {
                    console.error('创建offer时出错:', error);
                });
        })
        .catch(error => {
            console.error('获取用户媒体时出错:', error);
        });
</script>
</body>
</html>

后端代码:

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
)

func main() {
	r := gin.Default()
	r.LoadHTMLFiles("index.html")

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "index.html", nil)
	})

	r.GET("/ws", func(c *gin.Context) {
		echo(c.Writer, c.Request)
	})

	r.Run(":8080")
}

var upgrader = websocket.Upgrader{} // 使用默认选项

func echo(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("升级错误:", err)
		return
	}
	defer c.Close()
	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Println("读取错误:", err)
			break
		}
		log.Printf("接收到消息:%s", message)
		err = c.WriteMessage(mt, message)
		if err != nil {
			log.Println("写入错误:", err)
			break
		}
	}
}

我尝试在JavaScript中更改顺序,去掉了"await",但没有成功。我将后端更改为Node.js并进行了处理,没有出现任何错误,所以我认为后端部分可能存在问题,但我不确定。

英文:

I encountered the following error while working on a simple webrtc project. How should I deal with it? I'll refer to the front and back end codes together.

This is error Message:

> Error handling answer: DOMException: Failed to execute
> 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote
> answer sdp: Called in wrong state: stable

Frontend code:

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;WebRTC Example&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;WebRTC Example&lt;/h1&gt;
&lt;video id=&quot;localVideo&quot; autoplay&gt;&lt;/video&gt;
&lt;video id=&quot;remoteVideo&quot; autoplay&gt;&lt;/video&gt;
&lt;script&gt;
const ws = new WebSocket(&#39;ws://localhost:8080/ws&#39;);
const localVideo = document.getElementById(&#39;localVideo&#39;);
const remoteVideo = document.getElementById(&#39;remoteVideo&#39;);
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream =&gt; {
localVideo.srcObject = stream;
const pc = new RTCPeerConnection();
// Add local stream to RTCPeerConnection object
stream.getTracks().forEach(track =&gt; {
pc.addTrack(track, stream);
});
// Handle ICE candidates sent by the other peer
ws.addEventListener(&#39;message&#39;, async (event) =&gt; {
let message = event.data;
if (typeof message === &#39;string&#39;) {
message = JSON.parse(message);
} else if (message instanceof Blob) {
message = await message.text();
message = JSON.parse(message);
}
if (message.type === &#39;candidate&#39;) {
try {
await pc.addIceCandidate(message.candidate);
} catch (error) {
console.error(&#39;Error adding ICE candidate:&#39;, error);
}
} else if (message.type === &#39;offer&#39;) {
try {
await pc.setRemoteDescription(new RTCSessionDescription(message));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
ws.send(JSON.stringify({
type: &#39;answer&#39;,
sdp: pc.localDescription.sdp
}));
} catch (error) {
console.error(&#39;Error handling offer:&#39;, error);
}
} else if (message.type === &#39;answer&#39;) {
try {
await pc.setRemoteDescription(new RTCSessionDescription(message));
} catch (error) {
console.error(&#39;Error handling answer:&#39;, error);
}
}
});
// Send ICE candidates to the other peer
pc.addEventListener(&#39;icecandidate&#39;, (event) =&gt; {
if (event.candidate) {
ws.send(JSON.stringify({
type: &#39;candidate&#39;,
candidate: event.candidate,
}));
}
});
// Display remote video stream when available
pc.addEventListener(&#39;track&#39;, (event) =&gt; {
remoteVideo.srcObject = event.streams[0];
});
// Create offer and send it to the other peer
pc.createOffer()
.then(offer =&gt; {
pc.setLocalDescription(offer);
ws.send(JSON.stringify({
type: &#39;offer&#39;,
sdp: pc.localDescription.sdp
}));
})
.catch(error =&gt; {
console.error(&#39;Error creating offer:&#39;, error);
});
})
.catch(error =&gt; {
console.error(&#39;Error getting user media:&#39;, error);
});
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

Backend code:

package main
import (
&quot;github.com/gin-gonic/gin&quot;
&quot;github.com/gorilla/websocket&quot;
&quot;log&quot;
&quot;net/http&quot;
)
func main() {
r := gin.Default()
r.LoadHTMLFiles(&quot;index.html&quot;)
r.GET(&quot;/&quot;, func(c *gin.Context) {
c.HTML(200, &quot;index.html&quot;, nil)
})
r.GET(&quot;/ws&quot;, func(c *gin.Context) {
echo(c.Writer, c.Request)
})
r.Run(&quot;:8080&quot;)
}
var upgrader = websocket.Upgrader{} // use default options
func echo(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print(&quot;upgrade:&quot;, err)
return
}
defer c.Close() 
for {
mt, message, err := c.ReadMessage()
if err != nil {
log.Println(&quot;read:&quot;, err)
break
}
log.Printf(&quot;recv: %s&quot;, message)
err = c.WriteMessage(mt, message)
if err != nil {
log.Println(&quot;write:&quot;, err)
break
}
}
}

I tried to change the order without "await" in JavaScript, but it didn't work out. I changed the backend to Node.js and proceeded it, and it was handed over without any errors, so I think there will be a problem with the backend part, but I'm not sure.

答案1

得分: 1

这表明您正在尝试在已经设置了远程描述的连接上设置远程答案。基本上,您可能会遇到提供/答案的冲突,双方都在创建提供和答案。

基本上,您需要找到一种解决这些冲突的方法,以便在需要在两个 WebRTC 对等方之间建立连接时,一个对等方创建提供,另一个对等方创建答案。一种方法是让新加入会议的对等方创建提供。Mozilla 在这里详细说明了一个场景:https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation

英文:

To me, this indicates that you are trying to set a remote answer on a connection that already has a remote description set. Basically, you are likely getting a collision of offers/answers, where both sides are creating offers and answers.

Basically, you need a way to resolve these conflicts so that when you need to establish a connection between two webrtc peers, you will have one creating an offer and the other one creating an answer. One way of doing it is to let a new peer joining a conference create an offer. Mozilla has a detailed scenario spelled out here: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation

答案2

得分: 0

我不是专家,但我可以尝试帮助你!
这可能是一个CORS问题,意味着你的前端和后端代码之间没有通信。
要解决这个问题,你可以使用github.com/gin-contrib/cors包,并添加以下代码:

import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(cors.Default())
}

在尝试使用程序之前,使用"go get"命令安装它。就像我说的,我是新手,不太懂,但希望这可以帮到你!

英文:

I'm not an expert, but I can try to help you!
This could be a CORS issue, meaning that there is no communication between your front-end and back-end code.
To solve this problem, you can use the package github.com/gin-contrib/cors and add this code:

import (
&quot;github.com/gin-contrib/cors&quot;
&quot;github.com/gin-gonic/gin&quot;
)
func main() {
r := gin.Default()
r.Use(cors.Default())
}

Use the "go get" command to install it before trying to use your program. As I said, I'm new and don't understand much, but I hope this helps!

huangapple
  • 本文由 发表于 2023年3月7日 07:21:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/75656707.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定