基于Socket的Android手机视频实时传输.docx
- 文档编号:10699537
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:31
- 大小:413.38KB
基于Socket的Android手机视频实时传输.docx
《基于Socket的Android手机视频实时传输.docx》由会员分享,可在线阅读,更多相关《基于Socket的Android手机视频实时传输.docx(31页珍藏版)》请在冰豆网上搜索。
基于Socket的Android手机视频实时传输
基于Socket的Android手机视频实时传输
基于Socket的Android手机视频实时传输
肖泽云
首先,简单介绍一下原理。
主要是在手机客户端(Android)通过实现Camera.PreviewCallback接口,在其onPreviewFrame重载函数里面获取摄像头当前图像数据,然后通过Socket将图像数据和相关的用户名、命令等数据传输到服务器程序中。
服务器端(PC端)采用C#编写,通过监听相应的端口,在获取数据后进行相应的命令解析和图像数据还原,然后将图像数据传递至PictureBox控件中用于显示,这样就实现了手机摄像头的视频数据实时传输到服务器上。
如果需要将这些视频进行转发,通过服务器再将这些数据复制转发即可。
效果如下:
对于Android客户端上主要有几个地方需要注意,第一个就是Socket通信。
Socket通信可以通过Socket类来实现,直接结合PrintWriter来写入命令,如下定义的一个专门用于发送命令的线程类,当要连接到服务器和与服务器断开时,都需要发送命令通知服务器,此外在进行其他文字传输时也可以采用该方法,具体代码如下:
/**发送命令线程*/
classMySendCommondThreadextendsThread{
privateStringcommond;
publicMySendCommondThread(Stringcommond){
mond=commond;
}
publicvoidrun(){
//实例化Socket
try{
Socketsocket=newSocket(serverUrl,serverPort);
PrintWriterout=newPrintWriter(socket.getOutputStream());
out.println(commond);
out.flush();
}catch(UnknownHostExceptione){
}catch(IOExceptione){
}
}
}
如果是采用Socket发送文件,则可以通过OutputStream将ByteArrayInputStream数据流读入,而文件数据流则转换为ByteArrayOutputStream。
如果需要在前面添加文字,同样也需要转换为byte,然后写入OutputStream。
同样也可以通过定义一个线程类发送文件,如下:
/**发送文件线程*/
classMySendFileThreadextendsThread{
privateStringusername;
privateStringipname;
privateintport;
privatebytebyteBuffer[]=newbyte[1024];
privateOutputStreamoutsocket;
privateByteArrayOutputStreammyoutputstream;
publicMySendFileThread(ByteArrayOutputStreammyoutputstream,Stringusername,Stringipname,intport){
this.myoutputstream=myoutputstream;
this.username=username;
this.ipname=ipname;
this.port=port;
try{
myoutputstream.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
publicvoidrun(){
try{
//将图像数据通过Socket发送出去
SockettempSocket=newSocket(ipname,port);
outsocket=tempSocket.getOutputStream();
//写入头部数据信息
Stringmsg=.URLEncoder.encode("PHONEVIDEO|"+username+"|","utf-8");
byte[]buffer=msg.getBytes();
outsocket.write(buffer);
ByteArrayInputStreaminputstream=newByteArrayInputStream(myoutputstream.toByteArray());
intamount;
while((amount=inputstream.read(byteBuffer))!
=-1){
outsocket.write(byteBuffer,0,amount);
}
myoutputstream.flush();
myoutputstream.close();
tempSocket.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
而获取摄像头当前图像的关键在于onPreviewFrame()重载函数里面,该函数里面有两个参数,第一个参数为byte[],为摄像头当前图像数据,通过YuvImage可以将该数据转换为图片文件,同时还可用对该图片进行压缩和裁剪,将图片进行压缩转换后转换为ByteArrayOutputStream数据,即前面发送文件线程类中所需的文件数据,然后采用线程发送文件,如下代码:
@Override
publicvoidonPreviewFrame(byte[]data,Cameracamera){
//TODOAuto-generatedmethodstub
//如果没有指令传输视频,就先不传
if(!
startSendVideo)
return;
if(tempPreRate tempPreRate++; return; } tempPreRate=0; try{ if(data! =null) { YuvImageimage=newYuvImage(data,VideoFormatIndex,VideoWidth,VideoHeight,null); if(image! =null) { ByteArrayOutputStreamoutstream=newByteArrayOutputStream(); //在此设置图片的尺寸和质量 pressToJpeg(newRect(0,0,(int)(VideoWidthRatio*VideoWidth), (int)(VideoHeightRatio*VideoHeight)),VideoQuality,outstream); outstream.flush(); //启用线程将图像数据发送出去 Threadth=newMySendFileThread(outstream,pUsername,serverUrl,serverPort); th.start(); } } }catch(IOExceptione){ e.printStackTrace(); } } 值得注意的是,在调试中YuvImage可能找不到,在模拟机上无法执行该过程,但是编译后在真机中可以通过。 此外,以上传输文字字符都是采用UTF编码,在服务器端接收时进行解析时需要采用对应的编码进行解析,否则可能会出现错误解析。 Android客户端中关键的部分主要就这些,新建一个Android项目(项目名称为SocketCamera),在main布局中添加一个SurfaceView和两个按钮,如下图所示: 然后在SocketCameraActivity.java中添加代码,具体如下: packagecom.xzy; importjava.io.ByteArrayInputStream; importjava.io.ByteArrayOutputStream; importjava.io.IOException; importjava.io.OutputStream; importjava.io.PrintWriter; import.Socket; import.UnknownHostException; importandroid.app.Activity; importandroid.app.AlertDialog; importandroid.content.DialogInterface; importandroid.content.Intent; importandroid.content.SharedPreferences; importandroid.graphics.Rect; importandroid.graphics.YuvImage; importandroid.hardware.Camera; importandroid.hardware.Camera.Size; importandroid.os.Bundle; importandroid.preference.PreferenceManager; importandroid.view.Menu; importandroid.view.MenuItem; importandroid.view.SurfaceHolder; importandroid.view.SurfaceView; importandroid.view.View; importandroid.view.WindowManager; importandroid.view.View.OnClickListener; importandroid.widget.Button; publicclassSocketCameraActivityextendsActivityimplementsSurfaceHolder.Callback, Camera.PreviewCallback{ privateSurfaceViewmSurfaceview=null;//SurfaceView对象: (视图组件)视频显示 privateSurfaceHoldermSurfaceHolder=null;//SurfaceHolder对象: (抽象接口)SurfaceView支持类 privateCameramCamera=null;//Camera对象,相机预览 /**服务器地址*/ privateStringpUsername="XZY"; /**服务器地址*/ privateStringserverUrl="192.168.1.100"; /**服务器端口*/ privateintserverPort=8888; /**视频刷新间隔*/ privateintVideoPreRate=1; /**当前视频序号*/ privateinttempPreRate=0; /**视频质量*/ privateintVideoQuality=85; /**发送视频宽度比例*/ privatefloatVideoWidthRatio=1; /**发送视频高度比例*/ privatefloatVideoHeightRatio=1; /**发送视频宽度*/ privateintVideoWidth=320; /**发送视频高度*/ privateintVideoHeight=240; /**视频格式索引*/ privateintVideoFormatIndex=0; /**是否发送视频*/ privatebooleanstartSendVideo=false; /**是否连接主机*/ privatebooleanconnectedServer=false; privateButtonmyBtn01,myBtn02; /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); //禁止屏幕休眠getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); mSurfaceview=(SurfaceView)findViewById(R.id.camera_preview); myBtn01=(Button)findViewById(R.id.button1); myBtn02=(Button)findViewById(R.id.button2); //开始连接主机按钮 myBtn01.setOnClickListener(newOnClickListener(){ publicvoidonClick(Viewv){ //Common.SetGPSConnected(LoginActivity.this,false); if(connectedServer){//停止连接主机,同时断开传输 startSendVideo=false; connectedServer=false; myBtn02.setEnabled(false); myBtn01.setText("开始连接"); myBtn02.setText("开始传输"); //断开连接 Threadth=newMySendCommondThread("PHONEDISCONNECT|"+pUsername+"|"); th.start(); } else//连接主机 { //启用线程发送命令PHONECONNECT Threadth=newMySendCommondThread("PHONECONNECT|"+pUsername+"|"); th.start(); connectedServer=true; myBtn02.setEnabled(true); myBtn01.setText("停止连接"); } }}); myBtn02.setEnabled(false); myBtn02.setOnClickListener(newOnClickListener(){ publicvoidonClick(Viewv){ if(startSendVideo)//停止传输视频 { startSendVideo=false; myBtn02.setText("开始传输"); } else{//开始传输视频 startSendVideo=true; myBtn02.setText("停止传输"); } }}); } @Override publicvoidonStart()//重新启动的时候 { mSurfaceHolder=mSurfaceview.getHolder();//绑定SurfaceView,取得SurfaceHolder对象 mSurfaceHolder.addCallback(this);//SurfaceHolder加入回调接口 mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置显示器类型,setType必须设置 //读取配置文件 SharedPreferencespreParas=PreferenceManager.getDefaultSharedPreferences(SocketCameraActivity.this); pUsername=preParas.getString("Username","XZY"); serverUrl=preParas.getString("ServerUrl","192.168.0.100"); StringtempStr=preParas.getString("ServerPort","8888"); serverPort=Integer.parseInt(tempStr); tempStr=preParas.getString("VideoPreRate","1"); VideoPreRate=Integer.parseInt(tempStr); tempStr=preParas.getString("VideoQuality","85"); VideoQuality=Integer.parseInt(tempStr); tempStr=preParas.getString("VideoWidthRatio","100"); VideoWidthRatio=Integer.parseInt(tempStr); tempStr=preParas.getString("VideoHeightRatio","100"); VideoHeightRatio=Integer.parseInt(tempStr); VideoWidthRatio=VideoWidthRatio/100f; VideoHeightRatio=VideoHeightRatio/100f; super.onStart(); } @Override protectedvoidonResume(){ super.onResume(); InitCamera(); } /**初始化摄像头*/ privatevoidInitCamera(){ try{ mCamera=Camera.open(); }catch(Exceptione){ e.printStackTrace(); } } @Override protectedvoidonPause(){ super.onPause(); try{ if(mCamera! =null){ mCamera.setPreviewCallback(null);//! ! 这个必须在前,不然退出出错 mCamera.stopPreview(); mCamera.release(); mCamera=null; } }catch(Exceptione){ e.printStackTrace(); } } @Override publicvoidsurfaceChanged(SurfaceHolderarg0,intarg1,intarg2,intarg3){ //TODOAuto-generatedmethodstub if(mCamera==null){ return; } mCamera.stopPreview(); mCamera.setPreviewCallback(this); mCamera.setDisplayOrientation(90);//设置横行录制 //获取摄像头参数 Camera.Parametersparameters=mCamera.getParameters(); Sizesize=parameters.getPreviewSize(); VideoWidth=size.width; VideoHeight=size.height; VideoFormatIndex=parameters.getPreviewFormat(); mCamera.startPreview(); } @Override publicvoidsurfaceCreated(SurfaceHolderholder){ //TODOAuto-generatedmethodstub try{ if(mCamera! =null){ mCamera.setPreviewDisplay(mSurfaceHolder); mCamera.startPreview(); } }catch(IOExceptione){ e.printStackTrace(); } } @Override publicvoidsurfaceDestroyed(SurfaceHolderholder){ //TODOAuto-generatedmethodstub if(null! =mCamera){ mCamera.setPreviewCallback(null);//! ! 这个必须在前,不然退出出错 mCamera.stopPreview(); mCamera.release(); mCamera=null; } } @Override publicvoidonPreviewFrame(byte[]data,Cameracamera){ //TODOAuto-generatedmethodstub //如果没有指令传输视频,就先不传 if(! startSendVideo) return; if(tempPreRate tempPreRate++; retur
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基于 Socket Android 手机 视频 实时 传输