第一篇博客献给java和c#
最近做比赛需要远程视频接入pc端和安卓端,于是就有了本文。
首先,我们使用一块嵌入式板搭载摄像头,嵌入式板我由于比赛用的是Intel的upboard,当然也可以使用树莓派。
第一步:我们在树莓派上安装好系统,然后在terminal中输入ifconfig得到树莓派的内网ip地址。
第二步:我选用python写一个获取摄像头图片及服务端的脚本。以下是代码(python代码是搬运的,哈哈):
读取摄像头的代码:
#-*- coding: cp936 -*-
import cv2
import time
import socket
from PIL import Image
import StringIO
# 获取电脑自带摄像头,若用usb摄像头则把0改为1
cap = cv2.VideoCapture(0)
# 调整采集图像大小为640*480
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 这里的HOST对应树莓派的IP地址(自己输入ifconfig查),端口号自己随便定一个即可,但注意后面的程序中要保持统一
HOST="192.168.1.105"
PORT=15023
# 连接服务器
sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST,PORT))
while True:
# 获取一帧图像
ret, img = cap.read()
# 如果ret为false,表示没有获取到图像,退出循环
if ret is False:
print("can not get this frame")
continue
# 将opencv下的图像转换为PIL支持的格式
pi = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
buf = StringIO.StringIO()# 缓存对象
pi.save(buf, format='JPEG')# 将PIL下的图像压缩成jpeg格式,存入buf中
jpeg = buf.getvalue()# 从buf中读出jpeg格式的图像
buf.close()
sock.sendall(jpeg )# 通过socket传到服务器
# time.sleep(0.2)
sock.close()
然后是服务器代码:
# -*- coding: cp936 -*-
import socket
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("192.168.1.105",15023))# 注意bind的这里,IP地址和端口号都要与前面的程序中一样
sock.listen(2)# 监听端口
# 等待数据源端连接
src, src_addr = sock.accept()
print "Source Connected by", src_addr
# 等待目标端连接
dst, dst_addr = sock.accept()
print "Destination Connected by", dst_addr
while True:
msg = src.recv(1024 *1024)
if not msg:
break
try:
dst.sendall(msg)
except Exception as ex:
dst, dst_addr = sock.accept()
print "Destination Connected Again by", dst_addr
except KeyboardInterrupt:
print "Interrupted"
break
src.close()
dst.close()
sock.close()
以上代码有一个问题尚未解决,就是无法完全关闭摄像头进程,需要在杀第一个代码进程后再开一次再杀。。。
第三步就是我们的pc端了:
pc端我采用的是wpf界面来显示视频画面,代码由于夹杂着其他功能就不贴了。只是说一下重点部分,主要是多线程上的问题。在wpf中,ui线程没有办法直接在非主线程中直接更新,需要用控件的Invoke方法,如:
this.picbox.Dispatcher.Invoke(new Action(() =>
{
BitmapFrame frame = BitmapFrame.Create(bmpimg);
this.picbox.Source = frame;
}));
其中注意BitmapFrame,只有这个玩意才能跨线程更新,而BitmapImage是不行的。
最后是安卓端:
package rosclient.pc.com.test;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import android.os.Handler;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
BufferedReader br = null;
OutputStream os = null;
Bitmap bmp=null;
private ImageView imageView = null;
private static final int COMPLETED = 0;
Socket socket = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
Thread t1=new Thread(socketRun);
t1.start();
}
private void showTip(final String tip) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, tip, Toast.LENGTH_SHORT).show();
}
});
}
Runnable socketRun=new Runnable() {
@Override
public void run() {
try {
// 创建socket
socket = new Socket("192.168.1.105", 15023);
socket.setReceiveBufferSize(1024*1024);
}catch (Exception e) {
showTip("Error:" + e);
}
while (true)
{
try{
InputStream in = socket.getInputStream();
bmp=BitmapFactory.decodeStream(in);
}catch (Exception e) {
showTip("Error:" + e);
}
Message msg=new Message();
msg.what=0;
msg.obj=bmp;
handle.sendMessage(msg);
}
}
};
private Handler handle = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Bitmap bmp = (Bitmap) msg.obj;
if (bmp != null) {
imageView = (ImageView) findViewById(R.id.imageView1);
imageView.setImageBitmap(bmp);
}
break;
}
};
};
public Bitmap getimg()
{
if(socket.isConnected())
{
try{
InputStream in = socket.getInputStream();
bmp=BitmapFactory.decodeStream(in);
}catch (Exception e) {
showTip("Error:" + e);
}
}
return bmp;
}
}
不过要记得在manifest里给网络权限。
其中我遇到的问题首先是无法在主线程中使用网络,所以必须开另一个线程去socket连接,但是控件又不允许在非主线程中更新,所以要使用Handler方法
private Handler handle = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Bitmap bmp = (Bitmap) msg.obj;
if (bmp != null) {
imageView = (ImageView) findViewById(R.id.imageView1);
imageView.setImageBitmap(bmp);
}
break;
}
};
};
以上就完成了,撒花!!!!