接了一个国外的项目,项目采用网易云im + 网易云信令+声网rtm遇到的一些问题

这个项目只对接口,给的工期是两周,延了工期,问题还是比较多的

  1. 需要全局监听rtm信息,收到监听内容,引起视频通话
  2. 网易云给的文档太烂,所有的类型推策只能文档一点点推
  3. 声网的rtm配置网易云的信令,坑太多,比如声网接收的字段是number,网易云给的字段是string等一系列报错问题
  4. im普通的对接,体验太差,采用倒叙分页解决此问题
  5. im的上传图片上传过程无显示,需要做上传图片的百分比显示

解决 im普通的对接,体验太差,采用倒叙分页解决此问题和图片上传百分比显示

//imNIMMessageListOption option = NIMMessageListOption(conversationId: widget.conversationId ?? '',direction: NIMQueryDirection.desc, //倒叙limit: limit,anchorMessage: _anchorMessage,// endTime: endTime,);

//图片
// 采用模拟发送数据,根据im提供的NimCore.instance.messageService.sendMessage ,得到是否成功,来显示状态Future<void> _pickImage() async {try {_logI('Picking image from gallery');final XFile? pickedFile = await _imagePicker.pickImage(source: ImageSource.gallery,imageQuality: 80,);if (pickedFile != null) {// u83b7u53d6u6587u4ef6u4fe1u606ffinal File imageFile = File(pickedFile.path);final String fileName = pickedFile.name;// u83b7u53d6u56feu7247u5c3au5bf8final decodedImage =await decodeImageFromList(imageFile.readAsBytesSync());final int width = decodedImage.width;final int height = decodedImage.height;// u521bu5efau4e34u65f6u6d88u606ffinal tempMessage = UnifiedMessage.createTempImage(pickedFile.path);setState(() {_messages.insert(0, tempMessage);});_scrollToBottom();// u5f00u59cbu4e0au4f20_sendImageMessage(tempMessage, pickedFile.path, fileName, width, height);}} catch (e) {_logI('Error picking image: $e');ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Failed to pick image: $e')),);}}

全局监听rtm信息回调

建立 call_manager.dart,单页面引入

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:nim_core_v2/nim_core.dart';
import 'package:yunxin_alog/yunxin_alog.dart';
import '../screens/video_call_screen.dart';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import '../utils/event_bus.dart';
import '../utils/toast_util.dart';var listener;class CallManager {static final CallManager _instance = CallManager._internal();factory CallManager() => _instance;CallManager._internal();RtcEngine? _engine; // 添加 engine 变量final AudioPlayer _audioPlayer = AudioPlayer();Timer? _ringtoneTimer;BuildContext? _lastContext;bool _isShowingCallDialog = false;void initialize(BuildContext context) {_lastContext = context;_setupSignallingListeners();}void updateContext(BuildContext context) {_lastContext = context;}// 添加设置 engine 的方法void setEngine(RtcEngine engine) {_engine = engine;}// 修改现有的代码void _handleCallHangup(event) {print('关闭信令频道房间成功${event.channelInfo!.channelId} 目前一样');NimCore.instance.signallingService.closeRoom(event.channelInfo!.channelId!, true, null).then((result) async {_isShowingCallDialog = false;EventBusUtil().eventBus.fire(VideoCallEvent(VideoCallEvent.LEAVE_CHANNEL));if (result.isSuccess) {if (_engine != null) {await _engine!.leaveChannel();await _engine!.release();}// Success handling} else {// Error handling}});}@overridevoid dispose() {print('dispose');if (listener != null) {listener.cancel();listener = null;}}// 添加一个方法来检查并清理监听器void cleanup() {print('cleanup');if (listener != null) {listener.cancel();listener = null;}_ringtoneTimer?.cancel();_ringtoneTimer = null;_audioPlayer.stop();_isShowingCallDialog = false;}// NIMSignallingEventTypeUnknown	0	未知
// NIMSignallingEventTypeClose	1	关闭信令频道房间
// NIMSignallingEventTypeJoin	2	加入信令频道房间
// NIMSignallingEventTypeInvite	3	邀请加入信令频道房间
// NIMSignallingEventTypeCancelInvite	4	取消邀请加入信令频道房间
// NIMSignallingEventTypeReject	5	拒绝入房的邀请
// NIMSignallingEventTypeAccept	6	接受入房的邀请
// NIMSignallingEventTypeLeave	7	离开信令频道房间
// NIMSignallingEventTypeControl	8	自定义控制命令void _setupSignallingListeners() {// Listen for online events (when the app is active)listener = NimCore.instance.signallingService.onOnlineEvent.listen((NIMSignallingEvent event) {print("事件监听开始${event.toJson()}");_handleSignallingEvent(event);});// Listen for offline events (when the app is in background)NimCore.instance.signallingService.onOfflineEvent.listen((event) {// Handle offline eventsprint('Offline event: $event');});// Listen for multi-client eventsNimCore.instance.signallingService.onMultiClientEvent.listen((event) {// Handle multi-client eventsprint('Multi-client event: $event');});Alog.i(tag: 'CallManager', content: 'Signalling listeners setup complete');}void _handleSignallingEvent(NIMSignallingEvent event) {if (event.channelInfo != null &&event.eventType ==NIMSignallingEventType.NIMSignallingEventTypeInvite) {// 3_showIncomingCallDialog(event.channelInfo, event.requestId ?? '');}if (event.channelInfo != null &&event.eventType == NIMSignallingEventType.NIMSignallingEventTypeClose) {//1_handleCallHangup(event);}if (event.channelInfo != null &&event.eventType == NIMSignallingEventType.NIMSignallingEventTypeJoin) {EventBusUtil().eventBus.fire(VideoCallEvent(VideoCallEvent.USER_JOINED));}if (event.channelInfo != null &&event.eventType ==NIMSignallingEventType.NIMSignallingEventTypeReject) {ToastUtil.showDanger('user reject');cleanup();}}Future<void> _playRingtone() async {try {await _audioPlayer.play(AssetSource('sounds/incoming_call.mp3'));// Loop the ringtone_ringtoneTimer =Timer.periodic(const Duration(seconds: 3), (timer) async {await _audioPlayer.play(AssetSource('sounds/incoming_call.mp3'));});} catch (e) {Alog.e(tag: 'CallManager', content: 'Error playing ringtone: $e');}}void _stopRingtone() {_ringtoneTimer?.cancel();_ringtoneTimer = null;_audioPlayer.stop();}void _showIncomingCallDialog(NIMSignallingChannelInfo? channelInfo, String requestId) {if (channelInfo == null || _lastContext == null || _isShowingCallDialog) {return;}_isShowingCallDialog = true;_playRingtone();String? channelId = channelInfo.channelId;String? callerName = channelInfo.creatorAccountId;showDialog(context: _lastContext!,barrierDismissible: false,builder: (context) {return AlertDialog(backgroundColor: Colors.black87,title: const Text('Incoming Video Call',style: TextStyle(color: Colors.white),textAlign: TextAlign.center,),content: Column(mainAxisSize: MainAxisSize.min,children: [const CircleAvatar(radius: 40,backgroundColor: Colors.purple,child: Icon(Icons.person, size: 50, color: Colors.white),),const SizedBox(height: 16),Text(callerName ?? 'Unknown Caller',style: const TextStyle(color: Colors.white,fontSize: 18,fontWeight: FontWeight.bold,),),const SizedBox(height: 8),const Text('is calling you...',style: TextStyle(color: Colors.white70),),],),actions: [Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [// Decline buttonElevatedButton(onPressed: () {_stopRingtone();_isShowingCallDialog = false;Navigator.of(context).pop();// Reject the call - using hangup instead of reject since reject isn't available_rejectCall(channelInfo, requestId, context);},style: ElevatedButton.styleFrom(backgroundColor: Colors.red,shape: const CircleBorder(),padding: const EdgeInsets.all(16),),child: const Icon(Icons.call_end, color: Colors.white),),// Accept buttonElevatedButton(onPressed: () {_stopRingtone();_isShowingCallDialog = false;Navigator.of(context).pop();// Accept the call_acceptCall(channelInfo, requestId, context);},style: ElevatedButton.styleFrom(backgroundColor: Colors.green,shape: const CircleBorder(),padding: const EdgeInsets.all(16),),child: const Icon(Icons.call, color: Colors.white),),],),],);},).then((_) {_stopRingtone();_isShowingCallDialog = false;});}Future<void> _acceptCall(NIMSignallingChannelInfo channelInfo,String requestId, BuildContext context) async {String? channelId = channelInfo.channelId;String? creatorAccountId = channelInfo.creatorAccountId;if (channelId == null || creatorAccountId == null) {return;}final params = NIMSignallingCallSetupParams(channelId: channelId,callerAccountId: creatorAccountId,requestId: requestId,);try {final result = await NimCore.instance.signallingService.callSetup(params);if (result.isSuccess) {// Navigate to the video call screen with incomingCall flag// 检查 context 是否还有效print("1121212${result.toMap()}");final setUpChanelId = result.data?.roomInfo?.channelInfo?.channelId;final setUpCalleeAccountId =result.data?.roomInfo?.channelInfo?.creatorAccountId;// final setUpRemoteUid = result.data?.roomInfo?.members?.first.uid ?? 0;// final setUpRemoteUid = result.data?.roomInfo?.members?.first.uid;final setUpRemoteUid =result.data?.roomInfo?.channelInfo?.creatorAccountId;if (!context.mounted) {Alog.e(tag: 'CallManager', content: 'Context is not mounted anymore');// 如果原始 context 无效,尝试使用 _lastContextif (_lastContext != null && _lastContext!.mounted) {context = _lastContext!;} else {Alog.e(tag: 'CallManager',content: 'No valid context available for navigation');return;}}Navigator.push(context,MaterialPageRoute(builder: (context) => VideoCallScreen(calleeAccountId: channelInfo.creatorAccountId,isIncomingCall: true,setUpChanelId: setUpChanelId,setUpCalleeAccountId: setUpCalleeAccountId,setUpRemoteUid: int.tryParse(setUpRemoteUid!) ?? 0),),);} else {Alog.e(tag: 'CallManager',content: 'Failed to setup video call: ${result.code}');ToastUtil.showDanger('Failed to setup video call');}} catch (e) {ToastUtil.showDanger('Failed to setup video call');Alog.e(tag: 'CallManager', content: 'Error accepting call: $e');}}Future<void> _rejectCall(NIMSignallingChannelInfo channelInfo,String requestId, BuildContext context) async {try {String? channelId = channelInfo.channelId;String? creatorAccountId = channelInfo.creatorAccountId;if (channelId == null || creatorAccountId == null) {return;}final params = NIMSignallingRejectInviteParams(channelId: channelId,inviterAccountId: creatorAccountId,requestId: requestId,);// Close the room since direct reject isn't availablefinal result =await NimCore.instance.signallingService.rejectInvite(params);if (result.isSuccess) {Alog.i(tag: 'CallManager', content: 'Call rejected successfully');} else {Alog.e(tag: 'CallManager',content: 'Failed to reject call: ${result.code}');}} catch (e) {Alog.e(tag: 'CallManager', content: 'Error rejecting call: $e');}}
}

待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/89023.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/89023.shtml
英文地址,请注明出处:http://en.pswp.cn/web/89023.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hive/spark sql中unix_timestamp 函数的坑以及时间戳相关的转换

我用的是hive版本是3.1.3&#xff0c;spark版本是3.3.1&#xff0c;它们的unix_timestamp 函数在同样的语句下转换出来的时间戳是完全不同的&#xff0c;如下试验所示1.unix_timestamp 函数的坑上图试验中我同样的计算 2025-07-11 10:00:00 时间点对应的时间戳&#xff0c;但是…

MyBatis专栏介绍

专栏导读 在当今的软件开发领域&#xff0c;持久层框架的选择对于提高开发效率和数据处理能力至关重要。MyBatis作为一个半自动化的ORM框架&#xff0c;因其灵活、高效的特点&#xff0c;在众多开发者中广受好评。本专栏《MyBatis实战》旨在通过深入浅出的方式&#xff0c;帮助…

HarmonyOS从入门到精通:自定义组件开发指南(七):自定义事件与回调

HarmonyOS从入门到精通&#xff1a;自定义组件开发指南&#xff08;七&#xff09;&#xff1a;自定义事件与回调 在HarmonyOS应用开发中&#xff0c;组件化架构是构建复杂界面的基础&#xff0c;而组件间的高效通信则是实现业务逻辑的核心。自定义事件与回调机制作为组件交互的…

C++编程学习(第七天)

基于过程的程序设计C既可以用来进行基于过程的程序设计&#xff0c;又可以用来进行面向对象的程序设计。基于过程的程序设计又称为过程化的程序设计&#xff0c;它的特点是&#xff1a;程序必须告诉计算机应当具体怎么做&#xff0c;也就是要给出计算机全部操作的具体过程&…

ubuntu透网方案

场景&#xff1a;两个linux/Ubuntu系统&#xff0c;一个可以上网&#xff0c;一个不能&#xff0c;让不能上网的,让能上网的共享网络 步骤 1&#xff1a;修改 /etc/sysctl.conf sudo nano /etc/sysctl.conf 找到或添加以下行&#xff1a; net.ipv4.ip_forward1 按 CtrlO 保存&a…

基于Python的物联网岗位爬取与可视化系统的设计与实现【海量数据、全网岗位可换】

文章目录有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍数据采集数据预处理系统展示总结每文一语有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 随着物联网技术的迅速发展&#xff0c;物联网行业…

线性回归原理推导与应用(十):逻辑回归多分类实战

本篇文章将利用sklearn中内置的鸢尾花数据进行逻辑回归建模并对鸢尾花进行分类。对于逻辑回归和线性回归的相关原理&#xff0c;可以查看之前的文章 数据导入 鸢尾花数据是机器学习里的常用数据&#xff0c;首先导入一些基础库并从sklearn中导入数据集 #导入用到的一些pytho…

Docker 部署emberstack/sftp 镜像

Docker 部署 emberstack/sftp 镜像 1、找到国内可用的docker源,本次测试使用docker.1ms.run 2、下载emberstack/sftp镜像docker pull docker.1ms.run/emberstack/sftp3、安装并启动emberstack/sftp镜像docker run -d -p 22:22 --name SFTP -v D:\SFTP:/home/sftpuser/sftp --pr…

【华为OD】MVP争夺战2(C++、Java、Python)

文章目录题目题目描述输入描述输出描述示例思路核心思路&#xff1a;关键观察&#xff1a;算法步骤&#xff1a;排序策略&#xff1a;特殊情况处理&#xff1a;代码CJavaPython复杂度分析时间复杂度空间复杂度结果总结题目 题目描述 给定一个整型数组&#xff0c;请从该数组中…

Python打卡训练营Day58

DAY 58 经典时序预测模型2知识点回顾&#xff1a;时序建模的流程时序任务经典单变量数据集ARIMA&#xff08;p&#xff0c;d&#xff0c;q&#xff09;模型实战SARIMA摘要图的理解处理不平稳的2种差分n阶差分---处理趋势季节性差分---处理季节性建立一个ARIMA模型&#xff0c;通…

003大模型基础知识

大模型分类&#xff1a; 技术架构&#xff1a; Encoder Only Bert Decoder Only 著名的大模型都是 Encoder - Decoder T5 是否开源&#xff1a; 开源阵营&#xff1a; Llama DeepSeek Qwen 闭源阵营&#xff1a; ChatGpt Gemini Claude 语言模型发展阶段&am…

JVM监控及诊断工具-GUI篇

19.1. 工具概述 使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息&#xff0c;但它们存在下列局限&#xff1a; 1&#xff0e;无法获取方法级别的分析数据&#xff0c;如方法间的调用关系、各方法的调用次数和调用时间等&#xff08;这对定位应用性能瓶颈…

适用于Windows系统截图工具

1.Faststone Capture 官网网址&#xff1a;https://faststone-capture.com/ 网上很多注册码&#xff1a;https://www.cnblogs.com/LiuYanYGZ/p/16839503.html 2.Snipaste 官网网址&#xff1a;https://apps.microsoft.com/detail/9p1wxpkb68kx?launchtrue&modefull&…

区块链的三种共识机制——PoW、PoS和DPoS原理

区块链的核心是去中心化网络的信任机制&#xff0c;而共识机制是实现这一目标的关键。共识机制可分为两个阶段&#xff1a;&#xff08;1&#xff09;提出共识内容&#xff08;2&#xff09;对内容达成共识&#xff08;遵循最长链原则&#xff09;。三种主流的共识机制主要有工…

React 和 Vue的自定义Hooks是如何实现的,如何创建自定义钩子

目的&#xff1a;将公共逻辑提取出来&#xff0c;类似于 mixin&#xff0c;解决了mixin的设计缺陷。 React 和 Vue 自定义 Hooks 实现对比 React 自定义 Hooks React 的自定义 Hooks 是 JavaScript 函数&#xff0c;它们以 use 开头&#xff0c;可以调用其他 Hooks。 基本规则 …

构建高效事件驱动架构:AWS S3与SQS集成实践指南

引言 在现代云架构中,事件驱动的设计模式越来越受到开发者的青睐。AWS S3与SQS的集成为我们提供了一个强大的事件处理机制,能够在文件上传、删除或修改时自动触发后续的业务逻辑。本文将详细介绍如何配置S3事件通知到SQS队列,并分享实际项目中的最佳实践。 架构概述 S3事…

C++ -- STL-- List

////// 欢迎来到 aramae 的博客&#xff0c;愿 Bug 远离&#xff0c;好运常伴&#xff01; ////// 博主的Gitee地址&#xff1a;阿拉美 (aramae) - Gitee.com 时代不会辜负长期主义者&#xff0c;愿每一个努力的人都能达到理想的彼岸。1. list的介绍及使用 2. list的深度剖…

rt-thread 线程间同步方法详解

rt-thread 线程间同步方法详解一、什么是线程间同步线程同步的必要性线程同步的挑战二、同步方式1、信号量信号量工作机制信号量的管理方式信号量的创建与删除信号量的获取与释放信号量的典型应用场景信号量的注意事项2、互斥量互斥量工作机制互斥量的特性互斥量的操作接口互斥…

Spring Boot + Vue2 实现腾讯云 COS 文件上传:从零搭建分片上传系统

目录 一、项目目标 二、腾讯云 COS 基本配置 1. 创建存储桶 2. 获取 API 密钥 3. 设置跨域规则&#xff08;CORS&#xff09; 三、后端&#xff08;Spring Boot&#xff09;实现 1. 依赖配置 2. 配置腾讯云 COS&#xff08;application.yml&#xff09; 3. 初始化 COS…

使用 Java 获取 PDF 页面信息(页数、尺寸、旋转角度、方向、标签与边框)

目录 引言 一、安装和引入PDF处理库 二、获取 PDF 页数 三、获取页面尺寸&#xff08;宽高&#xff09; 四、获取页面旋转角度 五、判断页面方向&#xff08;横向 / 纵向&#xff09; 六、获取页面标签 七、获取页面边框信息 八、总结 引言 了解 PDF 页面属性是我们在…