流媒体协议HLS解析

参考资料:https://www.cnblogs.com/jimodetiantang/p/9133564.html
     https://cloud.tencent.com/developer/article/1032541
     https://blog.csdn.net/yuan1125/article/details/51540918
     https://blog.csdn.net/max_min_go/article/details/39463675
     https://blog.csdn.net/simongyley/article/details/34411577
     https://blog.csdn.net/heyatzw/article/details/76165756
     Android 源码分析之基于NuPlayer的HLS流媒体协议

文章目录

  • 1、综述
    • 1.1 原理介绍
    • 1.2 整体框架
    • 1.3 HLS协议编码格式要求
    • 1.4 HLS相关文章
  • 2、HLS 之 M3U8
    • 2.1 HLS Media Segments
      • 2.1.1 支持的 Media Segment 格式
    • 2.2 HLS Playlists
    • 2.3 Attribute Lists
    • 2.4 Basic Tags
    • 2.5 Media Segment Tags
    • 2.6 Media Playlist Tags
    • 2.7 Master Playlist Tags
    • 2.8 Media or Master Playlist Tags
  • 3、HLS 之 TS
    • 3.1 ts 层:Transport Stream
      • 3.1.1 ts header
      • 3.1.2 adaptation field
      • 3.1.3 PAT(Program Associate Table)格式 节目关联表
      • 3.1.4 PMT(Program Map Table)格式 节目映射表
    • 3.2 pes 层:Packet Elemental Stream
    • 3.3 es 层:Elementary Stream
      • 3.3.1 h.264 视频
      • 3.3.2 aac音频
    • 3.4 ts打包流程图
  • 4、HLS中的内容加密
    • 4.1 HLS概述
    • 4.2 加密方式
    • 4.3 加密机制
  • 5、HLS 播放
    • 5.1 播放未加密的HLS
    • 5.2 播放加密HLS
  • 6、HLS 协议总结
    • 6.1 优点
    • 6.2 缺点
    • 6.3 改进
      • 6.3.1 网宿的 Variant HLS
      • 6.3.2 又拍云的 HLS+
      • 6.3.3 HTTP 302
    • 6.4 HLS 延时分析
  • 7、客户端/服务器行为
    • 7.1 服务器进程
      • 7.1.1介绍
      • 7.1.2 滑动窗口播放列表
      • 7.1.3 加密媒体文件
      • 7.1.4 提供变种数据流
    • 7.2 客户端进程
      • 7.2.1 介绍
      • 7.2.2 加载播放列表文件
      • 7.2.3播放播放列表文件
      • 7.2.4重新载入播放列表文件
      • 7.2.5 确定下一个要加载的文件
      • 7.2.6 解密经加密的媒体文件

1、综述

HLS 全称是 HTTP Live Streaming,是一个由 Apple 公司提出的基于 HTTP 的媒体流传输协议,用于实时音视频流的传输。目前HLS协议被广泛的应用于视频点播和直播领域。

1.1 原理介绍

HLS 跟 DASH 协议的原理非常类似。通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件,然后提供一个配套的媒体列表文件,提供给客户端,让客户端顺序地拉取这些媒体文件播放,来实现看上去是在播放一条流的效果。由于传输层协议只需要标准的 HTTP 协议,HLS 可以方便的透过防火墙或者代理服务器,而且可以很方便的利用 CDN 进行分发加速,并且客户端实现起来也很方便。

HLS 把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。HLS 协议由三部分组成:HTTP、M3U8、TS。这三部分中,HTTP 是传输协议,M3U8 是索引文件,TS 是音视频的媒体信息。

关于 HLS 的详细介绍可参考: https://tools.ietf.org/html/draft-pantos-http-live-streaming-23

在 HTML5 页面上使用 HLS 非常简单:

直接:

<video src="example.m3u8" controls>video>

或者:

<video controls>
    <source src="example.m3u8">source>
video>

HLS 是提供一个 m3u8 地址,Apple 的 Safari 浏览器直接就能打开 m3u8 地址,譬如:http://demo.srs.com/live/livestream.m3u8

Android 不能直接打开,需要使用 html5 的 video 标签,然后在浏览器中打开这个页面即可,譬如:


<video width="640" height="360"
        autoplay controls autobuffer 
        src="http://demo.srs.com/live/livestream.m3u8"
        type="application/vnd.apple.mpegurl">
video>

HLS 的 m3u8,是一个 ts 的列表,也就是告诉浏览器可以播放这些 ts 文件,譬如:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:64
#EXT-X-TARGETDURATION:12
#EXTINF:11.550
livestream-64.ts
#EXTINF:5.250
livestream-65.ts
#EXTINF:7.700
livestream-66.ts
#EXTINF:6.850
livestream-67.ts

有几个关键的参数,这些参数在 SRS 的配置文件中都有配置项:

  • EXT-X-TARGETDURATION:所有切片的最大时长。有些 Apple 设备这个参数不正确会无法播放。SRS 会自动计算出 ts 文件的最大时长,然后更新 m3u8 时会自动更新这个值。用户不必自己配置。
  • EXTINF:ts 切片的实际时长,SRS 提供配置项 hls_fragment,但实际上的 ts 时长还受 gop 影响。
  • ts 文件的数目:SRS 可配置 hls_window,指定 m3u8 中保存多少个切片,SRS 会自动清理旧的切片。
  • livestream-67.ts:SRS 会自动维护 ts 切片的文件名,在编码器重推之后,这个编号会继续增长,保证流的连续性。直到 SRS 重启,这个编号才重置为 0。

譬如,每个 ts 切片为 10 秒,窗口为 60 秒,那么 m3u8 中会保存 6 个 ts 切片。

每一个 .m3u8 文件,分别对应若干个 ts 文件,这些 ts 文件才是真正存放视频的数据,m3u8 文件只是存放了一些 ts 文件的配置信息和相关路径,当视频播放时,.m3u8 是动态改变的,video 标签会解析这个文件,并找到对应的 ts 文件来播放,所以一般为了加快速度,.m3u8 放在 web 服务器上,ts 文件放在 cdn 上。

.m3u8 文件,其实就是以 utf-8 编码的 m3u 文件,这个文件本身不能播放,只是存放了播放信息的文本文件。

1.2 整体框架

HLS的架构分为三部分:Server,CDN,Client 。即服务器、分发组件和客户端。

下面是 HLS 整体架构图:

流媒体协议HLS解析_第1张图片
HLS 框架图

(1) Server

服务器端将视频数据流编码、封装和切割为连续的、时长很短的MPEG-TS格式的文件,通常一个ts分片大概是10s;并提供一个配套的媒体列表文件(m3u8文件)。

视频封装格式:MPEG-TS。
编码:视频编码为H.264,音频编码为AAC, MP3, AC-3或者EC-3格式。

HLS也支持纯音频格式,通常是MPEG基本音频文件(MP4封装的AAC格式)。

(2) Distribution

由标准的网络服务器组成,接收客户端的请求和分发所有的资源包括m3u8列表文件和ts分片文件。

(3) Client

客户端先通过下载m3u8文件,再通过m3u8文件的索引地址顺序地拉取ts媒体文件播放。对于直播,它的索引文件一直处于动态变化的,你需要不断的更新索引文件 playlist 然后移除旧的索引文件。

一般为了加快速度,m3u8 放在 web 服务器上,ts 文件放在 cdn 上。

把视频文件上传到服务器上,视频会被转换成HLS格式的视频(即TS和m3u8文件)。Media encoder模块负责将视频源中的视频数据转码到目标编码格式(H264)的视频数据,然后Stream Segment模块将视频切片,切片的结果就是index file(m3u8)和ts文件了。

流媒体协议HLS解析_第2张图片
HLS index file

HLS的index文件就是m3u8的文件,先下载一级index file(master_playlist.m3u8),它里面记录了二级索引文件的地址(Alternate-A、Alternate-B、Alternate-C)的地址,然后客户端再去下载二级索引文件,二级索引文件中又记录了TS文件的下载地址,这样客户端就可以按顺序下载TS视频文件并连续播放。

1.3 HLS协议编码格式要求

  • 视频的编码格式:H264
  • 音频的编码格式:AAC、MP3、AC-3
  • 视频的封装格式:ts
  • 保存 ts 索引的 m3u8 文件

1.4 HLS相关文章

  • HLS 协议详解
  • Apple Developer—HTTP Live Streaming Overview
  • MPEG-2 Stream Encryption Format for HTTP Live Streaming
  • 流媒体协议—HLS

DEMO:

  • M3U8 golang library
  • HLS downloader:读取一个 m3u8 URL,下载为 TS 文件。
  • https://github.com/selsta/hlsdl

2、HLS 之 M3U8

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

m3u8 文件示例 1:单码率适配流

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-MEDIA-SEQUENCE:2
#EXT-X-TARGETDURATION:16
#EXTINF:14.357, no desc
livestream-2.ts
#EXTINF:15.617, no desc
livestream-3.ts
#EXTINF:14.358, no desc
livestream-4.ts
#EXTINF:15.618, no desc
livestream-5.ts
#EXTINF:11.130, no desc
livestream-6.ts

该 m3u8 文件只是一个简单的 Media Playlist。

m3u8 文件示例 2:多码率适配流

#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-MEDIA:TYPE=AUDIO,GROUP-ID="audio_low",LANGUAGE="en",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="audio_low.m3u8"  
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio_high",LANGUAGE="en",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="audio_high.m3u8"   

#EXT-X-STREAM-INF:BANDWIDTH=100000,CODECS="mp4a.40.2,avc1.4d401e",AUDIO="audio_low"  
video.m3u8   

#EXT-X-STREAM-INF:BANDWIDTH=200000,CODECS="mp4a.40.2,avc1.4d401e",AUDIO="audio_high"  
video.m3u8  

包含多种比特率的 Master Playlist。该文件是一个实际使用中的顶级 m3u8 文件,该文件中又定义了 http://example.com/low.m3u8http://example.com/mid.m3u8等几个二级文件。顶级 m3u8 文件主要是做码率适配的,二级 m3u8 才是真正的切片文件,客户端会默认选择码率最高的请求,如果发现码率达不到,会请求降低码率的流。客户端拿到二级 m3u8 文件后,会继续请求里面的文件,这时就可以进行播放了。

  • HLS 通过 URI(RFC3986) 指向的一个 Playlist 来表示一个媒体流。
  • 一个 Playlist 可以是一个 Media Playlist 或者 Master Playlist,使用 UTF-8 编码的文本文件,包含一些 URI 跟描述性的 tags。
  • 一个 Media Playlist 包含一个 Media Segments 列表,当顺序播放时,能播放整个完整的流。
  • 要想播放这个 Playlist,客户端需要首先下载它,然后播放里面的每一个 Media Segment。
  • 更加复杂的情况是,Playlist 是一个 Master Playlist,包含一个 Variant Stream 集合,通常每个 Variant Stream 里面是同一个流的多个不同版本(如: 分辨率, 码率不同)。

2.1 HLS Media Segments

  • 每一个 Media Segment 通过一个 URI 指定,可能包含一个 byte range。
  • 每一个 Media Segment 的 duration 通过 EXTINF tag 指定。
  • 每一个 Media Segment 有一个唯一的整数 Media Segment Number。
  • 有些媒体格式需要一个 format-specific sequence 来初始化一个 parser,在 Media Segment 被 parse 之前。这个字段叫做 Media Initialization Section,通过 EXT-X-MAP tag 来指定。

2.1.1 支持的 Media Segment 格式

(1)MPEG-2 Transport Streams

  • 即最常见的 TS 文件。
  • RFC: ISO_13818。
  • Media Initialization Section:PAT(Program Association Table) 跟 PMT(Program Map Table)。
  • 每个 TS segment 必须包含一个 MPEG-2 Program。
  • 每一个 TS segment 包含一个 PAT 和 PMT, 最好在 segment 的开始处,或者通过一个 EXT-X-MAP tag 来指定。

(2)Fragmented MPEG-4

  • 即常提到的 fMP4。
  • RFC: ISOBMFF。
  • Media Initialization Section:ftyp box(包含一个高于 ios6 的 brand), moov box 必须紧跟在ftyp box 之后。moov box 必须包含一个 trak box(对于每个 fMP4 segment 里面的 traf box,包含匹配的 track_ID)。每个 trak box 应该包含一个 sample table,但是它的 sample count 必须为 0。mvhd boxtkhd 的 duration 必须为 0。mvex box 必须跟在上一个 trak box 后面。
  • 不像普通的 MP4 文件包含一个 moov box(包含 sample tables)和一个 mdat box(包含对应的 samples),一个 fMP4 包含一个 moof box(包含 sample table 的子集)和一个 mdat box(包含对应的 samples)。
  • 在每一个 fMP4 segment 里面,每一个 traf box 必须包含一个 tfdt box,fMP4 segment 必须使用 movie-fragment relative addressing。fMP4 segments 绝对不能使用外部的 data references。
  • 每一个 fMP4 segment 必须有一个 EXT-X-MAP tag。

(3)Packed Audio

  • 一个 Packed Audio Segment 包含编码的 audio samples 和 ID3 tags。简单的打包到一起,包含最小的 framing,并且没有 per-sample timestamp。
  • 支持的 Packed Audio:AAC with ADTS framing [ISO_13818_7],MP3 [ISO_13818_3],AC-3 [AC_3],Enhanced AC-3 [AC_3]。
  • 一个 Packed Audio Segment 没有 Media Initialization Section。
  • 每一个 Packed Audio Segment 必须在它的第一个 sample 指定 timestamp 通过一个 ID3 PRIV tag。
  • ID3 PRIV owner identifier 必须是 com.apple.streaming.transportStreamTimestamp
  • ID3 payload 必须是一个 33-bit MPEG-2 Program Elementary Stream timestamp 的大端 eight-octet number,高 31 为设置为 0。

(4)WebVTT

  • 一个 WebVTT Segment 是一个 WebVTT 文件的一个 section,WebVTT Segment 包含 subtitles。
  • Media Initialization Section:WebVTT header
  • 每一个 WebVTT Segment 必须有以一个 WebVTT header 开始,或者有一个 EXT-X-MAP tag 来指定。
  • 每一个 WebVTT header 应该有一个 X-TIMESTAMP-MAP 来保证音视频同步。

2.2 HLS Playlists

  • Playlist 文件的格式是起源于 M3U,并且继承两个 tag:EXTM3U 和 EXTINF
  • 下面的 tags 通过 BNF-style 语法来指定。
  • 一个 Playlist 文件必须通过 URI(.m3u8 或 m3u) 或者 HTTP Content-Type 来识别(application/vnd.apple.mpegurl 或 audio/mpegurl)。
  • 一个 m3u8 的 Playlist 就是一个由多个独立行组成的文本文件。
  • 每行由回车/换行区分,换行符可以用 \n 或者 \r\n。
  • 每一行可以是一个 URI、空白行或是一个 以 “#” 号开头的字符串,并且空格只能存在于一行中不同元素间的分隔。
  • 以 # 开头的是 tag 或者注释,以 #EXT 开头的是 tag,其余的为注释,在解析时应该忽略。
  • Playlist 里面的 URI 可以用绝对地址或者相对地址,如果使用相对地址,那么是相对于 Playlist 文件的地址。
  • 一个 URI 表示一个媒体段或是 "variant Playlist file"(最多支持一层嵌套,即一个 m3u8 文件中嵌套另一个 m3u8)。

2.3 Attribute Lists

  • 有的 tags 的值是 Attribute Lists。
  • 一个 Attribute List 是一个用逗号分隔的 attribute/value 对列表。
  • 格式为: AttributeName=AttributeValue

2.4 Basic Tags

Basic Tags 可以用在 Media Playlist 和 Master Playlist 里面。

(1)#EXTM3U

每个 m3u8 文件第一行必须是这个 tag,如上面的两个示例,标识是一个 Extended M3U Playlist 文件。这个标签在媒体播放列表和主播放列表中均应该被包含。形式为:#EXTM3U

(2)#EXT-X-VERSION

本标签标明Playlist播放列表文件兼容的版本。其格式为:#EXT-X-VERSION:

2.5 Media Segment Tags

每一个 Media Segment 通过一系列的 Media Segment tags 跟一个 URI 来指定。有的 Media Segment tags 只应用于下一个 segment,有的则是应用所有下面的 segments。一个 Media Segment tag 只能出现在 Media Playlist 里面。

(3)#EXTINF

指定每个媒体段(ts)的持续时间,这个仅对其后面的 URI 有效,每两个媒体段 URI 间被这个 tag 分隔开。其格式为:#EXTINF:,</code></p> <ul> <li>duration:表示持续的时间(秒),“Durations MUST be integers if the protocol version of the Playlist file is less than 3”,否则可以是浮点数。</li> <li>title:用于对其后文件片添加一些可读的描述信息。</li> </ul> <p><strong><code>(4)#EXT-X-BYTERANGE</code></strong></p> <p>表示媒体段是一个媒体 URI 资源中的一段,只对其后的 media URI 有效,格式为:<code>#EXT-X-BYTERANGE:<n>[@o]</code></p> <ul> <li>n:表示这个区间的大小</li> <li>o:表示在 URI 中的 offset。o为可选项,标明子集的起始位置,相当于从资源开始处计算的偏移量。若o未定义,则其开始位置为上一个子集的结束位置下一个字节。( 当o未定义的时候,其上一个文件资源必须是同一文件的子集,且其不能为文件列表中的第一个文件片。)</li> <li>本标签出现在4以上版本,且不应出现在主播放列表中。</li> </ul> <p><strong><code>(5)#EXT-X-DISCONTINUITY</code></strong></p> <p>标明前后两个文件片编码方式不连续,当遇到该 tag 的时候说明以下属性发生了变化:</p> <ul> <li>file format</li> <li>number and type of tracks</li> <li>encoding parameters</li> <li>encoding sequence</li> <li>timestamp sequence</li> </ul> <p>其不应出现在主播放列表中。实际中还没发现它的用处。</p> <p><strong><code>(6)#EXT-X-KEY</code></strong></p> <p>媒体文件片可能被加密,而本标签表明如何对其进行解密。它作用于其后所有的文件片,直到下一个同名标签(with the same KEYFORMAT)的出现。两个以上的EXT-X-KEY可能会将不同的KEYFORMAT属性作用于同一文件片,它们必须被解析为相同的key。格式为:<code>#EXT-X-KEY:<attribute-list></code></p> <p>以下是一些属性:</p> <p><strong>① METHOD</strong>:枚举值,标明加密方法,必须。</p> <ul> <li>NONE ,未加密,当METHOD取值为NONE时,以下属性不得出现 URI、IV、KEYFORMAT、KEYFORMATVERSION</li> <li>AES-128,使用128位密钥和PKCS7补齐的AES算法。该取值下,URI属性必须出现,IV属性可选。(对于 AES-128 的情况,keytag 和 URI 属性共同表示了一个 key 文件,通过 URI 可以获得这个 key,如果没有 IV(Initialization Vector),则使用序列号作为 IV 进行编解码,将序列号的高位赋到 16 个字节的 buffer 中,左边补 0;如果有 IV,则将该值当成 16 个字节的 16 进制数。)</li> <li>SAMPLE-AES ,标明文件片中包含使用AES-128加密的媒体取样,如audio或video,这些取样的加密和封装方式与媒体文件的编码和文件片类型有关。</li> </ul> <p>客户端遇到无法识别的METHOD,放弃解密。</p> <p><strong>② URI</strong>:引号包含的字符串,其URI指明获取密钥的地址,当METHOD取值为NONE时,该属性为必须。</p> <p><strong>③ IV</strong>:十六进制整数,指明密钥使用的初始向量。</p> <p><strong>④ KEYFORMAT</strong>:引号包含的字符串,指明密钥在URI中的表现形式。该属性可选,当该属性不出现时,其具有一个默认值”identity”。</p> <p><strong>⑤ KEYFORMATVERSION</strong>:引号包含的字符串,其内容为斜杠分隔的整数,如”1/3”,当有多个KEYFORMAT时,指明实例的版本,不出现表示值为1 。</p> <p><strong><code>(7)#EXT-X-MAP</code></strong></p> <p>用于指定 Media Initialization Section。本标签用于说明如何获取用于解析媒体文件片的头部信息,比如传输流 PAT/PMT 或者WebVTT头。它作用于其后出现的所有文件片,直到文件末尾或EXT-X-DISCONTINUITY出现。其格式如下:<code>#EXT-X-MAP:<attribute-list></code></p> <p>选项情况如下:</p> <ul> <li>URI:”string” ,指明包含头部信息的资源的URI,必须的。</li> <li>BYTERANGE:”string”,指明URI资源的一定字节范围,可选的。不填写该属性时,指代URI所指的全部资源。</li> </ul> <p>这个标签的使用情景:当播放列表文件中的第一个文件片在资源的开头部分没有和PAT/PMT紧随,且文件片带有EXT-X-I-FRAMES-ONLY标签。该标签不应出现在主列表文件中。建议EXT-X-MAP标记只用于资源不是以PAT/PMT开头的段。</p> <p><strong><code>(8)#EXT-X-PROGRAM-DATE-TIME</code></strong></p> <p>将一个绝对时间或是日期和一个媒体段中的第一个 sample 相关联一起来确定时间戳,只对下一个 media URI 有效,格式如下:<code>#EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ></code></p> <ul> <li>例如:#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00</li> </ul> <p><strong><code>(9)#EXT-X-DATERANGE</code></strong></p> <p>将一个时间范围和一组属性键值对结合到一起。</p> <h2>2.6 Media Playlist Tags</h2> <p>Media Playlist tags 描述 Media Playlist 的全局参数。同样地,Media Playlist tags 只能出现在 Media Playlist 里面。</p> <p><strong><code>(10)#EXT-X-TARGETDURATION</code></strong></p> <p>指定当前视频流中的单个切片(即 ts)文件的最大时长(秒)。所以 #EXTINF 中指定的时间长度必须小于或是等于这个最大值。这个 tag 在整个 Playlist 文件中只能出现一次(在嵌套的情况下,一般有真正 ts url 的 m3u8 才会出现该 tag,它不可出现在主播放列表中)。格式为:<code>#EXT-X-TARGETDURATION:<s></code></p> <ul> <li>s:表示最大的秒数。</li> </ul> <p><strong><code>(11)#EXT-X-MEDIA-SEQUENCE</code></strong></p> <p>用于指定第一个 Media Segment 的 Media Sequence Number。每一个 media URI 在 Playlist 中只有唯一的序号,相邻之间序号 +1。格式为:<code>#EXT-X-MEDIA-SEQUENCE:<number></code>。一个 media URI 并不是必须要包含的,如果没有,默认为 0。</p> <p>本标签需出现在第一个文件片之前,且不能出现在主播放列表中。当媒体播放列表不包含该标签时,其首个文件片的序列号被视为0 。</p> <p>文件资源定位符中可不出现序列号。</p> <p><strong><code>(12)#EXT-X-DISCONTINUITY-SEQUENCE</code></strong></p> <p>该标签允许多码流不同码流之间同步,并能够使多个流在它们的媒体播放列表文件中加入EXT-X-DISCONTINUITY标签。格式为:<code>#EXT-X-DISCONTINUITY-SEQUENCE:<number></code></p> <p>其中number是一个十进制整数。不连续的文件片序列号必须是递增的。</p> <p>一个媒体播放列表不能包含多于一个EXT-X-DISCONTINUITY-SEQUENCE标签。如果列表文件中不包含该标签,则文件列表中第一个文件片的不连续序列号标记为0。</p> <p>本标签必须出现在第一个文件片之前,且必须出现在EXT-X-DISCONTINUITY标签前,且只能出现在媒体播放文件列表中 。</p> <p>如果媒体列表文件中EXT-X-PLAYLIST-TYPE的值为VOD或者EVENT,则不可使用本标签。</p> <p><strong><code>(13)#EXT-X-ENDLIST</code></strong></p> <p>表示 m3u8 文件的结束,live m3u8 没有该 tag。它可以在 Playlist 中任意位置出现,但是只能出现一个。格式为:<code>#EXT-X-ENDLIST</code></p> <p><strong><code>(14)#EXT-X-PLAYLIST-TYPE</code></strong></p> <p>提供关于 Playlist 的可变性的类型信息,它对整个 Playlist 文件有效,是可选的。格式为:<code>#EXT-X-PLAYLIST-TYPE:<EVENT|VOD></code></p> <ul> <li>VOD,即为点播视频,服务器不能改变 Playlist 文件,换句话说就是该视频全部的 ts 文件已经被生成好了</li> <li>EVENT,就是实时生成 m3u8 和 ts 文件。服务器不能改变或是删除 Playlist 文件中的任何部分,但是可以向该文件中增加新的一行内容。它的索引文件一直处于动态变化中,播放的时候需要不断下载二级 index 文件,以获得最新生成的ts文件播放视频。如果一个二级index文件的末尾没有#EXT-X-ENDLIST标志,说明它是一个Live视频流。</li> </ul> <p><strong><code>(15)#EXT-X-I-FRAMES-ONLY</code></strong></p> <p>本选项标明播放列表文件中每个 Media Segment 都描述了一个单一的 I-frame。本标签作用于整个播放列表文件。其格式为:<code>#EXT-X-I-FRAMES-ONLY</code></p> <p>在拥有该标签的播放列表文件中,文件片的时长是从某一个I帧开始到另外一个I帧的出现或者文件列表末尾。</p> <p>包含关键帧资源的媒体文件必须以传输流PAT/PMT开始,或者与EXT-X-MAP标签一起出现。</p> <p>包含有EXT-X-BYTERANGE标签的I帧分片的字节范围不能包含PAT/PMT。本标签只应该出现在媒体文件列表中。</p> <p><strong><code>(16)#ZEN-TOTAL-DURATION</code></strong></p> <p>表示这个 m3u8 所含 ts 的总时间长度</p> <h2>2.7 Master Playlist Tags</h2> <p>Master Playlist tags 定义 Variant Streams,Renditions 和 其他显示的全局参数。Master Playlist tags 只能出现在 Master Playlist 中。</p> <p><strong><code>(17)#EXT-X-MEDIA</code></strong></p> <p>用于关联同一个内容的多个 Media Playlist 的多种 renditions。即被用来在 Playlist 中表示相同内容的不同语种/译文的版本,比如可以通过使用 3 个这种 tag 表示 3 种不同语音的音频,或者用 2 个这个 tag 表示不同角度的 video。在 Playlist 中,这个标签是独立存在的。其格式为:<code>#EXT-X-MEDIA:<attribute-list></code></p> <ul> <li>该属性列表中包含:URI、TYPE、GROUP-ID、LANGUAGE、NAME、DEFAULT、AUTOSELECT。</li> <li>URI:如果没有,则表示这个 tag 描述的可选择版本在主 PlayList 的 EXT-X-STREAM-INF 中存在;</li> <li>TYPE:AUDIO and VIDEO;</li> <li>GROUP-ID:具有相同 ID 的 MEDIAtag,组成一组样式;</li> <li>LANGUAGE:identifies the primary language used in the rendition。</li> <li>NAME:The value is a quoted-string containing a human-readable description of the rendition. If the LANGUAGE attribute is present then this description SHOULD be in that language。</li> <li>DEFAULT:YES 或是 NO,默认是 NO,如果是 YES,则客户端会以这种选项来播放,除非用户自己进行选择</li> <li>AUTOSELECT:YES 或是 NO,默认是 NO,如果是 YES,则客户端会根据当前播放环境来进行选择(用户没有根据自己偏好进行选择的前提下)</li> <li>The EXT-X-MEDIA tag appeared in version 4 of the protocol。</li> </ul> <p><strong><code>(18)#EXT-X-STREAM-INF</code></strong></p> <p>用于指定一个 Variant Stream。指定一个包含多媒体信息的 media URI 作为 Playlist,一般做 m3u8 的嵌套使用,它只对紧跟后面的 URI 有效。格式为:<code>#EXT-X-STREAM-INF:<attribute-list></code></p> <p>常用的属性如下:</p> <ul> <li>BANDWIDTH:带宽,必须有</li> <li>PROGRAM-ID:该值是一个十进制整数,唯一地标识一个在 Playlist 文件范围内的特定的描述。一个 Playlist 文件中可能包含多个有相同 ID 的此 tag</li> <li>CODECS:指定流的编码类型,不是必须的</li> <li>RESOLUTION:分辨率</li> <li>AUDIO:这个值必须和 AUDIO 类别的 “EXT-X-MEDIA” 标签中 “GROUP-ID” 属性值相匹配</li> <li>VIDEO:同上</li> </ul> <p>该标签不应出现在媒体文件播放列表中。</p> <p><strong><code>(19)#EXT-X-I-FRAME-STREAM-INF</code></strong></p> <p>本标签标明媒体播放列表文件包含多媒体内容的I-frame。本标签单独使用,不依赖于任何资源的URI。其格式为:<code>#EXT-X-I-FRAME-STREAM-INF:<attribute-list></code></p> <p>本标签支持所有EXT-X-I-FRAME-STREAM-INF支持的属性,并额外支持一个URI属性。</p> <p><strong><code>(20)EXT-X-SESSION-DATA</code></strong></p> <p>存放一些 session 数据。</p> <p><strong><code>(21)EXT-X-SESSION-KEY</code></strong></p> <p>用于解密。</p> <h2>2.8 Media or Master Playlist Tags</h2> <p>这里的 tags 可以出现在 Media Playlist 或者 Master Playlist 中。但是如果同时出现在同一个 Master Playlist 和 Media Playlist 中时,必须为相同值。</p> <p><strong><code>(22)#EXT-X-INDEPENDENT-SEGMENTS</code></strong></p> <p>本标签标明,一个分片可以独立解码而不需要其他分片的信息。本选项作用于列表文件中的所有项目。其格式为:<code>#EXT-X-INDEPENDENT-SEGMENTS</code></p> <p>本选项可选,但只能使用一次,当其放置在主列表文件中时,其作用于所有播放列表文件中的每一个文件片。貌似很有用啊。</p> <p><strong><code>(23)EXT-X-START</code></strong></p> <p>标识一个优选的点来播放这个 Playlist。</p> <p><strong><code>(24)#EXT-X-ALLOW-CACHE</code></strong></p> <p>是否允许做 cache,这个可以在 Playlist 文件中任意地方出现,并且最多只出现一次,作用效果是所有的媒体段。格式为:<code>#EXT-X-ALLOW-CACHE:<YES|NO></code></p> <h1>3、HLS 之 TS</h1> <p>来自: hls之m3u8、ts流格式详解</p> <p>ts 文件为传输流文件,视频编码主要格式为 H264/MPEG4,音频为 AAC/MP3。</p> <p>ts 文件分为三层:</p> <ul> <li>ts 层:Transport Stream,是在 pes 层的基础上加入数据流的识别和传输必须的信息。</li> <li>pes 层: Packet Elemental Stream,是在音视频数据上加了时间戳等对数据帧的说明信息。</li> <li>es 层:Elementary Stream,即音视频数据。<br> <a href="http://img.e-com-net.com/image/info8/eeeb37c05b4149318da3691dfac22b25.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/eeeb37c05b4149318da3691dfac22b25.jpg" alt="流媒体协议HLS解析_第3张图片" width="650" height="403" style="border:1px solid black;"></a></li> </ul> <h2>3.1 ts 层:Transport Stream</h2> <p>ts 包大小固定为 188 字节,ts 层分为三个部分:ts header、adaptation field、payload。ts header 固定 4 个字节;adaptation field 可能存在也可能不存在,主要作用是给不足 188 字节的数据做填充;payload 是 pes 数据。</p> <h3>3.1.1 ts header</h3> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(bit)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">sync_byte</td> <td align="left">8b</td> <td align="left">同步字节,固定为0x47</td> </tr> <tr> <td align="left">transport_error_indicator</td> <td align="left">1b</td> <td align="left">传输错误指示符,表明在ts头的adapt域后有一个无用字节,通常都为0,这个字节算在adapt域长度内</td> </tr> <tr> <td align="left">payload_unit_start_indicator</td> <td align="left">1b</td> <td align="left">负载单元起始标示符,一个完整的数据包开始时标记为1</td> </tr> <tr> <td align="left">transport_priority</td> <td align="left">1b</td> <td align="left">传输优先级,0为低优先级,1为高优先级,通常取0</td> </tr> <tr> <td align="left">pid</td> <td align="left">13b</td> <td align="left">pid值</td> </tr> <tr> <td align="left">transport_scrambling_control</td> <td align="left">2b</td> <td align="left">传输加扰控制,00表示未加密</td> </tr> <tr> <td align="left">adaptation_field_control</td> <td align="left">2b</td> <td align="left">是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。</td> </tr> <tr> <td align="left">continuity_counter</td> <td align="left">4b</td> <td align="left">递增计数器,从0-f,起始值不一定取0,但必须是连续的</td> </tr> </tbody> </table> <p>ts 层的内容是通过 PID 值来标识的,主要内容包括:PAT 表、PMT 表、音频流、视频流。解析 ts 流要先找到 PAT 表,只要找到 PAT 就可以找到 PMT,然后就可以找到音视频流了。PAT 表的和 PMT 表需要定期插入 ts 流,因为用户随时可能加入 ts 流,这个间隔比较小,通常每隔几个视频帧就要加入 PAT 和 PMT。PAT 和 PMT 表是必须的,还可以加入其它表如 SDT(业务描述表)等,不过 hls 流只要有 PAT 和 PMT 就可以播放了。</p> <ul> <li>PAT 表:主要的作用就是指明了 PMT 表的 PID 值。</li> <li>PMT 表:主要的作用就是指明了音视频流的 PID 值。</li> <li>音频流/视频流:承载音视频内容。</li> </ul> <h3>3.1.2 adaptation field</h3> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(Byte)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">adaptation_field_length</td> <td align="left">1B</td> <td align="left">自适应域长度,后面的字节数</td> </tr> <tr> <td align="left">flag</td> <td align="left">1B</td> <td align="left">取0x50表示包含PCR或0x40表示不包含PCR</td> </tr> <tr> <td align="left">PCR</td> <td align="left">5B</td> <td align="left">Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。</td> </tr> <tr> <td align="left">stuffing_bytes</td> <td align="left">xB</td> <td align="left">填充字节,取值0xff</td> </tr> </tbody> </table> <p>自适应区的长度要包含传输错误指示符标识的一个字节。pcr是节目时钟参考,pcr、dts、pts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr。如果没有该字段,ipad是可以播放的,但vlc无法播放。打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。</p> <p><a href="http://img.e-com-net.com/image/info8/ec01b4967d584815a93ff549497fd14f.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/ec01b4967d584815a93ff549497fd14f.jpg" alt="流媒体协议HLS解析_第4张图片" width="304" height="204" style="border:1px solid black;"></a></p> <h3>3.1.3 PAT(Program Associate Table)格式 节目关联表</h3> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(bit)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">table_id</td> <td align="left">8b</td> <td align="left">PAT表固定为0x00</td> </tr> <tr> <td align="left">section_syntax_indicator</td> <td align="left">1b</td> <td align="left">固定为1</td> </tr> <tr> <td align="left">zero</td> <td align="left">1b</td> <td align="left">固定为0</td> </tr> <tr> <td align="left">reserved</td> <td align="left">2b</td> <td align="left">固定为11</td> </tr> <tr> <td align="left">section_length</td> <td align="left">12b</td> <td align="left">后面数据的长度</td> </tr> <tr> <td align="left">transport_stream_id</td> <td align="left">16b</td> <td align="left">传输流ID,固定为0x0001</td> </tr> <tr> <td align="left">reserved</td> <td align="left">2b</td> <td align="left">固定为11</td> </tr> <tr> <td align="left">version_number</td> <td align="left">5b</td> <td align="left">版本号,固定为00000,如果PAT有变化则版本号加1</td> </tr> <tr> <td align="left">current_next_indicator</td> <td align="left">1b</td> <td align="left">固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT表</td> </tr> <tr> <td align="left">section_number</td> <td align="left">8b</td> <td align="left">固定为0x00</td> </tr> <tr> <td align="left">last_section_number</td> <td align="left">8b</td> <td align="left">固定为0x00</td> </tr> <tr> <td align="left">开始循环</td> <td align="left"></td> <td align="left"></td> </tr> <tr> <td align="left">program_number</td> <td align="left">16b</td> <td align="left">节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT</td> </tr> <tr> <td align="left">reserved</td> <td align="left">3b</td> <td align="left">固定为111</td> </tr> <tr> <td align="left">PID</td> <td align="left">13b</td> <td align="left">节目号对应内容的PID值</td> </tr> <tr> <td align="left">结束循环</td> <td align="left"></td> <td align="left"></td> </tr> <tr> <td align="left">CRC32</td> <td align="left">32b</td> <td align="left">前面数据的CRC32校验码</td> </tr> </tbody> </table> <h3>3.1.4 PMT(Program Map Table)格式 节目映射表</h3> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(bit)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">table_id</td> <td align="left">8b</td> <td align="left">PMT表取值随意,0x02</td> </tr> <tr> <td align="left">section_syntax_indicator</td> <td align="left">1b</td> <td align="left">固定为1</td> </tr> <tr> <td align="left">zero</td> <td align="left">1b</td> <td align="left">固定为0</td> </tr> <tr> <td align="left">reserved</td> <td align="left">2b</td> <td align="left">固定为11</td> </tr> <tr> <td align="left">section_length</td> <td align="left">12b</td> <td align="left">后面数据的长度</td> </tr> <tr> <td align="left">program_number</td> <td align="left">16b</td> <td align="left">频道号码,表示当前的PMT关联到的频道,取值0x0001</td> </tr> <tr> <td align="left">reserved</td> <td align="left">2b</td> <td align="left">固定为11</td> </tr> <tr> <td align="left">version_number</td> <td align="left">5b</td> <td align="left">版本号,固定为00000,如果PAT有变化则版本号加1</td> </tr> <tr> <td align="left">current_next_indicator</td> <td align="left">1b</td> <td align="left">固定为1</td> </tr> <tr> <td align="left">section_number</td> <td align="left">8b</td> <td align="left">固定为0x00</td> </tr> <tr> <td align="left">last_section_number</td> <td align="left">8b</td> <td align="left">固定为0x00</td> </tr> <tr> <td align="left">reserved</td> <td align="left">3b</td> <td align="left">固定为111</td> </tr> <tr> <td align="left">PCR_PID</td> <td align="left">13b</td> <td align="left">PCR(节目参考时钟)所在TS分组的PID,指定为视频PID</td> </tr> <tr> <td align="left">reserved</td> <td align="left">4b</td> <td align="left">固定为1111</td> </tr> <tr> <td align="left">program_info_length</td> <td align="left">12b</td> <td align="left">节目描述信息,指定为0x000表示没有</td> </tr> <tr> <td align="left">开始循环</td> <td align="left"></td> <td align="left"></td> </tr> <tr> <td align="left">stream_type</td> <td align="left">8b</td> <td align="left">流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03</td> </tr> <tr> <td align="left">reserved</td> <td align="left">3b</td> <td align="left">固定为111</td> </tr> <tr> <td align="left">elementary_PID</td> <td align="left">13b</td> <td align="left">与stream_type对应的PID</td> </tr> <tr> <td align="left">reserved</td> <td align="left">4b</td> <td align="left">固定为1111</td> </tr> <tr> <td align="left">ES_info_length</td> <td align="left">12b</td> <td align="left">描述信息,指定为0x000表示没有</td> </tr> <tr> <td align="left">结束循环</td> <td align="left"></td> <td align="left"></td> </tr> <tr> <td align="left">CRC32</td> <td align="left">32b</td> <td align="left">前面数据的CRC32校验码</td> </tr> </tbody> </table> <h2>3.2 pes 层:Packet Elemental Stream</h2> <p>pes 层是在每一个视频/音频帧上加入了时间戳等信息,pes 包内容很多,这里只留下最常用的。</p> <p><strong>pes 层格式如下图:</strong></p> <p><a href="http://img.e-com-net.com/image/info8/9c97c31545f94cb18aa3890ea113e029.png" target="_blank"><img src="http://img.e-com-net.com/image/info8/9c97c31545f94cb18aa3890ea113e029.png" alt="在这里插入图片描述" width="384" height="64"></a></p> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(Byte)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">pes start code</td> <td align="left">3B</td> <td align="left">开始码,固定为0x000001</td> </tr> <tr> <td align="left">stream id</td> <td align="left">1B</td> <td align="left">音频取值(0xc0-0xdf),通常为0xc0 ;视频取值(0xe0-0xef),通常为0xe0</td> </tr> <tr> <td align="left">pes packet length</td> <td align="left">2B</td> <td align="left">后面pes数据的长度,0表示长度不限制,只有视频数据长度会超过0xffff</td> </tr> <tr> <td align="left">flag</td> <td align="left">1B</td> <td align="left">通常取值0x80,表示数据不加密、无优先级、备份的数据</td> </tr> <tr> <td align="left">flag</td> <td align="left">1B</td> <td align="left">取值0x80表示只含有pts,取值0xc0表示含有pts和dts</td> </tr> <tr> <td align="left">pes data length</td> <td align="left">1B</td> <td align="left">后面数据的长度,取值5或10</td> </tr> <tr> <td align="left">pts</td> <td align="left">5B</td> <td align="left">33bit值</td> </tr> <tr> <td align="left">dts</td> <td align="left">5B</td> <td align="left">33bit值</td> </tr> </tbody> </table> <p>pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要,音频数据的pts和dts相同,所以只需要pts。有pts和dts两种时间戳是B帧引起的,I帧和P帧的pts等于dts。如果一个视频没有B帧,则pts永远和dts相同。从文件中顺序读取视频帧,取出的帧顺序和dts顺序相同。dts算法比较简单,初始值 + 增量即可,pts计算比较复杂,需要在dts的基础上加偏移量。</p> <p>音频的pes中只有pts(同dts),视频的I、P帧两种时间戳都要有,视频B帧只要pts(同dts)。打包pts和dts就需要知道视频帧类型,但是通过容器格式我们是无法判断帧类型的,必须解析h.264内容才可以获取帧类型。</p> <p>举例说明:</p> <pre><code>. 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 </code></pre> <p><strong>点播视频dts算法:</strong></p> <p><code>dts = 初始值 + 90000 / video_frame_rate</code>,初始值可以随便指定,但是最好不要取0,video_frame_rate就是帧率,比如23、30。</p> <p>pts和dts是以timescale为单位的,1s = 90000 time scale , 一帧就应该是90000/video_frame_rate 个timescale。</p> <p>用一帧的timescale除以采样频率就可以转换为一帧的播放时长</p> <p><strong>点播音频dts算法:</strong></p> <p><code>dts = 初始值 + (90000 * audio_samples_per_frame) / audio_sample_rate</code>,audio_samples_per_frame这个值与编解码相关,aac取值1024,mp3取值1158,audio_sample_rate是采样率,比如24000、41000。AAC一帧解码出来是每声道1024个sample,也就是说一帧的时长为1024/sample_rate秒。所以每一帧时间戳依次<code>0,1024/sample_rate,...,1024*n/sample_rate秒</code>。</p> <p><strong>直播视频的dts和pts应该直接用直播数据流中的时间,不应该按公式计算。</strong></p> <h2>3.3 es 层:Elementary Stream</h2> <p>es 层指的就是音视频数据。这里只介绍 h.264 视频和 aac 音频。</p> <pre><code>video: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | start code(4 byte)| nalu header(1 byte) | h264 data(x byte) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ audio: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | adts header(7 byte) | aac data(x byte) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ </code></pre> <h3>3.3.1 h.264 视频</h3> <p>打包 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 数据内容。</p> <pre><code>nalu header: 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |F|NRI| TYPE | +-+-+-+-+-+-+-+-+ </code></pre> <ul> <li>F:1bit,forbidden_zero_bit 禁止位,h.264 规定必须取 0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。</li> <li>NRI:2bits,nal_ref_idc,取值为 0~3,指示这个 nalu 的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。I 帧、sps、pps 通常取 3,P 帧常取 2,B 帧通常取 0。</li> <li>Type:5bits,取值如下表所示:</li> </ul> <p><a href="http://img.e-com-net.com/image/info8/3acc2edd4aad4038a8f87072c522cdd3.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/3acc2edd4aad4038a8f87072c522cdd3.jpg" alt="流媒体协议HLS解析_第5张图片" width="570" height="309" style="border:1px solid black;"></a><br> 打包 es 层数据时 pes 头和 es 数据之间要加入一个 type=9 的 nalu,关键帧 slice 前必须要加入 type=7 和 type=8 的 nalu,而且是紧邻的。如下图所示:</p> <p><a href="http://img.e-com-net.com/image/info8/4c1d01297a0543508abee65528963763.png" target="_blank"><img src="http://img.e-com-net.com/image/info8/4c1d01297a0543508abee65528963763.png" alt="流媒体协议HLS解析_第6张图片" width="704" height="104" style="border:1px solid black;"></a></p> <h3>3.3.2 aac音频</h3> <p>打包aac音频必须加上一个adts(Audio Data Transport Stream)头,一般情况下ADTS的头信息共7Byte,adts包括fixed_header和variable_header两部分,各28bit。固定头信息中的数据每一帧都相同,而可变头信息则在帧与帧之间可变。</p> <p><strong>fixed_header</strong></p> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(bit)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">syncword</td> <td align="left">12bit</td> <td align="left">固定为0xfff</td> </tr> <tr> <td align="left">id</td> <td align="left">1bit</td> <td align="left">0表示MPEG-4,1表示MPEG-2</td> </tr> <tr> <td align="left">layer</td> <td align="left">2bit</td> <td align="left">固定为00</td> </tr> <tr> <td align="left">protection_absent</td> <td align="left">1bit</td> <td align="left">ADTS Header的长度可能为7字节或9字节,0表示9字节,1表示7字节</td> </tr> <tr> <td align="left">profile</td> <td align="left">2bit</td> <td align="left">取值0~3,1表示aac</td> </tr> <tr> <td align="left">sampling_frequency_index</td> <td align="left">4bit</td> <td align="left">表示采样率,0: 96000 Hz,1: 88200 Hz,2: 64000 Hz,3:48000 Hz,4: 44100 Hz,5: 32000 Hz,6: 24000 Hz,7: 22050 Hz,8: 16000 Hz,9: 12000 Hz,10: 11025 Hz,11: 8000 Hz,12: 7350 Hz</td> </tr> <tr> <td align="left">private_bit</td> <td align="left">1bit</td> <td align="left">固定为0</td> </tr> <tr> <td align="left">channel_configuration</td> <td align="left">3bit</td> <td align="left">取值0~7,1: 1 channel: front-center,2: 2 channels: front-left, front-right,3: 3 channels: front-center, front-left, front-right,4: 4 channels: front-center, front-left, front-right, back-center</td> </tr> <tr> <td align="left">original_copy</td> <td align="left">1bit</td> <td align="left">固定为0</td> </tr> <tr> <td align="left">home</td> <td align="left">1bit</td> <td align="left">固定为0</td> </tr> </tbody> </table> <p><strong>variable_header</strong></p> <table> <thead> <tr> <th align="left">字段</th> <th align="left">大小 n(bit)</th> <th align="left">说明</th> </tr> </thead> <tbody> <tr> <td align="left">copyright_identification_bit</td> <td align="left">1bit</td> <td align="left">固定为0</td> </tr> <tr> <td align="left">copyright_identification_start</td> <td align="left">1bit</td> <td align="left">固定为0</td> </tr> <tr> <td align="left">aac_frame_length</td> <td align="left">13bit</td> <td align="left">包括adts头在内的音频数据总长度</td> </tr> <tr> <td align="left">adts_buffer_fullness</td> <td align="left">11bit</td> <td align="left">固定为0x7ff</td> </tr> <tr> <td align="left">number_of_raw_data_blocks_in_frame</td> <td align="left">2bit</td> <td align="left">固定为00</td> </tr> </tbody> </table> <h2>3.4 ts打包流程图</h2> <pre><code class="prism language-c"> 一个PAT包含整个TS流的信息,其中里面有一张表,比较重要的两个属性 program_number和program_map_PID,可能出现多对, 每一对program_number表示一个节目,而与该program_number对应的program_map_PID则表示该节目对应的流信息应该放在一个PMT表中, 而该PMT表的PID应该与这里的program_map_PID相等。 一个PMT中描述了流的类型,其中<span class="token number">0x0f</span>表示AAC音频,而<span class="token number">0x1b</span>表示H264视频,除这两种之外还有其他流,例如字幕。 elementay_PID表示该流的数据应该存放在以该PID为表示的TS包中。 <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> PAT <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> program_number <span class="token number">5</span> <span class="token operator">|</span>___ <span class="token operator">|</span> program_map_PID <span class="token number">10</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> program_number <span class="token number">6</span> <span class="token operator">|</span>___<span class="token operator">|</span>__ <span class="token operator">|</span> program_map_PID <span class="token number">11</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> program_number <span class="token number">7</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> program_map_PID <span class="token number">12</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> PMT <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> TS Header PID <span class="token operator">=</span> <span class="token number">10</span> <span class="token operator">|</span><span class="token operator"><</span>—— <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> stream_type <span class="token number">0x0f</span> <span class="token operator">|</span>______<span class="token operator">|</span>__________________0x0f表示AAC音频,下方AAC数据打包PID<span class="token operator">=</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token operator">|</span> elementary_PID <span class="token number">20</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> stream_type <span class="token number">0x1b</span> <span class="token operator">|</span>______<span class="token operator">|</span>__________________0x1b表示H264视频,下方H264数据打包PID<span class="token operator">=</span><span class="token number">22</span> <span class="token operator">|</span> elementary_PID <span class="token number">22</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> <span class="token operator">|</span> PMT <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> TS Header PID <span class="token operator">=</span> <span class="token number">6</span> <span class="token operator">|</span><span class="token operator"><</span>————— <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> stream_type <span class="token number">0x0f</span> <span class="token operator">|</span> <span class="token operator">|</span> elementary_PID <span class="token number">23</span> <span class="token operator">|</span> <span class="token operator">|</span> stream_type <span class="token number">0x1b</span> <span class="token operator">|</span> <span class="token operator">|</span> elementary_PID <span class="token number">24</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> 裸ACC数据<span class="token punctuation">:</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> AAC <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> 添加PES头的ACC数据<span class="token punctuation">:</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> AAC PES <span class="token operator">|</span> AAC <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> 添加TS头将PES分割之后的TS包,假设正好分割成<span class="token number">2</span>个TS包,包大小固定<span class="token number">188</span>字节,不够用adaptation域填充一般填充<span class="token number">0xFF</span><span class="token punctuation">:</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span>TS <span class="token operator">|</span> AAC PES <span class="token operator">|</span> AAC <span class="token number">1</span> <span class="token operator">|</span>TS <span class="token operator">|</span> adaptation<span class="token operator">|</span> AAC <span class="token number">2</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span><span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> Packet <span class="token number">1</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">|</span><span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> Packet <span class="token number">2</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">|</span> <span class="token operator"><</span>假设 PID <span class="token operator">=</span> <span class="token number">20</span> 的TS包<span class="token operator">></span> 裸H264数据,一帧<span class="token punctuation">:</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> H264 <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> 添加PES头之后的H264数据,一帧表示一个PES包<span class="token punctuation">:</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span> H264 PES<span class="token operator">|</span> H264 <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> 将一个PES包分割之后,分别添加TS头之后的TS包,这里假设分割成<span class="token number">3</span>个TS包,每个包固定大小<span class="token number">188</span>字节<span class="token punctuation">(</span>包含TS包头在内<span class="token punctuation">)</span>, 最后一个包不够<span class="token number">188</span>字节使用adaptaion域填充<span class="token number">0xFF</span><span class="token punctuation">:</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span>TS <span class="token operator">|</span> H264 PES<span class="token operator">|</span> H264 <span class="token number">1</span><span class="token operator">|</span>TS <span class="token operator">|</span> H264 <span class="token number">2</span> <span class="token operator">|</span>TS <span class="token operator">|</span> adaptation<span class="token operator">|</span> H264 <span class="token number">3</span> <span class="token operator">|</span> <span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span><span class="token operator">-</span><span class="token operator">+</span> <span class="token operator">|</span><span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> Packet <span class="token number">1</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">|</span><span class="token operator">-</span> <span class="token operator">-</span> Packet <span class="token number">2</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">|</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> Packet <span class="token number">3</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span> <span class="token operator">-</span><span class="token operator">|</span> <span class="token operator"><</span>假设 PID <span class="token operator">=</span> <span class="token number">22</span> 的TS包<span class="token operator">></span> </code></pre> <p><a href="http://img.e-com-net.com/image/info8/b661feeb855d4406a28facce8e00ae38.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/b661feeb855d4406a28facce8e00ae38.jpg" alt="流媒体协议HLS解析_第7张图片" width="650" height="296" style="border:1px solid black;"></a></p> <h1>4、HLS中的内容加密</h1> <h2>4.1 HLS概述</h2> <p>HLS是Apple公司推出的动态码率自适应技术,HLS内容格式在HTTP Live Streaming draft-pantos-http-live-streaming-23中描述。</p> <h2>4.2 加密方式</h2> <p>HTTP Live Streaming中内容加密有两种,<strong><code>一种是对TS切片文件直接加密;另一种是对H.264编码文件中类型为1和5的NAL单元进行加密,其它类型的NAL单元不加密</code></strong>[1]。</p> <h2>4.3 加密机制</h2> <p>HLS中媒体分块如果是加密的,其加密密钥通过M3U8文件中的#EXT-X-KEY来指定,密钥文件由客户端从服务器请求认证获得。一个播放列表可以有一个以上的#EXT-X-KEY,同一个媒体段也可以有多个不同KEYFORMAT属性值的#EXT-X-KEY。如果播放列表仅有一个#EXT-X-KEY,则密钥文件的生命期从当前#EXT-X-KEY开始到播放列表结束;如果播放列表有两个或以上的#EXT-X-KEY,则密钥文件的生命期从当前#EXT-X-KEY开始到下一个#EXT-X-KEY结束。</p> <p>#EXT-X-KEY的格式如下:</p> <p>#EXT-X-KEY:</p> <p>属性包括METHOD、URI、IV、KEYFORMAT、KEYFORMATVERSIONS,属性说明见表8。<br> <a href="http://img.e-com-net.com/image/info8/acac6894244b4165a2ace6a206171374.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/acac6894244b4165a2ace6a206171374.jpg" alt="流媒体协议HLS解析_第8张图片" width="650" height="234" style="border:1px solid black;"></a></p> <ul> <li> <p>METHOD属性为NONE时,表示媒体内容未加密,这种情况下不允许出现URI、IV、KEYFORMAT、KEYFORMATVERSIONS等属性;</p> </li> <li> <p>METHOD属性为AES-128时,表示媒体内容采用AES-128方式对TS切片文件直接加密,这种情况下URI属性必须出现,IV属性可以出现也可以不出现;</p> </li> <li> <p>METHOD属性为SAMPLE-AES时,表示对媒体段的部分或全部ES流加密,ES可以为音频、视频或其他样本,每一种ES的具体加密方式依赖于媒体编码,这种情况下IV属性可以出现也可以不出现。</p> </li> <li> <p>IV为十六进制整数,代表加密初始向量。采用AES-128方式加密时,如果IV属性存在,则必须使用IV作为初始向量实现加密;如果IV属性不存在,使用媒体段的序列码作为初始向量实现加密。</p> </li> <li> <p>KEYFORMAT标识密钥在密钥文件中的存储方式。默认是”identity”。GY/T 277-2014中增加扩展,如果KEYFORMAT=”chinadrm”,表示URI中给出的ChinaDRM规定的获取许可证的相关信息,包括许可证服务器地址和内容标识。</p> </li> </ul> <p><strong>(1)TS层加密</strong></p> <p>如果属性METHOD的值为 <strong><code>AES-128</code></strong>,并且播放列表中包含#EXT-X-I-FRAMES-ONLY标签(只包含I帧流),则整个媒体段使用AES-128 CBC加密。</p> <p>如果属性METHOD的值为 <strong><code>AES-128</code></strong>,并且播放列表中不包含#EXT-X-I-FRAMES-ONLY标签,则CBC不能跨越媒体段,每个媒体段单独使用AES-128 CBC加密,从而实现整个媒体段全部内容的加密。</p> <p>上述的两种加密方式,对TS切片文件直接加密,这种加密方式在切片时实现,需要在网络电视台及CDN系统编码器中集成加密功能,实施复杂度大,成本高。</p> <p><strong>(2)ES流加密</strong></p> <p>METHOD属性为 <strong><code>SAMPLE-AES</code></strong> 时,表示媒体在内容打包封装之前对ES流加密。<strong><code>ES层的加密对每个包含16字节整数倍的数据块以AES-128 CBC方式加密。对于视频数据,媒体段从第一个16字节数据块开始,每间隔10的整倍数的16字节的数据块加密(即1,16,26。。。),对于音频数据,所有的16字节数据块都必须加密。</code></strong></p> <p>SAMPLE-AES加密方式加密后的ES流不受上层封装及切片的影响,因此可以在不影响网络电视台现有系统及CDN部署的情况下实现对视频内容的加密,满足网络电视台等对视频内容保护的要求。</p> <ul> <li><strong><code>视频流的加密</code></strong></li> </ul> <p><strong><code>ES类型为视频流时,H.264编码文件中类型为1和5的NAL单元必须加密,其它类型的NAL单元不加密。</code></strong></p> <p><strong><code>加密的NAL单元需要增加预防二义性的前缀,该前缀是未加密的。NAL单元中第一个字节的NAL_unit_type和随后的31个字节是不加密的,其后是加密的数据段,数据段的长度必须是16的整数倍(因此长度小于48字节NAL单元是不被加密的),被保护的数据段采用10%跳跃加密,即每16字节的加密数据块,跟随9个16字节不加密的数据块,以此类推。加密H.264流时, 类型为1或5并且长度大于48个字节的NAL单元必须基于上述方式加密,加密完成后为这些加密单元加上前缀码;解密H.264流时,类型为1和5的并且长度大于48字节的NAL单元需要解密,首先移除前缀码,然后按上述方式定位加密数据并解密。</code></strong></p> <ul> <li><strong><code>音频流的加密</code></strong></li> </ul> <p><strong><code>ES类型为AAC音频帧时,包含ADTS头的音频帧为加密帧。AAC的加密帧不需要增加预防二义性的前缀,AAC帧中7-9字节的ADTS头,以及之后的首个16字节不加密,其后是加密数据段,加密数据段的长度应该为16的整数倍,余下的0-15个字节不加密。</code></strong></p> <p><strong><code>ES类型为AC-3音频帧时,全部的音频帧都加密。AC-3的加密帧不需要增加预防二义性的前缀,AC-3帧中的首个16字节不加密,其后是加密数据段,加密数据段的长度应该为16的整数倍,余下的0-15个字节不加密。</code></strong></p> <p><strong><code>(注意: 对于纯音频格式,MP4封装的AAC音频不需要增加预防二义性的前缀,其加密数据段的长度应该为16的整数倍,余下的0-15个字节不加密。)</code></strong></p> <h1>5、HLS 播放</h1> <h2>5.1 播放未加密的HLS</h2> <p>HLS格式的视频,只有安卓4.0以上才支持,目前4.0以下的机子基本可以考虑,不兼容了,所以为了减少工作量,这里继续使用MediaPlayer来进行播放。</p> <p>HLS格式的视频,通过一个m3u8文件,然后里面包含若干个TS文件片段,这里有个苹果的官方的一个例子:</p> <p>http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8</p> <p>里面的内容为:</p> <pre><code class="prism language-c"><span class="token macro property">#EXTM3U</span> <span class="token macro property">#EXT-X-TARGETDURATION:10</span> <span class="token macro property">#EXT-X-MEDIA-SEQUENCE:0</span> <span class="token macro property">#EXTINF:10, no desc</span> fileSequence0<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence1<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence2<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence3<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence4<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence5<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence6<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10, no desc</span> fileSequence7<span class="token punctuation">.</span>ts </code></pre> <p>我们可以看到里面它有一个一个ts视频片段,这一个一个视频片段就是我们需要的播放,那么它是如何被播放器识别播放的呢。</p> <p>其实上面的这些关键的字段都是约定好的,MediaPlayer会去按照规定好的字段去解析这个m3u8文件,然后拼接成最终的播放地址进行播放。那么它在播放器最终播放的地址是怎么样的呢,是这样的</p> <p>http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence0.ts</p> <p>你可以直接用这个地址播放看看。</p> <p>实现这种未加密的缓存还是比较好实现的,大概可以分为这几步。</p> <ul> <li>a、我们首先按照特定的格式去解析这m3u8文件。</li> <li>b、按照解析出来的ts文件按照我们知道的规则组拼起来,其下载这些ts文件,存放在手机的sd卡</li> <li>c、我们需要在本地搭建一个本地http服务器,我们之前本打算搭建一个https,但是由于生成的证书是自己生成导致播放器不去访问本地的服务器。</li> <li>d、本地服务器我们通过过滤特定的接口名字,来实现根据不同ts名字返回不同的视频文件(这里最好生成和原始的ts文件的名字一样)</li> <li>e、我们如何知道播放器播完一段视频呢,因为它是一段一段播放的,所以这里就需要我们在本地生成一份本地指向我们本地服务器的m3u8文件,直接播</li> </ul> <h2>5.2 播放加密HLS</h2> <p>看下加密的m3u8文件的格式:</p> <pre><code class="prism language-c"><span class="token macro property">#EXTM3U</span> <span class="token macro property">#EXT-X-VERSION:3</span> <span class="token macro property">#EXT-X-KEY:METHOD=AES-128,URI="http://xxxxxx:5555</span><span class="token comment">//test/1102/test/segments.key"</span> <span class="token macro property">#EXT-X-MEDIA-SEQUENCE:0</span> <span class="token macro property">#EXT-X-ALLOW-CACHE:YES</span> <span class="token macro property">#EXT-X-TARGETDURATION:19</span> <span class="token macro property">#EXTINF:13.966667,</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>xxxxxx<span class="token punctuation">:</span><span class="token number">5555</span><span class="token operator">/</span>test<span class="token operator">/</span><span class="token number">1102</span><span class="token operator">/</span>test<span class="token operator">/</span>segments0<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10.000000,</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>xxxxxx<span class="token punctuation">:</span><span class="token number">5555</span><span class="token operator">/</span>test<span class="token operator">/</span><span class="token number">1102</span><span class="token operator">/</span>test<span class="token operator">/</span>segments1<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10.000000,</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>xxxxxx<span class="token punctuation">:</span><span class="token number">5555</span><span class="token operator">/</span>test<span class="token operator">/</span><span class="token number">1102</span><span class="token operator">/</span>test<span class="token operator">/</span>segments2<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10.000000,</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>xxxxxx<span class="token punctuation">.</span>cn<span class="token punctuation">:</span><span class="token number">5555</span><span class="token operator">/</span>test<span class="token operator">/</span><span class="token number">1102</span><span class="token operator">/</span>test<span class="token operator">/</span>segments3<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10.000000,</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>xxxxxxn<span class="token punctuation">.</span>cn<span class="token punctuation">:</span><span class="token number">5555</span><span class="token operator">/</span>test<span class="token operator">/</span><span class="token number">1102</span><span class="token operator">/</span>test<span class="token operator">/</span>segments4<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:7.033333,</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>xxxxxx<span class="token punctuation">:</span><span class="token number">5555</span><span class="token operator">/</span>test<span class="token operator">/</span><span class="token number">1102</span><span class="token operator">/</span>test<span class="token operator">/</span>segments5<span class="token punctuation">.</span>ts <span class="token macro property">#EXTINF:10.000000,</span> </code></pre> <p>我们看到了多了个字段<code>EXT-X-KEY</code>,这个也是m3u8给规定好的加密字段,如果包含这个字段播放器就会先去请求这个key,然后拿这个这个key去访问加密的TS视频就可以播放了。 其实看到这我们就因该有思路怎么去做,加密的缓存播放了。</p> <p>实现播放加密缓存的思路:</p> <ul> <li>a、我们首先按照特定的格式去解析这m3u8文件。</li> <li>b、解析出来的ts文件按照我们知道的规则组拼起来,其下载这些ts文件,存放在手机的sd卡,这些下载下来的TS视频文件是播放不了的,再把正确的key下载下来。</li> <li>c、我们需要在本地搭建一个本地http服务器,我们之前本打算搭建一个https,但是由于生成的证书是自己生成导致播放器不去访问本地的服务器。</li> <li>d、本地服务器我们通过过滤特定的接口名字,来实现根据不同ts名字返回不同的视频文件(这里最好生成和原始的ts文件的名字一样)</li> <li>e、我们如何知道播放器播完一段视频呢,因为他是一段一段播放的,所以这里就需要我们在本地生成一份本地指向我们本地服务器的m3u8文件,直接播</li> </ul> <h1>6、HLS 协议总结</h1> <h2>6.1 优点</h2> <ul> <li>HLS 相对于 RTMP 来讲使用了标准的 HTTP 协议来传输数据,可以避免在一些特殊的网络环境下被屏蔽。</li> <li>HLS 相比 RTMP 在服务器端做负载均衡要简单得多。因为 HLS 是基于无状态协议 HTTP 实现的,客户端只需要按照顺序使用下载存储在服务器的普通 ts 文件进行播放就可以。而 RTMP 是一种有状态协议,很难对视频服务器进行平滑扩展,因为需要为每一个播放视频流的客户端维护状态。</li> <li>HLS 协议本身实现了码率自适应,在不同带宽情况下,设备可以自动切换到最适合自己码率的视频播放。</li> <li>Apple 的全系列产品支持,由于 HLS 是苹果提出的,所以在 Apple 的全系列产品包括 iphone,ipad,safari 都不需要安装任何插件就可以原生支持播放 HLS,现在,Android 也加入了对 HLS 的支持。</li> </ul> <h2>6.2 缺点</h2> <ul> <li>HLS 协议在直播的视频延迟时间很难做到 10 s 以下延时,而 RTMP 协议的延时可以降到 3s-4s 左右。</li> <li>对于点播服务来说,由于 TS 切片通常较小,海量碎片在文件分发,一致性缓存,存储等方面都有较大挑战。</li> </ul> <h2>6.3 改进</h2> <p>由于客户端每次请求 TS 或 M3U8 有可能都是一个新的连接请求,所以,我们无法有效的标识客户端,一旦出现问题,基本无法有效的定位问题。所以,一般工业级的服务器都会对传统的 HLS 做一些改进,常见优化是对每个M3U8文件增加Session来标识一条 HLS 连接。</p> <p>这里主要介绍网宿的 Variant HLS 与又拍云的 HLS+.</p> <h3>6.3.1 网宿的 Variant HLS</h3> <p>首先, 我们可以下载一条网宿的 M3U8 文件:</p> <pre><code class="prism language-c">wget http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>bililive<span class="token punctuation">.</span>kksmg<span class="token punctuation">.</span>com<span class="token operator">/</span>hls<span class="token operator">/</span>stvd6edb9a6_45b34047833af658bf4945a8<span class="token operator">/</span>playlist<span class="token punctuation">.</span>m3u8 </code></pre> <p>然后, 打开下载得到的 playlist 文件:</p> <pre><code class="prism language-c"><span class="token macro property">#EXTM3U</span> <span class="token macro property">#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=781000</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>bililive<span class="token punctuation">.</span>kksmg<span class="token punctuation">.</span>com<span class="token operator">/</span>hls<span class="token operator">/</span>stvd6edb9a6_45b34047833af658bf4945a8<span class="token operator">/</span>playlist<span class="token punctuation">.</span>m3u8<span class="token operator">?</span> wsSession<span class="token operator">=</span><span class="token number">0105</span>cb4e8fe63bccab511a4a<span class="token operator">-</span> <span class="token number">149017212774715</span><span class="token operator">&</span>wsIPSercert<span class="token operator">=</span>b80d38c068c9e3634a7ebb2f2bbf9b89<span class="token operator">&</span>wsMonitor<span class="token operator">=</span><span class="token operator">-</span><span class="token number">1</span> </code></pre> <p>可以看出这是一个 Master Playlist,里面嵌套了一层 M3U8,同时可以看出网宿采用 <code>wsSession</code> 来标识一条播放连接。</p> <h3>6.3.2 又拍云的 HLS+</h3> <p>Variant HLS</p> <p>首先,我们可以下载一条又拍云的 M3U8 文件:</p> <pre><code class="prism language-c">wget http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span>uplive<span class="token punctuation">.</span>b0<span class="token punctuation">.</span>upaiyun<span class="token punctuation">.</span>com<span class="token operator">/</span>live<span class="token operator">/</span>loading<span class="token punctuation">.</span>m3u8 </code></pre> <p>然后, 打开下载得到的 playlist 文件:</p> <pre><code class="prism language-c"><span class="token macro property">#EXTM3U</span> <span class="token macro property">#EXT-X-VERSION:3</span> <span class="token macro property">#EXT-X-ALLOW-CACHE:YES</span> <span class="token macro property">#EXT-X-MEDIA-SEQUENCE:0</span> <span class="token macro property">#EXT-X-TARGETDURATION:1</span> <span class="token macro property">#EXTINF:0.998, no desc</span> http<span class="token punctuation">:</span><span class="token operator">/</span><span class="token operator">/</span><span class="token number">183.158</span><span class="token number">.35</span><span class="token number">.12</span><span class="token punctuation">:</span><span class="token number">8080</span><span class="token operator">/</span>uplive<span class="token punctuation">.</span>b0<span class="token punctuation">.</span>upaiyun<span class="token punctuation">.</span>com<span class="token operator">/</span>live<span class="token operator">/</span>loading<span class="token operator">-</span><span class="token number">0.</span>ts<span class="token operator">?</span> shp_uuid<span class="token operator">=</span>e4989f34fcab282e21ef1fd2980284cb<span class="token operator">&</span>shp_ts<span class="token operator">=</span><span class="token number">1490172420851</span><span class="token operator">&</span>shp_cid<span class="token operator">=</span><span class="token number">17906</span><span class="token operator">&</span>shp_pid<span class="token operator">=</span><span class="token number">3</span> <span class="token number">370578</span><span class="token operator">&</span>shp_sip0<span class="token operator">=</span><span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span><span class="token operator">&</span>shp_sip1<span class="token operator">=</span><span class="token number">183.158</span><span class="token number">.35</span><span class="token number">.12</span><span class="token operator">&</span>domain<span class="token operator">=</span>uplive<span class="token punctuation">.</span>b0<span class="token punctuation">.</span>upaiyun<span class="token punctuation">.</span>com<span class="token operator">&</span>shp_seqno<span class="token operator">=</span><span class="token number">0</span> </code></pre> <p>可以看出又拍云的 HLS+ 也支持这种 Variant HLS 方式来标识一条 HLS 连接,可以看出,又拍云使用 uuid 来表示一条 HLS 连接。</p> <h3>6.3.3 HTTP 302</h3> <p>首先,以 HTTP 302 方式来请求播放地址。</p> <pre><code class="prism language-txt"> ❯ curl -v http://uplive.b0.upaiyun.com/live/loading.m3u8\?shp_identify\=302 -o playlist % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 183.158.35.59... * TCP_NODELAY set * Connected to uplive.b0.upaiyun.com (183.158.35.59) port 80 (#0) > GET /live/loading.m3u8?shp_identify=302 HTTP/1.1 > Host: uplive.b0.upaiyun.com > User-Agent: curl/7.51.0 > Accept: */* > < HTTP/1.1 302 Found < Server: marco/0.26 < Date: Wed, 22 Mar 2017 08:54:11 GMT < Content-Type: text/plain; charset=utf-8 < Content-Length: 259 < Connection: keep-alive < Access-Control-Allow-Methods: GET < Access-Control-Allow-Origin: * < Location: http://183.158.35.19:8080/uplive.b0.upaiyun.com/live/loading.m3u8? shp_uuid=2862b1b817a74cf719b1cd8f554616cd&shp_ts=1490172851450&shp_cid=59553&shp_pid=1 730488&shp_sip0=127.0.0.1&shp_sip1=183.158.35.19&domain=uplive.b0.upaiyun.com&shp_iden tify=302 < { [259 bytes data] * Curl_http_done: called premature == 0 100 259 100 259 0 0 4813 0 --:--:-- --:--:-- --:--:-- 4886 * Connection #0 to host uplive.b0.upaiyun.com left intact </code></pre> <p>打开 playlist 内容:</p> <pre><code class="prism language-txt">Redirect to http://183.158.35.19:8080/uplive.b0.upaiyun.com/live/loading.m3u8? shp_uuid=2862b1b817a74cf719b1cd8f554616cd&shp_ts=1490172851450&shp_cid=59553&shp_pid=1 730488&shp_sip0=127.0.0.1&shp_sip1=183.158.35.19&domain=uplive.b0.upaiyun.com&shp_identify=302 </code></pre> <p>在跳转之后的地址存放真正的 playlist,同时,也能够将 uuid 加入到了连接上。</p> <p>总地来说,不管通过哪种方式,最终我们都能通过一个唯一的 id 来标识一条流,这样在排查问题时就可以根据这个 id 来定位播放过程中的问题。</p> <h2>6.4 HLS 延时分析</h2> <pre><code class="prism language-c">HLS 理论延时 <span class="token operator">=</span> <span class="token number">1</span> 个切片的时长 <span class="token operator">+</span> <span class="token number">0</span><span class="token operator">-</span><span class="token number">1</span>个 td <span class="token punctuation">(</span>td 是 EXT<span class="token operator">-</span>X<span class="token operator">-</span>TARGETDURATION<span class="token punctuation">,</span> 可简单理解为播放器取片的间隔时间<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">0</span><span class="token operator">-</span>n 个启动切片<span class="token punctuation">(</span>苹果官方建议是请求到 <span class="token number">3</span> 个片之后才开始播放<span class="token punctuation">)</span> <span class="token operator">+</span> 播放器最开始请求的片的网络延时<span class="token punctuation">(</span>网络连接耗时<span class="token punctuation">)</span> </code></pre> <p>为了追求低延时效果,可以将切片切的更小,取片间隔做的更小,播放器未取到 3 个片就启动播放。但是,这些优化方式都会增加 HLS 不稳定和出现错误的风险。</p> <h1>7、客户端/服务器行为</h1> <p>本章介绍服务器怎样产生播放列表和媒体文件以及客户端怎样下载并播放。</p> <h2>7.1 服务器进程</h2> <h3>7.1.1介绍</h3> <p>MPEG-2数据流的产生超过了本文档的范围,本文档仅仅假设有一个数据流连续的源(比如rtmp直播流)。</p> <p>服务器必须将数据流分割成持续时间大致相等的媒体文件,服务器应该尝试点分割流来支持对个别媒体文件的有效解码,例如包和关键帧的边界。</p> <p>服务器必须为媒体文件创建URL,允许它的客户端能够获取到文件。</p> <p>服务器必须创建播放列表。播放列表必须符合第二章描述的格式。服务器要提供的媒体文件的URL必须按顺序出现在播放列表中。如果URL出现在了播放列表中,那么这个媒体文件对于客户端必须是可用的。</p> <p><strong><code>播放列表文件必须包含一个EXT-X-TARGRTDURATION标签,它必须指明添加到播放列表中媒体文件的最大EXTINF值。整个演示文稿期间,这个值必须保持不变。典型持续时间为10s。</code></strong></p> <p>播放列表文件应该(但非必须)包含EXT-X-VERSION标签来说明流对于版本的兼容性。它的值应该是服务器、播放列表文件和其所关联的媒体文件都能执行的最低协议版本。</p> <p>如果播放列表文件通过HTTP传输,那么服务器应该支持客户端请求使用gzip内容编码。</p> <p>从客户端的角度来看,播放列表文件的变更必须是自动的。</p> <p>服务器不可以改变EXT-X-ALLOW-CATCH的值。</p> <p><strong><code>播放列表中每个媒体文件的URL必须以EXTINF作为前缀来说明媒体文件的持续时间。</code></strong></p> <p>服务器可以将媒体文件和绝对的日期和时间关联起来,只要在它的URL前缀加上一个EXT-X-PROGRAM-DATE-TIME标签。 日期和时间的值提供了一个媒体时间表到挂钟时间的信息映射,该挂钟时间可以作为搜索、显示或其他目的的基准。</p> <p>如果服务器提供了这个映射,那么它应该在每个EXT-X-DISCONTINUITY标签的后边加一个EXT-X-PROGRAM-DATE-TIME标签。</p> <p>如果播放列表文件包含演示文稿的最后一个分片,那么应该加一个EXT-X-ENDLIST标签。</p> <p>如果播放列表文件没有包含EXT-X-ENDLIST标签(hls的live流),那么服务器应该使一个新版本的播放列表文件可用,并至少包含一个媒体文件的URL。新的播放列表文件必须与前一个播放列表文件在相对的时间内有效:从上一个播放列表文件开始有效的时间算起,不早于0.5倍持续时间,不晚于1.5倍持续时间。<strong><code>也就是说hls的live流要时刻更新m3u8文件,而更新时间要保持在[0.5,1.5]个ts持续时间内。</code></strong></p> <p>如果服务器期望移除演示文稿,它必须使播放列表文件对于客户端不可用,<strong>在播放列表被清除时,它应该确保播放列表文件中的所有媒体文件对于客户端来说至少在一个播放列表文件持续时间内是可用的。</strong></p> <h3>7.1.2 滑动窗口播放列表</h3> <p>服务器可以限制最近一段时间添加到播放列表文件中的媒体文件的可用性,为了达到这个目的,播放列表文件必须包含准确的EXT-X-MEDIA-SEQUENCE标签。标签的值是按照从播放列表中移除的媒体文件的URL递增的。</p> <p><strong><code>媒体文件的URL必须按照其加入的顺序移除。</code></strong> 当服务器从播放列表移除URL时,媒体文件在一段时间内必须保持可用,该时间等于媒体文件的时间加上包含该媒体文件的最长播放列表文件的时间。</p> <p>当媒体文件通过http传输给客户端后,如果服务器打算移除该文件,那么它应该确保http响应头包含反应生存时间的过期头。</p> <p><strong><code>那些不包含EXT-X-ENDLIST标签的播放列表文件的持续时间必须至少三倍于targrt dutration。之所以为三倍的targrt dutration可能是因为,根据hls协议来看,每个终端的播放行为都是不一致的,对于点播的m3u8文件来说,都是从第一个文件开始播,但是对于直播的m3u8,播放器可以从任意一个文件开始向后(新的文件)追溯。不过一般的播放器都是从倒数第三个开始。</code></strong></p> <h3>7.1.3 加密媒体文件</h3> <p><strong><code>如果媒体文件需要被加密,那么服务器必须定义一个URL来允许被授权的客户端获取包含解密密钥的密钥文件。</code></strong> 服务器可以在密钥响应中设置超时头来表名密钥可以被缓存。</p> <p>如果采用AES-128加密算法,那么AES-128 CBC加密模式应该适应于每一个媒体文件。整个文件必须是加密的。密码块的连接不能用于跨媒体文件。<strong><code>用于解密的初始化向量必须是媒体文件的序列号或者EXT-X-KEY标签的IV属性的值。</code></strong> 服务器必须使用这种加密算法和其他由紧随在播放列表文件中URL后边的EXT-X-KEY标签所指定的属性来加密播放列表文件中的每一个媒体文件。EXT-X-KEY标签中方法为none或者没有EXT-X-KEY标签的媒体文件不能被加密。</p> <p>如果播放列表文件包含了一个经过加密的媒体文件的URL,那么服务器不可以将EXT-X-KEY标签从播放列表文件中移除。</p> <h3>7.1.4 提供变种数据流</h3> <p>服务器可以提供多个播放列表文件来支持对同一个演示文稿的不同编码。<strong><code>提供变种播放列表文件列出每一个变种流,从而使得客户端可以在不同编码之间动态切换。</code></strong></p> <p>变种播放列表文件必须为每一个变种流包含一个EXT-X-STREAM-INF标签(即嵌套的m3u8结构)。同一演示文稿的每个EXT-X-STREAM-INF都必须有相同的programid。每个演示文稿的programid在变种播放列表内必须是唯一的。</p> <p>如果EXT-X-STREAM-INF标签包含CODECS属性,则属性值必须包含RFC4281定义的所有格式。</p> <p>服务器在生成变种流的时候必须遵守以下规则:</p> <ul> <li>1) 每一个变种流必须呈现相同的内容,包括流的间断性。</li> <li>2) 每个变种播放列表文件必须有相同的target duration。</li> <li>3) 只在个别变种播放列表文件中出现的内容必须放在列表文件的头或者尾,且不能超过target duration。</li> <li>4) 变种流内匹配内容,必须有匹配时间戳。这可以使客户端同步流。</li> <li>5) 基本音频流文件必须在文件中第一个样本的采样信号的时间戳前预先准备一个ID3 PRIV标签,标签的所有者标示符为“com.apple.streaming.transportStreamTimestamp”。二进制数据必须是33位的基本时间戳,用8字节的数字表示。</li> </ul> <p>另外,所有的变种流都应该包含相同编码的音频二进制流。这使得客户端在不同的流之间切换时没有毛刺声音(音视频不同步导致的)。</p> <h2>7.2 客户端进程</h2> <h3>7.2.1 介绍</h3> <p>客户端怎样获取播放列表中的URL不在本文档的范围之内,我们假设已经获取到了URL。</p> <h3>7.2.2 加载播放列表文件</h3> <p>每一次加载或者重载播放列表文件时:</p> <p>客户端必须保证播放列表文件以EXTM3U标签开头,并且如果协议版本号存在,客户端必须支持该版本。否则,客户端不可以试图使用该列表文件。</p> <p>客户端可以忽略它不能识别的标签和属性。</p> <p>如果播放列表文件包含了EXT-X-MEDIA-SEQUENCE标签,那么客户端会假设在播放列表被加载的时间内以及播放列表的持续时间内媒体文件将变得不可用。播放列表的持续时间等于其中包含的媒体文件时长的总和。</p> <h3>7.2.3播放播放列表文件</h3> <p>当开始播放的时候,客户端首先从播放列表中选择要播放的媒体文件。如果不存在EXT-X-ENDLIST标签,并且客户端想正常播放媒体(按顺序以标准速率播放),那么客户端就不应该从播放列表文件尾部选择少于三个target duration的媒体文件。</p> <p>为了达到正常播放的目的,媒体文件必须按照他们在播放列表中的顺序播放。客户端还可以用其他任何方式播放,比如顺序播放,随机播放,特效播放等。<br> 对于存在EXT-X-DISCONTINUITY标签的媒体文件,在播放之前客户端必须准备好重置分析和解码器。</p> <p>为了不间断播放,应该提前载入媒体文件,以补偿延时和吞吐量的变化。</p> <p>如果播放列表文件包含了EXT-X-ALLOW-CATCH标签,并且它的值为NO,那么客户端在播放以后不可以缓存媒体文件。否则允许缓存用来以后重播。</p> <p>客户端可以使用EXT-X-PROGRAM-DATE-TIME标签来为用户显示节目的起始时间。如果这个值包含了时区信息,那么客户端应该考虑到这点;如果不包含,那么客户端不可以推测时区。</p> <p>客户端不能依靠EXT-X-ALLOW-CATCH标签值的正确性和一致性。</p> <h3>7.2.4重新载入播放列表文件</h3> <p>客户端必须阶段性的重新载入播放列表文件,除非文件包含了EXT-X-ENDLIST标签。然而也不能过于频繁的载入。</p> <p><strong><code>当客户端第一次载入播放列表文件或者已经载入但是发现文件与上次载入的时候有了变化,客户端都必须等待一段时间才可以再次载入。这段时间被称为原始最小重载延迟,它是从客户端开始载入一个播放列表文件开始计算的。</code></strong></p> <p><strong><code>原始最小重载延迟是播放列表文件中最后一个媒体文件的持续时间。</code></strong> 媒体文件的持续时间由EXTINF标签来指定。</p> <p><strong><code>如果客户端重载了一个播放列表文件,但是发现文件并没有变化,那么它在重试之前必须等一段时间。最小延迟是target duration的倍数。第一次是0.5倍,第二次1.5倍,3倍。。。</code></strong></p> <h3>7.2.5 确定下一个要加载的文件</h3> <p>当播放列表文件被载入或者重载以后,客户端必须检查播放列表来确定要载入的媒体文件。要载入的第一个文件必须是客户端要播放的第一个文件(<strong><code>直播时候,未必是m3u8中的第一个文件</code></strong>)。</p> <p>如果要播放的文件已经被载入,并且播放列表文件不包含EXT-X-MEDIA-SEQUENCE标签,那么客户端必须确认播放列表文件包含了最后一个被载入的媒体文件的URL,如果不包含,则暂停播放。要载入的下一个媒体文件必须是上一次载入的媒体文件URL之后的第一个媒体文件的URL。</p> <p>如果要播放的文件已经被载入,并且播放列表文件包含EXT-X-MEDIA-SEQUENCE标签,那么要载入的下一个媒体文件就是比上一次载入的文件的序列号大的媒体文件中的序列号最小者。</p> <h3>7.2.6 解密经加密的媒体文件</h3> <p>如果播放列表文件包含了一个指定密钥文件URL的EXT-X-KEY标签,客户端必须获取密钥文件,并使用其中的密钥来解密KEY标签之后的所有媒体文件,直到遇到另一个EXT-X-KEY标签为止。</p> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1294469465139912704"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(流媒体)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1891785497869611008.htm" title="如何自建obs服务器,使用 Nginx+OBS 搭建rmtp直播服务器并进行直播" target="_blank">如何自建obs服务器,使用 Nginx+OBS 搭建rmtp直播服务器并进行直播</a> <span class="text-muted">卓相</span> <a class="tag" taget="_blank" href="/search/%E5%A6%82%E4%BD%95%E8%87%AA%E5%BB%BAobs%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">如何自建obs服务器</a> <div>目录简介和安装配置OBS推流运行拉流完成简介和安装Nginx本身是一个非常出色的HTTP服务器,OBS(OpenBroadcasterSoftware)是一个免费且开源的,全平台支持的视频录制和直播软件。这两个东西通过一个nginx的模块nginx-rtmp-module组合在一起,即可以搭建一个功能流媒体服务器。这个流媒体服务器可以支持RTMP和HLS(LiveHttpStream)。FFmpe</div> </li> <li><a href="/article/1891150785908109312.htm" title="【一文读懂】什么是RTSP协议?" target="_blank">【一文读懂】什么是RTSP协议?</a> <span class="text-muted">Bl_a_ck</span> <a class="tag" taget="_blank" href="/search/%E9%80%9A%E8%AE%AF%E5%8D%8F%E8%AE%AE/1.htm">通讯协议</a><a class="tag" taget="_blank" href="/search/udp/1.htm">udp</a><a class="tag" taget="_blank" href="/search/tcp%2Fip/1.htm">tcp/ip</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/rtsp/1.htm">rtsp</a> <div>RTSP协议(Real-TimeStreamingProtocol)RTSP(Real-TimeStreamingProtocol)是一个网络控制协议,用于在实时流媒体传输中管理多媒体数据的传输。RTSP并不传输数据本身,而是用于控制流媒体服务器,类似于HTTP协议,但它主要用于多媒体流的控制,比如音视频流的播放、暂停、停止等操作。工作原理RTSP协议本质上是一个客户端-服务器协议。客户端发起请求</div> </li> <li><a href="/article/1890629637634060288.htm" title="什么是RTMP 和 RTSP?它们之间有什么区别?" target="_blank">什么是RTMP 和 RTSP?它们之间有什么区别?</a> <span class="text-muted">wljslmz</span> <a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E6%8A%80%E6%9C%AF/1.htm">网络技术</a><a class="tag" taget="_blank" href="/search/RTMP/1.htm">RTMP</a><a class="tag" taget="_blank" href="/search/RTSP/1.htm">RTSP</a> <div>你好,这里是网络技术联盟站。视频盛行的时代,流媒体技术越来越重要,在本文中,我们将讨论两种流行的流媒体协议:RTMP和RTSP。什么是流协议?简单地说,流协议就是在两个通信系统之间传输多媒体文件的一套规则,它定义了视频文件将如何分解为小数据包以及它们在互联网上传输的顺序,RTMP与RTSP是比较常见的流媒体协议。RTMP英文全称:Real-TimeMessagingProtocol中文意思:实时消</div> </li> <li><a href="/article/1890416511470858240.htm" title="webrtc-stream和m7s可以实现thingsboard加载视频的功能,还有其其他网页的方法也可以实现如flask" target="_blank">webrtc-stream和m7s可以实现thingsboard加载视频的功能,还有其其他网页的方法也可以实现如flask</a> <span class="text-muted">鼾声鼾语</span> <a class="tag" taget="_blank" href="/search/%E8%87%AA%E5%8A%A8%E9%A9%BE%E9%A9%B6%E4%BA%91%E5%B9%B3%E5%8F%B0/1.htm">自动驾驶云平台</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/github/1.htm">github</a> <div>方法1,推荐参考的案例:去学习webrtc-stream下载地址:去下载方法2,推荐:m7s下载地址:去下载m7s流媒体服务搭建方法方法3不推荐,不能够直接迁入到thingsboard中flask实现网页读取rtsp视频流创建app.py在app.py同级目录下创建文件夹和htmltemplates/index.html启动app.py,然后在地址中就可以看到视频了</div> </li> <li><a href="/article/1890320802339942400.htm" title="流媒体传输的基本原理" target="_blank">流媒体传输的基本原理</a> <span class="text-muted">学习嵌入式的小羊~</span> <a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>•实时播放:流媒体通过网络传输多媒体数据(如音频、视频),允许用户在数据传输过程中就开始播放,而不是等到所有数据都下载完毕。这依赖于以下几个关键因素:•网络控制协议:例如RTSP(Real-TimeStreamingProtocol)用于控制流媒体会话,RTP(Real-timeTransportProtocol)用于数据传输,而RTCP(RTPControlProtocol)用于监控质量和提供反</div> </li> <li><a href="/article/1889548720693899264.htm" title="RTP协议详解" target="_blank">RTP协议详解</a> <span class="text-muted">dusk0825</span> <a class="tag" taget="_blank" href="/search/%E6%B5%81%E5%AA%92%E4%BD%93/1.htm">流媒体</a> <div>RTP协议负责对流媒体数据进行封包并实现媒体流的实时传输,即它按照RPT数据包格式来封装流媒体数据,并利用与它绑定的协议进行数据包的传输;RTP本身只保证实时数据的传输,并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠RTCP提供这些服务。一、RTP数据包格式RTP码流由多个RTP包组成,每个RTP包,由RTP头标准字段(固定12字节)、RTP头扩展字段、RTP负载、</div> </li> <li><a href="/article/1889427441391300608.htm" title="FFmpeg 命令规则与参数详解:输入/输出 YUV 文件的要求与分析" target="_blank">FFmpeg 命令规则与参数详解:输入/输出 YUV 文件的要求与分析</a> <span class="text-muted">陈皮话梅糖@</span> <a class="tag" taget="_blank" href="/search/FFmpeg%E9%9F%B3%E8%A7%86%E9%A2%91%E9%AB%98%E9%98%B6%E6%8A%80%E6%9C%AF%E8%AE%B2%E8%A7%A3/1.htm">FFmpeg音视频高阶技术讲解</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a> <div>FFmpeg命令规则与参数详解:输入/输出YUV文件的要求与分析FFmpeg是一个强大的多媒体处理工具,支持视频、音频的编解码、格式转换、流媒体处理等功能。使用FFmpeg时,命令行参数的配置非常重要,尤其是处理YUV文件时,需要明确指定输入/输出的格式、分辨率、帧率等参数。本文将详细分析FFmpeg命令的规则和要求,特别是针对YUV文件的输入和输出。1.FFmpeg命令的基本结构FFmpeg命令</div> </li> <li><a href="/article/1888993684578430976.htm" title="音视频流媒体中的实 时 流式 传 输 (Realtime (progressive streaming)。streaming )和 顺 序 流式 传 输" target="_blank">音视频流媒体中的实 时 流式 传 输 (Realtime (progressive streaming)。streaming )和 顺 序 流式 传 输</a> <span class="text-muted">学习嵌入式的小羊~</span> <a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a> <div>实时流式传输(RealtimeStreaming)和顺序流式传输(ProgressiveStreaming)是两种不同的视频传输方式,它们各自有不同的特点和应用场景:实时流式传输(RealtimeStreaming)定义:实时流式传输是指视频内容在被创建或捕获的同时进行传输。观众可以立即看到正在进行的直播内容。特点:实时性:内容几乎没有延迟地到达观众,这对于直播、体育赛事、新闻报道等场景非常重要。</div> </li> <li><a href="/article/1888652635943268352.htm" title="EasyNVR平台现已支持AAC、G711A及G711U音频编码格式" target="_blank">EasyNVR平台现已支持AAC、G711A及G711U音频编码格式</a> <span class="text-muted">EasyNVR</span> <a class="tag" taget="_blank" href="/search/EasyNVR%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/1.htm">EasyNVR问题解决</a><a class="tag" taget="_blank" href="/search/aac/1.htm">aac</a><a class="tag" taget="_blank" href="/search/g711/1.htm">g711</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E7%9B%91%E6%8E%A7/1.htm">视频监控</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a> <div>在视频监控和流媒体传输领域,音频与视频的质量和同步性是用户体验的关键因素。EasyNVR平台一直以来致力于为用户提供稳定、高效的音视频解决方案。然而,随着技术的发展和用户需求的多样化,平台在音频编码格式的支持上遇到了一些挑战。之前有客户反馈,在使用EasyNVR平台播放云端录像时没有声音。经排查发现,问题源于设备使用的是G711A音频编码格式,而当时EasyNVR平台仅支持AAC格式的云端录像音频</div> </li> <li><a href="/article/1888548121198260224.htm" title="实现使用Python和OpenCV播放RTMP视频流媒体的WebRTC" target="_blank">实现使用Python和OpenCV播放RTMP视频流媒体的WebRTC</a> <span class="text-muted">FdviAutoit</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/opencv/1.htm">opencv</a><a class="tag" taget="_blank" href="/search/%E5%AA%92%E4%BD%93/1.htm">媒体</a><a class="tag" taget="_blank" href="/search/WebRTC/1.htm">WebRTC</a> <div>WebRTC(Web实时通信)是一种用于浏览器之间实时通信的开放标准。它可以在Web浏览器中实现音频、视频和数据的传输,为实时通信应用程序提供了强大的功能。本文将介绍如何使用Python和OpenCV库来实现通过RTMP(实时媒体传输协议)播放视频流,并结合WebRTC实现实时的视频通信。在开始之前,确保你已经安装了Python和相应的库。你可以使用pip命令来安装OpenCV和其他所需的库:pi</div> </li> <li><a href="/article/1888453573767917568.htm" title="NGINX-RTMP 框架详解及与SRS对比" target="_blank">NGINX-RTMP 框架详解及与SRS对比</a> <span class="text-muted">Ryan-S</span> <a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>NGINX-RTMP框架详解及与SRS对比1.概述NGINX-RTMP是基于Nginx的第三方模块(由社区开发维护),专注于RTMP协议的流媒体处理,适用于直播推流、点播分发等场景。它通过扩展Nginx的能力,支持RTMP流接收、转码、录制及多协议输出(如HLS)。核心特点如下:2.核心功能推流与拉流:支持RTMP协议推流(如OBS推流)和拉流播放。转码与录制:集成FFmpeg进行实时转码(分辨率</div> </li> <li><a href="/article/1888353704566910976.htm" title="【QT】- QUdpSocket" target="_blank">【QT】- QUdpSocket</a> <span class="text-muted">追心嵌入式</span> <a class="tag" taget="_blank" href="/search/QT/1.htm">QT</a><a class="tag" taget="_blank" href="/search/qt/1.htm">qt</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>QUdpSocket是Qt自带的一个类,属于Qt网络模块,用于进行UDP(用户数据报协议)通信。它提供了简便的接口来发送和接收UDP数据报(datagrams)。UDP是一种无连接的协议,适用于那些不需要确保数据可靠性和顺序的应用场景,比如实时游戏、流媒体、DNS请求等。QUdpSocket概述QUdpSocket继承自QAbstractSocket,可以用于进行基于UDP协议的数据发送和接收。它</div> </li> <li><a href="/article/1887896661229891584.htm" title="Licode简介及与SRS对比" target="_blank">Licode简介及与SRS对比</a> <span class="text-muted">Ryan-S</span> <a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/Licode/1.htm">Licode</a> <div>Licode是一个开源的WebRTC通信框架,专注于多人实时音视频互动(如视频会议),而SRS是一个通用的流媒体服务器,支持直播、低延迟流分发等场景。以下是两者的详细对比和Licode的核心解析:一、Licode核心解析1.定位与设计目标核心功能:基于WebRTC的多人实时音视频通信(SFU/MCU混合架构)。设计思想:分布式架构:支持横向扩展(多个节点组成集群)。房间管理:以“房间(Room)”</div> </li> <li><a href="/article/1887486712712589312.htm" title="NVR管理平台EasyNVR深度解析:RTSP协议应用及摄像机集成" target="_blank">NVR管理平台EasyNVR深度解析:RTSP协议应用及摄像机集成</a> <span class="text-muted">EasyGBS</span> <a class="tag" taget="_blank" href="/search/EasyNVR/1.htm">EasyNVR</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E7%9B%91%E6%8E%A7/1.htm">视频监控</a> <div>随着信息技术的飞速发展,智慧视频监控平台已成为现代社会安全管理与智能化建设的重要基石。在众多视频监控平台中,NVR管理平台EasyNVR凭借其高效性、兼容性和灵活性,赢得了广泛的赞誉和应用。RTSP协议主要用于控制声音或影像的多媒体串流。它允许客户端与服务器进行交互,从而控制流媒体的播放、暂停、停止、倒放、快进等操作。RTSP协议应用及原理1、应用场景:共装了几台网络摄像机,想要实现多台录像机同时</div> </li> <li><a href="/article/1887485704355770368.htm" title="使用国标流媒体服务器查看监控摄像头视频流如何正确使用UDP及TCP协议?" target="_blank">使用国标流媒体服务器查看监控摄像头视频流如何正确使用UDP及TCP协议?</a> <span class="text-muted">EasyGBS</span> <a class="tag" taget="_blank" href="/search/EasyGBS/1.htm">EasyGBS</a><a class="tag" taget="_blank" href="/search/TCP%E5%8D%8F%E8%AE%AE/1.htm">TCP协议</a><a class="tag" taget="_blank" href="/search/%E5%9B%BD%E6%A0%87%E6%B5%81%E5%AA%92%E4%BD%93%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">国标流媒体服务器</a><a class="tag" taget="_blank" href="/search/%E5%9B%BD%E6%A0%87GB28181/1.htm">国标GB28181</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E9%98%B2%E6%91%84%E5%83%8F%E5%A4%B4/1.htm">安防摄像头</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E7%9B%B4%E6%92%AD/1.htm">视频直播</a> <div>用过国标流媒体服务器的朋友们应该都知道,GB28181协议是由公安部提出来的,能够对接公安部的网络系统,给安防带来了很大的便利性,我们的国标流媒体服务器就支持集成接入自己的平台,也能够对视频进行录像。使用国标流媒体服务器会有很多二次开发的可能,因为我们会提供丰富的二次开发接口。近期也有不少用户用https接入国标流媒体服务器,是一种十分实用的视频监控网页直播。有位开发者为了进行测试,在自己的现场环</div> </li> <li><a href="/article/1887343002456748032.htm" title="SRS分析及低延迟实现机制" target="_blank">SRS分析及低延迟实现机制</a> <span class="text-muted">Ryan-S</span> <a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>本文是关于SRS(SimpleRealtimeServer)的架构解析、性能优势以及低延迟实现机制的详细说明:一、SRS的核心架构SRS的架构设计以模块化、高性能、低延迟为核心目标,主要分为以下几个层次:1.进程模型单进程多线程:SRS默认采用单进程模型,通过多线程处理网络I/O、流媒体协议解析、转码等任务,减少进程间切换开销。多进程集群:支持通过--workers参数启动多个进程,充分利用多核C</div> </li> <li><a href="/article/1887330521420197888.htm" title="音视频开发成长之路与音视频知识点总结" target="_blank">音视频开发成长之路与音视频知识点总结</a> <span class="text-muted">Linux服务器开发</span> <a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/1.htm">音视频开发</a><a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/1.htm">音视频开发</a><a class="tag" taget="_blank" href="/search/%E6%B5%81%E5%AA%92%E4%BD%93%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%BC%80%E5%8F%91/1.htm">流媒体服务器开发</a><a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/FFmpeg/1.htm">FFmpeg</a><a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/1.htm">嵌入式音视频开发</a> <div>音视频涉及语音信号处理、数字图像处理、信息论、封装格式、编解码、流媒体协议、网络传输、渲染、算法等。在现实生活中,音视频发挥着越来越重要的作用,如视频会议、直播、短视频、播放器、语音聊天等。所以从事音视频开发是一件有意义的事情,机遇和挑战并存。本文将从:音视频开发基础、音视频高级成长、音视频工作方向、音视频开源库、音视频相关书籍,配套的学习资源等几个方面来进行介绍。那么我们该如何系统的学习音视频开</div> </li> <li><a href="/article/1887326365724766208.htm" title="FFmpeg:多媒体处理的瑞士军刀" target="_blank">FFmpeg:多媒体处理的瑞士军刀</a> <span class="text-muted">我码玄黄</span> <a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90/1.htm">开源</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E5%AA%92%E4%BD%93%E5%A4%84%E7%90%86/1.htm">多媒体处理</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90/1.htm">开源</a> <div>FFmpeg:多媒体处理的瑞士军刀前言FFmpeg是一个功能强大且跨平台的开源多媒体框架,广泛应用于音视频处理领域。它由多个库和工具组成,能够处理各种音视频格式,涵盖编码、解码、转码、流处理等多种操作。无论是专业视频编辑软件,还是流媒体服务,FFmpeg都扮演着重要角色。本文将从其核心组件、常用命令、应用场景以及优势与挑战等方面,深入探讨FFmpeg的功能与价值。核心组件解析FFmpeg的强大功能</div> </li> <li><a href="/article/1886914560280031232.htm" title="改善体验,降低成本,Akamai如何帮助球迷和平台实现双赢" target="_blank">改善体验,降低成本,Akamai如何帮助球迷和平台实现双赢</a> <span class="text-muted">Akamai中国</span> <a class="tag" taget="_blank" href="/search/%E4%BA%91%E8%AE%A1%E7%AE%97/1.htm">云计算</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E6%9C%8D%E5%8A%A1/1.htm">云服务</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E8%AE%A1%E7%AE%97/1.htm">云计算</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E5%8E%9F%E7%94%9F/1.htm">云原生</a><a class="tag" taget="_blank" href="/search/akamai/1.htm">akamai</a> <div>Bitmovin是一家领先的视频基础设施供应商,主要服务于全球数字媒体公司和服务商。Bitmovin总部位于美国加利福尼亚州旧金山,在奥地利维也纳和克拉根福、伦敦、柏林、丹佛设有办事处。该公司在全球拥有超过400个客户,包括ClassPass、BBC、FuboTV、Hulu和Discovery等。Bitmovin一直身处行业创新和发展的前沿,曾经构建了全球首个商业化的自适应流媒体播放器,并部署了首</div> </li> <li><a href="/article/1886717362879655936.htm" title="高并发架构设计漫谈" target="_blank">高并发架构设计漫谈</a> <span class="text-muted">北雨南萍</span> <a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E9%AB%98%E5%B9%B6%E5%8F%91/1.htm">高并发</a> <div>商业系统的架构设计知易行难,少有一蹴而就的。它往往是业务、技术、人员、时间的四重平衡与取舍。架构如下棋,如是见棋走子,不做提前两三步的预测和判断,通常难胜。当业务已经在系统上跑起来后,想要再改架构,积重难返。业务不等人,竞争对手进攻,决策层耐心有限,项目成员精力疲惫信心不在,何去何从?本文讨论的是面向商业的高并发系统,介绍了一种面向高并发的流媒体传输系统的架构设计,它和具体的业务关系不大。和自下而</div> </li> <li><a href="/article/1886458473009442816.htm" title="流媒体娱乐服务平台在AWS上使用Presto作为大数据的交互式查询引擎的具体流程和代码" target="_blank">流媒体娱乐服务平台在AWS上使用Presto作为大数据的交互式查询引擎的具体流程和代码</a> <span class="text-muted">weixin_30777913</span> <a class="tag" taget="_blank" href="/search/aws/1.htm">aws</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>一家流媒体娱乐服务平台拥有庞大的用户群体和海量的数据。为了高效处理和分析这些数据,它选择了Presto作为其在AWSEMR上的大数据查询引擎。在AWSEMR上使用Presto取得了显著的成果和收获。这些成果不仅提升了数据查询效率,降低了运维成本,还促进了业务的创新与发展。实施过程:Presto集群部署:在AWSEMR上部署了Presto集群,该集群与HiveMetastore和AmazonS3集成</div> </li> <li><a href="/article/1885801600140767232.htm" title="python实现webrtc通过whep拉取实时音频流" target="_blank">python实现webrtc通过whep拉取实时音频流</a> <span class="text-muted">眉梢i</span> <a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>需求背景:通过whep的方式从流媒体服务器平台(基于srs服务器改造的平台)拉取实时音频流,数据传递采用48khz、16bit、双声道音频流,接收到数据后,转换成16khz、16bit、单声道音频流,并将其以base64加密字节流方式通过websocket传递给第三方;1:通道及轨道的建立classAudioTrack(MediaStreamTrack):kind="audio"def__init</div> </li> <li><a href="/article/1885801600723775488.htm" title="pytthon实现webrtc通过whip推送实时流式音频流" target="_blank">pytthon实现webrtc通过whip推送实时流式音频流</a> <span class="text-muted">眉梢i</span> <a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>需求背景:通过whip的方式推送流式的实时音频流到流媒体服务器平台(基于srs服务器改造的平台)数据传递采用48khz、16bit、双声道音频流,将需要发送的数据,从16khz、16bit、单声道音频流转换成所需传递的格式,并对源音频流进行大小切割,阻塞限速,按照实际播放器的一倍速进行推送;核心代码:1:将音频流转换成可传递的音频帧asyncdefbytes_to_audio_frames(sel</div> </li> <li><a href="/article/1885300601265385472.htm" title="prebuilt-ffmpeg-android: 为Android设备预先构建的FFmpeg支持多架构" target="_blank">prebuilt-ffmpeg-android: 为Android设备预先构建的FFmpeg支持多架构</a> <span class="text-muted">古斯塔夫歼星炮</span> <div>本文还有配套的精品资源,点击获取简介:FFmpeg是一个开源的多媒体处理框架,用于处理音频和视频的编码、解码、转换和流媒体操作。在Android平台上,由于多种硬件架构的存在,如armv7,armv7-neon,和x86,为应用集成FFmpeg通常需要复杂的交叉编译过程。prebuilt-ffmpeg-android项目预先为这些架构构建了FFmpeg库,简化了开发者的集成过程,从而避免了自己编译</div> </li> <li><a href="/article/1885173211574890496.htm" title="【ffmpeg命令】RTMP推流" target="_blank">【ffmpeg命令】RTMP推流</a> <span class="text-muted">人才程序员</span> <a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91%E5%9F%BA%E7%A1%80%E5%A4%A7%E5%90%88%E9%9B%86/1.htm">音视频基础大合集</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E7%BC%96%E8%A7%A3%E7%A0%81/1.htm">视频编解码</a><a class="tag" taget="_blank" href="/search/%E5%AE%9E%E6%97%B6%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">实时音视频</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E9%9F%B3%E8%AF%86%E5%88%AB/1.htm">语音识别</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E8%A7%89%E6%A3%80%E6%B5%8B/1.htm">视觉检测</a> <div>文章目录前言推流是什么RTMP协议简介RTMP的基本概念RTMP的工作原理RTMP的优缺点ffmpegRTMP推流推流命令综合解释ffplay播放RTMP流总结前言在现代的视频直播中,RTMP(Real-TimeMessagingProtocol)是一种广泛使用的流媒体传输协议。它允许高效的实时视频和音频传输,是许多直播平台和应用的首选协议。FFmpeg是一个强大的多媒体处理工具,可以实现视频录制</div> </li> <li><a href="/article/1884494456758005760.htm" title="不了解Sora?看完这篇文章你就懂了" target="_blank">不了解Sora?看完这篇文章你就懂了</a> <span class="text-muted">KuaFuAI</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/AIGC/1.htm">AIGC</a><a class="tag" taget="_blank" href="/search/chatgpt/1.htm">chatgpt</a><a class="tag" taget="_blank" href="/search/DALL%C2%B7E/1.htm">DALL·E</a><a class="tag" taget="_blank" href="/search/2/1.htm">2</a><a class="tag" taget="_blank" href="/search/agi/1.htm">agi</a><a class="tag" taget="_blank" href="/search/gpt/1.htm">gpt</a><a class="tag" taget="_blank" href="/search/prompt/1.htm">prompt</a> <div>一、Sora的概念介绍2024年2月16日,OpenAI发布了“文生视频”(text-to-video)的大模型工具,Sora(利用自然语言描述,生成视频)。这个消息一经发出,全球社交主流媒体平台以及整个世界都再次被OpenAI震撼了。AI视频的高度一下子被Sora拉高了,要知道RunwayPika等文生视频工具,都还在突破几秒内的连贯性,而Sora已经可以直接生成长达60s的一镜到底视频,要知道</div> </li> <li><a href="/article/1883517453451980800.htm" title="freeswtch目录下modules.conf各个模块的介绍【freeswitch版本1.6.8】" target="_blank">freeswtch目录下modules.conf各个模块的介绍【freeswitch版本1.6.8】</a> <span class="text-muted">狂爱代码的码农</span> <a class="tag" taget="_blank" href="/search/VOIP%E9%82%A3%E4%BA%9B%E4%BA%8B/1.htm">VOIP那些事</a><a class="tag" taget="_blank" href="/search/freeswitch/1.htm">freeswitch</a> <div>应用模块(applications)mod_abstraction:为其他模块提供抽象层,有助于简化模块开发,让开发者能在统一框架下开发新功能,减少与底层系统的直接交互,提高代码可移植性和可维护性。mod_av:处理音频和视频相关操作,可用于音视频会议、流媒体播放等场景,支持多种音视频编解码格式,实现音视频数据的采集、处理和传输。mod_avmd:主要用于音频和视频元数据处理,能提取、分析和管理音</div> </li> <li><a href="/article/1883492859613605888.htm" title="基于dlib/face recognition人脸识别推拉流实现" target="_blank">基于dlib/face recognition人脸识别推拉流实现</a> <span class="text-muted">#北极星star</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB/1.htm">人脸识别</a><a class="tag" taget="_blank" href="/search/opencv/1.htm">opencv</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a> <div>目录一.环境搭建二.推拉流代码三.人脸检测推拉流一.环境搭建1.下载RTSP服务器MediaMTX与FFmpegFFmpeg是一款功能强大的开源多媒体处理工具,而MediaMTX则是一个轻量级的流媒体服务器。两者结合,可以实现将本地视频或者实时摄像头画面推送到RTSP流,从而实现视频直播、监控等功能。FFmpeg:负责将本地视频或实时摄像头画面编码成RTSP流所需要的格式,并发送给MediaMTX</div> </li> <li><a href="/article/1883193605661716480.htm" title="流媒体直播实时视频延迟时间排查和剖析:gop关键帧间隔导致延迟,流媒体和播放器缓存,B帧等导致的延迟" target="_blank">流媒体直播实时视频延迟时间排查和剖析:gop关键帧间隔导致延迟,流媒体和播放器缓存,B帧等导致的延迟</a> <span class="text-muted">eguid_1</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/1.4.3%E7%89%88%E6%9C%AC%EF%BC%89/1.htm">1.4.3版本)</a><a class="tag" taget="_blank" href="/search/%E7%9B%B4%E6%92%AD%E5%BB%B6%E8%BF%9F/1.htm">直播延迟</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E5%BB%B6%E8%BF%9F/1.htm">视频延迟</a><a class="tag" taget="_blank" href="/search/%E7%9B%B4%E6%92%AD%E5%B9%B3%E5%8F%B0/1.htm">直播平台</a><a class="tag" taget="_blank" href="/search/%E6%92%AD%E6%94%BE%E5%BB%B6%E8%BF%9F/1.htm">播放延迟</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%BB%B6%E8%BF%9F/1.htm">网络延迟</a> <div>本章是流媒体直播实时视频延迟时间排查和剖析javaCV系列文章:javacv开发详解之1:调用本机摄像头视频javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG、javaCV-openCV)javaCV开发详解之3:收流器实现,录制流媒体服务器的rtsp/rtmp视频文件(基于javaCV-FFMPEG)</div> </li> <li><a href="/article/1883187172807340032.htm" title="FFmpeg音视频采集" target="_blank">FFmpeg音视频采集</a> <span class="text-muted">yerennuo</span> <a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>文章目录音视频采集音频采集获取设备信息录制麦克风录制声卡视频采集摄像机画面采集音视频采集DirectShow(简称DShow)是一个Windows平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能,它支持多种多样的媒体文件格式,包括ASF、MPEG、AVI、MP3和WAV文件,同时支持使用WDM驱动或早期的VFW驱动来进行多媒体流的采集。DirectShow大大简化了媒体回放、格式转换和采集</div> </li> <li><a href="/article/44.htm" title="Enum 枚举" target="_blank">Enum 枚举</a> <span class="text-muted">120153216</span> <a class="tag" taget="_blank" href="/search/enum/1.htm">enum</a><a class="tag" taget="_blank" href="/search/%E6%9E%9A%E4%B8%BE/1.htm">枚举</a> <div>原文地址:http://www.cnblogs.com/Kavlez/p/4268601.html Enumeration 于Java 1.5增加的enum type...enum type是由一组固定的常量组成的类型,比如四个季节、扑克花色。在出现enum type之前,通常用一组int常量表示枚举类型。比如这样: public static final int APPLE_FUJI = 0</div> </li> <li><a href="/article/171.htm" title="Java8简明教程" target="_blank">Java8简明教程</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jdk1.8/1.htm">jdk1.8</a> <div>        Java 8已于2014年3月18日正式发布了,新版本带来了诸多改进,包括Lambda表达式、Streams、日期时间API等等。本文就带你领略Java 8的全新特性。  一.允许在接口中有默认方法实现         Java 8 允许我们使用default关键字,为接口声明添</div> </li> <li><a href="/article/298.htm" title="Oracle表维护 快速备份删除数据" target="_blank">Oracle表维护 快速备份删除数据</a> <span class="text-muted">cuisuqiang</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E7%B4%A2%E5%BC%95/1.htm">索引</a><a class="tag" taget="_blank" href="/search/%E5%BF%AB%E9%80%9F/1.htm">快速</a><a class="tag" taget="_blank" href="/search/%E5%A4%87%E4%BB%BD/1.htm">备份</a><a class="tag" taget="_blank" href="/search/%E5%88%A0%E9%99%A4/1.htm">删除</a> <div>我知道oracle表分区,不过那是数据库设计阶段的事情,目前是远水解不了近渴。 当前的数据库表,要求保留一个月数据,且表存在大量录入更新,不存在程序删除。 为了解决频繁查询和更新的瓶颈,我在oracle内根据需要创建了索引。但是随着数据量的增加,一个半月数据就要超千万,此时就算有索引,对高并发的查询和更新来说,让然有所拖累。 为了解决这个问题,我一般一个月会进行一次数据库维护,主要工作就是备</div> </li> <li><a href="/article/425.htm" title="java多态内存分析" target="_blank">java多态内存分析</a> <span class="text-muted">麦田的设计者</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%86%85%E5%AD%98%E5%88%86%E6%9E%90/1.htm">内存分析</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E6%80%81%E5%8E%9F%E7%90%86/1.htm">多态原理</a><a class="tag" taget="_blank" href="/search/%E6%8E%A5%E5%8F%A3%E5%92%8C%E6%8A%BD%E8%B1%A1%E7%B1%BB/1.htm">接口和抽象类</a> <div>      “  时针如果可以回头,熟悉那张脸,重温嬉戏这乐园,墙壁的松脱涂鸦已经褪色才明白存在的价值归于记忆。街角小店尚存在吗?这大时代会不会牵挂,过去现在花开怎么会等待。       但有种意外不管痛不痛都有伤害,光阴远远离开,那笑声徘徊与脑海。但这一秒可笑不再可爱,当天心</div> </li> <li><a href="/article/552.htm" title="Xshell实现Windows上传文件到Linux主机" target="_blank">Xshell实现Windows上传文件到Linux主机</a> <span class="text-muted">被触发</span> <a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a> <div>经常有这样的需求,我们在Windows下载的软件包,如何上传到远程Linux主机上?还有如何从Linux主机下载软件包到Windows下;之前我的做法现在看来好笨好繁琐,不过也达到了目的,笨人有本方法嘛; 我是怎么操作的: 1、打开一台本地Linux虚拟机,使用mount 挂载Windows的共享文件夹到Linux上,然后拷贝数据到Linux虚拟机里面;(经常第一步都不顺利,无法挂载Windo</div> </li> <li><a href="/article/679.htm" title="类的加载ClassLoader" target="_blank">类的加载ClassLoader</a> <span class="text-muted">肆无忌惮_</span> <a class="tag" taget="_blank" href="/search/ClassLoader/1.htm">ClassLoader</a> <div>类加载器ClassLoader是用来将java的类加载到虚拟机中,类加载器负责读取class字节文件到内存中,并将它转为Class的对象(类对象),通过此实例的 newInstance()方法就可以创建出该类的一个对象。   其中重要的方法为findClass(String name)。   如何写一个自己的类加载器呢? 首先写一个便于测试的类Student</div> </li> <li><a href="/article/806.htm" title="html5写的玫瑰花" target="_blank">html5写的玫瑰花</a> <span class="text-muted">知了ing</span> <a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a> <div><html> <head> <title>I Love You!</title> <meta charset="utf-8" /> </head> <body> <canvas id="c"></canvas> </div> </li> <li><a href="/article/933.htm" title="google的ConcurrentLinkedHashmap源代码解析" target="_blank">google的ConcurrentLinkedHashmap源代码解析</a> <span class="text-muted">矮蛋蛋</span> <a class="tag" taget="_blank" href="/search/LRU/1.htm">LRU</a> <div>原文地址: http://janeky.iteye.com/blog/1534352 简述 ConcurrentLinkedHashMap 是google团队提供的一个容器。它有什么用呢?其实它本身是对 ConcurrentHashMap的封装,可以用来实现一个基于LRU策略的缓存。详细介绍可以参见 http://code.google.com/p/concurrentlinke</div> </li> <li><a href="/article/1060.htm" title="webservice获取访问服务的ip地址" target="_blank">webservice获取访问服务的ip地址</a> <span class="text-muted">alleni123</span> <a class="tag" taget="_blank" href="/search/webservice/1.htm">webservice</a> <div>1. 首先注入javax.xml.ws.WebServiceContext, @Resource private WebServiceContext context; 2. 在方法中获取交换请求的对象。 javax.xml.ws.handler.MessageContext mc=context.getMessageContext(); com.sun.net.http</div> </li> <li><a href="/article/1187.htm" title="菜鸟的java基础提升之道——————>是否值得拥有" target="_blank">菜鸟的java基础提升之道——————>是否值得拥有</a> <span class="text-muted">百合不是茶</span> <div>1,c++,java是面向对象编程的语言,将万事万物都看成是对象;java做一件事情关注的是人物,java是c++继承过来的,java没有直接更改地址的权限但是可以通过引用来传值操作地址,java也没有c++中繁琐的操作,java以其优越的可移植型,平台的安全型,高效性赢得了广泛的认同,全世界越来越多的人去学习java,我也是其中的一员      java组成:</div> </li> <li><a href="/article/1314.htm" title="通过修改Linux服务自动启动指定应用程序" target="_blank">通过修改Linux服务自动启动指定应用程序</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>Linux中修改系统服务的命令是chkconfig (check config),命令的详细解释如下: chkconfig 功能说明:检查,设置系统的各种服务。 语  法:chkconfig [ -- add][ -- del][ -- list][系统服务] 或 chkconfig [ -- level  <</SPAN></div> </li> <li><a href="/article/1441.htm" title="spring拦截器的一个简单实例" target="_blank">spring拦截器的一个简单实例</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E6%8B%A6%E6%88%AA%E5%99%A8/1.htm">拦截器</a><a class="tag" taget="_blank" href="/search/Interceptor/1.htm">Interceptor</a> <div>Purview接口 package aop; public interface Purview { void checkLogin(); } Purview接口的实现类PurviesImpl.java package aop; public class PurviewImpl implements Purview { public void check</div> </li> <li><a href="/article/1568.htm" title="[Velocity二]自定义Velocity指令" target="_blank">[Velocity二]自定义Velocity指令</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/velocity/1.htm">velocity</a> <div>什么是Velocity指令 在Velocity中,#set,#if, #foreach, #elseif, #parse等,以#开头的称之为指令,Velocity内置的这些指令可以用来做赋值,条件判断,循环控制等脚本语言必备的逻辑控制等语句,Velocity的指令是可扩展的,即用户可以根据实际的需要自定义Velocity指令   自定义指令(Directive)的一般步骤 &nbs</div> </li> <li><a href="/article/1695.htm" title="【Hive十】Programming Hive学习笔记" target="_blank">【Hive十】Programming Hive学习笔记</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/programming/1.htm">programming</a> <div> 第二章 Getting Started 1.Hive最大的局限性是什么?一是不支持行级别的增删改(insert, delete, update)二是查询性能非常差(基于Hadoop MapReduce),不适合延迟小的交互式任务三是不支持事务2. Hive MetaStore是干什么的?Hive persists table schemas and other system metadata.</div> </li> <li><a href="/article/1822.htm" title="nginx有选择性进行限制" target="_blank">nginx有选择性进行限制</a> <span class="text-muted">ronin47</span> <a class="tag" taget="_blank" href="/search/nginx+%E5%8A%A8%E9%9D%99%E3%80%80%E9%99%90%E5%88%B6/1.htm">nginx 动静 限制</a> <div>http { limit_conn_zone $binary_remote_addr zone=addr:10m; limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;... server {... location ~.*\.(gif|png|css|js|icon)$ { </div> </li> <li><a href="/article/1949.htm" title="java-4.-在二元树中找出和为某一值的所有路径 ." target="_blank">java-4.-在二元树中找出和为某一值的所有路径 .</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> /* * 0.use a TwoWayLinkedList to store the path.when the node can't be path,you should/can delete it. * 1.curSum==exceptedSum:if the lastNode is TreeNode,printPath();delete the node otherwise </div> </li> <li><a href="/article/2076.htm" title="Netty学习笔记" target="_blank">Netty学习笔记</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/netty/1.htm">netty</a> <div>本文是阅读以下两篇文章时: http://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html http://seeallhearall.blogspot.com/2012/06/netty-tutorial-part-15-on-channel.html 我的一些笔记 ===</div> </li> <li><a href="/article/2203.htm" title="js获取项目路径" target="_blank">js获取项目路径</a> <span class="text-muted">cngolon</span> <a class="tag" taget="_blank" href="/search/js/1.htm">js</a> <div>//js获取项目根路径,如: http://localhost:8083/uimcardprj function getRootPath(){     //获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp     var curWwwPath=window.document.locati</div> </li> <li><a href="/article/2330.htm" title="oracle 的性能优化" target="_blank">oracle 的性能优化</a> <span class="text-muted">cuishikuan</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/SQL+Server/1.htm">SQL Server</a> <div>   在网上搜索了一些Oracle性能优化的文章,为了更加深层次的巩固[边写边记],也为了可以随时查看,所以发表这篇文章。     1.ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。(这点本人曾经做过实例验证过,的确如此哦!</div> </li> <li><a href="/article/2457.htm" title="Shell变量和数组使用详解" target="_blank">Shell变量和数组使用详解</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a><a class="tag" taget="_blank" href="/search/%E5%8F%98%E9%87%8F/1.htm">变量</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84/1.htm">数组</a> <div>Shell 变量 定义变量时,变量名不加美元符号($,PHP语言中变量需要),如: your_name="w3cschool.cc" 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则: 首个字符必须为字母(a-z,A-Z)。 中间不能有空格,可以使用下划线(_)。 不能使用标点符号。 不能使用ba</div> </li> <li><a href="/article/2584.htm" title="编程中的一些概念,KISS、DRY、MVC、OOP、REST" target="_blank">编程中的一些概念,KISS、DRY、MVC、OOP、REST</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/REST/1.htm">REST</a> <div>KISS、DRY、MVC、OOP、REST (1)KISS是指Keep It Simple,Stupid(摘自wikipedia),指设计时要坚持简约原则,避免不必要的复杂化。 (2)DRY是指Don't Repeat Yourself(摘自wikipedia),特指在程序设计以及计算中避免重复代码,因为这样会降低灵活性、简洁性,并且可能导致代码之间的矛盾。 (3)OOP 即Object-Orie</div> </li> <li><a href="/article/2711.htm" title="[Android]设置Activity为全屏显示的两种方法" target="_blank">[Android]设置Activity为全屏显示的两种方法</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/Activity/1.htm">Activity</a> <div>1. 方法1:AndroidManifest.xml 里,Activity的 android:theme  指定为" @android:style/Theme.NoTitleBar.Fullscreen" 示例:   <application      </div> </li> <li><a href="/article/2838.htm" title="solrcloud 部署方式比较" target="_blank">solrcloud 部署方式比较</a> <span class="text-muted">eksliang</span> <a class="tag" taget="_blank" href="/search/solrCloud/1.htm">solrCloud</a> <div>solrcloud 的部署其实有两种方式可选,那么我们在实践开发中应该怎样选择呢? 第一种:当启动solr服务器时,内嵌的启动一个Zookeeper服务器,然后将这些内嵌的Zookeeper服务器组成一个集群。  第二种:将Zookeeper服务器独立的配置一个集群,然后将solr交给Zookeeper进行管理   谈谈第一种:每启动一个solr服务器就内嵌的启动一个Zoo</div> </li> <li><a href="/article/2965.htm" title="Java synchronized关键字详解" target="_blank">Java synchronized关键字详解</a> <span class="text-muted">gqdy365</span> <a class="tag" taget="_blank" href="/search/synchronized/1.htm">synchronized</a> <div>转载自:http://www.cnblogs.com/mengdd/archive/2013/02/16/2913806.html 多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题。 同步机制可以使用synchronized关键字实现。 当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。 当s</div> </li> <li><a href="/article/3092.htm" title="js实现登录时记住用户名" target="_blank">js实现登录时记住用户名</a> <span class="text-muted">hw1287789687</span> <a class="tag" taget="_blank" href="/search/%E8%AE%B0%E4%BD%8F%E6%88%91/1.htm">记住我</a><a class="tag" taget="_blank" href="/search/%E8%AE%B0%E4%BD%8F%E5%AF%86%E7%A0%81/1.htm">记住密码</a><a class="tag" taget="_blank" href="/search/cookie/1.htm">cookie</a><a class="tag" taget="_blank" href="/search/%E8%AE%B0%E4%BD%8F%E7%94%A8%E6%88%B7%E5%90%8D/1.htm">记住用户名</a><a class="tag" taget="_blank" href="/search/%E8%AE%B0%E4%BD%8F%E8%B4%A6%E5%8F%B7/1.htm">记住账号</a> <div>在页面中如何获取cookie值呢? 如果是JSP的话,可以通过servlet的对象request 获取cookie,可以 参考:http://hw1287789687.iteye.com/blog/2050040 如果要求登录页面是html呢?html页面中如何获取cookie呢? 直接上代码了 页面:loginInput.html 代码: <!DOCTYPE html PUB</div> </li> <li><a href="/article/3219.htm" title="开发者必备的 Chrome 扩展" target="_blank">开发者必备的 Chrome 扩展</a> <span class="text-muted">justjavac</span> <a class="tag" taget="_blank" href="/search/chrome/1.htm">chrome</a> <div>Firebug:不用多介绍了吧https://chrome.google.com/webstore/detail/bmagokdooijbeehmkpknfglimnifench ChromeSnifferPlus:Chrome 探测器,可以探测正在使用的开源软件或者 js 类库https://chrome.google.com/webstore/detail/chrome-sniffer-pl</div> </li> <li><a href="/article/3346.htm" title="算法机试题" target="_blank">算法机试题</a> <span class="text-muted">李亚飞</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E8%AF%95%E9%A2%98/1.htm">机试题</a> <div>       在面试机试时,遇到一个算法题,当时没能写出来,最后是同学帮忙解决的。        这道题大致意思是:输入一个数,比如4,。这时会输出:           &n</div> </li> <li><a href="/article/3473.htm" title="正确配置Linux系统ulimit值" target="_blank">正确配置Linux系统ulimit值</a> <span class="text-muted">字符串</span> <a class="tag" taget="_blank" href="/search/ulimit/1.htm">ulimit</a> <div>在Linux下面部 署应用的时候,有时候会遇上Socket/File: Can’t open so many files的问题;这个值也会影响服务器的最大并发数,其实Linux是有文件句柄限制的,而且Linux默认不是很高,一般都是1024,生产服务器用 其实很容易就达到这个数量。下面说的是,如何通过正解配置来改正这个系统默认值。因为这个问题是我配置Nginx+php5时遇到了,所以我将这篇归纳进</div> </li> <li><a href="/article/3600.htm" title="hibernate调用返回游标的存储过程" target="_blank">hibernate调用返回游标的存储过程</a> <span class="text-muted">Supanccy2013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/DAO/1.htm">DAO</a><a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a><a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a> <div>注:原创作品,转载请注明出处。     上篇博文介绍的是hibernate调用返回单值的存储过程,本片博文说的是hibernate调用返回游标的存储过程。     此此扁博文的存储过程的功能相当于是jdbc调用select 的作用。 1,创建oracle中的包,并在该包中创建的游标类型。 ---创建oracle的程</div> </li> <li><a href="/article/3727.htm" title="Spring 4.2新特性-更简单的Application Event" target="_blank">Spring 4.2新特性-更简单的Application Event</a> <span class="text-muted">wiselyman</span> <a class="tag" taget="_blank" href="/search/application/1.htm">application</a> <div>1.1 Application Event Spring 4.1的写法请参考10点睛Spring4.1-Application Event 请对比10点睛Spring4.1-Application Event 使用一个@EventListener取代了实现ApplicationListener接口,使耦合度降低; 1.2 示例 包依赖 <p</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>