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

iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)

iOS视频编码详细步骤流程

1. 视频采集阶段

视频采集所使用的代码和之前的相同,所以不再过多进行赘述

  • 初始化配置
    • 通过VideoCaptureConfig设置分辨率1920x1080、帧率30fps、像素格式kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
    • 设置摄像头位置(默认前置)和镜像模式
  • 授权与初始化
    • 检查并请求相机权限
    • 创建AVCaptureSession会话
    • 配置摄像头输入源AVCaptureDeviceInput
    • 设置视频输出AVCaptureVideoDataOutput
    • 创建预览层AVCaptureVideoPreviewLayer
  • 数据回调
    • 实现AVCaptureVideoDataOutputSampleBufferDelegate接收视频帧
    • 通过sampleBufferOutputCallBack传递CMSampleBuffer

2. 视频编码准备

  • 编码参数配置
    • 创建KFVideoEncoderConfig对象
    • 设置分辨率1080x1920、码率5Mbps、帧率30fps、GOP帧数150帧
    • 检测设备支持情况,优先选择HEVC,不支持则降级为H264
    • 设置相应编码Profile(H264使用High Profile,HEVC使用Main Profile)
//
//  KFVideoEncoderConfig.swift
//  VideoDemo
//
//  Created by ricard.li on 2025/5/14.
//import Foundation
import AVFoundation
import VideoToolboxclass KFVideoEncoderConfig {/// 分辨率var size: CGSize/// 码率 (bps)var bitrate: Int/// 帧率 (fps)var fps: Int/// GOP 帧数 (关键帧间隔)var gopSize: Int/// 是否启用 B 帧var openBFrame: Bool/// 编码器类型var codecType: CMVideoCodecType/// 编码 profilevar profile: Stringinit() {self.size = CGSize(width: 1080, height: 1920)self.bitrate = 5000 * 1024self.fps = 30self.gopSize = self.fps * 5self.openBFrame = truevar supportHEVC = falseif #available(iOS 11.0, *) {// 注意 Swift 中直接调用 VTIsHardwareDecodeSupportedsupportHEVC = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)}if supportHEVC {self.codecType = kCMVideoCodecType_HEVCself.profile = kVTProfileLevel_HEVC_Main_AutoLevel as String} else {self.codecType = kCMVideoCodecType_H264self.profile = AVVideoProfileLevelH264HighAutoLevel}}
}
  • 编码器初始化
    • 创建KFVideoEncoder实例
    • 创建VTCompressionSession编码会话
    • 配置属性:kVTCompressionPropertyKey_RealTimekVTCompressionPropertyKey_ProfileLevel
    • 设置码率控制、GOP大小、帧率等参数
    • 配置编码回调函数
//
//  KFVideoEncoder.swift
//  VideoDemo
//
//  Created by ricard.li on 2025/5/14.
//import Foundation
import AVFoundation
import VideoToolbox
import UIKit/// 视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265
class KFVideoEncoder {/// 编码会话private var compressionSession: VTCompressionSession?/// 编码配置private(set) var config: KFVideoEncoderConfig/// 编码专用队列,避免线程竞争private let encoderQueue = DispatchQueue(label: "com.KeyFrameKit.videoEncoder")/// 用于串行化的信号量
//    private let semaphore = DispatchSemaphore(value: 1)/// 是否需要刷新 session(比如进入后台后)private var needRefreshSession = false/// 重试创建 session 计数private var retrySessionCount = 0/// 编码失败的帧计数private var encodeFrameFailedCount = 0/// 编码成功后的 SampleBuffer 回调var sampleBufferOutputCallBack: ((CMSampleBuffer) -> Void)?/// 错误回调var errorCallBack: ((Error) -> Void)?/// 最大允许重试 session 创建次数private let maxRetrySessionCount = 5/// 最大允许编码失败帧数private let maxEncodeFrameFailedCount = 20/// 初始化init(config: KFVideoEncoderConfig) {self.config = configNotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)}deinit {NotificationCenter.default.removeObserver(self)
//        semaphore.wait()releaseCompressionSession()
//        semaphore.signal()}/// 标记需要刷新 sessionfunc refresh() {needRefreshSession = true}/// 强制刷新编码器(不带完成回调)func flush() {encoderQueue.async { [weak self] inguard let self = self else { return }
//            self.semaphore.wait()self.flushInternal()
//            self.semaphore.signal()}}/// 强制刷新编码器(带完成回调)func flush(withCompleteHandler handler: @escaping () -> Void) {encoderQueue.async { [weak self] inguard let self = self else { return }
//            self.semaphore.wait()self.flushInternal()
//            self.semaphore.signal()handler()}}/// 编码单帧视频func encode(pixelBuffer: CVPixelBuffer, ptsTime: CMTime) {guard retrySessionCount < maxRetrySessionCount, encodeFrameFailedCount < maxEncodeFrameFailedCount else { return }encoderQueue.async { [weak self] inguard let self = self else { return }
//            self.semaphore.wait()var setupStatus: OSStatus = noErr/// 检查 session 是否需要重建if self.compressionSession == nil || self.needRefreshSession {self.releaseCompressionSession()setupStatus = self.setupCompressionSession()self.retrySessionCount = (setupStatus == noErr) ? 0 : (self.retrySessionCount + 1)if setupStatus != noErr {print("KFVideoEncoder setupCompressionSession error: \(setupStatus)")self.releaseCompressionSession()} else {self.needRefreshSession = false}}guard let session = self.compressionSession else {
//                self.semaphore.signal()if self.retrySessionCount >= self.maxRetrySessionCount {DispatchQueue.main.async {self.errorCallBack?(NSError(domain: "\(KFVideoEncoder.self)", code: Int(setupStatus), userInfo: nil))}}return}var flags: VTEncodeInfoFlags = []/// 编码当前帧let encodeStatus = VTCompressionSessionEncodeFrame(session, imageBuffer: pixelBuffer, presentationTimeStamp: ptsTime, duration: CMTime(value: 1, timescale: CMTimeScale(self.config.fps)), frameProperties: nil, sourceFrameRefcon: nil, infoFlagsOut: &flags)/// 检测 session 异常,尝试重建if encodeStatus == kVTInvalidSessionErr {self.releaseCompressionSession()setupStatus = self.setupCompressionSession()self.retrySessionCount = (setupStatus == noErr) ? 0 : (self.retrySessionCount + 1)if setupStatus == noErr {_ = VTCompressionSessionEncodeFrame(session, imageBuffer: pixelBuffer, presentationTimeStamp: ptsTime, duration: CMTime(value: 1, timescale: CMTimeScale(self.config.fps)), frameProperties: nil, sourceFrameRefcon: nil, infoFlagsOut: &flags)} else {self.releaseCompressionSession()}print("KFVideoEncoder kVTInvalidSessionErr")}/// 编码失败计数if encodeStatus != noErr {print("KFVideoEncoder VTCompressionSessionEncodeFrame error: \(encodeStatus)")}self.encodeFrameFailedCount = (encodeStatus == noErr) ? 0 : (self.encodeFrameFailedCount + 1)//            self.semaphore.signal()/// 达到最大失败次数,触发错误回调if self.encodeFrameFailedCount >= self.maxEncodeFrameFailedCount {DispatchQueue.main.async {self.errorCallBack?(NSError(domain: "\(KFVideoEncoder.self)", code: Int(encodeStatus), userInfo: nil))}}}}/// 进入后台,标记 session 需要刷新@objc private func didEnterBackground() {needRefreshSession = true}/// 创建编码会话private func setupCompressionSession() -> OSStatus {var session: VTCompressionSession?let status = VTCompressionSessionCreate(allocator: nil,width: Int32(config.size.width),height: Int32(config.size.height),codecType: config.codecType,encoderSpecification: nil,imageBufferAttributes: nil,compressedDataAllocator: nil,outputCallback: { (outputCallbackRefCon, _, status, infoFlags, sampleBuffer) inguard let sampleBuffer = sampleBuffer else {if infoFlags.contains(.frameDropped) {print("VideoToolboxEncoder kVTEncodeInfo_FrameDropped")}return}/// 将 sampleBuffer 通过回调抛出let encoder = Unmanaged<KFVideoEncoder>.fromOpaque(outputCallbackRefCon!).takeUnretainedValue()encoder.sampleBufferOutputCallBack?(sampleBuffer)},refcon: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),compressionSessionOut: &session)if status != noErr {return status}guard let compressionSession = session else { return status }self.compressionSession = compressionSession/// 设置基本属性VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_ProfileLevel, value: config.profile as CFString)VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_AllowFrameReordering, value: config.openBFrame as CFTypeRef)/// 针对 H264,设置 CABACif config.codecType == kCMVideoCodecType_H264 {VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_H264EntropyMode, value: kVTH264EntropyMode_CABAC)}/// 设置像素转换属性let transferDict: [String: Any] = [kVTPixelTransferPropertyKey_ScalingMode as String: kVTScalingMode_Letterbox]VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_PixelTransferProperties, value: transferDict as CFTypeRef)/// 设置码率VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_AverageBitRate, value: config.bitrate as CFTypeRef)/// 针对 H264 且不支持 B 帧,限制数据速率if !config.openBFrame && config.codecType == kCMVideoCodecType_H264 {let limits = [config.bitrate * 3 / 16, 1] as [NSNumber]VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_DataRateLimits, value: limits as CFArray)}/// 设置帧率、GOPVTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: config.fps as CFTypeRef)VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: config.gopSize as CFTypeRef)VTSessionSetProperty(compressionSession, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: (Double(config.gopSize) / Double(config.fps)) as CFTypeRef)/// 准备编码return VTCompressionSessionPrepareToEncodeFrames(compressionSession)}/// 释放编码会话private func releaseCompressionSession() {if let session = compressionSession {VTCompressionSessionCompleteFrames(session, untilPresentationTimeStamp: .invalid)VTCompressionSessionInvalidate(session)self.compressionSession = nil}}/// 内部刷新逻辑private func flushInternal() {if let session = compressionSession {VTCompressionSessionCompleteFrames(session, untilPresentationTimeStamp: .invalid)}}
}

可以很容易的知道,在编码采集成功后,会有一个视频帧输出回调

在这里插入图片描述
会调用上面文件的encode方法,encode方法中,会对session回话进行配置,我们再看向session会话,如果编码成功的话,会通过闭包返回 sampleBuffer
在这里插入图片描述

3. 编码过程执行

  • 输入画面
    • 摄像头采集到CMSampleBuffer数据
    • 从中提取CVPixelBuffer和时间戳信息
  • 编码操作
    • 通过VTCompressionSessionEncodeFrame提交帧进行编码
    • 设置时间戳、帧持续时间等属性
    • 支持编码状态检查和异常处理
  • 应对中断
    • 应用进入后台时标记需刷新会话
    • 会话失效时进行重建
    • 最多重试5次,每次失败计数

4. 数据处理与存储

  • 参数集提取
    • CMFormatDescription中获取H264的SPS、PPS或HEVC的VPS、SPS、PPS
    • 检测关键帧(判断kCMSampleAttachmentKey_NotSync是否存在)
  • 格式转换
    • 原始数据为AVCC/HVCC格式:[extradata]|[length][NALU]|[length][NALU]|...
    • 转换为AnnexB格式:[startcode][NALU]|[startcode][NALU]|...
    • 添加起始码0x00000001
  • 数据写入
    • 关键帧时写入参数集(VPS、SPS、PPS)+ 帧数据
    • 普通帧只写入帧数据
    • 使用FileHandle写入到.h264/.h265文件

5. 并发与线程控制

  • 专用队列隔离
    • 采集使用captureQueue队列
    • 编码使用encoderQueue队列
    • 避免线程竞争和阻塞UI
  • 错误处理
    • 编码失败计数与阈值控制
    • 异常回调通知上层处理
    • 编码状态监控

6. 控制与交互

  • 用户界面控制
    • Start按钮:开始编码
    • Stop按钮:停止编码并刷新
    • Camera按钮:切换前后摄像头
    • 双击屏幕:快速切换摄像头

相关文章:

iOS视频编码详细步骤(视频编码器,基于 VideoToolbox,支持硬件编码 H264/H265)

iOS视频编码详细步骤流程 1. 视频采集阶段 视频采集所使用的代码和之前的相同&#xff0c;所以不再过多进行赘述 初始化配置&#xff1a; 通过VideoCaptureConfig设置分辨率1920x1080、帧率30fps、像素格式kCVPixelFormatType_420YpCbCr8BiPlanarFullRange设置摄像头位置&am…...

行项目违反范围截止值

把允许负值打钩就可以&#xff0c;如果没有此字段&#xff0c;按照下面截图把屏幕格式放字段出来&#xff1b;字段放出来以后如果是灰色的话&#xff0c;就用SE16N调试模式修改字段值&#xff1b;...

Linux wlan 单频段 dual wifi创建

环境基础 TP LINK WN722N V1网卡linux 主机 查看设备是否支持双ap managed&#xff1a;客户端模式&#xff08;连接路由器/AP&#xff09;AP&#xff1a;接入点模式&#xff08;创建热点&#xff09;AP/VLAN&#xff1a;支持带VLAN标签的虚拟AP{ AP, mesh point, P2P-GO } &l…...

STC32G12K12实战:串口通信

STC32G12K128芯片写一个按键通过串口1发送字符串的程序。首先&#xff0c;确认芯片的串口1配置。STC32G系列通常使用UART1&#xff0c;相关的寄存器是P_SW1来选择引脚。默认情况下&#xff0c;UART1的TX是P3.1。 接下来是设置定时器作为波特率发生器。通常用定时器2&#xff0c…...

unity 鼠标更换指定图标

1.准备两张图 要求图片导入设置如下 将 Texture Type 改为 Cursor 确保 Read/Write Enabled 已勾选 取消勾选 Generate Mip Maps 将 Filter Mode 设为 Point (保持清晰边缘) 将 Compression 设为 None (无压缩) 2.创建脚本&#xff0c;把脚本挂到场景中 &#xff0c;该…...

会话管理中的cookie,以及浏览器操作cookie,在express中设置,删除,和获取cookie

会话管理中的 Cookie以及在浏览器和 Express 中操作 Cookie&#xff1a; &#x1f36a; 一、Cookie 在会话管理中的作用 ✅ Cookie 的定义&#xff1a; Cookie 是服务器发送到浏览器并保存在本地的小段文本数据&#xff0c;浏览器会在之后的请求中将这些数据回传给服务器。 …...

Mac的web服务器

最近用了个人觉得比较好用的集成环境。Flyenv - FlyEnv | 一体化全栈环境管理工具. 支持macOS / Windows / Linux 链接放在这了。 mac上安装&#xff1a; brew install flyenv 等着安装成功就好了 给个图。ollama也有...

数据可视化大屏——南方草牧商品交易所

代码综述 整体结构与用途 这是一段用于构建网页的 HTML 代码&#xff0c;结合引入的 JavaScript 和 CSS 文件&#xff0c;初步推测该网页是南方草牧商品交易所的相关页面&#xff0c;主要展示了与草牧商品交易相关的各类数据&#xff0c;包括产品挂牌数、成交量、会员信息等&…...

AIGC与数字媒体实验室解决方案分享

第1部分 概述 1.1 建设目标 1.深度融合AIGC技术&#xff0c;培养能够驾驭新质生产力的数字媒体人才 通过引入前沿的AIGC技术&#xff0c;确保学生能够接触到最先进的人工智能应用。教学内容理论和实践结合&#xff0c;让学生在实际操作中熟练掌握AIGC工具&#xff0c;生成高…...

Qwen3模型架构、训练方法梳理

qwen3炼丹真是全是技巧&#xff0c;下面来看看&#xff0c;仅供参考。 https://huggingface.co/Qwen https://modelscope.cn/organization/qwen https://github.com/QwenLM/Qwen3 模型架构 Dense 模型结构改进&#xff1a; GQA、SwiGLU、RoPE、RMSNorm with pre-normalization…...

MySQL 学习(九)bin log 与 redo log 的区别有哪些,为什么快速恢复使用 redo log 而不用 bin log?

目录 一、bin log 与 redo log 的区别1&#xff09;实现方式不同&#xff1a;2&#xff09;日志内容不同&#xff1a;3&#xff09;记录方式不同&#xff1a;4&#xff09;使用场合不同&#xff1a; 二、为什么快速恢复使用 redo log 而不用 bin log&#xff1f; 面试题&#x…...

【消息队列】RabbitMQ基本认识

目录 一、基本概念 1. 生产者&#xff08;Producer&#xff09; 2. 消费者&#xff08;Consumer&#xff09; 3. 队列&#xff08;Queue&#xff09; 4. 交换器&#xff08;Exchange&#xff09; 5. 绑定&#xff08;Binding&#xff09; 6. 路由键&#xff08;Routing …...

RabbitMQ 快速上手:安装配置与 HelloWorld 实践(二)

四、RabbitMQ 配置 4.1 用户及权限管理 在 RabbitMQ 中&#xff0c;用户管理是保障系统安全和正常运行的重要环节。通过合理的用户及权限设置&#xff0c;可以确保不同的应用或服务在使用 RabbitMQ 时&#xff0c;只能访问其被授权的资源&#xff0c;避免非法操作和数据泄露。…...

蓝桥杯 17. 修改数组

修改数组 原题目链接 题目描述 给定一个长度为 N 的数组 A [A1, A2, …, AN]&#xff0c;数组中可能包含重复的整数。 现在小明要按以下方法将其修改为没有重复整数的数组&#xff1a; 小明会依次修改 A2, A3, …, AN。 当修改 Ai 时&#xff0c;小明会检查 Ai 是否在 A…...

Redis内存淘汰策略和过期键删除策略有哪些?

Redis 提供 8 种内存淘汰策略&#xff0c;以下是详细解析及场景建议&#xff1a; 一、核心策略解析 noeviction (默认策略) 机制&#xff1a;内存满时拒绝新写入操作&#xff0c;返回错误优势&#xff1a;绝对数据安全场景&#xff1a;金融交易系统、医疗数据存储 allkeys-lr…...

基于 NanoDet 的工厂巡检机器人目标识别系统研究与实现​

摘要​ 本论文旨在设计并实现一个基于 NanoDet 模型的工厂巡检机器人目标识别系统。通过详细阐述数据集准备、模型训练、UI 界面构建以及系统部署的全过程&#xff0c;展示如何利用 NanoDet 模型的高效性和轻量化特点&#xff0c;结合合理的 UI 设计与数据集优化&#xff0c;使…...

深入解析Spring Security:JWT认证与授权实践

深入解析Spring Security&#xff1a;JWT认证与授权实践 引言 在现代Web应用中&#xff0c;安全性是至关重要的。Spring Security作为Spring生态中的安全框架&#xff0c;提供了强大的认证和授权功能。本文将重点介绍如何使用Spring Security结合JWT&#xff08;JSON Web Tok…...

《隐私计算:数据安全与隐私保护的新希望》

一、引言 在数字化时代&#xff0c;数据已成为企业和组织的核心资产。然而&#xff0c;数据的收集、存储和使用过程中面临着诸多隐私和安全挑战。隐私计算作为一种新兴技术&#xff0c;旨在解决数据隐私保护和数据共享之间的矛盾。本文将深入探讨隐私计算的基本概念、技术原理、…...

Elasticsearch索引设计与调优

一、分片策略设计 1.‌分片容量规划 单分片容量建议30GB(日志场景可放宽至100GB),避免超大分片引发查询延迟。分片总数计算公式:总数据量 / 30GB 1.2(20%余量应对未来增长)。主分片数创建后不可修改,副本分片数支持动态调整。2.‌分片分布优化 PUT logs-2025 { &qu…...

什么是im即时通讯?私有化im即时通讯软件优势是什么?

在当前企业高度关注数据主权的背景下&#xff0c;私有化IM即时通讯软件&#xff08;如BeeWorks&#xff09;的部署模式成为企业实现数据自主控制、规避外部风险的核心解决方案。以下是针对企业数据主权诉求的私有化IM软件优势深度解析&#xff1a; 一、数据主权诉求下的私有化I…...

【unity游戏开发——编辑器扩展】EditorWindow自定义unity窗口拓展

注意&#xff1a;考虑到编辑器扩展的内容比较多&#xff0c;我将编辑器扩展的内容分开&#xff0c;并全部整合放在【unity游戏开发——编辑器扩展】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言一、自定义窗口1、创建窗口类2、创建窗口对象3、显示窗…...

深入理解Java HotSpot中的即时编译

即时编译(Just-In-Time Compilation,简称JIT)是Java虚拟机(JVM)的核心技术之一,通过在运行时将字节码转换为本地机器码,显著提升了Java应用程序的性能。本文基于HotSpot JVM,详细探讨了JIT编译的工作原理、优化技术、日志分析以及最新的编译器发展,旨在帮助开发者更好…...

【教程】Docker方式本地部署Overleaf

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 背景说明 下载仓库 初始化配置 修改监听IP和端口 自定义网站名称 修改数据存放位置 更换Docker源 更换Docker存储位置 启动Overleaf 创…...

解决docker alpine缺少字体的问题 Could not initialize class sun.awt.X11FontManager

制作的springboot项目镜像&#xff0c;缺少字体报错Could not initialize class sun.awt.X11FontManager 原因镜像中缺少字体 解决&#xff1a; 制作镜像时&#xff0c;添加字体库&#xff0c;Dockerfile文件 中添加如下内容 注意&#xff1a; jdk版本一定要使用&#xff0…...

深度解析智能体:从概念到应用的全方位洞察

在科技日新月异的当下&#xff0c;智能体已从人工智能的前沿概念逐渐走进大众视野&#xff0c;成为备受瞩目的焦点。那么&#xff0c;究竟何为智能体呢&#xff1f; 一、智能体的概念 智能体&#xff0c;简单来讲&#xff0c;可看作是“大模型 超级工具人”的有机融合。它能够…...

python 异步执行多个 python 命令

前提说明&#xff1a; 1. 有N套用 python 爬取客户不同网站上的评论的程序 2. 有一套用 python 将评论出成 Excel 报表的程序 新需求&#xff1a; 做一个总入口&#xff0c;异步同时爬不同网站评论&#xff0c;或同时出不同月份报表&#xff0c;或爬虫做报表同时做的 # -…...

Keil5 MDK 安装教程

## 简介 Keil MDK&#xff08;Microcontroller Development Kit&#xff09;是ARM开发的一款集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于ARM Cortex-M系列微控制器的开发。MDK包含了μVision IDE和调试器、ARM C/C编译器、中间件组件等工具。本教程将指导您完…...

Doris与ClickHouse深度比较

一、核心架构差异 分布式管理 Doris 采用 FE&#xff08;前端&#xff09;与 BE&#xff08;后端&#xff09;分离的 MPP 架构&#xff0c;FE 负责元数据管理和查询规划&#xff0c;BE 处理存储与计算&#xff0c;支持自动扩缩容和故障恢复。这种设计简化了集群管理&#xff0c…...

插入排序希尔排序

插入排序&希尔排序 插入排序&#xff1a;将数据整体当做一组&#xff0c;从头开始遍历&#xff0c;确保遍历完的子序列都是有序的&#xff1b; 希尔排序&#xff1a;基于插入排序&#xff0c;增加新的分组思想&#xff0c;对数据进行分组的插入排序。 平均时间复杂度、最快…...

windows文件共享另一台电脑资源管理器网络文件夹无法找到机器

找不到共享出来的文件夹 1 网络问题,明确两台机器是否同一局域网 2 明确两台机器在本地所选的网络类型是专用网络,还是公共网络,对应的防火墙是否关闭 3 smb协议没有启用 4 以上都设置完毕还是无法找到机器,使用ip访问 在资源管理器地址栏输入 \\<对方IP>&#xff08;…...

K8S Ingress 实现金丝雀(灰度)发布

假设有如下三个节点的 K8S 集群&#xff1a; ​ k8s31master 是控制节点 k8s31node1、k8s31node2 是工作节点 容器运行时是 containerd 一、场景分析 阅读本文&#xff0c;默认您已经安装了 Ingress Nginx。 1&#xff09;A/B 测试 A/B 测试基于用户请求的元信息将流量路由…...

Python笔记:在环境变量中增加了dll加载路径,python提示DLL加载失败

在环境变量中增加了dll加载路径&#xff0c;python提示DLL加载失败 1.问题描述 from PySide2 import QtCore 提示如下错误 ImportError: DLL load failed while importing QtCore: 找不到指定的模块。 2.问题原因 在Python3.8文档中的What’s New In Python 3.8找到如下说明…...

WinFrom 使用 LiveCharts 实现动态折线图

上方是Winfrom使用LiveCharts 插件实现的动态折线图&#xff0c;下面是实现步骤。 一、创建新项目 我这里使用的是.Net Framework4.6.1版本 二、添加引用包 这样就代表安装完成了 三、引用控件 四、逻辑代码 using LiveCharts.Defaults; using LiveCharts.Wpf; using Syst…...

Deep Learning(手写字识别 - CNN)

Deep Learning&#xff08;手写字识别&#xff09; 数据集&#xff08;MNIST&#xff09;基于 PyTorch 深度学习框架使用 CNN 算法进行手写字识别案例参考教程 数据集&#xff08;MNIST&#xff09; 数据集 Github 官网&#xff08;注&#xff1a;不知为何官网无法直接下载数据…...

HDD 安全擦除:何时以及如何在 Windows PC 上安全擦除硬盘

HDD 安全擦除是一种从硬盘驱动器 (HDD) 中永久删除所有数据的方法&#xff0c;其方式几乎无法恢复。那么&#xff0c;什么时候需要在 Windows 11/10/8/7 上安全擦除硬盘驱动器&#xff1f;如何安全地擦除硬盘驱动器&#xff1f;幸运的是&#xff0c;所有的答案都在本指南中。继…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】附录-B. 错误代码与解决方案

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 附录B. PostgreSQL错误代码与解决方案速查表一、错误代码分类速查表二、常见错误代码详解三、错误排查方法论四、错误预防最佳实践五、典型错误场景解决方案六、官方资源与工…...

3DMAX脚本病毒Spy CA查杀方法

什么是Spy CA病毒&#xff1f; Spy CA是一种新型病毒&#xff0c;可以与从在线资源或3D商店下载的第三方3D模型一起下载该病毒。 该病毒是一种Maxscript代码&#xff0c;写入对象的自定义属性&#xff0c;并在打开场景、合并模型或操纵对象时执行。 通过对带有该病毒的场景做…...

联排半孔PCB如何进行SMT贴片?

在印刷电路板业务中&#xff0c;有两种将元件安装到电路板上的主要方法:联排半孔安装和表面安装。联排半孔安装是较老的技术&#xff0c;要求电路板制造商在PCB上钻孔并将引线插入孔中。最近&#xff0c;表面安装技术已经接管了该领域。 联排半孔元件 联排半孔元件有两种引线&a…...

CSS Grid布局:从入门到实战

CSS Grid布局&#xff1a;从入门到实战 一、初识Grid布局 还在为网页布局发愁吗&#xff1f;Flexbox虽然好用&#xff0c;但当遇到复杂布局时&#xff0c;CSS Grid才是真正的王者。Grid布局是CSS中最强大的二维布局系统&#xff0c;它就像一张无形的网格纸&#xff0c;让我们…...

小说所有设定(v3.0 preview)

设定以json格式提供&#xff1a; {"2y": {"2y_jsl": {"精神力的具体能力": {"学习能力组": {"瞬间掌握知识": "可以瞬间掌握所有知识&#xff0c;无需传统学习过程。","直接读取信息": "能直接…...

主题切换方案

方案一 CSS :root 选择器匹配文档的根元素。在 HTML 文档中&#xff0c;根元素就是 <html> 标签 1、定义颜色变量 可在公共样式文件里面定义 :root {--primary-color: #007bff; //定义颜色变量 } 2、使用 JavaScript 修改 CSS 变量 document.documentElement.styl…...

一个日志量突增的问题分析处理经历

问题描述 周一早上&#xff0c;还是和往常一样的巡检。通过告警日志&#xff0c;发现了生产区集群上的一个实例周末出现了异常。 图片 1 Alert_With_Checkpoint_log 如图&#xff0c;周六下午15时开始&#xff0c;数据库在线联机日志组疯狂切换&#xff0c;检查点发生的频率也…...

C#中的dynamic与var:看似相似却迥然不同

在C#编程的世界里&#xff0c;var和dynamic这两个关键字常常让初学者感到困惑。它们看起来都在定义变量时省略了显式类型声明&#xff0c;但实际上它们的工作方式和应用场景有着天壤之别。今天&#xff0c;让我们一起揭开这两个关键字的神秘面纱。 var&#xff1a;编译时的类型…...

VS Code 新旧版本 Remote-SSH 内网离线连接服务器方法(版本 ≤ 1.78.x 及 ≥ 1.79.0)

文章目录 前言版本概览目录结构模型迭代 旧版&#xff08;版本 ≤ 1.78.x&#xff09;离线部署流程1. 问题描述2. 原理说明3. 离线部署步骤 新版&#xff08;版本 ≥ 1.79.0&#xff09;离线部署流程1. 目录结构变化2. 全局设置调整3. 离线部署步骤 常见问题 & 排查总结与建…...

【unity游戏开发——编辑器扩展】使用EditorGUI的EditorGUILayout绘制工具类在自定义编辑器窗口绘制各种UI控件

注意&#xff1a;考虑到编辑器扩展的内容比较多&#xff0c;我将编辑器扩展的内容分开&#xff0c;并全部整合放在【unity游戏开发——编辑器扩展】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言常用的EditorGUILayout控件专栏推荐完结 前言 EditorG…...

310. 最小高度树

题目 树是一个无向图&#xff0c;其中任何两个顶点只通过一条路径连接。 换句话说&#xff0c;任何一个没有简单环路的连通图都是一棵树。 给你一棵包含 n 个节点的树&#xff0c;标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表&#xff08;每一个边都…...

SpringBoot中配置绑定到bean中

一、回顾value注解 SpringBoot中我们该如何将applicaton.properties中的数据绑定到bean中呢&#xff1f;在之间我们是可以通过value注解&#xff0c;在SpringBoot自动启动后&#xff0c;会自动的去相应的路径中去寻找applicaton.properties配置文件&#xff0c;然后将相应的值…...

Linux下的c/c++开发之操作Redis数据库

C/C 操作 Redis 的常用库 在 C/C 开发中操作 Redis 有多种方式&#xff0c;最主流的选择是使用第三方客户端库。由于 Redis 官方本身是使用 C 编写的&#xff0c;提供的 API 非常适合 C/C 调用。常见的 Redis C/C 客户端库包括&#xff1a; hiredis&#xff1a;官方推荐的轻量…...

通过SMTP协议实现Linux邮件发送配置指南

一、环境准备与基础配置 1. SMTP服务开通&#xff08;以qq邮箱为例&#xff09; 登录qq邮箱网页端&#xff0c;进入「设置」-「POP3/SMTP/IMAP」 开启「SMTP服务」并获取16位授权码&#xff08;替代邮箱密码使用&#xff09; 记录关键参数&#xff1a; SMTP服务器地址&#…...

数学复习笔记 8

前言 成为一个没有感情的刷题机器就可以变得很强了。 逆矩阵的运算 随便算一下就算出来了&#xff0c;没啥难的。主要是用天然可交换的矩阵来算。有三个天然可交换的矩阵&#xff0c;某矩阵和单位阵&#xff0c;该矩阵和它的伴随矩阵&#xff0c;该矩阵和它的逆矩阵。一定要…...