当前位置: 首页 > news >正文

janus-Gateway的服务端部署

janus-Gateway


需求是前后端的webRTC推拉流,但是后端用的是c++,于是使用了这个库做视频流的推送和拉取,记录踩坑过程。

如果你也需要自己部署janus的服务端并在前端拉流测试,希望对你有所帮助。


由于janus的服务器搭建需要linux环境,源码并不支持windows,因此需要准备局域网下的另一台ubantu电脑通过ssh连接。

服务端的搭建本人并不熟悉,参考的是newchenxf大佬的这篇文章,由于版本更新,有些报错的地方会在下文指正。



服务端搭建

1.下载源码

通过 github 下载源码,也可以在 官网 查看详细的api和在线demo


2.安装Janus依赖库

部分直接命令行安装,部分需要通过源码安装。

2.1 命令行安装

sudo aptitude install libmicrohttpd-dev libjansson-dev \libssl-dev libsrtp-dev libsofia-sip-ua-dev libglib2.0-dev \libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev \libconfig-dev pkg-config gengetopt libtool automake

请注意安装结果,如果有某一个安装失败,又是官网要求必须装的,则需要手动下载安装,方法参照下面的源码安装↓


2.2 源码安装libnice

libnice是一个建立ice连接必须的库,官方推荐方法依赖比较多,需要Python 3 Meson and Ninja(我直接用的第二种方法)

git clone https://gitlab.freedesktop.org/libnice/libnice
cd libnice
meson --prefix=/usr build && ninja -C build && sudo ninja -C build install

第二种方法,下载 libnice_0.1.16.orig.tar.gz,下载完后将压缩文件移动到usr/local

cd /usr/local
tar -zxvf libnice_0.1.16.orig.tar.gz
cd /usr/local/libnice_0.1.16
./configure && make && sudo make install

2.3 源码安装libwebsocket

webSocket不是必须的,但是最好装一下,因为websocket也很常用,网页Demo也会用。

下载 libwebsockets (蓝奏云)压缩包,将压缩文件移动到/usr/local

 sudo tar -zxvf libwebsockets.tar.gzcd /usr/local/libwebsocketsmkdir buildcd build/cmake ..make && make install

如无报错情况下,build/bin目录下,将生成 example类文件, 此时还需要进入example的源码目录:libwebsockets/minimal-examples-lowlevel/ws-server/minimal-ws-server二次编译。

 cd /usr/local/libwebsockets/minimal-examples-lowlevel/ws-server/minimal-ws-servercmake .make./lws-minimal-ws-server

终端执行结果应该是这样:
在这里插入图片描述

此时便可以通过本机ip7681端口(如果本机ip为1.1.1.1 则访问 1.1.1.1:7681),访问websocket网页查看服务是否启动↓(网页应该如下图所示)。
在这里插入图片描述
在编译 janus 时,janus 默认会增加对 WebSocket 的集成,或者通过增加编译参数 –enable-websockets 打开 WebSocket 开关。


2.4 源码安装libsrtp

这一个不是绝对必须的,因为前面通过命令行也安装了libsrtp-dev。事实上运行起demo是没有问题的,如果只跑demo可以跳过。只不过Janus官网建议,如果你的libsrtp版本低于1.5,建议升级到最新的,比如2.2,因为之前的版本可能有bug,会引入问题。

sudo apt-get install libsrtp-dev

如果是按步骤来,会提示已经安装过了(安装的是老版本,终端执行结果应该是这样)

Reading package lists... Done
Building dependency tree       
Reading state information... Done
Note, selecting 'libsrtp0-dev' instead of 'libsrtp-dev'
libsrtp0-dev is already the newest version (1.4.5~20130609~dfsg-2ubuntu1).

安装新版本,更新到2.2后,不需要pkg-config来修改版本,因为这个库名字和老版本不一样,叫做libsrtp2,不会冲突。如果有libsrtp2Janus会自动选这个库。

wget https://github.com/cisco/libsrtp/archive/v2.2.0.tar.gz
tar xfv v2.2.0.tar.gz
cd libsrtp-2.2.0
./configure --prefix=/usr --enable-openssl
make shared_library && sudo make install

2.5 源码安装usrsctp

这个也是非必须,如果要起Data Channel才需要,简单来说就是推拉流运行起来后,如果需要文本互相发送会用到。

装好以后,编译janus会默认启用他

git clone https://github.com/sctplab/usrsctp
cd usrsctp
./bootstrap
./configure --prefix=/usr --disable-programs --disable-inet --disable-inet6
make && sudo make install



3. 编译Janus

依赖都准备好了,可以编译janus

git clone https://github.com/meetecho/janus-gateway.git
sh autogen.sh
./configure --prefix=/opt/janus --enable-websockets
make
sudo make install

如果还想生成帮助文档,可以加一个–enable-docs,编译前,还需要装一下doxygen等辅助工具(可选)

aptitude install doxygen graphviz

执行configure

./configure

此时终端的执行结果应该为 ↓ ,没有配置Data Channel不影响demo运行

Compiler:                  gcc
libsrtp version:           2.x
SSL/crypto library:        OpenSSL
DTLS set-timeout:          not available
Mutex implementation:      GMutex (native futex on Linux)
DataChannels support:      yes
Recordings post-processor: no
TURN REST API client:      yes
Doxygen documentation:     no
Transports:REST (HTTP/HTTPS):     yesWebSockets:            yesRabbitMQ:              noMQTT:                  noUnix Sockets:          yesNanomsg:               no
Plugins:Echo Test:             yesStreaming:             yesVideo Call:            yesSIP Gateway:           yesNoSIP (RTP Bridge):    yesAudio Bridge:          yesVideo Room:            yesVoice Mail:            yesRecord&Play:           yesText Room:             yesLua Interpreter:       noDuktape Interpreter:   no
Event handlers:Sample event handler:  yesWebSocket ev. handler: yesRabbitMQ event handler:noMQTT event handler:    noNanomsg event handler: noGELF event handler:    yes
External loggers:JSON file logger:      no
JavaScript modules:        no

4. 运行Janus

Janus运行需要参数配置,安装目录(/opt/janus/etc/janus/)已经有配置的sample

进入以后,发现有很多jcfg.sample文件,比如janus.jcfg.sample janus.transport.websockets.jcfg.sample

需要把这些都复制成可被读取的文件(即去掉.sample结尾),要全部复制。

所以需要复制第一个文件名称,替换命令里面的名字,执行命令,再复制第二个文件名称,替换命令里面的名字,再执行…直到复制完文件夹下的十几个.sample文件

文件名均为 xxx.jcfg.sample 后面的文件名就是xxx.jcfg(即 前面的删掉 .sample

sudo cp -rf 文件名 文件名(去掉最后的sample)

所以假如复制的文件名为 janus.jcfg.sample ,命令就是 ↓ 这条

sudo cp -rf janus.jcfg.sample janus.jcfg

全部完成后,需要生成ssl证书。


5. ssl证书

Janus默认的配置,是没有enable SSL的,意味着,httpswss不支持。而有些浏览器,要拉起摄像头,必须是加密协议下的。所以,如果你Demo运行不起来,可以服务端配置一下SSL

cd ~
mkdir ssl
cd ssl
# Gen ssl certs:
openssl req -new -newkey rsa:4096 -nodes -keyout key.pem -out cert.csr
openssl x509 -req -sha256 -days 365 -in cert.csr -signkey key.pem -out cert.pem
chmod 600 cert.csr
chmod 600 cert.pem
chmod 600 key.pem

生成的过程中会让你输入信息,姓名密码之类的,直接enter跳过即可,不能跳过的随便填。

注意:如果填密码的时候是输入了密码的,那么要记住它,改配置文件需要填密码。

这个是私人证书,访问时会报不安全,如果公司性质的需要另外申请。


5.1 配置文件修改ssl信息

打开刚才改.sample文件的目录 /opt/janus/etc/janus/

使用文本编辑janus.jcfg,注意这里的证书路径需要在文件夹中找一下,不同的用户生成不同

比如我的是root用户,我的ssl证书生成在/root/ssl, 并且由于我填密码时enter跳过了,所以pwd为空

注意前面的#要去掉,填完之后前面三行复制即可

certificates: {cert_pem = "/root/ssl/cert.pem"cert_key = "/root/ssl/key.pem"cert_pwd = ""#dtls_accept_selfsigned = false#dtls_ciphers = "your-desired-openssl-ciphers"#rsa_private_key = false
}

再编辑同目录下的janus.transport.http.jcfg,把https改为true,去掉secure_port的注释#certificates配置跟上面一样

general: {#events = true                                  # Whether to notify event handlers about transport events (default=true)json = "indented"                               # Whether the JSON messages should be indented (default),# plain (no indentation) or compact (no indentation and no spaces)base_path = "/janus"                    # Base path to bind to in the web server (plain HTTP only)http = true                                             # Whether to enable the plain HTTP interfaceport = 8088                                             # Web server HTTP porthttps = true                                    # Whether to enable HTTPS (default=false)secure_port = 8089                              # Web server HTTPS port, if enabled}certificates: {cert_pem = "/root/ssl/cert.pem"cert_key = "/root/ssl/key.pem"cert_pwd = ""#ciphers = "PFS:-VERS-TLS1.0:-VERS-TLS1.1:-3DES-CBC:-ARCFOUR-128"
}

再编辑同目录下的janus.transport.websockets.jcfg,把wss改为true,去掉wss_port的注释#certificates配置跟上面一样

general: {ws = true                                               # Whether to enable the WebSockets APIws_port = 8188                                  # WebSockets server port#ws_interface = "eth0"                  # Whether we should bind this server to a specific interface only#ws_ip = "192.168.0.1"                  # Whether we should bind this server to a specific IP address onlywss = true                                              # Whether to enable secure WebSocketswss_port = 8989                         # WebSockets server secure port, if enabled
}certificates: {cert_pem = "/root/ssl/cert.pem"cert_key = "/root/ssl/key.pem"cert_pwd = ""#ciphers = "PFS:-VERS-TLS1.0:-VERS-TLS1.1:-3DES-CBC:-ARCFOUR-128"
}

注意:改的这三个文件均为.jcfg结尾的文件(刚才4. 运行Janus时转化的),如果改到了.sample结尾的文件,配置就不生效了。



6. 运行

所有的准备和配置都搞定了,接下来就是启动服务。

/opt/janus/bin/janus --debug-level=7

终端的执行结果

libwebsockets logging: 0
Websockets server started (port 8188)...
Using certificates:/root/ssl/cert.pem/root/ssl/key.pem
Secure Websockets server started (port 8989)...
Admin Websockets server disabled
Secure Admin Websockets server disabledJANUS WebSockets transport plugin initialized!    <---JANUS websocket初始化Version: 1 (0.0.1)[janus.transport.websockets] JANUS WebSockets transport pluginThis transport plugin adds WebSockets support to the Janus API via libwebsockets.Plugin API version: 8Janus API: enabledAdmin API: disabledWebSockets thread started     <---JANUS websocket启动成功

如果是这样说明启动成功了!

检查运行端口

lsof -i | grep janus

终端运行结果

janus   32306 chenxiaofeng    5u  IPv6  887081      0t0  UDP *:rfe 
janus   32306 chenxiaofeng    6u  IPv6  887082      0t0  UDP *:5004 
janus   32306 chenxiaofeng   13u  IPv6  884414      0t0  TCP *:omniorb (LISTEN)
janus   32306 chenxiaofeng   16u  IPv6  884415      0t0  TCP *:8089 (LISTEN)
janus   32306 chenxiaofeng   21u  IPv4  884417      0t0  TCP *:8188 (LISTEN)
janus   32306 chenxiaofeng   24u  IPv4  884418      0t0  TCP *:8989 (LISTEN)

8089https8188ws8989wss(websocket secure)

STUN server警告

[WARN] Janus is deployed on a private address (192.168.1.107) but you didn't specify any STUN server! Expect trouble if this is supposed to work over the internet and not just in a LAN...

启动的时候还会有STUN警告信息,对于我们运行Demo是没问题的。因为一般运行在局域网,没有P2P打通的需要。

但如果你是正式使用则需要解决这个警告,需要安装STUN server, 也就是coturn

打开/opt/janus/etc/janus/janus.jcfg,修改配置

nat: {#stun_server = "stun.voip.eutelia.it"#stun_port = 3478
..#turn_server = "myturnserver.com"#turn_port = 3478#turn_type = "udp"#turn_user = "myuser"#turn_pwd = "mypassword"

安装coturn

git clone https://github.com/coturn/coturn
cd coturn
./configure 
make 
sudo make install

安装coturn服务器不一定和janus一起,也可以一起。总之装好后把信息写到janus的配置文件中。



7. 在线demo


我们可以在服务端通过http-serverdemohtml发布到本地服务器,方便我们使用

首先我们需要在ubantu中安装nodejs,参考ubantu下nodejs安装

安装完成后,全局安装http-server

npm -g install http-server

安装好以后,进入janus源码的html目录。

cd /usr/local/janus-gateway/html

然后执行http-server

注意:webdemo,默认都是走http的,这在有些环境下的浏览器,是无法拉起摄像头的(权限问题),推荐使用https

这里/root/ssl是ssl证书生成的目录

http-server --ssl --cert /root/ssl/cert.pem --key /root/ssl/key.pem

启动后访问即可,由于是私人ssl证书,浏览器会有警告,继续前往即可。
在这里插入图片描述

此时就可以访问在线的demo了。


服务端的部署到这里就结束了,接下来是前端模块

相关文章:

janus-Gateway的服务端部署

janus-Gateway 需求是前后端的webRTC推拉流&#xff0c;但是后端用的是c&#xff0c;于是使用了这个库做视频流的推送和拉取&#xff0c;记录踩坑过程。 如果你也需要自己部署janus的服务端并在前端拉流测试&#xff0c;希望对你有所帮助。 由于janus的服务器搭建需要linux环境…...

力扣简单1道_两数之和

两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。你可以按任意顺序…...

从vue3速学react

单位老项目vue2,新项目vue3,业务已经熟练使用vue了&#xff0c;空余时间自学下react&#xff0c;写个博客记录下 react没有双向绑定&#xff0c;用的是jsx语法&#xff0c;useState后面是初始化值&#xff0c;需要改变data的时候&#xff0c;需要用定义的setXXX来改变XXX值&am…...

Kotlin 协程 CoroutineScope

协程定义&#xff1a; 19年官方是这样说的&#xff1a;协程是轻量级的线程&#xff0c;协程就是 Kotlin 提供的一套线程封装的 API&#xff1b; 现在官方是这样说的&#xff1a;协程是一种并发设计模式&#xff1b; 协程作用&#xff1a; 1.处理耗时任务&#xff1b; 2.保…...

如何构建适合自己的DevOps软件测试改进方案

​目录 DevOps成熟度模型分析 构建适合企业自身性能的测试过程改进框架 资料获取方法 根据2022年的DevOps全球调查报告显示&#xff0c;主流软件企业采用或部分采用DevOps且已获得良好成效的占比已达70%&#xff0c;DevOps俨然成为当下软件开发研究的重要方向。 测试作为软…...

PHP关于字符串的各类处理方法

判断字符串是否以指定子串开头或结尾 function startsWith($str, $prefix) {return stripos($str, $prefix) 0; }function endsWith($str, $suffix) {return substr_compare($str, $suffix, -strlen($suffix)) 0; }// 示例用法 $text "hello world"; $result st…...

Springboot+MyBatis使用

目录 依赖 配置信息 xml文件 mapper接口 打印日志 分页查询 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency><dependency><groupId>my…...

WebSocketServer的使用(@ServerEndpoint)

前端代码 function initWebSocket() {if (typeof WebSocket "undefined") {console.log("您的浏览器不支持WebSocket");} else {console.log("您的浏览器支持WebSocket");//实现化WebSocket对象&#xff0c;指定要连接的服务器地址与端口 建立连…...

【yolov8系列】将yolov8-seg 模型部署到瑞芯微RK3566上

前言 之前记录过【yolov5系列】将模型部署到瑞芯微RK3566上&#xff0c;整体比较流畅&#xff0c;记录了onnx转rknn的相关环境配置&#xff0c;使用的rk版本为rknn-toolkit2-v1.4.0。当前库已经更新为1.5&#xff0c;这里还是沿用1.4的版本进行记录。本篇博客是在上篇博客&…...

Java类的默认构造函数

什么情况下存在默认构造函数 说明 如果一个Java类没有显式包含构造函数的声明&#xff0c;那么隐含着有一个默认构造函数。 示例 定义一个类B&#xff0c;没有显式声明构造函数&#xff0c;所以存在一个默认构造函数&#xff1a; package com.thb;public class B {public …...

华为openGauss数据库入门 - gsql用法

目录 1.1 gsql的语法 1.2 gsql常用选项 1.2.1 最常用的必要选项 1.2.2 -r选项 1.2.3 -E选项 1.2.4 -t选项 1.2.5 -A选项 1.2.6 -v选项 1.2.7 -c选项 1.2.8 -f选项 1.2.9 -q选项 1.3 gsql的元命令 1.3.1 \l命令 1.3.2 \du命令和\dg命令 1.3.3 \db命令 1.3.4 \d…...

The Sandbox 重新上线,带来全新体验!

在经历了一个充满史诗般新回忆的全力开局后&#xff0c;我们短暂休息了片刻&#xff0c;为玩家准备了全新的、惊心动魄的游戏活动。 我们已经完成了功能的微调&#xff0c;准备将您的游戏体验提升到一个全新高度&#xff01; 想知道我们正在做什么吗&#xff1f;现在还无法公开…...

动态内存管理面试题

动态内存管理面试题 文章目录 动态内存管理面试题一、第一题此代码存在的问题运行结果分析原因修改 二、第二题此代码存在的问题运行结果分析原因修改 一、第一题 代码如下&#xff08;示例&#xff09;&#xff1a; #include<stdio.h> #include<string.h> #incl…...

树莓派外设开发编程

目录 一、树莓派的接口&#xff1a;驱动 二、wiringPi 库 1.在使用wiringPi库的时候&#xff0c;需要包含头文件 2.wiringPi库API 一、树莓派的接口&#xff1a;驱动 IO口&#xff1a;input output 对于主控来说 Input &#xff1a;人体、烟雾、振动 output: 继电…...

Java从入门到精通(一)

Java从入门到精通&#xff08;一&#xff09; 前言 温故而知新&#xff0c;闲着没事干&#xff0c;准备将Java编程语言的知识点从头梳理一遍&#xff0c;整理成笔记&#xff0c;逐篇发布。 部分图片素材来源与B站“黑马程序员”的课程。 一 Java背景 Java是1995年 由Sun公司…...

Electron从构建到打包程exe应用

Electron从构建到打包程exe应用 Electron文档搭建网页装载到 BrowserWindow中定义全局对象进程之间通信渲染器进程到主进程&#xff08;单向&#xff09;渲染器进程到主进程&#xff08;双向&#xff09;主进程到渲染器进程 打开调试器打包应用程序对代码进行签名 Electron文档…...

3分钟学会设计模式 -- 单例模式

►单例模式 ►使用场景 在编写软件时&#xff0c;对于某些类来说&#xff0c;只有一个实例很重要。例如&#xff0c;一个系统中可以存在多个打印任务&#xff0c;但是只能有一个正在工作的任务&#xff1b;一个系统中可以多次查询数据库&#xff0c;但是只需要一个连接&#x…...

《面试1v1》Kafka与传统消息系统区别

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…...

【算法第十三天7.27】平衡二叉树,二叉树所有路径,左叶子之和

链接力扣110-平衡二叉树 思路 1、左右子树高度差不超过1 2、左子树、右子树均为平衡二叉树 3、需要获得树高&#xff0c;如果不是平衡的就返回-1&#xff1b;如果是平衡&#xff0c;就返回对应的高 class Solution {public boolean isBalanced(TreeNode root) {return getH…...

arm架构cloudstack的agent报错No more available PCI slots如何解决

当ARM架构的CloudStack代理报错"No more available PCI slots"时&#xff0c;这通常表示ARM实例已用尽可用的PCI插槽。PCI插槽用于连接网络接口卡&#xff08;NIC&#xff09;和其他扩展设备到虚拟机实例。 解决此问题的方法可以考虑以下几点&#xff1a; 调整实例的…...

day43-Feedback Ui Design(反馈ui设计)

50 天学习 50 个项目 - HTMLCSS and JavaScript day43-Feedback Ui Design&#xff08;反馈ui设计&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport&q…...

TypeScript基础篇 - TS日常类型 上篇

目录 TS的常见用法介绍 example01.ts 逃避类型检查&#xff1a;any 思考一下~&#xff1a;不知道类型 类型标注 函数&#xff08;参数和返回值&#xff09; 匿名函数 TS如何知道匿名函数的类型&#xff1f; TS是一种标注式语言&#xff0c;不侵入JS的设计 TS的常见用法…...

量化交易——python数据分析及可视化

该项目分为两个部分&#xff1a;一是数据计算&#xff0c;二是可视化&#xff0c;三是MACD策略 一、计算MACD 1、数据部分 数据来源&#xff1a;tushare 数据字段包含&#xff1a;日期&#xff0c;开盘价&#xff0c;收盘价&#xff0c;最低价&#xff0c;最高价&#xff0c…...

微服务网关

1.网关是如何演化来的&#xff0c;在微服务中有什么作用&#xff1f; 随着单体架构转化为微服务架构的时候&#xff0c;由一个后台服务由一个单一的服务变成了多个微服务&#xff0c;前端应用需要调用多个服务的接口&#xff0c;为了解决这个问题&#xff0c;网关就产生了。网…...

【打卡】Datawhale暑期实训ML赛事

文章目录 赛题描述任务要求数据集介绍评估指标 赛题分析基于LightGBM模型Baseline详解改进baseline早停法添加特征 赛题描述 赛事地址&#xff1a;科大讯飞锂离子电池生产参数调控及生产温度预测挑战赛 任务要求 初赛任务&#xff1a;初赛提供了电炉17个温区的实际生产数据&…...

【python脚本】python实现:目标检测裁剪图片样本,根据类标签文件进行裁剪保存

python实现&#xff1a;目标检测裁剪图片样本&#xff0c;根据类标签文件进行裁剪保存 我在进行目标检测时候&#xff0c;比如红绿灯检测&#xff0c;目标区域很小&#xff0c;样本杂乱。 想要筛选错误样本的话&#xff0c;很困难。可以把目标区域裁剪出来。人大脑处理对于这…...

Mac 终端美化显示

Linux 也可安装 Zsh 后使用此套配置。 1. 安装 Oh My Zsh sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"2. 更换主题&#xff0c;修改文件&#xff1a;~/.zshrc&#xff0c;原内容&#xff1a; ZSH_THEME&quo…...

信息安全:密码学基本理论.

信息安全&#xff1a;密码学基本理论. 密码学是研究编制密码和破译密码的技术科学。研究密码变化的客观规律&#xff0c;应用于编制密码以保守通信秘密的&#xff0c;称为编码学&#xff1b;应用于破译密码以获取通信情报的&#xff0c;称为破译学&#xff0c;总称密码学. 目录…...

【linux升级ssh】 利用rpmbuild工具对ssh打包为rpm包进场安装升级

制作rpm包 rpmbuild命令用于创建软件的二进制包和源代码包。 官方文档&#xff1a;rpm.org - RPM Reference Manual rpmbuild 中文手册&#xff1a;rpmbuild 中文手册 [金步国] 使用rpmbuild将tar包打成rpm包 RPM打包使用的是rpmbuild命令&#xff0c;这个命令来自rpm-buil…...

UCloud上线可商用LLaMA2镜像,助力AGI应用发展

随着人工智能技术的快速发展&#xff0c;大模型应用在自然语言处理、图像识别、智能交互等领域展现出了巨大的潜力&#xff0c;为企业带来了更多创新和商机。众多企业纷纷将大模型应用于产品开发和业务优化中&#xff0c;希望通过提升智能化水平和用户体验来赢得竞争优势。近日…...