初识 HLS 协议

有一天偶然从公司 KM 的推送中看到 HLS 协议这么一个东西, 觉得有意思而且之前在上一家公司时, 也听闻这么一个技术, 但是一直没有机会接触, 于是乎找了找资料来了解下, 本文不准备讨论实现及应用, 只做简单了解.

HLS

HLS(http live stream), Apple 公司于2009年提出的基于 HTTP 协议的流媒体协议.
支持度: iOS、MacOSX 及 Apple TV 原生支持; Android 3.0+ 支持.

它的传输内容包括两部份, 一是 M3U8 描述文件, 而是 TS 媒体文件.

流媒体协议

  • RTP/RTCP
  • RTMP (各大直播平台常用的, 在 Flash 及 AIR 中有很好的支持)
  • DASH
  • HLS

HLS 特点

  • 支持直播和点播实现
  • 支持用户拖放、回放等操作
  • 支持点播资源的加密和鉴权(借助HTTPS)
  • 支持流的智能实时切换(针对网络带宽、设备)

HLS 优缺点

HLS 优点

  • 基于 HTTP
  • 可充分利用 CDN
  • 广播电视标准成熟, 快速进入产业链
  • 普通 Web 服务器即可
  • 自适应码率
  • 广泛的 iOS 平台支持

HLS 缺点

  • 延迟较高
  • PC 平台 HTML5 支持不够成熟, 需要第三方 (比如: Flash 播放)
  • MPEG2-TS 文件协议较复杂
  • 不适合做交互式语音应用

Web 中如何使用

1
2
3
<video controls>
<source src="example.m3u8"></source>
</video>

或者

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

M3U8

M3U8 是 M3U 的 UTF8 编码版本, M3U 是一种列表文件, 只提供一些描述信息.

M3U 文件中可以包含多个 tag, 每个 tag 的功能和属性如下

M3U8 常见 Tag

#EXTM3U
每个 M3U 文件第一行必须是这个 tag, 请标示作用

#EXT-X-MEDIA-SEQUENCE: <sequence>
每一个media URI 在 PlayList 中只有唯一的序号, 相邻之间序号 +1, 一个 media URI 并不是必须要包含的, 如果没有, 默认为 0

#EXTINF: <duration> [, <title>]
duration 指定每个媒体段(ts)的持续时间(秒), 仅对其后面的URI有效, title 是下载资源的 url

#EXT-X-TARGETDURATION: <duration>
指定最大的媒体段时间长(秒). 所以#EXTINF中指定的时间长度必须小于或是等于这个最大值. 这个 tag 在整个 PlayList 文件中只能出现一次 (在嵌套的情况下, 一般有真正 TS url 的 m3u8 才会出现该 tag)

#EXT-X-KEY: <attribute-list>
表示怎么对 media segments 进行解码. 其作用范围是下次该 tag 出现前的所有 media URI, 属性为 NONE 或者 AES-128.
NONE 表示 URI 以及 IV (Initialization Vector) 属性必须不存在; AES-128(Advanced EncryptionStandard)表示 URI 必须存在, IV 可以不存在.
对于 AES-128 的情况, keytag 和 URI 属性共同表示了一个key文件, 通过URI可以获得这个 key, 如果没有IV (Initialization Vector), 则使用序列号作为IV进行编解码, 将序列号的高位赋到 16 个字节的 buffer 中, 左边补 0; 如果有 IV, 则将改值当成 16 个字节的 16 进制数.

#EXT-X-PROGRAM-DATE-TIME
将一个绝对时间或是日期和一个媒体段中的第一个 Sample 相关联, 只对下一个 meida URI 有效
例子: #EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00

#EXT-X-ALLOW-CACHE:<YES|NO>
是否允许做 cache, 这个可以在 PlayList 文件中任意地方出现, 并且最多出现一次, 作用效果是所有的媒体段.

#EXT-X-PLAYLIST-TYPE
提供关于 PlayList 的可变性的信息, 这个对整个 PlayList 文件有效, 是可选的, 格式如下: #EXT-X-PLAYLIST-TYPE: 如果是 VOD, 则服务器不能改变 PlayList 文件; 如果是 EVENT, 则服务器不能改变或是删除 PlayList 文件中的任何部分, 但是可以向该文件中增加新的一行内容.

#EXT-X-ENDLIST
表示 PlayList 的末尾了, 它可以在 PlayList 中任意位置出现, 但是只能出现一个, 但作为直播的, 不需要这个 tag

#EXT-X-STREAM-INF
指定一个包含多媒体信息的 media URI 作为 PlayList, 一般做 M3U8 的嵌套使用, 它只对紧跟后面的 URI 有效

有以下属性:

  • BANDWIDTH: 带宽, 必须有
  • PROGRAM-ID: 该值是一个十进制整数, 惟一地标识一个在 PlayList 文件范围内的特定的描述. 一个 PlayList 文件中可能包含多个有相同 ID 的此 tag
  • CODECS: 不是必须的
  • RESOLUTION: 分辨率
  • AUDIO: 这个值必须和 AUDIO 类别的 EXT-X-MEDIA 标签中 GROUP-ID 属性值相匹配.
  • VIDEO: 同上

一个 m3u8 文件请求例子

由于 PC 端我当前使用的 Chrome 还不支持 HTML5 中用 <video> 标签播放 HLS, 所以我只能是找来了一个视频播放器来打开 M3U8 链接, 接着用 Wireshark 抓包来分析.

顺道一提, HTPP 请求的 raw 数据, 可以在 Wireshark 中通过右键菜单选到 follow => TCP Stream 或者菜单栏中的 Analyze => follow => TCP Steam 来查看.

M3U8 的请求如下:

发出请求

1
2
3
4
GET /live/virgin1/chunklist_w63623014.m3u8 HTTP/1.1
User-Agent: Daum PotPlayer
Host: wow01.105.net
Cache-Control: no-cache

返回 Header

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Date: Fri, 17 Jun 2016 03:35:58 GMT
Content-Type: application/vnd.apple.mpegurl
Accept-Ranges: bytes
Server: WowzaStreamingEngine/4.2.0.01
Cache-Control: no-cache
Content-Length: 213

返回内容

1
2
3
4
5
6
7
8
9
10
11
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:15
#EXT-X-MEDIA-SEQUENCE:1974
#EXTINF:7.64,
media_w63623014_1974.ts
#EXTINF:9.24,
media_w63623014_1975.ts
#EXTINF:0.96,
media_w63623014_1976.ts

请求到 M3U8 文件后, 读取到里面的描述信息, 就会根据信息中的 SEQUENCE 去请求对应的 TS 文件, 假如是直播的话, 则会一直请求这个 M3U8 文件, 但是返回的 SEQUENCE 已经不同了.

请求一个 TS 文件

1
2
3
4
5
6
7
8
9
10
11
12
GET /live/virgin1/media_w63623014_1972.ts HTTP/1.1
User-Agent: VLC/2.2.1 LibVLC/2.2.1
Host: wow01.105.net
Cache-Control: no-cache

HTTP/1.1 200 OK
Date: Fri, 17 Jun 2016 03:35:59 GMT
Content-Type: video/MP2T
Accept-Ranges: bytes
Server: WowzaStreamingEngine/4.2.0.01
Cache-Control: no-cache
Content-Length: 1274640

带嵌套的 M3U8 文件

1
2
3
4
5
6
7
8
9
#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

上边通过设定不同的 BANDWIDTH 可以让设备选择合适的列表进行播放.

TS 文件

TS 文件为传输流文件, 视频编码主要格式 h264/mpeg4, 音频为 acc/MP3. 关于视频编码, 音频编码一类的, 我还不是很清楚, 就大概知道这么回事就好了.

拓展

毕业后一年

由于没有写个人年度总结的习惯, 但是又想记录一些成长经历, 所以从毕业后开始算起吧, 不过现在已经不止毕业后一年, 都已经一年又三个月了呢.

大四:

事情其实应该从去年年初讲起了, 那会大四下学期还没开始, 顺着朋友的推荐, 还没过正月十五, 直接就从家跑到珠海去面试.

有惊无险的, 在面试后的第四天, 收到实习通知, 心里的石头也算是落下来, 这都大四了, 我还没参加过实习, 怎么想心里都挺慌的, 一来各种校招宣讲会都没有去参加, 没有拿到校招实习的机会, 再者, 也没跟老师处好关系, 得不到推荐机会. 在仅参加了鹅厂校招笔试, 然后还因为不会 C++ 直接笔试被刷, 感觉整个人就不好了.

但是话说回来, 在珠海拿到的实习岗位, 是测试工程师, 说得好听点, 是测试开发工程师, 在这个点上, 个人还是很不爽. 好歹在大学主攻的是 Java 编程, 以及 Web 开发, 所以总感觉顿时就掉价了不少, 好在从事的是 Android 相关的, 在这时候, 常用的 Java 经验发挥了作用.

实习

拿到这实习机会, 其实也只是解一解燃眉之急, 然后就等着混到毕业了. (大学里还有几门重修课等着考试).

在这个阶段, 主要工作是 Android 自动化脚本开发, 心里对自动化脚本虽然是讨厌的不行, 但是这时候接触到了不少东西, 从 Android 的一些开发工具, 批处理, 到自动化构建等等, 期间自己也根据别的 Android UI 测试框架对原声 UiAutomator 进行封装改进, 做的比较适合日常工作使用.

工作中流程以及规范等的东西, 值得改进的有好多, 也提了不少意见, 但是实际中, 要在组内推行起来, 并没有想象中简单, 更何况我是这会还只是个实习生, 想法我说出来了, 组长实不实行, 我不太关心, 虽然能做到, 那感觉会很不错.

于是乎, 就这么混到毕业, 然后没啥意外的签了卖身契, 正式开始了在珠海的打工生活.

每天的工作不外乎就是, 写脚本, 接上手机, 然后跑脚本, 脚本出错了, 就调脚本, 枯燥得来也几乎没有挑战, 工作方式还比较落后, 但是这时候, Android 的自动化实现也差不多都这样, 不是随便写个程序就能解决的了的.

这样的生活, 发生转机, 估计就是在加入了校区的攻城狮群吧, 我一直对别人说, 是这个群改变了我, 说得是夸张了点, 但也是在这时候, 我意识到一直写 Android 脚本不是我所追求的东西, 而且事实也证明, 写脚本的绩效还远不如写工具来得好, 这个已经在以前的同事身上证明了.

还没待到2014年底, 心里就开始计划着要跳槽, 离开珠海这家公司, 到广州还是深圳做个 Web 前端开发.

想法是挺美好的, 但是一眨眼, 就到第二年5月份. 在新年初, 工作被调到负责内部测试平台前端开发.

跳槽

(´▽`〃) 这该说是帮我积累了一点前端工作经验了么, 不过过程中也发现, 作为内部测试平台开发, 并且平台还只是作为一个测试组内使用的东西, 很多东西都变得很随意, 需求源源不断, 平台这个东西又是一个旧物, 历史问题很多, 在前端方面几乎也没要求, 拿了 bootstrap 跟 jQuery 就来用, 都是功能优先, 别的靠边站.

终于有了改版计划, 在这时候开始使用 Backbone 来构架前端的 MVC, 也是给了机会继续累积前端经验.

同时也得益于写脚本时工作算不上忙, 经常都是一边在看着电子书, 一边在优哉游哉写脚本程序.

到广州面试了两趟, 都算准备充分, 前端知识、技术等都挺能答上来的, 除了 CSS 的进阶知识有所欠缺, 不过最后还是成功应聘到 Web 前端开发工程师岗位. 但是值得反思的还是, 在 Web 方面的知识还是只偏向前端, 相关的一些网络知识, HTTP协议等, 都知之甚少. 在面试时比较不满意就是这些点.

总结

总结下这一年接触到的比较有价值的东西,

Android 相关:

Web 前端相关:

其它:

PacktPub 出版的 《SDL Game Development》 这本书, 断断续续看了好久好久, 但是对于想从0开始编写一个游戏的人来说, 帮助还是很大的, 有着一些新手所不知道的技巧.

YSlow 试用

YSlow 分析工具的简单试用.
Read more »

构建高性能 Web 的 14 条黄金法则

前几天被问到构建高性能 Web 时, 一时脑抽没反应过来这是个啥, 其实平时做的雪碧图, 以及 js 代码压缩等都属于这块的内容, 当然这只是其中的一部分.


“High Performance Web Sites” 这本书中, 我们可以看到, 它列举了 14 条高性能黄金法则.

  1. 使用更少的 HTTP 请求 (Make Fewer HTTP Requests)
  2. 使用内容分发网络 (Use a Content Delivery Network)
  3. 设置过期 Header (Add an Expires Header)
  4. 使用 gzip 压缩各组件 (Gzip Components)
  5. 把样式表放在文档顶部 (Put Stylesheets at the Top)
  6. 把 JavsScript 代码放在文档底部 (Put Scripts at the Bottom)
  7. 避免使用 CSS 表达式 (Avoid CSS Expressions)
  8. 使用外部的 JavaScript 及 CSS 文件 (Make JavaScript and CSS External)
  9. 减少 DNS 查找 (Reduce DNS Lookups)
  10. 压缩 JavaScript 代码 (Minify JavaScript)
  11. 避免使用重定向 (Avoid Redirects)
  12. 移除重复的脚本 (Remove Duplicate Scripts)
  13. 配置 ETags (Configure ETags)
  14. 使 Ajax 内容可缓存 (Make Ajax Cacheable)

内容较多, 具体还是看书上如何说明, 篇幅不过 200 页, 很快就可以看完, 这里就不细细写出来了.

果壳量子积木[小熊猫, 浣熊]

果壳量子积木, 属于每个人的六一节礼物!
Read more »