示例参考

  1. 初次配置IPC
  2. 检查SD卡状态
  3. 直播&人脸识别

1. 初次配置IPC

调用IPC的OpenAPI接口首先需要获取到IPC的IP地址,故第一步是把IPC接入网络,IPC连接网络的方式有有线连接和无线连接两种方式。

由于有线网络相对不容易受到环境干扰,稳定性和可靠性较高,故首选是有线方式接入网络。

有线接入:
有线接入只需要通过网线把IPC设备和集成SDK的Android设备接入到同一局域网即可,不需要其它设置。

无线接入:

无线配网方式相对要复杂点,步骤如下:

  1. 使用手机/PC的无线网卡扫描IPC的AP热点,一般AP热点的名称为SUNMI_XXXX,其中XXXX为MAC地址最后2个字节的16进制数字,MAC地址可以通过设备机身后背的标贴或者包装盒的标贴查到,AP热点本身是无加密的。
  2. 使用手机/PC的无线网卡连接IPC的AP热点,此时手机/PC就会获取到IPC分配的IP地址(按照设备发现描述的方法即可获取到),一般会是192.168.200.XXX,手机/PC的网关地址就是IPC的地址,一般会是192.168.200.1。
  3. 调用无线配置 API(见获取无线扫描AP列表(无需签名校验)的描述)获取IPC扫描到的AP热点。
  4. 调用无线配置 API(见设置无线参数(无需签名校验)的描述)设置IPC要连接的无线网络(例如无线路由器的SSID和密码),使得IPC能够从网关处获取到IP地址。
  5. 如果网络是可以正常上网的话,IPC取到IP地址后很快就会亮蓝灯,此时表明IPC可以正常连接Internet了。

SDK初始化&启动设备发现

private void initSdk() {
    ... ...
    String localAddress = "192.168.1.100";
    HttpsUtils.SSLParams sSlParams = getSSLParams();
    IPCameraManager mIPCameraManager = IPCameraManager.getInstance();
    mIPCameraManager.init(APP_ID, SECRET_KEY, LICENSE, sSlParams);
    // 启动设备发现
    mIPCameraManager.startDeviceTracker(localAddress);
    ... ...
}

private static HttpsUtils.SSLParams getSSLParams() {
    try {
        InputStream key = new FileInputStream(KEYSOTRE);
        InputStream ca = new FileInputStream(TRUSTSTORE);
        return HttpsUtils.getSslSocketFactory(key, KEYSOTRE_PASSWORD, ca);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    return null;
}

注册设备发现回调

mIPCameraManager.registerDeviceTrackListener(new DeviceTrackListener() {
    @Override
    public void onDeviceOnline(IPCameraInfo device) {
        showToast(getApplicationContext(), "[ " + device.getDeviceid() + " ]上线");
    }
    @Override
    public void onDeviceOffline(IPCameraInfo device) {
        showToast(getApplicationContext(), "[ " + device.getDeviceid() + " ]离线");
    }
});

扫描局域网内设备

mIPCameraManager.rescan();

扫描可连接的WIFI&设置连接WIFI

// 获取IPC扫描的AP热点
private void getWifiList() {
    BasicConfig.getInstance().getApListWithoutAuth(sunmiDevice.getDeviceid(),
            new RpcCallback‹RpcResponse‹ScanResultCollection››() {
                @Override
                public void onComplete(RpcResponse‹ScanResultCollection› result) {
                    if (result.code() == RPCErrorCode.SUCCESS) {
                        wifiListGetSuccess(result.data());
                    } else {
                        Log.i(TAG, "getApListWithoutAuth failed, errcode: " + result.code());
                    }
                }
        });

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            if (wifiList.size() == 0) {
                setNoWifiVisible(View.VISIBLE);
            }
        }
    }, TIMEOUT_GET_WIFI);
}

// 设置IPC连接的AP热点
private void setIpcWifi(String ssid, String psw) {
    showLoadingDialog();
    BasicConfig.getInstance().setWifiConfWithoutAuth(sunmiDevice.getDeviceid(),
            ssid, psw, new RpcCallback‹RpcResponse›() {
                @Override
                public void onComplete(RpcResponse result) {
                    if (result.code() == RPCErrorCode.SUCCESS) {
                        hideLoadingDialog();
                        shortTip("配置成功,请等待设备联网,联网后指示灯会变成蓝色");
                        createWaitDialog();
                    } else {
                        hideLoadingDialog();
                        shortTip("配置失败");
                    }
                }

                @Override
                public void onError(Throwable t) {
                    hideLoadingDialog();
                    shortTip("配置失败");
                }
            });
}

激活OpenAPI

// 由于激活需要与商米后台通信,因此在激活时确保IPC连接的网络可以连接外网。
DeviceManage.getInstance().activate(ipcList.get(postion).getDeviceid(),
    new RpcCallback‹RpcResponse›() {
        @Override
        public void onComplete(RpcResponse result) {
            if (result.code() == RPCErrorCode.SUCCESS || result.code() == RPCErrorCode.DEVICE_ACTIVATED) {
                Log.i(TAG, "activate ipc success");
            } else {
                Log.i(TAG, "activate ipc failed");
            }
        }

        @Override
        public void onError(Throwable t) {
            Log.i(TAG, "activate ipc failed");
        }
    });

画面缩放&聚焦

// 缩放画面
public void setZoomFactor(int zoomFactor){
    mBasicConfig.setZoom(mDevice.getDeviceid(), zoomFactor, new RpcCallback‹RpcResponse›() {
        @Override
        public void onComplete(RpcResponse result) {
            if (result.code() == RpcErrorCode.SUCCESS) {
                mLensConfig.zoom = zoomFactor;
            }
        }
    });
}

// 缩放后画面会自动聚焦,如果聚焦不够清晰,手动微调。
public void focus(boolean forward) {
        if (forward) {
            mLensConfig.focus = (mLensConfig.focus+FOCUS_STEPPER)›mLensConfig.max_focus?mLensConfig.max_focus:(mLensConfig.focus+FOCUS_STEPPER);
        } else {
            mLensConfig.focus = (mLensConfig.focus-FOCUS_STEPPER)‹0?0:(mLensConfig.focus-FOCUS_STEPPER);
        }
        mBasicConfig.manualFocus(mDevice.getDeviceid(), mLensConfig.focus, new RpcCallback‹RpcResponse›() {
            @Override
            public void onComplete(RpcResponse result) {
                if (result.code() == RpcErrorCode.SUCCESS) {
                    Log.i(TAG, "manual focus failed, error code: " + result.code());
                }
            }
        });
    }

设置拌线

// 设置拌线,拌线主要用于判定人流的方向,比如进入、离开、穿过等方向,需要进行人流统计的需要设置拌线。
public void line(int start_x, int start_y, int end_x, int end_y) {
    PeopleFlowStat.getInstance().setDoorLine(mDevice.getDeviceid(), 0, start_x, start_y, end_x, end_y,
            new RpcCallback‹RpcResponse›() {
                @Override
                public void onComplete(RpcResponse result) {
                    if (result.code() == RpcErrorCode.SUCCESS) {
                        stopLive();
                        mView.gotoHomePage();
                    }
                }
            });
}

2. 检查SD卡状态

IPC的录像、人脸识别和人流统计模块的功能都依赖于SD卡,因此需要在使用时确保SD卡插入并成功格式化。建议在发现设备上线后检查SD卡状态,如果获取状态失败则建议60s后再次获取(因为IPC重启后需要一定时间启动所有服务)。

检查SD卡状态&格式化

private void checkSdcardStatus() {
        mBasicConfig.getMemoryCardStatus(mDevice.getDeviceid(),
                new RPCCallback‹RPCResponse‹ExternalStorageBean››() {
                    @Override
                    public void onComplete(RPCResponse‹ExternalStorageBean› result) {
                        Log.d(TAG, "code: " + result.code() + ", sdcard status: " + result.data().status);
                        if (result.code() == 0 && result.data().status != 2) {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    int message = R.string.ipc_prompt_format_sdcard;
                                    switch (result.data().status) {
                                        case 0:
                                            message = R.string.ipc_prompt_format_sdcard_none;
                                            break;
                                        case 1:
                                            message = R.string.ipc_prompt_format_sdcard;
                                            break;
                                        case 3:
                                            message = R.string.ipc_prompt_format_sdcard_broken;
                                            break;
                                    }
                                    CommonDialog sdcardDialog = new CommonDialog.Builder(context)
                                            .setMessage(message)
                                            .setCancelButton(R.string.ipc_cancel)
                                            .setConfirmButton(R.string.ipc_format_sdcard, new DialogInterface.OnClickListener() {
                                                @Override
                                                public void onClick(DialogInterface dialog, int which) {
                                                    mBasicConfig.formatMemoryCard(mDevice.getDeviceid(),
                                                            new RPCCallback‹RPCResponse›() {
                                                                @Override
                                                                public void onComplete(RPCResponse result) {
                                                                    Log.d(TAG, "code: " + result.code());
                                                                }
                                                            });
                                                }
                                            })
                                            .create();
                                    sdcardDialog.show();
                                }
                            });
                        }
                    }
                });
    }

3. 直播&人脸识别

目前仅FM020支持人脸识别功能。

启动消息通道接收服务

启动此服务后才能接收到来自IPC的人脸识别、动态侦测等消息,启动此服务必须指定IP地址。

private void initSdk() {
    ... ...
    mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT ›= Build.VERSION_CODES.M) {
        if (mConnectivityManager != null) {
            mUsedNetwork = mConnectivityManager.getActiveNetwork();
            if (mUsedNetwork != null) {
                MessageCenter.startMessageReceiver(NetworkUtil.getIpAddress(this, mUsedNetwork), HTTP_SERVER_PORT);
            }
            // 为适配网络切换的情况,动态重启消息接收服务
            mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder().build(),
                    new ConnectivityManager.NetworkCallback() {
                        @Override
                        public void onLost(@NonNull Network network) {
                            super.onLost(network);
                            Log.i(TAG, "onLost");
                            if (network.equals(mUsedNetwork)) {
                                MessageCenter.stopMessageReceiver();
                                mUsedNetwork = null;
                                Network activeNetwork = mConnectivityManager.getActiveNetwork();
                                if (activeNetwork != null) {
                                    Log.i(TAG, "if: " + mConnectivityManager.getLinkProperties(activeNetwork).getInterfaceName());
                                    mUsedNetwork = activeNetwork;
                                    MessageCenter.startMessageReceiver(NetworkUtil.getIpAddress(getApplicationContext(), mUsedNetwork), HTTP_SERVER_PORT);
                                }
                            }
                        }

                        @Override
                        public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
                            super.onCapabilitiesChanged(network, networkCapabilities);
                            Log.i(TAG, "onCapabilitiesChanged: " + networkCapabilities.toString());
                            if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) && mUsedNetwork == null) {
                                mUsedNetwork = network;
                                MessageCenter.startMessageReceiver(NetworkUtil.getIpAddress(getApplicationContext(), mUsedNetwork), HTTP_SERVER_PORT);
                            }
                        }
                    });
        }
    }
    ... ...
}

注册人脸识别回调

private void registerCallback() {
    mFaceDetectListener = new FaceDetectListener() {
        @Override
        public void onFaceDetect(FaceRecognitionEvent event) {
            Bitmap bitmap = null;
            try {
                bitmap = downloadImage(event.pic_url);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(bitmap == null) {
                return;
            }
            Person person = new Person();
            person.setBitmap(bitmap);
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            long tlong = Long.parseLong(event.report_time) * 1000;
            Timestamp ts = new Timestamp(tlong);
            person.setPersonDate(sdf.format(ts));
            person.setPersonID(event.faceid);
            person.setPersonName(event.group_name);
            person.setSex(event.gender);
            person.setPersonAgeRange(event.age_range);
            personList.add(0, person);
            if (personList.size() › personListRecyclerSize) {
                personList.get(personListRecyclerSize).getBitmap().recycle();
                personList.remove(personListRecyclerSize);
            }
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    personListAdapter.notifyDataSetChanged();
                }
            });
        }
    };

    MessageCenter.registerFaceDetectListener(mFaceDetectListener);
}

订阅人脸识别消息

private void subscribeFaceRecognition() {
    List‹String› events = new ArrayList‹›();

    events.add("face_recog_event");
    MessageCenter.getInstance().subscribeEvent(mDevice.getDeviceid(), events, new RpcCallback‹RpcResponse›() {
        @Override
        public void onComplete(RpcResponse result) {
            Log.i(TAG, "subscribeEvent, code:" + result.code());
        }
    });
}

直播

// rtsp直播流的默认账号/密码是admin/admin
private void openMediaPlayer() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            VideoStream.getInstance().getLiveStream(mDevice.getDeviceid(),
                    new RpcCallback‹RpcResponse‹LiveAddress››() {
                        @Override
                        public void onComplete(RpcResponse‹LiveAddress› result) {
                            if (result.code() == RpcErrorCode.SUCCESS) {
                                Log.i(TAG, "live url: " + result.data().fhd_live_url);
                                if (mPlayer == null) {
                                    mPlayer = new SunmiPlayer(context);
                                    mPlayer.setListener(new SunmiPlayerListener() {
                                        @Override
                                        public void onPrepared(IMediaPlayer iMediaPlayer) {
                                            iMediaPlayer.start();
                                        }
                                    });
                                    mPlayer.setSurface(mVideoView.getHolder().getSurface());
                                    String liveUrl = result.data().fhd_live_url.replaceFirst("^rtsp://", "rtsp://admin:admin@");
                                    Log.i(TAG, "real url: " + liveUrl);
                                    mPlayer.setUp(liveUrl);
                                }
                            } else {
                                Log.e(TAG, " IPC Error: " + result.code());
                                showToast(getApplicationContext(), "IPC Error: "+ result.code());
                            }
                        }
            });
        }
    }).start();
}