Click here to Skip to main content
15,881,424 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am encountering a problem with the visibility of screen share in my WebRTC application. Our focus is solely on implementing screen sharing without involving video calls. I have successfully received all SDP answers and ICE negotiations, and I am logging the details in the browser console.

The issue arises when screen sharing is enabled; I can see the "enabled" message in the console, but the shared content is not visible, both for me and other users in the same room. Despite extensive debugging, no errors are logged, and the screen share feed remains invisible.

Our tech stack includes Kurento 7.0.0.0 as the media server, React for the front end, and Node.js for the back end. Can anyone provide insights into why screen share might not be working in this scenario? Any help in understanding and resolving this issue would be greatly appreciated.

What I have tried:

JavaScript
import React, { useState, useRef, useEffect } from 'react';
import io from 'socket.io-client';

const App = () => {
	const [ roomName, setRoomName ] = useState('');
	const [ screenSharing, setScreenSharing ] = useState(false);
	const [ peerConnection, setPeerConnection ] = useState(null);
	const [ remoteOffer, setRemoteOffer ] = useState(null);
	const [ remoteAnswer, setRemoteAnswer ] = useState(null);
	const [ roomUsers, setRoomUsers ] = useState(0);
	const videoRef = useRef(null);
	const socketRef = useRef(null);
	const participantPeerConnections = useRef({});

	useEffect(() => {
		const socket = io('http://localhost:3001');

		socket.on('connect', () => console.log('Connected to the server...'));
		socket.on('disconnect', (reason) => console.log('Disconnected from the server. Reason:', reason));
		socket.on('roomUsers', (usersCount) => setRoomUsers(usersCount));

		const handleStartScreenShareResponse = async (sdpAnswer) => {
			try {
				console.log('Received SDP Answer:', sdpAnswer);

				if (peerConnection) {
					const sessionDescription = new RTCSessionDescription({ type: 'answer', sdp: sdpAnswer });
					console.log('Attempting to set remote description:', sessionDescription);
					await peerConnection.setRemoteDescription(sessionDescription);
					console.log('Remote description set successfully.');
				}
			} catch (error) {
				console.error('Error setting remote description:', error);
			}
		};

		socket.on('startScreenShareResponse', handleStartScreenShareResponse);

		socket.on('iceCandidate', async (candidate) => {
			try {
				console.log('Received ICE candidate from server:', candidate);

				if (peerConnection) {
					await peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(candidate)));
					console.log('ICE candidate added successfully.');
				}
			} catch (error) {
				console.error('Error adding ICE candidate:', error);
			}
		});

		socket.on('offer', async (sdpOffer) => {
			try {
				console.log('Received SDP offer from participant:', sdpOffer);
				setRemoteOffer(JSON.parse(sdpOffer));

				if (peerConnection) {
					const offer = await peerConnection.createAnswer();
					await peerConnection.setLocalDescription(offer);
					console.log('SDP answer generated for participant:', offer);

					if (socketRef.current) {
						socketRef.current.emit('answer', JSON.stringify(offer));
					}
				}
			} catch (error) {
				console.error('Error handling participant offer:', error);
			}
		});

		socket.on('answer', async (sdpAnswer) => {
			try {
				console.log('Received SDP answer from participant:', sdpAnswer);
				setRemoteAnswer(JSON.parse(sdpAnswer));
			} catch (error) {
				console.error('Error parsing received answer:', error);
			}
		});

		socketRef.current = socket;

		socketRef.current.on('roomInfo', ({ roomName, roomUsers }) => {
			console.log(`Room Information - Room: ${roomName}, Users: ${roomUsers.join(', ')}`);
		});

		return () => {
			socket.disconnect();
			console.log('Disconnected from the server...');
		};
	}, []);

	useEffect(
		() => {
			const handleParticipantStartScreenShare = async (participantId, participantSdpOffer) => {
				try {
					const participantPeerConnection = new RTCPeerConnection({
						iceServers: [
							{ urls: 'stun:40.68.138.182:3478' },
							{ urls: 'turn:40.68.138.182:3478', username: 'test', credential: 'test123' },
							{ urls: 'stun:stun.l.google.com:19302' }
						]
					});

					console.log('Creating RTCPeerConnection for participant...', participantId);

					const participantStream = await navigator.mediaDevices.getDisplayMedia({ video: true });

					participantStream
						.getTracks()
						.forEach((track) => participantPeerConnection.addTrack(track, participantStream));

					const participantOffer = JSON.parse(participantSdpOffer);

					if (!peerConnection || !participantPeerConnections.current[participantId]) {
						console.warn('Received offer from unknown participant. Rejecting offer.');
						return;
					}

					await participantPeerConnection.setRemoteDescription(
						new RTCSessionDescription({ type: 'offer', sdp: participantOffer })
					);

					const participantAnswer = await participantPeerConnection.createAnswer();
					await participantPeerConnection.setLocalDescription(participantAnswer);

					console.log('SDP answer generated for participant:', participantAnswer);

					if (socketRef.current) {
						socketRef.current.emit('answer', participantId, JSON.stringify(participantAnswer));
					}

					participantPeerConnection.onicecandidate = (event) => {
						if (event.candidate && socketRef.current) {
							console.log('Sending ICE candidate for participant...', event.candidate);
							socketRef.current.emit(
								'iceCandidate',
								roomName,
								participantId,
								JSON.stringify(event.candidate)
							);
						}
					};

					participantPeerConnection.ontrack = (event) => {
						console.log('Receiving remote video stream for participant:', participantId);
						const participantVideo = document.createElement('video');
						participantVideo.srcObject = event.streams[0];
						participantVideo.autoPlay = true;
						participantVideo.playsInline = true;
						document.body.appendChild(participantVideo);
					};

					participantPeerConnection.oniceconnectionstatechange = (event) => {
						console.log(
							'ICE connection state changed for participant:',
							participantId,
							event.target.iceConnectionState
						);
					};

					participantPeerConnection.onnegotiationneeded = async (event) => {
						if (!remoteOffer || !remoteAnswer) {
							console.warn(
								'Negotiation needed for participant but no remote offer or answer received. Cancelling negotiation.'
							);
							return;
						}

						try {
							const newOffer = await participantPeerConnection.createOffer();
							await participantPeerConnection.setLocalDescription(newOffer);
							console.log('Generated new offer for participant:', participantId, newOffer);

							if (socketRef.current) {
								socketRef.current.emit('offer', roomName, participantId, JSON.stringify(newOffer));
							}
						} catch (error) {
							console.error('Error creating new offer for participant:', participantId, error);
						}
					};

					participantPeerConnections.current[participantId] = participantPeerConnection;

					socketRef.current.off('participantStartScreenShare', handleParticipantStartScreenShare);
				} catch (error) {
					console.error('Error handling participant start screen share:', error);
				}
			};
		},
		[ roomName ]
	);

	const joinRoom = () => {
		if (socketRef.current) {
			socketRef.current.emit('joinRoom', roomName);
			console.log(`Joining room: ${roomName}`);
		}
	};

	const startScreenShare = async () => {
		try {
			const newPeerConnection = new RTCPeerConnection({
				iceServers: [
					{ urls: 'stun:40.68.138.182:3478' },
					{ urls: 'turn:40.68.138.182:3478', username: 'test', credential: 'test123' },
					{ urls: 'stun:stun.l.google.com:19302' }
				]
			});

			console.log('Creating RTCPeerConnection...');

			const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });

			stream.getTracks().forEach((track) => newPeerConnection.addTrack(track, stream));

			const offer = await newPeerConnection.createOffer();
			await newPeerConnection.setLocalDescription(offer);

			console.log('SDP offer generated:', offer);

			if (socketRef.current) {
				console.log('Sending SDP offer to server...', JSON.stringify(offer));
				socketRef.current.emit('startScreenShare', roomName, JSON.stringify(offer));
			}

			newPeerConnection.ondatachannel = (event) => console.log('Data channel created:', event.channel);

			newPeerConnection.oniceconnectionstatechange = (event) =>
				console.log('Connection state changed:', event.target.iceConnectionState);

			newPeerConnection.onicecandidate = (event) => {
				if (event.candidate && socketRef.current) {
					console.log('Sending ICE candidate from client...', event.candidate);
					socketRef.current.emit('iceCandidate', roomName, JSON.stringify(event.candidate));
				}
			};

			newPeerConnection.ontrack = (event) => {
				videoRef.current.srcObject = event.streams[0];
				console.log('Receiving remote video stream');
			};

			newPeerConnection.onnegotiationneeded = async (event) => {
				if (!remoteOffer || !remoteAnswer) {
					console.warn(
						'Offer/answer needed, but no remote offer or answer received. Cancelling negotiation.'
					);
					return;
				}

				try {
					const newOffer = await newPeerConnection.createOffer();
					await newPeerConnection.setLocalDescription(newOffer);
					console.log('Generated new offer:', newOffer);

					if (socketRef.current) {
						socketRef.current.emit('offer', roomName, JSON.stringify(newOffer));
					}
				} catch (error) {
					console.error('Error creating new offer:', error);
				}
			};

			newPeerConnection.onicecandidate = (event) => {
				if (event.candidate && socketRef.current) {
					console.log('Sending ICE candidate from client...', event.candidate);
					socketRef.current.emit('iceCandidate', roomName, JSON.stringify(event.candidate));
				}
			};

			newPeerConnection.oniceconnectionstatechange = (event) =>
				console.log('ICE connection state changed:', event.target.iceConnectionState);

			setPeerConnection(newPeerConnection);
			setScreenSharing(true);
		} catch (error) {
			console.error('Error starting screen share:', error.message);
		}
	};

	const stopScreenShare = () => {
		if (peerConnection) {
			peerConnection.close();
			setPeerConnection(null);
		}
		setScreenSharing(false);
		videoRef.current.srcObject = null;
		if (socketRef.current) {
			socketRef.current.emit('stopScreenShare');
		}
	};

	return (
		<div>
			<h1>Screen Sharing App</h1>
			<div>
				<input
					type="text"
					placeholder="Enter room name"
					value={roomName}
					onChange={(e) => setRoomName(e.target.value)}
				/>
				<button onClick={joinRoom}>Join Room</button>
				<p>Users in the room: {roomUsers}</p>
				<p>Screen Sharing: {screenSharing ? 'Enabled' : 'Disabled'}</p>
			</div>
			{screenSharing ? (
				<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
					<video
						ref={videoRef}
						autoPlay
						playsInline
						style={{ width: '80%', maxWidth: '800px', marginBottom: '20px' }}
					/>
					<button onClick={stopScreenShare} style={{ padding: '10px', fontSize: '16px' }}>
						Stop Screen Share
					</button>
				</div>
			) : (
				<button onClick={startScreenShare} style={{ padding: '10px', fontSize: '16px' }}>
					Start Screen Share
				</button>
			)}
		</div>
	);
};

export default App;
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900