[mirotalksfu] - add RTMP server and multi-source streaming!, update dep
هذا الالتزام موجود في:
256
rtmpServers/demo/client-server-axios/client/client.js
Normal file
256
rtmpServers/demo/client-server-axios/client/client.js
Normal file
@@ -0,0 +1,256 @@
|
||||
'use strict';
|
||||
|
||||
const videoElement = document.getElementById('video');
|
||||
const startCameraButton = document.getElementById('startCamera');
|
||||
const startScreenButton = document.getElementById('startScreen');
|
||||
const stopButton = document.getElementById('stop');
|
||||
const apiSecretInput = document.getElementById('apiSecret'); // Replace with your actual API secret
|
||||
const rtmpInput = document.getElementById('rtmp');
|
||||
const copyButton = document.getElementById('copy');
|
||||
const popup = document.getElementById('popup');
|
||||
const popupMessage = document.getElementById('popupMessage');
|
||||
const closePopup = document.getElementById('closePopup');
|
||||
|
||||
/*
|
||||
Low Latency: 1-2 seconds
|
||||
Standard Use Case: 5 seconds
|
||||
High Bandwidth/Stability: 10 seconds
|
||||
*/
|
||||
const chunkDuration = 4000; // ms
|
||||
|
||||
let mediaRecorder = null;
|
||||
let rtmpKey = null; // To store the RTMP key
|
||||
|
||||
function toggleButtons(disabled = true) {
|
||||
startCameraButton.disabled = disabled;
|
||||
startScreenButton.disabled = disabled;
|
||||
stopButton.disabled = disabled;
|
||||
}
|
||||
|
||||
function showPopup(message, type) {
|
||||
popup.classList.remove('success', 'error', 'warning', 'info');
|
||||
popup.classList.add(type);
|
||||
popupMessage.textContent = message;
|
||||
popup.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
hidePopup();
|
||||
}, 5000); // Hide after 5 seconds
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
popup.classList.add('hidden');
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
showPopup(message, 'error');
|
||||
}
|
||||
|
||||
function checkBrowserSupport() {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
if (userAgent.includes('chrome') && !userAgent.includes('edge') && !userAgent.includes('opr')) {
|
||||
console.log('Browser is Chrome-based. Proceed with functionality.');
|
||||
} else {
|
||||
showError(
|
||||
'This application requires a Chrome-based browser (Chrome, Edge Chromium, etc.). Please switch to a supported browser.',
|
||||
);
|
||||
toggleButtons(true);
|
||||
}
|
||||
}
|
||||
|
||||
function checkRTMPEnabled() {
|
||||
axios
|
||||
.get('/rtmpEnabled')
|
||||
.then((response) => {
|
||||
const { enabled } = response.data;
|
||||
if (!enabled) {
|
||||
showPopup('The RTMP streaming feature has been disabled by the administrator', 'info');
|
||||
toggleButtons(true);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching RTMP status:', error);
|
||||
showError(`Error fetching RTMP status: ${error.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
checkBrowserSupport();
|
||||
checkRTMPEnabled();
|
||||
};
|
||||
|
||||
async function startCapture(constraints) {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
videoElement.srcObject = stream;
|
||||
return stream;
|
||||
} catch (err) {
|
||||
console.error('Error accessing media devices.', err);
|
||||
showError('Error accessing media devices. Please check your camera and microphone permissions.');
|
||||
}
|
||||
}
|
||||
|
||||
async function startScreenCapture(constraints) {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
|
||||
videoElement.srcObject = stream;
|
||||
return stream;
|
||||
} catch (err) {
|
||||
console.error('Error accessing screen media.', err);
|
||||
showError('Error accessing screen sharing. Please try again or check your screen sharing permissions.');
|
||||
}
|
||||
}
|
||||
|
||||
async function initRTMP(stream) {
|
||||
const apiSecret = apiSecretInput.value;
|
||||
try {
|
||||
const response = await axios.post(`/initRTMP`, null, {
|
||||
headers: {
|
||||
authorization: apiSecret,
|
||||
},
|
||||
});
|
||||
const { rtmp } = response.data;
|
||||
console.log('initRTMP response:', { res: response, rtmp: rtmp });
|
||||
rtmpInput.value = rtmp;
|
||||
rtmpKey = new URL(rtmp).pathname.split('/').pop(); // Extract the RTMP key from the URL
|
||||
toggleButtons(true);
|
||||
stopButton.disabled = false; // Enable stopButton on successful initialization
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
const { status, data } = error.response;
|
||||
showPopup(data, 'info');
|
||||
console.log('Init RTMP', {
|
||||
status,
|
||||
data,
|
||||
});
|
||||
} else {
|
||||
showError('Error initializing RTMP. Please try again.');
|
||||
console.error('Error initializing RTMP:', error);
|
||||
}
|
||||
stopStreaming();
|
||||
stopTracks(stream);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function stopRTMP() {
|
||||
if (mediaRecorder) {
|
||||
mediaRecorder.stop();
|
||||
const apiSecret = apiSecretInput.value;
|
||||
videoElement.srcObject = null;
|
||||
rtmpInput.value = '';
|
||||
try {
|
||||
await axios.post(`/stopRTMP?key=${rtmpKey}`, null, {
|
||||
headers: {
|
||||
authorization: apiSecret,
|
||||
},
|
||||
});
|
||||
toggleButtons(false);
|
||||
stopButton.disabled = true;
|
||||
} catch (error) {
|
||||
showError('Error stopping RTMP. Please try again.');
|
||||
console.error('Error stopping RTMP:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function streamRTMPChunk(data) {
|
||||
const apiSecret = apiSecretInput.value;
|
||||
|
||||
const arrayBuffer = await data.arrayBuffer();
|
||||
const chunkSize = 1000000; // 1mb
|
||||
const totalChunks = Math.ceil(arrayBuffer.byteLength / chunkSize);
|
||||
|
||||
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
|
||||
const chunk = arrayBuffer.slice(chunkIndex * chunkSize, (chunkIndex + 1) * chunkSize);
|
||||
try {
|
||||
await axios.post(`/streamRTMP?key=${rtmpKey}`, chunk, {
|
||||
headers: {
|
||||
authorization: apiSecret,
|
||||
'Content-Type': 'video/webm',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (mediaRecorder) {
|
||||
stopStreaming();
|
||||
console.error('Error syncing chunk:', error.message);
|
||||
showError(`Error syncing chunk: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopStreaming() {
|
||||
if (mediaRecorder) {
|
||||
mediaRecorder.stop();
|
||||
}
|
||||
videoElement.srcObject = null;
|
||||
rtmpInput.value = '';
|
||||
toggleButtons(false);
|
||||
stopButton.disabled = true;
|
||||
}
|
||||
|
||||
async function startStreaming(stream) {
|
||||
if (!stream) return;
|
||||
|
||||
const initRTMPStream = await initRTMP(stream);
|
||||
|
||||
if (!initRTMPStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm; codecs=vp8,opus' });
|
||||
|
||||
mediaRecorder.ondataavailable = async (event) => {
|
||||
if (event.data.size > 0) {
|
||||
await streamRTMPChunk(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = (event) => {
|
||||
console.log('Media recorder stopped');
|
||||
stopTracks(stream);
|
||||
mediaRecorder = null;
|
||||
};
|
||||
|
||||
mediaRecorder.start(chunkDuration); // Record in chunks of the specified duration
|
||||
}
|
||||
|
||||
function stopTracks(stream) {
|
||||
stream.getTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
}
|
||||
|
||||
async function startCameraStreaming() {
|
||||
const stream = await startCapture({ video: true, audio: true });
|
||||
await startStreaming(stream);
|
||||
}
|
||||
|
||||
async function startScreenStreaming() {
|
||||
const stream = await startScreenCapture({ video: true, audio: true });
|
||||
await startStreaming(stream);
|
||||
}
|
||||
|
||||
function copyRTMP() {
|
||||
const rtmpInput = document.getElementById('rtmp');
|
||||
if (!rtmpInput.value) {
|
||||
return showPopup('No RTMP URL detected', 'info');
|
||||
}
|
||||
rtmpInput.select();
|
||||
document.execCommand('copy');
|
||||
showPopup('Copied: ' + rtmpInput.value, 'success');
|
||||
}
|
||||
|
||||
startCameraButton.addEventListener('click', startCameraStreaming);
|
||||
startScreenButton.addEventListener('click', startScreenStreaming);
|
||||
stopButton.addEventListener('click', stopRTMP);
|
||||
copyButton.addEventListener('click', copyRTMP);
|
||||
closePopup.addEventListener('click', hidePopup);
|
||||
|
||||
// Stop RTMP streaming when the browser tab is closed
|
||||
window.addEventListener('beforeunload', async (event) => {
|
||||
if (mediaRecorder) {
|
||||
await stopRTMP();
|
||||
}
|
||||
});
|
||||
المرجع في مشكلة جديدة
حظر مستخدم