Flutter点对点通信插件flutter_p2p_engine的使用
Flutter点对点通信插件flutter_p2p_engine的使用
特性
- 支持iOS、macOS和Android平台。
- 支持通过HLS协议和MPEG-Dash(仅限Android)进行实时和VOD流传输。
- 支持加密的HLS流。
- 支持缓存以避免重复下载TS文件。
- 非常容易集成到现有的Flutter项目中。
- 支持任何Flutter播放器。
- 高度可配置。
- 使用IP数据库按ISP和地区分组对等节点。
使用
请参阅文档了解详细信息。
请求令牌
请参阅此处获取更多信息。
仪表盘
在dashboard注册您的AppId,您可以在那里查看与P2P相关的所有信息。
问题与功能请求
- 如果您发现了一个错误,请打开一个问题。
- 如果您有一个功能请求,请打开一个问题。
相关项目
- hlsjs-p2p-engine - Web视频分发技术,无需插件。
- ios-p2p-engine - 适用于任何播放器的iOS视频P2P引擎。
- android-p2p-engine - 适用于任何播放器的Android视频P2P引擎。
- react-native-swarmcloud - SwarmCloud P2P SDK的官方React Native绑定。
常见问题
我们收集了一些常见问题。在报告问题之前,请搜索FAQ是否有您问题的答案。
联系我们
示例代码
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:safemap/safemap.dart';
import 'package:flutter_p2p_engine/flutter_p2p_engine.dart';
import 'package:flutter_p2p_engine_example/style/color.dart';
import 'package:flutter_p2p_engine_example/views/confirm.dart';
import 'package:tapped/tapped.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
const token = 'ZMuO5qHZg'; // 替换为您的令牌
void main() {
runApp(const VideoApp());
SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
class VideoApp extends StatelessWidget {
const VideoApp({Key? key}) : super(key: key);
[@override](/user/override)
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Video Demo',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
[@override](/user/override)
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var playerReady = false;
VideoPlayerController? _controller;
[@override](/user/override)
void initState() {
super.initState();
init();
}
var url = 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/level_0.m3u8';
var totalHTTPDn = 0;
var totalP2PDn = 0;
var totalP2PUp = 0;
var connected = false;
var sdkVersion = '';
init() async {
try {
// Do not init FlutterP2pEngine more than once! 请不要多次init FlutterP2pEngine!
await FlutterP2pEngine.init(
token,
config: P2pConfig(
trackerZone: TrackerZone.Europe,
logEnabled: true,
logLevel: P2pLogLevel.debug,
),
infoListener: (info) {
if (SafeMap(info)["serverConnected"].hasValue) {
setState(() {
connected = SafeMap(info)["serverConnected"].boolean;
});
} else {
setState(() {
totalHTTPDn += SafeMap(info)["httpDownloaded"].intOrZero;
totalP2PDn += SafeMap(info)["p2pDownloaded"].intOrZero;
totalP2PUp += SafeMap(info)["p2pUploaded"].intOrZero;
});
}
},
);
setState(() {});
setUpVideo(url);
sdkVersion = await FlutterP2pEngine.getSDKVersion();
} catch (e) {
// print('Init Error $e');
}
}
bool showDetail = false;
setUpVideo(String url) async {
_controller?.dispose();
var res = await FlutterP2pEngine.parseStreamURL(url);
print('urlResult $res');
_controller = VideoPlayerController.networkUrl(Uri.parse(res ?? url))..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {
playerReady = true;
});
_controller?.play();
});
}
[@override](/user/override)
void dispose() {
_controller?.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorPlate.lightGray,
body: Center(
child: Stack(
alignment: Alignment.bottomLeft,
children: [
Center(
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width * 9.0 / 16.0,
child: playerReady
? AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
VideoPlayer(_controller!),
VideoProgressIndicator(_controller!, allowScrubbing: true),
GestureDetector(
onTap: () {
_controller!.value.isPlaying ? _controller!.pause() : _controller!.play();
},
),
],
),
)
: Container(),
),
),
Positioned(
left: 12,
bottom: 20,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.8),
borderRadius: BorderRadius.circular(6),
),
constraints: const BoxConstraints(
maxWidth: 300,
),
child: DefaultTextStyle(
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.normal,
fontSize: 10,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (showDetail)
Flexible(
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(
vertical: 2,
),
child: Text('Token:$token'),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 2,
),
child: Text('Url:$url'),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 2,
),
child: Text(
[
'SDK Version: $sdkVersion',
'Connected: $connected',
'TotalHTTPDownloaded: $totalHTTPDn',
'TotalP2PDownloaded: $totalP2PDn',
'TotalP2PUploaded: $totalP2PUp',
].join('\n'),
),
),
],
),
),
),
Column(
children: [
Tapped(
onTap: () {
setState(() {
showDetail = !showDetail;
});
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 12,
),
child: Icon(
showDetail ? Icons.clear : Icons.info_outline,
color: Colors.white,
size: 20,
),
),
),
if (showDetail)
Tapped(
onTap: () async {
var res = await editUrl(
context,
url: url,
);
var _url = res?.asMap()[0];
if (_url != null) {
setState(() {});
setUpVideo(_url);
}
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 12,
),
child: const Icon(
Icons.edit,
color: Colors.white,
size: 20,
),
),
),
],
),
],
),
),
),
)
],
),
),
);
}
}
/// 输入文本,可以通过onWillConfirm方法检查
Future<List<String>?> editUrl(
BuildContext context, {
ConfirmType? type,
String? url,
}) async {
InputHelper urlInput = InputHelper(defaultText: url);
var res = await confirm(
context,
type: type,
title: 'Edit',
ok: 'Save',
cancel: 'Cancel',
onWillConfirm: () async => true,
contentBuilder: (ctx) => Column(
children: [
Container(
margin: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: ColorPlate.lightGray,
borderRadius: BorderRadius.circular(6),
),
child: StTextField(
autofocus: true,
margin: EdgeInsets.zero,
helper: urlInput,
hintText: 'Input Url',
),
),
],
),
);
if (res == true) {
return [
urlInput.text,
];
}
return null;
}
更多关于Flutter点对点通信插件flutter_p2p_engine的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
1 回复
更多关于Flutter点对点通信插件flutter_p2p_engine的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter应用中使用flutter_p2p_engine
插件进行点对点通信的代码示例。这个插件通常用于实现P2P(Peer-to-Peer)视频通话、文件传输等功能。需要注意的是,这个示例假设你已经在pubspec.yaml
文件中添加了flutter_p2p_engine
依赖,并且已经运行了flutter pub get
。
1. 添加依赖
首先,在pubspec.yaml
文件中添加依赖:
dependencies:
flutter:
sdk: flutter
flutter_p2p_engine: ^最新版本号 # 请替换为实际最新版本号
2. 初始化Flutter项目
确保你的Flutter项目已经正确设置,并且有一个基本的页面结构。
3. 使用flutter_p2p_engine进行P2P通信
下面是一个简单的示例,展示如何使用flutter_p2p_engine
进行基本的P2P连接和数据传输。
import 'package:flutter/material.dart';
import 'package:flutter_p2p_engine/flutter_p2p_engine.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter P2P Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: P2PDemoPage(),
);
}
}
class P2PDemoPage extends StatefulWidget {
@override
_P2PDemoPageState createState() => _P2PDemoPageState();
}
class _P2PDemoPageState extends State<P2PDemoPage> {
final P2PEngine _p2pEngine = P2PEngine();
String _localId = '';
String _remoteId = '';
String _connectionStatus = 'Not Connected';
String _receivedData = '';
@override
void initState() {
super.initState();
_initP2PEngine();
}
Future<void> _initP2PEngine() async {
// 初始化P2P引擎,获取本地ID
_localId = await _p2pEngine.init();
setState(() {});
}
void _connectToPeer(String remoteId) async {
// 连接到远程对等点
try {
await _p2pEngine.connect(remoteId);
setState(() {
_connectionStatus = 'Connected to $remoteId';
});
// 监听接收到的数据
_p2pEngine.onDataReceived.listen((data) {
setState(() {
_receivedData += String.fromCharCodes(data);
});
});
} catch (e) {
setState(() {
_connectionStatus = 'Failed to connect: $e';
});
}
}
void _sendDataToPeer(String data) async {
// 发送数据到远程对等点
try {
await _p2pEngine.sendData(data.codeUnits);
} catch (e) {
print('Failed to send data: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter P2P Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Local ID: $_localId'),
SizedBox(height: 16),
TextField(
decoration: InputDecoration(labelText: 'Remote ID'),
controller: TextEditingController(text: _remoteId),
onChanged: (value) {
_remoteId = value;
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
_connectToPeer(_remoteId);
},
child: Text('Connect to Peer'),
),
SizedBox(height: 16),
TextField(
decoration: InputDecoration(labelText: 'Send Data'),
onChanged: (value) {
// 可以在这里添加发送按钮的触发逻辑,为了简单起见,这里不添加按钮
// 你可以通过调用_sendDataToPeer(value)来发送数据
},
),
SizedBox(height: 16),
Text('Connection Status: $_connectionStatus'),
SizedBox(height: 16),
Text('Received Data:\n$_receivedData'),
],
),
),
);
}
}
注意事项
- 权限:在实际应用中,P2P通信通常需要网络权限,请确保在
AndroidManifest.xml
和Info.plist
中添加了必要的权限声明。 - ICE服务器:为了成功建立P2P连接,通常需要一个ICE(Interactive Connectivity Establishment)服务器来协助NAT(Network Address Translation)穿透。这个示例没有包含ICE服务器的配置,但在实际应用中,你可能需要配置ICE服务器。
- 错误处理:示例中的错误处理非常基础,实际应用中应该添加更完善的错误处理和重试逻辑。
- 安全性:P2P通信中的数据传输是直接的,因此需要注意数据的安全性,可能需要加密传输的数据。
这个示例代码提供了一个基础框架,你可以根据实际需求进行扩展和修改。