Merge branch 'feature/client-api-integration'
This commit is contained in:
30
webrtc_controller_flutter/lib/app/app.dart
Normal file
30
webrtc_controller_flutter/lib/app/app.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:webrtc_controller_flutter/l10n/app_localizations.dart';
|
||||
|
||||
import 'router/app_router.dart';
|
||||
import 'theme/app_theme.dart';
|
||||
|
||||
/// 应用根组件:CupertinoApp + GoRouter。
|
||||
class WebrtcControllerApp extends StatelessWidget {
|
||||
const WebrtcControllerApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoApp.router(
|
||||
title: 'WebRTC 控制端',
|
||||
theme: AppTheme.light,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [
|
||||
Locale('zh'),
|
||||
Locale('en'),
|
||||
],
|
||||
routerConfig: appRouter,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 全局常量与 API 端点。
|
||||
|
||||
/// 服务端 HTTP 基址(与信令同源)。部署时通过 --dart-define=API_BASE= 注入。
|
||||
const String kApiBase = String.fromEnvironment(
|
||||
'API_BASE',
|
||||
defaultValue: 'http://192.168.5.224:8080',
|
||||
);
|
||||
|
||||
/// 信令 WebSocket 默认地址。
|
||||
const String kDefaultSignalServer = 'ws://192.168.5.224:8080/ws/signal';
|
||||
|
||||
/// DataChannel 标签,控制端与被控端需一致。
|
||||
const String kDataChannelLabel = 'control_channel';
|
||||
|
||||
/// 分辨率预设:width 为长边像素;0 表示被控端原生分辨率。
|
||||
const List<Map<String, Object>> kResolutionOptions = [
|
||||
{'label': '原始', 'width': 0, 'height': 0, 'fps': 0},
|
||||
{'label': '1080P', 'width': 1920, 'height': 0, 'fps': 0},
|
||||
{'label': '720P', 'width': 1280, 'height': 0, 'fps': 0},
|
||||
{'label': '480P', 'width': 854, 'height': 0, 'fps': 0},
|
||||
];
|
||||
|
||||
/// 默认帧率档位(收到被控端上报的 supported_fps 后以上报列表为准)。
|
||||
const List<int> kDefaultFpsOptions = [15, 24, 30, 60];
|
||||
68
webrtc_controller_flutter/lib/app/constants/ice_servers.dart
Normal file
68
webrtc_controller_flutter/lib/app/constants/ice_servers.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
/// ICE 服务器配置。
|
||||
///
|
||||
/// 与可正常连接的 `webrtc_controller_ios`(原生 Swift 项目)保持一致:
|
||||
/// 使用 `www.ttstd.com:3478` 作为 STUN/TURN 服务器。
|
||||
///
|
||||
/// 注意:旧配置曾误用 `175.178.213.60:3478`(错误凭据)及内网
|
||||
/// `192.168.5.224:3478`(模拟器不可达),导致 iOS 模拟器下
|
||||
/// `ICE Checking -> Failed`。已修正为与 iOS 原生项目相同的服务器。
|
||||
///
|
||||
/// **iOS 模拟器关键修复**:
|
||||
/// 明文 `turn:175.178.213.60:3478` 在 iOS 模拟器(libwebrtc)下不会发起
|
||||
/// RELAY 分配请求,导致控制端本地候选缺失 `typ relay`,两端无公共中继可
|
||||
/// 配对 → candidate-pair=0 → ICE Failed(Android 真机/网页则正常)。
|
||||
/// 改用 `turns:`(TLS over TCP 5349)后,模拟器能稳定拿到 relay 候选。
|
||||
/// 同时保留 udp / tcp 明文项作为降级,以及 STUN 用于 srflx。
|
||||
///
|
||||
/// **Linux / Windows 桌面端崩溃修复(free(): invalid pointer)**:
|
||||
/// flutter_webrtc 桌面端的原生 `RTCConfiguration` 使用**定长数组**存放
|
||||
/// ICE 服务器:`IceServer ice_servers[kMaxIceServerSize]`,其中
|
||||
/// `kMaxIceServerSize = 8`(见 third_party/libwebrtc/include/rtc_types.h)。
|
||||
/// 而 `FlutterWebRTCBase::CreateIceServers()` 直接按传入列表长度写入该数组,
|
||||
/// **没有做任何越界检查**。一旦条目数 > 8,就会越界写入相邻堆内存,
|
||||
/// 并向非法地址赋值 `std::string`,最终在释放时触发
|
||||
/// `free(): invalid pointer` 并导致进程闪退。
|
||||
///
|
||||
/// 因此下表条目数**必须 ≤ [kMaxIceServerCount](8)**。
|
||||
/// 这里按「同一组凭据合并为一条」的标准写法(`urls` 传数组),
|
||||
/// 在不丢失任何服务器地址的前提下把条目压缩到 6 条。
|
||||
///
|
||||
/// **桌面端 `urls` 数组的额外注意点**:
|
||||
/// 同一函数里解析 `urls` 列表时是 `ice_server.uri = ...` 逐个**覆盖**赋值,
|
||||
/// 只有**最后一个** URL 会真正生效(Android / iOS / Web 则会全部生效)。
|
||||
/// 因此每个数组都把「最通用、最可能连通」的地址放在**最后一位**:
|
||||
/// TURN 组以明文 `turn:...:3478`(UDP)收尾,内网组以 `turn:` 收尾。
|
||||
const int kMaxIceServerCount = 8;
|
||||
|
||||
const List<Map<String, dynamic>> kIceServers = [
|
||||
// 公共 STUN。
|
||||
{'urls': 'stun:stun.l.google.com:19302'},
|
||||
{'urls': 'stun:175.178.213.60:3478'},
|
||||
{
|
||||
// 同一组凭据的多个 TURN 地址合并为一条。
|
||||
// 顺序说明:移动端/Web 全部生效;桌面端只取最后一个,
|
||||
// 故把兼容性最好的明文 UDP turn 放在末尾。
|
||||
'urls': [
|
||||
'turns:175.178.213.60:5349',
|
||||
'turn:175.178.213.60:3478?transport=tcp',
|
||||
'turn:175.178.213.60:3478',
|
||||
],
|
||||
'username': 'fanhuitong',
|
||||
'credential': 'Fan19961907..',
|
||||
},
|
||||
{'urls': 'stun:47.242.112.133:3478'},
|
||||
{
|
||||
'urls': 'turn:47.242.112.133:3478',
|
||||
'username': 'ttstd',
|
||||
'credential': 'fanhuitong',
|
||||
},
|
||||
{
|
||||
// 内网 STUN/TURN(同机房/局域网时可直连)。
|
||||
'urls': [
|
||||
'stun:192.168.5.224:3478',
|
||||
'turn:192.168.5.224:3478',
|
||||
],
|
||||
'username': 'tt',
|
||||
'credential': 'fht',
|
||||
},
|
||||
];
|
||||
40
webrtc_controller_flutter/lib/app/router/app_router.dart
Normal file
40
webrtc_controller_flutter/lib/app/router/app_router.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../features/auth/presentation/pages/login_page.dart';
|
||||
import '../../features/auth/presentation/pages/splash_page.dart';
|
||||
import '../../features/connection/presentation/pages/control_page.dart';
|
||||
import '../../features/connection/presentation/pages/setup_page.dart';
|
||||
|
||||
/// 应用路由。
|
||||
///
|
||||
/// - `/splash`:启动页,恢复并校验令牌后重定向(初始路由)
|
||||
/// - `/login`:登录页,支持通过 `message` 查询参数展示失效提示
|
||||
/// - `/`:连接设置页
|
||||
/// - `/control`:控制面板页
|
||||
final appRouter = GoRouter(
|
||||
initialLocation: '/splash',
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/splash',
|
||||
name: 'splash',
|
||||
builder: (context, state) => const SplashPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
builder: (context, state) => LoginPage(
|
||||
message: state.uri.queryParameters['message'],
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/',
|
||||
name: 'setup',
|
||||
builder: (context, state) => const SetupPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/control',
|
||||
name: 'control',
|
||||
builder: (context, state) => const ControlPage(),
|
||||
),
|
||||
],
|
||||
);
|
||||
11
webrtc_controller_flutter/lib/app/theme/app_theme.dart
Normal file
11
webrtc_controller_flutter/lib/app/theme/app_theme.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
/// 全局 Cupertino 主题配置。
|
||||
class AppTheme {
|
||||
const AppTheme._();
|
||||
|
||||
/// 应用统一主题(iOS 风格)。
|
||||
static const CupertinoThemeData light = CupertinoThemeData(
|
||||
primaryColor: CupertinoColors.activeBlue,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user