国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android和C#基于UDP的實(shí)時(shí)圖像傳輸

2019-11-09 17:50:46
字體:
供稿:網(wǎng)友

實(shí)時(shí)圖像傳輸?shù)脑掃€是用UDP比較好,速度比TCP快,反正丟一些幀也沒有關(guān)系

照例先上圖

電腦端

手機(jī)端

項(xiàng)目:http://pan.baidu.com/s/1pLrYrij

Android端

首先修改AndroidManifest.xml文件

添加這兩個(gè)權(quán)限

<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.CAMERA"/>

然后把MainActiviy設(shè)置為橫屏

android:screenOrientation="landscape"

然后修改activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <SurfaceView        android:id="@+id/surfaceView"        android:layout_width="match_parent"        android:layout_height="match_parent" /></RelativeLayout>

很簡單,只是一個(gè)SurfaceView

最后就是MainActivity.java

package com.ffpy.cameratransmitclient;import android.graphics.ImageFormat;import android.graphics.Rect;import android.graphics.YuvImage;import android.hardware.Camera;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.Socket;import java.util.LinkedList;public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PReviewCallback {    private final int TCP_PORT = 3333;                  //TCP通訊的端口號    private final int UDP_PORT = 4444;                  //UDP通訊的端口號    private final String SERVER_ip = "192.168.1.104";   //服務(wù)器端的IP地址    private Camera mCamera;    private Camera.Size previewSize;            //預(yù)覽圖像的寬高    private DatagramSocket packetSenderSocket;  //發(fā)送圖像幀的套接字    private long lastSendTime;                  //上一次發(fā)送圖像幀的時(shí)間    private InetAddress serverAddress;          //服務(wù)端地址    private final LinkedList<DatagramPacket> packetList = new LinkedList<>();   //圖像數(shù)據(jù)包隊(duì)列    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);        SurfaceHolder holder = surfaceView.getHolder();        holder.setKeepScreenOn(true);  //保持屏幕常亮        holder.addCallback(this);        //開啟通訊連接線程,連接服務(wù)端        new ConnectThread().start();    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        //獲取相機(jī)        if (mCamera == null) {            mCamera = Camera.open();    //打開后攝像頭            Camera.Parameters parameters = mCamera.getParameters();            //設(shè)置預(yù)覽圖大小            //注意必須為parameters.getSupportedPreviewSizes()中的長寬,否則會報(bào)異常            parameters.setPreviewSize(960, 544);            previewSize = parameters.getPreviewSize();            //設(shè)置自動對焦            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);            mCamera.setParameters(parameters);            mCamera.cancelAutoFocus();            //設(shè)置回調(diào)            try {                mCamera.setPreviewDisplay(holder);                mCamera.setPreviewCallback(this);            } catch (IOException e) {                e.printStackTrace();            }        }        //開始預(yù)覽        mCamera.startPreview();    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        //釋放相機(jī)        if (mCamera != null) {            mCamera.setPreviewCallback(null);            mCamera.stopPreview();            mCamera.release();            mCamera = null;        }    }    /**     * 獲取每一幀的圖像數(shù)據(jù)     */    @Override    public void onPreviewFrame(byte[] data, Camera camera) {        long curTime = System.currentTimeMillis();        //每20毫秒發(fā)送一幀        if (serverAddress != null && curTime - lastSendTime >= 20) {            lastSendTime = curTime;            //NV21格式轉(zhuǎn)JPEG格式            YuvImage image = new YuvImage(data, ImageFormat.NV21, previewSize.width ,previewSize.height, null);            ByteArrayOutputStream bos = new ByteArrayOutputStream();            image.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 40, bos);            int packMaxSize = 65500;    //防止超過UDP包的最大大小            byte[] imgBytes = bos.toByteArray();            Log.i("tag", imgBytes.length + "");            //打包            DatagramPacket packet = new DatagramPacket(imgBytes, imgBytes.length > packMaxSize ? packMaxSize : imgBytes.length,                    serverAddress, UDP_PORT);            //添加到隊(duì)尾            synchronized (packetList) {                packetList.addLast(packet);            }        }    }    /**     * 連接線程     */    private class ConnectThread extends Thread {        @Override        public void run() {            super.run();            try {                //創(chuàng)建連接                packetSenderSocket = new DatagramSocket();                Socket socket = new Socket(SERVER_IP, TCP_PORT);                serverAddress = socket.getInetAddress();                //斷開連接                socket.close();                //啟動發(fā)送圖像數(shù)據(jù)包的線程                new ImgSendThread().start();            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 發(fā)送圖像數(shù)據(jù)包的線程     */    private class ImgSendThread extends Thread {        @Override        public void run() {            super.run();            while (packetSenderSocket != null) {                DatagramPacket packet;                synchronized (packetList) {                    //沒有待發(fā)送的包                    if (packetList.isEmpty()) {                        try {                            Thread.sleep(10);                            continue;                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    //取出隊(duì)頭                    packet = packetList.getFirst();                    packetList.removeFirst();                }                try {                    packetSenderSocket.send(packet);                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}surfaceCreated()、surfaceChanged()、surfaceDestroyed()這三個(gè)方法是對應(yīng)SurfaceView的,做過Android自定義相機(jī)的都知道的。需要注意的是,在surfaceCreated()中我設(shè)置了預(yù)覽圖像的寬高

parameters.setPreviewSize(960, 544);這個(gè)值我根據(jù)我手機(jī)屏幕的大小設(shè)置的,因?yàn)轭A(yù)覽圖會在SurfaceView上拉伸成SurfaceView的寬高,所以設(shè)置成跟SurfaceView的寬高比差不多,這樣看上去圖像不會變形。而且它不能隨便設(shè)置,設(shè)置得不對的話會報(bào)異常,可以通過parameters.getSupportedPreviewSizes()來查看手機(jī)支持的寬高列表,不同的手機(jī)所支持的不一定相同,所以如果在這里報(bào)錯(cuò)的話可以修改成你手機(jī)所支持的值。

onPreviewFrame()就是獲取每一幀圖像的地方了,在這里獲取攝像頭的圖像幀,并轉(zhuǎn)為JPEG格式。還有因?yàn)閁DP每個(gè)包的大小不能超過64K,減去一下包頭那些所占用的,大概還有65500個(gè)字節(jié)。因?yàn)槲疫@里是每一個(gè)包封裝一幀圖像,所以要注意壓縮圖像,不要超過65500個(gè)字節(jié),否則在電腦端就看不到超出的那部分圖像了。

C#

窗體很簡單,就是一個(gè)PictureBox放在Form上,把PictrureBox的Name設(shè)置為pictureBox,并讓它充滿Form。設(shè)置Form的大小為978, 591,這樣pictureBox的大小就是960, 544了,跟預(yù)覽圖的大小一致

Form1.cs

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Diagnostics;using System.Drawing;using System.IO;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Windows.Forms;namespace CameraTransmitServer{    public partial class Form1 : Form    {        private const int TCP_PORT = 3333;  //TCP通訊的端口號        private const int UDP_PORT = 4444;  //UDP通訊的端口號        private UdpClient client;        private IPEndPoint remote;        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            //創(chuàng)建等待連接線程            Thread thread = new Thread(new ThreadStart(waitConnect));            thread.IsBackground = true;            thread.Start();        }        //等待連接        private void waitConnect()        {            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(new IPEndPoint(IPAddress.Any, TCP_PORT));            serverSocket.Listen(10);            Debug.WriteLine("監(jiān)聽中");            Socket clientSocket = serverSocket.Accept();            Debug.WriteLine("連接成功");            remote = (IPEndPoint) clientSocket.RemoteEndPoint;            client = new UdpClient(new IPEndPoint(IPAddress.Any, UDP_PORT));                         //關(guān)閉套接字            serverSocket.Close();            clientSocket.Close();            //啟動接收線程            Thread thread = new Thread(new ThreadStart(recvImage));            thread.IsBackground = true;            thread.Start();        }        //接收圖像幀,并顯示到PictureBox上        private void recvImage()        {            while(true)             {                //接受圖像幀數(shù)據(jù)                byte[] recvBuf = client.Receive(ref remote);                MemoryStream ms = new MemoryStream(recvBuf);                try                {                    //顯示到pictureBox上                    pictureBox.Image = Image.FromStream(ms);                }                catch (ArgumentException)                { }            }        }    }}

這里就是等待手機(jī)端連接然后不斷地接收圖像數(shù)據(jù)并顯示在PictureBox上

你可能會問,不是UDP嗎,怎么這里還用了TCP?

我這里用TCP是為了保證連接的建立,確認(rèn)連接成功后再通過UDP發(fā)送圖像數(shù)據(jù),否則的話服務(wù)端還沒上線手機(jī)就開始不斷地發(fā)送圖像數(shù)據(jù)了。而且如果需要的話還可以用TCP的可靠連接來發(fā)送除了圖像以外的其它數(shù)據(jù),比如文字什么的。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 景德镇市| 平阴县| 嵊泗县| 龙胜| 武平县| 辽源市| 扬州市| 刚察县| 西和县| 乐都县| 凤庆县| 商南县| 志丹县| 清镇市| 宁南县| 莲花县| 彭州市| 文水县| 湘潭县| 五常市| 琼海市| 抚松县| 荥经县| 裕民县| 四子王旗| 平南县| 武川县| 英吉沙县| 贡山| 新晃| 东城区| 丹寨县| 东台市| 建昌县| 阜康市| 佛教| 天峨县| 炎陵县| 岳池县| 通河县| 沛县|