精华内容
下载资源
问答
  • 利用手机做监控摄像头
    2019-09-30 09:37:49

    GB28181流媒体服务

    具体介绍这边不多说,参考 https://www.liveqing.com/docs/products/LiveGBS.html

    国标设备语音对讲

    支持语音对讲的设备,可以直接接入LiveGBS,这样就可以从控制中心和您关注的设备间,进行语音对讲

    配置语音输出通道编码

    看到有 语音输出通道编码 表示支持语音对讲功能
    在这里插入图片描述

    按住说话

    在这里插入图片描述

    更多相关内容
  • 现有的智能手机是可以充当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远方在此谢过~

    在这里插入图片描述

    展开全文
  • 起序:新手触碰 Android开发 领域,有望大佬指点。 源码 - Gitee 源码 - Github 加注:不管是进入 Gitee or...新手机的快速迭代更新使得一部分人对最新版手机有更高的使用期望,这就造成了这部分人原来使用的手机会..
    • 起序:新手触碰 Android开发 领域,有望大佬指点。

      源码 - Gitee
      源码 - Github

    • 加注:不管是进入 Gitee or Github 中,给孩子点个 star 呗。

    一、为什么想做这个项目?

    这也许是一个比较令人深省的问题,可能是突发奇想,一时兴起,也可能是锻炼自己在 Android开发 领域的能力。虽然这不是一个特别新颖的想法,但在 资源回收利用 方面还是有那么一点能力体现的。新手机的快速迭代更新使得一部分人对最新版手机有更高的使用期望,这就造成了这部分人原来使用的手机会被 搁浅 ,而这些手机 最终的去处也各式各样 ,可能被 尘封、低价卖出、当板砖。以上纯属个人 YY,下面进入正题。

    二、简介

    项目主要二次利用 Android 系统比较旧版本的手机,把旧手机的摄像头当作 监控摄像头,利用视频聊天的方式,在旧手机上安装一个 APP (AndMon) 录取视频,在其他手机上安装一个 APP (Monitor) 接收旧手机的视频流,这个功能是在 Monitor 通过 扫一扫二维码 的形式进而实现监控的目的。在 AndMon 上同时存在着 录制视频 的功能,录制的视频可以上传到 阿里云服务器上 ,然而可以在 Monitor 上的 媒体库 选项中查看录制好的视频。

    三、框架

    项目难免会用到第三方框架实现一些 自己觉得看似牛逼 但常用的功能,虽然不是自己编写的,但读懂第三方框架简单实现的代码也是一种能力,对不对。

    • SMSSDK : 实现了手机号获取验证码
    • zxing : 实现了二维码的扫描
    • agora : 实现了新手机查看旧手机的视频监控

    注:这三个框架功能强大,我只是利用了部分功能,还有其他功能需要发掘使用,在这里不细说。

    四、架构图解

    新手绘制架构图,如有 架构师大佬指点 ,感激涕零。

    在这里插入图片描述

    五、页面展示

    只截取了静态页面,功能界面 需要录制视频,偷个懒,如有兴趣,下方有联系方式。

    在这里插入图片描述

    展开全文
  • 旧安卓手机改为门禁探头,或用作监控摄像头可行性资料分析,仅供创意灵感参考!其一、手机直接对准猫眼就是将手机直接贴在门上,摄像头对准猫眼(可能镜头会模糊,猫眼需改为广角,该方案还未测试),手机打开摄像头...

    旧安卓手机改为门禁探头,或用作监控摄像头可行性资料分析,仅供创意灵感参考!

    其一、手机直接对准猫眼

    e2fd3b18c573787c78fb7a424e5afae3.png
    5ba0699a20675eefb37a62a96e088dcb.png

    就是将手机直接贴在门上,摄像头对准猫眼(可能镜头会模糊,猫眼需改为广角,该方案还未测试),手机打开摄像头应用,关屏即可,如有来人敲门可解锁手机直接进入摄像头画面;或者将手机电源按键引线接到门铃按键上,有人按门铃手机就自动点亮。

    手机供电:可就近取楼道对讲机上电源,如电压不符,用调压模块改变。

    同时,该手机里面安装一个《IP摄像头》的APP,可实现无线摄像头功能,其它在用的手机也安装上就可以实现无线摄像头的功能了,在里屋也能看是谁来了,并且还可以录像。

    其二、用USB摄像头

    2e0982c8cc8161440aabad35e405c6aa.png

    前提:手机支持OTG功能、OTG线、USB摄像头、下载安装《USB摄像头》APP

    将USB摄像头对准或想法塞入猫眼,延长线视具体情况可选长度不等,手机放与楼道对讲机旁边。

    同样,手机打开《USB摄像头》应用,关屏即可,如有来人敲门可解锁手机直接进入摄像头画面;或者将手机电源按键引线接到门铃按键上,有人按门铃手机就自动点亮。

    再简单一点,直接购买一个智能摄像头,目前小米官网有一款99元的华来小方智能摄像机 1S,或者129元的小米米家智能摄像机,再或者直接购买智能门铃。

    本文的宗旨是想旧物创意利用起来,所以尽量考虑用旧手机改造。

    展开全文
  • 很多处于观望中的人群对监控摄像机感到困惑,因为很多摄像头都在宣传无线WiFi传输,WiFi远程监控,监控摄像头是否只有在有WiFi环境才能使用呢? 其实不然,WiFi只是一种信号或者信息素传输的媒介。WiFi网络摄像机并...
  • 今天教大家2个小技巧,用家中闲置的安卓手机或者电脑摄像头,DIY搭建小型视频监控 先在手机上安装一款远程控制软件向日葵,登录同一帐号; 然后在手机主控端【设备】列表下,找到需要被控的另一台手机,然后选择...
  • 利用JS调用手机摄像头小功能源码

    千次阅读 2021-04-20 16:34:57
    一个小功能源码,利用JS调用手机摄像头,当访问网址后就能拍摄照片,前提是客户端给了权限。 1、由于系统安全机制,ios系统必须使用自带的Safari浏览器(或者第三方APP调用的是Safari内核)才行 2、安卓端只要该APP...
  • 当前,视频监控应用在各种应用场景下已经大面积应用,并且经过多年日积月累的建设,所采用的厂商设备也是五花八门,比如有海康、大华、宇视、天地伟业等各种品牌的摄像头。 同时,随着各种数字化应用系统的建设,很...
  • wifi无线监控摄像头怎么安装?如果是家庭用的话,室内安装两到三台就足够了。当然,如果只监控一部分,比如客厅,书房,卧室,那么一台就可以了。家用监控的好处就是安装方便,自己通过说明书就可以安装,并不需要找...
  • 信不信由你,还真有不用网线不用电源的家用监控摄像头。速名网络科技研究了一下监控摄像行业市场,发现有好多种监控摄像头是可以实现无需布线无需电源,但是却可以长时间待机监控的。
  • 用OpenCV实现桌面的摄像头程序很简单,把这部分代码集成到一个简单的HTTP server上就可以实现浏览器远程监控。 OpenCV安装 我这里使用了opencv4nodejs: npm i opencv4nodejs 安装的时间会有点长,需要先下载OpenCV...
  • 小型无线摄像头是什么样子的呢?从字面上理解,小型意味着体积小,便携以及简单。无线意味着无需外接线,本身带锂电池或者数据线接充电器供电。摄像头意味着是一体机或者单纯的USB摄像镜头。将这三者合起来,小型...
  • 家庭监控摄像头(系统)安装费用是多少?在我家安装监控摄像头需要多少钱?监控摄像机的安装成本和价格取决于摄像机的数量、类型和位置。在这篇文章中,你将对监控摄像机的安装成本有一个总体印象。
  • 现如今智能手机更新换代太频繁,换下来的旧手机卖二手不值钱,丢弃又觉得可惜,...在这里要推荐一个变废为宝的好办法,就是通过安装一个免费的“警猫眼”的App,把闲置旧手机变成专业网络摄像头,即经济又环保!!!
  • 通过无线网络对监控平台进行移动方向、速度的控制,平台上的安卓手机摄像头实时采集视频图像信号并通过无线WiFi网络将视频信号实时传输到手机端观看。具有控制方便,监控灵活,模块化,可拓展性强等优点,可应用于...
  • 现如今智能手机更新换代太频繁,换下来的旧手机卖二手不值钱,丢弃又觉得可惜,而且产生电子垃圾污染环境。怎么办?...那就赶紧下载“警猫眼”软件,把闲置旧手机变成专业网络摄像头,即经济又环保!!!
  • 使用手机摄像头做网络ip摄像头 准备工作 1、带摄像头的安卓手机一部 2、电脑一台 3、电脑上需要有浏览器 4、下载DroidCamx到手机上: https://pan.baidu.com/s/1cHDqId70fEip0Blthmr_GA 提取码:20qd 下载好apk后,...
  • 其实每一个家庭都缺少一个家用监控器,这不是说笑的。不管你是长期在家还是外出打工,家里布置一台好的小型摄像头是非常有必要的。
  • 将安防行业的摄像头实现互联网直播监控,方便用户随时随地可以在电脑、手机上直接观看、交互是目前的趋势。 安防摄像机,目前基本都能提供RTSP流,但是根据摄像机所处网络不同,可以分为固定IP摄像机头、局域网内...
  • 监控摄像头RTSP低延时无插件直播解决方案第一章 应用简介第二章 方案的实现方式2.1 方案的技术架构2.2 功能模块构成第三章 平台的安装和部署3.1 视频转码工作站的搭建3.2 流媒体服务器搭建3.3 视频节目的WEB发布3.4 ...
  • 监控摄像头参数详细介绍

    万次阅读 2017-04-25 15:56:54
    一、不可小瞧的镜头 镜头是摄像机的眼睛,为了适应不同的监控环境和要求,需要配置不同规格的镜头。比如在室内的重点监视,要进行清晰且大视场角度的图像捕捉,得配置广角镜头;在室外的停车场,既要看到停车场全貌...
  • 一开始对旧手机的第一想法是拆了扔掉,一是本人确实有拆解的欲望,另一方面也是怕手机被不法分子获取到以后加以利用。后来在 QQ 视频时有一个念头冒出来,为什么不利用这些闲置的旧手机 DIY 组建成一套零
  • 同时,利用手机的移动性灵活性作为一个移动的监控台,配上制作的硬件报警系统和外部摄像头,即可实现对远方某一区域的实时安全监控。本作品在安卓原生系统的手机上进行了以上功能的真机测试,试验结果基本达到预期...
  • 利用无线WiFi传输数据,在手机下载APP软件,配对之后,从而实现无线远程监控,实时查看。虽然现在5G基站建设与覆盖城市,但是WiFi摄像机依旧用的是2.4G的WiFi网络,目前还没有开发匹配5GWiFi的无线远程监控摄像机。...
  • 利用WebRTC在一个页面中显示多个网络摄像头,以便在现场直播 应用程式资讯 作者 雷蒙德·西奥多·拉莫斯 版本 1.0.0 指示 怎么跑 在显示PC上,安装依赖项,运行npm install 在显示PC上,使用node index.js运行该...
  • 我们要一件事,首先要知道目标是啥吧,那我们要干啥,在电脑上使用手机摄像头~ 小明:那么问题来了,要怎么调用手机摄像头嘞? 经过我不断的尝试,最后让我找到一个非常厉害的软件,在局域网中可以自由调用手机...
  • 最近再研发一个项目,遇到了用浏览器在外网访问自己安装好的摄像头设备。实时观看视频。别的功能不多要。能够实时观看视频就好。那位大神有现成的SDK,谢谢了。
  • 手机利用 第二弹 ——网络摄像头

    千次阅读 2015-09-11 23:56:39
    手机利用 第二弹 ——网络摄像头 出门在外,想实时查看家里的情况吗?那就把旧手机变成网络摄像头吧~~
  • @H_502_0@在Ubuntu系统基于ROS使用废旧Android手机摄像头搭建监控设备@H_502_0@之前的博文介绍了一些使用android手机获取ROS中传感器,图像以及控制机器人小车的案例;@H_502_0@这里主要介绍如何让ROS获取手机摄像头...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,955
精华内容 3,182
关键字:

利用手机做监控摄像头