Обмен SDP, создание комнаты, улучшенная организация кода
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import { Call } from "@/app/components/Call/Call";
|
||||
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
|
||||
import { createContext, useEffect, useRef, useState } from "react";
|
||||
import { createContext, useRef, useState } from "react";
|
||||
import nacl from 'tweetnacl';
|
||||
import { useSender } from "../ProtocolProvider/useSender";
|
||||
import { PacketSignal, SignalType } from "../ProtocolProvider/protocol/packets/packet.signal";
|
||||
import { PacketSignalPeer, SignalType } from "../ProtocolProvider/protocol/packets/packet.signal.peer";
|
||||
import { usePacket } from "../ProtocolProvider/usePacket";
|
||||
import { usePublicKey } from "../AccountProvider/usePublicKey";
|
||||
import { PacketWebRTC, WebRTCSignalType } from "../ProtocolProvider/protocol/packets/packet.webrtc";
|
||||
|
||||
|
||||
export interface CallContextValue {
|
||||
@@ -64,15 +65,40 @@ export function CallProvider(props : CallProviderProps) {
|
||||
const send = useSender();
|
||||
const publicKey = usePublicKey();
|
||||
const peerConnectionRef = useRef<RTCPeerConnection | null>(null);
|
||||
const roomIdRef = useRef<string>("");
|
||||
|
||||
const roleRef = useRef<CallRole | null>(null);
|
||||
const [sharedSecret, setSharedSecret] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
console.info("TRACE -> ", sharedSecret)
|
||||
}, [sharedSecret]);
|
||||
usePacket(27, async (packet: PacketWebRTC) => {
|
||||
if(!activeCall || callState != CallState.WEB_RTC_EXCHANGE){
|
||||
/**
|
||||
* Нет активного звонка или мы не на стадии обмена WebRTC сигналами, игнорируем
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const signalType = packet.getSignalType();
|
||||
if(signalType == WebRTCSignalType.ANSWER){
|
||||
/**
|
||||
* Другая сторона (сервер SFU) отправил нам SDP ответ на наш оффер
|
||||
*/
|
||||
const sdp = JSON.parse(packet.getSdpOrCandidate());
|
||||
await peerConnectionRef.current?.setRemoteDescription(new RTCSessionDescription(sdp));
|
||||
info("Received WebRTC answer and set remote description");
|
||||
return;
|
||||
}
|
||||
if(signalType == WebRTCSignalType.ICE_CANDIDATE){
|
||||
/**
|
||||
* Другая сторона отправила нам ICE кандидата для установления WebRTC соединения
|
||||
*/
|
||||
const candidate = JSON.parse(packet.getSdpOrCandidate());
|
||||
await peerConnectionRef.current?.addIceCandidate(new RTCIceCandidate(candidate));
|
||||
info("Received WebRTC ICE candidate and added to peer connection");
|
||||
return;
|
||||
}
|
||||
}, [activeCall, sessionKeys, callState]);
|
||||
|
||||
usePacket(26, (packet: PacketSignal) => {
|
||||
usePacket(26, async (packet: PacketSignalPeer) => {
|
||||
const signalType = packet.getSignalType();
|
||||
if(activeCall){
|
||||
/**
|
||||
@@ -123,13 +149,19 @@ export function CallProvider(props : CallProviderProps) {
|
||||
/**
|
||||
* Нам нужно отправить свой публичный ключ другой стороне, чтобы она тоже могла создать общую секретную сессию
|
||||
*/
|
||||
const signalPacket = new PacketSignal();
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(packet.getSrc());
|
||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||
signalPacket.setSharedPublic(Buffer.from(sessionKeys.publicKey).toString('hex'));
|
||||
send(signalPacket);
|
||||
setCallState(CallState.WEB_RTC_EXCHANGE);
|
||||
/**
|
||||
* Создаем комнату на сервере SFU, комнату создает звонящий
|
||||
*/
|
||||
let webRtcSignal = new PacketSignalPeer();
|
||||
webRtcSignal.setSignalType(SignalType.CREATE_ROOM);
|
||||
send(webRtcSignal);
|
||||
}
|
||||
if(signalType == SignalType.KEY_EXCHANGE && roleRef.current == CallRole.CALLEE){
|
||||
console.info("EXCHANGE SIGNAL RECEIVED, CALLEE ROLE");
|
||||
@@ -152,6 +184,32 @@ export function CallProvider(props : CallProviderProps) {
|
||||
setSharedSecret(Buffer.from(computedSharedSecret).toString('hex'));
|
||||
setCallState(CallState.WEB_RTC_EXCHANGE);
|
||||
}
|
||||
if(signalType == SignalType.CREATE_ROOM) {
|
||||
/**
|
||||
* Создана комната для обмена WebRTC потоками
|
||||
*/
|
||||
roomIdRef.current = packet.getRoomId();
|
||||
info("WebRTC room created with id: " + packet.getRoomId());
|
||||
/**
|
||||
* Нужно отправить свой SDP оффер другой стороне, чтобы установить WebRTC соединение
|
||||
*/
|
||||
peerConnectionRef.current = new RTCPeerConnection({
|
||||
//Experemental
|
||||
iceServers: [
|
||||
{ urls: 'stun:stun.l.google.com:19302' }
|
||||
]
|
||||
});
|
||||
/**
|
||||
* Отправляем свой оффер другой стороне
|
||||
*/
|
||||
let offer = await peerConnectionRef.current.createOffer();
|
||||
peerConnectionRef.current.setLocalDescription(offer);
|
||||
let offerSignal = new PacketWebRTC();
|
||||
offerSignal.setSignalType(WebRTCSignalType.OFFER);
|
||||
offerSignal.setSdpOrCandidate(JSON.stringify(offer));
|
||||
send(offerSignal);
|
||||
return;
|
||||
}
|
||||
}, [activeCall, sessionKeys]);
|
||||
|
||||
const generateSessionKeys = () => {
|
||||
@@ -165,7 +223,7 @@ export function CallProvider(props : CallProviderProps) {
|
||||
setActiveCall(dialog);
|
||||
setCallState(CallState.CONNECTING);
|
||||
setShowCallView(true);
|
||||
const signalPacket = new PacketSignal();
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(dialog);
|
||||
signalPacket.setSignalType(SignalType.CALL);
|
||||
@@ -174,12 +232,13 @@ export function CallProvider(props : CallProviderProps) {
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
const packetSignal = new PacketSignal();
|
||||
const packetSignal = new PacketSignalPeer();
|
||||
packetSignal.setSrc(publicKey);
|
||||
packetSignal.setDst(activeCall);
|
||||
packetSignal.setSignalType(SignalType.END_CALL);
|
||||
send(packetSignal);
|
||||
peerConnectionRef.current = null;
|
||||
roomIdRef.current = "";
|
||||
setActiveCall("");
|
||||
setCallState(CallState.ENDED);
|
||||
setShowCallView(false);
|
||||
@@ -199,7 +258,7 @@ export function CallProvider(props : CallProviderProps) {
|
||||
* Звонок принят, генерируем ключи для сессии и отправляем их другой стороне для установления защищенного канала связи
|
||||
*/
|
||||
const keys = generateSessionKeys();
|
||||
const signalPacket = new PacketSignal();
|
||||
const signalPacket = new PacketSignalPeer();
|
||||
signalPacket.setSrc(publicKey);
|
||||
signalPacket.setDst(activeCall);
|
||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import Packet from "../packet";
|
||||
import Stream from "../stream";
|
||||
|
||||
export enum SignalType {
|
||||
CALL = 0,
|
||||
KEY_EXCHANGE = 1,
|
||||
ACTIVE_CALL = 2,
|
||||
END_CALL = 3,
|
||||
CREATE_ROOM = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* Пакет сигналинга, для сигналов WebRTC используется отдельный пакет 27 PacketWebRTCExchange
|
||||
*/
|
||||
export class PacketSignalPeer extends Packet {
|
||||
|
||||
private src: string = "";
|
||||
/**
|
||||
* Назначение
|
||||
*/
|
||||
private dst: string = "";
|
||||
/**
|
||||
* Используется если SignalType == KEY_EXCHANGE, для идентификации сессии обмена ключами
|
||||
*/
|
||||
private sharedPublic: string = "";
|
||||
|
||||
private signalType: SignalType = SignalType.CALL;
|
||||
|
||||
/**
|
||||
* Используется если SignalType == CREATE_ROOM,
|
||||
* для идентификации комнаты на SFU сервере, в которой будет происходить обмен сигналами
|
||||
* WebRTC для установления P2P соединения между участниками звонка
|
||||
*/
|
||||
private roomId: string = "";
|
||||
|
||||
|
||||
public getPacketId(): number {
|
||||
return 26;
|
||||
}
|
||||
|
||||
public _receive(stream: Stream): void {
|
||||
this.signalType = stream.readInt8();
|
||||
this.src = stream.readString();
|
||||
this.dst = stream.readString();
|
||||
if(this.signalType == SignalType.KEY_EXCHANGE){
|
||||
this.sharedPublic = stream.readString();
|
||||
}
|
||||
if(this.signalType == SignalType.CREATE_ROOM){
|
||||
this.roomId = stream.readString();
|
||||
}
|
||||
}
|
||||
|
||||
public _send(): Promise<Stream> | Stream {
|
||||
const stream = new Stream();
|
||||
stream.writeInt16(this.getPacketId());
|
||||
stream.writeInt8(this.signalType);
|
||||
stream.writeString(this.src);
|
||||
stream.writeString(this.dst);
|
||||
if(this.signalType == SignalType.KEY_EXCHANGE){
|
||||
stream.writeString(this.sharedPublic);
|
||||
}
|
||||
if(this.signalType == SignalType.CREATE_ROOM){
|
||||
stream.writeString(this.roomId);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
public setDst(dst: string) {
|
||||
this.dst = dst;
|
||||
}
|
||||
|
||||
public setSharedPublic(sharedPublic: string) {
|
||||
this.sharedPublic = sharedPublic;
|
||||
}
|
||||
|
||||
public setSignalType(signalType: SignalType) {
|
||||
this.signalType = signalType;
|
||||
}
|
||||
|
||||
public getDst(): string {
|
||||
return this.dst;
|
||||
}
|
||||
|
||||
public getSharedPublic(): string {
|
||||
return this.sharedPublic;
|
||||
}
|
||||
|
||||
public getSignalType(): SignalType {
|
||||
return this.signalType;
|
||||
}
|
||||
|
||||
public getSrc(): string {
|
||||
return this.src;
|
||||
}
|
||||
|
||||
public setSrc(src: string) {
|
||||
this.src = src;
|
||||
}
|
||||
|
||||
public getRoomId(): string {
|
||||
return this.roomId;
|
||||
}
|
||||
|
||||
public setRoomId(roomId: string) {
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user