WebRtsp/test.html
2025-12-09 17:31:21 +08:00

201 lines
7.7 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Multi-Stream WebRTC RTSP Player</title>
<style>
.video-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 10px;
padding: 10px;
}
.video-wrapper {
position: relative;
}
video {
width: 100%;
height: auto;
border: 1px solid #ccc;
}
.stream-name {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0,0,0,0.7);
color: white;
padding: 5px;
border-radius: 3px;
}
.stream-status {
position: absolute;
bottom: 10px;
right: 10px;
background: rgba(0,0,0,0.7);
color: white;
padding: 5px;
border-radius: 3px;
}
</style>
</head>
<body>
<div class="video-container" id="videoContainer"></div>
<script>
// 配置多个流
const streams = [
{ url: 'rtsp://10.0.0.17:8554/camera_test/2', name: 'Camera_2_A' },
{ url: 'rtsp://10.0.0.17:8554/camera_test/2', name: 'Camera_2_B' },
{ url: 'rtsp://10.0.0.17:8554/camera_test/2', name: 'Camera_2_C' },
// 添加更多流
];
function createVideoElement(streamConfig) {
const wrapper = document.createElement('div');
wrapper.className = 'video-wrapper';
const video = document.createElement('video');
video.autoplay = true;
video.playsinline = true;
video.id = `video-${streamConfig.name}`;
const nameLabel = document.createElement('div');
nameLabel.className = 'stream-name';
nameLabel.textContent = streamConfig.name;
const statusLabel = document.createElement('div');
statusLabel.className = 'stream-status';
statusLabel.style.position = 'absolute';
statusLabel.style.bottom = '10px';
statusLabel.style.right = '10px';
statusLabel.style.background = 'rgba(0,0,0,0.7)';
statusLabel.style.color = 'white';
statusLabel.style.padding = '5px';
statusLabel.style.borderRadius = '3px';
statusLabel.textContent = 'Connecting...';
wrapper.appendChild(video);
wrapper.appendChild(nameLabel);
wrapper.appendChild(statusLabel);
document.getElementById('videoContainer').appendChild(wrapper);
return { video, statusLabel };
}
async function startStream(streamConfig) {
const { video, statusLabel } = createVideoElement(streamConfig);
const pc = new RTCPeerConnection();
const maxRetries = 3;
let retryCount = 0;
// 添加更详细的连接状态监控
pc.onconnectionstatechange = () => {
const state = pc.connectionState;
statusLabel.textContent = `Connection: ${state}`;
console.log(`[${streamConfig.name}] Connection state changed to: ${state}`);
};
pc.oniceconnectionstatechange = () => {
const state = pc.iceConnectionState;
console.log(`[${streamConfig.name}] ICE state changed to: ${state}`);
};
pc.onicegatheringstatechange = () => {
console.log(`[${streamConfig.name}] ICE gathering state: ${pc.iceGatheringState}`);
};
pc.onicecandidate = event => {
if (event.candidate) {
console.log(`[${streamConfig.name}] New ICE candidate: ${event.candidate.candidate}`);
}
};
// 添加更详细的轨道监控
pc.ontrack = (event) => {
console.log(`[${streamConfig.name}] Track received:`, event.track);
console.log(`Track settings:`, event.track.getSettings());
console.log(`Track constraints:`, event.track.getConstraints());
if (event.track.kind === 'video') {
video.srcObject = event.streams[0];
video.onloadedmetadata = () => {
console.log(`[${streamConfig.name}] Video metadata loaded`);
statusLabel.textContent = 'Playing';
video.play().catch(e => {
console.error(`[${streamConfig.name}] Play error:`, e);
statusLabel.textContent = 'Play failed';
});
};
// 添加视频元素事件监听
video.onplay = () => console.log(`[${streamConfig.name}] Video started playing`);
video.onpause = () => console.log(`[${streamConfig.name}] Video paused`);
video.onwaiting = () => console.log(`[${streamConfig.name}] Video buffering`);
video.onerror = () => console.log(`[${streamConfig.name}] Video error:`, video.error);
}
};
const tryConnect = async () => {
try {
statusLabel.textContent = 'Creating offer...';
const offer = await pc.createOffer({
offerToReceiveVideo: true,
offerToReceiveAudio: false
});
statusLabel.textContent = 'Setting local description...';
await pc.setLocalDescription(offer);
statusLabel.textContent = 'Sending offer to server...';
const response = await fetch('http://localhost:8080/offer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
offer: offer,
streamConfig: streamConfig
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
}
statusLabel.textContent = 'Processing server response...';
const answer = await response.json();
statusLabel.textContent = 'Setting remote description...';
await pc.setRemoteDescription(answer);
console.log(`[${streamConfig.name}] Connection setup completed`);
} catch (e) {
console.error(`[${streamConfig.name}] Error:`, e);
statusLabel.textContent = `Error: ${e.message}`;
if (retryCount < maxRetries) {
retryCount++;
statusLabel.textContent = `Retrying (${retryCount}/${maxRetries})...`;
await new Promise(resolve => setTimeout(resolve, 2000)); // 等待2秒后重试
return tryConnect();
}
throw e;
}
};
try {
await tryConnect();
} catch (e) {
statusLabel.textContent = 'Failed to connect';
console.error(`[${streamConfig.name}] Final error:`, e);
}
}
// 启动所有流
streams.forEach(stream => startStream(stream));
</script>
</body>
</html>