在flutter中使用agora进行屏幕共享

时间:2021-06-21 05:02:41

标签: flutter agora.io

我需要在我的 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", err, 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", channel, 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, elapsed);
                                        Log.i(TAG, "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", uid, 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", videoCallJoinMsg, 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, uid, 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}', style: TextStyle(color: Colors.white),)));
        setState(() {
          remoteUid.add(uid);
        });
      },
      userOffline: (uid, reason) {
        print('userOffline  ${uid} ${reason}');
        ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('User Offline: ${uid} \n ${reason}', style: TextStyle(color: Colors.white),)));

        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()}', style: TextStyle(color: Colors.white),)));
        setState(() {
        });
      },
      remoteVideoStateChanged: (uid, reason, stats, num){
        print('RemoteVideo Stats $uid, $reason, $stats, $num\n');
        ScaffoldMessenger.of(context).showSnackBar(new SnackBar(content: Text('Remote Video Stats changed!', style: TextStyle(color: Colors.white),)));
      },
      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,
                      ),
                    ),
                  ),
                )),
              ),
            ),
          )
        ],
      ),
    );
  }
}

有人可以帮忙吗?

0 个答案:

没有答案