如何在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

$ 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>
posted @ 2021-04-14 17:31  无知是恶  阅读(1044)  评论(0编辑  收藏  举报