Я работаю над проектом, в котором мне нужно установить соединение WebRTC между клиентом C# WPF (с использованием .NET Core) и сервером Python FastAPI. Цель состоит в том, чтобы обеспечить однонаправленный видеопоток от сервера к клиенту.
Сервер использует aiortc для потоковой передачи синтетического видеопотока, а клиент создан с помощью SIPSorcery для получить поток. Однако после первоначального обмена кандидатами ICE и ответа SDP я постоянно вижу сбой соединения с сообщением «
" на стороне сервера через некоторое время, и клиент сразу же меняет состояние соединения на неудачное.
Минимальное количество воспроизводимых примеров
< h2>Клиентский код:
Video track added to PeerConnection.
Creating SDP offer.
SDP Offer created:
v=0
o=- 42541 0 IN IP4 127.0.0.1
s=sipsorcery
t=0 0
a=group:BUNDLE 0
m=video 9 UDP/TLS/RTP/SAVP 96 100
c=IN IP4 0.0.0.0
a=ice-ufrag:RRVM
a=ice-pwd:LJSKIMKDKLTSBQVKWDUMSJFN
a=fingerprint:sha-256 1A:78:0D:CD:C2:A3:9F:C0:75:0D:87:D4:F9:09:07:66:91:85:E9:C5:06:AB:F1:2F:39:78:F1:DD:BD:6D:75:24
a=setup:actpass
a=candidate:3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0
a=ice-options:ice2,trickle
a=mid:0
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtpmap:100 H264/90000
a=rtcp-fb:100 goog-remb
a=fmtp:100 packetization-mode=1
a=rtcp-mux
a=rtcp:9 IN IP4 0.0.0.0
a=end-of-candidates
a=sendrecv
a=ssrc:449817168 cname:841fc310-157d-4732-9ab4-39f2f6249f20
Message sent: {"sdp":"v=0\r\no=- 42541 0 IN IP4 127.0.0.1\r\ns=sipsorcery\r\nt=0 0\r\na=group:BUNDLE 0\r\nm=video 9 UDP/TLS/RTP/SAVP 96 100\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:RRVM\r\na=ice-pwd:LJSKIMKDKLTSBQVKWDUMSJFN\r\na=fingerprint:sha-256 1A:78:0D:CD:C2:A3:9F:C0:75:0D:87:D4:F9:09:07:66:91:85:E9:C5:06:AB:F1:2F:39:78:F1:DD:BD:6D:75:24\r\na=setup:actpass\r\na=candidate:3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0\r\na=ice-options:ice2,trickle\r\na=mid:0\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtpmap:100 H264/90000\r\na=rtcp-fb:100 goog-remb\r\na=fmtp:100 packetization-mode=1\r\na=rtcp-mux\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=end-of-candidates\r\na=sendrecv\r\na=ssrc:449817168 cname:841fc310-157d-4732-9ab4-39f2f6249f20\r\n","type":"offer"}
SDP Offer sent to server.
ICE candidate gathered:
3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0
Message sent: {"command":"Ice_Candidate","candidate":{"IceServer":null,"candidate":"3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0","sdpMid":null,"sdpMLineIndex":0,"foundation":"3168018052","component":1,"priority":2113937663,"address":"192.168.0.82","protocol":0,"port":53352,"type":0,"tcpType":0,"relatedAddress":null,"relatedPort":0,"usernameFragment":"RRVM","DestinationEndPoint":null}}
ICE candidate sent to server.
WebRTC connection setup complete.
Received message from server: {"sdp": "v=0\r\no=- 3938952277 3938952277 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic:WMS *\r\nm=video 58160 UDP/TLS/RTP/SAVPF 96 100\r\nc=IN IP4 172.18.0.2\r\na=sendrecv\r\na=mid:0\r\na=msid:20030b46-dcd7-402e-91a9-61acc96fc861 0526e15c-fb87-49ec-a372-9d752dbe5f46\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=rtcp-mux\r\na=ssrc:2884284437 cname:f6435a48-f40f-4fbc-8ac2-5833d73158f4\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtpmap:100 H264/90000\r\na=rtcp-fb:100 goog-remb\r\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=candidate:2042d42f166ed704ca002a0751b390c3 1 udp 2130706431 172.18.0.2 58160 typ host\r\na=candidate:b0d9b1b22e9ac473e83e963403e57c55 1 udp 1694498815 37.228.203.142 27206 typ srflx raddr 172.18.0.2 rport 58160\r\na=end-of-candidates\r\na=ice-ufrag:9GzX\r\na=ice-pwd:6ErYyxnwubAqbQOsEEf8np\r\na=fingerprint:sha-256 D6:D6:BE:00:51:1E:B4:DB:90:4C:EC:63:BC:00:E0:27:3E:97:1F:A2:61:10:8D:AB:5C:7D:CE:4D:8A:5A:03:79\r\na=setup:active\r\n", "type": "answer"}
Received SDP Answer:
v=0
o=- 3938952277 3938952277 IN IP4 0.0.0.0
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic:WMS *
m=video 58160 UDP/TLS/RTP/SAVPF 96 100
c=IN IP4 172.18.0.2
a=sendrecv
a=mid:0
a=msid:20030b46-dcd7-402e-91a9-61acc96fc861 0526e15c-fb87-49ec-a372-9d752dbe5f46
a=rtcp:9 IN IP4 0.0.0.0
a=rtcp-mux
a=ssrc:2884284437 cname:f6435a48-f40f-4fbc-8ac2-5833d73158f4
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtpmap:100 H264/90000
a=rtcp-fb:100 goog-remb
a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=candidate:2042d42f166ed704ca002a0751b390c3 1 udp 2130706431 172.18.0.2 58160 typ host
a=candidate:b0d9b1b22e9ac473e83e963403e57c55 1 udp 1694498815 37.228.203.142 27206 typ srflx raddr 172.18.0.2 rport 58160
a=end-of-candidates
a=ice-ufrag:9GzX
a=ice-pwd:6ErYyxnwubAqbQOsEEf8np
a=fingerprint:sha-256 D6:D6:BE:00:51:1E:B4:DB:90:4C:EC:63:BC:00:E0:27:3E:97:1F:A2:61:10:8D:AB:5C:7D:CE:4D:8A:5A:03:79
a=setup:active
ICE connection state changed to: checking
SDP Answer set on PeerConnection.
ICE connection state changed to: connected
Connection state changed to connecting
Connection state changed to closed
Connection state changed to failed
Кандидаты ICE и обмен SDP кажутся успешными. Однако состояние соединения клиента меняется на «Не удалось» вскоре после переключения на «Подключено».
Сервер регистрирует «Согласие на отправку истекло» сразу после сообщения о состоянии соединения ICE: завершено..
Есть ли какой-то шаг, который нам не хватает для поддержания соединения, или проблема с конфигурацией ICE? Будем очень признательны за любую информацию!
Я работаю над проектом, в котором мне нужно установить соединение WebRTC между клиентом C# WPF (с использованием .NET Core) и сервером Python FastAPI. Цель состоит в том, чтобы обеспечить однонаправленный видеопоток от сервера к клиенту. Сервер использует aiortc для потоковой передачи синтетического видеопотока, а клиент создан с помощью SIPSorcery для получить поток. Однако после первоначального обмена кандидатами ICE и ответа SDP я постоянно вижу сбой соединения с сообщением «[code]Consent to send expired[/code]" на стороне сервера через некоторое время, и клиент сразу же меняет состояние соединения на неудачное. Минимальное количество воспроизводимых примеров < h2>Клиентский код: [code]using Newtonsoft.Json; using System.Collections.Concurrent; using System.Net.Http; using System.Text; using System.Diagnostics; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using SIPSorcery.Net; using SIPSorceryMedia.Encoders; using SIPSorcery.Media;
namespace Iris.Services { public class IPCameraService { private ClientWebSocket ws = new ClientWebSocket(); // WebSocket for communication private RTCPeerConnection pc; private string wsUrl = "ws://your.websocket.url"; // Replace with actual WebSocket URL private ConcurrentDictionary CameraWebsockets = new ConcurrentDictionary();
public async Task StartSocket(CancellationToken token) { if (ws.State != WebSocketState.Open) { ws = new ClientWebSocket(); await ws.ConnectAsync(new Uri(wsUrl), token); CameraWebsockets[0] = ws; Debug.WriteLine("WebSocket connected."); } }
public async Task EstablishConnection(CancellationToken token) { Debug.WriteLine("Establishing PeerConnection."); pc = new RTCPeerConnection();
// Log ICE connection state changes pc.oniceconnectionstatechange += (state) => { Debug.WriteLine($"ICE connection state changed to: {state}"); }; pc.onconnectionstatechange += (state) => { Debug.WriteLine($"Connection state changed to {state}"); };
// Initialize a dummy video source var testPatternSource = new VideoTestPatternSource(new VpxVideoEncoder()); MediaStreamTrack videoTrack = new MediaStreamTrack(testPatternSource.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(videoTrack); Debug.WriteLine("Video track added to PeerConnection."); }
public async Task SendOfferWithCandidates(CancellationToken token) { Debug.WriteLine("Creating SDP offer."); var offer = pc.createOffer(); await pc.setLocalDescription(offer); Debug.WriteLine($"SDP Offer created:\n{offer.sdp}");
var offerMessage = new { sdp = offer.sdp, type = "offer" }; await SendMessage(offerMessage, token); Debug.WriteLine("SDP Offer sent to server.");
pc.onicecandidate += async (RTCIceCandidate candidate) => { if (candidate != null) { Debug.WriteLine($"ICE candidate gathered:\n{candidate}"); var candidateMessage = new { command = "Ice_Candidate", candidate }; await SendMessage(candidateMessage, token); Debug.WriteLine("ICE candidate sent to server."); } }; }
private async Task SendMessage(object message, CancellationToken token) { var ws = CameraWebsockets[0]; var messageJson = JsonConvert.SerializeObject(message); var messageBytes = Encoding.UTF8.GetBytes(messageJson); await ws.SendAsync(new ArraySegment(messageBytes), WebSocketMessageType.Text, true, token); Debug.WriteLine($"Message sent: {messageJson}"); }
public async Task ReceiveMessages(CancellationToken token) { var ws = CameraWebsockets[0]; var buffer = new ArraySegment(new byte[8192]);
while (ws.State == WebSocketState.Open && !token.IsCancellationRequested) { var result = await ws.ReceiveAsync(buffer, token); string response = Encoding.UTF8.GetString(buffer.Array, 0, result.Count); var jsonResponse = JsonConvert.DeserializeObject(response); Debug.WriteLine($"Received message from server: {response}");
// Handle SDP Answer if (jsonResponse.ContainsKey("sdp")) { var sdpAnswer = jsonResponse["sdp"].ToString(); var sdpType = jsonResponse["type"].ToString(); Debug.WriteLine($"Received SDP Answer:\n{sdpAnswer}"); var sdp = SDP.ParseSDPDescription(sdpAnswer); pc.SetRemoteDescription(sdpType == "answer" ? SdpType.answer : SdpType.offer, sdp); Debug.WriteLine("SDP Answer set on PeerConnection."); } // Handle ICE Candidates else if (jsonResponse.ContainsKey("candidate")) { var candidate = jsonResponse["candidate"].ToString(); Debug.WriteLine($"Received ICE Candidate:\n{candidate}"); var iceCandidate = new RTCIceCandidateInit { candidate = candidate }; pc.addIceCandidate(iceCandidate); Debug.WriteLine("ICE Candidate added to PeerConnection."); } } } } } [/code] Код сервера: [code]from fastapi import APIRouter, WebSocket import json import asyncio import logging from starlette.websockets import WebSocketDisconnect from aiortc import VideoStreamTrack, RTCPeerConnection, RTCSessionDescription, RTCIceCandidate from aiortc.mediastreams import VideoFrame from fractions import Fraction import time import numpy as np
logger = logging.getLogger("uvicorn")
class SyntheticVideoTrack(VideoStreamTrack): def __init__(self, fps=15, width=1280, height=720, color=(0, 255, 0)): super().__init__() self.width = width self.height = height self.fps = fps self.color = color # RGB color for the synthetic frame background self.frame_interval = 1.0 / fps self.start_time = time.time() # Track the start time for PTS calculation
async def recv(self): """Generate synthetic frames at a consistent frame rate.""" await asyncio.sleep(self.frame_interval)
# Create a solid color frame frame_data = np.full((self.height, self.width, 3), self.color, dtype=np.uint8) video_frame = VideoFrame.from_ndarray(frame_data, format="rgb24")
# Set presentation timestamp (PTS) and time base elapsed_time = time.time() - self.start_time video_frame.pts = int(elapsed_time * 90000) # PTS in 90kHz clock video_frame.time_base = Fraction(1, 90000)
return video_frame
class VideoProcessor: def __init__(self): self.pc = None # Peer connection
async def websocket_control(self, websocket: WebSocket): logger.info("WebSocket Server Started")
while True: try: data = await websocket.receive_text() message = json.loads(data) logger.info(f"Received message: {message}")
if message["command"] == "Start_Stream": camera_url = message.get("camera_url") if camera_url: logger.info(f"Starting WebRTC stream with camera URL: {camera_url}") await self.start_webrtc_stream(websocket, camera_url) else: logger.error("No camera URL provided. Cannot start stream.") elif message["command"] == "Ice_Candidate": # Handle ICE candidates logger.info("Received ICE candidate from client") if self.pc is not None: # Ensure pc is initialized # Extract the full candidate object from the received message candidate_data = message.get("candidate") logger.info(candidate_data) ice_candidate = RTCIceCandidate( foundation=candidate_data.get('foundation'), component=candidate_data.get('component'), priority=candidate_data.get('priority'), ip=candidate_data.get('address'), port=candidate_data.get('port'), type=candidate_data.get('type'), protocol=candidate_data.get('protocol'), sdpMid=candidate_data.get('sdpMid'), sdpMLineIndex=candidate_data.get('sdpMLineIndex') )
# Add the ICE candidate to the peer connection await self.pc.addIceCandidate(ice_candidate) logger.info(f"ICE candidate added: {ice_candidate}") else: logger.error("Peer connection not initialized; cannot add ICE candidate.")
except WebSocketDisconnect as e: logger.info(f"WebSocket disconnected: {e}") break
# Set up video track using the camera URL player = SyntheticVideoTrack(fps=15, width=1280, height=720, color=(0, 0, 255)) # Blue frames logger.info(f"Adding camera video track to peer connection: {camera_url}") self.pc.addTrack(player)
# Set remote description using the client's SDP offer offer_sdp = RTCSessionDescription(sdp=offer["sdp"], type=offer["type"]) await self.pc.setRemoteDescription(offer_sdp)
# Create answer and set local description answer = await self.pc.createAnswer() await self.pc.setLocalDescription(answer) logger.info(f"SDP Answer from server:\n{self.pc.localDescription.sdp}")
# Send SDP answer back to client await websocket.send_text(json.dumps({ "sdp": self.pc.localDescription.sdp, "type": self.pc.localDescription.type })) logger.info("SDP answer sent to client successfully.")
@ip_camera_route.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() await asyncio.create_task(processor.websocket_control(websocket)) [/code] Сведения о проблеме [list] [*]Сервер получает предложение SDP клиента и кандидатов ICE, отвечает ответом SDP и кандидатами ICE . [*]Клиент обрабатывает ответ SDP и показывает, что состояние соединения ICE меняется на подключенное. [*]Вскоре после этого состояние соединения клиента меняется на закрытое и затем произошел сбой. [*]На стороне сервера он регистрирует «Согласие на отправку истекло» после отображения состояния завершения соединения ICE. [/list] Воспроизведенные выходные данные и журналы [code]Server Logs:[/code] [code]INFO: Adding camera video track to peer connection: http://129.125.136.20/axis-cgi/mjpg/video.cgi?camera=1 INFO: ICE gathering state: gathering INFO: ICE gathering state: complete INFO: SDP Answer from server: v=0 o=- 3938952277 3938952277 IN IP4 0.0.0.0 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic:WMS * m=video 58160 UDP/TLS/RTP/SAVPF 96 100 c=IN IP4 172.18.0.2 a=sendrecv a=mid:0 a=msid:20030b46-dcd7-402e-91a9-61acc96fc861 0526e15c-fb87-49ec-a372-9d752dbe5f46 a=rtcp:9 IN IP4 0.0.0.0 a=rtcp-mux a=ssrc:2884284437 cname:f6435a48-f40f-4fbc-8ac2-5833d73158f4 a=rtpmap:96 VP8/90000 a=rtcp-fb:96 goog-remb a=rtpmap:100 H264/90000 a=rtcp-fb:100 goog-remb a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=candidate:2042d42f166ed704ca002a0751b390c3 1 udp 2130706431 172.18.0.2 58160 typ host a=candidate:b0d9b1b22e9ac473e83e963403e57c55 1 udp 1694498815 37.228.203.142 27206 typ srflx raddr 172.18.0.2 rport 58160 a=end-of-candidates a=ice-ufrag:9GzX a=ice-pwd:6ErYyxnwubAqbQOsEEf8np a=fingerprint:sha-256 D6:D6:BE:00:51:1E:B4:DB:90:4C:EC:63:BC:00:E0:27:3E:97:1F:A2:61:10:8D:AB:5C:7D:CE:4D:8A:5A:03:79 a=setup:active INFO: SDP answer sent to client successfully. INFO: Received message: {'command': 'Ice_Candidate', 'candidate': {'IceServer': None, 'candidate': '3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0', 'sdpMid': None, 'sdpMLineIndex': 0, 'foundation': '3168018052', 'component': 1, 'priority': 2113937663, 'address': '192.168.0.82', 'protocol': 0, 'port': 53352, 'type': 0, 'tcpType': 0, 'relatedAddress': None, 'relatedPort': 0, 'usernameFragment': 'RRVM', 'DestinationEndPoint': None}} INFO: Received ICE candidate from client INFO: {'IceServer': None, 'candidate': '3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0', 'sdpMid': None, 'sdpMLineIndex': 0, 'foundation': '3168018052', 'component': 1, 'priority': 2113937663, 'address': '192.168.0.82', 'protocol': 0, 'port': 53352, 'type': 0, 'tcpType': 0, 'relatedAddress': None, 'relatedPort': 0, 'usernameFragment': 'RRVM', 'DestinationEndPoint': None} INFO: ICE candidate added: RTCIceCandidate(component=1, foundation='3168018052', ip='192.168.0.82', port=53352, priority=2113937663, protocol=0, type=0, relatedAddress=None, relatedPort=None, sdpMid=None, sdpMLineIndex=0, tcpType=None) INFO: Connection(3) Check CandidatePair(('172.18.0.2', 58160) -> ('192.168.0.82', 53352)) State.FROZEN -> State.WAITING INFO: ICE connection state: checking INFO: Connection(3) Check CandidatePair(('172.18.0.2', 58160) -> ('192.168.0.82', 53352)) State.WAITING -> State.IN_PROGRESS INFO: Connection(3) Check CandidatePair(('172.18.0.2', 58160) -> ('192.168.0.82', 53352)) State.IN_PROGRESS -> State.SUCCEEDED INFO: Connection(3) ICE completed INFO: ICE connection state: completed INFO: Connection(3) Consent to send expired INFO: ICE connection state: failed [/code] [code]Client Logs:[/code] [code]Video track added to PeerConnection. Creating SDP offer. SDP Offer created: v=0 o=- 42541 0 IN IP4 127.0.0.1 s=sipsorcery t=0 0 a=group:BUNDLE 0 m=video 9 UDP/TLS/RTP/SAVP 96 100 c=IN IP4 0.0.0.0 a=ice-ufrag:RRVM a=ice-pwd:LJSKIMKDKLTSBQVKWDUMSJFN a=fingerprint:sha-256 1A:78:0D:CD:C2:A3:9F:C0:75:0D:87:D4:F9:09:07:66:91:85:E9:C5:06:AB:F1:2F:39:78:F1:DD:BD:6D:75:24 a=setup:actpass a=candidate:3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0 a=ice-options:ice2,trickle a=mid:0 a=rtpmap:96 VP8/90000 a=rtcp-fb:96 goog-remb a=rtpmap:100 H264/90000 a=rtcp-fb:100 goog-remb a=fmtp:100 packetization-mode=1 a=rtcp-mux a=rtcp:9 IN IP4 0.0.0.0 a=end-of-candidates a=sendrecv a=ssrc:449817168 cname:841fc310-157d-4732-9ab4-39f2f6249f20
Message sent: {"sdp":"v=0\r\no=- 42541 0 IN IP4 127.0.0.1\r\ns=sipsorcery\r\nt=0 0\r\na=group:BUNDLE 0\r\nm=video 9 UDP/TLS/RTP/SAVP 96 100\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:RRVM\r\na=ice-pwd:LJSKIMKDKLTSBQVKWDUMSJFN\r\na=fingerprint:sha-256 1A:78:0D:CD:C2:A3:9F:C0:75:0D:87:D4:F9:09:07:66:91:85:E9:C5:06:AB:F1:2F:39:78:F1:DD:BD:6D:75:24\r\na=setup:actpass\r\na=candidate:3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0\r\na=ice-options:ice2,trickle\r\na=mid:0\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtpmap:100 H264/90000\r\na=rtcp-fb:100 goog-remb\r\na=fmtp:100 packetization-mode=1\r\na=rtcp-mux\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=end-of-candidates\r\na=sendrecv\r\na=ssrc:449817168 cname:841fc310-157d-4732-9ab4-39f2f6249f20\r\n","type":"offer"} SDP Offer sent to server. ICE candidate gathered: 3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0 Message sent: {"command":"Ice_Candidate","candidate":{"IceServer":null,"candidate":"3168018052 1 udp 2113937663 192.168.0.82 53352 typ host generation 0","sdpMid":null,"sdpMLineIndex":0,"foundation":"3168018052","component":1,"priority":2113937663,"address":"192.168.0.82","protocol":0,"port":53352,"type":0,"tcpType":0,"relatedAddress":null,"relatedPort":0,"usernameFragment":"RRVM","DestinationEndPoint":null}} ICE candidate sent to server. WebRTC connection setup complete. Received message from server: {"sdp": "v=0\r\no=- 3938952277 3938952277 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic:WMS *\r\nm=video 58160 UDP/TLS/RTP/SAVPF 96 100\r\nc=IN IP4 172.18.0.2\r\na=sendrecv\r\na=mid:0\r\na=msid:20030b46-dcd7-402e-91a9-61acc96fc861 0526e15c-fb87-49ec-a372-9d752dbe5f46\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=rtcp-mux\r\na=ssrc:2884284437 cname:f6435a48-f40f-4fbc-8ac2-5833d73158f4\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtpmap:100 H264/90000\r\na=rtcp-fb:100 goog-remb\r\na=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=candidate:2042d42f166ed704ca002a0751b390c3 1 udp 2130706431 172.18.0.2 58160 typ host\r\na=candidate:b0d9b1b22e9ac473e83e963403e57c55 1 udp 1694498815 37.228.203.142 27206 typ srflx raddr 172.18.0.2 rport 58160\r\na=end-of-candidates\r\na=ice-ufrag:9GzX\r\na=ice-pwd:6ErYyxnwubAqbQOsEEf8np\r\na=fingerprint:sha-256 D6:D6:BE:00:51:1E:B4:DB:90:4C:EC:63:BC:00:E0:27:3E:97:1F:A2:61:10:8D:AB:5C:7D:CE:4D:8A:5A:03:79\r\na=setup:active\r\n", "type": "answer"} Received SDP Answer: v=0 o=- 3938952277 3938952277 IN IP4 0.0.0.0 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic:WMS * m=video 58160 UDP/TLS/RTP/SAVPF 96 100 c=IN IP4 172.18.0.2 a=sendrecv a=mid:0 a=msid:20030b46-dcd7-402e-91a9-61acc96fc861 0526e15c-fb87-49ec-a372-9d752dbe5f46 a=rtcp:9 IN IP4 0.0.0.0 a=rtcp-mux a=ssrc:2884284437 cname:f6435a48-f40f-4fbc-8ac2-5833d73158f4 a=rtpmap:96 VP8/90000 a=rtcp-fb:96 goog-remb a=rtpmap:100 H264/90000 a=rtcp-fb:100 goog-remb a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=candidate:2042d42f166ed704ca002a0751b390c3 1 udp 2130706431 172.18.0.2 58160 typ host a=candidate:b0d9b1b22e9ac473e83e963403e57c55 1 udp 1694498815 37.228.203.142 27206 typ srflx raddr 172.18.0.2 rport 58160 a=end-of-candidates a=ice-ufrag:9GzX a=ice-pwd:6ErYyxnwubAqbQOsEEf8np a=fingerprint:sha-256 D6:D6:BE:00:51:1E:B4:DB:90:4C:EC:63:BC:00:E0:27:3E:97:1F:A2:61:10:8D:AB:5C:7D:CE:4D:8A:5A:03:79 a=setup:active
ICE connection state changed to: checking SDP Answer set on PeerConnection. ICE connection state changed to: connected Connection state changed to connecting Connection state changed to closed Connection state changed to failed [/code] [list] [*]Кандидаты ICE и обмен SDP кажутся успешными. Однако состояние соединения клиента меняется на «Не удалось» вскоре после переключения на «Подключено». Сервер регистрирует «Согласие на отправку истекло» сразу после сообщения о состоянии соединения ICE: завершено.. [/list] Есть ли какой-то шаг, который нам не хватает для поддержания соединения, или проблема с конфигурацией ICE? Будем очень признательны за любую информацию!
Я работаю над проектом, в котором мне нужно установить соединение WebRTC между клиентом C# WPF (с использованием .NET Core) и сервером Python FastAPI. Цель состоит в том, чтобы обеспечить однонаправленный видеопоток от сервера к клиенту.
Сервер...
Я использую эту библиотеку для сброса пароля пользователя. Это блок кода, который генерирует код:
async def create_code(db: AsyncSession, user):
secret_key = pyotp.random_base32()
Я создаю приложение для голосовых/видеозвонков на Android с использованием WebRTC, где кандидаты SDP и ICE обмениваются через сервер WebSocket. Поток кандидатов SDP и ICE между двумя пользователями (A и B) выглядит правильно в журналах WebSocket, но...
Я создаю приложение для голосовых/видеозвонков на Android с использованием WebRTC, где кандидаты SDP и ICE обмениваются через сервер WebSocket. Поток кандидатов SDP и ICE между двумя пользователями (A и B) выглядит правильно в журналах WebSocket, но...
Я получаю сообщение об ошибке только для одного развертывания iOS: AADB2C90080: срок действия предоставленного гранта истек. Повторите аутентификацию и повторите попытку. при следующем вызове MSAL await ClientApplication.AcquireTokenSilent: