Skip to content

Instantly share code, notes, and snippets.

@bawantha
Created May 30, 2024 15:38
Show Gist options
  • Save bawantha/77380bc8b44f2b882c58f04325168c17 to your computer and use it in GitHub Desktop.
Save bawantha/77380bc8b44f2b882c58f04325168c17 to your computer and use it in GitHub Desktop.
relay
import 'package:agora_badger/remote_video_views_widget.dart';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:agora_uikit/agora_uikit.dart';
import 'package:flutter/material.dart';
class AgoraLiveStream extends StatefulWidget {
final String appId;
final String channelId;
final String token;
final String relayChannelId;
final String relayToken;
final bool isHost;
const AgoraLiveStream({
super.key,
required this.appId,
required this.channelId,
required this.token,
required this.relayChannelId,
required this.relayToken,
this.isHost = true,
});
@override
State<StatefulWidget> createState() => _State();
}
class _State extends State<AgoraLiveStream> with KeepRemoteVideoViewsMixin {
late final RtcEngine _engine;
bool _isReadyPreview = false;
bool isJoined = false;
int? remoteUid;
bool isRelaying = false;
@override
void initState() {
super.initState();
_initEngine();
}
@override
void dispose() {
super.dispose();
_dispose();
}
Future<void> _dispose() async {
await _engine.leaveChannel();
await _engine.release();
}
Future<void> _initEngine() async {
await [Permission.microphone, Permission.camera].request();
_engine = createAgoraRtcEngine();
await _engine.initialize(RtcEngineContext(
appId: widget.appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
_engine.registerEventHandler(RtcEngineEventHandler(
onError: (ErrorCodeType err, String msg) {
print('[onError] err: $err, msg: $msg');
},
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
print(
'[onJoinChannelSuccess] connection: ${connection.toJson()} elapsed: $elapsed');
setState(() {
isJoined = true;
});
},
onUserJoined: (RtcConnection connection, int rUid, int elapsed) {
print(
'[onUserJoined] connection: ${connection.toJson()} remoteUid: $rUid elapsed: $elapsed');
setState(() {
remoteUid = rUid;
});
},
onUserOffline:
(RtcConnection connection, int rUid, UserOfflineReasonType reason) {
print(
'[onUserOffline] connection: ${connection.toJson()} rUid: $rUid reason: $reason');
setState(() {
remoteUid = null;
});
},
onLeaveChannel: (RtcConnection connection, RtcStats stats) {
print(
'[onLeaveChannel] connection: ${connection.toJson()} stats: ${stats.toJson()}');
setState(() {
isJoined = false;
});
},
onChannelMediaRelayStateChanged:
(ChannelMediaRelayState state, ChannelMediaRelayError code) {
print('[onChannelMediaRelayStateChanged] state: $state, code: $code');
switch (state) {
case ChannelMediaRelayState.relayStateIdle:
setState(() {
isRelaying = false;
});
break;
case ChannelMediaRelayState.relayStateRunning:
setState(() {
isRelaying = true;
});
break;
case ChannelMediaRelayState.relayStateFailure:
setState(() {
isRelaying = false;
});
break;
default:
setState(() {
isRelaying = false;
});
break;
}
},
));
await _engine.enableVideo();
_engine.startPreview();
await _engine.setClientRole(
role: widget.isHost
? ClientRoleType.clientRoleBroadcaster
: ClientRoleType.clientRoleAudience);
setState(() {
_isReadyPreview = true;
});
_joinChannel();
}
void _joinChannel() async {
String chanelToJoin =
widget.isHost ? widget.channelId : widget.relayChannelId;
String tokenToJoin = widget.isHost ? widget.token : widget.relayToken;
await _engine.joinChannel(
token: tokenToJoin,
channelId: chanelToJoin,
uid: 0,
options: const ChannelMediaOptions());
setState(() {
isJoined = true;
});
}
Future<void> _leaveChannel() async {
await _onPressRelayOrStop();
await _engine.leaveChannel();
setState(() {
isJoined = false;
});
}
Future<void> _onPressRelayOrStop() async {
if (isRelaying) {
await _engine.stopChannelMediaRelay();
await _leaveChannel();
setState(() {
isRelaying = !isRelaying;
});
return;
}
await _engine.startOrUpdateChannelMediaRelay(ChannelMediaRelayConfiguration(
srcInfo: ChannelMediaInfo(
channelName: widget.channelId,
token: widget.token,
uid: 0,
),
destInfos: [
ChannelMediaInfo(
channelName: widget.relayChannelId,
token: widget.relayToken,
uid: 1)
],
destCount: 1,
));
}
@override
Widget build(BuildContext context) {
if (!_isReadyPreview) return Container();
return (widget.isHost)
? Stack(
children: [
Flex(
direction: Axis.vertical,
children: [
Flexible(
flex: 1,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: VideoCanvas(uid: remoteUid),
),
),
),
Flexible(
flex: 1,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(uid: 0),
),
),
),
],
),
if(isJoined)
Positioned(
bottom: 0,
right: 0,
child: ElevatedButton(
onPressed: ()=>_onPressRelayOrStop(),
child: Text(isRelaying ? 'Stop Relay' : 'Start Relay'),
),
),
],
)
: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment