基本过程是android作为socket客户端将采集到的每一帧图像数据发送出去,PC作为服务器接收并显示每一帧图像实现远程监控。图片如下(后来PC端加了个拍照功能)。。。
(PS。刚学android和java不久很多东西还不懂,高手若是知道哪些地方可以继续优化的话还请多多指点下啊)
系统代码如下:
一、android手机客户端
(1)AndroidManifest.xml文件。添加camera和socket权限,并设置了程序开始执行的activity、
<?xml
version= "1.0" encoding= "utf-8" ?>
<manifest
xmlns:android= "http://schemas.android.com/apk/res/android"
package = "org.wanghai.CameraTest"
android:versionCode= "1"
android:versionName= "1.0" >
<uses-sdk
android:minSdkVersion= "15" />
<!--
授予程序使用摄像头的权限 -->
<uses-permission
android:name= "android.permission.CAMERA" />
<uses-feature
android:name= "android.hardware.camera" />
<uses-feature
android:name= "android.hardware.camera.autofocus" />
<uses-permission
android:name= "android.permission.INTERNET" />
<uses-permission
android:name= "android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission
android:name= "android.permission.RESTART_PACKAGES" />
<application
android:icon= "@drawable/ic_launcher"
android:label= "@string/app_name" >
<activity
android:name= ".GetIP"
android:screenOrientation= "landscape"
android:label= "@string/app_name" >
<intent-filter>
<action
android:name= "android.intent.action.MAIN" />
<category
android:name= "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name= ".CameraTest"
android:screenOrientation= "landscape"
android:label= "@string/app_name" >
</activity>
</application>
</manifest>
|
(2)main.xml 设置surfaceview用于摄像头采集图像的预览
<?xml
version= "1.0" encoding= "utf-8" ?>
<LinearLayout
xmlns:android= "http://schemas.android.com/apk/res/android"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:orientation= "vertical" >
<SurfaceView
android:id= "@+id/sView"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:scaleType= "fitCenter" />
</LinearLayout>
|
(3)login.xml 登录界面,用于输入服务器IP
<?xml
version= "1.0" encoding= "utf-8" ?>
<TableLayout
xmlns:android= "http://schemas.android.com/apk/res/android"
android:id= "@+id/loginForm"
android:orientation= "vertical"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
>
<TableRow>
<TextView
android:layout_width= "fill_parent"
android:layout_height= "wrap_content"
android:text= "IP:"
android:textSize= "10pt"
/>
<!--
输入用户名的文本框 -->
<EditText
android:id= "@+id/ipedittext"
android:layout_width= "fill_parent"
android:layout_height= "wrap_content"
android:digits= "0123456789."
android:hint= "请填写服务器IP"
android:selectAllOnFocus= "true"
/>
</TableRow>
</TableLayout>
|
(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系统的手机上运行成功哦。
下面是测试成功时的启动画面:
源码链接: