小言_互联网的博客

视频播放开发笔记-手机控制视频播放(简述WebSocket 的使用)

297人阅读  评论(0)

自己在MPV基础开发的视频播放器windows版,已经可以流畅的播放视频了,并拥有了比较实用的字幕控制和截屏功能,要求不高的话,可以拿来做播放器了。因为我有时候用做显示器,鼠标又不是特别好用,就萌生了用手机控制视频播放的想法。为了同时能支持安卓和IOS,决定采用浏览器的方式。这样一来二去,就用到了WebSocket。
因此本文主要记录WebSocket的使用,核心是两个,第一是HTML 和C#的通信,第二是WebSocket传输图像的处理方法。

C#和HTML通过WebSocket通信的基本操作

开发环境:win10, VS2017
直接说方法吧,非技术方面的东西就不写了,没时间和心情写这玩意儿, 本身我也是抄来的。
C# WebSocket的使用
我用的是:Fleck, 当然还有其他第三方的package。
安装方法请参考:
使用NuGet增加常见包引用
安装成功后,C#方面的代码
说明:这里面用了lamda 表达式,不清楚的同学自己查一查吧。我也是一会儿清楚一会儿不清楚,因为先入为主的原因,特不情愿用这个东西。但是确实简单。

测试界面如下:

Open socket 的代码如下:

      private void button1_Click(object sender, EventArgs e)
        {
   
            FleckLog.Level = LogLevel.Debug;
            var server = new WebSocketServer("ws://192.168.0.103:58888");
            server.Start(socket =>
            {
   
                socket.OnOpen = () =>
                {
   
                    Console.WriteLine("Open!");
                    m_allSockets.Add(socket);
                };
                socket.OnClose = () =>
                {
   
                    Console.WriteLine("Close!");
                    m_allSockets.Remove(socket);
                };
                socket.OnMessage = message =>
                {
   
                    Console.WriteLine(message);
                    m_allSockets.ToList().ForEach(s => s.Send("Echo:" + message));
                    //foreach (var socket in allSockets.ToList())
                    {
   
                        //socket.Send(input);
                        //socket.Send(dat);
                    }
                };
            });

        }

其中 m_allSockets 的定义

List<IWebSocketConnection> m_allSockets = new List<IWebSocketConnection>();

Send UI的功能是发动图片到web browser,代码如下:

        private void btnSendUI_Click(object sender, EventArgs e)
        {
   
            {
   
                foreach (var socket in m_allSockets.ToList())
                {
   
                    //socket.Send(input);
                    Byte[] image = imgLittleKit.ImageToBytes(Properties.Resources.play_solid);
                    string btnID = "btnPlay";
                    SendUIDat(image, btnID,socket);

                    image = imgLittleKit.ImageToBytes(Properties.Resources.skip_previous);
                    btnID = "btnPrev";
                    SendUIDat(image, btnID, socket);
                    //socket.Send(dat);
                    image = imgLittleKit.ImageToBytes(Properties.Resources.skip_next);
                    btnID = "btnNext";
                    SendUIDat(image, btnID, socket);

                    image = imgLittleKit.ImageToBytes(Properties.Resources.volume_down_solid);
                    btnID = "btnVDown";
                    SendUIDat(image, btnID, socket);
                    image = imgLittleKit.ImageToBytes(Properties.Resources.volume_up_solid);
                    btnID = "btnVUp";
                    SendUIDat(image, btnID, socket);

                    image = imgLittleKit.ImageToBytes(Properties.Resources.closedcaption);
                    btnID = "btnSubOff";
                    SendUIDat(image, btnID, socket);

                }
                //input = Console.ReadLine();
            }

        }

发送图片和标识的简单思路:
将标识(String)和图片转换为byte数组发送到web,web方进行解码处理。
imgLittleKit.ImageToBytes函数的代码如下,其功能是将各种格式的图片转换为byte[].

        //from https://www.cnblogs.com/luxiaoxun/p/3378416.html
        public static byte[] ImageToBytes(Image image)
        {
   
            ImageFormat format = image.RawFormat;
            using (MemoryStream ms = new MemoryStream())
            {
   
                //image.Save(ms, ImageFormat.MemoryBmp);
                if (format.Equals(ImageFormat.Jpeg))
                {
   
                    image.Save(ms, ImageFormat.Jpeg);
                }
                else if (format.Equals(ImageFormat.Png))
                {
   
                    image.Save(ms, ImageFormat.Png);
                }
                else if (format.Equals(ImageFormat.Bmp))
                {
   
                    image.Save(ms, ImageFormat.Bmp);
                }
                else if (format.Equals(ImageFormat.Gif))
                {
   
                    image.Save(ms, ImageFormat.Gif);
                }
                else if (format.Equals(ImageFormat.Icon))
                {
   
                    image.Save(ms, ImageFormat.Icon);
                }
                else if (format.Equals(ImageFormat.MemoryBmp))
                {
   
                    image.Save(ms, ImageFormat.Jpeg);
                }

                byte[] buffer = new byte[ms.Length];
                //Image.Save()会改变MemoryStream的Position,需要重新Seek到Begin
                ms.Seek(0, SeekOrigin.Begin);
                ms.Read(buffer, 0, buffer.Length);
                return buffer;
            }
        }

SendUIDat(image, btnID, socket); 函数的代码如下:

        private static  void SendUIDat(byte[] image, string btnID,IWebSocketConnection socket        )
        {
   
            byte[] headArr = new byte[10];

            byte[] imgIDArr = System.Text.Encoding.ASCII.GetBytes(btnID);


            byte[] dat = new byte[headArr.Length + image.Length];
            //form the  byte array to be sent to the clinet , headID and the image ;
            Buffer.BlockCopy(imgIDArr, 0, headArr, 0, imgIDArr.Length);
            headArr[btnID.Length] = 32;// the end of the string , space 
            Buffer.BlockCopy(headArr, 0, dat, 0, headArr.Length);
            Buffer.BlockCopy(image, 0, dat, headArr.Length, image.Length);
            socket.Send(dat);
        }

此时,C#放的代码已经结束了,发送过去的是二进制数字流。

web方的js代码如下,Socket的握手代码

         function ConnectSvr() {
   
			//init();

            var ip = HostIP.value;//界面录入的IP地址
			if(ip==''){
   
				alert('Input the IP address please. CLick on the [ip] button on the playter to get it.');
				return;
			}
            var inc = document.getElementById('incomming');
            var wsImpl = window.WebSocket || window.MozWebSocket;
            var form = document.getElementById('sendForm');
            var input = document.getElementById('sendText');

            inc.innerHTML = "Connecting to the Host ";
			inc.style.color = "Black";

            // create a new websocket and connect
			var connStr = 'ws://'+ip+':58888';
            window.ws = new wsImpl(connStr);
			//wsImpl.binaryType = 'arraybuffer'; 

            // when data is comming from the server, this method is called
            ws.onmessage = function (e) {
   

				//var imgDat = evt.data; // 获取用户接收到的消息数据,为一个ArrayBuffer对象
				
                inc.innerHTML = e.data + '<br/>';
				inc.style.color = "Green";
				
				if(e.data=="[object Blob]"){
   
					var blob = e.data;//blob 
					
					var  headBlob = blogSlice(blob,0,10);//取前10个字节,解码得到标识
					var blobImg = blogSlice(blob,10,blob.size);//10 开始是传递的图像
					var imgID ="Null";
					var reader = new FileReader();
					reader.onload = function(event){
   
						str = reader.result;//内容就在这里
						var imgID = str.substr(0,str.lastIndexOf(" "));  //截取最后一个点号后4个字符
						
						//alert(imgID);
						
						const imgUrl =URL.createObjectURL(blobImg);//生成图像链接,Chrome 可用。
						//alert(imgUrl);
						var btn = document.getElementById(imgID);
						if(btn!=null){
   
							if(imgID=="VImg"){
   
								document.getElementById('VImg').src=imgUrl;//显示图像
							}else{
   
							btn.style="background:url(" +imgUrl+");background-position:center;background-repeat:no-repeat;width:100px;height:10%;";
							btn.innerHTML = "";
							}
						}

					  };
					  reader.readAsText(headBlob);
  
					
				}

				
            };

            // when the connection is established, this method is called
            ws.onopen = function () {
   
                inc.innerHTML = 'Connected to the Host.';
				inc.style.color = "Green";
            };

            // when the connection is closed, this method is called
            ws.onclose = function () {
   
                inc.innerHTML = 'Disconnected from the Host.';
				inc.style.color = "Red";
            }
        }


程序中用到的另一个函数,其功能是截取blob中的字节。

	function blogSlice( blob,start,end ){
   
			if( blob.slice ){
   
				return blob.slice(start,end)
			}else if( blob.webkitSlice ){
   
				  return blob.webkitSlice(start,end)
			}else if( blob.mozSlice ){
   
				return blob.mozSlice(start,end)         
			}else{
   
				  return null
			}
		}

说明上面是demo,不是手机上界面更新的真实代码,因为真实代码比较复杂。

效果如下:
手机初始化界面:

建立连接后,电脑端将图片发送过来,界面变成

这样就完成了一个简单手机控制器的生成。

另:播放器界面如下

以上代码涉及的东西还是比较多和凌乱的,如果认为对你有用,请尽情使用。如果有疑问,可以提出,我会给出解释。

马拉孙 于 泛五地区
2021–4-22


转载:https://blog.csdn.net/Uman/article/details/116032676
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场