把安卓手机改造成网络高清摄像头

千度号 资讯8阅读模式
如今的手机,得益于庞大的市场需求和激烈的市场竞争产生了先进的算法,价格相对低廉的手机摄像头拍摄出来的照片质量远远超过于昂贵的工业相机。所以,在某些特定的场合下,你对拍摄速度不讲究 但是对图像质量以及成本十分敏感的话,用手机去替代相机,未尝不是一个很好的思路。 1 编写手机端APP,该app 主要实现以下功能:和外部主机建立tcp连接,接收外部主机命令,打开摄像头,并将图片经过压缩后传输回主机。核心代码一个是网络传输处理的类TcpConnect,一个是控制摄像头的页面 cameraFramement。
class TcpConnect : Runnable {
    private var mServerSocket: ServerSocket? = null   //tcp主端  本地端
    private var mClient: Socket? = null   //tcp客户端  远程端
    private var callback: Handler.Callback? = null    //消息回调   实现了接口的页面对象
    private var out: BufferedOutputStream? = null  //输出流
    private val SendBufferSize = 16777216  //发送缓冲区 取16M 默认8K
    private val SERVER_PORT = 10086 //端口号

    //建立tcp主端
    init {
        try {
            mServerSocket = ServerSocket(SERVER_PORT)
            mServerSocket!!.reuseAddress = true
            println("建立Socket")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    //给客户端发送数据
    fun sendData(data: ByteArray?) {
        if (mClient == null || !mClient!!.isConnected) {
            return
        }
        try {
            out = BufferedOutputStream(mClient!!.getOutputStream())
            if (data != null && data.isNotEmpty()) {
                out!!.write(data)
                //  mClient.shutdownOutput();
            }
        } catch (e: IOException) {
            Log.e("output", "输入输出异常")
            e.printStackTrace()
        }
    }

    //把实现了callback接口的framement传进来
    @JvmName("setCallback1")
    fun setCallback(_callback: Handler.Callback?) {
        callback = _callback
    }

    //监听tcp端口 收到消息后 调用framement的handleMessage方法进行处理
    fun listen() {
        while (true) try {
            if (mServerSocket == null) {
                mServerSocket = ServerSocket(SERVER_PORT)
                mServerSocket!!.reuseAddress = true
            }
            mClient = mServerSocket!!.accept()
            mClient?.run {
                reuseAddress = true
                sendBufferSize = SendBufferSize
            }
            println("TcpConnect 在监听")
            val `in` = BufferedInputStream(mClient?.getInputStream())
            val buffer = ByteArray(2)
            //从文件中按字节读取内容,到文件尾部时read方法将返回-1
            while (`in`.read(buffer).also { bytesRead = it } != -1) {
                val msg = Message()
                msg.what = buffer[1].toInt()
                callback!!.handleMessage(msg)
            }
        } catch (e: IOException) {
            println("TcpConnect" + e.message)
        }
    }
class CameraFragment : Handler.Callback {
   //页面打开时初始化摄像头
  @SuppressLint("MissingPermission")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
     //启动tcp
      Thread {
                tcp=TcpConnect()
                tcp.setCallback(this@CameraFragment)
                tcp.listen()
            }.start()
      //初始化摄像头
        initializeCamera()
    }
  
    private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {
        // 打开摄像头
        camera = openCamera(cameraManager, args.cameraId, cameraHandler)
        // 设置拍摄画面的最大分辨率以及图像格式
        val size = characteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
                .getOutputSizes(args.pixelFormat).maxByOrNull { it.height * it.width }!!
        imageReader = ImageReader.newInstance(
                size.width, size.height, args.pixelFormat, IMAGE_BUFFER_SIZE)
      //这里比较关键,监听摄像头拍到的每帧图片 然后转成二进制流发送到客户端
        imageReader.setOnImageAvailableListener({
            val image = it.acquireNextImage()       
                val buffer = image.planes[0].buffer;
                val len = buffer.remaining()
                var outData = ByteArray(len)                        
                buffer.get(outData, 0, len)
                imageSendThread.run {
                    Runnable {
                        tcp.sendData(outData)
                        tcp.flush()
                        buffer.clear()
                    }.run()
                }          
            image.close();
        }, imageReaderHandler)
  
}
2 主机端应用程序。主要实现 发送控制命令(打开/关闭手机摄像头),接收图像并显示出来。我这里PC端是基于.net core的wpf。
public partial class MainWindow : Window
{
    Socket client;
    Thread listen;
    readonly BitmapImage bmp = new();
    byte[] imgData;

    public MainWindow()
    {
        InitializeComponent();
    }
  //与主端建立连接
    private void ConnectApp()
    {
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        client.SendTimeout = 5000;
        client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        client.ReceiveBufferSize = 4096 * 4096;
        IPAddress myIP = IPAddress.Parse("127.0.0.1");
        IPEndPoint EPhost = new IPEndPoint(myIP, 12580);
        client.Connect(EPhost);
        listen = new Thread(Received) { IsBackground = true };
        listen.Start();
    }
  
 //监听回调 收到数据后还原为图像 并显示
    void Received()
        {
            while (true)
            {
               // Thread.Sleep(1);
                if (client == null)
                {
                    continue;
                }
                if (!client.Connected || !listening)
                {
                    ShowData(3, "连接已断开,停止监听");
                    break;
                }

                try
                {
                    client.Receive(buffer);
                  .....
                }
            }
        }
文章源自千度号-https://www.taibaowl.com/26032.html
千度号
  • 本文由 发表于 2022年5月11日 22:42:00
  • 版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至395045033@qq.com举报,一经查实,本站将立刻删除。