Classroom Videoplayer kısımları düzeltildi.
This commit is contained in:
parent
c00bc1acf0
commit
d1ae106db7
4 changed files with 114 additions and 1 deletions
|
|
@ -360,6 +360,28 @@ public class ClassroomHub : Hub
|
||||||
await Clients.Group(sessionId.ToString()).SendAsync("HandRaiseDismissed", new { studentId });
|
await Clients.Group(sessionId.ToString()).SendAsync("HandRaiseDismissed", new { studentId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HubMethodName("SendOffer")]
|
||||||
|
public async Task SendOfferAsync(Guid sessionId, Guid targetUserId, object offer)
|
||||||
|
{
|
||||||
|
// Tek hedef kullanıcıya gönderiyoruz
|
||||||
|
await Clients.User(targetUserId.ToString())
|
||||||
|
.SendAsync("ReceiveOffer", _currentUser.Id?.ToString(), offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HubMethodName("SendAnswer")]
|
||||||
|
public async Task SendAnswerAsync(Guid sessionId, Guid targetUserId, object answer)
|
||||||
|
{
|
||||||
|
await Clients.User(targetUserId.ToString())
|
||||||
|
.SendAsync("ReceiveAnswer", _currentUser.Id?.ToString(), answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HubMethodName("SendIceCandidate")]
|
||||||
|
public async Task SendIceCandidateAsync(Guid sessionId, Guid targetUserId, object candidate)
|
||||||
|
{
|
||||||
|
await Clients.User(targetUserId.ToString())
|
||||||
|
.SendAsync("ReceiveIceCandidate", _currentUser.Id?.ToString(), candidate);
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task OnDisconnectedAsync(Exception exception)
|
public override async Task OnDisconnectedAsync(Exception exception)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ export class SignalRService {
|
||||||
private onParticipantMuted?: (userId: string, isMuted: boolean) => void
|
private onParticipantMuted?: (userId: string, isMuted: boolean) => void
|
||||||
private onHandRaiseReceived?: (studentId: string) => void
|
private onHandRaiseReceived?: (studentId: string) => void
|
||||||
private onHandRaiseDismissed?: (studentId: string) => void
|
private onHandRaiseDismissed?: (studentId: string) => void
|
||||||
|
private onOfferReceived?: (fromUserId: string, offer: RTCSessionDescriptionInit) => void
|
||||||
|
private onAnswerReceived?: (fromUserId: string, answer: RTCSessionDescriptionInit) => void
|
||||||
|
private onIceCandidateReceived?: (fromUserId: string, candidate: RTCIceCandidateInit) => void
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const { auth } = store.getState()
|
const { auth } = store.getState()
|
||||||
|
|
@ -62,6 +65,21 @@ export class SignalRService {
|
||||||
this.onHandRaiseDismissed?.(payload.studentId)
|
this.onHandRaiseDismissed?.(payload.studentId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.connection.on('ReceiveOffer', (fromUserId: string, offer: RTCSessionDescriptionInit) => {
|
||||||
|
this.onOfferReceived?.(fromUserId, offer)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on('ReceiveAnswer', (fromUserId: string, answer: RTCSessionDescriptionInit) => {
|
||||||
|
this.onAnswerReceived?.(fromUserId, answer)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.connection.on(
|
||||||
|
'ReceiveIceCandidate',
|
||||||
|
(fromUserId: string, candidate: RTCIceCandidateInit) => {
|
||||||
|
this.onIceCandidateReceived?.(fromUserId, candidate)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
this.connection.onreconnected(() => {
|
this.connection.onreconnected(() => {
|
||||||
console.log('SignalR reconnected')
|
console.log('SignalR reconnected')
|
||||||
})
|
})
|
||||||
|
|
@ -350,6 +368,21 @@ export class SignalRService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendOffer(sessionId: string, targetUserId: string, offer: RTCSessionDescriptionInit) {
|
||||||
|
if (!this.isConnected) return
|
||||||
|
await this.connection.invoke('SendOffer', sessionId, targetUserId, offer)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAnswer(sessionId: string, targetUserId: string, answer: RTCSessionDescriptionInit) {
|
||||||
|
if (!this.isConnected) return
|
||||||
|
await this.connection.invoke('SendAnswer', sessionId, targetUserId, answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendIceCandidate(sessionId: string, targetUserId: string, candidate: RTCIceCandidateInit) {
|
||||||
|
if (!this.isConnected) return
|
||||||
|
await this.connection.invoke('SendIceCandidate', sessionId, targetUserId, candidate)
|
||||||
|
}
|
||||||
|
|
||||||
setAttendanceUpdatedHandler(callback: (record: ClassroomAttendanceDto) => void) {
|
setAttendanceUpdatedHandler(callback: (record: ClassroomAttendanceDto) => void) {
|
||||||
this.onAttendanceUpdate = callback
|
this.onAttendanceUpdate = callback
|
||||||
}
|
}
|
||||||
|
|
@ -378,6 +411,24 @@ export class SignalRService {
|
||||||
this.onHandRaiseDismissed = callback
|
this.onHandRaiseDismissed = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOfferReceivedHandler(
|
||||||
|
callback: (fromUserId: string, offer: RTCSessionDescriptionInit) => void,
|
||||||
|
) {
|
||||||
|
this.onOfferReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnswerReceivedHandler(
|
||||||
|
callback: (fromUserId: string, answer: RTCSessionDescriptionInit) => void,
|
||||||
|
) {
|
||||||
|
this.onAnswerReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
setIceCandidateReceivedHandler(
|
||||||
|
callback: (fromUserId: string, candidate: RTCIceCandidateInit) => void,
|
||||||
|
) {
|
||||||
|
this.onIceCandidateReceived = callback
|
||||||
|
}
|
||||||
|
|
||||||
async disconnect(): Promise<void> {
|
async disconnect(): Promise<void> {
|
||||||
if (this.isConnected && this.connection) {
|
if (this.isConnected && this.connection) {
|
||||||
await this.connection.stop()
|
await this.connection.stop()
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ export class WebRTCService {
|
||||||
private peerConnections: Map<string, RTCPeerConnection> = new Map()
|
private peerConnections: Map<string, RTCPeerConnection> = new Map()
|
||||||
private localStream: MediaStream | null = null
|
private localStream: MediaStream | null = null
|
||||||
private onRemoteStream?: (userId: string, stream: MediaStream) => void
|
private onRemoteStream?: (userId: string, stream: MediaStream) => void
|
||||||
|
private onIceCandidate?: (userId: string, candidate: RTCIceCandidateInit) => void
|
||||||
|
|
||||||
// STUN servers for NAT traversal
|
// STUN servers for NAT traversal
|
||||||
private rtcConfiguration: RTCConfiguration = {
|
private rtcConfiguration: RTCConfiguration = {
|
||||||
|
|
@ -55,6 +56,7 @@ export class WebRTCService {
|
||||||
if (event.candidate) {
|
if (event.candidate) {
|
||||||
console.log('ICE candidate generated for user:', userId, event.candidate)
|
console.log('ICE candidate generated for user:', userId, event.candidate)
|
||||||
// In a real implementation, this would be sent via SignalR
|
// In a real implementation, this would be sent via SignalR
|
||||||
|
this.onIceCandidate?.(userId, event.candidate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +70,10 @@ export class WebRTCService {
|
||||||
return peerConnection
|
return peerConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIceCandidateHandler(callback: (userId: string, candidate: RTCIceCandidateInit) => void) {
|
||||||
|
this.onIceCandidate = callback
|
||||||
|
}
|
||||||
|
|
||||||
async createOffer(userId: string): Promise<RTCSessionDescriptionInit> {
|
async createOffer(userId: string): Promise<RTCSessionDescriptionInit> {
|
||||||
const peerConnection = this.peerConnections.get(userId)
|
const peerConnection = this.peerConnections.get(userId)
|
||||||
if (!peerConnection) throw new Error('Peer connection not found')
|
if (!peerConnection) throw new Error('Peer connection not found')
|
||||||
|
|
@ -138,6 +144,10 @@ export class WebRTCService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPeerConnection(userId: string): RTCPeerConnection | undefined {
|
||||||
|
return this.peerConnections.get(userId)
|
||||||
|
}
|
||||||
|
|
||||||
closeAllConnections(): void {
|
closeAllConnections(): void {
|
||||||
this.peerConnections.forEach((pc) => pc.close())
|
this.peerConnections.forEach((pc) => pc.close())
|
||||||
this.peerConnections.clear()
|
this.peerConnections.clear()
|
||||||
|
|
|
||||||
|
|
@ -240,8 +240,32 @@ const RoomDetail: React.FC = () => {
|
||||||
setParticipants((prev) => prev.map((p) => (p.id === userId ? { ...p, stream } : p)))
|
setParticipants((prev) => prev.map((p) => (p.id === userId ? { ...p, stream } : p)))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
webRTCServiceRef.current.setIceCandidateHandler(async (toUserId, candidate) => {
|
||||||
|
if (signalRServiceRef.current) {
|
||||||
|
await signalRServiceRef.current.sendIceCandidate(classSession.id, toUserId, candidate)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
signalRServiceRef.current.setOfferReceivedHandler(async (fromUserId, offer) => {
|
||||||
|
if (!webRTCServiceRef.current?.getPeerConnection(fromUserId)) {
|
||||||
|
await webRTCServiceRef.current?.createPeerConnection(fromUserId)
|
||||||
|
}
|
||||||
|
const answer = await webRTCServiceRef.current?.createAnswer(fromUserId, offer)
|
||||||
|
if (answer) {
|
||||||
|
await signalRServiceRef.current?.sendAnswer(classSession.id, fromUserId, answer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
signalRServiceRef.current.setAnswerReceivedHandler(async (fromUserId, answer) => {
|
||||||
|
await webRTCServiceRef.current?.handleAnswer(fromUserId, answer)
|
||||||
|
})
|
||||||
|
|
||||||
|
signalRServiceRef.current.setIceCandidateReceivedHandler(async (fromUserId, candidate) => {
|
||||||
|
await webRTCServiceRef.current?.addIceCandidate(fromUserId, candidate)
|
||||||
|
})
|
||||||
|
|
||||||
// Setup SignalR event handlers
|
// Setup SignalR event handlers
|
||||||
signalRServiceRef.current.setParticipantJoinHandler((userId, name) => {
|
signalRServiceRef.current.setParticipantJoinHandler(async (userId, name) => {
|
||||||
// 🔑 Eğer gelen participant bizsek, listeye ekleme
|
// 🔑 Eğer gelen participant bizsek, listeye ekleme
|
||||||
if (userId === user.id) return
|
if (userId === user.id) return
|
||||||
|
|
||||||
|
|
@ -250,6 +274,12 @@ const RoomDetail: React.FC = () => {
|
||||||
// Create WebRTC connection for new participant
|
// Create WebRTC connection for new participant
|
||||||
if (webRTCServiceRef.current) {
|
if (webRTCServiceRef.current) {
|
||||||
webRTCServiceRef.current.createPeerConnection(userId)
|
webRTCServiceRef.current.createPeerConnection(userId)
|
||||||
|
|
||||||
|
// Eğer biz teacher isek offer oluşturup gönderelim
|
||||||
|
if (user.role === 'teacher') {
|
||||||
|
const offer = await webRTCServiceRef.current.createOffer(userId)
|
||||||
|
await signalRServiceRef.current?.sendOffer(classSession.id, userId, offer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setParticipants((prev) => {
|
setParticipants((prev) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue