精华内容
下载资源
问答
  • 基于onvif规范的视频监控系统客户端(三):自动搜索设备,获得连接在局域网内的摄像头的rtsp地址
    千次阅读
    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);
    }
    完整代码下载地址:
    编译步骤:
    make
    g++ main.c -L这个文件的绝对路径 -LDisC

    更多相关内容
  • 局域网摄像头工具

    2019-04-18 19:03:36
    局域网里面的摄像头资料。改摄像头的信息。方便管理局域网里面的设备。
  • 易语言局域网监控系统源码
  • 项目需要到网上想下载一个UDP远程通信,客户端实时上报摄像头信息到服务端。服务端实现远程监控客户端摄像头的功能。
  • 现有的智能手机是可以充当Wifi摄像头来使用的,这就需要装一个App就能实现了,如果是用别的下载来APP安装用来会不会不放心呢,如果自己有能力,那就可以通过开发Android App项目过程来实现视频监控,有兴趣的来看看...

    闲置在家不用的Android手机有一两个都蒙尘了,想要把它们充分利用起来,可知道,现有的智能手机是可以充当Wifi摄像头来使用的,这就需要装一个App就能实现了,如果是用别的下载来APP安装用来会不会不放心呢,如果自己有能力,那就可以通过开发Android App项目过程来实现视频监控,有兴趣的来看看接下来的实现方案,

    要完成整个过程,至少需要两部手机,一个手机用来充当WIFI摄像头(可以开启WIFI热点),另一个手机当视频监控用的,还是建议用WIFI路由器,就看中它信号强,网络又稳定

    关于能看懂此文章的条件

    1. 会使用Android Studio开发工具
    2. 熟悉Java编程语言,开发过Android App
    3. 对WIFI路由器设置和网络信息收发报文TCPUDP原理有过了解

    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);
                    }
                }
            });
        }
    }
    
    1. 接下来,做一个扫描摄像头页面的布局,文件是activity_scan.xml,大致布局如下图所示,运行后的效果图,就一个ListView展示列表的组件,还有标题栏上的搜索图标,那是扫描按钮
      在这里插入图片描述

    2. 接着,创建一个对应页面的类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;
            }
        }
    }
    
    1. 看上面就会发现,扫描的处理操作是比较耗时的,放在线程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,具体怎么写的,这里不详细讲了,那说下它的作用,它是相当于一个可以复用的类吧,类似模板,可以这样理解,稍微能明白,实现不会复杂

    1. 把需要添加的权限都写上,在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>
    
    1. 接下来,打开视频监控,也就是远程摄像头预览的页面,大致布局如下图所示,文件是activity_remote.xml,放一个展示状态的TextView组件,还有一个预览画面的SurfaceView组件放在中间,宽高分别是固定的320dp,240dp
      在这里插入图片描述

    💡小提示
    有没有注意到,看视频监控上的状态栏,网络保持在23.3K/s每秒,这已经是一帧一帧的传输图像了,图像是320x240分辨率的,传输量会不会低了,可能有点卡吧,跟网络传输延迟有关的

    1. 接着,创建一个对应页面的类,在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();
        }
    }
    
    1. 看上一步就会发现,关键的处理接收方法都放在线程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();
        }
    }
    
    1. 接下来,做一个开启摄像头页面的布局,文件是activity_preview.xml,大致布局如下图所示,是运行后的效果图,同上面讲过,跟远程摄像头页面布局那个是一样的,现在是有多放了一个选择摄像头的下拉框组件Spinner
      在这里插入图片描述

    2. 接着,创建一个对应的页面类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;
        }
    
    }
    
    1. 看上一步就会发现,关键的处理发送方法都放在线程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;
        }
    }
    
    1. 接下来,剩下的完善细节不再是重点就不讲了,有个清晰的思路就好,需要自己完善一下细节,到最后能将Android项目顺利编译运行起来,接下来,做个实验测试几遍,准备一个WIFI路由器,用一个网线连接上电脑上(或者WIFI连接也可以),只要能登录路由器的控制页面,找到如下图所示,看看是否已取消勾选开启AP隔离,再点保存就可以了,取消AP隔离这样能让局域网的各种设备可互相连通,不需要连接到互联网
      在这里插入图片描述

    💡 小提示

    • 为了安全起见,路由器中不建议对访客开放的WIFI网络中禁用AP隔离哦,
    • 有些路由器中有访客WIFI开关,这个是没有AP隔离可禁用的
    • 没有WIFI路由器的话,可用其中的一个手机开启WIFI热点功能代替,然后安装上面开发的APP,点击开启摄像头按钮就可以了,其它的手机都能扫描到这个摄像头的
    1. 不知不觉发现写了很多,就讲到这里了,关于此Android项目源代码就在这里点此查看,在里面可找到,请放心下载,感谢耐心看完,若觉得此文章很有帮助,请点个赞❤再走,TA远方在此谢过~

    在这里插入图片描述

    展开全文
  • 一个场所的监控摄像头一般都处在同一个局域网内,当在调试和维护监控设备时,如果不知道监控摄像头的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
    提取码:bqyz

    2.阿里云盘链接:https://www.aliyundrive.com/s/bo3mmzFLysM

    软件工具主界面:
    在这里插入图片描述
    在这里插入图片描述

    关注公众号,获取更多资料
    在这里插入图片描述

    展开全文
  • 通用的无线摄像头调试小工具,把摄像头连接到局域网内再使用
  • Android通过Onvif协议发现局域网摄像头, 获取局域网摄像头的播放地址
  • 易语言局域网连接监控源码
  • Android 网络摄像头实现,基于Socket通讯的局域网监控摄像头
  • 局域网视频监控2. 与云服务器远程通信3. 云服务器中转实现远程监控     1. 局域网视频监控 socket 实现 测试可在同一台电脑运行 条件:处在同一局域网,并知道被监控端的 ip 观看监控视频时同步保存视频到本地  ...
  • 甜甜圈IP摄像头局域网监控系统(局域网监控软件)是专门用来监控电脑操作行为的一款客户端应用。这款系统主要用于甜甜圈局域网电脑端,可以远程监控聊天、视频播放以及软件安装使用情况。喜欢的朋友快来下载吧! ...
  • 现在的社会,家里装个,甚至说装几个摄像头是必须的。无论是看小孩、看老人、看XX,没有个东西可以远程查看实时状况,回看历史视频,还真是不方便。所以,虽然家里和老人家有了几个摄像头,最近还是入手了一个小型的...

    现在的社会,家里装个,甚至说装几个摄像头是必须的。无论是看小孩、看老人、看XX,没有个东西可以远程查看实时状况,回看历史视频,还真是不方便。

    所以,虽然家里和老人家有了几个摄像头,最近还是入手了一个小型的。

    0ee0782fcd0fd1058009f81c7e88d194.png

    这就是360的水滴摄像头夜视版,水滴摄像头,外观比较小巧可爱,打开盒子,发现外观是和封面图一样的,如同一滴小水滴,镜头上面有出厂的保护贴膜。

    3446328b8c812f19d7b84c4d70f369c0.png

    配件有供电电源,USB供电线,说明书,3M双面贴。

    0b9355ff483cd147bf8d13d6f61fe40a.png

    小水滴摄像机是白色的,通体白色,比较清新、干净、圆形的摄像头上面贴有出厂保护膜,用的时候要撕下来,否则会影响效果。下面自带一个底座,圆形的。

    51b9f0c48ad2cb5e1f00107f1760dae8.png

    底座背面有橡胶垫,圆圆的一圈,摄像头背面有复位按钮和散热孔,兼具喇叭。

    169cb3c6cdb85215e88723267b83de23.png

    在喇叭的下面有tf卡卡槽的位置,可以插入tf卡进行当地存储。

    2b9f606b47e1ad4a16c397c3d6210540.png

    摄像头的底座是万能可旋转调节的,所以可以360度调节固定角度。

    be147e8a4d80ae1f95a02a1b9c960e3c.png

    使用前先将保护贴膜撕掉。可看见下面有个小圆洞,这个是受话口,就是听筒啦。

    09cf73e12af3c5377c3363ab85cdae1e.png

    360小水滴摄像头的底座是磁吸的,所以,在家里安装的时候,会有更多的选择,比如就可以这样悬挂在金属支架的下方,获得更好的视角。

    2ce5fc75639a745f475298024ef1ae43.png

    当然,正放也是没有问题的了。

    283c1b9f5d65f3d14cb01fa472355dda.png

    360摄像头的使用和初始化设置,自然是需要安装app来进行了。360的app可以设置进入密码,这样避免别人用你的手机使用app,查看隐私画面。进去之后,选择右上角的+号,就可以添加设备了。

    3c84cd1417e846ee909e831a48b853f6.png

    添加设备只需要选择类型,比如这次就是小水滴,然后按照提示,进行就可以了。360的设置安装非常人性化,摄像头和手机都会有语音提示,提示相应的动作,即使不是懂技术的人,也可以方便的添加。

    168eac86c97e88d2a56aeeefa8b1c378.png

    添加之后就会在app里显示,当有多个摄像头的时候,是竖排的,只需滚动即可。当希望看的摄像头出现时,停留1秒就会自动开始实时播放画面。(马赛克是我后期加上的,不是画面画质不好)

    8ec17ad1e8a1de1fa2ff82b5e6afbcd9.png

    点击画面会进入大画面模式,这里可以看到下方有几个按钮,可以录像、拍照、选择打开或者关闭摄像机的喇叭,中间的蓝色电话标记,按动后,就可以对摄像头讲话,让远程的摄像头发出声音,可以和家里人实时通话。

    c1729c396b3207d048079d64bc1072e2.png

    点击右侧的箭头,画面上移,下方会出现历史的活动信息,就是画面内有物体移动,就会自动记录下来,方便查看。这个就是下方蓝色的看家界面,而点击右侧的操作,可以看到有回看、设置、清晰度、邀请家人,甚至还有儿歌,就是播放儿歌的功能,自然是给小孩听了。

    给小孩听?也许有的人会说,怎么会把小孩独自留在家里?这个当然是你在家的时候啦,比如婴儿,自己在婴儿车里休息,那么家长可以做其他的事情,如果小孩闹,可以放个儿歌听。

    这里的我的看家,是360摄像机常用到的功能,就是当监测画面发生变化的时候,自动的开始录像。会有画面的列表,可以选择需要观看的画面。侦测的时间和区域、灵敏度都可以设置。

    5a3c8519ce31a852a8da896c977c4965.png

    当然,除了这个还有回看的,回看同侦测不同,回看是连续的录像。回看可以有本地的存储卡(前面看过的,有卡槽),也可以同时选择购买云存储。本地存储和云存储各有利弊,也可以同时选择,看自己的需要吧。在回看画面,可以看到连续的时长,其中有红色竖条的,就是有看家事件发生的时段。

    31c5ec27e6b731743de13ed65d71fb0a.png

    所以说,这个摄像头,白天晚上都可以检测到,可以有移动事件触发记录,方便快速回看。还有回看记录,可以看连续的视频,家庭使用很是方便。当然,还有很多其他的设置。比如墙面我提到,摄像头可以正着,也可以反着。上面的画面,大家看是倒着的,这是因为我原来是挂着,后来改为立着,忘记在设置里改下了。

    2c6087edf43226019fed5b5a1a7eb0ea.png

    在设置里,可以选择摄像头是正着放还是倒着悬挂,可以设置摄像头发出声音的音量,可以设置是不是采集声音,还有红外夜视的设置,可以设置为自动,也可以手动设置。还可以设置状态指示灯是不是开启,这个指示灯是什么呢?就是下面这个。

    9aa3e2ece581b4e67db9a2f36b593d06.png

    在小水滴摄像机上面有个指示灯,蓝色的,你可以设置是不是让它开启,有个灯会提醒人们这里有监控,是不是开启,就看你自己的选择了。

    除了这个,还有其他的一些设置,比如看网络状态,譬如家人相册等等的高级功能。还具有人在家自动关闭摄像头的功能,就是可以设置当你的手机和摄像头在同一个局域网的时候,就自动关闭摄像头。

    7d92b9cc4588a5e2b0e1f2e136e11a44.png

    总的来说,这款小水滴简单易用,设置方便,适合在家里看家,看小孩,看宠物,可以正放,可以挂着,具有磁吸功能,还有其他的一些特点,适合在家里的各种不同场合使用。

    展开全文
  • 项目需要到网上想下载一个UDP远程通信,客户端实时上报摄像头信息到服务端。服务端实现远程监控客户端摄像头的功能。
  • 网眼局域网监控软件/网眼电脑监控软件是一款专业好用的局域网、电脑监控软件。软件可对员工电脑的屏幕、上网、聊天、邮件和公司的机密资料操作进行全面的监控,从而提高企业生产效率,减少不必要的资源浪费,同时还...
  • 掌控局域网监控软件

    2015-09-01 14:35:36
    3.远程开启电脑摄像头:可远程开启摄像头,上下左右旋转摄像头进行监控(带云台摄像头),还可进行语音视频交流和文字沟通,可对视频进行录像。 4.远程文件管理:上传、下载文件,远程修改、复制、粘帖、运行文件,实现...
  • 易语言ip摄像头源码
  • QT开源项目(局域网电脑摄像头识别二维码)-opencv+tcp+PIL+二维码前言一、客户端功能介绍程序二、使用步骤1.引入库2.读入数据总结 前言 本开源项目分为客户端和服务端,客户端安装到局域网内带摄像头的电脑上,...
  • 这是一个使用Android Studio开发App项目源码,WIFI局域网内两部手机实现通信,一个手机当摄像头,另一部手机当视频监控的App项目源码,配套的相关教程文章请看...
  • 查看的员工,单击“用户信息”可以查看该员工电脑的信息,包括该电脑使用者的员工姓名、IP地址、上线时间(可用作考勤)、操作系统、是否有摄像头等。该记录会将员工对电脑上的文件操作详细记录下来,包括员工电脑...
  • 掌控局域网监控软件是一款可以帮助用户监控同一局域网的软件。它只能控制同一局域网内的电脑(包括企业专网及VPN等虚拟局域网)包括内网、企业专网或VPN等虚拟局域网,并支持不同网段间电脑的控制。要想使用它的话,先...
  • 局域网屏幕监控简介: 1、本软件是为了网管员方便管理电脑之需而写,请别作其它非法用途。因使用此软件而产生的法律责任,本人一概不负! 2、本软件不是黑软,不跨网段不穿墙,使用时请注意防火墙设置。 3、不运行被...
  • 但是摄像头获取到的摄像头地址时什么呢?可以通过网络扫描来实现。如果是用mac系统,mac系统都自带python,使用下面脚本即可 #!/usr/bin/python3 # -*- coding: utf-8 -*- from socket import * def portScanner...
  • 局域网摄像头安装及调试手册 包含更新软件源、MJPG-streamer +普通usb摄像头 、实现在局域网内部网页浏览监控视频
  • 超级眼局域网监控软件是一款企业专用的局域网监控软件,本局域网监控软件为内网版,实时屏幕监控,多屏幕监控,摄像头监控,多摄像头监控,历史记录,远程管理,行为管理,程序运行记录,聊天行为管控,文件操作记录...
  • 监控集中管理软件

    2015-09-18 10:45:10
    这款是监控集中管理软件,CMS,用于配置模拟,网络摄像头。支持多用户互联。
  • 需要低延时的视频流监控播放,之前可以用rtmp的低延时播放(1秒左右),随着浏览器对rtmp的禁用,无插件的低延时播放怎么解决呢? webrtc的视频流播放来了,局域网测试,延时可以控制在(200ms-500ms) 2、WebRTC...
  • 局域网内(无外网条件下),支持web端集成大华相机、摄像头信号进行开发,不需要通过互联网或乐橙云。
  • 随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看、可控的诉求越来越多,互联网思维、架构和技术引入进传统监控行业里,成为新形势下全终端监控的基础需求。 因使用区域受限...
  • 3. 摄像头和推流器接入同个局域网; 4. 使用VLC播放器播放验证 摄像头或录像机输出的 rtsp 视频源,海康常见地址格式见文末; 5. 使用 推流器的局域网扫描工具 找到其 ip 地址并修改为同一网段, 不同设备可能不同; 6...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,290
精华内容 1,316
关键字:

局域网监控摄像头

友情链接: CT图像重建.rar