




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
基于Scket的Andrid手机视频实时传输肖泽云首先,简单介绍一下原理。主要是在手机客户端(Andrid)通过实现ewCallback接口,在其nPreviewFrame重载函数里面获取摄像头当前图像数据,然后通过Scket将图像数据和相关的用户名、命令等数据传输到服务器程序中。服务器端(PC端)采用C编写,通过监听相应的端口,在获取数据后进行相应的命令解析和图像数据还原,然后将图像数据传递至PictureB控件中用于显示,这样就实现了手机摄像头的视频数据实时传输到服务器上。如果需要将这些视频进行转发,通过服务器再将这些数据复制转发即可。效果如下:对于Andrid客户端上主要有几个地方需要注意,第一个就是Scket通信。Scket通信可以通过Scket类来实现,直接结合PrintWriter来写入命令,如下定义的一个专门用于发送命令的线程类,当要连接到服务器和与服务器断开时,都需要发送命令通知服务器,此外在进行其他文字传输时也可以采用该方法,具体代码如下:/发送命令线程/classMySendCmmndThreadetendsThread{ privateStringcmmnd; publicMySendCmmndThread(Stringcmmnd){ nd=cmmnd; } publicvidrun{ //实例化Sckettry{ Scketscket=newScket(serverUrl,serverPrt); PrintWriterut=newPrintWriter(tputStream); ln(cmmnd); ; }catch(UnknwnHstEceptine){ }catch(IEceptine){ } }}如果是采用Scket发送文件,则可以通过utputStream将ByteArrayInputStream数据流读入,而文件数据流则转换为ByteArrayutputStream。如果需要在前面添加文字,同样也需要转换为byte,然后写入utputStream。同样也可以通过定义一个线程类发送文件,如下:/发送文件线程/classMySendFileThreadetendsThread{ privateStringusername; privateStringipname; privateintprt; privatebytebyteBuffer[]=newbyte[1024]; privateutputStreamutscket; privateByteArrayutputStreammyutputstream; publicMySendFileThread(ByteArrayutputStreammyutputstream,Stringusername,Stringipname,intprt){ putstream=myutputstream; ame=username; e=ipname; =prt;try{ ; }catch(IEceptine){ e.printStackTrace; } } publicvidrun{try{ //将图像数据通过Scket发送出去SckettempScket=newScket(ipname,prt);utscket=tputStream;//写入头部数据信息 Stringmsg=.e("PHNEVIDE|"+username+"|","utf-8");byte[]buffer=tes;(buffer);ByteArrayInputStreaminputstream=newByteArrayInputStream(eArray);intamunt;while((amunt=(byteBuffer))!=-1){(byteBuffer,0,amunt);};;;}catch(IEceptine){e.printStackTrace;}}}而获取摄像头当前图像的关键在于nPreviewFrame重载函数里面,该函数里面有两个参数,第一个参数为byte[],为摄像头当前图像数据,通过YuvImage可以将该数据转换为图片文件,同时还可用对该图片进行压缩和裁剪,将图片进行压缩转换后转换为 ByteArrayutputStream数据,即前面发送文件线程类中所需的文件数据,然后采用线程发送文件,如下代码: @verride publicvidnPreviewFrame(byte[]data,Cameracamera){ //TDAut-generatedmethdstub //如果没有指令传输视频,就先不传 if(!startSendVide) return; if(tempPreRate<VidePreRate){ tempPreRate; return; } tempPreRate=0; try{ if(data!=null) { YuvImageimage=newYuvImage(data,VideFrmatInde,VideWidth,VideHeight,null); if(image!=null) { ByteArrayutputStreamutstream=newByteArrayutputStream; //在此设置图片的尺寸和质量 essTeg(newRect(0,0,(int)(VideWidthRatiVideWidth), (int)(VideHeightRatiVideHeight)),VideQuality,utstream); ; //启用线程将图像数据发送出去 Threadth=newMySendFileThread(utstream,pUsername,serverUrl,serverPrt); th.start; } } }catch(IEceptine){ e.printStackTrace; } }值得注意的是,在调试中YuvImage可能找不到,在模拟机上无法执行该过程,但是编译后在真机中可以通过。此外,以上传输文字字符都是采用UTF编码,在服务器端接收时进行解析时需要采用对应的编码进行解析,否则可能会出现错误解析。Andrid客户端中关键的部分主要就这些,新建一个Andrid项目(项目名称为ScketCamera),在main布局中添加一个SurfaceView和两个按钮,如下图所示:然后在中添加代码,具体如下:package;imprt.ByteArrayInputStream;imprt.ByteArrayutputStream;imprt.IEceptin;imprt.utputStream;imprt.PrintWriter;imprt.Scket;imprt.UnknwnHstEceptin;imprt.Activity;imprt.AlertDialg;imprtnt.DialgInterface;imprtnt.Intent;imprtnt.SharedPreferences;imprt;imprtage;imprta;imprta.Size;imprt.Bundle;imprtrenceManager;imprt.Menu;imprt.MenuItem;imprt.SurfaceHlder;imprt.SurfaceView;imprt.View;imprt.WindwManager;imprt.ckListener;imprtt.Buttn;publicclassScketCameraActivityetendsActivityimplementsack,ewCallback{ privateSurfaceViewmSurfaceview=null;//SurfaceView对象:(视图组件)视频显示privateSurfaceHldermSurfaceHlder=null;//SurfaceHlder对象:(抽象接口)SurfaceView支持类privateCameramCamera=null;//Camera对象,相机预览/服务器地址/privateStringpUsername="ZY";/服务器地址/privateStringserverUrl="00";/服务器端口/privateintserverPrt=8888;/视频刷新间隔/privateintVidePreRate=1;/当前视频序号/privateinttempPreRate=0;/视频质量/privateintVideQuality=85;/发送视频宽度比例/privateflatVideWidthRati=1;/发送视频高度比例/privateflatVideHeightRati=1;/发送视频宽度/privateintVideWidth=320;/发送视频高度/privateintVideHeight=240;/视频格式索引/privateintVideFrmatInde=0;/是否发送视频/privatebleanstartSendVide=false;/是否连接主机/privatebleancnnectedServer=false;privateButtnmyBtn01,myBtn02;/Calledwhentheactivityisfirstcreated./@verridepublicvidnCreate(BundlesavedInstanceState){ate(savedInstanceState);setCntentView(R.);//禁止屏幕休眠getWindw.setFlags(KEEPSCREENN, KEEPSCREENN);mSurfaceview=(SurfaceView)findViewById(R.id.camerapreview);myBtn01=(Buttn)findViewById(R.id.buttn1);myBtn02=(Buttn)findViewById(R.id.buttn2);//开始连接主机按钮ClickListener(newnClickListener{ publicvidnClick(Viewv){ //SCnnected(,false); if(cnnectedServer){//停止连接主机,同时断开传输 startSendVide=false; cnnectedServer=false; abled(false); t("开始连接"); t("开始传输"); //断开连接 Threadth=newMySendCmmndThread("PHNEDISCNNECT|"+pUsername+"|"); th.start; } else//连接主机 { //启用线程发送命令PHNECNNECT Threadth=newMySendCmmndThread("PHNECNNECT|"+pUsername+"|"); th.start; cnnectedServer=true; abled(true); t("停止连接"); } }});abled(false);ClickListener(newnClickListener{ publicvidnClick(Viewv){ if(startSendVide)//停止传输视频 { startSendVide=false; t("开始传输"); } else{//开始传输视频 startSendVide=true; t("停止传输"); } }});}@verridepublicvidnStart//重新启动的时候{ mSurfaceHlder=lder;//绑定SurfaceView,取得SurfaceHlder对象 llback(this);//SurfaceHlder加入回调接口 pe(CETYPEPUSHBUFFERS);//设置显示器类型,setType必须设置 //读取配置文件SharedPreferencespreParas=faultSharedPreferences;pUsername=ring("Username","ZY");serverUrl=ring("ServerUrl","00"); StringtempStr=ring("ServerPrt","8888"); serverPrt=Int(tempStr);tempStr=ring("VidePreRate","1");VidePreRate=Int(tempStr); tempStr=ring("VideQuality","85");VideQuality=Int(tempStr);tempStr=ring("VideWidthRati","100");VideWidthRati=Int(tempStr);tempStr=ring("VideHeightRati","100");VideHeightRati=Int(tempStr);VideWidthRati=VideWidthRati/100f;VideHeightRati=VideHeightRati/100f;rt;}@verrideprtectedvidnResume{ume;InitCamera;}/初始化摄像头/privatevidInitCamera{ try{ mCamera=; }catch(Eceptine){e.printStackTrace;}}@verrideprtectedvidnPause{se;try{ if(mCamera!=null){ eviewCallback(null);//!!这个必须在前,不然退出出错 review; se; mCamera=null; }}catch(Eceptine){e.printStackTrace;}} @verride publicvidsurfaceChanged(SurfaceHlderarg0,intarg1,intarg2,intarg3){ //TDAut-generatedmethdstub if(mCamera==null){return;}review;eviewCallback(this);splayrientatin(90);//设置横行录制//获取摄像头参数etersparameters=rameters;Sizesize=eviewSize;VideWidth=;VideHeight=t;VideFrmatInde=eviewFrmat;Preview; } @verride publicvidsurfaceCreated(SurfaceHlderhlder){ //TDAut-generatedmethdstub try{if(mCamera!=null){eviewDisplay(mSurfaceHlder);Preview;}}catch(IEceptine){e.printStackTrace;} } @verride publicvidsurfaceDestryed(SurfaceHlderhlder){ //TDAut-generatedmethdstub if(null!=mCamera){eviewCallback(null);//!!这个必须在前,不然退出出错review;se;mCamera=null;} } @verride publicvidnPreviewFrame(byte[]data,Cameracamera){ //TDAut-generatedmethdstub //如果没有指令传输视频,就先不传 if(!startSendVide) return; if(tempPreRate<VidePreRate){ tempPreRate; return; } tempPreRate=0; try{ if(data!=null) { YuvImageimage=newYuvImage(data,VideFrmatInde,VideWidth,VideHeight,null); if(image!=null) { ByteArrayutputStreamutstream=newByteArrayutputStream; //在此设置图片的尺寸和质量 essTeg(newRect(0,0,(int)(VideWidthRatiVideWidth), (int)(VideHeightRatiVideHeight)),VideQuality,utstream); ; //启用线程将图像数据发送出去 Threadth=newMySendFileThread(utstream,pUsername,serverUrl,serverPrt); th.start; } } }catch(IEceptine){ e.printStackTrace; } } /创建菜单/publicbleannCreateptinsMenu(Menumenu){ (0,0,0,"系统设置"); (0,1,1,"关于程序"); (0,2,2,"退出程序"); returnateptinsMenu(menu);}/菜单选中时发生的相应事件/publicbleannptinsItemSelected(MenuItemitem){ insItemSelected(item);//获取菜单 switch(emId)//菜单序号 { case0: //系统设置 { Intentintent=newIntent(this,); startActivity(intent); } break; case1://关于程序 { newer(this) .setTitle("关于本程序") .setMessage("本程序由武汉大学水利水电学院肖泽云设计、编写。\nEmail:") .setPsitiveButtn ( "我知道了", newckListener { @verride publicvidnClick(DialgInterfacedialg,intwhich) { } } ) .shw; } break; case2://退出程序 { //杀掉线程强制退出 .rcess(.); } break; } returntrue;}/发送命令线程/classMySendCmmndThreadetendsThread{ privateStringcmmnd; publicMySendCmmndThread(Stringcmmnd){ nd=cmmnd; } publicvidrun{ //实例化Sckettry{ Scketscket=newScket(serverUrl,serverPrt); PrintWriterut=newPrintWriter(tputStream); ln(cmmnd); ; }catch(UnknwnHstEceptine){ }catch(IEceptine){ } }}/发送文件线程/classMySendFileThreadetendsThread{ privateStringusername; privateStringipname; privateintprt; privatebytebyteBuffer[]=newbyte[1024]; privateutputStreamutscket; privateByteArrayutputStreammyutputstream; publicMySendFileThread(ByteArrayutputStreammyutputstream,Stringusername,Stringipname,intprt){ putstream=myutputstream; ame=username; e=ipname; =prt;try{ ; }catch(IEceptine){ e.printStackTrace; } } publicvidrun{try{ //将图像数据通过Scket发送出去SckettempScket=newScket(ipname,prt);utscket=tputStream;//写入头部数据信息 Stringmsg=.e("PHNEVIDE|"+username+"|","utf-8");byte[]buffer=tes;(buffer);ByteArrayInputStreaminputstream=newByteArrayInputStream(eArray);intamunt;while((amunt=(byteBuffer))!=-1){(byteBuffer,0,amunt);};;;}catch(IEceptine){e.printStackTrace;}}}}此外还有一些参数,在res/ml新建一个文件,添加服务器地址、端口、用户名等参数设置,如下:<?mlversin="1.0"encding="utf-8"?><PreferenceScreenmlns:andrid=""><PreferenceCategryandrid:title="服务器设置"><EditTetPreferenceandrid:key="Username"andrid:title="用户名"andrid:summary="用于连接服务器的用户名"andrid:defaultValue="ZY"/><EditTetPreferenceandrid:key="ServerUrl"andrid:title="视频服务器地址"andrid:summary="保存服务器地址"andrid:defaultValue="00"/><EditTetPreferenceandrid:key="ServerPrt"andrid:title="服务器端口"andrid:summary="连接服务器的端口地址"andrid:defaultValue="8888"/></PreferenceCategry><PreferenceCategryandrid:title="视频设置"> <EditTetPreferenceandrid:key="VidePreRate"andrid:title="视频刷新间隔"andrid:summary="设置视频刷新的间隔值,应大于等于0,值越大视频传输间隔越长"andrid:defaultValue="1"/><EditTetPreferenceandrid:key="VideQuality"andrid:title="图像质量"andrid:summary="设置图像压缩的质量,值为0~100,值越高越清晰,但同时数据也更大"andrid:defaultValue="85"/><EditTetPreferenceandrid:key="VideWidthRati"andrid:title="图像宽度缩放比例"andrid:summary="设置图像的宽度缩放比例,值为0~100,值越高图像分辨率越高"andrid:defaultValue="100"/><EditTetPreferenceandrid:key="VideHeightRati"andrid:title="图像高度缩放比例"andrid:summary="设置图像的高度缩放比例,值为0~100,值越高图像分辨率越高"andrid:defaultValue="100"/></PreferenceCategry></PreferenceScreen>编译程序,在模拟机上效果如下:接下来就是服务器端接收手机传输的视频数据,这与一般CS架构中服务器程序类似,主要是监听端口,然后解析数据。现新建一个C应用程序项目(项目名称为“手机摄像头”),首先定义一些全局变量,主要包括服务器地址、端口以及相关监听对象等,如下:///<summary>///服务器状态,如果为false表示服务器暂停,true表示服务器开启///</summary>publicblServerStatus=false;///<summary>///服务器地址///</summary>publicstringServerAddress;///<summary>///服务器端口///</summary>publicintServerPrt;///<summary>///开启服务的线程///</summary>privateThreadprcessr;///<summary>///用于TCP监听///</summary>privateTcpListenertcpListener;///<summary>///与客户端连接的套接字接口///</summary>privateScketclientScket;///<summary>///用于处理客户事件的线程///</summary>privateThreadclientThread;///<summary>///手机客户端所有客户端的套接字接口///</summary>privateHashtablePhneClientSckets=newHashtable;///<summary>///手机用户类数组///</summary>publicArrayListPhneUsersArray=newArrayList;///<summary>///手机用户名数组///</summary>publicArrayListPhneUserNamesArray=newArrayList;///<summary>///图像数据流///</summary>privateArrayListStreamArray;然后定义处理客户端传递数据的函数PrcessClient,主要对接收数据进行命令解析。如果是手机连接的命令("PHNECNNECT"),就在记录该套接字对象,同时在列表中添加该对象;如果是断开连接的命令("PHNEDISCNNECT"),就移除该对象;如果是手机视频命令("PHNEVIDE"),就分解其包含的图像数据,如果存在该用户对应的视频窗口,就传递该图像数据到这个视频窗口中。具体代码如下:regin处理客户端传递数据及处理事情///<summary>///处理客户端传递数据及处理事情///</summary>privatevidPrcessClient{Scketclient=clientScket;blkeepalive=true;while(keepalive){(50);Byte[]buffer=null;bltag=false;try{buffer=newByte[1024];//ableintcunt=ve(buffer,);//接收客户端套接字数据if(cunt>0)//接收到数据tag=true;}catch(Eceptine){keepalive=false;if(cted)nnect(true);;}if(!tag){if(cted)nnect(true);;keepalive=false;}stringclientCmmand="";try{clientCmmand=.8.GetString(buffer);//转换接收的数据,数据来源于客户端发送的消息if(ins("%7C"))//从Andrid客户端传递部分数据clientCmmand=ce("%7C","|");//替换UTF中字符%7C为|}catch{}//分析客户端传递的命令来判断各种操作string[]messages=('|');if(messages!=nullh>0){stringtempStr=messages[0];//第一个字符串为命令if(tempStr=="PHNECNNECT")//手机连接服务器{try{stringtempClientName=messages[1].Trim;e(messages[1]);//删除之前与该用户的连接(messages[1],client);//建立与该客户端的Scket连接UserClasstempUser=newUserClass;ame=tempClientName;Time=;SckettempScket=(Scket)PhneClientSckets[tempClientName];ress=ing;inttempInde=f(tempClientName);if(tempInde>=0){PhneUserNamesArray[tempInde]=tempClientName;PhneUsersArray[tempInde]=tempUser;MemryStreamstream2=(MemryStream)StreamArray[tempInde];if(stream2!=null){;se;}}else//新增加{(tempClientName);(tempUser);(null);}RefreshPhneUsers;}catch(Eceptinecept){}}elseif(tempStr=="PHNEDISCNNECT")//某个客户端退出了{try{stringtempClientName=messages[1];RemvePhneUser(tempClientName);inttempPhneInde=f(tempClientName);if(tempPhneInde>=0){eAt(tempPhneInde);MemryStreammemStream=(MemryStream)StreamArray[tempPhneInde];if(memStream!=null){;se;}eAt(tempPhneInde);}SckettempScket=(Scket)PhneClientSckets[tempClientName];//第1个为客户端的ID,找到该套接字if(tempScket!=null){;e(tempClientName);}keepalive=false;}catch(Eceptinecept){}RefreshPhneUsers;}elseif(tempStr=="PHNEVIDE")//接收手机数据流{try{stringtempClientName=messages[1];stringtempFr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 产品持股合同范本
- 科技创新推动下的金融业务防骗举措探讨
- 易货加盟合同范本
- 金融服务收购合同范本
- 电子设备中紧固件的防腐防潮技术探讨
- 2025-2030年中国特种油品产业发展状况及营销战略研究报告
- 2025-2030年中国热塑性弹性体市场规模分析及投资前景规划研究报告
- 服装聘请员工合同范本
- 2025-2030年中国有机棉市场运行状况及发展趋势分析报告
- 2025-2030年中国普利类市场运行态势及发展规模研究报告
- 《国际贸易实务》课件
- 班级管理课件:班级组织的建设
- 《共圆中国梦》示范课教学设计【部编人教版九年级道德与法治上册】
- 《更年期中医调》课件
- 公立医院绩效考核微创手术目录(第2版)
- 九年级中考物理-安培定则(右手螺旋定则)复习题汇总及解析
- 物流营销(第四版) 课件 胡延华 第1、2章 物流营销概述、物流营销市场调查与分析
- 华东师大版九年级数学下册全册课时练习(一课一练)
- “课程思政”融入专业课教学的探索课程思政与专业课结合
- 工程结算审核服务方案技术标
- 《中西医结合:心血管疾病的中西医防治》
评论
0/150
提交评论