web 端 H265 软解码实现原理与使用说明

H265 软解码实现原理与使用说明

网站信息

为什么需要软解码?

  1. 浏览器原生支持有限: Chrome 等主流浏览器对 H.265 的原生支持非常有限
  2. 硬件解码依赖: 即使浏览器支持,通常也需要设备硬件支持,兼容性差
  3. 跨平台需求: 软解码可以在任何支持 WebAssembly 的现代浏览器中运行,无需硬件支持

核心技术栈

1. FFmpeg WebAssembly (Emscripten)

  • 技术: FFmpeg 通过 Emscripten 编译为 WebAssembly
  • 运行环境 : 在 Web Worker (ffmpeg-worker-mp4.js) 中运行,避免阻塞主线程
  • 关键配置 :
    • 启用了 HEVC 解码器: --enable-decoder=hevc
    • 支持多种视频格式解码: vp8, vp9, h264, hevc 等
    • 支持音频解码: mp3, aac, opus, vorbis 等
    • 使用 hevc_mp4toannexb bitstream filter 进行格式转换

2. libde265.js

  • 功能: 纯 JavaScript 实现的 H.265 解码库
  • 作用: 在浏览器中进行 H.265 视频的软解码
  • 输出: 将解码后的 YUV 帧渲染到 Canvas 元素

3. Canvas 渲染

  • 元素: HTML5 Canvas 用于视频帧渲染
  • 方式: 通过 Canvas 2D API 逐帧绘制解码后的视频画面

4. Web Audio API

  • 元素: HTML5 Audio 元素
  • 功能: 播放分离出的音频流

工作流程详解

第一步:视频分离(点击"获取hevc"按钮)

javascript 复制代码
function getHEVC() {
  runCommand('-i input.mp4 -c:v copy -bsf hevc_mp4toannexb -f rawvideo video.hevc')
}

处理过程:

  1. 使用 FFmpeg 从 MP4 容器中提取 H.265 视频流
  2. -c:v copy: 视频流直接复制,不重新编码
  3. -bsf hevc_mp4toannexb: 将 MP4 格式的 HEVC 转换为 Annex-B 格式(NALU 格式)
    • MP4 中的 HEVC 使用 length-prefixed NAL units
    • Annex-B 格式使用 start code (0x00000001) 分隔 NAL units
    • libde265 需要 Annex-B 格式才能正确解码
  4. -f rawvideo: 输出原始视频流格式
  5. 生成 video.hevc 文件(Blob URL)

输出信息示例:

复制代码
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input.mp4':
  Stream #0:0(und): Video: hevc (Main) (hev1 / 0x31766568), 
    yuv420p(tv), 1270x720 [SAR 128:127 DAR 16:9], 
    666 kb/s, 25 fps, 25 tbr, 12800 tbn, 25 tbc

第二步:音频分离(点击"获取MP3"按钮)

javascript 复制代码
function getMP3() {
  runCommand('-i input.mp4 -b:a 192K -vn audio.mp3')
}

处理过程:

  1. -i input.mp4: 输入文件
  2. -b:a 192K: 音频比特率设置为 192Kbps
  3. -vn: 禁用视频流,只处理音频
  4. 生成 audio.mp3 文件(Blob URL)

第三步:视频解码与播放(点击"播放"按钮)

javascript 复制代码
player = new libde265.RawPlayer(video);  // video 是 canvas 元素
player.set_status_callback(function(msg, fps) {
  // 状态回调:loading, initializing, playing, fps 等
});
player.playback(hevcUrl);  // 播放 H.265 视频流
document.getElementById('audio').play();  // 同步播放音频

播放过程:

  1. 初始化: 创建 libde265 播放器实例,绑定到 Canvas 元素
  2. 加载: 从 Blob URL 加载 H.265 视频流
  3. 解码: libde265 逐帧解码 H.265 数据
  4. 渲染: 将解码后的 YUV 帧转换为 RGB 并绘制到 Canvas
  5. 同步: 同时播放音频,实现音视频同步
  6. 状态反馈: 实时显示播放状态和 FPS

状态回调:

  • loading: 正在加载视频
  • initializing: 正在初始化解码器
  • playing: 正在播放
  • fps: 显示当前帧率(如 "48.22 fps")
  • stopped: 播放停止

关键技术细节

1. FFmpeg Web Worker 通信

javascript 复制代码
worker = new Worker("./ffmpeg-worker-mp4.js");
worker.onmessage = function(e) {
  var msg = e.data;
  switch (msg.type) {
    case "ready":      // Worker 准备就绪
    case "stdout":     // 标准输出
    case "stderr":     // 错误输出
    case "done":       // 处理完成,返回文件数据
    case "exit":       // 进程退出
  }
};

// 发送命令
worker.postMessage({
  type: "run",
  arguments: ["-i", "input.mp4", ...],
  MEMFS: [{name: "input.mp4", data: videoData}]
});

2. 文件系统模拟 (MEMFS)

FFmpeg 在浏览器中运行时,使用 Emscripten 的 MEMFS(内存文件系统):

  • 输入文件通过 MEMFS 参数传递给 Worker
  • 输出文件通过 done 消息返回
  • 使用 Blob URL 创建下载链接

3. libde265 解码流程

  1. 加载视频流: 从 Blob URL 读取 H.265 数据
  2. 解析 NAL units: 识别 Annex-B 格式的 NAL units
  3. 解码帧: 使用 libde265 解码器解码每一帧
  4. YUV 转 RGB: 将 YUV420p 格式转换为 RGB
  5. Canvas 绘制 : 使用 putImageData 或类似方法绘制到 Canvas

4. 音视频同步机制

  • 视频通过 Canvas 逐帧渲染,帧率由解码速度决定
  • 音频通过 HTML5 Audio 元素播放,有独立的播放时间线
  • 通过状态回调监控播放进度,手动控制音频播放时机

使用说明

基本操作流程

  1. 打开网页: 访问 https://sparkmorry.github.io/mse-learning/h265/
  2. 等待加载 : 页面会显示 "Loading JavaScript files (it may take a minute)"
    • 正在加载 FFmpeg WebAssembly 模块
    • 正在加载 libde265.js 库
  3. 点击"获取hevc" :
    • 从 MP4 文件中提取 H.265 视频流
    • 处理完成后会显示下载链接
  4. 点击"获取MP3" :
    • 从 MP4 文件中提取音频
    • 处理完成后会显示下载链接,并自动设置到 audio 元素
  5. 点击"播放" :
    • 开始解码并播放 H.265 视频
    • 视频显示在 Canvas 上
    • 音频同步播放
    • 状态栏显示当前 FPS

高级功能

FFmpeg 命令行工具:

  • 页面底部提供了 FFmpeg 命令行输入框
  • 可以手动输入 FFmpeg 命令进行各种视频处理
  • 默认命令: -help 显示帮助信息

示例命令:

bash 复制代码
# 提取视频流
-i input.mp4 -c:v copy -bsf hevc_mp4toannexb -f rawvideo video.hevc

# 提取音频
-i input.mp4 -b:a 192K -vn audio.mp3

# 查看视频信息
-i input.mp4

技术限制与注意事项

性能限制

  1. CPU 占用高: 软解码完全依赖 CPU,高分辨率视频可能导致卡顿
  2. 内存消耗: WebAssembly 模块和视频数据会占用较多内存
  3. 解码速度: 软解码速度受 CPU 性能限制,可能无法达到实时播放

兼容性

  1. 浏览器要求:

    • 需要支持 WebAssembly 的现代浏览器
    • 需要支持 Web Worker
    • 需要支持 Canvas API
  2. 视频格式要求:

    • 输入视频必须是包含 H.265 编码的 MP4 文件
    • 视频编码格式: HEVC (H.265)

使用场景

适用场景:

  • 演示和教学 H.265 解码原理
  • 在不支持硬件解码的设备上播放 H.265 视频
  • 需要自定义视频处理流程的场景

不适用场景:

  • 生产环境的高性能视频播放(应使用硬件解码)
  • 移动设备上的长时间播放(耗电量大)
  • 需要低延迟的实时视频流

技术架构图

复制代码
┌─────────────────────────────────────────────────┐
│                 浏览器页面                        │
├─────────────────────────────────────────────────┤
│                                                 │
│  ┌──────────────┐    ┌──────────────┐          │
│  │  FFmpeg      │    │  libde265.js │          │
│  │  WebWorker   │    │  (解码器)    │          │
│  └──────┬───────┘    └──────┬───────┘          │
│         │                   │                  │
│         │ 提取视频流         │ 解码视频帧        │
│         │                   │                  │
│         ▼                   ▼                  │
│  ┌──────────────┐    ┌──────────────┐          │
│  │  video.hevc  │    │   Canvas     │          │
│  │  (Blob URL)  │    │   (渲染)     │          │
│  └──────────────┘    └──────────────┘          │
│                                                 │
│  ┌──────────────┐                              │
│  │  audio.mp3   │                              │
│  │  (Audio元素) │                              │
│  └──────────────┘                              │
│                                                 │
└─────────────────────────────────────────────────┘

相关资源

总结

该网站通过以下技术实现了浏览器中的 H.265 软解码播放:

  1. FFmpeg WebAssembly: 用于视频流分离和格式转换
  2. libde265.js: 用于 H.265 视频解码
  3. Canvas API: 用于视频帧渲染
  4. Web Worker: 避免阻塞主线程
  5. Blob URL: 处理内存中的文件数据

这种方案虽然性能不如硬件解码,但提供了跨平台的 H.265 播放能力,是学习和演示 H.265 解码原理的优秀示例。

相关推荐
倚栏听风雨1 天前
深度拆解:从 npm install 到手写一个全局 CLI 工具
前端
攀登的牵牛花1 天前
前端向架构突围系列 - 架构方法(二):UML前端建模的一般方法和工具
前端·前端框架·uml
代码猎人1 天前
Object.assign和扩展运算符是深拷贝还是浅拷贝,两者有什么区别
前端
秋天的一阵风1 天前
🎥解决前端 “复现难”:rrweb 录制回放从入门到精通
前端·javascript·开源
意法半导体STM321 天前
【文末送NUCLEO-G431RB】一文说明白STM32G4双Bank启动与升级 LAT1596
前端·数据库·stm32·单片机·嵌入式硬件·mcu·stm32开发
前端一课1 天前
分享:基于Next.js的企业级提示词AI平台
前端
小高0071 天前
🔥「从零到一」我用 Node BFF 手撸一个 Vue3 SSR 项目(附源码)
前端·javascript·vue.js
SailingCoder1 天前
AI 流式对话该怎么做?SSE、fetch、axios 一次讲清楚
前端·javascript·人工智能·ai·node.js
hxjhnct1 天前
Vue 实现多行文本“展开收起”
前端·javascript·vue.js