使用 PuTTY 安全复制客户端将文件传输到您的 Linux 实例

PuTTY 安全复制客户端 (PSCP) 是一个命令行工具,可用于在 Windows 计算机和 Linux 实例之间传输文件。

要使用 PSCP,您需要使用在使用 PuTTYgen 转换私有密钥中生成的私有密钥。您还需要使用 Linux 实例的公有 DNS 名称或 IPv6 地址(如果实例具有该地址)。

以下示例将 Sample_file.txt 文件从 Windows 计算机上的 C:\ 驱动器传输到 Amazon Linux 实例上的 instance-user-name 主目录。要传输文件,请使用以下命令之一。

  • (公有 DNS)要使用实例的公有 DNS 名称传输文件,请输入以下命令。pscp -i C:\path\my-key-pair.ppk C:\path\Sample_file.txt instance-user-name@instance-public-dns-name:/home/instance-user-name/Sample_file.txt
  • (IPv6) 或者,如果实例具有 IPv6 地址,要使用实例的 IPv6 地址传输文件,请输入以下命令。IPv6 地址必须用方括号 ([ ]) 括起来。pscp -i C:\path\my-key-pair.ppk C:\path\Sample_file.txt instance-user-name@[instance-IPv6-address]:/home/instance-user-name/Sample_file.txt

如果您更喜欢图形用户界面 (GUI),您可以使用一种叫作“WinSCP”的开源 GUI 工具。

WinSCP 是适用于 Windows 的基于 GUI 的文件管理器,您可以通过它来使用 SFTP、SCP、FTP 和 FTPS 协议将文件上传并传输到远程计算机。WinSCP 允许您将文件从 Windows 计算机拖放到 Linux 实例,或者在两个系统之间同步整个目录结构。

但您的 Linux 实例必须已安装 scp。对于某些操作系统,您需要安装 openssh-clients 软件包。对于其他操作系统(例如经 Amazon ECS 优化的 AMI),您需要安装 scp 软件包。请参阅您的 Linux 发行版的文档。

TCPing监听服务器的端口状态

当服务器设置了禁ping,TCPING可以监听服务器的端口状态,默认是80端口的,也可以指定其它端口。也可以看到ping 返回的时间

下载的tcping.exe或者tcping64.exe文件放到C:\Windows\System32目录下(ping命令也是在这个目录下)

“ tcping www.baidu.com”

“ tcping -t www.baidu.com 【参数-t 是让命令一直运行ping 】”

“ tcping -d -t www.baidu.com 【参数 -d 是显示时间,这样就可以更清楚了】”

“ tcping -d -t www.baidu.com 443/21 【这里只是举例,21是您所需要监听的端口,这个可以行定义。】”

无线路由下嗅探

1.openwrt路由下安装 tcpdump(可以将网络中传送的数据包完全截获下来提供分析)

  1. opkg update
  2. opkg install tcpdump

2.电脑装wireshark 和plink.exe(plink是putty下的一个工具,可以在windows下直连liunx并自动执行命令的工具)
https://www.wireshark.org/download.html
http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

3.路由下tcpdump抓包,PC端使用 wireshark 实时监听

  1. plink.exe -ssh -pw admin root@192.168.4.1 “tcpdump -ni wlan0 -s 0 -w – not port 22” | “C:\Program Files\Wireshark\Wireshark.exe” -k -i –

  admin 是路由的密码,root是用户名 ,wlan0 无线网卡 的数据链路层代号,
  ifconfig 可以看到具体详细信息,tcpdump的指令就不多说了,看官方文档吧


4.分析数据报文,获取敏感信息

  假如你使用了我的wifi,登录了百度的主页,我就获取你的登录cookies,
  有了cookies可以不用密就能登录你的百度账号,  浏览你的各种百度云盘里面的内容。
   

  wireshark 添加过滤的条件“http.cookie“  获取cookies的相关报文,点中右键,follow tcp steam,看到完整的报文,

  复制 http 请求时向服务器发送的cookies 字符串
              

5.注入cookies,登录账号
chrome 下使用一个 CookieHacker的插件 ,下载 http://evilcos.me/?p=366
打开 baidu.com,把cookies的字符串粘贴,点击inject cookie

刷新一下就看到用户已经登录了
P.S. 除了这些以外,你发出的所有请求都能在wireshark里面分析出来,当然这只是最简单的嗅探而已,

s-s搭建记录

客户端下载地址

服务端一键安装脚本(安装的go版本,但电脑客户端一些加密方式好像不支持)

wget --no-check-certificate -O shadowsocks-go.sh https://raw.githubusercontent.com/poetries/shadowsocks_install/master/shadowsocks-go.sh

chmod +x shadowsocks-go.sh
./shadowsocks-go.sh 2>&1 | tee shadowsocks-go.log

安装完成后,脚本提示如下:

Congratulations, Shadowsocks-go install completed!
Your Server IP:your_server_ip
Your Server Port:your_server_port
Your Password:your_password
Your Local Port:1080
Your Encryption Method:aes-256-cfb

Welcome to visit:https://teddysun.com/392.html
Enjoy it!

卸载方法

./shadowsocks-go.sh uninstall

可以查看 Shadowsocks-go 进程是否已经启动。 本脚本安装完成后,已将 shadowsocks-go 加入开机自启动。

使用命令:

  • 启动:/etc/init.d/shadowsocks start
  • 停止:/etc/init.d/shadowsocks stop
  • 重启:/etc/init.d/shadowsocks restart
  • 状态:/etc/init.d/shadowsocks status

多用户多端口配置文件示例: 配置文件路径:/etc/shadowsocks/config.json

{
    "port_password":{
         "8989":"password0",
         "9001":"password1",
    },
    "method":"aes-256-cfb",
    "timeout":600
}

根据上面的在ubuntu上只能安装go版本,而只能手机端用,WIN客户端有的加密方式,GO版本都没有,最后在github上找到v2ray服务器安装脚本,用的aws东京但只在debian系统装测试成功。客户端WIN一般用v2rayN,手机端用v2rayNG,v2flyNG,大家可直接在github下载

自动安装脚本

bash <(curl -s -L https://git.io/v2ray.sh)

另外推荐XUI
bash <(curl -Ls https://raw.githubusercontent.com/vaxilu/x-ui/master/install.sh)

传送门

AWS云主机 及使用中的坑

前往亚马逊官网注册,自己根据里面的资料慢慢填写即可。填写过程中必须要绑定信用卡

信用卡账单,在亚马逊官网信用卡付费是不需要密码的,也就是根据你的信用卡正反面信息即可直接在网络上操作扣费(一般扣费都可以申请追回,最好申请资金进出短信提醒),自己的信用卡自己保护好。亚马逊当然不会恶意扣费,只是免费套餐限制多,超出部分是要扣费的;

白嫖教程

创建EC2实例

进入控制中心,先选择地区,右上角有地区选择。如果搭建SS,建议亚太地区,如果是纯粹的网站。那就根据你的目标用户的位置来选择了。

图片[2] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

选择好了地区后,点击[服务]中的[EC2],现在我们就要开始创建EC2实例了。

图片[3] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路
图片[4] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

选择“仅限免费套餐”,这里我选择的是Red Hat,因为Red Hat更接近于CentOS,习惯用CentOS了。

图片[5] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

实例只能选择[符合条件的免费套餐],选择其他会产生费用的

然后创建密钥对,如果用putty连建议选ppk格式,否则要用工具再把ppm转成ppk,建好后

[安全组配置]选择默认规则就好了,稍后我们在[安全组]里修改为全部放行(注意入站、出站都要修改为全部放行)

图片[8] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

设置[安全组配置]

图片[9] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

查看实例,并用XFTP或者SSH连接

图片[10] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

XFTP或者SSH上填写好登录地址和用户名后导入密钥

如果最后一步出现错误,那是账号还没有完全通过审核,需要等待5分钟-4小时,邮件收到通过审核后再去创建实例;

如果显示创建成功可以正常使用了,这里又个坑,每月的免费流量只有100G,超过后就会在你的信用卡上扣费,所以我们要监控云主机的流量不要超过100G的出站流量;可以直接登录后点用户名,选进到账户页面再选左边的“FREE TIER”来查看免费套餐的使用情况,也可以按下面方式来统计

可以进实例后点中间的“监控”,里面选“网络输入”(还有网络输出也一样)面板的右上角选“在指标中查看”

图片[13] - Amazon – AWS免费一年云主机 以及使用中的一些坑 - 云线路

这里修改统计数据为总和,周期为30天,把上面的线形图改成数字,这样就可以统计一个月的流量数据了,(也可以点击左上角[服务]选择[管理与监管]–[CloudWatch]在这个里面来统计),还可以把统计数据添加到控制面板里,以后就不用繁琐操作看流量统计了

但这里又有个坑,ec2-user这个用户权限太低,我们需要root的权限。登录用证书登录也很麻烦,也需要一并去掉。

可以后台直接连接主机,也可以用putty填好IP后在 连接-SSH-认证里把私钥(putty要ppk格式,如是ppm要用PuTTYgen把格式转为ppk)传上去后再连接主机,刚进去一般用户名是ec2-user,如ubuntu用户名是ubuntu,红帽是admin,用普通用户连上系统后

创建root的密码,输入如下命令:

sudo passwd root

会提示你输入new password。输入一个你要设置的root的密码,需要你再输入一遍进行验证;

接下来,切换到root身份,输入如下命令:

su root

给ec2-user加个密码,输入如下命令:

passwd ec2-user

使用root身份编辑亚马逊云主机的ssh登录方式,在/etc/ssh/sshd_config找到三个地方,把no改成yes 输入:

vi /etc/ssh/sshd_config

键盘P、i编辑,esc、:wq保存

PermitRootLogin yesPasswordAuthentication yesUsePAM yes

接下来,要重新启动下sshd,这里重启sshd命令有可能不会生效需要重启系统:

reboot

这样就可以使用root身份直接登录ssh了,接下来可以删除ec2-user这个用用户了:

uesrdel -r ec2-user

这里还有个情况需要说明下,用Red Hat系统的话我是实测无法安装军哥的LNMP和宝塔的,也就是说如果需要安装网站环境的话需要自己编译WEB环境,或者更换成UBUNTU系统使用LNMP或者宝塔(这里我没测试),本人只用来搭建SS扶墙。

下面说下安装秋水的SS一键脚本,使用Shadowsocks-python版本无法搭建成功,这里我测试安装成功的是Shadowsocks-go,希望大家不要走弯路。

总结

注册AWS要使用从未在AWS上用过的信用卡,不然你就不符合Free Tier的使用条件,所有服务将会按需收费,申请免费成功后免费服务只有一年的有效期,到期时记得导出数据并停止所有服务;

免费额度只够创建一个EC2实例;

创建实例时,要选带有免费提示的操作系统,这类系统是免费提供的,不另收费;

建站时不要使用RHEL(Red Hat Enterprise Linux),此系统占用的EBS空间大而且极费IO,每月200万的免费IO可能不出几天就能耗完;

每月的免费存储是5G,注意不要超出这个上限,另外GET,POST等操作可能会超出免费额度;

流入AWS的流量完全是免费的,但每月的免费流出流量只有15G,超出部分就要收费了。而且这15G对于所有的AWS服务来说是共享的。托管一个小网站,架设个SS,明显不够用嘛;

基本监控是免费的,再高级别的就要收费了。免费监控虽说不够详细,但是足够让我们了解服务器最近一段时间的运行状况了,没必要使用高级别的监控服务;

因为实例每次停止再启动,IP都会变,所以如果经常停止,可以申请绑个弹性IP,把IP固定下来,绑在实例上的弹性IP是不收费的,但弹性IP没有捆绑到EC2实例时,一定要记得释放,如免费实例到期后弹性IP也要放,不然它会按小时收费。而且每月g头100次捆绑Elastic IP到EC2实例是免费的,超出则收费啦;

区块链POW和POS的区别

如果你是一名资深的比特币矿工或商人,你一定听说过POW和POS,但是当前在国内,却几乎没有人明白这到底是什么意思,也几乎没有相关的中文资料,小编吐血撰写,是为了让大家更好理解这些概念。

读完本文,相信会让你明白,原来,虚拟货币除了挖矿,还有利息!

第一段:通俗的概念解析

POW:全称Proof of Work,工作证明。

这是什么意思呢?这就是说,你获得多少货币,取决于你挖矿贡献的有效工作,也就是说,你电脑性能越好,分给你的矿就会越多,这就是根据你的工作证明来执行货币的分配。大部分的虚拟货币,比如比特币、莱特币等等,都是基于POW模式的虚拟货币(算力越高、挖矿时间越长,你获得的货币就越多)。

POS:全称Proof of Stake,股权证明。

这又是什么意思呢?简单来说,就是一个根据你持有货币的量和时间,给你发利息的一个制度,在股权证明POS模式下,有一个名词叫币龄,每个币每天产生1币龄,比如你持有100个币,总共持有了30天,那么,此时你的币龄就为3000,这个时候,如果你发现了一个POS区块,你的币龄就会被清空为0。你每被清空365币龄,你将会从区块中获得0.05个币的利息(可理解为年利率5%),那么在这个案例中,利息 = 3000 * 5% / 365 = 0.41个币,这下就很有意思了,持币有利息,非常好!(需要注意的是,5%的年利率仅仅是小编举例,并非每个POS模式的币种都是5%,比如点点币PPCoin就是1%年利率)

第二段:市面的POS和POW货币

前面我们已经学习了POS和POW的概念了,接下来我们看看,现在市面上的货币,哪些支持POW,哪些支持POS呢?

答案是:除了点点币PPCoin、新星币NovaCoin、雅币YaCoin这三个电子货币采用POW+POS结合的模式外,目前其余所有的货币都只采用了POW模式,而没有采用POS模式。

点点币PPCoin首次实现POS(年利率1%)

POS这个概念,最早在比特币项目里,就被提及,不过,在后续的实现当中,比特币开发团队考虑到其稳健性未经考验,并没有将它实现到比特币的系统中。直到2012年8月,一个叫点点币PPCoin(简称PPC)的电子货币出现,才真正实现了POS,这是PPCoin赖以成名的核心元素,PPCoin既支持POW模式的挖矿收入,也提供了POS模式下的利息收入。当时PPCoin给他们的货币设定了1%的年利率,并取得了很好的市场反响,很多用户大量囤积PPCoin来获取利息。

新星币NVC助力POS(年利率5%)

不过,由于POS难以被世人理解,且1%年利率的吸引力实在是低了点,后续出现的新型电子货币,很少使用POS模式。2013年2月,俄罗斯一个研发团队发布了第二款采用POS的货币新星币NovaCoin(简称NVC),NVC将算法进一步进行优化(采用scrypt),使其交易速度更快,同时将年利率修改为5%。NovaCoin的这两项修改大获成功,这让其每一个币的价值超越了号称白银货币的山寨币之王——莱特币LTC。小编发稿时,一个莱特币LTC等价于0.021个比特币,而一个新星币NVC则等价于0.033个比特币。

第三个POS货币——雅币YaCoin(年利率5%)

2013年5月,第三个采用POS模式的电子货币发布,它就是雅币,英文名YACoin,简称YAC。雅币YAC在新星币NVC的基础上再次有了重大突破和创新,它采用了新型的scrypt-jane算法,这个算法的特性之一,就是对CPU有着强烈的依赖,在此之前,所有已经出现的电子货币,都更依赖于显卡芯片GPU进行挖矿的运算,使用GPU进行挖矿的效率远远超过CPU。而雅币YAC一举打破了这个局面,它的新算法使其在使用CPU运算时,效率超过了GPU,这得到了很多没有好显卡的用户强烈支持,具有着不错的用户群。作为一个支持POS的电子货币,雅币YAC和新星币NVC一样提供了5%的年利率。

第四个来了——宇宙币CosmosCoin(年利率1.5%)

宇宙币CMC是第四款采用POS模式的电子货币,尽管CMC和CNC从名字上看着像兄弟,但其实他们什么关系都没有,因为CMC可比CNC优秀太多了。CMC在NVC的基础上新增了交易留言功能,采用加强难度的scrypt算法,产量每天只有5040个,稀缺性明显,在山寨币当中来看,币值相对较高,可以尝试挖掘存储。CMC和前面3款POS货币的年利率都不都,它提供了1.5%的年利率。

第三段:POS的设计理念和起源

POS的设计理念,来自于对比特币危机的思考,原因有三:

首先,我们都知道,比特币的区块产量每4年会减半,在不久的未来,随着比特币区块包含的产量越来越低,大家挖矿的动力将会不断下降,矿工人数越来越少,整个比特币网络有可能会逐渐陷入瘫痪(因为大家都减少了运行比特币客户端的时间,因此越来越难找到一个P2P节点去连接和同步网络数据)。

POS解决方案:在POS体系中,只有打开钱包客户端程序,才能发现POS区块,才会获得利息,这促使很多不想挖矿的人,也会常常打开自己的钱包客户端,这帮助了P2P货币网络的健壮。

其次,若干年后,随着矿工人数的下降,比特币很有可能被一些高算力的人、或团队、或矿池,进行51%攻击,导致整个比特币网络崩溃。51%攻击简单来说,就是当你拥有了超过全球51%的比特币算力时,你将能伪造比特币网络的任何数据,比如你伪造你有一百万个比特币但实际上你没有。(拓展阅读:什么是51%攻击)

POS解决方案:在POS体系中,即使你拥有了全球51%的算力,也未必能够进行51%攻击,因为,有一部分的货币并不是挖矿产生的,而是由利息产生(利息存放在POS区块中),这要求攻击者还需要持有全球超过51%的货币量。这大大提高了51%攻击的难度。

第三,虽然我们知道比特币是一个永远不会通货膨胀的体系,因为他的货币总量表面看起来是固定的,但是,你是否知道,比特币其实是一个通货紧缩的体系。因为,当我们重装了系统,或者忘记了钱包密钥时,我们会永远无法再拿回钱包里的钱,这意味着,每年都会有一些比特币随着钱包的丢失而永远被锁定,这就形成了实质上的通货紧缩,也许在五十年后,有效的比特币,将会只剩下一千万个。

POS解决方案:提供一定的年利率,尽可能保证既不通货膨胀,也不通货紧缩。

PON网络数据传输方式

那在OLT与POS之间其实只有一根光纤,如何实现上下行的数据在一根光纤中传输呢?

【答案】:划分不同的车道,即规划不同的波长。

【解析】:在PON网络中,上下行通信所采用的波长是不一样的,比如EPON/GPON同属于1G PON技术,它们下行波长规划的是1490nm,上行波长规划的是1310nm,互不影响,完全可以实现双向全双工的通信。此外,还有广电的CATV业务,单独的波长规划时1550nm。

到这里,有小伙伴又有疑问了:

为什么规定下行波长是1490nm,上行是1310nm,而不是反过来呢?

【答案】:成本考虑。

【解析】:标准制定的时候,1310nm的光器件已经相对来说比较成熟,成本较低,而1490nm成本较高。PON是一个点到多点的架构,那么我们自然要将成本比较低器件放在多点侧的发端,这样才会大量的降低PON网络初期的部署成本。

下面我们继续回到PON网络的数据传输的介绍。PON下行采用广播的方式传输数据,上行采用的是TDMA的方式传输数据。一连串的疑问又来了:

PON下行为何采用广播的方式传输数据呢?

【答案】:因为简单实用。

【解析】:PON的下行方向是指从OLT到ONU的这个方向,这个方向发端与收端的数量对比关系是“1对多”的关系,那我们自然就会选择用广播的方式来发送数据,因为这样最省事。同时,下行因受限于无源分路器的物理特性,PON口发出的数据经过无源分路器后平均分配至每一分路,其无源物理特性无法控制某一分路的通与不通,仅能实现单纯分路功能,故下行依据物理分路特性实现了被动广播的现象。

PON下行采用广播,每个ONU都能收到其他ONU的数据,如何保证数据安全?

【答案】:ONU主动过滤,同时数据有加密。

【解析】:一方面,ONU会根据相应的过滤条件主动过滤属于自己的数据,如通过ONU ID(GEM-PORT ID)过滤接收属于自己的数据;另一方面,OLT给每个ONU发送的数据会进行加密(如GPON的AES-128),且加密的密钥是由每个ONU产生并发给OLT的,ONU不会知道其他ONU的密钥,故也难解密属于其他ONU的数据。

动图封面

PON上行为何采用TDMA的方式传输数据呢?

【答案】:实现多ONU同时传输数据,公平竞争。

【解析】:PON的上行方向跟下行方向是反的,即指ONU到OLT的方向,这个方向发端与收端的数量对比关系是“多对一”的关系。既然是“多对一”的关系,肯定就不能让每个ONU想当然的发送数据了,否则就会存在同波长光信号叠加(如GPON的1310nm),OLT接收后也无法读取数据,出现误码帧或未知帧提示,并将其丢弃。

所以这种情况就必须要有一种仲裁机制,来保护我们的上行数据传输不发生冲突。这个仲裁机制便是TDMA,它将上行链路分成不同的时隙,再将这些时隙根据需要分给不同的ONU,ONU在属于自己的时隙发送数据。

动图封面

因此,我们这里也看到,ONU的发光时间严格受OLT指定,它是不会主动发光的,也不会长时间发光的。一旦某个ONU主动发光了或者长时间发光了,这个ONU就是一个“流氓ONU”,会影响整个PON口下的业务。

视频切片加密后播放

最近网上大学想下载一个视频,但下载下来的ts文件不能播放,把m3u8文件下载下来一看里面有

#EXT-X-KEY:METHOD=AES-128,URI="https://kc.zhixueyun.com/hls/9fd5d229-3b75-11e7-b669-44a84230d603",IV=0xd9221589c954ff5538bdc9af225e3e9e

里面的EXT-X-KEY:METHOD=AES-128说明服务器方在把文件切片成ts文件然后播放时进行了加密,所以我们先把m3u8这个文件下载下来,看有多少个ts文件,然后要把uri这个文件和所有ts文件下载下来(迅雷批量下载)放一个文件夹,把上面文件中的uri地址改成本地地址,用迅雷影音就可以直接打开m3u8文件播放。

一般hls视频流加密我们用到常见的一种是防盗链(严格来讲这不属于加密) , 也就是说给 m3u8 和 ts 文件的url动态生成一个 token , 比如这个:

http://www.cuplayer.com/m3u8/hunan/desc.m3u8?stream_id=hunan<m=1410595018&lkey=8bc1e0fe35f6321ef560f8ccffb70e5d&path=59.49.42.14,58.59.3.9,58.59.3.51&platid=10&splatid=1015&tag=live&cips=127.0.0.1&ext=m3u8&sign=live_tv

这个url是随着很多参数动态变化的,比如时间,用户id、ip地址,内容id , 导致你无法使用这个url盗链,这种方式可以防止其他网站直接使用你的url来观看或者一般用户的下载。

而ts文件的url 也需要加请求token , 会变化成类似 http://server/file.ts?token=xxxx 的方式, 这样的话, ts文件的磁盘存储位置不用变化,但是url是可以变化的(可以用query string方式,也可以用 url rewrite 方式), 注意因为url是m3u8生成的,意味着m3u8文件是动态生成而并非静态文件

另一种就是我上面说的DRM加密,用来给ts内容加密, m3u8有这个tag: #EXT-X-KEY

下面是网上找的如何把一个mp4视频使用ffmpeg视频切片并加密和视频AES-128加密后播放

创建加密文件:将一个mp4视频文件切割为多个ts片段,并在切割过程中对每一个片段使用AES-128 加密,最后生成一个m3u8的视频索引文件;

1.加密用的key(文件则保存当前目录)

指令:openssl rand -base64 20 > enc.key

提示打开文件本次生成的 n4DHLx7kMPeewvW3dGlm5i/EE8I=

2.另一个是iv(生成一段字符串,记下来):

指令:openssl rand -hex 16

提示打印出本次生成的682f5033538cf71567e1bdb38f5f9a07

新建一个文件enc.keyinfo 内容格式如下:
Key URI # enc.key的路径,使用http形式
Path to key file # enc.key文件
IV # 上面生成的iv

实例:
http://edu.gamagou.cn/enc.key
/usr/share/nginx/html/enc.key
682f5033538cf71567e1bdb38f5f9a07

  1. ffmpeg加密指令:

/home/programs/video/ffmpeg/ffmpeg -y -i /home/programs/video/test/123.mp4 -hls_time 12 -hls_key_info_file /home/programs/video/conf/enc.keyinfo -hls_playlist_type vod -hls_segment_filename “file%d.ts” playlist.m3u8

加密后的文件形式:

EXTM3U
EXT-X-VERSION:3
EXT-X-TARGETDURATION:12
EXT-X-MEDIA-SEQUENCE:0
EXT-X-PLAYLIST-TYPE:VOD
EXT-X-KEY:METHOD=AES-128,URI=”https://edu.amin.cc/enc.key”,IV=0x15de9b7283a69d7ef11f6f12e488fbb7
EXTINF:12.040000,

file0.ts

EXTINF:10.440000,

file1.ts

EXT-X-ENDLIST
  1. 播放部分:
    jq控件:hls.js
    手册:https://www.bootcdn.cn/hls.js/readme/

播放页面:

<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<!-- Or if you want a more recent canary version -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
<video id="video"></video>
<script>
  var video = document.getElementById('video');
  if(Hls.isSupported()) {
    var hls = new Hls();
    hls.loadSource('https://替换你的文件路径.m3u8');
    hls.attachMedia(video);
    hls.on(Hls.Events.MANIFEST_PARSED,function() {
      video.play();
  });
 } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = 'https://替换你的文件路径.m3u8';
    video.addEventListener('loadedmetadata',function() {
      video.play();
    });
  }
</script>

完成。


裸纤、专线、SDH、MSTP、MSTP+、OTN、PTN、IP-RAN

(一)裸纤
裸纤也叫裸光纤,运营商提供一条纯净光纤线路,中间不经过任何交换机或路由器,只经过配线架或配线箱做光纤跳纤,可以理解成运营商仅仅提供一条物理线路。实际项目中,裸光纤应用较多,比如某大学两个校区,相隔大概20KM,租用运营商裸光纤实现两个校区互联。可以理解成通过一根很长的光纤连接两个校区,拓扑图也简单,直连即可,如图所示:

最早两个校区均有运营商互联网出口,采用两套认证系统,管理维护麻烦,后续升级改造,两个校区租用运营商裸光纤,将两个校区互联起来,相当于将老校区网络作为一个子模块,接入到新校区,与新校区有机融合到一起,实现统一运营和管理。

租用运营商裸纤价格较高,一般按照公里收费,记得某项目租用20km裸纤,费用为20万/年,5年线路费用就是100万。有人可能会问,既然裸纤线路这么贵,为什么不自己拉一根光纤,连接两个校区,买光纤和工人布线施工的成本也用不了这么多呀!的确如此,但不是你想拉光纤就能拉的。国家法律明文规定,只有运营商、军队、市政等几个部门可以在公共区域破土施工,学校/医院/政府这类单位,在自己单位园区内部(也就是围墙内)随便怎么挖,没人会管,但到公共区域施工就不允许了,犯法!

裸光纤应用场景还很多,比如教育城域网,会租用运营商裸光纤,实现教育局与辖区各学校互联,将网络出口统一到教育局,从而实现教育资源共享,统一审计等功能,某县教育网城域网拓扑如图,租用运营商裸光纤实现学校与教育局互联,最后通过教育局网络统一网络出口访问互联网。

另外,在平安城市/视频专网/雪亮工程等项目中,也经常租用运营商裸纤,实现前端摄像头视频流量回传,如下为公安部统计,全国视频专网链路类型,35%采用裸纤。(注:交警前端电子警察一般采用裸纤回传,有钱就任性,而平安城市摄像头采用PON链路较多)

(二)专线
专线一般分三层专线和二层专线,三层专线一般是指MPLS V.P.N,这是CCIE RS方向考试的重点内容,说实话比较难,基本只在金融、电子政务等行业应用,一般人接触不上。我们日常说的专线基本指二层专线,二层专线相当于运营商给了你“一根线”,你两端连接PC,分别配置192.168.1.1/24和192.168.1.2/24,能直接ping通。效果看似跟裸纤差不多,但与裸纤的实质区别是: 裸纤中间不经过任何路由器交换机设备,运营商给你的是真实的一根线,而专线中间经过运营商的各类路由器交换机设备,只是运营商给你模拟出来了一根线,让你感觉跟裸纤效果差不多,至于专线的底层技术,这个是CCIE ISP运营商方向研究的内容,大部分人无需搞得那么透。

如果租用运营商裸纤,只要光纤不被挖断,运营商内部交换机路由器等设备故障,不会影响业务。如果你租用的光纤被挖断了,那就没办法了,业务肯定受影响。如果租用运营商专线,运营商设备出问题,可能影响用户业务,反而光纤被挖断,不一定会影响业务,因为运营商骨干网采用环形设计,传统SDH和新的OTN底层都会有环网,所以论可靠性而言,专线可靠性稍高。笔者今年经历的一个大型雪亮工程项目,一个主数据中心,七个分中心,考虑使用什么链路互联。最早规划使用裸纤互联,但后来考虑到可靠性,改成了租用运营商二层专线互联。当然,使用裸纤也有别的优势,比如带宽自主可控,如下图所示,你需要10G带宽,只需要在交换机上插10G光模块,需要40G带宽在交换机上插40G光模块,在交换机上插100G光模块,自然就有100G带宽。但是如果向运营商租用10G或40G专线,价格高得吓人!

专线可以看成是运营商的一种产品,底层实现技术很多,有SDH、MSTP、PTN、OTN等。下面为大家介绍一下运营商网络常见术语。

(三)SDH和MSTP
要明白SDH,首先要说TDM,TDM是时分复用,就是将一个标准时长(1 秒)分成若干段小的时间段(8000),每一个小时间段(1/8000=125us)传输一路信号;SDH系统就是传统的电路调度,电路调度是以TDM为基础,。早年的网络都是传输语音的,说白了电话网络的基础是TDM。
在SDH大红大紫的时候,另一场战争以太网和ATM(不是取款机哟)大战中,以太网取得全面胜利,从而以太网大行其道,其中又以IP最为强势,导致今天很多业务侧都IP 化了,服务器、PC、摄像头都得配置IP地址吧!
早年运营商骨干网SDH是大红人(运营商早期就是靠装电话挣钱),而园区网内部以太网是另一个大红人(上网数据通过以太网承载),能否合作一下?于是一拍即合,MSTP诞生,运营商骨干部分依旧使用SDH时分复用,但运营商甩给用户的链路接口采用园区网流行的以太网接口,也就是10M、100M、1G、10G这样的标准以太网接口,而不是155M、622M、2.5G这类POS/CPOS接口。

在合资公司MSTP中股份分配不太均匀:SDH 占股70%,以太网占股20%,其它包括ATM占股10%,掌权的还是SDH,内核还是TDM。以太网和ATM因为股权问题,都没有拿出像样的东西,只是须有其表(提供相应接口而已)毕竟早期网络,还是语音为主,数据为辅,但现在完全颠倒,以太网当然不干了,也就有了后来的WDM、OTN、IP-RAN。(就像一家公司,最牛逼的某销售贡献了90%的业绩,他还不出去自己创业,他是傻么?)

(四)波分WDM
随着互联网的大力普及,电脑、手机、电视等终端都能上网了,带宽的需求急剧增加,电信运营商们赚钱的机会来了,但挑战也来了,以前1*155M可以供好上千人打电话,现在人们需要打电话同时还要上网,家庭宽带都300M光纤入户了,带宽需求增长和现网资源出现矛盾,承载语音的TDM骨干网已经完全不能满足海量音视频传输要求。
要解决这个矛盾,专家们思考良久,某天看到二环路高架桥,恍然大悟,如果将网络传输也划分多个通道,也能提升传输性能。于是波分产生,波分WDM 就是将多个波道的信号放到同一条光纤中进行传送。WDM得到重用后,各地纷纷仿效,现在的WDM不仅在城市主干道里使用(城域波分),还用在跨市、跨省道路上(长途波分)。

裸光纤+波分的效果对比
如图使用传统裸光纤,目前商用较多的是100G光模块,那么链路最大100G。裸光纤两端部署波分设备,带宽惊人,线路带宽直接可以到4T甚至更高,单条光纤目前实验室最高波分速率是36T。当然,波分设备特别贵,不是一般单位买得起,波分设备厂商主要有华为、烽火等,客户为运营商、公安、BAT这类单位居多。

(五)OTN
OTN(OpticalTransportNetwork,光传送网)
WDM波分技术优势是带宽大,大得惊人。当然也存在一些问题,最突出的是实时流量监测和重点业务保障没有很好的措施。
SDH 笑道:小样儿,容量那么大,你监测和管理上还是有问题吧?
WDM 回应道:我容量确实比你大得多,但管理这方面的确没你们做得好;
于是他们握手言欢,优势互补,一个全新的制度诞生了—OTN。
所以简单的说:OTN=WDM+SDH

我们对以前的红人SDH在江湖的发展做了详细的描述,现在的SDH也只相当于OTN掌门下的一个堂主而已了,那么另一位红人以太网它现在发展的如何呢?

(六)MSTP+
由于先前MSTP成立时,股权分配不均,有很多遗留问题,以太网仅占20%,导致现在以太网严重不满意,毕竟现在网络上IP流量占了90%以上,为了安抚这个“销量”占公司90%的“销售”,SDH 集团研究后推出MSTP+(也叫Hybrid MSTP),50/50股权分配,进一步提升以太网在公司的地位,也不为一种好的补偿措施。

(七)IP-RAN和PTN
以太网现在声势越来越大,再加上又有MPLS 助阵,逐渐有了可以抗衡SDH的实力,面对MSTP+50%的股份分配,以太网并不高兴,发誓要有所改观……

为了对抗SDH阵营,以太网大力发展自己的势力,走农村包围城市的策略,先将末端IP化(终端PC、摄像头、打印机都得配IP)。同时建立以IP+以太网主导的骨干网,相当于自己开公司,创业了。以太网阵营,自由散漫惯了,现在出现了两种大的分歧:
第一种:认为我们自己成立的运输公司不让SDH的客户(TDM业务)上车,如果一定要进来,必须改头换面-伪装(仿真),同时我们没有时间上的保证(无时间同步),我们纯粹为我们以太网服务,我们的公司名叫IP-RAN;
第二种:认为我们应该吸收一些SDH的客户,SDH经营了这么多年,它的客户还是很多的(还有很多TDM业务需求),同样进来后还是要改头换面-伪装(仿真),然后再我们的帮派里活动,出帮派后再去掉伪装还原成自己原来的模样,这个公司取名叫PTN。
无论哪种方式,伪装-易容术总少不了,随后就开发了PWE3 易容术(CCIE ISP运营商方向重点讲的内容,一般人了解即可)。

(八)总结
MSTP+(Hybrid MSTP)可以看做是SDH向以太网的妥协方案,不得已而为之。随着相互学习融合,IP-RAN和PTN现在已趋于一致,差别不大了,所谓:分久必合合久必分!它们是IP/以太网向SDH发起的全面挑战,现在看来它们是胜利了。

现状:

第一档OTN专线现在多用于政企专网。相比IPRAN和PTN,OTN和互联网物理隔离,资源独享,是现阶段电信最好的端到端专线业务。

第二档是IPRAN云专线,接入端IPRAN,骨干CN2。

第三档是pon专线,直接从olt的pon出纤。

说到这,IP-RAN/PTN战胜了SDH,也就是数通战胜了传统通信,运营商骨干网完全IP化

流媒体视频基础 MSE 入门 & FFmpeg 制作视频预览缩略图和 fmp4

如果检查主流视频网站的视频,就会发现网站的 video 元素的 src 属性都是 blob 开头的字符串。

为什么视频链接前面会有 blob 前缀?这是因为视频网站使用了这篇文章要讲的 MSE 来播放视频。

Media Source Extensions

虽然 video 很强大,但是还有很多功能 video 并不支持,比如直播,即时切换视频清晰度,动态更新音频语言等功能。

MSE(Media Source Extensions)就来解决这些问题,它是 W3C 的一种规范,如今大部分浏览器都支持。

它使用 video 标签加 JS 来实现这些复杂的功能。它将 videosrc 设置为 MediaSource 对象,然后通过 HTTP 请求获取数据,然后传给 MeidaSource 中的 SourceBuffer 来实现视频播放。

如何将 MediaSourcevideo 元素连接呢?这就需要用到 URL.createObjectURL 它会创建一个 DOMString 表示指定的 File 对象或 Blob(二进制大对象) 对象。这个 URL 的生命周期和创建它的窗口中的 document 绑定。

const video = document.querySelector('video')
const mediaSource = new MediaSource()

mediaSource.addEventListener('sourceopen', ({ target }) => {
    URL.revokeObjectURL(video.src)
    const mime = 'video/webm; codecs="vorbis, vp8"'
    const sourceBuffer = target.addSourceBuffer(mime) // target 就是 mediaSource
    fetch('/static/media/flower.webm')
        .then(response => response.arrayBuffer())
        .then(arrayBuffer => {
            sourceBuffer.addEventListener('updateend', () => {
                if (!sourceBuffer.updating && target.readyState === 'open') {
                    target.endOfStream()
                    video.play()
                }
            })
            sourceBuffer.appendBuffer(arrayBuffer)
        })
})

video.src = URL.createObjectURL(mediaSource)
复制代码

addSourceBuffer 方法会根据给定的 MIME 类型创建一个新的 SourceBuffer 对象,然后会将它追加到 MediaSourceSourceBuffers 列表中。

我们需要传入相关具体的编解码器(codecs)字符串,这里第一个是音频(vorbis),第二个是视频(vp8),两个位置也可以互换,知道了具体的编解码器浏览器就无需下载具体数据就知道当前类型是否支持,如果不支持该方法就会抛出 NotSupportedError 错误。更多关于媒体类型 MIME 编解码器可以参考 RFC 4281

这里还在一开始就调用了 revokeObjectURL。这并不会破坏任何对象,可以在 MediaSource 连接到 video 后随时调用。 它允许浏览器在适当的时候进行垃圾回收。

视频并没有直接推送到 MediaSource 中,而是 SourceBuffer,一个 MeidaSource 中有一个或多个 SourceBuffer。每个都与一种内容类型关联,可能是视频、音频、视频和音频等。

视频格式

HTML5 标准指定时,想指定一种视频格式作为标准的一部分,所有浏览器都必须实现。但是对于 H.264 视频编码各个厂商产生的争论,主要是 H.264 非常强大(高画质、高压缩比、成熟的编解码器…),但是它也要高昂的授权费。Mozilla 这类免费浏览器,并没有从其开发的浏览器上获得直接收入,但是让 H.264 加入标准,它就要支付相应的授权费,所有认为是不可接受的。于是后来放弃了视频格式指定的统一,浏览器厂商可以自由选择支持的格式。

不过现在所有主流浏览器都支持 H.264 编码格式的视频,所有选择视频编码时优先选择 H.264 编码。

MediaSource API

MediaSource 属性

属性描述
sourceBuffers返回包含 MediaSource 所有 SourceBufferSourceBufferList 对象
activeSourceBuffers上个属性的子集,返回当前选择的视频轨道,启用的音频规定和显示或隐藏的文本轨道
duration获取或者设置当前媒体展示的时长,负数或 NaN 时抛出 InvalidAccessError
readyState 不为 openSourceBuffer.updating 属性为 true 时抛出 InvalidStateError
readyState表示 MediaSource 的当前状态

readyState 有以下值:

  • closed 未附着到一个 media 元素上
  • open 已附着到一个 media 元素并准备好接收 SourceBuffer 对象
  • ended 已附着到一个 media 元素,但流已被 MediaSource.endOfStream() 结束

MediaSource 方法

方法描述
addSourceBuffer(mime)根据给定 MIME 类型创建一个新的 SourceBuffer 对象,将它追加到 MediaSource 的 SourceBuffers 列表中
removeSourceBuffer(sourceBuffer)移除 MediaSource 中指定的 SourceBuffer。如果不存在则抛出 NotFoundError 异常
endOfStream(endOfStreamError)向 MediaSource 发送结束信号,接收一个 DOMString 参数,表示到达流末尾时将抛出的错误
setLiveSeekableRange(start, end)设置用户可以跳跃的视频范围,参数是以秒单位的 Double 类型,负数等非法参数会抛出异常
clearLiveSeekableRange清除上次设置的 LiveSeekableRange

其中 addSourceBuffer 可能会抛出一下错误:

错误描述
InvalidAccessError提交的 mimeType 是一个空字符串
InvalidStateErrorMediaSource.readyState 的值不等于 open
NotSupportedError当前浏览器不支持的 mimeType
QuotaExceededError浏览器不能再处理 SourceBuffer 对象

endOfStream 的参数可能是如下两种字符串:

  • network 终止播放并发出网络错误信号
  • decode 终止播放并发出解码错误信号

MediaSource.readyState 不等于 open 或有 SourceBuffer.updating 等于 true,调用 endOfStream 会抛出 InvalidStateError 异常。

它还有一个静态方法

静态方法描述
isTypeSupported(mime)是否支持指定的 mime 类型,返回 true 表示可能支持并不能保证

MediaSource 事件

错误描述
sourceopenreadyStateclosedendedopen
sourceendedreadyStateopenended
sourceclosereadyStateopenendedclosed

SourceBuffer API

SourceBuffer 通过 MediaSource 将一块块媒体数据传递给 meida 元素播放。

SourceBuffer 属性

属性描述
mode控制处理媒体片段序列,segments 片段时间戳决定播放顺序,sequence 添加顺序决定播放顺序,
MediaSource.addSourceBuffer() 中设置初始值,如果媒体片段有时间戳设置为 segments,否则 sequence
自己设置时只能从 segments 设置为 sequence,不能反过来。
updating是否正在更新,比如 appendBuffer()remove() 方法还在处理中
buffered返回当前缓冲的 TimeRanges 对象
timestampOffset控制媒体片段的时间戳偏移量,默认是 0
audioTracks返回当前包含的 AudioTrack 的 AudioTrackList 对象
videoTracks返回当前包含的 VideoTrack 的 VideoTrackList 对象
textTracks返回当前包含的 TextTrack 的 TextTrackList 对象
appendWindowStart设置或获取 append window 的开始时间戳
appendWindowEnd设置或获取 append window 的结束时间戳

append window 是一个时间戳范围来过滤 append 的编码帧。在范围内的编码编码帧允许添加到 SourceBuffer,之外的会被过滤。

SourceBuffer 方法

方法描述
appendBuffer(source)添加媒体数据片段(ArrayBufferArrayBufferView)到 SourceBuffer
abort中断当前片段,重置段解析器,可以让 updating 变成 false
remove(start, end)移除指定范围的媒体数据

SourceBuffer 事件

方法描述
updatestartupdatingfalse 变为 true
updateappendremove 已经成功完成,updatingtrue 变为 false
updateendappendremove 已经结束,在 update 之后触发
errorappend 时发生了错误,updatingtrue 变为 false
abortappendremoveabort() 方法中断,updatingtrue 变为 false

注意事项

  1. 几乎所有 MediaSource 方法调用或设置属性,在 MediaSource.readyState 不是 open 时会抛出 InvalidStateError 错误,应该在调用方法或设置属性前查看当前状态,即使是在事件回调中,因为可能在回调执行之前改变了状态。此外 endOfStream 方法还会因为 SourceBufferupdatingtrue 时也抛出该异常
  2. 在调用 SourceBuffer 方法或设置属性时,应该检查 SourceBuffer 是否为 false
  3. MediaSource.readyState 的值是 ended 时,调用 appendBuffer()remove() 或设置 modetimestampOffset 时,将会让 readyState 变为 open,并触发 sourceopen 事件,所有应该要有处理多个 sourceopen 事件准备

Initialization Segment

如果随便找一个 mp4 文件,使用上面那个例子播放,就会发现播放不了。这是因为 SourceBuffer 接收两种类型的数据:

  1. Initialization Segment 视频的初始片段,其中包含媒体片段序列进行解码所需的所有初始化信息。
  2. Media Segment 包含一部分媒体时间轴的打包和带时间戳的媒体数据。

MSE 需要使用 fmp4 (fragmented MP4) 格式,MP4 文件使用面向对象格式其中包含 Boxes (或叫 Atoms),可以使用 这个网站 查看 Mp4 文件信息。

这是一个普通的 MP4 文件,可以看到它有一个很大的 mdat (电影数据)box

这是 fragmented MP4 的截图,ISO BMFF 初始化段定义为单个文件类型框(File Type Box ftyp)后跟单个电影标题框(Movie Header Box moov),更多信息可以查看 ISO BMFF Byte Stream Format

moov 只包含一些视频基础的信息(类型,编码器等),moof 存放样本位置和大小,moof 框后都有一个 mdat,其中包含如前面的 moof 框中所述的样本。

要查看当前视频是不是 fmp4,就可以看 ftyp 后面是不是跟着 moov,然后是 moof mdat 对就行了。

要将普通 MP4 转换成 FMP4 可以下载 Bento4。然后用命令行输入如下命令就可以了。

mp4fragment ./friday.mp4 ./friday.fmp4
复制代码

Bento4 /bin 目录中有非常多好用的 mp4 工具,/utils 目录中都是 python 实用脚本。

工具

除了上面介绍的 Bento4,还有很多其他好用的工具。有了下面的工具,就可以快速制作 MSE 实践的视频素材了。

chrome://media-internals/

chrome://media-internals/ 是 chrome 浏览器用来调试多媒体的工具,直接在地址栏输入该值就可以。

Shaka Packager

Shaka Packager 是 Google 出的一个小巧视频工具,它只有 5M 左右,它可以用来查看视频信息,分离音频和视频,还支持 HLS 和 DASH。

它的命令行使用格式如下:

packager stream_descriptor[,stream_descriptor] [flags]
复制代码

比如用它查看视频信息:

packager in=friday.mp4 --dump_stream_info # 查看视频信息

File "friday.mp4":
Found 2 stream(s).
Stream [0] type: Audio
 codec_string: mp4a.40.2 # 音频的编码器字符串
 time_scale: 44100
 duration: 271360 (6.2 seconds)
 is_encrypted: false
 codec: AAC
 sample_bits: 16
 num_channels: 2
 sampling_frequency: 44100
 language: und

Stream [1] type: Video
 codec_string: avc1.4d401e # 视频的编码器字符串
 time_scale: 3000
 duration: 18500 (6.2 seconds)
 is_encrypted: false
 codec: H264
 width: 640
 height: 480
 pixel_aspect_ratio: 1:1
 trick_play_factor: 0
 nalu_length_size: 4
复制代码

更多请查看官方文档

FFmpeg

FFmpeg 是功能非常强大的视频处理开源软件,很多视频播放器就是使用它来做为内核。后面文章的实例都会使用这个工具。

比如上面将普通 MP4 转换为 FMP4,可以使用如下命令:

ffmpeg -i ./friday.mp4 -movflags empty_moov+frag_keyframe+default_base_moof f.mp4
复制代码

它的命令行格式如下:

ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
复制代码

它可以有不限数量的输入和输出文件,-i 后面是输入 url,后面不能解析为参数的为输出文件。

 _______              ______________
|       |            |              |
| input |  demuxer   | encoded data |   decoder
| file  | ---------> | packets      | -----+
|_______|            |______________|      |
                                           v
                                       _________
                                      |         |
                                      | decoded |
                                      | frames  |
                                      |_________|
 ________             ______________       |
|        |           |              |      |
| output | <-------- | encoded data | <----+
| file   |   muxer   | packets      |   encoder
|________|           |______________|
复制代码

它将输入文件的容器解开,然后对里面的数据进行解码,然后按照指定的格式进行编码,然后使用指定的容器进行封装生成输出文件。在 decoded frames 后 FFmpeg 可以使用 filter 进行处理,比如添加滤镜、旋转、锐化等操作,filter 分为简单和复杂,复杂可以处理多个输入流。

如果我们只是想改变视频的容器,那么就可以省略解码和编码过程,来提升速度。

ffmpeg -i input.avi -c copy output.mp4
复制代码

-c 是指定编码器,-c copy 表示直接复制编码,-c:v 表示视频编码,-c:a 表示音频编码,比如 -c:v libx264 表示使用 CPU 将视频编码为 h.264-c:v h264_nvenc 则是使用 N卡,这样速度更快。-c:a copy 表示直接复制音频编码。

ffmpeg -y -i myvideo.mp4 -c:v copy -an myvideo_video.mp4
ffmpeg -y -i myvideo.mp4 -c:a copy -vn myvideo_audio.m4a
复制代码

-an 去除音频流 -vn 去除视频流。-y 是不经过确认,输出时直接覆盖同名文件。

ffmpeg -help #查看帮助
ffmpeg -i input.mp4 # 查看视频信息
ffmpeg -formats # 查看支持的容器
ffmpeg -codecs # 查看支持的编码格式
ffmpeg -encoders # 查看内置的编码器
复制代码

更多关于 FFmpeg 请查看官方文档

视频缩略图预览

了解了上面好用的工具,就来用 FFmpeg 来实现一个视频播放器小功能吧。

现在视频网站,当鼠标放到进度条上时就会出现,一个小缩略图来预览这个时间点内容。

ffmpeg -i ./test.webm -vf 'fps=1/10:round=zero:start_time=-9,scale=160x90,tile=5x5' M%d.jpg
复制代码

我们可以通过上面这个命令生成一个雪碧图,由 25 张 160×90 预览图组成。

-vf 参数后面跟着过滤器,多个过滤器用 , 分开,一个过滤器多个参数使用 : 分开。

fps=1/10 表示 10 秒输出一张图,fps=1/60 为一分钟一张,round=zero 时间戳向 0 取整,start_time=-9 是因为 fps 是每多少秒生成一张,并不是从 0 秒开始 -9 是让它从 1 秒开始截取,忽略掉 0 秒的黑屏帧。

scale=160x90 设置输出图像分辨率大小,tile=5x5 将小图用 5×5 的方式组合在一起,M%d.jpg 表示输出为 jpg,而且文件是 M1.jpg M2.jpg... 这样递增。

如果想用 NodeJS,可以用 node-fluent-ffmpeg 的 thumbnails 方法来生成。

有了雪碧图,我们就在上篇文章实现的播放器的基础上在加个视频缩略图功能。主要通过 css 的 background 来实现。

const thumb = document.querySelector('.thumb')
const gapSec = 10 // 一张图片显示几秒
const images = [...] // 图片
const row = 5, col = 5 // 一张图有几行几列
const width = 160, height = 90; // 缩略图的宽高
const thumbQuantityPerImg = col * row

function updateThumbnail(seconds) { // 传入要显示缩略图的秒数
    const thumbNum = (seconds / gapSec) | 0; // 当前是第几张缩略图
    const url = images[(thumbNum / thumbQuantityPerImg) | 0];
    const x = (thumbNum % col) * width; // x 的偏移量
    const y = ~~((thumbNum % thumbQuantityPerImg) / row) * height; // y 的偏移量

    thumb.style.backgroundImage = `url(${url})`;
    thumb.style.backgroundPosition = `-${x}px -${y}px`;
}
复制代码

效果如下

image.png

这里只展示缩略图更新的逻辑,忽略了时间显示,事件处理和样式等代码。

完整的代码请参考 NPlayer

在线演示:nplayer.js.org/

视频切片

有了 MSE 我们就可以将一个视频分割成多个小视频,然后可以自己控制缓存进度来节省流量,还可以将视频压缩成不同的分辨率,在用户网不好的情况动态加载码率低的分段,分成多段还可以实现插入广告,动态切换音频语言等功能。

./audio/
  ├── ./128kbps/
  |     ├── segment0.mp4
  |     ├── segment1.mp4
  |     └── segment2.mp4
  └── ./320kbps/
        ├── segment0.mp4
        ├── segment1.mp4
        └── segment2.mp4
./video/
  ├── ./240p/
  |     ├── segment0.mp4
  |     ├── segment1.mp4
  |     └── segment2.mp4
  └── ./720p/
        ├── segment0.mp4
        ├── segment1.mp4
        └── segment2.mp4
复制代码

当然也可以不切割视频,而使用 HTTP Range 来范围请求数据。

ffmpeg -i ./friday.mp4 -f segment -segment_time 2 -segment_format_options movflags=dash ff%04d.mp4
复制代码

我们使用上面命令将一个视频切成 2 秒的 fmp4 视频片段。

window.MediaSource = window.MediaSource || window.WebKitMediaSource
const video = document.querySelector('video')
const mime = 'video/mp4; codecs="avc1.4d401e, mp4a.40.2"'
const segments = [
    '/static/media/ff0000.5b66d30e.mp4', 
    '/static/media/ff0001.89895c46.mp4', 
    '/static/media/ff0002.44cfe1e4.mp4'
]
const segmentData = []
let currentSegment = 0

if ('MediaSource' in window && MediaSource.isTypeSupported(mime)) {
    const ms = new MediaSource()
    video.src = URL.createObjectURL(ms)
    mediaSource.addEventListener('sourceopen', sourceOpen)
}

function sourceOpen({ target }) {
    URL.revokeObjectURL(video.src)
    target.removeEventListener('sourceopen', sourceOpen)
    const sb = target.addSourceBuffer(mime)
    fetchSegment(ab => sb.appendBuffer(ab))
    sb.addEventListener('updateend', () => {
        if (!sb.updating && segmentData.length ) {
            sb.appendBuffer(segmentData.shift())
        }
    })
    video.addEventListener('timeupdate', function timeupdate() {
        if (
            currentSegment > segments.length &&
            !sb.updating &&
            target.readyState === 'open'
        ) { // 所有片段全部加载完成
            target.endOfStream()
            video.removeEventListener('timeupdate', timeupdate)
        } else if (
            video.currentTime > (currentSegment * 2 * 0.8) 
            // 一个片段时长 2 秒
            // 在一个片段播放到 80% 的时候才去请求下一个片段
        ) {
            fetchSegment(ab => {
                if (sb.updating) {
                    segmentData.push(ab)
                } else {
                    sb.appendBuffer(ab)
                }
            })
        }
    })
    video.addEventListener('canplay', () => {
        video.play()
    })
}

function fetchSegment(cb) {
    fetch(segments[currentSegment])
        .then(response => response.arrayBuffer())
        .then(arrayBuffer => {
            currentSegment += 1
            cb(arrayBuffer)
        })
}
复制代码

这个实例非常简单,并没有处理 seek,自适应码率等复杂功能。

总结

现在视频网站几乎全部都在使用 MSE 来播放视频。使用 MSE 有提供更好的用户体验,更加节约成本等好处。虽然视频播放一般使用 hls dash 等协议的开源客户端来播放视频,我们自己不会使用到 MSE,但这些客户端底层都是使用 MSE,了解 MSE 才更了解这些客户端。

这是弹幕播放器系列文章第二篇,上篇请查看 从零开发弹幕视频播放器1。下篇请查看 原来爱优腾等视频网站都是用这个来播放流媒体的

作者:wopen
链接:https://juejin.cn/post/6953777965838630926