@stormstreaming/stormstreamer
Version:
A JavaScript library containing core webrtc streamer functionality for embedding live-video streams on a website. Part of StormStreaming Suite.
492 lines (402 loc) • 20.3 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Storm Streamer DEVELOPER PAGE</title>
<script src="../dist/iife/index.js"></script>
</head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
<style>
body {font-family: "Courier Prime", monospace;}
span {width:100%;float:left; font-size:10px}
button {margin:5px;padding:3px 6px; font-family: "Courier Prime", monospace;}
label {margin:-20px 0 -20px 0;position:absolute;display: block;background:white;padding-left:10px;padding-right:10px; font-family: "Courier Prime", monospace;font-style: italic;}
.selectWrapper {display: flex}
.selectWrapper label {position: relative;margin: 0;padding-top: 4px;float:left;font-size: 12px;font-style:normal;}
.selectWrapper select {float:left;position: relative}
.toolbox {padding:10px;margin-left:10px;position: relative;margin-top:20px;border:2px dashed grey}
.toolbox-wrapper {display:flex}
ul {margin-right:10px;display: block}
li { display: flex; flex-wrap: nowrap; align-items: center;font-size: 14px;font-style: normal;padding-bottom: 10px;}
li span { display: inline;font-size: 14px; font-weight: 600;}
.value {margin-left:7px}
</style>
<body>
<div id="storm_player_0">
<h1>Storm Streamer - DEVELOPER PAGE</h1>
<div id="main" style="width:1280px;height:720px;background:grey">
<div id="vContainer" style="background:red"></div>
</div>
</div>
<div id="vContainer2" style="position:fixed;left:0;top:0;width:30%;z-index:2;background:red"></div>
<div id="screenshotSample"></div>
<div class="toolbox-wrapper">
<div class="toolbox" style="width:100%;display: flex">
<label>Streamer</label>
<ul>
<li>StreamKey: <span id="streamKeyValue" class="value"></span></li>
<li>PublishState: <span id="publishStateValue" class="value"></span></li>
<li>DeviceState: <span id="deviceStateValue" class="value"></span></li>
<li>PublishDuration: <span id="publishDurationValue" class="value"></span></li>
<li>VideoWidth: <span id="videoWidthValue" class="value"></span></li>
<li>VideoHeight: <span id="videoHeightValue" class="value"></span></li>
</ul>
</div>
<div class="toolbox" style="width:100%;display: flex">
<label>Devices</label>
<ul>
<li>CameraState: <span id="cameraStateValue" class="value"></span></li>
<li>MicrophoneState: <span id="microphoneStateValue" class="value"></span></li>
<li>Selected Camera: <span id="cameraValue" class="value"></span></li>
<li>Selected Microphone: <span id="microphoneValue" class="value"></span></li>
<li>Microphone Muted: <span id="microphoneMutedValue" class="value"></span></li>
</ul>
</div>
<div class="toolbox" style="width:100%;display: flex">
<label>STREAM</label>
<ul>
<li>currentFPS: <span id="currentFPSValue" class="value"></span></li>
<li>currentFPS Graph: <div id="currentFPSGraph" style="width:100px;height:20px"></div></li>
<li>currentBitrate: <span id="currentBitrateValue" class="value"></span></li>
<li>currentBitrate Graph: <div id="currentBitrateGraph" style="width:100px;height:20px"></div></span></li>
<li>videoFrames: <span id="videoFramesCountValue" class="value"></span></li>
<li>audioFrames: <span id="audioFramesCountValue" class="value"></span></li>
</ul>
</div>
<div class="toolbox" style="width:100%;display: flex">
<label>INFO</label>
<ul>
<li>publishVBitrate: <span id="publishVBitrateValue" class="value"></span></li>
<li>publishABitrate: <span id="publishABitrateValue" class="value"></span></li>
<li>Microphone Graph: <div id="micGraph" style="width:100px;height:20px"></div></li>
</ul>
</div>
</div>
<div class="toolbox-wrapper">
<div class="toolbox">
<label>Publish</label>
<button onclick="storm.publish('test')">publish('test')</button>
<button onclick="storm.publish('test2')">publish('test2')</button>
<button onclick="storm.unpublish()">unpublish()</button>
<button onclick="storm.start()">start()</button>
<button onclick="storm.stop()">stop()</button>
<button onclick="storm.destroy()">destroy()</button>
<button onclick="console.log(storm.getPublishState())">getPublishState()</button>
<button onclick="console.log(storm.getDeviceState())">getDeviceState()</button>
</div>
<div class="toolbox">
<label>Sound</label>
<button onclick="storm.muteMicrophone(true)">muteMicrophone(true)</button>
<button onclick="storm.muteMicrophone(false)">muteMicrophone(false)</button>
<button onclick="storm.mute()">muteSound()</button>
<button onclick="storm.unmute()">unmuteSound()</button>
<meter id="low_sound" min="0" low="50" high="80" max="100" value="0"></meter>
<meter id="high_sound" min="0" low="50" high="80" max="100" value="0"></meter>
</div>
<div class="toolbox">
<label>Resize</label>
<button onclick="storm.setSize(640, 360)">resize(640, 360)</button>
<button onclick="storm.setSize(854, 480)">resize(854, 480)</button>
<button onclick="storm.setSize(1280, 720)">resize(1280, 720)</button>
<button onclick="storm.setWidth(360)">setWidth(360)</button>
<button onclick="storm.setWidth('50%')">setWidth('50%')</button>
<button onclick="storm.setWidth('100%')">setWidth('100%')</button>
<button onclick="storm.setHeight(180)">setHeight(180)</button>
<button onclick="storm.setHeight('50%')">setHeight('50%')</button>
<button onclick="storm.setHeight('100%')">setHeight('100%')</button>
<button onclick="console.log(storm.getWidth())">getWidth()</button>
<button onclick="console.log(storm.getHeight())">getHeight()</button>
<button onclick="storm.attachToContainer('vContainer')">attachToContainer('vContainer')</button>
<button onclick="storm.attachToContainer('vContainer2')">attachToContainer('vContainer2')</button>
<button onclick="storm.detachFromContainer()">detachFromContainer()</button>
</div>
<div class="toolbox">
<label>Camera List</label>
<button onclick="console.log(storm.getCurrentCamera())">getCurrentCamera()</button>
<button onclick="console.log(storm.getCurrentMicrophone())">getCurrentMicrophone()</button>
<button onclick="console.log(storm.getCameraList())">getCameraList()</button>
<button onclick="console.log(storm.getMicrophoneList())">getMicrophoneList()</button>
<button onclick="console.log(storm.getCamerState())">getCamerState()</button>
<button onclick="console.log(storm.getMicrophoneState())">getMicrophoneState()</button>
<button onclick="storm.clearSavedDevices()">clearSavedDevices()</button>
<button onclick="storm.messSavedDevices()">messSavedDevices()</button>
<div class="selectWrapper">
<label for="cameraList">Select Camera: </label>
<select id="cameraList"></select>
</div>
<div class="selectWrapper">
<label for="microphoneList">Select Microphone: </label>
<select id="microphoneList"></select>
</div>
</div>
</div>
<div class="toolbox-wrapper">
<div class="toolbox" style="width:50%">
<label>Player Log</label>
<div id="logContainer"></div>
</div>
<div class="toolbox" style="width:50%">
<label>Events recording</label>
<textarea id="events" rows="30" style="padding:10px;width: calc(100% - 20px)">Events:</textarea>
</div>
</div>
<script>
let config = {
stream: {
serverList: [ // list of streaming server, 2nd, 3rd etc. will be used as backup
{
host: "localhost", // host or ip to the streaming server
application:"webrtc", // application
port: 443, // server port
ssl: true // whenever SSL connection should be used or not
},
],
},
settings: {
autoStart: true,
video: {
scalingMode: "letterbox",
containerID:"vContainer",
width:"100%",
aspectRatio:"16:9",
resizeDebounce:0,
},
audio: {
startVolume:50,
},
debug: {
console: {
enabled: true,
logTypes: ["INFO","ERROR", "TRACE", "WARNING", "SUCCESS"],
monoColor: false
},
container: {
enabled: true,
containerID: "logContainer",
logTypes: ["ERROR", "INFO", "TRACE", "WARNING","SUCCESS"],
monoColor: false
}
}
}
}
let storm = stormStreamer(config);
storm.addEventListener("streamKeyInUseInterval", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - StreamKey already in use, restarting... ${event.count}/${event.maxCount}`);
})
storm.addEventListener("streamKeyInUse", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - StreamKey already in use, restarting...`);
})
storm.addEventListener("publishMetadataUpdate", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Publish : ${event.metadata.streamWidth}x${event.metadata.streamHeight}`);
})
storm.addEventListener("inputDevicesInitialized", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Devices initialized successfully`);
})
storm.addEventListener("savedCameraNotFound", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Access to saved camera devices was denied, last device id: ${event.savedDeviceID}`);
})
storm.addEventListener("savedMicrophoneNotFound", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Access to saved microphone devices was denied, last device id: ${event.savedDeviceID}`);
})
storm.addEventListener("cameraAccessDenied", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Access to camera devices was denied`);
})
storm.addEventListener("microphoneAccessDenied", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Access to all microphone devices was denied`);
})
storm.addEventListener("inputAccessDenied", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Access to all input devices was denied`);
})
storm.addEventListener("publish", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Publish set to: `+event.streamKey);
document.getElementById("streamKeyValue").innerHTML = event.streamKey;
})
storm.addEventListener("publish", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Publish set to: `+event.streamKey);
document.getElementById("streamKeyValue").innerHTML = event.streamKey;
})
storm.addEventListener("unpublish", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Stream was unpublished`);
document.getElementById("streamKeyValue").innerHTML = "";
})
storm.addEventListener("noCameraFound", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - No Camera has been found`);
})
storm.addEventListener("noMicrophoneFound", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - No Microphone has been found`);
})
storm.addEventListener("playerReady", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Core Player is ready for interaction!`);
})
storm.addEventListener("microphoneStateChange", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - Mute State Changed: ${event.isMuted}` );
})
storm.addEventListener("serverConnectionInitiate", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - (serverConnectionInitiate) Starting connection to: ${event.serverURL}`);
})
storm.addEventListener("serverConnect", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - (serverConnect) Successfully connected to: ${event.serverURL} | seqNum: ${event.sequenceNum} `);
})
storm.addEventListener("serverDisconnect", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - serverDisconnect: ${event.serverURL} | seqNum: ${event.sequenceNum} ` );
})
storm.addEventListener("serverConnectionError", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - (serverConnectionError) Could not connect to server: ${event.serverURL}`);
})
storm.addEventListener("allConnectionsFailed", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - All servers failed!`);
})
storm.addEventListener("videoElementCreate", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - videoElementCreate!`);
})
storm.addEventListener("fullScreenEnter", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - fullScreenEnter!`);
})
storm.addEventListener("fullScreenExit", function(event){
functionAppend(`Streamer ID: ${event.ref.getStreamerID()} - fullScreenExit!`);
})
storm.addEventListener("soundMeter", function(event){
const lowSoundMeter = document.getElementById('low_sound');
const highSoundMeter = document.getElementById('high_sound');
lowSoundMeter.value = event.low*100;
highSoundMeter.value = event.high*100;
})
storm.addEventListener("deviceListUpdate", function(event){
console.log(event);
updateCameraList(event.cameraList);
updateMicrophoneList(event.microphoneList);
})
storm.initialize();
storm.publish("test");
const graph1 = storm.createBitrateGraph("currentBitrateGraph").start();
const graph2 = storm.createFPSGraph("currentFPSGraph").start();
const graph3 = storm.createMicrophoneGraph("micGraph").start();
function functionAppend(appender){
let allText = document.getElementById("events").value;
document.getElementById("events").value = appender+ "\r\n\r\n" +allText
}
document.getElementById('cameraList').addEventListener('change', () => {
console.log("setCamera: "+document.getElementById('cameraList').value);
storm.setCamera(document.getElementById('cameraList').value);
});
document.getElementById('microphoneList').addEventListener('change', () => {
storm.setMicrophone(document.getElementById('microphoneList').value);
});
function updateCameraList(cameras) {
const cameraList = document.getElementById('cameraList');
cameraList.innerHTML = '';
cameras.forEach(camera => {
const option = document.createElement('option');
option.value = camera.id;
option.textContent = camera.label;
cameraList.appendChild(option);
if(camera.isSelected){
document.getElementById("cameraValue").innerHTML = camera.label;
cameraList.value = camera.id;
}
});
}
function updateMicrophoneList(microphones) {
const micList = document.getElementById('microphoneList');
micList.innerHTML = '';
microphones.forEach(mic => {
const option = document.createElement('option');
option.value = mic.id;
option.textContent = mic.label;
micList.appendChild(option);
if(mic.isSelected){
document.getElementById("microphoneValue").innerHTML = mic.label;
micList.value = mic.id;
}
});
}
function formatSecondsWithMilliseconds(milliseconds) {
const hours = Math.floor(milliseconds / 3600000);
milliseconds %= 3600000;
const minutes = Math.floor(milliseconds / 60000);
milliseconds %= 60000;
const seconds = Math.floor(milliseconds / 1000);
const milisecondsLeft = milliseconds % 1000;
const formattedHours = hours < 10 ? '0' + hours : hours;
const formattedMinutes = minutes < 10 ? '0' + minutes : minutes;
const formattedSeconds = seconds < 10 ? '0' + seconds : seconds;
const formattedMilliseconds = milisecondsLeft.toString().padStart(3, '0');
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}.${formattedMilliseconds}`;
}
setInterval(() => {
// STREAMER
// Stream Key (daj tutaj INPUT)
const streamKey = storm?.getStreamKey() ?? "None";
document.getElementById("streamKeyValue").innerHTML = streamKey;
// Publish State
const publishState = storm?.getPublishState() ?? "Unknown";
document.getElementById("publishStateValue").innerHTML = publishState;
// Device State
const deviceState = storm?.getInputDevicesState() ?? "Unknown";
document.getElementById("deviceStateValue").innerHTML = deviceState;
// Publish Duration
const now = new Date().getTime();
const publishTime = storm?.getPublishTime() ?? 0;
const publishDuration = formatSecondsWithMilliseconds(((publishTime === 0) ? 0 : now - publishTime));
document.getElementById("publishDurationValue").innerHTML = publishDuration;
// Video Width
const videoWidth = storm?.getStatsController().publishVideoWidth;
document.getElementById("videoWidthValue").innerHTML = videoWidth;
// Video Height
const videoHeight = storm?.getStatsController().publishVideoHeight;
document.getElementById("videoHeightValue").innerHTML = videoHeight;
// DEVICES
// Camera State
const cameraState = storm?.getCameraState() ?? "Unknown";
document.getElementById("cameraStateValue").innerHTML = cameraState;
// Microphone State
const microphoneState = storm?.getMicrophoneState() ?? "Unknown";
document.getElementById("microphoneStateValue").innerHTML = microphoneState;
// Selected Camera (daj tutaj INPUT jak w Stream Key)
const selectedCamera = storm?.getCurrentCamera()?.label ?? "None";
document.getElementById("cameraValue").innerHTML = selectedCamera;
// Selected Microphone (daj tutaj INPUT jak w Stream Key)
const selectedMicrophone = storm?.getCurrentMicrophone()?.label ?? "None";
document.getElementById("microphoneValue").innerHTML = selectedMicrophone;
// Is Muted
const isMicrophoneMuted = storm?.isMicrophoneMuted() ?? false;
document.getElementById("microphoneMutedValue").innerHTML = isMicrophoneMuted;
// STREAM
// Current FPS
const videoFPS = storm?.getStatsController().currentFPS ?? 0;
document.getElementById("currentFPSValue").innerHTML = videoFPS.toFixed(2);
// Current Bitrate
const bitrate = storm?.getStatsController().currentBitrate ?? 0;
document.getElementById("currentBitrateValue").innerHTML = bitrate;
// Delivered Video Frames
const videoFrames = storm?.getStatsController().deliveredVideoFrames ?? 0;
document.getElementById("videoFramesCountValue").innerHTML = videoFrames;
// Delivered Audio Frames
const audioFrames = storm?.getStatsController().deliveredAudioFrames ?? 0;
document.getElementById("audioFramesCountValue").innerHTML = audioFrames;
// INFO
// Publish VBitrate
const publishVBitrate = storm?.getStatsController().publishVideoBitrate ?? 0;
document.getElementById("publishVBitrateValue").innerHTML = publishVBitrate;
// Publish VBitrate
const publishABitrate = storm?.getStatsController().publishAudioBitrate ?? 0;
document.getElementById("publishABitrateValue").innerHTML = publishABitrate;
},100)
async function embedScreenshot() {
const blob = await storm.makeScreenshot(); // Używamy metody makeScreenshot z obiektu storm
const screenshotDiv = document.getElementById('screenshotSample');
if (blob) {
const imageUrl = URL.createObjectURL(blob); // Tworzymy URL z bloba
const imgElement = document.createElement('img'); // Tworzymy nowy element img
imgElement.src = imageUrl; // Ustawiamy źródło obrazu
screenshotDiv.innerHTML = ''; // Czyścimy poprzednią zawartość diva
screenshotDiv.appendChild(imgElement); // Dodajemy img do diva
} else {
screenshotDiv.innerHTML = '<p>Nie udało się uzyskać zrzutu ekranu.</p>'; // Informacja, gdy zrzut ekranu się nie powiedzie
}
}
</script>
</body>
</html>