如何使用金字塔结构来写一篇技术文档

技术人员都知道一篇好的技术文档的价值(这里换个表述),但是如何去写好一篇技术文档却一筹莫展。然而,在咨询业的传奇公司麦肯锡中,有一种叫做 ‘金字塔原理' 的写作方式可以快速的组织一篇结构化的文章。那么我们是否可以用 '金字塔原理' 来写一篇技术文档呢?

我们写技术文档的目的

对于软件来说,最重要的两点莫过于速度和质量了。写技术文档到底能不能提高软件研发速度不好衡量,但绝对可以提升软件研发过程的质量。软件的质量反映的是人类程序员的思考深度,而所有人类程序员都有如下两点先天限制: 缓存不足、上下文不同。

人脑的灵活性让我们有很高的创造力,但是在处理大型问题的时候却很难记住所有细节逻辑。所以将发散的思路梳理好并且记录下来这个过程,不仅可以帮助我们做好系统性的设计,还可以在设计阶段排除很多不必要的风险。简单来说就是: “高配 CPU 带小内存,你需要额外的空间来干活” 。

多人协作中,信息的传达效率以及信噪比是非常重要的。举个例子:我们知道在软件研发领域,英语是事实上的标准编程语言,但是在不同的业务场景中 "prod" 这几个字母的意思可能是 ”product“ 、”produce“ 、“producer” 这几个计算机领域常见词之一。文档的另一个核心作用就是: 显式固化只有开发人员才知道的隐性知识无论是缩写还是简写只要团队约定好字符串与实体的映射,那么整体沟通成本将明显下降。关于这一点,埃里克在 领域驱动设计) 这本书中有着精彩的论述,这里就不赘述了。

总的来说,写技术文档可以帮助我们作出更好的设计、显式固化隐性知识,从而提升软件研发的质量。

金字塔原理

我们用一些篇幅说明了文档和软件质量之间的联系,下面我们就来介绍一下金字塔原理。金字塔原理是一种突出重点、逻辑清晰、层次分明的思维方式,也是一种有效的分析问题、得出解决方案的有效工具。

01

作者在书中提供了一种金字塔式的文章组织方式,用来阐述有逻辑关联的事务,而软件正是不同事务变化时逻辑固化在硬盘上的表现,所以我认为用金字塔原理这种方式来写技术文档是再好不过的选择。

关于对金字塔原理的理解,可以选择看完《金字塔原理》的前三章,或者是通过我摘抄的作者对金字塔原理的解释来做初步了解:

  • 对受众(包括读者、听众、观众或学员)来说,最容易理解的顺序是: 先了解主要的、抽象的思想,然后了解次要的、为主要思想提供支持的思想。因为主要思想总是从次要思想概括总结得出,文章中所有思想的理想组织结构也必定是一个金字塔结构一一由一个总的思想统领多组思想。在这种金字塔结构中,思想之间的联系方式可以是纵向的( vertically)ー即任何一个层次上的思想都是对其下面一个层次上思想的总结;也可以是横向的( horizontally)-即多个思想因共同组成同一个逻辑推理过程,而被并列排在一起。
  • 受众的大脑只能逐句理解作者(演讲者、培训讲师)表达的思想。他们会假定一同出现的思想在逻辑上存在某种关系。如果你不预先告诉他们这种逻辑关系,而只是一句一句地表达你的思想,读者就会自动从中寻找共同点, 将你所表达的思想归类组合,以便了解各个组合的意义。

金字塔原理应用到技术文档写作的流程

02

时时刻刻,必须要提醒你自己:读这篇文档的人类是个缓存很小单线程图灵机,爆栈你就破功了,老弟/妹!

前面讲了很多理论性的东西,那么我们如何将理论与实践结合起来呢?下面分享一下我个人写技术文档的结构:

  1. 核心目标:金字塔的尖顶

    这里是金字塔最耀眼的尖顶,只写最核心的目标,比如说:调研某种技术方案、依照某个 PRD 开发产品部分功能。具体行文思路可以参照 《金字塔原理》 第4章— 序言的写法

  2. 方案概要设计: 金字塔的腰部

    这里是整体方案的蓝图,概要的抽象程度一定要很高,在这里必须可以看到全部架构。文字表述不清楚的地方可以多画图来辅助表达:

    1. 建议使用流程图用来表达状态流转
    2. 建议使用时序图用来表达交互顺序
    3. 建议使用C4 用来画系统架构
  3. 详细技术设计:金字塔坚实的基础

    详细设计部分是对上面概要设计的填充。这里是发现问题的黄金时间,一定要做很细致,需要有数据结构和关键逻辑的伪代码。细节设计做好之后,代码也写完三分之一了,剩下的就是写代码、写测试了。

  4. 维护:金字塔墙面砌砖与补漆抛光工艺

    根据我们上面对写文档目的的论述,文档不是一次性设计消耗,我们需要在开发有调整的时候,即时回来调整文档。举个不恰当的例子:汽车建造过程中刹车和油门换了位置,说明书总是要更新的吧。还有需要注意的一点就是: 当前文档只对当前版本负责,新版本不要在旧的文档上直接修改。

    03

这篇文章用了哪些金字塔原理中的方法

  1. 序言 :这里使用了 《第四章: 序言的常见模式》 中,背景-冲突-疑问 的三段模式,来隐晦的告诉读者可以用金字塔原理来写技术文档
  2. 金字塔原理应用的内容部分是用 顺序推理的方式来演示如何写技术文档

04

  1. 这篇文章的整体结构是归纳的方式

05

结语

金字塔原理是一套非常高效的结构化思考、表达工具。除了用来写文档,作者在书中也列举了很多其他应用场景,比如说:如何将 金字塔原理应用在做 PPT 上,如何用金字塔原理来分析问题等等。推荐各位仔细品读。

也来谈谈软件复杂度

最近一直在做框架开发,很多软件设计理论也有了尝试的机会,渐渐的也有些自己的思考。我一直在想一个问题:之前我所学习到的软件设计方式,几乎都是最佳实践类型的指引:你该如何去做。比如:SOLID 原则 告诉了我们遵从这 5 个原则就可以设计出优秀的软件。

那么什么是差的软件呢?是主观上的 bad smell 吗?读完《A Philosophy of software design》之后,我觉的软件的好坏可以由两个维度的 复杂度(complexity) 来描述:

  • 机器维度:空间和时间复杂度,时间和空间复杂度代表了执行同样任务的资源消耗
  • 人类维度:软件复杂度,人去理解、维护软件需要消耗的资源(包括时间,沟通,试错等各种成本)

对于软件复杂度 John Ousterhout 给的定义是:Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system. 学习过程中发现其他几篇博客也对软件复杂度有很精彩的观点:

在衡量软件复杂度的方面,Ousterhout 教授的定义方式是基于时间:

C=\sum_{p}{c_pt_p}

The overall complexity of a system (C) is determined by the complexity of each part p (cp) weighted by the fraction of time developers spend working on that part (tp).

这篇中还介绍了两种基于代码的衡量方式:cyclomatic complexiteyNPath, 我认为这两种在工程中有实践的意义。

复杂度是软件的固有属性之一,只有理解了什么是复杂度和衡量标准才能更好的处理软件中的复杂度。具体如何处理软件复杂度,Ousterhout 教授在他的书中有很多精彩的案例,强烈推荐阅读。

golang 疑问1:defer 调用之后到底发生了什么

先来看一个个函数,并猜猜它们的返回值

func f1() (result int) {
    defer func() {
        result *= 7
    }()
    return 6
}

尝试打印这个函数的返回值看看

fmt.Println(f1())
//stdout:42 

Why ‘42’ ???


‘42’ 的前世今生

首先,我们先用最简单的方式:单步调试看一下返回值的变化情况。

3f0076f6334f2d77907291a3c4dfa4d9.png
figure-1

55d020405d2e5102f15222240404124a.png
figure-2

ebbfa320125006bab01e2c7cbf0fe2e4.png
figure-3

69b503f7f002a832d18ac4ca51d55fc6.png
figure-4

通过上面的简单截图,我们可以看到:

  1. 返回值result初始值为 0
  2. 程序先在 figure-2 的时候,通过语句 return 6 将返回值修改为 6
  3. 然后在 figure-3的时候开始执行 defer 闭包
  4. 在 defer 执行完之后,返回值被修改成了42

通过上面简单分析,我们可以看到是 defer 最终修改了返回值,但是要想知道修改的机制,就要看下汇编了。

汇编分析

首先通过命令 GOOS=linux GOARCH=amd64 go tool compile -l -S main.go 获得如下汇编代码(注释是我自己加的):

"".f1 STEXT size=124 args=0x8 locals=0x20
    0x0000 00000 (main.go:7)    TEXT    "".f1(SB), ABIInternal, $32-8 // 生命函数即将分配的 栈帧32字节 参数8字节
    0x0000 00000 (main.go:7)    MOVQ    (TLS), CX // 这三行是检测栈空间是否足够,不够的话会跳转到runtime.morestack_noctxt(SB) 申请更多空间
    0x0009 00009 (main.go:7)    CMPQ    SP, 16(CX)
    0x000d 00013 (main.go:7)    JLS    117
    0x000f 00015 (main.go:7)    SUBQ    $32, SP // 申请32字节栈空间
    0x0013 00019 (main.go:7)    MOVQ    BP, 24(SP) // 保存 caller BP ,供函数执行完成之后恢复 caller 使用
    0x0018 00024 (main.go:7)    LEAQ    24(SP), BP // 将f1() 的栈低地址传入BP寄存器
    0x001d 00029 (main.go:7)    MOVQ    $0, "".result+40(SP) //赋初始值0给 返回参数 result
    0x0026 00038 (main.go:8)    MOVL    $8, (SP) // 将 deferproc 第一个参数压栈,runtime.panic.go:92 deferproc(siz int32, fn *funcval)
    0x002d 00045 (main.go:8)    LEAQ    "".f1.func1·f(SB), AX //保存匿名函数 AX
    0x0034 00052 (main.go:8)    MOVQ    AX, 8(SP) // 压栈
    0x0039 00057 (main.go:8)    LEAQ    "".result+40(SP), AX // 保存返回值地址 AX
    0x003e 00062 (main.go:8)    MOVQ    AX, 16(SP) //压栈, 和下方的deferproc 没太多关系,deferproc 只接受2个参数
    0x0043 00067 (main.go:8)    CALL    runtime.deferproc(SB) //调用 deferproc
    0x0048 00072 (main.go:8)    TESTL    AX, AX
    0x004a 00074 (main.go:8)    JNE    101
    0x004c 00076 (main.go:11)    MOVQ    $6, "".result+40(SP) //将数字6传入 返回参数 result
    0x0055 00085 (main.go:11)    XCHGL    AX, AX // 据说是只是为了填充指令 nop
    0x0056 00086 (main.go:11)    CALL    runtime.deferreturn(SB) //执行匿名函数, 在 runtime.panic.go:351 中,getcallersp() 获取了当前函数栈,
    0x005b 00091 (main.go:11)    MOVQ    24(SP), BP //恢复BP寄存器
    0x0060 00096 (main.go:11)    ADDQ    $32, SP // 释放32字节栈空间
    0x0064 00100 (main.go:11)    RET
    0x0065 00101 (main.go:8)    XCHGL    AX, AX
    0x0066 00102 (main.go:8)    CALL    runtime.deferreturn(SB)
    0x006b 00107 (main.go:8)    MOVQ    24(SP), BP
    0x0070 00112 (main.go:8)    ADDQ    $32, SP
    0x0074 00116 (main.go:8)    RET
    0x0075 00117 (main.go:8)    NOP
    0x0075 00117 (main.go:7)    CALL    runtime.morestack_noctxt(SB)
    0x007a 00122 (main.go:7)    JMP    0

"".f1.func1 STEXT nosplit size=20 args=0x8 locals=0x0 // 这里的栈结构要看 runtime.panic.go:345 deferreturn(arg0 uintptr)
    0x0000 00000 (main.go:8)    TEXT    "".f1.func1(SB), NOSPLIT|ABIInternal, $0-8
    0x0000 00000 (main.go:9)    MOVQ    "".&result+8(SP), AX // result 指针放入AX
    0x0005 00005 (main.go:9)    MOVQ    (AX), CX // AX指向的值放入 CX
    0x0008 00008 (main.go:9)    LEAQ    (CX)(CX*2), DX //DX = 2*CX + CX  -> 2*result + result = 3*result
    0x000c 00012 (main.go:9)    LEAQ    (CX)(DX*2), CX //CX = 2*DX + CX  -> 2*(3*result) + result = 7*result
    0x0010 00016 (main.go:9)    MOVQ    CX, (AX) // 将AX指向的值更新为 7*result
    0x0013 00019 (main.go:10)    RET

--未完待续--

参考连接

https://github.com/go-internals-cn/go-internals/blob/master/chapter1_assembly_primer/README.md
http://xargin.com/plan9-assembly/
https://github.com/golang/go/files/447163/GoFunctionsInAssembly.pdf
https://github.com/chai2010/advanced-go-programming-book/tree/master/ch3-asm
http://mcll.top/2018/04/15/golang%E4%B8%AD%E7%9A%84defer%E5%AE%9E%E7%8E%B0/

如果计算机体系不太熟,建议先看看这个

https://www.bilibili.com/video/av31289365/?p=6
http://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/schedule.html

Dash & 超低延时直播的研究

延时的来源

链式传播叠加的延时

  1. 编码和封装:引入延迟和参数配置、质量要求密切相关。某些流媒体协议可能会引入额外的延迟,因为它们只有在完全接收到后才输出一大块(chunk)媒体内容。
  2. 第一英里上传(first mile upload):将打包内容上传到CDN通常会受到商业条款的限制。例如,与来自新闻工作室的租用线路设置相比,如果通过无线连接完成上传将会产生更大的延迟。
  3. CDN传播:为了大规模传送内容,大多数媒体管道都利用内容传送网络(content delivery network)。因此,内容需要在不同缓存之间传播,从而引入额外延迟。
  4. 最后一英里交付(last mile delivery):用户网络连接可能会对延迟产生重大影响。用户可以在家庭网络连接到wifi热点,或者使用移动连接来访问网络内容。此外,由于可能会选取不同远近的CDN端点,用户地理位置也会造成额外延迟。
  5. 播放器缓冲区:视频播放器必须缓冲媒体以确保流畅播放。缓冲区大小通常在媒体规范中定义,但具有一定灵活性。播放缓冲是延迟的主要因素,优化缓冲区配置是常态。

7d8dfc0b722b8df93878853e19232c43.png

fig.1 (延迟来源)

延迟的长短

  • 典型延迟(typical latency)18-45s:如下图所示,在这个区域,我们看到一般都是HLS和MPEG-DASH设置,这两种适用于非时间敏感的线性广播,并且不会与广播公司或社交媒体上的其他观众进行任何交互。
  • 较少延迟(reduced latency)5-18s:通过调整HLS和MPEG-DASH流来减少延迟,减少了segment大小并增加了infrastructure的大小。该方法通常用于直播新闻和体育赛事。
  • 低延迟(low latency)1-5s:低延迟通常被视为每个发布者的目标,因为它允许更多交互式用例。
  • 超低延迟(ultra low latency)200ms-1s:可以实现更好的交互性,感觉接近实时。虽然不适合语音通信或会议,但这种延迟通常对于常见用例而言足够低。
  • 实时通信(real time communication)0-200ms:实时通信对于双向会议和通信等用例至关重要。

142a9a1242f3aa2fe1e5102e3219ed15.png

fig.2 (延迟长短定义)

DASH 和低延时

MPEG-DASH an overview

DASH 是一个类似于 HLS 的分片传输协议(其中一些多轨道,无缝切换之类的特性我们这里暂不讨论),DASH 中的列表文件是 mpd (Media Presentation Description) 。根据 mpd 中的几个时间字段(fig.3),我们可以算出 服务器和播放器直接的端到端延迟,这点很重要(详细算法可以看dash.js中getCurrentLiveLatency方法源码)。

fb8fe91d3dff26d1f7675ead769ed73f.png

fig.3 (DASH 时间模型)

能准确的获取端到端延迟在直播中最重要的意义就是:我们有了控制延迟的基础条件。在上面描述延迟图中(fig.1),第二步到第四步的网络传输抖动是我们无法控制的,但是只要我们知道了延迟的具体时间,就可以通过控制播放器播放进度,来实现快进或者慢放来保持稳定延时(sample)。 在下图(fig.4)中,我们通过播放器设置将延迟控制在了5s整。

96dfb5085b6ced836e6c7e04103e1d55.png

fig.4 (示例)

如何稳定进入5s以内? CMAF!

分块编码

实现低延迟的第一个必需行为是分块编码(chunked encoding)。根据MPEG CMAF标准,CMAF中各个对象的命名如图1所示。chunk是最小的可引用单元,至少包含moof和mdat这两部分。一个或多个chunk以形成fragment,一个或多个fragment形成一个segment。标准CMAF的media segment使用单个moof和mdat编码,如图2所示,mdat包含单个IDR(Instantaneous Decoder Refresh,瞬时解码器刷新)帧,这是每个segment开始传输所必需的。
b3a74ef9f53c4050ea245c6c167c2117.png

359bd2896cd7f9c61b115d42da3de80f.png
一般来说,segment将保持一系列chunk,即多个moof / mdat元组的序列,如图2所示。只有第一个元组保持IDR帧。将segment分成这些较短片段的优点是编码器可以在编码后立即输出每个chunk以便传输,这样就会导致整体延迟直接减少相同的量。每个块中包含多少帧没有固定的规定,目前的编码器范围为1至15帧。

DASH DASH-CMFA

再次强调一下,只有满足以下所有条件,才能稳定实现ULL-CMAF的减少延迟功能:

  1. CMAF段中的内容是块编码的。
  2. 编码器调整其DASH manifest/ HLS playlist以适应分块编码的使用和数据的早期可用性。
  3. 编码器使用HTTP 1.1块编码传输将内容推送到origin处。
  4. CDN在分发链的每个步骤使用HTTP块编码传输。
  5. 客户端:

    1. 准确地对segment的请求进行计时,并在live edge的一个segment持续时间内请求该切片;
    2. 在接收到比特流时对其进行解码,并且不用等到segment传输结束。在浏览器中运行的HTML5播放器必须使用Fetch而不是XHR API,因为Fetch允许在数据仍在下载时读取响应主体;
    3. 有一个估计吞吐量的方案,因为标准的segment定时技术将会失效;
    4. 具有缓冲和自适应逻辑以应对非常低的缓冲;
    5. 由于吞吐量波动,如果它落后于直播流,要具有赶上直播流的功能。

参考内容

优化延迟的最佳解决方案(一)
优化延迟的最佳解决方案(二)
优化延迟的最佳解决方案(三)

The importance of low latency in video streaming
视频传输延迟分析及解决方案:CMAF、LHLS

BEST PRACTICES FOR ULTRA-LOW LATENCY STREAMING USING CHUNKED-ENCODED AND CHUNK-TRANSFERRED CMAF
超低延迟CMAF流媒体方案解析

FFmpeg dash 相关参数翻译

由于个人英文水平、以及在流媒体领域水平有限,有限翻译可能有误,请参照原文食用。


Muxers are configured elements in FFmpeg which allow writing multimedia streams to a particular type of file.

4.7 dash

Dynamic Adaptive Streaming over HTTP (DASH) muxer that creates segments and manifest files according to the MPEG-DASH standard ISO/IEC 23009-1:2014.

For more information see:

ISO DASH Specification: http://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip
WebM DASH Specification: https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/webm-dash-specification
It creates a MPD manifest file and segment files for each stream.

The segment filename might contain pre-defined identifiers used with SegmentTemplate as defined in section 5.3.9.4.4 of the standard. Available identifiers are "$RepresentationID$", "$Number$", "$Bandwidth$" and "$Time$". In addition to the standard identifiers, an ffmpeg-specific "$ext$" identifier is also supported. When specified ffmpeg will replace $ext$ in the file name with muxing format’s extensions such as mp4, webm etc.,

SegmentTemplate 用到的 segment 文件名可能包含了在5.3.9.4.4节预先定义的标准标识符。可用的标识符为 "$RepresentationID$", "$Number$", "$Bandwidth$" 和 "$Time$"。除标准标识符之外,一个 ffmpeg 规格的 "$ext$" 也是支持的。 当这个值设置之后,ffmpeg 将会把$ext$ 替换为混流中的格式的扩展名,例如 mp4 , webm 等等。

ffmpeg -re -i <input> -map 0 -map 0 -c:a libfdk_aac -c:v libx264 \
-b:v:0 800k -b:v:1 300k -s:v:1 320x170 -profile:v:1 baseline \
-profile:v:0 main -bf 1 -keyint_min 120 -g 120 -sc_threshold 0 \
-b_strategy 0 -ar:a:1 22050 -use_timeline 1 -use_template 1 \
-window_size 5 -adaptation_sets "id=0,streams=v id=1,streams=a" \
-f dash /path/to/out.mpd
  • min_seg_duration microseconds
    This is a deprecated option to set the segment length in microseconds, use seg_duration instead.

这个选项用来设置 segment 长度,精度为毫秒,不建议使用。用 seg_duration 来替代

  • seg_duration duration
    Set the segment length in seconds (fractional value can be set). The value is treated as average segment duration when use_template is enabled and use_timeline is disabled and as minimum segment duration for all the other use cases.

设置 segment 长度,精度为秒(可以设置小数)。当 use_templateenable 状态,且 use_timelinedisable状态时,这个值会被当作 segment 的平均值。其他时候会被当作 segment 长度的最小值。

  • window_size size
    Set the maximum number of segments kept in the manifest.

设置保留在 manifestsegment 最大数量

  • extra_window_size size
    Set the maximum number of segments kept outside of the manifest before removing from disk.

设置从硬盘上删除之前 manifest 之外 segment 最大数量

  • remove_at_exit remove
    Enable (1) or disable (0) removal of all segments when finished.

enable (1) 或者 disable (0) 结束时删除所有 segments

  • use_template template
    Enable (1) or disable (0) use of SegmentTemplate instead of SegmentList.

Enable (1) 或者 disable (0) 使用 SegmentTemplate 替代 SegmentList.

  • use_timeline timeline
    Enable (1) or disable (0) use of SegmentTimeline in SegmentTemplate.

Enable (1) 或者 disable (0) 在 SegmentTemplate 中使用 SegmentTimeline .

  • single_file single_file
    Enable (1) or disable (0) storing all segments in one file, accessed using byte ranges.

Enable (1) 或者 disable (0) 将所有segments存储到一个文件中, accessed using byte ranges.
后半句有疑问

  • single_file_name file_name
    DASH-templated name to be used for baseURL. Implies single_file set to "1". In the template, "$ext$" is replaced with the file name extension specific for the segment format.

用于 baseURLDASH 模板名称。这意味着 single_file 设置为 “1”。在模板中,"$ext$" 将会被替换为 segment 格式指定的文件名扩展。

  1. $ext$ 这个参数用 ffmpeg34.1.3 尝试过,不生效
    -single_file_name "signame.\$Bandwidth\$.\$RepresentationID\$.\$Time\$.\$ext\$" --> signame.128000.1.0.$ext$
  • init_seg_name init_name
    DASH-templated name to used for the initialization segment. Default is "init-stream$RepresentationID$.$ext$". "$ext$" is replaced with the file name extension specific for the segment format.

用于初始segmentDASH模板名称。默认的 "init-stream$RepresentationID$.$ext$"。 "$ext$" 将会被替换为 segment 格式指定的文件名扩展。

  • media_seg_name segment_name
    DASH-templated name to used for the media segments. Default is "chunk-stream$RepresentationID$-$Number%05d$.$ext$". "$ext$" is replaced with the file name extension specific for the segment format.

用于媒体segmentDASH模板名称。默认为 "chunk-stream$RepresentationID$-$Number%05d$.$ext$"。"$ext$" 将会被替换为 segment 格式指定的文件名扩展。

  • utc_timing_url utc_url
    URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"

能以 ISO 格式返回 UTC 时间戳的网页地址。例如 "https://time.akamai.com/?iso"

  • method method
    Use the given HTTP method to create output files. Generally set to PUT or POST.

用给定的 HTTP method 创建输出文件。通常设置为 PUT 或者 POST

  • http_user_agent user_agent
    Override User-Agent field in HTTP header. Applicable only for HTTP output.

覆盖 HTTP headerUser-Agent字段。只适用于 HTTP 输出。

  • http_persistent http_persistent
    Use persistent HTTP connections. Applicable only for HTTP output.

使用持久HTTP连接。只适用于 HTTP 输出。

  • hls_playlist hls_playlist
    Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc.

同时生成 HLS 播放清单文件。生成的主播放清单文件名为 master.m3u8。每lu

  • streaming streaming
    Enable (1) or disable (0) chunk streaming mode of output. In chunk streaming mode, each frame will be a moof fragment which forms a chunk.

Enable (1) 或者 disable (0) 分块流模式输出。在分块流模式中,每一帧将会以 moof 片段的形式形成一个块。
moof movie segment,是分段形式解析,如同 hls 中的 ts 分片一样。

  • adaptation_sets adaptation_sets
    Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams.To map all video (or audio) streams to an AdaptationSet, "v" (or "a") can be used as stream identifier instead of IDs.When no assignment is defined, this defaults to an AdaptationSet for each stream.

分配 streamsAdaptationSets。语法是 "id=x,streams=a,b,c id=y,streams=d,e", x,yadaptation sets 的 ID,a,b,c,d,e 是映射到各路流的索引。
为了将所有视频(或音频)流映射到一个 AdaptationSetv(或者a)可以被用作流标识符来替代 ID。没有定义分配时,默认会将所有流放到一个 AdaptationSet 中(???)。

  • timeout timeout
    Set timeout for socket I/O operations. Applicable only for HTTP output.

设置 socket I/O 操作超时时间。只适用于 HTTP 输出。

  • index_correction index_correction
    Enable (1) or Disable (0) segment index correction logic. Applicable only when use_template is enabled and use_timeline is disabled.When enabled, the logic monitors the flow of segment indexes. If a streams’s segment index value is not at the expected real time position, then the logic corrects that index value.Typically this logic is needed in live streaming use cases. The network bandwidth fluctuations are common during long run streaming. Each fluctuation can cause the segment indexes fall behind the expected real time position.

enable (1) 或者 disable (0) segment 索引纠正逻辑。只适用于 use_template 开启,且use_timeline关闭时。
当这个参数开启时,这个逻辑会监控segment的索引流动(增量??)。如果一个流的 segment 索引值不在期望的真是时间位置,那么这个逻辑就会纠正索引值。
通常来说直播流情况下需要这个逻辑。在长时间直播时网络带宽通常会有波动。每次波动都有可能造成 segment 索引落后于期望的真是时间位置。

  • format_options options_list
    Set container format (mp4/webm) options using a : separated list of key=value parameters. Values containing : special characters must be escaped.

设置容器格式(mp4/webm)选项用一个 : 来分隔一系列 key=value 参数。Values 中包含的 : 特殊字符必须被转义。(The use of the word "escape" really means to temporarily escape out of parsing the text and into a another mode where the subsequent character is treated differently.)

  • global_sidx global_sidx
    Write global SIDX atom. Applicable only for single file, mp4 output, non-streaming mode.

写全局 SIDX atom。只适用于单文件,mp4输出,非流模式下。

  • dash_segment_type dash_segment_type
    Possible values:

    • auto
      If this flag is set, the dash segment files format will be selected based on the stream codec. This is the default mode.

    如果这个标志被设定,dash segment 文件格式将基于流格式。这个是默认模式。

    • mp4
      If this flag is set, the dash segment files will be in in ISOBMFF format.

    如果这个标志被设定,dash segment 文件将采用 ISOBMFF 格式。

    • webm
      If this flag is set, the dash segment files will be in in WebM format.

    如果这个标志被设定,dash segment 文件将采用 WebM 格式。

  • ignore_io_errors ignore_io_errors
    Ignore IO errors during open and write. Useful for long-duration runs with network output.

忽略 I/O 打开且在写时发生的错误。在长时间的网络输出中有用。

  • lhls lhls
    Enable Low-latency HLS(LHLS). Adds #EXT-X-PREFETCH tag with current segment’s URI. Apple doesn’t have an official spec for LHLS. Meanwhile hls.js player folks are trying to standardize a open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md This option will also try to comply with the above open spec, till Apple’s spec officially supports it. Applicable only when streaming and hls_playlist options are enabled. This is an experimental feature.

开启低延迟 HLS(LHLS)。给 #EXT-X-PREFETCH 标签增加当前的 segmentURI。 Apple 没有官方的 LHLS 规格。然而 hls.js播放器的人们正在尝试推行一个标准化的开放 LHLS 规格。规格草案在https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md可以看到。这个选项将会尝试遵循上面的公开规格,直到 Apple 有官方支持的规格。 只适用于 streaminghls_playlist 选项都开启时。这是个实验特性。

  • master_m3u8_publish_rate master_m3u8_publish_rate
    Publish master playlist repeatedly every after specified number of segment intervals.

特定的segment间隔数目后,重复输出主playlist