示例
M3U8 在线播放器的实用示例和代码片段。
基本使用示例
简单播放器
创建一个基本的 M3U8 播放器:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>M3U8 播放器示例</title>
<style>
.player-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.video-player {
width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.controls {
margin-top: 20px;
display: flex;
gap: 10px;
align-items: center;
}
.url-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
}
</style>
</head>
<body>
<div class="player-container">
<h1>M3U8 在线播放器</h1>
<video id="videoPlayer" class="video-player" controls>您的浏览器不支持视频播放。</video>
<div class="controls">
<input
type="text"
id="urlInput"
class="url-input"
placeholder="输入 M3U8 流地址..."
value="https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8"
/>
<button id="loadBtn" class="btn btn-primary">加载</button>
<button id="playBtn" class="btn btn-secondary">播放</button>
<button id="pauseBtn" class="btn btn-secondary">暂停</button>
</div>
<div
id="status"
style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 4px;"
>
状态: 就绪
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
class M3U8Player {
constructor(videoElement, statusElement) {
this.video = videoElement;
this.status = statusElement;
this.hls = null;
this.currentUrl = '';
this.init();
}
init() {
// 检查浏览器支持
if (Hls.isSupported()) {
console.log('HLS.js 支持此浏览器');
} else if (this.video.canPlayType('application/vnd.apple.mpegurl')) {
console.log('原生 HLS 支持');
} else {
this.updateStatus('错误: 浏览器不支持 HLS 播放');
return;
}
// 绑定事件
this.bindEvents();
}
bindEvents() {
// 视频事件
this.video.addEventListener('loadstart', () => {
this.updateStatus('开始加载...');
});
this.video.addEventListener('loadedmetadata', () => {
this.updateStatus('元数据加载完成');
});
this.video.addEventListener('canplay', () => {
this.updateStatus('可以播放');
});
this.video.addEventListener('play', () => {
this.updateStatus('正在播放');
});
this.video.addEventListener('pause', () => {
this.updateStatus('已暂停');
});
this.video.addEventListener('error', (e) => {
this.updateStatus('播放错误: ' + e.error.message);
});
// 控制按钮事件
document.getElementById('loadBtn').addEventListener('click', () => {
const url = document.getElementById('urlInput').value.trim();
if (url) {
this.loadStream(url);
}
});
document.getElementById('playBtn').addEventListener('click', () => {
this.video.play();
});
document.getElementById('pauseBtn').addEventListener('click', () => {
this.video.pause();
});
}
loadStream(url) {
this.currentUrl = url;
this.updateStatus('正在加载流...');
// 清理之前的 HLS 实例
if (this.hls) {
this.hls.destroy();
}
if (Hls.isSupported()) {
// 使用 HLS.js
this.hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: false,
backBufferLength: 90,
});
this.hls.loadSource(url);
this.hls.attachMedia(this.video);
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
this.updateStatus('流加载完成,可以播放');
});
this.hls.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS 错误:', data);
if (data.fatal) {
this.updateStatus('HLS 错误: ' + data.details);
}
});
} else if (this.video.canPlayType('application/vnd.apple.mpegurl')) {
// 使用原生 HLS 支持
this.video.src = url;
this.video.addEventListener('loadedmetadata', () => {
this.updateStatus('流加载完成,可以播放');
});
}
}
updateStatus(message) {
this.status.textContent = '状态: ' + message;
console.log('状态更新:', message);
}
}
// 初始化播放器
const videoElement = document.getElementById('videoPlayer');
const statusElement = document.getElementById('status');
const player = new M3U8Player(videoElement, statusElement);
</script>
</body>
</html>
高级功能示例
带质量选择的播放器
创建一个支持质量级别选择的播放器:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>高级 M3U8 播放器</title>
<style>
.player-container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.video-player {
width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.controls {
margin-top: 20px;
display: grid;
grid-template-columns: 1fr auto auto auto;
gap: 10px;
align-items: center;
}
.url-input {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.quality-select {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
background-color: white;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.info-panel {
margin-top: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 8px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 5px 0;
border-bottom: 1px solid #e9ecef;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-weight: bold;
color: #495057;
}
.info-value {
color: #6c757d;
}
</style>
</head>
<body>
<div class="player-container">
<h1>高级 M3U8 播放器</h1>
<video id="videoPlayer" class="video-player" controls>您的浏览器不支持视频播放。</video>
<div class="controls">
<input
type="text"
id="urlInput"
class="url-input"
placeholder="输入 M3U8 流地址..."
value="https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8"
/>
<select id="qualitySelect" class="quality-select">
<option value="-1">自动</option>
</select>
<button id="loadBtn" class="btn btn-primary">加载</button>
<button id="reloadBtn" class="btn btn-primary">重新加载</button>
</div>
<div class="info-panel">
<div class="info-item">
<span class="info-label">状态:</span>
<span id="status" class="info-value">就绪</span>
</div>
<div class="info-item">
<span class="info-label">当前质量:</span>
<span id="currentQuality" class="info-value">-</span>
</div>
<div class="info-item">
<span class="info-label">可用质量:</span>
<span id="availableQualities" class="info-value">-</span>
</div>
<div class="info-item">
<span class="info-label">当前时间:</span>
<span id="currentTime" class="info-value">00:00</span>
</div>
<div class="info-item">
<span class="info-label">总时长:</span>
<span id="duration" class="info-value">00:00</span>
</div>
<div class="info-item">
<span class="info-label">缓冲进度:</span>
<span id="bufferProgress" class="info-value">0%</span>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
class AdvancedM3U8Player {
constructor(videoElement) {
this.video = videoElement;
this.hls = null;
this.currentUrl = '';
this.qualityLevels = [];
this.init();
}
init() {
if (!Hls.isSupported() && !this.video.canPlayType('application/vnd.apple.mpegurl')) {
this.updateStatus('错误: 浏览器不支持 HLS 播放');
return;
}
this.bindEvents();
this.startTimeUpdate();
}
bindEvents() {
// 视频事件
this.video.addEventListener('loadstart', () => {
this.updateStatus('开始加载...');
});
this.video.addEventListener('loadedmetadata', () => {
this.updateStatus('元数据加载完成');
this.updateDuration();
});
this.video.addEventListener('canplay', () => {
this.updateStatus('可以播放');
});
this.video.addEventListener('play', () => {
this.updateStatus('正在播放');
});
this.video.addEventListener('pause', () => {
this.updateStatus('已暂停');
});
this.video.addEventListener('error', (e) => {
this.updateStatus('播放错误: ' + e.error.message);
});
// 控制按钮事件
document.getElementById('loadBtn').addEventListener('click', () => {
const url = document.getElementById('urlInput').value.trim();
if (url) {
this.loadStream(url);
}
});
document.getElementById('reloadBtn').addEventListener('click', () => {
if (this.currentUrl) {
this.loadStream(this.currentUrl);
}
});
// 质量选择事件
document.getElementById('qualitySelect').addEventListener('change', (e) => {
const level = parseInt(e.target.value);
this.setQualityLevel(level);
});
}
loadStream(url) {
this.currentUrl = url;
this.updateStatus('正在加载流...');
// 清理之前的 HLS 实例
if (this.hls) {
this.hls.destroy();
}
if (Hls.isSupported()) {
this.hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: false,
backBufferLength: 90,
maxBufferLength: 30,
maxMaxBufferLength: 600,
});
this.hls.loadSource(url);
this.hls.attachMedia(this.video);
this.hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
this.updateStatus('流加载完成,可以播放');
this.updateQualityLevels(data.levels);
});
this.hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
this.updateCurrentQuality(data.level);
});
this.hls.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS 错误:', data);
if (data.fatal) {
this.updateStatus('HLS 错误: ' + data.details);
}
});
} else if (this.video.canPlayType('application/vnd.apple.mpegurl')) {
this.video.src = url;
this.video.addEventListener('loadedmetadata', () => {
this.updateStatus('流加载完成,可以播放');
});
}
}
updateQualityLevels(levels) {
this.qualityLevels = levels;
const select = document.getElementById('qualitySelect');
// 清空现有选项(保留"自动"选项)
select.innerHTML = '<option value="-1">自动</option>';
// 添加质量级别选项
levels.forEach((level, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = this.formatQualityLevel(level);
select.appendChild(option);
});
this.updateAvailableQualities();
}
formatQualityLevel(level) {
if (level.height) {
return `${level.height}p (${Math.round(level.bitrate / 1000)}kbps)`;
} else if (level.bitrate) {
return `${Math.round(level.bitrate / 1000)}kbps`;
} else {
return `Level ${level.level}`;
}
}
setQualityLevel(level) {
if (this.hls && level >= 0 && level < this.qualityLevels.length) {
this.hls.currentLevel = level;
this.updateCurrentQuality(level);
} else if (level === -1) {
this.hls.currentLevel = -1;
this.updateCurrentQuality(-1);
}
}
updateCurrentQuality(level) {
const currentQualityElement = document.getElementById('currentQuality');
if (level === -1) {
currentQualityElement.textContent = '自动';
} else if (this.qualityLevels[level]) {
currentQualityElement.textContent = this.formatQualityLevel(this.qualityLevels[level]);
}
}
updateAvailableQualities() {
const availableQualitiesElement = document.getElementById('availableQualities');
const count = this.qualityLevels.length;
availableQualitiesElement.textContent = `${count} 个质量级别`;
}
updateDuration() {
const durationElement = document.getElementById('duration');
if (this.video.duration && !isNaN(this.video.duration)) {
durationElement.textContent = this.formatTime(this.video.duration);
}
}
startTimeUpdate() {
setInterval(() => {
this.updateCurrentTime();
this.updateBufferProgress();
}, 1000);
}
updateCurrentTime() {
const currentTimeElement = document.getElementById('currentTime');
if (this.video.currentTime && !isNaN(this.video.currentTime)) {
currentTimeElement.textContent = this.formatTime(this.video.currentTime);
}
}
updateBufferProgress() {
const bufferProgressElement = document.getElementById('bufferProgress');
if (this.video.buffered.length > 0) {
const bufferedEnd = this.video.buffered.end(this.video.buffered.length - 1);
const duration = this.video.duration;
if (duration > 0) {
const progress = (bufferedEnd / duration) * 100;
bufferProgressElement.textContent = `${Math.round(progress)}%`;
}
}
}
formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${secs.toString().padStart(2, '0')}`;
}
}
updateStatus(message) {
document.getElementById('status').textContent = message;
console.log('状态更新:', message);
}
}
// 初始化播放器
const videoElement = document.getElementById('videoPlayer');
const player = new AdvancedM3U8Player(videoElement);
</script>
</body>
</html>
React 组件示例
React M3U8 播放器组件
import React, { useState, useEffect, useRef } from 'react';
import Hls from 'hls.js';
const M3U8Player = ({ url, autoplay = false, controls = true }) => {
const videoRef = useRef(null);
const hlsRef = useRef(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [qualityLevels, setQualityLevels] = useState([]);
const [currentQuality, setCurrentQuality] = useState(-1);
useEffect(() => {
const video = videoRef.current;
if (!video) return;
// 检查浏览器支持
if (Hls.isSupported()) {
console.log('使用 HLS.js');
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
console.log('使用原生 HLS 支持');
} else {
setError('浏览器不支持 HLS 播放');
return;
}
// 绑定视频事件
const handleLoadStart = () => setIsLoading(true);
const handleLoadedMetadata = () => {
setIsLoading(false);
setDuration(video.duration);
};
const handleTimeUpdate = () => setCurrentTime(video.currentTime);
const handlePlay = () => setIsPlaying(true);
const handlePause = () => setIsPlaying(false);
const handleError = (e) => {
setError(`播放错误: ${e.error.message}`);
setIsLoading(false);
};
video.addEventListener('loadstart', handleLoadStart);
video.addEventListener('loadedmetadata', handleLoadedMetadata);
video.addEventListener('timeupdate', handleTimeUpdate);
video.addEventListener('play', handlePlay);
video.addEventListener('pause', handlePause);
video.addEventListener('error', handleError);
return () => {
video.removeEventListener('loadstart', handleLoadStart);
video.removeEventListener('loadedmetadata', handleLoadedMetadata);
video.removeEventListener('timeupdate', handleTimeUpdate);
video.removeEventListener('play', handlePlay);
video.removeEventListener('pause', handlePause);
video.removeEventListener('error', handleError);
};
}, []);
useEffect(() => {
if (!url) return;
const video = videoRef.current;
if (!video) return;
// 清理之前的 HLS 实例
if (hlsRef.current) {
hlsRef.current.destroy();
hlsRef.current = null;
}
setError(null);
setIsLoading(true);
if (Hls.isSupported()) {
const hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: false,
backBufferLength: 90,
});
hls.loadSource(url);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
setIsLoading(false);
setQualityLevels(data.levels);
if (autoplay) {
video.play();
}
});
hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
setCurrentQuality(data.level);
});
hls.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS 错误:', data);
if (data.fatal) {
setError(`HLS 错误: ${data.details}`);
setIsLoading(false);
}
});
hlsRef.current = hls;
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = url;
if (autoplay) {
video.play();
}
}
return () => {
if (hlsRef.current) {
hlsRef.current.destroy();
hlsRef.current = null;
}
};
}, [url, autoplay]);
const handleQualityChange = (level) => {
if (hlsRef.current && level >= 0 && level < qualityLevels.length) {
hlsRef.current.currentLevel = level;
setCurrentQuality(level);
} else if (level === -1) {
hlsRef.current.currentLevel = -1;
setCurrentQuality(-1);
}
};
const formatTime = (seconds) => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${secs.toString().padStart(2, '0')}`;
}
};
const formatQualityLevel = (level) => {
if (level.height) {
return `${level.height}p (${Math.round(level.bitrate / 1000)}kbps)`;
} else if (level.bitrate) {
return `${Math.round(level.bitrate / 1000)}kbps`;
} else {
return `Level ${level.level}`;
}
};
return (
<div className="m3u8-player">
<video ref={videoRef} controls={controls} style={{ width: '100%', height: 'auto' }}>
您的浏览器不支持视频播放。
</video>
{isLoading && <div className="loading">正在加载...</div>}
{error && (
<div className="error" style={{ color: 'red', marginTop: '10px' }}>
{error}
</div>
)}
{qualityLevels.length > 0 && (
<div className="quality-controls" style={{ marginTop: '10px' }}>
<label htmlFor="quality-select">质量选择: </label>
<select
id="quality-select"
value={currentQuality}
onChange={(e) => handleQualityChange(parseInt(e.target.value))}
>
<option value={-1}>自动</option>
{qualityLevels.map((level, index) => (
<option key={index} value={index}>
{formatQualityLevel(level)}
</option>
))}
</select>
</div>
)}
<div className="player-info" style={{ marginTop: '10px', fontSize: '14px', color: '#666' }}>
<span>
时间: {formatTime(currentTime)} / {formatTime(duration)}
</span>
{isPlaying ? <span> | 正在播放</span> : <span> | 已暂停</span>}
</div>
</div>
);
};
export default M3U8Player;
使用 React 组件
import React, { useState } from 'react';
import M3U8Player from './M3U8Player';
const App = () => {
const [streamUrl, setStreamUrl] = useState(
'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8',
);
return (
<div className="app">
<h1>M3U8 播放器示例</h1>
<div style={{ marginBottom: '20px' }}>
<input
type="text"
value={streamUrl}
onChange={(e) => setStreamUrl(e.target.value)}
placeholder="输入 M3U8 流地址..."
style={{ width: '100%', padding: '10px', marginBottom: '10px' }}
/>
</div>
<M3U8Player url={streamUrl} autoplay={false} controls={true} />
</div>
);
};
export default App;
Vue.js 组件示例
Vue M3U8 播放器组件
<template>
<div class="m3u8-player">
<video
ref="videoElement"
:controls="controls"
:autoplay="autoplay"
style="width: 100%; height: auto;"
>
您的浏览器不支持视频播放。
</video>
<div v-if="isLoading" class="loading">正在加载...</div>
<div v-if="error" class="error" style="color: red; margin-top: 10px;">
{{ error }}
</div>
<div v-if="qualityLevels.length > 0" class="quality-controls" style="margin-top: 10px;">
<label for="quality-select">质量选择: </label>
<select id="quality-select" :value="currentQuality" @change="handleQualityChange">
<option value="-1">自动</option>
<option v-for="(level, index) in qualityLevels" :key="index" :value="index">
{{ formatQualityLevel(level) }}
</option>
</select>
</div>
<div class="player-info" style="margin-top: 10px; font-size: 14px; color: #666;">
<span>时间: {{ formatTime(currentTime) }} / {{ formatTime(duration) }}</span>
<span v-if="isPlaying"> | 正在播放</span>
<span v-else> | 已暂停</span>
</div>
</div>
</template>
<script>
import Hls from 'hls.js';
export default {
name: 'M3U8Player',
props: {
url: {
type: String,
required: true,
},
autoplay: {
type: Boolean,
default: false,
},
controls: {
type: Boolean,
default: true,
},
},
data() {
return {
hls: null,
isLoading: false,
error: null,
currentTime: 0,
duration: 0,
isPlaying: false,
qualityLevels: [],
currentQuality: -1,
};
},
mounted() {
this.initPlayer();
},
beforeUnmount() {
this.destroyPlayer();
},
watch: {
url: {
handler(newUrl) {
if (newUrl) {
this.loadStream(newUrl);
}
},
immediate: true,
},
},
methods: {
initPlayer() {
const video = this.$refs.videoElement;
if (!video) return;
// 检查浏览器支持
if (Hls.isSupported()) {
console.log('使用 HLS.js');
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
console.log('使用原生 HLS 支持');
} else {
this.error = '浏览器不支持 HLS 播放';
return;
}
// 绑定视频事件
video.addEventListener('loadstart', this.handleLoadStart);
video.addEventListener('loadedmetadata', this.handleLoadedMetadata);
video.addEventListener('timeupdate', this.handleTimeUpdate);
video.addEventListener('play', this.handlePlay);
video.addEventListener('pause', this.handlePause);
video.addEventListener('error', this.handleError);
},
destroyPlayer() {
if (this.hls) {
this.hls.destroy();
this.hls = null;
}
},
loadStream(url) {
const video = this.$refs.videoElement;
if (!video) return;
this.destroyPlayer();
this.error = null;
this.isLoading = true;
if (Hls.isSupported()) {
this.hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: false,
backBufferLength: 90,
});
this.hls.loadSource(url);
this.hls.attachMedia(video);
this.hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
this.isLoading = false;
this.qualityLevels = data.levels;
if (this.autoplay) {
video.play();
}
});
this.hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
this.currentQuality = data.level;
});
this.hls.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS 错误:', data);
if (data.fatal) {
this.error = `HLS 错误: ${data.details}`;
this.isLoading = false;
}
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = url;
if (this.autoplay) {
video.play();
}
}
},
handleQualityChange(event) {
const level = parseInt(event.target.value);
if (this.hls && level >= 0 && level < this.qualityLevels.length) {
this.hls.currentLevel = level;
this.currentQuality = level;
} else if (level === -1) {
this.hls.currentLevel = -1;
this.currentQuality = -1;
}
},
handleLoadStart() {
this.isLoading = true;
},
handleLoadedMetadata() {
this.isLoading = false;
this.duration = this.$refs.videoElement.duration;
},
handleTimeUpdate() {
this.currentTime = this.$refs.videoElement.currentTime;
},
handlePlay() {
this.isPlaying = true;
},
handlePause() {
this.isPlaying = false;
},
handleError(event) {
this.error = `播放错误: ${event.error.message}`;
this.isLoading = false;
},
formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
} else {
return `${minutes}:${secs.toString().padStart(2, '0')}`;
}
},
formatQualityLevel(level) {
if (level.height) {
return `${level.height}p (${Math.round(level.bitrate / 1000)}kbps)`;
} else if (level.bitrate) {
return `${Math.round(level.bitrate / 1000)}kbps`;
} else {
return `Level ${level.level}`;
}
},
},
};
</script>
<style scoped>
.m3u8-player {
max-width: 800px;
margin: 0 auto;
}
.loading {
text-align: center;
padding: 20px;
color: #666;
}
.error {
padding: 10px;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
color: #721c24;
}
.quality-controls {
display: flex;
align-items: center;
gap: 10px;
}
.quality-controls select {
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.player-info {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
这些示例展示了如何在不同框架中集成和使用 M3U8 播放器,包括基本功能、高级功能和质量选择等特性。开发者可以根据自己的需求选择合适的示例进行参考和修改。