整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

html5的video标签实现对m3u8格式视频(HLS)的支持 亲测可用

切图网一个项目切图中遇到的,网页中嵌入视频 理所当然用html5自带的video标签即可实现,也有比较主流的插件videojs,但是这个比较特别播放的视频是m3u8格式的 (这种好像imac safari下用的多),这种情况需要添加额外的支持,好在hls.min.js可以实现。附代码 亲测可用

html5的video标签实现对HLS(m3u8格式)的支持 亲测可用

  1. <script src=“https://cdn.jsdelivr.net/hls.js/latest/hls.min.js”></script>
  2. <video id=“video”></video>
  3. <script>
  4. if(Hls.isSupported()) {
  5. var video = document.getElementById(‘video’);
  6. var hls = new Hls();
  7. hls.loadSource(‘http://www.streambox.fr/playlists/test_001/stream.m3u8’);
  8. hls.attachMedia(video);
  9. hls.on(Hls.Events.MANIFEST_PARSED,function() {
  10. video.play();
  11. });
  12. }
  13. </script>

技术提升美好事物发生的概率。Technologically, for greater probability to be happy

前言

只要在 HTML5 中使用过视频播放的同学对 video 标签一定不会陌生,不过很多同学只使用了 video 的基础功能,实际上 video 拥有强大潜能的,只要姿势正确就能让其拥有超能力。不妨从下面几个场景来逐渐了解下video 未曾被发掘的神秘空间:

  • 清晰度无缝切换
  • 节省视频流量

清晰度无缝切换

点播领域里 mp4 是最普遍、兼容性最好的视频容器,不过 mp4 也有它的局限性,比如常见的清晰度切换,我们是无法像youtube那样做到无缝切换的。我们可以看下普通的mp4播放的网络请求和youtube视频播放的网络请求的区别。

图1.1 普通mp4的下载请求过程

图1.2 Youtube视频下载请求过程

这两张图不难看出,在默认情况下 mp4 使用一次 http 请求所有的视频数据,Youtube 则分次请求。当然这个描述很不专业,但确实形象。造成这种差异的是 video 不支持流式的视频数据,Youtube 采用的是流式的视频容器 webm,而 mp4 是非流式的。那如何解释清楚流式的视频数据呢,从专业的角度三言两语很难说清楚,但用大白话翻译过来就是流式的视频数据支持分段独立播放,非流式的不可以。换句话说一个10M的视频文件,流式的视频可以把0~1M的数据请求回来单独播放,但是非流式的不可以。

上面我们描述了视频格式的不同,接下来我们要说的是第一张图中的视频加载是浏览器来控制的,通过给 video 的 src 属性配置视频地址,触发播放之后浏览器就会开始下载了,JS干涉不了。而 Youtube 的视频加载是通过JS来控制的,各位可以再次看下第二张图的网络请求类型:xhr,足以证明这一点。

上面两点搞清楚之后我们就该说下清晰度切换的事情了。这个需求大家都不陌生,但是直接使用 mp4 格式做无缝清晰度切换,难度还挺大的。先解释下“无缝清晰度切换”的概念:从播放一个分辨率的视频到另一个分辨率且保证画面、声音不停顿的平滑切换过程。了解了这个概念,大家应该知道了用 video 无缝切换 mp4 有多难。一方面,video 是不支持流式的视频格式的,一方面,video 的加载是不受JS控制的。通过切换 video 的 src 属性,必然会导致画面中断、重新请求视频数据等。有的同学想到说利用两个 video 再结合 z-index 来搞,但是当你生成另一个video去加载视频的时候,无法保证两个画面是严格一致的,即使将原来的画面暂停到一个时刻,用另一个视频通过 currentTime 属性与之同步,切换仍然看到画面闪烁,基本无法和 Youtube 无缝切换的体验匹敌。而且还会造成更多流量的浪费,背后的原因大家可以研究下 mp4 容器和 webm 容器的异同,也可以看下视频解码相关的文章。

还有一种方法就是将 mp4 格式统统转码到流式的视频格式比如 hls、webm 等。不过这种看上去可行的方式实际上会带来很大的成本开销,如将大量视频做转码会消耗高昂的机器资源、双倍存储的费用、CDN的双倍费用等等。其实我们也是在这种背景下研究出来新的技术问题解决清晰度无缝切换的。

首先,我们改变对 mp4 视频的播放流程,不再直接使用 video 的 src 来播放,因为我们没有任何可以操作的空间。video不仅支持 src 属性还支持 Blob 对象,我们就是利用后者。播放的流程如下:

图1.3 mp4 视频新播放流程

  1. 来请求 mp4 视频数据,这样可以结合视频 Range 服务,做到精确加载。
  2. 编写解析器将加载回来的部分 mp4 视频数据进行解复用
  3. 将解复用的视频数据转成 fmp4 格式并传递给 MediaSource
  4. 使用 video 进行解码完成播放

然后在做清晰度切换的时候流程如下:

图1.4 mp4视频清晰度切换原理示意图

  1. 播放视频A,过程同上
  2. 在某个时刻,用户切换到播放视频B,首先解析B的索引文件(moov),反向计算mp4的range区间
  3. 加载B的视频区间数据
  4. 解复用
  5. 把数据转换成fmp4格式并传递给MediaSource
  6. 删除A的部分Buffer
  7. 在下一个关键帧自动完成画质的切换

图1.5 mp4视频清晰度切换流程示意图

这个过程看上去比较繁琐,但是所有的操作都是在浏览器端完成,也就是说都是JS来实现的。这样之前说的所有成本问题都不存在,还能做到youtube相同体验的无缝切换。如果大家也想使用这个功能不需要自己再去实现一遍上述流程,可以使用如下代码:

如果对这段代码有什么疑惑,或者想深入了解下它背后是如何实现的,可以参考 Github:https://github.com/bytedance/xgplayer 或 阅读原文:https://techblog.toutiao.com/

节省视频流量

使用 video 的同学基本上都是这样用的,如下:

  1. 利用src属性

2.利用source标签

这样就可以播放视频了,不过前面我们讲过这样使用 video ,视频的加载是受浏览器控制的,可以看下浏览器在视频刚开始播放的时候下载了多少数据:

图2.1 video默认下载截图

我随便找了个视频,大家看下视频总长度是 02:08,在播放到 00:05 的时候,浏览器已经下载到 01:30 了,如果用户终止观看,下载的视频就这样被浪费掉了。当然,如果不断的 seek 也会造成较多的流量浪费。按照我们之前的统计在短视频领域,用户 seek 的频率在 80%,所以这部分流量是可以节省掉的。具体原理如下:

图2.2 播放器加载视频原理

  1. 设置每次加载的数据包大小
  2. 设置预加载时长
  3. 开启加载队列,完成第一次数据包下载,判断缓冲时间和预加载时长是否满足,不满足请求下一个数据包

具体实现代码如下:

这样就实现了视频在播放过程中永远只预加载10秒的数据,进而保证节省流量。

扩展链接,了解超能力西瓜视频是怎样炼成的。

LS 概述

HLS 全称是 HTTP Live Streaming, 是一个由 Apple 公司实现的基于 HTTP 的媒体流传输协议. 他跟 DASH 协议的原理非常类似. 通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件, 然后提供一个配套的媒体列表文件, 提供给客户端, 让客户端顺序地拉取这些媒体文件播放, 来实现看上去是在播放一条流的效果.

由于传输层协议只需要标准的 HTTP 协议, HLS 可以方便的透过防火墙或者代理服务器, 而且可以很方便的利用 CDN 进行分发加速, 并且客户端实现起来也很方便.

HLS 目前广泛地应用于点播和直播领域.

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

直接:

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

或者:

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

下面, 我将会概括性地介绍 HLS 协议的方方面面(并且包括 AES 加密部分的内容), 配合 HLS 的 RFC 食用效果更佳.

HLS 协议详解

hls_arch 上面是 HLS 整体架构图, 可以看出, 总共有三个部分: Server, CDN, Client.

其实, HLS 协议的主要内容是关于 M3U8 这个文本协议的, 其实生成与解析都非常简单. 为了更加直接地说明这一点, 我下面举两个简单的例子:

简单的 Media Playlist:

#EXTM3U

#EXT-X-VERSION:3

#EXT-X-TARGETDURATION:8

#EXT-X-MEDIA-SEQUENCE:2680

 

#EXTINF:7.975,

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

#EXTINF:7.941,

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

#EXTINF:7.975,

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

包含多种比特率的 Master Playlist:

#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
  • 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 里面是同一个流的多个不同版本(如: 分辨率, 码率不同).

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 来指定. 支持的 Media Segment 格式

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 来指定.

免费分享,2022最新最全学习提升资料包,资料内容包括《Andoird音视频开发必备手册+音视频最新学习视频+大厂面试真题+2022最新学习路线图》点击领取FFmpegWebRTCRTMPRTSPHLSRTP播放器-音视频流媒体高级开发

音视频学习交流扣裙788280672

Fragmented MPEG-4

即常提到的 fMP4.

  • RFC: ISOBMFF.
  • Media Initialization Section: ftyp box(包含一个高于 ios6 的 brand), ftypbox 必须紧跟在 moov box 之后. moov box 必须包含一个 trak box(对于每个 fMP4 segment 里面的 traf box, 包含匹配的 track_ID). 每个 trakbox 应该包含一个 sample table, 但是他的 sample count 必须为 0. mvhd box 跟 tkhd 的 duration 必须为 0. mvex box 必须跟在上一个 trak box 后面.
  • 不像普通的 MP4 文件包含一个 moov box(包含 sample tables) 和一个 mdatbox(包含对应的 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.

Packed Audio

  • 一个 Packed Audio Segment 包含编码的 audio samples 和 ID3 tags. 简单的打包到一起, 包含最小的 framing, 并且没有 per-sample timestamp.
  • 支持的 Packed Audio: AAC with ADTS framing [ISO138187], MP3 [ISO138183], AC-3 [AC3], Enhanced AC-3 [AC3].
  • 一个 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.

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 来保证音视频同步.

HLS Playlists

Playlist 文件的格式是起源于 M3U, 并且继承两个 tag: EXTM3U 和 EXTINF 下面的 tags 通过 BNF-style 语法来指定.

  • 一个 Playlist 文件必须通过 URI(.m3u8 或 m3u) 或者 HTTP Content-Type 来识别(application/vnd.apple.mpegurl 或 audio/mpegurl).
  • 换行符可以用 \n 或者 \r\n.
  • 以 # 开头的是 tag 或者注释, 以 #EXT 开头的是 tag, 其余的为注释, 在解析时应该忽略.
  • Playlist 里面的 URI 可以用绝对地址或者相对地址, 如果使用相对地址, 那么是相对于 Playlist 文件的地址.

Attribute Lists

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

Basic Tags

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

  • EXTM3U: 必须在文件的第一行, 标识是一个 Extended M3U Playlist 文件.
  • EXT-X-VERSION: 表示 Playlist 兼容的版本.

Media Segment Tags

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

  • EXTINF: 用于指定 Media Segment 的 duration
  • EXT-X-BYTERANGE: 用于指定 URI 的 sub-range
  • EXT-X-DISCONTINUITY: 表示不连续.
  • EXT-X-KEY: 表示 Media Segment 已加密, 该值用于解密.
  • EXT-X-MAP: 用于指定 Media Initialization Section.
  • EXT-X-PROGRAM-DATE-TIME: 和 Media Segment 的第一个 sample 一起来确定时间戳.
  • EXT-X-DATERANGE: 将一个时间范围和一组属性键值对结合到一起.

Media Playlist Tags

Media Playlist tags 描述 Media Playlist 的全局参数. 同样地, Media Playlist tags 只能出现在 Media Playlist 里面.

  • EXT-X-TARGETDURATION: 用于指定最大的 Media Segment duration.
  • EXT-X-MEDIA-SEQUENCE: 用于指定第一个 Media Segment 的 Media Sequence Number.
  • EXT-X-DISCONTINUITY-SEQUENCE: 用于不同 Variant Stream 之间同步.
  • EXT-X-ENDLIST: 表示结束.
  • EXT-X-PLAYLIST-TYPE: 可选, 指定整个 Playlist 的类型.
  • EXT-X-I-FRAMES-ONLY: 表示每个 Media Segment 描述一个单一的 I-frame.

Master Playlist Tags

Master Playlist tags 定义 Variant Streams, Renditions 和 其他显示的全局参数. Master Playlist tags 只能出现在 Master Playlist 中.

  • EXT-X-MEDIA: 用于关联同一个内容的多个 Media Playlist 的多种 renditions.
  • EXT-X-STREAM-INF: 用于指定一个 Variant Stream.
  • EXT-X-I-FRAME-STREAM-INF: 用于指定一个 Media Playlist 包含媒体的 I-frames.
  • EXT-X-SESSION-DATA: 存放一些 session 数据.
  • EXT-X-SESSION-KEY: 用于解密.

Media or Master Playlist Tags

这里的 tags 可以出现在 Media Playlist 或者 Master Playlist 中. 但是如果同时出现在同一个 Master Playlist 和 Media Playlist 中时, 必须为相同值.

  • EXT-X-INDEPENDENT-SEGMENTS: 表示每个 Media Segment 可以独立解码.
  • EXT-X-START: 标识一个优选的点来播放这个 Playlist.

服务器端与客户端逻辑

以下流程仅供参考, 其实不同的播放器客户端以及服务器端的拉取规则都有很多细节差异.

服务器端逻辑

  • 1、将媒体源切片成 Media Segment, 应该优先从可以高效解码的时间点来进行切片(如: I-frame).
  • 2、为每一个 Media Segment 生成 URI.
  • 3、Server 需要支持 “gzip” 方式压缩文本内容.
  • 4、创建一个 Media Playlist 索引文件, EXT-X-VERSION 不要高于他需要的版本, 来提供更好的兼容性.
  • 5、Server 不能随便修改 Media Playlist, 除了 Append 文本到文件末尾, 按顺序移除 Media Segment URIs, 增长 EXT-X-MEDIA-SEQUENCE 和 EXT-X-DISCONTINUITY-SEQUENCE, 添加 EXT-X-ENDLIST 到文件尾.
  • 6、在最后添加 EXT-X-ENDLIST tag, 来减少 Client reload Playlist 的次数.
  • 7、注意点播与直播服务器不同的地方是, 直播的 m3u8 文件会不断更新, 而点播的 m3u8 文件是不会变的, 只需要客户端在开始时请求一次即可.

客户端逻辑

  • 1、客户端通过 URI 获取 Playlist. 如果是 Master Playlist, 客户端可以选择一个 Variant Stream 来播放.
  • 2、客户端检查 EXT-X-VERSION 版本是否满足.
  • 3、客户端应该忽略不可识别的 tags, 忽略不可识别的属性键值对.
  • 4、加载 Media Playlist file.
  • 5、 播放 Media Playlist file.
  • 6、重加载 Media Playlist file.
  • 7、决定下一次要加载的 Media Segment.

HLS 的优势

  • 客户端支持简单, 只需要支持 HTTP 请求即可, HTTP 协议无状态, 只需要按顺序下载媒体片段即可.
  • 使用 HTTP 协议网络兼容性好, HTTP 数据包也可以方便地通过防火墙或者代理服务器, CDN 支持良好.
  • Apple 的全系列产品支持, 由于 HLS 是苹果提出的, 所以在 Apple 的全系列产品包括 iphone, ipad, safari 都不需要安装任何插件就可以原生支持播放 HLS, 现在, Android 也加入了对 HLS 的支持.
  • 自带多码率自适应, Apple 在提出 HLS 时, 就已经考虑了码流自适应的问题.

HLS 的劣势

  • 相比 RTMP 这类长连接协议, 延时较高, 难以用到互动直播场景.
  • 对于点播服务来说, 由于 TS 切片通常较小, 海量碎片在文件分发, 一致性缓存, 存储等方面都有较大挑战.

改进的 HLS 技术

由于客户端每次请求 TS 或 M3U8 有可能都是一个新的连接请求, 所以, 我们无法有效的标识客户端, 一旦出现问题, 基本无法有效的定位问题, 所以, 一般工业级的服务器都会对传统的 HLS 做一些改进.

这里主要介绍网宿的 Variant HLS 与又拍云的 HLS+.

网宿的 Variant HLS

首先, 我们可以下载一条网宿的 M3U8 文件:

wget http://bililive.kksmg.com/hls/stvd6edb9a6_45b34047833af658bf4945a8/playlist.m3u8

然后, 打开下载得到的 playlist 文件:

#EXTM3U

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

http://bililive.kksmg.com/hls/stvd6edb9a6_45b34047833af658bf4945a8/playlist.m3u8?

wsSession=0105cb4e8fe63bccab511a4a-

149017212774715&wsIPSercert=b80d38c068c9e3634a7ebb2f2bbf9b89&wsMonitor=-1

可以看出这是一个 Master Playlist, 里面嵌套了一层 M3U8, 同时可以看出网宿采用 wsSession 来标识一条播放连接.

又拍云的 HLS+

Variant HLS

首先, 我们可以下载一条又拍云的 M3U8 文件:

wget http://uplive.b0.upaiyun.com/live/loading.m3u8

然后, 打开下载得到的 playlist 文件:

#EXTM3U

#EXT-X-VERSION:3

#EXT-X-ALLOW-CACHE:YES

#EXT-X-MEDIA-SEQUENCE:0

#EXT-X-TARGETDURATION:1

#EXTINF:0.998, no desc

http://183.158.35.12:8080/uplive.b0.upaiyun.com/live/loading-0.ts?

shp_uuid=e4989f34fcab282e21ef1fd2980284cb&shp_ts=1490172420851&shp_cid=17906&shp_pid=3

370578&shp_sip0=127.0.0.1&shp_sip1=183.158.35.12&domain=uplive.b0.upaiyun.com&shp_seqno=0

可以看出又拍云的 HLS+ 也支持这种 Variant HLS 方式来标识一条 HLS 连接, 可以看出, 又拍云使用 uuid 来表示一条 HLS 连接.

HTTP 302

首先, 以 HTTP 302 方式来请求播放地址.

❯ 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

打开 playlist 内容:

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

在跳转之后的地址存放真正的 playlist, 同时, 也能够将 uuid 加入到了连接上.

总地来说, 不管通过哪种方式, 最终我们都能通过一个唯一的 id 来标识一条流, 这样在排查问题时就可以根据这个 id 来定位播放过程中的问题.

HLS 延时分析

HLS 理论延时 = 1 个切片的时长 + 0-1个 td (td 是 EXT-X-TARGETDURATION, 可简单理解为播放器取片的间隔时间) + 0-n 个启动切片(苹果官方建议是请求到 3 个片之后才开始播放) + 播放器最开始请求的片的网络延时(网络连接耗时) 为了追求低延时效果, 可以将切片切的更小, 取片间隔做的更小, 播放器未取到 3 个片就启动播放. 但是, 这些优化方式都会增加 HLS 不稳定和出现错误的风险.

播放未加密的HLS

1、我们知道HLS格式的视频,只有安卓4.0以上才支持,目前基本4.0一下的机子基本可以考虑,不兼容了,所以为了减少工作量,就没有打算使用三方的播放器,就继续使用MediaPlayer来进行播放

2、HLS格式的视频,他是通过一个m3u8文件,然后里面包含若干个TS文件片段,这里有个苹果的官方的一个例子:

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

毕竟这个是苹果研发的所以看官方的格式肯定没错。

3、里面的内容

#EXTM3U

#EXT-X-TARGETDURATION:10

#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:10, no desc

fileSequence0.ts

#EXTINF:10, no desc

fileSequence1.ts

#EXTINF:10, no desc

fileSequence2.ts

#EXTINF:10, no desc

fileSequence3.ts

#EXTINF:10, no desc

fileSequence4.ts

#EXTINF:10, no desc

fileSequence5.ts

#EXTINF:10, no desc

fileSequence6.ts

#EXTINF:10, no desc

fileSequence7.ts

我们可以看到里面他又一个一个ts视频片段,这个一个一个视频片段就是我们需要的播放,那么他是如何被播放器识别播放的呢。

4、其实上面的这些关键的字段都是约定好的,所以在MediaPlayer这个类的native层,会去按照规定好的字段去解析这个m3u8文件,那么他在播放器是最终播放的地址是怎么样的呢,是这样的

http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence0.ts

组拼起来的,你可以直接用这个地址播放看看。

5、实现这种未加密的缓存还是比较好实现的,大概可以分为这几步。

  • a.我们首先按照特定的格式去解析这m3u8文件。
  • b.按照解析出来的ts文件按照我们知道的规则组拼起来,其下载这些ts文件,存放在手机的sd卡
  • c.我们需要在本地搭建一个本地http服务器,我们之前本打算搭建一个https,但是由于生成的证书是自己生成导致播放器不去访问本地的服务器。
  • d.本地服务器我们通过过滤特定的接口名字,来实现根据不同ts名字返回不同的视频文件(这里最好生成和原始的ts文件的名字一样)
  • e.我们如何知道播放器播完一段视频呢,因为他是一段一段播放的,所以这里就需要我们在本地生成一份本地指向我们本地服务器的m3u8文件,直接播

播放加密HLS

看下加密的m3u8文件的格式

#EXTM3U

#EXT-X-VERSION:3

#EXT-X-KEY:METHOD=AES-128,URI="http://xxxxxx:5555//test/1102/test/segments.key"

#EXT-X-MEDIA-SEQUENCE:0

#EXT-X-ALLOW-CACHE:YES

#EXT-X-TARGETDURATION:19

#EXTINF:13.966667,

http://xxxxxx:5555/test/1102/test/segments0.ts

#EXTINF:10.000000,

http://xxxxxx:5555/test/1102/test/segments1.ts

#EXTINF:10.000000,

http://xxxxxx:5555/test/1102/test/segments2.ts

#EXTINF:10.000000,

http://xxxxxx.cn:5555/test/1102/test/segments3.ts

#EXTINF:10.000000,

http://xxxxxxn.cn:5555/test/1102/test/segments4.ts

#EXTINF:7.033333,

http://xxxxxx:5555/test/1102/test/segments5.ts

#EXTINF:10.000000,

我们看到了多了个字段EXT-X-KEY,这个也是m3u8给规定好的加密字段,如果包含这个字段播放器就会先去请求这个key,然后拿这个这个key去访问加密的TS视频就可以播放了。 其实看到这我们就因该有思路怎么去做,加密的缓存播放了。

实现播放加密缓存的思路

  • a、我们首先按照特定的格式去解析这m3u8文件。
  • b、按照解析出来的ts文件按照我们知道的规则组拼起来,其下载这些ts文件,存放在手机的sd卡,这些下载下来的TS视频文件是播放不了的,再把正确的key下载下来。
  • c、我们需要在本地搭建一个本地http服务器,我们之前本打算搭建一个https,但是由于生成的证书是自己生成导致播放器不去访问本地的服务器。
  • d、本地服务器我们通过过滤特定的接口名字,来实现根据不同ts名字返回不同的视频文件(这里最好生成和原始的ts文件的名字一样)
  • e、我们如何知道播放器播完一段视频呢,因为他是一段一段播放的,所以这里就需要我们在本地生成一份本地指向我们本地

推荐一个免费学习音视频的地址,里面都是视频教程干货满满:【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂