1前面基於awrtc.js連接kurento的unity客戶端,能播放出來了,但是在某些環境下會有問題。
一、現狀整理
1.kurento-player的前端頁面在不同瀏覽器都能運行。
不過在IE裏面它自己會彈出一個要求安裝插件的提示,安裝後就和其他瀏覽器一樣能夠播放了。這是不是說明IE目前沒有原生支持WebRTC?
“用IE打開頁面,提示需要安裝 Temasys WebRTC Plugin插件,下載、安裝、刷新,可以播放。”
2.基於awrtc.js的html客戶端頁面,只有在Chrome裏面能播放,Firefox、IE、Edge裏面不能播放。
Firefox裏面有到顯示Video的地方,但是沒有播放視頻;
IE裏面直接說沒有RTCPeerConnection;
Edge裏面則是沒有createDataChannel,說明也不支持。
但是,awrtc.js裏面有看到對WebRTC相關的實現代碼啊。奇怪。
3.我測試用的打包好的webgl頁面,在Chrome裏面能夠播放,在Firefox裏面不能播放。
4.給別人接手,放到一個Demo項目裏面的WebGL打包,連接的是另一個攝像頭,在Chrome裏面不能播放,在Firefox裏面能夠播放。
這裏是爲了給別人演示,放到了一個能外網訪問的電腦上,電腦是雙網卡,有個內網ip和外網ip,攝像頭是在內網上的。訪問的電腦(也就是我的電腦是無法直接訪問那個攝像頭的)。
Firefox上播放正常,Chrome上播放結果是:
偶爾有幾次,就算有上面的錯誤Chrome也能正常播放。
但是手機上的:Chrome播放又是正常的,Firefox正常播放;默認的自帶瀏覽器則不能播放,一直在那裏轉圈;UC瀏覽器姑且試了一下,點擊Start沒反應。
現在的問題是我計劃的WebGL的推薦用戶使用瀏覽器是 1.Chrome 2.Firefox 3.IE ,至少Chrome和Firefox都要正常使用
5.用純js客戶端方式連接外網電腦,Firefox也無法播放視頻。
提示:ICE failed, add a TURN server and see about:webrtc for more details
這個看來需要把TURN server弄起來了
---------------------------------------------------------------------------------------------
現在有兩條路走,1.找到不能播放的原因;2.改kurento-player的前端代碼,放到unity裏面。
都挺花時間的,考慮先做第二種試試吧。其實kurento-player的前端代碼比awrtc封裝的好,也比較靈活。
不能播放的問題的具體研究放到另外一篇文章(視頻服務器(10) Kurento[5] ICE問題)裏面吧。
二、kurento-player js客戶端2
相當於是對《視頻服務器(7) Kurento[2] js客戶端》的後續,這次相當於要把kurento-player-js的代碼和unity結合起來。本來在《視頻服務器(8) Kurento[3] unity客戶端 》裏面已經用awrtc.js插件把視頻播放出來了,換一個外網環境,chrome就播放不出來了。總之繼續弄吧,本來就是一個未完成的任務。
2.1 Unity和Html交互接口
參考之前的awrtc.js,寫一個新的接口,和html中的js代碼交互。爲了提高調試效率,具體js代碼,就不放在unity裏面,直接放在另一個外部js文件中。相關的引用添加到webgl的打包模板中。
2.1.1 Unity調用外部js代碼
在Unity裏面獲取相應的配置信息(videoUrl,serverUrl,uiInfo),發送給外部的js代碼,進行視頻播放和調試。
1.Unity調用接口代碼(KurentoClient.cs)
public void Connect()
{
Debug.Log("KurentoClient.Connect");
Config.ui = GetUIInfo();
string jsonConfig = JsonUtility.ToJson(Config);
Debug.Log(jsonConfig);
#if UNITY_WEBGL && !UNITY_EDITOR
var jsonResult=KurentoClientJS.Unity_Kurento_Connect(jsonConfig);
Debug.Log("jsonResult:" + jsonResult);
Id = JsonUtility.FromJson<ConnectId>(jsonResult);
Debug.Log(Id);
Debug.Log(Id.id);
#endif
if (KurentoManager.Instance)
{
KurentoManager.Instance.AddClient(this);
}
}
2.接口代碼Unity部分
public static class KurentoClientJS
{
[DllImport("__Internal")]//開始播放
public static extern string Unity_Kurento_Connect(string config);
[DllImport("__Internal")]//暫時沒用
public static extern int Unity_Kurento_Update(string e);
[DllImport("__Internal")]//停止、暫停、顯示界面、移動界面等指令
public static extern string Unity_Kurento_Command(string config);
}
2.接口代碼jslib部分(kurento_unity.jslib)
Unity_Kurento_Connect:function(pconfig){
var configJson=Pointer_stringify(pconfig);//轉換爲字符串
console.log("------- Unity_Kurento_Connect",configJson,this);
var config=JSON.parse(configJson);//轉換爲對象
console.log("------- config",config);
var client=kurento;//kurento_client.jspre裏面的kurento模塊
var result= client.CAPI_Kurento_Connect(config);//具體
console.log("------- result",result);
//轉換爲字符串
var returnStr=JSON.stringify(result);
console.log("------- returnStr",returnStr);
var bufferSize = lengthBytesUTF8(returnStr) + 1;
var buffer = _malloc(bufferSize);
stringToUTF8(returnStr, buffer, bufferSize);
console.log("------- buffer",buffer);
return buffer;
}
3.接口代碼jspre部分(kurento_client.jspre)
var kurentoClient=null;
if(unityTool!=null&&unityTool.kurentoClient!=null){
kurentoClient=unityTool.kurentoClient;//對接到外部js文件,提高開發調試效率
}
else{
kurentoClient=new KurentoClientClass();
}
function func_CAPI_Kurento_Connect(config){
console.log("------- func_CAPI_Kurento_Connect",config,this);
if(kurentoClient){
return kurentoClient.Connect(config);
}
return {id:-2};
}
4.外部js代碼
KurentoClient.prototype.Connect = function (config) {
//var player
config.devId="dev1";
config.serverUrl="ws://60.191.23.20:8443/player";
config.videoUrl="rtsp://admin:[email protected]/h264/ch1/main/av_strema";
console.log(tag + ".Connect", config, this);
var player=this.players.find(function(item){
return item.devId==config.devId;
});
if(player==null){//第一次播放
this.currentId++;
//動態創建Video並播放
this.CreateVideo(config);
player=new KurentoPlayerClass(config);
player.id=this.currentId;
player.devId=config.devId;
player.start();
//保存起來
this.players.push(player);
}
else{
this.stopPlayers.splice($.inArray(player,this.stopPlayers),1);
var $ele=$(player.video);
this.ShowHtmlElementEx($ele, config.ui);//調整位置
}
return {id: this.currentId};
};
//unityTool是個事先創建好的對象
unityTool.kurentoClient=null;
unityTool.kurentoClient=new KurentoClientClass();
unityTool.kurentoClient.StartRemovePlayerTimer();
2.1.2 js代碼發送信息給Unity
一些html事件觸發後通知unity,或者發送數據給unity。
現在用於在頁面大小發生變化時(最大化,還原)讓unity重新發送一下界面信息。
1.js代碼部分
$(window).resize(function(){//自動調整
console.info("client windowresize");
//....
console.info("setTimeout1",new Date(),this);
var t=setTimeout(function(){
console.info("setTimeout2",new Date(),this);
var evt={vid:0,type:1,data:"window_resize"};
var s=JSON.stringify(evt);
unityInstance.SendMessage('KurentoManager', 'ReceiveEvent',s);
},100);//這裏要延遲執行,立刻執行的話,比例不對,unity裏面的大小還沒變。
});
2.unity代碼部分
KurentoManager.js,腳本,在場景中掛在一個叫“KurentoManager"的空對象上。
public void ReceiveEvent(string evt)
{
Debug.Log("KurentoMananger.ReceiveEvent:" + evt);
KurentoEvent ke = JsonUtility.FromJson<KurentoEvent>(evt);
Debug.Log("ke:" + ke);
Debug.Log("type:" + ke.type);
Debug.Log("vid:" + ke.vid);
Debug.Log("data:" + ke.data);
Debug.Log("(ke.data == window_resize):" + (ke.data == "window_resize"));
Debug.Log("clients:" + clients.Count);
if (ke.vid == 0)//全部
{
foreach (KeyValuePair<int, KurentoClient> pair in clients)
{
pair.Value.DoEvent(ke);
}
}
else
{
KurentoClient client = GetClient(ke.vid);
if (client != null)
{
client.DoEvent(ke);
}
Debug.Log("client:" + client);
Debug.Log("id:" + client.Id.id);
}
}
KurentoClient.cs
public void DoEvent(KurentoEvent ke)
{
if (ke == null)
{
Debug.LogError("KurentoClient.DoEvent ke == null");
return;
}
if (ke.data == "window_resize")
{
MoveWnd(null);
}
}
2.2 kurento-player封裝
對kurento-player例子的index.js文件進行一下封裝,把相關的變量、方法都放到一個類裏面;這樣子可以把多個攝像頭的變量分類開來。(說實話javascript不是很精通,會用,能看得懂,但是具體的類啊,繼承啊,只會模仿現成的代碼。)
2.3 Unity和Html界面同步
unity部分,獲取界面信息,現在支持ScaleWithScreenSize了。
前面的座標轉換(UnityWebGL調研(5) 和網頁交互)需要Canvas上面的CanvsScaler是Constant Pixel Szie。
public UIInfo GetUIInfo()
{
UIInfo info = new UIInfo();
CanvasScaler canvasScaler = VideoImage.GetComponentInParent<CanvasScaler>();
if (canvasScaler)
{
if (canvasScaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize)
{
info.mode = 1;//默認是ConstantPixelSize,是0
}
}
//獲取相對於最外層Canvas的座標
var posEx = Vector2.zero;
RectTransform[] pRects=VideoImage.GetComponentsInParent<RectTransform>();
for (int i = 0; i < pRects.Length; i++)
{
RectTransform rec = pRects[i];
RectTransform recP = null;
if (i < pRects.Length - 1)
{
recP = pRects[i + 1];
var p = rec.anchoredPosition;
if (rec.anchorMin.x == 1 && rec.anchorMax.x == 1)
{
p.x += recP.rect.width / 2;
}
posEx += p;
}
else
{
info.swidth = rec.rect.width;
info.sheight = rec.rect.height;
}
Debug.Log("rec:" + rec.anchoredPosition + "," + rec.rect + ",|" + rec.anchorMin + "," + rec.anchorMax + "," + rec + "," + recP);
}
var pos = posEx;
var rect = VideoImage.rect;
info.id = "video1";
info.x = pos.x;
info.y = pos.y;
info.height = rect.height;
info.width = rect.width;
return info;
}
js部分
KurentoClient.prototype.ShowHtmlElement=function($ele,posX, posY,sizeX,sizeY,padding){
"use strict"
if(this.screenSizeX==null){
this.screenSizeX=$("#unityContainer").css("width").replace("px","");
}
if(this.screenSizeY==null){
this.screenSizeY=$("#unityContainer").css("height").replace("px","");
}
var width=sizeX-padding;
var height=sizeY-padding;
$ele.css("width",width);
$ele.css("height",height);
var x=this.screenSizeX/2+posX-sizeX/2+padding/2+1;
var y=this.screenSizeY/2-posY-sizeY/2+padding/2;
$ele.css("left",x);
$ele.css("top",y);
$ele.show();
};
KurentoClient.prototype.ShowHtmlElementEx=function($ele,ui){
"use strict"
if(this.unityContainer==null){
this.unityContainer=$("#unityContainer");
}
this.screenSizeX=this.unityContainer.css("width").replace("px","");
this.screenSizeY=this.unityContainer.css("height").replace("px","");
var scale=1;
if(ui.mode==1){//Unity裏面的CanvasScaler.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize,並且Match=0.5
scale=this.screenSizeX/ui.swidth;
}
console.error("ui",ui,ui.swidth/ui.sheight);
console.error("==>SetScreenInfo",this.screenSizeX,this.screenSizeY);
console.error("scale",scale);
this.ShowHtmlElement($ele, ui.x * scale, ui.y* scale, ui.width* scale, ui.height* scale,this.padding* scale);//調整位置
};
KurentoClient.prototype.Command = function (cmd) {
console.info(tag + ".Command", cmd, this);
if(cmd==null){
console.error("cmd==null");
return {success:false};
}
var player=this.players.find(function(item){
return item.id==cmd.vid;
});
if(player==null){
console.error("no find player",cmd);
return {success:false};
}
switch (cmd.cid) {
case "resume":
player.resume();
break;
//....
case "moveWnd":
//player.moveWnd();
var data=cmd.data;
if(data=="end"){
}else if(data=="begin"){
}
else{
var ui=cmd.data;
player.config.ui=ui;
ui.id="video_element_"+cmd.vid;
//var video=this.SetupVideoElement();//動態創建元素
var $ele=$(player.video);
this.ShowHtmlElementEx($ele, ui);//調整位置
}
break;
default:
break;
}
return {success:true};
};
html部分
<body>
<div class="webgl-content">
<div id="unityContainer" style="width: 960px; height: 600px"></div>
<div id="videoContainer" style="width:100%;height:100%;position:absolute;left:0px;top:0px;pointer-events: none;"></div>
</body>
2.4 效果
從結論上看,可以,而且,在網頁裏面播放視頻,不勉強放到unity裏面顯示的話,播放效果更好,也不影響unity的性能。
在Unity裏面拖動界面,視頻Video也能跟隨過去。
手機上的Chrome和Firefox能夠正常播放,PC上的Firefox能夠正常播放。
2.5 Unity中顯示圖片
暫時先不做