【音视频 | opus】opus编码的Ogg封装文件详解
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 opus 编码的 Ogg 封装文件🍭
😎金句分享😎:🍭🍭
本文未经允许,不得转发!!!
opus和Ogg相关系列文章:
1、RFC3533 :Ogg封装格式版本 0(The Ogg Encapsulation Format Version 0)
2、Ogg封装格式详解——包含Ogg封装过程、数据包(packet)、页(page)、段(segment)等
3、libogg库详解介绍以及使用——附带libogg库解析.opus文件的C源码
4、RFC7845:Opus音频编解码器的Ogg封装(Ogg Encapsulation for the Opus Audio Codec)
5、opus编解码库(opus-1.4)详细介绍以及使用——附带解码示例代码
6、opus编码的Ogg封装文件详解
目录
- 🎄一、概述
- 🎄二、ID头部数据(ID Header)
- ✨2.1、ID 头部数据的介绍
- ✨2.2、.opus 文件 ID头部数据 分析
- 🎄三、注释头部数据(Comment Header)
- ✨3.1、注释头部数据的介绍
- ✨3.2、.opus 文件 注释头部数据 分析
- 🎄四、opus 编码数据包
- 🎄五、opus编码的Ogg封装文件解码C语言源码
- 🎄六、总结
🎄一、概述
前面的文章 Ogg封装格式详解 介绍了Ogg格式的封装,介绍了封装过程、Ogg的页结构、Ogg的段,但没涉及到每个段的内容,因为这跟编码有关系。本文就介绍 opus 编码在 Ogg 封装文件中是怎样存储的,同样地,从一个 opus 编码的Ogg文件去查看十六进制,最终了解 opus 编码 Ogg 封装文件的整个结构,这样就可以轻易将这类文件解码为PCM了。
opus 编码的Ogg文件结构如下图:
- 逻辑Ogg比特流中的第一个数据包必须包含ID头部数据(ID Header),该头部数据将流唯一标识为Opus音频。
- 逻辑Ogg比特流中的第二个数据包必须包含注释头部数据(Comment Header),其中包含用户提供的元数据。
- 之后的所有页面(page)都是音频数据页面。
下文分析的Ogg封装文件为 48000Hz-s16le-1ch-ChengDu.opus,下载链接:https://download.csdn.net/download/wkd_007/88492683
🎄二、ID头部数据(ID Header)
✨2.1、ID 头部数据的介绍
opus编码的逻辑Ogg比特流中,第一页必须包含ID头部数据
,其结构如下图,包括8个字段,各个字段代表的含义如下:
-
1、Magic Signature(8字节):
这是一个8位字节(64位)字段,允许编解码器识别,并且是人类可读的。它按顺序包含以下的字母:
0x4F 'O' 0x70 'p' 0x75 'u' 0x73 's' 0x48 'H' 0x65 'e' 0x61 'a' 0x64 'd'
从“Op”开始有助于将其与音频数据包区分开来,因为这是一个无效的TOC序列。
-
2、Version (1字节, unsigned):
对于此版本的封装规范,版本号必须始终为“1”。实现应该将版本号的前四位与已识别规范的版本号匹配的流视为与该规范向后兼容。也就是说,版本号可以分为“主要”和“次要”版本子字段,次要子字段的变化(在较低的四位中)表示兼容的变化。例如,本规范的实现应该接受版本号为“15”或更低的任何流,并且应该假设版本号为”16“或更高的任何流不兼容。选择初始版本“1”是为了防止实现依赖此八位字节作为“OpusHead”字符串的null终止符。 -
3、Output Channel Count ‘C’ (1字节, unsigned)
这是输出通道的数量。这可能与编码信道的数量不同,编码信道的数目可以在逐包的基础上改变。此值不得为零。最大允许值取决于通道映射族,可能大到255。详见[RFC7845]
文档的第5.1.1节。 -
4、Pre-skip (2字节, unsigned, little endian):
这是开始播放时要从解码器输出中丢弃的采样数(48 kHz),也是要从页面的颗粒位置中减去以计算其PCM采样位置的数量。当裁剪现有Ogg Opus流的开头时,建议预跳过至少3840个样本(80ms),以确保解码器完全收敛。
-
5、Input Sample Rate (4字节, unsigned, little endian):
这是原始输入(编码前)的采样率,单位为Hz。该字段不是用于播放编码数据的采样率。
Opus可以在4、6、8、12和20kHz的内部音频带宽之间切换。流中的每个数据包可以具有不同的音频带宽。不管音频带宽如何,参考解码器都支持以8、12、16、24或48kHz的采样率对任何流进行解码。传递到编码器的音频的原始采样率不被有损压缩所保留。
Ogg Opus播放器应根据以下程序选择播放采样率:
1.如果硬件支持48 kHz播放,则以48 kHz进行解码。
2.否则,如果硬件的最高可用采样率是支持的速率,则以该采样率进行解码。
3.否则,如果硬件的最高可用采样率小于48kHz,则以高于最高可用硬件速率的下一个更高的Opus支持速率进行解码并重新采样。
4.否则,以48 kHz解码并重新采样。
然而,“输入采样率”字段允许复用器将原始输入流的采样率作为元数据传递。当用户需要输出采样率来匹配输入采样率时,这是有用的。例如,当不播放输出时,将PCM格式样本写入磁盘的实现可能会选择将音频重新采样回原始输入采样率,以减少用户的意外,因为用户可能会合理地期望以相同的采样率返回文件。
零值表示“未指定”。复用器应该写入实际输入采样率或零,但使用该字段执行某些操作的实现应该注意,如果给定疯狂的值(例如,如果请求,不要实际将输出上采样到10MHz),则行为要理智。实现应支持 8kHz 和 192kHz(包括8kHz)之间的输入采样率。此范围之外的速率可以通过回落到默认速率 48kHz 来忽略。
-
6、Output Gain (2字节, signed, little endian):
这是解码时要应用的增益。它是对解码器输出进行缩放以获得所需播放量的因子的20*log10,存储在具有8个小数比特的16比特、带符号的2的补码定点值中(即Q7.8[Q-NOTATION])。
为了应用增益,实现可以使用以下内容:
sample *= pow(10, output_gain/(20.0*256))
其中“output_gain”是来自标头的原始16位值。
玩家和媒体框架应该默认应用它。如果播放器选择应用任何音量调整或增益修改,如R128_TRACK_AIN(见第5.2节),则除了此输出增益外,还必须应用该调整,以实现标准化音量的播放。
复用器应该将该字段设置为零,而不是在编码之前应用任何增益,只要这是可能的,并且不会与用户的愿望相冲突。非零输出增益表示在编码之后调整了增益,或者用户希望在保持恢复原始信号幅度的能力的同时调整增益以进行回放。
尽管输出增益具有巨大的范围(+/-128 dB,足以将听不见的声音放大到身体疼痛的阈值),但大多数应用程序只能合理地使用零附近这个范围的一小部分。大范围的部分作用是确保增益始终可以在OpusHead和R128增益标签之间无损传输(见下文)而不会饱和。
-
7、Channel Mapping Family (1字节, unsigned):
这个八位字节表示输出通道的顺序和语义。
该八位位组的每个当前指定值都指示一个映射族,该映射族定义了一组允许的通道计数,以及每个允许的通道数的有序通道名称集。详见
[RFC7845]
文档的第5.1.1节。 -
8、Channel Mapping Table:
此表定义了从编码流到输出通道的映射。其内容见
[RFC7845]
文档的第5.1.1节。ID标头中的所有字段都是必需的,但“通道映射表”除外,当通道映射族为0时,必须省略该字段,否则为必需字段。如果流包含的ID标头没有足够的数据用于这些字段,即使它包含有效的“魔术签名”,实现也应该将其视为无效。该规范的未来版本,甚至是向后兼容的版本,可能会在ID头中包含额外的字段。如果ID标头具有兼容的主版本,但具有较大的次版本,则实现不得将其视为无效,因为它包含此处未指定的其他数据,前提是它仍然在第一页上完成。
✨2.2、.opus 文件 ID头部数据 分析
现在按照上面的字段,分析opus编码的Ogg文件,用Notepad打开 48000Hz-s16le-1ch-ChengDu.opus 并查看十六进制模式:
- 前面蓝色背景的部分(包括0x13)是Ogg页头部数据,不清楚的看 上篇文章 的 4.2 节;
- 紫色框开始是opus编码的ID头部数据,内容是8个字符:
OpusHead
; - 接下去的蓝色框是版本号,目前的版本,该值为
1
; - 紧接着的蓝色框是声道数量,这里值为
1
,表示单声道; - 后面的橙色框表示 “开始播放时要从解码器输出中丢弃的采样数”,值为
0x0138
; - 接着的绿色框表示 “原始输入(编码前)的采样率”,值为
0xbb80
,对应十进制48000
; - 后面的蓝色框表示 “解码时要应用的增益”;
- 后面的黑色框表示 “表示输出通道的顺序和语义”,值为
0
,代表使用映射族0,允许的通道数:1或2,且省略通道映射表(Channel Mapping Table);
🎄三、注释头部数据(Comment Header)
✨3.1、注释头部数据的介绍
opus编码的逻辑Ogg比特流中,第二页必须包含注释头部数据
。它可能跨越多个页面(page),从逻辑流的第二页开始。无论它跨越多少页,注释头数据包都必须结束在一个完整的页面,其结构如下图,包括6个字段,各个字段代表的含义如下:
-
1、Magic Signature(8个字节):
这是一个8位字节(64位)字段,允许编解码器识别,并且是人类可读的。它按顺序包含以下神奇数字:
0x4F 'O' 0x70 'p' 0x75 'u' 0x73 's' 0x54 'T' 0x61 'a' 0x67 'g' 0x73 's'
从“Op”开始有助于将其与音频数据包区分开来,因为这是一个无效的TOC序列。
-
2、Vendor String Length (4个字节, unsigned, little endian):
此字段给出下个字段
Vendor String
的长度,单位为八位字节。它不得指示供应商字符串比数据包的其余部分长。 -
3、Vendor String (variable length, UTF-8 vector):
这是一个用于供应商信息的简单可读标签,编码为UTF-8字符串[RFC3629]。不需要终止空八位字节。
此标签旨在识别编解码器编码器和封装实现,以跟踪技术行为的差异。面向用户的应用程序可以使用“ENCODER”用户注释标记来标识自己。
-
4、User Comment List Length (4个字节, unsigned, little endian):
此字段指示用户提供的注释个数。它可能表示用户提供的注释为零,在这种情况下,数据包中没有其他字段。它不能表明有太多的注释,以至于注释字符串长度需要比数据包其余部分更多的数据。
-
5、User Comment #i String Length (4个字节, unsigned, little endian):
此字段给出下个字段
User Comment #i String
的长度,单位为八位字节。“用户评论列表长度”字段指示的每个用户评论都有一个。它不能指示字符串比数据包的其余部分长。 -
6、User Comment #i String (variable length, UTF-8 vector):
此字段包含编码为UTF-8字符串的单个用户注释[RFC3629]。“用户评论列表长度”字段指示的每个用户评论都有一个。
✨3.2、.opus 文件 注释头部数据 分析
下图是用Notepad打开 48000Hz-s16le-1ch-ChengDu.opus 并查看十六进制模式的截图,这里分析的是第二页数据:
- 前面蓝色背景的部分是Ogg页头部数据,不清楚的看 上篇文章 的 4.2 节第二页数据;
- 紫色框开始是opus编码的注释头部数据,内容是8个字符:
OpusTags
; - 接下去的红色色框的4个字节是供应商字符串长度, 此字段给出下个字段
Vendor String
的长度,这里是0x0b
; - 紧接着的红色框的11个字节就是供应商字符串,内容是
libopus 1.1
- 后面的绿色框表示 “注释个数”,值为
0x01
,表示只有一个注释 ; - 接着的蓝色框表示 “第一个注释字符串的长度”,值为
0x25
,对应十进制37
,表示后面有37个字符的注释; - 后面的橙色框表示 “注释字符串”,内容是
ENCODER=opusenc form opus-tools 0.1.8
,共37个字符;
🎄四、opus 编码数据包
在一个opus编码的Ogg格式文件中,除了一个ID头部数据包
和一个 注释头部数据包
(有时可以跨多页),剩下的都是 opus编码音频数据包
,音频数据包也是存储在Ogg格式文件的各个段(Segment)中,下面从一个实际的Ogg文件分析音频数据包。
下图是用Notepad打开 48000Hz-s16le-1ch-ChengDu.opus 并查看十六进制模式的截图,这里分析的是第三页数据:
- 图中黑色框表示该Ogg页数据中有
0x33
(十进制51)个段(Segment),但并不表示有51个opus音频数据包,因为段的长度为0xff
时,表示该段存不下一个音频数据包,下个段仍然是这个包的数据。 - 蓝色背景的
51
个字节就是各个段的长度,可以看到第一个段长度是0xff
,表示第一个音频包存储了2个段,第一个音频包的实际长度应该是0xff+0x06
;后面49个段长度中,都没有0xff
,说明每个都存储了一个音频数据包。所以这一页应该是50个音频数据包。每个数据包读取后,都可以直接送到opus-1.4
的库去解码成PCM音频数据。
🎄五、opus编码的Ogg封装文件解码C语言源码
清楚了前面的知识,就可以对opus编码的Ogg文件进行解码了,你可以使用 libogg库加上 opus-1.4 的库进行解码,那样会更简洁一点,下面给出的源码只使用了opus-1.4去解码音频包,有很大的参考价值:
opusDec.h
/*** @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__
opusDec.cpp
/*** @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;
}
readOggFile.c
// g++ readOggFile.c opusDec.cpp ../../opus-1.4/result_gcc/lib/libopus.a -I ../../opus-1.4/result_gcc/include/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "opusDec.h"COpusDec g_OpusDec;// 8字节数组转成 unsigned long long
unsigned long long ToULL(unsigned char num[8], int len)
{unsigned long long ret = 0;if(len==8){ int i=0;for(i=0; i<len; i++){ret |= ((unsigned long long)num[i] << (i*8));}}return ret;
}// 4字节数组转成 unsigned int
unsigned int ToUInt(unsigned char num[4], int len)
{unsigned int ret = 0;if(len==4){int i=0;for(i=0; i<len; i++){ret |= ((unsigned int)num[i] << (i*8));}}return ret;
}int readOggPage(char *oggFile)
{typedef struct PAGE_HEADER{ char Oggs[4]; unsigned char ver;unsigned char header_type_flag;unsigned char granule_position[8];unsigned char stream_serial_num[4];unsigned char page_sequence_number[4];unsigned char CRC_checksum[4];unsigned char seg_num;unsigned char segment_table[];}PAGE_HEADER;FILE *fp=fopen(oggFile,"rb");FILE *fpout=fopen("opus_out.pcm","wb+");;while(fp!=NULL && !feof(fp)){// 1、读取 page_headerPAGE_HEADER page_header;if(1 != fread(&page_header,sizeof(page_header),1,fp))break;printf("page_num:%03u; ",ToUInt(page_header.page_sequence_number, 4));printf("Oggs:%c %c %c %c; ",page_header.Oggs[0],page_header.Oggs[1],page_header.Oggs[2],page_header.Oggs[3]);printf("type=%d, granule_position:%08llu; ", page_header.header_type_flag,ToULL(page_header.granule_position, 8));//printf("seg_num:%d \n",page_header.seg_num);// 2、读取 Segment_tableunsigned char *pSegment_table = (unsigned char *)malloc(page_header.seg_num);fread(pSegment_table,sizeof(unsigned char),page_header.seg_num,fp);// 3、计算段数据总大小unsigned int TotalSegSize = 0;int i=0;for(i=0; i<page_header.seg_num; i++){TotalSegSize += pSegment_table[i];}printf("TotalSegSize:%d \n",TotalSegSize);// 4、读取段数据unsigned char *pSegment_data = (unsigned char *)malloc(TotalSegSize);fread(pSegment_data,sizeof(unsigned char),TotalSegSize,fp);if(page_header.header_type_flag == 4)printf("Last 4 Byte: %x %x %x %x\n",pSegment_data[TotalSegSize-4],pSegment_data[TotalSegSize-3], pSegment_data[TotalSegSize-2],pSegment_data[TotalSegSize-1]);// 5、解码opusunsigned sample_pos = ToULL(page_header.granule_position, 8);if(sample_pos){short out_buf[48000*2] = {0};int decodedLen = 0; // 已经解码的长度int preSegLen =0; // 前面分段的长度for(i=0; i<page_header.seg_num; i++){if(pSegment_table[i] == 255)// 这个包延续到后面,把该段长度累加起来{preSegLen += pSegment_table[i];continue;}int decFrames = g_OpusDec.OpusDecode((unsigned char*)pSegment_data+decodedLen, (int)preSegLen+pSegment_table[i], out_buf);if(decFrames < 0){printf("decodedLen=%d TotalSegSize=%d pSegment_table[i]=%d %d %d\n",decodedLen, TotalSegSize,pSegment_table[i-1],pSegment_table[i],pSegment_table[i+1]);}else{fwrite(out_buf,decFrames * sizeof(unsigned short),1,fpout);}decodedLen += (preSegLen+pSegment_table[i]);preSegLen = 0;}}free(pSegment_data);free(pSegment_table);}fclose(fp);fclose(fpout);return 0;
}int main()
{g_OpusDec.CreateOpusDecoder(48000, 1);readOggPage((char *)"48000Hz-s16le-1ch-ChengDu.opus");return 0;
}
🎄六、总结
本文介绍了opus编码的Ogg文件的 ID数据头部、注释数据头部、opus编码数据包的结构和解析,并用一个.opus文件的十六进行带着读者理解各个数据包,最后给出了笔者的C语言代码,将opus编码Ogg格式解析为PCM。看完一定大有所获,别忘记点赞、收藏、支持一下。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
相关文章:

【音视频 | opus】opus编码的Ogg封装文件详解
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
【微信小程序】自定义组件(一)
自定义组件 组件的创建与引用1、创建组件2、引用组件3、全局引用VS局部引用4、组件和页面的区别 样式1、组件样式隔离2、组件样式隔离的注意点3、stylelsolation的可选值 数据、方法和属性1、data数据2、methods方法3、properties4、data和properties区别5、使用setData修改pr…...

如何通过一条数字人三维动画宣传片,打造出数字文旅
越来越多虚拟人,以文化挖掘者的身份通过数字人三维动画宣传片,打通次元壁,助力文化传播形式创造性转化、创新性表达,赋予文化发展新动能。 如南方都市报民间博物馆文化探寻者“岭梅香”,由一艘在南宋时期失事的沉船“南…...

【MongoDB】索引 - 数组字段的多键索引
数组字段创建索引时,MongoDB会为数组中的每个元素创建索引键(多键索引),多键索引支持数组字段的高效查询。 一、准备工作 这里准备一些数据 db.shop.insertMany([{_id: 1, name: "水果店1", fruits: ["apple&qu…...

2023.11.5 关于 Spring 创建 和 使用
目录 创建 Spring 项目 1.创建 Maven 项目 2.添加 Spring 依赖 将 Bean 对象存储到 Spring 容器中 创建 Bean 存储 Bean ApplicationContext 获取 Bean BeanFactory 获取 Bean ApplicationContext 和 BeanFactory 的区别 获取 Bean 的三种方式 根据 Bean id 获取…...

3D目标检测实战 | 图解KITTI数据集评价指标AP R40(附Python实现)
目录 1 准确率和召回率2 P-R曲线的绘制3 AP R11与AP R40标准4 实际案例 1 准确率和召回率 首先给出 T P TP TP、 F P FP FP、 F N FN FN、 T N TN TN的概念 真阳性 True Positive T P TP TP 预测为正(某类)且真值也为正(某类)的样本数,可视为 I o U > I o U t…...
制作一个ros2机器人需要学习的课本(还不全面)
1《C语言》---这个是基础200页左右 2《C》-----500-600页 3《高等数学》-----没有这个无法计算动态电路 4《电路分析》-----没有这个没法设计硬件电路 5《英语5000词汇》最少也得达到美国小学生毕业时候的词汇水平5000词汇量 6《ros1》因为ros2没有一本中文课本---有那么一…...

Qt OpenGL相机系统
文章目录 一、简介二、实现代码三、实现效果参考资料效果展示 一、简介 一直偷懒没有学习OpenGL,乘着这段有点时间重新学习一下OpenGL,做一个简单的小工具,有助于后面理解OSG。我们都知道OpenGL中存在着下面几个坐标空间:模型空间(物体空间)、世界空间、观察空间(或者称…...
英语语音识别,语言评测,语音打分实践与代码实现
项目在这:couldn/speech-evaluation-of-english 详细的可查看项目内的md文档...

【SpringBoot篇】SpringBoot整合Mybatis实战
🎊专栏【SpringBoot】 🍔喜欢的诗句:天行健,君子以自强不息。 🎆音乐分享【如愿】 🎄欢迎并且感谢大家指出小吉的问题🥰 文章目录 🌺Spring Boot和MyBatis的好处🌺创建工…...
android c++ 硬编码硬解码官方demo
参考: https://fossies.org/linux/opencv/modules/videoio/src/cap_android_mediandk.cpp 代码: // This file is part of OpenCV project.// It is subject to the license terms in the LICENSE file found in the top-level directory// of this d…...

Python之Excel数据相关
Excel Microsoft Excel是Microsoft为使用Windows和Apple Macintosh操作系统的电脑编写的一款电子表格软件。直观的界面、出色的计算功能和图表工具,再加上成功的市场营销,使Excel成为最流行的个人计算机数据处理软件。在1993年,作为Microsof…...

Ubuntu网络IP地址一直显示127.0.0.1
问题描述: 终端输入ip a显示127.0.0.1,原来类似192.168.231.1的地址不见了。 ip a 点击网络配置(ubuntu桌面版),发现无线网络模块看不见了 正常情况应该有wired 模块,就是下面标红的 解决方案:…...

Vulnhub-DC-3 靶机复现完整过程
啰嗦两句: 提权之前完成是一个月前做的,当时在提权处出了点问题就搁置了,今天才完成,所以IP地址可能会会有变化 注意:后续出现的IP地址为192.168.200.55同样是靶机IP地址,若本文能有帮助到你的地方…...
Dubbo篇---第三篇
系列文章目录 文章目录 系列文章目录一、Dubbo 容错策略二、Dubbo 动态代理策略有哪些?三、说说 Dubbo 与 Spring Cloud 的区别?一、Dubbo 容错策略 failover cluster 模式 provider 宕机重试以后,请求会分到其他的 provider 上,默认两次,可以手动设置重试次数,建 议把写…...

Redis-使用java代码操作Redis->java连接上redis,java操作redis的常见类型数据存储,redis中的项目应用
java连接上redisjava操作redis的常见类型数据存储redis中的项目应用 1.java连接上redis package com.zlj.ssm.redis;import redis.clients.jedis.Jedis;/*** author zlj* create 2023-11-03 19:27*/ public class Demo1 {public static void main(String[] args) { // …...
react 使用笔记
1.学习:https://reactjs.bootcss.com/learn 2.项目启动报错:Delete ␍ prettier/prettier 解决:https://blog.csdn.net/qq_30272167/article/details/133280165 3.访问地址配置 文件:config-overrides.js devServer: functio…...
Ubuntu下启动Apache对.htaccess 的支持步骤, 利用.htaccess绑定域名到子目录
Ubuntu下启动Apache对.htaccess 的支持步骤 1. 终端运行 sudo a2enmod 程序提示可供激活的模块名称,输入: rewrite 2. 修改/etc/apache2/sites-enabled/000-default (该链接指向的是站点配置文件) 把(默认的www目录、或者需要应用.htacc…...

C++常用格式化输出
在C语言中可以用printf以一定的格式打印字符,C当然也可以。 输入输出及命名空间还不太了解的小伙伴可以看一看C入门讲解第一篇。 在C中,可以用流操作符(stream manipulators)控制数据的输出格式,这些流操作符定义在2…...

QCC TX 音频输入切换+提示声音
QCC TX 音频输入切换提示声音 QCC蓝牙芯片(QCC3040 QCC3056 等等),AUX、I2S、USB输入 蓝牙音频输入,模拟输出是最常见的方式。 也可以再此基础上动态切换输入方式。 针对TX切换EQ,调节音量不能出提示声音问题,可以增…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...