0. 概述

环境:

  • 视频服务器:Ubuntu 16.04
  • 视频客户端:任意主机上的浏览器(推荐Chrome)

工具:

  • 服务器:Nginx
  • 编码器:FFmpeg
  • 视频切片:Bento4
  • 播放器:dash.js

*注:本文基于Bento4的mp4dash实现,关于基于MP4Box的实现方案,参见:Ubuntu下GPAC(MP4Box)的安装 | 基于MP4Box搭建DASH视频系统

视频:

  • Elephant Dream

分辨率级别:

  • 1920×1080(1080p)
  • 1280×720(720p)
  • 896×504(480p)1
  • 640×360(360p)
  • 256×144(144p)

参考:

说明:
本文记录了一个最基础的DASH视频服务器与播放器的搭建过程,并在本人掉坑之后补充了一些细节。有兴趣的初学者不需要了解具体知识,只需跟随说明,即可成功复现。
为了可读性,将工具的安装过程单独写在了别的博文里,点开即见,相当于函数封装了。


1. 搭建服务器(Nginx)

见:Ubuntu下Nginx的安装及使用
(建议使用源码安装)


2. 获取源视频及音频

视频名称:Elephants Dream
来源:Xiph.org :: Derf’s Test Media Collection

1. 视频
原始格式:y4m
原始帧率:24fps
原始时长:10m53s
原始比例:16:9
原始分辨率:1920×1080(1080p)
原始大小:46GB
下载链接:elephants_dream_1080p24.y4m.xz
文件为.xz压缩包,大小为7.13GB,解压命令为:

xz -d elephants_dream_1080p24.y4m.xz

2. 音频
原始格式:flac
原始时长:10m58s(*注:比视频长5s左右,后续没有对这个时间差进行处理)
下载页面:Index of /video/derf/flac/ED
下载链接:ED-CM-St-16bit.flac(*注:其他6个文件是5.1环绕立体声的6条音轨,只有这个是完整的音频)


3. 安装编码器(FFmpeg)

见:Ubuntu下FFmpeg的安装(支持libfdk_acc)
(需支持libfdk_aac,因此应使用源码安装)

若有更多编码需求(如VP9、AV1等),建议直接参阅官方文档:CompilationGuide/Ubuntu – FFmpeg


4. 视频编码

目的编码:H.264/AVC

目的分辨率级别:

  • 1920×1080(1080p)
  • 1280×720(720p)
  • 896×504(480p)
  • 640×360(360p)
  • 256×144(144p)

使用最简单的编码方式,每个分辨率视频的对应命令为:

ffmpeg -i elephants_dream_1080p24.y4m -s 1920x1080 -c:v libx264 -keyint_min 48 -g 48 -sc_threshold 0 -an ED1920x1080.mp4
ffmpeg -i elephants_dream_1080p24.y4m -s 1280x720 -c:v libx264 -keyint_min 48 -g 48 -sc_threshold 0 -an ED1280x720.mp4
ffmpeg -i elephants_dream_1080p24.y4m -s 896x504 -c:v libx264 -keyint_min 48 -g 48 -sc_threshold 0 -an ED896x504.mp4
ffmpeg -i elephants_dream_1080p24.y4m -s 640x360 -c:v libx264 -keyint_min 48 -g 48 -sc_threshold 0 -an ED640x360.mp4
ffmpeg -i elephants_dream_1080p24.y4m -s 256x144 -c:v libx264 -keyint_min 48 -g 48 -sc_threshold 0 -an ED256x144.mp4

关于选项:

  • -s:分辨率
  • -c:v libx264:使用x264编码库,将视频编码为H.264/AVC格式
  • -keyint_min 48 -g 48 -sc_threshold 0:固定GOP长度为48帧(在帧率24fps下对应2s的视频块时长,该数值按需调整),这里涉及到一个后续可能会遇到的错误2
  • -an:y4m格式不包含音频,因此此处不对音频进行编码

*注:

  • 编码可能需要很长时间,需要等待
  • 此时生成的各视频均不包含音频
  • 如需指定目标码率,可以使用-b:v选项进行配置,如-b:v 300k表示目标码率为300Kbps

编码完成后,各分辨率下的视频I帧对齐,I帧个数均为327个,对应327个GOP,每个GOP时长2s。各分辨率下的视频码率(单位:kbps)分别为(见ffmpeg输出信息):

  • 1920x1080(1080p):3988.49
  • 1280x720(720p):1983.08
  • 896x504(480p):1131.42
  • 640x360(360p):676.67
  • 256x144(144p):147.76

5. 音频编码

需要将音频使用AAC进行编码为m4a格式3,命令为:

ffmpeg -i ED-CM-St-16bit.flac -c:a libfdk_aac ED.m4a

为了后续的步骤,需要将该m4a音频与之前编码的视频进行合并,输出一个有音轨的视频4,具体命令为:

ffmpeg -i ED1920x1080.mp4 -i ED.m4a -vn -c:a copy ED_audio.mp4

*注:此视频只需要音轨,不需要视频画面,因此用-vn忽略视频编码,否则后面会多生成一种视频的Representation


*注:对音频编码时,若换用其他编码库(如alac),可能会在播放时引起错误:

[6395][Stream] audioCodec (audio/mp4;codecs="mp4a") is not supported.

这是因为浏览器不支持其他编码的音频,参考:Profiles and codecs supported by the Reference Client ( dash.js )?

Dash.js itself does not support codecs. It is a MSE client, meaning that it relies upon the browser’s MSE implementation to decode the content. It will pass along ANY codec that is specified within the manifest and ask the sourceBuffer if it can play it. So the answer to your question is really what codecs do Chrome, IE11, FF and Safari/Yosemite support for MSE?


6. 安装视频切片工具(Bento4)

见:Ubuntu下Bento4(mp4info、mp4fragment、mp4dash)的安装及使用

*注:鉴于mp4dash编码后MPD文件中的码率与视频真实码率不一致(见下),建议用MP4Box完成视频切片,详见:基于MP4Box搭建DASH视频系统


7. 视频切片

步骤:
1. fragment:使用mp4fragment对各分辨率视频及音频视频进行fragment(指定fragment时长为2s)

mp4fragment --fragment-duration 2000 ED1920x1080.mp4 f1080p.mp4
mp4fragment --fragment-duration 2000 ED1280x720.mp4 f720p.mp4
mp4fragment --fragment-duration 2000 ED896x504.mp4 f480p.mp4
mp4fragment --fragment-duration 2000 ED640x360.mp4 f360p.mp4
mp4fragment --fragment-duration 2000 ED256x144.mp4 f144p.mp4
mp4fragment --fragment-duration 2000 ED_audio.mp4 f_audio.mp4

2. 切片:使用mp4dash对已fragment的视频进行切片

mp4dash f1080p.mp4 f720p.mp4 f480p.mp4 f360p.mp4 f144p.mp4 f_audio.mp4

成功后,生成文件的目录结构为:

output/
├── audio/
│   └── und/
│       └── mp4a/
│           ├── init.mp4
│           ├── seg-1.m4s
│           ├── ...
│           └── seg-330.m4s
├── stream.mpd
└── video/
    └── avc1/
        ├── 1/
        ├── 2/
        ├── 3/
        ├── 4/
        └── 5/
            ├── init.mp4
            ├── seg-1.m4s
            ├── ...
            └── seg-327.m4s

可以看到,mp4dash将5种分辨率的视频分别分为了327个视频段,并为其生成了mpd文件。此外,由于音频比视频长5s左右,因此音频有330个段,比视频多了3个

生成的stream.mpd5的内容为:

<?xml version="1.0" ?>
<MPD mediaPresentationDuration="PT10M53.792S" minBufferTime="PT2.00S" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="static" xmlns="urn:mpeg:dash:schema:mpd:2011">
  <!-- Created with Bento4 mp4-dash.py, VERSION=1.8.0-629 -->
  <Period>
    <!-- Video -->
    <AdaptationSet maxHeight="1080" maxWidth="1920" mimeType="video/mp4" segmentAlignment="true" startWithSAP="1">
      <SegmentTemplate duration="2000" initialization="$RepresentationID$/init.mp4" media="$RepresentationID$/seg-$Number$.m4s" startNumber="1" timescale="1000"/>
      <Representation bandwidth="16079970" codecs="avc1.640028" frameRate="24" height="1080" id="video/avc1/1" scanType="progressive" width="1920"/>
      <Representation bandwidth="7753362" codecs="avc1.64001F" frameRate="24" height="720" id="video/avc1/2" scanType="progressive" width="1280"/>
      <Representation bandwidth="4310870" codecs="avc1.64001F" frameRate="24" height="504" id="video/avc1/3" scanType="progressive" width="896"/>
      <Representation bandwidth="2391544" codecs="avc1.64001E" frameRate="24" height="360" id="video/avc1/4" scanType="progressive" width="640"/>
      <Representation bandwidth="402408" codecs="avc1.64000C" frameRate="24" height="144" id="video/avc1/5" scanType="progressive" width="256"/>
    </AdaptationSet>
    <!-- Audio -->
    <AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1">
      <SegmentTemplate duration="2000" initialization="$RepresentationID$/init.mp4" media="$RepresentationID$/seg-$Number$.m4s" startNumber="1" timescale="1000"/>
      <Representation audioSamplingRate="48000" bandwidth="141821" codecs="mp4a.40.2" id="audio/und/mp4a">
        <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>

8. 编写网页播放器(dash.js)

*注:如果视频服务器有公网IP,则无需自己实现网页播放器,直接在Dash JavaScript Player中填上MPD文件的链接,点击"Load"即可(先完成本文第9步)。

播放器实现基于dash.js,其GitHub仓库为: Dash-Industry-Forum/dash.js: A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers

首先,需要下载dash.js到本地,下载链接为:dash.all.min.js
*注:若此链接失效,可通过dash.js的GitHub页面(见上)寻找最新版本文件。

之后,编写一个简单的播放器页面(index.html):

<!DOCTYPE html>
<html>
    <head>
        <title>Dash.js Rocks</title>
        <style>
            video {
                width: 640px;
                height: 360px;
            }
        </style>
    </head>
    <body>
        <div>
            <video data-dashjs-player autoplay src="./stream.mpd" controls>
            </video>
        </div>
        <script src="./dash.all.min.js"></script>
    </body>
</html>

*注意:这种写法要求index.htmldash.all.min.jsstream.mpd在同一目录下


9. 配置服务器

步骤:
1. 修改Nginx配置
/usr/local/conf/nginx.conf中,添加6

location /
{
	...
	add_header Access-Control-Allow-Methods "GET,OPTIONS,POST,HEAD,PUT,DELETE";
	add_header Accept-Ranges "bytes";
	add_header Access-Control-Allow-Origin "*";
	add_header Access-Control-Expose-Headers "Content-Lengrh,Content-Range,Date,Server,Transfer-Encoding,origin,range,x-goog-meta-foo1";
}

之后,验证并重载配置:

nginx -t
nginx -s reload

2. 部署相关文件

  • 将nginx的html/目录(即:/usr/local/nginx/html/)下原本的index.html改名为index_bak.html
  • 将之前生成的output/下所有文件(见上)转移至html/目录下
  • 将之前写好的index.html及下载的dash.all.min.js转移至html/目录下

此时,html/下的文件目录结构为:

/usr/local/nginx/html/
    50x.html
    audio/
        und/
            mp4a/
                init.mp4
                seg-1.m4s
                ...
                seg-330.m4s
    dash.all.min.js
    index_bak.html
    index.html
    stream.mpd
    video/
        avc1/
            1/
            2/
            3/
            4/
            5/
                init.mp4
                seg-1.m4s
                ...
                seg-327.m4s

最后,打开本地浏览器,输入:

http://localhost/

或在其他机器(客户端)上,输入:

http://服务器IP/

Elephant Dream
F12
可以看出,视频和音频是以一个个格式为m4s的分段进行传输的,其中较小的为音频,较大的为视频。

到此,一个完整的DASH视频系统就正式完成了。Enjoy it!~


  1. 这个分辨率是参考爱奇艺的 ↩︎

  2. 该问题详见:FFmpeg的GOP(I帧)对齐问题 ↩︎

  3. 参考:audio - How do I convert FLAC files to AAC (preferrably VBR 320k+)? ↩︎

  4. 参考:使用FFmpeg合并音视频 ↩︎

  5. 此处生成的MPD文件中的bandwidth值与之前ffmpeg编码得到的视频码率不一致,原因及分析见:mp4dash生成的MPD文件中的Bandwidth取值及其对客户端码率选择的影响 ↩︎

  6. 与HTTP访问控制相关,详见:HTTP访问控制(CORS) ↩︎

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐