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

音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现

 =================================================================

音视频入门基础:WAV专题系列文章:

音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件

音视频入门基础:WAV专题(2)——WAV格式简介

音视频入门基础:WAV专题(3)——FFmpeg源码中,判断某文件是否为WAV音频文件的实现

音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现

音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现

=================================================================

一、引言

通过FFmpeg命令可以获取到WAV文件的音频压缩编码格式、采样频率、声道数量、采样位数、码率等信息:

在vlc中也可以获取到这些信息(vlc底层也使用了FFmpeg进行解码):

所以FFmpeg和vlc是怎样获取到这些信息的呢?它们其实是通过WAV Header中的标签为“fmt ”的子区块获取的。在文章《音视频入门基础:WAV专题(2)——WAV格式简介》中,介绍了WAV格式和WAV的Header。WAV Header中的内容以区块(chunk)为最小单位,标签为“fmt ”的子区块被称为Format chunk,记录声道数量、采样率等信息:

而FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3)中是通过ff_get_wav_header函数来解码Format chunk,获取里面信息的。

二、ff_get_wav_header函数的声明

ff_get_wav_header函数声明在FFmpeg源码的头文件libavformat/riff.h中:

int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par, int size, int big_endian);

该函数作用是:解码WAV Header中的Format chunk,获取里面的信息。

形参s:输入型参数。为AVFormatContext类型的指针,这里主要用于打印日志,可忽略。

形参pb:既是输入型参数也是输出型参数。指向一个AVIOContext 类型变量,包含WAV文件中的二进制数据。

pb->buffer:恒指向输入缓冲区的开头,该缓冲区包含WAV文件最前面的二进制数据。由于WAV Header在WAV文件的最前面,所以该缓冲区包含整个WAV Header的二进制数据。

pb->buffer_size:pb->buffer指向的缓冲区的大小,单位为字节。FFmpeg解码WAV Header的时候不会读取完整个WAV文件,只会读取它前面的一部分,比如最开始的32768个字节。只要根据前面的这些字节就足够判断出它的格式了,所以p->buf_size的值一般就是32768。

pb->buf_ptr:指向输入缓冲区中当前读取到的位置。由于当前要读取的是Format chunk,所以pb->buf_ptr指向以“Format chunk”中的“音讯格式”为开头的数据。执行ff_get_wav_header函数后,pb->buf_ptr的值会增加,表示读取完了Format chunk:

pb->buf_end:恒指向输入缓冲区的末尾。

形参par:输出型参数。指向一个AVCodecParameters类型变量,也就是描述编码流属性的结构体。执行ff_get_wav_header函数后,通过par中的成员拿到Format chunk中的声道数量、采样率等信息。AVCodecParameters结构体部分成员如下:

/*** This struct describes the properties of an encoded stream.** sizeof(AVCodecParameters) is not a part of the public ABI, this struct must* be allocated with avcodec_parameters_alloc() and freed with* avcodec_parameters_free().*/
typedef struct AVCodecParameters {/*** General type of the encoded data.*/enum AVMediaType codec_type;enum AVCodecID   codec_id;/*** The average bitrate of the encoded data (in bits per second).*/int64_t bit_rate;/*** The number of bits per sample in the codedwords.** This is basically the bitrate per sample. It is mandatory for a bunch of* formats to actually decode them. It's the number of bits for one sample in* the actual coded bitstream.** This could be for example 4 for ADPCM* For PCM formats this matches bits_per_raw_sample* Can be 0*/int bits_per_coded_sample;/*** Audio only. The number of audio channels.*/int      channels;/*** Audio only. The number of audio samples per second.*/int      sample_rate;/*** Audio only. The number of bytes per coded audio frame, required by some* formats.** Corresponds to nBlockAlign in WAVEFORMATEX.*/int      block_align;//...
} AVCodecParameters;

执行ff_get_wav_header函数后:

par->codec_type会被赋值为AVMEDIA_TYPE_AUDIO,表示它对应的这路流是音频。

par->codec_id会被赋值为解码器的id。比如对于PCM这种音频压缩编码格式,其解码器id有如下类型:

    /* various PCM "codecs" */AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecsAV_CODEC_ID_PCM_S16LE = 0x10000,AV_CODEC_ID_PCM_S16BE,AV_CODEC_ID_PCM_U16LE,AV_CODEC_ID_PCM_U16BE,AV_CODEC_ID_PCM_S8,AV_CODEC_ID_PCM_U8,AV_CODEC_ID_PCM_MULAW,AV_CODEC_ID_PCM_ALAW,AV_CODEC_ID_PCM_S32LE,AV_CODEC_ID_PCM_S32BE,AV_CODEC_ID_PCM_U32LE,AV_CODEC_ID_PCM_U32BE,AV_CODEC_ID_PCM_S24LE,AV_CODEC_ID_PCM_S24BE,AV_CODEC_ID_PCM_U24LE,AV_CODEC_ID_PCM_U24BE,AV_CODEC_ID_PCM_S24DAUD,AV_CODEC_ID_PCM_ZORK,AV_CODEC_ID_PCM_S16LE_PLANAR,AV_CODEC_ID_PCM_DVD,AV_CODEC_ID_PCM_F32BE,AV_CODEC_ID_PCM_F32LE,AV_CODEC_ID_PCM_F64BE,AV_CODEC_ID_PCM_F64LE,AV_CODEC_ID_PCM_BLURAY,AV_CODEC_ID_PCM_LXF,AV_CODEC_ID_S302M,AV_CODEC_ID_PCM_S8_PLANAR,AV_CODEC_ID_PCM_S24LE_PLANAR,AV_CODEC_ID_PCM_S32LE_PLANAR,AV_CODEC_ID_PCM_S16BE_PLANAR,AV_CODEC_ID_PCM_S64LE,AV_CODEC_ID_PCM_S64BE,AV_CODEC_ID_PCM_F16LE,AV_CODEC_ID_PCM_F24LE,AV_CODEC_ID_PCM_VIDC,AV_CODEC_ID_PCM_SGA,

par->bit_rate会被赋值为该音频的码率,单位为bits/s。

par->bits_per_coded_sample会被赋值为音频的采样位数。

par->channels会被赋值为声道数量。

par->sample_rate会被赋值为音频的采样频率,单位为Hz。

par->block_align会被赋值为“区块对齐”,即每个采样点所需的字节数。

形参size:输入型参数。Format chunk的子区块大小,也就是“子区块1大小”:

形参big_endian:输入型参数,表示WAV Header中的区块中的真正数据是按照小端还是大端存贮。如果该WAV文件遵守RIFF格式的规则,形参big_endian为0,表示小端;如果该WAV文件遵守RIFX格式的规则,形参big_endian为1,表示大端。

返回值:返回0表示从Format chunk获取信息成功。返回一个负数表示从Format chunk获取信息失败。

三、ff_get_wav_header函数的定义

ff_get_wav_header函数的定义在FFmpeg源码的源文件libavformat/riffdec.c中:

/* "big_endian" values are needed for RIFX file format */
int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,AVCodecParameters *par, int size, int big_endian)
{int id;uint64_t bitrate = 0;if (size < 14) {avpriv_request_sample(s, "wav header size < 14");return AVERROR_INVALIDDATA;}par->codec_type  = AVMEDIA_TYPE_AUDIO;if (!big_endian) {id                 = avio_rl16(pb);if (id != 0x0165) {par->channels    = avio_rl16(pb);par->sample_rate = avio_rl32(pb);bitrate            = avio_rl32(pb) * 8LL;par->block_align = avio_rl16(pb);}} else {id                 = avio_rb16(pb);par->channels    = avio_rb16(pb);par->sample_rate = avio_rb32(pb);bitrate            = avio_rb32(pb) * 8LL;par->block_align = avio_rb16(pb);}if (size == 14) {  /* We're dealing with plain vanilla WAVEFORMAT */par->bits_per_coded_sample = 8;} else {if (!big_endian) {par->bits_per_coded_sample = avio_rl16(pb);} else {par->bits_per_coded_sample = avio_rb16(pb);}}if (id == 0xFFFE) {par->codec_tag = 0;} else {par->codec_tag = id;par->codec_id  = ff_wav_codec_get_id(id,par->bits_per_coded_sample);}if (size >= 18 && id != 0x0165) {  /* We're obviously dealing with WAVEFORMATEX */int cbSize = avio_rl16(pb); /* cbSize */if (big_endian) {avpriv_report_missing_feature(s, "WAVEFORMATEX support for RIFX files");return AVERROR_PATCHWELCOME;}size  -= 18;cbSize = FFMIN(size, cbSize);if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */parse_waveformatex(s, pb, par);cbSize -= 22;size   -= 22;}if (cbSize > 0) {if (ff_get_extradata(s, par, pb, cbSize) < 0)return AVERROR(ENOMEM);size -= cbSize;}/* It is possible for the chunk to contain garbage at the end */if (size > 0)avio_skip(pb, size);} else if (id == 0x0165 && size >= 32) {int nb_streams, i;size -= 4;if (ff_get_extradata(s, par, pb, size) < 0)return AVERROR(ENOMEM);nb_streams         = AV_RL16(par->extradata + 4);par->sample_rate   = AV_RL32(par->extradata + 12);par->channels      = 0;bitrate            = 0;if (size < 8 + nb_streams * 20)return AVERROR_INVALIDDATA;for (i = 0; i < nb_streams; i++)par->channels += par->extradata[8 + i * 20 + 17];}par->bit_rate = bitrate;if (par->sample_rate <= 0) {av_log(s, AV_LOG_ERROR,"Invalid sample rate: %d\n", par->sample_rate);return AVERROR_INVALIDDATA;}if (par->codec_id == AV_CODEC_ID_AAC_LATM) {/* Channels and sample_rate values are those prior to applying SBR* and/or PS. */par->channels    = 0;par->sample_rate = 0;}/* override bits_per_coded_sample for G.726 */if (par->codec_id == AV_CODEC_ID_ADPCM_G726 && par->sample_rate)par->bits_per_coded_sample = par->bit_rate / par->sample_rate;return 0;
}

四、ff_get_wav_header函数的内部实现分析

ff_get_wav_header函数中,首先通过如下语句判断Format chunk的子区块大小,也就是“子区块1大小”是否小于14,如果小于14返回AVERROR_INVALIDDATA,表示不合法:

if (size < 14) {avpriv_request_sample(s, "wav header size < 14");return AVERROR_INVALIDDATA;}

合法的“Format chunk的子区块大小”是不可能小于14,但可以等于14。如果等于14,则Format chunk不包含“位元深度”(采样位数):

然后让par->codec_type被赋值为AVMEDIA_TYPE_AUDIO,表示它对应的这路流是音频。

par->codec_type  = AVMEDIA_TYPE_AUDIO;

根据该WAV文件是遵守RIFF格式还是RIFX格式的规则,按照小端/大端模式读取声道数量、音频采样率、码率、“区块对齐”(每个采样点所需的字节数)。这里用到avio_rXXX系列函数,关于它们的用法可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》。由于Format chunk中的音频码率单位为byte per second,所以得将该值乘8,得到以bits/s为单位的码率:

if (!big_endian) {id                 = avio_rl16(pb);if (id != 0x0165) {par->channels    = avio_rl16(pb);par->sample_rate = avio_rl32(pb);bitrate            = avio_rl32(pb) * 8LL;par->block_align = avio_rl16(pb);}} else {id                 = avio_rb16(pb);par->channels    = avio_rb16(pb);par->sample_rate = avio_rb32(pb);bitrate            = avio_rb32(pb) * 8LL;par->block_align = avio_rb16(pb);}

当“Format chunk的子区块大小”为14时,表示这是最简单版本不包含音频采样位数的Format chunk,让par->bits_per_coded_sample音频采样位数默认取值为8。如果“Format chunk的子区块大小”不为14,读取音频的采样位数:

if (size == 14) {  /* We're dealing with plain vanilla WAVEFORMAT */par->bits_per_coded_sample = 8;} else {if (!big_endian) {par->bits_per_coded_sample = avio_rl16(pb);} else {par->bits_per_coded_sample = avio_rb16(pb);}}

根据音频压缩编码格式得到对应的解码器id:

if (id == 0xFFFE) {par->codec_tag = 0;} else {par->codec_tag = id;par->codec_id  = ff_wav_codec_get_id(id,par->bits_per_coded_sample);}

“Format chunk的子区块大小”的值一般是16,如果大于16,表示Format chunk包含扩展块。通过下面语句处理Format chunk包含扩展块的情况:

    if (size >= 18 && id != 0x0165) {  /* We're obviously dealing with WAVEFORMATEX */int cbSize = avio_rl16(pb); /* cbSize */if (big_endian) {avpriv_report_missing_feature(s, "WAVEFORMATEX support for RIFX files");return AVERROR_PATCHWELCOME;}size  -= 18;cbSize = FFMIN(size, cbSize);if (cbSize >= 22 && id == 0xfffe) { /* WAVEFORMATEXTENSIBLE */parse_waveformatex(s, pb, par);cbSize -= 22;size   -= 22;}if (cbSize > 0) {if (ff_get_extradata(s, par, pb, cbSize) < 0)return AVERROR(ENOMEM);size -= cbSize;}/* It is possible for the chunk to contain garbage at the end */if (size > 0)avio_skip(pb, size);} else if (id == 0x0165 && size >= 32) {int nb_streams, i;size -= 4;if (ff_get_extradata(s, par, pb, size) < 0)return AVERROR(ENOMEM);nb_streams         = AV_RL16(par->extradata + 4);par->sample_rate   = AV_RL32(par->extradata + 12);par->channels      = 0;bitrate            = 0;if (size < 8 + nb_streams * 20)return AVERROR_INVALIDDATA;for (i = 0; i < nb_streams; i++)par->channels += par->extradata[8 + i * 20 + 17];}

音频采样率不可能不大于0,如果不大于0返回AVERROR_INVALIDDATA,表示不合法:

    if (par->sample_rate <= 0) {av_log(s, AV_LOG_ERROR,"Invalid sample rate: %d\n", par->sample_rate);return AVERROR_INVALIDDATA;}

相关文章:

音视频入门基础:WAV专题(4)——FFmpeg源码中获取WAV文件音频压缩编码格式、采样频率、声道数量、采样位数、码率的实现

音视频入门基础&#xff1a;WAV专题系列文章&#xff1a; 音视频入门基础&#xff1a;WAV专题&#xff08;1&#xff09;——使用FFmpeg命令生成WAV音频文件 音视频入门基础&#xff1a;WAV专题&#xff08;2&#xff09;——WAV格式简介 音视频入门基础&#xff1a;WAV专题…...

环境变量在Conda中的魔法:控制包安装的秘诀

环境变量在Conda中的魔法&#xff1a;控制包安装的秘诀 Conda不仅是Python和其他语言包的包管理器&#xff0c;它还是一个强大的环境管理器。在使用Conda时&#xff0c;环境变量可以极大地增强其功能&#xff0c;允许用户控制包的安装过程&#xff0c;实现定制化的安装策略。本…...

VS Code C/C++ MSVC编译器

官方教程 通过快捷方式打开VS Code是编译不了的,需要对tasks.json修改(Tasks: Configure default build task) 先创建tasks.json 复制这段配置到tasks.json,记得修改VsDevCmd.bat的路径 {"version": "2.0.0","windows": {"options"…...

【技巧】IDEA 个性化配置

【技巧】IDEA 个性化配置 自动补全 关闭大小写区分 自动导包 插件 Rainbow Brackets 彩色括号 更容易区分是哪个括号...

`pytest` 中一些常用的选项

下面列出的参数和功能涵盖了 pytest 中一些常用的选项&#xff0c;但 pytest 还有许多其他参数和功能。以下是一些补充的 pytest 命令行参数和功能&#xff1a; 其他命令行参数 测试配置 --confcutdir<path>: 只加载指定目录及其子目录中的配置文件。例如 --confcutdirs…...

fme从json中提取位置到kml中

fme从json中提取位置到kml中 简单参考,我自己要用的,越弄越复杂。 概述-模板总体结构 数据就是官方提供的数据,模板的基本节结构是读模块+转换器+写模块,最近爬取一些json文件,用到了。 1.使用json读模块读取数据 首先检查一下源数据 使用文本打开数据集,可以看到非缩…...

在Ubuntu 18.04上安装和配置pgAdmin 4服务器模式的方法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 pgAdmin 是一个针对 PostgreSQL 及其相关数据库管理系统的开源管理和开发平台。它使用 Python 和 jQuery 编写&#xff0c;支持 P…...

NiFi :1 初识这把“十年一剑”的利器

--->更多内容&#xff0c;请移步“鲁班秘笈”&#xff01;&#xff01;<--- “现在AI和数据处理密不可分&#xff0c;80%的企业可以利用Apache NiFi轻松解决复杂的数据问题&#xff0c;快速完成场景建设。犹如花上百来块钱在家享受一顿不亚于五星级西餐厅的法式大餐。对…...

Pyside6实战教程专栏目录

Pyside6实战教程&#x1f680; 专栏目录介绍 本专栏将详细地向Python开发者展示如何利用PySide6框架创建功能丰富的桌面应用程序。无论你是刚刚接触GUI编程的新手&#xff0c;还是希望快速提升自己技能水平的进阶用户&#xff0c;本文都将为你提供一系列简单易懂的教程&#xf…...

【Dash】使用 Dash Design Kit (DDK) 创建图表

一、Styling Your App The examples in the previous section used Dash HTML Components to build a simple app layout, but you can style your app to look more professional. This section will give a brief overview of the multiple tools that you can use to enhan…...

C++ 几何算法 - 向量点乘,叉乘及其应用

一&#xff1a;点乘介绍 1. 向量点乘&#xff1a; 2. 向量点乘的性质&#xff1a; 3. 向量点乘公式&#xff1a; 4. 向量的点乘的属性&#xff1a; &#xff08;1&#xff09;&#xff1a;向量与自身做点乘&#xff0c;会得到向量长度的平方&#xff1a; &#xff08;2&#xf…...

Taro学习记录(具体项目实践)

一、安装taro-cli 二、项目文件 三、项目搭建 1、Eslint配置 在项目生成的 .eslintrc 中进行配置 {"extends": ["taro/react"], //一个配置文件&#xff0c;可以被基础配置中的已启用的规则继承"parser": "babel/eslint-parser…...

ICML 2024 | 矛与盾的较量!北大提出提示无关数据防御保护算法PID

文章链接&#xff1a;https://arxiv.org/pdf/2406.15305 代码地址&#xff1a;https://github.com/PKU-ML/Diffusion-PID-Protection 亮点直击 本文在实证观察中发现&#xff0c;保护阶段和利用阶段之间的提示不匹配可能会削弱当前数据保护算法的有效性。本文深入探讨了利用LDM…...

Oracle聚合函数LISTAGG和WM_CONCAT简介

目录 Oracle聚合函数LISTAGG和WM_CONCAT简介LISTAGG 函数1.语法2.示例3.去除重复值 WM_CONCAT 函数1.语法2.示例3.去除重复值 比较1.性能2.排序与分隔符3.去除重复值 Oracle聚合函数LISTAGG和WM_CONCAT简介 在处理数据库中的数据聚合任务时&#xff0c;我们经常需要将多行数据…...

【Unity】多种寻路算法实现 —— BFS,DFS,Dijkstra,A*

本实验寻路算法均基于网格实现&#xff0c;整体称呼为Grid&#xff0c;单个瓦片称之为Tile 考虑程序处理的简洁性&#xff0c;所有算法使用同一种Tile&#xff0c;且权值点&#xff0c;A*所需的记录数值也全部放在Tile中记录 前排贴上代码仓库链接&#xff1a; GitHub - Sir…...

十大游戏设计软件:创意实现的利器

在数字娱乐的多彩世界里&#xff0c;游戏设计无疑是一项充满创意与技术挑战的艺术。随着技术的进步&#xff0c;游戏设计师的手中拥有了一系列强大的工具&#xff0c;它们让想象中的世界得以呈现&#xff0c;让玩家的体验更加丰富和真实。本文将带你走进游戏设计的幕后&#xf…...

Pandas高级操作:多级索引、窗口函数、数据透视表等

在数据处理和分析中,pandas库提供了强大的功能,支持从简单到复杂的数据操作。本文将介绍一些pandas的高级操作,包括多级索引(MultiIndex)、窗口函数(Window Functions)、数据透视表与复杂聚合、数据合并与连接、高级数据变换以及时间序列数据的高级处理。 1. 多级索引(…...

mysql源码编译启动debug

对于没有C语言基础的同学来说&#xff0c;想看看源码&#xff0c;在搞定编辑器做debug的时候就被劝退了&#xff0c;发生点啥了&#xff0c;完全看不懂&#xff0c;不知道从哪里入手去做debug&#xff1b;我为了看看 mysql 的 insert buffer 到底存的是索引页还是数据页&#x…...

吴恩达机器学习-C1W3L2-逻辑回归之S型函数

可选实验:逻辑回归 在这个不评分的实验中&#xff0c;你会 探索sigmoid函数(也称为logistic函数)探索逻辑回归;哪个用到了s型函数 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from plt_one_addpt_onclick import plt_one_addpt_onclick from l…...

P-one新增火焰图-为性能测试开启新视野

随着软件业务流程的日益复杂&#xff0c;传统的性能测试方法已经难以满足对性能问题精准定位的需求。测试人员需要一种更加直观、全面的方式来分析软件在运行过程中的性能表现&#xff0c;以便快速准确地找到性能瓶颈并进行优化。因此&#xff0c;我们在性能测试平台P-One中加入…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...