当前位置: 首页 > news >正文

深圳网站建设李天亮佛山网站制作在线

深圳网站建设李天亮,佛山网站制作在线,福州网站制作托管维护,wordpress 对象储存大厂技术 高级前端 Node进阶 点击上方 程序员成长指北#xff0c;关注公众号 回复1#xff0c;加入高级Node交流群 作者#xff1a;然燃 #xff08;感谢小伙伴投稿分享#xff09;原文链接: https://juejin.cn/post/7307934456995856419 最近又遇到了web视频化的场景关注公众号 回复1加入高级Node交流群 作者然燃 感谢小伙伴投稿分享原文链接: https://juejin.cn/post/7307934456995856419 最近又遇到了web视频化的场景之前也有过调研H5视频化调研浅析1 但这次稍微复杂一些这次解决的是 视频播放的技术方案调研 服务端实现 视频转码生成不同码率的视频进行视频标准加密不同码率视频合并用于动态码率播放 web端实现 web端播放器的设计web端播放器的自定义扩展可拖拽进度条音量控制根据当前带宽自适应码率切换手动清晰度切换倍速播放样式自定义覆盖标准加密视频播放基于原生开发可在所有框架运行统一跨框架情况各浏览器控件统一 其中web端源码已添加MIT协议并完全开源如果看完对大家有帮助的话欢迎大家star,issue,pr也希望能友好交流 demo地址⇲https://chaxus.github.io/ran/src/ranui/player/ 源码地址⇲https://github.com/chaxus/ran/tree/main/packages/ranui demo文档做了国际化可切换到中文 任何一个项目立项肯定先是技术调研我们先看看一些大公司的视频播放方案 #一.一些知名公司的web视频播放方案 #1.B站 我们先看看B站的毕竟B站的主营业务就是视频弹幕网站简直专业对口。 先找一个例子⇲www.bilibili.com/video/BV1FM…⇲2 访问它。 打开控制台可以看到视频在播放的时候会不断的请求m4s的视频文件。 毕竟一整个视频文件往往比较大不可能先请求完视频文件再进行播放。因此将一个大的视频文件分割成很多小的片段边加载边播放是一种更好的方式。 每次请求的m4s文件大概在几十kb到几百kb不等。 那为什么不采用http的range呢可以请求一个文件的部分内容而且粒度更细可以设置字节范围。在http请求的header中类似这样 Range: bytes3171375-3203867 我们可以检查这个链接请求https://upos-sz-mirror08c.bilivideo.com/upgcxcode/67/92/1008149267/1008149267-1-30064.m4s的请求头就能发现B站采用的是即分片加载同时还用了range的方式。 #2. 爱奇艺爱奇艺、土豆、优酷 爱奇艺这里就不贴视频链接了因为随便点一个视频都要先看广告。 爱奇艺的视频主要请求的是f4v格式也是分片加载。 播放一个视频时请求多个f4v文件。 也采用Range。但和B站不一样的是B站的Range属性是在m4s请求的请求头里面而爱奇艺的看起来是在querystring上在请求query上带着range参数。 因为没发现这个请求的header里面有range参数。比如 https://v-6fce1712.71edge.com/videos/other/20231113/6b/bb/3f3fe83b89124248c3216156dfe2f4c3.f4v?dis_k2ba39ee8c55c4d23781e3fb9f91fa7a46dis_t1701439831dis_dzCNC-BeiJingdis_st46srciqiyi.comdis_hit0dis_tag01010000uuid72713f52-6569e957-351cross-domain1ssl1pv0.1cphcartarange0-9000 #3.抖音 抖音的方案简单粗暴访问的链接是这个 ⇲m.ixigua.com/douyin/shar…⇲3 通过查看控制台我们可以发现直接请求了一个视频的地址 没有进行分片但用到了请求range所以可以看到视频是边播放边缓冲一部分。 不过我在开发的时候发现目前租用的服务云厂商默认会帮我们实现这项技术。 因为我把mp4视频上传到云服务器通过链接进行播放的时候就是边缓冲边播放的。 我们可以直接把这个视频地址拿出来放到浏览器里面能直接播放这样观察更明显。 但B站和爱奇艺却不能这样因为他们采用的m4s和f4v都不是一种通用的视频格式需要使用专门的软件或工具才能打开和编辑。 #4.小红书 测试用的例子链接⇲www.xiaohongshu.com/discovery/i…⇲4 小红书的方案更加简单粗暴打开控制台直接告诉你就是请求一个mp4然后直接播放就完事了。 #5.总结 看完了以上的各家大厂的方案我们可以看到基本原理都是边播放边加载减少直接加载大视频文本的成本。并且通过分片传输还能动态控制视频码率清晰度。做到根据网速加载不同码率的分片文件做到动态码率适应。 同时采用的视频格式比如f4vm4s都不是能直接播放的媒体格式需要一定的处理。增加盗取视频的成本增加一定的安全性。 如果没有强要求也可以直接采用mp4或者直接用video播放一个视频文件地址。 #二.常见的视频格式与协议 我们知道视频的常见格式有mp4同时上面介绍了B站播放用的m4s格式爱奇艺用的f4v格式 •除了这些还有哪些视频格式•为什么有这么多视频格式有哪些不同点呢•为什么这些公司会采用这种格式来播放视频呢 #1. B站用的m4s M4S格式不是一种通用的视频格式需要使用专门的软件或工具才能打开和编辑。 M4S 通常会和 MPEG-DASH 流媒体技术一起通过流式传输的视频的一小部分。播放器会按接收顺序播放这些片段。第一个 M4S 段会包含一些初始化的数据标识。 MPEG-DASH 是一种自适应比特率流媒体技术通过将内容分解为一系列不同码率的M4S片段然后根据当前网络带宽进行自动调整。如果想在在web音视频中采用DASH技术可以看下 ⇲github.com/Dash-Indust…⇲5 #2. 爱奇艺的f4v F4V是一种流媒体格式它是由Adobe公司推出的继FLV格式之后支持H.264编码的流媒体格式。F4V格式的视频不是一种通用的视频格式但通常情况下都可以将文件后缀改为FLV这样就可以使用支持FLV的播放器进行观看。 FLV格式跟常见的MP4格式比起来结构更加简单所以加载metadata(视频元数据比如视频时长等信息)会更快。具体结构我们可以在这里查到⇲en.wikipedia.org/wiki/Flash_…⇲6 比如这是FLV文件的标准头定义了从几个比特到几个比特之间是什么含义。我们知道后可以用MediaSource进行读取和转码。 Field Data Type Default Details  Signature 签名byte[3]FLV  始终就是“FLV”Version 版本uint81 只有0x01才有效Flags 标志uint8 位掩码0x050x04是音频0x01是视频所以0x05是音频视频Header Size uint32_be9用于跳过较新的扩展标头 而MP4格式会稍微复杂一些具体标准在 ⇲ISO/IEC 14496-12⇲7 大概有两百多页这里放不下对这方面有兴趣的可以自行查看。 然而这并不表示MP4更差因为它是一种基础通用标准所以定义上会留有很多空间和各种情况甚至允许在标准之内进行自行发挥和扩展。而FLV格式则更加固定但优点也是更加简单。 对于FLV的视频播放我们可以采用⇲github.com/bilibili/fl…⇲8flvjs主要作用就是用MediaSource将flv转码成mp4从而喂给浏览器进行播放。 接下来是一些其他的视频格式简单介绍一下 #3.AVI 文件名以.avi结尾AVI 最初由 Microsoft 于 1992 年开发是 Windows 的标准视频格式。AVI 文件使用较少的压缩来存储文件并且比许多其他视频格式占用更多空间这导致文件大小非常大每分钟视频大约 2-3 GB。 无损文件不会随着时间的推移而降低质量无论您打开或保存文件多少次。此外这允许在不使用任何编解码器的情况下播放。参考资料⇲Audio Video Interleave⇲9 #4.MPEG 文件名以“.mpg”或“.mpeg”结尾MPEG 是由 ISO 和 IEC 联合成立的工作组联盟旨在制定媒体编码标准包括音频、视频、图形和基因组数据的压缩编码;以及各种应用程序的传输和文件格式。MPEG 格式用于各种多媒体系统。最广为人知的旧 MPEG 媒体格式通常使用 MPEG-1、MPEG-2 和 MPEG-4 AVC 媒体编码MPEG-2 系统传输流和节目流。较新的系统通常使用 MPEG 基本媒体文件格式和动态流式处理又名 .MPEG-DASH。参考资料⇲Moving Picture Experts Group⇲10 #5.MP4 带有音频和视频的 MPEG-4 文件通常使用标准的 .mp4 扩展名。纯音频 MPEG-4 文件通常具有 .m4a 扩展名原始 MPEG-4 可视比特流命名为 .m4v。Apple iPhone使用MPEG-4音频作为其铃声但使用.m4r扩展名而不是.m4a扩展名。参考资料⇲MPEG-4 Part 14⇲11 #6.QuickTime 文件名以“.mov”结尾QuickTime 能够包含媒体数据的抽象数据引用并将媒体数据与媒体偏移和轨道编辑列表分离这意味着 QuickTime 特别适合编辑因为它能够就地导入和编辑无需数据复制。由于 QuickTime 和 MP4 容器格式都可以使用相同的 MPEG-4 格式因此在仅限 QuickTime 的环境中它们大多可以互换。MP4作为国际标准得到了更多的支持。参考资料⇲QuickTime File Format⇲12 #7.TS TS是MPEG2-TS的简称是一种音视频封装格式。TS流的后缀通常是.ts、.mpg或者.mpeg多数播放器直接支持这种格式的播放。TS格式主要用于直播的码流结构具有很好的容错能力。 #三.浏览器对各种视频格式的兼容性 上面了解常用的视频格式和适用范围之后还需要看一下当前浏览器对各种视频格式的支持程度然后制定技术方案。 #1. Chrome 支持的视频格式从官方文档可以查到主要有以下这些 •MP4 (QuickTime/ MOV / ISO-BMFF / CMAF)•Ogg•WebM•WAV•HLS [Only on Android and only single-origin manifests 官方文档如下⇲www.chromium.org/audio-video…⇲13 #2. Safari 支持的视频格式有这些 官方文档⇲developer.apple.com/library/arc…⇲14 #3.Firefox 支持的视频格式 官方文档⇲support.mozilla.org/en-US/kb/ht…⇲15 #四.MediaSource和视频编码解码封装介绍 上面介绍了一些视频格式和目前浏览器的一些兼容性问题。就能发现在web上播放音视频其实限制还是很大的。如何解决这些限制就会用到MediaSource。 视频其实是无数个图片的叠加如果视频是一秒60帧大约一秒中需要播放60张图片。这就导致一个几分钟的视频就会非常大。比如上面介绍的无损格式avi格式每分钟视频大约 2-3 GB。这时候视频就需要进行编码。其实就是压缩。 编码分为视频编码和音频编码常见的视频编码有 •MPEG系列MPEG-1第二部分、MPEG-2第二部分等同于H.262、MPEG-4第二部分、MPEG-4第十部分等同于H.264有时候也被叫做“MPEG-4 AVC”或“H.264/AVC”。•H.26x系列H.261、H.262、H.263、H.264等同于MPEG-4第十部分、H.265/HEVCITU-T和ISO/IEC联合推出。•其它视频编码WMV系列、RV系列、VC-1、DivX、XviD、X264、X265、VP8、VP9、Sorenson Video、AVS。 常见的音频编码有AACMP3AC-3等 编码之后还需要将音频和视频合并在一个文件里这就是封装。 所以相对的播放一个视频就需要解封装解码音视频同步喂给声卡和显卡进行播放。 MediaSource做的就是这个工作读取视频流转换成浏览器能播放的格式。 以下是flv.js的parseChunks部分内容。读取buffer一个字节一个字节的根据标准进行解析。然后转码。 if (byteStart  0) {  // buffer with FLV headerif (chunk.byteLength  13) {let probeData  FLVDemuxer.probe(chunk);offset  probeData.dataOffset;} else {return 0;}}if (this._firstParse) {  // handle PreviousTagSize0 before Tag1this._firstParse  false;if (byteStart  offset ! this._dataOffset) {Log.w(this.TAG, First time parsing but chunk byteStart invalid!);}let v  new DataView(chunk, offset);let prevTagSize0  v.getUint32(0, !le);if (prevTagSize0 ! 0) {Log.w(this.TAG, PrevTagSize0 ! 0 !!!);}offset  4;} #1.MediaSource兼容性 由此可见基本都是绿色但有一个特殊情况就是Safari on IOS。这部分支持程度还是棕色。 #五. HLS 播放方案 采用HLS技术方案有以下几个原因 1.兼容性 上面介绍了各种视频格式还有浏览器的兼容性 其中 HLS协议是Apple公司实现的在 Apple 的全系列产品包括 iPhone、iPad、Safari 等都可以原生支持播放 HLS。 对于其他浏览器可以通过MediaSource解封装解码转码进行播放。 这样也就解决了MediaSource的兼容性问题。 业务场景需求 目前对于视频的加密有着强需求比如需要用户付费才能观看一些视频。而HLS协议天然自带标准加密同时也能基于HLS扩展私有加密。 HLS协议自带支持分片传输和动态码率自适应播放。有现成的技术方案Hls.js #六. 服务端开发 选择了采用HLS协议的播放方式那么首先需要处理视频这部分目前是在服务端进行处理。利用ffmpeg的能力。 如果以后能将ffmpeg搬上浏览器且没有性能问题就好了。现在有类似的webassembly的npm包但性能有点小问题 #1.视频的转码 视频的转码的ffmpeg命令如下 ffmpeg -i input.mp4 -hls_time 10 -hls_list_size 0 -c:v h264 -b:v 2M -hls_segment_filename output_%05d.ts output.m3u8 -y 每个参数的解释 •-i 指定输入的视频•-hls_time 指定分片的时间单位是秒•-hls_list_size 指定hls列表的数量这里不限制•-c:v 指定视频的编码格式•-b:v 指定视频的码率这里是2M比特率•-hls_segment_filename 指定输出的ts文件名字这里表示是output_  五位数字•output.m3u8指定输出m3u8文件的名字•-y有些场景比如是否覆盖直接选择是避免程序卡住 为了自动化执行这里会用到node的spawn模块创建一个子进程在子进程中执行ffmpeg的命令。 const exec  ({ params, data }: ExecOption): PromiseExecResult  {return new Promise((r, j)  {const cp  spawn(ffmpeg, params);cp.stderr.pipe(process.stdout);cp.on(error, (err)  {j(err);});cp.on(close, (code)  {r({ code, data });});cp.on(exit, (code)  {r({ code, data });});}); }; 这时候视频就会在指定的位置输出了会生成一个m3u8和多个ts ts是视频文件m3u8更像是索引文件用来描述ts比如在什么时间播放什么ts。主要内容如下 #EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.380622, 5_00000.ts #EXTINF:10.380622, 5_00001.ts #EXTINF:10.380622, 5_00002.ts #EXTINF:10.380622, 5_00003.ts #EXTINF:6.560556, 5_00004.ts #EXTINF:1.619378, 5_00005.ts #EXTINF:5.024189, 5_00006.ts #EXT-X-ENDLIST #2.视频的标准加密 HLS协议标准加密采用的是AES对称加密方案。先来实现一个最标准的加密 首先通过node原生模块crypto生成加密密钥 import crypto from node:crypto; // 生成加密密钥 const key  crypto.createHash(sha256).update(crypto.randomBytes(32)).digest(base64); const filePathKey  path.join(__dirname,../../public/uploads/hls/${dir}/${fileName}.key);const content  ${ctx.origin}/uploads/hls/${dir}/${fileName}.key\n${filePathKey}\n; // 密钥的文件 const fileKey  await writeFile(path.join(__dirname, ../../public/uploads/hls/${dir}/${fileName}.key),key); const keyInfoPath  path.join(__dirname,../../public/uploads/hls/${dir}/${fileName}_key.bin); // ffmpeg 需要的key.info const keyInfo  await writeFile(keyInfoPath, content); 然后再执行ffmpeg命令这里同样需要用node的spawn模块进行封装成接口 ffmpeg -i input.mp4 -hls_time 10 -hls_list_size 0 -c:v h264 -b:v 2M -hls_key_info_file keyInfoPath -hls_segment_filename output_%05d.ts output.m3u8 -y 主要就增加了一个hls_key_info_file参数表示加密密钥的地址。这时候生成的m3u8文件就发生了变化多了一行 #EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-KEY:METHODAES-128,URIhttp://localhost:30103/uploads/hls/5_1701577743851/5.key,IV0x00000000000000000000000000000000 #EXTINF:10.380622, 5_00000.ts #EXTINF:10.380622, 5_00001.ts #EXTINF:10.380622, 5_00002.ts #EXTINF:10.380622, 5_00003.ts #EXTINF:6.560556, 5_00004.ts #EXTINF:1.619378, 5_00005.ts #EXTINF:5.024189, 5_00006.ts #EXT-X-ENDLIST 多了 #EXT-X-KEY:METHODAES-128,URIhttp://localhost:30103/uploads/hls/5_1701577743851/5.key,IV0x00000000000000000000000000000000 •METHOD字段表示加密方式这里是AES•URI表示密钥地址这里是http://localhost:30103/uploads/hls/5_1701577743851/5.key•IV是加密解密时的偏移量现在是0 上述加密方式虽然视频确实加密了但会把密钥地址写在m3u8里。等于把房间上锁然后在锁上贴一个纸条上面写了密码。 #3.更好的安全方案 •有加密必然需要解密 首先我们知道视频要在web端进行播放那么无论如何都肯定需要先解密再播放。 •肯定不能在web端放置密钥•web端需要知道如何获取密钥•密钥用一次即失效每次加密视频都生成新的密钥 目前更好的安全性方式主要有两种 在请求密钥的地址上进行加固 •校验cookie既然是发起请求那么同域名会自动携带cookie只有购买过的用户才能获取密钥。总不能让付费的用户也不能看吧•生成密钥链接时带上ticket短时间失效控制时效性•请求头携带auth进行用户校验。比如jwt方案就是如此 采用私有加密方式比如m3u8里的METHOD可能不再是AES这种对称加密。自定义一套加密规则这种方式安全性会极大提高但同时就不遵守HLS协议的标准了。但大多数浏览器支持MediaSource。可以读取文件内容进行自定义加密和解密。根据上文的兼容性调研MediaSource在IOS上将会有兼容性问题所以这种方案在IOS上也会有兼容性问题。 #4.自适应码率播放 这里先介绍一下码率和清晰度的关系 码率是指 码率也称为比特率是指视频文件在单位时间内使用的数据流量。它反映了视频文件的数据压缩程度码率越高压缩比就越小画面质量就越高但文件体积也越大。通俗来说码率可以看作是取样率是视频编码中画面质量控制中最重要的部分。计算公式是文件体积时间X码率/8。 所以简单来说码率越高清晰度就越好。成正比关系。 需要根据视频的质量和业务场景去定义这里给出阿里云对码率和清晰度的定义可供参考 为了实现自适应码率播放我们需要将不同码率的m3u8合并成一个多码率的m3u8。 这里我没找到合适的ffmpeg命令但总有办法的最万能的方法就是查看HLS协议中多码率的m3u8格式标准自己写一个。 目前用node去写一个这样的索引文件具体内容如下 #EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID1,BANDWIDTH1000000,CODECSmp4a.40.2,avc1.64001f,RESOLUTION1280x720,NAME720 hls/5_1701577771368/5.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID2,BANDWIDTH50000,CODECSmp4a.40.5,avc1.42000d,RESOLUTION320x184,NAME320 hls/5_1701577744714/5.m3u8 •#EXT-X-STREAM-INF: 流媒体的描述•PROGRAM-ID: 表示唯一的ID。•BANDWIDTH: 流媒体的带宽即每秒传输的数据量。这里带宽为50000意味着每秒传输的数据量大约为50kbps。•CODECS: 流媒体使用的编解码器。这里是使用了mp4a.40.5AAC音频编码和avc1.42000dAVC视频编码。•RESOLUTION: 这个字段指示了视频的分辨率即宽度和高度。在这个例子中视频分辨率为320x184。•NAME: 这个字段为流媒体提供了一个名称本例中名称为320。我会习惯把清晰度放在NAME这个字段这里方便web端获取 实现一个接口传入以上的参数动态拼接字符串写入文件 async generateMasterPlayList(ctx: Context): Promisevoid {try {const { paths, filename  Date.now() }  ctx.request.body;let content  #EXTM3U\n;paths.forEach((item: MasterPlayListOption, index: number)  {const { id  index, bandWidth, codecs, resolution, name, url }  item;content  #EXT-X-STREAM-INF:PROGRAM-ID${id},BANDWIDTH${bandWidth},CODECS${codecs},RESOLUTION${resolution},NAME${name}\n${url}\n;});const dir  path.join(__dirname, ../../public/uploads/hls/);if (!existsSync(dir)) {await createDir(dir);}const filePath  dir  (filename.toString().endsWith(.m3u8) ? filename : ${filename}.m3u8)const { success, error }  await writeFile(filePath, content);const basename  path.basename(filePath);return success? ctx.successHandler({url: ${ctx.origin}/uploads/hls/${basename},}): ctx.failHandler(error);} catch (error) {ctx.errorHandler(error);}} 那么HLS是如何自适应码率的呢 其实是根据BANDWIDTH这个字段因为我们给不同的视频设置了不同的BANDWIDTH。那么就可以根据当前的网速进行动态切换。 #七.web端的实现 上面做了大量的工作主要是生成了HLS协议的视频播放的地址。接下来就是如何在web端进行播放。 #1.技术选型 我首先是看了现有的播放器npm比较知名的 •有西瓜playler ⇲github.com/bytedance/x…⇲16•阿里云点播方案⇲help.aliyun.com/zh/vod/deve…⇲17•知乎的player⇲github.com/zhihu/griff…⇲18 其中知乎和西瓜的播放器是开源的阿里云点播方案没有开源代码但是有开源demo。 但基本上实现的功能都很丰富同时配置项会不断的增加。 如果只是简单的初始化一个播放器那还好。但一般这种场景我们都会对播放器进行一定程度的定制化。比如B站。 就多了很多自定义的控件。进度条也是小电视的形状。 我们这边也是如此有自己的主题色有自己的播放按钮等等还有一些业务功能也要放在控制条上。 上述的开源方案都需要花时间去研究配置项而且使用方法都是new Player(options)的形式。 但尽量视图的归视图逻辑的归逻辑会更好些。 更何况实现一个播放器也不是很困难。 期望的player能满足 配置够简单最好看到就知道是怎么用的方便扩展和样式覆盖支持hls播放尽量适配前端的各种框架方便接入实现价值 #2.播放器设计 由于现在既有react项目也有vue项目甚至还有一些老的jquery项目。为了做到一次开发任何项目都可以使用和接入。采用了web components技术方案。 web components就不做介绍了具体可以去看这篇文章⇲手写web components组件⇲19简单来说就是可以自定义元素让我们像使用section一样使用自定义元素。 播放器我们需要考虑的点有 video生命周期loadedmetacanplay, ended, error等video状态播放正在播放暂停静音等video属性总时长当前时长音量大小倍速等video交互暂停播放知识点清晰度倍速全屏进度控制音量控制等 #(1).video的生命周期 对于video的生命周期我们期望做到两件事情 我们能知道当前video处于什么生命周期在不同的生命周期能挂载自定义事件。比如视频触发了ended。我们需要在ended时期跳转下一个视频 因此我们需要监听video的生命周期 listenEvent  ()  {if (!this._video) return;this.clearListenerEvent();this._video.addEventListener(canplay, this.onCanplay);this._video.addEventListener(canplaythrough, this.onCanplaythrough);this._video.addEventListener(complete, this.onComplete);this._video.addEventListener(durationchange, this.onDurationchange);this._video.addEventListener(emptied, this.onEmptied);this._video.addEventListener(ended, this.onEnded);this._video.addEventListener(error, this.onError);this._video.addEventListener(loadeddata, this.onLoadeddata);this._video.addEventListener(loadedmetadata, this.onLoadedmetadata);this._video.addEventListener(loadstart, this.onLoadstart);this._video.addEventListener(pause, this.onPause);this._video.addEventListener(play, this.onPlay);this._video.addEventListener(playing, this.onPlaying);this._video.addEventListener(progress, this.onProgress);this._video.addEventListener(ratechange, this.onRatechange);this._video.addEventListener(seeked, this.onSeeked);this._video.addEventListener(seeking, this.onSeeking);this._video.addEventListener(stalled, this.onStalled);this._video.addEventListener(suspend, this.onSuspend);this._video.addEventListener(timeupdate, this.onTimeupdate);this._video.addEventListener(volumechange, this.onVolumechange);this._video.addEventListener(waiting, this.onWaiting);}; 在触发不同的时期时让开发者知道。所以我们要先自定义事件进行触发 const change  (name: string, value: unknown): void  { const currentTime  this.getCurrentTime(); const duration  this.getTotalTime(); this.dispatchEvent(new CustomEvent(change, {detail: {type: name,data: value,currentTime,duration,tag: this, // 整个player的实例},}), ); }; const onCanplaythrough  (e: Event)  {this.ctx.currentState  e.type;this.change(canplaythrough, e); }; 这样就可以当生命周期事件触发后就会触发onchange。 在使用上我们可以 r-player onChange{change} srchls/example.m3u8 /r-playerconst change  (e:CustomEvent)  {const { type, data, currentTime, duration, tag }  e.detailif(type  ended){console.log(video ended)} } 其中type的类型有 名称说明canplay浏览器可以播放媒体文件了但估计没有足够的数据来支撑播放到结束不必停下来进一步缓冲内容。canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。completeOfflineAudioContext 渲染完成。durationchangeduration 属性的值改变时触发。emptied媒体内容变为空例如当这个 media 已经加载完成或者部分加载完成则发送此事件并调用 load() 方法重新加载它。ended视频停止播放因为 media 已经到达结束点。loadedmetadata已加载元数据。progress在浏览器加载资源时周期性触发。ratechange播放速率发生变化。seeked跳帧seek操作完成。seeking跳帧seek操作开始。stalled用户代理user agent正在尝试获取媒体数据但数据意外未出现。suspend媒体数据加载已暂停。loadeddatamedia 中的首帧已经完成加载。timeupdatecurrentTime 属性指定的时间发生变化。volumechange音量发生变化。waiting由于暂时缺少数据播放已停止。play播放已开始。playing由于缺乏数据而暂停或延迟后播放准备开始。pause播放已暂停。volume音量发生变化。fullscreen触发全屏事件 在不同的生命周期能挂载事情因此我们需要一个发布订阅类。 type Callback  (...args: unknown[])  unknown;type EventName  string | symbol;type EventItem  {name?: string | symbol;callback: Callback;initialCallback?: Callback; };const NEW_LISTENER  NEW_LISTENER;export class SyncHook {private _events: RecordEventName, ArrayEventItem;constructor() {this._events  {};}on  (eventName: EventName, eventItem: EventItem | Callback): void  {if (this._events[eventName]  eventName ! Symbol.for(NEW_LISTENER)) {this.emit(Symbol.for(NEW_LISTENER), eventName);}const callbacks  this._events[eventName] || [];if (typeof eventItem  function) {callbacks.push({name: eventName,callback: eventItem,});} else {callbacks.push(eventItem);}this._events[eventName]  callbacks;};emit  (eventName: EventName, ...args: Arrayunknown): void  {const callbacks  this._events[eventName] || [];callbacks.forEach((item)  {const { callback }  item;callback(...args);});};once  (eventName: EventName, eventItem: EventItem | Callback): void  {let one: EventItem;if (typeof eventItem  function) {one  {name: eventName,callback: (...args: Arrayunknown)  {eventItem(...args);this.off(eventName, one);},initialCallback: eventItem,};} else {const { callback }  eventItem;one  {name: eventName,callback: (...args: Arrayunknown)  {callback(...args);this.off(eventName, one);},initialCallback: callback,};}this.on(eventName, one);};off  (eventName: EventName, eventItem: EventItem | Callback): void  {const callbacks  this._events[eventName] || [];const newCallbacks  callbacks.filter((item)  {if (typeof eventItem  function) {return (item.callback ! eventItem  item.initialCallback ! eventItem);} else {const { callback }  eventItem;return item.callback ! callback  item.initialCallback ! callback;}});this._events[eventName]  newCallbacks;}; } 因此会给player元素上增加一个ctx属性作为全局的上下文。 this.ctx  {currentTime: 0, // 当前时间duration: 0, // 总时长currentState: , // 当前视频状态action: new SyncHook(), // 不同时期触发的状态 }; 我们想订阅视频的结束事件我们可以 通过Ref的方式: r-player ref{PlayerRef} onChange{change} srchls/example.m3u8 /r-playerconst endedEvent  ()  {console.log(video ended) }PlayerRef.current.ctx.action.off(ended,endedEvent)PlayerRef.current.ctx.action.on(ended,endedEvent) 通过change方法获取的实例: r-player onChange{change} srchls/example.m3u8 /r-playerlet playerconst endedEvent  ()  {console.log(video ended) }const change  (e:CustomEvent)  {const { type, data, currentTime, duration, tag }  e.detailplayer  tag }player.action.off(ended,endedEvent) player.action.on(ended,endedEvent) #(2).video的状态和属性 需要在全局上下文中记录下播放器的状态和属性 this.ctx  {currentTime: 0, // 当前时间duration: 0, // 总时长currentState: , // 当前视频状态action: new SyncHook(), // 不同时期触发的状态volume: 0.5, // 当前音量playbackRate: 1, // 当前倍速clarity: , // 当前清晰度fullScreen: false, // 是否全屏levels: [], // 清晰度列表url: , // 当前播放的地址levelMap: new Map(), // 清晰度和名字的映射关系 }; #(3).自定义video 默认长这个样子 demo地址⇲chaxus.github.io/ran/src/ran…⇲20 源码地址⇲github.com/chaxus/ran/…⇲21 如果不喜欢控制器或者按钮直接样式覆盖更符合直觉没有学习成本。 .ran-player-controller{display: none } 由于播放器本身就是一个元素那么可以任意的在里面添加元素添加逻辑。 r-player onChange{change} srchls/example.m3u8 section111111/section /r-player 所以这就解决配置项长达好几页的问题同时看到也就知道怎么配置怎么开发了。 #八.总结 目前已经从前后端的角度实现了 视频的标准加密视频的动态码率播放视频的分片加载可拖拽进度条音量控制手动清晰度切换倍速播放样式自定义覆盖基于原生开发可在所有框架运行统一跨框架情况各浏览器控件统一 这是demo和源码地址 demo和文档地址⇲https://chaxus.github.io/ran/src/ranui/player/ 源码地址⇲https://github.com/chaxus/ran/tree/main/packages/ranui demo文档做了国际化可切换到中文 Node 社群 我组建了一个氛围特别好的 Node.js 社群里面有很多 Node.js小伙伴如果你对Node.js学习感兴趣的话后续有计划也可以我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。 “分享、点赞、在看” 支持一下 参考资料 [1] H5视频化调研浅析:https://juejin.cn/post/7238739662822735933 [2] www.bilibili.com/video/BV1FM…:https://www.bilibili.com/video/BV1FM411N7LJ [3] m.ixigua.com/douyin/shar…:https://m.ixigua.com/douyin/share/video/7206914252840370721?aweme_type107schema_type1utm_sourcecopyutm_campaignclient_shareutm_mediumandroidappaweme [4] www.xiaohongshu.com/discovery/i…:https://www.xiaohongshu.com/discovery/item/63b286d1000000001f00b495 [5] github.com/Dash-Indust…:https://github.com/Dash-Industry-Forum/dash.js [6] en.wikipedia.org/wiki/Flash_…:https://en.wikipedia.org/wiki/Flash_Video#Flash_Video_Structure [7] ISO/IEC 14496-12:https://www.iso.org/standard/83102.html [8] github.com/bilibili/fl…:https://github.com/bilibili/flv.js [9] Audio Video Interleave:https://en.wikipedia.org/wiki/Audio_Video_Interleave [10] Moving Picture Experts Group:https://en.wikipedia.org/wiki/Moving_Picture_Experts_Group [11] MPEG-4 Part 14:https://en.wikipedia.org/wiki/MP4_file_format [12] QuickTime File Format:https://en.wikipedia.org/wiki/QuickTime_File_Format [13] www.chromium.org/audio-video…:https://www.chromium.org/audio-video/ [14] developer.apple.com/library/arc…:https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html [15] support.mozilla.org/en-US/kb/ht…:https://support.mozilla.org/en-US/kb/html5-audio-and-video-firefox [16] github.com/bytedance/x…:https://github.com/bytedance/xgplayer [17] help.aliyun.com/zh/vod/deve…:https://help.aliyun.com/zh/vod/developer-reference/overview-14 [18] github.com/zhihu/griff…:https://github.com/zhihu/griffith [19] 手写web components组件:https://juejin.cn/post/7170219296226803725 [20] chaxus.github.io/ran/src/ran…:https://chaxus.github.io/ran/src/ranui/player/ [21] github.com/chaxus/ran/…:https://github.com/chaxus/ran/tree/main/packages/ranui
http://www.pierceye.com/news/146379/

相关文章:

  • 做网站贵吗手机网站wap
  • linux建立网站做网站的应该怎么发广告
  • wordpress使用端口百度seo排名软
  • 用英文字母做网站关键词个人网站的设计与实现专业论文图像处理工具
  • 重庆企业网站推广流程php网站开发技术训练心得
  • 汽车销售网站学校建网站
  • 两台电脑一台做服务器 网站潍坊专业网站建设多少钱
  • 青岛科技街网站建设安徽 网站开发
  • 黑糖不苦建设的网站wordpress获取文章图片不显示
  • 美食网站建设的功能免费做简历的网站
  • 网站建设公司谁管手机如何创建网站
  • 可以自己做网站优化吗最好用的wordpress主题
  • 瓜子二手车网站开发智慧团建注册登记入口
  • 青岛网站开发建设安阳市商祺网络有限责任公司
  • 自己怎么做装修网站网站建设设计岗位职责
  • php语言 网站建设投资2 3万小生意
  • 全美网站开发微转app是用网站做的吗
  • 禹州 什么团购网站做的好广州网站建设程序开发
  • 成都市微信网站建设公司专业app开发
  • 郑州网站建设hndream神木网站设计公司
  • 关于网站集约化建设的讲话抓取网站访客qq号码
  • 南昌住房城市建设支行官方网站海洋网络提供网站建设
  • 网站外链建设的八大基本准则做网站卖得出去吗
  • 网站建设不完整 审核天元建设集团有限公司一公司尤作岭
  • 论坛程序做导航网站专做轮胎的网站
  • 网站开发软件解决方案个人网站可以做资讯吗
  • 网站右击无效是怎么做的牛商网建设的食品网站
  • 新北网站建设全网营销网站建设
  • 网站建设与管理 教学设计自己的身份已经网站备案了
  • 长沙网站列表网站开发实例及研究