本文主要是介绍Flutter高仿微信-第35篇-单聊-视频通话,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。
详情请查看
效果图:
目前市场上第三方视频接口的价格高的吓人
视频通话价格:
标清(SD) 14元/千分钟
高清(HD) 28元/千分钟
超高清(Full HD)63元/千分钟
2K 112元/千分钟
4K 252元/千分钟
这里的视频通话不接第三方sdk,自己实现的视频服务器。
详情请参考Flutter高仿微信-第29篇-单聊 , 这里只是提取视频通话的部分代码。
实现代码:
/*** Author : wangning* Email : maoning20080809@163.com* Date : 2022/9/25 14:46* Description : 发起视频请求页面*/class VideoCallWidget extends StatefulWidget {static String tag = 'video_call_widget';//视频账号final String videoPeerId;final String mediaFlag;String host = CommonUtils.BASE_IP;VideoCallWidget({required this.videoPeerId, required this.mediaFlag});@override_VideoCallState createState() => _VideoCallState(); }class _VideoCallState extends State<VideoCallWidget> {Signaling? _signaling;String? _selfId;final RTCVideoRenderer _localRenderer = RTCVideoRenderer();final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();bool _inCalling = false;Session? _session;DesktopCapturerSource? selected_source_;bool _waitAccept = false;bool _isExist = false;UserBean? userBean;//麦克风打开bool isMic = true;//扬声器bool isSpeaker = true;@overrideinitState() {super.initState();initRenderers();_connect(context);WidgetsBinding.instance.addPostFrameCallback((timeStamp) {Timer(Duration(seconds: 1),(){_invitePeer(context, widget.videoPeerId, false);_inCalling = false;setState(() {});});});loadUser();_playVoice();}void loadUser () async{userBean = await UserRepository.getInstance().findUserByAccount(widget.videoPeerId);if(userBean != null){setState(() {});}}initRenderers() async {await _localRenderer.initialize();await _remoteRenderer.initialize();}@overridedeactivate() {super.deactivate();_signaling?.close();_localRenderer.dispose();_remoteRenderer.dispose();_timer?.cancel();_stopVoice();}void _connect(BuildContext context) async {LogUtils.d("connect开始 ${widget.mediaFlag}");_signaling ??= Signaling(widget.host, context)..connect();LogUtils.d("connect结束");_signaling?.onSignalingStateChange = (SignalingState state) {LogUtils.d("video_call_sample onSignalingStateChange1: ${state}");switch (state) {case SignalingState.ConnectionClosed:case SignalingState.ConnectionError:case SignalingState.ConnectionOpen:break;}};_signaling?.onCallStateChange = (Session session, CallState state) async {LogUtils.d("video_call_sample onCallStateChange2:${state} , _waitAccept = ${_waitAccept}");switch (state) {case CallState.CallStateNew:setState(() {_session = session;});break;case CallState.CallStateRinging:bool? accept = await _showAcceptDialog();if (accept!) {_accept();setState(() {_inCalling = true;_processTimer();});} else {_reject();}break;case CallState.CallStateBye:LogUtils.d("video_call_sample 挂断::${_waitAccept}, ${mounted}");if(!_isExist){Navigator.pop(context);}setState(() {_localRenderer.srcObject = null;_remoteRenderer.srcObject = null;_inCalling = false;_session = null;});break;case CallState.CallStateInvite:_waitAccept = true;LogUtils.d("video_call_sample 邀请开始::${_waitAccept}");break;case CallState.CallStateConnected:_stopVoice();setState(() {_inCalling = true;_processTimer();});break;case CallState.CallStateRinging:}};_signaling?.onPeersUpdate = ((event) {setState(() {_selfId = event['self'];LogUtils.d("video_call_sample 我的账号:${_selfId}");});});_signaling?.onLocalStream = ((stream) {LogUtils.d("video_call_sample onLocalStream 3:");_localRenderer.srcObject = stream;setState(() {});});_signaling?.onAddRemoteStream = ((_, stream) {LogUtils.d("video_call_sample onAddRemoteStream 4:");_remoteRenderer.srcObject = stream;setState(() {});});_signaling?.onRemoveRemoteStream = ((_, stream) {LogUtils.d("video_call_sample onRemoveRemoteStream 5 :");_remoteRenderer.srcObject = null;});}Future<bool?> _showAcceptDialog() {return showDialog<bool?>(context: context,builder: (context) {return AlertDialog(title: Text("视频通话"),content: Text("是否接受好友的视频请求?"),actions: <Widget>[TextButton(child: Text("拒绝"),onPressed: () => Navigator.of(context).pop(false),),TextButton(child: Text("接受"),onPressed: () {Navigator.of(context).pop(true);},),],);},);}//开始播放视频声音void _playVoice(){final List<String> soundList = CommonUtils.getSoundList();int selectedVideoCallId = SpUtils.getIntDefaultValue(CommonUtils.SETTING_VIDEO_CALL_ID, 2);bool videoCallSwitch = SpUtils.getBoolDefaultValue(CommonUtils.SETTING_VIDEO_CALL_SWITCH, true);//如果设置视频通话不响铃if(!videoCallSwitch){return;}//设置了视频通话响铃,但是选择无声音if(videoCallSwitch && selectedVideoCallId == 0){return;}String sound = "${soundList[selectedVideoCallId]}";AudioPlayer.getInstance().playAsset("sounds/${sound}.mp3", isLoop:true, callback:(data){LogUtils.d("播放视频声音:${data}");});}void _stopVoice(){AudioPlayer.getInstance().stop();}//显示邀请页面Widget _showInvateWidget(){return Container(child: Column(mainAxisAlignment: MainAxisAlignment.spaceBetween,crossAxisAlignment: CrossAxisAlignment.start,children: [//SizedBox(height: 30,),Container(alignment: AlignmentDirectional.center,margin: EdgeInsets.only(top: 18),child: Column(children: [//Image.asset(CommonUtils.getBaseIconUrlPng("wc_chat_speaker_open"), width: 28, height: 28,),Text("等待对方接受邀请.", style: TextStyle(fontSize: 18, color: Colors.black),),SizedBox(height: 30,),CommonAvatarView.showBaseImage(userBean?.avatar??"", 100, 100),SizedBox(height: 10,),Text("${userBean?.nickName}", style: TextStyle(fontSize: 26, color: Colors.black),),],),),Container(margin: EdgeInsets.only(bottom: 40),alignment: AlignmentDirectional.center,child: FloatingActionButton(child: Icon(Icons.call_end),backgroundColor: Colors.pink,onPressed: _hangUp,),),],),);}_invitePeer(BuildContext context, String peerId, bool useScreen) async {if (_signaling != null && peerId != _selfId) {LogUtils.d("video_call_sample 邀请:${peerId} - ${widget.mediaFlag}");_signaling?.invite(peerId, 'video', widget.mediaFlag, useScreen);}}_accept() {LogUtils.d("video_call_sample 接受:${_session}");if (_session != null) {_signaling?.accept(_session!.sid);}}_reject() {LogUtils.d("video_call_sample 拒绝:${_session}");if (_session != null) {_signaling?.reject(_session!.sid);}}_hangUp() {LogUtils.d("video_call_sample 挂起:${_session} , ${_session?.sid}");if (_session != null) {_signaling?.bye(_session!.sid);}_isExist = true;Navigator.pop(context);}_switchCamera() {LogUtils.d("video_call_sample 切换摄像头:${_session}");_signaling?.switchCamera();}_muteMic() {LogUtils.d("video_call_sample 音频:${_session}");_signaling?.muteMic();isMic = !isMic;setState(() {});}enableSpeakerphone() {LogUtils.d("show_video_call 外放:_signaling = ${_signaling}");_signaling?.enableSpeakerphone();isSpeaker =!isSpeaker;setState(() {});}Timer? _timer;//计时多少秒int currentTimer = 0;//转换结果时间String resultTimer = "00:00";void _processTimer(){if(_inCalling && widget.mediaFlag == CommonUtils.MEDIA_FLAG_VOICE){_timer = Timer.periodic(Duration(seconds: 1), (timer) {currentTimer++;resultTimer = WnDateUtils.changeSecondToMMSS(currentTimer);setState(() {});});}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: WnAppBar.getAppBar(context, Text(widget.mediaFlag == CommonUtils.MEDIA_FLAG_VIDEO? '视频通话':'语音通话')),floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,floatingActionButton: _inCalling? SizedBox(width: double.infinity,child: Row(children: <Widget>[//扬声器图标: https://www.iconfont.cn/search/index?searchType=icon&q=扬声器getSwitchCameraWidget(),getHangUpWidget(),getMicWidget(),getSpeakerWidget(),])): null,body: _inCalling? OrientationBuilder(builder: (context, orientation) {return Container(child: Stack(children: <Widget>[Positioned(left: 0.0,right: 0.0,top: 0.0,bottom: 0.0,child: Offstage(offstage: widget.mediaFlag == CommonUtils.MEDIA_FLAG_VOICE,child:Container(margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: RTCVideoView(_remoteRenderer),decoration: BoxDecoration(color: Colors.black54),) ,),),Positioned(left: 20.0,top: 20.0,child: Offstage(offstage: widget.mediaFlag == CommonUtils.MEDIA_FLAG_VOICE,child: Container(width: orientation == Orientation.portrait ? 90.0 : 120.0,height:orientation == Orientation.portrait ? 120.0 : 90.0,child: RTCVideoView(_localRenderer, mirror: true),decoration: BoxDecoration(color: Colors.black54),),),),Positioned(left: 20.0,right: 20.0,top: 30.0,child: Offstage(offstage: widget.mediaFlag == CommonUtils.MEDIA_FLAG_VIDEO,child: Container(width: orientation == Orientation.portrait ? 190.0 : 220.0,height:orientation == Orientation.portrait ? 220.0 : 190.0,child: Column(children: [Text("${resultTimer}", style: TextStyle(fontSize: 20, color: Colors.grey.shade500),),SizedBox(height: 40,),CommonAvatarView.showBaseImage(userBean?.avatar??"", 80, 80),SizedBox(height: 8,),Text("${userBean?.nickName}", style: TextStyle(fontSize: 18, color: Colors.black),),],),),),)]),);}): _showInvateWidget(),);}//切换摄像头Widget getSwitchCameraWidget(){return Expanded(child: Container(width: 80,height: 100,child: Column(children: [FloatingActionButton(child: const Icon(Icons.switch_camera),onPressed: _switchCamera,),SizedBox(height: 10,),Text("切换摄像头", style: TextStyle(fontSize: 12, color: Colors.white),),],),));}//挂断Widget getHangUpWidget(){return Expanded(child: Container(width: 80,height: 100,child: Column(children: [FloatingActionButton(child: Icon(Icons.call_end),backgroundColor: Colors.pink,onPressed: _hangUp,),SizedBox(height: 10,),Text("挂 断", style: TextStyle(fontSize: 12, color: Colors.white),),],),));}//麦克风Widget getMicWidget(){return Expanded(child: Container(width: 80,height: 100,child: Column(children: [FloatingActionButton(child: Icon(isMic?Icons.mic:Icons.mic_off),onPressed: _muteMic,),SizedBox(height: 10,),Text(isMic?"麦克风已开":"麦克风已关", style: TextStyle(fontSize: 12, color: Colors.white),),],),));}//扬声器Widget getSpeakerWidget(){return Expanded(child: Container(width: 80,height: 100,child: Column(children: [FloatingActionButton(child: Image.asset(CommonUtils.getBaseIconUrlPng(isSpeaker?"wc_chat_speaker_open":"wc_chat_speaker_close"), width: 28, height: 28,),onPressed: enableSpeakerphone,),SizedBox(height: 10,),Text(isSpeaker?"扬声器已开":"扬声器已关", style: TextStyle(fontSize: 12, color: Colors.white),),],),));}}
这篇关于Flutter高仿微信-第35篇-单聊-视频通话的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!