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

相关文章

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in

微信公众号脚本-获取热搜自动新建草稿并发布文章

《微信公众号脚本-获取热搜自动新建草稿并发布文章》本来想写一个自动化发布微信公众号的小绿书的脚本,但是微信公众号官网没有小绿书的接口,那就写一个获取热搜微信普通文章的脚本吧,:本文主要介绍微信公众... 目录介绍思路前期准备环境要求获取接口token获取热搜获取热搜数据下载热搜图片给图片加上标题文字上传图片

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Flutter打包APK的几种方式小结

《Flutter打包APK的几种方式小结》Flutter打包不同于RN,Flutter可以在AndroidStudio里编写Flutter代码并最终打包为APK,本篇主要阐述涉及到的几种打包方式,通... 目录前言1. android原生打包APK方式2. Flutter通过原生工程打包方式3. Futte

如何用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实现多路视频播放功能服务端开