事情起源:公司視頻播放一直是采用的嵌入瀏覽器組件實現視頻的預覽回放等功能。這種實現方式要求客戶使用IE瀏覽器。
最近上線項目使用Html 5開發,要求IE11。項目中使用了視頻播放功能,如果全部升級到IE11問題多,工作量大。
存在的主要問題:
有些系統開發較早,不能在IE11上運行。
部分客戶電腦配置低還在使用XP。因為視頻播放插件要求IE瀏覽器,所以支持H5的chrome,firefox等瀏覽器又不符合要求。
修改項目兼容低版本瀏覽器那是不可能的,只能修改視頻播放插件。
1、視頻插件改為 應用程序 安裝到客戶計算機,使用瀏覽器自定義協議啟動。這樣解決了瀏覽器兼容問題,幾乎所有瀏覽器都支持自定義協議。
測試用例:注冊表寫入以下內容就可以啟動 d:\myapp.exe
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\myapp]
@="URL:AutoHotKey myapp Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\myapp\DefaultIcon]
@="myapp.exe,1"
[HKEY_CLASSES_ROOT\myapp\shell]
[HKEY_CLASSES_ROOT\myapp\shell\open]
[HKEY_CLASSES_ROOT\myapp\shell\open\command]
@="\"d:\\myapp.exe\" \"%1\""
javascript 點擊啟動myapp.exe
function LaunchApp() {
try {
window.location = 'myapp://,start';
}
catch (ex) {
errMsg = "啟動 myapp 報錯.\n\n";
alert(errMsg);
}
return;
};
2、如何判斷用戶有沒安裝視頻應用呢??
網上搜索了一下,看着比較靠譜的:
https://www.cnblogs.com/tangjiao/p/9646855.html
這個實現方案是有缺陷,在chrome瀏覽器通過計時器和當前窗體失去焦點來判段。
計時器設置時間短路了,程序在配置低的電腦上會失敗。
計時器設置時間長了,chrome上切換tab頁就失去焦點,認為已經安裝。
通過觀察百度雲盤的運行方式,得到啟發。百度雲盤也是采用了自定義協議。啟動yundetectservice.exe 監聽10000端口,
猜測web頁面和yundetectservice.exe進行了通信。yundetectservice.exe啟動成功會通知web頁面。
3、采用 websocket 實現,視頻應用啟動的時候監聽某端口等待 web頁面連接,如果一定時間內連接成功就認為已經安裝了插件。
web頁面websocket連接的例子網上比較多。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
var noSupportMessage = "Your browser cannot support WebSocket!";
var ws;
function appendMessage(message) {
$('body').append(message);
}
function connectSocketServer() {
var support = "MozWebSocket" in window ? 'MozWebSocket' : ("WebSocket" in window ? 'WebSocket' : null);
if (support == null) {
appendMessage("* " + noSupportMessage + "<br/>");
return;
}
appendMessage("* Connecting to server ..<br/>");
// create a new websocket and connect
ws = new window[support]('ws://localhost:12345/');
// when data is comming from the server, this metod is called
ws.onmessage = function (evt) {
appendMessage("# " + evt.data + "<br />");
};
// when the connection is established, this method is called
ws.onopen = function () {
appendMessage('* Connection open<br/>');
$('#messageInput').attr("disabled", "");
$('#sendButton').attr("disabled", "");
$('#connectButton').attr("disabled", "disabled");
$('#disconnectButton').attr("disabled", "");
};
// when the connection is closed, this method is called
ws.onclose = function () {
appendMessage('* Connection closed<br/>');
$('#messageInput').attr("disabled", "disabled");
$('#sendButton').attr("disabled", "disabled");
$('#connectButton').attr("disabled", "");
$('#disconnectButton').attr("disabled", "disabled");
}
}
function sendMessage() {
if (ws) {
var messageBox = document.getElementById('messageInput');
ws.send(messageBox.value);
messageBox.value = "";
}
}
function disconnectWebSocket() {
if (ws) {
ws.close();
}
}
function connectWebSocket() {
connectSocketServer();
}
window.onload = function () {
$('#messageInput').attr("disabled", "disabled");
$('#sendButton').attr("disabled", "disabled");
$('#disconnectButton').attr("disabled", "disabled");
}
</script>
</head>
<body>
<input type="button" id="connectButton" value="Connect" onclick="connectWebSocket()"/> <input type="button" id="disconnectButton" value="Disconnect" onclick="disconnectWebSocket()"/> <input type="text" id="messageInput" /> <input type="button" id="sendButton" value="Send" onclick="sendMessage()"/> <br />
</body>
</html>
實現websocket服務也有很多方法。
開源的有,SuperWebSocket,Fleck,WebSocketListener,websocket-sharp 等。
DotNet 高版本自帶了System.Net.WebSockets;
因為功能簡單,僅僅是接受連接請求,所以采用dontnet自帶的,不用引入多余的類庫。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace TestServer { class Program { static void Main(string[] args) { RunEchoServer().Wait(); } private static async Task RunEchoServer() { HttpListener listener = new HttpListener(); listener.Prefixes.Add("http://localhost:12345/"); listener.Start(); Console.WriteLine("Started"); while (true) { HttpListenerContext context = listener.GetContext(); // if (!context.Request.IsWebSocketRequest) { context.Response.Close(); continue; } // Console.WriteLine("Accepted"); // var wsContext = await context.AcceptWebSocketAsync(null); var webSocket = wsContext.WebSocket; // byte[] buffer = new byte[1024]; WebSocketReceiveResult received = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); while (received.MessageType != WebSocketMessageType.Close) { Console.WriteLine($"Echoing {received.Count} bytes received in a {received.MessageType} message; Fin={received.EndOfMessage}"); // Echo anything we receive await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, received.Count), received.MessageType, received.EndOfMessage, CancellationToken.None); received = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); } await webSocket.CloseAsync(received.CloseStatus.Value, received.CloseStatusDescription, CancellationToken.None); webSocket.Dispose(); Console.WriteLine("Finished"); } } } }
至此,通過自定義協議和websocket 解決了視頻插件的問題。
