专栏首页播放器tcplayer 源码改造第二弹 -> 加入倍速播放
原创

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

前序

简介

  • 主要介绍了基于tcplayer的源码改造,加入倍速播放功能 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)) }
  • 不涉及tcplayer的使用以及框架如何调用,详情请看腾讯云点播文档
  • 源码解析中有些注释是笔者加的,如需定位,请不要复制注释
  • 以下示例的代码为重新混淆压缩过,可能与原来的tcplayer.js函数名不同,不可直接复制使用,请务必跟着笔者一步步执行人群
  • 不想自己写播放器而使用tcplayer,但是又受限于播放器本身不带有倍速播放功能的开发人员
  • 不适合没有任何前端基础的小白,请谨慎观看git地址https://github.com/HaverLee1/hls-player源码改造(各位客官请自行格式化代码)实现倍速切换的函数添加配置参数在代码中定位videoSource,在第一个的位置,即初始化赋值的同层如下参数(带有注释的则是笔者加入的参数)

添加获取当前倍速的方法

定位"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

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

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

考虑到倍速播放的样式与切换清晰度类似(其实就是一样啦),可以定位到切换清晰度的代码,进行复制黏贴.

在chrome中打开开发者工具,定位到视频下方的控制栏中清晰度,可以找到对应的节点vcp-clarityswitcher.

视频下方控制栏清晰度的节点

在代码中搜索"vcp-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 = "";
          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
      }

加入倍速按钮

在chrome中打开开发者工具,定位到视频下方的控制栏,可以找到对应的节点"vcp-controls-panel".

视频下方控制栏的节点

在代码中搜索"vcp-controls-panel",会搜索到一些样式和实际添加节点的代码,如下:

    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_358535a.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'
});

相关推荐

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • tcplayer 源码改造第三弹 -> 防盗录

    此时,使用浏览器打开播放页面和开发者工具,定位到视频播放节点,可以看到在"vcp-player"出现了我们自定义的span节点,但是在播放器中还看不到文字浮现,...

    大洼X
  • tcplayer 源码改造第四弹 -> 字幕(srt)

    此时,使用浏览器打开播放页面和开发者工具,定位到视频播放节点,可以看到在"vcp-player"出现了我们自定义的span节点,但是在播放器中还看不到文字浮现,...

    大洼X
  • tcplayer 源码改造第一弹 -> 自定义加密

    在代码中定位videoSource,可定位到如下相似代码,下面的代码中加注释的即为笔者新增的参数

    大洼X
  • three.js 郭先生制作太阳系

    今天郭先生收到评论,想要之前制作太阳系的案例,因为找不到了,于是在vue版本又制作一版太阳系,在线案例请点击three.js制作太阳系(加载时间比较长,请稍等一...

    郭先生的博客
  • python3爬虫-知乎登陆

    参考的是这位博主的博客:https://home.cnblogs.com/u/zkqiang

    py3study
  • .glb格式的模型怎么在three.js中展示

    3D软件中导出的格式一般有.obj 和.glb ,下面是blender 2.8.2 生成模型并在three.js中展示的流程

    tianyawhl
  • 云终端系列(一)—— 实时音视频Web端接入体验(Vue基础音视频通话篇)

    这个系列呢,主要给各位观众老爷看看目前有较大趋势的SaaS应用的SDK在各种主流Web终端的使用姿势和异常分析,如果想要纯粹了解开发的或者云原生,云开发的可以去...

    楚歌
  • 闪屏还可以这样玩

    对于多数应用来说,在进入APP的时候使用短暂的闪屏广告来吸引用户是很常见的一个场景。但随着这种模式的频繁应用,越来越多的用户会感到审美疲劳,甚至不看就跳过闪屏了...

    林焕彬
  • threejs 场景切换 优化性能

    是实现2个场景的定时切换,由于是用在大屏系统,需要浏览器一直能正常运行,不能运行一段时间卡死

    tianyawhl
  • JavaScript this关键字

    Mirror王宇阳

扫码关注云+社区

领取腾讯云代金券