Created
May 30, 2024 15:38
-
-
Save bawantha/77380bc8b44f2b882c58f04325168c17 to your computer and use it in GitHub Desktop.
relay
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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