tcplayer 源码改造第二弹 -> 加入倍速播放

tcplayer 源码改造第二弹 -> 加入倍速播放前序简介主要介绍了基于 tcplayer 的源码改造 加入倍速播放功能不涉及 tcplayer 的使用以及框架如何调用 详情请看腾讯云点播文档源码解析中有些注释是笔者加的 如需定位 请不要复制注释以下示例的代码为重新混淆压缩过 与原来的 tcplayer js 函数名不同 不可直接复制使用 请务必跟着笔者一步步执行人群不想自己写播放器而使用 tcplayer 但是又受限于播放器本身不带有倍速

前序

简介

  • 主要介绍了基于tcplayer的源码改造,加入倍速播放功能
  • 不涉及tcplayer的使用以及框架如何调用,详情请看腾讯云点播文档
  • 源码解析中有些注释是笔者加的,如需定位,请不要复制注释
  • 以下示例的代码为重新混淆压缩过,可能与原来的tcplayer.js函数名不同,不可直接复制使用,请务必跟着笔者一步步执行

人群

  • 不想自己写播放器而使用tcplayer,但是又受限于播放器本身不带有倍速播放功能的开发人员
  • 不适合没有任何前端基础的小白,!!请谨慎观看!!

git地址

https://github.com/HaverLee1/hls-player

源码改造(各位客官请自行格式化代码)

实现倍速切换的函数

添加配置参数

在代码中定位videoSource,在第一个的位置,即初始化赋值的同层如下参数(带有注释的则是笔者加入的参数)

 function t(i, o) { 
    n(this, t); var s = l(o); M = ["od", "hd", "sd"]; var a = { 
    owner: i, videoSource: s, src: s.curUrl, autoplay: o.autoplay, live: o.live, flash: o.flash, flashUrl: o.flashUrl, poster: o.poster, width: o.width, height: o.height, volume: o.volume, listener: o.listener, wording: o.wording, controls: o.controls, clarity: o.clarity, clarityLabel: o.clarityLabel, showLoading: "boolean" != typeof o.showLoading || o.showLoading, pausePosterEnabled: void 0 === o.pausePosterEnabled || o.pausePosterEnabled, fullscreenEnabled: void 0 === o.fuScrnEnabled || o.fuScrnEnabled, systemFullscreen: o.systemFullscreen || !1, hls: o.hls || "0.12.4", h5_flv: o.h5_flv, x5_player: o.x5_player !== !1, x5_type: o.x5_type, x5_fullscreen: o.x5_fullscreen, x5_orientation: o.x5_orientation, x5_playsinline: o.x5_playsinline, preload: o.preload || "auto", hlsConfig: o.hlsConfig, flvConfig: o.flvConfig, // curRate表示当前倍速 curRate: o.curRate ? o.curRate : 1, // rates表示倍速数组 rates: o.rates ? o.rates : [2, 1.75, 1.5, 1.25, 1.0, 0.75, 0.5] }; return r(this, e.call(this, a)) } 

添加获取当前倍速的方法

定位”currentTime”,可以看到如下代码:

 }, e.prototype.currentTime = function (e) { 
    return this.video.currentTime(e) } 

由腾讯视频的官方文档可以知道,currentTime方法是暴露给用户,用于获取/设置当前时间的方法,同理,加入获取当前倍速的方法currentRate:

 }, e.prototype.currentTime = function (e) { 
    return this.video.currentTime(e) }, e.prototype.currentRate = function (e) { 
    return this.video.options.curRate; } 

添加切换倍速的函数

定位_switchClarity,找到放置该函数的位置,并加入_switchRate函数:

return s(t, e), t.prototype._switchClarity = function (e) { 
    e = e || "od"; var t = this.currentTime(), i = this.options.videoSource, o = c(i.urls, e), n = this.playing(); this.load(o.url), i.curUrl = o.url, i.curDef = o.definition, i.curFormat = o.format; var r = A.bind(this, function () { 
    parseInt(this.duration() - t) > 0 && !this.options.live && this.currentTime(t), n && this.play(!0), m.unsub(w.MetaLoaded, "*", r, this) }); m.sub(w.MetaLoaded, "*", r, this); // 切换清晰度后依旧保持原有的倍速 document.querySelector("video").playbackRate = this.options.curRate; }, t.prototype._switchRate = function (e) { 
    // 自定义的切换倍速的函数 e = e || 1; this.options.curRate = e; document.querySelector("video").playbackRate = e; }, t.prototype.switchClarity = function (e) { 
    this.claritySwitcher ? this.claritySwitcher.setClarity(e) : this._switchClarity(e) }, t.prototype.handleMsg = function (t) { 
    e.prototype.handleMsg.call(this, t) }, t 

参照切换清晰度的代码对控制栏加入倍速播放的节点

复制切换清晰度的代码,并修改点击函数

 return a(t, e), t.prototype.render = function (t) { 
    this.show = !1, this.createEl("div", { 
   "class": "vcp-clarityswitcher"}), this.current = p.createEl("a", { 
   "class": "vcp-vertical-switcher-current"}), this.container = p.createEl("div", { 
   "class": "vcp-vertical-switcher-container"}), this.items = [], this.currentItem = ""; var i = this.options.videoSource; this.current.innerHTML = f[i.curDef], this.el.appendChild(this.current); for (var o = 0; o < i.definitions.length; o++) { 
    var n = p.createEl("a", { 
   "class": "vcp-vertical-switcher-item"}); n.innerHTML = f[i.definitions[o]], i.definitions[o] == i.curDef && (p.addClass(n, "current"), this.currentItem = n), n.setAttribute("data-def", i.definitions[o]), this.items.push(n), this.container.appendChild(n) } return this.el.appendChild(this.container), e.prototype.render.call(this, t) } 

复制整个function,如下是整个function:

, function (e, t, i) { 
    "use strict"; function o(e) { 
    if (e && e.__esModule) return e; var t = { 
   }; if (null != e) for (var i in e) Object.prototype.hasOwnProperty.call(e, i) && (t[i] = e[i]); return t["default"] = e, t } function n(e) { 
    return e && e.__esModule ? e : { 
   "default": e} } function r(e, t) { 
    if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") } function s(e, t) { 
    if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return !t || "object" != typeof t && "function" != typeof t ? e : t } function a(e, t) { 
    if ("function" != typeof t && null !== t) throw new TypeError("Super expression must either be null or a function, not " + typeof t); e.prototype = Object.create(t && t.prototype, { 
    constructor: { 
    value: e, enumerable: !1, writable: !0, configurable: !0 } }), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t) } t.__esModule = !0; var l = i(24), c = n(l), u = i(2), p = o(u), h = i(3), d = o(h), f = { 
   od: "超清", hd: "高清", sd: "标清"}, y = function (e) { 
    function t(i) { 
    r(this, t); var o = s(this, e.call(this, i, "ClaritySwitcher")); return f = d.extend({ 
   }, i.options.clarityLabel, f), i.claritySwitcher = o, o } return a(t, e), t.prototype.render = function (t) { 
    this.show = !1, this.createEl("div", { 
   "class": "vcp-clarityswitcher"}), this.current = p.createEl("a", { 
   "class": "vcp-vertical-switcher-current"}), this.container = p.createEl("div", { 
   "class": "vcp-vertical-switcher-container"}), this.items = [], this.currentItem = ""; var i = this.options.videoSource; this.current.innerHTML = f[i.curDef], this.el.appendChild(this.current); for (var o = 0; o < i.definitions.length; o++) { 
    var n = p.createEl("a", { 
   "class": "vcp-vertical-switcher-item"}); n.innerHTML = f[i.definitions[o]], i.definitions[o] == i.curDef && (p.addClass(n, "current"), this.currentItem = n), n.setAttribute("data-def", i.definitions[o]), this.items.push(n), this.container.appendChild(n) } return this.el.appendChild(this.container), e.prototype.render.call(this, t) }, t.prototype.setup = function () { 
    this.on("click", this.onClick), this.on("mouseenter", this.onMouseEnter), this.on("mouseleave", this.onMouseLeave) }, t.prototype.onClick = function (e) { 
    var t = e.target.getAttribute("data-def"); t ? (this.current.innerHTML = f[t], p.removeClass(this.currentItem, "current"), p.addClass(e.target, "current"), this.currentItem = e.target, this.player._switchClarity(t)) : !this.show }, t.prototype.onMouseLeave = function () { 
    this.container.style.display = "none", this.show = !1 }, t.prototype.onMouseEnter = function () { 
    this.container.style.display = "block", this.show = !0 }, t.prototype.setClarity = function (e) { 
    e && (this.current.innerHTML = f[e], p.removeClass(document.querySelector(".vcp-vertical-switcher-item.current"), "current"), p.addClass(document.querySelector('.vcp-vertical-switcher-item[data-def="' + e + '"]'), "current"), this.currentItem = document.querySelector('.vcp-vertical-switcher-item[data-def="' + e + '"]'), this.player._switchClarity(e)) }, t }(c["default"]); t["default"] = y } 
// 删除了原有的f这个数组 var l = i(24), c = n(l), u = i(2), p = o(u), h = i(3), d = o(h), y = function (e) { 
    function t(i) { 
    return r(this, t), s(this, e.call(this, i, "ClaritySwitcher")) } return a(t, e), t.prototype.render = function (t) { 
    this.show = !1, this.createEl("div", { 
   "class": "vcp-clarityswitcher"}), this.current = p.createEl("a", { 
   "class": "vcp-vertical-switcher-current"}), this.container = p.createEl("div", { 
   "class": "vcp-vertical-switcher-container"}), this.items = [], this.currentItem = ""; // curRate为配置中的当前倍速 rates为倍速数组 var i = this.options.curRate, f = this.options.rates; this.current.innerHTML = f[i], this.el.appendChild(this.current); for (var o = 0; o < f.length; o++) { 
    var n = p.createEl("a", { 
   "class": "vcp-vertical-switcher-item"}); n.innerHTML = f[o], f[o] == i && (p.addClass(n, "current"), this.currentItem = n), n.setAttribute("data-def", o), this.items.push(n), this.container.appendChild(n) } return this.el.appendChild(this.container), e.prototype.render.call(this, t) }, t.prototype.setup = function () { 
    // 在初始化时加入修改倍速函数_switchRate,切换到当前倍速 this.player._switchRate(this.options.curRate), this.on("click", this.onClick), this.on("mouseenter", this.onMouseEnter), this.on("mouseleave", this.onMouseLeave) }, t.prototype.onClick = function (e) { 
    var t = e.target.getAttribute("data-def"),f = this.options.rates; // 修改点击函数,将_switchClarity改为自定义的_switchRate t ? (this.current.innerHTML = f[t], p.removeClass(this.currentItem, "current"), p.addClass(e.target, "current"), this.currentItem = e.target, this.player._switchRate(f[t])) : !this.show }, t.prototype.onMouseLeave = function () { 
    this.container.style.display = "none", this.show = !1 }, t.prototype.onMouseEnter = function () { 
    this.container.style.display = "block", this.show = !0 } // 去除了无效的setClarity函数,也可不去,对功能无影响,只是代码洁癖 , t } 

加入倍速按钮

 t.__esModule = !0; var l = i(24), c = n(l), u = i(28), p = n(u), h = i(29), d = n(h), f = i(30), y = i(31), A = n(y), v = i(32), m = n(v), g = i(33), w = n(g), b = i(34), M = n(b), I = i(4), S = i(2), E = o(S), _ = i(3), T = o(_), D = i(1), L = o(D), O = function (e) { 
    function t(i) { 
    return r(this, t), s(this, e.call(this, i, "Panel")) } return a(t, e), t.prototype.render = function (t) { 
    return this.createEl("div", { 
   "class": "vcp-controls-panel"}), this.el.appendChild(E.createEl("div", { 
   "class": "vcp-panel-bg"})), this.playToggle = new p["default"](this.player), this.playToggle.render(this.el), this.timelabel = new m["default"](this.player), this.timelabel.render(this.el), this.timeline = new A["default"](this.player), this.timeline.render(this.el), this.options.fullscreenEnabled === !0 && (this.fullscreen = new d["default"](this.player), this.fullscreen.render(this.el)), L.IS_MOBILE || (this.volume = new w["default"](this.player), this.volume.render(this.el)), this.options.videoSource && this.options.videoSource.definitions.length > 1 && !L.IS_MOBILE && (this.claritySwitcher = new M["default"](this.player), this.claritySwitcher.render(this.el)), e.prototype.render.call(this, t) } 

将其改为:

 t.__esModule = !0; // i为对应的esmodule,M["default"]表示n[i(34)]个,即分辨率是第34个function,相应的,我们将倍速的放在最后,就是第40个 var l = i(24), c = n(l), u = i(28), p = n(u), h = i(29), d = n(h), f = i(30), y = i(31), A = n(y), v = i(32), m = n(v), g = i(33), w = n(g), b = i(34), M = n(b), I = i(4), S = i(2), E = o(S), _ = i(3), T = o(_), D = i(1), L = o(D), rate = i(40), Rate = n(rate), O = function (e) { 
    function t(i) { 
    return r(this, t), s(this, e.call(this, i, "Panel")) } return a(t, e), t.prototype.render = function (t) { 
    // 加入倍速节点,由于现有的手机浏览器都支持倍速,所以去掉了手机端判断 return this.createEl("div", { 
   "class": "vcp-controls-panel"}), this.el.appendChild(E.createEl("div", { 
   "class": "vcp-panel-bg"})), this.playToggle = new p["default"](this.player), this.playToggle.render(this.el), this.timelabel = new m["default"](this.player), this.timelabel.render(this.el), this.timeline = new A["default"](this.player), this.timeline.render(this.el), this.options.fullscreenEnabled === !0 && (this.fullscreen = new d["default"](this.player), this.fullscreen.render(this.el)), L.IS_MOBILE || (this.volume = new w["default"](this.player), this.volume.render(this.el)), this.options.videoSource && this.options.videoSource.definitions.length > 1 && (this.claritySwitcher = new M["default"](this.player), this.claritySwitcher.render(this.el)) && (this.rateSwitcher = new Rate["default"](this.player), this.rateSwitcher.render(this.el)), e.prototype.render.call(this, t) } 

使用说明

使用时请先压缩js文件

参数说明

在原有播放器支持的参数下添加了两个参数

参数 类型 默认值 参数说明
rates Array [2, 1.75, 1.5, 1.25, 1, 0.75, 0.5] 倍速数组
curRate Number 1 默认倍速

增加方法&说明

方法 参数 返回值 说明 示例
currentRate() {int} 获取当前的倍速 player.currentRate()

使用示例

var player = new TcPlayer('id_test_video', { 
    "m3u8": "http://2157.liveplay.myqcloud.com/2157_a.m3u8", //请替换成实际可用的播放地址 "autoplay" : true, //iOS 下 safari 浏览器,以及大部分移动端浏览器是不开放视频自动播放这个能力的 "poster" : "http://www.test.com/myimage.jpg", "width" : '480',//视频的显示宽度,请尽量使用视频分辨率宽度 "height" : '320'//视频的显示高度,请尽量使用视频分辨率高度 "rates": [2, 1.5, 1, 0.5], "curRate": '1' }); 

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214410.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午4:20
下一篇 2026年3月18日 下午4:20


相关推荐

  • uboot的relocation原理详细分析「建议收藏」

    uboot的relocation原理详细分析「建议收藏」所谓的relocation,就是重定位,uboot运行后会将自身代码拷贝到sdram的另一个位置继续运行,这个在uboot启动流程分析中说过。但基于以前的理解,一个完整可运行的bin文件,link时指定的链接地址,load时的加载地址,运行时的运行地址,这3个地址应该是一致的,relocation后运行地址不同于加载地址特别是链接地址,ARM的寻址会不会出现问题?

    2022年6月22日
    41
  • 通俗易懂–岭回归(L2)、lasso回归(L1)、ElasticNet讲解(算法+案例)

    通俗易懂–岭回归(L2)、lasso回归(L1)、ElasticNet讲解(算法+案例)

    2021年6月20日
    144
  • rst和markdown_什么是RSA

    rst和markdown_什么是RSA无论是写博客还是相关技术文档,都或多或少会接触到文档文本的编辑,txt不足以满足我们的需求,rst和md文档就应运而生。本博客系列文档主要使用rst文档编写,也有部分md文档,rst文档预览以及文档编译使用到Sphinx和ReadTheDocs,编辑器使用vscode,[vscode配置参考这里](https://taotaodiy.readthedocs.io/en/latest/env/vscode.html)。

    2022年10月1日
    4
  • [数据库] 一文搞懂case when所有使用场景「建议收藏」

    [数据库] 一文搞懂case when所有使用场景「建议收藏」前几天,为了给产品分析当前用户数据结构,写sql的时候使用到了casewhen,今天来总结一下casewhen的使用方法,以此为戒,感觉写的不好请拍砖,感觉写的还可以,给哥们点个赞,或者回复一下,让我意识到我不是一个人在战斗,好了废话不多说了,进入正题。关于casewhen的使用情况,我总结下来有三种,第一、等值转换,第二、范围转换,第三、列转行操作。等值转换咱们在设计数据库的…

    2025年9月17日
    7
  • CreateMutex的使用

    CreateMutex的使用CreateMutex 作用是找出当前系统是否已经存在指定进程的实例 如果没有则创建一个互斥体 HANDLECreate LPSECURITY ATTRIBUTESlp 指向安全属性的指针 BOOLbInitial 初始化互斥对象的所有者 LPCTSTRlpNam 指向互斥对象名的指针 创建一个

    2026年3月17日
    2
  • SIkuli使用

    SIkuli使用SIkuli 使用 1 简介 2sikuli 实际用途 3 下载地址 4 安装步骤 1 简介 SikuliX 通过定位图像和键盘鼠标来操作 GUI 图形化用户界面 能很好的实现 flash 和桌面类应用的自动化 目前暂不支持移动端的使用 2sikuli 实际用途 1 Sikuli 可以用来自动化 Flash 对象或 Flash 网站 2 它可以简单地与 Selenium 或其他工具集成 3 使用 Sikuli 我们可以自动化桌面应用程

    2026年3月20日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号