# 无人机实时视频接口对接说明 ## 概述 本模块实现了无人机实时视频流的获取功能,采用清洁架构设计,支持多种镜头类型切换(广角、变焦、红外),并兼容火山引擎和声网两种RTC SDK。 ## API 接口 ### 接口地址 ``` POST http://1.95.137.212:59015/iot/UAV/changeLens ``` ### 请求参数 ```json { "sn": "1581F8HGX253U00A063U", // 无人机设备序列号(必填) "lensType": "", // 镜头类型:wide(广角)、zoom(变焦)、ir(红外),可为空 "cameraIndex": "176-0-0", // 摄像头编号(必填) "qualityType": "adaptive", // 清晰度:adaptive(自适应)、low、medium、high(默认adaptive) "videoExpire": 720000000 // Token有效期(毫秒,默认720000000) } ``` ### 返回数据 ```json { "msg": "操作成功", "code": 200, "data": { "sn": "1581F8HGX253U00A063U", "camera_index": "176-0-0", "url": "app_id=xxx&expire_time=xxx&room_id=xxx&token=xxx&user_id=xxx", "expire_ts": 1781155234, "url_type": "volc" // volc(火山引擎) 或 agora(声网) } } ``` ## 架构设计 ### 目录结构 ``` lib/features/v2/device_list/ ├── domain/ │ ├── entities/ │ │ └── uav_video_stream_entity.dart # 实体类 │ ├── repositories/ │ │ ── drone_station_repository.dart # 仓库接口 │ └── usecases/ │ └── get_uav_video_stream_usecase.dart # 用例 ├── data/ │ ├── datasources/ │ │ ├── drone_station_datasource.dart # 数据源接口 │ │ └── drone_station_datasource_impl.dart # 数据源实现 │ └── repositories/ │ └── drone_station_repository_impl.dart # 仓库实现 ── presentation/ ├── bloc/ │ ├── drone_station_bloc.dart # BLoC状态管理 │ ├── drone_station_event.dart # 事件定义 │ └── drone_station_state.dart # 状态定义 └── pages/ ── uav_live_video_page.dart # 示例页面 ``` ### 核心组件 #### 1. 实体类 (Entity) **文件**: `domain/entities/uav_video_stream_entity.dart` 定义了枚举类型和实体类: - `UavLensType`: 镜头类型枚举(wide、zoom、ir) - `VideoQualityType`: 视频质量枚举(adaptive、low、medium、high) - `UavVideoStreamEntity`: 视频流实体,包含解析RTC参数的方法 #### 2. 数据源 (DataSource) **文件**: `data/datasources/drone_station_datasource_impl.dart` 实现了API调用逻辑: - 发送POST请求到 `/iot/UAV/changeLens` - 处理响应并转换为实体对象 - 包含详细的日志输出用于调试 #### 3. 仓库 (Repository) **文件**: `data/repositories/drone_station_repository_impl.dart` 使用 `fpdart` 的 `Either` 类型处理错误: - 成功时返回 `Right(UavVideoStreamEntity)` - 失败时返回 `Left(Failure)` #### 4. 用例 (UseCase) **文件**: `domain/usecases/get_uav_video_stream_usecase.dart` 封装业务逻辑,供BLoC调用。 #### 5. BLoC 状态管理 **文件**: `presentation/bloc/drone_station_bloc.dart` 新增事件和状态: - **事件**: `UavVideoStreamLoad` - 加载视频流 - **状态**: - `UavVideoStreamLoading` - 加载中 - `UavVideoStreamLoaded` - 加载成功 - `UavVideoStreamError` - 加载失败 #### 6. 依赖注入 **文件**: `core/di/injection.dart` 已注册以下单例: ```dart sl.registerLazySingleton( () => GetUavVideoStreamUseCase(sl()), ); sl.registerFactory( () => DroneStationBloc(sl(), sl(), sl(), sl()), ); ``` ## 使用方式 ### 方式一:直接使用示例页面 ```dart import 'package:maibu_satabot_v2/features/v2/device_list/presentation/pages/uav_live_video_page.dart'; Navigator.push( context, MaterialPageRoute( builder: (context) => UavLiveVideoPage( droneSn: '1581F8HGX253U00A063U', cameraIndex: '176-0-0', ), ), ); ``` ### 方式二:在现有页面中使用 #### 1. 导入必要的类 ```dart import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../../core/di/injection.dart'; import '../../domain/entities/uav_video_stream_entity.dart'; import '../bloc/drone_station_bloc.dart'; import '../bloc/drone_station_event.dart'; import '../bloc/drone_station_state.dart'; ``` #### 2. 初始化 BLoC ```dart late DroneStationBloc _bloc; @override void initState() { super.initState(); _bloc = sl(); // 加载视频流 _loadVideoStream(UavLensType.wide); } @override void dispose() { _bloc.close(); super.dispose(); } ``` #### 3. 加载视频流 ```dart void _loadVideoStream(UavLensType lensType) { setState(() { _isLoading = true; _errorMessage = null; }); _bloc.add( UavVideoStreamLoad( sn: widget.droneSn, cameraIndex: widget.cameraIndex, lensType: lensType, qualityType: VideoQualityType.adaptive, videoExpire: 720000000, ), ); } ``` #### 4. 监听状态变化 ```dart BlocConsumer( listener: (context, state) { if (state is UavVideoStreamLoaded) { final videoStream = state.videoStream; debugPrint('URL Type: ${videoStream.urlType}'); debugPrint('AppId: ${videoStream.appId}'); debugPrint('RoomId: ${videoStream.roomId}'); debugPrint('UserId: ${videoStream.userId}'); // TODO: 根据 urlType 初始化对应的 RTC 引擎 if (videoStream.urlType.toLowerCase() == 'volc') { // 使用火山引擎 RTC SDK } else if (videoStream.urlType.toLowerCase() == 'agora') { // 使用声网 RTC SDK } } else if (state is UavVideoStreamError) { setState(() { _errorMessage = state.message; }); } }, builder: (context, state) { // 根据状态渲染UI if (state is UavVideoStreamLoading) { return const CircularProgressIndicator(); } if (state is UavVideoStreamLoaded) { // 显示视频画面 return Container(); } if (state is UavVideoStreamError) { return Text('错误: ${state.message}'); } return Container(); }, ) ``` #### 5. 切换镜头类型 ```dart // 切换到广角镜头 _loadVideoStream(UavLensType.wide); // 切换到变焦镜头 _loadVideoStream(UavLensType.zoom); // 切换到红外镜头 _loadVideoStream(UavLensType.ir); ``` ## RTC 集成 获取到视频流后,需要根据 `urlType` 选择对应的 RTC SDK: ### 火山引擎 (volc) ```dart final appId = videoStream.appId; final roomId = videoStream.roomId; final token = videoStream.token; final userId = videoStream.userId; // 使用 volc_engine_rtc SDK final engine = await RTCEngine.createRTCEngine( RTCVideoContext(appId: appId, eventHandler: handler), ); final room = await engine.createRTCRoom(roomId); await room.joinRoom(token: token, userId: userId); ``` ### 声网 (agora) ```dart final appId = videoStream.appId; final channelId = videoStream.roomId; final token = videoStream.token; final uid = int.tryParse(videoStream.userId) ?? 0; // 使用 agora_rtc_engine SDK final engine = createAgoraRtcEngine(); await engine.initialize(RtcEngineContext(appId: appId)); await engine.joinChannel( token: token, channelId: channelId, uid: uid, options: ChannelMediaOptions(...), ); ``` ## 注意事项 1. **可扩展性**: 本模块采用清洁架构设计,所有业务逻辑与UI分离,便于在其他页面复用。 2. **错误处理**: 使用 `Either` 模式统一处理错误,确保异常不会直接抛出。 3. **日志记录**: 数据源层包含详细的日志输出,方便调试和问题排查。 4. **默认值**: - `lensType` 可为空,后端会根据实际情况选择默认镜头 - `qualityType` 默认为 `adaptive`(自适应) - `videoExpire` 默认为 `720000000` 毫秒 5. **Token 有效期**: `videoExpire` 参数单位为毫秒,建议设置较长的有效期以避免频繁刷新。 6. **多镜头支持**: 通过 `UavLensType` 枚举可以轻松扩展更多镜头类型。 ## 后续工作 1. [ ] 集成 RTC SDK 显示实际视频画面 2. [ ] 添加视频录制功能 3. [ ] 添加截图功能 4. [ ] 优化视频加载超时处理 5. [ ] 添加视频质量切换功能 6. [ ] 支持多路视频同时观看 ## 相关文件清单 ### 新增文件 - `lib/features/v2/device_list/domain/entities/uav_video_stream_entity.dart` - `lib/features/v2/device_list/domain/usecases/get_uav_video_stream_usecase.dart` - `lib/features/v2/device_list/presentation/pages/uav_live_video_page.dart` ### 修改文件 - `lib/core/consts/http_api_consts.dart` - 添加 API 常量 - `lib/features/v2/device_list/data/datasources/drone_station_datasource.dart` - 添加接口方法 - `lib/features/v2/device_list/data/datasources/drone_station_datasource_impl.dart` - 实现接口 - `lib/features/v2/device_list/data/repositories/drone_station_repository_impl.dart` - 实现仓库 - `lib/features/v2/device_list/domain/repositories/drone_station_repository.dart` - 添加仓库接口 - `lib/features/v2/device_list/presentation/bloc/drone_station_event.dart` - 添加事件 - `lib/features/v2/device_list/presentation/bloc/drone_station_state.dart` - 添加状态 - `lib/features/v2/device_list/presentation/bloc/drone_station_bloc.dart` - 添加事件处理 - `lib/core/di/injection.dart` - 注册依赖