HTML5 视频与音频完全指南:从基础的 <video> / <audio> 标签到现代 Web 媒体应用

Hi,我是前端人类学 (之前叫布兰妮甜)!

HTML5彻底改变了我们在网页中处理媒体内容的方式,引入了原生支持的视频和音频元素,不再需要依赖Flash等第三方插件。本文将深入探讨HTML5视频和音频的实现方法、技术特性以及最佳实践。


文章目录


一、历史背景

在 HTML5 之前,网页视频几乎等同于 Flash。移动设备的崛起、SEO、安全性及电池续航压力,共同促成了 与 元素的标准化。2007 年 WHATWG 草案 → 2009 年 iPhone 率先不支持 Flash → 2010 年 YouTube 默认 HTML5 试播 → 2020 年底 Chrome 彻底禁用 Flash,标志 HTML5 媒体大一统。

二、HTML5视频实现

2.1 基本语法

html 复制代码
<video
  src="bunny.mp4"
  poster="bunny.jpg"
  preload="metadata"
  controls
  width="640"
  height="360">
  您的浏览器不支持 video 标签。
</video>

2.2 关键属性解析

  • autoplay: 页面加载后自动播放(受浏览器限制)
  • loop: 循环播放视频
  • muted: 静音播放
  • playsinline: iOS 禁止全屏弹窗

2.3 自适应比特率

html 复制代码
<video controls>
  <source src="480.mp4" type="video/mp4; codecs=avc1.42E01E">
  <source src="720.webm" type="video/webm; codecs=vp9">
  <source src="hls.m3u8" type="application/x-mpegURL">
</video>

浏览器自上而下匹配首个可解码资源;type 中带上 codecs 可避免下载不必要字节。

2.4 字幕与多轨道

html 复制代码
<track kind="subtitles" src="zh.vtt" srclang="zh-CN" label="中文" default>
<track kind="captions" src="en-cc.vtt" srclang="en">

WebVTT 支持样式、定位、声音描述。default 表示默认启用。

三、HTML5音频实现

音频元素与视频元素的使用方式几乎完全相同,但不需要视觉界面:

html 复制代码
<audio controls>
  <source src="audio.mp3" type="audio/mpeg">
  <source src="audio.ogg" type="audio/ogg">
  您的浏览器不支持 audio 标签
</audio>

四、浏览器支持

4.1 视频

  • H.264:Safari、Chrome、Edge、Firefox(需系统解码器)
  • VP9:Chrome、Edge、Firefox、Safari(14+)
  • AV1:Chrome 90+、Firefox 85+、Edge 90+、Safari 16.4+(软解或硬解)
  • HEVC:Safari(需付费授权)、Edge/Win11、Android 设备

4.2 音频

  • AAC:通用,但专利
  • Opus:WebRTC 默认,压缩效率最高
  • Vorbis:开源,WebM 标配
  • FLAC:无损,Safari 不支持

4.3 兼容性列表写法

javascript 复制代码
const support = {
  h264:  video.canPlayType('video/mp4; codecs="avc1.42E01E"'),
  av1:   video.canPlayType('video/mp4; codecs="av01.0.05M.08"'),
  opus:  audio.canPlayType('audio/webm; codecs="opus"')
};

五、JavaScript API

5.1 常用事件

  • loadstart, loadedmetadata, loadeddata, canplay, canplaythrough
  • waiting, stalled, error, ended
  • timeupdate(~4 Hz) vs requestVideoFrameCallback(精准帧同步)

5.2 自定义控制栏

隐藏原生控件:<video controls> 移除,利用 Shadow DOM 封装按钮。

javascript 复制代码
const video = document.querySelector('video');
const playBtn = document.querySelector('#play');
playBtn.onclick = () => video.paused ? video.play() : video.pause();

5.3 媒体会话

javascript 复制代码
navigator.mediaSession.metadata = new MediaMetadata({
  title: 'Big Buck Bunny',
  artist: 'Blender Foundation',
  artwork: [{ src: 'poster.jpg', sizes: '640x360', type: 'image/jpeg' }]
});
navigator.mediaSession.setActionHandler('play', () => video.play());

5.4 画中画

javascript 复制代码
video.requestPictureInPicture().then(pipWindow => { /* pipWindow.width/height 可监听 */ })

六、媒体能力检测

canPlayType 只返回 """maybe""probably",无法给出帧率/分辨率上限。

新标准:

javascript 复制代码
const result = await navigator.mediaCapabilities.decodingInfo({
  type: 'file',
  video: {
    contentType: 'video/webm;codecs=vp9',
    width: 1920, height: 1080, bitrate: 5000000, framerate: 30
  }
});
console.log(result.supported, result.powerEfficient);

七、高级功能与事件处理

7.1 媒体事件监听

javascript 复制代码
video.addEventListener('loadedmetadata', function() {
  console.log('视频时长:' + video.duration + '秒');
});

video.addEventListener('timeupdate', function() {
  const percentage = (video.currentTime / video.duration) * 100;
  updateProgressBar(percentage);
});

video.addEventListener('ended', function() {
  console.log('播放结束');
  showReplayButton();
});

7.2 全屏API

javascript 复制代码
function toggleFullscreen() {
  if (!document.fullscreenElement) {
    video.requestFullscreen().catch(err => {
      console.error(`全屏错误: ${err.message}`);
    });
  } else {
    document.exitFullscreen();
  }
}

八、响应式媒体设计

css 复制代码
video, audio {
  max-width: 100%;
  height: auto;
}

/* 移动设备优化 */
@media (max-width: 768px) {
  .media-container {
    padding: 10px;
  }
  
  video {
    border-radius: 8px;
  }
}

九、性能与体验优化

9.1 预加载策略

  • preload="none":节省流量,适用于首屏非关键视频
  • preload="metadata":只拉取首帧 & 时长
  • poster 使用 WebP/AVIF,减少 30--50 % 体积

9.3 自适应流

HLS(m3u8)与 DASH(mpd)对比:

  • HLS 原生支持 iOS,低延迟扩展 LL-HLS(EXT-X-PREFETCH)
  • DASH 支持 4K、HDR10、Dolby Atmos
  • shaka-player、hls.js、dash.js 三大开源库

9.3 缓冲与码率切换

使用 ABR 算法:下载带宽、缓冲区余量、设备性能三维评分,常用 BOLA、DYNAMIC。

十、安全与隐私

10.1 CORS

html 复制代码
<video crossorigin="anonymous" src="https://cdn.example.net/video.mp4">

10.2 DRM

  • EME 流程:获取许可证服务器 → 生成 MediaKeys → 创建 MediaKeySession → 解密。
  • 常用方案:Widevine、FairPlay、PlayReady。

10.3 防录屏

Chrome 94+ 支持 getDisplayMedia 检测,但无法阻止硬件采集;可通过水印 + 动态模糊 + 实时用户行为分析降低风险。

十一、无障碍

  • aria-label="播放"aria-pressed 状态
  • 键盘快捷键:空格(播放/暂停)、←/→(快退/快进 10 秒)、↑/↓(音量)
  • 音频描述轨道:<track kind="descriptions">

十二、完整示例:自定义视频播放器

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>自定义HTML5视频播放器</title>
    <style>
        .video-container {
            position: relative;
            max-width: 800px;
            margin: 20px auto;
            background: #000;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
        }
        
        video {
            width: 100%;
            display: block;
        }
        
        .custom-controls {
            position: absolute;
            bottom: 0;
            width: 100%;
            background: linear-gradient(transparent, rgba(0,0,0,0.7));
            padding: 15px;
            box-sizing: border-box;
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            transition: opacity 0.3s;
        }
        
        .video-container:hover .custom-controls {
            opacity: 1;
        }
        
        .btn {
            background: none;
            border: none;
            color: white;
            cursor: pointer;
            font-size: 16px;
            margin-right: 10px;
        }
        
        .progress-container {
            flex: 1;
            height: 8px;
            background: rgba(255,255,255,0.2);
            border-radius: 4px;
            margin: 0 10px;
            cursor: pointer;
            position: relative;
        }
        
        .progress-bar {
            height: 100%;
            background: #ff4081;
            border-radius: 4px;
            width: 0%;
        }
        
        .time {
            color: white;
            font-family: Arial, sans-serif;
            font-size: 14px;
            margin: 0 10px;
        }
        
        .volume-container {
            display: flex;
            align-items: center;
            margin-left: 10px;
        }
        
        .volume-slider {
            width: 80px;
            margin-left: 5px;
        }
        
        .fullscreen-btn {
            margin-left: auto;
        }
    </style>
</head>
<body>
    <div class="video-container">
        <video id="mainVideo" poster="https://placehold.co/800x450/000000/FFFFFF/png?text=视频封面">
            <source src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4">
            您的浏览器不支持HTML5视频
        </video>
        
        <div class="custom-controls">
            <button class="btn play-pause">▶</button>
            
            <div class="progress-container">
                <div class="progress-bar"></div>
            </div>
            
            <span class="time">0:00 / 0:00</span>
            
            <div class="volume-container">
                <button class="btn volume-btn">🔊</button>
                <input type="range" class="volume-slider" min="0" max="1" step="0.1" value="1">
            </div>
            
            <button class="btn fullscreen-btn">⛶</button>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const video = document.getElementById('mainVideo');
            const playPauseBtn = document.querySelector('.play-pause');
            const progressContainer = document.querySelector('.progress-container');
            const progressBar = document.querySelector('.progress-bar');
            const timeDisplay = document.querySelector('.time');
            const volumeBtn = document.querySelector('.volume-btn');
            const volumeSlider = document.querySelector('.volume-slider');
            const fullscreenBtn = document.querySelector('.fullscreen-btn');
            
            // 播放/暂停切换
            function togglePlay() {
                if (video.paused) {
                    video.play();
                    playPauseBtn.textContent = '❚❚';
                } else {
                    video.pause();
                    playPauseBtn.textContent = '▶';
                }
            }
            
            // 更新进度条
            function updateProgress() {
                const percent = (video.currentTime / video.duration) * 100;
                progressBar.style.width = `${percent}%`;
                
                // 更新时间显示
                const currentMinutes = Math.floor(video.currentTime / 60);
                const currentSeconds = Math.floor(video.currentTime % 60);
                const durationMinutes = Math.floor(video.duration / 60);
                const durationSeconds = Math.floor(video.duration % 60);
                
                timeDisplay.textContent = 
                    `${currentMinutes}:${currentSeconds < 10 ? '0' : ''}${currentSeconds} / ${durationMinutes}:${durationSeconds < 10 ? '0' : ''}${durationSeconds}`;
            }
            
            // 跳转到点击位置
            function setProgress(e) {
                const width = this.clientWidth;
                const clickX = e.offsetX;
                const duration = video.duration;
                video.currentTime = (clickX / width) * duration;
            }
            
            // 音量控制
            function updateVolume() {
                video.volume = volumeSlider.value;
                volumeBtn.textContent = video.volume === 0 ? '🔇' : '🔊';
            }
            
            // 切换全屏
            function toggleFullscreen() {
                if (!document.fullscreenElement) {
                    video.parentElement.requestFullscreen().catch(err => {
                        console.error(`全屏错误: ${err.message}`);
                    });
                    fullscreenBtn.textContent = '⛶';
                } else {
                    document.exitFullscreen();
                    fullscreenBtn.textContent = '⛶';
                }
            }
            
            // 事件监听
            playPauseBtn.addEventListener('click', togglePlay);
            video.addEventListener('click', togglePlay);
            video.addEventListener('timeupdate', updateProgress);
            progressContainer.addEventListener('click', setProgress);
            volumeSlider.addEventListener('input', updateVolume);
            volumeBtn.addEventListener('click', () => {
                video.volume = video.volume === 0 ? 1 : 0;
                volumeSlider.value = video.volume;
                updateVolume();
            });
            fullscreenBtn.addEventListener('click', toggleFullscreen);
            
            // 初始化
            updateVolume();
        });
    </script>
</body>
</html>

至此,HTML5 音视频的完整技术已一览无余。祝你在下一次项目中,编码无 Bug、播放零卡顿!