MediaMTX 移动端流媒体播放:WebRTC 与 LL-HLS 双路线的选型、配置与排障
【免费下载链接】mediamtxReady-to-use Media-over-QUIC / SRT / WebRTC / RTSP / RTMP / LL-HLS / MPEG-TS / RTP live media server and media proxy that allows to read, publish, proxy, record and playback real-time video and audio streams.项目地址: https://gitcode.com/GitHub_Trending/me/mediamtx
MediaMTX 是一个开源的实时流媒体服务器,内置 WebRTC(WHEP)与 HLS 读取端点,能把同一路流同时分发给 iOS 与 Android 客户端。本文面向在移动应用里接入它的开发者,覆盖移动端流媒体播放的两条路线:WebRTC 低延迟直播与 HLS 大规模分发,按「选型 → 服务端配置 → 客户端代码 → 排障」的顺序给出最小可运行方案。
协议选型:一张表决定走 WebRTC 还是 HLS
先按场景做决定,再写代码。
| 维度 | WebRTC(WHEP) | HLS(mpegts 变体) | LL-HLS |
|---|---|---|---|
| 端到端延迟 | 亚秒级,首帧约 0.5s | 5~10s | 1~3s |
| 并发规模 | 受服务器限制,每个观众一条 peer connection | 可经 CDN 扩展,观众数不设限 | 同 HLS,分片请求频率更高 |
| 安全 | DTLS-SRTP 强制加密,握手可叠加 HTTPS | 明文或 HTTPS 可选 | 建议 HTTPS,Apple 设备要求 HTTPS |
| 设备要求 | 需引入 WebRTC SDK,且媒体端口对客户端可达 | 系统播放器直接支持,无需 SDK | 系统播放器可播,低版本系统按普通 HLS 的延迟表现 |
选择标准:一对一查看、实时互动、延迟敏感的场景走 WebRTC;观众量大、需要 CDN 或只需分钟级时效的场景走 HLS,其中追求秒级延迟时选 LL-HLS。
服务端准备:MediaMTX 最小可用配置
先让路径里有流:用任意客户端(如 FFmpeg 推 RTSP 到rtsp://服务器:8554/mystream)发布到某路径,移动端之后读取同一路径即可。
开启 WebRTC(WHEP)读取
在mediamtx.yml中:
# mediamtx.yml webrtc: true # 开启 WebRTC 服务,含 WHEP 读取端点 webrtcAddress: :8889 # WHEP 握手与网页播放的 HTTP 端口 webrtcEncryption: false # 握手层是否走 HTTPS;媒体流本身始终有 DTLS 加密 webrtcLocalUDPAddress: :8189 # ICE 媒体通道 UDP 端口,客户端必须能直连 webrtcAdditionalHosts: [192.168.1.100] # 下发给客户端的服务器可达地址,跨 NAT/容器场景必填 webrtcHandshakeTimeout: 10s # 握手等待上限,超时即断开WHEP 端点固定为http://服务器:8889/路径/whep,客户端向它 POST 一个 SDP offer,服务器返回 answer。连接通不通,八成取决于webrtcAdditionalHosts和 UDP 端口放行,细节见 WebRTC 连接问题文档。
开启 HLS 读取(含 LL-HLS)
# mediamtx.yml hls: true # 开启 HLS 服务 hlsAddress: :8888 # HTTP 端口,播放入口为 http://服务器:8888/路径/index.m3u8 hlsEncryption: true # 启用 HTTPS;Apple 设备上 LL-HLS 要求 HTTPS hlsServerKey: server.key # 证书私钥路径 hlsServerCert: server.crt # 证书文件路径 hlsVariant: lowLatency # 三选一:mpegts / fmp4 / lowLatency hlsPartDuration: 200ms # LL-HLS 分片时长,直接决定延迟下限 hlsSegmentDuration: 1s # 完整分段时长,决定播放器缓冲粒度 hlsSegmentCount: 7 # 保留分段数,只影响可回看长度,不影响延迟完整参数列表见 配置文件参考。
实时场景落地:iOS/Android 端 WHEP 最小接入代码
两端的流程一致:创建 PeerConnection → 生成 offer → POST 到 WHEP 端点 → 用响应里的 answer 设置远端描述 → 在 track 到达回调中渲染。
iOS 端 WHEP 最小接入代码
依赖 GoogleWebRTC(CocoaPods 引入GoogleWebRTC),核心如下:
import WebRTC final class WHEPPlayer: NSObject { private let pc = RTCPeerConnectionFactory() .peerConnection(with: RTCConfiguration(), constraints: nil, delegate: nil) func connect(_ whepUrl: String) { pc.offer(for: RTCMediaConstraints()) { [weak self] offer, _ in guard let offer else { return } self?.pc.setLocalDescription(offer) { _ in self?.post(offer.sdp, to: whepUrl) // 关键一步:POST offer } } } private func post(_ sdp: String, to url: String) { var req = URLRequest(url: URL(string: url)!) req.httpMethod = "POST" req.setValue("application/sdp", forHTTPHeaderField: "Content-Type") req.httpBody = sdp.data(using: .utf8) URLSession.shared.dataTask(with: req) { data, _, _ in guard let sdp = data.flatMap({ String(data: $0, encoding: .utf8) }) else { return } // 响应 body 即 answer,设置后媒体流开始到达 self.pc.setRemoteDescription(RTCSessionDescription(type: .answer, sdp: sdp)) { _ in } }.resume() } }出画面:在RTCPeerConnectionDelegate的 track 到达回调中,把videoTrack加到RTCMTLVideoView上即可,其余样板代码从略。
Android 端 WHEP 最小接入代码
依赖org.webrtc:google-webrtc,核心如下:
class WHEPPlayer(private val surface: SurfaceViewRenderer) { private val pc = PeerConnectionFactory().createPeerConnection(RTCConfiguration(), observer) // observer 的 onAddTrack 回调里:track?.addSink(surface) fun connect(whepUrl: String) { val c = MediaConstraints().apply { mandatory["OfferToReceiveVideo"] = "true" mandatory["OfferToReceiveAudio"] = "true" } pc.createOffer(object : SdpObserver { override fun onCreateSuccess(sdp: SessionDescription) { pc.setLocalDescription(localCb, sdp) postOffer(sdp.description, whepUrl) // 成功后再执行 post } override fun onCreateFailure(e: String) {} override fun onSetSuccess() {} override fun onSetFailure(e: String) {} }, c) } // postOffer:POST application/sdp 到 whepUrl,响应 body 为 answer, // pc.setRemoteDescription(answer) 后媒体流触发 onAddTrack }验证连接成功
- 用 curl 向
/whep发 POST,返回201 Created,且响应头Location给出会话地址,说明握手成功。 - 客户端 PeerConnection 状态变为
connected,track 回调开始触发并渲染画面。 - 服务器日志出现该路径的 WebRTC 读取事件,说明服务器侧已建链。
- 快速自检:浏览器直接打开
http://服务器:8889/路径能播放,说明端点本身可用,问题可聚焦在客户端网络。
大规模分发落地:LL-HLS 低延迟模式怎么配
播放入口是http://服务器:8888/路径/index.m3u8。低延迟模式的取舍很直接:hlsPartDuration越小延迟越低,但分片请求频率越高,对服务器与 CDN 压力越大;mpegts变体兼容最旧,但延迟停留在 5 秒以上。弱网或需要兼容大量旧设备时退回mpegts,其余情况用lowLatency。要横向扩展观众数,把分片目录交给 CDN,并设置hlsCDNSecret识别 CDN 回源请求。
iOS 端 HLS 播放
系统 AVPlayer 即可,无需额外依赖:
let url = URL(string: "https://192.168.1.100:8888/mystream/index.m3u8")! let item = AVPlayerItem(url: url) item.preferredForwardBufferDuration = 2 // 压缩前向缓冲,让播放位置贴近直播边缘 let player = AVPlayer(playerItem: item) player.play()Android 端 LL-HLS 低延迟播放
ExoPlayer 开启 chunkless preparation,playlist 就绪即可播放,不必等完整分段下载:
val dataSource = DefaultHttpDataSource.Factory() .setUserAgent("MTX-Player") .createDataSource() val source = HlsMediaSource.Factory(dataSource) .setAllowChunklessPreparation(true) // LL-HLS 关键开关:不等整段,分段/分片即播 .createMediaSource(MediaItem.fromUri("https://192.168.1.100:8888/mystream/index.m3u8")) exoPlayer.setMediaSource(source) exoPlayer.playWhenReady = true exoPlayer.prepare()验证方式:curl 该.m3u8能返回含分段列表的 playlist(LL-HLS 下还会带#EXT-X-PART行),服务器日志出现 HLS 读取事件,即链路可用。
问题定位:连接失败、延迟高、播放卡顿排查表
| 现象 | 可能原因 | 处理动作 |
|---|---|---|
POST/whep返回 404 | 该路径没有正在发布的流 | 先向同路径发布流,或核对路径名与查询参数 |
POST/whep返回 403 | 无 read 权限 | 检查authInternalUsers中对应路径的read权限 |
| WHEP 返回 201 但不出画面 | ICE 不通:UDP 8189 未放行,或webrtcAdditionalHosts缺少客户端可达地址 | 填对webrtcAdditionalHosts、放行 8189/udp;仍不通则启用webrtcLocalTCPAddress,或配置 STUN/TURN |
| LL-HLS 延迟高于预期 | hlsPartDuration/hlsSegmentDuration过大,或播放器缓冲过大 | 将 part 降到 200ms 左右;调小播放器缓冲(iOS 用preferredForwardBufferDuration) |
| iOS 上 LL-HLS 没有低延迟效果 | 未启用 HTTPS | 将hlsEncryption设为 true,LL-HLS 在 Apple 设备上要求 HTTPS |
| 播放间歇性卡顿 | UDP 丢包,或服务器出口带宽不足 | 调大udpReadBufferSize;或改走 HLS 并由 CDN 分发(配合hlsCDNSecret) |
| 首帧慢 | 关键帧间隔过大 | 在编码端缩短 GOP(如-g 30降到-g 15),每段需含 IDR 帧 |
WebRTC 连通性的更多排查顺序(UDP → TCP → STUN → TURN)见 WebRTC 特定特性文档。
两种场景的推荐组合
实时查看、互动类场景:WebRTC/WHEP,hlsVariant保持默认即可,把精力放在 UDP 端口与webrtcAdditionalHosts上。面向大量观众或需要 CDN 的场景:LL-HLS(hlsVariant: lowLatency,hlsPartDuration: 200ms),需要兼容旧设备时退回mpegts。协议与端点的完整说明见 读取流文档、WebRTC 客户端文档 与 HLS 文档。
【免费下载链接】mediamtxReady-to-use Media-over-QUIC / SRT / WebRTC / RTSP / RTMP / LL-HLS / MPEG-TS / RTP live media server and media proxy that allows to read, publish, proxy, record and playback real-time video and audio streams.项目地址: https://gitcode.com/GitHub_Trending/me/mediamtx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考