• Android和C#实时视频传输Demo


    说起去年的Demo。以今天的免费整齐优势。

    原理很easy,虽然没有写android申请书。但,好了~ 高级语言是相通的。傲慢约。就这么简单研究了一下api后,找到相机对象有一个预览回调方法。

    意识到发生了什么Camera.PreviewCallback接口。就能够得到一个每一帧画面的回调事件,那么思路就非常easy了。

    拿到画面后,进行下简单的压缩,然后把图像用Socket传输到server上。server上绑定到一个窗体的picBox上就能够了。

    当然,这里还牵扯到多线程的问题,由于一个SocketServer能够实现和多个client建立连接,而每个连接都须要独立的线程来实现监听。

    安卓端代码:

    package com.xwg.monitorclient;
    
    
    import java.io.BufferedOutputStream;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.List;
    import java.util.zip.DeflaterOutputStream;
    
    import android.graphics.Rect;
    import android.graphics.YuvImage;
    import android.hardware.Camera;
    import android.hardware.Camera.Size;
    import android.os.Bundle;
    import android.preference.PreferenceManager;
    import android.app.Activity;
    import android.content.SharedPreferences;
    import android.view.Menu;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.view.WindowManager;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.EditText;
    
    public class MainActivity extends Activity implements SurfaceHolder.Callback,
    Camera.PreviewCallback{
    	private SurfaceView mSurfaceview = null; // SurfaceView对象:(视图组件)视频显示
        private SurfaceHolder mSurfaceHolder = null; // SurfaceHolder对象:(抽象接口)SurfaceView支持类
        private Camera mCamera = null; // Camera对象,相机预览 
        
        /**服务器地址*/
        private String pUsername="XZY";
        /**服务器地址*/
        private String serverUrl="192.168.0.3";
        /**服务器端口*/
        private int serverPort=9999;
        /**视频刷新间隔*/
        private int VideoPreRate=1;
        /**当前视频序号*/
        private int tempPreRate=0;
        /**视频质量*/
        private int VideoQuality=85;
        
        /**发送视频宽度比例*/
        private float VideoWidthRatio=1;
        /**发送视频高度比例*/
        private float VideoHeightRatio=1;
        
        /**发送视频宽度*/
        private int VideoWidth=320;
        /**发送视频高度*/
        private int VideoHeight=240;
        /**视频格式索引*/
        private int VideoFormatIndex=0;
        /**是否发送视频*/
        private boolean startSendVideo=false;
        /**是否连接主机*/
        private boolean connectedServer=false;
        
        private Button myBtn01, myBtn02;
        
        private EditText txtIP;
        
        @Override
        public void onStart()//又一次启动的时候
        {	
        	mSurfaceHolder = mSurfaceview.getHolder(); // 绑定SurfaceView。取得SurfaceHolder对象
        	mSurfaceHolder.addCallback(this); // SurfaceHolder增加回调接口       
        	mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 设置显示器类型,setType必须设置
    	    //读取配置文件
            SharedPreferences preParas = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
            pUsername=preParas.getString("Username", "XZY");
            serverUrl=preParas.getString("ServerUrl", "192.168.0.3");
        	String tempStr=preParas.getString("ServerPort", "9999");
        	serverPort=Integer.parseInt(tempStr);
            tempStr=preParas.getString("VideoPreRate", "1");
            VideoPreRate=Integer.parseInt(tempStr);	            
            tempStr=preParas.getString("VideoQuality", "85");
            VideoQuality=Integer.parseInt(tempStr);
            tempStr=preParas.getString("VideoWidthRatio", "100");
            VideoWidthRatio=Integer.parseInt(tempStr);
            tempStr=preParas.getString("VideoHeightRatio", "100");
            VideoHeightRatio=Integer.parseInt(tempStr);
            VideoWidthRatio=VideoWidthRatio/100f;
            VideoHeightRatio=VideoHeightRatio/100f;
            
            super.onStart();
        }
        
        @Override
        protected void onResume() {
            super.onResume();        
            InitCamera();
        }
        
        /**初始化摄像头*/
        private void InitCamera(){
        	try{
        		
        		mCamera = Camera.open();
        		List<Size> list = mCamera.getParameters().getSupportedPreviewSizes();
        		for(Size s : list)
        		{
        			if(s.width<=640)
        			{
        				Camera.Parameters params = mCamera.getParameters();
        				params.setPreviewSize(s.width, s.height);
        				mCamera.setParameters(params);
        				break;
        			}
        		}
        	} catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        @Override
        protected void onPause() {
        	try{
    	        if (mCamera != null) {
    	        	mCamera.setPreviewCallback(null); // 。。这个必须在前。不然退出出错
    	            mCamera.stopPreview();
    	            mCamera.release();
    	            mCamera = null;
    	        } 
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            super.onPause();
        }
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
          //禁止屏幕休眠
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 
            		WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                   
            mSurfaceview = (SurfaceView) findViewById(R.id.camera_preview);
            myBtn01=(Button)findViewById(R.id.button1);
            myBtn02=(Button)findViewById(R.id.button2);
            txtIP = (EditText)findViewById(R.id.editText1);
            //開始连接主机按钮
            myBtn01.setOnClickListener(new OnClickListener(){
    			public void onClick(View v) {
    				serverUrl = txtIP.getText().toString();
    				if(connectedServer){//停止连接主机,同一时候断开传输
    					startSendVideo=false;
    					connectedServer=false;					
    					myBtn02.setEnabled(false);
    					myBtn01.setText("開始连接");
    					myBtn02.setText("開始传输");
    					
    					//断开连接
    					//Thread th = new MySendCommondThread("PHONEDISCONNECT|"+pUsername+"|");
    			  	  	//th.start(); 
    					
    				}
    				else//连接主机
    				{
    					//启用线程发送命令PHONECONNECT
    			  	  	
    					connectedServer=true;
    					myBtn02.setEnabled(true);
    					myBtn01.setText("停止连接");
    				}
    			}});
            
            myBtn02.setEnabled(false);
            myBtn02.setOnClickListener(new OnClickListener(){
    			public void onClick(View v) {
    				if(startSendVideo)//停止传输视频
    				{
    					startSendVideo=false;
    					myBtn02.setText("開始传输");
    				}
    				else{ // 開始传输视频
    					Thread th = new MyThread();
    			  	  	th.start(); 
    					startSendVideo=true;
    					myBtn02.setText("停止传输");
    				}
    			}});
        }
        
    
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
    
        @Override
    	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    		// TODO Auto-generated method stub
    		if (mCamera == null) {
                return;
            }
            mCamera.stopPreview();
            mCamera.setPreviewCallback(this);
            mCamera.setDisplayOrientation(90); //设置横行录制
            
            //获取摄像头參数
            Camera.Parameters parameters = mCamera.getParameters();
            
            Size size = parameters.getPreviewSize();
            VideoWidth=size.width;
            VideoHeight=size.height;
    
            VideoFormatIndex=parameters.getPreviewFormat();
            
            mCamera.startPreview();
    	}
    
    	@Override
    	public void surfaceCreated(SurfaceHolder holder) {
    		// TODO Auto-generated method stub
    		try {
                if (mCamera != null) {
                    mCamera.setPreviewDisplay(mSurfaceHolder);
                    mCamera.startPreview();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } 
    	}
    
    	@Override
    	public void surfaceDestroyed(SurfaceHolder holder) {
    		// TODO Auto-generated method stub
    		if (null != mCamera) {
                mCamera.setPreviewCallback(null); // !。这个必须在前,不然退出出错
                mCamera.stopPreview();
                mCamera = null;
            }
    	}
    
    	@Override
    	public void onPreviewFrame(byte[] data, Camera camera) {
    		// TODO Auto-generated method stub
    		//假设没有指令传输视频,就先不传
    		if(!startSendVideo)
    			return;
    //		if(tempPreRate<VideoPreRate){
    //			tempPreRate++;
    //			return;
    //		}
    //		tempPreRate=0;		
    		try {
    		      if(data!=null)
    		      {
    		        YuvImage image = new YuvImage(data,VideoFormatIndex, VideoWidth, VideoHeight,null);
    		        if(image!=null)
    		        {
    		        	ByteArrayOutputStream outstream = new ByteArrayOutputStream();
    		        	
    		      	  	//在此设置图片的尺寸和质量 
    		      	  	image.compressToJpeg(new Rect(0, 0, (int)(VideoWidthRatio*VideoWidth), 
    		      	  		(int)(VideoHeightRatio*VideoHeight)), VideoQuality, outstream);  
    		      	  	outstream.flush();
    		      	  	
    		      	   SendMessage(outstream.toByteArray());
    		      	
    		      	  	//启用线程将图像数据发送出去
    		      	  	//Thread th = new MySendFileThread(outstream.toByteArray());
    		      	  	//th.start();  
    		        }
    		      }
    		  } catch (IOException e) {
    		      e.printStackTrace();
    		  }
    	}
    	
    	Socket client = null;
       
    	public void SendMessage(byte[] content)
    	{
    		if(client==null)
    			return;
    		OutputStream outsocket;
    		try{
    		outsocket = client.getOutputStream();
    		
    		//BufferedOutputStream dos = new BufferedOutputStream(outsocket);
    		DataOutputStream dos = new DataOutputStream(outsocket);
    		int len = content.length;
    		byte[] header = Int2Byte(len);
    		//outsocket.write(header);
    		
    		dos.write(header);
    		//outsocket.flush();
    		//outsocket.write(content);
    //		byte byteBuffer[] = new byte[1024];
    //		ByteArrayInputStream inputstream = new ByteArrayInputStream(content);
    //		
    //		PrintWriter pw = new PrintWriter(outsocket);
    //		
    //        int amount;
    //        while ((amount = inputstream.read(byteBuffer)) != -1) {
    //        	outsocket.write(byteBuffer, 0, amount);
    //        }
    		//outsocket.write(content);
    		dos.write(content);
    		//dos.flush();
    		//dos.close();
    		//outsocket.flush();
    		
    		}catch(Exception e)
    		{
    			e.printStackTrace();
    		}
    	}
    	
    	public byte[] Int2Byte(int len)
    	{
    		byte[] rtn = new byte[5];
    		rtn[0] = (byte)0;
    		rtn[1] = (byte)(len&0xff);
    		rtn[2] = (byte)((len>>8)&0xff);
    		rtn[3] = (byte)((len>>16)&0xff);
    		rtn[4] = (byte)(len>>>24);
    		return rtn;
    		
    	}
    	
    	/**发送命令线程*/
        class MyThread extends Thread{
        	
        	public void run(){
        		//实例化Socket  
                try {
                	client=new Socket(serverUrl,serverPort);
        			
        		} catch (UnknownHostException e) {
        		} catch (IOException e) {
        		}  
        	}
        }
        
        /**发送文件线程*/
        class MySendFileThread extends Thread{	
        	byte[] content = null;
        	public MySendFileThread(byte[] content){
        		this.content = content;
        	}
        	
            public void run() {
                try{
                	SendMessage(this.content);
                    //tempSocket.close();                   
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    </pre><p></p><p>安卓端代码通过一个全局Socket创建连接,然后通过onPreviewFrame事件,捕获图像帧。然后压缩,处理成byte[]然后发送啦。</p><p>只是java这里没有int和byte[]的转换,非常二有木有,歧视一下java。

    还得自己写代码转换,这里直接生成了header[],因为没设计其它操作,所以第一位默认0,</p><p>后4位是图片byte[]长度。

    </p><p>然后依次发送数据出去。</p><p>其它的相机设置的代码,来自baidu。

    </p><p>话说java这里不熟,所以比較乱。没有详细封装什么的。

    </p>C#代码<p>ClientInfo。一个用来保存连接的实体类,嘛~因为Demo吗,没细致处理,一下也是同样原因,没有详细优化过,只是測试过wifi条件下,传600p左右画质,开2~3个client还是能够的。

    </p><p></p><pre code_snippet_id="419870" snippet_file_name="blog_20140707_3_9307192" name="code" class="csharp">using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Net.Sockets; namespace com.xwg.net { public class ClientInfo { public Thread ReceiveThread; public Socket Client; public string ip; public string name; } }


    serverSocket管理类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Net.Sockets;
    using System.Net;
    using xwg.common;
    using System.IO;
    
    namespace com.xwg.net
    {
        public class SocketServer
        {
            #region 变量定义
            List<ClientInfo> clientList = null;
    
            private Socket socketServer = null;
    
            /// <summary>
            /// 服务器IP
            /// </summary>
            private IPAddress serverIP;
    
            /// <summary>
            /// 监听端口号
            /// </summary>
            private int portNo = 15693;
    
    
            /// <summary>
            /// 完整终端地址包括端口
            /// </summary>
            private IPEndPoint serverFullAddr;
    
            // Server监听线程
            Thread accpetThread = null;
    
            #endregion
    
            #region 构造函数
            public SocketServer(string ServerIP)
            {
                this.serverIP = IPAddress.Parse(ServerIP);
            }
    
            public SocketServer(string ServerIP, int portNo)
            {
                this.serverIP = IPAddress.Parse(ServerIP);
                this.portNo = portNo;
            }
            #endregion
    
            #region Event
    
            // 客户端接入事件
            public event ClientAccepted OnClientAccepted;
    
            // 连接接收数据事件
            public event StreamReceived OnStreamReceived;
    
            public event ClientBreak OnClentBreak;
    
            #endregion
    
            public void StartListen()
            {
                //取得完整地址
                serverFullAddr = new IPEndPoint(serverIP, portNo);//取端口号
                try
                {
                    // 实例化Server对象
                    socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                    // 绑定监听端口
                    socketServer.Bind(serverFullAddr);
    
                    // 启动监听。制定最大20挂起
                    socketServer.Listen(20);
    
                }
                catch (Exception e)
                {
                    Logger.Write("StartListen方法:"+e.Message);
                }
    
                clientList = new List<ClientInfo>();
    
                accpetThread = new Thread(new ThreadStart(AcceptSocket));
                accpetThread.IsBackground = true;
                accpetThread.Start();
            }
    
            private void AcceptSocket()
            {
                while (true)
                {
                    // client Socket 获得客户端连接
                    Socket acceptSock = socketServer.Accept();
                    
                    Logger.Write("接收到连接!");
                    string ip = ((IPEndPoint)acceptSock.RemoteEndPoint).Address.ToString();
                    Logger.Write("客户端IP:");
                    ClientInfo info = new ClientInfo();
                    info.Client = acceptSock;
                    info.ip = ip;
                    info.name = ip;
    
                    Thread recThread = new Thread(new ParameterizedThreadStart(ReceiveMsg));
                    recThread.IsBackground = true;
                    info.ReceiveThread = recThread;
    
                    clientList.Add(info);
                    
                    recThread.Start(info);
    
                    // 客户端接入响应事件
                    if (OnClientAccepted != null)
                        OnClientAccepted(this, info);
                }
            }
    
            private void ReceiveMsg(object obj)
            {
                ClientInfo info = (ClientInfo)obj;
                Socket clientSock = info.Client;
                try
                {
                    while (true)
                    {
                        // 推断连接状态
                        if (!clientSock.Connected)
                        {
                            clientList.Remove(info);
                            info.ReceiveThread.Abort();
                            clientSock.Close();
    
                        }
    
                        try
                        {
                            byte[] header = new byte[5];
                            int len = clientSock.Receive(header,SocketFlags.None);
                            if (len != 5)
                            {
                                // 错误 终端连接
                                Logger.Write("数据头接收错误,长度不足:" + len);
                                clientSock.Close();
                                info.ReceiveThread.Abort();
                                return;
                            }
    
                            int conLen = BitConverter.ToInt32(header, 1);
    
                            //byte[] content = new byte[conLen];
                            //len = clientSock.Receive(content, SocketFlags.None);
                            MemoryStream stream = new MemoryStream();
                            //byte [] buffer = new byte[1024];
                            //while ((len = clientSock.Receive(buffer)) > 0)
                            //{
                            //    stream.Write(buffer,0,len);
                            //}
                            //if (conLen != stream.Length)
                            //{
                            //    Logger.Write("长度错误:"+stream.Length+"/"+conLen);
                            //}
                            for (int i = 0; i < conLen; i++)
                            {
                                byte[] arr = new byte[1];
                                clientSock.Receive(arr,SocketFlags.None);
                                stream.Write(arr,0,1);
                            }
    
                            //stream.Write(content,0,content.Length);
                            stream.Flush();
                            
                            //len = clientSock.Receive(content, SocketFlags.None);
    
                            //if (len != conLen)
                            //{
                            //    // 错误 终端连接
                            //    Logger.Write("header:" + header[1] + "," + header[2] + "," + header[3] + "," + header[4]);
                            //    Logger.Write("Content接收错误。长度不足:" + len+"/"+conLen);
                            //    clientSock.Close();
                            //    return;
                            //}
    
                            // 接收事件
                            if (OnStreamReceived != null)
                            {
                                OnStreamReceived(info, stream);
                            }
                        }
                        catch (Exception ex)
                        {
                            if (OnClentBreak != null)
                            {
                                OnClentBreak(this, info);
                            }
                            Logger.Write("Receive数据:"+ex.Message);
                            clientSock.Close();
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    if (OnClentBreak != null)
                    {
                        OnClentBreak(this, info);
                    }
                    Logger.Write("ReceiveMsg:" + e.Message);
                    clientSock.Close();
                    return;
                }
            }
        }
        
        public delegate void ClientAccepted(object sender,ClientInfo info);
    
        public delegate void StreamReceived(object sender,MemoryStream stream);
    
        public delegate void ClientBreak(object sender,ClientInfo info);
    }
    

    这里面涉及了Socket和多线程处理的知识。

    简单一说,Socket既能够做Server,也能够做Client,当然你用TCPListener也一样效果就是了。

    这里因为是服务端,所以Socket被我Bind到了一个port上面,启用了Listen,监听方法。

    然后启用了一个accpet线程。总用时实现port监听。

    每当accpet到一个client的时候,会触发 OnClientAccepted 事件。accpet方法是会触发堵塞的,所以绝对不能够用主线程。否则就是程序无响应。

    C#处理过程中,实现封装的最好方法就是使用事件机制了,这是我觉得比Java方便的多的设计。能够把逻辑的实现。全然的抛出封装对象。

    然后就是AcceptSocket 这种方法了,这种方法其中,一旦接收到client连接,会创建一个ClientInfo对象。把一些相关属性设置上去保存。

    同一时候新建一个线程,实现ReceiveMessage的监听。IsBackground是一个小技巧,表示主线程结束时,把这些线程同一时候结束掉。

    比起手动结束方便多。

    假设你应用关闭,发现程序还在后台跑,那么多数是因为创建的线程没有结束的原因。这时候这个属性会起到关键作用。

    ReceiveMsg是接收数据的方法。这里简单定义了一个数据协议,数据发送时,分成两部分发送,先发送一个5byte的header。然后是实际内容content。

    header第一位表示操作标示(由于demo简单,所以没有详细设计协议,这个能够自己定义的,也是通用的处理方法)。后4位标示content内容流的长度,这样取数据的时候,就不至于乱掉。

    正常来说,Socket.Receive是有堵塞的啦,只是这里不知道为什么,接收的时候有问题,怀疑安卓socket流导致的。所以没办法直接定义buffer。一次性接受全部content。因为时间紧。没细致研究。反正总长度是一定不会变的,所以直接循环处理了...偷懒了有木有...

    接受到信息后,通过事件OnStreamReceived 吧数据流返回出去。

    服务端基本就这样了。由于不牵扯双向消息。所以没处理send啦。

    然后就是界面:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using com.xwg.net;
    using System.IO;
    using xwg.common;
    
    
    namespace MonitorServer
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            SocketServer server = null;
            List<ViewForm> list = new List<ViewForm>();
            //ViewForm vf = new ViewForm();
    
            private void button1_Click(object sender, EventArgs e)
            {
                
                server = new SocketServer(txtIP.Text, int.Parse(txtPort.Text));
                Logger.Write("server = new SocketServer(txtIP.Text, int.Parse(txtPort.Text));");
                server.OnClientAccepted += ClientAccepted;
                server.OnStreamReceived += StreamReceived;
                server.OnClentBreak += ClientBreak;
                server.StartListen();
                Logger.Write("StartListen");
                button1.Enabled = false;
            }
    
            public void AddClient(string ip)
            {
                try
                {
                    this.Invoke((EventHandler)delegate
                    {
                        listBox1.Items.Add(ip);
                    });
                }
                catch (Exception e)
                {
    
                }
            }
    
            public void RemoveClient(string ip)
            {
                try
                {
                    this.Invoke((EventHandler)delegate
                    {
                        listBox1.Items.Remove(ip);
                    });
                }
                catch (Exception e)
                {
    
                }
            }
    
            public void CloseViewForm(string ip)
            {
                try
                {
                    ViewForm vf = GetViewByIP(ip);
                    if (vf == null)
                        return;
                    vf.Invoke((EventHandler)delegate
                    {
                        vf.Close();
                    });
                }
                catch (Exception e)
                {
    
                }
            }
    
            public void SetViewImage(ViewForm vf, MemoryStream stream)
            {
                try
                {
                    vf.Invoke((EventHandler)delegate
                    {
                        Image img = Image.FromStream(stream);
                        vf.SetImage(img);
                        stream.Close();
                    });
                }
                catch (Exception e)
                {
    
                }
            }
    
            protected void ClientAccepted(object sender, ClientInfo info)
            {
                Logger.Write("ClientAccepted:"+info.ip);
                AddClient(info.ip);
                
                //ViewForm vf = new ViewForm();
                //list.Add(vf);
                //vf.SetTitle(info.ip);
                //vf.Show();
            }
    
            private ViewForm GetViewByIP(string ip)
            {
                foreach (ViewForm vf in list)
                {
                    if (vf.Text == ip)
                        return vf;
                }
                return null;
            }
    
            protected void StreamReceived(object sender, MemoryStream stream)
            {
                ClientInfo info = (ClientInfo) sender;
                try
                {
                    //Image img = Image.FromStream(stream);
                    ////img.Save("a.jpg"); 
                    ViewForm vf = GetViewByIP(info.ip);
                    if (vf == null)
                    {
                        stream.Close();
                        return;
                    }
                    SetViewImage(vf, stream);
                    
                    //vf.SetImage(img);
                    //stream.Close();
                }
                catch (Exception e)
                {
                    Logger.Write("StreamReceived:"+e.Message);
                }
            }
    
            protected void ClientBreak(object sender, ClientInfo info)
            {
                CloseViewForm(info.ip);
                list.Remove(GetViewByIP(info.ip));
                RemoveClient(info.ip);
            }
    
            private void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)
            {
                if (listBox1.SelectedItem == null)
                    return;
                string ip = listBox1.SelectedItem.ToString();
                ViewForm vf = new ViewForm();
                list.Add(vf);
                vf.SetTitle(ip);
                vf.Show();
            }
        }
    }
    

    界面实现了那几个事件,通过事件机制得到数据,进行UI设置。(界面嘛~就是UI层啦。一般仅仅关注UI,不要放逻辑代码)

    然后用来展示的窗口

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace MonitorServer
    {
        public partial class ViewForm : Form
        {
            public ViewForm()
            {
                InitializeComponent();
            }
    
            public void SetTitle(string title)
            {
                this.Text = title;
            }
    
            public void SetImage(Image img)
            {
                pictureBox1.Image = img;
            }
        }
    }
    

    事实上就是绑定了一个pictureBox啦。

    到此就结束了,启动server应用。開始监听。然后启动安卓端应用,输入server地址(要保证一个网络中),连接。

    服务端就会发现client连接,这时。双击ip,就会打开预览窗体。

    支持多client连接预览,可是打开多了会卡,毕竟演示demo。没处理优化。

    实际上,应该是连接后,server发送命令给client,client才開始传图片流,如今没处理。所以比較卡哦。

    而且这样的图片帧传输的方法。尽管比較清晰。可是压缩比小。会产生大量流量。所以仅仅能演示用哦。

    对了,还一个原因,这里用的tcp协议,可以保证数据包丢失重发。可是该机制会导致性能瓶颈,重发就会有时间影响哦,网络不好。easy出现抖动等现象,事实上是因为丢包引起的。这里可以换用udp,尽管可能会出现丢帧,可是抖动现象应该会有改善,速度也会比較快。

    而且这里不支持音频哦,假设想要完美实现的话。还是用上一篇文章提到的方法吧。

    做一个控制server,然后实现C#client和androidclient的直连,效果应该比較好。当然,这里也是因为spyroid这个项目。内置实现了rtsp协议server的原因啦。

    站在巨人身上总是会让事情变得简单。这里感谢国外开源项目组,同一时候歧视一下国内人员,百度到实用的东西,都不放出源代码,而是要收费。。。

    希望对大家实用。

    对了,源代码我上传到资源里面了,大家能够去我空间下载,包括安卓和C#后台完整项目代码。

    地址:http://download.csdn.net/detail/lanwilliam/7602669

    10资源子其实真的不高,调试后,1天离开。

    版权声明:欢迎转载,但请保留源解释

  • 相关阅读:
    OpenCV 轮廓填充drawContours函数解析
    C++ MultiByteToWideChar和WideCharToMultiByte用法详解
    删除后缀dll文件,无需下载软件
    MYSQL误删除DELETE数据找回
    神经网络减少GPU显存占用的技术
    keras tensorflow 获取某层输出和层内参数的值
    聚光灯矩阵
    tf.linalg.band_part 和 tf.matrix_band_part
    机器学习,BootStrap
    极大团算法思路小记
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4827012.html
Copyright © 2020-2023  润新知