如何解决在flutter中使用agora进行屏幕共享
我需要在我的 flutter 应用程序中使用 argora sdk 添加屏幕共享功能。 屏幕共享仅支持本机。但是不知道如何调用本机代码来颤动。 我试图从本机端启动屏幕共享服务,但是当服务启动时,视频视图被冻结。我试过下面的代码。
以下代码适用于安卓端:
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),CHANNEL)
.setMethodCallHandler(
(call,result) -> {
if (call.method.equals("shareScreen")) {
handler = new Handler(Looper.getMainLooper());
appId = call.argument("appId");
token = call.argument("token");
channelId = call.argument("ChannelId");
Log.d("AppId:" + appId,"Token:"+token);
IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler() {
@Override
public void onWarning(int warn) {
Log.w(TAG,String.format("onWarning code %d message %s",warn,RtcEngine.getErrorDescription(warn)));
}
/**Reports an error during SDK runtime.
* Error code: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html*/
@Override
public void onError(int err) {
Log.e(TAG,String.format("onError code %d message %s",err,RtcEngine.getErrorDescription(err)));
videoCallJoinMsg = String.format("onError code %d message %s",RtcEngine.getErrorDescription(err));
result.error("VIDEOCALL_ERROR",videoCallJoinMsg,null);
}
/**Occurs when the local user joins a specified channel.
* The channel name assignment is based on channelName specified in the joinChannel method.
* If the uid is not specified when joinChannel is called,the server automatically assigns a uid.
* @param channel Channel name
* @param uid User ID
* @param elapsed Time elapsed (ms) from the user calling joinChannel until this callback is triggered*/
@Override
public void onJoinChannelSuccess(String channel,int uid,int elapsed) {
Log.i(TAG,String.format("onJoinChannelSuccess channel %s uid %d",channel,uid));
videoCallJoinMsg = String.format("onJoinChannelSuccess channel %s uid %d",uid);
myUid = uid;
joined = true;
handler.post(() -> {
videoCallJoinMsg = "Joined Successfully";
joined = true;
result.success(videoCallJoinMsg);
});
}
@Override
public void onLocalVideoStateChanged(int localVideoState,int error) {
super.onLocalVideoStateChanged(localVideoState,error);
if (localVideoState == 1) {
Log.e(TAG,"launch successfully");
}
}
@Override
public void onRemoteVideoStateChanged(int uid,int state,int reason,int elapsed) {
super.onRemoteVideoStateChanged(uid,state,reason,elapsed);
Log.i(TAG,"onRemoteVideoStateChanged:uid->" + uid + ",state->" + state);
if (state == REMOTE_VIDEO_STATE_STARTING) {
/*Check if the context is correct/
Context context = getApplicationContext();
if (context == null) {
return;
}
handler.post(() ->
{
remoteUid = uid;
curRenderMode = RENDER_MODE_HIDDEN;
// setRemotePreview(context);
});
}
}
@Override
public void onRemoteVideoStats(RemoteVideoStats stats) {
super.onRemoteVideoStats(stats);
Log.d(TAG,"onRemoteVideoStats: width:" + stats.width + " x height:" + stats.height);
}
/**Occurs when a remote user (Communication)/host (Live Broadcast) joins the channel.
* @param uid ID of the user whose audio state changes.
* @param elapsed Time delay (ms) from the local user calling joinChannel/setClientRole
* until this callback is triggered.*/
@Override
public void onUserJoined(int uid,int elapsed) {
super.onUserJoined(uid,"onUserJoined->" + uid);
videoCallJoinMsg = String.format("user %d joined!",uid);
}
@Override
public void onUserOffline(int uid,int reason) {
Log.i(TAG,String.format("user %d offline! reason:%d",uid,reason));
videoCallJoinMsg = String.format("user %d offline! reason:%d",reason);
handler.post(() -> {
/**Clear render view
Note: The video will stay at its last frame,to completely remove it you will need to
remove the SurfaceView from its parent*/
ENGINE.setupRemoteVideo(new VideoCanvas(null,RENDER_MODE_HIDDEN,uid));
// fl_remote.removeAllViews();
});
}
};
try {
ENGINE = RtcEngine.create(getApplicationContext(),appId,iRtcEngineEventHandler);
Log.d("RtcEngine:","started");
}
catch (Exception e) {
e.printStackTrace();
Log.d("RtcEngine Error:",e.getMessage());
result.error("UNAVAILABLE",null);
onBackPressed();
}
bindVideoService();
}
else {
result.notImplemented();
}
}
);
}
private void bindVideoService() {
Intent intent = new Intent();
intent.setClass(getApplicationContext(),ExternalVideoInputService.class);
mServiceConnection = new VideoInputServiceConnection();
getApplicationContext().bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
}
private void unbindVideoService() {
if (mServiceConnection != null) {
getApplicationContext().unbindService(mServiceConnection);
mServiceConnection = null;
}
}
private class VideoInputServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName,IBinder iBinder) {
mService = (IExternalVideoInputService) iBinder;
/*Start the screen recording service of the system/
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
MediaProjectionManager mpm = (MediaProjectionManager)
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
Intent intent = mpm.createScreenCaptureIntent();
startActivityForResult(intent,PROJECTION_REQ_CODE);
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mService = null;
}
}
颤振侧代码:
class _State extends State<JoinChannelVideo> {
late final RtcEngine _engine;
String channelId = ChannelId;
bool isJoined = false,switchCamera = true,switchRender = true;
List<int> remoteUid = [];
TextEditingController? _controller;
static const platform = const MethodChannel("com.agora/videoCallScreenShareMethodChannel");
videoCallScreenShare() async{
try {
Map data = {
"appId" : appId,"token" : token,"ChannelId" : channelId
};
await _engine.stopPreview();
final result = await platform.invokeMethod('shareScreen',data);
print("Result: $result");
} on PlatformException catch (e) {
}
}
@override
void initState() {
super.initState();
_controller = TextEditingController(text: channelId);
this._initEngine();
}
@override
void dispose() {
super.dispose();
_engine.destroy();
}
_initEngine() async {
_engine = await RtcEngine.createWithConfig(RtcEngineConfig(appId));
this._addListeners();
await _engine.enableVideo();
await _engine.startPreview();
await _engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
await _engine.setClientRole(ClientRole.Broadcaster);
}
_addListeners() {
_engine.setEventHandler(RtcEngineEventHandler(
joinChannelSuccess: (channel,elapsed) {
print('joinChannelSuccess ${channel} ${uid} ${elapsed}');
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Channel $channel Joined Successfully! ${uid} \n ${elapsed}',style: TextStyle(color: Colors.white),)));
setState(() {
isJoined = true;
});
},userJoined: (uid,elapsed) {
print('userJoined ${uid} ${elapsed}');
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('User Joined: ${uid} \n ${elapsed}',)));
setState(() {
remoteUid.add(uid);
});
},userOffline: (uid,reason) {
print('userOffline ${uid} ${reason}');
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('User Offline: ${uid} \n ${reason}',)));
setState(() {
remoteUid.removeWhere((element) => element == uid);
});
},remoteVideoStats: (stats){
print('RemoteVideo Stats ${stats.toJson()}\n');
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Remote Video Stats: ${stats.toJson()}',)));
setState(() {
});
},remoteVideoStateChanged: (uid,stats,num){
print('RemoteVideo Stats $uid,$reason,$stats,$num\n');
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Remote Video Stats changed!',)));
},leaveChannel: (stats) {
print('leaveChannel ${stats.toJson()}');
setState(() {
isJoined = false;
remoteUid.clear();
});
},));
}
_joinChannel() async {
if (defaultTargetPlatform == TargetPlatform.android) {
await [Permission.microphone,Permission.camera].request();
}
await _engine.joinChannel(token,channelId,null,uid);
}
_leaveChannel() async {
await _engine.leaveChannel();
}
_switchCamera() {
_engine.switchCamera().then((value) {
setState(() {
switchCamera = !switchCamera;
});
}).catchError((err) {
print('switchCamera $err');
});
}
_switchRender() {
setState(() {
switchRender = !switchRender;
remoteUid = List.of(remoteUid.reversed);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('VideoView'),),body: Stack(
children: [
Column(
children: [
TextField(
controller: _controller,decoration: InputDecoration(hintText: 'Channel ID'),onChanged: (text) {
setState(() {
channelId = text;
});
},Row(
children: [
Expanded(
flex: 1,child: ElevatedButton(
onPressed:
isJoined ? this._leaveChannel : this._joinChannel,child: Text('${isJoined ? 'Leave' : 'Join'} channel'),)
],_renderVideo(),],Align(
alignment: Alignment.centerRight,child: Column(
mainAxisSize: MainAxisSize.min,children: [
ElevatedButton(
onPressed: this._switchCamera,child: Text('Camera ${switchCamera ? 'front' : 'rear'}'),ElevatedButton(
onPressed: videoCallScreenShare,child: Text('ScreenShare'),)
],);
}
_renderVideo() {
return Expanded(
child: Stack(
children: [
RtcLocalView.SurfaceView(),Align(
alignment: Alignment.bottomLeft,child: SingleChildScrollView(
scrollDirection: Axis.horizontal,child: Row(
children: List.of(remoteUid.map(
(e) => GestureDetector(
onTap: this._switchRender,child: Container(
width: 120,height: 150,margin: EdgeInsets.only(bottom: 50),color: Colors.black,child: RtcRemoteView.SurfaceView(
uid: e,)),);
}
}
有人可以帮忙吗?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。