安卓android通过socket接收外部摄像头视频

描述:linux作为服务器,将摄像头的视频数据实时传输到安卓客户端。

主要思路:linux端通过opencv,连续获取摄像头图片,然后将图片压缩,通过socket传输给                         安卓客户端,安卓客户端接收数据后将图片用控件(ImgView)显示出来。连续的的图片                     组成了视频。

1,服务端

#!/usr/bin/env python
# -*- coding=utf-8 -*-

import socket
import numpy as np
import urllib
import cv2 as cv
import threading
import time
import sys

print('this is Server')

cap = cv.VideoCapture(0)
cap.set(cv.CAP_PROP_FRAME_WIDTH, 200)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, 200)

def socket_service():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 防止socket server重启后端口被占用(socket.error: [Errno 98] Address already in use)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        #s.bind(('127.0.0.1', 6666))
        s.bind(('192.168.16.107', 6666))
        s.listen(10)
    except socket.error as msg:
        print (msg)
        sys.exit(1)
    print ('Waiting connection...')

    while True:
        conn, addr = s.accept()
        t = threading.Thread(target=deal_data, args=(conn, addr))
        t.start()

def deal_data(conn, addr):
    print ('Accept new connection from {0}'.format(addr))
    while True:
        # get a frame
        ret, frame = cap.read()
        # 重整图片像素大小
        frame = cv.resize(frame, (180,180))# 200,200 延迟约4秒
        # '.jpg'表示把当前图片img按照jpg格式编码
        img_encode = cv.imencode('.jpg', frame)[1]
        data_encode = np.array(img_encode)
        str_encode = data_encode.tostring()
        encode_len = len(str_encode)
        print('img size : %d'%encode_len)

        #开始先发送8个1 
        conn.send("11111111")
        time.sleep(0.05)
        #分段发送图片数据, 发送到android, 低于1408才稳定
        for i in range(encode_len/1380+1):
            temp = str_encode[1380*i : 1380*(i+1)]
            print("temp len : %d"%len(temp))
            conn.send(temp)
            time.sleep(0.05)# 0.05最小
        print("send a frame ok")
        #发送8个2 表示结束
        conn.send("22222222")
        time.sleep(0.05)
    conn.close()

socket_service()
注:测试的时候发现安卓‘一下’只能最大接收1408个字节数据(linux与linxu的传输不会出现这种问题,不知道为什么),而我要传输的图片远远大于这一数值,所以采用分段传输的方法。安卓端分段接收。其中用‘11111111’和‘22222222’分别代表一张图片传输的开始和结束。其中的延时是我自己测试的情况,不同设备可能效果不一样。

2,客户端

//Socket连接的子线程,联网后不断接收数据
    public class thread_ConnectAndReceive extends Thread{
        public void run(){
            String serverIP =ET_ip.getText().toString();
            int serverPort = Integer.parseInt(ET_port.getText().toString());
            try{
                socket = new Socket();
                socket.connect(new InetSocketAddress(serverIP,serverPort),500);

                inputStream = socket.getInputStream();
                outputStream = socket.getOutputStream();
                socket.setReceiveBufferSize(1024*32);
                sendMsgToHandler('S');//连接成功

                //接收数据并处理
                while(true){
                    byte[] buffer = new byte[1380];//稳定接收最大值1408
                    try {
                        inputStringLen = inputStream.read(buffer);//接收数据,返回值为数据长度
                        //收到头 "11111111"
                        if (headFlag == 0 && buffer[0] == 49 && buffer[1] == 49 && buffer[2] == 49 && buffer[3] == 49 &&
                                buffer[4] == 49 && buffer[5] == 49 && buffer[6] == 49 && buffer[7] == 49) {

                            headFlag = 1;

                        //收到尾 "22222222"
                        }else if(buffer[0] == 50 && buffer[1] == 50 && buffer[2] == 50 && buffer[3] == 50 &&
                                buffer[4] == 50 && buffer[5] == 50 && buffer[6] == 50 && buffer[7] == 50){

                            sendMsgToHandler('B');//更新画面
                            headFlag = 0;

                        //收图片数据
                        }else if(headFlag == 1) {

                            //分段收图片数据
                            tmp = byteMerger(tmp, buffer);//这个tmp是关键,后面用到


                        }

                    }catch (IOException e) {
                        //receiveMes = "receive err";
                        //sendMsgToHandler('M');
                    }
                }
            }catch (IOException e){
                //在命令号打印异常信息在程序中出错的位置和原因
                //e.printStackTrace();
            }

        }
    }

注:安卓主要是 接收数据并处理(while循环)。接收到8个1,准备开始接收图片,接收到8个2,则

认为图片数据传输完毕。然后发消息给子线程,在子线程里更新控件(ImgView)的显示。由于完整代码太不简洁,展示一下部分代码


//安卓内接收消息并处理的子线程
@SuppressLint("handlerLeak")
    private Handler messageHandler = new Handler(){
        public void handleMessage(Message msg){
            switch(msg.what){
                case 'S':
                    Tips("连接中");
                    BT_connect.setText("连接中,点击断开");
                    break;

                case 'D':
                    Tips("未连接");
                    BT_connect.setText("连接服务器");
                    break;

                case 'I':
                    Tips("无IP");
                    break;

                case 'N':
                    Tips("无内容");
                    break;

                case 'B'://更新画面 用到前面接收的数据的tmp
                    bitmap = BitmapFactory.decodeByteArray(tmp, 0,tmp.length);
                    imgView.setImageBitmap(bitmap);//这句就能显示图片(bitmap数据没问题的情况下)
                    tmp = new byte[0];
                    break;

            }
        }
    };

效果图:

安卓界面是之前的,还没改,主要是测试用。。。

安卓android通过socket接收外部摄像头视频

 安卓android通过socket接收外部摄像头视频

 注:在(180,180)的像素下,视频的延迟大概是3秒,这个好像和控件的大小也有关系。前面测试的时候,更小的像素和更小的控件,延迟大约2秒。

吐槽:这里花了最多时间的是,bitmap,和接收数据。开始bitmap不知道怎么用,用了又不知道对不对。后来传输了一张20*20的图片数据,才在显示出来。再后来,调试了很久才发现‘1408’这个奇怪的数,然后又用分段传输的思路,再后来加入头和尾的判断。。。。

感谢前面查到的资料的分享者,例如byte[]合并、bitmap显示. . . . . .

如果有更高效的视频传输方法,感谢留言指教。。

上一篇:Kafka客户端频繁FullGC


下一篇:08. C Pro File Operation