流媒体协议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/1833738291426455552.htm" title="调研App里的WebDAV功能@FE File Explorer 文件管理器(二)" target="_blank">调研App里的WebDAV功能@FE File Explorer 文件管理器(二)</a> <span class="text-muted">jaminezhong</span> <div>今天我又淘到一款特容易链接WebDAV的软件:FEFileExplorer我觉得它最大好处是在Mac版电脑、Android、iOS上,一个全功能的文件上管理app。而且还可以在您的iOS、安卓和Mac电脑上使用流媒体方式播放网络共享、NAS里面的视频和音乐。FEFileExplorerPro+穿越派=个人私有云需要穿越派的,在这里下:https://www.huluer.com/usercente</div> </li> <li><a href="/article/1833569302553391104.htm" title="推荐项目:Monibuca(m7s)—— 开源直播服务器框架的明日之星" target="_blank">推荐项目:Monibuca(m7s)—— 开源直播服务器框架的明日之星</a> <span class="text-muted">陶真蔷Scott</span> <div>推荐项目:Monibuca(m7s)——开源直播服务器框架的明日之星monibucaMonibucaisaModularized,ExtensibleframeworkforbuildingStreamingServer项目地址:https://gitcode.com/gh_mirrors/mo/monibuca随着在线视频和流媒体服务的日益普及,高质量且灵活的直播服务器框架成为了开发者眼中的瑰宝</div> </li> <li><a href="/article/1833377741844606976.htm" title="从吉利星越车名的发布,看吉利新车宣传的套路" target="_blank">从吉利星越车名的发布,看吉利新车宣传的套路</a> <span class="text-muted">wakechy</span> <div>这两天,吉利代号为FY11的车型终于公布了正式的中文名称——星越,车型外观采用类似宝马X6式的溜背设计,很是吸睛,令人相信会是吉利又一辆走量的车型。今天就借星越中文名发布这个契机,聊聊吉利这几年的营销宣传方式,或者说营销宣传套路。首先,在新车上市前一年的某个时刻,你会发现微信公众号,今日头条等平台,无论是自媒体账号,还是主流媒体账号,忽然之间出现了吉利某款车型的概念图。比如现在这款星越,早在201</div> </li> <li><a href="/article/1833125384351215616.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%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>视频的容器格式和编码格式是视频文件的两个核心概念,它们相互关联但具有不同的功能。以下是详细的解释:1.容器格式(ContainerFormat)容器格式,又称封装格式,指的是视频文件的外壳或容器,它用于封装视频、音频、字幕、元数据(如标题、章节信息等)等各种流媒体内容。容器格式本质上决定了文件的扩展名。常见的容器格式:MP4:支持多种编解码器,广泛用于互联网和移动设备。MKV:支持多轨音频、字幕,</div> </li> <li><a href="/article/1833011783334457344.htm" title="Android平台轻量级RTSP服务之GStreamer还是SmartRtspServer" target="_blank">Android平台轻量级RTSP服务之GStreamer还是SmartRtspServer</a> <span class="text-muted">音视频牛哥</span> <a class="tag" taget="_blank" href="/search/%E5%A4%A7%E7%89%9B%E7%9B%B4%E6%92%ADSDK/1.htm">大牛直播SDK</a><a class="tag" taget="_blank" href="/search/Android/1.htm">Android</a><a class="tag" taget="_blank" href="/search/RTSP%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">RTSP服务器</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%8D%93rtsp%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">安卓rtsp服务器</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/rtsp%E6%9C%8D%E5%8A%A1/1.htm">rtsp服务</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%8D%93%E5%90%AF%E5%8A%A8rtsp%E6%9C%8D%E5%8A%A1/1.htm">安卓启动rtsp服务</a><a class="tag" taget="_blank" href="/search/android%E7%BD%91%E7%BB%9C%E6%91%84%E5%83%8F%E5%A4%B4/1.htm">android网络摄像头</a><a class="tag" taget="_blank" href="/search/android%E5%86%85%E7%BD%91%E9%87%87%E9%9B%86%E6%91%84%E5%83%8F%E5%A4%B4/1.htm">android内网采集摄像头</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E7%89%9B%E7%9B%B4%E6%92%ADSDK/1.htm">大牛直播SDK</a> <div>技术背景Android上启动一个轻量级RTSP服务,让Android终端像网络摄像头一样提供个外部可供RTSP拉流的服务,在内网小并发又不希望部署单独流媒体服务的场景下非常适用,在Android终端实现这样的流媒体服务,决定了,只能是轻量级的服务。可以通过集成第三方库或编写自定义的RTSP服务器代码来实现这一功能。今天我们介绍两种方案,一种是GStreamer,另外一种,大牛直播SDK的Smart</div> </li> <li><a href="/article/1832875630803382272.htm" title="ios流媒体直播整个框架介绍(HLS、RTSP)" target="_blank">ios流媒体直播整个框架介绍(HLS、RTSP)</a> <span class="text-muted">少一些浮躁__</span> <a class="tag" taget="_blank" href="/search/IOS/1.htm">IOS</a> <div>一、HTTP(WebService)基于HTTP的渐进下载ProgressiveDownload流媒体播放仅是在完全下载后再播放模式基础上做了一些小的改进。与下载播放模式中必须等待整个文件下载完毕后才能开始播放不同,渐进下载客户端在开始播放之前仅需等待一段较短的时间用于下载和缓冲该媒体文件最前面的一部分数据,之后便可以一边下载一边播放。在正式开始播放之前的这一小段缓冲应使得后续即使在网络较为拥塞的</div> </li> <li><a href="/article/1832841469266849792.htm" title="MediaStream 的媒体流对象 (stream) 和流媒体轨道 (track) 详解" target="_blank">MediaStream 的媒体流对象 (stream) 和流媒体轨道 (track) 详解</a> <span class="text-muted">Wu Youlu</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>navigator.mediaDevices.getUserMedia和MediaStream是实时音视频处理的重要API。通过这些API,可以从摄像头、麦克风或其他设备捕获音视频流,应用于视频通话、录制等场景。本文将介绍navigator.mediaDevices.getUserMedia的参数配置、MediaStream的传参、属性和方法,配合详细的代码示例,特别是如何动态添加和移除音视频轨道</div> </li> <li><a href="/article/1832274655365394432.htm" title="Kafka,构建TB级异步消息系统" target="_blank">Kafka,构建TB级异步消息系统</a> <span class="text-muted">Fern977</span> <a class="tag" taget="_blank" href="/search/%E7%89%9B%E5%AE%A2%E8%AE%BA%E5%9D%9B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">牛客论坛学习笔记</a><a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a> <div>1.阻塞队列BlockingQueue解决线程通信的问题阻塞方法:put、take。生产者消费者模式生产者:产生数据的线程消费者:使用数据的线程实现类ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue、SynchronousQueue、DelayQueue等2.Kafka入门Kafka简介Kafka是一个分布式的流媒体平台应用:消息</div> </li> <li><a href="/article/1832270625805266944.htm" title="仿论坛项目--Kafka,构建TB级异步消息系统" target="_blank">仿论坛项目--Kafka,构建TB级异步消息系统</a> <span class="text-muted">HUT_Tyne265</span> <a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a> <div>阻塞队列•BlockingQueue解决线程通信的问题。阻塞方法:put、take。•生产者消费者模式生产者:产生数据的线程。消费者:使用数据的线程。•实现类ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue、SynchronousQueue、DelayQueue等。Kafka入门•Kafka简介Kafka是一个分布式的流媒体平台。</div> </li> <li><a href="/article/1832179346341720064.htm" title="25万人,30个亿,割韭菜,割到了体育界" target="_blank">25万人,30个亿,割韭菜,割到了体育界</a> <span class="text-muted">黑熊爱折腾</span> <div>黑熊/文0125万人被骗30个亿。其实在今年整个投资市场来说,并不是什么特别大的案子。30亿的涉案资金,在今年的网贷爆雷事件中,已经没有什么新闻价值了。恐怕只有上百亿的涉案资金量,主流媒体才会提起一点点的兴趣吧。不过,今天,我之所以想写一篇,关于25万人被骗30个亿的文章。原因就在于,这次,这25万被割韭菜的人,竟全都是体育界的人,而我自己,差点就变成了众多受害者之一。02之前一直关注我的朋友知道</div> </li> <li><a href="/article/1831387171211276288.htm" title="推荐书籍:FFmpeg从入门到精通" target="_blank">推荐书籍:FFmpeg从入门到精通</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>本书是一本介绍FFmpeg的实战技术指南,全书共10章,分为两个部分。第一部分部分(第1~7章)为FFmpeg的命令行使用篇,介绍了FFmpeg的基础组成部分、FFmpeg工具使用、FFmpeg的封装操作、FFmpeg的转码操作、FFmpeg的流媒体操作、FFmpeg的滤镜操作、FFmpeg的设备操作。第二部分(第8~10章)为FFmpeg的API使用篇,介绍了FFmpeg封装部分的API使用操作</div> </li> <li><a href="/article/1831167474188513280.htm" title="Qt/C++ 音视频开发 - FFmpeg保存裸流" target="_blank">Qt/C++ 音视频开发 - FFmpeg保存裸流</a> <span class="text-muted">鱼弦</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E6%97%B6%E4%BB%A3/1.htm">人工智能时代</a><a class="tag" taget="_blank" href="/search/qt/1.htm">qt</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>Qt/C++音视频开发-FFmpeg保存裸流介绍FFmpeg是一个开源的多媒体框架,能够处理音频、视频及其他多媒体文件和流。裸流指的是未经封装处理的原始音视频数据。使用FFmpeg保存裸流,可以通过高效的方式处理并存储音视频数据。应用使用场景实时流媒体传输:在没有时间进行复杂封装的情况下,将音视频数据直接传输。低延迟应用:减少因为封装产生的冗余数据和延迟。数据分析:对原始音视频数据进行深入分析时,</div> </li> <li><a href="/article/1830758685303992320.htm" title="只需四步轻松教你绕过校园WiFi认证上网!" target="_blank">只需四步轻松教你绕过校园WiFi认证上网!</a> <span class="text-muted">2401_85752865</span> <a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/php/1.htm">php</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/%E5%BE%AE%E4%BF%A1/1.htm">微信</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a> <div>仅适用于安卓/鸿蒙/澎湃系统/win电脑!打开软件注册账号,使用WiFi连接到校园网,不验证,返回软件,点击连接即可WiFi助手是一款专为学生设计的高效软件,旨在提升网络连接的速度与稳定性,减少延迟,从而优化日常的在线活动体验。不论是在线游戏、流媒体观影还是软件下载,它都能为你提供便利。这款软件的一大亮点是它能够简化复杂的校园网登录过程,为用户创造一个无障碍的上网环境。传统的校园网验证往往复杂且耗</div> </li> <li><a href="/article/1830502580850094080.htm" title="SRS:流媒体服务器如何实现负载均衡" target="_blank">SRS:流媒体服务器如何实现负载均衡</a> <span class="text-muted">音视频开发老马</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/%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/Android%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/1.htm">Android音视频开发</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/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</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%E9%A2%91%E7%BC%96%E8%A7%A3%E7%A0%81/1.htm">视频编解码</a> <div>当我们的业务超过单台流媒体服务器的承受能力,就会遇到负载均衡问题,一般我们会在集群中提供这种能力,但实际上集群并非是唯一的实现方式。有时候负载均衡还会和服务发现等时髦词汇联系起来,而云服务的LoadBalancer无疑不可回避,因此,这个问题其实相当复杂,以至于大家会在多个场合询问这个问题,我打算系统地阐述这个问题。如果你已经知道了以下问题的所有答案,并且深刻了解背后的原因,那么你可以不用看这篇文</div> </li> <li><a href="/article/1830478002589364224.htm" title="FFmpeg源码解析系列(一)目录和编译" target="_blank">FFmpeg源码解析系列(一)目录和编译</a> <span class="text-muted">头快撑不住了</span> <a class="tag" taget="_blank" href="/search/FFmpeg/1.htm">FFmpeg</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/1.htm">源码解析</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><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/%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86/1.htm">图像处理</a> <div>引言当我们欣赏一部电影、观看一段视频或者享受一首音乐时,很少有人会停下来思考这一切是如何呈现在我们眼前的。然而,在每一帧的画面、每一秒的声音背后,有着一个不可思议的技术世界这个充满音视频的世界的核心之一,就是FFmpeg。FFmpeg是一个强大而复杂的多媒体处理工具,它为视频和音频编解码、流媒体传输、格式转换等提供了优秀的解决方案。在这个系列博客中,我们将深入探讨FFmpeg源码,解析它的工作原理</div> </li> <li><a href="/article/1830115233121792000.htm" title="pcma和pcmu pcm编码_GitHub - mackenbaron/EasyAACEncoder: 简单、高效、稳定的开源音频编码库,支持将各种音频数据(G.711A/PCMA、G.711U/P..." target="_blank">pcma和pcmu pcm编码_GitHub - mackenbaron/EasyAACEncoder: 简单、高效、稳定的开源音频编码库,支持将各种音频数据(G.711A/PCMA、G.711U/P...</a> <span class="text-muted">CLOUDU</span> <a class="tag" taget="_blank" href="/search/pcma%E5%92%8Cpcmu/1.htm">pcma和pcmu</a><a class="tag" taget="_blank" href="/search/pcm%E7%BC%96%E7%A0%81/1.htm">pcm编码</a> <div>EasyAACEncoderEasyAACEncoder是EasyDarwin开源流媒体服务团队整理、开发的一款音频转码到AAC的工具库,目前支持G711a/G711u/G726/PCM等音频格式的转码,跨平台,支持Windows/Linux/arm.调用示例testEasyAACEncoder:通过EasyAACEncoderAPI对G711A/G711U/G726进行AAC转码;Windows</div> </li> <li><a href="/article/1829912547961171968.htm" title="浏览器播放h.265 rtsp流媒体解决方案" target="_blank">浏览器播放h.265 rtsp流媒体解决方案</a> <span class="text-muted">LiveWeb视频管理平台</span> <a class="tag" taget="_blank" href="/search/h.265/1.htm">h.265</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</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/web%E6%92%AD%E6%94%BE%E5%99%A8/1.htm">web播放器</a><a class="tag" taget="_blank" href="/search/h.266/1.htm">h.266</a> <div>近期研究在VUE中播放RTSP实时视频,客户要求延迟低于300毫秒,并且要求支持多路同时播放,支持H.265格式视频,比较了下目前市场上常见的几种方案,以供大家参考!一、海康威视官方WEB解决方案:海康威视官方提供了两种WEB解决方案,即无插件方案和有插件方案。1.无插件方案,实际采用的是服务器转码推流的方式,因为需要转码两次,导致延迟比较高,多路播放或者播放高清视频容易卡顿或者花屏,无法满足客户</div> </li> <li><a href="/article/1829502218257133568.htm" title="前端播放RTSP视频流,使用FLV请求RTSP视频流播放(Vue项目,在Vue中使用插件flv.js请求RTSP视频流播放)" target="_blank">前端播放RTSP视频流,使用FLV请求RTSP视频流播放(Vue项目,在Vue中使用插件flv.js请求RTSP视频流播放)</a> <span class="text-muted">北城笑笑</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/VueJS/1.htm">VueJS</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a> <div>简述:在浏览器中请求RTSP视频流并进行播放时,直接使用原生的浏览器API是行不通的,因为它们不支持RTSP协议。为了解决这个问题,开发者通常会选择使用像flv.js这样的库,它专为在浏览器中播放FLV和其他流媒体格式设计。然而,flv.js本身并不直接支持RTSP,这意味着我们需要一个额外的步骤来桥接RTSP和flv.js。这里来记录一下详细介绍:在Vue中使用flv.js插件播放RTSP视频流</div> </li> <li><a href="/article/1829258173807161344.htm" title="go srs 流媒体服务器_Go语言实现的流媒体服务器开发框架" target="_blank">go srs 流媒体服务器_Go语言实现的流媒体服务器开发框架</a> <span class="text-muted">西风吹浮华</span> <a class="tag" taget="_blank" href="/search/go/1.htm">go</a><a class="tag" taget="_blank" href="/search/srs/1.htm">srs</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/1.htm">流媒体服务器</a> <div>Monibucamonibuca.com背景市面上的流媒体服务器不可谓不多,从本人的第一份工作起,就一直接触和研究了形形色色的流媒体服务器,从最早的FCS(全称FlashCommunicationServer),后来改名为FMS(全称FlashMediaServer),到Red5(java语言开发),到CrtmpServer(C++开发),让我对流媒体服务器的基本原理有了深刻的认识。当时本人痴迷C</div> </li> <li><a href="/article/1829254516969664512.htm" title="强大的Monibuca RTMP插件:实时流媒体处理的新星" target="_blank">强大的Monibuca RTMP插件:实时流媒体处理的新星</a> <span class="text-muted">颜殉瑶Nydia</span> <div>强大的MonibucaRTMP插件:实时流媒体处理的新星在现代数字时代,实时流媒体已经成为各种在线活动的核心部分,从在线会议、直播游戏到远程教育等不胜枚举。Monibuca是一个轻量级、高效的开源实时通信系统,而它的RTMP插件更是为实时流媒体传输添加了强大的翅膀。本文将深入探讨这个项目的背景、技术特性以及其应用场景,以期吸引更多开发者和使用者。MonibucaRTMP插件是什么?Monibuca</div> </li> <li><a href="/article/1828760076794556416.htm" title="Qt/C++ 音视频开发 - VLC 解码播放(代码+部署测试)" target="_blank">Qt/C++ 音视频开发 - VLC 解码播放(代码+部署测试)</a> <span class="text-muted">鱼弦</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E6%97%B6%E4%BB%A3/1.htm">人工智能时代</a><a class="tag" taget="_blank" href="/search/qt/1.htm">qt</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>Qt/C++音视频开发-VLC解码播放介绍Qt是一个跨平台的C++应用程序框架,广泛用于GUI程序和跨平台应用。VLC是一个开源的跨平台多媒体播放器及框架,可以播放大多数多媒体文件及流媒体协议。结合两者可以实现强大的音视频处理功能。在Qt/C++项目中集成VLC可以实现高效的音视频解码和播放功能,从而增强了应用的多媒体处理能力。应用使用场景多媒体播放器:开发支持多种格式的音视频播放器。实时流媒体播</div> </li> <li><a href="/article/1828715058197262336.htm" title="【GB28181】 SDP 报文内容(UDP、TCP主动、TCP被动)" target="_blank">【GB28181】 SDP 报文内容(UDP、TCP主动、TCP被动)</a> <span class="text-muted">废人一枚</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/GB28181/1.htm">GB28181</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/GB28181/1.htm">GB28181</a><a class="tag" taget="_blank" href="/search/SDP/1.htm">SDP</a> <div>GB28181SDP报文UDP预览回放下载TCP主动预览回放下载TCP被动预览回放下载根据《GB/T28181—2016》4.3.1、5.2的要求,SIP和流媒体都应该支持TCP和UDP两种方式以下是抓取GB28181分别在视频预览、回放和下载时交互的SDP信息。UDP预览v=0o=3402000000132000001100INIP4192.168.10.128s=Playc=INIP4192.</div> </li> <li><a href="/article/1828387074319937536.htm" title="《Java核心技术:卷I 基础知识》第1章 Java 程序设计概述 阅读与重点标记" target="_blank">《Java核心技术:卷I 基础知识》第1章 Java 程序设计概述 阅读与重点标记</a> <span class="text-muted">EdwardPan1999</span> <a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>第1章Java程序设计概述1996年Java第一次发布就引起了人们的极大兴趣。关注Java的人士不仅限于计算机出版界,还有诸如《纽约时报》《华盛顿邮报》《商业周刊》这样的主流媒体。Java是第一种也是唯一一种在NationalPublicRadio(全国公共广播电视台)上占用了10分钟时间来进行介绍的程序设计语言,并且还得到了$100000000的风险投资基金。这些基金全部用来支持用这种特别的计算</div> </li> <li><a href="/article/1828127473569722368.htm" title="Centos安装netcat" target="_blank">Centos安装netcat</a> <span class="text-muted">羋学僧</span> <div>Centos安装netcatnetcat是网络工具中的瑞士军刀,它能通过TCP和UDP在网络中读写数据。通过与其他工具结合和重定向,你可以在脚本中以多种方式使用它。使用netcat命令所能完成的事情令人惊讶。netcat所做的就是在两台电脑之间建立链接并返回两个数据流,在这之后所能做的事就看你的想像力了。你能建立一个服务器,传输文件,与朋友聊天,传输流媒体或者用它作为其它协议的独立客户端。安装下载</div> </li> <li><a href="/article/1827934744805994496.htm" title="破解青少年心理健康难题亟需整体社会性方案2023-05-10" target="_blank">破解青少年心理健康难题亟需整体社会性方案2023-05-10</a> <span class="text-muted">清语宛如</span> <div>破解青少年心理健康难题亟需整体社会性方案原创本刊编辑部人民教育2023-05-0914:56发表于北京人民教育《人民教育》杂志官方订阅号。《人民教育》是教育部主管、中国教育报刊社主办的全国性教育主流媒体,拥有73年历史。定位“思想高地、行动智库”,宣传解读党和国家教育方针政策,引导社会教育观念,提供专业解决方案,推动教育现代化。2041篇原创内容公众号心理健康与人的生活质量、智力水平,以及幸福和美</div> </li> <li><a href="/article/1827622928594857984.htm" title="RK3588——网口实时传输视频" target="_blank">RK3588——网口实时传输视频</a> <span class="text-muted">张飞飞飞飞飞</span> <a class="tag" taget="_blank" href="/search/RK3588/1.htm">RK3588</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a> <div>由于通过流媒体服务器传输画面延迟太高的问题,不知道是没有调试到合适的参数还是其他什么问题。诞生了这篇博客。RK3588板端上接摄像头,采集画面,通过网口实时传输给上位机并显示。第一代版本RK3588代码importcv2importsocketimportstruct#配置SERVER_IP='192.168.137.1'#上位机的IP地址PORT=5000#端口号#创建一个socket对象cli</div> </li> <li><a href="/article/1826958660929155072.htm" title="ffmpeg.exe命令行参数的简单介绍" target="_blank">ffmpeg.exe命令行参数的简单介绍</a> <span class="text-muted">香菇滑稽之谈</span> <a class="tag" taget="_blank" href="/search/FFmpeg%E5%91%BD%E4%BB%A4%E8%A1%8C/1.htm">FFmpeg命令行</a><a class="tag" taget="_blank" href="/search/ffmpeg/1.htm">ffmpeg</a><a class="tag" taget="_blank" href="/search/ffmpeg%E5%91%BD%E4%BB%A4%E8%A1%8C/1.htm">ffmpeg命令行</a><a class="tag" taget="_blank" href="/search/ffmpeg%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">ffmpeg音视频</a><a class="tag" taget="_blank" href="/search/ffmpeg.exe/1.htm">ffmpeg.exe</a> <div>关于ffmpeg.exeffmpeg.exe是一个开源的多媒体处理工具,广泛应用于音视频的录制、转换、流媒体处理等领域。它由FFmpeg项目开发,支持多种音视频格式,并提供了丰富的命令行选项,使其功能强大且灵活。功能应用格式转换:支持几乎所有常见的音视频格式之间的转换。视频录制:可以从摄像头或屏幕录制视频。音频提取:可以从视频文件中提取音频,并保存为单独的音频文件。视频剪辑:可以对视频进行剪辑,截</div> </li> <li><a href="/article/1826356800404942848.htm" title="利用命令行从youtube下载影片,并用huggingface的大语言模型翻译成中文" target="_blank">利用命令行从youtube下载影片,并用huggingface的大语言模型翻译成中文</a> <span class="text-muted">SteveMiller</span> <a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/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><a class="tag" taget="_blank" href="/search/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/1.htm">自然语言处理</a> <div>今天,从网络流媒体上下载字幕,并把它翻译成各种语言是一个非常常规的操作。我创建了一个工作流程。可以根着这个工作流程,从网上先下载影片,然后转出字幕,最后再做翻译。https://github.com/victorspaceRMW/download-Youtube-with-yt-dlp-and-translate-with-HuggingFace-s-whisper-model/tree/main</div> </li> <li><a href="/article/1768627564735168512.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/%E6%9E%B6%E6%9E%84/1.htm">架构</a> <div>直播中的协议与格式在直播系统中,协议和格式的选择对于传输效率、画面质量和用户体验都至关重要。以下是直播中常见的协议与格式:协议RTSP(RealTimeStreamingProtocol)RTSP是一个网络流媒体协议,常用于视频监控和IPTV等场景。它本身并不传输数据,而是充当媒体服务器与客户端之间的通信协议。RTSP支持TCP和UDP传输,通常使用RTP(Real-timeTransportPr</div> </li> <li><a href="/article/1766425416253399040.htm" title="2020-06-05《学习究竟是什么》学习总结清单5" target="_blank">2020-06-05《学习究竟是什么》学习总结清单5</a> <span class="text-muted">魔域之晶</span> <div>你不是在找答案,而是在做研究。通过各种方式获取信息,然后以我为主,做出自己的判断,这是做研究。三种找书方法:看推荐,看主流媒体的书评,追作者。思维导图不是整理和记忆工具,而是思考工具。思维导图更高级的用法是决策和判断。借助智能化记录和储存信息,大脑用来思考。强力研读笔记法第一,清晰表现每一章的逻辑脉络;第二,带走书中所有的亮点;第三,大量自己的看法和心得;第四,发现这本书和以前读过的其他书或文章的</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>