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

PYNQ 框架 - VDMA驱动 - 帧缓存

目录

1. 简介

2. 代码分析

2.1 _FrameCache 类定义

2.1.1 xlnk.cma_array()

2.1.2 pointer=None

2.1.3 PynqBuffer

2.2 _FrameCache 例化与调用

2.3 _FrameCache 测试

2.4 _FrameList 类定义

2.5 _FrameList 例化与调用

2.6 _FrameList 测试

3. 帧的使用

3.1 读取帧

4. 总结


1. 简介

本文分享在 PYNQ 框架下,AXI VDMA 驱动的部分实现细节,重点分析帧缓存的管理和使用。

重点分析了 _FrameCache 和 _FrameList 类的实现与功能。这些类用于管理帧缓存,包括内存分配、帧获取、所有权管理等操作,确保高效的视频数据处理和传输。

代码的主要结构框架如下:

class _FrameCache:_xlnk = None...def getframe(self):"""从缓存中检索一个帧,或者在缓存为空时创建一个新帧。"""class AxiVDMA(DefaultIP):
"""Xilinx VideoDMA IP核的驱动类该驱动程序分为输入和输出通道,并通过 readchannel 和 writechannel 属性公开。
每个通道都有 start 和 stop 方法来控制数据传输。DMA 帧缓存使用单一所有权模型,即帧要么由 DMA 拥有,要么由用户代码拥有,
不能同时被两者拥有。S2MMChannel.readframe 和 MM2SChannel.newframe 都会
将帧返回给用户。用户有责任使用 freebuffer() 方法释放帧,或者使用
MM2SChannel.writeframe 将所有权交还给 DMA。一旦所有权被归还,用户不应访问帧
的内容,因为底层内存可能会在没有警告的情况下被删除。
"""class _FrameList:"""用于处理与 DMA 通道关联的帧列表的内部辅助类。除非通过 takeownership显式移除,否则假定其包含的所有帧的所有权。"""def __init__(self, parent, offset, count):self._frames = [None] * count...class S2MMChannel:...def start(self):...self._cache = _FrameCache(self._mode, cacheable=self.cacheable_frames)for i in range(len(self._frames)):self._frames[i] = self._cache.getframe()...class MM2SChannel:...def start(self):...self._cache = _FrameCache(self._mode, cacheable=self.cacheable_frames)self._frames[0] = self._cache.getframe()...def __init__(self, description, framecount=None):...super().__init__(description)if "parameters" in description:parameters = description["parameters"]has_s2mm = parameters["C_INCLUDE_S2MM"] == "1"has_mm2s = parameters["C_INCLUDE_MM2S"] == "1"...if has_s2mm: # 由IP设置确定是否包含属性self.readchannel = AxiVDMA.S2MMChannel(self, self.s2mm_introut, memory)if has_mm2s:self.writechannel = AxiVDMA.MM2SChannel(self, self.mm2s_introut, memory)...bindto = ["xilinx.com:ip:axi_vdma:6.2", "xilinx.com:ip:axi_vdma:6.3"]

2. 代码分析

2.1 _FrameCache 类定义

import asyncio
import numpy as np
from pynq.xlnk import ContiguousArray
from pynq import DefaultIP, Xlnkclass _FrameCache:_xlnk = None # 类变量(静态变量)def __init__(self, mode, capacity=5, cacheable=0):self._cache = [] # 空列表,帧缓存的指针self._mode = modeself._capacity = capacityself._cacheable = cacheabledef getframe(self):if self._cache: # 缓存有数据frame = _FrameCache._xlnk.cma_array(shape=self._mode.shape, dtype='u1', cacheable=self._cacheable,pointer=self._cache.pop(), cache=self)else: # 缓存为空if _FrameCache._xlnk is None:_FrameCache._xlnk = Xlnk() # 执行延迟初始化# 创建连续内存frame = _FrameCache._xlnk.cma_array(shape=self._mode.shape, dtype=np.uint8,cacheable=self._cacheable, cache=self)return frame# 添加到缓存列表def return_pointer(self, pointer):if len(self._cache) < self._capacity:self._cache.append(pointer)def clear(self):self._cache.clear()

1)主要功能

  • 初始化(__init__):缓存列表_cache、模式_mode、缓存容量_capacity和是否可缓存的标志_cacheable。
  • 获取帧(getframe):
    • 如果缓存中有可用的帧,则从缓存中取出一个帧并返回。
    • 如果缓存为空,则创建一个新的帧。
    • 使用 Xlnk 库的 cma_array 方法来分配连续内存的数组。
    • 返回的数组对象的 freebuffer 方法被重写,以便在被释放时将其返回到缓存,而不是直接释放掉。
  • 返回指针(return_pointer方法):
    • 将一个帧的指针返回到缓存中,如果缓存未达到容量限制,则将指针添加到缓存列表中。
  • 清空缓存(clear方法):
    • 清空缓存中的所有帧指针。

2)_xlnk = None

  • _xlnk 是一个类变量(静态变量),为了实现 Xlnk 实例的延迟初始化和共享。
  • 通过将 _xlnk 设为类变量,所有的 _FrameCache 实例都可以共享同一个 Xlnk 实例。这对于管理有限资源(如硬件设备的内存接口)是有用的,避免了重复的实例化和资源浪费。

3)cma_array() 函数的参数 cache=self

在 cma_array 创建的对象中保留对 _FrameCache 的引用,以便在对象不再使用时,可以通过 _FrameCache 实例执行特定的资源管理操作。

4)dtype='u1'

指定数据类型为无符号8位整数(等同于 np.uint8),表示每个元素占用1个字节。

2.1.1 xlnk.cma_array()

def cma_array(self, shape, dtype=np.uint32, cacheable=0,pointer=None, cache=None):if isinstance(shape, numbers.Integral):shape = [shape]dtype = np.dtype(dtype)elements = functools.reduce(lambda value, total: value * total, shape)length = elements * dtype.itemsizeif pointer is None:raw_pointer = self.cma_alloc(length, cacheable=cacheable)pointer = self.ffi.gc(raw_pointer, self.cma_free, size=length)buffer = self.cma_get_buffer(pointer, length)physical_address = self.cma_get_phy_addr(pointer)view = PynqBuffer(shape=shape, dtype=dtype, buffer=buffer,device_address=physical_address,coherent=not cacheable,bo=physical_address, device=self)view.pointer = pointerview.return_to = cachereturn view

功能:创建一个物理上连续的 numpy 数组。可以通过返回对象的 physical_address 属性找到该数组的物理地址。当不再需要数组时,应该使用 array.freebuffer() 或 array.close() 来释放数组。此外,cma_array 可以在 with 语句中使用,以便在代码块结束时自动释放内存。

参数:

  • shape(int 或 int 的元组)—— 要构建的数组的维度
  • dtype(numpy.dtype 或 str)—— 要构建的数据类型,默认为32位无符号整数
  • cacheable(int)—— 缓冲区是否可缓存,默认值为0

返回:numpy 数组;返回类型:numpy.ndarray

2.1.2 pointer=None

pointer 默认值 None。

1)内存分配

当 pointer 是 None 时,代码会执行 self.cma_alloc(length, cacheable=cacheable) 来分配一块所需大小 (length) 的内存,并返回一个指向这块内存的原始指针 raw_pointer。

2)自定义 pointer

如果调用者提供了一个自定义的 pointer,则意味着内存已经在函数外部分配好了,函数只需使用该指针而不再需要自行分配内存。

2.1.3 PynqBuffer

view = PynqBuffer(shape=shape, dtype=dtype, buffer=buffer,device_address=physical_address,coherent=not cacheable,bo=physical_address, device=self)

PynqBuffer 是一个类,用于创建一个物理上连续的内存缓冲区,继承自 numpy.ndarray,并添加了一些额外的属性和方法来处理物理地址和缓存一致性。

此处代码是创建一个 PynqBuffer 对象,并初始化其属性:

  • shape 和 dtype 定义了缓冲区的形状和数据类型。
  • buffer 是实际的数据缓冲区。
  • device_address 是缓冲区的物理地址。
  • coherent 表示缓冲区是否是一致的(即是否需要缓存一致性)。
  • bo 是缓冲区对象的标识符。
  • device 是与缓冲区关联的设备。

缓存一致性(Cache Coherence)

当多个处理器核心共享同一块内存区域时,如果一个核心修改了这块内存中的数据,其他核心的缓存中也必须反映这一变化,以避免数据不一致的问题。

cacheable 参数

  • cacheable=0(不可缓存):缓冲区的数据不会被缓存,直接从主内存读取和写入。这种情况下,数据的一致性由硬件保证,适用于需要频繁访问和修改的缓冲区。
  • cacheable=1(可缓存):缓冲区的数据会被缓存到处理器的缓存中,以提高访问速度。这种情况下,需要手动处理缓存一致性问题,例如在数据修改后刷新缓存,以确保其他处理器核心看到的数据是最新的。

2.2 _FrameCache 例化与调用

_FrameCache 类的实例化,是在 AxiVDMA 类的嵌套两个子类 S2MMChannel 和 MM2SChannel 类中 start 方法中进行的,代码如下:

class AxiVDMA(DefaultIP):"""Driver class for the Xilinx VideoDMA IP core"""class S2MMChannel:...def start(self):"""Start the DMA. The mode must be set prior to this being called"""if not self._mode:raise RuntimeError("Video mode not set, channel not started")self.desiredframe = 0# 创建 _FrameCache 对象(初始化)self._cache = _FrameCache(self._mode, cacheable=self.cacheable_frames)# 依据_frames数量,申请若干个CMA区域for i in range(len(self._frames)):self._frames[i] = self._cache.getframe()self._writemode()self.reload()self._mmio.write(0x30, 0x108b)#0x00011083)  # Start DMAself.irqframecount = 4  # Ensure all frames are written toself._mmio.write(0x34, 0x1000)  # Clear any interruptswhile not self.running:passself.reload()self.desiredframe = 1class MM2SChannel:...def start(self):"""Start the DMA channel with a blank screen. The mode mustbe set prior to calling or a RuntimeError will result."""if not self._mode:raise RuntimeError("Video mode not set, channel not started")self._cache = _FrameCache(self._mode, cacheable=self.cacheable_frames)self._frames[0] = self._cache.getframe()self._writemode()self.reload()self._mmio.write(0x00, 0x008b)#0x00011089)while not self.running:passself.reload()self.desiredframe = 0pass

_FrameCache 类会根据 _mode 中的形状申请内存区域,另一个参数是 cacheable_frames,标志是否可缓存。

self._cache = _FrameCache(self._mode, cacheable=self.cacheable_frames)

2.3 _FrameCache 测试

可以从以下几个方面进行测试:

1)初始化测试:

  • 确认 _FrameCache 类的实例化是否正确,检查 mode、capacity 和 cacheable 参数是否正确设置。
  • 确认 _cache 列表是否初始化为空。

2)getframe 方法测试:

  • 在缓存为空的情况下调用 getframe,确保能够正确创建新的 frame。
  • 在缓存不为空的情况下调用 getframe,确保能够从缓存中取出 frame。
  • 确认 frame 对象的 shape 和 dtype 是否与 mode 的 shape 和 dtype 一致。
  • 确认 frame 对象的 cacheable 属性是否正确设置。

3)缓存管理测试:

  • 调用 return_pointer 方法,确保指针正确返回到缓存中。
  • 确认缓存的大小不超过 capacity。

4)清理测试:

  • 调用 clear 方法,确保缓存被正确清空。

5)边界条件测试:

  • 测试 capacity 为 0 或负值的情况,确保代码能够正确处理。
  • 测试 mode 为不同形状和数据类型的情况,确保代码的通用性。

6)异常处理测试:

  • 确认在 _xlnk 未初始化的情况下,getframe 方法能够正确初始化 _xlnk。
  • 测试在 Xlnk 类不可用或初始化失败的情况下,代码的异常处理是否合理。

通过这些测试,可以全面验证 _FrameCache 类的功能和稳定性。

2.4 _FrameList 类定义

_FrameList 是一个内部辅助类,用于处理与 VDMA 通道关联的帧列表,负责管理帧的存储、访问和所有权转移。

class AxiVDMA(DefaultIP):class _FrameList:def __init__(self, parent, offset, count=3):self._frames = [None] * count # 创建列表[None, None, None]self._mmio = parent._mmioself._offset = offset # 即通道 Start Addressself._slaves = set() # 集合self.count = countself.reload = parent.reload # 写入VSize,重启 VDMA 通道def __getitem__(self, index):frame = self._frames[index]return framedef takeownership(self, index):self._frames[index] = Nonedef __len__(self):return self.countdef __setitem__(self, index, frame):self._frames[index] = frameif frame is not None:self._mmio.write(self._offset + 4 * index, frame.physical_address)else:self._mmio.write(self._offset + 4 * index, 0)self.reload()for s in self._slaves:s[index] = frames.takeownership(index)def addslave(self, slave):self._slaves.add(slave)for i in range(len(self._frames)):slave[i] = self[i]slave.takeownership(i)slave.reload()def removeslave(self, slave):self._slaves.remove(slave)

主要功能:

  • 初始化
  • 索引访问 __getitem__(self, index):通过索引访问帧列表中的某个帧
  • 长度访问 __len__(self)
  • 赋值行为:_FrameList[i] = value 
    • 设置或更新帧列表中指定索引位置的帧。
    • 如果帧非空,将帧的物理地址写入对应 Start Address。
    • 如果帧为空,则在对应的内存地址写入0。
    • 调用 reload() 方法来启动 VDMA。
    • 更新所有从属对象中对应的帧信息,并转移所有权。
  • 添加从属对象 addslave(self, slave)
  • 移除从属对象 removeslave(self, slave)

这个类的设计允许对帧进行集中管理,并通过内存映射输入输出与硬件设备进行交互,同时能够同步更新多个从属对象的帧信息。

2.5 _FrameList 例化与调用

class AxiVDMA(DefaultIP):class _FrameList:def __init__(self, parent, offset, count):self._frames = [None] * countself._mmio = parent._mmioself._offset = offsetself._slaves = set()self.count = countself.reload = parent.reload...class S2MMChannel:def __init__(self, parent, interrupt):self._mmio = parent.mmioself._frames = AxiVDMA._FrameList(self, 0xAC, parent.framecount)self._interrupt = interruptself._sinkchannel = Noneself._mode = Noneself.cacheable_frames = True...

1)def __init__(self, parent, offset, count)

_FrameList 的构造函数,包含3个参数:

  • parent:想要引用父对象的方法,parent._mmio,parent.reloa
  • offset:存放 S2MM 或者 MM2S 通道的 Start Address
  • count:存放 Frame Buffers 变量,是在 Vivado IDE 中设定的

注意混淆,第一个参数 self 是不需要传递的

2)self._frames = AxiVDMA._FrameList(self, 0xAC, parent.framecount)

创建一个 _FrameList 对象,并将其赋值给 self._frames。

  • self 是 S2MMChannel的实例,作为 parent 参数传入
  • 0xAC 是第二个参数
  • parent.framecount 是第三个参数

3)__setitem__(self, index, frame) 赋值操作

调用 __setitem__ 方法在通道 start 函数中:

首先,理解 self._cache.getframe() 本身是执行了内存申请的操作,并且返回物理地址。

class AxiVDMA(DefaultIP):...class S2MMChannel:...def start(self):...self._cache = _FrameCache(self._mode, cacheable=self.cacheable_frames)for i in range(len(self._frames)):self._frames[i] = self._cache.getframe()...

2.6 _FrameList 测试

3. 帧的使用

3.1 读取帧

def readframe(self):"""从通道读取一帧并返回给用户此函数可能会阻塞,直到读取完整帧为止。单帧缓冲区会被保留,因此在长时间暂停读取后读取的第一帧可能会返回过时的帧。为了确保在开始处理视频时获取最新的帧,请在开始处理循环之前额外读取一次。返回值-------视频帧的 numpy.ndarray"""if not self.running:raise RuntimeError('DMA channel not started')while self._mmio.read(0x34) & 0x1000 == 0:loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.ensure_future(self._interrupt.wait()))passself._mmio.write(0x34, 0x1000)return self._readframe_internal()
def _readframe_internal(self):if self._mmio.read(0x34) & 0x8980:# Some spurious errors can occur at the start of transfers# let's ignore them for nowself._mmio.write(0x34, 0x8980)self.irqframecount = 1nextframe = self._cache.getframe()previous_frame = (self.activeframe + 2) % len(self._frames)captured = self._frames[previous_frame]self._frames.takeownership(previous_frame)self._frames[previous_frame] = nextframepost_frame = (self.activeframe + 2) % len(self._frames)captured.invalidate()return captured

4. 总结

本文详细解析了在 PYNQ 框架下,AXI VDMA 驱动中帧缓存管理的实现细节,重点介绍了 _FrameCache 和 _FrameList 类。

  • _FrameCache 类通过管理帧缓存的内存分配和回收,确保了视频数据处理的高效性。其实现中,通过共享的 Xlnk 实例进行连续内存分配,并采用单一所有权模型来管理帧的使用和释放,避免了资源浪费和竞争。
  • _FrameList 类则负责管理帧的存储、访问与所有权转移,支持多从属对象的同步更新。

9a08a5fe3711436393de6ebd11549afd.png

相关文章:

PYNQ 框架 - VDMA驱动 - 帧缓存

目录 1. 简介 2. 代码分析 2.1 _FrameCache 类定义 2.1.1 xlnk.cma_array() 2.1.2 pointerNone 2.1.3 PynqBuffer 2.2 _FrameCache 例化与调用 2.3 _FrameCache 测试 2.4 _FrameList 类定义 2.5 _FrameList 例化与调用 2.6 _FrameList 测试 3. 帧的使用 3.1 读取帧…...

Java导出Word文档的几种方法

文章目录 1. 使用 Apache POI2. 使用 Docx4j3. 使用 JODConverter4. 使用 FreeMarker 模板 在 Java 中导出 Word 文档可以通过多种库和方法实现。以下是几种常用的方法&#xff1a; 1. 使用 Apache POI Apache POI 是一个强大的库&#xff0c;可以用来读写 Microsoft Office 格…...

OceanBase V4.3.3,首个面向实时分析场景的GA版本发布

在10月23日举办的 OceanBase年度发布会 上&#xff0c;我们怀着激动之情&#xff0c;正式向大家宣布了 OceanBase 4.3.3 GA 版的正式发布&#xff0c;这也是OceanBase 为实时分析&#xff08;AP&#xff09;场景打造的首个GA版本。 2024 年初&#xff0c;我们推出了 4.3.0 版本…...

Maven随笔

文章目录 1、什么是MAVEN2、Maven模型3、Maven仓库4、项目集成1_Idea集成Maven设置2_创建Maven项目3_POM配置详解4_maven 坐标详情5_Maven工程类型6_导入Maven项目 5、依赖管理1_依赖配置2_依赖传递3_可选依赖4_排除依赖4_可选依赖和排除依赖的区别5_依赖范围6_继承与聚合7_版本…...

牛客题目解析

一.最长回文子串 1.题目&#xff1a;给定一个仅包含小写字母的字符串&#xff0c;求它的最长回文子串的长度。 最长回文子串__牛客网 2.算法原理&#xff1a; <1>动态规划算法:O(n^2),O(n^2) 具有通性&#xff0c;凡涉及回文子串的问题都可利用此法解决 知识储备&am…...

AG32的3个ADC可以并联使用吗

AG32的3个ADC可以并联使用吗&#xff1f; Customer: 需求&#xff1a; 在t1时间段&#xff0c;用5M的速度ch1通道采样得到结果1. 在t2时间段&#xff0c;用5M的速度ch2通道采样得到结果2. 在t3时间段&#xff0c;用5M的速度ch3通道采样得到结果3. 然后如此循环 。 考虑用3…...

什么是 OpenTelemetry?

OpenTelemetry 定义 OpenTelemetry (OTel) 是一个开源可观测性框架&#xff0c;允许开发团队以单一、统一的格式生成、处理和传输遥测数据&#xff08;telemetry data&#xff09;。它由云原生计算基金会 (CNCF) 开发&#xff0c;旨在提供标准化协议和工具&#xff0c;用于收集…...

[vulnhub]DC:7

https://www.vulnhub.com/entry/dc-7,356/ 端口扫描主机发现 探测存活主机&#xff0c;178是靶机 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-03 13:30 CST Nmap scan report for 192.168.75.1 Host is up (0.00037s l…...

个性化十足的贵族服务器,惠普ML310e Gen8,服务器中的 “潘多拉魔盒”

个性化十足的贵族服务器&#xff0c;惠普ML310e Gen8&#xff0c;服务器中的 “潘多拉魔盒” 小伙伴们大家好呀&#xff0c;这里是勤奋的凯尔森同学&#xff0c;今天给大家分享一款好玩的服务器&#xff0c;惠普ML310e Gen8 V2&#xff0c;相比大家都很熟悉HP ProLiant MicroS…...

百度社招内推

百度社招内推 「百度内推」快来投递你心仪的职位吧&#xff08; 网申链接地址&#xff1a;https://dwz.cn/ah4OUcca&#xff09;&#xff0c;填入内推码&#xff0c;完成投递&#xff0c;get内推绿色通道~我的内推码&#xff1a;IZ9PVH 内推有什么好处&#xff1a; 简历直达…...

本地部署开源在线即时通讯软件Fiora打造个人私密聊天室

文章目录 前言1.关于Fiora2.安装Docker3.本地部署Fiora4.使用Fiora5.cpolar内网穿透工具安装6.创建远程连接公网地址7.固定Uptime Kuma公网地址 前言 相信大家在聊天时候总是很没安全感&#xff0c;比如在和小姐妹背着男朋友聊一些不能说的坏话&#xff0c;或者背着女朋友和兄…...

TS(类 接口 泛型)

文章目录 类复习相关知识属性修饰符public 修饰符属性的简写形式 protected修饰符private修饰符readonly修饰符 抽象类 接口&#xff08;interface&#xff09;定义类结构定义对象结构定义函数结构接口之间的继承接口自动合并 &#xff08;可重复定义&#xff09;一些相似的概念…...

docker 启动 neo4j

docker 启动 neo4j 1. 启动2. 导入数据 1. 启动 运行下面命令启动 neo4j&#xff0c; docker run \-d \--restartalways \--publish7474:7474 --publish7687:7687 \--volume$HOME/neo4j-4.4.38/data:/data \--name neo4j-apoc-4.4.38 \-e NEO4J_dbms_allow__upgradetrue \-e …...

OPENAI官方prompt文档解析

官方文档地址:https://platform.openai.com/docs/guides/gpt-best-practices 文档中文版来源:OpenAI 官方提示工程指南 [译] | 宝玉的分享 (baoyu.io) 1.写清楚说明 如果prompt给的范围十分模糊或是过于宽泛,那么GPT就会开始猜测您想要的内容,从而导致生成的结果偏离预期. …...

【GESP】C++一级练习BCQM3092,双面打印

GESP一级知识点if分支语句和取余、整除操作练习。比较简单。 题目题解详见&#xff1a;https://www.coderli.com/gesp-1-bcqm3092/ 【GESP】C一级练习BCQM3092&#xff0c;双面打印 | OneCoderGESP一级知识点if分支语句和取余、整除操作练习。比较简单。https://www.coderli.…...

mysql--多表查询

一、联合查询 作用&#xff1a;合并结果集就是把两个select语句的查询结果合并到一起&#xff01; 合并结果集有两种方式&#xff1a; UNION&#xff1a;合并并去除重复记录&#xff0c;例如&#xff1a;SELECT * FROM t1 UNION SELECT * FROM t2&#xff1b; UNION ALL&a…...

RHCE-Web-nginx http实验和nginx https实验

一、web服务器简介 &#xff08;1&#xff09;什么是www www 是 world wide web 的缩写&#xff0c;也就是全球信息广播的意思。通常说的上网就是使用 www 来查询用户 所需要的信息。 www 可以结合文字、图形、影像以及声音等多媒体&#xff0c;并通过可以让鼠标单击超链接的…...

少儿编程学习现状洞察:青少年编程教育需求与学习频率分析

随着少儿编程教育的逐渐普及&#xff0c;越来越多的家庭开始关注孩子在编程学习中的表现。根据最新数据&#xff0c;90%的少儿编程学员保持每周至少一节课的学习频率&#xff0c;而95%的编程课程都安排在周末。特别是在8岁、10岁、12岁及15岁以上的年龄层次&#xff0c;孩子的学…...

接口集成、快速对接-阿里身份证实名认证接口

身份证实名认证接口现已被应用在联网的各种业务场景中&#xff0c;如电商、在线教育、银行等等&#xff0c;下面以电商平台为例&#xff0c;列举翔云身份证实名认证接口在电商平台中的具体应用和优势。 电商平台的出现方便了人们的生活&#xff0c;进行电商的实名认证有助于提高…...

HTTP、WebSocket、gRPC 或 WebRTC:各种协议的区别

在为您的应用程序选择通信协议时&#xff0c;有很多不同的选择。 本文将了解四种流行的解决方案&#xff1a;HTTP、WebSocket、gRPC 和 WebRTC。 我们将通过深入学习其背后原理、最佳用途及其优缺点来探索每个协议。 通信方式在不断改进&#xff1a;变得更快、更方便、更可靠&…...

Unity3D学习FPS游戏(8)装弹和弹夹UI显示

前言&#xff1a;实现了武器的基本发射功能&#xff0c;但是我们弹夹数量是有限&#xff0c;之前并没有做装弹和弹夹显示的功能。本篇实现装弹和弹夹显示。 装弹和弹夹UI显示 装弹目标思路和实现 弹夹UI显示目标弹夹UI的思路和实现UI代码的思路和实现 武器控制的完整代码效果补…...

Android 托管 Github Action 发布 Github Packages ,实现 Mvn 免费自动化托管

自从多年前 JCenter 关闭服务之后&#xff0c;GSY 项目版本就一直发布在 Jitpack 上&#xff0c;如今每个月也都有大概 10w 左右下载&#xff0c;但是近年来时不时就会出现历史版本丢失的问题&#xff0c;而且有时候还不是某个具体版本丢失&#xff0c;而是版本里的某几个依赖突…...

火山引擎VeDI数据服务平台:在电商场景中,如何解决API编排问题?

01 平台介绍 数据服务平台可以在保证服务高可靠性和高安全性的同时&#xff0c;为各业务线搭建数据服务统一出口&#xff0c;促进数据共享&#xff0c;为数据和应用之间建立了一座“沟通桥梁”。 同时&#xff0c;解决数据理解困难、异构、重复建设、审计运维困难等问题&#x…...

【每日C/C++问题】

一、 结构体和联合体有什么区别&#xff1f;能否在声明过程当中缺省名字&#xff1f;&#xff08;需要写清楚使用方法&#xff09; 结构体的各个成员占用不同的内存空间&#xff0c;总大小是所有成员大小之和&#xff08;结构体字节对齐&#xff09;&#xff1a; typedef str…...

layaair做帧动画,等待一秒之后移动坐标,坐标位置明明相同,执行的时候却会抖动。

如下图&#xff1a;我将1秒后面的位置与0秒的坐标位置设置为一样&#xff0c;然后再第二秒的时候再设置位置移动。也就是想实现这个效果&#xff0c;小狗先停在这里一秒&#xff0c;然后再开始行走。现在的问题是停在这里依然抖动一下&#xff0c;也就是根本就停不住。还是会变…...

SAP分包业务中能否应用后继物料?

近期物流用户在工作中遇到新的问题。在分包业务中的原材料后继&#xff08;物料主数据设定非连续标识及后继物料&#xff09;不成功问题。对于未知应用&#xff0c;需要先研究期可行性&#xff0c;与问题或故障不同&#xff0c;如果系统本身就不支持&#xff0c;再多分析测试也…...

【数据结构】二叉树——判断是否为完全二叉树

一、思路 有完全二叉树的解释 我们想要判断二叉树是否为完全二叉树 我们可以用队列来实现 我们先将根节点入队列 再将根节点出队列&#xff0c;判断取出节点是否为空、 若不为空将该节点的左右节点入队列 左右节点为空也入队列 若为空则停止入队列 然后判断队列中是否有 NUL…...

FFmpeg 4.3 音视频-多路H265监控录放C++开发十. 多线程控制帧率。循环播放,QT connect 细节,

在前面&#xff0c;我们总结一下前面的代码。 在 FactoryModeForAVFrameShowSDL 构造函数中 init SDL。 通过 QT timerevent机制&#xff0c;通过startTimer(10);每隔10ms&#xff0c;就会调用timerEvent事件。 在timerEvent事件中&#xff0c;真正的去 读取数据&#xff0c…...

近百万奖金!2024 Web3.0 创新大赛重磅来袭!

10月30日&#xff0c;中国互联网协会与香港Web3.0协会共同组织举办的2024 Web3.0 创新大赛在上海举行启动会&#xff0c;宣布大赛正式在DataFountain竞赛平台&#xff08;简称DF平台&#xff0c;http://www.datafountain.cn&#xff09;启动上线。 大赛面向社会各界征集参赛团队…...

gRPC 一种现代、开源、高性能的远程过程调用 (RPC) 可以在任何地方运行的框架

背景介绍 gRPC 是一种现代开源高性能远程过程调用 &#xff08;RPC&#xff09; 可以在任何环境中运行的框架。它可以有效地连接服务 在数据中心内和数据中心之间&#xff0c;具有对负载平衡、跟踪、 运行状况检查和身份验证。它也适用于最后一英里 分布式计算&#xff0c;用于…...