如何在Ubuntu上搭建基于Dash.js的视频服务器
DASH,又叫MPEG DASH,DASH:Dynamic Adaptive Streaming over HTTP ,是一种在互联网上传送动态码率的Video Streaming技术,类似于苹果的HLS,DASH会通过media presentation description (MPD)将视频内容切片成一个很短的文件片段,每个切片都有多个不同的码率,DASH Client可以根据网络的情况选择一个码率进行播放,支持在不同码率之间无缝切换。
搭建一个Video服务器主要工具和步骤分为:
- youtube-dl: 获取视频(可选,如果有现成视频,这步不需要)
- ffmpeg: 将视频转码成不同分辨率的视频
- bento4: 将不同分辨率的视频和音频切割成多个小段的视频小段
- nginx: 搭建web服务器(示例用nginx)
- html+ dash.js: 需要一个简单的Dash client的html页面来访问Dash video
youtube下载测试视频
从youtube下载视频这里用的是python工具youtube-dl
$ sudo curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl
$ sudo chmod a+rx /usr/local/bin/youtube-dl
#Ubuntu也可以用apt安装
$ apt install youtube-dl
#确保youtube-dl的版本是最新的,否则会有error "YouTube said: Unable to extract video data"
$ youtube-dl --version
#查看不同格式和不同码率的视频
$ youtube-dl -F https://www.youtube.com/watch?v=sb7DHMFdNKE&t=39s
[info] Available formats for sb7DHMFdNKE:
format code extension resolution note
249 webm audio only tiny 48k , webm_dash container, opus @ 48k (48000Hz), 2.39MiB
250 webm audio only tiny 64k , webm_dash container, opus @ 64k (48000Hz), 3.16MiB
251 webm audio only tiny 128k , webm_dash container, opus @128k (48000Hz), 6.32MiB
140 m4a audio only tiny 129k , m4a_dash container, mp4a.40.2@129k (44100Hz), 6.36MiB
278 webm 256x144 144p 96k , webm_dash container, vp9@ 96k, 25fps, video only, 4.74MiB
160 mp4 256x144 144p 112k , mp4_dash container, avc1.4d400c@ 112k, 25fps, video only, 5.53MiB
242 webm 426x240 240p 205k , webm_dash container, vp9@ 205k, 25fps, video only, 10.08MiB
133 mp4 426x240 240p 248k , mp4_dash container, avc1.4d4015@ 248k, 25fps, video only, 12.19MiB
243 webm 640x360 360p 349k , webm_dash container, vp9@ 349k, 25fps, video only, 17.20MiB
134 mp4 640x360 360p 531k , mp4_dash container, avc1.4d401e@ 531k, 25fps, video only, 26.12MiB
244 webm 854x480 480p 607k , webm_dash container, vp9@ 607k, 25fps, video only, 29.83MiB
135 mp4 854x480 480p 1067k , mp4_dash container, avc1.4d401e@1067k, 25fps, video only, 52.45MiB
247 webm 1280x720 720p 1094k , webm_dash container, vp9@1094k, 25fps, video only, 53.78MiB
136 mp4 1280x720 720p 1986k , mp4_dash container, avc1.64001f@1986k, 25fps, video only, 97.62MiB
248 webm 1920x1080 1080p 1777k , webm_dash container, vp9@1777k, 25fps, video only, 87.36MiB
137 mp4 1920x1080 1080p 4205k , mp4_dash container, avc1.640028@4205k, 25fps, video only, 206.64MiB
18 mp4 640x360 360p 705k , avc1.42001E, 25fps, mp4a.40.2 (44100Hz), 34.66MiB
22 mp4 1280x720 720p 2115k , avc1.64001F, 25fps, mp4a.40.2 (44100Hz) (best)
#下载分辨率为1280x720的MP4
$ youtube-dl -f 22 https://www.youtube.com/watch?v=sb7DHMFdNKE&t=39s
安装ffmpeg和转码视频
下载链接:https://www.ffmpeg.org/download.html
#Ubuntu
sudo apt install ffmpeg
#将测试视频转码成不同分辨率
ffmpeg -i ball.mp4 -s 160x90 -c:v libx264 -b:v 250k -g 90 -an ball_video_160x90_250k.mp4
ffmpeg -i ball.mp4 -s 320x180 -c:v libx264 -b:v 500k -g 90 -an ball_video_320x180_500k.mp4
ffmpeg -i ball.mp4 -s 640x360 -c:v libx264 -b:v 750k -g 90 -an ball_video_640x360_750k.mp4
ffmpeg -i ball.mp4 -s 640x360 -c:v libx264 -b:v 1000k -g 90 -an ball_video_640x360_1000k.mp4
ffmpeg -i ball.mp4 -s 1280x720 -c:v libx264 -b:v 1500k -g 90 -an ball_video_1280x720_1500k.mp4
ffmpeg -i ball.mp4 -c:a aac -b:a 128k -vn ball_audio_128k.mp4
ffmpeg 各选项含义请查看文档 https://www.ffmpeg.org/ffmpeg.html
安装bento4和分割视频
- 下载linux zip安装包
#下载linux zip安装包
https://www.bento4.com/downloads/
#解压下载包
unzip Bento4-SDK-1-5-1-629.x86_64-unknown-linux.zip
- 添加环境变量
/etc/profile
BENTO4_PATH=/home/xxx/Bento4-SDK-1-5-1-629.x86_64-unknown-linux/bin
export PATH=$PATH:$BENTO4_PATH
- 环境变量生效
source /etc/profile
下面是将不同分辨率 的视频分割成dash格式的小视频的步骤:
$ mp4info ball.mp4
File:
major brand: mp42
minor version: 0
compatible brand: isom
compatible brand: mp42
fast start: yes
Movie:
duration: 412201 ms
time scale: 12800
fragments: no
Found 2 Tracks
Track 1:
flags: 3 ENABLED IN-MOVIE
id: 1
type: Video
duration: 412200 ms
language: und
media:
sample count: 10305
timescale: 12800
duration: 5276160 (media timescale units)
duration: 412200 (ms)
bitrate (computed): 1984.150 Kbps
display width: 1280.000000
display height: 720.000000
frame rate (computed): 25.000
Sample Description 0
Coding: avc1 (H.264)
Width: 1280
Height: 720
Depth: 24
AVC Profile: 100 (High)
AVC Profile Compat: 0
AVC Level: 31
AVC NALU Length Size: 4
AVC SPS: [6764001facd1005005bb016a020202800000030080000019078c1889]
AVC PPS: [68eb8f2c]
Codecs String: avc1.64001F
Track 2:
flags: 3 ENABLED IN-MOVIE
id: 2
type: Audio
duration: 412200 ms
language: und
media:
sample count: 17752
timescale: 44100
duration: 18178048 (media timescale units)
duration: 412201 (ms)
bitrate (computed): 128.006 Kbps
Sample Description 0
Coding: mp4a (MPEG-4 Audio)
Stream Type: Audio
Object Type: MPEG-4 Audio
Max Bitrate: 0
Avg Bitrate: 0
Buffer Size: 0
Codecs String: mp4a.40.2
MPEG-4 Audio Object Type: 2 (AAC Low Complexity)
MPEG-4 Audio Decoder Config:
Sampling Frequency: 44100
Channels: 2
Sample Rate: 44100
Sample Size: 16
Channels: 2
从上面可以看到该mp4的信息:
a. fragments: no 则必须先要做mp4fragment后才能转成dash小视频
b. 有两个Track,Track1为video(视频),Track2为Audio(音频)
- mp4fragments
mp4fragment ball.mp4 ball_fra.mp4
- mp4dash
$ mp4dash --mpd-name manifest.mpd ball_fra.mp4
Parsing media file 1: ball_fra.mp4
Splitting media file (audio) ball_fra.mp4
Splitting media file (video) ball_fra.mp4
默认会在当前目录下生成一个output文件,里面包括mpd文件,视频和音频文件夹,可以看到mp4的音频和视频文件都被分割成多个小文件。
ehaiyag@CN-00125496:/mnt/c/Users/ehaiyag/Desktop/dash/football$ tree output/
output/
├── audio
│ └── und
│ └── mp4a
│ ├── init.mp4
│ ├── seg-10.m4s
│ ├── seg-11.m4s
| ├── ...........
├── manifest.mpd
└── video
└── avc1
├── init.mp4
├── seg-10.m4s
├── seg-11.m4s
...................
$ cat manifest.mpd
<?xml version="1.0" ?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT5.12S" mediaPresentationDuration="PT6M52.200S" type="static">
<!-- Created with Bento4 mp4-dash.py, VERSION=2.0.0-637 -->
<Period>
<!-- Video -->
<AdaptationSet mimeType="video/mp4" segmentAlignment="true" startWithSAP="1" maxWidth="1280" maxHeight="720">
<SegmentTemplate timescale="1000" duration="5120" initialization="$RepresentationID$/init.mp4" media="$RepresentationID$/seg-$Number$.m4s" startNumber="1"/>
<Representation id="video/avc1" codecs="avc1.64001F" width="1280" height="720" scanType="progressive" frameRate="25" bandwidth="2140637"/>
</AdaptationSet>
<!-- Audio -->
<AdaptationSet mimeType="audio/mp4" startWithSAP="1" segmentAlignment="true">
<SegmentTemplate timescale="1000" duration="5120" initialization="$RepresentationID$/init.mp4" media="$RepresentationID$/seg-$Number$.m4s" startNumber="1"/>
<Representation id="audio/und/mp4a" codecs="mp4a.40.2" bandwidth="130417" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
</Representation>
</AdaptationSet>
</Period>
</MPD>
搭建nginx Web服务器
这里就不赘述怎么用nginx来实现web服务了,只记录应该要额外添加的nginx配置
由于在html文件里是直接获取上面生成的mpd文件来获取视频和音频文件,但是对于mpd文件和普通的m4s文件是必须要有额外的header(Access-Control-Allow-Headers, Access-Control-Allow-Origin)的,不然会报错播放不了;mpd文件还得特意指定conten-Type为dash+xml,具体如下
location ~ ^/dash/(.*).mpd$ {
root /home/pureload/www;
index index.html index.htm;
add_header Access-Control-Allow-Headers origin,range,hdntl,hdnts,accept-encoding,referer,CMCD-Request,CMCD-Object,CMCD-Status,CMCD-Session;
add_header Access-Control-Allow-Origin *;
add_header Content-Type application/dash+xml;
break;
}
location ~ ^/dash/(.*)$ {
root /home/pureload/www;
index index.html index.htm;
add_header Access-Control-Allow-Headers origin,range,hdntl,hdnts,accept-encoding,referer,CMCD-Request,CMCD-Object,CMCD-Status,CMCD-Session;
add_header Access-Control-Allow-Origin *;
break;
}
**Note: ** nginx起来后将上面生成的output文件夹里的所有内容放到/home/pureload/www/dash/NBA_720_mp4 目录跟下面访问(与下面的url匹配),注意html将会访问mpd文件而不是生成的视频和音频文件。
测试html
- 下载dash.js到指定目录
$ ll dash/*.js
-rwxrwxrwx 1 root root 7031604 Apr 14 09:08 dash/dash.all.debug.js*
-rwxrwxrwx 1 root root 650785 Apr 13 14:18 dash/dash.all.min.js*
- 测试html
a. 指定dash.js位置
b. 调用init方法来播放视频
c. 完整html
以下样例Html是监听了PLAYBACK_WAITING和CAN_PLAY事件的时间,并计算两者之间的差值来一个Video启动时间(Video start up time).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>video start up system test</title>
<style>
video {
width: 640px;
height: 360px;
}
</style>
</head>
<body>
<div>
<video id="videoPlayer" autoplay="autoplay" controls="controls"></video>
</div>
<p id="bufferTime">Total buffering time (ms):</p>
<p id="CAN_PLAYTime">CAN_PLAY time (ms):</p>
<p id="waiting_time">PLAYBACK_WAITING time (ms):</p>
<script src="dash/dash.all.min.js"></script>
<script>
function init() {
var video,
player,
waiting_time = 0,
CAN_PLAYTime = 0,
url = "http://23.0.48.1/dash/Football/manifest.mpd";
video = document.querySelector("#videoPlayer");
player = dashjs.MediaPlayer().create();
player.initialize(video, url, true);
player.on(dashjs.MediaPlayer.events["CAN_PLAY"], function () {
var myDate = new Date();
CAN_PLAYTime=myDate.getTime();
console.log("CAN_PLAY time = " + CAN_PLAYTime);
document.getElementById("CAN_PLAYTime").innerText = "CAN_PLAY time (ms):" + CAN_PLAYTime;
var bufferTime = CAN_PLAYTime - waiting_time;
document.getElementById("bufferTime").innerText = "Total buffering time (ms): " + bufferTime;
});
player.on(dashjs.MediaPlayer.events["PLAYBACK_WAITING"], function () {
var myDate = new Date();
waiting_time=myDate.getTime();
console.log("PLAYBACK_WAITING time = " + waiting_time);
document.getElementById("waiting_time").innerText = "PLAYBACK_WAITING time (ms): " + waiting_time;
});
}
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
init();
});
</script>
</body>
</html>
- 更多
a. dash用法请参考sample链接(https://reference.dashif.org/dash.js/latest/samples/index.html)
b. Dash.js的各种API module的使用方法参考(http://cdn.dashjs.org/latest/jsdoc/module-MediaPlayer.html)