Commit 21360be7 authored by 张宏's avatar 张宏

接入p2p完成,支持webrtc和p2p来回切换

parent 9895a233
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/zh/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"permission_handler_apple","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.10/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"shared_preferences_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":true},{"name":"url_launcher_ios","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_ios-6.4.1/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"vibration","path":"/Users/zh/.pub-cache/hosted/pub.dev/vibration-3.1.8/","native_build":true,"dependencies":[],"dev_dependency":true}],"android":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_usbcamera","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_usbcamera-0.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/zh/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni-1.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni_flutter","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni_flutter-1.0.1/","native_build":true,"dependencies":["jni"],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_android-2.3.1/","native_build":false,"dependencies":["jni","jni_flutter"],"dev_dependency":false},{"name":"permission_handler_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_android-12.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"shared_preferences_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.23/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"url_launcher_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.30/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"vibration","path":"/Users/zh/.pub-cache/hosted/pub.dev/vibration-3.1.8/","native_build":true,"dependencies":[],"dev_dependency":true}],"macos":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage_macos","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"shared_preferences_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":true},{"name":"url_launcher_macos","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.5/","native_build":true,"dependencies":[],"dev_dependency":true}],"linux":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":false,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni-1.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":true},{"name":"shared_preferences_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":true},{"name":"url_launcher_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":true}],"windows":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":false,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni-1.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"permission_handler_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":true},{"name":"shared_preferences_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":true},{"name":"url_launcher_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":true}],"web":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/zh/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"permission_handler_html","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.3+5/","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","dependencies":["url_launcher_web"],"dev_dependency":true},{"name":"shared_preferences_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":true},{"name":"url_launcher_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.3/","dependencies":[],"dev_dependency":true}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"flutter_usbcamera","dependencies":[]},{"name":"flutter_webrtc","dependencies":["path_provider"]},{"name":"fluttertoast","dependencies":[]},{"name":"jni","dependencies":[]},{"name":"jni_flutter","dependencies":["jni"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":["jni","jni_flutter"]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]},{"name":"vibration","dependencies":[]}],"date_created":"2026-06-18 17:59:11.784192","version":"3.41.9","swift_package_manager_enabled":{"ios":false,"macos":false}} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"flutter_image_compress_common","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_image_compress_common-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/zh/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"permission_handler_apple","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.10/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"shared_preferences_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_ios-6.4.1/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"vibration","path":"/Users/zh/.pub-cache/hosted/pub.dev/vibration-3.1.8/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"vsdk","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/vsdk/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"flutter_image_compress_common","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_image_compress_common-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_usbcamera","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_usbcamera-0.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/zh/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni-1.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni_flutter","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni_flutter-1.0.1/","native_build":true,"dependencies":["jni"],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_android-2.3.1/","native_build":false,"dependencies":["jni","jni_flutter"],"dev_dependency":false},{"name":"permission_handler_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_android-12.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"shared_preferences_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.23/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.30/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"vibration","path":"/Users/zh/.pub-cache/hosted/pub.dev/vibration-3.1.8/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"vsdk","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/vsdk/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"flutter_image_compress_macos","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_image_compress_macos-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"shared_preferences_foundation","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.5/","native_build":true,"dependencies":[],"dev_dependency":true}],"linux":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":false,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni-1.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":true},{"name":"shared_preferences_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":true}],"windows":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","native_build":false,"dependencies":[],"dev_dependency":true},{"name":"flutter_secure_storage_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_webrtc","path":"/Users/zh/Documents/work/zkrq/coding/laki_icu_app/plugins/flutter_webrtc/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"jni","path":"/Users/zh/.pub-cache/hosted/pub.dev/jni-1.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"permission_handler_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":true},{"name":"shared_preferences_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":true}],"web":[{"name":"device_info_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/device_info_plus-12.4.0/","dependencies":[],"dev_dependency":true},{"name":"flutter_image_compress_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_image_compress_web-0.1.5/","dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/zh/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"permission_handler_html","path":"/Users/zh/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.3+5/","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"/Users/zh/.pub-cache/hosted/pub.dev/share_plus-12.0.2/","dependencies":["url_launcher_web"],"dev_dependency":true},{"name":"shared_preferences_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/zh/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.3/","dependencies":[],"dev_dependency":true}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":[]},{"name":"flutter_image_compress","dependencies":["flutter_image_compress_common","flutter_image_compress_web","flutter_image_compress_macos","flutter_image_compress_ohos"]},{"name":"flutter_image_compress_common","dependencies":[]},{"name":"flutter_image_compress_macos","dependencies":[]},{"name":"flutter_image_compress_ohos","dependencies":[]},{"name":"flutter_image_compress_web","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"flutter_usbcamera","dependencies":[]},{"name":"flutter_webrtc","dependencies":["path_provider"]},{"name":"fluttertoast","dependencies":[]},{"name":"jni","dependencies":[]},{"name":"jni_flutter","dependencies":["jni"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":["jni","jni_flutter"]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]},{"name":"vibration","dependencies":[]},{"name":"vsdk","dependencies":["path_provider","flutter_image_compress","shared_preferences"]}],"date_created":"2026-06-20 14:58:12.558882","version":"3.41.9","swift_package_manager_enabled":{"ios":false,"macos":false}}
\ No newline at end of file \ No newline at end of file
org.gradle.java.home=/Library/Java/JavaVirtualMachines/jdk-22.0.1.jdk/Contents/Home
org.gradle.jvmargs=-Xmx4G org.gradle.jvmargs=-Xmx4G
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
/// 视频传输模式
///
/// 涉及页面:首页监护舱 - 监控画面
enum VideoStreamMode {
/// vsdk P2P + AppPlayer 连接模式
p2p,
/// flutter_webrtc HTTP 信令模式
webrtc,
}
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:vsdk/app_player.dart';
import 'package:vsdk/camera_device/camera_device.dart';
import 'package:vsdk/camera_device/commands/video_command.dart';
/// P2P 视频服务连接状态
enum P2pServiceState {
/// 未连接
disconnected,
/// 正在连接(含 P2P 穿透 + 设备登录 + 视频流启动)
connecting,
/// 已连接,视频流播放中
connected,
/// 连接失败
failed,
}
/// P2P 视频服务层
///
/// 职责:
/// - 封装 [CameraDevice] 的 P2P 连接生命周期
/// - 管理 [AppPlayerController] 实例(视频解码 + Texture 渲染)
/// - 向上层暴露连接状态流和播放控制器
///
/// 涉及页面:首页监护舱 - 监控画面
class P2pVideoService {
/// P2P 摄像头设备实例
CameraDevice? _cameraDevice;
/// 视频播放控制器(供 [AppPlayerView] 使用)
/// stop() 只停止播放不 dispose,保持 Texture widget 存活
AppPlayerController? _playerController;
/// 连接状态广播流
final StreamController<P2pServiceState> _stateController =
StreamController<P2pServiceState>.broadcast();
/// 连接状态流(供 UI 层订阅)
Stream<P2pServiceState> get stateStream => _stateController.stream;
P2pServiceState _currentState = P2pServiceState.disconnected;
/// 当前连接状态
P2pServiceState get currentState => _currentState;
/// 获取视频播放控制器
AppPlayerController? get playerController => _playerController;
// ==================== 公共接口 ====================
/// 初始化 P2P 连接并启动视频流
///
/// [deviceId] 设备序列号(与 WebRTC 使用相同的 deviceNo)
/// [username] 设备登录用户名,默认 'admin'
/// [password] 设备登录密码
/// [model] 设备型号,默认 'wifi'
Future<void> connect({
required String deviceId,
String username = 'admin',
required String password,
String model = 'wifi',
}) async {
_updateState(P2pServiceState.connecting);
try {
// 1. 清理上一次 CameraDevice 残留
await _cleanupCamera();
// 2. 创建 CameraDevice 并监听连接状态
_cameraDevice = CameraDevice(deviceId, 'camera', username, password, model);
_cameraDevice!.addListener<CameraConnectChanged>(_onConnectChanged);
// 3. 发起 P2P 连接(内部完成:创建客户端 → P2P 穿透 → 登录)
final result = await _cameraDevice!.connect();
debugPrint('[P2pVideoService] P2P connect 结果: $result');
if (result != CameraConnectState.connected) {
debugPrint('[P2pVideoService] 连接失败: $result');
_updateState(P2pServiceState.failed);
return;
}
// 4. 复用现有播放器或创建新播放器
final bool needCreate = _playerController == null;
if (needCreate) {
_playerController = AppPlayerController();
final created = await _playerController!.create(
audioRate: 8000,
hardware: 1,
);
if (!created) {
debugPrint('[P2pVideoService] 播放器创建失败');
_updateState(P2pServiceState.failed);
return;
}
}
// 5. 获取 clientPtr 并绑定视频源
final clientPtr = await _cameraDevice!.getClientPtr();
if (clientPtr == 0) {
debugPrint('[P2pVideoService] clientPtr 无效: $clientPtr');
_updateState(P2pServiceState.failed);
return;
}
final sourceSet = await _playerController!.setVideoSource(
LiveVideoSource(clientPtr),
);
if (!sourceSet) {
debugPrint('[P2pVideoService] 视频源绑定失败');
_updateState(P2pServiceState.failed);
return;
}
// 6. 开启直播流
final streamStarted = await _cameraDevice!.startStream(
resolution: VideoResolution.general,
);
if (!streamStarted) {
debugPrint('[P2pVideoService] 直播流启动失败');
_updateState(P2pServiceState.failed);
return;
}
// 7. 启动播放
final started = await _playerController!.start();
if (!started) {
debugPrint('[P2pVideoService] 播放器启动失败');
_updateState(P2pServiceState.failed);
return;
}
_updateState(P2pServiceState.connected);
debugPrint('[P2pVideoService] P2P 视频连接建立完成');
} catch (e) {
debugPrint('[P2pVideoService] 连接异常: $e');
_updateState(P2pServiceState.failed);
}
}
/// 停止当前连接(不关闭 StreamController,不销毁播放器)
///
/// 用于模式切换场景:停掉 P2P 连接资源(CameraDevice、直播流),
/// 但保持 AppPlayerController 存活(Texture widget 不会访问已销毁纹理)
/// 且保持 StreamController 存活。
Future<void> stop() async {
debugPrint('[P2pVideoService] 停止连接...');
await _cleanupCamera();
// 播放器保持存活 —— stop() 后 connect() 可复用
_updateState(P2pServiceState.disconnected);
}
/// 释放所有资源(含播放器 + StreamController)
///
/// 调用后此实例不可再复用 [connect]。
Future<void> dispose() async {
debugPrint('[P2pVideoService] 释放资源...');
await _cleanupCamera();
await _cleanupPlayer();
await _stateController.close();
}
// ==================== 内部实现 ====================
/// P2P 连接状态变化回调
void _onConnectChanged(CameraDevice device, CameraConnectState state) {
debugPrint('[P2pVideoService] CameraConnectState 变化: $state');
if (state == CameraConnectState.disconnect ||
state == CameraConnectState.offline) {
if (_currentState == P2pServiceState.connected) {
_updateState(P2pServiceState.failed);
}
}
}
/// 清理 CameraDevice(停流 + 移除监听 + 销毁设备)
Future<void> _cleanupCamera() async {
if (_cameraDevice != null) {
try {
await _cameraDevice!.stopStream();
} catch (_) {}
try {
_cameraDevice!.removeListener<CameraConnectChanged>(_onConnectChanged);
} catch (_) {}
try {
await _cameraDevice!.deviceDestroy();
} catch (_) {}
_cameraDevice = null;
}
}
/// 清理播放器(仅在最终 dispose 时调用)
Future<void> _cleanupPlayer() async {
if (_playerController != null) {
try {
await _playerController!.dispose();
} catch (_) {}
_playerController = null;
}
}
/// 更新连接状态并广播
void _updateState(P2pServiceState state) {
if (_currentState == state) return;
_currentState = state;
if (!_stateController.isClosed) {
_stateController.add(state);
}
}
}
...@@ -37,8 +37,14 @@ class WebrtcService { ...@@ -37,8 +37,14 @@ class WebrtcService {
final WebrtcRepository _repository; final WebrtcRepository _repository;
/// 视频渲染器(供 RTCVideoView 使用) /// 视频渲染器(供 RTCVideoView 使用)
/// 生命周期:[renderer] 在首次 connect 时 initialize,
/// stop() 只清空 srcObject 不 dispose,保持 PlatformView 存活;
/// 仅在 [dispose] 最终销毁。
final RTCVideoRenderer renderer = RTCVideoRenderer(); final RTCVideoRenderer renderer = RTCVideoRenderer();
/// 渲染器是否已完成首次初始化
bool _rendererInitialized = false;
/// P2P 连接实例 /// P2P 连接实例
RTCPeerConnection? _peerConnection; RTCPeerConnection? _peerConnection;
...@@ -123,11 +129,13 @@ class WebrtcService { ...@@ -123,11 +129,13 @@ class WebrtcService {
} }
} }
/// 释放所有资源 /// 停止当前连接(不关闭 StreamController,不销毁 Renderer)
/// ///
/// 必须在使用完毕后调用,否则会导致内存泄漏和摄像头资源占用。 /// 用于模式切换场景:停掉 WebRTC 连接资源(PeerConnection、DataChannel、
Future<void> dispose() async { /// 媒体轨道),但保持 StreamController 存活(后续 connect 可继续广播状态)
debugPrint('[WebrtcService] 释放资源...'); /// 且保持 Renderer 存活(RTCVideoView 的 PlatformView 不会访问已销毁纹理)。
Future<void> stop() async {
debugPrint('[WebrtcService] 停止连接...');
_heartbeatTimer?.cancel(); _heartbeatTimer?.cancel();
_heartbeatTimer = null; _heartbeatTimer = null;
...@@ -135,18 +143,27 @@ class WebrtcService { ...@@ -135,18 +143,27 @@ class WebrtcService {
await _dataChannel?.close(); await _dataChannel?.close();
_dataChannel = null; _dataChannel = null;
// 停止所有音视频轨道 // 停止所有音视频轨道,并清空 srcObject 使 RTCVideoView 显示黑屏
renderer.srcObject?.getAudioTracks().forEach((t) => t.stop()); renderer.srcObject?.getAudioTracks().forEach((t) => t.stop());
renderer.srcObject?.getVideoTracks().forEach((t) => t.stop()); renderer.srcObject?.getVideoTracks().forEach((t) => t.stop());
renderer.srcObject = null;
await _peerConnection?.close(); await _peerConnection?.close();
await _peerConnection?.dispose(); await _peerConnection?.dispose();
_peerConnection = null; _peerConnection = null;
await renderer.dispose(); _updateState(WebrtcConnectionState.disconnected);
}
/// 释放所有资源(含 Renderer + StreamController)
///
/// 调用后此实例不可再复用 [connect]。
Future<void> dispose() async {
debugPrint('[WebrtcService] 释放资源...');
await stop();
await renderer.dispose();
_rendererInitialized = false;
await _stateController.close(); await _stateController.close();
_updateState(WebrtcConnectionState.disconnected);
} }
// ==================== 内部实现 ==================== // ==================== 内部实现 ====================
...@@ -163,8 +180,11 @@ class WebrtcService { ...@@ -163,8 +180,11 @@ class WebrtcService {
// 和后续 onConnectionState 迟到触发重连 // 和后续 onConnectionState 迟到触发重连
await _cleanupConnection(); await _cleanupConnection();
// 初始化视频渲染器 // 仅首次需要初始化渲染器;stop() 后 renderer 保持存活无需重新初始化
if (!_rendererInitialized) {
await renderer.initialize(); await renderer.initialize();
_rendererInitialized = true;
}
// 创建 P2P 连接 // 创建 P2P 连接
_peerConnection = await createPeerConnection(_iceServers); _peerConnection = await createPeerConnection(_iceServers);
...@@ -238,9 +258,6 @@ class WebrtcService { ...@@ -238,9 +258,6 @@ class WebrtcService {
); );
// ========== 竞态防护 ========== // ========== 竞态防护 ==========
// 在 HTTP 请求期间可能触发了重连(_onReconnect → _initWebrtc),
// 此时 _peerConnection 已经是新连接,当前调用必须放弃,
// 否则会在新连接上错误地 setRemoteDescription → native SIGSEGV
if (currentGen != _connectionGen) { if (currentGen != _connectionGen) {
debugPrint( debugPrint(
'[WebrtcService] 代际不匹配 (current=$currentGen, latest=$_connectionGen),' '[WebrtcService] 代际不匹配 (current=$currentGen, latest=$_connectionGen),'
......
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/services/monitoring_service.dart'; import 'package:laki_icu_app/services/monitoring_service.dart';
import 'package:laki_icu_app/services/p2p_video_service.dart';
import 'package:laki_icu_app/services/webrtc_service.dart'; import 'package:laki_icu_app/services/webrtc_service.dart';
import 'package:laki_icu_app/utils/constants.dart';
import 'home_index_state.dart'; import 'home_index_state.dart';
...@@ -15,14 +17,22 @@ const _mockDevicePassword = 'MOCK_WIFI_PWD'; ...@@ -15,14 +17,22 @@ const _mockDevicePassword = 'MOCK_WIFI_PWD';
class HomeIndexCubit extends Cubit<HomeIndexState> { class HomeIndexCubit extends Cubit<HomeIndexState> {
final MonitoringService _monitoringService; final MonitoringService _monitoringService;
final WebrtcService _webrtcService; final WebrtcService _webrtcService;
final P2pVideoService _p2pVideoService;
StreamSubscription<WebrtcConnectionState>? _webrtcStateSub; StreamSubscription<WebrtcConnectionState>? _webrtcStateSub;
StreamSubscription<P2pServiceState>? _p2pStateSub;
/// 连接代际计数器 —— 每次切换模式时 +1,
/// 防止旧连接的异步结果污染当前模式的状态。
int _switchGen = 0;
HomeIndexCubit() HomeIndexCubit()
: _monitoringService = MonitoringService(), : _monitoringService = MonitoringService(),
_webrtcService = WebrtcService(), _webrtcService = WebrtcService(),
_p2pVideoService = P2pVideoService(),
super(const HomeIndexState()) { super(const HomeIndexState()) {
_listenWebrtcState(); _listenWebrtcState();
_listenP2pState();
loadData(); loadData();
} }
...@@ -34,6 +44,16 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -34,6 +44,16 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
}); });
} }
/// 监听 P2P 视频服务状态变化并同步到 State
void _listenP2pState() {
_p2pStateSub = _p2pVideoService.stateStream.listen((state) {
if (isClosed) return;
emit(this.state.copyWith(
isP2pConnected: state == P2pServiceState.connected,
));
});
}
Future<void> loadData() async { Future<void> loadData() async {
emit(state.copyWith(isLoading: true, error: null)); emit(state.copyWith(isLoading: true, error: null));
try { try {
...@@ -56,8 +76,8 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -56,8 +76,8 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
error: null, error: null,
)); ));
// 数据加载完成后初始化 WebRTC 视频连接 // 数据加载完成后,按当前模式初始化视频连接
_initWebrtc(); _initVideoByMode();
} catch (e) { } catch (e) {
if (isClosed) return; if (isClosed) return;
emit(state.copyWith( emit(state.copyWith(
...@@ -68,13 +88,17 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -68,13 +88,17 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
} }
} }
/// 初始化 WebRTC 视频连接 /// 依据当前 [VideoStreamMode] 启动对应的视频连接
Future<void> _initWebrtc() async { void _initVideoByMode() {
// if (Constants.useMockData) { if (state.videoStreamMode == VideoStreamMode.p2p) {
// // Mock 模式:跳过真实连接,保持断开状态 _initP2p();
// return; } else {
// } _initWebrtc();
}
}
/// 初始化 WebRTC 视频连接(已有逻辑保持不变)
Future<void> _initWebrtc() async {
await _webrtcService.connect( await _webrtcService.connect(
// deviceNo: _mockDeviceNo, // deviceNo: _mockDeviceNo,
// password: _mockDevicePassword, // password: _mockDevicePassword,
...@@ -83,11 +107,74 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -83,11 +107,74 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
); );
} }
/// 手动重试视频连接 /// 手动重试 WebRTC
Future<void> retryWebrtc() async { Future<void> retryWebrtc() async {
await _initWebrtc(); await _initWebrtc();
} }
/// 初始化 P2P 视频连接
Future<void> _initP2p() async {
await _p2pVideoService.connect(
deviceId: 'VE10085871QOXG',
username: 'admin',
password: '143548',
);
}
// ==================== 模式切换 ====================
/// 切换视频传输模式
///
/// 先停止当前模式 → 更新状态 → 启动新模式。
/// 使用 [_switchGen] 代际计数器防止旧连接的异步结果污染当前状态。
Future<void> switchStreamMode(VideoStreamMode mode) async {
if (state.isSwitchingMode) return;
if (state.videoStreamMode == mode) return;
final currentGen = ++_switchGen;
debugPrint(
'[HomeIndexCubit] 切换模式: ${state.videoStreamMode}$mode (gen=$currentGen)');
// 1. 标记切换中
emit(state.copyWith(isSwitchingMode: true));
// 2. 停止当前模式(用 stop 停连接,保留 StreamController 可复用)
if (state.videoStreamMode == VideoStreamMode.p2p) {
await _p2pVideoService.stop();
} else {
await _webrtcService.stop();
}
// 3. 竞态检查:如果在清理期间又触发了一次切换则放弃本次
if (currentGen != _switchGen || isClosed) return;
// 4. 切换到新模式
emit(state.copyWith(
videoStreamMode: mode,
isSwitchingMode: false,
isP2pConnected: false,
videoConnectionState: WebrtcConnectionState.disconnected,
));
// 5. 启动新模式视频连接
if (mode == VideoStreamMode.p2p) {
await _initP2p();
} else {
await _initWebrtc();
}
}
/// 手动重试当前模式的视频连接
Future<void> retryCurrentMode() async {
if (state.videoStreamMode == VideoStreamMode.p2p) {
await _p2pVideoService.stop();
await _initP2p();
} else {
await _webrtcService.stop();
await _initWebrtc();
}
}
Future<void> refreshData() async { Future<void> refreshData() async {
try { try {
final metrics = await _monitoringService.getMetrics(); final metrics = await _monitoringService.getMetrics();
...@@ -116,13 +203,18 @@ class HomeIndexCubit extends Cubit<HomeIndexState> { ...@@ -116,13 +203,18 @@ class HomeIndexCubit extends Cubit<HomeIndexState> {
} }
} }
/// 获取渲染器供 UI 层使用 /// 获取 WebRTC 渲染器供 UI 层使用
WebrtcService get webrtcService => _webrtcService; WebrtcService get webrtcService => _webrtcService;
/// 获取 P2P 视频播放控制器供 UI 层使用
P2pVideoService get p2pVideoService => _p2pVideoService;
@override @override
Future<void> close() { Future<void> close() {
_webrtcStateSub?.cancel(); _webrtcStateSub?.cancel();
_p2pStateSub?.cancel();
_webrtcService.dispose(); _webrtcService.dispose();
_p2pVideoService.dispose();
return super.close(); return super.close();
} }
} }
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/services/webrtc_service.dart'; import 'package:laki_icu_app/services/webrtc_service.dart';
...@@ -21,6 +22,15 @@ class HomeIndexState extends Equatable { ...@@ -21,6 +22,15 @@ class HomeIndexState extends Equatable {
/// WebRTC 视频连接状态 /// WebRTC 视频连接状态
final WebrtcConnectionState videoConnectionState; final WebrtcConnectionState videoConnectionState;
/// 当前视频传输模式
final VideoStreamMode videoStreamMode;
/// 是否正在切换传输模式(用于 UI loading 状态)
final bool isSwitchingMode;
/// P2P 模式是否已连接
final bool isP2pConnected;
const HomeIndexState({ const HomeIndexState({
this.status = HomeIndexStatus.initial, this.status = HomeIndexStatus.initial,
this.isLoading = false, this.isLoading = false,
...@@ -30,6 +40,9 @@ class HomeIndexState extends Equatable { ...@@ -30,6 +40,9 @@ class HomeIndexState extends Equatable {
this.alerts = const [], this.alerts = const [],
this.menuItems = const [], this.menuItems = const [],
this.videoConnectionState = WebrtcConnectionState.disconnected, this.videoConnectionState = WebrtcConnectionState.disconnected,
this.videoStreamMode = VideoStreamMode.webrtc,
this.isSwitchingMode = false,
this.isP2pConnected = false,
}); });
HomeIndexState copyWith({ HomeIndexState copyWith({
...@@ -41,6 +54,9 @@ class HomeIndexState extends Equatable { ...@@ -41,6 +54,9 @@ class HomeIndexState extends Equatable {
List<AlertInfoBO>? alerts, List<AlertInfoBO>? alerts,
List<MonitoringMenuItemBO>? menuItems, List<MonitoringMenuItemBO>? menuItems,
WebrtcConnectionState? videoConnectionState, WebrtcConnectionState? videoConnectionState,
VideoStreamMode? videoStreamMode,
bool? isSwitchingMode,
bool? isP2pConnected,
}) { }) {
return HomeIndexState( return HomeIndexState(
status: status ?? this.status, status: status ?? this.status,
...@@ -52,6 +68,9 @@ class HomeIndexState extends Equatable { ...@@ -52,6 +68,9 @@ class HomeIndexState extends Equatable {
menuItems: menuItems ?? this.menuItems, menuItems: menuItems ?? this.menuItems,
videoConnectionState: videoConnectionState:
videoConnectionState ?? this.videoConnectionState, videoConnectionState ?? this.videoConnectionState,
videoStreamMode: videoStreamMode ?? this.videoStreamMode,
isSwitchingMode: isSwitchingMode ?? this.isSwitchingMode,
isP2pConnected: isP2pConnected ?? this.isP2pConnected,
); );
} }
...@@ -65,5 +84,8 @@ class HomeIndexState extends Equatable { ...@@ -65,5 +84,8 @@ class HomeIndexState extends Equatable {
alerts, alerts,
menuItems, menuItems,
videoConnectionState, videoConnectionState,
videoStreamMode,
isSwitchingMode,
isP2pConnected,
]; ];
} }
...@@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; ...@@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart'; import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'cubit/home_index_cubit.dart'; import 'cubit/home_index_cubit.dart';
...@@ -10,7 +11,7 @@ import 'widgets/alert_info_card.dart'; ...@@ -10,7 +11,7 @@ import 'widgets/alert_info_card.dart';
import 'widgets/metric_card.dart'; import 'widgets/metric_card.dart';
import 'widgets/patient_info_card.dart'; import 'widgets/patient_info_card.dart';
import 'widgets/sidebar_menu_item.dart'; import 'widgets/sidebar_menu_item.dart';
import 'widgets/webrtc_video_widget.dart'; import 'widgets/video_player_widget.dart';
@RoutePage() @RoutePage()
class HomeIndexView extends StatelessWidget { class HomeIndexView extends StatelessWidget {
...@@ -175,10 +176,17 @@ class _HomeIndexContentState extends State<HomeIndexContent> { ...@@ -175,10 +176,17 @@ class _HomeIndexContentState extends State<HomeIndexContent> {
// 监控画面模块(2/3宽度、100%高度) // 监控画面模块(2/3宽度、100%高度)
Expanded( Expanded(
flex: 2, flex: 2,
child: WebrtcVideoWidget( child: VideoPlayerWidget(
renderer: _homeIndexCubit.webrtcService.renderer, streamMode: state.videoStreamMode,
connectionState: state.videoConnectionState, isSwitchingMode: state.isSwitchingMode,
onRetry: () => _homeIndexCubit.retryWebrtc(), webrtcRenderer:
_homeIndexCubit.webrtcService.renderer,
webrtcConnectionState: state.videoConnectionState,
p2pController:
_homeIndexCubit.p2pVideoService.playerController,
isP2pConnected: state.isP2pConnected,
onRetry: () => _homeIndexCubit.retryCurrentMode(),
onToggleMode: _onToggleStreamMode,
), ),
), ),
SizedBox(width: 24.w), SizedBox(width: 24.w),
...@@ -237,6 +245,16 @@ class _HomeIndexContentState extends State<HomeIndexContent> { ...@@ -237,6 +245,16 @@ class _HomeIndexContentState extends State<HomeIndexContent> {
); );
} }
/// 模式切换回调
void _onToggleStreamMode() {
final cubit = _homeIndexCubit;
final currentMode = cubit.state.videoStreamMode;
final nextMode = currentMode == VideoStreamMode.p2p
? VideoStreamMode.webrtc
: VideoStreamMode.p2p;
cubit.switchStreamMode(nextMode);
}
Widget _buildEmptyCard(String text) { Widget _buildEmptyCard(String text) {
return Container( return Container(
padding: EdgeInsets.all(24.w), padding: EdgeInsets.all(24.w),
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:vsdk/app_player.dart';
/// WebRTC 视频全屏播放页面 import '../../../../enums/video_stream_mode_enum.dart';
/// 通用视频全屏播放页面(兼容 WebRTC + P2P)
///
/// 全屏展示摄像头实时画面,点击任意位置退出全屏。
/// ///
/// 全屏展示远程摄像头实时画面,点击任意位置退出全屏。 /// 涉及页面:首页监护舱 - 监控画面
class WebrtcFullscreenPage extends StatelessWidget { class VideoFullscreenPage extends StatelessWidget {
final RTCVideoRenderer renderer; /// 当前视频传输模式
final VideoStreamMode mode;
/// WebRTC 模式下的视频渲染器
final RTCVideoRenderer? webrtcRenderer;
const WebrtcFullscreenPage({super.key, required this.renderer}); /// P2P 模式下的播放控制器
final AppPlayerController? p2pController;
const VideoFullscreenPage({
super.key,
required this.mode,
this.webrtcRenderer,
this.p2pController,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -18,15 +35,33 @@ class WebrtcFullscreenPage extends StatelessWidget { ...@@ -18,15 +35,33 @@ class WebrtcFullscreenPage extends StatelessWidget {
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
// 全屏视频画面 // 根据模式渲染不同的视频画面
Center( _buildVideoContent(),
child: RTCVideoView( // 顶部提示栏
renderer, _buildTopBar(),
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, ],
), ),
), ),
// 顶部提示栏 );
Positioned( }
/// 按模式渲染视频内容
Widget _buildVideoContent() {
if (mode == VideoStreamMode.p2p && p2pController != null) {
return AppPlayerView(controller: p2pController!);
}
if (mode == VideoStreamMode.webrtc && webrtcRenderer != null) {
return RTCVideoView(
webrtcRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
);
}
return const SizedBox.shrink();
}
/// 顶部提示栏
Widget _buildTopBar() {
return Positioned(
top: 0, top: 0,
left: 0, left: 0,
right: 0, right: 0,
...@@ -50,10 +85,6 @@ class WebrtcFullscreenPage extends StatelessWidget { ...@@ -50,10 +85,6 @@ class WebrtcFullscreenPage extends StatelessWidget {
], ],
), ),
), ),
),
],
),
),
); );
} }
} }
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:vsdk/app_player.dart';
import '../../../../enums/video_stream_mode_enum.dart';
import '../../../../services/webrtc_service.dart';
import 'video_fullscreen_page.dart';
import 'video_stream_switch.dart';
/// 通用实时视频播放组件(兼容 WebRTC + P2P 双模式)
///
/// 职责:
/// - WebRTC 模式:通过 [RTCVideoView] 渲染远程摄像头实时画面
/// - P2P 模式:通过 [AppPlayerView] 渲染 Texture 视频画面
/// - 依据当前模式的连接状态显示对应的状态指示器
/// - 右下角提供模式切换按钮(P2P/RTC)和全屏按钮
///
/// 涉及页面:首页监护舱 - 监控画面
class VideoPlayerWidget extends StatelessWidget {
/// 当前视频传输模式
final VideoStreamMode streamMode;
/// 是否正在切换模式
final bool isSwitchingMode;
/// WebRTC 视频渲染器(WebRTC 模式时使用)
final RTCVideoRenderer? webrtcRenderer;
/// WebRTC 连接状态
final WebrtcConnectionState webrtcConnectionState;
/// P2P 视频播放控制器(P2P 模式时使用)
final AppPlayerController? p2pController;
/// P2P 模式是否已连接
final bool isP2pConnected;
/// 连接失败时点击重试回调
final VoidCallback? onRetry;
/// 模式切换回调
final VoidCallback? onToggleMode;
const VideoPlayerWidget({
super.key,
required this.streamMode,
this.isSwitchingMode = false,
this.webrtcRenderer,
required this.webrtcConnectionState,
this.p2pController,
this.isP2pConnected = false,
this.onRetry,
this.onToggleMode,
});
/// 当前模式视频是否正在显示
bool get _isVideoShowing {
if (streamMode == VideoStreamMode.webrtc) {
return webrtcConnectionState == WebrtcConnectionState.connected;
}
return isP2pConnected && p2pController != null;
}
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(20.r),
child: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xCC63A0FF), Color(0xCC0024C4)],
),
border: Border.all(color: Colors.white24, width: 1),
),
child: Stack(
fit: StackFit.expand,
children: [
// 视频画面层
_buildVideoLayer(),
// 状态指示层
_buildStatusOverlay(),
// 模式切换按钮(右下角倒数第二个位置)
if (onToggleMode != null && !isSwitchingMode)
_buildToggleModeButton(),
// 全屏按钮(右下角最后一个位置)
if (_isVideoShowing) _buildFullscreenButton(context),
],
),
),
);
}
// ==================== 视频画面层 ====================
/// 视频画面
Widget _buildVideoLayer() {
if (streamMode == VideoStreamMode.webrtc) {
if (webrtcConnectionState == WebrtcConnectionState.connected &&
webrtcRenderer != null) {
return RTCVideoView(
webrtcRenderer!,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
);
}
return _buildPlaceholder();
}
// P2P 模式
if (isP2pConnected && p2pController != null) {
return AppPlayerView(controller: p2pController!);
}
return _buildPlaceholder();
}
// ==================== 状态遮罩层 ====================
/// 状态遮罩层
Widget _buildStatusOverlay() {
if (isSwitchingMode) {
return _buildSwitchingOverlay();
}
if (streamMode == VideoStreamMode.webrtc) {
switch (webrtcConnectionState) {
case WebrtcConnectionState.connecting:
return _buildLoadingOverlay('正在连接摄像头...');
case WebrtcConnectionState.reconnecting:
return _buildLoadingOverlay('正在重新连接...');
case WebrtcConnectionState.failed:
return _buildErrorOverlay();
default:
return const SizedBox.shrink();
}
}
// P2P 模式:未连接显示加载
if (!isP2pConnected) {
return _buildLoadingOverlay('正在 P2P 连接...');
}
return const SizedBox.shrink();
}
/// 模式切换中遮罩
Widget _buildSwitchingOverlay() {
return Container(
color: Colors.black26,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(color: Colors.white70),
SizedBox(height: 16.h),
Text(
'正在切换传输模式...',
style: TextStyle(fontSize: 18.sp, color: Colors.white70),
),
],
),
),
);
}
/// 加载中遮罩
Widget _buildLoadingOverlay(String message) {
return Container(
color: Colors.black26,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(color: Colors.white70),
SizedBox(height: 16.h),
Text(
message,
style: TextStyle(fontSize: 18.sp, color: Colors.white70),
),
],
),
),
);
}
/// 错误遮罩
Widget _buildErrorOverlay() {
return Container(
color: Colors.black38,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48.w, color: Colors.white54),
SizedBox(height: 16.h),
Text(
'视频连接失败',
style: TextStyle(
fontSize: 20.sp,
color: Colors.white70,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 24.h),
ElevatedButton.icon(
onPressed: onRetry,
icon: const Icon(Icons.refresh),
label: const Text('重新连接'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF3A87FF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.r),
),
),
),
],
),
),
);
}
// ==================== 叠加按钮 ====================
/// 全屏按钮(右下角)
Widget _buildFullscreenButton(BuildContext context) {
return Positioned(
right: 12.w,
bottom: 12.h,
child: GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => VideoFullscreenPage(
mode: streamMode,
webrtcRenderer: webrtcRenderer,
p2pController: p2pController,
),
),
);
},
child: Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.black38,
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
Icons.fullscreen,
color: Colors.white,
size: 22.w,
),
),
),
);
}
/// 模式切换按钮(全屏按钮左侧 8px 间距)
Widget _buildToggleModeButton() {
return Positioned(
right: 12.w + 40.w + 8.w,
bottom: 12.h,
child: VideoStreamSwitch(
currentMode: streamMode,
onToggle: onToggleMode!,
),
);
}
/// 未连接时的占位
Widget _buildPlaceholder() {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.videocam_off, size: 48.w, color: Colors.white38),
SizedBox(height: 16.h),
Text(
'暂无视频画面',
style: TextStyle(
fontSize: 20.sp,
color: Colors.white54,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../../../enums/video_stream_mode_enum.dart';
/// P2P/WebRTC 模式切换控件
///
/// 显示当前传输模式(P2P / RTC),点击切换至另一种模式。
/// 位置:视频容器右下角,全屏按钮左侧。
///
/// 涉及页面:首页监护舱 - 监控画面
class VideoStreamSwitch extends StatelessWidget {
/// 当前视频传输模式
final VideoStreamMode currentMode;
/// 点击切换回调
final VoidCallback onToggle;
const VideoStreamSwitch({
super.key,
required this.currentMode,
required this.onToggle,
});
@override
Widget build(BuildContext context) {
final isP2p = currentMode == VideoStreamMode.p2p;
return GestureDetector(
onTap: onToggle,
child: Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: isP2p
? const Color(0xFF3A87FF).withValues(alpha: 0.6)
: Colors.black38,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(
color: isP2p ? Colors.white54 : Colors.white24,
width: 1,
),
),
child: Center(
child: Text(
isP2p ? 'P2P' : 'RTC',
style: TextStyle(
fontSize: 10.sp,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import '../../../../services/webrtc_service.dart';
import 'webrtc_fullscreen_page.dart';
/// WebRTC 实时视频播放组件
///
/// 职责:
/// - 通过 [RTCVideoView] 渲染远程摄像头实时画面
/// - 依据 [connectionState] 显示对应的连接状态指示器
/// - 连接失败或未连接时显示重试占位
///
/// 涉及页面:首页监护舱 - 监控画面
class WebrtcVideoWidget extends StatelessWidget {
/// 视频渲染器(来自 [WebrtcService])
final RTCVideoRenderer renderer;
/// 当前连接状态
final WebrtcConnectionState connectionState;
/// 连接失败时点击重试回调
final VoidCallback? onRetry;
const WebrtcVideoWidget({
super.key,
required this.renderer,
required this.connectionState,
this.onRetry,
});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(20.r),
child: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xCC63A0FF), Color(0xCC0024C4)],
),
border: Border.all(color: Colors.white24, width: 1),
),
child: Stack(
fit: StackFit.expand,
children: [
// 视频画面层
_buildVideoLayer(),
// 状态指示层
_buildStatusOverlay(),
// 全屏按钮(仅连接状态下显示)
if (connectionState == WebrtcConnectionState.connected)
_buildFullscreenButton(context),
],
),
),
);
}
/// 视频画面
Widget _buildVideoLayer() {
if (connectionState == WebrtcConnectionState.connected) {
return RTCVideoView(
renderer,
objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
);
}
// 非连接状态显示占位
return _buildPlaceholder();
}
/// 状态遮罩层
Widget _buildStatusOverlay() {
switch (connectionState) {
case WebrtcConnectionState.connecting:
case WebrtcConnectionState.reconnecting:
return _buildLoadingOverlay();
case WebrtcConnectionState.failed:
return _buildErrorOverlay();
default:
return const SizedBox.shrink();
}
}
/// 加载中遮罩
Widget _buildLoadingOverlay() {
final isReconnecting =
connectionState == WebrtcConnectionState.reconnecting;
return Container(
color: Colors.black26,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(color: Colors.white70),
SizedBox(height: 16.h),
Text(
isReconnecting ? '正在重新连接...' : '正在连接摄像头...',
style: TextStyle(
fontSize: 18.sp,
color: Colors.white70,
),
),
],
),
),
);
}
/// 错误遮罩
Widget _buildErrorOverlay() {
return Container(
color: Colors.black38,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48.w, color: Colors.white54),
SizedBox(height: 16.h),
Text(
'视频连接失败',
style: TextStyle(
fontSize: 20.sp,
color: Colors.white70,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 24.h),
ElevatedButton.icon(
onPressed: onRetry,
icon: const Icon(Icons.refresh),
label: const Text('重新连接'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF3A87FF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.r),
),
),
),
],
),
),
);
}
/// 全屏按钮(右下角)
Widget _buildFullscreenButton(BuildContext context) {
return Positioned(
right: 12.w,
bottom: 12.h,
child: GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => WebrtcFullscreenPage(renderer: renderer),
),
);
},
child: Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
color: Colors.black38,
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
Icons.fullscreen,
color: Colors.white,
size: 22.w,
),
),
),
);
}
/// 未连接时的占位
Widget _buildPlaceholder() {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.videocam_off,
size: 48.w,
color: Colors.white38,
),
SizedBox(height: 16.h),
Text(
'暂无视频画面',
style: TextStyle(
fontSize: 20.sp,
color: Colors.white54,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
}
...@@ -6,6 +6,7 @@ import FlutterMacOS ...@@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation import Foundation
import device_info_plus import device_info_plus
import flutter_image_compress_macos
import flutter_secure_storage_macos import flutter_secure_storage_macos
import flutter_webrtc import flutter_webrtc
import share_plus import share_plus
...@@ -14,6 +15,7 @@ import url_launcher_macos ...@@ -14,6 +15,7 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin")) FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
......
## 0.0.1
低功耗:
AccessKey : 9QDU2DZ3gulyXnze
SecretKey :2LZaRSu4ND5f7wJ5
offline, 离线
deepSleep,深度休眠
sleep,深度休眠
online,在线
poweroff,关机
microPower, //微功耗模式
lowPowerOff, //低电关机
autoWakeup 唤醒设备接口
VELX0000223ENYV 888888
云储存
urls 播放视频地址
APP_BIND_DEV_UNKNOWN_ERR = 0, // 0 未知异常,当出现此错误信息请联系后台
APP_BIND_DEV_SUCCESS = 1, // 1 添加成功
APP_BIND_DEV_REPEAT = 2, // 2 重复添加
APP_BIND_DEV_PARAM_ERR = 3, // 3 参数缺失,调试期间请联系后台
APP_BIND_DEV_TOKEN_ERR = 4, // 4 设备token异常
APP_BIND_DEV_TOKEN_EXPIRE_TIME = 5, // 5 设备token超时
APP_BIND_DEV_AUTHKEY_ERR = 6, // 6 用户authkey异常
APP_BIND_DEV_AUTHKEY_EXPIRE_TIME = 7, // 7 用户authkey过期
CONNECT_NETWORK_FAIL = 1000, // 1000 连接网络失败
DEV_VUID_NULL = 1001, // VUID为空
GET_UID_BY_VUID_FAIL = 1002, // 获取UID服务器失败
AUTH_SERVER_FAIL = 1003, // 授权失败
REGISTER_SERVER_FAIL = 1004, // 注册服务器失败
DEV_UNKNOWN_ERROR = 1005, // 设备未知异常
DEV_UNKNOWN_ERROR = 1007, // 服务器受限
DEV_SIM_UNKNOWN_ERROR = 1008, // 非法卡
DEV_REPORT_FIRST_STAGE_TIMEOUT = 2001, // 设备上报第一步超时
DEV_REPORT_SECOND_STAGE_TIMEOUT = 2002, // 设备上报第二步超时
DEV_REPORT_THIRD_STAGE_TIMEOUT = 2003, // 设备上报第三步超时
更新内容
#1.0.1 (20231215)
更新支持二目,三目
var supportMutilSensorStream = RxInt(0);
/// 1 2 是双, 3是三目
直播 tf 和云存储 同步
2目效果 :img2.png
3目效果 :img3.png
20250926
支持16K 库的更新
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
group 'com.example.vsdk'
version '1.0'
buildscript {
ext.kotlin_version = '1.5.31'
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
flatDir {
dirs project(':vsdk').file('libs')
}
}
}
apply plugin: 'com.android.library'
android {
namespace 'com.example.vsdk'
compileSdkVersion 31
defaultConfig {
minSdkVersion 24
ndk {
// 设置支持的SO库架构
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
consumerProguardFiles 'consumer-rules.pro'
}
lintOptions {
disable 'InvalidPackage'
}
packagingOptions {
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
exclude 'META-INF/com.android.tools/proguard/coroutines.pro'
jniLibs {
useLegacyPackaging true
}
//
}
configurations.all {
resolutionStrategy {
force 'androidx.core:core:1.17.0'
}
}
}
dependencies {
implementation(name: 'vp_log-5.0.0', ext: 'aar')
implementation(name: 'app_p2p_api-5.1.0', ext: 'aar')
implementation(name: 'app_player-5.1.0', ext: 'aar')
// implementation(name: 'app_p2p_api-release', ext: 'aar')
// implementation(name: 'app_player-release', ext: 'aar')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
-dontwarn android.support.**
-keep class android.support.** { *; }
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-ignorewarnings
-dontskipnonpubliclibraryclassmembers
\ No newline at end of file
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
rootProject.name = 'vsdk'
include ':vp_log'
include ':vp_p2p'
include ':vp_play'
\ No newline at end of file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.vsdk">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
</manifest>
package com.example.vsdk;
import android.content.Context;
import androidx.annotation.NonNull;
import com.veepai.app_player.AppPlayerPlugin;
import com.vstarcam.app_p2p_api.AppP2PApiPlugin;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
/*import com.tencent.bugly.crashreport.CrashReport;*/
/** VsdkPlugin */
public class VsdkPlugin implements FlutterPlugin, MethodCallHandler {
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
/* CrashReport.initCrashReport(mContext, "weChatAppId", false);
CrashReport.putUserData(mContext, "BuglyVersion", CrashReport.getBuglyVersion(mContext));*/
AppP2PApiPlugin plugin = new AppP2PApiPlugin();
plugin.onAttachedToEngine(flutterPluginBinding);
AppPlayerPlugin playerPlugin = new AppPlayerPlugin();
playerPlugin.onAttachedToEngine(flutterPluginBinding);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
}
}
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh
\ No newline at end of file
#import <Flutter/Flutter.h>
@interface AppP2PApiPlugin : NSObject<FlutterPlugin>
@end
#import <Flutter/Flutter.h>
@interface AppPlayerPlugin : NSObject<FlutterPlugin>
@end
#import <Flutter/Flutter.h>
@interface VsdkPlugin : NSObject<FlutterPlugin>
@end
#import "VsdkPlugin.h"
#import "AppP2PApiPlugin.h"
#import "AppPlayerPlugin.h"
@implementation VsdkPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[AppP2PApiPlugin registerWithRegistrar:registrar];
[AppPlayerPlugin registerWithRegistrar:registrar];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}
@end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint vsdk.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'vsdk'
s.version = '0.0.1'
s.summary = 'A new Flutter project.'
s.description = <<-DESC
A new Flutter project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'emzail@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/VsdkPlugin.h'
s.dependency 'Flutter'
s.platform = :ios, '8.0'
s.vendored_libraries = 'SDK/*.a'
s.libraries = 'z', 'c++','iconv','bz2','m'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
end
import 'dart:ffi';
import 'dart:isolate';
import 'app_player.dart';
class VpDartExecuteArgsT extends Struct {
external Pointer<NativeFunction<Void Function(Pointer<Void>)>> function;
external Pointer<Void> args;
external Pointer<Void> syncLock;
}
typedef ProgressListener = void Function(dynamic data);
typedef GPSListener = void Function(dynamic data);
typedef VideoHeadListener = void Function(dynamic data);
typedef DrawListener = void Function(dynamic data);
class AppDart {
static final AppDart _instance = AppDart._internal();
factory AppDart() => _instance;
int Function(Pointer<NativeFunction<Void Function(Pointer<Void>)>>,
Pointer<VpDartExecuteArgsT>)? _vp_dart_execute;
int Function(Pointer<Void>, int)? _vp_dart_init;
void Function(
Pointer<
NativeFunction<
Void Function(Uint64, Uint32, Uint32, Uint32, Uint32, Uint32,
Uint32, Uint32, Uint64, Uint64)>>)? _app_player_listener;
void Function(
Pointer<
NativeFunction<
Void Function(
Uint64, Uint32, Uint32, Float, Float, Float, Float)>>)?
_app_player_gps_listener;
void Function(
Pointer<
NativeFunction<Void Function(Uint64, Uint32, Uint32, Uint32)>>)?
_app_player_head_listener;
void Function(
Pointer<
NativeFunction<
Void Function(Uint64, Uint32, Uint32, Uint32, Float, Float, Float,
Float)>>)? _app_player_draw_listener;
static void _playerCallback(
int textureId,
int total_duration,
int play_duration,
int cache_progress,
int loading_status,
int velocity,
int focal,
int version,
int timestamp,
int mainFrameCount) {
final listeners = _listenersByTexture[textureId];
if (listeners == null) return;
final data = [
textureId,
total_duration,
play_duration,
cache_progress,
loading_status,
velocity,
focal,
version,
timestamp,
mainFrameCount,
];
for (final listener in listeners) {
listener(data);
}
}
static void _playerGPSCallback(
int textureId,
int fix_status,
int satellites_inview,
double latitude,
double logitude,
double speed,
double coursel) {
final gpsListeners = _gpsListenersByTexture[textureId];
if (gpsListeners == null) return;
final gpsData = [
textureId,
fix_status,
satellites_inview,
latitude,
logitude,
speed,
coursel,
];
for (final listener in gpsListeners) {
listener(gpsData);
}
}
static void _playerDrawCallback(
int textureId,
int width,
int height,
int draw_type,
double percent_x1,
double percent_y1,
double percent_x2,
double percent_y2) {
final drawListeners = _drawListenersByTexture[textureId];
if (drawListeners == null) return;
final drawData = [
textureId,
width,
height,
draw_type,
percent_x1,
percent_y1,
percent_x2,
percent_y2,
];
for (final listener in drawListeners) {
listener(drawData);
}
}
static void _playerVideoHeadCallback(
int textureId, int resolution, int channel, int type) {
final headListeners = _headListenersByTexture[textureId];
if (headListeners == null) return;
final headData = [textureId, resolution, channel, type];
for (final listener in headListeners) {
listener(headData);
}
}
final ReceivePort receivePort = ReceivePort("vp_dart_port");
void _receiveOnData(message) {
if (message is int && _vp_dart_execute != null) {
final args = Pointer<VpDartExecuteArgsT>.fromAddress(message);
_vp_dart_execute!(args.ref.function, args);
}
}
AppDart._internal() {
_vp_dart_execute = AppPlayerController.playerLib
.lookup<
NativeFunction<
Int32 Function(
Pointer<NativeFunction<Void Function(Pointer<Void>)>>,
Pointer<VpDartExecuteArgsT>)>>("vp_dart_execute")
.asFunction();
_vp_dart_init = AppPlayerController.playerLib
.lookup<NativeFunction<Int32 Function(Pointer<Void>, Int64)>>(
"vp_dart_init")
.asFunction();
_app_player_listener = AppPlayerController.playerLib
.lookup<
NativeFunction<
Void Function(
Pointer<
NativeFunction<
Void Function(
Uint64,
Uint32,
Uint32,
Uint32,
Uint32,
Uint32,
Uint32,
Uint32,
Uint64,
Uint64)>>)>>("app_player_listener")
.asFunction();
_app_player_gps_listener = AppPlayerController.playerLib
.lookup<
NativeFunction<
Void Function(
Pointer<
NativeFunction<
Void Function(Uint64, Uint32, Uint32, Float, Float,
Float, Float)>>)>>("app_player_gps_listener")
.asFunction();
_app_player_head_listener = AppPlayerController.playerLib
.lookup<
NativeFunction<
Void Function(
Pointer<
NativeFunction<
Void Function(Uint64, Uint32, Uint32,
Uint32)>>)>>("app_player_head_listener")
.asFunction();
_app_player_draw_listener = AppPlayerController.playerLib
.lookup<
NativeFunction<
Void Function(
Pointer<
NativeFunction<
Void Function(
Uint64,
Uint32,
Uint32,
Uint32,
Float,
Float,
Float,
Float)>>)>>("app_player_draw_listener")
.asFunction();
receivePort.listen(_receiveOnData, onDone: () {
print("vp_dart_port done");
}, onError: (error) {
print("vp_dart_port error:$error");
});
print(
"data:${NativeApi.initializeApiDLData} nativePort:${receivePort.sendPort.nativePort}");
_vp_dart_init!(
NativeApi.initializeApiDLData, receivePort.sendPort.nativePort);
_app_player_listener!(Pointer.fromFunction(_playerCallback));
_app_player_gps_listener!(Pointer.fromFunction(_playerGPSCallback));
_app_player_head_listener!(Pointer.fromFunction(_playerVideoHeadCallback));
_app_player_draw_listener!(Pointer.fromFunction(_playerDrawCallback));
}
static final Map<int, List<ProgressListener>> _listenersByTexture = {};
void addListener(int textureId, ProgressListener listener) {
_listenersByTexture.putIfAbsent(textureId, () => []).add(listener);
}
void rmvListener(int textureId, ProgressListener listener) {
_listenersByTexture[textureId]?.remove(listener);
if (_listenersByTexture[textureId]?.isEmpty ?? false) {
_listenersByTexture.remove(textureId);
}
}
static final Map<int, List<GPSListener>> _gpsListenersByTexture = {};
void addGPSListener(int textureId, GPSListener listener) {
_gpsListenersByTexture.putIfAbsent(textureId, () => []).add(listener);
}
void rmvGPSListener(int textureId, GPSListener listener) {
_gpsListenersByTexture[textureId]?.remove(listener);
if (_gpsListenersByTexture[textureId]?.isEmpty ?? false) {
_gpsListenersByTexture.remove(textureId);
}
}
static final Map<int, List<VideoHeadListener>> _headListenersByTexture = {};
void addVideoHeadListener(int textureId, VideoHeadListener listener) {
_headListenersByTexture.putIfAbsent(textureId, () => []).add(listener);
}
void rmvVideoHeadListener(int textureId, VideoHeadListener listener) {
_headListenersByTexture[textureId]?.remove(listener);
if (_headListenersByTexture[textureId]?.isEmpty ?? false) {
_headListenersByTexture.remove(textureId);
}
}
static final Map<int, List<DrawListener>> _drawListenersByTexture = {};
void addDrawListener(int textureId, DrawListener listener) {
_drawListenersByTexture.putIfAbsent(textureId, () => []).add(listener);
}
void rmvDrawListener(int textureId, DrawListener listener) {
_drawListenersByTexture[textureId]?.remove(listener);
if (_drawListenersByTexture[textureId]?.isEmpty ?? false) {
_drawListenersByTexture.remove(textureId);
}
}
}
/*export 'package:app_devices/support_device/support_mode.dart';
export 'package:app_devices/support_device/support_device_manager.dart';
export 'package:app_devices/device_config.dart';
export 'package:app_devices/device_icons.dart';
export 'package:app_devices/camera_device/camera_device.dart';
export 'package:app_devices/dalie_device/dalie_camera_device.dart';*/
import 'dart:collection';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'dart:async';
enum ClientConnectState {
CONNECT_STATUS_INVALID_CLIENT,
/* client is no create*/
CONNECT_STATUS_CONNECTING,
/* connecting */
CONNECT_STATUS_INITIALING,
/* initialing */
CONNECT_STATUS_ONLINE,
/* on line */
CONNECT_STATUS_CONNECT_FAILED,
/* connect failed */
CONNECT_STATUS_DISCONNECT,
/*connect is off*/
CONNECT_STATUS_INVALID_ID,
/* invalid id */
CONNECT_STATUS_OFFLINE,
/* off line */
CONNECT_STATUS_CONNECT_TIMEOUT,
/* connect timeout */
CONNECT_STATUS_MAX_SESSION,
/* connect max session */
CONNECT_STATUS_MAX,
/*connect is -12*/
CONNECT_STATUS_REMOVE_CLOSE
}
enum ClientConnectMode {
CONNECT_MODE_NONE, //未知模式
CONNECT_MODE_P2P, //穿透模式
CONNECT_MODE_RELAY, //转发模式
CONNECT_MODE_SOCK //转发模式
}
class ClientChannelType {
//指令通道
static ClientChannelType P2P_CMD_CHANNEL = ClientChannelType(0);
//视频通道
static ClientChannelType P2P_VIDEO_CHANNEL = ClientChannelType(1);
//语音接收通道
static ClientChannelType P2P_AUDIO_CHANNEL = ClientChannelType(2);
//语音发送通道
static ClientChannelType P2P_TALKCHANNEL = ClientChannelType(3);
//回放通道
static ClientChannelType P2P_PLAYBACK = ClientChannelType(4);
//报警通道
static ClientChannelType P2P_SENSORALARM = ClientChannelType(5);
//Socket 指令通道
static ClientChannelType SOCK_CMD_CHANNEL = ClientChannelType(0);
// Socket 媒体通道
static ClientChannelType SOCK_MEDIA_CHANNEL = ClientChannelType(1);
// Socket 数据通道
static ClientChannelType SOCK_DATA_CHANNEL = ClientChannelType(2);
ClientChannelType(this.index);
final int index;
}
class ClientCheckBufferResult {
ClientCheckBufferResult(this.result, this.writeLen, this.readLen);
///0 * – P2P_SUCCESSFUL
///-5 * - P2P_INVALID_PARAMETER
///-1 * – P2P_NOT_INITIALIZED
///-11 * – P2P_INVALIED_SESSION_HANDLE
///-12 * – P2P_SESSION_CLOSED_REMOTE
///-13 * – P2P_SESSION_CLOSED_TIMEOUT
int result;
int writeLen;
int readLen;
}
class ClientCheckModeResult {
ClientCheckModeResult(this.result, this.mode);
bool result;
ClientConnectMode mode;
}
class ClientReadResult {
ClientReadResult(this.result, this.buffer);
/// > 0 读取成功,返回值为读取长度
/// -1 客户端未连接
/// -3 接收超时
/// -5 无效参数
/// -11 连接已失效
/// -12 远程关闭连接
/// -13 连接超时关闭
int result;
Uint8List buffer;
}
class ClientCommandResult {
late int cmd;
late Uint8List data;
}
typedef void ConnectListener(ClientConnectState state);
typedef void CommandListener(int cmd, Uint8List data);
class AppP2PApi {
/// 单例
static AppP2PApi? _instance;
/// 将构造函数指向单例
factory AppP2PApi() => getInstance();
///获取单例
static AppP2PApi getInstance() {
//如果单例为空则创建单例
if (_instance == null) {
_instance = new AppP2PApi._internal();
}
return _instance!;
}
bool _isNullOrZero(int value) {
return value == null || value == 0;
}
late MethodChannel _channel;
late EventChannel _connectChannel;
late EventChannel _commandChannel;
late Stream _connectStream;
late Stream _commandStream;
HashMap<int, ConnectListener> _connectListeners = HashMap();
HashMap<int, CommandListener> _commandListeners = HashMap();
AppP2PApi._internal() {
_channel = MethodChannel("app_p2p_api_channel");
_connectChannel = EventChannel("app_p2p_api_event_channel/connect");
_commandChannel = EventChannel("app_p2p_api_event_channel/command");
_connectStream = _connectChannel.receiveBroadcastStream("connect");
_commandStream = _commandChannel.receiveBroadcastStream("command");
_connectStream.listen(_onConnectListener);
_commandStream.listen(_onCommandListener);
}
void _onConnectListener(dynamic data) {
int clientPtr = data[0];
int state = data[1];
var listener = _connectListeners[clientPtr];
if (listener != null) {
listener(ClientConnectState.values[state]);
}
}
void _onCommandListener(dynamic data) {
int clientPtr = data[0];
int cmd = data[1];
Uint8List buffer = data[2];
var list = Uint8List.fromList(buffer.toList());
var listener = _commandListeners[clientPtr];
if (listener != null) {
listener(cmd, list);
}
}
///添加连接状态监听
void setConnectListener(int clientPtr, ConnectListener listener) {
if (_isNullOrZero(clientPtr)) return;
_connectListeners[clientPtr] = listener;
}
///添加设备指令监听
void setCommandListener(int clientPtr, CommandListener listener) {
if (_isNullOrZero(clientPtr)) return;
_commandListeners[clientPtr] = listener;
}
///移除连接状态监听
void removeConnectListener(int clientPtr) {
if (_isNullOrZero(clientPtr)) return;
_connectListeners.remove(clientPtr);
}
///移除设备指令监听
void removeCommandListener(int clientPtr) {
if (_isNullOrZero(clientPtr)) return;
_commandListeners.remove(clientPtr);
}
///创建P2P客户端
///@param [did] 设备ID
///@return 客户端指针
Future<int?> clientCreate(String? did, {String? did2}) async {
if (did == null) return 0;
var result = await _channel.invokeMethod<int>("client_create", [did, did2]);
return result;
}
///修改P2P客户端ID
///@param [did] 设备ID
///@return 客户端指针
Future<bool> clientChangeId(int clientPtr, String did) async {
if (did == null || _isNullOrZero(clientPtr)) return false;
var result =
await _channel.invokeMethod<bool>("client_change_id", [clientPtr, did]);
return result ?? false;
}
///客户端连接
///@param [clientPtr] 客户端指针
///@param [lanScan] 是否进行局域网搜索
///@param [serverParam] 服务器连接参数
///@return 连接状态 @see [ClientConnectState]
Future<ClientConnectState> clientConnect(
int clientPtr, bool lanScan, String serverParam,
{required int connectType, int p2pType = 0}) async {
if (_isNullOrZero(clientPtr))
return ClientConnectState.CONNECT_STATUS_INVALID_CLIENT;
int? result = await _channel.invokeMethod<int>("client_connect",
[clientPtr, lanScan, serverParam, connectType, p2pType]);
return ClientConnectState.values[result ?? 0];
}
///检测客户端超时时间
///@param [clientPtr] 客户端指针
///@param [lanScan] 是否进行局域网搜索
///@param [serverParam] 服务器连接参数
///@return 超时时间
Future<int> clientCheckTimeout(
int clientPtr, bool lanScan, String? serverParam,
{required int connectType}) async {
if (_isNullOrZero(clientPtr)) return 0;
int? result = await _channel.invokeMethod<int>(
"client_connect", [clientPtr, lanScan, serverParam, connectType]);
return result ?? 0;
}
///查看客户端连接模式
///@param [clientPtr] 客户端指针
///@return [ClientCheckModeResult]
Future<ClientCheckModeResult> clientCheckMode(int clientPtr) async {
if (_isNullOrZero(clientPtr))
return ClientCheckModeResult(false, ClientConnectMode.CONNECT_MODE_NONE);
List result = await _channel.invokeMethod("client_check_mode", [clientPtr]);
return ClientCheckModeResult(
result[0], ClientConnectMode.values[result[1]]);
}
///检查客户端buffer
///@param [clientPtr] 客户端指针
///@param [channelType] 要检查的通道ID @see [ClientChannelType]
///@return [ClientCheckBufferResult]
Future<ClientCheckBufferResult> clientCheckBuffer(
int clientPtr, ClientChannelType channelType) async {
if (_isNullOrZero(clientPtr) || channelType == null)
return ClientCheckBufferResult(-5, 0, 0);
List result = await _channel
.invokeMethod("client_check_buffer", [clientPtr, channelType.index]);
return ClientCheckBufferResult(result[0], result[1], result[2]);
}
///用户登录
///@param [clientPtr] 客户端指针
///@param [username] 用户名
///@param [password] 用户密码
///@return true 发送成功,false 发送失败
Future<bool> clientLogin(
int clientPtr, String username, String password) async {
if (_isNullOrZero(clientPtr) ||
username == null ||
username.length == 0 ||
password == null ||
password.length == 0) {
return false;
}
var result = await _channel
.invokeMethod("client_login", [clientPtr, username, password]);
return result;
}
///发送CGI指令
///@param [clientPtr] 客户端指针
///@param [username] 用户名
///@param [password] 用户密码
///@return true 发送成功,false 发送失败
Future<bool> clientWriteCgi(int clientPtr, String cgi,
{int timeout = 5}) async {
if (_isNullOrZero(clientPtr) || cgi == null || cgi.length == 0)
return false;
var result = await _channel
.invokeMethod("client_write_cig", [clientPtr, cgi, timeout]);
return result;
}
///发送buffer
///@param [clientPtr] 客户端指针
///@param [channelType] 要读取的通道ID @see [ClientChannelType]
///@param [buffer] 发送buffer
///@param [timeout] 超时时间 秒为单位
///@return
/// > 0 发送成功,返回值为发送长度
/// -1 客户端未连接
/// -3 发送超时
/// -5 无效参数
/// -11 连接已失效
/// -12 远程关闭连接
/// -13 连接超时关闭
/// -15 远程接收buffer已满
Future<int> clientWrite(int clientPtr, ClientChannelType channelType,
Uint8List buffer, int timeout) async {
if (_isNullOrZero(clientPtr) || channelType == null || buffer == null)
return -5;
var result = await _channel.invokeMethod(
"client_write", [clientPtr, channelType.index, buffer, timeout]);
return result;
}
///断开P2P客户端连接
///@param [clientPtr] 客户端指针
///@return 客户端指针
Future<bool> clientDisconnect(int clientPtr) async {
if (_isNullOrZero(clientPtr)) return false;
var result = await _channel.invokeMethod("client_disconnect", [clientPtr]);
return result;
}
///销毁P2P客户端
Future<void> clientDestroy(int clientPtr) async {
if (_isNullOrZero(clientPtr)) return;
_connectListeners.remove(clientPtr);
_commandListeners.remove(clientPtr);
await _channel.invokeMethod("client_destroy", [clientPtr]);
return;
}
///销毁P2P客户端
Future<void> clientConnectBreak(int clientPtr) async {
if (_isNullOrZero(clientPtr)) return;
await _channel.invokeMethod("client_connect_break", [clientPtr]);
return;
}
}
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'app_dart.dart';
enum VideoSourceType {
/// 文件源
FILE_SOURCE,
/// 设备实时源
LIVE_SOURCE,
/// 设备TF卡回放源
CARD_SOURCE,
/// 网络下载源
NETWORK_SOURCE,
/// TF卡时间轴回放源
TimeLine_SOURCE,
///
SUB_PLAYER_SOURCE,
//SUB2_PLAYER_SOURCE,
///移动端设备视频源
CAMERA_VIDEO_SOURCE,
}
abstract class VideoSource {
final VideoSourceType sourceType;
VideoSource(this.sourceType);
dynamic getSource();
}
class FileVideoSource extends VideoSource {
FileVideoSource(this.filePath) : super(VideoSourceType.FILE_SOURCE);
final String filePath;
@override
getSource() {
return filePath;
}
}
class LiveVideoSource extends VideoSource {
LiveVideoSource(this.clientPtr) : super(VideoSourceType.LIVE_SOURCE);
final int clientPtr;
@override
getSource() {
return clientPtr;
}
}
class CameraVideoSource extends VideoSource {
CameraVideoSource(this.clientPtr, {this.dir = 0, this.frameRate = 8})
: super(VideoSourceType.CAMERA_VIDEO_SOURCE);
final int clientPtr;
final int dir;
final int frameRate;
@override
getSource() {
return [clientPtr, dir, frameRate];
}
}
class CardVideoSource extends VideoSource {
CardVideoSource(this.clientPtr, this.size, {this.checkHead = 1})
: super(VideoSourceType.CARD_SOURCE);
final int clientPtr;
final int size;
final int checkHead;
@override
getSource() {
return [clientPtr, size, checkHead];
}
}
class TimeLineSource extends VideoSource {
TimeLineSource(this.clientPtr) : super(VideoSourceType.TimeLine_SOURCE);
final int clientPtr;
@override
getSource() {
return [clientPtr];
}
}
class NetworkVideoSource extends VideoSource {
NetworkVideoSource(this.urls) : super(VideoSourceType.NETWORK_SOURCE);
final List<String> urls;
@override
getSource() {
return urls;
}
}
class SubPlayerSource extends VideoSource {
SubPlayerSource() : super(VideoSourceType.SUB_PLAYER_SOURCE);
@override
getSource() {}
}
// class Sub2PlayerSource extends VideoSource {
// Sub2PlayerSource() : super(VideoSourceType.SUB2_PLAYER_SOURCE);
//
// @override
// getSource() {}
// }
enum VideoStatus { STOP, STARTING, PLAY, PAUSE }
enum VoiceStatus { PLAY, STOP }
enum RecordStatus { PLAY, STOP }
enum RecordEncoderType { ADPCM, G711, PCM }
enum SoundTouchType {
/// 无效果
TOUCH_0,
/// 大叔
TOUCH_1,
/// 搞怪
TOUCH_2
}
typedef CreatedCallback<T> = void Function(T data);
typedef StateChangeCallback<T> = void Function(
T data,
VideoStatus videoStatus,
VoiceStatus voiceStatus,
RecordStatus recordStatus,
SoundTouchType touchType);
typedef ProgressChangeCallback<T> = void Function(T data, int totalSec,
int playSec, int progress, int loadState, int velocity, int timestamp);
typedef FocalChangeCallback<T> = void Function(T data, int focal);
typedef GPSChangeCallback<T> = void Function(
T data,
int fixStatus,
int satellitesinview,
double latitude,
double logitude,
double speed,
double course);
typedef IFrameCountChangeCallback<T> = void Function(T data, int frameCount);
typedef VideoHeadInfoCallback<T> = void Function(
T data, int resolution, int channel, int type);
typedef DrawChangeCallback<T> = void Function(
T data,
int width,
int height,
int draw_type,
double percent_x1,
double percent_y1,
double percent_x2,
double percent_y2);
class AppPlayerController<T> {
static const MethodChannel app_player_channel =
const MethodChannel('app_player');
static const EventChannel app_player_event =
const EventChannel("app_player/event");
static Stream _eventStream = app_player_event.receiveBroadcastStream();
static final DynamicLibrary playerLib = Platform.isAndroid
? DynamicLibrary.open('libOKSMARTPLAY.so')
: DynamicLibrary.process();
void Function(int playerPtr, double minScale, double maxScale, int minFocal,
int maxFocal, int direction, double threshold)? appPlayerSetScale;
void Function(
int playerPtr,
double minScale,
double maxScale,
int minFocal,
int maxFocal,
int direction,
double threshold,
double x,
double y)? appPlayerSetScaleCenter;
void Function(int playerPtr, int channel, int key)? appPlayerSetChannel;
int Function(int playerPtr, int cameraPtr)? appPlayerEnableCamera;
int Function(int playerPtr)? appPlayerDisableCamera;
int Function(int playerPtr, int channel)? appPlayerSetVoiceChannel;
void Function(int playerPtr, int deviceType, int nvrFour, int nvrIndex)?
appPlayerSetDeviceType;
void scale(int direction, int minFocal, int maxFocal, double minScale,
double maxScale, double threshold) {
if (appPlayerSetScale == null) return;
if (minScale < 1.0) minScale = 1.0;
if (maxScale < 1.0) maxScale = 1.0;
appPlayerSetScale!(
playerId, minScale, maxScale, minFocal, maxFocal, direction, threshold);
}
void scaleCenter(int direction, int minFocal, int maxFocal, double minScale,
double maxScale, double threshold, double x, double y) {
if (appPlayerSetScale == null) return;
if (minScale < 1.0) minScale = 1.0;
if (maxScale < 1.0) maxScale = 1.0;
appPlayerSetScaleCenter!(playerId, minScale, maxScale, minFocal, maxFocal,
direction, threshold, x, y);
}
void setChannelKey(int channel, int key) {
appPlayerSetChannel!(playerId, channel, key);
}
void setDeviceType(int deviceType, int nvrFour, int nvrIndex) {
appPlayerSetDeviceType!(playerId, deviceType, nvrFour, nvrIndex);
}
StreamSubscription? _subscription;
T? userData;
AppPlayerController({this.changeCallback, this.userData}) {
// _subscription = _eventStream.listen(progressListener);
appPlayerSetScale = playerLib
.lookup<
NativeFunction<
Void Function(Int64, Double, Double, Uint8, Uint8, Int8,
Double)>>("app_player_set_scale")
.asFunction();
appPlayerSetScaleCenter = playerLib
.lookup<
NativeFunction<
Void Function(Int64, Double, Double, Uint8, Uint8, Int8, Double,
Double, Double)>>("app_player_set_scale_center")
.asFunction();
appPlayerSetChannel = playerLib
.lookup<NativeFunction<Void Function(Int64, Int32, Int32)>>(
"app_player_set_channel")
.asFunction();
appPlayerSetVoiceChannel = playerLib
.lookup<NativeFunction<Int32 Function(Int64, Int32)>>(
"app_player_set_voice_channel")
.asFunction();
appPlayerEnableCamera = playerLib
.lookup<NativeFunction<Int32 Function(Int64, Int64)>>(
"app_player_enable_camera")
.asFunction();
appPlayerDisableCamera = playerLib
.lookup<NativeFunction<Int32 Function(Int64)>>(
"app_player_disable_camera")
.asFunction();
appPlayerSetDeviceType = playerLib
.lookup<NativeFunction<Void Function(Int64, Int32, Int32, Int32)>>(
"app_player_set_device_type")
.asFunction();
}
void _registerAppDartListeners() {
if (textureId == 0) return;
AppDart().addListener(textureId, progressListener);
AppDart().addGPSListener(textureId, gpsListener);
AppDart().addVideoHeadListener(textureId, headInfoListener);
AppDart().addDrawListener(textureId, drawListener);
}
void _unregisterAppDartListeners() {
if (textureId == 0) return;
AppDart().rmvListener(textureId, progressListener);
AppDart().rmvGPSListener(textureId, gpsListener);
AppDart().rmvVideoHeadListener(textureId, headInfoListener);
AppDart().rmvDrawListener(textureId, drawListener);
}
CreatedCallback<T?>? createdCallback;
StateChangeCallback<T?>? changeCallback;
List<ProgressChangeCallback<T?>> progressCallbacks = [];
List<FocalChangeCallback<T?>> focalCallbacks = [];
List<GPSChangeCallback<T?>> gpsCallbacks = [];
List<VideoHeadInfoCallback<T?>> headCallbacks = [];
List<IFrameCountChangeCallback<T>> iFrameCountCallbacks = [];
List<DrawChangeCallback<T?>> drawCallbacks = [];
bool isCreated = false;
int textureId = 0;
int playerId = 0;
VideoSourceType? sourceType;
VoiceStatus _voiceStatus = VoiceStatus.STOP;
VoiceStatus get voiceStatus => _voiceStatus;
set voiceStatus(VoiceStatus value) {
if (value != _voiceStatus) {
_voiceStatus = value;
if (changeCallback != null)
changeCallback!(userData, _videoStatus, _voiceStatus, _recordStatus,
_soundTouchType);
}
}
VideoStatus _videoStatus = VideoStatus.STOP;
VideoStatus get videoStatus => _videoStatus;
set videoStatus(VideoStatus value) {
if (value != _videoStatus) {
_videoStatus = value;
if (changeCallback != null)
changeCallback!(userData, _videoStatus, _voiceStatus, _recordStatus,
_soundTouchType);
}
}
RecordStatus _recordStatus = RecordStatus.STOP;
RecordStatus get recordStatus => _recordStatus;
set recordStatus(RecordStatus value) {
if (value != _recordStatus) {
_recordStatus = value;
if (changeCallback != null)
changeCallback!(userData, _videoStatus, _voiceStatus, _recordStatus,
_soundTouchType);
}
}
SoundTouchType _soundTouchType = SoundTouchType.TOUCH_0;
SoundTouchType get soundTouchType => _soundTouchType;
set soundTouchType(SoundTouchType value) {
if (value != _soundTouchType) {
_soundTouchType = value;
if (changeCallback != null)
changeCallback!(userData, _videoStatus, _voiceStatus, _recordStatus,
_soundTouchType);
}
}
void setCreatedCallback(CreatedCallback<T?> callback) {
this.createdCallback = callback;
}
void setStateChangeCallback(StateChangeCallback<T?> callback) {
this.changeCallback = callback;
}
void addProgressChangeCallback(ProgressChangeCallback<T?> callback) {
this.progressCallbacks.add(callback);
}
void removeProgressChangeCallback(ProgressChangeCallback<T?> callback) {
this.progressCallbacks.remove(callback);
}
void clearProgressChangeCallback() {
this.progressCallbacks.clear();
}
void addFocalChangeCallback(FocalChangeCallback<T?> callback) {
this.focalCallbacks.add(callback);
}
void removeFocalChangeCallback(FocalChangeCallback<T> callback) {
this.focalCallbacks.remove(callback);
}
void clearFocalChangeCallback() {
this.focalCallbacks.clear();
}
void addGPSChangeCallback(GPSChangeCallback<T?> callback) {
this.gpsCallbacks.add(callback);
}
void removeGPSChangeCallback(GPSChangeCallback<T> callback) {
this.gpsCallbacks.remove(callback);
}
void clearGPSChangeCallback() {
this.gpsCallbacks.clear();
}
void addHeadInfoCallback(VideoHeadInfoCallback<T?> callback) {
this.headCallbacks.add(callback);
}
void removeHeadInfoCallback(VideoHeadInfoCallback<T> callback) {
this.headCallbacks.remove(callback);
}
void clearHeadInfoCallback() {
this.headCallbacks.clear();
}
void addIFrameCountChangeCallback(IFrameCountChangeCallback<T> callback) {
this.iFrameCountCallbacks.add(callback);
}
void removeIFrameCountChangeCallback(IFrameCountChangeCallback<T> callback) {
this.iFrameCountCallbacks.remove(callback);
}
void clearIFrameCountChangeCallback() {
this.iFrameCountCallbacks.clear();
}
void addDrawChangeCallback(DrawChangeCallback<T?> callback) {
this.drawCallbacks.add(callback);
}
void removeDrawChangeCallback(DrawChangeCallback<T> callback) {
this.drawCallbacks.remove(callback);
}
void clearDrawChangeCallback() {
this.drawCallbacks.clear();
}
dynamic source;
int totalSec = 0,
playSec = 0,
progress = 0,
velocity = 0,
loadState = 0,
focal = 0,
version = 0,
timestamp = 0,
mainFrameCount = 0;
bool changedProgress = false;
void progressListener(dynamic args) {
if (args is List) {
if (args[0] == textureId) {
totalSec = args[1];
playSec = args[2];
progress = args[3];
loadState = args[4];
velocity = args[5];
version = args[7];
timestamp = args[8];
mainFrameCount = args[9];
if (loadState == 4) {
if (videoStatus == VideoStatus.PLAY) {
videoStatus = VideoStatus.STARTING;
}
} else if (videoStatus == VideoStatus.STARTING) {
videoStatus = VideoStatus.PLAY;
}
// VPLog.file(
// "progressListener: totalSec:$totalSec playSec:$playSec progress:$progress loadState:$loadState velocity:$velocity version:$version videoStatus:$videoStatus timestamp:$timestamp",
// tag: "PLAY");
if (args[6] != focal) {
if (focalCallbacks.isNotEmpty) {
for (var item in focalCallbacks) {
item(userData, args[6]);
}
}
}
focal = args[6];
if (progressCallbacks.isNotEmpty && changedProgress == false) {
for (var item in progressCallbacks) {
item(
userData, args[1], args[2], args[3], args[4], args[5], args[8]);
}
}
if (iFrameCountCallbacks.isNotEmpty) {
for (var item in iFrameCountCallbacks) {
item(userData!, args[9]);
}
}
}
}
}
int? fixStatus, satellitesinview;
double? latitude, logitude, speed, course;
void gpsListener(dynamic args) {
if (args is List) {
if (args[0] == textureId) {
fixStatus = args[1];
satellitesinview = args[2];
latitude = args[3];
logitude = args[4];
speed = args[5];
course = args[6];
// VPLog.file(
// "gpsListener: fixStatus:$fixStatus satellitesinview:$satellitesinview latitude:$latitude logitude:$logitude speed:$speed course:$course",
// tag: "PLAY");
if (gpsCallbacks.isNotEmpty) {
for (var item in gpsCallbacks) {
item(
userData, args[1], args[2], args[3], args[4], args[5], args[6]);
}
}
}
}
}
int? resolution, channel, type;
void headInfoListener(dynamic args) {
if (args is List) {
if (args[0] == textureId) {
resolution = args[1];
channel = args[2];
type = args[3];
// VPLog.file(
// "headInfoListener: resolution:$resolution",
// tag: "PLAY");
if (headCallbacks.isNotEmpty) {
for (var item in headCallbacks) {
item(userData, args[1], args[2], args[3]);
}
}
}
}
}
int? width, height, draw_type;
double? percent_x1, percent_y1, percent_x2, percent_y2;
void drawListener(dynamic args) {
if (args is List) {
if (args[0] == textureId) {
width = args[1];
height = args[2];
draw_type = args[3];
percent_x1 = args[4];
percent_y1 = args[5];
percent_x2 = args[6];
percent_y2 = args[7];
// VPLog.file(
// "drawListener: width:$width height:$height draw_type:$draw_type x1:$percent_x1 y1:$percent_y1 x2:$percent_x2 y2:$percent_y2",
// tag: "PLAY");
if (drawCallbacks.isNotEmpty) {
for (var item in drawCallbacks) {
item(userData, args[1], args[2], args[3], args[4], args[5], args[6],
args[7]);
}
}
}
}
}
AppPlayerController? sub_controller;
AppPlayerController? sub2_controller;
AppPlayerController? sub3_controller;
Future<bool> create({int audioRate = 8000, int hardware = 0}) async {
// VPLog.file("start", tag: "PLAY");
if (isCreated == true) {
// VPLog.file("isCreated:$isCreated end", tag: "PLAY");
return true;
}
var result = await app_player_channel
.invokeMapMethod("app_player_create", [0, 0, audioRate, hardware]);
if (result == null || result["result"] == false) {
// VPLog.file("result:$result error", tag: "PLAY");
return false;
}
textureId = result["textureId"];
if (result.containsKey("playerId"))
playerId = result["playerId"];
else
playerId = 0;
isCreated = true;
_registerAppDartListeners();
if (createdCallback != null) {
createdCallback!(userData);
}
// VPLog.file("textureId:$textureId end", tag: "PLAY");
return true;
}
///
/// 设置视频源
///
/// [sourceType] 视频源类型
/// [source] 视频源
/// 如果[sourceType]为[VideoSourceType.VIDEO_FILE_SOURCE]
/// source应该为文件路径
///
/// 如果[sourceType]为[VideoSourceType.VIDEO_LIVE_SOURCE]
/// source应该为设备指针
///
/// 如果[sourceType]为[VideoSourceType.VIDEO_CARD_SOURCE]
/// source应该为设备指针
///
/// 如果[sourceType]为[VideoSourceType.VIDEO_NETWORK_SOURCE]
/// source应该为下载地址
Future<bool> setVideoSource(VideoSource source) async {
if (isCreated == false) return false;
// VPLog.file("textureId:$textureId source:$source start", tag: "PLAY");
if (source is FileVideoSource) {
if (source.filePath == null ||
File(source.filePath).existsSync() == false) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
}
if (source is NetworkVideoSource) {
if (source.urls == null || source.urls.isEmpty) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
}
if (source is LiveVideoSource) {
if (source.clientPtr == null || source.clientPtr == 0) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
}
if (source is CameraVideoSource) {
if (source.clientPtr == null || source.clientPtr == 0) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
}
if (source is CardVideoSource) {
if (source.clientPtr == null || source.clientPtr == 0) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
if (source.size == null || source.size < 0) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
}
if (source is TimeLineSource) {
if (source.clientPtr == null || source.clientPtr == 0) {
// VPLog.file("textureId:$textureId error", tag: "PLAY");
return false;
}
}
debugPrint(
"--setVideoSource---${this.textureId}----${source.sourceType.index}-------${source.getSource()}----------------");
var result = await app_player_channel.invokeMethod("app_player_source",
[this.textureId, source.sourceType.index, source.getSource()]);
if (result == true) {
this.sourceType = source.sourceType;
this.source = source;
}
// VPLog.file("textureId:$textureId result:$result end", tag: "PLAY");
return result;
}
int cameraPlayerId = 0;
bool enableCameraPlayer(int playerPtr) {
if (playerId == 0 || cameraPlayerId != 0) return false;
int ret = appPlayerEnableCamera!(playerId, playerPtr);
if (ret == 0) cameraPlayerId = playerPtr;
return ret == 0;
}
bool disableCameraPlayer() {
if (playerId == 0 || cameraPlayerId == 0) return false;
appPlayerDisableCamera!(playerId);
cameraPlayerId = 0;
return true;
}
void setVoiceChannel(int channel) {
appPlayerSetVoiceChannel!(playerId, channel);
}
Future<bool> enableSubPlayer(AppPlayerController controller) async {
if (sub_controller != null) return true;
var result = await app_player_channel.invokeMethod(
"app_player_enable_sub_player", [this.textureId, controller.textureId]);
sub_controller = controller;
// VPLog.file(
// "textureId:$textureId subTextureId:${controller
// .textureId} result:$result end",
// tag: "PLAY");
return result;
}
Future<bool> enableSub2Player(AppPlayerController controller) async {
if (sub2_controller != null) return true;
var result = await app_player_channel.invokeMethod(
"app_player_enable_sub2_player",
[this.textureId, controller.textureId]);
sub2_controller = controller;
// VPLog.file(
// "textureId:$textureId sub2TextureId:${controller
// .textureId} result:$result end",
// tag: "PLAY");
return result;
}
Future<bool> enableSub3Player(AppPlayerController controller) async {
if (sub3_controller != null) return true;
var result = await app_player_channel.invokeMethod(
"app_player_enable_sub3_player",
[this.textureId, controller.textureId]);
sub3_controller = controller;
// VPLog.file(
// "textureId:$textureId sub3TextureId:${controller
// .textureId} result:$result end",
// tag: "PLAY");
return result;
}
Future<bool> disableSubPlayer() async {
if (sub_controller == null) return true;
var result = await app_player_channel.invokeMethod(
"app_player_disable_sub_player", this.textureId);
// VPLog.file(
// "textureId:$textureId subTextureId:${sub_controller
// .textureId} result:$result end",
// tag: "PLAY");
sub_controller = null;
if (sub2_controller != null) {
sub2_controller = null;
}
if (sub3_controller != null) {
sub3_controller = null;
}
return result;
}
Future<bool> start() async {
// VPLog.file(
// "textureId:$textureId videoStatus:$videoStatus source:$source start",
// tag: "PLAY");
if (source == null) return false;
var result = await app_player_channel.invokeMethod(
"app_player_start", this.textureId);
if (result == true) {
if (videoStatus == VideoStatus.STOP)
this.videoStatus = VideoStatus.STARTING;
}
// VPLog.file("textureId:$textureId result:$result end", tag: "PLAY");
return result;
}
Future<bool> startVoice() async {
if (source == null) return false;
// VPLog.file("textureId:$textureId start", tag: "PLAY");
var result = await app_player_channel.invokeMethod(
"app_player_start_voice", this.textureId);
if (result == true) {
this.voiceStatus = VoiceStatus.PLAY;
}
// VPLog.file("textureId:$textureId result:$result end", tag: "PLAY");
return result;
}
Future<bool> startRecord(
{RecordEncoderType encoderType = RecordEncoderType.G711}) async {
if (source == null) return false;
var result = await app_player_channel.invokeMethod(
"app_player_start_record", [this.textureId, encoderType.index]);
if (result == true) {
this.recordStatus = RecordStatus.PLAY;
}
return result;
}
Future<bool> stop() async {
if (videoStatus == VideoStatus.STOP) return true;
// VPLog.file("textureId:$textureId start", tag: "PLAY");
var result = await app_player_channel.invokeMethod(
"app_player_stop", this.textureId);
if (result == true) {
this.videoStatus = VideoStatus.STOP;
}
// VPLog.file("textureId:$textureId result:$result end", tag: "PLAY");
return result;
}
Future<bool> stopVoice() async {
if (source == null) return false;
// VPLog.file("textureId:$textureId start", tag: "PLAY");
var result = await app_player_channel.invokeMethod(
"app_player_stop_voice", this.textureId);
if (result == true) {
this.voiceStatus = VoiceStatus.STOP;
}
// VPLog.file("textureId:$textureId result:$result end", tag: "PLAY");
return result;
}
Future<bool> stopRecord() async {
if (source == null) return false;
// VPLog.file("textureId:$textureId start", tag: "PLAY");
var result = await app_player_channel.invokeMethod(
"app_player_stop_record", this.textureId);
if (result == true) {
this.recordStatus = RecordStatus.STOP;
}
// VPLog.file("textureId:$textureId result:$result end", tag: "PLAY");
return result;
}
Future<bool> pause() async {
if (videoStatus == VideoStatus.PAUSE) return true;
if (videoStatus != VideoStatus.PLAY) return false;
var result = await app_player_channel.invokeMethod(
"app_player_pause", this.textureId);
if (result == true) {
this.videoStatus = VideoStatus.PAUSE;
}
return result;
}
Future<bool> flipCamera(int front) async {
// if (videoStatus == VideoStatus.PAUSE) return true;
// if (videoStatus != VideoStatus.PLAY) return false;
var result = await app_player_channel
.invokeMethod("flip_camera", [this.textureId, front]);
return result;
}
Future<bool> isFrontCamera() async {
var result = await app_player_channel
.invokeMethod("isFrontCamera", [this.textureId]);
return result;
}
Future<bool> resume() async {
if (videoStatus == VideoStatus.PLAY) return true;
if (videoStatus != VideoStatus.PAUSE) return false;
var result = await app_player_channel.invokeMethod(
"app_player_resume", this.textureId);
if (result == true) {
this.videoStatus = VideoStatus.PLAY;
}
return result;
}
Future<bool> setProgress(int duration,
{bool timeLine = false, int channel = 2, int key = 0}) async {
if (isCreated == false) return false;
if (source == null) return false;
if (videoStatus == VideoStatus.STOP) return false;
if (duration >= totalSec && timeLine == false) return false;
if (duration == playSec) return true;
changedProgress = true;
var result = await app_player_channel.invokeMethod(
"app_player_progress", [this.textureId, duration, channel, key]);
changedProgress = false;
if (result == true) {
playSec = duration;
}
return result;
}
Future<bool> screenshot(String filePath,
{String imageSize = "0",
double widthPercent = 0,
double heightPercent = 0,
int sub = 0}) async {
if (isCreated == false) return false;
if (source == null) return false;
int destWidth = 0;
int destHeight = 0;
if (imageSize != "0" && imageSize.contains("x")) {
var list = imageSize.split("x").toList();
destWidth = int.tryParse(list.first) ?? 0;
destHeight = int.tryParse(list.last) ?? 0;
}
return await app_player_channel.invokeMethod("app_player_screenshot", [
this.textureId,
filePath,
destWidth,
destHeight,
widthPercent,
heightPercent,
sub
]);
}
Future<bool> setSoundTouch(SoundTouchType touchType) async {
if (isCreated == false) return false;
if (source == null) return false;
var result = await app_player_channel.invokeMethod(
"app_player_soundTouch", [this.textureId, touchType.index]);
if (result == true) {
this.soundTouchType = touchType;
}
return result;
}
double playSpeed = 0;
Future<bool> setSpeed(double speed) async {
if (isCreated == false) return false;
if (source == null) return false;
if (source is LiveVideoSource) {
return false;
}
if (speed != 1.0) await stopVoice();
var result = await app_player_channel
.invokeMethod("app_player_speed", [this.textureId, speed]);
if (result == true) {
this.playSpeed = speed;
}
return result;
}
Future<int> save(String filePath,
{int start = 0, int end = 0xFFFFFFFF}) async {
if (isCreated == false) return -1;
if (source == null) return -1;
return await app_player_channel.invokeMethod(
"app_player_save", [this.textureId, filePath, start, end]);
}
Future<int> saveNVR(String filePath,
{int start = 0, int end = 0xFFFFFFFF, int index = 0}) async {
if (isCreated == false) return -1;
if (source == null) return -1;
return await app_player_channel.invokeMethod(
"app_player_save_nvr", [this.textureId, filePath, start, end, index]);
}
Future<bool> startDown(String filePath) async {
if (isCreated == false) return false;
if (source == null) return false;
return await app_player_channel
.invokeMethod("app_player_start_down", [this.textureId, filePath]);
}
Future<bool> stopDown() async {
if (isCreated == false) return false;
if (source == null) return false;
return await app_player_channel
.invokeMethod("app_player_stop_down", [this.textureId]);
}
static Future<bool> saveMP4(String srcPath, String destPath,
{int enableSub = 0,
int destWidth = 0,
int destHeight = 0,
int audioRate = 8000}) async {
if (Platform.isAndroid) {
destWidth = 0;
destHeight = 0;
}
return await app_player_channel.invokeMethod("app_player_save_mp4",
[srcPath, destPath, enableSub, destWidth, destHeight, audioRate]);
}
static Future<bool> saveWAVE(String srcPath, String destPath,
{int channel = 1, int fmt = 16, int rate = 8000}) async {
return await app_player_channel.invokeMethod(
"app_player_save_wave", [srcPath, destPath, channel, fmt, rate]);
}
Future<bool> clearCacheData() async {
return await app_player_channel
.invokeMethod("app_player_clear", [this.textureId]);
}
static Future<bool> saveMP4Rate(String srcPath, String destPath,
{double rate = 1.0}) async {
return await app_player_channel
.invokeMethod("app_player_save_mp4_rate", [srcPath, destPath, rate]);
}
Future<void> dispose() async {
if (isCreated == false) return;
isCreated = false;
progressCallbacks.clear();
focalCallbacks.clear();
gpsCallbacks.clear();
headCallbacks.clear();
iFrameCountCallbacks.clear();
if (_subscription != null) _subscription!.cancel();
_unregisterAppDartListeners();
await app_player_channel.invokeMethod("app_player_stop", this.textureId);
await app_player_channel.invokeMethod("app_player_destroy", this.textureId);
}
}
class AppPlayerView extends StatelessWidget {
const AppPlayerView({Key? key, required this.controller}) : super(key: key);
final AppPlayerController controller;
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
double width = constraints.constrainWidth();
double height = constraints.constrainHeight();
width = width * window.devicePixelRatio;
height = height * window.devicePixelRatio;
if (controller.isCreated == false) {
return FutureBuilder(
builder: (context, asyncSnapshot) {
if (asyncSnapshot.connectionState == ConnectionState.done) {
if (asyncSnapshot.data == true) {
return Container(
color: Colors.black,
child: Texture(textureId: controller.textureId),
);
}
}
return Container();
},
future: controller.create(),
);
}
// controller.changeSize(width, height);
return Container(
color: Colors.black,
child: Texture(textureId: controller.textureId),
);
});
}
@override
bool operator ==(Object other) {
if (other.runtimeType != AppPlayerView) {
return false;
}
if (other is AppPlayerView) {
return controller == other.controller;
}
return false;
}
@override
int get hashCode => Object.hash(this, controller);
}
import 'dart:async';
import 'dart:io';
import 'package:equatable/equatable.dart';
import 'package:path_provider/path_provider.dart';
abstract class ListenEvent extends Equatable {
@override
bool get stringify => true;
@override
List<Object?> get props => [];
}
class AddListenEvent<T> extends ListenEvent {
final T listener;
AddListenEvent(this.listener);
@override
List<Object?> get props => [listener];
}
class RemoveListenEvent<T> extends ListenEvent {
final T listener;
RemoveListenEvent(this.listener);
@override
List<Object?> get props => [listener];
}
class ActionListenEvent extends ListenEvent {
final Function callback;
final Type type;
ActionListenEvent(this.callback, this.type);
@override
List<Object> get props => [callback, type];
}
///基础设备
abstract class BasisDevice {
late StreamController<ListenEvent> _controller;
BasisDevice(this.id, this.name, this.model) {
_controller = StreamController<ListenEvent>();
_controller.stream.asyncMap(_funcListen).listen((event) {});
}
_funcListen(ListenEvent event) {
if (event is AddListenEvent) {
_listeners.add(event.listener);
} else if (event is RemoveListenEvent) {
_listeners.remove(event.listener);
} else if (event is ActionListenEvent) {
for (var func in _listeners) {
if (func.runtimeType == event.type) {
Function.apply(event.callback, [func]);
}
}
}
return event;
}
List _listeners = [];
void addListener<T>(T listener) {
if (!_controller.isClosed) _controller.add(AddListenEvent<T>(listener));
}
void removeListener<T>(T listener) {
if (!_controller.isClosed) _controller.add(RemoveListenEvent<T>(listener));
}
void notifyListeners<T>(void Function(T listener) callback) {
if (!_controller.isClosed) _controller.add(ActionListenEvent(callback, T));
// _listeners.forEach((func) {
// if (func is T) {
// callback(func);
// }
// });
}
void cleanListener() {
_controller.close();
_listeners.clear();
}
///设备名称
final String name;
///设备ID
final String id;
/// 设备型号
final String model;
Future<Directory> getDeviceDirectory() async {
// return null;
Directory dir = await getApplicationDocumentsDirectory();
dir = Directory("${dir.path}/$id");
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
return dir;
}
}
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'package:vsdk/p2p_device/p2p_connect.dart';
import 'package:vsdk/p2p_device/p2p_device.dart';
import 'package:vsdk/vp_log.dart';
import '../app_p2p_api.dart';
import '../device_wakeup_server.dart';
import 'commands/ai_command.dart';
import 'commands/alarm_command.dart';
import 'commands/camera_command.dart';
import 'commands/card_command.dart';
import 'commands/param_command.dart';
import 'commands/plan_command.dart';
import 'commands/record_command.dart';
import 'commands/status_command.dart';
import 'commands/video_command.dart';
import 'commands/voice_command.dart';
import 'commands/wakeup_command.dart';
enum CameraConnectState {
connecting, //连接中
logging, //登录中
connected, //在线
timeout, //连接超时
disconnect, //连接中断
password, //密码错误
maxUser, //观看人数过多
offline, //离线
illegal, //非法的
none,
}
typedef CameraConnectChanged = void Function(
CameraDevice device, CameraConnectState connectState);
typedef CameraVerifyListener = Future<bool> Function(CameraDevice device);
typedef CameraConnectTypeChanged = void Function(
CameraDevice device, int connectType, String password);
class CameraDevice extends P2PBasisDevice
with
P2PConnect,
P2PCommand,
StatusCommand,
CameraCommand,
ParamsCommand,
VideoCommand,
VoiceCommand,
WakeupCommand,
AICommand,
AlarmCommand,
PlanCommand,
CardCommand,
RecordCommand,
WidgetsBindingObserver {
CameraDevice(
String id, String name, String username, String password, String model,
{String? clientId, this.editPassword = true, this.connectType = 126})
: super(id, username, password, name, model, clientId: clientId) {
WidgetsBinding.instance.addObserver(this);
this.addListener<WakeupStateChanged>(_wakeupStateListener);
}
bool editPassword = true;
int connectType = 126;
int userid = 0;
Timer? wakeupTimer;
///旧密码(认证失败才会使用)
CameraConnectState _connectState = CameraConnectState.none;
CameraConnectState get connectState => _connectState;
set connectState(CameraConnectState value) {
if (_connectState == CameraConnectState.maxUser &&
value == CameraConnectState.disconnect) {
} else {
if (value != _connectState) {
_connectState = value;
notifyListeners<CameraConnectChanged>((CameraConnectChanged func) {
func(this, _connectState);
});
}
}
}
CameraVerifyListener? verifyListener;
//
// @Deprecated('不使用"p2pConnect"进行连接处理,调用函数connect')
// Future<ClientConnectState> p2pConnect(
// {bool lanScan = true, int reConnectCount = 2, required int connectType}) {
// debugPrint(
// "id:$id lanScan:$lanScan reConnectCount:$reConnectCount connectType:$connectType start");
// if (_destroyFlag == true) {
// debugPrint("id:$id isDestroy:$_destroyFlag error");
// return Future.value(ClientConnectState.CONNECT_STATUS_DISCONNECT);
// }
// return super.p2pConnect(
// lanScan: lanScan,
// reConnectCount: reConnectCount,
// connectType: connectType);
// }
@override
Future<bool> writeCgi(String cgi,
{int timeout = 5, bool needLogin = true}) async {
if (_destroyFlag == true) {
return false;
}
if (needLogin == true && connectState != CameraConnectState.connected) {
return false;
}
return super.writeCgi(cgi, timeout: timeout);
}
AppLifecycleState? _lifecycleState;
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
_lifecycleState = state;
if (state == AppLifecycleState.paused) {
debugPrint("id:$id state:$state");
isBack = true;
sirenCommand?.controlSiren(false); //进入后台停止警笛
// disconnect(); // 屏蔽后台自动断开连接
} else if (state == AppLifecycleState.resumed) {
isBack = false;
verifyOffline = false;
if (connectState != CameraConnectState.connected) {
connect();
}
}
}
Future<int> _checkConnectType(
int count, String clientId, int clientPtr) async {
if (_destroyFlag == true) {
debugPrint("id:$id isDestroy:$_destroyFlag error");
return Future.value(connectType);
}
var cType = connectType;
if (cType == 63) {
bool deviceAp = await super.checkDeviceAp(id);
if (!deviceAp) {
cType = 0x00;
await changeClientId();
}
}
if (isVirtualId == false) {
cType = 0x00;
} else {
if (clientId.startsWith('VSGG') || clientId.startsWith('VSGM')) {
cType = 0x7B;
}
if (cType != 63) {
// if (clientId == null) {
// cType = 0x00;
// return cType;
// }
if (count > 2) {
return 0x00;
}
if (count > 1) {
return 0x7b;
}
}
}
debugPrint("id:$id cType:$cType end");
return cType;
}
Future<bool> _checkOffline(
bool lanScan, String clientId, int clientPtr) async {
if (_destroyFlag == true) {
debugPrint("id:$id isDestroy:$_destroyFlag error");
return Future.value(false);
}
int timeout = await super.connectByTime(
clientPtr: clientPtr,
lanScan: lanScan,
reConnectCount: 1,
connectType: 0x79);
if (timeout > 300) {
verifyOffline = true;
connectState = CameraConnectState.offline;
}
debugPrint("id:$id timeout:$timeout connectState:$connectState end");
return timeout > 300;
}
Future<int> offlineSeconds() async {
int clientPtr = await getClientPtr();
int timeout = await super.connectByTime(
clientPtr: clientPtr,
lanScan: true,
reConnectCount: 1,
connectType: 0x79);
return timeout;
}
Future<StatusResult?> _login(int count, String pwd) async {
StatusResult? result;
int i = 0;
String admin = 'admin';
while (i < count && !_connectExitFlag && !_destroyFlag) {
result = await login(admin, pwd, timeout: 30);
i += 1;
if (result == null || result.isSuccess == false)
continue;
else
break;
}
return result;
}
Future<CameraConnectState> connect(
{bool lanScan = true, int connectCount = 3}) async {
if (_destroyFlag == true) {
debugPrint("id:$id isDestroy:$_destroyFlag error");
return CameraConnectState.disconnect;
}
if (isBack || isRemoteClose) {
debugPrint(
"id:$id CameraConnectState.disconnect error isBack:$isBack isRemoteClose:$isRemoteClose");
return CameraConnectState.disconnect;
}
if (connectState == CameraConnectState.connecting ||
connectState == CameraConnectState.logging ||
connectState == CameraConnectState.connected) {
return connectState;
}
_ensureWakeupPolling();
connectState = CameraConnectState.connecting;
_connectExitFlag = false;
var clientId = await getClientId();
int clientPtr = await getClientPtr();
VPLog.file('id:$id connect clientId:$clientId clientPtr:$clientPtr',
tag: "P2P");
if (clientPtr == null || clientPtr == 0) {
connectState = CameraConnectState.offline;
return CameraConnectState.offline;
}
if (verifyOffline) {
var offline = await _checkOffline(lanScan, clientId, clientPtr);
if (offline == true) {
debugPrint("id:$id CameraConnectState.offline end");
connectState = CameraConnectState.offline;
return connectState;
}
}
// this.statusResult?.batteryRate = null;
// this.statusResult?.isCharge = null;
this.statusResult = this.statusResult;
late ClientConnectState p2pState;
this.removeListener<P2PConnectStateChanged>(_connectStateListener);
var cType = connectType;
for (int i = 0; i < connectCount && !_connectExitFlag; i++) {
/// 检测连接类型
requestWakeup();
cType = await _checkConnectType(i, clientId, clientPtr);
p2pState = await super
.p2pConnect(lanScan: lanScan, reConnectCount: 1, connectType: cType);
if (p2pState != ClientConnectState.CONNECT_STATUS_ONLINE) {
if (i == 0 && (cType == 0x7B || cType == 95)) {
debugPrint("id:$id cType:$cType 转发连接失败后,检查设备离线时间");
var offline = await _checkOffline(lanScan, clientId, clientPtr);
if (offline == true) {
debugPrint("id:$id CameraConnectState.offline end");
return CameraConnectState.offline;
}
} else if (i == 1 && cType != 63) {
debugPrint("id:$id cType:$cType 转发连接失败后,检查设备离线时间");
var offline = await _checkOffline(lanScan, clientId, clientPtr);
if (offline == true) {
debugPrint("id:$id CameraConnectState.offline end");
return CameraConnectState.offline;
}
}
}
if (p2pState == ClientConnectState.CONNECT_STATUS_ONLINE) {
if (connectType == 63 && await super.checkDeviceAp(id)) {
cType = 63;
}
break;
}
}
if (p2pState != ClientConnectState.CONNECT_STATUS_ONLINE) {
connectState = _getConnectState();
debugPrint("id:$id $connectState end");
return connectState;
}
this.addListener<P2PConnectStateChanged>(_connectStateListener);
await this.setCommandListener();
connectState = CameraConnectState.logging;
StatusResult? result = await _login(connectCount, password);
if (result == null || result.isSuccess != true) {
connectState = CameraConnectState.timeout;
debugPrint("id:$id $connectState end");
return connectState;
}
if (result.result == "-4") {
connectState = CameraConnectState.illegal;
debugPrint("id:$id $connectState end");
return connectState;
}
if (result.result == "-1" ||
result.result == "-2" ||
result.result == "-3") {
connectState = CameraConnectState.password;
debugPrint("id:$id $connectState end");
return connectState;
}
var realDeviceId = result.realdeviceid;
int currentUsers = int.tryParse(result.current_users ?? "") ?? 0;
int maxSupportUsers = int.tryParse(result.max_support_users ?? "") ?? 0;
if (currentUsers > maxSupportUsers) {
connectState = CameraConnectState.maxUser;
debugPrint(
"id:$id currentUsers:$currentUsers maxUsers:$maxSupportUsers $connectState end");
return connectState;
}
if (isVirtualId == true && realDeviceId != null && id != realDeviceId) {
connectState = CameraConnectState.password;
debugPrint("id:$id vuid:$realDeviceId $connectState end");
return connectState;
}
String? dual = result.DualAuthentication;
if (dual != null && dual != "0" && verifyListener != null) {
bool verify = await verifyListener!(this);
if (verify == false) {
await disconnect();
connectState = CameraConnectState.disconnect;
debugPrint("id:$id dualVerify:$verify $connectState end");
return connectState;
}
}
connectState = CameraConnectState.connected;
debugPrint("id:$id $connectState end ");
keepAlive(time: 10);
getStatus(cache: true);
updateDateTime();
/*if(password == '888888')
{
///修改密码
String deviceName ='网络摄像机';
String area = '1';
String json =
'{"authkey":"SDK","security":"SDK","A":"${area}","D":"1","U":"10000","name":$deviceName';
password = "123456789acb";///修改摄像机随机密码
await updateAdminPassword(password, userid: 10000);
await set_ipc_binding(json);
}*/
return connectState;
}
@override
Future<bool> disconnect() {
_connectExitFlag = true;
if (_destroyFlag == true) return Future.value(true);
return super.disconnect();
}
bool _destroyFlag = false;
@override
Future<void> deviceDestroy() async {
if (_destroyFlag == true) return;
_destroyFlag = true;
wakeupTimer?.cancel();
wakeupTimer = null;
if (connectState == CameraConnectState.connected) {
try {
await stopStream();
} catch (_) {}
try {
await stopSoundStream();
} catch (_) {}
}
WidgetsBinding.instance.removeObserver(this);
await disconnect();
this.removeListener<P2PConnectStateChanged>(_connectStateListener);
await removeCommandListener();
await super.deviceDestroy();
}
bool _connectExitFlag = false;
///前后台标签
bool isBack = false;
bool isRemoteClose = false;
bool verifyOffline = false;
void _connectStateListener(
P2PBasisDevice sender, ClientConnectState connectState) async {
if (_destroyFlag) return;
this.connectState = _getConnectState();
if (_lifecycleState == AppLifecycleState.resumed) {
if (connectState == ClientConnectState.CONNECT_STATUS_DISCONNECT) {
connect();
}
}
}
void _requestStatusTimer(timer) {
requestWakeupStatus();
}
void _ensureWakeupPolling() {
if (_destroyFlag || wakeupTimer != null) return;
requestWakeupStatus();
wakeupTimer = Timer.periodic(Duration(seconds: 3), _requestStatusTimer);
}
void _wakeupStateListener(
P2PBasisDevice device, DeviceWakeupState? wakeupState) {
if (_destroyFlag) return;
debugPrint("id:${device.id} wakeupState:$wakeupState");
if (wakeupState == null) return;
if (wakeupState == DeviceWakeupState.online) {
wakeupTimer?.cancel();
wakeupTimer = Timer.periodic(Duration(seconds: 45), _requestStatusTimer);
if (this.connectState != CameraConnectState.connected) {
connect();
}
} else if (wakeupState == DeviceWakeupState.offline) {
wakeupTimer?.cancel();
wakeupTimer = Timer.periodic(Duration(seconds: 3), _requestStatusTimer);
}
if (this.connectState == CameraConnectState.disconnect ||
this.connectState == CameraConnectState.offline) {
if (wakeupState == DeviceWakeupState.online) connect();
}
}
CameraConnectState _getConnectState() {
switch (p2pConnectState) {
case ClientConnectState.CONNECT_STATUS_INVALID_ID:
case ClientConnectState.CONNECT_STATUS_INVALID_CLIENT:
case ClientConnectState.CONNECT_STATUS_OFFLINE:
case ClientConnectState.CONNECT_STATUS_MAX:
return CameraConnectState.offline;
case ClientConnectState.CONNECT_STATUS_CONNECTING:
case ClientConnectState.CONNECT_STATUS_INITIALING:
case ClientConnectState.CONNECT_STATUS_ONLINE:
return CameraConnectState.connecting;
case ClientConnectState.CONNECT_STATUS_MAX_SESSION:
return CameraConnectState.maxUser;
case ClientConnectState.CONNECT_STATUS_CONNECT_TIMEOUT:
return CameraConnectState.timeout;
case ClientConnectState.CONNECT_STATUS_CONNECT_FAILED:
case ClientConnectState.CONNECT_STATUS_DISCONNECT:
return CameraConnectState.disconnect;
}
return CameraConnectState.none;
}
}
import 'dart:convert';
import 'dart:typed_data';
import 'package:vsdk/camera_device/commands/status_command.dart';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'camera_command.dart';
mixin AICommand on CameraCommand {
@override
Future<StatusResult?> getStatus({int timeout = 5, bool cache = true}) async {
StatusResult? result =
await super.getStatus(timeout: timeout, cache: cache);
if (result != null) {
await setCameraCommand(result);
}
return result;
}
Future<void> setCameraCommand(StatusResult result) async {
if (humanTracking == null && result.support_humanDetect == "1") {
humanTracking = HumanTracking(this);
}
if (humanFraming == null && result.support_humanoidFrame == "1") {
humanFraming = HumanFraming(this);
}
if (humanZoom == null && result.support_humanoid_zoom == "1") {
humanZoom = HumanZoom(this);
}
if (customSound == null && result.support_voiceTypedef == "1") {
customSound = CustomSound(this);
}
if (aiDetect == null && result.support_mode_AiDetect != null) {
aiDetect = AiDetect(this);
}
}
HumanTracking? humanTracking;
HumanFraming? humanFraming;
HumanZoom? humanZoom;
CustomSound? customSound;
AiDetect? aiDetect;
}
typedef HumanTrackCallBack<T> = void Function(int value);
/// 人形追踪
class HumanTracking {
final AICommand _command;
HumanTracking(this._command);
int humanTrackingEnable = 0;
int humanTrackStatus = 0;
///获取设备人形跟踪
Future<bool> getHumanTracking(
{int timeout = 5, HumanTrackCallBack? humanTrackCallBack}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2127&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2127;") && str.contains("command=1;");
}
return false;
}, timeout);
_command.removeCallback(24785);
_command.addCallback(24785, (cmd, result) {
Map data = result.getMap();
if (data.containsKey("track_status")) {
if (humanTrackCallBack != null) {
humanTrackStatus = int.tryParse(data["track_status"] ?? "0") ?? 0;
humanTrackCallBack(humanTrackStatus);
}
}
});
if (result.isSuccess == true) {
Map data = result.getMap();
humanTrackingEnable = int.tryParse(data["enable"] ?? "0") ?? 0;
humanTrackStatus = int.tryParse(data["track_status"] ?? "0") ?? 0;
return data["result"] == "0";
}
}
return false;
}
///设置设备人形跟踪
Future<bool> setHumanTracking(int enable, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2127&command=0&enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2127;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
humanTrackingEnable = int.tryParse(data["enable"] ?? "0") ?? 0;
return data["result"] == "0";
}
}
return false;
}
}
/// 人形框定
class HumanFraming {
final AICommand _command;
HumanFraming(this._command);
int humanFrameEnable = 0;
///获取人形框定
Future<bool> getHumanFraming({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2126&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
humanFrameEnable = int.tryParse(data["bHumanoidFrame"] ?? "0") ?? 0;
return data["result"] == "0";
}
}
return false;
}
///设置人形框定
Future<bool> setHumanFraming(int enable, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2126&command=0&bHumanoidFrame=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
humanFrameEnable = int.tryParse(data["bHumanoidFrame"] ?? "0") ?? 0;
return data["result"] == "0";
}
}
return false;
}
}
/// 人形变倍跟踪
class HumanZoom {
final AICommand _command;
HumanZoom(this._command);
int humanZoomEnable = 0;
Future<bool> getHumanZoom({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2126&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
humanZoomEnable = int.tryParse(data["humanoid_zoom"] ?? "0") ?? 0;
return data["result"] == "0";
}
}
return false;
}
///设置人形框定
Future<bool> setHumanZoom(int enable, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2126&command=0&humanoid_zoom=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
humanZoomEnable = int.tryParse(data["humanoid_zoom"] ?? "0") ?? 0;
return data["result"] == "0";
}
}
return false;
}
}
class CustomSound {
final AICommand _command;
CustomSound(this._command);
Map? soundData;
///获取报警声音
///voicetype
///0---人脸侦测报警提 示音
///1---人形侦测报警提 示音
///2---烟感报警提示音
///3---移动侦测报警提 示音
///4---离岗检测提示音
///5---哭声检测提示音
///6---在岗监测提示音
///7---烟火相机火焰提示音
///8---烟火相机烟雾提示音
Future<bool> getVoiceInfo(int voiceType, {int timeout = 10}) async {
String cgi =
"trans_cmd_string.cgi?cmd=2135&command=1&voicetype=$voiceType&";
bool ret = await _command.writeCgi(cgi, timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2135;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>getVoiceInfo获取到的数据:${data}');
soundData = data;
return data["result"] == "0";
}
}
return false;
}
///设置报警声音
///param [voiceUrl] 下载服务器地址
///param [voiceName] 文件名
///param [swtich] 0无声音 1 有声音
///param [voicetype] 0---人脸侦测报警提 示音 1---人形侦测报警提 示音 2---烟感报警提示音 3---移动侦测报警提 示音 4---离岗检测提示音 5---哭声检测提示音 6---在岗监测提示音 7---烟火相机火焰提示音 8---烟火相机烟雾提示音
/// 9---区域入侵提示音,10---人逗留检测提示音,11---车违停检测提示音,12---越线检测提示音,13---离岗检测提示音,14---车辆逆行提示音,15---包裹监测(出现包裹),16----包裹消失,17---包裹滞留, 19----带屏设备呼叫声
///param [playInDevice] 是否让设备播放
Future<bool> setVoiceInfo(
String? voiceUrl, String voiceName, int swtich, int voicetype,
{bool playInDevice = false,
int timeout = 20,
String playTimes = '3'}) async {
String urlJson = "{}";
if (voiceUrl != null && voiceUrl.isNotEmpty) {
var dic = {"url": voiceUrl};
urlJson = json.encode(dic);
}
String cgi =
"trans_cmd_string.cgi?cmd=2135&command=0&urlJson=$urlJson&filename=$voiceName&switch=$swtich&voicetype=$voicetype&";
if (swtich == 0) {
cgi =
"trans_cmd_string.cgi?cmd=2135&command=0&switch=$swtich&voicetype=$voicetype&";
}
if (playInDevice == true) {
cgi = "${cgi}play=1&playtimes=$playTimes&";
} else {
cgi = "${cgi}playtimes=$playTimes&";
}
bool ret = await _command.writeCgi(cgi, timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2135;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
}
class AiDetect {
final AICommand _command;
AiDetect(this._command);
Map<String, dynamic>? aiConfigMap;
Future<bool> getAiDetectData(int aiType, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2400&command=1&AiType=$aiType&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2400;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>获取getAiDetectData:$data');
Map<String, dynamic> aiCfg = jsonDecode((data["AiCfg"]));
if (aiCfg is Map) {
aiConfigMap = aiCfg;
} else {
print('==>>AiCfg 不是个Map');
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setAiDetectData(int aiType, String aiConfigSring,
{int timeout = 5}) async {
print('==>>设置setAiDetectData 类型:${aiType} 数据:$aiConfigSring');
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2400&command=0&AiType=$aiType&AiCfg=$aiConfigSring&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2400;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>设置setAiDetectData结果:$data');
return data["result"] == "0";
}
}
return false;
}
// 获取AI定时计划
// command=11
// type=12 获取火计划
// type=14 获取区域入侵计划
// type=15 获取人逗留计划
// type=16 获取车违停计划
// type=17 获取越线检测计划
// type=18 获取离岗检测计划
// type=19 获取车辆逆行计划
// type=20 获取包裹计划
Map? firePlanData;
Map? areaIntrusionPlanData;
Map? personStayPlanData;
Map? illegalParkingPlanData;
Map? crossBorderPlanData;
Map? offPostMonitorPlanData;
Map? carRetrogradePlanData;
Map? packageDetectPlanData;
Future<bool> getAiDetectPlan(int type, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=1&type=$type&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") &&
str.contains("command=11;") &&
str.contains("type=$type;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
print('==>>获取到AI检测类型:${type}的结果:${data}');
if (type == 12) {
//获取火计划
if (data.containsKey("fire_plan_enable")) {
firePlanData = data;
}
} else if (type == 14) {
//获取区域入侵计划
if (data.containsKey("region_entry_plan_enable")) {
areaIntrusionPlanData = data;
}
} else if (type == 15) {
//获取人逗留计划
if (data.containsKey("person_stay_plan_enable")) {
personStayPlanData = data;
}
} else if (type == 16) {
//获取车违停计划
if (data.containsKey("car_stay_plan_enable")) {
illegalParkingPlanData = data;
}
} else if (type == 17) {
//获取越线检测计划
if (data.containsKey("line_cross_plan_enable")) {
crossBorderPlanData = data;
}
} else if (type == 18) {
//获取离岗检测计划
if (data.containsKey("person_onduty_plan_enable")) {
offPostMonitorPlanData = data;
}
} else if (type == 19) {
//获取车辆逆行计划
if (data.containsKey("car_retrograde_plan_enable")) {
carRetrogradePlanData = data;
}
} else if (type == 20) {
//包裹计划
if (data.containsKey("package_detect_plan_enable")) {
packageDetectPlanData = data;
}
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> configAiDetectPlan(int type,
{required List records, int enable = 1, int timeout = 5}) async {
String typeString;
switch (type) {
case 12:
//火
typeString = 'fire';
break;
case 14:
//区域入侵
typeString = 'region_entry';
break;
case 15:
//人逗留计划
typeString = 'person_stay';
break;
case 16:
//车违停计划
typeString = 'car_stay';
break;
case 17:
//越线检测计划
typeString = 'line_cross';
break;
case 18:
//离岗检测计划
typeString = 'person_onduty';
break;
case 19:
//车辆逆行计划
typeString = 'car_retrograde';
break;
case 20:
//包裹监测
typeString = 'package_detect';
break;
default:
typeString = '';
break;
}
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=$type&mark=1&"
"${typeString}_plan1=${records[0]}&"
"${typeString}_plan2=${records[1]}&"
"${typeString}_plan3=${records[2]}&"
"${typeString}_plan4=${records[3]}&"
"${typeString}_plan5=${records[4]}&"
"${typeString}_plan6=${records[5]}&"
"${typeString}_plan7=${records[6]}&"
"${typeString}_plan8=${records[7]}&"
"${typeString}_plan9=${records[8]}&"
"${typeString}_plan10=${records[9]}&"
"${typeString}_plan11=${records[10]}&"
"${typeString}_plan12=${records[11]}&"
"${typeString}_plan13=${records[12]}&"
"${typeString}_plan14=${records[13]}&"
"${typeString}_plan15=${records[14]}&"
"${typeString}_plan16=${records[15]}&"
"${typeString}_plan17=${records[16]}&"
"${typeString}_plan18=${records[17]}&"
"${typeString}_plan19=${records[18]}&"
"${typeString}_plan20=${records[19]}&"
"${typeString}_plan21=${records[20]}&"
"${typeString}_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=$type;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
print('==>>configAiDetectPlan:$type的结果:${data}');
if (data["status"] == "0" || data["result"] == "0") {
return true;
}
}
}
return false;
}
}
import 'dart:convert';
import 'dart:typed_data';
import 'package:vsdk/p2p_device/p2p_command.dart';
mixin AlarmCommand on P2PCommand {
bool? pirPushEnable;
bool? pirPushVideoEnable;
int? pirDetection;
int? pirCloudVideoDuration; //视频时长
int? pirLevel; //人体
int? motionLevel; //移动
int? humanLevel; //人形
int? pushEnable; //推送开关
int? videoEnable; //视频开关
int? videoDuration; //视频时长
int? humanDetection; //
int? distanceAdjust; //侦测距离
int? humanoidDetection; //人形检测
int? autoRecordVideoMode; //录像时长自动
Future<bool> getAlarmParam({int timeout = 5}) async {
bool ret = await writeCgi("trans_cmd_string.cgi?cmd=2106&command=8&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=8;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
pirPushEnable = data["pirPushSwitch"] == "1";
pirPushVideoEnable = data["pirPushSwitchVideo"] == "1";
if (data.containsKey("CloudVideoDuration")) {
pirCloudVideoDuration =
int.tryParse(data["CloudVideoDuration" ?? "15"]) ?? -1;
} else {
pirCloudVideoDuration = -1;
}
if (data.containsKey("autoRecordMode")) {
autoRecordVideoMode =
int.tryParse(data["autoRecordMode" ?? "0"]) ?? 0;
} else {
autoRecordVideoMode = 0;
}
await getPirDetection();
return data["result"] == "0";
}
}
return false;
}
Future<bool> setPriPush(
{bool pushEnable = true,
bool videoEnable = true,
int videoDuration = 15,
int autoRecordMode = 0,
int timeout = 5}) async {
pushEnable = (pushEnable ?? pirPushEnable) ?? false;
videoEnable = (videoEnable ?? pirPushVideoEnable) ?? false;
videoDuration = (videoDuration ?? pirCloudVideoDuration) ?? 15;
autoRecordMode = (autoRecordMode ?? autoRecordVideoMode) ?? 0;
String cgi =
"trans_cmd_string.cgi?cmd=2106&command=9&pirPushSwitch=${pushEnable ? 1 : 0}&pirPushSwitchVideo=${videoEnable ? 1 : 0}&";
if (videoDuration != -1) {
cgi =
"trans_cmd_string.cgi?cmd=2106&command=9&pirPushSwitch=${pushEnable ? 1 : 0}&pirPushSwitchVideo=${videoEnable ? 1 : 0}&CloudVideoDuration=${videoDuration ?? 15}&autoRecordMode=${autoRecordMode ?? 0}&";
}
bool ret = await writeCgi(cgi, timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=9;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
pirPushEnable = pushEnable;
pirPushVideoEnable = videoEnable;
pirCloudVideoDuration = videoDuration;
autoRecordVideoMode = autoRecordMode;
return true;
}
}
}
return false;
}
///人体侦测等级
///关-----0
///低-----1
///中-----2
///高-----3
Future<bool> getPirDetection({int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=3&mark=12345678&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=3;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
pirDetection = int.tryParse(data["humanDetection"] ?? "") ?? 0;
pirLevel = int.tryParse(data["humanDetection"] ?? "") ?? 0;
distanceAdjust = int.tryParse(data["DistanceAdjust"] ?? "") ?? 0;
humanoidDetection = int.tryParse(data["HumanoidDetection"] ?? "") ?? 0;
return data["result"] == "0";
}
}
return false;
}
Future<bool> setPriDetection(int detection, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=4&humanDetection=$detection&&mark=123456789&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=4;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
pirDetection = detection;
pirLevel = detection;
return true;
}
}
}
return false;
}
///移动侦测等级
Future<bool> setAlarmMotionDetection(bool enable, int level,
{int timeout = 5, int? videoDuration}) async {
int plan = 0;
if (enable) {
plan = -1;
}
videoDuration = videoDuration ?? pirCloudVideoDuration;
String cgi =
"set_alarm.cgi?enable_alarm_audio=0&motion_armed=${enable ? 1 : 0}&motion_sensitivity=$level&"
"input_armed=1&ioin_level=0&iolinkage=0&ioout_level=0&preset=0&mail=0&snapshot=1&"
"record=1&upload_interval=0&schedule_enable=1&schedule_sun_0=$plan&schedule_sun_1=$plan&"
"schedule_sun_2=$plan&schedule_mon_0=$plan&schedule_mon_1=$plan&schedule_mon_2=$plan&"
"schedule_tue_0=$plan&schedule_tue_1=$plan&schedule_tue_2=$plan&schedule_wed_0=$plan&"
"schedule_wed_1=$plan&schedule_wed_2=$plan&schedule_thu_0=$plan&schedule_thu_1=$plan&"
"schedule_thu_2=$plan&schedule_fri_0=$plan&schedule_fri_1=$plan&schedule_fri_2=$plan&"
"schedule_sat_0=$plan&schedule_sat_1=$plan&schedule_sat_2=$plan&defense_plan1=0&"
"defense_plan2=0&defense_plan3=0&defense_plan4=0&defense_plan5=0&defense_plan6=0&defense_plan7=0&"
"defense_plan8=0&defense_plan9=0&defense_plan10=0&defense_plan11=0&defense_plan12=0&defense_plan13=0&"
"defense_plan14=0&defense_plan15=0&defense_plan16=0&defense_plan17=0&defense_plan18=0&defense_plan19=0&"
"defense_plan20=0&defense_plan21=0&";
if (videoDuration != -1) {
cgi =
"set_alarm.cgi?enable_alarm_audio=0&motion_armed=${enable ? 1 : 0}&motion_sensitivity=$level&CloudVideoDuration=$videoDuration&"
"input_armed=1&ioin_level=0&iolinkage=0&ioout_level=0&preset=0&mail=0&snapshot=1&"
"record=1&upload_interval=0&schedule_enable=1&schedule_sun_0=$plan&schedule_sun_1=$plan&"
"schedule_sun_2=$plan&schedule_mon_0=$plan&schedule_mon_1=$plan&schedule_mon_2=$plan&"
"schedule_tue_0=$plan&schedule_tue_1=$plan&schedule_tue_2=$plan&schedule_wed_0=$plan&"
"schedule_wed_1=$plan&schedule_wed_2=$plan&schedule_thu_0=$plan&schedule_thu_1=$plan&"
"schedule_thu_2=$plan&schedule_fri_0=$plan&schedule_fri_1=$plan&schedule_fri_2=$plan&"
"schedule_sat_0=$plan&schedule_sat_1=$plan&schedule_sat_2=$plan&defense_plan1=0&"
"defense_plan2=0&defense_plan3=0&defense_plan4=0&defense_plan5=0&defense_plan6=0&defense_plan7=0&"
"defense_plan8=0&defense_plan9=0&defense_plan10=0&defense_plan11=0&defense_plan12=0&defense_plan13=0&"
"defense_plan14=0&defense_plan15=0&defense_plan16=0&defense_plan17=0&defense_plan18=0&defense_plan19=0&"
"defense_plan20=0&defense_plan21=0&";
}
bool ret = await writeCgi(cgi, timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24588;
}, timeout);
if (result.isSuccess) {
pirCloudVideoDuration = videoDuration;
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
///人形侦测等级
///关-----0
///高-----1
///中-----2
///低-----3
Future<bool> setHumanDetectionLevel(int sensitive, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2126&command=0&sensitive=$sensitive&&mark=123456789&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
humanLevel = sensitive;
return true;
}
}
}
return false;
}
Future<bool> getHumanDetectionLevel({int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2126&command=1&mark=123456789&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
humanLevel = int.tryParse(data["sensitive"] ?? "") ?? 0;
return true;
}
}
}
return false;
}
///获取pir视频录像状态(人体侦测)
Future<bool> getAlarmPirVideoPush({int timeout = 5}) async {
bool ret = await writeCgi("trans_cmd_string.cgi?cmd=2106&command=8&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=8;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
pushEnable = int.tryParse(data["pirPushSwitch"] ?? "") ?? 0;
videoEnable = int.tryParse(data["pirPushSwitchVideo"] ?? "") ?? 0;
if (data.containsKey("CloudVideoDuration")) {
videoDuration =
int.tryParse(data["CloudVideoDuration" ?? "15"]) ?? -1;
} else {
videoDuration = -1;
}
if (data.containsKey("autoRecordModey")) {
autoRecordVideoMode =
int.tryParse(data["autoRecordMode" ?? "0"]) ?? -1;
} else {
autoRecordVideoMode = 0;
}
}
return data["result"] == "0";
}
}
return false;
}
///设置pir视频录像状态(人体侦测)
Future<bool> setAlarmPirVideoPush(
{int? pirPushEnable,
int? pirVideoEnable,
int? pirVideoDuration,
int? autoRecordMode,
int timeout = 5}) async {
int? pushEnable1 = pirPushEnable ?? pushEnable;
int? videoEnable1 = pirVideoEnable ?? videoEnable;
int? videoDuration1 = pirVideoDuration ?? videoDuration;
int? autoRecordMode1 = autoRecordMode ?? autoRecordVideoMode;
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=9&pirPushSwitch=${pushEnable1 == 1 ? 1 : 0}&pirPushSwitchVideo=${videoEnable1 == 1 ? 1 : 0}&CloudVideoDuration=${videoDuration1 ?? 15}&autoRecordMode=${autoRecordMode1 ?? 0}&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=9;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
pushEnable = pushEnable1;
videoEnable = videoEnable1;
videoDuration = videoDuration1;
autoRecordVideoMode = autoRecordMode1;
return true;
}
}
}
return false;
}
///获取侦测距离
Future<bool> getDetectionRange({int timeout = 5}) async {
bool ret = await writeCgi("trans_cmd_string.cgi?cmd=2106&command=3&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=3;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
distanceAdjust = int.tryParse(data["DistanceAdjust"] ?? "0") ?? 0;
pirLevel = int.tryParse(data["humanDetection"] ?? "") ?? 0;
humanoidDetection =
int.tryParse(data["HumanoidDetection"] ?? "") ?? 0;
return true;
}
}
}
return false;
}
///设置侦测距离
Future<bool> setDetectionRange(int distance, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=4&humanDetection=$pirLevel&DistanceAdjust=$distance&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=4;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
distanceAdjust = distance;
return true;
}
}
}
return false;
}
///设置人形检测开关
Future<bool> setHuanoidDetection(int value, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=4&humanDetection=$pirLevel&DistanceAdjust=$distanceAdjust&HumanoidDetection=$value&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=4;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
humanDetection = value; //note
humanoidDetection = value;
return true;
}
}
}
return false;
}
///获取报警计划
///type == 2 报警计划
Future<bool> getAlarmPlan(int command, int type, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&type=$type&mark=123456789&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=11;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
if (data["motion_push_enable"] == "0") {
pirPushEnable = false;
} else {
pirPushEnable = true;
}
//currentAlarmType = int.parse(data["motion_push_enable"]);
return true;
}
}
}
return false;
}
///设置报警计划
///command == 2 报警计划
Future<bool> setAlarmPlan(
int command,
int motion_push_plan_enable,
int motion_push_plan1,
int motion_push_plan2,
int motion_push_plan3,
int motion_push_plan4,
int motion_push_plan5,
int motion_push_plan6,
int motion_push_plan7,
int motion_push_plan8,
int motion_push_plan9,
int motion_push_plan10,
int motion_push_plan11,
int motion_push_plan12,
int motion_push_plan13,
int motion_push_plan14,
int motion_push_plan15,
int motion_push_plan16,
int motion_push_plan17,
int motion_push_plan18,
int motion_push_plan19,
int motion_push_plan20,
int motion_push_plan21,
{int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=2&mark=10611404&motion_push_plan1=$motion_push_plan1&motion_push_plan2=$motion_push_plan2&motion_push_plan3=$motion_push_plan3&motion_push_plan4=$motion_push_plan4&motion_push_plan5=$motion_push_plan5&motion_push_plan6=$motion_push_plan6&motion_push_plan7=$motion_push_plan7"
"&motion_push_plan8=$motion_push_plan8&motion_push_plan9=$motion_push_plan9&motion_push_plan10=$motion_push_plan10&motion_push_plan11=$motion_push_plan11&motion_push_plan12=$motion_push_plan12&motion_push_plan13=$motion_push_plan13&motion_push_plan14=$motion_push_plan14&motion_push_plan15=$motion_push_plan15"
"&motion_push_plan16=$motion_push_plan16&motion_push_plan17=$motion_push_plan17&motion_push_plan18=$motion_push_plan18&motion_push_plan19=$motion_push_plan19&motion_push_plan20=$motion_push_plan20&motion_push_plan21=$motion_push_plan21&motion_push_plan_enable=$motion_push_plan_enable&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
///设置报警计划
///command == 2 报警计划
Future<bool> setAlarmMotion(int motion_push_plan_enable,
{int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=2&mark=10672216&motion_push_plan1=0&motion_push_plan2=0&motion_push_plan3=0&motion_push_plan4=0&motion_push_plan5=0&motion_push_plan6=0&motion_push_plan7=0&motion_push_plan8=0&motion_push_plan9=0&motion_push_plan10=0&motion_push_plan11=0&motion_push_plan12=0&motion_push_plan13=0&motion_push_plan14=0&motion_push_plan15=0&motion_push_plan16=0&motion_push_plan17=0&motion_push_plan18=0&motion_push_plan19=0&motion_push_plan20=0&motion_push_plan21=0&motion_push_plan_enable=$motion_push_plan_enable&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["motion_push_enable"] == "0") {
pirPushEnable = false;
} else if (data["motion_push_enable"] == "1") {
pirPushEnable = true;
}
return true;
}
}
return false;
}
///获取绘制区域
///command ===1 /移动侦测区域
///command ===3 /人形侦测区域
Future<bool> getAlarmZone(int command, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2123&command=$command&mark=123456789&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2123;") && str.contains("command=$command;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
///设置绘制区域
///command ===1 /移动侦测区域
///command ===3 /人形侦测区域
Future<bool> setAlarmZone(
int command,
int pd_reign0,
int pd_reign1,
int pd_reign2,
int pd_reign3,
int pd_reign4,
int pd_reign5,
int pd_reign6,
int pd_reign7,
int pd_reign8,
int pd_reign9,
int pd_reign10,
int pd_reign11,
int pd_reign12,
int pd_reign13,
int pd_reign14,
int pd_reign15,
int pd_reign16,
int pd_reign17,
{int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2123&command=$command&pd_reign0=$pd_reign0&pd_reign1=$pd_reign1&pd_reign2=$pd_reign2&pd_reign3=$pd_reign3&pd_reign4=$pd_reign4&pd_reign5=$pd_reign5&pd_reign6=$pd_reign6&pd_reign7=$pd_reign7&pd_reign8=$pd_reign8&pd_reign9=$pd_reign9&pd_reign10=$pd_reign10&pd_reign11=$pd_reign11&pd_reign12=$pd_reign12&pd_reign13=$pd_reign13&pd_reign14=$pd_reign14&pd_reign15=$pd_reign15&pd_reign16=$pd_reign16&pd_reign17=$pd_reign17&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2126;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
}
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
import 'package:vsdk/camera_device/commands/status_command.dart';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'package:vsdk/p2p_device/p2p_connect.dart';
import 'package:vsdk/p2p_device/p2p_device.dart';
mixin CameraCommand on P2PBasisDevice, P2PConnect, P2PCommand, StatusCommand {
@override
Future<StatusResult?> getStatus({int timeout = 5, bool cache = true}) async {
StatusResult? result =
await super.getStatus(timeout: timeout, cache: cache);
if (result != null) {
await cameraCommand(result);
if (result.sys_ver != null) {
if (result.sys_ver!.startsWith('48.')) {
if (sirenCommand != null) {
bool bl = await sirenCommand!.getLightSirenMode();
if (bl == true) {
if (sirenCommand!.sirenMode == true) {
await sirenCommand!.controlSirenMode(false);
}
}
}
}
}
}
return result;
}
Future<void> cameraCommand(StatusResult result) async {
if (result.haveMotor == "1" && motorCommand == null) {
motorCommand = MotorCommand(this);
}
int? supportMode = int.tryParse(result.hardwareTestFunc ?? "");
if (supportMode != null &&
supportMode & 0x04 != 0 &&
lightCommand == null) {
lightCommand = LightCommand(this);
}
//预置位巡航计划、巡航线
if (result.support_presetCruise == "1" && presetCruiseCommand == null) {
presetCruiseCommand = PresetCruiseCommand(this);
}
//红蓝灯
if (supportMode != null &&
supportMode & 0x0A != 0 &&
redBlueLightCommand == null) {
redBlueLightCommand = RedBlueLightCommand(this);
}
if (supportMode != null &&
supportMode & 0x08 != 0 &&
sirenCommand == null) {
sirenCommand = SirenCommand(this);
}
if (result.haveWifi == "1" && wifiCommand == null) {
wifiCommand = WifiCommand(this);
}
if (result.support_led_hidden_mode == "1" && ledCommand == null) {
ledCommand = LedCommand(this);
}
if (result.support_low_power != null &&
result.support_low_power != "0" &&
powerCommand == null) {
powerCommand = PowerCommand(this);
}
if (((result.support_mode_switch != null &&
result.support_mode_switch != "0") ||
["3", "4", "5", "6"].contains(result.support_low_power)) &&
dvrCommand == null) {
dvrCommand = DVRCommand(this);
}
if (result.support_4G_module != null &&
result.support_4G_module != "0" &&
mobileCommand == null) {
mobileCommand = MobileCommand(this);
mobileCommand!.getMobileInfo();
}
if (passwordCommand == null) {
passwordCommand = PasswordCommand(this, result);
}
if (multipleZoomCommand == null) {
multipleZoomCommand = MultipleZoomCommand(this);
}
if (result.support_privacy_pos != null &&
int.tryParse(result.support_privacy_pos!)! > 0 &&
privacyPositionCommand == null) {
privacyPositionCommand = PrivacyPositionCommand(this);
}
if (result.support_Remote_PowerOnOff_Switch != null &&
int.tryParse(result.support_Remote_PowerOnOff_Switch!)! > 0 &&
powerSwitchCommand == null) {
powerSwitchCommand = PowerSwitchCommand(this);
}
if (result.support_WiFi_Enhanced_Mode != null &&
int.tryParse(result.support_WiFi_Enhanced_Mode!)! > 0 &&
wifiEnhancedModeCommand == null) {
wifiEnhancedModeCommand = WifiEnhancedModeCommand(this);
}
//支持新的定时白光灯/红外灯控制
if (result.support_WhiteLed_Ctrl != null &&
int.tryParse(result.support_WhiteLed_Ctrl!)! > 0 &&
whiteLedCommand == null) {
whiteLedCommand = WhiteLedCommand(this);
}
//检测间隔
if (result.support_SleepCheckInterval != null &&
int.tryParse(result.support_SleepCheckInterval!)! > 0 &&
checkIntervalCommand == null) {
checkIntervalCommand = CheckIntervalCommand(this);
}
//徘徊检测
if (result.support_LingerCheck != null &&
int.tryParse(result.support_LingerCheck!)! > 0 &&
checkIntervalCommand == null) {
checkIntervalCommand = CheckIntervalCommand(this);
}
//门铃解除防拆报警
if (result.support_tamper_setting != null &&
int.tryParse(result.support_tamper_setting!)! > 0 &&
dismantleCommand == null) {
dismantleCommand = DismantleCommand(this);
}
//tf录像分辨率切换
if (result.support_record_resolution_switch != null &&
int.tryParse(result.support_record_resolution_switch!)! > 0 &&
recordResolutionCommand == null) {
recordResolutionCommand = RecordResolutionCommand(this);
}
if ((result.support_pininpic != null &&
int.tryParse(result.support_pininpic!)! > 0 ||
result.support_mutil_sensor_stream != null &&
int.tryParse(result.support_mutil_sensor_stream!)! > 0) &&
qiangQiuCommand == null) {
qiangQiuCommand = QiangQiuCommand(this);
}
}
Future<bool> updateOwnerPassword(String userId, String password,
{int timeout = 5}) async {
bool ret = await writeCgi(
"set_users.cgi?pwd_change_realtime=1&OwnerUser=$userId&OwnerPwd=$password&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24590;
}, timeout);
if (result.isSuccess) {
return true;
}
}
return false;
}
Future<bool> updateAdminPassword(String password,
{required int userid, int timeout = 5}) async {
bool ret = await writeCgi(
"set_users.cgi?pwd_change_realtime=1&user3=admin&pwd3=$password&app_id=${userid}&",
timeout: timeout,
needLogin: false);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24590;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> set_ipc_binding(String json, {int timeout = 5}) async {
bool ret = await writeCgi("set_ipc_binding_info.cgi?binding_body=${json}&",
timeout: timeout, needLogin: false);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24590 || cmd == 28938 || cmd == 28933;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
/// 重启指令
Future<bool> reboot({int timeout = 5}) async {
bool ret = await writeCgi("reboot.cgi?", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24615;
}, timeout);
if (result.isSuccess) {
return true;
}
}
return false;
}
/// 重置指令
Future<bool> restoreFactory({int timeout = 5}) async {
bool ret = await writeCgi("restore_factory.cgi&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return true;
}, timeout);
if (result.isSuccess) {
return true;
}
}
return false;
}
/// 云存储授权
Future<bool> updatePushUser({int timeout = 5}) async {
bool ret = await writeCgi("set_update_push_user.cgi?", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return true;
}, timeout);
if (result.isSuccess) {
return true;
}
}
return false;
}
Future<bool> updateDateTime({int timeout = 5}) async {
DateTime now = DateTime.now();
int tz = now.timeZoneOffset.inSeconds;
bool ret = await writeCgi(
"set_datetime.cgi?tz=${-tz}&ntp_enable=1&ntp_svr=time.windows.com&now=${(now.millisecondsSinceEpoch / 1000).floor()}&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24595;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
///修改水印时间格式
Future<bool> changeDateTimeType(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=34&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
return result.isSuccess;
}
return false;
}
Future<bool> updateFirmware(String server, String file,
{int timeout = 5}) async {
bool ret = await writeCgi(
"auto_download_file.cgi?server=$server&file=$file&type=0&resevered1=&resevered2=&resevered3=&resevered4=&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24578;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
///app版本号传给固件
Future<bool> setAppVersionOemId(
String appOemid, String appVersion, int aacSupport,
{int timeout = 5}) async {
bool ret = await writeCgi(
"set_users.cgi?app_oemid=$appOemid&app_version=$appVersion&aac_support=$aacSupport&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24578;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
///修改设备名字
Future<bool> setDeviceName(String name, {int timeout = 5}) async {
bool ret = await writeCgi("set_alias.cgi?alias=$name&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24591;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
///双目切换镜头
Future<bool> changeCameraLens(String value, {int timeout = 5}) async {
bool ret = await writeCgi("camera_control.cgi?param=36&value=$value&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
var videoCallStatus = 0;
///设置视频呼叫设备状态 0 待接听 1 已接听 2 已挂断
Future<bool> configVideoCallStatus(int value, {int timeout = 5}) async {
bool ret =
await writeCgi("callstatus.cgi?status=$value&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 29184;
}, timeout);
// VPLog.file(
// "configVideoCallStatus value:$value isSuccess:${result?.isSuccess} data:${result?.getMap()}",
// tag: "CMD");
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
videoCallStatus = int.tryParse(data["status"] ?? "0") ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
int sleepFlag = 0;
int sleepTime = 60;
int videoCallScreenBrightness = 50;
int blackMode = 0;
///获取带屏设备熄屏时间
Future<bool> getVideoCallScreenSleepTime({int timeout = 5}) async {
bool ret = await writeCgi("trans_cmd_string.cgi?cmd=4104&command=0&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4104;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>获取带屏设备熄屏时间:$data');
sleepFlag = int.tryParse(data["sleepflag"] ?? "0") ?? 0;
sleepTime = int.tryParse(data["sleeptime"] ?? "60") ?? 60;
return data["result"] == "0";
}
}
return false;
}
///设置带屏设备熄屏时间
Future<bool> setVideoCallScreenSleepTime(int flag, int time,
{int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=4104&command=1&sleepflag=$flag&sleeptime=$time&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4104;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>设置带屏设备熄屏时间结果:$data');
sleepFlag = int.tryParse(data["sleepflag"] ?? "0") ?? 0;
sleepTime = int.tryParse(data["sleeptime"] ?? "60") ?? 60;
return data["result"] == "0";
}
}
return false;
}
Future<bool> setVideoCallScreenBrightness(int value,
{int timeout = 5}) async {
if (value > 100) value = 100;
if (value < 0) value = 0;
bool ret = await writeCgi("display_pwm_ctrl.cgi?common=1&value=$value&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 0x7206;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
print('==>>设置亮度的结果:$data');
videoCallScreenBrightness = int.tryParse(data["value"] ?? "50") ?? 50;
return data["result"] == "0";
}
}
return false;
}
Future<bool> getVideoCallScreenBrightness({int timeout = 5}) async {
bool ret =
await writeCgi("display_pwm_ctrl.cgi?common=0&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 0x7206;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
print('==>>获取亮度的结果:$data');
videoCallScreenBrightness = int.tryParse(data["value"] ?? "50") ?? 50;
return data["result"] == "0";
}
}
return false;
}
Future<bool> getVideoCallScreenMode({int timeout = 5}) async {
bool ret = await writeCgi("trans_cmd_string.cgi?cmd=4106&command=0&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4106;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
print('==>>获取到的屏幕Mode:$data');
if (data.containsKey("black_mode")) {
blackMode = int.tryParse(data["black_mode"] ?? "0") ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setVideoCallScreenMode(int value, {int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=4106&command=1&black_mode=$value&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4106;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
print('==>>设置取到的屏幕Mode:$data');
if (data.containsKey("black_mode")) {
blackMode = int.tryParse(data["black_mode"] ?? "0") ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
MotorCommand? motorCommand;
WifiCommand? wifiCommand;
LedCommand? ledCommand;
LightCommand? lightCommand;
SirenCommand? sirenCommand;
PowerCommand? powerCommand;
DVRCommand? dvrCommand;
MobileCommand? mobileCommand;
PasswordCommand? passwordCommand;
RedBlueLightCommand? redBlueLightCommand;
MultipleZoomCommand? multipleZoomCommand;
PrivacyPositionCommand? privacyPositionCommand;
PowerSwitchCommand? powerSwitchCommand;
WifiEnhancedModeCommand? wifiEnhancedModeCommand;
WhiteLedCommand? whiteLedCommand;
CheckIntervalCommand? checkIntervalCommand;
DismantleCommand? dismantleCommand;
RecordResolutionCommand? recordResolutionCommand;
PresetCruiseCommand? presetCruiseCommand;
QiangQiuCommand? qiangQiuCommand;
}
class PrivacyPositionCommand {
final CameraCommand _command;
PrivacyPositionCommand(this._command);
bool? privacyFlag;
//设备是否在隐私位置
Future<bool> isDevicePrivacy({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2013&command=2&posturn=1&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2013;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
privacyFlag = data["privacyflag"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
//进入或退出隐私位
Future<bool> controlPrivacyPosStatus(bool value, {int timeout = 5}) async {
var switchValue = value ? '1' : '3';
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2013&command=1&posturn=$switchValue&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2013;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Map? privacyPlanData;
Future<bool> getPrivacyPlan({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=212&type=6&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") &&
str.contains("command=11;") &&
str.contains("type=6;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("privacy_plan_enable")) {
privacyPlanData = data;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setPrivacyPlan(
{required List records, required int enable, int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=6&mark=212&"
"privacy_plan1=${records[0]}&"
"privacy_plan2=${records[1]}&"
"privacy_plan3=${records[2]}&"
"privacy_plan4=${records[3]}&"
"privacy_plan5=${records[4]}&"
"privacy_plan6=${records[5]}&"
"privacy_plan7=${records[6]}&"
"privacy_plan8=${records[7]}&"
"privacy_plan9=${records[8]}&"
"privacy_plan10=${records[9]}&"
"privacy_plan11=${records[10]}&"
"privacy_plan12=${records[11]}&"
"privacy_plan13=${records[12]}&"
"privacy_plan14=${records[13]}&"
"privacy_plan15=${records[14]}&"
"privacy_plan16=${records[15]}&"
"privacy_plan17=${records[16]}&"
"privacy_plan18=${records[17]}&"
"privacy_plan19=${records[18]}&"
"privacy_plan20=${records[19]}&"
"privacy_plan21=${records[20]}&"
"privacy_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=6;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0" || data["result"] == "0") {
return true;
}
}
}
return false;
}
}
class MultipleZoomCommand {
final CameraCommand _command;
MultipleZoomCommand(this._command);
Future<bool> multipleZoomCommand(int scale, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=84&param=$scale&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
Future<bool> multipleZoom4XCommand(int scale, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=${scale + 20}&onestep=0&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
}
//定时白光灯红外灯的控制
class WhiteLedCommand {
final CameraCommand _command;
WhiteLedCommand(this._command);
int? whiteLed;
int? ledTimes;
int? mode;
//设置白光灯参数 亮度 时长 模式
Future<bool> controlWhiteLedValue(
{int? whiteValue,
int? timeValue,
int? modeValue,
int timeout = 5}) async {
if (whiteValue == null && timeValue == null && modeValue == null) {
return false;
}
bool? ret;
if (whiteValue != null && timeValue == null && modeValue == null) {
ret = await _command.writeCgi(
"set_whiteled_value.cgi?whiteled=$whiteValue&",
timeout: timeout);
}
if (whiteValue == null && timeValue != null && modeValue == null) {
ret = await _command.writeCgi(
"set_whiteled_value.cgi?ledtimes=$timeValue&",
timeout: timeout);
}
if (whiteValue == null && timeValue == null && modeValue != null) {
ret = await _command.writeCgi("set_whiteled_value.cgi?mode=$modeValue&",
timeout: timeout);
}
if (whiteValue != null && timeValue != null && modeValue == null) {
ret = await _command.writeCgi(
"set_whiteled_value.cgi?whiteled=$whiteValue&ledtimes=$timeValue&",
timeout: timeout);
}
if (whiteValue == null && timeValue != null && modeValue != null) {
ret = await _command.writeCgi(
"set_whiteled_value.cgi?ledtimes=$timeValue&mode=$modeValue&",
timeout: timeout);
}
if (whiteValue != null && timeValue == null && modeValue != null) {
ret = await _command.writeCgi(
"set_whiteled_value.cgi?whiteled=$whiteValue&mode=$modeValue&",
timeout: timeout);
}
if (whiteValue != null && timeValue != null && modeValue != null) {
ret = await _command.writeCgi(
"set_whiteled_value.cgi?whiteled=$whiteValue&ledtimes=$timeValue&mode=$modeValue&",
timeout: timeout);
}
if (ret ?? false) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return true;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
//获取白光灯参数 亮度 时长 模式
Future<bool> getWhiteLedValue({int timeout = 5}) async {
bool ret =
await _command.writeCgi("get_whiteled_value.cgi?", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return true;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
whiteLed = int.tryParse(data["whiteled"] ?? "0") ?? 0;
ledTimes = int.tryParse(data["ledtimes"] ?? "0") ?? 0;
// mode = int.tryParse(data["mode"]??'') ?? 0;
return data["result"] == "0";
}
}
return false;
}
//定时计划
Map? whiteLedLightPlanData;
Future<bool> getWhiteLedLightPlan({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=213&type=5&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=11;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("light_plan_enable")) {
whiteLedLightPlanData = data;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setWhiteLedLightPlan(
{required List records, int enable = 1, int timeout = 5}) async {
//计算enable和switch的值
var enableValue = 0;
var switchValue = 0;
var enableList = [];
var switchList = [];
for (int i = 0; i <= 20; i++) {
var tempEnable = records[i]['enable'];
var tempSwitch = records[i]['onOff'];
enableList.add(tempEnable);
switchList.add(tempSwitch);
}
var enableString = enableList.reversed
.toString()
.replaceAll(',', '')
.replaceAll(' ', '')
.replaceAll('(', '')
.replaceAll(')', '');
var switchSting = switchList.reversed
.toString()
.replaceAll(',', '')
.replaceAll(' ', '')
.replaceAll('(', '')
.replaceAll(')', '');
enableValue = int.parse(enableString, radix: 2);
switchValue = int.parse(switchSting, radix: 2);
var commandString = "trans_cmd_string.cgi?cmd=2017&command=5&mark=213&"
"light_plan1=${records[0]['content']}&"
"light_plan2=${records[1]['content']}&"
"light_plan3=${records[2]['content']}&"
"light_plan4=${records[3]['content']}"
"light_plan5=${records[4]['content']}&"
"light_plan6=${records[5]['content']}&"
"light_plan7=${records[6]['content']}&"
"light_plan8=${records[7]['content']}&"
"light_plan9=${records[8]['content']}&"
"light_plan10=${records[9]['content']}&"
"light_plan11=${records[10]['content']}&"
"light_plan12=${records[11]['content']}&"
"light_plan13=${records[12]['content']}&"
"light_plan14=${records[13]['content']}&"
"light_plan15=${records[14]['content']}&"
"light_plan16=${records[15]['content']}&"
"light_plan17=${records[16]['content']}&"
"light_plan18=${records[17]['content']}&"
"light_plan19=${records[18]['content']}&"
"light_plan20=${records[19]['content']}&"
"light_plan21=${records[20]['content']}&"
"light_enable=$enableValue&"
"light_switch=$switchValue&"
"light_plan_enable=$enable&";
// var commandString = "trans_cmd_string.cgi?cmd=2017&command=5&mark=213&"
// "light_plan1=${records[0]['content']}&enable1=${records[0]['enable']}&"
// "light_plan2=${records[1]['content']}&enable2=${records[1]['enable']}&"
// "light_plan3=${records[2]['content']}&enable3=${records[2]['enable']}&"
// "light_plan4=${records[3]['content']}&enable4=${records[3]['enable']}&"
// "light_plan5=${records[4]['content']}&enable5=${records[4]['enable']}&"
// "light_plan6=${records[5]['content']}&enable6=${records[5]['enable']}&"
// "light_plan7=${records[6]['content']}&enable7=${records[6]['enable']}&"
// "light_plan8=${records[7]['content']}&enable8=${records[7]['enable']}&"
// "light_plan9=${records[8]['content']}&enable9=${records[8]['enable']}&"
// "light_plan10=${records[9]['content']}&enable10=${records[9]['enable']}&"
// "light_plan11=${records[10]['content']}&enable11=${records[10]['enable']}&"
// "light_plan12=${records[11]['content']}&enable12=${records[11]['enable']}&"
// "light_plan13=${records[12]['content']}&enable13=${records[12]['enable']}&"
// "light_plan14=${records[13]['content']}&enable14=${records[13]['enable']}&"
// "light_plan15=${records[14]['content']}&enable15=${records[14]['enable']}&"
// "light_plan16=${records[15]['content']}&enable16=${records[15]['enable']}&"
// "light_plan17=${records[16]['content']}&enable17=${records[16]['enable']}&"
// "light_plan18=${records[17]['content']}&enable18=${records[17]['enable']}&"
// "light_plan19=${records[18]['content']}&enable19=${records[18]['enable']}&"
// "light_plan20=${records[19]['content']}&enable20=${records[19]['enable']}&"
// "light_plan21=${records[20]['content']}&enable21=${records[20]['enable']}&"
// "light_plan_enable=${enable}&";
bool ret = await _command.writeCgi(commandString, timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=5;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0") {
return true;
}
}
}
return false;
}
}
//预置位巡航线路
class PresetCruiseCommand {
final CameraCommand _command;
PresetCruiseCommand(this._command);
Map? presetCruiseLineData;
int? sumPreset;
//获取路线
Future<bool> getPresetCruiseLine({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2160&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2160;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("sumPreset")) {
presetCruiseLineData = data;
sumPreset = int.tryParse(data['sumPreset']) ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
//设置路线
Future<bool> setPresetCruiseLine(
{required List records, int timeout = 5}) async {
var cmdString = 'trans_cmd_string.cgi?cmd=2160&command=0&';
if (records == null) {
cmdString += 'sumPreset=0&';
} else {
cmdString += 'sumPreset=${records.length}&';
for (int i = 0; i < records.length; i++) {
var num = records[i]['num'];
var speed = records[i]['speed'];
var time = records[i]['time']; //停留时间
var flag = i + 1;
cmdString += 'preset${flag}_num=$num&';
cmdString += 'preset${flag}_speed=$speed&';
cmdString += 'preset${flag}_stoptime=$time&';
}
}
bool ret = await _command.writeCgi(cmdString, timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2160;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
//定时计划
Map? presetCruisePlanData;
int? presetCruisePlanEnable;
Future<bool> getPresetCruisePlan({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=781698&type=10&status=0&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=11;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("preset_cruise_plan_enable")) {
presetCruisePlanData = data;
presetCruisePlanEnable =
int.tryParse(data["preset_cruise_plan_enable"]) ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setPresetCruisePlan(
{required List records, required int enable, int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=10&mark=781698&"
"preset_cruise_plan1=${records[0]}&"
"preset_cruise_plan2=${records[1]}&"
"preset_cruise_plan3=${records[2]}&"
"preset_cruise_plan4=${records[3]}&"
"preset_cruise_plan5=${records[4]}&"
"preset_cruise_plan6=${records[5]}&"
"preset_cruise_plan7=${records[6]}&"
"preset_cruise_plan8=${records[7]}&"
"preset_cruise_plan9=${records[8]}&"
"preset_cruise_plan10=${records[9]}&"
"preset_cruise_plan11=${records[10]}&"
"preset_cruise_plan12=${records[11]}&"
"preset_cruise_plan13=${records[12]}&"
"preset_cruise_plan14=${records[13]}&"
"preset_cruise_plan15=${records[14]}&"
"preset_cruise_plan16=${records[15]}&"
"preset_cruise_plan17=${records[16]}&"
"preset_cruise_plan18=${records[17]}&"
"preset_cruise_plan19=${records[18]}&"
"preset_cruise_plan20=${records[19]}&"
"preset_cruise_plan21=${records[20]}&"
"preset_cruise_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=10;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
var presetCruiseLinePointsData; //已经设置预置位的数据
//获取已设置的预置位
Future<bool> getPresetCruiseLinePoints({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2161&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2161;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
presetCruiseLinePointsData = data;
return data["result"] == "0";
}
}
return false;
}
}
///红蓝光指令
class RedBlueLightCommand {
final CameraCommand _command;
RedBlueLightCommand(this._command);
bool? redBlueSwitch;
bool? redBlueMode;
//手动
Future<bool> getRedBlueLightStatus({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2109&command=2&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2109;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
redBlueSwitch = data["alarmLedStatus"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlRedBlueLightStatus(int value, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2109&command=0&alarmLed=$value&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2109;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
redBlueSwitch = data["alarmLedStatus"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
//报警
Future<bool> getRedBlueLightMode({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2108&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2108;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
redBlueMode = data["alarmLedMode"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlRedBlueLightMode(int mode, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2108&command=1&alarmLedMode=$mode&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2108;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
redBlueMode = data["alarmLedMode"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
}
/// 白光灯指令
class LightCommand {
final CameraCommand _command;
LightCommand(this._command);
bool? lightSwitch;
int? lightMode;
Future<bool> controlLight(bool light, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2109&command=0&light=${light == true ? 1 : 0}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2109;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") lightSwitch = data["lightStatus"] == "1";
return data["result"] == "0";
}
}
return false;
}
Future<bool> getLightState({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2109&command=2&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2109;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") lightSwitch = data["lightStatus"] == "1";
return data["result"] == "0";
}
}
return false;
}
///get 白光灯模式 闪烁/不逛
Future<bool> getLightSirenMode({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2108&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2108;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
lightMode = int.tryParse(data["lightMode"]) ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlLightMode(int light, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2108&command=1&lightMode=$light&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2108;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
lightMode = int.tryParse(data["lightMode"]) ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
}
///远程一键开关机指令
class PowerSwitchCommand {
final CameraCommand _command;
PowerSwitchCommand(this._command);
int? powerSwitch;
Future<bool> controlPowerSwitch(int open, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=13&PowerSwitch=$open&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=13;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
powerSwitch = int.tryParse(data["PowerSwitch"] ?? '0') ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> getPowerSwitch({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=14&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=14;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
powerSwitch = int.tryParse(data["PowerSwitch"] ?? '0') ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
}
///WiFi穿墙模式
class WifiEnhancedModeCommand {
final CameraCommand _command;
WifiEnhancedModeCommand(this._command);
int? wifiEnhancedMode;
Future<bool> controlWifiEnhancedMode(int open, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=15&WifiEnhancedMode=$open&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=15;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
wifiEnhancedMode = int.tryParse(data["WifiEnhancedMode"] ?? '0') ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> getWifiEnhancedMode({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=16&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=16;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
wifiEnhancedMode = int.tryParse(data["WifiEnhancedMode"] ?? '0') ?? 0;
}
return data["result"] == "0";
}
}
return false;
}
}
/// 警笛指令
class SirenCommand {
final CameraCommand _command;
SirenCommand(this._command);
bool? sirenSwitch;
bool? sirenMode;
Future<bool> controlSiren(bool siren, {int timeout = 5}) async {
print(
"-----------controlSiren--------siren-$siren----_command-$_command---------");
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2109&command=0&siren=${siren == true ? 1 : 0}&",
timeout: timeout);
print("-----------ret--------$ret---------");
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2109;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") sirenSwitch = data["sirenStatus"] == "1";
return data["result"] == "0";
}
}
return false;
}
Future<bool> getSirenState({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2109&command=2&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2109;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") sirenSwitch = data["sirenStatus"] == "1";
return data["result"] == "0";
}
}
return false;
}
///get 白光灯模式 闪烁/不逛
Future<bool> getLightSirenMode({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2108&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2108;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
sirenMode = data["sirenMode"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlSirenMode(bool siren, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2108&command=1&sirenMode=${siren == true ? 1 : 0}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2108;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
sirenMode = data["sirenMode"] == "1";
}
return data["result"] == "0";
}
}
return false;
}
}
/// 云台指令
class MotorCommand {
final CameraCommand _command;
MotorCommand(this._command);
Future<bool> left({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=4&onestep=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
Future<bool> right({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=6&onestep=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
Future<bool> up({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=0&onestep=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
Future<bool> down({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=2&onestep=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
Future<bool> ptzCorrect({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=25&onestep=0&",
timeout: timeout);
return ret;
}
Future<bool> startLeft(
{int? currBinocular, int? motorSpeed, int timeout = 5}) async {
String _cgi = "decoder_control.cgi?command=4&onestep=0&";
if (currBinocular != null) {
_cgi = _cgi + "curr_binocular=$currBinocular&";
}
if (motorSpeed != null) {
_cgi = _cgi + "motor_speed=$motorSpeed&";
}
bool ret = await _command.writeCgi(_cgi, timeout: timeout);
return ret;
}
Future<bool> startRight(
{int? currBinocular, int? motorSpeed, int timeout = 5}) async {
String _cgi = "decoder_control.cgi?command=6&onestep=0&";
if (currBinocular != null) {
_cgi = _cgi + "curr_binocular=$currBinocular&";
}
if (motorSpeed != null) {
_cgi = _cgi + "motor_speed=$motorSpeed&";
}
bool ret = await _command.writeCgi(_cgi, timeout: timeout);
return ret;
}
Future<bool> startUp(
{int? currBinocular, int? motorSpeed, int timeout = 5}) async {
String _cgi = "decoder_control.cgi?command=0&onestep=0&";
if (currBinocular != null) {
_cgi = _cgi + "curr_binocular=$currBinocular&";
}
if (motorSpeed != null) {
_cgi = _cgi + "motor_speed=$motorSpeed&";
}
bool ret = await _command.writeCgi(_cgi, timeout: timeout);
return ret;
}
Future<bool> startDown(
{int? currBinocular, int? motorSpeed, int timeout = 5}) async {
String _cgi = "decoder_control.cgi?command=2&onestep=0&";
if (currBinocular != null) {
_cgi = _cgi + "curr_binocular=$currBinocular&";
}
if (motorSpeed != null) {
_cgi = _cgi + "motor_speed=$motorSpeed&";
}
bool ret = await _command.writeCgi(_cgi, timeout: timeout);
return ret;
}
Future<bool> stopLeft({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=5&onestep=0&", timeout: timeout);
return ret;
}
Future<bool> stopRight({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=7&onestep=0&", timeout: timeout);
return ret;
}
Future<bool> stopUp({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=1&onestep=0&", timeout: timeout);
return ret;
}
Future<bool> stopDown({int timeout = 5}) async {
bool ret = await _command
.writeCgi("decoder_control.cgi?command=3&onestep=0&", timeout: timeout);
return ret;
}
//开启预置位自动巡航
Future<bool> startPresetCruise({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=22&onestep=0&",
timeout: timeout);
return ret;
}
//停止预置位自动巡航
Future<bool> stopPresetCruise({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=23&onestep=0&",
timeout: timeout);
return ret;
}
//开启上下巡航
Future<bool> startUpAndDown({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=26&onestep=0&",
timeout: timeout);
return ret;
}
//停止上下巡航
Future<bool> stopUpAndDown({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=27&onestep=0&",
timeout: timeout);
return ret;
}
//开启左右巡航
Future<bool> startLeftAndRight({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=28&onestep=0&",
timeout: timeout);
return ret;
}
//停止左右巡航
Future<bool> stopLeftAndRight({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=29&onestep=0&",
timeout: timeout);
return ret;
}
//圆圈循环巡航
Future<bool> startCircleLoop({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=80&onestep=0&",
timeout: timeout);
return ret;
}
//上下循环巡航
Future<bool> startUpAndDownLoop({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=79&onestep=0&",
timeout: timeout);
return ret;
}
//折线循环巡航
Future<bool> startPolylineLoop({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=78&onestep=0&",
timeout: timeout);
return ret;
}
//设置预置位
Future<bool> setPresetLocation(int index, {int timeout = 5}) async {
int? cmd;
switch (index) {
case 0:
cmd = 30;
break;
case 1:
cmd = 32;
break;
case 2:
cmd = 34;
break;
case 3:
cmd = 36;
break;
case 4:
cmd = 38;
break;
default:
break;
}
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=$cmd&onestep=0&",
timeout: timeout);
return ret;
}
//巡航到预置位
Future<bool> toPresetLocation(int index, {int timeout = 5}) async {
int? cmd;
switch (index) {
case 0:
cmd = 31;
break;
case 1:
cmd = 33;
break;
case 2:
cmd = 35;
break;
case 3:
cmd = 37;
break;
case 4:
cmd = 39;
break;
default:
break;
}
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=$cmd&onestep=0&",
timeout: timeout);
return ret;
}
//删除预置位
Future<bool> deletePresetLocation(int index, {int timeout = 5}) async {
int? cmd;
switch (index) {
case 0:
cmd = 62;
break;
case 1:
cmd = 63;
break;
case 2:
cmd = 64;
break;
case 3:
cmd = 65;
break;
case 4:
cmd = 66;
break;
default:
break;
}
bool ret = await _command.writeCgi(
"decoder_control.cgi?command=$cmd&onestep=0&",
timeout: timeout);
return ret;
}
//设置看守位 index 0代表关闭 1-16:对应位置
Future<bool> configCameraSensorGuard(int index, {int timeout = 5}) async {
if (index < 0 || index > 16) return false;
bool ret = await _command.writeCgi(
"set_sensor_preset.cgi?sensorid=255&presetid=$index&",
timeout: timeout);
return ret;
}
var presetCruiseLinePointsData; //已经设置预置位的数据
//获取已设置的预置位
Future<bool> getPresetCruiseLinePoints({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2161&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2161;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
presetCruiseLinePointsData = data;
return data["result"] == "0";
}
}
return false;
}
}
class WiFiInfo {
String? ssid;
String? mac;
String? security;
String? dbm0;
String? dbm1;
String? mode;
String? channel;
String toString() {
return ("ssid:$ssid mac:$mac security:$security mode:$mode channel:$channel dbm0:$dbm0 dbm1:$dbm1");
}
}
class WifiCommand {
final CameraCommand _command;
WifiCommand(this._command);
List<WiFiInfo> wifiList = [];
void _setWIfiInfo(Map data) {
String result = data["result"];
if (result == "0") {
String apNumberStr = data["ap_number"] ?? "";
int apNumber = int.tryParse(apNumberStr) ?? 0;
wifiList.clear();
for (int i = 0; i < apNumber; i++) {
WiFiInfo info = WiFiInfo();
info.ssid = data["ap_ssid[$i]"];
info.mac = data["ap_mac[$i]"];
info.security = data["ap_security[$i]"];
info.dbm0 = data["ap_dbm0[$i]"];
info.dbm1 = data["ap_dbm1[$i]"];
info.mode = data["ap_mode[$i]"];
info.channel = data["ap_channel[$i]"];
wifiList.add(info);
}
}
}
Future<List<WiFiInfo>> wifiScan({bool cache = true, int timeout = 5}) async {
if (cache == true) {
return wifiList;
}
//get_wifi_scan_result
//wifi_scan
bool ret = await _command.writeCgi("wifi_scan.cgi?", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24618;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data == null) {
bool ret2 = await _command.writeCgi("get_wifi_scan_result.cgi?",
timeout: timeout);
if (ret2) {
CommandResult result2 =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24584;
}, timeout);
if (result2.isSuccess) {
Map data2 = result2.getMap();
_setWIfiInfo(data2);
}
}
} else {
_setWIfiInfo(data);
}
}
}
return wifiList;
}
/// 获取设备WIFI
Future<bool> configWiFi(WiFiInfo info, String password,
{int timeout = 5, String area = ""}) async {
String cgiParam =
"ssid=${Uri.encodeQueryComponent(info.ssid ?? "")}&channel=${info.channel}&authtype=${info.security}&wpa_psk=${Uri.encodeQueryComponent(password)}&enable=1&";
if (area != "") {
cgiParam =
"ssid=${Uri.encodeQueryComponent(info.ssid ?? "")}&channel=${info.channel}&authtype=${info.security}&wpa_psk=${Uri.encodeQueryComponent(password)}&enable=1&$area&";
}
bool ret =
await _command.writeCgi("set_wifi.cgi?$cgiParam", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24593;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
return false;
}
return ret;
}
}
/// 摄像机LED灯控制
class LedCommand {
final CameraCommand _command;
LedCommand(this._command);
bool? hideLed;
Future<bool> controlLed(bool hide, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2125&command=0&hide_led_disable=${hide == true ? 1 : 0}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2125;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") hideLed = data["hide_led_disable"] == "1";
return data["result"] == "0";
}
}
return false;
}
Future<bool> getLedState({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2125&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2125;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") hideLed = data["hide_led_disable"] == "1";
return data["result"] == "0";
}
}
return false;
}
}
enum PowerMode {
none,
low,
veryLow,
}
/// 摄像机功耗控制
class PowerCommand {
final CameraCommand _command;
PowerCommand(this._command);
PowerMode? powerMode;
String? chargingNoSleep;
String _getPowerValue(PowerMode mode) {
switch (mode) {
case PowerMode.none:
return "0";
case PowerMode.low:
return "30";
case PowerMode.veryLow:
return "10000";
}
return "30";
}
PowerMode _getPowerMode(String value) {
if (value == "30") {
return PowerMode.low;
} else if (value == "10000") {
return PowerMode.veryLow;
} else {
return PowerMode.none;
}
}
Future<bool> controlPower(PowerMode mode, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=2&lowPower=${_getPowerValue(mode)}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") powerMode = _getPowerMode(data["lowPower"]);
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlChargingNoSleep(String isOpen, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=2&chargingNoSleep=$isOpen&lowPower=${_getPowerValue(powerMode!)}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["chargingNoSleep"] != null) {
chargingNoSleep = data["chargingNoSleep"];
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> getPowerMode({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2106&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") powerMode = _getPowerMode(data["lowPower"]);
if (data["chargingNoSleep"] != null) {
chargingNoSleep = data["chargingNoSleep"];
}
return data["result"] == "0";
}
}
return false;
}
bool? electricitySleepSwitch;
String? electricityThreshold;
///智能电量
Future<bool> getSmartElectricitySleep({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=17&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=17;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
electricitySleepSwitch =
data["Smart_Electricity_Sleep_Switch"] == "1";
if (data["Smart_Electricity_Threshold"] != null) {
electricityThreshold = data["Smart_Electricity_Threshold"];
}
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setSmartElectricitySleep(int enable,
{int timeout = 5, int electricityThreshold = 50}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=18&Smart_Electricity_Sleep_Switch=$enable&Smart_Electricity_Threshold=$electricityThreshold&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=18;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
}
/// 摄像机功耗控制
class DVRCommand {
final CameraCommand _command;
DVRCommand(this._command);
bool? dvrMode;
Future<bool> controlDVR(bool mode, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=12&DvModeSleepSwitch=${mode ? 1 : 0}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=12;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") dvrMode = data["DvModeSleepSwitch"] == "1";
return data["result"] == "0";
}
}
return false;
}
Future<bool> getDVRMode({int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=11&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=11;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") dvrMode = data["DvModeSleepSwitch"] == "1";
return data["result"] == "0";
}
}
return false;
}
}
enum MobileNetwork {
none,
china_mobile, //中国移动
china_unicom, //中国联通
china_telecom, //中国电信
}
typedef MobileNotification = void Function(
P2PBasisDevice device, MobileNetwork? network, int? single, String? iccid);
/// 移动网络指令
class MobileCommand {
final CameraCommand _command;
MobileCommand(this._command);
///移动网络类型
MobileNetwork? mobileNetwork;
///移动网络信号
int? mobileSingle;
///移动卡号ID
String? mobileICCID;
Future<bool> getMobileInfo({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2138&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2138;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
int index = int.tryParse(data["operator"] ?? "0") ?? 0;
if (index < MobileNetwork.values.length) {
mobileNetwork = MobileNetwork.values[index];
}
mobileSingle = int.tryParse(data["signal"] ?? "0") ?? 0;
mobileICCID = data["iccid"];
_command
.notifyListeners<MobileNotification>((MobileNotification func) {
func(_command, mobileNetwork, mobileSingle, mobileICCID);
});
}
return data["result"] == "0";
}
}
return false;
}
}
/// 摄像机修改密码指令
class PasswordCommand {
final CameraCommand _command;
final StatusResult _result;
PasswordCommand(this._command, this._result);
Future<bool> pwdChangeCommand(String password, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"set_users.cgi?&user1=''&user2=''&user3=admin&pwd1=''&pwd2=''&pwd3=$password&",
timeout: timeout);
if (_result.pwd_change_realtime != null &&
_result.pwd_change_realtime == '1') {
ret = await _command.writeCgi(
"set_users.cgi?pwd_change_realtime=1&user1=''&user2=''&user3=admin&pwd1=''&pwd2=''&pwd3=$password&",
timeout: timeout);
}
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return true;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
}
///检测间隔 徘徊检测 humanDetection人体侦测等级不能去掉
class CheckIntervalCommand {
final CameraCommand _command;
CheckIntervalCommand(this._command);
bool? sleepCheckIntervalSwitch;
bool? lingerCheckIntervalSwitch;
int sleepCheckIntervalDuration = 15;
int lingerCheckIntervalDuration = 15;
//获取检测间隔状态
Future<bool> getCheckInterval({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2106&command=3&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=3;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") {
sleepCheckIntervalSwitch = data["SleepCheckIntervalSwitch"] == "1";
lingerCheckIntervalSwitch = data["LingerCheckIntervalSwitch"] == "1";
var sleepDuration = data["SleepCheckIntervalDuration"];
var lingerDuration = data["LingerCheckIntervalSDuration"];
if (sleepDuration != null) {
sleepCheckIntervalDuration = int.tryParse(sleepDuration) ?? 0;
}
if (lingerDuration != null) {
lingerCheckIntervalDuration = int.tryParse(lingerDuration) ?? 0;
}
}
return data["result"] == "0";
}
}
return false;
}
//设置检测间隔状态
Future<bool> setSleepCheckInterval(int enable, int humanDetection,
{int timeout = 5, int duration = 15}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=4&humanDetection=$humanDetection&SleepCheckIntervalSwitch=$enable&SleepCheckIntervalDuration=$duration&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=4;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
//设置徘徊检测
Future<bool> setLingerCheckInterval(int enable, int humanDetection,
{int timeout = 5, int duration = 15}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2106&command=4&humanDetection=$humanDetection&LingerCheckIntervalSwitch=$enable&LingerCheckIntervalSDuration=$duration&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2106;") && str.contains("command=4;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
}
/// 门铃解除防拆报警
class DismantleCommand {
final CameraCommand _command;
DismantleCommand(this._command);
bool? dismantleAlarm;
//cmd = 0 永久设置, cmd = 1 获取, cmd = 2临时设置
Future<bool> controlDismantle(bool dismantl, int command,
{int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2203&command=$command&tamper_alarm=${dismantl == true ? 1 : 0}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2203;") && str.contains("command=$command;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") dismantleAlarm = data["tamper_alarm"] == "1";
return data["result"] == "0";
}
}
return false;
}
Future<bool> getDismantleState({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2203&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2203;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0") dismantleAlarm = data["tamper_alarm"] == "1";
return data["result"] == "0";
}
}
return false;
}
}
/// tf录像分辨率的切换
class RecordResolutionCommand {
final CameraCommand _command;
RecordResolutionCommand(this._command);
int? recordResolut;
// 0-->录像主码流(超高清)
// 1-->录像主码流 (高清)
// 2-->录像子码流(标清)
Future<bool> controlRecordResolution(int resolution,
{int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=2204&command=2&record_resolution=$resolution&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2204;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> getRecordResolutionState({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=2204&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2204;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0" && data["record_resolution"] != null)
recordResolut = int.tryParse(data["record_resolution"]) ?? 0;
return data["result"] == "0";
}
}
return false;
}
}
///枪球机指令
class QiangQiuCommand {
final CameraCommand _command;
int? picconrection_status;
int? gblinkage_enable;
QiangQiuCommand(this._command);
Future<bool> controlFocalPoint(int x_percent, int y_percent,
{int timeout = 5}) async {
bool ret = await _command.writeCgi(
"camera_control.cgi?param=39&value=0&x_percent=${x_percent}&y_percent=${y_percent}");
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlSensorSwitch(int sensor, {int timeout = 5}) async {
bool ret =
await _command.writeCgi("camera_control.cgi?param=38&value=${sensor}&");
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> controlLinkageEnable(int enable, {int timeout = 5}) async {
bool ret = await _command.writeCgi(
"trans_cmd_string.cgi?cmd=4101&command=1&gblinkage_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4101;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>设置抢球开关 返回结果:${data}');
if (data["result"] == "0" && data["gblinkage_enable"] != null)
gblinkage_enable = int.tryParse(data["gblinkage_enable"]) ?? 0;
return data["result"] == "0";
}
}
return false;
}
Future<bool> getLinkageEnable({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=4101&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4101;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
print('==>>获取抢球开关 返回结果:${data}');
if (data["result"] == "0" && data["gblinkage_enable"] != null)
gblinkage_enable = int.tryParse(data["gblinkage_enable"]) ?? 0;
return data["result"] == "0";
}
}
return false;
}
///云台复位进度
Future<bool> qiangqiuPTZReset({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=4100&command=0&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4100;") && str.contains("command=0;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0" && data["picconrection_status"] != null)
picconrection_status =
int.tryParse(data["picconrection_status"]) ?? 0;
return data["result"] == "0";
}
}
return false;
}
///查询复位进度
Future<bool> qiangqiuPTZCheck({int timeout = 5}) async {
bool ret = await _command
.writeCgi("trans_cmd_string.cgi?cmd=4100&command=1&", timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=4100;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess == true) {
Map data = result.getMap();
if (data["result"] == "0" && data["picconrection_status"] != null)
picconrection_status =
int.tryParse(data["picconrection_status"]) ?? 0;
return data["result"] == "0";
}
}
return false;
}
///画面校正
Future<bool> controlRevisePoint(int x_percent, int y_percent,
{int timeout = 5}) async {
bool ret = await _command.writeCgi(
"camera_control.cgi?param=40&value=0&x_percent=${x_percent}&y_percent=${y_percent}");
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<Directory> getDeviceDirectory() async {
Directory dir = await getApplicationDocumentsDirectory();
dir = Directory("${dir.path}/${'qiangji'}");
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
return dir;
}
Future<File> _saveSnapshotFile(Uint8List data, String name) async {
Directory directory = await getDeviceDirectory();
String filePath = '${directory.path}/images/${'qiangji'}${name}';
File file = File(filePath);
if (file.existsSync()) {
file.deleteSync();
}
file.createSync(recursive: true);
file = await file.writeAsBytes(data, mode: FileMode.write, flush: true);
return file;
}
/// 获取截图快照
Future<File?> getSnapshot(String name, {int timeout = 5}) async {
int time = DateTime.now().millisecondsSinceEpoch;
bool ret = await _command.writeCgi("snapshot.cgi?sensor=${name}&",
timeout: timeout);
if (ret) {
CommandResult result =
await _command.waitCommandResult((int cmd, Uint8List data) {
return cmd == 24597;
}, timeout);
if (result.isSuccess && result.data != null && result.data!.length > 0) {
return await _saveSnapshotFile(result.data!, '${time}${"_"}{$name}');
}
}
return null;
}
}
import 'dart:convert';
import 'dart:typed_data';
import 'package:get/get.dart';
import '../../p2p_device/p2p_command.dart';
import 'camera_command.dart';
class RecordFile {
// ignore: non_constant_identifier_names
String? record_name;
// 0 实时录像
// 1 报警录像
// ignore: non_constant_identifier_names
int? record_alarm;
// ignore: non_constant_identifier_names
int record_size = 0;
// ignore: non_constant_identifier_names
int record_cache_size = 0;
// ignore: non_constant_identifier_names
DateTime? record_time;
// ignore: non_constant_identifier_names
bool? record_head;
RecordTimeLineFile? lineFile;
RecordFile.fromData(int i, bool recordHead, Map data,
{bool isTypeSearch = false}) {
this.record_head = recordHead;
if (isTypeSearch == true) {
this.record_name = data["record_name[$i]"];
this.record_size = int.tryParse(data["record_size[$i]"] ?? "0")!;
} else {
this.record_name = data["record_name0[$i]"];
this.record_size = int.tryParse(data["record_size0[$i]"] ?? "0")!;
}
record_alarm = 0;
record_time = DateTime.now();
List<String> splits = this.record_name?.split("_") ?? [];
if (splits.length > 1 && splits[1].contains("010")) {
record_alarm = 1;
} else if (splits.length > 1 && splits[1].contains("011")) {
record_alarm = 2;
} else if (splits.length > 1 && splits[1].contains("012")) {
record_alarm = 3;
} else if (splits.length > 1 && splits[1].contains("013")) {
record_alarm = 4;
} else if (splits.length > 1 && splits[1].contains("014")) {
//区域入侵
record_alarm = 5;
} else if (splits.length > 1 && splits[1].contains("015")) {
//人逗留
record_alarm = 6;
} else if (splits.length > 1 && splits[1].contains("016")) {
//车违停
record_alarm = 7;
} else if (splits.length > 1 && splits[1].contains("017")) {
//越线检测
record_alarm = 8;
} else if (splits.length > 1 && splits[1].contains("018")) {
//离岗检测
record_alarm = 9;
} else if (splits.length > 1 && splits[1].contains("019")) {
//车辆逆行
record_alarm = 10;
} else if (splits.length > 1 && splits[1].contains("020")) {
//包裹
record_alarm = 11;
}
String date = splits[0];
if (date != null) {
int? year = int.tryParse(date.substring(0, 4));
int? month = int.tryParse(date.substring(4, 6));
int? day = int.tryParse(date.substring(6, 8));
int? hour = int.tryParse(date.substring(8, 10));
int? minute = int.tryParse(date.substring(10, 12));
int? second = int.tryParse(date.substring(12, 14));
if (year == null ||
month == null ||
day == null ||
hour == null ||
minute == null ||
second == null) {
return;
}
record_time = DateTime(year, month, day, hour, minute, second);
}
}
String toString() {
return ("record_alarm:$record_alarm "
"record_name:$record_name "
"record_size:$record_size "
"record_time:$record_time");
}
@override
bool operator ==(Object other) {
if (super == other) {
return true;
}
if (other is RecordFile) {
return other.record_name == this.record_name;
}
return false;
}
}
class RecordTimeLineFrame {
/// 关键帧时间戳
DateTime? timestamp;
/// 关键帧序号
// ignore: non_constant_identifier_names
int? frame_no;
/// 帧间隔
// ignore: non_constant_identifier_names
int? frame_gop;
String toString() {
return ("timestamp:$timestamp "
"frame_no:$frame_no "
"frame_gop:$frame_gop ");
}
}
class RecordTimeLineData {
ByteData? bytes;
Uint8List? data;
int? bytesOffset;
int? offset;
RecordTimeLineData(Uint8List data, int offset) {
this.bytes = ByteData.view(data.buffer, offset);
this.data = data;
this.bytesOffset = offset;
this.offset = 0;
}
}
class RecordTimeLineFile {
// ignore: non_constant_identifier_names
String record_name = "";
// ignore: non_constant_identifier_names
int record_time = 0;
// 0 实时录像
// 1 报警录像
// 2 人形报警
// ignore: non_constant_identifier_names
int record_alarm = 0;
/// 录像开始时间
// ignore: non_constant_identifier_names
DateTime? record_start;
/// 录像结束时间
// ignore: non_constant_identifier_names
DateTime? record_end;
///录像时长
// ignore: non_constant_identifier_names
int record_duration = 0;
// ignore: non_constant_identifier_names
int frame_len = 0;
// ignore: non_constant_identifier_names
int frame_interval = 0;
List<RecordTimeLineFrame> frames = [];
static String _fourDigits(int n) {
int absN = n.abs();
String sign = n < 0 ? "-" : "";
if (absN >= 1000) return "$n";
if (absN >= 100) return "${sign}0$absN";
if (absN >= 10) return "${sign}00$absN";
return "${sign}000$absN";
}
static String _twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
static String _eventName(int type) {
switch (type) {
case 0:
return "100";
case 1:
return "010";
case 2:
return "011";
case 3:
return "012";
case 4:
return "013";
case 5:
return "014"; //区域入侵
case 6:
return "015"; //人逗留
case 7:
return "016"; //车违停
case 8:
return "017"; //越线检测
case 9:
return "018"; //离岗检测
case 10:
return "019"; //车辆逆行
case 11:
return "020"; //包裹
}
return "";
}
static String dateString(DateTime dateTime) {
return '${_fourDigits(dateTime.year)}${_twoDigits(dateTime.month)}${_twoDigits(dateTime.day)}';
}
static String dateTimeString(DateTime dateTime) {
return '${_fourDigits(dateTime.year)}${_twoDigits(dateTime.month)}${_twoDigits(dateTime.day)}'
'${_twoDigits(dateTime.hour)}${_twoDigits(dateTime.minute)}${_twoDigits(dateTime.second)}';
}
static RecordTimeLineFile? fromData(RecordTimeLineData timeLineData) {
var bytes = timeLineData.bytes!;
int byteOffset = 0;
int type = bytes.getUint8(byteOffset);
byteOffset += 1;
if ((type & 0x03) != 0x00) {
timeLineData.offset = byteOffset;
return null;
}
RecordTimeLineFile lineFile = RecordTimeLineFile();
lineFile.record_alarm = (type & 0xFC) >> 2;
var timestamp = bytes!.getUint32(byteOffset, Endian.little);
byteOffset += 4;
timeLineData.offset = byteOffset;
var dateTime =
DateTime.fromMillisecondsSinceEpoch(timestamp * 1000, isUtc: true);
lineFile.record_time = timestamp;
lineFile.record_name =
'${dateTimeString(dateTime)}_${_eventName(lineFile.record_alarm!)}.mp4';
lineFile.frame_len = bytes.getUint16(byteOffset, Endian.little);
byteOffset += 2;
timeLineData.offset = byteOffset;
lineFile.frame_interval = bytes.getUint8(byteOffset);
byteOffset += 1;
timeLineData.offset = byteOffset;
for (int i = 0; i < lineFile.frame_len!; i++) {
type = bytes.getInt8(byteOffset);
byteOffset += 1;
timeLineData.offset = byteOffset;
if ((type & 0x03) != 0x01) continue;
RecordTimeLineFrame lineFrame = RecordTimeLineFrame();
timestamp = bytes.getUint32(byteOffset, Endian.little);
byteOffset += 4;
timeLineData.offset = byteOffset;
lineFrame.timestamp =
DateTime.fromMillisecondsSinceEpoch(timestamp * 1000, isUtc: true);
if (lineFile.record_start == null)
lineFile.record_start = lineFrame.timestamp;
lineFrame.frame_no = bytes.getUint16(byteOffset, Endian.little);
byteOffset += 2;
timeLineData.offset = byteOffset;
lineFrame.frame_gop = bytes.getUint8(byteOffset);
if (lineFrame.frame_gop == 45) lineFile.frame_interval = 3;
byteOffset += 1;
timeLineData.offset = byteOffset;
lineFile.frames.add(lineFrame);
}
type = bytes.getInt8(byteOffset);
byteOffset += 1;
timeLineData.offset = byteOffset;
if ((type & 0x03) != 0x02) {
timeLineData.offset = byteOffset;
return null;
}
timestamp = bytes.getUint32(byteOffset, Endian.little);
byteOffset += 4;
timeLineData.offset = byteOffset;
lineFile.record_end =
DateTime.fromMillisecondsSinceEpoch(timestamp * 1000, isUtc: true);
lineFile.record_duration =
lineFile.record_end?.difference(lineFile.record_start!).inSeconds ?? 0;
lineFile.frame_len = bytes.getUint16(byteOffset, Endian.little);
byteOffset += 2;
timeLineData.offset = byteOffset;
bytes.getUint8(byteOffset);
byteOffset += 1;
timeLineData.offset = byteOffset;
return lineFile;
}
String toString() {
return ("record_alarm:$record_alarm "
"record_name:$record_name "
"record_start:$record_start "
"record_end:$record_end "
"record_duration:$record_duration "
"frame_len:$frame_len "
"frame_interval:$frame_interval "
"frames:$frames ");
}
@override
bool operator ==(Object other) {
if (super == other) {
return true;
}
if (other is RecordTimeLineFile) {
return other.record_name == this.record_name;
}
return false;
}
}
class RecordTimeLineDown {
final String name;
final int start;
final int end;
RecordTimeLineDown(this.name, this.start, this.end);
Map getData() {
return {"f": name, "s": start, "e": end};
}
}
mixin CardCommand on CameraCommand {
List<RecordFile> recordFileList = [];
Map<String, List<RecordTimeLineFile>> _lineFileCache = Map();
int pageCount = 1;
void _setRecordInfo(List<RecordFile> files, Map data) {
String result = data["result"];
String supportRecordHead = data["support_record_head"] ?? "0";
if (result == "0") {
String recordNum = data["record_num0"] ?? "";
pageCount = int.tryParse(data["PageCount"] ?? "0")!;
int apNumber = int.tryParse(recordNum) ?? 0;
for (int i = 0; i < apNumber; i++) {
var recordName = data["record_name0[$i]"];
if (recordName == null || recordName.toString().length < 14) {
continue;
}
var recordFile = RecordFile.fromData(i, supportRecordHead == '1', data);
// if(recordFile.record_time.difference(DateTime.now()).inDays > 0){
// //过滤文件时间错误
// continue;
// }
dynamic file = null;
if (recordFileList.isNotEmpty) {
file = recordFileList
.firstWhereOrNull((element) => element == recordFile);
}
if (files.length > 0 && file == null) {
file = files.firstWhereOrNull((element) => element == recordFile);
}
if (file == null)
files.add(recordFile);
else
file.record_size = recordFile.record_size;
}
}
}
Future<List<RecordFile>> _getRecordFile(int pageSize, int pageIndex,
{int timeout = 5}) async {
List<RecordFile> files = [];
if (pageIndex == 0) {
var date = DateTime.now();
var key = RecordTimeLineFile.dateString(date);
await getRecordLineFile(key, cache: false);
}
bool ret = await writeCgi(
"get_record_file.cgi?PageSize=$pageSize&PageIndex=$pageIndex&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24583;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
_setRecordInfo(files, data);
}
}
List<RecordTimeLineFile> lines = [];
for (var item in files) {
var key = item.record_name!.substring(0, 8);
if (_lineFileCache.containsKey(key)) {
lines = _lineFileCache[key]!;
} else {
lines = await getRecordLineFile(key, cache: false);
}
var element = lines.firstWhereOrNull(
(element) => element.record_name == item.record_name);
item.lineFile = element;
}
return files;
}
void _setTypeSeachRecordInfo(List<RecordFile> files, Map data) {
String result = data["result"];
String supportRecordHead = data["support_record_head"];
if (result == "0") {
String recordNum = data["record_filenum"] ?? "0";
int apNumber = int.tryParse(recordNum) ?? 0;
for (int i = 0; i < apNumber; i++) {
var recordName = data["record_name[$i]"];
if (recordName == null || recordName.toString().length < 14) {
continue;
}
var recordFile = RecordFile.fromData(i, supportRecordHead == '1', data,
isTypeSearch: true);
var file =
recordFileList.firstWhereOrNull((element) => element == recordFile);
if (file == null) {
file = files.firstWhereOrNull((element) => element == recordFile);
}
if (file == null)
files.add(recordFile);
else
file.record_size = recordFile.record_size;
}
}
}
///当天所有录像文件 dirname=20230322&
Future<List<RecordFile>> _getTypeSeachRecordFile(String dirname,
{int timeout = 5}) async {
List<RecordFile> files = [];
String _twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
DateTime dateTime = DateTime.now();
String year = dateTime.year.toString();
String month = _twoDigits(dateTime.month);
String day = _twoDigits(dateTime.day);
String dateName = year + month + day;
if (dirname == dateName) {
var key = RecordTimeLineFile.dateString(dateTime);
await getRecordLineFile(key, cache: false);
}
bool ret = await writeCgi(
"get_record_file.cgi?GetType=file&dirname=$dirname&",
timeout: timeout);
if (ret) {
CommandResult result;
while (true) {
result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24583;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
_setTypeSeachRecordInfo(files, data);
String currentPage = data["current_page"];
String totolPage = data["totol_page"];
if (currentPage == null ||
totolPage == null ||
currentPage == totolPage) {
break;
}
} else {
break;
}
}
}
List<RecordTimeLineFile> lines = [];
for (var item in files) {
var key = item.record_name!.substring(0, 8);
if (_lineFileCache.containsKey(key)) {
lines = _lineFileCache[key]!;
} else {
lines = await getRecordLineFile(key, cache: false);
}
var element = lines.firstWhereOrNull(
(element) => element.record_name == item.record_name);
item.lineFile = element;
}
return files;
}
List<RecordTimeLineFile> getAllLineFile() {
List<RecordTimeLineFile> files = [];
_lineFileCache.forEach((key, value) {
files.addAll(value);
});
return files;
}
Future<bool> getRecordData(
String date, List<int> data, int timeout, int offset) async {
bool ret = await writeCgi("get_record_idx.cgi?dirname=$date&offset=$offset",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24833;
}, timeout);
if (result.isSuccess && result.data!.lengthInBytes > 12) {
data.addAll(result.data!.getRange(12, result.data!.lengthInBytes));
if (result.data!.lengthInBytes == 60000 + 12)
return getRecordData(date, data, timeout, offset + 1);
}
} else {}
return ret;
}
Future<List<RecordTimeLineFile>> getRecordLineFile(String date,
{bool cache = true, int timeout = 5}) async {
var timeLine = int.tryParse(statusResult?.support_time_line ?? "0") ?? 0;
if (timeLine < 1) {
_lineFileCache[date] = [];
return [];
}
if (cache == true) {
if (_lineFileCache.containsKey(date)) return _lineFileCache[date] ?? [];
}
List<RecordTimeLineFile> files = [];
List<int> data = [];
bool ret = await getRecordData(date, data, timeout, 0);
if (ret) {
var offset = 0;
while (offset < data.length) {
var lineData = RecordTimeLineData(Uint8List.fromList(data), offset);
RecordTimeLineFile? item;
try {
item = RecordTimeLineFile.fromData(lineData);
} catch (ex) {
item = null;
}
if (item != null) {
files.add(item);
} else {
if (lineData.offset == 0) break;
}
offset += lineData.offset ?? 0;
}
_lineFileCache[date] = files;
}
return files;
}
Future<List<RecordFile>> getRecordFile({
int pageIndex = 0,
int pageSize = 20,
bool cache = true,
bool supportRecordTypeSeach = false,
String? dateName,
int timeout = 5,
}) async {
if (supportRecordTypeSeach == true) {
if (dateName != null) {
List<RecordFile> files = await _getTypeSeachRecordFile(dateName);
recordFileList.addAll(files);
return recordFileList;
} else if (cache == false) {
recordFileList.clear();
List<String> recordDatas = await getRecordTypeSearchDate();
for (int i = 0; i < recordDatas.length; i++) {
String dateName = recordDatas[i];
List<RecordFile> files = await _getTypeSeachRecordFile(dateName);
recordFileList.addAll(files);
}
return recordFileList;
} else {
return recordFileList;
}
} else {
if (pageIndex == null) {
if (cache == true) {
return recordFileList;
}
pageIndex = 0;
if (pageCount == 0) {
pageCount = 1;
}
recordFileList.clear();
while (pageIndex < pageCount) {
List<RecordFile> files = await _getRecordFile(pageSize, pageIndex);
pageIndex += 1;
recordFileList.addAll(files);
}
return recordFileList;
} else {
if (pageIndex == 0) {
List<RecordFile> files = await _getRecordFile(pageSize, pageIndex);
recordFileList.addAll(files);
///首次获取数据,把files 添加到recordFileList
return recordFileList;
} else {
if (recordFileList.length >= pageSize * pageIndex + pageSize) {
return recordFileList;
}
List<RecordFile> files = await _getRecordFile(pageSize, pageIndex);
recordFileList.addAll(files);
return recordFileList;
}
}
}
}
Future<bool> startRecordFile(String recordName, int offset,
{int timeout = 5}) async {
bool ret = await writeCgi(
"livestream.cgi?streamid=4&filename=$recordName&offset=$offset&download=1&",
timeout: timeout);
if (ret) {
//AppTFCardPlayerPlugin.clearTFCardVideoBuf(clientPtr);
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24631;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> startRecordLineFile(int timestamp, int event,
{int timeout = 5, int channel = 4, int frameNo = 0, int key = 0}) async {
bool ret = await writeCgi(
"livestream.cgi?streamid=5&ntsamp=$timestamp&event=$event&framenum=$frameNo&recch=$channel&key=$key&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24631;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> startRecordLineFileDown(List<RecordTimeLineDown> files,
{int timeout = 5}) async {
Map data = Map();
List download = files.map((e) => e.getData()).toList();
data["download"] = download;
var str = "record_fastplay.cgi?ctrl=1&playlist=${jsonEncode(data)}&";
bool ret = await writeCgi(str, timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24837;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> stopRecordLineFileDown({int timeout = 5}) async {
bool ret = await writeCgi("record_fastplay.cgi?ctrl=0&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24837;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
Future<bool> deleteRecordFile(String recordName, {int timeout = 5}) async {
bool ret =
await writeCgi("del_file.cgi?name=$recordName&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24606;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
recordFileList
.removeWhere((element) => element.record_name == recordName);
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> stopRecordFile({int timeout = 5}) async {
bool ret = await writeCgi("livestream.cgi?streamid=17&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24631;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
return data["result"] == "0";
}
}
return false;
}
//日历摘要
Future<List<String>> getRecordTypeSearchDate({int timeout = 5}) async {
bool ret =
await writeCgi("get_record_file.cgi?GetType=date&", timeout: timeout);
List<String> recordDate = [];
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24583;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
int count = int.tryParse(data['record_datenum'] ?? '0') ?? 0;
if (count != 0) {
for (int i = 0; i < count; i++) {
String record = data['record_date[$i]'];
recordDate.add(record);
}
}
return recordDate;
}
}
return recordDate;
}
}
import 'dart:typed_data';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'package:vsdk/p2p_device/p2p_connect.dart';
import '../../app_p2p_api.dart';
class LoginResult {
bool? isSuccess;
int? cmd;
String? result;
// ignore: non_constant_identifier_names
String? current_users;
// ignore: non_constant_identifier_names
String? max_support_users;
LoginResult.form(CommandResult commandResult) {
isSuccess = false;
if (commandResult != null && commandResult.isSuccess == true) {
isSuccess = true;
cmd = commandResult.cmd;
try {
Map data = commandResult.getMap();
result = data["result"];
current_users = data["current_users"];
max_support_users = data["max_support_users"];
} catch (Exception) {}
}
}
@override
String toString() {
return "cmd:$cmd success:$isSuccess {result:$result,current_users:$current_users,max_support_users:$max_support_users}";
}
}
mixin LoginCommand on P2PCommand {
/// 登录指令
Future<LoginResult?> login(String username, String password,
{int timeout = 5}) async {
if (p2pConnectState != ClientConnectState.CONNECT_STATUS_ONLINE) {
return null;
}
int clientPtr = await getClientPtr();
bool ret = await AppP2PApi().clientLogin(clientPtr, username, password);
if (ret == true) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24736 || cmd == 24577;
}, timeout);
if (result.isSuccess) {
return LoginResult.form(result);
}
}
return null;
}
}
import 'dart:typed_data';
import 'package:vsdk/camera_device/commands/camera_command.dart';
import 'package:vsdk/p2p_device/p2p_command.dart';
class TimeParam {
String? now;
String? tz;
// ignore: non_constant_identifier_names
String? ntp_enable;
// ignore: non_constant_identifier_names
String? ntp_svr;
TimeParam.from(Map data) {
now = data["now"];
tz = data["tz"];
ntp_enable = data["ntp_enable"];
ntp_svr = data["ntp_svr"];
}
}
class NetworkParam {
String? dhcpen;
String? ip;
String? mask;
String? gateway;
String? dns1;
String? dns2;
String? port;
NetworkParam.from(Map data) {
dhcpen = data["dhcpen"];
ip = data["ip"];
mask = data["mask"];
gateway = data["gateway"];
dns1 = data["dns1"];
dns2 = data["dns2"];
port = data["port"];
}
}
class WiFiParam {
// ignore: non_constant_identifier_names
String? wifi_enable;
// ignore: non_constant_identifier_names
String? wifi_ssid;
// ignore: non_constant_identifier_names
String? wifi_mode;
// ignore: non_constant_identifier_names
String? wifi_encrypt;
// ignore: non_constant_identifier_names
String? wifi_authtype;
// ignore: non_constant_identifier_names
String? wifi_channel;
WiFiParam.from(Map data) {
wifi_enable = data["wifi_enable"];
wifi_ssid = data["wifi_ssid"];
wifi_mode = data["wifi_mode"];
wifi_encrypt = data["wifi_encrypt"];
wifi_authtype = data["wifi_authtype"];
wifi_channel = data["wifi_channel"];
}
}
class AlarmMotionParam {
// ignore: non_constant_identifier_names
String? alarm_motion_armed;
// ignore: non_constant_identifier_names
String? alarm_motion_sensitivity;
String? cloudVideoDuration;
AlarmMotionParam.from(Map data) {
alarm_motion_armed = data["alarm_motion_armed"];
alarm_motion_sensitivity = data["alarm_motion_sensitivity"];
if (data.containsKey("CloudVideoDuration")) {
cloudVideoDuration = data["CloudVideoDuration"];
} else {
cloudVideoDuration = "-1";
}
}
}
class ParamResult {
bool? isSuccess;
int? cmd;
String? result;
TimeParam? timeParam;
NetworkParam? networkParam;
WiFiParam? wifiParam;
AlarmMotionParam? alarmMotionParam;
Map? sourceData;
ParamResult.form(CommandResult commandResult) {
isSuccess = false;
if (commandResult != null && commandResult.isSuccess == true) {
isSuccess = true;
cmd = commandResult.cmd;
try {
Map data = commandResult.getMap();
sourceData = data;
result = data["result"];
timeParam = TimeParam.from(data);
networkParam = NetworkParam.from(data);
wifiParam = WiFiParam.from(data);
alarmMotionParam = AlarmMotionParam.from(data);
} catch (Exception) {}
}
}
@override
String toString() {
return "$sourceData";
}
}
mixin ParamsCommand on CameraCommand {
ParamResult? paramResult;
/// 获取设备参数
/// @param [cache] 是否使用缓存,默认为true 使用缓存
Future<ParamResult?> getParams({int timeout = 5, bool cache = true}) async {
if (cache) {
return paramResult;
}
bool ret = await writeCgi("get_params.cgi?", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24578;
}, timeout);
if (result.isSuccess) {
paramResult = ParamResult.form(result);
return paramResult;
}
}
return null;
}
Future<bool> left({int timeout = 5}) async {
print("left ${left} decoder_control.cgi?command=4&onestep=1&");
bool ret = await writeCgi("decoder_control.cgi?command=4&onestep=1&",
timeout: timeout);
print("left ${left} ");
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24601;
}, timeout);
return result.isSuccess;
}
return false;
}
/// 获取截图快照
Future<bool> getSnapshot(String name, {int timeout = 5}) async {
int time = DateTime.now().millisecondsSinceEpoch;
bool ret = await writeCgi("snapshot.cgi?sensor=${name}&", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24597;
}, timeout);
print(
"getSnapshot result.isSuccess ${result.isSuccess} result.data${result.data} ${result?.data?.length}");
if (result.isSuccess && result.data != null && result.data!.length > 0) {
///return await _saveSnapshotFile(result.data, '${time}${"_"}{$name}');
return true;
}
}
return false;
}
}
import 'dart:convert';
import 'dart:typed_data';
import 'package:vsdk/p2p_device/p2p_command.dart';
mixin PlanCommand on P2PCommand {
Map? whiteLightPlanData;
Future<bool> getWhiteLightPlan({int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=212&type=5&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") &&
str.contains("command=11;") &&
str.contains("type=5;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("light_plan_enable")) {
whiteLightPlanData = data;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setWhiteLightPlan(
{required List records, required int enable, int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=5&mark=212&"
"light_plan1=${records[0]}&"
"light_plan2=${records[1]}&"
"light_plan3=${records[2]}&"
"light_plan4=${records[3]}&"
"light_plan5=${records[4]}&"
"light_plan6=${records[5]}&"
"light_plan7=${records[6]}&"
"light_plan8=${records[7]}&"
"light_plan9=${records[8]}&"
"light_plan10=${records[9]}&"
"light_plan11=${records[10]}&"
"light_plan12=${records[11]}&"
"light_plan13=${records[12]}&"
"light_plan14=${records[13]}&"
"light_plan15=${records[14]}&"
"light_plan16=${records[15]}&"
"light_plan17=${records[16]}&"
"light_plan18=${records[17]}&"
"light_plan19=${records[18]}&"
"light_plan20=${records[19]}&"
"light_plan21=${records[20]}&"
"light_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=5;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0") {
return true;
}
}
}
return false;
}
Map? motionAlarmPlanData;
Future<bool> getMotionAlarmPlan({int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=212&type=2&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") &&
str.contains("command=11;") &&
str.contains("type=2;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("motion_push_enable")) {
motionAlarmPlanData = data;
}
return data["result"] == "0";
}
}
return false;
}
Future<bool> setMotionAlarmPlan(
{required List records, required int enable, int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=2&mark=212&"
"motion_push_plan1=${records[0]}&"
"motion_push_plan2=${records[1]}&"
"motion_push_plan3=${records[2]}&"
"motion_push_plan4=${records[3]}&"
"motion_push_plan5=${records[4]}&"
"motion_push_plan6=${records[5]}&"
"motion_push_plan7=${records[6]}&"
"motion_push_plan8=${records[7]}&"
"motion_push_plan9=${records[8]}&"
"motion_push_plan10=${records[9]}&"
"motion_push_plan11=${records[10]}&"
"motion_push_plan12=${records[11]}&"
"motion_push_plan13=${records[12]}&"
"motion_push_plan14=${records[13]}&"
"motion_push_plan15=${records[14]}&"
"motion_push_plan16=${records[15]}&"
"motion_push_plan17=${records[16]}&"
"motion_push_plan18=${records[17]}&"
"motion_push_plan19=${records[18]}&"
"motion_push_plan20=${records[19]}&"
"motion_push_plan21=${records[20]}&"
"motion_push_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=2;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0" || data["result"] == "0") {
return true;
}
}
}
return false;
}
///获取绘制区域
///command 1:移动侦测区域, 3:人形侦测区域, 5:离岗侦测区域, 7:脸侦测区域, 9:人脸识别区域
Map? customeZoneData;
Future<bool> getAlarmCustomeZone(int command,
{int timeout = 5, int sensor = 0}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2123&command=$command&sensor=${sensor}&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2123;") && str.contains("command=$command;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
customeZoneData = data;
return data["result"] == "0";
}
}
return false;
}
///设置侦测区域
Future<bool> setAlarmCustomeZone(
{required List records,
required int command,
int timeout = 5,
int sensor = 0}) async {
String reignString = "md_";
switch (command) {
case 0: //CustomAreaType_MoveDetect 设置移动侦测区域
reignString = "md_";
break;
case 2: //CustomAreaType_HumanDetect 设置人形侦测区域
reignString = "pd_";
break;
case 4: //CustomAreaType_OffDuty 设置离岗侦测区域
reignString = "depart_";
break;
case 6: //CustomAreaType_FaceDetect 设置人脸侦测区域
reignString = "face_detect_";
break;
case 8: //CustomAreaType_FaceDiscernment 设置人脸识别区域
reignString = "face_recognition_";
break;
default:
break;
}
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2123&command=$command&sensor=${sensor}&"
"${reignString}reign0=${records[0]}&"
"${reignString}reign1=${records[1]}&"
"${reignString}reign2=${records[2]}&"
"${reignString}reign3=${records[3]}&"
"${reignString}reign4=${records[4]}&"
"${reignString}reign5=${records[5]}&"
"${reignString}reign6=${records[6]}&"
"${reignString}reign7=${records[7]}&"
"${reignString}reign8=${records[8]}&"
"${reignString}reign9=${records[9]}&"
"${reignString}reign10=${records[10]}&"
"${reignString}reign11=${records[11]}&"
"${reignString}reign12=${records[12]}&"
"${reignString}reign13=${records[13]}&"
"${reignString}reign14=${records[14]}&"
"${reignString}reign15=${records[15]}&"
"${reignString}reign16=${records[16]}&"
"${reignString}reign17=${records[17]}&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2123;") && str.contains("command=$command;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0" || data["result"] == "0") {
return true;
}
}
}
return false;
}
///实时录像计划指令
///获取实时计划录像
Map? realTimeRecordPlanData;
Future<bool> getReocrdPlan({int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=212&type=3&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") &&
str.contains("command=11;") &&
str.contains("type=3;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("record_plan_enable")) {
realTimeRecordPlanData = data;
}
return data["result"] == "0";
}
}
return false;
}
///设置实时计划录像
Future<bool> setReocrdPlan(
{required List records, required int enable, int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=3&mark=212&"
"record_plan1=${records[0]}&"
"record_plan2=${records[1]}&"
"record_plan3=${records[2]}&"
"record_plan4=${records[3]}&"
"record_plan5=${records[4]}&"
"record_plan6=${records[5]}&"
"record_plan7=${records[6]}&"
"record_plan8=${records[7]}&"
"record_plan9=${records[8]}&"
"record_plan10=${records[9]}&"
"record_plan11=${records[10]}&"
"record_plan12=${records[11]}&"
"record_plan13=${records[12]}&"
"record_plan14=${records[13]}&"
"record_plan15=${records[14]}&"
"record_plan16=${records[15]}&"
"record_plan17=${records[16]}&"
"record_plan18=${records[17]}&"
"record_plan19=${records[18]}&"
"record_plan20=${records[19]}&"
"record_plan21=${records[20]}&"
"record_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=3;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0" || data["result"] == "0") {
return true;
}
}
}
return false;
}
///侦测计划录像计划指令
///获取侦测计划录像
Map? detectionRecordPlanData;
Future<bool> getDetectionReocrdPlan({int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=11&mark=212&type=1&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") &&
str.contains("command=11;") &&
str.contains("type=1;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data.containsKey("motion_record_enable")) {
detectionRecordPlanData = data;
}
return data["result"] == "0";
}
}
return false;
}
///设置侦测计划录像
Future<bool> setDetectionReocrdPlan(
{required List records, required int enable, int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2017&command=1&mark=212&"
"motion_record_plan1=${records[0]}&"
"motion_record_plan2=${records[1]}&"
"motion_record_plan3=${records[2]}&"
"motion_record_plan4=${records[3]}&"
"motion_record_plan5=${records[4]}&"
"motion_record_plan6=${records[5]}&"
"motion_record_plan7=${records[6]}&"
"motion_record_plan8=${records[7]}&"
"motion_record_plan9=${records[8]}&"
"motion_record_plan10=${records[9]}&"
"motion_record_plan11=${records[10]}&"
"motion_record_plan12=${records[11]}&"
"motion_record_plan13=${records[12]}&"
"motion_record_plan14=${records[13]}&"
"motion_record_plan15=${records[14]}&"
"motion_record_plan16=${records[15]}&"
"motion_record_plan17=${records[16]}&"
"motion_record_plan18=${records[17]}&"
"motion_record_plan19=${records[18]}&"
"motion_record_plan20=${records[19]}&"
"motion_record_plan21=${records[20]}&"
"motion_record_plan_enable=$enable&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2017;") && str.contains("command=1;");
}
return false;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["status"] == "0" || data["result"] == "0") {
return true;
}
}
}
return false;
}
}
import 'dart:typed_data';
import 'package:vsdk/p2p_device/p2p_command.dart';
class RecordResult {
RecordResult();
bool isSuccess = false;
int cmd = 0;
late String result;
// ignore: non_constant_identifier_names
late String enc_size; //跟resolution一致
// ignore: non_constant_identifier_names
late String enc_framerate; //主码流帧率
// ignore: non_constant_identifier_names
late String enc_keyframe; //主码流关键帧
// ignore: non_constant_identifier_names
late String enc_quant; //主码流画质
// ignore: non_constant_identifier_names
late String enc_bitrate; //主码流码率
// ignore: non_constant_identifier_names
late String enc_ratemode; //主码流码流模式
// ignore: non_constant_identifier_names
late String sub_enc_size; //跟resolutionsub一致
// ignore: non_constant_identifier_names
late String sub_enc_framerate; //次码流帧率
// ignore: non_constant_identifier_names
late String sub_enc_keyframe; //次码流关键帧
// ignore: non_constant_identifier_names
late String sub_enc_quant; //次码流画质
// ignore: non_constant_identifier_names
late String sub_enc_bitrate; //次码流码率
// ignore: non_constant_identifier_names
late String sub_enc_ratemode; //次码流码流模式
// ignore: non_constant_identifier_names
late String sub_sub_enc_size; //跟resolutionsubsub一致
// ignore: non_constant_identifier_names
late String sub_sub_enc_framerate; //次次码流帧率
// ignore: non_constant_identifier_names
late String sub_sub_enc_keyframe; //次次码流关键帧
// ignore: non_constant_identifier_names
late String sub_sub_enc_quant; //次次码流画质
// ignore: non_constant_identifier_names
late String sub_sub_enc_bitrate; //次次码流码率
// ignore: non_constant_identifier_names
late String sub_sub_enc_ratemode; //次次码流码流模式
// ignore: non_constant_identifier_names
late String record_audio; //表示录制音频0:不录制音频1:录制音频
// ignore: non_constant_identifier_names
late String record_cover_enable; //表示录像覆盖0->表示不允许覆盖1->表示允许覆盖
// ignore: non_constant_identifier_names
late String record_timer; //表示录像时长
// ignore: non_constant_identifier_names
late String record_size; //保留
// ignore: non_constant_identifier_names
late String record_time_enable;
// ignore: non_constant_identifier_names
late String tf_enable; //TF卡挂载状态
// ignore: non_constant_identifier_names
late String record_chnl; //录像通道选择0:主码流录像1:次码流录像2:次次码流录像
// ignore: non_constant_identifier_names
late String sdtotal; //TF卡总容量
// ignore: non_constant_identifier_names
late String sdfree; //TF卡剩余容量
// ignore: non_constant_identifier_names
String record_sd_status = ""; //TF卡状态
Map sourceData = {};
RecordResult.form(CommandResult? commandResult) {
isSuccess = false;
if (commandResult != null && commandResult.isSuccess == true) {
isSuccess = true;
cmd = commandResult.cmd;
try {
Map data = commandResult.getMap();
sourceData = data;
result = data["result"];
enc_size = data["enc_size"];
enc_framerate = data["enc_framerate"];
enc_keyframe = data["enc_keyframe"];
enc_quant = data["enc_quant"];
enc_bitrate = data["enc_bitrate"];
enc_ratemode = data["enc_ratemode"];
sub_enc_size = data["sub_enc_size"];
sub_enc_framerate = data["sub_enc_framerate"];
sub_enc_keyframe = data["sub_enc_keyframe"];
sub_enc_quant = data["sub_enc_quant"];
sub_enc_bitrate = data["sub_enc_bitrate"];
sub_enc_ratemode = data["sub_enc_ratemode"];
sub_sub_enc_size = data["sub_sub_enc_size"];
sub_sub_enc_framerate = data["sub_sub_enc_framerate"];
sub_sub_enc_keyframe = data["sub_sub_enc_keyframe"];
sub_sub_enc_quant = data["sub_sub_enc_quant"];
record_sd_status = data["record_sd_status"];
sub_sub_enc_bitrate = data["sub_sub_enc_bitrate"];
sub_sub_enc_ratemode = data["sub_sub_enc_ratemode"];
record_audio = data["record_audio"];
record_cover_enable = data["record_cover_enable"];
record_timer = data["record_timer"];
record_size = data["record_size"];
record_time_enable = data["record_time_enable"];
tf_enable = data["tf_enable"];
record_chnl = data["record_chnl"];
sdtotal = data["sdtotal"];
sdfree = data["sdfree"];
record_sd_status = data["record_sd_status"];
} catch (Exception) {}
}
}
@override
String toString() {
return "$sourceData";
}
}
mixin RecordCommand on P2PCommand {
RecordResult recordResult = RecordResult();
Future<bool> getRecordParam({int timeout = 5}) async {
bool ret = await writeCgi("get_record.cgi?", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24582;
}, timeout);
if (result.isSuccess) {
recordResult = RecordResult.form(result);
if (recordResult.result == "0") {
return true;
}
}
}
return false;
}
Future<bool> changedRecordVoice(bool enable, {int timeout = 5}) async {
//record_cover_enable=1&record_size=0&&record_timer=15 这个字段去掉
bool ret = await writeCgi(
"set_recordsch.cgi?record_audio=${enable ? 1 : 0}&",
timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24617;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
recordResult.record_audio = enable ? "1" : "0";
return true;
}
}
}
return false;
}
/// 格式化SD 卡
Future<bool> formatSD({int timeout = 5}) async {
bool ret = await writeCgi("set_formatsd.cgi?", timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24616;
}, timeout);
if (result.isSuccess) {
if (result.getMap()["result"] == "0") {
recordResult.record_sd_status = "4";
return true;
}
}
}
return false;
}
/// 实时录像
Future<bool> setRecordParams(int enable, {int timeout = 5}) async {
String strCMD;
if (enable == 1) {
strCMD = "set_recordsch.cgi?record_cover=1&"
"record_timer=${recordResult.record_timer}&"
"time_schedule_enable=$enable&"
"schedule_sun_0=-1&"
"schedule_sun_1=-1&"
"schedule_sun_2=-1&"
"schedule_mon_0=-1&"
"schedule_mon_1=-1&"
"schedule_mon_2=-1&"
"schedule_tue_0=-1&"
"schedule_tue_1=-1&"
"schedule_tue_2=-1&"
"schedule_wed_0=-1&"
"schedule_wed_1=-1&"
"schedule_wed_2=-1&"
"schedule_thu_0=-1&"
"schedule_thu_1=-1&"
"schedule_thu_2=-1&"
"schedule_fri_0=-1&"
"schedule_fri_1=-1&"
"schedule_fri_2=-1&"
"schedule_sat_0=-1&"
"schedule_sat_1=-1&"
"schedule_sat_2=-1&"
"record_audio=${recordResult.record_audio}&";
} else {
strCMD = "set_recordsch.cgi?record_cover=1&"
"record_timer=${recordResult.record_timer}&"
"time_schedule_enable=$enable&"
"schedule_sun_0=0&"
"schedule_sun_1=0&"
"schedule_sun_2=0&"
"schedule_mon_0=0&"
"schedule_mon_1=0&"
"schedule_mon_2=0&"
"schedule_tue_0=0&"
"schedule_tue_1=0&"
"schedule_tue_2=0&"
"schedule_wed_0=0&"
"schedule_wed_1=0&"
"schedule_wed_2=0&"
"schedule_thu_0=0&"
"schedule_thu_1=0&"
"schedule_thu_2=0&"
"schedule_fri_0=0&"
"schedule_fri_1=0&"
"schedule_fri_2=0&"
"schedule_sat_0=0&"
"schedule_sat_1=0&"
"schedule_sat_2=0&"
"record_audio=${recordResult.record_audio}&";
}
bool ret = await writeCgi(strCMD, timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24617;
}, timeout);
if (result.isSuccess) {
Map data = result.getMap();
if (data["result"] == "0") {
return true;
}
}
}
return false;
}
}
import 'dart:async';
import 'dart:typed_data';
import 'package:vsdk/camera_device/commands/camera_command.dart';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'package:vsdk/p2p_device/p2p_device.dart';
import '../../app_p2p_api.dart';
class StatusResult {
bool? isSuccess;
int? cmd;
String? result;
String? alias;
String? deviceid;
// ignore: non_constant_identifier_names
String? sys_ver;
// ignore: non_constant_identifier_names
String? app_version;
// ignore: non_constant_identifier_names
String? oem_id;
// ignore: non_constant_identifier_names
String? current_users;
// ignore: non_constant_identifier_names
String? max_support_users;
String? now;
// ignore: non_constant_identifier_names
String? alarm_status;
// ignore: non_constant_identifier_names
String? upnp_status;
// ignore: non_constant_identifier_names
String? batteryRate;
String? isCharge;
String? dnsenable;
String? osdenable;
// ignore: non_constant_identifier_names
String? syswifi_mode;
String? mac;
String? wifimac;
String? sdstatus;
// ignore: non_constant_identifier_names
String? record_sd_status;
// ignore: non_constant_identifier_names
String? dns_status;
String? internet;
String? p2pstatus;
String? devicetype;
String? devicesubtype;
String? externwifi;
String? encrypt;
String? under;
String? sdtotal;
String? sdfree;
String? sdlevel;
// ignore: non_constant_identifier_names
String? timeplan_ver;
// ignore: non_constant_identifier_names
String? camera_type;
// ignore: non_constant_identifier_names
String? params_md5;
// ignore: non_constant_identifier_names
String? pwd_change_realtime;
// ignore: non_constant_identifier_names
String? ircut_forceon;
String? hardwareTestFunc;
// ignore: non_constant_identifier_names
String? customer_code;
String? pixel;
String? cameraMode;
String? haveWifi;
String? haveMic;
String? haveHorn;
String? haveMotor;
String? haveTf;
String? support_pir_level;
// ignore: non_constant_identifier_names
String? DualAuthentication;
// ignore: non_constant_identifier_names
String? support_cryDetect;
// ignore: non_constant_identifier_names
String? support_rtspTls;
// ignore: non_constant_identifier_names
String? support_focus;
// ignore: non_constant_identifier_names
String? support_privacy_pos;
// ignore: non_constant_identifier_names
String? support_vuid;
String? realdeviceid;
String? vuidResult;
// ignore: non_constant_identifier_names
String? support_humanDetect;
// ignore: non_constant_identifier_names
String? support_h264_h265_shift;
// ignore: non_constant_identifier_names
String? support_pixel_shift;
// ignore: non_constant_identifier_names
String? wifi_change_realtime;
// ignore: non_constant_identifier_names
String? support_smokeDetect;
// ignore: non_constant_identifier_names
String? support_departDetect;
// ignore: non_constant_identifier_names
String? support_Face_Recognition;
// ignore: non_constant_identifier_names
String? support_faceSearch;
// ignore: non_constant_identifier_names
String? support_faceDetect;
// ignore: non_constant_identifier_names
String? support_humanoidFrame;
// ignore: non_constant_identifier_names
String? support_voiceTypedef;
// ignore: non_constant_identifier_names
String? support_motionArea;
// ignore: non_constant_identifier_names
String? support_4G_module;
// ignore: non_constant_identifier_names
String? support_mode_switch;
// ignore: non_constant_identifier_names
String? support_low_power;
// ignore: non_constant_identifier_names
String? EchoCancellationVer;
// ignore: non_constant_identifier_names
String? support_led_hidden_mode;
// ignore: non_constant_identifier_names
String? support_audio_g711a;
//是否为门铃 1:有门铃按键 0:没有门铃按键
String? haveDoorBell;
//1-->支持 PIR 唤醒后人形侦测双鉴定 2-->支持 PIR 唤醒后移动侦测双鉴定
String? suport_wakeup_correction;
String? support_Pir_Distance_Adjust;
String? support_PeopleDetection;
//(双重认证返回)双重认证明文开关 0 是关闭 1 是打开
String? ExUserSwitch;
//support_Plaintext_Pwd: 0,不支持明文密码
String? support_Plaintext_Pwd;
//MaxZoomMultiple 支持最大的变焦
String? MaxZoomMultiple;
//CurZoomMultiple 当前的变焦倍数
String? CurZoomMultiple;
//全彩夜视
String? support_full_color_night_vision_mode;
//是否支持远程一键开机
String? support_Remote_PowerOnOff_Switch;
//是否支持wifi穿墙增强模式
String? support_WiFi_Enhanced_Mode;
//是否支持白光灯/红外灯控制参数
String? support_WhiteLed_Ctrl;
//白光灯是否亮灯
String? whiteledstate;
//是否支持智能电量
String? support_Smart_Electricity_Sleep;
//是否支持单片机MCU
String? scm_version;
String? support_osd_adjustment;
//检测间隔
String? support_SleepCheckInterval;
//徘徊检测
String? support_LingerCheck;
//支持看守位
String? support_ptz_guard;
//支持防拆报警
String? support_tamper_setting;
//支持tf录像分辨率切换
String? support_record_resolution_switch;
//是否隐藏白光灯
String? support_manual_light;
//关闭录像模式选择
String? recordmod;
//关闭智能侦测定时
String? smartdetecttime;
//是否支持预置位自动巡航
String? support_preset_auto;
//是否支持预置位定时计划和线路
String? support_presetCruise;
//是否支持多目
String? support_binocular;
//镜头数量
String? binocular_num;
//长焦倍数
String? binocular_zoom;
//当前的镜头 0:默认 1:长焦
String? binocular_value;
//微功耗
String? support_micro_power;
//TF Card Time Line
String? support_time_line;
//其中[bit 0 -> 低功耗 bit 1 -> 持续工作 bit 2 ->超低功耗 bit 3 ->微功耗]
String? support_new_low_power;
String? support_presetRoi;
String? wifi_signal_quality;
String? preset_value;
String? watch_preset;
String? preset_cruise_status;
String? preset_cruise_status_h;
String? preset_cruise_status_v;
String? support_pininpic; // T31枪球
String? support_mutil_sensor_stream; //T40枪球
String? pininpic_sensor; //镜头0 1
List<int> binoculars = [];
int? binocular_offset_x;
int? binocular_offset_y;
String? support_auto_record_mode; //自动录像模式
String? support_humanoid_zoom; //人形变倍追踪
String? center_status; //云台矫正
String? support_record_type_seach; //TF卡录像文件否支持日期查询
String? support_fire_smoke; //烟火监测
String? fire_smoke_version; //烟火监测版本号
String? support_area; //烟火监测版本号
String? preset_cruise_curpos; //当前的巡航预置位
String? gblinkage_enable; // 0 不显示 1 开关开启 2 开关关闭
String? sirenStatus; //警笛
String? support_mode_AiDetect; //(按位计算:011代表车和人, 010代表车, 001代表人,后续可扩充第三位包裹)
String? support_pure_white_light; //为1时,说明是纯白光模式
String? support_fix_sensor; //固定镜头
String? start_or_stop_record_status; //录像
String? splitScreen; //二目转三目
Map? sourceData;
StatusResult.form(CommandResult? commandResult) {
isSuccess = false;
if (commandResult != null && commandResult.isSuccess == true) {
isSuccess = true;
cmd = commandResult.cmd;
try {
Map data = commandResult.getMap();
sourceData = data;
result = data["result"];
alias = data["alias"];
deviceid = data["deviceid"];
sys_ver = data["sys_ver"];
current_users = data["current_users"];
max_support_users = data["max_support_users"];
app_version = data["app_version"];
oem_id = data["oem_id"];
now = data["now"];
alarm_status = data["alarm_status"];
upnp_status = data["upnp_status"];
dnsenable = data["dnsenable"];
osdenable = data["osdenable"];
syswifi_mode = data["syswifi_mode"];
mac = data["mac"];
wifimac = data["wifimac"];
sdstatus = data["sdstatus"];
record_sd_status = data["record_sd_status"];
dns_status = data["dns_status"];
internet = data["internet"];
p2pstatus = data["p2pstatus"];
devicetype = data["devicetype"];
devicesubtype = data["devicesubtype"];
externwifi = data["externwifi"];
encrypt = data["encrypt"];
under = data["under"];
sdtotal = data["sdtotal"];
sdfree = data["sdfree"];
sdlevel = data["sdlevel"];
timeplan_ver = data["timeplan_ver"];
batteryRate = data["batteryRate"];
isCharge = data["isCharge"];
camera_type = data["camera_type"];
params_md5 = data["params_md5"];
pwd_change_realtime = data["pwd_change_realtime"];
ircut_forceon = data["ircut_forceon"];
hardwareTestFunc = data["hardwareTestFunc"];
customer_code = data["customer_code"];
pixel = data["pixel"];
cameraMode = data["cameraMode"];
haveWifi = data["haveWifi"];
haveMic = data["haveMic"];
haveHorn = data["haveHorn"];
haveMotor = data["haveMotor"];
DualAuthentication = data["DualAuthentication"];
support_cryDetect = data["support_cryDetect"];
support_rtspTls = data["support_rtspTls"];
support_focus = data["support_focus"];
support_privacy_pos = data["support_privacy_pos"];
support_vuid = data["support_vuid"];
realdeviceid = data["realdeviceid"];
support_humanDetect = data["support_humanDetect"];
support_h264_h265_shift = data["support_h264_h265_shift"];
support_pixel_shift = data["support_pixel_shift"];
wifi_change_realtime = data["wifi_change_realtime"];
support_smokeDetect = data["support_smokeDetect"];
support_departDetect = data["support_departDetect"];
support_Face_Recognition = data["support_Face_Recognition"];
support_faceSearch = data["support_faceSearch"];
support_faceDetect = data["support_faceDetect"];
support_humanoidFrame = data["support_humanoidFrame"];
support_voiceTypedef = data["support_voiceTypedef"];
support_motionArea = data["support_motionArea"];
support_4G_module = data["support_4G_module"];
support_low_power = data["support_low_power"];
EchoCancellationVer = data["EchoCancellationVer"];
support_led_hidden_mode = data["support_led_hidden_mode"];
support_mode_switch = data["support_mode_switch"];
support_audio_g711a = data["support_g711a"];
haveDoorBell = data["haveDoorBell"];
suport_wakeup_correction = data["suport_wakeup_correction"];
support_Pir_Distance_Adjust = data["support_Pir_Distance_Adjust"];
support_PeopleDetection = data["support_PeopleDetection"];
ExUserSwitch = data["ExUserSwitch"];
support_Plaintext_Pwd = data["support_Plaintext_Pwd"];
MaxZoomMultiple = data["MaxZoomMultiple"];
CurZoomMultiple = data["CurZoomMultiple"];
support_full_color_night_vision_mode =
data["support_full_color_night_vision_mode"];
support_Remote_PowerOnOff_Switch =
data["support_Remote_PowerOnOff_Switch"];
support_WiFi_Enhanced_Mode = data["support_WiFi_Enhanced_Mode"];
support_WhiteLed_Ctrl = data["support_WhiteLed_Ctrl"];
whiteledstate = data["whiteledstate"];
support_Smart_Electricity_Sleep =
data["support_Smart_Electricity_Sleep"];
scm_version = data["scm_version"];
support_osd_adjustment = data["support_osd_adjustment"];
support_SleepCheckInterval = data["support_SleepCheckInterval"];
support_LingerCheck = data["support_LingerCheck"];
support_ptz_guard = data["support_ptz_guard"];
support_tamper_setting = data["support_tamper_setting"];
support_record_resolution_switch =
data["support_record_resolution_switch"];
support_manual_light = data["support_manual_light"] ?? "";
recordmod = data["recordmod"] ?? "1";
smartdetecttime = data["smartdetecttime"] ?? "1";
support_preset_auto = data["support_preset_auto"];
support_presetCruise = data["support_presetCruise"];
support_binocular = data["support_binocular"];
binocular_zoom = data["binocular_zoom"];
binocular_value = data["binocular_value"];
support_micro_power = data["support_micro_power"];
support_time_line = data["support_time_line"];
support_pir_level = data["support_pir_level"];
support_new_low_power = data["support_new_low_power"];
support_presetRoi = data["support_presetRoi"];
wifi_signal_quality = data["wifi_signal_quality"];
preset_value = data["preset_value"];
watch_preset = data["watch_preset"];
preset_cruise_status = data["preset_cruise_status"];
preset_cruise_status_h = data["preset_cruise_status_h"];
preset_cruise_status_v = data["preset_cruise_status_v"];
support_pininpic = data['support_pininpic'];
support_mutil_sensor_stream = data['support_mutil_sensor_stream'];
pininpic_sensor = data['pininpic_sensor'];
support_auto_record_mode = data['support_auto_record_mode'];
support_humanoid_zoom = data['support_humanoid_zoom'];
center_status = data['center_status'];
support_record_type_seach = data['support_record_type_seach'];
support_fire_smoke = data['support_fire_smoke'];
fire_smoke_version = data['fire_smoke_version'];
preset_cruise_curpos = data['preset_cruise_curpos'];
gblinkage_enable = data['gblinkage_enable'];
support_area = data['support_area'];
sirenStatus = data['sirenStatus'];
int supportBinocular = int.tryParse(support_binocular ?? "0") ?? 0;
if (supportBinocular > 0) {
binocular_offset_x =
int.tryParse(data["binocular_offset_x"] ?? "0") ?? 0;
binocular_offset_y =
int.tryParse(data["binocular_offset_y"] ?? "0") ?? 0;
binocular_num = data["binocular_num"];
int binocularNum = int.tryParse(binocular_num ?? "0") ?? 0;
binoculars = [];
for (int i = 0; i < binocularNum; i++) {
String? binocular_zoom = data["binocular_zoom$i"];
if (binocular_zoom != null) {
int? binocularZoom = int.tryParse(binocular_zoom);
if (binocularZoom != null) {
binoculars.add(binocularZoom);
}
}
}
}
support_mode_AiDetect = data['support_mode_AiDetect'];
support_pure_white_light = data['support_pure_white_light'];
support_fix_sensor = data["support_fix_sensor"];
start_or_stop_record_status = data["start_or_stop_record_status"];
splitScreen = data["splitScreen"];
} catch (err) {}
}
}
@override
String toString() {
return "$sourceData";
}
}
typedef StatusChanged = void Function(P2PBasisDevice device, StatusResult?);
mixin StatusCommand on P2PCommand {
StatusResult? _statusResult;
StatusResult? get statusResult => _statusResult;
set statusResult(StatusResult? value) {
_statusResult = value;
notifyListeners<StatusChanged>((StatusChanged func) {
func(this, _statusResult);
});
}
String displayPassword(String password) {
if (password == null) return "null";
if (password == "888888") return "初始密码";
if (password.length > 6)
return "${password.substring(0, 2)}****${password.substring(password.length - 2)}";
if (password.length > 3)
return "****${password.substring(password.length - 2)}";
return "随机密码***$password";
}
/// 登录指令
Future<StatusResult?> login(String username, String password,
{int timeout = 5}) async {
var status = null;
if (p2pConnectState != ClientConnectState.CONNECT_STATUS_ONLINE) {
return status;
}
AppP2PApi().clientLogin(clientPtr!, username, password);
bool ret = await AppP2PApi().clientLogin(clientPtr!, username, password);
if (ret == true) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
if (cmd == 24577) return true;
if (cmd == 24785) {
CommandResult cmdResult = CommandResult(true, cmd, data);
var map = cmdResult.getMap();
if (map == null) return false;
if (map.containsKey("result") && map.keys.length == 1) return true;
if (map.containsKey("result") &&
map.containsKey("current_users") &&
map.containsKey("max_support_users")) return true;
}
return false;
}, timeout);
if (result.isSuccess) {
statusResult = StatusResult.form(result);
status = statusResult;
}
}
return status;
}
/// 获取设备状态
/// @param [cache] 是否使用缓存,默认为true 使用缓存
Future<StatusResult?> getStatus({int timeout = 5, bool cache = true}) async {
if (cache) {
return statusResult;
}
var getstatus = 'get_status.cgi?';
if (isVirtualId) {
getstatus = 'get_status.cgi?vuid=$id&';
}
bool ret = await writeCgi(getstatus, timeout: timeout);
if (ret) {
CommandResult result = await waitCommandResult((int cmd, Uint8List data) {
return cmd == 24577;
}, timeout);
if (result.isSuccess) {
statusResult = StatusResult.form(result);
return statusResult;
}
}
return statusResult;
}
}
import 'dart:async';
import 'dart:convert';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'package:vsdk/p2p_device/p2p_device.dart';
import 'camera_command.dart';
enum VideoResolution {
none,
high, //高分辨率
general, //普通分辨率.默认
unknown, // 未知分辨率
low, //低分辨率
superHD, //超高清
}
enum VideoDirection {
none,
mirror,
flip,
mirrorAndFlip,
}
typedef VideoStateChanged = void Function(P2PBasisDevice device,
VideoResolution resolution, VideoDirection direction);
mixin VideoCommand on CameraCommand {
VideoResolution _resolution = VideoResolution.general;
VideoResolution get resolution => _resolution;
set resolution(VideoResolution value) {
if (value != _resolution) {
_resolution = value;
notifyListeners<VideoStateChanged>((VideoStateChanged func) {
func(this, _resolution, _direction);
});
}
}
VideoDirection _direction = VideoDirection.none;
VideoDirection get direction => _direction;
set direction(VideoDirection value) {
if (value != direction) {
_direction = value;
notifyListeners<VideoStateChanged>((VideoStateChanged func) {
func(this, _resolution, _direction);
});
}
}
Future<bool> startStream({required VideoResolution resolution}) async {
resolution = resolution ?? this.resolution;
int index = resolution.index;
if (resolution == VideoResolution.superHD) {
index = 100;
}
bool ret = await writeCgi("livestream.cgi?streamid=10&substream=$index&");
print("startStream ret${ret}");
if (ret == true) {
final result = await waitCommandResult((cmd, data) {
return cmd == 24631;
}, 3);
ret = result?.isSuccess ?? false;
if (ret) {
this.resolution = resolution;
}
}
return ret;
}
Future<bool> stopStream() async {
bool ret = await writeCgi("livestream.cgi?streamid=16&substream=0&");
if (ret == true) {
var result = await waitCommandResult((cmd, data) {
return cmd == 24631;
}, 3);
ret = result?.isSuccess ?? false;
return ret;
}
return ret;
}
Future<bool> changeResolution(VideoResolution resolution,
{int timeout = 5}) async {
resolution = resolution ?? this.resolution;
if (this.resolution != resolution) {
int index = resolution.index;
if (resolution == VideoResolution.superHD) {
index = 100;
}
bool ret = await writeCgi("camera_control.cgi?param=16&value=$index&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess == true) {
this.resolution = resolution;
}
return result.isSuccess;
}
return false;
}
return true;
}
Future<bool> changeDirection(VideoDirection direction,
{int timeout = 5}) async {
if (direction == null) {
return false;
}
if (this.direction != direction) {
bool ret = await writeCgi(
"camera_control.cgi?param=5&value=${direction.index}&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess == true) {
this.direction = direction;
}
return result.isSuccess;
}
return false;
}
return true;
}
int brightness = (255 * 0.5).floor();
Future<bool> changeBrightness(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=1&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
brightness = value;
}
return result.isSuccess;
}
return false;
}
int contrast = (255 * 0.5).floor();
Future<bool> changeContrast(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=2&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
contrast = value;
}
return result.isSuccess;
}
return false;
}
int ircut = 1;
// ignore: non_constant_identifier_names
int night_vision_mode = 0; // 1夜视模式为全彩 0黑白
int full_color_mode = 2; //全彩子项为定时
int full_color_show = 7; //全彩子项全显示 二进制的
int full_color_default = 1; //全彩定时 是否勾选默认 0 使用自定义 1 使用默认
var full_color_start_hw = 19; //全彩定时 产测配置开始时间
var full_color_end_hw = 8; //全彩定时 产测配置结束时间
int full_color_start = 20; //全彩定时 APP自定义开始时间
int full_color_end = 8; //全彩定时 APP自定义结束时间
Future<bool> changeNightVision(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=14&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
ircut = value;
}
return result.isSuccess;
}
return false;
}
Future<bool> fullColorChangeNightVision(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=33&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
ircut = value;
}
return result.isSuccess;
}
return false;
}
Future<bool> fullColorChangeNightVisionChild(int value,
{int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi(
"camera_control.cgi?param=37&value=0&fullcolormode=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
return result.isSuccess;
}
return false;
}
//全彩定时默认设置
Future<bool> fullColorTimingDefault(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi(
"camera_control.cgi?param=37&value=0&fullcolormode=2&fullcolordefault=1&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
return result.isSuccess;
}
return false;
}
//全彩定时自定义设置
Future<bool> fullColorTiming(int start, int end, {int timeout = 5}) async {
if (start == null && end == null) {
return false;
}
bool ret = await writeCgi(
"camera_control.cgi?param=37&value=0&fullcolormode=2&fullcolordefault=0&fullcolorstart=$start&fullcolorstop=$end");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
return result.isSuccess;
}
return false;
}
int? lightMode;
int? involume;
int? outvolume;
Future<bool> changeVolume(int param, int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=$param&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
// lightMode = value;
}
return result.isSuccess;
}
return false;
}
Future<bool> changeLightMode(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("camera_control.cgi?param=3&value=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24594;
}, timeout);
if (result.isSuccess) {
lightMode = value;
}
return result.isSuccess;
}
return false;
}
Future<bool> getCameraParams({int timeout = 5}) async {
bool ret = await writeCgi("get_camera_params.cgi?");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24579;
}, timeout);
Map data = result.getMap();
if (result.isSuccess) {
this.contrast =
int.tryParse(data["vcontrast"] ?? "") ?? (255 * 0.5).floor();
this.brightness =
int.tryParse(data["vbright"] ?? "") ?? (255 * 0.5).floor();
try {
this.direction =
VideoDirection.values[int.tryParse(data["flip"] ?? "") ?? 0];
} catch (e) {
this.direction = VideoDirection.values[0];
}
this.ircut = int.tryParse(data["ircut"] ?? "") ?? 1;
this.night_vision_mode =
int.tryParse(data["night_vision_mode"] ?? "") ?? 0;
//print"night_vision_mode ===========0>> " + night_vision_mode.toString());
this.full_color_mode =
int.tryParse(data["full_color_mode"] ?? "") ?? 0; //全彩子项为定时
//print"full_color_mode ===========1>> " + full_color_mode.toString());
this.full_color_show =
int.tryParse(data["full_color_show"] ?? "") ?? 0; //全彩子项全显示 二进制的
//print"full_color_show ===========2>> " + full_color_show.toString());
this.full_color_default =
int.tryParse(data["full_color_default"] ?? "") ??
1; //全彩定时 是否勾选默认 0 使用自定义 1 使用默认
//print"full_color_default ===========3>> " + full_color_default.toString());
this.full_color_start_hw =
int.tryParse(data["full_color_start_hw"] ?? "") ??
0; //全彩定时 APP自定义开始时间
this.full_color_end_hw =
int.tryParse(data["full_color_end_hw"] ?? "") ??
0; //全彩定时 APP自定义结束时间
this.full_color_start =
int.tryParse(data["full_color_start"] ?? "") ?? 0; //全彩定时 APP自定义开始时间
//print"full_color_start ===========6>> " + full_color_start.toString());
this.full_color_end =
int.tryParse(data["full_color_end"] ?? "") ?? 0; //全彩定时 APP自定义结束时间
//print"full_color_end ===========7>> " + full_color_end.toString());
try {
this.lightMode = int.tryParse(data["mode"] ?? "") ?? 0;
} catch (e) {
this.lightMode = 0;
}
this.osdEnable = int.tryParse(data["OSDEnable"] ?? "") ?? 0;
this.involume = int.tryParse(data["involume"] ?? "") ?? 0;
this.outvolume = int.tryParse(data["outvolume"] ?? "") ?? 0;
}
return result.isSuccess;
}
return false;
}
int? osdEnable;
Future<bool> changeShowTime(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi("set_misc.cgi?osdenable=$value&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return true;
}, timeout);
if (result.isSuccess) {
osdEnable = value;
}
return result.isSuccess;
}
return false;
}
int? videoFormat;
Future<bool> changeVideoFormat(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2105&command=2&videoFormat=$value&mark=112233445566_1234&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return true;
}, timeout);
if (result.isSuccess) {
videoFormat = value;
}
return result.isSuccess;
}
return false;
}
Future<bool> getVideoDecodingMode({int timeout = 5}) async {
//解码模式
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2105&command=1&mark=112233445566_1234&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2105;") && str.contains("command=1;");
}
return false;
}, timeout);
Map data = result.getMap();
if (result.isSuccess) {
if (int.tryParse(data["cmd"] ?? "") == 2105) {
this.videoFormat = int.tryParse(data["videoFormat"] ?? "") ?? 0;
}
}
return result.isSuccess;
}
return false;
}
///200w 还是300w 参数
int? videoPix;
Future<bool> changeVideoPix(int value, {int timeout = 5}) async {
if (value == null) {
return false;
}
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2105&command=4&bPixel300=$value&mark=112233445566_1234&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return true;
}, timeout);
if (result.isSuccess) {
videoPix = value;
}
return result.isSuccess;
}
return false;
}
Future<bool> getVideoPix({int timeout = 5}) async {
//解码模式
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2105&command=3&mark=112233445566_1234&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
if (cmd == 24785) {
String str = utf8.decode(data);
return str.contains("cmd=2105;") && str.contains("command=3;");
}
return false;
}, timeout);
Map data = result.getMap();
if (result.isSuccess) {
if (int.tryParse(data["cmd"] ?? "") == 2105) {
this.videoPix = int.tryParse(data["bPixel300"] ?? "") ?? 0;
}
}
return result.isSuccess;
}
return false;
}
///双击坐标放大
Future<bool> videoDoubleTap(int width, int height, int doublex, int doubley,
{int timeout = 5}) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2163&command=0&mark=112233445566_1234&width=$width&height=$height&x=$doublex&y=$doubley&");
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24785;
}, timeout);
if (result.isSuccess) {
return result.isSuccess;
}
}
return false;
}
@override
Future<void> deviceDestroy() async {
super.deviceDestroy();
}
}
import 'camera_command.dart';
mixin VoiceCommand on CameraCommand {
Future<bool> _startSoundCgi(bool bSupportg711a, {int count = 0}) async {
final ret = bSupportg711a
? await writeCgi("audiostream.cgi?streamid=7&")
: await writeCgi("audiostream.cgi?streamid=1&adpcm_ver=1&");
if (!ret) return false;
final value = await waitCommandResult((cmd, data) {
return cmd == 24625;
}, 3);
if (!value.isSuccess && count < 3) {
return _startSoundCgi(bSupportg711a, count: count + 1);
}
return value.isSuccess;
}
Future<bool> _stopSoundCgi({int count = 0}) async {
final ret = await writeCgi("audiostream.cgi?streamid=16&");
if (!ret) return false;
final value = await waitCommandResult((cmd, data) {
return cmd == 24625;
}, 3);
if (!value.isSuccess && count < 3) {
return _stopSoundCgi(count: count + 1);
}
return value.isSuccess;
}
bool supportG711() {
return statusResult!.support_audio_g711a != null &&
statusResult!.support_audio_g711a != "0";
}
bool supportbothWay() {
return statusResult!.EchoCancellationVer != null &&
statusResult!.EchoCancellationVer != "0";
}
Future<bool> startSoundStream() async {
return await _startSoundCgi(supportG711());
}
Future<bool> stopSoundStream() async {
return await _stopSoundCgi();
}
@override
Future<void> deviceDestroy() async {
super.deviceDestroy();
}
}
import 'dart:async';
import 'dart:convert';
import 'package:vsdk/p2p_device/p2p_command.dart';
import 'package:vsdk/p2p_device/p2p_device.dart';
import '../../device_wakeup_server.dart';
import 'camera_command.dart';
typedef WakeupStateChanged = void Function(
P2PBasisDevice device, DeviceWakeupState? wakeupState);
mixin WakeupCommand on CameraCommand {
DeviceWakeupState? _wakeupState;
DeviceWakeupState? get wakeupState => _wakeupState;
set wakeupState(DeviceWakeupState? value) {
if (value != _wakeupState) {
_wakeupState = value;
notifyListeners<WakeupStateChanged>((WakeupStateChanged func) {
func(this, _wakeupState);
});
}
}
void _wakeupListener(String did, DeviceWakeupState state) {
if (state != _wakeupState) {
wakeupState = state;
}
}
Timer? keepAliveTimer;
Future<bool> _keepAliveCgi(int time) async {
bool ret = await writeCgi(
"trans_cmd_string.cgi?cmd=2131&command=1&DevActiveTime=$time&",
timeout: (time / 2).floor());
if (ret == true) {
CommandResult result = await waitCommandResult((cmd, data) {
return cmd == 24785 && utf8.decode(data).contains("cmd=2131;");
}, (time / 2).floor());
Map map = result.getMap();
if (map != null && map.containsKey("DevActiveTime")) {
return true;
} else {
return false;
}
}
return ret;
}
void keepAlive({int time = 10}) async {
keepAliveTimer?.cancel();
keepAliveTimer =
Timer.periodic(Duration(seconds: (time / 2).floor()), (timer) {
_keepAliveCgi(time);
});
}
void cancelKeepAlive() {
keepAliveTimer?.cancel();
}
void addListener<T>(T listener) async {
if (listener != null &&
listener is WakeupStateChanged &&
_addListener == false) {
DeviceWakeUpServer().removeListener(id, _wakeupListener);
DeviceWakeUpServer().addListener(id, _wakeupListener);
}
super.addListener(listener);
}
bool _addListener = false;
void requestWakeup() {
DeviceWakeUpServer().wakeup(id);
Timer(Duration(seconds: 3), () {
DeviceWakeUpServer().wakeup(id);
});
}
void requestWakeupOnce() {
DeviceWakeUpServer().wakeup(id);
}
void requestWakeupStatus() {
var bl = DeviceWakeUpServer().checkNotNode(id);
if (bl) return;
DeviceWakeUpServer().getStatus(id);
}
@override
Future<void> deviceDestroy() async {
cancelKeepAlive();
DeviceWakeUpServer().removeListener(id, _wakeupListener);
DeviceWakeUpServer().removeAutoWakeup(id);
super.deviceDestroy();
}
}
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'app_player.dart';
enum VideoState {
/// 默认状态
none,
/// 准备中
starting,
/// 正在播放
playing,
/// 暂停
pause,
/// 停止
stop,
/// 出现错误
error,
}
enum AudioState {
/// 默认状态
none,
/// 正在播放
play,
/// 停止
stop,
/// 出现错误
error,
}
class CameraPlayer {
int clientPtr = 0;
/// 图形绘制纹理
Rx<int> texture = Rx<int>(0);
/// 播放控制器
late AppPlayerController controller;
/// 视频状态
Rx<VideoState> videoState = Rx<VideoState>(VideoState.none);
/// 音频状态
Rx<AudioState> audioState = Rx<AudioState>(AudioState.none);
/// 视频时长
/// 秒为单位
var duration = 0.obs;
/// 播放进度
/// 秒为单位
var progress = 0.obs;
}
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'dart:math';
import 'package:crypto/crypto.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:equatable/equatable.dart';
int _byte4Int(List<int> bytes) {
int value = 0;
for (int i = 0; i < 4; i++) {
value |= (bytes[i] & 0xFF) << (8 * (3 - i));
}
return value;
}
List<int> _int4byte(int value) {
List<int> bytes = [];
for (int i = 0; i < 4; i++) {
bytes.add((value >> 8 * (3 - i)) & 0xFF);
}
return bytes;
}
Map _encryptionMap(Map data) {
var secretKey = '2LZaRSu4ND5f7wJ5';
data['AccessKey'] = '9QDU2DZ3gulyXnze';
data['timestamp'] =
(DateTime.now().toUtc().millisecondsSinceEpoch / 1000).floor();
data['sign'] = Random().nextInt(9999);
data['signature'] = _encryption(secretKey, data);
return data;
}
String _encryption(String secretKey, Map data) {
List<String> headers = [];
for (String key in data.keys) {
if (key != 'signature') {
headers.add(key);
}
}
//字典排序
headers.sort((a, b) {
return a.compareTo(b);
});
var parameter = "";
for (var i = 0; i < headers.length; i++) {
var name = headers[i];
parameter = '$parameter$name${data[name]}';
}
String str = base64Encode(
Hmac(sha1, utf8.encode(secretKey)).convert(utf8.encode(parameter)).bytes);
return str;
}
List<int> _socketBuffer(List<int> bytes) {
List<int> lenBytes = _int4byte(bytes.length);
List<int> buffer = [];
buffer.insertAll(0, lenBytes);
buffer.insertAll(4, bytes);
return buffer;
}
class SocketData {
List<int> _bufferPool = [];
int? _dataLength;
String? push(List<int> buffer) {
_bufferPool.addAll(buffer);
if (_bufferPool.length >= 4 && _dataLength == null) {
_dataLength = _byte4Int(_bufferPool.sublist(0, 4));
}
if (_dataLength != null && _bufferPool.length >= _dataLength! + 4) {
String? result;
if (_dataLength! > 0)
result = utf8.decode(_bufferPool.sublist(4, _dataLength! + 4));
_bufferPool.removeRange(0, _dataLength! + 4);
_dataLength = null;
return result;
}
return null;
}
void clean() {
_bufferPool.clear();
}
}
abstract class SocketEvent extends Equatable {
final String debugMsg;
SocketEvent(this.debugMsg);
@override
bool get stringify => true;
@override
List<Object> get props => [];
}
class ConnectEvent extends SocketEvent {
ConnectEvent({String debugMsg = ""}) : super(debugMsg);
@override
List<Object> get props => [super.debugMsg];
}
class SendEvent extends SocketEvent {
final List<int> data;
SendEvent(this.data, {String debugMsg = ""}) : super(debugMsg);
@override
List<Object> get props => data;
}
class CloseEvent extends SocketEvent {
CloseEvent({String debugMsg = ""}) : super(debugMsg);
@override
List<Object> get props => [debugMsg];
}
class SocketServer {
late final String ip;
late final int port;
final void Function(String data)? dataListener;
final int keepTime;
late StreamController<SocketEvent> _controller;
Socket? _socket;
late DateTime lastTime;
SocketData _socketData = SocketData();
Timer? _timer;
SocketServer(this.ip, this.port, this.dataListener, {this.keepTime = 5}) {
lastTime = DateTime.now();
_controller = StreamController<SocketEvent>();
_controller.stream
.asyncMap(_eventListen)
.listen(_eventDone, onError: _eventError)
.onError(_eventError);
lastTime = DateTime.now();
_timer = Timer.periodic(Duration(seconds: keepTime), (timer) {
Duration duration = DateTime.now().difference(lastTime);
if (duration.inSeconds > keepTime) {
_controller.add(CloseEvent(debugMsg: "close timeout"));
}
});
_controller.add(ConnectEvent(debugMsg: "connect init"));
}
void close() {
_timer?.cancel();
_controller.close();
_socket?.close();
}
void _eventError(error) {
_controller.add(CloseEvent(debugMsg: "event Error $error"));
}
void _eventDone(SocketEvent event) {
// VPLog.debug("$ip:$port ${event.runtimeType} end:${event.debugMsg}",
// tag: "WAKE");
}
Future<SocketEvent> _eventListen(SocketEvent event) async {
if (event is ConnectEvent) {
if (_socket == null) {
_socket = await _connectServer();
return event;
}
} else if (event is SendEvent) {
if (_socket == null) {
return event;
}
try {
_socket?.add(event.data);
await _socket?.flush();
} catch (e) {
_controller.add(CloseEvent(debugMsg: "send Error $e"));
_controller.add(ConnectEvent(debugMsg: "send Error reConnect"));
_controller.add(SendEvent(event.data, debugMsg: "send Error reSend"));
}
} else if (event is CloseEvent) {
if (_socket != null) {
_socket?.close();
_socket?.destroy();
_socket = null;
}
}
return event;
}
void _dataListen(List<int> buffer) {
lastTime = DateTime.now();
String? data = _socketData.push(buffer);
while (data != null && data.isNotEmpty) {
if (dataListener != null) {
dataListener!(data);
}
data = _socketData.push([]);
}
}
void _dataErrorListen(error) async {
_controller.add(CloseEvent(debugMsg: "readServer Error $error"));
}
Future<Socket> _connectServer() async {
// ignore: close_sinks
lastTime = DateTime.now();
Socket socket =
await Socket.connect(ip, port, timeout: Duration(seconds: 5))
.catchError((error) {
_controller.add(CloseEvent(debugMsg: "connectServer Error $error"));
});
if (socket == null) {
return socket;
}
socket.done.onError((error, stackTrace) {
_controller.add(CloseEvent(debugMsg: "connectServer Error $error"));
});
lastTime = DateTime.now();
_socketData.clean();
var stream = socket.handleError(_dataErrorListen);
stream
.listen(_dataListen, onError: _dataErrorListen, cancelOnError: true)
.onError(_dataErrorListen);
return socket;
}
void send(List<int> data) async {
if (_socket == null) {
_controller.add(ConnectEvent(debugMsg: "send connect"));
}
_controller.add(SendEvent(data, debugMsg: "send"));
}
}
enum DeviceWakeupState {
offline,
deepSleep,
sleep,
online,
poweroff,
microPower, //微功耗模式
lowPowerOff, //低电关机
}
typedef DeviceWakeupStateChanged = void Function(
String did, DeviceWakeupState state);
typedef WakeupServerStateChanged = void Function(
String did, String server, DeviceWakeupState state);
class WakeupServer {
late final String host;
late final int port;
late final keepTime;
late final String shareKey;
late final WakeupServerStateChanged listener;
late SocketServer _socketServer;
WakeupServer(
this.host, this.port, this.keepTime, this.shareKey, this.listener) {
_socketServer = SocketServer(this.host, this.port, _dataListen,
keepTime: this.keepTime);
}
HashMap<String, NodeClient> _clientNodes = HashMap();
HashMap<String, NodeClient> _deviceNodes = HashMap();
NodeClient _getOrAddNode(String did, String ip, int port) {
String nodeKey = '$ip:$port';
NodeClient? nodeClient = _clientNodes[nodeKey];
if (nodeClient == null) {
nodeClient = NodeClient(ip, port, _stateListener);
_clientNodes[nodeKey] = nodeClient;
}
if (_deviceNodes[did] == null || _deviceNodes[did]!.ip != ip)
_deviceNodes[did] = nodeClient;
if (_autoWakeupMap.containsKey(did)) {
String dip = _autoWakeupMap[did]["ip"];
int dport = _autoWakeupMap[did]["port"];
if (dip != ip || dport != port) {
saveAutoWakeup(did);
}
}
return nodeClient;
}
void _dataListen(String? data) {
if (data != null) {
Map map = json.decode(data);
if (map["event"] == "getDeviceInfo" && map["ret"] == 1) {
String ip = map["node_ip"];
int port = map["node_port"];
String did = map["did"];
DeviceWakeUpServer().addHasNode(did);
NodeClient nodeClient = _getOrAddNode(did, ip, port);
if (_wakeupMap.containsKey(did)) {
nodeClient.wakeUp(did);
_wakeupMap.remove(did);
}
if (_statusMap.containsKey(did)) {
nodeClient.getStatus(did);
_statusMap.remove(did);
}
} else if (map["event"] == "getDeviceInfo" && map["ret"] == 0) {
String did = map["did"];
DeviceWakeUpServer().addNotNode(port, did);
}
}
}
void _requestNode(String did) {
String str = json.encode(_encryptionMap({
"event": "getDeviceInfo",
'did': did,
}));
List<int> bytes = utf8.encode(str);
var buffer = _socketBuffer(bytes);
_socketServer.send(buffer);
}
Map<String, DeviceWakeupState> _deviceState = Map();
void _stateListener(String did, DeviceWakeupState state) {
if (_deviceState[did] != state) {
_deviceState[did] = state;
if (listener != null) {
listener(did, shareKey, state);
}
}
}
Map _wakeupMap = Map();
void wakeup(String did) {
_requestNode(did);
if (_deviceNodes.containsKey(did)) {
print("-------wakeup------did-$did------------");
_deviceNodes[did]!.wakeUp(did);
}
_wakeupMap[did] = true;
}
Map _statusMap = Map();
void getStatus(String did) {
if (DeviceWakeUpServer().hasNotNode(port, did) == true) return;
_requestNode(did);
if (_deviceNodes.containsKey(did)) {
_deviceNodes[did]!.getStatus(did);
} else {
_statusMap[did] = true;
}
}
Map<dynamic, dynamic> _autoWakeupMap = Map();
Future<void> autoWakeup() async {
SharedPreferences sp = await SharedPreferences.getInstance();
String? str = sp.getString(shareKey);
if (str != null) {
_autoWakeupMap = json.decode(str);
_autoWakeupMap.forEach((key, value) {
_requestNode(key);
if (value.isNotEmpty) {
NodeClient client = _getOrAddNode(key, value["ip"], value["port"]);
client.wakeUp(key);
}
});
}
}
Future<bool> checkAutoWakeup(String did) async {
if (_autoWakeupMap.isEmpty) {
await autoWakeup();
}
return _autoWakeupMap.containsKey(did);
}
Future<void> saveAutoWakeup(String did) async {
if (_deviceNodes.containsKey(did)) {
NodeClient client = _deviceNodes[did]!;
_autoWakeupMap[did] = {"ip": client.ip, "port": client.port};
SharedPreferences sp = await SharedPreferences.getInstance();
await sp.setString(shareKey, json.encode(_autoWakeupMap));
} else {
_autoWakeupMap[did] = {};
_requestNode(did);
}
}
Future<void> removeAutoWakeup(String did) async {
if (_autoWakeupMap.containsKey(did)) {
SharedPreferences sp = await SharedPreferences.getInstance();
_autoWakeupMap.remove(did);
await sp.setString(shareKey, json.encode(_autoWakeupMap));
}
}
void close() {
_deviceState.clear();
if (_clientNodes != null && _clientNodes.isNotEmpty) {
_clientNodes.forEach((key, value) {
value.close();
});
_clientNodes.clear();
}
_deviceNodes.clear();
}
}
class DeviceWakeUpServer {
// ignore: non_constant_identifier_names
static String SP_KEY = "wakeup_device";
/// 单例
static DeviceWakeUpServer? _instance;
/// 将构造函数指向单例
factory DeviceWakeUpServer() => getInstance();
///获取单例
static DeviceWakeUpServer getInstance() {
if (_instance == null) {
_instance = new DeviceWakeUpServer._internal();
}
return _instance!;
}
late WakeupServer _mobileServer;
late WakeupServer _wifiServer;
void reloadAagin() {
_instance = null;
_instance = new DeviceWakeUpServer._internal();
}
String lowPath = 'liteos-master.eye4.cn';
DeviceWakeUpServer._internal() {
_wifiServer =
WakeupServer(lowPath, 32320, 5, "WIFI_WAKE_KEY", _stateListener);
_mobileServer =
WakeupServer(lowPath, 12320, 5, "MOBILE_WAKE_KEY", _stateListener);
}
HashMap<String, List<DeviceWakeupStateChanged>> _listeners = HashMap();
Map<String, DeviceWakeupState> _deviceState = Map();
Map _deviceLastServer = Map();
HashMap<String, bool> _notWifiNodes = HashMap();
HashMap<String, bool> _notMobileNodes = HashMap();
HashMap<String, bool> _hasNodes = HashMap();
void addHasNode(String did) {
_hasNodes[did] = true;
}
void addNotNode(int port, String did) {
if (port == 32320)
_notWifiNodes[did] = true;
else
_notMobileNodes[did] = true;
}
bool hasNotNode(int port, String did) {
if (port == 32320)
return _notWifiNodes[did] ?? true;
else
return _notMobileNodes[did] ?? true;
}
bool checkNotNode(String did) {
if (_notWifiNodes[did] == true && _notMobileNodes[did] == true) return true;
return false;
}
void removeNotNode(int port, String did) {
if (port == 32320)
_notWifiNodes.remove(did);
else
_notMobileNodes.remove(did);
}
void _stateListener(String did, String serverKey, DeviceWakeupState state) {
if (_listeners.containsKey(did) && _deviceState[did] != state) {
if (_deviceLastServer.containsKey(did) && _deviceState.containsKey(did)) {
if (_deviceLastServer[did] != serverKey) {
if (state.index < _deviceState[did]!.index) {
return;
}
}
}
_deviceState[did] = state;
_deviceLastServer[did] = serverKey;
List<DeviceWakeupStateChanged>? listeners = _listeners[did];
if (listeners != null) {
for (DeviceWakeupStateChanged item in listeners) {
item(did, state);
}
}
}
}
void removeListener(String did, DeviceWakeupStateChanged listener) {
List<DeviceWakeupStateChanged>? listeners = _listeners[did];
if (listeners != null) {
listeners.remove(listener);
}
}
void addListener(String did, DeviceWakeupStateChanged listener) {
List<DeviceWakeupStateChanged>? listeners = _listeners[did];
if (listeners == null) {
listeners = [];
_listeners[did] = listeners;
}
if (_deviceState.containsKey(did)) listener(did, _deviceState[did]!);
listeners.add(listener);
}
void wakeup(String did) {
_wifiServer.wakeup(did);
_mobileServer.wakeup(did);
}
void getStatus(String did) {
_wifiServer.getStatus(did);
_mobileServer.getStatus(did);
}
Future<void> autoWakeup() async {
await _wifiServer.autoWakeup();
await _mobileServer.autoWakeup();
}
Future<bool> checkAutoWakeup(String did) async {
return await _wifiServer.checkAutoWakeup(did);
}
Future<void> saveAutoWakeup(String did) async {
await _wifiServer.saveAutoWakeup(did);
await _mobileServer.saveAutoWakeup(did);
}
Future<void> removeAutoWakeup(String did) async {
await _wifiServer.removeAutoWakeup(did);
await _mobileServer.removeAutoWakeup(did);
}
void close() {
_wifiServer.close();
_mobileServer.close();
_listeners.clear();
_deviceLastServer.clear();
_deviceState.clear();
}
}
class NodeClient {
final String ip;
final int port;
late SocketServer _socketServer;
DeviceWakeupStateChanged? listener;
NodeClient(this.ip, this.port, this.listener) {
_socketServer = SocketServer(ip, port, _dataListen);
_register();
}
void _register() {
String str = json.encode(_encryptionMap({"event": "register"}));
List<int> bytes = utf8.encode(str);
var buffer = _socketBuffer(bytes);
_socketServer.send(buffer);
}
void _callback(String did, DeviceWakeupState state) {
if (listener != null) listener!(did, state);
}
Timer? _timer;
void close() {
_timer?.cancel();
_socketServer.close();
}
void _dataListen(String data) {
if (data != null) {
Map map = json.decode(data);
if (map != null) {
if (map["event"] == "register") {
// _timer?.cancel();
// _timer = Timer.periodic(Duration(seconds: 3), (timer) {
// if (DateTime
// .now()
// .millisecondsSinceEpoch - _lastTime > 3) {
// _socketServer.send([0, 0, 0, 0]);
// }
// });
} else if (map["event"] == "timeout") {
} else if (map["event"] == "getStatus") {
String did = map["did"];
var status = map['status'];
if (status == 'activation') {
_callback(did, DeviceWakeupState.online);
} else if (status == 'sleep') {
_callback(did, DeviceWakeupState.sleep);
} else if (status == 'offline') {
_callback(did, DeviceWakeupState.offline);
} else if (status == 'ULPC') {
_callback(did, DeviceWakeupState.deepSleep);
} else if (status == 'power_off') {
_callback(did, DeviceWakeupState.poweroff);
} else if (status == 'ULPC2') {
_callback(did, DeviceWakeupState.microPower);
} else if (status == 'LBS') {
_callback(did, DeviceWakeupState.lowPowerOff);
}
} else if (map["event"] == "toDevice") {
String did = map["did"];
String status = map["deviceStatus"];
if (status == "offline") {
_callback(did, DeviceWakeupState.offline);
} else if (status == 'activation') {
_callback(did, DeviceWakeupState.online);
} else if (status == 'sleep') {
_callback(did, DeviceWakeupState.sleep);
} else if (status == 'offline') {
_callback(did, DeviceWakeupState.offline);
} else if (status == 'ULPC') {
_callback(did, DeviceWakeupState.deepSleep);
} else if (status == 'power_off') {
_callback(did, DeviceWakeupState.poweroff);
} else if (status == 'ULPC2') {
_callback(did, DeviceWakeupState.microPower);
} else if (status == 'LBS') {
_callback(did, DeviceWakeupState.lowPowerOff);
}
} else if (map["event"] == "sleep") {
String did = map["did"];
_callback(did, DeviceWakeupState.sleep);
} else if (map["event"] == "online") {
String did = map["did"];
_callback(did, DeviceWakeupState.online);
} else if (map["event"] == "offline") {
String did = map["did"];
_callback(did, DeviceWakeupState.offline);
} else if (map["event"] == "ULPC") {
String did = map["did"];
_callback(did, DeviceWakeupState.deepSleep);
} else if (map["event"] == "power_off") {
String did = map["did"];
_callback(did, DeviceWakeupState.poweroff);
} else if (map["event"] == 'ULPC2') {
String did = map["did"];
_callback(did, DeviceWakeupState.microPower);
} else if (map["event"] == 'LBS') {
String did = map["did"];
_callback(did, DeviceWakeupState.lowPowerOff);
}
}
}
}
void getStatus(String did) {
String str =
json.encode(_encryptionMap({'event': 'getStatus', 'did': did}));
List<int> bytes = utf8.encode(str);
var buffer = _socketBuffer(bytes);
_socketServer.send(buffer);
}
void wakeUp(String did) {
String str = json.encode(
_encryptionMap({"event": "toDevice", "did": did, "cmd": "wakeup"}));
List<int> bytes = utf8.encode(str);
var buffer = _socketBuffer(bytes);
var now = DateTime.now();
_socketServer.send(buffer);
}
}
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:fast_gbk/fast_gbk.dart';
import '../app_p2p_api.dart';
import 'p2p_connect.dart';
import 'p2p_device.dart';
typedef CommandFilterFunc = bool Function(int cmd, Uint8List data);
typedef CommandCallbackFunc = void Function(int cmd, CommandResult result);
class CommandCondition {
final CommandFilterFunc filter;
Completer<CommandResult> completer;
CommandCondition(this.filter, this.completer);
}
class UTF8Check {
int _getUtf8charByteNum(int ch) {
int byteNum = 0;
if (ch >= 0xFC && ch < 0xFE) {
byteNum = 6;
} else if (ch >= 0xF8) {
byteNum = 5;
} else if (ch >= 0xF0) {
byteNum = 4;
} else if (ch >= 0xE0) {
byteNum = 3;
} else if (ch >= 0xC0) {
byteNum = 2;
} else if (0 == (ch & 0x80)) {
byteNum = 1;
}
return byteNum;
}
bool isUtf8Format(List<int> buffer) {
if (buffer.isEmpty) return false;
int byteNum = 0;
int ch = 0;
int off = 0;
do {
ch = buffer[off];
if (byteNum == 0) {
if (0 == (byteNum = _getUtf8charByteNum(ch))) return false;
} else {
if ((ch & 0xC0) != 0x80) return false;
}
byteNum--;
off++;
} while (off < buffer.length);
if (byteNum > 0) return false;
return true;
}
}
class CommandResult {
final bool isSuccess;
final int cmd;
final Uint8List? data;
static UTF8Check utf8check = UTF8Check();
CommandResult(this.isSuccess, this.cmd, this.data);
String getString() {
if (data == null) {
return "";
}
try {
return utf8.decode(data!);
} catch (e) {
return gbk.decode(data!);
}
}
String getString2(List<int> buffer) {
if (utf8check.isUtf8Format(buffer)) {
try {
return utf8.decode(buffer, allowMalformed: true);
} catch (e) {
return String.fromCharCodes(buffer);
}
} else {
try {
return gbk.decode(buffer, allowMalformed: true);
} catch (e) {
return String.fromCharCodes(buffer);
}
}
}
List<int> _replaceAll(Map<int, int> replace) {
var newData = data?.toList() ?? [];
List<int> removeList = [];
for (int i = 0; i < newData.length; i++) {
var item = newData[i];
if (replace.containsKey(item)) {
var value = replace[item];
if (value == 0) {
removeList.add(i);
} else {
newData[i] = value!;
}
}
}
var removeLen = 0;
for (var element in removeList) {
newData.removeAt(element - removeLen);
removeLen += 1;
}
return newData;
}
List<List<int>> _split(List<int> data, int sp) {
List<List<int>> splitBuffer = [];
int index = 0;
int start = 0;
while (index >= 0) {
index = data.indexOf(sp, start);
if (index > 0) {
var spBuffer = data.getRange(start, index).toList();
splitBuffer.add(spBuffer);
start = index + 1;
}
}
return splitBuffer;
}
Map getMap() {
if (data == null || data!.isEmpty) return {};
var newData = _replaceAll({'\r'.codeUnitAt(0): 0, '\n'.codeUnitAt(0): 0});
var lines = _split(newData, ';'.codeUnitAt(0));
Map maps = {};
for (var buffer in lines) {
var line = getString2(buffer);
if (line.contains("=")) {
List<String> keyValue = line.split("=");
if (keyValue.length >= 2) {
if (keyValue[0].trim().replaceAll("var ", "") == 'AiCfg') {
var value = keyValue[1].trim().replaceFirst('"', '');
int lastIndex = value.lastIndexOf('"');
value = value.substring(0, lastIndex);
maps.putIfAbsent(
keyValue[0].trim().replaceAll("var ", ""), () => value.trim());
} else {
maps.putIfAbsent(keyValue[0].trim().replaceAll("var ", ""),
() => keyValue[1].trim().replaceAll('"', ""));
}
}
}
}
return maps;
}
}
mixin P2PCommand on P2PBasisDevice, P2PConnect {
final List<CommandCondition> _commandFilters = [];
final Map<int, CommandCallbackFunc> _callbackMaps = {};
void _onCommandListener(int cmd, Uint8List data) {
List<CommandCondition> _remove = [];
for (var item in _commandFilters) {
if (item.filter(cmd, data)) {
_remove.add(item);
if (!item.completer.isCompleted) {
item.completer.complete(CommandResult(true, cmd, data));
}
break;
} else {}
}
if (_remove.isEmpty) {
if (_callbackMaps.containsKey(cmd)) {
_callbackMaps[cmd]!(cmd, CommandResult(true, cmd, data));
}
}
for (var item in _remove) {
_commandFilters.remove(item);
}
}
void addCallback(int cmd, CommandCallbackFunc func) {
_callbackMaps[cmd] = func;
}
void removeCallback(int cmd) {
_callbackMaps.remove(cmd);
}
///数据发送,用于发送原始数据
Future<bool> write(
ClientChannelType channel, Uint8List buffer, int timeout) async {
if (p2pConnectState != ClientConnectState.CONNECT_STATUS_ONLINE) {
return false;
}
int clientPtr = await getClientPtr();
int ret =
await AppP2PApi().clientWrite(clientPtr, channel, buffer, timeout);
return ret == buffer.length;
}
/// 发送CGI指令
/// 底层封装CGI数据格式
Future<bool> writeCgi(String cgi,
{int timeout = 5, bool needLogin = true}) async {
if (p2pConnectState != ClientConnectState.CONNECT_STATUS_ONLINE) {
return false;
}
int clientPtr = await getClientPtr();
bool bl =
await AppP2PApi().clientWriteCgi(clientPtr, cgi, timeout: timeout);
if (bl != true) {
bl = await AppP2PApi().clientWriteCgi(clientPtr, cgi, timeout: timeout);
}
return bl;
}
///等待指令返回
///@param [filterFunc] 指令筛选条件
///@param [timeout] 等待时间 秒为单位
///
///@return [CommandResult]
Future<CommandResult> waitCommandResult(
CommandFilterFunc filterFunc, int timeout) async {
Completer<CommandResult> completer = Completer();
CommandCondition condition = CommandCondition(filterFunc, completer);
Future.delayed(Duration(seconds: timeout), () {
_commandFilters.remove(condition);
if (!completer.isCompleted) {
completer.complete(CommandResult(false, 0, null));
}
});
_commandFilters.add(condition);
return completer.future;
}
/// 设置指令监听
Future<void> setCommandListener() async {
int clientPtr = await getClientPtr();
AppP2PApi().setCommandListener(clientPtr, _onCommandListener);
}
void _clearPendingCommands() {
for (final item in List<CommandCondition>.of(_commandFilters)) {
if (!item.completer.isCompleted) {
item.completer.complete(CommandResult(false, 0, null));
}
}
_commandFilters.clear();
_callbackMaps.clear();
}
/// 移除指令监听
Future<void> removeCommandListener() async {
_clearPendingCommands();
final clientPtr = await getClientPtr();
if (clientPtr != 0) {
AppP2PApi().removeCommandListener(clientPtr);
}
}
}
import 'dart:async';
import '../app_p2p_api.dart';
import 'p2p_device.dart';
typedef void P2PConnectStateChanged(
P2PBasisDevice sender, ClientConnectState state);
/// P2P连接功能
mixin P2PConnect on P2PBasisDevice {
/// 连接状态
ClientConnectState _p2pConnectState = ClientConnectState.CONNECT_STATUS_MAX;
ClientConnectState get p2pConnectState => _p2pConnectState;
set p2pConnectState(ClientConnectState value) {
if (value != _p2pConnectState) {
_p2pConnectState = value;
notifyListeners<P2PConnectStateChanged>((P2PConnectStateChanged func) {
func(this, _p2pConnectState);
});
}
}
/// 连接状态回调
/// 但读取数据失败时,触发回调[ClientConnectState.CONNECT_STATUS_DISCONNECT]
void _onConnectListener(ClientConnectState state) async {
p2pConnectState = state;
if (state == ClientConnectState.CONNECT_STATUS_DISCONNECT) {
int clientPtr = await getClientPtr();
await AppP2PApi().clientDisconnect(clientPtr);
if (_disconnectCompleter != null) {
_disconnectCompleter!.complete(true);
_disconnectCompleter = null;
}
}
}
Completer<bool>? _connectCompleter;
/// 等待设备连接中断
Future<bool> _waitConnected() async {
if (_connectCompleter != null) {
bool bl = await _connectCompleter!.future
.timeout(Duration(seconds: 10), onTimeout: () => false);
_connectCompleter = null;
return bl;
}
return false;
}
int _connectByTimeTimeout(int clientPtr) {
return -3;
}
Future<int> connectByTime(
{required int clientPtr,
required bool lanScan,
required int reConnectCount,
required int connectType}) async {
String? serverParam = await getServiceParam();
int time = await AppP2PApi()
.clientCheckTimeout(clientPtr, lanScan, serverParam,
connectType: connectType)
.timeout(Duration(seconds: 15),
onTimeout: () => _connectByTimeTimeout(clientPtr));
return time;
}
ClientConnectState _beginConnectTimeout(int clientPtr) {
return ClientConnectState.CONNECT_STATUS_CONNECT_TIMEOUT;
}
Future<ClientConnectState> _beginConnect(int clientPtr, String serverParam,
bool lanScan, int reConnectCount, int connectType) async {
_connectCompleter = Completer();
p2pConnectState = ClientConnectState.CONNECT_STATUS_CONNECTING;
_isDisconnect = false;
ClientConnectState state = ClientConnectState.CONNECT_STATUS_DISCONNECT;
do {
state = await AppP2PApi()
.clientConnect(clientPtr, lanScan, serverParam,
connectType: connectType)
.onError((error, stackTrace) =>
ClientConnectState.CONNECT_STATUS_DISCONNECT)
.timeout(Duration(seconds: 20),
onTimeout: () => _beginConnectTimeout(clientPtr));
reConnectCount -= 1;
} while (state == ClientConnectState.CONNECT_STATUS_CONNECT_TIMEOUT &&
reConnectCount > 0 &&
!_isDisconnect);
if (_isDisconnect) {
state = ClientConnectState.CONNECT_STATUS_DISCONNECT;
}
if (_connectCompleter!.isCompleted == false)
_connectCompleter!.complete(true);
return state;
}
/// 进行连接
/// @param [lanScan] 是否进行局域网搜索
/// @param [recount] 重连次数
///
/// @return true 调用成功
/// false 调用失败
Future<ClientConnectState> p2pConnect(
{bool lanScan = true,
int reConnectCount = 2,
required int connectType}) async {
if (p2pConnectState == ClientConnectState.CONNECT_STATUS_CONNECTING) {
return p2pConnectState;
}
p2pConnectState = ClientConnectState.CONNECT_STATUS_CONNECTING;
int clientPtr = await getClientPtr();
if (clientPtr == null || clientPtr == 0) {
p2pConnectState = ClientConnectState.CONNECT_STATUS_INVALID_CLIENT;
return p2pConnectState;
}
String? serverParam = await getServiceParam();
if (serverParam == null) {
p2pConnectState = ClientConnectState.CONNECT_STATUS_INVALID_CLIENT;
return p2pConnectState;
}
ClientConnectState state = await _beginConnect(
clientPtr, serverParam, lanScan, reConnectCount, connectType);
if (state == ClientConnectState.CONNECT_STATUS_ONLINE) {
_disconnectCompleter = Completer();
AppP2PApi().setConnectListener(clientPtr, _onConnectListener);
} else {}
p2pConnectState = state;
return p2pConnectState;
}
bool _isDisconnect = false;
Completer<bool>? _disconnectCompleter;
/// 等待连接断开
/// 如果设备已经连接处于ONLINE 状态,需要等待设备回调DISCONNECT
/// 才能确定设备已经完全断开了
Future<bool> _waitDisconnected() async {
if (_disconnectCompleter != null) {
bool bl = await _disconnectCompleter!.future
.timeout(Duration(seconds: 10), onTimeout: () async {
return false;
});
_disconnectCompleter = null;
return bl;
}
return true;
}
/// 断开连接
/// 如果设备正处于连接状态,需要等待设备退出连接后再调用
/// `bool ret = await AppP2PApi().clientDisconnect(clientPtr);`
/// 调用`clientDisconnect`如果返回true,则说明设备曾经连接成功,
/// 需要等待设备回调disconnect才能确保设备已经完全断开连接
/// 需要调用`await _waitDisconnected()`来确认设备已经完全断开
///
/// @return true 连接已断开
/// false 断开失败
///
Future<bool> disconnect() async {
int clientPtr = await getClientPtr();
AppP2PApi().clientConnectBreak(clientPtr);
bool ret = await _waitConnected();
ret = await AppP2PApi().clientDisconnect(clientPtr);
if (ret) {
ret = await _waitDisconnected();
AppP2PApi().removeConnectListener(clientPtr);
}
return ret;
}
}
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import '../app_p2p_api.dart';
import '../basis_device.dart';
abstract class P2PBasisDevice extends BasisDevice {
P2PBasisDevice(
String id, this.username, this.password, String name, String model,
{String? clientId})
: _clientId = clientId,
super(id, name, model) {
if (clientId != null) {
_localClientId = true;
}
RegExp exp = RegExp(r'^[a-zA-Z]{1,}\d{7,}.*[a-zA-Z]$');
isVirtualId = exp.hasMatch(id);
_controller.stream.asyncMap(_createClientPtr).listen((event) {});
_controller.add(Completer());
}
bool _localClientId = false;
late String username;
late String password;
late String cluster;
late String supplier;
late bool isVirtualId;
String? _clientId;
Future<String> getClientId() async {
if (_clientId != null) return _clientId!;
if (isVirtualId) {
_clientId = await _requestClientId();
} else {
_clientId = id;
}
return _clientId!;
}
set clientId(String value) {
_clientId = value;
}
int? _clientPtr;
int? get clientPtr => _clientPtr;
StreamController<Completer<int>> _controller =
StreamController<Completer<int>>(sync: true);
Future<int> getClientPtr() async {
if (_clientPtr != null) return _clientPtr!;
Completer<int> completer = Completer();
_controller.add(completer);
return completer.future;
}
Future<bool> changeClientId() async {
bool result = false;
if (_localClientId == true && isVirtualId) {
String? temp = _clientId;
_clientId = null;
String clientId = await getClientId();
if (clientId != null) {
result = await AppP2PApi().clientChangeId(_clientPtr!, clientId);
if (result == true) {
temp = _serviceParam;
_serviceParam = null;
_serviceParam = await getServiceParam();
if (_serviceParam == null) {
_serviceParam = temp;
}
}
} else {
_clientId = temp;
}
}
return result;
}
Future<int> _createClientPtr(Completer<int> completer) async {
if (_clientPtr == null) {
String clientId = await getClientId();
if (clientId != null) {
_clientPtr = await AppP2PApi().clientCreate(clientId);
}
}
if (!completer.isCompleted) completer.complete(_clientPtr);
return _clientPtr!;
}
String? _serviceParam;
Future<String?> getServiceParam() async {
if (_serviceParam != null) {
return _serviceParam!;
}
_serviceParam = await _requestServiceParam();
return _serviceParam;
}
Map _serviceMap = {
"ETIM":
"ECGAEKBKKAJOHFJFEAGDEOEOHJMHHJNAGMEEBHCFBIJALIKMDMANDLPNGGLOIOLNALMJKFDOOIMABKCKJIMM",
"SURE":
"BKGPAGBMKFIBDKJHAGGLFJBEHPJECKNIDDFIEBHDFFJKOHKIHDFJCALLHMLAMEKDFGMGLGDCLJMEEFDOJEIPIGENJP",
"PARC":
"EBGBEMBMKGJMGAJPEIGIFKEGHBMCHMNFGKEGBFCBBMJELILDCJADCIOLHHLLJBKEAMMBLCDGONMDBJCJJPNFJP",
"ESCM":
"AACCEIBLNGIPBOJNFEHDEBAKHHJEGFJBHABNAECLFAJJKLOBHBAFDCIDHKNJIKKFBCMIOPDMLLMGFODDMCNOJGEDNCPMFIGOBEDLEEBLKCODFIHKEOFFKEMLHKNIFDDCEKEKDNNEGK",
"HVC":
"BIHOAHEKOMIOCKIHACHLEJAEGPIEDKJHCNBLFEGGFLNDPFPNHBEHGMKLCNKFMNKMBIMCOIDELFMKFDHPJIIPIEAO",
"ZXBJ":
"BBGIACBPKEIADLJGAHGKFIBFHOJFCLNCDFFNEDHDFFJKOHKIHDFJCALLHMLAMPKEFCMFLFDBLKMHEGDNJHIMIFEOJM",
"AIPC":
"BBGIACBPKEIADLJGAHGKFIBFHOJFCLNCDFFNEDHDFFJKOHKIHDFJCALLHMLAMPKEFCMFLFDBLKMHEGDNJHIMIFEOJM",
"SIP":
"BPGBBOEOKJMBHJNCFFDFAEEJCMMHDANKDFADBNHDBGJMLDLOCIACCJOPHMLCJPKJABMELOCMOCJKABHGIANGNPBLIPOIFPCPAIGJDCFDMMLCEFHLACBDPGNA",
"VSTE":
"EEGDFHBAKKIOGNJHEGHMFEEDGLNOHJMPHAFPBEDLADILKEKPDLBDDNPOHKKCIFKJBNNNKLCPPPNDBFDL",
"ZHJ":
"EFGBFFBKKLJFGJJGECGGFEELHKMIHGNLGBFHBICJBAJILHLPCLABCOOJGPLKJALMAIMJLPDLOIMPAMCAJBNBJL",
"POLI":
"AACCEIBLODIGDIJPFGHDEBAKHHJEGFJBHABNAECLFAJJKLOBHBAFDCLGHDPPIIKHBDMJOODNLKMHFPDCMDNPJHECNDPNFJGPBFDKHABDIFOAFLHIEMFHKGMJHINKFBDAEIEIDPNGGI",
"HSEE":
"AAHEAGAPLGJCCJIEBFHIEKAHGMIHDJNBDLELEBHDFFJKOHKIHDFJCALLHMLANOLIFGNFKFCBKKNHFGCNIHJMJFFOIM",
"VSTA":
"EFGBFFBJKDJKGGJMEAHKFHEJHNNHHANIHIFBBKDFAMJDLKKODNANCBPJGGLEIELOBGNDKADKPGNJBNCNIF",
"VSTC":
"EBGBEMBMKGJMGAJPEIGIFKEGHBMCHMNFGKEGBFCBBMJELILDCJADCIOLHHLLJBKEAMMBLCDGONMDBJCJJPNFJP",
"VSTB":
"EBGBEMBMKGJMGAJPEIGIFKEGHBMCHMNFGKEGBFCBBMJELILDCJADCIOLHHLLJBKEAMMBLCDGONMDBJCJJPNFJP",
"SSFX":
"BGHDBBAGOAINGNINAAHHBOEJGDIGHLIOCMFCFKCDECIKPDPIHEBJGAOBDCPMIBOMAHNPPLCELOMJFDDGIBNHNIBNJGONBHHABFHKGM",
"VSTD":
"HZLXLQIDLKASPHIAHUPDEKICEEAUIHLRSWAOSUPEEJSQLOHWEGPAHYPGLQERIBIALKLNEMENHUHXELEHEEEIEKEP-\$\$",
"VSTG":
"EEGDFHBOKCIGGFJPECHIFNEBGJNLHOMIHEFJBADPAGJELNKJDKANCBPJGHLAIALAADMDKPDGOENEBECCIK:vstarcam2018",
"VSTF":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-\$\$",
"GYNC":
"BKHDACBGKNIJDCJPAOGDFBBMHHJMCCNACHFEEDHAFGJJOEKLHAFKCDLIHPLDMHLMFBMPLMDILDMOEPDEJOIFIMEHJF",
"GCAM":
"DHGEDPAMLFJBCKIHBGHLEJAEGPIEDKOFCIHBEBHCFEJLOGKJHCFICBLKHNLBOIKJGONHKGCCKJNEFFCOIEJPJGFNIP",
"OBJ":
"BPGBBOEOKJMBHJNCFFDFAEEJCMMHDANKDFADBNHDBGJMLDLOCIACCJOPHMLCJPKJABMELOCMOCJKABHGIANGNPBLIPOIFPCPAIGJDCFDMMLCEFHLACBDPGNA",
"ELSO":
"AACCEIBLPGJADBJAFJHMEOAFHIJLGKJOHPBCALCEFPJGKEOOHOAKDNKMGKPJIIKHBDMJOODNLKMHFPDCMDNPJHECNDPNFJGPBFDKGFAFIMOPFEHHEDFIKJMGHHNFFODPEHEHDANJGH",
"ISRP":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-FKJIJFJGIHHXERFSFPFQFAIGELAQSSPCLMHWEQEHEG",
"ELSA":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-FKJIJFJGIHHXERFSFPFQFAIGELAQSSPCLMHWEQEHEG",
"APLK":
"EFGBFFBJKBJLGHJIEPGNFPEGHANKHNNBHBFIBNCKAMJMLKKODOAICCPKGFLBIBLCAMMOKCDJOKMCBICCJDMH",
"EXCE":
"BJHJAABPPKMBDNMABCDGEMALHMNECFNCDBAFBBGAALMMLPLHDLAMCAOECIKBMMOAAFJOLBDHLMNCEFCGMPIEMMFJJBKDBBHMAFCJHABDIEKE",
"AHTM":
"BBGJAHBEKNIJDCJPAOGDFBBMHHJMCCNLDNFBEBHCFEJLOGKJHCFICBLKHNLBMOKEFGMPLODKLBMMENDGJMIHIOEFJH",
"RUSS":
"EBGJEGBGKNJFGJJGEKGLFJEFHDMMHCNLGGEHBECEBKIOLOLCCGAMCEOEHOLCJNKIBMMGLFCPPJMJAMDLIJNPIIBHNPPB",
"WCM":
"AACDBHBOPJIBDPJFFMCLFGBFHEIAHDMFCIFPEPGCFPMNPNPFGFFEGAKGDIOINIONFNIFLHCPODIKBBGBIJJFNOBLIEOLFKCMEHGLDCFBMGOG",
"LNFX":
"AACCBGAEOCILDIJLFBCGFFBGDOIOCMJKCFAFFGGGFKMKPKPFGCEKCAOHGDOPJPPOBJIAOMCFKINLAD",
"EEEE":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-FKJIJFJGIHHXERFSFPFQFAIGELAQSSPCLMHWEQEHEG",
"PPCN":
"EFGBFFBJKDJIGEJIENGKFIEPHMNGDEJOGAEKBJCLBFIGPLKAGPEFDBKAHOLCNMOMBKNNLBCIPMNHEACDMKJMJEFHNNPD",
"EMTV":
"DIGHDDAINHOPBDLEDDFCDHDHFALJEGPCDEGDFABEELMDLEONDKEHHDOECBKMJONEBNKHKAFNIMLMCBEALBLAPIDNKMMDDL",
"HAXZ":
"BIGJALAPKJMGGNMGEBDJEKBBHBNJDIMPGNBNABHIAKJOKOPBCKELDCKKGPPKIAKMFJMCKMGOONILAFDEJOIDJMFPMCKMBHGGAICEGNAONI",
"WGKJ":
"DHGKCEBPNEJGHHJHBKGNAEFDHJJMGBJEDGGJFJAMEBKEPCPJHFBIGBOADDPNIAONAGNONLDMIKNBGGCMJKMMMDAGINPGAMGLAOGBHH",
"QSHV":
"AACCEIBLOCILDIJHFOHLEJACHPJMGNJJHIBFAMCDFIJBKDOJHJANDKLPHGPHIIKHBDMJOODNLKMHFPDCMDNPJHECNDPNFJGPBFDKHBBOIFOIFDHAEEFPKOMBHANCFJDIEAEADHNOGA",
"GCMN":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-FKJIJFJGIHHXERFSFPFQFAIGELAQSSPCLMHWEQEHEG",
"TCXF":
"EBGDEJBJKGJEGIJOENHHFOEEGMNLHINIHIFDBPDAAOJIKIKDDJAMDKPKGHKJIGLEAGNMKMDFOHNHBBCAJFMB",
"YUNV":
"AACCEIBLOKJFCJJBFIHNEPAEHJJKGLJPHOBDAKCFFOJHKFOPHPALDMLBGOOAIIKHBDMJOODNLKMHFPDCMDNPJHECNDPNFJGPBFDKHJAAJEOOFFHGECFJKIMHHGNEFPDOEGEGDBNIGG",
"QHSV":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-FKJIJFJGIHHXERFSFPFQFAIGELAQSSPCLMHWEQEHEG",
"CSCX":
"BDHAAJAPODJDHDJDBOGJAAFHHNJIGFJADCEJEHCFFFJHPDPKHGBLGCODDAPOIDOOAFNNPMCFKEMCFCCKJPMJMGADIIPDAJGOALGEHC",
"VIAN":
"AACCEIBLMFIGBFJFFMHKEIADHOJNGMJIHJBEANCCFJJAKCOIHIAMDLJJHKNLILKEBDMJOODNLKMHFPDCMDNPJHECNDPNFJGPBFDKFGBDKIOKFBHCEGFNKMMDHCNAFLDKECECDFNMGC",
"PISR":
"EFGFFBBOKAIEGHJAEDHJFEEOHMNGDCNJCDFKAKHLEBJHKEKMCAFCDLLLHAOCJPPMBHMNOMCJKGJEBGGHJHIOMFBDNPKNFEGCEGCBGCALMFOHBCGMFK",
"ROSS":
"HZLXEJIALKHYATPCHULNSVLMEELSHWIHPFIBAOHXIDICSQEHENEKPAARSTELERPDLNEPLKEILPHUHXHZEJEEEHEGEM-FKJIJFJGIHHXERFSFPFQFAIGELAQSSPCLMHWEQEHEG",
"RTOS":
"EFGBFFBJKEJKGGJJEEGFFHELHHNNHONHGLFNBHCCAEJDLNLPDDAGCIOFGDLGJMLAAOMOKCDLOONOBICJJIMM",
"VSTH":
"EEGDFHBLKGJIGEJLEKGOFMEDHAMHHJNAGGFABMCOBGJOLHLJDFAFCPPHGILKIKLMANNHKEDKOINIBNCPJOMK:vstarcam2018",
"VSTJ":
"EEGDFHBLKGJIGEJNEOHEFBEIGANCHHMBHIFEAHDEAMJCKCKJDJAFDDPPHLKJIHLMBENHKDCHPHNJBODA:vstarcam2019",
"VSTK":
"EBGDEJBJKGJFGJJBEFHPFCEKHGNMHNNMHMFFBICPAJJNLDLLDHACCNONGLLPJGLKANMJLDDHODMEBOCIJEMA:vstarcam2019",
"VSTN":
"EEGDFHBBKBIFGAIAFGHDFLFJGJNIGEMOHFFPAMDMAAIIKBKNCDBDDMOGHLKCJCKFBFMPLMCBPEMG:vstarcam2019",
"VSTM":
"EBGEEOBOKHJNHGJGEAGAEPEPHDMGHINBGIECBBCBBJIKLKLCCDBBCFODHLKLJJKPBOMELECKPNMNAICEJCNNJH:vstarcam2019",
"VSTL":
"EEGDFHBLKGJIGEJIEIGNFPEEHGNMHPNBGOFIBECEBLJDLMLGDKAPCNPFGOLLJFLJAOMKLBDFOGMAAFCJJPNFJP:vstarcam2019",
"VSTP":
"EEGDFHBLKGJIGEJLEIGJFLENHLNBHCNMGAFGBNCOAIJMLKKODNALCCPKGBLHJLLHAHMBKNDFOGNGBDCIJFMB:vstarcam2019",
"VSGG":
"EBGDEJBJKGJEGIJHELGKFIEEHBMPHBNNGEFCBGCAAGJBLOLFDJAMCPODGFLEJIKNAFMBKNDHPLNEAM:vstarcam2021",
"VSKK":
"EIHGFNBAKMIIGLJHECHIFFECGKNNHONAHAFOBBDOAEJGLLKPDMAMCAPIGGLBIBLCAEMIKEDPOEMMBGCFJHNMJG:veepai2023",
"VSGM":
"AGGFBIALNMLLFLLLDKFHCODEBOPLAHLDELCADKFGABMKOFPBDGELDEPEHAPDIDKNFNNKPPDIIFPGDBBNPMPNPOGMLMNNGHEP:vstarcam2021",
"VSKM":
"EIHGFNBAKMIIGLJEEAHKFEEJHLNBHCNIGFFDBLCMAKJNLELPDDAGCNOOGILMJFLJAOMKLADEOEMJAPCDJCNPJF:veepai2024",
"VSLL":
"EIHGFOBBKKIOGNJKENHHFKEEGMNOHLMNHBFKBBDOAHJBLMKIDLAJCAPIGDLBJGLKAOMILNDJOJMGAHCLJHNMJG:veepai2024",
"VSME":
"EBGFEHBHKNJCHDJDEFGDEIEIHINCHCNLGGFABPCLBDJLLELNCNAHCFOCGELMJCLOAKMNLHDDODMPACCOJLNLIKAA:veepai2024",
"VSIA":
"EBGDEABAKKJCHHJHEBGEENENHBMDHNNEGIEIBLCLANJKLGLNDBAECGOCGELFJJKJABMELGDCOCMIAKCGJENNJH:veepai2024",
"VPTK":
"DAS-8ED76A3380D998ECDA94D6D805A36877AC32B07FFAAD5805AD6F7E3782B7A3FD60DAFC2A75B54DA9A7CBFA8BA808B0B5F4E4EC1668C909DFAF0C0C8FB60C92FE07AE2199BA4A23958A5E5525D1988DB0DFB1A80BE7032B800B0B99775473141C",
"VPGG":
"DAS-8ED76A3380D998ECDA94D6D805A36877C0D359112E88293A287AE8138179A4B94F18ED8372AEEC81CCA047696A14299E4819066B372A34EDB9C71A6CA526B12F3B06CB161C66DDB39491417DFF6CBD38",
"VSGS":
"EBGDEJBJKGJEGIJHELGKFIEEHBMPHBNNGEFCBGCAAGJBLOLFDJAMCPODGFLEJIKNAFMBKNDHPLNEAM:vstarcam2021",
};
Future<String?> _requestServiceParam() async {
String clientId = await getClientId();
String head = clientId.substring(0, 4) ?? "";
if (_serviceMap.containsKey(head)) {
return _serviceMap[head];
}
try {
Response response = await Dio(BaseOptions(
connectTimeout: Duration(milliseconds: 5000),
sendTimeout: Duration(milliseconds: 5000),
receiveTimeout: Duration(milliseconds: 5000)))
.post("https://authentication.eye4.cn/getInitstring",
data: {
"uid": [head]
},
options: Options(
headers: {"Content-Type": "application/json; charset=utf-8"}))
.catchError((error) {});
if (response.statusCode == 200) {
return response.data.length > 0 ? response.data[0] : null;
}
return null;
} catch (_) {
return null;
}
}
Map getMap(String data) {
String command = data;
command = command.replaceAll("\r", "").replaceAll("\n", "");
List<String> lines = command.split(";");
Map maps = Map();
lines.forEach((line) {
if (line.contains("=")) {
List<String> keyValue = line.split("=");
if (keyValue.length >= 2) {
maps.putIfAbsent(keyValue[0].trim().replaceAll("var ", ""),
() => keyValue[1].trim().replaceAll('"', ""));
}
}
});
return maps;
}
Future<bool> checkDeviceAp(String id, {int count = 0}) async {
var bl = false;
try {
Response response = await Dio(BaseOptions(
sendTimeout: Duration(milliseconds: 5000),
connectTimeout: Duration(milliseconds: 1000),
receiveTimeout: Duration(milliseconds: 5000)))
.get(
"http://192.168.168.1:81/get_status.cgi?loginuse=admin&loginpas=888888",
);
if (response?.statusCode == 200) {
Map result = getMap(response.data);
String realDeviceid = result["realdeviceid"];
bl = (realDeviceid == id);
} else
bl = false;
} on DioError {
if (count < 5)
bl = await checkDeviceAp(id, count: count + 1);
else
bl = false;
}
print('id:$id ap:$bl count:$count end');
return bl;
}
dynamic _socketError(
Completer<bool> completer, error, String type, String host, int port) {
if (!completer.isCompleted) completer.complete(false);
return null;
}
dynamic _socketTcpConnect(Completer<bool> completer, Socket socket) async {
if (socket == null) {
if (!completer.isCompleted) completer.complete(false);
return null;
}
socket.write('{"id":"$id","uid":"$_clientId","clientPtr":"$_clientPtr"}');
var data = await socket.first.timeout(Duration(seconds: 5));
if (data != null) {
if (!completer.isCompleted) completer.complete(true);
} else {
if (!completer.isCompleted) completer.complete(false);
}
socket.close();
socket.destroy();
return null;
}
Future<String> _requestClientId() async {
Response response = await Dio(BaseOptions(
connectTimeout: Duration(milliseconds: 30000),
sendTimeout: Duration(milliseconds: 30000),
receiveTimeout: Duration(milliseconds: 30000)))
.get("https://vuid.eye4.cn",
queryParameters: {"vuid": id},
options: Options(
headers: {"Content-Type": "application/json; charset=utf-8"}))
.catchError((error) {
String code = _errorResponse(error);
if (code == '550') {}
return Future.value(null);
});
if (response?.statusCode == 200) {
supplier = response.data["supplier"];
cluster = response.data["cluster"];
_localClientId = false;
return response.data["uid"];
}
return Future.value(null);
}
///转换错误信息.
///正常情况下返回服务器响应的错误信息
///如果是调用[CancelToken]取消请求,那么[Response]中[statusCode]为0
///如果是其他连接错误 [Response]中[statusCode]为 -1
String _errorResponse(DioError error, {bool checkAuthError = true}) {
if (error.response != null) {
if (error.response?.statusCode == 550) {
var result = error?.response?.data.toString();
if (result?.contains('vuid inactivated') ?? false) {
return '550';
}
}
return '-1';
} else {
return '-1';
}
}
Future<void> deviceDestroy() async {
cleanListener();
int clientPtr = await getClientPtr();
await AppP2PApi().clientDestroy(clientPtr);
Directory dir = await getDeviceDirectory();
dir.deleteSync(recursive: true);
await _controller.close();
}
}
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:vsdk/vp_log_code.dart';
enum VPLogLevel {
INFO,
DEBUG,
WARN,
ERROR,
FILE,
MAX,
}
class VPLogStackTraceInfo {
final StackTrace trace;
String fileName = "Unknown";
String lineNumber = "0";
String columnNumber = "0";
String funName = "Unknown";
int stackTraceNum = 0;
VPLogStackTraceInfo(this.trace, int traceNum) {
stackTraceNum = traceNum;
}
void parseTrace() {
if (trace == null) return;
var stackString = trace.toString();
var strList = stackString.split("\n");
if (stackTraceNum >= strList.length) return;
var traceString = strList[stackTraceNum];
var funList = traceString.split("(");
funName = funList.first;
funName = funName.replaceAll("#$stackTraceNum", "").trim();
funList = funName.split(".");
funName = funList.last;
var fileInfo = traceString.split("/").last;
var listOfInfo = fileInfo.split(":");
if (listOfInfo.length < 1) return;
fileName = "${listOfInfo[0]}";
if (listOfInfo.length < 2) return;
lineNumber = listOfInfo[1];
if (listOfInfo.length < 3) return;
columnNumber = listOfInfo[2].replaceFirst(")", "");
}
}
typedef LogFileFullListener = Future<void> Function(File file);
class VPLog {
static const EventChannel _logEvent = const EventChannel('vp_log/event');
static const MethodChannel _logMethod = const MethodChannel('vp_log/method');
static VPLogLevel _logLevel = VPLogLevel.INFO;
static int _maxFileSize = 15;
static int _logFileSize = 0;
static File? _logFile;
static IOSink? _logFileIO;
static LogFileFullListener? _logListener;
static String _levelString(VPLogLevel level) {
switch (level) {
case VPLogLevel.FILE:
return " FILE";
case VPLogLevel.ERROR:
return "ERROR";
case VPLogLevel.WARN:
return " WARN";
case VPLogLevel.DEBUG:
return "DEBUG";
case VPLogLevel.INFO:
return " INFO";
case VPLogLevel.MAX:
break;
}
return " NONE";
}
static void _writeFile(String log) async {
if (_logLevel == VPLogLevel.MAX) return;
if (_logFile == null || _logFileIO == null) return;
try {
List<int> listLogs = utf8.encode(log);
var encodeStr = logEncode(Uint8List.fromList(listLogs));
_logFileIO?.writeln(encodeStr);
_logFileSize += encodeStr.length;
if (_logFileSize > _maxFileSize) {
await _logFileIO?.flush();
await _logFileIO?.close();
if (_logListener != null) {
await _logListener!(_logFile!);
}
_logFile?.deleteSync();
_logFile?.createSync();
_logFileIO = _logFile?.openWrite(mode: FileMode.write);
_logFileSize = 0;
}
} catch (ex) {}
}
static String _fourDigits(int n) {
int absN = n.abs();
if (absN >= 1000) return "$n";
if (absN >= 100) return "0$absN";
if (absN >= 10) return "00$absN";
return "000$absN";
}
static String _sixDigits(int n) {
int absN = n.abs();
if (absN >= 100000) return "$n";
if (absN >= 10000) return "0$absN";
if (absN >= 1000) return "00$absN";
if (absN >= 100) return "000$absN";
if (absN >= 10) return "000$absN";
return "00000$absN";
}
static String _twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
static String _dateString(DateTime date) {
String y = _fourDigits(date.year);
String m = _twoDigits(date.month);
String d = _twoDigits(date.day);
String h = _twoDigits(date.hour);
String min = _twoDigits(date.minute);
String sec = _twoDigits(date.second);
String ms = _sixDigits(date.millisecond * 1000 + date.microsecond);
return "$y-$m-$d $h:$min:$sec.$ms";
}
static void _log(Object msg,
{String tag = "APP",
bool writeFile = false,
VPLogLevel level = VPLogLevel.INFO,
VPLogStackTraceInfo? stack}) {
if (_logLevel == VPLogLevel.MAX) return;
if (msg == null) msg = "null";
if (level.index < _logLevel.index && writeFile == false) return;
if (stack == null) stack = VPLogStackTraceInfo(StackTrace.current, 0);
stack.parseTrace();
var date = DateTime.now();
String data =
'${_dateString(date)} [${_levelString(level)}] [${stack?.fileName}:${stack?.lineNumber}] ${stack?.funName} :[$tag] $msg';
if (level.index >= _logLevel.index) print(data);
if (writeFile) _writeFile(data);
}
static void _nativeLog(dynamic data) {
_writeFile(data);
}
static void printLog(String msg) {
_log(msg,
level: VPLogLevel.INFO,
stack: VPLogStackTraceInfo(StackTrace.current, 5));
}
static void printError(Object error, StackTrace stackTrace) {
List<String> str = [];
if (error != null) str.addAll(error.toString().split("\n"));
if (stackTrace != null) str.addAll(stackTrace.toString().split("\n"));
for (var o in str) {
_log(o,
tag: "ERR",
writeFile: true,
level: VPLogLevel.ERROR,
stack: VPLogStackTraceInfo(StackTrace.current, 1));
}
}
/// 初始化日志记录器
static init(
{VPLogLevel logLevel = VPLogLevel.INFO,
int logFileSize = 30,
String? logFilePath}) {
_maxFileSize = logFileSize * 1024 * 1024;
_logLevel = logLevel;
if (_logFileIO == null) {
try {
if (logFilePath != null)
_logFile = File(logFilePath);
else
_logFile = File("${Directory.systemTemp.path}/logs.log");
if (!_logFile!.existsSync()) {
_logFile?.createSync();
}
_logFileSize = _logFile!.lengthSync();
_logFileIO = _logFile?.openWrite(mode: FileMode.append);
} catch (ex) {}
}
_logEvent.receiveBroadcastStream().listen(_nativeLog);
_logMethod.invokeMethod("vp_log_set_level", [_logLevel.index]);
}
static void warn(Object object,
{String tag = "APP", bool writeFile = false}) {
_log(object,
level: VPLogLevel.WARN,
writeFile: writeFile,
tag: tag,
stack: VPLogStackTraceInfo(StackTrace.current, 1));
}
static void error(Object object,
{String tag = "APP", bool writeFile = false}) {
_log(object,
level: VPLogLevel.ERROR,
writeFile: writeFile,
tag: tag,
stack: VPLogStackTraceInfo(StackTrace.current, 1));
}
static void file(Object object, {String tag = "APP"}) {
_log(object,
level: VPLogLevel.FILE,
writeFile: true,
tag: tag,
stack: VPLogStackTraceInfo(StackTrace.current, 1));
}
static File? getLogFile() {
return _logFile;
}
static void setLogFileFullListener(LogFileFullListener listener) {
_logListener = listener;
}
}
import 'dart:typed_data';
const String _log_chars =
"ZWXYMPTERQUONCHBJILGFSDVKAhiopxyzamtbeqnwrsjklucdvfg089/174+5236";
bool _isBase64(String char) {
return _log_chars.contains(char);
}
String logEncode(Uint8List input) {
if (input == null || input.isEmpty) {
return "";
}
int length = input.length;
int i = 0, j = 0, s = 0;
var charArray3 = []..length = 3;
var charArray4 = []..length = 4;
var b64str = StringBuffer();
while (length-- > 0) {
charArray3[i] = input[s];
s += 1;
i += 1;
if (i == 3) {
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
charArray4[1] =
((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
charArray4[2] =
((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;
for (i = 0; i < 4; i++)
b64str.writeCharCode(_log_chars.codeUnitAt(charArray4[i]));
i = 0;
}
}
if (i != 0) {
for (j = i; j < 3; j++) charArray3[j] = 0;
charArray4[0] = (charArray3[0] & 0xfc) >> 2;
charArray4[1] =
((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
charArray4[2] =
((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;
for (j = 0; j < i + 1; j++)
b64str.writeCharCode(_log_chars.codeUnitAt(charArray4[j]));
while (i++ < 3) b64str.writeCharCode('='.codeUnitAt(0));
}
return b64str.toString();
}
List<int>? logDecode(String input) {
if (input == null || input.isEmpty) {
return null;
}
int length = input.length;
int i = 0;
int j = 0;
int idx = 0;
var charArray4 = []..length = 4;
var charArray3 = []..length = 3;
var output = <int>[];
while (length-- > 0 && input[idx] != '=') {
if (!_isBase64(input[idx])) {
idx++;
continue;
}
charArray4[i++] = input[idx++];
if (i == 4) {
for (i = 0; i < 4; i++) {
charArray4[i] = _log_chars.indexOf(charArray4[i]);
}
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
charArray3[1] =
((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
for (i = 0; (i < 3); i++) output.add(charArray3[i]);
i = 0;
}
}
if (i != 0) {
for (j = i; j < 4; j++) charArray4[j] = '\0';
for (j = 0; j < 4; j++) {
charArray4[j] = _log_chars.indexOf(charArray4[j]);
}
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
charArray3[1] =
((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
for (j = 0; (j < i - 1); j++) output.add(charArray3[j]);
}
return output;
}
name: vsdk
description: A new Flutter project.
version: 1.0.1
author:
homepage:
environment:
sdk: ">=2.17.0 <4.0.0"
flutter: ">=1.20.0"
dependencies:
flutter:
sdk: flutter
fast_gbk: ^1.0.0
equatable: ^2.0.0
dio: ^5.4.0
crypto: ^3.0.0
get: ^4.6.0
flutter_easyloading: ^3.0.5
path_provider: ^2.0.0
flutter_image_compress: ^2.4.0
shared_preferences: ^2.0.0
# 日志
logger: ^2.5.0
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: com.example.vsdk
pluginClass: VsdkPlugin
ios:
pluginClass: VsdkPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
...@@ -54,6 +54,8 @@ dependencies: ...@@ -54,6 +54,8 @@ dependencies:
path_provider: ^2.1.2 path_provider: ^2.1.2
flutter_webrtc: flutter_webrtc:
path: ./plugins/flutter_webrtc path: ./plugins/flutter_webrtc
vsdk:
path: ./plugins/vsdk
# flutter_usbcamera: ^0.0.1 # flutter_usbcamera: ^0.0.1
# fl_chart: ^0.71.0 # fl_chart: ^0.71.0
# fl_chart: ^1.1.0 # fl_chart: ^1.1.0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment