Licode—基于webrtc的SFU/MCU实现
1. webrtc浅析
webrtc的前世今生、编译方法、行业应用、最佳实践等技术与产业类的文章在网上卷帙浩繁,重复的内容我不再赘述。对我来讲,webrtc的概念可以有三个角度去解释:
(1).一个W3C和IETF制定的标准,约定了Web间实时音视频通信机制,通过该标准可开发基于浏览器的、无插件的web多媒体应用(一般是js),该标准仅设定了点对点无中心的实时会话场景,没有强制约束信令协议与内容,没有要求有媒体处理的中心服务器,主要目标是形成开发者与浏览器厂商良好的生态环境,并积极向HTML5靠拢争取被纳入进去;
(2).一群音视频算法和网络适应性算法,这些算法囊括了视频会议几乎所有的核心技术,包括音视频的采集、编解码、网络传输、播放显示、安全等功能,还提供了操作系统系统调用跨平台封装的实现,包含Windows,Linux,Mac,Android,iOS;
(3).一个开源工程,核心由c++实现,可通过修改、封装、提取代码等方式实现一套视频会议系统,客户端可实现为Web js、App或Windows应用程序等多种形式,服务端可实现包括业务外的所有服务,包括媒体服务、信令服务、穿墙服务、中继服务等等,这些服务稍微调整后可轻易支持分布式部署、容器部署、云部署等。
对webrtc的理解与使用,我认为有三个境界:
(1).能搭建一个简易的视频会议系统,其中客户端部分可以这样做: Windows端Mac端Linux(x86)端在自带的peerconnection client或libjingle改一下(取决于信令是http还是sip家族信令);Android/iOS端在apprtc或licodeAndroidClient及Licode-ErizoClientIOS改一下;web端用webrtc 自带js api实现一下。服务端部分可以这样做:信令服务器在apprtc的collider改一下;穿墙服务器用自带的stun server,turn server部署一下;中继服务器在自带的relay server改一下;媒体服务器在kurentos、licode、jitsi、Intel Collaboration Suite for WebRTC或janus改一下;如果需要和传统的SIP体系互通则在webrtc2sip改一下;如果需要关注实时通信过程中的延时、丢包、接通率、掉线率等质量问题,可以部署callstats来进行专业监测;把所有服务都再部署于云平台的多个虚拟主机上或阿里云的容器云服务,完成以上操作就搭建起初级规模的云上视频会议系统原型了。
(2).能提取、理解并使用webrtc的算法模块,即或将算法模块融入到自己的代码中,或将自己的算法添加至webrtc里作为开源贡献或自己产品的差异性优势。值得提取的算法模块包括音视频编解码与处理框架(vp8、vp9、voice engine、video engine),音视频采集呈现封装、音视频预处理(NetEq、NS、AEC、Video Jitter Buffer)、网络适应性(GCC算法、ARQ、FEC)、安全性(Dtls、Srtp/Srtcp)等,自己可添加的有视频编码模块(x265、nvenc、intel qsv、xavs2、以及其他定制化的网络传输策略)。
(3).能够结合基于rtmp-cdn/p2p等直播技术,构建既支持交互互动,又支持海量围观,可商用、能运营、易扩展、全兼容的音视频PaaS服务平台,完成一个多媒体通信的终极产品。
Licode是达到第一境界技术层面所需要了解的开源工程,它从webrtc代码中提取出了SFU/MCU媒体服务所用到的音视频、媒体传输、信令的功能代码,结合libnice与libav实现了ICE与媒体转发功能,并封装成了可被调用的js API,此外还用js实现了包含全局管理服务、数据库服务、业务逻辑服务与信令媒体的调用服务的业务代码。Licode实现了webrtc开源工程没直接实现的中心侧的媒体服务功能,并提供了简单的业务模型与分布式部署方式,从而得到了多媒体通信工程师的广泛关注。但是Licode的相关文档与资料非常少,故本文是在我个人观察运行调试代码后总结所写的,不正确之处希望在留言中能够得以指正。
2. Licode系统分析
2.1 系统架构
Licode的系统架构如下,有客户端和服务端,客户端包括ErizoClient和NuveClient,分别用来进行信令与音视频交互和业务操作,类似于终端和会控集成在一起;服务端包括Nuve、ErizoController、ErizoAgent和MessageBus,分别用来实现业务服务与全局管理服务、信令服务、媒体服务和服务通信,其中ErizoController类似于MC、ErizoAgent类似于MP。
图1 系统架构图
2.2 客户端与服务端交互流程
客户端与服务端的交互流程如下图所示,其中客户端代码集中在N.API.js与Client.js上,服务端代码集中在nuve.js与erizoController.js上。
图2 信令交互图
3. 系统组成与模块划分
3.1 系统组成
本系统组成仅指服务端的系统组成,包括Nuve业务管理服务,ErizoController信令服务,ErizoAgent媒体服务,ErizoJS媒体转发实体。个人觉得代码在文件命名与文件组织结构上比较乱,初学者不易理解,如erizo_controller文件夹包括了除nuve以及webrtc的js api之外的所有js代码,不仅仅是erizoController,此外webrtc核心部分命名为erizo,也让人与整个系统相混淆。系统组成如下图所示,一个Nuve管理多个ErizoController(以下简称EC),一个EC管理多个ErizoAgent(以下简称EA),一个EA管理多个ErizoJS(以下简称EJ),一个EJ就是一个SFU,封装了C++实现的webrtc、libav与libnice。
图3 服务端系统组成图
3.2 Nuve业务管理服务
图4 Nuve业务管理服务
图5 Nuve各模块用途及对应文件
此外,这篇文章Licode(二):Nuve源码分析 - CSDN博客 对Nuve做了更详细的分析。
3.3 ErizoController信令服务
图6 ErizoController信令服务
图7 ErizoController各模块用途及对应文件
3.4 ErizoAgent媒体服务
图8 ErizoAgent媒体服务
图9 ErizoAgent各模块用途及对应文件
3.5 ErizoJS媒体转发实体
图10 ErizoJS媒体转发实体
图11 ErizoJS各模块用途及对应文件
3.6 webrtc
由于webrtc工程代码本身非常庞大,编译工具也很独特,Licode并未全部引入,仅仅抽取了webrtc的部分代码,如下图所示
图12 webrtc各组件用途及对应目录
由此可见,所抽取的代码没有webrtc自带的音视频处理框架(voice engine/video engine/media engine),这是由于SFC只在RTP层进行转发并不解码,只需要check一下媒体流属性(如I帧头)即可。抽取代码的亮点在于将webrtc强大的抵抗网络时延和丢包的网络适应性算法和协议都提炼出来了,可以供广大研究视频传输的网络适应方向的开发者们单独学习、实验并快速集成到自己非webrtc-based的产品中来。
webrtc的网络适应性手段包括丢包重传(ARQ)、前向纠错(FEC)和拥塞控制(GCC),其中自动码率(ARC)在GCC中。对网络适应性有兴趣的朋友可对照代码看如下几篇博文:
(1). 丢包重传 (ARQ)
RFC4585 RTP/AVPF 传输层反馈;
RFC4588 4585补充 重传包格式;
RFC5104 4585补充 负载层反馈;
RFC5124 RTP/SAVPF
RFC5450 4585补充 Jitter报告
(2). 前向纠错 (FEC)
RFC2198 WebRTC中的前向纠错编码 - Red Packet
RFC5109 WebRTC 的前向纠错编码 - XOR FEC
(3). 带宽检测 (BWE)
传统的基于RTCP RR报文中丢包数来进行带宽检测的算法目前被认为是最low的了,因为属于事后处理;先进的算法是需要事前感知的(突发情况除外),假定带宽是逐渐变窄的,根据信号估计理论可以在网络链路发生丢包以前就监测到网络拥塞,
GCC草案
REMB草案
GCC算法与函数调用 WebRTC基于GCC的拥塞控制(上) - 算法分析 WebRTC基于GCC的拥塞控制(下) - 实现分析
带宽估计 WebRTC之带宽控制部分学习(1) ------基本demo的介绍
带宽检测 WebRTC 中的带宽侦测 - CSDN博客
接收缓冲区延迟估计 WebRTC视频接收缓冲区基于KalmanFilter的延迟模型
3.7 libav、libnice、libsrtp
libav是从ffmpeg分离出的开发者发布的开源工程,与早期ffmpeg有很大相似性,对于仅调用api的开发者来说就简单看成是代码量更小、第三方依赖库更少、编译更简单的轻量级ffmpeg吧。这个库在Licode编译过程中有处问题,Licode使用版本较旧的libav的接口,其中avcodec_alloc_frame和avcodec_free_frame都已经遗弃,改为av_frame_alloc/av_frame_free才可编译通过。
libnice库基于ICE协议实现的网络层库,Licode使用libnice库来实现端到端的ICE连接和数据流发送接收,以及candidates(候选地址)和SDP(媒体描述文件)的相互交换。
libsrtp库主要是是用来加密rtp/rtcp的。
4. 关键技术
4.1 网络收发流水线架构技术
网络收发或一个SFU实现,并非简单的从一个源地址收到若干个数据包再复制多份发送给多个目的地址,正如3.6所描述,除了需要堆栈式的ICE地址转换、DTLS/SRTP的解密,还需要流水线式的RTP/RTCP的丢包检测与重传、FEC处理、带宽检测与估计、包缓冲区调整以及平滑发送等若干个复杂步骤,这个流程在Licode代码中是通过Pipeline-Handler这样的架构实现的,由于webrtc仅提供底层算法并未提供SFU架构,所以我认为这种架构是Licode最重要的关键技术,可供未来想成为软件架构师的开发者参考学习。日后我也再详细分析这个架构,看看是否能抽象出一个新的设计模式来。
整个网络收发的层级模型图如下图所示,其中每一层右侧标注出了关键类。
图13 网络收发层级模型
Pipeline的类图如下图
图14 Pipeline相关类图
对Pipeline来说,需要全局管理、长时处理或非逐个包处理的流程,实现对象叫Service,包括Handler的管理、RTCP的计算与处理、当前状态的处理、网络质量的管理以及包缓冲的管理都是Service;而需要每次逐包处理的流程,实现对象叫Handler,包括19项Handler,其代码都位于erizo\src\erizo\rtp目录下
//新增全局处理的Service
pipeline_->addService(handler_manager_);
pipeline_->addService(rtcp_processor_);
pipeline_->addService(stats_);
pipeline_->addService(quality_manager_);
pipeline_->addService(packet_buffer_);
//新增单次处理的Handler
pipeline_->addFront(std::make_shared<RtcpProcessorHandler>());
pipeline_->addFront(std::make_shared<FecReceiverHandler>());
pipeline_->addFront(std::make_shared<LayerBitrateCalculationHandler>());
pipeline_->addFront(std::make_shared<QualityFilterHandler>());
pipeline_->addFront(std::make_shared<IncomingStatsHandler>());
pipeline_->addFront(std::make_shared<RtpTrackMuteHandler>());
pipeline_->addFront(std::make_shared<RtpSlideShowHandler>());
pipeline_->addFront(std::make_shared<RtpPaddingGeneratorHandler>());
pipeline_->addFront(std::make_shared<PliPacerHandler>());
pipeline_->addFront(std::make_shared<BandwidthEstimationHandler>());
pipeline_->addFront(std::make_shared<RtpPaddingRemovalHandler>());
pipeline_->addFront(std::make_shared<RtcpFeedbackGenerationHandler>());
pipeline_->addFront(std::make_shared<RtpRetransmissionHandler>());
pipeline_->addFront(std::make_shared<SRPacketHandler>());
pipeline_->addFront(std::make_shared<SenderBandwidthEstimationHandler>());
pipeline_->addFront(std::make_shared<LayerDetectorHandler>());
pipeline_->addFront(std::make_shared<OutgoingStatsHandler>());
pipeline_->addFront(std::make_shared<PacketCodecParser>());
pipeline_->addFront(std::make_shared<PacketWriter>(this));
4.2 分布式保活技术
Licode的系统设计如3.1所述,Nuve管理多个EC,EC管理多个EA,EA启动多个EJ进程;在资源上,Nuve负责EC的负载均摊,EC负责EA和EJ的负载均摊(毕竟EJ在EA内,EA除了监控OS性能并未太多其他工作),故Nuve需要EC分布式保活,EC需要EA分布式保活。Licode实现比较简单,代码可参考CloudHandler.js与ecCloudHandler.js。
例如Nuve每启动一个EC立刻分配id,并加入到ECList中,并在DB中写入id作为key,且在该key对应的value上进行累加,此后Nuve的秒级循环中不断check这个value并继续累加,如果超过阈值,则认为该EC失效,将此EC从list中删除。Nuve同时提供keepalive接口,EC通过RPC调用此接口不断对数据库中本id的value清零,从而实现了分布式保活。EC与EA间方法一样。
4.3 资源管理技术
这里定义的资源有三个含义:设备资源(设备简单分为发布设备、接收设备、收发设备)、内容资源(某地的实时视频或已录制视频)以及server处理能力余量(例如还能转发的路数,还能容纳的新成员)。
Licode的publish-subscribe模型将信令交互、内容管理(源站管理)、媒体分发三者分开,从而可实现多维度的资源管理。
Client 1 发的第一个信令仅起到了一个资源注册的作用,即room中存在Client 1的id了;
Client 1 发的第二个信令表明该id可以发布新内容了,内容为Stream 1;
Client 2 同理,room记录了Client 2的id与Stream 2;
Client 1发的第三个信令表明对Client 2的Stream 2内容感兴趣开始订阅了,于是启动了SFU过程;
Client 3加入room后,可以发布资源(Stream3),可以订阅资源(Stream1、Stream2),也可以什么都不做,因为任何在该room中注册的设备都可以pubilsh内容资源或subscirbe已publish的内容资源。完全解耦带来的好处是内容资源同设备资源一样也可以轻易管理了,在启动SFU过程中,server处理能力余量也能得到较好的管理。
H.323是紧耦合的,信令交互后必须开启媒体交互(电路交换的思维,信道容量有限,申请资源赶紧用,用完赶紧释放),对内容管理过于依赖于会议逻辑的实现,为了实现更灵活的互听互看,其逻辑会堆砌的很复杂。
然而好的方法也不是万能的,publish-subscribe的完全解耦虽带来极大的可扩展性,但对于某些传统业务更复杂了。比如MCU两个终端互看,就需要启动两个erizoJS,互相订阅发布,根据负载均摊的算法,可能两个erizoJS都不在一个erizoAgent里(不在同一台物理机或虚拟机上)。MCU轮询模式也稍复杂,轮询者需要不断的subscribe room中的发布者,发布者也要不断的去remove、add,切换速度比接收所有流+I Frame Check的传统方式要慢的多。
5. 总结
本文先介绍了自己对webrtc的概念理解与使用参考,接下来从系统架构、交互流程、系统组成与模块划分几个角度对Licode进行概要设计级别的分析,最后对自己觉得Licode比较有特色的三大技术,即网络收发流水线架构技术、分布式保活技术与资源管理技术进行了浅层次的解释。文中表述内容可能会有不准确之处,希望能与读者们交流并及时改正。
原文https://zhuanlan.zhihu.com/p/40462946
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
相关文章:
Licode—基于webrtc的SFU/MCU实现
1. webrtc浅析webrtc的前世今生、编译方法、行业应用、最佳实践等技术与产业类的文章在网上卷帙浩繁,重复的内容我不再赘述。对我来讲,webrtc的概念可以有三个角度去解释:(1).一个W3C和IETF制定的标准,约定…...
开发运维工具推荐 --- 解决远程访问局域网服务的问题。开发调试推荐
一、FastNat 可为您解决的问题1. 没公网服务器,需要发布本地的站点或网络程序到公网上,供他人访问;此项功能大大方面开发人员进行远程调试,微信小程序等开发工作进行。2. 需要远程到在其他网络中的设备,但两处的网络不…...
【华为OD机试 】单词倒序(C++ Java JS Python)
文章目录 题目描述输入描述输出描述备注用例题目解析C++ 解法JavaScript算法源码Java算法源码Python解法题目描述 输入单行英文句子,里面包含英文字母,空格以及,.?三种标点符号,请将句子内每个单词进行倒序,并输出倒序后的语句。 输入描述 输入字符串S,S的长度 1 ≤ N…...
PLC 诊断故障的基本原理
(1)东欢坨选煤厂机电设备故障信号主要取自开关量信号,PLC 通过开关量的通和断对设备进行故障诊断。PLC 对开关量的识别是通过输入模块来实现的。PLC 控制设备运行时,设备中的温度、压力、急停、跑偏、速度、过热以及各种按钮和行程开关的传感器与 PLC 输入模块相连接,输入模块的…...
QT打开外部程序并嵌入Qt子窗口的缺点
首先可以参考如下文章: QT打开外部程序并嵌入Qt界面_qt界面嵌入外部应用程序_初学小白Lu的博客-CSDN博客 Qt嵌入外部程序界面初探_qt嵌入其他程序窗口_liming4675的博客-CSDN博客 QT 如何把外部程序嵌入到QT界面_qt嵌入其他程序窗口_hellokandy的博客-CSDN博客 Qt界…...
如何系统地学习 C++ 语言?
C作为具有广泛适用性的编程语言,学习C的人越来越多,但是如何系统地学习C还是个问题,下面我们一起来看一下C学习的方法有哪些吧。 首先,要学习C,最重要的就是掌握C的基础知识。 比如数据结构、算法、微积分等。这些都是…...
【数据结构】单链表
链表1.为什么存在链表2.链表的概念3.单链表的实现4.测试1.为什么存在链表 我们在学习顺序表的时候,了解到顺序表有一定的缺陷:(1)在中间插入数据和删除数据需要挪动数据,时间复杂度是O(N)&…...
Windows 右键菜单扩展容器 [开源]
今天给大家分享一个我做的小工具,可以自定义扩展右键菜单的功能来提高工作效率,效果图如下: 如上图,右键菜单多了几个我自定义的菜单: 复制文件路径 复制文件夹路径 我的工具箱 <走配置文件动态创建子菜单&#x…...
爆文制造机!小红书热榜3个方向,告诉你选题诀窍!
我们知道,不论是达人创作内容,还是品牌制定Brief,都需要提前调研筛选海量信息,这时候如果有一个自己的内容素材库,就省事多啦。按照内容需求,我们可以按3个角度划分小红书内容素材:笔记类型、竞…...
【Web安全社工篇】——水坑攻击
作者名:白昼安全主页面链接: 主页传送门创作初心: 以后赚大钱座右铭: 不要让时代的悲哀成为你的悲哀专研方向: web安全,后渗透技术每日鸡汤:努力赚钱不是因为爱钱“水坑攻击”,黑客攻…...
SpringBoot 整合 MongoDB 实现数据的增删改查!
一、介绍在 MongoDB 中有三个比较重要的名词:数据库、集合、文档!数据库(Database):和关系型数据库一样,每个数据库中有自己的用户权限,不同的项目组可以使用不同的数据库集合(Colle…...
VUE前端常问面试题
文章目录一、VUE前端常问面试题二、文档下载地址一、VUE前端常问面试题 1、MVC和MVVM 区别 MVC:MVC全名是 Model View Controller,即模型-视图-控制器的缩写,一种软件设计典范。 Model(模型):是用于处理应用程序数据逻辑部分。通…...
c++中map/unordered_map的不同遍历方式以及结构化绑定
文章目录方式一:值传递遍历方式二:引用传递遍历方式三:使用迭代器遍历方式四:结构化绑定(c17特性)结构化绑定示例(1)元组tuple结构化绑定(2)结构体结构化绑定(3ÿ…...
Kafka系列之:Kraft模式
Kafka系列之:Kraft模式 一、Kraft架构二、Kafka的Kraft集群部署三、初始化集群数据目录四、创建KafkaTopic五、查看Kafka Topic六、创建生产者七、创建消费者一、Kraft架构 Kafka元数据存储在zookeeper中,运行时动态选举controller,由controller进行Kafka集群管理。Kraft模式…...
动态规划:leetcode 139.单词拆分、多重背包问题
leetcode 139.单词拆分leetcode 139.单词拆分给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。说明:拆分时可以重复使用字典中的单词。你可以假设字典中没有重复的单词。示例 1&…...
Stable Diffusion原理详解
Stable Diffusion原理详解 最近AI图像生成异常火爆,听说鹅厂都开始用AI图像生成做前期设定了,小厂更是直接用AI替代了原画师的岗位。这一张张丰富细腻、风格各异、以假乱真的AI生成图像,背后离不开Stable Diffusion算法。 Stable Diffusion…...
webpack高级配置
摇树(tree shaking) 我主要是想说摇树失败的原因(tree shaking 失败的原因),先讲下摇树本身效果 什么是摇树? 举个例子 首先 webpack.config.js配置 const webpack require("webpack");/**…...
jQuery 事件
jQuery 事件 Date: February 28, 2023 Sum: jQuery事件注册、处理、对象 目标: 能够说出4种常见的注册事件 能够说出 on 绑定事件的优势 能够说出 jQuery 事件委派的优点以及方式 能够说出绑定事件与解绑事件 jQuery 事件注册 单个时间注册 语法:…...
【批处理脚本】-2.3-解析地址命令arp
"><--点击返回「批处理BAT从入门到精通」总目录--> 共2页精讲(列举了所有arp的用法,图文并茂,通俗易懂) 目录 1 arp命令解析 1.1 询问当前协议数据,显示当前 ARP 项...
改进 YOLO V5 的密集行人检测算法研究(论文研读)——目标检测
改进 YOLO V5 的密集行人检测算法研究(2021.08)摘 要:1 YOLO V52 SENet 通道注意力机制3 改进的 YOLO V5 模型3.1 训练数据处理改进3.2 YOLO V5 网络改进3.3 损失函数改进3.3.1 使用 CIoU3.3.2 非极大值抑制改进4 研究方案与结果分析4.1 实验…...
Python - Opencv应用实例之CT图像检测边缘和内部缺陷
Python - Opencv应用实例之CT图像检测边缘和内部缺陷 将传统图像处理处理算法应用于CT图像的边缘检测和缺陷检测,想要实现效果如下: 关于图像处理算法,主要涉及的有:灰度、阈值化、边缘或角点等特征提取、灰度相似度变换,主要偏向于一些2D的几何变换、涉及图像矩阵的一些统…...
管理逻辑备数据库(Logical Standby Database)
1. SQL Apply架构概述 SQL Apply使用一组后台进程来应用来自主数据库的更改到逻辑备数据库。 在日志挖掘和应用处理中涉及到的不同的进程和它们的功能如下: 在日志挖掘过程中: 1)READER进程从归档redo日志文件或备redo日志文件中读取redo记…...
【C++】构造函数(初始化列表)、explicit、 Static成员、友元、内部类、匿名对象
构造函数(初始化列表)前提构造函数体赋值初始化列表explicit关键字static成员概念特性(重要)有元友元函数友元类内部类匿名对象构造函数(初始化列表) 前提 前面 六个默认成员对象中我们已经学过什么是构造…...
(六十)再来看看几个最常见和最基本的索引使用规则
今天我们来讲一下最常见和最基本的几个索引使用规则,也就是说,当我们建立好一个联合索引之后,我们的SQL语句要怎么写,才能让他的查询使用到我们建立好的索引呢? 下面就一起来看看,还是用之前的例子来说明。…...
机器学习与目标检测作业(数组相加:形状需要满足哪些条件)
机器学习与目标检测(数组相加:形状需要满足哪些条件)机器学习与目标检测(数组相加:形状需要满足哪些条件)一、形状相同1.1、形状相同示例程序二、符合广播机制2.1、符合广播机制的描述2.2、符合广播机制的示例程序机器学习与目标检…...
CentOS救援模式(Rescue Mode)及紧急模式(Emergency Mode)
当CentOS操作系统崩溃,无法正常启动时,可以通过救援模式或者紧急模式进行系统登录。启动CentOS, 当出现下面界面时,按e进入编辑界面。在编辑界面里,加入参数:systemd.unitrescue.target ,然后Ctrl-X启动进入…...
从面试官角度告诉你高级性能测试工程师面试必问的十大问题
目录 1、介绍下最近做过的项目,背景、预期指标、系统架构、场景设计及遇到的性能问题,定位分析及优化; 2、项目处于什么阶段适合性能测试介入,原因是什么? 3、性能测试场景设计要考虑哪些因素? 4、对于一…...
通过知识库深度了解用户的心理
自助服务知识库的价值是毋庸置疑的,如果执行得当,可以帮助减少客户服务团队的工作量,仅仅编写内容和发布是不够的,需要知道知识库对客户来说是否有用,需要了解客户获得的反馈,如果你正确的使用知识库软件&a…...
HiveSQL一天一个小技巧:如何将分组内数据填充完整?
0 需求1 需求分析需求分析:需求中需要求出分组中按成绩排名取倒数第二的值作为新字段,且分组内没有倒数第二条的时候取当前值。如果本题只是求分组内排序后倒数第二,则很简单,使用row_number()函数即可求出,但是本题问…...
【亲测可用】BEV Fusion (MIT) 环境配置
CUDA环境 首先我们需要打上对应版本的显卡驱动: 接下来下载CUDA包和CUDNN包: wget https://developer.download.nvidia.com/compute/cuda/11.6.2/local_installers/cuda_11.6.2_510.47.03_linux.run sudo sh cuda_11.6.2_510.47.03_linux.runwget htt…...
python可以做网站开发吗/淘宝大数据查询平台
大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答。matlab坐标轴范围的设置方法是:1、打开matlab,输入“x0:0.1:100;ysin(x)100;plot(x,y)”画出一个正弦函数图像。2、可以看…...
网络游戏中心/站长之家seo综合
先看牛逼的草图 知乎上刚看到类似的需求 Python Web导出有排版要求的PDF文件 关键技术 转载于:https://www.cnblogs.com/wancy86/p/PDFPY.html...
模板网站有利于做seo吗/百度链接收录提交入口
把握好现在,生活好现在,让自己和亲人家人朋友身边的人都快乐,憧憬未来---生活的真谛转载于:https://www.cnblogs.com/silentjesse/archive/2011/08/23/2150169.html...
个人网站icp备案网/百度一下点击搜索
题目背景 学生在我们USACO的竞赛中的得分越多我们越高兴。 我们试着设计我们的竞赛以便人们能尽可能的多得分,这需要你的帮助 题目描述 我们可以从几个种类中选取竞赛的题目,这里的一个"种类"是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同…...
如皋网站开发/网页免费制作网站
1. 问题:给定一个时间的value:1541059860000,把它转换为东八区的显示格式 let a moment(1541059860000).subtract(moment().utcOffset(), minute).add(480, minute).format(YYYY-MM-DD HH:mm);let b moment(1541059860000).utcOffset(480).format(YYYY…...
asp网站制作实例教程/如何做品牌宣传与推广
数据库之存储过程和存储函数(六) 什么是存储过程 存储过程是一组为了完成某项特定功能的SQL语句集,其实质就是一段存储在数据库中的代码。它可以由声明式的sql语句和过程式sql语句组成。 优点 1. 可以增强sql语言的功能和灵活性 2. 良好的封装性3. 高性能4. 减少…...