分享一个Edge浏览器播放H265 RTSP流的问题,涉及到ZLMediaKit
问题背景
公司测试环境摄像头有五个,四个大华的,一个海康的,那一个海康的在edge浏览器上播放不了,在chrome浏览器上可以播放,
业务代码逻辑是这样的,这些摄像头都连接了一个硬盘录像机,然后一个后端服务会记录下有哪些流,然后前端可以调用接口查询,查询到在通过ZLMediaKit过滤拉取过来。
核心问题:
- RTSP摄像头流是 H265 (HEVC) 编码
- Chrome浏览器 原生支持H265
- Edge浏览器 不支持H265
- Edge播放时报错:
Assertion failed: (have_active_media, 必须确保最少有一个活跃的track)
错误原因: Edge发送的WebRTC Offer不包含H265编解码器,ZLMediaKit无法找到匹配的编解码器进行协商。
解决方案架构
方案:智能检测 + 自动转码 + 优雅降级
┌─────────────────────────────────────────────────────────────┐
│ 1. 浏览器H265支持检测 │
│ ├─ 支持H265 (Chrome) → 直接拉流播放 │
│ └─ 不支持H265 (Edge) → 尝试FFmpeg转码 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 2. FFmpeg自动转码 (H265 → H264) │
│ ├─ 成功 → 播放转码后的H264流 │
│ └─ 失败 → 回退到直接拉流 (会报错但有明确提示) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 3. WebRTC播放 │
│ ├─ 流状态校验 (FFmpeg转码需要更长等待时间) │
│ ├─ WebRTC信令交换 │
│ └─ 视频播放 │
└─────────────────────────────────────────────────────────────┘
技术实现细节
1. 提前检测浏览器H265支持
javascript
// 创建临时RTCPeerConnection检测编解码器支持
const tempPc = new RTCPeerConnection();
const tempOffer = await tempPc.createOffer({
offerToReceiveVideo: true,
offerToReceiveAudio: false
});
const supportsH265 = tempOffer.sdp.includes('H265') || tempOffer.sdp.includes('H.265');
tempPc.close();
优势:
- 在拉流之前就知道是否需要转码
- 避免无效的API调用和资源浪费
2. FFmpeg自动转码配置
关键点:使用RTMP协议作为FFmpeg输出
javascript
// 错误:FFmpeg不支持输出到RTSP
dst_url: `rtsp://192.168.3.174:554/live/stream_0_xxx`
// 正确:使用RTMP协议
dst_url: `rtmp://192.168.3.174:1935/live/stream_0_xxx`
API调用:
javascript
await axios.post('/index/api/addFFmpegSource', {
src_url: rtspUrl, // 原始H265流
dst_url: `rtmp://${ip}:1935/${app}/${streamName}`, // 转码后的H264流
timeout_ms: 30000,
enable_hls: 1,
enable_mp4: 0,
secret: secret
});
3. ZLMediaKit配置 (config.ini)
ini
[ffmpeg]
# FFmpeg可执行程序路径
bin=F:\project\ffmpeg-7.0.2-full_build\ffmpeg-7.0.2-full_build\bin\ffmpeg.exe
# 转码命令(H265 → H264优化)
cmd=%s -rtsp_transport tcp -i %s -vcodec libx264 -preset ultrafast -tune zerolatency -profile:v baseline -level 3.1 -s 1280x720 -r 15 -b:v 1500k -acodec aac -ar 44100 -ab 48k -f flv %s
# FFmpeg日志路径
log=./ffmpeg/ffmpeg.log
# 截图命令
snap=%s -i %s -y -f mjpeg -frames:v 1 -an %s
# 自动重启时间(秒),0表示不自动重启
restart_sec=0
参数说明:
-rtsp_transport tcp:使用TCP传输,减少丢包-preset ultrafast:最快编码速度(实时转码必需)-tune zerolatency:最小延迟-profile:v baseline:最大兼容性-s 1280x720:降低分辨率(从2560x1440降到720p)-r 15:降低帧率(从25fps降到15fps)-b:v 1500k:合理的720p码率-f flv:输出FLV格式(配合RTMP协议)
4. 流状态校验优化
javascript
// FFmpeg转码需要更长的初始化时间
const maxRetries = usedFFmpeg ? 20 : 5; // 40秒 vs 5秒
const retryDelay = usedFFmpeg ? 2000 : 1000;
const isOnline = await this.checkStreamOnline(streamName, maxRetries, retryDelay);
原因:
- 直接拉流:几乎立即可用
- FFmpeg转码:需要时间初始化、解码、编码、推流
工作流程对比
Chrome浏览器(支持H265)
1. 检测到支持H265
2. 直接拉流 (addStreamProxy)
3. 流状态校验 (5秒内)
4. WebRTC播放 H265流
5. 播放成功
Edge浏览器(不支持H265)
1. 检测到不支持H265
2. 尝试FFmpeg转码 (addFFmpegSource)
├─ 成功 → 推流到 rtmp://ip:1935/live/stream_xxx
└─ 失败 → 回退到直接拉流
3. 流状态校验 (40秒内,等待转码完成)
4. WebRTC播放 H264流
5. 播放成功
还有一个方式好像是因为海康的流使用的是主码流