腾讯云 Web 超级播放器开发实战

关于超级播放器

腾讯云 Web 超级播放器 TCPlayer 可实现在手机浏览器和 PC 浏览器上播放音视频流的问题,功能强劲,兼容性好,可以不依赖用户安装 App,就能进行播放。

在实际的应用中,我们仍然根据需求直接改造了混淆代码,主要解决了以下问题:

1、增加、集成了播放快进组件

2、更改了一些样式

3、增强了一些旧版手机的兼容性

范例运行环境

操作系统: Windows Server 2019 DataCenter

.net版本: .netFramework4.0 或以上

开发工具:VS2019 C#

浏览器需要支持 H5 技术。

开发前准备

(1)我们需要引入腾讯云 Web 超级播放器的 JS 库,以下是我改造后的资源,可点击如下链接进行下载:

https://download.csdn.net/download/michaelline/89367455

(2)前端布局,下载我的资源后,假设放在当前应用目录下,首先我们需要引入样式单,如下代码:

代码语言:javascript
复制
<link rel="stylesheet" href="tcplayer2021.css" />

其次,引入核心库,如下代码:

代码语言:javascript
复制
 <script type="text/javascript" src="hls.min.0.12.4.js"> </script>
 <script type="text/javascript" src="tcplayer.v4.min.js"> </script>

(3)需要引入 Jquery,以下是一组基于Jquery的自定义开发的扩展应用库,请下载我的资源:

https://download.csdn.net/download/michaelline/88615565

进行引用,本库用于调用服务器静态方法等功能使用。

设计与实现

初始化播放器

播放器需要引入与结合 H5 的 Video 控件,假设有如下引用:

代码语言:javascript
复制
<div id="coplayer" style="box-shadow:2px 0px 35px  #000;  -webkit-box-shadow:2px 0px 35px  #000; background-color:rgb(69,69,69); position:fixed;top:40px;left:0px;width:80%;height:100px;  margin: 0px auto;">

<video id="realcoplayer" autoplay="autoplay" controls="controls" webkit-playsinline playsinline x5-playsinline x-webkit-airplay="allow" runat="server" ></video>
<a id="b_rate" onclick="rate(this);" style=" float:right; line-height:25px; margin-right:10px; color:#fff;display:none;">1x</a>

</div>

其中

1、coplayer 为外围容器层,控制一些样式和位置输出

2、realcoplayer 为 h5 video 控件,用于结合腾讯 web 超级播放器使用

其关键属性说明如下:

序号

属性与设置

说明

1

autoplay="autoplay"

设置是否自动播放,在移动端或IOS系统可能无法实现

2

controls="controls"

是否显示控制工具栏,这里设置为需要显示

3

webkit-playsinline playsinline

兼容性属性:webkit-playsinline使ios 10中设置可以让视频在小窗内播放,即不全屏播放。 playsinline 可使用IOS/微信浏览器支持小窗内播放

4

x5-playsinline

H5 移动是否禁用全屏,这里为允许,为空则不允许

5

x-webkit-airplay="allow"

使此视频支持ios的AirPlay(隔空播放)功能,隔空播放能将各种 Apple 设备中的音乐流传输到家中的多个扬声器上,并让这些扬声器中播放的旋律始终保持合拍, 让音乐荡漾在每个房间。

3、b_rate 为用于改造及引入超级播放器的快进组件,配合其使用。

客户端播放器的初始化代码如下:

代码语言:javascript
复制
var player = new TCPlayer('realcoplayer', {
fileID: 111,
appID: '125407',
playbackRates: [0.5, 1, 1.25, 1.5, 2],
autoplay: false,
live: false,
x5_player: true,
volume: 0.5,
flash: true,
x5_player: true,
systemFullscreen: false,
playsinline: true,
x5_orientation: 0,
x5_type: 'h5',
allowFullScreen: false,
width:_w,
height:_h,

});

播放器重要属性设置

在实际使用中,为保证良好的可用性和兼容性,还需要设置如下属性,说明见下表:

序号

参数

类型

说明

1

fileID

string

云点播平台可播放视频文件的 fileID

2

appID

string

云点播平台申请的 appID

3

playbackRates

float[]

快进倍速设置,如此数组 [0.5, 1, 1.25, 1.5, 2]

4

autoplay

bool

是否设置为自动播放,false 为不自动

5

live

bool

是否直播功能,默认为 false

6

x5_player

bool

设为 true 。是否启用 TBS 的播放 flv 或 hls 。启用时播放器将在 TBS 模式下(例如 Android 的微信、QQ 浏览器),将 flv 或 hls 播放地址直接赋给 <video> 播放。

7

volume

float

默认音量,0-1,0.5为居中

8

flash

bool

一个兼容的重要属性,设为 true

9

systemFullscreen

bool

开启后(true),在不支持 Fullscreen API 的浏览器环境下,尝试使用浏览器提供的 webkitEnterFullScreen 方法进行全屏,如果支持,将进入系统全屏,控件为系统控件

10

playsinline

bool

兼容性属性,设为 true

11

x5_orientation

int

通过 video 属性 “x5-video-orientation” 声明 TBS 播放器支持的方向,可选值: 0:landscape 横屏 1:portraint 竖屏 2:landscape &verbar; portrait 跟随手机自动旋转。 (备注:该属性为 TBS 内核实验性属性,非 TBS 内核不支持)

12

x5_type

string

通过 video 属性 “x5-video-player-type” 声明启用同层 H5 播放器,支持的值:h5-page (该属性为 TBS 内核实验性属性,非 TBS 内核不支持)

13

allowFullScreen

bool

兼容性写法,是否允许全屏播放

14

width

int

设置播放器宽度,单位为像素。

15

height

int

设置播放器高度,单位为像素。

播放器实用事件

通过跟踪超级播放器提供的监听事件,实现我们的开发需求,其关键事件说明如下:

序号

事件

说明

1

timeupdate

播放时间更新事件,可记录播放时间,其结构体如下: player.on('timeupdate',function(){ })

2

play

开始播放时事件,其结构体如下: player.on('play',function(){ })

3

fullscreenchange

切换全屏状态事件,其结构体如下: player.on('fullscreenchange',function(){ })

4

seeked

拖动播放进度结束事件,其结构体如下: player.on('seeked',function(){ })

5

pause

播放暂停时事件,其结构体如下: player.on('pause',function(){ })

6

ended

播放结束时事件,其结构体如下: player.on('ended',function(){ })

7

canplay

播放能力执行成功事件,其结构体如下: player.on('canplay',function(){ })

8

loadeddata

音视频数据加载完毕时事件,其结构体如下: player.on('loadeddata',function(){ })

9

ratechange

改变快进倍速完成时事件,其结构体如下: player.on('ratechange',function(){ })

类实现代码如下:

代码语言:javascript
复制
public class QR_LIMIT_STR_SCENE
{
public string AccessToken { get; set; }
public string ticket { get; set; }
public string url { get; set; }
public string ResultJson = "";
public QR_LIMIT_STR_SCENE()
{
}
public string getUrl(string scene_str)
{
string PostJson = "{&#34;action_name&#34;: &#34;QR_LIMIT_STR_SCENE&#34;, &#34;action_info&#34;: {&#34;scene&#34;: {&#34;scene_str&#34;: &#34;" + scene_str + "&#34;}}}";
String action = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + AccessToken;
WebService ws = new WebService();
string rs = ws.GetResponseResult(action, Encoding.UTF8, "POST", PostJson);
Newtonsoft.Json.Linq.JObject jsonObj = Newtonsoft.Json.Linq.JObject.Parse(rs);
ticket = jsonObj["ticket"] != null ? jsonObj["ticket"].ToString() : "";
url = jsonObj["url"] != null ? jsonObj["url"].ToString() : "";
ResultJson = rs;
return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;

        }

}

一些兼容性判断
浏览器支持

编写浏览器是否支持终端H5播放的判断,实现代码如下:

代码语言:javascript
复制
        function checkVideo() {
if (!!document.createElement('video').canPlayType) {
var vidTest = document.createElement("video");
oggTest = vidTest.canPlayType('video/ogg; codecs="theora, vorbis"');
if (!oggTest) {
h264Test = vidTest.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
if (!h264Test) {
return false;
}
else {
if (h264Test == "probably") {
return true;
}
else {
return false;
}
}
}
else {
if (oggTest == "probably") {
return true;
}
else {
return false;
}
}
}
else {
return false;
}
return true;
}
if (!checkVideo()) {
alert('您的浏览器不支持Video播放,请使用支持H5技术的浏览器!');
}
关于华为手机

在某些华为手机我们发现倍速快进组件样式显示异常,因此引入 JS 函数,对机型增加了一些判断 ,代码如下:

代码语言:javascript
复制
    function judgeBrand(sUserAgent) {
var isIphone = sUserAgent.match(/iphone/i) == "iphone";
var isHuawei = sUserAgent.match(/huawei/i) == "huawei";
var isHonor = sUserAgent.match(/honor/i) == "honor";
var isOppo = sUserAgent.match(/oppo/i) == "oppo";
var isOppoR15 = sUserAgent.match(/pacm00/i) == "pacm00";
var isVivo = sUserAgent.match(/vivo/i) == "vivo";
var isXiaomi = sUserAgent.match(/mi\s/i) == "mi ";
var isXiaomi2s = sUserAgent.match(/mix\s/i) == "mix ";
var isRedmi = sUserAgent.match(/redmi/i) == "redmi";
var isSamsung = sUserAgent.match(/sm-/i) == "sm-";

   if (isIphone) {
       return &#39;iphone&#39;;
   } else if (isHuawei || isHonor) {
       return &#39;huawei&#39;;
   } else if (isOppo || isOppoR15) {
       return &#39;oppo&#39;;
   } else if (isVivo) {
       return &#39;vivo&#39;;
   } else if (isXiaomi || isRedmi || isXiaomi2s) {
       return &#39;xiaomi&#39;;
   } else if (isSamsung) {
       return &#39;samsung&#39;;
   } else {
       return &#39;default&#39;;
   }

}

对华为手机的判断处理代码如下:

代码语言:javascript
复制
if(judgeBrand(navigator.userAgent.toLowerCase())=='huawei'){
$("#b_rate").html="1x";
$("#b_rate").css("display","");
}else{
$("#b_rate").css("display","none");
}
实现代码

相对完整的实现代码(包括样式引入、前端控件和JS控制)如下:

代码语言:javascript
复制
<link rel="stylesheet" href="tcplayer2021.css" />
<asp:TextBox ID="x_roomid" checkSchema="" runat="server" style="display:none"></asp:TextBox>
<script type="text/javascript" src="hls.min.0.12.4.js"> </script>
<script type="text/javascript" src="tcplayer.v4.min.js"> </script>
<script type="text/javascript" language="javascript" src="jquery.js" ></script>

<div id="h5panel" runat="server" >
<asp:TextBox ID="x_fileid" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="current" Text="0" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="duration" Text="0" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="mname" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="x_mp4url" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="x_CoverUrl" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="x_playMark" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="x_lasttime" style="display:none;" runat="server"></asp:TextBox>
<asp:TextBox ID="confirmflag" Text="0" runat="server" style="display:none;" ></asp:TextBox>
<asp:TextBox ID="playMarkflag" Text="0" runat="server" style="display:none;" ></asp:TextBox>
<asp:TextBox ID="x_videocount" Text="0" style="display:none;" runat="server"></asp:TextBox>

<div id="coplayer" style="box-shadow:2px 0px 35px #000; -webkit-box-shadow:2px 0px 35px #000; background-color:rgb(69,69,69); position:fixed;top:40px;left:0px;width:80%;height:100px; margin: 0px auto;">
<video id="realcoplayer" autoplay="autoplay" controls="controls" webkit-playsinline playsinline x5-playsinline x-webkit-airplay="allow" runat="server" ></video>
<a id="b_rate" onclick="rate(this);" style=" float:right; line-height:25px; margin-right:10px; color:#fff;display:none;">1x</a>
</div>

<script language="javascript">
var windowheight=$(window).height();
form = document.forms[0];

    function checkVideo() {
        if (!!document.createElement(&#39;video&#39;).canPlayType) {
            var vidTest = document.createElement(&#34;video&#34;);
            oggTest = vidTest.canPlayType(&#39;video/ogg; codecs=&#34;theora, vorbis&#34;&#39;);
            if (!oggTest) {
                h264Test = vidTest.canPlayType(&#39;video/mp4; codecs=&#34;avc1.42E01E, mp4a.40.2&#34;&#39;);
                if (!h264Test) {
                    return false;
                }
                else {
                    if (h264Test == &#34;probably&#34;) {
                        return true;
                    }
                    else {
                        return false;
                    }
                }
            }
            else {
                if (oggTest == &#34;probably&#34;) {
                    return true;
                }
                else {
                    return false;
                }
            }
        }
        else {
            return false;
        }
        return true;
    }
    if (!checkVideo()) {
        alert(&#39;您的浏览器不支持Video播放,请使用支持H5技术的浏览器!&#39;);
    }    

var timer=null;
var curtime=0;
function stimer(){ 
    timer=window.setInterval(&#34;setpmark()&#34;,1000);
}
function etimer(){
    window.clearInterval(timer);
}

    function secondToDate(result) {
    var h = Math.floor(result / 3600) &lt; 10 ? &#39;0&#39;+Math.floor(result / 3600) : Math.floor(result / 3600);
    var m = Math.floor((result / 60 % 60)) &lt; 10 ? &#39;0&#39; + Math.floor((result / 60 % 60)) : Math.floor((result / 60 % 60));
    var s = Math.floor((result % 60)) &lt; 10 ? &#39;0&#39; + Math.floor((result % 60)) : Math.floor((result % 60));
    if(h==&#34;00&#34;){
    return result = m + &#34;:&#34; + s;
    }else{
    return result = h + &#34;:&#34; + m + &#34;:&#34; + s;
    }
}
    var _w = $(document).width();
    if(_w&gt;=1200){
        _w=640;
    }
    _h=_w/16*9;
    
function resizeAll(){
    var _w = $(document).width();
    if(_w&gt;=1200){
        _w=640;
    }
    _h=_w/16*9;
    if(player.isFullscreen()==true){
    }
    document.getElementById(&#39;coplayer&#39;).style.left=($(document).width()-_w)/2+&#39;px&#39;;
    document.getElementById(&#39;coplayer&#39;).style.width=_w+&#39;px&#39;;
    document.getElementById(&#39;coplayer&#39;).style.height=_h+&#39;px&#39;;

function aiplaymark(curtime){
    player.currentTime(curtime);
    player.play();
}

var debug=document.getElementById(&#39;debug&#39;);
var curtime=0;
var seeked=false;

var player = new TCPlayer(&#39;realcoplayer&#39;, { fileID: document.getElementById(&#39;x_fileid&#39;).value, appID: &#39;12540&#39;,

playbackRates: [0.5, 1, 1.25, 1.5, 2],
autoplay: false,
live: false,
x5_player: true,
volume: 0.5,
flash: true,
x5_player: true,
systemFullscreen: false,
playsinline: true,
x5_orientation: 0,
x5_type: 'h5',
allowFullScreen: false,
width:_w,
height:_h,

});

player.on(&#39;timeupdate&#39;,function(){ 
    document.getElementById(&#34;current&#34;).value=player.currentTime();
})
player.on(&#39;play&#39;,function(){
    stimer();
    updcountinfo(&#39;play&#39;,0);
})
player.on(&#39;fullscreenchange&#39;,function(){
})

player.on(&#39;seeked&#39;,function(){
})
player.on(&#39;pause&#39;,function(){
    etimer();
})
player.on(&#39;ended&#39;,function(){
    etimer();
})
player.on(&#39;canplay&#39;,function(){
})
player.on(&#39;loadeddata&#39;,function(){
    player.poster(document.getElementById(&#39;x_CoverUrl&#39;).value);
    if(judgeBrand(navigator.userAgent.toLowerCase())==&#39;huawei&#39;){
        $(&#34;#b_rate&#34;).html=&#34;1x&#34;;
        $(&#34;#b_rate&#34;).css(&#34;display&#34;,&#34;&#34;);
    }else{
        $(&#34;#b_rate&#34;).css(&#34;display&#34;,&#34;none&#34;);
    }
      this.currentTime(document.getElementById(&#34;current&#34;).value);
})

player.on(&#39;ratechange&#39;,function(){
    $(&#39;#b_rate&#39;).html($(&#39;.vjs-playback-rate-value&#39;).html());
})
function rate(o) {         
    document.querySelectorAll(&#39;.vjs-playback-rate&#39;)[1].click();
}

function judgeBrand(sUserAgent) {
   var isIphone = sUserAgent.match(/iphone/i) == &#34;iphone&#34;;
   var isHuawei = sUserAgent.match(/huawei/i) == &#34;huawei&#34;;
   var isHonor = sUserAgent.match(/honor/i) == &#34;honor&#34;;
   var isOppo = sUserAgent.match(/oppo/i) == &#34;oppo&#34;;
   var isOppoR15 = sUserAgent.match(/pacm00/i) == &#34;pacm00&#34;;
   var isVivo = sUserAgent.match(/vivo/i) == &#34;vivo&#34;;
   var isXiaomi = sUserAgent.match(/mi\s/i) == &#34;mi &#34;;
   var isXiaomi2s = sUserAgent.match(/mix\s/i) == &#34;mix &#34;;
   var isRedmi = sUserAgent.match(/redmi/i) == &#34;redmi&#34;;
   var isSamsung = sUserAgent.match(/sm-/i) == &#34;sm-&#34;;

   if (isIphone) {
       return &#39;iphone&#39;;
   } else if (isHuawei || isHonor) {
       return &#39;huawei&#39;;
   } else if (isOppo || isOppoR15) {
       return &#39;oppo&#39;;
   } else if (isVivo) {
       return &#39;vivo&#39;;
   } else if (isXiaomi || isRedmi || isXiaomi2s) {
       return &#39;xiaomi&#39;;
   } else if (isSamsung) {
       return &#39;samsung&#39;;
   } else {
       return &#39;default&#39;;
   }

}
function updcountinfo(ctype,counts){
if(ctype=='play'){
if(document.getElementById('x_videocount').value=='0'){
document.getElementById('x_videocount').value='1';
}else{
return;
}
}
callServerFunction("", "updateCountInfo","{cid:'"+$("#x_cid").val()+"',ctype:'"+ctype+"',counts:'"+counts+"'}", sscount);
}

function setpmark(){
     curtime++;
     callServerFunction(&#34;&#34;, &#34;updatePersonLearnInfo&#34;,&#34;{cid:&#39;&#34;+$(&#34;#x_cid&#34;).val()+&#34;&#39;,uid:&#39;&#34;+$(&#34;#x_uid&#34;).val()+&#34;&#39;,pid:&#39;&#34;+$(&#34;#x_pid&#34;).val()+&#34;&#39;,playmark:&#39;&#34;+$(&#39;#current&#39;).val()+&#34;&#39;,steptime:&#39;1&#39;}&#34;, ss);
}

var sTime;

</script>

小结

(1)关于更多的超级播放器 SDK 开发介绍,请参照如下链接:

https://cloud.tencent.com/document/product/881/30818

(2)实现代码中事件代码仅供参考,对于服务器静态方法实现需要根据我们实际的应用需求进行开发。

(3)实现代码中的前端控件,只为演示实例使用,可根据需要改造符合自己的开发规范。

感谢您的阅读,希望本文能够对您有所帮助。