Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Live VP9 WebM streaming via Icecast2 using Plyr.js #2864

dosgr started this conversation in Show and tell
Discussion options

Hi everyone,

We’re broadcasting a live 1080p/2K WebM stream using Icecast2 and Plyr.js for the player. This setup is fully browser-native (VP9 / WebM), plugin-free, and works across modern browsers.
Here’s a minimal example showing just the player:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>WebM Stream Player</title>
 
 <!-- Plyr CSS from CDN -->
 <link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
 <style>
 body {
 margin: 0;
 padding: 0;
 background: #000;
 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 }
 
 .video-wrapper {
 position: relative;
 width: 100%;
 max-width: 1200px;
 margin: 0 auto;
 padding-bottom: 56.25%;
 background: #000;
 }
 
 .plyr {
 position: absolute;
 top: 0; 
 left: 0;
 width: 100%; 
 height: 100%;
 }
 
 .status-message {
 position: absolute;
 top: 20px;
 left: 50%;
 transform: translateX(-50%);
 background: rgba(0,0,0,0.8);
 color: white;
 padding: 10px 20px;
 border-radius: 5px;
 z-index: 1000;
 font-size: 14px;
 border-left: 4px solid #3b82f6;
 transition: opacity 0.3s;
 }
 
 .play-overlay {
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
 background: rgba(0,0,0,0.8);
 display: flex;
 align-items: center;
 justify-content: center;
 z-index: 1000;
 cursor: pointer;
 }
 
 .play-button {
 background: #3b82f6;
 color: white;
 border: none;
 padding: 15px 30px;
 border-radius: 8px;
 font-size: 18px;
 cursor: pointer;
 transition: background 0.3s;
 }
 
 .play-button:hover {
 background: #2563eb;
 }
 
 .loading-spinner {
 position: absolute;
 top: 50%;
 left: 50%;
 transform: translate(-50%, -50%);
 width: 50px;
 height: 50px;
 border: 3px solid rgba(255,255,255,0.3);
 border-radius: 50%;
 border-top-color: #3b82f6;
 animation: spin 1s ease-in-out infinite;
 z-index: 999;
 }
 
 @keyframes spin {
 to { transform: translate(-50%, -50%) rotate(360deg); }
 }
 </style>
</head>
<body>
 <div class="video-wrapper">
 <video id="player" 
 playsinline 
 controls 
 autoplay 
 muted 
 preload="auto"
 crossorigin="anonymous">
 <source src="https://rdst.win:59000/dos.webm" type="video/webm">
 Your browser does not support WebM video streaming.
 </video>
 </div>
 <!-- Plyr JS from CDN -->
 <script src="https://cdn.plyr.io/3.7.8/plyr.js"></script>
 <script>
 document.addEventListener('DOMContentLoaded', async () => {
 const video = document.getElementById('player');
 const videoWrapper = document.querySelector('.video-wrapper');
 
 // Initialize Plyr player
 const player = new Plyr('#player', {
 muted: true,
 autoplay: true,
 hideControls: false,
 controls: [
 'play-large',
 'play',
 'progress',
 'current-time',
 'mute',
 'volume',
 'fullscreen'
 ],
 ratio: '16:9'
 });
 // Show status message
 function showStatusMessage(text, type = 'info') {
 const existingMessage = videoWrapper.querySelector('.status-message');
 if (existingMessage) existingMessage.remove();
 const message = document.createElement('div');
 message.className = 'status-message';
 message.textContent = text;
 
 if (type === 'error') {
 message.style.borderLeftColor = '#ef4444';
 } else if (type === 'success') {
 message.style.borderLeftColor = '#22c55e';
 }
 
 videoWrapper.appendChild(message);
 setTimeout(() => {
 if (message.parentNode) {
 message.style.opacity = '0';
 setTimeout(() => message.remove(), 300);
 }
 }, 4000);
 }
 // Show loading spinner
 function showLoadingSpinner() {
 const existingSpinner = videoWrapper.querySelector('.loading-spinner');
 if (existingSpinner) existingSpinner.remove();
 const spinner = document.createElement('div');
 spinner.className = 'loading-spinner';
 videoWrapper.appendChild(spinner);
 return spinner;
 }
 // Hide loading spinner
 function hideLoadingSpinner() {
 const spinner = videoWrapper.querySelector('.loading-spinner');
 if (spinner) spinner.remove();
 }
 // Show play button overlay
 function showPlayButton() {
 const existingOverlay = videoWrapper.querySelector('.play-overlay');
 if (existingOverlay) existingOverlay.remove();
 const overlay = document.createElement('div');
 overlay.className = 'play-overlay';
 
 overlay.innerHTML = `
 <button class="play-button">
 ▶️ Play Stream
 </button>
 `;
 
 overlay.querySelector('.play-button').addEventListener('click', async function() {
 overlay.remove();
 showLoadingSpinner();
 showStatusMessage('🔄 Connecting to stream...');
 
 try {
 await video.play();
 showStatusMessage('🟢 Stream connected successfully!', 'success');
 } catch (err) {
 showStatusMessage('❌ Failed to play stream', 'error');
 showPlayButton();
 } finally {
 hideLoadingSpinner();
 }
 });
 
 videoWrapper.appendChild(overlay);
 }
 // Attempt autoplay
 async function attemptAutoplay() {
 showLoadingSpinner();
 showStatusMessage('🔄 Attempting autoplay...');
 
 try {
 await new Promise(resolve => setTimeout(resolve, 1000));
 await video.play();
 showStatusMessage('🟢 Live stream connected!', 'success');
 hideLoadingSpinner();
 return true;
 } catch (err) {
 hideLoadingSpinner();
 showStatusMessage('🔇 Click play to start stream');
 showPlayButton();
 return false;
 }
 }
 // Video event listeners
 video.addEventListener('loadstart', () => {
 showStatusMessage('📥 Loading stream...');
 });
 video.addEventListener('canplay', () => {
 hideLoadingSpinner();
 });
 video.addEventListener('waiting', () => {
 showStatusMessage('⏳ Buffering...');
 });
 video.addEventListener('playing', () => {
 showStatusMessage('🟢 Live stream playing', 'success');
 hideLoadingSpinner();
 });
 video.addEventListener('error', (e) => {
 hideLoadingSpinner();
 showStatusMessage('❌ Stream error - Click play to retry', 'error');
 showPlayButton();
 });
 // Initialize
 setTimeout(attemptAutoplay, 500);
 // Click fallback for autoplay
 document.addEventListener('click', async () => {
 if (video.paused) {
 try {
 await video.play();
 } catch (err) {
 console.log('Click-triggered play failed:', err);
 }
 }
 }, { once: true });
 });
 </script>
</body>
</html>

Live stream preview URL: https://webm.win

Feedback and discussion are welcome!

Note:
This page demonstrates a live WebM stream via Icecast (VP9 video with Vorbis audio, fully playable in modern browsers using the HTML5 video element) and provides instructions for anyone interested in setting up similar broadcasting.

You must be logged in to vote

Replies: 0 comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant

AltStyle によって変換されたページ (->オリジナル) /