本文主要是探讨下b站的音视频流分离dash格式实时转为hls流媒体,以方便在旧的播放器上播放。
什么是DASH
dash是基于HTTP的动态自适应流(英語:Dynamic Adaptive Streaming over HTTP,也称MPEG-DASH)的简称。现在b站最新的视频都是返回这种格式,并且是音视频分离的。b站dash实践可以参考这篇ppt: MPEG-DASH在bilibili的应用与实践
转换HLS的实现方式
b站dash音视频分别为单独的两个链接,不同的码率和编码对应不同的链接,下面只是使用其中一个音视频链接来举例。实现方式如下:
- 提供生成m3u8文件的http服务,m3u8视频块根据时长(duration)切分为5秒为一片,m3u8格式如下:
拿不到总的时长的话,可以通过#EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:5 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF: 5.000000, http://xxx/api/hls/segments/1 #EXTINF: 5.000000, http://xxx/api/hls/segments/2 ....
ffprobe
命令获取:http服务的响应头ffprobe -v quiet -print_format json -show_format "http://xxx/video.m4s"
content-type
需设为application/vnd.apple.mpegurl
- 提供切片下载的http服务,如上面m3u8文件中的
http://xxx/api/hls/segments/1
,切片可以通过ffmpeg进行音视合并和切分,命令如下:本命令是把重新编码后的视频数据输出到控制台,需要http服务把输出数据实时写入到响应中,这样播放器就能实时下载播放了# 切分从视频第(-ss)5.00秒开始,时长(-t)5.00秒 # -ss和-initial_offset数值需要根据当前切片递增 ffmpeg -timelimit 45 -ss 5.00 -i http://xxx/video.m4s -ss 5.00 -i http://xxx/audio.m4s -t 5.00 -c:v libx264 -preset veryfast -c:a copy -force_key_frames "expr:gte(t,n_forced*5.000)" -f ssegment -segment_time 5.00 -initial_offset 5.00 pipe:out%03d.ts # 同时合并字幕xxx.srt ffmpeg -timelimit 45 -ss 5.00 -copyts -i http://xxx/video.m4s -ss 5.00 -i http://xxx/audio.m4s -t 5.00 -vf subtitles=xxx.srt -c:v libx264 -preset veryfast -c:a copy -force_key_frames "expr:gte(t,n_forced*5.000)" -f ssegment -segment_time 5.00 -initial_offset 5.00 -ss 5.00 pipe:out%03d.ts
会遇到的问题
ffmpeg切片时音频使用了copy
命令,但视频却使用libx264
重新编码,导致转码很慢,而且cpu占用居高不下,为什么视频不也用copy
模式,避免重新转码?
这是因为ffmpeg视频copy模式切片时只能根据关键帧切分,这样会导致每个切片可能会多几帧,播放时出现画面重复播放和声画不同步,具体问题可参考:https://www.jianshu.com/p/f7d83fdec111
切片重新编码是避免不了了,如何可以提高转码速度,降低cpu占用?
——视频编码硬件加速!
ffmpeg硬件加速
视频编码硬件加速主要有如下几种:
- VAAPI:intel集显支持,是openwrt中最常见和使用的
- QSV:intel集显支持,可在intel产品规格页搜索自己的cpu,看下有没支持
Quick Sync Video
- NVENC:nvidia显卡支持,openwrt中一般没独立显卡,很少使用到
这里只介绍最常用的vaapi方式,ffmpeg编码时使用vaapi硬件加速切片命令如下:
# 开启vaapi硬件加速编码
ffmpeg -vaapi_device /dev/dri/renderD128 -timelimit 45 -ss 5.00 -i http://xxx/video.m4s -ss 5.00 -i http://xxx/audio.m4s -t 5.00 -vf 'format=nv12,hwupload' -c:v h264_vaapi -c:a copy -force_key_frames "expr:gte(t,n_forced*5.000)" -f ssegment -segment_time 5.00 -initial_offset 5.00 pipe:out%03d.ts
# 同时合并字幕xxx.srt
ffmpeg -vaapi_device /dev/dri/renderD128 -timelimit 45 -ss 5.00 -copyts -i http://xxx/video.m4s -ss 5.00 -i http://xxx/audio.m4s -t 5.00 -vf 'subtitles=xxx.srt,format=nv12,hwupload' -c:v h264_vaapi -c:a copy -force_key_frames "expr:gte(t,n_forced*5.000)" -f ssegment -segment_time 5.00 -initial_offset 5.00 -ss 5.00 pipe:out%03d.ts
-vaapi_device /dev/dri/renderD128
,-vf 'format=nv12,hwupload'
和-c:v h264_vaapi
这三个参数是必须的
如何在docker中使用硬件加速
假如hls流服务是部署在docker中使用的,打开硬件加速可以使用已经安装了vaapi支持的镜像。jrottenberg/ffmpeg有生成好的支持vaapi的docker镜像,镜像名称带有vaapi
标签,可自行搜索需要的ffmpeg版本使用
docker启动时,需要使用--device
参数绑定显卡设备:
docker run --device /dev/dri:/dev/dri jrottenberg/ffmpeg:vaapi
在openwrt中使用的话中,可在docker配置页device
栏绑定显卡设备