标签归档:HLS

HTTP直播HTTP Live Streaming (HLS)

增加EXT-X-MEDIA-SEQUENCE或EXT-X 
      -DISCONTINUITY-SEQUENCE标签的值(第6.2.2节)。

      添加或删除EXT-X-STREAM-INF标签或EXT-XI-FRAME-STREAM-INF 
      标签(第6.2.4节)。请注意,客户端不需要
      重新加载主播放列表文件,因此更改它们可能不会
      立即生效。

      将EXT-X-ENDLIST标记添加到播放列表(第6.2.1节)。

   此外,播放列表文件可以包含EXT-X-PLAYLIST-TYPE标记
   ,其值为EVENT或VOD。如果标签存在且
   值为EVENT,则服务器不得更改或删除任何部分
   播放列表文件(尽管它可以向其添加行)。如果标签
   存在且值为VOD,则播放列表文件不得更改。

   播放列表中的每个媒体片段都必须应用EXTINF标记,以
   指示媒体片段的持续时间。

   媒体播放列表中的每个片段都具有整数不连续
   序列号。
   除了媒体内的时间戳之外,还可以使用不连续序列号来
   跨不同的再现来同步媒体段。Pantos&May将于2014年10月16日到期[第23页]

 
Internet-Draft HTTP Live Streaming 2014年4月

 
   段的不连续序列号是EXT-X 
   -DISCONTINUITY-SEQUENCE标记的值(如果没有,则为零)加上
   URI行之前的播放列表中的EXT-X-DISCONTINUITY标记的数量
   细分市场。

   包含EXT-X-PLAYLIST-TYPE标记的媒体播放列表,其
   值为EVENT或VOD,不得包含EXT-X-DISCONTINUITY- 
   SEQUENCE标记。

   服务器可以
   通过对其应用EXT-X-PROGRAM-DATE-TIME标记将绝对日期和时间与媒体段相关联。这
   定义了(挂钟)日期和时间的信息映射
   由标签指定给段中的第一媒体时间戳,
   其可以用作寻找,显示或用于其他
   目的的基础。如果服务器提供此映射,它应该将
   EXT-X-PROGRAM-DATE-TIME 
   标记应用于应用了EXT-X-DISCONTINUITY标记的每个段。

   如果媒体播放列表包含
   演示文稿的最终媒体段,则播放列表文件必须包含EXT-X-ENDLIST 
   标记。

   如果媒体播放列表不包含EXT-X-ENDLIST标记,则
   服务器必须使新版本的播放列表文件可用,
   其中包含至少一个新媒体段。它必须可用
   相对于以前版本的播放列表文件
   可用的时间:不早于该时间
   之后的目标持续时间的一半,并且不晚于该时间
   之后的目标持续时间的1.5倍。

   如果服务器希望删除整个演示文稿,则必须使
   播放列表文件对客户端不可用。它应该确保
   播放列表文件中的所有媒体段
   至少在删除时播放列表文件的持续时间内仍然可供客户端使用。

6.2.2。现场播放列表

   服务器可以通过
   从播放列表文件中删除媒体段来限制媒体段的可用性(第6.2.1节)。如果
   要删除媒体段,播放列表文件必须只包含
   一个EXT-X-MEDIA-SEQUENCE标记。对于
   从播放列表文件中删除的每个媒体段,其值必须递增1 。

   媒体片段必须按照
   它们在播放列表中出现的顺序从播放列表文件中删除。

   如果
   播放列表文件的持续时间减去段的持续时间
   小于目标持续时间的三倍,则服务器不得从播放列表文件中删除媒体段。

Pantos&May将于2014年10月16日到期[第24页]
 
Internet-Draft HTTP Live Streaming 2014年4月

 
   当服务器从播放列表中删除媒体段时,
   相应的媒体URI应该对客户端保持可用
   的时间段,该时间段等于段的持续时间加上
   分发的最长播放列表文件的持续时间。包含
   该段的服务器。

   如果服务器希望从
   包含EXT-X-DISCONTINUITY标签的媒体播放列表中删除段,则播放列表必须包含
   EXT-X-DISCONTINUITY-SEQUENCE标记。

   如果服务器从媒体
   播放列表中删除EXT-X-DISCONTINUITY标记,它必须增加EXT-X-DISCONTINUITY-的值 –
   SEQUENCE标记,以便
   仍然在播放列表中的段的不连续序列号保持不变。

   如果服务器计划在
   通过HTTP 将媒体段传递给客户端后将其删除,则应该确保HTTP响应包含
   反映计划生存时间的Expires标头。

   实时播放列表不得包含EXT-X-PLAYLIST-TYPE标记。

hls之m3u8、ts流格式详解

HLSHttp Live Streaming 是由Apple公司定义的用于实时流传输的协议,HLS基于HTTP协议实现,传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。

1、M3U8文件

   用文本方式对媒体文件进行描述,由一系列标签组成。

#EXTM3U

#EXT-X-TARGETDURATION:5

#EXTINF:5,

./0.ts

#EXTINF:5,

./1.ts

#EXTM3U:每个M3U8文件第一行必须是这个tag

#EXT-X-TARGETDURATION:指定最大的媒体段时间长度(秒),#EXTINF中指定的时间长度必须小于或等于这个最大值。该值只能出现一次。

#EXTINF:描述单个媒体文件的长度。后面为媒体文件,如./0.ts 

 

2、ts文件

    ts文件为传输流文件,视频编码主要格式h264/mpeg4,音频为acc/MP3。

   ts文件分为三层:ts层Transport Stream、pes层 Packet Elemental Stream、es层 Elementary Stream. es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ts层就是在pes层加入数据流的识别和传输必须的信息

注: 详解如下

 (1)ts层     ts包大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。ts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes数据。

ts header

sync_byte 8b 同步字节,固定为0x47
transport_error_indicator 1b 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内
payload_unit_start_indicator 1b 负载单元起始标示符,一个完整的数据包开始时标记为1
transport_priority 1b 传输优先级,0为低优先级,1为高优先级,通常取0
pid 13b pid值
transport_scrambling_control 2b 传输加扰控制,00表示未加密
adaptation_field_control 2b 是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。
continuity_counter 4b 递增计数器,从0-f,起始值不一定取0,但必须是连续的

     ts层的内容是通过PID值来标识的,主要内容包括:PAT表、PMT表、音频流、视频流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音视频流了。PAT表的PID值固定为0。PAT表和PMT表需要定期插入ts流,因为用户随时可能加入ts流,这个间隔比较小,通常每隔几个视频帧就要加入PAT和PMT。PAT和PMT表是必须的,还可以加入其它表如SDT(业务描述表)等,不过hls流只要有PAT和PMT就可以播放了。

  • PAT表:他主要的作用就是指明了PMT表的PID值。

  • PMT表:他主要的作用就是指明了音视频流的PID值。

  • 音频流/视频流:承载音视频内容。

adaption

adaptation_field_length 1B 自适应域长度,后面的字节数
flag 1B 取0x50表示包含PCR或0x40表示不包含PCR
PCR 5B Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。
stuffing_bytes xB 填充字节,取值0xff

     自适应区的长度要包含传输错误指示符标识的一个字节。pcr是节目时钟参考,pcr、dts、pts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr。如果没有字段,ipad是可以播放的,但vlc无法播放。打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。

PAT格式

table_id 8b PAT表固定为0x00
section_syntax_indicator 1b 固定为1
zero 1b 固定为0
reserved 2b 固定为11
section_length 12b 后面数据的长度
transport_stream_id 16b 传输流ID,固定为0x0001
reserved 2b 固定为11
version_number 5b 版本号,固定为00000,如果PAT有变化则版本号加1
current_next_indicator 1b 固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT表
section_number 8b 固定为0x00
last_section_number 8b 固定为0x00
开始循环    
program_number 16b 节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT
reserved 3b 固定为111
PID 13b 节目号对应内容的PID值
结束循环    
CRC32 32b 前面数据的CRC32校验码

PMT格式

table_id 8b PMT表取值随意,0x02
section_syntax_indicator 1b 固定为1
zero 1b 固定为0
reserved 2b 固定为11
section_length 12b 后面数据的长度
program_number 16b 频道号码,表示当前的PMT关联到的频道,取值0x0001
reserved 2b 固定为11
version_number 5b 版本号,固定为00000,如果PAT有变化则版本号加1
current_next_indicator 1b 固定为1
section_number 8b 固定为0x00
last_section_number 8b 固定为0x00
reserved 3b 固定为111
PCR_PID 13b PCR(节目参考时钟)所在TS分组的PID,指定为视频PID
reserved 4b 固定为1111
program_info_length 12b 节目描述信息,指定为0x000表示没有
开始循环    
stream_type 8b 流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03
reserved 3b 固定为111
elementary_PID 13b 与stream_type对应的PID
reserved 4b 固定为1111
ES_info_length 12b 描述信息,指定为0x000表示没有
结束循环    
CRC32 32b 前面数据的CRC32校验码



(2)pes层

     pes层是在每一个视频/音频帧上加入了时间戳等信息,pes包内容很多,我们只留下最常用的。

pes start code 3B 开始码,固定为0x000001
stream id 1B 音频取值(0xc0-0xdf),通常为0xc0

视频取值(0xe0-0xef),通常为0xe0
pes packet length 2B 后面pes数据的长度,0表示长度不限制,

只有视频数据长度会超过0xffff
flag 1B 通常取值0x80,表示数据不加密、无优先级、备份的数据
flag 1B 取值0x80表示只含有pts,取值0xc0表示含有pts和dts
pes data length 1B 后面数据的长度,取值5或10
pts 5B 33bit值
dts 5B 33bit值

     pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要,音频数据的pts和dts相同,所以只需要pts。有pts和dts两种时间戳是B帧引起的,I帧和P帧的pts等于dts。如果一个视频没有B帧,则pts永远和dts相同。从文件中顺序读取视频帧,取出的帧顺序和dts顺序相同。dts算法比较简单,初始值 + 增量即可,pts计算比较复杂,需要在dts的基础上加偏移量。

     音频的pes中只有pts(同dts),视频的I、P帧两种时间戳都要有,视频B帧只要pts(同dts)。打包pts和dts就需要知道视频帧类型,但是通过容器格式我们是无法判断帧类型的,必须解析h.264内容才可以获取帧类型。

举例说明:

                         I          P          B          B          B          P

读取顺序:         1         2          3          4          5          6

dts顺序:           1         2          3          4          5          6

pts顺序:           1         5          3          2          4          6

点播视频dts算法:

dts = 初始值 + 90000 / video_frame_rate,初始值可以随便指定,但是最好不要取0,video_frame_rate就是帧率,比如23、30。

pts和dts是以timescale为单位的,1s = 90000 time scale , 一帧就应该是90000/video_frame_rate 个timescale。

用一帧的timescale除以采样频率就可以转换为一帧的播放时长

点播音频dts算法:

dts = 初始值 + (90000 * audio_samples_per_frame) / audio_sample_rate,audio_samples_per_frame这个值与编解码相关,aac取值1024,mp3取值1158,audio_sample_rate是采样率,比如24000、41000。AAC一帧解码出来是每声道1024个sample,也就是说一帧的时长为1024/sample_rate秒。所以每一帧时间戳依次0,1024/sample_rate,…,1024*n/sample_rate秒。

直播视频的dts和pts应该直接用直播数据流中的时间,不应该按公式计算。

(3)es层

     es层指的就是音视频数据,我们只介绍h.264视频和aac音频。

h.264视频:

     打包h.264数据我们必须给视频数据加上一个nalu(Network Abstraction Layer unit),nalu包括nalu header和nalu type,nalu header固定为0x00000001(帧开始)或0x000001(帧中)。h.264的数据是由slice组成的,slice的内容包括:视频、sps、pps等。nalu type决定了后面的h.264数据内容。

F 1b forbidden_zero_bit,h.264规定必须取0
NRI 2b nal_ref_idc,取值0~3,指示这个nalu的重要性,I帧、sps、pps通常取3,P帧通常取2,B帧通常取0
Type 5b 参考下表
nal_unit_type 说明
0 未使用
1 非IDR图像片,IDR指关键帧
2 片分区A
3 片分区B
4 片分区C
5 IDR图像片,即关键帧
6 补充增强信息单元(SEI)
7 SPS序列参数集
8 PPS图像参数集
9 分解符
10 序列结束
11 码流结束
12 填充
13~23 保留
24~31 未使用

     红色字体显示的内容是最常用的,打包es层数据时pes头和es数据之间要加入一个type=9的nalu,关键帧slice前必须要加入type=7和type=8的nalu,而且是紧邻。

转自:http://my.oschina.NET/u/727148/blog/666824

M3U8的简单介绍和在Android中使用的思路

 (在项目中有用到m3u8,现在写篇博文,算是简单的总结

首先是名词介绍,什么是m3u8。m3u8是m3u的一种,不过是utf-8格式的,我记忆中说m3u8是苹果公司搞出来的一种播放的标准吧,其实简单来说就是把整个视频切成一段一段的,然后呢用一个m3u8格式来存这些个小段视频们的地址。可能大家就要问了,这么麻烦干嘛。其实m3u8是为了码率适配而生,而怎样去适配码率呢,这个下面介绍格式的时候会介绍到。

上两个m3u8文件的例子地址,大家能有直观的认识,这是我从Vitamio的官网上扒的。

http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8

http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8

 

我总结了一下我遇到的m3u8格式,虽然不能说涵盖了全部的情况,但是也差不多了:

1、一级目录(我觉着一级的目录没有适配码率的功能)

1.1、打开第一级m3u8文件,能找到真正的视频地址

1.2、第一级m3u8文件中,没有真正的视频地址,需要拼接才能找到真正的视频地址

2、二级目录

2.1、二级地址在一级文件中直接能看到

2.2、二级地址在一级文件中不能直接看到,需要拼接一级链接的地址才能找到二级文件的地址

2.2、打开二级目录,能找到整整的视频地址

2.3、没有真正的视频地址,需要拼接才能找到真正的视频地址

 

篇幅关系我不能给大家全部列举出这些全部的可能性。我就拿最麻烦的举个例子,其他的大家自行脑补吧,原理都是一样的,怎么样都跑不出协议的范畴之外。

我们在浏览器中输入http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8,会得到一个名为bipbopall.m3u8的文件,此文件的内容如下:

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=200000

gear1/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=311111

gear2/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=484444

gear3/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=737777

gear4/prog_index.m3u8

这就符合上面的2.2种情况,这四种码率的m3u8的地址你都不能直接得到,那怎么办呢,我们用得到这个文件的链接地址的前半段http://devimages.apple.com/iphone/samples/bipbop/拼接上二级文件的相对地址gear1/prog_index.m3u8得到一个地址http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8

把此地址放到浏览器中,我们又会得到一个同样名为prog_index.m3u8的文件,内容如下:

#EXTM3U

#EXT-X-TARGETDURATION:10

#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:10, no desc

fileSequence0.ts

#EXTINF:10, no desc

fileSequence1.ts

#EXTINF:10, no desc

fileSequence2.ts

#EXTINF:10, no desc

fileSequence3.ts

#EXTINF:10, no desc

fileSequence4.ts

#EXTINF:10, no desc

fileSequence5.ts

#EXTINF:10, no desc

.

.

.

#EXTINF:10, no desc

fileSequence179.ts

#EXTINF:1, no desc

fileSequence180.ts

#EXT-X-ENDLIST

我们很开心的发现,这设计简直是巧(sang)夺(xin)天(bing)工(kuang),我们还是没有得到真正的视频地址,老办法拼接后我们得到这么一段链http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence179.ts,这就是真正的视频地址。

我举的这个例子是最复杂的情况,一般的情况对于这个来说都是相对简单的。就跟软件设计一样,我们先考虑到最难得情况,简单的来说就迎刃而解了

此篇博文没有具体介绍m3u8的格式,各位看管不了解的话还请自行Google之。

此篇博文完全是作者的经验之谈,可能有不确切的地方还请见谅,转载请贴原文地址。

HLS协议直播延时优化(35s到10S)

1、首先要了解HLS延时的机制,也就是为什么会延时,延时主要发生在什么地方。

HTTP Live Streaming 并不是一个真正实时的流媒体系统,这是因为对应于媒体分段的大小和持续时间有一定潜在的时间延时。在客户端,至少在一个分段媒体文件被完全下载后才能够开始播放,而通常要求下载完两个媒体文件之后才开始播放以保证不同分段音视频之间的无缝连接。此外,在客户端开始下载之前,必须等待服务器端的编码器和流分割器至少生成一个TS文件,这也会带来潜在的时延。服务器软件将接收到的流每缓存一定时间后包装为一个新的TS文件,然后更新m3u8文件。m3u8文件中只保留最新的几个片段的索引,以保证观众任何时候连接进来都会看到较新的内容,实现近似直播的效果。这种方式的理论最小延时为一个ts文件的时长,一般为2-3个ts文件的时长。

所以,hls的延时主要由以下三个部分组成:

(1)服务器端的编码器和流分割器生成TS文件的时间

(2)客户端下载TS文件的时间,而通常要求下载完两个TS媒体文件

(3)客户端解码并播放时间

这三个方面里面,前两个方面我们是可以控制调节的,对于第三个方面只能取决于客户端的性能。

2、具体优化方法

由于服务器端生成TS流段需要时间,那么我们可以调节每段TS文件的大小,让其小些,那么服务器生成它的速度就加快,时间缩短。这样一来,客户端下载第一段或者前两段的时间就会减少,延时就会降低。根据上述的方式可以更改HLS的分段大小,方法是修改nginx配置文件nginx.conf,默认情况下nginx.conf文件的hls配置部分如下:

复制代码
rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        application live {
                live on;
        }
        hls on;
        hls_path /tmp/hls;
    }
}
复制代码

文件并没有设置HLS 分段长度,添加设置:

hls_fragment  1s; 

将每段的长度限定为1s,HLS官方推荐的是10s,但是在我这里10s延时太大。但是段的时长越短,服务器的负载越大,延时越少。对于这句话我不是十分理解,至少我并没有发现服务器负载增加。当每段的长度固定之后,播放列表的长度也会影响延时时间,而且会对再次播放时的开始时间产生影响,非首次播放时,客户端会在播放列表的开头开始播放,所以总的延时时间等于播放列表长度加上上述的延时时间。所以将播放列表长度不要设置太大:

hls_playlist_length 3s; 

这样设置完之后的配置文件RTMP模块配置部分为:

复制代码
rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        application live {
                live on;
        }
        hls on;
        hls_path /tmp/hls;
        hls_fragment 1s;
        hls_playlist_length 3s;
    }
}
复制代码

配置完成后重新启动nginx,重新使用ffmpeg推流,结果延时时间降到7~8s。

优化前测试结果:26S


 

优化后VLC播放测试结果:11s


 

 

贵在坚持,相信自己.

基于Nginx搭建http/rtmp/hls协议的MP4/FLV流媒体服务器

一.搭建nginx平台:
基本是基于网上资料一步步安装nginx搭建流媒体服务器 这篇博客来搭建。
我的ubuntu是14.04LTS。各种包的版本是:
nginx-1.9.9
nginx_mod_h264_streaming-2.2.7.tar.gz
openssl-0.9.8zh.tar.gz
pcre-8.12.tar.gz
zlib-1.2.7.tar.gz
为了支持rtmp,还下载了一个模块nginx-rtmp-module-master,没有版本的区别。
在安装过程中的注意事项:
1.上述无非是./configure make make install.
2.记住如果要修改nginx的./configure的话只用make无需install因为不是覆盖安装。
3.要在sbin的目录下检查./nginx -V 才能看到nginx的配置。
4.nginx在make的时候注意把objs里面的Makefile的权限改成a+w,并且将-Werror删掉,这样就不会把warning看成error来对待。修改makefile是在configure之后make之前。
二.测试http、rtmp、hls的功能
1.配置文件(仅支持http和rtmp)
user  www www;  ##管理用户

worker_processes 30;    ##后台进程

error_log  /usr/local/nginx/logs/error.log;

##nginx错误日志存放路径

pid        /usr/local/nginx/logs/nginx.pid;

events {

use epoll;

##轮训方式

worker_connections      65535;

##允许的最大连接数

}

http {

include       mime.types;

default_type  application/octet-stream;

log_format  main  ‘$remote_addr – $remote_user [$time_local] “$request” ‘

‘$status $body_bytes_sent “$http_referer” ‘

‘”$http_user_agent” “$http_x_forwarded_for”‘;

access_log  /usr/local/nginx/logs/access.log;

sendfile        on;

tcp_nopush     on;

#keepalive_timeout  0;

keepalive_timeout  65;

gzip on;
gzip_min_length 1000;
gzip_buffers 4 8k;
gzip_types text/plain;

server {

listen       80;

server_name  192.168.16.69;
#root html;
root /usr/local/nginx/html/flv_file;

#charset koi8-r;

limit_rate_after 5m;

limit_rate  512k;
index  index.html;
charset utf-8;

#   access_log  /usr/local/nginx/logs/host.access.log  main;

#    location / {

#    root   /usr/local/nginx/html/flv_file;

#            index  index.html;

# limit_rate_after 5m;

# limit_rate  512k;

#error_page  404              /404.html;

location ~ \.flv$ {
#     root /var/www/flv;
flv;
}

location ~ \.mp4$ {
#          root /var/www/mp4;
mp4;
}
}
}
rtmp {
server {
listen 1935;
chunk_size 4000;

# video on demand
application vod {
play /usr/local/nginx/html/flv_file;
}

# HLS
# HLS requires libavformat & should be configured as a separate
# NGINX module in addition to nginx-rtmp-module:
# ./configure … –add-module=/path/to/nginx-rtmp-module/hls …
# For HLS to work please create a directory in tmpfs (/tmp/app here)
# for the fragments. The directory contents is served via HTTP (see
# http{} section in config)
#
# Incoming stream must be in H264/AAC/MP3. For iPhones use baseline #H264
# profile (see ffmpeg example).
# This example creates RTMP stream from movie ready for HLS:
#
# ffmpeg -loglevel verbose -re -i movie.avi  -vcodec libx264
#    -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
#    -f flv rtmp://localhost:1935/hls/movie
#
# If you need to transcode live stream use ‘exec’ feature.
#
application hls {
hls on;
hls_path /usr/local/nginx/html/flv_file;
hls_fragment 10s;
}
}
}
2.配置文件(可支持http,rtmp,hls)

#filename:nginx.conf

#user  nobody;
worker_processes  1;

error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

pid        logs/nginx.pid;

events {
use epoll;
worker_connections  1024;
}

rtmp {
server {
listen 1935;
chunk_size 4000;

# video on demand
application vod {
play /usr/local/nginx/html/flv_file;
}

# HLS
# HLS requires libavformat & should be configured as a separate
# NGINX module in addition to nginx-rtmp-module:
# ./configure … –add-module=/path/to/nginx-rtmp-module/hls …
# For HLS to work please create a directory in tmpfs (/tmp/app here)
# for the fragments. The directory contents is served via HTTP (see
# http{} section in config)
#
# Incoming stream must be in H264/AAC/MP3. For iPhones use baseline #H264
# profile (see ffmpeg example).
# This example creates RTMP stream from movie ready for HLS:
#
# ffmpeg -loglevel verbose -re -i movie.avi  -vcodec libx264
#    -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1
#    -f flv rtmp://localhost:1935/hls/movie
#
# If you need to transcode live stream use ‘exec’ feature.
#
application hls {
hls on;
hls_path /usr/local/nginx/html/flv_file;
hls_fragment 10s;
}
}
}

http {

include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;

#log format

log_format  access  ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘$status $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” $http_x_forwarded_for’;

#定义一个名为addr的limit_zone,大小10M内存来存储session
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
listen 8080;
server_name localhost;

# HTTP can be used for accessing RTMP stats
# This URL provides RTMP statistics in XML
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /home/nairely/Documents/nginxserver/nginx-rtmp-module-master;
}
location /control {
rtmp_control all;
}
location / {
root /home/nairely/Documents/nginxserver/nginx-rtmp-module-master/test/rtmp-publisher;
}
}

server {
listen 80;
server_name localhost;

location / {
root /usr/local/nginx/html/flv_file;
index index.html;
}

location ~ \.flv$ {
root /usr/local/nginx/html/flv_file;
flv;
limit_conn addr 20;
limit_rate 200k;
}
location ~ \.mp4$ {
root /usr/local/nginx/html/flv_file;
mp4;
limit_conn addr 20;
limit_rate 200k;
}

location /hls {
# Serve HLS fragments
alias /usr/local/nginx/html/flv_file;
}

access_log  logs/nginxflv_access.log access;
}
}

3.如果视频不能播放很有可能是权限的问题,设置成644. root(read and write),group(read only)user(read only)
4.nginx作为一个后台服务器,用户从html/flv_file的文件夹中取出视频用户点播。测试的播放器用的是vlc。
http的地址是http://192.168.16.46/yequ.flv
rtmp的地址是rtmp://192.168.16.46:1935/vod/yequ.flv
hls的地址是http://192.168.16.46/hls/sample.m3u8(在segment命令将output的URL设置为http://192.168.16.46/hls)将sample.m3u8文件放在html/flv_file文件夹下,当然这取决与segment语句在哪个地址下进行。
5.如何启动nginx:
cd /usr/local/nginx/sbin
./nginx -t 检查配置文件的语法问题
如果发现配置文件的路径不对
./nginx -c 路径
./nginx -s reload重新加载配置文件
killall -9 nginx 杀掉所有进程从头开始,如果遇到端口被占用的问题的话。
./nginx 启动,注意在杀掉进程之后一定要重新启动。
service nginx start
三.配置hls+ffmpeg的环境
在配置http和rtmp都比较简单。在搭建nginx平台的前提下就可以实现点播。配置hls出现的问题都比较简单。只是会花一段时间。
主要的配置还是根据这篇来的http://www.lc365.net/blog/b/31519/ 基于HTTP Live Streaming(HLS) 搭建在线点播系统
这些包实在太多了,我简直属于无聊的阶段。
1.faac的编译问题
http://zhidao.baidu.com/link?url=ASyVwiBE-01ox_O0QascgPdqNNRlXpHCfI6cXyg71JIboOK5MTj3NLfHUPC31HH5b0FiE3tbWUetUfKL29HAzzXu4q0p75Iveu05HPp_ST3

2.在编译ffmpeg的时候发现x264notfound的解决方法
在x264的./configure –enable-static –enable-shared
–enable-visualize –system-libx264 –enable-gprof –enable-strip
–enable-pic –enable-debug
就是把所以的开关都打开。这简直是误打误撞。
3.m3u8configure的时候总是会遇到error的问题,可以看到这些错误都是什么东西过时的啥的,直接换ffmpeg。换ffmpeg版本到最新。ffmpeg-2.8.4
git clone https://github.com/johnf/m3u8-segmenter
cd m3u8-segmenter
然后configure的时候
gcc -Wall -g m3u8-segmenter.c -o segmenter -lavformat -lavcodec -lavutil
cp segmenter /usr/bin/
在html/flv_file路径下
ffmpeg -i /var/flvs/baluobu.flv  -f mpegts -acodec libmp3lame -ar
48000 -ab 128k -vcodec libx264 -b 96k -flags +loop -cmp +chroma
-partitions +parti4x4+partp8x8+partb8x8 -subq 5 -trellis 1 -refs 1
-coder 0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71
-bt 200k -maxrate 96k -bufsize 96k -rc_eq ‘blurCplx^(1-qComp)’ -qcomp
0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30 -async 2
/var/app/baluobu/baluobu.ts
上述是把flv文件转换成ts文件
下面用segmenter命令将ts转换成10秒一个ts切片,segmenter_test是ts小切片的前缀,-u制定URL地址。
segmenter -i vp5.ts -d 10 -p segmenter_test -m vp5.m3u8 -u http://192.168.16.46/hls/
在segmenter的时候有一个红色的ignore,之前一直认为是一个error,我去才发现是一个不用管的东西,然后在vlc上用http://192.168.16.46/hls/vp5.m3u8放,其实类似一个直播的功能,播完了就没有ts文件了。
再来一次测试就得再来一遍ffmpeg和segmenter.
总结到这儿。

M3U8有啥好处 ?

网上搜索了一下,大家众说纷纭,个人理解主要是可以做多码率的适配,根据网络带宽,客户端会选择一个适合自己码率的文件进行播放,保证视频流的流畅。

在IOS device和mac上可以用http的方式进行分发,其中playlist标准为由m3u扩展而来的m3u8文件,媒体文件为MPEG2-TS或者AAC文件(audio only)。

m3u8文件有两种应用场景:

多码率适配流,

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000

http://example.com/low.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000

http://example.com/mid.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000

http://example.com/hi.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=”mp4a.40.5″

http://example.com/audio-only.m3u8

单码率适配流

#EXTM3U

#EXT-X-TARGETDURATION:5220

#EXTINF:5220,

http://media.example.com/entire.ts

#EXT-X-ENDLIST



国际标准组织对此的定义 rfc doc:

http://tools.ietf.org/html/draft-pantos-http-live-streaming-06

m3u8 文件是m3u文件的扩展。在该rfc中定义了扩展的关键字:

其中:

#EXT-X-TARGETDURATION

定义每个TS的最大的duration。

#EXT-X-MEDIA-SEQUENCE

定义当前m3u8文件中第一个文件的序列号,每个ts文件在m3u8文件中都有固定唯一的序列号,该序列号用于在MBR时切换码率进行对齐。

#EXT-X-KEY

定义加密方式和key文件的url,用于取得16bytes的key文件解码ts文件。

属性:

METHOD

URL

#EXT-X-PROGRAM-DATE-TIME

第一个文件的绝对时间

#EXT-X-ALLOW-CACHE

是否允许cache。

#EXT-X-ENDLIST

表明m3u8文件的结束。live m3u8没有该tag。

#EXT-X-STREAM-INF

属性:

BANDWIDTH              指定码率

PROGRAM-ID            唯一ID

CODECS                    指定流的编码类型

#EXT-X-DISCONTINUITY

当遇到该tag的时候说明以下属性发生了变化:

file format 

number and type of tracks

encoding parameters

encoding sequence

timestamp sequence

#EXT-X-VERSION             该属性用不用都可以,可以没有





M3U8分顶级M3U8和二级M3U8, 顶级M3U8主要是做多码率适配的, 二级M3U8才是真正的切片文件,

客户端默认会首先选择码率最高的请求,如果发现码率达不到,会请求郊低码率的流

一个实际使用中的顶级M3U8文件如下 :

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=201273221265,BANDWIDTH=358400

11.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=201273221265,BANDWIDTH=972800

22.m3u8



上面顶级M3U8文件中又定义了 11.m3u8 和 22.m3u8 两个二级文件,客户端会选择其中一个获取其内容。

二级M3U8文件内容如下:



#EXTM3U

#EXT-X-VERSION:1

#EXT-X-TARGETDURATION:10

#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:3,

1-4.ts

#EXTINF:8,

1-6.ts

#EXTINF:8,

1-8.ts

#EXTINF:8,

1-10.ts

#EXTINF:8,

1-12.ts

#EXTINF:8,

1-14.ts

#EXTINF:8,

1-16.ts

#EXTINF:9,

1-18.ts

#EXTINF:6,

1-20.ts

#EXTINF:8,

1-22.ts

#EXTINF:9,

1-24.ts

#EXTINF:3,

1-26.ts

#EXT-X-ENDLIST



客户端拿到上面的二级M3U8文件后,会继续请求里面的文件,这时就可进行播放了。

上面讲解的是点播的情况,直播的情况,M3U8文件里面会有属性告诉是直播,客户端会定时来请求新的M3U8文件。

流媒体开发之–HLS–M3U8解析(2): HLS草案

目录

1 简介 2

2 概述 2

3 播放列表文件 3

3.1 介绍 3

3.2新标签 4

3.2.1 EXT-X-TARGETDURATION 4

3.2.2 EXT-X-MEDIA-SEQUENCE 4

3.2.3 EXT-X-KEY 4

3.2.4 EXT-X-PROGRAM-DATE-TIME 5

3.2.5 EXT-X-ALLOW-CATCH 5

3.2.6 EXT-X-ENDLIST 5

3.2.7 EXT-X-STREAM-INF 5

3.2.8 EXT-X-DISCONTINUITY 6

3.2.9 EXT-X-VERSION 6

4 多媒体文件 7

5 密钥文件 7

5.1 介绍 7

5.2  IV FOR AES-128 7

6 客户端/服务器行为 8

6.1 介绍 8

6.2 服务器进程 8

6.2.1介绍 8

6.2.2 滑动窗口播放列表 9

6.2.3 加密媒体文件 9

6.2.4 提供变种数据流 10

6.3 客户端进程 10

6.2.1 介绍 10

6.2.2 加载播放列表文件 11

6.2.3播放播放列表文件 11

6.2.4重新载入播放列表文件 11

6.2.5 确定下一个要加载的文件 12

6.2.6 解密经加密的媒体文件 12

7 协议版本的兼容性 12

8 例子 12

8.1 简单的播放列表文件 12

8.2 滑动窗口播放列表,使用https 13

8.3 加密的媒体文件与播放列表文件 13

8.4 变种的播放列表文件 13

 
 

1简介

本文档介绍了通过HTTP传输极大的多媒体数据流的协议[RFC2616]。该协议支持媒体数据的加密,并提供流的备用版本(如比特率)。媒体数据可以在创建后被很快地传输,允许它在近实时被接收。

在第11章中列出了,如HTTP的,描述相关标准的外部引用。

 

2概述

多媒体演示文稿是由播放列表文件中的URI指定的,播放列表是一个由uri和信息标签组成的有序列表。每一个URI都关联了一个媒体文件,该媒体文件是一个连续数据流的一个分片。
为了播放数据流,客户端首先获取播放列表文件,然后获取并播放列表中的每一个媒体文件。正如本文档所描述的那样,它通过重载播放列表文件来发现其他新增的分片。
文档中的关键词“必须”“不准”,“需要”“应该”“不应该”“推荐”“可以”“可选”等见RFC2119
 

3播放列表文件

3.1介绍

播放列表必须是扩展的M3U文件,该文档通过定义新的标签扩展了m3u文件的格式。M3U播放列表是一个文本文件,它包含了各自独立的行,行以一个LF字符或者LF字符紧跟一个CR字符来结束。行可以是一个URI,空行,或者以字符#开头。空行将会被忽略。空格只能作为一行中不同元素间的分隔。

一个URI 表示一个媒体文件或是变种播放列表文件(见3.2.7)

    URI可以是相对的,一个相对的URI必须被包含该URI的播放列表文件中的URI所解析。

以注释字符#开头的行可能是注释或者标签,标签以#EXT开头,其他所有行都应该被忽略。播放列表文件的持续时间是他所指向的媒体文件的时长的总和。

.M3U8作为文件名后缀或者HTTPContent-Type(RFC2616)为“Application/vnd.apple.mpegurl”的M3U播放列表文件使用UTF-8(RFC3629)编码。以.M3U作为文件名后缀或者HTTPContent-Type为“audio/mpegurl”的M3U播放列表文件使用US-ASCII编码。

播放列表文件名必须以.M3U8为后缀、HTTPContent-Type为“Application/vnd.apple.mpegurl”(如果使用http传输)或者以.M3U为后缀、HTTPContent-Type为“audio/mpegurl”。

扩展的M3U文件格式定义了两种标签:EXTM3U和EXTINF。区分扩展的M3U文件与普通M3U文件的关键在于前者的首行为#EXTM3U。

EXTINF是一个记录标记,该标记描述了后边URI所指定的媒体文件。每个媒体文件URI前边必须有EXTINF标签。格式如下:

#EXTINF: <DURATION>,<TITLE>

DURATION是一个整数,它指定了媒体文件以秒为单位的持续时间,时间应四舍五入到最接近的整数。行内逗号后边的剩余部分是媒体文件的名字,该名字是媒体分片的人眼可读的信息标题。

该文档定义了如下的新标签:EXT-X-TARGETDURATION,EXT-X-MEDIA-SEQUENCE,EXT-X-KEY,EXT-X-PROGRAM-DATE-TIME,EXT-X-ALLOW-CATCH,EXT-X-ENDLIST,EXT-X-STREAM-INF,EXT-X-DISCONTINUITY,EXT-X-VERSION

 

3.2新标签

3.2.1      EXT-X-TARGETDURATION

该标签指定了媒体文件持续时间的最大值,播放文件列表中的媒体文件在EXTINF标签中定义的持续时间必须小于或者等于该标签指定的持续时间。该标签在播放列表文件中必须出现一次,其格式为:
# EXT-X-TARGETDURATION:<s>
S是一个以秒为单位的整数。

3.2.2      EXT-X-MEDIA-SEQUENCE

播放列表文件中每个媒体文件的URI都有一个唯一的序列号。URI的序列号等于它之前那个RUI的序列号加一。EXT-X-MEDIA-SEQUENCE指明了出现在播放列表文件中的第一个URI的序列号。其格式如下:
#EXT-X-MEDIA-SEQUENCE:<Number>
播放列表文件中的EXT-X-MEDIA-SEQUENCE标签不能多于一个。如果播放列表文件中没有EXT-X-MEDIA-SEQUENCE标签,那么将会把播放列表中第一个URI的序列号当成0。
媒体文件的序列号码不是必须出现在它的URI中的。见6.3.2和6.3.5。

3.2.3      EXT-X-KEY

媒体文件可能是被加密的,EXT-X-KEY提供了解密媒体文件的必要信息,它的格式如下:
#EXT-X-KEY:METHOD=<method> [,URI = “<uri>”] [,IV = <iv>]
Method属性指定了加密方法,定义了两种加密方法:NONE和AES-128。
加密方法NONE表示媒体文件不被加密,如果加密方法是NONE,那么URI和IV属性不允许存在。
加密方法AES-128表示媒体文件使用高级加密标准128位密钥和PKCS7 padding加密。如果加密方法是AES-128,那么对于URI属性,如果存在,则指定获取密钥的方法;对于IV属性,如果存在,则指定使用密钥的初始化向量。
IV属性出现在协议版本2中,新的EXT-X-KEY将会取代任何一个先前的EXT-X-KEY。
如果播放列表文件没有包含EXT-X-KEY标签,那么媒体文件将不会被加密。
密钥文件的格式见第五章,媒体文件加密信息见5.2、6.2.3、6.3.6。

3.2.4      EXT-X-PROGRAM-DATE-TIME

EXT-X-PROGRAM-DATE-TIME标签将下一个媒体文件的开头和绝对日期关联起来。日期/时间的表示基于ISO/IEC,并且要指明时区。例如:
#EXT-X-PROGRAM-DATE-TIME:<YYYY–MM–DDThh:mm:ssZ>
详见6.2.1和6.3.3

3.2.5      EXT-X-ALLOW-CATCH

EXT-X-ALLOW-CATCH标签指定客户端可以或者不准缓存下载的媒体文件用来重播。它可能会出现在播放列表文件的任何地方,但是不能出现两次或以上。该标签适用于播放列表中的所有分片。其格式如下:
#EXT-X-ALLOW-CACHE:<YES|NO>
详见6.3.3

3.2.6      EXT-X-ENDLIST

    EXT-X-ENDLIST标签标示没有更多媒体文件将会加入到播放列表中,它可能会出现在播放列表文件的任何地方,但是不能出现两次或以上。其格式如下:

#EXT-X-ENDLIST

3.2.7      EXT-X-STREAM-INF

     EXT-X-STREAM-INF标签表示在播放列表中的下一个URI标识另一个播放列表文件。格式如下:

#EXT-X-STREAM-INF:[attribute=value][,attribute=value]* <URI>

在一个EXT-X-STREAM-INF标签中attribute不能出现两次或以上。其它属性定义:
BANDWIDTH = <n>
n为每秒比特数,它必须是每个媒体文件比特速率的上限,必须经过计算包含那些在播放列表中出现的或者将要出现的容器开销。
PROGRAM-ID=<i>
i是一个数字,在播放列表文件的范围内唯一的标识了一个特定的演示文稿。
    一个播放列表文件可能包含多个具有相同PROGRAM-ID 的EXT-X-STREAM-INF标签来标识某个演示文稿的不同编码。这些变种的的播放列表可能包含额外的EXT-X-STREAM-INF标签。
 
CODECS="[format][,format]*"
 
每一种格式都指定了存在于媒体文件中的媒体类型。合法的格式标示符都是那些在ISO文件格式名称空间被RFC4281定义的格式。
RESOLUTION=<N>x<M>
 
N是流中视频水平编码分辨率的近似,以像素数表示,M是编码垂直分辨率的近似。

3.2.8      EXT-X-DISCONTINUITY

     EXT-X-DISCONTINUITY标签表示该标签后边的媒体文件和之前的媒体文件之间的编码间断。特性可能改变的一组是:
file format
number and type of tracks
encoding parameters
encoding sequence
详见第四章,6.2.1、6.3.3。
 
 

3.2.9      EXT-X-VERSION

EXT-X-VERSION标签指出了播放列表版本的适应性。播放列表文件、其关联的媒体和服务器必须遵守最新版本的所有规定。
 
 

4多媒体文件

每一个媒体文件资源定位符都必须标识一个媒体文件,该文件是整体数据的一个分片。每个媒体文件必须按照MPEG-2的传输流和MPEG-2音频流的格式。[ISO13818]
传输流文件必须包含一个MPEG-2节目。在每个文件的开始应该有一个节目关联表和一个节目映射表。包含视频的文件应该有至少一个密钥帧和足够的信息来完全初始化一个视频解码器。
播放列表中的媒体文件必须是编码流中媒体文件的末尾与先前的序列号的延续,除非它是播放列表中出现的第一个媒体文件,或者它前边有EXT-X-DISCONTINUITY标签。
客户端应该准备好处理一个特定类型(音频或视频等)的多个轨道。一个没有优先级的客户端应该选择它能播放的具有最小数字编号的音轨。
客户端应该忽略那些传输流的内部不能识别的流。
媒体文件内样本流和相应的多媒体流的编码参数应保持一致。然而客户端应该解决编码的变化问题,例如缩放视频内容以适应分辨率改变。

5密钥文件

5.1介绍

    URI属性中EXT-X-KEY标签标识一个密钥文件。密钥文件包含解密播放列表中媒体文件的密钥。AES-128加密算法使用16字节的密钥。密钥文件的格式为16字节的二进制数数组。

5.2  IV FOR AES-128

128位AES在加密和解密的时候需要提供一个相同的16字节的初始化向量(IV),变换IV可以提高密钥的健壮性。
如果EXT-X-KEY标签有IV属性,在使用密钥加密或者解密的时候必须使用此属性值作为IV。这个值必须被解释为128位的16进制数,而且必须有前缀0x。
    如果EXT-X-KEY标签没有IV属性,在加密或者解密媒体文件的时候必须使用序列号作为IV值。大端二进制表示的序列号应该放置在16字节的缓冲区中且左边补0。

6客户端/服务器行为

6.1介绍

本章介绍服务器怎样产生播放列表和媒体文件以及客户端怎样下载并播放。

6.2服务器进程

6.2.1介绍

MPEG-2数据流的产生超过了本文档的范围,本文档仅仅假设有一个数据流连续的源。
服务器必须将数据流分割成持续时间大致相等的媒体文件,服务器应该尝试点分割流来支持对个别媒体文件的有效解码,例如包和关键帧的边界。
服务器必须为媒体文件创建URI,允许它的客户端能够获取到文件。
服务器必须创建播放列表。播放列表必须符合第三章描述的格式。服务器要提供的媒体文件的URI必须按顺序出现在播放列表中。如果URI出现在了播放列表中,那么这个媒体文件对于客户端必须是可用的。
播放列表文件必须包含一个EXT-X-TARGRTDURATION标签,它必须指明添加到播放列表中媒体文件的最大EXTINF值。整个演示文稿期间,这个值必须保持不变。典型持续时间为10s。
播放列表文件应该包含EXT-X-VERSION标签来说明流对于版本的兼容性。它的值应该是服务器、播放列表文件和其所关联的媒体文件都能执行的最低协议版本。
如果播放列表文件通过HTTP传输,那么服务器应该支持客户端请求使用gzip内容编码。
从客户端的角度来看,播放列表文件的变更必须是自动的。
服务器不可以改变EXT-X-ALLOW-CATCH的值。
播放列表中每个媒体文件的URI必须以EXTINF作为前缀来说明媒体文件的持续时间。
服务器可以将媒体文件和绝对的日期和时间关联起来,只要在它的URI前缀上一个EXT-X-PROGRAM-DATE-TIME标签。日期和时间的值提供了一个媒体时间表到挂钟时间的信息映射,该挂钟时间可以作为搜索、显示或其他目的的基准。
如果服务器提供了这个映射,那么它应该在每个EXT-X-DISCONTINUITY标签的后边加一个EXT-X-PROGRAM-DATE-TIME标签。
如果播放列表文件包含演示文稿的最后一个分片,那么应该加一个EXT-X-ENDLIST标签。
如果播放列表文件没有包含EXT-X-ENDLIST标签,那么服务器应该使一个新版本的播放列表文件可用,并至少包含一个媒体文件的URI。新的播放列表文件必须与前一个播放列表文件在相对的时间内有效:从上一个播放列表文件开始有效的时间算起,不早于0.5倍持续时间,不晚于1.5倍持续时间。//不太清楚可用是什么意思?
如果服务器期望移除演示文稿,它必须使播放列表文件对于客户端不可用,在播放列表被清除时,它应该确保播放列表文件中的所有媒体文件对于客户端来说至少在一个播放列表文件持续时间内是可用的。

6.2.2滑动窗口播放列表

服务器可以限制最近一段时间添加到播放列表文件中的媒体文件的可用性,为了达到这个目的,播放列表文件必须包含准确的EXT-X-MEDIA-SEQUENCE标签。标签的值是按照从播放列表中移除的媒体文件的URI递增的。
媒体文件的URI必须按照其加入的顺序移除。当服务器从播放列表移除URI时,媒体文件在一段时间内必须保持可用,该时间等于媒体文件的时间加上包含该媒体文件的最长播放列表文件的时间。
当媒体文件通过http传输给客户端后,如果服务器打算移除该文件,那么它应该确保http响应头包含反应生存时间的过期头。
那些不包含EXT-X-ENDLIST标签的播放列表文件的持续时间必须至少三倍于targrtdutration。//为什么是三倍?

6.2.3加密媒体文件

如果媒体文件需要被加密,那么服务器必须定义一个URI来允许被授权的客户端获取包含解密密钥的密钥文件。密钥文件必须符合第五章描述的格式。服务器可以在密钥响应中设置超时头来表名密钥可以被缓存。
如果采用AES-128加密算法,那么AES-128 CBC加密模式应该适应于每一个媒体文件。整个文件必须是加密的。密码块的连接不能用于跨媒体文件。用于解密的初始化向量必须是媒体文件的序列号或者EXT-X-KEY标签的IV属性的值。服务器必须使用这种加密算法和其他由紧随在播放列表文件中URI后边的EXT-X-KEY标签所指定的属性来加密播放列表文件中的每一个媒体文件。EXT-X-KEY标签中方法为none或者没有EXT-X-KEY标签的媒体文件不能被加密。
    如果播放列表文件包含了一个经过加密的媒体文件的URI,那么服务器不可以将EXT-X-KEY标签从播放列表文件中移除。

6.2.4提供变种数据流

服务器可以提供多个播放列表文件来支持对同一个演示文稿的不同编码。提供变种播放列表文件列出每一个变种流,从而使得客户端可以在不同编码之间动态切换。
变种播放列表文件必须为每一个变种流包含一个EXT-X-STREAM-INF标签。同一演示文稿的每个EXT-X-STREAM-INF都必须有相同的programid。每个演示文稿的programid在变种播放列表内必须是唯一的。
如果EXT-X-STREAM-INF标签包含CODECS属性,则属性值必须包含RFC4281定义的所有格式,
 
服务器在生成变种流的时候必须遵守以下规则:
1)每一个变种流必须呈现相同的内容,包括流的间断性。
2)每个变种播放列表文件必须有相同的target duration。
3)只在个别变种播放列表文件中出现的内容必须放在列表文件的头或者尾,且不能超过target duration。
4)变种流内匹配内容,必须有匹配时间戳。这可以使客户端同步流。
5)基本音频流文件必须在文件中第一个样本的采样信号的时间戳前预先准备一个ID3 PRIV标签,标签的所有者标示符为“com.apple.streaming.transportStreamTimestamp”。二进制数据必须是33位的基本时间戳,用8字节的数字表示。
 
另外,所有的变种流都应该包含相同编码的音频二进制流。这使得客户端在不同的流之间切换时没有毛刺声音。//什么事毛刺声音?

6.3客户端进程

6.3.1介绍

客户端怎样获取播放列表中的URI不在本文档的范围之内,我们假设已经获取到了URI。

6.3.2加载播放列表文件

每一次加载或者重载播放列表文件时:
客户端必须保证播放列表文件以EXTM3U标签开头,并且如果协议版本号存在,客户端必须支持该版本。否则,客户端不可以试图使用该列表文件。
客户端可以忽略它不能识别的标签和属性。
如果播放列表文件包含了EXT-X-MEDIA-SEQUENCE标签,那么客户端会假设在播放列表被加载的时间内以及播放列表的持续时间内媒体文件将变得不可用。播放列表的持续时间等于其中包含的媒体文件时长的总和。//为啥假设不可用?

6.3.3播放播放列表文件

当开始播放的时候,客户端首先从播放列表中选择要播放的媒体文件。如果不存在EXT-X-ENDLIST标签,并且客户端想正常播放媒体(按顺序以标准速率播放),那么客户端就不应该从播放列表文件尾部选择少于三个target duration的媒体文件。
为了达到正常播放的目的,媒体文件必须按照他们在播放列表中的顺序播放。客户端还可以用其他任何方式播放,比如顺序播放,随机播放,特效播放等。
对于存在EXT-X-DISCONTINUITY标签的媒体文件,在播放之前客户端必须准备好重置分析和解码器。
为了不间断播放,应该提前载入媒体文件,以补偿延时和吞吐量的变化。
如果播放列表文件包含了EXT-X-ALLOW-CATCH标签,并且它的值为NO,那么客户端在播放以后不可以缓存媒体文件。否则允许缓存用来以后重播。
客户端可以使用EXT-X-PROGRAM-DATE-TIME标签来为用户显示节目的起始时间。如果这个值包含了时区信息,那么客户端应该考虑到这点;如果不包含,那么客户端不可以推测时区。
客户端不能依靠EXT-X-ALLOW-CATCH标签值的正确性和一致性。

6.3.4重新载入播放列表文件

客户端必须阶段性的重新载入播放列表文件,除非文件包含了EXT-X-ENDLIST标签。然而也不能过于频繁的载入。
当客户端第一次载入播放列表文件或者已经载入但是发现文件与上次载入的时候有了变化,客户端都必须等待一段时间在可以再次载入。这段时间被称为原始最小重载延迟,它是从客户端开始载入一个播放列表文件开始计算的。
原始最小重载延迟是播放列表文件中最后一个媒体文件的持续时间。媒体文件的持续时间由EXTINF标签来指定。
如果客户端重载了一个播放列表文件,但是发现文件并没有变化,那么它在重试之前必须等一段时间。最小延迟是target duration的倍数。第一次是0.5倍,第二次1.5倍,3倍。。。

6.3.5确定下一个要加载的文件

当播放列表文件被载入或者重载以后,客户端必须检查播放列表来确定要载入的媒体文件。要载入的第一个文件必须是客户端要播放的第一个文件,见6.3.3。
    如果要播放的文件已经被载入,并且播放列表文件不包含EXT-X-MEDIA-SEQUENCE标签,那么客户端必须确认播放列表文件包含了最后一个被载入的媒体文件的URI,如果不包含,则暂停播放。要载入的下一个媒体文件必须是上一次载入的媒体文件URI之后的第一个媒体文件的URI。
    如果要播放的文件已经被载入,并且播放列表文件包含EXT-X-MEDIA-SEQUENCE标签,那么要载入的下一个媒体文件就是比上一次载入的文件的序列号大的媒体文件中的序列号最小者。

6.3.6解密经加密的媒体文件

如果播放列表文件包含了一个指定密钥文件URI的EXT-X-KEY标签,客户端必须获取密钥文件,并使用其中的密钥来解密KEY标签之后的所有媒体文件,直到遇到另一个EXT-X-KEY标签为止。

7协议版本的兼容性

客户端和服务器必须使用版本2以及更高版本。
 
 

8例子

8.1简单的播放列表文件

#EXTM3U

#EXT-X-TARGETDURATION:5220

#EXTINF:5220,

http://media.example.com/entire.ts

#EXT-X-ENDLIST

 

8.2滑动窗口播放列表,使用https

#EXTM3U

#EXT-X-TARGETDURATION:8

#EXT-X-MEDIA-SEQUENCE:2680

#EXTINF:8,

https://priv.example.com/fileSequence2680.ts

#EXTINF:8,

https://priv.example.com/fileSequence2681.ts

#EXTINF:8,

https://priv.example.com/fileSequence2682.ts
 

8.3加密的媒体文件与播放列表文件

#EXTM3U

#EXT-X-MEDIA-SEQUENCE:7794

#EXT-X-TARGETDURATION:15

#EXT-X-KEY:METHOD=AES-128,URI=”https://priv.example.com/key.php?r=52″

#EXTINF:15,

http://media.example.com/fileSequence52-1.ts

#EXTINF:15,

http://media.example.com/fileSequence52-2.ts

#EXTINF:15,

http://media.example.com/fileSequence52-3.ts

#EXT-X-KEY:METHOD=AES-128,URI=”https://priv.example.com/key.php?r=53″

#EXTINF:15,

http://media.example.com/fileSequence53-1.ts

变种的播放列表文件

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000

http://example.com/low.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000

http://example.com/mid.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000

http://example.com/hi.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS=”mp4a.40.5″

http://example.com/audio-only.m3u8

ffmpeg对mp4视频进行TS切片及m3u8索引文件支持hls

要想利用HLS来实现视频的在线播放,就得需要将一个完整的视频文件切割成多个ts视频流,然后利用m3u8的索引文件来播放。基本是利用开源的ffmpeg对mp4视频进行TS切片及建立m3u8索引文件支持hls,提升播放速度。

1.ffmpeg切片命令,以H264和AAC的形式对视频进行输出

ffmpeg -i input.mp4 -c:v libx264 -c:a aac -strict -2 -f hls output.m3u8

2.ffmpeg转化成HLS时附带的指令 

-hls_time n: 设置每片的长度,默认值为2。单位为秒

-hls_list_size n:设置播放列表保存的最多条目,设置为0会保存有所片信息,默认值为5

-hls_wrap n:设置多少片之后开始覆盖,如果设置为0则不会覆盖,默认值为0.这个选项能够避免在磁盘上存储过多的片,而且能够限制写入磁盘的最多的片的数量

-hls_start_number n:设置播放列表中sequence number的值为number,默认值为0

3.对ffmpeg切片指令的使用

ffmpeg -i output.mp4 -c:v libx264 -c:a aac -strict -2 -f hls -hls_list_size 0 -hls_time 5 output1.m3u8 

将输出的 M3u8 可直接使用vlc打开,发现拖动的时候会出现画面丢失的现象,待解决。

ffmpeg  -i output.mp4 -vcodec h264 -vb 1000k -profile baseline -acodec aac -ac 1 -ar 44100 -ab 24 -hls_list_size 0 -hls_time 10 -f hls output.mp4.m3u8

全民大直播,流媒体选择Nginx是福还是祸?

CDN,视频云,已经“僧多粥少”

视频直播的持续升温,无意间也让带宽生意的争夺变得异常残酷。一时间,各种云计算、CDN、视频云提供商都在视频尤其是直播上投入重兵,揭竿而起的新生起义军们也正马不停蹄的赶往这方战场,各种号称可以在IaaS、PaaS、SaaS不同层面提供平台级、接口级以及产品级服务的花式作战口号此起彼伏,让人眼花缭乱,“僧多粥少”可能成为了当前支撑视频技术解决方案市场最恰当的提法。如此局面之下,视频云和CDN们,技术上到底是在竞争什么?作为视频平台和即将要进入视频领域的运营者,在技术平台的选型和搭建上又如何才能避免掉入大坑?

一个播放器的背后

谁都知道视频直播最重要的是流畅和高清,但这光鲜亮丽的背后是技术和成本的双高门槛,是诸多技术环节艰难积累和苦逼的人肉运维。主播发起一个简单的直播,主干流程就历经了采集、编码、推流、转码、分发、拉流、解码和播放这么多环节,还要求在数秒内完成,除此之外直播还有如录制、流控、安全、审核等等诸多复杂功能需求。

再如下图,仅一个屌丝观众从播放器看这个主播,就可能出现如此多不可知情形发生。这个屌丝的接入网络怎么样?使用的系统环境又怎么样?一个观众尚且如此,要保障百万千万级别流畅的观看,难度可想而知。

高清流畅到底靠的是什么

也许对于部分视频运营商和新进入者来说,直播推流端和播放器端依然觉得头大,但整体来说,除移动端外,PC端推流和播放技术已经比较成熟。难,主要难在传输和分发!正常情况下,只要推流端网络状况良好,传输和分发决定着直播是否能够流畅。

传输和分发,涉及到了视频最核心技术、巨额服务器和带宽成本以及国内网络环境极度错综复杂也因为如此,视频平台基本上都将传输和分发环节交由专业的第三方视频云服务商或CDN服务商来完成。我们从网络传输的七层中拿出与视频传输分发相关的四层,如下图:

L2资源层:对视频云和CDN来说,资源的确存在差别,但在其可承受范围内,可以视为差别不大;

L4传输层:传输层可针对不同业务场景,比如针对超低延迟可以基于UDP做私有协议等。本文侧重阐述视频流畅的保障,不同应用场景的支持后续文章将专门介绍;

L3网络层:视频云和CDN公司在该层实现各运营商网间打通、多层Cache系统设计以及用户就近调度。该层的设计及优化对访问质量极为重要,随着CDN技术的日益成熟,虽然各家可能存在架构区别,但基本都能保障网络路由正常运转;

L7应用层:抛开细枝末节,视频流的主线还是输入、传输与输出,承担这些工作的就是视频平台最核心组件流媒体服务器,这就是视频直播分发最本质的特点,需要专门的流媒体服务器来分发,所有视频云和CDN,都需要在中心层和边缘层部署流媒体Server。

 

通过以上逐层分析可知,当资源和网络层面相差不大的情况下,流媒体Server的性能决定了视频流分发的效果和质量,故流媒体Server才是视频云和CDN技术竞争的至高点。



市面主要的流媒体服务器对比

目前市面上主流的流媒体服务器,有以Adobe FMS、Real Helix、Wowza为代表的第一代产品,它们的特点是单进程多线程。基于Linux2.7 epoll技术,出现了以多进程单线程为特点的第二代流媒体服务器,NginxRTMP、Crtmpd为其优秀的代表,另外还有基于JAVA的流媒体祖先Red5等。

观止云开源流媒体服务器SRS(Simple RTMP Server),凭借其功能强大、轻量易用、特别适合互动直播等诸多特点备受海内外视频从业者的青睐。蓝汛Chiancache曾用SRS承载其直播边缘分发业务,高升CDN基于SRS搭建其流媒体基础平台,其它还有赛维安讯、VeryCDN、VeryCloud、云博视等也将SRS应用到了自身的业务当中。各家视频云、云计算平台在源站的对接上也非常注重对SRS的支持。SRS作为纯国产的开源Server,在中国流媒体业界实属难能可贵。

观止云源站集群BMS(Bravo Media Server)是SRS的商业版,BMS在SRS基础上增强了11项大功能,新增了9个大功能

增项的11项大功能:



新增的9项大功能:





流媒体Server的话说来也不短,上述列举的目前市面上主流流媒体服务器中,有名副其实的先烈RED5,有生不逢时的CRTMPD,都未大规模商用就不过于讨论了。其中应用最为广泛莫属nginx-rtmp,以下是nginx-rtmp几个盛行于世的重要因素:

  • 2012年CDN业务开始极增长,随之直播需求也多了起来,彼时业界都还没有一套公认的特别满意的流媒体服务器;

  • Nginx是HTTP领域绝对的霸主,大家(尤其是CDN运维)对Nginx熟悉程度很高,便于上手维护;

  • 基于Nginx,直播点播使用一套服务器,这也极具诱惑力,一套管理起来总比多套要简单;

  • CDN是靠运维的行当,运维的信心都是长年运出来的,Nginx在图文上那么优秀,Nginx RTMP也差不了。



nginx-rtmp确实生来就自带光环外,性能也的确是高,比Crtmpd还要高。然而,时过境迁,随着互动直播、移动直播的强势兴起的大直播时代,选择nginx-rtmp到底是福还是祸?

下面小编将从协议支持、体系架构、核心功能支持、配置运维、性能、服务器日志、数据这七大维度将目前市面主流的流媒体Server做一个横向对比,供视频从业者根据自身业务场景特性择优选用。



1
网络协议对比

BMS支持HDS、DASH、RTMPE/S/T等协议的分发,这将支持更多业务应用场景,FLASH P2P的支持能够显著降低网络带宽成本。



2
体系架构对比

架构方面,较之于nginx-rtmp的16万行代码,SRS仅用了6.5万行代码就实现了比nginx-rtmp 多了230%的功能nginx-rtmp注释率为3%,而SRS是23.7%。由此可见SRS在体系架构上的轻,Simple。

观止云BMS在SRS的基础上新增了多进程支持、源站集群、动态配置、可追溯日志等方面能力。源站集群子系统打通了跨网跨地区的源站分布式部署难题;动态配置子系统从业务系统读取配置,依据更新机制动态更新配置,保证直播业务配置变化时依然不中断;端到端的可追溯日志及监控排错子系统将直播故障定位时间缩短到了分钟级别。



3
核心功能对比

核心功能方面,BMS支持了当期互动直播、移动直播急需的大规模直播流实时转码、大规模录制、秒级低延迟、HLS+、并发回源等其它所有流媒体系统不具备的功能。HLS+基于每个播放请求实现了流媒体的“虚拟连接 ”(UUID标识),在减小回源量、排错、防盗链、移动Web端低延迟等方面具有诸多优势。并发回源能够解决回源网络状况差、跨国传输丢包严重等方面能够显著提升回源质量。



4
配置运维对比

以下仅是流媒体众多配置之中几个常用例子,运维日常工作中,需要操作的配置数量更多。

(1)vhost配置

FMS

拷贝默认vhost目录:sudo cp -r conf/_defaultRoot_/_defaultVHost_ conf/_defaultRoot_/bravo.sina.com



nginx-rtmp

不支持



SRS/BMS

动态获取配置文件:vhost bravo.sina.com { }

结论:BMS动态获取配置最简单

(2)app配置

 FMS

拷贝默认app目录:cp applications/live applications/mylive -r



nginx-rtmp

修改配置文件,增加如下内容:application live {  live on; }



SRS/BMS

无需配置

结论:BMS无需配置,最简单 

(3)http配置

在输出为hls、http-flv等基于http协议的直播流时,需要配置http服务

FMS

配置FMS内置的Apache服务器文件:Apache2.2/conf/httpd.conf

再修改如下字段:

<Location /hds-live>

    HttpStreamingEnabled true

    HttpStreamingLiveEventPath “../applications” 

    HttpStreamingContentPath “../applications” 

    HttpStreamingF4MMaxAge 2

    HttpStreamingBootstrapMaxAge 2

    HttpStreamingFragMaxAge -1

    Options -Indexes FollowSymLinks

</Location



nginx-rtmp

nginx本身就是一个http服务器,

修改其配置文件:

conf/nginx.conf

设置端口和根目录:

http {

    include       mime.types;

    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    server {

        listen       80;

        server_name  localhost;

        location /dash {

            root /tmp;

            add_header Cache-Control no-cache;

        }

    }

}



SRS/BMS

修改其配置文件:

conf/http.hls.conf

设置端口和根目录:

http_stream {

    enabled         on;

    listen          8080;

    dir             ./objs/nginx/html;

}

结论:nginx-rtmp需指定与app对应的ts文件存放目录,SRS/BMS会自动生成,更简单。

(4)推流、播放URL配置

RTMP直播时,各大服务器推流、播流URL均为:

rtmp://server_ip_or_dns/app/stream



用作HLS直播时,

FMS 

推流域名:

rtmp://fms-ip-or-dns/app/stream?adbe-live-event=liveevent

播流域名:

http://fms-ip-or-dns/hds-live/app/_definst_/liveevent/stream.f4m



nginx-rtmp

推流域名:

rtmp://server_ip_or_dns/app/stream

播流域名:

http://server_ip_or_dns/app/stream.m3u8



SRS/BMS

同nginx-rtmp

结论:nginx-rtmp、SRS/BMS均简单,FMS较复杂。



5
性能

先说结论:

SRS单进程能支持9000并发,nginx-rtmp单进程最多支持3000个,单进程的性能SRS是nginx-rtmp的三倍。单进程性能SRS > nginx-rtmp > crtmpd > wowza > fms > RED5

 

再例举SRS性能如此高的几个原因:

1. st-load,这个是SRS能做到高性能的最重要的原因,一个st-load可以模拟2000+的客户端,如果没有st-load,如何知道系统的性能瓶颈在哪里?总不能打开3000个flash页面播放rtmp流吧?开启3000个ffmpeg来抓流?高性能不是想象和猜测出来的,而是反复测试、调试和改进出来的。

2. gperf/gprof性能,编译SRS时,就可以打开gcp或者gprof的性能分析选项,非常方便的拿到数据。缩短了改进和优化开发周期。

3. 引用计数的msgs避免内存拷贝。

4. 使用writev发送chunked包,避免消息到chunked包的内存拷贝。

5. mw(merged-write)技术,即一次发送多个消息。

6. 减少timeout recv,每个连接都是一个st-thread在服务。

7. fast buffer和cache。

8. vector还是list?vector!vector比list高10%性能。



6
服务器日志

日志是定位故障的唯一途径,定位故障才能快速排错。可以这么说,对于直播,10分钟的排错,谁都会觉得长。然而,当前的视频云或CDN,谁又能做到10分钟呢?

来看看日志吧。

FMS的日志是这样的,恕我愚钝,你能看得出什么信息么?

2015-03-24 12:23:58 3409 (s)2641173 Accepted a connection from IP:192.168.1.141, referrer:http://www.ossrs.net/players/srs_player/release/srs_player.swf?_version=1.23,pageurl: http://www.ossrs.net/players/srs_player.html?vhost=dev&stream=livestream&server=dev&port=1935-

702111234525315439     3130         3448         normal      livestream         –        –         rtmp://192.168.1.185:1935/live/livestream     rtmp://192.168.1.185:1935/live/livestream        –        flv     –        –        0       –        0       0         –        –    http://www.ossrs.net/players/srs_player.html?vhost=dev&stream=livestream&server=dev&port=1935    -1      -1.000000         

crtmpd的日志详细,但我又愚钝,若是上千人在线,你又能看出什么有用的东西么?

/home/winlin/tools/crtmpserver.20130514.794/sources/thelib/src/netio/epoll/iohandlermanager.cpp:120Handlers count changed: 15->16 IOHT_TCP_CARRIER

/home/winlin/tools/crtmpserver.20130514.794/sources/thelib/src/netio/epoll/tcpacceptor.cpp:185Client connected: 192.168.1.141:54823 -> 192.168.1.173:1935

/home/winlin/tools/crtmpserver.20130514.794/sources/applications/appselector/src/rtmpap

利用ffmpeg将MP4文件切成ts和m3u8(苹果官方推荐ffmpeg脚本)

1、将MP4转成m3u8

ffmpeg -i test.mp4 -codec copy -bsf h264_mp4toannexb test.ts

2、将ts转成m3u8

网上很多垃圾文章推荐segmenter工具,但用的时候,3.5G的ts文件丢了一半的数据,于是想到了ffmpeg转。

在国外网站找到命令,一句话搞定,没报半句错:

ffmpeg -i 12生肖.ts -c copy -map 0 -f segment -segment_list playlist.m3u8 -segment_time 10 output%03d.ts

顺便共享给各位国内的同仁,免得深受其苦。毕竟,大家都说HLS代表future,rtsp已经是过去式了。

苹果官方推荐ffmpeg脚本

#!/bin/sh

BR=800k

ffmpeg -i $1 -f mpegts -acodec libmp3lame -ar 48000 -ab 64k -s 320×240 -vcodec libx264 -b $BR -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -subq 5 -trellis 1 -refs 1 -coder0 -me_range 16 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bt 200k -maxrate $BR -bufsize $BR-rc_eq ‘blurCplx^(1-qComp)’ -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30-async 2 sample_$BR_pre.ts

segmenter sample_$BR_pre.ts 10 sample_$BR stream-$BR.m3u8 http://www.ioncannon.net/

rm -f sample_$BR_pre.ts

移动直播技术秒开优化经验(含PPT)

徐立,七牛创始合伙人兼产品副总裁,负责七牛直播云的整体研发,是国内 Go / Docker / Container 技术早期布道者,Go / Containers / Distributed Systems 技术的忠实爱好者和实践者。曾合著国内第一本 Go 语言图书《Go 语言编程》,翻译《Go 语言程序设计》。

现今移动直播技术上的挑战要远远难于传统设备或电脑直播,其完整的处理环节包括但不限于:音视频采集、美颜/滤镜/特效处理、编码、封包、推流、转码、分发、解码/渲染/播放等。

直播常见的问题包括

  • 主播在不稳定的网络环境下如何稳定推流?

  • 偏远地区的观众如何高清流畅观看直播?

  • 直播卡顿时如何智能切换线路?

  • 如何精确度量直播质量指标并实时调整?

  • 移动设备上不同的芯片平台如何高性能编码和渲染视频?

  • 美颜等滤镜特效处理怎么做?

  • 如何实现播放秒开?

  • 如何保障直播持续播放流畅不卡顿?

本次分享将为大家揭开移动直播核心技术的神秘面纱。

视频、直播等基础知识

什么是视频?

首先我们需要理解一个最基本的概念:视频。从感性的角度来看,视频就是一部充满趣味的影片,可以是电影,可以是短片,是一连贯的视觉冲击力表现丰富的画面和音频。但从理性的角度来看,视频是一种有结构的数据,用工程的语言解释,我们可以把视频剖析成如下结构:

内容元素 ( Content )

  • 图像 ( Image )

  • 音频 ( Audio )

  • 元信息 ( Metadata ) 

编码格式 ( Codec )

  • Video : H.264,H.265, …

  • Audio : AAC, HE-AAC, …

容器封装 (Container)

  • MP4,MOV,FLV,RM,RMVB,AVI,…

任何一个视频 Video 文件,从结构上讲,都是这样一种组成方式:

  • 由图像和音频构成最基本的内容元素;

  • 图像经过视频编码压缩格式处理(通常是 H.264);

  • 音频经过音频编码压缩格式处理(例如 AAC);

  • 注明相应的元信息(Metadata);

最后经过一遍容器(Container)封装打包(例如 MP4),构成一个完整的视频文件。

如果觉得难以理解,可以想象成一瓶番茄酱。最外层的瓶子好比这个容器封装(Container),瓶子上注明的原材料和加工厂地等信息好比元信息(Metadata),瓶盖打开(解封装)后,番茄酱本身好比经过压缩处理过后的编码内容,番茄和调料加工成番茄酱的过程就好比编码(Codec),而原材料番茄和调料则好比最原本的内容元素(Content)。

视频的实时传输

简而言之,理性的认知视频的结构后,有助于我们理解视频直播。如果视频是一种“有结构的数据”,那么视频直播无疑是实时传输这种“有结构的数据”(视频)的方式。

那么一个显而易见的问题是:如何实时(Real-Time)传输这种“有结构的数据”(视频)呢?

这里边一个悖论是:一个经过容器(Container)封装后的视频,一定是不可变的 ( Immutable ) 视频文件,不可变的 ( Immutable ) 的视频文件已经是一个生产结果,根据“相对论”,而这个生产结果显然不可能精确到实时的程度,它已经是一段时空的记忆。

因此视频直播,一定是一个 “边生产,边传输,边消费”的过程。这意味着,我们需要更近一步了解视频从原始的内容元素 ( 图像和音频 ) 到成品 ( 视频文件 ) 之前的中间过程 ( 编码 )。

视频编码压缩

不妨让我们来深入浅出理解视频编码压缩技术。

为了便于视频内容的存储和传输,通常需要减少视频内容的体积,也就是需要将原始的内容元素(图像和音频)经过压缩,压缩算法也简称编码格式。例如视频里边的原始图像数据会采用 H.264 编码格式进行压缩,音频采样数据会采用 AAC 编码格式进行压缩。

视频内容经过编码压缩后,确实有利于存储和传输; 不过当要观看播放时,相应地也需要解码过程。因此编码和解码之间,显然需要约定一种编码器和解码器都可以理解的约定。就视频图像编码和解码而言,这种约定很简单:

编码器将多张图像进行编码后生产成一段一段的 GOP ( Group of Pictures ) , 解码器在播放时则是读取一段一段的 GOP 进行解码后读取画面再渲染显示。

GOP ( Group of Pictures ) 是一组连续的画面,由一张 I 帧和数张 B / P 帧组成,是视频图像编码器和解码器存取的基本单位,它的排列顺序将会一直重复到影像结束。

I 帧是内部编码帧(也称为关键帧),P 帧是前向预测帧(前向参考帧),B 帧是双向内插帧(双向参考帧)。简单地讲,I 帧是一个完整的画面,而 P 帧和 B 帧记录的是相对于 I 帧的变化。

如果没有 I 帧,P 帧和 B 帧就无法解码。

小结一下,一个视频 ( Video ) ,其图像部分的数据是一组 GOP 的集合, 而单个 GOP 则是一组 I / P / B 帧图像的集合。

在这样的一种几何关系中,Video 好比一个 “物体”,GOP 好比 “分子”,I / P / B 帧的图像则好比 “原子”。

想象一下,如果我们把传输一个 “物体”,改成传输一个一个的 “原子”,将最小颗粒以光速传送,那么以人的生物肉眼来感知,将是一种怎样的体验?

什么是视频直播?

不难脑洞大开一下,直播就是这样的一种体验。视频直播技术,就是将视频内容的最小颗粒 ( I / P / B 帧,…),基于时间序列,以光速进行传送的一种技术。

简而言之,直播就是将每一帧数据 ( Video / Audio / Data Frame ),打上时序标签 ( Timestamp ) 后进行流式传输的过程。发送端源源不断的采集音视频数据,经过编码、封包、推流,再经过中继分发网络进行扩散传播,播放端再源源不断地下载数据并按时序进行解码播放。如此就实现了 “边生产、边传输、边消费” 的直播过程。

理解以上两个关于 视频 和 直播 两个基础概念后,接下来我们就可以一窥直播的业务逻辑了。

直播的业务逻辑

如下是一个最精简的一对多直播业务模型,以及各个层级之间的协议。

各协议差异对比如下

以上就是关于直播技术的一些基础概念。下面我们进一步了解下影响人们视觉体验的直播性能指标。

影响视觉体验的直播性能指标

直播第一个性能指标是延迟,延迟是数据从信息源发送到目的地所需的时间。

根据爱因斯坦的狭义相对论,光速是所有能量、物质和信息运动所能达到的最高速度,这个结论给传播速度设定了上限。因此,即便我们肉眼感觉到的实时,实际上也是有一定的延迟。

由于 RTMP/HLS 是基于 TCP 之上的应用层协议,TCP 三次握手,四次挥手,慢启动过程中的每一次往返来回,都会加上一次往返耗时 ( RTT ),这些交互过程都会增加延迟。

其次根据 TCP 丢包重传特性,网络抖动可能导致丢包重传,也会间接导致延迟加大。

一个完整的直播过程,包括但不限于以下环节:采集、处理、编码、封包、推流、传输、转码、分发、拉流、解码、播放。从推流到播放,再经过中间转发环节,延迟越低,则用户体验越好。

第二个直播性能指标卡顿,是指视频播放过程中出现画面滞帧,让人们明显感觉到“卡”。单位时间内的播放卡顿次数统计称之为卡顿率

造成卡顿的因素有可能是推流端发送数据中断,也有可能是公网传输拥塞或网络抖动异常,也有可能是终端设备的解码性能太差。卡顿频次越少或没有,则说明用户体验越好。

第三个直播性能指标首屏耗时,指第一次点击播放后,肉眼看到画面所等待的时间。技术上指播放器解码第一帧渲染显示画面所花的耗时。通常说的 “秒开”,指点击播放后,一秒内即可看到播放画面。首屏打开越快,说明用户体验越好。

如上三个直播性能指标,分别对应一个低延迟、高清流畅、极速秒开 的用户体验诉求。了解这三个性能指标,对优化移动直播 APP 的用户体验至关重要。

那么移动直播场景下具体而言有哪些常见的坑呢?

根据实践总结下来的经验,移动平台上视频直播的坑主要可以总结为两方面:设备差异,以及网络环境这些场景下带来的技术考验。

移动直播场景的坑与规避措施

不同芯片平台上的编码差异

iOS 平台上无论硬编还是软编,由于是 Apple 一家公司出厂,几乎不存在因为芯片平台不同而导致的编码差异。

然而,在 Android 平台上,Android Framework SDK 提供的 MediaCodec 编码器,在不同的芯片平台上,差异表现很大, 不同的厂家使用不同的芯片,而不同的芯片平台上 Android MediaCodec 表现略有差异,通常实现全平台兼容的成本不低。

另外就是 Android MediaCodec 硬编层面的 H.264 编码画质参数是固定的 baseline,所以画质通常也一般。因此,在 Android 平台下,推荐是用软编,好处是画质可调控,兼容性也更好

低端设备如何上高性能地采集和编码?

例如 Camera 采集输出的可能是图片,一张图的体积并不会小,如果采集的频次很高,编码的帧率很高,每张图都经过编码器,那么编码器又可能会出现过载。

这个时候,可以考虑在编码前,不影响画质的前提下(前面我们讲过帧率的微观意义),进行选择性丢帧,以此降低编码环节的功耗开销。

弱网下如何保障高清流畅推流

移动网络下,通常容易遇到网络不稳定,连接被重置,断线重连,一方面频繁重连,建立连接需要开销。另一方面尤其是发生 GPRS / 2G / 3G / 4G 切换时,带宽可能出现瓶颈。当带宽不够,帧率较高/码率较高的内容较难发送出去,这个时候就需要可变码率支持。

即在推流端,可检测网络状态和简单测速,动态来切换码率,以保障网络切换时的推流流畅。

其次编码、封包、推流 这一部分的逻辑也可以做微调,可以尝试选择性丢帧,比如优先丢视频参考帧(不丢 I 帧和音频帧 ),这样也可以减少要传输的数据内容,但同时又达到了不影响画质和版视听流畅的目的。

需要区分直播流的状态和业务状态

直播是媒体流、APP 的交互是 API 信令流,两者的状态不能混为一谈。尤其是不能基于 APP 的交互的 API 状态来判断直播流的状态。

以上是移动直播场景下常见的几个坑和规避措施。

移动直播场景其他优化措施

一、怎么优化打开速度,达到传说中的 “秒开”?

大家可能会看到,市面上某些手机直播 APP 的打开速度非常快,一点就开。而某些手机直播 APP,点击播放后要等好几秒以后才能播放。是什么原因导致如此的天壤之别呢?

大部分播放器都是拿到一个完成的 GOP 后才能解码播放,基于 FFmpeg 移植的播放器甚至需要等待音画时间戳同步后才能播放(如果一个直播里边没有音频只有视频相当于要等待音频超时后才能播放画面)。

“秒开”可以从以下几个方面考虑:

1. 改写播放器逻辑让播放器拿到第一个关键帧后就给予显示。

GOP 的第一帧通常都是关键帧,由于加载的数据较少,可以达到 “首帧秒开”。

如果直播服务器支持 GOP 缓存,意味着播放器在和服务器建立连接后可立即拿到数据,从而省却跨地域和跨运营商的回源传输时间。

GOP 体现了关键帧的周期,也就是两个关键帧之间的距离,即一个帧组的最大帧数。假设一个视频的恒定帧率是 24fps(即1秒24帧图像),关键帧周期为 2s,那么一个 GOP 就是 48 张图像。一般而言,每一秒视频至少需要使用一个关键帧。

增加关键帧个数可改善画质(GOP 通常为 FPS 的倍数),但是同时增加了带宽和网络负载。这意味着,客户端播放器下载一个 GOP,毕竟该 GOP 存在一定的数据体积,如果播放端网络不佳,有可能不是能够快速在秒级以内下载完该 GOP,进而影响观感体验。

如果不能更改播放器行为逻辑为首帧秒开,直播服务器也可以做一些取巧处理,比如从缓存 GOP 改成缓存双关键帧(减少图像数量),这样可以极大程度地减少播放器加载 GOP 要传输的内容体积。

2. 在 APP 业务逻辑层面方面优化。

比如提前做好 DNS 解析(省却几十毫秒),和提前做好测速选线(择取最优线路)。经过这样的预处理后,在点击播放按钮时,将极大提高下载性能。

一方面,可以围绕传输层面做性能优化;另一方面,可以围绕客户播放行为做业务逻辑优化。两者可以有效的互为补充,作为秒开的优化空间。

二、美颜等滤镜如何处理?

在手机直播场景下,这就是一个刚需。没有美颜功能的手机直播 APP,主播基本不爱用。可以在采集画面后,将数据送给编码器之前,将数据源回调给滤镜处理程序,原始数据经过滤镜处理完后,再送回给编码器进行编码即可。

除了移动端可以做体验优化之外,直播流媒体服务端架构也可以降低延迟。例如收流服务器主动推送 GOP 至边缘节点,边缘节点缓存 GOP,播放端则可以快速加载,减少回源延迟。

其次,可以贴近终端就近处理和分发

三、如何保障直播持续播放流畅不卡顿?

“秒开”解决的是直播首次加载的播放体验,如何保障直播持续播放过程中的画面和声音视听流畅呢?因为,一个直播毕竟不是一个 HTTP 一样的一次性请求,而是一个 Socket 层面的长连接维持,直到直到主播主动终止推流。

上述我们讲过卡顿的定义:即播放时画面滞帧,触发了人们的视觉感受。在不考虑终端设备性能差异的情况下,针对网络传输层面的原因,我们看看如何保障一个持续的直播不卡顿。

这其实是一个直播过程中传输网络不可靠时的容错问题。例如,播放端临时断网了,但又快速恢复了,针对这种场景,播放端如果不做容错处理,很难不出现黑屏或是重新加载播放的现象。

为了容忍这种网络错误,并达到让终端用户无感知,客户端播放器可以考虑构建一个FIFO(先进先出)的缓冲队列,解码器从播放缓存队列读取数据,缓存队列从直播服务器源源不断的下载数据。通常,缓存队列的容量是以时间为单位(比如3s),在播放端网络不可靠时,客户端缓存区可以起到“断网无感”的过渡作用。

显然,这只是一个“缓兵之计”,如果直播服务器边缘节点出现故障,而此时客户端播放器又是长连接,在无法收到对端的连接断开信号,客户端的缓冲区容量再大也不管用了,这个时候就需要结合客户端业务逻辑来做调度。

重要的是客户端结合服务端,可以做精准调度。在初始化直播推流之前,例如基于 IP 地理位置和运营商的精确调度,分配线路质量最优的边缘接入节点。在直播推流的过程中,可以实时监测帧率反馈等质量数据,基于直播流的质量动态调整线路。

Q&A 

1. 关键帧设置频率一般是多少?有没有根据接入动态设置?过长首屏秒会很难做到。

徐立:关键帧间隔越长,也就是 GOP 越长,理论上画面越高清。但是生成 HLS 直播时,最小切割粒度也是一个 GOP,所以针对交互直播,通常不建议 GOP 设置太长。直播一般 2 个关键帧间隔即可。比如帧率是 24fps, 那么 2 个关键帧的间隔就是 48fps ,这个 GOP 就是2s。

2. 七牛这个直播是用的网宿加速?有遇到什么坑没?

徐立:七牛在直播方面主要是自建节点,也支持融合众多第三方 CDN 服务商,多样化的线路组合为客户提供更优质的服务。在和第三方 CDN 合作的过程中遇到的问题等有机会再做更细粒度的交流和分享。

3. RTMP 直播流除了优化线路外,还有什么加速手段吗?

徐立:物理上优化线路,逻辑上优化策略,比如选择性丢帧,不影响编码画质的前提下减轻传输体积。

4. OBS 推流,播放端 HLS 出现视/音频不同步是哪个环节的问题?怎么优化?

徐立:有可能是采集端的问题,如果是采集端编码环节就出现音画不同步,可以在收流服务器上做音画时间戳同步,这样是全局的校对。如果是播放端解码性能问题,那么需要调节播放逻辑,比如保证音画时间戳强一致性的前提下,选择性丢一部帧。

5. PPT 前几页中一个概念好像错了,I 帧不是关键帧,IDR 帧才是。IDR 帧是 I 帧,但是 I 帧不一定是 IDR 帧。只有 IDR 帧才是可重入的。

徐立:中文都把 I 帧翻译成关键帧了,不过既然提到了 IDR 帧,可以展开说明一下。所有的 IDR 帧都是 I 帧,但是并不是所有 I 帧都是 IDR 帧,IDR 帧是 I 帧的子集。I 帧严格定义是帧内编码帧,由于是一个全帧压缩编码帧,通常用 I 帧表示 “关键帧”。IDR 是基于 I 帧的一个 “扩展”,带了控制逻辑,IDR 图像都是 I 帧图像,当解码器解码到 IDR 图像时,会立即将参考帧队列清空,将已解码的数据全部输出或抛弃。重新查找参数集,开始一个新的序列。这样如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR 图像之后的图像永远不会使用 IDR 之前的图像的数据来解码。

6. 有没有调研过 nginx rtmp module,为什么没有用,对它有什么评价?

徐立:有调研过,nginx_rtmp_module 是单进程多线程,非 go 这种轻量级线程/协程用并发自然语义的方式编写流业务。nginx 原本的代码量较大(约 16 万行,但和直播业务相关的功能并不是很多)。且主要靠写 nginx.conf 做配置租户,通常单租户可以,但业务可扩展性方面不是很灵活,可满足基本需求,不满足高级功能。

7. 用到了那些开源软件?编码用的是 x264 吗?直播服务器你们自己开发还是开源的?

徐立:直播服务器用 go 开发的,移动端编码优先硬编,软编用 x264

8. 请教一下用 OBS 推流到 nginx_rtmp_module 的时候是已经做了视频压缩了还是需要基于 OBS 再开发?

徐立:OBS 把编码压缩都做了,不需要再开发。

9. 视频直播想在 HLS 流中无缝插入一段广告的 ts 文件,有问题想请教一下:1、这段 ts 的分辨率是否一定要和之前的视频流一致?2、pts 时间戳是否要和上一个 ts 递增?

徐立:1、可以不一致。这种情况两段视频完全是独立状态,可以没有任何关系,只需要插入 discontinue 标记,播放器在识别到这个标记之后重置解码器参数就可以无缝播放,画面会很平滑的切换。2、不需要递增。举个例子,视频 A 正在直播,播放到 pts 在 5s 的时候,插入一个视频 B,需要先插入一个 discontinue,再插入 B,等 B 播放完之后,再插入一个 discontinue,再插入 A,这个时候 A 的 pts 可以和之前递增,也可以按照中间插入的 B 的时长做偏移,一般做点播和时移的时候 pts 会连续递增,直播的话会算上 B 的时长。

PPT 下载地址