开发要求:将Android摄像头拍摄的视频传输到Ubuntu系统上显示。
发送的数据结构为:图像起标志+数据长度+图像数据,其中一帧图像的起始标志为303030303。
为加快传输速率,视频图像通过Opencv压缩。
Ubuntu设置为服务器,ip地址为:192.168.3.18。
Android手机为客户端,ip地址为:192.168.3.11。
一、服务器端(Ubuntu)
服务器端通过Python语言编写,代码如下:
import socket
import cv2
import numpy as np
def rotate_bound(image, angle):
# grab the dimensions of the image and then determine the
# center
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# grab the rotation matrix (applying the negative of the
# angle to rotate clockwise), then grab the sine and cosine
# (i.e., the rotation components of the matrix)
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the new bounding dimensions of the image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
return cv2.warpAffine(image, M, (nW, nH))
# 接受图片大小的信息
def recv_size(m_sock, m_count):
buf = bytes()
while m_count:
newbuf = m_sock.recv(m_count)
if not newbuf: return None
buf += newbuf
m_count -= len(newbuf)
return buf
HOST_IP = "192.168.3.18" # 主机作为AP热点的ip地址
HOST_PORT = 7654 # 端口号
print("Starting socket: TCP...")
socket_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建socket
print("TCP server listen @ %s:%d!" % (HOST_IP, HOST_PORT))
host_addr = (HOST_IP, HOST_PORT)
socket_tcp.bind(host_addr) # 绑定主机的ip地址和端口号
socket_tcp.listen(1) # listen函数的参数是监听客户端的个数,这里只监听一个,即只允许与一个客户端创建连接
print('waiting for connection...')
socket_con, (client_ip, client_port) = socket_tcp.accept() # 接收客户端的请求
print("Connection accepted from %s." % client_ip)
# send_str = "this is string example....wow!!!"
# send_byte=send_str.encode()
# socket_con.send(send_byte) # 发送数据
while True:
data1 = recv_size(socket_con, 4) # 接收开始标志
start = int.from_bytes(data1, byteorder='little', signed=False)
print(start)
if start == 303030303: # 收到开始标志,开始接收图像
data2 = recv_size(socket_con, 4) # 接收要接收的数据长度
image_length = int.from_bytes(data2, byteorder='little', signed=False)
data3 = recv_size(socket_con, image_length)
buff = np.fromstring(data3, np.uint8)
img_decode = cv2.imdecode(buff, cv2.IMREAD_COLOR)
rotated = rotate_bound(img_decode,90)
cv2.imshow('image', rotated )
cv2.waitKey(33)
socket_tcp.close()
二、客户端(Android)
客户端用java语言编写,代码如下:
1、AndroidManifest.xml中添加权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
2、activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity">
<org.opencv.android.JavaCameraView
android:id="@+id/javaCameraView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:camera_id="back"
app:show_fps="true" />
</LinearLayout>
3、MainActivity.java
package com.example.mysocketvideo;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import static android.support.v4.content.PermissionChecker.PERMISSION_GRANTED;
import static org.opencv.core.CvType.CV_8UC3;
import static org.opencv.imgcodecs.Imgcodecs.IMWRITE_JPEG_QUALITY;
import static org.opencv.imgcodecs.Imgcodecs.imencode;
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {
private JavaCameraView javaCameraView;
private Handler handler = null;
Socket socket = null;
//static {
// System.loadLibrary("opencv_java3");
//}
public void requestPower() {
// checkSelfPermission 判断是否已经申请了此权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
//如果应用之前请求过此权限但用户拒绝了请求,shouldShowRequestPermissionRationale将返回 true。
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
} else {
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA,}, 1);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PERMISSION_GRANTED) {
Toast.makeText(this, "" + "权限" + permissions[i] + "申请成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "" + "权限" + permissions[i] + "申请失败", Toast.LENGTH_SHORT).show();
}
}
}
}
private BaseLoaderCallback baseLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status){
case LoaderCallbackInterface.SUCCESS:{
javaCameraView.enableView();
}
break;
default:
super.onManagerConnected(status);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
requestPower();
handler = new Handler();
//单开一个线程来进行socket通信
new Thread(new Runnable() {
@Override
public void run() {
try {
socket = new Socket("192.168.3.18" , 7654);
if (socket!=null) {
System.out.println("###################");
while (true) { //循环进行收发
//recv();
//send();
}
}
else
System.out.println("socket is null");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
javaCameraView = (JavaCameraView) findViewById(R.id.javaCameraView);
javaCameraView.setVisibility(SurfaceView.VISIBLE);
javaCameraView.setCvCameraViewListener(this);
}
private void recv() {
//单开一个线程循环接收来自服务器端的消息
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
if (inputStream!=null){
try {
byte[] buffer = new byte[1024];
int count = inputStream.read(buffer);//count是传输的字节数
String recv_buff = new String(buffer);//socket通信传输的是byte类型,需要转为String类型
} catch (IOException e) {
e.printStackTrace();
}
}
}
public byte[] intToByte(int val){
byte[] b = new byte[4];
b[0] = (byte)(val & 0xff);
b[1] = (byte)((val >> 8) & 0xff);
b[2] = (byte)((val >> 16) & 0xff);
b[3] = (byte)((val >> 24) & 0xff);
return b;
}
private void send(MatOfByte data_encode) {
final byte[] byteArray = data_encode.toArray();
new Thread(new Runnable() {
@Override
public void run() {
//向服务器端发送消息
System.out.println("------------------------");
OutputStream outputStream=null;
try {
outputStream = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
int startflag=303030303;
byte start[]=intToByte(startflag);
if(outputStream!=null) {
try {
outputStream.write(start);
System.out.println("1111111111111111111111");
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
int m_length=byteArray.length;
byte temp[]=intToByte(m_length);
if(outputStream!=null) {
try {
outputStream.write(temp);
System.out.println("1111111111111111111111");
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outputStream!=null) {
try {
outputStream.write(byteArray);
System.out.println("1111111111111111111111");
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
public void onPause(){
super.onPause();
if (javaCameraView != null)
javaCameraView.disableView();
}
@Override
public void onResume(){
super.onResume();
if (!OpenCVLoader.initDebug()){
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_3_0,this,baseLoaderCallback);
}else{
baseLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
@Override
public void onCameraViewStarted(int width,int height){
}
@Override
public void onCameraViewStopped(){
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame){
if (socket!=null) {
MatOfByte data_encode = new MatOfByte();
MatOfInt params90 = new MatOfInt(IMWRITE_JPEG_QUALITY, 60);
imencode(".jpg", inputFrame.rgba(), data_encode);
send(data_encode);
}
return inputFrame.rgba();
}
}
三、测试结果
1、Ubuntu端
2、Android端