Classroom Videoplayer kısımları düzeltildi.

This commit is contained in:
Sedat Öztürk 2025-08-29 22:46:42 +03:00
parent c00bc1acf0
commit d1ae106db7
4 changed files with 114 additions and 1 deletions

View file

@ -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

View file

@ -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()

View file

@ -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()

View file

@ -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) => {