基本过程是android作为socket客户端将采集到的每一帧图像数据发送出去,PC作为服务器接收并显示每一帧图像实现远程监控。图片如下(后来PC端加了个拍照功能)。。。
(PS。刚学android和java不久很多东西还不懂,高手若是知道哪些地方可以继续优化的话还请多多指点下啊)
系统代码如下:
一、android手机客户端
(1)AndroidManifest.xml文件。添加camera和socket权限,并设置了程序开始执行的activity、
package
=
"org.wanghai.CameraTest"
android:versionCode=
"1"
android:versionName=
"1.0"
>
android:icon=
"@drawable/ic_launcher"
android:label=
"@string/app_name"
>
android:name=
".GetIP"
android:screenOrientation=
"landscape"
android:label=
"@string/app_name"
>
android:name=
".CameraTest"
android:screenOrientation=
"landscape"
android:label=
"@string/app_name"
>
|
(2)main.xml 设置surfaceview用于摄像头采集图像的预览
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:orientation=
"vertical"
>
android:id=
"@+id/sView"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:scaleType=
"fitCenter"
/>
|
(3)login.xml 登录界面,用于输入服务器IP
android:id=
"@+id/loginForm"
android:orientation=
"vertical"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
>
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:text=
"IP:"
android:textSize=
"10pt"
/>
android:id=
"@+id/ipedittext"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:digits=
"0123456789."
android:hint=
"请填写服务器IP"
android:selectAllOnFocus=
"true"
/>
|
(4)GetIP.java 获得服务器IP后,通过Intent启动CameraTest的activity,ip信息通过Bundle传递
public
class
GetIP
extends
Activity {
String ipname =
null
;
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
// 设置全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
final
Builder builder =
new
AlertDialog.Builder(
this
);
//定义一个AlertDialog.Builder对象
builder.setTitle(
"登录服务器对话框"
);
// 设置对话框的标题
//装载/res/layout/login.xml界面布局
TableLayout loginForm = (TableLayout)getLayoutInflater().inflate( R.layout.login,
null
);
final
EditText iptext = (EditText)loginForm.findViewById(R.id.ipedittext);
builder.setView(loginForm);
// 设置对话框显示的View对象
// 为对话框设置一个“登录”按钮
builder.setPositiveButton(
"登录"
// 为按钮设置监听器
,
new
OnClickListener() {
@Override
public
void
onClick(DialogInterface dialog,
int
which) {
//此处可执行登录处理
ipname = iptext.getText().toString().trim();
Bundle data =
new
Bundle();
data.putString(
"ipname"
,ipname);
Intent intent =
new
Intent(GetIP.
this
,CameraTest.
class
);
intent.putExtras(data);
startActivity(intent);
}
});
// 为对话框设置一个“取消”按钮
builder.setNegativeButton(
"取消"
,
new
OnClickListener()
{
@Override
public
void
onClick(DialogInterface dialog,
int
which)
{
//取消登录,不做任何事情。
System.exit(
1
);
}
});
//创建、并显示对话框
builder.create().show();
}
}
|
(5)CameraTest.java 程序主体。设置PreviewCallback后,每当一帧图像数据采集完成后将调用PreviewCallback的onPreviewFrame函数。在这里我们将YUV格式数据转为jpg,再启用线程将数据通过socket发送出去。
public
class
CameraTest
extends
Activity {
SurfaceView sView;
SurfaceHolder surfaceHolder;
int
screenWidth, screenHeight;
Camera camera;
// 定义系统所用的照相机
boolean
isPreview =
false
;
//是否在浏览中
private
String ipname;
@SuppressWarnings
(
"deprecation"
)
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
// 设置全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
// 获取IP地址
Intent intent = getIntent();
Bundle data = intent.getExtras();
ipname = data.getString(
"ipname"
);
screenWidth =
640
;
screenHeight =
480
;
sView = (SurfaceView) findViewById(R.id.sView);
// 获取界面中SurfaceView组件
surfaceHolder = sView.getHolder();
// 获得SurfaceView的SurfaceHolder
// 为surfaceHolder添加一个回调监听器
surfaceHolder.addCallback(
new
Callback() {
@Override
public
void
surfaceChanged(SurfaceHolder holder,
int
format,
int
width,
int
height) {
}
@Override
public
void
surfaceCreated(SurfaceHolder holder) {
initCamera();
// 打开摄像头
}
@Override
public
void
surfaceDestroyed(SurfaceHolder holder) {
// 如果camera不为null ,释放摄像头
if
(camera !=
null
) {
if
(isPreview)
camera.stopPreview();
camera.release();
camera =
null
;
}
System.exit(
0
);
}
});
// 设置该SurfaceView自己不维护缓冲
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
private
void
initCamera() {
if
(!isPreview) {
camera = Camera.open();
}
if
(camera !=
null
&& !isPreview) {
try
{
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(screenWidth, screenHeight);
// 设置预览照片的大小
parameters.setPreviewFpsRange(
20
,
30
);
// 每秒显示20~30帧
parameters.setPictureFormat(ImageFormat.NV21);
// 设置图片格式
parameters.setPictureSize(screenWidth, screenHeight);
// 设置照片的大小
//camera.setParameters(parameters); // android2.3.3以后不需要此行代码
camera.setPreviewDisplay(surfaceHolder);
// 通过SurfaceView显示取景画面
camera.setPreviewCallback(
new
StreamIt(ipname));
// 设置回调的类
camera.startPreview();
// 开始预览
camera.autoFocus(
null
);
// 自动对焦
}
catch
(Exception e) {
e.printStackTrace();
}
isPreview =
true
;
}
}
}
class
StreamIt
implements
Camera.PreviewCallback {
private
String ipname;
public
StreamIt(String ipname){
this
.ipname = ipname;
}
@Override
public
void
onPreviewFrame(
byte
[] data, Camera camera) {
Size size = camera.getParameters().getPreviewSize();
try
{
//调用image.compressToJpeg()将YUV格式图像数据data转为jpg格式
YuvImage image =
new
YuvImage(data, ImageFormat.NV21, size.width, size.height,
null
);
if
(image!=
null
){
ByteArrayOutputStream outstream =
new
ByteArrayOutputStream();
image.compressToJpeg(
new
Rect(
0
,
0
, size.width, size.height),
80
, outstream);
outstream.flush();
//启用线程将图像数据发送出去
Thread th =
new
MyThread(outstream,ipname);
th.start();
}
}
catch
(Exception ex){
Log.e(
"Sys"
,
"Error:"
+ex.getMessage());
}
}
}
class
MyThread
extends
Thread{
private
byte
byteBuffer[] =
new
byte
[
1024
];
private
OutputStream outsocket;
private
ByteArrayOutputStream myoutputstream;
private
String ipname;
public
MyThread(ByteArrayOutputStream myoutputstream,String ipname){
this
.myoutputstream = myoutputstream;
this
.ipname = ipname;
try
{
myoutputstream.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
public
void
run() {
try
{
//将图像数据通过Socket发送出去
Socket tempSocket =
new
Socket(ipname,
6000
);
outsocket = tempSocket.getOutputStream();
ByteArrayInputStream inputstream =
new
ByteArrayInputStream(myoutputstream.toByteArray());
int
amount;
while
((amount = inputstream.read(byteBuffer)) != -
1
) {
outsocket.write(byteBuffer,
0
, amount);
}
myoutputstream.flush();
myoutputstream.close();
tempSocket.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
|
二、PC服务器端
ImageServer.java 用于显示图像,并且可以拍照
public
class
ImageServer {
public
static
ServerSocket ss =
null
;
public
static
void
main(String args[])
throws
IOException{
ss =
new
ServerSocket(
6000
);
final
ImageFrame frame =
new
ImageFrame(ss);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(
true
);
while
(
true
){
frame.panel.getimage();
frame.repaint();
}
}
}
/**
A frame with an image panel
*/
@SuppressWarnings
(
"serial"
)
class
ImageFrame
extends
JFrame{
public
ImagePanel panel;
public
JButton jb;
public
ImageFrame(ServerSocket ss){
// get screen dimensions
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int
screenHeight = screenSize.height;
int
screenWidth = screenSize.width;
// center frame in screen
setTitle(
"ImageTest"
);
setLocation((screenWidth - DEFAULT_WIDTH) /
2
, (screenHeight - DEFAULT_HEIGHT) /
2
);
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// add panel to frame
this
.getContentPane().setLayout(
null
);
panel =
new
ImagePanel(ss);
panel.setSize(
640
,
480
);
panel.setLocation(
0
,
0
);
add(panel);
jb =
new
JButton(
"拍照"
);
jb.setBounds(
0
,
480
,
640
,
50
);
add(jb);
saveimage saveaction =
new
saveimage(ss);
jb.addActionListener(saveaction);
}
public
static
final
int
DEFAULT_WIDTH =
640
;
public
static
final
int
DEFAULT_HEIGHT =
560
;
}
/**
A panel that displays a tiled image
*/
@SuppressWarnings
(
"serial"
)
class
ImagePanel
extends
JPanel {
private
ServerSocket ss;
private
Image image;
private
InputStream ins;
public
ImagePanel(ServerSocket ss) {
this
.ss = ss;
}
public
void
getimage()
throws
IOException{
Socket s =
this
.ss.accept();
System.out.println(
"连接成功!"
);
this
.ins = s.getInputStream();
this
.image = ImageIO.read(ins);
this
.ins.close();
}
public
void
paintComponent(Graphics g){
super
.paintComponent(g);
if
(image ==
null
)
return
;
g.drawImage(image,
0
,
0
,
null
);
}
}
class
saveimage
implements
ActionListener {
RandomAccessFile inFile =
null
;
byte
byteBuffer[] =
new
byte
[
1024
];
InputStream ins;
private
ServerSocket ss;
public
saveimage(ServerSocket ss){
this
.ss = ss;
}
public
void
actionPerformed(ActionEvent event){
try
{
Socket s = ss.accept();
ins = s.getInputStream();
// 文件选择器以当前的目录打开
JFileChooser jfc =
new
JFileChooser(
"."
);
jfc.showSaveDialog(
new
javax.swing.JFrame());
// 获取当前的选择文件引用
File savedFile = jfc.getSelectedFile();
// 已经选择了文件
if
(savedFile !=
null
) {
// 读取文件的数据,可以每次以快的方式读取数据
try
{
inFile =
new
RandomAccessFile(savedFile,
"rw"
);
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
}
int
amount;
while
((amount = ins.read(byteBuffer)) != -
1
) {
inFile.write(byteBuffer,
0
, amount);
}
inFile.close();
ins.close();
s.close();
javax.swing.JOptionPane.showMessageDialog(
new
javax.swing.JFrame(),
"已接保存成功"
,
"提示!"
, javax.swing.JOptionPane.PLAIN_MESSAGE);
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
|
开放源码如下(android我使用的是4.03的SDK,其它版本请自行更改。2.3.3版本以下的请注意initCamera()里被注释掉的哪一行)
只能在android4.04系统的手机上运行成功哦。
下面是测试成功时的启动画面:
源码链接:
http://files.cnblogs.com/feifei1010/%E5%9F%BA%E4%BA%8Eandroid%E6%89%8B%E6%9C%BA%E7%9A%84%E8%BF%9C%E7%A8%8B%E8%A7%86%E9%A2%91%E7%9B%91%E6%8E%A7%E7%B3%BB%E7%BB%9F.rar