andrew_live_sdk 0.0.1
andrew_live_sdk: ^0.0.1 copied to clipboard
Live streaming SDK for Flutter — WHIP/WHEP WebRTC, beauty, effects, virtual background, linkmic, PK, danmaku. Layer 1 core with SPI architecture.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:andrew_live_sdk/andrew_live_sdk.dart';
/// live_sdk 最小使用示例:初始化 → 推流 → 拉流 → 美颜。
///
/// 运行前请替换下方 url 为真实推拉流地址。
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Live SDK Demo',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final _client = LiveClient();
final _logs = <String>[];
bool _publishing = false;
int? _playTextureId;
void _log(String s) {
setState(() {
_logs.insert(0, '${DateTime.now().toIso8601String().substring(11, 19)} $s');
});
}
Future<void> _init() async {
_log('init engine...');
await _client.init(const LiveSdkConfig(
enableGpu: true,
danmakuDisplayArea: 0.5,
danmakuMaxConcurrent: 80,
));
_client.addListener(_DemoListener(_log));
_log('init done');
}
Future<void> _togglePublish() async {
if (!_client.isInitialized) await _init();
if (_publishing) {
_log('stop publish...');
await _client.streamManager.stopPush();
setState(() => _publishing = false);
_log('stopped');
} else {
_log('start publish...');
await _client.streamManager.startPush(
const PushStreamConfig(
url: 'https://your-srs.example.com/rtc/v1/whip/?app=live&stream=demo',
width: 1080,
height: 1920,
fps: 30,
videoBitrate: 2000000,
),
);
setState(() => _publishing = true);
_log('publishing');
}
}
Future<void> _togglePlay() async {
if (!_client.isInitialized) await _init();
if (_playTextureId != null) {
await _client.streamManager.stopPull();
setState(() => _playTextureId = null);
_log('play stopped');
} else {
_log('start play...');
final textureId = await _client.streamManager.startPull(
const PullStreamConfig(
url: 'https://your-srs.example.com/rtc/v1/whep/?app=live&stream=demo',
),
);
setState(() => _playTextureId = textureId);
_log('playing, textureId=$textureId');
}
}
@override
void dispose() {
_client.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Live SDK Demo')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Wrap(
spacing: 8,
children: [
ElevatedButton(
onPressed: _togglePublish,
child: Text(_publishing ? 'Stop Publish' : 'Publish'),
),
ElevatedButton(
onPressed: _togglePlay,
child: Text(_playTextureId != null ? 'Stop Play' : 'Play'),
),
],
),
),
if (_playTextureId != null)
SizedBox(
height: 240,
child: Texture(textureId: _playTextureId!),
),
const Divider(),
Expanded(
child: ListView.builder(
itemCount: _logs.length,
itemBuilder: (_, i) => ListTile(
dense: true,
title: Text(_logs[i], style: const TextStyle(fontSize: 12)),
),
),
),
],
),
);
}
}
class _DemoListener extends LiveEventListener {
_DemoListener(this.log);
final void Function(String) log;
@override
void onEvent(LiveEvent event) {
log('event: ${event.type}');
}
}