音频文件格式——AAC、OGG和FLAC
3.AAC文件格式
3.1 封装格式解析
高级音频编码 (Advanced Audio Coding) 是一种用于有损数字音频压缩的音频编码标准。它被设计为 MP3 格式的继承者,在相同比特率下通常可以获得比 MP3 更高的音质。AAC有两种封装格式:
- ADIF(Audio Data Interchange Format),音频数据交换格式,这种格式的特点是只在文件头部存储用于音频解码播放的头信息(例如采样率,通道数等),它的解码播放必须从文件头部开始,一般用于存储在本地磁盘中播放。
- ADTS(Audio Data Transport Stream),音频数据传输流,这种格式的特点是可以将数据看做一个个的音频帧,而每帧都存储了用于音频解码播放的头信息(例如采样率,通道数等),即可以从任何帧位置解码播放,更适用于流媒体传输。
目前在网络传输中常用的就是ADTS格式的封装,所以我们常见的AAC原始码流是由一个一个的ADTS frame(音频数据传输流帧)组成,每一帧由ADTS的帧头和原始数据块(MEPG2 TS),也就是说每个ADTS frame都可以单独去解码。
对于AAC的头部由7/9个字节组成,包含以下信息:
序号 | 长度(位) | 字段 | 描述 |
---|---|---|---|
A | 12 | syncword | 同步标志位,所有位都必须设置为1,固定为0xFF,表示ADTS的帧开始 |
B | 1 | id | MPEG版本,0:MPEG-4;1:MPEG-2 |
C | 2 | layer | 图层,始终设置为0。 |
D | 1 | Protection absence | CRC校验标识,0:使用CRC校验;1:不使用CRC校验 |
E | 2 | Profile | 音频对象类型,按照MPEG-4的音频对象类型序号减1 |
F | 4 | sampling frequency index | 音频采样频率索引,如下所示: 0: 96000 Hz 1: 88200 Hz 2 : 64000 Hz 3 : 48000 Hz 4: 44100 Hz ... |
G | 1 | private bit | 私有位,编码设置为0,解码时忽略 |
H | 3 | channel configuration | 声道配置 0: Defined in AOT Specifc Config 1: 1 channel : front - center 2 : 2 channels : front - left, front - right 3 : 3 channels : front - center, front - left, front - right ... |
I | 1 | originality | 设置为1表示音频的原创性,否则设置为0 |
J | 1 | home | 编码是设置为0,解码时忽略 |
K | 1 | copyright id bit | 版权ID位 |
L | 1 | copyright id start | 通过设置1和0来表示此帧的版权ID位的第一位 |
M | 13 | frame length | 一个ADTS帧的⻓度,包括ADTS头和AAC原始流 |
O | 11 | buffer fullness | 缓冲区充满度,0x7FF说明是码率可变的码流,不需要此字段。CBR可能需要此字段,不同编码器使用情况不同 |
P | 2 | num raw data blocks | ADTS帧的AAC帧数(原始数据块)减1.为了获取最大的兼容性,始终为每个ADTS帧使用一个AAC帧 |
Q | 16 | crc | 如果CRC校验标识为0,进行CRC检查。 |
3.2 解析文件的报头信息
下面的程序会使用ffmpeg库解析AAC文件的报头信息并打印出来
#include <stdio.h>
#include <libavformat/avformat.h>void print_adts_header(const uint8_t *header) {int syncword = (header[0] << 4) | (header[1] >> 4);int id = (header[1] >> 3) & 0x01;int layer = (header[1] >> 1) & 0x03;int protection_absent = header[1] & 0x01;int profile = (header[2] >> 6) & 0x03;int sampling_frequency_index = (header[2] >> 2) & 0x0F;int private_bit = (header[2] >> 1) & 0x01;int channel_configuration = ((header[2] & 0x01) << 2) | (header[3] >> 6);int original_copy = (header[3] >> 5) & 0x01;int home = (header[3] >> 4) & 0x01;int copyright_identification_bit = (header[3] >> 3) & 0x01;int copyright_identification_start = (header[3] >> 2) & 0x01;int frame_length = ((header[3] & 0x03) << 11) | (header[4] << 3) | (header[5] >> 5);int adts_buffer_fullness = ((header[5] & 0x1F) << 6) | (header[6] >> 2);int number_of_raw_data_blocks_in_frame = header[6] & 0x03;printf("Syncword: 0x%X\n", syncword);printf("ID: %d\n", id);printf("Layer: %d\n", layer);printf("Protection absent: %d\n", protection_absent);printf("Profile: %d\n", profile);printf("Sampling frequency index: %d\n", sampling_frequency_index);printf("Private bit: %d\n", private_bit);printf("Channel configuration: %d\n", channel_configuration);printf("Original/copy: %d\n", original_copy);printf("Home: %d\n", home);printf("Copyright identification bit: %d\n", copyright_identification_bit);printf("Copyright identification start: %d\n", copyright_identification_start);printf("Frame length: %d\n", frame_length);printf("ADTS buffer fullness: %d\n", adts_buffer_fullness);printf("Number of raw data blocks in frame: %d\n", number_of_raw_data_blocks_in_frame);
}int main(int argc, char *argv[]) {if (argc < 2) {fprintf(stderr, "Usage: %s <input.aac>\n", argv[0]);return -1;}av_register_all();AVFormatContext *fmt_ctx = NULL;if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) < 0) {fprintf(stderr, "Could not open input file '%s'\n", argv[1]);return -1;}if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");avformat_close_input(&fmt_ctx);return -1;}AVPacket pkt;av_init_packet(&pkt);while (av_read_frame(fmt_ctx, &pkt) >= 0) {if (pkt.stream_index == 0) { // Assuming the first stream is audioprint_adts_header(pkt.data);break;}av_packet_unref(&pkt);}avformat_close_input(&fmt_ctx);return 0;
}
执行效果为:
3.2 读取文件的ADTS帧数
下面的程序会使用FFmpeg库读取AAC文件中所有ADTS的帧,并打印其数量和帧率。
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>int main(int argc, char *argv[]) {if (argc < 2) {printf("Usage: %s <input file>\n", argv[0]);return -1;}const char *input_filename = argv[1];AVFormatContext *format_ctx = NULL;AVCodecContext *codec_ctx = NULL;AVPacket packet;int ret, stream_index;av_register_all();// Open input file and allocate format contextif (avformat_open_input(&format_ctx, input_filename, NULL, NULL) < 0) {fprintf(stderr, "Could not open input file '%s'\n", input_filename);return -1;}// Retrieve stream informationif (avformat_find_stream_info(format_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");return -1;}// Find the first audio streamstream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);if (stream_index < 0) {fprintf(stderr, "Could not find audio stream in the input file\n");return -1;}codec_ctx = format_ctx->streams[stream_index]->codec;// Initialize packetav_init_packet(&packet);packet.data = NULL;packet.size = 0;// Read frames and print informationint frame_count = 0;while (av_read_frame(format_ctx, &packet) >= 0) {if (packet.stream_index == stream_index) {frame_count++;printf("Frame %d: size=%d, sample_rate=%d\n", frame_count, packet.size, codec_ctx->sample_rate);}av_packet_unref(&packet);}// Clean upavcodec_close(codec_ctx);avformat_close_input(&format_ctx);return 0;
}
4.OGG文件格式
参考链接:
- OGG官方文档:Ogg Documentation
OGG 是由 Xiph.Org Foundation 维护的免费开放容器格式。OGG 格式的作者表示,它不受软件专利的限制,旨在提供高质量的数字多媒体的高效流式处理和操作。它用途广泛且灵活,旨在容纳不同类型的媒体数据,如音频、视频、文本和元数据。OGG 用于流式传输和本地播放,并以其开源性质而闻名。对于音频而言,OGG支持的音频格式有:
有损
- Speex:以低比特率 (~2.1–32 kbit/s/通道) 处理语音数据
- Vorbis:以中高级可变比特率(每通道 ≈16–500 kbit/s)处理一般音频数据
- Opus:以低和高可变比特率(每通道 ≈6–510 kbit/s)处理语音、音乐和通用音频
无损
- FLAC 处理存档和高保真音频数据。
- OggPCM 允许在 Ogg 容器中存储标准未压缩的 PCM 音频。
当然OGG是支持视频格式的封装,这里暂时不探讨。
4.1 文件格式
OGG文件是由一个个大小可变的页(Page)组成,页的大小通常为4-8KB,最大为65307字节。每个页中包含被拆分的数据包,由于数据包可能会很大,所以可能一个数据包被拆分到不同的页中存储。所以每个页是由页的头部(page header)和被拆分的包数据组成。
下面我们参考官方文档了解其封装过程:
- 编码后的逻辑码流会分包给OGG,具体分包的大小由编码格式而定
- 每个包会被分割为若干段,每段通常为255字节,最后一段可能小于255字节。
- 将一组连续的段打包成一个页(Page)。每个页都有一个页头(Page Header),包含页序号、版本号、标志位等。
- 将多个页按照顺序组合成一个物理比特流。
4.2 页面结构
类型 | 长度(bits) | 描述 |
---|---|---|
Capture pattern (捕获模式) | 32 | 捕获模式或同步代码,表示页面开始,用于确保在解析 Ogg 文件时同步。每个页面都以四个 ASCII 字符序列 “OggS” 开头。这有助于在数据丢失或损坏的情况下重新同步解析器,并且是在开始解析页面结构之前进行健全性检查。 |
Version(版本) | 8 | 此字段表示 Ogg 文件格式的版本,以允许将来扩展。目前强制要求为 0。 |
Header Type (头部类型) | 8 | 标志位,用于表示页面的类型。 位 值 标志位 页面类型 0 0x0 延续 此页面的第一个packet是前一个packet的延续 1 0x2 BOS 比特流的开始,表示此页面位比特流的第一页 2 0x4 EOS 比特流的结束,表示此页面位比特流的最后一页 |
Granule position(位置信息) | 64 | 位置信息字段,对于音频流保存此页面的编码后PCM样本数;对于视频流保存此页面的编码后视频帧的总数。 |
Bitstream serial number(比特流序列号) | 32 | 比特流序列号,包含唯一序列号的4字节字段,用于将 page 标识为属于特定逻辑比特流 |
Page sequence number(页面序列号) | 32 | 页面序列号,包含唯一序列号的4字节字段,每个序列号随着逻辑比特流单调递增,假设第一页位0,第二页为1,依次类推。 |
Checksum(校验和) | 32 | 校验和,此字段提供整个页面(包括页眉,在校验和字段设置为 0 的情况下计算)中数据的 CRC32校验和。这允许验证数据自创作以来是否未损坏。未通过校验和的页面应被丢弃。校验和是使用多项式值 0x04C11DB7 生成的。 |
Page Segments(页段) | 8 | 页段,表示此页面存在的区段数量。它还指示此字段后面的区段表中有多少字节。任何一个页面中最多可以有 255 个区段。 |
Segment table(段表) | n | 段表是一个 8 位值的数组,每个值指示页面正文中相应段的长度。区段数由 preceding page segments 字段确定。 |
4.3 解析OGG格式的报头信息
使用ffmpeg将pcm文件进行opus编码并使用ogg文件进行封装:
ffmpeg -f s16le -ar 44100 -ac 2 -i input.pcm -c:a libopus output.ogg
-f s16le
:指定输入格式为16位小端PCM。-ar 44100
:指定采样率为44100Hz。-ac 2
:指定音频通道数为2(立体声)。-i input.pcm
:指定输入文件为input.pcm
。-c:a libopus
:指定音频编解码器为Opus。output.ogg
:指定输出文件为output.ogg
。
下面的程序将会读取OGG文件的前5页,并打印前5页的报头信息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define ReadframNum 5// 将8字节数组转换为unsigned long long
unsigned long long convertToULL(unsigned char num[8], int len) {unsigned long long result = 0;if (len == 8) {for (int i = 0; i < len; i++) {result |= ((unsigned long long)num[i] << (i * 8));}}return result;
}// 将4字节数组转换为unsigned int
unsigned int convertToUInt(unsigned char num[4], int len) {unsigned int result = 0;if (len == 4) {for (int i = 0; i < len; i++) {result |= ((unsigned int)num[i] << (i * 8));}}return result;
}// 读取OGG文件的页面头部信息
int readOggPageHeaders(const char *oggFile) {// 定义页面头部结构typedef struct {char capturePattern[4]; // 捕获模式unsigned char version; // 版本unsigned char headerType; // 头部类型unsigned char granulePosition[8];// 粒度位置unsigned char serialNumber[4]; // 比特流序列号unsigned char pageSequence[4]; // 页面序列号unsigned char checksum[4]; // 校验和unsigned char segmentCount; // 页面段数unsigned char segmentTable[]; // 段表} OggPageHeader;// 打开OGG文件FILE *file = fopen(oggFile, "rb");if (!file) {perror("Failed to open file");return 1;}int frameCount = 0;// 循环读取前5帧的页面头部信息while (!feof(file) && frameCount < ReadframNum) {// 读取页面头部OggPageHeader pageHeader;if (1 != fread(&pageHeader, sizeof(pageHeader), 1, file))break;// 打印页面头部信息printf("Capture pattern: %c%c%c%c\n", pageHeader.capturePattern[0], pageHeader.capturePattern[1], pageHeader.capturePattern[2], pageHeader.capturePattern[3]);printf("Version: %d\n", pageHeader.version);printf("Header Type: %d\n", pageHeader.headerType);printf("Granule position: %llu\n", convertToULL(pageHeader.granulePosition, 8));printf("Bitstream serial number: %u\n", convertToUInt(pageHeader.serialNumber, 4));printf("Page sequence number: %u\n", convertToUInt(pageHeader.pageSequence, 4));printf("Checksum: %u\n", convertToUInt(pageHeader.checksum, 4));printf("Page Segments: %d\n", pageHeader.segmentCount);// 读取段表unsigned char *segmentTable = (unsigned char *)malloc(pageHeader.segmentCount);fread(segmentTable, sizeof(unsigned char), pageHeader.segmentCount, file);// 打印段表printf("Segment table: ");for (int i = 0; i < pageHeader.segmentCount; i++) {printf("%d ", segmentTable[i]);}printf("\n");// 计算段数据总大小unsigned int totalSegmentSize = 0;for (int i = 0; i < pageHeader.segmentCount; i++) {totalSegmentSize += segmentTable[i];}printf("Total Segment Size: %d\n", totalSegmentSize);// 读取段数据unsigned char *segmentData = (unsigned char *)malloc(totalSegmentSize);fread(segmentData, sizeof(unsigned char), totalSegmentSize, file);// 如果头部类型标志为4,打印最后4个字节if (pageHeader.headerType == 4)printf("Last 4 Bytes: %x %x %x %x\n", segmentData[totalSegmentSize - 4], segmentData[totalSegmentSize - 3], segmentData[totalSegmentSize - 2], segmentData[totalSegmentSize - 1]);// 释放内存free(segmentData);free(segmentTable);frameCount++;}// 关闭文件fclose(file);return 0;
}int main(int argc, char *argv[]) {// 检查命令行参数if (argc < 2) {fprintf(stderr, "Usage: %s <input.ogg>\n", argv[0]);return 1;}// 读取OGG文件的页面头部信息return readOggPageHeaders(argv[1]);
}
5.FLAC文件格式
参考资料:
- FLAC 格式:https://xiph.org/flac/format.html
- FLAC音频编码规范:https://datatracker.ietf.org/doc/draft-ietf-cellar-flac/
FLAC,全称为 Free Lossless Audio Codec,是一种无损音频压缩格式。它的主要特点是能够在保持原始音频质量的同时大幅减少文件大小,通常可以达到原始文件大小的 50% 至 70% 左右。
其编码算法流程:
- 输入音频被分割成块。如果音频包含多个声道,则每个声道将单独编码为一个子块。
- 编码器尝试通过拟合简单的多项式或通过一般线性预测编码来找到块的良好数学近似值。然后编写近似值的描述,其长度仅为几个字节。
- 近似值和输入值之间的差异(称为残差)使用 Rice 编码进行编码。在许多情况下,与使用脉冲编码调制相比,近似值和编码残差的描述占用的空间更少。
编码后的音频被划分为多个帧,每个帧由一个标头、一个元数据块和一个音频编码数据和组成。每个帧都是彼此独立的编码。帧标头以同步字开头,用于标识有效帧的开头。标头的其余部分包含样本数、帧位置、通道分配以及可选的采样率和位深度。数据块包含音频信息。
文件头(File Header)
标志(Magic Number):FLAC文件的开头是一个4字节的标志“fLaC”,用于标识文件类型。
元数据块区域(Metadata Block Area)
元数据块区域包含描述FLAC音频流的信息以及一些附加信息。每个元数据块都有一个类型标识符和长度字段,以下是常见的元数据块类型:
- 流信息块(STREAMINFO):包含整个音频流的基本信息,如采样率、声道数、总采样数等。这是FLAC文件中必须存在的第一个元数据块。
- 填充块(PADDING):用于在文件中预留空间,以便以后添加元数据。
- 应用程序数据块(APPLICATION):包含特定应用程序的信息。
- 定位表块(SEEKTABLE):保存快速定位点,用于快速跳转到文件中的特定位置。
- 标签信息块(VORBIS_COMMENT):存储一系列可读的“名/值”键值对,通常用于存储标签信息。
- 索引表块(CUESHEET):存储用于CD刻录的索引信息。
- 图片块(PICTURE):保存相关图片信息,如专辑封面。
音频帧区域(Audio Frame Area)
音频帧区域包含实际的音频数据,每个音频帧由帧头、子帧和帧尾组成:
- 帧头(Frame Header):记录帧的相关信息,如同步码、采样率、声道数、采样深度等。
- 子帧(Subframe):每个子帧对应一个声道的数据,包含子帧头和编码后的音频数据。
- 帧尾(Frame Footer):包含CRC-16校验码,用于校验帧数据的完整性。
参考链接:FLAC - Format
相关文章:

音频文件格式——AAC、OGG和FLAC
3.AAC文件格式 3.1 封装格式解析 高级音频编码 (Advanced Audio Coding) 是一种用于有损数字音频压缩的音频编码标准。它被设计为 MP3 格式的继承者,在相同比特率下通常可以获得比 MP3 更高的音质。AAC有两种封装格式: ADIF&am…...

BUU26 [极客大挑战 2019]HardSQL1
输入一些SQL关键词,发现空格,,union,and,by都被过滤了 被过滤,就用like替代 发现登录成功,可以注入 报错注入 注意 1.这里过滤了空格,就用()将内容包裹起来 比如说:…...

多光谱成像技术在华为Mate70系列的应用
华为Mate70系列搭载了光谱技术的产物——红枫原色摄像头,这是一款150万像素的多光谱摄像头。 相较于普通摄像头,它具有以下优势: 色彩还原度高:色彩还原准确度提升约 120%,能捕捉更多光谱信息,使拍摄照片色…...

借助 Cursor 快速实现小程序前端开发
借助 Cursor 快速实现小程序前端开发 在当今快节奏的互联网时代,小程序因其便捷性、高效性以及无需下载安装的特点,成为众多企业和开发者关注的焦点。然而,小程序的开发往往需要耗费大量的时间和精力,尤其是在前端开发阶段。幸运…...

【deepseek】ollama chatbox webui 本地部署deepseek 踩坑记录
部署 1、前往Ollama官网下载跨平台工具 官网直达:https://ollama.com/download 2、挑选适合自己设备的模型版本,获取运行指令 访问模型库:https://ollama.com/library/deepseek-r1 ▌配置建议: • 入门级:1.5B版本&…...

在离线的服务器上部署Python的安装库
在离线服务器上部署 Python 安装库(如 SQLAlchemy、pandas、pyodbc 等),可以使用以下方法: 方法 1:在联网机器上下载依赖,拷贝到离线服务器 适用于:服务器完全无法访问互联网。 步骤 1. 在联网…...

计算机网络笔记再战——理解几个经典的协议2
理解互联网与TCP/IP 下面,我们将会开始理解互联网这个东西,进一步的,我们会理解何为TCP/IP 我们的互联网就是一个巨大的网状结构,需要注意的是——每一个网状的节点之间都是使用一个叫做NOC,Network Operation Center…...

设计高效的测试用例:从需求到验证
在现代软件开发过程中,测试用例的设计一直是质量保证(QA)环节的核心。有效的测试用例不仅能够帮助发现潜在缺陷,提升软件质量,还能降低后期修复成本,提高开发效率。尽管如此,如何从需求出发&…...

git reset 命令
git reset 的作用 git reset 是一个非常强大的命令,用于将当前分支的 HEAD(即当前指向的提交)重置到指定的提交。它还可以根据参数的不同,对工作区(Working Directory)和暂存区(Staging Area&a…...

docker被“遗忘”的那些参数该如何拯救
一、docker容器启动时没有指定端口,如何在不删除容器的情况下配置端口呢 在 Docker 中,如果容器启动时没有指定端口映射,可以通过以下步骤在不删除容器的情况下配置端口: 方法 1: 使用 docker commit 和 docker run 提交容器为新…...

BFS算法——广度优先搜索,探索未知的旅程(下)
文章目录 前言一. N叉树的层序遍历1.1 题目链接:https://leetcode.cn/problems/n-ary-tree-level-order-traversal/description/1.2 题目分析:1.3 思路讲解:1.4 代码实现: 二. 二叉树的锯齿形层序遍历2.1 题目链接:htt…...

Python分享20个Excel自动化脚本
在数据处理和分析的过程中,Excel文件是我们日常工作中常见的格式。通过Python,我们可以实现对Excel文件的各种自动化操作,提高工作效率。 本文将分享20个实用的Excel自动化脚本,以帮助新手小白更轻松地掌握这些技能。 1. Excel单…...

pytest+request+yaml+allure 接口自动化测试全解析[手动写的跟AI的对比]
我手动写的:Python3:pytest+request+yaml+allure接口自动化测试_request+pytest+yaml-CSDN博客 AI写的:pytest+request+yaml+allure 接口自动化测试全解析 在当今的软件开发流程中,接口自动化测试扮演着至关重要的角色。它不仅能够提高测试效率,确保接口的稳定性和正确性…...

深入解析 FFmpeg 的 AAC 编解码过程
深入解析 FFmpeg 的 AAC 编解码过程 —— 技术详解与代码实现 AAC(Advanced Audio Coding) 是一种高效的有损音频压缩格式,因其高压缩效率和良好的音质而被广泛应用于流媒体、广播和音频存储等领域。FFmpeg 是一个强大的多媒体处理工具,支持 AAC 的编码和解码。本文将详细…...

嵌入式硬件篇---OpenMV串口通信json字符串
文章目录 前言第一部分:Json字符串通信协议优点缺点 Json优点缺点编码与解码 第二部分:UART串口通信UART常用函数注意 总结 前言 以上就是今天要讲的内容,本文简单介绍了Json字符串、UART串口通信。 第一部分:Json字符串 通信协议 在传统的单片机应用中ÿ…...

Python基于Django的课堂投票系统的设计与实现【附源码】
博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...

蓝桥杯 Java 之输入输出
一、输入输出方式:Scanner vs BufferedReader Scanner类 简介:Scanner 是 Java 中一个非常方便的用于读取用户输入的类,它可以从多种输入源(如标准输入、文件等)读取基本数据类型和字符串。 1. Scanner的细节与使用…...

Kubernetes是什么?为什么它是云原生的基石
从“手工时代”到“自动化工厂” 想象一下,你正在经营一家工厂。在传统模式下,每个工人(服务器)需要手动组装产品(应用),效率低下且容易出错。而Kubernetes(k8s)就像一个…...

@emotion/styled / styled-components创建带有样式的 React 组件
一、安装依赖 npm install emotion/styled styled-components 二、使用 import styled from emotion/styled; import styled from styled-components;// 创建一个带样式的按钮 const StyledButton styled.buttonbackground-color: #4caf50;color: white;padding: 10px 20px…...

Android 常用命令和工具解析之Battery Historian
Batterystats是包含在 Android 框架中的一种工具,用于收集设备上的电池数据。您可以使用adb bugreport命令抓取日志,将收集的电池数据转储到开发机器,并生成可使用 Battery Historian 分析的报告。Battery Historian 会将报告从 Batterystats…...

家用报警器的UML 设计及其在C++和VxWorks 上的实现01
M.W.Richardson 著,liuweiw 译 论文描述了如何运用 UML(统一建模语言)设计一个简单的家用报警器,并实现到 VxWorks 操作系统上。本文分两个部分,第一部分描述了如何用 UML 设计和验证家用报警器的模型,以使…...

k8s常见面试题2
k8s常见面试题2 安全与权限RBAC配置如何保护 Kubernetes 集群的 API Server?如何管理集群中的敏感信息(如密码、密钥)?如何限制容器的权限(如使用 SecurityContext)?如何防止容器逃逸࿰…...

CSS 伪类(Pseudo-classes)的详细介绍
CSS 伪类详解与示例 在日常的前端开发中,CSS 伪类可以帮助我们非常精准地选择元素或其特定状态,从而达到丰富页面表现的目的。本文将详细介绍以下伪类的使用: 表单相关伪类 :checked、:disabled、:enabled、:in-range、:invalid、:optional、…...

将Deepseek接入pycharm 进行AI编程
目录 专栏导读1、进入Deepseek开放平台创建 API key 2、调用 API代码 3、成功4、补充说明多轮对话 总结 专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️🌈 博客主页:请点击——…...

【Ollama】一、介绍
介绍 Ollama 是一个开源项目,专注于提供本地化的大型语言模型(LLM)部署和运行解决方案。它允许用户在本地环境中轻松运行和微调各种开源语言模型(如 LLaMA、Falcon 等),而无需依赖云服务或高性能 GPU。Oll…...

ASP.NET Core JWT
目录 Session的缺点 JWT(Json Web Token) 优点: 登录流程 JWT的基本使用 生成JWT 解码JWT 用JwtSecurityTokenHandler对JWT解码 注意 Session的缺点 对于分布式集群环境,Session数据保存在服务器内存中就不合适了&#…...

查询引擎:它们是什么以及为什么重要
了解查询引擎、它们的优势以及如何简化现代应用程序的数据管理。查询引擎是高效处理和检索数据的强大工具,但并非所有查询引擎都能满足现代应用程序对速度和实时性的需求。在本文中,我们将解析查询引擎的定义、主要优势以及它们如何用于实时数据和AI应用…...

03/29 使用 海康SDK 对接时使用的 MysqlUtils
前言 最近朋友的需求, 是需要使用 海康sdk 连接海康设备, 进行数据的获取, 比如 进出车辆, 进出人员 这一部分是 资源比较贫瘠时的一个 Mysql 工具类 测试用例 public class MysqlUtils {public static String MYSQL_HOST "192.168.31.9";public static int MY…...

2025.2.7 Python开发岗面试复盘
2025.2.7 Python开发岗面试复盘 问题: 是否了解过其他语言? 了解过Java、JavaScript、C等语言,但主要技术栈是Python。 Python跟Java的区别? Python是解释型语言,Java是编译型语言 Python动态类型,Java静态类型 Python简洁易读,Java相对严谨复杂 Python GIL限制并发,Java并…...

一个sql只能有一个order by
ORDER BY 子句在 SQL 中只能出现一次,静态部分和动态部分只能写一个 ORDER BY...