【音视频 | opus】opus编解码库(opus-1.4)详细介绍以及使用——附带解码示例代码

news2024/11/27 20:29:01

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍opus编解码库(opus-1.4)的使用🍭
😎金句分享😎:🍭🍭

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、opus 库编译
    • ✨2.1 编译环境
    • ✨2.2 opus-1.4 库编译
    • ✨2.3 opus_demo 使用
  • 🎄三、opus-1.4 库函数介绍
    • ✨3.1 opus 编码相关库函数
    • ✨3.2 opus 解码相关库函数
  • 🎄四、opus 解码流程及源码
  • 🎄五、总结


在这里插入图片描述

🎄一、概述

Opus是一款完全开放、免版税、高度通用的音频编解码器。Opus在互联网上的交互式语音和音乐传输方面无与伦比,但也适用于存储和流媒体应用。它被互联网工程任务组(IETF)标准化为 RFC 6716,它结合了Skype的SILK编解码器和Xiph.Org的CELT编解码器的技术。

Opus可以处理广泛的音频应用程序,包括IP语音、视频会议、游戏内聊天,甚至远程现场音乐表演。它可以从低比特率的窄带语音扩展到非常高质量的立体声音乐。支持的功能包括:

  • 比特率从6 kb/s到510 kb/s

  • 采样率从8kHz(窄带)到48kHz(全频带)

  • 帧大小从2.5毫秒到60毫秒

  • 支持恒定比特率(CBR)和可变比特率(VBR)

  • 从窄带到全频带的音频带宽

  • 支持语音和音乐

  • 支持单声道和立体声

  • 最多支持255个通道(多流帧)

  • 可动态调整的比特率、音频带宽和帧大小

  • 良好的丢失鲁棒性和丢包隐藏性(PLC)

  • 浮点和定点实现

需要了解更多opus的内容,可以到opus官网:https://www.opus-codec.org 。

在这里插入图片描述

🎄二、opus 库编译

下载 opus 库,点击链接:https://www.opus-codec.org/downloads/,本文下载的是opus-1.4.tar.gz

✨2.1 编译环境

$ uname -a
Linux ubuntu 4.4.0-128-generic #154~14.04.1-Ubuntu SMP Fri May 25 14:58:51 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.5 LTS
Release:        14.04
Codename:       trusty

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.4' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4) 

✨2.2 opus-1.4 库编译

tar zxvf opus-1.4.tar.gz
cd opus-1.4/
./configure --prefix=`pwd`/result_gcc
make && make install

✨2.3 opus_demo 使用

opus-1.4 库编译之后,在opus-1.4/.libs目录会出现一个名为 opus_demo 的可执行程序,该程序的源码在opus-1.4/src/opus_demo.c 路径。

opus_demo 是演示 opus-1.4 库 API 函数的一个demo,功能是将PCM音频文件编码成opus数据,或者将opus数据解码成PCM文件。注意,它编码出来的opus并不能直接使用VLC播放,因为只是一些数据包,并没有封装成VLC可识别的音频文件。

使用下面命令进行opus编码,这里将48000Hz-16bit-2ch-ChengDu.pcm作为输入文件,输入采样率48000、通道数2、510000的比特流,最后编码输出 opus_demo.out 文件:

./opus_demo -e audio 48000 2 510000 48000Hz-16bit-2ch-ChengDu.pcm opus_demo.out

在这里插入图片描述

使用下面命令可以将上面的输出文件opus_demo.out还原回PCM音频文件opus_demo_dec.pcm

./opus_demo -d 48000 2 opus_demo.out opus_demo_dec.pcm

在这里插入图片描述

在这里插入图片描述

🎄三、opus-1.4 库函数介绍

opus-1.4 的库函数都在 opus.h 头文件中,库函数总共分为下面6个部分,在头文件搜索defgroup可以依次跳到各个部分开头,现在只有前三个部分。:

  • opus_encoder :编码相关
  • opus_decoder :解码相关
  • opus_repacketizer :重新打包器相关
  • opus_multistream
  • opus_libinfo
  • opus_custom

每个API函数在 opus.h 头文件中都要详细的英文解释,部分函数还有实例代码,如果还有不懂,就看opus_demo.c的代码,也有助于清楚怎么使用。

下面摘取个别常用的库函数:

✨3.1 opus 编码相关库函数

int opus_encoder_get_size(int channels);
功能:获取一个 OpusEncoder 结构体的大小
参数:
	channels:通道数,必须12;
返回值:以字节为单位的大小

//---------------------------------------
OpusEncoder *opus_encoder_create(opus_int32 Fs, int channels, int application, int *error);
功能:分配并初始化编码器状态
参数:
	Fs:采样率,必须是 8000, 12000, 16000, 24000, 48000 之一;
	channels:声道数,12;
	application:OPUS_APPLICATION_VOIP 或 OPUS_APPLICATION_AUDIO 或 PUS_APPLICATION_RESTRICTED_LOWDELAY
	error:输出参数,错误码;
返回值:成功返回编码器指针,失败返回NULL//---------------------------------------
int opus_encoder_init(OpusEncoder *st, opus_int32 Fs, int channels, int application);
功能:初始化之前分配的编码器状态;

//---------------------------------------
opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);
功能:编码一个Opus帧;

//---------------------------------------
opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);
功能:从浮点输入编码一个Opus帧

//---------------------------------------
void opus_encoder_destroy(OpusEncoder *st);
功能:释放 opus_encoder_create 分配的 OpusEncoder 指针;

//---------------------------------------
int opus_encoder_ctl(OpusEncoder *st, int request, ...);
功能:在Opus编码器上执行控制功能。

✨3.2 opus 解码相关库函数

int opus_decoder_get_size(int channels);
功能:获取一个 OpusDecoder 结构体的大小
参数:
	channels:通道数,必须12;
返回值:以字节为单位的大小

//---------------------------------------
OpusDecoder *opus_decoder_create(opus_int32 Fs,int channels,int *error);
功能:分配并初始化解码器状态
参数:
	Fs:采样率,必须是 8000, 12000, 16000, 24000, 48000 之一;
	channels:声道数,12;
	error:输出参数,错误码;
返回值:成功返回解码器指针,失败返回NULL//---------------------------------------
int opus_decoder_init(OpusDecoder *st, opus_int32 Fs, int channels);
功能:初始化之前分配的解码器状态;

//---------------------------------------
int opus_decode(OpusDecoder *st, const unsigned char *data, opus_int32 len,
    opus_int16 *pcm, int frame_size, int decode_fec);
功能:解码一个Opus数据包。

//---------------------------------------
int opus_decode_float(OpusDecoder *st, const unsigned char *data, opus_int32 len,
    float *pcm, int frame_size, int decode_fec);
功能:用浮点输出解码一个Opus数据包。

//---------------------------------------
int opus_decoder_ctl(OpusDecoder *st, int request, ...);
功能:在Opus解码器上执行控制功能。

//---------------------------------------
void opus_decoder_destroy(OpusDecoder *st);
功能:释放 opus_decoder_create分配的 OpusDecoder 指针;

//---------------------------------------
int opus_packet_parse(const unsigned char *data,opus_int32 len, unsigned char *out_toc,
   const unsigned char *frames[48],opus_int16 size[48],int *payload_offset);

//---------------------------------------  
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_bandwidth(const unsigned char *data) OPUS_ARG_NONNULL(1);

/** Gets the number of samples per frame from an Opus packet.
  * @param [in] data <tt>char*</tt>: Opus packet.
  *                                  This must contain at least one byte of
  *                                  data.
  * @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
  *                                     This must be a multiple of 400, or
  *                                     inaccurate results will be returned.
  * @returns Number of samples per frame.
  */
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) OPUS_ARG_NONNULL(1);

/** Gets the number of channels from an Opus packet.
  * @param [in] data <tt>char*</tt>: Opus packet
  * @returns Number of channels
  * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
  */
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_channels(const unsigned char *data) OPUS_ARG_NONNULL(1);

/** Gets the number of frames in an Opus packet.
  * @param [in] packet <tt>char*</tt>: Opus packet
  * @param [in] len <tt>opus_int32</tt>: Length of packet
  * @returns Number of frames
  * @retval OPUS_BAD_ARG Insufficient data was passed to the function
  * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
  */
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1);

/** Gets the number of samples of an Opus packet.
  * @param [in] packet <tt>char*</tt>: Opus packet
  * @param [in] len <tt>opus_int32</tt>: Length of packet
  * @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
  *                                     This must be a multiple of 400, or
  *                                     inaccurate results will be returned.
  * @returns Number of samples
  * @retval OPUS_BAD_ARG Insufficient data was passed to the function
  * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
  */
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1);

/** Gets the number of samples of an Opus packet.
  * @param [in] dec <tt>OpusDecoder*</tt>: Decoder state
  * @param [in] packet <tt>char*</tt>: Opus packet
  * @param [in] len <tt>opus_int32</tt>: Length of packet
  * @returns Number of samples
  * @retval OPUS_BAD_ARG Insufficient data was passed to the function
  * @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
  */
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);

/** Applies soft-clipping to bring a float signal within the [-1,1] range. If
  * the signal is already in that range, nothing is done. If there are values
  * outside of [-1,1], then the signal is clipped as smoothly as possible to
  * both fit in the range and avoid creating excessive distortion in the
  * process.
  * @param [in,out] pcm <tt>float*</tt>: Input PCM and modified PCM
  * @param [in] frame_size <tt>int</tt> Number of samples per channel to process
  * @param [in] channels <tt>int</tt>: Number of channels
  * @param [in,out] softclip_mem <tt>float*</tt>: State memory for the soft clipping process (one float per channel, initialized to zero)
  */
OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem);

在这里插入图片描述

🎄四、opus 解码流程及源码

opus 解码流程:
1、创建 opus 解码器:opus_decoder_create;
2、配置 opus 解码器:opus_decoder_ctl;
3、取出 opus 包;
4、使用 opus_decode 函数对 opus 包解码;

示例代码:

/**
 * @file opusDec.h
 * @author wkd_007
 * @date 2023-10-27 15:47:38
 */
#ifndef	__OPUS_DEC_H__
#define __OPUS_DEC_H__

#include "opus/opus.h"

#define MAX_OPUS_DEC_FRAME  48000   // opus解码最大采样点个数,如果个数时间小于120ms,可能停止解码,这里设置1000ms的个数
#define OPUS_DEC_CHANNELS   2
class COpusDec
{
public:
    COpusDec();
    ~COpusDec();

    int CreateOpusDecoder(int sampleRate, int channels);
    int OpusDecode(unsigned char* in_data, int in_len, short *out_buf);

private:
    OpusDecoder *decoder;   // opus 解码器指针
    int sample_rate;        // 采样率
    int channel_num;        // 通道数
};
#endif// __OPUS_DEC_H__
/**
 * @file 	opusDec.cpp
 * @author 	wkd_007
 * @brief 	opus 解码
 * @date 	2023-10-27 15:38:43
 */

#include <stdio.h>
#include "opusDec.h"

COpusDec::COpusDec()
{
	decoder = NULL;
	sample_rate = 0;
	channel_num = 0;
}

COpusDec::~COpusDec()
{
	sample_rate = 0;
	channel_num = 0;
	if(decoder)
	{
		opus_decoder_destroy(decoder);
		decoder = NULL;
	}
}

int COpusDec::CreateOpusDecoder(int sampleRate, int channels)
{
	int err = 0;
    decoder = opus_decoder_create(sampleRate, channels, &err);
	if(err != OPUS_OK || decoder == NULL)
    {
        printf("[%s %d]err=%d decoderIsNULL=%d\n",__FILE__,__LINE__,err, decoder == NULL);
        return -1;
	}
    opus_decoder_ctl(decoder, OPUS_SET_LSB_DEPTH(16));
    sample_rate = sampleRate;
    channel_num = channels;
	return err;
}

int COpusDec::OpusDecode(unsigned char* in_data, int in_len, short *out_buf)
{
	if(decoder == NULL)
		return -1;
    int frame_size = opus_decode(decoder, in_data, in_len, out_buf, MAX_OPUS_DEC_FRAME, 0);
    if (frame_size < 0)
    {
       printf("[%s %d] frame_size=%d in_len=%d\n",__FILE__,__LINE__, frame_size,in_len);
       return frame_size;
    }
    return frame_size;
}

在这里插入图片描述

🎄五、总结

本文介绍了opus-1.4 库的下载、编译,demo程序的使用,以及简单介绍常用的opus编解码库函数 ,最后给出opus解码示例代码;

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
从RTP包中分析OPUS码流:https://blog.csdn.net/hclbeloved/article/details/115528990
libopus 实现pcm 编码到opus:https://blog.csdn.net/sinat_27720649/article/details/126530085

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动曝光算法(第二讲)

序言 第一章说了&#xff0c;自动曝光算法的目的&#xff1a;已知当前raw图亮度、当前曝光时间、当前增益和目标亮度&#xff0c;当环境光发生变化的时候&#xff0c;是通过控制增益、曝光时间和光圈使raw图的亮度&#xff0c;保持在目标亮度附近。本章想讲一下目标亮度的相关…

修改c盘用户名后的注意

文章目录 修改C盘及相关配置形成原因修改用户名和文件夹名解决软件双击无法打开问题修改其它相关注册表修改环境变量的内容 修改C盘及相关配置 形成原因 曾修改过文件夹&#xff0c;具体哪个文件夹&#xff0c;待会会有所参透 由于我感觉自己的用户文件夹不是太好看&#xff…

记录--这个前端Api管理方案会更好?

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 简介 大家好&#xff0c;前端小白一枚&#xff0c;目前接触后台管理系统比较多&#xff0c;经常遇到不同对象的增删改查的接口&#xff0c;如何对Api进行一个有比较好的管理是个问题。在学习偏函数的时…

[概述] 点云滤波器

拓扑结构 点云是一种三维数据&#xff0c;有几种方法可以描述其空间结构&#xff0c;以利于展开搜索 https://blog.csdn.net/weixin_45824067/article/details/131317939 KD树 头文件&#xff1a;pcl/kdtree/kdtree_flann.h 函数&#xff1a;pcl::KdTreeFLANN 作用&#xff1a…

压缩软件 7-Zip VS WinZips?

7-zip在联想应用商店给强烈推荐&#xff1f; 要说它好用还行&#xff0c;但每次压缩都显示网络连接失败等异常广告信息。 相反好用的7-ZIP必须鼠标点击右键点击更多才能够看到&#xff0c;这次更新体验也太差了吧&#xff1f; 用户放在第一位&#xff1f; 要不是更新后一直推…

编译原理学习:随机生成算术表达式

最近用Python写了一个随机向右生成数学表达式的算法。如下图所示&#xff0c;点一下运行就能随机生成一个二叉树形式的算术表达式。这个树形图是用“graphviz”画的&#xff0c;完全是它自动布局画出来的&#xff0c;画的还挺不错的。代码在&#xff1a;becomequantum (becomeq…

Ubuntu 系统内核 kernel panic

Ubuntu 系统内核 kernel panic 不能进入系统&#xff1a;报错end kernel panic -not syncing: attemped to kill init! exit code 0x00000100 系统启动的时候&#xff0c;按下‘e’键进入grub编辑界面&#xff0c;编辑grub菜单&#xff0c;选择“kernel /vmlinuz-XXXXro root…

【LeetCode:117. 填充每个节点的下一个右侧节点指针 II | DFS | BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【强化学习】15 —— TRPO(Trust Region Policy Optimization)

文章目录 前言TRPO特点策略梯度的优化目标使用重要性采样忽略状态分布的差异约束策略的变化近似求解线性搜索算法伪代码广义优势估计代码实践离散动作空间连续动作空间 参考 前言 之前介绍的基于策略的方法包括策略梯度算法和 Actor-Critic 算法。这些方法虽然简单、直观&…

银行和金融企业为何青睐这8款项目管理工具

银行、金融行业中主流的8款项目管理系统&#xff1a;1.PingCode&#xff1b;2.Worktile&#xff1b;3.Microsoft Project&#xff1b;4.Jira by Atlassian&#xff1b;5.Asana&#xff1b;6.Trello&#xff1b;7.Wrike&#xff1b;8.Teambition。 银行和金融性质的公司在项目管…

【C++】多态 ⑪ ( 纯虚函数和抽象类 | 纯虚函数语法 | 抽象类和实现 | 代码示例 )

文章目录 一、纯虚函数和抽象类1、纯虚函数2、纯虚函数语法3、抽象类和实现 二、完整代码示例 一、纯虚函数和抽象类 1、纯虚函数 纯虚函数 : 在 C 语言中 , " 纯虚函数 " 是 特殊类型的 虚函数 , " 纯虚函数 " 在 父类 中 声明 , 但是没有实现 ; 抽象类 …

计算虚拟化2——内存虚拟化

目录 物理机内存访问过程 虚拟地址VA和物理地址PA概念 MUU实现VA到PA所使用的映射表 内存虚拟化类型 内存软件辅助虚拟化 内存硬件辅助虚拟化 内存虚拟化-内存超分配 内存共享 内存置换 内存气泡 物理机内存访问过程 内存的基本知识 内存都是从物理地址0开始的&…

基于LDA主题+协同过滤+矩阵分解算法的智能电影推荐系统——机器学习算法应用(含python、JavaScript工程源码)+MovieLens数据集(二)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据爬取及处理 相关其它博客工程源代码下载其它资料下载 前言 前段时间&#xff0c;博主分享过关于一篇使用协同过滤算法进行智能电影推荐系统的博文《基于TensorFlowCNN协同过滤算法的智能电影推荐系统——深…

文献查询辅助工具,查看文献影响因子期刊,显示文献排名,翻译文献

插件工具&#xff1a;easyScholar 适配浏览器&#xff08;Edge、chrome、Firefox&#xff09;&#xff0c;本文以Edge为例&#xff1a; 1.打开Edge浏览器&#xff0c;输入&#xff1a; edge://extensions/ 2.点击获取Microsoft Edge扩展 3.搜索 easyscholar&#xff0c;然后…

Hive【Hive(八)自定义函数】

自定义函数用的最多的是单行函数&#xff0c;所以这里只介绍自定义单行函数。 Coding 导入依赖 <dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.3</version></dependency>…

路由器基础(十):防火墙配置

一、防火墙默认的区域 防火墙是基于安全区域进行工作的安全设备。 一个安全区域是若干接口所连网络的集合&#xff0c;这些网络中的用户具有相同的安全属性。 通常防火墙认为在同一安全区域内部发生的数据流动是不存在安全风险的&#xff0c;不需要实施任何安全策略。只有当不同…

代码随想录Day36 动态规划05 LeetCode T1049最后一块石头的重量II T494 目标和 T474 一和零

前言 : 动规五部曲 理论基础 : 代码随想录Day34 LeetCode T343整数拆分 T96 不同的二叉搜索树-CSDN博客 1.明白dp数组的含义 2.明白递推公式的含义 3.初始化dp数组 4.注意dp数组的遍历顺序 5.打印dp数组排错 LeetCode T1049 最后一块石头的重量II 题目链接:1049. 最后一块石头…

LeetCode:117. 填充每个节点的下一个右侧节点指针 II(C++)

117. 填充每个节点的下一个右侧节点指针 II 题目描述&#xff1a; 给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将…

Promise的并发控制 - 从普通并发池到动态并发池

一、场景 给你一个有200个URL的数组&#xff0c;通过这些URL来发送请求&#xff0c;要求并发请求数不能超过五个。 这是一道很常考的面试题&#xff0c;接下来让我们来学习一下Promise并发控制 二、普通并发池的实现 主要思路就是&#xff0c;判断当前队列是否满&#xff0c;…

【IDEA】在工具栏设置快速创建包和类的图表

页面效果&#xff1a; 操作步骤&#xff1a; 设置 --> 外观与行为 --> 菜单与工具栏 --> 点击 主工具栏 --> 点击 ---- --> 点击 号 --> 添加操作 主菜单 --> 文件 --> 文件打开操作 --> 打开项目操作 --> 新建 --> 往下找 找到 clas…