基于本地代理的边下边播技术分析
1.边下边播技术介绍
我们熟知的边下边播技术,是迅雷提供的,还有之前的快播、快车等工具,它们使用的技术基本上都是P2P下载技术。P2P下载技术,本质上它并不是C-S的架构,P2P----> Peer to Peer,实际上它将各个客户端的资源调度起来,给上传资源种子,方便后续的下载者可以快速有效的下载资源,这种方式需要服务器整合各个Client,在有用户需要下载的情况下,服务器能及时调度资源,开始给下载者提供资源信息,保证下载者下载资源越快越好。P2P的下载方式后面我们专门介绍一下。这儿不继续展开了。
对一个普通开发者而言,我不想这么费事,我能访问视频资源服务器,我直接从视频源服务器上下载不行吗?
视频下载和视频播放本来是两件完全不相干的事情,但是也有共通之处:播放视频的同时就是需要请求视频资源的;我们要实现边下边播,那就要在请求完视频资源的时候,传输中的比特流,给播放器送去数据,同时也存在本地,这样才是边下边播。正常情况下,播放的速度肯定不如下载的速度,所以一般是先把视频资源存到本地,然后读取本地视频数据送到播放器中。
2.边下边播技术演进
正常的模型是播放器 <----> 视频源服务器模型,播放器请求视频资源,视频源服务器收到了请求,返回相应的数据,播放器播放视频数据,这种情况下,也是可以做边下边播的,但是有限制;限制主要是边下边播的控制逻辑非常复杂,因为边下边播的逻辑和播放器的控制逻辑势必搞在一起,这样不仅从架构上无法区分,而且代码上也不好分开,后续维护的成本比较高;一般情况下不建议这么做;
播放器 <----> 代理服务器 <----> 视频源服务器:这是提出的一个改进的想法,改进的一个点就是在 播放器 和 视频源服务器之间架了 一个 代理服务器,代理服务器请求 视频源服务器数据,然后返回给播放器,这下就实现了将播放模块与下载模块隔离开来;但是代理服务器是需要服务器配置的,一般公司没必要搞视频源代理服务器,太耗带宽了。
本地代理服务器替代一下这个代理服务器是比较好的一种方法,既可以实现将播放模块和下载模块分层,也可以实现边下边播的功能。
这就是演进到播放器 <----> 本地代理服务器 <----> 视频源服务器,我们本文所讲的 边下边播的技术就是 基于本地代理服务展开的。
3.Socket监听
边下边播是基于本地代理,本地代理的建立依赖于Socket监听,那么什么是Socket?
Socket就是一组API,对TCP/IP协议进行封装的API,可以将Socket理解为处于传输层和应用层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。
Socket通讯流程简化如下:
服务端这边首先创建一个Socket(Socket()),然后绑定IP地址和端口号(Bind()),之后注册监听(Listen()),这样服务端就可以监听指定的Socket地址了;
客户端这边也创建一个Socket(Socket())并打开,然后根据服务器IP地址和端口号向服务器Socket发送连接请求(Connect());
服务器Socket监听到客户端Socket发来的连接请求之后,被动打开,并调用Accept()函数接收请求,这样客户端和服务器之间的连接就建立好了;
成功建立连接之后就可以你侬我侬了,客户端和服务器进行数据交互(Receive()、Send());
在腻歪完之后,各自关闭连接(Close()),交互结束;
我们建立本地代理,服务端的地址就是http://127.0.0.1:port,其他的交互流程都和普通的Socket监听是一样的。值得注意的是,Client端和本地代理的服务端http://127.0.0.1:port进行交互,本地代理的服务端和视频源服务器进行交互的时候,本地代理服务端就作为一个“Client端”存在。这种角色易位我们要能理解。
开启一个ServerSocket监听,需要确定一个本地可用的端口,就是这个端口没有被占用。这儿非常重要,使用一个端口一定要确定一下这个端口是否被占用,一旦被占用,本地代理整个服务就挂掉了,代码逻辑上做好控制即可。
4.边下边播技术点解析
4.1 视频类型
视频类型,我们知道有非分片视频和分片视频区分;像 mp4 mov mkv avi rmvb 这些封装格式都是非分片视频,一般情况下,播放器可以一次请求,后续处理,这些视频最终会存储到一个文件中;像现在mp4的封装格式应用的最广泛,有一个很优秀的开源库:https://github.com/danikula/AndroidVideoCache,主要是针对非分片视频的边下边播来的;
分片视频,就是一个整视频被分为若干个小分片视频,请求的时候不能一次性地请求所有的视频文件;会有一个视频索引文件,至于各个分片要依次请求,这种叫做分片视频。之前我们详细分析过M3U8视频的格式:多媒体文件格式剖析:M3U8篇,还有其中的TS视频格式:多媒体文件格式剖析:TS篇
#EXTM3U
#EXT-X-TARGETDURATION:10#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
#EXT-X-ENDLIST
这个HLS文件中有3个分片ts视频,我们请求的时候,需要一个一个请求,整视频请求数据是一次就可以的,后续使用206分段下载;
实现mp4 等非分片视频的边下边播是可以的,那么HLS分片视频如何实现边下边播呢?
4.2 分片视频如何处理
HLS视频----> HTTP Live Streaming,就是熟知的M3U8视频;
https://tv.youkutv.cc/2019/10/28/6MSVuLec4zbpYFlj/playlist.m3u8
什么样算是HLS类型的视频?
从上面的流程图可以得到如何判断视频是M3U8类型:
简单的判断,url解析出的path后缀名是 .m3u8的就是M3U8类型
通过视频的mimetype来判断:如果发现是下面四种类型,就是M3U8类型的视频,我们就可以按照M3U8解析的规则来解析这个视频url
public static String MIME_TYPE_M3U8_1 = "application/vnd.apple.mpegurl";
public static String MIME_TYPE_M3U8_2 = "application/x-mpegurl";
public static String MIME_TYPE_M3U8_3 = "vnd.apple.mpegurl";
public static String MIME_TYPE_M3U8_4 = "applicationnd.apple.mpegurl";private boolean isM3U8Mimetype(String mimeType) {return mimeType.contains(MIME_TYPE_M3U8_1)|| mimeType.contains(MIME_TYPE_M3U8_2)|| mimeType.contains(MIME_TYPE_M3U8_3)|| mimeType.contains(MIME_TYPE_M3U8_4);
}
解析的视频url是:
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.main.m3u8
这个m3u8文件存储信息是:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:11.0
#EXTINF:10.12,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_0_10.ts
#EXTINF:9.88,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_10_20.ts
#EXTINF:10.08,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_20_30.ts
#EXTINF:10.28,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_30_40.ts
#EXTINF:9.68,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_40_50.ts
#EXTINF:10.12,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_50_60.ts
#EXTINF:3.04,
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_60_63.ts
#EXT-X-ENDLIST
转化成一个本地代理的文件:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:11.0
#EXTINF:10.12,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts
#EXTINF:9.88,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_10_20.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_1.ts
#EXTINF:10.08,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_20_30.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_2.ts
#EXTINF:10.28,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_30_40.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_3.ts
#EXTINF:9.68,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_40_50.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_4.ts
#EXTINF:10.12,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_50_60.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_5.ts
#EXTINF:3.04,
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_60_63.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_6.ts
#EXT-X-ENDLIST
请求一个
http://127.0.0.1:3888/http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts
这个http://127.0.0.1:3888的url会被拦截,直接解析出后续的参数:
http%3A%2F%2Fvideoconverter.vivo.com.cn%2F201706%2F655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts
对这个url decode 一下:
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_0_10.ts&jeffmony&/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts
我们自己定义的一个隔离字符串 &jeffmony&,这个分隔符将字符串分为两部分:
http://videoconverter.vivo.com.cn/201706/655_1498479540118.mp4.f10_0_10.ts/3bfd0b2eec722da9ed67509a9388dbe2/seg_0.ts
第一个表示当前分片视频的网络url;第二个表示当前文件本地存储的位置;我们在解析的时候,先判断是否存在本地的分片视频,如果存在,直接读取本地的文件,如果不存在,那要去请求网络的分片url;
最终缓存文件夹下内容如下:
PD1710:/sdcard/Android/data/com.android.media/cache/.local/3bfd0b2eec722da9ed67509a9388dbe2 $ ls
proxy.m3u8 remote.m3u8 seg_0.ts seg_1.ts seg_2.ts seg_3.ts seg_4.ts seg_5.ts seg_6.ts video.info
分片视频都下载到了本地;真正下载的逻辑应用不需要介绍了,这个大家直接看代码吧;
4.3 非分片视频分段如何处理
视频播放不是孤立的行为,用户有可能会拖动进度条的,拖动进度条,如何拖动到当前没有下载到的位置,那就必须要从拖动到位置向后重新下载,这个分段缓存片段的管理也是比较重要的;
用户随意拖动进度条,可能会产生若干个分段的缓存块,这些缓存块是不连续的,但是一旦用户拖动进度条到之前的某个位置,下载资源的时候会将各个分段的缓存块连起来,连接起来之后就是一个完整的视频;
不过维护缓存块的逻辑是比较重要的,这儿主要讲解一下思想,具体看下项目代码吧,是按照上面阐述的思想来的。
4.4 边下边播架构图
最后贴一下边下边播的架构图。代码见开源项目:
https://github.com/JeffMony/MediaSDK
推荐阅读:
Android JNI 中的线程操作
Android JNI Crash定位步骤
推荐几个堪称教科书级别的 Android 音视频入门项目
觉得不错,点个在看呗~
基于本地代理的边下边播技术分析相关推荐
- 基于WiFi的室内三维定位运用技术分析
基于WiFi的室内三维定位运用技术分析 1.短程感应技术 短程感应不过分强调定位位置的精确程度,而是运用信号采集方式来将用户处于信号范围内大致活动距离进行估算,划定室内活动空间,以方便进一步进行数据收 ...
- 开源OS FreeBSD 中 ftpd chroot 本地提权漏洞 (CVE-2020-7468) 的技术分析
聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士团队 7月份,一名匿名研究员向 ZDI 报告了 FreeBSD 中的一个本地提权漏洞.它位于 FreeBSD 的文件传输协议守护进程 (ft ...
- Android安卓手机能不能实现BT文件边下边播?
Android安卓手机能不能实现BT文件边下边播?Android安卓手机实现类似pc端的bt边下载边播放技术的可行性分析 PC端实现对BT文件的边下载边播放功能,已经有很长时间,也在很多领域得到了很好 ...
- IPv6任播技术及其任播地址解析协议概述
IPv6任播技术及其任播地址解析协议概述 作者:王建 李方伟 摘要:任播技术是一种新的网络应用.任播通信是支持导向服务地址的,但是目前IPv6中它的很多定义是模糊的,并且在控制路由方面没有达成一致的协 ...
- linux监听9080,基于UPnP发现与组播技术的IPTV终端实现
摘 要:本文设计并实现的终端系统工作在mClinux操作系统下,通过应用UPnP中的SSDP服务发现协议,实现在局域网中IPTV终端的自动发现, 并使用IP组播技术将TV视频信号及本地的AV视频信号. ...
- 【计算机网络】网络层 : 移动 IP 技术 ( 移动节点 | 本地代理 | 外部代理 | 永久地址 | 转交地址 | 移动 IP 通信过程 )
文章目录 一.移动 IP 技术 简介 二.移动 IP 通信 一.移动 IP 技术 简介 移动 IP 技术 简介 : ① "移动 IP 技术" 概念 : 移动节点 以 固定的网络 I ...
- Android从本地服务器获取Mp3实现边下边播(JavaEE+Tomcat+SQLServer)
项目实例:https://download.csdn.net/download/qq_37437983/10484636 实现环境: 1)Lenovo G50-80 Ubuntu16.04笔记本 2) ...
- 内网安全:Socks 代理 || 本地代理 技术.
内网安全:Socks 代理 || 本地代理 技术. Socks 代理又称全能代理,就像有很多跳线的转接板,它只是简单地将一端的系统连接到另外一端.支持多种协议,包括http.ftp请求及其它类型的请求 ...
- [iOS]仿微博视频边下边播之滑动 TableView 自动播放
注意:框架已经迭代到2.0版本,我重新架构了整个框架,API 也得到了更好的设计,我为 2.0 版本的实现写了一篇文章 [iOS]如何重新架构 JPVideoPlayer ?.此文中的实现思路仍然是一 ...
- 仿微博视频边下边播之封装播放器
来源:NewPan(@盼盼_HKbuy) 链接:http://www.jianshu.com/p/0d4588a7540f Tips:这次的内容分为两篇文章讲述 01.[iOS]仿微博视频边下边播之封 ...
最新文章
- 一个女程序媛征男友的需求说明书
- 离线轻量级大数据平台Spark之MLib机器学习库朴素贝叶斯实例
- 二进制转十进制-栈的方式实现
- 知乎:GAN 的发展对于研究通用人工智能有什么意义?
- 【转】医学影像调窗技术!!!!
- python 长度queue_python:常见的数据结构
- C++常用强制类型转换
- 基于kali linux 跑字典暴力破解wifi教程
- git 使用代理加速
- 关于网络营销基本理论的概述
- 浏览器被360劫持怎么办
- 设计一个person类java_定义一个Person类,含姓名、性别、年龄等字段;继承Person类设计...
- 快学会这个技能-.NET API拦截技法
- 学渣的刷题之旅 leetcode刷题 35.搜索插入位置(暴力法、二分查找)
- C#连接MySQL数据库详细步骤
- 计算机表格求和乘公式,在EXCEL中怎么设置公式求乘积、求和
- OpenGL学习——着色器
- 认识植物 - 桫椤(蕨类植物之王植物活化石)
- matlab光流lk,Matlab数字视频处理 光流LK算法
- 忘掉 Snowflake,感受一下性能高出587倍的全局唯一ID生成算法
热门文章
- 企业转型遇到这些难题,就可以考虑一款APS生产计划排产软件了
- 如何回复审稿人的意见?(总结)
- 爬虫练习网站 -http://quotes.toscrape.com的爬虫练习
- Bn层之前的卷积层不需要加偏执
- C语言—内存的管理和释放
- excel软件的IF函数及其用法
- tds for mysql_PostgreSQL9.3安装tds_fdw扩展
- oracle数据库 tds是什么,别被忽悠了才后悔 TDS值到底是什么
- 使用高德地图自定义marker、infowindow
- 微信视频号头像怎么换?怎么设置?必看!5个思路帮你快速敲定头像