-
2018-01-12 13:37:10
注: 本文提供的参考代码其实网上都可以找到,这里做一个整理,供大家交流学习,共同提高。
搜索:Probe : 发现网络摄像头,获取 webserver 地址 。例如:http://192.168.15.240/onvif/device_service
能力获取:GetCapabilities :获取设备能力文件,从中识别出媒体信息地址 URI。 例如:http://192.168.15.240/onvif/Media
媒体信息获取:GetProfiles : 获取媒体信息文件,识别主通道、子通道的视频编码分辨率
RTSP地址获取:GetStreamUri :获取指定通道的流媒体地址 例如:rtsp://192.168.15.240:554/Streaming/Channels/2?transportmode=unicast
在基于onvif规范的视频监控系统客户端(二)中,我们已经搭建好了onvif框架,创建了一个名为onvif_new的文件夹,搜索设备的代码我们也添加在这个文件夹下面。具体代码如下:能力获取函数char* UserGetCapabilities(struct __wsdd__ProbeMatches *resp,struct _tds__GetCapabilitiesResponse *capa_resp)
{
struct soap* soap;
struct SOAP_ENV__Header header;
struct _tds__GetCapabilities capa_req;
struct _trt__GetProfilesResponse getProfilesResponse;
struct _trt__GetStreamUriResponse getStreamUriResponse;soap = ONVIF_Initsoap(&header, NULL, NULL, 10);capa_req.Category = (enum tt__CapabilityCategory *)soap_malloc(soap, sizeof(int));
capa_req.__sizeCategory = 1;
*(capa_req.Category) = (enum tt__CapabilityCategory)(tt__CapabilityCategory__Media);const char *soap_action = "http://www.onvif.org/ver10/device/wsdl/GetCapabilities";int result = soap_call___tds__GetCapabilities(soap, resp->wsdd__ProbeMatches->ProbeMatch->XAddrs, soap_action, &capa_req, capa_resp);
//int result = soap_call___tds__GetCapabilities(soap, "http://192.168.1.51/onvif/device_service", NULL, &capa_req, capa_resp);if (soap->error){
printf("[%d]--->>> soap error: %d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
int retval = soap->error;
exit(-1) ;
}else{
if(capa_resp->Capabilities==NULL){
printf(" GetCapabilities failed! result=%d \n",result);
}else{
return(UserGetProfiles(&getProfilesResponse,capa_resp));
}
}soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}
媒体信息获取char* UserGetProfiles(struct _trt__GetProfilesResponse *getProfilesResponse ,struct _tds__GetCapabilitiesResponse *capa_resp)
{
int result=0 ;struct soap* soap;
struct SOAP_ENV__Header header;
struct _trt__GetProfiles getProfiles;
struct _trt__GetStreamUriResponse getStreamUriResponse;soap = ONVIF_Initsoap(&header, NULL, NULL, 10);//soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);
result = soap_call___trt__GetProfiles(soap, capa_resp->Capabilities->Media->XAddr, NULL, &getProfiles, getProfilesResponse);
if (result==-1){
printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
result = soap->error;
exit(-1);
}else{
if(getProfilesResponse->Profiles!=NULL) {
if(getProfilesResponse->Profiles->Name!=NULL){
printf("Profiles Name:%s \n",getProfilesResponse->Profiles->Name);}
if(getProfilesResponse->Profiles->token!=NULL){
printf("Profiles Taken:%s\n",getProfilesResponse->Profiles->token);
}
return(UserGetUri(&getStreamUriResponse,getProfilesResponse,capa_resp));
}else{
printf("Profiles Get inner Error\n");
}
}soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}
rtsp地址获取:char* UserGetUri(struct _trt__GetStreamUriResponse *trt__GetStreamUriResponse,struct _trt__GetProfilesResponse *trt__GetProfilesResponse,struct _tds__GetCapabilitiesResponse *capa_resp)
{
int result=0 ;
struct soap* soap;
struct SOAP_ENV__Header header;
struct _trt__GetStreamUri getStreamUri;
soap = ONVIF_Initsoap(&header, NULL, NULL, 10);
soap->socket = 3;
getStreamUri.StreamSetup = (struct tt__StreamSetup*)soap_malloc(soap, sizeof(struct tt__StreamSetup));//初始化,分配空间
getStreamUri.StreamSetup->Stream = tt__StreamType__RTP_Unicast;//0;//stream type
getStreamUri.StreamSetup->Transport = (struct tt__Transport *)soap_malloc(soap, sizeof(struct tt__Transport));//初始化,分配空间
getStreamUri.StreamSetup->Transport->Protocol = tt__TransportProtocol__UDP;//0;
getStreamUri.StreamSetup->Transport->Tunnel = NULL;
getStreamUri.StreamSetup->__size = 1;
getStreamUri.StreamSetup->__any = NULL;
getStreamUri.StreamSetup->__anyAttribute =NULL;
getStreamUri.ProfileToken = (char*)soap_malloc(soap,1024);
strcpy(getStreamUri.ProfileToken, trt__GetProfilesResponse->Profiles->token);
// getStreamUri.ProfileToken = trt__GetProfilesResponse->Profiles->token ;
//soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);
soap_call___trt__GetStreamUri(soap, capa_resp->Capabilities->Media->XAddr, NULL, &getStreamUri, trt__GetStreamUriResponse);
if (soap->error) {
printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
result = soap->error;
}
return trt__GetStreamUriResponse->MediaUri->Uri;
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
}
完整代码下载地址:编译步骤:makeg++ main.c -L这个文件的绝对路径 -LDisC
更多相关内容 -
局域网找摄像头工具
2019-04-18 19:03:36找局域网里面的摄像头资料。改摄像头的信息。方便管理局域网里面的设备。 -
易语言局域网监控系统源码-易语言
2021-06-12 22:22:18易语言局域网监控系统源码 -
局域网远程监控摄像头C#.rar
2020-04-10 12:00:09项目需要到网上想下载一个UDP远程通信,客户端实时上报摄像头信息到服务端。服务端实现远程监控客户端摄像头的功能。 -
【Android】关于WIFI局域网的手机摄像头当视频监控用实现方案详解
2022-04-03 18:43:26现有的智能手机是可以充当Wifi摄像头来使用的,这就需要装一个App就能实现了,如果是用别的下载来APP安装用来会不会不放心呢,如果自己有能力,那就可以通过开发Android App项目过程来实现视频监控,有兴趣的来看看...闲置在家不用的Android手机有一两个都蒙尘了,想要把它们充分利用起来,可知道,现有的智能手机是可以充当Wifi摄像头来使用的,这就需要装一个App就能实现了,如果是用别的下载来APP安装用来会不会不放心呢,如果自己有能力,那就可以通过开发Android App项目过程来实现视频监控,有兴趣的来看看接下来的实现方案,
要完成整个过程,至少需要两部手机,一个手机用来充当WIFI摄像头(可以开启WIFI热点),另一个手机当视频监控用的,还是建议用WIFI路由器,就看中它信号强,网络又稳定
关于能看懂此文章的条件
- 会使用Android Studio开发工具
- 熟悉Java编程语言,开发过Android App
- 对WIFI路由器设置和网络信息收发报文
TCP
,UDP
原理有过了解
1.首先,打开Android Studio开发工具,选择新建Android 项目,使用Java语言,模板就选择 Emtpy Activity,在activity_main.xml文件中做好布局,具体布局内容太多这里就不贴了,自己布局就好,拖放组件是很简单的操作,只需要放三个按钮组件即可,分别是扫描摄像头,开启摄像头,退出APP,其它的不重要
2. 然后,在MainActivity.class上写代码,实现能打开按钮对应的页面即可,请看如下代码,其中用到的一些类,例如DeviceInfo.class, Common.class, BaseBackActivity.class这些就不贴了,看注释,具体的请等在后面提供的项目源码里看public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //...这里省略了,只是处理了对标题栏的隐藏 setContentView(R.layout.activity_main); //获取布局中的按钮组件 Button btnScan = findViewById(R.id.button_scan); Button btnPreview = findViewById(R.id.button_preview); Button btnExit = findViewById(R.id.buttonExit); final Context context = MainActivity.this; //设置点击事件 btnExit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MainActivity.this.finish();//退出 } }); btnScan.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //初始化设备信息,包括了手机的摄像头相关属性,如名称,IP,数量 DeviceInfo di = DeviceInfo.init(context); //...省略了一些判断细节,如判断IP是否正确,判断摄像头的网络状态 //打开扫描局域网内的摄像头页面 BaseBackActivity.navigateTo(MainActivity.this, ScanActivity.class, di); } }); btnPreview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //初始化设备信息 DeviceInfo di = DeviceInfo.init(context); //...省略了一些判断细节,这一步是判断摄像头的授权 if(Common.requestCameraPermission(MainActivity.this)){ //打开WIFI摄像头的预览页面 BaseBackActivity.navigateTo(MainActivity.this, PreviewActivity.class, di); } } }); } }
-
接下来,做一个扫描摄像头页面的布局,文件是activity_scan.xml,大致布局如下图所示,运行后的效果图,就一个
ListView
展示列表的组件,还有标题栏上的搜索图标,那是扫描按钮
-
接着,创建一个对应页面的类ScanActivity.class 文件后,写上代码,如下
/** * 扫描摄像头窗口 * */ public class ScanActivity extends BaseBackActivity { //定义扫描线程 private ScanThread thread; //定义列表组件 private ListView list; //定义对初始化扫描的判断值 private boolean isFirstScan = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scan); //省略了,处理初始化标题栏的 //获取上一页传来的设备信息对象, getSerializable()是来自父类BaseBackActivity的方法 DeviceInfo di = (DeviceInfo) getSerializable(); //创建线程时,传入设备信息对象 thread = new ScanThread(this, di, new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); //处理线程传来的消息 switch (msg.what) { //扫描完成通知 case BaseThread.MESSAGE_SUCCESS: { //获取扫描后的局域网内所有可用的摄像头 ArrayList<RemoteCamera> cameras = thread.getCameras(); if(cameras.isEmpty()) { //showToast方法来自父类,弹出提示 showToast(ScanActivity.this, "找不到可用的摄像头!"); }else{ //更新摄像头列表显示的 CamerasAdapter adapter = new CamerasAdapter(ScanActivity.this, cameras); list.setAdapter(adapter); list.invalidate(); showToast(ScanActivity.this, "扫描完成!"); } } break; //扫描失败,或更新状态 case ScanThread.MESSAGE_FAIL: case ScanThread.MESSAGE_LOADING: showToast(ScanActivity.this, (String) msg.obj); break; default: } } }); list = findViewById(R.id.listview); //列表的点击事件 list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { RemoteCamera camera = thread.getCameras().get(i); //打开远程摄像头连接页面,传递一个摄像头信息camera navigateTo(ScanActivity.this, RemoteActivity.class, camera); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { //...此处省略,加载菜单布局的 return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { //监听菜单按钮 switch (item.getItemId()) { //扫描图标按钮被点击 case R.id.app_bar_search: thread.startScanCamera(); return true; default: } return super.onOptionsItemSelected(item); } @Override protected void onResume() { super.onResume(); //第一次打开页面就扫描 if (!isFirstScan) { thread.startScanCamera(); isFirstScan = true; } } }
- 看上面就会发现,扫描的处理操作是比较耗时的,放在线程ScanThread.class里处理是合理的,这样用户操作就不会觉得卡,处理操作的方法大致讲一下
public class ScanThread extends BaseThread { private ArrayList<RemoteCamera> cameras; private DeviceInfo info; //定义一个扫描线程 private Thread scanThread = null; public ScanThread(Activity context, DeviceInfo info, Handler handler) { //传参给父类BaseThread的构造方法,初始化 super(context, handler); this.info = info; this.cameras = new ArrayList<RemoteCamera>(); //来自父类的线程,用于处理接收的 thread = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub try { //初始化端口 mSocket = new DatagramSocket(null); //... mSocket.bind(new InetSocketAddress(SenderThread.FIND_CAMERA_PORT)); while(!Thread.interrupted()) { //定一个空的数据报文 DatagramPacket pack = new DatagramPacket(new byte[1028], 1028); //用空数据报文来接收数据,这时会一直等待,阻塞 mSocket.receive(pack); //收到时,将报文里的数据转换成字符串 String s = new String(pack.getData(), 0, pack.getLength()); //在把字符串转成字符串数组,将接收到数据按照约定的协议转换一下 String[] datas = Common.getDeviceData(s); //...此处省略,处理拿到count, 是摄像头数量,添加到cameras中 cameras.add(new RemoteCamera(cameras.size(), datas[0], count, datas[2])); //发完成提示消息 showToast("扫到一个摄像头", MESSAGE_SUCCESS); } } catch (Exception e) { showToast(e.getMessage());//遇到错误! } finally { cancelScan(true); } } }); thread.start(); } public void startScanCamera() { //...次数省略判断的细节,下一步提示用户扫描中,建一个线程处理 showToast("扫描中...", MESSAGE_LOADING); scanThread = new Thread(new Runnable() { @Override public void run() { try { //...此处省略一些细节,定义data数据 // 定义局域网的广播地址,这样表示 *.*.*.255, InetAddress cameraAddress = InetAddress.getByName(Common.getWanIP(info.getLocalIp())+"255"); // 将data数据封装到报文中,还有IP地址,FIND_CAMERA_PORT 是 30000 DatagramPacket pack = new DatagramPacket(data, data.length, cameraAddress, FIND_CAMERA_PORT); //将数据报文发送到广播地址,只要是连接到此局域网内的所有设备开放的30000端口都会收到该广播报文 mSocket.send(pack); } catch (Exception e) { showToast(e.getMessage());//遇到错误! } finally { //处理完后取消操作 cancelScan(false); } } }); scanThread.start(); } //判断是否在扫描 public boolean isScaning() { return scanThread!=null; } public ArrayList<RemoteCamera> getCameras() { return cameras; } //取消扫描 public void cancelScan(boolean isCancelAll) { if(isCancelAll) { //处理来自父类的方法 cancelThread(); } if (isScaning()) { scanThread.interrupt(); scanThread = null; } } }
💡小提示
注意到创建的页面都有继承BaseBackActivity.class类,创建的线程都有继承类BaseThread.class,具体怎么写的,这里不详细讲了,那说下它的作用,它是相当于一个可以复用的类吧,类似模板,可以这样理解,稍微能明白,实现不会复杂- 把需要添加的权限都写上,在AndroidManifest.xml文件中,添加如下关键的代码
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="..."> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> <!-- 此处省略... --> </manifest>
- 接下来,打开视频监控,也就是远程摄像头预览的页面,大致布局如下图所示,文件是activity_remote.xml,放一个展示状态的
TextView
组件,还有一个预览画面的SurfaceView
组件放在中间,宽高分别是固定的320dp,240dp
💡小提示
有没有注意到,看视频监控上的状态栏,网络保持在23.3K/s每秒,这已经是一帧一帧的传输图像了,图像是320x240分辨率的,传输量会不会低了,可能有点卡吧,跟网络传输延迟有关的- 接着,创建一个对应页面的类,在RemoteActivity.class 文件里,写上代码,如下
public class RemoteActivity extends BaseBackActivity { //定义一个网络接收的线程 private ReceiveThread thread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_remote); //...省略了,处理初始化标题栏的 //获取上一页传来的设备信息对象, getSerializable()是来自父类BaseBackActivity的方法 RemoteCamera remote = (RemoteCamera) getSerializable(); //从布局中获取组件 SurfaceView view = findViewById(R.id.surfaceView2); final TextView showState = findViewById(R.id.textView_state2); //将远程设备信息设置到标题栏上 setTitle("远程摄像头:"+remote.toString()); //先获取焦点,然后设置屏幕长亮 view.setFocusable(true); view.setKeepScreenOn(true); //建立一个接收线程,传一个远程设备信息对象,还有预览组件的holder用于更新画面 thread = new ReceiveThread(this, new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); //...处理线程发来的消息提示 } }, remote, view.getHolder()); } @Override protected void onPostResume() { super.onPostResume(); //让线程开始接收工作 thread.startReceive(); } @Override protected void onDestroy() { //当前页面关闭时,让线程结束工作 thread.cancelReceive(); super.onDestroy(); } }
- 看上一步就会发现,关键的处理接收方法都放在线程ReceiveThread.class里,那是比较耗时的操作,大致讲一下
public class ReceiveThread extends BaseThread { private RemoteCamera remote; private SurfaceHolder holder; public ReceiveThread(Activity context, Handler handler, RemoteCamera remote, SurfaceHolder holder) { super(context, handler); this.remote = remote; this.holder = holder; } public void startReceive() { if (thread!=null) { return; } thread = new Thread(new Runnable() { @Override public void run() { String errMsg = "未知错误"; //... try { //... if (mSocket==null) { mSocket = new DatagramSocket(null); //用开放30000端口来接收 FIND_CAMERA_PORT mSocket.bind(new InetSocketAddress(FIND_CAMERA_PORT)); //... showToast("连接中...", MESSAGE_UPDATE_STATE); //发送请求接收下一帧图片 sendRet(mSocket, baos); //... showToast("等待接收...", MESSAGE_UPDATE_STATE); while(mSocket!=null) { //...定义空的数据报文packet try { //接收中,等待,此处阻塞 mSocket.receive(packet); }catch (SocketTimeoutException te) { showToast("连接超时..."+getLocalDateTime(), MESSAGE_UPDATE_STATE); //再次发送请求 sendRet(mSocket, baos); //...继续循环,重新接收 continue; } //判断一帧图片baos数据是否接收完成 if(packet.getLength() == endlen) { String end = new String(packet.getData(), 0, endlen); if(end.startsWith(PACKET_END)) { //...获取time时间数据,下一步更新显示 updateViewDisplay(baos, time); //设置接收下一帧等待时长,至少每100ms接收下一帧,可以设置更小,让视频看着更流畅 Thread.sleep(100); baos.flush(); //再次发送请求 sendRet(mSocket, baos); //... showToast("接收中..."+getLocalDateTime(), MESSAGE_UPDATE_STATE); continue; } } //接收一帧图片数据流 baos.write(packet.getData(), 0, packet.getLength()); } } } catch (Exception e) { errMsg = e.getMessage();//断开连接!; } finally { //... cancelThread(errMsg); } } }); thread.start(); } private void sendRet(DatagramSocket dSocket, ByteArrayOutputStream baos) throws IOException, Exception { //省略...处理发送接收下一帧图片请求 InetAddress address = InetAddress.getByName(remote.getIp()); DatagramPacket pack = new DatagramPacket(data, data.length, address, FIND_CAMERA_PORT); dSocket.send(pack); //... } private void updateViewDisplay(final ByteArrayOutputStream baos, final String time) { //省略...处理转换图片 context.runOnUiThread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub //...锁定中,从组件中获取画布Canvas Canvas canvas = holder.lockCanvas(null); //...将图片画组件中,让用户可以看到 canvas.drawBitmap(bitmap2, 0, 0, null); //...画上时间 canvas.drawText(time, 20, 30, p); //解除锁定 holder.unlockCanvasAndPost(canvas); //... } }); } public void cancelReceive() { cancelThread(); } }
-
接下来,做一个开启摄像头页面的布局,文件是activity_preview.xml,大致布局如下图所示,是运行后的效果图,同上面讲过,跟远程摄像头页面布局那个是一样的,现在是有多放了一个选择摄像头的下拉框组件
Spinner
-
接着,创建一个对应的页面类PreviewActivity.class文件,写上代码,参考如下
public class PreviewActivity extends BaseBackActivity { private Camera camera = null; private SurfaceHolder holder; private Spinner seletep; private int selectCameraId = 0; //定义发送图片的线程 private SenderThread thread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_preview); //... final DeviceInfo info = (DeviceInfo) getSerializable(); //...获取布局中的组件,SurfaceView是绘制组件 final SurfaceView view = findViewById(R.id.surfaceView); seletep = findViewById(R.id.spinner); final TextView stateView = findViewById(R.id.textView_state); //创建一个发送图片的线程,传入设备信息对象 thread = new SenderThread(this, info, new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); //...处理线程发来的消息 } }); String localIp = thread.getLocalIp(); String name = thread.getDeviceName(); setTitle("设备名:"+name+ ", 局域网IP:"+localIp); seletep.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { //...切换摄像头 } }); //先获取焦点 view.setFocusable(true); //然后设置屏幕长亮 view.setKeepScreenOn(true); holder = view.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { //绘制组件创建,准备摄像头 int cameraCount = Camera.getNumberOfCameras(); //... thread.setCameraCount(cameraCount); } @Override public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) { //绘制组件大小改变,重置摄像头 //... openCamera(); } @Override public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) { thread.cancelThread(); //绘制组件销毁,释放摄像头资源 closeCamera(); } }); } @Override protected void onPostResume() { super.onPostResume(); //可被局域网内发现摄像头 thread.canFind(true); } private void openCamera() { //... try{ camera = Camera.open(selectCameraId); Camera.Parameters params = camera.getParameters(); List<Camera.Size> sizes = params.getSupportedPictureSizes(); //图像大小 final int PICTURE_WIDTH = 320, PICTURE_HEIGHT = 240; Camera.Size size = null; //省略细节...查找摄像头配置参数,赋值图像大小 params.setPreviewSize(size.width, size.height); params.setPreviewFrameRate(20); params.setPictureFormat(PixelFormat.YCbCr_420_SP); camera.setParameters(params); //讲摄像头的图像设置到绘制组件中 camera.setPreviewDisplay(holder); camera.setPreviewCallback(new Camera.PreviewCallback(){ @Override public void onPreviewFrame(byte[] bytes, Camera camera) { //...省略细节...处理摄像头传来的图片,将bytes转换成image,当然可以不转换,直接发送更高效吧 //交给线程去发送 thread.setSendCameraImage(image); } }); camera.startPreview(); } catch (Exception e) { showToast(this, "开启摄像头遇到了错误!"); } } void closeCamera() { //... camera.stopPreview(); camera.setPreviewCallback(null); camera.release(); camera = null; } }
- 看上一步就会发现,关键的处理发送方法都放在线程SenderThread.class里,那也是比较耗时的操作,大致讲一下
public class SenderThread extends BaseThread { private int cameraCount = 0; public boolean isSending = false; private DeviceInfo info; private YuvImage image = null; public SenderThread(Activity context, DeviceInfo info, Handler handler){ super(context, handler); this.info = info; } //... public void setCameraCount(int cameraCount) { this.cameraCount = cameraCount; } public void canFind(boolean isFind) { if(isFind==true && thread==null) { this.thread = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub String errMsg = "未知错误"; try { mSocket = new DatagramSocket(null); //...绑定开放的30000端口 mSocket.bind(new InetSocketAddress(FIND_CAMERA_PORT)); do { //... DatagramPacket pack = new DatagramPacket(new byte[1028], 1028); try { //接收数据,等待中,会阻塞 mSocket.receive(pack); }catch (Exception e){ e.printStackTrace(); throw e; } //获取发来请求的设备地址 SocketAddress sendAddress = pack.getSocketAddress(); //... ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(pack.getData())); Integer code = (Integer) ois.readObject(); //... switch (code){ case GET_CAMERA_IP: { //...判断请求1,将摄像头的数据包装成data,封装在报文中回发过去 DatagramPacket packet = new DatagramPacket(data, data.length, sendAddress); mSocket.send(packet); } break; case RET_CAMERA_IP: { isSending = true; //...判断请求2,处理一帧图片回发过去 sendImage(image, sendAddress, sendTime); isSending = false; } break; default: } }while (!thread.isInterrupted() && mSocket!=null); } catch (Exception e) { errMsg = e.getMessage(); } finally { cancelThread(errMsg); } } }); this.thread.start(); }else{ cancelThread(); } } private void sendImage(YuvImage image, SocketAddress sendAddress, String sendTime) throws Exception { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); //将图片转换成数据流,压缩了图片就变小,减少传输量 image.compressToJpeg(new Rect(0,0, image.getWidth(), image.getHeight()), 80, outStream); //...定义缓存大小,转换图片数据流 byte[] buffer = new byte[1024]; ByteArrayInputStream bais = new ByteArrayInputStream(outStream.toByteArray()); try{ int len; DatagramPacket pack; //...读取图片数据流,并拆分几次分发出去 while((len = bais.read(buffer, 0, buffer.length)) != -1) { pack = new DatagramPacket(buffer, len, sendAddress); mSocket.send(pack); } //分发完成后,最后发一个结束信息,告诉接收方这一帧图片已发完 byte[] end = (PACKET_END+sendTime).getBytes(); pack = new DatagramPacket(end, end.length, sendAddress); mSocket.send(pack); }catch (Exception e){ e.printStackTrace(); }finally { bais.close(); } } public void setSendCameraImage(YuvImage image) { if (isSending()) { return; } this.image = image; } public boolean isSending() { return isSending; } }
- 接下来,剩下的完善细节不再是重点就不讲了,有个清晰的思路就好,需要自己完善一下细节,到最后能将Android项目顺利编译运行起来,接下来,做个实验测试几遍,准备一个WIFI路由器,用一个网线连接上电脑上(或者WIFI连接也可以),只要能登录路由器的控制页面,找到如下图所示,看看是否已取消勾选
开启AP隔离
,再点保存就可以了,取消AP隔离这样能让局域网的各种设备可互相连通,不需要连接到互联网
💡 小提示
- 为了安全起见,路由器中不建议对访客开放的WIFI网络中禁用AP隔离哦,
- 有些路由器中有访客WIFI开关,这个是没有AP隔离可禁用的
- 没有WIFI路由器的话,可用其中的一个手机开启WIFI热点功能代替,然后安装上面开发的APP,点击开启摄像头按钮就可以了,其它的手机都能扫描到这个摄像头的
-
局域网在线监控设备扫描工具V1.0软件说明
2021-10-26 10:52:19一个场所的监控摄像头一般都处在同一个局域网内,当在调试和维护监控设备时,如果不知道监控摄像头的IP 地址,可以使用本工具扫描局域网内监控设备IP 地址和MAC 地址,方便简单。还可以将扫描结果导出到excel文件。 ...软件简介:
一个场所的监控摄像头一般都处在同一个局域网内,当在调试和维护监控设备时,如果不知道监控摄像头的IP 地址,可以使用本工具扫描局域网内监控设备IP 地址和MAC 地址,方便简单。还可以将扫描结果导出到excel文件。优酷视频演示:
https://v.youku.com/v_show/id_XNTgxNTUwNzcwOA==.html软件下载衔接:
1.百度网盘链接:https://pan.baidu.com/s/1-ogPtA1ISu58g–oDh9q8g
提取码:bqyz2.阿里云盘链接:https://www.aliyundrive.com/s/bo3mmzFLysM
软件工具主界面:
关注公众号,获取更多资料
-
网络摄像头局域网查找工具
2019-01-16 15:24:06通用的无线摄像头调试小工具,把摄像头连接到局域网内再使用 -
Android通过Onvif协议发现并获取局域网内摄像头播放地址
2019-04-01 15:35:45Android通过Onvif协议发现局域网内摄像头, 获取局域网内摄像头的播放地址 -
易语言局域网连接监控源码-易语言
2021-06-13 05:50:16易语言局域网连接监控源码 -
Android 网络摄像头实现
2018-03-20 17:08:06Android 网络摄像头实现,基于Socket通讯的局域网监控摄像头 -
从局域网监控到广域网实时监控的实现
2020-12-21 11:14:57局域网视频监控2. 与云服务器远程通信3. 云服务器中转实现远程监控 1. 局域网视频监控 socket 实现 测试可在同一台电脑运行 条件:处在同一局域网,并知道被监控端的 ip 观看监控视频时同步保存视频到本地 ... -
甜甜圈IP摄像头局域网监控系统(局域网监控软件)2017v1.0.1免费绿色版
2019-08-07 00:52:54甜甜圈IP摄像头局域网监控系统(局域网监控软件)是专门用来监控电脑操作行为的一款客户端应用。这款系统主要用于甜甜圈局域网电脑端,可以远程监控聊天、视频播放以及软件安装使用情况。喜欢的朋友快来下载吧! ... -
局域网怎么查看单位摄像头_简单易用,夜里看的更清楚,360新品水滴摄像头夜视版实测...
2020-11-24 09:36:18现在的社会,家里装个,甚至说装几个摄像头是必须的。无论是看小孩、看老人、看XX,没有个东西可以远程查看实时状况,回看历史视频,还真是不方便。所以,虽然家里和老人家有了几个摄像头,最近还是入手了一个小型的...现在的社会,家里装个,甚至说装几个摄像头是必须的。无论是看小孩、看老人、看XX,没有个东西可以远程查看实时状况,回看历史视频,还真是不方便。
所以,虽然家里和老人家有了几个摄像头,最近还是入手了一个小型的。
这就是360的水滴摄像头夜视版,水滴摄像头,外观比较小巧可爱,打开盒子,发现外观是和封面图一样的,如同一滴小水滴,镜头上面有出厂的保护贴膜。
配件有供电电源,USB供电线,说明书,3M双面贴。
小水滴摄像机是白色的,通体白色,比较清新、干净、圆形的摄像头上面贴有出厂保护膜,用的时候要撕下来,否则会影响效果。下面自带一个底座,圆形的。
底座背面有橡胶垫,圆圆的一圈,摄像头背面有复位按钮和散热孔,兼具喇叭。
在喇叭的下面有tf卡卡槽的位置,可以插入tf卡进行当地存储。
摄像头的底座是万能可旋转调节的,所以可以360度调节固定角度。
使用前先将保护贴膜撕掉。可看见下面有个小圆洞,这个是受话口,就是听筒啦。
360小水滴摄像头的底座是磁吸的,所以,在家里安装的时候,会有更多的选择,比如就可以这样悬挂在金属支架的下方,获得更好的视角。
当然,正放也是没有问题的了。
360摄像头的使用和初始化设置,自然是需要安装app来进行了。360的app可以设置进入密码,这样避免别人用你的手机使用app,查看隐私画面。进去之后,选择右上角的+号,就可以添加设备了。
添加设备只需要选择类型,比如这次就是小水滴,然后按照提示,进行就可以了。360的设置安装非常人性化,摄像头和手机都会有语音提示,提示相应的动作,即使不是懂技术的人,也可以方便的添加。
添加之后就会在app里显示,当有多个摄像头的时候,是竖排的,只需滚动即可。当希望看的摄像头出现时,停留1秒就会自动开始实时播放画面。(马赛克是我后期加上的,不是画面画质不好)
点击画面会进入大画面模式,这里可以看到下方有几个按钮,可以录像、拍照、选择打开或者关闭摄像机的喇叭,中间的蓝色电话标记,按动后,就可以对摄像头讲话,让远程的摄像头发出声音,可以和家里人实时通话。
点击右侧的箭头,画面上移,下方会出现历史的活动信息,就是画面内有物体移动,就会自动记录下来,方便查看。这个就是下方蓝色的看家界面,而点击右侧的操作,可以看到有回看、设置、清晰度、邀请家人,甚至还有儿歌,就是播放儿歌的功能,自然是给小孩听了。
给小孩听?也许有的人会说,怎么会把小孩独自留在家里?这个当然是你在家的时候啦,比如婴儿,自己在婴儿车里休息,那么家长可以做其他的事情,如果小孩闹,可以放个儿歌听。
这里的我的看家,是360摄像机常用到的功能,就是当监测画面发生变化的时候,自动的开始录像。会有画面的列表,可以选择需要观看的画面。侦测的时间和区域、灵敏度都可以设置。
当然,除了这个还有回看的,回看同侦测不同,回看是连续的录像。回看可以有本地的存储卡(前面看过的,有卡槽),也可以同时选择购买云存储。本地存储和云存储各有利弊,也可以同时选择,看自己的需要吧。在回看画面,可以看到连续的时长,其中有红色竖条的,就是有看家事件发生的时段。
所以说,这个摄像头,白天晚上都可以检测到,可以有移动事件触发记录,方便快速回看。还有回看记录,可以看连续的视频,家庭使用很是方便。当然,还有很多其他的设置。比如墙面我提到,摄像头可以正着,也可以反着。上面的画面,大家看是倒着的,这是因为我原来是挂着,后来改为立着,忘记在设置里改下了。
在设置里,可以选择摄像头是正着放还是倒着悬挂,可以设置摄像头发出声音的音量,可以设置是不是采集声音,还有红外夜视的设置,可以设置为自动,也可以手动设置。还可以设置状态指示灯是不是开启,这个指示灯是什么呢?就是下面这个。
在小水滴摄像机上面有个指示灯,蓝色的,你可以设置是不是让它开启,有个灯会提醒人们这里有监控,是不是开启,就看你自己的选择了。
除了这个,还有其他的一些设置,比如看网络状态,譬如家人相册等等的高级功能。还具有人在家自动关闭摄像头的功能,就是可以设置当你的手机和摄像头在同一个局域网的时候,就自动关闭摄像头。
总的来说,这款小水滴简单易用,设置方便,适合在家里看家,看小孩,看宠物,可以正放,可以挂着,具有磁吸功能,还有其他的一些特点,适合在家里的各种不同场合使用。
-
C# udp远程监控摄像头工具.rar
2020-04-10 12:05:43项目需要到网上想下载一个UDP远程通信,客户端实时上报摄像头信息到服务端。服务端实现远程监控客户端摄像头的功能。 -
网眼局域网监控软件/网眼电脑监控软件.zip
2019-07-14 04:21:04网眼局域网监控软件/网眼电脑监控软件是一款专业好用的局域网、电脑监控软件。软件可对员工电脑的屏幕、上网、聊天、邮件和公司的机密资料操作进行全面的监控,从而提高企业生产效率,减少不必要的资源浪费,同时还... -
掌控局域网监控软件
2015-09-01 14:35:363.远程开启电脑摄像头:可远程开启摄像头,上下左右旋转摄像头进行监控(带云台摄像头),还可进行语音视频交流和文字沟通,可对视频进行录像。 4.远程文件管理:上传、下载文件,远程修改、复制、粘帖、运行文件,实现... -
易语言ip摄像头源码-易语言
2021-06-13 15:08:31易语言ip摄像头源码 -
12月首发,QT开源项目(局域网电脑摄像头)-opencv+tcp+PIL
2021-12-01 16:03:40QT开源项目(局域网电脑摄像头识别二维码)-opencv+tcp+PIL+二维码前言一、客户端功能介绍程序二、使用步骤1.引入库2.读入数据总结 前言 本开源项目分为客户端和服务端,客户端安装到局域网内带摄像头的电脑上,... -
【Android】WIFI局域网内用手机摄像头当视频监控用APP项目源码
2022-04-10 00:01:04这是一个使用Android Studio开发App项目源码,WIFI局域网内两部手机实现通信,一个手机当摄像头,另一部手机当视频监控的App项目源码,配套的相关教程文章请看... -
超级眼局域网监控软件
2015-11-17 15:32:52查看的员工,单击“用户信息”可以查看该员工电脑的信息,包括该电脑使用者的员工姓名、IP地址、上线时间(可用作考勤)、操作系统、是否有摄像头等。该记录会将员工对电脑上的文件操作详细记录下来,包括员工电脑... -
局域网屏幕监控软件_掌控局域网监控软件(zklan)
2020-10-22 17:12:30掌控局域网监控软件是一款可以帮助用户监控同一局域网的软件。它只能控制同一局域网内的电脑(包括企业专网及VPN等虚拟局域网)包括内网、企业专网或VPN等虚拟局域网,并支持不同网段间电脑的控制。要想使用它的话,先... -
局域网监控软件,用在局域网的监控工具非常好用
2011-09-02 09:48:33局域网屏幕监控简介: 1、本软件是为了网管员方便管理电脑之需而写,请别作其它非法用途。因使用此软件而产生的法律责任,本人一概不负! 2、本软件不是黑软,不跨网段不穿墙,使用时请注意防火墙设置。 3、不运行被... -
Mac系统使用终端检测局域网内开放端口的设备ip,扫描本地网络上的监控摄像头
2021-12-13 14:41:48但是摄像头获取到的摄像头地址时什么呢?可以通过网络扫描来实现。如果是用mac系统,mac系统都自带python,使用下面脚本即可 #!/usr/bin/python3 # -*- coding: utf-8 -*- from socket import * def portScanner... -
局域网摄像头安装及调试手册_picakinq_v1
2019-01-09 15:31:28局域网摄像头安装及调试手册 包含更新软件源、MJPG-streamer +普通usb摄像头 、实现在局域网内部网页浏览监控视频 -
超级眼局域网监控软件V8.30免费版
2018-06-02 13:52:58超级眼局域网监控软件是一款企业专用的局域网监控软件,本局域网监控软件为内网版,实时屏幕监控,多屏幕监控,摄像头监控,多摄像头监控,历史记录,远程管理,行为管理,程序运行记录,聊天行为管控,文件操作记录... -
监控集中管理软件
2015-09-18 10:45:10这款是监控集中管理软件,CMS,用于配置模拟,网络摄像头。支持多用户互联。 -
海康威视监控摄像头大华摄像头webrtc监控低时延无插件直播页面播放毫无延迟
2021-10-22 14:43:18需要低延时的视频流监控播放,之前可以用rtmp的低延时播放(1秒左右),随着浏览器对rtmp的禁用,无插件的低延时播放怎么解决呢? webrtc的视频流播放来了,局域网测试,延时可以控制在(200ms-500ms) 2、WebRTC... -
本地局域网web端集成大华摄像头视频信号开发ocx_1.zip
2020-03-25 11:26:51在局域网内(无外网条件下),支持web端集成大华相机、摄像头信号进行开发,不需要通过互联网或乐橙云。 -
海康、大华等网络摄像头RTSP_Onvif网页无插件直播流媒体服务器EasyNVR在内网环境下,设备不在线问题处理
2019-10-25 11:16:46随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看、可控的诉求越来越多,互联网思维、架构和技术引入进传统监控行业里,成为新形势下全终端监控的基础需求。 因使用区域受限... -
web 页面接入局域网监控视频
2021-08-19 19:56:423. 摄像头和推流器接入同个局域网; 4. 使用VLC播放器播放验证 摄像头或录像机输出的 rtsp 视频源,海康常见地址格式见文末; 5. 使用 推流器的局域网扫描工具 找到其 ip 地址并修改为同一网段, 不同设备可能不同; 6...