Обмен SDP, создание комнаты, улучшенная организация кода
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
import { Call } from "@/app/components/Call/Call";
|
import { Call } from "@/app/components/Call/Call";
|
||||||
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
|
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
|
||||||
import { createContext, useEffect, useRef, useState } from "react";
|
import { createContext, useRef, useState } from "react";
|
||||||
import nacl from 'tweetnacl';
|
import nacl from 'tweetnacl';
|
||||||
import { useSender } from "../ProtocolProvider/useSender";
|
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 { usePacket } from "../ProtocolProvider/usePacket";
|
||||||
import { usePublicKey } from "../AccountProvider/usePublicKey";
|
import { usePublicKey } from "../AccountProvider/usePublicKey";
|
||||||
|
import { PacketWebRTC, WebRTCSignalType } from "../ProtocolProvider/protocol/packets/packet.webrtc";
|
||||||
|
|
||||||
|
|
||||||
export interface CallContextValue {
|
export interface CallContextValue {
|
||||||
@@ -64,15 +65,40 @@ export function CallProvider(props : CallProviderProps) {
|
|||||||
const send = useSender();
|
const send = useSender();
|
||||||
const publicKey = usePublicKey();
|
const publicKey = usePublicKey();
|
||||||
const peerConnectionRef = useRef<RTCPeerConnection | null>(null);
|
const peerConnectionRef = useRef<RTCPeerConnection | null>(null);
|
||||||
|
const roomIdRef = useRef<string>("");
|
||||||
|
|
||||||
const roleRef = useRef<CallRole | null>(null);
|
const roleRef = useRef<CallRole | null>(null);
|
||||||
const [sharedSecret, setSharedSecret] = useState<string>("");
|
const [sharedSecret, setSharedSecret] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
usePacket(27, async (packet: PacketWebRTC) => {
|
||||||
console.info("TRACE -> ", sharedSecret)
|
if(!activeCall || callState != CallState.WEB_RTC_EXCHANGE){
|
||||||
}, [sharedSecret]);
|
/**
|
||||||
|
* Нет активного звонка или мы не на стадии обмена 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();
|
const signalType = packet.getSignalType();
|
||||||
if(activeCall){
|
if(activeCall){
|
||||||
/**
|
/**
|
||||||
@@ -123,13 +149,19 @@ export function CallProvider(props : CallProviderProps) {
|
|||||||
/**
|
/**
|
||||||
* Нам нужно отправить свой публичный ключ другой стороне, чтобы она тоже могла создать общую секретную сессию
|
* Нам нужно отправить свой публичный ключ другой стороне, чтобы она тоже могла создать общую секретную сессию
|
||||||
*/
|
*/
|
||||||
const signalPacket = new PacketSignal();
|
const signalPacket = new PacketSignalPeer();
|
||||||
signalPacket.setSrc(publicKey);
|
signalPacket.setSrc(publicKey);
|
||||||
signalPacket.setDst(packet.getSrc());
|
signalPacket.setDst(packet.getSrc());
|
||||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
||||||
signalPacket.setSharedPublic(Buffer.from(sessionKeys.publicKey).toString('hex'));
|
signalPacket.setSharedPublic(Buffer.from(sessionKeys.publicKey).toString('hex'));
|
||||||
send(signalPacket);
|
send(signalPacket);
|
||||||
setCallState(CallState.WEB_RTC_EXCHANGE);
|
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){
|
if(signalType == SignalType.KEY_EXCHANGE && roleRef.current == CallRole.CALLEE){
|
||||||
console.info("EXCHANGE SIGNAL RECEIVED, CALLEE ROLE");
|
console.info("EXCHANGE SIGNAL RECEIVED, CALLEE ROLE");
|
||||||
@@ -152,6 +184,32 @@ export function CallProvider(props : CallProviderProps) {
|
|||||||
setSharedSecret(Buffer.from(computedSharedSecret).toString('hex'));
|
setSharedSecret(Buffer.from(computedSharedSecret).toString('hex'));
|
||||||
setCallState(CallState.WEB_RTC_EXCHANGE);
|
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]);
|
}, [activeCall, sessionKeys]);
|
||||||
|
|
||||||
const generateSessionKeys = () => {
|
const generateSessionKeys = () => {
|
||||||
@@ -165,7 +223,7 @@ export function CallProvider(props : CallProviderProps) {
|
|||||||
setActiveCall(dialog);
|
setActiveCall(dialog);
|
||||||
setCallState(CallState.CONNECTING);
|
setCallState(CallState.CONNECTING);
|
||||||
setShowCallView(true);
|
setShowCallView(true);
|
||||||
const signalPacket = new PacketSignal();
|
const signalPacket = new PacketSignalPeer();
|
||||||
signalPacket.setSrc(publicKey);
|
signalPacket.setSrc(publicKey);
|
||||||
signalPacket.setDst(dialog);
|
signalPacket.setDst(dialog);
|
||||||
signalPacket.setSignalType(SignalType.CALL);
|
signalPacket.setSignalType(SignalType.CALL);
|
||||||
@@ -174,12 +232,13 @@ export function CallProvider(props : CallProviderProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
const packetSignal = new PacketSignal();
|
const packetSignal = new PacketSignalPeer();
|
||||||
packetSignal.setSrc(publicKey);
|
packetSignal.setSrc(publicKey);
|
||||||
packetSignal.setDst(activeCall);
|
packetSignal.setDst(activeCall);
|
||||||
packetSignal.setSignalType(SignalType.END_CALL);
|
packetSignal.setSignalType(SignalType.END_CALL);
|
||||||
send(packetSignal);
|
send(packetSignal);
|
||||||
peerConnectionRef.current = null;
|
peerConnectionRef.current = null;
|
||||||
|
roomIdRef.current = "";
|
||||||
setActiveCall("");
|
setActiveCall("");
|
||||||
setCallState(CallState.ENDED);
|
setCallState(CallState.ENDED);
|
||||||
setShowCallView(false);
|
setShowCallView(false);
|
||||||
@@ -199,7 +258,7 @@ export function CallProvider(props : CallProviderProps) {
|
|||||||
* Звонок принят, генерируем ключи для сессии и отправляем их другой стороне для установления защищенного канала связи
|
* Звонок принят, генерируем ключи для сессии и отправляем их другой стороне для установления защищенного канала связи
|
||||||
*/
|
*/
|
||||||
const keys = generateSessionKeys();
|
const keys = generateSessionKeys();
|
||||||
const signalPacket = new PacketSignal();
|
const signalPacket = new PacketSignalPeer();
|
||||||
signalPacket.setSrc(publicKey);
|
signalPacket.setSrc(publicKey);
|
||||||
signalPacket.setDst(activeCall);
|
signalPacket.setDst(activeCall);
|
||||||
signalPacket.setSignalType(SignalType.KEY_EXCHANGE);
|
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