使用 MPEG-DASH 技术让视频画质自动适应网速

MPEG-DASH(基于HTTP的动态自适应流,Dynamic Adaptive Streaming over HTTP)技术可以使播放器在播放过程中根据网速实时调节视频清晰度,起到降低缓冲时间、提升画质的效果。有关 DASH 的详细介绍请看维基百科。

本文主要介绍将视频封装为 DASH 格式,并在网页上播放的方法。

1. 环境准备

要运行下文的大部分命令,您必须安装好 ffmpeg 和 x264,并且配置环境变量(Windows)等内容。或者,您也可以下载或编译出 ffmpeg 和 x264 的二进制文件,然后将下文命令中的 ffmpegx264 替换成完整路径。

为什么使用 ffmpeg,而不是 MP4Box?

在我的测试中,MP4Box 对帧率不同的视频支持状况不佳,会导致播放速度出错。

安装步骤

下面将介绍在 Ubuntu 18.04 中安装 ffmpeg 和 x264 的过程。本文编写时使用 ffmpeg 4.1.4 版本。x264 压制参数变化不大,但出现问题时建议尝试更新版本。同时,你也可以通过其他方式安装 ffmpeg 和 x264。

本文直接使用别人编译好的二进制。本文不对这些二进制文件的安全性负责。

(1) 添加软件源

运行命令

sudo add-apt-repository ppa:jonathonf/ffmpeg-4
sudo apt-get update

按提示操作。

(2) 安装软件

sudo apt install -y ffmpeg x264

2. 压制/准备视频

要实现画质切换,您需要将视频压制成不同清晰度。如果您喜欢,可以将音频也压制成不同的规格和码率。

注意点:按照本文的步骤,没有必要严格对齐 GOP。因为本文中会对不同清晰度的视频采用不同的帧率。

下面是压制命令。这条命令使用 ffmpeg 解码并改变视频帧率,然后通过管道将未压缩视频传给 x264 进行编码。

ffmpeg -i ${input} -an -r 60000/1001 -f yuv4mpegpipe -pix_fmt yuv420p - | x264 --crf ${crf} --demuxer y4m --preset veryslow -I ${i_frame} -r 4 -b 3 --me umh -i 1 --scenecut 60 -f 1:1 --qcomp 0.5 --psy-rd 0.3:0 --aq-mode 2 --aq-strength 0.8 --vf resize:3840,2160,,,, -o "${output_prefix}-2160p60.mp4" -

在上面的命令中:

  • ${input} 表示源文件名
  • -r 60000/1001 表示将视频帧率变为每秒 60000/1000 帧(约为 60,但是兼容性好于整数 60)
  • ${crf} 表示使用的 crf 值,您可以使用 22 左右的数值
  • ${i_frame} 表示最大关键帧间隔,单位是帧,不可过大,建议使用每秒帧率*2(舍入至最接近的整数即可)
  • --vf resize:3840,2160,,,, 会将输出视频缩放至指定的分辨率
  • -o "${output_prefix}-2160p60.mp4" 表示输出文件。

您也可以改用任何您喜欢的方式进行编码,但是需要注意兼容性。同时,也不要使关键帧间隔过大。

修改上面命令中的参数,压制出不同清晰度和帧率的视频。

3. 使用 ffmpeg 生成分片

假定压制了 5 个不同清晰度的视频,分别是

fate.2160p60.mp4
fate.1080p60.mp4
fate.720p60.mp4
fate.450p.mp4
fate.360p.mp4

同时,提取源文件中的音频(假定为 aac 编码。如果不是 aac 编码,您需要自行转换)

ffmpeg -i ${input} -vn -c copy fate.a.m4a

该命令会输出名为 fate.a.m4a 的音频流。

然后我们就可以使用 ffmpeg 生成分片了。

首先建立 fate 文件夹,用于存放生成的分片。文件夹的名称无要求,但是您需要修改后面的命令来确保将文件输出到您建立的文件夹中。

然后执行以下命令:

ffmpeg \
-i fate.2160p60.mp4 \
-i fate.1080p60.mp4 \
-i fate.720p60.mp4 \
-i fate.450p.mp4 \
-i fate.360p.mp4 \
-i fate.a.m4a \
-c copy \
-map 0 -map 1 -map 2 -map 3 -map 4 -map 5 \
-f dash \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
fate/manifest.mpd

接下来 ffmpeg 会输出大量信息。完成后,我们可以在 fate 文件夹中发现分好的分片。

4. 分析并修改 manifest 文件,减少载入时间

打开 fate/manifest.mpd 文件(或者你指定的其他路径)。可以看到文件开头如下。


<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="urn:mpeg:dash:schema:mpd:2011"
	xmlns:xlink="http://www.w3.org/1999/xlink"
	xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
	profiles="urn:mpeg:dash:profile:isoff-live:2011"
	type="static"
	mediaPresentationDuration="PT1M34.0S"
	minBufferTime="PT10.0S">

我们关注的是这一部分。

minBufferTime="PT10.0S"

这个属性的意思是,视频必须至少缓冲 10 秒才能开始播放。这里的数字可能因压制参数不同而变化。但是,缓冲 10 秒太长了,不但没有必要,还会严重拖慢载入速度。实际上,一般载入一点就可以开始播放了。因此,我们可以把时间改为 1.0 秒。

minBufferTime="PT1.0S"

5. 在网页上播放 DASH 视频,实现自动切换清晰度

有很多 js 库可以播放 DASH 视频。本文使用谷歌 shaka 播放器播放 DASH 视频。

为什么不用 dash.js?

dash.js 似乎也是使用比较广泛的 DASH 播放库。但是我在使用 dash.js 的过程中经常卡死,调试工具显示播放器反复请求同一分片。不确定是否是 GOP 未严格对齐的问题。

为了避免这个问题,干脆换用其他库。我在使用 shaka 的过程中没有遇到疑难问题。

编写测试网页

首先创建 myapp.js 文件。内容如下。

// myapp.js

function initApp() {
    // Install built-in polyfills to patch browser incompatibilities.
    shaka.polyfill.installAll();

    // Check to see if the browser supports the basic APIs Shaka needs.
    if (shaka.Player.isBrowserSupported()) {
        // Everything looks good!
        initPlayer();
    } else {
        // This browser does not have the minimum set of APIs we need.
        console.error('Browser not supported!');
    }
}

function initPlayer() {
    // Create a Player instance.
    var video = document.getElementById('video');
    var src = video.src;
    var player = new shaka.Player(video);

    player.configure({
        streaming: {
            bufferingGoal: 45,
            bufferBehind: 5,
            retryParameters: {
                timeout: 0,       // timeout in ms, after which we abort; 0 means never
                maxAttempts: 200,   // the maximum number of requests before we fail
                baseDelay: 100,  // the base delay in ms between retries
                // backoffFactor: 2, // the multiplicative backoff factor between retries
                // fuzzFactor: 0.5,  // the fuzz factor to apply to each retry delay
            }
        },
        abr: {
            defaultBandwidthEstimate: 500, // bits per second.
            switchInterval: 1
        }
    });

    // Attach player to the window to make it easy to access in the JS console.
    window.player = player;

    // Listen for error events.
    player.addEventListener('error', onErrorEvent);

    // Try to load a manifest.
    // This is an asynchronous process.
    player.load(src).then(function () {
        // This runs if the asynchronous load is successful.
        console.log('The video has now been loaded!');
    }).catch(onError);  // onError is executed if the asynchronous load fails.
}

function onErrorEvent(event) {
    // Extract the shaka.util.Error object from the event.
    onError(event.detail);
}

function onError(error) {
    // Log the error.
    console.error('Error code', error.code, 'object', error);
}

document.addEventListener('DOMContentLoaded', initApp);

然后创建一个 html 文件。您需要根据您的路径修改 src 属性。


<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/shaka-player/2.4.7/shaka-player.compiled.js">script>
    <script src="myapp.js">script>
head>
<body>
    <video id="video" src="fate/manifest.mpd" width="500" controls>video>
body>

将 js 文件、html 文件和 fate 文件夹一起上传到服务器,放到同一个目录。如果您网速较快,并且到您的服务器网速较好,应该可以看到视频由模糊变清晰,并且在网络波动时自动调整清晰度。若要修改默认清晰度,您可以修改 myapp.js 文件中的 defaultBandwidthEstimate 字段。 shaka 播放器会根据该字段设置的值选择默认清晰度。

你可能感兴趣的:(使用 MPEG-DASH 技术让视频画质自动适应网速)