Triple HLS Comparison Tool 是一个专业的 HLS (HTTP Live Streaming) 三重对比测试工具,同时运行三种不同的 HLS 播放器进行对比:Native HLS、HLS.js Standard 和 HLS.js ABR。该项目基于 Next.js 15 构建,使用 TypeScript 开发,提供专业级的流媒体兼容性测试功能。
- 框架: Next.js 15.5.2 (App Router)
- 开发语言: TypeScript 5.x
- UI 样式: Tailwind CSS 4.0
- 视频播放: HLS.js 1.6.11
- 构建工具: Turbopack (Next.js 内置)
- 代码质量: ESLint 9.x
{
"dependencies": {
"hls.js": "^1.6.11",
"next": "15.5.2",
"react": "19.1.1",
"react-dom": "19.1.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"eslint": "^9",
"typescript": "^5"
}
}src/
├── app/ # App Router 目录
│ ├── layout.tsx # 根布局组件
│ ├── page.tsx # 首页组件
│ └── globals.css # 全局样式
├── components/ # 可复用组件
│ ├── TripleComparisonPlayer.tsx # 三重对比播放器
│ ├── StreamSelector.tsx # 流选择器
│ └── DeviceInfo.tsx # 设备信息展示
└── data/
└── test-streams.ts # 测试流数据配置
Triple Comparison Mode 允许您同时运行三种不同的 HLS 播放器,实时对比它们的性能和兼容性差异:
- Native HLS - 浏览器原生 HLS 支持
- HLS.js Standard - 标准 HLS.js 配置
- HLS.js ABR - 增强自适应码率配置
- 从流选择器中选择一个预设的测试流
- 或者输入自定义 HLS 流 URL
- 支持的测试流包括:
- Big Buck Bunny (fMP4) - Apple官方测试流
- Dolby Vision/Atmos (4K HDR) - 高质量HDR内容
- Wowza Test Stream - Bitdash测试流
- HLS.js Demo Stream - Mux低带宽测试
- Cloudflare Stream - 自适应码率测试
- 左侧 - Native HLS: 使用浏览器原生 HLS 支持,在 Safari 中提供最佳性能
- 中间 - HLS.js Standard: 使用标准 HLS.js 配置,适用于大多数浏览器
- 右侧 - HLS.js ABR: 使用增强 ABR 配置,优化自适应码率切换
每个播放器都有独立的控制:
- 播放/暂停按钮: 独立控制每个播放器
- 音量控制: 独立音量设置
- 进度条: 显示播放进度和缓冲状态
每个播放器显示实时指标:
- Player Type: 播放器类型识别
- Load Time: 初始化加载时间
- Current Time: 当前播放时间
- Buffer: 缓冲区剩余时间
- Bitrate: 当前码率(kbps)
- Playing: 播放状态指示
显示三个播放器的详细对比:
Native HLS HLS.js Standard HLS.js ABR
Type: native Type: hls.js Type: hls.js-abr
Load Time: 245ms Load Time: 312ms Load Time: 289ms
Current Time: 15.23s Current Time: 15.19s Current Time: 15.21s
Buffer: 5.2s Buffer: 4.8s Buffer: 6.1s
Bitrate: 2500kbps Bitrate: 2300kbps Bitrate: 2800kbps
Playing: ▶️ Yes Playing: ▶️ Yes Playing: ▶️ Yes
- Max Sync Diff: 三个播放器间的最大时间同步差异
- Load Time Range: 加载时间差异范围
- Buffer Range: 缓冲区差异范围
- Bitrate Range: 码率差异范围
- 理想值: < 0.5s 差异
- 警告值: 0.5s - 2s 差异
- 问题指标: > 2s 差异表示存在兼容性问题
- Native HLS 通常在 Safari 中加载最快
- HLS.js Standard 提供跨浏览器一致性
- HLS.js ABR 可能有轻微延迟但提供更好的自适应性
- 观察三个播放器的缓冲策略差异
- ABR 配置通常维持更大的缓冲区
- Native HLS 依赖浏览器默认缓冲策略
- 比较不同配置的码率切换策略
- ABR 配置对网络变化更敏感
- 监控码率切换的平滑度
1. 选择 Big Buck Bunny 测试流
2. 观察三个播放器的加载时间
3. 检查播放同步差异
4. 监控初始缓冲行为
1. 选择多码率流(如 Cloudflare Stream)
2. 模拟网络波动
3. 对比三种播放器的码率切换
4. 分析 ABR 配置的优势
1. 选择 4K HDR Dolby Vision 内容
2. 比较不同播放器的解码性能
3. 检查高码率下的缓冲策略
4. 验证高级特性支持
1. 在不同浏览器中测试相同流
2. 对比 Safari vs Chrome vs Firefox
3. 验证 Native HLS 与 HLS.js 的差异
4. 记录特定浏览器的问题
- 可能原因: 浏览器不支持原生 HLS
- 解决方案: 在 Safari 中测试,或检查流格式兼容性
- 可能原因: MSE 不支持或流格式问题
- 解决方案: 检查浏览器 MediaSource 支持
- 可能原因: 网络延迟或解码性能差异
- 分析方法: 检查网络条件和设备性能
- 可能原因: ABR 配置过于激进或网络不稳定
- 调整方案: 修改 ABR 参数或测试不同网络环境
这是项目的核心组件,同时管理三个不同的 HLS 播放器实例。
interface TripleComparisonPlayerProps {
src: string;
title: string;
className?: string;
onMetricsUpdate?: (metrics: {
nativePlayer: PlayerMetrics;
hlsjsPlayer: PlayerMetrics;
autoPlayer: PlayerMetrics;
}) => void;
}
interface PlayerMetrics {
playerType: string;
loadTime: number;
currentTime: number;
buffered: number;
isPlaying: boolean;
bitrate: number;
}// 检测原生 HLS 支持
const nativeSupport = video.canPlayType('application/vnd.apple.mpegurl');
if (nativeSupport) {
video.src = src; // 直接使用原生播放
}const hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 30,
maxBufferLength: 120,
maxMaxBufferLength: 300,
});const hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 30,
maxBufferLength: 120,
maxMaxBufferLength: 300,
// ABR 特定设置
capLevelOnFPSDrop: true,
capLevelToPlayerSize: true,
abrEwmaFastLive: 3.0,
abrEwmaSlowLive: 9.0,
abrEwmaFastVoD: 3.0,
abrEwmaSlowVoD: 9.0,
abrEwmaDefaultEstimate: 500000,
abrBandWidthFactor: 0.95,
abrBandWidthUpFactor: 0.7,
maxStarvationDelay: 4,
maxLoadingDelay: 4,
});const initializePlayer = useCallback(async () => {
if (!videoRef.current || !src) return;
setIsLoading(true);
setError(null);
onLoadStart?.();
try {
const video = videoRef.current;
// 检测 HLS 原生支持 (Safari)
const safari = isSafari();
const nativeSupport = video.canPlayType('application/vnd.apple.mpegurl');
if (nativeSupport && safari) {
// 使用 Safari 原生 HLS 支持
video.src = src;
setPlayerInfo({
currentLevel: -1,
levels: [],
isSupported: true,
isSafari: true,
version: 'Native Safari Support'
});
} else if (Hls.isSupported()) {
// 使用 hls.js 处理其他浏览器
const hls = new Hls({
debug: process.env.NODE_ENV === 'development',
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90,
maxBufferLength: 300,
maxMaxBufferLength: 600,
capLevelOnFPSDrop: true,
capLevelToPlayerSize: true,
});
// 事件监听器配置
hls.on(Hls.Events.MANIFEST_PARSED, (_, data) => {
console.log('Manifest parsed, levels:', data.levels);
setPlayerInfo({
currentLevel: hls.currentLevel,
levels: data.levels,
isSupported: true,
isSafari: false,
version: Hls.version
});
onLoadComplete?.();
});
hls.on(Hls.Events.LEVEL_SWITCHED, (_, data) => {
console.log('Level switched to:', data.level);
setPlayerInfo(prev => prev ? { ...prev, currentLevel: data.level } : null);
});
// 错误处理
hls.on(Hls.Events.ERROR, (_, data) => {
console.error('HLS Error:', data);
if (data.fatal) {
setError(`HLS Error: ${data.type} - ${data.details}`);
onError?.(data);
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
hls.recoverMediaError();
break;
default:
hls.destroy();
break;
}
}
});
hls.loadSource(src);
hls.attachMedia(video);
} else {
setError('HLS is not supported in this browser');
onError?.({ type: 'UNSUPPORTED', message: 'HLS not supported' });
}
} catch (err) {
console.error('Player initialization error:', err);
setError('Failed to initialize player');
onError?.(err instanceof Error ? err : { message: 'Failed to initialize player' });
} finally {
setIsLoading(false);
}
}, [src, onError, onLoadStart, onLoadComplete, isSafari]);负责管理和展示可用的测试流,支持分类筛选和自定义 URL 输入。
export default function StreamSelector({ onStreamSelect, selectedStream }: StreamSelectorProps) {
const [selectedCategory, setSelectedCategory] = useState<StreamCategory>(STREAM_CATEGORIES.ALL);
const [customUrl, setCustomUrl] = useState('');
const [showCustomInput, setShowCustomInput] = useState(false);
// 流筛选逻辑
const filteredStreams = TEST_STREAMS.filter(stream => {
if (selectedCategory === STREAM_CATEGORIES.ALL) return true;
if (selectedCategory === STREAM_CATEGORIES.VOD) return stream.type === 'VOD';
if (selectedCategory === STREAM_CATEGORIES.LIVE) return stream.type === 'LIVE';
if (selectedCategory === STREAM_CATEGORIES.LOW_RES) {
return stream.resolution.includes('240p') || stream.resolution.includes('360p');
}
if (selectedCategory === STREAM_CATEGORIES.HIGH_RES) {
return stream.resolution.includes('4K') || stream.resolution.includes('UHD') || stream.resolution.includes('1080p');
}
if (selectedCategory === STREAM_CATEGORIES.MOBILE) {
return stream.resolution.includes('240p') || stream.resolution.includes('360p') || stream.resolution.includes('480p');
}
return true;
});
// 自定义流处理
const handleCustomStreamLoad = () => {
if (customUrl.trim()) {
const customStream: StreamConfig = {
name: 'Custom Stream',
url: customUrl.trim(),
description: 'User provided stream URL',
resolution: 'Unknown',
type: 'VOD',
source: 'Custom'
};
onStreamSelect(customStream);
setShowCustomInput(false);
}
};
}提供详细的设备、浏览器和兼容性信息,用于诊断播放问题。
const detectDevice = () => {
const ua = navigator.userAgent;
// 浏览器检测
const isSafari = /^((?!chrome|android).)*safari/i.test(ua);
const isChrome = /chrome/i.test(ua) && !/edge/i.test(ua);
const isFirefox = /firefox/i.test(ua);
const isEdge = /edge/i.test(ua);
// 操作系统检测
const isIOS = /iPad|iPhone|iPod/.test(ua);
const isAndroid = /Android/.test(ua);
const isMac = /Mac/.test(ua);
const isWindows = /Win/.test(ua);
const isLinux = /Linux/.test(ua);
// HLS 支持检测
const video = document.createElement('video');
const nativeHlsSupported = video.canPlayType('application/vnd.apple.mpegurl') !== '';
const hlsSupported = Hls.isSupported();
const mseSupported = 'MediaSource' in window;
// 网络连接信息
const connection = (navigator as Navigator & {
connection?: { effectiveType?: string; type?: string };
mozConnection?: { effectiveType?: string; type?: string };
webkitConnection?: { effectiveType?: string; type?: string }
}).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection;
const connectionType = connection ? connection.effectiveType || connection.type || 'Unknown' : 'Unknown';
// 屏幕信息
const screenSize = `${screen.width}x${screen.height}`;
const viewportSize = `${window.innerWidth}x${window.innerHeight}`;
const pixelRatio = window.devicePixelRatio;
};提供连接性测试、结果记录和导出功能。
interface TestResult {
streamName: string;
url: string;
timestamp: Date;
status: 'success' | 'error' | 'warning';
loadTime: number;
errorMessage?: string;
playerType: 'hls.js' | 'native' | 'unknown';
quality?: string;
}const runConnectivityTest = async () => {
if (!currentStream) return;
setIsRunningTest(true);
const startTime = performance.now();
try {
const response = await fetch(currentStream, {
method: 'HEAD',
mode: 'cors'
});
const loadTime = performance.now() - startTime;
const result: TestResult = {
streamName: 'Stream Connectivity Test',
url: currentStream,
timestamp: new Date(),
status: response.ok ? 'success' : 'error',
loadTime: Math.round(loadTime),
errorMessage: response.ok ? undefined : `HTTP ${response.status}: ${response.statusText}`,
playerType: 'unknown'
};
setTestResults(prev => [result, ...prev.slice(0, 49)]);
onTestResult?.(result);
} catch (error) {
const loadTime = performance.now() - startTime;
const result: TestResult = {
streamName: 'Stream Connectivity Test',
url: currentStream,
timestamp: new Date(),
status: 'error',
loadTime: Math.round(loadTime),
errorMessage: error instanceof Error ? error.message : 'Network error',
playerType: 'unknown'
};
setTestResults(prev => [result, ...prev.slice(0, 49)]);
onTestResult?.(result);
} finally {
setIsRunningTest(false);
}
};项目包含丰富的预设测试流,涵盖不同质量、格式和来源:
export interface StreamConfig {
name: string;
url: string;
description: string;
resolution: string;
type: 'VOD' | 'LIVE';
source: string;
}
export const TEST_STREAMS: StreamConfig[] = [
// Apple 官方测试流
{
name: 'Big Buck Bunny (fMP4)',
url: 'https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8',
description: '经典测试视频,支持自适应码率',
resolution: '多分辨率 (240p-1080p)',
type: 'VOD',
source: 'Apple'
},
// 杜比视界/全景声内容
{
name: 'Dolby Vision/Atmos (4K HDR)',
url: 'https://devstreaming-cdn.apple.com/videos/streaming/examples/adv_dv_atmos/main.m3u8',
description: '支持Dolby Vision和Atmos的4K HDR内容',
resolution: '4K (3840x2160)',
type: 'VOD',
source: 'Apple'
},
// 实时流
{
name: 'Akamai Test Live',
url: 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8',
description: 'Akamai提供的实时流测试',
resolution: '多分辨率',
type: 'LIVE',
source: 'Akamai'
}
// ... 更多测试流
];export const STREAM_CATEGORIES = {
ALL: 'all',
VOD: 'VOD',
LIVE: 'LIVE',
LOW_RES: 'low_res',
HIGH_RES: 'high_res',
MOBILE: 'mobile'
} as const;export default function Home() {
const [selectedStream, setSelectedStream] = useState<StreamConfig | null>(null);
const [playerErrors, setPlayerErrors] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(false);
// 流选择处理
const handleStreamSelect = useCallback((stream: StreamConfig) => {
setSelectedStream(stream);
setPlayerErrors([]);
}, []);
// 错误处理
const handlePlayerError = useCallback((error: Error | string | { message?: string; details?: string }) => {
const errorMessage = typeof error === 'string' ? error :
('message' in error ? error.message : undefined) || ('details' in error ? error.details : undefined) || 'Unknown player error';
setPlayerErrors(prev => [...prev.slice(0, 4), errorMessage]);
}, []);
// 加载状态管理
const handleLoadStart = useCallback(() => {
setIsLoading(true);
}, []);
const handleLoadComplete = useCallback(() => {
setIsLoading(false);
}, []);
}- 初始化: Next.js 应用启动,加载全局样式和字体
- 设备检测: DeviceInfo 组件检测浏览器、操作系统和 HLS 支持
- 界面渲染: 渲染流选择器和播放器界面
- 等待交互: 等待用户选择测试流
- 流选择: 用户在 StreamSelector 中选择或输入流 URL
- 播放器配置: HLSPlayer 根据浏览器类型选择播放策略
- 兼容性处理:
- Safari: 优先使用原生 HLS 支持
- 其他浏览器: 使用 HLS.js 库
- 错误处理: 监听各种播放错误并提供恢复机制
- 状态反馈: 实时更新播放状态和错误信息
- 连接性测试: TestControlPanel 发送 HEAD 请求测试流可达性
- 播放测试: 实际播放流并监控性能指标
- 结果记录: 记录测试结果并存储到 localStorage
- 报告生成: 支持导出 JSON 格式的测试报告
const hls = new Hls({
debug: process.env.NODE_ENV === 'development',
enableWorker: true, // 启用 Web Worker
lowLatencyMode: true, // 低延迟模式
backBufferLength: 90, // 后缓冲区长度
maxBufferLength: 300, // 最大缓冲区长度
maxMaxBufferLength: 600, // 最大缓冲区限制
capLevelOnFPSDrop: true, // 帧率下降时限制质量
capLevelToPlayerSize: true, // 根据播放器大小限制质量
});hls.on(Hls.Events.ERROR, (_, data) => {
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
// 网络错误:重新开始加载
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
// 媒体错误:尝试恢复
hls.recoverMediaError();
break;
default:
// 其他致命错误:销毁播放器
hls.destroy();
break;
}
}
});- 使用 Tailwind CSS 的响应式类名
- 支持移动端、平板和桌面设备
- 自适应视口尺寸变化
{isLoading && (
<div className="absolute inset-0 bg-black bg-opacity-75 flex items-center justify-center z-10">
<div className="text-white text-center">
<div className="animate-spin w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full mx-auto mb-2"></div>
<p className="text-sm">Loading {title}...</p>
</div>
</div>
)}{error && (
<div className="absolute inset-0 bg-red-900 bg-opacity-90 flex items-center justify-center z-10">
<div className="text-white text-center p-4">
<div className="text-red-400 text-xl mb-2">⚠️</div>
<p className="text-sm font-medium mb-1">Playback Error</p>
<p className="text-xs opacity-75">{error}</p>
<button
onClick={initializePlayer}
className="mt-3 px-3 py-1 bg-red-600 hover:bg-red-700 text-white text-xs rounded transition-colors"
>
Retry
</button>
</div>
</div>
)}项目使用严格的 TypeScript 配置,确保类型安全:
// 播放器属性类型
interface HLSPlayerProps {
src: string;
title: string;
className?: string;
onError?: (error: Error | { type?: string; message?: string; details?: string }) => void;
onLoadStart?: () => void;
onLoadComplete?: () => void;
}
// 设备信息类型
interface DeviceInfoState {
browser: string;
version: string;
os: string;
isMobile: boolean;
isTablet: boolean;
isSafari: boolean;
isChrome: boolean;
isFirefox: boolean;
isEdge: boolean;
hlsSupported: boolean;
mseSupported: boolean;
nativeHlsSupported: boolean;
screenSize: string;
pixelRatio: number;
connectionType: string;
userAgent: string;
viewportSize: string;
}使用严格的 ESLint 规则,禁止使用 any 类型,确保代码质量。
虽然项目代码中没有显式的字幕处理逻辑,但支持字幕显示的原理是:
- Apple 测试流(如
img_bipbop_adv_example_fmp4)包含内嵌的 WebVTT 字幕轨道 - 杜比视界内容通常包含多语言字幕
- Safari 原生支持: 自动检测并显示 HLS 流中的字幕轨道
- HLS.js 支持: 自动解析 manifest 中的字幕信息并处理 WebVTT 内容
- 字幕选择选项会自动出现在视频播放控制栏中
- 用户可以通过浏览器原生界面选择字幕语言
{
"scripts": {
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint"
}
}// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// 使用默认配置,支持 Turbopack
};
export default nextConfig;// vercel.json
{
"functions": {
"app/**/*": {
"includeFiles": "public/**"
}
}
}- 使用 Next.js App Router 的自动代码分割
- 组件级别的懒加载
- Turbopack 构建优化
- 静态资源预生成
- 字体优化(Geist 字体)
- 自适应码率选择
- 缓冲区优化配置
- 错误恢复机制
useEffect(() => {
return () => {
// 清理 HLS 实例
if (hlsRef.current) {
hlsRef.current.destroy();
hlsRef.current = null;
}
};
}, []);- Safari: 原生 HLS 支持,最佳兼容性
- Chrome/Edge: HLS.js 支持,功能完整
- Firefox: HLS.js 支持,需要 MSE
- 移动浏览器: iOS Safari 原生支持,Android Chrome 通过 HLS.js
- 桌面设备: 全功能支持
- 移动设备: 触摸优化,原生播放控件
- 平板设备: 响应式界面适配
项目内置多种测试流,覆盖:
- 不同分辨率(240p - 4K)
- 不同编码格式(H.264, HEVC)
- 不同内容类型(VOD, Live)
- 高级特性(HDR, 杜比全景声)
- 浏览器检测和报告
- HLS 支持能力检测
- 网络条件测试
- 性能指标监控
- 网络中断恢复
- 媒体解码错误处理
- 流切换测试
- 超时处理
根据浏览器类型和支持能力自动选择最优播放方案:
- Safari 优先使用原生 HLS
- 其他浏览器使用 HLS.js
- 自动错误恢复和降级
提供全面的设备和环境信息,帮助诊断播放问题:
- 浏览器版本检测
- HLS 支持能力分析
- 网络条件评估
- 屏幕和设备信息
内置专业的测试功能:
- 连接性测试
- 性能指标监控
- 测试结果记录和导出
- 自定义流测试支持
- 响应式设计,适配各种设备
- 直观的错误提示和恢复机制
- 实时状态反馈
- 可折叠的详细信息面板
- 每个功能模块独立封装
- 清晰的接口定义
- 易于复用和扩展
- 测试流配置与代码分离
- 支持动态添加新的测试内容
- 分类和筛选系统
- 标准化的错误处理接口
- 可扩展的错误恢复策略
- 详细的错误信息记录
项目实现了专业级的自适应码率配置,包括:
- 智能带宽检测:
abrEwmaFastLive和abrEwmaSlowLive参数优化 - 带宽因子调整:
abrBandWidthFactor和abrBandWidthUpFactor精确控制 - 延迟优化:
maxStarvationDelay和maxLoadingDelay配置
- 同时监控三个播放器的性能指标
- 实时计算同步差异和性能范围
- 提供详细的播放状态可视化
精选的测试流覆盖:
- Apple 官方认证测试内容
- 4K HDR 和 Dolby Vision 内容
- 跨 CDN 分发测试
- 自适应码率验证流
npm run dev --turbopack # 启动开发服务器
npm run lint # 代码检查
npm run build --turbopack # 构建生产版本- Node.js 18+
- 支持 Next.js 15 的托管环境
- 推荐 Vercel 部署以获得最佳性能
Triple HLS Comparison Tool 是一个专业的 HLS 流媒体三重对比测试平台。通过同时运行 Native HLS、HLS.js Standard 和 HLS.js ABR 三种不同的播放器配置,为开发者提供了全面的 HLS 兼容性和性能分析能力。
项目的核心价值:
- 专业三重对比: 同时测试三种不同的 HLS 播放策略
- 增强 ABR 优化: 专业级自适应码率配置和优化
- 实时性能监控: 详细的播放指标和同步分析
- 全面兼容性测试: 涵盖多种设备和浏览器环境
- 现代化技术栈: Next.js 15 + TypeScript + HLS.js 1.6.11
这个工具特别适用于流媒体开发者、QA 工程师和视频平台技术团队,帮助他们验证 HLS 流在不同播放器配置下的表现差异,优化用户体验。