Flutter高仿微信-第35篇-单聊-视频通话

2023-10-22 12:59

本文主要是介绍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篇-单聊-视频通话的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/261620

相关文章

如何用java对接微信小程序下单后的发货接口

《如何用java对接微信小程序下单后的发货接口》:本文主要介绍在微信小程序后台实现发货通知的步骤,包括获取Access_token、使用RestTemplate调用发货接口、处理AccessTok... 目录配置参数 调用代码获取Access_token调用发货的接口类注意点总结配置参数 首先需要获取Ac

Flutter监听当前页面可见与隐藏状态的代码详解

《Flutter监听当前页面可见与隐藏状态的代码详解》文章介绍了如何在Flutter中使用路由观察者来监听应用进入前台或后台状态以及页面的显示和隐藏,并通过代码示例讲解的非常详细,需要的朋友可以参考下... flutter 可以监听 app 进入前台还是后台状态,也可以监听当http://www.cppcn

Java如何获取视频文件的视频时长

《Java如何获取视频文件的视频时长》文章介绍了如何使用Java获取视频文件的视频时长,包括导入maven依赖和代码案例,同时,也讨论了在运行过程中遇到的SLF4J加载问题,并给出了解决方案... 目录Java获取视频文件的视频时长1、导入maven依赖2、代码案例3、SLF4J: Failed to lo

Python实现多路视频多窗口播放功能

《Python实现多路视频多窗口播放功能》这篇文章主要为大家详细介绍了Python实现多路视频多窗口播放功能的相关知识,文中的示例代码讲解详细,有需要的小伙伴可以跟随小编一起学习一下... 目录一、python实现多路视频播放功能二、代码实现三、打包代码实现总结一、python实现多路视频播放功能服务端开

Python实现视频转换为音频的方法详解

《Python实现视频转换为音频的方法详解》这篇文章主要为大家详细Python如何将视频转换为音频并将音频文件保存到特定文件夹下,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5. 注意事项

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Python视频处理库VidGear使用小结

《Python视频处理库VidGear使用小结》VidGear是一个高性能的Python视频处理库,本文主要介绍了Python视频处理库VidGear使用小结,文中通过示例代码介绍的非常详细,对大家的... 目录一、VidGear的安装二、VidGear的主要功能三、VidGear的使用示例四、VidGea

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接