324 lines
9.4 KiB
Markdown
324 lines
9.4 KiB
Markdown
# 无人机实时视频接口对接说明
|
||
|
||
## 概述
|
||
|
||
本模块实现了无人机实时视频流的获取功能,采用清洁架构设计,支持多种镜头类型切换(广角、变焦、红外),并兼容火山引擎和声网两种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>(
|
||
() => GetUavVideoStreamUseCase(sl()),
|
||
);
|
||
sl.registerFactory<DroneStationBloc>(
|
||
() => 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<DroneStationBloc>();
|
||
// 加载视频流
|
||
_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<DroneStationBloc, DroneStationState>(
|
||
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<Failure, T>` 模式统一处理错误,确保异常不会直接抛出。
|
||
|
||
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` - 注册依赖
|